zcash_client_backend: Move the `ShieldedPoolTester` trait from `zcash_client_sqlite`

This commit is contained in:
Kris Nuttycombe 2024-09-06 14:47:52 -06:00
parent 49dffbf6ee
commit 4f5b3efe09
7 changed files with 156 additions and 122 deletions

View File

@ -0,0 +1,90 @@
use std::{cmp::Eq, hash::Hash};
use incrementalmerkletree::Level;
use rand::RngCore;
use shardtree::error::ShardTreeError;
use zcash_keys::{address::Address, keys::UnifiedSpendingKey};
use zcash_primitives::transaction::Transaction;
use zcash_protocol::{
consensus::{self, BlockHeight},
memo::MemoBytes,
value::Zatoshis,
ShieldedProtocol,
};
use crate::{
data_api::{
chain::{CommitmentTreeRoot, ScanSummary},
DecryptedTransaction, InputSource, WalletCommitmentTrees, WalletRead, WalletSummary,
},
wallet::{Note, ReceivedNote},
};
use super::{TestFvk, TestState};
/// Trait that exposes the pool-specific types and operations necessary to run the
/// single-shielded-pool tests on a given pool.
pub trait ShieldedPoolTester {
const SHIELDED_PROTOCOL: ShieldedProtocol;
type Sk;
type Fvk: TestFvk;
type MerkleTreeHash;
type Note;
fn test_account_fvk<Cache, DbT: WalletRead, P: consensus::Parameters>(
st: &TestState<Cache, DbT, P>,
) -> Self::Fvk;
fn usk_to_sk(usk: &UnifiedSpendingKey) -> &Self::Sk;
fn sk(seed: &[u8]) -> Self::Sk;
fn sk_to_fvk(sk: &Self::Sk) -> Self::Fvk;
fn sk_default_address(sk: &Self::Sk) -> Address;
fn fvk_default_address(fvk: &Self::Fvk) -> Address;
fn fvks_equal(a: &Self::Fvk, b: &Self::Fvk) -> bool;
fn random_fvk(mut rng: impl RngCore) -> Self::Fvk {
let sk = {
let mut sk_bytes = vec![0; 32];
rng.fill_bytes(&mut sk_bytes);
Self::sk(&sk_bytes)
};
Self::sk_to_fvk(&sk)
}
fn random_address(rng: impl RngCore) -> Address {
Self::fvk_default_address(&Self::random_fvk(rng))
}
fn empty_tree_leaf() -> Self::MerkleTreeHash;
fn empty_tree_root(level: Level) -> Self::MerkleTreeHash;
fn put_subtree_roots<Cache, DbT: WalletRead + WalletCommitmentTrees, P>(
st: &mut TestState<Cache, DbT, P>,
start_index: u64,
roots: &[CommitmentTreeRoot<Self::MerkleTreeHash>],
) -> Result<(), ShardTreeError<<DbT as WalletCommitmentTrees>::Error>>;
fn next_subtree_index<A: Hash + Eq>(s: &WalletSummary<A>) -> u64;
#[allow(clippy::type_complexity)]
fn select_spendable_notes<Cache, DbT: InputSource + WalletRead, P>(
st: &TestState<Cache, DbT, P>,
account: <DbT as InputSource>::AccountId,
target_value: Zatoshis,
anchor_height: BlockHeight,
exclude: &[DbT::NoteRef],
) -> Result<Vec<ReceivedNote<DbT::NoteRef, Self::Note>>, <DbT as InputSource>::Error>;
fn decrypted_pool_outputs_count<A>(d_tx: &DecryptedTransaction<'_, A>) -> usize;
fn with_decrypted_pool_memos<A>(d_tx: &DecryptedTransaction<'_, A>, f: impl FnMut(&MemoBytes));
fn try_output_recovery<P: consensus::Parameters>(
params: &P,
height: BlockHeight,
tx: &Transaction,
fvk: &Self::Fvk,
) -> Option<(Note, Address, MemoBytes)>;
fn received_note_count(summary: &ScanSummary) -> usize;
}

View File

@ -38,7 +38,7 @@ pub struct DecryptedOutput<Note, AccountId> {
transfer_type: TransferType,
}
impl<Note, AccountId: Copy> DecryptedOutput<Note, AccountId> {
impl<Note, AccountId> DecryptedOutput<Note, AccountId> {
pub fn new(
index: usize,
note: Note,

View File

@ -7,11 +7,11 @@ use std::{
num::{NonZeroU32, NonZeroU8},
};
use incrementalmerkletree::{frontier::Frontier, Level};
use rand_core::RngCore;
use incrementalmerkletree::frontier::Frontier;
use rusqlite::params;
use secrecy::Secret;
use shardtree::error::ShardTreeError;
use zcash_primitives::{
block::BlockHash,
consensus::{BranchId, NetworkUpgrade, Parameters},
@ -31,28 +31,25 @@ use zcash_client_backend::{
address::Address,
data_api::{
self,
chain::{self, ChainState, CommitmentTreeRoot, ScanSummary},
chain::{self, ChainState, CommitmentTreeRoot},
error::Error,
testing::{
input_selector, AddressType, FakeCompactOutput, InitialChainState, TestBuilder,
TestFvk, TestState,
input_selector, pool::ShieldedPoolTester, AddressType, FakeCompactOutput,
InitialChainState, TestBuilder, TestState,
},
wallet::{
decrypt_and_store_transaction,
input_selection::{GreedyInputSelector, GreedyInputSelectorError},
},
Account as _, AccountBirthday, DecryptedTransaction, InputSource, Ratio,
WalletCommitmentTrees, WalletRead, WalletSummary, WalletWrite,
Account as _, AccountBirthday, Ratio, WalletRead, WalletWrite,
},
decrypt_transaction,
fees::{fixed, standard, DustOutputPolicy},
keys::UnifiedSpendingKey,
scanning::ScanError,
wallet::{Note, OvkPolicy, ReceivedNote},
wallet::{Note, OvkPolicy},
zip321::{self, Payment, TransactionRequest},
ShieldedProtocol,
};
use zcash_protocol::consensus::{self, BlockHeight};
use crate::{
error::SqliteClientError,
@ -61,12 +58,13 @@ use crate::{
BlockCache,
},
wallet::{commitment_tree, parse_scope, truncate_to_height},
AccountId, NoteId, ReceivedNoteId,
NoteId, ReceivedNoteId,
};
#[cfg(feature = "transparent-inputs")]
use {
zcash_client_backend::wallet::WalletTransparentOutput,
crate::AccountId,
zcash_client_backend::{data_api::DecryptedTransaction, wallet::WalletTransparentOutput},
zcash_primitives::transaction::{
components::{OutPoint, TxOut},
fees::zip317,
@ -74,77 +72,13 @@ use {
};
#[cfg(feature = "orchard")]
use zcash_client_backend::PoolType;
use {
zcash_client_backend::PoolType,
zcash_protocol::{consensus::BlockHeight, ShieldedProtocol},
};
/// Trait that exposes the pool-specific types and operations necessary to run the
/// single-shielded-pool tests on a given pool.
pub(crate) trait ShieldedPoolTester {
const SHIELDED_PROTOCOL: ShieldedProtocol;
pub(crate) trait ShieldedPoolPersistence {
const TABLES_PREFIX: &'static str;
type Sk;
type Fvk: TestFvk;
type MerkleTreeHash;
type Note;
fn test_account_fvk<Cache, DbT: WalletRead, P: consensus::Parameters>(
st: &TestState<Cache, DbT, P>,
) -> Self::Fvk;
fn usk_to_sk(usk: &UnifiedSpendingKey) -> &Self::Sk;
fn sk(seed: &[u8]) -> Self::Sk;
fn sk_to_fvk(sk: &Self::Sk) -> Self::Fvk;
fn sk_default_address(sk: &Self::Sk) -> Address;
fn fvk_default_address(fvk: &Self::Fvk) -> Address;
fn fvks_equal(a: &Self::Fvk, b: &Self::Fvk) -> bool;
fn random_fvk(mut rng: impl RngCore) -> Self::Fvk {
let sk = {
let mut sk_bytes = vec![0; 32];
rng.fill_bytes(&mut sk_bytes);
Self::sk(&sk_bytes)
};
Self::sk_to_fvk(&sk)
}
fn random_address(rng: impl RngCore) -> Address {
Self::fvk_default_address(&Self::random_fvk(rng))
}
fn empty_tree_leaf() -> Self::MerkleTreeHash;
fn empty_tree_root(level: Level) -> Self::MerkleTreeHash;
fn put_subtree_roots<Cache, DbT: WalletRead + WalletCommitmentTrees, P>(
st: &mut TestState<Cache, DbT, P>,
start_index: u64,
roots: &[CommitmentTreeRoot<Self::MerkleTreeHash>],
) -> Result<(), ShardTreeError<<DbT as WalletCommitmentTrees>::Error>>;
fn next_subtree_index(s: &WalletSummary<AccountId>) -> u64;
#[allow(clippy::type_complexity)]
fn select_spendable_notes<Cache, DbT: InputSource + WalletRead, P>(
st: &TestState<Cache, DbT, P>,
account: <DbT as InputSource>::AccountId,
target_value: NonNegativeAmount,
anchor_height: BlockHeight,
exclude: &[DbT::NoteRef],
) -> Result<Vec<ReceivedNote<DbT::NoteRef, Self::Note>>, <DbT as InputSource>::Error>;
fn decrypted_pool_outputs_count(d_tx: &DecryptedTransaction<'_, AccountId>) -> usize;
fn with_decrypted_pool_memos(
d_tx: &DecryptedTransaction<'_, AccountId>,
f: impl FnMut(&MemoBytes),
);
fn try_output_recovery<P: consensus::Parameters>(
params: &P,
height: BlockHeight,
tx: &Transaction,
fvk: &Self::Fvk,
) -> Option<(Note, Address, MemoBytes)>;
fn received_note_count(summary: &ScanSummary) -> usize;
}
pub(crate) fn send_single_step_proposed_transfer<T: ShieldedPoolTester>() {
@ -1291,7 +1225,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 + ShieldedPoolPersistence>() {
let mut st = TestBuilder::new()
.with_data_store_factory(TestDbFactory)
.with_block_cache(BlockCache::new())

View File

@ -1081,17 +1081,19 @@ mod tests {
Marking, Position, Retention,
};
use shardtree::ShardTree;
use zcash_client_backend::data_api::chain::CommitmentTreeRoot;
use zcash_client_backend::data_api::{
chain::CommitmentTreeRoot, testing::pool::ShieldedPoolTester,
};
use zcash_primitives::consensus::{BlockHeight, Network};
use super::SqliteShardStore;
use crate::{
testing::pool::ShieldedPoolTester,
testing::pool::ShieldedPoolPersistence,
wallet::{init::init_wallet_db, sapling::tests::SaplingPoolTester},
WalletDb,
};
fn new_tree<T: ShieldedPoolTester>(
fn new_tree<T: ShieldedPoolTester + ShieldedPoolPersistence>(
m: usize,
) -> ShardTree<SqliteShardStore<rusqlite::Connection, String, 3>, 4, 3> {
let data_file = NamedTempFile::new().unwrap();
@ -1191,7 +1193,7 @@ mod tests {
put_shard_roots::<SaplingPoolTester>()
}
fn put_shard_roots<T: ShieldedPoolTester>() {
fn put_shard_roots<T: ShieldedPoolTester + ShieldedPoolPersistence>() {
let data_file = NamedTempFile::new().unwrap();
let mut db_data = WalletDb::for_path(data_file.path(), Network::TestNetwork).unwrap();
data_file.keep().unwrap();

View File

@ -387,18 +387,21 @@ pub(crate) fn mark_orchard_note_spent(
#[cfg(test)]
pub(crate) mod tests {
use std::hash::Hash;
use incrementalmerkletree::{Hashable, Level};
use orchard::{
keys::{FullViewingKey, SpendingKey},
note_encryption::OrchardDomain,
tree::MerkleHashOrchard,
};
use shardtree::error::ShardTreeError;
use zcash_client_backend::{
data_api::{
chain::CommitmentTreeRoot, testing::TestState, DecryptedTransaction, InputSource,
WalletCommitmentTrees, WalletRead, WalletSummary,
chain::CommitmentTreeRoot,
testing::{pool::ShieldedPoolTester, TestState},
DecryptedTransaction, InputSource, WalletCommitmentTrees, WalletRead, WalletSummary,
},
wallet::{Note, ReceivedNote},
};
@ -416,15 +419,17 @@ pub(crate) mod tests {
};
use crate::{
testing::{self, pool::ShieldedPoolTester},
testing::{self, pool::ShieldedPoolPersistence},
wallet::sapling::tests::SaplingPoolTester,
ORCHARD_TABLES_PREFIX,
};
pub(crate) struct OrchardPoolTester;
impl ShieldedPoolPersistence for OrchardPoolTester {
const TABLES_PREFIX: &'static str = ORCHARD_TABLES_PREFIX;
}
impl ShieldedPoolTester for OrchardPoolTester {
const SHIELDED_PROTOCOL: ShieldedProtocol = ShieldedProtocol::Orchard;
const TABLES_PREFIX: &'static str = ORCHARD_TABLES_PREFIX;
// const MERKLE_TREE_DEPTH: u8 = {orchard::NOTE_COMMITMENT_TREE_DEPTH as u8};
type Sk = SpendingKey;
@ -491,7 +496,7 @@ pub(crate) mod tests {
.put_orchard_subtree_roots(start_index, roots)
}
fn next_subtree_index(s: &WalletSummary<crate::AccountId>) -> u64 {
fn next_subtree_index<A: Hash + Eq>(s: &WalletSummary<A>) -> u64 {
s.next_orchard_subtree_index()
}
@ -514,14 +519,12 @@ pub(crate) mod tests {
.map(|n| n.take_orchard())
}
fn decrypted_pool_outputs_count(
d_tx: &DecryptedTransaction<'_, crate::AccountId>,
) -> usize {
fn decrypted_pool_outputs_count<A>(d_tx: &DecryptedTransaction<'_, A>) -> usize {
d_tx.orchard_outputs().len()
}
fn with_decrypted_pool_memos(
d_tx: &DecryptedTransaction<'_, crate::AccountId>,
fn with_decrypted_pool_memos<A>(
d_tx: &DecryptedTransaction<'_, A>,
mut f: impl FnMut(&MemoBytes),
) {
for output in d_tx.orchard_outputs() {

View File

@ -400,15 +400,27 @@ pub(crate) fn put_received_note<T: ReceivedSaplingOutput>(
#[cfg(test)]
pub(crate) mod tests {
use std::hash::Hash;
use incrementalmerkletree::{Hashable, Level};
use shardtree::error::ShardTreeError;
use sapling::{
self,
note_encryption::try_sapling_output_recovery,
zip32::{DiversifiableFullViewingKey, ExtendedSpendingKey},
};
use shardtree::error::ShardTreeError;
use zcash_client_backend::{
address::Address,
data_api::{
chain::CommitmentTreeRoot,
testing::{pool::ShieldedPoolTester, TestState},
DecryptedTransaction, InputSource, WalletCommitmentTrees, WalletRead, WalletSummary,
},
keys::UnifiedSpendingKey,
wallet::{Note, ReceivedNote},
ShieldedProtocol,
};
use zcash_primitives::{
consensus::BlockHeight,
memo::MemoBytes,
@ -418,28 +430,19 @@ pub(crate) mod tests {
},
zip32::Scope,
};
use zcash_client_backend::{
address::Address,
data_api::{
chain::CommitmentTreeRoot, testing::TestState, DecryptedTransaction, InputSource,
WalletCommitmentTrees, WalletRead, WalletSummary,
},
keys::UnifiedSpendingKey,
wallet::{Note, ReceivedNote},
ShieldedProtocol,
};
use zcash_protocol::consensus;
use crate::{
testing::{self, pool::ShieldedPoolTester},
AccountId, SAPLING_TABLES_PREFIX,
testing::{self, pool::ShieldedPoolPersistence},
SAPLING_TABLES_PREFIX,
};
pub(crate) struct SaplingPoolTester;
impl ShieldedPoolPersistence for SaplingPoolTester {
const TABLES_PREFIX: &'static str = SAPLING_TABLES_PREFIX;
}
impl ShieldedPoolTester for SaplingPoolTester {
const SHIELDED_PROTOCOL: ShieldedProtocol = ShieldedProtocol::Sapling;
const TABLES_PREFIX: &'static str = SAPLING_TABLES_PREFIX;
// const MERKLE_TREE_DEPTH: u8 = sapling::NOTE_COMMITMENT_TREE_DEPTH;
type Sk = ExtendedSpendingKey;
@ -494,7 +497,7 @@ pub(crate) mod tests {
.put_sapling_subtree_roots(start_index, roots)
}
fn next_subtree_index(s: &WalletSummary<AccountId>) -> u64 {
fn next_subtree_index<A: Hash + Eq>(s: &WalletSummary<A>) -> u64 {
s.next_sapling_subtree_index()
}
@ -517,12 +520,12 @@ pub(crate) mod tests {
.map(|n| n.take_sapling())
}
fn decrypted_pool_outputs_count(d_tx: &DecryptedTransaction<'_, AccountId>) -> usize {
fn decrypted_pool_outputs_count<A>(d_tx: &DecryptedTransaction<'_, A>) -> usize {
d_tx.sapling_outputs().len()
}
fn with_decrypted_pool_memos(
d_tx: &DecryptedTransaction<'_, AccountId>,
fn with_decrypted_pool_memos<A>(
d_tx: &DecryptedTransaction<'_, A>,
mut f: impl FnMut(&MemoBytes),
) {
for output in d_tx.sapling_outputs() {

View File

@ -587,7 +587,10 @@ pub(crate) mod tests {
use zcash_client_backend::data_api::{
chain::{ChainState, CommitmentTreeRoot},
scanning::{spanning_tree::testing::scan_range, ScanPriority},
testing::{AddressType, FakeCompactOutput, InitialChainState, TestBuilder, TestState},
testing::{
pool::ShieldedPoolTester, AddressType, FakeCompactOutput, InitialChainState,
TestBuilder, TestState,
},
AccountBirthday, Ratio, WalletRead, WalletWrite, SAPLING_SHARD_HEIGHT,
};
use zcash_primitives::{
@ -601,7 +604,6 @@ pub(crate) mod tests {
error::SqliteClientError,
testing::{
db::{TestDb, TestDbFactory},
pool::ShieldedPoolTester,
BlockCache,
},
wallet::{