Skip to content

Commit c883bf4

Browse files
committed
zcash_client_backend: Permit spending pool selection.
1 parent 19868af commit c883bf4

File tree

13 files changed

+373
-161
lines changed

13 files changed

+373
-161
lines changed

zcash_client_backend/CHANGELOG.md

+12-3
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ and this library adheres to Rust's notion of
1515
- `zcash_client_backend::data_api`:
1616
- `AccountBalance::with_orchard_balance_mut`
1717
- `AccountBirthday::orchard_frontier`
18+
- `AccountSources`
1819
- `BlockMetadata::orchard_tree_size`
1920
- `DecryptedTransaction::{new, tx(), orchard_outputs()}`
2021
- `ScannedBlock::orchard`
@@ -49,10 +50,14 @@ and this library adheres to Rust's notion of
4950
- Arguments to `ScannedBlock::from_parts` have changed.
5051
- Changes to the `WalletRead` trait:
5152
- Added `get_orchard_nullifiers`
53+
- `get_account_for_ufvk` now returns an `AccountSources` instead of a bare
54+
`AccountId`
5255
- Changes to the `InputSource` trait:
53-
- `select_spendable_notes` now takes its `target_value` argument as a
54-
`NonNegativeAmount`. Also, the values of the returned map are also
55-
`NonNegativeAmount`s instead of `Amount`s.
56+
- `select_spendable_notes` has changed:
57+
- It now takes its `target_value` argument as a `NonNegativeAmount`.
58+
- Instead of an `AccountId`, it takes an `AccountSources` argument. The
59+
separate `sources` argument has been removed.
60+
- The values of the returned map are `NonNegativeAmount`s instead of `Amount`s.
5661
- Fields of `DecryptedTransaction` are now private. Use `DecryptedTransaction::new`
5762
and the newly provided accessors instead.
5863
- Fields of `SentTransaction` are now private. Use `SentTransaction::new`
@@ -64,6 +69,10 @@ and this library adheres to Rust's notion of
6469
- `fn put_orchard_subtree_roots`
6570
- Added method `WalletRead::validate_seed`
6671
- Removed `Error::AccountNotFound` variant.
72+
- `wallet::input_selection::InputSelector::propose_transaction` now takes an
73+
`AccountSources` rather than a bare `AccountId`.
74+
- `wallet::{propose_transfer, propose_standard_transfer_to_address}` now
75+
each take an `AccountSources` instead of a bare `AccountId`.
6776
- `zcash_client_backend::decrypt`:
6877
- Fields of `DecryptedOutput` are now private. Use `DecryptedOutput::new`
6978
and the newly provided accessors instead.

zcash_client_backend/src/data_api.rs

+70-9
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,68 @@ impl AccountBalance {
308308
}
309309
}
310310

311+
/// A type that describes what FVK components are known to the wallet for an account.
312+
#[derive(Clone, Copy, Debug)]
313+
pub struct AccountSources<AccountId> {
314+
account_id: AccountId,
315+
use_orchard: bool,
316+
use_sapling: bool,
317+
use_transparent: bool,
318+
}
319+
320+
impl<AccountId: Copy> AccountSources<AccountId> {
321+
/// Constructs AccountSources from its constituent parts
322+
pub fn new(
323+
account_id: AccountId,
324+
use_orchard: bool,
325+
use_sapling: bool,
326+
use_transparent: bool,
327+
) -> Self {
328+
Self {
329+
account_id,
330+
use_orchard,
331+
use_sapling,
332+
use_transparent,
333+
}
334+
}
335+
336+
/// Returns the id for the account to which this metadata applies.
337+
pub fn account_id(&self) -> AccountId {
338+
self.account_id
339+
}
340+
341+
/// Returns whether the account has an Orchard balance and spendability determination
342+
/// capability.
343+
pub fn use_orchard(&self) -> bool {
344+
self.use_orchard
345+
}
346+
347+
/// Returns whether the account has an Sapling balance and spendability determination
348+
/// capability.
349+
pub fn use_sapling(&self) -> bool {
350+
self.use_sapling
351+
}
352+
353+
/// Returns whether the account has a Transparent balance determination capability.
354+
pub fn use_transparent(&self) -> bool {
355+
self.use_transparent
356+
}
357+
358+
/// Restricts the sources to be used to those for which the given [`UnifiedSpendingKey`]
359+
/// provides a spending capability.
360+
pub fn filter_with_usk(&mut self, usk: &UnifiedSpendingKey) {
361+
self.use_orchard &= usk.has_orchard();
362+
self.use_sapling &= usk.has_sapling();
363+
self.use_transparent &= usk.has_transparent();
364+
}
365+
366+
/// Returns the [`UnifiedAddressRequest`] that will produce a [`UnifiedAddress`] having
367+
/// receivers corresponding to the spending capabilities described by this value.
368+
pub fn to_unified_address_request(&self) -> Option<UnifiedAddressRequest> {
369+
UnifiedAddressRequest::new(self.use_orchard, self.use_sapling, self.use_transparent)
370+
}
371+
}
372+
311373
/// A polymorphic ratio type, usually used for rational numbers.
312374
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
313375
pub struct Ratio<T> {
@@ -444,9 +506,8 @@ pub trait InputSource {
444506
/// be included.
445507
fn select_spendable_notes(
446508
&self,
447-
account: Self::AccountId,
509+
inputs_from: AccountSources<Self::AccountId>,
448510
target_value: NonNegativeAmount,
449-
sources: &[ShieldedProtocol],
450511
anchor_height: BlockHeight,
451512
exclude: &[Self::NoteRef],
452513
) -> Result<Vec<ReceivedNote<Self::NoteRef, Note>>, Self::Error>;
@@ -606,7 +667,7 @@ pub trait WalletRead {
606667
fn get_account_for_ufvk(
607668
&self,
608669
ufvk: &UnifiedFullViewingKey,
609-
) -> Result<Option<Self::AccountId>, Self::Error>;
670+
) -> Result<Option<AccountSources<Self::AccountId>>, Self::Error>;
610671

611672
/// Returns the wallet balances and sync status for an account given the specified minimum
612673
/// number of confirmations, or `Ok(None)` if the wallet has no balance data available.
@@ -1372,9 +1433,10 @@ pub mod testing {
13721433
};
13731434

13741435
use super::{
1375-
chain::CommitmentTreeRoot, scanning::ScanRange, AccountBirthday, BlockMetadata,
1376-
DecryptedTransaction, InputSource, NullifierQuery, ScannedBlock, SentTransaction,
1377-
WalletCommitmentTrees, WalletRead, WalletSummary, WalletWrite, SAPLING_SHARD_HEIGHT,
1436+
chain::CommitmentTreeRoot, scanning::ScanRange, AccountBirthday, AccountSources,
1437+
BlockMetadata, DecryptedTransaction, InputSource, NullifierQuery, ScannedBlock,
1438+
SentTransaction, WalletCommitmentTrees, WalletRead, WalletSummary, WalletWrite,
1439+
SAPLING_SHARD_HEIGHT,
13781440
};
13791441

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

14261488
fn select_spendable_notes(
14271489
&self,
1428-
_account: Self::AccountId,
1490+
_inputs_from: AccountSources<Self::AccountId>,
14291491
_target_value: NonNegativeAmount,
1430-
_sources: &[ShieldedProtocol],
14311492
_anchor_height: BlockHeight,
14321493
_exclude: &[Self::NoteRef],
14331494
) -> Result<Vec<ReceivedNote<Self::NoteRef, Note>>, Self::Error> {
@@ -1523,7 +1584,7 @@ pub mod testing {
15231584
fn get_account_for_ufvk(
15241585
&self,
15251586
_ufvk: &UnifiedFullViewingKey,
1526-
) -> Result<Option<Self::AccountId>, Self::Error> {
1587+
) -> Result<Option<AccountSources<Self::AccountId>>, Self::Error> {
15271588
Ok(None)
15281589
}
15291590

zcash_client_backend/src/data_api/wallet.rs

+11-9
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ use sapling::{
4141
};
4242
use std::num::NonZeroU32;
4343

44-
use super::InputSource;
44+
use super::{AccountSources, InputSource};
4545
use crate::{
4646
address::Address,
4747
data_api::{
@@ -405,7 +405,7 @@ where
405405
pub fn propose_transfer<DbT, ParamsT, InputsT, CommitmentTreeErrT>(
406406
wallet_db: &mut DbT,
407407
params: &ParamsT,
408-
spend_from_account: <DbT as InputSource>::AccountId,
408+
sources: AccountSources<<DbT as InputSource>::AccountId>,
409409
input_selector: &InputsT,
410410
request: zip321::TransactionRequest,
411411
min_confirmations: NonZeroU32,
@@ -435,7 +435,7 @@ where
435435
wallet_db,
436436
target_height,
437437
anchor_height,
438-
spend_from_account,
438+
sources,
439439
request,
440440
)
441441
.map_err(Error::from)
@@ -453,9 +453,10 @@ where
453453
/// * `wallet_db`: A read/write reference to the wallet database.
454454
/// * `params`: Consensus parameters.
455455
/// * `fee_rule`: The fee rule to use in creating the transaction.
456-
/// * `spend_from_account`: The unified account that controls the funds that will be spent
457-
/// in the resulting transaction. This procedure will return an error if the
458-
/// account ID does not correspond to an account known to the wallet.
456+
/// * `sources`: Metadata that describes the unified account and the pools from which
457+
/// funds may be spent in the resulting transaction. This procedure will return an
458+
/// error if the contained account ID does not correspond to an account known to
459+
/// the wallet.
459460
/// * `min_confirmations`: The minimum number of confirmations that a previously
460461
/// received note must have in the blockchain in order to be considered for being
461462
/// spent. A value of 10 confirmations is recommended and 0-conf transactions are
@@ -472,7 +473,7 @@ pub fn propose_standard_transfer_to_address<DbT, ParamsT, CommitmentTreeErrT>(
472473
wallet_db: &mut DbT,
473474
params: &ParamsT,
474475
fee_rule: StandardFeeRule,
475-
spend_from_account: <DbT as InputSource>::AccountId,
476+
sources: AccountSources<<DbT as InputSource>::AccountId>,
476477
min_confirmations: NonZeroU32,
477478
to: &Address,
478479
amount: NonNegativeAmount,
@@ -520,7 +521,7 @@ where
520521
propose_transfer(
521522
wallet_db,
522523
params,
523-
spend_from_account,
524+
sources,
524525
&input_selector,
525526
request,
526527
min_confirmations,
@@ -694,7 +695,8 @@ where
694695
let account = wallet_db
695696
.get_account_for_ufvk(&usk.to_unified_full_viewing_key())
696697
.map_err(Error::DataSource)?
697-
.ok_or(Error::KeyNotRecognized)?;
698+
.ok_or(Error::KeyNotRecognized)?
699+
.account_id();
698700

699701
let (sapling_anchor, sapling_inputs) =
700702
if proposal_step.involves(PoolType::Shielded(ShieldedProtocol::Sapling)) {

zcash_client_backend/src/data_api/wallet/input_selection.rs

+4-15
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ use zcash_primitives::{
2121

2222
use crate::{
2323
address::{Address, UnifiedAddress},
24-
data_api::InputSource,
24+
data_api::{AccountSources, InputSource},
2525
fees::{sapling, ChangeError, ChangeStrategy, DustOutputPolicy},
2626
proposal::{Proposal, ProposalError, ShieldedInputs},
2727
wallet::{Note, ReceivedNote, WalletTransparentOutput},
@@ -148,7 +148,7 @@ pub trait InputSelector {
148148
wallet_db: &Self::InputSource,
149149
target_height: BlockHeight,
150150
anchor_height: BlockHeight,
151-
account: <Self::InputSource as InputSource>::AccountId,
151+
sources: AccountSources<<Self::InputSource as InputSource>::AccountId>,
152152
transaction_request: TransactionRequest,
153153
) -> Result<
154154
Proposal<Self::FeeRule, <Self::InputSource as InputSource>::NoteRef>,
@@ -328,7 +328,7 @@ where
328328
wallet_db: &Self::InputSource,
329329
target_height: BlockHeight,
330330
anchor_height: BlockHeight,
331-
account: <DbT as InputSource>::AccountId,
331+
sources: AccountSources<<DbT as InputSource>::AccountId>,
332332
transaction_request: TransactionRequest,
333333
) -> Result<
334334
Proposal<Self::FeeRule, DbT::NoteRef>,
@@ -454,19 +454,8 @@ where
454454
Err(other) => return Err(other.into()),
455455
}
456456

457-
#[cfg(not(feature = "orchard"))]
458-
let selectable_pools = &[ShieldedProtocol::Sapling];
459-
#[cfg(feature = "orchard")]
460-
let selectable_pools = &[ShieldedProtocol::Sapling, ShieldedProtocol::Orchard];
461-
462457
shielded_inputs = wallet_db
463-
.select_spendable_notes(
464-
account,
465-
amount_required,
466-
selectable_pools,
467-
anchor_height,
468-
&exclude,
469-
)
458+
.select_spendable_notes(sources, amount_required, anchor_height, &exclude)
470459
.map_err(InputSelectorError::DataSource)?;
471460

472461
let new_available = shielded_inputs

0 commit comments

Comments
 (0)