pepper_sync/
wallet.rs

1//! Module for wallet structs and types generated by the sync engine from block chain data or to track the wallet's
2//! sync status.
3//! The structs will be (or be transposed into) the fundamental wallet components for the wallet interfacing with this
4//! sync engine.
5
6use std::{
7    collections::{BTreeMap, BTreeSet},
8    convert::Infallible,
9    fmt::Debug,
10    ops::Range,
11    sync::{
12        Arc,
13        atomic::{self, AtomicU8},
14    },
15};
16
17use incrementalmerkletree::Position;
18use orchard::tree::MerkleHashOrchard;
19use shardtree::{ShardTree, store::memory::MemoryShardStore};
20use tokio::sync::mpsc;
21use zcash_address::unified::ParseError;
22use zcash_client_backend::{
23    data_api::scanning::{ScanPriority, ScanRange},
24    proto::compact_formats::CompactBlock,
25};
26use zcash_keys::{address::UnifiedAddress, encoding::encode_payment_address};
27use zcash_primitives::{
28    block::BlockHash,
29    legacy::Script,
30    memo::Memo,
31    transaction::{TxId, components::transparent::OutPoint},
32};
33use zcash_protocol::{
34    PoolType, ShieldedProtocol,
35    consensus::{self, BlockHeight},
36    value::Zatoshis,
37};
38
39use zingo_status::confirmation_status::ConfirmationStatus;
40
41use crate::{
42    client::FetchRequest,
43    error::{ServerError, SyncModeError},
44    keys::{self, KeyId, transparent::TransparentAddressId},
45    scan::compact_blocks::calculate_block_tree_bounds,
46    sync::MAX_VERIFICATION_WINDOW,
47    witness,
48};
49
50pub mod traits;
51
52#[cfg(feature = "wallet_essentials")]
53pub mod serialization;
54
55/// Block height and txid of relevant transactions that have yet to be scanned. These may be added due to transparent
56/// output/spend discovery or for targetted rescan.
57pub type Locator = (BlockHeight, TxId);
58
59/// Initial sync state.
60///
61/// All fields will be reset when a new sync session starts.
62#[derive(Debug, Clone)]
63pub struct InitialSyncState {
64    /// One block above the fully scanned wallet height at start of sync session.
65    ///
66    /// If chain height is not larger than fully scanned height when sync is called, this value will be set to chain
67    /// height instead.
68    pub(crate) sync_start_height: BlockHeight,
69    /// The tree sizes of the fully scanned height and chain tip at start of sync session.
70    pub(crate) wallet_tree_bounds: TreeBounds,
71    /// Total number of blocks scanned in previous sync sessions.
72    pub(crate) previously_scanned_blocks: u32,
73    /// Total number of sapling outputs to scanned in previous sync sessions.
74    pub(crate) previously_scanned_sapling_outputs: u32,
75    /// Total number of orchard outputs to scanned in previous sync sessions.
76    pub(crate) previously_scanned_orchard_outputs: u32,
77}
78
79impl InitialSyncState {
80    /// Create new InitialSyncState
81    pub fn new() -> Self {
82        InitialSyncState {
83            sync_start_height: 0.into(),
84            wallet_tree_bounds: TreeBounds {
85                sapling_initial_tree_size: 0,
86                sapling_final_tree_size: 0,
87                orchard_initial_tree_size: 0,
88                orchard_final_tree_size: 0,
89            },
90            previously_scanned_blocks: 0,
91            previously_scanned_sapling_outputs: 0,
92            previously_scanned_orchard_outputs: 0,
93        }
94    }
95}
96
97impl Default for InitialSyncState {
98    fn default() -> Self {
99        Self::new()
100    }
101}
102
103/// Encapsulates the current state of sync
104#[derive(Debug, Clone)]
105pub struct SyncState {
106    /// A vec of block ranges with scan priorities from wallet birthday to chain tip.
107    /// In block height order with no overlaps or gaps.
108    pub(crate) scan_ranges: Vec<ScanRange>,
109    /// The block ranges that contain all sapling outputs of complete sapling shards.
110    ///
111    /// There is an edge case where a range may include two (or more) shards. However, this only occurs when the lower
112    /// shards are already scanned so will cause no issues when punching in the higher scan priorites.
113    pub(crate) sapling_shard_ranges: Vec<Range<BlockHeight>>,
114    /// The block ranges that contain all orchard outputs of complete orchard shards.
115    ///
116    /// There is an edge case where a range may include two (or more) shards. However, this only occurs when the lower
117    /// shards are already scanned so will cause no issues when punching in the higher scan priorites.
118    pub(crate) orchard_shard_ranges: Vec<Range<BlockHeight>>,
119    /// Locators for relevant transactions to the wallet.
120    pub(crate) locators: BTreeSet<Locator>,
121    /// Initial sync state.
122    pub(crate) initial_sync_state: InitialSyncState,
123}
124
125impl SyncState {
126    /// Create new SyncState
127    pub fn new() -> Self {
128        SyncState {
129            scan_ranges: Vec::new(),
130            sapling_shard_ranges: Vec::new(),
131            orchard_shard_ranges: Vec::new(),
132            locators: BTreeSet::new(),
133            initial_sync_state: InitialSyncState::new(),
134        }
135    }
136
137    /// Scan ranges
138    pub fn scan_ranges(&self) -> &[ScanRange] {
139        &self.scan_ranges
140    }
141
142    /// Returns true if all scan ranges are scanned.
143    pub(crate) fn scan_complete(&self) -> bool {
144        self.scan_ranges
145            .iter()
146            .all(|scan_range| scan_range.priority() == ScanPriority::Scanned)
147    }
148
149    /// Returns the block height at which all blocks equal to and below this height are scanned.
150    /// Returns `None` if `self.scan_ranges` is empty.
151    pub fn fully_scanned_height(&self) -> Option<BlockHeight> {
152        if let Some(scan_range) = self
153            .scan_ranges
154            .iter()
155            .find(|scan_range| scan_range.priority() != ScanPriority::Scanned)
156        {
157            Some(scan_range.block_range().start - 1)
158        } else {
159            self.scan_ranges
160                .last()
161                .map(|range| range.block_range().end - 1)
162        }
163    }
164
165    /// Returns the highest block height that has been scanned.
166    /// If no scan ranges have been scanned, returns the block below the wallet birthday.
167    /// Returns `None` if `self.scan_ranges` is empty.
168    pub fn highest_scanned_height(&self) -> Option<BlockHeight> {
169        if let Some(last_scanned_range) = self
170            .scan_ranges
171            .iter()
172            .filter(|scan_range| scan_range.priority() == ScanPriority::Scanned)
173            .last()
174        {
175            Some(last_scanned_range.block_range().end - 1)
176        } else {
177            self.wallet_birthday().map(|birthday| birthday - 1)
178        }
179    }
180
181    /// Returns the wallet birthday or `None` if `self.scan_ranges` is empty.
182    ///
183    /// If the wallet birthday is below the sapling activation height, returns the sapling activation height instead.
184    pub fn wallet_birthday(&self) -> Option<BlockHeight> {
185        self.scan_ranges
186            .first()
187            .map(|range| range.block_range().start)
188    }
189
190    /// Returns the last known chain height to the wallet or `None` if `self.scan_ranges` is empty.
191    pub fn wallet_height(&self) -> Option<BlockHeight> {
192        self.scan_ranges
193            .last()
194            .map(|range| range.block_range().end - 1)
195    }
196}
197
198impl Default for SyncState {
199    fn default() -> Self {
200        Self::new()
201    }
202}
203
204/// Sync modes.
205#[derive(Debug, Clone, Copy, PartialEq, Eq)]
206pub enum SyncMode {
207    /// Sync is not running.
208    NotRunning,
209    /// Sync is held in a paused state and the wallet guard is dropped.
210    Paused,
211    /// Sync is running.
212    Running,
213    /// Sync is shutting down.
214    Shutdown,
215}
216
217impl SyncMode {
218    /// Constructor from u8.
219    ///
220    /// Returns `None` if `mode` is not a valid enum variant.
221    pub fn from_u8(mode: u8) -> Result<Self, SyncModeError> {
222        match mode {
223            0 => Ok(Self::NotRunning),
224            1 => Ok(Self::Paused),
225            2 => Ok(Self::Running),
226            3 => Ok(Self::Shutdown),
227            _ => Err(SyncModeError::InvalidSyncMode(mode)),
228        }
229    }
230
231    /// Creates [`crate::wallet::SyncMode`] from an atomic u8.
232    ///
233    /// # Panic
234    ///
235    /// Panics if `atomic_sync_mode` corresponds to an invalid enum variant.
236    /// It is the consumers responsibility to ensure the library restricts the user API to only set valid values via
237    /// [`crate::wallet::SyncMode`].
238    pub fn from_atomic_u8(atomic_sync_mode: Arc<AtomicU8>) -> Result<SyncMode, SyncModeError> {
239        SyncMode::from_u8(atomic_sync_mode.load(atomic::Ordering::Acquire))
240    }
241}
242
243/// Initial and final tree sizes.
244#[derive(Debug, Clone, Copy)]
245#[allow(missing_docs)]
246pub struct TreeBounds {
247    pub sapling_initial_tree_size: u32,
248    pub sapling_final_tree_size: u32,
249    pub orchard_initial_tree_size: u32,
250    pub orchard_final_tree_size: u32,
251}
252
253/// Output ID for a given pool type.
254#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)]
255pub struct OutputId {
256    /// ID of associated transaction.
257    txid: TxId,
258    /// Index of output within the transactions bundle of the given pool type.
259    output_index: u16,
260}
261
262impl OutputId {
263    /// Creates new OutputId from parts.
264    pub fn new(txid: TxId, output_index: u16) -> Self {
265        OutputId { txid, output_index }
266    }
267
268    /// Transaction ID of output's associated transaction.
269    pub fn txid(&self) -> TxId {
270        self.txid
271    }
272
273    /// Index of output within the transactions bundle of the given pool type.
274    pub fn output_index(&self) -> u16 {
275        self.output_index
276    }
277}
278
279impl std::fmt::Display for OutputId {
280    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
281        write!(
282            f,
283            "{{
284                txid: {}
285                output index: {}
286            }}",
287            self.txid, self.output_index
288        )
289    }
290}
291
292impl From<&OutPoint> for OutputId {
293    fn from(value: &OutPoint) -> Self {
294        OutputId::new(*value.txid(), value.n() as u16)
295    }
296}
297
298impl From<OutputId> for OutPoint {
299    fn from(value: OutputId) -> Self {
300        OutPoint::new(value.txid.into(), value.output_index as u32)
301    }
302}
303
304/// Binary tree map of nullifiers from transaction spends or actions
305#[derive(Debug)]
306pub struct NullifierMap {
307    /// Sapling nullifer map
308    pub sapling: BTreeMap<sapling_crypto::Nullifier, Locator>,
309    /// Orchard nullifer map
310    pub orchard: BTreeMap<orchard::note::Nullifier, Locator>,
311}
312
313impl NullifierMap {
314    /// Construct new nullifier map.
315    pub fn new() -> Self {
316        Self {
317            sapling: BTreeMap::new(),
318            orchard: BTreeMap::new(),
319        }
320    }
321
322    /// Clear nullifier map.
323    pub fn clear(&mut self) {
324        self.sapling.clear();
325        self.orchard.clear();
326    }
327}
328
329impl Default for NullifierMap {
330    fn default() -> Self {
331        Self::new()
332    }
333}
334
335/// Wallet block data
336#[derive(Debug, Clone)]
337pub struct WalletBlock {
338    pub(crate) block_height: BlockHeight,
339    pub(crate) block_hash: BlockHash,
340    pub(crate) prev_hash: BlockHash,
341    pub(crate) time: u32,
342    pub(crate) txids: Vec<TxId>,
343    pub(crate) tree_bounds: TreeBounds,
344}
345
346impl WalletBlock {
347    pub(crate) async fn from_compact_block(
348        consensus_parameters: &impl consensus::Parameters,
349        fetch_request_sender: mpsc::UnboundedSender<FetchRequest>,
350        block: &CompactBlock,
351    ) -> Result<Self, ServerError> {
352        let tree_bounds =
353            calculate_block_tree_bounds(consensus_parameters, fetch_request_sender, block).await?;
354
355        Ok(Self {
356            block_height: block.height(),
357            block_hash: block.hash(),
358            prev_hash: block.prev_hash(),
359            time: block.time,
360            txids: block
361                .vtx
362                .iter()
363                .map(|transaction| transaction.txid())
364                .collect(),
365            tree_bounds,
366        })
367    }
368
369    /// Block height.
370    pub fn block_height(&self) -> BlockHeight {
371        self.block_height
372    }
373
374    /// Block hash.
375    pub fn block_hash(&self) -> BlockHash {
376        self.block_hash
377    }
378
379    /// Previous block hash.
380    pub fn prev_hash(&self) -> BlockHash {
381        self.prev_hash
382    }
383
384    /// Time block was mined.
385    pub fn time(&self) -> u32 {
386        self.time
387    }
388
389    /// Transaction IDs of transactions in the block.
390    pub fn txids(&self) -> &[TxId] {
391        &self.txids
392    }
393
394    /// Tree size bounds
395    pub fn tree_bounds(&self) -> TreeBounds {
396        self.tree_bounds
397    }
398}
399
400/// Wallet transaction
401pub struct WalletTransaction {
402    pub(crate) txid: TxId,
403    pub(crate) status: ConfirmationStatus,
404    pub(crate) transaction: zcash_primitives::transaction::Transaction,
405    pub(crate) datetime: u32,
406    pub(crate) transparent_coins: Vec<TransparentCoin>,
407    pub(crate) sapling_notes: Vec<SaplingNote>,
408    pub(crate) orchard_notes: Vec<OrchardNote>,
409    pub(crate) outgoing_sapling_notes: Vec<OutgoingSaplingNote>,
410    pub(crate) outgoing_orchard_notes: Vec<OutgoingOrchardNote>,
411}
412
413impl WalletTransaction {
414    /// Transaction ID
415    pub fn txid(&self) -> TxId {
416        self.txid
417    }
418
419    /// Confirmation status
420    pub fn status(&self) -> ConfirmationStatus {
421        self.status
422    }
423
424    /// [`zcash_primitives::transaction::Transaction`]
425    pub fn transaction(&self) -> &zcash_primitives::transaction::Transaction {
426        &self.transaction
427    }
428
429    /// Datetime. In form of seconds since unix epoch.
430    pub fn datetime(&self) -> u32 {
431        self.datetime
432    }
433
434    /// Transparent coins
435    pub fn transparent_coins(&self) -> &[TransparentCoin] {
436        &self.transparent_coins
437    }
438
439    /// Transparent coins mutable
440    pub fn transparent_coins_mut(&mut self) -> Vec<&mut TransparentCoin> {
441        self.transparent_coins.iter_mut().collect()
442    }
443
444    /// Sapling notes
445    pub fn sapling_notes(&self) -> &[SaplingNote] {
446        &self.sapling_notes
447    }
448
449    /// Sapling notes mutable
450    pub fn sapling_notes_mut(&mut self) -> Vec<&mut SaplingNote> {
451        self.sapling_notes.iter_mut().collect()
452    }
453
454    /// Orchard notes
455    pub fn orchard_notes(&self) -> &[OrchardNote] {
456        &self.orchard_notes
457    }
458
459    /// Orchard notes mutable
460    pub fn orchard_notes_mut(&mut self) -> Vec<&mut OrchardNote> {
461        self.orchard_notes.iter_mut().collect()
462    }
463
464    /// Outgoing sapling notes
465    pub fn outgoing_sapling_notes(&self) -> &[OutgoingSaplingNote] {
466        &self.outgoing_sapling_notes
467    }
468
469    /// Outgoing orchard notes
470    pub fn outgoing_orchard_notes(&self) -> &[OutgoingOrchardNote] {
471        &self.outgoing_orchard_notes
472    }
473
474    /// Returns nullifers from sapling bundle.
475    /// Returns empty vec if bundle is `None`.
476    pub fn sapling_nullifiers(&self) -> Vec<&sapling_crypto::Nullifier> {
477        self.transaction
478            .sapling_bundle()
479            .map_or_else(Vec::new, |bundle| {
480                bundle
481                    .shielded_spends()
482                    .iter()
483                    .map(|spend| spend.nullifier())
484                    .collect::<Vec<_>>()
485            })
486    }
487
488    /// Returns nullifers from orchard bundle.
489    /// Returns empty vec if bundle is `None`.
490    pub fn orchard_nullifiers(&self) -> Vec<&orchard::note::Nullifier> {
491        self.transaction
492            .orchard_bundle()
493            .map_or_else(Vec::new, |bundle| {
494                bundle
495                    .actions()
496                    .iter()
497                    .map(|action| action.nullifier())
498                    .collect::<Vec<_>>()
499            })
500    }
501
502    /// Returns outpoints from transparent bundle.
503    /// Returns empty vec if bundle is `None`.
504    pub fn outpoints(&self) -> Vec<&OutPoint> {
505        self.transaction
506            .transparent_bundle()
507            .map_or_else(Vec::new, |bundle| {
508                bundle
509                    .vin
510                    .iter()
511                    .map(|txin| &txin.prevout)
512                    .collect::<Vec<_>>()
513            })
514    }
515}
516
517#[cfg(feature = "wallet_essentials")]
518impl WalletTransaction {
519    /// Returns the total value sent to receivers, excluding value sent to the wallet's own addresses.
520    pub fn total_value_sent(&self) -> u64 {
521        let transparent_value_sent = self
522            .transaction
523            .transparent_bundle()
524            .map_or(0, |bundle| {
525                bundle
526                    .vout
527                    .iter()
528                    .map(|output| output.value.into_u64())
529                    .sum()
530            })
531            .saturating_sub(self.total_output_value::<TransparentCoin>());
532
533        // TODO: it is not intended behaviour to create outgoing change notes. the logic must be changed to be resilient
534        // to this fix to zcash client backend
535        let sapling_value_sent = self
536            .total_outgoing_note_value::<OutgoingSaplingNote>()
537            .saturating_sub(self.total_output_value::<SaplingNote>());
538        let orchard_value_sent = self
539            .total_outgoing_note_value::<OutgoingOrchardNote>()
540            .saturating_sub(self.total_output_value::<OrchardNote>());
541
542        transparent_value_sent + sapling_value_sent + orchard_value_sent
543    }
544
545    /// Returns total sum of all output values.
546    pub fn total_value_received(&self) -> u64 {
547        self.total_output_value::<TransparentCoin>()
548            + self.total_output_value::<SaplingNote>()
549            + self.total_output_value::<OrchardNote>()
550    }
551
552    /// Returns total sum of output values for a given pool.
553    pub fn total_output_value<Op: OutputInterface>(&self) -> u64 {
554        Op::transaction_outputs(self)
555            .iter()
556            .map(|output| output.value())
557            .sum()
558    }
559
560    /// Returns total sum of outgoing note values for a given shielded pool.
561    pub fn total_outgoing_note_value<Op: OutgoingNoteInterface>(&self) -> u64 {
562        Op::transaction_outgoing_notes(self)
563            .iter()
564            .map(|note| note.value())
565            .sum()
566    }
567}
568
569impl std::fmt::Debug for WalletTransaction {
570    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
571        f.debug_struct("WalletTransaction")
572            .field("txid", &self.txid)
573            .field("confirmation_status", &self.status)
574            .field("datetime", &self.datetime)
575            .field("transparent_coins", &self.transparent_coins)
576            .field("sapling_notes", &self.sapling_notes)
577            .field("orchard_notes", &self.orchard_notes)
578            .field("outgoing_sapling_notes", &self.outgoing_sapling_notes)
579            .field("outgoing_orchard_notes", &self.outgoing_orchard_notes)
580            .finish()
581    }
582}
583
584/// Provides a common API for all output types.
585pub trait OutputInterface: Sized {
586    /// Identifier for key used to decrypt output.
587    type KeyId;
588    /// Transaction input type associated with spend detection of output.
589    type Input: Clone + Debug + PartialEq + Eq + PartialOrd + Ord;
590
591    /// Output's associated pool type.
592    const POOL_TYPE: PoolType;
593
594    /// Output ID.
595    fn output_id(&self) -> OutputId;
596
597    /// Identifier for key used to decrypt output.
598    fn key_id(&self) -> Self::KeyId;
599
600    /// Transaction ID of transaction this output was spent.
601    /// If `None`, output is not spent.
602    fn spending_transaction(&self) -> Option<TxId>;
603
604    /// Sets spending transaction.
605    fn set_spending_transaction(&mut self, spending_transaction: Option<TxId>);
606
607    /// Note value..
608    fn value(&self) -> u64;
609
610    /// Returns the type used to link with transaction inputs for spend detection.
611    /// Returns `None` in the case the nullifier is not available for shielded outputs.
612    ///
613    /// Nullifier for shielded outputs.
614    /// Outpoint for transparent outputs.
615    fn spend_link(&self) -> Option<Self::Input>;
616
617    /// Inputs within `transaction` used to detect an output's spend status.
618    ///
619    /// Nullifiers for shielded outputs.
620    /// Out points for transparent outputs.
621    fn transaction_inputs(transaction: &WalletTransaction) -> Vec<&Self::Input>;
622
623    /// Outputs within `transaction`.
624    fn transaction_outputs(transaction: &WalletTransaction) -> &[Self];
625}
626
627/// Provides a common API for all shielded output types.
628pub trait NoteInterface: OutputInterface {
629    /// Decrypted note type.
630    type ZcashNote;
631    /// Nullifier type.
632    type Nullifier: Copy;
633
634    /// Note's associated shielded protocol.
635    const SHIELDED_PROTOCOL: ShieldedProtocol;
636
637    /// Decrypted note with recipient and value
638    fn note(&self) -> &Self::ZcashNote;
639
640    /// Derived nullifier
641    fn nullifier(&self) -> Option<Self::Nullifier>;
642
643    /// Commitment tree leaf position
644    fn position(&self) -> Option<Position>;
645
646    /// Memo
647    fn memo(&self) -> &Memo;
648}
649
650///  Transparent coin (output) with metadata relevant to the wallet.
651#[derive(Debug, Clone)]
652pub struct TransparentCoin {
653    /// Output ID.
654    pub(crate) output_id: OutputId,
655    /// Identifier for key used to derive address.
656    pub(crate) key_id: TransparentAddressId,
657    /// Encoded transparent address.
658    pub(crate) address: String,
659    /// Script.
660    pub(crate) script: Script,
661    /// Coin value.
662    pub(crate) value: Zatoshis,
663    /// Transaction ID of transaction this output was spent.
664    /// If `None`, output is not spent.
665    pub(crate) spending_transaction: Option<TxId>,
666}
667
668impl TransparentCoin {
669    /// Address received to.
670    pub fn address(&self) -> &str {
671        &self.address
672    }
673
674    /// Script.
675    pub fn script(&self) -> &Script {
676        &self.script
677    }
678}
679
680impl OutputInterface for TransparentCoin {
681    type KeyId = TransparentAddressId;
682    type Input = OutPoint;
683
684    const POOL_TYPE: PoolType = PoolType::Transparent;
685
686    fn output_id(&self) -> OutputId {
687        self.output_id
688    }
689
690    fn key_id(&self) -> Self::KeyId {
691        self.key_id
692    }
693
694    fn spending_transaction(&self) -> Option<TxId> {
695        self.spending_transaction
696    }
697
698    fn set_spending_transaction(&mut self, spending_transaction: Option<TxId>) {
699        self.spending_transaction = spending_transaction;
700    }
701
702    fn value(&self) -> u64 {
703        self.value.into_u64()
704    }
705
706    fn spend_link(&self) -> Option<Self::Input> {
707        Some(self.output_id.into())
708    }
709
710    fn transaction_inputs(transaction: &WalletTransaction) -> Vec<&Self::Input> {
711        transaction.outpoints()
712    }
713
714    fn transaction_outputs(transaction: &WalletTransaction) -> &[Self] {
715        &transaction.transparent_coins
716    }
717}
718
719/// Wallet note, shielded output with metadata relevant to the wallet.
720#[derive(Debug, Clone)]
721pub struct WalletNote<N, Nf: Copy> {
722    /// Output ID.
723    pub(crate) output_id: OutputId,
724    /// Identifier for key used to decrypt output.
725    pub(crate) key_id: KeyId,
726    /// Decrypted note with recipient and value.
727    pub(crate) note: N,
728    /// Derived nullifier.
729    pub(crate) nullifier: Option<Nf>, //TODO: syncing without nullifier deriving key
730    /// Commitment tree leaf position.
731    pub(crate) position: Option<Position>,
732    /// Memo.
733    pub(crate) memo: Memo,
734    /// Transaction ID of transaction this output was spent.
735    /// If `None`, output is not spent.
736    pub(crate) spending_transaction: Option<TxId>,
737}
738
739/// Sapling note.
740pub type SaplingNote = WalletNote<sapling_crypto::Note, sapling_crypto::Nullifier>;
741
742impl OutputInterface for SaplingNote {
743    type KeyId = KeyId;
744    type Input = sapling_crypto::Nullifier;
745
746    const POOL_TYPE: PoolType = PoolType::Shielded(ShieldedProtocol::Sapling);
747
748    fn output_id(&self) -> OutputId {
749        self.output_id
750    }
751
752    fn key_id(&self) -> KeyId {
753        self.key_id
754    }
755
756    fn spending_transaction(&self) -> Option<TxId> {
757        self.spending_transaction
758    }
759
760    fn set_spending_transaction(&mut self, spending_transaction: Option<TxId>) {
761        self.spending_transaction = spending_transaction;
762    }
763
764    fn value(&self) -> u64 {
765        self.note.value().inner()
766    }
767
768    fn spend_link(&self) -> Option<Self::Input> {
769        self.nullifier
770    }
771
772    fn transaction_inputs(transaction: &WalletTransaction) -> Vec<&Self::Input> {
773        transaction.sapling_nullifiers()
774    }
775
776    fn transaction_outputs(transaction: &WalletTransaction) -> &[Self] {
777        &transaction.sapling_notes
778    }
779}
780
781impl NoteInterface for SaplingNote {
782    type ZcashNote = sapling_crypto::Note;
783    type Nullifier = Self::Input;
784
785    const SHIELDED_PROTOCOL: ShieldedProtocol = ShieldedProtocol::Sapling;
786
787    fn note(&self) -> &Self::ZcashNote {
788        &self.note
789    }
790
791    fn nullifier(&self) -> Option<Self::Nullifier> {
792        self.nullifier
793    }
794
795    fn position(&self) -> Option<Position> {
796        self.position
797    }
798
799    fn memo(&self) -> &Memo {
800        &self.memo
801    }
802}
803
804/// Orchard note.
805pub type OrchardNote = WalletNote<orchard::Note, orchard::note::Nullifier>;
806
807impl OutputInterface for OrchardNote {
808    type KeyId = KeyId;
809    type Input = orchard::note::Nullifier;
810
811    const POOL_TYPE: PoolType = PoolType::Shielded(ShieldedProtocol::Orchard);
812
813    fn output_id(&self) -> OutputId {
814        self.output_id
815    }
816
817    fn key_id(&self) -> KeyId {
818        self.key_id
819    }
820
821    fn spending_transaction(&self) -> Option<TxId> {
822        self.spending_transaction
823    }
824
825    fn set_spending_transaction(&mut self, spending_transaction: Option<TxId>) {
826        self.spending_transaction = spending_transaction;
827    }
828
829    fn value(&self) -> u64 {
830        self.note.value().inner()
831    }
832
833    fn spend_link(&self) -> Option<Self::Input> {
834        self.nullifier
835    }
836
837    fn transaction_inputs(transaction: &WalletTransaction) -> Vec<&Self::Input> {
838        transaction.orchard_nullifiers()
839    }
840
841    fn transaction_outputs(transaction: &WalletTransaction) -> &[Self] {
842        &transaction.orchard_notes
843    }
844}
845
846impl NoteInterface for OrchardNote {
847    type ZcashNote = orchard::Note;
848    type Nullifier = Self::Input;
849
850    const SHIELDED_PROTOCOL: ShieldedProtocol = ShieldedProtocol::Orchard;
851
852    fn note(&self) -> &Self::ZcashNote {
853        &self.note
854    }
855
856    fn nullifier(&self) -> Option<Self::Nullifier> {
857        self.spend_link()
858    }
859
860    fn position(&self) -> Option<Position> {
861        self.position
862    }
863
864    fn memo(&self) -> &Memo {
865        &self.memo
866    }
867}
868
869/// Provides a common API for all outgoing note types.
870pub trait OutgoingNoteInterface: Sized {
871    /// Decrypted note type.
872    type ZcashNote;
873    /// Address type.
874    type Address: Clone + Copy + Debug + PartialEq + Eq;
875    /// Encoding error
876    type Error: Debug + std::error::Error;
877
878    /// Note's associated shielded protocol.
879    const SHIELDED_PROTOCOL: ShieldedProtocol;
880
881    /// Output ID.
882    fn output_id(&self) -> OutputId;
883
884    /// Identifier for key used to decrypt outgoing note.
885    fn key_id(&self) -> KeyId;
886
887    /// Note value.
888    fn value(&self) -> u64;
889
890    /// Decrypted note with recipient and value.
891    fn note(&self) -> &Self::ZcashNote;
892
893    /// Memo.
894    fn memo(&self) -> &Memo;
895
896    /// Recipient address.
897    fn recipient(&self) -> Self::Address;
898
899    /// Recipient unified address as given by recipient and recorded in an encoded memo (all original receivers).
900    fn recipient_full_unified_address(&self) -> Option<&UnifiedAddress>;
901
902    /// Encoded recipient address recorded in note on chain (single receiver).
903    fn encoded_recipient<P>(&self, parameters: &P) -> Result<String, Self::Error>
904    where
905        P: consensus::Parameters + consensus::NetworkConstants;
906
907    /// Encoded recipient unified address as given by recipient and recorded in an encoded memo (all original receivers).
908    fn encoded_recipient_full_unified_address<P>(&self, consensus_parameters: &P) -> Option<String>
909    where
910        P: consensus::Parameters + consensus::NetworkConstants;
911
912    /// Outgoing notes within `transaction`.
913    fn transaction_outgoing_notes(transaction: &WalletTransaction) -> &[Self];
914}
915
916/// Note sent from this capability to a recipient.
917#[derive(Debug, Clone, PartialEq)]
918pub struct OutgoingNote<N> {
919    /// Output ID.
920    pub(crate) output_id: OutputId,
921    /// Identifier for key used to decrypt output.
922    pub(crate) key_id: KeyId,
923    /// Decrypted note with recipient and value.
924    pub(crate) note: N,
925    /// Memo.
926    pub(crate) memo: Memo,
927    /// Recipient's full unified address from encoded memo.
928    pub(crate) recipient_full_unified_address: Option<UnifiedAddress>,
929}
930
931/// Outgoing sapling note.
932pub type OutgoingSaplingNote = OutgoingNote<sapling_crypto::Note>;
933
934impl OutgoingNoteInterface for OutgoingSaplingNote {
935    type ZcashNote = sapling_crypto::Note;
936    type Address = sapling_crypto::PaymentAddress;
937    type Error = Infallible;
938
939    const SHIELDED_PROTOCOL: ShieldedProtocol = ShieldedProtocol::Sapling;
940
941    fn output_id(&self) -> OutputId {
942        self.output_id
943    }
944
945    fn key_id(&self) -> KeyId {
946        self.key_id
947    }
948
949    fn value(&self) -> u64 {
950        self.note.value().inner()
951    }
952
953    fn note(&self) -> &Self::ZcashNote {
954        &self.note
955    }
956
957    fn memo(&self) -> &Memo {
958        &self.memo
959    }
960
961    fn recipient(&self) -> Self::Address {
962        self.note.recipient()
963    }
964
965    fn recipient_full_unified_address(&self) -> Option<&UnifiedAddress> {
966        self.recipient_full_unified_address.as_ref()
967    }
968
969    fn encoded_recipient<P>(&self, consensus_parameters: &P) -> Result<String, Self::Error>
970    where
971        P: consensus::Parameters + consensus::NetworkConstants,
972    {
973        Ok(encode_payment_address(
974            consensus_parameters.hrp_sapling_payment_address(),
975            &self.note().recipient(),
976        ))
977    }
978
979    fn encoded_recipient_full_unified_address<P>(&self, consensus_parameters: &P) -> Option<String>
980    where
981        P: consensus::Parameters + consensus::NetworkConstants,
982    {
983        self.recipient_full_unified_address
984            .as_ref()
985            .map(|unified_address| unified_address.encode(consensus_parameters))
986    }
987
988    fn transaction_outgoing_notes(transaction: &WalletTransaction) -> &[Self] {
989        &transaction.outgoing_sapling_notes
990    }
991}
992
993/// Outgoing orchard note.
994pub type OutgoingOrchardNote = OutgoingNote<orchard::Note>;
995
996impl OutgoingNoteInterface for OutgoingOrchardNote {
997    type ZcashNote = orchard::Note;
998    type Address = orchard::Address;
999    type Error = ParseError;
1000
1001    const SHIELDED_PROTOCOL: ShieldedProtocol = ShieldedProtocol::Orchard;
1002
1003    fn output_id(&self) -> OutputId {
1004        self.output_id
1005    }
1006
1007    fn key_id(&self) -> KeyId {
1008        self.key_id
1009    }
1010
1011    fn value(&self) -> u64 {
1012        self.note.value().inner()
1013    }
1014
1015    fn note(&self) -> &Self::ZcashNote {
1016        &self.note
1017    }
1018
1019    fn memo(&self) -> &Memo {
1020        &self.memo
1021    }
1022
1023    fn recipient(&self) -> Self::Address {
1024        self.note.recipient()
1025    }
1026
1027    fn recipient_full_unified_address(&self) -> Option<&UnifiedAddress> {
1028        self.recipient_full_unified_address.as_ref()
1029    }
1030
1031    fn encoded_recipient<P>(&self, parameters: &P) -> Result<String, Self::Error>
1032    where
1033        P: consensus::Parameters + consensus::NetworkConstants,
1034    {
1035        keys::encode_orchard_receiver(parameters, &self.note().recipient())
1036    }
1037
1038    fn encoded_recipient_full_unified_address<P>(&self, consensus_parameters: &P) -> Option<String>
1039    where
1040        P: consensus::Parameters + consensus::NetworkConstants,
1041    {
1042        self.recipient_full_unified_address
1043            .as_ref()
1044            .map(|unified_address| unified_address.encode(consensus_parameters))
1045    }
1046
1047    fn transaction_outgoing_notes(transaction: &WalletTransaction) -> &[Self] {
1048        &transaction.outgoing_orchard_notes
1049    }
1050}
1051
1052// TODO: allow consumer to define shard store
1053/// Type alias for sapling memory shard store
1054pub type SaplingShardStore = MemoryShardStore<sapling_crypto::Node, BlockHeight>;
1055
1056/// Type alias for orchard memory shard store
1057pub type OrchardShardStore = MemoryShardStore<MerkleHashOrchard, BlockHeight>;
1058
1059/// Shard tree wallet data struct
1060#[derive(Debug)]
1061pub struct ShardTrees {
1062    /// Sapling shard tree
1063    pub sapling: ShardTree<
1064        SaplingShardStore,
1065        { sapling_crypto::NOTE_COMMITMENT_TREE_DEPTH },
1066        { witness::SHARD_HEIGHT },
1067    >,
1068    /// Orchard shard tree
1069    pub orchard: ShardTree<
1070        OrchardShardStore,
1071        { orchard::NOTE_COMMITMENT_TREE_DEPTH as u8 },
1072        { witness::SHARD_HEIGHT },
1073    >,
1074}
1075
1076impl ShardTrees {
1077    /// Create new ShardTrees
1078    pub fn new() -> Self {
1079        let mut sapling =
1080            ShardTree::new(MemoryShardStore::empty(), MAX_VERIFICATION_WINDOW as usize);
1081        let mut orchard =
1082            ShardTree::new(MemoryShardStore::empty(), MAX_VERIFICATION_WINDOW as usize);
1083
1084        sapling
1085            .checkpoint(BlockHeight::from_u32(0))
1086            .expect("should never fail");
1087        orchard
1088            .checkpoint(BlockHeight::from_u32(0))
1089            .expect("should never fail");
1090
1091        Self { sapling, orchard }
1092    }
1093}
1094
1095impl Default for ShardTrees {
1096    fn default() -> Self {
1097        Self::new()
1098    }
1099}