Skip to content

Commit 62298aa

Browse files
dairanuttycomJack Grigg
committed
Work-in-progress implementation of ZIP 320.
Co-authored-by: Kris Nuttycombe <[email protected]> Co-authored-by: Jack Grigg <[email protected]> Signed-off-by: Daira-Emma Hopwood <[email protected]>
1 parent b192eec commit 62298aa

File tree

14 files changed

+729
-208
lines changed

14 files changed

+729
-208
lines changed

zcash_client_backend/proto/proposal.proto

+2
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,8 @@ enum FeeRule {
113113
// The proposed change outputs and fee value.
114114
message TransactionBalance {
115115
// A list of change output values.
116+
// Any `ChangeValue`s for the transparent value pool represent ephemeral
117+
// outputs that will each be given a unique t-address.
116118
repeated ChangeValue proposedChange = 1;
117119
// The fee to be paid by the proposed transaction, in zatoshis.
118120
uint64 feeRequired = 2;

zcash_client_backend/src/data_api.rs

+133-17
Original file line numberDiff line numberDiff line change
@@ -30,18 +30,19 @@
3030
//!
3131
//! ## Core Traits
3232
//!
33-
//! The utility functions described above depend upon four important traits defined in this
33+
//! The utility functions described above depend upon five important traits defined in this
3434
//! module, which between them encompass the data storage requirements of a light wallet.
35-
//! The relevant traits are [`InputSource`], [`WalletRead`], [`WalletWrite`], and
36-
//! [`WalletCommitmentTrees`]. A complete implementation of the data storage layer for a wallet
37-
//! will include an implementation of all four of these traits. See the [`zcash_client_sqlite`]
38-
//! crate for a complete example of the implementation of these traits.
35+
//! The relevant traits are [`InputSource`], [`WalletRead`], [`WalletWrite`],
36+
//! [`WalletCommitmentTrees`], and [`WalletAddressTracking`]. A complete implementation of the
37+
//! data storage layer for a wallet will include an implementation of all five of these traits.
38+
//! See the [`zcash_client_sqlite`] crate for a complete example of the implementation of these
39+
//! traits.
3940
//!
4041
//! ## Accounts
4142
//!
42-
//! The operation of the [`InputSource`], [`WalletRead`] and [`WalletWrite`] traits is built around
43-
//! the concept of a wallet having one or more accounts, with a unique `AccountId` for each
44-
//! account.
43+
//! The operation of the [`InputSource`], [`WalletRead`], [`WalletWrite`], and
44+
//! [`WalletAddressTracking`] traits is built around the concept of a wallet having one or more
45+
//! accounts, with a unique `AccountId` for each account.
4546
//!
4647
//! An account identifier corresponds to at most a single [`UnifiedSpendingKey`]'s worth of spend
4748
//! authority, with the received and spent notes of that account tracked via the corresponding
@@ -57,7 +58,7 @@
5758
5859
use std::{
5960
collections::HashMap,
60-
fmt::Debug,
61+
fmt::{self, Debug, Display},
6162
hash::Hash,
6263
io,
6364
num::{NonZeroU32, TryFromIntError},
@@ -85,6 +86,7 @@ use crate::{
8586
use zcash_primitives::{
8687
block::BlockHash,
8788
consensus::BlockHeight,
89+
legacy::TransparentAddress,
8890
memo::{Memo, MemoBytes},
8991
transaction::{
9092
components::amount::{BalanceError, NonNegativeAmount},
@@ -94,8 +96,7 @@ use zcash_primitives::{
9496

9597
#[cfg(feature = "transparent-inputs")]
9698
use {
97-
crate::wallet::TransparentAddressMetadata,
98-
zcash_primitives::{legacy::TransparentAddress, transaction::components::OutPoint},
99+
crate::wallet::TransparentAddressMetadata, zcash_primitives::transaction::components::OutPoint,
99100
};
100101

101102
#[cfg(feature = "test-dependencies")]
@@ -1512,8 +1513,11 @@ pub trait WalletWrite: WalletRead {
15121513
received_tx: DecryptedTransaction<Self::AccountId>,
15131514
) -> Result<(), Self::Error>;
15141515

1515-
/// Saves information about a transaction that was constructed and sent by the wallet to the
1516-
/// persistent wallet store.
1516+
/// Saves information about a transaction constructed by the wallet to the persistent
1517+
/// wallet store.
1518+
///
1519+
/// The name `store_sent_tx` is somewhat misleading; this must be called *before* the
1520+
/// transaction is sent to the network.
15171521
fn store_sent_tx(
15181522
&mut self,
15191523
sent_tx: &SentTransaction<Self::AccountId>,
@@ -1606,6 +1610,87 @@ pub trait WalletCommitmentTrees {
16061610
) -> Result<(), ShardTreeError<Self::Error>>;
16071611
}
16081612

1613+
/// An error related to tracking of ephemeral transparent addresses.
1614+
#[derive(Debug, Clone, PartialEq, Eq)]
1615+
pub enum AddressTrackingError {
1616+
/// Only ZIP 32 accounts are supported for ZIP 320 (TEX Addresses).
1617+
UnsupportedAccountType,
1618+
1619+
/// The proposal cannot be constructed until transactions with previously reserved
1620+
/// ephemeral address outputs have been mined.
1621+
ReachedGapLimit,
1622+
1623+
/// Internal error.
1624+
Internal(String),
1625+
}
1626+
1627+
impl Display for AddressTrackingError {
1628+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1629+
match self {
1630+
AddressTrackingError::UnsupportedAccountType => write!(
1631+
f,
1632+
"Only ZIP 32 accounts are supported for ZIP 320 (TEX Addresses)."
1633+
),
1634+
AddressTrackingError::ReachedGapLimit => write!(
1635+
f,
1636+
"The proposal cannot be constructed until transactions with previously reserved ephemeral address outputs have been mined."
1637+
),
1638+
AddressTrackingError::Internal(e) => write!(
1639+
f,
1640+
"Internal address tracking error: {}", e
1641+
),
1642+
}
1643+
}
1644+
}
1645+
1646+
pub trait WalletAddressTracking {
1647+
/// Reserves the next available address.
1648+
///
1649+
/// To ensure that sufficient information is stored on-chain to allow recovering
1650+
/// funds sent back to any of the used addresses, a "gap limit" of 20 addresses
1651+
/// should be observed as described in [BIP 44]. An implementation should record
1652+
/// the index of the first unmined address, and update it for addresses that have
1653+
/// been observed as outputs in mined transactions when `addresses_are_mined` is
1654+
/// called.
1655+
///
1656+
/// Returns an error if all addresses within the gap limit have already been
1657+
/// reserved.
1658+
///
1659+
/// [BIP 44]: https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki#user-content-Address_gap_limit
1660+
fn reserve_next_address(
1661+
&self,
1662+
account: zip32::AccountId,
1663+
) -> Result<TransparentAddress, AddressTrackingError>;
1664+
1665+
/// Frees previously reserved ephemeral transparent addresses.
1666+
///
1667+
/// This should only be used in the case that an error occurs in transaction
1668+
/// construction after the address was reserved. It is sufficient for an
1669+
/// implementation to only be able to unreserve the last reserved address from
1670+
/// the given account.
1671+
///
1672+
/// Returns an error if the account identifier does not correspond to a known
1673+
/// account.
1674+
fn unreserve_addresses(
1675+
&self,
1676+
address: &[TransparentAddress],
1677+
) -> Result<(), AddressTrackingError>;
1678+
1679+
/// Mark addresses as having been used.
1680+
fn mark_addresses_as_used(
1681+
&self,
1682+
address: &[TransparentAddress],
1683+
) -> Result<(), AddressTrackingError>;
1684+
1685+
/// Checks the set of ephemeral transparent addresses within the gap limit for the
1686+
/// given mined t-addresses, in order to update the first unmined ephemeral t-address
1687+
/// index if necessary.
1688+
fn mark_addresses_as_mined(
1689+
&self,
1690+
addresses: &[TransparentAddress],
1691+
) -> Result<(), AddressTrackingError>;
1692+
}
1693+
16091694
#[cfg(feature = "test-dependencies")]
16101695
pub mod testing {
16111696
use incrementalmerkletree::Address;
@@ -1617,6 +1702,7 @@ pub mod testing {
16171702
use zcash_primitives::{
16181703
block::BlockHash,
16191704
consensus::{BlockHeight, Network},
1705+
legacy::TransparentAddress,
16201706
memo::Memo,
16211707
transaction::{components::amount::NonNegativeAmount, Transaction, TxId},
16221708
};
@@ -1631,13 +1717,13 @@ pub mod testing {
16311717
use super::{
16321718
chain::{ChainState, CommitmentTreeRoot},
16331719
scanning::ScanRange,
1634-
AccountBirthday, BlockMetadata, DecryptedTransaction, InputSource, NullifierQuery,
1635-
ScannedBlock, SentTransaction, SpendableNotes, WalletCommitmentTrees, WalletRead,
1636-
WalletSummary, WalletWrite, SAPLING_SHARD_HEIGHT,
1720+
AccountBirthday, AddressTrackingError, BlockMetadata, DecryptedTransaction, InputSource,
1721+
NullifierQuery, ScannedBlock, SentTransaction, SpendableNotes, WalletAddressTracking,
1722+
WalletCommitmentTrees, WalletRead, WalletSummary, WalletWrite, SAPLING_SHARD_HEIGHT,
16371723
};
16381724

16391725
#[cfg(feature = "transparent-inputs")]
1640-
use {crate::wallet::TransparentAddressMetadata, zcash_primitives::legacy::TransparentAddress};
1726+
use crate::wallet::TransparentAddressMetadata;
16411727

16421728
#[cfg(feature = "orchard")]
16431729
use super::ORCHARD_SHARD_HEIGHT;
@@ -1924,6 +2010,36 @@ pub mod testing {
19242010
}
19252011
}
19262012

2013+
impl WalletAddressTracking for MockWalletDb {
2014+
fn reserve_next_address(
2015+
&self,
2016+
_account: zip32::AccountId,
2017+
) -> Result<TransparentAddress, AddressTrackingError> {
2018+
Err(AddressTrackingError::ReachedGapLimit)
2019+
}
2020+
2021+
fn unreserve_addresses(
2022+
&self,
2023+
_addresses: &[TransparentAddress],
2024+
) -> Result<(), AddressTrackingError> {
2025+
Ok(())
2026+
}
2027+
2028+
fn mark_addresses_as_used(
2029+
&self,
2030+
_addresses: &[TransparentAddress],
2031+
) -> Result<(), AddressTrackingError> {
2032+
Ok(())
2033+
}
2034+
2035+
fn mark_addresses_as_mined(
2036+
&self,
2037+
_addresses: &[TransparentAddress],
2038+
) -> Result<(), AddressTrackingError> {
2039+
Ok(())
2040+
}
2041+
}
2042+
19272043
impl WalletCommitmentTrees for MockWalletDb {
19282044
type Error = Infallible;
19292045
type SaplingShardStore<'a> = MemoryShardStore<sapling::Node, BlockHeight>;

zcash_client_backend/src/data_api/error.rs

+9
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ use zcash_primitives::legacy::TransparentAddress;
2020

2121
use crate::wallet::NoteId;
2222

23+
use super::AddressTrackingError;
24+
2325
/// Errors that can occur as a consequence of wallet operations.
2426
#[derive(Debug)]
2527
pub enum Error<DataSourceError, CommitmentTreeError, SelectionError, FeeError> {
@@ -83,6 +85,9 @@ pub enum Error<DataSourceError, CommitmentTreeError, SelectionError, FeeError> {
8385

8486
#[cfg(feature = "transparent-inputs")]
8587
AddressNotRecognized(TransparentAddress),
88+
89+
/// An error related to tracking of ephemeral transparent addresses.
90+
AddressTracking(AddressTrackingError),
8691
}
8792

8893
impl<DE, CE, SE, FE> fmt::Display for Error<DE, CE, SE, FE>
@@ -149,6 +154,9 @@ where
149154
Error::AddressNotRecognized(_) => {
150155
write!(f, "The specified transparent address was not recognized as belonging to the wallet.")
151156
}
157+
Error::AddressTracking(e) => {
158+
write!(f, "Error related to tracking of ephemeral transparent addresses: {}", e)
159+
}
152160
}
153161
}
154162
}
@@ -198,6 +206,7 @@ impl<DE, CE, SE, FE> From<InputSelectorError<DE, SE>> for Error<DE, CE, SE, FE>
198206
required,
199207
},
200208
InputSelectorError::SyncRequired => Error::ScanRequired,
209+
InputSelectorError::AddressTracking(e) => Error::AddressTracking(e),
201210
}
202211
}
203212
}

0 commit comments

Comments
 (0)