Browse Source

zkSNARK: Enforce merkle authentication path from nonzero-valued public inputs to root.

pull/145/head
Sean Bowe 8 years ago
parent
commit
59c3d926c6
  1. 34
      src/zcash/circuit/gadget.tcc
  2. 60
      src/zcash/circuit/merkle.tcc
  3. 41
      src/zcash/circuit/note.tcc

34
src/zcash/circuit/gadget.tcc

@ -1,6 +1,7 @@
#include "zcash/circuit/utils.tcc"
#include "zcash/circuit/prfs.tcc"
#include "zcash/circuit/commitment.tcc"
#include "zcash/circuit/merkle.tcc"
#include "zcash/circuit/note.tcc"
template<typename FieldT, size_t NumInputs, size_t NumOutputs>
@ -94,7 +95,8 @@ public:
zk_input_notes[i].reset(new input_note_gadget<FieldT>(
pb,
ZERO,
zk_input_nullifiers[i]
zk_input_nullifiers[i],
*zk_merkle_root
));
// The input keys authenticate h_sig to prevent
@ -180,6 +182,16 @@ public:
// Witness `zero`
this->pb.val(ZERO) = FieldT::zero();
// Witness rt. This is not a sanity check.
//
// This ensures the read gadget constrains
// the intended root in the event that
// both inputs are zero-valued.
zk_merkle_root->bits.fill_with_bits(
this->pb,
uint256_to_bool_vector(rt)
);
// Witness public balance values
zk_vpub_old.fill_with_bits(
this->pb,
@ -204,7 +216,12 @@ public:
for (size_t i = 0; i < NumInputs; i++) {
// Witness the input information.
zk_input_notes[i]->generate_r1cs_witness(inputs[i].key, inputs[i].note);
auto merkle_path = inputs[i].witness.path();
zk_input_notes[i]->generate_r1cs_witness(
merkle_path,
inputs[i].key,
inputs[i].note
);
// Witness hmacs
zk_hmac_authentication[i]->generate_r1cs_witness();
@ -215,6 +232,17 @@ public:
zk_output_notes[i]->generate_r1cs_witness(outputs[i]);
}
// [SANITY CHECK] Ensure that the intended root
// was witnessed by the inputs, even if the read
// gadget overwrote it. This allows the prover to
// fail instead of the verifier, in the event that
// the roots of the inputs do not match the
// treestate provided to the proving API.
zk_merkle_root->bits.fill_with_bits(
this->pb,
uint256_to_bool_vector(rt)
);
// This happens last, because only by now are all the
// verifier inputs resolved.
unpacker->generate_r1cs_witness_from_bits();
@ -231,7 +259,7 @@ public:
) {
std::vector<bool> verify_inputs;
insert_uint256(verify_inputs, uint256()); // TODO: rt
insert_uint256(verify_inputs, rt);
insert_uint256(verify_inputs, h_sig);
for (size_t i = 0; i < NumInputs; i++) {

60
src/zcash/circuit/merkle.tcc

@ -0,0 +1,60 @@
template<typename FieldT>
class merkle_tree_gadget : gadget<FieldT> {
private:
typedef sha256_two_to_one_hash_gadget<FieldT> sha256_gadget;
pb_variable_array<FieldT> positions;
std::shared_ptr<merkle_authentication_path_variable<FieldT, sha256_gadget>> authvars;
std::shared_ptr<merkle_tree_check_read_gadget<FieldT, sha256_gadget>> auth;
public:
merkle_tree_gadget(
protoboard<FieldT>& pb,
digest_variable<FieldT> leaf,
digest_variable<FieldT> root,
pb_variable<FieldT>& enforce
) : gadget<FieldT>(pb) {
positions.allocate(pb, INCREMENTAL_MERKLE_TREE_DEPTH);
authvars.reset(new merkle_authentication_path_variable<FieldT, sha256_gadget>(
pb, INCREMENTAL_MERKLE_TREE_DEPTH, "auth"
));
auth.reset(new merkle_tree_check_read_gadget<FieldT, sha256_gadget>(
pb,
INCREMENTAL_MERKLE_TREE_DEPTH,
positions,
leaf,
root,
*authvars,
enforce,
""
));
}
void generate_r1cs_constraints() {
for (size_t i = 0; i < INCREMENTAL_MERKLE_TREE_DEPTH; i++) {
// TODO: This might not be necessary, and doesn't
// appear to be done in libsnark's tests, but there
// is no documentation, so let's do it anyway to
// be safe.
generate_boolean_r1cs_constraint<FieldT>(
this->pb,
positions[i],
"boolean_positions"
);
}
authvars->generate_r1cs_constraints();
auth->generate_r1cs_constraints();
}
void generate_r1cs_witness(const MerklePath& path) {
// TODO: Change libsnark so that it doesn't require this goofy
// number thing in its API.
size_t path_index = libzerocash::convertVectorToInt(path.index);
positions.fill_with_bits_of_ulong(this->pb, path_index);
authvars->generate_r1cs_witness(path_index, path.authentication_path);
auth->generate_r1cs_witness();
}
};

41
src/zcash/circuit/note.tcc

@ -36,6 +36,9 @@ private:
std::shared_ptr<digest_variable<FieldT>> commitment;
std::shared_ptr<note_commitment_gadget<FieldT>> commit_to_inputs;
pb_variable<FieldT> value_enforce;
std::shared_ptr<merkle_tree_gadget<FieldT>> witness_input;
std::shared_ptr<PRF_addr_a_pk_gadget<FieldT>> spend_authority;
std::shared_ptr<PRF_nf_gadget<FieldT>> expose_nullifiers;
public:
@ -44,7 +47,8 @@ public:
input_note_gadget(
protoboard<FieldT>& pb,
pb_variable<FieldT>& ZERO,
std::shared_ptr<digest_variable<FieldT>> nullifier
std::shared_ptr<digest_variable<FieldT>> nullifier,
digest_variable<FieldT> rt
) : note_gadget<FieldT>(pb) {
a_sk.reset(new digest_variable<FieldT>(pb, 252, ""));
a_pk.reset(new digest_variable<FieldT>(pb, 256, ""));
@ -75,6 +79,15 @@ public:
this->r->bits,
commitment
));
value_enforce.allocate(pb);
witness_input.reset(new merkle_tree_gadget<FieldT>(
pb,
*commitment,
rt,
value_enforce
));
}
void generate_r1cs_constraints() {
@ -92,9 +105,27 @@ public:
expose_nullifiers->generate_r1cs_constraints();
commit_to_inputs->generate_r1cs_constraints();
// value * (1 - enforce) = 0
// Given `enforce` is boolean constrained:
// If `value` is zero, `enforce` _can_ be zero.
// If `value` is nonzero, `enforce` _must_ be one.
generate_boolean_r1cs_constraint<FieldT>(this->pb, value_enforce,"");
this->pb.add_r1cs_constraint(r1cs_constraint<FieldT>(
packed_addition(this->value),
(1 - value_enforce),
0
), "");
witness_input->generate_r1cs_constraints();
}
void generate_r1cs_witness(const SpendingKey& key, const Note& note) {
void generate_r1cs_witness(
const MerklePath& path,
const SpendingKey& key,
const Note& note
) {
note_gadget<FieldT>::generate_r1cs_witness(note);
// Witness a_sk for the input
@ -130,6 +161,12 @@ public:
this->pb,
uint256_to_bool_vector(note.cm())
);
// Set enforce flag for nonzero input value
this->pb.val(value_enforce) = (note.value != 0) ? FieldT::one() : FieldT::zero();
// Witness merkle tree authentication path
witness_input->generate_r1cs_witness(path);
}
};

Loading…
Cancel
Save