dweb/helpers/
graph_entry.rs

1/*
2Copyright (c) 2024-2025 Mark Hughes
3
4This program is free software: you can redistribute it and/or modify
5it under the terms of the GNU Affero General Public License as published by
6the Free Software Foundation, either version 3 of the License, or
7(at your option) any later version.
8
9This program is distributed in the hope that it will be useful,
10but WITHOUT ANY WARRANTY; without even the implied warranty of
11MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12GNU Affero General Public License for more details.
13
14You should have received a copy of the GNU Affero General Public License
15along with this program. If not, see <https://www.gnu.org/licenses/>.
16*/
17
18use autonomi::files::archive_public::ArchiveAddress;
19use blsttc::PublicKey;
20use color_eyre::{eyre::eyre, Result};
21
22use autonomi::client::key_derivation::{DerivationIndex, MainSecretKey};
23use autonomi::client::vault::VaultSecretKey as SecretKey;
24use autonomi::client::Client;
25use autonomi::{graph::GraphError, GraphEntry, GraphEntryAddress};
26
27use crate::history::HistoryValue;
28
29/// Print a summary for a GraphEntry. If main_owner.is_some() the output
30/// will use this to show the addresses of parent and descendents instead
31/// of their PublicKeys.
32pub fn debug_print_graph_entry(
33    intro: &str,
34    graph_entry: &GraphEntry,
35    main_owner: Option<MainSecretKey>,
36) {
37    let showing_addresses = if main_owner.is_some() {
38        "\n       (showing GraphEntry addresses of parents/descendents)"
39    } else {
40        ""
41    };
42
43    println!(
44        "DEBUG {intro} graph entry with address {}{showing_addresses}",
45        graph_entry.address().to_hex()
46    );
47    let parents = if graph_entry.parents.len() > 0 {
48        if main_owner.is_none() {
49            &graph_entry.parents[0].to_hex() // PublicKey
50        } else {
51            let parent_public_key = graph_entry.parents[0];
52            let address = GraphEntryAddress::new(parent_public_key);
53            &address.to_hex()
54        }
55    } else {
56        ""
57    };
58
59    let descendents = if graph_entry.descendants.len() > 0 {
60        if main_owner.is_none() {
61            &graph_entry.descendants[0].0.to_hex() // PublicKey
62        } else {
63            let derivation_index = get_derivation_from_graph_entry(&graph_entry);
64            let descendent_public_key = main_owner
65                .unwrap()
66                .derive_key(&derivation_index.unwrap())
67                .public_key();
68
69            // let descendent_public_key = graph_entry.descendants[0].0;
70            let address = GraphEntryAddress::new(descendent_public_key.into());
71            &address.to_hex()
72        }
73    } else {
74        ""
75    };
76
77    println!(
78        "\n       owner      : {}\n       parents    : [{}]\n       content    : {}\n       descendents: [{}])",
79        graph_entry.owner.to_hex(),
80        parents,
81        hex::encode(&graph_entry.content),
82        descendents
83    );
84}
85
86/// Get a GraphEntry from the network
87pub async fn graph_entry_get(
88    client: &Client,
89    graph_entry_address: &GraphEntryAddress,
90    check_exists: bool,
91) -> Result<GraphEntry> {
92    // println!("DEBUG graph_entry_get() {}", graph_entry_address.to_hex());
93
94    if check_exists {
95        match client
96            .graph_entry_check_existence(graph_entry_address)
97            .await
98        {
99            Ok(exists) => {
100                if !exists {
101                    println!("DEBUG GraphEntry does not exist");
102                    return Err(eyre!("GraphEntry does not exist"));
103                } else {
104                    println!("DEBUG GraphEntry exists");
105                }
106            }
107            Err(_e) => {}
108        };
109    };
110
111    match client.graph_entry_get(graph_entry_address).await {
112        Ok(entry) => {
113            // debug_print_graph_entry("returning", &entry, None);
114            Ok(entry)
115        }
116        Err(GraphError::Fork(entries)) => {
117            println!("Forked history, {entries:?} found. Using the smallest derivation index for the next entry");
118            let (entry_by_smallest_derivation, _) = if let Some(entry) = entries
119                .into_iter()
120                .filter_map(|e| {
121                    get_derivation_from_graph_entry(&e)
122                        .ok()
123                        .map(|derivation| (e, derivation))
124                })
125                .min_by(|a, b| a.1.cmp(&b.1))
126            {
127                // debug_print_graph_entry("returning", &entry.0, None);
128                entry
129            } else {
130                let msg = format!(
131                    "No valid descendants found for forked entry at {graph_entry_address:?}"
132                );
133                println!("{msg}");
134                return Err(eyre!(msg));
135            };
136            // debug_print_graph_entry(
137            //     "returning smallest by derivation ",
138            //     &entry_by_smallest_derivation,
139            //     None,
140            // );
141            Ok(entry_by_smallest_derivation)
142        }
143        Err(e) => {
144            let msg = format!("failed to get graph entry - {e}");
145            // println!("DEBUG graph_entry_get() {msg}");
146            return Err(eyre!(msg));
147        }
148    }
149}
150
151/// Create a new entry with the new value
152pub async fn create_graph_entry(
153    history_secret_key: &SecretKey,
154    parent_entry: Option<&GraphEntry>,
155    new_derivation: &DerivationIndex,
156    new_value: ArchiveAddress,
157) -> Result<GraphEntry> {
158    println!("DEBUG create_graph_entry()");
159
160    let history_secret_key = MainSecretKey::new(history_secret_key.clone());
161    let parents = if let Some(parent_entry) = parent_entry {
162        vec![parent_entry.owner]
163    } else {
164        vec![]
165    };
166
167    let entry_secret_key: SecretKey = if parent_entry.is_none() {
168        history_secret_key.clone().into()
169    } else {
170        history_secret_key
171            .clone()
172            .derive_key(&new_derivation)
173            .into()
174    };
175    let next_public_key = history_secret_key.public_key().derive_key(new_derivation);
176    let next_derivation = DerivationIndex::random(&mut rand::thread_rng());
177    let descendants: Vec<(PublicKey, [u8; 32])> =
178        vec![(next_public_key.into(), next_derivation.into_bytes())];
179
180    println!(
181        "DEBUG entry_secret_key: {}",
182        hex::encode(&history_secret_key.to_bytes())
183    );
184    println!(
185        "DEBUG next_public_key : {}",
186        hex::encode(&next_public_key.to_bytes())
187    );
188    let parents_str = if parents.len() > 0 {
189        &parents[0].to_hex()
190    } else {
191        ""
192    };
193    println!("DEBUG creating GraphEntry::new(\n       owner      : {}\n       parents    : [{}]\n       content    : {}\n       descendents: [{}])",
194        entry_secret_key.public_key().to_hex(), parents_str, new_value.to_hex(), descendants[0].0.to_hex() );
195
196    let new_value: HistoryValue = new_value.xorname().0;
197    let next_entry = GraphEntry::new(&entry_secret_key, parents, new_value, descendants);
198    // debug_print_graph_entry(
199    //     "returning created next_entry",
200    //     &next_entry,
201    //     Some(history_secret_key),
202    // );
203
204    Ok(next_entry)
205}
206
207/// Get a graph entry and the next derivation index (from its first descendent)
208/// In normal circumstances, there is only one entry with one descendant, yielding ONE entry and ONE derivation index
209/// In the case of a fork or a corrupt History, the smallest derivation index among all the entries descendants is chosen
210/// We chose here to deal with the errors instead of erroring out to allow users to solve Fork and Corrupt issues by
211/// updating the register
212pub async fn get_graph_entry_and_next_derivation_index(
213    client: &Client,
214    graph_entry_addr: &GraphEntryAddress,
215) -> Result<(GraphEntry, DerivationIndex)> {
216    let entry = match client.graph_entry_get(graph_entry_addr).await {
217        Ok(e) => e,
218        Err(GraphError::Fork(entries)) => {
219            println!("DEBUG Forked register, multiple entries found: {entries:?}, choosing the one with the smallest derivation index for the next entry");
220            let (entry_by_smallest_derivation, _) = entries
221                .into_iter()
222                .filter_map(|e| {
223                    get_derivation_from_graph_entry(&e)
224                        .ok()
225                        .map(|derivation| (e, derivation))
226                })
227                .min_by(|a, b| a.1.cmp(&b.1))
228                .ok_or(eyre!(
229                    "no valid descendants found for FORKED entry at {graph_entry_addr:?}"
230                ))?;
231            entry_by_smallest_derivation
232        }
233        Err(err) => return Err(err.into()),
234    };
235    let new_derivation = get_derivation_from_graph_entry(&entry)?;
236    Ok((entry, new_derivation))
237}
238
239/// Get the derivation index of the first descendent
240pub fn get_derivation_from_graph_entry(entry: &GraphEntry) -> Result<DerivationIndex> {
241    let graph_entry_addr = GraphEntryAddress::new(entry.owner);
242    let d = match entry.descendants.as_slice() {
243        [d] => d.1,
244        // TODO maybe just use first descendent rather than error?
245        _ => {
246            let msg =    format!("History graph_entry_addr: {:?} is corrupted, expected one descendant but got {}: {:?}",
247            graph_entry_addr,
248            entry.descendants.len(),
249            entry.descendants);
250            println!("DEBUG get_derivation_from_graph_entry() failed: {msg}");
251            return Err(eyre!(msg));
252        }
253    };
254    Ok(DerivationIndex::from_bytes(d))
255}