zcash_client_sqlite: Generalise `wallet::scanning` tests

This commit is contained in:
Jack Grigg 2024-03-09 15:14:35 +00:00
parent 399ae762b7
commit 9d40588a50
3 changed files with 111 additions and 76 deletions

View File

@ -4,6 +4,7 @@
use std::{convert::Infallible, num::NonZeroU32}; use std::{convert::Infallible, num::NonZeroU32};
use incrementalmerkletree::Level;
use rusqlite::params; use rusqlite::params;
use secrecy::Secret; use secrecy::Secret;
use shardtree::error::ShardTreeError; use shardtree::error::ShardTreeError;
@ -29,7 +30,7 @@ use zcash_client_backend::{
chain::CommitmentTreeRoot, chain::CommitmentTreeRoot,
error::Error, error::Error,
wallet::input_selection::{GreedyInputSelector, GreedyInputSelectorError}, wallet::input_selection::{GreedyInputSelector, GreedyInputSelectorError},
AccountBirthday, DecryptedTransaction, Ratio, WalletRead, WalletWrite, AccountBirthday, DecryptedTransaction, Ratio, WalletRead, WalletSummary, WalletWrite,
}, },
decrypt_transaction, decrypt_transaction,
fees::{fixed, standard, DustOutputPolicy}, fees::{fixed, standard, DustOutputPolicy},
@ -87,6 +88,7 @@ pub(crate) trait ShieldedPoolTester {
fn fvks_equal(a: &Self::Fvk, b: &Self::Fvk) -> bool; fn fvks_equal(a: &Self::Fvk, b: &Self::Fvk) -> bool;
fn empty_tree_leaf() -> Self::MerkleTreeHash; fn empty_tree_leaf() -> Self::MerkleTreeHash;
fn empty_tree_root(level: Level) -> Self::MerkleTreeHash;
fn put_subtree_roots<Cache>( fn put_subtree_roots<Cache>(
st: &mut TestState<Cache>, st: &mut TestState<Cache>,
@ -94,6 +96,8 @@ pub(crate) trait ShieldedPoolTester {
roots: &[CommitmentTreeRoot<Self::MerkleTreeHash>], roots: &[CommitmentTreeRoot<Self::MerkleTreeHash>],
) -> Result<(), ShardTreeError<commitment_tree::Error>>; ) -> Result<(), ShardTreeError<commitment_tree::Error>>;
fn next_subtree_index(s: &WalletSummary<AccountId>) -> u64;
fn select_spendable_notes<Cache>( fn select_spendable_notes<Cache>(
st: &TestState<Cache>, st: &TestState<Cache>,
account: AccountId, account: AccountId,

View File

@ -464,7 +464,7 @@ pub(crate) fn put_received_note<T: ReceivedSaplingOutput>(
#[cfg(test)] #[cfg(test)]
pub(crate) mod tests { pub(crate) mod tests {
use incrementalmerkletree::Hashable; use incrementalmerkletree::{Hashable, Level};
use shardtree::error::ShardTreeError; use shardtree::error::ShardTreeError;
use zcash_proofs::prover::LocalTxProver; use zcash_proofs::prover::LocalTxProver;
@ -486,7 +486,9 @@ pub(crate) mod tests {
use zcash_client_backend::{ use zcash_client_backend::{
address::Address, address::Address,
data_api::{chain::CommitmentTreeRoot, DecryptedTransaction, WalletCommitmentTrees}, data_api::{
chain::CommitmentTreeRoot, DecryptedTransaction, WalletCommitmentTrees, WalletSummary,
},
keys::UnifiedSpendingKey, keys::UnifiedSpendingKey,
wallet::{Note, ReceivedNote}, wallet::{Note, ReceivedNote},
ShieldedProtocol, ShieldedProtocol,
@ -544,6 +546,10 @@ pub(crate) mod tests {
sapling::Node::empty_leaf() sapling::Node::empty_leaf()
} }
fn empty_tree_root(level: Level) -> Self::MerkleTreeHash {
sapling::Node::empty_root(level)
}
fn put_subtree_roots<Cache>( fn put_subtree_roots<Cache>(
st: &mut TestState<Cache>, st: &mut TestState<Cache>,
start_index: u64, start_index: u64,
@ -553,6 +559,10 @@ pub(crate) mod tests {
.put_sapling_subtree_roots(start_index, roots) .put_sapling_subtree_roots(start_index, roots)
} }
fn next_subtree_index(s: &WalletSummary<AccountId>) -> u64 {
s.next_sapling_subtree_index()
}
fn select_spendable_notes<Cache>( fn select_spendable_notes<Cache>(
st: &TestState<Cache>, st: &TestState<Cache>,
account: AccountId, account: AccountId,

View File

@ -547,8 +547,7 @@ pub(crate) mod tests {
use zcash_client_backend::data_api::{ use zcash_client_backend::data_api::{
chain::CommitmentTreeRoot, chain::CommitmentTreeRoot,
scanning::{spanning_tree::testing::scan_range, ScanPriority}, scanning::{spanning_tree::testing::scan_range, ScanPriority},
AccountBirthday, Ratio, WalletCommitmentTrees, WalletRead, WalletWrite, AccountBirthday, Ratio, WalletRead, WalletWrite, SAPLING_SHARD_HEIGHT,
SAPLING_SHARD_HEIGHT,
}; };
use zcash_primitives::{ use zcash_primitives::{
block::BlockHash, block::BlockHash,
@ -567,7 +566,11 @@ pub(crate) mod tests {
}; };
#[test] #[test]
fn scan_complete() { fn sapling_scan_complete() {
scan_complete::<SaplingPoolTester>();
}
fn scan_complete<T: ShieldedPoolTester>() {
use ScanPriority::*; use ScanPriority::*;
let mut st = TestBuilder::new() let mut st = TestBuilder::new()
@ -575,26 +578,27 @@ pub(crate) mod tests {
.with_test_account(AccountBirthday::from_sapling_activation) .with_test_account(AccountBirthday::from_sapling_activation)
.build(); .build();
let dfvk = st.test_account_sapling().unwrap(); let dfvk = T::test_account_fvk(&st);
let sapling_activation_height = st.sapling_activation_height(); let sapling_activation_height = st.sapling_activation_height();
assert_matches!( assert_matches!(
// In the following, we don't care what the root hashes are, they just need to be // In the following, we don't care what the root hashes are, they just need to be
// distinct. // distinct.
st.wallet_mut().put_sapling_subtree_roots( T::put_subtree_roots(
&mut st,
0, 0,
&[ &[
CommitmentTreeRoot::from_parts( CommitmentTreeRoot::from_parts(
sapling_activation_height + 100, sapling_activation_height + 100,
Node::empty_root(Level::from(0)) T::empty_tree_root(Level::from(0))
), ),
CommitmentTreeRoot::from_parts( CommitmentTreeRoot::from_parts(
sapling_activation_height + 200, sapling_activation_height + 200,
Node::empty_root(Level::from(1)) T::empty_tree_root(Level::from(1))
), ),
CommitmentTreeRoot::from_parts( CommitmentTreeRoot::from_parts(
sapling_activation_height + 300, sapling_activation_height + 300,
Node::empty_root(Level::from(2)) T::empty_tree_root(Level::from(2))
), ),
] ]
), ),
@ -687,7 +691,7 @@ pub(crate) mod tests {
st.wallet() st.wallet()
.get_wallet_summary(0) .get_wallet_summary(0)
.unwrap() .unwrap()
.map(|s| s.next_sapling_subtree_index()), .map(|s| T::next_subtree_index(&s)),
Some(2), Some(2),
); );
} }
@ -725,10 +729,14 @@ pub(crate) mod tests {
} }
#[test] #[test]
fn create_account_creates_ignored_range() { fn sapling_create_account_creates_ignored_range() {
create_account_creates_ignored_range::<SaplingPoolTester>();
}
fn create_account_creates_ignored_range<T: ShieldedPoolTester>() {
use ScanPriority::*; use ScanPriority::*;
let (st, _, birthday, sap_active) = test_with_canopy_birthday::<SaplingPoolTester>(); let (st, _, birthday, sap_active) = test_with_canopy_birthday::<T>();
let birthday_height = birthday.height().into(); let birthday_height = birthday.height().into();
let expected = vec![ let expected = vec![
@ -784,10 +792,14 @@ pub(crate) mod tests {
} }
#[test] #[test]
fn update_chain_tip_with_no_subtree_roots() { fn sapling_update_chain_tip_with_no_subtree_roots() {
update_chain_tip_with_no_subtree_roots::<SaplingPoolTester>();
}
fn update_chain_tip_with_no_subtree_roots<T: ShieldedPoolTester>() {
use ScanPriority::*; use ScanPriority::*;
let (mut st, _, birthday, sap_active) = test_with_canopy_birthday::<SaplingPoolTester>(); let (mut st, _, birthday, sap_active) = test_with_canopy_birthday::<T>();
// Set up the following situation: // Set up the following situation:
// //
@ -815,10 +827,14 @@ pub(crate) mod tests {
} }
#[test] #[test]
fn update_chain_tip_when_never_scanned() { fn sapling_update_chain_tip_when_never_scanned() {
update_chain_tip_when_never_scanned::<SaplingPoolTester>();
}
fn update_chain_tip_when_never_scanned<T: ShieldedPoolTester>() {
use ScanPriority::*; use ScanPriority::*;
let (mut st, _, birthday, sap_active) = test_with_canopy_birthday::<SaplingPoolTester>(); let (mut st, _, birthday, sap_active) = test_with_canopy_birthday::<T>();
// Set up the following situation: // Set up the following situation:
// //
@ -829,16 +845,16 @@ pub(crate) mod tests {
// Set up some shard root history before the wallet birthday. // Set up some shard root history before the wallet birthday.
let last_shard_start = birthday.height() - 1000; let last_shard_start = birthday.height() - 1000;
st.wallet_mut() T::put_subtree_roots(
.put_sapling_subtree_roots( &mut st,
0, 0,
&[CommitmentTreeRoot::from_parts( &[CommitmentTreeRoot::from_parts(
last_shard_start, last_shard_start,
// fake a hash, the value doesn't matter // fake a hash, the value doesn't matter
Node::empty_leaf(), T::empty_tree_leaf(),
)], )],
) )
.unwrap(); .unwrap();
// Update the chain tip. // Update the chain tip.
let tip_height = prior_tip_height + 500; let tip_height = prior_tip_height + 500;
@ -860,11 +876,15 @@ pub(crate) mod tests {
} }
#[test] #[test]
fn update_chain_tip_unstable_max_scanned() { fn sapling_update_chain_tip_unstable_max_scanned() {
update_chain_tip_unstable_max_scanned::<SaplingPoolTester>();
}
fn update_chain_tip_unstable_max_scanned<T: ShieldedPoolTester>() {
use ScanPriority::*; use ScanPriority::*;
// this birthday is 1234 notes into the second shard // this birthday is 1234 notes into the second shard
let (mut st, dfvk, birthday, sap_active) = test_with_canopy_birthday::<SaplingPoolTester>(); let (mut st, dfvk, birthday, sap_active) = test_with_canopy_birthday::<T>();
// Set up the following situation: // Set up the following situation:
// //
@ -876,16 +896,16 @@ pub(crate) mod tests {
// Set up some shard root history before the wallet birthday. // Set up some shard root history before the wallet birthday.
let initial_shard_end = birthday.height() - 1000; let initial_shard_end = birthday.height() - 1000;
st.wallet_mut() T::put_subtree_roots(
.put_sapling_subtree_roots( &mut st,
0, 0,
&[CommitmentTreeRoot::from_parts( &[CommitmentTreeRoot::from_parts(
initial_shard_end, initial_shard_end,
// fake a hash, the value doesn't matter // fake a hash, the value doesn't matter
Node::empty_leaf(), T::empty_tree_leaf(),
)], )],
) )
.unwrap(); .unwrap();
// Set up prior chain state. This simulates us having imported a wallet // Set up prior chain state. This simulates us having imported a wallet
// with a birthday 520 blocks below the chain tip. // with a birthday 520 blocks below the chain tip.
@ -928,16 +948,16 @@ pub(crate) mod tests {
// Now simulate shutting down, and then restarting 90 blocks later, after a shard // Now simulate shutting down, and then restarting 90 blocks later, after a shard
// has been completed. // has been completed.
let last_shard_start = prior_tip + 70; let last_shard_start = prior_tip + 70;
st.wallet_mut() T::put_subtree_roots(
.put_sapling_subtree_roots( &mut st,
0, 0,
&[CommitmentTreeRoot::from_parts( &[CommitmentTreeRoot::from_parts(
last_shard_start, last_shard_start,
// fake a hash, the value doesn't matter // fake a hash, the value doesn't matter
Node::empty_leaf(), T::empty_tree_leaf(),
)], )],
) )
.unwrap(); .unwrap();
let new_tip = last_shard_start + 20; let new_tip = last_shard_start + 20;
st.wallet_mut().update_chain_tip(new_tip).unwrap(); st.wallet_mut().update_chain_tip(new_tip).unwrap();
@ -972,10 +992,14 @@ pub(crate) mod tests {
} }
#[test] #[test]
fn update_chain_tip_stable_max_scanned() { fn sapling_update_chain_tip_stable_max_scanned() {
update_chain_tip_stable_max_scanned::<SaplingPoolTester>();
}
fn update_chain_tip_stable_max_scanned<T: ShieldedPoolTester>() {
use ScanPriority::*; use ScanPriority::*;
let (mut st, dfvk, birthday, sap_active) = test_with_canopy_birthday::<SaplingPoolTester>(); let (mut st, dfvk, birthday, sap_active) = test_with_canopy_birthday::<T>();
// Set up the following situation: // Set up the following situation:
// //
@ -988,16 +1012,16 @@ pub(crate) mod tests {
// Set up some shard root history before the wallet birthday. // Set up some shard root history before the wallet birthday.
let second_to_last_shard_start = birthday.height() - 1000; let second_to_last_shard_start = birthday.height() - 1000;
st.wallet_mut() T::put_subtree_roots(
.put_sapling_subtree_roots( &mut st,
0, 0,
&[CommitmentTreeRoot::from_parts( &[CommitmentTreeRoot::from_parts(
second_to_last_shard_start, second_to_last_shard_start,
// fake a hash, the value doesn't matter // fake a hash, the value doesn't matter
Node::empty_leaf(), T::empty_tree_leaf(),
)], )],
) )
.unwrap(); .unwrap();
// We have scan ranges and a subtree, but have scanned no blocks. // We have scan ranges and a subtree, but have scanned no blocks.
let summary = st.get_wallet_summary(1); let summary = st.get_wallet_summary(1);
@ -1032,10 +1056,7 @@ pub(crate) mod tests {
// We have scanned a block, so we now have a starting tree position, 500 blocks above the // We have scanned a block, so we now have a starting tree position, 500 blocks above the
// wallet birthday but before the end of the shard. // wallet birthday but before the end of the shard.
let summary = st.get_wallet_summary(1); let summary = st.get_wallet_summary(1);
assert_eq!( assert_eq!(summary.as_ref().map(|s| T::next_subtree_index(s)), Some(0),);
summary.as_ref().map(|s| s.next_sapling_subtree_index()),
Some(0),
);
assert_eq!( assert_eq!(
summary.and_then(|s| s.scan_progress()), summary.and_then(|s| s.scan_progress()),
Some(Ratio::new(1, 0x1 << SAPLING_SHARD_HEIGHT)) Some(Ratio::new(1, 0x1 << SAPLING_SHARD_HEIGHT))
@ -1044,16 +1065,16 @@ pub(crate) mod tests {
// Now simulate shutting down, and then restarting 70 blocks later, after a shard // Now simulate shutting down, and then restarting 70 blocks later, after a shard
// has been completed. // has been completed.
let last_shard_start = prior_tip + 50; let last_shard_start = prior_tip + 50;
st.wallet_mut() T::put_subtree_roots(
.put_sapling_subtree_roots( &mut st,
0, 0,
&[CommitmentTreeRoot::from_parts( &[CommitmentTreeRoot::from_parts(
last_shard_start, last_shard_start,
// fake a hash, the value doesn't matter // fake a hash, the value doesn't matter
Node::empty_leaf(), T::empty_tree_leaf(),
)], )],
) )
.unwrap(); .unwrap();
let new_tip = last_shard_start + 20; let new_tip = last_shard_start + 20;
st.wallet_mut().update_chain_tip(new_tip).unwrap(); st.wallet_mut().update_chain_tip(new_tip).unwrap();