From b3db623e29bfca2a70a2e7e2f77a01dfe134dbdc Mon Sep 17 00:00:00 2001 From: Deirdre Connolly Date: Mon, 30 Mar 2020 04:25:47 -0400 Subject: [PATCH] Fill out SaplingShieldedAddress impls and some tests --- zebra-chain/src/addresses/sapling.rs | 149 +++++++++++++++++++++++++++ 1 file changed, 149 insertions(+) diff --git a/zebra-chain/src/addresses/sapling.rs b/zebra-chain/src/addresses/sapling.rs index b816c07d..b722f2c7 100644 --- a/zebra-chain/src/addresses/sapling.rs +++ b/zebra-chain/src/addresses/sapling.rs @@ -2,16 +2,165 @@ use std::{fmt, io}; +use bech32::{self, FromBase32, ToBase32}; + #[cfg(test)] use proptest::{arbitrary::Arbitrary, array, prelude::*}; use crate::{ keys::sapling, serialization::{ReadZcashExt, SerializationError, ZcashDeserialize, ZcashSerialize}, + Network, }; +/// Human-Readable Parts for input to bech32 encoding. +mod human_readable_parts { + pub const MAINNET: &'static str = "zs"; + pub const TESTNET: &'static str = "ztestsapling"; +} + /// +#[derive(Clone, Copy, PartialEq)] pub struct SaplingShieldedAddress { diversifier: sapling::Diversifier, transmission_key: sapling::TransmissionKey, } + +impl fmt::Debug for SaplingShieldedAddress { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("SaplingShieldedAddress") + .field("diversifier", &self.diversifier) + .field("transmission_key", &self.transmission_key) + .finish() + } +} + +impl SaplingShieldedAddress { + fn to_human_readable_address(&self, network: Network) -> Result { + let mut bytes = io::Cursor::new(Vec::new()); + let _ = self.zcash_serialize(&mut bytes); + + let mut hrp = ""; + + match network { + Network::Mainnet => hrp = human_readable_parts::MAINNET, + _ => hrp = human_readable_parts::TESTNET, + } + + bech32::encode(hrp, bytes.get_ref().to_base32()) + } +} + +impl Eq for SaplingShieldedAddress {} + +impl ZcashSerialize for SaplingShieldedAddress { + fn zcash_serialize(&self, mut writer: W) -> Result<(), io::Error> { + writer.write_all(&self.diversifier.0[..])?; + writer.write_all(&self.transmission_key.to_bytes())?; + + Ok(()) + } +} + +impl ZcashDeserialize for SaplingShieldedAddress { + fn zcash_deserialize(mut reader: R) -> Result { + let mut diversifier_bytes = [0; 11]; + reader.read_exact(&mut diversifier_bytes)?; + + let transmission_key_bytes = reader.read_32_bytes()?; + + Ok(SaplingShieldedAddress { + diversifier: sapling::Diversifier(diversifier_bytes), + transmission_key: sapling::TransmissionKey::from_bytes(transmission_key_bytes).unwrap(), + }) + } +} + +impl std::str::FromStr for SaplingShieldedAddress { + type Err = SerializationError; + + fn from_str(s: &str) -> Result { + match bech32::decode(s) { + Ok((_, bytes)) => { + let decoded = Vec::::from_base32(&bytes).unwrap(); + Self::zcash_deserialize(io::Cursor::new(decoded)) + } + Err(_) => Err(SerializationError::Parse("bech32 decoding error")), + } + } +} + +#[cfg(test)] +impl Arbitrary for SaplingShieldedAddress { + type Parameters = (); + + fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy { + (array::uniform11(any::()), array::uniform32(any::())) + .prop_map(|(diversifier_bytes, transmission_key_bytes)| { + return Self { + diversifier: sapling::Diversifier(diversifier_bytes), + transmission_key: sapling::TransmissionKey::from_bytes(transmission_key_bytes) + .unwrap(), + }; + }) + .boxed() + } + + type Strategy = BoxedStrategy; +} + +#[cfg(test)] +mod tests { + + use std::str::FromStr; + + use super::*; + + #[test] + fn from_str_debug() { + let zs_addr = SaplingShieldedAddress::from_str( + "zs1qqqqqqqqqqqqqqqqqrjq05nyfku05msvu49mawhg6kr0wwljahypwyk2h88z6975u563j8nfaxd", + ) + .expect("sapling z-addr string to parse"); + + assert_eq!( + format!("{:?}", zs_addr), + + "SaplingShieldedAddress { diversifier: Diversifier(\"0000000000000000000000\"), transmission_key: AffinePoint { u: 0x038c2b77a9a63b652905adb192ad7d535beef23468d12c6e6033d7aa9753a98a, v: 0x1935e5d4172dceb9ca1217c8edf23bf786d5e8babe4be50c6efab84d64d207e4 } }" + ); + } + + #[test] + fn to_human_readable_address() { + let zs_addr = SaplingShieldedAddress::from_str( + "zs1qqqqqqqqqqqqqqqqqrjq05nyfku05msvu49mawhg6kr0wwljahypwyk2h88z6975u563j8nfaxd", + ) + .expect("sapling z-addr string to parse"); + + let address = zs_addr + .to_human_readable_address(Network::Mainnet) + .expect("z-addr to serialize in a human-readable way"); + + assert_eq!( + format!("{}", address), + "zs1qqqqqqqqqqqqqqqqqrjq05nyfku05msvu49mawhg6kr0wwljahypwyk2h88z6975u563j8nfaxd" + ); + } +} + +#[cfg(test)] +proptest! { + + // #[test] + // fn sapling_address_roundtrip(zaddr in any::()) { + + // let mut data = Vec::new(); + + // zaddr.zcash_serialize(&mut data).expect("sapling z-addr should serialize"); + + // let zaddr2 = SaplingShieldedAddress::zcash_deserialize(&data[..]) + // .expect("randomized sapling z-addr should deserialize"); + + // prop_assert_eq![zaddr, zaddr2]; + // } +}