From 9acc3cfc3a8deb6a1f318d2028d55e2cd7c67fb8 Mon Sep 17 00:00:00 2001 From: Oscar Pepper Date: Wed, 19 Feb 2025 07:55:00 +0000 Subject: [PATCH 01/26] added sync status api --- Cargo.lock | 1 + pepper-sync/Cargo.toml | 3 +++ pepper-sync/src/scan.rs | 4 ++-- pepper-sync/src/wallet.rs | 23 +++++++++++++++++++++++ zingolib/src/commands.rs | 26 ++++++++++++++++++++------ 5 files changed, 49 insertions(+), 8 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0c16d41c2..de9575b3a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2289,6 +2289,7 @@ dependencies = [ "crossbeam-channel", "futures", "incrementalmerkletree", + "json", "memuse", "orchard", "rayon", diff --git a/pepper-sync/Cargo.toml b/pepper-sync/Cargo.toml index f33c0038f..d8bbd02f4 100644 --- a/pepper-sync/Cargo.toml +++ b/pepper-sync/Cargo.toml @@ -45,6 +45,9 @@ rayon.workspace = true # Error handling thiserror.workspace = true +# JSON +json.workspace = true + # temp zcash_address.workspace = true sha2.workspace = true diff --git a/pepper-sync/src/scan.rs b/pepper-sync/src/scan.rs index 35290d17b..52619278b 100644 --- a/pepper-sync/src/scan.rs +++ b/pepper-sync/src/scan.rs @@ -135,12 +135,12 @@ where .first() .expect("compacts blocks should not be empty") .height - != scan_range.block_range().start.into() + != u64::from(scan_range.block_range().start) || compact_blocks .last() .expect("compacts blocks should not be empty") .height - != (scan_range.block_range().end - 1).into() + != u64::from(scan_range.block_range().end - 1) { panic!("compact blocks do not match scan range!") } diff --git a/pepper-sync/src/wallet.rs b/pepper-sync/src/wallet.rs index 63e4c7cc2..3caf6ce87 100644 --- a/pepper-sync/src/wallet.rs +++ b/pepper-sync/src/wallet.rs @@ -218,6 +218,7 @@ pub struct SyncStatus { pub percentage_outputs_scanned: f32, } +// TODO: complete display, scan ranges in raw form are too verbose impl std::fmt::Display for SyncStatus { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!( @@ -231,6 +232,28 @@ impl std::fmt::Display for SyncStatus { } } +impl From for json::JsonValue { + fn from(value: SyncStatus) -> Self { + let scan_ranges: Vec = value + .scan_ranges + .iter() + .map(|range| json::JsonValue::from(range.to_string())) + .collect(); + + json::object! { + "scan ranges" => scan_ranges, + "scanned blocks" => value.scanned_blocks, + "unscanned blocks" => value.unscanned_blocks, + "percentage blocks scanned" => value.percentage_blocks_scanned, + "scanned sapling outputs" => value.scanned_sapling_outputs, + "unscanned sapling outputs" => value.unscanned_sapling_outputs, + "scanned orchard outputs" => value.scanned_orchard_outputs, + "unscanned orchard outputs" => value.unscanned_orchard_outputs, + "percentage outputs scanned" => value.percentage_outputs_scanned, + } + } +} + /// Output ID for a given pool type. #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)] pub struct OutputId { diff --git a/zingolib/src/commands.rs b/zingolib/src/commands.rs index 6fbfc1ea3..ab40fecc2 100644 --- a/zingolib/src/commands.rs +++ b/zingolib/src/commands.rs @@ -379,7 +379,8 @@ struct SyncCommand {} impl Command for SyncCommand { fn help(&self) -> &'static str { indoc! {r#" - Sync the light client with the server + Sync the wallet with the blockchain + Usage: sync @@ -387,12 +388,17 @@ impl Command for SyncCommand { } fn short_help(&self) -> &'static str { - "Download CompactBlocks and sync to the server" + "Sync the wallet with the blockchain" } - fn exec(&self, _args: &[&str], lightclient: &LightClient) -> String { + fn exec(&self, args: &[&str], lightclient: &LightClient) -> String { + if !args.is_empty() { + return self.help().to_string(); + } + RT.block_on(async move { - match lightclient.do_sync(true).await { + // TODO: zingo CLI sync status updates + match lightclient.do_sync(false).await { Ok(j) => j.to_json().pretty(2), Err(e) => e, } @@ -405,6 +411,7 @@ impl Command for SyncStatusCommand { fn help(&self) -> &'static str { indoc! {r#" Get the sync status of the wallet + Usage: syncstatus @@ -415,8 +422,15 @@ impl Command for SyncStatusCommand { "Get the sync status of the wallet" } - fn exec(&self, _args: &[&str], _lightclient: &LightClient) -> String { - todo!() + fn exec(&self, args: &[&str], lightclient: &LightClient) -> String { + if !args.is_empty() { + return self.help().to_string(); + } + + RT.block_on(async move { + json::JsonValue::from(pepper_sync::sync_status(lightclient.wallet.clone()).await) + .pretty(2) + }) } } From 5caf385eea86538a2184e3ed609e7618ed6fc125 Mon Sep 17 00:00:00 2001 From: Oscar Pepper Date: Wed, 19 Feb 2025 09:09:20 +0000 Subject: [PATCH 02/26] improve scan range json object and add sync start height to status --- pepper-sync/src/sync.rs | 1 + pepper-sync/src/wallet.rs | 9 ++++++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/pepper-sync/src/sync.rs b/pepper-sync/src/sync.rs index 51e73ee85..3fb446207 100644 --- a/pepper-sync/src/sync.rs +++ b/pepper-sync/src/sync.rs @@ -260,6 +260,7 @@ where SyncStatus { scan_ranges: sync_state.scan_ranges.clone(), + sync_start_height: sync_state.initial_sync_state.sync_start_height, scanned_blocks, unscanned_blocks, percentage_blocks_scanned, diff --git a/pepper-sync/src/wallet.rs b/pepper-sync/src/wallet.rs index 3caf6ce87..ee40de701 100644 --- a/pepper-sync/src/wallet.rs +++ b/pepper-sync/src/wallet.rs @@ -208,6 +208,7 @@ pub struct TreeBounds { #[allow(missing_docs)] pub struct SyncStatus { pub scan_ranges: Vec, + pub sync_start_height: BlockHeight, pub scanned_blocks: u32, pub unscanned_blocks: u32, pub percentage_blocks_scanned: f32, @@ -237,7 +238,13 @@ impl From for json::JsonValue { let scan_ranges: Vec = value .scan_ranges .iter() - .map(|range| json::JsonValue::from(range.to_string())) + .map(|range| { + json::object! { + "priority" => format!("{:?}", range.priority()), + "start block" => range.block_range().start.to_string(), + "end block" => (range.block_range().end - 1).to_string(), + } + }) .collect(); json::object! { From 3bf6144af9b645b774ac08cc6f3b976a974e1e13 Mon Sep 17 00:00:00 2001 From: Oscar Pepper Date: Wed, 19 Feb 2025 09:19:54 +0000 Subject: [PATCH 03/26] fix spaces in json objects --- pepper-sync/src/wallet.rs | 22 +++++++++++----------- zingolib/src/commands.rs | 6 +++--- zingolib/src/wallet/data.rs | 2 +- zingolib/src/wallet/summary.rs | 14 +++++++------- 4 files changed, 22 insertions(+), 22 deletions(-) diff --git a/pepper-sync/src/wallet.rs b/pepper-sync/src/wallet.rs index ee40de701..7c634c1d7 100644 --- a/pepper-sync/src/wallet.rs +++ b/pepper-sync/src/wallet.rs @@ -241,22 +241,22 @@ impl From for json::JsonValue { .map(|range| { json::object! { "priority" => format!("{:?}", range.priority()), - "start block" => range.block_range().start.to_string(), - "end block" => (range.block_range().end - 1).to_string(), + "start_block" => range.block_range().start.to_string(), + "end_block" => (range.block_range().end - 1).to_string(), } }) .collect(); json::object! { - "scan ranges" => scan_ranges, - "scanned blocks" => value.scanned_blocks, - "unscanned blocks" => value.unscanned_blocks, - "percentage blocks scanned" => value.percentage_blocks_scanned, - "scanned sapling outputs" => value.scanned_sapling_outputs, - "unscanned sapling outputs" => value.unscanned_sapling_outputs, - "scanned orchard outputs" => value.scanned_orchard_outputs, - "unscanned orchard outputs" => value.unscanned_orchard_outputs, - "percentage outputs scanned" => value.percentage_outputs_scanned, + "scan_ranges" => scan_ranges, + "scanned_blocks" => value.scanned_blocks, + "unscanned_blocks" => value.unscanned_blocks, + "percentage_blocks_scanned" => value.percentage_blocks_scanned, + "scanned_sapling_outputs" => value.scanned_sapling_outputs, + "unscanned_sapling_outputs" => value.unscanned_sapling_outputs, + "scanned_orchard_outputs" => value.scanned_orchard_outputs, + "unscanned_orchard_outputs" => value.unscanned_orchard_outputs, + "percentage_outputs_scanned" => value.percentage_outputs_scanned, } } } diff --git a/zingolib/src/commands.rs b/zingolib/src/commands.rs index ab40fecc2..3e84b818b 100644 --- a/zingolib/src/commands.rs +++ b/zingolib/src/commands.rs @@ -1744,8 +1744,8 @@ impl Command for NotesCommand { let wallet = lightclient.wallet.lock().await; json::object! { - "orchard notes" => json::JsonValue::from(wallet.note_summaries::(all_notes)), - "sapling notes" => json::JsonValue::from(wallet.note_summaries::(all_notes)), + "orchard_notes" => json::JsonValue::from(wallet.note_summaries::(all_notes)), + "sapling_notes" => json::JsonValue::from(wallet.note_summaries::(all_notes)), } .pretty(2) }) @@ -1790,7 +1790,7 @@ impl Command for CoinsCommand { RT.block_on(async move { json::object! { - "transparent coins" => json::JsonValue::from(lightclient.wallet.lock().await.coin_summaries(all_coins)), + "transparent_coins" => json::JsonValue::from(lightclient.wallet.lock().await.coin_summaries(all_coins)), } .pretty(2) }) diff --git a/zingolib/src/wallet/data.rs b/zingolib/src/wallet/data.rs index 9d3b54a07..0ef72c93a 100644 --- a/zingolib/src/wallet/data.rs +++ b/zingolib/src/wallet/data.rs @@ -1819,7 +1819,7 @@ pub mod summaries { "value" => note.value, "memo" => note.memo, "recipient" => note.recipient, - "recipient unified address" => note.recipient_unified_address + "recipient_unified_address" => note.recipient_unified_address } } } diff --git a/zingolib/src/wallet/summary.rs b/zingolib/src/wallet/summary.rs index 3f5489d84..ed29a3716 100644 --- a/zingolib/src/wallet/summary.rs +++ b/zingolib/src/wallet/summary.rs @@ -96,12 +96,12 @@ impl From for json::JsonValue { json::object! { "value" => note.value, "status" => format!("{} at block height {}", note.status, note.block_height), - "spend status" => note.spend_status.to_string(), + "spend_status" => note.spend_status.to_string(), "memo" => note.memo, "time" => note.time, "txid" => note.txid.to_string(), - "output index" => note.output_index, - "account id" => u32::from(note.account_id), + "output_index" => note.output_index, + "account_id" => u32::from(note.account_id), "scope" => note.scope.to_string(), } } @@ -164,13 +164,13 @@ impl From for json::JsonValue { json::object! { "value" => coin.value, "status" => format!("{} at block height {}", coin.status, coin.block_height), - "spend status" => coin.spend_status.to_string(), + "spend_status" => coin.spend_status.to_string(), "time" => coin.time, "txid" => coin.txid.to_string(), - "output index" => coin.output_index, - "account id" => u32::from(coin.account_id), + "output_index" => coin.output_index, + "account_id" => u32::from(coin.account_id), "scope" => coin.scope.to_string(), - "address index" => coin.address_index + "address_index" => coin.address_index } } } From 4bb92fee45a2ecf705bcda4dce303a6acd5d459c Mon Sep 17 00:00:00 2001 From: Oscar Pepper Date: Wed, 19 Feb 2025 09:26:23 +0000 Subject: [PATCH 04/26] add sync start to json --- pepper-sync/src/wallet.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/pepper-sync/src/wallet.rs b/pepper-sync/src/wallet.rs index 7c634c1d7..d9755db4b 100644 --- a/pepper-sync/src/wallet.rs +++ b/pepper-sync/src/wallet.rs @@ -249,6 +249,7 @@ impl From for json::JsonValue { json::object! { "scan_ranges" => scan_ranges, + "sync_start_height" => u32::from(value.sync_start_height), "scanned_blocks" => value.scanned_blocks, "unscanned_blocks" => value.unscanned_blocks, "percentage_blocks_scanned" => value.percentage_blocks_scanned, From d1faabac29c180e4c5da39f73e086cc7b506949b Mon Sep 17 00:00:00 2001 From: Oscar Pepper Date: Thu, 20 Feb 2025 05:28:26 +0000 Subject: [PATCH 05/26] print updates true for sync command --- zingolib/src/commands.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zingolib/src/commands.rs b/zingolib/src/commands.rs index 3e84b818b..6d78c3e9e 100644 --- a/zingolib/src/commands.rs +++ b/zingolib/src/commands.rs @@ -398,7 +398,7 @@ impl Command for SyncCommand { RT.block_on(async move { // TODO: zingo CLI sync status updates - match lightclient.do_sync(false).await { + match lightclient.do_sync(true).await { Ok(j) => j.to_json().pretty(2), Err(e) => e, } From e5a5adee4251906f11f21fdae73083bb98475879 Mon Sep 17 00:00:00 2001 From: Oscar Pepper Date: Thu, 20 Feb 2025 07:41:59 +0000 Subject: [PATCH 06/26] implemented sync modes for pausing --- darkside-tests/tests/tests.rs | 35 +++------------- libtonode-tests/tests/sync.rs | 30 +------------- pepper-sync/src/sync.rs | 27 +++++++++++- pepper-sync/src/wallet.rs | 26 ++++++++++++ zingolib/src/commands.rs | 70 +++----------------------------- zingolib/src/lightclient.rs | 13 +++--- zingolib/src/lightclient/sync.rs | 16 +++++++- 7 files changed, 86 insertions(+), 131 deletions(-) diff --git a/darkside-tests/tests/tests.rs b/darkside-tests/tests/tests.rs index 4116a96e5..405673e22 100644 --- a/darkside-tests/tests/tests.rs +++ b/darkside-tests/tests/tests.rs @@ -3,8 +3,6 @@ use darkside_tests::utils::prepare_darksidewalletd; // use darkside_tests::utils::scenarios::DarksideEnvironment; // use darkside_tests::utils::update_tree_states_for_transaction; use darkside_tests::utils::DarksideHandler; -use std::future::Future; -use std::pin::Pin; use testvectors::seeds::DARKSIDE_SEED; // use tokio::time::sleep; // use zcash_client_backend::PoolType::Shielded; @@ -12,7 +10,6 @@ use testvectors::seeds::DARKSIDE_SEED; // use zingo_status::confirmation_status::ConfirmationStatus; use zingolib::config::RegtestNetwork; // use zingolib::get_base_address_macro; -use zingolib::lightclient::LightClient; use zingolib::lightclient::PoolBalances; // use zingolib::testutils::chain_generics::conduct_chain::ConductChain as _; // use zingolib::testutils::chain_generics::with_assertions::to_clients_proposal; @@ -58,27 +55,8 @@ async fn simple_sync() { ); } -#[ignore = "attempts to unwrap failed checked_sub on sapling output count"] #[tokio::test] -async fn reorg_away_receipt_pepper() { - reorg_receipt_sync_generic(|lc| { - Box::pin(async { - let uri = lc.config().lightwalletd_uri.read().unwrap().clone(); - let client = zingo_netutils::GrpcConnector::new(uri) - .get_client() - .await - .unwrap(); - pepper_sync::sync(client, &lc.config().chain.clone(), lc.wallet.clone()) - .await - .map_err(|e| e.to_string()) - }) - }) - .await; -} -async fn reorg_receipt_sync_generic(sync_fn: F) -where - F: for<'a> Fn(&'a mut LightClient) -> Pin> + 'a>>, -{ +async fn reorg_receipt_sync_generic() { let darkside_handler = DarksideHandler::new(None); let server_id = zingolib::config::construct_lightwalletd_uri(Some(format!( @@ -90,12 +68,11 @@ where .unwrap(); let regtest_network = RegtestNetwork::all_upgrades_active(); - let mut light_client = - ClientBuilder::new(server_id.clone(), darkside_handler.darkside_dir.clone()) - .build_client(DARKSIDE_SEED.to_string(), 0, true, regtest_network) - .await; + let light_client = ClientBuilder::new(server_id.clone(), darkside_handler.darkside_dir.clone()) + .build_client(DARKSIDE_SEED.to_string(), 0, true, regtest_network) + .await; + light_client.do_sync(false).await.unwrap(); - sync_fn(&mut light_client).await.unwrap(); assert_eq!( light_client.do_balance().await, PoolBalances { @@ -113,7 +90,7 @@ where prepare_darksidewalletd(server_id.clone(), false) .await .unwrap(); - sync_fn(&mut light_client).await.unwrap(); + light_client.do_sync(false).await.unwrap(); assert_eq!( light_client.do_balance().await, PoolBalances { diff --git a/libtonode-tests/tests/sync.rs b/libtonode-tests/tests/sync.rs index d73fae39a..78cb3da71 100644 --- a/libtonode-tests/tests/sync.rs +++ b/libtonode-tests/tests/sync.rs @@ -1,8 +1,5 @@ -use std::time::Duration; - use tempfile::TempDir; use testvectors::seeds::HOSPITAL_MUSEUM_SEED; -use zingo_netutils::GrpcConnector; use zingolib::{ config::{construct_lightwalletd_uri, load_clientconfig, DEFAULT_LIGHTWALLETD_SERVER}, get_base_address_macro, @@ -37,11 +34,7 @@ async fn sync_mainnet_test() { .await .unwrap(); - let client = GrpcConnector::new(uri).get_client().await.unwrap(); - - pepper_sync::sync(client, &config.chain, lightclient.wallet.clone()) - .await - .unwrap(); + lightclient.do_sync(false).await.unwrap(); let wallet = lightclient.wallet.lock().await; // dbg!(&wallet.wallet_blocks); @@ -76,26 +69,7 @@ async fn sync_status() { .await .unwrap(); - let client = GrpcConnector::new(uri).get_client().await.unwrap(); - - let wallet = lightclient.wallet.clone(); - let sync_handle = tokio::spawn(async move { - pepper_sync::sync(client, &config.chain, wallet) - .await - .unwrap(); - }); - - let wallet = lightclient.wallet.clone(); - tokio::spawn(async move { - loop { - let wallet = wallet.clone(); - let sync_status = pepper_sync::sync_status(wallet).await; - dbg!(sync_status); - tokio::time::sleep(Duration::from_secs(1)).await; - } - }); - - sync_handle.await.unwrap(); + lightclient.do_sync(true).await.unwrap(); } // temporary test for sync development diff --git a/pepper-sync/src/sync.rs b/pepper-sync/src/sync.rs index 3fb446207..91a2f04e4 100644 --- a/pepper-sync/src/sync.rs +++ b/pepper-sync/src/sync.rs @@ -32,7 +32,7 @@ use crate::scan::ScanResults; use crate::wallet::traits::{ SyncBlocks, SyncNullifiers, SyncOutPoints, SyncShardTrees, SyncTransactions, SyncWallet, }; -use crate::wallet::{NullifierMap, SyncStatus}; +use crate::wallet::{NullifierMap, SyncMode, SyncStatus}; use crate::witness; pub mod error; @@ -43,16 +43,27 @@ pub(crate) mod transparent; const VERIFY_BLOCK_RANGE_SIZE: u32 = 10; pub(crate) const MAX_VERIFICATION_WINDOW: u32 = 100; -/// Syncs a wallet to the latest state of the blockchain +/// Syncs a wallet to the latest state of the blockchain. +/// +/// `sync_mode` is intended to be stored as [`crate::wallet::SyncMode`] and converted with TODO. pub async fn sync( client: CompactTxStreamerClient, consensus_parameters: &P, wallet: Arc>, + sync_mode: Arc, ) -> Result<(), SyncError> where P: consensus::Parameters + Sync + Send + 'static, W: SyncWallet + SyncBlocks + SyncTransactions + SyncNullifiers + SyncOutPoints + SyncShardTrees, { + let mut sync_mode_enum = SyncMode::from_u8(sync_mode.load(atomic::Ordering::Acquire)).unwrap(); + if sync_mode_enum == SyncMode::Stopped { + sync_mode_enum = SyncMode::Running; + sync_mode.store(sync_mode_enum as u8, atomic::Ordering::Release); + } else { + panic!("Sync is already running!"); + } + tracing::info!("Starting sync..."); // create channel for sending fetch requests and launch fetcher task @@ -179,6 +190,18 @@ where // allow tasks outside the sync engine access to the wallet data drop(wallet_guard); + + sync_mode_enum = SyncMode::from_u8(sync_mode.load(atomic::Ordering::Acquire)).unwrap(); + if sync_mode_enum == SyncMode::Paused { + let mut pause_interval = tokio::time::interval(Duration::from_secs(1)); + pause_interval.tick().await; + while sync_mode_enum != SyncMode::Running { + pause_interval.tick().await; + sync_mode_enum = SyncMode::from_u8(sync_mode.load(atomic::Ordering::Acquire)).unwrap(); + } + + } + wallet_guard = wallet.lock().await; } diff --git a/pepper-sync/src/wallet.rs b/pepper-sync/src/wallet.rs index d9755db4b..a1b72281e 100644 --- a/pepper-sync/src/wallet.rs +++ b/pepper-sync/src/wallet.rs @@ -191,6 +191,32 @@ impl Default for SyncState { } } +/// Sync modes. +#[repr(u8)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum SyncMode { + /// Sync is not running. + Stopped, + /// Sync is held in a paused state and the wallet guard is dropped. + Paused, + /// Sync is running. + Running, +} + +impl SyncMode { + /// Constructor from u8. + /// + /// Returns `None` if `mode` is not a valid enum variant. + pub fn from_u8(mode: u8) -> Option { + match mode { + 0 => Some(Self::Stopped), + 1 => Some(Self::Paused), + 2 => Some(Self::Running), + _ => None, + } + } +} + /// Initial and final tree sizes. #[derive(Debug, Clone, Copy)] #[allow(missing_docs)] diff --git a/zingolib/src/commands.rs b/zingolib/src/commands.rs index 6d78c3e9e..ed9faceed 100644 --- a/zingolib/src/commands.rs +++ b/zingolib/src/commands.rs @@ -178,35 +178,6 @@ impl Command for WalletKindCommand { } } -// FIXME: zingo2 -// struct InterruptCommand {} -// impl Command for InterruptCommand { -// fn help(&self) -> &'static str { -// "Toggle the sync interrupt after batch flag." -// } -// fn short_help(&self) -> &'static str { -// "Toggle the sync interrupt after batch flag." -// } -// fn exec(&self, args: &[&str], lightclient: &LightClient) -> String { -// match args.len() { -// 1 => RT.block_on(async move { -// match args[0] { -// "true" => { -// lightclient.interrupt_sync_after_batch(true).await; -// "true".to_string() -// } -// "false" => { -// lightclient.interrupt_sync_after_batch(false).await; -// "false".to_string() -// } -// _ => self.help().to_string(), -// } -// }), -// _ => self.help().to_string(), -// } -// } -// } - struct ParseAddressCommand {} impl Command for ParseAddressCommand { fn help(&self) -> &'static str { @@ -379,10 +350,11 @@ struct SyncCommand {} impl Command for SyncCommand { fn help(&self) -> &'static str { indoc! {r#" - Sync the wallet with the blockchain + Sync the wallet with the blockchain. Usage: - sync + sync run + sync pause "#} } @@ -392,12 +364,12 @@ impl Command for SyncCommand { } fn exec(&self, args: &[&str], lightclient: &LightClient) -> String { - if !args.is_empty() { + if args.len() != 1 { return self.help().to_string(); } RT.block_on(async move { - // TODO: zingo CLI sync status updates + // TODO: improve zingo CLI sync status updates match lightclient.do_sync(true).await { Ok(j) => j.to_json().pretty(2), Err(e) => e, @@ -1374,33 +1346,6 @@ impl Command for TransactionsCommand { } } -// FIXME: zingo2, re-implement -// struct DetailedTransactionsCommand {} -// impl Command for DetailedTransactionsCommand { -// fn help(&self) -> &'static str { -// indoc! {r#" -// Provides a detailed list of transaction summaries related to this wallet in order of blockheight. - -// Usage: -// detailed_transactions -// "#} -// } - -// fn short_help(&self) -> &'static str { -// "Provides a detailed list of transaction summaries related to this wallet in order of blockheight." -// } - -// fn exec(&self, args: &[&str], lightclient: &LightClient) -> String { -// if !args.is_empty() { -// return "Error: invalid arguments\nTry 'help detailed_transactions' for correct usage and examples" -// .to_string(); -// } -// RT.block_on( -// async move { format!("{}", lightclient.detailed_transaction_summaries().await) }, -// ) -// } -// } - struct MemoBytesToAddressCommand {} impl Command for MemoBytesToAddressCommand { fn help(&self) -> &'static str { @@ -1885,7 +1830,6 @@ pub fn get_commands() -> HashMap<&'static str, Box> { ("decryptmessage", Box::new(DecryptMessageCommand {})), ("parse_address", Box::new(ParseAddressCommand {})), ("parse_viewkey", Box::new(ParseViewKeyCommand {})), - // ("interrupt_sync_after_batch", Box::new(InterruptCommand {})), ("changeserver", Box::new(ChangeServerCommand {})), ("rescan", Box::new(RescanCommand {})), ("clear", Box::new(ClearCommand {})), @@ -1898,10 +1842,6 @@ pub fn get_commands() -> HashMap<&'static str, Box> { // ("setoption", Box::new(SetOptionCommand {})), ("valuetransfers", Box::new(ValueTransfersCommand {})), ("transactions", Box::new(TransactionsCommand {})), - // ( - // "detailed_transactions", - // Box::new(DetailedTransactionsCommand {}), - // ), ("value_to_address", Box::new(ValueToAddressCommand {})), ("sends_to_address", Box::new(SendsToAddressCommand {})), ("messages", Box::new(MessagesFilterCommand {})), diff --git a/zingolib/src/lightclient.rs b/zingolib/src/lightclient.rs index add6d753b..70f036b63 100644 --- a/zingolib/src/lightclient.rs +++ b/zingolib/src/lightclient.rs @@ -3,7 +3,7 @@ use json::{array, object, JsonValue}; use log::error; use serde::Serialize; -use std::sync::{atomic::AtomicBool, Arc}; +use std::sync::{atomic::AtomicU8, Arc}; use tokio::sync::{Mutex, RwLock}; use zcash_client_backend::encoding::{decode_payment_address, encode_payment_address}; @@ -292,23 +292,24 @@ pub struct UserBalances { /// * from a fresh start with reasonable defaults /// 2. synchronization of the client with the state of the blockchain via a gRPC server /// * -#[allow(dead_code)] // TODO: remove after sync integration +/// `sync_mode` is an atomic representation of [`pepper_sync::wallet::SyncMode`]. pub struct LightClient { // TODO: split zingoconfig so data is not duplicated pub(crate) config: ZingoConfig, /// Wallet data pub wallet: Arc>, - syncing: Arc, // TODO: add interrupt to sync engine + sync_mode: Arc, latest_proposal: Arc>>, // TODO: move to wallet - save_buffer: ZingoSaveBuffer, // TODO: move save buffer to wallet itself? + save_buffer: ZingoSaveBuffer, // TODO: move save buffer to wallet itself? } /// all the wonderfully intertwined ways to conjure a LightClient pub mod instantiation { use log::debug; + use pepper_sync::wallet::SyncMode; use std::{ io::{self, Error, ErrorKind}, - sync::{atomic::AtomicBool, Arc}, + sync::{atomic::AtomicU8, Arc}, }; use tokio::{ runtime::Runtime, @@ -334,7 +335,7 @@ pub mod instantiation { Ok(LightClient { config, wallet: Arc::new(Mutex::new(wallet)), - syncing: Arc::new(AtomicBool::new(false)), + sync_mode: Arc::new(AtomicU8::new(SyncMode::Stopped as u8)), latest_proposal: Arc::new(RwLock::new(None)), save_buffer: ZingoSaveBuffer::new(buffer), }) diff --git a/zingolib/src/lightclient/sync.rs b/zingolib/src/lightclient/sync.rs index 0b141078c..a86782740 100644 --- a/zingolib/src/lightclient/sync.rs +++ b/zingolib/src/lightclient/sync.rs @@ -10,6 +10,7 @@ use std::time::Duration; use log::{debug, error}; use pepper_sync::error::SyncError; use pepper_sync::sync::error::MempoolError; +use pepper_sync::wallet::SyncMode; use super::LightClient; use super::SyncResult; @@ -37,8 +38,11 @@ impl LightClient { .unwrap(); let network = self.wallet.lock().await.network; let wallet = self.wallet.clone(); + let sync_mode = self.sync_mode.clone(); let sync_handle = - tokio::spawn(async move { pepper_sync::sync(client, &network, wallet).await }); + tokio::spawn( + async move { pepper_sync::sync(client, &network, wallet, sync_mode).await }, + ); // FIXME: replace with lightclient syncing field let syncing = Arc::new(AtomicBool::new(true)); @@ -98,6 +102,16 @@ impl LightClient { response } + + pub fn sync_mode(&self) -> SyncMode { + SyncMode::from_u8(self.sync_mode.load(atomic::Ordering::Acquire)) + .expect("API does not support setting of non-valid variant values") + } + + pub fn set_sync_mode(&self, sync_mode: SyncMode) { + self.sync_mode + .store(sync_mode as u8, atomic::Ordering::Release); + } } #[cfg(test)] From b38508d7eb477bb54a94fb1983487aa4da4abc53 Mon Sep 17 00:00:00 2001 From: Oscar Pepper Date: Sat, 22 Feb 2025 02:59:34 +0000 Subject: [PATCH 07/26] change to sync api where lightclient stores a sync handle to allow background sync in rust --- darkside-tests/src/utils.rs | 8 +- darkside-tests/tests/advanced_reorg_tests.rs | 142 ++++---- .../tests/network_interruption_tests.rs | 15 +- darkside-tests/tests/tests.rs | 20 +- libtonode-tests/tests/concrete.rs | 313 ++++++++++-------- libtonode-tests/tests/shield_transparent.rs | 6 +- libtonode-tests/tests/sync.rs | 14 +- libtonode-tests/tests/wallet.rs | 4 +- pepper-sync/src/sync.rs | 47 ++- pepper-sync/src/wallet.rs | 47 ++- zingolib/src/commands.rs | 25 +- zingolib/src/lightclient.rs | 70 +--- zingolib/src/lightclient/describe.rs | 2 +- zingolib/src/lightclient/sync.rs | 97 +++--- zingolib/src/testutils.rs | 38 +-- .../testutils/chain_generics/conduct_chain.rs | 8 +- .../src/testutils/chain_generics/fixtures.rs | 68 ++-- .../chain_generics/with_assertions.rs | 16 +- zingolib/src/testutils/interrupts.rs | 12 - zingolib/src/testutils/macros.rs | 76 ----- zingolib/src/testutils/scenarios.rs | 64 ++-- zingolib/src/wallet/disk/testing/tests.rs | 26 +- 22 files changed, 533 insertions(+), 585 deletions(-) delete mode 100644 zingolib/src/testutils/interrupts.rs diff --git a/darkside-tests/src/utils.rs b/darkside-tests/src/utils.rs index 384b76b0f..67d8b5695 100644 --- a/darkside-tests/src/utils.rs +++ b/darkside-tests/src/utils.rs @@ -724,13 +724,13 @@ pub mod scenarios { pub fn get_regtest_network(&self) -> &RegtestNetwork { &self.regtest_network } - pub fn get_faucet(&self) -> &LightClient { + pub fn get_faucet(&mut self) -> &mut LightClient { self.faucet - .as_ref() + .as_mut() .expect("scenario should have a faucet lightclient") } - pub fn get_lightclient(&self, lightclient_index: u64) -> &LightClient { - &self.lightclients[lightclient_index as usize] + pub fn get_lightclient(&mut self, lightclient_index: u64) -> &mut LightClient { + &mut self.lightclients[lightclient_index as usize] } pub fn get_staged_blockheight(&self) -> &BlockHeight { &self.staged_blockheight diff --git a/darkside-tests/tests/advanced_reorg_tests.rs b/darkside-tests/tests/advanced_reorg_tests.rs index 6b5fd9ad9..8895bcc34 100644 --- a/darkside-tests/tests/advanced_reorg_tests.rs +++ b/darkside-tests/tests/advanced_reorg_tests.rs @@ -34,16 +34,17 @@ async fn reorg_changes_incoming_tx_height() { .await .unwrap(); - let light_client = ClientBuilder::new(server_id.clone(), darkside_handler.darkside_dir.clone()) - .build_client( - ADVANCED_REORG_TESTS_USER_WALLET.to_string(), - 202, - true, - RegtestNetwork::all_upgrades_active(), - ) - .await; - - light_client.do_sync(true).await.unwrap(); + let mut light_client = + ClientBuilder::new(server_id.clone(), darkside_handler.darkside_dir.clone()) + .build_client( + ADVANCED_REORG_TESTS_USER_WALLET.to_string(), + 202, + true, + RegtestNetwork::all_upgrades_active(), + ) + .await; + + light_client.sync_and_await(true).await.unwrap(); assert_eq!( light_client.do_balance().await, PoolBalances { @@ -71,7 +72,7 @@ async fn reorg_changes_incoming_tx_height() { .await .unwrap(); - let reorg_sync_result = light_client.do_sync(true).await; + let reorg_sync_result = light_client.sync_and_await(true).await; match reorg_sync_result { Ok(value) => println!("{}", value), @@ -190,16 +191,17 @@ async fn reorg_changes_incoming_tx_index() { .await .unwrap(); - let light_client = ClientBuilder::new(server_id.clone(), darkside_handler.darkside_dir.clone()) - .build_client( - ADVANCED_REORG_TESTS_USER_WALLET.to_string(), - 202, - true, - RegtestNetwork::all_upgrades_active(), - ) - .await; - - light_client.do_sync(true).await.unwrap(); + let mut light_client = + ClientBuilder::new(server_id.clone(), darkside_handler.darkside_dir.clone()) + .build_client( + ADVANCED_REORG_TESTS_USER_WALLET.to_string(), + 202, + true, + RegtestNetwork::all_upgrades_active(), + ) + .await; + + light_client.sync_and_await(true).await.unwrap(); assert_eq!( light_client.do_balance().await, PoolBalances { @@ -227,7 +229,7 @@ async fn reorg_changes_incoming_tx_index() { .await .unwrap(); - let reorg_sync_result = light_client.do_sync(true).await; + let reorg_sync_result = light_client.sync_and_await(true).await; match reorg_sync_result { Ok(value) => println!("{}", value), @@ -345,16 +347,17 @@ async fn reorg_expires_incoming_tx() { .await .unwrap(); - let light_client = ClientBuilder::new(server_id.clone(), darkside_handler.darkside_dir.clone()) - .build_client( - ADVANCED_REORG_TESTS_USER_WALLET.to_string(), - 202, - true, - RegtestNetwork::all_upgrades_active(), - ) - .await; - - light_client.do_sync(true).await.unwrap(); + let mut light_client = + ClientBuilder::new(server_id.clone(), darkside_handler.darkside_dir.clone()) + .build_client( + ADVANCED_REORG_TESTS_USER_WALLET.to_string(), + 202, + true, + RegtestNetwork::all_upgrades_active(), + ) + .await; + + light_client.sync_and_await(true).await.unwrap(); assert_eq!( light_client.do_balance().await, PoolBalances { @@ -382,7 +385,7 @@ async fn reorg_expires_incoming_tx() { .await .unwrap(); - let reorg_sync_result = light_client.do_sync(true).await; + let reorg_sync_result = light_client.sync_and_await(true).await; match reorg_sync_result { Ok(value) => println!("{}", value), @@ -523,16 +526,17 @@ async fn reorg_changes_outgoing_tx_height() { .await .unwrap(); - let light_client = ClientBuilder::new(server_id.clone(), darkside_handler.darkside_dir.clone()) - .build_client( - ADVANCED_REORG_TESTS_USER_WALLET.to_string(), - 202, - true, - RegtestNetwork::all_upgrades_active(), - ) - .await; - - light_client.do_sync(true).await.unwrap(); + let mut light_client = + ClientBuilder::new(server_id.clone(), darkside_handler.darkside_dir.clone()) + .build_client( + ADVANCED_REORG_TESTS_USER_WALLET.to_string(), + 202, + true, + RegtestNetwork::all_upgrades_active(), + ) + .await; + + light_client.sync_and_await(true).await.unwrap(); assert_eq!( light_client.do_balance().await, PoolBalances { @@ -579,7 +583,7 @@ async fn reorg_changes_outgoing_tx_height() { let sent_tx_height: i32 = 205; _ = connector.apply_staged(sent_tx_height).await; - light_client.do_sync(true).await.unwrap(); + light_client.sync_and_await(true).await.unwrap(); let expected_after_send_balance = PoolBalances { sapling_balance: Some(0), @@ -637,7 +641,7 @@ async fn reorg_changes_outgoing_tx_height() { _ = connector.apply_staged(211).await; - let reorg_sync_result = light_client.do_sync(true).await; + let reorg_sync_result = light_client.sync_and_await(true).await; match reorg_sync_result { Ok(value) => println!("{}", value), @@ -763,14 +767,15 @@ async fn reorg_expires_outgoing_tx_height() { .await .unwrap(); - let light_client = ClientBuilder::new(server_id.clone(), darkside_handler.darkside_dir.clone()) - .build_client( - ADVANCED_REORG_TESTS_USER_WALLET.to_string(), - 202, - true, - RegtestNetwork::all_upgrades_active(), - ) - .await; + let mut light_client = + ClientBuilder::new(server_id.clone(), darkside_handler.darkside_dir.clone()) + .build_client( + ADVANCED_REORG_TESTS_USER_WALLET.to_string(), + 202, + true, + RegtestNetwork::all_upgrades_active(), + ) + .await; let expected_initial_balance = PoolBalances { sapling_balance: Some(0), @@ -784,7 +789,7 @@ async fn reorg_expires_outgoing_tx_height() { transparent_balance: Some(0), }; - light_client.do_sync(true).await.unwrap(); + light_client.sync_and_await(true).await.unwrap(); assert_eq!(light_client.do_balance().await, expected_initial_balance); let before_reorg_transactions = light_client.sorted_value_transfers(true).await; @@ -811,7 +816,7 @@ async fn reorg_expires_outgoing_tx_height() { let sent_tx_height: i32 = 205; _ = connector.apply_staged(sent_tx_height).await; - light_client.do_sync(true).await.unwrap(); + light_client.sync_and_await(true).await.unwrap(); let expected_after_send_balance = PoolBalances { sapling_balance: Some(0), @@ -862,7 +867,7 @@ async fn reorg_expires_outgoing_tx_height() { // this will remove the submitted transaction from our view of the blockchain _ = connector.apply_staged(245).await; - let reorg_sync_result = light_client.do_sync(true).await; + let reorg_sync_result = light_client.sync_and_await(true).await; match reorg_sync_result { Ok(value) => println!("{}", value), @@ -940,16 +945,17 @@ async fn reorg_changes_outgoing_tx_index() { .await .unwrap(); - let light_client = ClientBuilder::new(server_id.clone(), darkside_handler.darkside_dir.clone()) - .build_client( - ADVANCED_REORG_TESTS_USER_WALLET.to_string(), - 202, - true, - RegtestNetwork::all_upgrades_active(), - ) - .await; - - light_client.do_sync(true).await.unwrap(); + let mut light_client = + ClientBuilder::new(server_id.clone(), darkside_handler.darkside_dir.clone()) + .build_client( + ADVANCED_REORG_TESTS_USER_WALLET.to_string(), + 202, + true, + RegtestNetwork::all_upgrades_active(), + ) + .await; + + light_client.sync_and_await(true).await.unwrap(); assert_eq!( light_client.do_balance().await, PoolBalances { @@ -996,7 +1002,7 @@ async fn reorg_changes_outgoing_tx_index() { let sent_tx_height: i32 = 205; _ = connector.apply_staged(sent_tx_height).await; - light_client.do_sync(true).await.unwrap(); + light_client.sync_and_await(true).await.unwrap(); let expected_after_send_balance = PoolBalances { sapling_balance: Some(0), @@ -1063,7 +1069,7 @@ async fn reorg_changes_outgoing_tx_index() { _ = connector.apply_staged(312).await; - let reorg_sync_result = light_client.do_sync(true).await; + let reorg_sync_result = light_client.sync_and_await(true).await; match reorg_sync_result { Ok(value) => println!("{}", value), diff --git a/darkside-tests/tests/network_interruption_tests.rs b/darkside-tests/tests/network_interruption_tests.rs index 5e2abb48a..8ee6f22f1 100644 --- a/darkside-tests/tests/network_interruption_tests.rs +++ b/darkside-tests/tests/network_interruption_tests.rs @@ -30,7 +30,7 @@ async fn interrupt_initial_tree_fetch() { .await .unwrap(); let regtest_network = RegtestNetwork::all_upgrades_active(); - let light_client = ClientBuilder::new(server_id, darkside_handler.darkside_dir.clone()) + let mut light_client = ClientBuilder::new(server_id, darkside_handler.darkside_dir.clone()) .build_client(DARKSIDE_SEED.to_string(), 0, true, regtest_network) .await; let mut cond_log = @@ -62,7 +62,7 @@ async fn interrupt_initial_tree_fetch() { println!("aborted proxy"); }); println!("spawned abortion task"); - let result = light_client.do_sync(true).await; + let result = light_client.sync_and_await(true).await; assert_eq!(result.unwrap_err(),"status: Unavailable, message: \"error trying to connect: tcp connect error: Connection refused (os error 111)\", details: [], metadata: MetadataMap { headers: {} }"); } @@ -86,11 +86,12 @@ async fn shielded_note_marked_as_change_chainbuild() { scenario .stage_and_apply_blocks(thousands_blocks_count * 1000 - 2, 0) .await; - scenario.get_faucet().do_sync(false).await.unwrap(); + scenario.get_faucet().sync_and_await(false).await.unwrap(); + let recipient_addr = get_base_address_macro!(scenario.get_lightclient(0), "sapling"); scenario .send_and_write_transaction( DarksideSender::Faucet, - &get_base_address_macro!(scenario.get_lightclient(0), "sapling"), + &recipient_addr, 50_000, &chainbuild_file, ) @@ -98,7 +99,11 @@ async fn shielded_note_marked_as_change_chainbuild() { scenario .apply_blocks(thousands_blocks_count * 1000 - 1) .await; - scenario.get_lightclient(0).do_sync(false).await.unwrap(); + scenario + .get_lightclient(0) + .sync_and_await(false) + .await + .unwrap(); scenario .shield_and_write_transaction(DarksideSender::IndexedClient(0), &chainbuild_file) .await; diff --git a/darkside-tests/tests/tests.rs b/darkside-tests/tests/tests.rs index 405673e22..b2167eac4 100644 --- a/darkside-tests/tests/tests.rs +++ b/darkside-tests/tests/tests.rs @@ -28,17 +28,16 @@ async fn simple_sync() { .await .unwrap(); let regtest_network = RegtestNetwork::all_upgrades_active(); - let light_client = ClientBuilder::new(server_id, darkside_handler.darkside_dir.clone()) + let mut light_client = ClientBuilder::new(server_id, darkside_handler.darkside_dir.clone()) .build_client(DARKSIDE_SEED.to_string(), 0, true, regtest_network) .await; - let result = light_client.do_sync(true).await.unwrap(); + let result = light_client.sync_and_await(true).await.unwrap(); println!("{}", result); - assert!(result.success); - assert_eq!(result.latest_block, 3); - assert_eq!(result.total_blocks_synced, 3); + assert_eq!(result.sync_end_height, 3.into()); + assert_eq!(result.scanned_blocks, 3); assert_eq!( light_client.do_balance().await, PoolBalances { @@ -68,10 +67,11 @@ async fn reorg_receipt_sync_generic() { .unwrap(); let regtest_network = RegtestNetwork::all_upgrades_active(); - let light_client = ClientBuilder::new(server_id.clone(), darkside_handler.darkside_dir.clone()) - .build_client(DARKSIDE_SEED.to_string(), 0, true, regtest_network) - .await; - light_client.do_sync(false).await.unwrap(); + let mut light_client = + ClientBuilder::new(server_id.clone(), darkside_handler.darkside_dir.clone()) + .build_client(DARKSIDE_SEED.to_string(), 0, true, regtest_network) + .await; + light_client.sync_and_await(false).await.unwrap(); assert_eq!( light_client.do_balance().await, @@ -90,7 +90,7 @@ async fn reorg_receipt_sync_generic() { prepare_darksidewalletd(server_id.clone(), false) .await .unwrap(); - light_client.do_sync(false).await.unwrap(); + light_client.sync_and_await(false).await.unwrap(); assert_eq!( light_client.do_balance().await, PoolBalances { diff --git a/libtonode-tests/tests/concrete.rs b/libtonode-tests/tests/concrete.rs index b0f1af460..d931fc76f 100644 --- a/libtonode-tests/tests/concrete.rs +++ b/libtonode-tests/tests/concrete.rs @@ -458,11 +458,11 @@ mod fast { async fn filter_empty_messages() { let mut environment = LibtonodeEnvironment::setup().await; - let faucet = environment.create_faucet().await; - let recipient = environment.create_client().await; + let mut faucet = environment.create_faucet().await; + let mut recipient = environment.create_client().await; environment.bump_chain().await; - faucet.do_sync(false).await.unwrap(); + faucet.sync_and_await(false).await.unwrap(); check_client_balances!(faucet, o: 0 s: 2_500_000_000u64 t: 0u64); @@ -485,7 +485,7 @@ mod fast { .unwrap(); environment.bump_chain().await; - recipient.do_sync(false).await.unwrap(); + recipient.sync_and_await(false).await.unwrap(); let no_messages = &recipient.messages_containing(None).await; @@ -510,7 +510,7 @@ mod fast { .unwrap(); environment.bump_chain().await; - recipient.do_sync(false).await.unwrap(); + recipient.sync_and_await(false).await.unwrap(); let single_message = &recipient.messages_containing(None).await; @@ -532,7 +532,7 @@ mod fast { #[tokio::test] async fn message_thread() { // Begin test setup - let (regtest_manager, _cph, faucet, recipient, _txid) = + let (regtest_manager, _cph, mut faucet, mut recipient, _txid) = scenarios::faucet_funded_recipient_default(10_000_000).await; macro_rules! send_and_sync { ($client:ident, $message:ident) => { @@ -544,7 +544,7 @@ mod fast { .await .unwrap(); // Increase the height and wait for the client - increase_height_and_wait_for_client(®test_manager, &$client, 1) + increase_height_and_wait_for_client(®test_manager, &mut $client, 1) .await .unwrap(); }; @@ -650,7 +650,7 @@ mod fast { send_and_sync!(recipient, alice_to_charlie); send_and_sync!(faucet, charlie_to_alice); // Final sync of recipient - increase_height_and_wait_for_client(®test_manager, &recipient, 1) + increase_height_and_wait_for_client(®test_manager, &mut recipient, 1) .await .unwrap(); @@ -685,11 +685,11 @@ mod fast { async fn value_transfers() { let mut environment = LibtonodeEnvironment::setup().await; - let faucet = environment.create_faucet().await; - let recipient = environment.create_client().await; + let mut faucet = environment.create_faucet().await; + let mut recipient = environment.create_client().await; environment.bump_chain().await; - faucet.do_sync(false).await.unwrap(); + faucet.sync_and_await(false).await.unwrap(); check_client_balances!(faucet, o: 0 s: 2_500_000_000u64 t: 0u64); @@ -722,7 +722,7 @@ mod fast { .unwrap(); environment.bump_chain().await; - recipient.do_sync(false).await.unwrap(); + recipient.sync_and_await(false).await.unwrap(); let value_transfers = &recipient.sorted_value_transfers(true).await; let value_transfers1 = &recipient.sorted_value_transfers(true).await; @@ -802,7 +802,7 @@ mod fast { async fn received_tx_status_pending_to_confirmed_with_mempool_monitor() { tracing_subscriber::fmt().init(); - let (regtest_manager, _cph, faucet, recipient, _txid) = + let (regtest_manager, _cph, faucet, mut recipient, _txid) = scenarios::faucet_funded_recipient_default(100_000).await; from_inputs::quick_send( @@ -817,7 +817,7 @@ mod fast { .await .unwrap(); - recipient.do_sync(false).await.unwrap(); + recipient.sync_and_await(false).await.unwrap(); let transactions = &recipient.transaction_summaries().await.0; transactions.iter().for_each(|tx| { @@ -832,7 +832,7 @@ mod fast { ConfirmationStatus::Mempool(BlockHeight::from_u32(6)) ); - increase_height_and_wait_for_client(®test_manager, &recipient, 1) + increase_height_and_wait_for_client(®test_manager, &mut recipient, 1) .await .unwrap(); @@ -965,7 +965,7 @@ mod fast { #[tokio::test] async fn diversified_addresses_receive_funds_in_best_pool() { - let (regtest_manager, _cph, faucet, recipient) = + let (regtest_manager, _cph, faucet, mut recipient) = scenarios::faucet_recipient_default().await; for code in ["o", "zo", "z"] { recipient.do_new_address(code).await.unwrap(); @@ -978,9 +978,13 @@ mod fast { from_inputs::quick_send(&faucet, address_5000_nonememo_tuples) .await .unwrap(); - zingolib::testutils::increase_height_and_wait_for_client(®test_manager, &recipient, 1) - .await - .unwrap(); + zingolib::testutils::increase_height_and_wait_for_client( + ®test_manager, + &mut recipient, + 1, + ) + .await + .unwrap(); let balance_b = recipient.do_balance().await; assert_eq!( balance_b, @@ -1096,9 +1100,9 @@ mod fast { #[tokio::test] async fn sync_all_epochs_from_sapling() { let regtest_network = RegtestNetwork::new(1, 1, 3, 5, 7, 9, 11); - let (regtest_manager, _cph, lightclient) = + let (regtest_manager, _cph, mut lightclient) = scenarios::unfunded_client(regtest_network, true).await; - increase_height_and_wait_for_client(®test_manager, &lightclient, 14) + increase_height_and_wait_for_client(®test_manager, &mut lightclient, 14) .await .unwrap(); } @@ -1108,14 +1112,14 @@ mod fast { tracing_subscriber::fmt().init(); let regtest_network = RegtestNetwork::all_upgrades_active(); - let (regtest_manager, _cph, faucet) = scenarios::faucet( + let (regtest_manager, _cph, mut faucet) = scenarios::faucet( PoolType::Shielded(ShieldedProtocol::Orchard), regtest_network, true, ) .await; check_client_balances!(faucet, o: 1_875_000_000 s: 0 t: 0); - increase_height_and_wait_for_client(®test_manager, &faucet, 1) + increase_height_and_wait_for_client(®test_manager, &mut faucet, 1) .await .unwrap(); check_client_balances!(faucet, o: 2_500_000_000u64 s: 0 t: 0); @@ -1124,14 +1128,14 @@ mod fast { #[tokio::test] async fn mine_to_sapling() { let regtest_network = RegtestNetwork::all_upgrades_active(); - let (regtest_manager, _cph, faucet) = scenarios::faucet( + let (regtest_manager, _cph, mut faucet) = scenarios::faucet( PoolType::Shielded(ShieldedProtocol::Sapling), regtest_network, true, ) .await; check_client_balances!(faucet, o: 0 s: 1_875_000_000 t: 0); - increase_height_and_wait_for_client(®test_manager, &faucet, 1) + increase_height_and_wait_for_client(®test_manager, &mut faucet, 1) .await .unwrap(); check_client_balances!(faucet, o: 0 s: 2_500_000_000u64 t: 0); @@ -1140,10 +1144,10 @@ mod fast { #[tokio::test] async fn mine_to_transparent() { let regtest_network = RegtestNetwork::all_upgrades_active(); - let (regtest_manager, _cph, faucet, _recipient) = + let (regtest_manager, _cph, mut faucet, _recipient) = scenarios::faucet_recipient(PoolType::Transparent, regtest_network, true).await; check_client_balances!(faucet, o: 0 s: 0 t: 1_875_000_000); - increase_height_and_wait_for_client(®test_manager, &faucet, 1) + increase_height_and_wait_for_client(®test_manager, &mut faucet, 1) .await .unwrap(); check_client_balances!(faucet, o: 0 s: 0 t: 2_500_000_000u64); @@ -1155,9 +1159,9 @@ mod fast { #[tokio::test] async fn sync_all_epochs() { let regtest_network = RegtestNetwork::new(1, 3, 5, 7, 9, 11, 13); - let (regtest_manager, _cph, lightclient) = + let (regtest_manager, _cph, mut lightclient) = scenarios::unfunded_client(regtest_network, true).await; - increase_height_and_wait_for_client(®test_manager, &lightclient, 14) + increase_height_and_wait_for_client(®test_manager, &mut lightclient, 14) .await .unwrap(); } @@ -1167,9 +1171,9 @@ mod fast { #[tokio::test] async fn mine_to_transparent_and_shield() { let regtest_network = RegtestNetwork::all_upgrades_active(); - let (regtest_manager, _cph, faucet, _recipient) = + let (regtest_manager, _cph, mut faucet, _recipient) = scenarios::faucet_recipient(PoolType::Transparent, regtest_network, true).await; - increase_height_and_wait_for_client(®test_manager, &faucet, 100) + increase_height_and_wait_for_client(®test_manager, &mut faucet, 100) .await .unwrap(); faucet.quick_shield().await.unwrap(); @@ -1177,9 +1181,9 @@ mod fast { #[tokio::test] async fn mine_to_transparent_and_propose_shielding() { let regtest_network = RegtestNetwork::all_upgrades_active(); - let (regtest_manager, _cph, faucet, _recipient) = + let (regtest_manager, _cph, mut faucet, _recipient) = scenarios::faucet_recipient(PoolType::Transparent, regtest_network, true).await; - increase_height_and_wait_for_client(®test_manager, &faucet, 1) + increase_height_and_wait_for_client(®test_manager, &mut faucet, 1) .await .unwrap(); let proposal = faucet.propose_shield().await.unwrap(); @@ -1209,9 +1213,9 @@ mod fast { #[tokio::test] async fn mine_to_transparent_and_propose_shielding_with_div_addr() { let regtest_network = RegtestNetwork::all_upgrades_active(); - let (regtest_manager, _cph, faucet, _recipient) = + let (regtest_manager, _cph, mut faucet, _recipient) = scenarios::faucet_recipient(PoolType::Transparent, regtest_network, true).await; - increase_height_and_wait_for_client(®test_manager, &faucet, 1) + increase_height_and_wait_for_client(®test_manager, &mut faucet, 1) .await .unwrap(); faucet.do_new_address("zto").await.unwrap(); @@ -2100,12 +2104,12 @@ mod slow { #[tokio::test] async fn send_orchard_back_and_forth() { // setup - let (regtest_manager, _cph, faucet, recipient) = + let (regtest_manager, _cph, mut faucet, mut recipient) = scenarios::faucet_recipient_default().await; let faucet_to_recipient_amount = 20_000u64; let recipient_to_faucet_amount = 5_000u64; // check start state - faucet.do_sync(true).await.unwrap(); + faucet.sync_and_await(true).await.unwrap(); let wallet_height = faucet.do_wallet_last_scanned_height().await; assert_eq!( wallet_height.as_fixed_point_u64(0).unwrap(), @@ -2129,10 +2133,14 @@ mod slow { .unwrap(); let orch_change = block_rewards::CANOPY - (faucet_to_recipient_amount + u64::from(MINIMUM_FEE)); - zingolib::testutils::increase_height_and_wait_for_client(®test_manager, &recipient, 1) - .await - .unwrap(); - faucet.do_sync(true).await.unwrap(); + zingolib::testutils::increase_height_and_wait_for_client( + ®test_manager, + &mut recipient, + 1, + ) + .await + .unwrap(); + faucet.sync_and_await(true).await.unwrap(); let faucet_orch = three_blocks_reward + orch_change + u64::from(MINIMUM_FEE); println!( @@ -2158,10 +2166,10 @@ mod slow { ) .await .unwrap(); - zingolib::testutils::increase_height_and_wait_for_client(®test_manager, &faucet, 1) + zingolib::testutils::increase_height_and_wait_for_client(®test_manager, &mut faucet, 1) .await .unwrap(); - recipient.do_sync(true).await.unwrap(); + recipient.sync_and_await(true).await.unwrap(); let faucet_final_orch = faucet_orch + recipient_to_faucet_amount @@ -2182,7 +2190,7 @@ mod slow { // consistent with all the notes in the relevant block changing state. // NOTE that the balance doesn't give insight into the distribution across notes. let regtest_network = RegtestNetwork::all_upgrades_active(); - let (regtest_manager, _cph, faucet) = scenarios::faucet( + let (regtest_manager, _cph, mut faucet) = scenarios::faucet( PoolType::Shielded(ShieldedProtocol::Sapling), regtest_network, true, @@ -2226,7 +2234,7 @@ mod slow { ) .await .unwrap(); - zingolib::testutils::increase_height_and_wait_for_client(®test_manager, &faucet, 1) + zingolib::testutils::increase_height_and_wait_for_client(®test_manager, &mut faucet, 1) .await .unwrap(); let balance = faucet.do_balance().await; @@ -2241,13 +2249,13 @@ mod slow { #[tokio::test] async fn send_heartwood_sapling_funds() { let regtest_network = RegtestNetwork::new(1, 1, 1, 1, 3, 5, 5); - let (regtest_manager, _cph, faucet, recipient) = scenarios::faucet_recipient( + let (regtest_manager, _cph, mut faucet, mut recipient) = scenarios::faucet_recipient( PoolType::Shielded(ShieldedProtocol::Sapling), regtest_network, true, ) .await; - increase_height_and_wait_for_client(®test_manager, &faucet, 3) + increase_height_and_wait_for_client(®test_manager, &mut faucet, 3) .await .unwrap(); check_client_balances!(faucet, o: 0 s: 3_500_000_000u64 t: 0); @@ -2262,7 +2270,7 @@ mod slow { .await .unwrap(); check_client_balances!(faucet, o: 0 s: 0 t: 0); - increase_height_and_wait_for_client(®test_manager, &recipient, 1) + increase_height_and_wait_for_client(®test_manager, &mut recipient, 1) .await .unwrap(); check_client_balances!(recipient, o: 3_499_960_000u64 s: 0 t: 0); @@ -2291,7 +2299,7 @@ mod slow { } #[tokio::test] async fn self_send_to_t_displays_as_one_transaction() { - let (regtest_manager, _cph, faucet, recipient) = + let (regtest_manager, _cph, mut faucet, mut recipient) = scenarios::faucet_recipient_default().await; let recipient_unified_address = get_base_address_macro!(recipient, "unified"); let sent_value = 80_000; @@ -2301,9 +2309,13 @@ mod slow { ) .await .unwrap(); - zingolib::testutils::increase_height_and_wait_for_client(®test_manager, &recipient, 1) - .await - .unwrap(); + zingolib::testutils::increase_height_and_wait_for_client( + ®test_manager, + &mut recipient, + 1, + ) + .await + .unwrap(); let recipient_taddr = get_base_address_macro!(recipient, "transparent"); let recipient_zaddr = get_base_address_macro!(recipient, "sapling"); let sent_to_taddr_value = 5_000; @@ -2315,9 +2327,13 @@ mod slow { ) .await .unwrap(); - zingolib::testutils::increase_height_and_wait_for_client(®test_manager, &recipient, 1) - .await - .unwrap(); + zingolib::testutils::increase_height_and_wait_for_client( + ®test_manager, + &mut recipient, + 1, + ) + .await + .unwrap(); from_inputs::quick_send( &recipient, vec![ @@ -2332,7 +2348,7 @@ mod slow { ) .await .unwrap(); - faucet.do_sync(false).await.unwrap(); + faucet.sync_and_await(false).await.unwrap(); from_inputs::quick_send( &faucet, vec![ @@ -2347,9 +2363,13 @@ mod slow { ) .await .unwrap(); - zingolib::testutils::increase_height_and_wait_for_client(®test_manager, &recipient, 1) - .await - .unwrap(); + zingolib::testutils::increase_height_and_wait_for_client( + ®test_manager, + &mut recipient, + 1, + ) + .await + .unwrap(); println!( "{}", json::stringify_pretty(recipient.transaction_summaries().await, 4) @@ -2599,7 +2619,7 @@ mod slow { // } #[tokio::test] async fn sapling_dust_fee_collection() { - let (regtest_manager, __cph, faucet, recipient) = + let (regtest_manager, __cph, faucet, mut recipient) = scenarios::faucet_recipient_default().await; let recipient_sapling = get_base_address_macro!(recipient, "sapling"); let recipient_unified = get_base_address_macro!(recipient, "unified"); @@ -2616,9 +2636,13 @@ mod slow { ) .await .unwrap(); - zingolib::testutils::increase_height_and_wait_for_client(®test_manager, &recipient, 1) - .await - .unwrap(); + zingolib::testutils::increase_height_and_wait_for_client( + ®test_manager, + &mut recipient, + 1, + ) + .await + .unwrap(); check_client_balances!(recipient, o: for_orchard s: for_sapling t: 0 ); from_inputs::quick_send( @@ -2631,9 +2655,13 @@ mod slow { ) .await .unwrap(); - zingolib::testutils::increase_height_and_wait_for_client(®test_manager, &recipient, 1) - .await - .unwrap(); + zingolib::testutils::increase_height_and_wait_for_client( + ®test_manager, + &mut recipient, + 1, + ) + .await + .unwrap(); let remaining_orchard = for_orchard - (6 * fee); check_client_balances!(recipient, o: remaining_orchard s: for_sapling t: 0); } @@ -2703,13 +2731,11 @@ mod slow { // } /// This mod collects tests of outgoing_metadata (a TransactionRecordField) across rescans mod rescan_still_have_outgoing_notes { - use zingolib::validate_outgoing_notes; - use super::*; #[tokio::test] async fn self_send() { - let (regtest_manager, _cph, faucet) = scenarios::faucet_default().await; + let (regtest_manager, _cph, mut faucet) = scenarios::faucet_default().await; let faucet_sapling_addr = get_base_address_macro!(faucet, "sapling"); let mut txids = vec![]; for memo in [None, Some("Second Transaction")] { @@ -2724,22 +2750,23 @@ mod slow { ); zingolib::testutils::increase_height_and_wait_for_client( ®test_manager, - &faucet, + &mut faucet, 1, ) .await .unwrap(); } - let nom_txid = &txids[0]; - let memo_txid = &txids[1]; - validate_outgoing_notes!(faucet, nom_txid, memo_txid); + let pre_rescan_summaries = faucet.transaction_summaries().await; + faucet.do_rescan().await.unwrap(); + let post_rescan_summaries = faucet.transaction_summaries().await; + assert_eq!(pre_rescan_summaries, post_rescan_summaries); } #[tokio::test] async fn external_send() { - let (regtest_manager, _cph, faucet, recipient) = + let (regtest_manager, _cph, mut faucet, recipient) = scenarios::faucet_recipient_default().await; - let external_send_txid_with_memo = *from_inputs::quick_send( + let _external_send_txid_with_memo = *from_inputs::quick_send( &faucet, vec![( get_base_address_macro!(recipient, "sapling").as_str(), @@ -2750,7 +2777,7 @@ mod slow { .await .unwrap() .first(); - let external_send_txid_no_memo = *from_inputs::quick_send( + let _external_send_txid_no_memo = *from_inputs::quick_send( &faucet, vec![( get_base_address_macro!(recipient, "sapling").as_str(), @@ -2763,31 +2790,37 @@ mod slow { .first(); // TODO: This chain height bump should be unnecessary. I think removing // this increase_height call reveals a bug! - zingolib::testutils::increase_height_and_wait_for_client(®test_manager, &faucet, 1) - .await - .unwrap(); - let external_send_txid_no_memo_ref = &external_send_txid_no_memo; - let external_send_txid_with_memo_ref = &external_send_txid_with_memo; - validate_outgoing_notes!( - faucet, - external_send_txid_no_memo_ref, - external_send_txid_with_memo_ref - ); + zingolib::testutils::increase_height_and_wait_for_client( + ®test_manager, + &mut faucet, + 1, + ) + .await + .unwrap(); + + let pre_rescan_summaries = faucet.transaction_summaries().await; + faucet.do_rescan().await.unwrap(); + let post_rescan_summaries = faucet.transaction_summaries().await; + assert_eq!(pre_rescan_summaries, post_rescan_summaries); } #[tokio::test] async fn check_list_value_transfers_across_rescan() { let inital_value = 100_000; - let (ref regtest_manager, _cph, faucet, ref recipient, _txid) = + let (ref regtest_manager, _cph, faucet, mut recipient, _txid) = scenarios::faucet_funded_recipient_default(inital_value).await; from_inputs::quick_send( - recipient, + &recipient, vec![(&get_base_address_macro!(faucet, "unified"), 10_000, None); 2], ) .await .unwrap(); - zingolib::testutils::increase_height_and_wait_for_client(regtest_manager, recipient, 1) - .await - .unwrap(); + zingolib::testutils::increase_height_and_wait_for_client( + regtest_manager, + &mut recipient, + 1, + ) + .await + .unwrap(); let pre_rescan_transactions = recipient.transaction_summaries().await; let pre_rescan_summaries = recipient.sorted_value_transfers(true).await; recipient.do_rescan().await.unwrap(); @@ -2804,9 +2837,9 @@ mod slow { // In addition to testing the order in which notes are selected this test: // * sends to a sapling address // * sends back to the original sender's UA - let (regtest_manager, _cph, faucet, recipient) = + let (regtest_manager, _cph, mut faucet, mut recipient) = scenarios::faucet_recipient_default().await; - zingolib::testutils::increase_height_and_wait_for_client(®test_manager, &faucet, 5) + zingolib::testutils::increase_height_and_wait_for_client(®test_manager, &mut faucet, 5) .await .unwrap(); @@ -2830,9 +2863,13 @@ mod slow { .await .unwrap(); - zingolib::testutils::increase_height_and_wait_for_client(®test_manager, &recipient, 5) - .await - .unwrap(); + zingolib::testutils::increase_height_and_wait_for_client( + ®test_manager, + &mut recipient, + 5, + ) + .await + .unwrap(); // We know that the largest single note that 2 received from 1 was 30_000, for 2 to send // 30_000 back to 1 it will have to collect funds from two notes to pay the full 30_000 // plus the transaction fee. @@ -2921,7 +2958,7 @@ mod slow { #[tokio::test] async fn mempool_and_balance() { let value = 100_000; - let (regtest_manager, _cph, faucet, recipient, _txid) = + let (regtest_manager, _cph, faucet, mut recipient, _txid) = scenarios::faucet_funded_recipient_default(value).await; let bal = recipient.do_balance().await; @@ -2931,9 +2968,13 @@ mod slow { assert_eq!(bal.verified_orchard_balance.unwrap(), value); // 3. Mine 10 blocks - zingolib::testutils::increase_height_and_wait_for_client(®test_manager, &recipient, 10) - .await - .unwrap(); + zingolib::testutils::increase_height_and_wait_for_client( + ®test_manager, + &mut recipient, + 10, + ) + .await + .unwrap(); let bal = recipient.do_balance().await; assert_eq!(bal.orchard_balance.unwrap(), value); assert_eq!(bal.verified_orchard_balance.unwrap(), value); @@ -2963,9 +3004,13 @@ mod slow { assert_eq!(bal.unverified_orchard_balance.unwrap(), new_bal); // 5. Mine the pending block, making the funds verified and spendable. - zingolib::testutils::increase_height_and_wait_for_client(®test_manager, &recipient, 10) - .await - .unwrap(); + zingolib::testutils::increase_height_and_wait_for_client( + ®test_manager, + &mut recipient, + 10, + ) + .await + .unwrap(); let bal = recipient.do_balance().await; @@ -2981,20 +3026,20 @@ mod slow { // Check that list_value_transfers behaves correctly given different fee scenarios let (regtest_manager, _cph, mut client_builder, regtest_network) = scenarios::custom_clients_default().await; - let faucet = client_builder.build_faucet(false, regtest_network).await; - let pool_migration_client = client_builder + let mut faucet = client_builder.build_faucet(false, regtest_network).await; + let mut pool_migration_client = client_builder .build_client(HOSPITAL_MUSEUM_SEED.to_string(), 0, false, regtest_network) .await; let pmc_taddr = get_base_address_macro!(pool_migration_client, "transparent"); let pmc_sapling = get_base_address_macro!(pool_migration_client, "sapling"); let pmc_unified = get_base_address_macro!(pool_migration_client, "unified"); // Ensure that the client has confirmed spendable funds - zingolib::testutils::increase_height_and_wait_for_client(®test_manager, &faucet, 3) + zingolib::testutils::increase_height_and_wait_for_client(®test_manager, &mut faucet, 3) .await .unwrap(); macro_rules! bump_and_check_pmc { (o: $o:tt s: $s:tt t: $t:tt) => { - zingolib::testutils::increase_height_and_wait_for_client(®test_manager, &pool_migration_client, 1).await.unwrap(); + zingolib::testutils::increase_height_and_wait_for_client(®test_manager, &mut pool_migration_client, 1).await.unwrap(); check_client_balances!(pool_migration_client, o:$o s:$s t:$t); }; } @@ -3027,8 +3072,8 @@ mod slow { // sapling value into the orchard value pool. let (regtest_manager, _cph, mut client_builder, regtest_network) = scenarios::custom_clients_default().await; - let sapling_faucet = client_builder.build_faucet(false, regtest_network).await; - let client = client_builder + let mut sapling_faucet = client_builder.build_faucet(false, regtest_network).await; + let mut client = client_builder .build_client(HOSPITAL_MUSEUM_SEED.to_string(), 0, false, regtest_network) .await; let pmc_taddr = get_base_address_macro!(client, "transparent"); @@ -3037,14 +3082,14 @@ mod slow { // Ensure that the client has confirmed spendable funds zingolib::testutils::increase_height_and_wait_for_client( ®test_manager, - &sapling_faucet, + &mut sapling_faucet, 1, ) .await .unwrap(); macro_rules! bump_and_check { (o: $o:tt s: $s:tt t: $t:tt) => { - zingolib::testutils::increase_height_and_wait_for_client(®test_manager, &client, 1).await.unwrap(); + zingolib::testutils::increase_height_and_wait_for_client(®test_manager, &mut client, 1).await.unwrap(); check_client_balances!(client, o:$o s:$s t:$t); }; } @@ -3325,9 +3370,9 @@ mod slow { } #[tokio::test] async fn factor_do_shield_to_call_do_send() { - let (regtest_manager, __cph, faucet, recipient) = + let (regtest_manager, __cph, mut faucet, recipient) = scenarios::faucet_recipient_default().await; - zingolib::testutils::increase_height_and_wait_for_client(®test_manager, &faucet, 2) + zingolib::testutils::increase_height_and_wait_for_client(®test_manager, &mut faucet, 2) .await .unwrap(); from_inputs::quick_send( @@ -3363,10 +3408,10 @@ mod slow { #[tokio::test] async fn by_address_finsight() { - let (regtest_manager, _cph, faucet, recipient) = + let (regtest_manager, _cph, mut faucet, recipient) = scenarios::faucet_recipient_default().await; let base_uaddress = get_base_address_macro!(recipient, "unified"); - zingolib::testutils::increase_height_and_wait_for_client(®test_manager, &faucet, 2) + zingolib::testutils::increase_height_and_wait_for_client(®test_manager, &mut faucet, 2) .await .unwrap(); from_inputs::quick_send(&faucet, vec![(&base_uaddress, 1_000u64, Some("1"))]) @@ -3654,7 +3699,7 @@ mod basic_transactions { #[tokio::test] async fn send_and_sync_with_multiple_notes_no_panic() { - let (regtest_manager, _cph, faucet, recipient) = + let (regtest_manager, _cph, mut faucet, mut recipient) = scenarios::faucet_recipient_default().await; let recipient_addr_ua = get_base_address_macro!(recipient, "unified"); @@ -3664,8 +3709,8 @@ mod basic_transactions { .await .unwrap(); - recipient.do_sync(true).await.unwrap(); - faucet.do_sync(true).await.unwrap(); + recipient.sync_and_await(true).await.unwrap(); + faucet.sync_and_await(true).await.unwrap(); for _ in 0..2 { from_inputs::quick_send(&faucet, vec![(recipient_addr_ua.as_str(), 40_000, None)]) @@ -3677,8 +3722,8 @@ mod basic_transactions { .await .unwrap(); - recipient.do_sync(true).await.unwrap(); - faucet.do_sync(true).await.unwrap(); + recipient.sync_and_await(true).await.unwrap(); + faucet.sync_and_await(true).await.unwrap(); from_inputs::quick_send(&recipient, vec![(faucet_addr_ua.as_str(), 50_000, None)]) .await @@ -3688,8 +3733,8 @@ mod basic_transactions { .await .unwrap(); - recipient.do_sync(true).await.unwrap(); - faucet.do_sync(true).await.unwrap(); + recipient.sync_and_await(true).await.unwrap(); + faucet.sync_and_await(true).await.unwrap(); } // FIXME: @@ -4039,7 +4084,7 @@ async fn proxy_server_worky() { // FIXME: does not assert dust was included in the proposal #[tokio::test] async fn propose_orchard_dust_to_sapling() { - let (regtest_manager, _cph, faucet, recipient, _) = + let (regtest_manager, _cph, faucet, mut recipient, _) = scenarios::faucet_funded_recipient_default(100_000).await; from_inputs::quick_send( @@ -4048,7 +4093,7 @@ async fn propose_orchard_dust_to_sapling() { ) .await .unwrap(); - increase_height_and_wait_for_client(®test_manager, &recipient, 1) + increase_height_and_wait_for_client(®test_manager, &mut recipient, 1) .await .unwrap(); @@ -4067,7 +4112,7 @@ mod send_all { use super::*; #[tokio::test] async fn toggle_zennies_for_zingo() { - let (regtest_manager, _cph, faucet, recipient) = + let (regtest_manager, _cph, faucet, mut recipient) = scenarios::faucet_recipient_default().await; let initial_funds = 2_000_000; @@ -4083,7 +4128,7 @@ mod send_all { ) .await .unwrap(); - increase_height_and_wait_for_client(®test_manager, &recipient, 1) + increase_height_and_wait_for_client(®test_manager, &mut recipient, 1) .await .unwrap(); let external_uaddress = @@ -4101,7 +4146,7 @@ mod send_all { #[tokio::test] async fn ptfm_general() { - let (regtest_manager, _cph, faucet, recipient, _) = + let (regtest_manager, _cph, mut faucet, mut recipient, _) = scenarios::faucet_funded_recipient_default(100_000).await; from_inputs::quick_send( @@ -4110,7 +4155,7 @@ mod send_all { ) .await .unwrap(); - increase_height_and_wait_for_client(®test_manager, &faucet, 1) + increase_height_and_wait_for_client(®test_manager, &mut faucet, 1) .await .unwrap(); from_inputs::quick_send( @@ -4123,7 +4168,7 @@ mod send_all { ) .await .unwrap(); - increase_height_and_wait_for_client(®test_manager, &faucet, 1) + increase_height_and_wait_for_client(®test_manager, &mut faucet, 1) .await .unwrap(); from_inputs::quick_send( @@ -4132,7 +4177,7 @@ mod send_all { ) .await .unwrap(); - increase_height_and_wait_for_client(®test_manager, &faucet, 1) + increase_height_and_wait_for_client(®test_manager, &mut faucet, 1) .await .unwrap(); from_inputs::quick_send( @@ -4141,10 +4186,10 @@ mod send_all { ) .await .unwrap(); - increase_height_and_wait_for_client(®test_manager, &faucet, 1) + increase_height_and_wait_for_client(®test_manager, &mut faucet, 1) .await .unwrap(); - recipient.do_sync(false).await.unwrap(); + recipient.sync_and_await(false).await.unwrap(); recipient .propose_send_all( @@ -4158,10 +4203,10 @@ mod send_all { .complete_and_broadcast_stored_proposal() .await .unwrap(); - increase_height_and_wait_for_client(®test_manager, &recipient, 1) + increase_height_and_wait_for_client(®test_manager, &mut recipient, 1) .await .unwrap(); - faucet.do_sync(false).await.unwrap(); + faucet.sync_and_await(false).await.unwrap(); assert_eq!( recipient diff --git a/libtonode-tests/tests/shield_transparent.rs b/libtonode-tests/tests/shield_transparent.rs index 502bf71c1..0c9fa0b4d 100644 --- a/libtonode-tests/tests/shield_transparent.rs +++ b/libtonode-tests/tests/shield_transparent.rs @@ -4,7 +4,7 @@ use zingolib::testutils::{lightclient::from_inputs, scenarios::faucet_recipient_ #[tokio::test] #[ignore] async fn shield_transparent() { - let (regtest_manager, _cph, faucet, recipient) = faucet_recipient_default().await; + let (regtest_manager, _cph, faucet, mut recipient) = faucet_recipient_default().await; let transparent_funds = 100_000; println!( @@ -32,7 +32,7 @@ async fn shield_transparent() { serde_json::to_string_pretty(&faucet.do_balance().await).unwrap(), serde_json::to_string_pretty(&recipient.do_balance().await).unwrap(), ); - zingolib::testutils::increase_height_and_wait_for_client(®test_manager, &recipient, 1) + zingolib::testutils::increase_height_and_wait_for_client(®test_manager, &mut recipient, 1) .await .unwrap(); @@ -53,7 +53,7 @@ async fn shield_transparent() { .complete_and_broadcast_stored_proposal() .await .unwrap(); - zingolib::testutils::increase_height_and_wait_for_client(®test_manager, &recipient, 1) + zingolib::testutils::increase_height_and_wait_for_client(®test_manager, &mut recipient, 1) .await .unwrap(); diff --git a/libtonode-tests/tests/sync.rs b/libtonode-tests/tests/sync.rs index 78cb3da71..c06f04726 100644 --- a/libtonode-tests/tests/sync.rs +++ b/libtonode-tests/tests/sync.rs @@ -25,7 +25,7 @@ async fn sync_mainnet_test() { zingolib::config::ChainType::Mainnet, ) .unwrap(); - let lightclient = LightClient::create_from_wallet_base_async( + let mut lightclient = LightClient::create_from_wallet_base_async( WalletBase::from_string(HOSPITAL_MUSEUM_SEED.to_string()), &config, 2_650_318, @@ -34,7 +34,7 @@ async fn sync_mainnet_test() { .await .unwrap(); - lightclient.do_sync(false).await.unwrap(); + lightclient.sync_and_await(false).await.unwrap(); let wallet = lightclient.wallet.lock().await; // dbg!(&wallet.wallet_blocks); @@ -59,7 +59,7 @@ async fn sync_status() { zingolib::config::ChainType::Mainnet, ) .unwrap(); - let lightclient = LightClient::create_from_wallet_base_async( + let mut lightclient = LightClient::create_from_wallet_base_async( WalletBase::from_string(HOSPITAL_MUSEUM_SEED.to_string()), &config, // 2_750_000, @@ -69,7 +69,7 @@ async fn sync_status() { .await .unwrap(); - lightclient.do_sync(true).await.unwrap(); + lightclient.sync_and_await(true).await.unwrap(); } // temporary test for sync development @@ -78,7 +78,7 @@ async fn sync_status() { async fn sync_test() { tracing_subscriber::fmt().init(); - let (regtest_manager, _cph, faucet, recipient, _txid) = + let (regtest_manager, _cph, faucet, mut recipient, _txid) = scenarios::faucet_funded_recipient_default(5_000_000).await; from_inputs::quick_send( &faucet, @@ -102,10 +102,10 @@ async fn sync_test() { // .unwrap(); increase_server_height(®test_manager, 1).await; - recipient.do_sync(false).await.unwrap(); + recipient.sync_and_await(false).await.unwrap(); recipient.quick_shield().await.unwrap(); increase_server_height(®test_manager, 1).await; - recipient.do_sync(true).await.unwrap(); + recipient.sync_and_await(true).await.unwrap(); // let wallet = recipient.wallet.lock().await; // dbg!(&wallet.wallet_transactions); diff --git a/libtonode-tests/tests/wallet.rs b/libtonode-tests/tests/wallet.rs index 6468357b7..8f01006fc 100644 --- a/libtonode-tests/tests/wallet.rs +++ b/libtonode-tests/tests/wallet.rs @@ -309,10 +309,10 @@ mod load_wallet { // interrupting send, it made it immediately obvious that this was // the wrong height to use! The correct height is the // "mempool height" which is the server_height + 1 - let (regtest_manager, _cph, faucet, recipient) = + let (regtest_manager, _cph, mut faucet, recipient) = scenarios::faucet_recipient_default().await; // Ensure that the client has confirmed spendable funds - zingolib::testutils::increase_height_and_wait_for_client(®test_manager, &faucet, 5) + zingolib::testutils::increase_height_and_wait_for_client(®test_manager, &mut faucet, 5) .await .unwrap(); diff --git a/pepper-sync/src/sync.rs b/pepper-sync/src/sync.rs index 91a2f04e4..af4d895f5 100644 --- a/pepper-sync/src/sync.rs +++ b/pepper-sync/src/sync.rs @@ -32,7 +32,7 @@ use crate::scan::ScanResults; use crate::wallet::traits::{ SyncBlocks, SyncNullifiers, SyncOutPoints, SyncShardTrees, SyncTransactions, SyncWallet, }; -use crate::wallet::{NullifierMap, SyncMode, SyncStatus}; +use crate::wallet::{NullifierMap, SyncMode, SyncResult, SyncStatus}; use crate::witness; pub mod error; @@ -45,19 +45,24 @@ pub(crate) const MAX_VERIFICATION_WINDOW: u32 = 100; /// Syncs a wallet to the latest state of the blockchain. /// -/// `sync_mode` is intended to be stored as [`crate::wallet::SyncMode`] and converted with TODO. +/// `sync_mode` is intended to be stored in a struct that owns the wallet(s) (i.e. lightclient) and has a non-atomic +/// counterpart [`crate::wallet::SyncMode`]. The sync engine will set the `sync_mode` to `Running` or `NotRunning` +/// at the start and finish of sync, respectively. `sync_mode` may also be set to `Paused` externally to drop the wallet +/// lock after the next batch is completed and pause scanning. Setting `sync_mode` back to `Running` will resume +/// scanning when the wallet guard is next available. +// TODO: setting sync_mode to `NotRunning` should kill the sync task immediately. pub async fn sync( client: CompactTxStreamerClient, consensus_parameters: &P, wallet: Arc>, sync_mode: Arc, -) -> Result<(), SyncError> +) -> Result where P: consensus::Parameters + Sync + Send + 'static, W: SyncWallet + SyncBlocks + SyncTransactions + SyncNullifiers + SyncOutPoints + SyncShardTrees, { let mut sync_mode_enum = SyncMode::from_u8(sync_mode.load(atomic::Ordering::Acquire)).unwrap(); - if sync_mode_enum == SyncMode::Stopped { + if sync_mode_enum == SyncMode::NotRunning { sync_mode_enum = SyncMode::Running; sync_mode.store(sync_mode_enum as u8, atomic::Ordering::Release); } else { @@ -231,25 +236,45 @@ where } } + let sync_status = sync_status(&*wallet_guard).await; + drop(wallet_guard); drop(scanner); drop(fetch_request_sender); - mempool_handle.await.unwrap()?; - fetcher_handle.await.unwrap().unwrap(); - Ok(()) + match mempool_handle.await.unwrap() { + Ok(_) => (), + Err(e @ MempoolError::ShutdownWithoutStream) => tracing::warn!("{e}"), + Err(e) => return Err(e.into()), + } + fetcher_handle.await.unwrap().unwrap(); + sync_mode.store(SyncMode::NotRunning as u8, atomic::Ordering::Release); + + Ok(SyncResult { + sync_start_height: sync_status.sync_start_height, + sync_end_height: (sync_status + .scan_ranges + .last() + .expect("should be non-empty after syncing") + .block_range() + .end + - 1) + .into(), + scanned_blocks: sync_status.scanned_blocks, + scanned_sapling_outputs: sync_status.scanned_sapling_outputs, + scanned_orchard_outputs: sync_status.scanned_orchard_outputs, + }) } /// Obtains the mutex guard to the wallet and creates a [`crate::wallet::SyncStatus`] from the wallet's current /// [`crate::wallet::SyncState`]. /// /// Designed to be called during the sync process with minimal interruption. -pub async fn sync_status(wallet: Arc>) -> SyncStatus +pub async fn sync_status(wallet: &W) -> SyncStatus where W: SyncWallet + SyncBlocks, { - let wallet_guard = wallet.lock().await; - let sync_state = wallet_guard.get_sync_state().unwrap().clone(); + let sync_state = wallet.get_sync_state().unwrap().clone(); let unscanned_blocks = sync_state .scan_ranges() @@ -267,7 +292,7 @@ where (scanned_blocks as f32 / sync_state.initial_sync_state.total_blocks_to_scan as f32) * 100.0; let (unscanned_sapling_outputs, unscanned_orchard_outputs) = - state::calculate_unscanned_outputs(&*wallet_guard); + state::calculate_unscanned_outputs(wallet); let scanned_sapling_outputs = sync_state .initial_sync_state .total_sapling_outputs_to_scan diff --git a/pepper-sync/src/wallet.rs b/pepper-sync/src/wallet.rs index a1b72281e..4fe6e4556 100644 --- a/pepper-sync/src/wallet.rs +++ b/pepper-sync/src/wallet.rs @@ -196,7 +196,7 @@ impl Default for SyncState { #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum SyncMode { /// Sync is not running. - Stopped, + NotRunning, /// Sync is held in a paused state and the wallet guard is dropped. Paused, /// Sync is running. @@ -209,7 +209,7 @@ impl SyncMode { /// Returns `None` if `mode` is not a valid enum variant. pub fn from_u8(mode: u8) -> Option { match mode { - 0 => Some(Self::Stopped), + 0 => Some(Self::NotRunning), 1 => Some(Self::Paused), 2 => Some(Self::Running), _ => None, @@ -288,6 +288,49 @@ impl From for json::JsonValue { } } +/// Returned when [`crate::sync::sync`] successfully completes. +#[derive(Debug, Clone)] +#[allow(missing_docs)] +pub struct SyncResult { + pub sync_start_height: BlockHeight, + pub sync_end_height: BlockHeight, + pub scanned_blocks: u32, + pub scanned_sapling_outputs: u32, + pub scanned_orchard_outputs: u32, +} + +impl std::fmt::Display for SyncResult { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "{{ + sync start height: {} + sync end height: {} + scanned blocks: {} + scanned sapling outputs: {} + scanned orchard outputs: {} + }}", + self.sync_start_height, + self.sync_end_height, + self.scanned_blocks, + self.scanned_sapling_outputs, + self.scanned_orchard_outputs + ) + } +} + +impl From for json::JsonValue { + fn from(value: SyncResult) -> Self { + json::object! { + "sync_start_height" => u32::from(value.sync_start_height), + "sync_end_height" => u32::from(value.sync_end_height), + "scanned_blocks" => value.scanned_blocks, + "scanned_sapling_outputs" => value.scanned_sapling_outputs, + "scanned_orchard_outputs" => value.scanned_orchard_outputs, + } + } +} + /// Output ID for a given pool type. #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)] pub struct OutputId { diff --git a/zingolib/src/commands.rs b/zingolib/src/commands.rs index ed9faceed..5944fd9d2 100644 --- a/zingolib/src/commands.rs +++ b/zingolib/src/commands.rs @@ -368,16 +368,18 @@ impl Command for SyncCommand { return self.help().to_string(); } - RT.block_on(async move { - // TODO: improve zingo CLI sync status updates - match lightclient.do_sync(true).await { - Ok(j) => j.to_json().pretty(2), - Err(e) => e, - } - }) + // RT.block_on(async move { + // // TODO: improve zingo CLI sync status updates + // match lightclient.sync_and_await(true).await { + // Ok(j) => j.to_json().pretty(2), + // Err(e) => e, + // } + // }) + todo!() } } +// FIXME: add to sync command struct SyncStatusCommand {} impl Command for SyncStatusCommand { fn help(&self) -> &'static str { @@ -399,10 +401,11 @@ impl Command for SyncStatusCommand { return self.help().to_string(); } - RT.block_on(async move { - json::JsonValue::from(pepper_sync::sync_status(lightclient.wallet.clone()).await) - .pretty(2) - }) + // RT.block_on(async move { + // json::JsonValue::from(pepper_sync::sync_status(lightclient.wallet.clone()).await) + // .pretty(2) + // }) + todo!() } } diff --git a/zingolib/src/lightclient.rs b/zingolib/src/lightclient.rs index 70f036b63..8351702a2 100644 --- a/zingolib/src/lightclient.rs +++ b/zingolib/src/lightclient.rs @@ -2,9 +2,13 @@ use json::{array, object, JsonValue}; use log::error; +use pepper_sync::{error::SyncError, wallet::SyncResult}; use serde::Serialize; use std::sync::{atomic::AtomicU8, Arc}; -use tokio::sync::{Mutex, RwLock}; +use tokio::{ + sync::{Mutex, RwLock}, + task::JoinHandle, +}; use zcash_client_backend::encoding::{decode_payment_address, encode_payment_address}; use zcash_primitives::{ @@ -12,70 +16,12 @@ use zcash_primitives::{ memo::{Memo, MemoBytes}, }; -use crate::config::ZingoConfig; +use crate::{config::ZingoConfig, data::proposal::ZingoProposal}; use crate::wallet::{ keys::unified::ReceiverSelection, message::Message, LightWallet, SendProgress, }; -use crate::data::proposal::ZingoProposal; - -/// TODO: Add Doc Comment Here! -#[derive(Clone, Debug, Default)] -pub struct SyncResult { - /// TODO: Add Doc Comment Here! - pub success: bool, - /// TODO: Add Doc Comment Here! - pub latest_block: u64, - /// TODO: Add Doc Comment Here! - pub total_blocks_synced: u64, -} - -impl SyncResult { - /// Converts this object to a JSON object that meets the contract expected by Zingo Mobile. - pub fn to_json(&self) -> JsonValue { - object! { - "result" => if self.success { "success" } else { "failure" }, - "latest_block" => self.latest_block, - "total_blocks_synced" => self.total_blocks_synced, - } - } -} - -impl std::fmt::Display for SyncResult { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.write_str( - format!( - "{{ success: {}, latest_block: {}, total_blocks_synced: {}}}", - self.success, self.latest_block, self.total_blocks_synced - ) - .as_str(), - ) - } -} - -/// TODO: Add Doc Comment Here! -#[derive(Clone, Debug, Default)] -pub struct WalletStatus { - /// TODO: Add Doc Comment Here! - pub is_syncing: bool, - /// TODO: Add Doc Comment Here! - pub total_blocks: u64, - /// TODO: Add Doc Comment Here! - pub synced_blocks: u64, -} - -impl WalletStatus { - /// TODO: Add Doc Comment Here! - pub fn new() -> Self { - WalletStatus { - is_syncing: false, - total_blocks: 0, - synced_blocks: 0, - } - } -} - /// TODO: Add Doc Comment Here! #[derive(Debug, Clone)] pub struct LightWalletSendProgress { @@ -299,6 +245,7 @@ pub struct LightClient { /// Wallet data pub wallet: Arc>, sync_mode: Arc, + sync_handle: Option>>, latest_proposal: Arc>>, // TODO: move to wallet save_buffer: ZingoSaveBuffer, // TODO: move save buffer to wallet itself? } @@ -335,7 +282,8 @@ pub mod instantiation { Ok(LightClient { config, wallet: Arc::new(Mutex::new(wallet)), - sync_mode: Arc::new(AtomicU8::new(SyncMode::Stopped as u8)), + sync_mode: Arc::new(AtomicU8::new(SyncMode::NotRunning as u8)), + sync_handle: None, latest_proposal: Arc::new(RwLock::new(None)), save_buffer: ZingoSaveBuffer::new(buffer), }) diff --git a/zingolib/src/lightclient/describe.rs b/zingolib/src/lightclient/describe.rs index b09e9741f..238d4fb84 100644 --- a/zingolib/src/lightclient/describe.rs +++ b/zingolib/src/lightclient/describe.rs @@ -217,7 +217,7 @@ impl LightClient { } /// TODO: Add Doc Comment Here! - // TODO: revisit + // TODO: remove pub async fn do_wallet_last_scanned_height(&self) -> JsonValue { json::JsonValue::from(u32::from( self.wallet.lock().await.sync_state.fully_scanned_height(), diff --git a/zingolib/src/lightclient/sync.rs b/zingolib/src/lightclient/sync.rs index a86782740..75cc53308 100644 --- a/zingolib/src/lightclient/sync.rs +++ b/zingolib/src/lightclient/sync.rs @@ -3,39 +3,38 @@ //! the difference between this and wallet/sync.rs is that these can interact with the network layer. use std::sync::atomic; -use std::sync::atomic::AtomicBool; +use std::sync::atomic::AtomicU8; use std::sync::Arc; use std::time::Duration; -use log::{debug, error}; -use pepper_sync::error::SyncError; -use pepper_sync::sync::error::MempoolError; +use log::debug; use pepper_sync::wallet::SyncMode; +use zingo_netutils::GetClientError; use super::LightClient; use super::SyncResult; -#[allow(missing_docs)] // error types document themselves -#[derive(Debug, thiserror::Error)] -/// likely no errors here. but this makes clippy (and fv) happier -pub enum StartMempoolMonitorError { - #[error("Mempool Monitor is disabled.")] - Disabled, - #[error("could not read mempool monitor: {0}")] - CouldNotRead(String), - #[error("could not write mempool monitor: {0}")] - CouldNotWrite(String), - #[error("Mempool Monitor does not exist.")] - DoesNotExist, -} - impl LightClient { - /// Sync the wallet to the latest state of the block chain. - pub async fn do_sync(&self, print_updates: bool) -> Result { + /// Calls [`self::sync`] and awaits the handle. + // TODO: error handling, concrete error types + pub async fn sync_and_await(&mut self, print_updates: bool) -> Result { + self.sync(print_updates).await.map_err(|e| e.to_string())?; + + Ok(self + .sync_handle + .take() + .expect("handle should always exist after calling `sync`") + .await + .map_err(|e| e.to_string())? + .map_err(|e| e.to_string())?) + } + + /// Launches a task for syncing the wallet to the latest state of the block chain, storing the handle in the + /// `sync_handle` field. + pub async fn sync(&mut self, print_updates: bool) -> Result<(), GetClientError> { let client = zingo_netutils::GrpcConnector::new(self.config.get_lightwalletd_uri()) .get_client() - .await - .unwrap(); + .await?; let network = self.wallet.lock().await.network; let wallet = self.wallet.clone(); let sync_mode = self.sync_mode.clone(); @@ -43,71 +42,48 @@ impl LightClient { tokio::spawn( async move { pepper_sync::sync(client, &network, wallet, sync_mode).await }, ); + self.sync_handle = Some(sync_handle); - // FIXME: replace with lightclient syncing field - let syncing = Arc::new(AtomicBool::new(true)); + // TODO: replace this with calls to `sync status` in zingo-cli if print_updates { - let syncing = syncing.clone(); + let sync_mode = self.sync_mode.clone(); let wallet = self.wallet.clone(); tokio::spawn(async move { loop { - if !syncing.load(atomic::Ordering::Acquire) { + if LightClient::sync_mode(sync_mode.clone()) == SyncMode::NotRunning { break; }; - let sync_status = pepper_sync::sync_status(wallet.clone()).await; + let sync_status = pepper_sync::sync_status(&*wallet.lock().await).await; println!("{}", sync_status); tokio::time::sleep(Duration::from_secs(1)).await; } }); } - match sync_handle.await.unwrap() { - Ok(_) => (), - Err(SyncError::MempoolError(e @ MempoolError::ShutdownWithoutStream)) => { - log::warn!("{}", e); - } - Err(e) => return Err(e.to_string()), - } - syncing.store(false, atomic::Ordering::Release); - - let final_sync_status = pepper_sync::sync_status(self.wallet.clone()).await; - if print_updates { - println!("{}", &final_sync_status); - } - - Ok(SyncResult { - success: true, - latest_block: (final_sync_status - .scan_ranges - .last() - .expect("should be non-empty after syncing") - .block_range() - .end - - 1) - .into(), - total_blocks_synced: final_sync_status.scanned_blocks as u64, - }) + Ok(()) } /// Clear the wallet state and rescan from wallet birthday. - pub async fn do_rescan(&self) -> Result { + pub async fn do_rescan(&mut self) -> Result { debug!("Rescan starting"); self.wallet.lock().await.clear_all(); - let response = self.do_sync(false).await; + let response = self.sync_and_await(false).await; debug!("Rescan finished"); response } - pub fn sync_mode(&self) -> SyncMode { - SyncMode::from_u8(self.sync_mode.load(atomic::Ordering::Acquire)) - .expect("API does not support setting of non-valid variant values") + /// Creates [`pepper_sync::wallet::SyncMode`] from an atomic u8. + pub fn sync_mode(atomic_sync_mode: Arc) -> SyncMode { + SyncMode::from_u8(atomic_sync_mode.load(atomic::Ordering::Acquire)) + .expect("this library does not allow setting of non-valid sync mode variants") } + // TODO: split into separate functions with checks pub fn set_sync_mode(&self, sync_mode: SyncMode) { self.sync_mode .store(sync_mode as u8, atomic::Ordering::Release); @@ -129,9 +105,10 @@ pub mod test { log::error!("Error installing crypto provider: {:?}", e) }; - let lc = wallet_case.load_example_wallet_with_client().await; + let mut lc = wallet_case.load_example_wallet_with_client().await; - lc.do_sync(true).await.unwrap(); + let sync_result = lc.sync_and_await(false).await.unwrap(); + println!("{}", sync_result); println!("{:?}", lc.do_balance().await); lc } diff --git a/zingolib/src/testutils.rs b/zingolib/src/testutils.rs index aabca6f4d..94f74b4e8 100644 --- a/zingolib/src/testutils.rs +++ b/zingolib/src/testutils.rs @@ -3,7 +3,6 @@ #![warn(missing_docs)] -pub mod interrupts; pub mod scenarios; // use crate::lightclient::describe::UAReceivers; @@ -30,7 +29,6 @@ use crate::lightclient::LightClient; use json::JsonValue; // use log::debug; use regtest::RegtestManager; -use tokio::time::sleep; pub mod assertions; pub mod chain_generics; @@ -79,15 +77,6 @@ pub async fn build_fvk_client(fvks: &[&Fvk], zingoconfig: &ZingoConfig) -> Light .unwrap() } -async fn get_synced_wallet_height(client: &LightClient) -> Result { - client.do_sync(false).await?; - Ok(client - .do_wallet_last_scanned_height() - .await - .as_u32() - .unwrap()) -} - fn poll_server_height(manager: &RegtestManager) -> JsonValue { let temp_tips = manager.get_chain_tip().unwrap().stdout; let tips = json::parse(&String::from_utf8_lossy(&temp_tips)).unwrap(); @@ -104,7 +93,7 @@ pub async fn increase_server_height(manager: &RegtestManager, n: u32) { .expect("Called for side effect, failed!"); let mut count = 0; while poll_server_height(manager).as_fixed_point_u64(2).unwrap() < target { - sleep(Duration::from_millis(50)).await; + tokio::time::sleep(Duration::from_millis(50)).await; count = dbg!(count + 1); } } @@ -250,14 +239,14 @@ fn check_spend_status_equality(first: SpendStatus, second: SpendStatus) -> bool /// This function increases the chain height reliably (with polling) but /// it _also_ ensures that the client state is synced. -/// Unsynced clients are very interesting to us. See increate_server_height +/// Unsynced clients are very interesting to us. See increase_server_height /// to reliably increase the server without syncing the client pub async fn increase_height_and_wait_for_client( manager: &RegtestManager, - client: &LightClient, + client: &mut LightClient, n: u32, ) -> Result<(), String> { - wait_until_client_reaches_block_height( + sync_to_target_height( client, generate_n_blocks_return_new_height(manager, n) .await @@ -280,21 +269,20 @@ pub async fn generate_n_blocks_return_new_height( Ok(target) } -/// will hang if RegtestManager does not reach target_block_height -pub async fn wait_until_client_reaches_block_height( - client: &LightClient, +/// Will hang if chain does not reach `target_block_height` +pub async fn sync_to_target_height( + client: &mut LightClient, target_block_height: u32, ) -> Result<(), String> { - while check_wallet_chainheight_value(client, target_block_height).await? { - sleep(Duration::from_millis(50)).await; + while u32::from(client.wallet.lock().await.sync_state.fully_scanned_height()) + < target_block_height + { + tokio::time::sleep(Duration::from_millis(500)).await; + client.sync_and_await(false).await.unwrap(); } Ok(()) } -async fn check_wallet_chainheight_value(client: &LightClient, target: u32) -> Result { - Ok(get_synced_wallet_height(client).await? != target) -} - /// TODO: Add Doc Comment Here! pub struct RecordingReader { from: Reader, @@ -615,7 +603,7 @@ pub async fn check_proxy_server_works() { let (_proxy_handle, proxy_status) = start_proxy_and_connect_lightclient(faucet, HashMap::new()); proxy_status.store(false, std::sync::atomic::Ordering::Relaxed); tokio::task::spawn(async move { - sleep(Duration::from_secs(5)).await; + tokio::time::sleep(Duration::from_secs(5)).await; println!("Wakening proxy!"); proxy_status.store(true, std::sync::atomic::Ordering::Relaxed); }); diff --git a/zingolib/src/testutils/chain_generics/conduct_chain.rs b/zingolib/src/testutils/chain_generics/conduct_chain.rs index e1e6c6cfc..240c2e5bb 100644 --- a/zingolib/src/testutils/chain_generics/conduct_chain.rs +++ b/zingolib/src/testutils/chain_generics/conduct_chain.rs @@ -61,11 +61,11 @@ pub trait ConductChain { /// builds a client and funds it in orchard and syncs it async fn fund_client_orchard(&mut self, _value: u64) -> LightClient { - let faucet = self.create_faucet().await; - let recipient = self.create_client().await; + let mut faucet = self.create_faucet().await; + let mut recipient = self.create_client().await; self.bump_chain().await; - faucet.do_sync(false).await.unwrap(); + faucet.sync_and_await(false).await.unwrap(); // FIXME: zingo2 // from_inputs::quick_send( @@ -81,7 +81,7 @@ pub trait ConductChain { self.bump_chain().await; - recipient.do_sync(false).await.unwrap(); + recipient.sync_and_await(false).await.unwrap(); recipient } diff --git a/zingolib/src/testutils/chain_generics/fixtures.rs b/zingolib/src/testutils/chain_generics/fixtures.rs index b693c0e9b..12e4cb71a 100644 --- a/zingolib/src/testutils/chain_generics/fixtures.rs +++ b/zingolib/src/testutils/chain_generics/fixtures.rs @@ -1,6 +1,5 @@ //! these functions are each meant to be 'test-in-a-box' //! simply plug in a mock server as a chain conductor and provide some values -use std::sync::Arc; use zcash_client_backend::PoolType; // use zcash_client_backend::PoolType::Shielded; @@ -10,7 +9,6 @@ use zcash_client_backend::ShieldedProtocol; // use zcash_client_backend::ShieldedProtocol::Sapling; // use zcash_primitives::transaction::fees::zip317::MARGINAL_FEE; -use crate::lightclient::LightClient; // use crate::wallet::data::summaries::SelfSendValueTransfer; // use crate::wallet::data::summaries::SentValueTransfer; // use crate::wallet::data::summaries::ValueTransferKind; @@ -18,7 +16,7 @@ use crate::lightclient::LightClient; use crate::testutils::chain_generics::conduct_chain::ConductChain; // use crate::testutils::chain_generics::with_assertions; use crate::testutils::fee_tables; -// use crate::testutils::lightclient::from_inputs; +use crate::testutils::lightclient::from_inputs; /// Fixture for testing various vt transactions pub async fn create_various_value_transfers() @@ -393,47 +391,43 @@ pub async fn shpool_to_pool_insufficient_error( } /// the simplest test that sends from a specific shielded pool to another specific pool. also known as simpool. -pub async fn to_pool_unfunded_error(pool: PoolType, _try_amount: u64) +pub async fn to_pool_unfunded_error(pool: PoolType, try_amount: u64) where CC: ConductChain, { let mut environment = CC::setup().await; - let secondary = environment.create_client().await; + let mut secondary = environment.create_client().await; let tertiary = environment.create_client().await; - let ref_secondary: Arc = Arc::new(secondary); - let _ref_tertiary: Arc = Arc::new(tertiary); - - ref_secondary.do_sync(false).await.unwrap(); - - let _expected_fee = fee_tables::one_to_one(None, pool, true); - - // FIXME: zingo2 - // assert_eq!( - // from_inputs::propose( - // &ref_secondary, - // vec![( - // ref_tertiary - // .wallet - // .lock() - // .await - // .get_first_address(pool) - // .unwrap() - // .as_str(), - // try_amount, - // None, - // )], - // ) - // .await - // .unwrap_err() - // .to_string(), - // format!( - // "Insufficient balance (have {}, need {} including fee)", - // 0, - // try_amount + expected_fee - // ) - // ); + secondary.sync_and_await(false).await.unwrap(); + + let expected_fee = fee_tables::one_to_one(None, pool, true); + + assert_eq!( + from_inputs::propose( + &secondary, + vec![( + tertiary + .wallet + .lock() + .await + .get_first_address(pool) + .unwrap() + .as_str(), + try_amount, + None, + )], + ) + .await + .unwrap_err() + .to_string(), + format!( + "Insufficient balance (have {}, need {} including fee)", + 0, + try_amount + expected_fee + ) + ); } /// the simplest test that sends from a specific shielded pool to another specific pool. also known as simpool. diff --git a/zingolib/src/testutils/chain_generics/with_assertions.rs b/zingolib/src/testutils/chain_generics/with_assertions.rs index df2988639..074f179d5 100644 --- a/zingolib/src/testutils/chain_generics/with_assertions.rs +++ b/zingolib/src/testutils/chain_generics/with_assertions.rs @@ -111,8 +111,8 @@ use zingo_status::confirmation_status::ConfirmationStatus; /// returns Ok(total_fee, total_received, total_change) pub async fn follow_proposal( environment: &mut CC, - sender: &LightClient, - recipients: Vec<&LightClient>, + sender: &mut LightClient, + mut recipients: Vec<&mut LightClient>, proposal: &Proposal, txids: NonEmpty, test_mempool: bool, @@ -160,7 +160,7 @@ where let option_recipient_mempool_outputs = if test_mempool { // mempool scan shows the same - sender.do_sync(false).await.unwrap(); + sender.sync_and_await(false).await.unwrap(); // let the mempool monitor get a chance // to listen @@ -198,8 +198,8 @@ where } let mut recipients_mempool_outputs: Vec> = vec![]; - for recipient in recipients.clone() { - recipient.do_sync(false).await.unwrap(); + for recipient in recipients.iter_mut() { + recipient.sync_and_await(false).await.unwrap(); // check that each record has the status, returning the output value let (recipient_mempool_outputs, recipient_mempool_statuses): ( @@ -236,7 +236,7 @@ where environment.bump_chain().await; // chain scan shows the same - sender.do_sync(false).await.unwrap(); + sender.sync_and_await(false).await.unwrap(); // check that each record has the expected fee and status, returning the fee and outputs let (sender_confirmed_fees, (sender_confirmed_outputs, sender_confirmed_statuses)): ( @@ -270,8 +270,8 @@ where } let mut recipients_confirmed_outputs = vec![]; - for recipient in recipients { - recipient.do_sync(false).await.unwrap(); + for recipient in recipients.iter_mut() { + recipient.sync_and_await(false).await.unwrap(); // check that each record has the status, returning the output value let (recipient_confirmed_outputs, recipient_confirmed_statuses): ( diff --git a/zingolib/src/testutils/interrupts.rs b/zingolib/src/testutils/interrupts.rs deleted file mode 100644 index d2c356502..000000000 --- a/zingolib/src/testutils/interrupts.rs +++ /dev/null @@ -1,12 +0,0 @@ -//! TODO: Add Mod Description Here! - -use crate::lightclient::LightClient; - -/// TODO: Add Doc Comment Here! -pub async fn sync_with_timeout_millis(lightclient: &LightClient, timeout: u64) -> Result<(), ()> { - tokio::select!( - biased; - _ = tokio::time::sleep(std::time::Duration::from_millis(timeout)) => Err(()), - _ = lightclient.do_sync(false) => Ok(()), - ) -} diff --git a/zingolib/src/testutils/macros.rs b/zingolib/src/testutils/macros.rs index 8efc92740..17966ea43 100644 --- a/zingolib/src/testutils/macros.rs +++ b/zingolib/src/testutils/macros.rs @@ -71,79 +71,3 @@ macro_rules! check_client_balances { ); }; } -/// Specific to two tests, validate outgoing notes before and after rescan -#[macro_export] -macro_rules! validate_outgoing_notes { - ($client:ident, $nom_txid:ident, $memo_txid:ident) => { - let wallet = $client.wallet.lock().await; - let pre_rescan_summaries = wallet.transaction_summaries().await; - let pre_rescan_no_memo_self_send_outgoing_sapling_notes = wallet - .wallet_transactions - .get($nom_txid) - .unwrap() - .outgoing_sapling_notes() - .clone(); - let pre_rescan_with_memo_self_send_outgoing_sapling_notes = wallet - .wallet_transactions - .get($memo_txid) - .unwrap() - .outgoing_sapling_notes() - .clone(); - let pre_rescan_no_memo_self_send_outgoing_orchard_notes = wallet - .wallet_transactions - .get($nom_txid) - .unwrap() - .outgoing_orchard_notes() - .clone(); - let pre_rescan_with_memo_self_send_outgoing_orchard_notes = wallet - .wallet_transactions - .get($memo_txid) - .unwrap() - .outgoing_orchard_notes() - .clone(); - $client.do_rescan().await.unwrap(); - let post_rescan_no_memo_self_send_outgoing_sapling_notes = wallet - .wallet_transactions - .get($nom_txid) - .unwrap() - .outgoing_sapling_notes() - .clone(); - let post_rescan_with_memo_self_send_outgoing_sapling_notes = wallet - .wallet_transactions - .get($memo_txid) - .unwrap() - .outgoing_sapling_notes() - .clone(); - let post_rescan_no_memo_self_send_outgoing_orchard_notes = wallet - .wallet_transactions - .get($nom_txid) - .unwrap() - .outgoing_orchard_notes() - .clone(); - let post_rescan_with_memo_self_send_outgoing_orchard_notes = wallet - .wallet_transactions - .get($memo_txid) - .unwrap() - .outgoing_orchard_notes() - .clone(); - assert_eq!( - pre_rescan_no_memo_self_send_outgoing_sapling_notes, - post_rescan_no_memo_self_send_outgoing_sapling_notes - ); - assert_eq!( - pre_rescan_with_memo_self_send_outgoing_sapling_notes, - post_rescan_with_memo_self_send_outgoing_sapling_notes - ); - assert_eq!( - pre_rescan_no_memo_self_send_outgoing_orchard_notes, - post_rescan_no_memo_self_send_outgoing_orchard_notes - ); - assert_eq!( - pre_rescan_with_memo_self_send_outgoing_orchard_notes, - post_rescan_with_memo_self_send_outgoing_orchard_notes - ); - for summary in pre_rescan_summaries.iter() { - zingolib::testutils::assert_transaction_summary_exists(&$client, summary).await; - } - }; -} diff --git a/zingolib/src/testutils/scenarios.rs b/zingolib/src/testutils/scenarios.rs index 345c87855..e0b425121 100644 --- a/zingolib/src/testutils/scenarios.rs +++ b/zingolib/src/testutils/scenarios.rs @@ -395,8 +395,8 @@ pub async fn faucet( lightwalletd_feature, ) .await; - let faucet = sb.client_builder.build_faucet(false, regtest_network).await; - faucet.do_sync(false).await.unwrap(); + let mut faucet = sb.client_builder.build_faucet(false, regtest_network).await; + faucet.sync_and_await(false).await.unwrap(); ( sb.regtest_manager, sb.child_process_handler.unwrap(), @@ -434,8 +434,8 @@ pub async fn faucet_recipient( lightwalletd_feature, ) .await; - let faucet = sb.client_builder.build_faucet(false, regtest_network).await; - faucet.do_sync(false).await.unwrap(); + let mut faucet = sb.client_builder.build_faucet(false, regtest_network).await; + faucet.sync_and_await(false).await.unwrap(); let recipient = sb .client_builder @@ -487,9 +487,9 @@ pub async fn faucet_funded_recipient( Option, Option, ) { - let (regtest_manager, child_process_handler, faucet, recipient) = + let (regtest_manager, child_process_handler, mut faucet, mut recipient) = faucet_recipient(mine_to_pool, regtest_network, lightwalletd_feature).await; - increase_height_and_wait_for_client(®test_manager, &faucet, 1) + increase_height_and_wait_for_client(®test_manager, &mut faucet, 1) .await .unwrap(); @@ -539,10 +539,10 @@ pub async fn faucet_funded_recipient( } else { None }; - increase_height_and_wait_for_client(®test_manager, &recipient, 1) + increase_height_and_wait_for_client(®test_manager, &mut recipient, 1) .await .unwrap(); - faucet.do_sync(false).await.unwrap(); + faucet.sync_and_await(false).await.unwrap(); ( regtest_manager, child_process_handler, @@ -650,7 +650,7 @@ pub async fn funded_orchard_mobileclient(_value: u64) -> (RegtestManager, ChildP true, ) .await; - let faucet = scenario_builder + let mut faucet = scenario_builder .client_builder .build_faucet(false, regtest_network) .await; @@ -658,7 +658,7 @@ pub async fn funded_orchard_mobileclient(_value: u64) -> (RegtestManager, ChildP .client_builder .build_client(HOSPITAL_MUSEUM_SEED.to_string(), 0, false, regtest_network) .await; - faucet.do_sync(false).await.unwrap(); + faucet.sync_and_await(false).await.unwrap(); // FIXME: zingo2 // super::lightclient::from_inputs::quick_send( // &faucet, @@ -689,15 +689,15 @@ pub async fn funded_orchard_with_3_txs_mobileclient( true, ) .await; - let faucet = scenario_builder + let mut faucet = scenario_builder .client_builder .build_faucet(false, regtest_network) .await; - let recipient = scenario_builder + let mut recipient = scenario_builder .client_builder .build_client(HOSPITAL_MUSEUM_SEED.to_string(), 0, false, regtest_network) .await; - increase_height_and_wait_for_client(&scenario_builder.regtest_manager, &faucet, 1) + increase_height_and_wait_for_client(&scenario_builder.regtest_manager, &mut faucet, 1) .await .unwrap(); // received from a faucet @@ -708,7 +708,7 @@ pub async fn funded_orchard_with_3_txs_mobileclient( // ) // .await // .unwrap(); - increase_height_and_wait_for_client(&scenario_builder.regtest_manager, &recipient, 1) + increase_height_and_wait_for_client(&scenario_builder.regtest_manager, &mut recipient, 1) .await .unwrap(); // send to a faucet @@ -723,7 +723,7 @@ pub async fn funded_orchard_with_3_txs_mobileclient( // ) // .await // .unwrap(); - increase_height_and_wait_for_client(&scenario_builder.regtest_manager, &recipient, 1) + increase_height_and_wait_for_client(&scenario_builder.regtest_manager, &mut recipient, 1) .await .unwrap(); // send to self sapling @@ -759,15 +759,15 @@ pub async fn funded_transparent_mobileclient(_value: u64) -> (RegtestManager, Ch true, ) .await; - let faucet = scenario_builder + let mut faucet = scenario_builder .client_builder .build_faucet(false, regtest_network) .await; - let recipient = scenario_builder + let mut recipient = scenario_builder .client_builder .build_client(HOSPITAL_MUSEUM_SEED.to_string(), 0, false, regtest_network) .await; - increase_height_and_wait_for_client(&scenario_builder.regtest_manager, &faucet, 1) + increase_height_and_wait_for_client(&scenario_builder.regtest_manager, &mut faucet, 1) .await .unwrap(); @@ -783,7 +783,7 @@ pub async fn funded_transparent_mobileclient(_value: u64) -> (RegtestManager, Ch // ) // .await // .unwrap(); - increase_height_and_wait_for_client(&scenario_builder.regtest_manager, &recipient, 1) + increase_height_and_wait_for_client(&scenario_builder.regtest_manager, &mut recipient, 1) .await .unwrap(); @@ -811,15 +811,15 @@ pub async fn funded_orchard_sapling_transparent_shielded_mobileclient( true, ) .await; - let faucet = scenario_builder + let mut faucet = scenario_builder .client_builder .build_faucet(false, regtest_network) .await; - let recipient = scenario_builder + let mut recipient = scenario_builder .client_builder .build_client(HOSPITAL_MUSEUM_SEED.to_string(), 0, false, regtest_network) .await; - increase_height_and_wait_for_client(&scenario_builder.regtest_manager, &faucet, 1) + increase_height_and_wait_for_client(&scenario_builder.regtest_manager, &mut faucet, 1) .await .unwrap(); // received from a faucet to orchard @@ -834,7 +834,7 @@ pub async fn funded_orchard_sapling_transparent_shielded_mobileclient( // ) // .await // .unwrap(); - increase_height_and_wait_for_client(&scenario_builder.regtest_manager, &faucet, 1) + increase_height_and_wait_for_client(&scenario_builder.regtest_manager, &mut faucet, 1) .await .unwrap(); // received from a faucet to sapling @@ -849,7 +849,7 @@ pub async fn funded_orchard_sapling_transparent_shielded_mobileclient( // ) // .await // .unwrap(); - increase_height_and_wait_for_client(&scenario_builder.regtest_manager, &faucet, 1) + increase_height_and_wait_for_client(&scenario_builder.regtest_manager, &mut faucet, 1) .await .unwrap(); // received from a faucet to transparent @@ -864,7 +864,7 @@ pub async fn funded_orchard_sapling_transparent_shielded_mobileclient( // ) // .await // .unwrap(); - increase_height_and_wait_for_client(&scenario_builder.regtest_manager, &recipient, 1) + increase_height_and_wait_for_client(&scenario_builder.regtest_manager, &mut recipient, 1) .await .unwrap(); // send to a faucet @@ -879,7 +879,7 @@ pub async fn funded_orchard_sapling_transparent_shielded_mobileclient( // ) // .await // .unwrap(); - increase_height_and_wait_for_client(&scenario_builder.regtest_manager, &recipient, 1) + increase_height_and_wait_for_client(&scenario_builder.regtest_manager, &mut recipient, 1) .await .unwrap(); // send to self orchard @@ -894,7 +894,7 @@ pub async fn funded_orchard_sapling_transparent_shielded_mobileclient( // ) // .await // .unwrap(); - increase_height_and_wait_for_client(&scenario_builder.regtest_manager, &recipient, 1) + increase_height_and_wait_for_client(&scenario_builder.regtest_manager, &mut recipient, 1) .await .unwrap(); // send to self sapling @@ -909,7 +909,7 @@ pub async fn funded_orchard_sapling_transparent_shielded_mobileclient( // ) // .await // .unwrap(); - increase_height_and_wait_for_client(&scenario_builder.regtest_manager, &recipient, 1) + increase_height_and_wait_for_client(&scenario_builder.regtest_manager, &mut recipient, 1) .await .unwrap(); // send to self transparent @@ -924,13 +924,13 @@ pub async fn funded_orchard_sapling_transparent_shielded_mobileclient( // ) // .await // .unwrap(); - increase_height_and_wait_for_client(&scenario_builder.regtest_manager, &recipient, 1) + increase_height_and_wait_for_client(&scenario_builder.regtest_manager, &mut recipient, 1) .await .unwrap(); // shield transparent // FIXME: zingo2 // recipient.quick_shield().await.unwrap(); - increase_height_and_wait_for_client(&scenario_builder.regtest_manager, &recipient, 1) + increase_height_and_wait_for_client(&scenario_builder.regtest_manager, &mut recipient, 1) .await .unwrap(); // end @@ -967,8 +967,8 @@ pub mod chainload { let regtest_network = crate::config::RegtestNetwork::all_upgrades_active(); let mut sb = setup::ScenarioBuilder::new_load_1153_saplingcb_regtest_chain(®test_network).await; - let faucet = sb.client_builder.build_faucet(false, regtest_network).await; - faucet.do_sync(false).await.unwrap(); + let mut faucet = sb.client_builder.build_faucet(false, regtest_network).await; + faucet.sync_and_await(false).await.unwrap(); let recipient = sb .client_builder .build_client(HOSPITAL_MUSEUM_SEED.to_string(), 0, false, regtest_network) diff --git a/zingolib/src/wallet/disk/testing/tests.rs b/zingolib/src/wallet/disk/testing/tests.rs index 8ed153236..869f7db79 100644 --- a/zingolib/src/wallet/disk/testing/tests.rs +++ b/zingolib/src/wallet/disk/testing/tests.rs @@ -178,23 +178,25 @@ async fn verify_example_wallet_mainnet_hhcclaltpcckcsslpcnetblr_latest() { } async fn loaded_wallet_assert( - lightclient: LightClient, + mut lightclient: LightClient, expected_seed_phrase: String, expected_balance: u64, expected_num_addresses: usize, ) { - let wallet = lightclient.wallet.lock().await; - assert_wallet_capability_matches_seed(&wallet, expected_seed_phrase).await; + { + let wallet = lightclient.wallet.lock().await; + assert_wallet_capability_matches_seed(&wallet, expected_seed_phrase).await; - assert_eq!(wallet.unified_addresses.len(), expected_num_addresses); - for addr in wallet.unified_addresses.iter() { - assert!(addr.orchard().is_some()); - assert!(addr.sapling().is_some()); - assert!(addr.transparent().is_some()); - } + assert_eq!(wallet.unified_addresses.len(), expected_num_addresses); + for addr in wallet.unified_addresses.iter() { + assert!(addr.orchard().is_some()); + assert!(addr.sapling().is_some()); + assert!(addr.transparent().is_some()); + } - let balance = lightclient.do_balance().await; - assert_eq!(balance.orchard_balance, Some(expected_balance)); + let balance = lightclient.do_balance().await; + assert_eq!(balance.orchard_balance, Some(expected_balance)); + } if expected_balance > 0 { // FIXME: zingo2 // crate::testutils::lightclient::from_inputs::quick_send( @@ -207,7 +209,7 @@ async fn loaded_wallet_assert( // ) // .await // .unwrap(); - lightclient.do_sync(true).await.unwrap(); + lightclient.sync_and_await(true).await.unwrap(); // FIXME: zingo2 // crate::testutils::lightclient::from_inputs::quick_send( // &lightclient, From f9b688abc76891bb6612117f75c425ffa6812e4b Mon Sep 17 00:00:00 2001 From: Oscar Pepper Date: Sat, 22 Feb 2025 06:41:12 +0000 Subject: [PATCH 08/26] implemented new sync command except sub-command --- pepper-sync/src/wallet.rs | 16 +++ zingocli/src/lib.rs | 20 ++-- zingolib/src/commands.rs | 169 +++++++++++++++---------------- zingolib/src/lightclient/sync.rs | 24 +++-- 4 files changed, 122 insertions(+), 107 deletions(-) diff --git a/pepper-sync/src/wallet.rs b/pepper-sync/src/wallet.rs index 4fe6e4556..b2907f1b0 100644 --- a/pepper-sync/src/wallet.rs +++ b/pepper-sync/src/wallet.rs @@ -7,6 +7,10 @@ use std::{ collections::{BTreeMap, BTreeSet}, fmt::Debug, ops::Range, + sync::{ + atomic::{self, AtomicU8}, + Arc, + }, }; use incrementalmerkletree::Position; @@ -215,6 +219,18 @@ impl SyncMode { _ => None, } } + + /// Creates [`crate::wallet::SyncMode`] from an atomic u8. + /// + /// # Panic + /// + /// Panics if `atomic_sync_mode` corresponds to an invalid enum variant. + /// It is the consumers responsibility to ensure the library restricts the user API to only set valid values via + /// [`crate::wallet::SyncMode`]. + pub fn from_atomic_u8(atomic_sync_mode: Arc) -> SyncMode { + SyncMode::from_u8(atomic_sync_mode.load(atomic::Ordering::Acquire)) + .expect("this library does not allow setting of non-valid sync mode variants") + } } /// Initial and final tree sizes. diff --git a/zingocli/src/lib.rs b/zingocli/src/lib.rs index 3e638218c..7cc5a1454 100644 --- a/zingocli/src/lib.rs +++ b/zingocli/src/lib.rs @@ -6,7 +6,6 @@ use std::path::PathBuf; use std::sync::mpsc::{channel, Receiver, Sender}; -use std::sync::Arc; use log::{error, info}; @@ -242,7 +241,7 @@ fn start_interactive( /// TODO: Add Doc Comment Here! pub fn command_loop( - lightclient: Arc, + mut lightclient: LightClient, ) -> (Sender<(String, Vec)>, Receiver) { let (command_transmitter, command_receiver) = channel::<(String, Vec)>(); let (resp_transmitter, resp_receiver) = channel::(); @@ -251,7 +250,7 @@ pub fn command_loop( while let Ok((cmd, args)) = command_receiver.recv() { let args: Vec<_> = args.iter().map(|s| s.as_ref()).collect(); - let cmd_response = commands::do_user_command(&cmd, &args[..], lightclient.as_ref()); + let cmd_response = commands::do_user_command(&cmd, &args[..], &mut lightclient); resp_transmitter.send(cmd_response).unwrap(); if cmd == "quit" { @@ -439,16 +438,16 @@ pub fn startup( .unwrap(); regtest_config_check(&filled_template.regtest_manager, &config.chain); - let lightclient = match filled_template.from.clone() { - Some(phrase) => Arc::new(LightClient::create_from_wallet_base( + let mut lightclient = match filled_template.from.clone() { + Some(phrase) => LightClient::create_from_wallet_base( WalletBase::from_string(phrase), &config, filled_template.birthday, false, - )?), + )?, None => { if config.wallet_path_exists() { - Arc::new(LightClient::read_wallet_from_disk(&config)?) + LightClient::read_wallet_from_disk(&config)? } else { println!("Creating a new wallet"); // Call the lightwalletd server to get the current block-height @@ -456,10 +455,7 @@ pub fn startup( let server_uri = config.get_lightwalletd_uri(); let block_height = zingolib::get_latest_block_height(server_uri); // Create a wallet with height - 100, to protect against reorgs - Arc::new(LightClient::new( - &config, - block_height?.saturating_sub(100), - )?) + LightClient::new(&config, block_height?.saturating_sub(100))? } } }; @@ -478,7 +474,7 @@ pub fn startup( // At startup, run a sync. if filled_template.sync { - let update = commands::do_user_command("sync", &[], lightclient.as_ref()); + let update = commands::do_user_command("sync", &[], &mut lightclient); println!("{}", update); } diff --git a/zingolib/src/commands.rs b/zingolib/src/commands.rs index 5944fd9d2..1d0b48c97 100644 --- a/zingolib/src/commands.rs +++ b/zingolib/src/commands.rs @@ -7,7 +7,7 @@ use crate::{lightclient::LightClient, wallet}; use indoc::indoc; use json::object; use lazy_static::lazy_static; -use pepper_sync::wallet::{OrchardNote, SaplingNote}; +use pepper_sync::wallet::{OrchardNote, SaplingNote, SyncMode}; use std::collections::HashMap; use std::convert::TryInto; use std::str::FromStr; @@ -36,7 +36,7 @@ pub trait Command { /// in zingocli, this string is printed to console /// consumers occasionally make assumptions about this /// e. expect it to be a json object - fn exec(&self, _args: &[&str], lightclient: &LightClient) -> String; + fn exec(&self, _args: &[&str], lightclient: &mut LightClient) -> String; } /// TODO: Add Doc Comment Here! @@ -57,7 +57,7 @@ impl Command for GetVersionCommand { "Get version of build code" } - fn exec(&self, _args: &[&str], _lightclient: &LightClient) -> String { + fn exec(&self, _args: &[&str], _lightclient: &mut LightClient) -> String { crate::git_description().to_string() } } @@ -79,7 +79,7 @@ impl Command for ChangeServerCommand { "Change lightwalletd server" } - fn exec(&self, args: &[&str], lightclient: &LightClient) -> String { + fn exec(&self, args: &[&str], lightclient: &mut LightClient) -> String { match args.len() { 0 => { lightclient.set_server(http::Uri::default()); @@ -121,7 +121,7 @@ impl Command for GetBirthdayCommand { "Get wallet birthday." } - fn exec(&self, _args: &[&str], lightclient: &LightClient) -> String { + fn exec(&self, _args: &[&str], lightclient: &mut LightClient) -> String { RT.block_on(async move { lightclient.wallet.lock().await.birthday.to_string() }) } } @@ -140,7 +140,7 @@ impl Command for WalletKindCommand { "Displays the kind of wallet currently loaded" } - fn exec(&self, _args: &[&str], lightclient: &LightClient) -> String { + fn exec(&self, _args: &[&str], lightclient: &mut LightClient) -> String { RT.block_on(async move { if lightclient.do_seed_phrase().await.is_ok() { object! {"kind" => "Loaded from seed phrase", @@ -195,7 +195,7 @@ impl Command for ParseAddressCommand { "Parse an address" } - fn exec(&self, args: &[&str], _lightclient: &LightClient) -> String { + fn exec(&self, args: &[&str], _lightclient: &mut LightClient) -> String { if args.len() > 1 || args.is_empty() { return self.help().to_string(); } @@ -297,7 +297,7 @@ impl Command for ParseViewKeyCommand { "Parse a view_key." } - fn exec(&self, args: &[&str], _lightclient: &LightClient) -> String { + fn exec(&self, args: &[&str], _lightclient: &mut LightClient) -> String { match args.len() { 1 => { json::stringify_pretty( @@ -350,11 +350,20 @@ struct SyncCommand {} impl Command for SyncCommand { fn help(&self) -> &'static str { indoc! {r#" - Sync the wallet with the blockchain. + Launches a task for syncing the wallet to the latest state of the block chain. + + Sub-commands: + `run` starts or resumes sync. + `pause` pauses scanning until sync is resumed. + `status` returns a report of the wallet's current sync status. + `result` reports whether the sync task has finished and if complete, returns a sync result. If sync failed + will return the error instead. Usage: sync run sync pause + sync status + sync result "#} } @@ -363,49 +372,40 @@ impl Command for SyncCommand { "Sync the wallet with the blockchain" } - fn exec(&self, args: &[&str], lightclient: &LightClient) -> String { + fn exec(&self, args: &[&str], lightclient: &mut LightClient) -> String { if args.len() != 1 { return self.help().to_string(); } - // RT.block_on(async move { - // // TODO: improve zingo CLI sync status updates - // match lightclient.sync_and_await(true).await { - // Ok(j) => j.to_json().pretty(2), - // Err(e) => e, - // } - // }) - todo!() - } -} - -// FIXME: add to sync command -struct SyncStatusCommand {} -impl Command for SyncStatusCommand { - fn help(&self) -> &'static str { - indoc! {r#" - Get the sync status of the wallet - - Usage: - syncstatus - - "#} - } - - fn short_help(&self) -> &'static str { - "Get the sync status of the wallet" - } - - fn exec(&self, args: &[&str], lightclient: &LightClient) -> String { - if !args.is_empty() { - return self.help().to_string(); + match args[0] { + "run" => { + if lightclient.sync_mode() == SyncMode::Paused { + lightclient.resume_sync(); + "Resuming sync...".to_string() + } else { + if let Err(e) = RT.block_on(async move { lightclient.sync(true).await }) { + return format!("Error: {}", e.to_string()); + } + "Sync running...".to_string() + } + } + "pause" => { + lightclient.pause_sync(); + "Pausing sync...".to_string() + } + "status" => RT + .block_on(async move { + json::JsonValue::from( + pepper_sync::sync_status(&*lightclient.wallet.lock().await).await, + ) + }) + .pretty(2), + "result" => RT.block_on(async move { + // TODO: check sync handle and report + "".to_string() + }), + _ => self.help().to_string(), } - - // RT.block_on(async move { - // json::JsonValue::from(pepper_sync::sync_status(lightclient.wallet.clone()).await) - // .pretty(2) - // }) - todo!() } } @@ -423,7 +423,7 @@ impl Command for SendProgressCommand { "Get the progress of any send transactions that are currently computing" } - fn exec(&self, _args: &[&str], lightclient: &LightClient) -> String { + fn exec(&self, _args: &[&str], lightclient: &mut LightClient) -> String { RT.block_on(async move { match lightclient.do_send_progress().await { Ok(p) => p.to_json().pretty(2), @@ -450,7 +450,7 @@ impl Command for RescanCommand { "Rescan the wallet, downloading and scanning all blocks and transactions" } - fn exec(&self, _args: &[&str], _lightclient: &LightClient) -> String { + fn exec(&self, _args: &[&str], _lightclient: &mut LightClient) -> String { todo!() } } @@ -471,7 +471,7 @@ impl Command for ClearCommand { "Clear the wallet state, rolling back the wallet to an empty state." } - fn exec(&self, _args: &[&str], lightclient: &LightClient) -> String { + fn exec(&self, _args: &[&str], lightclient: &mut LightClient) -> String { RT.block_on(async move { lightclient.wallet.lock().await.clear_all(); @@ -501,7 +501,7 @@ impl Command for HelpCommand { "Lists all available commands" } - fn exec(&self, args: &[&str], _: &LightClient) -> String { + fn exec(&self, args: &[&str], _: &mut LightClient) -> String { let mut responses = vec![]; // Print a list of all commands @@ -563,7 +563,7 @@ impl Command for InfoCommand { "Get the lightwalletd server's info" } - fn exec(&self, _args: &[&str], lightclient: &LightClient) -> String { + fn exec(&self, _args: &[&str], lightclient: &mut LightClient) -> String { RT.block_on(async move { lightclient.do_info().await }) } } @@ -584,7 +584,7 @@ impl Command for UpdateCurrentPriceCommand { "Get the latest ZEC price in the wallet's currency (USD)" } - fn exec(&self, _args: &[&str], lightclient: &LightClient) -> String { + fn exec(&self, _args: &[&str], lightclient: &mut LightClient) -> String { RT.block_on(async move { lightclient.update_current_price().await }) } } @@ -604,7 +604,7 @@ impl Command for BalanceCommand { "Return the current ZEC balance in the wallet" } - fn exec(&self, _args: &[&str], lightclient: &LightClient) -> String { + fn exec(&self, _args: &[&str], lightclient: &mut LightClient) -> String { RT.block_on(async move { serde_json::to_string_pretty(&lightclient.do_balance().await).unwrap() }) @@ -627,7 +627,7 @@ impl Command for PrintBalanceCommand { "Show the current ZEC balance in the wallet" } - fn exec(&self, _args: &[&str], lightclient: &LightClient) -> String { + fn exec(&self, _args: &[&str], lightclient: &mut LightClient) -> String { RT.block_on(async move { lightclient.do_balance().await.to_string() }) } } @@ -656,7 +656,7 @@ impl Command for SpendableBalanceCommand { "Display the wallet's spendable balance." } - fn exec(&self, args: &[&str], lightclient: &LightClient) -> String { + fn exec(&self, args: &[&str], lightclient: &mut LightClient) -> String { let (address, zennies_for_zingo) = match utils::parse_spendable_balance_args(args) { Ok(address_and_zennies) => address_and_zennies, Err(e) => { @@ -700,7 +700,7 @@ impl Command for AddressCommand { "List all addresses in the wallet" } - fn exec(&self, args: &[&str], lightclient: &LightClient) -> String { + fn exec(&self, args: &[&str], lightclient: &mut LightClient) -> String { use crate::lightclient::describe::UAReceivers; match args.len() { 0 => RT.block_on( @@ -744,7 +744,7 @@ impl Command for ExportUfvkCommand { "Export full viewing key for wallet addresses" } - fn exec(&self, _args: &[&str], lightclient: &LightClient) -> String { + fn exec(&self, _args: &[&str], lightclient: &mut LightClient) -> String { RT.block_on(async move { let ufvk: UnifiedFullViewingKey = match (&lightclient.wallet.lock().await.unified_key_store).try_into() { @@ -781,7 +781,7 @@ impl Command for EncryptMessageCommand { "Encrypt a memo to be sent to a z-address offline" } - fn exec(&self, args: &[&str], lightclient: &LightClient) -> String { + fn exec(&self, args: &[&str], lightclient: &mut LightClient) -> String { if args.is_empty() || args.len() > 3 { return self.help().to_string(); } @@ -852,7 +852,7 @@ impl Command for DecryptMessageCommand { "Attempt to decrypt a message with all the view keys in the wallet." } - fn exec(&self, args: &[&str], lightclient: &LightClient) -> String { + fn exec(&self, args: &[&str], lightclient: &mut LightClient) -> String { if args.len() != 1 { return self.help().to_string(); } @@ -889,7 +889,7 @@ impl Command for SendCommand { "Propose a transfer of ZEC to the given address(es) and display a proposal for confirmation." } - fn exec(&self, args: &[&str], lightclient: &LightClient) -> String { + fn exec(&self, args: &[&str], lightclient: &mut LightClient) -> String { let receivers = match utils::parse_send_args(args) { Ok(receivers) => receivers, Err(e) => { @@ -953,7 +953,7 @@ impl Command for SendAllCommand { "Propose to transfer all ZEC from shielded pools to a given address and display a proposal for confirmation." } - fn exec(&self, args: &[&str], lightclient: &LightClient) -> String { + fn exec(&self, args: &[&str], lightclient: &mut LightClient) -> String { let (address, zennies_for_zingo, memo) = match utils::parse_send_all_args(args) { Ok(parse_results) => parse_results, Err(e) => { @@ -1013,7 +1013,7 @@ impl Command for QuickSendCommand { "Send ZEC to the given address(es). Combines `send` and `confirm` into a single command." } - fn exec(&self, args: &[&str], lightclient: &LightClient) -> String { + fn exec(&self, args: &[&str], lightclient: &mut LightClient) -> String { let receivers = match utils::parse_send_args(args) { Ok(receivers) => receivers, Err(e) => { @@ -1067,7 +1067,7 @@ impl Command for ShieldCommand { "Propose a shield of transparent funds to the orchard pool and display a proposal for confirmation.." } - fn exec(&self, args: &[&str], lightclient: &LightClient) -> String { + fn exec(&self, args: &[&str], lightclient: &mut LightClient) -> String { if !args.is_empty() { return format!( "Error: {}\nTry 'help shield' for correct usage and examples.", @@ -1123,7 +1123,7 @@ impl Command for QuickShieldCommand { "Shield transparent funds to the orchard pool. Combines `shield` and `confirm` into a single command." } - fn exec(&self, args: &[&str], lightclient: &LightClient) -> String { + fn exec(&self, args: &[&str], lightclient: &mut LightClient) -> String { if !args.is_empty() { return format!( "Error: {}\nTry 'help shield' for correct usage and examples.", @@ -1168,7 +1168,7 @@ impl Command for ConfirmCommand { "Confirms the latest proposal, completing and broadcasting the transaction(s)." } - fn exec(&self, args: &[&str], lightclient: &LightClient) -> String { + fn exec(&self, args: &[&str], lightclient: &mut LightClient) -> String { if !args.is_empty() { return format!( "Error: {}\nTry 'help confirm' for correct usage and examples.", @@ -1211,7 +1211,7 @@ impl Command for DeleteCommand { "Delete wallet file from disk" } - fn exec(&self, _args: &[&str], lightclient: &LightClient) -> String { + fn exec(&self, _args: &[&str], lightclient: &mut LightClient) -> String { RT.block_on(async move { match lightclient.do_delete().await { Ok(_) => { @@ -1248,7 +1248,7 @@ impl Command for SeedCommand { "Display the seed phrase" } - fn exec(&self, _args: &[&str], lightclient: &LightClient) -> String { + fn exec(&self, _args: &[&str], lightclient: &mut LightClient) -> String { RT.block_on(async move { match lightclient.do_seed_phrase().await { Ok(m) => serde_json::to_string_pretty(&m).unwrap(), @@ -1274,7 +1274,7 @@ impl Command for ValueTransfersCommand { "List all value transfers for this wallet." } - fn exec(&self, args: &[&str], lightclient: &LightClient) -> String { + fn exec(&self, args: &[&str], lightclient: &mut LightClient) -> String { if args.len() > 1 { return "Error: invalid arguments\nTry 'help valuetransfers' for correct usage and examples" .to_string(); @@ -1311,7 +1311,7 @@ impl Command for MessagesFilterCommand { "List memos for this wallet." } - fn exec(&self, args: &[&str], lightclient: &LightClient) -> String { + fn exec(&self, args: &[&str], lightclient: &mut LightClient) -> String { if args.len() > 1 { return "Error: invalid arguments\nTry 'help messages' for correct usage and examples" .to_string(); @@ -1340,7 +1340,7 @@ impl Command for TransactionsCommand { "Provides a list of transaction summaries related to this wallet in order of blockheight." } - fn exec(&self, args: &[&str], lightclient: &LightClient) -> String { + fn exec(&self, args: &[&str], lightclient: &mut LightClient) -> String { if !args.is_empty() { return "Error: invalid arguments\nTry 'help transactions' for correct usage and examples" .to_string(); @@ -1363,7 +1363,7 @@ impl Command for MemoBytesToAddressCommand { "Show by address memo_bytes transfers for this seed." } - fn exec(&self, args: &[&str], lightclient: &LightClient) -> String { + fn exec(&self, args: &[&str], lightclient: &mut LightClient) -> String { if args.len() > 1 { return format!("didn't understand arguments\n{}", self.help()); } @@ -1388,7 +1388,7 @@ impl Command for ValueToAddressCommand { "Show by address value transfers for this seed." } - fn exec(&self, args: &[&str], lightclient: &LightClient) -> String { + fn exec(&self, args: &[&str], lightclient: &mut LightClient) -> String { if args.len() > 1 { return format!("didn't understand arguments\n{}", self.help()); } @@ -1413,7 +1413,7 @@ impl Command for SendsToAddressCommand { "Show by address number of sends for this seed." } - fn exec(&self, args: &[&str], lightclient: &LightClient) -> String { + fn exec(&self, args: &[&str], lightclient: &mut LightClient) -> String { if args.len() > 1 { return format!("didn't understand arguments\n{}", self.help()); } @@ -1442,7 +1442,7 @@ impl Command for SendsToAddressCommand { // "Set a wallet option" // } -// fn exec(&self, args: &[&str], lightclient: &LightClient) -> String { +// fn exec(&self, args: &[&str], lightclient: &mut LightClient) -> String { // if args.len() != 1 { // return format!("Error: Need exactly 1 argument\n\n{}", self.help()); // } @@ -1526,7 +1526,7 @@ impl Command for SendsToAddressCommand { // "Get a wallet option" // } -// fn exec(&self, args: &[&str], lightclient: &LightClient) -> String { +// fn exec(&self, args: &[&str], lightclient: &mut LightClient) -> String { // if args.len() != 1 { // return format!("Error: Need exactly 1 argument\n\n{}", self.help()); // } @@ -1583,7 +1583,7 @@ impl Command for HeightCommand { "Get the latest block height that the wallet is at" } - fn exec(&self, _args: &[&str], lightclient: &LightClient) -> String { + fn exec(&self, _args: &[&str], lightclient: &mut LightClient) -> String { RT.block_on(async move { object! { "height" => lightclient.do_wallet_last_scanned_height().await}.pretty(2) }) @@ -1607,7 +1607,7 @@ impl Command for DefaultFeeCommand { "Returns the default fee in zats for outgoing transactions" } - fn exec(&self, args: &[&str], _lightclient: &LightClient) -> String { + fn exec(&self, args: &[&str], _lightclient: &mut LightClient) -> String { if args.len() > 1 { return format!("Was expecting at most 1 argument\n{}", self.help()); } @@ -1637,7 +1637,7 @@ impl Command for NewAddressCommand { "Create a new address in this wallet" } - fn exec(&self, args: &[&str], lightclient: &LightClient) -> String { + fn exec(&self, args: &[&str], lightclient: &mut LightClient) -> String { if args.len() != 1 { return format!("No address type specified\n{}", self.help()); } @@ -1667,7 +1667,7 @@ impl Command for NotesCommand { "Show all notes (shielded outputs) in this wallet" } - fn exec(&self, args: &[&str], lightclient: &LightClient) -> String { + fn exec(&self, args: &[&str], lightclient: &mut LightClient) -> String { // Parse the args. if args.len() > 1 { return self.short_help().to_string(); @@ -1715,7 +1715,7 @@ impl Command for CoinsCommand { "Show all coins (transparent outputs) in this wallet" } - fn exec(&self, args: &[&str], lightclient: &LightClient) -> String { + fn exec(&self, args: &[&str], lightclient: &mut LightClient) -> String { // Parse the args. if args.len() > 1 { return self.short_help().to_string(); @@ -1760,7 +1760,7 @@ impl Command for QuitCommand { "Quit the lightwallet, saving state to disk" } - fn exec(&self, _args: &[&str], lightclient: &LightClient) -> String { + fn exec(&self, _args: &[&str], lightclient: &mut LightClient) -> String { // before shutting down, shut down all child processes.. // ...but only if the network being used is regtest. let o = RT.block_on(async move { lightclient.do_info().await }); @@ -1816,7 +1816,7 @@ impl Command for DeprecatedNoCommand { "Deprecated command." } - fn exec(&self, _args: &[&str], _lightclient: &LightClient) -> String { + fn exec(&self, _args: &[&str], _lightclient: &mut LightClient) -> String { ".deprecated.".to_string() } } @@ -1828,7 +1828,6 @@ pub fn get_commands() -> HashMap<&'static str, Box> { let mut entries: Vec<(&'static str, Box)> = vec![ (("version"), Box::new(GetVersionCommand {})), ("sync", Box::new(SyncCommand {})), - ("syncstatus", Box::new(SyncStatusCommand {})), ("encryptmessage", Box::new(EncryptMessageCommand {})), ("decryptmessage", Box::new(DecryptMessageCommand {})), ("parse_address", Box::new(ParseAddressCommand {})), @@ -1880,7 +1879,7 @@ pub fn get_commands() -> HashMap<&'static str, Box> { } /// TODO: Add Doc Comment Here! -pub fn do_user_command(cmd: &str, args: &[&str], lightclient: &LightClient) -> String { +pub fn do_user_command(cmd: &str, args: &[&str], lightclient: &mut LightClient) -> String { match get_commands().get(cmd.to_ascii_lowercase().as_str()) { Some(cmd) => cmd.exec(args, lightclient), None => format!( diff --git a/zingolib/src/lightclient/sync.rs b/zingolib/src/lightclient/sync.rs index 75cc53308..672d6aa8d 100644 --- a/zingolib/src/lightclient/sync.rs +++ b/zingolib/src/lightclient/sync.rs @@ -3,8 +3,6 @@ //! the difference between this and wallet/sync.rs is that these can interact with the network layer. use std::sync::atomic; -use std::sync::atomic::AtomicU8; -use std::sync::Arc; use std::time::Duration; use log::debug; @@ -50,7 +48,7 @@ impl LightClient { let wallet = self.wallet.clone(); tokio::spawn(async move { loop { - if LightClient::sync_mode(sync_mode.clone()) == SyncMode::NotRunning { + if SyncMode::from_atomic_u8(sync_mode.clone()) == SyncMode::NotRunning { break; }; @@ -65,6 +63,7 @@ impl LightClient { } /// Clear the wallet state and rescan from wallet birthday. + // TODO: rescan without awaiting pub async fn do_rescan(&mut self) -> Result { debug!("Rescan starting"); @@ -77,16 +76,21 @@ impl LightClient { response } - /// Creates [`pepper_sync::wallet::SyncMode`] from an atomic u8. - pub fn sync_mode(atomic_sync_mode: Arc) -> SyncMode { - SyncMode::from_u8(atomic_sync_mode.load(atomic::Ordering::Acquire)) - .expect("this library does not allow setting of non-valid sync mode variants") + /// Returns the lightclient's sync mode in non-atomic (enum) form. + pub fn sync_mode(&self) -> SyncMode { + SyncMode::from_atomic_u8(self.sync_mode.clone()) } - // TODO: split into separate functions with checks - pub fn set_sync_mode(&self, sync_mode: SyncMode) { + /// Pause the sync engine, releasing the wallet lock until [`self::resume_sync`] is called. + pub fn pause_sync(&self) { self.sync_mode - .store(sync_mode as u8, atomic::Ordering::Release); + .store(SyncMode::Paused as u8, atomic::Ordering::Release); + } + + /// Resume scanning after [`self::pause_sync`] has been called. + pub fn resume_sync(&self) { + self.sync_mode + .store(SyncMode::Running as u8, atomic::Ordering::Release); } } From 10d5aa4964c9f352f2467d751ee1b2606caa6049 Mon Sep 17 00:00:00 2001 From: Oscar Pepper Date: Sat, 22 Feb 2025 11:04:34 +0000 Subject: [PATCH 09/26] implemented sync poll sub command --- zingolib/src/commands.rs | 27 ++++++++++++++++----------- zingolib/src/lightclient/sync.rs | 27 +++++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 11 deletions(-) diff --git a/zingolib/src/commands.rs b/zingolib/src/commands.rs index 1d0b48c97..01f9ac3f1 100644 --- a/zingolib/src/commands.rs +++ b/zingolib/src/commands.rs @@ -2,6 +2,7 @@ //! upgrade-or-replace use crate::data::proposal; +use crate::lightclient::sync::SyncPollReport; use crate::wallet::keys::unified::UnifiedKeyStore; use crate::{lightclient::LightClient, wallet}; use indoc::indoc; @@ -356,20 +357,20 @@ impl Command for SyncCommand { `run` starts or resumes sync. `pause` pauses scanning until sync is resumed. `status` returns a report of the wallet's current sync status. - `result` reports whether the sync task has finished and if complete, returns a sync result. If sync failed - will return the error instead. + `poll` polls the sync task handle, returning a sync result if complete. If sync failed, returns the error + instead. Usage: sync run sync pause sync status - sync result + sync poll "#} } fn short_help(&self) -> &'static str { - "Sync the wallet with the blockchain" + "Sync the wallet to the latest state of the blockchain." } fn exec(&self, args: &[&str], lightclient: &mut LightClient) -> String { @@ -381,17 +382,17 @@ impl Command for SyncCommand { "run" => { if lightclient.sync_mode() == SyncMode::Paused { lightclient.resume_sync(); - "Resuming sync...".to_string() + "Resuming sync task...".to_string() } else { if let Err(e) = RT.block_on(async move { lightclient.sync(true).await }) { return format!("Error: {}", e.to_string()); } - "Sync running...".to_string() + "Launching sync task...".to_string() } } "pause" => { lightclient.pause_sync(); - "Pausing sync...".to_string() + "Pausing sync task...".to_string() } "status" => RT .block_on(async move { @@ -400,10 +401,14 @@ impl Command for SyncCommand { ) }) .pretty(2), - "result" => RT.block_on(async move { - // TODO: check sync handle and report - "".to_string() - }), + "poll" => match lightclient.poll_sync() { + SyncPollReport::NoHandle => "Sync task has not been launched.".to_string(), + SyncPollReport::NotReady => "Sync task is not complete.".to_string(), + SyncPollReport::Ready(sync_result) => match sync_result { + Ok(success) => success.to_string(), + Err(failure) => failure.to_string(), + }, + }, _ => self.help().to_string(), } } diff --git a/zingolib/src/lightclient/sync.rs b/zingolib/src/lightclient/sync.rs index 672d6aa8d..4621c0664 100644 --- a/zingolib/src/lightclient/sync.rs +++ b/zingolib/src/lightclient/sync.rs @@ -2,10 +2,13 @@ //! LightClient sync stuff. //! the difference between this and wallet/sync.rs is that these can interact with the network layer. +use std::borrow::BorrowMut; use std::sync::atomic; use std::time::Duration; +use futures::FutureExt; use log::debug; +use pepper_sync::error::SyncError; use pepper_sync::wallet::SyncMode; use zingo_netutils::GetClientError; @@ -92,6 +95,30 @@ impl LightClient { self.sync_mode .store(SyncMode::Running as u8, atomic::Ordering::Release); } + + /// Polls the sync task, returning [`crate::lightclient::SyncPollReport`]. + pub fn poll_sync(&mut self) -> SyncPollReport { + if let Some(mut sync_handle) = self.sync_handle.take() { + if let Some(sync_result) = sync_handle.borrow_mut().now_or_never() { + SyncPollReport::Ready(sync_result.unwrap()) + } else { + self.sync_handle = Some(sync_handle); + SyncPollReport::NotReady + } + } else { + SyncPollReport::NoHandle + } + } +} + +/// Returned from [`crate::lightclient::LightClient::poll_sync`]. +pub enum SyncPollReport { + /// Sync task has not been launched. + NoHandle, + /// Sync task is not complete. + NotReady, + /// Sync task has completed successfully or failed. + Ready(Result), } #[cfg(test)] From 76ba04e2a3560ea82efafcd9eb085cc84b44f3f9 Mon Sep 17 00:00:00 2001 From: Oscar Pepper Date: Sat, 22 Feb 2025 11:38:58 +0000 Subject: [PATCH 10/26] updating zingo cli to sync in background --- pepper-sync/src/wallet.rs | 15 ++++++++------- zingocli/src/lib.rs | 8 +++++++- zingolib/src/commands.rs | 4 ++-- 3 files changed, 17 insertions(+), 10 deletions(-) diff --git a/pepper-sync/src/wallet.rs b/pepper-sync/src/wallet.rs index b2907f1b0..9327e1273 100644 --- a/pepper-sync/src/wallet.rs +++ b/pepper-sync/src/wallet.rs @@ -319,13 +319,14 @@ impl std::fmt::Display for SyncResult { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!( f, - "{{ - sync start height: {} - sync end height: {} - scanned blocks: {} - scanned sapling outputs: {} - scanned orchard outputs: {} - }}", + "Sync completed succesfully: +{{ + sync start height: {} + sync end height: {} + scanned blocks: {} + scanned sapling outputs: {} + scanned orchard outputs: {} +}}", self.sync_start_height, self.sync_end_height, self.scanned_blocks, diff --git a/zingocli/src/lib.rs b/zingocli/src/lib.rs index 7cc5a1454..483a3cf87 100644 --- a/zingocli/src/lib.rs +++ b/zingocli/src/lib.rs @@ -190,6 +190,12 @@ fn start_interactive( .as_i64() .unwrap(); + match send_command("sync".to_string(), vec!["poll".to_string()]) { + poll if poll.starts_with("Error:") => eprintln!("{poll}"), + poll if poll.starts_with("Sync completed succesfully:") => println!("{poll}"), + _ => (), + } + let readline = rl.readline(&format!( "({}) Block:{} (type 'help') >> ", chain_name, height @@ -474,7 +480,7 @@ pub fn startup( // At startup, run a sync. if filled_template.sync { - let update = commands::do_user_command("sync", &[], &mut lightclient); + let update = commands::do_user_command("sync", &["run"], &mut lightclient); println!("{}", update); } diff --git a/zingolib/src/commands.rs b/zingolib/src/commands.rs index 01f9ac3f1..044890ff1 100644 --- a/zingolib/src/commands.rs +++ b/zingolib/src/commands.rs @@ -358,7 +358,7 @@ impl Command for SyncCommand { `pause` pauses scanning until sync is resumed. `status` returns a report of the wallet's current sync status. `poll` polls the sync task handle, returning a sync result if complete. If sync failed, returns the error - instead. + instead. Poll is not intended to be called manually for zingo-cli. Usage: sync run @@ -406,7 +406,7 @@ impl Command for SyncCommand { SyncPollReport::NotReady => "Sync task is not complete.".to_string(), SyncPollReport::Ready(sync_result) => match sync_result { Ok(success) => success.to_string(), - Err(failure) => failure.to_string(), + Err(failure) => format!("Error: {}", failure.to_string()), }, }, _ => self.help().to_string(), From b717fa7ae001f46b4964d17fb6509a368c1fe166 Mon Sep 17 00:00:00 2001 From: Oscar Pepper Date: Mon, 24 Feb 2025 04:17:33 +0000 Subject: [PATCH 11/26] cleaned up and implemented rescan with correct error handling and no forced awaits --- .../tests/network_interruption_tests.rs | 5 +- libtonode-tests/tests/concrete.rs | 9 ++- pepper-sync/src/sync.rs | 3 +- zingolib/src/commands.rs | 37 +++++++---- zingolib/src/lightclient.rs | 13 ++-- zingolib/src/lightclient/error.rs | 14 +++++ zingolib/src/lightclient/sync.rs | 63 +++++++++---------- zingolib/src/wallet.rs | 4 +- zingolib/src/wallet/sync.rs | 2 +- 9 files changed, 86 insertions(+), 64 deletions(-) create mode 100644 zingolib/src/lightclient/error.rs diff --git a/darkside-tests/tests/network_interruption_tests.rs b/darkside-tests/tests/network_interruption_tests.rs index 8ee6f22f1..16dd31d13 100644 --- a/darkside-tests/tests/network_interruption_tests.rs +++ b/darkside-tests/tests/network_interruption_tests.rs @@ -62,7 +62,10 @@ async fn interrupt_initial_tree_fetch() { println!("aborted proxy"); }); println!("spawned abortion task"); - let result = light_client.sync_and_await(true).await; + let result = light_client + .sync_and_await(true) + .await + .map_err(|e| e.to_string()); assert_eq!(result.unwrap_err(),"status: Unavailable, message: \"error trying to connect: tcp connect error: Connection refused (os error 111)\", details: [], metadata: MetadataMap { headers: {} }"); } diff --git a/libtonode-tests/tests/concrete.rs b/libtonode-tests/tests/concrete.rs index d931fc76f..427b88adc 100644 --- a/libtonode-tests/tests/concrete.rs +++ b/libtonode-tests/tests/concrete.rs @@ -2758,7 +2758,8 @@ mod slow { } let pre_rescan_summaries = faucet.transaction_summaries().await; - faucet.do_rescan().await.unwrap(); + faucet.rescan(false).await.unwrap(); + faucet.await_sync().await.unwrap(); let post_rescan_summaries = faucet.transaction_summaries().await; assert_eq!(pre_rescan_summaries, post_rescan_summaries); } @@ -2799,7 +2800,8 @@ mod slow { .unwrap(); let pre_rescan_summaries = faucet.transaction_summaries().await; - faucet.do_rescan().await.unwrap(); + faucet.rescan(false).await.unwrap(); + faucet.await_sync().await.unwrap(); let post_rescan_summaries = faucet.transaction_summaries().await; assert_eq!(pre_rescan_summaries, post_rescan_summaries); } @@ -2823,7 +2825,8 @@ mod slow { .unwrap(); let pre_rescan_transactions = recipient.transaction_summaries().await; let pre_rescan_summaries = recipient.sorted_value_transfers(true).await; - recipient.do_rescan().await.unwrap(); + recipient.rescan(false).await.unwrap(); + recipient.await_sync().await.unwrap(); let post_rescan_transactions = recipient.transaction_summaries().await; let post_rescan_summaries = recipient.sorted_value_transfers(true).await; assert_eq!(pre_rescan_transactions, post_rescan_transactions); diff --git a/pepper-sync/src/sync.rs b/pepper-sync/src/sync.rs index af4d895f5..6e144f1c2 100644 --- a/pepper-sync/src/sync.rs +++ b/pepper-sync/src/sync.rs @@ -258,8 +258,7 @@ where .expect("should be non-empty after syncing") .block_range() .end - - 1) - .into(), + - 1), scanned_blocks: sync_status.scanned_blocks, scanned_sapling_outputs: sync_status.scanned_sapling_outputs, scanned_orchard_outputs: sync_status.scanned_orchard_outputs, diff --git a/zingolib/src/commands.rs b/zingolib/src/commands.rs index 044890ff1..65c4ead64 100644 --- a/zingolib/src/commands.rs +++ b/zingolib/src/commands.rs @@ -375,7 +375,8 @@ impl Command for SyncCommand { fn exec(&self, args: &[&str], lightclient: &mut LightClient) -> String { if args.len() != 1 { - return self.help().to_string(); + return "Error: sync command expects 1 argument. Type \"sync help\" for usage." + .to_string(); } match args[0] { @@ -385,7 +386,7 @@ impl Command for SyncCommand { "Resuming sync task...".to_string() } else { if let Err(e) = RT.block_on(async move { lightclient.sync(true).await }) { - return format!("Error: {}", e.to_string()); + return format!("Error: {e}"); } "Launching sync task...".to_string() } @@ -404,12 +405,12 @@ impl Command for SyncCommand { "poll" => match lightclient.poll_sync() { SyncPollReport::NoHandle => "Sync task has not been launched.".to_string(), SyncPollReport::NotReady => "Sync task is not complete.".to_string(), - SyncPollReport::Ready(sync_result) => match sync_result { - Ok(success) => success.to_string(), - Err(failure) => format!("Error: {}", failure.to_string()), + SyncPollReport::Ready(result) => match result { + Ok(sync_result) => sync_result.to_string(), + Err(e) => format!("Error: {e}"), }, }, - _ => self.help().to_string(), + _ => "Error: invalid sub-command. Type \"sync help\" for usage.".to_string(), } } } @@ -442,21 +443,31 @@ struct RescanCommand {} impl Command for RescanCommand { fn help(&self) -> &'static str { indoc! {r#" - Rescan the wallet, rescanning all blocks for new transactions + Rescan the wallet, clearing all wallet data obtained from the blockchain and launching sync from the wallet + birthday. + Usage: rescan - - This command will download all blocks since the initial block again from the light client server - and attempt to scan each block for transactions belonging to the wallet. "#} } fn short_help(&self) -> &'static str { - "Rescan the wallet, downloading and scanning all blocks and transactions" + "Rescan the wallet, clearing all wallet data obtained from the blockchain and launching sync from the wallet + birthday." } - fn exec(&self, _args: &[&str], _lightclient: &mut LightClient) -> String { - todo!() + fn exec(&self, args: &[&str], lightclient: &mut LightClient) -> String { + if !args.is_empty() { + return "Error: rescan command expects no arguments. Type \"rescan help\" for usage." + .to_string(); + } + + RT.block_on(async move { + match lightclient.rescan(true).await { + Ok(_) => "Launching rescan...".to_string(), + Err(e) => format!("Error: {e}"), + } + }) } } diff --git a/zingolib/src/lightclient.rs b/zingolib/src/lightclient.rs index 8351702a2..4e4eb7216 100644 --- a/zingolib/src/lightclient.rs +++ b/zingolib/src/lightclient.rs @@ -22,6 +22,8 @@ use crate::wallet::{ keys::unified::ReceiverSelection, message::Message, LightWallet, SendProgress, }; +pub mod error; + /// TODO: Add Doc Comment Here! #[derive(Debug, Clone)] pub struct LightWalletSendProgress { @@ -230,14 +232,9 @@ pub struct UserBalances { pub incoming_dust: u64, } -/// The LightClient connects one LightWallet to one indexer server via gRPC. -/// 1. initialization of stored state -/// * from seed -/// * from keys -/// * from wallets -/// * from a fresh start with reasonable defaults -/// 2. synchronization of the client with the state of the blockchain via a gRPC server -/// * +/// Struct which owns and manages the [`crate::wallet::LightWallet`]. Responsible for network operations such as +/// storing the indexer URI, creating gRPC clients and syncing the wallet to the blockchain. +/// /// `sync_mode` is an atomic representation of [`pepper_sync::wallet::SyncMode`]. pub struct LightClient { // TODO: split zingoconfig so data is not duplicated diff --git a/zingolib/src/lightclient/error.rs b/zingolib/src/lightclient/error.rs new file mode 100644 index 000000000..cc0991c2a --- /dev/null +++ b/zingolib/src/lightclient/error.rs @@ -0,0 +1,14 @@ +//! Errors assoicated with [`crate::lightclient::LightClient`]. + +#[derive(Debug, thiserror::Error)] +pub enum LightClientError { + /// Sync not running. + #[error("No sync handle. Sync is not running.")] + SyncNotRunning, + /// Sync failed. + #[error("Sync failed. {0}")] + SyncFailed(#[from] pepper_sync::error::SyncError), + /// gPRC client error + #[error("gRPC client error. {0}")] + ClientError(#[from] zingo_netutils::GetClientError), +} diff --git a/zingolib/src/lightclient/sync.rs b/zingolib/src/lightclient/sync.rs index 4621c0664..b55ce20f6 100644 --- a/zingolib/src/lightclient/sync.rs +++ b/zingolib/src/lightclient/sync.rs @@ -1,35 +1,19 @@ -//! AWISOTT -//! LightClient sync stuff. -//! the difference between this and wallet/sync.rs is that these can interact with the network layer. +//! Sync implementations for [crate::lightclient::LightClient] and related types. use std::borrow::BorrowMut; use std::sync::atomic; use std::time::Duration; use futures::FutureExt; -use log::debug; use pepper_sync::error::SyncError; use pepper_sync::wallet::SyncMode; use zingo_netutils::GetClientError; +use super::error::LightClientError; use super::LightClient; use super::SyncResult; impl LightClient { - /// Calls [`self::sync`] and awaits the handle. - // TODO: error handling, concrete error types - pub async fn sync_and_await(&mut self, print_updates: bool) -> Result { - self.sync(print_updates).await.map_err(|e| e.to_string())?; - - Ok(self - .sync_handle - .take() - .expect("handle should always exist after calling `sync`") - .await - .map_err(|e| e.to_string())? - .map_err(|e| e.to_string())?) - } - /// Launches a task for syncing the wallet to the latest state of the block chain, storing the handle in the /// `sync_handle` field. pub async fn sync(&mut self, print_updates: bool) -> Result<(), GetClientError> { @@ -65,18 +49,10 @@ impl LightClient { Ok(()) } - /// Clear the wallet state and rescan from wallet birthday. - // TODO: rescan without awaiting - pub async fn do_rescan(&mut self) -> Result { - debug!("Rescan starting"); - + /// Clear the wallet data obtained from the blockchain and launch sync from wallet birthday. + pub async fn rescan(&mut self, print_updates: bool) -> Result<(), GetClientError> { self.wallet.lock().await.clear_all(); - - let response = self.sync_and_await(false).await; - - debug!("Rescan finished"); - - response + self.sync(print_updates).await } /// Returns the lightclient's sync mode in non-atomic (enum) form. @@ -84,23 +60,23 @@ impl LightClient { SyncMode::from_atomic_u8(self.sync_mode.clone()) } - /// Pause the sync engine, releasing the wallet lock until [`self::resume_sync`] is called. + /// Pause the sync engine, releasing the wallet lock until [`crate::lightclient::LightClient::resume_sync`] is called. pub fn pause_sync(&self) { self.sync_mode .store(SyncMode::Paused as u8, atomic::Ordering::Release); } - /// Resume scanning after [`self::pause_sync`] has been called. + /// Resume scanning after [`crate::lightclient::LightClient::pause_sync`] has been called. pub fn resume_sync(&self) { self.sync_mode .store(SyncMode::Running as u8, atomic::Ordering::Release); } - /// Polls the sync task, returning [`crate::lightclient::SyncPollReport`]. + /// Polls the sync task, returning [`self::SyncPollReport`]. pub fn poll_sync(&mut self) -> SyncPollReport { if let Some(mut sync_handle) = self.sync_handle.take() { if let Some(sync_result) = sync_handle.borrow_mut().now_or_never() { - SyncPollReport::Ready(sync_result.unwrap()) + SyncPollReport::Ready(sync_result.expect("task panicked")) } else { self.sync_handle = Some(sync_handle); SyncPollReport::NotReady @@ -109,6 +85,27 @@ impl LightClient { SyncPollReport::NoHandle } } + + /// Awaits until sync has completed + /// Returns [`pepper_sync::wallet::SyncResult`] if successful. + /// Returns [`crate::lightclient::error::LightClientError`] on failure. + pub async fn await_sync(&mut self) -> Result { + Ok(self + .sync_handle + .take() + .ok_or(LightClientError::SyncNotRunning)? + .await + .expect("task panicked")?) + } + + /// Calls [`crate::lightclient::LightClient::sync`] and then [`crate::lightclient::LightClient::await_sync`]. + pub async fn sync_and_await( + &mut self, + print_updates: bool, + ) -> Result { + self.sync(print_updates).await?; + self.await_sync().await + } } /// Returned from [`crate::lightclient::LightClient::poll_sync`]. diff --git a/zingolib/src/wallet.rs b/zingolib/src/wallet.rs index 255aac90a..59311266b 100644 --- a/zingolib/src/wallet.rs +++ b/zingolib/src/wallet.rs @@ -219,9 +219,7 @@ pub struct LightWallet { } impl LightWallet { - /// Clears all the downloaded blocks and resets the state back to the initial block. - /// After this, the wallet's initial state will need to be set - /// and the wallet will need to be rescanned + /// Clears all wallet data obtained from the block chain including the sync state. pub fn clear_all(&mut self) { self.wallet_blocks.clear(); self.wallet_transactions.clear(); diff --git a/zingolib/src/wallet/sync.rs b/zingolib/src/wallet/sync.rs index ad672f79a..0a6b17978 100644 --- a/zingolib/src/wallet/sync.rs +++ b/zingolib/src/wallet/sync.rs @@ -1,4 +1,4 @@ -//! Trait implementations for sync interface +//! Trait implementations for interfacing [`crate::wallet::LightWallet`] with [`pepper_sync`] sync engine. use std::collections::{BTreeMap, HashMap}; From 300c0b89cfa4374f02bc740266cb5587c2c667f0 Mon Sep 17 00:00:00 2001 From: Oscar Pepper Date: Mon, 24 Feb 2025 04:44:08 +0000 Subject: [PATCH 12/26] remove print updates as it's not useful in tests and only used in zingo-cli where it breaks the user input now, added a todo for adding live updates when printing during user input is implemented --- darkside-tests/tests/advanced_reorg_tests.rs | 30 ++++++------- .../tests/network_interruption_tests.rs | 10 ++--- darkside-tests/tests/tests.rs | 6 +-- libtonode-tests/tests/concrete.rs | 42 +++++++++---------- libtonode-tests/tests/sync.rs | 8 ++-- pepper-sync/src/wallet.rs | 2 +- zingolib/src/commands.rs | 6 +-- zingolib/src/lightclient/sync.rs | 34 ++++----------- zingolib/src/testutils.rs | 2 +- .../testutils/chain_generics/conduct_chain.rs | 4 +- .../src/testutils/chain_generics/fixtures.rs | 2 +- .../chain_generics/with_assertions.rs | 8 ++-- zingolib/src/testutils/scenarios.rs | 10 ++--- zingolib/src/wallet/disk/testing/tests.rs | 2 +- 14 files changed, 71 insertions(+), 95 deletions(-) diff --git a/darkside-tests/tests/advanced_reorg_tests.rs b/darkside-tests/tests/advanced_reorg_tests.rs index 8895bcc34..0bc0b5d6c 100644 --- a/darkside-tests/tests/advanced_reorg_tests.rs +++ b/darkside-tests/tests/advanced_reorg_tests.rs @@ -44,7 +44,7 @@ async fn reorg_changes_incoming_tx_height() { ) .await; - light_client.sync_and_await(true).await.unwrap(); + light_client.sync_and_await().await.unwrap(); assert_eq!( light_client.do_balance().await, PoolBalances { @@ -72,7 +72,7 @@ async fn reorg_changes_incoming_tx_height() { .await .unwrap(); - let reorg_sync_result = light_client.sync_and_await(true).await; + let reorg_sync_result = light_client.sync_and_await().await; match reorg_sync_result { Ok(value) => println!("{}", value), @@ -201,7 +201,7 @@ async fn reorg_changes_incoming_tx_index() { ) .await; - light_client.sync_and_await(true).await.unwrap(); + light_client.sync_and_await().await.unwrap(); assert_eq!( light_client.do_balance().await, PoolBalances { @@ -229,7 +229,7 @@ async fn reorg_changes_incoming_tx_index() { .await .unwrap(); - let reorg_sync_result = light_client.sync_and_await(true).await; + let reorg_sync_result = light_client.sync_and_await().await; match reorg_sync_result { Ok(value) => println!("{}", value), @@ -357,7 +357,7 @@ async fn reorg_expires_incoming_tx() { ) .await; - light_client.sync_and_await(true).await.unwrap(); + light_client.sync_and_await().await.unwrap(); assert_eq!( light_client.do_balance().await, PoolBalances { @@ -385,7 +385,7 @@ async fn reorg_expires_incoming_tx() { .await .unwrap(); - let reorg_sync_result = light_client.sync_and_await(true).await; + let reorg_sync_result = light_client.sync_and_await().await; match reorg_sync_result { Ok(value) => println!("{}", value), @@ -536,7 +536,7 @@ async fn reorg_changes_outgoing_tx_height() { ) .await; - light_client.sync_and_await(true).await.unwrap(); + light_client.sync_and_await().await.unwrap(); assert_eq!( light_client.do_balance().await, PoolBalances { @@ -583,7 +583,7 @@ async fn reorg_changes_outgoing_tx_height() { let sent_tx_height: i32 = 205; _ = connector.apply_staged(sent_tx_height).await; - light_client.sync_and_await(true).await.unwrap(); + light_client.sync_and_await().await.unwrap(); let expected_after_send_balance = PoolBalances { sapling_balance: Some(0), @@ -641,7 +641,7 @@ async fn reorg_changes_outgoing_tx_height() { _ = connector.apply_staged(211).await; - let reorg_sync_result = light_client.sync_and_await(true).await; + let reorg_sync_result = light_client.sync_and_await().await; match reorg_sync_result { Ok(value) => println!("{}", value), @@ -789,7 +789,7 @@ async fn reorg_expires_outgoing_tx_height() { transparent_balance: Some(0), }; - light_client.sync_and_await(true).await.unwrap(); + light_client.sync_and_await().await.unwrap(); assert_eq!(light_client.do_balance().await, expected_initial_balance); let before_reorg_transactions = light_client.sorted_value_transfers(true).await; @@ -816,7 +816,7 @@ async fn reorg_expires_outgoing_tx_height() { let sent_tx_height: i32 = 205; _ = connector.apply_staged(sent_tx_height).await; - light_client.sync_and_await(true).await.unwrap(); + light_client.sync_and_await().await.unwrap(); let expected_after_send_balance = PoolBalances { sapling_balance: Some(0), @@ -867,7 +867,7 @@ async fn reorg_expires_outgoing_tx_height() { // this will remove the submitted transaction from our view of the blockchain _ = connector.apply_staged(245).await; - let reorg_sync_result = light_client.sync_and_await(true).await; + let reorg_sync_result = light_client.sync_and_await().await; match reorg_sync_result { Ok(value) => println!("{}", value), @@ -955,7 +955,7 @@ async fn reorg_changes_outgoing_tx_index() { ) .await; - light_client.sync_and_await(true).await.unwrap(); + light_client.sync_and_await().await.unwrap(); assert_eq!( light_client.do_balance().await, PoolBalances { @@ -1002,7 +1002,7 @@ async fn reorg_changes_outgoing_tx_index() { let sent_tx_height: i32 = 205; _ = connector.apply_staged(sent_tx_height).await; - light_client.sync_and_await(true).await.unwrap(); + light_client.sync_and_await().await.unwrap(); let expected_after_send_balance = PoolBalances { sapling_balance: Some(0), @@ -1069,7 +1069,7 @@ async fn reorg_changes_outgoing_tx_index() { _ = connector.apply_staged(312).await; - let reorg_sync_result = light_client.sync_and_await(true).await; + let reorg_sync_result = light_client.sync_and_await().await; match reorg_sync_result { Ok(value) => println!("{}", value), diff --git a/darkside-tests/tests/network_interruption_tests.rs b/darkside-tests/tests/network_interruption_tests.rs index 16dd31d13..b513fa9ed 100644 --- a/darkside-tests/tests/network_interruption_tests.rs +++ b/darkside-tests/tests/network_interruption_tests.rs @@ -63,7 +63,7 @@ async fn interrupt_initial_tree_fetch() { }); println!("spawned abortion task"); let result = light_client - .sync_and_await(true) + .sync_and_await() .await .map_err(|e| e.to_string()); assert_eq!(result.unwrap_err(),"status: Unavailable, message: \"error trying to connect: tcp connect error: Connection refused (os error 111)\", details: [], metadata: MetadataMap { headers: {} }"); @@ -89,7 +89,7 @@ async fn shielded_note_marked_as_change_chainbuild() { scenario .stage_and_apply_blocks(thousands_blocks_count * 1000 - 2, 0) .await; - scenario.get_faucet().sync_and_await(false).await.unwrap(); + scenario.get_faucet().sync_and_await().await.unwrap(); let recipient_addr = get_base_address_macro!(scenario.get_lightclient(0), "sapling"); scenario .send_and_write_transaction( @@ -102,11 +102,7 @@ async fn shielded_note_marked_as_change_chainbuild() { scenario .apply_blocks(thousands_blocks_count * 1000 - 1) .await; - scenario - .get_lightclient(0) - .sync_and_await(false) - .await - .unwrap(); + scenario.get_lightclient(0).sync_and_await().await.unwrap(); scenario .shield_and_write_transaction(DarksideSender::IndexedClient(0), &chainbuild_file) .await; diff --git a/darkside-tests/tests/tests.rs b/darkside-tests/tests/tests.rs index b2167eac4..d5a0296e7 100644 --- a/darkside-tests/tests/tests.rs +++ b/darkside-tests/tests/tests.rs @@ -32,7 +32,7 @@ async fn simple_sync() { .build_client(DARKSIDE_SEED.to_string(), 0, true, regtest_network) .await; - let result = light_client.sync_and_await(true).await.unwrap(); + let result = light_client.sync_and_await().await.unwrap(); println!("{}", result); @@ -71,7 +71,7 @@ async fn reorg_receipt_sync_generic() { ClientBuilder::new(server_id.clone(), darkside_handler.darkside_dir.clone()) .build_client(DARKSIDE_SEED.to_string(), 0, true, regtest_network) .await; - light_client.sync_and_await(false).await.unwrap(); + light_client.sync_and_await().await.unwrap(); assert_eq!( light_client.do_balance().await, @@ -90,7 +90,7 @@ async fn reorg_receipt_sync_generic() { prepare_darksidewalletd(server_id.clone(), false) .await .unwrap(); - light_client.sync_and_await(false).await.unwrap(); + light_client.sync_and_await().await.unwrap(); assert_eq!( light_client.do_balance().await, PoolBalances { diff --git a/libtonode-tests/tests/concrete.rs b/libtonode-tests/tests/concrete.rs index 427b88adc..25bf5b3f3 100644 --- a/libtonode-tests/tests/concrete.rs +++ b/libtonode-tests/tests/concrete.rs @@ -462,7 +462,7 @@ mod fast { let mut recipient = environment.create_client().await; environment.bump_chain().await; - faucet.sync_and_await(false).await.unwrap(); + faucet.sync_and_await().await.unwrap(); check_client_balances!(faucet, o: 0 s: 2_500_000_000u64 t: 0u64); @@ -485,7 +485,7 @@ mod fast { .unwrap(); environment.bump_chain().await; - recipient.sync_and_await(false).await.unwrap(); + recipient.sync_and_await().await.unwrap(); let no_messages = &recipient.messages_containing(None).await; @@ -510,7 +510,7 @@ mod fast { .unwrap(); environment.bump_chain().await; - recipient.sync_and_await(false).await.unwrap(); + recipient.sync_and_await().await.unwrap(); let single_message = &recipient.messages_containing(None).await; @@ -689,7 +689,7 @@ mod fast { let mut recipient = environment.create_client().await; environment.bump_chain().await; - faucet.sync_and_await(false).await.unwrap(); + faucet.sync_and_await().await.unwrap(); check_client_balances!(faucet, o: 0 s: 2_500_000_000u64 t: 0u64); @@ -722,7 +722,7 @@ mod fast { .unwrap(); environment.bump_chain().await; - recipient.sync_and_await(false).await.unwrap(); + recipient.sync_and_await().await.unwrap(); let value_transfers = &recipient.sorted_value_transfers(true).await; let value_transfers1 = &recipient.sorted_value_transfers(true).await; @@ -817,7 +817,7 @@ mod fast { .await .unwrap(); - recipient.sync_and_await(false).await.unwrap(); + recipient.sync_and_await().await.unwrap(); let transactions = &recipient.transaction_summaries().await.0; transactions.iter().for_each(|tx| { @@ -2109,7 +2109,7 @@ mod slow { let faucet_to_recipient_amount = 20_000u64; let recipient_to_faucet_amount = 5_000u64; // check start state - faucet.sync_and_await(true).await.unwrap(); + faucet.sync_and_await().await.unwrap(); let wallet_height = faucet.do_wallet_last_scanned_height().await; assert_eq!( wallet_height.as_fixed_point_u64(0).unwrap(), @@ -2140,7 +2140,7 @@ mod slow { ) .await .unwrap(); - faucet.sync_and_await(true).await.unwrap(); + faucet.sync_and_await().await.unwrap(); let faucet_orch = three_blocks_reward + orch_change + u64::from(MINIMUM_FEE); println!( @@ -2169,7 +2169,7 @@ mod slow { zingolib::testutils::increase_height_and_wait_for_client(®test_manager, &mut faucet, 1) .await .unwrap(); - recipient.sync_and_await(true).await.unwrap(); + recipient.sync_and_await().await.unwrap(); let faucet_final_orch = faucet_orch + recipient_to_faucet_amount @@ -2348,7 +2348,7 @@ mod slow { ) .await .unwrap(); - faucet.sync_and_await(false).await.unwrap(); + faucet.sync_and_await().await.unwrap(); from_inputs::quick_send( &faucet, vec![ @@ -2758,7 +2758,7 @@ mod slow { } let pre_rescan_summaries = faucet.transaction_summaries().await; - faucet.rescan(false).await.unwrap(); + faucet.rescan().await.unwrap(); faucet.await_sync().await.unwrap(); let post_rescan_summaries = faucet.transaction_summaries().await; assert_eq!(pre_rescan_summaries, post_rescan_summaries); @@ -2800,7 +2800,7 @@ mod slow { .unwrap(); let pre_rescan_summaries = faucet.transaction_summaries().await; - faucet.rescan(false).await.unwrap(); + faucet.rescan().await.unwrap(); faucet.await_sync().await.unwrap(); let post_rescan_summaries = faucet.transaction_summaries().await; assert_eq!(pre_rescan_summaries, post_rescan_summaries); @@ -2825,7 +2825,7 @@ mod slow { .unwrap(); let pre_rescan_transactions = recipient.transaction_summaries().await; let pre_rescan_summaries = recipient.sorted_value_transfers(true).await; - recipient.rescan(false).await.unwrap(); + recipient.rescan().await.unwrap(); recipient.await_sync().await.unwrap(); let post_rescan_transactions = recipient.transaction_summaries().await; let post_rescan_summaries = recipient.sorted_value_transfers(true).await; @@ -3712,8 +3712,8 @@ mod basic_transactions { .await .unwrap(); - recipient.sync_and_await(true).await.unwrap(); - faucet.sync_and_await(true).await.unwrap(); + recipient.sync_and_await().await.unwrap(); + faucet.sync_and_await().await.unwrap(); for _ in 0..2 { from_inputs::quick_send(&faucet, vec![(recipient_addr_ua.as_str(), 40_000, None)]) @@ -3725,8 +3725,8 @@ mod basic_transactions { .await .unwrap(); - recipient.sync_and_await(true).await.unwrap(); - faucet.sync_and_await(true).await.unwrap(); + recipient.sync_and_await().await.unwrap(); + faucet.sync_and_await().await.unwrap(); from_inputs::quick_send(&recipient, vec![(faucet_addr_ua.as_str(), 50_000, None)]) .await @@ -3736,8 +3736,8 @@ mod basic_transactions { .await .unwrap(); - recipient.sync_and_await(true).await.unwrap(); - faucet.sync_and_await(true).await.unwrap(); + recipient.sync_and_await().await.unwrap(); + faucet.sync_and_await().await.unwrap(); } // FIXME: @@ -4192,7 +4192,7 @@ mod send_all { increase_height_and_wait_for_client(®test_manager, &mut faucet, 1) .await .unwrap(); - recipient.sync_and_await(false).await.unwrap(); + recipient.sync_and_await().await.unwrap(); recipient .propose_send_all( @@ -4209,7 +4209,7 @@ mod send_all { increase_height_and_wait_for_client(®test_manager, &mut recipient, 1) .await .unwrap(); - faucet.sync_and_await(false).await.unwrap(); + faucet.sync_and_await().await.unwrap(); assert_eq!( recipient diff --git a/libtonode-tests/tests/sync.rs b/libtonode-tests/tests/sync.rs index c06f04726..85aea4187 100644 --- a/libtonode-tests/tests/sync.rs +++ b/libtonode-tests/tests/sync.rs @@ -34,7 +34,7 @@ async fn sync_mainnet_test() { .await .unwrap(); - lightclient.sync_and_await(false).await.unwrap(); + lightclient.sync_and_await().await.unwrap(); let wallet = lightclient.wallet.lock().await; // dbg!(&wallet.wallet_blocks); @@ -69,7 +69,7 @@ async fn sync_status() { .await .unwrap(); - lightclient.sync_and_await(true).await.unwrap(); + lightclient.sync_and_await().await.unwrap(); } // temporary test for sync development @@ -102,10 +102,10 @@ async fn sync_test() { // .unwrap(); increase_server_height(®test_manager, 1).await; - recipient.sync_and_await(false).await.unwrap(); + recipient.sync_and_await().await.unwrap(); recipient.quick_shield().await.unwrap(); increase_server_height(®test_manager, 1).await; - recipient.sync_and_await(true).await.unwrap(); + recipient.sync_and_await().await.unwrap(); // let wallet = recipient.wallet.lock().await; // dbg!(&wallet.wallet_transactions); diff --git a/pepper-sync/src/wallet.rs b/pepper-sync/src/wallet.rs index 9327e1273..c16de4496 100644 --- a/pepper-sync/src/wallet.rs +++ b/pepper-sync/src/wallet.rs @@ -325,7 +325,7 @@ impl std::fmt::Display for SyncResult { sync end height: {} scanned blocks: {} scanned sapling outputs: {} - scanned orchard outputs: {} + scanned orchard outputs: {} }}", self.sync_start_height, self.sync_end_height, diff --git a/zingolib/src/commands.rs b/zingolib/src/commands.rs index 65c4ead64..c0f403501 100644 --- a/zingolib/src/commands.rs +++ b/zingolib/src/commands.rs @@ -385,7 +385,7 @@ impl Command for SyncCommand { lightclient.resume_sync(); "Resuming sync task...".to_string() } else { - if let Err(e) = RT.block_on(async move { lightclient.sync(true).await }) { + if let Err(e) = RT.block_on(async move { lightclient.sync().await }) { return format!("Error: {e}"); } "Launching sync task...".to_string() @@ -445,7 +445,7 @@ impl Command for RescanCommand { indoc! {r#" Rescan the wallet, clearing all wallet data obtained from the blockchain and launching sync from the wallet birthday. - + Usage: rescan "#} @@ -463,7 +463,7 @@ impl Command for RescanCommand { } RT.block_on(async move { - match lightclient.rescan(true).await { + match lightclient.rescan().await { Ok(_) => "Launching rescan...".to_string(), Err(e) => format!("Error: {e}"), } diff --git a/zingolib/src/lightclient/sync.rs b/zingolib/src/lightclient/sync.rs index b55ce20f6..606273410 100644 --- a/zingolib/src/lightclient/sync.rs +++ b/zingolib/src/lightclient/sync.rs @@ -2,7 +2,6 @@ use std::borrow::BorrowMut; use std::sync::atomic; -use std::time::Duration; use futures::FutureExt; use pepper_sync::error::SyncError; @@ -16,7 +15,8 @@ use super::SyncResult; impl LightClient { /// Launches a task for syncing the wallet to the latest state of the block chain, storing the handle in the /// `sync_handle` field. - pub async fn sync(&mut self, print_updates: bool) -> Result<(), GetClientError> { + // TODO: add realtime sync updates to zingo-cli when it can handle printing during user input + pub async fn sync(&mut self) -> Result<(), GetClientError> { let client = zingo_netutils::GrpcConnector::new(self.config.get_lightwalletd_uri()) .get_client() .await?; @@ -29,30 +29,13 @@ impl LightClient { ); self.sync_handle = Some(sync_handle); - // TODO: replace this with calls to `sync status` in zingo-cli - if print_updates { - let sync_mode = self.sync_mode.clone(); - let wallet = self.wallet.clone(); - tokio::spawn(async move { - loop { - if SyncMode::from_atomic_u8(sync_mode.clone()) == SyncMode::NotRunning { - break; - }; - - let sync_status = pepper_sync::sync_status(&*wallet.lock().await).await; - println!("{}", sync_status); - tokio::time::sleep(Duration::from_secs(1)).await; - } - }); - } - Ok(()) } /// Clear the wallet data obtained from the blockchain and launch sync from wallet birthday. - pub async fn rescan(&mut self, print_updates: bool) -> Result<(), GetClientError> { + pub async fn rescan(&mut self) -> Result<(), GetClientError> { self.wallet.lock().await.clear_all(); - self.sync(print_updates).await + self.sync().await } /// Returns the lightclient's sync mode in non-atomic (enum) form. @@ -99,11 +82,8 @@ impl LightClient { } /// Calls [`crate::lightclient::LightClient::sync`] and then [`crate::lightclient::LightClient::await_sync`]. - pub async fn sync_and_await( - &mut self, - print_updates: bool, - ) -> Result { - self.sync(print_updates).await?; + pub async fn sync_and_await(&mut self) -> Result { + self.sync().await?; self.await_sync().await } } @@ -135,7 +115,7 @@ pub mod test { let mut lc = wallet_case.load_example_wallet_with_client().await; - let sync_result = lc.sync_and_await(false).await.unwrap(); + let sync_result = lc.sync_and_await().await.unwrap(); println!("{}", sync_result); println!("{:?}", lc.do_balance().await); lc diff --git a/zingolib/src/testutils.rs b/zingolib/src/testutils.rs index 94f74b4e8..205eecdac 100644 --- a/zingolib/src/testutils.rs +++ b/zingolib/src/testutils.rs @@ -278,7 +278,7 @@ pub async fn sync_to_target_height( < target_block_height { tokio::time::sleep(Duration::from_millis(500)).await; - client.sync_and_await(false).await.unwrap(); + client.sync_and_await().await.unwrap(); } Ok(()) } diff --git a/zingolib/src/testutils/chain_generics/conduct_chain.rs b/zingolib/src/testutils/chain_generics/conduct_chain.rs index 240c2e5bb..9b42dc64a 100644 --- a/zingolib/src/testutils/chain_generics/conduct_chain.rs +++ b/zingolib/src/testutils/chain_generics/conduct_chain.rs @@ -65,7 +65,7 @@ pub trait ConductChain { let mut recipient = self.create_client().await; self.bump_chain().await; - faucet.sync_and_await(false).await.unwrap(); + faucet.sync_and_await().await.unwrap(); // FIXME: zingo2 // from_inputs::quick_send( @@ -81,7 +81,7 @@ pub trait ConductChain { self.bump_chain().await; - recipient.sync_and_await(false).await.unwrap(); + recipient.sync_and_await().await.unwrap(); recipient } diff --git a/zingolib/src/testutils/chain_generics/fixtures.rs b/zingolib/src/testutils/chain_generics/fixtures.rs index 12e4cb71a..bad8de1ef 100644 --- a/zingolib/src/testutils/chain_generics/fixtures.rs +++ b/zingolib/src/testutils/chain_generics/fixtures.rs @@ -400,7 +400,7 @@ where let mut secondary = environment.create_client().await; let tertiary = environment.create_client().await; - secondary.sync_and_await(false).await.unwrap(); + secondary.sync_and_await().await.unwrap(); let expected_fee = fee_tables::one_to_one(None, pool, true); diff --git a/zingolib/src/testutils/chain_generics/with_assertions.rs b/zingolib/src/testutils/chain_generics/with_assertions.rs index 074f179d5..07cc36f94 100644 --- a/zingolib/src/testutils/chain_generics/with_assertions.rs +++ b/zingolib/src/testutils/chain_generics/with_assertions.rs @@ -160,7 +160,7 @@ where let option_recipient_mempool_outputs = if test_mempool { // mempool scan shows the same - sender.sync_and_await(false).await.unwrap(); + sender.sync_and_await().await.unwrap(); // let the mempool monitor get a chance // to listen @@ -199,7 +199,7 @@ where let mut recipients_mempool_outputs: Vec> = vec![]; for recipient in recipients.iter_mut() { - recipient.sync_and_await(false).await.unwrap(); + recipient.sync_and_await().await.unwrap(); // check that each record has the status, returning the output value let (recipient_mempool_outputs, recipient_mempool_statuses): ( @@ -236,7 +236,7 @@ where environment.bump_chain().await; // chain scan shows the same - sender.sync_and_await(false).await.unwrap(); + sender.sync_and_await().await.unwrap(); // check that each record has the expected fee and status, returning the fee and outputs let (sender_confirmed_fees, (sender_confirmed_outputs, sender_confirmed_statuses)): ( @@ -271,7 +271,7 @@ where let mut recipients_confirmed_outputs = vec![]; for recipient in recipients.iter_mut() { - recipient.sync_and_await(false).await.unwrap(); + recipient.sync_and_await().await.unwrap(); // check that each record has the status, returning the output value let (recipient_confirmed_outputs, recipient_confirmed_statuses): ( diff --git a/zingolib/src/testutils/scenarios.rs b/zingolib/src/testutils/scenarios.rs index e0b425121..fa3c648c0 100644 --- a/zingolib/src/testutils/scenarios.rs +++ b/zingolib/src/testutils/scenarios.rs @@ -396,7 +396,7 @@ pub async fn faucet( ) .await; let mut faucet = sb.client_builder.build_faucet(false, regtest_network).await; - faucet.sync_and_await(false).await.unwrap(); + faucet.sync_and_await().await.unwrap(); ( sb.regtest_manager, sb.child_process_handler.unwrap(), @@ -435,7 +435,7 @@ pub async fn faucet_recipient( ) .await; let mut faucet = sb.client_builder.build_faucet(false, regtest_network).await; - faucet.sync_and_await(false).await.unwrap(); + faucet.sync_and_await().await.unwrap(); let recipient = sb .client_builder @@ -542,7 +542,7 @@ pub async fn faucet_funded_recipient( increase_height_and_wait_for_client(®test_manager, &mut recipient, 1) .await .unwrap(); - faucet.sync_and_await(false).await.unwrap(); + faucet.sync_and_await().await.unwrap(); ( regtest_manager, child_process_handler, @@ -658,7 +658,7 @@ pub async fn funded_orchard_mobileclient(_value: u64) -> (RegtestManager, ChildP .client_builder .build_client(HOSPITAL_MUSEUM_SEED.to_string(), 0, false, regtest_network) .await; - faucet.sync_and_await(false).await.unwrap(); + faucet.sync_and_await().await.unwrap(); // FIXME: zingo2 // super::lightclient::from_inputs::quick_send( // &faucet, @@ -968,7 +968,7 @@ pub mod chainload { let mut sb = setup::ScenarioBuilder::new_load_1153_saplingcb_regtest_chain(®test_network).await; let mut faucet = sb.client_builder.build_faucet(false, regtest_network).await; - faucet.sync_and_await(false).await.unwrap(); + faucet.sync_and_await().await.unwrap(); let recipient = sb .client_builder .build_client(HOSPITAL_MUSEUM_SEED.to_string(), 0, false, regtest_network) diff --git a/zingolib/src/wallet/disk/testing/tests.rs b/zingolib/src/wallet/disk/testing/tests.rs index 869f7db79..b72741303 100644 --- a/zingolib/src/wallet/disk/testing/tests.rs +++ b/zingolib/src/wallet/disk/testing/tests.rs @@ -209,7 +209,7 @@ async fn loaded_wallet_assert( // ) // .await // .unwrap(); - lightclient.sync_and_await(true).await.unwrap(); + lightclient.sync_and_await().await.unwrap(); // FIXME: zingo2 // crate::testutils::lightclient::from_inputs::quick_send( // &lightclient, From ad8c34d2ab0db92407e1f96082990f0f544e9c4b Mon Sep 17 00:00:00 2001 From: Oscar Pepper Date: Mon, 24 Feb 2025 04:55:27 +0000 Subject: [PATCH 13/26] fix scenarios --- zingolib/src/testutils/scenarios.rs | 249 +++++++++++++--------------- 1 file changed, 118 insertions(+), 131 deletions(-) diff --git a/zingolib/src/testutils/scenarios.rs b/zingolib/src/testutils/scenarios.rs index fa3c648c0..8ff56f54c 100644 --- a/zingolib/src/testutils/scenarios.rs +++ b/zingolib/src/testutils/scenarios.rs @@ -640,7 +640,7 @@ pub async fn unfunded_mobileclient() -> (RegtestManager, ChildProcessHandler) { } /// TODO: Add Doc Comment Here! -pub async fn funded_orchard_mobileclient(_value: u64) -> (RegtestManager, ChildProcessHandler) { +pub async fn funded_orchard_mobileclient(value: u64) -> (RegtestManager, ChildProcessHandler) { let regtest_network = crate::config::RegtestNetwork::all_upgrades_active(); let mut scenario_builder = setup::ScenarioBuilder::build_configure_launch( Some(PoolType::Shielded(ShieldedProtocol::Sapling)), @@ -654,18 +654,17 @@ pub async fn funded_orchard_mobileclient(_value: u64) -> (RegtestManager, ChildP .client_builder .build_faucet(false, regtest_network) .await; - let _recipient = scenario_builder + let recipient = scenario_builder .client_builder .build_client(HOSPITAL_MUSEUM_SEED.to_string(), 0, false, regtest_network) .await; faucet.sync_and_await().await.unwrap(); - // FIXME: zingo2 - // super::lightclient::from_inputs::quick_send( - // &faucet, - // vec![(&get_base_address_macro!(recipient, "unified"), value, None)], - // ) - // .await - // .unwrap(); + super::lightclient::from_inputs::quick_send( + &faucet, + vec![(&get_base_address_macro!(recipient, "unified"), value, None)], + ) + .await + .unwrap(); scenario_builder .regtest_manager .generate_n_blocks(1) @@ -678,7 +677,7 @@ pub async fn funded_orchard_mobileclient(_value: u64) -> (RegtestManager, ChildP /// TODO: Add Doc Comment Here! pub async fn funded_orchard_with_3_txs_mobileclient( - _value: u64, + value: u64, ) -> (RegtestManager, ChildProcessHandler) { let regtest_network = crate::config::RegtestNetwork::all_upgrades_active(); let mut scenario_builder = setup::ScenarioBuilder::build_configure_launch( @@ -701,43 +700,40 @@ pub async fn funded_orchard_with_3_txs_mobileclient( .await .unwrap(); // received from a faucet - // FIXME: zingo2 - // super::lightclient::from_inputs::quick_send( - // &faucet, - // vec![(&get_base_address_macro!(recipient, "unified"), value, None)], - // ) - // .await - // .unwrap(); + super::lightclient::from_inputs::quick_send( + &faucet, + vec![(&get_base_address_macro!(recipient, "unified"), value, None)], + ) + .await + .unwrap(); increase_height_and_wait_for_client(&scenario_builder.regtest_manager, &mut recipient, 1) .await .unwrap(); // send to a faucet - // FIXME: zingo2 - // super::lightclient::from_inputs::quick_send( - // &recipient, - // vec![( - // &get_base_address_macro!(faucet, "unified"), - // value.checked_div(10).unwrap(), - // None, - // )], - // ) - // .await - // .unwrap(); + super::lightclient::from_inputs::quick_send( + &recipient, + vec![( + &get_base_address_macro!(faucet, "unified"), + value.checked_div(10).unwrap(), + None, + )], + ) + .await + .unwrap(); increase_height_and_wait_for_client(&scenario_builder.regtest_manager, &mut recipient, 1) .await .unwrap(); // send to self sapling - // FIXME: zingo2 - // super::lightclient::from_inputs::quick_send( - // &recipient, - // vec![( - // &get_base_address_macro!(recipient, "sapling"), - // value.checked_div(10).unwrap(), - // Some("note-to-self test memo"), - // )], - // ) - // .await - // .unwrap(); + super::lightclient::from_inputs::quick_send( + &recipient, + vec![( + &get_base_address_macro!(recipient, "sapling"), + value.checked_div(10).unwrap(), + Some("note-to-self test memo"), + )], + ) + .await + .unwrap(); scenario_builder .regtest_manager .generate_n_blocks(4) @@ -749,7 +745,7 @@ pub async fn funded_orchard_with_3_txs_mobileclient( } /// This scenario funds a client with transparent funds. -pub async fn funded_transparent_mobileclient(_value: u64) -> (RegtestManager, ChildProcessHandler) { +pub async fn funded_transparent_mobileclient(value: u64) -> (RegtestManager, ChildProcessHandler) { let regtest_network = crate::config::RegtestNetwork::all_upgrades_active(); let mut scenario_builder = setup::ScenarioBuilder::build_configure_launch( Some(PoolType::Shielded(ShieldedProtocol::Sapling)), @@ -772,17 +768,16 @@ pub async fn funded_transparent_mobileclient(_value: u64) -> (RegtestManager, Ch .unwrap(); // received from a faucet to transparent - // FIXME: zingo2 - // super::lightclient::from_inputs::quick_send( - // &faucet, - // vec![( - // &get_base_address_macro!(recipient, "transparent"), - // value.checked_div(4).unwrap(), - // None, - // )], - // ) - // .await - // .unwrap(); + super::lightclient::from_inputs::quick_send( + &faucet, + vec![( + &get_base_address_macro!(recipient, "transparent"), + value.checked_div(4).unwrap(), + None, + )], + ) + .await + .unwrap(); increase_height_and_wait_for_client(&scenario_builder.regtest_manager, &mut recipient, 1) .await .unwrap(); @@ -800,7 +795,7 @@ pub async fn funded_transparent_mobileclient(_value: u64) -> (RegtestManager, Ch /// TODO: Add Doc Comment Here! pub async fn funded_orchard_sapling_transparent_shielded_mobileclient( - _value: u64, + value: u64, ) -> (RegtestManager, ChildProcessHandler) { let regtest_network = crate::config::RegtestNetwork::all_upgrades_active(); let mut scenario_builder = setup::ScenarioBuilder::build_configure_launch( @@ -823,113 +818,105 @@ pub async fn funded_orchard_sapling_transparent_shielded_mobileclient( .await .unwrap(); // received from a faucet to orchard - // FIXME: zingo2 - // super::lightclient::from_inputs::quick_send( - // &faucet, - // vec![( - // &get_base_address_macro!(recipient, "unified"), - // value.checked_div(2).unwrap(), - // None, - // )], - // ) - // .await - // .unwrap(); + super::lightclient::from_inputs::quick_send( + &faucet, + vec![( + &get_base_address_macro!(recipient, "unified"), + value.checked_div(2).unwrap(), + None, + )], + ) + .await + .unwrap(); increase_height_and_wait_for_client(&scenario_builder.regtest_manager, &mut faucet, 1) .await .unwrap(); // received from a faucet to sapling - // FIXME: zingo2 - // super::lightclient::from_inputs::quick_send( - // &faucet, - // vec![( - // &get_base_address_macro!(recipient, "sapling"), - // value.checked_div(4).unwrap(), - // None, - // )], - // ) - // .await - // .unwrap(); + super::lightclient::from_inputs::quick_send( + &faucet, + vec![( + &get_base_address_macro!(recipient, "sapling"), + value.checked_div(4).unwrap(), + None, + )], + ) + .await + .unwrap(); increase_height_and_wait_for_client(&scenario_builder.regtest_manager, &mut faucet, 1) .await .unwrap(); // received from a faucet to transparent - // FIXME: zingo2 - // super::lightclient::from_inputs::quick_send( - // &faucet, - // vec![( - // &get_base_address_macro!(recipient, "transparent"), - // value.checked_div(4).unwrap(), - // None, - // )], - // ) - // .await - // .unwrap(); + super::lightclient::from_inputs::quick_send( + &faucet, + vec![( + &get_base_address_macro!(recipient, "transparent"), + value.checked_div(4).unwrap(), + None, + )], + ) + .await + .unwrap(); increase_height_and_wait_for_client(&scenario_builder.regtest_manager, &mut recipient, 1) .await .unwrap(); // send to a faucet - // FIXME: zingo2 - // super::lightclient::from_inputs::quick_send( - // &recipient, - // vec![( - // &get_base_address_macro!(faucet, "unified"), - // value.checked_div(10).unwrap(), - // None, - // )], - // ) - // .await - // .unwrap(); + super::lightclient::from_inputs::quick_send( + &recipient, + vec![( + &get_base_address_macro!(faucet, "unified"), + value.checked_div(10).unwrap(), + None, + )], + ) + .await + .unwrap(); increase_height_and_wait_for_client(&scenario_builder.regtest_manager, &mut recipient, 1) .await .unwrap(); // send to self orchard - // FIXME: zingo2 - // super::lightclient::from_inputs::quick_send( - // &recipient, - // vec![( - // &get_base_address_macro!(recipient, "unified"), - // value.checked_div(10).unwrap(), - // None, - // )], - // ) - // .await - // .unwrap(); + super::lightclient::from_inputs::quick_send( + &recipient, + vec![( + &get_base_address_macro!(recipient, "unified"), + value.checked_div(10).unwrap(), + None, + )], + ) + .await + .unwrap(); increase_height_and_wait_for_client(&scenario_builder.regtest_manager, &mut recipient, 1) .await .unwrap(); // send to self sapling - // FIXME: zingo2 - // super::lightclient::from_inputs::quick_send( - // &recipient, - // vec![( - // &get_base_address_macro!(recipient, "sapling"), - // value.checked_div(10).unwrap(), - // None, - // )], - // ) - // .await - // .unwrap(); + super::lightclient::from_inputs::quick_send( + &recipient, + vec![( + &get_base_address_macro!(recipient, "sapling"), + value.checked_div(10).unwrap(), + None, + )], + ) + .await + .unwrap(); increase_height_and_wait_for_client(&scenario_builder.regtest_manager, &mut recipient, 1) .await .unwrap(); // send to self transparent - // FIXME: zingo2 - // super::lightclient::from_inputs::quick_send( - // &recipient, - // vec![( - // &get_base_address_macro!(recipient, "transparent"), - // value.checked_div(10).unwrap(), - // None, - // )], - // ) - // .await - // .unwrap(); + super::lightclient::from_inputs::quick_send( + &recipient, + vec![( + &get_base_address_macro!(recipient, "transparent"), + value.checked_div(10).unwrap(), + None, + )], + ) + .await + .unwrap(); increase_height_and_wait_for_client(&scenario_builder.regtest_manager, &mut recipient, 1) .await .unwrap(); // shield transparent - // FIXME: zingo2 - // recipient.quick_shield().await.unwrap(); + recipient.quick_shield().await.unwrap(); increase_height_and_wait_for_client(&scenario_builder.regtest_manager, &mut recipient, 1) .await .unwrap(); From 8061a388f80b51349e83ee3d81c5d1141b74012b Mon Sep 17 00:00:00 2001 From: Oscar Pepper Date: Mon, 24 Feb 2025 10:45:13 +0000 Subject: [PATCH 14/26] complete wallet tx debug impl --- pepper-sync/src/wallet.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pepper-sync/src/wallet.rs b/pepper-sync/src/wallet.rs index c16de4496..5ab3c5cf5 100644 --- a/pepper-sync/src/wallet.rs +++ b/pepper-sync/src/wallet.rs @@ -620,7 +620,9 @@ impl WalletTransaction { impl std::fmt::Debug for WalletTransaction { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { f.debug_struct("WalletTransaction") + .field("txid", &self.txid) .field("confirmation_status", &self.status) + .field("datetime", &self.datetime) .field("transparent_coins", &self.transparent_coins) .field("sapling_notes", &self.sapling_notes) .field("orchard_notes", &self.orchard_notes) From c203a03bd9ac6c2cabb7346318de2165f205cce6 Mon Sep 17 00:00:00 2001 From: Oscar Pepper Date: Mon, 24 Feb 2025 11:55:56 +0000 Subject: [PATCH 15/26] fixed some chain generic framework --- libtonode-tests/tests/chain_generics.rs | 9 +- .../testutils/chain_generics/conduct_chain.rs | 31 +-- .../src/testutils/chain_generics/fixtures.rs | 259 +++++++++--------- .../chain_generics/with_assertions.rs | 111 ++++---- zingolib/src/wallet/output.rs | 17 +- 5 files changed, 218 insertions(+), 209 deletions(-) diff --git a/libtonode-tests/tests/chain_generics.rs b/libtonode-tests/tests/chain_generics.rs index 4c70e34b0..f203c19b8 100644 --- a/libtonode-tests/tests/chain_generics.rs +++ b/libtonode-tests/tests/chain_generics.rs @@ -42,11 +42,10 @@ mod chain_generics { async fn ignore_dust_inputs() { fixtures::ignore_dust_inputs::().await; } - // FIXME: zingo2 - // #[tokio::test] - // async fn note_selection_order() { - // fixtures::note_selection_order::().await; - // } + #[tokio::test] + async fn note_selection_order() { + fixtures::note_selection_order::().await; + } #[tokio::test] async fn simpool_insufficient_1_sapling_to_transparent() { fixtures::shpool_to_pool_insufficient_error::( diff --git a/zingolib/src/testutils/chain_generics/conduct_chain.rs b/zingolib/src/testutils/chain_generics/conduct_chain.rs index 9b42dc64a..6e9e5733a 100644 --- a/zingolib/src/testutils/chain_generics/conduct_chain.rs +++ b/zingolib/src/testutils/chain_generics/conduct_chain.rs @@ -4,8 +4,8 @@ //! darkside, a mode for the lightserver which mocks zcashd. search 'impl ConductChain for DarksideScenario use crate::config::ZingoConfig; -// use crate::get_base_address_macro; -// use crate::testutils::lightclient::from_inputs; +use crate::get_base_address_macro; +use crate::testutils::lightclient::from_inputs; use crate::{lightclient::LightClient, wallet::LightWallet}; #[allow(async_fn_in_trait)] @@ -55,29 +55,24 @@ pub trait ConductChain { /// and confirming transactions that were received by the server async fn bump_chain(&mut self); - // gets the height. does not yet need to be async - // fn get_chain_height(&mut self) -> u32; - // deprecated. use get_latest_block - /// builds a client and funds it in orchard and syncs it - async fn fund_client_orchard(&mut self, _value: u64) -> LightClient { + async fn fund_client_orchard(&mut self, value: u64) -> LightClient { let mut faucet = self.create_faucet().await; let mut recipient = self.create_client().await; self.bump_chain().await; faucet.sync_and_await().await.unwrap(); - // FIXME: zingo2 - // from_inputs::quick_send( - // &faucet, - // vec![( - // (get_base_address_macro!(recipient, "unified")).as_str(), - // value, - // None, - // )], - // ) - // .await - // .unwrap(); + from_inputs::quick_send( + &faucet, + vec![( + (get_base_address_macro!(recipient, "unified")).as_str(), + value, + None, + )], + ) + .await + .unwrap(); self.bump_chain().await; diff --git a/zingolib/src/testutils/chain_generics/fixtures.rs b/zingolib/src/testutils/chain_generics/fixtures.rs index bad8de1ef..8b57adfca 100644 --- a/zingolib/src/testutils/chain_generics/fixtures.rs +++ b/zingolib/src/testutils/chain_generics/fixtures.rs @@ -1,22 +1,19 @@ //! these functions are each meant to be 'test-in-a-box' //! simply plug in a mock server as a chain conductor and provide some values +use pepper_sync::wallet::SaplingNote; use zcash_client_backend::PoolType; -// use zcash_client_backend::PoolType::Shielded; -// use zcash_client_backend::PoolType::Transparent; use zcash_client_backend::ShieldedProtocol; -// use zcash_client_backend::ShieldedProtocol::Orchard; -// use zcash_client_backend::ShieldedProtocol::Sapling; -// use zcash_primitives::transaction::fees::zip317::MARGINAL_FEE; - -// use crate::wallet::data::summaries::SelfSendValueTransfer; -// use crate::wallet::data::summaries::SentValueTransfer; -// use crate::wallet::data::summaries::ValueTransferKind; +use zcash_primitives::transaction::fees::zip317::MARGINAL_FEE; use crate::testutils::chain_generics::conduct_chain::ConductChain; -// use crate::testutils::chain_generics::with_assertions; +use crate::testutils::chain_generics::with_assertions; use crate::testutils::fee_tables; use crate::testutils::lightclient::from_inputs; +use crate::testutils::lightclient::get_base_address; +use crate::wallet::output::query::OutputPoolQuery; +use crate::wallet::output::query::OutputQuery; +use crate::wallet::output::query::OutputSpendStatusQuery; /// Fixture for testing various vt transactions pub async fn create_various_value_transfers() @@ -215,123 +212,131 @@ where // ); } -// FIXME: zingo2 -// /// In order to fund a transaction multiple notes may be selected and consumed. -// /// The algorithm selects the smallest covering note(s). -// pub async fn note_selection_order() -// where -// CC: ConductChain, -// { -// // toDo: proptest different values for these first two variables -// let number_of_notes = 4; -// let expected_value_from_transaction_2: u64 = 40_000; - -// let transaction_1_values = (1..=number_of_notes).map(|n| n * 10_000); - -// let expected_fee_for_transaction_1 = (number_of_notes + 2) * MARGINAL_FEE.into_u64(); -// let expected_value_from_transaction_1: u64 = transaction_1_values.clone().sum(); - -// let mut environment = CC::setup().await; -// let primary = environment -// .fund_client_orchard(expected_fee_for_transaction_1 + expected_value_from_transaction_1) -// .await; -// let secondary = environment.create_client().await; - -// // Send number_of_notes transfers in increasing 10_000 zat increments -// let (recorded_fee, recorded_value, recorded_change) = -// with_assertions::propose_send_bump_sync_all_recipients( -// &mut environment, -// &primary, -// transaction_1_values -// .map(|value| (&secondary, Shielded(Sapling), value, None)) -// .collect(), -// false, -// ) -// .await -// .unwrap(); -// assert_eq!( -// (recorded_fee, recorded_value, recorded_change), -// ( -// expected_fee_for_transaction_1, -// recorded_value, -// recorded_change -// ) -// ); - -// let expected_orchard_contribution_for_transaction_2 = 2; - -// // calculate what will be spent -// let mut expected_highest_unselected: i64 = 10_000 * number_of_notes as i64; -// let mut expected_inputs_for_transaction_2 = 0; -// let mut max_unselected_value_for_transaction_2: i64 = (expected_value_from_transaction_2 -// + expected_orchard_contribution_for_transaction_2) -// as i64; -// loop { -// // add an input -// expected_inputs_for_transaction_2 += 1; -// max_unselected_value_for_transaction_2 += MARGINAL_FEE.into_u64() as i64; -// max_unselected_value_for_transaction_2 -= expected_highest_unselected; -// expected_highest_unselected -= 10_000; - -// if max_unselected_value_for_transaction_2 <= 0 { -// // met target -// break; -// } -// if expected_highest_unselected <= 0 { -// // did not meet target. expect error on send -// break; -// } -// } -// let expected_fee_for_transaction_2 = (expected_inputs_for_transaction_2 -// + expected_orchard_contribution_for_transaction_2) -// * MARGINAL_FEE.into_u64(); - -// // the second client selects notes to cover the transaction. -// let (recorded_fee, recorded_value, recorded_change) = -// with_assertions::propose_send_bump_sync_all_recipients( -// &mut environment, -// &secondary, -// vec![( -// &primary, -// Shielded(Orchard), -// expected_value_from_transaction_2, -// None, -// )], -// false, -// ) -// .await -// .unwrap(); -// assert_eq!( -// (recorded_fee, recorded_value, recorded_change), -// ( -// expected_fee_for_transaction_2, -// expected_value_from_transaction_2, -// 0 -// ) -// ); - -// let received_change_from_transaction_2 = secondary -// .wallet -// .lock() -// .await -// .sum_queried_output_values(OutputQuery { -// spend_status: OutputSpendStatusQuery::only_unspent(), -// pools: OutputPoolQuery::one_pool(Shielded(Orchard)), -// }); -// // if 10_000 or more change, would have used a smaller note -// assert!(received_change_from_transaction_2 < 10_000); - -// let all_outputs = secondary.list_outputs().await; -// let spent_sapling_outputs: Vec<_> = all_outputs -// .iter() -// .filter(|o| matches!(o.pool_type(), Shielded(Sapling))) -// .filter(|o| o.is_spent_confirmed()) -// .collect(); -// assert_eq!( -// spent_sapling_outputs.len(), -// expected_inputs_for_transaction_2 as usize -// ); -// } +/// In order to fund a transaction multiple notes may be selected and consumed. +/// The algorithm selects the smallest covering note(s). +pub async fn note_selection_order() +where + CC: ConductChain, +{ + // toDo: proptest different values for these first two variables + let number_of_notes = 4; + let expected_value_from_transaction_2: u64 = 40_000; + + let transaction_1_values = (1..=number_of_notes).map(|n| n * 10_000); + + let expected_fee_for_transaction_1 = (number_of_notes + 2) * MARGINAL_FEE.into_u64(); + let expected_value_from_transaction_1: u64 = transaction_1_values.clone().sum(); + + let mut environment = CC::setup().await; + let mut primary = environment + .fund_client_orchard(expected_fee_for_transaction_1 + expected_value_from_transaction_1) + .await; + let mut secondary = environment.create_client().await; + + // Send number_of_notes transfers in increasing 10_000 zat increments + let secondary_sapling_addr = + get_base_address(&secondary, PoolType::Shielded(ShieldedProtocol::Sapling)).await; + let (recorded_fee, recorded_value, recorded_change) = + with_assertions::propose_send_bump_sync_all_recipients( + &mut environment, + &mut primary, + transaction_1_values + .map(|value| (secondary_sapling_addr.as_str(), value, None)) + .collect(), + vec![&mut secondary], + false, + ) + .await + .unwrap(); + assert_eq!( + (recorded_fee, recorded_value, recorded_change), + ( + expected_fee_for_transaction_1, + recorded_value, + recorded_change + ) + ); + + let expected_orchard_contribution_for_transaction_2 = 2; + + // calculate what will be spent + let mut expected_highest_unselected: i64 = 10_000 * number_of_notes as i64; + let mut expected_inputs_for_transaction_2 = 0; + let mut max_unselected_value_for_transaction_2: i64 = (expected_value_from_transaction_2 + + expected_orchard_contribution_for_transaction_2) + as i64; + loop { + // add an input + expected_inputs_for_transaction_2 += 1; + max_unselected_value_for_transaction_2 += MARGINAL_FEE.into_u64() as i64; + max_unselected_value_for_transaction_2 -= expected_highest_unselected; + expected_highest_unselected -= 10_000; + + if max_unselected_value_for_transaction_2 <= 0 { + // met target + break; + } + if expected_highest_unselected <= 0 { + // did not meet target. expect error on send + break; + } + } + let expected_fee_for_transaction_2 = (expected_inputs_for_transaction_2 + + expected_orchard_contribution_for_transaction_2) + * MARGINAL_FEE.into_u64(); + + // the second client selects notes to cover the transaction. + let primary_orchard_addr = + get_base_address(&primary, PoolType::Shielded(ShieldedProtocol::Orchard)).await; + let (recorded_fee, recorded_value, recorded_change) = + with_assertions::propose_send_bump_sync_all_recipients( + &mut environment, + &mut secondary, + vec![( + primary_orchard_addr.as_str(), + expected_value_from_transaction_2, + None, + )], + vec![&mut primary], + false, + ) + .await + .unwrap(); + assert_eq!( + (recorded_fee, recorded_value, recorded_change), + ( + expected_fee_for_transaction_2, + expected_value_from_transaction_2, + 0 + ) + ); + + let received_change_from_transaction_2 = secondary + .wallet + .lock() + .await + .sum_queried_output_values(OutputQuery { + spend_status: OutputSpendStatusQuery::only_unspent(), + pools: OutputPoolQuery::one_pool(PoolType::Shielded(ShieldedProtocol::Orchard)), + }); + // if 10_000 or more change, would have used a smaller note + assert!(received_change_from_transaction_2 < 10_000); + + let secondary_wallet = secondary.wallet.lock().await; + let spent_sapling_outputs = secondary_wallet + .wallet_outputs::() + .into_iter() + .filter(|&output| { + secondary_wallet + .output_spend_status(output) + .is_confirmed_spent() + }) + .collect::>(); + assert_eq!( + spent_sapling_outputs.len(), + expected_inputs_for_transaction_2 as usize + ); +} /// the simplest test that sends from a specific shielded pool to another specific pool. error variant. pub async fn shpool_to_pool_insufficient_error( diff --git a/zingolib/src/testutils/chain_generics/with_assertions.rs b/zingolib/src/testutils/chain_generics/with_assertions.rs index 07cc36f94..a07b34598 100644 --- a/zingolib/src/testutils/chain_generics/with_assertions.rs +++ b/zingolib/src/testutils/chain_generics/with_assertions.rs @@ -4,75 +4,70 @@ use crate::lightclient::LightClient; use crate::testutils::assertions::compare_fee; use crate::testutils::assertions::for_each_proposed_transaction; use crate::testutils::chain_generics::conduct_chain::ConductChain; -// use crate::testutils::lightclient::from_inputs; -// use crate::testutils::lightclient::get_base_address; +use crate::testutils::lightclient::from_inputs; +use crate::testutils::lightclient::get_base_address; use crate::wallet::output::query::OutputQuery; use nonempty::NonEmpty; use zcash_client_backend::proposal::Proposal; -// use zcash_client_backend::PoolType; +use zcash_client_backend::PoolType; use zcash_primitives::consensus::BlockHeight; use zcash_primitives::transaction::TxId; use zingo_status::confirmation_status::ConfirmationStatus; -// FIXME: zingo2 -// /// this function handles inputs and their lifetimes to create a proposal -// pub async fn to_clients_proposal( -// sender: &LightClient, -// sends: &[(&LightClient, PoolType, u64, Option<&str>)], -// ) -> zcash_client_backend::proposal::Proposal< -// zcash_primitives::transaction::fees::zip317::FeeRule, -// zcash_client_backend::wallet::NoteId, -// > { -// let mut subraw_receivers = vec![]; -// for (recipient, pooltype, amount, memo_str) in sends { -// let address = get_base_address(recipient, *pooltype).await; -// subraw_receivers.push((address, amount, memo_str)); -// } - -// let raw_receivers = subraw_receivers -// .iter() -// .map(|(address, amount, opt_memo)| (address.as_str(), **amount, **opt_memo)) -// .collect(); +/// this function handles inputs and their lifetimes to create a proposal +pub async fn to_clients_proposal( + sender: &LightClient, + sends: &[(&LightClient, PoolType, u64, Option<&str>)], +) -> zcash_client_backend::proposal::Proposal< + zcash_primitives::transaction::fees::zip317::FeeRule, + zcash_client_backend::wallet::NoteId, +> { + let mut subraw_receivers = vec![]; + for (recipient, pooltype, amount, memo_str) in sends { + let address = get_base_address(recipient, *pooltype).await; + subraw_receivers.push((address, amount, memo_str)); + } -// from_inputs::propose(sender, raw_receivers).await.unwrap() -// } + let raw_receivers = subraw_receivers + .iter() + .map(|(address, amount, opt_memo)| (address.as_str(), **amount, **opt_memo)) + .collect(); -// FIXME: zingo2 -// /// sends to any combo of recipient clients checks that each recipient also received the expected balances -// /// test-only generic -// /// NOTICE this function bumps the chain and syncs the client -// /// test_mempool can be enabled when the test harness supports it -// /// returns Ok(total_fee, total_received, total_change) -// pub async fn propose_send_bump_sync_all_recipients( -// environment: &mut CC, -// sender: &LightClient, -// sends: Vec<(&LightClient, PoolType, u64, Option<&str>)>, -// test_mempool: bool, -// ) -> Result<(u64, u64, u64), String> -// where -// CC: ConductChain, -// { -// sender.do_sync(false).await.unwrap(); -// let proposal = to_clients_proposal(sender, &sends).await; + from_inputs::propose(sender, raw_receivers).await.unwrap() +} -// let txids = sender -// .complete_and_broadcast_stored_proposal() -// .await -// .unwrap(); +/// sends to any combo of recipient clients checks that each recipient also received the expected balances +/// test-only generic +/// NOTICE this function bumps the chain and syncs the client +/// test_mempool can be enabled when the test harness supports it +/// returns Ok(total_fee, total_received, total_change) +pub async fn propose_send_bump_sync_all_recipients( + environment: &mut CC, + sender: &mut LightClient, + payments: Vec<(&str, u64, Option<&str>)>, + recipients: Vec<&mut LightClient>, + test_mempool: bool, +) -> Result<(u64, u64, u64), String> +where + CC: ConductChain, +{ + sender.sync_and_await().await.unwrap(); + let proposal = from_inputs::propose(sender, payments).await.unwrap(); + let txids = sender + .complete_and_broadcast_stored_proposal() + .await + .unwrap(); -// follow_proposal( -// environment, -// sender, -// sends -// .iter() -// .map(|(recipient, _, _, _)| *recipient) -// .collect(), -// &proposal, -// txids, -// test_mempool, -// ) -// .await -// } + follow_proposal( + environment, + sender, + recipients, + &proposal, + txids, + test_mempool, + ) + .await +} // FIXME: zingo2 // /// a test-only generic version of shield that includes assertions that the proposal was fulfilled diff --git a/zingolib/src/wallet/output.rs b/zingolib/src/wallet/output.rs index 4883695f0..641374855 100644 --- a/zingolib/src/wallet/output.rs +++ b/zingolib/src/wallet/output.rs @@ -51,6 +51,21 @@ pub enum SpendStatus { Spent(TxId), } +impl SpendStatus { + pub fn is_unspent(&self) -> bool { + matches!(self, Self::Unspent) + } + + pub fn is_pending_spent(&self) -> bool { + matches!(self, Self::CalculatedSpent(_)) + || matches!(self, Self::TransmittedSpent(_)) + || matches!(self, Self::MempoolSpent(_)) + } + pub fn is_confirmed_spent(&self) -> bool { + matches!(self, Self::Spent(_)) + } +} + impl std::fmt::Display for SpendStatus { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -91,7 +106,7 @@ impl LightWallet { } /// Gets all outputs of a given type in the wallet. - pub(super) fn wallet_outputs(&self) -> Vec<&Op> { + pub fn wallet_outputs(&self) -> Vec<&Op> { self.wallet_transactions .values() .flat_map(|transaction| Op::transaction_outputs(transaction)) From da65a1ac50bafd504ab6303326a941bfa7031eb5 Mon Sep 17 00:00:00 2001 From: Oscar Pepper Date: Mon, 24 Feb 2025 12:39:48 +0000 Subject: [PATCH 16/26] fixed lightclient send tests --- zingolib/src/lightclient/send.rs | 390 ++++++++---------- .../chain_generics/with_assertions.rs | 52 +-- 2 files changed, 205 insertions(+), 237 deletions(-) diff --git a/zingolib/src/lightclient/send.rs b/zingolib/src/lightclient/send.rs index b7fa5de3c..17134a686 100644 --- a/zingolib/src/lightclient/send.rs +++ b/zingolib/src/lightclient/send.rs @@ -172,224 +172,200 @@ pub mod send_with_proposal { #[cfg(test)] mod test { - // use zcash_client_backend::PoolType; - - // use crate::{ - // lightclient::sync::test::sync_example_wallet, - // testutils::chain_generics::{ - // conduct_chain::ConductChain as _, live_chain::LiveChain, with_assertions, - // }, - // wallet::disk::testing::examples, - // }; - - // all tests below (and in this mod) use example wallets, which describe real-world chains. - - // FIXME: zingo2 - // #[tokio::test] - // async fn complete_and_broadcast_unconnected_error() { - // use crate::{ - // config::ZingoConfigBuilder, lightclient::LightClient, - // mocks::proposal::ProposalBuilder, - // }; - // use testvectors::seeds::ABANDON_ART_SEED; - // let lc = LightClient::create_unconnected( - // &ZingoConfigBuilder::default().create(), - // crate::wallet::WalletBase::MnemonicPhrase(ABANDON_ART_SEED.to_string()), - // 1, - // ) - // .await - // .unwrap(); - // let proposal = ProposalBuilder::default().build(); - // lc.complete_and_broadcast(&proposal).await.unwrap_err(); - // // TODO: match on specific error - // } + //! all tests below (and in this mod) use example wallets, which describe real-world chains. + + use zcash_client_backend::PoolType; + + use crate::{ + lightclient::sync::test::sync_example_wallet, + testutils::chain_generics::{ + conduct_chain::ConductChain as _, live_chain::LiveChain, with_assertions, + }, + wallet::disk::testing::examples, + }; + + #[tokio::test] + async fn complete_and_broadcast_unconnected_error() { + use crate::{ + config::ZingoConfigBuilder, lightclient::LightClient, + mocks::proposal::ProposalBuilder, + }; + use testvectors::seeds::ABANDON_ART_SEED; + let lc = LightClient::create_unconnected( + &ZingoConfigBuilder::default().create(), + crate::wallet::WalletBase::MnemonicPhrase(ABANDON_ART_SEED.to_string()), + 1, + ) + .await + .unwrap(); + let proposal = ProposalBuilder::default().build(); + lc.complete_and_broadcast(&proposal).await.unwrap_err(); + // TODO: match on specific error + } /// live sync: execution time increases linearly until example wallet is upgraded /// live send TESTNET: these assume the wallet has on-chain TAZ. /// - waits 150 seconds for confirmation per transaction. see [zingolib/src/testutils/chain_generics/live_chain.rs] mod testnet { - // use super::*; - - // FIXME: zingo2 - // /// requires 1 confirmation: expect 3 minute runtime - // #[ignore = "live testnet: testnet relies on NU6"] - // #[tokio::test] - // async fn glory_goddess_simple_send() { - // let case = examples::NetworkSeedVersion::Testnet( - // examples::TestnetSeedVersion::GloryGoddess, - // ); - // let client = sync_example_wallet(case).await; - - // with_assertions::assure_propose_shield_bump_sync( - // &mut LiveChain::setup().await, - // &client, - // true, - // ) - // .await - // .unwrap(); - // } - - // FIXME: zingo2 - // #[ignore = "live testnet: testnet relies on NU6"] - // #[tokio::test] - // /// this is a live sync test. its execution time scales linearly since last updated - // /// this is a live send test. whether it can work depends on the state of live wallet on the blockchain - // /// note: live send waits 2 minutes for confirmation. expect 3min runtime - // async fn testnet_send_to_self_orchard() { - // let case = examples::NetworkSeedVersion::Testnet( - // examples::TestnetSeedVersion::ChimneyBetter( - // examples::ChimneyBetterVersion::Latest, - // ), - // ); - - // let client = sync_example_wallet(case).await; - - // with_assertions::propose_send_bump_sync_all_recipients( - // &mut LiveChain::setup().await, - // &client, - // vec![( - // &client, - // PoolType::Shielded(zcash_client_backend::ShieldedProtocol::Orchard), - // 10_000, - // None, - // )], - // false, - // ) - // .await - // .unwrap(); - // } - - // FIXME: zingo2 - // #[ignore = "live testnet: testnet relies on NU6"] - // #[tokio::test] - // /// this is a live sync test. its execution time scales linearly since last updated - // /// note: live send waits 2 minutes for confirmation. expect 3min runtime - // async fn testnet_shield() { - // let case = examples::NetworkSeedVersion::Testnet( - // examples::TestnetSeedVersion::ChimneyBetter( - // examples::ChimneyBetterVersion::Latest, - // ), - // ); - - // let client = sync_example_wallet(case).await; - - // with_assertions::assure_propose_shield_bump_sync( - // &mut LiveChain::setup().await, - // &client, - // true, - // ) - // .await - // .unwrap(); - // } + use zcash_client_backend::ShieldedProtocol; + + use crate::testutils::lightclient::get_base_address; + + use super::*; + + /// requires 1 confirmation: expect 3 minute runtime + #[ignore = "live testnet: testnet relies on NU6"] + #[tokio::test] + async fn glory_goddess_simple_send() { + let case = examples::NetworkSeedVersion::Testnet( + examples::TestnetSeedVersion::GloryGoddess, + ); + let mut client = sync_example_wallet(case).await; + + with_assertions::assure_propose_shield_bump_sync( + &mut LiveChain::setup().await, + &mut client, + true, + ) + .await + .unwrap(); + } + + #[ignore = "live testnet: testnet relies on NU6"] + #[tokio::test] + /// this is a live sync test. its execution time scales linearly since last updated + /// this is a live send test. whether it can work depends on the state of live wallet on the blockchain + /// note: live send waits 2 minutes for confirmation. expect 3min runtime + async fn testnet_send_to_self_orchard() { + let case = examples::NetworkSeedVersion::Testnet( + examples::TestnetSeedVersion::ChimneyBetter( + examples::ChimneyBetterVersion::Latest, + ), + ); + + let mut client = sync_example_wallet(case).await; + let client_addr = + get_base_address(&client, PoolType::Shielded(ShieldedProtocol::Orchard)).await; + + with_assertions::propose_send_bump_sync_all_recipients( + &mut LiveChain::setup().await, + &mut client, + vec![(&client_addr, 10_000, None)], + vec![], + false, + ) + .await + .unwrap(); + } + + #[ignore = "live testnet: testnet relies on NU6"] + #[tokio::test] + /// this is a live sync test. its execution time scales linearly since last updated + /// note: live send waits 2 minutes for confirmation. expect 3min runtime + async fn testnet_shield() { + let case = examples::NetworkSeedVersion::Testnet( + examples::TestnetSeedVersion::ChimneyBetter( + examples::ChimneyBetterVersion::Latest, + ), + ); + + let mut client = sync_example_wallet(case).await; + + with_assertions::assure_propose_shield_bump_sync( + &mut LiveChain::setup().await, + &mut client, + true, + ) + .await + .unwrap(); + } } /// live sync: execution time increases linearly until example wallet is upgraded /// live send MAINNET: spends on-chain ZEC. /// - waits 150 seconds for confirmation per transaction. see [zingolib/src/testutils/chain_generics/live_chain.rs] mod mainnet { - // use super::*; - - // FIXME: zingo2 - // /// requires 1 confirmation: expect 3 minute runtime - // #[tokio::test] - // #[ignore = "dont automatically run hot tests! this test spends actual zec!"] - // async fn mainnet_send_to_self_orchard() { - // let case = examples::NetworkSeedVersion::Mainnet( - // examples::MainnetSeedVersion::HotelHumor(examples::HotelHumorVersion::Latest), - // ); - // let target_pool = PoolType::Shielded(ShieldedProtocol::Orchard); - - // let client = sync_example_wallet(case).await; - - // println!( - // "mainnet_hhcclaltpcckcsslpcnetblr has {} transactions in it", - // client - // .wallet - // .lock() - // .await - // .transaction_context - // .transaction_metadata_set - // .read() - // .await - // .transaction_records_by_id - // .len() - // ); - - // with_assertions::propose_send_bump_sync_all_recipients( - // &mut LiveChain::setup().await, - // &client, - // vec![(&client, target_pool, 10_000, None)], - // false, - // ) - // .await - // .unwrap(); - // } - - // FIXME: zingo2 - // /// requires 1 confirmation: expect 3 minute runtime - // #[tokio::test] - // #[ignore = "dont automatically run hot tests! this test spends actual zec!"] - // async fn mainnet_send_to_self_sapling() { - // let case = examples::NetworkSeedVersion::Mainnet( - // examples::MainnetSeedVersion::HotelHumor(examples::HotelHumorVersion::Latest), - // ); - // let target_pool = PoolType::Shielded(ShieldedProtocol::Sapling); - - // let client = sync_example_wallet(case).await; - - // println!( - // "mainnet_hhcclaltpcckcsslpcnetblr has {} transactions in it", - // client - // .wallet - // .lock() - // .await - // .transaction_context - // .transaction_metadata_set - // .read() - // .await - // .transaction_records_by_id - // .len() - // ); - - // with_assertions::propose_send_bump_sync_all_recipients( - // &mut LiveChain::setup().await, - // &client, - // vec![(&client, target_pool, 400_000, None)], - // false, - // ) - // .await - // .unwrap(); - // } - - // FIXME: zingo2 - // /// requires 2 confirmations: expect 6 minute runtime - // #[tokio::test] - // #[ignore = "dont automatically run hot tests! this test spends actual zec!"] - // async fn mainnet_send_to_self_transparent_and_then_shield() { - // let case = examples::NetworkSeedVersion::Mainnet( - // examples::MainnetSeedVersion::HotelHumor(examples::HotelHumorVersion::Latest), - // ); - // let target_pool = PoolType::Transparent; - - // let client = sync_example_wallet(case).await; - - // with_assertions::propose_send_bump_sync_all_recipients( - // &mut LiveChain::setup().await, - // &client, - // vec![(&client, target_pool, 400_000, None)], - // false, - // ) - // .await - // .unwrap(); - - // with_assertions::assure_propose_shield_bump_sync( - // &mut LiveChain::setup().await, - // &client, - // false, - // ) - // .await - // .unwrap(); - // } + use zcash_client_backend::ShieldedProtocol; + + use crate::testutils::lightclient::get_base_address; + + use super::*; + + /// requires 1 confirmation: expect 3 minute runtime + #[tokio::test] + #[ignore = "dont automatically run hot tests! this test spends actual zec!"] + async fn mainnet_send_to_self_orchard() { + let case = examples::NetworkSeedVersion::Mainnet( + examples::MainnetSeedVersion::HotelHumor(examples::HotelHumorVersion::Latest), + ); + + let mut client = sync_example_wallet(case).await; + let client_addr = + get_base_address(&client, PoolType::Shielded(ShieldedProtocol::Orchard)).await; + + with_assertions::propose_send_bump_sync_all_recipients( + &mut LiveChain::setup().await, + &mut client, + vec![(&client_addr, 10_000, None)], + vec![], + false, + ) + .await + .unwrap(); + } + + /// requires 1 confirmation: expect 3 minute runtime + #[tokio::test] + #[ignore = "dont automatically run hot tests! this test spends actual zec!"] + async fn mainnet_send_to_self_sapling() { + let case = examples::NetworkSeedVersion::Mainnet( + examples::MainnetSeedVersion::HotelHumor(examples::HotelHumorVersion::Latest), + ); + + let mut client = sync_example_wallet(case).await; + let client_addr = + get_base_address(&client, PoolType::Shielded(ShieldedProtocol::Sapling)).await; + + with_assertions::propose_send_bump_sync_all_recipients( + &mut LiveChain::setup().await, + &mut client, + vec![(&client_addr, 400_000, None)], + vec![], + false, + ) + .await + .unwrap(); + } + + /// requires 2 confirmations: expect 6 minute runtime + #[tokio::test] + #[ignore = "dont automatically run hot tests! this test spends actual zec!"] + async fn mainnet_send_to_self_transparent_and_then_shield() { + let case = examples::NetworkSeedVersion::Mainnet( + examples::MainnetSeedVersion::HotelHumor(examples::HotelHumorVersion::Latest), + ); + + let mut client = sync_example_wallet(case).await; + let client_addr = get_base_address(&client, PoolType::Transparent).await; + + with_assertions::propose_send_bump_sync_all_recipients( + &mut LiveChain::setup().await, + &mut client, + vec![(&client_addr, 400_000, None)], + vec![], + false, + ) + .await + .unwrap(); + + with_assertions::assure_propose_shield_bump_sync( + &mut LiveChain::setup().await, + &mut client, + false, + ) + .await + .unwrap(); + } } } } diff --git a/zingolib/src/testutils/chain_generics/with_assertions.rs b/zingolib/src/testutils/chain_generics/with_assertions.rs index a07b34598..fdc1ad757 100644 --- a/zingolib/src/testutils/chain_generics/with_assertions.rs +++ b/zingolib/src/testutils/chain_generics/with_assertions.rs @@ -69,38 +69,30 @@ where .await } -// FIXME: zingo2 -// /// a test-only generic version of shield that includes assertions that the proposal was fulfilled -// /// NOTICE this function bumps the chain and syncs the client -// /// only compatible with zip317 -// /// returns Ok(total_fee, total_shielded) -// pub async fn assure_propose_shield_bump_sync( -// environment: &mut CC, -// client: &LightClient, -// test_mempool: bool, -// ) -> Result<(u64, u64), String> -// where -// CC: ConductChain, -// { -// let proposal = client.propose_shield().await.map_err(|e| e.to_string())?; +/// a test-only generic version of shield that includes assertions that the proposal was fulfilled +/// NOTICE this function bumps the chain and syncs the client +/// only compatible with zip317 +/// returns Ok(total_fee, total_shielded) +pub async fn assure_propose_shield_bump_sync( + environment: &mut CC, + client: &mut LightClient, + test_mempool: bool, +) -> Result<(u64, u64), String> +where + CC: ConductChain, +{ + let proposal = client.propose_shield().await.map_err(|e| e.to_string())?; -// let txids = client -// .complete_and_broadcast_stored_proposal() -// .await -// .unwrap(); + let txids = client + .complete_and_broadcast_stored_proposal() + .await + .unwrap(); -// let (total_fee, r_shielded, s_shielded) = follow_proposal( -// environment, -// client, -// vec![client], -// &proposal, -// txids, -// test_mempool, -// ) -// .await?; -// assert_eq!(r_shielded, s_shielded); -// Ok((total_fee, s_shielded)) -// } + let (total_fee, r_shielded, s_shielded) = + follow_proposal(environment, client, vec![], &proposal, txids, test_mempool).await?; + assert_eq!(r_shielded, s_shielded); + Ok((total_fee, s_shielded)) +} /// given a just-broadcast proposal, confirms that it achieves all expected checkpoints. /// returns Ok(total_fee, total_received, total_change) From ec2e083f9a1b55075ae24f500ecda73be86c0d44 Mon Sep 17 00:00:00 2001 From: Oscar Pepper Date: Mon, 24 Feb 2025 13:01:48 +0000 Subject: [PATCH 17/26] removed kruft and deprecations features --- libtonode-tests/Cargo.toml | 2 +- zingocli/Cargo.toml | 2 +- zingolib/Cargo.toml | 2 - zingolib/src/lightclient.rs | 24 -- zingolib/src/lightclient/deprecated.rs | 111 ------ zingolib/src/lightclient/propose.rs | 35 +- zingolib/src/testutils.rs | 2 +- zingolib/src/wallet/data.rs | 334 ------------------ zingolib/src/wallet/disk/testing/tests.rs | 42 ++- zingolib/src/wallet/propose.rs | 13 +- zingolib/src/wallet/transaction_record.rs | 61 ---- .../src/wallet/transaction_records_by_id.rs | 125 +------ 12 files changed, 52 insertions(+), 701 deletions(-) delete mode 100644 zingolib/src/lightclient/deprecated.rs diff --git a/libtonode-tests/Cargo.toml b/libtonode-tests/Cargo.toml index 926764063..ae029c30a 100644 --- a/libtonode-tests/Cargo.toml +++ b/libtonode-tests/Cargo.toml @@ -9,7 +9,7 @@ ci = ["zingolib/ci"] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -zingolib = { path = "../zingolib", features = [ "deprecations", "test-elevation" ] } +zingolib = { path = "../zingolib", features = [ "test-elevation" ] } zingo-status = { path = "../zingo-status" } zingo-netutils = { path = "../zingo-netutils" } pepper-sync = { path = "../pepper-sync" } diff --git a/zingocli/Cargo.toml b/zingocli/Cargo.toml index a3af13608..7d01ef5a4 100644 --- a/zingocli/Cargo.toml +++ b/zingocli/Cargo.toml @@ -4,7 +4,7 @@ version = "0.2.0" edition = "2021" [dependencies] -zingolib = { path = "../zingolib/", features = ["deprecations", "test-elevation"] } +zingolib = { path = "../zingolib/", features = [ "test-elevation" ] } clap = { workspace = true } http = { workspace = true } diff --git a/zingolib/Cargo.toml b/zingolib/Cargo.toml index 4467c3c7c..249114baf 100644 --- a/zingolib/Cargo.toml +++ b/zingolib/Cargo.toml @@ -9,8 +9,6 @@ edition = "2021" [features] ci = [] darkside_tests = [] -deprecations = ["lightclient-deprecated"] -lightclient-deprecated = [] test-elevation = ["portpicker", "testvectors", "tempfile", "tempdir"] zaino-test = ['test-elevation'] diff --git a/zingolib/src/lightclient.rs b/zingolib/src/lightclient.rs index 4e4eb7216..612ffbce8 100644 --- a/zingolib/src/lightclient.rs +++ b/zingolib/src/lightclient.rs @@ -502,28 +502,6 @@ impl LightClient { } } } - - /// This function sorts notes into - /// unspent - /// spend_is_pending - /// spend_is_confirmed - // FIXME: zingo2 - #[allow(dead_code)] - fn unspent_pending_spent( - &self, - note: JsonValue, - unspent: &mut Vec, - spend_is_pending: &mut Vec, - spend_is_confirmed: &mut Vec, - ) { - if note["spent"].is_null() && note["pending_spent"].is_null() { - unspent.push(note); - } else if !note["spent"].is_null() { - spend_is_confirmed.push(note); - } else { - spend_is_pending.push(note); - } - } } use serde_json::Value; @@ -692,5 +670,3 @@ mod tests { } } -#[cfg(feature = "lightclient-deprecated")] -mod deprecated; diff --git a/zingolib/src/lightclient/deprecated.rs b/zingolib/src/lightclient/deprecated.rs deleted file mode 100644 index 3ea78ae0a..000000000 --- a/zingolib/src/lightclient/deprecated.rs +++ /dev/null @@ -1,111 +0,0 @@ -// use crate::wallet::transaction_record::TransactionRecord; - -use super::*; -// use crate::wallet::notes::OutputInterface; -// use crate::wallet::notes::ShieldedNoteInterface; -// use zcash_note_encryption::Domain; - -impl LightClient { - // FIXME: sync integration - // fn add_nonchange_notes<'a, 'b, 'c>( - // &'a self, - // transaction_metadata: &'b TransactionRecord, - // unified_spend_auth: &'c crate::wallet::keys::unified::WalletCapability, - // ) -> impl Iterator + 'b - // where - // 'a: 'b, - // 'c: 'b, - // { - // self.add_wallet_notes_in_transaction_to_list_inner::<'a, 'b, 'c, sapling_crypto::note_encryption::SaplingDomain>( - // transaction_metadata, - // unified_spend_auth, - // ) - // .chain( - // self.add_wallet_notes_in_transaction_to_list_inner::<'a, 'b, 'c, orchard::note_encryption::OrchardDomain>( - // transaction_metadata, - // unified_spend_auth, - // ), - // ) - // } - - // fn add_wallet_notes_in_transaction_to_list_inner<'a, 'b, 'c, D>( - // &'a self, - // transaction_metadata: &'b TransactionRecord, - // unified_spend_auth: &'c crate::wallet::keys::unified::WalletCapability, - // ) -> impl Iterator + 'b - // where - // 'a: 'b, - // 'c: 'b, - // D: crate::wallet::traits::DomainWalletExt, - // D::WalletNote: 'b, - // ::Recipient: crate::wallet::traits::Recipient, - // ::Note: PartialEq + Clone, - // { - // D::WalletNote::transaction_metadata_notes(transaction_metadata).iter().filter(|nd| !nd.is_change()).enumerate().map(|(i, nd)| { - // let block_height: u32 = transaction_metadata.status.get_height().into(); - // object! { - // "block_height" => block_height, - // "pending" => !transaction_metadata.status.is_confirmed(), - // "datetime" => transaction_metadata.datetime, - // "position" => i, - // "txid" => format!("{}", transaction_metadata.txid), - // "amount" => nd.value() as i64, - // "zec_price" => transaction_metadata.price.map(|p| (p * 100.0).round() / 100.0), - // "address" => LightWallet::note_address::(&self.config.chain, nd, unified_spend_auth), - // "memo" => LightWallet::memo_str(nd.memo().clone()) - // } - - // }) - // } - - // #[allow(deprecated)] - // fn append_change_notes( - // wallet_transaction: &TransactionRecord, - // received_utxo_value: u64, - // ) -> JsonValue { - // // TODO: Understand why sapling and orchard have an "is_change" filter, but transparent does not - // // It seems like this already depends on an invariant where all outgoing utxos are change. - // // This should never be true _AFTER SOME VERSION_ since we only send change to orchard. - // // If I sent a transaction to a foreign transparent address wouldn't this "total_change" value - // // be wrong? - // let total_change = wallet_transaction - // .sapling_notes - // .iter() - // .filter(|nd| nd.is_change) - // .map(|nd| nd.sapling_crypto_note.value().inner()) - // .sum::() - // + wallet_transaction - // .orchard_notes - // .iter() - // .filter(|nd| nd.is_change) - // .map(|nd| nd.orchard_crypto_note.value().inner()) - // .sum::() - // + received_utxo_value; - - // // Collect outgoing metadata - // let outgoing_json = wallet_transaction - // .outgoing_tx_data - // .iter() - // .map(|om| { - // object! { - // // Is this address ever different than the address in the containing struct - // // this is the full UA. - // "address" => om.recipient_ua.clone().unwrap_or(om.recipient_address.clone()), - // "value" => om.value, - // "memo" => LightWallet::memo_str(Some(om.memo.clone())) - // } - // }) - // .collect::>(); - - // let block_height: u32 = wallet_transaction.status.get_height().into(); - // object! { - // "block_height" => block_height, - // "pending" => !wallet_transaction.status.is_confirmed(), - // "datetime" => wallet_transaction.datetime, - // "txid" => format!("{}", wallet_transaction.txid), - // "zec_price" => wallet_transaction.price.map(|p| (p * 100.0).round() / 100.0), - // "amount" => total_change as i64 - wallet_transaction.total_value_spent() as i64, - // "outgoing_metadata" => outgoing_json, - // } - // } -} diff --git a/zingolib/src/lightclient/propose.rs b/zingolib/src/lightclient/propose.rs index 03b0bcce8..5dca04e95 100644 --- a/zingolib/src/lightclient/propose.rs +++ b/zingolib/src/lightclient/propose.rs @@ -164,6 +164,8 @@ impl LightClient { #[cfg(test)] mod shielding { + use crate::wallet::propose::ProposeShieldError; + async fn create_basic_client() -> crate::lightclient::LightClient { crate::lightclient::LightClient::create_unconnected( &crate::config::ZingoConfigBuilder::default().create(), @@ -175,23 +177,22 @@ mod shielding { .await .unwrap() } - // FIXME: zingo2 - // #[tokio::test] - // async fn propose_shield_missing_scan_prerequisite() { - // let basic_client = create_basic_client().await; - // let propose_shield_result = basic_client - // .wallet - // .lock() - // .await - // .create_shield_proposal() - // .await; - // match propose_shield_result { - // Err(ProposeShieldError::Component( - // zcash_client_backend::data_api::error::Error::ScanRequired, - // )) => true, - // _ => panic!("Unexpected error state!"), - // }; - // } + #[tokio::test] + async fn propose_shield_missing_scan_prerequisite() { + let basic_client = create_basic_client().await; + let propose_shield_result = basic_client + .wallet + .lock() + .await + .create_shield_proposal() + .await; + match propose_shield_result { + Err(ProposeShieldError::Component( + zcash_client_backend::data_api::error::Error::ScanRequired, + )) => true, + _ => panic!("Unexpected error state!"), + }; + } #[tokio::test] async fn get_transparent_addresses() { let basic_client = create_basic_client().await; diff --git a/zingolib/src/testutils.rs b/zingolib/src/testutils.rs index 205eecdac..319fd04c7 100644 --- a/zingolib/src/testutils.rs +++ b/zingolib/src/testutils.rs @@ -209,7 +209,7 @@ fn check_spend_status_equality(first: SpendStatus, second: SpendStatus) -> bool ) } -// FIXME: zingo2 +// FIXME: zingo2 used in large test that needs re-integrating // /// Send from sender to recipient and then sync the recipient // pub async fn send_value_between_clients_and_sync( // manager: &RegtestManager, diff --git a/zingolib/src/wallet/data.rs b/zingolib/src/wallet/data.rs index 0ef72c93a..6861973e8 100644 --- a/zingolib/src/wallet/data.rs +++ b/zingolib/src/wallet/data.rs @@ -1296,314 +1296,6 @@ pub mod summaries { } } - // FIXME: zingo2, re-implement detailed tx summary with cleaner interface - // /// Detailed transaction summary. - // /// A struct designed for conveniently displaying information to the user or converting to JSON to pass through an FFI. - // /// A "snapshot" of the state of a transaction in the wallet at the time the summary was constructed. - // /// Not to be used for internal logic in the system. - // #[derive(Clone, PartialEq, Debug)] - // pub struct DetailedTransactionSummary { - // txid: TxId, - // datetime: u64, - // status: ConfirmationStatus, - // blockheight: BlockHeight, - // kind: TransactionKind, - // value: u64, - // fee: Option, - // zec_price: Option, - // orchard_nullifiers: Vec, - // sapling_nullifiers: Vec, - // orchard_notes: Vec, - // sapling_notes: Vec, - // transparent_coins: Vec, - // outgoing_tx_data: Vec, - // } - - // impl DetailedTransactionSummary { - // /// Gets orchard nullifiers - // pub fn orchard_nullifiers(&self) -> Vec<&str> { - // self.orchard_nullifiers.iter().map(|n| n.as_str()).collect() - // } - // /// Gets sapling nullifiers - // pub fn sapling_nullifiers(&self) -> Vec<&str> { - // self.sapling_nullifiers.iter().map(|n| n.as_str()).collect() - // } - // } - - // impl TransactionSummaryInterface for DetailedTransactionSummary { - // fn txid(&self) -> TxId { - // self.txid - // } - // fn datetime(&self) -> u64 { - // self.datetime - // } - // fn status(&self) -> ConfirmationStatus { - // self.status - // } - // fn blockheight(&self) -> BlockHeight { - // self.blockheight - // } - // fn kind(&self) -> TransactionKind { - // self.kind - // } - // fn value(&self) -> u64 { - // self.value - // } - // fn fee(&self) -> Option { - // self.fee - // } - // fn zec_price(&self) -> Option { - // self.zec_price - // } - // fn orchard_notes(&self) -> &[OrchardNoteSummary] { - // &self.orchard_notes - // } - // fn sapling_notes(&self) -> &[SaplingNoteSummary] { - // &self.sapling_notes - // } - // fn transparent_coins(&self) -> &[TransparentCoinSummary] { - // &self.transparent_coins - // } - // fn outgoing_orchard_notes(&self) -> &[OutgoingNoteSummary] { - // &self.outgoing_orchard_notes - // } - // fn outgoing_orchard_notes(&self) -> &[OutgoingNoteSummary] { - // &self.outgoing_orchard_notes - // } - // } - - // impl std::fmt::Display for DetailedTransactionSummary { - // fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - // let ( - // datetime, - // fee, - // zec_price, - // orchard_notes, - // sapling_notes, - // transparent_coins, - // outgoing_tx_data_summaries, - // ) = self.prepare_for_display(); - // let orchard_nullifier_summaries = - // OrchardNullifierSummaries(self.orchard_nullifiers.clone()); - // let sapling_nullifier_summaries = - // SaplingNullifierSummaries(self.sapling_nullifiers.clone()); - // write!( - // f, - // "{{ - // txid: {} - // datetime: {} - // status: {} - // blockheight: {} - // kind: {} - // value: {} - // fee: {} - // zec price: {} - // orchard_nullifiers: {} - // sapling_nullifiers: {} - // orchard notes: {} - // sapling notes: {} - // transparent coins: {} - // outgoing data: {} - // }}", - // self.txid, - // datetime, - // self.status, - // u64::from(self.blockheight), - // self.kind, - // self.value, - // fee, - // zec_price, - // orchard_nullifier_summaries, - // sapling_nullifier_summaries, - // orchard_notes, - // sapling_notes, - // transparent_coins, - // outgoing_tx_data_summaries, - // ) - // } - // } - - // impl From for JsonValue { - // fn from(transaction: DetailedTransactionSummary) -> Self { - // json::object! { - // "txid" => transaction.txid.to_string(), - // "datetime" => transaction.datetime, - // "status" => transaction.status.to_string(), - // "blockheight" => u64::from(transaction.blockheight), - // "kind" => transaction.kind.to_string(), - // "value" => transaction.value, - // "fee" => transaction.fee, - // "zec_price" => transaction.zec_price, - // "orchard_nullifiers" => JsonValue::from(transaction.orchard_nullifiers), - // "sapling_nullifiers" => JsonValue::from(transaction.sapling_nullifiers), - // "orchard_notes" => JsonValue::from(transaction.orchard_notes), - // "sapling_notes" => JsonValue::from(transaction.sapling_notes), - // "transparent_coins" => JsonValue::from(transaction.transparent_coins), - // "outgoing_tx_data" => JsonValue::from(transaction.outgoing_tx_data), - // } - // } - // } - - // /// Wraps a vec of detailed transaction summaries for the implementation of std::fmt::Display - // #[derive(PartialEq, Debug)] - // pub struct DetailedTransactionSummaries(pub Vec); - - // impl DetailedTransactionSummaries { - // /// Creates a new DetailedTransactionSummaries struct - // pub fn new(transaction_summaries: Vec) -> Self { - // DetailedTransactionSummaries(transaction_summaries) - // } - // /// Implicitly dispatch to the wrapped data - // pub fn iter(&self) -> std::slice::Iter { - // self.0.iter() - // } - // /// Total fees captured by these summaries - // pub fn paid_fees(&self) -> u64 { - // self.iter().filter_map(|summary| summary.fee()).sum() - // } - // /// A Vec of the txids - // pub fn txids(&self) -> Vec { - // self.iter().map(|summary| summary.txid()).collect() - // } - // } - - // impl std::fmt::Display for DetailedTransactionSummaries { - // fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - // for transaction_summary in &self.0 { - // write!(f, "\n{}", transaction_summary)?; - // } - // Ok(()) - // } - // } - - // impl From for JsonValue { - // fn from(transaction_summaries: DetailedTransactionSummaries) -> Self { - // let transaction_summaries: Vec = transaction_summaries - // .0 - // .into_iter() - // .map(JsonValue::from) - // .collect(); - // json::object! { - // "detailed_transaction_summaries" => transaction_summaries - // } - // } - // } - - // /// Builder for DetailedTransactionSummary - // pub struct DetailedTransactionSummaryBuilder { - // txid: Option, - // datetime: Option, - // status: Option, - // blockheight: Option, - // kind: Option, - // value: Option, - // fee: Option>, - // zec_price: Option>, - // orchard_nullifiers: Option>, - // sapling_nullifiers: Option>, - // orchard_notes: Option>, - // sapling_notes: Option>, - // transparent_coins: Option>, - // outgoing_tx_data: Option>, - // } - - // impl DetailedTransactionSummaryBuilder { - // /// Creates a new DetailedTransactionSummary builder - // pub fn new() -> DetailedTransactionSummaryBuilder { - // DetailedTransactionSummaryBuilder { - // txid: None, - // datetime: None, - // status: None, - // blockheight: None, - // kind: None, - // value: None, - // fee: None, - // zec_price: None, - // orchard_nullifiers: None, - // sapling_nullifiers: None, - // orchard_notes: None, - // sapling_notes: None, - // transparent_coins: None, - // outgoing_tx_data: None, - // } - // } - - // build_method!(txid, TxId); - // build_method!(datetime, u64); - // build_method!(status, ConfirmationStatus); - // build_method!(blockheight, BlockHeight); - // build_method!(kind, TransactionKind); - // build_method!(value, u64); - // build_method!(fee, Option); - // build_method!(zec_price, Option); - // build_method!(orchard_nullifiers, Vec); - // build_method!(sapling_nullifiers, Vec); - // build_method!(orchard_notes, Vec); - // build_method!(sapling_notes, Vec); - // build_method!(transparent_coins, Vec); - // build_method!(outgoing_tx_data, Vec); - - // /// Builds DetailedTransactionSummary from builder - // pub fn build(&self) -> Result { - // Ok(DetailedTransactionSummary { - // txid: self - // .txid - // .ok_or(BuildError::MissingField("txid".to_string()))?, - // datetime: self - // .datetime - // .ok_or(BuildError::MissingField("datetime".to_string()))?, - // status: self - // .status - // .ok_or(BuildError::MissingField("status".to_string()))?, - // blockheight: self - // .blockheight - // .ok_or(BuildError::MissingField("blockheight".to_string()))?, - // kind: self - // .kind - // .ok_or(BuildError::MissingField("kind".to_string()))?, - // value: self - // .value - // .ok_or(BuildError::MissingField("value".to_string()))?, - // fee: self - // .fee - // .ok_or(BuildError::MissingField("fee".to_string()))?, - // zec_price: self - // .zec_price - // .ok_or(BuildError::MissingField("zec_price".to_string()))?, - // orchard_nullifiers: self - // .orchard_nullifiers - // .clone() - // .ok_or(BuildError::MissingField("orchard_nullifiers".to_string()))?, - // sapling_nullifiers: self - // .sapling_nullifiers - // .clone() - // .ok_or(BuildError::MissingField("sapling_nullifiers".to_string()))?, - // orchard_notes: self - // .orchard_notes - // .clone() - // .ok_or(BuildError::MissingField("orchard_notes".to_string()))?, - // sapling_notes: self - // .sapling_notes - // .clone() - // .ok_or(BuildError::MissingField("sapling_notes".to_string()))?, - // transparent_coins: self - // .transparent_coins - // .clone() - // .ok_or(BuildError::MissingField("transparent_coins".to_string()))?, - // outgoing_tx_data: self - // .outgoing_tx_data - // .clone() - // .ok_or(BuildError::MissingField("outgoing_tx_data".to_string()))?, - // }) - // } - // } - - // impl Default for DetailedTransactionSummaryBuilder { - // fn default() -> Self { - // Self::new() - // } - // } - /// Basic note summary. /// /// Intended in the context of a transaction summary to provide the most useful data to user without cluttering up @@ -1835,32 +1527,6 @@ pub mod summaries { Ok(()) } } - - /// Wraps a vec of orchard nullifier summaries for the implementation of std::fmt::Display - pub struct OrchardNullifierSummaries(Vec); - - impl std::fmt::Display for OrchardNullifierSummaries { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - for nullifier in &self.0 { - write!(f, "\n{}", nullifier)?; - } - Ok(()) - } - } - - /// Wraps a vec of sapling nullifier summaries for the implementation of std::fmt::Display - // FIXME: zingo2 - #[allow(dead_code)] - struct SaplingNullifierSummaries(Vec); - - impl std::fmt::Display for SaplingNullifierSummaries { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - for nullifier in &self.0 { - write!(f, "\n{}", nullifier)?; - } - Ok(()) - } - } } /// Convenience wrapper for primitive diff --git a/zingolib/src/wallet/disk/testing/tests.rs b/zingolib/src/wallet/disk/testing/tests.rs index b72741303..5921d16be 100644 --- a/zingolib/src/wallet/disk/testing/tests.rs +++ b/zingolib/src/wallet/disk/testing/tests.rs @@ -198,29 +198,27 @@ async fn loaded_wallet_assert( assert_eq!(balance.orchard_balance, Some(expected_balance)); } if expected_balance > 0 { - // FIXME: zingo2 - // crate::testutils::lightclient::from_inputs::quick_send( - // &lightclient, - // vec![( - // &crate::get_base_address_macro!(lightclient, "sapling"), - // 11011, - // None, - // )], - // ) - // .await - // .unwrap(); + crate::testutils::lightclient::from_inputs::quick_send( + &lightclient, + vec![( + &crate::get_base_address_macro!(lightclient, "sapling"), + 11011, + None, + )], + ) + .await + .unwrap(); lightclient.sync_and_await().await.unwrap(); - // FIXME: zingo2 - // crate::testutils::lightclient::from_inputs::quick_send( - // &lightclient, - // vec![( - // &crate::get_base_address_macro!(lightclient, "transparent"), - // 28000, - // None, - // )], - // ) - // .await - // .unwrap(); + crate::testutils::lightclient::from_inputs::quick_send( + &lightclient, + vec![( + &crate::get_base_address_macro!(lightclient, "transparent"), + 28000, + None, + )], + ) + .await + .unwrap(); } } diff --git a/zingolib/src/wallet/propose.rs b/zingolib/src/wallet/propose.rs index 312316b43..624c288e7 100644 --- a/zingolib/src/wallet/propose.rs +++ b/zingolib/src/wallet/propose.rs @@ -181,19 +181,18 @@ mod test { ) .load_example_wallet_with_client() .await; - let wallet = client.wallet.lock().await; + let mut wallet = client.wallet.lock().await; let pool = PoolType::Shielded(zcash_client_backend::ShieldedProtocol::Orchard); let self_address = wallet.get_first_address(pool).unwrap(); let receivers = vec![(self_address.as_str(), 100_000, None)]; - let _request = transaction_request_from_send_inputs(receivers) + let request = transaction_request_from_send_inputs(receivers) .expect("actually all of this logic oughta be internal to propose"); - // FIXME: zingo2 - // wallet - // .create_send_proposal(request) - // .await - // .expect("can propose from existing data"); + wallet + .create_send_proposal(request) + .await + .expect("can propose from existing data"); } } diff --git a/zingolib/src/wallet/transaction_record.rs b/zingolib/src/wallet/transaction_record.rs index 895678c3c..8c99c458c 100644 --- a/zingolib/src/wallet/transaction_record.rs +++ b/zingolib/src/wallet/transaction_record.rs @@ -798,67 +798,6 @@ mod tests { assert_eq!(new.value_spent_by_pool(), t); } - // FIXME: zingo2 - // #[test_matrix( - // [true, false], - // [true, false], - // [true, false], - // [true, false], - // [true, false], - // [true, false] - // )] - // fn query_for_outputs( - // unspent: bool, - // pending_spent: bool, - // spent: bool, - // transparent: bool, - // sapling: bool, - // orchard: bool, - // ) { - // let queried_spend_state = OutputSpendStatusQuery { - // unspent, - // pending_spent, - // spent, - // }; - // let queried_pools = OutputPoolQuery { - // transparent, - // sapling, - // orchard, - // }; - // let mut queried_spend_state_count = 0; - // if unspent { - // queried_spend_state_count += 1; - // } - // if pending_spent { - // queried_spend_state_count += 1; - // } - // if spent { - // queried_spend_state_count += 1; - // } - // let mut queried_pools_count = 0; - // if transparent { - // queried_pools_count += 1; - // } - // if sapling { - // queried_pools_count += 1; - // } - // if orchard { - // queried_pools_count += 1; - // } - - // let expected = queried_spend_state_count * queried_pools_count; - - // let default_nn_transaction_record = nine_note_transaction_record_default(); - // let requested_outputs: Vec = - // Output::get_record_outputs(&default_nn_transaction_record) - // .iter() - // .filter(|o| o.spend_status_query(queried_spend_state)) - // .filter(|&o| o.pool_query(queried_pools)) - // .cloned() - // .collect(); - // assert_eq!(requested_outputs.len(), expected); - // } - #[test_matrix( [true, false], [true, false], diff --git a/zingolib/src/wallet/transaction_records_by_id.rs b/zingolib/src/wallet/transaction_records_by_id.rs index 8e3b33c01..ffaee7a7b 100644 --- a/zingolib/src/wallet/transaction_records_by_id.rs +++ b/zingolib/src/wallet/transaction_records_by_id.rs @@ -219,7 +219,7 @@ impl TransactionRecordsById { }); } - // FIXME: + // FIXME: zingo2 this should be a user API so not to lose failed sends with important memos etc. // /// Invalidates all those transactions which were broadcast but never 'confirmed' accepted by a miner. // pub(crate) fn clear_expired_mempool(&mut self, latest_height: u64) { // // Pending window: How long to wait past the chain tip before clearing a pending @@ -471,132 +471,17 @@ impl Default for TransactionRecordsById { #[cfg(test)] mod tests { - use crate::{ - mocks::{orchard_note::OrchardCryptoNoteBuilder, SaplingCryptoNoteBuilder}, - wallet::{ - output::{ - orchard::mocks::OrchardNoteBuilder, query::OutputSpendStatusQuery, - sapling::mocks::SaplingNoteBuilder, transparent::mocks::TransparentOutputBuilder, - OldOutputInterface, - }, - transaction_record::mocks::nine_note_transaction_record, - }, + use crate::wallet::{ + output::{query::OutputSpendStatusQuery, OldOutputInterface}, + transaction_record::mocks::nine_note_transaction_record, }; use super::TransactionRecordsById; use sapling_crypto::note_encryption::SaplingDomain; use zcash_client_backend::{wallet::ReceivedNote, ShieldedProtocol}; - use zcash_primitives::transaction::TxId; - use zingo_status::confirmation_status::ConfirmationStatus; - // FIXME: zingo2 - // #[test] - // fn invalidate_all_transactions_after_or_at_height() { - // let transaction_record_later = TransactionRecordBuilder::default() - // .randomize_txid() - // .status(Confirmed(15.into())) - // .transparent_outputs(TransparentOutputBuilder::default()) - // .build(); - // let spending_txid = transaction_record_later.txid; - - // let spend_in_known_tx = Some((spending_txid, Confirmed(15.into()))); - - // let transaction_record_early = TransactionRecordBuilder::default() - // .randomize_txid() - // .status(Confirmed(5.into())) - // .transparent_outputs( - // TransparentOutputBuilder::default() - // .spending_tx_status(spend_in_known_tx) - // .clone(), - // ) - // .sapling_notes( - // SaplingNoteBuilder::default() - // .spending_tx_status(spend_in_known_tx) - // .clone(), - // ) - // .orchard_notes( - // OrchardNoteBuilder::default() - // .spending_tx_status(spend_in_known_tx) - // .clone(), - // ) - // .orchard_notes(OrchardNoteBuilder::default()) - // .set_output_indexes() - // .build(); - - // let txid_containing_valid_note_with_invalid_spends = transaction_record_early.txid; - - // let mut transaction_records_by_id = TransactionRecordsById::default(); - // transaction_records_by_id.insert_transaction_record(transaction_record_early); - // transaction_records_by_id.insert_transaction_record(transaction_record_later); - - // let reorg_height: BlockHeight = 10.into(); - - // transaction_records_by_id.invalidate_all_transactions_after_or_at_height(reorg_height); - - // assert_eq!(transaction_records_by_id.len(), 1); - // //^ the deleted tx is not around - // let transaction_record_cvnwis = transaction_records_by_id - // .get(&txid_containing_valid_note_with_invalid_spends) - // .unwrap(); - - // let query_for_spentish_notes = OutputSpendStatusQuery::spentish(); - // let spentish_sapling_notes_in_tx_cvnwis = Output::get_all_outputs_with_status( - // transaction_record_cvnwis, - // query_for_spentish_notes, - // ); - // assert_eq!(spentish_sapling_notes_in_tx_cvnwis.len(), 0); - // } - - // TODO: move this into an associated fn of TransparentOutputBuilder - // FIXME: zingo2 - #[allow(dead_code)] - fn spent_transparent_output_builder( - amount: u64, - sent: (TxId, ConfirmationStatus), - ) -> TransparentOutputBuilder { - TransparentOutputBuilder::default() - .value(amount) - .spending_tx_status(Some(sent)) - .to_owned() - } - - // FIXME: zingo2 - #[allow(dead_code)] - fn spent_sapling_note_builder( - amount: u64, - sent: (TxId, ConfirmationStatus), - sapling_nullifier: &sapling_crypto::Nullifier, - ) -> SaplingNoteBuilder { - SaplingNoteBuilder::default() - .note( - SaplingCryptoNoteBuilder::default() - .value(sapling_crypto::value::NoteValue::from_raw(amount)) - .to_owned(), - ) - .spending_tx_status(Some(sent)) - .nullifier(Some(*sapling_nullifier)) - .to_owned() - } - // FIXME: zingo2 - #[allow(dead_code)] - fn spent_orchard_note_builder( - amount: u64, - sent: (TxId, ConfirmationStatus), - orchard_nullifier: &orchard::note::Nullifier, - ) -> OrchardNoteBuilder { - OrchardNoteBuilder::default() - .note( - OrchardCryptoNoteBuilder::default() - .value(orchard::value::NoteValue::from_raw(amount)) - .to_owned(), - ) - .spending_tx_status(Some(sent)) - .nullifier(Some(*orchard_nullifier)) - .to_owned() - } - - // FIXME: zingo2 + // FIXME: zingo2 test with integration tests // #[test] // fn calculate_transaction_fee() { // let mut sapling_nullifier_builder = SaplingNullifierBuilder::new(); From 8377c809bac0e367048047eca5d948de1be25608 Mon Sep 17 00:00:00 2001 From: Oscar Pepper Date: Mon, 24 Feb 2025 13:41:23 +0000 Subject: [PATCH 18/26] fixed all chain generic fixtures --- .../src/testutils/chain_generics/fixtures.rs | 567 +++++++++--------- zingolib/src/wallet/describe.rs | 115 +--- 2 files changed, 296 insertions(+), 386 deletions(-) diff --git a/zingolib/src/testutils/chain_generics/fixtures.rs b/zingolib/src/testutils/chain_generics/fixtures.rs index 8b57adfca..6b789d649 100644 --- a/zingolib/src/testutils/chain_generics/fixtures.rs +++ b/zingolib/src/testutils/chain_generics/fixtures.rs @@ -11,6 +11,9 @@ use crate::testutils::chain_generics::with_assertions; use crate::testutils::fee_tables; use crate::testutils::lightclient::from_inputs; use crate::testutils::lightclient::get_base_address; +use crate::wallet::data::summaries::SelfSendValueTransfer; +use crate::wallet::data::summaries::SentValueTransfer; +use crate::wallet::data::summaries::ValueTransferKind; use crate::wallet::output::query::OutputPoolQuery; use crate::wallet::output::query::OutputQuery; use crate::wallet::output::query::OutputSpendStatusQuery; @@ -20,139 +23,153 @@ pub async fn create_various_value_transfers() where CC: ConductChain, { - // FIXME: zingo2 - // let mut environment = CC::setup().await; - // let sender = environment.fund_client_orchard(250_000).await; - // let send_value_for_recipient = 23_000; - // let send_value_self = 17_000; - - // println!("client is ready to send"); - - // let recipient = environment.create_client().await; - // with_assertions::propose_send_bump_sync_all_recipients( - // &mut environment, - // &sender, - // vec![ - // ( - // &recipient, - // PoolType::Shielded(Orchard), - // send_value_for_recipient, - // Some("Orchard sender to recipient"), - // ), - // ( - // &sender, - // PoolType::Shielded(Sapling), - // send_value_self, - // Some("Orchard sender to self"), - // ), - // (&sender, PoolType::Transparent, send_value_self, None), - // ], - // false, - // ) - // .await - // .unwrap(); - - // assert_eq!(sender.sorted_value_transfers(true).await.len(), 3); - - // assert!(sender - // .sorted_value_transfers(false) - // .await - // .iter() - // .any(|vt| { vt.kind() == ValueTransferKind::Received })); - - // assert!(sender - // .sorted_value_transfers(false) - // .await - // .iter() - // .any(|vt| { vt.kind() == ValueTransferKind::Sent(SentValueTransfer::Send) })); - - // assert!(sender.sorted_value_transfers(false).await.iter().any(|vt| { - // vt.kind() - // == ValueTransferKind::Sent(SentValueTransfer::SendToSelf( - // SelfSendValueTransfer::MemoToSelf, - // )) - // })); - - // assert_eq!(recipient.sorted_value_transfers(true).await.len(), 1); - - // with_assertions::propose_send_bump_sync_all_recipients( - // &mut environment, - // &sender, - // vec![(&sender, PoolType::Shielded(Orchard), send_value_self, None)], - // false, - // ) - // .await - // .unwrap(); - - // assert_eq!(sender.sorted_value_transfers(true).await.len(), 4); - // assert_eq!( - // sender.sorted_value_transfers(true).await[0].kind(), - // ValueTransferKind::Sent(SentValueTransfer::SendToSelf(SelfSendValueTransfer::Basic)) - // ); - - // with_assertions::assure_propose_shield_bump_sync(&mut environment, &sender, false) - // .await - // .unwrap(); - // assert_eq!(sender.sorted_value_transfers(true).await.len(), 5); - // assert_eq!( - // sender.sorted_value_transfers(true).await[0].kind(), - // ValueTransferKind::Sent(SentValueTransfer::SendToSelf(SelfSendValueTransfer::Shield)) - // ); + let mut environment = CC::setup().await; + let mut sender = environment.fund_client_orchard(250_000).await; + let sender_orchard_addr = + get_base_address(&sender, PoolType::Shielded(ShieldedProtocol::Orchard)).await; + let sender_sapling_addr = + get_base_address(&sender, PoolType::Shielded(ShieldedProtocol::Sapling)).await; + let sender_taddr = get_base_address(&sender, PoolType::Transparent).await; + let send_value_for_recipient = 23_000; + let send_value_self = 17_000; + + println!("client is ready to send"); + + let mut recipient = environment.create_client().await; + with_assertions::propose_send_bump_sync_all_recipients( + &mut environment, + &mut sender, + vec![ + ( + &get_base_address(&recipient, PoolType::Shielded(ShieldedProtocol::Orchard)).await, + send_value_for_recipient, + Some("Orchard sender to recipient"), + ), + ( + &sender_sapling_addr, + send_value_self, + Some("Orchard sender to self"), + ), + (&sender_taddr, send_value_self, None), + ], + vec![&mut recipient], + false, + ) + .await + .unwrap(); + + assert_eq!(sender.sorted_value_transfers(true).await.len(), 3); + + assert!(sender + .sorted_value_transfers(false) + .await + .iter() + .any(|vt| { vt.kind() == ValueTransferKind::Received })); + + assert!(sender + .sorted_value_transfers(false) + .await + .iter() + .any(|vt| { vt.kind() == ValueTransferKind::Sent(SentValueTransfer::Send) })); + + assert!(sender.sorted_value_transfers(false).await.iter().any(|vt| { + vt.kind() + == ValueTransferKind::Sent(SentValueTransfer::SendToSelf( + SelfSendValueTransfer::MemoToSelf, + )) + })); + + assert_eq!(recipient.sorted_value_transfers(true).await.len(), 1); + + with_assertions::propose_send_bump_sync_all_recipients( + &mut environment, + &mut sender, + vec![(&sender_orchard_addr, send_value_self, None)], + vec![], + false, + ) + .await + .unwrap(); + + assert_eq!(sender.sorted_value_transfers(true).await.len(), 4); + assert_eq!( + sender.sorted_value_transfers(true).await[0].kind(), + ValueTransferKind::Sent(SentValueTransfer::SendToSelf(SelfSendValueTransfer::Basic)) + ); + + with_assertions::assure_propose_shield_bump_sync(&mut environment, &mut sender, false) + .await + .unwrap(); + assert_eq!(sender.sorted_value_transfers(true).await.len(), 5); + assert_eq!( + sender.sorted_value_transfers(true).await[0].kind(), + ValueTransferKind::Sent(SentValueTransfer::SendToSelf(SelfSendValueTransfer::Shield)) + ); } /// sends back and forth several times, including sends to transparent -pub async fn send_shield_cycle(_n: u64) +pub async fn send_shield_cycle(n: u64) where CC: ConductChain, { - // FIXME: zingo2 - // let mut environment = CC::setup().await; - // let primary_fund = 1_000_000; - // let primary = environment.fund_client_orchard(primary_fund).await; - - // let secondary = environment.create_client().await; - - // for _ in 0..n { - // let (recorded_fee, recorded_value, recorded_change) = - // with_assertions::propose_send_bump_sync_all_recipients( - // &mut environment, - // &primary, - // vec![ - // (&secondary, Transparent, 100_000, None), - // (&secondary, Transparent, 4_000, None), - // ], - // false, - // ) - // .await - // .unwrap(); - // assert_eq!( - // (recorded_fee, recorded_value, recorded_change), - // (MARGINAL_FEE.into_u64() * 4, recorded_value, recorded_change) - // ); - - // let (recorded_fee, recorded_value) = - // with_assertions::assure_propose_shield_bump_sync(&mut environment, &secondary, false) - // .await - // .unwrap(); - // assert_eq!( - // (recorded_fee, recorded_value), - // (MARGINAL_FEE.into_u64() * 3, 100_000 - recorded_fee) - // ); - - // let (recorded_fee, recorded_value, recorded_change) = - // with_assertions::propose_send_bump_sync_all_recipients( - // &mut environment, - // &secondary, - // vec![(&primary, Shielded(Orchard), 50_000, None)], - // false, - // ) - // .await - // .unwrap(); - // assert_eq!( - // (recorded_fee, recorded_value, recorded_change), - // (MARGINAL_FEE.into_u64() * 2, 50_000, recorded_change) - // ); - // } + let mut environment = CC::setup().await; + let primary_fund = 1_000_000; + let mut primary = environment.fund_client_orchard(primary_fund).await; + + let mut secondary = environment.create_client().await; + let secondary_taddr = get_base_address(&secondary, PoolType::Transparent).await; + + for _ in 0..n { + let (recorded_fee, recorded_value, recorded_change) = + with_assertions::propose_send_bump_sync_all_recipients( + &mut environment, + &mut primary, + vec![ + (&secondary_taddr, 100_000, None), + (&secondary_taddr, 4_000, None), + ], + vec![&mut secondary], + false, + ) + .await + .unwrap(); + assert_eq!( + (recorded_fee, recorded_value, recorded_change), + (MARGINAL_FEE.into_u64() * 4, recorded_value, recorded_change) + ); + + let (recorded_fee, recorded_value) = with_assertions::assure_propose_shield_bump_sync( + &mut environment, + &mut secondary, + false, + ) + .await + .unwrap(); + assert_eq!( + (recorded_fee, recorded_value), + (MARGINAL_FEE.into_u64() * 3, 100_000 - recorded_fee) + ); + + let (recorded_fee, recorded_value, recorded_change) = + with_assertions::propose_send_bump_sync_all_recipients( + &mut environment, + &mut secondary, + vec![( + &get_base_address(&primary, PoolType::Shielded(ShieldedProtocol::Orchard)) + .await, + 50_000, + None, + )], + vec![&mut primary], + false, + ) + .await + .unwrap(); + assert_eq!( + (recorded_fee, recorded_value, recorded_change), + (MARGINAL_FEE.into_u64() * 2, 50_000, recorded_change) + ); + } } /// overlooks a bunch of dust inputs to find a pair of inputs marginally big enough to send @@ -160,56 +177,63 @@ pub async fn ignore_dust_inputs() where CC: ConductChain, { - // FIXME: zingo2 - // let mut environment = CC::setup().await; - - // let primary = environment.fund_client_orchard(120_000).await; - // let secondary = environment.create_client().await; - - // // send a bunch of dust - // let (recorded_fee, recorded_value, recorded_change) = - // with_assertions::propose_send_bump_sync_all_recipients( - // &mut environment, - // &primary, - // vec![ - // (&secondary, Shielded(Sapling), 1_000, None), - // (&secondary, Shielded(Sapling), 1_000, None), - // (&secondary, Shielded(Sapling), 1_000, None), - // (&secondary, Shielded(Sapling), 1_000, None), - // (&secondary, Shielded(Sapling), 15_000, None), - // (&secondary, Shielded(Orchard), 1_000, None), - // (&secondary, Shielded(Orchard), 1_000, None), - // (&secondary, Shielded(Orchard), 1_000, None), - // (&secondary, Shielded(Orchard), 1_000, None), - // (&secondary, Shielded(Orchard), 15_000, None), - // ], - // false, - // ) - // .await - // .unwrap(); - // assert_eq!( - // (recorded_fee, recorded_value, recorded_change), - // ( - // 11 * MARGINAL_FEE.into_u64(), - // recorded_value, - // recorded_change - // ) - // ); - - // // combine the only valid sapling note with the only valid orchard note to send - // let (recorded_fee, recorded_value, recorded_change) = - // with_assertions::propose_send_bump_sync_all_recipients( - // &mut environment, - // &secondary, - // vec![(&primary, Shielded(Orchard), 10_000, None)], - // false, - // ) - // .await - // .unwrap(); - // assert_eq!( - // (recorded_fee, recorded_value, recorded_change), - // (4 * MARGINAL_FEE.into_u64(), 10_000, recorded_change) - // ); + let mut environment = CC::setup().await; + + let mut primary = environment.fund_client_orchard(120_000).await; + let mut secondary = environment.create_client().await; + let secondary_orchard_addr = + get_base_address(&secondary, PoolType::Shielded(ShieldedProtocol::Orchard)).await; + + // send a bunch of dust + let (recorded_fee, recorded_value, recorded_change) = + with_assertions::propose_send_bump_sync_all_recipients( + &mut environment, + &mut primary, + vec![ + (&secondary_orchard_addr, 1_000, None), + (&secondary_orchard_addr, 1_000, None), + (&secondary_orchard_addr, 1_000, None), + (&secondary_orchard_addr, 1_000, None), + (&secondary_orchard_addr, 15_000, None), + (&secondary_orchard_addr, 1_000, None), + (&secondary_orchard_addr, 1_000, None), + (&secondary_orchard_addr, 1_000, None), + (&secondary_orchard_addr, 1_000, None), + (&secondary_orchard_addr, 15_000, None), + ], + vec![&mut secondary], + false, + ) + .await + .unwrap(); + assert_eq!( + (recorded_fee, recorded_value, recorded_change), + ( + 11 * MARGINAL_FEE.into_u64(), + recorded_value, + recorded_change + ) + ); + + // combine the only valid sapling note with the only valid orchard note to send + let (recorded_fee, recorded_value, recorded_change) = + with_assertions::propose_send_bump_sync_all_recipients( + &mut environment, + &mut secondary, + vec![( + &get_base_address(&primary, PoolType::Shielded(ShieldedProtocol::Orchard)).await, + 10_000, + None, + )], + vec![&mut primary], + false, + ) + .await + .unwrap(); + assert_eq!( + (recorded_fee, recorded_value, recorded_change), + (4 * MARGINAL_FEE.into_u64(), 10_000, recorded_change) + ); } /// In order to fund a transaction multiple notes may be selected and consumed. @@ -293,7 +317,7 @@ where &mut environment, &mut secondary, vec![( - primary_orchard_addr.as_str(), + &primary_orchard_addr, expected_value_from_transaction_2, None, )], @@ -340,59 +364,57 @@ where /// the simplest test that sends from a specific shielded pool to another specific pool. error variant. pub async fn shpool_to_pool_insufficient_error( - _shpool: ShieldedProtocol, - _pool: PoolType, - _underflow_amount: u64, + shpool: ShieldedProtocol, + pool: PoolType, + underflow_amount: u64, ) where CC: ConductChain, { - // FIXME: zingo2 - // let mut environment = CC::setup().await; - - // let primary = environment.fund_client_orchard(1_000_000).await; - // let secondary = environment.create_client().await; - - // let expected_fee = fee_tables::one_to_one(Some(shpool), pool, true); - // let secondary_fund = 100_000 + expected_fee - underflow_amount; - // with_assertions::propose_send_bump_sync_all_recipients( - // &mut environment, - // &primary, - // vec![(&secondary, Shielded(shpool), secondary_fund, None)], - // false, - // ) - // .await - // .unwrap(); - - // let tertiary = environment.create_client().await; - - // let ref_secondary: Arc = Arc::new(secondary); - // let ref_tertiary: Arc = Arc::new(tertiary); - - // let tertiary_fund = 100_000; - // assert_eq!( - // from_inputs::propose( - // &ref_secondary, - // vec![( - // ref_tertiary - // .wallet - // .lock() - // .await - // .get_first_address(pool) - // .unwrap() - // .as_str(), - // tertiary_fund, - // None, - // )], - // ) - // .await - // .unwrap_err() - // .to_string(), - // format!( - // "Insufficient balance (have {}, need {} including fee)", - // secondary_fund, - // tertiary_fund + expected_fee - // ) - // ); + let mut environment = CC::setup().await; + + let mut primary = environment.fund_client_orchard(1_000_000).await; + let mut secondary = environment.create_client().await; + let secondary_addr = get_base_address(&secondary, PoolType::Shielded(shpool)).await; + + let expected_fee = fee_tables::one_to_one(Some(shpool), pool, true); + let secondary_fund = 100_000 + expected_fee - underflow_amount; + with_assertions::propose_send_bump_sync_all_recipients( + &mut environment, + &mut primary, + vec![(&secondary_addr, secondary_fund, None)], + vec![&mut secondary], + false, + ) + .await + .unwrap(); + + let tertiary = environment.create_client().await; + + let tertiary_fund = 100_000; + assert_eq!( + from_inputs::propose( + &secondary, + vec![( + tertiary + .wallet + .lock() + .await + .get_first_address(pool) + .unwrap() + .as_str(), + tertiary_fund, + None, + )], + ) + .await + .unwrap_err() + .to_string(), + format!( + "Insufficient balance (have {}, need {} including fee)", + secondary_fund, + tertiary_fund + expected_fee + ) + ); } /// the simplest test that sends from a specific shielded pool to another specific pool. also known as simpool. @@ -437,51 +459,52 @@ where /// the simplest test that sends from a specific shielded pool to another specific pool. also known as simpool. pub async fn single_sufficient_send( - _shpool: ShieldedProtocol, - _pool: PoolType, - _receiver_value: u64, - _change: u64, - _test_mempool: bool, + shpool: ShieldedProtocol, + pool: PoolType, + receiver_value: u64, + change: u64, + test_mempool: bool, ) where CC: ConductChain, { - // FIXME: zingo2 - // let mut environment = CC::setup().await; - - // let primary = environment.fund_client_orchard(1_000_000).await; - // let secondary = environment.create_client().await; - // let tertiary = environment.create_client().await; - // let ref_primary: Arc = Arc::new(primary); - // let ref_secondary: Arc = Arc::new(secondary); - // let ref_tertiary: Arc = Arc::new(tertiary); - - // let expected_fee = fee_tables::one_to_one(Some(shpool), pool, true); - - // with_assertions::propose_send_bump_sync_all_recipients( - // &mut environment, - // &ref_primary, - // vec![( - // &ref_secondary, - // Shielded(shpool), - // receiver_value + change + expected_fee, - // None, - // )], - // test_mempool, - // ) - // .await - // .unwrap(); - - // let (recorded_fee, recorded_value, recorded_change) = - // with_assertions::propose_send_bump_sync_all_recipients( - // &mut environment, - // &ref_secondary, - // vec![(&ref_tertiary, pool, receiver_value, None)], - // test_mempool, - // ) - // .await - // .unwrap(); - // assert_eq!( - // (recorded_fee, recorded_value, recorded_change), - // (expected_fee, receiver_value, change) - // ); + let mut environment = CC::setup().await; + + let mut primary = environment.fund_client_orchard(1_000_000).await; + let mut secondary = environment.create_client().await; + let mut tertiary = environment.create_client().await; + + let expected_fee = fee_tables::one_to_one(Some(shpool), pool, true); + + with_assertions::propose_send_bump_sync_all_recipients( + &mut environment, + &mut primary, + vec![( + &get_base_address(&secondary, PoolType::Shielded(shpool)).await, + receiver_value + change + expected_fee, + None, + )], + vec![&mut secondary], + test_mempool, + ) + .await + .unwrap(); + + let (recorded_fee, recorded_value, recorded_change) = + with_assertions::propose_send_bump_sync_all_recipients( + &mut environment, + &mut secondary, + vec![( + &get_base_address(&tertiary, pool).await, + receiver_value, + None, + )], + vec![&mut tertiary], + test_mempool, + ) + .await + .unwrap(); + assert_eq!( + (recorded_fee, recorded_value, recorded_change), + (expected_fee, receiver_value, change) + ); } diff --git a/zingolib/src/wallet/describe.rs b/zingolib/src/wallet/describe.rs index 3e9d9b933..d22d90776 100644 --- a/zingolib/src/wallet/describe.rs +++ b/zingolib/src/wallet/describe.rs @@ -23,21 +23,13 @@ use std::cmp::Ordering; use bip0039::Mnemonic; -use zcash_note_encryption::Domain; - use crate::config::ChainType; use crate::config::ZENNIES_FOR_ZINGO_DONATION_ADDRESS; use crate::config::ZENNIES_FOR_ZINGO_REGTEST_ADDRESS; use crate::config::ZENNIES_FOR_ZINGO_TESTNET_ADDRESS; use crate::utils; -use crate::wallet::output::ShieldedNoteInterface; - -use crate::wallet::traits::Diversifiable as _; use crate::wallet::error::BalanceError; -use crate::wallet::keys::unified::WalletCapability; -use crate::wallet::traits::DomainWalletExt; -use crate::wallet::traits::Recipient; use crate::wallet::LightWallet; use crate::UAReceivers; @@ -213,27 +205,6 @@ impl LightWallet { )?) } - /// TODO: Add Doc Comment Here! - // FIXME: zingo2 - #[allow(dead_code)] - pub(crate) fn note_address( - network: &crate::config::ChainType, - note: &D::WalletNote, - wallet_capability: &WalletCapability, - ) -> String - where - ::Recipient: Recipient, - ::Note: PartialEq + Clone, - { - D::unified_key_store_to_fvk(&wallet_capability.unified_key_store).expect("to get fvk from the unified key store") - .diversified_address(*note.diversifier()) - .and_then(|address| { - D::ua_from_contained_receiver(wallet_capability, &address) - .map(|ua| ua.encode(network)) - }) - .unwrap_or("Diversifier not in wallet. Perhaps you restored from seed and didn't restore addresses".to_string()) - } - /// TODO: Add Doc Comment Here! pub fn mnemonic(&self) -> Option<&(Mnemonic, u32)> { self.mnemonic.as_ref() @@ -786,65 +757,6 @@ impl LightWallet { pub async fn value_transfers_json_string(&self) -> String { json::JsonValue::from(self.sorted_value_transfers(true).await).pretty(2) } - - // FIXME: zingo2, re implement - // /// Provides a detailed list of transaction summaries related to this wallet in order of blockheight - // pub async fn detailed_transaction_summaries(&self) -> DetailedTransactionSummaries { - // let wallet = self.wallet.lock().await; - // let transaction_map = wallet - // .transaction_context - // .transaction_metadata_set - // .read() - // .await; - // let transaction_records = &transaction_map.transaction_records_by_id; - - // let mut transaction_summaries = transaction_records - // .values() - // .map(|tx| { - // let (kind, value, fee, orchard_notes, sapling_notes, transparent_coins) = - // basic_transaction_summary_parts(tx, transaction_records, &self.config().chain); - // let orchard_nullifiers: Vec = tx - // .spent_orchard_nullifiers - // .iter() - // .map(|nullifier| hex::encode(nullifier.to_bytes())) - // .collect(); - // let sapling_nullifiers: Vec = tx - // .spent_sapling_nullifiers - // .iter() - // .map(hex::encode) - // .collect(); - - // DetailedTransactionSummaryBuilder::new() - // .txid(tx.txid) - // .datetime(tx.datetime) - // .blockheight(tx.status.get_height()) - // .kind(kind) - // .value(value) - // .fee(fee) - // .status(tx.status) - // .zec_price(tx.price) - // .orchard_notes(orchard_notes) - // .sapling_notes(sapling_notes) - // .transparent_coins(transparent_coins) - // .outgoing_tx_data(tx.outgoing_tx_data.clone()) - // .orchard_nullifiers(orchard_nullifiers) - // .sapling_nullifiers(sapling_nullifiers) - // .build() - // .expect("all fields should be populated") - // }) - // .collect::>(); - // drop(transaction_map); - // drop(wallet); - - // transaction_summaries.sort_by_key(|tx| tx.blockheight()); - - // DetailedTransactionSummaries::new(transaction_summaries) - // } - - // /// TODO: doc comment - // pub async fn detailed_transaction_summaries_json_string(&self) -> String { - // json::JsonValue::from(self.detailed_transaction_summaries().await).pretty(2) - // } } #[cfg(any(test, feature = "test-elevation"))] @@ -903,32 +815,7 @@ mod test { } } - // FIXME: zingo2 - // #[cfg(test)] - // use crate::Orchard; - // #[cfg(test)] - // use crate::Sapling; - // #[cfg(test)] - // use zingo_status::confirmation_status::ConfirmationStatus; - - // #[cfg(test)] - // use crate::config::ZingoConfigBuilder; - // #[cfg(test)] - // use crate::mocks::orchard_note::OrchardCryptoNoteBuilder; - // #[cfg(test)] - // use crate::mocks::SaplingCryptoNoteBuilder; - // #[cfg(test)] - // use crate::wallet::notes::orchard::mocks::OrchardNoteBuilder; - // #[cfg(test)] - // use crate::wallet::notes::sapling::mocks::SaplingNoteBuilder; - // #[cfg(test)] - // use crate::wallet::notes::transparent::mocks::TransparentOutputBuilder; - // #[cfg(test)] - // use crate::wallet::transaction_record::mocks::TransactionRecordBuilder; - // #[cfg(test)] - // use crate::wallet::WalletBase; - - // FIXME: zingo2 + // FIXME: zingo2 rewrite as an integration test // #[tokio::test] // async fn confirmed_balance_excluding_dust() { // let wallet = LightWallet::new( From dce09cc21757bb5f6113f21d7e6d89a88434bc47 Mon Sep 17 00:00:00 2001 From: Oscar Pepper Date: Mon, 24 Feb 2025 13:45:21 +0000 Subject: [PATCH 19/26] fixded darkside tests --- darkside-tests/tests/tests.rs | 311 +++++++++++----------------------- 1 file changed, 103 insertions(+), 208 deletions(-) diff --git a/darkside-tests/tests/tests.rs b/darkside-tests/tests/tests.rs index d5a0296e7..7636fa1d5 100644 --- a/darkside-tests/tests/tests.rs +++ b/darkside-tests/tests/tests.rs @@ -1,19 +1,18 @@ -// use darkside_tests::darkside_connector::DarksideConnector; +use darkside_tests::darkside_connector::DarksideConnector; use darkside_tests::utils::prepare_darksidewalletd; // use darkside_tests::utils::scenarios::DarksideEnvironment; -// use darkside_tests::utils::update_tree_states_for_transaction; +use darkside_tests::utils::update_tree_states_for_transaction; use darkside_tests::utils::DarksideHandler; use testvectors::seeds::DARKSIDE_SEED; -// use tokio::time::sleep; // use zcash_client_backend::PoolType::Shielded; // use zcash_client_backend::ShieldedProtocol::Orchard; // use zingo_status::confirmation_status::ConfirmationStatus; use zingolib::config::RegtestNetwork; -// use zingolib::get_base_address_macro; +use zingolib::get_base_address_macro; use zingolib::lightclient::PoolBalances; // use zingolib::testutils::chain_generics::conduct_chain::ConductChain as _; // use zingolib::testutils::chain_generics::with_assertions::to_clients_proposal; -// use zingolib::testutils::lightclient::from_inputs; +use zingolib::testutils::lightclient::from_inputs; use zingolib::testutils::scenarios::setup::ClientBuilder; #[tokio::test] @@ -107,211 +106,107 @@ async fn reorg_receipt_sync_generic() { ); } -// FIXME: zingo2 -// #[tokio::test] -// async fn sent_transaction_reorged_into_mempool() { -// let darkside_handler = DarksideHandler::new(None); - -// let server_id = zingolib::config::construct_lightwalletd_uri(Some(format!( -// "http://127.0.0.1:{}", -// darkside_handler.grpc_port -// ))); -// prepare_darksidewalletd(server_id.clone(), true) -// .await -// .unwrap(); - -// let mut client_manager = -// ClientBuilder::new(server_id.clone(), darkside_handler.darkside_dir.clone()); -// let regtest_network = RegtestNetwork::all_upgrades_active(); -// let light_client = client_manager -// .build_client(DARKSIDE_SEED.to_string(), 0, true, regtest_network) -// .await; -// let recipient = client_manager -// .build_client( -// testvectors::seeds::HOSPITAL_MUSEUM_SEED.to_string(), -// 1, -// true, -// regtest_network, -// ) -// .await; - -// light_client.do_sync(true).await.unwrap(); -// assert_eq!( -// light_client.do_balance().await, -// PoolBalances { -// sapling_balance: Some(0), -// verified_sapling_balance: Some(0), -// spendable_sapling_balance: Some(0), -// unverified_sapling_balance: Some(0), -// orchard_balance: Some(100000000), -// verified_orchard_balance: Some(100000000), -// spendable_orchard_balance: Some(100000000), -// unverified_orchard_balance: Some(0), -// transparent_balance: Some(0) -// } -// ); -// let one_txid = from_inputs::quick_send( -// &light_client, -// vec![(&get_base_address_macro!(recipient, "unified"), 10_000, None)], -// ) -// .await -// .unwrap(); -// println!("{}", one_txid.first()); -// recipient.do_sync(false).await.unwrap(); -// dbg!(recipient.list_outputs().await); - -// let connector = DarksideConnector(server_id.clone()); -// let mut streamed_raw_txns = connector.get_incoming_transactions().await.unwrap(); -// let raw_tx = streamed_raw_txns.message().await.unwrap().unwrap(); -// // There should only be one transaction incoming -// assert!(streamed_raw_txns.message().await.unwrap().is_none()); -// connector -// .stage_transactions_stream(vec![(raw_tx.data.clone(), 4)]) -// .await -// .unwrap(); -// connector.stage_blocks_create(4, 1, 0).await.unwrap(); -// update_tree_states_for_transaction(&server_id, raw_tx.clone(), 4).await; -// connector.apply_staged(4).await.unwrap(); -// sleep(std::time::Duration::from_secs(1)).await; - -// recipient.do_sync(false).await.unwrap(); -// // light_client.do_sync(false).await.unwrap(); -// dbg!("Recipient pre-reorg: {}", recipient.list_outputs().await); -// println!( -// "Recipient pre-reorg: {}", -// serde_json::to_string_pretty(&recipient.do_balance().await).unwrap() -// ); -// println!( -// "Sender pre-reorg (unsynced): {}", -// serde_json::to_string_pretty(&light_client.do_balance().await).unwrap() -// ); - -// prepare_darksidewalletd(server_id.clone(), true) -// .await -// .unwrap(); -// let connector = DarksideConnector(server_id.clone()); -// connector.stage_blocks_create(4, 102, 0).await.unwrap(); -// connector.apply_staged(105).await.unwrap(); -// sleep(std::time::Duration::from_secs(1)).await; - -// recipient.do_sync(false).await.unwrap(); -// light_client.do_sync(false).await.unwrap(); -// println!( -// "Recipient post-reorg: {}", -// serde_json::to_string_pretty(&recipient.do_balance().await).unwrap() -// ); -// println!( -// "Sender post-reorg: {}", -// serde_json::to_string_pretty(&light_client.do_balance().await).unwrap() -// ); -// dbg!("Sender post-reorg: {}", light_client.list_outputs().await); -// let loaded_client = -// zingolib::testutils::lightclient::new_client_from_save_buffer(&light_client) -// .await -// .unwrap(); -// loaded_client.do_sync(false).await.unwrap(); -// dbg!("Sender post-load: {}", loaded_client.list_outputs().await); -// assert_eq!( -// loaded_client.do_balance().await.orchard_balance, -// Some(100000000) -// ); -// } - -// FIXME: zingo2 -// #[tokio::test] -// #[ignore = "incomplete"] -// async fn evicted_transaction_is_rebroadcast() { -// std::env::set_var("RUST_BACKTRACE", "1"); - -// let mut environment = DarksideEnvironment::setup().await; -// environment.bump_chain().await; - -// let primary = environment.fund_client_orchard(1_000_000).await; -// let secondary = environment.create_client().await; -// primary.do_sync(false).await.unwrap(); - -// let proposal = -// to_clients_proposal(&primary, &[(&secondary, Shielded(Orchard), 100_000, None)]).await; - -// let mut send_height = 0; - -// let txids = &primary -// .complete_and_broadcast_stored_proposal() -// .await -// .unwrap(); - -// println!( -// "{:?}", -// primary -// .wallet -// .lock() -// .await -// .wallet_transactions -// .keys() -// .cloned() -// .collect::>() -// ); - -// let _recorded_fee = *zingolib::testutils::assertions::lookup_fees_with_proposal_check( -// &primary, &proposal, txids, -// ) -// .await -// .first() -// .expect("one transaction must have been proposed") -// .as_ref() -// .expect("record must exist"); - -// zingolib::testutils::lightclient::lookup_statuses(&primary, txids.clone()) -// .await -// .map(|status| { -// assert_eq!( -// status, -// Some(ConfirmationStatus::Transmitted(send_height.into())) -// ); -// }); - -// zingolib::testutils::lightclient::lookup_statuses(&secondary, txids.clone()) -// .await -// .map(|status| { -// assert!(status.is_none()); -// }); - -// environment -// .get_connector() -// .clear_incoming_transactions() -// .await -// .unwrap(); -// environment.bump_chain().await; - -// zingolib::testutils::lightclient::lookup_statuses(&primary, txids.clone()) -// .await -// .map(|status| { -// assert_eq!( -// status, -// Some(ConfirmationStatus::Transmitted(send_height.into())) -// ); -// }); +#[tokio::test] +async fn sent_transaction_reorged_into_mempool() { + let darkside_handler = DarksideHandler::new(None); -// zingolib::testutils::lightclient::lookup_statuses(&secondary, txids.clone()) -// .await -// .map(|status| { -// assert!(status.is_none()); -// }); + let server_id = zingolib::config::construct_lightwalletd_uri(Some(format!( + "http://127.0.0.1:{}", + darkside_handler.grpc_port + ))); + prepare_darksidewalletd(server_id.clone(), true) + .await + .unwrap(); -// send_height = 0; + let mut client_manager = + ClientBuilder::new(server_id.clone(), darkside_handler.darkside_dir.clone()); + let regtest_network = RegtestNetwork::all_upgrades_active(); + let mut light_client = client_manager + .build_client(DARKSIDE_SEED.to_string(), 0, true, regtest_network) + .await; + let mut recipient = client_manager + .build_client( + testvectors::seeds::HOSPITAL_MUSEUM_SEED.to_string(), + 1, + true, + regtest_network, + ) + .await; -// primary.do_sync(false).await.unwrap(); + light_client.sync_and_await().await.unwrap(); + assert_eq!( + light_client.do_balance().await, + PoolBalances { + sapling_balance: Some(0), + verified_sapling_balance: Some(0), + spendable_sapling_balance: Some(0), + unverified_sapling_balance: Some(0), + orchard_balance: Some(100000000), + verified_orchard_balance: Some(100000000), + spendable_orchard_balance: Some(100000000), + unverified_orchard_balance: Some(0), + transparent_balance: Some(0) + } + ); + let one_txid = from_inputs::quick_send( + &light_client, + vec![(&get_base_address_macro!(recipient, "unified"), 10_000, None)], + ) + .await + .unwrap(); + println!("{}", one_txid.first()); + recipient.sync_and_await().await.unwrap(); + + let connector = DarksideConnector(server_id.clone()); + let mut streamed_raw_txns = connector.get_incoming_transactions().await.unwrap(); + let raw_tx = streamed_raw_txns.message().await.unwrap().unwrap(); + // There should only be one transaction incoming + assert!(streamed_raw_txns.message().await.unwrap().is_none()); + connector + .stage_transactions_stream(vec![(raw_tx.data.clone(), 4)]) + .await + .unwrap(); + connector.stage_blocks_create(4, 1, 0).await.unwrap(); + update_tree_states_for_transaction(&server_id, raw_tx.clone(), 4).await; + connector.apply_staged(4).await.unwrap(); + tokio::time::sleep(std::time::Duration::from_secs(1)).await; + + recipient.sync_and_await().await.unwrap(); + // light_client.do_sync(false).await.unwrap(); + println!( + "Recipient pre-reorg: {}", + serde_json::to_string_pretty(&recipient.do_balance().await).unwrap() + ); + println!( + "Sender pre-reorg (unsynced): {}", + serde_json::to_string_pretty(&light_client.do_balance().await).unwrap() + ); -// zingolib::testutils::lightclient::lookup_statuses(&primary, txids.clone()) -// .await -// .map(|status| { -// assert_eq!( -// status, -// Some(ConfirmationStatus::Transmitted(send_height.into())) -// ); -// }); + prepare_darksidewalletd(server_id.clone(), true) + .await + .unwrap(); + let connector = DarksideConnector(server_id.clone()); + connector.stage_blocks_create(4, 102, 0).await.unwrap(); + connector.apply_staged(105).await.unwrap(); + tokio::time::sleep(std::time::Duration::from_secs(1)).await; -// // FIXME: -// // let ref_primary: Arc = Arc::new(primary); -// // LightClient::start_mempool_monitor(ref_primary).unwrap(); -// } + recipient.sync_and_await().await.unwrap(); + light_client.sync_and_await().await.unwrap(); + println!( + "Recipient post-reorg: {}", + serde_json::to_string_pretty(&recipient.do_balance().await).unwrap() + ); + println!( + "Sender post-reorg: {}", + serde_json::to_string_pretty(&light_client.do_balance().await).unwrap() + ); + let mut loaded_client = + zingolib::testutils::lightclient::new_client_from_save_buffer(&light_client) + .await + .unwrap(); + loaded_client.sync_and_await().await.unwrap(); + assert_eq!( + loaded_client.do_balance().await.orchard_balance, + Some(100000000) + ); +} From 5ee99f265d8001089b8f791b57f289ad23d1bb81 Mon Sep 17 00:00:00 2001 From: Oscar Pepper Date: Mon, 24 Feb 2025 13:54:45 +0000 Subject: [PATCH 20/26] cleanup more kruft --- zingolib/src/commands.rs | 8 +- zingolib/src/wallet/keys/unified.rs | 61 +---- zingolib/src/wallet/transaction.rs | 1 + .../src/wallet/transaction_records_by_id.rs | 248 ------------------ 4 files changed, 5 insertions(+), 313 deletions(-) diff --git a/zingolib/src/commands.rs b/zingolib/src/commands.rs index c0f403501..d26d190c0 100644 --- a/zingolib/src/commands.rs +++ b/zingolib/src/commands.rs @@ -1440,7 +1440,7 @@ impl Command for SendsToAddressCommand { } } -// FIXME: zingo2 +// TODO: zingo2 // struct SetOptionCommand {} // impl Command for SetOptionCommand { // fn help(&self) -> &'static str { @@ -1524,7 +1524,7 @@ impl Command for SendsToAddressCommand { // } // } -// FIXME: zingo2 +// TODO: zingo2 // struct GetOptionCommand {} // impl Command for GetOptionCommand { // fn help(&self) -> &'static str { @@ -1839,8 +1839,6 @@ impl Command for DeprecatedNoCommand { /// TODO: Add Doc Comment Here! pub fn get_commands() -> HashMap<&'static str, Box> { - // FIXME zingo2, re-impl or delete commented commands - #[allow(unused_mut)] let mut entries: Vec<(&'static str, Box)> = vec![ (("version"), Box::new(GetVersionCommand {})), ("sync", Box::new(SyncCommand {})), @@ -1857,7 +1855,6 @@ pub fn get_commands() -> HashMap<&'static str, Box> { ("addresses", Box::new(AddressCommand {})), ("height", Box::new(HeightCommand {})), ("sendprogress", Box::new(SendProgressCommand {})), - // ("setoption", Box::new(SetOptionCommand {})), ("valuetransfers", Box::new(ValueTransfersCommand {})), ("transactions", Box::new(TransactionsCommand {})), ("value_to_address", Box::new(ValueToAddressCommand {})), @@ -1867,7 +1864,6 @@ pub fn get_commands() -> HashMap<&'static str, Box> { "memobytes_to_address", Box::new(MemoBytesToAddressCommand {}), ), - // ("getoption", Box::new(GetOptionCommand {})), ("exportufvk", Box::new(ExportUfvkCommand {})), ("info", Box::new(InfoCommand {})), ("updatecurrentprice", Box::new(UpdateCurrentPriceCommand {})), diff --git a/zingolib/src/wallet/keys/unified.rs b/zingolib/src/wallet/keys/unified.rs index 5e8e763d4..8777b9887 100644 --- a/zingolib/src/wallet/keys/unified.rs +++ b/zingolib/src/wallet/keys/unified.rs @@ -2,7 +2,6 @@ use std::sync::atomic; use std::{ - collections::HashSet, io::{self, Read, Write}, sync::atomic::AtomicBool, }; @@ -474,17 +473,6 @@ fn read_write_receiver_selections() { } impl WalletCapability { - // FIXME: zingo2 - #[allow(dead_code)] - pub(crate) fn get_ua_from_contained_transparent_receiver( - &self, - receiver: &TransparentAddress, - ) -> Option { - self.unified_addresses - .iter() - .find(|ua| ua.transparent() == Some(receiver)) - .cloned() - } /// TODO: Add Doc Comment Here! pub fn addresses(&self) -> &AppendOnlyVec { &self.unified_addresses @@ -660,40 +648,6 @@ impl WalletCapability { Self::new_from_seed(config, &bip39_seed, position) } - /// external here refers to HD keys: - /// - /// where external and internal were inherited from the BIP44 conventions - // FIXME: zingo2 - #[allow(dead_code)] - fn get_external_taddrs(&self, chain: &crate::config::ChainType) -> HashSet { - self.unified_addresses - .iter() - .filter_map(|address| { - address.transparent().and_then(|transparent_receiver| { - if let zcash_primitives::legacy::TransparentAddress::PublicKeyHash(hash) = - transparent_receiver - { - Some(super::ToBase58Check::to_base58check( - hash.as_slice(), - &chain.b58_pubkey_address_prefix(), - &[], - )) - } else { - None - } - }) - }) - .collect() - } - - // FIXME: zingo2 - #[allow(dead_code)] - pub(crate) fn get_taddrs(&self, chain: &crate::config::ChainType) -> HashSet { - self.get_external_taddrs(chain) - .union(&self.get_rejection_address_set(chain)) - .cloned() - .collect() - } /// TODO: Add Doc Comment Here! pub fn first_sapling_address(&self) -> sapling_crypto::PaymentAddress { // This index is dangerous, but all ways to instantiate a UnifiedSpendAuthority @@ -985,11 +939,11 @@ impl Fvk for sapling_crypto::zip32::DiversifiableFullViewingKey { } } mod rejection { - use std::{collections::HashSet, sync::Arc}; + use std::sync::Arc; use append_only_vec::AppendOnlyVec; use zcash_client_backend::wallet::TransparentAddressMetadata; - use zcash_keys::{encoding::AddressCodec, keys::DerivationError}; + use zcash_keys::keys::DerivationError; use zcash_primitives::legacy::{ keys::{AccountPubKey, NonHardenedChildIndex, TransparentKeyScope}, TransparentAddress, @@ -1028,16 +982,5 @@ mod rejection { ) -> &Arc> { &self.rejection_addresses } - // FIXME: zingo2 - #[allow(dead_code)] - pub(crate) fn get_rejection_address_set( - &self, - chain: &crate::config::ChainType, - ) -> HashSet { - self.rejection_addresses - .iter() - .map(|(transparent_address, _metadata)| transparent_address.encode(chain)) - .collect() - } } } diff --git a/zingolib/src/wallet/transaction.rs b/zingolib/src/wallet/transaction.rs index 63197012b..2bd6d99aa 100644 --- a/zingolib/src/wallet/transaction.rs +++ b/zingolib/src/wallet/transaction.rs @@ -63,6 +63,7 @@ impl LightWallet { /// Calculate the fee for a transaction in the wallet. /// /// Fails if transparent spends are not found in the wallet. + // FIXME: zingo2, write integration tests pub fn calculate_transaction_fee( &self, transaction: &WalletTransaction, diff --git a/zingolib/src/wallet/transaction_records_by_id.rs b/zingolib/src/wallet/transaction_records_by_id.rs index ffaee7a7b..ed5411547 100644 --- a/zingolib/src/wallet/transaction_records_by_id.rs +++ b/zingolib/src/wallet/transaction_records_by_id.rs @@ -219,28 +219,6 @@ impl TransactionRecordsById { }); } - // FIXME: zingo2 this should be a user API so not to lose failed sends with important memos etc. - // /// Invalidates all those transactions which were broadcast but never 'confirmed' accepted by a miner. - // pub(crate) fn clear_expired_mempool(&mut self, latest_height: u64) { - // // Pending window: How long to wait past the chain tip before clearing a pending - // let pending_window = 2; - // let cutoff = BlockHeight::from_u32((latest_height.saturating_sub(pending_window)) as u32); - - // let txids_to_remove = self - // .iter() - // .filter(|(_, transaction_metadata)| { - // transaction_metadata.status.is_pending_before(&cutoff) - // }) // this transaction was submitted to the mempool before the cutoff and has not been confirmed. we deduce that it has expired. - // .map(|(_, transaction_metadata)| transaction_metadata.txid) - // .collect::>(); - - // txids_to_remove - // .iter() - // .for_each(|t| println!("Removing expired mempool tx {}", t)); - - // self.invalidate_transactions(txids_to_remove); - // } - /// TODO: Add Doc Comment Here! #[allow(deprecated)] #[deprecated(note = "uses unstable deprecated functions")] @@ -481,232 +459,6 @@ mod tests { use sapling_crypto::note_encryption::SaplingDomain; use zcash_client_backend::{wallet::ReceivedNote, ShieldedProtocol}; - // FIXME: zingo2 test with integration tests - // #[test] - // fn calculate_transaction_fee() { - // let mut sapling_nullifier_builder = SaplingNullifierBuilder::new(); - // let mut orchard_nullifier_builder = OrchardNullifierBuilder::new(); - - // let sent_transaction_record = TransactionRecordBuilder::default() - // .status(Confirmed(15.into())) - // .spent_sapling_nullifiers(sapling_nullifier_builder.assign_unique_nullifier().clone()) - // .spent_sapling_nullifiers(sapling_nullifier_builder.assign_unique_nullifier().clone()) - // .spent_orchard_nullifiers(orchard_nullifier_builder.assign_unique_nullifier().clone()) - // .spent_orchard_nullifiers(orchard_nullifier_builder.assign_unique_nullifier().clone()) - // .transparent_outputs(TransparentOutputBuilder::default()) // value 100_000 - // .sapling_notes(SaplingNoteBuilder::default()) // value 200_000 - // .orchard_notes(OrchardNoteBuilder::default()) // value 800_000 - // .outgoing_tx_data(OutgoingTxDataBuilder::default()) // value 50_000 - // .build(); - // let sent_txid = sent_transaction_record.txid; - // let first_sapling_nullifier = sent_transaction_record.spent_sapling_nullifiers[0]; - // let second_sapling_nullifier = sent_transaction_record.spent_sapling_nullifiers[1]; - // let first_orchard_nullifier = sent_transaction_record.spent_orchard_nullifiers[0]; - // let second_orchard_nullifier = sent_transaction_record.spent_orchard_nullifiers[1]; - // // t-note + s-note + o-note + outgoing_tx_data - // let expected_output_value: u64 = 100_000 + 200_000 + 800_000 + 50_000; // 1_150_000 - - // let spent_in_sent_txid = (sent_txid, Confirmed(15.into())); - // let first_received_transaction_record = TransactionRecordBuilder::default() - // .randomize_txid() - // .status(Confirmed(5.into())) - // .sapling_notes(spent_sapling_note_builder( - // 175_000, - // spent_in_sent_txid, - // &first_sapling_nullifier, - // )) - // .sapling_notes(spent_sapling_note_builder( - // 325_000, - // spent_in_sent_txid, - // &second_sapling_nullifier, - // )) - // .orchard_notes(spent_orchard_note_builder( - // 500_000, - // spent_in_sent_txid, - // &first_orchard_nullifier, - // )) - // .transparent_outputs(spent_transparent_output_builder(30_000, spent_in_sent_txid)) // 100_000 - // .sapling_notes( - // SaplingNoteBuilder::default() - // .spending_tx_status(Some((random_txid(), Confirmed(12.into())))) - // .to_owned(), - // ) - // .orchard_notes(OrchardNoteBuilder::default()) // 800_000 - // .set_output_indexes() - // .build(); - // let second_received_transaction_record = TransactionRecordBuilder::default() - // .randomize_txid() - // .status(Confirmed(7.into())) - // .orchard_notes(spent_orchard_note_builder( - // 200_000, - // spent_in_sent_txid, - // &second_orchard_nullifier, - // )) - // .transparent_outputs(TransparentOutputBuilder::default()) - // .sapling_notes(SaplingNoteBuilder::default().clone()) - // .orchard_notes( - // OrchardNoteBuilder::default() - // .spending_tx_status(Some((random_txid(), Confirmed(13.into())))) - // .to_owned(), - // ) - // .set_output_indexes() - // .build(); - // // s-note1 + s-note2 + o-note1 + o-note2 + sent_transaction.total_transparent_value_spent - // let expected_spend_value: u64 = 175_000 + 325_000 + 500_000 + 200_000 + 30_000; - - // let mut transaction_records_by_id = TransactionRecordsById::default(); - // transaction_records_by_id.insert_transaction_record(sent_transaction_record); - // transaction_records_by_id.insert_transaction_record(first_received_transaction_record); - // transaction_records_by_id.insert_transaction_record(second_received_transaction_record); - - // let fee = transaction_records_by_id - // .calculate_transaction_fee(transaction_records_by_id.get(&sent_txid).unwrap()) - // .unwrap(); - // assert_eq!(expected_spend_value - expected_output_value, fee); - // } - - // mod calculate_transaction_fee_errors { - // use crate::{ - // mocks::{ - // nullifier::{OrchardNullifierBuilder, SaplingNullifierBuilder}, - // orchard_note::OrchardCryptoNoteBuilder, - // SaplingCryptoNoteBuilder, - // }, - // wallet::{ - // data::mocks::OutgoingTxDataBuilder, - // error::KindError, - // notes::{ - // orchard::mocks::OrchardNoteBuilder, sapling::mocks::SaplingNoteBuilder, - // transparent::mocks::TransparentOutputBuilder, - // }, - // transaction_record::mocks::TransactionRecordBuilder, - // transaction_records_by_id::{ - // tests::spent_transparent_output_builder, TransactionRecordsById, - // }, - // }, - // }; - - // use zingo_status::confirmation_status::ConfirmationStatus::Confirmed; - - // #[test] - // fn spend_not_found() { - // let mut sapling_nullifier_builder = SaplingNullifierBuilder::new(); - // let mut orchard_nullifier_builder = OrchardNullifierBuilder::new(); - - // let sent_transaction_record = TransactionRecordBuilder::default() - // .status(Confirmed(15.into())) - // .spent_sapling_nullifiers( - // sapling_nullifier_builder.assign_unique_nullifier().clone(), - // ) - // .spent_orchard_nullifiers( - // orchard_nullifier_builder.assign_unique_nullifier().clone(), - // ) - // .outgoing_tx_data(OutgoingTxDataBuilder::default()) - // .transparent_outputs(TransparentOutputBuilder::default()) - // .sapling_notes(SaplingNoteBuilder::default()) - // .orchard_notes(OrchardNoteBuilder::default()) - // .build(); - // let sent_txid = sent_transaction_record.txid; - // let sapling_nullifier = sent_transaction_record.spent_sapling_nullifiers[0]; - - // let received_transaction_record = TransactionRecordBuilder::default() - // .randomize_txid() - // .status(Confirmed(5.into())) - // .sapling_notes( - // SaplingNoteBuilder::default() - // .note( - // SaplingCryptoNoteBuilder::default() - // .value(sapling_crypto::value::NoteValue::from_raw(175_000)) - // .to_owned(), - // ) - // .spending_tx_status(Some((sent_txid, Confirmed(15.into())))) - // .nullifier(Some(sapling_nullifier)) - // .to_owned(), - // ) - // .build(); - - // let mut transaction_records_by_id = TransactionRecordsById::default(); - // transaction_records_by_id.insert_transaction_record(sent_transaction_record); - // transaction_records_by_id.insert_transaction_record(received_transaction_record); - - // let fee = transaction_records_by_id - // .calculate_transaction_fee(transaction_records_by_id.get(&sent_txid).unwrap()); - // assert!(matches!(fee, Err(KindError::OrchardSpendNotFound(_)))); - // } - // #[test] - // fn received_transaction() { - // let transaction_record = TransactionRecordBuilder::default() - // .status(Confirmed(15.into())) - // .transparent_outputs(TransparentOutputBuilder::default()) - // .sapling_notes(SaplingNoteBuilder::default()) - // .orchard_notes(OrchardNoteBuilder::default()) - // .build(); - // let sent_txid = transaction_record.txid; - - // let mut transaction_records_by_id = TransactionRecordsById::default(); - // transaction_records_by_id.insert_transaction_record(transaction_record); - - // let fee = transaction_records_by_id - // .calculate_transaction_fee(transaction_records_by_id.get(&sent_txid).unwrap()); - // assert!(matches!(fee, Err(KindError::ReceivedTransaction))); - // } - // #[test] - // fn outgoing_tx_data_but_no_spends_found() { - // let transaction_record = TransactionRecordBuilder::default() - // .status(Confirmed(15.into())) - // .transparent_outputs(TransparentOutputBuilder::default()) - // .sapling_notes(SaplingNoteBuilder::default()) - // .orchard_notes(OrchardNoteBuilder::default()) - // .outgoing_tx_data(OutgoingTxDataBuilder::default()) - // .build(); - // let sent_txid = transaction_record.txid; - - // let mut transaction_records_by_id = TransactionRecordsById::default(); - // transaction_records_by_id.insert_transaction_record(transaction_record); - - // let fee = transaction_records_by_id - // .calculate_transaction_fee(transaction_records_by_id.get(&sent_txid).unwrap()); - // assert!(matches!(fee, Err(KindError::OutgoingWithoutSpends))); - // } - // #[test] - // fn transparent_spends_not_fully_synced() { - // let transaction_record = TransactionRecordBuilder::default() - // .status(Confirmed(15.into())) - // .orchard_notes( - // OrchardNoteBuilder::default() - // .note( - // OrchardCryptoNoteBuilder::default() - // .value(orchard::value::NoteValue::from_raw(50_000)) - // .to_owned(), - // ) - // .to_owned(), - // ) - // .build(); - // let sent_txid = transaction_record.txid; - // let spent_in_sent_txid = (sent_txid, Confirmed(15.into())); - // let transparent_funding_tx = TransactionRecordBuilder::default() - // .randomize_txid() - // .status(Confirmed(7.into())) - // .transparent_outputs(spent_transparent_output_builder(20_000, spent_in_sent_txid)) - // .set_output_indexes() - // .build(); - - // let mut transaction_records_by_id = TransactionRecordsById::default(); - // transaction_records_by_id.insert_transaction_record(transaction_record); - // transaction_records_by_id.insert_transaction_record(transparent_funding_tx); - - // let fee = transaction_records_by_id - // .calculate_transaction_fee(transaction_records_by_id.get(&sent_txid).unwrap()); - // assert!(matches!( - // fee, - // Err(KindError::FeeUnderflow { - // input_value: _, - // explicit_output_value: _, - // }) - // )); - // } - // } - #[test] fn get_received_spendable_note_from_identifier() { let mut trbid = TransactionRecordsById::new(); From f994b12ea0b03e33cdd76eb28034d13569aae060 Mon Sep 17 00:00:00 2001 From: Oscar Pepper Date: Tue, 25 Feb 2025 02:00:55 +0000 Subject: [PATCH 21/26] fixing more concrete tests --- .../tests/network_interruption_tests.rs | 227 +++--- libtonode-tests/tests/concrete.rs | 711 ++++++++++-------- zingolib/src/commands.rs | 14 +- zingolib/src/lightclient/sync.rs | 6 + zingolib/src/testutils.rs | 17 +- zingolib/src/wallet/data.rs | 30 +- zingolib/src/wallet/describe.rs | 6 +- zingolib/src/wallet/summary.rs | 42 +- 8 files changed, 561 insertions(+), 492 deletions(-) diff --git a/darkside-tests/tests/network_interruption_tests.rs b/darkside-tests/tests/network_interruption_tests.rs index b513fa9ed..6f2b344d2 100644 --- a/darkside-tests/tests/network_interruption_tests.rs +++ b/darkside-tests/tests/network_interruption_tests.rs @@ -1,21 +1,28 @@ use std::{ collections::HashMap, - sync::{atomic::AtomicBool, Arc, Mutex}, + sync::{ + atomic::{self, AtomicBool}, + Arc, Mutex, + }, + time::Duration, }; use darkside_tests::{ constants::DARKSIDE_SEED, utils::{ - create_chainbuild_file, prepare_darksidewalletd, + create_chainbuild_file, load_chainbuild_file, prepare_darksidewalletd, scenarios::{DarksideEnvironment, DarksideSender}, DarksideHandler, }, }; use zcash_client_backend::{PoolType, ShieldedProtocol}; -use zingolib::config::RegtestNetwork; -use zingolib::get_base_address_macro; -use zingolib::testutils::{scenarios::setup::ClientBuilder, start_proxy_and_connect_lightclient}; +use zingolib::{config::RegtestNetwork, lightclient::PoolBalances}; +use zingolib::{get_base_address_macro, wallet::data::summaries::TransactionSummaryInterface as _}; +use zingolib::{ + testutils::{scenarios::setup::ClientBuilder, start_proxy_and_connect_lightclient}, + wallet::transaction_record::{SendType, TransactionKind}, +}; #[ignore] #[tokio::test] @@ -121,111 +128,105 @@ async fn shielded_note_marked_as_change_chainbuild() { // json::stringify_pretty(scenario.get_lightclient(0).do_list_notes(true).await, 4) // ); } -// // FIXME: -// #[ignore] -// #[tokio::test] -// async fn shielded_note_marked_as_change_test() { -// const BLOCKCHAIN_HEIGHT: u64 = 20_000; -// let transaction_set = load_chainbuild_file("shielded_note_marked_as_change"); -// let mut scenario = DarksideEnvironment::default_faucet_recipient(PoolType::Shielded( -// ShieldedProtocol::Sapling, -// )) -// .await; - -// // stage a send to self every thousand blocks -// for thousands_blocks_count in 1..BLOCKCHAIN_HEIGHT / 1000 { -// scenario -// .stage_and_apply_blocks(thousands_blocks_count * 1000 - 2, 0) -// .await; -// scenario.stage_next_transaction(&transaction_set).await; -// scenario -// .apply_blocks(thousands_blocks_count * 1000 - 1) -// .await; -// scenario.stage_next_transaction(&transaction_set).await; -// } -// // stage and apply final blocks -// scenario.stage_and_apply_blocks(BLOCKCHAIN_HEIGHT, 0).await; - -// // setup gRPC network interrupt conditions -// let mut conditional_logic = -// HashMap::<&'static str, Box) + Send + Sync>>::new(); -// // conditional_logic.insert( -// // "get_block_range", -// // Box::new(|online: &Arc| { -// // println!("Turning off, as we received get_block_range call"); -// // online.store(false, Ordering::Relaxed); -// // }), -// // ); -// conditional_logic.insert( -// "get_tree_state", -// Box::new(|online: Arc| { -// println!("Turning off, as we received get_tree_state call"); -// online.store(false, Ordering::Relaxed); -// }), -// ); -// conditional_logic.insert( -// "get_transaction", -// Box::new(|online: Arc| { -// println!("Turning off, as we received get_transaction call"); -// online.store(false, Ordering::Relaxed); -// }), -// ); - -// let (_proxy_handle, proxy_status) = -// start_proxy_and_connect_lightclient(scenario.get_lightclient(0), conditional_logic); -// tokio::task::spawn(async move { -// loop { -// sleep(Duration::from_secs(5)).await; -// proxy_status.store(true, std::sync::atomic::Ordering::Relaxed); -// println!("Set proxy status to true"); -// } -// }); - -// // start test -// scenario.get_lightclient(0).do_sync(false).await.unwrap(); - -// // debug info -// println!("do list_notes:"); -// println!( -// "{}", -// json::stringify_pretty(scenario.get_lightclient(0).do_list_notes(true).await, 4) -// ); -// println!("do list tx summaries:"); -// dbg!( -// scenario -// .get_lightclient(0) -// .sorted_value_transfers(true) -// .await -// ); - -// // assert the balance is correct -// assert_eq!( -// scenario.get_lightclient(0).do_balance().await, -// PoolBalances { -// sapling_balance: Some(0), -// verified_sapling_balance: Some(0), -// spendable_sapling_balance: Some(0), -// unverified_sapling_balance: Some(0), -// orchard_balance: Some(760_000), -// verified_orchard_balance: Some(760_000), -// unverified_orchard_balance: Some(0), -// spendable_orchard_balance: Some(760_000), -// transparent_balance: Some(0), -// } -// ); -// // assert all fees are 10000 zats -// let transaction_summaries = scenario.get_lightclient(0).transaction_summaries().await; -// for summary in transaction_summaries.iter() { -// if let Some(fee) = summary.fee() { -// assert_eq!(fee, 10_000); -// } -// } -// // assert the number of shields are correct -// assert_eq!( -// transaction_summaries -// .iter() -// .filter(|summary| summary.kind() == TransactionKind::Sent(SendType::Shield)) -// .count(), -// (BLOCKCHAIN_HEIGHT / 1000 - 1) as usize -// ); -// } + +#[ignore] +#[tokio::test] +async fn shielded_note_marked_as_change_test() { + const BLOCKCHAIN_HEIGHT: u64 = 20_000; + let transaction_set = load_chainbuild_file("shielded_note_marked_as_change"); + let mut scenario = DarksideEnvironment::default_faucet_recipient(PoolType::Shielded( + ShieldedProtocol::Sapling, + )) + .await; + + // stage a send to self every thousand blocks + for thousands_blocks_count in 1..BLOCKCHAIN_HEIGHT / 1000 { + scenario + .stage_and_apply_blocks(thousands_blocks_count * 1000 - 2, 0) + .await; + scenario.stage_next_transaction(&transaction_set).await; + scenario + .apply_blocks(thousands_blocks_count * 1000 - 1) + .await; + scenario.stage_next_transaction(&transaction_set).await; + } + // stage and apply final blocks + scenario.stage_and_apply_blocks(BLOCKCHAIN_HEIGHT, 0).await; + + // setup gRPC network interrupt conditions + let mut conditional_logic = + HashMap::<&'static str, Box) + Send + Sync>>::new(); + // conditional_logic.insert( + // "get_block_range", + // Box::new(|online: &Arc| { + // println!("Turning off, as we received get_block_range call"); + // online.store(false, Ordering::Relaxed); + // }), + // ); + conditional_logic.insert( + "get_tree_state", + Box::new(|online: Arc| { + println!("Turning off, as we received get_tree_state call"); + online.store(false, atomic::Ordering::Relaxed); + }), + ); + conditional_logic.insert( + "get_transaction", + Box::new(|online: Arc| { + println!("Turning off, as we received get_transaction call"); + online.store(false, atomic::Ordering::Relaxed); + }), + ); + + let (_proxy_handle, proxy_status) = + start_proxy_and_connect_lightclient(scenario.get_lightclient(0), conditional_logic); + tokio::task::spawn(async move { + loop { + tokio::time::sleep(Duration::from_secs(5)).await; + proxy_status.store(true, std::sync::atomic::Ordering::Relaxed); + println!("Set proxy status to true"); + } + }); + + // start test + scenario.get_lightclient(0).sync_and_await().await.unwrap(); + + println!("value transfers:"); + dbg!( + scenario + .get_lightclient(0) + .sorted_value_transfers(true) + .await + ); + + // assert the balance is correct + assert_eq!( + scenario.get_lightclient(0).do_balance().await, + PoolBalances { + sapling_balance: Some(0), + verified_sapling_balance: Some(0), + spendable_sapling_balance: Some(0), + unverified_sapling_balance: Some(0), + orchard_balance: Some(760_000), + verified_orchard_balance: Some(760_000), + unverified_orchard_balance: Some(0), + spendable_orchard_balance: Some(760_000), + transparent_balance: Some(0), + } + ); + // assert all fees are 10000 zats + let transaction_summaries = scenario.get_lightclient(0).transaction_summaries().await; + for summary in transaction_summaries.iter() { + if let Some(fee) = summary.fee() { + assert_eq!(fee, 10_000); + } + } + // assert the number of shields are correct + assert_eq!( + transaction_summaries + .iter() + .filter(|summary| summary.kind() == TransactionKind::Sent(SendType::Shield)) + .count(), + (BLOCKCHAIN_HEIGHT / 1000 - 1) as usize + ); +} diff --git a/libtonode-tests/tests/concrete.rs b/libtonode-tests/tests/concrete.rs index 25bf5b3f3..ac5d5852e 100644 --- a/libtonode-tests/tests/concrete.rs +++ b/libtonode-tests/tests/concrete.rs @@ -2,6 +2,7 @@ use json::JsonValue; use testvectors::{block_rewards, seeds::HOSPITAL_MUSEUM_SEED, BASE_HEIGHT}; +use zcash_address::unified::Fvk; use zcash_primitives::transaction::components::amount::NonNegativeAmount; use zcash_primitives::transaction::fees::zip317::MINIMUM_FEE; use zingolib::config::RegtestNetwork; @@ -9,94 +10,105 @@ use zingolib::lightclient::PoolBalances; use zingolib::testutils::lightclient::from_inputs; use zingolib::testutils::{increase_height_and_wait_for_client, scenarios}; use zingolib::utils::conversion::address_from_str; +use zingolib::wallet::keys::unified::UnifiedKeyStore; use zingolib::wallet::propose::ProposeSendError; +use zingolib::wallet::summary::{CoinSummary, NoteSummary}; use zingolib::{check_client_balances, get_base_address_macro}; -// FIXME: -// fn check_expected_balance_with_fvks( -// fvks: &Vec<&Fvk>, -// balance: PoolBalances, -// o_expect: u64, -// s_expect: u64, -// t_expect: u64, -// ) { -// for fvk in fvks { -// match fvk { -// Fvk::Sapling(_) => { -// assert_eq!(balance.sapling_balance.unwrap(), s_expect); -// assert_eq!(balance.verified_sapling_balance.unwrap(), s_expect); -// assert_eq!(balance.unverified_sapling_balance.unwrap(), s_expect); -// } -// Fvk::Orchard(_) => { -// assert_eq!(balance.orchard_balance.unwrap(), o_expect); -// assert_eq!(balance.verified_orchard_balance.unwrap(), o_expect); -// assert_eq!(balance.unverified_orchard_balance.unwrap(), o_expect); -// } -// Fvk::P2pkh(_) => { -// assert_eq!(balance.transparent_balance.unwrap(), t_expect); -// } -// _ => panic!(), -// } -// } -// } - -// #[allow(clippy::too_many_arguments)] -// // FIXME: -// fn check_view_capability_bounds( -// balance: &PoolBalances, -// watch_wc: &WalletCapability, -// fvks: &[&Fvk], -// orchard_fvk: &Fvk, -// sapling_fvk: &Fvk, -// transparent_fvk: &Fvk, -// sent_o_value: Option, -// sent_s_value: Option, -// sent_t_value: Option, -// notes: &JsonValue, -// ) { -// let UnifiedKeyStore::View(ufvk) = &watch_wc.unified_key_store else { -// panic!("should be viewing key!") -// }; -// //Orchard -// if !fvks.contains(&orchard_fvk) { -// assert!(ufvk.orchard().is_none()); -// assert_eq!(balance.orchard_balance, None); -// assert_eq!(balance.verified_orchard_balance, None); -// assert_eq!(balance.unverified_orchard_balance, None); -// assert_eq!(notes["unspent_orchard_notes"].members().count(), 0); -// } else { -// assert!(ufvk.orchard().is_some()); -// assert_eq!(balance.orchard_balance, sent_o_value); -// assert_eq!(balance.verified_orchard_balance, sent_o_value); -// assert_eq!(balance.unverified_orchard_balance, Some(0)); -// // assert 1 Orchard note, or 2 notes if a dummy output is included -// let orchard_notes_count = notes["unspent_orchard_notes"].members().count(); -// assert!((1..=2).contains(&orchard_notes_count)); -// } -// //Sapling -// if !fvks.contains(&sapling_fvk) { -// assert!(ufvk.sapling().is_none()); -// assert_eq!(balance.sapling_balance, None); -// assert_eq!(balance.verified_sapling_balance, None); -// assert_eq!(balance.unverified_sapling_balance, None); -// assert_eq!(notes["unspent_sapling_notes"].members().count(), 0); -// } else { -// assert!(ufvk.sapling().is_some()); -// assert_eq!(balance.sapling_balance, sent_s_value); -// assert_eq!(balance.verified_sapling_balance, sent_s_value); -// assert_eq!(balance.unverified_sapling_balance, Some(0)); -// assert_eq!(notes["unspent_sapling_notes"].members().count(), 1); -// } -// if !fvks.contains(&transparent_fvk) { -// assert!(ufvk.transparent().is_none()); -// assert_eq!(balance.transparent_balance, None); -// assert_eq!(notes["utxos"].members().count(), 0); -// } else { -// assert!(ufvk.transparent().is_some()); -// assert_eq!(balance.transparent_balance, sent_t_value); -// assert_eq!(notes["utxos"].members().count(), 1); -// } -// } +fn check_expected_balance_with_fvks( + fvks: &Vec<&Fvk>, + balance: PoolBalances, + o_expect: u64, + s_expect: u64, + t_expect: u64, +) { + for fvk in fvks { + match fvk { + Fvk::Sapling(_) => { + assert_eq!(balance.sapling_balance.unwrap(), s_expect); + assert_eq!(balance.verified_sapling_balance.unwrap(), s_expect); + assert_eq!(balance.unverified_sapling_balance.unwrap(), s_expect); + } + Fvk::Orchard(_) => { + assert_eq!(balance.orchard_balance.unwrap(), o_expect); + assert_eq!(balance.verified_orchard_balance.unwrap(), o_expect); + assert_eq!(balance.unverified_orchard_balance.unwrap(), o_expect); + } + Fvk::P2pkh(_) => { + assert_eq!(balance.transparent_balance.unwrap(), t_expect); + } + _ => panic!(), + } + } +} + +#[allow(clippy::too_many_arguments)] +fn check_view_capability_bounds( + balance: &PoolBalances, + unified_key_store: &UnifiedKeyStore, + fvks: &[&Fvk], + orchard_fvk: &Fvk, + sapling_fvk: &Fvk, + transparent_fvk: &Fvk, + sent_o_value: Option, + sent_s_value: Option, + sent_t_value: Option, + orchard_notes: &[NoteSummary], + sapling_notes: &[NoteSummary], + transparent_coins: &[CoinSummary], +) { + let UnifiedKeyStore::View(ufvk) = unified_key_store else { + panic!("should be viewing key!") + }; + //Orchard + if !fvks.contains(&orchard_fvk) { + assert!(ufvk.orchard().is_none()); + assert_eq!(balance.orchard_balance, None); + assert_eq!(balance.verified_orchard_balance, None); + assert_eq!(balance.unverified_orchard_balance, None); + assert_eq!(orchard_notes.len(), 0); + } else { + assert!(ufvk.orchard().is_some()); + assert_eq!(balance.orchard_balance, sent_o_value); + assert_eq!(balance.verified_orchard_balance, sent_o_value); + assert_eq!(balance.unverified_orchard_balance, Some(0)); + // assert 1 Orchard note, or 2 notes if a dummy output is included + let orchard_notes_count = orchard_notes + .iter() + .filter(|note| note.spend_status.is_unspent()) + .count(); + assert!((1..=2).contains(&orchard_notes_count)); + } + //Sapling + if !fvks.contains(&sapling_fvk) { + assert!(ufvk.sapling().is_none()); + assert_eq!(balance.sapling_balance, None); + assert_eq!(balance.verified_sapling_balance, None); + assert_eq!(balance.unverified_sapling_balance, None); + assert_eq!(sapling_notes.len(), 0); + } else { + assert!(ufvk.sapling().is_some()); + assert_eq!(balance.sapling_balance, sent_s_value); + assert_eq!(balance.verified_sapling_balance, sent_s_value); + assert_eq!(balance.unverified_sapling_balance, Some(0)); + assert_eq!( + sapling_notes + .iter() + .filter(|note| note.spend_status.is_unspent()) + .count(), + 1 + ); + } + if !fvks.contains(&transparent_fvk) { + assert!(ufvk.transparent().is_none()); + assert_eq!(balance.transparent_balance, None); + assert_eq!(transparent_coins.len(), 0); + } else { + assert!(ufvk.transparent().is_some()); + assert_eq!(balance.transparent_balance, sent_t_value); + assert_eq!(transparent_coins.len(), 1); + } +} mod fast { use std::str::FromStr as _; @@ -1245,101 +1257,140 @@ mod fast { } } mod slow { + use pepper_sync::wallet::{OrchardNote, OutputInterface, SaplingNote, TransparentCoin}; use shardtree::store::ShardStore; use zcash_client_backend::{PoolType, ShieldedProtocol}; use zcash_primitives::transaction::fees::zip317::MARGINAL_FEE; + use zingolib::config::ChainType; use zingolib::lightclient::send::send_with_proposal::QuickSendError; + use zingolib::testutils::build_fvk_client; use zingolib::testutils::lightclient::{from_inputs, get_fees_paid_by_client}; + use zingolib::wallet::data::summaries::TransactionSummaryInterface; + use zingolib::wallet::error::{KeyError, WalletError}; + use zingolib::UAReceivers; use super::*; - // FIXME: - // #[tokio::test] - // async fn zero_value_receipts() { - // let (regtest_manager, _cph, faucet, recipient, _txid) = - // scenarios::faucet_funded_recipient_default(100_000).await; + #[tokio::test] + async fn zero_value_receipts() { + let (regtest_manager, _cph, faucet, mut recipient, _txid) = + scenarios::faucet_funded_recipient_default(100_000).await; - // let sent_value = 0; - // let _sent_transaction_id = from_inputs::quick_send( - // &faucet, - // vec![( - // &get_base_address_macro!(recipient, "unified"), - // sent_value, - // None, - // )], - // ) - // .await - // .unwrap(); + let sent_value = 0; + let _sent_transaction_id = from_inputs::quick_send( + &faucet, + vec![( + &get_base_address_macro!(recipient, "unified"), + sent_value, + None, + )], + ) + .await + .unwrap(); - // zingolib::testutils::increase_height_and_wait_for_client(®test_manager, &recipient, 5) - // .await - // .unwrap(); - // let _sent_transaction_id = from_inputs::quick_send( - // &recipient, - // vec![(&get_base_address_macro!(faucet, "unified"), 1000, None)], - // ) - // .await - // .unwrap(); - // zingolib::testutils::increase_height_and_wait_for_client(®test_manager, &recipient, 5) - // .await - // .unwrap(); + zingolib::testutils::increase_height_and_wait_for_client( + ®test_manager, + &mut recipient, + 5, + ) + .await + .unwrap(); + let _sent_transaction_id = from_inputs::quick_send( + &recipient, + vec![(&get_base_address_macro!(faucet, "unified"), 1000, None)], + ) + .await + .unwrap(); + zingolib::testutils::increase_height_and_wait_for_client( + ®test_manager, + &mut recipient, + 5, + ) + .await + .unwrap(); - // println!("{}", recipient.do_list_transactions().await.pretty(4)); - // println!( - // "{}", - // serde_json::to_string_pretty(&recipient.do_balance().await).unwrap() - // ); - // println!( - // "{}", - // JsonValue::from(recipient.sorted_value_transfers(true).await).pretty(4) - // ); - // } - // #[tokio::test] - // async fn zero_value_change() { - // // 1. Send an incoming transaction to fill the wallet - // let value = 100_000; - // let (regtest_manager, _cph, faucet, recipient, _txid) = - // scenarios::faucet_funded_recipient_default(value).await; + println!( + "{}", + serde_json::to_string_pretty(&recipient.do_balance().await).unwrap() + ); + println!( + "{}", + JsonValue::from(recipient.sorted_value_transfers(true).await).pretty(4) + ); + } + #[tokio::test] + async fn zero_value_change() { + let value = 100_000; + let (regtest_manager, _cph, faucet, mut recipient, _txid) = + scenarios::faucet_funded_recipient_default(value).await; - // let sent_value = value - u64::from(MINIMUM_FEE); - // let sent_transaction_id = from_inputs::quick_send( - // &recipient, - // vec![( - // &get_base_address_macro!(faucet, "unified"), - // sent_value, - // None, - // )], - // ) - // .await - // .unwrap() - // .first() - // .to_string(); + let sent_value = value - u64::from(MINIMUM_FEE); + let sent_transaction_id = from_inputs::quick_send( + &recipient, + vec![( + &get_base_address_macro!(faucet, "unified"), + sent_value, + None, + )], + ) + .await + .unwrap() + .first() + .to_string(); - // zingolib::testutils::increase_height_and_wait_for_client(®test_manager, &recipient, 5) - // .await - // .unwrap(); + zingolib::testutils::increase_height_and_wait_for_client( + ®test_manager, + &mut recipient, + 5, + ) + .await + .unwrap(); - // let notes = recipient.do_list_notes(true).await; - // assert_eq!(notes["unspent_sapling_notes"].len(), 0); - // assert_eq!(notes["pending_sapling_notes"].len(), 0); - // assert_eq!(notes["unspent_orchard_notes"].len(), 1); - // assert_eq!(notes["pending_orchard_notes"].len(), 0); - // assert_eq!(notes["utxos"].len(), 0); - // assert_eq!(notes["pending_utxos"].len(), 0); + let recipient_wallet = recipient.wallet.lock().await; + let transparent_coins = recipient_wallet.wallet_outputs::(); + assert_eq!(transparent_coins.len(), 0); + let sapling_notes = recipient_wallet.wallet_outputs::(); + assert_eq!(sapling_notes.len(), 0); + let orchard_notes = recipient_wallet.wallet_outputs::(); + let unspent_orchard_notes = orchard_notes + .iter() + .filter(|&¬e| recipient_wallet.output_spend_status(note).is_unspent()) + .collect::>(); + let spent_orchard_notes = orchard_notes + .iter() + .filter(|&¬e| { + recipient_wallet + .output_spend_status(note) + .is_confirmed_spent() + }) + .collect::>(); - // assert_eq!(notes["spent_sapling_notes"].len(), 0); - // assert_eq!(notes["spent_orchard_notes"].len(), 1); - // assert_eq!(notes["spent_utxos"].len(), 0); - // // We should still have a change note even of zero value, as we send - // // ourself a wallet-readable memo - // assert_eq!(notes["unspent_orchard_notes"][0]["value"], 0); - // assert_eq!( - // notes["spent_orchard_notes"][0]["spent"], - // sent_transaction_id - // ); + assert_eq!(unspent_orchard_notes.len(), 1); + assert_eq!( + orchard_notes + .iter() + .filter(|&¬e| recipient_wallet + .output_spend_status(note) + .is_pending_spent()) + .count(), + 0 + ); + assert_eq!(spent_orchard_notes.len(), 1); - // check_client_balances!(recipient, o: 0 s: 0 t: 0); - // } + assert_eq!(unspent_orchard_notes.first().unwrap().value(), 0); + assert_eq!( + spent_orchard_notes + .first() + .unwrap() + .spending_transaction() + .unwrap() + .to_string(), + sent_transaction_id + ); + + check_client_balances!(recipient, o: 0 s: 0 t: 0); + } + // FIXME: zingo2 // #[tokio::test] // async fn witness_clearing() { // let (regtest_manager, _cph, faucet, recipient, txid) = @@ -1513,168 +1564,181 @@ mod slow { // .contains(&position)); // } - // #[tokio::test] - // async fn test_scanning_in_watch_only_mode() { - // // # Scenario: - // // 3. reset wallet - // // 4. for every combination of FVKs - // // 4.1. init a wallet with UFVK - // // 4.2. check that the wallet is empty - // // 4.3. rescan - // // 4.4. check that notes and utxos were detected by the wallet - // // - // // # Current watch-only mode limitations: - // // - wallet will not detect funds on all transparent addresses - // // see: https://github.com/zingolabs/zingolib/issues/245 - // // - wallet will not detect funds on internal addresses - // // see: https://github.com/zingolabs/zingolib/issues/246 - - // let (regtest_manager, _cph, mut client_builder, regtest_network) = - // scenarios::custom_clients_default().await; - // let faucet = client_builder.build_faucet(false, regtest_network).await; - // let original_recipient = client_builder - // .build_client(HOSPITAL_MUSEUM_SEED.to_string(), 0, false, regtest_network) - // .await; - // let zingo_config = zingolib::config::load_clientconfig( - // client_builder.server_id, - // Some(client_builder.zingo_datadir), - // ChainType::Regtest(regtest_network), - // true, - // ) - // .unwrap(); + #[tokio::test] + async fn test_scanning_in_watch_only_mode() { + // # Scenario: + // 3. reset wallet + // 4. for every combination of FVKs + // 4.1. init a wallet with UFVK + // 4.2. check that the wallet is empty + // 4.3. rescan + // 4.4. check that notes and utxos were detected by the wallet - // let (recipient_taddr, recipient_sapling, recipient_unified) = ( - // get_base_address_macro!(original_recipient, "transparent"), - // get_base_address_macro!(original_recipient, "sapling"), - // get_base_address_macro!(original_recipient, "unified"), - // ); - // let addr_amount_memos = vec![ - // (recipient_taddr.as_str(), 1_000u64, None), - // (recipient_sapling.as_str(), 2_000u64, None), - // (recipient_unified.as_str(), 3_000u64, None), - // ]; - // // 1. fill wallet with a coinbase transaction by syncing faucet with 1-block increase - // zingolib::testutils::increase_height_and_wait_for_client(®test_manager, &faucet, 1) - // .await - // .unwrap(); - // // 2. send a transaction containing all types of outputs - // from_inputs::quick_send(&faucet, addr_amount_memos) - // .await - // .unwrap(); - // zingolib::testutils::increase_height_and_wait_for_client( - // ®test_manager, - // &original_recipient, - // 1, - // ) - // .await - // .unwrap(); - // let original_recipient_balance = original_recipient.do_balance().await; - // let sent_t_value = original_recipient_balance.transparent_balance.unwrap(); - // let sent_s_value = original_recipient_balance.sapling_balance.unwrap(); - // let sent_o_value = original_recipient_balance.orchard_balance.unwrap(); - // assert_eq!(sent_t_value, 1000u64); - // assert_eq!(sent_s_value, 2000u64); - // assert_eq!(sent_o_value, 3000u64); - - // // check that do_rescan works - // original_recipient.do_rescan().await.unwrap(); - // check_client_balances!(original_recipient, o: sent_o_value s: sent_s_value t: sent_t_value); - - // // Extract viewing keys - // let wallet_capability = original_recipient.wallet.wallet_capability().clone(); - // let [o_fvk, s_fvk, t_fvk] = - // zingolib::testutils::build_fvks_from_wallet_capability(&wallet_capability); - // let fvks_sets = [ - // vec![&o_fvk], - // vec![&s_fvk], - // vec![&o_fvk, &s_fvk], - // vec![&o_fvk, &t_fvk], - // vec![&s_fvk, &t_fvk], - // vec![&o_fvk, &s_fvk, &t_fvk], - // ]; - // for fvks in fvks_sets.iter() { - // log::info!("testing UFVK containing:"); - // log::info!(" orchard fvk: {}", fvks.contains(&&o_fvk)); - // log::info!(" sapling fvk: {}", fvks.contains(&&s_fvk)); - // log::info!(" transparent fvk: {}", fvks.contains(&&t_fvk)); - - // let watch_client = build_fvk_client(fvks, &zingo_config).await; - // let watch_wc = watch_client.wallet.wallet_capability(); - // // assert empty wallet before rescan - // let balance = watch_client.do_balance().await; - // check_expected_balance_with_fvks(fvks, balance, 0, 0, 0); - // watch_client.do_rescan().await.unwrap(); - // let balance = watch_client.do_balance().await; - // let notes = watch_client.do_list_notes(true).await; - - // check_view_capability_bounds( - // &balance, - // &watch_wc, - // fvks, - // &o_fvk, - // &s_fvk, - // &t_fvk, - // Some(sent_o_value), - // Some(sent_s_value), - // Some(sent_t_value), - // ¬es, - // ); + let (regtest_manager, _cph, mut client_builder, regtest_network) = + scenarios::custom_clients_default().await; + let mut faucet = client_builder.build_faucet(false, regtest_network).await; + let mut original_recipient = client_builder + .build_client(HOSPITAL_MUSEUM_SEED.to_string(), 0, false, regtest_network) + .await; + let zingo_config = zingolib::config::load_clientconfig( + client_builder.server_id, + Some(client_builder.zingo_datadir), + ChainType::Regtest(regtest_network), + ) + .unwrap(); - // watch_client.do_rescan().await.unwrap(); - // assert!(matches!( - // from_inputs::quick_send(&watch_client, vec![(testvectors::EXT_TADDR, 1000, None)]) - // .await, - // Err(QuickSendError::ProposeSend(ProposeSendError::Proposal( - // zcash_client_backend::data_api::error::Error::DataSource( - // TxMapTraitError::NoSpendCapability - // ) - // ))) - // )); - // } - // } - // #[tokio::test] - // async fn t_incoming_t_outgoing_disallowed() { - // let (regtest_manager, _cph, faucet, recipient) = - // scenarios::faucet_recipient_default().await; + let (recipient_taddr, recipient_sapling, recipient_unified) = ( + get_base_address_macro!(original_recipient, "transparent"), + get_base_address_macro!(original_recipient, "sapling"), + get_base_address_macro!(original_recipient, "unified"), + ); + let addr_amount_memos = vec![ + (recipient_taddr.as_str(), 1_000u64, None), + (recipient_sapling.as_str(), 2_000u64, None), + (recipient_unified.as_str(), 3_000u64, None), + ]; + // 1. fill wallet with a coinbase transaction by syncing faucet with 1-block increase + zingolib::testutils::increase_height_and_wait_for_client(®test_manager, &mut faucet, 1) + .await + .unwrap(); + // 2. send a transaction containing all types of outputs + from_inputs::quick_send(&faucet, addr_amount_memos) + .await + .unwrap(); + zingolib::testutils::increase_height_and_wait_for_client( + ®test_manager, + &mut original_recipient, + 1, + ) + .await + .unwrap(); + let original_recipient_balance = original_recipient.do_balance().await; + let sent_t_value = original_recipient_balance.transparent_balance.unwrap(); + let sent_s_value = original_recipient_balance.sapling_balance.unwrap(); + let sent_o_value = original_recipient_balance.orchard_balance.unwrap(); + assert_eq!(sent_t_value, 1000u64); + assert_eq!(sent_s_value, 2000u64); + assert_eq!(sent_o_value, 3000u64); + + // check that do_rescan works + original_recipient.rescan_and_await().await.unwrap(); + check_client_balances!(original_recipient, o: sent_o_value s: sent_s_value t: sent_t_value); + + // Extract viewing keys + let original_wallet = original_recipient.wallet.lock().await; + let [o_fvk, s_fvk, t_fvk] = zingolib::testutils::build_fvks_from_unified_keystore( + &original_wallet.unified_key_store, + ); + let fvks_sets = [ + vec![&o_fvk], + vec![&s_fvk], + vec![&o_fvk, &s_fvk], + vec![&o_fvk, &t_fvk], + vec![&s_fvk, &t_fvk], + vec![&o_fvk, &s_fvk, &t_fvk], + ]; + for fvks in fvks_sets.iter() { + log::info!("testing UFVK containing:"); + log::info!(" orchard fvk: {}", fvks.contains(&&o_fvk)); + log::info!(" sapling fvk: {}", fvks.contains(&&s_fvk)); + log::info!(" transparent fvk: {}", fvks.contains(&&t_fvk)); + + let mut watch_client = build_fvk_client(fvks, &zingo_config).await; + // assert empty wallet before rescan + let balance = watch_client.do_balance().await; + check_expected_balance_with_fvks(fvks, balance, 0, 0, 0); + watch_client.rescan_and_await().await.unwrap(); + let balance = watch_client.do_balance().await; + let watch_wallet = watch_client.wallet.lock().await; + let orchard_notes = watch_wallet.note_summaries::(true); + let sapling_notes = watch_wallet.note_summaries::(true); + let transparent_coin = watch_wallet.coin_summaries(true); + + check_view_capability_bounds( + &balance, + &watch_wallet.unified_key_store, + fvks, + &o_fvk, + &s_fvk, + &t_fvk, + Some(sent_o_value), + Some(sent_s_value), + Some(sent_t_value), + &orchard_notes, + &sapling_notes, + &transparent_coin, + ); + drop(watch_wallet); + + watch_client.rescan_and_await().await.unwrap(); + assert!(matches!( + from_inputs::quick_send(&watch_client, vec![(testvectors::EXT_TADDR, 1000, None)]) + .await, + Err(QuickSendError::ProposeSend(ProposeSendError::Proposal( + zcash_client_backend::data_api::error::Error::DataSource( + WalletError::KeyError(KeyError::NoSpendCapability) + ) + ))) + )); + } + } + #[tokio::test] + async fn t_incoming_t_outgoing_disallowed() { + let (regtest_manager, _cph, faucet, mut recipient) = + scenarios::faucet_recipient_default().await; - // // 2. Get an incoming transaction to a t address - // let recipient_taddr = get_base_address_macro!(recipient, "transparent"); - // let value = 100_000; + // 2. Get an incoming transaction to a t address + let recipient_taddr = get_base_address_macro!(recipient, "transparent"); + let value = 100_000; - // from_inputs::quick_send(&faucet, vec![(recipient_taddr.as_str(), value, None)]) - // .await - // .unwrap(); + from_inputs::quick_send(&faucet, vec![(recipient_taddr.as_str(), value, None)]) + .await + .unwrap(); - // zingolib::testutils::increase_height_and_wait_for_client(®test_manager, &recipient, 1) - // .await - // .unwrap(); - // recipient.do_sync(true).await.unwrap(); + zingolib::testutils::increase_height_and_wait_for_client( + ®test_manager, + &mut recipient, + 1, + ) + .await + .unwrap(); + recipient.sync_and_await().await.unwrap(); - // // 3. Test the list - // let list = recipient.do_list_transactions().await; - // assert_eq!(list[0]["block_height"].as_u64().unwrap(), 4); - // assert_eq!( - // recipient.do_addresses(UAReceivers::All).await[0]["receivers"]["transparent"].to_string(), - // recipient_taddr - // ); - // assert_eq!(list[0]["amount"].as_u64().unwrap(), value); + // 3. Test the list + let transaction = recipient + .wallet + .lock() + .await + .transaction_summaries() + .await + .0 + .first() + .unwrap() + .clone(); + assert_eq!(transaction.blockheight(), 4.into()); + assert_eq!( + recipient.do_addresses(UAReceivers::All).await[0]["receivers"]["transparent"] + .to_string(), + recipient_taddr + ); + assert_eq!(transaction.value(), value); - // // 4. We can't spend the funds, as they're transparent. We need to shield first - // let sent_value = 20_000; - // let sent_transaction_error = - // from_inputs::quick_send(&recipient, vec![(testvectors::EXT_TADDR, sent_value, None)]) - // .await - // .unwrap_err(); - // assert!(matches!( - // sent_transaction_error, - // QuickSendError::ProposeSend(ProposeSendError::Proposal( - // zcash_client_backend::data_api::error::Error::InsufficientFunds { - // available: _, - // required: _ - // } - // )) - // )); - // } + // 4. We can't spend the funds, as they're transparent. We need to shield first + let sent_value = 20_000; + let sent_transaction_error = + from_inputs::quick_send(&recipient, vec![(testvectors::EXT_TADDR, sent_value, None)]) + .await + .unwrap_err(); + assert!(matches!( + sent_transaction_error, + QuickSendError::ProposeSend(ProposeSendError::Proposal( + zcash_client_backend::data_api::error::Error::InsufficientFunds { + available: _, + required: _ + } + )) + )); + } // FIXME: sync integration // #[tokio::test] @@ -2758,8 +2822,7 @@ mod slow { } let pre_rescan_summaries = faucet.transaction_summaries().await; - faucet.rescan().await.unwrap(); - faucet.await_sync().await.unwrap(); + faucet.rescan_and_await().await.unwrap(); let post_rescan_summaries = faucet.transaction_summaries().await; assert_eq!(pre_rescan_summaries, post_rescan_summaries); } @@ -2800,8 +2863,7 @@ mod slow { .unwrap(); let pre_rescan_summaries = faucet.transaction_summaries().await; - faucet.rescan().await.unwrap(); - faucet.await_sync().await.unwrap(); + faucet.rescan_and_await().await.unwrap(); let post_rescan_summaries = faucet.transaction_summaries().await; assert_eq!(pre_rescan_summaries, post_rescan_summaries); } @@ -2825,8 +2887,7 @@ mod slow { .unwrap(); let pre_rescan_transactions = recipient.transaction_summaries().await; let pre_rescan_summaries = recipient.sorted_value_transfers(true).await; - recipient.rescan().await.unwrap(); - recipient.await_sync().await.unwrap(); + recipient.rescan_and_await().await.unwrap(); let post_rescan_transactions = recipient.transaction_summaries().await; let post_rescan_summaries = recipient.sorted_value_transfers(true).await; assert_eq!(pre_rescan_transactions, post_rescan_transactions); diff --git a/zingolib/src/commands.rs b/zingolib/src/commands.rs index d26d190c0..b09e328b5 100644 --- a/zingolib/src/commands.rs +++ b/zingolib/src/commands.rs @@ -762,14 +762,14 @@ impl Command for ExportUfvkCommand { fn exec(&self, _args: &[&str], lightclient: &mut LightClient) -> String { RT.block_on(async move { - let ufvk: UnifiedFullViewingKey = - match (&lightclient.wallet.lock().await.unified_key_store).try_into() { - Ok(ufvk) => ufvk, - Err(e) => return e.to_string(), - }; + let wallet = lightclient.wallet.lock().await; + let ufvk: UnifiedFullViewingKey = match (&wallet.unified_key_store).try_into() { + Ok(ufvk) => ufvk, + Err(e) => return e.to_string(), + }; object! { - "ufvk" => ufvk.encode(&lightclient.config().chain), - "birthday" => u32::from(lightclient.wallet.lock().await.birthday) + "ufvk" => ufvk.encode(&wallet.network), + "birthday" => u32::from(wallet.birthday) } .pretty(2) }) diff --git a/zingolib/src/lightclient/sync.rs b/zingolib/src/lightclient/sync.rs index 606273410..49d99ca68 100644 --- a/zingolib/src/lightclient/sync.rs +++ b/zingolib/src/lightclient/sync.rs @@ -86,6 +86,12 @@ impl LightClient { self.sync().await?; self.await_sync().await } + + /// Calls [`crate::lightclient::LightClient::rescan`] and then [`crate::lightclient::LightClient::await_sync`]. + pub async fn rescan_and_await(&mut self) -> Result { + self.rescan().await?; + self.await_sync().await + } } /// Returned from [`crate::lightclient::LightClient::poll_sync`]. diff --git a/zingolib/src/testutils.rs b/zingolib/src/testutils.rs index 319fd04c7..bb1cb40d9 100644 --- a/zingolib/src/testutils.rs +++ b/zingolib/src/testutils.rs @@ -7,9 +7,9 @@ pub mod scenarios; // use crate::lightclient::describe::UAReceivers; use crate::wallet::data::summaries::{ - BasicNoteSummary, TransactionSummary, TransactionSummaryInterface as _, TransparentCoinSummary, + BasicCoinSummary, BasicNoteSummary, TransactionSummary, TransactionSummaryInterface as _, }; -use crate::wallet::keys::unified::WalletCapability; +use crate::wallet::keys::unified::{UnifiedKeyStore, WalletCapability}; use crate::wallet::output::SpendStatus; use crate::wallet::WalletBase; use grpc_proxy::ProxyServer; @@ -45,13 +45,12 @@ pub mod paths; pub mod regtest; /// TODO: Add Doc Comment Here! -pub fn build_fvks_from_wallet_capability(wallet_capability: &WalletCapability) -> [Fvk; 3] { - let orchard_vk: orchard::keys::FullViewingKey = - (&wallet_capability.unified_key_store).try_into().unwrap(); +pub fn build_fvks_from_unified_keystore(unified_keystore: &UnifiedKeyStore) -> [Fvk; 3] { + let orchard_vk: orchard::keys::FullViewingKey = unified_keystore.try_into().unwrap(); let sapling_vk: sapling_crypto::zip32::DiversifiableFullViewingKey = - (&wallet_capability.unified_key_store).try_into().unwrap(); + unified_keystore.try_into().unwrap(); let transparent_vk: zcash_primitives::legacy::keys::AccountPubKey = - (&wallet_capability.unified_key_store).try_into().unwrap(); + unified_keystore.try_into().unwrap(); let mut transparent_vk_bytes = [0u8; 65]; transparent_vk_bytes.copy_from_slice(&transparent_vk.serialize()); @@ -180,8 +179,8 @@ fn check_note_summary_equality(first: &[BasicNoteSummary], second: &[BasicNoteSu /// TODO: doc comment fn check_transparent_coin_summary_equality( - first: &[TransparentCoinSummary], - second: &[TransparentCoinSummary], + first: &[BasicCoinSummary], + second: &[BasicCoinSummary], ) -> bool { if first.len() != second.len() { return false; diff --git a/zingolib/src/wallet/data.rs b/zingolib/src/wallet/data.rs index 6861973e8..65bf9ce64 100644 --- a/zingolib/src/wallet/data.rs +++ b/zingolib/src/wallet/data.rs @@ -947,7 +947,7 @@ pub mod summaries { /// Gets slice of sapling note summaries fn sapling_notes(&self) -> &[BasicNoteSummary]; /// Gets slice of transparent coin summaries - fn transparent_coins(&self) -> &[TransparentCoinSummary]; + fn transparent_coins(&self) -> &[BasicCoinSummary]; /// Gets slice of outgoing orchard notes fn outgoing_orchard_notes(&self) -> &[OutgoingNoteSummary]; /// Gets slice of outgoing sapling notes @@ -976,7 +976,7 @@ pub mod summaries { String, BasicNoteSummaries, BasicNoteSummaries, - TransparentCoinSummaries, + BasicCoinSummaries, OutgoingNoteSummaries, OutgoingNoteSummaries, ) { @@ -997,7 +997,7 @@ pub mod summaries { }; let orchard_notes = BasicNoteSummaries(self.orchard_notes().to_vec()); let sapling_notes = BasicNoteSummaries(self.sapling_notes().to_vec()); - let transparent_coins = TransparentCoinSummaries(self.transparent_coins().to_vec()); + let transparent_coins = BasicCoinSummaries(self.transparent_coins().to_vec()); let outgoing_orchard_notes = OutgoingNoteSummaries(self.outgoing_orchard_notes().to_vec()); let outgoing_sapling_notes = @@ -1032,7 +1032,7 @@ pub mod summaries { zec_price: Option, orchard_notes: Vec, sapling_notes: Vec, - transparent_coins: Vec, + transparent_coins: Vec, outgoing_orchard_notes: Vec, outgoing_sapling_notes: Vec, } @@ -1068,7 +1068,7 @@ pub mod summaries { fn sapling_notes(&self) -> &[BasicNoteSummary] { &self.sapling_notes } - fn transparent_coins(&self) -> &[TransparentCoinSummary] { + fn transparent_coins(&self) -> &[BasicCoinSummary] { &self.transparent_coins } fn outgoing_orchard_notes(&self) -> &[OutgoingNoteSummary] { @@ -1202,7 +1202,7 @@ pub mod summaries { zec_price: Option>, orchard_notes: Option>, sapling_notes: Option>, - transparent_coins: Option>, + transparent_coins: Option>, outgoing_orchard_notes: Option>, outgoing_sapling_notes: Option>, } @@ -1237,7 +1237,7 @@ pub mod summaries { build_method!(zec_price, Option); build_method!(orchard_notes, Vec); build_method!(sapling_notes, Vec); - build_method!(transparent_coins, Vec); + build_method!(transparent_coins, Vec); build_method!(outgoing_orchard_notes, Vec); build_method!(outgoing_sapling_notes, Vec); @@ -1395,16 +1395,16 @@ pub mod summaries { /// A "snapshot" of the state of the output in the wallet at the time the summary was constructed. /// Not to be used for internal logic in the system. #[derive(Clone, PartialEq, Debug)] - pub struct TransparentCoinSummary { + pub struct BasicCoinSummary { value: u64, spend_summary: SpendStatus, output_index: u32, } - impl TransparentCoinSummary { + impl BasicCoinSummary { /// Creates a SaplingNoteSummary from parts pub fn from_parts(value: u64, spend_status: SpendStatus, output_index: u32) -> Self { - TransparentCoinSummary { + BasicCoinSummary { value, spend_summary: spend_status, output_index, @@ -1426,7 +1426,7 @@ pub mod summaries { } } - impl std::fmt::Display for TransparentCoinSummary { + impl std::fmt::Display for BasicCoinSummary { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!( f, @@ -1439,8 +1439,8 @@ pub mod summaries { ) } } - impl From for JsonValue { - fn from(note: TransparentCoinSummary) -> Self { + impl From for JsonValue { + fn from(note: BasicCoinSummary) -> Self { json::object! { "value" => note.value, "spend_status" => note.spend_summary.to_string(), @@ -1450,9 +1450,9 @@ pub mod summaries { } /// Wraps a vec of transparent coin summaries for the implementation of std::fmt::Display - pub struct TransparentCoinSummaries(Vec); + pub struct BasicCoinSummaries(Vec); - impl std::fmt::Display for TransparentCoinSummaries { + impl std::fmt::Display for BasicCoinSummaries { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { for coin in &self.0 { write!(f, "\n{}", coin)?; diff --git a/zingolib/src/wallet/describe.rs b/zingolib/src/wallet/describe.rs index d22d90776..655848f70 100644 --- a/zingolib/src/wallet/describe.rs +++ b/zingolib/src/wallet/describe.rs @@ -34,6 +34,7 @@ use crate::wallet::error::BalanceError; use crate::wallet::LightWallet; use crate::UAReceivers; +use super::data::summaries::BasicCoinSummary; use super::data::summaries::BasicNoteSummary; use super::data::summaries::OutgoingNoteSummary; use super::data::summaries::SelfSendValueTransfer; @@ -42,7 +43,6 @@ use super::data::summaries::TransactionSummaries; use super::data::summaries::TransactionSummary; use super::data::summaries::TransactionSummaryBuilder; use super::data::summaries::TransactionSummaryInterface as _; -use super::data::summaries::TransparentCoinSummary; use super::data::summaries::ValueTransfer; use super::data::summaries::ValueTransferBuilder; use super::data::summaries::ValueTransferKind; @@ -369,7 +369,7 @@ impl LightWallet { Option, Vec, Vec, - Vec, + Vec, Vec, Vec, ) { @@ -427,7 +427,7 @@ impl LightWallet { .map(|output| { let spend_status = self.output_spend_status(output); - TransparentCoinSummary::from_parts( + BasicCoinSummary::from_parts( output.value(), spend_status, output.output_id().output_index() as u32, diff --git a/zingolib/src/wallet/summary.rs b/zingolib/src/wallet/summary.rs index ed29a3716..33e6ae663 100644 --- a/zingolib/src/wallet/summary.rs +++ b/zingolib/src/wallet/summary.rs @@ -42,17 +42,18 @@ impl From for Scope { /// Note summary. /// /// Intended for returning a standalone summary of all notes to the user / consumer outside the context of transactions. +#[allow(missing_docs)] pub struct NoteSummary { - value: u64, - status: ConfirmationStatus, - block_height: BlockHeight, - spend_status: SpendStatus, - memo: Option, - time: u32, - txid: TxId, - output_index: u16, - account_id: zip32::AccountId, - scope: Scope, + pub value: u64, + pub status: ConfirmationStatus, + pub block_height: BlockHeight, + pub spend_status: SpendStatus, + pub memo: Option, + pub time: u32, + pub txid: TxId, + pub output_index: u16, + pub account_id: zip32::AccountId, + pub scope: Scope, } impl std::fmt::Display for NoteSummary { @@ -111,17 +112,18 @@ impl From for json::JsonValue { /// /// Intended for returning a standalone summary of all transparent coins to the user / consumer outside the context of /// transactions. +#[allow(missing_docs)] pub struct CoinSummary { - value: u64, - status: ConfirmationStatus, - block_height: BlockHeight, - spend_status: SpendStatus, - time: u32, - txid: TxId, - output_index: u16, - account_id: zip32::AccountId, - scope: TransparentScope, - address_index: u32, + pub value: u64, + pub status: ConfirmationStatus, + pub block_height: BlockHeight, + pub spend_status: SpendStatus, + pub time: u32, + pub txid: TxId, + pub output_index: u16, + pub account_id: zip32::AccountId, + pub scope: TransparentScope, + pub address_index: u32, } impl std::fmt::Display for CoinSummary { From 2db4c115de3693345f5d3ba21dfbe5eb5057a62f Mon Sep 17 00:00:00 2001 From: Oscar Pepper Date: Tue, 25 Feb 2025 02:46:37 +0000 Subject: [PATCH 22/26] fixing concrete tests cont. --- libtonode-tests/tests/concrete.rs | 993 ++++++++++++++++-------------- 1 file changed, 532 insertions(+), 461 deletions(-) diff --git a/libtonode-tests/tests/concrete.rs b/libtonode-tests/tests/concrete.rs index ac5d5852e..21edd6461 100644 --- a/libtonode-tests/tests/concrete.rs +++ b/libtonode-tests/tests/concrete.rs @@ -1257,17 +1257,29 @@ mod fast { } } mod slow { + use pepper_sync::keys::KeyId; use pepper_sync::wallet::{OrchardNote, OutputInterface, SaplingNote, TransparentCoin}; use shardtree::store::ShardStore; + use testvectors::TEST_TXID; use zcash_client_backend::{PoolType, ShieldedProtocol}; + use zcash_primitives::consensus::BlockHeight; use zcash_primitives::transaction::fees::zip317::MARGINAL_FEE; + use zcash_primitives::zip32::{self, AccountId}; + use zingo_status::confirmation_status::ConfirmationStatus; use zingolib::config::ChainType; use zingolib::lightclient::send::send_with_proposal::QuickSendError; - use zingolib::testutils::build_fvk_client; use zingolib::testutils::lightclient::{from_inputs, get_fees_paid_by_client}; - use zingolib::wallet::data::summaries::TransactionSummaryInterface; + use zingolib::testutils::{ + assert_transaction_summary_equality, assert_transaction_summary_exists, build_fvk_client, + }; + use zingolib::wallet::data::summaries::{ + BasicNoteSummary, OutgoingNoteSummary, TransactionSummaryBuilder, + TransactionSummaryInterface, + }; use zingolib::wallet::error::{KeyError, WalletError}; - use zingolib::UAReceivers; + use zingolib::wallet::output::SpendStatus; + use zingolib::wallet::transaction_record::{SendType, TransactionKind}; + use zingolib::{utils, UAReceivers}; use super::*; @@ -1649,26 +1661,27 @@ mod slow { check_expected_balance_with_fvks(fvks, balance, 0, 0, 0); watch_client.rescan_and_await().await.unwrap(); let balance = watch_client.do_balance().await; - let watch_wallet = watch_client.wallet.lock().await; - let orchard_notes = watch_wallet.note_summaries::(true); - let sapling_notes = watch_wallet.note_summaries::(true); - let transparent_coin = watch_wallet.coin_summaries(true); - - check_view_capability_bounds( - &balance, - &watch_wallet.unified_key_store, - fvks, - &o_fvk, - &s_fvk, - &t_fvk, - Some(sent_o_value), - Some(sent_s_value), - Some(sent_t_value), - &orchard_notes, - &sapling_notes, - &transparent_coin, - ); - drop(watch_wallet); + { + let watch_wallet = watch_client.wallet.lock().await; + let orchard_notes = watch_wallet.note_summaries::(true); + let sapling_notes = watch_wallet.note_summaries::(true); + let transparent_coin = watch_wallet.coin_summaries(true); + + check_view_capability_bounds( + &balance, + &watch_wallet.unified_key_store, + fvks, + &o_fvk, + &s_fvk, + &t_fvk, + Some(sent_o_value), + Some(sent_s_value), + Some(sent_t_value), + &orchard_notes, + &sapling_notes, + &transparent_coin, + ); + } watch_client.rescan_and_await().await.unwrap(); assert!(matches!( @@ -1740,430 +1753,487 @@ mod slow { )); } - // FIXME: sync integration - // #[tokio::test] - // async fn sends_to_self_handle_balance_properly() { - // let transparent_funding = 100_000; - // let (ref regtest_manager, _cph, faucet, ref recipient) = - // scenarios::faucet_recipient_default().await; - // from_inputs::quick_send( - // &faucet, - // vec![( - // &get_base_address_macro!(recipient, "transparent"), - // transparent_funding, - // None, - // )], - // ) - // .await - // .unwrap(); - // zingolib::testutils::increase_height_and_wait_for_client(regtest_manager, recipient, 1) - // .await - // .unwrap(); - // recipient.quick_shield().await.unwrap(); - // zingolib::testutils::increase_height_and_wait_for_client(regtest_manager, recipient, 1) - // .await - // .unwrap(); - // println!( - // "{}", - // serde_json::to_string_pretty(&recipient.do_balance().await).unwrap() - // ); - // println!("{}", recipient.do_list_transactions().await.pretty(2)); - // println!( - // "{}", - // JsonValue::from(recipient.sorted_value_transfers(true).await).pretty(2) - // ); - // recipient.do_rescan().await.unwrap(); - // println!( - // "{}", - // serde_json::to_string_pretty(&recipient.do_balance().await).unwrap() - // ); - // println!("{}", recipient.do_list_transactions().await.pretty(2)); - // println!( - // "{}", - // JsonValue::from(recipient.sorted_value_transfers(true).await).pretty(2) - // ); - // // TODO: Add asserts! - // } - // FIXME: sync integration - // #[tokio::test] - // async fn send_to_ua_saves_full_ua_in_wallet() { - // let (regtest_manager, _cph, faucet, recipient) = - // scenarios::faucet_recipient_default().await; - // //utils::increase_height_and_wait_for_client(®test_manager, &faucet, 5).await; - // let recipient_unified_address = get_base_address_macro!(recipient, "unified"); - // let sent_value = 50_000; - // from_inputs::quick_send( - // &faucet, - // vec![(recipient_unified_address.as_str(), sent_value, None)], - // ) - // .await - // .unwrap(); - // zingolib::testutils::increase_height_and_wait_for_client(®test_manager, &faucet, 1) - // .await - // .unwrap(); - // let list = faucet.do_list_transactions().await; - // assert!(list.members().any(|transaction| { - // transaction.entries().any(|(key, value)| { - // if key == "outgoing_metadata" { - // value[0]["address"] == recipient_unified_address - // } else { - // false - // } - // }) - // })); - // faucet.do_rescan().await.unwrap(); - // let new_list = faucet.do_list_transactions().await; - // assert!(new_list.members().any(|transaction| { - // transaction.entries().any(|(key, value)| { - // if key == "outgoing_metadata" { - // value[0]["address"] == recipient_unified_address - // } else { - // false - // } - // }) - // })); - // assert_eq!( - // list, - // new_list, - // "Pre-Rescan: {}\n\n\nPost-Rescan: {}\n\n\n", - // json::stringify_pretty(list.clone(), 4), - // json::stringify_pretty(new_list.clone(), 4) - // ); - // } - // FIXME: sync integration - // #[tokio::test] - // async fn send_to_transparent_and_sapling_maintain_balance() { - // // Receipt of orchard funds - // let recipient_initial_funds = 100_000_000; - // let (ref regtest_manager, _cph, faucet, recipient, _txid) = - // scenarios::faucet_funded_recipient_default(recipient_initial_funds).await; - - // let summary_orchard_receipt = TransactionSummaryBuilder::new() - // .blockheight(BlockHeight::from_u32(5)) - // .status(ConfirmationStatus::Confirmed(BlockHeight::from_u32(5))) - // .datetime(0) - // .txid(utils::conversion::txid_from_hex_encoded_str(TEST_TXID).unwrap()) - // .value(recipient_initial_funds) - // .zec_price(None) - // .kind(TransactionKind::Received) - // .fee(None) - // .orchard_notes(vec![OrchardNoteSummary::from_parts( - // recipient_initial_funds, - // SpendSummary::Spent( - // utils::conversion::txid_from_hex_encoded_str(TEST_TXID).unwrap(), - // ), - // Some(0), - // None, - // )]) - // .sapling_notes(vec![]) - // .transparent_coins(vec![]) - // .outgoing_tx_data(vec![]) - // .build() - // .unwrap(); + #[tokio::test] + async fn sends_to_self_handle_balance_properly() { + let transparent_funding = 100_000; + let (ref regtest_manager, _cph, faucet, mut recipient) = + scenarios::faucet_recipient_default().await; + from_inputs::quick_send( + &faucet, + vec![( + &get_base_address_macro!(recipient, "transparent"), + transparent_funding, + None, + )], + ) + .await + .unwrap(); + zingolib::testutils::increase_height_and_wait_for_client( + regtest_manager, + &mut recipient, + 1, + ) + .await + .unwrap(); + recipient.quick_shield().await.unwrap(); + zingolib::testutils::increase_height_and_wait_for_client( + regtest_manager, + &mut recipient, + 1, + ) + .await + .unwrap(); + println!( + "{}", + serde_json::to_string_pretty(&recipient.do_balance().await).unwrap() + ); + println!("{}", recipient.transaction_summaries().await); + println!( + "{}", + JsonValue::from(recipient.sorted_value_transfers(true).await).pretty(2) + ); + recipient.rescan_and_await().await.unwrap(); + println!( + "{}", + serde_json::to_string_pretty(&recipient.do_balance().await).unwrap() + ); + println!("{}", recipient.transaction_summaries().await); + println!( + "{}", + JsonValue::from(recipient.sorted_value_transfers(true).await).pretty(2) + ); + // TODO: Add asserts! + } + #[tokio::test] + async fn send_to_ua_saves_full_ua_in_wallet() { + let (regtest_manager, _cph, mut faucet, recipient) = + scenarios::faucet_recipient_default().await; + //utils::increase_height_and_wait_for_client(®test_manager, &faucet, 5).await; + let recipient_unified_address = get_base_address_macro!(recipient, "unified"); + let sent_value = 50_000; + from_inputs::quick_send( + &faucet, + vec![(recipient_unified_address.as_str(), sent_value, None)], + ) + .await + .unwrap(); + zingolib::testutils::increase_height_and_wait_for_client(®test_manager, &mut faucet, 1) + .await + .unwrap(); + let transactions = faucet.transaction_summaries().await.0; + assert!(transactions.iter().any(|transaction| { + transaction + .outgoing_orchard_notes() + .iter() + .chain(transaction.outgoing_sapling_notes().iter()) + .any(|note| { + note.recipient_unified_address == Some(recipient_unified_address.clone()) + }) + })); + faucet.rescan_and_await().await.unwrap(); + let rescanned_transactions = faucet.transaction_summaries().await.0; + assert!(rescanned_transactions.iter().any(|transaction| { + transaction + .outgoing_orchard_notes() + .iter() + .chain(transaction.outgoing_sapling_notes().iter()) + .any(|note| { + note.recipient_unified_address == Some(recipient_unified_address.clone()) + }) + })); + assert_eq!( + transactions, + rescanned_transactions, + "Pre-Rescan: {}\n\n\nPost-Rescan: {}\n\n\n", + json::stringify_pretty(transactions.clone(), 4), + json::stringify_pretty(rescanned_transactions.clone(), 4) + ); + } + #[tokio::test] + async fn send_to_transparent_and_sapling_maintain_balance() { + // Receipt of orchard funds + let recipient_initial_funds = 100_000_000; + let (ref regtest_manager, _cph, mut faucet, mut recipient, _txid) = + scenarios::faucet_funded_recipient_default(recipient_initial_funds).await; + + let summary_orchard_receipt = TransactionSummaryBuilder::new() + .blockheight(BlockHeight::from_u32(5)) + .status(ConfirmationStatus::Confirmed(BlockHeight::from_u32(5))) + .datetime(0) + .txid(utils::conversion::txid_from_hex_encoded_str(TEST_TXID).unwrap()) + .value(recipient_initial_funds) + .zec_price(None) + .kind(TransactionKind::Received) + .fee(None) + .orchard_notes(vec![BasicNoteSummary::from_parts( + recipient_initial_funds, + SpendStatus::Spent( + utils::conversion::txid_from_hex_encoded_str(TEST_TXID).unwrap(), + ), + 0, + None, + )]) + .sapling_notes(vec![]) + .transparent_coins(vec![]) + .outgoing_orchard_notes(vec![]) + .outgoing_sapling_notes(vec![]) + .build() + .unwrap(); - // // Send to faucet (external) sapling - // let first_send_to_sapling = 20_000; - // from_inputs::quick_send( - // &recipient, - // vec![( - // &get_base_address_macro!(faucet, "sapling"), - // first_send_to_sapling, - // None, - // )], - // ) - // .await - // .unwrap(); - // zingolib::testutils::increase_height_and_wait_for_client(regtest_manager, &recipient, 1) - // .await - // .unwrap(); - // let summary_external_sapling = TransactionSummaryBuilder::new() - // .blockheight(BlockHeight::from_u32(6)) - // .status(ConfirmationStatus::Confirmed(BlockHeight::from_u32(6))) - // .datetime(0) - // .txid(utils::conversion::txid_from_hex_encoded_str(TEST_TXID).unwrap()) - // .value(first_send_to_sapling) - // .zec_price(None) - // .kind(TransactionKind::Sent(SendType::Send)) - // .fee(Some(20_000)) - // .orchard_notes(vec![OrchardNoteSummary::from_parts( - // 99_960_000, - // SpendSummary::TransmittedSpent( - // utils::conversion::txid_from_hex_encoded_str(TEST_TXID).unwrap(), - // ), - // Some(0), - // None, - // )]) - // .sapling_notes(vec![]) - // .transparent_coins(vec![]) - // .outgoing_tx_data(vec![OutgoingTxData { - // recipient_address: "zregtestsapling1fmq2ufux3gm0v8qf7x585wj56le4wjfsqsj27zprjghntrerntggg507hxh2ydcdkn7sx8kya7p".to_string(), - // value: first_send_to_sapling, - // memo: Memo::Empty, - // recipient_ua: None, - // output_index: None, - // }]) - // .build() - // .unwrap(); + // Send to faucet (external) sapling + let first_send_to_sapling = 20_000; + from_inputs::quick_send( + &recipient, + vec![( + &get_base_address_macro!(faucet, "sapling"), + first_send_to_sapling, + None, + )], + ) + .await + .unwrap(); + zingolib::testutils::increase_height_and_wait_for_client( + regtest_manager, + &mut recipient, + 1, + ) + .await + .unwrap(); + let summary_external_sapling = TransactionSummaryBuilder::new() + .blockheight(BlockHeight::from_u32(6)) + .status(ConfirmationStatus::Confirmed(BlockHeight::from_u32(6))) + .datetime(0) + .txid(utils::conversion::txid_from_hex_encoded_str(TEST_TXID).unwrap()) + .value(first_send_to_sapling) + .zec_price(None) + .kind(TransactionKind::Sent(SendType::Send)) + .fee(Some(20_000)) + .orchard_notes(vec![BasicNoteSummary::from_parts( + 99_960_000, + SpendStatus::TransmittedSpent( + utils::conversion::txid_from_hex_encoded_str(TEST_TXID).unwrap(), + ), + 0, + None, + )]) + .sapling_notes(vec![]) + .transparent_coins(vec![]) + .outgoing_orchard_notes(vec![OutgoingNoteSummary { + output_index: 0, + key_id: KeyId { + account_id: AccountId::ZERO, + scope: zip32::Scope::External, + }, + value: first_send_to_sapling, + memo: None, + recipient: "zregtestsapling1fmq2ufux3gm0v8qf7x585wj56le4wjfsqsj27zprjghntrerntggg507hxh2ydcdkn7sx8kya7p".to_string(), + recipient_unified_address: None, + }]) + .outgoing_sapling_notes(vec![]) + .build() + .unwrap(); - // // Send to faucet (external) transparent - // let first_send_to_transparent = 20_000; - // let summary_external_transparent = TransactionSummaryBuilder::new() - // .blockheight(BlockHeight::from_u32(7)) - // // We're not monitoring the mempool for this test - // .status(ConfirmationStatus::Transmitted(BlockHeight::from_u32(7))) - // .datetime(0) - // .txid(utils::conversion::txid_from_hex_encoded_str(TEST_TXID).unwrap()) - // .value(first_send_to_transparent) - // .zec_price(None) - // .kind(TransactionKind::Sent(SendType::Send)) - // .fee(Some(15_000)) - // .orchard_notes(vec![OrchardNoteSummary::from_parts( - // 99_925_000, - // SpendSummary::Unspent, - // Some(0), - // None, - // )]) - // .sapling_notes(vec![]) - // .transparent_coins(vec![]) - // .outgoing_tx_data(vec![OutgoingTxData { - // recipient_address: "tmBsTi2xWTjUdEXnuTceL7fecEQKeWaPDJd".to_string(), - // value: first_send_to_transparent, - // memo: Memo::Empty, - // recipient_ua: None, - // output_index: None, - // }]) - // .build() - // .unwrap(); + // Send to faucet (external) transparent + let first_send_to_transparent = 20_000; + let summary_external_transparent = TransactionSummaryBuilder::new() + .blockheight(BlockHeight::from_u32(7)) + // We're not monitoring the mempool for this test + .status(ConfirmationStatus::Transmitted(BlockHeight::from_u32(7))) + .datetime(0) + .txid(utils::conversion::txid_from_hex_encoded_str(TEST_TXID).unwrap()) + .value(first_send_to_transparent) + .zec_price(None) + .kind(TransactionKind::Sent(SendType::Send)) + .fee(Some(15_000)) + .orchard_notes(vec![BasicNoteSummary::from_parts( + 99_925_000, + SpendStatus::Unspent, + 0, + None, + )]) + .sapling_notes(vec![]) + .transparent_coins(vec![]) + .outgoing_orchard_notes(vec![OutgoingNoteSummary { + output_index: 0, + key_id: KeyId { + account_id: AccountId::ZERO, + scope: zip32::Scope::External, + }, + value: first_send_to_transparent, + memo: None, + recipient: "tmBsTi2xWTjUdEXnuTceL7fecEQKeWaPDJd".to_string(), + recipient_unified_address: None, + }]) + .outgoing_sapling_notes(vec![]) + .build() + .unwrap(); - // from_inputs::quick_send( - // &recipient, - // vec![( - // &get_base_address_macro!(faucet, "transparent"), - // first_send_to_transparent, - // None, - // )], - // ) - // .await - // .unwrap(); + from_inputs::quick_send( + &recipient, + vec![( + &get_base_address_macro!(faucet, "transparent"), + first_send_to_transparent, + None, + )], + ) + .await + .unwrap(); - // // Assert transactions are as expected - // assert_transaction_summary_equality( - // &recipient.transaction_summaries().await.0[0], - // &summary_orchard_receipt, - // ); - // assert_transaction_summary_equality( - // &recipient.transaction_summaries().await.0[1], - // &summary_external_sapling, - // ); - // assert_transaction_summary_equality( - // &recipient.transaction_summaries().await.0[2], - // &summary_external_transparent, - // ); + // Assert transactions are as expected + assert_transaction_summary_equality( + &recipient.transaction_summaries().await.0[0], + &summary_orchard_receipt, + ); + assert_transaction_summary_equality( + &recipient.transaction_summaries().await.0[1], + &summary_external_sapling, + ); + assert_transaction_summary_equality( + &recipient.transaction_summaries().await.0[2], + &summary_external_transparent, + ); - // // Check several expectations about recipient wallet state: - // // (1) shielded balance total is expected amount - // let expected_funds = recipient_initial_funds - // - first_send_to_sapling - // - (4 * u64::from(MARGINAL_FEE)) - // - first_send_to_transparent - // - (3 * u64::from(MARGINAL_FEE)); - // assert_eq!( - // recipient.wallet.pending_balance::().await, - // Some(expected_funds) - // ); - // // (2) The balance is not yet verified - // assert_eq!( - // recipient.wallet.confirmed_balance::().await, - // Some(0) - // ); + // Check several expectations about recipient wallet state: + // (1) shielded balance total is expected amount + let expected_funds = recipient_initial_funds + - first_send_to_sapling + - (4 * u64::from(MARGINAL_FEE)) + - first_send_to_transparent + - (3 * u64::from(MARGINAL_FEE)); - // zingolib::testutils::increase_height_and_wait_for_client(regtest_manager, &faucet, 1) - // .await - // .unwrap(); + { + let recipient_wallet = recipient.wallet.lock().await; + assert_eq!( + recipient_wallet.pending_balance::().await, + Some(expected_funds) + ); + // (2) The balance is not yet verified + assert_eq!( + recipient_wallet.confirmed_balance::().await, + Some(0) + ); + } - // let recipient_second_funding = 1_000_000; - // let summary_orchard_receipt_2 = TransactionSummaryBuilder::new() - // .blockheight(BlockHeight::from_u32(8)) - // .status(ConfirmationStatus::Confirmed(BlockHeight::from_u32(8))) - // .datetime(0) - // .txid(utils::conversion::txid_from_hex_encoded_str(TEST_TXID).unwrap()) - // .value(recipient_second_funding) - // .zec_price(None) - // .kind(TransactionKind::Received) - // .fee(None) - // .orchard_notes(vec![OrchardNoteSummary::from_parts( - // recipient_second_funding, - // SpendSummary::Spent( - // utils::conversion::txid_from_hex_encoded_str(TEST_TXID).unwrap(), - // ), - // Some(0), - // Some("Second wave incoming".to_string()), - // )]) - // .sapling_notes(vec![]) - // .transparent_coins(vec![]) - // .outgoing_tx_data(vec![]) - // .build() - // .unwrap(); - // from_inputs::quick_send( - // &faucet, - // vec![( - // &get_base_address_macro!(recipient, "unified"), - // recipient_second_funding, - // Some("Second wave incoming"), - // )], - // ) - // .await - // .unwrap(); - // zingolib::testutils::increase_height_and_wait_for_client(regtest_manager, &recipient, 1) - // .await - // .unwrap(); + zingolib::testutils::increase_height_and_wait_for_client(regtest_manager, &mut faucet, 1) + .await + .unwrap(); - // // Send to external (faucet) transparent - // let second_send_to_transparent = 20_000; - // let summary_exteranl_transparent_2 = TransactionSummaryBuilder::new() - // .blockheight(BlockHeight::from_u32(9)) - // .status(ConfirmationStatus::Confirmed(BlockHeight::from_u32(9))) - // .datetime(0) - // .txid(utils::conversion::txid_from_hex_encoded_str(TEST_TXID).unwrap()) - // .value(second_send_to_transparent) - // .zec_price(None) - // .kind(TransactionKind::Sent(SendType::Send)) - // .fee(Some(15_000)) - // .orchard_notes(vec![OrchardNoteSummary::from_parts( - // 965_000, - // SpendSummary::Spent( - // utils::conversion::txid_from_hex_encoded_str(TEST_TXID).unwrap(), - // ), - // Some(0), - // None, - // )]) - // .sapling_notes(vec![]) - // .transparent_coins(vec![]) - // .outgoing_tx_data(vec![OutgoingTxData { - // recipient_address: "tmBsTi2xWTjUdEXnuTceL7fecEQKeWaPDJd".to_string(), - // value: second_send_to_transparent, - // memo: Memo::Empty, - // recipient_ua: None, - // output_index: None, - // }]) - // .build() - // .unwrap(); - // from_inputs::quick_send( - // &recipient, - // vec![( - // &get_base_address_macro!(faucet, "transparent"), - // second_send_to_transparent, - // None, - // )], - // ) - // .await - // .unwrap(); + let recipient_second_funding = 1_000_000; + let summary_orchard_receipt_2 = TransactionSummaryBuilder::new() + .blockheight(BlockHeight::from_u32(8)) + .status(ConfirmationStatus::Confirmed(BlockHeight::from_u32(8))) + .datetime(0) + .txid(utils::conversion::txid_from_hex_encoded_str(TEST_TXID).unwrap()) + .value(recipient_second_funding) + .zec_price(None) + .kind(TransactionKind::Received) + .fee(None) + .orchard_notes(vec![BasicNoteSummary::from_parts( + recipient_second_funding, + SpendStatus::Spent( + utils::conversion::txid_from_hex_encoded_str(TEST_TXID).unwrap(), + ), + 0, + Some("Second wave incoming".to_string()), + )]) + .sapling_notes(vec![]) + .transparent_coins(vec![]) + .outgoing_orchard_notes(vec![]) + .outgoing_sapling_notes(vec![]) + .build() + .unwrap(); + from_inputs::quick_send( + &faucet, + vec![( + &get_base_address_macro!(recipient, "unified"), + recipient_second_funding, + Some("Second wave incoming"), + )], + ) + .await + .unwrap(); + zingolib::testutils::increase_height_and_wait_for_client( + regtest_manager, + &mut recipient, + 1, + ) + .await + .unwrap(); - // // Send to faucet (external) sapling 2 - // let second_send_to_sapling = 20_000; - // let summary_external_sapling_2 = TransactionSummaryBuilder::new() - // .blockheight(BlockHeight::from_u32(9)) - // .status(ConfirmationStatus::Confirmed(BlockHeight::from_u32(9))) - // .datetime(0) - // .txid(utils::conversion::txid_from_hex_encoded_str(TEST_TXID).unwrap()) - // .value(second_send_to_sapling) - // .zec_price(None) - // .kind(TransactionKind::Sent(SendType::Send)) - // .fee(Some(20_000)) - // .orchard_notes(vec![OrchardNoteSummary::from_parts( - // 99_885_000, - // SpendSummary::Unspent, - // Some(0), - // None, - // )]) - // .sapling_notes(vec![]) - // .transparent_coins(vec![]) - // .outgoing_tx_data(vec![OutgoingTxData { - // recipient_address: "zregtestsapling1fmq2ufux3gm0v8qf7x585wj56le4wjfsqsj27zprjghntrerntggg507hxh2ydcdkn7sx8kya7p".to_string(), - // value: second_send_to_sapling, - // memo: Memo::Empty, - // recipient_ua: None, - // output_index: None, - // }]) - // .build() - // .unwrap(); - // from_inputs::quick_send( - // &recipient, - // vec![( - // &get_base_address_macro!(faucet, "sapling"), - // second_send_to_sapling, - // None, - // )], - // ) - // .await - // .unwrap(); - // zingolib::testutils::increase_height_and_wait_for_client(regtest_manager, &recipient, 1) - // .await - // .unwrap(); + // Send to external (faucet) transparent + let second_send_to_transparent = 20_000; + let summary_exteranl_transparent_2 = TransactionSummaryBuilder::new() + .blockheight(BlockHeight::from_u32(9)) + .status(ConfirmationStatus::Confirmed(BlockHeight::from_u32(9))) + .datetime(0) + .txid(utils::conversion::txid_from_hex_encoded_str(TEST_TXID).unwrap()) + .value(second_send_to_transparent) + .zec_price(None) + .kind(TransactionKind::Sent(SendType::Send)) + .fee(Some(15_000)) + .orchard_notes(vec![BasicNoteSummary::from_parts( + 965_000, + SpendStatus::Spent( + utils::conversion::txid_from_hex_encoded_str(TEST_TXID).unwrap(), + ), + 0, + None, + )]) + .sapling_notes(vec![]) + .transparent_coins(vec![]) + .outgoing_orchard_notes(vec![OutgoingNoteSummary { + output_index: 0, + key_id: KeyId { + account_id: AccountId::ZERO, + scope: zip32::Scope::External, + }, + value: second_send_to_transparent, + memo: None, + recipient: "tmBsTi2xWTjUdEXnuTceL7fecEQKeWaPDJd".to_string(), + recipient_unified_address: None, + }]) + .outgoing_sapling_notes(vec![]) + .build() + .unwrap(); + from_inputs::quick_send( + &recipient, + vec![( + &get_base_address_macro!(faucet, "transparent"), + second_send_to_transparent, + None, + )], + ) + .await + .unwrap(); - // // Third external transparent - // let external_transparent_3 = 20_000; - // let summary_external_transparent_3 = TransactionSummaryBuilder::new() - // .blockheight(BlockHeight::from_u32(10)) - // .status(ConfirmationStatus::Confirmed(BlockHeight::from_u32(10))) - // .datetime(0) - // .txid(utils::conversion::txid_from_hex_encoded_str(TEST_TXID).unwrap()) - // .value(external_transparent_3) - // .zec_price(None) - // .kind(TransactionKind::Sent(SendType::Send)) - // .fee(Some(15_000)) - // .orchard_notes(vec![OrchardNoteSummary::from_parts( - // 930_000, - // SpendSummary::Unspent, - // Some(0), - // None, - // )]) - // .sapling_notes(vec![]) - // .transparent_coins(vec![]) - // .outgoing_tx_data(vec![OutgoingTxData { - // recipient_address: "tmBsTi2xWTjUdEXnuTceL7fecEQKeWaPDJd".to_string(), - // value: external_transparent_3, - // memo: Memo::Empty, - // recipient_ua: None, - // output_index: None, - // }]) - // .build() - // .unwrap(); - // from_inputs::quick_send( - // &recipient, - // vec![( - // &get_base_address_macro!(faucet, "transparent"), - // external_transparent_3, - // None, - // )], - // ) - // .await - // .unwrap(); - // zingolib::testutils::increase_height_and_wait_for_client(regtest_manager, &recipient, 1) - // .await - // .unwrap(); + // Send to faucet (external) sapling 2 + let second_send_to_sapling = 20_000; + let summary_external_sapling_2 = TransactionSummaryBuilder::new() + .blockheight(BlockHeight::from_u32(9)) + .status(ConfirmationStatus::Confirmed(BlockHeight::from_u32(9))) + .datetime(0) + .txid(utils::conversion::txid_from_hex_encoded_str(TEST_TXID).unwrap()) + .value(second_send_to_sapling) + .zec_price(None) + .kind(TransactionKind::Sent(SendType::Send)) + .fee(Some(20_000)) + .orchard_notes(vec![BasicNoteSummary::from_parts( + 99_885_000, + SpendStatus::Unspent, + 0, + None, + )]) + .sapling_notes(vec![]) + .transparent_coins(vec![]) + .outgoing_orchard_notes(vec![OutgoingNoteSummary { + output_index: 0, + key_id: KeyId { + account_id: AccountId::ZERO, + scope: zip32::Scope::External, + }, + value: second_send_to_sapling, + memo: None, + recipient: "zregtestsapling1fmq2ufux3gm0v8qf7x585wj56le4wjfsqsj27zprjghntrerntggg507hxh2ydcdkn7sx8kya7p".to_string(), + recipient_unified_address: None, + }]) + .outgoing_sapling_notes(vec![]) + .build() + .unwrap(); + from_inputs::quick_send( + &recipient, + vec![( + &get_base_address_macro!(faucet, "sapling"), + second_send_to_sapling, + None, + )], + ) + .await + .unwrap(); + zingolib::testutils::increase_height_and_wait_for_client( + regtest_manager, + &mut recipient, + 1, + ) + .await + .unwrap(); - // // Final check - // assert_transaction_summary_equality( - // &recipient.transaction_summaries().await.0[3], - // &summary_orchard_receipt_2, - // ); - // assert_transaction_summary_exists(&recipient, &summary_exteranl_transparent_2).await; // due to summaries of the same blockheight changing order - // assert_transaction_summary_exists(&recipient, &summary_external_sapling_2).await; // we check all summaries for these expected transactions - // assert_transaction_summary_equality( - // &recipient.transaction_summaries().await.0[6], - // &summary_external_transparent_3, - // ); - // let second_wave_expected_funds = expected_funds + recipient_second_funding - // - second_send_to_sapling - // - second_send_to_transparent - // - external_transparent_3 - // - (5 * u64::from(MINIMUM_FEE)); - // assert_eq!( - // recipient.wallet.spendable_balance::().await, - // Some(second_wave_expected_funds), - // ); - // } + // Third external transparent + let external_transparent_3 = 20_000; + let summary_external_transparent_3 = TransactionSummaryBuilder::new() + .blockheight(BlockHeight::from_u32(10)) + .status(ConfirmationStatus::Confirmed(BlockHeight::from_u32(10))) + .datetime(0) + .txid(utils::conversion::txid_from_hex_encoded_str(TEST_TXID).unwrap()) + .value(external_transparent_3) + .zec_price(None) + .kind(TransactionKind::Sent(SendType::Send)) + .fee(Some(15_000)) + .orchard_notes(vec![BasicNoteSummary::from_parts( + 930_000, + SpendStatus::Unspent, + 0, + None, + )]) + .sapling_notes(vec![]) + .transparent_coins(vec![]) + .outgoing_orchard_notes(vec![OutgoingNoteSummary { + output_index: 0, + key_id: KeyId { + account_id: AccountId::ZERO, + scope: zip32::Scope::External, + }, + value: external_transparent_3, + memo: None, + recipient: "tmBsTi2xWTjUdEXnuTceL7fecEQKeWaPDJd".to_string(), + recipient_unified_address: None, + }]) + .outgoing_sapling_notes(vec![]) + .build() + .unwrap(); + from_inputs::quick_send( + &recipient, + vec![( + &get_base_address_macro!(faucet, "transparent"), + external_transparent_3, + None, + )], + ) + .await + .unwrap(); + zingolib::testutils::increase_height_and_wait_for_client( + regtest_manager, + &mut recipient, + 1, + ) + .await + .unwrap(); + + // Final check + assert_transaction_summary_equality( + &recipient.transaction_summaries().await.0[3], + &summary_orchard_receipt_2, + ); + assert_transaction_summary_exists(&recipient, &summary_exteranl_transparent_2).await; // due to summaries of the same blockheight changing order + assert_transaction_summary_exists(&recipient, &summary_external_sapling_2).await; // we check all summaries for these expected transactions + assert_transaction_summary_equality( + &recipient.transaction_summaries().await.0[6], + &summary_external_transparent_3, + ); + let second_wave_expected_funds = expected_funds + recipient_second_funding + - second_send_to_sapling + - second_send_to_transparent + - external_transparent_3 + - (5 * u64::from(MINIMUM_FEE)); + assert_eq!( + recipient + .wallet + .lock() + .await + .spendable_balance::() + .await, + Some(second_wave_expected_funds), + ); + } #[tokio::test] async fn send_orchard_back_and_forth() { @@ -2261,31 +2331,32 @@ mod slow { ) .await; - let wallet = faucet.wallet.lock().await; - dbg!(wallet.sync_state.wallet_height()); - dbg!(wallet - .shard_trees - .sapling - .store() - .max_checkpoint_id() - .unwrap()); - dbg!(wallet - .shard_trees - .orchard - .store() - .max_checkpoint_id() - .unwrap()); - dbg!(wallet - .shard_trees - .sapling - .root_at_checkpoint_id(&3.into()) - .unwrap()); - dbg!(wallet - .shard_trees - .orchard - .root_at_checkpoint_id(&3.into()) - .unwrap()); - drop(wallet); + { + let wallet = faucet.wallet.lock().await; + dbg!(wallet.sync_state.wallet_height()); + dbg!(wallet + .shard_trees + .sapling + .store() + .max_checkpoint_id() + .unwrap()); + dbg!(wallet + .shard_trees + .orchard + .store() + .max_checkpoint_id() + .unwrap()); + dbg!(wallet + .shard_trees + .sapling + .root_at_checkpoint_id(&3.into()) + .unwrap()); + dbg!(wallet + .shard_trees + .orchard + .root_at_checkpoint_id(&3.into()) + .unwrap()); + } let amount_to_send = 5_000; from_inputs::quick_send( From 297c5f33964929242b8c6fd94dde5b8ae869a05b Mon Sep 17 00:00:00 2001 From: Oscar Pepper Date: Tue, 25 Feb 2025 12:37:34 +0000 Subject: [PATCH 23/26] fix all remaining zingo2 fixmes or comment with reason why it should wait --- libtonode-tests/tests/concrete.rs | 906 ++++++++++++++---------------- libtonode-tests/tests/wallet.rs | 319 ++++------- pepper-sync/src/keys.rs | 1 + zingolib/src/testutils.rs | 68 +-- zingolib/src/wallet/data.rs | 1 + 5 files changed, 570 insertions(+), 725 deletions(-) diff --git a/libtonode-tests/tests/concrete.rs b/libtonode-tests/tests/concrete.rs index 21edd6461..846381a15 100644 --- a/libtonode-tests/tests/concrete.rs +++ b/libtonode-tests/tests/concrete.rs @@ -1258,20 +1258,26 @@ mod fast { } mod slow { use pepper_sync::keys::KeyId; - use pepper_sync::wallet::{OrchardNote, OutputInterface, SaplingNote, TransparentCoin}; + use pepper_sync::wallet::{ + NoteInterface, OrchardNote, OutgoingNoteInterface, OutputInterface, SaplingNote, + TransparentCoin, + }; use shardtree::store::ShardStore; use testvectors::TEST_TXID; use zcash_client_backend::{PoolType, ShieldedProtocol}; use zcash_primitives::consensus::BlockHeight; + use zcash_primitives::memo::Memo; use zcash_primitives::transaction::fees::zip317::MARGINAL_FEE; use zcash_primitives::zip32::{self, AccountId}; use zingo_status::confirmation_status::ConfirmationStatus; use zingolib::config::ChainType; use zingolib::lightclient::send::send_with_proposal::QuickSendError; + use zingolib::lightclient::LightClient; use zingolib::testutils::lightclient::{from_inputs, get_fees_paid_by_client}; use zingolib::testutils::{ assert_transaction_summary_equality, assert_transaction_summary_exists, build_fvk_client, }; + use zingolib::utils::conversion::txid_from_hex_encoded_str; use zingolib::wallet::data::summaries::{ BasicNoteSummary, OutgoingNoteSummary, TransactionSummaryBuilder, TransactionSummaryInterface, @@ -2512,246 +2518,329 @@ mod slow { let mut txids = recipient.transaction_summaries().await.txids().into_iter(); assert!(itertools::Itertools::all_unique(&mut txids)); } - // FIXME: sync integration - // #[tokio::test] - // async fn sapling_to_sapling_scan_together() { - // // Create an incoming transaction, and then send that transaction, and scan everything together, to make sure it works. - // // (For this test, the Sapling Domain is assumed in all cases.) - // // Sender Setup: - // // 1. create a spend key: SpendK_S - // // 2. derive a Shielded Payment Address from SpendK_S: SPA_KS - // // 3. construct a Block Reward Transaction where SPA_KS receives a block reward: BRT - // // 4. publish BRT - // // 5. optionally mine a block including BRT <-- There are two separate tests to run - // // 6. optionally mine sufficient subsequent blocks to "validate" BRT - // // Recipient Setup: - // // 1. create a spend key: "SpendK_R" - // // 2. from SpendK_R derive a Shielded Payment Address: SPA_R - // // Test Procedure: - // // 1. construct a transaction "spending" from a SpendK_S output to SPA_R - // // 2. publish the transaction to the mempool - // // 3. mine a block - // // Constraints: - // // 1. SpendK_S controls start - spend funds - // // 2. SpendK_R controls 0 + spend funds - // let (regtest_manager, _cph, faucet, recipient) = - // scenarios::faucet_recipient_default().await; + #[tokio::test] + async fn sapling_to_sapling_scan_together() { + // Create an incoming transaction, and then send that transaction, and scan everything together, to make sure it works. + // (For this test, the Sapling Domain is assumed in all cases.) + // Sender Setup: + // 1. create a spend key: SpendK_S + // 2. derive a Shielded Payment Address from SpendK_S: SPA_KS + // 3. construct a Block Reward Transaction where SPA_KS receives a block reward: BRT + // 4. publish BRT + // 5. optionally mine a block including BRT <-- There are two separate tests to run + // 6. optionally mine sufficient subsequent blocks to "validate" BRT + // Recipient Setup: + // 1. create a spend key: "SpendK_R" + // 2. from SpendK_R derive a Shielded Payment Address: SPA_R + // Test Procedure: + // 1. construct a transaction "spending" from a SpendK_S output to SPA_R + // 2. publish the transaction to the mempool + // 3. mine a block + // Constraints: + // 1. SpendK_S controls start - spend funds + // 2. SpendK_R controls 0 + spend funds + let (regtest_manager, _cph, mut faucet, mut recipient) = + scenarios::faucet_recipient_default().await; - // // Give the faucet a block reward - // zingolib::testutils::increase_height_and_wait_for_client(®test_manager, &faucet, 1) - // .await - // .unwrap(); - // let value = 100_000; + // Give the faucet a block reward + zingolib::testutils::increase_height_and_wait_for_client(®test_manager, &mut faucet, 1) + .await + .unwrap(); + let value = 100_000; - // // Send some sapling value to the recipient - // let txid = zingolib::testutils::send_value_between_clients_and_sync( - // ®test_manager, - // &faucet, - // &recipient, - // value, - // "sapling", - // ) - // .await - // .unwrap(); + // Send some sapling value to the recipient + let txid = zingolib::testutils::send_value_between_clients_and_sync( + ®test_manager, + &mut faucet, + &mut recipient, + value, + PoolType::Shielded(ShieldedProtocol::Sapling), + ) + .await + .unwrap(); - // let spent_value = 250; + let spent_value = 250; - // // Construct transaction to wallet-external recipient-address. - // let exit_zaddr = get_base_address_macro!(faucet, "sapling"); - // let spent_txid = - // from_inputs::quick_send(&recipient, vec![(&exit_zaddr, spent_value, None)]) - // .await - // .unwrap() - // .first() - // .to_string(); + // Construct transaction to wallet-external recipient-address. + let exit_zaddr = get_base_address_macro!(faucet, "sapling"); + let spent_txid = + from_inputs::quick_send(&recipient, vec![(&exit_zaddr, spent_value, None)]) + .await + .unwrap() + .first() + .to_string(); - // zingolib::testutils::increase_height_and_wait_for_client(®test_manager, &recipient, 1) - // .await - // .unwrap(); - // // 5. Check the transaction list to make sure we got all transactions - // let list = recipient.do_list_transactions().await; + zingolib::testutils::increase_height_and_wait_for_client( + ®test_manager, + &mut recipient, + 1, + ) + .await + .unwrap(); + // 5. Check the transaction list to make sure we got all transactions + let transactions = recipient + .wallet + .lock() + .await + .transaction_summaries() + .await + .0; - // assert_eq!(list[0]["block_height"].as_u64().unwrap(), 5); - // assert_eq!(list[0]["txid"], txid.to_string()); - // assert_eq!(list[0]["amount"].as_i64().unwrap(), (value as i64)); + assert_eq!(transactions.first().unwrap().blockheight(), 5.into()); + assert_eq!(transactions.first().unwrap().txid().to_string(), txid); + assert_eq!(transactions.first().unwrap().value(), value); - // assert_eq!(list[1]["block_height"].as_u64().unwrap(), 6); - // assert_eq!(list[1]["txid"], spent_txid); - // assert_eq!( - // list[1]["amount"].as_i64().unwrap(), - // -((spent_value + u64::from(MINIMUM_FEE)) as i64) - // ); - // assert_eq!(list[1]["outgoing_metadata"][0]["address"], exit_zaddr); - // assert_eq!( - // list[1]["outgoing_metadata"][0]["value"].as_u64().unwrap(), - // spent_value - // ); - // } - // FIXME: - // #[tokio::test] - // async fn sapling_incoming_sapling_outgoing() { - // // TODO: Add assertions about Sapling change note. - // let (regtest_manager, _cph, faucet, recipient) = - // scenarios::faucet_recipient_default().await; - // let value = 100_000; + assert_eq!(transactions.get(1).unwrap().blockheight(), 6.into()); + assert_eq!(transactions.get(1).unwrap().txid().to_string(), spent_txid); + assert_eq!( + transactions.get(1).unwrap().value(), + spent_value + u64::from(MINIMUM_FEE) + ); + assert_eq!( + transactions + .get(1) + .unwrap() + .outgoing_orchard_notes() + .first() + .unwrap() + .recipient, + exit_zaddr + ); + assert_eq!( + transactions + .get(1) + .unwrap() + .outgoing_orchard_notes() + .first() + .unwrap() + .value, + spent_value + ); + } + #[tokio::test] + async fn sapling_incoming_sapling_outgoing() { + // TODO: Add assertions about Sapling change note. + let (regtest_manager, _cph, faucet, mut recipient) = + scenarios::faucet_recipient_default().await; + let value = 100_000; - // // 2. Send an incoming transaction to fill the wallet - // let faucet_funding_txid = from_inputs::quick_send( - // &faucet, - // vec![(&get_base_address_macro!(recipient, "sapling"), value, None)], - // ) - // .await - // .unwrap() - // .first() - // .to_string(); - // zingolib::testutils::increase_height_and_wait_for_client(®test_manager, &recipient, 1) - // .await - // .unwrap(); + // 2. Send an incoming transaction to fill the wallet + let faucet_funding_txid = from_inputs::quick_send( + &faucet, + vec![(&get_base_address_macro!(&recipient, "sapling"), value, None)], + ) + .await + .unwrap() + .first() + .to_string(); + zingolib::testutils::increase_height_and_wait_for_client( + ®test_manager, + &mut recipient, + 1, + ) + .await + .unwrap(); - // assert_eq!(recipient.wallet.last_synced_height().await, 4); + assert_eq!( + recipient + .wallet + .lock() + .await + .sync_state + .fully_scanned_height(), + 4.into() + ); - // // 3. Check the balance is correct, and we received the incoming transaction from ?outside? - // let b = recipient.do_balance().await; - // let addresses = recipient.do_addresses(UAReceivers::All).await; - // assert_eq!(b.sapling_balance.unwrap(), value); - // assert_eq!(b.unverified_sapling_balance.unwrap(), 0); - // assert_eq!(b.spendable_sapling_balance.unwrap(), value); - // assert_eq!( - // addresses[0]["receivers"]["sapling"], - // encode_payment_address( - // recipient.config().chain.hrp_sapling_payment_address(), - // recipient.wallet.wallet_capability().addresses()[0] - // .sapling() - // .unwrap() - // ), - // ); + // 3. Check the balance is correct, and we received the incoming transaction from ?outside? + let network = recipient.wallet.lock().await.network; + let balance = recipient.do_balance().await; + assert_eq!(balance.sapling_balance.unwrap(), value); + assert_eq!(balance.unverified_sapling_balance.unwrap(), 0); + assert_eq!(balance.spendable_sapling_balance.unwrap(), value); - // let list = recipient.do_list_transactions().await; - // if let JsonValue::Array(list) = list { - // assert_eq!(list.len(), 1); - // let faucet_sent_transaction = list[0].clone(); - - // assert_eq!(faucet_sent_transaction["txid"], faucet_funding_txid); - // assert_eq!(faucet_sent_transaction["amount"].as_u64().unwrap(), value); - // assert_eq!( - // faucet_sent_transaction["address"], - // recipient.wallet.wallet_capability().addresses()[0] - // .encode(&recipient.config().chain) - // ); - // assert_eq!(faucet_sent_transaction["block_height"].as_u64().unwrap(), 4); - // } else { - // panic!("Expecting an array"); - // } + { + let recipient_sapling_address = *recipient.wallet.lock().await.unified_addresses[0] + .sapling() + .unwrap(); + let transactions = &recipient.wallet.lock().await.wallet_transactions; + assert_eq!(transactions.len(), 1); + let received_transaction = transactions + .get(&txid_from_hex_encoded_str(&faucet_funding_txid).unwrap()) + .unwrap(); - // // 4. Send z-to-z transaction to external z address with a memo - // let sent_value = 2_000; - // let outgoing_memo = "Outgoing Memo"; + assert_eq!(received_transaction.txid().to_string(), faucet_funding_txid); + assert_eq!( + received_transaction + .status() + .get_confirmed_height() + .unwrap(), + 4.into() + ); - // let sent_transaction_id = from_inputs::quick_send( - // &recipient, - // vec![( - // &get_base_address_macro!(faucet, "sapling"), - // sent_value, - // Some(outgoing_memo), - // )], - // ) - // .await - // .unwrap() - // .first() - // .to_string(); + let received_note = received_transaction + .sapling_notes() + .first() + .unwrap() + .clone(); - // // 5. Check the pending transaction is present - // // 5.1 Check notes + assert_eq!(received_note.value(), value); + assert_eq!(received_note.note().recipient(), recipient_sapling_address); + } - // let notes = dbg!(recipient.do_list_notes(true).await); + // 4. Send z-to-z transaction to external z address with a memo + let sent_value = 2_000; + let outgoing_memo = "Outgoing Memo"; - // // Has a new (pending) unspent note (the change) - // assert_eq!(notes["unspent_orchard_notes"].len(), 0); // Change for z-to-z is now sapling + let sent_transaction_id = from_inputs::quick_send( + &recipient, + vec![( + &get_base_address_macro!(faucet, "sapling"), + sent_value, + Some(outgoing_memo), + )], + ) + .await + .unwrap() + .first() + .to_string(); - // assert_eq!(notes["spent_sapling_notes"].len(), 0); + // 5. Check the pending transaction is present + // 5.1 Check notes - // // note is now pending_spent - // assert_eq!(notes["pending_sapling_notes"].len(), 1); - // assert_eq!( - // notes["pending_sapling_notes"][0]["created_in_txid"], - // faucet_funding_txid.to_string() - // ); - // assert_eq!( - // notes["pending_sapling_notes"][0]["pending_spent"], - // sent_transaction_id - // ); - // assert!(notes["pending_sapling_notes"][0]["spent"].is_null()); + let sapling_notes = recipient + .wallet + .lock() + .await + .note_summaries::(true); - // // Check transaction list - // let list = recipient.do_list_transactions().await; + assert_eq!( + recipient + .wallet + .lock() + .await + .wallet_outputs::() + .len(), + 0 + ); + assert_eq!( + sapling_notes + .iter() + .filter(|note| note.spend_status.is_confirmed_spent()) + .count(), + 0 + ); + assert_eq!( + sapling_notes + .iter() + .filter(|note| note.spend_status.is_pending_spent()) + .count(), + 1 + ); - // assert_eq!(list.len(), 2); - // let send_transaction = list - // .members() - // .find(|transaction| transaction["txid"] == sent_transaction_id) - // .unwrap(); + let pending_sapling_note = *sapling_notes + .iter() + .filter(|¬e| note.spend_status.is_pending_spent()) + .collect::>() + .first() + .unwrap(); + assert_eq!(pending_sapling_note.txid.to_string(), faucet_funding_txid); + if let SpendStatus::TransmittedSpent(txid) = pending_sapling_note.spend_status { + assert_eq!(txid.to_string(), sent_transaction_id); + } else { + panic!("incorrect spend status!"); + } - // assert_eq!(send_transaction["txid"], sent_transaction_id); - // assert_eq!( - // send_transaction["amount"].as_i64().unwrap(), - // -(sent_value as i64 + u64::from(MINIMUM_FEE) as i64) - // ); - // assert!(send_transaction["pending"].as_bool().unwrap()); - // assert_eq!(send_transaction["block_height"].as_u64().unwrap(), 5); + { + // Check transaction list + let transactions = &recipient.wallet.lock().await.wallet_transactions; + assert_eq!(transactions.len(), 2); + let sent_transaction = transactions + .get(&txid_from_hex_encoded_str(&sent_transaction_id).unwrap()) + .unwrap(); - // assert_eq!( - // send_transaction["outgoing_metadata"][0]["address"], - // get_base_address_macro!(faucet, "sapling") - // ); - // assert_eq!( - // send_transaction["outgoing_metadata"][0]["memo"], - // outgoing_memo - // ); - // assert_eq!( - // send_transaction["outgoing_metadata"][0]["value"] - // .as_u64() - // .unwrap(), - // sent_value - // ); + assert_eq!(sent_transaction.txid().to_string(), sent_transaction_id); + assert_eq!(sent_transaction.total_value_sent(), sent_value); + assert!(!sent_transaction.status().is_confirmed()); + assert_eq!(sent_transaction.status().get_height(), 5.into()); - // // 6. Mine the sent transaction - // zingolib::testutils::increase_height_and_wait_for_client(®test_manager, &recipient, 1) - // .await - // .unwrap(); + let outgoing_sapling_note = sent_transaction.outgoing_sapling_notes().first().unwrap(); + assert_eq!( + outgoing_sapling_note.encoded_recipient(&network), + get_base_address_macro!(faucet, "sapling") + ); + if let Memo::Text(memo) = outgoing_sapling_note.memo() { + assert_eq!(&String::from(memo.clone()), outgoing_memo); + } else { + panic!("no text memo"); + } + assert_eq!(outgoing_sapling_note.value(), sent_value); + } - // assert!(!send_transaction.contains("pending")); - // assert_eq!(send_transaction["block_height"].as_u64().unwrap(), 5); + // 6. Mine the sent transaction + zingolib::testutils::increase_height_and_wait_for_client( + ®test_manager, + &mut recipient, + 1, + ) + .await + .unwrap(); - // // 7. Check the notes to see that we have one spent sapling note and one unspent orchard note (change) - // // Which is immediately spendable. - // let notes = recipient.do_list_notes(true).await; - // println!("{}", json::stringify_pretty(notes.clone(), 4)); + { + let transactions = &recipient.wallet.lock().await.wallet_transactions; + let sent_transaction = transactions + .get(&txid_from_hex_encoded_str(&sent_transaction_id).unwrap()) + .unwrap(); + assert!(sent_transaction.status().is_confirmed()); + assert_eq!( + sent_transaction.status().get_confirmed_height().unwrap(), + 5.into() + ); + } - // assert_eq!(notes["spent_sapling_notes"].len(), 1); - // assert_eq!( - // notes["spent_sapling_notes"][0]["created_in_block"] - // .as_u64() - // .unwrap(), - // 4 - // ); - // assert_eq!( - // notes["spent_sapling_notes"][0]["value"].as_u64().unwrap(), - // value - // ); - // assert!(!notes["spent_sapling_notes"][0]["spendable"] - // .as_bool() - // .unwrap()); // Already spent - // assert_eq!( - // notes["spent_sapling_notes"][0]["spent"], - // sent_transaction_id - // ); - // assert_eq!( - // notes["spent_sapling_notes"][0]["spent_at_height"] - // .as_u64() - // .unwrap(), - // 5 - // ); - // } + // 7. Check the notes to see that we have one spent sapling note and one unspent sapling note (change) + // Which is immediately spendable. + let recipient_wallet = recipient.wallet.lock().await; + let sapling_notes = recipient_wallet.note_summaries::(true); + let orchard_notes = recipient_wallet.note_summaries::(true); + + assert!(orchard_notes.is_empty()); + assert_eq!(sapling_notes.len(), 2); + assert_eq!( + sapling_notes + .iter() + .filter(|¬e| note.spend_status.is_unspent()) + .count(), + 1 + ); + assert_eq!( + sapling_notes + .iter() + .filter(|¬e| note.spend_status.is_confirmed_spent()) + .count(), + 1 + ); + let spent_sapling_note = sapling_notes + .iter() + .find(|¬e| note.spend_status.is_confirmed_spent()) + .unwrap(); + assert_eq!(spent_sapling_note.block_height, 4.into()); + assert_eq!(spent_sapling_note.value, value); + if let SpendStatus::Spent(txid) = spent_sapling_note.spend_status { + assert_eq!(txid.to_string(), sent_transaction_id); + assert_eq!( + recipient_wallet + .wallet_transactions + .get(&txid) + .unwrap() + .status() + .get_confirmed_height() + .unwrap(), + 5.into() + ); + } else { + panic!("note not spent!") + } + } #[tokio::test] async fn sapling_dust_fee_collection() { let (regtest_manager, __cph, faucet, mut recipient) = @@ -2800,7 +2889,7 @@ mod slow { let remaining_orchard = for_orchard - (6 * fee); check_client_balances!(recipient, o: remaining_orchard s: for_sapling t: 0); } - // FIXME: + // FIXME: zingo2 yet to implement transaction filter in sync engine. its also not clear how this test exceeds the tx filter. // #[tokio::test] // async fn sandblast_filter_preserves_trees() { // let (ref regtest_manager, _cph, ref faucet, ref recipient, _txid) = @@ -3568,264 +3657,139 @@ mod slow { ); } - // FIXME: - // #[tokio::test] - // async fn zero_value_change_to_orchard_created() { - // let (regtest_manager, _cph, faucet, recipient, _txid) = - // scenarios::faucet_funded_recipient_default(100_000).await; - - // zingolib::testutils::increase_height_and_wait_for_client(®test_manager, &recipient, 1) - // .await - // .unwrap(); - - // // 1. Send a transaction to an external z addr - // let sent_zvalue = 80_000; - // let sent_zmemo = "Ext z"; - // let sent_transaction_id = from_inputs::quick_send( - // &recipient, - // vec![( - // &get_base_address_macro!(faucet, "sapling"), - // sent_zvalue, - // Some(sent_zmemo), - // )], - // ) - // .await - // .unwrap() - // .first() - // .to_string(); - - // // Validate transaction - // zingolib::testutils::increase_height_and_wait_for_client(®test_manager, &recipient, 1) - // .await - // .unwrap(); - - // let requested_txid = &zingolib::wallet::utils::txid_from_slice( - // hex::decode(sent_transaction_id.clone()) - // .unwrap() - // .into_iter() - // .rev() - // .collect::>() - // .as_slice(), - // ); - // let orchard_note = recipient - // .wallet - // .transaction_context - // .transaction_metadata_set - // .read() - // .await - // .transaction_records_by_id - // .get(requested_txid) - // .unwrap() - // .orchard_notes - // .first() - // .unwrap() - // .clone(); - // assert_eq!(orchard_note.value(), 0); - // } - // #[tokio::test] - // #[ignore = "test does not correspond to real-world case"] - // async fn aborted_resync() { - // let (regtest_manager, _cph, faucet, recipient, _txid) = - // scenarios::faucet_funded_recipient_default(500_000).await; - - // zingolib::testutils::increase_height_and_wait_for_client(®test_manager, &recipient, 15) - // .await - // .unwrap(); - - // // 1. Send a transaction to both external t-addr and external z addr and mine it - // let sent_zvalue = 80_000; - // let sent_zmemo = "Ext z"; - // let sent_transaction_id = from_inputs::quick_send( - // &recipient, - // vec![( - // &get_base_address_macro!(faucet, "sapling"), - // sent_zvalue, - // Some(sent_zmemo), - // )], - // ) - // .await - // .unwrap() - // .first() - // .to_string(); - - // zingolib::testutils::increase_height_and_wait_for_client(®test_manager, &recipient, 5) - // .await - // .unwrap(); - - // let notes_before = recipient.do_list_notes(true).await; - // let list_before = recipient.do_list_transactions().await; - // let requested_txid = &zingolib::wallet::utils::txid_from_slice( - // hex::decode(sent_transaction_id.clone()) - // .unwrap() - // .into_iter() - // .rev() - // .collect::>() - // .as_slice(), - // ); - // let witness_before = recipient - // .wallet - // .transaction_context - // .transaction_metadata_set - // .read() - // .await - // .witness_trees() - // .unwrap() - // .witness_tree_orchard - // .witness_at_checkpoint_depth( - // recipient - // .wallet - // .transaction_context - // .transaction_metadata_set - // .read() - // .await - // .transaction_records_by_id - // .get(requested_txid) - // .unwrap() - // .orchard_notes - // .first() - // .unwrap() - // .witnessed_position - // .unwrap(), - // 0, - // ); - - // // 5. Now, we'll manually remove some of the blocks in the wallet, pretending that the sync was aborted in the middle. - // // We'll remove the top 20 blocks, so now the wallet only has the first 3 blocks - // recipient.wallet.last_100_blocks.write().await.drain(0..20); - // assert_eq!(recipient.wallet.last_synced_height().await, 5); - - // // 6. Do a sync again - // recipient.do_sync(true).await.unwrap(); - // assert_eq!(recipient.wallet.last_synced_height().await, 25); - - // // 7. Should be exactly the same - // let notes_after = recipient.do_list_notes(true).await; - // let list_after = recipient.do_list_transactions().await; - // let witness_after = recipient - // .wallet - // .transaction_context - // .transaction_metadata_set - // .read() - // .await - // .witness_trees() - // .unwrap() - // .witness_tree_orchard - // .witness_at_checkpoint_depth( - // recipient - // .wallet - // .transaction_context - // .transaction_metadata_set - // .read() - // .await - // .transaction_records_by_id - // .get(requested_txid) - // .unwrap() - // .orchard_notes - // .first() - // .unwrap() - // .witnessed_position - // .unwrap(), - // 0, - // ); + #[tokio::test] + async fn zero_value_change_to_orchard_created() { + let (regtest_manager, _cph, faucet, mut recipient, _txid) = + scenarios::faucet_funded_recipient_default(100_000).await; - // assert_eq!(notes_before, notes_after); - // assert_eq!(list_before, list_after); - // assert_eq!(witness_before.unwrap(), witness_after.unwrap()); - // } - // #[tokio::test] - // async fn mempool_spends_correctly_marked_pending_spent() { - // let (_regtest_manager, _cph, _faucet, recipient, _txid) = - // scenarios::faucet_funded_recipient_default(1_000_000).await; - // from_inputs::quick_send( - // &recipient, - // vec![( - // &get_base_address_macro!(recipient, "sapling"), - // 100_000, - // None, - // )], - // ) - // .await - // .unwrap(); - // let recipient_saved = recipient.export_save_buffer_async().await.unwrap(); - // let recipient_loaded = std::sync::Arc::new( - // LightClient::read_wallet_from_buffer_async(recipient.config(), &recipient_saved[..]) - // .await - // .unwrap(), - // ); - // LightClient::start_mempool_monitor(recipient_loaded.clone()).unwrap(); - // // This seems to be long enough for the mempool monitor to kick in. - // // One second is insufficient. Even if this fails, this can only ever be - // // a false negative, giving us a balance of 100_000. Still, could be improved. - // tokio::time::sleep(Duration::from_secs(5)).await; - // assert_eq!( - // recipient_loaded.do_balance().await.orchard_balance, - // Some(880_000) - // ); - // } - // #[ignore] - // #[tokio::test] - // async fn timed_sync_interrupt() { - // let (regtest_manager, _cph, faucet, recipient) = - // scenarios::faucet_recipient_default().await; - // for i in 1..4 { - // faucet.do_sync(false).await.unwrap(); - // from_inputs::quick_send( - // &faucet, - // vec![(&get_base_address_macro!(recipient, "sapling"), 10_100, None)], - // ) - // .await - // .unwrap(); - // let chainwait: u32 = 6; - // let amount: u64 = u64::from(chainwait * i); - // zingolib::testutils::increase_server_height(®test_manager, chainwait).await; - // recipient.do_sync(false).await.unwrap(); - // from_inputs::quick_send( - // &recipient, - // vec![(&get_base_address_macro!(recipient, "unified"), amount, None)], - // ) - // .await - // .unwrap(); - // } - // zingolib::testutils::increase_server_height(®test_manager, 1).await; - - // let _synciiyur = recipient.do_sync(false).await; - // // let summ_sim = recipient.list_value_transfers(true).await; - // let bala_sim = recipient.do_balance().await; - - // recipient.clear_state().await; - // dbg!("finished basic sync. restarting for interrupted data"); - // let timeout = 28; - // let race_condition = - // zingolib::testutils::interrupts::sync_with_timeout_millis(&recipient, timeout).await; - // match race_condition { - // Ok(_) => { - // println!("synced in less than {} millis ", timeout); - // dbg!("syncedd"); - // } - // Err(_) => { - // println!("interrupted after {} millis ", timeout); - // dbg!("interruptedidd!"); - // } - // } + zingolib::testutils::increase_height_and_wait_for_client( + ®test_manager, + &mut recipient, + 1, + ) + .await + .unwrap(); - // // let summ_int = recipient.list_value_transfers(true).await; - // // let bala_int = recipient.do_balance().await; - // let _synciiyur = recipient.do_sync(false).await; - // // let summ_syn = recipient.list_value_transfers(true).await; - // let bala_syn = recipient.do_balance().await; + // 1. Send a transaction to an external z addr + let sent_zvalue = 80_000; + let sent_zmemo = "Ext z"; + let sent_transaction_id = from_inputs::quick_send( + &recipient, + vec![( + &get_base_address_macro!(faucet, "sapling"), + sent_zvalue, + Some(sent_zmemo), + )], + ) + .await + .unwrap() + .first() + .to_string(); - // dbg!( - // &recipient - // .wallet - // .transaction_context - // .transaction_metadata_set - // .read() - // .await - // .transaction_records_by_id - // ); + // Validate transaction + zingolib::testutils::increase_height_and_wait_for_client( + ®test_manager, + &mut recipient, + 1, + ) + .await + .unwrap(); - // assert_eq!(bala_sim, bala_syn); - // } + let sent_txid = txid_from_hex_encoded_str(&sent_transaction_id).unwrap(); + let orchard_note = recipient + .wallet + .lock() + .await + .wallet_transactions + .get(&sent_txid) + .unwrap() + .orchard_notes() + .first() + .unwrap() + .clone(); + assert_eq!(orchard_note.value(), 0); + } + #[tokio::test] + async fn mempool_spends_correctly_marked_pending_spent() { + let (regtest_manager, _cph, faucet, mut recipient, _txid) = + scenarios::faucet_funded_recipient_default(1_000_000).await; + let sent_txids = from_inputs::quick_send( + &recipient, + vec![(&get_base_address_macro!(faucet, "sapling"), 100_000, None)], + ) + .await + .unwrap(); + recipient.sync_and_await().await.unwrap(); + { + let recipient_wallet = recipient.wallet.lock().await; + let sapling_notes = recipient_wallet.wallet_outputs::(); + assert_eq!(sapling_notes.len(), 0); + let orchard_notes = recipient_wallet.wallet_outputs::(); + assert_eq!(orchard_notes.len(), 2); + let spent_orchard_note = (*orchard_notes + .iter() + .find(|&¬e| note.value() == 1_000_000) + .unwrap()) + .clone(); + assert_eq!( + recipient_wallet.output_spend_status(&spent_orchard_note), + SpendStatus::MempoolSpent(*sent_txids.first()) + ); + let orchard_change_note = (*orchard_notes + .iter() + .find(|&¬e| note.value() == 880_000) + .unwrap()) + .clone(); + assert_eq!( + recipient_wallet.output_spend_status(&orchard_change_note), + SpendStatus::Unspent + ); + assert!(!recipient_wallet + .output_transaction(&orchard_change_note) + .status() + .is_confirmed()); + let balance = recipient.do_balance().await; + assert_eq!(balance.orchard_balance, Some(880_000)); + assert_eq!(balance.unverified_orchard_balance, Some(880_000)); + assert_eq!(balance.verified_orchard_balance, Some(0)); + } + increase_height_and_wait_for_client(®test_manager, &mut recipient, 1) + .await + .unwrap(); + { + let recipient_wallet = recipient.wallet.lock().await; + let sapling_notes = recipient_wallet.wallet_outputs::(); + assert_eq!(sapling_notes.len(), 0); + let orchard_notes = recipient_wallet.wallet_outputs::(); + assert_eq!(orchard_notes.len(), 2); + let spent_orchard_note = (*orchard_notes + .iter() + .find(|&¬e| note.value() == 1_000_000) + .unwrap()) + .clone(); + assert_eq!( + recipient_wallet.output_spend_status(&spent_orchard_note), + SpendStatus::Spent(*sent_txids.first()) + ); + let orchard_change_note = (*orchard_notes + .iter() + .find(|&¬e| note.value() == 880_000) + .unwrap()) + .clone(); + assert_eq!( + recipient_wallet.output_spend_status(&orchard_change_note), + SpendStatus::Unspent + ); + assert!(recipient_wallet + .output_transaction(&orchard_change_note) + .status() + .is_confirmed()); + let balance = recipient.do_balance().await; + assert_eq!(balance.orchard_balance, Some(880_000)); + assert_eq!(balance.unverified_orchard_balance, Some(0)); + assert_eq!(balance.verified_orchard_balance, Some(880_000)); + } + } } mod basic_transactions { @@ -3872,7 +3836,7 @@ mod basic_transactions { faucet.sync_and_await().await.unwrap(); } - // FIXME: + // FIXME: zingo2 rewrite action / inputs / outputs counting using new interface // #[tokio::test] // async fn standard_send_fees() { // let (regtest_manager, _cph, faucet, recipient) = diff --git a/libtonode-tests/tests/wallet.rs b/libtonode-tests/tests/wallet.rs index 8f01006fc..502f4e3d4 100644 --- a/libtonode-tests/tests/wallet.rs +++ b/libtonode-tests/tests/wallet.rs @@ -1,107 +1,75 @@ #![forbid(unsafe_code)] mod load_wallet { + use zcash_client_backend::{PoolType, ShieldedProtocol}; + use zingolib::{ + config::RegtestNetwork, + testutils::{paths::get_cargo_manifest_dir, scenarios}, + wallet::disk::testing::examples, + }; + + // FIXME: sync integration semi-complete, need to transribe all the old do_list_transactions printouts to new types + // #[tokio::test] + // async fn load_old_wallet_at_reorged_height() { + // let regtest_network = RegtestNetwork::new(1, 1, 1, 1, 1, 1, 200); + // let (ref regtest_manager, cph, ref faucet) = scenarios::faucet( + // PoolType::Shielded(ShieldedProtocol::Orchard), + // regtest_network, + // false, + // ) + // .await; + // println!("Shutting down initial zcd/lwd unneeded processes"); + // drop(cph); + + // let zcd_datadir = ®test_manager.zcashd_data_dir; + // let zingo_datadir = ®test_manager.zingo_datadir; + // // This test is the unique consumer of: + // // zingolib/src/testvectors/old_wallet_reorg_test_wallet + // let cached_data_dir = get_cargo_manifest_dir() + // .parent() + // .unwrap() + // .join("zingolib/src/testvectors") + // .join("old_wallet_reorg_test_wallet"); + // let zcd_source = cached_data_dir + // .join("zcashd") + // .join(".") + // .to_string_lossy() + // .to_string(); + // let zcd_dest = zcd_datadir.to_string_lossy().to_string(); + // std::process::Command::new("rm") + // .arg("-r") + // .arg(&zcd_dest) + // .output() + // .expect("directory rm failed"); + // std::fs::DirBuilder::new() + // .create(&zcd_dest) + // .expect("Dir recreate failed"); + // std::process::Command::new("cp") + // .arg("-r") + // .arg(zcd_source) + // .arg(zcd_dest) + // .output() + // .expect("directory copy failed"); + // let zingo_source = cached_data_dir + // .join("zingo-wallet.dat") + // .to_string_lossy() + // .to_string(); + // let zingo_dest = zingo_datadir.to_string_lossy().to_string(); + // std::process::Command::new("cp") + // .arg("-f") + // .arg(zingo_source) + // .arg(&zingo_dest) + // .output() + // .expect("wallet copy failed"); + // let _cph = regtest_manager.launch(false).unwrap(); + // println!("loading wallet"); + + // let recipient = examples::NetworkSeedVersion::Regtest( + // examples::RegtestSeedVersion::HospitalMuseum(examples::HospitalMuseumVersion::V27), + // ) + // .load_example_wallet_with_client() + // .await; - // use std::fs::File; - - // use zcash_client_backend::PoolType; - // use zcash_client_backend::ShieldedProtocol; - // use zingolib::check_client_balances; - // use zingolib::config::RegtestNetwork; - // use zingolib::config::ZingoConfig; - use zingolib::get_base_address_macro; - // use zingolib::lightclient::send::send_with_proposal::QuickSendError; - // use zingolib::lightclient::LightClient; - // use zingolib::lightclient::PoolBalances; - // use zingolib::testutils::chain_generics::conduct_chain::ConductChain as _; - // use zingolib::testutils::chain_generics::libtonode::LibtonodeEnvironment; - use zingolib::testutils::lightclient::from_inputs; - // use zingolib::testutils::paths::get_cargo_manifest_dir; - use zingolib::testutils::scenarios; - // use zingolib::utils; - // use zingolib::wallet::disk::testing::examples; - // use zingolib::wallet::propose::ProposeSendError::Proposal; - - // FIXME: sync integration - // #[tokio::test] - // async fn load_old_wallet_at_reorged_height() { - // let regtest_network = RegtestNetwork::new(1, 1, 1, 1, 1, 1, 200); - // let (ref regtest_manager, cph, ref faucet) = scenarios::faucet( - // PoolType::Shielded(ShieldedProtocol::Orchard), - // regtest_network, - // false, - // ) - // .await; - // println!("Shutting down initial zcd/lwd unneeded processes"); - // drop(cph); - - // let zcd_datadir = ®test_manager.zcashd_data_dir; - // let zingo_datadir = ®test_manager.zingo_datadir; - // // This test is the unique consumer of: - // // zingolib/src/testvectors/old_wallet_reorg_test_wallet - // let cached_data_dir = get_cargo_manifest_dir() - // .parent() - // .unwrap() - // .join("zingolib/src/testvectors") - // .join("old_wallet_reorg_test_wallet"); - // let zcd_source = cached_data_dir - // .join("zcashd") - // .join(".") - // .to_string_lossy() - // .to_string(); - // let zcd_dest = zcd_datadir.to_string_lossy().to_string(); - // std::process::Command::new("rm") - // .arg("-r") - // .arg(&zcd_dest) - // .output() - // .expect("directory rm failed"); - // std::fs::DirBuilder::new() - // .create(&zcd_dest) - // .expect("Dir recreate failed"); - // std::process::Command::new("cp") - // .arg("-r") - // .arg(zcd_source) - // .arg(zcd_dest) - // .output() - // .expect("directory copy failed"); - // let zingo_source = cached_data_dir - // .join("zingo-wallet.dat") - // .to_string_lossy() - // .to_string(); - // let zingo_dest = zingo_datadir.to_string_lossy().to_string(); - // std::process::Command::new("cp") - // .arg("-f") - // .arg(zingo_source) - // .arg(&zingo_dest) - // .output() - // .expect("wallet copy failed"); - // let _cph = regtest_manager.launch(false).unwrap(); - // println!("loading wallet"); - - // let wallet = examples::NetworkSeedVersion::Regtest( - // examples::RegtestSeedVersion::HospitalMuseum(examples::HospitalMuseumVersion::V27), - // ) - // .load_example_wallet() - // .await; - - // // let wallet = zingolib::testutils::load_wallet( - // // zingo_dest.into(), - // // ChainType::Regtest(regtest_network), - // // ) - // // .await; - // println!("setting uri"); - // *wallet - // .transaction_context - // .config - // .lightwalletd_uri - // .write() - // .unwrap() = faucet.get_server_uri(); - // println!("creating lightclient"); - // let recipient = LightClient::create_from_wallet_async(wallet).await.unwrap(); - // println!( - // "pre-sync transactions: {}", - // recipient.do_list_transactions().await.pretty(2) - // ); - // let expected_pre_sync_transactions = r#"[ + // let expected_pre_sync_transactions = r#"[ // { // "outgoing_metadata": [], // "amount": 100000, @@ -139,12 +107,12 @@ mod load_wallet { // "address": "uregtest1wdukkmv5p5n824e8ytnc3m6m77v9vwwl7hcpj0wangf6z23f9x0fnaen625dxgn8cgp67vzw6swuar6uwp3nqywfvvkuqrhdjffxjfg644uthqazrtxhrgwac0a6ujzgwp8y9cwthjeayq8r0q6786yugzzyt9vevxn7peujlw8kp3vf6d8p4fvvpd8qd5p7xt2uagelmtf3vl6w3u8" // } // ]"#; - // assert_eq!( - // expected_pre_sync_transactions, - // recipient.do_list_transactions().await.pretty(2) - // ); - // recipient.do_sync(false).await.unwrap(); - // let expected_post_sync_transactions = r#"[ + // assert_eq!( + // expected_pre_sync_transactions, + // recipient.do_list_transactions().await.pretty(2) + // ); + // recipient.do_sync(false).await.unwrap(); + // let expected_post_sync_transactions = r#"[ // { // "outgoing_metadata": [], // "amount": 100000, @@ -170,28 +138,28 @@ mod load_wallet { // "address": "uregtest1wdukkmv5p5n824e8ytnc3m6m77v9vwwl7hcpj0wangf6z23f9x0fnaen625dxgn8cgp67vzw6swuar6uwp3nqywfvvkuqrhdjffxjfg644uthqazrtxhrgwac0a6ujzgwp8y9cwthjeayq8r0q6786yugzzyt9vevxn7peujlw8kp3vf6d8p4fvvpd8qd5p7xt2uagelmtf3vl6w3u8" // } // ]"#; - // assert_eq!( - // expected_post_sync_transactions, - // recipient.do_list_transactions().await.pretty(2) - // ); - // let expected_post_sync_balance = PoolBalances { - // sapling_balance: Some(0), - // verified_sapling_balance: Some(0), - // spendable_sapling_balance: Some(0), - // unverified_sapling_balance: Some(0), - // orchard_balance: Some(150000), - // verified_orchard_balance: Some(150000), - // spendable_orchard_balance: Some(150000), - // unverified_orchard_balance: Some(0), - // transparent_balance: Some(0), - // }; - // assert_eq!(expected_post_sync_balance, recipient.do_balance().await); - // let missing_output_index = from_inputs::quick_send( - // &recipient, - // vec![(&get_base_address_macro!(faucet, "unified"), 14000, None)], - // ) - // .await; - // if let Err(QuickSendError::ProposeSend(Proposal( + // assert_eq!( + // expected_post_sync_transactions, + // recipient.do_list_transactions().await.pretty(2) + // ); + // let expected_post_sync_balance = PoolBalances { + // sapling_balance: Some(0), + // verified_sapling_balance: Some(0), + // spendable_sapling_balance: Some(0), + // unverified_sapling_balance: Some(0), + // orchard_balance: Some(150000), + // verified_orchard_balance: Some(150000), + // spendable_orchard_balance: Some(150000), + // unverified_orchard_balance: Some(0), + // transparent_balance: Some(0), + // }; + // assert_eq!(expected_post_sync_balance, recipient.do_balance().await); + // let missing_output_index = from_inputs::quick_send( + // &recipient, + // vec![(&get_base_address_macro!(faucet, "unified"), 14000, None)], + // ) + // .await; + // if let Err(QuickSendError::ProposeSend(Proposal( // zcash_client_backend::data_api::error::Error::DataSource(zingolib::wallet::tx_map::TxMapTraitError::InputSource( // zingolib::wallet::transaction_records_by_id::trait_inputsource::InputSourceError::MissingOutputIndexes(output_error) // )), @@ -206,99 +174,6 @@ mod load_wallet { // assert!(missing_index_txids == expected_txids, "{:?}\n\n{:?}", missing_index_txids, expected_txids); // } // }; - // } - - // FIXME: sync integration - // #[tokio::test] - // async fn pending_notes_are_not_saved() { - // let mut environment = LibtonodeEnvironment::setup().await; - - // let faucet = environment.create_faucet().await; - // let recipient = environment.create_client().await; - - // environment.bump_chain().await; - // faucet.do_sync(false).await.unwrap(); - - // check_client_balances!(faucet, o: 0 s: 2_500_000_000u64 t: 0u64); - - // let pending_txid = *from_inputs::quick_send( - // &faucet, - // vec![( - // get_base_address_macro!(recipient, "unified").as_str(), - // 5_000, - // Some("this note never makes it to the wallet! or chain"), - // )], - // ) - // .await - // .unwrap() - // .first(); - - // assert!(!faucet - // .transaction_summaries() - // .await - // .iter() - // .find(|transaction_summary| transaction_summary.txid() == pending_txid) - // .unwrap() - // .status() - // .is_confirmed()); - - // assert_eq!( - // faucet.do_list_notes(true).await["unspent_orchard_notes"].len(), - // 1 - // ); - - // // Create a new client using the faucet's wallet - // let faucet_reloaded = { - // let mut wallet_location = environment.scenario_builder.regtest_manager.zingo_datadir; - // wallet_location.pop(); - // wallet_location.push("zingo_client_1"); - // // Create zingo config - // let zingo_config = ZingoConfig::build(zingolib::config::ChainType::Regtest( - // environment.regtest_network, - // )) - // .set_wallet_dir(wallet_location.clone()) - // .create(); - // wallet_location.push("zingo-wallet.dat"); - // let read_buffer = File::open(wallet_location.clone()).unwrap(); - - // // Create wallet from faucet zingo-wallet.dat - // let faucet_wallet = - // zingolib::wallet::LightWallet::read_internal(read_buffer, &zingo_config) - // .await - // .unwrap(); - - // // Create client based on config and wallet of faucet - // LightClient::create_from_wallet_async(faucet_wallet) - // .await - // .unwrap() - // }; - - // assert_eq!( - // &faucet_reloaded.do_seed_phrase().await.unwrap(), - // &faucet.do_seed_phrase().await.unwrap() - // ); // Sanity check identity - - // // assert that the unconfirmed unspent orchard note was dropped - // assert_eq!( - // faucet_reloaded.do_list_notes(true).await["unspent_orchard_notes"].len(), - // 0 - // ); - - // // assert that the unconfirmed transaction was dropped - // assert!(!faucet_reloaded - // .transaction_summaries() - // .await - // .iter() - // .any(|transaction_summary| transaction_summary.txid() == pending_txid)); - - // // sanity-check that the other transactions in the wallet are identical. - // let mut faucet_transactions = faucet.do_list_transactions().await; - // let mut faucet_reloaded_transactions = faucet_reloaded.do_list_transactions().await; - - // faucet_transactions.pop(); - // faucet_transactions.pop(); - // faucet_reloaded_transactions.pop(); - // assert_eq!(faucet_transactions, faucet_reloaded_transactions); // } #[tokio::test] diff --git a/pepper-sync/src/keys.rs b/pepper-sync/src/keys.rs index e6e977cb5..333bdbfd3 100644 --- a/pepper-sync/src/keys.rs +++ b/pepper-sync/src/keys.rs @@ -25,6 +25,7 @@ pub struct KeyId { pub account_id: zcash_primitives::zip32::AccountId, /// Scope pub scope: Scope, + // TODO: add address index and unified address recovery } pub mod transparent; diff --git a/zingolib/src/testutils.rs b/zingolib/src/testutils.rs index bb1cb40d9..4a00a894c 100644 --- a/zingolib/src/testutils.rs +++ b/zingolib/src/testutils.rs @@ -5,15 +5,18 @@ pub mod scenarios; +use crate::lightclient::error::LightClientError; // use crate::lightclient::describe::UAReceivers; use crate::wallet::data::summaries::{ BasicCoinSummary, BasicNoteSummary, TransactionSummary, TransactionSummaryInterface as _, }; -use crate::wallet::keys::unified::{UnifiedKeyStore, WalletCapability}; +use crate::wallet::keys::unified::UnifiedKeyStore; use crate::wallet::output::SpendStatus; use crate::wallet::WalletBase; +use crate::UAReceivers; use grpc_proxy::ProxyServer; pub use incrementalmerkletree; +use lightclient::get_base_address; use std::collections::HashMap; use std::io::Read; use std::string::String; @@ -208,33 +211,32 @@ fn check_spend_status_equality(first: SpendStatus, second: SpendStatus) -> bool ) } -// FIXME: zingo2 used in large test that needs re-integrating -// /// Send from sender to recipient and then sync the recipient -// pub async fn send_value_between_clients_and_sync( -// manager: &RegtestManager, -// sender: &LightClient, -// recipient: &LightClient, -// value: u64, -// address_type: &str, -// ) -> Result { -// debug!( -// "recipient address is: {}", -// &recipient.do_addresses(UAReceivers::All).await[0]["address"] -// ); -// let txid = lightclient::from_inputs::quick_send( -// sender, -// vec![( -// &crate::get_base_address_macro!(recipient, address_type), -// value, -// None, -// )], -// ) -// .await -// .unwrap(); -// increase_height_and_wait_for_client(manager, sender, 1).await?; -// recipient.do_sync(false).await?; -// Ok(txid.first().to_string()) -// } +/// Send from sender to recipient and then sync the recipient +pub async fn send_value_between_clients_and_sync( + manager: &RegtestManager, + sender: &mut LightClient, + recipient: &mut LightClient, + value: u64, + address_pool: PoolType, +) -> Result { + log::debug!( + "recipient address is: {}", + recipient.do_addresses(UAReceivers::All).await[0]["address"] + ); + let txid = lightclient::from_inputs::quick_send( + sender, + vec![( + &get_base_address(recipient, address_pool).await, + value, + None, + )], + ) + .await + .unwrap(); + increase_height_and_wait_for_client(manager, sender, 1).await?; + recipient.sync_and_await().await?; + Ok(txid.first().to_string()) +} /// This function increases the chain height reliably (with polling) but /// it _also_ ensures that the client state is synced. @@ -244,7 +246,7 @@ pub async fn increase_height_and_wait_for_client( manager: &RegtestManager, client: &mut LightClient, n: u32, -) -> Result<(), String> { +) -> Result<(), LightClientError> { sync_to_target_height( client, generate_n_blocks_return_new_height(manager, n) @@ -272,12 +274,14 @@ pub async fn generate_n_blocks_return_new_height( pub async fn sync_to_target_height( client: &mut LightClient, target_block_height: u32, -) -> Result<(), String> { +) -> Result<(), LightClientError> { + // sync first so ranges exist for the `fully_scanned_height` call + client.sync_and_await().await?; while u32::from(client.wallet.lock().await.sync_state.fully_scanned_height()) < target_block_height { tokio::time::sleep(Duration::from_millis(500)).await; - client.sync_and_await().await.unwrap(); + client.sync_and_await().await?; } Ok(()) } @@ -321,7 +325,7 @@ pub struct TxActionsCount { pub orchard_tx_actions: usize, } -// FIXME: +// FIXME: zingo2 rewrite with wallet data or note summaries // /// Returns number of notes used as inputs for txid as TxNotesCount (transparent_notes, sapling_notes, orchard_notes). // pub async fn tx_inputs(client: &LightClient, txid: &str) -> TxNotesCount { // let notes = client.do_list_notes(true).await; diff --git a/zingolib/src/wallet/data.rs b/zingolib/src/wallet/data.rs index 65bf9ce64..b462785d7 100644 --- a/zingolib/src/wallet/data.rs +++ b/zingolib/src/wallet/data.rs @@ -1310,6 +1310,7 @@ pub mod summaries { spend_status: SpendStatus, output_index: u32, memo: Option, + // TODO: add key id with address index, not implemented into sync engine yet } impl BasicNoteSummary { From b2c50bcc2d581a01cb51db45d40b44fcf6f6db3d Mon Sep 17 00:00:00 2001 From: Oscar Pepper Date: Wed, 26 Feb 2025 01:43:34 +0000 Subject: [PATCH 24/26] fix concrete test missed due to no FIXME --- libtonode-tests/tests/concrete.rs | 88 ++++++++++++------------------- pepper-sync/src/sync.rs | 1 - 2 files changed, 35 insertions(+), 54 deletions(-) diff --git a/libtonode-tests/tests/concrete.rs b/libtonode-tests/tests/concrete.rs index 846381a15..535af0bf9 100644 --- a/libtonode-tests/tests/concrete.rs +++ b/libtonode-tests/tests/concrete.rs @@ -114,6 +114,7 @@ mod fast { use std::str::FromStr as _; use bip0039::Mnemonic; + use pepper_sync::wallet::{OutputInterface, TransparentCoin}; use zcash_address::ZcashAddress; use zcash_client_backend::{ zip321::{Payment, TransactionRequest}, @@ -859,58 +860,40 @@ mod fast { ); } - // #[tokio::test] - // async fn utxos_are_not_prematurely_confirmed() { - // let (regtest_manager, _cph, faucet, recipient) = - // scenarios::faucet_recipient_default().await; - // from_inputs::quick_send( - // &faucet, - // vec![( - // &get_base_address_macro!(recipient, "transparent"), - // 100_000, - // None, - // )], - // ) - // .await - // .unwrap(); - // increase_height_and_wait_for_client(®test_manager, &recipient, 1) - // .await - // .unwrap(); - // let preshield_utxos = dbg!(recipient.wallet.get_utxos().await); - // recipient.quick_shield().await.unwrap(); - // let postshield_utxos = dbg!(recipient.wallet.get_utxos().await); - // assert_eq!(preshield_utxos[0].address, postshield_utxos[0].address); - // assert_eq!( - // preshield_utxos[0].output_index, - // postshield_utxos[0].output_index - // ); - // assert_eq!(preshield_utxos[0].value, postshield_utxos[0].value); - // assert_eq!(preshield_utxos[0].script, postshield_utxos[0].script); - // assert!(preshield_utxos[0].spending_tx_status().is_none()); - // assert!(postshield_utxos[0].spending_tx_status().is_some()); - // } - - // TODO: zip317 - check reorg buffer offset is still accounted for in zip317 sends, fix or delete this test - // #[tokio::test] - // async fn send_without_reorg_buffer_blocks_gives_correct_error() { - // let (_regtest_manager, _cph, faucet, mut recipient) = - // scenarios::faucet_recipient_default().await; - // recipient - // .wallet - // .transaction_context - // .config - // .reorg_buffer_offset = 4; - // println!( - // "{}", - // serde_json::to_string_pretty(&recipient.do_balance().await).unwrap() - // ); - // assert_eq!( - // from_inputs::quick_send(&recipient, vec![(&get_base_address_macro!(faucet, "unified"), 100_000, None)]) - // .await - // .unwrap_err(), - // "The reorg buffer offset has been set to 4 but there are only 1 blocks in the wallet. Please sync at least 4 more blocks before trying again" - // ); - // } + #[tokio::test] + async fn utxos_are_not_prematurely_confirmed() { + let (regtest_manager, _cph, faucet, mut recipient) = + scenarios::faucet_recipient_default().await; + from_inputs::quick_send( + &faucet, + vec![( + &get_base_address_macro!(recipient, "transparent"), + 100_000, + None, + )], + ) + .await + .unwrap(); + increase_height_and_wait_for_client(®test_manager, &mut recipient, 1) + .await + .unwrap(); + let wallet = recipient.wallet.lock().await; + let preshield_utxos = wallet.wallet_outputs::(); + assert_eq!(preshield_utxos.len(), 1); + assert!(wallet + .output_spend_status(*preshield_utxos.first().unwrap()) + .is_unspent()); + recipient.quick_shield().await.unwrap(); + let postshield_utxos = wallet.wallet_outputs::(); + assert_eq!(postshield_utxos.len(), 1); + assert!(wallet + .output_spend_status(*preshield_utxos.first().unwrap()) + .is_confirmed_spent()); + assert_eq!( + preshield_utxos.first().unwrap().output_id(), + postshield_utxos.first().unwrap().output_id(), + ); + } #[tokio::test] async fn zcashd_sapling_commitment_tree() { @@ -1272,7 +1255,6 @@ mod slow { use zingo_status::confirmation_status::ConfirmationStatus; use zingolib::config::ChainType; use zingolib::lightclient::send::send_with_proposal::QuickSendError; - use zingolib::lightclient::LightClient; use zingolib::testutils::lightclient::{from_inputs, get_fees_paid_by_client}; use zingolib::testutils::{ assert_transaction_summary_equality, assert_transaction_summary_exists, build_fvk_client, diff --git a/pepper-sync/src/sync.rs b/pepper-sync/src/sync.rs index 6e144f1c2..747da9838 100644 --- a/pepper-sync/src/sync.rs +++ b/pepper-sync/src/sync.rs @@ -167,7 +167,6 @@ where ); scanner.launch(); - // TODO: invalidate any pending transactions after eviction height (40 below best chain height?) // TODO: implement an option for continuous scanning where it doesnt exit when complete let mut wallet_guard = wallet.lock().await; From f003174cea61bf835a994cb75bdcad500b3d1075 Mon Sep 17 00:00:00 2001 From: Oscar Pepper Date: Wed, 26 Feb 2025 01:55:54 +0000 Subject: [PATCH 25/26] fix wallet test error --- libtonode-tests/tests/wallet.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/libtonode-tests/tests/wallet.rs b/libtonode-tests/tests/wallet.rs index 502f4e3d4..37aaca1c6 100644 --- a/libtonode-tests/tests/wallet.rs +++ b/libtonode-tests/tests/wallet.rs @@ -1,10 +1,8 @@ #![forbid(unsafe_code)] mod load_wallet { - use zcash_client_backend::{PoolType, ShieldedProtocol}; use zingolib::{ - config::RegtestNetwork, - testutils::{paths::get_cargo_manifest_dir, scenarios}, - wallet::disk::testing::examples, + get_base_address_macro, + testutils::{lightclient::from_inputs, scenarios}, }; // FIXME: sync integration semi-complete, need to transribe all the old do_list_transactions printouts to new types From adbc0dbc7e3ea44b65569f9c658393b450c9788e Mon Sep 17 00:00:00 2001 From: Oscar Pepper Date: Wed, 26 Feb 2025 04:02:30 +0000 Subject: [PATCH 26/26] cargo fmt --- zingolib/src/lightclient.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/zingolib/src/lightclient.rs b/zingolib/src/lightclient.rs index 612ffbce8..730d04e29 100644 --- a/zingolib/src/lightclient.rs +++ b/zingolib/src/lightclient.rs @@ -669,4 +669,3 @@ mod tests { }); } } -