From 932f1c97372bddd99944ebadbff51e5e19ed587f Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Fri, 24 May 2019 15:17:36 +0100 Subject: [PATCH] Implement TransparentAddress encoding and decoding --- zcash_client_backend/Cargo.toml | 1 + zcash_client_backend/src/constants/mainnet.rs | 10 ++ zcash_client_backend/src/constants/testnet.rs | 10 ++ zcash_client_backend/src/encoding.rs | 105 ++++++++++++++++++ 4 files changed, 126 insertions(+) diff --git a/zcash_client_backend/Cargo.toml b/zcash_client_backend/Cargo.toml index 562d1a50e..b7dd7b731 100644 --- a/zcash_client_backend/Cargo.toml +++ b/zcash_client_backend/Cargo.toml @@ -13,6 +13,7 @@ edition = "2018" [dependencies] bech32 = "0.7" +bs58 = { version = "0.3", features = ["check"] } ff = { version = "0.6", path = "../ff" } hex = "0.3" pairing = { version = "0.16", path = "../pairing" } diff --git a/zcash_client_backend/src/constants/mainnet.rs b/zcash_client_backend/src/constants/mainnet.rs index f3f020dc5..db932fa56 100644 --- a/zcash_client_backend/src/constants/mainnet.rs +++ b/zcash_client_backend/src/constants/mainnet.rs @@ -28,3 +28,13 @@ pub const HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY: &str = "zxviews"; /// [`PaymentAddress`]: zcash_primitives::primitives::PaymentAddress /// [Zcash Protocol Specification]: https://github.com/zcash/zips/blob/master/protocol/protocol.pdf pub const HRP_SAPLING_PAYMENT_ADDRESS: &str = "zs"; + +/// The prefix for a Base58Check-encoded mainnet [`TransparentAddress::PublicKey`]. +/// +/// [`TransparentAddress::PublicKey`]: zcash_primitives::legacy::TransparentAddress::PublicKey +pub const B58_PUBKEY_ADDRESS_PREFIX: [u8; 2] = [0x1c, 0xb8]; + +/// The prefix for a Base58Check-encoded mainnet [`TransparentAddress::Script`]. +/// +/// [`TransparentAddress::Script`]: zcash_primitives::legacy::TransparentAddress::Script +pub const B58_SCRIPT_ADDRESS_PREFIX: [u8; 2] = [0x1c, 0xbd]; diff --git a/zcash_client_backend/src/constants/testnet.rs b/zcash_client_backend/src/constants/testnet.rs index 5379d5abd..d39a11d53 100644 --- a/zcash_client_backend/src/constants/testnet.rs +++ b/zcash_client_backend/src/constants/testnet.rs @@ -28,3 +28,13 @@ pub const HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY: &str = "zxviewtestsapling"; /// [`PaymentAddress`]: zcash_primitives::primitives::PaymentAddress /// [Zcash Protocol Specification]: https://github.com/zcash/zips/blob/master/protocol/protocol.pdf pub const HRP_SAPLING_PAYMENT_ADDRESS: &str = "ztestsapling"; + +/// The prefix for a Base58Check-encoded testnet [`TransparentAddress::PublicKey`]. +/// +/// [`TransparentAddress::PublicKey`]: zcash_primitives::legacy::TransparentAddress::PublicKey +pub const B58_PUBKEY_ADDRESS_PREFIX: [u8; 2] = [0x1d, 0x25]; + +/// The prefix for a Base58Check-encoded testnet [`TransparentAddress::Script`]. +/// +/// [`TransparentAddress::Script`]: zcash_primitives::legacy::TransparentAddress::Script +pub const B58_SCRIPT_ADDRESS_PREFIX: [u8; 2] = [0x1c, 0xba]; diff --git a/zcash_client_backend/src/encoding.rs b/zcash_client_backend/src/encoding.rs index ba999126c..91522fd50 100644 --- a/zcash_client_backend/src/encoding.rs +++ b/zcash_client_backend/src/encoding.rs @@ -6,9 +6,12 @@ //! [`constants`]: crate::constants use bech32::{self, Error, FromBase32, ToBase32}; +use bs58::{self, decode::Error as Bs58Error}; use pairing::bls12_381::Bls12; +use std::convert::TryInto; use std::io::{self, Write}; use zcash_primitives::{ + legacy::TransparentAddress, primitives::PaymentAddress, zip32::{ExtendedFullViewingKey, ExtendedSpendingKey}, JUBJUB, @@ -176,6 +179,108 @@ pub fn decode_payment_address(hrp: &str, s: &str) -> Result String { + let decoded = match addr { + TransparentAddress::PublicKey(key_id) => { + let mut decoded = vec![0; pubkey_version.len() + 20]; + decoded[..pubkey_version.len()].copy_from_slice(pubkey_version); + decoded[pubkey_version.len()..].copy_from_slice(key_id); + decoded + } + TransparentAddress::Script(script_id) => { + let mut decoded = vec![0; script_version.len() + 20]; + decoded[..script_version.len()].copy_from_slice(script_version); + decoded[script_version.len()..].copy_from_slice(script_id); + decoded + } + }; + bs58::encode(decoded).with_check().into_string() +} + +/// Decodes a [`TransparentAddress`] from a Base58Check-encoded string. +/// +/// # Examples +/// +/// ``` +/// use zcash_client_backend::{ +/// constants::testnet::{B58_PUBKEY_ADDRESS_PREFIX, B58_SCRIPT_ADDRESS_PREFIX}, +/// encoding::decode_transparent_address, +/// }; +/// use zcash_primitives::legacy::TransparentAddress; +/// +/// assert_eq!( +/// decode_transparent_address( +/// &B58_PUBKEY_ADDRESS_PREFIX, +/// &B58_SCRIPT_ADDRESS_PREFIX, +/// "tm9iMLAuYMzJ6jtFLcA7rzUmfreGuKvr7Ma", +/// ), +/// Ok(Some(TransparentAddress::PublicKey([0; 20]))), +/// ); +/// +/// assert_eq!( +/// decode_transparent_address( +/// &B58_PUBKEY_ADDRESS_PREFIX, +/// &B58_SCRIPT_ADDRESS_PREFIX, +/// "t26YoyZ1iPgiMEWL4zGUm74eVWfhyDMXzY2", +/// ), +/// Ok(Some(TransparentAddress::Script([0; 20]))), +/// ); +/// ``` +pub fn decode_transparent_address( + pubkey_version: &[u8], + script_version: &[u8], + s: &str, +) -> Result, Bs58Error> { + bs58::decode(s).with_check(None).into_vec().map(|decoded| { + if decoded.starts_with(pubkey_version) { + decoded[pubkey_version.len()..] + .try_into() + .ok() + .map(TransparentAddress::PublicKey) + } else if decoded.starts_with(script_version) { + decoded[script_version.len()..] + .try_into() + .ok() + .map(TransparentAddress::Script) + } else { + None + } + }) +} + #[cfg(test)] mod tests { use pairing::bls12_381::Bls12;