Skip to content

Commit

Permalink
zcash_client_backend: Permit spending pool selection.
Browse files Browse the repository at this point in the history
  • Loading branch information
nuttycom committed Mar 9, 2024
1 parent 5511bac commit 94b4719
Show file tree
Hide file tree
Showing 13 changed files with 373 additions and 161 deletions.
15 changes: 12 additions & 3 deletions zcash_client_backend/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ and this library adheres to Rust's notion of
- `zcash_client_backend::data_api`:
- `AccountBalance::with_orchard_balance_mut`
- `AccountBirthday::orchard_frontier`
- `AccountSources`
- `BlockMetadata::orchard_tree_size`
- `DecryptedTransaction::{new, tx(), orchard_outputs()}`
- `ScannedBlock::orchard`
Expand Down Expand Up @@ -49,10 +50,14 @@ and this library adheres to Rust's notion of
- Arguments to `ScannedBlock::from_parts` have changed.
- Changes to the `WalletRead` trait:
- Added `get_orchard_nullifiers`
- `get_account_for_ufvk` now returns an `AccountSources` instead of a bare
`AccountId`
- Changes to the `InputSource` trait:
- `select_spendable_notes` now takes its `target_value` argument as a
`NonNegativeAmount`. Also, the values of the returned map are also
`NonNegativeAmount`s instead of `Amount`s.
- `select_spendable_notes` has changed:
- It now takes its `target_value` argument as a `NonNegativeAmount`.
- Instead of an `AccountId`, it takes an `AccountSources` argument. The
separate `sources` argument has been removed.
- The values of the returned map are `NonNegativeAmount`s instead of `Amount`s.
- Fields of `DecryptedTransaction` are now private. Use `DecryptedTransaction::new`
and the newly provided accessors instead.
- Fields of `SentTransaction` are now private. Use `SentTransaction::new`
Expand All @@ -64,6 +69,10 @@ and this library adheres to Rust's notion of
- `fn put_orchard_subtree_roots`
- Added method `WalletRead::validate_seed`
- Removed `Error::AccountNotFound` variant.
- `wallet::input_selection::InputSelector::propose_transaction` now takes an
`AccountSources` rather than a bare `AccountId`.
- `wallet::{propose_transfer, propose_standard_transfer_to_address}` now
each take an `AccountSources` instead of a bare `AccountId`.
- `zcash_client_backend::decrypt`:
- Fields of `DecryptedOutput` are now private. Use `DecryptedOutput::new`
and the newly provided accessors instead.
Expand Down
79 changes: 70 additions & 9 deletions zcash_client_backend/src/data_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,68 @@ impl AccountBalance {
}
}

/// A type that describes what FVK components are known to the wallet for an account.
#[derive(Clone, Copy, Debug)]
pub struct AccountSources<AccountId> {
account_id: AccountId,
use_orchard: bool,
use_sapling: bool,
use_transparent: bool,
}

impl<AccountId: Copy> AccountSources<AccountId> {
/// Constructs AccountSources from its constituent parts
pub fn new(
account_id: AccountId,
use_orchard: bool,
use_sapling: bool,
use_transparent: bool,
) -> Self {
Self {
account_id,
use_orchard,
use_sapling,
use_transparent,
}
}

/// Returns the id for the account to which this metadata applies.
pub fn account_id(&self) -> AccountId {
self.account_id
}

/// Returns whether the account has an Orchard balance and spendability determination
/// capability.
pub fn use_orchard(&self) -> bool {
self.use_orchard

Check warning on line 344 in zcash_client_backend/src/data_api.rs

View check run for this annotation

Codecov / codecov/patch

zcash_client_backend/src/data_api.rs#L343-L344

Added lines #L343 - L344 were not covered by tests
}

/// Returns whether the account has an Sapling balance and spendability determination
/// capability.
pub fn use_sapling(&self) -> bool {
self.use_sapling
}

/// Returns whether the account has a Transparent balance determination capability.
pub fn use_transparent(&self) -> bool {
self.use_transparent

Check warning on line 355 in zcash_client_backend/src/data_api.rs

View check run for this annotation

Codecov / codecov/patch

zcash_client_backend/src/data_api.rs#L354-L355

Added lines #L354 - L355 were not covered by tests
}

/// Restricts the sources to be used to those for which the given [`UnifiedSpendingKey`]
/// provides a spending capability.
pub fn filter_with_usk(&mut self, usk: &UnifiedSpendingKey) {
self.use_orchard &= usk.has_orchard();
self.use_sapling &= usk.has_sapling();
self.use_transparent &= usk.has_transparent();

Check warning on line 363 in zcash_client_backend/src/data_api.rs

View check run for this annotation

Codecov / codecov/patch

zcash_client_backend/src/data_api.rs#L360-L363

Added lines #L360 - L363 were not covered by tests
}

/// Returns the [`UnifiedAddressRequest`] that will produce a [`UnifiedAddress`] having
/// receivers corresponding to the spending capabilities described by this value.
pub fn to_unified_address_request(&self) -> Option<UnifiedAddressRequest> {
UnifiedAddressRequest::new(self.use_orchard, self.use_sapling, self.use_transparent)
}
}

/// A polymorphic ratio type, usually used for rational numbers.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Ratio<T> {
Expand Down Expand Up @@ -444,9 +506,8 @@ pub trait InputSource {
/// be included.
fn select_spendable_notes(
&self,
account: Self::AccountId,
inputs_from: AccountSources<Self::AccountId>,
target_value: NonNegativeAmount,
sources: &[ShieldedProtocol],
anchor_height: BlockHeight,
exclude: &[Self::NoteRef],
) -> Result<Vec<ReceivedNote<Self::NoteRef, Note>>, Self::Error>;
Expand Down Expand Up @@ -606,7 +667,7 @@ pub trait WalletRead {
fn get_account_for_ufvk(
&self,
ufvk: &UnifiedFullViewingKey,
) -> Result<Option<Self::AccountId>, Self::Error>;
) -> Result<Option<AccountSources<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.
Expand Down Expand Up @@ -1372,9 +1433,10 @@ pub mod testing {
};

use super::{
chain::CommitmentTreeRoot, scanning::ScanRange, AccountBirthday, BlockMetadata,
DecryptedTransaction, InputSource, NullifierQuery, ScannedBlock, SentTransaction,
WalletCommitmentTrees, WalletRead, WalletSummary, WalletWrite, SAPLING_SHARD_HEIGHT,
chain::CommitmentTreeRoot, scanning::ScanRange, AccountBirthday, AccountSources,
BlockMetadata, DecryptedTransaction, InputSource, NullifierQuery, ScannedBlock,
SentTransaction, WalletCommitmentTrees, WalletRead, WalletSummary, WalletWrite,
SAPLING_SHARD_HEIGHT,
};

#[cfg(feature = "transparent-inputs")]
Expand Down Expand Up @@ -1425,9 +1487,8 @@ pub mod testing {

fn select_spendable_notes(
&self,
_account: Self::AccountId,
_inputs_from: AccountSources<Self::AccountId>,
_target_value: NonNegativeAmount,
_sources: &[ShieldedProtocol],
_anchor_height: BlockHeight,
_exclude: &[Self::NoteRef],
) -> Result<Vec<ReceivedNote<Self::NoteRef, Note>>, Self::Error> {
Expand Down Expand Up @@ -1523,7 +1584,7 @@ pub mod testing {
fn get_account_for_ufvk(
&self,
_ufvk: &UnifiedFullViewingKey,
) -> Result<Option<Self::AccountId>, Self::Error> {
) -> Result<Option<AccountSources<Self::AccountId>>, Self::Error> {
Ok(None)
}

Expand Down
20 changes: 11 additions & 9 deletions zcash_client_backend/src/data_api/wallet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ use sapling::{
};
use std::num::NonZeroU32;

use super::InputSource;
use super::{AccountSources, InputSource};
use crate::{
address::Address,
data_api::{
Expand Down Expand Up @@ -405,7 +405,7 @@ where
pub fn propose_transfer<DbT, ParamsT, InputsT, CommitmentTreeErrT>(
wallet_db: &mut DbT,
params: &ParamsT,
spend_from_account: <DbT as InputSource>::AccountId,
sources: AccountSources<<DbT as InputSource>::AccountId>,
input_selector: &InputsT,
request: zip321::TransactionRequest,
min_confirmations: NonZeroU32,
Expand Down Expand Up @@ -435,7 +435,7 @@ where
wallet_db,
target_height,
anchor_height,
spend_from_account,
sources,
request,
)
.map_err(Error::from)
Expand All @@ -453,9 +453,10 @@ where
/// * `wallet_db`: A read/write reference to the wallet database.
/// * `params`: Consensus parameters.
/// * `fee_rule`: The fee rule to use in creating the transaction.
/// * `spend_from_account`: The unified account that controls the funds that will be spent
/// in the resulting transaction. This procedure will return an error if the
/// account ID does not correspond to an account known to the wallet.
/// * `sources`: Metadata that describes the unified account and the pools from which
/// funds may be spent in the resulting transaction. This procedure will return an
/// error if the contained account ID does not correspond to an account known to
/// the wallet.
/// * `min_confirmations`: The minimum number of confirmations that a previously
/// received note must have in the blockchain in order to be considered for being
/// spent. A value of 10 confirmations is recommended and 0-conf transactions are
Expand All @@ -472,7 +473,7 @@ pub fn propose_standard_transfer_to_address<DbT, ParamsT, CommitmentTreeErrT>(
wallet_db: &mut DbT,
params: &ParamsT,
fee_rule: StandardFeeRule,
spend_from_account: <DbT as InputSource>::AccountId,
sources: AccountSources<<DbT as InputSource>::AccountId>,
min_confirmations: NonZeroU32,
to: &Address,
amount: NonNegativeAmount,
Expand Down Expand Up @@ -520,7 +521,7 @@ where
propose_transfer(
wallet_db,
params,
spend_from_account,
sources,
&input_selector,
request,
min_confirmations,
Expand Down Expand Up @@ -694,7 +695,8 @@ where
let account = wallet_db
.get_account_for_ufvk(&usk.to_unified_full_viewing_key())
.map_err(Error::DataSource)?
.ok_or(Error::KeyNotRecognized)?;
.ok_or(Error::KeyNotRecognized)?
.account_id();

let (sapling_anchor, sapling_inputs) =
if proposal_step.involves(PoolType::Shielded(ShieldedProtocol::Sapling)) {
Expand Down
19 changes: 4 additions & 15 deletions zcash_client_backend/src/data_api/wallet/input_selection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ use zcash_primitives::{

use crate::{
address::{Address, UnifiedAddress},
data_api::InputSource,
data_api::{AccountSources, InputSource},
fees::{sapling, ChangeError, ChangeStrategy, DustOutputPolicy},
proposal::{Proposal, ProposalError, ShieldedInputs},
wallet::{Note, ReceivedNote, WalletTransparentOutput},
Expand Down Expand Up @@ -148,7 +148,7 @@ pub trait InputSelector {
wallet_db: &Self::InputSource,
target_height: BlockHeight,
anchor_height: BlockHeight,
account: <Self::InputSource as InputSource>::AccountId,
sources: AccountSources<<Self::InputSource as InputSource>::AccountId>,
transaction_request: TransactionRequest,
) -> Result<
Proposal<Self::FeeRule, <Self::InputSource as InputSource>::NoteRef>,
Expand Down Expand Up @@ -328,7 +328,7 @@ where
wallet_db: &Self::InputSource,
target_height: BlockHeight,
anchor_height: BlockHeight,
account: <DbT as InputSource>::AccountId,
sources: AccountSources<<DbT as InputSource>::AccountId>,
transaction_request: TransactionRequest,
) -> Result<
Proposal<Self::FeeRule, DbT::NoteRef>,
Expand Down Expand Up @@ -454,19 +454,8 @@ where
Err(other) => return Err(other.into()),
}

#[cfg(not(feature = "orchard"))]
let selectable_pools = &[ShieldedProtocol::Sapling];
#[cfg(feature = "orchard")]
let selectable_pools = &[ShieldedProtocol::Sapling, ShieldedProtocol::Orchard];

shielded_inputs = wallet_db
.select_spendable_notes(
account,
amount_required,
selectable_pools,
anchor_height,
&exclude,
)
.select_spendable_notes(sources, amount_required, anchor_height, &exclude)
.map_err(InputSelectorError::DataSource)?;

let new_available = shielded_inputs
Expand Down
Loading

0 comments on commit 94b4719

Please sign in to comment.