zcash_client_sqlite: Modify `TestState` to allow initialization with chain state.

This commit is contained in:
Kris Nuttycombe 2024-03-19 14:39:00 -06:00
parent 978f838aae
commit dc4fccf566
8 changed files with 322 additions and 278 deletions

View File

@ -71,6 +71,8 @@ and this library adheres to Rust's notion of
- `get_transaction` now returns `Result<Option<Transaction>, _>` rather - `get_transaction` now returns `Result<Option<Transaction>, _>` rather
than returning an `Err` if the `txid` parameter does not correspond to than returning an `Err` if the `txid` parameter does not correspond to
a transaction in the database. a transaction in the database.
- `WalletWrite::create_account` now takes its `AccountBirthday` argument by
reference.
- Changes to the `InputSource` trait: - Changes to the `InputSource` trait:
- `select_spendable_notes` now takes its `target_value` argument as a - `select_spendable_notes` now takes its `target_value` argument as a
`NonNegativeAmount`. Also, it now returns a `SpendableNotes` data `NonNegativeAmount`. Also, it now returns a `SpendableNotes` data

View File

@ -1297,11 +1297,7 @@ impl<AccountId> SentTransactionOutput<AccountId> {
/// note commitment tree state is recorded at that height. /// note commitment tree state is recorded at that height.
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct AccountBirthday { pub struct AccountBirthday {
height: BlockHeight, prior_chain_state: ChainState,
sapling_frontier: Frontier<sapling::Node, { sapling::NOTE_COMMITMENT_TREE_DEPTH }>,
#[cfg(feature = "orchard")]
orchard_frontier:
Frontier<orchard::tree::MerkleHashOrchard, { orchard::NOTE_COMMITMENT_TREE_DEPTH as u8 }>,
recover_until: Option<BlockHeight>, recover_until: Option<BlockHeight>,
} }
@ -1326,10 +1322,9 @@ impl From<io::Error> for BirthdayError {
impl AccountBirthday { impl AccountBirthday {
/// Constructs a new [`AccountBirthday`] from its constituent parts. /// Constructs a new [`AccountBirthday`] from its constituent parts.
/// ///
/// * `height`: The birthday height of the account. This is defined as the height of the first /// * `prior_chain_state`: The chain state prior to the birthday height of the account. The
/// block to be scanned in wallet recovery. /// birthday height is defined as the height of the first block to be scanned in wallet
/// * `sapling_frontier`: The Sapling note commitment tree frontier as of the end of the block /// recovery.
/// prior to the birthday height.
/// * `recover_until`: An optional height at which the wallet should exit "recovery mode". In /// * `recover_until`: An optional height at which the wallet should exit "recovery mode". In
/// order to avoid confusing shifts in wallet balance and spendability that may temporarily be /// order to avoid confusing shifts in wallet balance and spendability that may temporarily be
/// visible to a user during the process of recovering from seed, wallets may optionally set a /// visible to a user during the process of recovering from seed, wallets may optionally set a
@ -1340,20 +1335,9 @@ impl AccountBirthday {
/// This API is intended primarily to be used in testing contexts; under normal circumstances, /// This API is intended primarily to be used in testing contexts; under normal circumstances,
/// [`AccountBirthday::from_treestate`] should be used instead. /// [`AccountBirthday::from_treestate`] should be used instead.
#[cfg(feature = "test-dependencies")] #[cfg(feature = "test-dependencies")]
pub fn from_parts( pub fn from_parts(prior_chain_state: ChainState, recover_until: Option<BlockHeight>) -> Self {
height: BlockHeight,
sapling_frontier: Frontier<sapling::Node, { sapling::NOTE_COMMITMENT_TREE_DEPTH }>,
#[cfg(feature = "orchard")] orchard_frontier: Frontier<
orchard::tree::MerkleHashOrchard,
{ orchard::NOTE_COMMITMENT_TREE_DEPTH as u8 },
>,
recover_until: Option<BlockHeight>,
) -> Self {
Self { Self {
height, prior_chain_state,
sapling_frontier,
#[cfg(feature = "orchard")]
orchard_frontier,
recover_until, recover_until,
} }
} }
@ -1373,10 +1357,7 @@ impl AccountBirthday {
recover_until: Option<BlockHeight>, recover_until: Option<BlockHeight>,
) -> Result<Self, BirthdayError> { ) -> Result<Self, BirthdayError> {
Ok(Self { Ok(Self {
height: BlockHeight::try_from(treestate.height + 1)?, prior_chain_state: treestate.to_chain_state()?,
sapling_frontier: treestate.sapling_tree()?.to_frontier(),
#[cfg(feature = "orchard")]
orchard_frontier: treestate.orchard_tree()?.to_frontier(),
recover_until, recover_until,
}) })
} }
@ -1386,7 +1367,7 @@ impl AccountBirthday {
pub fn sapling_frontier( pub fn sapling_frontier(
&self, &self,
) -> &Frontier<sapling::Node, { sapling::NOTE_COMMITMENT_TREE_DEPTH }> { ) -> &Frontier<sapling::Node, { sapling::NOTE_COMMITMENT_TREE_DEPTH }> {
&self.sapling_frontier self.prior_chain_state.final_sapling_tree()
} }
/// Returns the Orchard note commitment tree frontier as of the end of the block at /// Returns the Orchard note commitment tree frontier as of the end of the block at
@ -1396,12 +1377,12 @@ impl AccountBirthday {
&self, &self,
) -> &Frontier<orchard::tree::MerkleHashOrchard, { orchard::NOTE_COMMITMENT_TREE_DEPTH as u8 }> ) -> &Frontier<orchard::tree::MerkleHashOrchard, { orchard::NOTE_COMMITMENT_TREE_DEPTH as u8 }>
{ {
&self.orchard_frontier self.prior_chain_state.final_orchard_tree()
} }
/// Returns the birthday height of the account. /// Returns the birthday height of the account.
pub fn height(&self) -> BlockHeight { pub fn height(&self) -> BlockHeight {
self.height self.prior_chain_state.block_height() + 1
} }
/// Returns the height at which the wallet should exit "recovery mode". /// Returns the height at which the wallet should exit "recovery mode".
@ -1409,7 +1390,7 @@ impl AccountBirthday {
self.recover_until self.recover_until
} }
#[cfg(feature = "test-dependencies")] #[cfg(any(test, feature = "test-dependencies"))]
/// Constructs a new [`AccountBirthday`] at the given network upgrade's activation, /// Constructs a new [`AccountBirthday`] at the given network upgrade's activation,
/// with no "recover until" height. /// with no "recover until" height.
/// ///
@ -1419,17 +1400,18 @@ impl AccountBirthday {
pub fn from_activation<P: zcash_primitives::consensus::Parameters>( pub fn from_activation<P: zcash_primitives::consensus::Parameters>(
params: &P, params: &P,
network_upgrade: NetworkUpgrade, network_upgrade: NetworkUpgrade,
prior_block_hash: BlockHash,
) -> AccountBirthday { ) -> AccountBirthday {
AccountBirthday::from_parts( AccountBirthday::from_parts(
params.activation_height(network_upgrade).unwrap(), ChainState::empty(
Frontier::empty(), params.activation_height(network_upgrade).unwrap() - 1,
#[cfg(feature = "orchard")] prior_block_hash,
Frontier::empty(), ),
None, None,
) )
} }
#[cfg(feature = "test-dependencies")] #[cfg(any(test, feature = "test-dependencies"))]
/// Constructs a new [`AccountBirthday`] at Sapling activation, with no /// Constructs a new [`AccountBirthday`] at Sapling activation, with no
/// "recover until" height. /// "recover until" height.
/// ///
@ -1438,8 +1420,9 @@ impl AccountBirthday {
/// Panics if the Sapling activation height is not set. /// Panics if the Sapling activation height is not set.
pub fn from_sapling_activation<P: zcash_primitives::consensus::Parameters>( pub fn from_sapling_activation<P: zcash_primitives::consensus::Parameters>(
params: &P, params: &P,
prior_block_hash: BlockHash,
) -> AccountBirthday { ) -> AccountBirthday {
Self::from_activation(params, NetworkUpgrade::Sapling) Self::from_activation(params, NetworkUpgrade::Sapling, prior_block_hash)
} }
} }
@ -1482,7 +1465,7 @@ pub trait WalletWrite: WalletRead {
fn create_account( fn create_account(
&mut self, &mut self,
seed: &SecretVec<u8>, seed: &SecretVec<u8>,
birthday: AccountBirthday, birthday: &AccountBirthday,
) -> Result<(Self::AccountId, UnifiedSpendingKey), Self::Error>; ) -> Result<(Self::AccountId, UnifiedSpendingKey), Self::Error>;
/// Generates and persists the next available diversified address, given the current /// Generates and persists the next available diversified address, given the current
@ -1887,7 +1870,7 @@ pub mod testing {
fn create_account( fn create_account(
&mut self, &mut self,
seed: &SecretVec<u8>, seed: &SecretVec<u8>,
_birthday: AccountBirthday, _birthday: &AccountBirthday,
) -> Result<(Self::AccountId, UnifiedSpendingKey), Self::Error> { ) -> Result<(Self::AccountId, UnifiedSpendingKey), Self::Error> {
let account = zip32::AccountId::ZERO; let account = zip32::AccountId::ZERO;
UnifiedSpendingKey::from_seed(&self.network, seed.expose_secret(), account) UnifiedSpendingKey::from_seed(&self.network, seed.expose_secret(), account)

View File

@ -530,7 +530,7 @@ impl<P: consensus::Parameters> WalletWrite for WalletDb<rusqlite::Connection, P>
fn create_account( fn create_account(
&mut self, &mut self,
seed: &SecretVec<u8>, seed: &SecretVec<u8>,
birthday: AccountBirthday, birthday: &AccountBirthday,
) -> Result<(AccountId, UnifiedSpendingKey), Self::Error> { ) -> Result<(AccountId, UnifiedSpendingKey), Self::Error> {
self.transactionally(|wdb| { self.transactionally(|wdb| {
let seed_fingerprint = let seed_fingerprint =
@ -1690,7 +1690,8 @@ extern crate assert_matches;
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use secrecy::SecretVec; use secrecy::SecretVec;
use zcash_client_backend::data_api::{AccountBirthday, WalletRead, WalletWrite}; use zcash_client_backend::data_api::{WalletRead, WalletWrite};
use zcash_primitives::block::BlockHash;
use crate::{testing::TestBuilder, AccountId, DEFAULT_UA_REQUEST}; use crate::{testing::TestBuilder, AccountId, DEFAULT_UA_REQUEST};
@ -1703,7 +1704,7 @@ mod tests {
#[test] #[test]
fn validate_seed() { fn validate_seed() {
let st = TestBuilder::new() let st = TestBuilder::new()
.with_test_account(AccountBirthday::from_sapling_activation) .with_account_from_sapling_activation(BlockHash([0; 32]))
.build(); .build();
let account = st.test_account().unwrap(); let account = st.test_account().unwrap();
@ -1732,7 +1733,7 @@ mod tests {
#[test] #[test]
pub(crate) fn get_next_available_address() { pub(crate) fn get_next_available_address() {
let mut st = TestBuilder::new() let mut st = TestBuilder::new()
.with_test_account(AccountBirthday::from_sapling_activation) .with_account_from_sapling_activation(BlockHash([0; 32]))
.build(); .build();
let account = st.test_account().cloned().unwrap(); let account = st.test_account().cloned().unwrap();
@ -1763,7 +1764,7 @@ mod tests {
// Add an account to the wallet. // Add an account to the wallet.
let st = TestBuilder::new() let st = TestBuilder::new()
.with_block_cache() .with_block_cache()
.with_test_account(AccountBirthday::from_sapling_activation) .with_account_from_sapling_activation(BlockHash([0; 32]))
.build(); .build();
let account = st.test_account().unwrap(); let account = st.test_account().unwrap();
let ufvk = account.usk().to_unified_full_viewing_key(); let ufvk = account.usk().to_unified_full_viewing_key();

View File

@ -13,7 +13,7 @@ use rand_chacha::ChaChaRng;
use rand_core::{CryptoRng, RngCore, SeedableRng}; use rand_core::{CryptoRng, RngCore, SeedableRng};
use rusqlite::{params, Connection}; use rusqlite::{params, Connection};
use secrecy::{Secret, SecretVec}; use secrecy::{Secret, SecretVec};
use shardtree::error::ShardTreeError;
use tempfile::NamedTempFile; use tempfile::NamedTempFile;
#[cfg(feature = "unstable")] #[cfg(feature = "unstable")]
@ -102,45 +102,57 @@ use crate::{
pub(crate) mod pool; pub(crate) mod pool;
pub(crate) struct InitialChainState {
pub(crate) chain_state: ChainState,
pub(crate) prior_sapling_roots: Vec<CommitmentTreeRoot<sapling::Node>>,
#[cfg(feature = "orchard")]
pub(crate) prior_orchard_roots: Vec<CommitmentTreeRoot<MerkleHashOrchard>>,
}
/// A builder for a `zcash_client_sqlite` test. /// A builder for a `zcash_client_sqlite` test.
pub(crate) struct TestBuilder<Cache> { pub(crate) struct TestBuilder<Cache> {
rng: ChaChaRng,
network: LocalNetwork, network: LocalNetwork,
cache: Cache, cache: Cache,
test_account_birthday: Option<AccountBirthday>, initial_chain_state: Option<InitialChainState>,
rng: ChaChaRng, account_birthday: Option<AccountBirthday>,
} }
impl TestBuilder<()> { impl TestBuilder<()> {
pub const DEFAULT_NETWORK: LocalNetwork = LocalNetwork {
overwinter: Some(BlockHeight::from_u32(1)),
sapling: Some(BlockHeight::from_u32(100_000)),
blossom: Some(BlockHeight::from_u32(100_000)),
heartwood: Some(BlockHeight::from_u32(100_000)),
canopy: Some(BlockHeight::from_u32(100_000)),
nu5: Some(BlockHeight::from_u32(100_000)),
#[cfg(zcash_unstable = "nu6")]
nu6: None,
#[cfg(zcash_unstable = "zfuture")]
z_future: None,
};
/// Constructs a new test. /// Constructs a new test.
pub(crate) fn new() -> Self { pub(crate) fn new() -> Self {
TestBuilder { TestBuilder {
rng: ChaChaRng::seed_from_u64(0),
// Use a fake network where Sapling through NU5 activate at the same height. // Use a fake network where Sapling through NU5 activate at the same height.
// We pick 100,000 to be large enough to handle any hard-coded test offsets. // We pick 100,000 to be large enough to handle any hard-coded test offsets.
network: LocalNetwork { network: Self::DEFAULT_NETWORK,
overwinter: Some(BlockHeight::from_u32(1)),
sapling: Some(BlockHeight::from_u32(100_000)),
blossom: Some(BlockHeight::from_u32(100_000)),
heartwood: Some(BlockHeight::from_u32(100_000)),
canopy: Some(BlockHeight::from_u32(100_000)),
nu5: Some(BlockHeight::from_u32(100_000)),
#[cfg(zcash_unstable = "nu6")]
nu6: None,
#[cfg(zcash_unstable = "zfuture")]
z_future: None,
},
cache: (), cache: (),
test_account_birthday: None, initial_chain_state: None,
rng: ChaChaRng::seed_from_u64(0), account_birthday: None,
} }
} }
/// Adds a [`BlockDb`] cache to the test. /// Adds a [`BlockDb`] cache to the test.
pub(crate) fn with_block_cache(self) -> TestBuilder<BlockCache> { pub(crate) fn with_block_cache(self) -> TestBuilder<BlockCache> {
TestBuilder { TestBuilder {
rng: self.rng,
network: self.network, network: self.network,
cache: BlockCache::new(), cache: BlockCache::new(),
test_account_birthday: self.test_account_birthday, initial_chain_state: self.initial_chain_state,
rng: self.rng, account_birthday: self.account_birthday,
} }
} }
@ -148,20 +160,69 @@ impl TestBuilder<()> {
#[cfg(feature = "unstable")] #[cfg(feature = "unstable")]
pub(crate) fn with_fs_block_cache(self) -> TestBuilder<FsBlockCache> { pub(crate) fn with_fs_block_cache(self) -> TestBuilder<FsBlockCache> {
TestBuilder { TestBuilder {
rng: self.rng,
network: self.network, network: self.network,
cache: FsBlockCache::new(), cache: FsBlockCache::new(),
test_account_birthday: self.test_account_birthday, initial_chain_state: self.initial_chain_state,
rng: self.rng, account_birthday: self.account_birthday,
} }
} }
} }
impl<Cache> TestBuilder<Cache> { impl<Cache> TestBuilder<Cache> {
pub(crate) fn with_test_account<F: FnOnce(&LocalNetwork) -> AccountBirthday>( pub(crate) fn with_initial_chain_state(
mut self, mut self,
birthday: F, chain_state: impl FnOnce(&mut ChaChaRng, &LocalNetwork) -> InitialChainState,
) -> Self { ) -> Self {
self.test_account_birthday = Some(birthday(&self.network)); assert!(self.initial_chain_state.is_none());
assert!(self.account_birthday.is_none());
self.initial_chain_state = Some(chain_state(&mut self.rng, &self.network));
self
}
pub(crate) fn with_account_birthday(
mut self,
birthday: impl FnOnce(
&mut ChaChaRng,
&LocalNetwork,
Option<&InitialChainState>,
) -> AccountBirthday,
) -> Self {
assert!(self.account_birthday.is_none());
self.account_birthday = Some(birthday(
&mut self.rng,
&self.network,
self.initial_chain_state.as_ref(),
));
self
}
pub(crate) fn with_account_from_sapling_activation(mut self, prev_hash: BlockHash) -> Self {
assert!(self.account_birthday.is_none());
self.account_birthday = Some(AccountBirthday::from_parts(
ChainState::empty(
self.network
.activation_height(NetworkUpgrade::Sapling)
.unwrap()
- 1,
prev_hash,
),
None,
));
self
}
pub(crate) fn with_account_having_current_birthday(mut self) -> Self {
assert!(self.account_birthday.is_none());
assert!(self.initial_chain_state.is_some());
self.account_birthday = Some(AccountBirthday::from_parts(
self.initial_chain_state
.as_ref()
.unwrap()
.chain_state
.clone(),
None,
));
self self
} }
@ -171,24 +232,75 @@ impl<Cache> TestBuilder<Cache> {
let mut db_data = WalletDb::for_path(data_file.path(), self.network).unwrap(); let mut db_data = WalletDb::for_path(data_file.path(), self.network).unwrap();
init_wallet_db(&mut db_data, None).unwrap(); init_wallet_db(&mut db_data, None).unwrap();
let test_account = if let Some(birthday) = self.test_account_birthday { let mut cached_blocks = BTreeMap::new();
if let Some(initial_state) = self.initial_chain_state {
db_data
.put_sapling_subtree_roots(0, &initial_state.prior_sapling_roots)
.unwrap();
db_data
.with_sapling_tree_mut(|t| {
t.insert_frontier(
initial_state.chain_state.final_sapling_tree().clone(),
Retention::Checkpoint {
id: initial_state.chain_state.block_height(),
is_marked: false,
},
)
})
.unwrap();
#[cfg(feature = "orchard")]
{
db_data
.put_orchard_subtree_roots(0, &initial_state.prior_orchard_roots)
.unwrap();
db_data
.with_orchard_tree_mut(|t| {
t.insert_frontier(
initial_state.chain_state.final_orchard_tree().clone(),
Retention::Checkpoint {
id: initial_state.chain_state.block_height(),
is_marked: false,
},
)
})
.unwrap();
}
let final_sapling_tree_size =
initial_state.chain_state.final_sapling_tree().tree_size() as u32;
let _final_orchard_tree_size = 0;
#[cfg(feature = "orchard")]
let _final_orchard_tree_size =
initial_state.chain_state.final_orchard_tree().tree_size() as u32;
cached_blocks.insert(
initial_state.chain_state.block_height(),
CachedBlock {
chain_state: initial_state.chain_state.clone(),
sapling_end_size: final_sapling_tree_size,
orchard_end_size: _final_orchard_tree_size,
},
);
};
let test_account = self.account_birthday.map(|birthday| {
let seed = Secret::new(vec![0u8; 32]); let seed = Secret::new(vec![0u8; 32]);
let (account_id, usk) = db_data.create_account(&seed, birthday.clone()).unwrap(); let (account_id, usk) = db_data.create_account(&seed, &birthday).unwrap();
Some(( (
seed, seed,
TestAccount { TestAccount {
account_id, account_id,
usk, usk,
birthday, birthday,
}, },
)) )
} else { });
None
};
TestState { TestState {
cache: self.cache, cache: self.cache,
cached_blocks: BTreeMap::new(), cached_blocks,
latest_block_height: None, latest_block_height: None,
_data_file: data_file, _data_file: data_file,
db_data, db_data,
@ -341,67 +453,6 @@ where
self.cache.insert(&compact_block) self.cache.insert(&compact_block)
} }
/// Ensure that the provided chain state and subtree roots exist in the wallet's note
/// commitment tree(s). This may result in a conflict if either the provided subtree roots or
/// the chain state conflict with existing note commitment tree data.
pub(crate) fn establish_chain_state(
&mut self,
state: ChainState,
prior_sapling_roots: &[CommitmentTreeRoot<sapling::Node>],
#[cfg(feature = "orchard")] prior_orchard_roots: &[CommitmentTreeRoot<MerkleHashOrchard>],
) -> Result<(), ShardTreeError<commitment_tree::Error>> {
self.wallet_mut()
.put_sapling_subtree_roots(0, prior_sapling_roots)?;
#[cfg(feature = "orchard")]
self.wallet_mut()
.put_orchard_subtree_roots(0, prior_orchard_roots)?;
self.wallet_mut().with_sapling_tree_mut(|t| {
t.insert_frontier(
state.final_sapling_tree().clone(),
Retention::Checkpoint {
id: state.block_height(),
is_marked: false,
},
)
})?;
let final_sapling_tree_size = state.final_sapling_tree().tree_size() as u32;
#[cfg(feature = "orchard")]
self.wallet_mut().with_orchard_tree_mut(|t| {
t.insert_frontier(
state.final_orchard_tree().clone(),
Retention::Checkpoint {
id: state.block_height(),
is_marked: false,
},
)
})?;
let _final_orchard_tree_size = 0;
#[cfg(feature = "orchard")]
let _final_orchard_tree_size = state.final_orchard_tree().tree_size() as u32;
self.insert_cached_block(state, final_sapling_tree_size, _final_orchard_tree_size);
Ok(())
}
fn insert_cached_block(
&mut self,
chain_state: ChainState,
sapling_end_size: u32,
orchard_end_size: u32,
) {
self.cached_blocks.insert(
chain_state.block_height(),
CachedBlock {
chain_state,
sapling_end_size,
orchard_end_size,
},
);
}
/// Creates a fake block at the expected next height containing a single output of the /// Creates a fake block at the expected next height containing a single output of the
/// given value, and inserts it into the cache. /// given value, and inserts it into the cache.
pub(crate) fn generate_next_block<Fvk: TestFvk>( pub(crate) fn generate_next_block<Fvk: TestFvk>(
@ -692,11 +743,6 @@ impl<Cache> TestState<Cache> {
self.db_data.params self.db_data.params
} }
/// Exposes the random number source for the test state
pub(crate) fn rng(&mut self) -> &mut ChaChaRng {
&mut self.rng
}
/// Convenience method for obtaining the Sapling activation height for the network under test. /// Convenience method for obtaining the Sapling activation height for the network under test.
pub(crate) fn sapling_activation_height(&self) -> BlockHeight { pub(crate) fn sapling_activation_height(&self) -> BlockHeight {
self.db_data self.db_data

View File

@ -53,9 +53,6 @@ use crate::{
AccountId, NoteId, ReceivedNoteId, AccountId, NoteId, ReceivedNoteId,
}; };
#[cfg(feature = "orchard")]
use zcash_primitives::consensus::NetworkUpgrade;
#[cfg(feature = "transparent-inputs")] #[cfg(feature = "transparent-inputs")]
use { use {
zcash_client_backend::{ zcash_client_backend::{
@ -129,7 +126,7 @@ pub(crate) trait ShieldedPoolTester {
pub(crate) fn send_single_step_proposed_transfer<T: ShieldedPoolTester>() { pub(crate) fn send_single_step_proposed_transfer<T: ShieldedPoolTester>() {
let mut st = TestBuilder::new() let mut st = TestBuilder::new()
.with_block_cache() .with_block_cache()
.with_test_account(AccountBirthday::from_sapling_activation) .with_account_from_sapling_activation(BlockHash([0; 32]))
.build(); .build();
let account = st.test_account().cloned().unwrap(); let account = st.test_account().cloned().unwrap();
@ -287,7 +284,7 @@ pub(crate) fn send_multi_step_proposed_transfer<T: ShieldedPoolTester>() {
let mut st = TestBuilder::new() let mut st = TestBuilder::new()
.with_block_cache() .with_block_cache()
.with_test_account(AccountBirthday::from_sapling_activation) .with_account_from_sapling_activation(BlockHash([0; 32]))
.build(); .build();
let account = st.test_account().cloned().unwrap(); let account = st.test_account().cloned().unwrap();
@ -439,7 +436,7 @@ pub(crate) fn send_multi_step_proposed_transfer<T: ShieldedPoolTester>() {
#[allow(deprecated)] #[allow(deprecated)]
pub(crate) fn create_to_address_fails_on_incorrect_usk<T: ShieldedPoolTester>() { pub(crate) fn create_to_address_fails_on_incorrect_usk<T: ShieldedPoolTester>() {
let mut st = TestBuilder::new() let mut st = TestBuilder::new()
.with_test_account(AccountBirthday::from_sapling_activation) .with_account_from_sapling_activation(BlockHash([0; 32]))
.build(); .build();
let dfvk = T::test_account_fvk(&st); let dfvk = T::test_account_fvk(&st);
let to = T::fvk_default_address(&dfvk); let to = T::fvk_default_address(&dfvk);
@ -467,7 +464,7 @@ pub(crate) fn create_to_address_fails_on_incorrect_usk<T: ShieldedPoolTester>()
#[allow(deprecated)] #[allow(deprecated)]
pub(crate) fn proposal_fails_with_no_blocks<T: ShieldedPoolTester>() { pub(crate) fn proposal_fails_with_no_blocks<T: ShieldedPoolTester>() {
let mut st = TestBuilder::new() let mut st = TestBuilder::new()
.with_test_account(AccountBirthday::from_sapling_activation) .with_account_from_sapling_activation(BlockHash([0; 32]))
.build(); .build();
let account_id = st.test_account().unwrap().account_id(); let account_id = st.test_account().unwrap().account_id();
@ -496,7 +493,7 @@ pub(crate) fn proposal_fails_with_no_blocks<T: ShieldedPoolTester>() {
pub(crate) fn spend_fails_on_unverified_notes<T: ShieldedPoolTester>() { pub(crate) fn spend_fails_on_unverified_notes<T: ShieldedPoolTester>() {
let mut st = TestBuilder::new() let mut st = TestBuilder::new()
.with_block_cache() .with_block_cache()
.with_test_account(AccountBirthday::from_sapling_activation) .with_account_from_sapling_activation(BlockHash([0; 32]))
.build(); .build();
let account = st.test_account().cloned().unwrap(); let account = st.test_account().cloned().unwrap();
@ -647,7 +644,7 @@ pub(crate) fn spend_fails_on_unverified_notes<T: ShieldedPoolTester>() {
pub(crate) fn spend_fails_on_locked_notes<T: ShieldedPoolTester>() { pub(crate) fn spend_fails_on_locked_notes<T: ShieldedPoolTester>() {
let mut st = TestBuilder::new() let mut st = TestBuilder::new()
.with_block_cache() .with_block_cache()
.with_test_account(AccountBirthday::from_sapling_activation) .with_account_from_sapling_activation(BlockHash([0; 32]))
.build(); .build();
let account = st.test_account().cloned().unwrap(); let account = st.test_account().cloned().unwrap();
@ -785,7 +782,7 @@ pub(crate) fn spend_fails_on_locked_notes<T: ShieldedPoolTester>() {
pub(crate) fn ovk_policy_prevents_recovery_from_chain<T: ShieldedPoolTester>() { pub(crate) fn ovk_policy_prevents_recovery_from_chain<T: ShieldedPoolTester>() {
let mut st = TestBuilder::new() let mut st = TestBuilder::new()
.with_block_cache() .with_block_cache()
.with_test_account(AccountBirthday::from_sapling_activation) .with_account_from_sapling_activation(BlockHash([0; 32]))
.build(); .build();
let account = st.test_account().cloned().unwrap(); let account = st.test_account().cloned().unwrap();
@ -881,7 +878,7 @@ pub(crate) fn ovk_policy_prevents_recovery_from_chain<T: ShieldedPoolTester>() {
pub(crate) fn spend_succeeds_to_t_addr_zero_change<T: ShieldedPoolTester>() { pub(crate) fn spend_succeeds_to_t_addr_zero_change<T: ShieldedPoolTester>() {
let mut st = TestBuilder::new() let mut st = TestBuilder::new()
.with_block_cache() .with_block_cache()
.with_test_account(AccountBirthday::from_sapling_activation) .with_account_from_sapling_activation(BlockHash([0; 32]))
.build(); .build();
let account = st.test_account().cloned().unwrap(); let account = st.test_account().cloned().unwrap();
@ -928,7 +925,7 @@ pub(crate) fn spend_succeeds_to_t_addr_zero_change<T: ShieldedPoolTester>() {
pub(crate) fn change_note_spends_succeed<T: ShieldedPoolTester>() { pub(crate) fn change_note_spends_succeed<T: ShieldedPoolTester>() {
let mut st = TestBuilder::new() let mut st = TestBuilder::new()
.with_block_cache() .with_block_cache()
.with_test_account(AccountBirthday::from_sapling_activation) .with_account_from_sapling_activation(BlockHash([0; 32]))
.build(); .build();
let account = st.test_account().cloned().unwrap(); let account = st.test_account().cloned().unwrap();
@ -998,17 +995,11 @@ pub(crate) fn external_address_change_spends_detected_in_restore_from_seed<
// Add two accounts to the wallet. // Add two accounts to the wallet.
let seed = Secret::new([0u8; 32].to_vec()); let seed = Secret::new([0u8; 32].to_vec());
let birthday = AccountBirthday::from_sapling_activation(&st.network()); let birthday = AccountBirthday::from_sapling_activation(&st.network(), BlockHash([0; 32]));
let (account_id, usk) = st let (account_id, usk) = st.wallet_mut().create_account(&seed, &birthday).unwrap();
.wallet_mut()
.create_account(&seed, birthday.clone())
.unwrap();
let dfvk = T::sk_to_fvk(T::usk_to_sk(&usk)); let dfvk = T::sk_to_fvk(T::usk_to_sk(&usk));
let (account2, usk2) = st let (account2, usk2) = st.wallet_mut().create_account(&seed, &birthday).unwrap();
.wallet_mut()
.create_account(&seed, birthday.clone())
.unwrap();
let dfvk2 = T::sk_to_fvk(T::usk_to_sk(&usk2)); let dfvk2 = T::sk_to_fvk(T::usk_to_sk(&usk2));
// Add funds to the wallet in a single note // Add funds to the wallet in a single note
@ -1081,16 +1072,13 @@ pub(crate) fn external_address_change_spends_detected_in_restore_from_seed<
st.reset(); st.reset();
// Account creation and DFVK derivation should be deterministic. // Account creation and DFVK derivation should be deterministic.
let (_, restored_usk) = st let (_, restored_usk) = st.wallet_mut().create_account(&seed, &birthday).unwrap();
.wallet_mut()
.create_account(&seed, birthday.clone())
.unwrap();
assert!(T::fvks_equal( assert!(T::fvks_equal(
&T::sk_to_fvk(T::usk_to_sk(&restored_usk)), &T::sk_to_fvk(T::usk_to_sk(&restored_usk)),
&dfvk, &dfvk,
)); ));
let (_, restored_usk2) = st.wallet_mut().create_account(&seed, birthday).unwrap(); let (_, restored_usk2) = st.wallet_mut().create_account(&seed, &birthday).unwrap();
assert!(T::fvks_equal( assert!(T::fvks_equal(
&T::sk_to_fvk(T::usk_to_sk(&restored_usk2)), &T::sk_to_fvk(T::usk_to_sk(&restored_usk2)),
&dfvk2, &dfvk2,
@ -1105,7 +1093,7 @@ pub(crate) fn external_address_change_spends_detected_in_restore_from_seed<
pub(crate) fn zip317_spend<T: ShieldedPoolTester>() { pub(crate) fn zip317_spend<T: ShieldedPoolTester>() {
let mut st = TestBuilder::new() let mut st = TestBuilder::new()
.with_block_cache() .with_block_cache()
.with_test_account(AccountBirthday::from_sapling_activation) .with_account_from_sapling_activation(BlockHash([0; 32]))
.build(); .build();
let account = st.test_account().cloned().unwrap(); let account = st.test_account().cloned().unwrap();
@ -1199,7 +1187,7 @@ pub(crate) fn zip317_spend<T: ShieldedPoolTester>() {
pub(crate) fn shield_transparent<T: ShieldedPoolTester>() { pub(crate) fn shield_transparent<T: ShieldedPoolTester>() {
let mut st = TestBuilder::new() let mut st = TestBuilder::new()
.with_block_cache() .with_block_cache()
.with_test_account(AccountBirthday::from_sapling_activation) .with_account_from_sapling_activation(BlockHash([0; 32]))
.build(); .build();
let account = st.test_account().cloned().unwrap(); let account = st.test_account().cloned().unwrap();
@ -1259,7 +1247,7 @@ pub(crate) fn shield_transparent<T: ShieldedPoolTester>() {
#[allow(dead_code)] #[allow(dead_code)]
pub(crate) fn birthday_in_anchor_shard<T: ShieldedPoolTester>() { pub(crate) fn birthday_in_anchor_shard<T: ShieldedPoolTester>() {
// Use a non-zero birthday offset because Sapling and NU5 are activated at the same height. // Use a non-zero birthday offset because Sapling and NU5 are activated at the same height.
let (mut st, dfvk, birthday, _) = test_with_nu5_birthday_offset::<T>(76); let (mut st, dfvk, birthday, _) = test_with_nu5_birthday_offset::<T>(76, BlockHash([0; 32]));
// Set up the following situation: // Set up the following situation:
// //
@ -1364,7 +1352,7 @@ pub(crate) fn birthday_in_anchor_shard<T: ShieldedPoolTester>() {
pub(crate) fn checkpoint_gaps<T: ShieldedPoolTester>() { pub(crate) fn checkpoint_gaps<T: ShieldedPoolTester>() {
let mut st = TestBuilder::new() let mut st = TestBuilder::new()
.with_block_cache() .with_block_cache()
.with_test_account(AccountBirthday::from_sapling_activation) .with_account_from_sapling_activation(BlockHash([0; 32]))
.build(); .build();
let account = st.test_account().cloned().unwrap(); let account = st.test_account().cloned().unwrap();
@ -1433,7 +1421,8 @@ pub(crate) fn checkpoint_gaps<T: ShieldedPoolTester>() {
pub(crate) fn pool_crossing_required<P0: ShieldedPoolTester, P1: ShieldedPoolTester>() { pub(crate) fn pool_crossing_required<P0: ShieldedPoolTester, P1: ShieldedPoolTester>() {
let mut st = TestBuilder::new() let mut st = TestBuilder::new()
.with_block_cache() .with_block_cache()
.with_test_account(|params| AccountBirthday::from_activation(params, NetworkUpgrade::Nu5)) .with_account_from_sapling_activation(BlockHash([0; 32])) // TODO: Allow for Orchard
// activation after Sapling
.build(); .build();
let account = st.test_account().cloned().unwrap(); let account = st.test_account().cloned().unwrap();
@ -1522,7 +1511,8 @@ pub(crate) fn pool_crossing_required<P0: ShieldedPoolTester, P1: ShieldedPoolTes
pub(crate) fn fully_funded_fully_private<P0: ShieldedPoolTester, P1: ShieldedPoolTester>() { pub(crate) fn fully_funded_fully_private<P0: ShieldedPoolTester, P1: ShieldedPoolTester>() {
let mut st = TestBuilder::new() let mut st = TestBuilder::new()
.with_block_cache() .with_block_cache()
.with_test_account(|params| AccountBirthday::from_activation(params, NetworkUpgrade::Nu5)) .with_account_from_sapling_activation(BlockHash([0; 32])) // TODO: Allow for Orchard
// activation after Sapling
.build(); .build();
let account = st.test_account().cloned().unwrap(); let account = st.test_account().cloned().unwrap();
@ -1612,7 +1602,7 @@ pub(crate) fn fully_funded_fully_private<P0: ShieldedPoolTester, P1: ShieldedPoo
pub(crate) fn valid_chain_states<T: ShieldedPoolTester>() { pub(crate) fn valid_chain_states<T: ShieldedPoolTester>() {
let mut st = TestBuilder::new() let mut st = TestBuilder::new()
.with_block_cache() .with_block_cache()
.with_test_account(AccountBirthday::from_sapling_activation) .with_account_from_sapling_activation(BlockHash([0; 32]))
.build(); .build();
let dfvk = T::test_account_fvk(&st); let dfvk = T::test_account_fvk(&st);
@ -1646,7 +1636,7 @@ pub(crate) fn valid_chain_states<T: ShieldedPoolTester>() {
pub(crate) fn invalid_chain_cache_disconnected<T: ShieldedPoolTester>() { pub(crate) fn invalid_chain_cache_disconnected<T: ShieldedPoolTester>() {
let mut st = TestBuilder::new() let mut st = TestBuilder::new()
.with_block_cache() .with_block_cache()
.with_test_account(AccountBirthday::from_sapling_activation) .with_account_from_sapling_activation(BlockHash([0; 32]))
.build(); .build();
let dfvk = T::test_account_fvk(&st); let dfvk = T::test_account_fvk(&st);
@ -1697,7 +1687,7 @@ pub(crate) fn invalid_chain_cache_disconnected<T: ShieldedPoolTester>() {
pub(crate) fn data_db_truncation<T: ShieldedPoolTester>() { pub(crate) fn data_db_truncation<T: ShieldedPoolTester>() {
let mut st = TestBuilder::new() let mut st = TestBuilder::new()
.with_block_cache() .with_block_cache()
.with_test_account(AccountBirthday::from_sapling_activation) .with_account_from_sapling_activation(BlockHash([0; 32]))
.build(); .build();
let account = st.test_account().cloned().unwrap(); let account = st.test_account().cloned().unwrap();
@ -1753,7 +1743,7 @@ pub(crate) fn data_db_truncation<T: ShieldedPoolTester>() {
pub(crate) fn scan_cached_blocks_allows_blocks_out_of_order<T: ShieldedPoolTester>() { pub(crate) fn scan_cached_blocks_allows_blocks_out_of_order<T: ShieldedPoolTester>() {
let mut st = TestBuilder::new() let mut st = TestBuilder::new()
.with_block_cache() .with_block_cache()
.with_test_account(AccountBirthday::from_sapling_activation) .with_account_from_sapling_activation(BlockHash([0; 32]))
.build(); .build();
let account = st.test_account().cloned().unwrap(); let account = st.test_account().cloned().unwrap();
@ -1813,7 +1803,7 @@ pub(crate) fn scan_cached_blocks_allows_blocks_out_of_order<T: ShieldedPoolTeste
pub(crate) fn scan_cached_blocks_finds_received_notes<T: ShieldedPoolTester>() { pub(crate) fn scan_cached_blocks_finds_received_notes<T: ShieldedPoolTester>() {
let mut st = TestBuilder::new() let mut st = TestBuilder::new()
.with_block_cache() .with_block_cache()
.with_test_account(AccountBirthday::from_sapling_activation) .with_account_from_sapling_activation(BlockHash([0; 32]))
.build(); .build();
let account = st.test_account().cloned().unwrap(); let account = st.test_account().cloned().unwrap();
@ -1856,7 +1846,7 @@ pub(crate) fn scan_cached_blocks_finds_received_notes<T: ShieldedPoolTester>() {
pub(crate) fn scan_cached_blocks_finds_change_notes<T: ShieldedPoolTester>() { pub(crate) fn scan_cached_blocks_finds_change_notes<T: ShieldedPoolTester>() {
let mut st = TestBuilder::new() let mut st = TestBuilder::new()
.with_block_cache() .with_block_cache()
.with_test_account(AccountBirthday::from_sapling_activation) .with_account_from_sapling_activation(BlockHash([0; 32]))
.build(); .build();
let account = st.test_account().cloned().unwrap(); let account = st.test_account().cloned().unwrap();
@ -1895,7 +1885,7 @@ pub(crate) fn scan_cached_blocks_finds_change_notes<T: ShieldedPoolTester>() {
pub(crate) fn scan_cached_blocks_detects_spends_out_of_order<T: ShieldedPoolTester>() { pub(crate) fn scan_cached_blocks_detects_spends_out_of_order<T: ShieldedPoolTester>() {
let mut st = TestBuilder::new() let mut st = TestBuilder::new()
.with_block_cache() .with_block_cache()
.with_test_account(AccountBirthday::from_sapling_activation) .with_account_from_sapling_activation(BlockHash([0; 32]))
.build(); .build();
let account = st.test_account().cloned().unwrap(); let account = st.test_account().cloned().unwrap();

View File

@ -351,7 +351,7 @@ pub(crate) fn add_account<P: consensus::Parameters>(
params: &P, params: &P,
kind: AccountSource, kind: AccountSource,
viewing_key: ViewingKey, viewing_key: ViewingKey,
birthday: AccountBirthday, birthday: &AccountBirthday,
) -> Result<AccountId, SqliteClientError> { ) -> Result<AccountId, SqliteClientError> {
let (hd_seed_fingerprint, hd_account_index) = match kind { let (hd_seed_fingerprint, hd_account_index) = match kind {
AccountSource::Derived { AccountSource::Derived {
@ -2720,7 +2720,7 @@ mod tests {
use std::num::NonZeroU32; use std::num::NonZeroU32;
use sapling::zip32::ExtendedSpendingKey; use sapling::zip32::ExtendedSpendingKey;
use zcash_client_backend::data_api::{AccountBirthday, AccountSource, WalletRead}; use zcash_client_backend::data_api::{AccountSource, WalletRead};
use zcash_primitives::{block::BlockHash, transaction::components::amount::NonNegativeAmount}; use zcash_primitives::{block::BlockHash, transaction::components::amount::NonNegativeAmount};
use crate::{ use crate::{
@ -2749,7 +2749,7 @@ mod tests {
#[test] #[test]
fn empty_database_has_no_balance() { fn empty_database_has_no_balance() {
let st = TestBuilder::new() let st = TestBuilder::new()
.with_test_account(AccountBirthday::from_sapling_activation) .with_account_from_sapling_activation(BlockHash([0; 32]))
.build(); .build();
let account = st.test_account().unwrap(); let account = st.test_account().unwrap();
@ -2784,7 +2784,7 @@ mod tests {
use crate::testing::TestBuilder; use crate::testing::TestBuilder;
let mut st = TestBuilder::new() let mut st = TestBuilder::new()
.with_test_account(AccountBirthday::from_sapling_activation) .with_account_from_sapling_activation(BlockHash([0; 32]))
.build(); .build();
let account_id = st.test_account().unwrap().account_id(); let account_id = st.test_account().unwrap().account_id();
@ -2873,7 +2873,7 @@ mod tests {
use crate::testing::TestBuilder; use crate::testing::TestBuilder;
let st = TestBuilder::new() let st = TestBuilder::new()
.with_test_account(AccountBirthday::from_sapling_activation) .with_account_from_sapling_activation(BlockHash([0; 32]))
.build(); .build();
let account_id = st.test_account().unwrap().account_id(); let account_id = st.test_account().unwrap().account_id();
let account_parameters = st.wallet().get_account(account_id).unwrap().unwrap(); let account_parameters = st.wallet().get_account(account_id).unwrap().unwrap();
@ -2892,10 +2892,10 @@ mod tests {
let mut st = TestBuilder::new() let mut st = TestBuilder::new()
.with_block_cache() .with_block_cache()
.with_test_account(AccountBirthday::from_sapling_activation) .with_account_from_sapling_activation(BlockHash([0; 32]))
.build(); .build();
let account = st.test_account().unwrap().clone(); let account = st.test_account().cloned().unwrap();
let uaddr = st let uaddr = st
.wallet() .wallet()
.get_current_address(account.account_id()) .get_current_address(account.account_id())
@ -3043,7 +3043,7 @@ mod tests {
fn block_fully_scanned() { fn block_fully_scanned() {
let mut st = TestBuilder::new() let mut st = TestBuilder::new()
.with_block_cache() .with_block_cache()
.with_test_account(AccountBirthday::from_sapling_activation) .with_account_from_sapling_activation(BlockHash([0; 32]))
.build(); .build();
let block_fully_scanned = |st: &TestState<BlockCache>| { let block_fully_scanned = |st: &TestState<BlockCache>| {

View File

@ -1427,6 +1427,7 @@ mod tests {
#[cfg(feature = "transparent-inputs")] #[cfg(feature = "transparent-inputs")]
fn account_produces_expected_ua_sequence() { fn account_produces_expected_ua_sequence() {
use zcash_client_backend::data_api::{AccountBirthday, AccountSource, WalletRead}; use zcash_client_backend::data_api::{AccountBirthday, AccountSource, WalletRead};
use zcash_primitives::block::BlockHash;
let network = Network::MainNetwork; let network = Network::MainNetwork;
let data_file = NamedTempFile::new().unwrap(); let data_file = NamedTempFile::new().unwrap();
@ -1445,9 +1446,9 @@ mod tests {
Ok(()) Ok(())
); );
let birthday = AccountBirthday::from_sapling_activation(&network); let birthday = AccountBirthday::from_sapling_activation(&network, BlockHash([0; 32]));
let (account_id, _usk) = db_data let (account_id, _usk) = db_data
.create_account(&Secret::new(seed.to_vec()), birthday) .create_account(&Secret::new(seed.to_vec()), &birthday)
.unwrap(); .unwrap();
assert_matches!( assert_matches!(
db_data.get_account(account_id), db_data.get_account(account_id),

View File

@ -581,9 +581,8 @@ pub(crate) fn update_chain_tip<P: consensus::Parameters>(
pub(crate) mod tests { pub(crate) mod tests {
use std::num::NonZeroU8; use std::num::NonZeroU8;
use incrementalmerkletree::{frontier::Frontier, Hashable, Position}; use incrementalmerkletree::{frontier::Frontier, Position};
use sapling::Node;
use secrecy::SecretVec; use secrecy::SecretVec;
use zcash_client_backend::data_api::{ use zcash_client_backend::data_api::{
chain::{ChainState, CommitmentTreeRoot}, chain::{ChainState, CommitmentTreeRoot},
@ -599,7 +598,10 @@ pub(crate) mod tests {
use crate::{ use crate::{
error::SqliteClientError, error::SqliteClientError,
testing::{pool::ShieldedPoolTester, AddressType, BlockCache, TestBuilder, TestState}, testing::{
pool::ShieldedPoolTester, AddressType, BlockCache, InitialChainState, TestBuilder,
TestState,
},
wallet::{ wallet::{
sapling::tests::SaplingPoolTester, sapling::tests::SaplingPoolTester,
scanning::{insert_queue_entries, replace_queue_entries, suggest_scan_ranges}, scanning::{insert_queue_entries, replace_queue_entries, suggest_scan_ranges},
@ -609,7 +611,7 @@ pub(crate) mod tests {
#[cfg(feature = "orchard")] #[cfg(feature = "orchard")]
use { use {
crate::wallet::orchard::tests::OrchardPoolTester, orchard::tree::MerkleHashOrchard, crate::wallet::orchard::tests::OrchardPoolTester,
zcash_client_backend::data_api::ORCHARD_SHARD_HEIGHT, zcash_client_backend::data_api::ORCHARD_SHARD_HEIGHT,
}; };
@ -626,66 +628,75 @@ pub(crate) mod tests {
fn scan_complete<T: ShieldedPoolTester>() { fn scan_complete<T: ShieldedPoolTester>() {
use ScanPriority::*; use ScanPriority::*;
let initial_height_offset = 310;
let mut st = TestBuilder::new()
.with_block_cache()
.with_test_account(AccountBirthday::from_sapling_activation)
.build();
let sapling_activation_height = st.sapling_activation_height();
// We'll start inserting leaf notes 5 notes after the end of the third subtree, with a gap // We'll start inserting leaf notes 5 notes after the end of the third subtree, with a gap
// of 10 blocks. After `scan_cached_blocks`, the scan queue should have a requested scan // of 10 blocks. After `scan_cached_blocks`, the scan queue should have a requested scan
// range of 300..310 with `FoundNote` priority, 310..320 with `Scanned` priority. // range of 300..310 with `FoundNote` priority, 310..320 with `Scanned` priority.
// We set both Sapling and Orchard to the same initial tree size for simplicity. // We set both Sapling and Orchard to the same initial tree size for simplicity.
let prior_block_hash = BlockHash([0; 32]);
let initial_sapling_tree_size: u32 = (0x1 << 16) * 3 + 5; let initial_sapling_tree_size: u32 = (0x1 << 16) * 3 + 5;
let initial_orchard_tree_size: u32 = (0x1 << 16) * 3 + 5; let initial_orchard_tree_size: u32 = (0x1 << 16) * 3 + 5;
let initial_height_offset = 310;
// Construct a fake chain state for the end of block 300 let mut st = TestBuilder::new()
let (prior_sapling_frontiers, sapling_initial_tree) = .with_block_cache()
Frontier::random_with_prior_subtree_roots( .with_initial_chain_state(|rng, network| {
st.rng(), let sapling_activation_height =
initial_sapling_tree_size.into(), network.activation_height(NetworkUpgrade::Sapling).unwrap();
NonZeroU8::new(16).unwrap(), // Construct a fake chain state for the end of block 300
); let (prior_sapling_roots, sapling_initial_tree) =
let sapling_subtree_roots = prior_sapling_frontiers Frontier::random_with_prior_subtree_roots(
.into_iter() rng,
.zip(0u32..) initial_sapling_tree_size.into(),
.map(|(root, i)| { NonZeroU8::new(16).unwrap(),
CommitmentTreeRoot::from_parts(sapling_activation_height + (100 * (i + 1)), root) );
}) let prior_sapling_roots = prior_sapling_roots
.collect::<Vec<_>>(); .into_iter()
.zip(0u32..)
.map(|(root, i)| {
CommitmentTreeRoot::from_parts(
sapling_activation_height + (100 * (i + 1)),
root,
)
})
.collect::<Vec<_>>();
#[cfg(feature = "orchard")]
let (prior_orchard_frontiers, orchard_initial_tree) =
Frontier::random_with_prior_subtree_roots(
st.rng(),
initial_orchard_tree_size.into(),
NonZeroU8::new(16).unwrap(),
);
#[cfg(feature = "orchard")]
let orchard_subtree_roots = prior_orchard_frontiers
.into_iter()
.zip(0u32..)
.map(|(root, i)| {
CommitmentTreeRoot::from_parts(sapling_activation_height + (100 * (i + 1)), root)
})
.collect::<Vec<_>>();
let prior_block_hash = BlockHash([0; 32]);
st.establish_chain_state(
ChainState::new(
sapling_activation_height + initial_height_offset - 1,
prior_block_hash,
sapling_initial_tree,
#[cfg(feature = "orchard")] #[cfg(feature = "orchard")]
orchard_initial_tree, let (prior_orchard_roots, orchard_initial_tree) =
), Frontier::random_with_prior_subtree_roots(
&sapling_subtree_roots, rng,
#[cfg(feature = "orchard")] initial_orchard_tree_size.into(),
&orchard_subtree_roots, NonZeroU8::new(16).unwrap(),
) );
.unwrap(); #[cfg(feature = "orchard")]
let prior_orchard_roots = prior_orchard_roots
.into_iter()
.zip(0u32..)
.map(|(root, i)| {
CommitmentTreeRoot::from_parts(
sapling_activation_height + (100 * (i + 1)),
root,
)
})
.collect::<Vec<_>>();
InitialChainState {
chain_state: ChainState::new(
sapling_activation_height + initial_height_offset - 1,
prior_block_hash,
sapling_initial_tree,
#[cfg(feature = "orchard")]
orchard_initial_tree,
),
prior_sapling_roots,
#[cfg(feature = "orchard")]
prior_orchard_roots,
}
})
.with_account_from_sapling_activation(BlockHash([3; 32]))
.build();
let sapling_activation_height = st.sapling_activation_height();
let dfvk = T::test_account_fvk(&st); let dfvk = T::test_account_fvk(&st);
let value = NonNegativeAmount::const_from_u64(50000); let value = NonNegativeAmount::const_from_u64(50000);
@ -776,37 +787,43 @@ pub(crate) mod tests {
} }
pub(crate) fn test_with_nu5_birthday_offset<T: ShieldedPoolTester>( pub(crate) fn test_with_nu5_birthday_offset<T: ShieldedPoolTester>(
offset: u32, birthday_offset: u32,
prior_block_hash: BlockHash,
) -> (TestState<BlockCache>, T::Fvk, AccountBirthday, u32) { ) -> (TestState<BlockCache>, T::Fvk, AccountBirthday, u32) {
let st = TestBuilder::new() let st = TestBuilder::new()
.with_block_cache() .with_block_cache()
.with_test_account(|network| { .with_account_birthday(|rng, network, initial_chain_state| {
// We're constructing the birthday without adding any chain data.
assert!(initial_chain_state.is_none());
// We set the Sapling and Orchard frontiers at the birthday height to be // We set the Sapling and Orchard frontiers at the birthday height to be
// 1234 notes into the second shard. // 1234 notes into the second shard.
let birthday_height =
network.activation_height(NetworkUpgrade::Nu5).unwrap() + offset;
let frontier_position = Position::from((0x1 << 16) + 1234); let frontier_position = Position::from((0x1 << 16) + 1234);
let sapling_frontier = Frontier::from_parts( let birthday_height =
frontier_position, network.activation_height(NetworkUpgrade::Nu5).unwrap() + birthday_offset;
Node::empty_leaf(),
vec![Node::empty_leaf(); frontier_position.past_ommer_count().into()], // Construct a fake chain state for the end of the block with the given
) // birthday_offset from the Nu5 birthday.
.unwrap(); let (_, sapling_initial_tree) = Frontier::random_with_prior_subtree_roots(
rng,
(frontier_position + 1).into(),
NonZeroU8::new(16).unwrap(),
);
#[cfg(feature = "orchard")] #[cfg(feature = "orchard")]
let orchard_frontier = Frontier::from_parts( let (_, orchard_initial_tree) = Frontier::random_with_prior_subtree_roots(
frontier_position, rng,
MerkleHashOrchard::empty_leaf(), (frontier_position + 1).into(),
vec![ NonZeroU8::new(16).unwrap(),
MerkleHashOrchard::empty_leaf(); );
frontier_position.past_ommer_count().into()
],
)
.unwrap();
AccountBirthday::from_parts( AccountBirthday::from_parts(
birthday_height, ChainState::new(
sapling_frontier, birthday_height,
#[cfg(feature = "orchard")] prior_block_hash,
orchard_frontier, sapling_initial_tree,
#[cfg(feature = "orchard")]
orchard_initial_tree,
),
None, None,
) )
}) })
@ -834,7 +851,8 @@ pub(crate) mod tests {
use ScanPriority::*; use ScanPriority::*;
// Use a non-zero birthday offset because Sapling and NU5 are activated at the same height. // Use a non-zero birthday offset because Sapling and NU5 are activated at the same height.
let (st, _, birthday, sap_active) = test_with_nu5_birthday_offset::<T>(76); let (st, _, birthday, sap_active) =
test_with_nu5_birthday_offset::<T>(76, BlockHash([0; 32]));
let birthday_height = birthday.height().into(); let birthday_height = birthday.height().into();
let expected = vec![ let expected = vec![
@ -869,11 +887,8 @@ pub(crate) mod tests {
st.wallet_mut() st.wallet_mut()
.create_account( .create_account(
&SecretVec::new(vec![0; 32]), &SecretVec::new(vec![0; 32]),
AccountBirthday::from_parts( &AccountBirthday::from_parts(
wallet_birthday, ChainState::empty(wallet_birthday - 1, BlockHash([0; 32])),
Frontier::empty(),
#[cfg(feature = "orchard")]
Frontier::empty(),
None, None,
), ),
) )
@ -904,7 +919,8 @@ pub(crate) mod tests {
use ScanPriority::*; use ScanPriority::*;
// Use a non-zero birthday offset because Sapling and NU5 are activated at the same height. // Use a non-zero birthday offset because Sapling and NU5 are activated at the same height.
let (mut st, _, birthday, sap_active) = test_with_nu5_birthday_offset::<T>(76); let (mut st, _, birthday, sap_active) =
test_with_nu5_birthday_offset::<T>(76, BlockHash([0; 32]));
// Set up the following situation: // Set up the following situation:
// //
@ -921,7 +937,9 @@ pub(crate) mod tests {
// Verify that the suggested scan ranges match what is expected. // Verify that the suggested scan ranges match what is expected.
let expected = vec![ let expected = vec![
// The wallet's birthday onward is marked for recovery. // The wallet's birthday onward is marked for recovery. Because we don't
// yet have any chain state, it is marked with `Historic` priority rather
// than `ChainTip`.
scan_range(wallet_birthday..chain_end, Historic), scan_range(wallet_birthday..chain_end, Historic),
// The range below the wallet's birthday height is ignored. // The range below the wallet's birthday height is ignored.
scan_range(sap_active..wallet_birthday, Ignored), scan_range(sap_active..wallet_birthday, Ignored),
@ -946,7 +964,8 @@ pub(crate) mod tests {
use ScanPriority::*; use ScanPriority::*;
// Use a non-zero birthday offset because Sapling and NU5 are activated at the same height. // Use a non-zero birthday offset because Sapling and NU5 are activated at the same height.
let (mut st, _, birthday, sap_active) = test_with_nu5_birthday_offset::<T>(76); let (mut st, _, birthday, sap_active) =
test_with_nu5_birthday_offset::<T>(76, BlockHash([0; 32]));
// Set up the following situation: // Set up the following situation:
// //
@ -1007,7 +1026,8 @@ pub(crate) mod tests {
// Use a non-zero birthday offset because Sapling and NU5 are activated at the same height. // Use a non-zero birthday offset because Sapling and NU5 are activated at the same height.
// 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_nu5_birthday_offset::<T>(76); let (mut st, dfvk, birthday, sap_active) =
test_with_nu5_birthday_offset::<T>(76, BlockHash([0; 32]));
// Set up the following situation: // Set up the following situation:
// //
@ -1149,7 +1169,8 @@ pub(crate) mod tests {
use ScanPriority::*; use ScanPriority::*;
// Use a non-zero birthday offset because Sapling and NU5 are activated at the same height. // Use a non-zero birthday offset because Sapling and NU5 are activated at the same height.
let (mut st, dfvk, birthday, sap_active) = test_with_nu5_birthday_offset::<T>(76); let (mut st, dfvk, birthday, sap_active) =
test_with_nu5_birthday_offset::<T>(76, BlockHash([0; 32]));
// Set up the following situation: // Set up the following situation:
// //