Skip to content

Commit

Permalink
Merge pull request zcash#60 from ebfull/optional-enforce
Browse files Browse the repository at this point in the history
Allow the authentication path to be not enforced
  • Loading branch information
ebfull authored Mar 27, 2018
2 parents 7ee2950 + def5de3 commit 470c957
Showing 1 changed file with 87 additions and 43 deletions.
130 changes: 87 additions & 43 deletions src/circuit/sapling/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use pairing::{
PrimeField,
PrimeFieldRepr,
Field,
};

use bellman::{
Expand Down Expand Up @@ -51,7 +52,11 @@ pub struct Spend<'a, E: JubjubEngine> {
pub ar: Option<E::Fs>,

/// The authentication path of the commitment in the tree
pub auth_path: Vec<Option<(E::Fr, bool)>>
pub auth_path: Vec<Option<(E::Fr, bool)>>,

/// The anchor; the root of the tree. If the note being
/// spent is zero-value, this can be anything.
pub anchor: Option<E::Fr>
}

/// This is an output circuit instance.
Expand Down Expand Up @@ -265,13 +270,32 @@ impl<'a, E: JubjubEngine> Circuit<E> for Spend<'a, E> {
// 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
)?);
// Handle the value; we'll need it later for the
// dummy input check.
let mut value_num = num::Num::zero();
{
// Get the value in little-endian bit order
let value_bits = expose_value_commitment(
cs.namespace(|| "value commitment"),
self.value_commitment,
self.params
)?;

// Compute the note's value as a linear combination
// of the bits.
let mut coeff = E::Fr::one();
for bit in &value_bits {
value_num = value_num.add_bool_with_coeff(
CS::one(),
bit,
coeff
);
coeff.double();
}

// Place the value in the note
note_contents.extend(value_bits);
}

// Place g_d in the note
note_contents.extend(
Expand Down Expand Up @@ -379,8 +403,30 @@ impl<'a, E: JubjubEngine> Circuit<E> for Spend<'a, E> {
)?.get_x().clone(); // Injective encoding
}

// Expose the anchor
cur.inputize(cs.namespace(|| "anchor"))?;
{
let real_anchor_value = self.anchor;

// Allocate the "real" anchor that will be exposed.
let rt = num::AllocatedNum::alloc(
cs.namespace(|| "conditional anchor"),
|| {
Ok(*real_anchor_value.get()?)
}
)?;

// (cur - rt) * value = 0
// if value is zero, cur and rt can be different
// if value is nonzero, they must be equal
cs.enforce(
|| "conditionally enforce correct root",
|lc| lc + cur.get_variable() - rt.get_variable(),
|lc| lc + &value_num.lc(E::Fr::one()),
|lc| lc
);

// Expose the anchor
rt.inputize(cs.namespace(|| "anchor"))?;
}

// Compute the cm + g^position for preventing
// faerie gold attacks
Expand Down Expand Up @@ -607,35 +653,8 @@ fn test_input_circuit_with_bls12_381() {
let ar: fs::Fs = rng.gen();

{
let mut cs = TestConstraintSystem::<Bls12>::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()
};

instance.synthesize(&mut cs).unwrap();

assert!(cs.is_satisfied());
assert_eq!(cs.num_constraints(), 98776);
assert_eq!(cs.hash(), "8211d52b5ad2618b2f8106c7c3f9ab213f6206e3ddbbb39e786167de5ea85dc3");

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(),
Expand All @@ -644,11 +663,10 @@ fn test_input_circuit_with_bls12_381() {
};

let mut position = 0u64;
let mut cur = note.cm(params);

assert_eq!(cs.get("randomization of note commitment/x3/num"), cur);
let cm: Fr = note.cm(params);
let mut cur = cm.clone();

for (i, val) in auth_path.into_iter().enumerate()
for (i, val) in auth_path.clone().into_iter().enumerate()
{
let (uncle, b) = val.unwrap();

Expand Down Expand Up @@ -678,13 +696,39 @@ fn test_input_circuit_with_bls12_381() {
}
}

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::<Bls12>(&expected_nf);
assert_eq!(expected_nf.len(), 2);

let mut cs = TestConstraintSystem::<Bls12>::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(),
anchor: Some(cur)
};

instance.synthesize(&mut cs).unwrap();

assert!(cs.is_satisfied());
assert_eq!(cs.num_constraints(), 98777);
assert_eq!(cs.hash(), "aedc6d7646e8e019db327bf256c322e54bc72aa9ac4e86943899557eb96507f3");

assert_eq!(cs.get("randomization of note commitment/x3/num"), cm);

assert_eq!(cs.num_inputs(), 8);
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(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!(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(6, "pack nullifier/input 0"), expected_nf[0]);
assert_eq!(cs.get_input(7, "pack nullifier/input 1"), expected_nf[1]);
}
Expand Down

0 comments on commit 470c957

Please sign in to comment.