zkSNARK: Foundations of circuit design and verification logic.
This commit is contained in:
parent
369df06583
commit
074eb3a2cf
|
@ -2,6 +2,10 @@
|
|||
#include "prf.h"
|
||||
#include "sodium.h"
|
||||
|
||||
#include "zerocash/utils/util.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <boost/format.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
#include <fstream>
|
||||
|
|
|
@ -1,16 +1,66 @@
|
|||
#include "zcash/circuit/utils.tcc"
|
||||
|
||||
template<typename FieldT, size_t NumInputs, size_t NumOutputs>
|
||||
class joinsplit_gadget : gadget<FieldT> {
|
||||
private:
|
||||
// Verifier inputs
|
||||
pb_variable_array<FieldT> zk_packed_inputs;
|
||||
pb_variable_array<FieldT> zk_unpacked_inputs;
|
||||
std::shared_ptr<multipacking_gadget<FieldT>> unpacker;
|
||||
|
||||
std::shared_ptr<digest_variable<FieldT>> zk_merkle_root;
|
||||
std::shared_ptr<digest_variable<FieldT>> zk_h_sig;
|
||||
boost::array<std::shared_ptr<digest_variable<FieldT>>, NumInputs> zk_input_nullifiers;
|
||||
boost::array<std::shared_ptr<digest_variable<FieldT>>, NumInputs> zk_input_hmacs;
|
||||
boost::array<std::shared_ptr<digest_variable<FieldT>>, NumOutputs> zk_output_commitments;
|
||||
pb_variable_array<FieldT> zk_vpub_old;
|
||||
pb_variable_array<FieldT> zk_vpub_new;
|
||||
|
||||
public:
|
||||
joinsplit_gadget(protoboard<FieldT> &pb) : gadget<FieldT>(pb) {
|
||||
pb_variable_array<FieldT> 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());
|
||||
|
||||
// TODO!
|
||||
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]);
|
||||
}
|
||||
|
||||
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<FieldT>(
|
||||
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<FieldT> witness_map(
|
||||
|
@ -34,11 +86,64 @@ public:
|
|||
uint64_t vpub_old,
|
||||
uint64_t vpub_new
|
||||
) {
|
||||
// todo
|
||||
std::vector<bool> verify_inputs;
|
||||
|
||||
std::vector<FieldT> input_as_field_elements;
|
||||
input_as_field_elements.push_back(FieldT::zero());
|
||||
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
|
||||
}
|
||||
|
||||
return input_as_field_elements;
|
||||
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
|
||||
|
||||
assert(verify_inputs.size() == verifying_input_bit_size());
|
||||
auto verify_field_elements = pack_bit_vector_into_field_element_vector<FieldT>(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 acc;
|
||||
}
|
||||
|
||||
static size_t verifying_field_element_size() {
|
||||
return div_ceil(verifying_input_bit_size(), FieldT::capacity());
|
||||
}
|
||||
|
||||
void alloc_uint256(
|
||||
pb_variable_array<FieldT>& packed_into,
|
||||
std::shared_ptr<digest_variable<FieldT>>& var
|
||||
) {
|
||||
var.reset(new digest_variable<FieldT>(this->pb, 256, ""));
|
||||
packed_into.insert(packed_into.end(), var->bits.begin(), var->bits.end());
|
||||
}
|
||||
|
||||
void alloc_uint64(
|
||||
pb_variable_array<FieldT>& packed_into,
|
||||
pb_variable_array<FieldT>& integer
|
||||
) {
|
||||
integer.allocate(this->pb, 64, "");
|
||||
packed_into.insert(packed_into.end(), integer.begin(), integer.end());
|
||||
}
|
||||
};
|
|
@ -0,0 +1,29 @@
|
|||
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);
|
||||
libzerocash::convertBytesVectorToVector(
|
||||
input_v,
|
||||
output_bv
|
||||
);
|
||||
|
||||
return output_bv;
|
||||
}
|
||||
|
||||
std::vector<bool> uint64_to_bool_vector(uint64_t input) {
|
||||
std::vector<unsigned char> num_bv(8, 0);
|
||||
libzerocash::convertIntToBytesVector(input, num_bv);
|
||||
std::vector<bool> num_v(64, 0);
|
||||
libzerocash::convertBytesVectorToVector(num_bv, num_v);
|
||||
|
||||
return num_v;
|
||||
}
|
||||
|
||||
void insert_uint256(std::vector<bool>& into, uint256 from) {
|
||||
std::vector<bool> blob = uint256_to_bool_vector(from);
|
||||
into.insert(into.end(), blob.begin(), blob.end());
|
||||
}
|
||||
|
||||
void insert_uint64(std::vector<bool>& into, uint64_t from) {
|
||||
std::vector<bool> num = uint64_to_bool_vector(from);
|
||||
into.insert(into.end(), num.begin(), num.end());
|
||||
}
|
Loading…
Reference in New Issue