From ef85173df54f37774a30290b6aa77d4507cdea5c Mon Sep 17 00:00:00 2001 From: Sean Bowe Date: Tue, 6 Mar 2018 22:25:15 -0700 Subject: [PATCH 01/15] Fix comments in jubjub code. --- src/jubjub/edwards.rs | 20 ++++++++++++++++++++ src/jubjub/mod.rs | 36 +++++++++++++++++++----------------- src/jubjub/montgomery.rs | 19 ++++++++++++++++--- 3 files changed, 55 insertions(+), 20 deletions(-) diff --git a/src/jubjub/edwards.rs b/src/jubjub/edwards.rs index eeabe9d49..e73c0efc7 100644 --- a/src/jubjub/edwards.rs +++ b/src/jubjub/edwards.rs @@ -28,6 +28,9 @@ use std::io::{ // Represents the affine point (X/Z, Y/Z) via the extended // twisted Edwards coordinates. +// +// See "Twisted Edwards Curves Revisited" +// Huseyin Hisil, Kenneth Koon-Ho Wong, Gary Carter, and Ed Dawson pub struct Point { x: E::Fr, y: E::Fr, @@ -120,7 +123,14 @@ impl Point { params: &E::Params ) -> io::Result { + // Jubjub points are encoded least significant bit first. + // The most significant bit (bit 254) encodes the parity + // of the x-coordinate. + let mut y_repr = ::Repr::default(); + + // This reads in big-endian, so we perform a swap of the + // limbs in the representation and swap the bit order. y_repr.read_be(reader)?; y_repr.as_mut().reverse(); @@ -393,11 +403,19 @@ impl Point { } pub fn double(&self, params: &E::Params) -> Self { + // Point addition is unified and complete. + // There are dedicated formulae, but we do + // not implement these now. + self.add(self, params) } pub fn add(&self, other: &Self, params: &E::Params) -> Self { + // See "Twisted Edwards Curves Revisited" + // Huseyin Hisil, Kenneth Koon-Ho Wong, Gary Carter, and Ed Dawson + // 3.1 Unified Addition in E^e + // A = x1 * x2 let mut a = self.x; a.mul_assign(&other.x); @@ -470,6 +488,8 @@ impl Point { params: &E::Params ) -> Self { + // Standard double-and-add scalar multiplication + let mut res = Self::zero(); for b in BitIterator::new(scalar.into()) { diff --git a/src/jubjub/mod.rs b/src/jubjub/mod.rs index b9bdfafc1..9c4c864c7 100644 --- a/src/jubjub/mod.rs +++ b/src/jubjub/mod.rs @@ -1,18 +1,21 @@ -//! Jubjub is an elliptic curve defined over the BLS12-381 scalar field, Fr. -//! It is a Montgomery curve that takes the form `y^2 = x^3 + Ax^2 + x` where -//! `A = 40962`. This is the smallest integer choice of A such that: +//! Jubjub is a twisted Edwards curve defined over the BLS12-381 scalar +//! field, Fr. It takes the form `-x^2 + y^2 = 1 + dx^2y^2` with +//! `d = -(10240/10241)`. It is birationally equivalent to a Montgomery +//! curve of the form `y^2 = x^3 + Ax^2 + x` with `A = 40962`. This +//! value `A` is the smallest integer choice such that: //! //! * `(A - 2) / 4` is a small integer (`10240`). //! * `A^2 - 4` is quadratic residue. -//! * The group order of the curve and its quadratic twist has a large prime factor. +//! * The group order of the curve and its quadratic twist has a large +//! prime factor. //! //! Jubjub has `s = 0x0e7db4ea6533afa906673b0101343b00a6682093ccc81082d0970e5ed6f72cb7` -//! as the prime subgroup order, with cofactor 8. (The twist has cofactor 4.) +//! as the prime subgroup order, with cofactor 8. (The twist has +//! cofactor 4.) //! -//! This curve is birationally equivalent to a twisted Edwards curve of the -//! form `-x^2 + y^2 = 1 + dx^2y^2` with `d = -(10240/10241)`. In fact, this equivalence -//! forms a group isomorphism, so points can be freely converted between the Montgomery -//! and twisted Edwards forms. +//! It is a complete twisted Edwards curve, so the equivalence with +//! the Montgomery curve forms a group isomorphism, allowing points +//! to be freely converted between the two forms. use pairing::{ Engine, @@ -30,10 +33,17 @@ use pairing::bls12_381::{ pub mod edwards; pub mod montgomery; +pub mod fs; #[cfg(test)] pub mod tests; +/// Point of unknown order. +pub enum Unknown { } + +/// Point of prime order. +pub enum PrimeOrder { } + /// Fixed generators of the Jubjub curve of unknown /// exponent. #[derive(Copy, Clone)] @@ -104,14 +114,6 @@ pub trait JubjubParams: Sized { fn circuit_generators(&self, FixedGenerators) -> &[Vec<(E::Fr, E::Fr)>]; } -/// Point of unknown order. -pub enum Unknown { } - -/// Point of prime order. -pub enum PrimeOrder { } - -pub mod fs; - impl JubjubEngine for Bls12 { type Fs = self::fs::Fs; type Params = JubjubBls12; diff --git a/src/jubjub/montgomery.rs b/src/jubjub/montgomery.rs index e82711122..224387717 100644 --- a/src/jubjub/montgomery.rs +++ b/src/jubjub/montgomery.rs @@ -20,8 +20,7 @@ use rand::{ use std::marker::PhantomData; -// Represents the affine point (X/Z, Y/Z) via the extended -// twisted Edwards coordinates. +// Represents the affine point (X, Y) pub struct Point { x: E::Fr, y: E::Fr, @@ -69,7 +68,7 @@ impl PartialEq for Point { impl Point { pub fn get_for_x(x: E::Fr, sign: bool, params: &E::Params) -> Option { - // given an x on the curve, y^2 = x^3 + A*x^2 + x + // Given an x on the curve, y = sqrt(x^3 + A*x^2 + x) let mut x2 = x; x2.square(); @@ -230,10 +229,17 @@ impl Point { return Point::zero(); } + // (0, 0) is the point of order 2. Doubling + // produces the point at infinity. if self.y == E::Fr::zero() { return Point::zero(); } + // This is a standard affine point doubling formula + // See 4.3.2 The group law for Weierstrass curves + // Montgomery curves and the Montgomery Ladder + // Daniel J. Bernstein and Tanja Lange + let mut delta = E::Fr::one(); { let mut tmp = params.montgomery_a().clone(); @@ -276,6 +282,11 @@ impl Point { pub fn add(&self, other: &Self, params: &E::Params) -> Self { + // This is a standard affine point addition formula + // See 4.3.2 The group law for Weierstrass curves + // Montgomery curves and the Montgomery Ladder + // Daniel J. Bernstein and Tanja Lange + match (self.infinity, other.infinity) { (true, true) => Point::zero(), (true, false) => other.clone(), @@ -325,6 +336,8 @@ impl Point { params: &E::Params ) -> Self { + // Standard double-and-add scalar multiplication + let mut res = Self::zero(); for b in BitIterator::new(scalar.into()) { From 2d4be07560361882144cd41102c86f3a9e36aa09 Mon Sep 17 00:00:00 2001 From: Sean Bowe Date: Tue, 6 Mar 2018 22:26:03 -0700 Subject: [PATCH 02/15] Fix group hash comment. --- src/group_hash.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/group_hash.rs b/src/group_hash.rs index 58ece78f2..8dd0df5d8 100644 --- a/src/group_hash.rs +++ b/src/group_hash.rs @@ -6,10 +6,9 @@ use blake2_rfc::blake2s::Blake2s; /// the algorithm, for rigidity purposes. pub const FIRST_BLOCK: &'static [u8; 64] = b"0000000000000000002ffe76b973aabaff1d1557d79acf2c3795809c83caf580"; -/// Produces an (x, y) pair (Montgomery) for a -/// random point in the Jubjub curve. The point -/// is guaranteed to be prime order and not the -/// identity. +/// Produces a random point in the Jubjub curve. +/// The point is guaranteed to be prime order +/// and not the identity. pub fn group_hash( tag: &[u8], personalization: &[u8], From 0242ed35ab6036f0f40d8d60a69b4bfacb988b41 Mon Sep 17 00:00:00 2001 From: Sean Bowe Date: Wed, 7 Mar 2018 12:19:56 -0700 Subject: [PATCH 03/15] Add some circuit tests for inputs. --- src/circuit/mod.rs | 76 +++++++++++++++++++++++++++++++++-------- src/circuit/test/mod.rs | 13 +++++++ src/primitives/mod.rs | 6 ++-- 3 files changed, 79 insertions(+), 16 deletions(-) diff --git a/src/circuit/mod.rs b/src/circuit/mod.rs index def6d9ed5..a81d6c707 100644 --- a/src/circuit/mod.rs +++ b/src/circuit/mod.rs @@ -23,6 +23,7 @@ use bellman::{ use jubjub::{ JubjubEngine, Unknown, + PrimeOrder, FixedGenerators, edwards }; @@ -329,9 +330,9 @@ pub struct Output<'a, E: JubjubEngine> { /// Randomness that will hide the value pub value_randomness: Option, /// The diversified base, computed by GH(d) - pub g_d: Option>, + pub g_d: Option>, /// The diversified address point, computed by GH(d)^ivk - pub p_d: Option>, + pub pk_d: Option>, /// The randomness used to hide the note commitment data pub commitment_randomness: Option, /// The ephemeral secret key for DH with recipient @@ -413,20 +414,20 @@ impl<'a, E: JubjubEngine> Circuit for Output<'a, E> { epk.inputize(cs.namespace(|| "epk"))?; } - // Now let's deal with p_d. We don't do any checks and + // Now let's deal with pk_d. We don't do any checks and // essentially allow the prover to witness any 256 bits // they would like. { - let p_d = self.p_d.map(|e| e.into_xy()); + let pk_d = self.pk_d.map(|e| e.into_xy()); let y_contents = boolean::field_into_boolean_vec_le( - cs.namespace(|| "p_d bits of y"), - p_d.map(|e| e.1) + cs.namespace(|| "pk_d bits of y"), + pk_d.map(|e| e.1) )?; let sign_bit = boolean::Boolean::from(boolean::AllocatedBit::alloc( - cs.namespace(|| "p_d bit of x"), - p_d.map(|e| e.0.into_repr().is_odd()) + cs.namespace(|| "pk_d bit of x"), + pk_d.map(|e| e.0.into_repr().is_odd()) )?); note_contents.extend(y_contents); @@ -481,10 +482,11 @@ impl<'a, E: JubjubEngine> Circuit for Output<'a, E> { #[test] fn test_input_circuit_with_bls12_381() { + use pairing::{Field}; use pairing::bls12_381::*; use rand::{SeedableRng, Rng, XorShiftRng}; use ::circuit::test::*; - use jubjub::{JubjubBls12, fs}; + use jubjub::{JubjubParams, JubjubBls12, fs}; let params = &JubjubBls12::new(); let rng = &mut XorShiftRng::from_seed([0x3dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); @@ -510,7 +512,7 @@ fn test_input_circuit_with_bls12_381() { ak: Some(ak), g_d: Some(g_d), commitment_randomness: Some(commitment_randomness), - auth_path: auth_path + auth_path: auth_path.clone() }; instance.synthesize(&mut cs).unwrap(); @@ -518,23 +520,42 @@ fn test_input_circuit_with_bls12_381() { assert!(cs.is_satisfied()); assert_eq!(cs.num_constraints(), 101550); assert_eq!(cs.hash(), "3cc6d9383ca882ae3666267618e826e9d51a3177fc89ef6d42d9f63b84179f77"); + + let expected_value_cm = params.generator(FixedGenerators::ValueCommitmentValue) + .mul(fs::FsRepr::from(value), params) + .add( + ¶ms.generator(FixedGenerators::ValueCommitmentRandomness) + .mul(value_randomness, params), + params + ); + let expected_value_cm_xy = expected_value_cm.into_xy(); + + assert_eq!(cs.num_inputs(), 6); + assert_eq!(cs.get_input(0, "ONE"), Fr::one()); + assert_eq!(cs.get_input(1, "value commitment/x/input variable"), expected_value_cm_xy.0); + assert_eq!(cs.get_input(2, "value commitment/y/input variable"), expected_value_cm_xy.1); + + cs.get_input(3, "anchor/input variable"); + cs.get_input(4, "nullifier/x/input variable"); + cs.get_input(5, "nullifier/y/input variable"); } } #[test] fn test_output_circuit_with_bls12_381() { + use pairing::{Field}; use pairing::bls12_381::*; use rand::{SeedableRng, Rng, XorShiftRng}; use ::circuit::test::*; - use jubjub::{JubjubBls12, fs}; + use jubjub::{JubjubParams, JubjubBls12, fs}; let params = &JubjubBls12::new(); let rng = &mut XorShiftRng::from_seed([0x3dbe6258, 0x8d313d76, 0x3237db17, 0xe5bc0654]); let value: u64 = 1; let value_randomness: fs::Fs = rng.gen(); - let g_d: edwards::Point = edwards::Point::rand(rng, params); - let p_d: edwards::Point = edwards::Point::rand(rng, params); + let g_d: edwards::Point = edwards::Point::rand(rng, params).mul_by_cofactor(params); + let pk_d: edwards::Point = edwards::Point::rand(rng, params).mul_by_cofactor(params); let commitment_randomness: fs::Fs = rng.gen(); let esk: fs::Fs = rng.gen(); @@ -546,7 +567,7 @@ fn test_output_circuit_with_bls12_381() { value: Some(value), value_randomness: Some(value_randomness), g_d: Some(g_d.clone()), - p_d: Some(p_d.clone()), + pk_d: Some(pk_d.clone()), commitment_randomness: Some(commitment_randomness), esk: Some(esk.clone()) }; @@ -556,5 +577,32 @@ fn test_output_circuit_with_bls12_381() { assert!(cs.is_satisfied()); assert_eq!(cs.num_constraints(), 7827); assert_eq!(cs.hash(), "2896f259ad7a50c83604976ee9362358396d547b70f2feaf91d82d287e4ffc1d"); + + let expected_cm = ::primitives::Note { + value: value, + g_d: g_d.clone(), + pk_d: pk_d.clone(), + r: commitment_randomness.clone() + }.cm(params); + + let expected_value_cm = params.generator(FixedGenerators::ValueCommitmentValue) + .mul(fs::FsRepr::from(value), params) + .add( + ¶ms.generator(FixedGenerators::ValueCommitmentRandomness) + .mul(value_randomness, params), + params + ); + let expected_value_cm_xy = expected_value_cm.into_xy(); + + let expected_epk = g_d.mul(esk, params); + let expected_epk_xy = expected_epk.into_xy(); + + assert_eq!(cs.num_inputs(), 6); + assert_eq!(cs.get_input(0, "ONE"), Fr::one()); + assert_eq!(cs.get_input(1, "value commitment/x/input variable"), expected_value_cm_xy.0); + assert_eq!(cs.get_input(2, "value commitment/y/input variable"), expected_value_cm_xy.1); + assert_eq!(cs.get_input(3, "epk/x/input variable"), expected_epk_xy.0); + assert_eq!(cs.get_input(4, "epk/y/input variable"), expected_epk_xy.1); + assert_eq!(cs.get_input(5, "commitment/input variable"), expected_cm); } } diff --git a/src/circuit/test/mod.rs b/src/circuit/test/mod.rs index 01fda4a1c..9728e04a6 100644 --- a/src/circuit/test/mod.rs +++ b/src/circuit/test/mod.rs @@ -294,6 +294,19 @@ impl TestConstraintSystem { } } + pub fn num_inputs(&self) -> usize { + self.inputs.len() + } + + pub fn get_input(&mut self, index: usize, path: &str) -> E::Fr + { + let (assignment, name) = self.inputs[index].clone(); + + assert_eq!(path, name); + + assignment + } + pub fn get(&mut self, path: &str) -> E::Fr { match self.named_objects.get(path) { diff --git a/src/primitives/mod.rs b/src/primitives/mod.rs index 7a990920b..237b633b3 100644 --- a/src/primitives/mod.rs +++ b/src/primitives/mod.rs @@ -5,7 +5,7 @@ use pedersen_hash::{ use byteorder::{ BigEndian, - ByteOrder + WriteBytesExt }; use jubjub::{ @@ -35,7 +35,7 @@ impl Note { let mut note_contents = vec![]; // Write the value in big endian - BigEndian::write_u64(&mut note_contents, self.value); + (&mut note_contents).write_u64::(self.value).unwrap(); // Write g_d self.g_d.write(&mut note_contents).unwrap(); @@ -43,6 +43,8 @@ impl Note { // 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, From b998190f9ea4b2b3ce4228a167857253e2e3a880 Mon Sep 17 00:00:00 2001 From: Sean Bowe Date: Wed, 7 Mar 2018 23:45:08 -0700 Subject: [PATCH 04/15] Disable this, for now. --- examples/{bench.rs => bench.rs.disabled} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename examples/{bench.rs => bench.rs.disabled} (100%) diff --git a/examples/bench.rs b/examples/bench.rs.disabled similarity index 100% rename from examples/bench.rs rename to examples/bench.rs.disabled From 25a8050df8bfddc9b55360c84d5570de7805d310 Mon Sep 17 00:00:00 2001 From: Sean Bowe Date: Wed, 7 Mar 2018 23:59:04 -0700 Subject: [PATCH 05/15] Remaining tests for input circuit --- src/circuit/mod.rs | 90 +++++++++++++++++++++++---- src/lib.rs | 2 + src/primitives/mod.rs | 137 ++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 212 insertions(+), 17 deletions(-) diff --git a/src/circuit/mod.rs b/src/circuit/mod.rs index a81d6c707..b407b69ec 100644 --- a/src/circuit/mod.rs +++ b/src/circuit/mod.rs @@ -22,7 +22,6 @@ use bellman::{ use jubjub::{ JubjubEngine, - Unknown, PrimeOrder, FixedGenerators, edwards @@ -55,9 +54,9 @@ pub struct Spend<'a, E: JubjubEngine> { /// The public key that will be re-randomized for /// use as a nullifier and signing key for the /// transaction. - pub ak: Option>, + pub ak: Option>, /// The diversified base used to compute pk_d. - pub g_d: Option>, + pub g_d: Option>, /// The randomness used to hide the note commitment data pub commitment_randomness: Option, /// The authentication path of the commitment in the tree @@ -482,7 +481,7 @@ impl<'a, E: JubjubEngine> Circuit for Output<'a, E> { #[test] fn test_input_circuit_with_bls12_381() { - use pairing::{Field}; + use pairing::{Field, BitIterator}; use pairing::bls12_381::*; use rand::{SeedableRng, Rng, XorShiftRng}; use ::circuit::test::*; @@ -495,10 +494,34 @@ fn test_input_circuit_with_bls12_381() { let value: u64 = 1; let value_randomness: fs::Fs = rng.gen(); - let ak: edwards::Point = edwards::Point::rand(rng, params); - let g_d: edwards::Point = edwards::Point::rand(rng, params); - let commitment_randomness: fs::Fs = rng.gen(); + let rsk: fs::Fs = rng.gen(); + let ak: edwards::Point = edwards::Point::rand(rng, params).mul_by_cofactor(params); + + let proof_generation_key = ::primitives::ProofGenerationKey { + ak: ak.clone(), + rsk: rsk.clone() + }; + + let viewing_key = proof_generation_key.into_viewing_key(params); + + let payment_address; + + loop { + let diversifier = ::primitives::Diversifier(rng.gen()); + + if let Some(p) = viewing_key.into_payment_address( + diversifier, + params + ) + { + payment_address = p; + break; + } + } + + let g_d = payment_address.diversifier.g_d(params).unwrap(); + let commitment_randomness: fs::Fs = rng.gen(); let auth_path = vec![Some((rng.gen(), rng.gen())); tree_depth]; { @@ -510,7 +533,7 @@ fn test_input_circuit_with_bls12_381() { value_randomness: Some(value_randomness), rsk: Some(rsk), ak: Some(ak), - g_d: Some(g_d), + g_d: Some(g_d.clone()), commitment_randomness: Some(commitment_randomness), auth_path: auth_path.clone() }; @@ -535,9 +558,54 @@ fn test_input_circuit_with_bls12_381() { assert_eq!(cs.get_input(1, "value commitment/x/input variable"), expected_value_cm_xy.0); assert_eq!(cs.get_input(2, "value commitment/y/input variable"), expected_value_cm_xy.1); - cs.get_input(3, "anchor/input variable"); - cs.get_input(4, "nullifier/x/input variable"); - cs.get_input(5, "nullifier/y/input variable"); + let note = ::primitives::Note { + value: value, + g_d: g_d.clone(), + pk_d: payment_address.pk_d.clone(), + r: commitment_randomness.clone() + }; + + let mut position = 0u64; + let mut cur = note.cm(params); + + assert_eq!(cs.get("randomization of note commitment/x3/num"), cur); + + for (i, val) in auth_path.into_iter().enumerate() + { + let (uncle, b) = val.unwrap(); + + let mut lhs = cur; + let mut rhs = uncle; + + if b { + ::std::mem::swap(&mut lhs, &mut rhs); + } + + let mut lhs: Vec = BitIterator::new(lhs.into_repr()).collect(); + let mut rhs: Vec = BitIterator::new(rhs.into_repr()).collect(); + + lhs.reverse(); + rhs.reverse(); + + cur = ::pedersen_hash::pedersen_hash::( + ::pedersen_hash::Personalization::MerkleTree(i), + lhs.into_iter() + .take(Fr::NUM_BITS as usize) + .chain(rhs.into_iter().take(Fr::NUM_BITS as usize)), + params + ).into_xy().0; + + if b { + position |= 1 << i; + } + } + + let expected_nf = note.nf(&viewing_key, position, params); + let expected_nf_xy = expected_nf.into_xy(); + + assert_eq!(cs.get_input(3, "anchor/input variable"), cur); + assert_eq!(cs.get_input(4, "nullifier/x/input variable"), expected_nf_xy.0); + assert_eq!(cs.get_input(5, "nullifier/y/input variable"), expected_nf_xy.1); } } diff --git a/src/lib.rs b/src/lib.rs index 686f93a37..100bbb006 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -25,6 +25,8 @@ const PRF_NR_PERSONALIZATION: &'static [u8; 8] = b"WhatTheH"; // Group hash personalizations /// BLAKE2s Personalization for Pedersen hash generators. const PEDERSEN_HASH_GENERATORS_PERSONALIZATION: &'static [u8; 8] = b"PEDERSEN"; +/// BLAKE2s Personalization for the group hash for key diversification +const KEY_DIVERSIFICATION_PERSONALIZATION: &'static [u8; 8] = b"Zcash_gh"; /// BLAKE2s Personalization for the proof generation key base point const PROOF_GENERATION_KEY_BASE_GENERATOR_PERSONALIZATION: &'static [u8; 8] = b"12345678"; /// BLAKE2s Personalization for the note commitment randomness generator diff --git a/src/primitives/mod.rs b/src/primitives/mod.rs index 237b633b3..d5bb5509e 100644 --- a/src/primitives/mod.rs +++ b/src/primitives/mod.rs @@ -1,3 +1,10 @@ +use pairing::{ + PrimeField, + PrimeFieldRepr +}; + +use group_hash::group_hash; + use pedersen_hash::{ pedersen_hash, Personalization @@ -16,6 +23,83 @@ use jubjub::{ FixedGenerators }; +use blake2_rfc::blake2s::Blake2s; + +pub struct ProofGenerationKey { + pub ak: edwards::Point, + pub rsk: E::Fs +} + +impl ProofGenerationKey { + pub fn into_viewing_key(&self, params: &E::Params) -> ViewingKey { + ViewingKey { + ak: self.ak.clone(), + rk: params.generator(FixedGenerators::ProofGenerationKey) + .mul(self.rsk, params) + } + } +} + +pub struct ViewingKey { + pub ak: edwards::Point, + pub rk: edwards::Point +} + +impl ViewingKey { + fn ivk(&self) -> E::Fs { + let mut preimage = [0; 64]; + + self.ak.write(&mut preimage[0..32]).unwrap(); + self.rk.write(&mut preimage[32..64]).unwrap(); + + let mut h = Blake2s::with_params(32, &[], &[], ::CRH_IVK_PERSONALIZATION); + h.update(&preimage); + let mut h = h.finalize().as_ref().to_vec(); + + // Drop the first five bits, so it can be interpreted as a scalar. + h[0] &= 0b0000_0111; + + let mut e = ::Repr::default(); + e.read_be(&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)] +pub struct Diversifier(pub [u8; 11]); + +impl Diversifier { + pub fn g_d( + &self, + params: &E::Params + ) -> Option> + { + group_hash::(&self.0, ::KEY_DIVERSIFICATION_PERSONALIZATION, params) + } +} + +pub struct PaymentAddress { + pub pk_d: edwards::Point, + pub diversifier: Diversifier +} + pub struct Note { /// The value of the note pub value: u64, @@ -28,8 +112,8 @@ pub struct Note { } impl Note { - /// Computes the note commitment - pub fn cm(&self, params: &E::Params) -> E::Fr + /// 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![]; @@ -56,12 +140,53 @@ impl Note { ); // Compute final commitment - let cm = params.generator(FixedGenerators::NoteCommitmentRandomness) - .mul(self.r, params) - .add(&hash_of_contents, params); + 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 + ) -> edwards::Point + { + // Compute cm + position + let cm_plus_position = self + .cm_full_point(params) + .add( + ¶ms.generator(FixedGenerators::NullifierPosition) + .mul(position, params), + params + ); + + // Compute nr = drop_5(BLAKE2s(rk | cm_plus_position)) + let mut nr_preimage = [0u8; 64]; + viewing_key.rk.write(&mut nr_preimage[0..32]).unwrap(); + cm_plus_position.write(&mut nr_preimage[32..64]).unwrap(); + let mut h = Blake2s::with_params(32, &[], &[], ::PRF_NR_PERSONALIZATION); + h.update(&nr_preimage); + let mut h = h.finalize().as_ref().to_vec(); + + // Drop the first five bits, so it can be interpreted as a scalar. + h[0] &= 0b0000_0111; + + let mut e = ::Repr::default(); + e.read_be(&h[..]).unwrap(); + + let nr = E::Fs::from_repr(e).expect("should be a valid scalar"); + + viewing_key.ak.mul(nr, params) + } + + /// 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. - cm.into_xy().0 + self.cm_full_point(params).into_xy().0 } } From c7c8d3c039e060a4b9293ea36398f649ebf684e4 Mon Sep 17 00:00:00 2001 From: Sean Bowe Date: Thu, 8 Mar 2018 00:06:53 -0700 Subject: [PATCH 06/15] Move personalization constants to submodule. --- src/circuit/mod.rs | 6 ++++-- src/constants.rs | 23 +++++++++++++++++++++++ src/jubjub/mod.rs | 18 ++++++++++-------- src/lib.rs | 26 +------------------------- src/primitives/mod.rs | 8 +++++--- 5 files changed, 43 insertions(+), 38 deletions(-) create mode 100644 src/constants.rs diff --git a/src/circuit/mod.rs b/src/circuit/mod.rs index b407b69ec..42ec6bf36 100644 --- a/src/circuit/mod.rs +++ b/src/circuit/mod.rs @@ -27,6 +27,8 @@ use jubjub::{ edwards }; +use constants; + trait Assignment { fn get(&self) -> Result<&T, SynthesisError>; } @@ -157,7 +159,7 @@ impl<'a, E: JubjubEngine> Circuit for Spend<'a, E> { let mut ivk = blake2s::blake2s( cs.namespace(|| "computation of ivk"), &vk, - ::CRH_IVK_PERSONALIZATION + constants::CRH_IVK_PERSONALIZATION )?; // Little endian bit order @@ -301,7 +303,7 @@ impl<'a, E: JubjubEngine> Circuit for Spend<'a, E> { let mut rho = blake2s::blake2s( cs.namespace(|| "rho computation"), &rho_preimage, - ::PRF_NR_PERSONALIZATION + constants::PRF_NR_PERSONALIZATION )?; // Little endian bit order diff --git a/src/constants.rs b/src/constants.rs new file mode 100644 index 000000000..4b1e1ac52 --- /dev/null +++ b/src/constants.rs @@ -0,0 +1,23 @@ +// BLAKE2s invocation personalizations +/// BLAKE2s Personalization for CRH^ivk = BLAKE2s(ak | rk) +pub const CRH_IVK_PERSONALIZATION: &'static [u8; 8] = b"Zcashivk"; +/// BLAKE2s Personalization for PRF^nr = BLAKE2s(rk | cm + position) +pub const PRF_NR_PERSONALIZATION: &'static [u8; 8] = b"WhatTheH"; + +// Group hash personalizations +/// BLAKE2s Personalization for Pedersen hash generators. +pub const PEDERSEN_HASH_GENERATORS_PERSONALIZATION: &'static [u8; 8] = b"PEDERSEN"; +/// BLAKE2s Personalization for the group hash for key diversification +pub const KEY_DIVERSIFICATION_PERSONALIZATION: &'static [u8; 8] = b"Zcash_gh"; +/// BLAKE2s Personalization for the proof generation key base point +pub const PROOF_GENERATION_KEY_BASE_GENERATOR_PERSONALIZATION: &'static [u8; 8] = b"12345678"; +/// BLAKE2s Personalization for the note commitment randomness generator +pub const NOTE_COMMITMENT_RANDOMNESS_GENERATOR_PERSONALIZATION: &'static [u8; 8] = b"abcdefgh"; +/// BLAKE2s Personalization for the nullifier position generator (for PRF^nr) +pub const NULLIFIER_POSITION_IN_TREE_GENERATOR_PERSONALIZATION: &'static [u8; 8] = b"nfnfnfnf"; +/// BLAKE2s Personalization for the value commitment generator for the value +pub const VALUE_COMMITMENT_VALUE_GENERATOR_PERSONALIZATION: &'static [u8; 8] = b"45u8gh45"; +/// BLAKE2s Personalization for the value commitment randomness generator +pub const VALUE_COMMITMENT_RANDOMNESS_GENERATOR_PERSONALIZATION: &'static [u8; 8] = b"11111111"; +/// BLAKE2s Personalization for the spending key base point +pub const SPENDING_KEY_GENERATOR_PERSONALIZATION: &'static [u8; 8] = b"sksksksk"; diff --git a/src/jubjub/mod.rs b/src/jubjub/mod.rs index 9c4c864c7..7e1374baa 100644 --- a/src/jubjub/mod.rs +++ b/src/jubjub/mod.rs @@ -24,7 +24,9 @@ use pairing::{ SqrtField }; -use super::group_hash::group_hash; +use group_hash::group_hash; + +use constants; use pairing::bls12_381::{ Bls12, @@ -188,7 +190,7 @@ impl JubjubBls12 { let mut pedersen_hash_generators = vec![]; while pedersen_hash_generators.len() < 5 { - let gh = group_hash(&[cur], ::PEDERSEN_HASH_GENERATORS_PERSONALIZATION, &tmp); + let gh = group_hash(&[cur], constants::PEDERSEN_HASH_GENERATORS_PERSONALIZATION, &tmp); // We don't want to overflow and start reusing generators assert!(cur != u8::max_value()); cur += 1; @@ -228,22 +230,22 @@ impl JubjubBls12 { for c in 0..(FixedGenerators::Max as usize) { let p = match c { c if c == (FixedGenerators::ProofGenerationKey as usize) => { - ::PROOF_GENERATION_KEY_BASE_GENERATOR_PERSONALIZATION + constants::PROOF_GENERATION_KEY_BASE_GENERATOR_PERSONALIZATION }, c if c == (FixedGenerators::NoteCommitmentRandomness as usize) => { - ::NOTE_COMMITMENT_RANDOMNESS_GENERATOR_PERSONALIZATION + constants::NOTE_COMMITMENT_RANDOMNESS_GENERATOR_PERSONALIZATION }, c if c == (FixedGenerators::NullifierPosition as usize) => { - ::NULLIFIER_POSITION_IN_TREE_GENERATOR_PERSONALIZATION + constants::NULLIFIER_POSITION_IN_TREE_GENERATOR_PERSONALIZATION }, c if c == (FixedGenerators::ValueCommitmentValue as usize) => { - ::VALUE_COMMITMENT_VALUE_GENERATOR_PERSONALIZATION + constants::VALUE_COMMITMENT_VALUE_GENERATOR_PERSONALIZATION }, c if c == (FixedGenerators::ValueCommitmentRandomness as usize) => { - ::VALUE_COMMITMENT_RANDOMNESS_GENERATOR_PERSONALIZATION + constants::VALUE_COMMITMENT_RANDOMNESS_GENERATOR_PERSONALIZATION }, c if c == (FixedGenerators::SpendingKeyGenerator as usize) => { - ::SPENDING_KEY_GENERATOR_PERSONALIZATION + constants::SPENDING_KEY_GENERATOR_PERSONALIZATION }, _ => unreachable!() }; diff --git a/src/lib.rs b/src/lib.rs index 100bbb006..2a5230b3e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,7 +3,6 @@ extern crate bellman; extern crate blake2_rfc; extern crate digest; extern crate rand; - extern crate byteorder; #[cfg(test)] @@ -15,27 +14,4 @@ pub mod circuit; pub mod group_hash; pub mod pedersen_hash; pub mod primitives; - -// BLAKE2s invocation personalizations -/// BLAKE2s Personalization for CRH^ivk = BLAKE2s(ak | rk) -const CRH_IVK_PERSONALIZATION: &'static [u8; 8] = b"Zcashivk"; -/// BLAKE2s Personalization for PRF^nr = BLAKE2s(rk | cm + position) -const PRF_NR_PERSONALIZATION: &'static [u8; 8] = b"WhatTheH"; - -// Group hash personalizations -/// BLAKE2s Personalization for Pedersen hash generators. -const PEDERSEN_HASH_GENERATORS_PERSONALIZATION: &'static [u8; 8] = b"PEDERSEN"; -/// BLAKE2s Personalization for the group hash for key diversification -const KEY_DIVERSIFICATION_PERSONALIZATION: &'static [u8; 8] = b"Zcash_gh"; -/// BLAKE2s Personalization for the proof generation key base point -const PROOF_GENERATION_KEY_BASE_GENERATOR_PERSONALIZATION: &'static [u8; 8] = b"12345678"; -/// BLAKE2s Personalization for the note commitment randomness generator -const NOTE_COMMITMENT_RANDOMNESS_GENERATOR_PERSONALIZATION: &'static [u8; 8] = b"abcdefgh"; -/// BLAKE2s Personalization for the nullifier position generator (for PRF^nr) -const NULLIFIER_POSITION_IN_TREE_GENERATOR_PERSONALIZATION: &'static [u8; 8] = b"nfnfnfnf"; -/// BLAKE2s Personalization for the value commitment generator for the value -const VALUE_COMMITMENT_VALUE_GENERATOR_PERSONALIZATION: &'static [u8; 8] = b"45u8gh45"; -/// BLAKE2s Personalization for the value commitment randomness generator -const VALUE_COMMITMENT_RANDOMNESS_GENERATOR_PERSONALIZATION: &'static [u8; 8] = b"11111111"; -/// BLAKE2s Personalization for the spending key base point -const SPENDING_KEY_GENERATOR_PERSONALIZATION: &'static [u8; 8] = b"sksksksk"; +mod constants; diff --git a/src/primitives/mod.rs b/src/primitives/mod.rs index d5bb5509e..5aa226072 100644 --- a/src/primitives/mod.rs +++ b/src/primitives/mod.rs @@ -3,6 +3,8 @@ use pairing::{ PrimeFieldRepr }; +use constants; + use group_hash::group_hash; use pedersen_hash::{ @@ -52,7 +54,7 @@ impl ViewingKey { self.ak.write(&mut preimage[0..32]).unwrap(); self.rk.write(&mut preimage[32..64]).unwrap(); - let mut h = Blake2s::with_params(32, &[], &[], ::CRH_IVK_PERSONALIZATION); + let mut h = Blake2s::with_params(32, &[], &[], constants::CRH_IVK_PERSONALIZATION); h.update(&preimage); let mut h = h.finalize().as_ref().to_vec(); @@ -91,7 +93,7 @@ impl Diversifier { params: &E::Params ) -> Option> { - group_hash::(&self.0, ::KEY_DIVERSIFICATION_PERSONALIZATION, params) + group_hash::(&self.0, constants::KEY_DIVERSIFICATION_PERSONALIZATION, params) } } @@ -167,7 +169,7 @@ impl Note { let mut nr_preimage = [0u8; 64]; viewing_key.rk.write(&mut nr_preimage[0..32]).unwrap(); cm_plus_position.write(&mut nr_preimage[32..64]).unwrap(); - let mut h = Blake2s::with_params(32, &[], &[], ::PRF_NR_PERSONALIZATION); + let mut h = Blake2s::with_params(32, &[], &[], constants::PRF_NR_PERSONALIZATION); h.update(&nr_preimage); let mut h = h.finalize().as_ref().to_vec(); From 896b144a7da16781fb8279e62d288ced58a579bb Mon Sep 17 00:00:00 2001 From: Sean Bowe Date: Thu, 8 Mar 2018 00:09:34 -0700 Subject: [PATCH 07/15] Move first block of group hash to constants submodule. --- src/constants.rs | 6 ++++++ src/group_hash.rs | 7 ++----- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/constants.rs b/src/constants.rs index 4b1e1ac52..71b96e1a8 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -1,3 +1,9 @@ +/// First 64 bytes of the BLAKE2s input during group hash. +/// This is chosen to be some random string that we couldn't have anticipated when we designed +/// the algorithm, for rigidity purposes. +/// We deliberately use an ASCII hex string of 32 bytes here. +pub const GH_FIRST_BLOCK: &'static [u8; 64] = b"0000000000000000002ffe76b973aabaff1d1557d79acf2c3795809c83caf580"; + // BLAKE2s invocation personalizations /// BLAKE2s Personalization for CRH^ivk = BLAKE2s(ak | rk) pub const CRH_IVK_PERSONALIZATION: &'static [u8; 8] = b"Zcashivk"; diff --git a/src/group_hash.rs b/src/group_hash.rs index 8dd0df5d8..a545d5fd0 100644 --- a/src/group_hash.rs +++ b/src/group_hash.rs @@ -1,10 +1,7 @@ use jubjub::*; use pairing::*; use blake2_rfc::blake2s::Blake2s; - -/// This is chosen to be some random string that we couldn't have anticipated when we designed -/// the algorithm, for rigidity purposes. -pub const FIRST_BLOCK: &'static [u8; 64] = b"0000000000000000002ffe76b973aabaff1d1557d79acf2c3795809c83caf580"; +use constants; /// Produces a random point in the Jubjub curve. /// The point is guaranteed to be prime order @@ -21,7 +18,7 @@ pub fn group_hash( assert!(E::Fr::NUM_BITS == 255); let mut h = Blake2s::with_params(32, &[], &[], personalization); - h.update(FIRST_BLOCK); + h.update(constants::GH_FIRST_BLOCK); h.update(tag); let mut h = h.finalize().as_ref().to_vec(); assert!(h.len() == 32); From b6ef12b0771c29358278a7712859b39d24e8d6c5 Mon Sep 17 00:00:00 2001 From: Sean Bowe Date: Thu, 8 Mar 2018 00:41:47 -0700 Subject: [PATCH 08/15] General code quality improvements. --- src/circuit/mod.rs | 95 +++++++++++++++++++++++++++++++++++++--------- src/group_hash.rs | 13 ++++++- src/jubjub/mod.rs | 61 ++++++++++++++++++++--------- src/lib.rs | 4 +- 4 files changed, 133 insertions(+), 40 deletions(-) diff --git a/src/circuit/mod.rs b/src/circuit/mod.rs index 42ec6bf36..1efc8acff 100644 --- a/src/circuit/mod.rs +++ b/src/circuit/mod.rs @@ -29,6 +29,13 @@ use jubjub::{ use constants; + +// TODO: This should probably be removed and we +// should use existing helper methods on `Option` +// for mapping with an error. +/// This basically is just an extension to `Option` +/// which allows for a convenient mapping to an +/// error on `None`. trait Assignment { fn get(&self) -> Result<&T, SynthesisError>; } @@ -42,6 +49,7 @@ impl Assignment for Option { } } +/// This is an instance of the `Spend` circuit. pub struct Spend<'a, E: JubjubEngine> { pub params: &'a E::Params, /// Value of the note being spent @@ -75,6 +83,7 @@ impl<'a, E: JubjubEngine> Circuit for Spend<'a, E> { )?; { + // Compute the note value in the exponent let gv = ecc::fixed_base_multiplication( cs.namespace(|| "compute the value in the exponent"), FixedGenerators::ValueCommitmentValue, @@ -82,12 +91,15 @@ impl<'a, E: JubjubEngine> Circuit for Spend<'a, E> { self.params )?; - // Booleanize the randomness + // Booleanize the randomness. This does not ensure + // the bit representation is "in the field" because + // it doesn't matter for security. let hr = boolean::field_into_boolean_vec_le( cs.namespace(|| "hr"), self.value_randomness )?; + // Compute the randomness in the exponent let hr = ecc::fixed_base_multiplication( cs.namespace(|| "computation of randomization for value commitment"), FixedGenerators::ValueCommitmentRandomness, @@ -95,12 +107,14 @@ impl<'a, E: JubjubEngine> Circuit for Spend<'a, E> { self.params )?; + // Compute the Pedersen commitment to the value let gvhr = gv.add( cs.namespace(|| "computation of value commitment"), &hr, self.params )?; + // Expose the commitment as an input to the circuit gvhr.inputize(cs.namespace(|| "value commitment"))?; } @@ -118,6 +132,7 @@ impl<'a, E: JubjubEngine> Circuit for Spend<'a, E> { // demonstrate the prover knows it. If they know a // congruency then that's equivalent. + // Compute rk = [rsk] ProvingPublicKey rk = ecc::fixed_base_multiplication( cs.namespace(|| "computation of rk"), FixedGenerators::ProofGenerationKey, @@ -133,29 +148,40 @@ impl<'a, E: JubjubEngine> Circuit for Spend<'a, E> { self.params )?; + // There are no sensible attacks on small order points + // of ak (that we're aware of!) but it's a cheap check, + // so we do it. ak.assert_not_small_order( cs.namespace(|| "ak not small order"), self.params )?; // Unpack ak and rk for input to BLAKE2s + + // This is the "viewing key" preimage for CRH^ivk let mut vk = vec![]; - let mut rho_preimage = vec![]; vk.extend( ak.repr(cs.namespace(|| "representation of ak"))? ); + + // This is the nullifier randomness preimage for PRF^nr + let mut nr_preimage = vec![]; + + // Extend vk and nr preimages with the representation of + // rk. { let repr_rk = rk.repr( cs.namespace(|| "representation of rk") )?; vk.extend(repr_rk.iter().cloned()); - rho_preimage.extend(repr_rk); + nr_preimage.extend(repr_rk); } assert_eq!(vk.len(), 512); + assert_eq!(nr_preimage.len(), 256); - // Compute the incoming viewing key + // Compute the incoming viewing key ivk let mut ivk = blake2s::blake2s( cs.namespace(|| "computation of ivk"), &vk, @@ -164,16 +190,24 @@ impl<'a, E: JubjubEngine> Circuit for Spend<'a, E> { // Little endian bit order ivk.reverse(); - ivk.truncate(E::Fs::CAPACITY as usize); // drop_5 - // Witness g_d + // drop_5 to ensure it's in the field + ivk.truncate(E::Fs::CAPACITY as usize); + + // Witness g_d. Ensures the point is on the + // curve, but not its order. If the prover + // manages to witness a commitment in the + // tree, then the Output circuit would have + // already guaranteed this. + // TODO: We might as well just perform the + // check again here, since it's not expensive. let g_d = ecc::EdwardsPoint::witness( cs.namespace(|| "witness g_d"), self.g_d, self.params )?; - // Compute pk_d + // Compute pk_d = g_d^ivk let pk_d = g_d.mul( cs.namespace(|| "compute pk_d"), &ivk, @@ -181,6 +215,7 @@ impl<'a, E: JubjubEngine> Circuit for Spend<'a, E> { )?; // Compute note contents + // value (in big endian) followed by g_d and pk_d let mut note_contents = vec![]; note_contents.extend(value_bits.into_iter().rev()); note_contents.extend( @@ -206,12 +241,13 @@ impl<'a, E: JubjubEngine> Circuit for Spend<'a, E> { )?; { - // Booleanize the randomness + // Booleanize the randomness for the note commitment let cmr = boolean::field_into_boolean_vec_le( cs.namespace(|| "cmr"), self.commitment_randomness )?; + // Compute the note commitment randomness in the exponent let cmr = ecc::fixed_base_multiplication( cs.namespace(|| "computation of commitment randomness"), FixedGenerators::NoteCommitmentRandomness, @@ -219,6 +255,8 @@ impl<'a, E: JubjubEngine> Circuit for Spend<'a, E> { self.params )?; + // Randomize the note commitment. Pedersen hashes are not + // themselves hiding commitments. cm = cm.add( cs.namespace(|| "randomization of note commitment"), &cmr, @@ -228,21 +266,30 @@ impl<'a, E: JubjubEngine> Circuit for Spend<'a, E> { let tree_depth = self.auth_path.len(); + // This will store (least significant bit first) + // the position of the note in the tree, for use + // in nullifier computation. let mut position_bits = vec![]; - // Injective encoding. + // This is an injective encoding, as cur is a + // point in the prime order subgroup. let mut cur = cm.get_x().clone(); for (i, e) in self.auth_path.into_iter().enumerate() { let cs = &mut cs.namespace(|| format!("merkle tree hash {}", i)); + // Determines if the current subtree is the "right" leaf at this + // depth of the tree. let cur_is_right = boolean::Boolean::from(boolean::AllocatedBit::alloc( cs.namespace(|| "position bit"), e.map(|e| e.1) )?); + // Push this boolean for nullifier computation later position_bits.push(cur_is_right.clone()); + // Witness the authentication path element adjacent + // at this depth. let path_element = num::AllocatedNum::alloc( cs.namespace(|| "path element"), || { @@ -250,6 +297,7 @@ impl<'a, E: JubjubEngine> Circuit for Spend<'a, E> { } )?; + // Swap the two if the current subtree is on the right let (xl, xr) = num::AllocatedNum::conditionally_reverse( cs.namespace(|| "conditional reversal of preimage"), &cur, @@ -265,6 +313,7 @@ impl<'a, E: JubjubEngine> Circuit for Spend<'a, E> { preimage.extend(xl.into_bits_le(cs.namespace(|| "xl into bits"))?); preimage.extend(xr.into_bits_le(cs.namespace(|| "xr into bits"))?); + // Compute the new subtree value cur = pedersen_hash::pedersen_hash( cs.namespace(|| "computation of pedersen hash"), pedersen_hash::Personalization::MerkleTree(i), @@ -278,7 +327,10 @@ impl<'a, E: JubjubEngine> Circuit for Spend<'a, E> { // Expose the anchor cur.inputize(cs.namespace(|| "anchor"))?; + // Compute the cm + g^position for preventing + // faerie gold attacks { + // Compute the position in the exponent let position = ecc::fixed_base_multiplication( cs.namespace(|| "g^position"), FixedGenerators::NullifierPosition, @@ -286,6 +338,7 @@ impl<'a, E: JubjubEngine> Circuit for Spend<'a, E> { self.params )?; + // Add the position to the commitment cm = cm.add( cs.namespace(|| "faerie gold prevention"), &position, @@ -293,30 +346,36 @@ impl<'a, E: JubjubEngine> Circuit for Spend<'a, E> { )?; } - // Let's compute rho = BLAKE2s(rk || cm + position) - rho_preimage.extend( + // Let's compute nr = BLAKE2s(rk || cm + position) + nr_preimage.extend( cm.repr(cs.namespace(|| "representation of cm"))? ); - assert_eq!(rho_preimage.len(), 512); + assert_eq!(nr_preimage.len(), 512); - let mut rho = blake2s::blake2s( - cs.namespace(|| "rho computation"), - &rho_preimage, + // Compute nr + let mut nr = blake2s::blake2s( + cs.namespace(|| "nr computation"), + &nr_preimage, constants::PRF_NR_PERSONALIZATION )?; // Little endian bit order - rho.reverse(); - rho.truncate(E::Fs::CAPACITY as usize); // drop_5 + nr.reverse(); + + // We want the randomization in the field to + // simplify outside code. + // TODO: This isn't uniformly random. + nr.truncate(E::Fs::CAPACITY as usize); // Compute nullifier let nf = ak.mul( cs.namespace(|| "computation of nf"), - &rho, + &nr, self.params )?; + // Expose the nullifier publicly nf.inputize(cs.namespace(|| "nullifier"))?; Ok(()) diff --git a/src/group_hash.rs b/src/group_hash.rs index a545d5fd0..9e6b2f878 100644 --- a/src/group_hash.rs +++ b/src/group_hash.rs @@ -1,5 +1,14 @@ -use jubjub::*; -use pairing::*; +use jubjub::{ + JubjubEngine, + PrimeOrder, + edwards +}; + +use pairing::{ + PrimeField, + PrimeFieldRepr +}; + use blake2_rfc::blake2s::Blake2s; use constants; diff --git a/src/jubjub/mod.rs b/src/jubjub/mod.rs index 7e1374baa..cff5c4a08 100644 --- a/src/jubjub/mod.rs +++ b/src/jubjub/mod.rs @@ -33,8 +33,14 @@ use pairing::bls12_381::{ Fr }; +/// This is an implementation of the twisted Edwards Jubjub curve. pub mod edwards; + +/// This is an implementation of the birationally equivalent +/// Montgomery curve. pub mod montgomery; + +/// This is an implementation of the scalar field for Jubjub. pub mod fs; #[cfg(test)] @@ -83,7 +89,9 @@ pub enum FixedGenerators { /// offers a scalar field for the embedded curve (Jubjub) /// and some pre-computed parameters. pub trait JubjubEngine: Engine { + /// The scalar field of the Jubjub curve type Fs: PrimeField + SqrtField; + /// The parameters of Jubjub and the Sapling protocol type Params: JubjubParams; } @@ -167,7 +175,7 @@ impl JubjubBls12 { let mut montgomery_2a = montgomery_a; montgomery_2a.double(); - let mut tmp = JubjubBls12 { + let mut tmp_params = JubjubBls12 { // d = -(10240/10241) edwards_d: Fr::from_str("19257038036680949359750312669786877991949435402254120286184196891950884077233").unwrap(), // A = 40962 @@ -177,20 +185,24 @@ impl JubjubBls12 { // scaling factor = sqrt(4 / (a - d)) scale: Fr::from_str("17814886934372412843466061268024708274627479829237077604635722030778476050649").unwrap(), + // We'll initialize these below pedersen_hash_generators: vec![], pedersen_circuit_generators: vec![], - fixed_base_generators: vec![], fixed_base_circuit_generators: vec![], }; // Create the bases for the Pedersen hashes { + // TODO: This currently does not match the specification let mut cur = 0; let mut pedersen_hash_generators = vec![]; + // TODO: This generates more bases for the Pedersen hashes + // than necessary, which is just a performance issue in + // practice. while pedersen_hash_generators.len() < 5 { - let gh = group_hash(&[cur], constants::PEDERSEN_HASH_GENERATORS_PERSONALIZATION, &tmp); + let gh = group_hash(&[cur], constants::PEDERSEN_HASH_GENERATORS_PERSONALIZATION, &tmp_params); // We don't want to overflow and start reusing generators assert!(cur != u8::max_value()); cur += 1; @@ -200,7 +212,20 @@ impl JubjubBls12 { } } - tmp.pedersen_hash_generators = pedersen_hash_generators; + // Check for duplicates, far worse than spec inconsistencies! + for (i, p1) in pedersen_hash_generators.iter().enumerate() { + if p1 == &edwards::Point::zero() { + panic!("Neutral element!"); + } + + for p2 in pedersen_hash_generators.iter().skip(i+1) { + if p1 == p2 { + panic!("Duplicate generator!"); + } + } + } + + tmp_params.pedersen_hash_generators = pedersen_hash_generators; } // Create the bases for other parts of the protocol @@ -211,10 +236,10 @@ impl JubjubBls12 { // Each generator is found by invoking the group hash // on tag 0x00, 0x01, ... until we find a valid result. let find_first_gh = |personalization| { - let mut cur = 0; + let mut cur = 0u8; loop { - let gh = group_hash::(&[cur], personalization, &tmp); + let gh = group_hash::(&[cur], personalization, &tmp_params); // We don't want to overflow. assert!(cur != u8::max_value()); cur += 1; @@ -267,7 +292,7 @@ impl JubjubBls12 { } } - tmp.fixed_base_generators = fixed_base_generators; + tmp_params.fixed_base_generators = fixed_base_generators; } // Create the 2-bit window table lookups for each 4-bit @@ -276,10 +301,10 @@ impl JubjubBls12 { let mut pedersen_circuit_generators = vec![]; // Process each segment - for mut gen in tmp.pedersen_hash_generators.iter().cloned() { - let mut gen = montgomery::Point::from_edwards(&gen, &tmp); + for mut gen in tmp_params.pedersen_hash_generators.iter().cloned() { + let mut gen = montgomery::Point::from_edwards(&gen, &tmp_params); let mut windows = vec![]; - for _ in 0..tmp.pedersen_hash_chunks_per_generator() { + for _ in 0..tmp_params.pedersen_hash_chunks_per_generator() { // Create (x, y) coeffs for this chunk let mut coeffs = vec![]; let mut g = gen.clone(); @@ -287,19 +312,19 @@ impl JubjubBls12 { // coeffs = g, g*2, g*3, g*4 for _ in 0..4 { coeffs.push(g.into_xy().expect("cannot produce O")); - g = g.add(&gen, &tmp); + g = g.add(&gen, &tmp_params); } windows.push(coeffs); // Our chunks are separated by 2 bits to prevent overlap. for _ in 0..4 { - gen = gen.double(&tmp); + gen = gen.double(&tmp_params); } } pedersen_circuit_generators.push(windows); } - tmp.pedersen_circuit_generators = pedersen_circuit_generators; + tmp_params.pedersen_circuit_generators = pedersen_circuit_generators; } // Create the 3-bit window table lookups for fixed-base @@ -307,14 +332,14 @@ impl JubjubBls12 { { let mut fixed_base_circuit_generators = vec![]; - for mut gen in tmp.fixed_base_generators.iter().cloned() { + for mut gen in tmp_params.fixed_base_generators.iter().cloned() { let mut windows = vec![]; - for _ in 0..tmp.fixed_base_chunks_per_generator() { + for _ in 0..tmp_params.fixed_base_chunks_per_generator() { let mut coeffs = vec![(Fr::zero(), Fr::one())]; let mut g = gen.clone(); for _ in 0..7 { coeffs.push(g.into_xy()); - g = g.add(&gen, &tmp); + g = g.add(&gen, &tmp_params); } windows.push(coeffs); @@ -324,10 +349,10 @@ impl JubjubBls12 { fixed_base_circuit_generators.push(windows); } - tmp.fixed_base_circuit_generators = fixed_base_circuit_generators; + tmp_params.fixed_base_circuit_generators = fixed_base_circuit_generators; } - tmp + tmp_params } } diff --git a/src/lib.rs b/src/lib.rs index 2a5230b3e..0f5fded65 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -10,8 +10,8 @@ extern crate byteorder; extern crate hex_literal; pub mod jubjub; -pub mod circuit; pub mod group_hash; +pub mod circuit; pub mod pedersen_hash; pub mod primitives; -mod constants; +pub mod constants; From f61cc88a71ec428fd906d84c13becbc5c521c6dc Mon Sep 17 00:00:00 2001 From: Sean Bowe Date: Thu, 8 Mar 2018 00:51:53 -0700 Subject: [PATCH 09/15] More comment improvements. --- src/circuit/mod.rs | 35 ++++++++++++++++++++++++++++++++--- 1 file changed, 32 insertions(+), 3 deletions(-) diff --git a/src/circuit/mod.rs b/src/circuit/mod.rs index 1efc8acff..be115abf7 100644 --- a/src/circuit/mod.rs +++ b/src/circuit/mod.rs @@ -409,6 +409,7 @@ impl<'a, E: JubjubEngine> Circuit for Output<'a, E> { )?; { + // Compute the note value in the exponent let gv = ecc::fixed_base_multiplication( cs.namespace(|| "compute the value in the exponent"), FixedGenerators::ValueCommitmentValue, @@ -416,12 +417,15 @@ impl<'a, E: JubjubEngine> Circuit for Output<'a, E> { self.params )?; - // Booleanize the randomness + // Booleanize the randomness. This does not ensure + // the bit representation is "in the field" because + // it doesn't matter for security. let hr = boolean::field_into_boolean_vec_le( cs.namespace(|| "hr"), self.value_randomness )?; + // Compute the randomness in the exponent let hr = ecc::fixed_base_multiplication( cs.namespace(|| "computation of randomization for value commitment"), FixedGenerators::ValueCommitmentRandomness, @@ -429,48 +433,66 @@ impl<'a, E: JubjubEngine> Circuit for Output<'a, E> { self.params )?; + // Compute the Pedersen commitment to the value let gvhr = gv.add( cs.namespace(|| "computation of value commitment"), &hr, self.params )?; + // Expose the commitment as an input to the circuit gvhr.inputize(cs.namespace(|| "value commitment"))?; } - // Let's start to construct our note + // Let's start to construct our note, which contains + // value (big endian) let mut note_contents = vec![]; note_contents.extend(value_bits.into_iter().rev()); // Let's deal with g_d { + // Prover witnesses g_d, ensuring it's on the + // curve. let g_d = ecc::EdwardsPoint::witness( cs.namespace(|| "witness g_d"), self.g_d, self.params )?; + // g_d is ensured to be large order. The relationship + // between g_d and pk_d ultimately binds ivk to the + // note. If this were a small order point, it would + // not do this correctly, and the prover could + // double-spend by finding random ivk's that satisfy + // the relationship. + // + // Further, if it were small order, epk would be + // small order too! g_d.assert_not_small_order( cs.namespace(|| "g_d not small order"), self.params )?; + // Extend our note contents with the representation of + // g_d. note_contents.extend( g_d.repr(cs.namespace(|| "representation of g_d"))? ); - // Compute epk from esk + // Booleanize our ephemeral secret key let esk = boolean::field_into_boolean_vec_le( cs.namespace(|| "esk"), self.esk )?; + // Create the ephemeral public key from g_d. let epk = g_d.mul( cs.namespace(|| "epk computation"), &esk, self.params )?; + // Expose epk publicly. epk.inputize(cs.namespace(|| "epk"))?; } @@ -478,18 +500,23 @@ impl<'a, E: JubjubEngine> Circuit for Output<'a, E> { // essentially allow the prover to witness any 256 bits // they would like. { + // Just grab pk_d from the witness let pk_d = self.pk_d.map(|e| e.into_xy()); + // Witness the y-coordinate, encoded as little + // endian bits (to match the representation) let y_contents = boolean::field_into_boolean_vec_le( cs.namespace(|| "pk_d bits of y"), pk_d.map(|e| e.1) )?; + // Witness the sign bit let sign_bit = boolean::Boolean::from(boolean::AllocatedBit::alloc( cs.namespace(|| "pk_d bit of x"), pk_d.map(|e| e.0.into_repr().is_odd()) )?); + // Extend the note with pk_d representation note_contents.extend(y_contents); note_contents.push(sign_bit); } @@ -516,6 +543,7 @@ impl<'a, E: JubjubEngine> Circuit for Output<'a, E> { self.commitment_randomness )?; + // Compute the note commitment randomness in the exponent let cmr = ecc::fixed_base_multiplication( cs.namespace(|| "computation of commitment randomness"), FixedGenerators::NoteCommitmentRandomness, @@ -523,6 +551,7 @@ impl<'a, E: JubjubEngine> Circuit for Output<'a, E> { self.params )?; + // Randomize our note commitment cm = cm.add( cs.namespace(|| "randomization of note commitment"), &cmr, From d6d86737c8b6b1c19de9c23b9c3ea81302c351ca Mon Sep 17 00:00:00 2001 From: Sean Bowe Date: Thu, 8 Mar 2018 01:01:00 -0700 Subject: [PATCH 10/15] Remove some code duplication for value commitment witnessing. --- src/circuit/mod.rs | 152 ++++++++++++++++++++------------------------- 1 file changed, 68 insertions(+), 84 deletions(-) diff --git a/src/circuit/mod.rs b/src/circuit/mod.rs index be115abf7..3c1826ec0 100644 --- a/src/circuit/mod.rs +++ b/src/circuit/mod.rs @@ -49,6 +49,60 @@ impl Assignment for Option { } } +/// Exposes a Pedersen commitment to the value as an +/// input to the circuit +fn expose_value_commitment( + mut cs: CS, + value: Option, + randomness: Option, + params: &E::Params +) -> Result, SynthesisError> + where E: JubjubEngine, + CS: ConstraintSystem +{ + // Booleanize the value into little-endian bit order + let value_bits = boolean::u64_into_boolean_vec_le( + cs.namespace(|| "value"), + value + )?; + + // Compute the note value in the exponent + let gv = ecc::fixed_base_multiplication( + cs.namespace(|| "compute the value in the exponent"), + FixedGenerators::ValueCommitmentValue, + &value_bits, + params + )?; + + // Booleanize the randomness. This does not ensure + // the bit representation is "in the field" because + // it doesn't matter for security. + let hr = boolean::field_into_boolean_vec_le( + cs.namespace(|| "hr"), + randomness + )?; + + // Compute the randomness in the exponent + let hr = ecc::fixed_base_multiplication( + cs.namespace(|| "computation of randomization for value commitment"), + FixedGenerators::ValueCommitmentRandomness, + &hr, + params + )?; + + // Compute the Pedersen commitment to the value + let gvhr = gv.add( + cs.namespace(|| "computation of value commitment"), + &hr, + params + )?; + + // Expose the commitment as an input to the circuit + gvhr.inputize(cs.namespace(|| "commitment point"))?; + + Ok(value_bits) +} + /// This is an instance of the `Spend` circuit. pub struct Spend<'a, E: JubjubEngine> { pub params: &'a E::Params, @@ -76,48 +130,13 @@ pub struct Spend<'a, E: JubjubEngine> { impl<'a, E: JubjubEngine> Circuit for Spend<'a, E> { fn synthesize>(self, cs: &mut CS) -> Result<(), SynthesisError> { - // Booleanize the value into little-endian bit order - let value_bits = boolean::u64_into_boolean_vec_le( - cs.namespace(|| "value"), - self.value + let value_bits = expose_value_commitment( + cs.namespace(|| "value commitment"), + self.value, + self.value_randomness, + self.params )?; - { - // Compute the note value in the exponent - let gv = ecc::fixed_base_multiplication( - cs.namespace(|| "compute the value in the exponent"), - FixedGenerators::ValueCommitmentValue, - &value_bits, - self.params - )?; - - // Booleanize the randomness. This does not ensure - // the bit representation is "in the field" because - // it doesn't matter for security. - let hr = boolean::field_into_boolean_vec_le( - cs.namespace(|| "hr"), - self.value_randomness - )?; - - // Compute the randomness in the exponent - let hr = ecc::fixed_base_multiplication( - cs.namespace(|| "computation of randomization for value commitment"), - FixedGenerators::ValueCommitmentRandomness, - &hr, - self.params - )?; - - // Compute the Pedersen commitment to the value - let gvhr = gv.add( - cs.namespace(|| "computation of value commitment"), - &hr, - self.params - )?; - - // Expose the commitment as an input to the circuit - gvhr.inputize(cs.namespace(|| "value commitment"))?; - } - // Compute rk = [rsk] ProvingPublicKey let rk; { @@ -402,48 +421,13 @@ pub struct Output<'a, E: JubjubEngine> { impl<'a, E: JubjubEngine> Circuit for Output<'a, E> { fn synthesize>(self, cs: &mut CS) -> Result<(), SynthesisError> { - // Booleanize the value into little-endian bit order - let value_bits = boolean::u64_into_boolean_vec_le( - cs.namespace(|| "value"), - self.value + let value_bits = expose_value_commitment( + cs.namespace(|| "value commitment"), + self.value, + self.value_randomness, + self.params )?; - { - // Compute the note value in the exponent - let gv = ecc::fixed_base_multiplication( - cs.namespace(|| "compute the value in the exponent"), - FixedGenerators::ValueCommitmentValue, - &value_bits, - self.params - )?; - - // Booleanize the randomness. This does not ensure - // the bit representation is "in the field" because - // it doesn't matter for security. - let hr = boolean::field_into_boolean_vec_le( - cs.namespace(|| "hr"), - self.value_randomness - )?; - - // Compute the randomness in the exponent - let hr = ecc::fixed_base_multiplication( - cs.namespace(|| "computation of randomization for value commitment"), - FixedGenerators::ValueCommitmentRandomness, - &hr, - self.params - )?; - - // Compute the Pedersen commitment to the value - let gvhr = gv.add( - cs.namespace(|| "computation of value commitment"), - &hr, - self.params - )?; - - // Expose the commitment as an input to the circuit - gvhr.inputize(cs.namespace(|| "value commitment"))?; - } - // Let's start to construct our note, which contains // value (big endian) let mut note_contents = vec![]; @@ -645,8 +629,8 @@ fn test_input_circuit_with_bls12_381() { assert_eq!(cs.num_inputs(), 6); assert_eq!(cs.get_input(0, "ONE"), Fr::one()); - assert_eq!(cs.get_input(1, "value commitment/x/input variable"), expected_value_cm_xy.0); - assert_eq!(cs.get_input(2, "value commitment/y/input variable"), expected_value_cm_xy.1); + assert_eq!(cs.get_input(1, "value commitment/commitment point/x/input variable"), expected_value_cm_xy.0); + assert_eq!(cs.get_input(2, "value commitment/commitment point/y/input variable"), expected_value_cm_xy.1); let note = ::primitives::Note { value: value, @@ -757,8 +741,8 @@ fn test_output_circuit_with_bls12_381() { assert_eq!(cs.num_inputs(), 6); assert_eq!(cs.get_input(0, "ONE"), Fr::one()); - assert_eq!(cs.get_input(1, "value commitment/x/input variable"), expected_value_cm_xy.0); - assert_eq!(cs.get_input(2, "value commitment/y/input variable"), expected_value_cm_xy.1); + assert_eq!(cs.get_input(1, "value commitment/commitment point/x/input variable"), expected_value_cm_xy.0); + assert_eq!(cs.get_input(2, "value commitment/commitment point/y/input variable"), expected_value_cm_xy.1); assert_eq!(cs.get_input(3, "epk/x/input variable"), expected_epk_xy.0); assert_eq!(cs.get_input(4, "epk/y/input variable"), expected_epk_xy.1); assert_eq!(cs.get_input(5, "commitment/input variable"), expected_cm); From 3fbbd933cfaf9087b82782022c341f89d11c1e3e Mon Sep 17 00:00:00 2001 From: Sean Bowe Date: Thu, 8 Mar 2018 01:16:21 -0700 Subject: [PATCH 11/15] Simplify value commitment abstraction. --- src/circuit/mod.rs | 93 +++++++++++++++++++++---------------------- src/primitives/mod.rs | 22 ++++++++++ 2 files changed, 67 insertions(+), 48 deletions(-) diff --git a/src/circuit/mod.rs b/src/circuit/mod.rs index 3c1826ec0..c4f0db251 100644 --- a/src/circuit/mod.rs +++ b/src/circuit/mod.rs @@ -29,6 +29,10 @@ use jubjub::{ use constants; +use primitives::{ + ValueCommitment +}; + // TODO: This should probably be removed and we // should use existing helper methods on `Option` @@ -53,8 +57,7 @@ impl Assignment for Option { /// input to the circuit fn expose_value_commitment( mut cs: CS, - value: Option, - randomness: Option, + value_commitment: Option>, params: &E::Params ) -> Result, SynthesisError> where E: JubjubEngine, @@ -63,7 +66,7 @@ fn expose_value_commitment( // Booleanize the value into little-endian bit order let value_bits = boolean::u64_into_boolean_vec_le( cs.namespace(|| "value"), - value + value_commitment.as_ref().map(|c| c.value) )?; // Compute the note value in the exponent @@ -79,7 +82,7 @@ fn expose_value_commitment( // it doesn't matter for security. let hr = boolean::field_into_boolean_vec_le( cs.namespace(|| "hr"), - randomness + value_commitment.as_ref().map(|c| c.randomness) )?; // Compute the randomness in the exponent @@ -106,23 +109,27 @@ fn expose_value_commitment( /// This is an instance of the `Spend` circuit. pub struct Spend<'a, E: JubjubEngine> { pub params: &'a E::Params, - /// Value of the note being spent - pub value: Option, - /// Randomness that will hide the value - pub value_randomness: Option, + + /// Pedersen commitment to the value being spent + pub value_commitment: Option>, + /// Key which allows the proof to be constructed /// as defense-in-depth against a flaw in the /// protocol that would otherwise be exploitable /// by a holder of a viewing key. pub rsk: Option, + /// The public key that will be re-randomized for /// use as a nullifier and signing key for the /// transaction. pub ak: Option>, + /// The diversified base used to compute pk_d. pub g_d: Option>, + /// The randomness used to hide the note commitment data pub commitment_randomness: Option, + /// The authentication path of the commitment in the tree pub auth_path: Vec> } @@ -132,8 +139,7 @@ impl<'a, E: JubjubEngine> Circuit for Spend<'a, E> { { let value_bits = expose_value_commitment( cs.namespace(|| "value commitment"), - self.value, - self.value_randomness, + self.value_commitment, self.params )?; @@ -404,16 +410,19 @@ impl<'a, E: JubjubEngine> Circuit for Spend<'a, E> { /// This is an output circuit instance. pub struct Output<'a, E: JubjubEngine> { pub params: &'a E::Params, - /// Value of the note being created - pub value: Option, - /// Randomness that will hide the value - pub value_randomness: Option, + + /// Pedersen commitment to the value being spent + pub value_commitment: Option>, + /// The diversified base, computed by GH(d) pub g_d: Option>, + /// The diversified address point, computed by GH(d)^ivk pub pk_d: Option>, + /// The randomness used to hide the note commitment data pub commitment_randomness: Option, + /// The ephemeral secret key for DH with recipient pub esk: Option } @@ -423,8 +432,7 @@ impl<'a, E: JubjubEngine> Circuit for Output<'a, E> { { let value_bits = expose_value_commitment( cs.namespace(|| "value commitment"), - self.value, - self.value_randomness, + self.value_commitment, self.params )?; @@ -559,15 +567,17 @@ fn test_input_circuit_with_bls12_381() { use pairing::bls12_381::*; use rand::{SeedableRng, Rng, XorShiftRng}; use ::circuit::test::*; - use jubjub::{JubjubParams, JubjubBls12, fs}; + use jubjub::{JubjubBls12, fs}; let params = &JubjubBls12::new(); let rng = &mut XorShiftRng::from_seed([0x3dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); let tree_depth = 32; - let value: u64 = 1; - let value_randomness: fs::Fs = rng.gen(); + let value_commitment = ValueCommitment { + value: rng.gen(), + randomness: rng.gen() + }; let rsk: fs::Fs = rng.gen(); let ak: edwards::Point = edwards::Point::rand(rng, params).mul_by_cofactor(params); @@ -603,8 +613,7 @@ fn test_input_circuit_with_bls12_381() { let instance = Spend { params: params, - value: Some(value), - value_randomness: Some(value_randomness), + value_commitment: Some(value_commitment.clone()), rsk: Some(rsk), ak: Some(ak), g_d: Some(g_d.clone()), @@ -618,22 +627,15 @@ fn test_input_circuit_with_bls12_381() { assert_eq!(cs.num_constraints(), 101550); assert_eq!(cs.hash(), "3cc6d9383ca882ae3666267618e826e9d51a3177fc89ef6d42d9f63b84179f77"); - let expected_value_cm = params.generator(FixedGenerators::ValueCommitmentValue) - .mul(fs::FsRepr::from(value), params) - .add( - ¶ms.generator(FixedGenerators::ValueCommitmentRandomness) - .mul(value_randomness, params), - params - ); - let expected_value_cm_xy = expected_value_cm.into_xy(); + let expected_value_cm = value_commitment.cm(params).into_xy(); assert_eq!(cs.num_inputs(), 6); assert_eq!(cs.get_input(0, "ONE"), Fr::one()); - assert_eq!(cs.get_input(1, "value commitment/commitment point/x/input variable"), expected_value_cm_xy.0); - assert_eq!(cs.get_input(2, "value commitment/commitment point/y/input variable"), expected_value_cm_xy.1); + assert_eq!(cs.get_input(1, "value commitment/commitment point/x/input variable"), expected_value_cm.0); + assert_eq!(cs.get_input(2, "value commitment/commitment point/y/input variable"), expected_value_cm.1); let note = ::primitives::Note { - value: value, + value: value_commitment.value, g_d: g_d.clone(), pk_d: payment_address.pk_d.clone(), r: commitment_randomness.clone() @@ -689,13 +691,16 @@ fn test_output_circuit_with_bls12_381() { use pairing::bls12_381::*; use rand::{SeedableRng, Rng, XorShiftRng}; use ::circuit::test::*; - use jubjub::{JubjubParams, JubjubBls12, fs}; + use jubjub::{JubjubBls12, fs}; let params = &JubjubBls12::new(); let rng = &mut XorShiftRng::from_seed([0x3dbe6258, 0x8d313d76, 0x3237db17, 0xe5bc0654]); - let value: u64 = 1; - let value_randomness: fs::Fs = rng.gen(); + let value_commitment = ValueCommitment { + value: rng.gen(), + randomness: rng.gen() + }; + let g_d: edwards::Point = edwards::Point::rand(rng, params).mul_by_cofactor(params); let pk_d: edwards::Point = edwards::Point::rand(rng, params).mul_by_cofactor(params); let commitment_randomness: fs::Fs = rng.gen(); @@ -706,8 +711,7 @@ fn test_output_circuit_with_bls12_381() { let instance = Output { params: params, - value: Some(value), - value_randomness: Some(value_randomness), + value_commitment: Some(value_commitment.clone()), g_d: Some(g_d.clone()), pk_d: Some(pk_d.clone()), commitment_randomness: Some(commitment_randomness), @@ -721,28 +725,21 @@ fn test_output_circuit_with_bls12_381() { assert_eq!(cs.hash(), "2896f259ad7a50c83604976ee9362358396d547b70f2feaf91d82d287e4ffc1d"); let expected_cm = ::primitives::Note { - value: value, + value: value_commitment.value, g_d: g_d.clone(), pk_d: pk_d.clone(), r: commitment_randomness.clone() }.cm(params); - let expected_value_cm = params.generator(FixedGenerators::ValueCommitmentValue) - .mul(fs::FsRepr::from(value), params) - .add( - ¶ms.generator(FixedGenerators::ValueCommitmentRandomness) - .mul(value_randomness, params), - params - ); - let expected_value_cm_xy = expected_value_cm.into_xy(); + let expected_value_cm = value_commitment.cm(params).into_xy(); let expected_epk = g_d.mul(esk, params); let expected_epk_xy = expected_epk.into_xy(); assert_eq!(cs.num_inputs(), 6); assert_eq!(cs.get_input(0, "ONE"), Fr::one()); - assert_eq!(cs.get_input(1, "value commitment/commitment point/x/input variable"), expected_value_cm_xy.0); - assert_eq!(cs.get_input(2, "value commitment/commitment point/y/input variable"), expected_value_cm_xy.1); + assert_eq!(cs.get_input(1, "value commitment/commitment point/x/input variable"), expected_value_cm.0); + assert_eq!(cs.get_input(2, "value commitment/commitment point/y/input variable"), expected_value_cm.1); assert_eq!(cs.get_input(3, "epk/x/input variable"), expected_epk_xy.0); assert_eq!(cs.get_input(4, "epk/y/input variable"), expected_epk_xy.1); assert_eq!(cs.get_input(5, "commitment/input variable"), expected_cm); diff --git a/src/primitives/mod.rs b/src/primitives/mod.rs index 5aa226072..6c12892d9 100644 --- a/src/primitives/mod.rs +++ b/src/primitives/mod.rs @@ -27,6 +27,28 @@ use jubjub::{ use blake2_rfc::blake2s::Blake2s; +#[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 + ) + } +} + pub struct ProofGenerationKey { pub ak: edwards::Point, pub rsk: E::Fs From 512a394b303efaed12fb16c336a3b754d830496e Mon Sep 17 00:00:00 2001 From: Sean Bowe Date: Thu, 8 Mar 2018 01:36:16 -0700 Subject: [PATCH 12/15] Simplify witness for Spend statement. --- src/circuit/mod.rs | 51 +++++++++++++++++++++++-------------------- src/primitives/mod.rs | 12 ++++++++++ 2 files changed, 39 insertions(+), 24 deletions(-) diff --git a/src/circuit/mod.rs b/src/circuit/mod.rs index c4f0db251..30a84c08c 100644 --- a/src/circuit/mod.rs +++ b/src/circuit/mod.rs @@ -30,7 +30,9 @@ use jubjub::{ use constants; use primitives::{ - ValueCommitment + ValueCommitment, + ProofGenerationKey, + PaymentAddress }; @@ -113,21 +115,14 @@ pub struct Spend<'a, E: JubjubEngine> { /// Pedersen commitment to the value being spent pub value_commitment: Option>, - /// Key which allows the proof to be constructed - /// as defense-in-depth against a flaw in the - /// protocol that would otherwise be exploitable - /// by a holder of a viewing key. - pub rsk: Option, + /// Key required to construct proofs for spending notes + /// for a particular spending key + pub proof_generation_key: Option>, - /// The public key that will be re-randomized for - /// use as a nullifier and signing key for the - /// transaction. - pub ak: Option>, + /// The payment address associated with the note + pub payment_address: Option>, - /// The diversified base used to compute pk_d. - pub g_d: Option>, - - /// The randomness used to hide the note commitment data + /// The randomness of the note commitment pub commitment_randomness: Option, /// The authentication path of the commitment in the tree @@ -149,7 +144,7 @@ impl<'a, E: JubjubEngine> Circuit for Spend<'a, E> { // Witness rsk as bits let rsk = boolean::field_into_boolean_vec_le( cs.namespace(|| "rsk"), - self.rsk + self.proof_generation_key.as_ref().map(|k| k.rsk.clone()) )?; // NB: We don't ensure that the bit representation of rsk @@ -169,7 +164,7 @@ impl<'a, E: JubjubEngine> Circuit for Spend<'a, E> { // Prover witnesses ak (ensures that it's on the curve) let ak = ecc::EdwardsPoint::witness( cs.namespace(|| "ak"), - self.ak, + self.proof_generation_key.as_ref().map(|k| k.ak.clone()), self.params )?; @@ -226,11 +221,20 @@ impl<'a, E: JubjubEngine> Circuit for Spend<'a, E> { // already guaranteed this. // TODO: We might as well just perform the // check again here, since it's not expensive. - let g_d = ecc::EdwardsPoint::witness( - cs.namespace(|| "witness g_d"), - self.g_d, - self.params - )?; + let g_d = { + // This binding is to avoid a weird edge case in Rust's + // ownership/borrowing rules. self is partially moved + // above, but the closure for and_then will have to + // move self (or a reference to self) to reference + // self.params, so we have to copy self.params here. + let params = self.params; + + ecc::EdwardsPoint::witness( + cs.namespace(|| "witness g_d"), + self.payment_address.as_ref().and_then(|a| a.g_d(params)), + self.params + )? + }; // Compute pk_d = g_d^ivk let pk_d = g_d.mul( @@ -614,9 +618,8 @@ fn test_input_circuit_with_bls12_381() { let instance = Spend { params: params, value_commitment: Some(value_commitment.clone()), - rsk: Some(rsk), - ak: Some(ak), - g_d: Some(g_d.clone()), + proof_generation_key: Some(proof_generation_key.clone()), + payment_address: Some(payment_address.clone()), commitment_randomness: Some(commitment_randomness), auth_path: auth_path.clone() }; diff --git a/src/primitives/mod.rs b/src/primitives/mod.rs index 6c12892d9..028c6b738 100644 --- a/src/primitives/mod.rs +++ b/src/primitives/mod.rs @@ -49,6 +49,7 @@ impl ValueCommitment { } } +#[derive(Clone)] pub struct ProofGenerationKey { pub ak: edwards::Point, pub rsk: E::Fs @@ -119,11 +120,22 @@ impl Diversifier { } } +#[derive(Clone)] pub struct PaymentAddress { pub pk_d: edwards::Point, pub diversifier: Diversifier } +impl PaymentAddress { + pub fn g_d( + &self, + params: &E::Params + ) -> Option> + { + self.diversifier.g_d(params) + } +} + pub struct Note { /// The value of the note pub value: u64, From 999840011707b5dfe6323c079c79f4085bff68c5 Mon Sep 17 00:00:00 2001 From: Sean Bowe Date: Thu, 8 Mar 2018 01:37:55 -0700 Subject: [PATCH 13/15] Relocate structs for cleanliness. --- src/circuit/mod.rs | 83 +++++++++++++++++++++++----------------------- 1 file changed, 41 insertions(+), 42 deletions(-) diff --git a/src/circuit/mod.rs b/src/circuit/mod.rs index 30a84c08c..19d0940ff 100644 --- a/src/circuit/mod.rs +++ b/src/circuit/mod.rs @@ -35,7 +35,6 @@ use primitives::{ PaymentAddress }; - // TODO: This should probably be removed and we // should use existing helper methods on `Option` // for mapping with an error. @@ -55,6 +54,47 @@ impl Assignment for Option { } } +/// This is an instance of the `Spend` circuit. +pub struct Spend<'a, E: JubjubEngine> { + pub params: &'a E::Params, + + /// Pedersen commitment to the value being spent + pub value_commitment: Option>, + + /// Key required to construct proofs for spending notes + /// for a particular spending key + pub proof_generation_key: Option>, + + /// The payment address associated with the note + pub payment_address: Option>, + + /// The randomness of the note commitment + pub commitment_randomness: Option, + + /// The authentication path of the commitment in the tree + pub auth_path: Vec> +} + +/// This is an output circuit instance. +pub struct Output<'a, E: JubjubEngine> { + pub params: &'a E::Params, + + /// Pedersen commitment to the value being spent + pub value_commitment: Option>, + + /// The diversified base, computed by GH(d) + pub g_d: Option>, + + /// The diversified address point, computed by GH(d)^ivk + pub pk_d: Option>, + + /// The randomness used to hide the note commitment data + pub commitment_randomness: Option, + + /// The ephemeral secret key for DH with recipient + pub esk: Option +} + /// Exposes a Pedersen commitment to the value as an /// input to the circuit fn expose_value_commitment( @@ -108,27 +148,6 @@ fn expose_value_commitment( Ok(value_bits) } -/// This is an instance of the `Spend` circuit. -pub struct Spend<'a, E: JubjubEngine> { - pub params: &'a E::Params, - - /// Pedersen commitment to the value being spent - pub value_commitment: Option>, - - /// Key required to construct proofs for spending notes - /// for a particular spending key - pub proof_generation_key: Option>, - - /// The payment address associated with the note - pub payment_address: Option>, - - /// The randomness of the note commitment - pub commitment_randomness: Option, - - /// The authentication path of the commitment in the tree - pub auth_path: Vec> -} - impl<'a, E: JubjubEngine> Circuit for Spend<'a, E> { fn synthesize>(self, cs: &mut CS) -> Result<(), SynthesisError> { @@ -411,26 +430,6 @@ impl<'a, E: JubjubEngine> Circuit for Spend<'a, E> { } } -/// This is an output circuit instance. -pub struct Output<'a, E: JubjubEngine> { - pub params: &'a E::Params, - - /// Pedersen commitment to the value being spent - pub value_commitment: Option>, - - /// The diversified base, computed by GH(d) - pub g_d: Option>, - - /// The diversified address point, computed by GH(d)^ivk - pub pk_d: Option>, - - /// The randomness used to hide the note commitment data - pub commitment_randomness: Option, - - /// The ephemeral secret key for DH with recipient - pub esk: Option -} - impl<'a, E: JubjubEngine> Circuit for Output<'a, E> { fn synthesize>(self, cs: &mut CS) -> Result<(), SynthesisError> { From db28ff7ba16159384e6b88032cd173aabd6628ff Mon Sep 17 00:00:00 2001 From: Sean Bowe Date: Thu, 8 Mar 2018 01:49:27 -0700 Subject: [PATCH 14/15] Simplify the Output witness. --- src/circuit/mod.rs | 66 +++++++++++++++++++++++++++---------------- src/primitives/mod.rs | 17 +++++++++++ 2 files changed, 59 insertions(+), 24 deletions(-) diff --git a/src/circuit/mod.rs b/src/circuit/mod.rs index 19d0940ff..828fa3196 100644 --- a/src/circuit/mod.rs +++ b/src/circuit/mod.rs @@ -22,9 +22,7 @@ use bellman::{ use jubjub::{ JubjubEngine, - PrimeOrder, - FixedGenerators, - edwards + FixedGenerators }; use constants; @@ -82,11 +80,8 @@ pub struct Output<'a, E: JubjubEngine> { /// Pedersen commitment to the value being spent pub value_commitment: Option>, - /// The diversified base, computed by GH(d) - pub g_d: Option>, - - /// The diversified address point, computed by GH(d)^ivk - pub pk_d: Option>, + /// The payment address of the recipient + pub payment_address: Option>, /// The randomness used to hide the note commitment data pub commitment_randomness: Option, @@ -446,11 +441,13 @@ impl<'a, E: JubjubEngine> Circuit for Output<'a, E> { // Let's deal with g_d { + let params = self.params; + // Prover witnesses g_d, ensuring it's on the // curve. let g_d = ecc::EdwardsPoint::witness( cs.namespace(|| "witness g_d"), - self.g_d, + self.payment_address.as_ref().and_then(|a| a.g_d(params)), self.params )?; @@ -496,7 +493,7 @@ impl<'a, E: JubjubEngine> Circuit for Output<'a, E> { // they would like. { // Just grab pk_d from the witness - let pk_d = self.pk_d.map(|e| e.into_xy()); + let pk_d = self.payment_address.as_ref().map(|e| e.pk_d.into_xy()); // Witness the y-coordinate, encoded as little // endian bits (to match the representation) @@ -570,7 +567,7 @@ fn test_input_circuit_with_bls12_381() { use pairing::bls12_381::*; use rand::{SeedableRng, Rng, XorShiftRng}; use ::circuit::test::*; - use jubjub::{JubjubBls12, fs}; + use jubjub::{JubjubBls12, fs, edwards}; let params = &JubjubBls12::new(); let rng = &mut XorShiftRng::from_seed([0x3dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); @@ -583,7 +580,7 @@ fn test_input_circuit_with_bls12_381() { }; let rsk: fs::Fs = rng.gen(); - let ak: edwards::Point = edwards::Point::rand(rng, params).mul_by_cofactor(params); + let ak = edwards::Point::rand(rng, params).mul_by_cofactor(params); let proof_generation_key = ::primitives::ProofGenerationKey { ak: ak.clone(), @@ -693,7 +690,7 @@ fn test_output_circuit_with_bls12_381() { use pairing::bls12_381::*; use rand::{SeedableRng, Rng, XorShiftRng}; use ::circuit::test::*; - use jubjub::{JubjubBls12, fs}; + use jubjub::{JubjubBls12, fs, edwards}; let params = &JubjubBls12::new(); let rng = &mut XorShiftRng::from_seed([0x3dbe6258, 0x8d313d76, 0x3237db17, 0xe5bc0654]); @@ -703,8 +700,31 @@ fn test_output_circuit_with_bls12_381() { randomness: rng.gen() }; - let g_d: edwards::Point = edwards::Point::rand(rng, params).mul_by_cofactor(params); - let pk_d: edwards::Point = edwards::Point::rand(rng, params).mul_by_cofactor(params); + let rsk: fs::Fs = rng.gen(); + let ak = edwards::Point::rand(rng, params).mul_by_cofactor(params); + + let proof_generation_key = ::primitives::ProofGenerationKey { + ak: ak.clone(), + rsk: rsk.clone() + }; + + let viewing_key = proof_generation_key.into_viewing_key(params); + + let payment_address; + + loop { + let diversifier = ::primitives::Diversifier(rng.gen()); + + if let Some(p) = viewing_key.into_payment_address( + diversifier, + params + ) + { + payment_address = p; + break; + } + } + let commitment_randomness: fs::Fs = rng.gen(); let esk: fs::Fs = rng.gen(); @@ -714,8 +734,7 @@ fn test_output_circuit_with_bls12_381() { let instance = Output { params: params, value_commitment: Some(value_commitment.clone()), - g_d: Some(g_d.clone()), - pk_d: Some(pk_d.clone()), + payment_address: Some(payment_address.clone()), commitment_randomness: Some(commitment_randomness), esk: Some(esk.clone()) }; @@ -726,16 +745,15 @@ fn test_output_circuit_with_bls12_381() { assert_eq!(cs.num_constraints(), 7827); assert_eq!(cs.hash(), "2896f259ad7a50c83604976ee9362358396d547b70f2feaf91d82d287e4ffc1d"); - let expected_cm = ::primitives::Note { - value: value_commitment.value, - g_d: g_d.clone(), - pk_d: pk_d.clone(), - r: commitment_randomness.clone() - }.cm(params); + let expected_cm = payment_address.create_note( + value_commitment.value, + commitment_randomness, + params + ).expect("should be valid").cm(params); let expected_value_cm = value_commitment.cm(params).into_xy(); - let expected_epk = g_d.mul(esk, params); + let expected_epk = payment_address.g_d(params).expect("should be valid").mul(esk, params); let expected_epk_xy = expected_epk.into_xy(); assert_eq!(cs.num_inputs(), 6); diff --git a/src/primitives/mod.rs b/src/primitives/mod.rs index 028c6b738..6ac10402d 100644 --- a/src/primitives/mod.rs +++ b/src/primitives/mod.rs @@ -134,6 +134,23 @@ impl PaymentAddress { { 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() + } + }) + } } pub struct Note { From b6e1b52a44071e736ea2329559250070ed80607d Mon Sep 17 00:00:00 2001 From: Sean Bowe Date: Thu, 8 Mar 2018 13:03:07 -0700 Subject: [PATCH 15/15] Fix comment about Montgomery curve selection --- src/jubjub/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/jubjub/mod.rs b/src/jubjub/mod.rs index cff5c4a08..ca874c6c8 100644 --- a/src/jubjub/mod.rs +++ b/src/jubjub/mod.rs @@ -5,7 +5,7 @@ //! value `A` is the smallest integer choice such that: //! //! * `(A - 2) / 4` is a small integer (`10240`). -//! * `A^2 - 4` is quadratic residue. +//! * `A^2 - 4` is quadratic nonresidue. //! * The group order of the curve and its quadratic twist has a large //! prime factor. //!