From 5e61a78fecb077b1666a034392e2f0ff347b1b49 Mon Sep 17 00:00:00 2001 From: Sean Bowe Date: Wed, 4 May 2016 18:26:02 -0600 Subject: [PATCH] zkSNARK: Enforce disclosure of commitments to output notes. --- src/zcash/JoinSplit.cpp | 1 + src/zcash/circuit/commitment.tcc | 104 +++++++++++++++++++++++++++++++ src/zcash/circuit/gadget.tcc | 6 +- src/zcash/circuit/note.tcc | 29 ++++++++- src/zcash/circuit/utils.tcc | 11 ++++ 5 files changed, 148 insertions(+), 3 deletions(-) create mode 100644 src/zcash/circuit/commitment.tcc diff --git a/src/zcash/JoinSplit.cpp b/src/zcash/JoinSplit.cpp index f797ea859..37b57fb96 100644 --- a/src/zcash/JoinSplit.cpp +++ b/src/zcash/JoinSplit.cpp @@ -6,6 +6,7 @@ #include +#include #include #include #include diff --git a/src/zcash/circuit/commitment.tcc b/src/zcash/circuit/commitment.tcc new file mode 100644 index 000000000..769749a68 --- /dev/null +++ b/src/zcash/circuit/commitment.tcc @@ -0,0 +1,104 @@ +template +class note_commitment_gadget : gadget { +private: + std::shared_ptr> block1; + std::shared_ptr> block2; + std::shared_ptr> hasher1; + std::shared_ptr> intermediate_hash; + std::shared_ptr> hasher2; + +public: + note_commitment_gadget( + protoboard &pb, + pb_variable& ZERO, + pb_variable_array& a_pk, + pb_variable_array& v, + pb_variable_array& rho, + pb_variable_array& r, + std::shared_ptr> result + ) : gadget(pb) { + pb_variable_array leading_byte = + from_bits({1, 0, 1, 1, 0, 0, 0, 0}, ZERO); + + pb_variable_array first_of_rho(rho.begin(), rho.begin()+184); + pb_variable_array last_of_rho(rho.begin()+184, rho.end()); + + intermediate_hash.reset(new digest_variable(pb, 256, "")); + + // final padding + pb_variable_array length_padding = + from_bits({ + // padding + 1,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + + // length of message (840 bits) + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,1,1, + 0,1,0,0,1,0,0,0 + }, ZERO); + + block1.reset(new block_variable(pb, { + leading_byte, + a_pk, + v, + first_of_rho + }, "")); + + block2.reset(new block_variable(pb, { + last_of_rho, + r, + length_padding + }, "")); + + pb_linear_combination_array IV = SHA256_default_IV(pb); + + hasher1.reset(new sha256_compression_function_gadget( + pb, + IV, + block1->bits, + *intermediate_hash, + "")); + + pb_linear_combination_array IV2(intermediate_hash->bits); + + hasher2.reset(new sha256_compression_function_gadget( + pb, + IV2, + block2->bits, + *result, + "")); + } + + void generate_r1cs_constraints() { + // TODO: This may not be necessary if SHA256 constrains + // its output digests to be boolean anyway. + intermediate_hash->generate_r1cs_constraints(); + + hasher1->generate_r1cs_constraints(); + hasher2->generate_r1cs_constraints(); + } + + void generate_r1cs_witness() { + hasher1->generate_r1cs_witness(); + hasher2->generate_r1cs_witness(); + } +}; diff --git a/src/zcash/circuit/gadget.tcc b/src/zcash/circuit/gadget.tcc index 2f9e4e42c..9ce142143 100644 --- a/src/zcash/circuit/gadget.tcc +++ b/src/zcash/circuit/gadget.tcc @@ -1,5 +1,6 @@ #include "zcash/circuit/utils.tcc" #include "zcash/circuit/prfs.tcc" +#include "zcash/circuit/commitment.tcc" #include "zcash/circuit/note.tcc" template @@ -114,7 +115,8 @@ public: ZERO, zk_phi->bits, zk_h_sig->bits, - i ? true : false + i ? true : false, + zk_output_commitments[i] )); } } @@ -206,7 +208,7 @@ public: } for (size_t i = 0; i < NumOutputs; i++) { - insert_uint256(verify_inputs, uint256()); // TODO: commitment + insert_uint256(verify_inputs, commitments[i]); } insert_uint64(verify_inputs, 0); // TODO: vpub_old diff --git a/src/zcash/circuit/note.tcc b/src/zcash/circuit/note.tcc index e4efb763b..5133e26ff 100644 --- a/src/zcash/circuit/note.tcc +++ b/src/zcash/circuit/note.tcc @@ -110,8 +110,10 @@ template class output_note_gadget : public note_gadget { private: std::shared_ptr> rho; + std::shared_ptr> a_pk; std::shared_ptr> prevent_faerie_gold; + std::shared_ptr> commit_to_outputs; public: output_note_gadget( @@ -119,9 +121,11 @@ public: pb_variable& ZERO, pb_variable_array& phi, pb_variable_array& h_sig, - bool nonce + bool nonce, + std::shared_ptr> commitment ) : note_gadget(pb) { rho.reset(new digest_variable(pb, 256, "")); + a_pk.reset(new digest_variable(pb, 256, "")); // Do not allow the caller to choose the same "rho" // for any two valid notes in a given view of the @@ -135,16 +139,32 @@ public: nonce, rho )); + + // Commit to the output notes publicly without + // disclosing them. + commit_to_outputs.reset(new note_commitment_gadget( + pb, + ZERO, + a_pk->bits, + this->value, + rho->bits, + this->r->bits, + commitment + )); } void generate_r1cs_constraints() { note_gadget::generate_r1cs_constraints(); + a_pk->generate_r1cs_constraints(); + // TODO: This constraint may not be necessary if SHA256 // already boolean constrains its outputs. rho->generate_r1cs_constraints(); prevent_faerie_gold->generate_r1cs_constraints(); + + commit_to_outputs->generate_r1cs_constraints(); } void generate_r1cs_witness(const Note& note) { @@ -158,5 +178,12 @@ public: this->pb, uint256_to_bool_vector(note.rho) ); + + a_pk->bits.fill_with_bits( + this->pb, + uint256_to_bool_vector(note.a_pk) + ); + + commit_to_outputs->generate_r1cs_witness(); } }; diff --git a/src/zcash/circuit/utils.tcc b/src/zcash/circuit/utils.tcc index 1cc6bb9e9..3088362cb 100644 --- a/src/zcash/circuit/utils.tcc +++ b/src/zcash/circuit/utils.tcc @@ -1,3 +1,14 @@ +template +pb_variable_array from_bits(std::vector bits, pb_variable& ZERO) { + pb_variable_array acc; + + BOOST_FOREACH(bool bit, bits) { + acc.emplace_back(bit ? ONE : ZERO); + } + + return acc; +} + std::vector trailing252(std::vector input) { if (input.size() != 256) { throw std::length_error("trailing252 input invalid length");