1use 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::proto::compact_formats::CompactBlock;
23use zcash_keys::{address::UnifiedAddress, encoding::encode_payment_address};
24use zcash_primitives::{
25 block::BlockHash,
26 legacy::Script,
27 memo::Memo,
28 transaction::{TxId, components::transparent::OutPoint},
29};
30use zcash_protocol::{
31 PoolType, ShieldedProtocol,
32 consensus::{self, BlockHeight},
33 value::Zatoshis,
34};
35
36use zingo_status::confirmation_status::ConfirmationStatus;
37
38use crate::{
39 client::FetchRequest,
40 error::{ServerError, SyncModeError},
41 keys::{self, KeyId, transparent::TransparentAddressId},
42 scan::compact_blocks::calculate_block_tree_bounds,
43 sync::{MAX_VERIFICATION_WINDOW, ScanPriority, ScanRange},
44 witness,
45};
46
47pub mod traits;
48
49#[cfg(feature = "wallet_essentials")]
50pub mod serialization;
51
52#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
62pub struct ScanTarget {
63 pub block_height: BlockHeight,
65 pub txid: TxId,
67 pub narrow_scan_area: bool,
69}
70
71#[derive(Debug, Clone)]
75pub struct InitialSyncState {
76 pub(crate) sync_start_height: BlockHeight,
81 pub(crate) wallet_tree_bounds: TreeBounds,
83 pub(crate) previously_scanned_blocks: u32,
85 pub(crate) previously_scanned_sapling_outputs: u32,
87 pub(crate) previously_scanned_orchard_outputs: u32,
89}
90
91impl InitialSyncState {
92 #[must_use]
94 pub fn new() -> Self {
95 InitialSyncState {
96 sync_start_height: 0.into(),
97 wallet_tree_bounds: TreeBounds {
98 sapling_initial_tree_size: 0,
99 sapling_final_tree_size: 0,
100 orchard_initial_tree_size: 0,
101 orchard_final_tree_size: 0,
102 },
103 previously_scanned_blocks: 0,
104 previously_scanned_sapling_outputs: 0,
105 previously_scanned_orchard_outputs: 0,
106 }
107 }
108}
109
110impl Default for InitialSyncState {
111 fn default() -> Self {
112 Self::new()
113 }
114}
115
116#[derive(Debug, Clone)]
118pub struct SyncState {
119 pub(crate) scan_ranges: Vec<ScanRange>,
122 pub(crate) sapling_shard_ranges: Vec<Range<BlockHeight>>,
127 pub(crate) orchard_shard_ranges: Vec<Range<BlockHeight>>,
132 pub(crate) scan_targets: BTreeSet<ScanTarget>,
134 pub(crate) initial_sync_state: InitialSyncState,
136}
137
138impl SyncState {
139 #[must_use]
141 pub fn new() -> Self {
142 SyncState {
143 scan_ranges: Vec::new(),
144 sapling_shard_ranges: Vec::new(),
145 orchard_shard_ranges: Vec::new(),
146 scan_targets: BTreeSet::new(),
147 initial_sync_state: InitialSyncState::new(),
148 }
149 }
150
151 #[must_use]
153 pub fn scan_ranges(&self) -> &[ScanRange] {
154 &self.scan_ranges
155 }
156
157 #[must_use]
159 pub fn sapling_shard_ranges(&self) -> &[Range<BlockHeight>] {
160 &self.sapling_shard_ranges
161 }
162
163 #[must_use]
165 pub fn orchard_shard_ranges(&self) -> &[Range<BlockHeight>] {
166 &self.orchard_shard_ranges
167 }
168
169 pub(crate) fn scan_complete(&self) -> bool {
171 self.scan_ranges
172 .iter()
173 .all(|scan_range| scan_range.priority() == ScanPriority::Scanned)
174 }
175
176 #[must_use]
179 pub fn fully_scanned_height(&self) -> Option<BlockHeight> {
180 if let Some(scan_range) = self
181 .scan_ranges
182 .iter()
183 .find(|scan_range| scan_range.priority() != ScanPriority::Scanned)
184 {
185 Some(scan_range.block_range().start - 1)
186 } else {
187 self.scan_ranges
188 .last()
189 .map(|range| range.block_range().end - 1)
190 }
191 }
192
193 #[must_use]
197 pub fn highest_scanned_height(&self) -> Option<BlockHeight> {
198 if let Some(last_scanned_range) = self
199 .scan_ranges
200 .iter()
201 .filter(|scan_range| {
202 scan_range.priority() == ScanPriority::Scanned
203 || scan_range.priority() == ScanPriority::ScannedWithoutMapping
204 })
205 .next_back()
206 {
207 Some(last_scanned_range.block_range().end - 1)
208 } else {
209 self.wallet_birthday().map(|birthday| birthday - 1)
210 }
211 }
212
213 #[must_use]
217 pub fn wallet_birthday(&self) -> Option<BlockHeight> {
218 self.scan_ranges
219 .first()
220 .map(|range| range.block_range().start)
221 }
222
223 #[must_use]
225 pub fn wallet_height(&self) -> Option<BlockHeight> {
226 self.scan_ranges
227 .last()
228 .map(|range| range.block_range().end - 1)
229 }
230}
231
232impl Default for SyncState {
233 fn default() -> Self {
234 Self::new()
235 }
236}
237
238#[derive(Debug, Clone, Copy, PartialEq, Eq)]
240pub enum SyncMode {
241 NotRunning,
243 Paused,
245 Running,
247 Shutdown,
249}
250
251impl SyncMode {
252 pub fn from_u8(mode: u8) -> Result<Self, SyncModeError> {
256 match mode {
257 0 => Ok(Self::NotRunning),
258 1 => Ok(Self::Paused),
259 2 => Ok(Self::Running),
260 3 => Ok(Self::Shutdown),
261 _ => Err(SyncModeError::InvalidSyncMode(mode)),
262 }
263 }
264
265 pub fn from_atomic_u8(atomic_sync_mode: Arc<AtomicU8>) -> Result<SyncMode, SyncModeError> {
273 SyncMode::from_u8(atomic_sync_mode.load(atomic::Ordering::Acquire))
274 }
275}
276
277#[derive(Debug, Clone, Copy)]
279#[allow(missing_docs)]
280pub struct TreeBounds {
281 pub sapling_initial_tree_size: u32,
282 pub sapling_final_tree_size: u32,
283 pub orchard_initial_tree_size: u32,
284 pub orchard_final_tree_size: u32,
285}
286
287#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)]
289pub struct OutputId {
290 txid: TxId,
292 output_index: u16,
294}
295
296impl OutputId {
297 #[must_use]
299 pub fn new(txid: TxId, output_index: u16) -> Self {
300 OutputId { txid, output_index }
301 }
302
303 #[must_use]
305 pub fn txid(&self) -> TxId {
306 self.txid
307 }
308
309 #[must_use]
311 pub fn output_index(&self) -> u16 {
312 self.output_index
313 }
314}
315
316impl std::fmt::Display for OutputId {
317 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
318 write!(
319 f,
320 "{{
321 txid: {}
322 output index: {}
323 }}",
324 self.txid, self.output_index
325 )
326 }
327}
328
329impl From<&OutPoint> for OutputId {
330 fn from(value: &OutPoint) -> Self {
331 OutputId::new(*value.txid(), value.n() as u16)
332 }
333}
334
335impl From<OutputId> for OutPoint {
336 fn from(value: OutputId) -> Self {
337 OutPoint::new(value.txid.into(), u32::from(value.output_index))
338 }
339}
340
341#[derive(Debug)]
343pub struct NullifierMap {
344 pub sapling: BTreeMap<sapling_crypto::Nullifier, ScanTarget>,
346 pub orchard: BTreeMap<orchard::note::Nullifier, ScanTarget>,
348}
349
350impl NullifierMap {
351 #[must_use]
353 pub fn new() -> Self {
354 Self {
355 sapling: BTreeMap::new(),
356 orchard: BTreeMap::new(),
357 }
358 }
359
360 pub fn clear(&mut self) {
362 self.sapling.clear();
363 self.orchard.clear();
364 }
365}
366
367impl Default for NullifierMap {
368 fn default() -> Self {
369 Self::new()
370 }
371}
372
373#[derive(Debug, Clone)]
375pub struct WalletBlock {
376 pub(crate) block_height: BlockHeight,
377 pub(crate) block_hash: BlockHash,
378 pub(crate) prev_hash: BlockHash,
379 pub(crate) time: u32,
380 pub(crate) txids: Vec<TxId>,
381 pub(crate) tree_bounds: TreeBounds,
382}
383
384impl WalletBlock {
385 pub(crate) async fn from_compact_block(
386 consensus_parameters: &impl consensus::Parameters,
387 fetch_request_sender: mpsc::UnboundedSender<FetchRequest>,
388 block: &CompactBlock,
389 ) -> Result<Self, ServerError> {
390 let tree_bounds =
391 calculate_block_tree_bounds(consensus_parameters, fetch_request_sender, block).await?;
392
393 Ok(Self {
394 block_height: block.height(),
395 block_hash: block.hash(),
396 prev_hash: block.prev_hash(),
397 time: block.time,
398 txids: block
399 .vtx
400 .iter()
401 .map(zcash_client_backend::proto::compact_formats::CompactTx::txid)
402 .collect(),
403 tree_bounds,
404 })
405 }
406
407 #[must_use]
409 pub fn block_height(&self) -> BlockHeight {
410 self.block_height
411 }
412
413 #[must_use]
415 pub fn block_hash(&self) -> BlockHash {
416 self.block_hash
417 }
418
419 #[must_use]
421 pub fn prev_hash(&self) -> BlockHash {
422 self.prev_hash
423 }
424
425 #[must_use]
427 pub fn time(&self) -> u32 {
428 self.time
429 }
430
431 #[must_use]
433 pub fn txids(&self) -> &[TxId] {
434 &self.txids
435 }
436
437 #[must_use]
439 pub fn tree_bounds(&self) -> TreeBounds {
440 self.tree_bounds
441 }
442}
443
444pub struct WalletTransaction {
446 pub(crate) txid: TxId,
447 pub(crate) status: ConfirmationStatus,
448 pub(crate) transaction: zcash_primitives::transaction::Transaction,
449 pub(crate) datetime: u32,
450 pub(crate) transparent_coins: Vec<TransparentCoin>,
451 pub(crate) sapling_notes: Vec<SaplingNote>,
452 pub(crate) orchard_notes: Vec<OrchardNote>,
453 pub(crate) outgoing_sapling_notes: Vec<OutgoingSaplingNote>,
454 pub(crate) outgoing_orchard_notes: Vec<OutgoingOrchardNote>,
455}
456
457impl WalletTransaction {
458 #[must_use]
460 pub fn txid(&self) -> TxId {
461 self.txid
462 }
463
464 #[must_use]
466 pub fn status(&self) -> ConfirmationStatus {
467 self.status
468 }
469
470 #[must_use]
472 pub fn transaction(&self) -> &zcash_primitives::transaction::Transaction {
473 &self.transaction
474 }
475
476 #[must_use]
478 pub fn datetime(&self) -> u32 {
479 self.datetime
480 }
481
482 #[must_use]
484 pub fn transparent_coins(&self) -> &[TransparentCoin] {
485 &self.transparent_coins
486 }
487
488 pub fn transparent_coins_mut(&mut self) -> Vec<&mut TransparentCoin> {
490 self.transparent_coins.iter_mut().collect()
491 }
492
493 #[must_use]
495 pub fn sapling_notes(&self) -> &[SaplingNote] {
496 &self.sapling_notes
497 }
498
499 pub fn sapling_notes_mut(&mut self) -> Vec<&mut SaplingNote> {
501 self.sapling_notes.iter_mut().collect()
502 }
503
504 #[must_use]
506 pub fn orchard_notes(&self) -> &[OrchardNote] {
507 &self.orchard_notes
508 }
509
510 pub fn orchard_notes_mut(&mut self) -> Vec<&mut OrchardNote> {
512 self.orchard_notes.iter_mut().collect()
513 }
514
515 #[must_use]
517 pub fn outgoing_sapling_notes(&self) -> &[OutgoingSaplingNote] {
518 &self.outgoing_sapling_notes
519 }
520
521 #[must_use]
523 pub fn outgoing_orchard_notes(&self) -> &[OutgoingOrchardNote] {
524 &self.outgoing_orchard_notes
525 }
526
527 pub fn sapling_nullifiers(&self) -> Vec<&sapling_crypto::Nullifier> {
530 self.transaction
531 .sapling_bundle()
532 .map_or_else(Vec::new, |bundle| {
533 bundle
534 .shielded_spends()
535 .iter()
536 .map(sapling_crypto::bundle::SpendDescription::nullifier)
537 .collect::<Vec<_>>()
538 })
539 }
540
541 pub fn orchard_nullifiers(&self) -> Vec<&orchard::note::Nullifier> {
544 self.transaction
545 .orchard_bundle()
546 .map_or_else(Vec::new, |bundle| {
547 bundle
548 .actions()
549 .iter()
550 .map(orchard::Action::nullifier)
551 .collect::<Vec<_>>()
552 })
553 }
554
555 pub fn outpoints(&self) -> Vec<&OutPoint> {
558 self.transaction
559 .transparent_bundle()
560 .map_or_else(Vec::new, |bundle| {
561 bundle
562 .vin
563 .iter()
564 .map(zcash_transparent::bundle::TxIn::prevout)
565 .collect::<Vec<_>>()
566 })
567 }
568}
569
570#[cfg(feature = "wallet_essentials")]
571impl WalletTransaction {
572 #[must_use]
574 pub fn total_value_sent(&self) -> u64 {
575 let transparent_value_sent = self
576 .transaction
577 .transparent_bundle()
578 .map_or(0, |bundle| {
579 bundle
580 .vout
581 .iter()
582 .map(|output| output.value().into_u64())
583 .sum()
584 })
585 .saturating_sub(self.total_output_value::<TransparentCoin>());
586
587 let sapling_value_sent = self
590 .total_outgoing_note_value::<OutgoingSaplingNote>()
591 .saturating_sub(self.total_output_value::<SaplingNote>());
592 let orchard_value_sent = self
593 .total_outgoing_note_value::<OutgoingOrchardNote>()
594 .saturating_sub(self.total_output_value::<OrchardNote>());
595
596 transparent_value_sent + sapling_value_sent + orchard_value_sent
597 }
598
599 #[must_use]
601 pub fn total_value_received(&self) -> u64 {
602 self.total_output_value::<TransparentCoin>()
603 + self.total_output_value::<SaplingNote>()
604 + self.total_output_value::<OrchardNote>()
605 }
606
607 #[must_use]
609 pub fn total_output_value<Op: OutputInterface>(&self) -> u64 {
610 Op::transaction_outputs(self)
611 .iter()
612 .map(OutputInterface::value)
613 .sum()
614 }
615
616 #[must_use]
618 pub fn total_outgoing_note_value<Op: OutgoingNoteInterface>(&self) -> u64 {
619 Op::transaction_outgoing_notes(self)
620 .iter()
621 .map(OutgoingNoteInterface::value)
622 .sum()
623 }
624}
625
626impl std::fmt::Debug for WalletTransaction {
627 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
628 f.debug_struct("WalletTransaction")
629 .field("txid", &self.txid)
630 .field("confirmation_status", &self.status)
631 .field("datetime", &self.datetime)
632 .field("transparent_coins", &self.transparent_coins)
633 .field("sapling_notes", &self.sapling_notes)
634 .field("orchard_notes", &self.orchard_notes)
635 .field("outgoing_sapling_notes", &self.outgoing_sapling_notes)
636 .field("outgoing_orchard_notes", &self.outgoing_orchard_notes)
637 .finish()
638 }
639}
640
641pub trait KeyIdInterface {
643 fn account_id(&self) -> zip32::AccountId;
645}
646
647pub trait OutputInterface: Sized {
649 type KeyId: KeyIdInterface;
651 type Input: Clone + Debug + PartialEq + Eq + PartialOrd + Ord;
653
654 const POOL_TYPE: PoolType;
656
657 fn output_id(&self) -> OutputId;
659
660 fn key_id(&self) -> Self::KeyId;
662
663 fn spending_transaction(&self) -> Option<TxId>;
666
667 fn set_spending_transaction(&mut self, spending_transaction: Option<TxId>);
669
670 fn value(&self) -> u64;
673
674 fn spend_link(&self) -> Option<Self::Input>;
680
681 fn transaction_inputs(transaction: &WalletTransaction) -> Vec<&Self::Input>;
686
687 fn transaction_outputs(transaction: &WalletTransaction) -> &[Self];
689}
690
691pub trait NoteInterface: OutputInterface {
693 type ZcashNote;
695 type Nullifier: Copy;
697
698 const SHIELDED_PROTOCOL: ShieldedProtocol;
700
701 fn note(&self) -> &Self::ZcashNote;
703
704 fn nullifier(&self) -> Option<Self::Nullifier>;
706
707 fn position(&self) -> Option<Position>;
709
710 fn memo(&self) -> &Memo;
712}
713
714#[derive(Debug, Clone)]
716pub struct TransparentCoin {
717 pub(crate) output_id: OutputId,
719 pub(crate) key_id: TransparentAddressId,
721 pub(crate) address: String,
723 pub(crate) script: Script,
725 pub(crate) value: Zatoshis,
727 pub(crate) spending_transaction: Option<TxId>,
730}
731
732impl TransparentCoin {
733 #[must_use]
735 pub fn address(&self) -> &str {
736 &self.address
737 }
738
739 #[must_use]
741 pub fn script(&self) -> &Script {
742 &self.script
743 }
744}
745
746impl OutputInterface for TransparentCoin {
747 type KeyId = TransparentAddressId;
748 type Input = OutPoint;
749
750 const POOL_TYPE: PoolType = PoolType::Transparent;
751
752 fn output_id(&self) -> OutputId {
753 self.output_id
754 }
755
756 fn key_id(&self) -> Self::KeyId {
757 self.key_id
758 }
759
760 fn spending_transaction(&self) -> Option<TxId> {
761 self.spending_transaction
762 }
763
764 fn set_spending_transaction(&mut self, spending_transaction: Option<TxId>) {
765 self.spending_transaction = spending_transaction;
766 }
767
768 fn value(&self) -> u64 {
769 self.value.into_u64()
770 }
771
772 fn spend_link(&self) -> Option<Self::Input> {
773 Some(self.output_id.into())
774 }
775
776 fn transaction_inputs(transaction: &WalletTransaction) -> Vec<&Self::Input> {
777 transaction.outpoints()
778 }
779
780 fn transaction_outputs(transaction: &WalletTransaction) -> &[Self] {
781 &transaction.transparent_coins
782 }
783}
784
785#[derive(Debug, Clone)]
787pub struct WalletNote<N, Nf: Copy> {
788 pub(crate) output_id: OutputId,
790 pub(crate) key_id: KeyId,
792 pub(crate) note: N,
794 pub(crate) nullifier: Option<Nf>, pub(crate) position: Option<Position>,
798 pub(crate) memo: Memo,
800 pub(crate) spending_transaction: Option<TxId>,
803}
804
805pub type SaplingNote = WalletNote<sapling_crypto::Note, sapling_crypto::Nullifier>;
807
808impl OutputInterface for SaplingNote {
809 type KeyId = KeyId;
810 type Input = sapling_crypto::Nullifier;
811
812 const POOL_TYPE: PoolType = PoolType::Shielded(ShieldedProtocol::Sapling);
813
814 fn output_id(&self) -> OutputId {
815 self.output_id
816 }
817
818 fn key_id(&self) -> KeyId {
819 self.key_id
820 }
821
822 fn spending_transaction(&self) -> Option<TxId> {
823 self.spending_transaction
824 }
825
826 fn set_spending_transaction(&mut self, spending_transaction: Option<TxId>) {
827 self.spending_transaction = spending_transaction;
828 }
829
830 fn value(&self) -> u64 {
831 self.note.value().inner()
832 }
833
834 fn spend_link(&self) -> Option<Self::Input> {
835 self.nullifier
836 }
837
838 fn transaction_inputs(transaction: &WalletTransaction) -> Vec<&Self::Input> {
839 transaction.sapling_nullifiers()
840 }
841
842 fn transaction_outputs(transaction: &WalletTransaction) -> &[Self] {
843 &transaction.sapling_notes
844 }
845}
846
847impl NoteInterface for SaplingNote {
848 type ZcashNote = sapling_crypto::Note;
849 type Nullifier = Self::Input;
850
851 const SHIELDED_PROTOCOL: ShieldedProtocol = ShieldedProtocol::Sapling;
852
853 fn note(&self) -> &Self::ZcashNote {
854 &self.note
855 }
856
857 fn nullifier(&self) -> Option<Self::Nullifier> {
858 self.nullifier
859 }
860
861 fn position(&self) -> Option<Position> {
862 self.position
863 }
864
865 fn memo(&self) -> &Memo {
866 &self.memo
867 }
868}
869
870pub type OrchardNote = WalletNote<orchard::Note, orchard::note::Nullifier>;
872
873impl OutputInterface for OrchardNote {
874 type KeyId = KeyId;
875 type Input = orchard::note::Nullifier;
876
877 const POOL_TYPE: PoolType = PoolType::Shielded(ShieldedProtocol::Orchard);
878
879 fn output_id(&self) -> OutputId {
880 self.output_id
881 }
882
883 fn key_id(&self) -> KeyId {
884 self.key_id
885 }
886
887 fn spending_transaction(&self) -> Option<TxId> {
888 self.spending_transaction
889 }
890
891 fn set_spending_transaction(&mut self, spending_transaction: Option<TxId>) {
892 self.spending_transaction = spending_transaction;
893 }
894
895 fn value(&self) -> u64 {
896 self.note.value().inner()
897 }
898
899 fn spend_link(&self) -> Option<Self::Input> {
900 self.nullifier
901 }
902
903 fn transaction_inputs(transaction: &WalletTransaction) -> Vec<&Self::Input> {
904 transaction.orchard_nullifiers()
905 }
906
907 fn transaction_outputs(transaction: &WalletTransaction) -> &[Self] {
908 &transaction.orchard_notes
909 }
910}
911
912impl NoteInterface for OrchardNote {
913 type ZcashNote = orchard::Note;
914 type Nullifier = Self::Input;
915
916 const SHIELDED_PROTOCOL: ShieldedProtocol = ShieldedProtocol::Orchard;
917
918 fn note(&self) -> &Self::ZcashNote {
919 &self.note
920 }
921
922 fn nullifier(&self) -> Option<Self::Nullifier> {
923 self.spend_link()
924 }
925
926 fn position(&self) -> Option<Position> {
927 self.position
928 }
929
930 fn memo(&self) -> &Memo {
931 &self.memo
932 }
933}
934
935pub trait OutgoingNoteInterface: Sized {
937 type ZcashNote;
939 type Address: Clone + Copy + Debug + PartialEq + Eq;
941 type Error: Debug + std::error::Error;
943
944 const SHIELDED_PROTOCOL: ShieldedProtocol;
946
947 fn output_id(&self) -> OutputId;
949
950 fn key_id(&self) -> KeyId;
952
953 fn value(&self) -> u64;
955
956 fn note(&self) -> &Self::ZcashNote;
958
959 fn memo(&self) -> &Memo;
961
962 fn recipient(&self) -> Self::Address;
964
965 fn recipient_full_unified_address(&self) -> Option<&UnifiedAddress>;
967
968 fn encoded_recipient<P>(&self, parameters: &P) -> Result<String, Self::Error>
970 where
971 P: consensus::Parameters + consensus::NetworkConstants;
972
973 fn encoded_recipient_full_unified_address<P>(&self, consensus_parameters: &P) -> Option<String>
975 where
976 P: consensus::Parameters + consensus::NetworkConstants;
977
978 fn transaction_outgoing_notes(transaction: &WalletTransaction) -> &[Self];
980}
981
982#[derive(Debug, Clone, PartialEq)]
984pub struct OutgoingNote<N> {
985 pub(crate) output_id: OutputId,
987 pub(crate) key_id: KeyId,
989 pub(crate) note: N,
991 pub(crate) memo: Memo,
993 pub(crate) recipient_full_unified_address: Option<UnifiedAddress>,
995}
996
997pub type OutgoingSaplingNote = OutgoingNote<sapling_crypto::Note>;
999
1000impl OutgoingNoteInterface for OutgoingSaplingNote {
1001 type ZcashNote = sapling_crypto::Note;
1002 type Address = sapling_crypto::PaymentAddress;
1003 type Error = Infallible;
1004
1005 const SHIELDED_PROTOCOL: ShieldedProtocol = ShieldedProtocol::Sapling;
1006
1007 fn output_id(&self) -> OutputId {
1008 self.output_id
1009 }
1010
1011 fn key_id(&self) -> KeyId {
1012 self.key_id
1013 }
1014
1015 fn value(&self) -> u64 {
1016 self.note.value().inner()
1017 }
1018
1019 fn note(&self) -> &Self::ZcashNote {
1020 &self.note
1021 }
1022
1023 fn memo(&self) -> &Memo {
1024 &self.memo
1025 }
1026
1027 fn recipient(&self) -> Self::Address {
1028 self.note.recipient()
1029 }
1030
1031 fn recipient_full_unified_address(&self) -> Option<&UnifiedAddress> {
1032 self.recipient_full_unified_address.as_ref()
1033 }
1034
1035 fn encoded_recipient<P>(&self, consensus_parameters: &P) -> Result<String, Self::Error>
1036 where
1037 P: consensus::Parameters + consensus::NetworkConstants,
1038 {
1039 Ok(encode_payment_address(
1040 consensus_parameters.hrp_sapling_payment_address(),
1041 &self.note().recipient(),
1042 ))
1043 }
1044
1045 fn encoded_recipient_full_unified_address<P>(&self, consensus_parameters: &P) -> Option<String>
1046 where
1047 P: consensus::Parameters + consensus::NetworkConstants,
1048 {
1049 self.recipient_full_unified_address
1050 .as_ref()
1051 .map(|unified_address| unified_address.encode(consensus_parameters))
1052 }
1053
1054 fn transaction_outgoing_notes(transaction: &WalletTransaction) -> &[Self] {
1055 &transaction.outgoing_sapling_notes
1056 }
1057}
1058
1059pub type OutgoingOrchardNote = OutgoingNote<orchard::Note>;
1061
1062impl OutgoingNoteInterface for OutgoingOrchardNote {
1063 type ZcashNote = orchard::Note;
1064 type Address = orchard::Address;
1065 type Error = ParseError;
1066
1067 const SHIELDED_PROTOCOL: ShieldedProtocol = ShieldedProtocol::Orchard;
1068
1069 fn output_id(&self) -> OutputId {
1070 self.output_id
1071 }
1072
1073 fn key_id(&self) -> KeyId {
1074 self.key_id
1075 }
1076
1077 fn value(&self) -> u64 {
1078 self.note.value().inner()
1079 }
1080
1081 fn note(&self) -> &Self::ZcashNote {
1082 &self.note
1083 }
1084
1085 fn memo(&self) -> &Memo {
1086 &self.memo
1087 }
1088
1089 fn recipient(&self) -> Self::Address {
1090 self.note.recipient()
1091 }
1092
1093 fn recipient_full_unified_address(&self) -> Option<&UnifiedAddress> {
1094 self.recipient_full_unified_address.as_ref()
1095 }
1096
1097 fn encoded_recipient<P>(&self, parameters: &P) -> Result<String, Self::Error>
1098 where
1099 P: consensus::Parameters + consensus::NetworkConstants,
1100 {
1101 keys::encode_orchard_receiver(parameters, &self.note().recipient())
1102 }
1103
1104 fn encoded_recipient_full_unified_address<P>(&self, consensus_parameters: &P) -> Option<String>
1105 where
1106 P: consensus::Parameters + consensus::NetworkConstants,
1107 {
1108 self.recipient_full_unified_address
1109 .as_ref()
1110 .map(|unified_address| unified_address.encode(consensus_parameters))
1111 }
1112
1113 fn transaction_outgoing_notes(transaction: &WalletTransaction) -> &[Self] {
1114 &transaction.outgoing_orchard_notes
1115 }
1116}
1117
1118pub type SaplingShardStore = MemoryShardStore<sapling_crypto::Node, BlockHeight>;
1122
1123pub type OrchardShardStore = MemoryShardStore<MerkleHashOrchard, BlockHeight>;
1125
1126#[derive(Debug)]
1128pub struct ShardTrees {
1129 pub sapling: ShardTree<
1131 SaplingShardStore,
1132 { sapling_crypto::NOTE_COMMITMENT_TREE_DEPTH },
1133 { witness::SHARD_HEIGHT },
1134 >,
1135 pub orchard: ShardTree<
1137 OrchardShardStore,
1138 { orchard::NOTE_COMMITMENT_TREE_DEPTH as u8 },
1139 { witness::SHARD_HEIGHT },
1140 >,
1141}
1142
1143impl ShardTrees {
1144 #[must_use]
1146 pub fn new() -> Self {
1147 let mut sapling =
1148 ShardTree::new(MemoryShardStore::empty(), MAX_VERIFICATION_WINDOW as usize);
1149 let mut orchard =
1150 ShardTree::new(MemoryShardStore::empty(), MAX_VERIFICATION_WINDOW as usize);
1151
1152 sapling
1153 .checkpoint(BlockHeight::from_u32(0))
1154 .expect("should never fail");
1155 orchard
1156 .checkpoint(BlockHeight::from_u32(0))
1157 .expect("should never fail");
1158
1159 Self { sapling, orchard }
1160 }
1161}
1162
1163impl Default for ShardTrees {
1164 fn default() -> Self {
1165 Self::new()
1166 }
1167}