diff --git a/src/zcash/JoinSplit.cpp b/src/zcash/JoinSplit.cpp index 6f92652d3..f797ea859 100644 --- a/src/zcash/JoinSplit.cpp +++ b/src/zcash/JoinSplit.cpp @@ -2,6 +2,10 @@ #include "prf.h" #include "sodium.h" +#include "zerocash/utils/util.h" + +#include + #include #include #include diff --git a/src/zcash/circuit/gadget.tcc b/src/zcash/circuit/gadget.tcc index 6877620d0..46801e710 100644 --- a/src/zcash/circuit/gadget.tcc +++ b/src/zcash/circuit/gadget.tcc @@ -1,16 +1,66 @@ +#include "zcash/circuit/utils.tcc" + template class joinsplit_gadget : gadget { +private: + // Verifier inputs + pb_variable_array zk_packed_inputs; + pb_variable_array zk_unpacked_inputs; + std::shared_ptr> unpacker; + + std::shared_ptr> zk_merkle_root; + std::shared_ptr> zk_h_sig; + boost::array>, NumInputs> zk_input_nullifiers; + boost::array>, NumInputs> zk_input_hmacs; + boost::array>, NumOutputs> zk_output_commitments; + pb_variable_array zk_vpub_old; + pb_variable_array zk_vpub_new; + public: joinsplit_gadget(protoboard &pb) : gadget(pb) { - pb_variable_array test; - test.allocate(pb, 1); - pb.set_input_sizes(1); + // Verification + { + // The verification inputs are all bit-strings of various + // lengths (256-bit digests and 64-bit integers) and so we + // pack them into as few field elements as possible. (The + // more verification inputs you have, the more expensive + // verification is.) + zk_packed_inputs.allocate(pb, verifying_field_element_size()); + pb.set_input_sizes(verifying_field_element_size()); + + alloc_uint256(zk_unpacked_inputs, zk_merkle_root); + alloc_uint256(zk_unpacked_inputs, zk_h_sig); + + for (size_t i = 0; i < NumInputs; i++) { + alloc_uint256(zk_unpacked_inputs, zk_input_nullifiers[i]); + alloc_uint256(zk_unpacked_inputs, zk_input_hmacs[i]); + } + + for (size_t i = 0; i < NumOutputs; i++) { + alloc_uint256(zk_unpacked_inputs, zk_output_commitments[i]); + } - // TODO! + alloc_uint64(zk_unpacked_inputs, zk_vpub_old); + alloc_uint64(zk_unpacked_inputs, zk_vpub_new); + + assert(zk_unpacked_inputs.size() == verifying_input_bit_size()); + + // This gadget will ensure that all of the inputs we provide are + // boolean constrained. + unpacker.reset(new multipacking_gadget( + pb, + zk_unpacked_inputs, + zk_packed_inputs, + FieldT::capacity(), + "unpacker" + )); + } } void generate_r1cs_constraints() { - // TODO! + // The true passed here ensures all the inputs + // are boolean constrained. + unpacker->generate_r1cs_constraints(true); } void generate_r1cs_witness( @@ -22,7 +72,9 @@ public: uint64_t vpub_old, uint64_t vpub_new ) { - // TODO! + // This happens last, because only by now are all the + // verifier inputs resolved. + unpacker->generate_r1cs_witness_from_bits(); } static r1cs_primary_input witness_map( @@ -34,11 +86,64 @@ public: uint64_t vpub_old, uint64_t vpub_new ) { - // todo + std::vector verify_inputs; + + insert_uint256(verify_inputs, uint256()); // TODO: rt + insert_uint256(verify_inputs, uint256()); // TODO: h_sig + + for (size_t i = 0; i < NumInputs; i++) { + insert_uint256(verify_inputs, uint256()); // TODO: nullifier + insert_uint256(verify_inputs, uint256()); // TODO: hmac + } + + for (size_t i = 0; i < NumOutputs; i++) { + insert_uint256(verify_inputs, uint256()); // TODO: commitment + } + + insert_uint64(verify_inputs, 0); // TODO: vpub_old + insert_uint64(verify_inputs, 0); // TODO: vpub_new - std::vector input_as_field_elements; - input_as_field_elements.push_back(FieldT::zero()); + assert(verify_inputs.size() == verifying_input_bit_size()); + auto verify_field_elements = pack_bit_vector_into_field_element_vector(verify_inputs); + assert(verify_field_elements.size() == verifying_field_element_size()); + return verify_field_elements; + } + + static size_t verifying_input_bit_size() { + size_t acc = 0; + + acc += 256; // the merkle root (anchor) + acc += 256; // h_sig + for (size_t i = 0; i < NumInputs; i++) { + acc += 256; // nullifier + acc += 256; // hmac + } + for (size_t i = 0; i < NumOutputs; i++) { + acc += 256; // new commitment + } + acc += 64; // vpub_old + acc += 64; // vpub_new - return input_as_field_elements; + return acc; + } + + static size_t verifying_field_element_size() { + return div_ceil(verifying_input_bit_size(), FieldT::capacity()); + } + + void alloc_uint256( + pb_variable_array& packed_into, + std::shared_ptr>& var + ) { + var.reset(new digest_variable(this->pb, 256, "")); + packed_into.insert(packed_into.end(), var->bits.begin(), var->bits.end()); + } + + void alloc_uint64( + pb_variable_array& packed_into, + pb_variable_array& integer + ) { + integer.allocate(this->pb, 64, ""); + packed_into.insert(packed_into.end(), integer.begin(), integer.end()); } }; \ No newline at end of file diff --git a/src/zcash/circuit/utils.tcc b/src/zcash/circuit/utils.tcc new file mode 100644 index 000000000..64018a3b4 --- /dev/null +++ b/src/zcash/circuit/utils.tcc @@ -0,0 +1,29 @@ +std::vector uint256_to_bool_vector(uint256 input) { + std::vector input_v(input.begin(), input.end()); + std::vector output_bv(256, 0); + libzerocash::convertBytesVectorToVector( + input_v, + output_bv + ); + + return output_bv; +} + +std::vector uint64_to_bool_vector(uint64_t input) { + std::vector num_bv(8, 0); + libzerocash::convertIntToBytesVector(input, num_bv); + std::vector num_v(64, 0); + libzerocash::convertBytesVectorToVector(num_bv, num_v); + + return num_v; +} + +void insert_uint256(std::vector& into, uint256 from) { + std::vector blob = uint256_to_bool_vector(from); + into.insert(into.end(), blob.begin(), blob.end()); +} + +void insert_uint64(std::vector& into, uint64_t from) { + std::vector num = uint64_to_bool_vector(from); + into.insert(into.end(), num.begin(), num.end()); +} \ No newline at end of file