use std::{fmt, io}; use rand_core::{CryptoRng, RngCore}; use crate::{ keys::sapling::find_group_hash, serde_helpers, serialization::{ReadZcashExt, SerializationError, ZcashDeserialize, ZcashSerialize}, }; // TODO: replace with reference to redjubjub or jubjub when merged and // exported. type Scalar = jubjub::Fr; /// The randomness used in the Pedersen Hash for note commitment. #[derive(Copy, Clone, Debug, PartialEq)] pub struct CommitmentRandomness(redjubjub::Randomizer); /// Note commitments for the output notes. #[derive(Clone, Copy, Deserialize, PartialEq, Serialize)] //#[cfg_attr(test, derive(proptest_derive::Arbitrary))] pub struct NoteCommitment(#[serde(with = "serde_helpers::AffinePoint")] pub jubjub::AffinePoint); impl fmt::Debug for NoteCommitment { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_struct("NoteCommitment") .field("u", &hex::encode(self.0.get_u().to_bytes())) .field("v", &hex::encode(self.0.get_v().to_bytes())) .finish() } } impl From<[u8; 32]> for NoteCommitment { fn from(bytes: [u8; 32]) -> Self { Self(jubjub::AffinePoint::from_bytes(bytes).unwrap()) } } impl Eq for NoteCommitment {} impl From for [u8; 32] { fn from(cm: NoteCommitment) -> [u8; 32] { cm.0.to_bytes() } } impl ZcashSerialize for NoteCommitment { fn zcash_serialize(&self, mut writer: W) -> Result<(), io::Error> { writer.write_all(&self.0.to_bytes())?; Ok(()) } } impl ZcashDeserialize for NoteCommitment { fn zcash_deserialize(mut reader: R) -> Result { Ok(Self( jubjub::AffinePoint::from_bytes(reader.read_32_bytes()?).unwrap(), )) } } impl NoteCommitment { /// Generate a new _NoteCommitment_. /// /// https://zips.z.cash/protocol/protocol.pdf#concretewindowedcommit #[allow(non_snake_case)] pub fn new(csprng: &mut T, value_bytes: [u8; 32]) -> Self where T: RngCore + CryptoRng, { let v = Scalar::from_bytes(&value_bytes).unwrap(); let mut rcv_bytes = [0u8; 32]; csprng.fill_bytes(&mut rcv_bytes); let rcv = Scalar::from_bytes(&rcv_bytes).unwrap(); let V = find_group_hash(*b"Zcash_cv", b"v"); let R = find_group_hash(*b"Zcash_cv", b"r"); Self::from(V * v + R * rcv) } /// Hash Extractor for Jubjub (?) /// /// https://zips.z.cash/protocol/protocol.pdf#concreteextractorjubjub pub fn extract_u(self) -> jubjub::Fq { self.0.get_u() } } /// A Homomorphic Pedersen commitment to the value of a note, used in /// Spend and Output Descriptions. /// /// https://zips.z.cash/protocol/protocol.pdf#concretehomomorphiccommit #[derive(Clone, Deserialize, PartialEq, Serialize)] //#[cfg_attr(test, derive(proptest_derive::Arbitrary))] pub struct ValueCommitment(#[serde(with = "serde_helpers::AffinePoint")] pub jubjub::AffinePoint); impl fmt::Debug for ValueCommitment { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_struct("ValueCommitment") .field("u", &hex::encode(self.0.get_u().to_bytes())) .field("v", &hex::encode(self.0.get_v().to_bytes())) .finish() } } impl From<[u8; 32]> for ValueCommitment { fn from(bytes: [u8; 32]) -> Self { Self(jubjub::AffinePoint::from_bytes(bytes).unwrap()) } } impl From for ValueCommitment { fn from(extended_point: jubjub::ExtendedPoint) -> Self { Self(jubjub::AffinePoint::from(extended_point)) } } impl Eq for ValueCommitment {} impl From for [u8; 32] { fn from(cm: ValueCommitment) -> [u8; 32] { cm.0.to_bytes() } } /// LEBS2OSP256(repr_J(cv)) /// /// https://zips.z.cash/protocol/protocol.pdf#spendencoding /// https://zips.z.cash/protocol/protocol.pdf#jubjub impl ZcashSerialize for ValueCommitment { fn zcash_serialize(&self, mut writer: W) -> Result<(), io::Error> { writer.write_all(&self.0.to_bytes())?; Ok(()) } } impl ZcashDeserialize for ValueCommitment { fn zcash_deserialize(mut reader: R) -> Result { Ok(Self( jubjub::AffinePoint::from_bytes(reader.read_32_bytes()?).unwrap(), )) } } impl ValueCommitment { /// Generate a new _ValueCommitment_. /// /// https://zips.z.cash/protocol/protocol.pdf#concretehomomorphiccommit // TODO: accept an Amount instead? #[allow(non_snake_case)] pub fn new(csprng: &mut T, value_bytes: [u8; 32]) -> Self where T: RngCore + CryptoRng, { let v = Scalar::from_bytes(&value_bytes).unwrap(); let mut rcv_bytes = [0u8; 32]; csprng.fill_bytes(&mut rcv_bytes); let rcv = Scalar::from_bytes(&rcv_bytes).unwrap(); let V = find_group_hash(*b"Zcash_cv", b"v"); let R = find_group_hash(*b"Zcash_cv", b"r"); Self::from(V * v + R * rcv) } }