From 897018a4718d56692bd552fff37898e511246db9 Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Fri, 30 Aug 2024 14:11:59 -0600 Subject: [PATCH 1/3] zcash_keys: Add `decode_extfvk_with_network` --- zcash_keys/CHANGELOG.md | 3 +++ zcash_keys/src/encoding.rs | 40 ++++++++++++++++++++++++++++++++++---- 2 files changed, 39 insertions(+), 4 deletions(-) diff --git a/zcash_keys/CHANGELOG.md b/zcash_keys/CHANGELOG.md index 060a70dca1..e7454fb178 100644 --- a/zcash_keys/CHANGELOG.md +++ b/zcash_keys/CHANGELOG.md @@ -6,6 +6,9 @@ and this library adheres to Rust's notion of ## [Unreleased] +### Added +- `zcash_keys::encoding::decode_extfvk_with_network` + ## [0.3.0] - 2024-08-19 ### Notable changes - `zcash_keys`: diff --git a/zcash_keys/src/encoding.rs b/zcash_keys/src/encoding.rs index 8de7125a05..f53931f7da 100644 --- a/zcash_keys/src/encoding.rs +++ b/zcash_keys/src/encoding.rs @@ -6,7 +6,7 @@ use crate::address::UnifiedAddress; use bs58::{self, decode::Error as Bs58Error}; use std::fmt; -use zcash_primitives::consensus::NetworkConstants; +use zcash_primitives::consensus::{NetworkConstants, NetworkType}; use zcash_address::unified::{self, Encoding}; use zcash_primitives::{consensus, legacy::TransparentAddress}; @@ -245,9 +245,8 @@ pub fn encode_extended_full_viewing_key(hrp: &str, extfvk: &ExtendedFullViewingK bech32_encode(hrp, |w| extfvk.write(w)) } -/// Decodes an [`ExtendedFullViewingKey`] from a Bech32-encoded string. -/// -/// [`ExtendedFullViewingKey`]: sapling::zip32::ExtendedFullViewingKey +/// Decodes an [`ExtendedFullViewingKey`] from a Bech32-encoded string, verifying that it matches +/// the provided human-readable prefix. #[cfg(feature = "sapling")] pub fn decode_extended_full_viewing_key( hrp: &str, @@ -256,6 +255,39 @@ pub fn decode_extended_full_viewing_key( bech32_decode(hrp, s, |data| ExtendedFullViewingKey::read(&data[..]).ok()) } +/// Decodes an [`ExtendedFullViewingKey`] and the [`NetworkType`] that it is intended for use with +/// from a Bech32-encoded string. +#[cfg(feature = "sapling")] +pub fn decode_extfvk_with_network( + s: &str, +) -> Result<(NetworkType, ExtendedFullViewingKey), Bech32DecodeError> { + use zcash_protocol::constants::{mainnet, regtest, testnet}; + + let (decoded_hrp, data, variant) = bech32::decode(s)?; + if variant != Variant::Bech32 { + Err(Bech32DecodeError::IncorrectVariant(variant)) + } else { + let network = match &decoded_hrp[..] { + mainnet::HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY => Ok(NetworkType::Main), + testnet::HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY => Ok(NetworkType::Test), + regtest::HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY => Ok(NetworkType::Regtest), + other => Err(Bech32DecodeError::HrpMismatch { + expected: format!( + "One of {}, {}, or {}", + mainnet::HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY, + testnet::HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY, + regtest::HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY, + ), + actual: other.to_string(), + }), + }?; + let fvk = ExtendedFullViewingKey::read(&Vec::::from_base32(&data)?[..]) + .map_err(|_| Bech32DecodeError::ReadError)?; + + Ok((network, fvk)) + } +} + /// Writes a [`PaymentAddress`] as a Bech32-encoded string. /// /// # Examples From af695dc1c2989ec910a302e5a1ad5b07b26aed4a Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Fri, 30 Aug 2024 14:30:16 -0600 Subject: [PATCH 2/3] Add unstable `UnifiedFullViewingKey::from_sapling_extended_full_viewing_key` --- zcash_keys/src/keys.rs | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/zcash_keys/src/keys.rs b/zcash_keys/src/keys.rs index f1f8d6eb6c..cd8b6a18f4 100644 --- a/zcash_keys/src/keys.rs +++ b/zcash_keys/src/keys.rs @@ -37,6 +37,9 @@ use { #[cfg(feature = "orchard")] use orchard::{self, keys::Scope}; +#[cfg(all(feature = "sapling", feature = "unstable"))] +use ::sapling::zip32::ExtendedFullViewingKey; + #[cfg(feature = "sapling")] pub mod sapling { pub use sapling::zip32::{ @@ -674,6 +677,24 @@ impl UnifiedFullViewingKey { vec![], ) } + + #[cfg(all(feature = "sapling", feature = "unstable"))] + pub fn from_sapling_extended_full_viewing_key( + sapling: ExtendedFullViewingKey, + ) -> Result { + Self::from_checked_parts( + #[cfg(feature = "transparent-inputs")] + None, + #[cfg(feature = "sapling")] + Some(sapling.to_diversifiable_full_viewing_key()), + #[cfg(feature = "orchard")] + None, + // We don't currently allow constructing new UFVKs with unknown items, but we store + // this to allow parsing such UFVKs. + vec![], + ) + } + /// Construct a UFVK from its constituent parts, after verifying that UIVK derivation can /// succeed. fn from_checked_parts( From a7329323aa19d47aa85fb1d6bdbe34a29df9963e Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Fri, 30 Aug 2024 14:30:16 -0600 Subject: [PATCH 3/3] zcash_keys: implement std::error::Error for decoding errors --- zcash_keys/CHANGELOG.md | 3 +++ zcash_keys/src/encoding.rs | 10 ++++++++++ zcash_keys/src/keys.rs | 4 ++++ 3 files changed, 17 insertions(+) diff --git a/zcash_keys/CHANGELOG.md b/zcash_keys/CHANGELOG.md index e7454fb178..a0bb242d35 100644 --- a/zcash_keys/CHANGELOG.md +++ b/zcash_keys/CHANGELOG.md @@ -8,6 +8,9 @@ and this library adheres to Rust's notion of ### Added - `zcash_keys::encoding::decode_extfvk_with_network` +- `impl std::error::Error for Bech32DecodeError` +- `impl std::error::Error for DecodingError` +- `impl std::error::Error for DerivationError` ## [0.3.0] - 2024-08-19 ### Notable changes diff --git a/zcash_keys/src/encoding.rs b/zcash_keys/src/encoding.rs index f53931f7da..894103a04b 100644 --- a/zcash_keys/src/encoding.rs +++ b/zcash_keys/src/encoding.rs @@ -66,6 +66,16 @@ impl fmt::Display for Bech32DecodeError { } } +#[cfg(feature = "sapling")] +impl std::error::Error for Bech32DecodeError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match &self { + Bech32DecodeError::Bech32Error(e) => Some(e), + _ => None, + } + } +} + #[cfg(feature = "sapling")] fn bech32_decode(hrp: &str, s: &str, read: F) -> Result where diff --git a/zcash_keys/src/keys.rs b/zcash_keys/src/keys.rs index cd8b6a18f4..dbd8f79346 100644 --- a/zcash_keys/src/keys.rs +++ b/zcash_keys/src/keys.rs @@ -114,6 +114,8 @@ impl Display for DerivationError { } } +impl std::error::Error for DerivationError {} + /// A version identifier for the encoding of unified spending keys. /// /// Each era corresponds to a range of block heights. During an era, the unified spending key @@ -179,6 +181,8 @@ impl std::fmt::Display for DecodingError { } } +impl std::error::Error for DecodingError {} + #[cfg(feature = "unstable")] impl Era { /// Returns the unique identifier for the era.