diff --git a/zcash_client_backend/CHANGELOG.md b/zcash_client_backend/CHANGELOG.md index a76588f05..bb6badaa3 100644 --- a/zcash_client_backend/CHANGELOG.md +++ b/zcash_client_backend/CHANGELOG.md @@ -19,9 +19,11 @@ and this library adheres to Rust's notion of - `AccountKind` - `BlockMetadata::orchard_tree_size` - `DecryptedTransaction::{new, tx(), orchard_outputs()}` + - `NoteRetention` - `ScannedBlock::orchard` - `ScannedBlockCommitments::orchard` - `SentTransaction::new` + - `SpendableNotes` - `ORCHARD_SHARD_HEIGHT` - `BlockMetadata::orchard_tree_size` - `WalletSummary::next_orchard_subtree_index` @@ -46,6 +48,7 @@ and this library adheres to Rust's notion of - `WalletOrchardSpend` - `WalletOrchardOutput` - `WalletTx::{orchard_spends, orchard_outputs}` + - `ReceivedNote::map_note` ### Changed - `zcash_client_backend::data_api`: diff --git a/zcash_client_backend/src/data_api.rs b/zcash_client_backend/src/data_api.rs index 1c0ff1745..16b40805e 100644 --- a/zcash_client_backend/src/data_api.rs +++ b/zcash_client_backend/src/data_api.rs @@ -484,6 +484,70 @@ impl WalletSummary { } } +/// A predicate that can be used to choose whether or not a particular note is retained in note +/// selection. +pub trait NoteRetention { + /// Returns whether the specified Sapling note should be retained. + fn should_retain_sapling(&self, note: &ReceivedNote) -> bool; + /// Returns whether the specified Orchard note should be retained. + #[cfg(feature = "orchard")] + fn should_retain_orchard(&self, note: &ReceivedNote) -> bool; +} + +/// Spendable shielded outputs controlled by the wallet. +pub struct SpendableNotes { + sapling: Vec>, + #[cfg(feature = "orchard")] + orchard: Vec>, +} + +impl SpendableNotes { + /// Construct a new [`SpendableNotes`] from its constituent parts. + pub fn new( + sapling: Vec>, + #[cfg(feature = "orchard")] orchard: Vec>, + ) -> Self { + Self { + sapling, + #[cfg(feature = "orchard")] + orchard, + } + } + + /// Returns the set of spendable Sapling notes. + pub fn sapling(&self) -> &[ReceivedNote] { + self.sapling.as_ref() + } + + /// Returns the set of spendable Orchard notes. + #[cfg(feature = "orchard")] + pub fn orchard(&self) -> &[ReceivedNote] { + self.orchard.as_ref() + } + + /// Consumes this [`SpendableNotes`] value and produces a vector of + /// [`ReceivedNote`] values. + pub fn into_vec( + self, + retention: &impl NoteRetention, + ) -> Vec> { + let iter = self.sapling.into_iter().filter_map(|n| { + retention + .should_retain_sapling(&n) + .then(|| n.map_note(Note::Sapling)) + }); + + #[cfg(feature = "orchard")] + let iter = iter.chain(self.orchard.into_iter().filter_map(|n| { + retention + .should_retain_orchard(&n) + .then(|| n.map_note(Note::Orchard)) + })); + + iter.collect() + } +} + /// A trait representing the capability to query a data store for unspent transaction outputs /// belonging to a wallet. pub trait InputSource { diff --git a/zcash_client_backend/src/wallet.rs b/zcash_client_backend/src/wallet.rs index f8d8f4ffb..e3b187247 100644 --- a/zcash_client_backend/src/wallet.rs +++ b/zcash_client_backend/src/wallet.rs @@ -430,6 +430,21 @@ impl ReceivedNote { pub fn note_commitment_tree_position(&self) -> Position { self.note_commitment_tree_position } + + /// Map over the `note` field of this data structure. + /// + /// Consume this value, applying the provided function to the value of its `note` field and + /// returning a new `ReceivedNote` with the result as its `note` field value. + pub fn map_note N>(self, f: F) -> ReceivedNote { + ReceivedNote { + note_id: self.note_id, + txid: self.txid, + output_index: self.output_index, + note: f(self.note), + spending_key_scope: self.spending_key_scope, + note_commitment_tree_position: self.note_commitment_tree_position, + } + } } impl sapling_fees::InputView for (NoteRef, sapling::value::NoteValue) {