Skip to content

Commit

Permalink
libzecale: move variable allocation and circuit control to the aggreg…
Browse files Browse the repository at this point in the history
…ator_circuit_wrapper
  • Loading branch information
dtebbs committed Aug 24, 2020
1 parent 952317a commit a79220e
Show file tree
Hide file tree
Showing 4 changed files with 130 additions and 111 deletions.
32 changes: 31 additions & 1 deletion libzecale/circuits/aggregator_circuit_wrapper.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,41 @@ class aggregator_circuit_wrapper
private:
using npp = other_curve<wppT>;
using nsnark = typename nverifierT::snark;
using verification_key_variable_gadget =
typename nverifierT::verification_key_variable_gadget;
using proof_variable_gadget = typename nverifierT::proof_variable_gadget;

const size_t _num_inputs_per_nested_proof;

libsnark::protoboard<libff::Fr<wppT>> _pb;
aggregator_gadget<wppT, nverifierT, NumProofs> _aggregator_gadget;

/// (Primary) The nested primary inputs lie in the scalar field
/// `libff::Fr<nppT>`, and must be represented as elements of
/// `libff::Fr<wppT>` for use in the wrapper proof.
std::array<libsnark::pb_variable_array<libff::Fr<wppT>>, NumProofs>
_nested_primary_inputs;

/// (Primary) The array of the results of the verifiers. 1 meaning that the
/// nested proof is valid, 0 meaning it may not be valid.
std::array<libsnark::pb_variable<libff::Fr<wppT>>, NumProofs>
_nested_proof_results;

/// (Auxiliary) Verification key used to verify the nested proofs. Consists
/// of group elements of `nppT`, which again, can be represented using
/// elements in `libff::Fr<wppT>`.
std::shared_ptr<verification_key_variable_gadget> _nested_vk;

/// (Auxiliary) The nested proofs (defined over `nppT`) to verify. As above,
/// these are verified by virtue of the fact that the base field for nppT is
/// the scalar field of wppT. These gadgets handle take a witness in the
/// form of a proof with group elements from nppT and represent them as
/// variables in the wppT scalar field.
/// (Variables are expected to be auxiliary inputs).
std::array<std::shared_ptr<proof_variable_gadget>, NumProofs>
_nested_proofs;

std::shared_ptr<aggregator_gadget<wppT, nverifierT, NumProofs>>
_aggregator_gadget;

// TODO: Cache the circuit to save reconstructing it at every call.

Expand Down
79 changes: 69 additions & 10 deletions libzecale/circuits/aggregator_circuit_wrapper.tcc
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,58 @@ namespace libzecale
template<typename wppT, typename wsnarkT, typename nverifierT, size_t NumProofs>
aggregator_circuit_wrapper<wppT, wsnarkT, nverifierT, NumProofs>::
aggregator_circuit_wrapper(const size_t inputs_per_nested_proof)
: _num_inputs_per_nested_proof(inputs_per_nested_proof)
, _pb()
, _aggregator_gadget(_pb, _num_inputs_per_nested_proof)
: _num_inputs_per_nested_proof(inputs_per_nested_proof), _pb()
{
_aggregator_gadget.generate_r1cs_constraints();
// The order of allocation here is important as it determines which inputs
// are primary.

// For each proof in a batch, allocate primary inputs and results. These
// are the primary inputs. Note: bother inputs and results will be
// populated by the aggregator gadget.
for (size_t i = 0; i < NumProofs; i++) {
_nested_primary_inputs[i].allocate(
_pb,
_num_inputs_per_nested_proof,
FMT("", "_nested_primary_inputs_bits[%zu]", i));

_nested_proof_results[i].allocate(
_pb, FMT("", "_nested_proof_results[%zu]", i));
}

// Set the number of primary inputs.
const size_t total_primary_inputs =
NumProofs * (inputs_per_nested_proof + 1);
_pb.set_input_sizes(total_primary_inputs);

// Allocate vk and the intermediate bit representation
const size_t vk_size_in_bits =
verification_key_variable_gadget::size_in_bits(
_num_inputs_per_nested_proof);
libsnark::pb_variable_array<libff::Fr<wppT>> nested_vk_bits;
nested_vk_bits.allocate(_pb, vk_size_in_bits, "nested_vk_bits");
_nested_vk.reset(new verification_key_variable_gadget(
_pb, nested_vk_bits, _num_inputs_per_nested_proof, "_nested_vk"));

// Allocate proof variables.
for (size_t i = 0; i < NumProofs; i++) {
_nested_proofs[i].reset(
new proof_variable_gadget(_pb, FMT("", "_nested_proofs[%zu]", i)));
}

_aggregator_gadget.reset(new aggregator_gadget<wppT, nverifierT, NumProofs>(
_pb,
*_nested_vk,
_nested_primary_inputs,
_nested_proofs,
_nested_proof_results,
"_aggregator_gadget"));

// Initialize all constraints in the circuit.
_nested_vk->generate_r1cs_constraints(true);
for (size_t i = 0; i < NumProofs; ++i) {
_nested_proofs[i]->generate_r1cs_constraints();
}
_aggregator_gadget->generate_r1cs_constraints();
}

template<typename wppT, typename wsnarkT, typename nverifierT, size_t NumProofs>
Expand Down Expand Up @@ -56,21 +103,33 @@ libzeth::extended_proof<wppT, wsnarkT> aggregator_circuit_wrapper<
NumProofs> &extended_proofs,
const typename wsnarkT::proving_key &aggregator_proving_key)
{
for (const libzeth::extended_proof<npp, nsnark> *ep : extended_proofs) {
if (ep->get_primary_inputs().size() != _num_inputs_per_nested_proof) {
// Witness the proofs and construct the array of primary inputs (in npp).
// These will be used to populate _nested_primary_inputs.
std::array<const libsnark::r1cs_primary_input<libff::Fr<npp>> *, NumProofs>
nested_inputs{};
for (size_t i = 0; i < NumProofs; ++i) {
const libzeth::extended_proof<npp, nsnark> &ep = *(extended_proofs[i]);
if (ep.get_primary_inputs().size() != _num_inputs_per_nested_proof) {
throw std::runtime_error(
"attempt to aggregate proof with invalid number of inputs");
}

nested_inputs[i] = &ep.get_primary_inputs();
_nested_proofs[i]->generate_r1cs_witness(ep.get_proof());
}

// We pass to the witness generation function the elements defined over the
// "other curve". See:
// https://github.com/scipr-lab/libsnark/blob/master/libsnark/gadgetlib1/gadgets/verifiers/r1cs_ppzksnark_verifier_gadget.hpp#L98
_aggregator_gadget.generate_r1cs_witness(nested_vk, extended_proofs);
// Witness the verification key
_nested_vk->generate_r1cs_witness(nested_vk);

// Pass the input values (in npp) to the aggregator gadget.
_aggregator_gadget->generate_r1cs_witness(nested_inputs);

#ifdef DEBUG
// Check the validity of the circuit.
bool is_valid_witness = _pb.is_satisfied();
std::cout << "*** [DEBUG] Satisfiability result: " << is_valid_witness
<< " ***" << std::endl;
#endif

// Return an extended_proof for the given witness.
return extended_proof<wppT, wsnarkT>(
Expand Down
40 changes: 12 additions & 28 deletions libzecale/circuits/aggregator_gadget.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,20 +43,10 @@ class aggregator_gadget : libsnark::gadget<libff::Fr<wppT>>

const size_t num_inputs_per_nested_proof;

/// The nested primary inputs lie in the scalar field `libff::Fr<nppT>`,
/// and must be represented as elements of `libff::Fr<wppT>` for use in the
/// wrapper proof. This gadget assumes that libff::Fr<nppT> can be
/// represented as a single `libff::Fr<wppT>`, and internally asserts this.
/// (Expected to be primary inputs to the wrapping statement).
// Required in order to generate the bit strings from witness value.
std::array<libsnark::pb_variable_array<libff::Fr<wppT>>, NumProofs>
nested_primary_inputs;

/// The array of the results of the verifiers. 1 meaning that the nested
/// proof is valid, 0 meaning it may not be valid. (Expected to be a
/// primary inputs to the wrapping statement).
std::array<libsnark::pb_variable<libff::Fr<wppT>>, NumProofs>
nested_proofs_results;

/// The binary representation of inputs to the nested Fr<nppT> inputs. Each
/// entry is the concatenation of all bits of the inputs to the a single
/// nested proof. (Expected to be an auxiliary input).
Expand All @@ -67,35 +57,29 @@ class aggregator_gadget : libsnark::gadget<libff::Fr<wppT>>
std::vector<std::shared_ptr<input_packing_gadget>>
nested_primary_input_packers;

/// The nested proofs (defined over `nppT`) to verify. As above, these are
/// verified by virtue of the fact that the base field for nppT is the
/// scalar field of wppT. These gadgets take a witness in the form of a
/// proof with group elements from nppT and represent them as variables in
/// the wppT scalar field. (Variables are expected to be auxiliary inputs).
std::array<std::shared_ptr<proof_variable_gadget>, NumProofs> nested_proofs;

/// (Nested) verification key used to verify the nested proofs. Consists of
/// group elements of `nppT`, which again, can be represented using
/// elements in `libff::Fr<wppT>`.
std::shared_ptr<verification_key_variable_gadget> nested_vk;

/// Gadgets that verify the proofs and inputs against nested_vk.
// Gadgets that verify the proofs and inputs against nested_vk.
std::array<std::shared_ptr<verifier_gadget>, NumProofs> verifiers;

public:
aggregator_gadget(
libsnark::protoboard<libff::Fr<wppT>> &pb,
const size_t inputs_per_nested_proof,
const std::string &annotation_prefix = "aggregator_gadget");
const verification_key_variable_gadget &vk,
const std::array<
libsnark::pb_variable_array<libff::Fr<wppT>>,
NumProofs> &inputs,
const std::array<std::shared_ptr<proof_variable_gadget>, NumProofs>
&proofs,
const std::array<libsnark::pb_variable<libff::Fr<wppT>>, NumProofs>
&proof_results,
const std::string &annotation_prefix);

void generate_r1cs_constraints();

/// Set the wppT scalar variables based on the nested verification key,
/// proofs and inputs in nppT.
void generate_r1cs_witness(
const typename nsnark::verification_key &in_nested_vk,
const std::array<
const libzeth::extended_proof<npp, nsnark> *,
const libsnark::r1cs_primary_input<libff::Fr<npp>> *,
NumProofs> &in_extended_proofs);
};

Expand Down
90 changes: 18 additions & 72 deletions libzecale/circuits/aggregator_gadget.tcc
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,16 @@ namespace libzecale
template<typename wppT, typename nverifierT, size_t NumProofs>
aggregator_gadget<wppT, nverifierT, NumProofs>::aggregator_gadget(
libsnark::protoboard<libff::Fr<wppT>> &pb,
const size_t inputs_per_nested_proof,
const verification_key_variable_gadget &vk,
const std::array<libsnark::pb_variable_array<libff::Fr<wppT>>, NumProofs>
&inputs,
const std::array<std::shared_ptr<proof_variable_gadget>, NumProofs> &proofs,
const std::array<libsnark::pb_variable<libff::Fr<wppT>>, NumProofs>
&proof_results,
const std::string &annotation_prefix)
: libsnark::gadget<libff::Fr<wppT>>(pb, annotation_prefix)
, num_inputs_per_nested_proof(inputs_per_nested_proof)
, num_inputs_per_nested_proof(vk.input_size)
, nested_primary_inputs(inputs)
{
// Assert that a single input of a nested proof (element of
// libff::Fr<nppT>) can be encoded in a single input of the wrapping proof
Expand All @@ -28,27 +34,6 @@ aggregator_gadget<wppT, nverifierT, NumProofs>::aggregator_gadget(
// <= `libff::Fr<wppT>::num_bits`.
assert(libff::Fr<npp>::num_bits <= libff::Fr<wppT>::num_bits);

// TODO: allocations of these "public" variables should happen outside of
// the gadget.

// Allocate the primary inputs and results first (these are expected to be
// public).
for (size_t i = 0; i < NumProofs; i++) {
nested_primary_inputs[i].allocate(
pb,
num_inputs_per_nested_proof,
FMT(this->annotation_prefix,
" nested_primary_inputs_bits[%zu]",
i));

// Allocation of the results
nested_proofs_results[i].allocate(
pb, FMT(this->annotation_prefix, " nested_proofs_results[%zu]", i));
}

// TODO: Allocate (or accept) a variable to store the hash of the VK (to
// avoid proofs generated with a malicious keypair).

// Allocate the bit representation of the public inputs and initialize the
// input packers.
const size_t num_bits_per_input = libff::Fr<npp>::size_in_bits();
Expand All @@ -65,88 +50,49 @@ aggregator_gadget<wppT, nverifierT, NumProofs>::aggregator_gadget(
nested_primary_input_packers.emplace_back(new input_packing_gadget(
pb,
nested_primary_inputs_bits[i],
nested_primary_inputs[i],
inputs[i],
num_bits_per_input,
FMT(annotation_prefix, " nested_input_packers[%zu]", i)));
}

// TODO: this should be done by the caller - not by a gadget.

// Set the number of primary inputs.
const size_t total_primary_inputs =
NumProofs * (num_inputs_per_nested_proof + 1);
pb.set_input_sizes(total_primary_inputs);

// The nested VK is interpreted as an array of bits. The number of primary
// inputs is required since it determines the size of the nested VK.
const size_t vk_size_in_bits =
verification_key_variable_gadget::size_in_bits(
num_inputs_per_nested_proof);
libsnark::pb_variable_array<libff::Fr<wppT>> nested_vk_bits;
nested_vk_bits.allocate(
pb, vk_size_in_bits, FMT(this->annotation_prefix, " vk_size_in_bits"));
nested_vk.reset(new verification_key_variable_gadget(
pb,
nested_vk_bits,
num_inputs_per_nested_proof,
FMT(this->annotation_prefix, " nested_vk")));

// Allocate proof variable gadgets
for (size_t i = 0; i < NumProofs; i++) {
nested_proofs[i].reset(new proof_variable_gadget(
pb, FMT(this->annotation_prefix, " nested_proofs[%zu]", i)));
}

// Initialize the verifier gadgets
for (size_t i = 0; i < NumProofs; i++) {
verifiers[i].reset(new verifier_gadget(
pb,
*nested_vk,
vk,
nested_primary_inputs_bits[i],
libff::Fr<npp>::size_in_bits(),
*nested_proofs[i],
nested_proofs_results[i],
*proofs[i],
proof_results[i],
FMT(this->annotation_prefix, " verifiers[%zu]", i)));
}
}

template<typename wppT, typename nverifierT, size_t NumProofs>
void aggregator_gadget<wppT, nverifierT, NumProofs>::generate_r1cs_constraints()
{
// Generate constraints for the verification key
nested_vk->generate_r1cs_constraints(true); // ensure bitness

// Generate constraints (including boolean-ness of the bit representations)
// for input packers, nested proofs and the proof verifiers.
for (size_t i = 0; i < NumProofs; i++) {
nested_primary_input_packers[i]->generate_r1cs_constraints(true);
nested_proofs[i]->generate_r1cs_constraints();
verifiers[i]->generate_r1cs_constraints();
}
}

template<typename wppT, typename nverifierT, size_t NumProofs>
void aggregator_gadget<wppT, nverifierT, NumProofs>::generate_r1cs_witness(
const typename nsnark::verification_key &in_nested_vk,
const std::array<const libzeth::extended_proof<npp, nsnark> *, NumProofs>
&in_extended_proofs)
const std::array<
const libsnark::r1cs_primary_input<libff::Fr<npp>> *,
NumProofs> &nested_inputs)
{
// Witness the VK
nested_vk->generate_r1cs_witness(in_nested_vk);

for (size_t i = 0; i < NumProofs; i++) {
// Witness the nested_proofs
nested_proofs[i]->generate_r1cs_witness(
in_extended_proofs[i]->get_proof());

// Witness the nested_prinary_inputs. This is done by input values are
// Witness the nested_primary_inputs. This is done by input values are
// of type libff::Fr<nppT>. They are converted to bit arrays to
// populate `nested_primary_inputs_bits`, andn then the
// populate `nested_primary_inputs_bits`, and the
// `nested_primary_input_packers` are used to convert to variables of
// the circuit (elements of libff::Fr<wppT>).
const libsnark::r1cs_primary_input<libff::Fr<npp>>
&other_curve_primary_inputs =
in_extended_proofs[i]->get_primary_inputs();
&other_curve_primary_inputs = *(nested_inputs[i]);
const libff::bit_vector input_bits =
libff::convert_field_element_vector_to_bit_vector<libff::Fr<npp>>(
other_curve_primary_inputs);
Expand Down

0 comments on commit a79220e

Please sign in to comment.