Merge pull request #1202 from nuttycom/generic_account_id

zcash_client_backend: Make `AccountId` an associated type of `WalletRead`
This commit is contained in:
Kris Nuttycombe 2024-02-29 11:53:26 -07:00 committed by GitHub
commit e2331dbd6f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 297 additions and 203 deletions

View File

@ -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<u32>` 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.

View File

@ -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<T> Ratio<T> {
/// 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<AccountId, AccountBalance>,
pub struct WalletSummary<AccountId: Eq + Hash> {
account_balances: HashMap<AccountId, AccountBalance>,
chain_tip_height: BlockHeight,
fully_scanned_height: BlockHeight,
scan_progress: Option<Ratio<u64>>,
next_sapling_subtree_index: u64,
}
impl WalletSummary {
impl<AccountId: Eq + Hash> WalletSummary<AccountId> {
/// Constructs a new [`WalletSummary`] from its constituent parts.
pub fn new(
account_balances: BTreeMap<AccountId, AccountBalance>,
account_balances: HashMap<AccountId, AccountBalance>,
chain_tip_height: BlockHeight,
fully_scanned_height: BlockHeight,
scan_progress: Option<Ratio<u64>>,
@ -318,7 +317,7 @@ impl WalletSummary {
}
/// Returns the balances of accounts in the wallet, keyed by account ID.
pub fn account_balances(&self) -> &BTreeMap<AccountId, AccountBalance> {
pub fn account_balances(&self) -> &HashMap<AccountId, AccountBalance> {
&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<BlockHeight, Self::Error>;
fn get_account_birthday(&self, account: Self::AccountId) -> Result<BlockHeight, Self::Error>;
/// 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<Option<UnifiedAddress>, Self::Error>;
/// Returns all unified full viewing keys known to this wallet.
fn get_unified_full_viewing_keys(
&self,
) -> Result<HashMap<AccountId, UnifiedFullViewingKey>, Self::Error>;
) -> Result<HashMap<Self::AccountId, UnifiedFullViewingKey>, Self::Error>;
/// Returns the account id corresponding to a given [`UnifiedFullViewingKey`], if any.
fn get_account_for_ufvk(
&self,
ufvk: &UnifiedFullViewingKey,
) -> Result<Option<AccountId>, Self::Error>;
) -> Result<Option<Self::AccountId>, 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<Option<WalletSummary>, Self::Error>;
) -> Result<Option<WalletSummary<Self::AccountId>>, Self::Error>;
/// Returns the memo for a note.
///
@ -549,7 +562,7 @@ pub trait WalletRead {
fn get_sapling_nullifiers(
&self,
query: NullifierQuery,
) -> Result<Vec<(AccountId, sapling::Nullifier)>, Self::Error>;
) -> Result<Vec<(Self::AccountId, sapling::Nullifier)>, 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<Vec<(AccountId, orchard::note::Nullifier)>, Self::Error>;
) -> Result<Vec<(Self::AccountId, orchard::note::Nullifier)>, 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<HashMap<TransparentAddress, Option<TransparentAddressMetadata>>, 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<HashMap<TransparentAddress, Amount>, Self::Error> {
Ok(HashMap::new())
}
/// Returns a vector with the IDs of all accounts known to this wallet.
fn get_account_ids(&self) -> Result<Vec<AccountId>, Self::Error>;
fn get_account_ids(&self) -> Result<Vec<Self::AccountId>, 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<Nf, S> {
pub struct ScannedBlock<Nf, S, A> {
block_height: BlockHeight,
block_hash: BlockHash,
block_time: u32,
transactions: Vec<WalletTx<Nf, S>>,
transactions: Vec<WalletTx<Nf, S, A>>,
sapling: ScannedBundles<sapling::Node, sapling::Nullifier>,
#[cfg(feature = "orchard")]
orchard: ScannedBundles<orchard::note::NoteCommitment, orchard::note::Nullifier>,
}
impl<Nf, S> ScannedBlock<Nf, S> {
impl<Nf, S, A> ScannedBlock<Nf, S, A> {
/// Constructs a new `ScannedBlock`
pub(crate) fn from_parts(
block_height: BlockHeight,
block_hash: BlockHash,
block_time: u32,
transactions: Vec<WalletTx<Nf, S>>,
transactions: Vec<WalletTx<Nf, S, A>>,
sapling: ScannedBundles<sapling::Node, sapling::Nullifier>,
#[cfg(feature = "orchard")] orchard: ScannedBundles<
orchard::note::NoteCommitment,
@ -750,7 +763,7 @@ impl<Nf, S> ScannedBlock<Nf, S> {
}
/// Returns the list of transactions from this block that are relevant to the wallet.
pub fn transactions(&self) -> &[WalletTx<Nf, S>] {
pub fn transactions(&self) -> &[WalletTx<Nf, S, A>] {
&self.transactions
}
@ -794,9 +807,9 @@ impl<Nf, S> ScannedBlock<Nf, S> {
///
/// 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<DecryptedOutput<sapling::Note>>,
pub sapling_outputs: &'a Vec<DecryptedOutput<sapling::Note, AccountId>>,
}
/// 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<SentTransactionOutput>,
pub outputs: Vec<SentTransactionOutput<AccountId>>,
pub fee_amount: Amount,
#[cfg(feature = "transparent-inputs")]
pub utxos_spent: Vec<OutPoint>,
@ -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<AccountId> {
output_index: usize,
recipient: Recipient<Note>,
recipient: Recipient<AccountId, Note>,
value: NonNegativeAmount,
memo: Option<MemoBytes>,
}
impl SentTransactionOutput {
impl<AccountId> SentTransactionOutput<AccountId> {
/// 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<Note>,
recipient: Recipient<AccountId, Note>,
value: NonNegativeAmount,
memo: Option<MemoBytes>,
) -> 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<Note> {
pub fn recipient(&self) -> &Recipient<AccountId, Note> {
&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<u8>,
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<Option<UnifiedAddress>, 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<ScannedBlock<sapling::Nullifier, Scope>>,
blocks: Vec<ScannedBlock<sapling::Nullifier, Scope, Self::AccountId>>,
) -> 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<Self::AccountId>,
) -> 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<Self::AccountId>,
) -> 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<Option<BlockHeight>, Self::Error> {
Ok(None)
@ -1296,34 +1319,37 @@ pub mod testing {
Ok(None)
}
fn get_account_birthday(&self, _account: AccountId) -> Result<BlockHeight, Self::Error> {
fn get_account_birthday(
&self,
_account: Self::AccountId,
) -> Result<BlockHeight, Self::Error> {
Err(())
}
fn get_current_address(
&self,
_account: AccountId,
_account: Self::AccountId,
) -> Result<Option<UnifiedAddress>, Self::Error> {
Ok(None)
}
fn get_unified_full_viewing_keys(
&self,
) -> Result<HashMap<AccountId, UnifiedFullViewingKey>, Self::Error> {
) -> Result<HashMap<Self::AccountId, UnifiedFullViewingKey>, Self::Error> {
Ok(HashMap::new())
}
fn get_account_for_ufvk(
&self,
_ufvk: &UnifiedFullViewingKey,
) -> Result<Option<AccountId>, Self::Error> {
) -> Result<Option<Self::AccountId>, Self::Error> {
Ok(None)
}
fn get_wallet_summary(
&self,
_min_confirmations: u32,
) -> Result<Option<WalletSummary>, Self::Error> {
) -> Result<Option<WalletSummary<Self::AccountId>>, Self::Error> {
Ok(None)
}
@ -1338,7 +1364,7 @@ pub mod testing {
fn get_sapling_nullifiers(
&self,
_query: NullifierQuery,
) -> Result<Vec<(AccountId, sapling::Nullifier)>, Self::Error> {
) -> Result<Vec<(Self::AccountId, sapling::Nullifier)>, Self::Error> {
Ok(Vec::new())
}
@ -1346,14 +1372,14 @@ pub mod testing {
fn get_orchard_nullifiers(
&self,
_query: NullifierQuery,
) -> Result<Vec<(AccountId, orchard::note::Nullifier)>, Self::Error> {
) -> Result<Vec<(Self::AccountId, orchard::note::Nullifier)>, Self::Error> {
Ok(Vec::new())
}
#[cfg(feature = "transparent-inputs")]
fn get_transparent_receivers(
&self,
_account: AccountId,
_account: Self::AccountId,
) -> Result<HashMap<TransparentAddress, Option<TransparentAddressMetadata>>, 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<HashMap<TransparentAddress, Amount>, Self::Error> {
Ok(HashMap::new())
}
fn get_account_ids(&self) -> Result<Vec<AccountId>, Self::Error> {
fn get_account_ids(&self) -> Result<Vec<Self::AccountId>, Self::Error> {
Ok(Vec::new())
}
}
@ -1380,16 +1406,16 @@ pub mod testing {
&mut self,
seed: &SecretVec<u8>,
_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<Option<UnifiedAddress>, Self::Error> {
Ok(None)
@ -1398,7 +1424,7 @@ pub mod testing {
#[allow(clippy::type_complexity)]
fn put_blocks(
&mut self,
_blocks: Vec<ScannedBlock<sapling::Nullifier, Scope>>,
_blocks: Vec<ScannedBlock<sapling::Nullifier, Scope, Self::AccountId>>,
) -> Result<(), Self::Error> {
Ok(())
}
@ -1409,12 +1435,15 @@ pub mod testing {
fn store_decrypted_tx(
&mut self,
_received_tx: DecryptedTransaction,
_received_tx: DecryptedTransaction<Self::AccountId>,
) -> 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<Self::AccountId>,
) -> Result<(), Self::Error> {
Ok(())
}

View File

@ -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,
<DbT as WalletRead>::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());

View File

@ -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<ParamsT, DbT>(
@ -222,8 +221,12 @@ pub fn create_spend_to_address<DbT, ParamsT>(
>
where
ParamsT: consensus::Parameters + Clone,
DbT: WalletWrite + WalletCommitmentTrees + InputSource<Error = <DbT as WalletRead>::Error>,
<DbT as InputSource>::NoteRef: Copy + Eq + Ord,
DbT: InputSource,
DbT: WalletWrite<
Error = <DbT as InputSource>::Error,
AccountId = <DbT as InputSource>::AccountId,
>,
DbT: WalletCommitmentTrees,
{
let account = wallet_db
.get_account_for_ufvk(&usk.to_unified_full_viewing_key())
@ -328,8 +331,12 @@ pub fn spend<DbT, ParamsT, InputsT>(
>,
>
where
DbT: WalletWrite + WalletCommitmentTrees + InputSource<Error = <DbT as WalletRead>::Error>,
<DbT as InputSource>::NoteRef: Copy + Eq + Ord,
DbT: InputSource,
DbT: WalletWrite<
Error = <DbT as InputSource>::Error,
AccountId = <DbT as InputSource>::AccountId,
>,
DbT: WalletCommitmentTrees,
ParamsT: consensus::Parameters + Clone,
InputsT: InputSelector<InputSource = DbT>,
{
@ -366,7 +373,7 @@ where
pub fn propose_transfer<DbT, ParamsT, InputsT, CommitmentTreeErrT>(
wallet_db: &mut DbT,
params: &ParamsT,
spend_from_account: AccountId,
spend_from_account: <DbT as InputSource>::AccountId,
input_selector: &InputsT,
request: zip321::TransactionRequest,
min_confirmations: NonZeroU32,
@ -433,7 +440,7 @@ pub fn propose_standard_transfer_to_address<DbT, ParamsT, CommitmentTreeErrT>(
wallet_db: &mut DbT,
params: &ParamsT,
fee_rule: StandardFeeRule,
spend_from_account: AccountId,
spend_from_account: <DbT as InputSource>::AccountId,
min_confirmations: NonZeroU32,
to: &Address,
amount: NonNegativeAmount,
@ -451,7 +458,11 @@ pub fn propose_standard_transfer_to_address<DbT, ParamsT, CommitmentTreeErrT>(
>
where
ParamsT: consensus::Parameters + Clone,
DbT: WalletRead + InputSource<Error = <DbT as WalletRead>::Error>,
DbT: InputSource,
DbT: WalletRead<
Error = <DbT as InputSource>::Error,
AccountId = <DbT as InputSource>::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)

View File

@ -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: <Self::InputSource as InputSource>::AccountId,
transaction_request: TransactionRequest,
) -> Result<
Proposal<Self::FeeRule, <Self::InputSource as InputSource>::NoteRef>,
@ -329,7 +328,7 @@ where
wallet_db: &Self::InputSource,
target_height: BlockHeight,
anchor_height: BlockHeight,
account: AccountId,
account: <DbT as InputSource>::AccountId,
transaction_request: TransactionRequest,
) -> Result<
Proposal<Self::FeeRule, DbT::NoteRef>,

View File

@ -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<Note> {
pub struct DecryptedOutput<Note, AccountId> {
/// The index of the output within [`shielded_outputs`].
///
/// [`shielded_outputs`]: zcash_primitives::transaction::TransactionData
@ -47,12 +47,12 @@ pub struct DecryptedOutput<Note> {
/// Scans a [`Transaction`] for any information that can be decrypted by the set of
/// [`UnifiedFullViewingKey`]s.
pub fn decrypt_transaction<P: consensus::Parameters>(
pub fn decrypt_transaction<P: consensus::Parameters, A: Clone>(
params: &P,
height: BlockHeight,
tx: &Transaction,
ufvks: &HashMap<AccountId, UnifiedFullViewingKey>,
) -> Vec<DecryptedOutput<sapling::Note>> {
ufvks: &HashMap<A, UnifiedFullViewingKey>,
) -> Vec<DecryptedOutput<sapling::Note, A>> {
let zip212_enforcement = consensus::sapling_zip212_enforcement(params, height);
tx.sapling_bundle()
.iter()
@ -60,7 +60,9 @@ pub fn decrypt_transaction<P: consensus::Parameters>(
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<P: consensus::Parameters>(
.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<P: consensus::Parameters>(
.map(move |((note, _, memo), transfer_type)| DecryptedOutput {
index,
note,
account,
account: account.clone(),
memo: MemoBytes::from_bytes(&memo).expect("correct length"),
transfer_type,
})

View File

@ -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<P: consensus::Parameters + Send + 'static, K: ScanningKey>(
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<ScannedBlock<K::Nf, K::Scope>, ScanError> {
scan_block_with_runner::<_, _, ()>(
) -> Result<ScannedBlock<K::Nf, K::Scope, A>, ScanError> {
scan_block_with_runner::<_, _, (), A>(
params,
block,
vks,
@ -268,20 +270,20 @@ pub fn scan_block<P: consensus::Parameters + Send + 'static, K: ScanningKey>(
)
}
type TaggedBatch<S> =
Batch<(AccountId, S), SaplingDomain, CompactOutputDescription, CompactDecryptor>;
type TaggedBatchRunner<S, T> =
BatchRunner<(AccountId, S), SaplingDomain, CompactOutputDescription, CompactDecryptor, T>;
type TaggedBatch<A, S> = Batch<(A, S), SaplingDomain, CompactOutputDescription, CompactDecryptor>;
type TaggedBatchRunner<A, S, T> =
BatchRunner<(A, S), SaplingDomain, CompactOutputDescription, CompactDecryptor, T>;
#[tracing::instrument(skip_all, fields(height = block.height))]
pub(crate) fn add_block_to_runner<P, S, T>(
pub(crate) fn add_block_to_runner<P, S, T, A>(
params: &P,
block: CompactBlock,
batch_runner: &mut TaggedBatchRunner<S, T>,
batch_runner: &mut TaggedBatchRunner<A, S, T>,
) where
P: consensus::Parameters + Send + 'static,
S: Clone + Send + 'static,
T: Tasks<TaggedBatch<S>>,
T: Tasks<TaggedBatch<A, S>>,
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<TaggedBatch<K::Scope>> + Sync,
T: Tasks<TaggedBatch<A, K::Scope>> + 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<K::Scope, T>>,
) -> Result<ScannedBlock<K::Nf, K::Scope>, ScanError> {
mut batch_runner: Option<&mut TaggedBatchRunner<A, K::Scope, T>>,
) -> Result<ScannedBlock<K::Nf, K::Scope, A>, 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<WalletTx<K::Nf, K::Scope>> = vec![];
let mut wtxs: Vec<WalletTx<K::Nf, K::Scope, A>> = vec![];
let mut sapling_nullifier_map = Vec::with_capacity(block.vtx.len());
let mut sapling_note_commitments: Vec<(sapling::Node, Retention<BlockHeight>)> = 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<WalletSaplingOutput<K::Nf, K::Scope>> = vec![];
let mut shielded_outputs: Vec<WalletSaplingOutput<K::Nf, K::Scope, A>> = 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

View File

@ -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<N> {
pub enum Recipient<AccountId, N> {
Transparent(TransparentAddress),
Sapling(sapling::PaymentAddress),
Unified(UnifiedAddress, PoolType),
InternalAccount(AccountId, N),
}
impl<N> Recipient<N> {
pub fn map_internal_account<B, F: FnOnce(N) -> B>(self, f: F) -> Recipient<B> {
impl<AccountId, N> Recipient<AccountId, N> {
pub fn map_internal_account_note<B, F: FnOnce(N) -> B>(self, f: F) -> Recipient<AccountId, B> {
match self {
Recipient::Transparent(t) => Recipient::Transparent(t),
Recipient::Sapling(s) => Recipient::Sapling(s),
@ -83,8 +83,8 @@ impl<N> Recipient<N> {
}
}
impl<N> Recipient<Option<N>> {
pub fn internal_account_transpose_option(self) -> Option<Recipient<N>> {
impl<AccountId, N> Recipient<AccountId, Option<N>> {
pub fn internal_account_note_transpose_option(self) -> Option<Recipient<AccountId, N>> {
match self {
Recipient::Transparent(t) => Some(Recipient::Transparent(t)),
Recipient::Sapling(s) => Some(Recipient::Sapling(s)),
@ -97,11 +97,11 @@ impl<N> Recipient<Option<N>> {
/// A subset of a [`Transaction`] relevant to wallets and light clients.
///
/// [`Transaction`]: zcash_primitives::transaction::Transaction
pub struct WalletTx<N, S> {
pub struct WalletTx<N, S, A> {
pub txid: TxId,
pub index: usize,
pub sapling_spends: Vec<WalletSaplingSpend>,
pub sapling_outputs: Vec<WalletSaplingOutput<N, S>>,
pub sapling_spends: Vec<WalletSaplingSpend<A>>,
pub sapling_outputs: Vec<WalletSaplingOutput<N, S, A>>,
}
#[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<AccountId> {
index: usize,
nf: sapling::Nullifier,
account: AccountId,
}
impl WalletSaplingSpend {
impl<AccountId> WalletSaplingSpend<AccountId> {
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<N, S> {
pub struct WalletSaplingOutput<N, S, A> {
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<N, S> {
recipient_key_scope: S,
}
impl<N, S> WalletSaplingOutput<N, S> {
impl<N, S, A> WalletSaplingOutput<N, S, A> {
/// 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<N, S> WalletSaplingOutput<N, S> {
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

View File

@ -179,6 +179,7 @@ impl<P: consensus::Parameters + Clone> WalletDb<Connection, P> {
impl<C: Borrow<rusqlite::Connection>, P: consensus::Parameters> InputSource for WalletDb<C, P> {
type Error = SqliteClientError;
type NoteRef = ReceivedNoteId;
type AccountId = AccountId;
fn get_spendable_note(
&self,
@ -242,6 +243,7 @@ impl<C: Borrow<rusqlite::Connection>, P: consensus::Parameters> InputSource for
impl<C: Borrow<rusqlite::Connection>, P: consensus::Parameters> WalletRead for WalletDb<C, P> {
type Error = SqliteClientError;
type AccountId = AccountId;
fn chain_height(&self) -> Result<Option<BlockHeight>, Self::Error> {
wallet::scan_queue_extrema(self.conn.borrow())
@ -322,7 +324,7 @@ impl<C: Borrow<rusqlite::Connection>, P: consensus::Parameters> WalletRead for W
fn get_wallet_summary(
&self,
min_confirmations: u32,
) -> Result<Option<WalletSummary>, Self::Error> {
) -> Result<Option<WalletSummary<Self::AccountId>>, 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<C: Borrow<rusqlite::Connection>, P: consensus::Parameters> WalletRead for W
}
}
#[cfg(feature = "transparent-inputs")]
fn get_transparent_receivers(
&self,
_account: AccountId,
) -> Result<HashMap<TransparentAddress, Option<TransparentAddressMetadata>>, 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<HashMap<TransparentAddress, Amount>, 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<C: Borrow<rusqlite::Connection>, P: consensus::Parameters> WalletRead for W
todo!()
}
#[cfg(feature = "transparent-inputs")]
fn get_transparent_receivers(
&self,
account: AccountId,
) -> Result<HashMap<TransparentAddress, Option<TransparentAddressMetadata>>, 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<HashMap<TransparentAddress, Amount>, Self::Error> {
wallet::get_transparent_balances(self.conn.borrow(), &self.params, account, max_height)
}
fn get_account_ids(&self) -> Result<Vec<AccountId>, Self::Error> {
wallet::get_account_ids(self.conn.borrow())
}
@ -452,7 +454,7 @@ impl<P: consensus::Parameters> WalletWrite for WalletDb<rusqlite::Connection, P>
#[allow(clippy::type_complexity)]
fn put_blocks(
&mut self,
blocks: Vec<ScannedBlock<sapling::Nullifier, Scope>>,
blocks: Vec<ScannedBlock<sapling::Nullifier, Scope, Self::AccountId>>,
) -> Result<(), Self::Error> {
self.transactionally(|wdb| {
let start_positions = blocks.first().map(|block| {
@ -591,7 +593,10 @@ impl<P: consensus::Parameters> WalletWrite for WalletDb<rusqlite::Connection, P>
Ok(())
}
fn store_decrypted_tx(&mut self, d_tx: DecryptedTransaction) -> Result<(), Self::Error> {
fn store_decrypted_tx(
&mut self,
d_tx: DecryptedTransaction<AccountId>,
) -> 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<P: consensus::Parameters> WalletWrite for WalletDb<rusqlite::Connection, P>
})
}
fn store_sent_tx(&mut self, sent_tx: &SentTransaction) -> Result<(), Self::Error> {
fn store_sent_tx(&mut self, sent_tx: &SentTransaction<AccountId>) -> Result<(), Self::Error> {
self.transactionally(|wdb| {
let tx_ref = wallet::put_tx_data(
wdb.conn.0,

View File

@ -731,7 +731,10 @@ impl<Cache> TestState<Cache> {
})
}
pub(crate) fn get_wallet_summary(&self, min_confirmations: u32) -> Option<WalletSummary> {
pub(crate) fn get_wallet_summary(
&self,
min_confirmations: u32,
) -> Option<WalletSummary<AccountId>> {
get_wallet_summary(
&self.wallet().conn.unchecked_transaction().unwrap(),
&self.wallet().params,

View File

@ -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<P: consensus::Parameters>(
params: &P,
min_confirmations: u32,
progress: &impl ScanProgress,
) -> Result<Option<WalletSummary>, SqliteClientError> {
) -> Result<Option<WalletSummary<AccountId>>, 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<P: consensus::Parameters>(
.map_err(|_| SqliteClientError::AccountIdOutOfRange)
.map(|a| (a, AccountBalance::ZERO))
})
.collect::<Result<BTreeMap<AccountId, AccountBalance>, _>>()?;
.collect::<Result<HashMap<AccountId, AccountBalance>, _>>()?;
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<N, S>(
conn: &rusqlite::Connection,
tx: &WalletTx<N, S>,
tx: &WalletTx<N, S, AccountId>,
height: BlockHeight,
) -> Result<i64, SqliteClientError> {
// 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<P: consensus::Parameters>(
params: &P,
to: &Recipient<Note>,
to: &Recipient<AccountId, Note>,
) -> (Option<String>, Option<u32>, PoolType) {
match to {
Recipient::Transparent(addr) => (Some(addr.encode(params)), None, PoolType::Transparent),
@ -1819,7 +1818,7 @@ pub(crate) fn insert_sent_output<P: consensus::Parameters>(
params: &P,
tx_ref: i64,
from_account: AccountId,
output: &SentTransactionOutput,
output: &SentTransactionOutput<AccountId>,
) -> 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<P: consensus::Parameters>(
from_account: AccountId,
tx_ref: i64,
output_index: usize,
recipient: &Recipient<Note>,
recipient: &Recipient<AccountId, Note>,
value: NonNegativeAmount,
memo: Option<&MemoBytes>,
) -> Result<(), SqliteClientError> {
@ -2034,7 +2033,7 @@ pub(crate) fn query_nullifier_map<N: AsRef<[u8]>, S>(
// change or explicit in-wallet recipient.
put_tx_meta(
conn,
&WalletTx::<N, S> {
&WalletTx::<N, S, AccountId> {
txid,
index,
sapling_spends: vec![],

View File

@ -38,12 +38,12 @@ pub(crate) trait ReceivedSaplingOutput {
fn recipient_key_scope(&self) -> Scope;
}
impl ReceivedSaplingOutput for WalletSaplingOutput<sapling::Nullifier, Scope> {
impl ReceivedSaplingOutput for WalletSaplingOutput<sapling::Nullifier, Scope, AccountId> {
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<sapling::Nullifier, Scope> {
}
}
impl ReceivedSaplingOutput for DecryptedOutput<sapling::Note> {
impl ReceivedSaplingOutput for DecryptedOutput<sapling::Note, AccountId> {
fn index(&self) -> usize {
self.index
}