From 7e05feb90bd6880d90313d1b123eff939a3341b8 Mon Sep 17 00:00:00 2001 From: Sean Bowe Date: Fri, 16 Mar 2018 10:31:14 -0600 Subject: [PATCH 01/11] Changes to names and circuit design to match spec. --- src/circuit/sapling/mod.rs | 142 +++++++++++++++++++++---------------- src/primitives/mod.rs | 51 ++++++------- 2 files changed, 108 insertions(+), 85 deletions(-) diff --git a/src/circuit/sapling/mod.rs b/src/circuit/sapling/mod.rs index 9b3b6de..2d51312 100644 --- a/src/circuit/sapling/mod.rs +++ b/src/circuit/sapling/mod.rs @@ -28,6 +28,7 @@ use super::ecc; use super::pedersen_hash; use super::blake2s; use super::num; +use super::multipack; /// This is an instance of the `Spend` circuit. pub struct Spend<'a, E: JubjubEngine> { @@ -46,6 +47,9 @@ pub struct Spend<'a, E: JubjubEngine> { /// The randomness of the note commitment pub commitment_randomness: Option, + /// Re-randomization of the public key + pub ar: Option, + /// The authentication path of the commitment in the tree pub auth_path: Vec> } @@ -129,25 +133,25 @@ impl<'a, E: JubjubEngine> Circuit for Spend<'a, E> { self.params )?; - // Compute rk = [rsk] ProvingPublicKey - let rk; + // Compute nk = [nsk] ProvingPublicKey + let nk; { - // Witness rsk as bits - let rsk = boolean::field_into_boolean_vec_le( - cs.namespace(|| "rsk"), - self.proof_generation_key.as_ref().map(|k| k.rsk.clone()) + // Witness nsk as bits + let nsk = boolean::field_into_boolean_vec_le( + cs.namespace(|| "nsk"), + self.proof_generation_key.as_ref().map(|k| k.nsk.clone()) )?; - // NB: We don't ensure that the bit representation of rsk + // NB: We don't ensure that the bit representation of nsk // is "in the field" (Fs) because it's not used except to // 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"), + // Compute nk = [nsk] ProvingPublicKey + nk = ecc::fixed_base_multiplication( + cs.namespace(|| "computation of nk"), FixedGenerators::ProofGenerationKey, - &rsk, + &nsk, self.params )?; } @@ -175,22 +179,46 @@ impl<'a, E: JubjubEngine> Circuit for Spend<'a, E> { 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. + // Rerandomize ak and expose it as an input to the circuit { - let repr_rk = rk.repr( - cs.namespace(|| "representation of rk") + let ar = boolean::field_into_boolean_vec_le( + cs.namespace(|| "ar"), + self.ar )?; - vk.extend(repr_rk.iter().cloned()); - nr_preimage.extend(repr_rk); + // Compute the randomness in the exponent + let ar = ecc::fixed_base_multiplication( + cs.namespace(|| "computation of randomization for the signing key"), + FixedGenerators::SpendingKeyGenerator, + &ar, + self.params + )?; + + let rk = ak.add( + cs.namespace(|| "computation of rk"), + &ar, + self.params + )?; + + rk.inputize(cs.namespace(|| "rk"))?; + } + + // This is the nullifier preimage for PRF^nf + let mut nf_preimage = vec![]; + + // Extend vk and nr preimages with the representation of + // nk. + { + let repr_nk = nk.repr( + cs.namespace(|| "representation of nk") + )?; + + vk.extend(repr_nk.iter().cloned()); + nf_preimage.extend(repr_nk); } assert_eq!(vk.len(), 512); - assert_eq!(nr_preimage.len(), 256); + assert_eq!(nf_preimage.len(), 256); // Compute the incoming viewing key ivk let mut ivk = blake2s::blake2s( @@ -353,6 +381,7 @@ impl<'a, E: JubjubEngine> Circuit for Spend<'a, E> { // Compute the cm + g^position for preventing // faerie gold attacks + let mut rho = cm; { // Compute the position in the exponent let position = ecc::fixed_base_multiplication( @@ -363,46 +392,28 @@ impl<'a, E: JubjubEngine> Circuit for Spend<'a, E> { )?; // Add the position to the commitment - cm = cm.add( + rho = rho.add( cs.namespace(|| "faerie gold prevention"), &position, self.params )?; } - // Let's compute nr = BLAKE2s(rk || cm + position) - nr_preimage.extend( - cm.repr(cs.namespace(|| "representation of cm"))? + // Let's compute nf = BLAKE2s(rk || rho) + nf_preimage.extend( + rho.repr(cs.namespace(|| "representation of rho"))? ); - assert_eq!(nr_preimage.len(), 512); + assert_eq!(nf_preimage.len(), 512); - // Compute nr - let mut nr = blake2s::blake2s( - cs.namespace(|| "nr computation"), - &nr_preimage, + // Compute nf + let nf = blake2s::blake2s( + cs.namespace(|| "nf computation"), + &nf_preimage, constants::PRF_NR_PERSONALIZATION )?; - // Little endian bit order - 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"), - &nr, - self.params - )?; - - // Expose the nullifier publicly - nf.inputize(cs.namespace(|| "nullifier"))?; - - Ok(()) + multipack::pack_into_inputs(cs.namespace(|| "pack nullifier"), &nf) } } @@ -560,12 +571,12 @@ fn test_input_circuit_with_bls12_381() { randomness: rng.gen() }; - let rsk: fs::Fs = rng.gen(); + let nsk: 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() + nsk: nsk.clone() }; let viewing_key = proof_generation_key.into_viewing_key(params); @@ -588,6 +599,7 @@ fn test_input_circuit_with_bls12_381() { 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]; + let ar: fs::Fs = rng.gen(); { let mut cs = TestConstraintSystem::::new(); @@ -598,22 +610,27 @@ fn test_input_circuit_with_bls12_381() { proof_generation_key: Some(proof_generation_key.clone()), payment_address: Some(payment_address.clone()), commitment_randomness: Some(commitment_randomness), + ar: Some(ar), auth_path: auth_path.clone() }; instance.synthesize(&mut cs).unwrap(); assert!(cs.is_satisfied()); - assert_eq!(cs.num_constraints(), 101018); - assert_eq!(cs.hash(), "eedcef5fd638e0168ae4d53ac58df66f0acdabea46749cc5f4b39459c8377804"); + assert_eq!(cs.num_constraints(), 98776); + assert_eq!(cs.hash(), "c5c377cad6310a5caa74305b2fe72b53e27a9c1db110edd9c4af164e99c0db71"); let expected_value_cm = value_commitment.cm(params).into_xy(); - assert_eq!(cs.num_inputs(), 6); + assert_eq!(cs.num_inputs(), 8); 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.0); assert_eq!(cs.get_input(2, "value commitment/commitment point/y/input variable"), expected_value_cm.1); + let rk = viewing_key.rk(ar, params).into_xy(); + assert_eq!(cs.get_input(3, "rk/x/input variable"), rk.0); + assert_eq!(cs.get_input(4, "rk/y/input variable"), rk.1); + let note = ::primitives::Note { value: value_commitment.value, g_d: g_d.clone(), @@ -656,12 +673,15 @@ fn test_input_circuit_with_bls12_381() { } } - let expected_nf = note.nf(&viewing_key, position, params); - let expected_nf_xy = expected_nf.into_xy(); + assert_eq!(cs.get_input(5, "anchor/input variable"), cur); - 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); + let expected_nf = note.nf(&viewing_key, position, params); + let expected_nf = multipack::bytes_to_bits(&expected_nf); + let expected_nf = multipack::compute_multipacking::(&expected_nf); + assert_eq!(expected_nf.len(), 2); + + assert_eq!(cs.get_input(6, "pack nullifier/input 0"), expected_nf[0]); + assert_eq!(cs.get_input(7, "pack nullifier/input 1"), expected_nf[1]); } } @@ -681,12 +701,12 @@ fn test_output_circuit_with_bls12_381() { randomness: rng.gen() }; - let rsk: fs::Fs = rng.gen(); + let nsk: 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() + nsk: nsk.clone() }; let viewing_key = proof_generation_key.into_viewing_key(params); diff --git a/src/primitives/mod.rs b/src/primitives/mod.rs index 2ac43b1..53d05b3 100644 --- a/src/primitives/mod.rs +++ b/src/primitives/mod.rs @@ -53,30 +53,42 @@ impl ValueCommitment { #[derive(Clone)] pub struct ProofGenerationKey { pub ak: edwards::Point, - pub rsk: E::Fs + pub nsk: 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) + nk: params.generator(FixedGenerators::ProofGenerationKey) + .mul(self.nsk, params) } } } pub struct ViewingKey { pub ak: edwards::Point, - pub rk: 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 + ) + } + 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(); + self.nk.write(&mut preimage[32..64]).unwrap(); let mut h = Blake2s::with_params(32, &[], &[], constants::CRH_IVK_PERSONALIZATION); h.update(&preimage); @@ -215,10 +227,10 @@ impl Note { viewing_key: &ViewingKey, position: u64, params: &E::Params - ) -> edwards::Point + ) -> Vec { - // Compute cm + position - let cm_plus_position = self + // Compute rho = cm + position.G + let rho = self .cm_full_point(params) .add( ¶ms.generator(FixedGenerators::NullifierPosition) @@ -226,23 +238,14 @@ impl Note { 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(); + // 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(); let mut h = Blake2s::with_params(32, &[], &[], constants::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) + h.update(&nf_preimage); + + h.finalize().as_ref().to_vec() } /// Computes the note commitment From 8b2f231e2f6cc887ba9d752615fa6cb9f4094275 Mon Sep 17 00:00:00 2001 From: Sean Bowe Date: Fri, 16 Mar 2018 10:58:08 -0600 Subject: [PATCH 02/11] Change personalization to match specification. --- src/circuit/sapling/mod.rs | 6 ++--- src/constants.rs | 55 +++++++++++++++++++++++++------------- src/primitives/mod.rs | 2 +- 3 files changed, 41 insertions(+), 22 deletions(-) diff --git a/src/circuit/sapling/mod.rs b/src/circuit/sapling/mod.rs index 2d51312..83a6d58 100644 --- a/src/circuit/sapling/mod.rs +++ b/src/circuit/sapling/mod.rs @@ -410,7 +410,7 @@ impl<'a, E: JubjubEngine> Circuit for Spend<'a, E> { let nf = blake2s::blake2s( cs.namespace(|| "nf computation"), &nf_preimage, - constants::PRF_NR_PERSONALIZATION + constants::PRF_NF_PERSONALIZATION )?; multipack::pack_into_inputs(cs.namespace(|| "pack nullifier"), &nf) @@ -618,7 +618,7 @@ fn test_input_circuit_with_bls12_381() { assert!(cs.is_satisfied()); assert_eq!(cs.num_constraints(), 98776); - assert_eq!(cs.hash(), "c5c377cad6310a5caa74305b2fe72b53e27a9c1db110edd9c4af164e99c0db71"); + assert_eq!(cs.hash(), "e6d326669533baf3f771267e86bd752b246184d34b1f2a68f9a6b9283f42e325"); let expected_value_cm = value_commitment.cm(params).into_xy(); @@ -744,7 +744,7 @@ fn test_output_circuit_with_bls12_381() { assert!(cs.is_satisfied()); assert_eq!(cs.num_constraints(), 7827); - assert_eq!(cs.hash(), "2896f259ad7a50c83604976ee9362358396d547b70f2feaf91d82d287e4ffc1d"); + assert_eq!(cs.hash(), "0c3d4ee7b0ac346836f177a471b2453c3558ea5760c526faad72feb65caf275b"); let expected_cm = payment_address.create_note( value_commitment.value, diff --git a/src/constants.rs b/src/constants.rs index 71b96e1..b0ba9d5 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -2,28 +2,47 @@ /// 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"; +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"; -/// BLAKE2s Personalization for PRF^nr = BLAKE2s(rk | cm + position) -pub const PRF_NR_PERSONALIZATION: &'static [u8; 8] = b"WhatTheH"; +/// BLAKE2s Personalization for CRH^ivk = BLAKE2s(ak | nk) +pub const CRH_IVK_PERSONALIZATION: &'static [u8; 8] + = b"Zcashivk"; + +/// BLAKE2s Personalization for PRF^nf = BLAKE2s(nk | rho) +pub const PRF_NF_PERSONALIZATION: &'static [u8; 8] + = b"Zcash_nf"; // Group hash personalizations /// BLAKE2s Personalization for Pedersen hash generators. -pub const PEDERSEN_HASH_GENERATORS_PERSONALIZATION: &'static [u8; 8] = b"PEDERSEN"; +pub const PEDERSEN_HASH_GENERATORS_PERSONALIZATION: &'static [u8; 8] + = b"Zcash_PH"; + /// 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"; +pub const KEY_DIVERSIFICATION_PERSONALIZATION: &'static [u8; 8] + = b"Zcash_gd"; + /// BLAKE2s Personalization for the spending key base point -pub const SPENDING_KEY_GENERATOR_PERSONALIZATION: &'static [u8; 8] = b"sksksksk"; +pub const SPENDING_KEY_GENERATOR_PERSONALIZATION: &'static [u8; 8] + = b"Zcash_G_"; + +/// BLAKE2s Personalization for the proof generation key base point +pub const PROOF_GENERATION_KEY_BASE_GENERATOR_PERSONALIZATION: &'static [u8; 8] + = b"Zcash_H_"; + +/// BLAKE2s Personalization for the note commitment randomness generator +pub const NOTE_COMMITMENT_RANDOMNESS_GENERATOR_PERSONALIZATION: &'static [u8; 8] + = b"Zcashrcm"; + +/// BLAKE2s Personalization for the value commitment randomness generator +pub const VALUE_COMMITMENT_RANDOMNESS_GENERATOR_PERSONALIZATION: &'static [u8; 8] + = b"Zcashrcv"; + +/// BLAKE2s Personalization for the value commitment generator for the value +pub const VALUE_COMMITMENT_VALUE_GENERATOR_PERSONALIZATION: &'static [u8; 8] + = b"Zcash_cv"; + +/// BLAKE2s Personalization for the nullifier position generator (for computing rho) +pub const NULLIFIER_POSITION_IN_TREE_GENERATOR_PERSONALIZATION: &'static [u8; 8] + = b"Zcashrho"; diff --git a/src/primitives/mod.rs b/src/primitives/mod.rs index 53d05b3..bb1298b 100644 --- a/src/primitives/mod.rs +++ b/src/primitives/mod.rs @@ -242,7 +242,7 @@ impl Note { let mut nf_preimage = [0u8; 64]; viewing_key.nk.write(&mut nf_preimage[0..32]).unwrap(); rho.write(&mut nf_preimage[32..64]).unwrap(); - let mut h = Blake2s::with_params(32, &[], &[], constants::PRF_NR_PERSONALIZATION); + let mut h = Blake2s::with_params(32, &[], &[], constants::PRF_NF_PERSONALIZATION); h.update(&nf_preimage); h.finalize().as_ref().to_vec() From 00ee962429508830f397fd96da9cf19501285f11 Mon Sep 17 00:00:00 2001 From: Sean Bowe Date: Fri, 16 Mar 2018 12:30:00 -0600 Subject: [PATCH 03/11] Fix some names of variables. --- src/circuit/sapling/mod.rs | 55 +++++++++++++++++++------------------- 1 file changed, 27 insertions(+), 28 deletions(-) diff --git a/src/circuit/sapling/mod.rs b/src/circuit/sapling/mod.rs index 83a6d58..00e6c70 100644 --- a/src/circuit/sapling/mod.rs +++ b/src/circuit/sapling/mod.rs @@ -88,7 +88,7 @@ fn expose_value_commitment( )?; // Compute the note value in the exponent - let gv = ecc::fixed_base_multiplication( + let value = ecc::fixed_base_multiplication( cs.namespace(|| "compute the value in the exponent"), FixedGenerators::ValueCommitmentValue, &value_bits, @@ -98,28 +98,28 @@ fn expose_value_commitment( // 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"), + let rcv = boolean::field_into_boolean_vec_le( + cs.namespace(|| "rcv"), value_commitment.as_ref().map(|c| c.randomness) )?; // Compute the randomness in the exponent - let hr = ecc::fixed_base_multiplication( - cs.namespace(|| "computation of randomization for value commitment"), + let rcv = ecc::fixed_base_multiplication( + cs.namespace(|| "computation of rcv"), FixedGenerators::ValueCommitmentRandomness, - &hr, + &rcv, params )?; // Compute the Pedersen commitment to the value - let gvhr = gv.add( - cs.namespace(|| "computation of value commitment"), - &hr, + let cv = value.add( + cs.namespace(|| "computation of cv"), + &rcv, params )?; // Expose the commitment as an input to the circuit - gvhr.inputize(cs.namespace(|| "commitment point"))?; + cv.inputize(cs.namespace(|| "commitment point"))?; Ok(value_bits) } @@ -133,7 +133,7 @@ impl<'a, E: JubjubEngine> Circuit for Spend<'a, E> { self.params )?; - // Compute nk = [nsk] ProvingPublicKey + // Compute nk = [nsk] ProofGenerationKey let nk; { // Witness nsk as bits @@ -174,8 +174,8 @@ impl<'a, E: JubjubEngine> Circuit for Spend<'a, E> { // Unpack ak and rk for input to BLAKE2s // This is the "viewing key" preimage for CRH^ivk - let mut vk = vec![]; - vk.extend( + let mut ivk_preimage = vec![]; + ivk_preimage.extend( ak.repr(cs.namespace(|| "representation of ak"))? ); @@ -206,24 +206,24 @@ impl<'a, E: JubjubEngine> Circuit for Spend<'a, E> { // This is the nullifier preimage for PRF^nf let mut nf_preimage = vec![]; - // Extend vk and nr preimages with the representation of + // Extend ivk and nf preimages with the representation of // nk. { let repr_nk = nk.repr( cs.namespace(|| "representation of nk") )?; - vk.extend(repr_nk.iter().cloned()); + ivk_preimage.extend(repr_nk.iter().cloned()); nf_preimage.extend(repr_nk); } - assert_eq!(vk.len(), 512); + assert_eq!(ivk_preimage.len(), 512); assert_eq!(nf_preimage.len(), 256); // Compute the incoming viewing key ivk let mut ivk = blake2s::blake2s( cs.namespace(|| "computation of ivk"), - &vk, + &ivk_preimage, constants::CRH_IVK_PERSONALIZATION )?; @@ -233,11 +233,7 @@ impl<'a, E: JubjubEngine> Circuit for Spend<'a, E> { // 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. + // Witness g_d, checking that it's on the curve. let g_d = { // This binding is to avoid a weird edge case in Rust's // ownership/borrowing rules. self is partially moved @@ -257,7 +253,10 @@ impl<'a, E: JubjubEngine> Circuit for Spend<'a, E> { // is already done in the Output circuit, and this proof ensures // g_d is bound to a product of that check, but for defense in // depth let's check it anyway. It's cheap. - g_d.assert_not_small_order(cs.namespace(|| "g_d not small order"), self.params)?; + g_d.assert_not_small_order( + cs.namespace(|| "g_d not small order"), + self.params + )?; // Compute pk_d = g_d^ivk let pk_d = g_d.mul( @@ -294,16 +293,16 @@ impl<'a, E: JubjubEngine> Circuit for Spend<'a, E> { { // Booleanize the randomness for the note commitment - let cmr = boolean::field_into_boolean_vec_le( - cs.namespace(|| "cmr"), + let rcm = boolean::field_into_boolean_vec_le( + cs.namespace(|| "rcm"), self.commitment_randomness )?; // Compute the note commitment randomness in the exponent - let cmr = ecc::fixed_base_multiplication( + let rcm = ecc::fixed_base_multiplication( cs.namespace(|| "computation of commitment randomness"), FixedGenerators::NoteCommitmentRandomness, - &cmr, + &rcm, self.params )?; @@ -311,7 +310,7 @@ impl<'a, E: JubjubEngine> Circuit for Spend<'a, E> { // themselves hiding commitments. cm = cm.add( cs.namespace(|| "randomization of note commitment"), - &cmr, + &rcm, self.params )?; } From 13b03a0a1a4fdd60d1cbacc3bb89688dbfb3b6b8 Mon Sep 17 00:00:00 2001 From: Sean Bowe Date: Fri, 16 Mar 2018 22:21:29 -0600 Subject: [PATCH 04/11] Relocate circuit components for intuitive code paths. --- src/circuit/sapling/mod.rs | 95 +++++++++++++++++++------------------- 1 file changed, 47 insertions(+), 48 deletions(-) diff --git a/src/circuit/sapling/mod.rs b/src/circuit/sapling/mod.rs index 00e6c70..4300bb8 100644 --- a/src/circuit/sapling/mod.rs +++ b/src/circuit/sapling/mod.rs @@ -127,35 +127,6 @@ fn expose_value_commitment( impl<'a, E: JubjubEngine> Circuit for Spend<'a, E> { fn synthesize>(self, cs: &mut CS) -> Result<(), SynthesisError> { - let value_bits = expose_value_commitment( - cs.namespace(|| "value commitment"), - self.value_commitment, - self.params - )?; - - // Compute nk = [nsk] ProofGenerationKey - let nk; - { - // Witness nsk as bits - let nsk = boolean::field_into_boolean_vec_le( - cs.namespace(|| "nsk"), - self.proof_generation_key.as_ref().map(|k| k.nsk.clone()) - )?; - - // NB: We don't ensure that the bit representation of nsk - // is "in the field" (Fs) because it's not used except to - // demonstrate the prover knows it. If they know a - // congruency then that's equivalent. - - // Compute nk = [nsk] ProvingPublicKey - nk = ecc::fixed_base_multiplication( - cs.namespace(|| "computation of nk"), - FixedGenerators::ProofGenerationKey, - &nsk, - self.params - )?; - } - // Prover witnesses ak (ensures that it's on the curve) let ak = ecc::EdwardsPoint::witness( cs.namespace(|| "ak"), @@ -171,14 +142,6 @@ impl<'a, E: JubjubEngine> Circuit for Spend<'a, E> { self.params )?; - // Unpack ak and rk for input to BLAKE2s - - // This is the "viewing key" preimage for CRH^ivk - let mut ivk_preimage = vec![]; - ivk_preimage.extend( - ak.repr(cs.namespace(|| "representation of ak"))? - ); - // Rerandomize ak and expose it as an input to the circuit { let ar = boolean::field_into_boolean_vec_le( @@ -203,6 +166,37 @@ impl<'a, E: JubjubEngine> Circuit for Spend<'a, E> { rk.inputize(cs.namespace(|| "rk"))?; } + // Compute nk = [nsk] ProofGenerationKey + let nk; + { + // Witness nsk as bits + let nsk = boolean::field_into_boolean_vec_le( + cs.namespace(|| "nsk"), + self.proof_generation_key.as_ref().map(|k| k.nsk.clone()) + )?; + + // NB: We don't ensure that the bit representation of nsk + // is "in the field" (Fs) because it's not used except to + // demonstrate the prover knows it. If they know a + // congruency then that's equivalent. + + // Compute nk = [nsk] ProvingPublicKey + nk = ecc::fixed_base_multiplication( + cs.namespace(|| "computation of nk"), + FixedGenerators::ProofGenerationKey, + &nsk, + self.params + )?; + } + + // This is the "viewing key" preimage for CRH^ivk + let mut ivk_preimage = vec![]; + + // Place ak in the preimage for CRH^ivk + ivk_preimage.extend( + ak.repr(cs.namespace(|| "representation of ak"))? + ); + // This is the nullifier preimage for PRF^nf let mut nf_preimage = vec![]; @@ -265,6 +259,14 @@ impl<'a, E: JubjubEngine> Circuit for Spend<'a, E> { self.params )?; + // Expose the value commitment and get the + // bits of the value (in little endian order) + let value_bits = expose_value_commitment( + cs.namespace(|| "value commitment"), + self.value_commitment, + self.params + )?; + // Compute note contents // value (in big endian) followed by g_d and pk_d let mut note_contents = vec![]; @@ -315,8 +317,6 @@ 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. @@ -326,6 +326,7 @@ impl<'a, E: JubjubEngine> Circuit for Spend<'a, E> { // point in the prime order subgroup. let mut cur = cm.get_x().clone(); + // Ascend the merkle tree authentication path for (i, e) in self.auth_path.into_iter().enumerate() { let cs = &mut cs.namespace(|| format!("merkle tree hash {}", i)); @@ -373,8 +374,6 @@ impl<'a, E: JubjubEngine> Circuit for Spend<'a, E> { )?.get_x().clone(); // Injective encoding } - assert_eq!(position_bits.len(), tree_depth); - // Expose the anchor cur.inputize(cs.namespace(|| "anchor"))?; @@ -617,18 +616,18 @@ fn test_input_circuit_with_bls12_381() { assert!(cs.is_satisfied()); assert_eq!(cs.num_constraints(), 98776); - assert_eq!(cs.hash(), "e6d326669533baf3f771267e86bd752b246184d34b1f2a68f9a6b9283f42e325"); - - let expected_value_cm = value_commitment.cm(params).into_xy(); + assert_eq!(cs.hash(), "729850617d4e6d95cbf348f07cbe0c63b01d35718f24cbcf7df79e2c3e1a7648"); assert_eq!(cs.num_inputs(), 8); 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.0); - assert_eq!(cs.get_input(2, "value commitment/commitment point/y/input variable"), expected_value_cm.1); let rk = viewing_key.rk(ar, params).into_xy(); - assert_eq!(cs.get_input(3, "rk/x/input variable"), rk.0); - assert_eq!(cs.get_input(4, "rk/y/input variable"), rk.1); + assert_eq!(cs.get_input(1, "rk/x/input variable"), rk.0); + assert_eq!(cs.get_input(2, "rk/y/input variable"), rk.1); + + let expected_value_cm = value_commitment.cm(params).into_xy(); + assert_eq!(cs.get_input(3, "value commitment/commitment point/x/input variable"), expected_value_cm.0); + assert_eq!(cs.get_input(4, "value commitment/commitment point/y/input variable"), expected_value_cm.1); let note = ::primitives::Note { value: value_commitment.value, From 6d01e78711b8ae8cc63fbb3406ecc54dd7ecd168 Mon Sep 17 00:00:00 2001 From: Sean Bowe Date: Sat, 17 Mar 2018 09:02:29 -0600 Subject: [PATCH 05/11] Fix variable names in output circuit --- src/circuit/sapling/mod.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/circuit/sapling/mod.rs b/src/circuit/sapling/mod.rs index 4300bb8..936930d 100644 --- a/src/circuit/sapling/mod.rs +++ b/src/circuit/sapling/mod.rs @@ -507,7 +507,7 @@ impl<'a, E: JubjubEngine> Circuit for Output<'a, E> { note_contents.len(), 64 + // value 256 + // g_d - 256 // p_d + 256 // pk_d ); // Compute the hash of the note contents @@ -520,23 +520,23 @@ impl<'a, E: JubjubEngine> Circuit for Output<'a, E> { { // Booleanize the randomness - let cmr = boolean::field_into_boolean_vec_le( - cs.namespace(|| "cmr"), + let rcm = boolean::field_into_boolean_vec_le( + cs.namespace(|| "rcm"), self.commitment_randomness )?; // Compute the note commitment randomness in the exponent - let cmr = ecc::fixed_base_multiplication( + let rcm = ecc::fixed_base_multiplication( cs.namespace(|| "computation of commitment randomness"), FixedGenerators::NoteCommitmentRandomness, - &cmr, + &rcm, self.params )?; // Randomize our note commitment cm = cm.add( cs.namespace(|| "randomization of note commitment"), - &cmr, + &rcm, self.params )?; } From d09c4d6ce8e4bb313951a12d904488defb203ba0 Mon Sep 17 00:00:00 2001 From: Sean Bowe Date: Sat, 17 Mar 2018 09:10:13 -0600 Subject: [PATCH 06/11] Simplify value commitment gadget. --- src/circuit/sapling/mod.rs | 39 ++++++++++++++++++++++---------------- 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/src/circuit/sapling/mod.rs b/src/circuit/sapling/mod.rs index 936930d..bc2178f 100644 --- a/src/circuit/sapling/mod.rs +++ b/src/circuit/sapling/mod.rs @@ -82,7 +82,7 @@ fn expose_value_commitment( CS: ConstraintSystem { // Booleanize the value into little-endian bit order - let value_bits = boolean::u64_into_boolean_vec_le( + let mut value_bits = boolean::u64_into_boolean_vec_le( cs.namespace(|| "value"), value_commitment.as_ref().map(|c| c.value) )?; @@ -121,6 +121,9 @@ fn expose_value_commitment( // Expose the commitment as an input to the circuit cv.inputize(cs.namespace(|| "commitment point"))?; + // Reorder value_bits so that it's big-endian + value_bits.reverse(); + Ok(value_bits) } @@ -259,21 +262,24 @@ impl<'a, E: JubjubEngine> Circuit for Spend<'a, E> { self.params )?; - // Expose the value commitment and get the - // bits of the value (in little endian order) - let value_bits = expose_value_commitment( + // Compute note contents: + // value (in big endian) followed by g_d and pk_d + let mut note_contents = vec![]; + + // Expose the value commitment and place the value + // in the note. + note_contents.extend(expose_value_commitment( cs.namespace(|| "value commitment"), self.value_commitment, self.params - )?; + )?); - // 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()); + // Place g_d in the note note_contents.extend( g_d.repr(cs.namespace(|| "representation of g_d"))? ); + + // Place pk_d in the note note_contents.extend( pk_d.repr(cs.namespace(|| "representation of pk_d"))? ); @@ -418,16 +424,17 @@ impl<'a, E: JubjubEngine> Circuit for Spend<'a, E> { impl<'a, E: JubjubEngine> Circuit for Output<'a, E> { fn synthesize>(self, cs: &mut CS) -> Result<(), SynthesisError> { - let value_bits = expose_value_commitment( - cs.namespace(|| "value commitment"), - self.value_commitment, - self.params - )?; - // 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()); + + // Expose the value commitment and place the value + // in the note. + note_contents.extend(expose_value_commitment( + cs.namespace(|| "value commitment"), + self.value_commitment, + self.params + )?); // Let's deal with g_d { From 8b6f113052ecd4c96d0490509c88b2395856dbde Mon Sep 17 00:00:00 2001 From: Sean Bowe Date: Sat, 17 Mar 2018 10:24:55 -0600 Subject: [PATCH 07/11] Change personalization to more closely align with the spec. --- src/circuit/sapling/mod.rs | 4 +- src/jubjub/mod.rs | 105 ++++++++++++++++++------------------- 2 files changed, 53 insertions(+), 56 deletions(-) diff --git a/src/circuit/sapling/mod.rs b/src/circuit/sapling/mod.rs index bc2178f..eacb48c 100644 --- a/src/circuit/sapling/mod.rs +++ b/src/circuit/sapling/mod.rs @@ -623,7 +623,7 @@ fn test_input_circuit_with_bls12_381() { assert!(cs.is_satisfied()); assert_eq!(cs.num_constraints(), 98776); - assert_eq!(cs.hash(), "729850617d4e6d95cbf348f07cbe0c63b01d35718f24cbcf7df79e2c3e1a7648"); + assert_eq!(cs.hash(), "d810fa887178359f3fc5723781a0750b750dd0c02aeb0b14ff19a343db9868f1"); assert_eq!(cs.num_inputs(), 8); assert_eq!(cs.get_input(0, "ONE"), Fr::one()); @@ -749,7 +749,7 @@ fn test_output_circuit_with_bls12_381() { assert!(cs.is_satisfied()); assert_eq!(cs.num_constraints(), 7827); - assert_eq!(cs.hash(), "0c3d4ee7b0ac346836f177a471b2453c3558ea5760c526faad72feb65caf275b"); + assert_eq!(cs.hash(), "e49724488227ae83b2360a5ddbda7e44c83e6f526a369cefeb747c5dd6aab7c7"); let expected_cm = payment_address.create_note( value_commitment.value, diff --git a/src/jubjub/mod.rs b/src/jubjub/mod.rs index a04fe40..8966c7b 100644 --- a/src/jubjub/mod.rs +++ b/src/jubjub/mod.rs @@ -192,25 +192,51 @@ impl JubjubBls12 { fixed_base_circuit_generators: vec![], }; - // Create the bases for the Pedersen hashes + fn find_group_hash( + m: &[u8], + personalization: &[u8; 8], + params: &E::Params + ) -> edwards::Point { - // TODO: This currently does not match the specification - let mut cur = 0; - let mut pedersen_hash_generators = vec![]; + let mut tag = m.to_vec(); + let i = tag.len(); + tag.push(0u8); + + loop { + let gh = group_hash( + &tag, + personalization, + params + ); - // 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_params); // We don't want to overflow and start reusing generators - assert!(cur != u8::max_value()); - cur += 1; + assert!(tag[i] != u8::max_value()); + tag[i] += 1; if let Some(gh) = gh { - pedersen_hash_generators.push(gh); + break gh; } } + } + + // Create the bases for the Pedersen hashes + { + let mut pedersen_hash_generators = vec![]; + + for m in 0..5 { + use byteorder::{WriteBytesExt, BigEndian}; + + let mut segment_number = [0u8; 4]; + (&mut segment_number[0..4]).write_u32::(m).unwrap(); + + pedersen_hash_generators.push( + find_group_hash( + &segment_number, + constants::PEDERSEN_HASH_GENERATORS_PERSONALIZATION, + &tmp_params + ) + ); + } // Check for duplicates, far worse than spec inconsistencies! for (i, p1) in pedersen_hash_generators.iter().enumerate() { @@ -232,52 +258,23 @@ impl JubjubBls12 { { let mut fixed_base_generators = vec![edwards::Point::zero(); FixedGenerators::Max as usize]; - { - // 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 = 0u8; + fixed_base_generators[FixedGenerators::ProofGenerationKey as usize] = + find_group_hash(b"0", constants::PROOF_GENERATION_KEY_BASE_GENERATOR_PERSONALIZATION, &tmp_params); - loop { - let gh = group_hash::(&[cur], personalization, &tmp_params); - // We don't want to overflow. - assert!(cur != u8::max_value()); - cur += 1; + fixed_base_generators[FixedGenerators::NoteCommitmentRandomness as usize] = + find_group_hash(b"0", constants::NOTE_COMMITMENT_RANDOMNESS_GENERATOR_PERSONALIZATION, &tmp_params); - if let Some(gh) = gh { - break gh; - } - } - }; + fixed_base_generators[FixedGenerators::NullifierPosition as usize] = + find_group_hash(b"0", constants::NULLIFIER_POSITION_IN_TREE_GENERATOR_PERSONALIZATION, &tmp_params); - // Written this way for exhaustion (double entendre). There's no - // way to iterate over the variants of an enum, so it's hideous. - for c in 0..(FixedGenerators::Max as usize) { - let p = match c { - c if c == (FixedGenerators::ProofGenerationKey as usize) => { - constants::PROOF_GENERATION_KEY_BASE_GENERATOR_PERSONALIZATION - }, - c if c == (FixedGenerators::NoteCommitmentRandomness as usize) => { - constants::NOTE_COMMITMENT_RANDOMNESS_GENERATOR_PERSONALIZATION - }, - c if c == (FixedGenerators::NullifierPosition as usize) => { - constants::NULLIFIER_POSITION_IN_TREE_GENERATOR_PERSONALIZATION - }, - c if c == (FixedGenerators::ValueCommitmentValue as usize) => { - constants::VALUE_COMMITMENT_VALUE_GENERATOR_PERSONALIZATION - }, - c if c == (FixedGenerators::ValueCommitmentRandomness as usize) => { - constants::VALUE_COMMITMENT_RANDOMNESS_GENERATOR_PERSONALIZATION - }, - c if c == (FixedGenerators::SpendingKeyGenerator as usize) => { - constants::SPENDING_KEY_GENERATOR_PERSONALIZATION - }, - _ => unreachable!() - }; + fixed_base_generators[FixedGenerators::ValueCommitmentValue as usize] = + find_group_hash(b"0", constants::VALUE_COMMITMENT_VALUE_GENERATOR_PERSONALIZATION, &tmp_params); - fixed_base_generators[c] = find_first_gh(p); - } - } + fixed_base_generators[FixedGenerators::ValueCommitmentRandomness as usize] = + find_group_hash(b"0", constants::VALUE_COMMITMENT_RANDOMNESS_GENERATOR_PERSONALIZATION, &tmp_params); + + fixed_base_generators[FixedGenerators::SpendingKeyGenerator as usize] = + find_group_hash(b"0", constants::SPENDING_KEY_GENERATOR_PERSONALIZATION, &tmp_params); // Check for duplicates, far worse than spec inconsistencies! for (i, p1) in fixed_base_generators.iter().enumerate() { From 219d03cc11e04940847b2b47541cda11f3397873 Mon Sep 17 00:00:00 2001 From: Sean Bowe Date: Mon, 19 Mar 2018 17:05:51 -0600 Subject: [PATCH 08/11] Perform multiple checks of circuit in test. --- src/circuit/sapling/mod.rs | 330 +++++++++++++++++++------------------ 1 file changed, 167 insertions(+), 163 deletions(-) diff --git a/src/circuit/sapling/mod.rs b/src/circuit/sapling/mod.rs index eacb48c..d58e6e7 100644 --- a/src/circuit/sapling/mod.rs +++ b/src/circuit/sapling/mod.rs @@ -571,122 +571,124 @@ fn test_input_circuit_with_bls12_381() { let tree_depth = 32; - let value_commitment = ValueCommitment { - value: rng.gen(), - randomness: rng.gen() - }; - - let nsk: fs::Fs = rng.gen(); - let ak = edwards::Point::rand(rng, params).mul_by_cofactor(params); - - let proof_generation_key = ::primitives::ProofGenerationKey { - ak: ak.clone(), - nsk: nsk.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]; - let ar: fs::Fs = rng.gen(); - - { - let mut cs = TestConstraintSystem::::new(); - - let instance = Spend { - params: params, - value_commitment: Some(value_commitment.clone()), - proof_generation_key: Some(proof_generation_key.clone()), - payment_address: Some(payment_address.clone()), - commitment_randomness: Some(commitment_randomness), - ar: Some(ar), - auth_path: auth_path.clone() + for _ in 0..10 { + let value_commitment = ValueCommitment { + value: rng.gen(), + randomness: rng.gen() }; - instance.synthesize(&mut cs).unwrap(); + let nsk: fs::Fs = rng.gen(); + let ak = edwards::Point::rand(rng, params).mul_by_cofactor(params); - assert!(cs.is_satisfied()); - assert_eq!(cs.num_constraints(), 98776); - assert_eq!(cs.hash(), "d810fa887178359f3fc5723781a0750b750dd0c02aeb0b14ff19a343db9868f1"); - - assert_eq!(cs.num_inputs(), 8); - assert_eq!(cs.get_input(0, "ONE"), Fr::one()); - - let rk = viewing_key.rk(ar, params).into_xy(); - assert_eq!(cs.get_input(1, "rk/x/input variable"), rk.0); - assert_eq!(cs.get_input(2, "rk/y/input variable"), rk.1); - - let expected_value_cm = value_commitment.cm(params).into_xy(); - assert_eq!(cs.get_input(3, "value commitment/commitment point/x/input variable"), expected_value_cm.0); - assert_eq!(cs.get_input(4, "value commitment/commitment point/y/input variable"), expected_value_cm.1); - - let note = ::primitives::Note { - value: value_commitment.value, - g_d: g_d.clone(), - pk_d: payment_address.pk_d.clone(), - r: commitment_randomness.clone() + let proof_generation_key = ::primitives::ProofGenerationKey { + ak: ak.clone(), + nsk: nsk.clone() }; - let mut position = 0u64; - let mut cur = note.cm(params); + let viewing_key = proof_generation_key.into_viewing_key(params); - assert_eq!(cs.get("randomization of note commitment/x3/num"), cur); + let payment_address; - for (i, val) in auth_path.into_iter().enumerate() - { - let (uncle, b) = val.unwrap(); + loop { + let diversifier = ::primitives::Diversifier(rng.gen()); - 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)), + if let Some(p) = viewing_key.into_payment_address( + diversifier, params - ).into_xy().0; - - if b { - position |= 1 << i; + ) + { + payment_address = p; + break; } } - assert_eq!(cs.get_input(5, "anchor/input variable"), cur); + 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]; + let ar: fs::Fs = rng.gen(); - let expected_nf = note.nf(&viewing_key, position, params); - let expected_nf = multipack::bytes_to_bits(&expected_nf); - let expected_nf = multipack::compute_multipacking::(&expected_nf); - assert_eq!(expected_nf.len(), 2); + { + let mut cs = TestConstraintSystem::::new(); - assert_eq!(cs.get_input(6, "pack nullifier/input 0"), expected_nf[0]); - assert_eq!(cs.get_input(7, "pack nullifier/input 1"), expected_nf[1]); + let instance = Spend { + params: params, + value_commitment: Some(value_commitment.clone()), + proof_generation_key: Some(proof_generation_key.clone()), + payment_address: Some(payment_address.clone()), + commitment_randomness: Some(commitment_randomness), + ar: Some(ar), + auth_path: auth_path.clone() + }; + + instance.synthesize(&mut cs).unwrap(); + + assert!(cs.is_satisfied()); + assert_eq!(cs.num_constraints(), 98776); + assert_eq!(cs.hash(), "d810fa887178359f3fc5723781a0750b750dd0c02aeb0b14ff19a343db9868f1"); + + assert_eq!(cs.num_inputs(), 8); + assert_eq!(cs.get_input(0, "ONE"), Fr::one()); + + let rk = viewing_key.rk(ar, params).into_xy(); + assert_eq!(cs.get_input(1, "rk/x/input variable"), rk.0); + assert_eq!(cs.get_input(2, "rk/y/input variable"), rk.1); + + let expected_value_cm = value_commitment.cm(params).into_xy(); + assert_eq!(cs.get_input(3, "value commitment/commitment point/x/input variable"), expected_value_cm.0); + assert_eq!(cs.get_input(4, "value commitment/commitment point/y/input variable"), expected_value_cm.1); + + let note = ::primitives::Note { + value: value_commitment.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; + } + } + + assert_eq!(cs.get_input(5, "anchor/input variable"), cur); + + let expected_nf = note.nf(&viewing_key, position, params); + let expected_nf = multipack::bytes_to_bits(&expected_nf); + let expected_nf = multipack::compute_multipacking::(&expected_nf); + assert_eq!(expected_nf.len(), 2); + + assert_eq!(cs.get_input(6, "pack nullifier/input 0"), expected_nf[0]); + assert_eq!(cs.get_input(7, "pack nullifier/input 1"), expected_nf[1]); + } } } @@ -701,73 +703,75 @@ fn test_output_circuit_with_bls12_381() { let params = &JubjubBls12::new(); let rng = &mut XorShiftRng::from_seed([0x3dbe6258, 0x8d313d76, 0x3237db17, 0xe5bc0654]); - let value_commitment = ValueCommitment { - value: rng.gen(), - randomness: rng.gen() - }; - - let nsk: fs::Fs = rng.gen(); - let ak = edwards::Point::rand(rng, params).mul_by_cofactor(params); - - let proof_generation_key = ::primitives::ProofGenerationKey { - ak: ak.clone(), - nsk: nsk.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(); - - { - let mut cs = TestConstraintSystem::::new(); - - let instance = Output { - params: params, - value_commitment: Some(value_commitment.clone()), - payment_address: Some(payment_address.clone()), - commitment_randomness: Some(commitment_randomness), - esk: Some(esk.clone()) + for _ in 0..100 { + let value_commitment = ValueCommitment { + value: rng.gen(), + randomness: rng.gen() }; - instance.synthesize(&mut cs).unwrap(); + let nsk: fs::Fs = rng.gen(); + let ak = edwards::Point::rand(rng, params).mul_by_cofactor(params); - assert!(cs.is_satisfied()); - assert_eq!(cs.num_constraints(), 7827); - assert_eq!(cs.hash(), "e49724488227ae83b2360a5ddbda7e44c83e6f526a369cefeb747c5dd6aab7c7"); + let proof_generation_key = ::primitives::ProofGenerationKey { + ak: ak.clone(), + nsk: nsk.clone() + }; - let expected_cm = payment_address.create_note( - value_commitment.value, - commitment_randomness, - params - ).expect("should be valid").cm(params); + let viewing_key = proof_generation_key.into_viewing_key(params); - let expected_value_cm = value_commitment.cm(params).into_xy(); + let payment_address; - let expected_epk = payment_address.g_d(params).expect("should be valid").mul(esk, params); - let expected_epk_xy = expected_epk.into_xy(); + loop { + let diversifier = ::primitives::Diversifier(rng.gen()); - 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.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); + 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(); + + { + let mut cs = TestConstraintSystem::::new(); + + let instance = Output { + params: params, + value_commitment: Some(value_commitment.clone()), + payment_address: Some(payment_address.clone()), + commitment_randomness: Some(commitment_randomness), + esk: Some(esk.clone()) + }; + + instance.synthesize(&mut cs).unwrap(); + + assert!(cs.is_satisfied()); + assert_eq!(cs.num_constraints(), 7827); + assert_eq!(cs.hash(), "e49724488227ae83b2360a5ddbda7e44c83e6f526a369cefeb747c5dd6aab7c7"); + + 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 = 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); + 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.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); + } } } From b14c9f8d68eeb17dd50f39546ff6b50866e9d915 Mon Sep 17 00:00:00 2001 From: Sean Bowe Date: Mon, 19 Mar 2018 17:26:28 -0600 Subject: [PATCH 09/11] Change personalizations to actually match spec this time. --- src/circuit/sapling/mod.rs | 4 ++-- src/constants.rs | 12 ++---------- src/jubjub/mod.rs | 16 ++++++++-------- 3 files changed, 12 insertions(+), 20 deletions(-) diff --git a/src/circuit/sapling/mod.rs b/src/circuit/sapling/mod.rs index d58e6e7..840ad9f 100644 --- a/src/circuit/sapling/mod.rs +++ b/src/circuit/sapling/mod.rs @@ -624,7 +624,7 @@ fn test_input_circuit_with_bls12_381() { assert!(cs.is_satisfied()); assert_eq!(cs.num_constraints(), 98776); - assert_eq!(cs.hash(), "d810fa887178359f3fc5723781a0750b750dd0c02aeb0b14ff19a343db9868f1"); + assert_eq!(cs.hash(), "2080d5f350cd7eff7742ab05dff18f82c0a2f29a5d2a758d805236067b2ed31f"); assert_eq!(cs.num_inputs(), 8); assert_eq!(cs.get_input(0, "ONE"), Fr::one()); @@ -752,7 +752,7 @@ fn test_output_circuit_with_bls12_381() { assert!(cs.is_satisfied()); assert_eq!(cs.num_constraints(), 7827); - assert_eq!(cs.hash(), "e49724488227ae83b2360a5ddbda7e44c83e6f526a369cefeb747c5dd6aab7c7"); + assert_eq!(cs.hash(), "a7810a444f7ef6d0caa8ba026ce06e64654863cd0557241282ca337858039a53"); let expected_cm = payment_address.create_note( value_commitment.value, diff --git a/src/constants.rs b/src/constants.rs index b0ba9d5..fa717c9 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -31,18 +31,10 @@ pub const SPENDING_KEY_GENERATOR_PERSONALIZATION: &'static [u8; 8] pub const PROOF_GENERATION_KEY_BASE_GENERATOR_PERSONALIZATION: &'static [u8; 8] = b"Zcash_H_"; -/// BLAKE2s Personalization for the note commitment randomness generator -pub const NOTE_COMMITMENT_RANDOMNESS_GENERATOR_PERSONALIZATION: &'static [u8; 8] - = b"Zcashrcm"; - -/// BLAKE2s Personalization for the value commitment randomness generator -pub const VALUE_COMMITMENT_RANDOMNESS_GENERATOR_PERSONALIZATION: &'static [u8; 8] - = b"Zcashrcv"; - /// BLAKE2s Personalization for the value commitment generator for the value -pub const VALUE_COMMITMENT_VALUE_GENERATOR_PERSONALIZATION: &'static [u8; 8] +pub const VALUE_COMMITMENT_GENERATOR_PERSONALIZATION: &'static [u8; 8] = b"Zcash_cv"; /// BLAKE2s Personalization for the nullifier position generator (for computing rho) pub const NULLIFIER_POSITION_IN_TREE_GENERATOR_PERSONALIZATION: &'static [u8; 8] - = b"Zcashrho"; + = b"Zcash_J_"; diff --git a/src/jubjub/mod.rs b/src/jubjub/mod.rs index 8966c7b..3b786c1 100644 --- a/src/jubjub/mod.rs +++ b/src/jubjub/mod.rs @@ -224,10 +224,10 @@ impl JubjubBls12 { let mut pedersen_hash_generators = vec![]; for m in 0..5 { - use byteorder::{WriteBytesExt, BigEndian}; + use byteorder::{WriteBytesExt, LittleEndian}; let mut segment_number = [0u8; 4]; - (&mut segment_number[0..4]).write_u32::(m).unwrap(); + (&mut segment_number[0..4]).write_u32::(m).unwrap(); pedersen_hash_generators.push( find_group_hash( @@ -259,22 +259,22 @@ impl JubjubBls12 { let mut fixed_base_generators = vec![edwards::Point::zero(); FixedGenerators::Max as usize]; fixed_base_generators[FixedGenerators::ProofGenerationKey as usize] = - find_group_hash(b"0", constants::PROOF_GENERATION_KEY_BASE_GENERATOR_PERSONALIZATION, &tmp_params); + find_group_hash(&[], constants::PROOF_GENERATION_KEY_BASE_GENERATOR_PERSONALIZATION, &tmp_params); fixed_base_generators[FixedGenerators::NoteCommitmentRandomness as usize] = - find_group_hash(b"0", constants::NOTE_COMMITMENT_RANDOMNESS_GENERATOR_PERSONALIZATION, &tmp_params); + find_group_hash(b"r", constants::PEDERSEN_HASH_GENERATORS_PERSONALIZATION, &tmp_params); fixed_base_generators[FixedGenerators::NullifierPosition as usize] = - find_group_hash(b"0", constants::NULLIFIER_POSITION_IN_TREE_GENERATOR_PERSONALIZATION, &tmp_params); + find_group_hash(&[], constants::NULLIFIER_POSITION_IN_TREE_GENERATOR_PERSONALIZATION, &tmp_params); fixed_base_generators[FixedGenerators::ValueCommitmentValue as usize] = - find_group_hash(b"0", constants::VALUE_COMMITMENT_VALUE_GENERATOR_PERSONALIZATION, &tmp_params); + find_group_hash(b"v", constants::VALUE_COMMITMENT_GENERATOR_PERSONALIZATION, &tmp_params); fixed_base_generators[FixedGenerators::ValueCommitmentRandomness as usize] = - find_group_hash(b"0", constants::VALUE_COMMITMENT_RANDOMNESS_GENERATOR_PERSONALIZATION, &tmp_params); + find_group_hash(b"r", constants::VALUE_COMMITMENT_GENERATOR_PERSONALIZATION, &tmp_params); fixed_base_generators[FixedGenerators::SpendingKeyGenerator as usize] = - find_group_hash(b"0", constants::SPENDING_KEY_GENERATOR_PERSONALIZATION, &tmp_params); + find_group_hash(&[], constants::SPENDING_KEY_GENERATOR_PERSONALIZATION, &tmp_params); // Check for duplicates, far worse than spec inconsistencies! for (i, p1) in fixed_base_generators.iter().enumerate() { From f9e58c01cec86cb6e7270306dbe7b044793936b8 Mon Sep 17 00:00:00 2001 From: Sean Bowe Date: Mon, 19 Mar 2018 17:54:44 -0600 Subject: [PATCH 10/11] Swap bit-endianness of value in note commitment. --- src/circuit/sapling/mod.rs | 9 +++------ src/jubjub/edwards.rs | 30 ++---------------------------- src/lib.rs | 1 + src/primitives/mod.rs | 11 +++++++++-- src/util.rs | 27 +++++++++++++++++++++++++++ 5 files changed, 42 insertions(+), 36 deletions(-) create mode 100644 src/util.rs diff --git a/src/circuit/sapling/mod.rs b/src/circuit/sapling/mod.rs index 840ad9f..159381f 100644 --- a/src/circuit/sapling/mod.rs +++ b/src/circuit/sapling/mod.rs @@ -82,7 +82,7 @@ fn expose_value_commitment( CS: ConstraintSystem { // Booleanize the value into little-endian bit order - let mut value_bits = boolean::u64_into_boolean_vec_le( + let value_bits = boolean::u64_into_boolean_vec_le( cs.namespace(|| "value"), value_commitment.as_ref().map(|c| c.value) )?; @@ -121,9 +121,6 @@ fn expose_value_commitment( // Expose the commitment as an input to the circuit cv.inputize(cs.namespace(|| "commitment point"))?; - // Reorder value_bits so that it's big-endian - value_bits.reverse(); - Ok(value_bits) } @@ -624,7 +621,7 @@ fn test_input_circuit_with_bls12_381() { assert!(cs.is_satisfied()); assert_eq!(cs.num_constraints(), 98776); - assert_eq!(cs.hash(), "2080d5f350cd7eff7742ab05dff18f82c0a2f29a5d2a758d805236067b2ed31f"); + assert_eq!(cs.hash(), "ba8b2232a910b00399e90030c87c16a770e6e692fe3b4316675bdd7795df6e50"); assert_eq!(cs.num_inputs(), 8); assert_eq!(cs.get_input(0, "ONE"), Fr::one()); @@ -752,7 +749,7 @@ fn test_output_circuit_with_bls12_381() { assert!(cs.is_satisfied()); assert_eq!(cs.num_constraints(), 7827); - assert_eq!(cs.hash(), "a7810a444f7ef6d0caa8ba026ce06e64654863cd0557241282ca337858039a53"); + assert_eq!(cs.hash(), "8db50ff0e14fae19a7d83ef47f6da3a7e3e2644d251e37b387c6408d85df3ae7"); let expected_cm = payment_address.create_note( value_commitment.value, diff --git a/src/jubjub/edwards.rs b/src/jubjub/edwards.rs index e4f5e85..d1d2772 100644 --- a/src/jubjub/edwards.rs +++ b/src/jubjub/edwards.rs @@ -26,6 +26,8 @@ use std::io::{ Read }; +use util::swap_bits_u64; + // Represents the affine point (X/Z, Y/Z) via the extended // twisted Edwards coordinates. // @@ -89,34 +91,6 @@ impl PartialEq for Point { } } -fn swap_bits_u64(x: u64) -> u64 -{ - let mut tmp = 0; - for i in 0..64 { - tmp |= ((x >> i) & 1) << (63 - i); - } - tmp -} - -#[test] -fn test_swap_bits_u64() { - assert_eq!(swap_bits_u64(17182120934178543809), 0b1000001100011011110000011000111000101111111001001100111001110111); - assert_eq!(swap_bits_u64(15135675916470734665), 0b1001001011110010001101010010001110110000100111010011000001001011); - assert_eq!(swap_bits_u64(6724233301461108393), 0b1001010101100000100011100001010111110001011000101000101010111010); - assert_eq!(swap_bits_u64(206708183275952289), 0b1000010100011010001010100011101011111111111110100111101101000000); - assert_eq!(swap_bits_u64(12712751566144824320), 0b0000000000100110010110111000001110001100001000110011011000001101); - - let mut a = 15863238721320035327u64; - for _ in 0..1000 { - a = a.wrapping_mul(a); - - let swapped = swap_bits_u64(a); - let unswapped = swap_bits_u64(swapped); - - assert_eq!(a, unswapped); - } -} - impl Point { pub fn read( reader: R, diff --git a/src/lib.rs b/src/lib.rs index 0f5fded..a053dd1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -15,3 +15,4 @@ pub mod circuit; pub mod pedersen_hash; pub mod primitives; pub mod constants; +mod util; diff --git a/src/primitives/mod.rs b/src/primitives/mod.rs index bb1298b..d631eea 100644 --- a/src/primitives/mod.rs +++ b/src/primitives/mod.rs @@ -28,6 +28,8 @@ use jubjub::{ use blake2_rfc::blake2s::Blake2s; +use util::swap_bits_u64; + #[derive(Clone)] pub struct ValueCommitment { pub value: u64, @@ -193,8 +195,13 @@ impl Note { // Calculate the note contents, as bytes let mut note_contents = vec![]; - // Write the value in big endian - (&mut note_contents).write_u64::(self.value).unwrap(); + // Write the value in little-endian bit order + // swapping the bits to ensure the order is + // correct (LittleEndian byte order would + // be incorrect here.) + (&mut note_contents).write_u64::( + swap_bits_u64(self.value) + ).unwrap(); // Write g_d self.g_d.write(&mut note_contents).unwrap(); diff --git a/src/util.rs b/src/util.rs new file mode 100644 index 0000000..5291fef --- /dev/null +++ b/src/util.rs @@ -0,0 +1,27 @@ +pub fn swap_bits_u64(x: u64) -> u64 +{ + let mut tmp = 0; + for i in 0..64 { + tmp |= ((x >> i) & 1) << (63 - i); + } + tmp +} + +#[test] +fn test_swap_bits_u64() { + assert_eq!(swap_bits_u64(17182120934178543809), 0b1000001100011011110000011000111000101111111001001100111001110111); + assert_eq!(swap_bits_u64(15135675916470734665), 0b1001001011110010001101010010001110110000100111010011000001001011); + assert_eq!(swap_bits_u64(6724233301461108393), 0b1001010101100000100011100001010111110001011000101000101010111010); + assert_eq!(swap_bits_u64(206708183275952289), 0b1000010100011010001010100011101011111111111110100111101101000000); + assert_eq!(swap_bits_u64(12712751566144824320), 0b0000000000100110010110111000001110001100001000110011011000001101); + + let mut a = 15863238721320035327u64; + for _ in 0..1000 { + a = a.wrapping_mul(a); + + let swapped = swap_bits_u64(a); + let unswapped = swap_bits_u64(swapped); + + assert_eq!(a, unswapped); + } +} From 601e8e38f861a98102b4a81c4fb333dc190eddbf Mon Sep 17 00:00:00 2001 From: Sean Bowe Date: Mon, 19 Mar 2018 18:06:44 -0600 Subject: [PATCH 11/11] Little-endian byte order interpretation of the output of CRH^ivk. --- src/circuit/sapling/mod.rs | 8 +++++--- src/primitives/mod.rs | 3 +++ 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/circuit/sapling/mod.rs b/src/circuit/sapling/mod.rs index 159381f..4e2ea40 100644 --- a/src/circuit/sapling/mod.rs +++ b/src/circuit/sapling/mod.rs @@ -221,8 +221,10 @@ impl<'a, E: JubjubEngine> Circuit for Spend<'a, E> { constants::CRH_IVK_PERSONALIZATION )?; - // Little endian bit order - ivk.reverse(); + // Swap bit-endianness in each byte + for ivk_byte in ivk.chunks_mut(8) { + ivk_byte.reverse(); + } // drop_5 to ensure it's in the field ivk.truncate(E::Fs::CAPACITY as usize); @@ -621,7 +623,7 @@ fn test_input_circuit_with_bls12_381() { assert!(cs.is_satisfied()); assert_eq!(cs.num_constraints(), 98776); - assert_eq!(cs.hash(), "ba8b2232a910b00399e90030c87c16a770e6e692fe3b4316675bdd7795df6e50"); + assert_eq!(cs.hash(), "8211d52b5ad2618b2f8106c7c3f9ab213f6206e3ddbbb39e786167de5ea85dc3"); assert_eq!(cs.num_inputs(), 8); assert_eq!(cs.get_input(0, "ONE"), Fr::one()); diff --git a/src/primitives/mod.rs b/src/primitives/mod.rs index d631eea..825aed5 100644 --- a/src/primitives/mod.rs +++ b/src/primitives/mod.rs @@ -96,6 +96,9 @@ impl ViewingKey { h.update(&preimage); let mut h = h.finalize().as_ref().to_vec(); + // Reverse the bytes to interpret it in little-endian byte order + h.reverse(); + // Drop the first five bits, so it can be interpreted as a scalar. h[0] &= 0b0000_0111;