autonomi/client/high_level/vault/
mod.rs1pub mod key;
10pub mod user_data;
11
12pub use key::{VaultSecretKey, vault_derive_key};
13pub use user_data::UserData;
14
15use crate::client::config::FILE_UPLOAD_BATCH_SIZE;
16use crate::client::data_types::scratchpad::ScratchpadError;
17use crate::client::key_derivation::{DerivationIndex, MainSecretKey};
18use crate::client::payment::PaymentOption;
19use crate::client::quote::CostError;
20use crate::client::{Client, GetError};
21use crate::graph::GraphError;
22use crate::utils::process_tasks_with_max_concurrency;
23use ant_evm::{AttoTokens, U256};
24use ant_protocol::Bytes;
25use ant_protocol::storage::{
26 GraphContent, GraphEntry, GraphEntryAddress, Scratchpad, ScratchpadAddress,
27};
28use bls::PublicKey;
29use std::hash::{DefaultHasher, Hash, Hasher};
30use tracing::info;
31
32pub type VaultContentType = u64;
38
39pub const MAX_CONTENT_PER_SCRATCHPAD: usize = Scratchpad::MAX_SIZE - 1024;
41
42pub const NUM_OF_SCRATCHPADS_PER_GRAPH_ENTRY: usize = 1_000;
45
46pub const VAULT_HEAD_DERIVATION_INDEX: [u8; 32] = [0; 32];
49
50pub fn vault_content_type_from_app_name<T: Hash>(s: T) -> VaultContentType {
52 let mut hasher = DefaultHasher::new();
53 s.hash(&mut hasher);
54 hasher.finish()
55}
56
57#[deprecated(
59 since = "0.6.0",
60 note = "Use `vault_content_type_from_app_name` instead"
61)]
62pub fn app_name_to_vault_content_type<T: Hash>(s: T) -> VaultContentType {
63 vault_content_type_from_app_name(s)
64}
65
66#[derive(Debug, thiserror::Error)]
67pub enum VaultError {
68 #[error("Vault Scratchpad related error: {0}")]
69 Scratchpad(#[from] ScratchpadError),
70 #[error("Vault GraphEntry related error: {0}")]
71 GraphEntry(#[from] GraphError),
72 #[error("Vault Cost related error: {0}")]
73 Cost(#[from] CostError),
74 #[error("Protocol: {0}")]
75 Protocol(#[from] ant_protocol::Error),
76 #[error("Vault doesn't have enough graph descendants: {0}")]
77 VaultNotEnoughGraphDescendants(String),
78 #[error("Vault with empty content")]
79 VaultWithZeroContentSize,
80}
81
82impl Client {
83 pub async fn vault_get(
87 &self,
88 secret_key: &VaultSecretKey,
89 ) -> Result<(Bytes, VaultContentType), VaultError> {
90 info!("Fetching and decrypting vault...");
91 let main_secret_key = MainSecretKey::new(secret_key.clone());
92 let public_key = main_secret_key
93 .derive_key(&DerivationIndex::from_bytes(VAULT_HEAD_DERIVATION_INDEX))
94 .public_key();
95
96 let mut cur_graph_entry_addr = GraphEntryAddress::new(public_key.into());
97 let mut decrypted_full_text = vec![];
98 let mut content_type = 0;
99 let mut has_end_reached = false;
100
101 while !has_end_reached {
102 let graph_entry = self.graph_entry_get(&cur_graph_entry_addr).await?;
103
104 match graph_entry.descendants.split_first() {
106 Some((&(first, _), rest)) => {
107 cur_graph_entry_addr = GraphEntryAddress::new(first);
108 let scratchpad_addresses = rest.to_vec();
109
110 let (decrypt_data, cur_content_type, is_end_reached) = self
111 .fetch_scratchpads_of_one_graph_entry_and_decrypt(
112 &main_secret_key,
113 scratchpad_addresses,
114 )
115 .await?;
116 decrypted_full_text.push(decrypt_data);
117 content_type = cur_content_type;
118 has_end_reached = is_end_reached;
119 }
120 None => {
121 let msg = format!(
122 "Vault's GraphEntry at {cur_graph_entry_addr:?} only has {} descendants.",
123 graph_entry.descendants.len()
124 );
125 return Err(VaultError::VaultNotEnoughGraphDescendants(msg));
126 }
127 }
128 }
129
130 debug!("vault data is successfully fetched and decrypted");
131 Ok((Bytes::from(decrypted_full_text.concat()), content_type))
132 }
133
134 pub async fn vault_cost(
138 &self,
139 owner: &VaultSecretKey,
140 max_size: u64,
141 ) -> Result<AttoTokens, VaultError> {
142 if max_size == 0 {
143 return Err(VaultError::VaultWithZeroContentSize);
144 }
145
146 info!("Getting cost for vault");
147 let public_key = MainSecretKey::new(owner.clone())
148 .derive_key(&DerivationIndex::from_bytes(VAULT_HEAD_DERIVATION_INDEX))
149 .public_key();
150 let graph_entry_cost = self.graph_entry_cost(&public_key.into()).await?;
151 if graph_entry_cost.is_zero() {
152 Ok(graph_entry_cost)
154 } else {
155 let scratchpad_cost = self.scratchpad_cost(&public_key.into()).await?;
156
157 let num_of_scratchpads = max_size / MAX_CONTENT_PER_SCRATCHPAD as u64 + 1;
158 let num_of_graph_entry =
159 num_of_scratchpads / NUM_OF_SCRATCHPADS_PER_GRAPH_ENTRY as u64 + 1;
160
161 let total_cost = U256::from(num_of_graph_entry) * graph_entry_cost.as_atto()
162 + U256::from(num_of_scratchpads) * scratchpad_cost.as_atto();
163 Ok(AttoTokens::from_atto(total_cost))
164 }
165 }
166
167 pub async fn vault_put(
173 &self,
174 data: Bytes,
175 payment_option: PaymentOption,
176 secret_key: &VaultSecretKey,
177 content_type: VaultContentType,
178 ) -> Result<AttoTokens, VaultError> {
179 if data.is_empty() {
180 return Err(VaultError::VaultWithZeroContentSize);
181 }
182
183 info!("Writing {} bytes to vault ...", data.len());
184 let mut total_cost = AttoTokens::zero();
185 let main_secret_key = MainSecretKey::new(secret_key.clone());
186
187 let (mut cur_free_graphentry_derivation, mut scratchpad_derivations) = self
189 .vault_claimed_capacity(
190 &main_secret_key,
191 DerivationIndex::from_bytes(VAULT_HEAD_DERIVATION_INDEX),
192 )
193 .await?;
194
195 let contents = vault_split_bytes(data);
196
197 info!(
198 "Current capacity is {}, meanwhile requiring {}",
199 scratchpad_derivations.len(),
200 contents.len()
201 );
202
203 while scratchpad_derivations.len() < contents.len() {
207 let (new_free_graphentry_derivation, new_scratchpad_derivations, graph_cost) = self
208 .vault_expand_capacity(
209 &main_secret_key,
210 &cur_free_graphentry_derivation,
211 payment_option.clone(),
212 )
213 .await?;
214 cur_free_graphentry_derivation = new_free_graphentry_derivation;
215 scratchpad_derivations.extend(&new_scratchpad_derivations);
216 total_cost = AttoTokens::from_atto(total_cost.as_atto() + graph_cost.as_atto());
217 }
218
219 let update_futures: Vec<_> = contents
221 .into_iter()
222 .enumerate()
223 .map(|(i, content)| {
224 let sp_secret_key = main_secret_key
225 .derive_key(&DerivationIndex::from_bytes(scratchpad_derivations[i].1));
226 let client = self.clone();
227 let payment_option_clone = payment_option.clone();
228
229 async move {
230 let target_addr = ScratchpadAddress::new(sp_secret_key.public_key().into());
231 let already_exists = self.scratchpad_check_existence(&target_addr).await?;
232
233 if already_exists {
234 info!(
235 "Updating Scratchpad at {target_addr:?} with content of {} bytes",
236 content.len()
237 );
238 match client
239 .scratchpad_update(&sp_secret_key.clone().into(), content_type, &content)
240 .await
241 {
242 Ok(()) => {
243 info!(
244 "Updated Scratchpad at {target_addr:?} with content of {} bytes",
245 content.len()
246 );
247 Ok(None)
248 }
249 Err(err) => Err(err.into()),
250 }
251 } else {
252 info!("Creating Scratchpad at {target_addr:?}");
253 let (price, addr) = client
254 .scratchpad_create(
255 &sp_secret_key.into(),
256 content_type,
257 &content,
258 payment_option_clone,
259 )
260 .await?;
261 info!("Created Scratchpad at {addr:?} with cost of {price:?}");
262 Ok(Some(price))
263 }
264 }
265 })
266 .collect();
267
268 let update_results =
269 process_tasks_with_max_concurrency(update_futures, *FILE_UPLOAD_BATCH_SIZE).await;
270
271 for result in update_results {
273 match result {
274 Ok(Some(price)) => {
275 total_cost = AttoTokens::from_atto(total_cost.as_atto() + price.as_atto());
276 }
277 Ok(None) => (),
278 Err(e) => return Err(e),
279 }
280 }
281
282 Ok(total_cost)
283 }
284
285 pub async fn vault_expand_capacity(
291 &self,
292 main_secret_key: &MainSecretKey,
293 cur_graphentry_derivation: &DerivationIndex,
294 payment_option: PaymentOption,
295 ) -> Result<(DerivationIndex, Vec<(PublicKey, GraphContent)>, AttoTokens), VaultError> {
296 let own_secret_key = main_secret_key.derive_key(cur_graphentry_derivation);
297
298 let parents = vec![];
300 let initial_value = [0u8; 32];
302
303 let new_graphentry_derivation = DerivationIndex::random(&mut rand::thread_rng());
305 let public_key: PublicKey = main_secret_key
306 .derive_key(&new_graphentry_derivation)
307 .public_key()
308 .into();
309 let mut descendants = vec![(public_key, new_graphentry_derivation.into_bytes())];
310
311 descendants.extend((0..NUM_OF_SCRATCHPADS_PER_GRAPH_ENTRY).map(|_| {
313 let derivation_index = DerivationIndex::random(&mut rand::thread_rng());
314 let public_key: PublicKey = main_secret_key
315 .derive_key(&derivation_index)
316 .public_key()
317 .into();
318 (public_key, derivation_index.into_bytes())
319 }));
320
321 let graph_entry = GraphEntry::new(
322 &own_secret_key.into(),
323 parents,
324 initial_value,
325 descendants.clone(),
326 );
327
328 let (graph_cost, _addr) = self.graph_entry_put(graph_entry, payment_option).await?;
330
331 let scratchpad_derivations = descendants.split_off(1);
332 Ok((
333 new_graphentry_derivation,
334 scratchpad_derivations,
335 graph_cost,
336 ))
337 }
338
339 pub async fn vault_claimed_capacity(
345 &self,
346 main_secret_key: &MainSecretKey,
347 mut cur_free_graphentry_derivation: DerivationIndex,
348 ) -> Result<(DerivationIndex, Vec<(PublicKey, GraphContent)>), VaultError> {
349 let mut scratchpad_derivations = vec![];
350 loop {
351 let public_key = main_secret_key
352 .derive_key(&cur_free_graphentry_derivation)
353 .public_key();
354 let cur_graph_entry_addr = GraphEntryAddress::new(public_key.into());
355
356 match self.graph_entry_get(&cur_graph_entry_addr).await {
357 Ok(entry) => {
358 if entry.descendants.len() <= NUM_OF_SCRATCHPADS_PER_GRAPH_ENTRY {
363 let msg = format!(
364 "Vault's GraphEntry at {cur_graph_entry_addr:?} only has {} descendants.",
365 entry.descendants.len()
366 );
367 return Err(VaultError::VaultNotEnoughGraphDescendants(msg));
368 }
369 cur_free_graphentry_derivation =
370 DerivationIndex::from_bytes(entry.descendants[0].1);
371 scratchpad_derivations.extend(&entry.descendants[1..]);
372 }
373 Err(GraphError::GetError(GetError::RecordNotFound)) => {
374 info!(
376 "vault capacity is successfully fetched, with {} scratchpads",
377 scratchpad_derivations.len()
378 );
379 return Ok((cur_free_graphentry_derivation, scratchpad_derivations));
380 }
381 Err(err) => {
382 return Err(err.into());
383 }
384 }
385 }
386 }
387
388 async fn fetch_scratchpads_of_one_graph_entry_and_decrypt(
389 &self,
390 main_secret_key: &MainSecretKey,
391 scratchpad_addresses: Vec<(PublicKey, [u8; 32])>,
392 ) -> Result<(Bytes, VaultContentType, bool), VaultError> {
393 let mut decrypted_full_text = vec![];
394 let mut content_type = 0;
395 let mut has_end_reached = false;
396 for (pub_key, derive_bytes) in scratchpad_addresses {
398 let addr = ScratchpadAddress::new(pub_key);
399 let secret_key = main_secret_key.derive_key(&DerivationIndex::from_bytes(derive_bytes));
400
401 let sp = self.scratchpad_get(&addr).await?;
402 content_type = sp.data_encoding();
403 let decrypt_data = sp.decrypt_data(&secret_key.into())?;
404 decrypted_full_text.push(decrypt_data);
405 if sp.encrypted_data().len() < MAX_CONTENT_PER_SCRATCHPAD {
406 has_end_reached = true;
407 break;
408 }
409 }
410
411 Ok((
412 Bytes::from(decrypted_full_text.concat()),
413 content_type,
414 has_end_reached,
415 ))
416 }
417
418 #[deprecated(since = "0.2.0", note = "Use `vault_get` instead")]
420 pub async fn fetch_and_decrypt_vault(
421 &self,
422 secret_key: &VaultSecretKey,
423 ) -> Result<(Bytes, VaultContentType), VaultError> {
424 self.vault_get(secret_key).await
425 }
426
427 #[deprecated(since = "0.2.0", note = "Use `vault_put` instead")]
429 pub async fn write_bytes_to_vault(
430 &self,
431 data: Bytes,
432 payment_option: PaymentOption,
433 secret_key: &VaultSecretKey,
434 content_type: VaultContentType,
435 ) -> Result<AttoTokens, VaultError> {
436 self.vault_put(data, payment_option, secret_key, content_type)
437 .await
438 }
439}
440
441pub fn vault_split_bytes(input: Bytes) -> Vec<Bytes> {
442 let mut contents = Vec::new();
443 let mut offset = 0;
444
445 while offset < input.len() {
446 let end = (offset + MAX_CONTENT_PER_SCRATCHPAD).min(input.len());
447 contents.push(input.slice(offset..end));
448 offset = end;
449 }
450
451 contents
452}