Skip to content

Commit

Permalink
Merge pull request #1523 from zcash/keys/decode_extfvk_with_network
Browse files Browse the repository at this point in the history
zcash_keys: Add `decode_extfvk_with_network`
  • Loading branch information
nuttycom authored Aug 30, 2024
2 parents 7a1387d + a732932 commit 50cdf73
Show file tree
Hide file tree
Showing 3 changed files with 77 additions and 4 deletions.
6 changes: 6 additions & 0 deletions zcash_keys/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ and this library adheres to Rust's notion of

## [Unreleased]

### 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
- `zcash_keys`:
Expand Down
50 changes: 46 additions & 4 deletions zcash_keys/src/encoding.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -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<T, F>(hrp: &str, s: &str, read: F) -> Result<T, Bech32DecodeError>
where
Expand Down Expand Up @@ -245,9 +255,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,
Expand All @@ -256,6 +265,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::<u8>::from_base32(&data)?[..])
.map_err(|_| Bech32DecodeError::ReadError)?;

Ok((network, fvk))
}
}

/// Writes a [`PaymentAddress`] as a Bech32-encoded string.
///
/// # Examples
Expand Down
25 changes: 25 additions & 0 deletions zcash_keys/src/keys.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::{
Expand Down Expand Up @@ -111,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
Expand Down Expand Up @@ -176,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.
Expand Down Expand Up @@ -674,6 +681,24 @@ impl UnifiedFullViewingKey {
vec![],
)
}

#[cfg(all(feature = "sapling", feature = "unstable"))]
pub fn from_sapling_extended_full_viewing_key(
sapling: ExtendedFullViewingKey,
) -> Result<UnifiedFullViewingKey, DerivationError> {
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(
Expand Down

0 comments on commit 50cdf73

Please sign in to comment.