diff --git a/zcash_client_backend/CHANGELOG.md b/zcash_client_backend/CHANGELOG.md index 57bd0bbe9..985bad20f 100644 --- a/zcash_client_backend/CHANGELOG.md +++ b/zcash_client_backend/CHANGELOG.md @@ -97,6 +97,40 @@ and this library adheres to Rust's notion of - `parse::Param::name` ### Changed +- Several structs and functions now take an `AccountId` type parameter + parameter in order to decouple the concept of an account identifier from + the ZIP 32 account index. Many APIs that previously referenced + `zcash_primitives::zip32::AccountId` now reference the generic type. + Impacted types and functions are: + - `zcash_client_backend::data_api`: + - `WalletRead` now has an associated `AccountId` type. + - `WalletRead::{ + get_account_birthday, + get_current_address, + get_unified_full_viewing_keys, + get_account_for_ufvk, + get_wallet_summary, + get_sapling_nullifiers, + get_transparent_receivers, + get_transparent_balances, + get_account_ids + }` now refer to the `WalletRead::AccountId` associated type. + - `WalletWrite::{create_account, get_next_available_address}` + now refer to the `WalletRead::AccountId` associated type. + - `ScannedBlock` now takes an additional `AccountId` type parameter. + - `DecryptedTransaction` is now parameterized by `AccountId` + - `SentTransaction` is now parameterized by `AccountId` + - `SentTransactionOutput` is now parameterized by `AccountId` + - `WalletSummary` is now parameterized by `AccountId` + - `zcash_client_backend::decrypt` + - `DecryptedOutput` is now parameterized by `AccountId` + - `decrypt_transaction` is now parameterized by `AccountId` + - `zcash_client_backend::scanning::scan_block` is now parameterized by `AccountId` + - `zcash_client_backend::wallet`: + - `Recipient` now takes an additional `AccountId` type parameter. + - `WalletTx` now takes an additional `AccountId` type parameter. + - `WalletSaplingSpend` now takes an additional `AccountId` type parameter. + - `WalletSaplingOutput` now takes an additional `AccountId` type parameter. - `zcash_client_backend::data_api`: - `BlockMetadata::sapling_tree_size` now returns an `Option` instead of a `u32` for future consistency with Orchard. @@ -112,9 +146,13 @@ and this library adheres to Rust's notion of - Fields of `Balance` and `AccountBalance` have been made private and the values of these fields have been made available via methods having the same names as the previously-public fields. - - `WalletSummary::new` now takes an additional `next_sapling_subtree_index` - argument. + - `WalletSummary::new` now takes an additional `next_sapling_subtree_index` argument. + - `WalletSummary::new` now takes a `HashMap` instead of a `BTreeMap` for its + `account_balances` argument. + - `WalletSummary::account_balances` now returns a `HashMap` instead of a `BTreeMap`. - Changes to the `WalletRead` trait: + - Added associated type `AccountId`. + - Added `get_account` function. - `get_checkpoint_depth` has been removed without replacement. This is no longer needed given the change to use the stored anchor height for transaction proposal execution. @@ -274,7 +312,7 @@ and this library adheres to Rust's notion of ### Removed - `zcash_client_backend::wallet`: - `ReceivedSaplingNote` (use `zcash_client_backend::ReceivedNote` instead). - - `input_selection::{Proposal, ProposalError}` (moved to + - `input_selection::{Proposal, ShieldedInputs, ProposalError}` (moved to `zcash_client_backend::proposal`). - `SentTransactionOutput::sapling_change_to` - the note created by an internal transfer is now conveyed in the `recipient` field. diff --git a/zcash_client_backend/src/data_api.rs b/zcash_client_backend/src/data_api.rs index 16e0f757f..8af3b4b04 100644 --- a/zcash_client_backend/src/data_api.rs +++ b/zcash_client_backend/src/data_api.rs @@ -1,8 +1,9 @@ //! Interfaces for wallet data persistence & low-level wallet utilities. use std::{ - collections::{BTreeMap, HashMap}, + collections::HashMap, fmt::Debug, + hash::Hash, io, num::{NonZeroU32, TryFromIntError}, }; @@ -10,17 +11,8 @@ use std::{ use incrementalmerkletree::{frontier::Frontier, Retention}; use secrecy::SecretVec; use shardtree::{error::ShardTreeError, store::ShardStore, ShardTree}; -use zcash_primitives::{ - block::BlockHash, - consensus::BlockHeight, - memo::{Memo, MemoBytes}, - transaction::{ - components::amount::{Amount, BalanceError, NonNegativeAmount}, - Transaction, TxId, - }, - zip32::{AccountId, Scope}, -}; +use self::{chain::CommitmentTreeRoot, scanning::ScanRange}; use crate::{ address::UnifiedAddress, decrypt::DecryptedOutput, @@ -29,9 +21,16 @@ use crate::{ wallet::{Note, NoteId, ReceivedNote, Recipient, WalletTransparentOutput, WalletTx}, ShieldedProtocol, }; - -use self::chain::CommitmentTreeRoot; -use self::scanning::ScanRange; +use zcash_primitives::{ + block::BlockHash, + consensus::BlockHeight, + memo::{Memo, MemoBytes}, + transaction::{ + components::amount::{Amount, BalanceError, NonNegativeAmount}, + Transaction, TxId, + }, + zip32::Scope, +}; #[cfg(feature = "transparent-inputs")] use { @@ -291,18 +290,18 @@ impl Ratio { /// this circumstance it is possible that a newly created transaction could conflict with a /// not-yet-mined transaction in the mempool. #[derive(Debug, Clone, PartialEq, Eq)] -pub struct WalletSummary { - account_balances: BTreeMap, +pub struct WalletSummary { + account_balances: HashMap, chain_tip_height: BlockHeight, fully_scanned_height: BlockHeight, scan_progress: Option>, next_sapling_subtree_index: u64, } -impl WalletSummary { +impl WalletSummary { /// Constructs a new [`WalletSummary`] from its constituent parts. pub fn new( - account_balances: BTreeMap, + account_balances: HashMap, chain_tip_height: BlockHeight, fully_scanned_height: BlockHeight, scan_progress: Option>, @@ -318,7 +317,7 @@ impl WalletSummary { } /// Returns the balances of accounts in the wallet, keyed by account ID. - pub fn account_balances(&self) -> &BTreeMap { + pub fn account_balances(&self) -> &HashMap { &self.account_balances } @@ -361,10 +360,17 @@ pub trait InputSource { /// The type of errors produced by a wallet backend. type Error; + /// Backend-specific account identifier. + /// + /// An account identifier corresponds to at most a single unified spending key's worth of spend + /// authority, such that both received notes and change spendable by that spending authority + /// will be interpreted as belonging to that account. This might be a database identifier type + /// or a UUID. + type AccountId: Copy + Debug + Eq + Hash; + /// Backend-specific note identifier. /// - /// For example, this might be a database identifier type - /// or a UUID. + /// For example, this might be a database identifier type or a UUID. type NoteRef: Copy + Debug + Eq + Ord; /// Fetches a spendable note by indexing into a transaction's shielded outputs for the @@ -384,7 +390,7 @@ pub trait InputSource { /// be included. fn select_spendable_notes( &self, - account: AccountId, + account: Self::AccountId, target_value: Amount, sources: &[ShieldedProtocol], anchor_height: BlockHeight, @@ -425,6 +431,13 @@ pub trait WalletRead { /// The type of errors that may be generated when querying a wallet data store. type Error; + /// The type of the account identifier. + /// + /// An account identifier corresponds to at most a single unified spending key's worth of spend + /// authority, such that both received notes and change spendable by that spending authority + /// will be interpreted as belonging to that account. + type AccountId: Copy + Debug + Eq + Hash; + /// Returns the height of the chain as known to the wallet as of the most recent call to /// [`WalletWrite::update_chain_tip`]. /// @@ -503,7 +516,7 @@ pub trait WalletRead { /// Returns the birthday height for the given account, or an error if the account is not known /// to the wallet. - fn get_account_birthday(&self, account: AccountId) -> Result; + fn get_account_birthday(&self, account: Self::AccountId) -> Result; /// Returns the most recently generated unified address for the specified account, if the /// account identifier specified refers to a valid account for this wallet. @@ -512,26 +525,26 @@ pub trait WalletRead { /// account. fn get_current_address( &self, - account: AccountId, + account: Self::AccountId, ) -> Result, Self::Error>; /// Returns all unified full viewing keys known to this wallet. fn get_unified_full_viewing_keys( &self, - ) -> Result, Self::Error>; + ) -> Result, Self::Error>; /// Returns the account id corresponding to a given [`UnifiedFullViewingKey`], if any. fn get_account_for_ufvk( &self, ufvk: &UnifiedFullViewingKey, - ) -> Result, Self::Error>; + ) -> Result, Self::Error>; /// Returns the wallet balances and sync status for an account given the specified minimum /// number of confirmations, or `Ok(None)` if the wallet has no balance data available. fn get_wallet_summary( &self, min_confirmations: u32, - ) -> Result, Self::Error>; + ) -> Result>, Self::Error>; /// Returns the memo for a note. /// @@ -549,7 +562,7 @@ pub trait WalletRead { fn get_sapling_nullifiers( &self, query: NullifierQuery, - ) -> Result, Self::Error>; + ) -> Result, Self::Error>; /// Returns the nullifiers for Orchard notes that the wallet is tracking, along with their /// associated account IDs, that are either unspent or have not yet been confirmed as spent (in @@ -558,7 +571,7 @@ pub trait WalletRead { fn get_orchard_nullifiers( &self, query: NullifierQuery, - ) -> Result, Self::Error>; + ) -> Result, Self::Error>; /// Returns the set of all transparent receivers associated with the given account. /// @@ -568,7 +581,7 @@ pub trait WalletRead { #[cfg(feature = "transparent-inputs")] fn get_transparent_receivers( &self, - _account: AccountId, + _account: Self::AccountId, ) -> Result>, Self::Error> { Ok(HashMap::new()) } @@ -578,14 +591,14 @@ pub trait WalletRead { #[cfg(feature = "transparent-inputs")] fn get_transparent_balances( &self, - _account: AccountId, + _account: Self::AccountId, _max_height: BlockHeight, ) -> Result, Self::Error> { Ok(HashMap::new()) } /// Returns a vector with the IDs of all accounts known to this wallet. - fn get_account_ids(&self) -> Result, Self::Error>; + fn get_account_ids(&self) -> Result, Self::Error>; } /// Metadata describing the sizes of the zcash note commitment trees as of a particular block. @@ -700,23 +713,23 @@ pub struct ScannedBlockCommitments { /// decrypted and extracted from a [`CompactBlock`]. /// /// [`CompactBlock`]: crate::proto::compact_formats::CompactBlock -pub struct ScannedBlock { +pub struct ScannedBlock { block_height: BlockHeight, block_hash: BlockHash, block_time: u32, - transactions: Vec>, + transactions: Vec>, sapling: ScannedBundles, #[cfg(feature = "orchard")] orchard: ScannedBundles, } -impl ScannedBlock { +impl ScannedBlock { /// Constructs a new `ScannedBlock` pub(crate) fn from_parts( block_height: BlockHeight, block_hash: BlockHash, block_time: u32, - transactions: Vec>, + transactions: Vec>, sapling: ScannedBundles, #[cfg(feature = "orchard")] orchard: ScannedBundles< orchard::note::NoteCommitment, @@ -750,7 +763,7 @@ impl ScannedBlock { } /// Returns the list of transactions from this block that are relevant to the wallet. - pub fn transactions(&self) -> &[WalletTx] { + pub fn transactions(&self) -> &[WalletTx] { &self.transactions } @@ -794,9 +807,9 @@ impl ScannedBlock { /// /// The purpose of this struct is to permit atomic updates of the /// wallet database when transactions are successfully decrypted. -pub struct DecryptedTransaction<'a> { +pub struct DecryptedTransaction<'a, AccountId> { pub tx: &'a Transaction, - pub sapling_outputs: &'a Vec>, + pub sapling_outputs: &'a Vec>, } /// A transaction that was constructed and sent by the wallet. @@ -804,11 +817,11 @@ pub struct DecryptedTransaction<'a> { /// The purpose of this struct is to permit atomic updates of the /// wallet database when transactions are created and submitted /// to the network. -pub struct SentTransaction<'a> { +pub struct SentTransaction<'a, AccountId> { pub tx: &'a Transaction, pub created: time::OffsetDateTime, pub account: AccountId, - pub outputs: Vec, + pub outputs: Vec>, pub fee_amount: Amount, #[cfg(feature = "transparent-inputs")] pub utxos_spent: Vec, @@ -817,14 +830,14 @@ pub struct SentTransaction<'a> { /// An output of a transaction generated by the wallet. /// /// This type is capable of representing both shielded and transparent outputs. -pub struct SentTransactionOutput { +pub struct SentTransactionOutput { output_index: usize, - recipient: Recipient, + recipient: Recipient, value: NonNegativeAmount, memo: Option, } -impl SentTransactionOutput { +impl SentTransactionOutput { /// Constructs a new [`SentTransactionOutput`] from its constituent parts. /// /// ### Fields: @@ -836,7 +849,7 @@ impl SentTransactionOutput { /// * `memo` - the memo that was sent with this output pub fn from_parts( output_index: usize, - recipient: Recipient, + recipient: Recipient, value: NonNegativeAmount, memo: Option, ) -> Self { @@ -859,7 +872,7 @@ impl SentTransactionOutput { } /// Returns the recipient address of the transaction, or the account id and /// resulting note for wallet-internal outputs. - pub fn recipient(&self) -> &Recipient { + pub fn recipient(&self) -> &Recipient { &self.recipient } /// Returns the value of the newly created output. @@ -998,7 +1011,9 @@ pub trait WalletWrite: WalletRead { /// current set of [ZIP 316] account identifiers known to the wallet database. /// /// Returns the account identifier for the newly-created wallet database entry, along with the - /// associated [`UnifiedSpendingKey`]. + /// associated [`UnifiedSpendingKey`]. Note that the unique account identifier should *not* be + /// assumed equivalent to the ZIP 32 account index. It is an opaque identifier for a pool of + /// funds or set of outputs controlled by a single spending authority. /// /// If `birthday.height()` is below the current chain tip, this operation will /// trigger a re-scan of the blocks at and above the provided height. The birthday height is @@ -1024,7 +1039,7 @@ pub trait WalletWrite: WalletRead { &mut self, seed: &SecretVec, birthday: AccountBirthday, - ) -> Result<(AccountId, UnifiedSpendingKey), Self::Error>; + ) -> Result<(Self::AccountId, UnifiedSpendingKey), Self::Error>; /// Generates and persists the next available diversified address, given the current /// addresses known to the wallet. @@ -1033,7 +1048,7 @@ pub trait WalletWrite: WalletRead { /// account. fn get_next_available_address( &mut self, - account: AccountId, + account: Self::AccountId, request: UnifiedAddressRequest, ) -> Result, Self::Error>; @@ -1044,7 +1059,7 @@ pub trait WalletWrite: WalletRead { /// `blocks` must be sequential, in order of increasing block height fn put_blocks( &mut self, - blocks: Vec>, + blocks: Vec>, ) -> Result<(), Self::Error>; /// Updates the wallet's view of the blockchain. @@ -1057,11 +1072,17 @@ pub trait WalletWrite: WalletRead { fn update_chain_tip(&mut self, tip_height: BlockHeight) -> Result<(), Self::Error>; /// Caches a decrypted transaction in the persistent wallet store. - fn store_decrypted_tx(&mut self, received_tx: DecryptedTransaction) -> Result<(), Self::Error>; + fn store_decrypted_tx( + &mut self, + received_tx: DecryptedTransaction, + ) -> Result<(), Self::Error>; /// Saves information about a transaction that was constructed and sent by the wallet to the /// persistent wallet store. - fn store_sent_tx(&mut self, sent_tx: &SentTransaction) -> Result<(), Self::Error>; + fn store_sent_tx( + &mut self, + sent_tx: &SentTransaction, + ) -> Result<(), Self::Error>; /// Truncates the wallet database to the specified height. /// @@ -1167,7 +1188,7 @@ pub mod testing { consensus::{BlockHeight, Network}, memo::Memo, transaction::{components::Amount, Transaction, TxId}, - zip32::{AccountId, Scope}, + zip32::Scope, }; use crate::{ @@ -1218,6 +1239,7 @@ pub mod testing { impl InputSource for MockWalletDb { type Error = (); type NoteRef = u32; + type AccountId = u32; fn get_spendable_note( &self, @@ -1230,7 +1252,7 @@ pub mod testing { fn select_spendable_notes( &self, - _account: AccountId, + _account: Self::AccountId, _target_value: Amount, _sources: &[ShieldedProtocol], _anchor_height: BlockHeight, @@ -1242,6 +1264,7 @@ pub mod testing { impl WalletRead for MockWalletDb { type Error = (); + type AccountId = u32; fn chain_height(&self) -> Result, Self::Error> { Ok(None) @@ -1296,34 +1319,37 @@ pub mod testing { Ok(None) } - fn get_account_birthday(&self, _account: AccountId) -> Result { + fn get_account_birthday( + &self, + _account: Self::AccountId, + ) -> Result { Err(()) } fn get_current_address( &self, - _account: AccountId, + _account: Self::AccountId, ) -> Result, Self::Error> { Ok(None) } fn get_unified_full_viewing_keys( &self, - ) -> Result, Self::Error> { + ) -> Result, Self::Error> { Ok(HashMap::new()) } fn get_account_for_ufvk( &self, _ufvk: &UnifiedFullViewingKey, - ) -> Result, Self::Error> { + ) -> Result, Self::Error> { Ok(None) } fn get_wallet_summary( &self, _min_confirmations: u32, - ) -> Result, Self::Error> { + ) -> Result>, Self::Error> { Ok(None) } @@ -1338,7 +1364,7 @@ pub mod testing { fn get_sapling_nullifiers( &self, _query: NullifierQuery, - ) -> Result, Self::Error> { + ) -> Result, Self::Error> { Ok(Vec::new()) } @@ -1346,14 +1372,14 @@ pub mod testing { fn get_orchard_nullifiers( &self, _query: NullifierQuery, - ) -> Result, Self::Error> { + ) -> Result, Self::Error> { Ok(Vec::new()) } #[cfg(feature = "transparent-inputs")] fn get_transparent_receivers( &self, - _account: AccountId, + _account: Self::AccountId, ) -> Result>, Self::Error> { Ok(HashMap::new()) @@ -1362,13 +1388,13 @@ pub mod testing { #[cfg(feature = "transparent-inputs")] fn get_transparent_balances( &self, - _account: AccountId, + _account: Self::AccountId, _max_height: BlockHeight, ) -> Result, Self::Error> { Ok(HashMap::new()) } - fn get_account_ids(&self) -> Result, Self::Error> { + fn get_account_ids(&self) -> Result, Self::Error> { Ok(Vec::new()) } } @@ -1380,16 +1406,16 @@ pub mod testing { &mut self, seed: &SecretVec, _birthday: AccountBirthday, - ) -> Result<(AccountId, UnifiedSpendingKey), Self::Error> { - let account = AccountId::ZERO; + ) -> Result<(Self::AccountId, UnifiedSpendingKey), Self::Error> { + let account = zip32::AccountId::ZERO; UnifiedSpendingKey::from_seed(&self.network, seed.expose_secret(), account) - .map(|k| (account, k)) + .map(|k| (u32::from(account), k)) .map_err(|_| ()) } fn get_next_available_address( &mut self, - _account: AccountId, + _account: Self::AccountId, _request: UnifiedAddressRequest, ) -> Result, Self::Error> { Ok(None) @@ -1398,7 +1424,7 @@ pub mod testing { #[allow(clippy::type_complexity)] fn put_blocks( &mut self, - _blocks: Vec>, + _blocks: Vec>, ) -> Result<(), Self::Error> { Ok(()) } @@ -1409,12 +1435,15 @@ pub mod testing { fn store_decrypted_tx( &mut self, - _received_tx: DecryptedTransaction, + _received_tx: DecryptedTransaction, ) -> Result<(), Self::Error> { Ok(()) } - fn store_sent_tx(&mut self, _sent_tx: &SentTransaction) -> Result<(), Self::Error> { + fn store_sent_tx( + &mut self, + _sent_tx: &SentTransaction, + ) -> Result<(), Self::Error> { Ok(()) } diff --git a/zcash_client_backend/src/data_api/chain.rs b/zcash_client_backend/src/data_api/chain.rs index 1555785f8..d3283126e 100644 --- a/zcash_client_backend/src/data_api/chain.rs +++ b/zcash_client_backend/src/data_api/chain.rs @@ -146,6 +146,7 @@ use std::ops::Range; use sapling::note_encryption::PreparedIncomingViewingKey; +use subtle::ConditionallySelectable; use zcash_primitives::{ consensus::{self, BlockHeight}, zip32::Scope, @@ -161,6 +162,8 @@ use crate::{ pub mod error; use error::Error; +use super::WalletRead; + /// A struct containing metadata about a subtree root of the note commitment tree. /// /// This stores the block height at which the leaf that completed the subtree was @@ -277,6 +280,7 @@ where ParamsT: consensus::Parameters + Send + 'static, BlockSourceT: BlockSource, DbT: WalletWrite, + ::AccountId: ConditionallySelectable + Default + Send + 'static, { // Fetch the UnifiedFullViewingKeys we are tracking let ufvks = data_db @@ -374,7 +378,7 @@ where sapling_nullifiers.extend(scanned_block.transactions.iter().flat_map(|tx| { tx.sapling_outputs .iter() - .map(|out| (out.account(), *out.nf())) + .map(|out| (*out.account(), *out.nf())) })); prior_block_metadata = Some(scanned_block.to_block_metadata()); diff --git a/zcash_client_backend/src/data_api/wallet.rs b/zcash_client_backend/src/data_api/wallet.rs index c8f383a16..96806d69b 100644 --- a/zcash_client_backend/src/data_api/wallet.rs +++ b/zcash_client_backend/src/data_api/wallet.rs @@ -1,11 +1,11 @@ -use std::num::NonZeroU32; - use nonempty::NonEmpty; use rand_core::OsRng; use sapling::{ note_encryption::{try_sapling_note_decryption, PreparedIncomingViewingKey}, prover::{OutputProver, SpendProver}, }; +use std::num::NonZeroU32; + use zcash_primitives::{ consensus::{self, BlockHeight, NetworkUpgrade}, memo::MemoBytes, @@ -15,9 +15,10 @@ use zcash_primitives::{ fees::{zip317::FeeError as Zip317FeeError, FeeRule, StandardFeeRule}, Transaction, TxId, }, - zip32::{AccountId, Scope}, + zip32::Scope, }; +use super::InputSource; use crate::{ address::Address, data_api::{ @@ -33,13 +34,6 @@ use crate::{ PoolType, ShieldedProtocol, }; -pub mod input_selection; -use input_selection::{ - GreedyInputSelector, GreedyInputSelectorError, InputSelector, InputSelectorError, -}; - -use super::InputSource; - #[cfg(feature = "transparent-inputs")] use { input_selection::ShieldingSelector, @@ -49,6 +43,11 @@ use { zcash_primitives::transaction::components::{OutPoint, TxOut}, }; +pub mod input_selection; +use input_selection::{ + GreedyInputSelector, GreedyInputSelectorError, InputSelector, InputSelectorError, +}; + /// Scans a [`Transaction`] for any information that can be decrypted by the accounts in /// the wallet, and saves it to the wallet. pub fn decrypt_and_store_transaction( @@ -222,8 +221,12 @@ pub fn create_spend_to_address( > where ParamsT: consensus::Parameters + Clone, - DbT: WalletWrite + WalletCommitmentTrees + InputSource::Error>, - ::NoteRef: Copy + Eq + Ord, + DbT: InputSource, + DbT: WalletWrite< + Error = ::Error, + AccountId = ::AccountId, + >, + DbT: WalletCommitmentTrees, { let account = wallet_db .get_account_for_ufvk(&usk.to_unified_full_viewing_key()) @@ -328,8 +331,12 @@ pub fn spend( >, > where - DbT: WalletWrite + WalletCommitmentTrees + InputSource::Error>, - ::NoteRef: Copy + Eq + Ord, + DbT: InputSource, + DbT: WalletWrite< + Error = ::Error, + AccountId = ::AccountId, + >, + DbT: WalletCommitmentTrees, ParamsT: consensus::Parameters + Clone, InputsT: InputSelector, { @@ -366,7 +373,7 @@ where pub fn propose_transfer( wallet_db: &mut DbT, params: &ParamsT, - spend_from_account: AccountId, + spend_from_account: ::AccountId, input_selector: &InputsT, request: zip321::TransactionRequest, min_confirmations: NonZeroU32, @@ -433,7 +440,7 @@ pub fn propose_standard_transfer_to_address( wallet_db: &mut DbT, params: &ParamsT, fee_rule: StandardFeeRule, - spend_from_account: AccountId, + spend_from_account: ::AccountId, min_confirmations: NonZeroU32, to: &Address, amount: NonNegativeAmount, @@ -451,7 +458,11 @@ pub fn propose_standard_transfer_to_address( > where ParamsT: consensus::Parameters + Clone, - DbT: WalletRead + InputSource::Error>, + DbT: InputSource, + DbT: WalletRead< + Error = ::Error, + AccountId = ::AccountId, + >, DbT::NoteRef: Copy + Eq + Ord, { let request = zip321::TransactionRequest::new(vec![Payment { @@ -1057,7 +1068,7 @@ where .expect("An action should exist in the transaction for each Orchard output."); let recipient = recipient - .map_internal_account(|pool| { + .map_internal_account_note(|pool| { assert!(pool == PoolType::Shielded(ShieldedProtocol::Orchard)); build_result .transaction() @@ -1068,7 +1079,7 @@ where .map(|(note, _, _)| Note::Orchard(note)) }) }) - .internal_account_transpose_option() + .internal_account_note_transpose_option() .expect("Wallet-internal outputs must be decryptable with the wallet's IVK"); SentTransactionOutput::from_parts(output_index, recipient, value, memo) @@ -1087,7 +1098,7 @@ where .expect("An output should exist in the transaction for each Sapling payment."); let recipient = recipient - .map_internal_account(|pool| { + .map_internal_account_note(|pool| { assert!(pool == PoolType::Shielded(ShieldedProtocol::Sapling)); build_result .transaction() @@ -1104,7 +1115,7 @@ where .map(|(note, _, _)| Note::Sapling(note)) }) }) - .internal_account_transpose_option() + .internal_account_note_transpose_option() .expect("Wallet-internal outputs must be decryptable with the wallet's IVK"); SentTransactionOutput::from_parts(output_index, recipient, value, memo) diff --git a/zcash_client_backend/src/data_api/wallet/input_selection.rs b/zcash_client_backend/src/data_api/wallet/input_selection.rs index 45502c379..ebf891ca1 100644 --- a/zcash_client_backend/src/data_api/wallet/input_selection.rs +++ b/zcash_client_backend/src/data_api/wallet/input_selection.rs @@ -17,7 +17,6 @@ use zcash_primitives::{ }, fees::FeeRule, }, - zip32::AccountId, }; use crate::{ @@ -149,7 +148,7 @@ pub trait InputSelector { wallet_db: &Self::InputSource, target_height: BlockHeight, anchor_height: BlockHeight, - account: AccountId, + account: ::AccountId, transaction_request: TransactionRequest, ) -> Result< Proposal::NoteRef>, @@ -329,7 +328,7 @@ where wallet_db: &Self::InputSource, target_height: BlockHeight, anchor_height: BlockHeight, - account: AccountId, + account: ::AccountId, transaction_request: TransactionRequest, ) -> Result< Proposal, diff --git a/zcash_client_backend/src/decrypt.rs b/zcash_client_backend/src/decrypt.rs index 62c1fb8bd..e658b0658 100644 --- a/zcash_client_backend/src/decrypt.rs +++ b/zcash_client_backend/src/decrypt.rs @@ -7,7 +7,7 @@ use zcash_primitives::{ consensus::{self, BlockHeight}, memo::MemoBytes, transaction::Transaction, - zip32::{AccountId, Scope}, + zip32::Scope, }; use crate::keys::UnifiedFullViewingKey; @@ -27,7 +27,7 @@ pub enum TransferType { } /// A decrypted shielded output. -pub struct DecryptedOutput { +pub struct DecryptedOutput { /// The index of the output within [`shielded_outputs`]. /// /// [`shielded_outputs`]: zcash_primitives::transaction::TransactionData @@ -47,12 +47,12 @@ pub struct DecryptedOutput { /// Scans a [`Transaction`] for any information that can be decrypted by the set of /// [`UnifiedFullViewingKey`]s. -pub fn decrypt_transaction( +pub fn decrypt_transaction( params: &P, height: BlockHeight, tx: &Transaction, - ufvks: &HashMap, -) -> Vec> { + ufvks: &HashMap, +) -> Vec> { let zip212_enforcement = consensus::sapling_zip212_enforcement(params, height); tx.sapling_bundle() .iter() @@ -60,7 +60,9 @@ pub fn decrypt_transaction( ufvks .iter() .flat_map(move |(account, ufvk)| { - ufvk.sapling().into_iter().map(|dfvk| (*account, dfvk)) + ufvk.sapling() + .into_iter() + .map(|dfvk| (account.to_owned(), dfvk)) }) .flat_map(move |(account, dfvk)| { let ivk_external = @@ -74,6 +76,7 @@ pub fn decrypt_transaction( .iter() .enumerate() .flat_map(move |(index, output)| { + let account = account.clone(); try_sapling_note_decryption(&ivk_external, output, zip212_enforcement) .map(|ret| (ret, TransferType::Incoming)) .or_else(|| { @@ -92,7 +95,7 @@ pub fn decrypt_transaction( .map(move |((note, _, memo), transfer_type)| DecryptedOutput { index, note, - account, + account: account.clone(), memo: MemoBytes::from_bytes(&memo).expect("correct length"), transfer_type, }) diff --git a/zcash_client_backend/src/scanning.rs b/zcash_client_backend/src/scanning.rs index 38d03881f..348756a61 100644 --- a/zcash_client_backend/src/scanning.rs +++ b/zcash_client_backend/src/scanning.rs @@ -3,6 +3,7 @@ use std::collections::{HashMap, HashSet}; use std::convert::TryFrom; use std::fmt::{self, Debug}; +use std::hash::Hash; use incrementalmerkletree::{Position, Retention}; use sapling::{ @@ -13,10 +14,7 @@ use sapling::{ use subtle::{ConditionallySelectable, ConstantTimeEq, CtOption}; use zcash_note_encryption::batch; use zcash_primitives::consensus::{BlockHeight, NetworkUpgrade}; -use zcash_primitives::{ - consensus, - zip32::{AccountId, Scope}, -}; +use zcash_primitives::{consensus, zip32::Scope}; use crate::data_api::{BlockMetadata, ScannedBlock, ScannedBundles}; use crate::{ @@ -251,14 +249,18 @@ impl fmt::Display for ScanError { /// [`IncrementalWitness`]: sapling::IncrementalWitness /// [`WalletSaplingOutput`]: crate::wallet::WalletSaplingOutput /// [`WalletTx`]: crate::wallet::WalletTx -pub fn scan_block( +pub fn scan_block< + P: consensus::Parameters + Send + 'static, + K: ScanningKey, + A: Default + Eq + Hash + Send + ConditionallySelectable + 'static, +>( params: &P, block: CompactBlock, - vks: &[(&AccountId, &K)], - sapling_nullifiers: &[(AccountId, sapling::Nullifier)], + vks: &[(&A, &K)], + sapling_nullifiers: &[(A, sapling::Nullifier)], prior_block_metadata: Option<&BlockMetadata>, -) -> Result, ScanError> { - scan_block_with_runner::<_, _, ()>( +) -> Result, ScanError> { + scan_block_with_runner::<_, _, (), A>( params, block, vks, @@ -268,20 +270,20 @@ pub fn scan_block( ) } -type TaggedBatch = - Batch<(AccountId, S), SaplingDomain, CompactOutputDescription, CompactDecryptor>; -type TaggedBatchRunner = - BatchRunner<(AccountId, S), SaplingDomain, CompactOutputDescription, CompactDecryptor, T>; +type TaggedBatch = Batch<(A, S), SaplingDomain, CompactOutputDescription, CompactDecryptor>; +type TaggedBatchRunner = + BatchRunner<(A, S), SaplingDomain, CompactOutputDescription, CompactDecryptor, T>; #[tracing::instrument(skip_all, fields(height = block.height))] -pub(crate) fn add_block_to_runner( +pub(crate) fn add_block_to_runner( params: &P, block: CompactBlock, - batch_runner: &mut TaggedBatchRunner, + batch_runner: &mut TaggedBatchRunner, ) where P: consensus::Parameters + Send + 'static, S: Clone + Send + 'static, - T: Tasks>, + T: Tasks>, + A: Copy + Default + Eq + Send + 'static, { let block_hash = block.hash(); let block_height = block.height(); @@ -333,15 +335,16 @@ fn check_hash_continuity( pub(crate) fn scan_block_with_runner< P: consensus::Parameters + Send + 'static, K: ScanningKey, - T: Tasks> + Sync, + T: Tasks> + Sync, + A: Send + Default + Eq + Hash + ConditionallySelectable + 'static, >( params: &P, block: CompactBlock, - vks: &[(&AccountId, K)], - nullifiers: &[(AccountId, sapling::Nullifier)], + vks: &[(&A, K)], + nullifiers: &[(A, sapling::Nullifier)], prior_block_metadata: Option<&BlockMetadata>, - mut batch_runner: Option<&mut TaggedBatchRunner>, -) -> Result, ScanError> { + mut batch_runner: Option<&mut TaggedBatchRunner>, +) -> Result, ScanError> { if let Some(scan_error) = check_hash_continuity(&block, prior_block_metadata) { return Err(scan_error); } @@ -444,7 +447,7 @@ pub(crate) fn scan_block_with_runner< )?; let compact_block_tx_count = block.vtx.len(); - let mut wtxs: Vec> = vec![]; + let mut wtxs: Vec> = vec![]; let mut sapling_nullifier_map = Vec::with_capacity(block.vtx.len()); let mut sapling_note_commitments: Vec<(sapling::Node, Retention)> = vec![]; for (tx_idx, tx) in block.vtx.into_iter().enumerate() { @@ -468,7 +471,7 @@ pub(crate) fn scan_block_with_runner< let spend = nullifiers .iter() .map(|&(account, nf)| CtOption::new(account, nf.ct_eq(&spend_nf))) - .fold(CtOption::new(AccountId::ZERO, 0.into()), |first, next| { + .fold(CtOption::new(A::default(), 0.into()), |first, next| { CtOption::conditional_select(&next, &first, first.is_some()) }) .map(|account| WalletSaplingSpend::from_parts(index, spend_nf, account)); @@ -498,7 +501,7 @@ pub(crate) fn scan_block_with_runner< u32::try_from(tx.actions.len()).expect("Orchard action count cannot exceed a u32"); // Check for incoming notes while incrementing tree and witnesses - let mut shielded_outputs: Vec> = vec![]; + let mut shielded_outputs: Vec> = vec![]; { let decoded = &tx .outputs @@ -874,7 +877,7 @@ mod tests { assert_eq!(tx.sapling_spends.len(), 0); assert_eq!(tx.sapling_outputs.len(), 1); assert_eq!(tx.sapling_outputs[0].index(), 0); - assert_eq!(tx.sapling_outputs[0].account(), account); + assert_eq!(*tx.sapling_outputs[0].account(), account); assert_eq!(tx.sapling_outputs[0].note().value().inner(), 5); assert_eq!( tx.sapling_outputs[0].note_commitment_tree_position(), @@ -955,7 +958,7 @@ mod tests { assert_eq!(tx.sapling_spends.len(), 0); assert_eq!(tx.sapling_outputs.len(), 1); assert_eq!(tx.sapling_outputs[0].index(), 0); - assert_eq!(tx.sapling_outputs[0].account(), AccountId::ZERO); + assert_eq!(*tx.sapling_outputs[0].account(), AccountId::ZERO); assert_eq!(tx.sapling_outputs[0].note().value().inner(), 5); assert_eq!( @@ -1010,7 +1013,7 @@ mod tests { assert_eq!(tx.sapling_outputs.len(), 0); assert_eq!(tx.sapling_spends[0].index(), 0); assert_eq!(tx.sapling_spends[0].nf(), &nf); - assert_eq!(tx.sapling_spends[0].account(), account); + assert_eq!(tx.sapling_spends[0].account().to_owned(), account); assert_eq!( scanned_block diff --git a/zcash_client_backend/src/wallet.rs b/zcash_client_backend/src/wallet.rs index c12143df7..ea0f3372c 100644 --- a/zcash_client_backend/src/wallet.rs +++ b/zcash_client_backend/src/wallet.rs @@ -14,7 +14,7 @@ use zcash_primitives::{ fees::transparent as transparent_fees, TxId, }, - zip32::{AccountId, Scope}, + zip32::Scope, }; use crate::{address::UnifiedAddress, fees::sapling as sapling_fees, PoolType, ShieldedProtocol}; @@ -65,15 +65,15 @@ impl NoteId { /// internal account ID and the pool to which funds were sent in the case of a wallet-internal /// output. #[derive(Debug, Clone)] -pub enum Recipient { +pub enum Recipient { Transparent(TransparentAddress), Sapling(sapling::PaymentAddress), Unified(UnifiedAddress, PoolType), InternalAccount(AccountId, N), } -impl Recipient { - pub fn map_internal_account B>(self, f: F) -> Recipient { +impl Recipient { + pub fn map_internal_account_note B>(self, f: F) -> Recipient { match self { Recipient::Transparent(t) => Recipient::Transparent(t), Recipient::Sapling(s) => Recipient::Sapling(s), @@ -83,8 +83,8 @@ impl Recipient { } } -impl Recipient> { - pub fn internal_account_transpose_option(self) -> Option> { +impl Recipient> { + pub fn internal_account_note_transpose_option(self) -> Option> { match self { Recipient::Transparent(t) => Some(Recipient::Transparent(t)), Recipient::Sapling(s) => Some(Recipient::Sapling(s)), @@ -97,11 +97,11 @@ impl Recipient> { /// A subset of a [`Transaction`] relevant to wallets and light clients. /// /// [`Transaction`]: zcash_primitives::transaction::Transaction -pub struct WalletTx { +pub struct WalletTx { pub txid: TxId, pub index: usize, - pub sapling_spends: Vec, - pub sapling_outputs: Vec>, + pub sapling_spends: Vec>, + pub sapling_outputs: Vec>, } #[derive(Debug, Clone, PartialEq, Eq)] @@ -161,13 +161,13 @@ impl transparent_fees::InputView for WalletTransparentOutput { /// A subset of a [`SpendDescription`] relevant to wallets and light clients. /// /// [`SpendDescription`]: sapling::bundle::SpendDescription -pub struct WalletSaplingSpend { +pub struct WalletSaplingSpend { index: usize, nf: sapling::Nullifier, account: AccountId, } -impl WalletSaplingSpend { +impl WalletSaplingSpend { pub fn from_parts(index: usize, nf: sapling::Nullifier, account: AccountId) -> Self { Self { index, nf, account } } @@ -178,8 +178,8 @@ impl WalletSaplingSpend { pub fn nf(&self) -> &sapling::Nullifier { &self.nf } - pub fn account(&self) -> AccountId { - self.account + pub fn account(&self) -> &AccountId { + &self.account } } @@ -195,11 +195,11 @@ impl WalletSaplingSpend { /// `()` for sent notes. /// /// [`OutputDescription`]: sapling::bundle::OutputDescription -pub struct WalletSaplingOutput { +pub struct WalletSaplingOutput { index: usize, cmu: sapling::note::ExtractedNoteCommitment, ephemeral_key: EphemeralKeyBytes, - account: AccountId, + account: A, note: sapling::Note, is_change: bool, note_commitment_tree_position: Position, @@ -207,14 +207,14 @@ pub struct WalletSaplingOutput { recipient_key_scope: S, } -impl WalletSaplingOutput { +impl WalletSaplingOutput { /// Constructs a new `WalletSaplingOutput` value from its constituent parts. #[allow(clippy::too_many_arguments)] pub fn from_parts( index: usize, cmu: sapling::note::ExtractedNoteCommitment, ephemeral_key: EphemeralKeyBytes, - account: AccountId, + account: A, note: sapling::Note, is_change: bool, note_commitment_tree_position: Position, @@ -243,8 +243,8 @@ impl WalletSaplingOutput { pub fn ephemeral_key(&self) -> &EphemeralKeyBytes { &self.ephemeral_key } - pub fn account(&self) -> AccountId { - self.account + pub fn account(&self) -> &A { + &self.account } pub fn note(&self) -> &sapling::Note { &self.note diff --git a/zcash_client_sqlite/src/lib.rs b/zcash_client_sqlite/src/lib.rs index 365d9b183..af9c3dbe4 100644 --- a/zcash_client_sqlite/src/lib.rs +++ b/zcash_client_sqlite/src/lib.rs @@ -179,6 +179,7 @@ impl WalletDb { impl, P: consensus::Parameters> InputSource for WalletDb { type Error = SqliteClientError; type NoteRef = ReceivedNoteId; + type AccountId = AccountId; fn get_spendable_note( &self, @@ -242,6 +243,7 @@ impl, P: consensus::Parameters> InputSource for impl, P: consensus::Parameters> WalletRead for WalletDb { type Error = SqliteClientError; + type AccountId = AccountId; fn chain_height(&self) -> Result, Self::Error> { wallet::scan_queue_extrema(self.conn.borrow()) @@ -322,7 +324,7 @@ impl, P: consensus::Parameters> WalletRead for W fn get_wallet_summary( &self, min_confirmations: u32, - ) -> Result, Self::Error> { + ) -> Result>, Self::Error> { // This will return a runtime error if we call `get_wallet_summary` from two // threads at the same time, as transactions cannot nest. wallet::get_wallet_summary( @@ -356,23 +358,6 @@ impl, P: consensus::Parameters> WalletRead for W } } - #[cfg(feature = "transparent-inputs")] - fn get_transparent_receivers( - &self, - _account: AccountId, - ) -> Result>, Self::Error> { - wallet::get_transparent_receivers(self.conn.borrow(), &self.params, _account) - } - - #[cfg(feature = "transparent-inputs")] - fn get_transparent_balances( - &self, - _account: AccountId, - _max_height: BlockHeight, - ) -> Result, Self::Error> { - wallet::get_transparent_balances(self.conn.borrow(), &self.params, _account, _max_height) - } - #[cfg(feature = "orchard")] fn get_orchard_nullifiers( &self, @@ -381,6 +366,23 @@ impl, P: consensus::Parameters> WalletRead for W todo!() } + #[cfg(feature = "transparent-inputs")] + fn get_transparent_receivers( + &self, + account: AccountId, + ) -> Result>, Self::Error> { + wallet::get_transparent_receivers(self.conn.borrow(), &self.params, account) + } + + #[cfg(feature = "transparent-inputs")] + fn get_transparent_balances( + &self, + account: AccountId, + max_height: BlockHeight, + ) -> Result, Self::Error> { + wallet::get_transparent_balances(self.conn.borrow(), &self.params, account, max_height) + } + fn get_account_ids(&self) -> Result, Self::Error> { wallet::get_account_ids(self.conn.borrow()) } @@ -452,7 +454,7 @@ impl WalletWrite for WalletDb #[allow(clippy::type_complexity)] fn put_blocks( &mut self, - blocks: Vec>, + blocks: Vec>, ) -> Result<(), Self::Error> { self.transactionally(|wdb| { let start_positions = blocks.first().map(|block| { @@ -591,7 +593,10 @@ impl WalletWrite for WalletDb Ok(()) } - fn store_decrypted_tx(&mut self, d_tx: DecryptedTransaction) -> Result<(), Self::Error> { + fn store_decrypted_tx( + &mut self, + d_tx: DecryptedTransaction, + ) -> Result<(), Self::Error> { self.transactionally(|wdb| { let tx_ref = wallet::put_tx_data(wdb.conn.0, d_tx.tx, None, None)?; @@ -683,7 +688,7 @@ impl WalletWrite for WalletDb }) } - fn store_sent_tx(&mut self, sent_tx: &SentTransaction) -> Result<(), Self::Error> { + fn store_sent_tx(&mut self, sent_tx: &SentTransaction) -> Result<(), Self::Error> { self.transactionally(|wdb| { let tx_ref = wallet::put_tx_data( wdb.conn.0, diff --git a/zcash_client_sqlite/src/testing.rs b/zcash_client_sqlite/src/testing.rs index 43e1c8f54..4c164b3d1 100644 --- a/zcash_client_sqlite/src/testing.rs +++ b/zcash_client_sqlite/src/testing.rs @@ -731,7 +731,10 @@ impl TestState { }) } - pub(crate) fn get_wallet_summary(&self, min_confirmations: u32) -> Option { + pub(crate) fn get_wallet_summary( + &self, + min_confirmations: u32, + ) -> Option> { get_wallet_summary( &self.wallet().conn.unchecked_transaction().unwrap(), &self.wallet().params, diff --git a/zcash_client_sqlite/src/wallet.rs b/zcash_client_sqlite/src/wallet.rs index b0b1aa03e..7db47d201 100644 --- a/zcash_client_sqlite/src/wallet.rs +++ b/zcash_client_sqlite/src/wallet.rs @@ -67,42 +67,41 @@ use incrementalmerkletree::Retention; use rusqlite::{self, named_params, OptionalExtension}; use shardtree::{error::ShardTreeError, store::ShardStore, ShardTree}; -use std::collections::{BTreeMap, HashMap}; +use std::collections::HashMap; use std::convert::TryFrom; use std::io::{self, Cursor}; use std::num::NonZeroU32; use std::ops::RangeInclusive; use tracing::debug; -use zcash_client_backend::data_api::{AccountBalance, Ratio, WalletSummary}; -use zcash_client_backend::wallet::Note; -use zcash_primitives::transaction::components::amount::NonNegativeAmount; -use zcash_primitives::zip32::Scope; - -use zcash_primitives::{ - block::BlockHash, - consensus::{self, BlockHeight, BranchId, NetworkUpgrade, Parameters}, - memo::{Memo, MemoBytes}, - merkle_tree::read_commitment_tree, - transaction::{components::Amount, Transaction, TransactionData, TxId}, - zip32::{AccountId, DiversifierIndex}, -}; use zcash_client_backend::{ address::{Address, UnifiedAddress}, data_api::{ scanning::{ScanPriority, ScanRange}, - AccountBirthday, BlockMetadata, SentTransactionOutput, SAPLING_SHARD_HEIGHT, + AccountBalance, AccountBirthday, BlockMetadata, Ratio, SentTransactionOutput, + WalletSummary, SAPLING_SHARD_HEIGHT, }, encoding::AddressCodec, keys::UnifiedFullViewingKey, - wallet::{NoteId, Recipient, WalletTx}, + wallet::{Note, NoteId, Recipient, WalletTx}, PoolType, ShieldedProtocol, }; +use zcash_primitives::{ + block::BlockHash, + consensus::{self, BlockHeight, BranchId, NetworkUpgrade, Parameters}, + memo::{Memo, MemoBytes}, + merkle_tree::read_commitment_tree, + transaction::{ + components::{amount::NonNegativeAmount, Amount}, + Transaction, TransactionData, TxId, + }, + zip32::{AccountId, DiversifierIndex, Scope}, +}; -use crate::wallet::commitment_tree::{get_max_checkpointed_height, SqliteShardStore}; -use crate::DEFAULT_UA_REQUEST; use crate::{ - error::SqliteClientError, SqlTransaction, WalletCommitmentTrees, WalletDb, PRUNING_DEPTH, + error::SqliteClientError, + wallet::commitment_tree::{get_max_checkpointed_height, SqliteShardStore}, + SqlTransaction, WalletCommitmentTrees, WalletDb, DEFAULT_UA_REQUEST, PRUNING_DEPTH, SAPLING_TABLES_PREFIX, }; @@ -603,7 +602,7 @@ pub(crate) fn get_wallet_summary( params: &P, min_confirmations: u32, progress: &impl ScanProgress, -) -> Result, SqliteClientError> { +) -> Result>, SqliteClientError> { let chain_tip_height = match scan_queue_extrema(tx)? { Some(range) => *range.end(), None => { @@ -655,7 +654,7 @@ pub(crate) fn get_wallet_summary( .map_err(|_| SqliteClientError::AccountIdOutOfRange) .map(|a| (a, AccountBalance::ZERO)) }) - .collect::, _>>()?; + .collect::, _>>()?; let sapling_trace = tracing::info_span!("stmt_select_notes").entered(); let mut stmt_select_notes = tx.prepare_cached( @@ -1608,7 +1607,7 @@ pub(crate) fn put_block( /// contain a note related to this wallet into the database. pub(crate) fn put_tx_meta( conn: &rusqlite::Connection, - tx: &WalletTx, + tx: &WalletTx, height: BlockHeight, ) -> Result { // It isn't there, so insert our transaction into the database. @@ -1795,7 +1794,7 @@ pub(crate) fn update_expired_notes( // and `put_sent_output` fn recipient_params( params: &P, - to: &Recipient, + to: &Recipient, ) -> (Option, Option, PoolType) { match to { Recipient::Transparent(addr) => (Some(addr.encode(params)), None, PoolType::Transparent), @@ -1819,7 +1818,7 @@ pub(crate) fn insert_sent_output( params: &P, tx_ref: i64, from_account: AccountId, - output: &SentTransactionOutput, + output: &SentTransactionOutput, ) -> Result<(), SqliteClientError> { let mut stmt_insert_sent_output = conn.prepare_cached( "INSERT INTO sent_notes ( @@ -1865,7 +1864,7 @@ pub(crate) fn put_sent_output( from_account: AccountId, tx_ref: i64, output_index: usize, - recipient: &Recipient, + recipient: &Recipient, value: NonNegativeAmount, memo: Option<&MemoBytes>, ) -> Result<(), SqliteClientError> { @@ -2034,7 +2033,7 @@ pub(crate) fn query_nullifier_map, S>( // change or explicit in-wallet recipient. put_tx_meta( conn, - &WalletTx:: { + &WalletTx:: { txid, index, sapling_spends: vec![], diff --git a/zcash_client_sqlite/src/wallet/sapling.rs b/zcash_client_sqlite/src/wallet/sapling.rs index 7061e62e8..84a86d190 100644 --- a/zcash_client_sqlite/src/wallet/sapling.rs +++ b/zcash_client_sqlite/src/wallet/sapling.rs @@ -38,12 +38,12 @@ pub(crate) trait ReceivedSaplingOutput { fn recipient_key_scope(&self) -> Scope; } -impl ReceivedSaplingOutput for WalletSaplingOutput { +impl ReceivedSaplingOutput for WalletSaplingOutput { fn index(&self) -> usize { self.index() } fn account(&self) -> AccountId { - WalletSaplingOutput::account(self) + *WalletSaplingOutput::account(self) } fn note(&self) -> &sapling::Note { WalletSaplingOutput::note(self) @@ -66,7 +66,7 @@ impl ReceivedSaplingOutput for WalletSaplingOutput { } } -impl ReceivedSaplingOutput for DecryptedOutput { +impl ReceivedSaplingOutput for DecryptedOutput { fn index(&self) -> usize { self.index }