From 3eeb9925ebd896974caadb220a823c8aef00925e Mon Sep 17 00:00:00 2001 From: Deirdre Connolly Date: Wed, 15 Apr 2020 04:49:01 -0400 Subject: [PATCH] Impl Debug, Display, and FromStr for Sapling FullViewingKey --- zebra-chain/src/keys/sapling.rs | 95 ++++++++++++++++++++++++++++++++- 1 file changed, 94 insertions(+), 1 deletion(-) diff --git a/zebra-chain/src/keys/sapling.rs b/zebra-chain/src/keys/sapling.rs index c090d3c9f..4957a7eb8 100644 --- a/zebra-chain/src/keys/sapling.rs +++ b/zebra-chain/src/keys/sapling.rs @@ -8,8 +8,14 @@ //! [ps]: https://zips.z.cash/protocol/protocol.pdf#saplingkeycomponents //! [3.1]: https://zips.z.cash/protocol/protocol.pdf#addressesandkeys -use std::{convert::TryFrom, fmt, ops::Deref}; +use std::{ + convert::{From, Into, TryFrom}, + fmt, + io::{self, Write}, + ops::Deref, +}; +use bech32::{self, FromBase32, ToBase32}; use blake2b_simd; use blake2s_simd; use jubjub; @@ -21,6 +27,11 @@ use proptest::{array, prelude::*}; #[cfg(test)] use proptest_derive::Arbitrary; +use crate::{ + serialization::{ReadZcashExt, SerializationError}, + Network, +}; + /// The [Randomness Beacon][1] ("URS"). /// /// First 64 bytes of the BLAKE2s input during JubJub group hash. URS @@ -270,6 +281,13 @@ impl fmt::Debug for OutgoingViewingKey { } } +impl From<[u8; 32]> for OutgoingViewingKey { + /// Generate an _OutgoingViewingKey_ from existing bytes. + fn from(bytes: [u8; 32]) -> Self { + Self(bytes) + } +} + impl From for OutgoingViewingKey { /// For this invocation of Blake2b-512 as _PRF^expand_, t=2. /// @@ -332,6 +350,12 @@ impl From for AuthorizingKey { #[derive(Copy, Clone, PartialEq)] pub struct NullifierDerivingKey(pub jubjub::AffinePoint); +impl From<[u8; 32]> for NullifierDerivingKey { + fn from(bytes: [u8; 32]) -> Self { + Self(jubjub::AffinePoint::from_bytes(bytes).unwrap()) + } +} + impl Deref for NullifierDerivingKey { type Target = jubjub::AffinePoint; @@ -543,6 +567,14 @@ impl Arbitrary for TransmissionKey { type Strategy = BoxedStrategy; } +/// Magic human-readable strings used to identify what networks +/// Sapling FullViewingKeys are associated with when encoded/decoded +/// with bech32. +mod fvk_hrp { + pub const MAINNET: &str = "zviews"; + pub const TESTNET: &str = "zviewtestsapling"; +} + /// Full Viewing Keys /// /// Allows recognizing both incoming and outgoing notes without having @@ -554,11 +586,71 @@ impl Arbitrary for TransmissionKey { /// /// https://zips.z.cash/protocol/protocol.pdf#saplingfullviewingkeyencoding pub struct FullViewingKey { + network: Network, authorizing_key: AuthorizingKey, nullifier_deriving_key: NullifierDerivingKey, outgoing_viewing_key: OutgoingViewingKey, } +impl fmt::Debug for FullViewingKey { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("FullViewingKey") + .field("network", &self.network) + .field("authorizing_key", &self.authorizing_key) + .field("nullifier_deriving_key", &self.nullifier_deriving_key) + .field("outgoing_viewing_key", &self.outgoing_viewing_key) + .finish() + } +} + +impl fmt::Display for FullViewingKey { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let mut bytes = io::Cursor::new(Vec::new()); + + let auth_key_bytes: [u8; 32] = self.authorizing_key.into(); + + let _ = bytes.write_all(&auth_key_bytes); + let _ = bytes.write_all(&self.nullifier_deriving_key.to_bytes()); + let _ = bytes.write_all(&self.outgoing_viewing_key.0); + + let hrp = match self.network { + Network::Mainnet => fvk_hrp::MAINNET, + _ => fvk_hrp::TESTNET, + }; + + bech32::encode_to_fmt(f, hrp, bytes.get_ref().to_base32()).unwrap() + } +} + +impl std::str::FromStr for FullViewingKey { + type Err = SerializationError; + + fn from_str(s: &str) -> Result { + match bech32::decode(s) { + Ok((hrp, bytes)) => { + let mut decoded_bytes = io::Cursor::new(Vec::::from_base32(&bytes).unwrap()); + + let authorizing_key_bytes = decoded_bytes.read_32_bytes()?; + let nullifier_deriving_key_bytes = decoded_bytes.read_32_bytes()?; + let outgoing_key_bytes = decoded_bytes.read_32_bytes()?; + + Ok(FullViewingKey { + network: match hrp.as_str() { + fvk_hrp::MAINNET => Network::Mainnet, + _ => Network::Testnet, + }, + authorizing_key: AuthorizingKey::from(authorizing_key_bytes), + nullifier_deriving_key: NullifierDerivingKey::from( + nullifier_deriving_key_bytes, + ), + outgoing_viewing_key: OutgoingViewingKey::from(outgoing_key_bytes), + }) + } + Err(_) => Err(SerializationError::Parse("bech32 decoding error")), + } + } +} + #[cfg(test)] mod tests { @@ -593,6 +685,7 @@ mod tests { let _transmission_key = TransmissionKey::from(incoming_viewing_key, diversifier); let _full_viewing_key = FullViewingKey { + network: Network::default(), authorizing_key, nullifier_deriving_key, outgoing_viewing_key,