diff --git a/.github/workflows/memcheck.yml b/.github/workflows/memcheck.yml index 4fae7db742..55c5b99c53 100644 --- a/.github/workflows/memcheck.yml +++ b/.github/workflows/memcheck.yml @@ -99,7 +99,7 @@ jobs: mkdir $ANT_DATA_PATH/client ls -l $ANT_DATA_PATH ./target/release/ant --log-output-dest=data-dir file upload --public "./the-test-data.zip" > ./upload_output_second 2>&1 - rg 'Total cost: 0 AttoTokens' ./upload_output_second -c --stats + rg 'All chunks already exist on the network.' ./upload_output_second -c --stats env: ANT_LOG: "all" timeout-minutes: 25 diff --git a/ant-cli/src/commands/file.rs b/ant-cli/src/commands/file.rs index 146133e348..412d4879be 100644 --- a/ant-cli/src/commands/file.rs +++ b/ant-cli/src/commands/file.rs @@ -76,13 +76,17 @@ pub async fn upload(file: &str, public: bool, peers: Vec) -> Result<( // get summary let summary = upload_summary_thread.await?; - if summary.record_count == 0 { + if summary.records_paid == 0 { println!("All chunks already exist on the network."); } else { println!("Successfully uploaded: {file}"); println!("At address: {local_addr}"); info!("Successfully uploaded: {file} at address: {local_addr}"); - println!("Number of chunks uploaded: {}", summary.record_count); + println!("Number of chunks uploaded: {}", summary.records_paid); + println!( + "Number of chunks already paid/uploaded: {}", + summary.records_already_paid + ); println!("Total cost: {} AttoTokens", summary.tokens_spent); } info!("Summary for upload of file {file} at {local_addr:?}: {summary:?}"); diff --git a/ant-cli/src/commands/register.rs b/ant-cli/src/commands/register.rs index 5598fc0544..ce840c28ff 100644 --- a/ant-cli/src/commands/register.rs +++ b/ant-cli/src/commands/register.rs @@ -99,7 +99,7 @@ pub async fn create(name: &str, value: &str, public: bool, peers: Vec } let summary = upload_summary_thread.await?; - if summary.record_count == 0 { + if summary.records_paid == 0 { println!("✅ The register already exists on the network at address: {address}."); println!("No tokens were spent."); } else { diff --git a/ant-cli/src/utils.rs b/ant-cli/src/utils.rs index 5f031a3c24..19f02878f9 100644 --- a/ant-cli/src/utils.rs +++ b/ant-cli/src/utils.rs @@ -20,6 +20,7 @@ pub fn collect_upload_summary( let stats_thread = tokio::spawn(async move { let mut tokens_spent: Amount = Amount::from(0); let mut record_count = 0; + let mut records_already_paid = 0; loop { tokio::select! { @@ -27,7 +28,8 @@ pub fn collect_upload_summary( match event { Some(ClientEvent::UploadComplete(upload_summary)) => { tokens_spent += upload_summary.tokens_spent; - record_count += upload_summary.record_count; + record_count += upload_summary.records_paid; + records_already_paid += upload_summary.records_already_paid; } None => break, } @@ -41,14 +43,16 @@ pub fn collect_upload_summary( match event { ClientEvent::UploadComplete(upload_summary) => { tokens_spent += upload_summary.tokens_spent; - record_count += upload_summary.record_count; + record_count += upload_summary.records_paid; + records_already_paid += upload_summary.records_already_paid; } } } UploadSummary { tokens_spent, - record_count, + records_paid: record_count, + records_already_paid, } }); diff --git a/autonomi/src/client/data/mod.rs b/autonomi/src/client/data/mod.rs index d336c60dfc..5fc1eb3290 100644 --- a/autonomi/src/client/data/mod.rs +++ b/autonomi/src/client/data/mod.rs @@ -222,7 +222,7 @@ impl Client { // Pay for all chunks let xor_names: Vec<_> = chunks.iter().map(|chunk| *chunk.name()).collect(); info!("Paying for {} addresses", xor_names.len()); - let receipt = self + let (receipt, skipped_payments) = self .pay_for_content_addrs(xor_names.into_iter(), payment_option) .await .inspect_err(|err| error!("Error paying for data: {err:?}"))?; @@ -244,7 +244,7 @@ impl Client { return Err(last_chunk_fail.1); } - let record_count = chunks.len(); + let record_count = chunks.len().saturating_sub(skipped_payments); // Reporting if let Some(channel) = self.client_event_sender.as_ref() { @@ -254,7 +254,8 @@ impl Client { .sum::(); let summary = UploadSummary { - record_count, + records_paid: record_count, + records_already_paid: skipped_payments, tokens_spent, }; if let Err(err) = channel.send(ClientEvent::UploadComplete(summary)).await { diff --git a/autonomi/src/client/data/public.rs b/autonomi/src/client/data/public.rs index 8b434c5a98..fde2908964 100644 --- a/autonomi/src/client/data/public.rs +++ b/autonomi/src/client/data/public.rs @@ -59,7 +59,7 @@ impl Client { // Pay for all chunks + data map chunk info!("Paying for {} addresses", xor_names.len()); - let receipt = self + let (receipt, skipped_payments) = self .pay_for_content_addrs(xor_names.into_iter(), payment_option) .await .inspect_err(|err| error!("Error paying for data: {err:?}"))?; @@ -87,7 +87,7 @@ impl Client { return Err(last_chunk_fail.1); } - let record_count = chunks.len() + 1; + let record_count = (chunks.len() + 1) - skipped_payments; // Reporting if let Some(channel) = self.client_event_sender.as_ref() { @@ -97,7 +97,8 @@ impl Client { .sum::(); let summary = UploadSummary { - record_count, + records_paid: record_count, + records_already_paid: skipped_payments, tokens_spent, }; if let Err(err) = channel.send(ClientEvent::UploadComplete(summary)).await { diff --git a/autonomi/src/client/files/archive.rs b/autonomi/src/client/files/archive.rs index 18ecd1c735..19e9642191 100644 --- a/autonomi/src/client/files/archive.rs +++ b/autonomi/src/client/files/archive.rs @@ -166,6 +166,13 @@ impl Client { let bytes = archive .to_bytes() .map_err(|e| PutError::Serialization(format!("Failed to serialize archive: {e:?}")))?; + + #[cfg(feature = "loud")] + println!( + "Uploading private archive referencing {} files", + archive.map().len() + ); + let result = self.data_put(bytes, payment_option).await; debug!("Uploaded private archive {archive:?} to the network and address is {result:?}"); result diff --git a/autonomi/src/client/files/archive_public.rs b/autonomi/src/client/files/archive_public.rs index dd7cb046e2..bf8cb44bff 100644 --- a/autonomi/src/client/files/archive_public.rs +++ b/autonomi/src/client/files/archive_public.rs @@ -158,6 +158,13 @@ impl Client { let bytes = archive .to_bytes() .map_err(|e| PutError::Serialization(format!("Failed to serialize archive: {e:?}")))?; + + #[cfg(feature = "loud")] + println!( + "Uploading public archive referencing {} files", + archive.map().len() + ); + let result = self.data_put_public(bytes, wallet.into()).await; debug!("Uploaded archive {archive:?} to the network and the address is {result:?}"); result diff --git a/autonomi/src/client/graph.rs b/autonomi/src/client/graph.rs index cf78903209..b9fb0fcca2 100644 --- a/autonomi/src/client/graph.rs +++ b/autonomi/src/client/graph.rs @@ -68,7 +68,7 @@ impl Client { // pay for the transaction let xor_name = address.xorname(); debug!("Paying for transaction at address: {address:?}"); - let payment_proofs = self + let (payment_proofs, skipped_payments) = self .pay(std::iter::once(*xor_name), wallet) .await .inspect_err(|err| { @@ -124,7 +124,8 @@ impl Client { // send client event if let Some(channel) = self.client_event_sender.as_ref() { let summary = UploadSummary { - record_count: 1, + records_paid: 1usize.saturating_sub(skipped_payments), + records_already_paid: skipped_payments, tokens_spent: price.as_atto(), }; if let Err(err) = channel.send(ClientEvent::UploadComplete(summary)).await { diff --git a/autonomi/src/client/mod.rs b/autonomi/src/client/mod.rs index df394611e2..0c8fd811e5 100644 --- a/autonomi/src/client/mod.rs +++ b/autonomi/src/client/mod.rs @@ -372,6 +372,10 @@ pub enum ClientEvent { /// Summary of an upload operation. #[derive(Debug, Clone)] pub struct UploadSummary { - pub record_count: usize, + /// Records that were uploaded to the network + pub records_paid: usize, + /// Records that were already paid for so were not re-uploaded + pub records_already_paid: usize, + /// Total cost of the upload pub tokens_spent: Amount, } diff --git a/autonomi/src/client/payment.rs b/autonomi/src/client/payment.rs index 29a8f11576..b6dc4c936e 100644 --- a/autonomi/src/client/payment.rs +++ b/autonomi/src/client/payment.rs @@ -5,6 +5,8 @@ use ant_evm::{AttoTokens, EncodedPeerId, EvmWallet, ProofOfPayment}; use std::collections::HashMap; use xor_name::XorName; +use super::utils::AlreadyPaidAddressesCount; + /// Contains the proof of payments for each XOR address and the amount paid pub type Receipt = HashMap; @@ -65,13 +67,13 @@ impl Client { &self, content_addrs: impl Iterator + Clone, payment_option: PaymentOption, - ) -> Result { + ) -> Result<(Receipt, AlreadyPaidAddressesCount), PayError> { match payment_option { PaymentOption::Wallet(wallet) => { - let receipt = self.pay(content_addrs, &wallet).await?; - Ok(receipt) + let (receipt, skipped) = self.pay(content_addrs, &wallet).await?; + Ok((receipt, skipped)) } - PaymentOption::Receipt(receipt) => Ok(receipt), + PaymentOption::Receipt(receipt) => Ok((receipt, 0)), } } } diff --git a/autonomi/src/client/pointer.rs b/autonomi/src/client/pointer.rs index a5f95e18f8..eb65128eae 100644 --- a/autonomi/src/client/pointer.rs +++ b/autonomi/src/client/pointer.rs @@ -62,7 +62,7 @@ impl Client { // pay for the pointer storage let xor_name = *address.xorname(); debug!("Paying for pointer at address: {address:?}"); - let payment_proofs = self + let (payment_proofs, _skipped_payments) = self .pay(std::iter::once(xor_name), wallet) .await .inspect_err(|err| { diff --git a/autonomi/src/client/registers.rs b/autonomi/src/client/registers.rs index d28fbacf2f..ee8514e7b5 100644 --- a/autonomi/src/client/registers.rs +++ b/autonomi/src/client/registers.rs @@ -319,12 +319,12 @@ impl Client { let reg_xor = address.xorname(); debug!("Paying for register at address: {address}"); - let payment_proofs = self + let (payment_proofs, skipped_payments) = self .pay(std::iter::once(reg_xor), wallet) .await .inspect_err(|err| { - error!("Failed to pay for register at address: {address} : {err}") - })?; + error!("Failed to pay for register at address: {address} : {err}") + })?; let (proof, price) = if let Some((proof, price)) = payment_proofs.get(®_xor) { (proof, price) } else { @@ -373,7 +373,8 @@ impl Client { if let Some(channel) = self.client_event_sender.as_ref() { let summary = UploadSummary { - record_count: 1, + records_paid: 1usize.saturating_sub(skipped_payments), + records_already_paid: skipped_payments, tokens_spent: price.as_atto(), }; if let Err(err) = channel.send(ClientEvent::UploadComplete(summary)).await { diff --git a/autonomi/src/client/utils.rs b/autonomi/src/client/utils.rs index 2a8eb70e3e..ae0cac0b48 100644 --- a/autonomi/src/client/utils.rs +++ b/autonomi/src/client/utils.rs @@ -27,6 +27,8 @@ use super::{ }; use crate::self_encryption::DataMapLevel; +pub type AlreadyPaidAddressesCount = usize; + impl Client { /// Fetch and decrypt all chunks in the data map. pub(crate) async fn fetch_from_data_map(&self, data_map: &DataMap) -> Result { @@ -167,7 +169,7 @@ impl Client { &self, content_addrs: impl Iterator + Clone, wallet: &EvmWallet, - ) -> Result { + ) -> Result<(Receipt, AlreadyPaidAddressesCount), PayError> { let number_of_content_addrs = content_addrs.clone().count(); let quotes = self.get_store_quotes(content_addrs).await?; @@ -197,7 +199,7 @@ impl Client { let receipt = receipt_from_store_quotes(quotes); - Ok(receipt) + Ok((receipt, skipped_chunks)) } } diff --git a/autonomi/src/client/vault.rs b/autonomi/src/client/vault.rs index d57c197fc6..45fa3e2256 100644 --- a/autonomi/src/client/vault.rs +++ b/autonomi/src/client/vault.rs @@ -192,7 +192,7 @@ impl Client { info!("Writing to vault at {scratch_address:?}",); let record = if is_new { - let receipt = self + let (receipt, _skipped_payments) = self .pay_for_content_addrs(std::iter::once(scratch.xorname()), payment_option) .await .inspect_err(|err| {