Browse Source

zkSNARK: Enforce spend-authority of input notes.

pull/145/head
Sean Bowe 8 years ago
parent
commit
ca8d6c9347
  1. 24
      src/zcash/circuit/gadget.tcc
  2. 82
      src/zcash/circuit/note.tcc
  3. 77
      src/zcash/circuit/prfs.tcc
  4. 8
      src/zcash/circuit/utils.tcc

24
src/zcash/circuit/gadget.tcc

@ -1,4 +1,6 @@
#include "zcash/circuit/utils.tcc"
#include "zcash/circuit/prfs.tcc"
#include "zcash/circuit/note.tcc"
template<typename FieldT, size_t NumInputs, size_t NumOutputs>
class joinsplit_gadget : gadget<FieldT> {
@ -19,6 +21,9 @@ private:
// Aux inputs
pb_variable<FieldT> ZERO;
// Input note gadgets
boost::array<std::shared_ptr<input_note_gadget<FieldT>>, NumInputs> zk_input_notes;
public:
joinsplit_gadget(protoboard<FieldT> &pb) : gadget<FieldT>(pb) {
// Verification
@ -67,7 +72,14 @@ public:
// to be one automatically for us, and is known as `ONE`.
ZERO.allocate(pb);
for (size_t i = 0; i < NumInputs; i++) {
// Input note gadget for commitments, hmacs, nullifiers,
// and spend authority.
zk_input_notes[i].reset(new input_note_gadget<FieldT>(
pb,
ZERO
));
}
}
void generate_r1cs_constraints() {
@ -77,6 +89,11 @@ public:
// Constrain `ZERO`
generate_r1cs_equals_const_constraint<FieldT>(this->pb, ZERO, FieldT::zero(), "ZERO");
for (size_t i = 0; i < NumInputs; i++) {
// Constrain the JoinSplit input constraints.
zk_input_notes[i]->generate_r1cs_constraints();
}
}
void generate_r1cs_witness(
@ -91,6 +108,11 @@ public:
// Witness `zero`
this->pb.val(ZERO) = FieldT::zero();
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);
}
// This happens last, because only by now are all the
// verifier inputs resolved.
unpacker->generate_r1cs_witness_from_bits();

82
src/zcash/circuit/note.tcc

@ -0,0 +1,82 @@
template<typename FieldT>
class note_gadget : public gadget<FieldT> {
public:
pb_variable_array<FieldT> value;
std::shared_ptr<digest_variable<FieldT>> r;
note_gadget(protoboard<FieldT> &pb) : gadget<FieldT>(pb) {
value.allocate(pb, 64);
r.reset(new digest_variable<FieldT>(pb, 256, ""));
}
void generate_r1cs_constraints() {
for (size_t i = 0; i < 64; i++) {
generate_boolean_r1cs_constraint<FieldT>(
this->pb,
value[i],
"boolean_value"
);
}
r->generate_r1cs_constraints();
}
void generate_r1cs_witness(const Note& note) {
r->bits.fill_with_bits(this->pb, uint256_to_bool_vector(note.r));
value.fill_with_bits(this->pb, uint64_to_bool_vector(note.value));
}
};
template<typename FieldT>
class input_note_gadget : public note_gadget<FieldT> {
public:
std::shared_ptr<digest_variable<FieldT>> a_sk;
std::shared_ptr<digest_variable<FieldT>> a_pk;
std::shared_ptr<PRF_addr_a_pk_gadget<FieldT>> spend_authority;
input_note_gadget(
protoboard<FieldT>& pb,
pb_variable<FieldT>& ZERO
) : note_gadget<FieldT>(pb) {
a_sk.reset(new digest_variable<FieldT>(pb, 252, ""));
a_pk.reset(new digest_variable<FieldT>(pb, 256, ""));
spend_authority.reset(new PRF_addr_a_pk_gadget<FieldT>(
pb,
ZERO,
a_sk->bits,
a_pk
));
}
void generate_r1cs_constraints() {
note_gadget<FieldT>::generate_r1cs_constraints();
a_sk->generate_r1cs_constraints();
// TODO: This constraint may not be necessary if SHA256
// already boolean constrains its outputs.
a_pk->generate_r1cs_constraints();
spend_authority->generate_r1cs_constraints();
}
void generate_r1cs_witness(const SpendingKey& key, const Note& note) {
note_gadget<FieldT>::generate_r1cs_witness(note);
// Witness a_sk for the input
a_sk->bits.fill_with_bits(
this->pb,
trailing252(uint256_to_bool_vector(key))
);
// Witness a_pk for a_sk with PRF_addr
spend_authority->generate_r1cs_witness();
// [SANITY CHECK] Witness a_pk with note information
a_pk->bits.fill_with_bits(
this->pb,
uint256_to_bool_vector(note.a_pk)
);
}
};

77
src/zcash/circuit/prfs.tcc

@ -0,0 +1,77 @@
template<typename FieldT>
class PRF_gadget : gadget<FieldT> {
private:
std::shared_ptr<block_variable<FieldT>> block;
std::shared_ptr<sha256_compression_function_gadget<FieldT>> hasher;
std::shared_ptr<digest_variable<FieldT>> result;
public:
PRF_gadget(
protoboard<FieldT>& pb,
pb_variable<FieldT>& ZERO,
bool a,
bool b,
bool c,
bool d,
pb_variable_array<FieldT> x,
boost::optional<pb_variable_array<FieldT>> y,
std::shared_ptr<digest_variable<FieldT>> result
) : gadget<FieldT>(pb), result(result) {
pb_linear_combination_array<FieldT> IV = SHA256_default_IV(pb);
pb_variable_array<FieldT> discriminants;
discriminants.emplace_back(a ? ONE : ZERO);
discriminants.emplace_back(b ? ONE : ZERO);
discriminants.emplace_back(c ? ONE : ZERO);
discriminants.emplace_back(d ? ONE : ZERO);
if (!y) {
// Create y and pad it with zeroes.
y = pb_variable_array<FieldT>();
while (y->size() < 256) {
y->emplace_back(ZERO);
}
}
block.reset(new block_variable<FieldT>(pb, {
discriminants,
x,
*y
}, "PRF_block"));
hasher.reset(new sha256_compression_function_gadget<FieldT>(
pb,
IV,
block->bits,
*result,
"PRF_hasher"));
}
void generate_r1cs_constraints() {
hasher->generate_r1cs_constraints();
}
void generate_r1cs_witness() {
hasher->generate_r1cs_witness();
}
};
template<typename FieldT>
class PRF_addr_a_pk_gadget : public PRF_gadget<FieldT> {
public:
PRF_addr_a_pk_gadget(
protoboard<FieldT>& pb,
pb_variable<FieldT>& ZERO,
pb_variable_array<FieldT>& a_sk,
std::shared_ptr<digest_variable<FieldT>> result
) : PRF_gadget<FieldT>(pb, ZERO, 1, 1, 0, 0, a_sk, boost::none, result) {}
void generate_r1cs_constraints() {
PRF_gadget<FieldT>::generate_r1cs_constraints();
}
void generate_r1cs_witness() {
PRF_gadget<FieldT>::generate_r1cs_witness();
}
};

8
src/zcash/circuit/utils.tcc

@ -1,3 +1,11 @@
std::vector<bool> trailing252(std::vector<bool> input) {
if (input.size() != 256) {
throw std::length_error("trailing252 input invalid length");
}
return std::vector<bool>(input.begin() + 4, input.end());
}
std::vector<bool> uint256_to_bool_vector(uint256 input) {
std::vector<unsigned char> input_v(input.begin(), input.end());
std::vector<bool> output_bv(256, 0);

Loading…
Cancel
Save