use std::{
collections::{BTreeMap, BTreeSet},
convert::Infallible,
fmt::Debug,
ops::Range,
sync::{
Arc,
atomic::{self, AtomicU8},
},
};
use incrementalmerkletree::Position;
use orchard::tree::MerkleHashOrchard;
use shardtree::{ShardTree, store::memory::MemoryShardStore};
use tokio::sync::mpsc;
use zcash_address::unified::ParseError;
use zcash_client_backend::proto::compact_formats::CompactBlock;
use zcash_keys::{address::UnifiedAddress, encoding::encode_payment_address};
use zcash_primitives::{
block::BlockHash,
memo::Memo,
transaction::{TxId, components::transparent::OutPoint},
};
use zcash_protocol::{
PoolType, ShieldedProtocol,
consensus::{self, BlockHeight},
value::Zatoshis,
};
use zcash_transparent::address::Script;
use zingo_status::confirmation_status::ConfirmationStatus;
use crate::{
client::FetchRequest,
error::{ServerError, SyncModeError},
keys::{self, KeyId, transparent::TransparentAddressId},
scan::compact_blocks::calculate_block_tree_bounds,
sync::{MAX_REORG_ALLOWANCE, ScanPriority, ScanRange},
witness,
};
pub mod traits;
#[cfg(feature = "wallet_essentials")]
pub mod serialization;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub struct ScanTarget {
pub block_height: BlockHeight,
pub txid: TxId,
pub narrow_scan_area: bool,
}
#[derive(Debug, Clone)]
pub struct InitialSyncState {
pub(crate) sync_start_height: BlockHeight,
pub(crate) wallet_tree_bounds: TreeBounds,
pub(crate) previously_scanned_blocks: u32,
pub(crate) previously_scanned_sapling_outputs: u32,
pub(crate) previously_scanned_orchard_outputs: u32,
}
impl InitialSyncState {
#[must_use]
pub fn new() -> Self {
InitialSyncState {
sync_start_height: 0.into(),
wallet_tree_bounds: TreeBounds {
sapling_initial_tree_size: 0,
sapling_final_tree_size: 0,
orchard_initial_tree_size: 0,
orchard_final_tree_size: 0,
},
previously_scanned_blocks: 0,
previously_scanned_sapling_outputs: 0,
previously_scanned_orchard_outputs: 0,
}
}
}
impl Default for InitialSyncState {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct SyncState {
pub(crate) scan_ranges: Vec<ScanRange>,
pub(crate) sapling_shard_ranges: Vec<Range<BlockHeight>>,
pub(crate) orchard_shard_ranges: Vec<Range<BlockHeight>>,
pub(crate) scan_targets: BTreeSet<ScanTarget>,
pub(crate) initial_sync_state: InitialSyncState,
}
impl SyncState {
#[must_use]
pub fn new() -> Self {
SyncState {
scan_ranges: Vec::new(),
sapling_shard_ranges: Vec::new(),
orchard_shard_ranges: Vec::new(),
scan_targets: BTreeSet::new(),
initial_sync_state: InitialSyncState::new(),
}
}
#[must_use]
pub fn scan_ranges(&self) -> &[ScanRange] {
&self.scan_ranges
}
#[must_use]
pub fn sapling_shard_ranges(&self) -> &[Range<BlockHeight>] {
&self.sapling_shard_ranges
}
#[must_use]
pub fn orchard_shard_ranges(&self) -> &[Range<BlockHeight>] {
&self.orchard_shard_ranges
}
pub(crate) fn scan_complete(&self) -> bool {
self.scan_ranges
.iter()
.all(|scan_range| scan_range.priority() == ScanPriority::Scanned)
}
#[must_use]
pub fn fully_scanned_height(&self) -> Option<BlockHeight> {
if let Some(scan_range) = self
.scan_ranges
.iter()
.find(|scan_range| scan_range.priority() != ScanPriority::Scanned)
{
Some(scan_range.block_range().start - 1)
} else {
self.scan_ranges
.last()
.map(|range| range.block_range().end - 1)
}
}
#[must_use]
pub fn highest_scanned_height(&self) -> Option<BlockHeight> {
if let Some(last_scanned_range) = self
.scan_ranges
.iter()
.filter(|scan_range| {
scan_range.priority() == ScanPriority::Scanned
|| scan_range.priority() == ScanPriority::ScannedWithoutMapping
|| scan_range.priority() == ScanPriority::RefetchingNullifiers
})
.next_back()
{
Some(last_scanned_range.block_range().end - 1)
} else {
self.wallet_birthday().map(|start| start - 1)
}
}
#[must_use]
pub fn wallet_birthday(&self) -> Option<BlockHeight> {
self.scan_ranges
.first()
.map(|range| range.block_range().start)
}
#[must_use]
pub fn last_known_chain_height(&self) -> Option<BlockHeight> {
self.scan_ranges
.last()
.map(|range| range.block_range().end - 1)
}
}
impl Default for SyncState {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SyncMode {
NotRunning,
Paused,
Running,
Shutdown,
}
impl SyncMode {
pub fn from_u8(mode: u8) -> Result<Self, SyncModeError> {
match mode {
0 => Ok(Self::NotRunning),
1 => Ok(Self::Paused),
2 => Ok(Self::Running),
3 => Ok(Self::Shutdown),
_ => Err(SyncModeError::InvalidSyncMode(mode)),
}
}
pub fn from_atomic_u8(atomic_sync_mode: Arc<AtomicU8>) -> Result<SyncMode, SyncModeError> {
SyncMode::from_u8(atomic_sync_mode.load(atomic::Ordering::Acquire))
}
}
#[derive(Debug, Clone, Copy)]
#[allow(missing_docs)]
pub struct TreeBounds {
pub sapling_initial_tree_size: u32,
pub sapling_final_tree_size: u32,
pub orchard_initial_tree_size: u32,
pub orchard_final_tree_size: u32,
}
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)]
pub struct OutputId {
txid: TxId,
output_index: u16,
}
impl OutputId {
#[must_use]
pub fn new(txid: TxId, output_index: u16) -> Self {
OutputId { txid, output_index }
}
#[must_use]
pub fn txid(&self) -> TxId {
self.txid
}
#[must_use]
pub fn output_index(&self) -> u16 {
self.output_index
}
}
impl std::fmt::Display for OutputId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{{
txid: {}
output index: {}
}}",
self.txid, self.output_index
)
}
}
impl From<&OutPoint> for OutputId {
fn from(value: &OutPoint) -> Self {
OutputId::new(*value.txid(), value.n() as u16)
}
}
impl From<OutputId> for OutPoint {
fn from(value: OutputId) -> Self {
OutPoint::new(value.txid.into(), u32::from(value.output_index))
}
}
#[derive(Debug)]
pub struct NullifierMap {
pub sapling: BTreeMap<sapling_crypto::Nullifier, ScanTarget>,
pub orchard: BTreeMap<orchard::note::Nullifier, ScanTarget>,
}
impl NullifierMap {
#[must_use]
pub fn new() -> Self {
Self {
sapling: BTreeMap::new(),
orchard: BTreeMap::new(),
}
}
pub fn clear(&mut self) {
self.sapling.clear();
self.orchard.clear();
}
}
impl Default for NullifierMap {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct WalletBlock {
pub(crate) block_height: BlockHeight,
pub(crate) block_hash: BlockHash,
pub(crate) prev_hash: BlockHash,
pub(crate) time: u32,
pub(crate) txids: Vec<TxId>,
pub(crate) tree_bounds: TreeBounds,
}
impl WalletBlock {
pub(crate) async fn from_compact_block(
consensus_parameters: &impl consensus::Parameters,
fetch_request_sender: mpsc::UnboundedSender<FetchRequest>,
block: &CompactBlock,
) -> Result<Self, ServerError> {
let tree_bounds =
calculate_block_tree_bounds(consensus_parameters, fetch_request_sender, block).await?;
Ok(Self {
block_height: block.height(),
block_hash: block.hash(),
prev_hash: block.prev_hash(),
time: block.time,
txids: block
.vtx
.iter()
.map(zcash_client_backend::proto::compact_formats::CompactTx::txid)
.collect(),
tree_bounds,
})
}
#[must_use]
pub fn block_height(&self) -> BlockHeight {
self.block_height
}
#[must_use]
pub fn block_hash(&self) -> BlockHash {
self.block_hash
}
#[must_use]
pub fn prev_hash(&self) -> BlockHash {
self.prev_hash
}
#[must_use]
pub fn time(&self) -> u32 {
self.time
}
#[must_use]
pub fn txids(&self) -> &[TxId] {
&self.txids
}
#[must_use]
pub fn tree_bounds(&self) -> TreeBounds {
self.tree_bounds
}
}
pub struct WalletTransaction {
pub(crate) txid: TxId,
pub(crate) status: ConfirmationStatus,
pub(crate) transaction: zcash_primitives::transaction::Transaction,
pub(crate) datetime: u32,
pub(crate) transparent_coins: Vec<TransparentCoin>,
pub(crate) sapling_notes: Vec<SaplingNote>,
pub(crate) orchard_notes: Vec<OrchardNote>,
pub(crate) outgoing_sapling_notes: Vec<OutgoingSaplingNote>,
pub(crate) outgoing_orchard_notes: Vec<OutgoingOrchardNote>,
}
impl WalletTransaction {
#[must_use]
pub fn txid(&self) -> TxId {
self.txid
}
#[must_use]
pub fn status(&self) -> ConfirmationStatus {
self.status
}
#[must_use]
pub fn transaction(&self) -> &zcash_primitives::transaction::Transaction {
&self.transaction
}
#[must_use]
pub fn datetime(&self) -> u32 {
self.datetime
}
#[must_use]
pub fn transparent_coins(&self) -> &[TransparentCoin] {
&self.transparent_coins
}
pub fn transparent_coins_mut(&mut self) -> Vec<&mut TransparentCoin> {
self.transparent_coins.iter_mut().collect()
}
#[must_use]
pub fn sapling_notes(&self) -> &[SaplingNote] {
&self.sapling_notes
}
pub fn sapling_notes_mut(&mut self) -> Vec<&mut SaplingNote> {
self.sapling_notes.iter_mut().collect()
}
#[must_use]
pub fn orchard_notes(&self) -> &[OrchardNote] {
&self.orchard_notes
}
pub fn orchard_notes_mut(&mut self) -> Vec<&mut OrchardNote> {
self.orchard_notes.iter_mut().collect()
}
#[must_use]
pub fn outgoing_sapling_notes(&self) -> &[OutgoingSaplingNote] {
&self.outgoing_sapling_notes
}
#[must_use]
pub fn outgoing_orchard_notes(&self) -> &[OutgoingOrchardNote] {
&self.outgoing_orchard_notes
}
pub fn sapling_nullifiers(&self) -> Vec<&sapling_crypto::Nullifier> {
self.transaction
.sapling_bundle()
.map_or_else(Vec::new, |bundle| {
bundle
.shielded_spends()
.iter()
.map(sapling_crypto::bundle::SpendDescription::nullifier)
.collect::<Vec<_>>()
})
}
pub fn orchard_nullifiers(&self) -> Vec<&orchard::note::Nullifier> {
self.transaction
.orchard_bundle()
.map_or_else(Vec::new, |bundle| {
bundle
.actions()
.iter()
.map(orchard::Action::nullifier)
.collect::<Vec<_>>()
})
}
pub fn outpoints(&self) -> Vec<&OutPoint> {
self.transaction
.transparent_bundle()
.map_or_else(Vec::new, |bundle| {
bundle
.vin
.iter()
.map(zcash_transparent::bundle::TxIn::prevout)
.collect::<Vec<_>>()
})
}
pub fn update_status(&mut self, status: ConfirmationStatus, datetime: u32) {
match status {
ConfirmationStatus::Transmitted(_)
if matches!(self.status(), ConfirmationStatus::Calculated(_)) =>
{
self.status = status;
self.datetime = datetime;
}
ConfirmationStatus::Mempool(_)
if matches!(
self.status(),
ConfirmationStatus::Calculated(_) | ConfirmationStatus::Transmitted(_)
) =>
{
self.status = status;
self.datetime = datetime;
}
ConfirmationStatus::Confirmed(_)
if matches!(
self.status(),
ConfirmationStatus::Calculated(_)
| ConfirmationStatus::Transmitted(_)
| ConfirmationStatus::Mempool(_)
) =>
{
self.status = status;
self.datetime = datetime;
}
ConfirmationStatus::Failed(_)
if !matches!(self.status(), ConfirmationStatus::Failed(_)) =>
{
self.status = status;
self.datetime = datetime;
}
_ => (),
}
}
}
#[cfg(feature = "test-features")]
impl WalletTransaction {
pub fn new_for_test(txid: TxId, status: ConfirmationStatus) -> Self {
use zcash_primitives::transaction::{TransactionData, TxVersion};
use zcash_protocol::consensus::BranchId;
let transaction = TransactionData::from_parts(
TxVersion::V5,
BranchId::Nu5,
0,
BlockHeight::from_u32(0),
None,
None,
None,
None,
)
.freeze()
.expect("empty v5 transaction should always be valid");
Self {
txid,
status,
transaction,
datetime: 0,
transparent_coins: Vec::new(),
sapling_notes: Vec::new(),
orchard_notes: Vec::new(),
outgoing_sapling_notes: Vec::new(),
outgoing_orchard_notes: Vec::new(),
}
}
}
#[cfg(feature = "wallet_essentials")]
impl WalletTransaction {
#[must_use]
pub fn total_value_sent(&self) -> u64 {
let transparent_value_sent = self
.transaction
.transparent_bundle()
.map_or(0, |bundle| {
bundle
.vout
.iter()
.map(|output| output.value().into_u64())
.sum()
})
.saturating_sub(self.total_output_value::<TransparentCoin>());
let sapling_value_sent = self
.total_outgoing_note_value::<OutgoingSaplingNote>()
.saturating_sub(self.total_output_value::<SaplingNote>());
let orchard_value_sent = self
.total_outgoing_note_value::<OutgoingOrchardNote>()
.saturating_sub(self.total_output_value::<OrchardNote>());
transparent_value_sent + sapling_value_sent + orchard_value_sent
}
#[must_use]
pub fn total_value_received(&self) -> u64 {
self.total_output_value::<TransparentCoin>()
+ self.total_output_value::<SaplingNote>()
+ self.total_output_value::<OrchardNote>()
}
#[must_use]
pub fn total_output_value<Op: OutputInterface>(&self) -> u64 {
Op::transaction_outputs(self)
.iter()
.map(OutputInterface::value)
.sum()
}
#[must_use]
pub fn total_outgoing_note_value<Op: OutgoingNoteInterface>(&self) -> u64 {
Op::transaction_outgoing_notes(self)
.iter()
.map(OutgoingNoteInterface::value)
.sum()
}
}
impl std::fmt::Debug for WalletTransaction {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
f.debug_struct("WalletTransaction")
.field("txid", &self.txid)
.field("confirmation_status", &self.status)
.field("datetime", &self.datetime)
.field("transparent_coins", &self.transparent_coins)
.field("sapling_notes", &self.sapling_notes)
.field("orchard_notes", &self.orchard_notes)
.field("outgoing_sapling_notes", &self.outgoing_sapling_notes)
.field("outgoing_orchard_notes", &self.outgoing_orchard_notes)
.finish()
}
}
pub trait KeyIdInterface {
fn account_id(&self) -> zip32::AccountId;
}
pub trait OutputInterface: Sized {
type KeyId: KeyIdInterface;
type Input: Clone + Debug + PartialEq + Eq + PartialOrd + Ord;
const POOL_TYPE: PoolType;
fn output_id(&self) -> OutputId;
fn key_id(&self) -> Self::KeyId;
fn spending_transaction(&self) -> Option<TxId>;
fn set_spending_transaction(&mut self, spending_transaction: Option<TxId>);
fn value(&self) -> u64;
fn spend_link(&self) -> Option<Self::Input>;
fn transaction_inputs(transaction: &WalletTransaction) -> Vec<&Self::Input>;
fn transaction_outputs(transaction: &WalletTransaction) -> &[Self];
}
pub trait NoteInterface: OutputInterface {
type ZcashNote;
type Nullifier: Copy;
const SHIELDED_PROTOCOL: ShieldedProtocol;
fn note(&self) -> &Self::ZcashNote;
fn nullifier(&self) -> Option<Self::Nullifier>;
fn position(&self) -> Option<Position>;
fn memo(&self) -> &Memo;
fn refetch_nullifier_ranges(&self) -> &[Range<BlockHeight>];
}
#[derive(Debug, Clone)]
pub struct TransparentCoin {
pub(crate) output_id: OutputId,
pub(crate) key_id: TransparentAddressId,
pub(crate) address: String,
pub(crate) script: Script,
pub(crate) value: Zatoshis,
pub(crate) spending_transaction: Option<TxId>,
}
impl TransparentCoin {
#[must_use]
pub fn address(&self) -> &str {
&self.address
}
#[must_use]
pub fn script(&self) -> &Script {
&self.script
}
}
impl OutputInterface for TransparentCoin {
type KeyId = TransparentAddressId;
type Input = OutPoint;
const POOL_TYPE: PoolType = PoolType::Transparent;
fn output_id(&self) -> OutputId {
self.output_id
}
fn key_id(&self) -> Self::KeyId {
self.key_id
}
fn spending_transaction(&self) -> Option<TxId> {
self.spending_transaction
}
fn set_spending_transaction(&mut self, spending_transaction: Option<TxId>) {
self.spending_transaction = spending_transaction;
}
fn value(&self) -> u64 {
self.value.into_u64()
}
fn spend_link(&self) -> Option<Self::Input> {
Some(self.output_id.into())
}
fn transaction_inputs(transaction: &WalletTransaction) -> Vec<&Self::Input> {
transaction.outpoints()
}
fn transaction_outputs(transaction: &WalletTransaction) -> &[Self] {
&transaction.transparent_coins
}
}
#[derive(Debug, Clone)]
pub struct WalletNote<N, Nf: Copy> {
pub(crate) output_id: OutputId,
pub(crate) key_id: KeyId,
pub(crate) note: N,
pub(crate) nullifier: Option<Nf>, pub(crate) position: Option<Position>,
pub(crate) memo: Memo,
pub(crate) spending_transaction: Option<TxId>,
pub(crate) refetch_nullifier_ranges: Vec<Range<BlockHeight>>,
}
pub type SaplingNote = WalletNote<sapling_crypto::Note, sapling_crypto::Nullifier>;
impl OutputInterface for SaplingNote {
type KeyId = KeyId;
type Input = sapling_crypto::Nullifier;
const POOL_TYPE: PoolType = PoolType::Shielded(ShieldedProtocol::Sapling);
fn output_id(&self) -> OutputId {
self.output_id
}
fn key_id(&self) -> KeyId {
self.key_id
}
fn spending_transaction(&self) -> Option<TxId> {
self.spending_transaction
}
fn set_spending_transaction(&mut self, spending_transaction: Option<TxId>) {
self.spending_transaction = spending_transaction;
}
fn value(&self) -> u64 {
self.note.value().inner()
}
fn spend_link(&self) -> Option<Self::Input> {
self.nullifier
}
fn transaction_inputs(transaction: &WalletTransaction) -> Vec<&Self::Input> {
transaction.sapling_nullifiers()
}
fn transaction_outputs(transaction: &WalletTransaction) -> &[Self] {
&transaction.sapling_notes
}
}
impl NoteInterface for SaplingNote {
type ZcashNote = sapling_crypto::Note;
type Nullifier = Self::Input;
const SHIELDED_PROTOCOL: ShieldedProtocol = ShieldedProtocol::Sapling;
fn note(&self) -> &Self::ZcashNote {
&self.note
}
fn nullifier(&self) -> Option<Self::Nullifier> {
self.nullifier
}
fn position(&self) -> Option<Position> {
self.position
}
fn memo(&self) -> &Memo {
&self.memo
}
fn refetch_nullifier_ranges(&self) -> &[Range<BlockHeight>] {
&self.refetch_nullifier_ranges
}
}
pub type OrchardNote = WalletNote<orchard::Note, orchard::note::Nullifier>;
impl OutputInterface for OrchardNote {
type KeyId = KeyId;
type Input = orchard::note::Nullifier;
const POOL_TYPE: PoolType = PoolType::Shielded(ShieldedProtocol::Orchard);
fn output_id(&self) -> OutputId {
self.output_id
}
fn key_id(&self) -> KeyId {
self.key_id
}
fn spending_transaction(&self) -> Option<TxId> {
self.spending_transaction
}
fn set_spending_transaction(&mut self, spending_transaction: Option<TxId>) {
self.spending_transaction = spending_transaction;
}
fn value(&self) -> u64 {
self.note.value().inner()
}
fn spend_link(&self) -> Option<Self::Input> {
self.nullifier
}
fn transaction_inputs(transaction: &WalletTransaction) -> Vec<&Self::Input> {
transaction.orchard_nullifiers()
}
fn transaction_outputs(transaction: &WalletTransaction) -> &[Self] {
&transaction.orchard_notes
}
}
impl NoteInterface for OrchardNote {
type ZcashNote = orchard::Note;
type Nullifier = Self::Input;
const SHIELDED_PROTOCOL: ShieldedProtocol = ShieldedProtocol::Orchard;
fn note(&self) -> &Self::ZcashNote {
&self.note
}
fn nullifier(&self) -> Option<Self::Nullifier> {
self.spend_link()
}
fn position(&self) -> Option<Position> {
self.position
}
fn memo(&self) -> &Memo {
&self.memo
}
fn refetch_nullifier_ranges(&self) -> &[Range<BlockHeight>] {
&self.refetch_nullifier_ranges
}
}
pub trait OutgoingNoteInterface: Sized {
type ZcashNote;
type Address: Clone + Copy + Debug + PartialEq + Eq;
type Error: Debug + std::error::Error;
const SHIELDED_PROTOCOL: ShieldedProtocol;
fn output_id(&self) -> OutputId;
fn key_id(&self) -> KeyId;
fn value(&self) -> u64;
fn note(&self) -> &Self::ZcashNote;
fn memo(&self) -> &Memo;
fn recipient(&self) -> Self::Address;
fn recipient_full_unified_address(&self) -> Option<&UnifiedAddress>;
fn encoded_recipient<P>(&self, parameters: &P) -> Result<String, Self::Error>
where
P: consensus::Parameters + consensus::NetworkConstants;
fn encoded_recipient_full_unified_address<P>(&self, consensus_parameters: &P) -> Option<String>
where
P: consensus::Parameters + consensus::NetworkConstants;
fn transaction_outgoing_notes(transaction: &WalletTransaction) -> &[Self];
}
#[derive(Debug, Clone, PartialEq)]
pub struct OutgoingNote<N> {
pub(crate) output_id: OutputId,
pub(crate) key_id: KeyId,
pub(crate) note: N,
pub(crate) memo: Memo,
pub(crate) recipient_full_unified_address: Option<UnifiedAddress>,
}
pub type OutgoingSaplingNote = OutgoingNote<sapling_crypto::Note>;
impl OutgoingNoteInterface for OutgoingSaplingNote {
type ZcashNote = sapling_crypto::Note;
type Address = sapling_crypto::PaymentAddress;
type Error = Infallible;
const SHIELDED_PROTOCOL: ShieldedProtocol = ShieldedProtocol::Sapling;
fn output_id(&self) -> OutputId {
self.output_id
}
fn key_id(&self) -> KeyId {
self.key_id
}
fn value(&self) -> u64 {
self.note.value().inner()
}
fn note(&self) -> &Self::ZcashNote {
&self.note
}
fn memo(&self) -> &Memo {
&self.memo
}
fn recipient(&self) -> Self::Address {
self.note.recipient()
}
fn recipient_full_unified_address(&self) -> Option<&UnifiedAddress> {
self.recipient_full_unified_address.as_ref()
}
fn encoded_recipient<P>(&self, consensus_parameters: &P) -> Result<String, Self::Error>
where
P: consensus::Parameters + consensus::NetworkConstants,
{
Ok(encode_payment_address(
consensus_parameters.hrp_sapling_payment_address(),
&self.note().recipient(),
))
}
fn encoded_recipient_full_unified_address<P>(&self, consensus_parameters: &P) -> Option<String>
where
P: consensus::Parameters + consensus::NetworkConstants,
{
self.recipient_full_unified_address
.as_ref()
.map(|unified_address| unified_address.encode(consensus_parameters))
}
fn transaction_outgoing_notes(transaction: &WalletTransaction) -> &[Self] {
&transaction.outgoing_sapling_notes
}
}
pub type OutgoingOrchardNote = OutgoingNote<orchard::Note>;
impl OutgoingNoteInterface for OutgoingOrchardNote {
type ZcashNote = orchard::Note;
type Address = orchard::Address;
type Error = ParseError;
const SHIELDED_PROTOCOL: ShieldedProtocol = ShieldedProtocol::Orchard;
fn output_id(&self) -> OutputId {
self.output_id
}
fn key_id(&self) -> KeyId {
self.key_id
}
fn value(&self) -> u64 {
self.note.value().inner()
}
fn note(&self) -> &Self::ZcashNote {
&self.note
}
fn memo(&self) -> &Memo {
&self.memo
}
fn recipient(&self) -> Self::Address {
self.note.recipient()
}
fn recipient_full_unified_address(&self) -> Option<&UnifiedAddress> {
self.recipient_full_unified_address.as_ref()
}
fn encoded_recipient<P>(&self, parameters: &P) -> Result<String, Self::Error>
where
P: consensus::Parameters + consensus::NetworkConstants,
{
keys::encode_orchard_receiver(parameters, &self.note().recipient())
}
fn encoded_recipient_full_unified_address<P>(&self, consensus_parameters: &P) -> Option<String>
where
P: consensus::Parameters + consensus::NetworkConstants,
{
self.recipient_full_unified_address
.as_ref()
.map(|unified_address| unified_address.encode(consensus_parameters))
}
fn transaction_outgoing_notes(transaction: &WalletTransaction) -> &[Self] {
&transaction.outgoing_orchard_notes
}
}
pub type SaplingShardStore = MemoryShardStore<sapling_crypto::Node, BlockHeight>;
pub type OrchardShardStore = MemoryShardStore<MerkleHashOrchard, BlockHeight>;
#[derive(Debug)]
pub struct ShardTrees {
pub sapling: ShardTree<
SaplingShardStore,
{ sapling_crypto::NOTE_COMMITMENT_TREE_DEPTH },
{ witness::SHARD_HEIGHT },
>,
pub orchard: ShardTree<
OrchardShardStore,
{ orchard::NOTE_COMMITMENT_TREE_DEPTH as u8 },
{ witness::SHARD_HEIGHT },
>,
}
impl ShardTrees {
#[must_use]
pub fn new() -> Self {
let mut sapling = ShardTree::new(MemoryShardStore::empty(), MAX_REORG_ALLOWANCE as usize);
let mut orchard = ShardTree::new(MemoryShardStore::empty(), MAX_REORG_ALLOWANCE as usize);
sapling
.checkpoint(BlockHeight::from_u32(0))
.expect("should never fail");
orchard
.checkpoint(BlockHeight::from_u32(0))
.expect("should never fail");
Self { sapling, orchard }
}
}
impl Default for ShardTrees {
fn default() -> Self {
Self::new()
}
}