From 2337f7948479f15cbacea4ed46f2015eb0a40cd4 Mon Sep 17 00:00:00 2001 From: Daira-Emma Hopwood Date: Sun, 16 Jun 2024 19:02:29 +0100 Subject: [PATCH] Add `zcash_keys::Address::Tex`. Signed-off-by: Daira-Emma Hopwood --- zcash_client_backend/CHANGELOG.md | 2 ++ zcash_client_backend/src/data_api/wallet.rs | 3 +++ .../src/data_api/wallet/input_selection.rs | 10 +++++++ zcash_client_sqlite/src/testing.rs | 4 ++- .../wallet/init/migrations/ufvk_support.rs | 6 +++-- zcash_keys/CHANGELOG.md | 6 +++++ zcash_keys/src/address.rs | 27 ++++++++++++++++--- 7 files changed, 52 insertions(+), 6 deletions(-) diff --git a/zcash_client_backend/CHANGELOG.md b/zcash_client_backend/CHANGELOG.md index e57fcfcf1..f9ac86891 100644 --- a/zcash_client_backend/CHANGELOG.md +++ b/zcash_client_backend/CHANGELOG.md @@ -35,6 +35,8 @@ and this library adheres to Rust's notion of if a memo is given for the transparent pool. Use `ChangeValue::shielded` to avoid this error case when creating a `ChangeValue` known to be for a shielded pool. +- `zcash_client_backend::input_selection::GreedyInputSelectorError` has a + new variant `UnsupportedTexAddress`. - `zcash_client_backend::wallet::Recipient` variants have changed. Instead of wrapping protocol-address types, the `Recipient` type now wraps a `zcash_address::ZcashAddress`. This simplifies the process of tracking the diff --git a/zcash_client_backend/src/data_api/wallet.rs b/zcash_client_backend/src/data_api/wallet.rs index 3921b6a6e..0f7fcb2a9 100644 --- a/zcash_client_backend/src/data_api/wallet.rs +++ b/zcash_client_backend/src/data_api/wallet.rs @@ -1038,6 +1038,9 @@ where payment.amount(), )); } + Address::Tex(_) => { + return Err(Error::ProposalNotSupported); + } } } diff --git a/zcash_client_backend/src/data_api/wallet/input_selection.rs b/zcash_client_backend/src/data_api/wallet/input_selection.rs index ddcba7304..8ac49c92e 100644 --- a/zcash_client_backend/src/data_api/wallet/input_selection.rs +++ b/zcash_client_backend/src/data_api/wallet/input_selection.rs @@ -222,6 +222,8 @@ pub enum GreedyInputSelectorError { Balance(BalanceError), /// A unified address did not contain a supported receiver. UnsupportedAddress(Box), + /// Support for transparent-source-only (TEX) addresses requires the transparent-inputs feature. + UnsupportedTexAddress, /// An error was encountered in change selection. Change(ChangeError), } @@ -239,6 +241,9 @@ impl fmt::Display for GreedyInputSelectorErro // don't have network parameters here write!(f, "Unified address contains no supported receivers.") } + GreedyInputSelectorError::UnsupportedTexAddress => { + write!(f, "Support for transparent-source-only (TEX) addresses requires the transparent-inputs feature.") + } GreedyInputSelectorError::Change(err) => { write!(f, "An error occurred computing change and fees: {}", err) } @@ -373,6 +378,11 @@ where script_pubkey: addr.script(), }); } + Address::Tex(_) => { + return Err(InputSelectorError::Selection( + GreedyInputSelectorError::UnsupportedTexAddress, + )); + } Address::Sapling(_) => { payment_pools.insert(*idx, PoolType::SAPLING); sapling_outputs.push(SaplingPayment(payment.amount())); diff --git a/zcash_client_sqlite/src/testing.rs b/zcash_client_sqlite/src/testing.rs index d6161ea2d..53d2b6c19 100644 --- a/zcash_client_sqlite/src/testing.rs +++ b/zcash_client_sqlite/src/testing.rs @@ -1807,7 +1807,9 @@ fn fake_compact_block_spending( ) .0, ), - Address::Transparent(_) => panic!("transparent addresses not supported in compact blocks"), + Address::Transparent(_) | Address::Tex(_) => { + panic!("transparent addresses not supported in compact blocks") + } Address::Unified(ua) => { // This is annoying to implement, because the protocol-aware UA type has no // concept of ZIP 316 preference order. diff --git a/zcash_client_sqlite/src/wallet/init/migrations/ufvk_support.rs b/zcash_client_sqlite/src/wallet/init/migrations/ufvk_support.rs index 5a97b24ea..c8d14f422 100644 --- a/zcash_client_sqlite/src/wallet/init/migrations/ufvk_support.rs +++ b/zcash_client_sqlite/src/wallet/init/migrations/ufvk_support.rs @@ -129,7 +129,7 @@ impl RusqliteMigration for Migration

{ }); } } - Address::Transparent(_) => { + Address::Transparent(_) | Address::Tex(_) => { return Err(WalletMigrationError::CorruptedData( "Address field value decoded to a transparent address; should have been Sapling or unified.".to_string())); } @@ -261,7 +261,9 @@ impl RusqliteMigration for Migration

{ })?; let output_pool = match decoded_address { Address::Sapling(_) => Ok(pool_code(PoolType::SAPLING)), - Address::Transparent(_) => Ok(pool_code(PoolType::TRANSPARENT)), + Address::Transparent(_) | Address::Tex(_) => { + Ok(pool_code(PoolType::TRANSPARENT)) + } Address::Unified(_) => Err(WalletMigrationError::CorruptedData( "Unified addresses should not yet appear in the sent_notes table." .to_string(), diff --git a/zcash_keys/CHANGELOG.md b/zcash_keys/CHANGELOG.md index 6987ac04e..accb894fb 100644 --- a/zcash_keys/CHANGELOG.md +++ b/zcash_keys/CHANGELOG.md @@ -5,11 +5,17 @@ and this library adheres to Rust's notion of [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] +### Notable changes +- `zcash_keys` now supports TEX (transparent-source-only) addresses as specified + in [ZIP 320](https://zips.z.cash/zip-0320). + ### Added - `zcash_keys::address::Address::try_from_zcash_address` - `zcash_keys::address::Receiver` ### Changed +- `zcash_keys::Address` has a new variant `Tex`. +- `zcash_keys::address::Address::has_receiver` has been renamed to `can_receive_as`. - MSRV is now 1.70.0. - `zcash_keys::keys`: - The (unstable) encoding of `UnifiedSpendingKey` has changed. diff --git a/zcash_keys/src/address.rs b/zcash_keys/src/address.rs index 8d3e0e460..ab74bb105 100644 --- a/zcash_keys/src/address.rs +++ b/zcash_keys/src/address.rs @@ -293,10 +293,22 @@ impl Receiver { /// An address that funds can be sent to. #[derive(Debug, PartialEq, Eq, Clone)] pub enum Address { + /// A Sapling payment address. #[cfg(feature = "sapling")] Sapling(PaymentAddress), + + /// A transparent address corresponding to either a public key or a `Script`. Transparent(TransparentAddress), + + /// A [ZIP 316] Unified Address. + /// + /// [ZIP 316]: https://zips.z.cash/zip-0316 Unified(UnifiedAddress), + + /// A [ZIP 320] transparent-source-only P2PKH address, or "TEX address". + /// + /// [ZIP 320]: https://zips.z.cash/zip-0320 + Tex([u8; 20]), } #[cfg(feature = "sapling")] @@ -344,6 +356,10 @@ impl TryFromRawAddress for Address { fn try_from_raw_transparent_p2sh(data: [u8; 20]) -> Result> { Ok(TransparentAddress::ScriptHash(data).into()) } + + fn try_from_raw_tex(data: [u8; 20]) -> Result> { + Ok(Address::Tex(data)) + } } impl Address { @@ -379,6 +395,7 @@ impl Address { } }, Address::Unified(ua) => ua.to_address(net), + Address::Tex(data) => ZcashAddress::from_tex(net, *data), } } @@ -387,14 +404,16 @@ impl Address { self.to_zcash_address(params).to_string() } - /// Returns whether or not this [`Address`] can send funds to the specified pool. - pub fn has_receiver(&self, pool_type: PoolType) -> bool { + /// Returns whether or not this [`Address`] can receive funds in the specified pool. + pub fn can_receive_as(&self, pool_type: PoolType) -> bool { match self { #[cfg(feature = "sapling")] Address::Sapling(_) => { matches!(pool_type, PoolType::Shielded(ShieldedProtocol::Sapling)) } - Address::Transparent(_) => matches!(pool_type, PoolType::Transparent), + Address::Transparent(_) | Address::Tex(_) => { + matches!(pool_type, PoolType::Transparent) + } Address::Unified(ua) => match pool_type { PoolType::Transparent => ua.transparent().is_some(), PoolType::Shielded(ShieldedProtocol::Sapling) => { @@ -449,6 +468,7 @@ pub mod testing { arb_payment_address().prop_map(Address::Sapling), arb_transparent_addr().prop_map(Address::Transparent), arb_unified_addr(Network::TestNetwork, request).prop_map(Address::Unified), + proptest::array::uniform20(any::()).prop_map(Address::Tex), ] } @@ -457,6 +477,7 @@ pub mod testing { return prop_oneof![ arb_transparent_addr().prop_map(Address::Transparent), arb_unified_addr(Network::TestNetwork, request).prop_map(Address::Unified), + proptest::array::uniform20(any::()).prop_map(Address::Tex), ]; } }