diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 56e9732025..1af4ca1631 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -278,10 +278,12 @@ jobs: path: crates # We use a synthetic crate to ensure no dev-dependencies are enabled, which can # be incompatible with some of these targets. + - name: Copy Rust toolchain into the root for use in synthetic crate setup + run: cp crates/rust-toolchain.toml . - name: Create synthetic crate for testing run: cargo init --lib ci-build - - name: Copy Rust version into synthetic crate - run: cp crates/rust-toolchain.toml ci-build/ + - name: Move Rust toolchain file into synthetic crate + run: mv rust-toolchain.toml ci-build/ - name: Copy patch directives into synthetic crate run: | echo "[patch.crates-io]" >> ./ci-build/Cargo.toml @@ -315,10 +317,12 @@ jobs: path: crates # We use a synthetic crate to ensure no dev-dependencies are enabled, which can # be incompatible with some of these targets. + - name: Copy Rust toolchain into the root for use in synthetic crate setup + run: cp crates/rust-toolchain.toml . - name: Create synthetic crate for testing run: cargo init --lib ci-build - - name: Copy Rust version into synthetic crate - run: cp crates/rust-toolchain.toml ci-build/ + - name: Move Rust toolchain file into synthetic crate + run: mv rust-toolchain.toml ci-build/ - name: Copy patch directives into synthetic crate run: | echo "[patch.crates-io]" >> ./ci-build/Cargo.toml diff --git a/Cargo.lock b/Cargo.lock index 4659b7f6a4..2aabec0079 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6134,7 +6134,7 @@ dependencies = [ [[package]] name = "zcash_address" -version = "0.7.0" +version = "0.7.1" dependencies = [ "assert_matches", "bech32", diff --git a/components/zcash_address/CHANGELOG.md b/components/zcash_address/CHANGELOG.md index 7d37df3adc..10f5a55685 100644 --- a/components/zcash_address/CHANGELOG.md +++ b/components/zcash_address/CHANGELOG.md @@ -7,6 +7,11 @@ and this library adheres to Rust's notion of ## [Unreleased] +## [0.6.3, 0.7.1] - 2025-05-07 +### Added +- `zcash_address::Converter` +- `zcash_address::ZcashAddress::convert_with` + ## [0.7.0] - 2025-02-21 ### Added - `zcash_address::unified::Item` to expose the opaque typed encoding of unified diff --git a/components/zcash_address/Cargo.toml b/components/zcash_address/Cargo.toml index 3f7d6bb2ac..5fb4a21d33 100644 --- a/components/zcash_address/Cargo.toml +++ b/components/zcash_address/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "zcash_address" description = "Zcash address parsing and serialization" -version = "0.7.0" +version = "0.7.1" authors = [ "Jack Grigg ", ] diff --git a/components/zcash_address/src/convert.rs b/components/zcash_address/src/convert.rs index 5e0277b2bd..925ca6040e 100644 --- a/components/zcash_address/src/convert.rs +++ b/components/zcash_address/src/convert.rs @@ -305,6 +305,104 @@ impl TryFromAddress for (NetworkType, T) { } } +/// A trait for converter types that can project from a [`ZcashAddress`] into another type. +/// +/// [`ZcashAddress`]: crate::ZcashAddress +/// +/// # Examples +/// +/// ``` +/// use zcash_address::{ConversionError, Converter, UnsupportedAddress, ZcashAddress}; +/// use zcash_protocol::consensus::NetworkType; +/// +/// struct KeyFinder { } +/// +/// impl KeyFinder { +/// fn find_sapling_extfvk(&self, data: [u8; 43]) -> Option<[u8; 73]> { +/// todo!() +/// } +/// } +/// +/// // Makes it possible to use a KeyFinder to find the Sapling extfvk that corresponds +/// // to a given ZcashAddress. +/// impl Converter> for KeyFinder { +/// type Error = &'static str; +/// +/// fn convert_sapling( +/// &self, +/// net: NetworkType, +/// data: [u8; 43], +/// ) -> Result, ConversionError> { +/// Ok(self.find_sapling_extfvk(data)) +/// } +/// } +/// ``` +pub trait Converter { + /// Conversion errors for the user type (e.g. failing to parse the data passed to + /// [`Self::convert_sapling`] as a valid Sapling address). + type Error; + + fn convert_sprout( + &self, + net: NetworkType, + data: [u8; 64], + ) -> Result> { + let _ = (net, data); + Err(ConversionError::Unsupported(UnsupportedAddress("Sprout"))) + } + + fn convert_sapling( + &self, + net: NetworkType, + data: [u8; 43], + ) -> Result> { + let _ = (net, data); + Err(ConversionError::Unsupported(UnsupportedAddress("Sapling"))) + } + + fn convert_unified( + &self, + net: NetworkType, + data: unified::Address, + ) -> Result> { + let _ = (net, data); + Err(ConversionError::Unsupported(UnsupportedAddress("Unified"))) + } + + fn convert_transparent_p2pkh( + &self, + net: NetworkType, + data: [u8; 20], + ) -> Result> { + let _ = (net, data); + Err(ConversionError::Unsupported(UnsupportedAddress( + "transparent P2PKH", + ))) + } + + fn convert_transparent_p2sh( + &self, + net: NetworkType, + data: [u8; 20], + ) -> Result> { + let _ = (net, data); + Err(ConversionError::Unsupported(UnsupportedAddress( + "transparent P2SH", + ))) + } + + fn convert_tex( + &self, + net: NetworkType, + data: [u8; 20], + ) -> Result> { + let _ = (net, data); + Err(ConversionError::Unsupported(UnsupportedAddress( + "transparent-source restricted P2PKH", + ))) + } +} + /// A helper trait for converting another type into a [`ZcashAddress`]. /// /// This trait is sealed and cannot be implemented for types outside this crate. Its diff --git a/components/zcash_address/src/lib.rs b/components/zcash_address/src/lib.rs index e07ad40778..dff13848a3 100644 --- a/components/zcash_address/src/lib.rs +++ b/components/zcash_address/src/lib.rs @@ -147,7 +147,7 @@ mod kind; pub mod test_vectors; pub use convert::{ - ConversionError, ToAddress, TryFromAddress, TryFromRawAddress, UnsupportedAddress, + ConversionError, Converter, ToAddress, TryFromAddress, TryFromRawAddress, UnsupportedAddress, }; pub use encoding::ParseError; pub use kind::unified; @@ -282,6 +282,25 @@ impl ZcashAddress { } } + /// Converts this address into another type using the specified converter. + /// + /// `convert` can convert into any type `T` for which an implementation of the [`Converter`] + /// trait exists. This enables conversion of [`ZcashAddress`] values into other types to rely + /// on additional context. + pub fn convert_with>( + self, + converter: C, + ) -> Result> { + match self.kind { + AddressKind::Sprout(data) => converter.convert_sprout(self.net, data), + AddressKind::Sapling(data) => converter.convert_sapling(self.net, data), + AddressKind::Unified(data) => converter.convert_unified(self.net, data), + AddressKind::P2pkh(data) => converter.convert_transparent_p2pkh(self.net, data), + AddressKind::P2sh(data) => converter.convert_transparent_p2sh(self.net, data), + AddressKind::Tex(data) => converter.convert_tex(self.net, data), + } + } + /// Returns whether this address has the ability to receive transfers of the given pool type. pub fn can_receive_as(&self, pool_type: PoolType) -> bool { use AddressKind::*;