//! Structs for core Zcash primitives. use ff::{Field, PrimeField, PrimeFieldRepr}; use crate::constants; use crate::group_hash::group_hash; use crate::pedersen_hash::{pedersen_hash, Personalization}; use byteorder::{LittleEndian, WriteBytesExt}; use crate::jubjub::{edwards, FixedGenerators, JubjubEngine, JubjubParams, PrimeOrder}; use blake2s_simd::Params as Blake2sParams; #[derive(Clone)] pub struct ValueCommitment { pub value: u64, pub randomness: E::Fs, } impl ValueCommitment { pub fn cm(&self, params: &E::Params) -> edwards::Point { params .generator(FixedGenerators::ValueCommitmentValue) .mul(E::Fs::from(self.value), params) .add( ¶ms .generator(FixedGenerators::ValueCommitmentRandomness) .mul(self.randomness, params), params, ) } } #[derive(Clone)] pub struct ProofGenerationKey { pub ak: edwards::Point, pub nsk: E::Fs, } impl ProofGenerationKey { pub fn to_viewing_key(&self, params: &E::Params) -> ViewingKey { ViewingKey { ak: self.ak.clone(), nk: params .generator(FixedGenerators::ProofGenerationKey) .mul(self.nsk, params), } } } #[derive(Debug)] pub struct ViewingKey { pub ak: edwards::Point, pub nk: edwards::Point, } impl ViewingKey { pub fn rk(&self, ar: E::Fs, params: &E::Params) -> edwards::Point { self.ak.add( ¶ms .generator(FixedGenerators::SpendingKeyGenerator) .mul(ar, params), params, ) } pub fn ivk(&self) -> E::Fs { let mut preimage = [0; 64]; self.ak.write(&mut preimage[0..32]).unwrap(); self.nk.write(&mut preimage[32..64]).unwrap(); let mut h = [0; 32]; h.copy_from_slice( Blake2sParams::new() .hash_length(32) .personal(constants::CRH_IVK_PERSONALIZATION) .hash(&preimage) .as_bytes(), ); // Drop the most significant five bits, so it can be interpreted as a scalar. h[31] &= 0b0000_0111; let mut e = ::Repr::default(); e.read_le(&h[..]).unwrap(); E::Fs::from_repr(e).expect("should be a valid scalar") } pub fn to_payment_address( &self, diversifier: Diversifier, params: &E::Params, ) -> Option> { diversifier.g_d(params).and_then(|g_d| { let pk_d = g_d.mul(self.ivk(), params); PaymentAddress::from_parts(diversifier, pk_d) }) } } #[derive(Copy, Clone, Debug, PartialEq)] pub struct Diversifier(pub [u8; 11]); impl Diversifier { pub fn g_d( &self, params: &E::Params, ) -> Option> { group_hash::( &self.0, constants::KEY_DIVERSIFICATION_PERSONALIZATION, params, ) } } /// A Sapling payment address. /// /// # Invariants /// /// `pk_d` is guaranteed to be prime-order (i.e. in the prime-order subgroup of Jubjub, /// and not the identity). #[derive(Clone, Debug)] pub struct PaymentAddress { pk_d: edwards::Point, diversifier: Diversifier, } impl PartialEq for PaymentAddress { fn eq(&self, other: &Self) -> bool { self.pk_d == other.pk_d && self.diversifier == other.diversifier } } impl PaymentAddress { /// Constructs a PaymentAddress from a diversifier and a Jubjub point. /// /// Returns None if `pk_d` is the identity. pub fn from_parts( diversifier: Diversifier, pk_d: edwards::Point, ) -> Option { if pk_d == edwards::Point::zero() { None } else { Some(PaymentAddress { pk_d, diversifier }) } } /// Constructs a PaymentAddress from a diversifier and a Jubjub point. /// /// Only for test code, as this explicitly bypasses the invariant. #[cfg(test)] pub(crate) fn from_parts_unchecked( diversifier: Diversifier, pk_d: edwards::Point, ) -> Self { PaymentAddress { pk_d, diversifier } } /// Parses a PaymentAddress from bytes. pub fn from_bytes(bytes: &[u8; 43], params: &E::Params) -> Option { let diversifier = { let mut tmp = [0; 11]; tmp.copy_from_slice(&bytes[0..11]); Diversifier(tmp) }; // Check that the diversifier is valid if diversifier.g_d::(params).is_none() { return None; } edwards::Point::::read(&bytes[11..43], params) .ok()? .as_prime_order(params) .and_then(|pk_d| PaymentAddress::from_parts(diversifier, pk_d)) } /// Returns the byte encoding of this `PaymentAddress`. pub fn to_bytes(&self) -> [u8; 43] { let mut bytes = [0; 43]; bytes[0..11].copy_from_slice(&self.diversifier.0); self.pk_d.write(&mut bytes[11..]).unwrap(); bytes } /// Returns the [`Diversifier`] for this `PaymentAddress`. pub fn diversifier(&self) -> &Diversifier { &self.diversifier } /// Returns `pk_d` for this `PaymentAddress`. pub fn pk_d(&self) -> &edwards::Point { &self.pk_d } pub fn g_d(&self, params: &E::Params) -> Option> { self.diversifier.g_d(params) } pub fn create_note( &self, value: u64, randomness: E::Fs, params: &E::Params, ) -> Option> { self.g_d(params).map(|g_d| Note { value, r: randomness, g_d, pk_d: self.pk_d.clone(), }) } } #[derive(Clone, Debug)] pub struct Note { /// The value of the note pub value: u64, /// The diversified base of the address, GH(d) pub g_d: edwards::Point, /// The public key of the address, g_d^ivk pub pk_d: edwards::Point, /// The commitment randomness pub r: E::Fs, } impl PartialEq for Note { fn eq(&self, other: &Self) -> bool { self.value == other.value && self.g_d == other.g_d && self.pk_d == other.pk_d && self.r == other.r } } impl Note { pub fn uncommitted() -> E::Fr { // The smallest u-coordinate that is not on the curve // is one. // TODO: This should be relocated to JubjubEngine as // it's specific to the curve we're using, not all // twisted edwards curves. E::Fr::one() } /// Computes the note commitment, returning the full point. fn cm_full_point(&self, params: &E::Params) -> edwards::Point { // Calculate the note contents, as bytes let mut note_contents = vec![]; // Writing the value in little endian (&mut note_contents) .write_u64::(self.value) .unwrap(); // Write g_d self.g_d.write(&mut note_contents).unwrap(); // Write pk_d self.pk_d.write(&mut note_contents).unwrap(); assert_eq!(note_contents.len(), 32 + 32 + 8); // Compute the Pedersen hash of the note contents let hash_of_contents = pedersen_hash( Personalization::NoteCommitment, note_contents .into_iter() .flat_map(|byte| (0..8).map(move |i| ((byte >> i) & 1) == 1)), params, ); // Compute final commitment params .generator(FixedGenerators::NoteCommitmentRandomness) .mul(self.r, params) .add(&hash_of_contents, params) } /// Computes the nullifier given the viewing key and /// note position pub fn nf(&self, viewing_key: &ViewingKey, position: u64, params: &E::Params) -> Vec { // Compute rho = cm + position.G let rho = self.cm_full_point(params).add( ¶ms .generator(FixedGenerators::NullifierPosition) .mul(E::Fs::from(position), params), params, ); // Compute nf = BLAKE2s(nk | rho) let mut nf_preimage = [0u8; 64]; viewing_key.nk.write(&mut nf_preimage[0..32]).unwrap(); rho.write(&mut nf_preimage[32..64]).unwrap(); Blake2sParams::new() .hash_length(32) .personal(constants::PRF_NF_PERSONALIZATION) .hash(&nf_preimage) .as_bytes() .to_vec() } /// Computes the note commitment pub fn cm(&self, params: &E::Params) -> E::Fr { // The commitment is in the prime order subgroup, so mapping the // commitment to the x-coordinate is an injective encoding. self.cm_full_point(params).to_xy().0 } }