cargo fmt zcash_proofs

This commit is contained in:
Eirik Ogilvie-Wigley
2019-08-15 10:40:07 -06:00
parent 81c58172c3
commit 272be62212
11 changed files with 595 additions and 847 deletions

View File

@@ -1,30 +1,20 @@
extern crate ff;
extern crate bellman; extern crate bellman;
extern crate ff;
extern crate pairing; extern crate pairing;
extern crate rand_core; extern crate rand_core;
extern crate rand_xorshift; extern crate rand_xorshift;
extern crate zcash_primitives; extern crate zcash_primitives;
extern crate zcash_proofs; extern crate zcash_proofs;
use ff::Field;
use std::time::{Duration, Instant};
use zcash_primitives::jubjub::{
JubjubBls12,
edwards,
fs,
};
use zcash_proofs::circuit::sapling::{
Spend
};
use zcash_primitives::primitives::{
Diversifier,
ProofGenerationKey,
ValueCommitment
};
use bellman::groth16::*; use bellman::groth16::*;
use ff::Field;
use pairing::bls12_381::{Bls12, Fr};
use rand_core::{RngCore, SeedableRng}; use rand_core::{RngCore, SeedableRng};
use rand_xorshift::XorShiftRng; use rand_xorshift::XorShiftRng;
use pairing::bls12_381::{Bls12, Fr}; use std::time::{Duration, Instant};
use zcash_primitives::jubjub::{edwards, fs, JubjubBls12};
use zcash_primitives::primitives::{Diversifier, ProofGenerationKey, ValueCommitment};
use zcash_proofs::circuit::sapling::Spend;
const TREE_DEPTH: usize = 32; const TREE_DEPTH: usize = 32;
@@ -45,10 +35,11 @@ fn main() {
commitment_randomness: None, commitment_randomness: None,
ar: None, ar: None,
auth_path: vec![None; TREE_DEPTH], auth_path: vec![None; TREE_DEPTH],
anchor: None anchor: None,
}, },
rng rng,
).unwrap(); )
.unwrap();
const SAMPLES: u32 = 50; const SAMPLES: u32 = 50;
@@ -56,7 +47,7 @@ fn main() {
for _ in 0..SAMPLES { for _ in 0..SAMPLES {
let value_commitment = ValueCommitment { let value_commitment = ValueCommitment {
value: 1, value: 1,
randomness: fs::Fs::random(rng) randomness: fs::Fs::random(rng),
}; };
let nsk = fs::Fs::random(rng); let nsk = fs::Fs::random(rng);
@@ -64,7 +55,7 @@ fn main() {
let proof_generation_key = ProofGenerationKey { let proof_generation_key = ProofGenerationKey {
ak: ak.clone(), ak: ak.clone(),
nsk: nsk.clone() nsk: nsk.clone(),
}; };
let viewing_key = proof_generation_key.into_viewing_key(jubjub_params); let viewing_key = proof_generation_key.into_viewing_key(jubjub_params);
@@ -78,11 +69,7 @@ fn main() {
Diversifier(d) Diversifier(d)
}; };
if let Some(p) = viewing_key.into_payment_address( if let Some(p) = viewing_key.into_payment_address(diversifier, jubjub_params) {
diversifier,
jubjub_params
)
{
payment_address = p; payment_address = p;
break; break;
} }
@@ -94,21 +81,25 @@ fn main() {
let anchor = Fr::random(rng); let anchor = Fr::random(rng);
let start = Instant::now(); let start = Instant::now();
let _ = create_random_proof(Spend { let _ = create_random_proof(
params: jubjub_params, Spend {
value_commitment: Some(value_commitment), params: jubjub_params,
proof_generation_key: Some(proof_generation_key), value_commitment: Some(value_commitment),
payment_address: Some(payment_address), proof_generation_key: Some(proof_generation_key),
commitment_randomness: Some(commitment_randomness), payment_address: Some(payment_address),
ar: Some(ar), commitment_randomness: Some(commitment_randomness),
auth_path: auth_path, ar: Some(ar),
anchor: Some(anchor) auth_path: auth_path,
}, &groth_params, rng).unwrap(); anchor: Some(anchor),
},
&groth_params,
rng,
)
.unwrap();
total_time += start.elapsed(); total_time += start.elapsed();
} }
let avg = total_time / SAMPLES; let avg = total_time / SAMPLES;
let avg = avg.subsec_nanos() as f64 / 1_000_000_000f64 let avg = avg.subsec_nanos() as f64 / 1_000_000_000f64 + (avg.as_secs() as f64);
+ (avg.as_secs() as f64);
println!("Average proving time (in seconds): {}", avg); println!("Average proving time (in seconds): {}", avg);
} }

File diff suppressed because it is too large Load Diff

View File

@@ -1,17 +1,13 @@
use super::ecc::{ use super::ecc::{EdwardsPoint, MontgomeryPoint};
MontgomeryPoint,
EdwardsPoint
};
use bellman::gadgets::boolean::Boolean; use bellman::gadgets::boolean::Boolean;
use zcash_primitives::jubjub::*;
use bellman::{
ConstraintSystem, SynthesisError
};
use bellman::gadgets::lookup::*; use bellman::gadgets::lookup::*;
use bellman::{ConstraintSystem, SynthesisError};
use zcash_primitives::jubjub::*;
pub use zcash_primitives::pedersen_hash::Personalization; pub use zcash_primitives::pedersen_hash::Personalization;
fn get_constant_bools(person: &Personalization) -> Vec<Boolean> { fn get_constant_bools(person: &Personalization) -> Vec<Boolean> {
person.get_bits() person
.get_bits()
.into_iter() .into_iter()
.map(|e| Boolean::constant(e)) .map(|e| Boolean::constant(e))
.collect() .collect()
@@ -21,9 +17,10 @@ pub fn pedersen_hash<E: JubjubEngine, CS>(
mut cs: CS, mut cs: CS,
personalization: Personalization, personalization: Personalization,
bits: &[Boolean], bits: &[Boolean],
params: &E::Params params: &E::Params,
) -> Result<EdwardsPoint<E>, SynthesisError> ) -> Result<EdwardsPoint<E>, SynthesisError>
where CS: ConstraintSystem<E> where
CS: ConstraintSystem<E>,
{ {
let personalization = get_constant_bools(&personalization); let personalization = get_constant_bools(&personalization);
assert_eq!(personalization.len(), 6); assert_eq!(personalization.len(), 6);
@@ -36,8 +33,7 @@ pub fn pedersen_hash<E: JubjubEngine, CS>(
let mut segment_i = 0; let mut segment_i = 0;
loop { loop {
let mut segment_result = None; let mut segment_result = None;
let mut segment_windows = &segment_generators.next() let mut segment_windows = &segment_generators.next().expect("enough segments")[..];
.expect("enough segments")[..];
let mut window_i = 0; let mut window_i = 0;
while let Some(a) = bits.next() { while let Some(a) = bits.next() {
@@ -47,7 +43,7 @@ pub fn pedersen_hash<E: JubjubEngine, CS>(
let tmp = lookup3_xy_with_conditional_negation( let tmp = lookup3_xy_with_conditional_negation(
cs.namespace(|| format!("segment {}, window {}", segment_i, window_i)), cs.namespace(|| format!("segment {}, window {}", segment_i, window_i)),
&[a.clone(), b.clone(), c.clone()], &[a.clone(), b.clone(), c.clone()],
&segment_windows[0] &segment_windows[0],
)?; )?;
let tmp = MontgomeryPoint::interpret_unchecked(tmp.0, tmp.1); let tmp = MontgomeryPoint::interpret_unchecked(tmp.0, tmp.1);
@@ -55,12 +51,14 @@ pub fn pedersen_hash<E: JubjubEngine, CS>(
match segment_result { match segment_result {
None => { None => {
segment_result = Some(tmp); segment_result = Some(tmp);
}, }
Some(ref mut segment_result) => { Some(ref mut segment_result) => {
*segment_result = tmp.add( *segment_result = tmp.add(
cs.namespace(|| format!("addition of segment {}, window {}", segment_i, window_i)), cs.namespace(|| {
format!("addition of segment {}, window {}", segment_i, window_i)
}),
segment_result, segment_result,
params params,
)?; )?;
} }
} }
@@ -79,22 +77,24 @@ pub fn pedersen_hash<E: JubjubEngine, CS>(
// Convert this segment into twisted Edwards form. // Convert this segment into twisted Edwards form.
let segment_result = segment_result.into_edwards( let segment_result = segment_result.into_edwards(
cs.namespace(|| format!("conversion of segment {} into edwards", segment_i)), cs.namespace(|| format!("conversion of segment {} into edwards", segment_i)),
params params,
)?; )?;
match edwards_result { match edwards_result {
Some(ref mut edwards_result) => { Some(ref mut edwards_result) => {
*edwards_result = segment_result.add( *edwards_result = segment_result.add(
cs.namespace(|| format!("addition of segment {} to accumulator", segment_i)), cs.namespace(|| {
format!("addition of segment {} to accumulator", segment_i)
}),
edwards_result, edwards_result,
params params,
)?; )?;
}, }
None => { None => {
edwards_result = Some(segment_result); edwards_result = Some(segment_result);
} }
} }
}, }
None => { None => {
// We didn't process any new bits. // We didn't process any new bits.
break; break;
@@ -110,37 +110,44 @@ pub fn pedersen_hash<E: JubjubEngine, CS>(
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use super::*; use super::*;
use bellman::gadgets::boolean::{AllocatedBit, Boolean};
use bellman::gadgets::test::*; use bellman::gadgets::test::*;
use bellman::gadgets::boolean::{Boolean, AllocatedBit};
use zcash_primitives::pedersen_hash;
use ff::PrimeField; use ff::PrimeField;
use pairing::bls12_381::{Bls12, Fr}; use pairing::bls12_381::{Bls12, Fr};
use rand_core::{RngCore, SeedableRng}; use rand_core::{RngCore, SeedableRng};
use rand_xorshift::XorShiftRng; use rand_xorshift::XorShiftRng;
use zcash_primitives::pedersen_hash;
#[test] #[test]
fn test_pedersen_hash_constraints() { fn test_pedersen_hash_constraints() {
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
0xe5, 0xbc, 0xe5,
]); ]);
let params = &JubjubBls12::new(); let params = &JubjubBls12::new();
let mut cs = TestConstraintSystem::<Bls12>::new(); let mut cs = TestConstraintSystem::<Bls12>::new();
let input: Vec<bool> = (0..(Fr::NUM_BITS * 2)).map(|_| rng.next_u32() % 2 != 0).collect(); let input: Vec<bool> = (0..(Fr::NUM_BITS * 2))
.map(|_| rng.next_u32() % 2 != 0)
.collect();
let input_bools: Vec<Boolean> = input.iter().enumerate().map(|(i, b)| { let input_bools: Vec<Boolean> = input
Boolean::from( .iter()
AllocatedBit::alloc(cs.namespace(|| format!("input {}", i)), Some(*b)).unwrap() .enumerate()
) .map(|(i, b)| {
}).collect(); Boolean::from(
AllocatedBit::alloc(cs.namespace(|| format!("input {}", i)), Some(*b)).unwrap(),
)
})
.collect();
pedersen_hash( pedersen_hash(
cs.namespace(|| "pedersen hash"), cs.namespace(|| "pedersen hash"),
Personalization::NoteCommitment, Personalization::NoteCommitment,
&input_bools, &input_bools,
params params,
).unwrap(); )
.unwrap();
assert!(cs.is_satisfied()); assert!(cs.is_satisfied());
assert_eq!(cs.num_constraints(), 1377); assert_eq!(cs.num_constraints(), 1377);
@@ -149,8 +156,8 @@ mod test {
#[test] #[test]
fn test_pedersen_hash() { fn test_pedersen_hash() {
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
0xe5, 0xbc, 0xe5,
]); ]);
let params = &JubjubBls12::new(); let params = &JubjubBls12::new();
@@ -160,26 +167,33 @@ mod test {
let mut cs = TestConstraintSystem::<Bls12>::new(); let mut cs = TestConstraintSystem::<Bls12>::new();
let input_bools: Vec<Boolean> = input.iter().enumerate().map(|(i, b)| { let input_bools: Vec<Boolean> = input
Boolean::from( .iter()
AllocatedBit::alloc(cs.namespace(|| format!("input {}", i)), Some(*b)).unwrap() .enumerate()
) .map(|(i, b)| {
}).collect(); Boolean::from(
AllocatedBit::alloc(cs.namespace(|| format!("input {}", i)), Some(*b))
.unwrap(),
)
})
.collect();
let res = pedersen_hash( let res = pedersen_hash(
cs.namespace(|| "pedersen hash"), cs.namespace(|| "pedersen hash"),
Personalization::MerkleTree(1), Personalization::MerkleTree(1),
&input_bools, &input_bools,
params params,
).unwrap(); )
.unwrap();
assert!(cs.is_satisfied()); assert!(cs.is_satisfied());
let expected = pedersen_hash::pedersen_hash::<Bls12, _>( let expected = pedersen_hash::pedersen_hash::<Bls12, _>(
Personalization::MerkleTree(1), Personalization::MerkleTree(1),
input.clone().into_iter(), input.clone().into_iter(),
params params,
).into_xy(); )
.into_xy();
assert_eq!(res.get_x().get_value().unwrap(), expected.0); assert_eq!(res.get_x().get_value().unwrap(), expected.0);
assert_eq!(res.get_y().get_value().unwrap(), expected.1); assert_eq!(res.get_y().get_value().unwrap(), expected.1);
@@ -188,8 +202,9 @@ mod test {
let unexpected = pedersen_hash::pedersen_hash::<Bls12, _>( let unexpected = pedersen_hash::pedersen_hash::<Bls12, _>(
Personalization::MerkleTree(0), Personalization::MerkleTree(0),
input.into_iter(), input.into_iter(),
params params,
).into_xy(); )
.into_xy();
assert!(res.get_x().get_value().unwrap() != unexpected.0); assert!(res.get_x().get_value().unwrap() != unexpected.0);
assert!(res.get_y().get_value().unwrap() != unexpected.1); assert!(res.get_y().get_value().unwrap() != unexpected.1);

View File

@@ -1,31 +1,20 @@
use ff::{Field, PrimeField, PrimeFieldRepr}; use ff::{Field, PrimeField, PrimeFieldRepr};
use bellman::{ use bellman::{Circuit, ConstraintSystem, SynthesisError};
SynthesisError,
ConstraintSystem,
Circuit
};
use zcash_primitives::jubjub::{ use zcash_primitives::jubjub::{FixedGenerators, JubjubEngine};
JubjubEngine,
FixedGenerators
};
use zcash_primitives::constants; use zcash_primitives::constants;
use zcash_primitives::primitives::{ use zcash_primitives::primitives::{PaymentAddress, ProofGenerationKey, ValueCommitment};
ValueCommitment,
ProofGenerationKey,
PaymentAddress
};
use bellman::gadgets::Assignment;
use bellman::gadgets::boolean;
use super::ecc; use super::ecc;
use super::pedersen_hash; use super::pedersen_hash;
use bellman::gadgets::blake2s; use bellman::gadgets::blake2s;
use bellman::gadgets::num; use bellman::gadgets::boolean;
use bellman::gadgets::multipack; use bellman::gadgets::multipack;
use bellman::gadgets::num;
use bellman::gadgets::Assignment;
pub const TREE_DEPTH: usize = zcash_primitives::sapling::SAPLING_COMMITMENT_TREE_DEPTH; pub const TREE_DEPTH: usize = zcash_primitives::sapling::SAPLING_COMMITMENT_TREE_DEPTH;
@@ -54,7 +43,7 @@ pub struct Spend<'a, E: JubjubEngine> {
/// The anchor; the root of the tree. If the note being /// The anchor; the root of the tree. If the note being
/// spent is zero-value, this can be anything. /// spent is zero-value, this can be anything.
pub anchor: Option<E::Fr> pub anchor: Option<E::Fr>,
} }
/// This is an output circuit instance. /// This is an output circuit instance.
@@ -71,7 +60,7 @@ pub struct Output<'a, E: JubjubEngine> {
pub commitment_randomness: Option<E::Fs>, pub commitment_randomness: Option<E::Fs>,
/// The ephemeral secret key for DH with recipient /// The ephemeral secret key for DH with recipient
pub esk: Option<E::Fs> pub esk: Option<E::Fs>,
} }
/// Exposes a Pedersen commitment to the value as an /// Exposes a Pedersen commitment to the value as an
@@ -79,15 +68,16 @@ pub struct Output<'a, E: JubjubEngine> {
fn expose_value_commitment<E, CS>( fn expose_value_commitment<E, CS>(
mut cs: CS, mut cs: CS,
value_commitment: Option<ValueCommitment<E>>, value_commitment: Option<ValueCommitment<E>>,
params: &E::Params params: &E::Params,
) -> Result<Vec<boolean::Boolean>, SynthesisError> ) -> Result<Vec<boolean::Boolean>, SynthesisError>
where E: JubjubEngine, where
CS: ConstraintSystem<E> E: JubjubEngine,
CS: ConstraintSystem<E>,
{ {
// Booleanize the value into little-endian bit order // Booleanize the value into little-endian bit order
let value_bits = boolean::u64_into_boolean_vec_le( let value_bits = boolean::u64_into_boolean_vec_le(
cs.namespace(|| "value"), cs.namespace(|| "value"),
value_commitment.as_ref().map(|c| c.value) value_commitment.as_ref().map(|c| c.value),
)?; )?;
// Compute the note value in the exponent // Compute the note value in the exponent
@@ -95,7 +85,7 @@ fn expose_value_commitment<E, CS>(
cs.namespace(|| "compute the value in the exponent"), cs.namespace(|| "compute the value in the exponent"),
FixedGenerators::ValueCommitmentValue, FixedGenerators::ValueCommitmentValue,
&value_bits, &value_bits,
params params,
)?; )?;
// Booleanize the randomness. This does not ensure // Booleanize the randomness. This does not ensure
@@ -103,7 +93,7 @@ fn expose_value_commitment<E, CS>(
// it doesn't matter for security. // it doesn't matter for security.
let rcv = boolean::field_into_boolean_vec_le( let rcv = boolean::field_into_boolean_vec_le(
cs.namespace(|| "rcv"), cs.namespace(|| "rcv"),
value_commitment.as_ref().map(|c| c.randomness) value_commitment.as_ref().map(|c| c.randomness),
)?; )?;
// Compute the randomness in the exponent // Compute the randomness in the exponent
@@ -111,15 +101,11 @@ fn expose_value_commitment<E, CS>(
cs.namespace(|| "computation of rcv"), cs.namespace(|| "computation of rcv"),
FixedGenerators::ValueCommitmentRandomness, FixedGenerators::ValueCommitmentRandomness,
&rcv, &rcv,
params params,
)?; )?;
// Compute the Pedersen commitment to the value // Compute the Pedersen commitment to the value
let cv = value.add( let cv = value.add(cs.namespace(|| "computation of cv"), &rcv, params)?;
cs.namespace(|| "computation of cv"),
&rcv,
params
)?;
// Expose the commitment as an input to the circuit // Expose the commitment as an input to the circuit
cv.inputize(cs.namespace(|| "commitment point"))?; cv.inputize(cs.namespace(|| "commitment point"))?;
@@ -128,43 +114,32 @@ fn expose_value_commitment<E, CS>(
} }
impl<'a, E: JubjubEngine> Circuit<E> for Spend<'a, E> { impl<'a, E: JubjubEngine> Circuit<E> for Spend<'a, E> {
fn synthesize<CS: ConstraintSystem<E>>(self, cs: &mut CS) -> Result<(), SynthesisError> fn synthesize<CS: ConstraintSystem<E>>(self, cs: &mut CS) -> Result<(), SynthesisError> {
{
// Prover witnesses ak (ensures that it's on the curve) // Prover witnesses ak (ensures that it's on the curve)
let ak = ecc::EdwardsPoint::witness( let ak = ecc::EdwardsPoint::witness(
cs.namespace(|| "ak"), cs.namespace(|| "ak"),
self.proof_generation_key.as_ref().map(|k| k.ak.clone()), self.proof_generation_key.as_ref().map(|k| k.ak.clone()),
self.params self.params,
)?; )?;
// There are no sensible attacks on small order points // There are no sensible attacks on small order points
// of ak (that we're aware of!) but it's a cheap check, // of ak (that we're aware of!) but it's a cheap check,
// so we do it. // so we do it.
ak.assert_not_small_order( ak.assert_not_small_order(cs.namespace(|| "ak not small order"), self.params)?;
cs.namespace(|| "ak not small order"),
self.params
)?;
// Rerandomize ak and expose it as an input to the circuit // Rerandomize ak and expose it as an input to the circuit
{ {
let ar = boolean::field_into_boolean_vec_le( let ar = boolean::field_into_boolean_vec_le(cs.namespace(|| "ar"), self.ar)?;
cs.namespace(|| "ar"),
self.ar
)?;
// Compute the randomness in the exponent // Compute the randomness in the exponent
let ar = ecc::fixed_base_multiplication( let ar = ecc::fixed_base_multiplication(
cs.namespace(|| "computation of randomization for the signing key"), cs.namespace(|| "computation of randomization for the signing key"),
FixedGenerators::SpendingKeyGenerator, FixedGenerators::SpendingKeyGenerator,
&ar, &ar,
self.params self.params,
)?; )?;
let rk = ak.add( let rk = ak.add(cs.namespace(|| "computation of rk"), &ar, self.params)?;
cs.namespace(|| "computation of rk"),
&ar,
self.params
)?;
rk.inputize(cs.namespace(|| "rk"))?; rk.inputize(cs.namespace(|| "rk"))?;
} }
@@ -175,7 +150,7 @@ impl<'a, E: JubjubEngine> Circuit<E> for Spend<'a, E> {
// Witness nsk as bits // Witness nsk as bits
let nsk = boolean::field_into_boolean_vec_le( let nsk = boolean::field_into_boolean_vec_le(
cs.namespace(|| "nsk"), cs.namespace(|| "nsk"),
self.proof_generation_key.as_ref().map(|k| k.nsk.clone()) self.proof_generation_key.as_ref().map(|k| k.nsk.clone()),
)?; )?;
// NB: We don't ensure that the bit representation of nsk // NB: We don't ensure that the bit representation of nsk
@@ -188,7 +163,7 @@ impl<'a, E: JubjubEngine> Circuit<E> for Spend<'a, E> {
cs.namespace(|| "computation of nk"), cs.namespace(|| "computation of nk"),
FixedGenerators::ProofGenerationKey, FixedGenerators::ProofGenerationKey,
&nsk, &nsk,
self.params self.params,
)?; )?;
} }
@@ -196,9 +171,7 @@ impl<'a, E: JubjubEngine> Circuit<E> for Spend<'a, E> {
let mut ivk_preimage = vec![]; let mut ivk_preimage = vec![];
// Place ak in the preimage for CRH^ivk // Place ak in the preimage for CRH^ivk
ivk_preimage.extend( ivk_preimage.extend(ak.repr(cs.namespace(|| "representation of ak"))?);
ak.repr(cs.namespace(|| "representation of ak"))?
);
// This is the nullifier preimage for PRF^nf // This is the nullifier preimage for PRF^nf
let mut nf_preimage = vec![]; let mut nf_preimage = vec![];
@@ -206,9 +179,7 @@ impl<'a, E: JubjubEngine> Circuit<E> for Spend<'a, E> {
// Extend ivk and nf preimages with the representation of // Extend ivk and nf preimages with the representation of
// nk. // nk.
{ {
let repr_nk = nk.repr( let repr_nk = nk.repr(cs.namespace(|| "representation of nk"))?;
cs.namespace(|| "representation of nk")
)?;
ivk_preimage.extend(repr_nk.iter().cloned()); ivk_preimage.extend(repr_nk.iter().cloned());
nf_preimage.extend(repr_nk); nf_preimage.extend(repr_nk);
@@ -221,7 +192,7 @@ impl<'a, E: JubjubEngine> Circuit<E> for Spend<'a, E> {
let mut ivk = blake2s::blake2s( let mut ivk = blake2s::blake2s(
cs.namespace(|| "computation of ivk"), cs.namespace(|| "computation of ivk"),
&ivk_preimage, &ivk_preimage,
constants::CRH_IVK_PERSONALIZATION constants::CRH_IVK_PERSONALIZATION,
)?; )?;
// drop_5 to ensure it's in the field // drop_5 to ensure it's in the field
@@ -239,7 +210,7 @@ impl<'a, E: JubjubEngine> Circuit<E> for Spend<'a, E> {
ecc::EdwardsPoint::witness( ecc::EdwardsPoint::witness(
cs.namespace(|| "witness g_d"), cs.namespace(|| "witness g_d"),
self.payment_address.as_ref().and_then(|a| a.g_d(params)), self.payment_address.as_ref().and_then(|a| a.g_d(params)),
self.params self.params,
)? )?
}; };
@@ -247,17 +218,10 @@ impl<'a, E: JubjubEngine> Circuit<E> for Spend<'a, E> {
// is already done in the Output circuit, and this proof ensures // 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 // g_d is bound to a product of that check, but for defense in
// depth let's check it anyway. It's cheap. // depth let's check it anyway. It's cheap.
g_d.assert_not_small_order( g_d.assert_not_small_order(cs.namespace(|| "g_d not small order"), self.params)?;
cs.namespace(|| "g_d not small order"),
self.params
)?;
// Compute pk_d = g_d^ivk // Compute pk_d = g_d^ivk
let pk_d = g_d.mul( let pk_d = g_d.mul(cs.namespace(|| "compute pk_d"), &ivk, self.params)?;
cs.namespace(|| "compute pk_d"),
&ivk,
self.params
)?;
// Compute note contents: // Compute note contents:
// value (in big endian) followed by g_d and pk_d // value (in big endian) followed by g_d and pk_d
@@ -271,18 +235,14 @@ impl<'a, E: JubjubEngine> Circuit<E> for Spend<'a, E> {
let value_bits = expose_value_commitment( let value_bits = expose_value_commitment(
cs.namespace(|| "value commitment"), cs.namespace(|| "value commitment"),
self.value_commitment, self.value_commitment,
self.params self.params,
)?; )?;
// Compute the note's value as a linear combination // Compute the note's value as a linear combination
// of the bits. // of the bits.
let mut coeff = E::Fr::one(); let mut coeff = E::Fr::one();
for bit in &value_bits { for bit in &value_bits {
value_num = value_num.add_bool_with_coeff( value_num = value_num.add_bool_with_coeff(CS::one(), bit, coeff);
CS::one(),
bit,
coeff
);
coeff.double(); coeff.double();
} }
@@ -291,14 +251,10 @@ impl<'a, E: JubjubEngine> Circuit<E> for Spend<'a, E> {
} }
// Place g_d in the note // Place g_d in the note
note_contents.extend( note_contents.extend(g_d.repr(cs.namespace(|| "representation of g_d"))?);
g_d.repr(cs.namespace(|| "representation of g_d"))?
);
// Place pk_d in the note // Place pk_d in the note
note_contents.extend( note_contents.extend(pk_d.repr(cs.namespace(|| "representation of pk_d"))?);
pk_d.repr(cs.namespace(|| "representation of pk_d"))?
);
assert_eq!( assert_eq!(
note_contents.len(), note_contents.len(),
@@ -312,14 +268,14 @@ impl<'a, E: JubjubEngine> Circuit<E> for Spend<'a, E> {
cs.namespace(|| "note content hash"), cs.namespace(|| "note content hash"),
pedersen_hash::Personalization::NoteCommitment, pedersen_hash::Personalization::NoteCommitment,
&note_contents, &note_contents,
self.params self.params,
)?; )?;
{ {
// Booleanize the randomness for the note commitment // Booleanize the randomness for the note commitment
let rcm = boolean::field_into_boolean_vec_le( let rcm = boolean::field_into_boolean_vec_le(
cs.namespace(|| "rcm"), cs.namespace(|| "rcm"),
self.commitment_randomness self.commitment_randomness,
)?; )?;
// Compute the note commitment randomness in the exponent // Compute the note commitment randomness in the exponent
@@ -327,7 +283,7 @@ impl<'a, E: JubjubEngine> Circuit<E> for Spend<'a, E> {
cs.namespace(|| "computation of commitment randomness"), cs.namespace(|| "computation of commitment randomness"),
FixedGenerators::NoteCommitmentRandomness, FixedGenerators::NoteCommitmentRandomness,
&rcm, &rcm,
self.params self.params,
)?; )?;
// Randomize the note commitment. Pedersen hashes are not // Randomize the note commitment. Pedersen hashes are not
@@ -335,7 +291,7 @@ impl<'a, E: JubjubEngine> Circuit<E> for Spend<'a, E> {
cm = cm.add( cm = cm.add(
cs.namespace(|| "randomization of note commitment"), cs.namespace(|| "randomization of note commitment"),
&rcm, &rcm,
self.params self.params,
)?; )?;
} }
@@ -356,7 +312,7 @@ impl<'a, E: JubjubEngine> Circuit<E> for Spend<'a, E> {
// depth of the tree. // depth of the tree.
let cur_is_right = boolean::Boolean::from(boolean::AllocatedBit::alloc( let cur_is_right = boolean::Boolean::from(boolean::AllocatedBit::alloc(
cs.namespace(|| "position bit"), cs.namespace(|| "position bit"),
e.map(|e| e.1) e.map(|e| e.1),
)?); )?);
// Push this boolean for nullifier computation later // Push this boolean for nullifier computation later
@@ -364,19 +320,15 @@ impl<'a, E: JubjubEngine> Circuit<E> for Spend<'a, E> {
// Witness the authentication path element adjacent // Witness the authentication path element adjacent
// at this depth. // at this depth.
let path_element = num::AllocatedNum::alloc( let path_element =
cs.namespace(|| "path element"), num::AllocatedNum::alloc(cs.namespace(|| "path element"), || Ok(e.get()?.0))?;
|| {
Ok(e.get()?.0)
}
)?;
// Swap the two if the current subtree is on the right // Swap the two if the current subtree is on the right
let (xl, xr) = num::AllocatedNum::conditionally_reverse( let (xl, xr) = num::AllocatedNum::conditionally_reverse(
cs.namespace(|| "conditional reversal of preimage"), cs.namespace(|| "conditional reversal of preimage"),
&cur, &cur,
&path_element, &path_element,
&cur_is_right &cur_is_right,
)?; )?;
// We don't need to be strict, because the function is // We don't need to be strict, because the function is
@@ -392,20 +344,19 @@ impl<'a, E: JubjubEngine> Circuit<E> for Spend<'a, E> {
cs.namespace(|| "computation of pedersen hash"), cs.namespace(|| "computation of pedersen hash"),
pedersen_hash::Personalization::MerkleTree(i), pedersen_hash::Personalization::MerkleTree(i),
&preimage, &preimage,
self.params self.params,
)?.get_x().clone(); // Injective encoding )?
.get_x()
.clone(); // Injective encoding
} }
{ {
let real_anchor_value = self.anchor; let real_anchor_value = self.anchor;
// Allocate the "real" anchor that will be exposed. // Allocate the "real" anchor that will be exposed.
let rt = num::AllocatedNum::alloc( let rt = num::AllocatedNum::alloc(cs.namespace(|| "conditional anchor"), || {
cs.namespace(|| "conditional anchor"), Ok(*real_anchor_value.get()?)
|| { })?;
Ok(*real_anchor_value.get()?)
}
)?;
// (cur - rt) * value = 0 // (cur - rt) * value = 0
// if value is zero, cur and rt can be different // if value is zero, cur and rt can be different
@@ -414,7 +365,7 @@ impl<'a, E: JubjubEngine> Circuit<E> for Spend<'a, E> {
|| "conditionally enforce correct root", || "conditionally enforce correct root",
|lc| lc + cur.get_variable() - rt.get_variable(), |lc| lc + cur.get_variable() - rt.get_variable(),
|lc| lc + &value_num.lc(E::Fr::one()), |lc| lc + &value_num.lc(E::Fr::one()),
|lc| lc |lc| lc,
); );
// Expose the anchor // Expose the anchor
@@ -430,29 +381,27 @@ impl<'a, E: JubjubEngine> Circuit<E> for Spend<'a, E> {
cs.namespace(|| "g^position"), cs.namespace(|| "g^position"),
FixedGenerators::NullifierPosition, FixedGenerators::NullifierPosition,
&position_bits, &position_bits,
self.params self.params,
)?; )?;
// Add the position to the commitment // Add the position to the commitment
rho = rho.add( rho = rho.add(
cs.namespace(|| "faerie gold prevention"), cs.namespace(|| "faerie gold prevention"),
&position, &position,
self.params self.params,
)?; )?;
} }
// Let's compute nf = BLAKE2s(nk || rho) // Let's compute nf = BLAKE2s(nk || rho)
nf_preimage.extend( nf_preimage.extend(rho.repr(cs.namespace(|| "representation of rho"))?);
rho.repr(cs.namespace(|| "representation of rho"))?
);
assert_eq!(nf_preimage.len(), 512); assert_eq!(nf_preimage.len(), 512);
// Compute nf // Compute nf
let nf = blake2s::blake2s( let nf = blake2s::blake2s(
cs.namespace(|| "nf computation"), cs.namespace(|| "nf computation"),
&nf_preimage, &nf_preimage,
constants::PRF_NF_PERSONALIZATION constants::PRF_NF_PERSONALIZATION,
)?; )?;
multipack::pack_into_inputs(cs.namespace(|| "pack nullifier"), &nf) multipack::pack_into_inputs(cs.namespace(|| "pack nullifier"), &nf)
@@ -460,8 +409,7 @@ impl<'a, E: JubjubEngine> Circuit<E> for Spend<'a, E> {
} }
impl<'a, E: JubjubEngine> Circuit<E> for Output<'a, E> { impl<'a, E: JubjubEngine> Circuit<E> for Output<'a, E> {
fn synthesize<CS: ConstraintSystem<E>>(self, cs: &mut CS) -> Result<(), SynthesisError> fn synthesize<CS: ConstraintSystem<E>>(self, cs: &mut CS) -> Result<(), SynthesisError> {
{
// Let's start to construct our note, which contains // Let's start to construct our note, which contains
// value (big endian) // value (big endian)
let mut note_contents = vec![]; let mut note_contents = vec![];
@@ -471,7 +419,7 @@ impl<'a, E: JubjubEngine> Circuit<E> for Output<'a, E> {
note_contents.extend(expose_value_commitment( note_contents.extend(expose_value_commitment(
cs.namespace(|| "value commitment"), cs.namespace(|| "value commitment"),
self.value_commitment, self.value_commitment,
self.params self.params,
)?); )?);
// Let's deal with g_d // Let's deal with g_d
@@ -483,7 +431,7 @@ impl<'a, E: JubjubEngine> Circuit<E> for Output<'a, E> {
let g_d = ecc::EdwardsPoint::witness( let g_d = ecc::EdwardsPoint::witness(
cs.namespace(|| "witness g_d"), cs.namespace(|| "witness g_d"),
self.payment_address.as_ref().and_then(|a| a.g_d(params)), self.payment_address.as_ref().and_then(|a| a.g_d(params)),
self.params self.params,
)?; )?;
// g_d is ensured to be large order. The relationship // g_d is ensured to be large order. The relationship
@@ -495,29 +443,17 @@ impl<'a, E: JubjubEngine> Circuit<E> for Output<'a, E> {
// //
// Further, if it were small order, epk would be // Further, if it were small order, epk would be
// small order too! // small order too!
g_d.assert_not_small_order( g_d.assert_not_small_order(cs.namespace(|| "g_d not small order"), self.params)?;
cs.namespace(|| "g_d not small order"),
self.params
)?;
// Extend our note contents with the representation of // Extend our note contents with the representation of
// g_d. // g_d.
note_contents.extend( note_contents.extend(g_d.repr(cs.namespace(|| "representation of g_d"))?);
g_d.repr(cs.namespace(|| "representation of g_d"))?
);
// Booleanize our ephemeral secret key // Booleanize our ephemeral secret key
let esk = boolean::field_into_boolean_vec_le( let esk = boolean::field_into_boolean_vec_le(cs.namespace(|| "esk"), self.esk)?;
cs.namespace(|| "esk"),
self.esk
)?;
// Create the ephemeral public key from g_d. // Create the ephemeral public key from g_d.
let epk = g_d.mul( let epk = g_d.mul(cs.namespace(|| "epk computation"), &esk, self.params)?;
cs.namespace(|| "epk computation"),
&esk,
self.params
)?;
// Expose epk publicly. // Expose epk publicly.
epk.inputize(cs.namespace(|| "epk"))?; epk.inputize(cs.namespace(|| "epk"))?;
@@ -534,13 +470,13 @@ impl<'a, E: JubjubEngine> Circuit<E> for Output<'a, E> {
// endian bits (to match the representation) // endian bits (to match the representation)
let y_contents = boolean::field_into_boolean_vec_le( let y_contents = boolean::field_into_boolean_vec_le(
cs.namespace(|| "pk_d bits of y"), cs.namespace(|| "pk_d bits of y"),
pk_d.map(|e| e.1) pk_d.map(|e| e.1),
)?; )?;
// Witness the sign bit // Witness the sign bit
let sign_bit = boolean::Boolean::from(boolean::AllocatedBit::alloc( let sign_bit = boolean::Boolean::from(boolean::AllocatedBit::alloc(
cs.namespace(|| "pk_d bit of x"), cs.namespace(|| "pk_d bit of x"),
pk_d.map(|e| e.0.into_repr().is_odd()) pk_d.map(|e| e.0.into_repr().is_odd()),
)?); )?);
// Extend the note with pk_d representation // Extend the note with pk_d representation
@@ -560,14 +496,14 @@ impl<'a, E: JubjubEngine> Circuit<E> for Output<'a, E> {
cs.namespace(|| "note content hash"), cs.namespace(|| "note content hash"),
pedersen_hash::Personalization::NoteCommitment, pedersen_hash::Personalization::NoteCommitment,
&note_contents, &note_contents,
self.params self.params,
)?; )?;
{ {
// Booleanize the randomness // Booleanize the randomness
let rcm = boolean::field_into_boolean_vec_le( let rcm = boolean::field_into_boolean_vec_le(
cs.namespace(|| "rcm"), cs.namespace(|| "rcm"),
self.commitment_randomness self.commitment_randomness,
)?; )?;
// Compute the note commitment randomness in the exponent // Compute the note commitment randomness in the exponent
@@ -575,14 +511,14 @@ impl<'a, E: JubjubEngine> Circuit<E> for Output<'a, E> {
cs.namespace(|| "computation of commitment randomness"), cs.namespace(|| "computation of commitment randomness"),
FixedGenerators::NoteCommitmentRandomness, FixedGenerators::NoteCommitmentRandomness,
&rcm, &rcm,
self.params self.params,
)?; )?;
// Randomize our note commitment // Randomize our note commitment
cm = cm.add( cm = cm.add(
cs.namespace(|| "randomization of note commitment"), cs.namespace(|| "randomization of note commitment"),
&rcm, &rcm,
self.params self.params,
)?; )?;
} }
@@ -604,7 +540,7 @@ fn test_input_circuit_with_bls12_381() {
use rand_core::{RngCore, SeedableRng}; use rand_core::{RngCore, SeedableRng};
use rand_xorshift::XorShiftRng; use rand_xorshift::XorShiftRng;
use zcash_primitives::{ use zcash_primitives::{
jubjub::{JubjubBls12, fs, edwards}, jubjub::{edwards, fs, JubjubBls12},
pedersen_hash, pedersen_hash,
primitives::{Diversifier, Note, ProofGenerationKey}, primitives::{Diversifier, Note, ProofGenerationKey},
}; };
@@ -628,7 +564,7 @@ fn test_input_circuit_with_bls12_381() {
let proof_generation_key = ProofGenerationKey { let proof_generation_key = ProofGenerationKey {
ak: ak.clone(), ak: ak.clone(),
nsk: nsk.clone() nsk: nsk.clone(),
}; };
let viewing_key = proof_generation_key.into_viewing_key(params); let viewing_key = proof_generation_key.into_viewing_key(params);
@@ -642,11 +578,7 @@ fn test_input_circuit_with_bls12_381() {
Diversifier(d) Diversifier(d)
}; };
if let Some(p) = viewing_key.into_payment_address( if let Some(p) = viewing_key.into_payment_address(diversifier, params) {
diversifier,
params
)
{
payment_address = p; payment_address = p;
break; break;
} }
@@ -664,15 +596,14 @@ fn test_input_circuit_with_bls12_381() {
value: value_commitment.value, value: value_commitment.value,
g_d: g_d.clone(), g_d: g_d.clone(),
pk_d: payment_address.pk_d.clone(), pk_d: payment_address.pk_d.clone(),
r: commitment_randomness.clone() r: commitment_randomness.clone(),
}; };
let mut position = 0u64; let mut position = 0u64;
let cm: Fr = note.cm(params); let cm: Fr = note.cm(params);
let mut cur = cm.clone(); let mut cur = cm.clone();
for (i, val) in auth_path.clone().into_iter().enumerate() for (i, val) in auth_path.clone().into_iter().enumerate() {
{
let (uncle, b) = val.unwrap(); let (uncle, b) = val.unwrap();
let mut lhs = cur; let mut lhs = cur;
@@ -691,10 +622,12 @@ fn test_input_circuit_with_bls12_381() {
cur = pedersen_hash::pedersen_hash::<Bls12, _>( cur = pedersen_hash::pedersen_hash::<Bls12, _>(
pedersen_hash::Personalization::MerkleTree(i), pedersen_hash::Personalization::MerkleTree(i),
lhs.into_iter() lhs.into_iter()
.take(Fr::NUM_BITS as usize) .take(Fr::NUM_BITS as usize)
.chain(rhs.into_iter().take(Fr::NUM_BITS as usize)), .chain(rhs.into_iter().take(Fr::NUM_BITS as usize)),
params params,
).into_xy().0; )
.into_xy()
.0;
if b { if b {
position |= 1 << i; position |= 1 << i;
@@ -716,14 +649,17 @@ fn test_input_circuit_with_bls12_381() {
commitment_randomness: Some(commitment_randomness), commitment_randomness: Some(commitment_randomness),
ar: Some(ar), ar: Some(ar),
auth_path: auth_path.clone(), auth_path: auth_path.clone(),
anchor: Some(cur) anchor: Some(cur),
}; };
instance.synthesize(&mut cs).unwrap(); instance.synthesize(&mut cs).unwrap();
assert!(cs.is_satisfied()); assert!(cs.is_satisfied());
assert_eq!(cs.num_constraints(), 98777); assert_eq!(cs.num_constraints(), 98777);
assert_eq!(cs.hash(), "d37c738e83df5d9b0bb6495ac96abf21bcb2697477e2c15c2c7916ff7a3b6a89"); assert_eq!(
cs.hash(),
"d37c738e83df5d9b0bb6495ac96abf21bcb2697477e2c15c2c7916ff7a3b6a89"
);
assert_eq!(cs.get("randomization of note commitment/x3/num"), cm); assert_eq!(cs.get("randomization of note commitment/x3/num"), cm);
@@ -731,8 +667,14 @@ fn test_input_circuit_with_bls12_381() {
assert_eq!(cs.get_input(0, "ONE"), Fr::one()); assert_eq!(cs.get_input(0, "ONE"), Fr::one());
assert_eq!(cs.get_input(1, "rk/x/input variable"), rk.0); assert_eq!(cs.get_input(1, "rk/x/input variable"), rk.0);
assert_eq!(cs.get_input(2, "rk/y/input variable"), rk.1); assert_eq!(cs.get_input(2, "rk/y/input variable"), rk.1);
assert_eq!(cs.get_input(3, "value commitment/commitment point/x/input variable"), expected_value_cm.0); assert_eq!(
assert_eq!(cs.get_input(4, "value commitment/commitment point/y/input variable"), expected_value_cm.1); 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
);
assert_eq!(cs.get_input(5, "anchor/input variable"), cur); assert_eq!(cs.get_input(5, "anchor/input variable"), cur);
assert_eq!(cs.get_input(6, "pack nullifier/input 0"), expected_nf[0]); 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]); assert_eq!(cs.get_input(7, "pack nullifier/input 1"), expected_nf[1]);
@@ -748,7 +690,7 @@ fn test_output_circuit_with_bls12_381() {
use rand_core::{RngCore, SeedableRng}; use rand_core::{RngCore, SeedableRng};
use rand_xorshift::XorShiftRng; use rand_xorshift::XorShiftRng;
use zcash_primitives::{ use zcash_primitives::{
jubjub::{JubjubBls12, fs, edwards}, jubjub::{edwards, fs, JubjubBls12},
primitives::{Diversifier, ProofGenerationKey}, primitives::{Diversifier, ProofGenerationKey},
}; };
@@ -769,7 +711,7 @@ fn test_output_circuit_with_bls12_381() {
let proof_generation_key = ProofGenerationKey { let proof_generation_key = ProofGenerationKey {
ak: ak.clone(), ak: ak.clone(),
nsk: nsk.clone() nsk: nsk.clone(),
}; };
let viewing_key = proof_generation_key.into_viewing_key(params); let viewing_key = proof_generation_key.into_viewing_key(params);
@@ -783,11 +725,7 @@ fn test_output_circuit_with_bls12_381() {
Diversifier(d) Diversifier(d)
}; };
if let Some(p) = viewing_key.into_payment_address( if let Some(p) = viewing_key.into_payment_address(diversifier, params) {
diversifier,
params
)
{
payment_address = p; payment_address = p;
break; break;
} }
@@ -804,30 +742,41 @@ fn test_output_circuit_with_bls12_381() {
value_commitment: Some(value_commitment.clone()), value_commitment: Some(value_commitment.clone()),
payment_address: Some(payment_address.clone()), payment_address: Some(payment_address.clone()),
commitment_randomness: Some(commitment_randomness), commitment_randomness: Some(commitment_randomness),
esk: Some(esk.clone()) esk: Some(esk.clone()),
}; };
instance.synthesize(&mut cs).unwrap(); instance.synthesize(&mut cs).unwrap();
assert!(cs.is_satisfied()); assert!(cs.is_satisfied());
assert_eq!(cs.num_constraints(), 7827); assert_eq!(cs.num_constraints(), 7827);
assert_eq!(cs.hash(), "c26d5cdfe6ccd65c03390902c02e11393ea6bb96aae32a7f2ecb12eb9103faee"); assert_eq!(
cs.hash(),
"c26d5cdfe6ccd65c03390902c02e11393ea6bb96aae32a7f2ecb12eb9103faee"
);
let expected_cm = payment_address.create_note( let expected_cm = payment_address
value_commitment.value, .create_note(value_commitment.value, commitment_randomness, params)
commitment_randomness, .expect("should be valid")
params .cm(params);
).expect("should be valid").cm(params);
let expected_value_cm = value_commitment.cm(params).into_xy(); 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 = payment_address
.g_d(params)
.expect("should be valid")
.mul(esk, params);
let expected_epk_xy = expected_epk.into_xy(); let expected_epk_xy = expected_epk.into_xy();
assert_eq!(cs.num_inputs(), 6); assert_eq!(cs.num_inputs(), 6);
assert_eq!(cs.get_input(0, "ONE"), Fr::one()); 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!(
assert_eq!(cs.get_input(2, "value commitment/commitment point/y/input variable"), expected_value_cm.1); 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(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(4, "epk/y/input variable"), expected_epk_xy.1);
assert_eq!(cs.get_input(5, "commitment/input variable"), expected_cm); assert_eq!(cs.get_input(5, "commitment/input variable"), expected_cm);

View File

@@ -1,20 +1,18 @@
use pairing::{Engine}; use bellman::gadgets::boolean::Boolean;
use bellman::gadgets::sha256::sha256;
use bellman::{ConstraintSystem, SynthesisError}; use bellman::{ConstraintSystem, SynthesisError};
use bellman::gadgets::sha256::{ use pairing::Engine;
sha256
};
use bellman::gadgets::boolean::{
Boolean
};
pub fn note_comm<E, CS>( pub fn note_comm<E, CS>(
cs: CS, cs: CS,
a_pk: &[Boolean], a_pk: &[Boolean],
value: &[Boolean], value: &[Boolean],
rho: &[Boolean], rho: &[Boolean],
r: &[Boolean] r: &[Boolean],
) -> Result<Vec<Boolean>, SynthesisError> ) -> Result<Vec<Boolean>, SynthesisError>
where E: Engine, CS: ConstraintSystem<E> where
E: Engine,
CS: ConstraintSystem<E>,
{ {
assert_eq!(a_pk.len(), 256); assert_eq!(a_pk.len(), 256);
assert_eq!(value.len(), 64); assert_eq!(value.len(), 64);
@@ -35,8 +33,5 @@ pub fn note_comm<E, CS>(
image.extend(rho.iter().cloned()); image.extend(rho.iter().cloned());
image.extend(r.iter().cloned()); image.extend(r.iter().cloned());
sha256( sha256(cs, &image)
cs,
&image
)
} }

View File

@@ -1,16 +1,11 @@
use pairing::{Engine}; use bellman::gadgets::boolean::{AllocatedBit, Boolean};
use bellman::gadgets::sha256::sha256_block_no_padding;
use bellman::{ConstraintSystem, SynthesisError}; use bellman::{ConstraintSystem, SynthesisError};
use bellman::gadgets::sha256::{ use pairing::Engine;
sha256_block_no_padding
};
use bellman::gadgets::boolean::{
AllocatedBit,
Boolean
};
use super::*;
use super::prfs::*;
use super::commitment::note_comm; use super::commitment::note_comm;
use super::prfs::*;
use super::*;
pub struct InputNote { pub struct InputNote {
pub nf: Vec<Boolean>, pub nf: Vec<Boolean>,
@@ -27,49 +22,33 @@ impl InputNote {
h_sig: &[Boolean], h_sig: &[Boolean],
nonce: bool, nonce: bool,
auth_path: [Option<([u8; 32], bool)>; TREE_DEPTH], auth_path: [Option<([u8; 32], bool)>; TREE_DEPTH],
rt: &[Boolean] rt: &[Boolean],
) -> Result<InputNote, SynthesisError> ) -> Result<InputNote, SynthesisError>
where E: Engine, CS: ConstraintSystem<E> where
E: Engine,
CS: ConstraintSystem<E>,
{ {
let a_sk = witness_u252( let a_sk = witness_u252(
cs.namespace(|| "a_sk"), cs.namespace(|| "a_sk"),
a_sk.as_ref().map(|a_sk| &a_sk.0[..]) a_sk.as_ref().map(|a_sk| &a_sk.0[..]),
)?; )?;
let rho = witness_u256( let rho = witness_u256(cs.namespace(|| "rho"), rho.as_ref().map(|rho| &rho.0[..]))?;
cs.namespace(|| "rho"),
rho.as_ref().map(|rho| &rho.0[..])
)?;
let r = witness_u256( let r = witness_u256(cs.namespace(|| "r"), r.as_ref().map(|r| &r.0[..]))?;
cs.namespace(|| "r"),
r.as_ref().map(|r| &r.0[..])
)?;
let a_pk = prf_a_pk( let a_pk = prf_a_pk(cs.namespace(|| "a_pk computation"), &a_sk)?;
cs.namespace(|| "a_pk computation"),
&a_sk
)?;
let nf = prf_nf( let nf = prf_nf(cs.namespace(|| "nf computation"), &a_sk, &rho)?;
cs.namespace(|| "nf computation"),
&a_sk,
&rho
)?;
let mac = prf_pk( let mac = prf_pk(cs.namespace(|| "mac computation"), &a_sk, h_sig, nonce)?;
cs.namespace(|| "mac computation"),
&a_sk,
h_sig,
nonce
)?;
let cm = note_comm( let cm = note_comm(
cs.namespace(|| "cm computation"), cs.namespace(|| "cm computation"),
&a_pk, &a_pk,
&value.bits_le(), &value.bits_le(),
&rho, &rho,
&r &r,
)?; )?;
// Witness into the merkle tree // Witness into the merkle tree
@@ -80,13 +59,13 @@ impl InputNote {
let cur_is_right = AllocatedBit::alloc( let cur_is_right = AllocatedBit::alloc(
cs.namespace(|| "cur is right"), cs.namespace(|| "cur is right"),
layer.as_ref().map(|&(_, p)| p) layer.as_ref().map(|&(_, p)| p),
)?; )?;
let lhs = cur; let lhs = cur;
let rhs = witness_u256( let rhs = witness_u256(
cs.namespace(|| "sibling"), cs.namespace(|| "sibling"),
layer.as_ref().map(|&(ref sibling, _)| &sibling[..]) layer.as_ref().map(|&(ref sibling, _)| &sibling[..]),
)?; )?;
// Conditionally swap if cur is right // Conditionally swap if cur is right
@@ -94,19 +73,16 @@ impl InputNote {
cs.namespace(|| "conditional swap"), cs.namespace(|| "conditional swap"),
&lhs[..], &lhs[..],
&rhs[..], &rhs[..],
&cur_is_right &cur_is_right,
)?; )?;
cur = sha256_block_no_padding( cur = sha256_block_no_padding(cs.namespace(|| "hash of this layer"), &preimage)?;
cs.namespace(|| "hash of this layer"),
&preimage
)?;
} }
// enforce must be true if the value is nonzero // enforce must be true if the value is nonzero
let enforce = AllocatedBit::alloc( let enforce = AllocatedBit::alloc(
cs.namespace(|| "enforce"), cs.namespace(|| "enforce"),
value.get_value().map(|n| n != 0) value.get_value().map(|n| n != 0),
)?; )?;
// value * (1 - enforce) = 0 // value * (1 - enforce) = 0
@@ -116,7 +92,7 @@ impl InputNote {
|| "enforce validity", || "enforce validity",
|_| value.lc(), |_| value.lc(),
|lc| lc + CS::one() - enforce.get_variable(), |lc| lc + CS::one() - enforce.get_variable(),
|lc| lc |lc| lc,
); );
assert_eq!(cur.len(), rt.len()); assert_eq!(cur.len(), rt.len());
@@ -132,14 +108,11 @@ impl InputNote {
|| format!("conditionally enforce correct root for bit {}", i), || format!("conditionally enforce correct root for bit {}", i),
|_| cur.lc(CS::one(), E::Fr::one()) - &rt.lc(CS::one(), E::Fr::one()), |_| cur.lc(CS::one(), E::Fr::one()) - &rt.lc(CS::one(), E::Fr::one()),
|lc| lc + enforce.get_variable(), |lc| lc + enforce.get_variable(),
|lc| lc |lc| lc,
); );
} }
Ok(InputNote { Ok(InputNote { mac: mac, nf: nf })
mac: mac,
nf: nf
})
} }
} }
@@ -149,9 +122,11 @@ pub fn conditionally_swap_u256<E, CS>(
mut cs: CS, mut cs: CS,
lhs: &[Boolean], lhs: &[Boolean],
rhs: &[Boolean], rhs: &[Boolean],
condition: &AllocatedBit condition: &AllocatedBit,
) -> Result<Vec<Boolean>, SynthesisError> ) -> Result<Vec<Boolean>, SynthesisError>
where E: Engine, CS: ConstraintSystem<E>, where
E: Engine,
CS: ConstraintSystem<E>,
{ {
assert_eq!(lhs.len(), 256); assert_eq!(lhs.len(), 256);
assert_eq!(rhs.len(), 256); assert_eq!(rhs.len(), 256);
@@ -164,13 +139,9 @@ pub fn conditionally_swap_u256<E, CS>(
let x = Boolean::from(AllocatedBit::alloc( let x = Boolean::from(AllocatedBit::alloc(
cs.namespace(|| "x"), cs.namespace(|| "x"),
condition.get_value().and_then(|v| { condition
if v { .get_value()
rhs.get_value() .and_then(|v| if v { rhs.get_value() } else { lhs.get_value() }),
} else {
lhs.get_value()
}
})
)?); )?);
// x = (1-condition)lhs + (condition)rhs // x = (1-condition)lhs + (condition)rhs
@@ -184,33 +155,25 @@ pub fn conditionally_swap_u256<E, CS>(
// x = rhs // x = rhs
cs.enforce( cs.enforce(
|| "conditional swap for x", || "conditional swap for x",
|lc| lc + &rhs.lc(CS::one(), E::Fr::one()) |lc| lc + &rhs.lc(CS::one(), E::Fr::one()) - &lhs.lc(CS::one(), E::Fr::one()),
- &lhs.lc(CS::one(), E::Fr::one()),
|lc| lc + condition.get_variable(), |lc| lc + condition.get_variable(),
|lc| lc + &x.lc(CS::one(), E::Fr::one()) |lc| lc + &x.lc(CS::one(), E::Fr::one()) - &lhs.lc(CS::one(), E::Fr::one()),
- &lhs.lc(CS::one(), E::Fr::one())
); );
let y = Boolean::from(AllocatedBit::alloc( let y = Boolean::from(AllocatedBit::alloc(
cs.namespace(|| "y"), cs.namespace(|| "y"),
condition.get_value().and_then(|v| { condition
if v { .get_value()
lhs.get_value() .and_then(|v| if v { lhs.get_value() } else { rhs.get_value() }),
} else {
rhs.get_value()
}
})
)?); )?);
// y = (1-condition)rhs + (condition)lhs // y = (1-condition)rhs + (condition)lhs
// y - rhs = condition (lhs - rhs) // y - rhs = condition (lhs - rhs)
cs.enforce( cs.enforce(
|| "conditional swap for y", || "conditional swap for y",
|lc| lc + &lhs.lc(CS::one(), E::Fr::one()) |lc| lc + &lhs.lc(CS::one(), E::Fr::one()) - &rhs.lc(CS::one(), E::Fr::one()),
- &rhs.lc(CS::one(), E::Fr::one()),
|lc| lc + condition.get_variable(), |lc| lc + condition.get_variable(),
|lc| lc + &y.lc(CS::one(), E::Fr::one()) |lc| lc + &y.lc(CS::one(), E::Fr::one()) - &rhs.lc(CS::one(), E::Fr::one()),
- &rhs.lc(CS::one(), E::Fr::one())
); );
new_lhs.push(x); new_lhs.push(x);

View File

@@ -1,16 +1,13 @@
use bellman::gadgets::boolean::{AllocatedBit, Boolean};
use bellman::gadgets::multipack::pack_into_inputs;
use bellman::{Circuit, ConstraintSystem, LinearCombination, SynthesisError};
use ff::Field; use ff::Field;
use pairing::Engine; use pairing::Engine;
use bellman::{ConstraintSystem, SynthesisError, Circuit, LinearCombination};
use bellman::gadgets::boolean::{
AllocatedBit,
Boolean
};
use bellman::gadgets::multipack::pack_into_inputs;
mod prfs;
mod commitment; mod commitment;
mod input; mod input;
mod output; mod output;
mod prfs;
use self::input::*; use self::input::*;
use self::output::*; use self::output::*;
@@ -37,39 +34,29 @@ pub struct JSInput {
pub a_sk: Option<SpendingKey>, pub a_sk: Option<SpendingKey>,
pub rho: Option<UniqueRandomness>, pub rho: Option<UniqueRandomness>,
pub r: Option<CommitmentRandomness>, pub r: Option<CommitmentRandomness>,
pub auth_path: [Option<([u8; 32], bool)>; TREE_DEPTH] pub auth_path: [Option<([u8; 32], bool)>; TREE_DEPTH],
} }
pub struct JSOutput { pub struct JSOutput {
pub value: Option<u64>, pub value: Option<u64>,
pub a_pk: Option<PayingKey>, pub a_pk: Option<PayingKey>,
pub r: Option<CommitmentRandomness> pub r: Option<CommitmentRandomness>,
} }
impl<E: Engine> Circuit<E> for JoinSplit { impl<E: Engine> Circuit<E> for JoinSplit {
fn synthesize<CS: ConstraintSystem<E>>( fn synthesize<CS: ConstraintSystem<E>>(self, cs: &mut CS) -> Result<(), SynthesisError> {
self,
cs: &mut CS
) -> Result<(), SynthesisError>
{
assert_eq!(self.inputs.len(), 2); assert_eq!(self.inputs.len(), 2);
assert_eq!(self.outputs.len(), 2); assert_eq!(self.outputs.len(), 2);
// vpub_old is the value entering the // vpub_old is the value entering the
// JoinSplit from the "outside" value // JoinSplit from the "outside" value
// pool // pool
let vpub_old = NoteValue::new( let vpub_old = NoteValue::new(cs.namespace(|| "vpub_old"), self.vpub_old)?;
cs.namespace(|| "vpub_old"),
self.vpub_old
)?;
// vpub_new is the value leaving the // vpub_new is the value leaving the
// JoinSplit into the "outside" value // JoinSplit into the "outside" value
// pool // pool
let vpub_new = NoteValue::new( let vpub_new = NoteValue::new(cs.namespace(|| "vpub_new"), self.vpub_new)?;
cs.namespace(|| "vpub_new"),
self.vpub_new
)?;
// The left hand side of the balance equation // The left hand side of the balance equation
// vpub_old + inputs[0].value + inputs[1].value // vpub_old + inputs[0].value + inputs[1].value
@@ -80,22 +67,17 @@ impl<E: Engine> Circuit<E> for JoinSplit {
let mut rhs = vpub_new.lc(); let mut rhs = vpub_new.lc();
// Witness rt (merkle tree root) // Witness rt (merkle tree root)
let rt = witness_u256( let rt = witness_u256(cs.namespace(|| "rt"), self.rt.as_ref().map(|v| &v[..])).unwrap();
cs.namespace(|| "rt"),
self.rt.as_ref().map(|v| &v[..])
).unwrap();
// Witness h_sig // Witness h_sig
let h_sig = witness_u256( let h_sig = witness_u256(
cs.namespace(|| "h_sig"), cs.namespace(|| "h_sig"),
self.h_sig.as_ref().map(|v| &v[..]) self.h_sig.as_ref().map(|v| &v[..]),
).unwrap(); )
.unwrap();
// Witness phi // Witness phi
let phi = witness_u252( let phi = witness_u252(cs.namespace(|| "phi"), self.phi.as_ref().map(|v| &v[..])).unwrap();
cs.namespace(|| "phi"),
self.phi.as_ref().map(|v| &v[..])
).unwrap();
let mut input_notes = vec![]; let mut input_notes = vec![];
let mut lhs_total = self.vpub_old; let mut lhs_total = self.vpub_old;
@@ -110,17 +92,14 @@ impl<E: Engine> Circuit<E> for JoinSplit {
} }
// Allocate the value of the note // Allocate the value of the note
let value = NoteValue::new( let value = NoteValue::new(cs.namespace(|| "value"), input.value)?;
cs.namespace(|| "value"),
input.value
)?;
// Compute the nonce (for PRF inputs) which is false // Compute the nonce (for PRF inputs) which is false
// for the first input, and true for the second input. // for the first input, and true for the second input.
let nonce = match i { let nonce = match i {
0 => false, 0 => false,
1 => true, 1 => true,
_ => unreachable!() _ => unreachable!(),
}; };
// Perform input note computations // Perform input note computations
@@ -133,7 +112,7 @@ impl<E: Engine> Circuit<E> for JoinSplit {
&h_sig, &h_sig,
nonce, nonce,
input.auth_path, input.auth_path,
&rt &rt,
)?); )?);
// Add the note value to the left hand side of // Add the note value to the left hand side of
@@ -148,10 +127,8 @@ impl<E: Engine> Circuit<E> for JoinSplit {
{ {
// Expected sum of the left hand side of the balance // Expected sum of the left hand side of the balance
// equation, expressed as a 64-bit unsigned integer // equation, expressed as a 64-bit unsigned integer
let lhs_total = NoteValue::new( let lhs_total =
cs.namespace(|| "total value of left hand side"), NoteValue::new(cs.namespace(|| "total value of left hand side"), lhs_total)?;
lhs_total
)?;
// Enforce that the left hand side can be expressed as a 64-bit // Enforce that the left hand side can be expressed as a 64-bit
// integer // integer
@@ -159,7 +136,7 @@ impl<E: Engine> Circuit<E> for JoinSplit {
|| "left hand side can be expressed as a 64-bit unsigned integer", || "left hand side can be expressed as a 64-bit unsigned integer",
|_| lhs.clone(), |_| lhs.clone(),
|lc| lc + CS::one(), |lc| lc + CS::one(),
|_| lhs_total.lc() |_| lhs_total.lc(),
); );
} }
@@ -169,17 +146,14 @@ impl<E: Engine> Circuit<E> for JoinSplit {
for (i, output) in self.outputs.into_iter().enumerate() { for (i, output) in self.outputs.into_iter().enumerate() {
let cs = &mut cs.namespace(|| format!("output {}", i)); let cs = &mut cs.namespace(|| format!("output {}", i));
let value = NoteValue::new( let value = NoteValue::new(cs.namespace(|| "value"), output.value)?;
cs.namespace(|| "value"),
output.value
)?;
// Compute the nonce (for PRF inputs) which is false // Compute the nonce (for PRF inputs) which is false
// for the first output, and true for the second output. // for the first output, and true for the second output.
let nonce = match i { let nonce = match i {
0 => false, 0 => false,
1 => true, 1 => true,
_ => unreachable!() _ => unreachable!(),
}; };
// Perform output note computations // Perform output note computations
@@ -190,7 +164,7 @@ impl<E: Engine> Circuit<E> for JoinSplit {
output.r, output.r,
&phi, &phi,
&h_sig, &h_sig,
nonce nonce,
)?); )?);
// Add the note value to the right hand side of // Add the note value to the right hand side of
@@ -203,7 +177,7 @@ impl<E: Engine> Circuit<E> for JoinSplit {
|| "balance equation", || "balance equation",
|_| lhs.clone(), |_| lhs.clone(),
|lc| lc + CS::one(), |lc| lc + CS::one(),
|_| rhs |_| rhs,
); );
let mut public_inputs = vec![]; let mut public_inputs = vec![];
@@ -229,15 +203,14 @@ impl<E: Engine> Circuit<E> for JoinSplit {
pub struct NoteValue { pub struct NoteValue {
value: Option<u64>, value: Option<u64>,
// Least significant digit first // Least significant digit first
bits: Vec<AllocatedBit> bits: Vec<AllocatedBit>,
} }
impl NoteValue { impl NoteValue {
fn new<E, CS>( fn new<E, CS>(mut cs: CS, value: Option<u64>) -> Result<NoteValue, SynthesisError>
mut cs: CS, where
value: Option<u64> E: Engine,
) -> Result<NoteValue, SynthesisError> CS: ConstraintSystem<E>,
where E: Engine, CS: ConstraintSystem<E>,
{ {
let mut values; let mut values;
match value { match value {
@@ -247,7 +220,7 @@ impl NoteValue {
values.push(Some(val & 1 == 1)); values.push(Some(val & 1 == 1));
val >>= 1; val >>= 1;
} }
}, }
None => { None => {
values = vec![None; 64]; values = vec![None; 64];
} }
@@ -255,28 +228,27 @@ impl NoteValue {
let mut bits = vec![]; let mut bits = vec![];
for (i, value) in values.into_iter().enumerate() { for (i, value) in values.into_iter().enumerate() {
bits.push( bits.push(AllocatedBit::alloc(
AllocatedBit::alloc( cs.namespace(|| format!("bit {}", i)),
cs.namespace(|| format!("bit {}", i)), value,
value )?);
)?
);
} }
Ok(NoteValue { Ok(NoteValue {
value: value, value: value,
bits: bits bits: bits,
}) })
} }
/// Encodes the bits of the value into little-endian /// Encodes the bits of the value into little-endian
/// byte order. /// byte order.
fn bits_le(&self) -> Vec<Boolean> { fn bits_le(&self) -> Vec<Boolean> {
self.bits.chunks(8) self.bits
.flat_map(|v| v.iter().rev()) .chunks(8)
.cloned() .flat_map(|v| v.iter().rev())
.map(|e| Boolean::from(e)) .cloned()
.collect() .map(|e| Boolean::from(e))
.collect()
} }
/// Computes this value as a linear combination of /// Computes this value as a linear combination of
@@ -304,15 +276,18 @@ fn witness_bits<E, CS>(
mut cs: CS, mut cs: CS,
value: Option<&[u8]>, value: Option<&[u8]>,
num_bits: usize, num_bits: usize,
skip_bits: usize skip_bits: usize,
) -> Result<Vec<Boolean>, SynthesisError> ) -> Result<Vec<Boolean>, SynthesisError>
where E: Engine, CS: ConstraintSystem<E>, where
E: Engine,
CS: ConstraintSystem<E>,
{ {
let bit_values = if let Some(value) = value { let bit_values = if let Some(value) = value {
let mut tmp = vec![]; let mut tmp = vec![];
for b in value.iter() for b in value
.flat_map(|&m| (0..8).rev().map(move |i| m >> i & 1 == 1)) .iter()
.skip(skip_bits) .flat_map(|&m| (0..8).rev().map(move |i| m >> i & 1 == 1))
.skip(skip_bits)
{ {
tmp.push(Some(b)); tmp.push(Some(b));
} }
@@ -327,37 +302,35 @@ fn witness_bits<E, CS>(
for (i, value) in bit_values.into_iter().enumerate() { for (i, value) in bit_values.into_iter().enumerate() {
bits.push(Boolean::from(AllocatedBit::alloc( bits.push(Boolean::from(AllocatedBit::alloc(
cs.namespace(|| format!("bit {}", i)), cs.namespace(|| format!("bit {}", i)),
value value,
)?)); )?));
} }
Ok(bits) Ok(bits)
} }
fn witness_u256<E, CS>( fn witness_u256<E, CS>(cs: CS, value: Option<&[u8]>) -> Result<Vec<Boolean>, SynthesisError>
cs: CS, where
value: Option<&[u8]>, E: Engine,
) -> Result<Vec<Boolean>, SynthesisError> CS: ConstraintSystem<E>,
where E: Engine, CS: ConstraintSystem<E>,
{ {
witness_bits(cs, value, 256, 0) witness_bits(cs, value, 256, 0)
} }
fn witness_u252<E, CS>( fn witness_u252<E, CS>(cs: CS, value: Option<&[u8]>) -> Result<Vec<Boolean>, SynthesisError>
cs: CS, where
value: Option<&[u8]>, E: Engine,
) -> Result<Vec<Boolean>, SynthesisError> CS: ConstraintSystem<E>,
where E: Engine, CS: ConstraintSystem<E>,
{ {
witness_bits(cs, value, 252, 4) witness_bits(cs, value, 252, 4)
} }
#[test] #[test]
fn test_sprout_constraints() { fn test_sprout_constraints() {
use pairing::bls12_381::{Bls12};
use bellman::gadgets::test::*; use bellman::gadgets::test::*;
use pairing::bls12_381::Bls12;
use byteorder::{WriteBytesExt, ReadBytesExt, LittleEndian}; use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
let test_vector = include_bytes!("test_vectors.dat"); let test_vector = include_bytes!("test_vectors.dat");
let mut test_vector = &test_vector[..]; let mut test_vector = &test_vector[..];
@@ -393,9 +366,7 @@ fn test_sprout_constraints() {
} }
let mut position = test_vector.read_u64::<LittleEndian>().unwrap(); let mut position = test_vector.read_u64::<LittleEndian>().unwrap();
for i in 0..TREE_DEPTH { for i in 0..TREE_DEPTH {
auth_path[i].as_mut().map(|p| { auth_path[i].as_mut().map(|p| p.1 = (position & 1) == 1);
p.1 = (position & 1) == 1
});
position >>= 1; position >>= 1;
} }
@@ -407,15 +378,13 @@ fn test_sprout_constraints() {
let r = Some(CommitmentRandomness(get_u256(&mut test_vector))); let r = Some(CommitmentRandomness(get_u256(&mut test_vector)));
let a_sk = Some(SpendingKey(get_u256(&mut test_vector))); let a_sk = Some(SpendingKey(get_u256(&mut test_vector)));
inputs.push( inputs.push(JSInput {
JSInput { value: value,
value: value, a_sk: a_sk,
a_sk: a_sk, rho: rho,
rho: rho, r: r,
r: r, auth_path: auth_path,
auth_path: auth_path });
}
);
} }
let mut outputs = vec![]; let mut outputs = vec![];
@@ -426,13 +395,11 @@ fn test_sprout_constraints() {
get_u256(&mut test_vector); get_u256(&mut test_vector);
let r = Some(CommitmentRandomness(get_u256(&mut test_vector))); let r = Some(CommitmentRandomness(get_u256(&mut test_vector)));
outputs.push( outputs.push(JSOutput {
JSOutput { value: value,
value: value, a_pk: a_pk,
a_pk: a_pk, r: r,
r: r });
}
);
} }
let vpub_old = Some(test_vector.read_u64::<LittleEndian>().unwrap()); let vpub_old = Some(test_vector.read_u64::<LittleEndian>().unwrap());
@@ -454,7 +421,7 @@ fn test_sprout_constraints() {
phi: phi, phi: phi,
inputs: inputs, inputs: inputs,
outputs: outputs, outputs: outputs,
rt: rt rt: rt,
}; };
js.synthesize(&mut cs).unwrap(); js.synthesize(&mut cs).unwrap();
@@ -465,7 +432,10 @@ fn test_sprout_constraints() {
assert!(cs.is_satisfied()); assert!(cs.is_satisfied());
assert_eq!(cs.num_constraints(), 1989085); assert_eq!(cs.num_constraints(), 1989085);
assert_eq!(cs.num_inputs(), 10); assert_eq!(cs.num_inputs(), 10);
assert_eq!(cs.hash(), "1a228d3c6377130d1778c7885811dc8b8864049cb5af8aff7e6cd46c5bc4b84c"); assert_eq!(
cs.hash(),
"1a228d3c6377130d1778c7885811dc8b8864049cb5af8aff7e6cd46c5bc4b84c"
);
let mut expected_inputs = vec![]; let mut expected_inputs = vec![];
expected_inputs.extend(rt.unwrap().to_vec()); expected_inputs.extend(rt.unwrap().to_vec());
@@ -476,8 +446,12 @@ fn test_sprout_constraints() {
expected_inputs.extend(mac2.to_vec()); expected_inputs.extend(mac2.to_vec());
expected_inputs.extend(cm1.to_vec()); expected_inputs.extend(cm1.to_vec());
expected_inputs.extend(cm2.to_vec()); expected_inputs.extend(cm2.to_vec());
expected_inputs.write_u64::<LittleEndian>(vpub_old.unwrap()).unwrap(); expected_inputs
expected_inputs.write_u64::<LittleEndian>(vpub_new.unwrap()).unwrap(); .write_u64::<LittleEndian>(vpub_old.unwrap())
.unwrap();
expected_inputs
.write_u64::<LittleEndian>(vpub_new.unwrap())
.unwrap();
use bellman::gadgets::multipack; use bellman::gadgets::multipack;

View File

@@ -1,13 +1,13 @@
use pairing::{Engine}; use bellman::gadgets::boolean::Boolean;
use bellman::{ConstraintSystem, SynthesisError}; use bellman::{ConstraintSystem, SynthesisError};
use bellman::gadgets::boolean::{Boolean}; use pairing::Engine;
use super::*;
use super::prfs::*;
use super::commitment::note_comm; use super::commitment::note_comm;
use super::prfs::*;
use super::*;
pub struct OutputNote { pub struct OutputNote {
pub cm: Vec<Boolean> pub cm: Vec<Boolean>,
} }
impl OutputNote { impl OutputNote {
@@ -18,37 +18,29 @@ impl OutputNote {
r: Option<CommitmentRandomness>, r: Option<CommitmentRandomness>,
phi: &[Boolean], phi: &[Boolean],
h_sig: &[Boolean], h_sig: &[Boolean],
nonce: bool nonce: bool,
) -> Result<Self, SynthesisError> ) -> Result<Self, SynthesisError>
where E: Engine, CS: ConstraintSystem<E>, where
E: Engine,
CS: ConstraintSystem<E>,
{ {
let rho = prf_rho( let rho = prf_rho(cs.namespace(|| "rho"), phi, h_sig, nonce)?;
cs.namespace(|| "rho"),
phi,
h_sig,
nonce
)?;
let a_pk = witness_u256( let a_pk = witness_u256(
cs.namespace(|| "a_pk"), cs.namespace(|| "a_pk"),
a_pk.as_ref().map(|a_pk| &a_pk.0[..]) a_pk.as_ref().map(|a_pk| &a_pk.0[..]),
)?; )?;
let r = witness_u256( let r = witness_u256(cs.namespace(|| "r"), r.as_ref().map(|r| &r.0[..]))?;
cs.namespace(|| "r"),
r.as_ref().map(|r| &r.0[..])
)?;
let cm = note_comm( let cm = note_comm(
cs.namespace(|| "cm computation"), cs.namespace(|| "cm computation"),
&a_pk, &a_pk,
&value.bits_le(), &value.bits_le(),
&rho, &rho,
&r &r,
)?; )?;
Ok(OutputNote { Ok(OutputNote { cm: cm })
cm: cm
})
} }
} }

View File

@@ -1,11 +1,7 @@
use pairing::{Engine}; use bellman::gadgets::boolean::Boolean;
use bellman::gadgets::sha256::sha256_block_no_padding;
use bellman::{ConstraintSystem, SynthesisError}; use bellman::{ConstraintSystem, SynthesisError};
use bellman::gadgets::sha256::{ use pairing::Engine;
sha256_block_no_padding
};
use bellman::gadgets::boolean::{
Boolean
};
fn prf<E, CS>( fn prf<E, CS>(
cs: CS, cs: CS,
@@ -14,9 +10,11 @@ fn prf<E, CS>(
c: bool, c: bool,
d: bool, d: bool,
x: &[Boolean], x: &[Boolean],
y: &[Boolean] y: &[Boolean],
) -> Result<Vec<Boolean>, SynthesisError> ) -> Result<Vec<Boolean>, SynthesisError>
where E: Engine, CS: ConstraintSystem<E> where
E: Engine,
CS: ConstraintSystem<E>,
{ {
assert_eq!(x.len(), 252); assert_eq!(x.len(), 252);
assert_eq!(y.len(), 256); assert_eq!(y.len(), 256);
@@ -31,27 +29,35 @@ fn prf<E, CS>(
assert_eq!(image.len(), 512); assert_eq!(image.len(), 512);
sha256_block_no_padding( sha256_block_no_padding(cs, &image)
cs,
&image
)
} }
pub fn prf_a_pk<E, CS>( pub fn prf_a_pk<E, CS>(cs: CS, a_sk: &[Boolean]) -> Result<Vec<Boolean>, SynthesisError>
cs: CS, where
a_sk: &[Boolean] E: Engine,
) -> Result<Vec<Boolean>, SynthesisError> CS: ConstraintSystem<E>,
where E: Engine, CS: ConstraintSystem<E>
{ {
prf(cs, true, true, false, false, a_sk, &(0..256).map(|_| Boolean::constant(false)).collect::<Vec<_>>()) prf(
cs,
true,
true,
false,
false,
a_sk,
&(0..256)
.map(|_| Boolean::constant(false))
.collect::<Vec<_>>(),
)
} }
pub fn prf_nf<E, CS>( pub fn prf_nf<E, CS>(
cs: CS, cs: CS,
a_sk: &[Boolean], a_sk: &[Boolean],
rho: &[Boolean] rho: &[Boolean],
) -> Result<Vec<Boolean>, SynthesisError> ) -> Result<Vec<Boolean>, SynthesisError>
where E: Engine, CS: ConstraintSystem<E> where
E: Engine,
CS: ConstraintSystem<E>,
{ {
prf(cs, true, true, true, false, a_sk, rho) prf(cs, true, true, true, false, a_sk, rho)
} }
@@ -60,9 +66,11 @@ pub fn prf_pk<E, CS>(
cs: CS, cs: CS,
a_sk: &[Boolean], a_sk: &[Boolean],
h_sig: &[Boolean], h_sig: &[Boolean],
nonce: bool nonce: bool,
) -> Result<Vec<Boolean>, SynthesisError> ) -> Result<Vec<Boolean>, SynthesisError>
where E: Engine, CS: ConstraintSystem<E> where
E: Engine,
CS: ConstraintSystem<E>,
{ {
prf(cs, false, nonce, false, false, a_sk, h_sig) prf(cs, false, nonce, false, false, a_sk, h_sig)
} }
@@ -71,9 +79,11 @@ pub fn prf_rho<E, CS>(
cs: CS, cs: CS,
phi: &[Boolean], phi: &[Boolean],
h_sig: &[Boolean], h_sig: &[Boolean],
nonce: bool nonce: bool,
) -> Result<Vec<Boolean>, SynthesisError> ) -> Result<Vec<Boolean>, SynthesisError>
where E: Engine, CS: ConstraintSystem<E> where
E: Engine,
CS: ConstraintSystem<E>,
{ {
prf(cs, false, nonce, true, false, phi, h_sig) prf(cs, false, nonce, true, false, phi, h_sig)
} }

View File

@@ -3,11 +3,11 @@
use bellman::groth16::{Parameters, PreparedVerifyingKey}; use bellman::groth16::{Parameters, PreparedVerifyingKey};
use directories::BaseDirs; use directories::BaseDirs;
use pairing::bls12_381::{Bls12, Fr}; use pairing::bls12_381::{Bls12, Fr};
use std::path::Path;
use zcash_primitives::{ use zcash_primitives::{
jubjub::{edwards, fs::Fs, Unknown}, jubjub::{edwards, fs::Fs, Unknown},
primitives::{Diversifier, PaymentAddress, ProofGenerationKey}, primitives::{Diversifier, PaymentAddress, ProofGenerationKey},
}; };
use std::path::Path;
use zcash_primitives::{ use zcash_primitives::{
merkle_tree::CommitmentTreeWitness, merkle_tree::CommitmentTreeWitness,
prover::TxProver, prover::TxProver,

View File

@@ -1,8 +1,6 @@
use bellman::{ use bellman::{
gadgets::multipack, gadgets::multipack,
groth16::{ groth16::{create_random_proof, verify_proof, Parameters, PreparedVerifyingKey, Proof},
create_random_proof, verify_proof, Parameters, PreparedVerifyingKey, Proof,
},
}; };
use ff::Field; use ff::Field;
use pairing::bls12_381::{Bls12, Fr}; use pairing::bls12_381::{Bls12, Fr};