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::{
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
55pub type Locator = (BlockHeight, TxId);
58
59#[derive(Debug, Clone)]
63pub struct InitialSyncState {
64 pub(crate) sync_start_height: BlockHeight,
69 pub(crate) wallet_tree_bounds: TreeBounds,
71 pub(crate) previously_scanned_blocks: u32,
73 pub(crate) previously_scanned_sapling_outputs: u32,
75 pub(crate) previously_scanned_orchard_outputs: u32,
77}
78
79impl InitialSyncState {
80 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#[derive(Debug, Clone)]
105pub struct SyncState {
106 pub(crate) scan_ranges: Vec<ScanRange>,
109 pub(crate) sapling_shard_ranges: Vec<Range<BlockHeight>>,
114 pub(crate) orchard_shard_ranges: Vec<Range<BlockHeight>>,
119 pub(crate) locators: BTreeSet<Locator>,
121 pub(crate) initial_sync_state: InitialSyncState,
123}
124
125impl SyncState {
126 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 pub fn scan_ranges(&self) -> &[ScanRange] {
139 &self.scan_ranges
140 }
141
142 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 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 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 pub fn wallet_birthday(&self) -> Option<BlockHeight> {
185 self.scan_ranges
186 .first()
187 .map(|range| range.block_range().start)
188 }
189
190 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#[derive(Debug, Clone, Copy, PartialEq, Eq)]
206pub enum SyncMode {
207 NotRunning,
209 Paused,
211 Running,
213 Shutdown,
215}
216
217impl SyncMode {
218 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 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#[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#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)]
255pub struct OutputId {
256 txid: TxId,
258 output_index: u16,
260}
261
262impl OutputId {
263 pub fn new(txid: TxId, output_index: u16) -> Self {
265 OutputId { txid, output_index }
266 }
267
268 pub fn txid(&self) -> TxId {
270 self.txid
271 }
272
273 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#[derive(Debug)]
306pub struct NullifierMap {
307 pub sapling: BTreeMap<sapling_crypto::Nullifier, Locator>,
309 pub orchard: BTreeMap<orchard::note::Nullifier, Locator>,
311}
312
313impl NullifierMap {
314 pub fn new() -> Self {
316 Self {
317 sapling: BTreeMap::new(),
318 orchard: BTreeMap::new(),
319 }
320 }
321
322 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#[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 pub fn block_height(&self) -> BlockHeight {
371 self.block_height
372 }
373
374 pub fn block_hash(&self) -> BlockHash {
376 self.block_hash
377 }
378
379 pub fn prev_hash(&self) -> BlockHash {
381 self.prev_hash
382 }
383
384 pub fn time(&self) -> u32 {
386 self.time
387 }
388
389 pub fn txids(&self) -> &[TxId] {
391 &self.txids
392 }
393
394 pub fn tree_bounds(&self) -> TreeBounds {
396 self.tree_bounds
397 }
398}
399
400pub 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 pub fn txid(&self) -> TxId {
416 self.txid
417 }
418
419 pub fn status(&self) -> ConfirmationStatus {
421 self.status
422 }
423
424 pub fn transaction(&self) -> &zcash_primitives::transaction::Transaction {
426 &self.transaction
427 }
428
429 pub fn datetime(&self) -> u32 {
431 self.datetime
432 }
433
434 pub fn transparent_coins(&self) -> &[TransparentCoin] {
436 &self.transparent_coins
437 }
438
439 pub fn transparent_coins_mut(&mut self) -> Vec<&mut TransparentCoin> {
441 self.transparent_coins.iter_mut().collect()
442 }
443
444 pub fn sapling_notes(&self) -> &[SaplingNote] {
446 &self.sapling_notes
447 }
448
449 pub fn sapling_notes_mut(&mut self) -> Vec<&mut SaplingNote> {
451 self.sapling_notes.iter_mut().collect()
452 }
453
454 pub fn orchard_notes(&self) -> &[OrchardNote] {
456 &self.orchard_notes
457 }
458
459 pub fn orchard_notes_mut(&mut self) -> Vec<&mut OrchardNote> {
461 self.orchard_notes.iter_mut().collect()
462 }
463
464 pub fn outgoing_sapling_notes(&self) -> &[OutgoingSaplingNote] {
466 &self.outgoing_sapling_notes
467 }
468
469 pub fn outgoing_orchard_notes(&self) -> &[OutgoingOrchardNote] {
471 &self.outgoing_orchard_notes
472 }
473
474 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 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 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 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 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 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 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 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
584pub trait OutputInterface: Sized {
586 type KeyId;
588 type Input: Clone + Debug + PartialEq + Eq + PartialOrd + Ord;
590
591 const POOL_TYPE: PoolType;
593
594 fn output_id(&self) -> OutputId;
596
597 fn key_id(&self) -> Self::KeyId;
599
600 fn spending_transaction(&self) -> Option<TxId>;
603
604 fn set_spending_transaction(&mut self, spending_transaction: Option<TxId>);
606
607 fn value(&self) -> u64;
609
610 fn spend_link(&self) -> Option<Self::Input>;
616
617 fn transaction_inputs(transaction: &WalletTransaction) -> Vec<&Self::Input>;
622
623 fn transaction_outputs(transaction: &WalletTransaction) -> &[Self];
625}
626
627pub trait NoteInterface: OutputInterface {
629 type ZcashNote;
631 type Nullifier: Copy;
633
634 const SHIELDED_PROTOCOL: ShieldedProtocol;
636
637 fn note(&self) -> &Self::ZcashNote;
639
640 fn nullifier(&self) -> Option<Self::Nullifier>;
642
643 fn position(&self) -> Option<Position>;
645
646 fn memo(&self) -> &Memo;
648}
649
650#[derive(Debug, Clone)]
652pub struct TransparentCoin {
653 pub(crate) output_id: OutputId,
655 pub(crate) key_id: TransparentAddressId,
657 pub(crate) address: String,
659 pub(crate) script: Script,
661 pub(crate) value: Zatoshis,
663 pub(crate) spending_transaction: Option<TxId>,
666}
667
668impl TransparentCoin {
669 pub fn address(&self) -> &str {
671 &self.address
672 }
673
674 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#[derive(Debug, Clone)]
721pub struct WalletNote<N, Nf: Copy> {
722 pub(crate) output_id: OutputId,
724 pub(crate) key_id: KeyId,
726 pub(crate) note: N,
728 pub(crate) nullifier: Option<Nf>, pub(crate) position: Option<Position>,
732 pub(crate) memo: Memo,
734 pub(crate) spending_transaction: Option<TxId>,
737}
738
739pub 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
804pub 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
869pub trait OutgoingNoteInterface: Sized {
871 type ZcashNote;
873 type Address: Clone + Copy + Debug + PartialEq + Eq;
875 type Error: Debug + std::error::Error;
877
878 const SHIELDED_PROTOCOL: ShieldedProtocol;
880
881 fn output_id(&self) -> OutputId;
883
884 fn key_id(&self) -> KeyId;
886
887 fn value(&self) -> u64;
889
890 fn note(&self) -> &Self::ZcashNote;
892
893 fn memo(&self) -> &Memo;
895
896 fn recipient(&self) -> Self::Address;
898
899 fn recipient_full_unified_address(&self) -> Option<&UnifiedAddress>;
901
902 fn encoded_recipient<P>(&self, parameters: &P) -> Result<String, Self::Error>
904 where
905 P: consensus::Parameters + consensus::NetworkConstants;
906
907 fn encoded_recipient_full_unified_address<P>(&self, consensus_parameters: &P) -> Option<String>
909 where
910 P: consensus::Parameters + consensus::NetworkConstants;
911
912 fn transaction_outgoing_notes(transaction: &WalletTransaction) -> &[Self];
914}
915
916#[derive(Debug, Clone, PartialEq)]
918pub struct OutgoingNote<N> {
919 pub(crate) output_id: OutputId,
921 pub(crate) key_id: KeyId,
923 pub(crate) note: N,
925 pub(crate) memo: Memo,
927 pub(crate) recipient_full_unified_address: Option<UnifiedAddress>,
929}
930
931pub 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
993pub 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
1052pub type SaplingShardStore = MemoryShardStore<sapling_crypto::Node, BlockHeight>;
1055
1056pub type OrchardShardStore = MemoryShardStore<MerkleHashOrchard, BlockHeight>;
1058
1059#[derive(Debug)]
1061pub struct ShardTrees {
1062 pub sapling: ShardTree<
1064 SaplingShardStore,
1065 { sapling_crypto::NOTE_COMMITMENT_TREE_DEPTH },
1066 { witness::SHARD_HEIGHT },
1067 >,
1068 pub orchard: ShardTree<
1070 OrchardShardStore,
1071 { orchard::NOTE_COMMITMENT_TREE_DEPTH as u8 },
1072 { witness::SHARD_HEIGHT },
1073 >,
1074}
1075
1076impl ShardTrees {
1077 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}