From 7ba36c7bd2f2588fb96f3ddd1079177c5387e873 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Thu, 3 Aug 2023 17:28:09 +0000 Subject: [PATCH 1/5] zcash_client_sqlite: Move testing helper functions into module --- zcash_client_sqlite/src/chain.rs | 48 +-- zcash_client_sqlite/src/lib.rs | 323 ++---------------- zcash_client_sqlite/src/testing.rs | 304 +++++++++++++++++ zcash_client_sqlite/src/wallet.rs | 8 +- .../src/wallet/commitment_tree.rs | 6 +- zcash_client_sqlite/src/wallet/init.rs | 41 +-- .../init/migrations/add_transaction_views.rs | 16 +- .../migrations/received_notes_nullable_nf.rs | 8 +- .../init/migrations/v_transactions_net.rs | 12 +- zcash_client_sqlite/src/wallet/sapling.rs | 84 ++--- zcash_client_sqlite/src/wallet/scanning.rs | 8 +- 11 files changed, 442 insertions(+), 416 deletions(-) create mode 100644 zcash_client_sqlite/src/testing.rs diff --git a/zcash_client_sqlite/src/chain.rs b/zcash_client_sqlite/src/chain.rs index f95f6b63a..e23321eb7 100644 --- a/zcash_client_sqlite/src/chain.rs +++ b/zcash_client_sqlite/src/chain.rs @@ -348,7 +348,7 @@ mod tests { use crate::{ chain::init::init_cache_database, - tests::{ + testing::{ self, fake_compact_block, fake_compact_block_spending, init_test_accounts_table, insert_into_cache, sapling_activation_height, AddressType, }, @@ -363,7 +363,7 @@ mod tests { init_cache_database(&db_cache).unwrap(); let data_file = NamedTempFile::new().unwrap(); - let mut db_data = WalletDb::for_path(data_file.path(), tests::network()).unwrap(); + let mut db_data = WalletDb::for_path(data_file.path(), testing::network()).unwrap(); init_wallet_db(&mut db_data, Some(Secret::new(vec![]))).unwrap(); // Add an account to the wallet @@ -386,7 +386,7 @@ mod tests { // Scan the cache scan_cached_blocks( - &tests::network(), + &testing::network(), &db_cache, &mut db_data, sapling_activation_height(), @@ -409,7 +409,7 @@ mod tests { // Scanning should detect no inconsistencies assert_matches!( scan_cached_blocks( - &tests::network(), + &testing::network(), &db_cache, &mut db_data, sapling_activation_height() + 1, @@ -426,7 +426,7 @@ mod tests { init_cache_database(&db_cache).unwrap(); let data_file = NamedTempFile::new().unwrap(); - let mut db_data = WalletDb::for_path(data_file.path(), tests::network()).unwrap(); + let mut db_data = WalletDb::for_path(data_file.path(), testing::network()).unwrap(); init_wallet_db(&mut db_data, Some(Secret::new(vec![]))).unwrap(); // Add an account to the wallet @@ -455,7 +455,7 @@ mod tests { // Scanning the cache should find no inconsistencies assert_matches!( scan_cached_blocks( - &tests::network(), + &testing::network(), &db_cache, &mut db_data, sapling_activation_height(), @@ -487,7 +487,7 @@ mod tests { // Data+cache chain should be invalid at the data/cache boundary assert_matches!( scan_cached_blocks( - &tests::network(), + &testing::network(), &db_cache, &mut db_data, sapling_activation_height() + 2, @@ -505,7 +505,7 @@ mod tests { init_cache_database(&db_cache).unwrap(); let data_file = NamedTempFile::new().unwrap(); - let mut db_data = WalletDb::for_path(data_file.path(), tests::network()).unwrap(); + let mut db_data = WalletDb::for_path(data_file.path(), testing::network()).unwrap(); init_wallet_db(&mut db_data, Some(Secret::new(vec![]))).unwrap(); // Add an account to the wallet @@ -542,7 +542,7 @@ mod tests { // Scan the cache scan_cached_blocks( - &tests::network(), + &testing::network(), &db_cache, &mut db_data, sapling_activation_height(), @@ -584,7 +584,7 @@ mod tests { // Scan the cache again scan_cached_blocks( - &tests::network(), + &testing::network(), &db_cache, &mut db_data, sapling_activation_height(), @@ -606,7 +606,7 @@ mod tests { init_cache_database(&db_cache).unwrap(); let data_file = NamedTempFile::new().unwrap(); - let mut db_data = WalletDb::for_path(data_file.path(), tests::network()).unwrap(); + let mut db_data = WalletDb::for_path(data_file.path(), testing::network()).unwrap(); init_wallet_db(&mut db_data, Some(Secret::new(vec![]))).unwrap(); // Add an account to the wallet @@ -626,7 +626,7 @@ mod tests { ); insert_into_cache(&db_cache, &cb1); scan_cached_blocks( - &tests::network(), + &testing::network(), &db_cache, &mut db_data, sapling_activation_height(), @@ -660,7 +660,7 @@ mod tests { insert_into_cache(&db_cache, &cb3); assert_matches!( scan_cached_blocks( - &tests::network(), + &testing::network(), &db_cache, &mut db_data, sapling_activation_height() + 2, @@ -672,7 +672,7 @@ mod tests { // If we add a block of height SAPLING_ACTIVATION_HEIGHT + 1, we can now scan that insert_into_cache(&db_cache, &cb2); scan_cached_blocks( - &tests::network(), + &testing::network(), &db_cache, &mut db_data, sapling_activation_height() + 1, @@ -701,7 +701,7 @@ mod tests { assert_matches!( spend( &mut db_data, - &tests::network(), + &testing::network(), crate::wallet::sapling::tests::test_prover(), &input_selector, &usk, @@ -720,7 +720,7 @@ mod tests { init_cache_database(&db_cache).unwrap(); let data_file = NamedTempFile::new().unwrap(); - let mut db_data = WalletDb::for_path(data_file.path(), tests::network()).unwrap(); + let mut db_data = WalletDb::for_path(data_file.path(), testing::network()).unwrap(); init_wallet_db(&mut db_data, Some(Secret::new(vec![]))).unwrap(); // Add an account to the wallet @@ -746,7 +746,7 @@ mod tests { // Scan the cache scan_cached_blocks( - &tests::network(), + &testing::network(), &db_cache, &mut db_data, sapling_activation_height(), @@ -774,7 +774,7 @@ mod tests { // Scan the cache again scan_cached_blocks( - &tests::network(), + &testing::network(), &db_cache, &mut db_data, sapling_activation_height() + 1, @@ -796,7 +796,7 @@ mod tests { init_cache_database(&db_cache).unwrap(); let data_file = NamedTempFile::new().unwrap(); - let mut db_data = WalletDb::for_path(data_file.path(), tests::network()).unwrap(); + let mut db_data = WalletDb::for_path(data_file.path(), testing::network()).unwrap(); init_wallet_db(&mut db_data, Some(Secret::new(vec![]))).unwrap(); // Add an account to the wallet @@ -822,7 +822,7 @@ mod tests { // Scan the cache scan_cached_blocks( - &tests::network(), + &testing::network(), &db_cache, &mut db_data, sapling_activation_height(), @@ -855,7 +855,7 @@ mod tests { // Scan the cache again scan_cached_blocks( - &tests::network(), + &testing::network(), &db_cache, &mut db_data, sapling_activation_height() + 1, @@ -877,7 +877,7 @@ mod tests { init_cache_database(&db_cache).unwrap(); let data_file = NamedTempFile::new().unwrap(); - let mut db_data = WalletDb::for_path(data_file.path(), tests::network()).unwrap(); + let mut db_data = WalletDb::for_path(data_file.path(), testing::network()).unwrap(); init_wallet_db(&mut db_data, Some(Secret::new(vec![]))).unwrap(); // Add an account to the wallet @@ -920,7 +920,7 @@ mod tests { // Scan the spending block first. scan_cached_blocks( - &tests::network(), + &testing::network(), &db_cache, &mut db_data, sapling_activation_height() + 1, @@ -936,7 +936,7 @@ mod tests { // Now scan the block in which we received the note that was spent. scan_cached_blocks( - &tests::network(), + &testing::network(), &db_cache, &mut db_data, sapling_activation_height(), diff --git a/zcash_client_sqlite/src/lib.rs b/zcash_client_sqlite/src/lib.rs index 4935e2c5c..df4f40785 100644 --- a/zcash_client_sqlite/src/lib.rs +++ b/zcash_client_sqlite/src/lib.rs @@ -90,6 +90,9 @@ pub mod serialization; pub mod wallet; use wallet::commitment_tree::{self, put_shard_roots}; +#[cfg(test)] +mod testing; + /// The maximum number of blocks the wallet is allowed to rewind. This is /// consistent with the bound in zcashd, and allows block data deeper than /// this delta from the chain tip to be pruned. @@ -1082,317 +1085,33 @@ extern crate assert_matches; #[cfg(test)] mod tests { - use prost::Message; - use rand_core::{OsRng, RngCore}; - use rusqlite::params; - use std::collections::HashMap; - - #[cfg(feature = "unstable")] - use std::{fs::File, path::Path}; - - #[cfg(feature = "transparent-inputs")] - use zcash_primitives::{legacy, legacy::keys::IncomingViewingKey}; - - use zcash_note_encryption::Domain; - use zcash_primitives::{ - block::BlockHash, - consensus::{BlockHeight, Network, NetworkUpgrade, Parameters}, - legacy::TransparentAddress, - memo::MemoBytes, - sapling::{ - note_encryption::{sapling_note_encryption, SaplingDomain}, - util::generate_random_rseed, - value::NoteValue, - Note, Nullifier, PaymentAddress, - }, - transaction::components::Amount, - zip32::{sapling::DiversifiableFullViewingKey, DiversifierIndex}, - }; - - use zcash_client_backend::{ - data_api::{WalletRead, WalletWrite}, - keys::{sapling, UnifiedFullViewingKey}, - proto::compact_formats::{ - self as compact, CompactBlock, CompactSaplingOutput, CompactSaplingSpend, CompactTx, - }, - }; + use zcash_client_backend::data_api::{WalletRead, WalletWrite}; use crate::{ - wallet::init::{init_accounts_table, init_wallet_db}, + testing::{init_test_accounts_table_ufvk, network}, + wallet::init::init_wallet_db, AccountId, WalletDb, }; - use super::BlockDb; + #[cfg(feature = "unstable")] + use zcash_primitives::{ + block::BlockHash, + consensus::{BlockHeight, Parameters}, + transaction::components::Amount, + }; #[cfg(feature = "unstable")] - use super::{ - chain::{init::init_blockmeta_db, BlockMeta}, + use zcash_client_backend::keys::sapling; + + #[cfg(feature = "unstable")] + use crate::{ + chain::init::init_blockmeta_db, + testing::{fake_compact_block, store_in_fsblockdb, AddressType}, FsBlockDb, }; - #[cfg(feature = "mainnet")] - pub(crate) fn network() -> Network { - Network::MainNetwork - } - - #[cfg(not(feature = "mainnet"))] - pub(crate) fn network() -> Network { - Network::TestNetwork - } - - #[cfg(feature = "mainnet")] - pub(crate) fn sapling_activation_height() -> BlockHeight { - Network::MainNetwork - .activation_height(NetworkUpgrade::Sapling) - .unwrap() - } - - #[cfg(not(feature = "mainnet"))] - pub(crate) fn sapling_activation_height() -> BlockHeight { - Network::TestNetwork - .activation_height(NetworkUpgrade::Sapling) - .unwrap() - } - - #[cfg(test)] - pub(crate) fn init_test_accounts_table( - db_data: &mut WalletDb, - ) -> (DiversifiableFullViewingKey, Option) { - let (ufvk, taddr) = init_test_accounts_table_ufvk(db_data); - (ufvk.sapling().unwrap().clone(), taddr) - } - - #[cfg(test)] - pub(crate) fn init_test_accounts_table_ufvk( - db_data: &mut WalletDb, - ) -> (UnifiedFullViewingKey, Option) { - let seed = [0u8; 32]; - let account = AccountId::from(0); - let extsk = sapling::spending_key(&seed, network().coin_type(), account); - let dfvk = extsk.to_diversifiable_full_viewing_key(); - - #[cfg(feature = "transparent-inputs")] - let (tkey, taddr) = { - let tkey = legacy::keys::AccountPrivKey::from_seed(&network(), &seed, account) - .unwrap() - .to_account_pubkey(); - let taddr = tkey.derive_external_ivk().unwrap().default_address().0; - (Some(tkey), Some(taddr)) - }; - - #[cfg(not(feature = "transparent-inputs"))] - let taddr = None; - - let ufvk = UnifiedFullViewingKey::new( - #[cfg(feature = "transparent-inputs")] - tkey, - Some(dfvk), - None, - ) - .unwrap(); - - let ufvks = HashMap::from([(account, ufvk.clone())]); - init_accounts_table(db_data, &ufvks).unwrap(); - - (ufvk, taddr) - } - - #[allow(dead_code)] - pub(crate) enum AddressType { - DefaultExternal, - DiversifiedExternal(DiversifierIndex), - Internal, - } - - /// Create a fake CompactBlock at the given height, containing a single output paying - /// an address. Returns the CompactBlock and the nullifier for the new note. - pub(crate) fn fake_compact_block( - height: BlockHeight, - prev_hash: BlockHash, - dfvk: &DiversifiableFullViewingKey, - req: AddressType, - value: Amount, - initial_sapling_tree_size: u32, - ) -> (CompactBlock, Nullifier) { - let to = match req { - AddressType::DefaultExternal => dfvk.default_address().1, - AddressType::DiversifiedExternal(idx) => dfvk.find_address(idx).unwrap().1, - AddressType::Internal => dfvk.change_address().1, - }; - - // Create a fake Note for the account - let mut rng = OsRng; - let rseed = generate_random_rseed(&network(), height, &mut rng); - let note = Note::from_parts(to, NoteValue::from_raw(value.into()), rseed); - let encryptor = sapling_note_encryption::<_, Network>( - Some(dfvk.fvk().ovk), - note.clone(), - MemoBytes::empty(), - &mut rng, - ); - let cmu = note.cmu().to_bytes().to_vec(); - let ephemeral_key = SaplingDomain::::epk_bytes(encryptor.epk()) - .0 - .to_vec(); - let enc_ciphertext = encryptor.encrypt_note_plaintext(); - - // Create a fake CompactBlock containing the note - let cout = CompactSaplingOutput { - cmu, - ephemeral_key, - ciphertext: enc_ciphertext.as_ref()[..52].to_vec(), - }; - let mut ctx = CompactTx::default(); - let mut txid = vec![0; 32]; - rng.fill_bytes(&mut txid); - ctx.hash = txid; - ctx.outputs.push(cout); - let mut cb = CompactBlock { - hash: { - let mut hash = vec![0; 32]; - rng.fill_bytes(&mut hash); - hash - }, - height: height.into(), - ..Default::default() - }; - cb.prev_hash.extend_from_slice(&prev_hash.0); - cb.vtx.push(ctx); - cb.chain_metadata = Some(compact::ChainMetadata { - sapling_commitment_tree_size: initial_sapling_tree_size - + cb.vtx.iter().map(|tx| tx.outputs.len() as u32).sum::(), - ..Default::default() - }); - (cb, note.nf(&dfvk.fvk().vk.nk, 0)) - } - - /// Create a fake CompactBlock at the given height, spending a single note from the - /// given address. - pub(crate) fn fake_compact_block_spending( - height: BlockHeight, - prev_hash: BlockHash, - (nf, in_value): (Nullifier, Amount), - dfvk: &DiversifiableFullViewingKey, - to: PaymentAddress, - value: Amount, - initial_sapling_tree_size: u32, - ) -> CompactBlock { - let mut rng = OsRng; - let rseed = generate_random_rseed(&network(), height, &mut rng); - - // Create a fake CompactBlock containing the note - let cspend = CompactSaplingSpend { nf: nf.to_vec() }; - let mut ctx = CompactTx::default(); - let mut txid = vec![0; 32]; - rng.fill_bytes(&mut txid); - ctx.hash = txid; - ctx.spends.push(cspend); - - // Create a fake Note for the payment - ctx.outputs.push({ - let note = Note::from_parts(to, NoteValue::from_raw(value.into()), rseed); - let encryptor = sapling_note_encryption::<_, Network>( - Some(dfvk.fvk().ovk), - note.clone(), - MemoBytes::empty(), - &mut rng, - ); - let cmu = note.cmu().to_bytes().to_vec(); - let ephemeral_key = SaplingDomain::::epk_bytes(encryptor.epk()) - .0 - .to_vec(); - let enc_ciphertext = encryptor.encrypt_note_plaintext(); - - CompactSaplingOutput { - cmu, - ephemeral_key, - ciphertext: enc_ciphertext.as_ref()[..52].to_vec(), - } - }); - - // Create a fake Note for the change - ctx.outputs.push({ - let change_addr = dfvk.default_address().1; - let rseed = generate_random_rseed(&network(), height, &mut rng); - let note = Note::from_parts( - change_addr, - NoteValue::from_raw((in_value - value).unwrap().into()), - rseed, - ); - let encryptor = sapling_note_encryption::<_, Network>( - Some(dfvk.fvk().ovk), - note.clone(), - MemoBytes::empty(), - &mut rng, - ); - let cmu = note.cmu().to_bytes().to_vec(); - let ephemeral_key = SaplingDomain::::epk_bytes(encryptor.epk()) - .0 - .to_vec(); - let enc_ciphertext = encryptor.encrypt_note_plaintext(); - - CompactSaplingOutput { - cmu, - ephemeral_key, - ciphertext: enc_ciphertext.as_ref()[..52].to_vec(), - } - }); - - let mut cb = CompactBlock { - hash: { - let mut hash = vec![0; 32]; - rng.fill_bytes(&mut hash); - hash - }, - height: height.into(), - ..Default::default() - }; - cb.prev_hash.extend_from_slice(&prev_hash.0); - cb.vtx.push(ctx); - cb.chain_metadata = Some(compact::ChainMetadata { - sapling_commitment_tree_size: initial_sapling_tree_size - + cb.vtx.iter().map(|tx| tx.outputs.len() as u32).sum::(), - ..Default::default() - }); - cb - } - - /// Insert a fake CompactBlock into the cache DB. - pub(crate) fn insert_into_cache(db_cache: &BlockDb, cb: &CompactBlock) { - let cb_bytes = cb.encode_to_vec(); - db_cache - .0 - .prepare("INSERT INTO compactblocks (height, data) VALUES (?, ?)") - .unwrap() - .execute(params![u32::from(cb.height()), cb_bytes,]) - .unwrap(); - } - #[cfg(feature = "unstable")] - pub(crate) fn store_in_fsblockdb>( - fsblockdb_root: P, - cb: &CompactBlock, - ) -> BlockMeta { - use std::io::Write; - - let meta = BlockMeta { - height: cb.height(), - block_hash: cb.hash(), - block_time: cb.time, - sapling_outputs_count: cb.vtx.iter().map(|tx| tx.outputs.len() as u32).sum(), - orchard_actions_count: cb.vtx.iter().map(|tx| tx.actions.len() as u32).sum(), - }; - - let blocks_dir = fsblockdb_root.as_ref().join("blocks"); - let block_path = meta.block_file_path(&blocks_dir); - - File::create(block_path) - .unwrap() - .write_all(&cb.encode_to_vec()) - .unwrap(); - - meta - } + use super::BlockDb; #[test] pub(crate) fn get_next_available_address() { @@ -1422,7 +1141,9 @@ mod tests { use secrecy::Secret; use tempfile::NamedTempFile; - use crate::{chain::init::init_cache_database, wallet::init::init_wallet_db}; + use crate::{ + chain::init::init_cache_database, testing::network, wallet::init::init_wallet_db, + }; let cache_file = NamedTempFile::new().unwrap(); let db_cache = BlockDb::for_path(cache_file.path()).unwrap(); diff --git a/zcash_client_sqlite/src/testing.rs b/zcash_client_sqlite/src/testing.rs new file mode 100644 index 000000000..1dc76db02 --- /dev/null +++ b/zcash_client_sqlite/src/testing.rs @@ -0,0 +1,304 @@ +use std::collections::HashMap; + +#[cfg(feature = "unstable")] +use std::{fs::File, path::Path}; + +use prost::Message; +use rand_core::{OsRng, RngCore}; +use rusqlite::params; + +use zcash_client_backend::{ + keys::{sapling, UnifiedFullViewingKey}, + proto::compact_formats::{ + self as compact, CompactBlock, CompactSaplingOutput, CompactSaplingSpend, CompactTx, + }, +}; +use zcash_note_encryption::Domain; +use zcash_primitives::{ + block::BlockHash, + consensus::{BlockHeight, Network, NetworkUpgrade, Parameters}, + legacy::TransparentAddress, + memo::MemoBytes, + sapling::{ + note_encryption::{sapling_note_encryption, SaplingDomain}, + util::generate_random_rseed, + value::NoteValue, + Note, Nullifier, PaymentAddress, + }, + transaction::components::Amount, + zip32::{sapling::DiversifiableFullViewingKey, DiversifierIndex}, +}; + +#[cfg(feature = "transparent-inputs")] +use zcash_primitives::{legacy, legacy::keys::IncomingViewingKey}; + +use crate::{wallet::init::init_accounts_table, AccountId, WalletDb}; + +use super::BlockDb; + +#[cfg(feature = "unstable")] +use super::chain::BlockMeta; + +#[cfg(feature = "mainnet")] +pub(crate) fn network() -> Network { + Network::MainNetwork +} + +#[cfg(not(feature = "mainnet"))] +pub(crate) fn network() -> Network { + Network::TestNetwork +} + +#[cfg(feature = "mainnet")] +pub(crate) fn sapling_activation_height() -> BlockHeight { + Network::MainNetwork + .activation_height(NetworkUpgrade::Sapling) + .unwrap() +} + +#[cfg(not(feature = "mainnet"))] +pub(crate) fn sapling_activation_height() -> BlockHeight { + Network::TestNetwork + .activation_height(NetworkUpgrade::Sapling) + .unwrap() +} + +#[cfg(test)] +pub(crate) fn init_test_accounts_table( + db_data: &mut WalletDb, +) -> (DiversifiableFullViewingKey, Option) { + let (ufvk, taddr) = init_test_accounts_table_ufvk(db_data); + (ufvk.sapling().unwrap().clone(), taddr) +} + +#[cfg(test)] +pub(crate) fn init_test_accounts_table_ufvk( + db_data: &mut WalletDb, +) -> (UnifiedFullViewingKey, Option) { + let seed = [0u8; 32]; + let account = AccountId::from(0); + let extsk = sapling::spending_key(&seed, network().coin_type(), account); + let dfvk = extsk.to_diversifiable_full_viewing_key(); + + #[cfg(feature = "transparent-inputs")] + let (tkey, taddr) = { + let tkey = legacy::keys::AccountPrivKey::from_seed(&network(), &seed, account) + .unwrap() + .to_account_pubkey(); + let taddr = tkey.derive_external_ivk().unwrap().default_address().0; + (Some(tkey), Some(taddr)) + }; + + #[cfg(not(feature = "transparent-inputs"))] + let taddr = None; + + let ufvk = UnifiedFullViewingKey::new( + #[cfg(feature = "transparent-inputs")] + tkey, + Some(dfvk), + None, + ) + .unwrap(); + + let ufvks = HashMap::from([(account, ufvk.clone())]); + init_accounts_table(db_data, &ufvks).unwrap(); + + (ufvk, taddr) +} + +#[allow(dead_code)] +pub(crate) enum AddressType { + DefaultExternal, + DiversifiedExternal(DiversifierIndex), + Internal, +} + +/// Create a fake CompactBlock at the given height, containing a single output paying +/// an address. Returns the CompactBlock and the nullifier for the new note. +pub(crate) fn fake_compact_block( + height: BlockHeight, + prev_hash: BlockHash, + dfvk: &DiversifiableFullViewingKey, + req: AddressType, + value: Amount, + initial_sapling_tree_size: u32, +) -> (CompactBlock, Nullifier) { + let to = match req { + AddressType::DefaultExternal => dfvk.default_address().1, + AddressType::DiversifiedExternal(idx) => dfvk.find_address(idx).unwrap().1, + AddressType::Internal => dfvk.change_address().1, + }; + + // Create a fake Note for the account + let mut rng = OsRng; + let rseed = generate_random_rseed(&network(), height, &mut rng); + let note = Note::from_parts(to, NoteValue::from_raw(value.into()), rseed); + let encryptor = sapling_note_encryption::<_, Network>( + Some(dfvk.fvk().ovk), + note.clone(), + MemoBytes::empty(), + &mut rng, + ); + let cmu = note.cmu().to_bytes().to_vec(); + let ephemeral_key = SaplingDomain::::epk_bytes(encryptor.epk()) + .0 + .to_vec(); + let enc_ciphertext = encryptor.encrypt_note_plaintext(); + + // Create a fake CompactBlock containing the note + let cout = CompactSaplingOutput { + cmu, + ephemeral_key, + ciphertext: enc_ciphertext.as_ref()[..52].to_vec(), + }; + let mut ctx = CompactTx::default(); + let mut txid = vec![0; 32]; + rng.fill_bytes(&mut txid); + ctx.hash = txid; + ctx.outputs.push(cout); + let mut cb = CompactBlock { + hash: { + let mut hash = vec![0; 32]; + rng.fill_bytes(&mut hash); + hash + }, + height: height.into(), + ..Default::default() + }; + cb.prev_hash.extend_from_slice(&prev_hash.0); + cb.vtx.push(ctx); + cb.chain_metadata = Some(compact::ChainMetadata { + sapling_commitment_tree_size: initial_sapling_tree_size + + cb.vtx.iter().map(|tx| tx.outputs.len() as u32).sum::(), + ..Default::default() + }); + (cb, note.nf(&dfvk.fvk().vk.nk, 0)) +} + +/// Create a fake CompactBlock at the given height, spending a single note from the +/// given address. +pub(crate) fn fake_compact_block_spending( + height: BlockHeight, + prev_hash: BlockHash, + (nf, in_value): (Nullifier, Amount), + dfvk: &DiversifiableFullViewingKey, + to: PaymentAddress, + value: Amount, + initial_sapling_tree_size: u32, +) -> CompactBlock { + let mut rng = OsRng; + let rseed = generate_random_rseed(&network(), height, &mut rng); + + // Create a fake CompactBlock containing the note + let cspend = CompactSaplingSpend { nf: nf.to_vec() }; + let mut ctx = CompactTx::default(); + let mut txid = vec![0; 32]; + rng.fill_bytes(&mut txid); + ctx.hash = txid; + ctx.spends.push(cspend); + + // Create a fake Note for the payment + ctx.outputs.push({ + let note = Note::from_parts(to, NoteValue::from_raw(value.into()), rseed); + let encryptor = sapling_note_encryption::<_, Network>( + Some(dfvk.fvk().ovk), + note.clone(), + MemoBytes::empty(), + &mut rng, + ); + let cmu = note.cmu().to_bytes().to_vec(); + let ephemeral_key = SaplingDomain::::epk_bytes(encryptor.epk()) + .0 + .to_vec(); + let enc_ciphertext = encryptor.encrypt_note_plaintext(); + + CompactSaplingOutput { + cmu, + ephemeral_key, + ciphertext: enc_ciphertext.as_ref()[..52].to_vec(), + } + }); + + // Create a fake Note for the change + ctx.outputs.push({ + let change_addr = dfvk.default_address().1; + let rseed = generate_random_rseed(&network(), height, &mut rng); + let note = Note::from_parts( + change_addr, + NoteValue::from_raw((in_value - value).unwrap().into()), + rseed, + ); + let encryptor = sapling_note_encryption::<_, Network>( + Some(dfvk.fvk().ovk), + note.clone(), + MemoBytes::empty(), + &mut rng, + ); + let cmu = note.cmu().to_bytes().to_vec(); + let ephemeral_key = SaplingDomain::::epk_bytes(encryptor.epk()) + .0 + .to_vec(); + let enc_ciphertext = encryptor.encrypt_note_plaintext(); + + CompactSaplingOutput { + cmu, + ephemeral_key, + ciphertext: enc_ciphertext.as_ref()[..52].to_vec(), + } + }); + + let mut cb = CompactBlock { + hash: { + let mut hash = vec![0; 32]; + rng.fill_bytes(&mut hash); + hash + }, + height: height.into(), + ..Default::default() + }; + cb.prev_hash.extend_from_slice(&prev_hash.0); + cb.vtx.push(ctx); + cb.chain_metadata = Some(compact::ChainMetadata { + sapling_commitment_tree_size: initial_sapling_tree_size + + cb.vtx.iter().map(|tx| tx.outputs.len() as u32).sum::(), + ..Default::default() + }); + cb +} + +/// Insert a fake CompactBlock into the cache DB. +pub(crate) fn insert_into_cache(db_cache: &BlockDb, cb: &CompactBlock) { + let cb_bytes = cb.encode_to_vec(); + db_cache + .0 + .prepare("INSERT INTO compactblocks (height, data) VALUES (?, ?)") + .unwrap() + .execute(params![u32::from(cb.height()), cb_bytes,]) + .unwrap(); +} + +#[cfg(feature = "unstable")] +pub(crate) fn store_in_fsblockdb>( + fsblockdb_root: P, + cb: &CompactBlock, +) -> BlockMeta { + use std::io::Write; + + let meta = BlockMeta { + height: cb.height(), + block_hash: cb.hash(), + block_time: cb.time, + sapling_outputs_count: cb.vtx.iter().map(|tx| tx.outputs.len() as u32).sum(), + orchard_actions_count: cb.vtx.iter().map(|tx| tx.actions.len() as u32).sum(), + }; + + let blocks_dir = fsblockdb_root.as_ref().join("blocks"); + let block_path = meta.block_file_path(&blocks_dir); + + File::create(block_path) + .unwrap() + .write_all(&cb.encode_to_vec()) + .unwrap(); + + meta +} diff --git a/zcash_client_sqlite/src/wallet.rs b/zcash_client_sqlite/src/wallet.rs index 9de950997..8fb7ebe70 100644 --- a/zcash_client_sqlite/src/wallet.rs +++ b/zcash_client_sqlite/src/wallet.rs @@ -1579,7 +1579,7 @@ mod tests { use zcash_client_backend::data_api::WalletRead; use crate::{ - tests, + testing, wallet::{get_current_address, init::init_wallet_db}, AccountId, WalletDb, }; @@ -1600,11 +1600,11 @@ mod tests { #[test] fn empty_database_has_no_balance() { let data_file = NamedTempFile::new().unwrap(); - let mut db_data = WalletDb::for_path(data_file.path(), tests::network()).unwrap(); + let mut db_data = WalletDb::for_path(data_file.path(), testing::network()).unwrap(); init_wallet_db(&mut db_data, Some(Secret::new(vec![]))).unwrap(); // Add an account to the wallet - tests::init_test_accounts_table(&mut db_data); + testing::init_test_accounts_table(&mut db_data); // The account should be empty assert_eq!( @@ -1635,7 +1635,7 @@ mod tests { #[cfg(feature = "transparent-inputs")] fn put_received_transparent_utxo() { let data_file = NamedTempFile::new().unwrap(); - let mut db_data = WalletDb::for_path(data_file.path(), tests::network()).unwrap(); + let mut db_data = WalletDb::for_path(data_file.path(), testing::network()).unwrap(); init_wallet_db(&mut db_data, None).unwrap(); // Add an account to the wallet diff --git a/zcash_client_sqlite/src/wallet/commitment_tree.rs b/zcash_client_sqlite/src/wallet/commitment_tree.rs index bce22ca7b..324208071 100644 --- a/zcash_client_sqlite/src/wallet/commitment_tree.rs +++ b/zcash_client_sqlite/src/wallet/commitment_tree.rs @@ -988,11 +988,11 @@ mod tests { use zcash_primitives::consensus::BlockHeight; use super::SqliteShardStore; - use crate::{tests, wallet::init::init_wallet_db, WalletDb, SAPLING_TABLES_PREFIX}; + use crate::{testing, wallet::init::init_wallet_db, WalletDb, SAPLING_TABLES_PREFIX}; fn new_tree(m: usize) -> ShardTree, 4, 3> { let data_file = NamedTempFile::new().unwrap(); - let mut db_data = WalletDb::for_path(data_file.path(), tests::network()).unwrap(); + let mut db_data = WalletDb::for_path(data_file.path(), testing::network()).unwrap(); data_file.keep().unwrap(); init_wallet_db(&mut db_data, None).unwrap(); @@ -1040,7 +1040,7 @@ mod tests { #[test] fn put_shard_roots() { let data_file = NamedTempFile::new().unwrap(); - let mut db_data = WalletDb::for_path(data_file.path(), tests::network()).unwrap(); + let mut db_data = WalletDb::for_path(data_file.path(), testing::network()).unwrap(); data_file.keep().unwrap(); init_wallet_db(&mut db_data, None).unwrap(); diff --git a/zcash_client_sqlite/src/wallet/init.rs b/zcash_client_sqlite/src/wallet/init.rs index 1a7f24cc9..40913504c 100644 --- a/zcash_client_sqlite/src/wallet/init.rs +++ b/zcash_client_sqlite/src/wallet/init.rs @@ -393,7 +393,7 @@ mod tests { use crate::{ error::SqliteClientError, - tests::{self, network}, + testing::{self, network}, wallet::scanning::priority_code, AccountId, WalletDb, }; @@ -415,7 +415,7 @@ mod tests { #[test] fn verify_schema() { let data_file = NamedTempFile::new().unwrap(); - let mut db_data = WalletDb::for_path(data_file.path(), tests::network()).unwrap(); + let mut db_data = WalletDb::for_path(data_file.path(), testing::network()).unwrap(); init_wallet_db(&mut db_data, None).unwrap(); use regex::Regex; @@ -609,7 +609,7 @@ mod tests { AND (scan_queue.block_range_end - 1) >= shard.subtree_end_height ) WHERE scan_queue.priority > {}", - u32::from(tests::network().activation_height(NetworkUpgrade::Sapling).unwrap()), + u32::from(testing::network().activation_height(NetworkUpgrade::Sapling).unwrap()), priority_code(&ScanPriority::Scanned), ), // v_transactions @@ -852,11 +852,11 @@ mod tests { )?; let address = encode_payment_address( - tests::network().hrp_sapling_payment_address(), + testing::network().hrp_sapling_payment_address(), &extfvk.default_address().1, ); let extfvk = encode_extended_full_viewing_key( - tests::network().hrp_sapling_extended_full_viewing_key(), + testing::network().hrp_sapling_extended_full_viewing_key(), extfvk, ); wdb.conn.execute( @@ -874,10 +874,10 @@ mod tests { let seed = [0xab; 32]; let account = AccountId::from(0); - let secret_key = sapling::spending_key(&seed, tests::network().coin_type(), account); + let secret_key = sapling::spending_key(&seed, testing::network().coin_type(), account); let extfvk = secret_key.to_extended_full_viewing_key(); let data_file = NamedTempFile::new().unwrap(); - let mut db_data = WalletDb::for_path(data_file.path(), tests::network()).unwrap(); + let mut db_data = WalletDb::for_path(data_file.path(), testing::network()).unwrap(); init_0_3_0(&mut db_data, &extfvk, account).unwrap(); init_wallet_db(&mut db_data, Some(Secret::new(seed.to_vec()))).unwrap(); } @@ -984,11 +984,11 @@ mod tests { )?; let address = encode_payment_address( - tests::network().hrp_sapling_payment_address(), + testing::network().hrp_sapling_payment_address(), &extfvk.default_address().1, ); let extfvk = encode_extended_full_viewing_key( - tests::network().hrp_sapling_extended_full_viewing_key(), + testing::network().hrp_sapling_extended_full_viewing_key(), extfvk, ); wdb.conn.execute( @@ -1040,10 +1040,10 @@ mod tests { let seed = [0xab; 32]; let account = AccountId::from(0); - let secret_key = sapling::spending_key(&seed, tests::network().coin_type(), account); + let secret_key = sapling::spending_key(&seed, testing::network().coin_type(), account); let extfvk = secret_key.to_extended_full_viewing_key(); let data_file = NamedTempFile::new().unwrap(); - let mut db_data = WalletDb::for_path(data_file.path(), tests::network()).unwrap(); + let mut db_data = WalletDb::for_path(data_file.path(), testing::network()).unwrap(); init_autoshielding(&mut db_data, &extfvk, account).unwrap(); init_wallet_db(&mut db_data, Some(Secret::new(seed.to_vec()))).unwrap(); } @@ -1150,9 +1150,9 @@ mod tests { [], )?; - let ufvk_str = ufvk.encode(&tests::network()); + let ufvk_str = ufvk.encode(&testing::network()); let address_str = - RecipientAddress::Unified(ufvk.default_address().0).encode(&tests::network()); + RecipientAddress::Unified(ufvk.default_address().0).encode(&testing::network()); wdb.conn.execute( "INSERT INTO accounts (account, ufvk, address, transparent_address) VALUES (?, ?, ?, '')", @@ -1168,7 +1168,7 @@ mod tests { { let taddr = RecipientAddress::Transparent(*ufvk.default_address().0.transparent().unwrap()) - .encode(&tests::network()); + .encode(&testing::network()); wdb.conn.execute( "INSERT INTO blocks (height, hash, time, sapling_tree) VALUES (0, 0, 0, x'000000')", [], @@ -1188,9 +1188,10 @@ mod tests { let seed = [0xab; 32]; let account = AccountId::from(0); - let secret_key = UnifiedSpendingKey::from_seed(&tests::network(), &seed, account).unwrap(); + let secret_key = + UnifiedSpendingKey::from_seed(&testing::network(), &seed, account).unwrap(); let data_file = NamedTempFile::new().unwrap(); - let mut db_data = WalletDb::for_path(data_file.path(), tests::network()).unwrap(); + let mut db_data = WalletDb::for_path(data_file.path(), testing::network()).unwrap(); init_main( &mut db_data, &secret_key.to_unified_full_viewing_key(), @@ -1203,7 +1204,7 @@ mod tests { #[test] fn init_accounts_table_only_works_once() { let data_file = NamedTempFile::new().unwrap(); - let mut db_data = WalletDb::for_path(data_file.path(), tests::network()).unwrap(); + let mut db_data = WalletDb::for_path(data_file.path(), testing::network()).unwrap(); init_wallet_db(&mut db_data, Some(Secret::new(vec![]))).unwrap(); // We can call the function as many times as we want with no data @@ -1272,7 +1273,7 @@ mod tests { #[test] fn init_blocks_table_only_works_once() { let data_file = NamedTempFile::new().unwrap(); - let mut db_data = WalletDb::for_path(data_file.path(), tests::network()).unwrap(); + let mut db_data = WalletDb::for_path(data_file.path(), testing::network()).unwrap(); init_wallet_db(&mut db_data, Some(Secret::new(vec![]))).unwrap(); // First call with data should initialise the blocks table @@ -1299,14 +1300,14 @@ mod tests { #[test] fn init_accounts_table_stores_correct_address() { let data_file = NamedTempFile::new().unwrap(); - let mut db_data = WalletDb::for_path(data_file.path(), tests::network()).unwrap(); + let mut db_data = WalletDb::for_path(data_file.path(), testing::network()).unwrap(); init_wallet_db(&mut db_data, None).unwrap(); let seed = [0u8; 32]; // Add an account to the wallet let account_id = AccountId::from(0); - let usk = UnifiedSpendingKey::from_seed(&tests::network(), &seed, account_id).unwrap(); + let usk = UnifiedSpendingKey::from_seed(&testing::network(), &seed, account_id).unwrap(); let ufvk = usk.to_unified_full_viewing_key(); let expected_address = ufvk.sapling().unwrap().default_address().1; let ufvks = HashMap::from([(account_id, ufvk)]); diff --git a/zcash_client_sqlite/src/wallet/init/migrations/add_transaction_views.rs b/zcash_client_sqlite/src/wallet/init/migrations/add_transaction_views.rs index 06efb1327..0d217c545 100644 --- a/zcash_client_sqlite/src/wallet/init/migrations/add_transaction_views.rs +++ b/zcash_client_sqlite/src/wallet/init/migrations/add_transaction_views.rs @@ -286,7 +286,7 @@ mod tests { use zcash_primitives::zip32::AccountId; use crate::{ - tests, + testing, wallet::init::{init_wallet_db_internal, migrations::addresses_table}, WalletDb, }; @@ -311,10 +311,10 @@ mod tests { #[test] fn transaction_views() { let data_file = NamedTempFile::new().unwrap(); - let mut db_data = WalletDb::for_path(data_file.path(), tests::network()).unwrap(); + let mut db_data = WalletDb::for_path(data_file.path(), testing::network()).unwrap(); init_wallet_db_internal(&mut db_data, None, &[addresses_table::MIGRATION_ID]).unwrap(); let usk = - UnifiedSpendingKey::from_seed(&tests::network(), &[0u8; 32][..], AccountId::from(0)) + UnifiedSpendingKey::from_seed(&testing::network(), &[0u8; 32][..], AccountId::from(0)) .unwrap(); let ufvk = usk.to_unified_full_viewing_key(); @@ -322,7 +322,7 @@ mod tests { .conn .execute( "INSERT INTO accounts (account, ufvk) VALUES (0, ?)", - params![ufvk.encode(&tests::network())], + params![ufvk.encode(&testing::network())], ) .unwrap(); @@ -403,7 +403,7 @@ mod tests { #[cfg(feature = "transparent-inputs")] fn migrate_from_wm2() { let data_file = NamedTempFile::new().unwrap(); - let mut db_data = WalletDb::for_path(data_file.path(), tests::network()).unwrap(); + let mut db_data = WalletDb::for_path(data_file.path(), testing::network()).unwrap(); init_wallet_db_internal( &mut db_data, None, @@ -440,7 +440,7 @@ mod tests { tx.write(&mut tx_bytes).unwrap(); let usk = - UnifiedSpendingKey::from_seed(&tests::network(), &[0u8; 32][..], AccountId::from(0)) + UnifiedSpendingKey::from_seed(&testing::network(), &[0u8; 32][..], AccountId::from(0)) .unwrap(); let ufvk = usk.to_unified_full_viewing_key(); let (ua, _) = ufvk.default_address(); @@ -451,11 +451,11 @@ mod tests { .ok() .map(|k| k.derive_address(0).unwrap()) }) - .map(|a| a.encode(&tests::network())); + .map(|a| a.encode(&testing::network())); db_data.conn.execute( "INSERT INTO accounts (account, ufvk, address, transparent_address) VALUES (0, ?, ?, ?)", - params![ufvk.encode(&tests::network()), ua.encode(&tests::network()), &taddr] + params![ufvk.encode(&testing::network()), ua.encode(&testing::network()), &taddr] ).unwrap(); db_data .conn diff --git a/zcash_client_sqlite/src/wallet/init/migrations/received_notes_nullable_nf.rs b/zcash_client_sqlite/src/wallet/init/migrations/received_notes_nullable_nf.rs index 5567d60dc..2c946f3aa 100644 --- a/zcash_client_sqlite/src/wallet/init/migrations/received_notes_nullable_nf.rs +++ b/zcash_client_sqlite/src/wallet/init/migrations/received_notes_nullable_nf.rs @@ -236,7 +236,7 @@ mod tests { use zcash_primitives::zip32::AccountId; use crate::{ - tests, + testing, wallet::init::{init_wallet_db_internal, migrations::v_transactions_net}, WalletDb, }; @@ -244,19 +244,19 @@ mod tests { #[test] fn received_notes_nullable_migration() { let data_file = NamedTempFile::new().unwrap(); - let mut db_data = WalletDb::for_path(data_file.path(), tests::network()).unwrap(); + let mut db_data = WalletDb::for_path(data_file.path(), testing::network()).unwrap(); init_wallet_db_internal(&mut db_data, None, &[v_transactions_net::MIGRATION_ID]).unwrap(); // Create an account in the wallet let usk0 = - UnifiedSpendingKey::from_seed(&tests::network(), &[0u8; 32][..], AccountId::from(0)) + UnifiedSpendingKey::from_seed(&testing::network(), &[0u8; 32][..], AccountId::from(0)) .unwrap(); let ufvk0 = usk0.to_unified_full_viewing_key(); db_data .conn .execute( "INSERT INTO accounts (account, ufvk) VALUES (0, ?)", - params![ufvk0.encode(&tests::network())], + params![ufvk0.encode(&testing::network())], ) .unwrap(); diff --git a/zcash_client_sqlite/src/wallet/init/migrations/v_transactions_net.rs b/zcash_client_sqlite/src/wallet/init/migrations/v_transactions_net.rs index 7f82cea4a..b6bfe4a19 100644 --- a/zcash_client_sqlite/src/wallet/init/migrations/v_transactions_net.rs +++ b/zcash_client_sqlite/src/wallet/init/migrations/v_transactions_net.rs @@ -215,7 +215,7 @@ mod tests { use zcash_primitives::zip32::AccountId; use crate::{ - tests, + testing, wallet::init::{init_wallet_db_internal, migrations::add_transaction_views}, WalletDb, }; @@ -223,32 +223,32 @@ mod tests { #[test] fn v_transactions_net() { let data_file = NamedTempFile::new().unwrap(); - let mut db_data = WalletDb::for_path(data_file.path(), tests::network()).unwrap(); + let mut db_data = WalletDb::for_path(data_file.path(), testing::network()).unwrap(); init_wallet_db_internal(&mut db_data, None, &[add_transaction_views::MIGRATION_ID]) .unwrap(); // Create two accounts in the wallet. let usk0 = - UnifiedSpendingKey::from_seed(&tests::network(), &[0u8; 32][..], AccountId::from(0)) + UnifiedSpendingKey::from_seed(&testing::network(), &[0u8; 32][..], AccountId::from(0)) .unwrap(); let ufvk0 = usk0.to_unified_full_viewing_key(); db_data .conn .execute( "INSERT INTO accounts (account, ufvk) VALUES (0, ?)", - params![ufvk0.encode(&tests::network())], + params![ufvk0.encode(&testing::network())], ) .unwrap(); let usk1 = - UnifiedSpendingKey::from_seed(&tests::network(), &[1u8; 32][..], AccountId::from(1)) + UnifiedSpendingKey::from_seed(&testing::network(), &[1u8; 32][..], AccountId::from(1)) .unwrap(); let ufvk1 = usk1.to_unified_full_viewing_key(); db_data .conn .execute( "INSERT INTO accounts (account, ufvk) VALUES (1, ?)", - params![ufvk1.encode(&tests::network())], + params![ufvk1.encode(&testing::network())], ) .unwrap(); diff --git a/zcash_client_sqlite/src/wallet/sapling.rs b/zcash_client_sqlite/src/wallet/sapling.rs index 9e1ed2e02..0bfc60905 100644 --- a/zcash_client_sqlite/src/wallet/sapling.rs +++ b/zcash_client_sqlite/src/wallet/sapling.rs @@ -448,7 +448,7 @@ pub(crate) mod tests { use crate::{ chain::init::init_cache_database, error::SqliteClientError, - tests::{ + testing::{ self, fake_compact_block, insert_into_cache, network, sapling_activation_height, AddressType, }, @@ -480,7 +480,7 @@ pub(crate) mod tests { init_cache_database(&db_cache).unwrap(); let data_file = NamedTempFile::new().unwrap(); - let mut db_data = WalletDb::for_path(data_file.path(), tests::network()).unwrap(); + let mut db_data = WalletDb::for_path(data_file.path(), testing::network()).unwrap(); init_wallet_db(&mut db_data, None).unwrap(); // Add an account to the wallet @@ -500,7 +500,7 @@ pub(crate) mod tests { ); insert_into_cache(&db_cache, &cb); scan_cached_blocks( - &tests::network(), + &testing::network(), &db_cache, &mut db_data, sapling_activation_height(), @@ -540,7 +540,7 @@ pub(crate) mod tests { &GreedyInputSelector::new(change_strategy, DustOutputPolicy::default()); let proposal_result = propose_transfer::<_, _, _, Infallible>( &mut db_data, - &tests::network(), + &testing::network(), account, input_selector, request, @@ -551,7 +551,7 @@ pub(crate) mod tests { let change_memo = "Test change memo".parse::().unwrap(); let create_proposed_result = create_proposed_transaction::<_, _, Infallible, _>( &mut db_data, - &tests::network(), + &testing::network(), test_prover(), &usk, OvkPolicy::Sender, @@ -571,7 +571,7 @@ pub(crate) mod tests { .into_iter() .collect(); let decrypted_outputs = decrypt_transaction( - &tests::network(), + &testing::network(), sapling_activation_height() + 1, &tx, &ufvks, @@ -650,7 +650,7 @@ pub(crate) mod tests { #[test] fn create_to_address_fails_on_incorrect_usk() { let data_file = NamedTempFile::new().unwrap(); - let mut db_data = WalletDb::for_path(data_file.path(), tests::network()).unwrap(); + let mut db_data = WalletDb::for_path(data_file.path(), testing::network()).unwrap(); init_wallet_db(&mut db_data, Some(Secret::new(vec![]))).unwrap(); // Add an account to the wallet @@ -667,7 +667,7 @@ pub(crate) mod tests { assert_matches!( create_spend_to_address( &mut db_data, - &tests::network(), + &testing::network(), test_prover(), &usk1, &to, @@ -683,7 +683,7 @@ pub(crate) mod tests { #[test] fn create_to_address_fails_with_no_blocks() { let data_file = NamedTempFile::new().unwrap(); - let mut db_data = WalletDb::for_path(data_file.path(), tests::network()).unwrap(); + let mut db_data = WalletDb::for_path(data_file.path(), testing::network()).unwrap(); init_wallet_db(&mut db_data, None).unwrap(); // Add an account to the wallet @@ -702,7 +702,7 @@ pub(crate) mod tests { assert_matches!( create_spend_to_address( &mut db_data, - &tests::network(), + &testing::network(), test_prover(), &usk, &to, @@ -722,7 +722,7 @@ pub(crate) mod tests { init_cache_database(&db_cache).unwrap(); let data_file = NamedTempFile::new().unwrap(); - let mut db_data = WalletDb::for_path(data_file.path(), tests::network()).unwrap(); + let mut db_data = WalletDb::for_path(data_file.path(), testing::network()).unwrap(); init_wallet_db(&mut db_data, None).unwrap(); // Add an account to the wallet @@ -742,7 +742,7 @@ pub(crate) mod tests { ); insert_into_cache(&db_cache, &cb); scan_cached_blocks( - &tests::network(), + &testing::network(), &db_cache, &mut db_data, sapling_activation_height(), @@ -776,7 +776,7 @@ pub(crate) mod tests { .0; insert_into_cache(&db_cache, &cb); scan_cached_blocks( - &tests::network(), + &testing::network(), &db_cache, &mut db_data, sapling_activation_height() + 1, @@ -804,7 +804,7 @@ pub(crate) mod tests { assert_matches!( create_spend_to_address( &mut db_data, - &tests::network(), + &testing::network(), test_prover(), &usk, &to, @@ -836,7 +836,7 @@ pub(crate) mod tests { insert_into_cache(&db_cache, &cb); } scan_cached_blocks( - &tests::network(), + &testing::network(), &db_cache, &mut db_data, sapling_activation_height() + 2, @@ -848,7 +848,7 @@ pub(crate) mod tests { assert_matches!( create_spend_to_address( &mut db_data, - &tests::network(), + &testing::network(), test_prover(), &usk, &to, @@ -877,7 +877,7 @@ pub(crate) mod tests { .0; insert_into_cache(&db_cache, &cb); scan_cached_blocks( - &tests::network(), + &testing::network(), &db_cache, &mut db_data, sapling_activation_height() + 10, @@ -889,7 +889,7 @@ pub(crate) mod tests { assert_matches!( create_spend_to_address( &mut db_data, - &tests::network(), + &testing::network(), test_prover(), &usk, &to, @@ -909,7 +909,7 @@ pub(crate) mod tests { init_cache_database(&db_cache).unwrap(); let data_file = NamedTempFile::new().unwrap(); - let mut db_data = WalletDb::for_path(data_file.path(), tests::network()).unwrap(); + let mut db_data = WalletDb::for_path(data_file.path(), testing::network()).unwrap(); init_wallet_db(&mut db_data, Some(Secret::new(vec![]))).unwrap(); // Add an account to the wallet @@ -929,7 +929,7 @@ pub(crate) mod tests { ); insert_into_cache(&db_cache, &cb); scan_cached_blocks( - &tests::network(), + &testing::network(), &db_cache, &mut db_data, sapling_activation_height(), @@ -947,7 +947,7 @@ pub(crate) mod tests { assert_matches!( create_spend_to_address( &mut db_data, - &tests::network(), + &testing::network(), test_prover(), &usk, &to, @@ -963,7 +963,7 @@ pub(crate) mod tests { assert_matches!( create_spend_to_address( &mut db_data, - &tests::network(), + &testing::network(), test_prover(), &usk, &to, @@ -994,7 +994,7 @@ pub(crate) mod tests { insert_into_cache(&db_cache, &cb); } scan_cached_blocks( - &tests::network(), + &testing::network(), &db_cache, &mut db_data, sapling_activation_height() + 1, @@ -1006,7 +1006,7 @@ pub(crate) mod tests { assert_matches!( create_spend_to_address( &mut db_data, - &tests::network(), + &testing::network(), test_prover(), &usk, &to, @@ -1034,7 +1034,7 @@ pub(crate) mod tests { .0; insert_into_cache(&db_cache, &cb); scan_cached_blocks( - &tests::network(), + &testing::network(), &db_cache, &mut db_data, sapling_activation_height() + 42, @@ -1045,7 +1045,7 @@ pub(crate) mod tests { // Second spend should now succeed create_spend_to_address( &mut db_data, - &tests::network(), + &testing::network(), test_prover(), &usk, &to, @@ -1059,7 +1059,7 @@ pub(crate) mod tests { #[test] fn ovk_policy_prevents_recovery_from_chain() { - let network = tests::network(); + let network = testing::network(); let cache_file = NamedTempFile::new().unwrap(); let db_cache = BlockDb(Connection::open(cache_file.path()).unwrap()); init_cache_database(&db_cache).unwrap(); @@ -1085,7 +1085,7 @@ pub(crate) mod tests { ); insert_into_cache(&db_cache, &cb); scan_cached_blocks( - &tests::network(), + &testing::network(), &db_cache, &mut db_data, sapling_activation_height(), @@ -1116,7 +1116,7 @@ pub(crate) mod tests { > { let txid = create_spend_to_address( db_data, - &tests::network(), + &testing::network(), test_prover(), &usk, &to, @@ -1200,7 +1200,7 @@ pub(crate) mod tests { init_cache_database(&db_cache).unwrap(); let data_file = NamedTempFile::new().unwrap(); - let mut db_data = WalletDb::for_path(data_file.path(), tests::network()).unwrap(); + let mut db_data = WalletDb::for_path(data_file.path(), testing::network()).unwrap(); init_wallet_db(&mut db_data, None).unwrap(); // Add an account to the wallet @@ -1220,7 +1220,7 @@ pub(crate) mod tests { ); insert_into_cache(&db_cache, &cb); scan_cached_blocks( - &tests::network(), + &testing::network(), &db_cache, &mut db_data, sapling_activation_height(), @@ -1246,7 +1246,7 @@ pub(crate) mod tests { assert_matches!( create_spend_to_address( &mut db_data, - &tests::network(), + &testing::network(), test_prover(), &usk, &to, @@ -1266,7 +1266,7 @@ pub(crate) mod tests { init_cache_database(&db_cache).unwrap(); let data_file = NamedTempFile::new().unwrap(); - let mut db_data = WalletDb::for_path(data_file.path(), tests::network()).unwrap(); + let mut db_data = WalletDb::for_path(data_file.path(), testing::network()).unwrap(); init_wallet_db(&mut db_data, None).unwrap(); // Add an account to the wallet @@ -1286,7 +1286,7 @@ pub(crate) mod tests { ); insert_into_cache(&db_cache, &cb); scan_cached_blocks( - &tests::network(), + &testing::network(), &db_cache, &mut db_data, sapling_activation_height(), @@ -1312,7 +1312,7 @@ pub(crate) mod tests { assert_matches!( create_spend_to_address( &mut db_data, - &tests::network(), + &testing::network(), test_prover(), &usk, &to, @@ -1332,7 +1332,7 @@ pub(crate) mod tests { init_cache_database(&db_cache).unwrap(); let data_file = NamedTempFile::new().unwrap(); - let mut db_data = WalletDb::for_path(data_file.path(), tests::network()).unwrap(); + let mut db_data = WalletDb::for_path(data_file.path(), testing::network()).unwrap(); init_wallet_db(&mut db_data, None).unwrap(); // Add an account to the wallet @@ -1366,7 +1366,7 @@ pub(crate) mod tests { } scan_cached_blocks( - &tests::network(), + &testing::network(), &db_cache, &mut db_data, sapling_activation_height(), @@ -1408,7 +1408,7 @@ pub(crate) mod tests { assert_matches!( spend( &mut db_data, - &tests::network(), + &testing::network(), test_prover(), &input_selector, &usk, @@ -1436,7 +1436,7 @@ pub(crate) mod tests { assert_matches!( spend( &mut db_data, - &tests::network(), + &testing::network(), test_prover(), &input_selector, &usk, @@ -1456,7 +1456,7 @@ pub(crate) mod tests { init_cache_database(&db_cache).unwrap(); let data_file = NamedTempFile::new().unwrap(); - let mut db_data = WalletDb::for_path(data_file.path(), tests::network()).unwrap(); + let mut db_data = WalletDb::for_path(data_file.path(), testing::network()).unwrap(); init_wallet_db(&mut db_data, None).unwrap(); // Add an account to the wallet @@ -1495,7 +1495,7 @@ pub(crate) mod tests { ); insert_into_cache(&db_cache, &cb); scan_cached_blocks( - &tests::network(), + &testing::network(), &db_cache, &mut db_data, sapling_activation_height(), @@ -1506,7 +1506,7 @@ pub(crate) mod tests { assert_matches!( shield_transparent_funds( &mut db_data, - &tests::network(), + &testing::network(), test_prover(), &input_selector, NonNegativeAmount::from_u64(10000).unwrap(), diff --git a/zcash_client_sqlite/src/wallet/scanning.rs b/zcash_client_sqlite/src/wallet/scanning.rs index 719d6d082..27863ff9e 100644 --- a/zcash_client_sqlite/src/wallet/scanning.rs +++ b/zcash_client_sqlite/src/wallet/scanning.rs @@ -755,7 +755,7 @@ mod tests { use crate::{ chain::init::init_cache_database, - tests::{ + testing::{ self, fake_compact_block, init_test_accounts_table, insert_into_cache, sapling_activation_height, AddressType, }, @@ -1098,7 +1098,7 @@ mod tests { init_cache_database(&db_cache).unwrap(); let data_file = NamedTempFile::new().unwrap(); - let mut db_data = WalletDb::for_path(data_file.path(), tests::network()).unwrap(); + let mut db_data = WalletDb::for_path(data_file.path(), testing::network()).unwrap(); init_wallet_db(&mut db_data, Some(Secret::new(vec![]))).unwrap(); // Add an account to the wallet. @@ -1159,7 +1159,7 @@ mod tests { assert_matches!( scan_cached_blocks( - &tests::network(), + &testing::network(), &db_cache, &mut db_data, initial_height, @@ -1226,7 +1226,7 @@ mod tests { use ScanPriority::*; let data_file = NamedTempFile::new().unwrap(); - let mut db_data = WalletDb::for_path(data_file.path(), tests::network()).unwrap(); + let mut db_data = WalletDb::for_path(data_file.path(), testing::network()).unwrap(); init_wallet_db(&mut db_data, Some(Secret::new(vec![]))).unwrap(); let sap_active = db_data From ce3e4cd34a5b6fbabaa87739de2950d168bd9dcc Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Mon, 28 Aug 2023 16:28:48 +0000 Subject: [PATCH 2/5] zcash_client_sqlite: Introduce `TestBuilder` and `TestRunner` These provide a standard way to set up tests, and helper methods for performing common test actions. --- zcash_client_sqlite/src/testing.rs | 598 +++++++++++++++++++++++++++-- 1 file changed, 558 insertions(+), 40 deletions(-) diff --git a/zcash_client_sqlite/src/testing.rs b/zcash_client_sqlite/src/testing.rs index 1dc76db02..5c0cbee8f 100644 --- a/zcash_client_sqlite/src/testing.rs +++ b/zcash_client_sqlite/src/testing.rs @@ -1,17 +1,38 @@ -use std::collections::HashMap; +use std::convert::Infallible; +use std::fmt; +use std::{collections::HashMap, num::NonZeroU32}; #[cfg(feature = "unstable")] -use std::{fs::File, path::Path}; +use std::fs::File; use prost::Message; use rand_core::{OsRng, RngCore}; -use rusqlite::params; +use rusqlite::{params, Connection}; +use secrecy::SecretVec; +use tempfile::NamedTempFile; +#[cfg(feature = "unstable")] +use tempfile::TempDir; + +#[allow(deprecated)] +use zcash_client_backend::data_api::wallet::create_spend_to_address; use zcash_client_backend::{ - keys::{sapling, UnifiedFullViewingKey}, + address::RecipientAddress, + data_api::{ + self, + chain::{scan_cached_blocks, BlockSource}, + wallet::{ + create_proposed_transaction, + input_selection::{GreedyInputSelectorError, InputSelector, Proposal}, + propose_transfer, spend, + }, + }, + keys::{sapling, UnifiedFullViewingKey, UnifiedSpendingKey}, proto::compact_formats::{ self as compact, CompactBlock, CompactSaplingOutput, CompactSaplingSpend, CompactTx, }, + wallet::OvkPolicy, + zip321, }; use zcash_note_encryption::Domain; use zcash_primitives::{ @@ -25,19 +46,452 @@ use zcash_primitives::{ value::NoteValue, Note, Nullifier, PaymentAddress, }, - transaction::components::Amount, + transaction::{ + components::{amount::BalanceError, Amount}, + fees::FeeRule, + TxId, + }, zip32::{sapling::DiversifiableFullViewingKey, DiversifierIndex}, }; #[cfg(feature = "transparent-inputs")] -use zcash_primitives::{legacy, legacy::keys::IncomingViewingKey}; +use zcash_client_backend::data_api::wallet::shield_transparent_funds; +#[cfg(feature = "transparent-inputs")] +use zcash_primitives::{ + legacy, legacy::keys::IncomingViewingKey, transaction::components::amount::NonNegativeAmount, +}; -use crate::{wallet::init::init_accounts_table, AccountId, WalletDb}; +use crate::{ + chain::init::init_cache_database, + error::SqliteClientError, + wallet::{ + commitment_tree, + init::{init_accounts_table, init_wallet_db}, + sapling::tests::test_prover, + }, + AccountId, ReceivedNoteId, WalletDb, +}; use super::BlockDb; #[cfg(feature = "unstable")] -use super::chain::BlockMeta; +use crate::{ + chain::{init::init_blockmeta_db, BlockMeta}, + FsBlockDb, +}; +/// A builder for a `zcash_client_sqlite` test. +pub(crate) struct TestBuilder { + cache: Cache, + seed: Option>, + with_test_account: bool, +} + +impl TestBuilder<()> { + /// Constructs a new test. + pub(crate) fn new() -> Self { + TestBuilder { + cache: (), + seed: None, + with_test_account: false, + } + } + + /// Adds a [`BlockDb`] cache to the test. + pub(crate) fn with_block_cache(self) -> TestBuilder { + TestBuilder { + cache: BlockCache::new(), + seed: self.seed, + with_test_account: self.with_test_account, + } + } + + /// Adds a [`FsBlockDb`] cache to the test. + #[cfg(feature = "unstable")] + pub(crate) fn with_fs_block_cache(self) -> TestBuilder { + TestBuilder { + cache: FsBlockCache::new(), + seed: self.seed, + with_test_account: self.with_test_account, + } + } +} + +impl TestBuilder { + /// Gives the test knowledge of the wallet seed for initialization. + pub(crate) fn with_seed(mut self, seed: SecretVec) -> Self { + // TODO remove + self.seed = Some(seed); + self + } + + pub(crate) fn with_test_account(mut self) -> Self { + self.with_test_account = true; + self + } + + /// Builds the runner for this test. + pub(crate) fn build(self) -> TestRunner { + let params = network(); + + let data_file = NamedTempFile::new().unwrap(); + let mut db_data = WalletDb::for_path(data_file.path(), params).unwrap(); + init_wallet_db(&mut db_data, self.seed).unwrap(); + + let test_account = if self.with_test_account { + // Add an account to the wallet + Some(init_test_accounts_table_ufvk(&mut db_data)) + } else { + None + }; + + TestRunner { + params, + cache: self.cache, + latest_cached_block: None, + _data_file: data_file, + db_data, + test_account, + } + } +} + +/// A `zcash_client_sqlite` test runner. +pub(crate) struct TestRunner { + params: Network, + cache: Cache, + latest_cached_block: Option<(BlockHeight, BlockHash, u32)>, + _data_file: NamedTempFile, + db_data: WalletDb, + test_account: Option<(UnifiedFullViewingKey, Option)>, +} + +impl TestRunner +where + ::Error: fmt::Debug, +{ + /// Exposes an immutable reference to the test's [`BlockSource`]. + #[cfg(feature = "unstable")] + pub(crate) fn cache(&self) -> &Cache::BlockSource { + self.cache.block_source() + } + + /// Creates a fake block at the expected next height containing a single output of the + /// given value, and inserts it into the cache. + pub(crate) fn generate_next_block( + &mut self, + dfvk: &DiversifiableFullViewingKey, + req: AddressType, + value: Amount, + ) -> (BlockHeight, Cache::InsertResult, Nullifier) { + let (height, prev_hash, initial_sapling_tree_size) = self + .latest_cached_block + .map(|(prev_height, prev_hash, end_size)| (prev_height + 1, prev_hash, end_size)) + .unwrap_or_else(|| (sapling_activation_height(), BlockHash([0; 32]), 0)); + + let (res, nf) = self.generate_block_at( + height, + prev_hash, + dfvk, + req, + value, + initial_sapling_tree_size, + ); + + (height, res, nf) + } + + /// Creates a fake block with the given height and hash containing a single output of + /// the given value, and inserts it into the cache. + /// + /// This generated block will be treated as the latest block, and subsequent calls to + /// [`Self::generate_next_block`] will build on it. + pub(crate) fn generate_block_at( + &mut self, + height: BlockHeight, + prev_hash: BlockHash, + dfvk: &DiversifiableFullViewingKey, + req: AddressType, + value: Amount, + initial_sapling_tree_size: u32, + ) -> (Cache::InsertResult, Nullifier) { + let (cb, nf) = fake_compact_block( + height, + prev_hash, + dfvk, + req, + value, + initial_sapling_tree_size, + ); + let res = self.cache.insert(&cb); + + self.latest_cached_block = Some(( + height, + cb.hash(), + initial_sapling_tree_size + + cb.vtx.iter().map(|tx| tx.outputs.len() as u32).sum::(), + )); + + (res, nf) + } + + /// Creates a fake block at the expected next height spending the given note, and + /// inserts it into the cache. + pub(crate) fn generate_next_block_spending( + &mut self, + dfvk: &DiversifiableFullViewingKey, + note: (Nullifier, Amount), + to: PaymentAddress, + value: Amount, + ) -> (BlockHeight, Cache::InsertResult) { + let (height, prev_hash, initial_sapling_tree_size) = self + .latest_cached_block + .map(|(prev_height, prev_hash, end_size)| (prev_height + 1, prev_hash, end_size)) + .unwrap_or_else(|| (sapling_activation_height(), BlockHash([0; 32]), 0)); + + let cb = fake_compact_block_spending( + height, + prev_hash, + note, + dfvk, + to, + value, + initial_sapling_tree_size, + ); + let res = self.cache.insert(&cb); + + self.latest_cached_block = Some(( + height, + cb.hash(), + initial_sapling_tree_size + + cb.vtx.iter().map(|tx| tx.outputs.len() as u32).sum::(), + )); + + (height, res) + } + + /// Invokes [`scan_cached_blocks`] with the given arguments, expecting success. + pub(crate) fn scan_cached_blocks(&mut self, from_height: BlockHeight, limit: usize) { + self.try_scan_cached_blocks(from_height, limit) + .expect("should succeed for this test"); + } + + /// Invokes [`scan_cached_blocks`] with the given arguments. + pub(crate) fn try_scan_cached_blocks( + &mut self, + from_height: BlockHeight, + limit: usize, + ) -> Result< + (), + data_api::chain::error::Error< + SqliteClientError, + ::Error, + >, + > { + scan_cached_blocks( + &self.params, + self.cache.block_source(), + &mut self.db_data, + from_height, + limit, + ) + } +} + +impl TestRunner { + /// Exposes an immutable reference to the test's [`WalletDb`]. + pub(crate) fn wallet(&self) -> &WalletDb { + &self.db_data + } + + /// Exposes a mutable reference to the test's [`WalletDb`]. + pub(crate) fn wallet_mut(&mut self) -> &mut WalletDb { + &mut self.db_data + } + + /// Exposes the test account, if enabled via [`TestBuilder::with_test_account`]. + #[cfg(feature = "unstable")] + pub(crate) fn test_account( + &self, + ) -> Option<(UnifiedFullViewingKey, Option)> { + self.test_account.as_ref().cloned() + } + + /// Exposes the test account's Sapling DFVK, if enabled via [`TestBuilder::with_test_account`]. + pub(crate) fn test_account_sapling(&self) -> Option { + self.test_account + .as_ref() + .map(|(ufvk, _)| ufvk.sapling().unwrap().clone()) + } + + /// Invokes [`create_spend_to_address`] with the given arguments. + #[allow(deprecated)] + #[allow(clippy::type_complexity)] + pub(crate) fn create_spend_to_address( + &mut self, + usk: &UnifiedSpendingKey, + to: &RecipientAddress, + amount: Amount, + memo: Option, + ovk_policy: OvkPolicy, + min_confirmations: NonZeroU32, + ) -> Result< + TxId, + data_api::error::Error< + SqliteClientError, + commitment_tree::Error, + GreedyInputSelectorError, + Infallible, + ReceivedNoteId, + >, + > { + create_spend_to_address( + &mut self.db_data, + &self.params, + test_prover(), + usk, + to, + amount, + memo, + ovk_policy, + min_confirmations, + ) + } + + /// Invokes [`spend`] with the given arguments. + #[allow(clippy::type_complexity)] + pub(crate) fn spend( + &mut self, + input_selector: &InputsT, + usk: &UnifiedSpendingKey, + request: zip321::TransactionRequest, + ovk_policy: OvkPolicy, + min_confirmations: NonZeroU32, + ) -> Result< + TxId, + data_api::error::Error< + SqliteClientError, + commitment_tree::Error, + InputsT::Error, + ::Error, + ReceivedNoteId, + >, + > + where + InputsT: InputSelector>, + { + spend( + &mut self.db_data, + &self.params, + test_prover(), + input_selector, + usk, + request, + ovk_policy, + min_confirmations, + ) + } + + /// Invokes [`propose_transfer`] with the given arguments. + #[allow(clippy::type_complexity)] + pub(crate) fn propose_transfer( + &mut self, + spend_from_account: AccountId, + input_selector: &InputsT, + request: zip321::TransactionRequest, + min_confirmations: NonZeroU32, + ) -> Result< + Proposal, + data_api::error::Error< + SqliteClientError, + Infallible, + InputsT::Error, + ::Error, + ReceivedNoteId, + >, + > + where + InputsT: InputSelector>, + { + propose_transfer::<_, _, _, Infallible>( + &mut self.db_data, + &self.params, + spend_from_account, + input_selector, + request, + min_confirmations, + ) + } + + /// Invokes [`create_proposed_transaction`] with the given arguments. + pub(crate) fn create_proposed_transaction( + &mut self, + usk: &UnifiedSpendingKey, + ovk_policy: OvkPolicy, + proposal: Proposal, + min_confirmations: NonZeroU32, + change_memo: Option, + ) -> Result< + TxId, + data_api::error::Error< + SqliteClientError, + commitment_tree::Error, + Infallible, + FeeRuleT::Error, + ReceivedNoteId, + >, + > + where + FeeRuleT: FeeRule, + { + create_proposed_transaction::<_, _, Infallible, _>( + &mut self.db_data, + &self.params, + test_prover(), + usk, + ovk_policy, + proposal, + min_confirmations, + change_memo, + ) + } + + /// Invokes [`shield_transparent_funds`] with the given arguments. + #[cfg(feature = "transparent-inputs")] + #[allow(clippy::type_complexity)] + pub(crate) fn shield_transparent_funds( + &mut self, + input_selector: &InputsT, + shielding_threshold: NonNegativeAmount, + usk: &UnifiedSpendingKey, + from_addrs: &[TransparentAddress], + memo: &MemoBytes, + min_confirmations: NonZeroU32, + ) -> Result< + TxId, + data_api::error::Error< + SqliteClientError, + commitment_tree::Error, + InputsT::Error, + ::Error, + ReceivedNoteId, + >, + > + where + InputsT: InputSelector>, + { + shield_transparent_funds( + &mut self.db_data, + &self.params, + test_prover(), + input_selector, + shielding_threshold, + usk, + from_addrs, + memo, + min_confirmations, + ) + } +} #[cfg(feature = "mainnet")] pub(crate) fn network() -> Network { @@ -266,39 +720,103 @@ pub(crate) fn fake_compact_block_spending( cb } -/// Insert a fake CompactBlock into the cache DB. -pub(crate) fn insert_into_cache(db_cache: &BlockDb, cb: &CompactBlock) { - let cb_bytes = cb.encode_to_vec(); - db_cache - .0 - .prepare("INSERT INTO compactblocks (height, data) VALUES (?, ?)") - .unwrap() - .execute(params![u32::from(cb.height()), cb_bytes,]) - .unwrap(); +/// Trait used by tests that require a block cache. +pub(crate) trait TestCache { + type BlockSource: BlockSource; + type InsertResult; + + /// Exposes the block cache as a [`BlockSource`]. + fn block_source(&self) -> &Self::BlockSource; + + /// Inserts a CompactBlock into the cache DB. + fn insert(&self, cb: &CompactBlock) -> Self::InsertResult; +} + +pub(crate) struct BlockCache { + _cache_file: NamedTempFile, + db_cache: BlockDb, +} + +impl BlockCache { + fn new() -> Self { + let cache_file = NamedTempFile::new().unwrap(); + let db_cache = BlockDb::for_path(cache_file.path()).unwrap(); + init_cache_database(&db_cache).unwrap(); + + BlockCache { + _cache_file: cache_file, + db_cache, + } + } +} + +impl TestCache for BlockCache { + type BlockSource = BlockDb; + type InsertResult = (); + + fn block_source(&self) -> &Self::BlockSource { + &self.db_cache + } + + fn insert(&self, cb: &CompactBlock) { + let cb_bytes = cb.encode_to_vec(); + self.db_cache + .0 + .prepare("INSERT INTO compactblocks (height, data) VALUES (?, ?)") + .unwrap() + .execute(params![u32::from(cb.height()), cb_bytes,]) + .unwrap(); + } } #[cfg(feature = "unstable")] -pub(crate) fn store_in_fsblockdb>( - fsblockdb_root: P, - cb: &CompactBlock, -) -> BlockMeta { - use std::io::Write; - - let meta = BlockMeta { - height: cb.height(), - block_hash: cb.hash(), - block_time: cb.time, - sapling_outputs_count: cb.vtx.iter().map(|tx| tx.outputs.len() as u32).sum(), - orchard_actions_count: cb.vtx.iter().map(|tx| tx.actions.len() as u32).sum(), - }; - - let blocks_dir = fsblockdb_root.as_ref().join("blocks"); - let block_path = meta.block_file_path(&blocks_dir); - - File::create(block_path) - .unwrap() - .write_all(&cb.encode_to_vec()) - .unwrap(); - - meta +pub(crate) struct FsBlockCache { + fsblockdb_root: TempDir, + db_meta: FsBlockDb, +} + +#[cfg(feature = "unstable")] +impl FsBlockCache { + fn new() -> Self { + let fsblockdb_root = tempfile::tempdir().unwrap(); + let mut db_meta = FsBlockDb::for_path(&fsblockdb_root).unwrap(); + init_blockmeta_db(&mut db_meta).unwrap(); + + FsBlockCache { + fsblockdb_root, + db_meta, + } + } +} + +#[cfg(feature = "unstable")] +impl TestCache for FsBlockCache { + type BlockSource = FsBlockDb; + type InsertResult = BlockMeta; + + fn block_source(&self) -> &Self::BlockSource { + &self.db_meta + } + + fn insert(&self, cb: &CompactBlock) -> Self::InsertResult { + use std::io::Write; + + let meta = BlockMeta { + height: cb.height(), + block_hash: cb.hash(), + block_time: cb.time, + sapling_outputs_count: cb.vtx.iter().map(|tx| tx.outputs.len() as u32).sum(), + orchard_actions_count: cb.vtx.iter().map(|tx| tx.actions.len() as u32).sum(), + }; + + let blocks_dir = self.fsblockdb_root.as_ref().join("blocks"); + let block_path = meta.block_file_path(&blocks_dir); + + File::create(block_path) + .unwrap() + .write_all(&cb.encode_to_vec()) + .unwrap(); + + meta + } } From 84a5d5ca94dfb733b57d799d949b466cb27e7d6c Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Mon, 28 Aug 2023 16:31:02 +0000 Subject: [PATCH 3/5] zcash_client_sqlite: Migrate all tests to `TestBuilder` --- zcash_client_sqlite/src/chain.rs | 470 ++++------------- zcash_client_sqlite/src/lib.rs | 121 ++--- zcash_client_sqlite/src/testing.rs | 8 - zcash_client_sqlite/src/wallet.rs | 60 +-- zcash_client_sqlite/src/wallet/sapling.rs | 577 +++++---------------- zcash_client_sqlite/src/wallet/scanning.rs | 95 ++-- 6 files changed, 337 insertions(+), 994 deletions(-) diff --git a/zcash_client_sqlite/src/chain.rs b/zcash_client_sqlite/src/chain.rs index e23321eb7..35f90a80a 100644 --- a/zcash_client_sqlite/src/chain.rs +++ b/zcash_client_sqlite/src/chain.rs @@ -325,7 +325,6 @@ mod tests { use std::num::NonZeroU32; use secrecy::Secret; - use tempfile::NamedTempFile; use zcash_primitives::{ block::BlockHash, @@ -336,9 +335,8 @@ mod tests { use zcash_client_backend::{ address::RecipientAddress, data_api::{ - chain::{error::Error, scan_cached_blocks}, - wallet::{input_selection::GreedyInputSelector, spend}, - WalletRead, WalletWrite, + chain::error::Error, wallet::input_selection::GreedyInputSelector, WalletRead, + WalletWrite, }, fees::{zip317::SingleOutputChangeStrategy, DustOutputPolicy}, scanning::ScanError, @@ -347,340 +345,192 @@ mod tests { }; use crate::{ - chain::init::init_cache_database, - testing::{ - self, fake_compact_block, fake_compact_block_spending, init_test_accounts_table, - insert_into_cache, sapling_activation_height, AddressType, - }, - wallet::{get_balance, init::init_wallet_db, truncate_to_height}, - AccountId, BlockDb, WalletDb, + testing::{AddressType, TestBuilder}, + wallet::{get_balance, truncate_to_height}, + AccountId, }; #[test] fn valid_chain_states() { - let cache_file = NamedTempFile::new().unwrap(); - let db_cache = BlockDb::for_path(cache_file.path()).unwrap(); - init_cache_database(&db_cache).unwrap(); + let mut test = TestBuilder::new() + .with_block_cache() + .with_seed(Secret::new(vec![])) + .with_test_account() + .build(); - let data_file = NamedTempFile::new().unwrap(); - let mut db_data = WalletDb::for_path(data_file.path(), testing::network()).unwrap(); - init_wallet_db(&mut db_data, Some(Secret::new(vec![]))).unwrap(); - - // Add an account to the wallet - let (dfvk, _taddr) = init_test_accounts_table(&mut db_data); + let dfvk = test.test_account_sapling().unwrap(); // Empty chain should return None - assert_matches!(db_data.chain_height(), Ok(None)); + assert_matches!(test.wallet().chain_height(), Ok(None)); // Create a fake CompactBlock sending value to the address - let (cb, _) = fake_compact_block( - sapling_activation_height(), - BlockHash([0; 32]), + let (h1, _, _) = test.generate_next_block( &dfvk, AddressType::DefaultExternal, Amount::from_u64(5).unwrap(), - 0, ); - insert_into_cache(&db_cache, &cb); - // Scan the cache - scan_cached_blocks( - &testing::network(), - &db_cache, - &mut db_data, - sapling_activation_height(), - 1, - ) - .unwrap(); + test.scan_cached_blocks(h1, 1); // Create a second fake CompactBlock sending more value to the address - let (cb2, _) = fake_compact_block( - sapling_activation_height() + 1, - cb.hash(), + let (h2, _, _) = test.generate_next_block( &dfvk, AddressType::DefaultExternal, Amount::from_u64(7).unwrap(), - 1, ); - insert_into_cache(&db_cache, &cb2); - // Scanning should detect no inconsistencies - assert_matches!( - scan_cached_blocks( - &testing::network(), - &db_cache, - &mut db_data, - sapling_activation_height() + 1, - 1, - ), - Ok(()) - ); + test.scan_cached_blocks(h2, 1); } #[test] fn invalid_chain_cache_disconnected() { - let cache_file = NamedTempFile::new().unwrap(); - let db_cache = BlockDb::for_path(cache_file.path()).unwrap(); - init_cache_database(&db_cache).unwrap(); + let mut test = TestBuilder::new() + .with_block_cache() + .with_seed(Secret::new(vec![])) + .with_test_account() + .build(); - let data_file = NamedTempFile::new().unwrap(); - let mut db_data = WalletDb::for_path(data_file.path(), testing::network()).unwrap(); - init_wallet_db(&mut db_data, Some(Secret::new(vec![]))).unwrap(); - - // Add an account to the wallet - let (dfvk, _taddr) = init_test_accounts_table(&mut db_data); + let dfvk = test.test_account_sapling().unwrap(); // Create some fake CompactBlocks - let (cb, _) = fake_compact_block( - sapling_activation_height(), - BlockHash([0; 32]), + let (h, _, _) = test.generate_next_block( &dfvk, AddressType::DefaultExternal, Amount::from_u64(5).unwrap(), - 0, ); - let (cb2, _) = fake_compact_block( - sapling_activation_height() + 1, - cb.hash(), + let (last_contiguous_height, _, _) = test.generate_next_block( &dfvk, AddressType::DefaultExternal, Amount::from_u64(7).unwrap(), - 1, ); - insert_into_cache(&db_cache, &cb); - insert_into_cache(&db_cache, &cb2); // Scanning the cache should find no inconsistencies - assert_matches!( - scan_cached_blocks( - &testing::network(), - &db_cache, - &mut db_data, - sapling_activation_height(), - 2, - ), - Ok(()) - ); + test.scan_cached_blocks(h, 2); // Create more fake CompactBlocks that don't connect to the scanned ones - let (cb3, _) = fake_compact_block( - sapling_activation_height() + 2, + let disconnect_height = last_contiguous_height + 1; + test.generate_block_at( + disconnect_height, BlockHash([1; 32]), &dfvk, AddressType::DefaultExternal, Amount::from_u64(8).unwrap(), 2, ); - let (cb4, _) = fake_compact_block( - sapling_activation_height() + 3, - cb3.hash(), + test.generate_next_block( &dfvk, AddressType::DefaultExternal, Amount::from_u64(3).unwrap(), - 3, ); - insert_into_cache(&db_cache, &cb3); - insert_into_cache(&db_cache, &cb4); // Data+cache chain should be invalid at the data/cache boundary assert_matches!( - scan_cached_blocks( - &testing::network(), - &db_cache, - &mut db_data, - sapling_activation_height() + 2, + test.try_scan_cached_blocks( + disconnect_height, 2 ), Err(Error::Scan(ScanError::PrevHashMismatch { at_height })) - if at_height == sapling_activation_height() + 2 + if at_height == disconnect_height ); } #[test] fn data_db_truncation() { - let cache_file = NamedTempFile::new().unwrap(); - let db_cache = BlockDb::for_path(cache_file.path()).unwrap(); - init_cache_database(&db_cache).unwrap(); + let mut test = TestBuilder::new() + .with_block_cache() + .with_seed(Secret::new(vec![])) + .with_test_account() + .build(); - let data_file = NamedTempFile::new().unwrap(); - let mut db_data = WalletDb::for_path(data_file.path(), testing::network()).unwrap(); - init_wallet_db(&mut db_data, Some(Secret::new(vec![]))).unwrap(); - - // Add an account to the wallet - let (dfvk, _taddr) = init_test_accounts_table(&mut db_data); + let dfvk = test.test_account_sapling().unwrap(); // Account balance should be zero assert_eq!( - get_balance(&db_data.conn, AccountId::from(0)).unwrap(), + get_balance(&test.wallet().conn, AccountId::from(0)).unwrap(), Amount::zero() ); // Create fake CompactBlocks sending value to the address let value = Amount::from_u64(5).unwrap(); let value2 = Amount::from_u64(7).unwrap(); - let (cb, _) = fake_compact_block( - sapling_activation_height(), - BlockHash([0; 32]), - &dfvk, - AddressType::DefaultExternal, - value, - 0, - ); - - let (cb2, _) = fake_compact_block( - sapling_activation_height() + 1, - cb.hash(), - &dfvk, - AddressType::DefaultExternal, - value2, - 1, - ); - insert_into_cache(&db_cache, &cb); - insert_into_cache(&db_cache, &cb2); + let (h, _, _) = test.generate_next_block(&dfvk, AddressType::DefaultExternal, value); + test.generate_next_block(&dfvk, AddressType::DefaultExternal, value2); // Scan the cache - scan_cached_blocks( - &testing::network(), - &db_cache, - &mut db_data, - sapling_activation_height(), - 2, - ) - .unwrap(); + test.scan_cached_blocks(h, 2); // Account balance should reflect both received notes assert_eq!( - get_balance(&db_data.conn, AccountId::from(0)).unwrap(), + get_balance(&test.wallet().conn, AccountId::from(0)).unwrap(), (value + value2).unwrap() ); // "Rewind" to height of last scanned block - db_data - .transactionally(|wdb| { - truncate_to_height(wdb.conn.0, &wdb.params, sapling_activation_height() + 1) - }) + test.wallet_mut() + .transactionally(|wdb| truncate_to_height(wdb.conn.0, &wdb.params, h + 1)) .unwrap(); // Account balance should be unaltered assert_eq!( - get_balance(&db_data.conn, AccountId::from(0)).unwrap(), + get_balance(&test.wallet().conn, AccountId::from(0)).unwrap(), (value + value2).unwrap() ); // Rewind so that one block is dropped - db_data - .transactionally(|wdb| { - truncate_to_height(wdb.conn.0, &wdb.params, sapling_activation_height()) - }) + test.wallet_mut() + .transactionally(|wdb| truncate_to_height(wdb.conn.0, &wdb.params, h)) .unwrap(); // Account balance should only contain the first received note assert_eq!( - get_balance(&db_data.conn, AccountId::from(0)).unwrap(), + get_balance(&test.wallet().conn, AccountId::from(0)).unwrap(), value ); // Scan the cache again - scan_cached_blocks( - &testing::network(), - &db_cache, - &mut db_data, - sapling_activation_height(), - 2, - ) - .unwrap(); + test.scan_cached_blocks(h, 2); // Account balance should again reflect both received notes assert_eq!( - get_balance(&db_data.conn, AccountId::from(0)).unwrap(), + get_balance(&test.wallet().conn, AccountId::from(0)).unwrap(), (value + value2).unwrap() ); } #[test] fn scan_cached_blocks_allows_blocks_out_of_order() { - let cache_file = NamedTempFile::new().unwrap(); - let db_cache = BlockDb::for_path(cache_file.path()).unwrap(); - init_cache_database(&db_cache).unwrap(); - - let data_file = NamedTempFile::new().unwrap(); - let mut db_data = WalletDb::for_path(data_file.path(), testing::network()).unwrap(); - init_wallet_db(&mut db_data, Some(Secret::new(vec![]))).unwrap(); + let mut test = TestBuilder::new() + .with_block_cache() + .with_seed(Secret::new(vec![])) + .build(); // Add an account to the wallet let seed = Secret::new([0u8; 32].to_vec()); - let (_, usk) = db_data.create_account(&seed).unwrap(); + let (_, usk) = test.wallet_mut().create_account(&seed).unwrap(); let dfvk = usk.sapling().to_diversifiable_full_viewing_key(); // Create a block with height SAPLING_ACTIVATION_HEIGHT let value = Amount::from_u64(50000).unwrap(); - let (cb1, _) = fake_compact_block( - sapling_activation_height(), - BlockHash([0; 32]), - &dfvk, - AddressType::DefaultExternal, - value, - 0, - ); - insert_into_cache(&db_cache, &cb1); - scan_cached_blocks( - &testing::network(), - &db_cache, - &mut db_data, - sapling_activation_height(), - 1, - ) - .unwrap(); + let (h1, _, _) = test.generate_next_block(&dfvk, AddressType::DefaultExternal, value); + test.scan_cached_blocks(h1, 1); assert_eq!( - get_balance(&db_data.conn, AccountId::from(0)).unwrap(), + get_balance(&test.wallet().conn, AccountId::from(0)).unwrap(), value ); // Create blocks to reach SAPLING_ACTIVATION_HEIGHT + 2 - let (cb2, _) = fake_compact_block( - sapling_activation_height() + 1, - cb1.hash(), - &dfvk, - AddressType::DefaultExternal, - value, - 1, - ); - let (cb3, _) = fake_compact_block( - sapling_activation_height() + 2, - cb2.hash(), - &dfvk, - AddressType::DefaultExternal, - value, - 2, - ); + let (h2, _, _) = test.generate_next_block(&dfvk, AddressType::DefaultExternal, value); + let (h3, _, _) = test.generate_next_block(&dfvk, AddressType::DefaultExternal, value); // Scan the later block first - insert_into_cache(&db_cache, &cb3); - assert_matches!( - scan_cached_blocks( - &testing::network(), - &db_cache, - &mut db_data, - sapling_activation_height() + 2, - 1 - ), - Ok(_) - ); + test.scan_cached_blocks(h3, 1); - // If we add a block of height SAPLING_ACTIVATION_HEIGHT + 1, we can now scan that - insert_into_cache(&db_cache, &cb2); - scan_cached_blocks( - &testing::network(), - &db_cache, - &mut db_data, - sapling_activation_height() + 1, - 1, - ) - .unwrap(); + // Now scan the block of height SAPLING_ACTIVATION_HEIGHT + 1 + test.scan_cached_blocks(h2, 1); assert_eq!( - get_balance(&db_data.conn, AccountId::from(0)).unwrap(), + get_balance(&test.wallet().conn, AccountId::from(0)).unwrap(), Amount::from_u64(150_000).unwrap() ); @@ -699,10 +549,7 @@ mod tests { DustOutputPolicy::default(), ); assert_matches!( - spend( - &mut db_data, - &testing::network(), - crate::wallet::sapling::tests::test_prover(), + test.spend( &input_selector, &usk, req, @@ -715,124 +562,74 @@ mod tests { #[test] fn scan_cached_blocks_finds_received_notes() { - let cache_file = NamedTempFile::new().unwrap(); - let db_cache = BlockDb::for_path(cache_file.path()).unwrap(); - init_cache_database(&db_cache).unwrap(); + let mut test = TestBuilder::new() + .with_block_cache() + .with_seed(Secret::new(vec![])) + .with_test_account() + .build(); - let data_file = NamedTempFile::new().unwrap(); - let mut db_data = WalletDb::for_path(data_file.path(), testing::network()).unwrap(); - init_wallet_db(&mut db_data, Some(Secret::new(vec![]))).unwrap(); - - // Add an account to the wallet - let (dfvk, _taddr) = init_test_accounts_table(&mut db_data); + let dfvk = test.test_account_sapling().unwrap(); // Account balance should be zero assert_eq!( - get_balance(&db_data.conn, AccountId::from(0)).unwrap(), + get_balance(&test.wallet().conn, AccountId::from(0)).unwrap(), Amount::zero() ); // Create a fake CompactBlock sending value to the address let value = Amount::from_u64(5).unwrap(); - let (cb, _) = fake_compact_block( - sapling_activation_height(), - BlockHash([0; 32]), - &dfvk, - AddressType::DefaultExternal, - value, - 0, - ); - insert_into_cache(&db_cache, &cb); + let (h1, _, _) = test.generate_next_block(&dfvk, AddressType::DefaultExternal, value); // Scan the cache - scan_cached_blocks( - &testing::network(), - &db_cache, - &mut db_data, - sapling_activation_height(), - 1, - ) - .unwrap(); + test.scan_cached_blocks(h1, 1); // Account balance should reflect the received note assert_eq!( - get_balance(&db_data.conn, AccountId::from(0)).unwrap(), + get_balance(&test.wallet().conn, AccountId::from(0)).unwrap(), value ); // Create a second fake CompactBlock sending more value to the address let value2 = Amount::from_u64(7).unwrap(); - let (cb2, _) = fake_compact_block( - sapling_activation_height() + 1, - cb.hash(), - &dfvk, - AddressType::DefaultExternal, - value2, - 1, - ); - insert_into_cache(&db_cache, &cb2); + let (h2, _, _) = test.generate_next_block(&dfvk, AddressType::DefaultExternal, value2); // Scan the cache again - scan_cached_blocks( - &testing::network(), - &db_cache, - &mut db_data, - sapling_activation_height() + 1, - 1, - ) - .unwrap(); + test.scan_cached_blocks(h2, 1); // Account balance should reflect both received notes assert_eq!( - get_balance(&db_data.conn, AccountId::from(0)).unwrap(), + get_balance(&test.wallet().conn, AccountId::from(0)).unwrap(), (value + value2).unwrap() ); } #[test] fn scan_cached_blocks_finds_change_notes() { - let cache_file = NamedTempFile::new().unwrap(); - let db_cache = BlockDb::for_path(cache_file.path()).unwrap(); - init_cache_database(&db_cache).unwrap(); + let mut test = TestBuilder::new() + .with_block_cache() + .with_seed(Secret::new(vec![])) + .with_test_account() + .build(); - let data_file = NamedTempFile::new().unwrap(); - let mut db_data = WalletDb::for_path(data_file.path(), testing::network()).unwrap(); - init_wallet_db(&mut db_data, Some(Secret::new(vec![]))).unwrap(); - - // Add an account to the wallet - let (dfvk, _taddr) = init_test_accounts_table(&mut db_data); + let dfvk = test.test_account_sapling().unwrap(); // Account balance should be zero assert_eq!( - get_balance(&db_data.conn, AccountId::from(0)).unwrap(), + get_balance(&test.wallet().conn, AccountId::from(0)).unwrap(), Amount::zero() ); // Create a fake CompactBlock sending value to the address let value = Amount::from_u64(5).unwrap(); - let (cb, nf) = fake_compact_block( - sapling_activation_height(), - BlockHash([0; 32]), - &dfvk, - AddressType::DefaultExternal, - value, - 0, - ); - insert_into_cache(&db_cache, &cb); + let (received_height, _, nf) = + test.generate_next_block(&dfvk, AddressType::DefaultExternal, value); // Scan the cache - scan_cached_blocks( - &testing::network(), - &db_cache, - &mut db_data, - sapling_activation_height(), - 1, - ) - .unwrap(); + test.scan_cached_blocks(received_height, 1); // Account balance should reflect the received note assert_eq!( - get_balance(&db_data.conn, AccountId::from(0)).unwrap(), + get_balance(&test.wallet().conn, AccountId::from(0)).unwrap(), value ); @@ -840,113 +637,60 @@ mod tests { let extsk2 = ExtendedSpendingKey::master(&[0]); let to2 = extsk2.default_address().1; let value2 = Amount::from_u64(2).unwrap(); - insert_into_cache( - &db_cache, - &fake_compact_block_spending( - sapling_activation_height() + 1, - cb.hash(), - (nf, value), - &dfvk, - to2, - value2, - 1, - ), - ); + let (spent_height, _) = test.generate_next_block_spending(&dfvk, (nf, value), to2, value2); // Scan the cache again - scan_cached_blocks( - &testing::network(), - &db_cache, - &mut db_data, - sapling_activation_height() + 1, - 1, - ) - .unwrap(); + test.scan_cached_blocks(spent_height, 1); // Account balance should equal the change assert_eq!( - get_balance(&db_data.conn, AccountId::from(0)).unwrap(), + get_balance(&test.wallet().conn, AccountId::from(0)).unwrap(), (value - value2).unwrap() ); } #[test] fn scan_cached_blocks_detects_spends_out_of_order() { - let cache_file = NamedTempFile::new().unwrap(); - let db_cache = BlockDb::for_path(cache_file.path()).unwrap(); - init_cache_database(&db_cache).unwrap(); + let mut test = TestBuilder::new() + .with_block_cache() + .with_seed(Secret::new(vec![])) + .with_test_account() + .build(); - let data_file = NamedTempFile::new().unwrap(); - let mut db_data = WalletDb::for_path(data_file.path(), testing::network()).unwrap(); - init_wallet_db(&mut db_data, Some(Secret::new(vec![]))).unwrap(); - - // Add an account to the wallet - let (dfvk, _taddr) = init_test_accounts_table(&mut db_data); + let dfvk = test.test_account_sapling().unwrap(); // Account balance should be zero assert_eq!( - get_balance(&db_data.conn, AccountId::from(0)).unwrap(), + get_balance(&test.wallet().conn, AccountId::from(0)).unwrap(), Amount::zero() ); // Create a fake CompactBlock sending value to the address let value = Amount::from_u64(5).unwrap(); - let (cb, nf) = fake_compact_block( - sapling_activation_height(), - BlockHash([0; 32]), - &dfvk, - AddressType::DefaultExternal, - value, - 0, - ); - insert_into_cache(&db_cache, &cb); + let (received_height, _, nf) = + test.generate_next_block(&dfvk, AddressType::DefaultExternal, value); // Create a second fake CompactBlock spending value from the address let extsk2 = ExtendedSpendingKey::master(&[0]); let to2 = extsk2.default_address().1; let value2 = Amount::from_u64(2).unwrap(); - insert_into_cache( - &db_cache, - &fake_compact_block_spending( - sapling_activation_height() + 1, - cb.hash(), - (nf, value), - &dfvk, - to2, - value2, - 1, - ), - ); + let (spent_height, _) = test.generate_next_block_spending(&dfvk, (nf, value), to2, value2); // Scan the spending block first. - scan_cached_blocks( - &testing::network(), - &db_cache, - &mut db_data, - sapling_activation_height() + 1, - 1, - ) - .unwrap(); + test.scan_cached_blocks(spent_height, 1); // Account balance should equal the change assert_eq!( - get_balance(&db_data.conn, AccountId::from(0)).unwrap(), + get_balance(&test.wallet().conn, AccountId::from(0)).unwrap(), (value - value2).unwrap() ); // Now scan the block in which we received the note that was spent. - scan_cached_blocks( - &testing::network(), - &db_cache, - &mut db_data, - sapling_activation_height(), - 1, - ) - .unwrap(); + test.scan_cached_blocks(received_height, 1); // Account balance should be the same. assert_eq!( - get_balance(&db_data.conn, AccountId::from(0)).unwrap(), + get_balance(&test.wallet().conn, AccountId::from(0)).unwrap(), (value - value2).unwrap() ); } diff --git a/zcash_client_sqlite/src/lib.rs b/zcash_client_sqlite/src/lib.rs index df4f40785..e3ecf3eca 100644 --- a/zcash_client_sqlite/src/lib.rs +++ b/zcash_client_sqlite/src/lib.rs @@ -1087,51 +1087,33 @@ extern crate assert_matches; mod tests { use zcash_client_backend::data_api::{WalletRead, WalletWrite}; - use crate::{ - testing::{init_test_accounts_table_ufvk, network}, - wallet::init::init_wallet_db, - AccountId, WalletDb, - }; + use crate::{testing::TestBuilder, AccountId}; #[cfg(feature = "unstable")] - use zcash_primitives::{ - block::BlockHash, - consensus::{BlockHeight, Parameters}, - transaction::components::Amount, - }; + use zcash_primitives::{consensus::Parameters, transaction::components::Amount}; #[cfg(feature = "unstable")] use zcash_client_backend::keys::sapling; #[cfg(feature = "unstable")] - use crate::{ - chain::init::init_blockmeta_db, - testing::{fake_compact_block, store_in_fsblockdb, AddressType}, - FsBlockDb, - }; - - #[cfg(feature = "unstable")] - use super::BlockDb; + use crate::testing::AddressType; #[test] pub(crate) fn get_next_available_address() { - use tempfile::NamedTempFile; - - let data_file = NamedTempFile::new().unwrap(); - let mut db_data = WalletDb::for_path(data_file.path(), network()).unwrap(); + let mut test = TestBuilder::new().with_test_account().build(); let account = AccountId::from(0); - init_wallet_db(&mut db_data, None).unwrap(); - init_test_accounts_table_ufvk(&mut db_data); - - let current_addr = db_data.get_current_address(account).unwrap(); + let current_addr = test.wallet().get_current_address(account).unwrap(); assert!(current_addr.is_some()); - let addr2 = db_data.get_next_available_address(account).unwrap(); + let addr2 = test + .wallet_mut() + .get_next_available_address(account) + .unwrap(); assert!(addr2.is_some()); assert_ne!(current_addr, addr2); - let addr2_cur = db_data.get_current_address(account).unwrap(); + let addr2_cur = test.wallet().get_current_address(account).unwrap(); assert_eq!(addr2, addr2_cur); } @@ -1139,25 +1121,18 @@ mod tests { #[test] fn transparent_receivers() { use secrecy::Secret; - use tempfile::NamedTempFile; - use crate::{ - chain::init::init_cache_database, testing::network, wallet::init::init_wallet_db, - }; - - let cache_file = NamedTempFile::new().unwrap(); - let db_cache = BlockDb::for_path(cache_file.path()).unwrap(); - init_cache_database(&db_cache).unwrap(); - - let data_file = NamedTempFile::new().unwrap(); - let mut db_data = WalletDb::for_path(data_file.path(), network()).unwrap(); - init_wallet_db(&mut db_data, Some(Secret::new(vec![]))).unwrap(); + let test = TestBuilder::new() + .with_block_cache() + .with_seed(Secret::new(vec![])) + .with_test_account() + .build(); // Add an account to the wallet. - let (ufvk, taddr) = init_test_accounts_table_ufvk(&mut db_data); + let (ufvk, taddr) = test.test_account().unwrap(); let taddr = taddr.unwrap(); - let receivers = db_data.get_transparent_receivers(0.into()).unwrap(); + let receivers = test.wallet().get_transparent_receivers(0.into()).unwrap(); // The receiver for the default UA should be in the set. assert!(receivers.contains_key(ufvk.default_address().0.transparent().unwrap())); @@ -1169,74 +1144,44 @@ mod tests { #[cfg(feature = "unstable")] #[test] pub(crate) fn fsblockdb_api() { - // Initialise a BlockMeta DB in a new directory. - let fsblockdb_root = tempfile::tempdir().unwrap(); - let mut db_meta = FsBlockDb::for_path(&fsblockdb_root).unwrap(); - init_blockmeta_db(&mut db_meta).unwrap(); + let mut test = TestBuilder::new().with_fs_block_cache().build(); // The BlockMeta DB starts off empty. - assert_eq!(db_meta.get_max_cached_height().unwrap(), None); + assert_eq!(test.cache().get_max_cached_height().unwrap(), None); // Generate some fake CompactBlocks. let seed = [0u8; 32]; let account = AccountId::from(0); - let extsk = sapling::spending_key(&seed, network().coin_type(), account); + let extsk = sapling::spending_key(&seed, test.wallet().params.coin_type(), account); let dfvk = extsk.to_diversifiable_full_viewing_key(); - let (cb1, _) = fake_compact_block( - BlockHeight::from_u32(1), - BlockHash([1; 32]), + let (h1, meta1, _) = test.generate_next_block( &dfvk, AddressType::DefaultExternal, Amount::from_u64(5).unwrap(), - 0, ); - let (cb2, _) = fake_compact_block( - BlockHeight::from_u32(2), - BlockHash([2; 32]), + let (h2, meta2, _) = test.generate_next_block( &dfvk, AddressType::DefaultExternal, Amount::from_u64(10).unwrap(), - 1, ); - // Write the CompactBlocks to the BlockMeta DB's corresponding disk storage. - let meta1 = store_in_fsblockdb(&fsblockdb_root, &cb1); - let meta2 = store_in_fsblockdb(&fsblockdb_root, &cb2); - // The BlockMeta DB is not updated until we do so explicitly. - assert_eq!(db_meta.get_max_cached_height().unwrap(), None); + assert_eq!(test.cache().get_max_cached_height().unwrap(), None); // Inform the BlockMeta DB about the newly-persisted CompactBlocks. - db_meta.write_block_metadata(&[meta1, meta2]).unwrap(); + test.cache().write_block_metadata(&[meta1, meta2]).unwrap(); // The BlockMeta DB now sees blocks up to height 2. - assert_eq!( - db_meta.get_max_cached_height().unwrap(), - Some(BlockHeight::from_u32(2)), - ); - assert_eq!( - db_meta.find_block(BlockHeight::from_u32(1)).unwrap(), - Some(meta1), - ); - assert_eq!( - db_meta.find_block(BlockHeight::from_u32(2)).unwrap(), - Some(meta2), - ); - assert_eq!(db_meta.find_block(BlockHeight::from_u32(3)).unwrap(), None); + assert_eq!(test.cache().get_max_cached_height().unwrap(), Some(h2),); + assert_eq!(test.cache().find_block(h1).unwrap(), Some(meta1)); + assert_eq!(test.cache().find_block(h2).unwrap(), Some(meta2)); + assert_eq!(test.cache().find_block(h2 + 1).unwrap(), None); // Rewinding to height 1 should cause the metadata for height 2 to be deleted. - db_meta - .truncate_to_height(BlockHeight::from_u32(1)) - .unwrap(); - assert_eq!( - db_meta.get_max_cached_height().unwrap(), - Some(BlockHeight::from_u32(1)), - ); - assert_eq!( - db_meta.find_block(BlockHeight::from_u32(1)).unwrap(), - Some(meta1), - ); - assert_eq!(db_meta.find_block(BlockHeight::from_u32(2)).unwrap(), None); - assert_eq!(db_meta.find_block(BlockHeight::from_u32(3)).unwrap(), None); + test.cache().truncate_to_height(h1).unwrap(); + assert_eq!(test.cache().get_max_cached_height().unwrap(), Some(h1)); + assert_eq!(test.cache().find_block(h1).unwrap(), Some(meta1)); + assert_eq!(test.cache().find_block(h2).unwrap(), None); + assert_eq!(test.cache().find_block(h2 + 1).unwrap(), None); } } diff --git a/zcash_client_sqlite/src/testing.rs b/zcash_client_sqlite/src/testing.rs index 5c0cbee8f..db6a135fb 100644 --- a/zcash_client_sqlite/src/testing.rs +++ b/zcash_client_sqlite/src/testing.rs @@ -517,14 +517,6 @@ pub(crate) fn sapling_activation_height() -> BlockHeight { .unwrap() } -#[cfg(test)] -pub(crate) fn init_test_accounts_table( - db_data: &mut WalletDb, -) -> (DiversifiableFullViewingKey, Option) { - let (ufvk, taddr) = init_test_accounts_table_ufvk(db_data); - (ufvk.sapling().unwrap().clone(), taddr) -} - #[cfg(test)] pub(crate) fn init_test_accounts_table_ufvk( db_data: &mut WalletDb, diff --git a/zcash_client_sqlite/src/wallet.rs b/zcash_client_sqlite/src/wallet.rs index 8fb7ebe70..d2fdbc639 100644 --- a/zcash_client_sqlite/src/wallet.rs +++ b/zcash_client_sqlite/src/wallet.rs @@ -1572,17 +1572,12 @@ mod tests { use std::num::NonZeroU32; use secrecy::Secret; - use tempfile::NamedTempFile; use zcash_primitives::transaction::components::Amount; use zcash_client_backend::data_api::WalletRead; - use crate::{ - testing, - wallet::{get_current_address, init::init_wallet_db}, - AccountId, WalletDb, - }; + use crate::{testing::TestBuilder, AccountId}; use super::get_balance; @@ -1599,22 +1594,20 @@ mod tests { #[test] fn empty_database_has_no_balance() { - let data_file = NamedTempFile::new().unwrap(); - let mut db_data = WalletDb::for_path(data_file.path(), testing::network()).unwrap(); - init_wallet_db(&mut db_data, Some(Secret::new(vec![]))).unwrap(); - - // Add an account to the wallet - testing::init_test_accounts_table(&mut db_data); + let test = TestBuilder::new() + .with_seed(Secret::new(vec![])) + .with_test_account() + .build(); // The account should be empty assert_eq!( - get_balance(&db_data.conn, AccountId::from(0)).unwrap(), + get_balance(&test.wallet().conn, AccountId::from(0)).unwrap(), Amount::zero() ); // We can't get an anchor height, as we have not scanned any blocks. assert_eq!( - db_data + test.wallet() .get_target_and_anchor_heights(NonZeroU32::new(10).unwrap()) .unwrap(), None @@ -1622,11 +1615,11 @@ mod tests { // An invalid account has zero balance assert_matches!( - get_current_address(&db_data.conn, &db_data.params, AccountId::from(1)), + test.wallet().get_current_address(AccountId::from(1)), Ok(None) ); assert_eq!( - get_balance(&db_data.conn, AccountId::from(0)).unwrap(), + get_balance(&test.wallet().conn, AccountId::from(0)).unwrap(), Amount::zero() ); } @@ -1634,17 +1627,20 @@ mod tests { #[test] #[cfg(feature = "transparent-inputs")] fn put_received_transparent_utxo() { - let data_file = NamedTempFile::new().unwrap(); - let mut db_data = WalletDb::for_path(data_file.path(), testing::network()).unwrap(); - init_wallet_db(&mut db_data, None).unwrap(); + let mut test = TestBuilder::new().build(); // Add an account to the wallet let seed = Secret::new([0u8; 32].to_vec()); - let (account_id, _usk) = db_data.create_account(&seed).unwrap(); - let uaddr = db_data.get_current_address(account_id).unwrap().unwrap(); + let (account_id, _usk) = test.wallet_mut().create_account(&seed).unwrap(); + let uaddr = test + .wallet() + .get_current_address(account_id) + .unwrap() + .unwrap(); let taddr = uaddr.transparent().unwrap(); - let bal_absent = db_data + let bal_absent = test + .wallet() .get_transparent_balances(account_id, BlockHeight::from_u32(12345)) .unwrap(); assert!(bal_absent.is_empty()); @@ -1659,7 +1655,7 @@ mod tests { ) .unwrap(); - let res0 = super::put_received_transparent_utxo(&db_data.conn, &db_data.params, &utxo); + let res0 = test.wallet_mut().put_received_transparent_utxo(&utxo); assert_matches!(res0, Ok(_)); // Change the mined height of the UTXO and upsert; we should get back @@ -1673,13 +1669,11 @@ mod tests { BlockHeight::from_u32(34567), ) .unwrap(); - let res1 = super::put_received_transparent_utxo(&db_data.conn, &db_data.params, &utxo2); + let res1 = test.wallet_mut().put_received_transparent_utxo(&utxo2); assert_matches!(res1, Ok(id) if id == res0.unwrap()); assert_matches!( - super::get_unspent_transparent_outputs( - &db_data.conn, - &db_data.params, + test.wallet().get_unspent_transparent_outputs( taddr, BlockHeight::from_u32(12345), &[] @@ -1688,9 +1682,7 @@ mod tests { ); assert_matches!( - super::get_unspent_transparent_outputs( - &db_data.conn, - &db_data.params, + test.wallet().get_unspent_transparent_outputs( taddr, BlockHeight::from_u32(34567), &[] @@ -1702,21 +1694,21 @@ mod tests { ); assert_matches!( - db_data.get_transparent_balances(account_id, BlockHeight::from_u32(34567)), + test.wallet().get_transparent_balances(account_id, BlockHeight::from_u32(34567)), Ok(h) if h.get(taddr) == Amount::from_u64(100000).ok().as_ref() ); // Artificially delete the address from the addresses table so that // we can ensure the update fails if the join doesn't work. - db_data + test.wallet() .conn .execute( "DELETE FROM addresses WHERE cached_transparent_receiver_address = ?", - [Some(taddr.encode(&db_data.params))], + [Some(taddr.encode(&test.wallet().params))], ) .unwrap(); - let res2 = super::put_received_transparent_utxo(&db_data.conn, &db_data.params, &utxo2); + let res2 = test.wallet_mut().put_received_transparent_utxo(&utxo2); assert_matches!(res2, Err(_)); } } diff --git a/zcash_client_sqlite/src/wallet/sapling.rs b/zcash_client_sqlite/src/wallet/sapling.rs index 0bfc60905..8d95fcaea 100644 --- a/zcash_client_sqlite/src/wallet/sapling.rs +++ b/zcash_client_sqlite/src/wallet/sapling.rs @@ -403,14 +403,11 @@ pub(crate) fn put_received_note( pub(crate) mod tests { use std::{convert::Infallible, num::NonZeroU32}; - use rusqlite::Connection; use secrecy::Secret; - use tempfile::NamedTempFile; use zcash_proofs::prover::LocalTxProver; use zcash_primitives::{ - block::BlockHash, consensus::BranchId, legacy::TransparentAddress, memo::{Memo, MemoBytes}, @@ -429,13 +426,8 @@ pub(crate) mod tests { address::RecipientAddress, data_api::{ self, - chain::scan_cached_blocks, error::Error, - wallet::{ - create_proposed_transaction, create_spend_to_address, - input_selection::{GreedyInputSelector, GreedyInputSelectorError}, - propose_transfer, spend, - }, + wallet::input_selection::{GreedyInputSelector, GreedyInputSelectorError}, ShieldedProtocol, WalletRead, WalletWrite, }, decrypt_transaction, @@ -446,21 +438,15 @@ pub(crate) mod tests { }; use crate::{ - chain::init::init_cache_database, error::SqliteClientError, - testing::{ - self, fake_compact_block, insert_into_cache, network, sapling_activation_height, - AddressType, - }, - wallet::{commitment_tree, get_balance, get_balance_at, init::init_wallet_db}, - AccountId, BlockDb, NoteId, ReceivedNoteId, WalletDb, + testing::{self, network, AddressType, BlockCache, TestBuilder, TestRunner}, + wallet::{commitment_tree, get_balance, get_balance_at}, + AccountId, NoteId, ReceivedNoteId, }; #[cfg(feature = "transparent-inputs")] use { - zcash_client_backend::{ - data_api::wallet::shield_transparent_funds, wallet::WalletTransparentOutput, - }, + zcash_client_backend::wallet::WalletTransparentOutput, zcash_primitives::transaction::components::{amount::NonNegativeAmount, OutPoint, TxOut}, }; @@ -475,50 +461,30 @@ pub(crate) mod tests { #[test] fn send_proposed_transfer() { - let cache_file = NamedTempFile::new().unwrap(); - let db_cache = BlockDb(Connection::open(cache_file.path()).unwrap()); - init_cache_database(&db_cache).unwrap(); - - let data_file = NamedTempFile::new().unwrap(); - let mut db_data = WalletDb::for_path(data_file.path(), testing::network()).unwrap(); - init_wallet_db(&mut db_data, None).unwrap(); + let mut test = TestBuilder::new().with_block_cache().build(); // Add an account to the wallet let seed = Secret::new([0u8; 32].to_vec()); - let (account, usk) = db_data.create_account(&seed).unwrap(); + let (account, usk) = test.wallet_mut().create_account(&seed).unwrap(); let dfvk = usk.sapling().to_diversifiable_full_viewing_key(); // Add funds to the wallet in a single note let value = Amount::from_u64(60000).unwrap(); - let (cb, _) = fake_compact_block( - sapling_activation_height(), - BlockHash([0; 32]), - &dfvk, - AddressType::DefaultExternal, - value, - 0, - ); - insert_into_cache(&db_cache, &cb); - scan_cached_blocks( - &testing::network(), - &db_cache, - &mut db_data, - sapling_activation_height(), - 1, - ) - .unwrap(); + let (h, _, _) = test.generate_next_block(&dfvk, AddressType::DefaultExternal, value); + test.scan_cached_blocks(h, 1); // Verified balance matches total balance - let (_, anchor_height) = db_data + let (_, anchor_height) = test + .wallet() .get_target_and_anchor_heights(NonZeroU32::new(1).unwrap()) .unwrap() .unwrap(); assert_eq!( - get_balance(&db_data.conn, AccountId::from(0)).unwrap(), + get_balance(&test.wallet().conn, AccountId::from(0)).unwrap(), value ); assert_eq!( - get_balance_at(&db_data.conn, AccountId::from(0), anchor_height).unwrap(), + get_balance_at(&test.wallet().conn, AccountId::from(0), anchor_height).unwrap(), value ); @@ -538,9 +504,7 @@ pub(crate) mod tests { let change_strategy = fixed::SingleOutputChangeStrategy::new(fee_rule); let input_selector = &GreedyInputSelector::new(change_strategy, DustOutputPolicy::default()); - let proposal_result = propose_transfer::<_, _, _, Infallible>( - &mut db_data, - &testing::network(), + let proposal_result = test.propose_transfer( account, input_selector, request, @@ -549,10 +513,7 @@ pub(crate) mod tests { assert_matches!(proposal_result, Ok(_)); let change_memo = "Test change memo".parse::().unwrap(); - let create_proposed_result = create_proposed_transaction::<_, _, Infallible, _>( - &mut db_data, - &testing::network(), - test_prover(), + let create_proposed_result = test.create_proposed_transaction( &usk, OvkPolicy::Sender, proposal_result.unwrap(), @@ -564,18 +525,14 @@ pub(crate) mod tests { let sent_tx_id = create_proposed_result.unwrap(); // Verify that the sent transaction was stored and that we can decrypt the memos - let tx = db_data + let tx = test + .wallet() .get_transaction(sent_tx_id) .expect("Created transaction was stored."); let ufvks = [(account, usk.to_unified_full_viewing_key())] .into_iter() .collect(); - let decrypted_outputs = decrypt_transaction( - &testing::network(), - sapling_activation_height() + 1, - &tx, - &ufvks, - ); + let decrypted_outputs = decrypt_transaction(&testing::network(), h + 1, &tx, &ufvks); assert_eq!(decrypted_outputs.len(), 2); let mut found_tx_change_memo = false; @@ -592,7 +549,8 @@ pub(crate) mod tests { assert!(found_tx_empty_memo); // Verify that the stored sent notes match what we're expecting - let mut stmt_sent_notes = db_data + let mut stmt_sent_notes = test + .wallet() .conn .prepare( "SELECT output_index @@ -622,7 +580,8 @@ pub(crate) mod tests { let mut found_sent_change_memo = false; let mut found_sent_empty_memo = false; for sent_note_id in sent_note_ids { - match db_data + match test + .wallet() .get_memo(sent_note_id) .expect("Note id is valid") .as_ref() @@ -642,20 +601,19 @@ pub(crate) mod tests { // Check that querying for a nonexistent sent note returns None assert_matches!( - db_data.get_memo(NoteId::new(sent_tx_id, ShieldedProtocol::Sapling, 12345)), + test.wallet() + .get_memo(NoteId::new(sent_tx_id, ShieldedProtocol::Sapling, 12345)), Ok(None) ); } #[test] fn create_to_address_fails_on_incorrect_usk() { - let data_file = NamedTempFile::new().unwrap(); - let mut db_data = WalletDb::for_path(data_file.path(), testing::network()).unwrap(); - init_wallet_db(&mut db_data, Some(Secret::new(vec![]))).unwrap(); + let mut test = TestBuilder::new().with_seed(Secret::new(vec![])).build(); // Add an account to the wallet let seed = Secret::new([0u8; 32].to_vec()); - let (_, usk) = db_data.create_account(&seed).unwrap(); + let (_, usk) = test.wallet_mut().create_account(&seed).unwrap(); let dfvk = usk.sapling().to_diversifiable_full_viewing_key(); let to = dfvk.default_address().1.into(); @@ -665,10 +623,7 @@ pub(crate) mod tests { // Attempting to spend with a USK that is not in the wallet results in an error assert_matches!( - create_spend_to_address( - &mut db_data, - &testing::network(), - test_prover(), + test.create_spend_to_address( &usk1, &to, Amount::from_u64(1).unwrap(), @@ -682,28 +637,23 @@ pub(crate) mod tests { #[test] fn create_to_address_fails_with_no_blocks() { - let data_file = NamedTempFile::new().unwrap(); - let mut db_data = WalletDb::for_path(data_file.path(), testing::network()).unwrap(); - init_wallet_db(&mut db_data, None).unwrap(); + let mut test = TestBuilder::new().build(); // Add an account to the wallet let seed = Secret::new([0u8; 32].to_vec()); - let (_, usk) = db_data.create_account(&seed).unwrap(); + let (_, usk) = test.wallet_mut().create_account(&seed).unwrap(); let dfvk = usk.sapling().to_diversifiable_full_viewing_key(); let to = dfvk.default_address().1.into(); // Account balance should be zero assert_eq!( - get_balance(&db_data.conn, AccountId::from(0)).unwrap(), + get_balance(&test.wallet().conn, AccountId::from(0)).unwrap(), Amount::zero() ); // We cannot do anything if we aren't synchronised assert_matches!( - create_spend_to_address( - &mut db_data, - &testing::network(), - test_prover(), + test.create_spend_to_address( &usk, &to, Amount::from_u64(1).unwrap(), @@ -717,84 +667,49 @@ pub(crate) mod tests { #[test] fn create_to_address_fails_on_unverified_notes() { - let cache_file = NamedTempFile::new().unwrap(); - let db_cache = BlockDb(Connection::open(cache_file.path()).unwrap()); - init_cache_database(&db_cache).unwrap(); - - let data_file = NamedTempFile::new().unwrap(); - let mut db_data = WalletDb::for_path(data_file.path(), testing::network()).unwrap(); - init_wallet_db(&mut db_data, None).unwrap(); + let mut test = TestBuilder::new().with_block_cache().build(); // Add an account to the wallet let seed = Secret::new([0u8; 32].to_vec()); - let (_, usk) = db_data.create_account(&seed).unwrap(); + let (_, usk) = test.wallet_mut().create_account(&seed).unwrap(); let dfvk = usk.sapling().to_diversifiable_full_viewing_key(); // Add funds to the wallet in a single note let value = Amount::from_u64(50000).unwrap(); - let (mut cb, _) = fake_compact_block( - sapling_activation_height(), - BlockHash([0; 32]), - &dfvk, - AddressType::DefaultExternal, - value, - 0, - ); - insert_into_cache(&db_cache, &cb); - scan_cached_blocks( - &testing::network(), - &db_cache, - &mut db_data, - sapling_activation_height(), - 1, - ) - .unwrap(); + let (h1, _, _) = test.generate_next_block(&dfvk, AddressType::DefaultExternal, value); + test.scan_cached_blocks(h1, 1); // Verified balance matches total balance - let (_, anchor_height) = db_data + let (_, anchor_height) = test + .wallet() .get_target_and_anchor_heights(NonZeroU32::new(10).unwrap()) .unwrap() .unwrap(); assert_eq!( - get_balance(&db_data.conn, AccountId::from(0)).unwrap(), + get_balance(&test.wallet().conn, AccountId::from(0)).unwrap(), value ); assert_eq!( - get_balance_at(&db_data.conn, AccountId::from(0), anchor_height).unwrap(), + get_balance_at(&test.wallet().conn, AccountId::from(0), anchor_height).unwrap(), value ); // Add more funds to the wallet in a second note - cb = fake_compact_block( - sapling_activation_height() + 1, - cb.hash(), - &dfvk, - AddressType::DefaultExternal, - value, - 1, - ) - .0; - insert_into_cache(&db_cache, &cb); - scan_cached_blocks( - &testing::network(), - &db_cache, - &mut db_data, - sapling_activation_height() + 1, - 1, - ) - .unwrap(); + let (h2, _, _) = test.generate_next_block(&dfvk, AddressType::DefaultExternal, value); + test.scan_cached_blocks(h2, 1); // Verified balance does not include the second note - let (_, anchor_height2) = db_data + let (_, anchor_height2) = test + .wallet() .get_target_and_anchor_heights(NonZeroU32::new(10).unwrap()) .unwrap() .unwrap(); assert_eq!( - get_balance(&db_data.conn, AccountId::from(0)).unwrap(), + get_balance(&test.wallet().conn, AccountId::from(0)).unwrap(), (value + value).unwrap() ); assert_eq!( - get_balance_at(&db_data.conn, AccountId::from(0), anchor_height2).unwrap(), + get_balance_at(&test.wallet().conn, AccountId::from(0), anchor_height2).unwrap(), value ); @@ -802,10 +717,7 @@ pub(crate) mod tests { let extsk2 = ExtendedSpendingKey::master(&[]); let to = extsk2.default_address().1.into(); assert_matches!( - create_spend_to_address( - &mut db_data, - &testing::network(), - test_prover(), + test.create_spend_to_address( &usk, &to, Amount::from_u64(70000).unwrap(), @@ -823,33 +735,14 @@ pub(crate) mod tests { // Mine blocks SAPLING_ACTIVATION_HEIGHT + 2 to 9 until just before the second // note is verified - for i in 2..10 { - cb = fake_compact_block( - sapling_activation_height() + i, - cb.hash(), - &dfvk, - AddressType::DefaultExternal, - value, - i, - ) - .0; - insert_into_cache(&db_cache, &cb); + for _ in 2..10 { + test.generate_next_block(&dfvk, AddressType::DefaultExternal, value); } - scan_cached_blocks( - &testing::network(), - &db_cache, - &mut db_data, - sapling_activation_height() + 2, - 8, - ) - .unwrap(); + test.scan_cached_blocks(h2 + 1, 8); // Second spend still fails assert_matches!( - create_spend_to_address( - &mut db_data, - &testing::network(), - test_prover(), + test.create_spend_to_address( &usk, &to, Amount::from_u64(70000).unwrap(), @@ -866,31 +759,12 @@ pub(crate) mod tests { ); // Mine block 11 so that the second note becomes verified - cb = fake_compact_block( - sapling_activation_height() + 10, - cb.hash(), - &dfvk, - AddressType::DefaultExternal, - value, - 10, - ) - .0; - insert_into_cache(&db_cache, &cb); - scan_cached_blocks( - &testing::network(), - &db_cache, - &mut db_data, - sapling_activation_height() + 10, - 1, - ) - .unwrap(); + let (h11, _, _) = test.generate_next_block(&dfvk, AddressType::DefaultExternal, value); + test.scan_cached_blocks(h11, 1); // Second spend should now succeed assert_matches!( - create_spend_to_address( - &mut db_data, - &testing::network(), - test_prover(), + test.create_spend_to_address( &usk, &to, Amount::from_u64(70000).unwrap(), @@ -904,40 +778,22 @@ pub(crate) mod tests { #[test] fn create_to_address_fails_on_locked_notes() { - let cache_file = NamedTempFile::new().unwrap(); - let db_cache = BlockDb(Connection::open(cache_file.path()).unwrap()); - init_cache_database(&db_cache).unwrap(); - - let data_file = NamedTempFile::new().unwrap(); - let mut db_data = WalletDb::for_path(data_file.path(), testing::network()).unwrap(); - init_wallet_db(&mut db_data, Some(Secret::new(vec![]))).unwrap(); + let mut test = TestBuilder::new() + .with_block_cache() + .with_seed(Secret::new(vec![])) + .build(); // Add an account to the wallet let seed = Secret::new([0u8; 32].to_vec()); - let (_, usk) = db_data.create_account(&seed).unwrap(); + let (_, usk) = test.wallet_mut().create_account(&seed).unwrap(); let dfvk = usk.sapling().to_diversifiable_full_viewing_key(); // Add funds to the wallet in a single note let value = Amount::from_u64(50000).unwrap(); - let (mut cb, _) = fake_compact_block( - sapling_activation_height(), - BlockHash([0; 32]), - &dfvk, - AddressType::DefaultExternal, - value, - 0, - ); - insert_into_cache(&db_cache, &cb); - scan_cached_blocks( - &testing::network(), - &db_cache, - &mut db_data, - sapling_activation_height(), - 1, - ) - .unwrap(); + let (h1, _, _) = test.generate_next_block(&dfvk, AddressType::DefaultExternal, value); + test.scan_cached_blocks(h1, 1); assert_eq!( - get_balance(&db_data.conn, AccountId::from(0)).unwrap(), + get_balance(&test.wallet().conn, AccountId::from(0)).unwrap(), value ); @@ -945,10 +801,7 @@ pub(crate) mod tests { let extsk2 = ExtendedSpendingKey::master(&[]); let to = extsk2.default_address().1.into(); assert_matches!( - create_spend_to_address( - &mut db_data, - &testing::network(), - test_prover(), + test.create_spend_to_address( &usk, &to, Amount::from_u64(15000).unwrap(), @@ -961,10 +814,7 @@ pub(crate) mod tests { // A second spend fails because there are no usable notes assert_matches!( - create_spend_to_address( - &mut db_data, - &testing::network(), - test_prover(), + test.create_spend_to_address( &usk, &to, Amount::from_u64(2000).unwrap(), @@ -982,32 +832,17 @@ pub(crate) mod tests { // Mine blocks SAPLING_ACTIVATION_HEIGHT + 1 to 41 (that don't send us funds) // until just before the first transaction expires for i in 1..42 { - cb = fake_compact_block( - sapling_activation_height() + i, - cb.hash(), + test.generate_next_block( &ExtendedSpendingKey::master(&[i as u8]).to_diversifiable_full_viewing_key(), AddressType::DefaultExternal, value, - i, - ) - .0; - insert_into_cache(&db_cache, &cb); + ); } - scan_cached_blocks( - &testing::network(), - &db_cache, - &mut db_data, - sapling_activation_height() + 1, - 41, - ) - .unwrap(); + test.scan_cached_blocks(h1 + 1, 41); // Second spend still fails assert_matches!( - create_spend_to_address( - &mut db_data, - &testing::network(), - test_prover(), + test.create_spend_to_address( &usk, &to, Amount::from_u64(2000).unwrap(), @@ -1023,30 +858,15 @@ pub(crate) mod tests { ); // Mine block SAPLING_ACTIVATION_HEIGHT + 42 so that the first transaction expires - cb = fake_compact_block( - sapling_activation_height() + 42, - cb.hash(), + let (h43, _, _) = test.generate_next_block( &ExtendedSpendingKey::master(&[42]).to_diversifiable_full_viewing_key(), AddressType::DefaultExternal, value, - 42, - ) - .0; - insert_into_cache(&db_cache, &cb); - scan_cached_blocks( - &testing::network(), - &db_cache, - &mut db_data, - sapling_activation_height() + 42, - 1, - ) - .unwrap(); + ); + test.scan_cached_blocks(h43, 1); // Second spend should now succeed - create_spend_to_address( - &mut db_data, - &testing::network(), - test_prover(), + test.create_spend_to_address( &usk, &to, Amount::from_u64(2000).unwrap(), @@ -1059,41 +879,19 @@ pub(crate) mod tests { #[test] fn ovk_policy_prevents_recovery_from_chain() { - let network = testing::network(); - let cache_file = NamedTempFile::new().unwrap(); - let db_cache = BlockDb(Connection::open(cache_file.path()).unwrap()); - init_cache_database(&db_cache).unwrap(); - - let data_file = NamedTempFile::new().unwrap(); - let mut db_data = WalletDb::for_path(data_file.path(), network).unwrap(); - init_wallet_db(&mut db_data, None).unwrap(); + let mut test = TestBuilder::new().with_block_cache().build(); // Add an account to the wallet let seed = Secret::new([0u8; 32].to_vec()); - let (_, usk) = db_data.create_account(&seed).unwrap(); + let (_, usk) = test.wallet_mut().create_account(&seed).unwrap(); let dfvk = usk.sapling().to_diversifiable_full_viewing_key(); // Add funds to the wallet in a single note let value = Amount::from_u64(50000).unwrap(); - let (mut cb, _) = fake_compact_block( - sapling_activation_height(), - BlockHash([0; 32]), - &dfvk, - AddressType::DefaultExternal, - value, - 0, - ); - insert_into_cache(&db_cache, &cb); - scan_cached_blocks( - &testing::network(), - &db_cache, - &mut db_data, - sapling_activation_height(), - 1, - ) - .unwrap(); + let (h1, _, _) = test.generate_next_block(&dfvk, AddressType::DefaultExternal, value); + test.scan_cached_blocks(h1, 1); assert_eq!( - get_balance(&db_data.conn, AccountId::from(0)).unwrap(), + get_balance(&test.wallet().conn, AccountId::from(0)).unwrap(), value ); @@ -1102,7 +900,7 @@ pub(crate) mod tests { let to = addr2.into(); #[allow(clippy::type_complexity)] - let send_and_recover_with_policy = |db_data: &mut WalletDb, + let send_and_recover_with_policy = |test: &mut TestRunner, ovk_policy| -> Result< Option<(Note, PaymentAddress, MemoBytes)>, @@ -1114,10 +912,7 @@ pub(crate) mod tests { ReceivedNoteId, >, > { - let txid = create_spend_to_address( - db_data, - &testing::network(), - test_prover(), + let txid = test.create_spend_to_address( &usk, &to, Amount::from_u64(15000).unwrap(), @@ -1127,7 +922,8 @@ pub(crate) mod tests { )?; // Fetch the transaction from the database - let raw_tx: Vec<_> = db_data + let raw_tx: Vec<_> = test + .wallet() .conn .query_row( "SELECT raw FROM transactions @@ -1141,8 +937,8 @@ pub(crate) mod tests { for output in tx.sapling_bundle().unwrap().shielded_outputs() { // Find the output that decrypts with the external OVK let result = try_sapling_output_recovery( - &network, - sapling_activation_height(), + &network(), + h1, &dfvk.to_ovk(Scope::External), output, ); @@ -1158,96 +954,61 @@ pub(crate) mod tests { // Send some of the funds to another address, keeping history. // The recipient output is decryptable by the sender. assert_matches!( - send_and_recover_with_policy(&mut db_data, OvkPolicy::Sender), + send_and_recover_with_policy(&mut test, OvkPolicy::Sender), Ok(Some((_, recovered_to, _))) if recovered_to == addr2 ); // Mine blocks SAPLING_ACTIVATION_HEIGHT + 1 to 42 (that don't send us funds) // so that the first transaction expires for i in 1..=42 { - cb = fake_compact_block( - sapling_activation_height() + i, - cb.hash(), + test.generate_next_block( &ExtendedSpendingKey::master(&[i as u8]).to_diversifiable_full_viewing_key(), AddressType::DefaultExternal, value, - i, - ) - .0; - insert_into_cache(&db_cache, &cb); + ); } - scan_cached_blocks( - &network, - &db_cache, - &mut db_data, - sapling_activation_height() + 1, - 42, - ) - .unwrap(); + test.scan_cached_blocks(h1 + 1, 42); // Send the funds again, discarding history. // Neither transaction output is decryptable by the sender. assert_matches!( - send_and_recover_with_policy(&mut db_data, OvkPolicy::Discard), + send_and_recover_with_policy(&mut test, OvkPolicy::Discard), Ok(None) ); } #[test] fn create_to_address_succeeds_to_t_addr_zero_change() { - let cache_file = NamedTempFile::new().unwrap(); - let db_cache = BlockDb(Connection::open(cache_file.path()).unwrap()); - init_cache_database(&db_cache).unwrap(); - - let data_file = NamedTempFile::new().unwrap(); - let mut db_data = WalletDb::for_path(data_file.path(), testing::network()).unwrap(); - init_wallet_db(&mut db_data, None).unwrap(); + let mut test = TestBuilder::new().with_block_cache().build(); // Add an account to the wallet let seed = Secret::new([0u8; 32].to_vec()); - let (_, usk) = db_data.create_account(&seed).unwrap(); + let (_, usk) = test.wallet_mut().create_account(&seed).unwrap(); let dfvk = usk.sapling().to_diversifiable_full_viewing_key(); // Add funds to the wallet in a single note let value = Amount::from_u64(60000).unwrap(); - let (cb, _) = fake_compact_block( - sapling_activation_height(), - BlockHash([0; 32]), - &dfvk, - AddressType::DefaultExternal, - value, - 0, - ); - insert_into_cache(&db_cache, &cb); - scan_cached_blocks( - &testing::network(), - &db_cache, - &mut db_data, - sapling_activation_height(), - 1, - ) - .unwrap(); + let (h, _, _) = test.generate_next_block(&dfvk, AddressType::DefaultExternal, value); + test.scan_cached_blocks(h, 1); // Verified balance matches total balance - let (_, anchor_height) = db_data + let (_, anchor_height) = test + .wallet() .get_target_and_anchor_heights(NonZeroU32::new(1).unwrap()) .unwrap() .unwrap(); assert_eq!( - get_balance(&db_data.conn, AccountId::from(0)).unwrap(), + get_balance(&test.wallet().conn, AccountId::from(0)).unwrap(), value ); assert_eq!( - get_balance_at(&db_data.conn, AccountId::from(0), anchor_height).unwrap(), + get_balance_at(&test.wallet().conn, AccountId::from(0), anchor_height).unwrap(), value ); let to = TransparentAddress::PublicKey([7; 20]).into(); assert_matches!( - create_spend_to_address( - &mut db_data, - &testing::network(), - test_prover(), + test.create_spend_to_address( &usk, &to, Amount::from_u64(50000).unwrap(), @@ -1261,59 +1022,36 @@ pub(crate) mod tests { #[test] fn create_to_address_spends_a_change_note() { - let cache_file = NamedTempFile::new().unwrap(); - let db_cache = BlockDb(Connection::open(cache_file.path()).unwrap()); - init_cache_database(&db_cache).unwrap(); - - let data_file = NamedTempFile::new().unwrap(); - let mut db_data = WalletDb::for_path(data_file.path(), testing::network()).unwrap(); - init_wallet_db(&mut db_data, None).unwrap(); + let mut test = TestBuilder::new().with_block_cache().build(); // Add an account to the wallet let seed = Secret::new([0u8; 32].to_vec()); - let (_, usk) = db_data.create_account(&seed).unwrap(); + let (_, usk) = test.wallet_mut().create_account(&seed).unwrap(); let dfvk = usk.sapling().to_diversifiable_full_viewing_key(); // Add funds to the wallet in a single note let value = Amount::from_u64(60000).unwrap(); - let (cb, _) = fake_compact_block( - sapling_activation_height(), - BlockHash([0; 32]), - &dfvk, - AddressType::Internal, - value, - 0, - ); - insert_into_cache(&db_cache, &cb); - scan_cached_blocks( - &testing::network(), - &db_cache, - &mut db_data, - sapling_activation_height(), - 1, - ) - .unwrap(); + let (h, _, _) = test.generate_next_block(&dfvk, AddressType::Internal, value); + test.scan_cached_blocks(h, 1); // Verified balance matches total balance - let (_, anchor_height) = db_data + let (_, anchor_height) = test + .wallet() .get_target_and_anchor_heights(NonZeroU32::new(10).unwrap()) .unwrap() .unwrap(); assert_eq!( - get_balance(&db_data.conn, AccountId::from(0)).unwrap(), + get_balance(&test.wallet().conn, AccountId::from(0)).unwrap(), value ); assert_eq!( - get_balance_at(&db_data.conn, AccountId::from(0), anchor_height).unwrap(), + get_balance_at(&test.wallet().conn, AccountId::from(0), anchor_height).unwrap(), value ); let to = TransparentAddress::PublicKey([7; 20]).into(); assert_matches!( - create_spend_to_address( - &mut db_data, - &testing::network(), - test_prover(), + test.create_spend_to_address( &usk, &to, Amount::from_u64(50000).unwrap(), @@ -1327,65 +1065,44 @@ pub(crate) mod tests { #[test] fn zip317_spend() { - let cache_file = NamedTempFile::new().unwrap(); - let db_cache = BlockDb(Connection::open(cache_file.path()).unwrap()); - init_cache_database(&db_cache).unwrap(); - - let data_file = NamedTempFile::new().unwrap(); - let mut db_data = WalletDb::for_path(data_file.path(), testing::network()).unwrap(); - init_wallet_db(&mut db_data, None).unwrap(); + let mut test = TestBuilder::new().with_block_cache().build(); // Add an account to the wallet let seed = Secret::new([0u8; 32].to_vec()); - let (_, usk) = db_data.create_account(&seed).unwrap(); + let (_, usk) = test.wallet_mut().create_account(&seed).unwrap(); let dfvk = usk.sapling().to_diversifiable_full_viewing_key(); // Add funds to the wallet - let (mut cb, _) = fake_compact_block( - sapling_activation_height(), - BlockHash([0; 32]), + let (h1, _, _) = test.generate_next_block( &dfvk, AddressType::Internal, Amount::from_u64(50000).unwrap(), - 0, ); - insert_into_cache(&db_cache, &cb); // Add 10 dust notes to the wallet - for i in 1..=10 { - cb = fake_compact_block( - sapling_activation_height() + i, - cb.hash(), + for _ in 1..=10 { + test.generate_next_block( &dfvk, AddressType::DefaultExternal, Amount::from_u64(1000).unwrap(), - i, - ) - .0; - insert_into_cache(&db_cache, &cb); + ); } - scan_cached_blocks( - &testing::network(), - &db_cache, - &mut db_data, - sapling_activation_height(), - 11, - ) - .unwrap(); + test.scan_cached_blocks(h1, 11); // Verified balance matches total balance let total = Amount::from_u64(60000).unwrap(); - let (_, anchor_height) = db_data + let (_, anchor_height) = test + .wallet() .get_target_and_anchor_heights(NonZeroU32::new(1).unwrap()) .unwrap() .unwrap(); assert_eq!( - get_balance(&db_data.conn, AccountId::from(0)).unwrap(), + get_balance(&test.wallet().conn, AccountId::from(0)).unwrap(), total ); assert_eq!( - get_balance_at(&db_data.conn, AccountId::from(0), anchor_height).unwrap(), + get_balance_at(&test.wallet().conn, AccountId::from(0), anchor_height).unwrap(), total ); @@ -1406,10 +1123,7 @@ pub(crate) mod tests { .unwrap(); assert_matches!( - spend( - &mut db_data, - &testing::network(), - test_prover(), + test.spend( &input_selector, &usk, req, @@ -1434,10 +1148,7 @@ pub(crate) mod tests { .unwrap(); assert_matches!( - spend( - &mut db_data, - &testing::network(), - test_prover(), + test.spend( &input_selector, &usk, req, @@ -1451,32 +1162,38 @@ pub(crate) mod tests { #[test] #[cfg(feature = "transparent-inputs")] fn shield_transparent() { - let cache_file = NamedTempFile::new().unwrap(); - let db_cache = BlockDb(Connection::open(cache_file.path()).unwrap()); - init_cache_database(&db_cache).unwrap(); - - let data_file = NamedTempFile::new().unwrap(); - let mut db_data = WalletDb::for_path(data_file.path(), testing::network()).unwrap(); - init_wallet_db(&mut db_data, None).unwrap(); + let mut test = TestBuilder::new().with_block_cache().build(); // Add an account to the wallet let seed = Secret::new([0u8; 32].to_vec()); - let (account_id, usk) = db_data.create_account(&seed).unwrap(); + let (account_id, usk) = test.wallet_mut().create_account(&seed).unwrap(); let dfvk = usk.sapling().to_diversifiable_full_viewing_key(); - let uaddr = db_data.get_current_address(account_id).unwrap().unwrap(); + let uaddr = test + .wallet() + .get_current_address(account_id) + .unwrap() + .unwrap(); let taddr = uaddr.transparent().unwrap(); + // Ensure that the wallet has at least one block + let (h, _, _) = test.generate_next_block( + &dfvk, + AddressType::Internal, + Amount::from_u64(50000).unwrap(), + ); + test.scan_cached_blocks(h, 1); + let utxo = WalletTransparentOutput::from_parts( OutPoint::new([1u8; 32], 1), TxOut { value: Amount::from_u64(10000).unwrap(), script_pubkey: taddr.script(), }, - sapling_activation_height(), + h, ) .unwrap(); - let res0 = db_data.put_received_transparent_utxo(&utxo); + let res0 = test.wallet_mut().put_received_transparent_utxo(&utxo); assert!(matches!(res0, Ok(_))); let input_selector = GreedyInputSelector::new( @@ -1484,30 +1201,8 @@ pub(crate) mod tests { DustOutputPolicy::default(), ); - // Ensure that the wallet has at least one block - let (cb, _) = fake_compact_block( - sapling_activation_height(), - BlockHash([0; 32]), - &dfvk, - AddressType::Internal, - Amount::from_u64(50000).unwrap(), - 0, - ); - insert_into_cache(&db_cache, &cb); - scan_cached_blocks( - &testing::network(), - &db_cache, - &mut db_data, - sapling_activation_height(), - 1, - ) - .unwrap(); - assert_matches!( - shield_transparent_funds( - &mut db_data, - &testing::network(), - test_prover(), + test.shield_transparent_funds( &input_selector, NonNegativeAmount::from_u64(10000).unwrap(), &usk, diff --git a/zcash_client_sqlite/src/wallet/scanning.rs b/zcash_client_sqlite/src/wallet/scanning.rs index 27863ff9e..8bfb8c1ce 100644 --- a/zcash_client_sqlite/src/wallet/scanning.rs +++ b/zcash_client_sqlite/src/wallet/scanning.rs @@ -738,11 +738,9 @@ mod tests { use std::ops::Range; use incrementalmerkletree::{Hashable, Level}; - use rusqlite::Connection; use secrecy::Secret; - use tempfile::NamedTempFile; use zcash_client_backend::data_api::{ - chain::{scan_cached_blocks, CommitmentTreeRoot}, + chain::CommitmentTreeRoot, scanning::{ScanPriority, ScanRange}, WalletCommitmentTrees, WalletRead, WalletWrite, }; @@ -754,16 +752,8 @@ mod tests { }; use crate::{ - chain::init::init_cache_database, - testing::{ - self, fake_compact_block, init_test_accounts_table, insert_into_cache, - sapling_activation_height, AddressType, - }, - wallet::{ - init::{init_blocks_table, init_wallet_db}, - scanning::suggest_scan_ranges, - }, - BlockDb, WalletDb, + testing::{sapling_activation_height, AddressType, TestBuilder}, + wallet::{init::init_blocks_table, scanning::suggest_scan_ranges}, }; use super::{join_nonoverlapping, Joined, RangeOrdering, SpanningTree}; @@ -1093,21 +1083,18 @@ mod tests { fn scan_complete() { use ScanPriority::*; - let cache_file = NamedTempFile::new().unwrap(); - let db_cache = BlockDb(Connection::open(cache_file.path()).unwrap()); - init_cache_database(&db_cache).unwrap(); + let mut test = TestBuilder::new() + .with_block_cache() + .with_seed(Secret::new(vec![])) + .with_test_account() + .build(); - let data_file = NamedTempFile::new().unwrap(); - let mut db_data = WalletDb::for_path(data_file.path(), testing::network()).unwrap(); - init_wallet_db(&mut db_data, Some(Secret::new(vec![]))).unwrap(); - - // Add an account to the wallet. - let (dfvk, _taddr) = init_test_accounts_table(&mut db_data); + let dfvk = test.test_account_sapling().unwrap(); assert_matches!( // In the following, we don't care what the root hashes are, they just need to be // distinct. - db_data.put_sapling_subtree_roots( + test.wallet_mut().put_sapling_subtree_roots( 0, &[ CommitmentTreeRoot::from_parts( @@ -1134,7 +1121,7 @@ mod tests { let initial_height = sapling_activation_height() + 310; let value = Amount::from_u64(50000).unwrap(); - let (mut cb, _) = fake_compact_block( + test.generate_block_at( initial_height, BlockHash([0; 32]), &dfvk, @@ -1142,36 +1129,21 @@ mod tests { value, initial_sapling_tree_size, ); - insert_into_cache(&db_cache, &cb); - for i in 1..=10 { - cb = fake_compact_block( - initial_height + i, - cb.hash(), + for _ in 1..=10 { + test.generate_next_block( &dfvk, AddressType::DefaultExternal, Amount::from_u64(10000).unwrap(), - initial_sapling_tree_size + i, - ) - .0; - insert_into_cache(&db_cache, &cb); + ); } - assert_matches!( - scan_cached_blocks( - &testing::network(), - &db_cache, - &mut db_data, - initial_height, - 10, - ), - Ok(()) - ); + test.scan_cached_blocks(initial_height, 10); // Verify the that adjacent range needed to make the note spendable has been prioritized. let sap_active = u32::from(sapling_activation_height()); assert_matches!( - db_data.suggest_scan_ranges(), + test.wallet().suggest_scan_ranges(), Ok(scan_ranges) if scan_ranges == vec![ scan_range((sap_active + 300)..(sap_active + 310), FoundNote) ] @@ -1179,7 +1151,7 @@ mod tests { // Check that the scanned range has been properly persisted. assert_matches!( - suggest_scan_ranges(&db_data.conn, Scanned), + suggest_scan_ranges(&test.wallet().conn, Scanned), Ok(scan_ranges) if scan_ranges == vec![ scan_range((sap_active + 300)..(sap_active + 310), FoundNote), scan_range((sap_active + 310)..(sap_active + 320), Scanned) @@ -1189,14 +1161,15 @@ mod tests { // Simulate the wallet going offline for a bit, update the chain tip to 20 blocks in the // future. assert_matches!( - db_data.update_chain_tip(sapling_activation_height() + 340), + test.wallet_mut() + .update_chain_tip(sapling_activation_height() + 340), Ok(()) ); // Check the scan range again, we should see a `ChainTip` range for the period we've been // offline. assert_matches!( - db_data.suggest_scan_ranges(), + test.wallet().suggest_scan_ranges(), Ok(scan_ranges) if scan_ranges == vec![ scan_range((sap_active + 320)..(sap_active + 341), ChainTip), scan_range((sap_active + 300)..(sap_active + 310), ChainTip) @@ -1205,14 +1178,15 @@ mod tests { // Now simulate a jump ahead more than 100 blocks. assert_matches!( - db_data.update_chain_tip(sapling_activation_height() + 450), + test.wallet_mut() + .update_chain_tip(sapling_activation_height() + 450), Ok(()) ); // Check the scan range again, we should see a `Validate` range for the previous wallet // tip, and then a `ChainTip` for the remaining range. assert_matches!( - db_data.suggest_scan_ranges(), + test.wallet().suggest_scan_ranges(), Ok(scan_ranges) if scan_ranges == vec![ scan_range((sap_active + 319)..(sap_active + 329), Verify), scan_range((sap_active + 329)..(sap_active + 451), ChainTip), @@ -1225,22 +1199,22 @@ mod tests { fn init_blocks_table_creates_ignored_range() { use ScanPriority::*; - let data_file = NamedTempFile::new().unwrap(); - let mut db_data = WalletDb::for_path(data_file.path(), testing::network()).unwrap(); - init_wallet_db(&mut db_data, Some(Secret::new(vec![]))).unwrap(); + let mut test = TestBuilder::new().with_seed(Secret::new(vec![])).build(); - let sap_active = db_data + let sap_active = test + .wallet() .params .activation_height(NetworkUpgrade::Sapling) .unwrap(); // Initialise the blocks table. We use Canopy activation as an arbitrary birthday height // that's greater than Sapling activation. - let birthday_height = db_data + let birthday_height = test + .wallet() .params .activation_height(NetworkUpgrade::Canopy) .unwrap(); init_blocks_table( - &mut db_data, + test.wallet_mut(), birthday_height, BlockHash([1; 32]), 1, @@ -1256,12 +1230,12 @@ mod tests { ), ]; assert_matches!( - suggest_scan_ranges(&db_data.conn, Ignored), + suggest_scan_ranges(&test.wallet().conn, Ignored), Ok(scan_ranges) if scan_ranges == expected ); // Set up some shard history - db_data + test.wallet_mut() .put_sapling_subtree_roots( 0, &[ @@ -1280,11 +1254,12 @@ mod tests { .unwrap(); // Update the chain tip - let tip_height = db_data + let tip_height = test + .wallet() .params .activation_height(NetworkUpgrade::Nu5) .unwrap(); - db_data.update_chain_tip(tip_height).unwrap(); + test.wallet_mut().update_chain_tip(tip_height).unwrap(); // Verify that the suggested scan ranges match what is expected let expected = vec![ @@ -1315,7 +1290,7 @@ mod tests { ]; assert_matches!( - suggest_scan_ranges(&db_data.conn, Ignored), + suggest_scan_ranges(&test.wallet().conn, Ignored), Ok(scan_ranges) if scan_ranges == expected ); } From 578eac15df42190a1e04fd573c379ca685a12b60 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Mon, 28 Aug 2023 18:54:35 +0000 Subject: [PATCH 4/5] zcash_client_sqlite: Rename `TestRunner` to `TestState` --- zcash_client_sqlite/src/chain.rs | 132 +++++++------- zcash_client_sqlite/src/lib.rs | 49 +++--- zcash_client_sqlite/src/testing.rs | 14 +- zcash_client_sqlite/src/wallet.rs | 34 ++-- zcash_client_sqlite/src/wallet/sapling.rs | 192 ++++++++++----------- zcash_client_sqlite/src/wallet/scanning.rs | 42 ++--- 6 files changed, 230 insertions(+), 233 deletions(-) diff --git a/zcash_client_sqlite/src/chain.rs b/zcash_client_sqlite/src/chain.rs index 35f90a80a..519f891bb 100644 --- a/zcash_client_sqlite/src/chain.rs +++ b/zcash_client_sqlite/src/chain.rs @@ -352,66 +352,66 @@ mod tests { #[test] fn valid_chain_states() { - let mut test = TestBuilder::new() + let mut st = TestBuilder::new() .with_block_cache() .with_seed(Secret::new(vec![])) .with_test_account() .build(); - let dfvk = test.test_account_sapling().unwrap(); + let dfvk = st.test_account_sapling().unwrap(); // Empty chain should return None - assert_matches!(test.wallet().chain_height(), Ok(None)); + assert_matches!(st.wallet().chain_height(), Ok(None)); // Create a fake CompactBlock sending value to the address - let (h1, _, _) = test.generate_next_block( + let (h1, _, _) = st.generate_next_block( &dfvk, AddressType::DefaultExternal, Amount::from_u64(5).unwrap(), ); // Scan the cache - test.scan_cached_blocks(h1, 1); + st.scan_cached_blocks(h1, 1); // Create a second fake CompactBlock sending more value to the address - let (h2, _, _) = test.generate_next_block( + let (h2, _, _) = st.generate_next_block( &dfvk, AddressType::DefaultExternal, Amount::from_u64(7).unwrap(), ); // Scanning should detect no inconsistencies - test.scan_cached_blocks(h2, 1); + st.scan_cached_blocks(h2, 1); } #[test] fn invalid_chain_cache_disconnected() { - let mut test = TestBuilder::new() + let mut st = TestBuilder::new() .with_block_cache() .with_seed(Secret::new(vec![])) .with_test_account() .build(); - let dfvk = test.test_account_sapling().unwrap(); + let dfvk = st.test_account_sapling().unwrap(); // Create some fake CompactBlocks - let (h, _, _) = test.generate_next_block( + let (h, _, _) = st.generate_next_block( &dfvk, AddressType::DefaultExternal, Amount::from_u64(5).unwrap(), ); - let (last_contiguous_height, _, _) = test.generate_next_block( + let (last_contiguous_height, _, _) = st.generate_next_block( &dfvk, AddressType::DefaultExternal, Amount::from_u64(7).unwrap(), ); // Scanning the cache should find no inconsistencies - test.scan_cached_blocks(h, 2); + st.scan_cached_blocks(h, 2); // Create more fake CompactBlocks that don't connect to the scanned ones let disconnect_height = last_contiguous_height + 1; - test.generate_block_at( + st.generate_block_at( disconnect_height, BlockHash([1; 32]), &dfvk, @@ -419,7 +419,7 @@ mod tests { Amount::from_u64(8).unwrap(), 2, ); - test.generate_next_block( + st.generate_next_block( &dfvk, AddressType::DefaultExternal, Amount::from_u64(3).unwrap(), @@ -427,7 +427,7 @@ mod tests { // Data+cache chain should be invalid at the data/cache boundary assert_matches!( - test.try_scan_cached_blocks( + st.try_scan_cached_blocks( disconnect_height, 2 ), @@ -438,99 +438,99 @@ mod tests { #[test] fn data_db_truncation() { - let mut test = TestBuilder::new() + let mut st = TestBuilder::new() .with_block_cache() .with_seed(Secret::new(vec![])) .with_test_account() .build(); - let dfvk = test.test_account_sapling().unwrap(); + let dfvk = st.test_account_sapling().unwrap(); // Account balance should be zero assert_eq!( - get_balance(&test.wallet().conn, AccountId::from(0)).unwrap(), + get_balance(&st.wallet().conn, AccountId::from(0)).unwrap(), Amount::zero() ); // Create fake CompactBlocks sending value to the address let value = Amount::from_u64(5).unwrap(); let value2 = Amount::from_u64(7).unwrap(); - let (h, _, _) = test.generate_next_block(&dfvk, AddressType::DefaultExternal, value); - test.generate_next_block(&dfvk, AddressType::DefaultExternal, value2); + let (h, _, _) = st.generate_next_block(&dfvk, AddressType::DefaultExternal, value); + st.generate_next_block(&dfvk, AddressType::DefaultExternal, value2); // Scan the cache - test.scan_cached_blocks(h, 2); + st.scan_cached_blocks(h, 2); // Account balance should reflect both received notes assert_eq!( - get_balance(&test.wallet().conn, AccountId::from(0)).unwrap(), + get_balance(&st.wallet().conn, AccountId::from(0)).unwrap(), (value + value2).unwrap() ); // "Rewind" to height of last scanned block - test.wallet_mut() + st.wallet_mut() .transactionally(|wdb| truncate_to_height(wdb.conn.0, &wdb.params, h + 1)) .unwrap(); // Account balance should be unaltered assert_eq!( - get_balance(&test.wallet().conn, AccountId::from(0)).unwrap(), + get_balance(&st.wallet().conn, AccountId::from(0)).unwrap(), (value + value2).unwrap() ); // Rewind so that one block is dropped - test.wallet_mut() + st.wallet_mut() .transactionally(|wdb| truncate_to_height(wdb.conn.0, &wdb.params, h)) .unwrap(); // Account balance should only contain the first received note assert_eq!( - get_balance(&test.wallet().conn, AccountId::from(0)).unwrap(), + get_balance(&st.wallet().conn, AccountId::from(0)).unwrap(), value ); // Scan the cache again - test.scan_cached_blocks(h, 2); + st.scan_cached_blocks(h, 2); // Account balance should again reflect both received notes assert_eq!( - get_balance(&test.wallet().conn, AccountId::from(0)).unwrap(), + get_balance(&st.wallet().conn, AccountId::from(0)).unwrap(), (value + value2).unwrap() ); } #[test] fn scan_cached_blocks_allows_blocks_out_of_order() { - let mut test = TestBuilder::new() + let mut st = TestBuilder::new() .with_block_cache() .with_seed(Secret::new(vec![])) .build(); // Add an account to the wallet let seed = Secret::new([0u8; 32].to_vec()); - let (_, usk) = test.wallet_mut().create_account(&seed).unwrap(); + let (_, usk) = st.wallet_mut().create_account(&seed).unwrap(); let dfvk = usk.sapling().to_diversifiable_full_viewing_key(); // Create a block with height SAPLING_ACTIVATION_HEIGHT let value = Amount::from_u64(50000).unwrap(); - let (h1, _, _) = test.generate_next_block(&dfvk, AddressType::DefaultExternal, value); - test.scan_cached_blocks(h1, 1); + let (h1, _, _) = st.generate_next_block(&dfvk, AddressType::DefaultExternal, value); + st.scan_cached_blocks(h1, 1); assert_eq!( - get_balance(&test.wallet().conn, AccountId::from(0)).unwrap(), + get_balance(&st.wallet().conn, AccountId::from(0)).unwrap(), value ); // Create blocks to reach SAPLING_ACTIVATION_HEIGHT + 2 - let (h2, _, _) = test.generate_next_block(&dfvk, AddressType::DefaultExternal, value); - let (h3, _, _) = test.generate_next_block(&dfvk, AddressType::DefaultExternal, value); + let (h2, _, _) = st.generate_next_block(&dfvk, AddressType::DefaultExternal, value); + let (h3, _, _) = st.generate_next_block(&dfvk, AddressType::DefaultExternal, value); // Scan the later block first - test.scan_cached_blocks(h3, 1); + st.scan_cached_blocks(h3, 1); // Now scan the block of height SAPLING_ACTIVATION_HEIGHT + 1 - test.scan_cached_blocks(h2, 1); + st.scan_cached_blocks(h2, 1); assert_eq!( - get_balance(&test.wallet().conn, AccountId::from(0)).unwrap(), + get_balance(&st.wallet().conn, AccountId::from(0)).unwrap(), Amount::from_u64(150_000).unwrap() ); @@ -549,7 +549,7 @@ mod tests { DustOutputPolicy::default(), ); assert_matches!( - test.spend( + st.spend( &input_selector, &usk, req, @@ -562,74 +562,74 @@ mod tests { #[test] fn scan_cached_blocks_finds_received_notes() { - let mut test = TestBuilder::new() + let mut st = TestBuilder::new() .with_block_cache() .with_seed(Secret::new(vec![])) .with_test_account() .build(); - let dfvk = test.test_account_sapling().unwrap(); + let dfvk = st.test_account_sapling().unwrap(); // Account balance should be zero assert_eq!( - get_balance(&test.wallet().conn, AccountId::from(0)).unwrap(), + get_balance(&st.wallet().conn, AccountId::from(0)).unwrap(), Amount::zero() ); // Create a fake CompactBlock sending value to the address let value = Amount::from_u64(5).unwrap(); - let (h1, _, _) = test.generate_next_block(&dfvk, AddressType::DefaultExternal, value); + let (h1, _, _) = st.generate_next_block(&dfvk, AddressType::DefaultExternal, value); // Scan the cache - test.scan_cached_blocks(h1, 1); + st.scan_cached_blocks(h1, 1); // Account balance should reflect the received note assert_eq!( - get_balance(&test.wallet().conn, AccountId::from(0)).unwrap(), + get_balance(&st.wallet().conn, AccountId::from(0)).unwrap(), value ); // Create a second fake CompactBlock sending more value to the address let value2 = Amount::from_u64(7).unwrap(); - let (h2, _, _) = test.generate_next_block(&dfvk, AddressType::DefaultExternal, value2); + let (h2, _, _) = st.generate_next_block(&dfvk, AddressType::DefaultExternal, value2); // Scan the cache again - test.scan_cached_blocks(h2, 1); + st.scan_cached_blocks(h2, 1); // Account balance should reflect both received notes assert_eq!( - get_balance(&test.wallet().conn, AccountId::from(0)).unwrap(), + get_balance(&st.wallet().conn, AccountId::from(0)).unwrap(), (value + value2).unwrap() ); } #[test] fn scan_cached_blocks_finds_change_notes() { - let mut test = TestBuilder::new() + let mut st = TestBuilder::new() .with_block_cache() .with_seed(Secret::new(vec![])) .with_test_account() .build(); - let dfvk = test.test_account_sapling().unwrap(); + let dfvk = st.test_account_sapling().unwrap(); // Account balance should be zero assert_eq!( - get_balance(&test.wallet().conn, AccountId::from(0)).unwrap(), + get_balance(&st.wallet().conn, AccountId::from(0)).unwrap(), Amount::zero() ); // Create a fake CompactBlock sending value to the address let value = Amount::from_u64(5).unwrap(); let (received_height, _, nf) = - test.generate_next_block(&dfvk, AddressType::DefaultExternal, value); + st.generate_next_block(&dfvk, AddressType::DefaultExternal, value); // Scan the cache - test.scan_cached_blocks(received_height, 1); + st.scan_cached_blocks(received_height, 1); // Account balance should reflect the received note assert_eq!( - get_balance(&test.wallet().conn, AccountId::from(0)).unwrap(), + get_balance(&st.wallet().conn, AccountId::from(0)).unwrap(), value ); @@ -637,60 +637,60 @@ mod tests { let extsk2 = ExtendedSpendingKey::master(&[0]); let to2 = extsk2.default_address().1; let value2 = Amount::from_u64(2).unwrap(); - let (spent_height, _) = test.generate_next_block_spending(&dfvk, (nf, value), to2, value2); + let (spent_height, _) = st.generate_next_block_spending(&dfvk, (nf, value), to2, value2); // Scan the cache again - test.scan_cached_blocks(spent_height, 1); + st.scan_cached_blocks(spent_height, 1); // Account balance should equal the change assert_eq!( - get_balance(&test.wallet().conn, AccountId::from(0)).unwrap(), + get_balance(&st.wallet().conn, AccountId::from(0)).unwrap(), (value - value2).unwrap() ); } #[test] fn scan_cached_blocks_detects_spends_out_of_order() { - let mut test = TestBuilder::new() + let mut st = TestBuilder::new() .with_block_cache() .with_seed(Secret::new(vec![])) .with_test_account() .build(); - let dfvk = test.test_account_sapling().unwrap(); + let dfvk = st.test_account_sapling().unwrap(); // Account balance should be zero assert_eq!( - get_balance(&test.wallet().conn, AccountId::from(0)).unwrap(), + get_balance(&st.wallet().conn, AccountId::from(0)).unwrap(), Amount::zero() ); // Create a fake CompactBlock sending value to the address let value = Amount::from_u64(5).unwrap(); let (received_height, _, nf) = - test.generate_next_block(&dfvk, AddressType::DefaultExternal, value); + st.generate_next_block(&dfvk, AddressType::DefaultExternal, value); // Create a second fake CompactBlock spending value from the address let extsk2 = ExtendedSpendingKey::master(&[0]); let to2 = extsk2.default_address().1; let value2 = Amount::from_u64(2).unwrap(); - let (spent_height, _) = test.generate_next_block_spending(&dfvk, (nf, value), to2, value2); + let (spent_height, _) = st.generate_next_block_spending(&dfvk, (nf, value), to2, value2); // Scan the spending block first. - test.scan_cached_blocks(spent_height, 1); + st.scan_cached_blocks(spent_height, 1); // Account balance should equal the change assert_eq!( - get_balance(&test.wallet().conn, AccountId::from(0)).unwrap(), + get_balance(&st.wallet().conn, AccountId::from(0)).unwrap(), (value - value2).unwrap() ); // Now scan the block in which we received the note that was spent. - test.scan_cached_blocks(received_height, 1); + st.scan_cached_blocks(received_height, 1); // Account balance should be the same. assert_eq!( - get_balance(&test.wallet().conn, AccountId::from(0)).unwrap(), + get_balance(&st.wallet().conn, AccountId::from(0)).unwrap(), (value - value2).unwrap() ); } diff --git a/zcash_client_sqlite/src/lib.rs b/zcash_client_sqlite/src/lib.rs index e3ecf3eca..dde55aa63 100644 --- a/zcash_client_sqlite/src/lib.rs +++ b/zcash_client_sqlite/src/lib.rs @@ -1100,20 +1100,17 @@ mod tests { #[test] pub(crate) fn get_next_available_address() { - let mut test = TestBuilder::new().with_test_account().build(); + let mut st = TestBuilder::new().with_test_account().build(); let account = AccountId::from(0); - let current_addr = test.wallet().get_current_address(account).unwrap(); + let current_addr = st.wallet().get_current_address(account).unwrap(); assert!(current_addr.is_some()); - let addr2 = test - .wallet_mut() - .get_next_available_address(account) - .unwrap(); + let addr2 = st.wallet_mut().get_next_available_address(account).unwrap(); assert!(addr2.is_some()); assert_ne!(current_addr, addr2); - let addr2_cur = test.wallet().get_current_address(account).unwrap(); + let addr2_cur = st.wallet().get_current_address(account).unwrap(); assert_eq!(addr2, addr2_cur); } @@ -1122,17 +1119,17 @@ mod tests { fn transparent_receivers() { use secrecy::Secret; - let test = TestBuilder::new() + let st = TestBuilder::new() .with_block_cache() .with_seed(Secret::new(vec![])) .with_test_account() .build(); // Add an account to the wallet. - let (ufvk, taddr) = test.test_account().unwrap(); + let (ufvk, taddr) = st.test_account().unwrap(); let taddr = taddr.unwrap(); - let receivers = test.wallet().get_transparent_receivers(0.into()).unwrap(); + let receivers = st.wallet().get_transparent_receivers(0.into()).unwrap(); // The receiver for the default UA should be in the set. assert!(receivers.contains_key(ufvk.default_address().0.transparent().unwrap())); @@ -1144,44 +1141,44 @@ mod tests { #[cfg(feature = "unstable")] #[test] pub(crate) fn fsblockdb_api() { - let mut test = TestBuilder::new().with_fs_block_cache().build(); + let mut st = TestBuilder::new().with_fs_block_cache().build(); // The BlockMeta DB starts off empty. - assert_eq!(test.cache().get_max_cached_height().unwrap(), None); + assert_eq!(st.cache().get_max_cached_height().unwrap(), None); // Generate some fake CompactBlocks. let seed = [0u8; 32]; let account = AccountId::from(0); - let extsk = sapling::spending_key(&seed, test.wallet().params.coin_type(), account); + let extsk = sapling::spending_key(&seed, st.wallet().params.coin_type(), account); let dfvk = extsk.to_diversifiable_full_viewing_key(); - let (h1, meta1, _) = test.generate_next_block( + let (h1, meta1, _) = st.generate_next_block( &dfvk, AddressType::DefaultExternal, Amount::from_u64(5).unwrap(), ); - let (h2, meta2, _) = test.generate_next_block( + let (h2, meta2, _) = st.generate_next_block( &dfvk, AddressType::DefaultExternal, Amount::from_u64(10).unwrap(), ); // The BlockMeta DB is not updated until we do so explicitly. - assert_eq!(test.cache().get_max_cached_height().unwrap(), None); + assert_eq!(st.cache().get_max_cached_height().unwrap(), None); // Inform the BlockMeta DB about the newly-persisted CompactBlocks. - test.cache().write_block_metadata(&[meta1, meta2]).unwrap(); + st.cache().write_block_metadata(&[meta1, meta2]).unwrap(); // The BlockMeta DB now sees blocks up to height 2. - assert_eq!(test.cache().get_max_cached_height().unwrap(), Some(h2),); - assert_eq!(test.cache().find_block(h1).unwrap(), Some(meta1)); - assert_eq!(test.cache().find_block(h2).unwrap(), Some(meta2)); - assert_eq!(test.cache().find_block(h2 + 1).unwrap(), None); + assert_eq!(st.cache().get_max_cached_height().unwrap(), Some(h2),); + assert_eq!(st.cache().find_block(h1).unwrap(), Some(meta1)); + assert_eq!(st.cache().find_block(h2).unwrap(), Some(meta2)); + assert_eq!(st.cache().find_block(h2 + 1).unwrap(), None); // Rewinding to height 1 should cause the metadata for height 2 to be deleted. - test.cache().truncate_to_height(h1).unwrap(); - assert_eq!(test.cache().get_max_cached_height().unwrap(), Some(h1)); - assert_eq!(test.cache().find_block(h1).unwrap(), Some(meta1)); - assert_eq!(test.cache().find_block(h2).unwrap(), None); - assert_eq!(test.cache().find_block(h2 + 1).unwrap(), None); + st.cache().truncate_to_height(h1).unwrap(); + assert_eq!(st.cache().get_max_cached_height().unwrap(), Some(h1)); + assert_eq!(st.cache().find_block(h1).unwrap(), Some(meta1)); + assert_eq!(st.cache().find_block(h2).unwrap(), None); + assert_eq!(st.cache().find_block(h2 + 1).unwrap(), None); } } diff --git a/zcash_client_sqlite/src/testing.rs b/zcash_client_sqlite/src/testing.rs index db6a135fb..6c4e53942 100644 --- a/zcash_client_sqlite/src/testing.rs +++ b/zcash_client_sqlite/src/testing.rs @@ -129,8 +129,8 @@ impl TestBuilder { self } - /// Builds the runner for this test. - pub(crate) fn build(self) -> TestRunner { + /// Builds the state for this test. + pub(crate) fn build(self) -> TestState { let params = network(); let data_file = NamedTempFile::new().unwrap(); @@ -144,7 +144,7 @@ impl TestBuilder { None }; - TestRunner { + TestState { params, cache: self.cache, latest_cached_block: None, @@ -155,8 +155,8 @@ impl TestBuilder { } } -/// A `zcash_client_sqlite` test runner. -pub(crate) struct TestRunner { +/// The state for a `zcash_client_sqlite` test. +pub(crate) struct TestState { params: Network, cache: Cache, latest_cached_block: Option<(BlockHeight, BlockHash, u32)>, @@ -165,7 +165,7 @@ pub(crate) struct TestRunner { test_account: Option<(UnifiedFullViewingKey, Option)>, } -impl TestRunner +impl TestState where ::Error: fmt::Debug, { @@ -297,7 +297,7 @@ where } } -impl TestRunner { +impl TestState { /// Exposes an immutable reference to the test's [`WalletDb`]. pub(crate) fn wallet(&self) -> &WalletDb { &self.db_data diff --git a/zcash_client_sqlite/src/wallet.rs b/zcash_client_sqlite/src/wallet.rs index d2fdbc639..0434c2c6b 100644 --- a/zcash_client_sqlite/src/wallet.rs +++ b/zcash_client_sqlite/src/wallet.rs @@ -1594,20 +1594,20 @@ mod tests { #[test] fn empty_database_has_no_balance() { - let test = TestBuilder::new() + let st = TestBuilder::new() .with_seed(Secret::new(vec![])) .with_test_account() .build(); // The account should be empty assert_eq!( - get_balance(&test.wallet().conn, AccountId::from(0)).unwrap(), + get_balance(&st.wallet().conn, AccountId::from(0)).unwrap(), Amount::zero() ); // We can't get an anchor height, as we have not scanned any blocks. assert_eq!( - test.wallet() + st.wallet() .get_target_and_anchor_heights(NonZeroU32::new(10).unwrap()) .unwrap(), None @@ -1615,11 +1615,11 @@ mod tests { // An invalid account has zero balance assert_matches!( - test.wallet().get_current_address(AccountId::from(1)), + st.wallet().get_current_address(AccountId::from(1)), Ok(None) ); assert_eq!( - get_balance(&test.wallet().conn, AccountId::from(0)).unwrap(), + get_balance(&st.wallet().conn, AccountId::from(0)).unwrap(), Amount::zero() ); } @@ -1627,19 +1627,19 @@ mod tests { #[test] #[cfg(feature = "transparent-inputs")] fn put_received_transparent_utxo() { - let mut test = TestBuilder::new().build(); + let mut st = TestBuilder::new().build(); // Add an account to the wallet let seed = Secret::new([0u8; 32].to_vec()); - let (account_id, _usk) = test.wallet_mut().create_account(&seed).unwrap(); - let uaddr = test + let (account_id, _usk) = st.wallet_mut().create_account(&seed).unwrap(); + let uaddr = st .wallet() .get_current_address(account_id) .unwrap() .unwrap(); let taddr = uaddr.transparent().unwrap(); - let bal_absent = test + let bal_absent = st .wallet() .get_transparent_balances(account_id, BlockHeight::from_u32(12345)) .unwrap(); @@ -1655,7 +1655,7 @@ mod tests { ) .unwrap(); - let res0 = test.wallet_mut().put_received_transparent_utxo(&utxo); + let res0 = st.wallet_mut().put_received_transparent_utxo(&utxo); assert_matches!(res0, Ok(_)); // Change the mined height of the UTXO and upsert; we should get back @@ -1669,11 +1669,11 @@ mod tests { BlockHeight::from_u32(34567), ) .unwrap(); - let res1 = test.wallet_mut().put_received_transparent_utxo(&utxo2); + let res1 = st.wallet_mut().put_received_transparent_utxo(&utxo2); assert_matches!(res1, Ok(id) if id == res0.unwrap()); assert_matches!( - test.wallet().get_unspent_transparent_outputs( + st.wallet().get_unspent_transparent_outputs( taddr, BlockHeight::from_u32(12345), &[] @@ -1682,7 +1682,7 @@ mod tests { ); assert_matches!( - test.wallet().get_unspent_transparent_outputs( + st.wallet().get_unspent_transparent_outputs( taddr, BlockHeight::from_u32(34567), &[] @@ -1694,21 +1694,21 @@ mod tests { ); assert_matches!( - test.wallet().get_transparent_balances(account_id, BlockHeight::from_u32(34567)), + st.wallet().get_transparent_balances(account_id, BlockHeight::from_u32(34567)), Ok(h) if h.get(taddr) == Amount::from_u64(100000).ok().as_ref() ); // Artificially delete the address from the addresses table so that // we can ensure the update fails if the join doesn't work. - test.wallet() + st.wallet() .conn .execute( "DELETE FROM addresses WHERE cached_transparent_receiver_address = ?", - [Some(taddr.encode(&test.wallet().params))], + [Some(taddr.encode(&st.wallet().params))], ) .unwrap(); - let res2 = test.wallet_mut().put_received_transparent_utxo(&utxo2); + let res2 = st.wallet_mut().put_received_transparent_utxo(&utxo2); assert_matches!(res2, Err(_)); } } diff --git a/zcash_client_sqlite/src/wallet/sapling.rs b/zcash_client_sqlite/src/wallet/sapling.rs index 8d95fcaea..edf4ac205 100644 --- a/zcash_client_sqlite/src/wallet/sapling.rs +++ b/zcash_client_sqlite/src/wallet/sapling.rs @@ -439,7 +439,7 @@ pub(crate) mod tests { use crate::{ error::SqliteClientError, - testing::{self, network, AddressType, BlockCache, TestBuilder, TestRunner}, + testing::{self, network, AddressType, BlockCache, TestBuilder, TestState}, wallet::{commitment_tree, get_balance, get_balance_at}, AccountId, NoteId, ReceivedNoteId, }; @@ -461,30 +461,30 @@ pub(crate) mod tests { #[test] fn send_proposed_transfer() { - let mut test = TestBuilder::new().with_block_cache().build(); + let mut st = TestBuilder::new().with_block_cache().build(); // Add an account to the wallet let seed = Secret::new([0u8; 32].to_vec()); - let (account, usk) = test.wallet_mut().create_account(&seed).unwrap(); + let (account, usk) = st.wallet_mut().create_account(&seed).unwrap(); let dfvk = usk.sapling().to_diversifiable_full_viewing_key(); // Add funds to the wallet in a single note let value = Amount::from_u64(60000).unwrap(); - let (h, _, _) = test.generate_next_block(&dfvk, AddressType::DefaultExternal, value); - test.scan_cached_blocks(h, 1); + let (h, _, _) = st.generate_next_block(&dfvk, AddressType::DefaultExternal, value); + st.scan_cached_blocks(h, 1); // Verified balance matches total balance - let (_, anchor_height) = test + let (_, anchor_height) = st .wallet() .get_target_and_anchor_heights(NonZeroU32::new(1).unwrap()) .unwrap() .unwrap(); assert_eq!( - get_balance(&test.wallet().conn, AccountId::from(0)).unwrap(), + get_balance(&st.wallet().conn, AccountId::from(0)).unwrap(), value ); assert_eq!( - get_balance_at(&test.wallet().conn, AccountId::from(0), anchor_height).unwrap(), + get_balance_at(&st.wallet().conn, AccountId::from(0), anchor_height).unwrap(), value ); @@ -504,7 +504,7 @@ pub(crate) mod tests { let change_strategy = fixed::SingleOutputChangeStrategy::new(fee_rule); let input_selector = &GreedyInputSelector::new(change_strategy, DustOutputPolicy::default()); - let proposal_result = test.propose_transfer( + let proposal_result = st.propose_transfer( account, input_selector, request, @@ -513,7 +513,7 @@ pub(crate) mod tests { assert_matches!(proposal_result, Ok(_)); let change_memo = "Test change memo".parse::().unwrap(); - let create_proposed_result = test.create_proposed_transaction( + let create_proposed_result = st.create_proposed_transaction( &usk, OvkPolicy::Sender, proposal_result.unwrap(), @@ -525,7 +525,7 @@ pub(crate) mod tests { let sent_tx_id = create_proposed_result.unwrap(); // Verify that the sent transaction was stored and that we can decrypt the memos - let tx = test + let tx = st .wallet() .get_transaction(sent_tx_id) .expect("Created transaction was stored."); @@ -549,7 +549,7 @@ pub(crate) mod tests { assert!(found_tx_empty_memo); // Verify that the stored sent notes match what we're expecting - let mut stmt_sent_notes = test + let mut stmt_sent_notes = st .wallet() .conn .prepare( @@ -580,7 +580,7 @@ pub(crate) mod tests { let mut found_sent_change_memo = false; let mut found_sent_empty_memo = false; for sent_note_id in sent_note_ids { - match test + match st .wallet() .get_memo(sent_note_id) .expect("Note id is valid") @@ -601,7 +601,7 @@ pub(crate) mod tests { // Check that querying for a nonexistent sent note returns None assert_matches!( - test.wallet() + st.wallet() .get_memo(NoteId::new(sent_tx_id, ShieldedProtocol::Sapling, 12345)), Ok(None) ); @@ -609,11 +609,11 @@ pub(crate) mod tests { #[test] fn create_to_address_fails_on_incorrect_usk() { - let mut test = TestBuilder::new().with_seed(Secret::new(vec![])).build(); + let mut st = TestBuilder::new().with_seed(Secret::new(vec![])).build(); // Add an account to the wallet let seed = Secret::new([0u8; 32].to_vec()); - let (_, usk) = test.wallet_mut().create_account(&seed).unwrap(); + let (_, usk) = st.wallet_mut().create_account(&seed).unwrap(); let dfvk = usk.sapling().to_diversifiable_full_viewing_key(); let to = dfvk.default_address().1.into(); @@ -623,7 +623,7 @@ pub(crate) mod tests { // Attempting to spend with a USK that is not in the wallet results in an error assert_matches!( - test.create_spend_to_address( + st.create_spend_to_address( &usk1, &to, Amount::from_u64(1).unwrap(), @@ -637,23 +637,23 @@ pub(crate) mod tests { #[test] fn create_to_address_fails_with_no_blocks() { - let mut test = TestBuilder::new().build(); + let mut st = TestBuilder::new().build(); // Add an account to the wallet let seed = Secret::new([0u8; 32].to_vec()); - let (_, usk) = test.wallet_mut().create_account(&seed).unwrap(); + let (_, usk) = st.wallet_mut().create_account(&seed).unwrap(); let dfvk = usk.sapling().to_diversifiable_full_viewing_key(); let to = dfvk.default_address().1.into(); // Account balance should be zero assert_eq!( - get_balance(&test.wallet().conn, AccountId::from(0)).unwrap(), + get_balance(&st.wallet().conn, AccountId::from(0)).unwrap(), Amount::zero() ); // We cannot do anything if we aren't synchronised assert_matches!( - test.create_spend_to_address( + st.create_spend_to_address( &usk, &to, Amount::from_u64(1).unwrap(), @@ -667,49 +667,49 @@ pub(crate) mod tests { #[test] fn create_to_address_fails_on_unverified_notes() { - let mut test = TestBuilder::new().with_block_cache().build(); + let mut st = TestBuilder::new().with_block_cache().build(); // Add an account to the wallet let seed = Secret::new([0u8; 32].to_vec()); - let (_, usk) = test.wallet_mut().create_account(&seed).unwrap(); + let (_, usk) = st.wallet_mut().create_account(&seed).unwrap(); let dfvk = usk.sapling().to_diversifiable_full_viewing_key(); // Add funds to the wallet in a single note let value = Amount::from_u64(50000).unwrap(); - let (h1, _, _) = test.generate_next_block(&dfvk, AddressType::DefaultExternal, value); - test.scan_cached_blocks(h1, 1); + let (h1, _, _) = st.generate_next_block(&dfvk, AddressType::DefaultExternal, value); + st.scan_cached_blocks(h1, 1); // Verified balance matches total balance - let (_, anchor_height) = test + let (_, anchor_height) = st .wallet() .get_target_and_anchor_heights(NonZeroU32::new(10).unwrap()) .unwrap() .unwrap(); assert_eq!( - get_balance(&test.wallet().conn, AccountId::from(0)).unwrap(), + get_balance(&st.wallet().conn, AccountId::from(0)).unwrap(), value ); assert_eq!( - get_balance_at(&test.wallet().conn, AccountId::from(0), anchor_height).unwrap(), + get_balance_at(&st.wallet().conn, AccountId::from(0), anchor_height).unwrap(), value ); // Add more funds to the wallet in a second note - let (h2, _, _) = test.generate_next_block(&dfvk, AddressType::DefaultExternal, value); - test.scan_cached_blocks(h2, 1); + let (h2, _, _) = st.generate_next_block(&dfvk, AddressType::DefaultExternal, value); + st.scan_cached_blocks(h2, 1); // Verified balance does not include the second note - let (_, anchor_height2) = test + let (_, anchor_height2) = st .wallet() .get_target_and_anchor_heights(NonZeroU32::new(10).unwrap()) .unwrap() .unwrap(); assert_eq!( - get_balance(&test.wallet().conn, AccountId::from(0)).unwrap(), + get_balance(&st.wallet().conn, AccountId::from(0)).unwrap(), (value + value).unwrap() ); assert_eq!( - get_balance_at(&test.wallet().conn, AccountId::from(0), anchor_height2).unwrap(), + get_balance_at(&st.wallet().conn, AccountId::from(0), anchor_height2).unwrap(), value ); @@ -717,7 +717,7 @@ pub(crate) mod tests { let extsk2 = ExtendedSpendingKey::master(&[]); let to = extsk2.default_address().1.into(); assert_matches!( - test.create_spend_to_address( + st.create_spend_to_address( &usk, &to, Amount::from_u64(70000).unwrap(), @@ -736,13 +736,13 @@ pub(crate) mod tests { // Mine blocks SAPLING_ACTIVATION_HEIGHT + 2 to 9 until just before the second // note is verified for _ in 2..10 { - test.generate_next_block(&dfvk, AddressType::DefaultExternal, value); + st.generate_next_block(&dfvk, AddressType::DefaultExternal, value); } - test.scan_cached_blocks(h2 + 1, 8); + st.scan_cached_blocks(h2 + 1, 8); // Second spend still fails assert_matches!( - test.create_spend_to_address( + st.create_spend_to_address( &usk, &to, Amount::from_u64(70000).unwrap(), @@ -759,12 +759,12 @@ pub(crate) mod tests { ); // Mine block 11 so that the second note becomes verified - let (h11, _, _) = test.generate_next_block(&dfvk, AddressType::DefaultExternal, value); - test.scan_cached_blocks(h11, 1); + let (h11, _, _) = st.generate_next_block(&dfvk, AddressType::DefaultExternal, value); + st.scan_cached_blocks(h11, 1); // Second spend should now succeed assert_matches!( - test.create_spend_to_address( + st.create_spend_to_address( &usk, &to, Amount::from_u64(70000).unwrap(), @@ -778,22 +778,22 @@ pub(crate) mod tests { #[test] fn create_to_address_fails_on_locked_notes() { - let mut test = TestBuilder::new() + let mut st = TestBuilder::new() .with_block_cache() .with_seed(Secret::new(vec![])) .build(); // Add an account to the wallet let seed = Secret::new([0u8; 32].to_vec()); - let (_, usk) = test.wallet_mut().create_account(&seed).unwrap(); + let (_, usk) = st.wallet_mut().create_account(&seed).unwrap(); let dfvk = usk.sapling().to_diversifiable_full_viewing_key(); // Add funds to the wallet in a single note let value = Amount::from_u64(50000).unwrap(); - let (h1, _, _) = test.generate_next_block(&dfvk, AddressType::DefaultExternal, value); - test.scan_cached_blocks(h1, 1); + let (h1, _, _) = st.generate_next_block(&dfvk, AddressType::DefaultExternal, value); + st.scan_cached_blocks(h1, 1); assert_eq!( - get_balance(&test.wallet().conn, AccountId::from(0)).unwrap(), + get_balance(&st.wallet().conn, AccountId::from(0)).unwrap(), value ); @@ -801,7 +801,7 @@ pub(crate) mod tests { let extsk2 = ExtendedSpendingKey::master(&[]); let to = extsk2.default_address().1.into(); assert_matches!( - test.create_spend_to_address( + st.create_spend_to_address( &usk, &to, Amount::from_u64(15000).unwrap(), @@ -814,7 +814,7 @@ pub(crate) mod tests { // A second spend fails because there are no usable notes assert_matches!( - test.create_spend_to_address( + st.create_spend_to_address( &usk, &to, Amount::from_u64(2000).unwrap(), @@ -832,17 +832,17 @@ pub(crate) mod tests { // Mine blocks SAPLING_ACTIVATION_HEIGHT + 1 to 41 (that don't send us funds) // until just before the first transaction expires for i in 1..42 { - test.generate_next_block( + st.generate_next_block( &ExtendedSpendingKey::master(&[i as u8]).to_diversifiable_full_viewing_key(), AddressType::DefaultExternal, value, ); } - test.scan_cached_blocks(h1 + 1, 41); + st.scan_cached_blocks(h1 + 1, 41); // Second spend still fails assert_matches!( - test.create_spend_to_address( + st.create_spend_to_address( &usk, &to, Amount::from_u64(2000).unwrap(), @@ -858,15 +858,15 @@ pub(crate) mod tests { ); // Mine block SAPLING_ACTIVATION_HEIGHT + 42 so that the first transaction expires - let (h43, _, _) = test.generate_next_block( + let (h43, _, _) = st.generate_next_block( &ExtendedSpendingKey::master(&[42]).to_diversifiable_full_viewing_key(), AddressType::DefaultExternal, value, ); - test.scan_cached_blocks(h43, 1); + st.scan_cached_blocks(h43, 1); // Second spend should now succeed - test.create_spend_to_address( + st.create_spend_to_address( &usk, &to, Amount::from_u64(2000).unwrap(), @@ -879,19 +879,19 @@ pub(crate) mod tests { #[test] fn ovk_policy_prevents_recovery_from_chain() { - let mut test = TestBuilder::new().with_block_cache().build(); + let mut st = TestBuilder::new().with_block_cache().build(); // Add an account to the wallet let seed = Secret::new([0u8; 32].to_vec()); - let (_, usk) = test.wallet_mut().create_account(&seed).unwrap(); + let (_, usk) = st.wallet_mut().create_account(&seed).unwrap(); let dfvk = usk.sapling().to_diversifiable_full_viewing_key(); // Add funds to the wallet in a single note let value = Amount::from_u64(50000).unwrap(); - let (h1, _, _) = test.generate_next_block(&dfvk, AddressType::DefaultExternal, value); - test.scan_cached_blocks(h1, 1); + let (h1, _, _) = st.generate_next_block(&dfvk, AddressType::DefaultExternal, value); + st.scan_cached_blocks(h1, 1); assert_eq!( - get_balance(&test.wallet().conn, AccountId::from(0)).unwrap(), + get_balance(&st.wallet().conn, AccountId::from(0)).unwrap(), value ); @@ -900,7 +900,7 @@ pub(crate) mod tests { let to = addr2.into(); #[allow(clippy::type_complexity)] - let send_and_recover_with_policy = |test: &mut TestRunner, + let send_and_recover_with_policy = |test: &mut TestState, ovk_policy| -> Result< Option<(Note, PaymentAddress, MemoBytes)>, @@ -954,61 +954,61 @@ pub(crate) mod tests { // Send some of the funds to another address, keeping history. // The recipient output is decryptable by the sender. assert_matches!( - send_and_recover_with_policy(&mut test, OvkPolicy::Sender), + send_and_recover_with_policy(&mut st, OvkPolicy::Sender), Ok(Some((_, recovered_to, _))) if recovered_to == addr2 ); // Mine blocks SAPLING_ACTIVATION_HEIGHT + 1 to 42 (that don't send us funds) // so that the first transaction expires for i in 1..=42 { - test.generate_next_block( + st.generate_next_block( &ExtendedSpendingKey::master(&[i as u8]).to_diversifiable_full_viewing_key(), AddressType::DefaultExternal, value, ); } - test.scan_cached_blocks(h1 + 1, 42); + st.scan_cached_blocks(h1 + 1, 42); // Send the funds again, discarding history. // Neither transaction output is decryptable by the sender. assert_matches!( - send_and_recover_with_policy(&mut test, OvkPolicy::Discard), + send_and_recover_with_policy(&mut st, OvkPolicy::Discard), Ok(None) ); } #[test] fn create_to_address_succeeds_to_t_addr_zero_change() { - let mut test = TestBuilder::new().with_block_cache().build(); + let mut st = TestBuilder::new().with_block_cache().build(); // Add an account to the wallet let seed = Secret::new([0u8; 32].to_vec()); - let (_, usk) = test.wallet_mut().create_account(&seed).unwrap(); + let (_, usk) = st.wallet_mut().create_account(&seed).unwrap(); let dfvk = usk.sapling().to_diversifiable_full_viewing_key(); // Add funds to the wallet in a single note let value = Amount::from_u64(60000).unwrap(); - let (h, _, _) = test.generate_next_block(&dfvk, AddressType::DefaultExternal, value); - test.scan_cached_blocks(h, 1); + let (h, _, _) = st.generate_next_block(&dfvk, AddressType::DefaultExternal, value); + st.scan_cached_blocks(h, 1); // Verified balance matches total balance - let (_, anchor_height) = test + let (_, anchor_height) = st .wallet() .get_target_and_anchor_heights(NonZeroU32::new(1).unwrap()) .unwrap() .unwrap(); assert_eq!( - get_balance(&test.wallet().conn, AccountId::from(0)).unwrap(), + get_balance(&st.wallet().conn, AccountId::from(0)).unwrap(), value ); assert_eq!( - get_balance_at(&test.wallet().conn, AccountId::from(0), anchor_height).unwrap(), + get_balance_at(&st.wallet().conn, AccountId::from(0), anchor_height).unwrap(), value ); let to = TransparentAddress::PublicKey([7; 20]).into(); assert_matches!( - test.create_spend_to_address( + st.create_spend_to_address( &usk, &to, Amount::from_u64(50000).unwrap(), @@ -1022,36 +1022,36 @@ pub(crate) mod tests { #[test] fn create_to_address_spends_a_change_note() { - let mut test = TestBuilder::new().with_block_cache().build(); + let mut st = TestBuilder::new().with_block_cache().build(); // Add an account to the wallet let seed = Secret::new([0u8; 32].to_vec()); - let (_, usk) = test.wallet_mut().create_account(&seed).unwrap(); + let (_, usk) = st.wallet_mut().create_account(&seed).unwrap(); let dfvk = usk.sapling().to_diversifiable_full_viewing_key(); // Add funds to the wallet in a single note let value = Amount::from_u64(60000).unwrap(); - let (h, _, _) = test.generate_next_block(&dfvk, AddressType::Internal, value); - test.scan_cached_blocks(h, 1); + let (h, _, _) = st.generate_next_block(&dfvk, AddressType::Internal, value); + st.scan_cached_blocks(h, 1); // Verified balance matches total balance - let (_, anchor_height) = test + let (_, anchor_height) = st .wallet() .get_target_and_anchor_heights(NonZeroU32::new(10).unwrap()) .unwrap() .unwrap(); assert_eq!( - get_balance(&test.wallet().conn, AccountId::from(0)).unwrap(), + get_balance(&st.wallet().conn, AccountId::from(0)).unwrap(), value ); assert_eq!( - get_balance_at(&test.wallet().conn, AccountId::from(0), anchor_height).unwrap(), + get_balance_at(&st.wallet().conn, AccountId::from(0), anchor_height).unwrap(), value ); let to = TransparentAddress::PublicKey([7; 20]).into(); assert_matches!( - test.create_spend_to_address( + st.create_spend_to_address( &usk, &to, Amount::from_u64(50000).unwrap(), @@ -1065,15 +1065,15 @@ pub(crate) mod tests { #[test] fn zip317_spend() { - let mut test = TestBuilder::new().with_block_cache().build(); + let mut st = TestBuilder::new().with_block_cache().build(); // Add an account to the wallet let seed = Secret::new([0u8; 32].to_vec()); - let (_, usk) = test.wallet_mut().create_account(&seed).unwrap(); + let (_, usk) = st.wallet_mut().create_account(&seed).unwrap(); let dfvk = usk.sapling().to_diversifiable_full_viewing_key(); // Add funds to the wallet - let (h1, _, _) = test.generate_next_block( + let (h1, _, _) = st.generate_next_block( &dfvk, AddressType::Internal, Amount::from_u64(50000).unwrap(), @@ -1081,28 +1081,28 @@ pub(crate) mod tests { // Add 10 dust notes to the wallet for _ in 1..=10 { - test.generate_next_block( + st.generate_next_block( &dfvk, AddressType::DefaultExternal, Amount::from_u64(1000).unwrap(), ); } - test.scan_cached_blocks(h1, 11); + st.scan_cached_blocks(h1, 11); // Verified balance matches total balance let total = Amount::from_u64(60000).unwrap(); - let (_, anchor_height) = test + let (_, anchor_height) = st .wallet() .get_target_and_anchor_heights(NonZeroU32::new(1).unwrap()) .unwrap() .unwrap(); assert_eq!( - get_balance(&test.wallet().conn, AccountId::from(0)).unwrap(), + get_balance(&st.wallet().conn, AccountId::from(0)).unwrap(), total ); assert_eq!( - get_balance_at(&test.wallet().conn, AccountId::from(0), anchor_height).unwrap(), + get_balance_at(&st.wallet().conn, AccountId::from(0), anchor_height).unwrap(), total ); @@ -1123,7 +1123,7 @@ pub(crate) mod tests { .unwrap(); assert_matches!( - test.spend( + st.spend( &input_selector, &usk, req, @@ -1148,7 +1148,7 @@ pub(crate) mod tests { .unwrap(); assert_matches!( - test.spend( + st.spend( &input_selector, &usk, req, @@ -1162,13 +1162,13 @@ pub(crate) mod tests { #[test] #[cfg(feature = "transparent-inputs")] fn shield_transparent() { - let mut test = TestBuilder::new().with_block_cache().build(); + let mut st = TestBuilder::new().with_block_cache().build(); // Add an account to the wallet let seed = Secret::new([0u8; 32].to_vec()); - let (account_id, usk) = test.wallet_mut().create_account(&seed).unwrap(); + let (account_id, usk) = st.wallet_mut().create_account(&seed).unwrap(); let dfvk = usk.sapling().to_diversifiable_full_viewing_key(); - let uaddr = test + let uaddr = st .wallet() .get_current_address(account_id) .unwrap() @@ -1176,12 +1176,12 @@ pub(crate) mod tests { let taddr = uaddr.transparent().unwrap(); // Ensure that the wallet has at least one block - let (h, _, _) = test.generate_next_block( + let (h, _, _) = st.generate_next_block( &dfvk, AddressType::Internal, Amount::from_u64(50000).unwrap(), ); - test.scan_cached_blocks(h, 1); + st.scan_cached_blocks(h, 1); let utxo = WalletTransparentOutput::from_parts( OutPoint::new([1u8; 32], 1), @@ -1193,7 +1193,7 @@ pub(crate) mod tests { ) .unwrap(); - let res0 = test.wallet_mut().put_received_transparent_utxo(&utxo); + let res0 = st.wallet_mut().put_received_transparent_utxo(&utxo); assert!(matches!(res0, Ok(_))); let input_selector = GreedyInputSelector::new( @@ -1202,7 +1202,7 @@ pub(crate) mod tests { ); assert_matches!( - test.shield_transparent_funds( + st.shield_transparent_funds( &input_selector, NonNegativeAmount::from_u64(10000).unwrap(), &usk, diff --git a/zcash_client_sqlite/src/wallet/scanning.rs b/zcash_client_sqlite/src/wallet/scanning.rs index 8bfb8c1ce..25a1be909 100644 --- a/zcash_client_sqlite/src/wallet/scanning.rs +++ b/zcash_client_sqlite/src/wallet/scanning.rs @@ -1083,18 +1083,18 @@ mod tests { fn scan_complete() { use ScanPriority::*; - let mut test = TestBuilder::new() + let mut st = TestBuilder::new() .with_block_cache() .with_seed(Secret::new(vec![])) .with_test_account() .build(); - let dfvk = test.test_account_sapling().unwrap(); + let dfvk = st.test_account_sapling().unwrap(); assert_matches!( // In the following, we don't care what the root hashes are, they just need to be // distinct. - test.wallet_mut().put_sapling_subtree_roots( + st.wallet_mut().put_sapling_subtree_roots( 0, &[ CommitmentTreeRoot::from_parts( @@ -1121,7 +1121,7 @@ mod tests { let initial_height = sapling_activation_height() + 310; let value = Amount::from_u64(50000).unwrap(); - test.generate_block_at( + st.generate_block_at( initial_height, BlockHash([0; 32]), &dfvk, @@ -1131,19 +1131,19 @@ mod tests { ); for _ in 1..=10 { - test.generate_next_block( + st.generate_next_block( &dfvk, AddressType::DefaultExternal, Amount::from_u64(10000).unwrap(), ); } - test.scan_cached_blocks(initial_height, 10); + st.scan_cached_blocks(initial_height, 10); // Verify the that adjacent range needed to make the note spendable has been prioritized. let sap_active = u32::from(sapling_activation_height()); assert_matches!( - test.wallet().suggest_scan_ranges(), + st.wallet().suggest_scan_ranges(), Ok(scan_ranges) if scan_ranges == vec![ scan_range((sap_active + 300)..(sap_active + 310), FoundNote) ] @@ -1151,7 +1151,7 @@ mod tests { // Check that the scanned range has been properly persisted. assert_matches!( - suggest_scan_ranges(&test.wallet().conn, Scanned), + suggest_scan_ranges(&st.wallet().conn, Scanned), Ok(scan_ranges) if scan_ranges == vec![ scan_range((sap_active + 300)..(sap_active + 310), FoundNote), scan_range((sap_active + 310)..(sap_active + 320), Scanned) @@ -1161,7 +1161,7 @@ mod tests { // Simulate the wallet going offline for a bit, update the chain tip to 20 blocks in the // future. assert_matches!( - test.wallet_mut() + st.wallet_mut() .update_chain_tip(sapling_activation_height() + 340), Ok(()) ); @@ -1169,7 +1169,7 @@ mod tests { // Check the scan range again, we should see a `ChainTip` range for the period we've been // offline. assert_matches!( - test.wallet().suggest_scan_ranges(), + st.wallet().suggest_scan_ranges(), Ok(scan_ranges) if scan_ranges == vec![ scan_range((sap_active + 320)..(sap_active + 341), ChainTip), scan_range((sap_active + 300)..(sap_active + 310), ChainTip) @@ -1178,7 +1178,7 @@ mod tests { // Now simulate a jump ahead more than 100 blocks. assert_matches!( - test.wallet_mut() + st.wallet_mut() .update_chain_tip(sapling_activation_height() + 450), Ok(()) ); @@ -1186,7 +1186,7 @@ mod tests { // Check the scan range again, we should see a `Validate` range for the previous wallet // tip, and then a `ChainTip` for the remaining range. assert_matches!( - test.wallet().suggest_scan_ranges(), + st.wallet().suggest_scan_ranges(), Ok(scan_ranges) if scan_ranges == vec![ scan_range((sap_active + 319)..(sap_active + 329), Verify), scan_range((sap_active + 329)..(sap_active + 451), ChainTip), @@ -1199,22 +1199,22 @@ mod tests { fn init_blocks_table_creates_ignored_range() { use ScanPriority::*; - let mut test = TestBuilder::new().with_seed(Secret::new(vec![])).build(); + let mut st = TestBuilder::new().with_seed(Secret::new(vec![])).build(); - let sap_active = test + let sap_active = st .wallet() .params .activation_height(NetworkUpgrade::Sapling) .unwrap(); // Initialise the blocks table. We use Canopy activation as an arbitrary birthday height // that's greater than Sapling activation. - let birthday_height = test + let birthday_height = st .wallet() .params .activation_height(NetworkUpgrade::Canopy) .unwrap(); init_blocks_table( - test.wallet_mut(), + st.wallet_mut(), birthday_height, BlockHash([1; 32]), 1, @@ -1230,12 +1230,12 @@ mod tests { ), ]; assert_matches!( - suggest_scan_ranges(&test.wallet().conn, Ignored), + suggest_scan_ranges(&st.wallet().conn, Ignored), Ok(scan_ranges) if scan_ranges == expected ); // Set up some shard history - test.wallet_mut() + st.wallet_mut() .put_sapling_subtree_roots( 0, &[ @@ -1254,12 +1254,12 @@ mod tests { .unwrap(); // Update the chain tip - let tip_height = test + let tip_height = st .wallet() .params .activation_height(NetworkUpgrade::Nu5) .unwrap(); - test.wallet_mut().update_chain_tip(tip_height).unwrap(); + st.wallet_mut().update_chain_tip(tip_height).unwrap(); // Verify that the suggested scan ranges match what is expected let expected = vec![ @@ -1290,7 +1290,7 @@ mod tests { ]; assert_matches!( - suggest_scan_ranges(&test.wallet().conn, Ignored), + suggest_scan_ranges(&st.wallet().conn, Ignored), Ok(scan_ranges) if scan_ranges == expected ); } From 8af74de93125fd6edc45f314786fb6a58594810d Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Mon, 28 Aug 2023 18:58:37 +0000 Subject: [PATCH 5/5] `zcash_client_sqlite`: Add `TestState::propose_shielding` Currently unused (causing an expected lint), but will be in an upcoming PR. --- zcash_client_sqlite/src/testing.rs | 34 +++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/zcash_client_sqlite/src/testing.rs b/zcash_client_sqlite/src/testing.rs index 6c4e53942..84eb383ed 100644 --- a/zcash_client_sqlite/src/testing.rs +++ b/zcash_client_sqlite/src/testing.rs @@ -55,7 +55,7 @@ use zcash_primitives::{ }; #[cfg(feature = "transparent-inputs")] -use zcash_client_backend::data_api::wallet::shield_transparent_funds; +use zcash_client_backend::data_api::wallet::{propose_shielding, shield_transparent_funds}; #[cfg(feature = "transparent-inputs")] use zcash_primitives::{ legacy, legacy::keys::IncomingViewingKey, transaction::components::amount::NonNegativeAmount, @@ -422,6 +422,38 @@ impl TestState { ) } + /// Invokes [`propose_shielding`] with the given arguments. + #[cfg(feature = "transparent-inputs")] + #[allow(clippy::type_complexity)] + pub(crate) fn propose_shielding( + &mut self, + input_selector: &InputsT, + shielding_threshold: NonNegativeAmount, + from_addrs: &[TransparentAddress], + min_confirmations: NonZeroU32, + ) -> Result< + Proposal, + data_api::error::Error< + SqliteClientError, + Infallible, + InputsT::Error, + ::Error, + ReceivedNoteId, + >, + > + where + InputsT: InputSelector>, + { + propose_shielding::<_, _, _, Infallible>( + &mut self.db_data, + &self.params, + input_selector, + shielding_threshold, + from_addrs, + min_confirmations, + ) + } + /// Invokes [`create_proposed_transaction`] with the given arguments. pub(crate) fn create_proposed_transaction( &mut self,