Skip to content

Commit

Permalink
ZIP 320 implementation work-in-progress.
Browse files Browse the repository at this point in the history
Co-authored-by: Kris Nuttycombe <[email protected]>
Co-authored-by: Jack Grigg <[email protected]>
Signed-off-by: Daira-Emma Hopwood <[email protected]>
  • Loading branch information
3 people committed Jun 4, 2024
1 parent 79f6b02 commit 964ef81
Show file tree
Hide file tree
Showing 19 changed files with 989 additions and 237 deletions.
21 changes: 20 additions & 1 deletion zcash_client_backend/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,23 @@ and this library adheres to Rust's notion of
[Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]
### Notable changes
`zcash_client_backend` now supports TEX (transparent-source-only) addresses as specified
in ZIP 320. Sending to one or more TEX addresses will automatically create a multi-step
proposal that uses two transactions. This is intended to be used in conjunction with
`zcash_client_sqlite` 0.11 or later.

In order to take advantage of this support, client wallets will need to be able to send
multiple transactions created from `zcash_client_backend::data_api::wallet::create_proposed_transactions`.
This API was added in `zcash_client_backend` 0.11.0 but previously could only return a
single transaction.

**Note:** This feature changes the use of transparent addresses in ways that are relevant
to security and access to funds, and that may interact with other wallet behaviour. In
particular it exposes new ephemeral transparent addresses belonging to the wallet, which
need to be scanned in order to recover funds if the first transaction of the proposal is
mined but the second is not, or if someone (e.g. the TEX-address recipient) sends back
funds to those addresses. See [ZIP 320](https://zips.z.cash/zip-0320) for details.

### Added
- `zcash_client_backend::data_api`:
Expand Down Expand Up @@ -33,7 +50,9 @@ and this library adheres to Rust's notion of
- `zcash_client_backend::input_selection::GreedyInputSelectorError` has a
new variant `UnsupportedTexAddress`.
- `zcash_client_backend::wallet`:
- `Recipient` variants have changed. Instead of wrapping protocol-address
- `Recipient` variants have changed. It has a new `EphemeralTransparent`
variant, and an extra generic parameter giving the type of metadata about
an ephemeral transparent outpoint. Instead of wrapping protocol-address
types, the `External` and `InternalAccount` variants now wrap a
`zcash_address::ZcashAddress`. This simplifies the process of tracking
the original address to which value was sent.
Expand Down
2 changes: 2 additions & 0 deletions zcash_client_backend/proto/proposal.proto
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,8 @@ enum FeeRule {
// The proposed change outputs and fee value.
message TransactionBalance {
// A list of change output values.
// Any `ChangeValue`s for the transparent value pool represent ephemeral
// outputs that will each be given a unique t-address.
repeated ChangeValue proposedChange = 1;
// The fee to be paid by the proposed transaction, in zatoshis.
uint64 feeRequired = 2;
Expand Down
73 changes: 62 additions & 11 deletions zcash_client_backend/src/data_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,16 +88,16 @@ use zcash_primitives::{
consensus::BlockHeight,
memo::{Memo, MemoBytes},
transaction::{
components::amount::{BalanceError, NonNegativeAmount},
components::{
amount::{BalanceError, NonNegativeAmount},
OutPoint,
},
Transaction, TxId,
},
};

#[cfg(feature = "transparent-inputs")]
use {
crate::wallet::TransparentAddressMetadata,
zcash_primitives::{legacy::TransparentAddress, transaction::components::OutPoint},
};
use {crate::wallet::TransparentAddressMetadata, zcash_primitives::legacy::TransparentAddress};

#[cfg(any(test, feature = "test-dependencies"))]
use zcash_primitives::consensus::NetworkUpgrade;
Expand Down Expand Up @@ -909,6 +909,16 @@ pub trait WalletRead {
) -> Result<HashMap<TransparentAddress, NonNegativeAmount>, Self::Error> {
Ok(HashMap::new())
}

/// Returns the set of reserved ephemeral addresses controlled by this wallet.
#[cfg(feature = "transparent-inputs")]
fn get_reserved_ephemeral_addresses(
&self,
_account: Self::AccountId,
_for_sync: bool,
) -> Result<Vec<TransparentAddress>, Self::Error> {
Ok(vec![])
}
}

/// The relevance of a seed to a given wallet.
Expand Down Expand Up @@ -1239,7 +1249,7 @@ impl<'a, AccountId> SentTransaction<'a, AccountId> {
/// This type is capable of representing both shielded and transparent outputs.
pub struct SentTransactionOutput<AccountId> {
output_index: usize,
recipient: Recipient<AccountId, Note>,
recipient: Recipient<AccountId, Note, OutPoint>,
value: NonNegativeAmount,
memo: Option<MemoBytes>,
}
Expand All @@ -1256,7 +1266,7 @@ impl<AccountId> SentTransactionOutput<AccountId> {
/// * `memo` - the memo that was sent with this output
pub fn from_parts(
output_index: usize,
recipient: Recipient<AccountId, Note>,
recipient: Recipient<AccountId, Note, OutPoint>,
value: NonNegativeAmount,
memo: Option<MemoBytes>,
) -> Self {
Expand All @@ -1278,8 +1288,8 @@ impl<AccountId> SentTransactionOutput<AccountId> {
self.output_index
}
/// Returns the recipient address of the transaction, or the account id and
/// resulting note for wallet-internal outputs.
pub fn recipient(&self) -> &Recipient<AccountId, Note> {
/// resulting note/outpoint for wallet-internal outputs.
pub fn recipient(&self) -> &Recipient<AccountId, Note, OutPoint> {
&self.recipient
}
/// Returns the value of the newly created output.
Expand Down Expand Up @@ -1514,8 +1524,11 @@ pub trait WalletWrite: WalletRead {
received_tx: DecryptedTransaction<Self::AccountId>,
) -> Result<(), Self::Error>;

/// Saves information about a transaction that was constructed and sent by the wallet to the
/// persistent wallet store.
/// Saves information about a transaction constructed by the wallet to the persistent
/// wallet store.
///
/// The name `store_sent_tx` is somewhat misleading; this must be called *before* the
/// transaction is sent to the network.
fn store_sent_tx(
&mut self,
sent_tx: &SentTransaction<Self::AccountId>,
Expand All @@ -1535,6 +1548,26 @@ pub trait WalletWrite: WalletRead {
///
/// There may be restrictions on heights to which it is possible to truncate.
fn truncate_to_height(&mut self, block_height: BlockHeight) -> Result<(), Self::Error>;

/// Reserves the next `n` available ephemeral addresses for the given account.
/// This cannot be undone, so as far as possible, errors associated with transaction
/// construction should have been reported before calling this method.
///
/// To ensure that sufficient information is stored on-chain to allow recovering
/// funds sent back to any of the used addresses, a "gap limit" of 20 addresses
/// should be observed as described in [BIP 44].
///
/// Returns an error if there is insufficient space within the gap limit to allocate
/// the given number of addresses, or if the account identifier does not correspond
/// to a known account.
///
/// [BIP 44]: https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki#user-content-Address_gap_limit
#[cfg(feature = "transparent-inputs")]
fn reserve_next_n_ephemeral_addresses(
&mut self,
account_id: Self::AccountId,
n: u32,
) -> Result<Vec<TransparentAddress>, Self::Error>;
}

/// This trait describes a capability for manipulating wallet note commitment trees.
Expand Down Expand Up @@ -1862,6 +1895,15 @@ pub mod testing {
) -> Result<HashMap<TransparentAddress, NonNegativeAmount>, Self::Error> {
Ok(HashMap::new())
}

#[cfg(feature = "transparent-inputs")]
fn get_reserved_ephemeral_addresses(
&self,
_account: Self::AccountId,
_for_sync: bool,
) -> Result<Vec<TransparentAddress>, Self::Error> {
Ok(vec![])
}
}

impl WalletWrite for MockWalletDb {
Expand Down Expand Up @@ -1924,6 +1966,15 @@ pub mod testing {
) -> Result<Self::UtxoRef, Self::Error> {
Ok(0)
}

#[cfg(feature = "transparent-inputs")]
fn reserve_next_n_ephemeral_addresses(
&mut self,
_account_id: Self::AccountId,
_n: u32,
) -> Result<Vec<TransparentAddress>, Self::Error> {
Err(())
}
}

impl WalletCommitmentTrees for MockWalletDb {
Expand Down
2 changes: 2 additions & 0 deletions zcash_client_backend/src/data_api/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,8 @@ pub enum Error<DataSourceError, CommitmentTreeError, SelectionError, FeeError> {
/// An error occurred parsing the address from a payment request.
Address(ConversionError<&'static str>),

/// The address associated with a record being inserted was not recognized as
/// belonging to the wallet
#[cfg(feature = "transparent-inputs")]
AddressNotRecognized(TransparentAddress),
}
Expand Down
Loading

0 comments on commit 964ef81

Please sign in to comment.