From 49e284424f9fb2cf27e786c452f1ff4319f62eb0 Mon Sep 17 00:00:00 2001 From: Eric Tu Date: Tue, 27 Aug 2024 16:23:46 -0400 Subject: [PATCH 01/14] scan queue --- zcash_client_memory/src/error.rs | 6 +- zcash_client_memory/src/mem_wallet/mod.rs | 1 + .../src/mem_wallet/scanning.rs | 197 ++++++++++++++++++ .../src/mem_wallet/wallet_read.rs | 10 + .../src/mem_wallet/wallet_write.rs | 54 +++-- 5 files changed, 246 insertions(+), 22 deletions(-) create mode 100644 zcash_client_memory/src/mem_wallet/scanning.rs diff --git a/zcash_client_memory/src/error.rs b/zcash_client_memory/src/error.rs index 8548404a56..08bea0e1e1 100644 --- a/zcash_client_memory/src/error.rs +++ b/zcash_client_memory/src/error.rs @@ -1,6 +1,6 @@ use zcash_keys::keys::{AddressGenerationError, DerivationError}; use zcash_primitives::transaction::TxId; -use zcash_protocol::memo; +use zcash_protocol::{consensus::BlockHeight, memo}; use crate::mem_wallet::AccountId; @@ -36,6 +36,10 @@ pub enum Error { CorruptedData(String), #[error("An error occurred while processing an account due to a failure in deriving the account's keys: {0}")] BadAccountData(String), + #[error("Blocks are non sequental")] + NonSequentialBlocks, + #[error("Invalid scan range start {0}, end {1}: {2}")] + InvalidScanRange(BlockHeight, BlockHeight, String), #[error("Other error: {0}")] Other(String), } diff --git a/zcash_client_memory/src/mem_wallet/mod.rs b/zcash_client_memory/src/mem_wallet/mod.rs index 20b8f44dc5..bd72234dda 100644 --- a/zcash_client_memory/src/mem_wallet/mod.rs +++ b/zcash_client_memory/src/mem_wallet/mod.rs @@ -55,6 +55,7 @@ use zcash_client_backend::{data_api::ORCHARD_SHARD_HEIGHT, wallet::WalletOrchard use crate::error::Error; +mod scanning; mod tables; mod wallet_commitment_trees; mod wallet_read; diff --git a/zcash_client_memory/src/mem_wallet/scanning.rs b/zcash_client_memory/src/mem_wallet/scanning.rs new file mode 100644 index 0000000000..57dd097d36 --- /dev/null +++ b/zcash_client_memory/src/mem_wallet/scanning.rs @@ -0,0 +1,197 @@ +#![allow(unused)] +use core::time; +use incrementalmerkletree::{Address, Marking, Position, Retention}; +use sapling::NullifierDerivingKey; +use secrecy::{ExposeSecret, SecretVec}; +use shardtree::{error::ShardTreeError, store::memory::MemoryShardStore, ShardTree}; +use std::{ + cell::RefCell, + cmp::Ordering, + collections::{hash_map::Entry, BTreeMap, BTreeSet, HashMap, HashSet}, + convert::Infallible, + hash::Hash, + num::NonZeroU32, + ops::{Deref, Range}, + path::Iter, + rc::Rc, +}; +use zcash_keys::keys::{AddressGenerationError, DerivationError, UnifiedIncomingViewingKey}; +use zip32::{fingerprint::SeedFingerprint, DiversifierIndex, Scope}; + +use zcash_primitives::{ + block::{self, BlockHash}, + consensus::{BlockHeight, Network}, + transaction::{components::OutPoint, txid, Authorized, Transaction, TransactionData, TxId}, +}; +use zcash_protocol::{ + memo::{self, Memo, MemoBytes}, + value::{ZatBalance, Zatoshis}, + PoolType, + ShieldedProtocol::{self, Orchard, Sapling}, +}; + +use zcash_client_backend::{ + address::UnifiedAddress, + data_api::{ + chain::ChainState, + scanning::{spanning_tree::SpanningTree, ScanPriority}, + Account as _, AccountPurpose, AccountSource, SeedRelevance, SentTransactionOutput, + TransactionDataRequest, TransactionStatus, + }, + keys::{UnifiedAddressRequest, UnifiedFullViewingKey, UnifiedSpendingKey}, + wallet::{ + Note, NoteId, Recipient, WalletSaplingOutput, WalletSpend, WalletTransparentOutput, + WalletTx, + }, +}; + +use zcash_client_backend::data_api::{ + chain::CommitmentTreeRoot, scanning::ScanRange, AccountBirthday, BlockMetadata, + DecryptedTransaction, NullifierQuery, ScannedBlock, SentTransaction, WalletCommitmentTrees, + WalletRead, WalletSummary, WalletWrite, SAPLING_SHARD_HEIGHT, +}; + +use super::AccountId; + +#[cfg(feature = "transparent-inputs")] +use { + zcash_client_backend::wallet::TransparentAddressMetadata, + zcash_primitives::legacy::TransparentAddress, +}; + +#[cfg(feature = "orchard")] +use { + zcash_client_backend::data_api::ORCHARD_SHARD_HEIGHT, + zcash_client_backend::wallet::WalletOrchardOutput, +}; + +use crate::error::Error; +pub(super) const TABLE_SCAN_QUEUE: &str = " +CREATE TABLE scan_queue ( + block_range_start INTEGER NOT NULL, + block_range_end INTEGER NOT NULL, + priority INTEGER NOT NULL, + CONSTRAINT range_start_uniq UNIQUE (block_range_start), + CONSTRAINT range_end_uniq UNIQUE (block_range_end), + CONSTRAINT range_bounds_order CHECK ( + block_range_start < block_range_end + ) +)"; + +/// A queue of scanning ranges. Contains the start and end heights of each range, along with the +/// priority of scanning that range. +pub struct ScanQueue(Vec<(BlockHeight, BlockHeight, ScanPriority)>); + +impl ScanQueue { + pub fn suggest_scan_ranges(&self, min_priority: ScanPriority) -> Vec { + let mut priorities: Vec<_> = self + .0 + .iter() + .filter(|(_, _, p)| *p >= min_priority) + .collect(); + priorities.sort_by(|(_, _, a), (_, _, b)| b.cmp(a)); + + priorities + .into_iter() + .map(|(start, end, priority)| { + let range = Range { + start: *start, + end: *end, + }; + ScanRange::from_parts(range, *priority) + }) + .collect() + } + pub fn insert_queue_entries<'a>( + &mut self, + entries: impl Iterator, + ) -> Result<(), Error> { + for entry in entries { + if entry.block_range().start >= entry.block_range().end { + return Err(Error::InvalidScanRange( + entry.block_range().start, + entry.block_range().end, + "start must be less than end".to_string(), + )); + } + + for (start, end, _) in &self.0 { + if *start == entry.block_range().start || *end == entry.block_range().end { + return Err(Error::InvalidScanRange( + entry.block_range().start, + entry.block_range().end, + "at least part of range is already covered by another range".to_string(), + )); + } + } + + self.0.push(( + entry.block_range().start, + entry.block_range().end, + entry.priority(), + )); + } + Ok(()) + } + pub fn replace_queue_entries( + &mut self, + query_range: &Range, + entries: impl Iterator, + force_rescans: bool, + ) -> Result<(), Error> { + let (to_create, to_delete_ends) = { + let mut rows: Vec<_> = self + .0 + .iter() + .filter(|(start, end, _)| { + // Ignore ranges that do not overlap and are not adjacent to the query range. + !(start > &query_range.end || &query_range.start > end) + }) + .collect(); + rows.sort_by(|(_, end_a, _), (_, end_b, _)| end_a.cmp(end_b)); + + // Iterate over the ranges in the scan queue that overlap the range that we have + // identified as needing to be fully scanned. For each such range add it to the + // spanning tree (these should all be nonoverlapping ranges, but we might coalesce + // some in the process). + let mut to_create: Option = None; + let mut to_delete_ends: Vec = vec![]; + while let Some((start, end, priority)) = rows.into_iter().next() { + let entry = ScanRange::from_parts( + Range { + start: *start, + end: *end, + }, + *priority, + ); + to_delete_ends.push(entry.block_range().end); + to_create = if let Some(cur) = to_create { + Some(cur.insert(entry, force_rescans)) + } else { + Some(SpanningTree::Leaf(entry)) + }; + } + + // Update the tree that we read from the database, or if we didn't find any ranges + // start with the scanned range. + for entry in entries { + to_create = if let Some(cur) = to_create { + Some(cur.insert(entry, force_rescans)) + } else { + Some(SpanningTree::Leaf(entry)) + }; + } + (to_create, to_delete_ends) + }; + + if let Some(tree) = to_create { + self.0.retain(|(_, block_range_end, _)| { + // if the block_range_end is equal to any in to_delete_ends, remove it + !to_delete_ends.contains(block_range_end) + }); + let scan_ranges = tree.into_vec(); + self.insert_queue_entries(scan_ranges.iter()); + } + Ok(()) + } +} diff --git a/zcash_client_memory/src/mem_wallet/wallet_read.rs b/zcash_client_memory/src/mem_wallet/wallet_read.rs index 8b82987696..2bd651b727 100644 --- a/zcash_client_memory/src/mem_wallet/wallet_read.rs +++ b/zcash_client_memory/src/mem_wallet/wallet_read.rs @@ -210,6 +210,16 @@ impl WalletRead for MemoryWalletDb { &self, _min_confirmations: u32, ) -> Result>, Self::Error> { + // let summary = WalletSummary::new( + // account_balances, + // chain_tip_height, + // fully_scanned_height, + // None, + // next_sapling_subtree_index, + // #[cfg(feature = "orchard")] + // next_orchard_subtree_index, + // ); + // Ok(Some(summary)) todo!() } diff --git a/zcash_client_memory/src/mem_wallet/wallet_write.rs b/zcash_client_memory/src/mem_wallet/wallet_write.rs index ae195904d7..b9fb7e4d98 100644 --- a/zcash_client_memory/src/mem_wallet/wallet_write.rs +++ b/zcash_client_memory/src/mem_wallet/wallet_write.rs @@ -117,68 +117,77 @@ impl WalletWrite for MemoryWalletDb { // - Make sure blocks are coming in order. // - Make sure the first block in the sequence is tip + 1? // - Add a check to make sure the blocks are not already in the data store. + let start_height = blocks.first().map(|b| b.height()); + let mut last_scanned_height = None; + for block in blocks.into_iter() { let mut transactions = HashMap::new(); let mut memos = HashMap::new(); + if last_scanned_height + .iter() + .any(|prev| block.height() != *prev + 1) + { + return Err(Error::NonSequentialBlocks); + } + for transaction in block.transactions().iter() { let txid = transaction.txid(); // Mark the Sapling nullifiers of the spent notes as spent in the `sapling_spends` map. - transaction - .sapling_spends() - .iter() - .map(|s| self.mark_sapling_note_spent(*s.nf(), txid)); + for spend in transaction.sapling_spends() { + self.mark_sapling_note_spent(*spend.nf(), txid); + } - #[cfg(feature = "orchard")] // Mark the Orchard nullifiers of the spent notes as spent in the `orchard_spends` map. - transaction - .orchard_spends() - .iter() - .map(|s| self.mark_orchard_note_spent(*s.nf(), txid)); + #[cfg(feature = "orchard")] + for spend in transaction.orchard_spends() { + self.mark_orchard_note_spent(*spend.nf(), txid); + } - transaction.sapling_outputs().iter().map(|o| { + for output in transaction.sapling_outputs() { // Insert the memo into the `memos` map. let note_id = NoteId::new( txid, Sapling, - u16::try_from(o.index()).expect("output indices are representable as u16"), + u16::try_from(output.index()) + .expect("output indices are representable as u16"), ); if let Ok(Some(memo)) = self.get_memo(note_id) { memos.insert(note_id, memo.encode()); } // Check whether this note was spent in a later block range that // we previously scanned. - let spent_in = o + let spent_in = output .nf() .and_then(|nf| self.nullifiers.get(&Nullifier::Sapling(*nf))) .and_then(|(height, tx_idx)| self.tx_locator.get(*height, *tx_idx)) .map(|x| *x); - self.insert_received_sapling_note(note_id, &o, spent_in); - }); + self.insert_received_sapling_note(note_id, &output, spent_in); + } #[cfg(feature = "orchard")] - transaction.orchard_outputs().iter().map(|o| { + for output in transaction.orchard_outputs().iter() { // Insert the memo into the `memos` map. let note_id = NoteId::new( txid, Orchard, - u16::try_from(o.index()).expect("output indices are representable as u16"), + u16::try_from(output.index()) + .expect("output indices are representable as u16"), ); if let Ok(Some(memo)) = self.get_memo(note_id) { memos.insert(note_id, memo.encode()); } // Check whether this note was spent in a later block range that // we previously scanned. - let spent_in = o + let spent_in = output .nf() .and_then(|nf| self.nullifiers.get(&&Nullifier::Orchard(*nf))) .and_then(|(height, tx_idx)| self.tx_locator.get(*height, *tx_idx)) .map(|x| *x); - self.insert_received_orchard_note(note_id, &o, spent_in) - }); - + self.insert_received_orchard_note(note_id, &output, spent_in) + } // Add frontier to the sapling tree self.sapling_tree.insert_frontier( from_state.final_sapling_tree().clone(), @@ -197,7 +206,7 @@ impl WalletWrite for MemoryWalletDb { marking: Marking::Reference, }, ); - + last_scanned_height = Some(block.height()); transactions.insert(txid, transaction.clone()); } @@ -214,10 +223,12 @@ impl WalletWrite for MemoryWalletDb { memos, }; + // Insert transaction metadata into the transaction table transactions .into_iter() .for_each(|(_id, tx)| self.tx_table.put_tx_meta(tx, block.height())); + // Insert the block into the block map self.blocks.insert(block.height(), memory_block); // Add the Sapling commitments to the sapling tree. @@ -240,6 +251,7 @@ impl WalletWrite for MemoryWalletDb { .batch_insert(start_position, block_commitments.orchard.into_iter()); } } + // We can do some pruning of the tx_locator_map here Ok(()) } From 4fcba9980538e2613715f0a4304978cd7f8a17f4 Mon Sep 17 00:00:00 2001 From: Eric Tu Date: Tue, 27 Aug 2024 16:49:17 -0400 Subject: [PATCH 02/14] block_fully_scanned() --- zcash_client_memory/src/mem_wallet/mod.rs | 4 ++ .../src/mem_wallet/scanning.rs | 37 ++++++++++++-- .../src/mem_wallet/wallet_read.rs | 51 +++++++++++++++++-- 3 files changed, 85 insertions(+), 7 deletions(-) diff --git a/zcash_client_memory/src/mem_wallet/mod.rs b/zcash_client_memory/src/mem_wallet/mod.rs index bd72234dda..8d63881682 100644 --- a/zcash_client_memory/src/mem_wallet/mod.rs +++ b/zcash_client_memory/src/mem_wallet/mod.rs @@ -2,6 +2,7 @@ use core::time; use incrementalmerkletree::{Address, Marking, Retention}; use sapling::NullifierDerivingKey; +use scanning::ScanQueue; use secrecy::{ExposeSecret, SecretVec}; use shardtree::{error::ShardTreeError, store::memory::MemoryShardStore, ShardTree}; use std::{ @@ -84,6 +85,8 @@ pub struct MemoryWalletDb { tx_locator: TxLocatorMap, + scan_queue: ScanQueue, + sapling_tree: ShardTree< MemoryShardStore, { SAPLING_SHARD_HEIGHT * 2 }, @@ -110,6 +113,7 @@ impl MemoryWalletDb { nullifiers: NullifierMap::new(), tx_locator: TxLocatorMap::new(), receieved_note_spends: ReceievdNoteSpends::new(), + scan_queue: ScanQueue::new(), } } fn mark_sapling_note_spent(&mut self, nf: sapling::Nullifier, txid: TxId) -> Result<(), Error> { diff --git a/zcash_client_memory/src/mem_wallet/scanning.rs b/zcash_client_memory/src/mem_wallet/scanning.rs index 57dd097d36..67e1b3c22c 100644 --- a/zcash_client_memory/src/mem_wallet/scanning.rs +++ b/zcash_client_memory/src/mem_wallet/scanning.rs @@ -11,7 +11,7 @@ use std::{ convert::Infallible, hash::Hash, num::NonZeroU32, - ops::{Deref, Range}, + ops::{Deref, DerefMut, Range}, path::Iter, rc::Rc, }; @@ -83,6 +83,10 @@ CREATE TABLE scan_queue ( pub struct ScanQueue(Vec<(BlockHeight, BlockHeight, ScanPriority)>); impl ScanQueue { + pub fn new() -> Self { + ScanQueue(Vec::new()) + } + pub fn suggest_scan_ranges(&self, min_priority: ScanPriority) -> Vec { let mut priorities: Vec<_> = self .0 @@ -140,7 +144,7 @@ impl ScanQueue { force_rescans: bool, ) -> Result<(), Error> { let (to_create, to_delete_ends) = { - let mut rows: Vec<_> = self + let mut q_ranges: Vec<_> = self .0 .iter() .filter(|(start, end, _)| { @@ -148,7 +152,7 @@ impl ScanQueue { !(start > &query_range.end || &query_range.start > end) }) .collect(); - rows.sort_by(|(_, end_a, _), (_, end_b, _)| end_a.cmp(end_b)); + q_ranges.sort_by(|(_, end_a, _), (_, end_b, _)| end_a.cmp(end_b)); // Iterate over the ranges in the scan queue that overlap the range that we have // identified as needing to be fully scanned. For each such range add it to the @@ -156,7 +160,9 @@ impl ScanQueue { // some in the process). let mut to_create: Option = None; let mut to_delete_ends: Vec = vec![]; - while let Some((start, end, priority)) = rows.into_iter().next() { + + let mut q_ranges = q_ranges.into_iter(); + while let Some((start, end, priority)) = q_ranges.next() { let entry = ScanRange::from_parts( Range { start: *start, @@ -195,3 +201,26 @@ impl ScanQueue { Ok(()) } } + +impl IntoIterator for ScanQueue { + type Item = (BlockHeight, BlockHeight, ScanPriority); + type IntoIter = as IntoIterator>::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + self.0.into_iter() + } +} + +// We deref to slice so that we can reuse the slice impls +impl Deref for ScanQueue { + type Target = [(BlockHeight, BlockHeight, ScanPriority)]; + + fn deref(&self) -> &Self::Target { + &self.0[..] + } +} +impl DerefMut for ScanQueue { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0[..] + } +} diff --git a/zcash_client_memory/src/mem_wallet/wallet_read.rs b/zcash_client_memory/src/mem_wallet/wallet_read.rs index 2bd651b727..6ffa2032d1 100644 --- a/zcash_client_memory/src/mem_wallet/wallet_read.rs +++ b/zcash_client_memory/src/mem_wallet/wallet_read.rs @@ -18,8 +18,8 @@ use std::ops::Add; use zcash_client_backend::{ address::UnifiedAddress, data_api::{ - chain::ChainState, Account as _, AccountPurpose, AccountSource, SeedRelevance, - TransactionDataRequest, TransactionStatus, + chain::ChainState, scanning::ScanPriority, Account as _, AccountPurpose, AccountSource, + SeedRelevance, TransactionDataRequest, TransactionStatus, }, keys::{UnifiedAddressRequest, UnifiedFullViewingKey, UnifiedSpendingKey}, wallet::{NoteId, WalletSpend, WalletTransparentOutput, WalletTx}, @@ -242,7 +242,52 @@ impl WalletRead for MemoryWalletDb { } fn block_fully_scanned(&self) -> Result, Self::Error> { - todo!() + if let Some(birthday_height) = self.get_wallet_birthday()? { + // We assume that the only way we get a contiguous range of block heights in the `blocks` table + // starting with the birthday block, is if all scanning operations have been performed on those + // blocks. This holds because the `blocks` table is only altered by `WalletDb::put_blocks` via + // `put_block`, and the effective combination of intra-range linear scanning and the nullifier + // map ensures that we discover all wallet-related information within the contiguous range. + // + // We also assume that every contiguous range of block heights in the `blocks` table has a + // single matching entry in the `scan_queue` table with priority "Scanned". This requires no + // bugs in the scan queue update logic, which we have had before. However, a bug here would + // mean that we return a more conservative fully-scanned height, which likely just causes a + // performance regression. + // + // The fully-scanned height is therefore the last height that falls within the first range in + // the scan queue with priority "Scanned". + // SQL query problems. + + let mut scanned_ranges: Vec<_> = self + .scan_queue + .iter() + .filter(|(_, _, p)| p == &ScanPriority::Scanned) + .collect(); + scanned_ranges.sort_by(|(start_a, _, _), (start_b, _, _)| start_a.cmp(start_b)); + if let Some(fully_scanned_height) = + scanned_ranges + .first() + .and_then(|(block_range_start, block_range_end, priority)| { + // If the start of the earliest scanned range is greater than + // the birthday height, then there is an unscanned range between + // the wallet birthday and that range, so there is no fully + // scanned height. + if *block_range_start <= birthday_height { + // Scan ranges are end-exclusive. + Some(*block_range_end - 1) + } else { + None + } + }) + { + self.block_metadata(fully_scanned_height) + } else { + Ok(None) + } + } else { + Ok(None) + } } fn get_max_height_hash(&self) -> Result, Self::Error> { From 7a7386fbeaa14bbc970e553a70c37b22e7f4aaf2 Mon Sep 17 00:00:00 2001 From: Eric Tu Date: Tue, 27 Aug 2024 17:40:27 -0400 Subject: [PATCH 03/14] suggest_scan_ranges --- zcash_client_memory/src/mem_wallet/mod.rs | 9 ++++-- .../src/mem_wallet/wallet_read.rs | 32 ++++++++++++++++--- .../src/mem_wallet/wallet_write.rs | 6 ++++ 3 files changed, 39 insertions(+), 8 deletions(-) diff --git a/zcash_client_memory/src/mem_wallet/mod.rs b/zcash_client_memory/src/mem_wallet/mod.rs index 8d63881682..d1e359246e 100644 --- a/zcash_client_memory/src/mem_wallet/mod.rs +++ b/zcash_client_memory/src/mem_wallet/mod.rs @@ -70,6 +70,12 @@ struct MemoryWalletBlock { // Just the transactions that involve an account in this wallet transactions: HashSet, memos: HashMap, + sapling_commitment_tree_size: Option, + sapling_output_count: Option, + #[cfg(feature = "orchard")] + orchard_commitment_tree_size: Option, + #[cfg(feature = "orchard")] + orchard_action_count: Option, } pub struct MemoryWalletDb { @@ -129,9 +135,6 @@ impl MemoryWalletDb { Ok(()) } - // fn get_account(&self, account_id: AccountId) -> Option<&Account> { - // self.accounts.get(*account_id as usize) - // } fn get_account_mut(&mut self, account_id: AccountId) -> Option<&mut Account> { self.accounts.get_mut(*account_id as usize) } diff --git a/zcash_client_memory/src/mem_wallet/wallet_read.rs b/zcash_client_memory/src/mem_wallet/wallet_read.rs index 6ffa2032d1..f84df8abf5 100644 --- a/zcash_client_memory/src/mem_wallet/wallet_read.rs +++ b/zcash_client_memory/src/mem_wallet/wallet_read.rs @@ -50,7 +50,7 @@ use { }; use super::{Account, AccountId, MemoryWalletDb}; -use crate::error::Error; +use crate::{error::Error, mem_wallet::MemoryWalletBlock}; impl WalletRead for MemoryWalletDb { type Error = Error; @@ -237,8 +237,25 @@ impl WalletRead for MemoryWalletDb { })) } - fn block_metadata(&self, _height: BlockHeight) -> Result, Self::Error> { - todo!() + fn block_metadata(&self, height: BlockHeight) -> Result, Self::Error> { + Ok(self.blocks.get(&height).map(|block| { + let MemoryWalletBlock { + height, + hash, + sapling_commitment_tree_size, + #[cfg(feature = "orchard")] + orchard_commitment_tree_size, + .. + } = block; + // TODO: Deal with legacy sapling trees + BlockMetadata::from_parts( + *height, + *hash, + *sapling_commitment_tree_size, + #[cfg(feature = "orchard")] + *orchard_commitment_tree_size, + ) + })) } fn block_fully_scanned(&self) -> Result, Self::Error> { @@ -295,11 +312,16 @@ impl WalletRead for MemoryWalletDb { } fn block_max_scanned(&self) -> Result, Self::Error> { - todo!() + Ok(self + .blocks + .last_key_value() + .map(|(height, _)| self.block_metadata(*height)) + .transpose()? + .flatten()) } fn suggest_scan_ranges(&self) -> Result, Self::Error> { - Ok(vec![]) + Ok(self.scan_queue.suggest_scan_ranges(ScanPriority::Historic)) } fn get_target_and_anchor_heights( diff --git a/zcash_client_memory/src/mem_wallet/wallet_write.rs b/zcash_client_memory/src/mem_wallet/wallet_write.rs index b9fb7e4d98..54fa0bb5e7 100644 --- a/zcash_client_memory/src/mem_wallet/wallet_write.rs +++ b/zcash_client_memory/src/mem_wallet/wallet_write.rs @@ -221,6 +221,12 @@ impl WalletWrite for MemoryWalletDb { block_time: block.block_time(), transactions: transactions.keys().cloned().collect(), memos, + sapling_commitment_tree_size: Some(block.sapling().final_tree_size()), + sapling_output_count: Some(block.sapling().commitments().len().try_into().unwrap()), + #[cfg(feature = "orchard")] + orchard_commitment_tree_size: Some(block.orchard().final_tree_size()), + #[cfg(feature = "orchard")] + orchard_action_count: Some(block.orchard().commitments().len().try_into().unwrap()), }; // Insert transaction metadata into the transaction table From 59af5a05cd74bd133b210f2334c9dabce1b44db5 Mon Sep 17 00:00:00 2001 From: Eric Tu Date: Tue, 27 Aug 2024 17:56:35 -0400 Subject: [PATCH 04/14] get_max_height_hash --- zcash_client_memory/src/mem_wallet/wallet_read.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/zcash_client_memory/src/mem_wallet/wallet_read.rs b/zcash_client_memory/src/mem_wallet/wallet_read.rs index f84df8abf5..610e423d21 100644 --- a/zcash_client_memory/src/mem_wallet/wallet_read.rs +++ b/zcash_client_memory/src/mem_wallet/wallet_read.rs @@ -308,7 +308,10 @@ impl WalletRead for MemoryWalletDb { } fn get_max_height_hash(&self) -> Result, Self::Error> { - todo!() + Ok(self + .blocks + .last_key_value() + .map(|(height, block)| (*height, block.hash))) } fn block_max_scanned(&self) -> Result, Self::Error> { From 916a170c1e32138907359cffc5785c0d31394127 Mon Sep 17 00:00:00 2001 From: Eric Tu Date: Tue, 27 Aug 2024 18:02:07 -0400 Subject: [PATCH 05/14] more reads --- zcash_client_memory/src/mem_wallet/wallet_read.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/zcash_client_memory/src/mem_wallet/wallet_read.rs b/zcash_client_memory/src/mem_wallet/wallet_read.rs index 610e423d21..7dd5daf9cf 100644 --- a/zcash_client_memory/src/mem_wallet/wallet_read.rs +++ b/zcash_client_memory/src/mem_wallet/wallet_read.rs @@ -224,7 +224,13 @@ impl WalletRead for MemoryWalletDb { } fn chain_height(&self) -> Result, Self::Error> { - todo!() + Ok(self + .scan_queue + .iter() + .max_by(|(_, end_a, _), (_, end_b, _)| end_a.cmp(end_b)) + // Scan ranges are end-exclusive, so we subtract 1 from `max_height` to obtain the + // height of the last known chain tip; + .and_then(|(_, end, _)| Some(end.saturating_sub(1)))) } fn get_block_hash(&self, block_height: BlockHeight) -> Result, Self::Error> { From ba63cace0df93a828b52197797c43c4f5c92132f Mon Sep 17 00:00:00 2001 From: Eric Tu <6364934+ec2@users.noreply.github.com> Date: Tue, 27 Aug 2024 19:03:52 -0400 Subject: [PATCH 06/14] Update zcash_client_memory/src/mem_wallet/wallet_read.rs Co-authored-by: Willem Olding --- zcash_client_memory/src/mem_wallet/wallet_read.rs | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/zcash_client_memory/src/mem_wallet/wallet_read.rs b/zcash_client_memory/src/mem_wallet/wallet_read.rs index 7dd5daf9cf..c25ae8e6b9 100644 --- a/zcash_client_memory/src/mem_wallet/wallet_read.rs +++ b/zcash_client_memory/src/mem_wallet/wallet_read.rs @@ -210,16 +210,6 @@ impl WalletRead for MemoryWalletDb { &self, _min_confirmations: u32, ) -> Result>, Self::Error> { - // let summary = WalletSummary::new( - // account_balances, - // chain_tip_height, - // fully_scanned_height, - // None, - // next_sapling_subtree_index, - // #[cfg(feature = "orchard")] - // next_orchard_subtree_index, - // ); - // Ok(Some(summary)) todo!() } From c722ea4c726c9ac904ff48510ff757bb2962f177 Mon Sep 17 00:00:00 2001 From: Eric Tu Date: Tue, 27 Aug 2024 19:04:43 -0400 Subject: [PATCH 07/14] suggestions --- zcash_client_memory/src/mem_wallet/scanning.rs | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/zcash_client_memory/src/mem_wallet/scanning.rs b/zcash_client_memory/src/mem_wallet/scanning.rs index 67e1b3c22c..5f48f544ee 100644 --- a/zcash_client_memory/src/mem_wallet/scanning.rs +++ b/zcash_client_memory/src/mem_wallet/scanning.rs @@ -66,17 +66,6 @@ use { }; use crate::error::Error; -pub(super) const TABLE_SCAN_QUEUE: &str = " -CREATE TABLE scan_queue ( - block_range_start INTEGER NOT NULL, - block_range_end INTEGER NOT NULL, - priority INTEGER NOT NULL, - CONSTRAINT range_start_uniq UNIQUE (block_range_start), - CONSTRAINT range_end_uniq UNIQUE (block_range_end), - CONSTRAINT range_bounds_order CHECK ( - block_range_start < block_range_end - ) -)"; /// A queue of scanning ranges. Contains the start and end heights of each range, along with the /// priority of scanning that range. From d43fe2d0a056d0c7349e542138d610976f239379 Mon Sep 17 00:00:00 2001 From: Eric Tu Date: Wed, 28 Aug 2024 13:18:40 -0400 Subject: [PATCH 08/14] refactor module structure --- zcash_client_memory/src/error.rs | 4 +- zcash_client_memory/src/lib.rs | 262 ++++++++++- zcash_client_memory/src/mem_wallet/mod.rs | 440 ------------------ zcash_client_memory/src/types/account.rs | 203 ++++++++ zcash_client_memory/src/types/block.rs | 91 ++++ zcash_client_memory/src/types/mod.rs | 13 + zcash_client_memory/src/types/notes.rs | 229 +++++++++ zcash_client_memory/src/types/nullifier.rs | 92 ++++ .../src/{mem_wallet => types}/scanning.rs | 3 +- .../tables.rs => types/transaction.rs} | 210 +-------- .../wallet_commitment_trees.rs | 0 .../src/{mem_wallet => }/wallet_read.rs | 2 +- .../src/{mem_wallet => }/wallet_write.rs | 4 +- 13 files changed, 897 insertions(+), 656 deletions(-) delete mode 100644 zcash_client_memory/src/mem_wallet/mod.rs create mode 100644 zcash_client_memory/src/types/account.rs create mode 100644 zcash_client_memory/src/types/block.rs create mode 100644 zcash_client_memory/src/types/mod.rs create mode 100644 zcash_client_memory/src/types/notes.rs create mode 100644 zcash_client_memory/src/types/nullifier.rs rename zcash_client_memory/src/{mem_wallet => types}/scanning.rs (99%) rename zcash_client_memory/src/{mem_wallet/tables.rs => types/transaction.rs} (51%) rename zcash_client_memory/src/{mem_wallet => }/wallet_commitment_trees.rs (100%) rename zcash_client_memory/src/{mem_wallet => }/wallet_read.rs (99%) rename zcash_client_memory/src/{mem_wallet => }/wallet_write.rs (99%) diff --git a/zcash_client_memory/src/error.rs b/zcash_client_memory/src/error.rs index 08bea0e1e1..a007e13f4d 100644 --- a/zcash_client_memory/src/error.rs +++ b/zcash_client_memory/src/error.rs @@ -2,7 +2,7 @@ use zcash_keys::keys::{AddressGenerationError, DerivationError}; use zcash_primitives::transaction::TxId; use zcash_protocol::{consensus::BlockHeight, memo}; -use crate::mem_wallet::AccountId; +use crate::AccountId; type Type = AddressGenerationError; @@ -16,7 +16,7 @@ pub enum Error { MemoDecryption(memo::Error), #[error("Error deriving key: {0}")] KeyDerivation(DerivationError), - #[error("Unknown ZIP32 derivation ")] + #[error("Unknown ZIP32 derivation")] UnknownZip32Derivation, #[error("Error generating address: {0}")] AddressGeneration(Type), diff --git a/zcash_client_memory/src/lib.rs b/zcash_client_memory/src/lib.rs index 81cca52c82..13d3902d4a 100644 --- a/zcash_client_memory/src/lib.rs +++ b/zcash_client_memory/src/lib.rs @@ -1,2 +1,262 @@ +use core::time; +use incrementalmerkletree::{Address, Marking, Retention}; +use sapling::NullifierDerivingKey; +use scanning::ScanQueue; +use secrecy::{ExposeSecret, SecretVec}; +use shardtree::{error::ShardTreeError, store::memory::MemoryShardStore, ShardTree}; +use std::{ + cmp::Ordering, + collections::{hash_map::Entry, BTreeMap, HashMap, HashSet}, + convert::Infallible, + hash::Hash, + num::NonZeroU32, + ops::Deref, +}; +use zcash_keys::keys::{AddressGenerationError, DerivationError, UnifiedIncomingViewingKey}; +use zip32::{fingerprint::SeedFingerprint, DiversifierIndex, Scope}; + +use zcash_primitives::{ + block::BlockHash, + consensus::{BlockHeight, Network}, + transaction::{components::OutPoint, txid, Authorized, Transaction, TransactionData, TxId}, +}; +use zcash_protocol::{ + memo::{self, Memo, MemoBytes}, + value::{ZatBalance, Zatoshis}, + PoolType, + ShieldedProtocol::{Orchard, Sapling}, +}; + +use zcash_client_backend::{ + address::UnifiedAddress, + data_api::{ + chain::ChainState, Account as _, AccountPurpose, AccountSource, SeedRelevance, + TransactionDataRequest, TransactionStatus, + }, + keys::{UnifiedAddressRequest, UnifiedFullViewingKey, UnifiedSpendingKey}, + proto::service::ShieldedProtocol, + wallet::{Note, NoteId, WalletSaplingOutput, WalletSpend, WalletTransparentOutput, WalletTx}, +}; + +use zcash_client_backend::data_api::{ + chain::CommitmentTreeRoot, scanning::ScanRange, AccountBirthday, BlockMetadata, + DecryptedTransaction, NullifierQuery, ScannedBlock, SentTransaction, WalletCommitmentTrees, + WalletRead, WalletSummary, WalletWrite, SAPLING_SHARD_HEIGHT, +}; + +#[cfg(feature = "transparent-inputs")] +use { + zcash_client_backend::wallet::TransparentAddressMetadata, + zcash_primitives::legacy::TransparentAddress, +}; + +#[cfg(feature = "orchard")] +use zcash_client_backend::{data_api::ORCHARD_SHARD_HEIGHT, wallet::WalletOrchardOutput}; + +use crate::error::Error; mod error; -pub mod mem_wallet; +pub mod types; +pub mod wallet_commitment_trees; +pub mod wallet_read; +pub mod wallet_write; +pub(crate) use types::*; + +/// The ID type for accounts. +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Default)] +pub struct AccountId(u32); + +impl Deref for AccountId { + type Target = u32; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +/// The main in-memory wallet database. Implements all the traits needed to be used as a backend. +pub struct MemoryWalletDb { + network: Network, + accounts: Vec, + blocks: BTreeMap, + + tx_table: TransactionTable, + + received_notes: ReceivedNoteTable, + receieved_note_spends: ReceievdNoteSpends, + nullifiers: NullifierMap, + + tx_locator: TxLocatorMap, + + scan_queue: ScanQueue, + + sapling_tree: ShardTree< + MemoryShardStore, + { SAPLING_SHARD_HEIGHT * 2 }, + SAPLING_SHARD_HEIGHT, + >, + #[cfg(feature = "orchard")] + orchard_tree: ShardTree< + MemoryShardStore, + { ORCHARD_SHARD_HEIGHT * 2 }, + ORCHARD_SHARD_HEIGHT, + >, +} + +impl MemoryWalletDb { + pub fn new(network: Network, max_checkpoints: usize) -> Self { + Self { + network, + accounts: Vec::new(), + blocks: BTreeMap::new(), + sapling_tree: ShardTree::new(MemoryShardStore::empty(), max_checkpoints), + #[cfg(feature = "orchard")] + orchard_tree: ShardTree::new(MemoryShardStore::empty(), max_checkpoints), + tx_table: TransactionTable::new(), + received_notes: ReceivedNoteTable::new(), + nullifiers: NullifierMap::new(), + tx_locator: TxLocatorMap::new(), + receieved_note_spends: ReceievdNoteSpends::new(), + scan_queue: ScanQueue::new(), + } + } + pub(crate) fn mark_sapling_note_spent( + &mut self, + nf: sapling::Nullifier, + txid: TxId, + ) -> Result<(), Error> { + let note_id = self + .received_notes + .0 + .iter() + .filter(|v| v.nullifier() == Some(&Nullifier::Sapling(nf))) + .map(|v| v.note_id()) + .next() + .ok_or_else(|| Error::NoteNotFound)?; + self.receieved_note_spends.insert_spend(note_id, txid); + Ok(()) + } + + pub(crate) fn get_account_mut(&mut self, account_id: AccountId) -> Option<&mut Account> { + self.accounts.get_mut(*account_id as usize) + } + + #[cfg(feature = "orchard")] + pub(crate) fn mark_orchard_note_spent( + &mut self, + nf: orchard::note::Nullifier, + txid: TxId, + ) -> Result<(), Error> { + let note_id = self + .received_notes + .0 + .iter() + .filter(|v| v.nullifier() == Some(&Nullifier::Orchard(nf))) + .map(|v| v.note_id()) + .next() + .ok_or_else(|| Error::NoteNotFound)?; + self.receieved_note_spends.insert_spend(note_id, txid); + Ok(()) + } + + pub(crate) fn max_zip32_account_index( + &self, + seed_fingerprint: &SeedFingerprint, + ) -> Result, Error> { + Ok(self + .accounts + .iter() + .filter_map(|a| match a.source() { + AccountSource::Derived { + seed_fingerprint: sf, + account_index, + } => { + if &sf == seed_fingerprint { + Some(account_index) + } else { + None + } + } + _ => None, + }) + .max()) + } + pub(crate) fn insert_received_sapling_note( + &mut self, + note_id: NoteId, + output: &WalletSaplingOutput, + spent_in: Option, + ) { + self.received_notes + .insert_received_note(ReceivedNote::from_wallet_sapling_output(note_id, output)); + if let Some(spent_in) = spent_in { + self.receieved_note_spends.insert_spend(note_id, spent_in); + } + } + #[cfg(feature = "orchard")] + pub(crate) fn insert_received_orchard_note( + &mut self, + note_id: NoteId, + output: &WalletOrchardOutput, + spent_in: Option, + ) { + self.received_notes + .insert_received_note(ReceivedNote::from_wallet_orchard_output(note_id, output)); + if let Some(spent_in) = spent_in { + self.receieved_note_spends.insert_spend(note_id, spent_in); + } + } + pub(crate) fn insert_sapling_nullifier_map( + &mut self, + block_height: BlockHeight, + new_entries: &[(TxId, u16, Vec)], + ) -> Result<(), Error> { + for (txid, tx_index, nullifiers) in new_entries { + match self.tx_locator.entry((block_height, *tx_index as u32)) { + Entry::Occupied(x) => { + if txid == x.get() { + // This is a duplicate entry + continue; + } else { + return Err(Error::ConflictingTxLocator); + } + } + Entry::Vacant(entry) => { + entry.insert(*txid); + } + } + for nf in nullifiers.iter() { + self.nullifiers + .insert(block_height, *tx_index as u32, Nullifier::Sapling(*nf)); + } + } + Ok(()) + } + + #[cfg(feature = "orchard")] + pub(crate) fn insert_orchard_nullifier_map( + &mut self, + block_height: BlockHeight, + new_entries: &[(TxId, u16, Vec)], + ) -> Result<(), Error> { + for (txid, tx_index, nullifiers) in new_entries { + match self.tx_locator.entry((block_height, *tx_index as u32)) { + Entry::Occupied(x) => { + if txid == x.get() { + // This is a duplicate entry + continue; + } else { + return Err(Error::ConflictingTxLocator); + } + } + Entry::Vacant(entry) => { + entry.insert(*txid); + } + } + for nf in nullifiers.iter() { + self.nullifiers + .insert(block_height, *tx_index as u32, Nullifier::Orchard(*nf)); + } + } + Ok(()) + } +} diff --git a/zcash_client_memory/src/mem_wallet/mod.rs b/zcash_client_memory/src/mem_wallet/mod.rs deleted file mode 100644 index d1e359246e..0000000000 --- a/zcash_client_memory/src/mem_wallet/mod.rs +++ /dev/null @@ -1,440 +0,0 @@ -#![allow(unused)] -use core::time; -use incrementalmerkletree::{Address, Marking, Retention}; -use sapling::NullifierDerivingKey; -use scanning::ScanQueue; -use secrecy::{ExposeSecret, SecretVec}; -use shardtree::{error::ShardTreeError, store::memory::MemoryShardStore, ShardTree}; -use std::{ - cmp::Ordering, - collections::{hash_map::Entry, BTreeMap, HashMap, HashSet}, - convert::Infallible, - hash::Hash, - num::NonZeroU32, - ops::Deref, -}; -use zcash_keys::keys::{AddressGenerationError, DerivationError, UnifiedIncomingViewingKey}; -use zip32::{fingerprint::SeedFingerprint, DiversifierIndex, Scope}; - -use zcash_primitives::{ - block::BlockHash, - consensus::{BlockHeight, Network}, - transaction::{components::OutPoint, txid, Authorized, Transaction, TransactionData, TxId}, -}; -use zcash_protocol::{ - memo::{self, Memo, MemoBytes}, - value::{ZatBalance, Zatoshis}, - PoolType, - ShieldedProtocol::{Orchard, Sapling}, -}; - -use zcash_client_backend::{ - address::UnifiedAddress, - data_api::{ - chain::ChainState, Account as _, AccountPurpose, AccountSource, SeedRelevance, - TransactionDataRequest, TransactionStatus, - }, - keys::{UnifiedAddressRequest, UnifiedFullViewingKey, UnifiedSpendingKey}, - proto::service::ShieldedProtocol, - wallet::{Note, NoteId, WalletSaplingOutput, WalletSpend, WalletTransparentOutput, WalletTx}, -}; - -use zcash_client_backend::data_api::{ - chain::CommitmentTreeRoot, scanning::ScanRange, AccountBirthday, BlockMetadata, - DecryptedTransaction, NullifierQuery, ScannedBlock, SentTransaction, WalletCommitmentTrees, - WalletRead, WalletSummary, WalletWrite, SAPLING_SHARD_HEIGHT, -}; - -#[cfg(feature = "transparent-inputs")] -use { - zcash_client_backend::wallet::TransparentAddressMetadata, - zcash_primitives::legacy::TransparentAddress, -}; - -#[cfg(feature = "orchard")] -use zcash_client_backend::{data_api::ORCHARD_SHARD_HEIGHT, wallet::WalletOrchardOutput}; - -use crate::error::Error; - -mod scanning; -mod tables; -mod wallet_commitment_trees; -mod wallet_read; -mod wallet_write; -use tables::*; - -struct MemoryWalletBlock { - height: BlockHeight, - hash: BlockHash, - block_time: u32, - // Just the transactions that involve an account in this wallet - transactions: HashSet, - memos: HashMap, - sapling_commitment_tree_size: Option, - sapling_output_count: Option, - #[cfg(feature = "orchard")] - orchard_commitment_tree_size: Option, - #[cfg(feature = "orchard")] - orchard_action_count: Option, -} - -pub struct MemoryWalletDb { - network: Network, - accounts: Vec, - blocks: BTreeMap, - - tx_table: TransactionTable, - - received_notes: ReceivedNoteTable, - receieved_note_spends: ReceievdNoteSpends, - nullifiers: NullifierMap, - - tx_locator: TxLocatorMap, - - scan_queue: ScanQueue, - - sapling_tree: ShardTree< - MemoryShardStore, - { SAPLING_SHARD_HEIGHT * 2 }, - SAPLING_SHARD_HEIGHT, - >, - #[cfg(feature = "orchard")] - orchard_tree: ShardTree< - MemoryShardStore, - { ORCHARD_SHARD_HEIGHT * 2 }, - ORCHARD_SHARD_HEIGHT, - >, -} -impl MemoryWalletDb { - pub fn new(network: Network, max_checkpoints: usize) -> Self { - Self { - network, - accounts: Vec::new(), - blocks: BTreeMap::new(), - sapling_tree: ShardTree::new(MemoryShardStore::empty(), max_checkpoints), - #[cfg(feature = "orchard")] - orchard_tree: ShardTree::new(MemoryShardStore::empty(), max_checkpoints), - tx_table: TransactionTable::new(), - received_notes: ReceivedNoteTable::new(), - nullifiers: NullifierMap::new(), - tx_locator: TxLocatorMap::new(), - receieved_note_spends: ReceievdNoteSpends::new(), - scan_queue: ScanQueue::new(), - } - } - fn mark_sapling_note_spent(&mut self, nf: sapling::Nullifier, txid: TxId) -> Result<(), Error> { - let note_id = self - .received_notes - .0 - .iter() - .filter(|v| v.nullifier() == Some(&Nullifier::Sapling(nf))) - .map(|v| v.note_id()) - .next() - .ok_or_else(|| Error::NoteNotFound)?; - self.receieved_note_spends.insert_spend(note_id, txid); - Ok(()) - } - - fn get_account_mut(&mut self, account_id: AccountId) -> Option<&mut Account> { - self.accounts.get_mut(*account_id as usize) - } - - #[cfg(feature = "orchard")] - fn mark_orchard_note_spent( - &mut self, - nf: orchard::note::Nullifier, - txid: TxId, - ) -> Result<(), Error> { - let note_id = self - .received_notes - .0 - .iter() - .filter(|v| v.nullifier() == Some(&Nullifier::Orchard(nf))) - .map(|v| v.note_id()) - .next() - .ok_or_else(|| Error::NoteNotFound)?; - self.receieved_note_spends.insert_spend(note_id, txid); - Ok(()) - } - - fn max_zip32_account_index( - &self, - seed_fingerprint: &SeedFingerprint, - ) -> Result, Error> { - Ok(self - .accounts - .iter() - .filter_map(|a| match a.source() { - AccountSource::Derived { - seed_fingerprint: sf, - account_index, - } => { - if &sf == seed_fingerprint { - Some(account_index) - } else { - None - } - } - _ => None, - }) - .max()) - } - pub fn insert_received_sapling_note( - &mut self, - note_id: NoteId, - output: &WalletSaplingOutput, - spent_in: Option, - ) { - self.received_notes - .insert_received_note(ReceivedNote::from_wallet_sapling_output(note_id, output)); - if let Some(spent_in) = spent_in { - self.receieved_note_spends.insert_spend(note_id, spent_in); - } - } - #[cfg(feature = "orchard")] - pub fn insert_received_orchard_note( - &mut self, - note_id: NoteId, - output: &WalletOrchardOutput, - spent_in: Option, - ) { - self.received_notes - .insert_received_note(ReceivedNote::from_wallet_orchard_output(note_id, output)); - if let Some(spent_in) = spent_in { - self.receieved_note_spends.insert_spend(note_id, spent_in); - } - } - fn insert_sapling_nullifier_map( - &mut self, - block_height: BlockHeight, - new_entries: &[(TxId, u16, Vec)], - ) -> Result<(), Error> { - for (txid, tx_index, nullifiers) in new_entries { - match self.tx_locator.entry((block_height, *tx_index as u32)) { - Entry::Occupied(x) => { - if txid == x.get() { - // This is a duplicate entry - continue; - } else { - return Err(Error::ConflictingTxLocator); - } - } - Entry::Vacant(entry) => { - entry.insert(*txid); - } - } - for nf in nullifiers.iter() { - self.nullifiers - .insert(block_height, *tx_index as u32, Nullifier::Sapling(*nf)); - } - } - Ok(()) - } - - #[cfg(feature = "orchard")] - fn insert_orchard_nullifier_map( - &mut self, - block_height: BlockHeight, - new_entries: &[(TxId, u16, Vec)], - ) -> Result<(), Error> { - for (txid, tx_index, nullifiers) in new_entries { - match self.tx_locator.entry((block_height, *tx_index as u32)) { - Entry::Occupied(x) => { - if txid == x.get() { - // This is a duplicate entry - continue; - } else { - return Err(Error::ConflictingTxLocator); - } - } - Entry::Vacant(entry) => { - entry.insert(*txid); - } - } - for nf in nullifiers.iter() { - self.nullifiers - .insert(block_height, *tx_index as u32, Nullifier::Orchard(*nf)); - } - } - Ok(()) - } -} - -/// The viewing key that an [`Account`] has available to it. -#[derive(Debug, Clone)] -pub(crate) enum ViewingKey { - /// A full viewing key. - /// - /// This is available to derived accounts, as well as accounts directly imported as - /// full viewing keys. - Full(Box), - - /// An incoming viewing key. - /// - /// Accounts that have this kind of viewing key cannot be used in wallet contexts, - /// because they are unable to maintain an accurate balance. - Incoming(Box), -} - -/// The ID type for accounts. -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Default)] -pub struct AccountId(u32); - -impl Deref for AccountId { - type Target = u32; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -/// An account stored in a `zcash_client_sqlite` database. -#[derive(Debug, Clone)] -pub struct Account { - account_id: AccountId, - kind: AccountSource, - viewing_key: ViewingKey, - birthday: AccountBirthday, - purpose: AccountPurpose, // TODO: Remove this. AccountSource should be sufficient. - addresses: BTreeMap, - notes: HashSet, -} - -impl Account { - fn new( - account_id: AccountId, - kind: AccountSource, - viewing_key: ViewingKey, - birthday: AccountBirthday, - purpose: AccountPurpose, - ) -> Result { - let mut acc = Self { - account_id, - kind, - viewing_key, - birthday, - purpose, - addresses: BTreeMap::new(), - notes: HashSet::new(), - }; - let ua_request = acc - .viewing_key - .uivk() - .to_address_request() - .and_then(|ua_request| ua_request.intersect(&UnifiedAddressRequest::all().unwrap())) - .ok_or_else(|| { - Error::AddressGeneration(AddressGenerationError::ShieldedReceiverRequired) - })?; - - let (addr, diversifier_index) = acc.default_address(ua_request)?; - acc.addresses.insert(diversifier_index, addr); - Ok(acc) - } - /// Returns the default Unified Address for the account, - /// along with the diversifier index that generated it. - /// - /// The diversifier index may be non-zero if the Unified Address includes a Sapling - /// receiver, and there was no valid Sapling receiver at diversifier index zero. - pub(crate) fn default_address( - &self, - request: UnifiedAddressRequest, - ) -> Result<(UnifiedAddress, DiversifierIndex), AddressGenerationError> { - self.uivk().default_address(request) - } - - fn birthday(&self) -> &AccountBirthday { - &self.birthday - } - - fn addresses(&self) -> &BTreeMap { - &self.addresses - } - - fn current_address(&self) -> Option<(DiversifierIndex, UnifiedAddress)> { - self.addresses - .last_key_value() - .map(|(diversifier_index, address)| (*diversifier_index, address.clone())) - } - fn kind(&self) -> &AccountSource { - &self.kind - } - fn viewing_key(&self) -> &ViewingKey { - &self.viewing_key - } - fn next_available_address( - &mut self, - request: UnifiedAddressRequest, - ) -> Result, Error> { - match self.ufvk() { - Some(ufvk) => { - let search_from = match self.current_address() { - Some((mut last_diversifier_index, _)) => { - last_diversifier_index - .increment() - .map_err(|_| AddressGenerationError::DiversifierSpaceExhausted)?; - last_diversifier_index - } - None => DiversifierIndex::default(), - }; - let (addr, diversifier_index) = ufvk.find_address(search_from, request)?; - self.addresses.insert(diversifier_index, addr.clone()); - Ok(Some(addr)) - } - None => Ok(None), - } - } -} - -impl zcash_client_backend::data_api::Account for Account { - fn id(&self) -> AccountId { - self.account_id - } - - fn source(&self) -> AccountSource { - self.kind - } - - fn ufvk(&self) -> Option<&UnifiedFullViewingKey> { - self.viewing_key.ufvk() - } - - fn uivk(&self) -> UnifiedIncomingViewingKey { - self.viewing_key.uivk() - } -} - -impl ViewingKey { - fn ufvk(&self) -> Option<&UnifiedFullViewingKey> { - match self { - ViewingKey::Full(ufvk) => Some(ufvk), - ViewingKey::Incoming(_) => None, - } - } - - fn uivk(&self) -> UnifiedIncomingViewingKey { - match self { - ViewingKey::Full(ufvk) => ufvk.as_ref().to_unified_incoming_viewing_key(), - ViewingKey::Incoming(uivk) => uivk.as_ref().clone(), - } - } -} - -impl PartialEq for MemoryWalletBlock { - fn eq(&self, other: &Self) -> bool { - (self.height, self.block_time) == (other.height, other.block_time) - } -} - -impl Eq for MemoryWalletBlock {} - -impl PartialOrd for MemoryWalletBlock { - fn partial_cmp(&self, other: &Self) -> Option { - Some((self.height, self.block_time).cmp(&(other.height, other.block_time))) - } -} - -impl Ord for MemoryWalletBlock { - fn cmp(&self, other: &Self) -> Ordering { - (self.height, self.block_time).cmp(&(other.height, other.block_time)) - } -} diff --git a/zcash_client_memory/src/types/account.rs b/zcash_client_memory/src/types/account.rs new file mode 100644 index 0000000000..4829fdc930 --- /dev/null +++ b/zcash_client_memory/src/types/account.rs @@ -0,0 +1,203 @@ +use crate::{AccountId, ScanQueue}; +use core::time; +use incrementalmerkletree::{Address, Marking, Retention}; +use sapling::NullifierDerivingKey; +use secrecy::{ExposeSecret, SecretVec}; +use shardtree::{error::ShardTreeError, store::memory::MemoryShardStore, ShardTree}; +use std::{ + cmp::Ordering, + collections::{hash_map::Entry, BTreeMap, HashMap, HashSet}, + convert::Infallible, + hash::Hash, + num::NonZeroU32, + ops::Deref, +}; +use zcash_keys::keys::{AddressGenerationError, DerivationError, UnifiedIncomingViewingKey}; +use zip32::{fingerprint::SeedFingerprint, DiversifierIndex, Scope}; + +use zcash_primitives::{ + block::BlockHash, + consensus::{BlockHeight, Network}, + transaction::{components::OutPoint, txid, Authorized, Transaction, TransactionData, TxId}, +}; +use zcash_protocol::{ + memo::{self, Memo, MemoBytes}, + value::{ZatBalance, Zatoshis}, + PoolType, + ShieldedProtocol::{Orchard, Sapling}, +}; + +use zcash_client_backend::{ + address::UnifiedAddress, + data_api::{ + chain::ChainState, Account as _, AccountPurpose, AccountSource, SeedRelevance, + TransactionDataRequest, TransactionStatus, + }, + keys::{UnifiedAddressRequest, UnifiedFullViewingKey, UnifiedSpendingKey}, + proto::service::ShieldedProtocol, + wallet::{Note, NoteId, WalletSaplingOutput, WalletSpend, WalletTransparentOutput, WalletTx}, +}; + +use zcash_client_backend::data_api::{ + chain::CommitmentTreeRoot, scanning::ScanRange, AccountBirthday, BlockMetadata, + DecryptedTransaction, NullifierQuery, ScannedBlock, SentTransaction, WalletCommitmentTrees, + WalletRead, WalletSummary, WalletWrite, SAPLING_SHARD_HEIGHT, +}; + +#[cfg(feature = "transparent-inputs")] +use { + zcash_client_backend::wallet::TransparentAddressMetadata, + zcash_primitives::legacy::TransparentAddress, +}; + +#[cfg(feature = "orchard")] +use zcash_client_backend::{data_api::ORCHARD_SHARD_HEIGHT, wallet::WalletOrchardOutput}; + +use crate::error::Error; + +/// An account stored in a `zcash_client_sqlite` database. +#[derive(Debug, Clone)] +pub struct Account { + account_id: AccountId, + kind: AccountSource, + viewing_key: ViewingKey, + birthday: AccountBirthday, + purpose: AccountPurpose, // TODO: Remove this. AccountSource should be sufficient. + addresses: BTreeMap, + notes: HashSet, +} + +/// The viewing key that an [`Account`] has available to it. +#[derive(Debug, Clone)] +pub(crate) enum ViewingKey { + /// A full viewing key. + /// + /// This is available to derived accounts, as well as accounts directly imported as + /// full viewing keys. + Full(Box), + + /// An incoming viewing key. + /// + /// Accounts that have this kind of viewing key cannot be used in wallet contexts, + /// because they are unable to maintain an accurate balance. + Incoming(Box), +} + +impl Account { + pub(crate) fn new( + account_id: AccountId, + kind: AccountSource, + viewing_key: ViewingKey, + birthday: AccountBirthday, + purpose: AccountPurpose, + ) -> Result { + let mut acc = Self { + account_id, + kind, + viewing_key, + birthday, + purpose, + addresses: BTreeMap::new(), + notes: HashSet::new(), + }; + let ua_request = acc + .viewing_key + .uivk() + .to_address_request() + .and_then(|ua_request| ua_request.intersect(&UnifiedAddressRequest::all().unwrap())) + .ok_or_else(|| { + Error::AddressGeneration(AddressGenerationError::ShieldedReceiverRequired) + })?; + + let (addr, diversifier_index) = acc.default_address(ua_request)?; + acc.addresses.insert(diversifier_index, addr); + Ok(acc) + } + /// Returns the default Unified Address for the account, + /// along with the diversifier index that generated it. + /// + /// The diversifier index may be non-zero if the Unified Address includes a Sapling + /// receiver, and there was no valid Sapling receiver at diversifier index zero. + pub(crate) fn default_address( + &self, + request: UnifiedAddressRequest, + ) -> Result<(UnifiedAddress, DiversifierIndex), AddressGenerationError> { + self.uivk().default_address(request) + } + + pub(crate) fn birthday(&self) -> &AccountBirthday { + &self.birthday + } + + pub(crate) fn addresses(&self) -> &BTreeMap { + &self.addresses + } + + pub(crate) fn current_address(&self) -> Option<(DiversifierIndex, UnifiedAddress)> { + self.addresses + .last_key_value() + .map(|(diversifier_index, address)| (*diversifier_index, address.clone())) + } + pub(crate) fn kind(&self) -> &AccountSource { + &self.kind + } + pub(crate) fn viewing_key(&self) -> &ViewingKey { + &self.viewing_key + } + pub(crate) fn next_available_address( + &mut self, + request: UnifiedAddressRequest, + ) -> Result, Error> { + match self.ufvk() { + Some(ufvk) => { + let search_from = match self.current_address() { + Some((mut last_diversifier_index, _)) => { + last_diversifier_index + .increment() + .map_err(|_| AddressGenerationError::DiversifierSpaceExhausted)?; + last_diversifier_index + } + None => DiversifierIndex::default(), + }; + let (addr, diversifier_index) = ufvk.find_address(search_from, request)?; + self.addresses.insert(diversifier_index, addr.clone()); + Ok(Some(addr)) + } + None => Ok(None), + } + } +} + +impl zcash_client_backend::data_api::Account for Account { + fn id(&self) -> AccountId { + self.account_id + } + + fn source(&self) -> AccountSource { + self.kind + } + + fn ufvk(&self) -> Option<&UnifiedFullViewingKey> { + self.viewing_key.ufvk() + } + + fn uivk(&self) -> UnifiedIncomingViewingKey { + self.viewing_key.uivk() + } +} + +impl ViewingKey { + fn ufvk(&self) -> Option<&UnifiedFullViewingKey> { + match self { + ViewingKey::Full(ufvk) => Some(ufvk), + ViewingKey::Incoming(_) => None, + } + } + + fn uivk(&self) -> UnifiedIncomingViewingKey { + match self { + ViewingKey::Full(ufvk) => ufvk.as_ref().to_unified_incoming_viewing_key(), + ViewingKey::Incoming(uivk) => uivk.as_ref().clone(), + } + } +} diff --git a/zcash_client_memory/src/types/block.rs b/zcash_client_memory/src/types/block.rs new file mode 100644 index 0000000000..63a131a2b0 --- /dev/null +++ b/zcash_client_memory/src/types/block.rs @@ -0,0 +1,91 @@ +use core::time; +use incrementalmerkletree::{Address, Marking, Retention}; +use sapling::NullifierDerivingKey; +use secrecy::{ExposeSecret, SecretVec}; +use shardtree::{error::ShardTreeError, store::memory::MemoryShardStore, ShardTree}; +use std::{ + cmp::Ordering, + collections::{hash_map::Entry, BTreeMap, HashMap, HashSet}, + convert::Infallible, + hash::Hash, + num::NonZeroU32, + ops::Deref, +}; +use zcash_keys::keys::{AddressGenerationError, DerivationError, UnifiedIncomingViewingKey}; +use zip32::{fingerprint::SeedFingerprint, DiversifierIndex, Scope}; + +use zcash_primitives::{ + block::BlockHash, + consensus::{BlockHeight, Network}, + transaction::{components::OutPoint, txid, Authorized, Transaction, TransactionData, TxId}, +}; +use zcash_protocol::{ + memo::{self, Memo, MemoBytes}, + value::{ZatBalance, Zatoshis}, + PoolType, + ShieldedProtocol::{Orchard, Sapling}, +}; + +use zcash_client_backend::{ + address::UnifiedAddress, + data_api::{ + chain::ChainState, Account as _, AccountPurpose, AccountSource, SeedRelevance, + TransactionDataRequest, TransactionStatus, + }, + keys::{UnifiedAddressRequest, UnifiedFullViewingKey, UnifiedSpendingKey}, + proto::service::ShieldedProtocol, + wallet::{Note, NoteId, WalletSaplingOutput, WalletSpend, WalletTransparentOutput, WalletTx}, +}; + +use zcash_client_backend::data_api::{ + chain::CommitmentTreeRoot, scanning::ScanRange, AccountBirthday, BlockMetadata, + DecryptedTransaction, NullifierQuery, ScannedBlock, SentTransaction, WalletCommitmentTrees, + WalletRead, WalletSummary, WalletWrite, SAPLING_SHARD_HEIGHT, +}; + +#[cfg(feature = "transparent-inputs")] +use { + zcash_client_backend::wallet::TransparentAddressMetadata, + zcash_primitives::legacy::TransparentAddress, +}; + +#[cfg(feature = "orchard")] +use zcash_client_backend::{data_api::ORCHARD_SHARD_HEIGHT, wallet::WalletOrchardOutput}; + +use crate::error::Error; + +/// Internal wallet representation of a Block. +pub(crate) struct MemoryWalletBlock { + pub(crate) height: BlockHeight, + pub(crate) hash: BlockHash, + pub(crate) block_time: u32, + // Just the transactions that involve an account in this wallet + pub(crate) transactions: HashSet, + pub(crate) memos: HashMap, + pub(crate) sapling_commitment_tree_size: Option, + pub(crate) sapling_output_count: Option, + #[cfg(feature = "orchard")] + pub(crate) orchard_commitment_tree_size: Option, + #[cfg(feature = "orchard")] + pub(crate) orchard_action_count: Option, +} + +impl PartialEq for MemoryWalletBlock { + fn eq(&self, other: &Self) -> bool { + (self.height, self.block_time) == (other.height, other.block_time) + } +} + +impl Eq for MemoryWalletBlock {} + +impl PartialOrd for MemoryWalletBlock { + fn partial_cmp(&self, other: &Self) -> Option { + Some((self.height, self.block_time).cmp(&(other.height, other.block_time))) + } +} + +impl Ord for MemoryWalletBlock { + fn cmp(&self, other: &Self) -> Ordering { + (self.height, self.block_time).cmp(&(other.height, other.block_time)) + } +} diff --git a/zcash_client_memory/src/types/mod.rs b/zcash_client_memory/src/types/mod.rs new file mode 100644 index 0000000000..1319f2c8a5 --- /dev/null +++ b/zcash_client_memory/src/types/mod.rs @@ -0,0 +1,13 @@ +pub(crate) mod account; +pub(crate) mod block; +pub(crate) mod notes; +pub(crate) mod nullifier; +pub(crate) mod scanning; +pub(crate) mod transaction; + +pub(crate) use account::*; +pub(crate) use block::*; +pub(crate) use notes::*; +pub(crate) use nullifier::*; +pub(crate) use scanning::*; +pub(crate) use transaction::*; diff --git a/zcash_client_memory/src/types/notes.rs b/zcash_client_memory/src/types/notes.rs new file mode 100644 index 0000000000..a0aa9674cc --- /dev/null +++ b/zcash_client_memory/src/types/notes.rs @@ -0,0 +1,229 @@ +use core::time; +use incrementalmerkletree::{Address, Marking, Position, Retention}; +use sapling::NullifierDerivingKey; +use secrecy::{ExposeSecret, SecretVec}; +use shardtree::{error::ShardTreeError, store::memory::MemoryShardStore, ShardTree}; +use std::{ + cell::RefCell, + cmp::Ordering, + collections::{hash_map::Entry, BTreeMap, BTreeSet, HashMap, HashSet}, + convert::Infallible, + hash::Hash, + num::NonZeroU32, + ops::Deref, + rc::Rc, +}; +use zcash_keys::keys::{AddressGenerationError, DerivationError, UnifiedIncomingViewingKey}; +use zip32::{fingerprint::SeedFingerprint, DiversifierIndex, Scope}; + +use zcash_primitives::{ + block::BlockHash, + consensus::{BlockHeight, Network}, + transaction::{components::OutPoint, txid, Authorized, Transaction, TransactionData, TxId}, +}; +use zcash_protocol::{ + memo::{self, Memo, MemoBytes}, + value::{ZatBalance, Zatoshis}, + PoolType, + ShieldedProtocol::{self, Orchard, Sapling}, +}; + +use zcash_client_backend::{ + address::UnifiedAddress, + data_api::{ + chain::ChainState, Account as _, AccountPurpose, AccountSource, SeedRelevance, + SentTransactionOutput, TransactionDataRequest, TransactionStatus, + }, + keys::{UnifiedAddressRequest, UnifiedFullViewingKey, UnifiedSpendingKey}, + wallet::{ + Note, NoteId, Recipient, WalletSaplingOutput, WalletSpend, WalletTransparentOutput, + WalletTx, + }, +}; + +use zcash_client_backend::data_api::{ + chain::CommitmentTreeRoot, scanning::ScanRange, AccountBirthday, BlockMetadata, + DecryptedTransaction, NullifierQuery, ScannedBlock, SentTransaction, WalletCommitmentTrees, + WalletRead, WalletSummary, WalletWrite, SAPLING_SHARD_HEIGHT, +}; + +use crate::AccountId; + +#[cfg(feature = "transparent-inputs")] +use { + zcash_client_backend::wallet::TransparentAddressMetadata, + zcash_primitives::legacy::TransparentAddress, +}; + +#[cfg(feature = "orchard")] +use { + zcash_client_backend::data_api::ORCHARD_SHARD_HEIGHT, + zcash_client_backend::wallet::WalletOrchardOutput, +}; + +use crate::{error::Error, Nullifier}; + +/// Keeps track of notes that are spent in which transaction +pub(crate) struct ReceievdNoteSpends(HashMap); + +impl ReceievdNoteSpends { + pub fn new() -> Self { + Self(HashMap::new()) + } + pub fn insert_spend(&mut self, note_id: NoteId, txid: TxId) -> Option { + self.0.insert(note_id, txid) + } +} + +/// A note that has been received by the wallet +/// TODO: Instead of Vec, perhaps we should identify by some unique ID +pub(crate) struct ReceivedNoteTable(pub Vec); + +pub(crate) struct ReceivedNote { + // Uniquely identifies this note + pub(crate) note_id: NoteId, + pub(crate) txid: TxId, + // output_index: sapling, action_index: orchard + pub(crate) output_index: u32, + pub(crate) account_id: AccountId, + //sapling: (diversifier, value, rcm) orchard: (diversifier, value, rho, rseed) + pub(crate) note: Note, + pub(crate) nf: Option, + pub(crate) is_change: bool, + pub(crate) memo: Memo, + pub(crate) commitment_tree_position: Option, + pub(crate) recipient_key_scope: Option, +} +impl ReceivedNote { + pub fn pool(&self) -> PoolType { + match self.note { + Note::Sapling { .. } => PoolType::SAPLING, + #[cfg(feature = "orchard")] + Note::Orchard { .. } => PoolType::ORCHARD, + } + } + pub fn account_id(&self) -> AccountId { + self.account_id + } + pub fn nullifier(&self) -> Option<&Nullifier> { + self.nf.as_ref() + } + pub fn txid(&self) -> TxId { + self.txid + } + pub fn note_id(&self) -> NoteId { + self.note_id + } + pub fn from_sent_tx_output( + txid: TxId, + output: &SentTransactionOutput, + ) -> Result { + match output.recipient() { + Recipient::InternalAccount { + receiving_account, + note: Note::Sapling(note), + .. + } => Ok(ReceivedNote { + note_id: NoteId::new(txid, Sapling, output.output_index() as u16), + txid: txid, + output_index: output.output_index() as u32, + account_id: *receiving_account, + note: Note::Sapling(note.clone()), + nf: None, + is_change: true, + memo: output.memo().map(|m| Memo::try_from(m).unwrap()).unwrap(), + commitment_tree_position: None, + recipient_key_scope: Some(Scope::Internal), + }), + #[cfg(feature = "orchard")] + Recipient::InternalAccount { + receiving_account, + note: Note::Orchard(note), + .. + } => Ok(ReceivedNote { + note_id: NoteId::new(txid, Orchard, output.output_index() as u16), + txid: txid, + output_index: output.output_index() as u32, + account_id: *receiving_account, + note: Note::Orchard(note.clone()), + nf: None, + is_change: true, + memo: output.memo().map(|m| Memo::try_from(m).unwrap()).unwrap(), + commitment_tree_position: None, + recipient_key_scope: Some(Scope::Internal), + }), + _ => Err(Error::Other( + "Recipient is not an internal shielded account".to_owned(), + )), + } + } + pub fn from_wallet_sapling_output( + note_id: NoteId, + output: &WalletSaplingOutput, + ) -> Self { + ReceivedNote { + note_id, + txid: *note_id.txid(), + output_index: output.index() as u32, + account_id: *output.account_id(), + note: Note::Sapling(output.note().clone()), + nf: output.nf().map(|nf| Nullifier::Sapling(*nf)), + is_change: output.is_change(), + memo: Memo::Empty, + commitment_tree_position: Some(output.note_commitment_tree_position()), + recipient_key_scope: output.recipient_key_scope(), + } + } + #[cfg(feature = "orchard")] + pub fn from_wallet_orchard_output( + note_id: NoteId, + output: &WalletOrchardOutput, + ) -> Self { + ReceivedNote { + note_id, + txid: *note_id.txid(), + output_index: output.index() as u32, + account_id: *output.account_id(), + note: Note::Orchard(output.note().clone()), + nf: output.nf().map(|nf| Nullifier::Orchard(*nf)), + is_change: output.is_change(), + memo: Memo::Empty, + commitment_tree_position: Some(output.note_commitment_tree_position()), + recipient_key_scope: output.recipient_key_scope(), + } + } +} + +impl ReceivedNoteTable { + pub fn new() -> Self { + Self(Vec::new()) + } + + pub fn get_sapling_nullifiers( + &self, + ) -> impl Iterator + '_ { + self.0.iter().filter_map(|entry| { + if let Some(Nullifier::Sapling(nf)) = entry.nullifier() { + Some((entry.account_id(), entry.txid(), *nf)) + } else { + None + } + }) + } + #[cfg(feature = "orchard")] + pub fn get_orchard_nullifiers( + &self, + ) -> impl Iterator + '_ { + self.0.iter().filter_map(|entry| { + if let Some(Nullifier::Orchard(nf)) = entry.nullifier() { + Some((entry.account_id(), entry.txid(), *nf)) + } else { + None + } + }) + } + + pub fn insert_received_note(&mut self, note: ReceivedNote) { + self.0.push(note); + } +} diff --git a/zcash_client_memory/src/types/nullifier.rs b/zcash_client_memory/src/types/nullifier.rs new file mode 100644 index 0000000000..cea9364d7b --- /dev/null +++ b/zcash_client_memory/src/types/nullifier.rs @@ -0,0 +1,92 @@ +use core::time; +use incrementalmerkletree::{Address, Marking, Position, Retention}; +use sapling::NullifierDerivingKey; +use secrecy::{ExposeSecret, SecretVec}; +use shardtree::{error::ShardTreeError, store::memory::MemoryShardStore, ShardTree}; +use std::{ + cell::RefCell, + cmp::Ordering, + collections::{hash_map::Entry, BTreeMap, BTreeSet, HashMap, HashSet}, + convert::Infallible, + hash::Hash, + num::NonZeroU32, + ops::Deref, + rc::Rc, +}; +use zcash_keys::keys::{AddressGenerationError, DerivationError, UnifiedIncomingViewingKey}; +use zip32::{fingerprint::SeedFingerprint, DiversifierIndex, Scope}; + +use zcash_primitives::{ + block::BlockHash, + consensus::{BlockHeight, Network}, + transaction::{components::OutPoint, txid, Authorized, Transaction, TransactionData, TxId}, +}; +use zcash_protocol::{ + memo::{self, Memo, MemoBytes}, + value::{ZatBalance, Zatoshis}, + PoolType, + ShieldedProtocol::{self, Orchard, Sapling}, +}; + +use zcash_client_backend::{ + address::UnifiedAddress, + data_api::{ + chain::ChainState, Account as _, AccountPurpose, AccountSource, SeedRelevance, + SentTransactionOutput, TransactionDataRequest, TransactionStatus, + }, + keys::{UnifiedAddressRequest, UnifiedFullViewingKey, UnifiedSpendingKey}, + wallet::{ + Note, NoteId, Recipient, WalletSaplingOutput, WalletSpend, WalletTransparentOutput, + WalletTx, + }, +}; + +use zcash_client_backend::data_api::{ + chain::CommitmentTreeRoot, scanning::ScanRange, AccountBirthday, BlockMetadata, + DecryptedTransaction, NullifierQuery, ScannedBlock, SentTransaction, WalletCommitmentTrees, + WalletRead, WalletSummary, WalletWrite, SAPLING_SHARD_HEIGHT, +}; + +/// Maps a block height and transaction (i.e. transaction locator) index to a nullifier. +pub(crate) struct NullifierMap(BTreeMap); + +impl NullifierMap { + pub fn new() -> Self { + Self(BTreeMap::new()) + } + pub fn insert(&mut self, height: BlockHeight, index: u32, nullifier: Nullifier) { + self.0.insert(nullifier, (height, index)); + } + + pub fn get(&self, nullifier: &Nullifier) -> Option<&(BlockHeight, u32)> { + self.0.get(nullifier) + } +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] +pub(crate) enum Nullifier { + #[cfg(feature = "orchard")] + Orchard(orchard::note::Nullifier), + Sapling(sapling::Nullifier), +} + +impl Nullifier { + pub(crate) fn pool(&self) -> PoolType { + match self { + #[cfg(feature = "orchard")] + Nullifier::Orchard(_) => PoolType::ORCHARD, + Nullifier::Sapling(_) => PoolType::SAPLING, + } + } +} +#[cfg(feature = "orchard")] +impl From for Nullifier { + fn from(n: orchard::note::Nullifier) -> Self { + Nullifier::Orchard(n) + } +} +impl From for Nullifier { + fn from(n: sapling::Nullifier) -> Self { + Nullifier::Sapling(n) + } +} diff --git a/zcash_client_memory/src/mem_wallet/scanning.rs b/zcash_client_memory/src/types/scanning.rs similarity index 99% rename from zcash_client_memory/src/mem_wallet/scanning.rs rename to zcash_client_memory/src/types/scanning.rs index 5f48f544ee..f1365b4c1a 100644 --- a/zcash_client_memory/src/mem_wallet/scanning.rs +++ b/zcash_client_memory/src/types/scanning.rs @@ -1,4 +1,3 @@ -#![allow(unused)] use core::time; use incrementalmerkletree::{Address, Marking, Position, Retention}; use sapling::NullifierDerivingKey; @@ -51,7 +50,7 @@ use zcash_client_backend::data_api::{ WalletRead, WalletSummary, WalletWrite, SAPLING_SHARD_HEIGHT, }; -use super::AccountId; +use crate::AccountId; #[cfg(feature = "transparent-inputs")] use { diff --git a/zcash_client_memory/src/mem_wallet/tables.rs b/zcash_client_memory/src/types/transaction.rs similarity index 51% rename from zcash_client_memory/src/mem_wallet/tables.rs rename to zcash_client_memory/src/types/transaction.rs index c16ca4d229..5caa96951b 100644 --- a/zcash_client_memory/src/mem_wallet/tables.rs +++ b/zcash_client_memory/src/types/transaction.rs @@ -1,4 +1,3 @@ -#![allow(unused)] use core::time; use incrementalmerkletree::{Address, Marking, Position, Retention}; use sapling::NullifierDerivingKey; @@ -48,7 +47,7 @@ use zcash_client_backend::data_api::{ WalletRead, WalletSummary, WalletWrite, SAPLING_SHARD_HEIGHT, }; -use super::AccountId; +use crate::AccountId; #[cfg(feature = "transparent-inputs")] use { @@ -62,35 +61,11 @@ use { zcash_client_backend::wallet::WalletOrchardOutput, }; -use crate::error::Error; +use crate::{error::Error, nullifier::Nullifier}; /// Maps a block height and transaction index to a transaction ID. pub struct TxLocatorMap(HashMap<(BlockHeight, u32), TxId>); -/// Maps a block height and transaction (i.e. transaction locator) index to a nullifier. -pub struct NullifierMap(BTreeMap); - -/// Keeps track of notes that are spent in which transaction -pub struct ReceievdNoteSpends(HashMap); - -pub struct ReceivedNoteTable(pub Vec); - -pub struct ReceivedNote { - // Uniquely identifies this note - pub note_id: NoteId, - pub txid: TxId, - // output_index: sapling, action_index: orchard - pub output_index: u32, - pub account_id: AccountId, - //sapling: (diversifier, value, rcm) orchard: (diversifier, value, rho, rseed) - pub note: Note, - pub nf: Option, - pub is_change: bool, - pub memo: Memo, - pub commitment_tree_position: Option, - pub recipient_key_scope: Option, -} - /// A table of received notes. Corresponds to sapling_received_notes and orchard_received_notes tables. pub struct TransactionEntry { // created: String, @@ -210,140 +185,6 @@ impl TransactionTable { } } -impl ReceivedNote { - pub fn pool(&self) -> PoolType { - match self.note { - Note::Sapling { .. } => PoolType::SAPLING, - #[cfg(feature = "orchard")] - Note::Orchard { .. } => PoolType::ORCHARD, - } - } - pub fn account_id(&self) -> AccountId { - self.account_id - } - pub fn nullifier(&self) -> Option<&Nullifier> { - self.nf.as_ref() - } - pub fn txid(&self) -> TxId { - self.txid - } - pub fn note_id(&self) -> NoteId { - self.note_id - } - pub fn from_sent_tx_output( - txid: TxId, - output: &SentTransactionOutput, - ) -> Result { - match output.recipient() { - Recipient::InternalAccount { - receiving_account, - note: Note::Sapling(note), - .. - } => Ok(ReceivedNote { - note_id: NoteId::new(txid, Sapling, output.output_index() as u16), - txid: txid, - output_index: output.output_index() as u32, - account_id: *receiving_account, - note: Note::Sapling(note.clone()), - nf: None, - is_change: true, - memo: output.memo().map(|m| Memo::try_from(m).unwrap()).unwrap(), - commitment_tree_position: None, - recipient_key_scope: Some(Scope::Internal), - }), - #[cfg(feature = "orchard")] - Recipient::InternalAccount { - receiving_account, - note: Note::Orchard(note), - .. - } => Ok(ReceivedNote { - note_id: NoteId::new(txid, Orchard, output.output_index() as u16), - txid: txid, - output_index: output.output_index() as u32, - account_id: *receiving_account, - note: Note::Orchard(note.clone()), - nf: None, - is_change: true, - memo: output.memo().map(|m| Memo::try_from(m).unwrap()).unwrap(), - commitment_tree_position: None, - recipient_key_scope: Some(Scope::Internal), - }), - _ => Err(Error::Other( - "Recipient is not an internal shielded account".to_owned(), - )), - } - } - pub fn from_wallet_sapling_output( - note_id: NoteId, - output: &WalletSaplingOutput, - ) -> Self { - ReceivedNote { - note_id, - txid: *note_id.txid(), - output_index: output.index() as u32, - account_id: *output.account_id(), - note: Note::Sapling(output.note().clone()), - nf: output.nf().map(|nf| Nullifier::Sapling(*nf)), - is_change: output.is_change(), - memo: Memo::Empty, - commitment_tree_position: Some(output.note_commitment_tree_position()), - recipient_key_scope: output.recipient_key_scope(), - } - } - #[cfg(feature = "orchard")] - pub fn from_wallet_orchard_output( - note_id: NoteId, - output: &WalletOrchardOutput, - ) -> Self { - ReceivedNote { - note_id, - txid: *note_id.txid(), - output_index: output.index() as u32, - account_id: *output.account_id(), - note: Note::Orchard(output.note().clone()), - nf: output.nf().map(|nf| Nullifier::Orchard(*nf)), - is_change: output.is_change(), - memo: Memo::Empty, - commitment_tree_position: Some(output.note_commitment_tree_position()), - recipient_key_scope: output.recipient_key_scope(), - } - } -} - -impl ReceivedNoteTable { - pub fn new() -> Self { - Self(Vec::new()) - } - - pub fn get_sapling_nullifiers( - &self, - ) -> impl Iterator + '_ { - self.0.iter().filter_map(|entry| { - if let Some(Nullifier::Sapling(nf)) = entry.nullifier() { - Some((entry.account_id(), entry.txid(), *nf)) - } else { - None - } - }) - } - #[cfg(feature = "orchard")] - pub fn get_orchard_nullifiers( - &self, - ) -> impl Iterator + '_ { - self.0.iter().filter_map(|entry| { - if let Some(Nullifier::Orchard(nf)) = entry.nullifier() { - Some((entry.account_id(), entry.txid(), *nf)) - } else { - None - } - }) - } - - pub fn insert_received_note(&mut self, note: ReceivedNote) { - self.0.push(note); - } -} - impl TransactionTable { pub fn get(&self, txid: &TxId) -> Option<&TransactionEntry> { self.0.get(txid) @@ -357,18 +198,7 @@ impl TransactionTable { self.0.remove(txid) } } -impl NullifierMap { - pub fn new() -> Self { - Self(BTreeMap::new()) - } - pub fn insert(&mut self, height: BlockHeight, index: u32, nullifier: Nullifier) { - self.0.insert(nullifier, (height, index)); - } - pub fn get(&self, nullifier: &Nullifier) -> Option<&(BlockHeight, u32)> { - self.0.get(nullifier) - } -} impl TxLocatorMap { pub fn new() -> Self { Self(HashMap::new()) @@ -384,39 +214,3 @@ impl TxLocatorMap { self.0.entry(k) } } -impl ReceievdNoteSpends { - pub fn new() -> Self { - Self(HashMap::new()) - } - pub fn insert_spend(&mut self, note_id: NoteId, txid: TxId) -> Option { - self.0.insert(note_id, txid) - } -} - -#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] -pub enum Nullifier { - #[cfg(feature = "orchard")] - Orchard(orchard::note::Nullifier), - Sapling(sapling::Nullifier), -} - -impl Nullifier { - pub fn pool(&self) -> PoolType { - match self { - #[cfg(feature = "orchard")] - Nullifier::Orchard(_) => PoolType::ORCHARD, - Nullifier::Sapling(_) => PoolType::SAPLING, - } - } -} -#[cfg(feature = "orchard")] -impl From for Nullifier { - fn from(n: orchard::note::Nullifier) -> Self { - Nullifier::Orchard(n) - } -} -impl From for Nullifier { - fn from(n: sapling::Nullifier) -> Self { - Nullifier::Sapling(n) - } -} diff --git a/zcash_client_memory/src/mem_wallet/wallet_commitment_trees.rs b/zcash_client_memory/src/wallet_commitment_trees.rs similarity index 100% rename from zcash_client_memory/src/mem_wallet/wallet_commitment_trees.rs rename to zcash_client_memory/src/wallet_commitment_trees.rs diff --git a/zcash_client_memory/src/mem_wallet/wallet_read.rs b/zcash_client_memory/src/wallet_read.rs similarity index 99% rename from zcash_client_memory/src/mem_wallet/wallet_read.rs rename to zcash_client_memory/src/wallet_read.rs index c25ae8e6b9..c5136c9b81 100644 --- a/zcash_client_memory/src/mem_wallet/wallet_read.rs +++ b/zcash_client_memory/src/wallet_read.rs @@ -50,7 +50,7 @@ use { }; use super::{Account, AccountId, MemoryWalletDb}; -use crate::{error::Error, mem_wallet::MemoryWalletBlock}; +use crate::{error::Error, MemoryWalletBlock}; impl WalletRead for MemoryWalletDb { type Error = Error; diff --git a/zcash_client_memory/src/mem_wallet/wallet_write.rs b/zcash_client_memory/src/wallet_write.rs similarity index 99% rename from zcash_client_memory/src/mem_wallet/wallet_write.rs rename to zcash_client_memory/src/wallet_write.rs index 54fa0bb5e7..e434a07bc9 100644 --- a/zcash_client_memory/src/mem_wallet/wallet_write.rs +++ b/zcash_client_memory/src/wallet_write.rs @@ -47,10 +47,10 @@ use zcash_client_backend::data_api::{ WalletRead, WalletSummary, WalletWrite, SAPLING_SHARD_HEIGHT, }; -use super::{ +use crate::error::Error; +use crate::{ Account, AccountId, MemoryWalletBlock, MemoryWalletDb, Nullifier, ReceivedNote, ViewingKey, }; -use crate::error::Error; impl WalletWrite for MemoryWalletDb { type UtxoRef = u32; From be01aac83b301036d86107aa967d63c2809b6fe0 Mon Sep 17 00:00:00 2001 From: Eric Tu Date: Wed, 28 Aug 2024 13:19:16 -0400 Subject: [PATCH 09/14] clippy first pass, remove allow unused --- zcash_client_memory/src/lib.rs | 41 ++++-------- zcash_client_memory/src/types/account.rs | 48 +++++-------- zcash_client_memory/src/types/block.rs | 46 ++++--------- zcash_client_memory/src/types/mod.rs | 2 +- zcash_client_memory/src/types/notes.rs | 48 +++++-------- zcash_client_memory/src/types/nullifier.rs | 49 +++----------- zcash_client_memory/src/types/scanning.rs | 52 ++++---------- zcash_client_memory/src/types/transaction.rs | 49 +++++--------- .../src/wallet_commitment_trees.rs | 43 +++--------- zcash_client_memory/src/wallet_read.rs | 67 ++++++++----------- zcash_client_memory/src/wallet_write.rs | 54 ++++++--------- 11 files changed, 155 insertions(+), 344 deletions(-) diff --git a/zcash_client_memory/src/lib.rs b/zcash_client_memory/src/lib.rs index 13d3902d4a..45b1e8cb1b 100644 --- a/zcash_client_memory/src/lib.rs +++ b/zcash_client_memory/src/lib.rs @@ -1,47 +1,32 @@ -use core::time; -use incrementalmerkletree::{Address, Marking, Retention}; -use sapling::NullifierDerivingKey; + + + use scanning::ScanQueue; -use secrecy::{ExposeSecret, SecretVec}; -use shardtree::{error::ShardTreeError, store::memory::MemoryShardStore, ShardTree}; + +use shardtree::{store::memory::MemoryShardStore, ShardTree}; use std::{ - cmp::Ordering, - collections::{hash_map::Entry, BTreeMap, HashMap, HashSet}, - convert::Infallible, + collections::{hash_map::Entry, BTreeMap}, hash::Hash, - num::NonZeroU32, ops::Deref, }; -use zcash_keys::keys::{AddressGenerationError, DerivationError, UnifiedIncomingViewingKey}; -use zip32::{fingerprint::SeedFingerprint, DiversifierIndex, Scope}; + +use zip32::{fingerprint::SeedFingerprint}; use zcash_primitives::{ - block::BlockHash, consensus::{BlockHeight, Network}, - transaction::{components::OutPoint, txid, Authorized, Transaction, TransactionData, TxId}, -}; -use zcash_protocol::{ - memo::{self, Memo, MemoBytes}, - value::{ZatBalance, Zatoshis}, - PoolType, - ShieldedProtocol::{Orchard, Sapling}, + transaction::{TxId}, }; + use zcash_client_backend::{ - address::UnifiedAddress, data_api::{ - chain::ChainState, Account as _, AccountPurpose, AccountSource, SeedRelevance, - TransactionDataRequest, TransactionStatus, + Account as _, AccountSource, }, - keys::{UnifiedAddressRequest, UnifiedFullViewingKey, UnifiedSpendingKey}, - proto::service::ShieldedProtocol, - wallet::{Note, NoteId, WalletSaplingOutput, WalletSpend, WalletTransparentOutput, WalletTx}, + wallet::{NoteId, WalletSaplingOutput}, }; use zcash_client_backend::data_api::{ - chain::CommitmentTreeRoot, scanning::ScanRange, AccountBirthday, BlockMetadata, - DecryptedTransaction, NullifierQuery, ScannedBlock, SentTransaction, WalletCommitmentTrees, - WalletRead, WalletSummary, WalletWrite, SAPLING_SHARD_HEIGHT, + SAPLING_SHARD_HEIGHT, }; #[cfg(feature = "transparent-inputs")] diff --git a/zcash_client_memory/src/types/account.rs b/zcash_client_memory/src/types/account.rs index 4829fdc930..4f7a390868 100644 --- a/zcash_client_memory/src/types/account.rs +++ b/zcash_client_memory/src/types/account.rs @@ -1,47 +1,29 @@ -use crate::{AccountId, ScanQueue}; -use core::time; -use incrementalmerkletree::{Address, Marking, Retention}; -use sapling::NullifierDerivingKey; -use secrecy::{ExposeSecret, SecretVec}; -use shardtree::{error::ShardTreeError, store::memory::MemoryShardStore, ShardTree}; +use crate::{AccountId}; + + + + + use std::{ - cmp::Ordering, - collections::{hash_map::Entry, BTreeMap, HashMap, HashSet}, - convert::Infallible, - hash::Hash, - num::NonZeroU32, - ops::Deref, + collections::{BTreeMap, HashSet}, }; -use zcash_keys::keys::{AddressGenerationError, DerivationError, UnifiedIncomingViewingKey}; -use zip32::{fingerprint::SeedFingerprint, DiversifierIndex, Scope}; +use zcash_keys::keys::{AddressGenerationError, UnifiedIncomingViewingKey}; +use zip32::{DiversifierIndex}; + + -use zcash_primitives::{ - block::BlockHash, - consensus::{BlockHeight, Network}, - transaction::{components::OutPoint, txid, Authorized, Transaction, TransactionData, TxId}, -}; -use zcash_protocol::{ - memo::{self, Memo, MemoBytes}, - value::{ZatBalance, Zatoshis}, - PoolType, - ShieldedProtocol::{Orchard, Sapling}, -}; use zcash_client_backend::{ address::UnifiedAddress, data_api::{ - chain::ChainState, Account as _, AccountPurpose, AccountSource, SeedRelevance, - TransactionDataRequest, TransactionStatus, + Account as _, AccountPurpose, AccountSource, }, - keys::{UnifiedAddressRequest, UnifiedFullViewingKey, UnifiedSpendingKey}, - proto::service::ShieldedProtocol, - wallet::{Note, NoteId, WalletSaplingOutput, WalletSpend, WalletTransparentOutput, WalletTx}, + keys::{UnifiedAddressRequest, UnifiedFullViewingKey}, + wallet::{NoteId}, }; use zcash_client_backend::data_api::{ - chain::CommitmentTreeRoot, scanning::ScanRange, AccountBirthday, BlockMetadata, - DecryptedTransaction, NullifierQuery, ScannedBlock, SentTransaction, WalletCommitmentTrees, - WalletRead, WalletSummary, WalletWrite, SAPLING_SHARD_HEIGHT, + AccountBirthday, }; #[cfg(feature = "transparent-inputs")] diff --git a/zcash_client_memory/src/types/block.rs b/zcash_client_memory/src/types/block.rs index 63a131a2b0..3bec9a901c 100644 --- a/zcash_client_memory/src/types/block.rs +++ b/zcash_client_memory/src/types/block.rs @@ -1,47 +1,29 @@ -use core::time; -use incrementalmerkletree::{Address, Marking, Retention}; -use sapling::NullifierDerivingKey; -use secrecy::{ExposeSecret, SecretVec}; -use shardtree::{error::ShardTreeError, store::memory::MemoryShardStore, ShardTree}; + + + + + use std::{ cmp::Ordering, - collections::{hash_map::Entry, BTreeMap, HashMap, HashSet}, - convert::Infallible, - hash::Hash, - num::NonZeroU32, - ops::Deref, + collections::{HashMap, HashSet}, }; -use zcash_keys::keys::{AddressGenerationError, DerivationError, UnifiedIncomingViewingKey}; -use zip32::{fingerprint::SeedFingerprint, DiversifierIndex, Scope}; + + use zcash_primitives::{ block::BlockHash, - consensus::{BlockHeight, Network}, - transaction::{components::OutPoint, txid, Authorized, Transaction, TransactionData, TxId}, + consensus::{BlockHeight}, + transaction::{TxId}, }; use zcash_protocol::{ - memo::{self, Memo, MemoBytes}, - value::{ZatBalance, Zatoshis}, - PoolType, - ShieldedProtocol::{Orchard, Sapling}, + memo::{MemoBytes}, }; use zcash_client_backend::{ - address::UnifiedAddress, - data_api::{ - chain::ChainState, Account as _, AccountPurpose, AccountSource, SeedRelevance, - TransactionDataRequest, TransactionStatus, - }, - keys::{UnifiedAddressRequest, UnifiedFullViewingKey, UnifiedSpendingKey}, - proto::service::ShieldedProtocol, - wallet::{Note, NoteId, WalletSaplingOutput, WalletSpend, WalletTransparentOutput, WalletTx}, + wallet::{NoteId}, }; -use zcash_client_backend::data_api::{ - chain::CommitmentTreeRoot, scanning::ScanRange, AccountBirthday, BlockMetadata, - DecryptedTransaction, NullifierQuery, ScannedBlock, SentTransaction, WalletCommitmentTrees, - WalletRead, WalletSummary, WalletWrite, SAPLING_SHARD_HEIGHT, -}; + #[cfg(feature = "transparent-inputs")] use { @@ -52,7 +34,7 @@ use { #[cfg(feature = "orchard")] use zcash_client_backend::{data_api::ORCHARD_SHARD_HEIGHT, wallet::WalletOrchardOutput}; -use crate::error::Error; + /// Internal wallet representation of a Block. pub(crate) struct MemoryWalletBlock { diff --git a/zcash_client_memory/src/types/mod.rs b/zcash_client_memory/src/types/mod.rs index 1319f2c8a5..9f864e5825 100644 --- a/zcash_client_memory/src/types/mod.rs +++ b/zcash_client_memory/src/types/mod.rs @@ -9,5 +9,5 @@ pub(crate) use account::*; pub(crate) use block::*; pub(crate) use notes::*; pub(crate) use nullifier::*; -pub(crate) use scanning::*; + pub(crate) use transaction::*; diff --git a/zcash_client_memory/src/types/notes.rs b/zcash_client_memory/src/types/notes.rs index a0aa9674cc..44e9944aea 100644 --- a/zcash_client_memory/src/types/notes.rs +++ b/zcash_client_memory/src/types/notes.rs @@ -1,51 +1,33 @@ -use core::time; -use incrementalmerkletree::{Address, Marking, Position, Retention}; -use sapling::NullifierDerivingKey; -use secrecy::{ExposeSecret, SecretVec}; -use shardtree::{error::ShardTreeError, store::memory::MemoryShardStore, ShardTree}; + +use incrementalmerkletree::{Position}; + + + use std::{ - cell::RefCell, - cmp::Ordering, - collections::{hash_map::Entry, BTreeMap, BTreeSet, HashMap, HashSet}, - convert::Infallible, - hash::Hash, - num::NonZeroU32, - ops::Deref, - rc::Rc, + collections::{HashMap}, }; -use zcash_keys::keys::{AddressGenerationError, DerivationError, UnifiedIncomingViewingKey}; -use zip32::{fingerprint::SeedFingerprint, DiversifierIndex, Scope}; + +use zip32::{Scope}; use zcash_primitives::{ - block::BlockHash, - consensus::{BlockHeight, Network}, - transaction::{components::OutPoint, txid, Authorized, Transaction, TransactionData, TxId}, + transaction::{TxId}, }; use zcash_protocol::{ - memo::{self, Memo, MemoBytes}, - value::{ZatBalance, Zatoshis}, + memo::{Memo}, PoolType, - ShieldedProtocol::{self, Orchard, Sapling}, + ShieldedProtocol::{Sapling}, }; use zcash_client_backend::{ - address::UnifiedAddress, data_api::{ - chain::ChainState, Account as _, AccountPurpose, AccountSource, SeedRelevance, - SentTransactionOutput, TransactionDataRequest, TransactionStatus, + SentTransactionOutput, }, - keys::{UnifiedAddressRequest, UnifiedFullViewingKey, UnifiedSpendingKey}, wallet::{ - Note, NoteId, Recipient, WalletSaplingOutput, WalletSpend, WalletTransparentOutput, - WalletTx, + Note, NoteId, Recipient, WalletSaplingOutput, }, }; -use zcash_client_backend::data_api::{ - chain::CommitmentTreeRoot, scanning::ScanRange, AccountBirthday, BlockMetadata, - DecryptedTransaction, NullifierQuery, ScannedBlock, SentTransaction, WalletCommitmentTrees, - WalletRead, WalletSummary, WalletWrite, SAPLING_SHARD_HEIGHT, -}; + use crate::AccountId; @@ -125,7 +107,7 @@ impl ReceivedNote { .. } => Ok(ReceivedNote { note_id: NoteId::new(txid, Sapling, output.output_index() as u16), - txid: txid, + txid, output_index: output.output_index() as u32, account_id: *receiving_account, note: Note::Sapling(note.clone()), diff --git a/zcash_client_memory/src/types/nullifier.rs b/zcash_client_memory/src/types/nullifier.rs index cea9364d7b..f3235b8627 100644 --- a/zcash_client_memory/src/types/nullifier.rs +++ b/zcash_client_memory/src/types/nullifier.rs @@ -1,51 +1,24 @@ -use core::time; -use incrementalmerkletree::{Address, Marking, Position, Retention}; -use sapling::NullifierDerivingKey; -use secrecy::{ExposeSecret, SecretVec}; -use shardtree::{error::ShardTreeError, store::memory::MemoryShardStore, ShardTree}; + + + + + use std::{ - cell::RefCell, - cmp::Ordering, - collections::{hash_map::Entry, BTreeMap, BTreeSet, HashMap, HashSet}, - convert::Infallible, - hash::Hash, - num::NonZeroU32, - ops::Deref, - rc::Rc, + collections::{BTreeMap}, }; -use zcash_keys::keys::{AddressGenerationError, DerivationError, UnifiedIncomingViewingKey}; -use zip32::{fingerprint::SeedFingerprint, DiversifierIndex, Scope}; + + use zcash_primitives::{ - block::BlockHash, - consensus::{BlockHeight, Network}, - transaction::{components::OutPoint, txid, Authorized, Transaction, TransactionData, TxId}, + consensus::{BlockHeight}, }; use zcash_protocol::{ - memo::{self, Memo, MemoBytes}, - value::{ZatBalance, Zatoshis}, PoolType, - ShieldedProtocol::{self, Orchard, Sapling}, }; -use zcash_client_backend::{ - address::UnifiedAddress, - data_api::{ - chain::ChainState, Account as _, AccountPurpose, AccountSource, SeedRelevance, - SentTransactionOutput, TransactionDataRequest, TransactionStatus, - }, - keys::{UnifiedAddressRequest, UnifiedFullViewingKey, UnifiedSpendingKey}, - wallet::{ - Note, NoteId, Recipient, WalletSaplingOutput, WalletSpend, WalletTransparentOutput, - WalletTx, - }, -}; -use zcash_client_backend::data_api::{ - chain::CommitmentTreeRoot, scanning::ScanRange, AccountBirthday, BlockMetadata, - DecryptedTransaction, NullifierQuery, ScannedBlock, SentTransaction, WalletCommitmentTrees, - WalletRead, WalletSummary, WalletWrite, SAPLING_SHARD_HEIGHT, -}; + + /// Maps a block height and transaction (i.e. transaction locator) index to a nullifier. pub(crate) struct NullifierMap(BTreeMap); diff --git a/zcash_client_memory/src/types/scanning.rs b/zcash_client_memory/src/types/scanning.rs index f1365b4c1a..98a996227f 100644 --- a/zcash_client_memory/src/types/scanning.rs +++ b/zcash_client_memory/src/types/scanning.rs @@ -1,56 +1,30 @@ -use core::time; -use incrementalmerkletree::{Address, Marking, Position, Retention}; -use sapling::NullifierDerivingKey; -use secrecy::{ExposeSecret, SecretVec}; -use shardtree::{error::ShardTreeError, store::memory::MemoryShardStore, ShardTree}; + + + + + use std::{ - cell::RefCell, - cmp::Ordering, - collections::{hash_map::Entry, BTreeMap, BTreeSet, HashMap, HashSet}, - convert::Infallible, - hash::Hash, - num::NonZeroU32, ops::{Deref, DerefMut, Range}, - path::Iter, - rc::Rc, }; -use zcash_keys::keys::{AddressGenerationError, DerivationError, UnifiedIncomingViewingKey}; -use zip32::{fingerprint::SeedFingerprint, DiversifierIndex, Scope}; + + use zcash_primitives::{ - block::{self, BlockHash}, - consensus::{BlockHeight, Network}, - transaction::{components::OutPoint, txid, Authorized, Transaction, TransactionData, TxId}, -}; -use zcash_protocol::{ - memo::{self, Memo, MemoBytes}, - value::{ZatBalance, Zatoshis}, - PoolType, - ShieldedProtocol::{self, Orchard, Sapling}, + consensus::{BlockHeight}, }; + use zcash_client_backend::{ - address::UnifiedAddress, data_api::{ - chain::ChainState, scanning::{spanning_tree::SpanningTree, ScanPriority}, - Account as _, AccountPurpose, AccountSource, SeedRelevance, SentTransactionOutput, - TransactionDataRequest, TransactionStatus, - }, - keys::{UnifiedAddressRequest, UnifiedFullViewingKey, UnifiedSpendingKey}, - wallet::{ - Note, NoteId, Recipient, WalletSaplingOutput, WalletSpend, WalletTransparentOutput, - WalletTx, }, }; use zcash_client_backend::data_api::{ - chain::CommitmentTreeRoot, scanning::ScanRange, AccountBirthday, BlockMetadata, - DecryptedTransaction, NullifierQuery, ScannedBlock, SentTransaction, WalletCommitmentTrees, - WalletRead, WalletSummary, WalletWrite, SAPLING_SHARD_HEIGHT, + scanning::ScanRange, }; -use crate::AccountId; + #[cfg(feature = "transparent-inputs")] use { @@ -149,8 +123,8 @@ impl ScanQueue { let mut to_create: Option = None; let mut to_delete_ends: Vec = vec![]; - let mut q_ranges = q_ranges.into_iter(); - while let Some((start, end, priority)) = q_ranges.next() { + let q_ranges = q_ranges.into_iter(); + for (start, end, priority) in q_ranges { let entry = ScanRange::from_parts( Range { start: *start, diff --git a/zcash_client_memory/src/types/transaction.rs b/zcash_client_memory/src/types/transaction.rs index 5caa96951b..9d7f1f03e1 100644 --- a/zcash_client_memory/src/types/transaction.rs +++ b/zcash_client_memory/src/types/transaction.rs @@ -1,51 +1,32 @@ -use core::time; -use incrementalmerkletree::{Address, Marking, Position, Retention}; -use sapling::NullifierDerivingKey; -use secrecy::{ExposeSecret, SecretVec}; -use shardtree::{error::ShardTreeError, store::memory::MemoryShardStore, ShardTree}; + + + + + use std::{ - cell::RefCell, - cmp::Ordering, - collections::{hash_map::Entry, BTreeMap, BTreeSet, HashMap, HashSet}, - convert::Infallible, - hash::Hash, - num::NonZeroU32, - ops::Deref, - rc::Rc, + collections::{hash_map::Entry, HashMap}, }; -use zcash_keys::keys::{AddressGenerationError, DerivationError, UnifiedIncomingViewingKey}; -use zip32::{fingerprint::SeedFingerprint, DiversifierIndex, Scope}; + + use zcash_primitives::{ - block::BlockHash, - consensus::{BlockHeight, Network}, - transaction::{components::OutPoint, txid, Authorized, Transaction, TransactionData, TxId}, + consensus::{BlockHeight}, + transaction::{Transaction, TxId}, }; use zcash_protocol::{ - memo::{self, Memo, MemoBytes}, - value::{ZatBalance, Zatoshis}, - PoolType, - ShieldedProtocol::{self, Orchard, Sapling}, + value::{Zatoshis}, }; use zcash_client_backend::{ - address::UnifiedAddress, data_api::{ - chain::ChainState, Account as _, AccountPurpose, AccountSource, SeedRelevance, - SentTransactionOutput, TransactionDataRequest, TransactionStatus, + TransactionStatus, }, - keys::{UnifiedAddressRequest, UnifiedFullViewingKey, UnifiedSpendingKey}, wallet::{ - Note, NoteId, Recipient, WalletSaplingOutput, WalletSpend, WalletTransparentOutput, WalletTx, }, }; -use zcash_client_backend::data_api::{ - chain::CommitmentTreeRoot, scanning::ScanRange, AccountBirthday, BlockMetadata, - DecryptedTransaction, NullifierQuery, ScannedBlock, SentTransaction, WalletCommitmentTrees, - WalletRead, WalletSummary, WalletWrite, SAPLING_SHARD_HEIGHT, -}; + use crate::AccountId; @@ -61,7 +42,7 @@ use { zcash_client_backend::wallet::WalletOrchardOutput, }; -use crate::{error::Error, nullifier::Nullifier}; +use crate::{error::Error}; /// Maps a block height and transaction index to a transaction ID. pub struct TxLocatorMap(HashMap<(BlockHeight, u32), TxId>); @@ -177,7 +158,7 @@ impl TransactionTable { entry.tx_status = status; Ok(()) } else { - return Err(Error::TransactionNotFound(*txid)); + Err(Error::TransactionNotFound(*txid)) } } pub fn get_tx_raw(&self, txid: &TxId) -> Option<&[u8]> { diff --git a/zcash_client_memory/src/wallet_commitment_trees.rs b/zcash_client_memory/src/wallet_commitment_trees.rs index 2c2f5dfdbe..bcd06f0cd0 100644 --- a/zcash_client_memory/src/wallet_commitment_trees.rs +++ b/zcash_client_memory/src/wallet_commitment_trees.rs @@ -1,50 +1,29 @@ -use incrementalmerkletree::{Address, Marking, Retention}; -use sapling::NullifierDerivingKey; -use secrecy::{ExposeSecret, SecretVec}; +use incrementalmerkletree::{Address}; + + use shardtree::{error::ShardTreeError, store::memory::MemoryShardStore, ShardTree}; use std::{ - cmp::Ordering, - collections::{BTreeMap, HashMap, HashSet}, convert::Infallible, - hash::Hash, - num::NonZeroU32, }; -use zcash_keys::keys::{AddressGenerationError, DerivationError, UnifiedIncomingViewingKey}; -use zip32::{fingerprint::SeedFingerprint, DiversifierIndex, Scope}; + + use zcash_primitives::{ - block::BlockHash, - consensus::{BlockHeight, Network}, - transaction::{Transaction, TxId}, - zip32::AccountId, -}; -use zcash_protocol::{ - memo::{self, Memo, MemoBytes}, - value::Zatoshis, - ShieldedProtocol::{Orchard, Sapling}, + consensus::{BlockHeight}, }; -use zcash_client_backend::{ - address::UnifiedAddress, - data_api::{ - chain::ChainState, AccountPurpose, AccountSource, SeedRelevance, TransactionDataRequest, - TransactionStatus, - }, - keys::{UnifiedAddressRequest, UnifiedFullViewingKey, UnifiedSpendingKey}, - wallet::{NoteId, WalletSpend, WalletTransparentOutput, WalletTx}, -}; + + use zcash_client_backend::data_api::{ - chain::CommitmentTreeRoot, scanning::ScanRange, Account as _, AccountBirthday, BlockMetadata, - DecryptedTransaction, NullifierQuery, ScannedBlock, SentTransaction, WalletCommitmentTrees, - WalletRead, WalletSummary, WalletWrite, SAPLING_SHARD_HEIGHT, + chain::CommitmentTreeRoot, WalletCommitmentTrees, SAPLING_SHARD_HEIGHT, }; #[cfg(feature = "orchard")] use zcash_client_backend::data_api::ORCHARD_SHARD_HEIGHT; -use super::{Account, MemoryWalletBlock, MemoryWalletDb}; -use crate::error::Error; +use super::{MemoryWalletDb}; + impl WalletCommitmentTrees for MemoryWalletDb { type Error = Infallible; diff --git a/zcash_client_memory/src/wallet_read.rs b/zcash_client_memory/src/wallet_read.rs index c5136c9b81..2e28b6b56a 100644 --- a/zcash_client_memory/src/wallet_read.rs +++ b/zcash_client_memory/src/wallet_read.rs @@ -1,47 +1,41 @@ -use incrementalmerkletree::{Address, Marking, Retention}; + use nonempty::NonEmpty; -use sapling::NullifierDerivingKey; + use secrecy::{ExposeSecret, SecretVec}; -use shardtree::{error::ShardTreeError, store::memory::MemoryShardStore, ShardTree}; + use std::{ - clone, - cmp::Ordering, - collections::{BTreeMap, HashMap, HashSet}, - convert::Infallible, + collections::{HashMap}, hash::Hash, num::NonZeroU32, }; -use zcash_keys::keys::{AddressGenerationError, DerivationError, UnifiedIncomingViewingKey}; -use zip32::{fingerprint::SeedFingerprint, DiversifierIndex, Scope}; +use zcash_keys::keys::{UnifiedIncomingViewingKey}; +use zip32::{fingerprint::SeedFingerprint}; + -use std::ops::Add; use zcash_client_backend::{ address::UnifiedAddress, data_api::{ - chain::ChainState, scanning::ScanPriority, Account as _, AccountPurpose, AccountSource, + scanning::ScanPriority, Account as _, AccountSource, SeedRelevance, TransactionDataRequest, TransactionStatus, }, keys::{UnifiedAddressRequest, UnifiedFullViewingKey, UnifiedSpendingKey}, - wallet::{NoteId, WalletSpend, WalletTransparentOutput, WalletTx}, + wallet::{NoteId}, }; use zcash_primitives::{ block::BlockHash, - consensus::{BlockHeight, Network}, + consensus::{BlockHeight}, transaction::{Transaction, TransactionData, TxId}, }; use zcash_protocol::{ consensus::{self, BranchId}, - memo::{self, Memo, MemoBytes}, - value::Zatoshis, - ShieldedProtocol::{Orchard, Sapling}, + memo::{Memo}, }; use zcash_client_backend::data_api::{ - chain::CommitmentTreeRoot, scanning::ScanRange, AccountBirthday, BlockMetadata, - DecryptedTransaction, NullifierQuery, ScannedBlock, SentTransaction, WalletCommitmentTrees, - WalletRead, WalletSummary, WalletWrite, SAPLING_SHARD_HEIGHT, + scanning::ScanRange, BlockMetadata, NullifierQuery, + WalletRead, WalletSummary, }; -use zcash_primitives::transaction::components::OutPoint; + #[cfg(feature = "transparent-inputs")] use { @@ -65,7 +59,7 @@ impl WalletRead for MemoryWalletDb { &self, account_id: Self::AccountId, ) -> Result, Self::Error> { - Ok(self.accounts.get(*account_id as usize).map(|a| a.clone())) + Ok(self.accounts.get(*account_id as usize).cloned()) } fn get_derived_account( @@ -84,7 +78,7 @@ impl WalletRead for MemoryWalletDb { None } } - AccountSource::Imported { purpose } => None, + AccountSource::Imported { purpose: _ } => None, })) } @@ -217,10 +211,7 @@ impl WalletRead for MemoryWalletDb { Ok(self .scan_queue .iter() - .max_by(|(_, end_a, _), (_, end_b, _)| end_a.cmp(end_b)) - // Scan ranges are end-exclusive, so we subtract 1 from `max_height` to obtain the - // height of the last known chain tip; - .and_then(|(_, end, _)| Some(end.saturating_sub(1)))) + .max_by(|(_, end_a, _), (_, end_b, _)| end_a.cmp(end_b)).map(|(_, end, _)| end.saturating_sub(1))) } fn get_block_hash(&self, block_height: BlockHeight) -> Result, Self::Error> { @@ -281,7 +272,7 @@ impl WalletRead for MemoryWalletDb { if let Some(fully_scanned_height) = scanned_ranges .first() - .and_then(|(block_range_start, block_range_end, priority)| { + .and_then(|(block_range_start, block_range_end, _priority)| { // If the start of the earliest scanned range is greater than // the birthday height, then there is an unscanned range between // the wallet birthday and that range, so there is no fully @@ -348,24 +339,20 @@ impl WalletRead for MemoryWalletDb { Ok(self .accounts .iter() - .filter_map(|account| match account.ufvk() { - Some(ufvk) => Some((account.id(), ufvk.clone())), - None => None, - }) + .filter_map(|account| account.ufvk().map(|ufvk| (account.id(), ufvk.clone()))) .collect()) } - fn get_memo(&self, id_note: NoteId) -> Result, Self::Error> { + fn get_memo(&self, _id_note: NoteId) -> Result, Self::Error> { todo!() } fn get_transaction(&self, txid: TxId) -> Result, Self::Error> { - let raw = self.tx_table.get_tx_raw(&txid); - let status = self.tx_table.tx_status(&txid); - let expiry_height = self.tx_table.expiry_height(&txid); + let _raw = self.tx_table.get_tx_raw(&txid); + let _status = self.tx_table.tx_status(&txid); + let _expiry_height = self.tx_table.expiry_height(&txid); self.tx_table - .get(&txid) - .and_then(|tx| Some((tx.status(), tx.expiry_height(), tx.raw()))) + .get(&txid).map(|tx| (tx.status(), tx.expiry_height(), tx.raw())) .map(|(status, expiry_height, raw)| { // We need to provide a consensus branch ID so that pre-v5 `Transaction` structs // (which don't commit directly to one) can store it internally. @@ -378,18 +365,18 @@ impl WalletRead for MemoryWalletDb { // height or return an error. if let TransactionStatus::Mined(height) = status { return Ok(Some( - Transaction::read(&raw[..], BranchId::for_height(&self.network, height)) + Transaction::read(raw, BranchId::for_height(&self.network, height)) .map(|t| (height, t)), )); } if let Some(height) = expiry_height.filter(|h| h > &BlockHeight::from(0)) { return Ok(Some( - Transaction::read(&raw[..], BranchId::for_height(&self.network, height)) + Transaction::read(raw, BranchId::for_height(&self.network, height)) .map(|t| (height, t)), )); } - let tx_data = Transaction::read(&raw[..], BranchId::Sprout) + let tx_data = Transaction::read(raw, BranchId::Sprout) .map_err(Self::Error::from)? .into_data(); diff --git a/zcash_client_memory/src/wallet_write.rs b/zcash_client_memory/src/wallet_write.rs index e434a07bc9..3cf13b8ea8 100644 --- a/zcash_client_memory/src/wallet_write.rs +++ b/zcash_client_memory/src/wallet_write.rs @@ -1,50 +1,37 @@ -use incrementalmerkletree::{Address, Marking, Retention}; -use sapling::NullifierDerivingKey; +use incrementalmerkletree::{Marking, Retention}; + use secrecy::{ExposeSecret, SecretVec}; -use shardtree::{error::ShardTreeError, store::memory::MemoryShardStore, ShardTree}; + use std::{ - cmp::Ordering, - collections::{BTreeMap, HashMap, HashSet}, - convert::Infallible, - hash::Hash, - num::NonZeroU32, -}; -use zcash_keys::{ - address::Receiver, - keys::{AddressGenerationError, DerivationError, UnifiedIncomingViewingKey}, + collections::{HashMap}, }; -use zip32::{fingerprint::SeedFingerprint, DiversifierIndex, Scope}; + +use zip32::{fingerprint::SeedFingerprint}; use zcash_primitives::{ - block::BlockHash, - consensus::{BlockHeight, Network}, - transaction::{Transaction, TxId}, + consensus::{BlockHeight}, + transaction::{TxId}, }; use zcash_protocol::{ - memo::{self, Memo, MemoBytes}, - value::Zatoshis, - PoolType, - ShieldedProtocol::{Orchard, Sapling}, + ShieldedProtocol::{Sapling}, }; use zcash_client_backend::{ address::UnifiedAddress, data_api::{ - chain::ChainState, AccountPurpose, AccountSource, SeedRelevance, TransactionDataRequest, + chain::ChainState, AccountPurpose, AccountSource, TransactionStatus, }, keys::{UnifiedAddressRequest, UnifiedFullViewingKey, UnifiedSpendingKey}, wallet::{ - Note, NoteId, Recipient, WalletSaplingOutput, WalletSpend, WalletTransparentOutput, - WalletTx, + NoteId, Recipient, WalletTransparentOutput, }, - TransferType, }; use zcash_client_backend::data_api::{ - chain::CommitmentTreeRoot, scanning::ScanRange, Account as _, AccountBirthday, BlockMetadata, - DecryptedTransaction, NullifierQuery, ScannedBlock, SentTransaction, WalletCommitmentTrees, - WalletRead, WalletSummary, WalletWrite, SAPLING_SHARD_HEIGHT, + Account as _, AccountBirthday, + DecryptedTransaction, ScannedBlock, SentTransaction, + WalletRead, WalletWrite, }; use crate::error::Error; @@ -117,7 +104,7 @@ impl WalletWrite for MemoryWalletDb { // - Make sure blocks are coming in order. // - Make sure the first block in the sequence is tip + 1? // - Add a check to make sure the blocks are not already in the data store. - let start_height = blocks.first().map(|b| b.height()); + let _start_height = blocks.first().map(|b| b.height()); let mut last_scanned_height = None; for block in blocks.into_iter() { @@ -160,10 +147,9 @@ impl WalletWrite for MemoryWalletDb { let spent_in = output .nf() .and_then(|nf| self.nullifiers.get(&Nullifier::Sapling(*nf))) - .and_then(|(height, tx_idx)| self.tx_locator.get(*height, *tx_idx)) - .map(|x| *x); + .and_then(|(height, tx_idx)| self.tx_locator.get(*height, *tx_idx)).copied(); - self.insert_received_sapling_note(note_id, &output, spent_in); + self.insert_received_sapling_note(note_id, output, spent_in); } #[cfg(feature = "orchard")] @@ -383,9 +369,9 @@ impl WalletWrite for MemoryWalletDb { ); } Recipient::EphemeralTransparent { - receiving_account, - ephemeral_address, - outpoint_metadata, + receiving_account: _, + ephemeral_address: _, + outpoint_metadata: _, } => { // mark ephemeral address as used } From db39cf90753084582d50f4f0493dcd07524b09ad Mon Sep 17 00:00:00 2001 From: Eric Tu Date: Wed, 28 Aug 2024 13:21:20 -0400 Subject: [PATCH 10/14] fmt, --all-features fail --- zcash_client_memory/src/lib.rs | 16 ++--- zcash_client_memory/src/types/account.rs | 25 ++------ zcash_client_memory/src/types/block.rs | 25 +------- zcash_client_memory/src/types/notes.rs | 31 +++------- zcash_client_memory/src/types/nullifier.rs | 23 +------ zcash_client_memory/src/types/scanning.rs | 28 ++------- zcash_client_memory/src/types/transaction.rs | 30 ++------- .../src/wallet_commitment_trees.rs | 19 ++---- zcash_client_memory/src/wallet_read.rs | 62 ++++++++----------- zcash_client_memory/src/wallet_write.rs | 32 +++------- 10 files changed, 73 insertions(+), 218 deletions(-) diff --git a/zcash_client_memory/src/lib.rs b/zcash_client_memory/src/lib.rs index 45b1e8cb1b..3ec66dfeb6 100644 --- a/zcash_client_memory/src/lib.rs +++ b/zcash_client_memory/src/lib.rs @@ -1,6 +1,3 @@ - - - use scanning::ScanQueue; use shardtree::{store::memory::MemoryShardStore, ShardTree}; @@ -10,24 +7,19 @@ use std::{ ops::Deref, }; -use zip32::{fingerprint::SeedFingerprint}; +use zip32::fingerprint::SeedFingerprint; use zcash_primitives::{ consensus::{BlockHeight, Network}, - transaction::{TxId}, + transaction::TxId, }; - use zcash_client_backend::{ - data_api::{ - Account as _, AccountSource, - }, + data_api::{Account as _, AccountSource}, wallet::{NoteId, WalletSaplingOutput}, }; -use zcash_client_backend::data_api::{ - SAPLING_SHARD_HEIGHT, -}; +use zcash_client_backend::data_api::SAPLING_SHARD_HEIGHT; #[cfg(feature = "transparent-inputs")] use { diff --git a/zcash_client_memory/src/types/account.rs b/zcash_client_memory/src/types/account.rs index 4f7a390868..8daef6d794 100644 --- a/zcash_client_memory/src/types/account.rs +++ b/zcash_client_memory/src/types/account.rs @@ -1,30 +1,17 @@ -use crate::{AccountId}; +use crate::AccountId; - - - - -use std::{ - collections::{BTreeMap, HashSet}, -}; +use std::collections::{BTreeMap, HashSet}; use zcash_keys::keys::{AddressGenerationError, UnifiedIncomingViewingKey}; -use zip32::{DiversifierIndex}; - - - +use zip32::DiversifierIndex; use zcash_client_backend::{ address::UnifiedAddress, - data_api::{ - Account as _, AccountPurpose, AccountSource, - }, + data_api::{Account as _, AccountPurpose, AccountSource}, keys::{UnifiedAddressRequest, UnifiedFullViewingKey}, - wallet::{NoteId}, + wallet::NoteId, }; -use zcash_client_backend::data_api::{ - AccountBirthday, -}; +use zcash_client_backend::data_api::AccountBirthday; #[cfg(feature = "transparent-inputs")] use { diff --git a/zcash_client_memory/src/types/block.rs b/zcash_client_memory/src/types/block.rs index 3bec9a901c..cc40be1b3f 100644 --- a/zcash_client_memory/src/types/block.rs +++ b/zcash_client_memory/src/types/block.rs @@ -1,29 +1,12 @@ - - - - - use std::{ cmp::Ordering, collections::{HashMap, HashSet}, }; +use zcash_primitives::{block::BlockHash, consensus::BlockHeight, transaction::TxId}; +use zcash_protocol::memo::MemoBytes; - -use zcash_primitives::{ - block::BlockHash, - consensus::{BlockHeight}, - transaction::{TxId}, -}; -use zcash_protocol::{ - memo::{MemoBytes}, -}; - -use zcash_client_backend::{ - wallet::{NoteId}, -}; - - +use zcash_client_backend::wallet::NoteId; #[cfg(feature = "transparent-inputs")] use { @@ -34,8 +17,6 @@ use { #[cfg(feature = "orchard")] use zcash_client_backend::{data_api::ORCHARD_SHARD_HEIGHT, wallet::WalletOrchardOutput}; - - /// Internal wallet representation of a Block. pub(crate) struct MemoryWalletBlock { pub(crate) height: BlockHeight, diff --git a/zcash_client_memory/src/types/notes.rs b/zcash_client_memory/src/types/notes.rs index 44e9944aea..f5eb9b7074 100644 --- a/zcash_client_memory/src/types/notes.rs +++ b/zcash_client_memory/src/types/notes.rs @@ -1,34 +1,17 @@ +use incrementalmerkletree::Position; -use incrementalmerkletree::{Position}; +use std::collections::HashMap; +use zip32::Scope; - -use std::{ - collections::{HashMap}, -}; - -use zip32::{Scope}; - -use zcash_primitives::{ - transaction::{TxId}, -}; -use zcash_protocol::{ - memo::{Memo}, - PoolType, - ShieldedProtocol::{Sapling}, -}; +use zcash_primitives::transaction::TxId; +use zcash_protocol::{memo::Memo, PoolType, ShieldedProtocol::Sapling}; use zcash_client_backend::{ - data_api::{ - SentTransactionOutput, - }, - wallet::{ - Note, NoteId, Recipient, WalletSaplingOutput, - }, + data_api::SentTransactionOutput, + wallet::{Note, NoteId, Recipient, WalletSaplingOutput}, }; - - use crate::AccountId; #[cfg(feature = "transparent-inputs")] diff --git a/zcash_client_memory/src/types/nullifier.rs b/zcash_client_memory/src/types/nullifier.rs index f3235b8627..d5b0a5dc0d 100644 --- a/zcash_client_memory/src/types/nullifier.rs +++ b/zcash_client_memory/src/types/nullifier.rs @@ -1,24 +1,7 @@ +use std::collections::BTreeMap; - - - - -use std::{ - collections::{BTreeMap}, -}; - - - -use zcash_primitives::{ - consensus::{BlockHeight}, -}; -use zcash_protocol::{ - PoolType, -}; - - - - +use zcash_primitives::consensus::BlockHeight; +use zcash_protocol::PoolType; /// Maps a block height and transaction (i.e. transaction locator) index to a nullifier. pub(crate) struct NullifierMap(BTreeMap); diff --git a/zcash_client_memory/src/types/scanning.rs b/zcash_client_memory/src/types/scanning.rs index 98a996227f..959946e053 100644 --- a/zcash_client_memory/src/types/scanning.rs +++ b/zcash_client_memory/src/types/scanning.rs @@ -1,30 +1,10 @@ +use std::ops::{Deref, DerefMut, Range}; +use zcash_primitives::consensus::BlockHeight; +use zcash_client_backend::data_api::scanning::{spanning_tree::SpanningTree, ScanPriority}; - - -use std::{ - ops::{Deref, DerefMut, Range}, -}; - - - -use zcash_primitives::{ - consensus::{BlockHeight}, -}; - - -use zcash_client_backend::{ - data_api::{ - scanning::{spanning_tree::SpanningTree, ScanPriority}, - }, -}; - -use zcash_client_backend::data_api::{ - scanning::ScanRange, -}; - - +use zcash_client_backend::data_api::scanning::ScanRange; #[cfg(feature = "transparent-inputs")] use { diff --git a/zcash_client_memory/src/types/transaction.rs b/zcash_client_memory/src/types/transaction.rs index 9d7f1f03e1..3cff3668b5 100644 --- a/zcash_client_memory/src/types/transaction.rs +++ b/zcash_client_memory/src/types/transaction.rs @@ -1,32 +1,12 @@ - - - - - -use std::{ - collections::{hash_map::Entry, HashMap}, -}; - - +use std::collections::{hash_map::Entry, HashMap}; use zcash_primitives::{ - consensus::{BlockHeight}, + consensus::BlockHeight, transaction::{Transaction, TxId}, }; -use zcash_protocol::{ - value::{Zatoshis}, -}; - -use zcash_client_backend::{ - data_api::{ - TransactionStatus, - }, - wallet::{ - WalletTx, - }, -}; - +use zcash_protocol::value::Zatoshis; +use zcash_client_backend::{data_api::TransactionStatus, wallet::WalletTx}; use crate::AccountId; @@ -42,7 +22,7 @@ use { zcash_client_backend::wallet::WalletOrchardOutput, }; -use crate::{error::Error}; +use crate::error::Error; /// Maps a block height and transaction index to a transaction ID. pub struct TxLocatorMap(HashMap<(BlockHeight, u32), TxId>); diff --git a/zcash_client_memory/src/wallet_commitment_trees.rs b/zcash_client_memory/src/wallet_commitment_trees.rs index bcd06f0cd0..37825f9e95 100644 --- a/zcash_client_memory/src/wallet_commitment_trees.rs +++ b/zcash_client_memory/src/wallet_commitment_trees.rs @@ -1,19 +1,9 @@ -use incrementalmerkletree::{Address}; - +use incrementalmerkletree::Address; use shardtree::{error::ShardTreeError, store::memory::MemoryShardStore, ShardTree}; -use std::{ - convert::Infallible, -}; - - - -use zcash_primitives::{ - consensus::{BlockHeight}, -}; - - +use std::convert::Infallible; +use zcash_primitives::consensus::BlockHeight; use zcash_client_backend::data_api::{ chain::CommitmentTreeRoot, WalletCommitmentTrees, SAPLING_SHARD_HEIGHT, @@ -22,8 +12,7 @@ use zcash_client_backend::data_api::{ #[cfg(feature = "orchard")] use zcash_client_backend::data_api::ORCHARD_SHARD_HEIGHT; -use super::{MemoryWalletDb}; - +use super::MemoryWalletDb; impl WalletCommitmentTrees for MemoryWalletDb { type Error = Infallible; diff --git a/zcash_client_memory/src/wallet_read.rs b/zcash_client_memory/src/wallet_read.rs index 2e28b6b56a..a799e4a498 100644 --- a/zcash_client_memory/src/wallet_read.rs +++ b/zcash_client_memory/src/wallet_read.rs @@ -1,42 +1,34 @@ - use nonempty::NonEmpty; use secrecy::{ExposeSecret, SecretVec}; -use std::{ - collections::{HashMap}, - hash::Hash, - num::NonZeroU32, -}; -use zcash_keys::keys::{UnifiedIncomingViewingKey}; -use zip32::{fingerprint::SeedFingerprint}; - +use std::{collections::HashMap, hash::Hash, num::NonZeroU32}; +use zcash_keys::keys::UnifiedIncomingViewingKey; +use zip32::fingerprint::SeedFingerprint; use zcash_client_backend::{ address::UnifiedAddress, data_api::{ - scanning::ScanPriority, Account as _, AccountSource, - SeedRelevance, TransactionDataRequest, TransactionStatus, + scanning::ScanPriority, Account as _, AccountSource, SeedRelevance, TransactionDataRequest, + TransactionStatus, }, keys::{UnifiedAddressRequest, UnifiedFullViewingKey, UnifiedSpendingKey}, - wallet::{NoteId}, + wallet::NoteId, }; use zcash_primitives::{ block::BlockHash, - consensus::{BlockHeight}, + consensus::BlockHeight, transaction::{Transaction, TransactionData, TxId}, }; use zcash_protocol::{ consensus::{self, BranchId}, - memo::{Memo}, + memo::Memo, }; use zcash_client_backend::data_api::{ - scanning::ScanRange, BlockMetadata, NullifierQuery, - WalletRead, WalletSummary, + scanning::ScanRange, BlockMetadata, NullifierQuery, WalletRead, WalletSummary, }; - #[cfg(feature = "transparent-inputs")] use { zcash_client_backend::wallet::TransparentAddressMetadata, @@ -211,7 +203,8 @@ impl WalletRead for MemoryWalletDb { Ok(self .scan_queue .iter() - .max_by(|(_, end_a, _), (_, end_b, _)| end_a.cmp(end_b)).map(|(_, end, _)| end.saturating_sub(1))) + .max_by(|(_, end_a, _), (_, end_b, _)| end_a.cmp(end_b)) + .map(|(_, end, _)| end.saturating_sub(1))) } fn get_block_hash(&self, block_height: BlockHeight) -> Result, Self::Error> { @@ -269,22 +262,20 @@ impl WalletRead for MemoryWalletDb { .filter(|(_, _, p)| p == &ScanPriority::Scanned) .collect(); scanned_ranges.sort_by(|(start_a, _, _), (start_b, _, _)| start_a.cmp(start_b)); - if let Some(fully_scanned_height) = - scanned_ranges - .first() - .and_then(|(block_range_start, block_range_end, _priority)| { - // If the start of the earliest scanned range is greater than - // the birthday height, then there is an unscanned range between - // the wallet birthday and that range, so there is no fully - // scanned height. - if *block_range_start <= birthday_height { - // Scan ranges are end-exclusive. - Some(*block_range_end - 1) - } else { - None - } - }) - { + if let Some(fully_scanned_height) = scanned_ranges.first().and_then( + |(block_range_start, block_range_end, _priority)| { + // If the start of the earliest scanned range is greater than + // the birthday height, then there is an unscanned range between + // the wallet birthday and that range, so there is no fully + // scanned height. + if *block_range_start <= birthday_height { + // Scan ranges are end-exclusive. + Some(*block_range_end - 1) + } else { + None + } + }, + ) { self.block_metadata(fully_scanned_height) } else { Ok(None) @@ -352,7 +343,8 @@ impl WalletRead for MemoryWalletDb { let _status = self.tx_table.tx_status(&txid); let _expiry_height = self.tx_table.expiry_height(&txid); self.tx_table - .get(&txid).map(|tx| (tx.status(), tx.expiry_height(), tx.raw())) + .get(&txid) + .map(|tx| (tx.status(), tx.expiry_height(), tx.raw())) .map(|(status, expiry_height, raw)| { // We need to provide a consensus branch ID so that pre-v5 `Transaction` structs // (which don't commit directly to one) can store it internally. diff --git a/zcash_client_memory/src/wallet_write.rs b/zcash_client_memory/src/wallet_write.rs index 3cf13b8ea8..a86fdd1585 100644 --- a/zcash_client_memory/src/wallet_write.rs +++ b/zcash_client_memory/src/wallet_write.rs @@ -2,36 +2,23 @@ use incrementalmerkletree::{Marking, Retention}; use secrecy::{ExposeSecret, SecretVec}; -use std::{ - collections::{HashMap}, -}; +use std::collections::HashMap; -use zip32::{fingerprint::SeedFingerprint}; +use zip32::fingerprint::SeedFingerprint; -use zcash_primitives::{ - consensus::{BlockHeight}, - transaction::{TxId}, -}; -use zcash_protocol::{ - ShieldedProtocol::{Sapling}, -}; +use zcash_primitives::{consensus::BlockHeight, transaction::TxId}; +use zcash_protocol::ShieldedProtocol::Sapling; use zcash_client_backend::{ address::UnifiedAddress, - data_api::{ - chain::ChainState, AccountPurpose, AccountSource, - TransactionStatus, - }, + data_api::{chain::ChainState, AccountPurpose, AccountSource, TransactionStatus}, keys::{UnifiedAddressRequest, UnifiedFullViewingKey, UnifiedSpendingKey}, - wallet::{ - NoteId, Recipient, WalletTransparentOutput, - }, + wallet::{NoteId, Recipient, WalletTransparentOutput}, }; use zcash_client_backend::data_api::{ - Account as _, AccountBirthday, - DecryptedTransaction, ScannedBlock, SentTransaction, - WalletRead, WalletWrite, + Account as _, AccountBirthday, DecryptedTransaction, ScannedBlock, SentTransaction, WalletRead, + WalletWrite, }; use crate::error::Error; @@ -147,7 +134,8 @@ impl WalletWrite for MemoryWalletDb { let spent_in = output .nf() .and_then(|nf| self.nullifiers.get(&Nullifier::Sapling(*nf))) - .and_then(|(height, tx_idx)| self.tx_locator.get(*height, *tx_idx)).copied(); + .and_then(|(height, tx_idx)| self.tx_locator.get(*height, *tx_idx)) + .copied(); self.insert_received_sapling_note(note_id, output, spent_in); } From 1143fa01cb1711ec5bfbf22e971c56705938d769 Mon Sep 17 00:00:00 2001 From: Eric Tu Date: Wed, 28 Aug 2024 13:28:43 -0400 Subject: [PATCH 11/14] more clippy --- zcash_client_memory/src/lib.rs | 6 ----- zcash_client_memory/src/types/account.rs | 9 -------- zcash_client_memory/src/types/block.rs | 9 -------- zcash_client_memory/src/types/notes.rs | 9 +------- zcash_client_memory/src/types/scanning.rs | 24 +++++--------------- zcash_client_memory/src/types/transaction.rs | 12 ---------- zcash_client_memory/src/wallet_read.rs | 8 +++---- zcash_client_memory/src/wallet_write.rs | 3 +++ 8 files changed, 14 insertions(+), 66 deletions(-) diff --git a/zcash_client_memory/src/lib.rs b/zcash_client_memory/src/lib.rs index 3ec66dfeb6..33ce927ea1 100644 --- a/zcash_client_memory/src/lib.rs +++ b/zcash_client_memory/src/lib.rs @@ -21,12 +21,6 @@ use zcash_client_backend::{ use zcash_client_backend::data_api::SAPLING_SHARD_HEIGHT; -#[cfg(feature = "transparent-inputs")] -use { - zcash_client_backend::wallet::TransparentAddressMetadata, - zcash_primitives::legacy::TransparentAddress, -}; - #[cfg(feature = "orchard")] use zcash_client_backend::{data_api::ORCHARD_SHARD_HEIGHT, wallet::WalletOrchardOutput}; diff --git a/zcash_client_memory/src/types/account.rs b/zcash_client_memory/src/types/account.rs index 8daef6d794..ec1af22ca0 100644 --- a/zcash_client_memory/src/types/account.rs +++ b/zcash_client_memory/src/types/account.rs @@ -13,15 +13,6 @@ use zcash_client_backend::{ use zcash_client_backend::data_api::AccountBirthday; -#[cfg(feature = "transparent-inputs")] -use { - zcash_client_backend::wallet::TransparentAddressMetadata, - zcash_primitives::legacy::TransparentAddress, -}; - -#[cfg(feature = "orchard")] -use zcash_client_backend::{data_api::ORCHARD_SHARD_HEIGHT, wallet::WalletOrchardOutput}; - use crate::error::Error; /// An account stored in a `zcash_client_sqlite` database. diff --git a/zcash_client_memory/src/types/block.rs b/zcash_client_memory/src/types/block.rs index cc40be1b3f..a7e0831d0e 100644 --- a/zcash_client_memory/src/types/block.rs +++ b/zcash_client_memory/src/types/block.rs @@ -8,15 +8,6 @@ use zcash_protocol::memo::MemoBytes; use zcash_client_backend::wallet::NoteId; -#[cfg(feature = "transparent-inputs")] -use { - zcash_client_backend::wallet::TransparentAddressMetadata, - zcash_primitives::legacy::TransparentAddress, -}; - -#[cfg(feature = "orchard")] -use zcash_client_backend::{data_api::ORCHARD_SHARD_HEIGHT, wallet::WalletOrchardOutput}; - /// Internal wallet representation of a Block. pub(crate) struct MemoryWalletBlock { pub(crate) height: BlockHeight, diff --git a/zcash_client_memory/src/types/notes.rs b/zcash_client_memory/src/types/notes.rs index f5eb9b7074..e0d1451e54 100644 --- a/zcash_client_memory/src/types/notes.rs +++ b/zcash_client_memory/src/types/notes.rs @@ -14,16 +14,9 @@ use zcash_client_backend::{ use crate::AccountId; -#[cfg(feature = "transparent-inputs")] -use { - zcash_client_backend::wallet::TransparentAddressMetadata, - zcash_primitives::legacy::TransparentAddress, -}; - #[cfg(feature = "orchard")] use { - zcash_client_backend::data_api::ORCHARD_SHARD_HEIGHT, - zcash_client_backend::wallet::WalletOrchardOutput, + zcash_client_backend::wallet::WalletOrchardOutput, zcash_protocol::ShieldedProtocol::Orchard, }; use crate::{error::Error, Nullifier}; diff --git a/zcash_client_memory/src/types/scanning.rs b/zcash_client_memory/src/types/scanning.rs index 959946e053..800834f22e 100644 --- a/zcash_client_memory/src/types/scanning.rs +++ b/zcash_client_memory/src/types/scanning.rs @@ -6,30 +6,18 @@ use zcash_client_backend::data_api::scanning::{spanning_tree::SpanningTree, Scan use zcash_client_backend::data_api::scanning::ScanRange; -#[cfg(feature = "transparent-inputs")] -use { - zcash_client_backend::wallet::TransparentAddressMetadata, - zcash_primitives::legacy::TransparentAddress, -}; - -#[cfg(feature = "orchard")] -use { - zcash_client_backend::data_api::ORCHARD_SHARD_HEIGHT, - zcash_client_backend::wallet::WalletOrchardOutput, -}; - use crate::error::Error; /// A queue of scanning ranges. Contains the start and end heights of each range, along with the /// priority of scanning that range. -pub struct ScanQueue(Vec<(BlockHeight, BlockHeight, ScanPriority)>); +pub(crate) struct ScanQueue(Vec<(BlockHeight, BlockHeight, ScanPriority)>); impl ScanQueue { - pub fn new() -> Self { + pub(crate) fn new() -> Self { ScanQueue(Vec::new()) } - pub fn suggest_scan_ranges(&self, min_priority: ScanPriority) -> Vec { + pub(crate) fn suggest_scan_ranges(&self, min_priority: ScanPriority) -> Vec { let mut priorities: Vec<_> = self .0 .iter() @@ -48,7 +36,7 @@ impl ScanQueue { }) .collect() } - pub fn insert_queue_entries<'a>( + pub(crate) fn insert_queue_entries<'a>( &mut self, entries: impl Iterator, ) -> Result<(), Error> { @@ -79,7 +67,7 @@ impl ScanQueue { } Ok(()) } - pub fn replace_queue_entries( + pub(crate) fn replace_queue_entries( &mut self, query_range: &Range, entries: impl Iterator, @@ -138,7 +126,7 @@ impl ScanQueue { !to_delete_ends.contains(block_range_end) }); let scan_ranges = tree.into_vec(); - self.insert_queue_entries(scan_ranges.iter()); + self.insert_queue_entries(scan_ranges.iter())?; } Ok(()) } diff --git a/zcash_client_memory/src/types/transaction.rs b/zcash_client_memory/src/types/transaction.rs index 3cff3668b5..c0aac5daec 100644 --- a/zcash_client_memory/src/types/transaction.rs +++ b/zcash_client_memory/src/types/transaction.rs @@ -10,18 +10,6 @@ use zcash_client_backend::{data_api::TransactionStatus, wallet::WalletTx}; use crate::AccountId; -#[cfg(feature = "transparent-inputs")] -use { - zcash_client_backend::wallet::TransparentAddressMetadata, - zcash_primitives::legacy::TransparentAddress, -}; - -#[cfg(feature = "orchard")] -use { - zcash_client_backend::data_api::ORCHARD_SHARD_HEIGHT, - zcash_client_backend::wallet::WalletOrchardOutput, -}; - use crate::error::Error; /// Maps a block height and transaction index to a transaction ID. diff --git a/zcash_client_memory/src/wallet_read.rs b/zcash_client_memory/src/wallet_read.rs index a799e4a498..af3b1e4ff6 100644 --- a/zcash_client_memory/src/wallet_read.rs +++ b/zcash_client_memory/src/wallet_read.rs @@ -2,7 +2,7 @@ use nonempty::NonEmpty; use secrecy::{ExposeSecret, SecretVec}; -use std::{collections::HashMap, hash::Hash, num::NonZeroU32}; +use std::{collections::HashMap, num::NonZeroU32}; use zcash_keys::keys::UnifiedIncomingViewingKey; use zip32::fingerprint::SeedFingerprint; @@ -460,9 +460,9 @@ impl WalletRead for MemoryWalletDb { #[cfg(feature = "transparent-inputs")] fn get_transparent_balances( &self, - account: Self::AccountId, - max_height: BlockHeight, - ) -> Result, Self::Error> { + _account: Self::AccountId, + _max_height: BlockHeight, + ) -> Result, Self::Error> { todo!() } diff --git a/zcash_client_memory/src/wallet_write.rs b/zcash_client_memory/src/wallet_write.rs index a86fdd1585..f41fcfdd57 100644 --- a/zcash_client_memory/src/wallet_write.rs +++ b/zcash_client_memory/src/wallet_write.rs @@ -26,6 +26,9 @@ use crate::{ Account, AccountId, MemoryWalletBlock, MemoryWalletDb, Nullifier, ReceivedNote, ViewingKey, }; +#[cfg(feature = "orchard")] +use zcash_protocol::ShieldedProtocol::Orchard; + impl WalletWrite for MemoryWalletDb { type UtxoRef = u32; From cb7ec5af0e75a37808aca40f24e3f80afb6d88d4 Mon Sep 17 00:00:00 2001 From: Eric Tu Date: Wed, 28 Aug 2024 13:53:47 -0400 Subject: [PATCH 12/14] clippy --- zcash_client_memory/src/error.rs | 4 +- zcash_client_memory/src/types/account.rs | 18 +++--- zcash_client_memory/src/types/block.rs | 8 +-- zcash_client_memory/src/types/notes.rs | 64 ++++++++++---------- zcash_client_memory/src/types/nullifier.rs | 2 +- zcash_client_memory/src/types/scanning.rs | 6 +- zcash_client_memory/src/types/transaction.rs | 57 ++++++++--------- zcash_client_memory/src/wallet_write.rs | 28 ++++----- 8 files changed, 88 insertions(+), 99 deletions(-) diff --git a/zcash_client_memory/src/error.rs b/zcash_client_memory/src/error.rs index a007e13f4d..b3d727640e 100644 --- a/zcash_client_memory/src/error.rs +++ b/zcash_client_memory/src/error.rs @@ -31,7 +31,7 @@ pub enum Error { #[error("Conflicting Tx Locator map entry")] ConflictingTxLocator, #[error("Io Error: {0}")] - IoError(std::io::Error), + Io(std::io::Error), #[error("Corrupted Data: {0}")] CorruptedData(String), #[error("An error occurred while processing an account due to a failure in deriving the account's keys: {0}")] @@ -64,6 +64,6 @@ impl From for Error { impl From for Error { fn from(value: std::io::Error) -> Self { - Error::IoError(value) + Error::Io(value) } } diff --git a/zcash_client_memory/src/types/account.rs b/zcash_client_memory/src/types/account.rs index ec1af22ca0..d1de8d2e7b 100644 --- a/zcash_client_memory/src/types/account.rs +++ b/zcash_client_memory/src/types/account.rs @@ -22,9 +22,9 @@ pub struct Account { kind: AccountSource, viewing_key: ViewingKey, birthday: AccountBirthday, - purpose: AccountPurpose, // TODO: Remove this. AccountSource should be sufficient. + _purpose: AccountPurpose, // TODO: Remove this. AccountSource should be sufficient. addresses: BTreeMap, - notes: HashSet, + _notes: HashSet, } /// The viewing key that an [`Account`] has available to it. @@ -40,7 +40,7 @@ pub(crate) enum ViewingKey { /// /// Accounts that have this kind of viewing key cannot be used in wallet contexts, /// because they are unable to maintain an accurate balance. - Incoming(Box), + _Incoming(Box), } impl Account { @@ -56,9 +56,9 @@ impl Account { kind, viewing_key, birthday, - purpose, + _purpose: purpose, addresses: BTreeMap::new(), - notes: HashSet::new(), + _notes: HashSet::new(), }; let ua_request = acc .viewing_key @@ -89,7 +89,7 @@ impl Account { &self.birthday } - pub(crate) fn addresses(&self) -> &BTreeMap { + pub(crate) fn _addresses(&self) -> &BTreeMap { &self.addresses } @@ -101,7 +101,7 @@ impl Account { pub(crate) fn kind(&self) -> &AccountSource { &self.kind } - pub(crate) fn viewing_key(&self) -> &ViewingKey { + pub(crate) fn _viewing_key(&self) -> &ViewingKey { &self.viewing_key } pub(crate) fn next_available_address( @@ -150,14 +150,14 @@ impl ViewingKey { fn ufvk(&self) -> Option<&UnifiedFullViewingKey> { match self { ViewingKey::Full(ufvk) => Some(ufvk), - ViewingKey::Incoming(_) => None, + ViewingKey::_Incoming(_) => None, } } fn uivk(&self) -> UnifiedIncomingViewingKey { match self { ViewingKey::Full(ufvk) => ufvk.as_ref().to_unified_incoming_viewing_key(), - ViewingKey::Incoming(uivk) => uivk.as_ref().clone(), + ViewingKey::_Incoming(uivk) => uivk.as_ref().clone(), } } } diff --git a/zcash_client_memory/src/types/block.rs b/zcash_client_memory/src/types/block.rs index a7e0831d0e..d165675f39 100644 --- a/zcash_client_memory/src/types/block.rs +++ b/zcash_client_memory/src/types/block.rs @@ -14,14 +14,14 @@ pub(crate) struct MemoryWalletBlock { pub(crate) hash: BlockHash, pub(crate) block_time: u32, // Just the transactions that involve an account in this wallet - pub(crate) transactions: HashSet, - pub(crate) memos: HashMap, + pub(crate) _transactions: HashSet, + pub(crate) _memos: HashMap, pub(crate) sapling_commitment_tree_size: Option, - pub(crate) sapling_output_count: Option, + pub(crate) _sapling_output_count: Option, #[cfg(feature = "orchard")] pub(crate) orchard_commitment_tree_size: Option, #[cfg(feature = "orchard")] - pub(crate) orchard_action_count: Option, + pub(crate) _orchard_action_count: Option, } impl PartialEq for MemoryWalletBlock { diff --git a/zcash_client_memory/src/types/notes.rs b/zcash_client_memory/src/types/notes.rs index e0d1451e54..da9c95bb8f 100644 --- a/zcash_client_memory/src/types/notes.rs +++ b/zcash_client_memory/src/types/notes.rs @@ -42,19 +42,19 @@ pub(crate) struct ReceivedNote { pub(crate) note_id: NoteId, pub(crate) txid: TxId, // output_index: sapling, action_index: orchard - pub(crate) output_index: u32, + pub(crate) _output_index: u32, pub(crate) account_id: AccountId, //sapling: (diversifier, value, rcm) orchard: (diversifier, value, rho, rseed) - pub(crate) note: Note, + pub(crate) _note: Note, pub(crate) nf: Option, - pub(crate) is_change: bool, - pub(crate) memo: Memo, - pub(crate) commitment_tree_position: Option, - pub(crate) recipient_key_scope: Option, + pub(crate) _is_change: bool, + pub(crate) _memo: Memo, + pub(crate) _commitment_tree_position: Option, + pub(crate) _recipient_key_scope: Option, } impl ReceivedNote { - pub fn pool(&self) -> PoolType { - match self.note { + pub fn _pool(&self) -> PoolType { + match self._note { Note::Sapling { .. } => PoolType::SAPLING, #[cfg(feature = "orchard")] Note::Orchard { .. } => PoolType::ORCHARD, @@ -84,14 +84,14 @@ impl ReceivedNote { } => Ok(ReceivedNote { note_id: NoteId::new(txid, Sapling, output.output_index() as u16), txid, - output_index: output.output_index() as u32, + _output_index: output.output_index() as u32, account_id: *receiving_account, - note: Note::Sapling(note.clone()), + _note: Note::Sapling(note.clone()), nf: None, - is_change: true, - memo: output.memo().map(|m| Memo::try_from(m).unwrap()).unwrap(), - commitment_tree_position: None, - recipient_key_scope: Some(Scope::Internal), + _is_change: true, + _memo: output.memo().map(|m| Memo::try_from(m).unwrap()).unwrap(), + _commitment_tree_position: None, + _recipient_key_scope: Some(Scope::Internal), }), #[cfg(feature = "orchard")] Recipient::InternalAccount { @@ -101,14 +101,14 @@ impl ReceivedNote { } => Ok(ReceivedNote { note_id: NoteId::new(txid, Orchard, output.output_index() as u16), txid: txid, - output_index: output.output_index() as u32, + _output_index: output.output_index() as u32, account_id: *receiving_account, - note: Note::Orchard(note.clone()), + _note: Note::Orchard(note.clone()), nf: None, - is_change: true, - memo: output.memo().map(|m| Memo::try_from(m).unwrap()).unwrap(), - commitment_tree_position: None, - recipient_key_scope: Some(Scope::Internal), + _is_change: true, + _memo: output.memo().map(|m| Memo::try_from(m).unwrap()).unwrap(), + _commitment_tree_position: None, + _recipient_key_scope: Some(Scope::Internal), }), _ => Err(Error::Other( "Recipient is not an internal shielded account".to_owned(), @@ -122,14 +122,14 @@ impl ReceivedNote { ReceivedNote { note_id, txid: *note_id.txid(), - output_index: output.index() as u32, + _output_index: output.index() as u32, account_id: *output.account_id(), - note: Note::Sapling(output.note().clone()), + _note: Note::Sapling(output.note().clone()), nf: output.nf().map(|nf| Nullifier::Sapling(*nf)), - is_change: output.is_change(), - memo: Memo::Empty, - commitment_tree_position: Some(output.note_commitment_tree_position()), - recipient_key_scope: output.recipient_key_scope(), + _is_change: output.is_change(), + _memo: Memo::Empty, + _commitment_tree_position: Some(output.note_commitment_tree_position()), + _recipient_key_scope: output.recipient_key_scope(), } } #[cfg(feature = "orchard")] @@ -140,14 +140,14 @@ impl ReceivedNote { ReceivedNote { note_id, txid: *note_id.txid(), - output_index: output.index() as u32, + _output_index: output.index() as u32, account_id: *output.account_id(), - note: Note::Orchard(output.note().clone()), + _note: Note::Orchard(output.note().clone()), nf: output.nf().map(|nf| Nullifier::Orchard(*nf)), - is_change: output.is_change(), - memo: Memo::Empty, - commitment_tree_position: Some(output.note_commitment_tree_position()), - recipient_key_scope: output.recipient_key_scope(), + _is_change: output.is_change(), + _memo: Memo::Empty, + _commitment_tree_position: Some(output.note_commitment_tree_position()), + _recipient_key_scope: output.recipient_key_scope(), } } } diff --git a/zcash_client_memory/src/types/nullifier.rs b/zcash_client_memory/src/types/nullifier.rs index d5b0a5dc0d..86049549e0 100644 --- a/zcash_client_memory/src/types/nullifier.rs +++ b/zcash_client_memory/src/types/nullifier.rs @@ -27,7 +27,7 @@ pub(crate) enum Nullifier { } impl Nullifier { - pub(crate) fn pool(&self) -> PoolType { + pub(crate) fn _pool(&self) -> PoolType { match self { #[cfg(feature = "orchard")] Nullifier::Orchard(_) => PoolType::ORCHARD, diff --git a/zcash_client_memory/src/types/scanning.rs b/zcash_client_memory/src/types/scanning.rs index 800834f22e..eb970913de 100644 --- a/zcash_client_memory/src/types/scanning.rs +++ b/zcash_client_memory/src/types/scanning.rs @@ -36,7 +36,7 @@ impl ScanQueue { }) .collect() } - pub(crate) fn insert_queue_entries<'a>( + fn _insert_queue_entries<'a>( &mut self, entries: impl Iterator, ) -> Result<(), Error> { @@ -67,7 +67,7 @@ impl ScanQueue { } Ok(()) } - pub(crate) fn replace_queue_entries( + pub(crate) fn _replace_queue_entries( &mut self, query_range: &Range, entries: impl Iterator, @@ -126,7 +126,7 @@ impl ScanQueue { !to_delete_ends.contains(block_range_end) }); let scan_ranges = tree.into_vec(); - self.insert_queue_entries(scan_ranges.iter())?; + self._insert_queue_entries(scan_ranges.iter())?; } Ok(()) } diff --git a/zcash_client_memory/src/types/transaction.rs b/zcash_client_memory/src/types/transaction.rs index c0aac5daec..64cfa6d8e2 100644 --- a/zcash_client_memory/src/types/transaction.rs +++ b/zcash_client_memory/src/types/transaction.rs @@ -13,10 +13,10 @@ use crate::AccountId; use crate::error::Error; /// Maps a block height and transaction index to a transaction ID. -pub struct TxLocatorMap(HashMap<(BlockHeight, u32), TxId>); +pub(crate) struct TxLocatorMap(HashMap<(BlockHeight, u32), TxId>); /// A table of received notes. Corresponds to sapling_received_notes and orchard_received_notes tables. -pub struct TransactionEntry { +pub(crate) struct TransactionEntry { // created: String, /// Combines block height and mined_height into a txn status tx_status: TransactionStatus, @@ -29,7 +29,7 @@ pub struct TransactionEntry { /// will only be set for transactions created using this wallet specifically, and not any /// other wallet that uses the same seed (including previous installations of the same /// wallet application.) - target_height: Option, + _target_height: Option, } impl TransactionEntry { pub fn new_from_tx_meta(tx_meta: WalletTx, height: BlockHeight) -> Self { @@ -39,46 +39,37 @@ impl TransactionEntry { expiry_height: None, raw: Vec::new(), fee: None, - target_height: None, + _target_height: None, } } - pub fn expiry_height(&self) -> Option { + pub(crate) fn expiry_height(&self) -> Option { self.expiry_height } - pub fn status(&self) -> TransactionStatus { + pub(crate) fn status(&self) -> TransactionStatus { self.tx_status } - pub fn tx_index(&self) -> Option { - self.tx_index - } - pub fn fee(&self) -> Option { - self.fee - } - pub fn target_height(&self) -> Option { - self.target_height - } - pub fn raw(&self) -> &[u8] { + pub(crate) fn raw(&self) -> &[u8] { self.raw.as_slice() } } -pub struct TransactionTable(HashMap); +pub(crate) struct TransactionTable(HashMap); impl TransactionTable { - pub fn new() -> Self { + pub(crate) fn new() -> Self { Self(HashMap::new()) } /// Returns transaction status for a given transaction ID. None if the transaction is not known. - pub fn tx_status(&self, txid: &TxId) -> Option { + pub(crate) fn tx_status(&self, txid: &TxId) -> Option { self.0.get(txid).map(|entry| entry.tx_status) } - pub fn expiry_height(&self, txid: &TxId) -> Option { + pub(crate) fn expiry_height(&self, txid: &TxId) -> Option { self.0.get(txid).and_then(|entry| entry.expiry_height) } - pub fn get_transaction(&self, txid: TxId) -> Option<&TransactionEntry> { + pub(crate) fn _get_transaction(&self, txid: TxId) -> Option<&TransactionEntry> { self.0.get(&txid) } /// Inserts information about a MINED transaction that was observed to /// contain a note related to this wallet - pub fn put_tx_meta(&mut self, tx_meta: WalletTx, height: BlockHeight) { + pub(crate) fn put_tx_meta(&mut self, tx_meta: WalletTx, height: BlockHeight) { match self.0.entry(tx_meta.txid()) { Entry::Occupied(mut entry) => { entry.get_mut().tx_index = Some(tx_meta.block_index() as u32); @@ -90,7 +81,7 @@ impl TransactionTable { } } /// Inserts full transaction data - pub fn put_tx_data( + pub(crate) fn put_tx_data( &mut self, tx: &Transaction, fee: Option, @@ -112,12 +103,12 @@ impl TransactionTable { expiry_height: Some(tx.expiry_height()), raw, fee, - target_height, + _target_height: target_height, }); } } } - pub fn set_transaction_status( + pub(crate) fn set_transaction_status( &mut self, txid: &TxId, status: TransactionStatus, @@ -129,37 +120,37 @@ impl TransactionTable { Err(Error::TransactionNotFound(*txid)) } } - pub fn get_tx_raw(&self, txid: &TxId) -> Option<&[u8]> { + pub(crate) fn get_tx_raw(&self, txid: &TxId) -> Option<&[u8]> { self.0.get(txid).map(|entry| entry.raw.as_slice()) } } impl TransactionTable { - pub fn get(&self, txid: &TxId) -> Option<&TransactionEntry> { + pub(crate) fn get(&self, txid: &TxId) -> Option<&TransactionEntry> { self.0.get(txid) } - pub fn get_mut(&mut self, txid: &TxId) -> Option<&mut TransactionEntry> { + pub(crate) fn _get_mut(&mut self, txid: &TxId) -> Option<&mut TransactionEntry> { self.0.get_mut(txid) } - pub fn remove(&mut self, txid: &TxId) -> Option { + pub(crate) fn _remove(&mut self, txid: &TxId) -> Option { self.0.remove(txid) } } impl TxLocatorMap { - pub fn new() -> Self { + pub(crate) fn new() -> Self { Self(HashMap::new()) } - pub fn insert(&mut self, height: BlockHeight, index: u32, txid: TxId) { + pub(crate) fn _insert(&mut self, height: BlockHeight, index: u32, txid: TxId) { self.0.insert((height, index), txid); } - pub fn get(&self, height: BlockHeight, index: u32) -> Option<&TxId> { + pub(crate) fn get(&self, height: BlockHeight, index: u32) -> Option<&TxId> { self.0.get(&(height, index)) } - pub fn entry(&mut self, k: (BlockHeight, u32)) -> Entry<(BlockHeight, u32), TxId> { + pub(crate) fn entry(&mut self, k: (BlockHeight, u32)) -> Entry<(BlockHeight, u32), TxId> { self.0.entry(k) } } diff --git a/zcash_client_memory/src/wallet_write.rs b/zcash_client_memory/src/wallet_write.rs index f41fcfdd57..d95d048e75 100644 --- a/zcash_client_memory/src/wallet_write.rs +++ b/zcash_client_memory/src/wallet_write.rs @@ -112,13 +112,13 @@ impl WalletWrite for MemoryWalletDb { // Mark the Sapling nullifiers of the spent notes as spent in the `sapling_spends` map. for spend in transaction.sapling_spends() { - self.mark_sapling_note_spent(*spend.nf(), txid); + self.mark_sapling_note_spent(*spend.nf(), txid)?; } // Mark the Orchard nullifiers of the spent notes as spent in the `orchard_spends` map. #[cfg(feature = "orchard")] for spend in transaction.orchard_spends() { - self.mark_orchard_note_spent(*spend.nf(), txid); + self.mark_orchard_note_spent(*spend.nf(), txid)?; } for output in transaction.sapling_outputs() { @@ -196,14 +196,18 @@ impl WalletWrite for MemoryWalletDb { height: block.height(), hash: block.block_hash(), block_time: block.block_time(), - transactions: transactions.keys().cloned().collect(), - memos, + _transactions: transactions.keys().cloned().collect(), + _memos: memos, sapling_commitment_tree_size: Some(block.sapling().final_tree_size()), - sapling_output_count: Some(block.sapling().commitments().len().try_into().unwrap()), + _sapling_output_count: Some( + block.sapling().commitments().len().try_into().unwrap(), + ), #[cfg(feature = "orchard")] orchard_commitment_tree_size: Some(block.orchard().final_tree_size()), #[cfg(feature = "orchard")] - orchard_action_count: Some(block.orchard().commitments().len().try_into().unwrap()), + _orchard_action_count: Some( + block.orchard().commitments().len().try_into().unwrap(), + ), }; // Insert transaction metadata into the transaction table @@ -322,7 +326,7 @@ impl WalletWrite for MemoryWalletDb { // Mark sapling notes as spent if let Some(bundle) = sent_tx.tx().sapling_bundle() { for spend in bundle.shielded_spends() { - self.mark_sapling_note_spent(*spend.nullifier(), sent_tx.tx().txid()); + self.mark_sapling_note_spent(*spend.nullifier(), sent_tx.tx().txid())?; } } // Mark orchard notes as spent @@ -330,7 +334,7 @@ impl WalletWrite for MemoryWalletDb { #[cfg(feature = "orchard")] { for action in _bundle.actions() { - self.mark_orchard_note_spent(*action.nullifier(), sent_tx.tx().txid()); + self.mark_orchard_note_spent(*action.nullifier(), sent_tx.tx().txid())?; } } @@ -339,7 +343,7 @@ impl WalletWrite for MemoryWalletDb { } // Mark transparent UTXOs as spent #[cfg(feature = "transparent-inputs")] - for utxo_outpoint in sent_tx.utxos_spent() { + for _utxo_outpoint in sent_tx.utxos_spent() { // self.mark_transparent_utxo_spent(wdb.conn.0, tx_ref, utxo_outpoint)?; todo!() } @@ -348,12 +352,6 @@ impl WalletWrite for MemoryWalletDb { // TODO: insert sent output match output.recipient() { - Recipient::InternalAccount { .. } => { - self.received_notes.insert_received_note( - ReceivedNote::from_sent_tx_output(sent_tx.tx().txid(), output)?, - ); - } - #[cfg(feature = "orchard")] Recipient::InternalAccount { .. } => { self.received_notes.insert_received_note( ReceivedNote::from_sent_tx_output(sent_tx.tx().txid(), output)?, From 9a6f496670a338e30ca7627c8f7d5f56e8257c7c Mon Sep 17 00:00:00 2001 From: Eric Tu Date: Wed, 28 Aug 2024 13:58:25 -0400 Subject: [PATCH 13/14] shardtree error handling --- zcash_client_memory/src/error.rs | 11 +++++++++++ zcash_client_memory/src/wallet_write.rs | 6 +++--- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/zcash_client_memory/src/error.rs b/zcash_client_memory/src/error.rs index b3d727640e..dc3b90e325 100644 --- a/zcash_client_memory/src/error.rs +++ b/zcash_client_memory/src/error.rs @@ -1,3 +1,6 @@ +use std::convert::Infallible; + +use shardtree::error::ShardTreeError; use zcash_keys::keys::{AddressGenerationError, DerivationError}; use zcash_primitives::transaction::TxId; use zcash_protocol::{consensus::BlockHeight, memo}; @@ -40,6 +43,8 @@ pub enum Error { NonSequentialBlocks, #[error("Invalid scan range start {0}, end {1}: {2}")] InvalidScanRange(BlockHeight, BlockHeight, String), + #[error("ShardTree error: {0}")] + ShardTree(ShardTreeError), #[error("Other error: {0}")] Other(String), } @@ -67,3 +72,9 @@ impl From for Error { Error::Io(value) } } + +impl From> for Error { + fn from(value: ShardTreeError) -> Self { + Error::ShardTree(value) + } +} diff --git a/zcash_client_memory/src/wallet_write.rs b/zcash_client_memory/src/wallet_write.rs index d95d048e75..784012134a 100644 --- a/zcash_client_memory/src/wallet_write.rs +++ b/zcash_client_memory/src/wallet_write.rs @@ -172,7 +172,7 @@ impl WalletWrite for MemoryWalletDb { id: from_state.block_height(), marking: Marking::Reference, }, - ); + )?; #[cfg(feature = "orchard")] // Add frontier to the orchard tree @@ -225,7 +225,7 @@ impl WalletWrite for MemoryWalletDb { .value() .map_or(0.into(), |t| t.position() + 1); self.sapling_tree - .batch_insert(start_position, block_commitments.sapling.into_iter()); + .batch_insert(start_position, block_commitments.sapling.into_iter())?; #[cfg(feature = "orchard")] { @@ -235,7 +235,7 @@ impl WalletWrite for MemoryWalletDb { .value() .map_or(0.into(), |t| t.position() + 1); self.orchard_tree - .batch_insert(start_position, block_commitments.orchard.into_iter()); + .batch_insert(start_position, block_commitments.orchard.into_iter())?; } } // We can do some pruning of the tx_locator_map here From f73f2868d0310e6baf8e400cb88bbe57b861aa9b Mon Sep 17 00:00:00 2001 From: Eric Tu Date: Wed, 28 Aug 2024 14:01:32 -0400 Subject: [PATCH 14/14] fix all clippy --- zcash_client_memory/src/types/notes.rs | 6 +++--- zcash_client_memory/src/wallet_write.rs | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/zcash_client_memory/src/types/notes.rs b/zcash_client_memory/src/types/notes.rs index da9c95bb8f..2a1b9a1829 100644 --- a/zcash_client_memory/src/types/notes.rs +++ b/zcash_client_memory/src/types/notes.rs @@ -100,10 +100,10 @@ impl ReceivedNote { .. } => Ok(ReceivedNote { note_id: NoteId::new(txid, Orchard, output.output_index() as u16), - txid: txid, + txid, _output_index: output.output_index() as u32, account_id: *receiving_account, - _note: Note::Orchard(note.clone()), + _note: Note::Orchard(*note), nf: None, _is_change: true, _memo: output.memo().map(|m| Memo::try_from(m).unwrap()).unwrap(), @@ -142,7 +142,7 @@ impl ReceivedNote { txid: *note_id.txid(), _output_index: output.index() as u32, account_id: *output.account_id(), - _note: Note::Orchard(output.note().clone()), + _note: Note::Orchard(*output.note()), nf: output.nf().map(|nf| Nullifier::Orchard(*nf)), _is_change: output.is_change(), _memo: Memo::Empty, diff --git a/zcash_client_memory/src/wallet_write.rs b/zcash_client_memory/src/wallet_write.rs index 784012134a..f0a00f54c1 100644 --- a/zcash_client_memory/src/wallet_write.rs +++ b/zcash_client_memory/src/wallet_write.rs @@ -159,11 +159,11 @@ impl WalletWrite for MemoryWalletDb { // we previously scanned. let spent_in = output .nf() - .and_then(|nf| self.nullifiers.get(&&Nullifier::Orchard(*nf))) + .and_then(|nf| self.nullifiers.get(&Nullifier::Orchard(*nf))) .and_then(|(height, tx_idx)| self.tx_locator.get(*height, *tx_idx)) - .map(|x| *x); + .copied(); - self.insert_received_orchard_note(note_id, &output, spent_in) + self.insert_received_orchard_note(note_id, output, spent_in) } // Add frontier to the sapling tree self.sapling_tree.insert_frontier( @@ -182,7 +182,7 @@ impl WalletWrite for MemoryWalletDb { id: from_state.block_height(), marking: Marking::Reference, }, - ); + )?; last_scanned_height = Some(block.height()); transactions.insert(txid, transaction.clone()); }