diff --git a/Cargo.lock b/Cargo.lock index 6fd7629..a785792 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -130,6 +130,14 @@ dependencies = [ "byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "bs58" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "byte-tools" version = "0.3.1" @@ -650,6 +658,7 @@ name = "zcash_client_backend" version = "0.0.0" dependencies = [ "bech32 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", + "bs58 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", "ff 0.4.0", "hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "pairing 0.14.2", @@ -733,6 +742,7 @@ dependencies = [ "checksum block-buffer 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b" "checksum block-cipher-trait 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1c924d49bd09e7c06003acda26cd9742e796e34282ec6c1189404dee0c1f4774" "checksum block-padding 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "6d4dc3af3ee2e12f3e5d224e5e1e3d73668abbeb69e566d361f7d5563a4fdf09" +"checksum bs58 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "0d9644ad62ff4df43da2e057febbbce576f7124cb5cd8e90e0ce7027f38aa2dd" "checksum byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" "checksum byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a7c3dd8985a7111efc5c80b44e23ecdd8c007de8ade3b96595387e812b957cf5" "checksum c2-chacha 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7d64d04786e0f528460fc884753cf8dddcc466be308f6026f8e355c41a0e4101" diff --git a/zcash_client_backend/Cargo.toml b/zcash_client_backend/Cargo.toml index ed6e502..b522e28 100644 --- a/zcash_client_backend/Cargo.toml +++ b/zcash_client_backend/Cargo.toml @@ -8,6 +8,7 @@ edition = "2018" [dependencies] bech32 = "0.7" +bs58 = { version = "0.2", features = ["check"] } ff = { path = "../ff" } hex = "0.3" pairing = { path = "../pairing" } diff --git a/zcash_client_backend/src/constants/mainnet.rs b/zcash_client_backend/src/constants/mainnet.rs index ed90697..b004c0c 100644 --- a/zcash_client_backend/src/constants/mainnet.rs +++ b/zcash_client_backend/src/constants/mainnet.rs @@ -26,3 +26,13 @@ pub const HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY: &str = "zxviews"; /// [`PaymentAddress`]: sapling_crypto::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 f151110..012c2d0 100644 --- a/zcash_client_backend/src/constants/testnet.rs +++ b/zcash_client_backend/src/constants/testnet.rs @@ -26,3 +26,13 @@ pub const HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY: &str = "zxviewtestsapling"; /// [`PaymentAddress`]: sapling_crypto::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 d39973d..0bd1644 100644 --- a/zcash_client_backend/src/encoding.rs +++ b/zcash_client_backend/src/encoding.rs @@ -4,6 +4,7 @@ //! module. use bech32::{self, Error, FromBase32, ToBase32}; +use bs58::{self, decode::DecodeError}; use pairing::bls12_381::Bls12; use std::io::{self, Write}; use zcash_primitives::{ @@ -11,6 +12,7 @@ use zcash_primitives::{ primitives::{Diversifier, PaymentAddress}, }; use zcash_primitives::{ + legacy::TransparentAddress, zip32::{ExtendedFullViewingKey, ExtendedSpendingKey}, JUBJUB, }; @@ -182,6 +184,113 @@ 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, DecodeError> { + let decoded = bs58::decode(s).with_check(None).into_vec()?; + if &decoded[..pubkey_version.len()] == pubkey_version { + if decoded.len() == pubkey_version.len() + 20 { + let mut data = [0; 20]; + data.copy_from_slice(&decoded[pubkey_version.len()..]); + Ok(Some(TransparentAddress::PublicKey(data))) + } else { + Ok(None) + } + } else if &decoded[..script_version.len()] == script_version { + if decoded.len() == script_version.len() + 20 { + let mut data = [0; 20]; + data.copy_from_slice(&decoded[script_version.len()..]); + Ok(Some(TransparentAddress::Script(data))) + } else { + Ok(None) + } + } else { + Ok(None) + } +} + #[cfg(test)] mod tests { use pairing::bls12_381::Bls12;