zcash_client_backend: Add `SpendableNotes` type and `NoteRetention` trait.

This commit is contained in:
Kris Nuttycombe 2024-03-13 18:07:09 -06:00
parent 2e0a3005de
commit 22f341888f
3 changed files with 82 additions and 0 deletions

View File

@ -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`:

View File

@ -484,6 +484,70 @@ impl<AccountId: Eq + Hash> WalletSummary<AccountId> {
}
}
/// A predicate that can be used to choose whether or not a particular note is retained in note
/// selection.
pub trait NoteRetention<NoteRef> {
/// Returns whether the specified Sapling note should be retained.
fn should_retain_sapling(&self, note: &ReceivedNote<NoteRef, sapling::Note>) -> bool;
/// Returns whether the specified Orchard note should be retained.
#[cfg(feature = "orchard")]
fn should_retain_orchard(&self, note: &ReceivedNote<NoteRef, orchard::note::Note>) -> bool;
}
/// Spendable shielded outputs controlled by the wallet.
pub struct SpendableNotes<NoteRef> {
sapling: Vec<ReceivedNote<NoteRef, sapling::Note>>,
#[cfg(feature = "orchard")]
orchard: Vec<ReceivedNote<NoteRef, orchard::note::Note>>,
}
impl<NoteRef> SpendableNotes<NoteRef> {
/// Construct a new [`SpendableNotes`] from its constituent parts.
pub fn new(
sapling: Vec<ReceivedNote<NoteRef, sapling::Note>>,
#[cfg(feature = "orchard")] orchard: Vec<ReceivedNote<NoteRef, orchard::note::Note>>,
) -> Self {
Self {
sapling,
#[cfg(feature = "orchard")]
orchard,
}
}
/// Returns the set of spendable Sapling notes.
pub fn sapling(&self) -> &[ReceivedNote<NoteRef, sapling::Note>] {
self.sapling.as_ref()
}
/// Returns the set of spendable Orchard notes.
#[cfg(feature = "orchard")]
pub fn orchard(&self) -> &[ReceivedNote<NoteRef, orchard::note::Note>] {
self.orchard.as_ref()
}
/// Consumes this [`SpendableNotes`] value and produces a vector of
/// [`ReceivedNote<NoteRef, Note>`] values.
pub fn into_vec(
self,
retention: &impl NoteRetention<NoteRef>,
) -> Vec<ReceivedNote<NoteRef, Note>> {
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 {

View File

@ -430,6 +430,21 @@ impl<NoteRef, NoteT> ReceivedNote<NoteRef, NoteT> {
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, F: Fn(NoteT) -> N>(self, f: F) -> ReceivedNote<NoteRef, N> {
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<NoteRef> sapling_fees::InputView<NoteRef> for (NoteRef, sapling::value::NoteValue) {