use std::marker::PhantomData;
use autonomi::files::archive_public::ArchiveAddress;
use blsttc::PublicKey;
use color_eyre::eyre::{Result, eyre};
use serde::{Deserialize, Serialize};
use autonomi::SecretKey;
use autonomi::client::data::DataAddress;
use autonomi::client::data_types::graph::{GraphContent, GraphError};
use autonomi::client::key_derivation::{DerivationIndex, MainPubkey, MainSecretKey};
use autonomi::{
AttoTokens, Bytes, GraphEntry, GraphEntryAddress, Pointer, PointerAddress,
pointer::PointerTarget,
};
use crate::client::DwebClient;
use crate::data::autonomi_get_file_public;
use crate::helpers::graph_entry::{
create_graph_entry, get_derivation_from_graph_entry, graph_entry_get,
};
use crate::helpers::retry::retry_until_ok;
use crate::token::{Spends, show_spend_return_value};
use crate::types::{HISTORY_POINTER_DERIVATION_INDEX, derive_named_object_secret};
const LARGEST_VERSION: u64 = u64::MAX;
pub type HistoryValue = GraphContent;
pub const HISTORY_VALUE_SIZE: usize = size_of::<HistoryValue>();
pub fn history_value_from_bytes(bytes: &[u8]) -> Result<HistoryValue> {
if bytes.len() > HISTORY_VALUE_SIZE {
return Err(eyre!(
"history_value_from_bytes() invalid length of bytes: {}",
bytes.len()
));
}
let mut content: HistoryValue = [0; HISTORY_VALUE_SIZE];
content[..bytes.len()].copy_from_slice(bytes);
Ok(content)
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct HistoryAddress {
pub owner: PublicKey,
}
impl HistoryAddress {
pub fn new(owner: PublicKey) -> Self {
Self { owner }
}
pub fn owner(&self) -> PublicKey {
self.owner
}
pub fn to_underlying_graph_root(&self) -> GraphEntryAddress {
GraphEntryAddress::new(self.owner)
}
pub fn to_hex(&self) -> String {
self.owner.to_hex()
}
pub fn from_hex(hex: &str) -> Result<Self, blsttc::Error> {
let owner = PublicKey::from_hex(hex)?;
Ok(Self { owner })
}
}
impl std::fmt::Display for HistoryAddress {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.to_hex())
}
}
#[allow(async_fn_in_trait)]
pub trait Trove<T> {
fn trove_type() -> DataAddress;
fn to_bytes(trove: &T) -> Result<Bytes>;
async fn from_bytes(client: &DwebClient, bytes: Bytes) -> Result<T>;
}
pub struct History<T: Trove<T> + Clone> {
client: DwebClient,
history_address: HistoryAddress,
name: String,
num_entries: u64,
head_graphentry: Option<GraphEntry>,
pointer_counter: u64,
pointer_target: Option<GraphEntryAddress>,
default_version: Option<u64>,
pub cached_version: Option<TroveVersion<T>>,
phantom: std::marker::PhantomData<T>,
}
impl<T: Trove<T> + Clone> History<T> {
pub async fn create_online(
client: DwebClient,
name: String,
owner_secret_key: SecretKey,
) -> Result<(AttoTokens, Self)> {
println!("DEBUG History::create_online({name})");
if name.is_empty() {
return Err(eyre!(
"History::create_online() failed - cannot use an empty name"
));
}
let spends = Spends::new(&client, Some(&"History create online cost: ")).await?;
let history_secret_key =
Self::history_main_secret_key(owner_secret_key).derive_child(name.as_bytes());
let history_address = HistoryAddress::new(history_secret_key.public_key());
let root_entry = create_graph_entry(
&history_secret_key,
None,
&DerivationIndex::random(&mut rand::thread_rng()),
Self::trove_type(),
)
.await?;
println!(
"DEBUG graph_entry_put() at {}",
root_entry.address().to_hex()
);
let (graph_cost, root_entry_address) = match client
.client
.graph_entry_put(root_entry.clone(), client.payment_option())
.await
{
Ok(result) => result,
Err(e) => {
let msg = format!("failed to put graph entry - {e}");
println!("DEBUG graph_entry_put() {msg}");
return Err(eyre!(msg));
}
};
let pointer_secret_key = Self::history_pointer_secret_key(history_secret_key);
let pointer_target = PointerTarget::GraphEntryAddress(root_entry_address);
match client
.client
.pointer_create(
&pointer_secret_key,
pointer_target,
client.wallet.clone().into(),
)
.await
{
Ok((pointer_cost, pointer_address)) => {
println!(
"DEBUG History::new() created new pointer at {}",
pointer_address.to_hex()
);
let history = History {
client: client.clone(),
name,
history_address,
num_entries: 1,
head_graphentry: Some(root_entry), pointer_counter: 0,
pointer_target: None,
default_version: None,
cached_version: None,
phantom: PhantomData,
};
let total_cost = if let Some(total_cost) = pointer_cost.checked_add(graph_cost) {
total_cost
} else {
return show_spend_return_value::<Result<(AttoTokens, Self)>>(
&spends,
Err(eyre!("Invalid cost")),
)
.await;
};
Ok((total_cost, history))
}
Err(e) => {
let message = format!("History::new() failed to create pointer: {e}");
println!("DEBUG {message}");
return show_spend_return_value::<Result<(AttoTokens, Self)>>(
&spends,
Err(eyre!("Invalid cost")),
)
.await;
}
}
}
async fn update_online(
&mut self,
owner_secret_key: SecretKey,
trove_address: ArchiveAddress,
) -> Result<(AttoTokens, u64)> {
println!("DEBUG History::update_online()");
let history_secret_key =
Self::history_main_secret_key(owner_secret_key).derive_child(self.name.as_bytes());
let history_address = HistoryAddress::new(history_secret_key.public_key());
println!("Updating History at {}", history_address.to_hex());
let spends = Spends::new(&self.client, Some(&"History update online cost: ")).await?;
let pointer_address = pointer_address_from_history_address(history_address.clone())?;
match get_and_verify_pointer(&self.client, &pointer_address).await {
Ok(pointer) => {
self.pointer_counter = pointer.counter();
let head_address = self.head_graphentry.clone().unwrap().address();
let (graph_cost, next_address) = match self
.create_next_graph_entry_online(
history_secret_key.clone(),
head_address,
&trove_address,
)
.await
{
Ok(result) => result,
Err(e) => {
return show_spend_return_value::<Result<(AttoTokens, u64)>>(
&spends,
Err(eyre!("failed to create next GraphEnry: {e}")),
)
.await;
}
};
println!("Pointer retrieved with counter {}", pointer.counter());
let pointer_secret_key = Self::history_pointer_secret_key(history_secret_key);
println!(
"Updating pointer with new GraphEntry at: {}",
next_address.to_hex()
);
let client = self.client.client.clone();
let pointer_target = PointerTarget::GraphEntryAddress(next_address);
match retry_until_ok(
self.client.api_control.api_tries,
&"pointer_update()",
(client, pointer_secret_key, pointer_target),
async move |(client, pointer_secret_key, pointer_target)| match client
.pointer_update(&pointer_secret_key, pointer_target)
.await
{
Ok(result) => {
println!("Pointer updated");
Ok(result)
}
Err(e) => {
return Err(eyre!("Failed to add a trove to history: {e:?}"));
}
},
)
.await
{
Ok(_) => {
self.pointer_counter = pointer.counter();
self.pointer_target = Some(next_address);
return show_spend_return_value::<Result<(AttoTokens, u64)>>(
&spends,
Ok((graph_cost, self.pointer_counter)),
)
.await;
}
Err(e) => return Err(eyre!("Retries exceeded: {e:?}")),
}
}
Err(e) => return Err(eyre!("DEBUG failed to get history prior to update!\n{e}")),
}
}
pub async fn from_name(
client: DwebClient,
owner_secret_key: SecretKey,
name: String,
ignore_pointer: bool,
minimum_entry_index: u64,
) -> Result<Self> {
println!("DEBUG History::from_name({name})");
if name.is_empty() {
return Err(eyre!(
"History::from_name() failed - cannot use an empty name"
));
}
let history_secret_key =
Self::history_main_secret_key(owner_secret_key).derive_child(name.as_bytes());
let history_address = HistoryAddress::new(history_secret_key.public_key());
let pointer_address = pointer_address_from_history_address(history_address.clone())?;
let pointer = match get_and_verify_pointer(&client, &pointer_address).await {
Ok(pointer) => pointer,
Err(e) => {
let msg = format!(
"failed to get pointer network address {} - {e}",
pointer_address.to_hex()
);
println!("DEBUG History::from_name() {msg}");
return Err(e.into());
}
};
println!(
"DEBUG History::from_name() obtained pointer from {}",
pointer.address().to_hex()
);
let pointer_target = match pointer.target() {
PointerTarget::GraphEntryAddress(pointer_target) => *pointer_target,
other => {
return Err(eyre!(
"History::from_name() pointer target is not a GraphEntry - this is probably a bug. Target: {other:?}"
));
}
};
let mut history = History {
client: client.clone(),
name,
history_address,
num_entries: 0,
head_graphentry: None,
pointer_counter: pointer.counter(),
pointer_target: Some(pointer_target),
default_version: None,
cached_version: None,
phantom: PhantomData,
};
if ignore_pointer || (!ignore_pointer && minimum_entry_index > pointer.counter()) {
history
.update_from_graph_internal(&pointer_target, pointer.counter())
.await?;
} else {
match history
.get_graph_entry_from_network(&pointer_target, false)
.await
{
Ok(pointer_head) => {
if pointer.counter() == 0 {
println!("WARNING: initialising History with pointer.counter() of 0");
}
history.num_entries = pointer.counter() + 1;
history.head_graphentry = Some(pointer_head);
history.pointer_counter = pointer.counter() + 1;
history.pointer_target = Some(pointer_target);
}
Err(e) => return Err(eyre!("Failed to get pointer target entry - {e}")),
};
}
Ok(history)
}
pub async fn from_history_address(
client: DwebClient,
history_address: HistoryAddress,
ignore_pointer: bool,
minimum_entry_index: u64,
) -> Result<History<T>> {
println!(
"DEBUG History::from_history_address({})",
history_address.to_hex()
);
let pointer_address = pointer_address_from_history_address(history_address.clone())?;
let pointer = match get_and_verify_pointer(&client, &pointer_address).await {
Ok(pointer) => pointer,
Err(e) => {
let msg = format!(
"failed to get pointer network address {} - {e}",
pointer_address.to_hex()
);
println!("DEBUG History::from_history_address() {msg}");
return Err(e.into());
}
};
let pointer_target = match pointer.target() {
PointerTarget::GraphEntryAddress(pointer_target) => *pointer_target,
other => {
return Err(eyre!(
"History::from_history_address() pointer target is not a GraphEntry - this is probably a bug. Target: {other:?}"
));
}
};
println!("DEBUG INSPECT pointer_counter: {}", pointer.counter());
println!("DEBUG INSPECT pointer_target : {}", pointer_target.to_hex());
let mut history = History::<T> {
client,
name: String::from(""),
history_address,
num_entries: 0,
head_graphentry: None,
pointer_counter: pointer.counter(),
pointer_target: Some(pointer_target),
default_version: None,
cached_version: None,
phantom: PhantomData,
};
if ignore_pointer || (!ignore_pointer && minimum_entry_index > pointer.counter()) {
history
.update_from_graph_internal(&pointer_target, pointer.counter())
.await?;
} else {
match history
.get_graph_entry_from_network(&pointer_target, false)
.await
{
Ok(pointer_head) => {
if pointer.counter() == 0 {
println!("WARNING: initialising History with pointer.counter() of 0");
}
history.num_entries = pointer.counter() + 1;
history.head_graphentry = Some(pointer_head);
history.pointer_counter = pointer.counter() + 1;
history.pointer_target = Some(pointer_target);
}
Err(e) => return Err(eyre!("Failed to get pointer target entry - {e}")),
};
}
println!(
"DEBUG from_history_address() returning History with num_entries: {}",
history.num_entries
);
history.update_default_version();
Ok(history)
}
pub async fn update_from_graph(&mut self) -> Result<GraphEntry> {
println!("DEBUG History::update_from_graph()");
if self.head_graphentry.is_some() {
return Ok(self.head_graphentry.clone().unwrap());
}
let pointer_address = pointer_address_from_history_address(self.history_address.clone())?;
let pointer = match get_and_verify_pointer(&self.client, &pointer_address).await {
Ok(pointer) => pointer,
Err(e) => {
let msg = format!(
"failed to get pointer network address {} - {e}",
pointer_address.to_hex()
);
println!("DEBUG History::from_history_address() {msg}");
return Err(e.into());
}
};
let pointer_target = match pointer.target() {
PointerTarget::GraphEntryAddress(pointer_target) => *pointer_target,
other => {
return Err(eyre!(
"History::from_history_address() pointer target is not a GraphEntry - this is probably a bug. Target: {other:?}"
));
}
};
self.update_from_graph_internal(&pointer_target, pointer.counter())
.await
}
async fn update_from_graph_internal(
&mut self,
pointer_target: &GraphEntryAddress,
pointer_counter: u64,
) -> Result<GraphEntry> {
println!("DEBUG History::update_from_graph_internal()");
if self.head_graphentry.is_some() {
return Ok(self.head_graphentry.clone().unwrap());
}
let pointer_target_entry = match self
.get_graph_entry_from_network(pointer_target, false)
.await
{
Ok(head) => head,
Err(e) => return Err(eyre!("Failed to get pointer target entry - {e}")),
};
let mut iter_entry = pointer_target_entry;
let mut iter_index = pointer_counter;
let mut final_index = iter_index;
let mut final_entry;
loop {
println!("DEBUG get child of GraphEntry index {iter_index} - child...");
final_entry = iter_entry.clone();
iter_entry = if let Some(entry) = self.get_child_entry_of(&iter_entry, true).await {
iter_index = iter_index + 1;
final_index = final_index + 1;
entry
} else {
break;
}
}
self.head_graphentry = Some(final_entry.clone());
self.num_entries = final_index + 1;
Ok(final_entry)
}
fn create_pointer_for_update(
counter: u64,
graph_entry_address: &GraphEntryAddress,
history_secret_key: &SecretKey,
) -> Pointer {
println!("DEBUG create_pointer_for_update()");
let pointer_target = PointerTarget::GraphEntryAddress(*graph_entry_address);
Pointer::new(history_secret_key, counter, pointer_target)
}
pub fn history_main_secret_key(owner_secret_key: SecretKey) -> SecretKey {
let derivation_index: [u8; 32] = Self::trove_type().xorname().to_vec().try_into().unwrap();
MainSecretKey::new(owner_secret_key.clone())
.derive_key(&DerivationIndex::from_bytes(derivation_index))
.into()
}
pub fn parse_history_address_or_name(
owner_secret_key: SecretKey,
address_or_name: &String,
) -> HistoryAddress {
if let Ok(address) = crate::helpers::convert::str_to_history_address(address_or_name) {
return address;
}
let history_secret_key = Self::history_main_secret_key(owner_secret_key)
.derive_child(address_or_name.as_bytes());
return HistoryAddress::new(history_secret_key.public_key());
}
fn history_pointer_secret_key(history_secret_key: SecretKey) -> SecretKey {
derive_named_object_secret(
history_secret_key,
&HISTORY_POINTER_DERIVATION_INDEX,
&None,
None,
None,
)
}
pub fn root_graph_entry_address(history_address: GraphEntryAddress) -> GraphEntryAddress {
history_address
}
pub fn head_entry_address(&self) -> Result<GraphEntryAddress> {
match self.head_graphentry.clone() {
Some(head_entry) => Ok(head_entry.address()),
None => Err(eyre!("History has uninitialised head_graphentry_entry")),
}
}
pub fn pointer_counter(&self) -> u64 {
self.pointer_counter
}
fn update_default_version(&mut self) -> Option<u64> {
self.default_version = match self.num_versions() {
Ok(version) => Some(version),
Err(_) => None,
};
self.default_version
}
pub fn trove_type() -> ArchiveAddress {
T::trove_type()
}
pub fn history_address(&self) -> HistoryAddress {
self.history_address.clone()
}
pub fn num_entries(&self) -> u64 {
self.num_entries
}
pub fn num_versions(&self) -> Result<u64> {
let num_entries = self.num_entries;
if num_entries == 0 {
let message = "pointer is empty (0 entries)";
Err(eyre!(message))
} else {
Ok(num_entries - 1)
}
}
async fn trove_download(&self, data_address: ArchiveAddress) -> Result<T> {
return History::<T>::raw_trove_download(&self.client, data_address).await;
}
pub async fn raw_trove_download(
client: &DwebClient,
data_address: ArchiveAddress,
) -> Result<T> {
println!(
"DEBUG directory_tree_download() at {}",
data_address.to_hex()
);
retry_until_ok(
client.api_control.api_tries,
&"autonomi_get_file_public()",
(client, data_address),
async move |(client, data_address)| match autonomi_get_file_public(
client,
&data_address,
)
.await
{
Ok(content) => {
println!("Retrieved {} bytes", content.len());
let trove: T = match T::from_bytes(client, content).await {
Ok(trove) => trove,
Err(e) => {
println!("FAILED: {e}");
return Err(eyre!(e));
}
};
Ok(trove)
}
Err(e) => {
println!("FAILED: {e}");
Err(eyre!(e))
}
},
)
.await
}
pub async fn get_version_entry_value(
&mut self,
version: u64,
ignore_pointer: bool,
) -> Result<ArchiveAddress> {
println!("DEBUG History::get_version_entry_value(version: {version})");
if ignore_pointer {
self.update_from_graph().await?;
} else {
if self.pointer_counter == version {
if let Some(head) = &self.head_graphentry {
if let Ok(archive_address) =
ArchiveAddress::from_hex(&hex::encode(head.content))
{
return Ok(archive_address);
}
} else {
if let Some(head) = self.get_head_entry().await? {
if let Ok(archive_address) =
ArchiveAddress::from_hex(&hex::encode(head.content))
{
return Ok(archive_address);
}
}
}
}
}
let num_entries = self.num_entries();
let version = if version == 0 {
num_entries - 1
} else {
version
};
let max_version = if num_entries > 0 { num_entries - 1 } else { 0 };
if version > max_version {
let message = format!(
"History::get_version_entry_value({version}) out of range for max_version: {max_version}"
);
println!("{message}");
return Err(eyre!(message));
}
self.get_entry_value(version).await
}
pub async fn get_entry_value(&mut self, index: u64) -> Result<ArchiveAddress> {
println!("DEBUG History::get_entry_value(index: {index})");
match self.get_graph_entry(index).await {
Ok(entry) => {
if let Ok(entry) = ArchiveAddress::from_hex(&hex::encode(entry.content)) {
Ok(entry)
} else {
Err(eyre!(
"History::get_entry_value() - invalid ArchiveAddress in GraphEntry - probably a bug"
))
}
}
Err(e) => Err(eyre!("History::get_entry_value() - {e}")),
}
}
pub async fn get_graph_entry(&mut self, index: u64) -> Result<GraphEntry> {
let num_entries = self.num_entries();
if index >= num_entries {
return Err(eyre!(
"Index out of range, index: {index}, number of entries {num_entries}"
));
};
Ok(if index > num_entries / 2 {
let mut iter_entry = match self.get_head_entry().await {
Ok(head) => {
if head.is_some() {
head.unwrap()
} else {
return Err(eyre!("Empty history - no head entry"));
}
}
Err(e) => return Err(e),
};
let mut iter_index = num_entries - 1;
while index < iter_index {
println!("DEBUG stepping backwards: index {index} < {iter_index} iter_index");
iter_index = iter_index - 1;
iter_entry = if let Some(entry) = self.get_parent_entry_of(&iter_entry).await? {
entry
} else {
return Err(eyre!(
"Ran out of entries - probably a bug in History::get_entry_value()"
));
}
}
iter_entry
} else {
let mut iter_entry = match self.get_root_entry().await {
Ok(root) => {
if root.is_some() {
root.unwrap()
} else {
return Err(eyre!(
"Failed to get root entry in History::get_entry_value()"
));
}
}
Err(e) => return Err(e),
};
let mut iter_index = 0;
while index > iter_index {
println!("DEBUG stepping forwards: index {index} > {iter_index} iter_index");
iter_index = iter_index + 1;
iter_entry = if let Some(entry) = self.get_child_entry_of(&iter_entry, false).await
{
entry
} else {
return Err(eyre!(
"Ran out of entries - may be a bug in History::get_entry_value()"
));
}
}
iter_entry
})
}
async fn get_graph_entry_from_network(
&self,
graph_entry_address: &GraphEntryAddress,
check_exists: bool,
) -> Result<GraphEntry> {
Ok(graph_entry_get(&self.client.client, graph_entry_address, check_exists).await?)
}
pub async fn get_root_entry(&self) -> Result<Option<GraphEntry>> {
Ok(Some(
self.get_graph_entry_from_network(
&Self::root_graph_entry_address(GraphEntryAddress::new(
self.history_address.owner(),
)),
false,
)
.await?,
))
}
pub async fn get_head_entry(&self) -> Result<Option<GraphEntry>> {
Ok(Some(
self.get_graph_entry_from_network(&self.head_entry_address()?, false)
.await?,
))
}
pub async fn get_parent_entry_of(
&self,
graph_entry: &GraphEntry,
) -> Result<Option<GraphEntry>> {
let parent = GraphEntryAddress::new(graph_entry.parents[0]);
Ok(Some(
self.get_graph_entry_from_network(&parent, false).await?,
))
}
pub async fn get_child_entry_of(
&self,
graph_entry: &GraphEntry,
check_exists: bool,
) -> Option<GraphEntry> {
let next_derivation = DerivationIndex::from_bytes(graph_entry.descendants[0].1);
let next_entry_pk: PublicKey = MainPubkey::from(self.history_address().owner)
.derive_key(&next_derivation)
.into();
let child = GraphEntryAddress::new(next_entry_pk);
match self
.get_graph_entry_from_network(&child, check_exists)
.await
{
Ok(graph_entry) => Some(graph_entry),
Err(_) => None,
}
}
pub fn get_cached_version_number(&self) -> Option<u64> {
if let Some(trove_version) = &self.cached_version {
if trove_version.trove.is_some() {
return Some(trove_version.version);
}
}
None
}
async fn history_get_graph_entry_and_next_derivation_index(
&self,
graph_entry_address: &GraphEntryAddress,
) -> Result<(GraphEntry, DerivationIndex)> {
println!("DEBUG history_get_graph_entry_and_next_derivation_index()");
let entry = match self
.get_graph_entry_from_network(graph_entry_address, false)
.await
{
Ok(entry) => entry,
Err(e) => {
let msg = format!("Failed to get graph entry from network - {e}");
println!("DEBUG get_graph_entry_from_network() {msg}");
return Err(eyre!("msg"));
}
};
let new_derivation = get_derivation_from_graph_entry(&entry)?;
println!(
"DEBUG returning ({}, {})",
entry.address().to_hex(),
hex::encode(new_derivation.as_bytes())
);
Ok((entry, new_derivation))
}
pub async fn heal_pointer_using_put(
&mut self,
owner_secret_key: SecretKey,
counter: u64,
graphentry_address: GraphEntryAddress,
) -> Result<()> {
println!("DEBUG History::heal_pointer_using_put()");
let history_secret_key =
Self::history_main_secret_key(owner_secret_key).derive_child(self.name.as_bytes());
let history_address = HistoryAddress::new(history_secret_key.public_key());
println!("Updating History at {}", history_address.to_hex());
let history_address = self.history_address();
println!("Healing History at {}", history_address.to_hex());
let pointer_secret_key = Self::history_pointer_secret_key(history_secret_key);
let pointer = Pointer::new(
&pointer_secret_key,
counter,
PointerTarget::GraphEntryAddress(graphentry_address),
);
println!(
"Updating pointer counter to {counter}, target to {}",
graphentry_address.to_hex()
);
let client = self.client.client.clone();
let payment_option = self.client.payment_option();
match retry_until_ok(
1, &"pointer_put()",
(client, pointer, payment_option),
async move |(client, pointer, payment_option)| match client
.pointer_put(pointer, payment_option)
.await
{
Ok(result) => {
println!("Pointer updated");
Ok(result)
}
Err(e) => {
return Err(eyre!("Failed to put pointer: {e:?}"));
}
},
)
.await
{
Ok(_) => {
self.pointer_counter = counter;
self.pointer_target = Some(graphentry_address);
return Ok(());
}
Err(e) => return Err(eyre!("Retries exceeded: {e:?}")),
}
}
pub async fn heal_pointer_using_update(
&mut self,
owner_secret_key: SecretKey,
graphentry_address: GraphEntryAddress,
) -> Result<u64> {
println!("DEBUG History::heal_pointer_using_update()");
let history_secret_key =
Self::history_main_secret_key(owner_secret_key).derive_child(self.name.as_bytes());
let history_address = HistoryAddress::new(history_secret_key.public_key());
println!("Updating History at {}", history_address.to_hex());
let history_address = self.history_address();
println!("Healing History at {}", history_address.to_hex());
let pointer_address = pointer_address_from_history_address(history_address.clone())?;
match get_and_verify_pointer(&self.client, &pointer_address).await {
Ok(pointer) => {
println!("Pointer retrieved with counter {}", pointer.counter());
let pointer_secret_key = Self::history_pointer_secret_key(history_secret_key);
let pointer_target = PointerTarget::GraphEntryAddress(graphentry_address);
println!("Updating pointer target to: {}", pointer_target.to_hex());
let client = self.client.client.clone();
match retry_until_ok(
1, &"pointer_update()",
(client, pointer_secret_key, pointer_target),
async move |(client, pointer_secret_key, pointer_target)| match client
.pointer_update(&pointer_secret_key, pointer_target)
.await
{
Ok(result) => {
println!("Pointer updated");
Ok(result)
}
Err(e) => {
return Err(eyre!("Failed to update pointer: {e:?}"));
}
},
)
.await
{
Ok(_) => {
self.pointer_counter = pointer.counter();
self.pointer_target = Some(graphentry_address);
return Ok(self.pointer_counter);
}
Err(e) => return Err(eyre!("Retries exceeded: {e:?}")),
}
}
Err(e) => return Err(eyre!("DEBUG failed to get history for update!\n{e}")),
}
}
async fn create_next_graph_entry_online(
&self,
history_secret_key: SecretKey,
head_address: GraphEntryAddress,
content: &ArchiveAddress,
) -> Result<(AttoTokens, GraphEntryAddress)> {
println!(
"DEBUG create_next_graph_entry_online() with content {}",
content.to_hex()
);
println!("DEBUG head_address: {}", head_address.to_hex());
let mut head_address = head_address;
loop {
let (parent_entry, new_derivation) = self
.history_get_graph_entry_and_next_derivation_index(&head_address)
.await?;
let new_entry = create_graph_entry(
&history_secret_key,
Some(&parent_entry),
&new_derivation,
*content,
)
.await?;
println!("DEBUG new_entry address: {}", new_entry.address().to_hex());
match self
.client
.client
.graph_entry_put(new_entry, self.client.payment_option())
.await
{
Ok(result) => return Ok(result),
Err(e) => match e {
GraphError::AlreadyExists(existing_address) => {
println!(
"DEBUG new_entry already exists, trying again with that as 'head'"
);
head_address = existing_address
}
_ => {
let msg = format!("Failed graph_entry_put() - {e}");
println!("DEBUG {msg}");
return Err(eyre!("{msg}"));
}
},
}
} }
pub async fn publish_new_version(
&mut self,
owner_secret_key: SecretKey,
trove_address: &ArchiveAddress,
) -> Result<(AttoTokens, u64)> {
let (update_cost, _) = self.update_online(owner_secret_key, *trove_address).await?;
println!("trove_address added to history: {}", trove_address.to_hex());
let version = self.num_versions()?;
self.default_version = Some(version);
self.cached_version = Some(TroveVersion::<T>::new(version, trove_address.clone(), None));
Ok((update_cost, version))
}
pub async fn fetch_version_trove(&mut self, version: Option<u64>) -> Option<T> {
println!(
"DEBUG fetch_version_trove(version: {version:?}) self.cached_version.is_some(): {}",
self.cached_version.is_some()
);
let mut version = if version.is_some() {
version.unwrap()
} else {
0
};
if version == 0 {
if self.default_version.is_some() {
version = self.default_version.unwrap()
} else {
println!("No default_version available to select");
return None;
}
};
let version = if version == LARGEST_VERSION {
0
} else {
version
};
if let Some(trove_version) = &self.cached_version {
if trove_version.version == version && trove_version.trove.is_some() {
return trove_version.trove.clone();
}
}
let data_address = match self.get_trove_address_from_history(version).await {
Ok(data_address) => data_address,
Err(e) => {
println!("select_version() failed to get version {version} from history:\n{e}");
return None;
}
};
let trove: Option<T> = match self.trove_download(data_address).await {
Ok(trove) => Some(trove),
Err(e) => {
println!("select_version() failed to get trove from network:\n{e}");
None
}
};
self.cached_version = Some(TroveVersion::new(version, data_address, trove.clone()));
trove
}
pub fn get_cached_version(&self) -> Option<TroveVersion<T>> {
if let Some(cached_version) = &self.cached_version {
Some(cached_version.clone())
} else {
None
}
}
pub async fn get_trove_address_from_history(&mut self, version: u64) -> Result<ArchiveAddress> {
println!("DEBUG get_trove_address_from_history(version: {version})");
if let Some(trove_version) = &self.cached_version {
if trove_version.version == version {
println!(
"DEBUG get_trove_address_from_history() returning cached trove address: {}",
&trove_version.trove_address
);
return Ok(trove_version.trove_address.clone());
}
};
let ignore_pointer = false;
self.get_version_entry_value(version, ignore_pointer).await
}
}
#[derive(Clone)]
pub struct TroveVersion<ST: Trove<ST> + Clone> {
pub version: u64,
pub trove_address: ArchiveAddress,
pub trove: Option<ST>,
}
impl<ST: Trove<ST> + Clone> TroveVersion<ST> {
pub fn new(version: u64, trove_address: ArchiveAddress, trove: Option<ST>) -> TroveVersion<ST> {
TroveVersion {
version,
trove_address: trove_address,
trove,
}
}
pub fn trove_address(&self) -> ArchiveAddress {
self.trove_address
}
pub fn trove(&self) -> Option<ST> {
match &self.trove {
Some(trove) => Some(trove.clone()),
None => None,
}
}
}
pub fn pointer_address_from_history_address(
history_address: HistoryAddress,
) -> Result<PointerAddress> {
let history_main_public_key: MainPubkey = MainPubkey::new(history_address.owner());
let derivation_index: [u8; 32] = HISTORY_POINTER_DERIVATION_INDEX
.as_bytes()
.try_into()
.unwrap();
let pointer_pk =
history_main_public_key.derive_key(&DerivationIndex::from_bytes(derivation_index));
Ok(PointerAddress::new(pointer_pk.into()))
}
pub async fn get_and_verify_pointer(
client: &DwebClient,
pointer_address: &PointerAddress,
) -> Result<Pointer> {
let operation_label = format!("pointer_get({pointer_address})");
retry_until_ok(
client.api_control.api_tries,
&operation_label,
(client, pointer_address),
async move |(client, pointer_address)| match client
.client
.pointer_get(pointer_address)
.await
{
Ok(pointer) => {
if !pointer.verify_signature() {
let message =
format!("Error - pointer retrieved from network has INVALID SIGNATURE");
println!("{message}");
return Err(eyre!(message));
}
let head_address = match pointer.target() {
PointerTarget::GraphEntryAddress(address) => address,
other => return Err(eyre!("Invalid head address {:?}", other.clone())),
};
println!(
"DEBUG pointer counter: {}, head address: {}",
pointer.counter(),
head_address.to_hex()
);
Ok(pointer)
}
Err(e) => {
let message =
format!("failed to get pointer network address {pointer_address} - {e}",);
println!("{message}");
return Err(eyre!(message));
}
},
)
.await
}