use ff::{Field, PrimeField, PrimeFieldRepr}; use constants; use group_hash::group_hash; use pedersen_hash::{pedersen_hash, Personalization}; use byteorder::{LittleEndian, WriteBytesExt}; use 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(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 into_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 into_payment_address( &self, diversifier: Diversifier, params: &E::Params, ) -> Option> { diversifier.g_d(params).map(|g_d| { let pk_d = g_d.mul(self.ivk(), params); PaymentAddress { pk_d: pk_d, diversifier: diversifier, } }) } } #[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, ) } } #[derive(Clone, Debug)] pub struct PaymentAddress { pub pk_d: edwards::Point, pub diversifier: Diversifier, } impl PartialEq for PaymentAddress { fn eq(&self, other: &Self) -> bool { self.pk_d == other.pk_d && self.diversifier == other.diversifier } } impl PaymentAddress { 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: value, r: randomness, g_d: 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(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).into_xy().0 } }