diff --git a/aggregator_server/aggregator_server.cpp b/aggregator_server/aggregator_server.cpp index c0d4bfbe..326f122f 100644 --- a/aggregator_server/aggregator_server.cpp +++ b/aggregator_server/aggregator_server.cpp @@ -67,12 +67,13 @@ using napi_handler = libzeth::groth16_api_handler; #endif using nsnark = typename nverifier::snark; +using hash = libzeth::BLAKE2s_256>; static const size_t batch_size = 2; static const size_t num_inputs_per_nested_proof = 1; -using aggregator_circuit_wrapper = - libzecale::aggregator_circuit_wrapper; +using aggregator_circuit_wrapper = libzecale:: + aggregator_circuit_wrapper; /// The aggregator_server class inherits from the Aggregator service defined in /// the proto files, and provides an implementation of the service. @@ -401,8 +402,9 @@ int main(int argc, char **argv) npp::init_public_params(); wpp::init_public_params(); - libzecale::aggregator_circuit_wrapper - aggregator(num_inputs_per_nested_proof); + libzecale:: + aggregator_circuit_wrapper + aggregator(num_inputs_per_nested_proof); wsnark::keypair keypair = [&keypair_file, &aggregator]() { if (!keypair_file.empty()) { #ifdef ZKSNARK_GROTH16 diff --git a/libzecale/circuits/aggregator_circuit_wrapper.hpp b/libzecale/circuits/aggregator_circuit_wrapper.hpp index 2c62806e..3673a88a 100644 --- a/libzecale/circuits/aggregator_circuit_wrapper.hpp +++ b/libzecale/circuits/aggregator_circuit_wrapper.hpp @@ -7,6 +7,7 @@ #include "libzecale/circuits/aggregator_gadget.hpp" #include "libzecale/circuits/pairing/pairing_params.hpp" +#include "libzecale/circuits/verification_key_hash_gadget.hpp" #include @@ -15,7 +16,26 @@ using namespace libzeth; namespace libzecale { -template +/// Creates a circuit for creating a wrapping proof aggregating a batch of +/// nested proofs. Inputs are allocated as follows: +/// +/// [hash of nested verfication key] +/// +/// [proof 1, input 1] ... [proof 1, input M] +/// [proof 1 result (0 or 1)] +/// +/// ... +/// +/// [proof N, input 1] ... [proof N, input M] +/// [proof N result (0 or 1)] +/// +/// (where N = NumProofs, M = num_inputs_per_nested_proof) +template< + typename wppT, + typename wsnarkT, + typename nverifierT, + typename hashT, + size_t NumProofs> class aggregator_circuit_wrapper { private: @@ -29,6 +49,11 @@ class aggregator_circuit_wrapper libsnark::protoboard> _pb; + /// (Primary) Variable holding the hash of the verification key for nested + /// proofs. Verified against the actual verification key values, by the + /// _nested_vk_hash_gadget. + libsnark::pb_variable> _nested_vk_hash; + /// (Primary) The nested primary inputs lie in the scalar field /// `libff::Fr`, and must be represented as elements of /// `libff::Fr` for use in the wrapper proof. @@ -54,6 +79,11 @@ class aggregator_circuit_wrapper std::array, NumProofs> _nested_proofs; + /// Gadget to check the hash of the nested verification key. + std::shared_ptr> + _nested_vk_hash_gadget; + + /// Gadget to aggregate proofs. std::shared_ptr> _aggregator_gadget; diff --git a/libzecale/circuits/aggregator_circuit_wrapper.tcc b/libzecale/circuits/aggregator_circuit_wrapper.tcc index d69571e5..07d355ec 100644 --- a/libzecale/circuits/aggregator_circuit_wrapper.tcc +++ b/libzecale/circuits/aggregator_circuit_wrapper.tcc @@ -14,14 +14,22 @@ using namespace libzeth; namespace libzecale { -template -aggregator_circuit_wrapper:: +template< + typename wppT, + typename wsnarkT, + typename nverifierT, + typename hashT, + size_t NumProofs> +aggregator_circuit_wrapper:: aggregator_circuit_wrapper(const size_t inputs_per_nested_proof) : _num_inputs_per_nested_proof(inputs_per_nested_proof), _pb() { // The order of allocation here is important as it determines which inputs // are primary. + // Input for hash of nested verification key. + _nested_vk_hash.allocate(_pb, FMT("", "_nested_vk_hash")); + // For each proof in a batch, allocate primary inputs and results. These // are the primary inputs. Note: both inputs and results will be // populated by the aggregator gadget. @@ -37,7 +45,7 @@ aggregator_circuit_wrapper:: // Set the number of primary inputs. const size_t total_primary_inputs = - NumProofs * (inputs_per_nested_proof + 1); + 1 + NumProofs * (inputs_per_nested_proof + 1); _pb.set_input_sizes(total_primary_inputs); // Allocate vk and the intermediate bit representation @@ -55,6 +63,15 @@ aggregator_circuit_wrapper:: new proof_variable_gadget(_pb, FMT("", "_nested_proofs[%zu]", i))); } + // Nested verification key hash gadget + _nested_vk_hash_gadget.reset( + new verification_key_hash_gadget( + _pb, + *_nested_vk, + _nested_vk_hash, + FMT("", "_nested_vk_hash_gadget"))); + + // Aggregator gadget _aggregator_gadget.reset(new aggregator_gadget( _pb, *_nested_vk, @@ -68,33 +85,51 @@ aggregator_circuit_wrapper:: for (size_t i = 0; i < NumProofs; ++i) { _nested_proofs[i]->generate_r1cs_constraints(); } + _nested_vk_hash_gadget->generate_r1cs_constraints(); _aggregator_gadget->generate_r1cs_constraints(); } -template +template< + typename wppT, + typename wsnarkT, + typename nverifierT, + typename hashT, + size_t NumProofs> typename wsnarkT::keypair aggregator_circuit_wrapper< wppT, wsnarkT, nverifierT, + hashT, NumProofs>::generate_trusted_setup() const { // Generate a verification and proving key (trusted setup) return wsnarkT::generate_setup(_pb); } -template +template< + typename wppT, + typename wsnarkT, + typename nverifierT, + typename hashT, + size_t NumProofs> const libsnark::protoboard> - &aggregator_circuit_wrapper:: + &aggregator_circuit_wrapper:: get_constraint_system() const { return _pb; } -template +template< + typename wppT, + typename wsnarkT, + typename nverifierT, + typename hashT, + size_t NumProofs> libzeth::extended_proof aggregator_circuit_wrapper< wppT, wsnarkT, nverifierT, + hashT, NumProofs>:: prove( const typename nsnark::verification_key &nested_vk, @@ -121,6 +156,9 @@ libzeth::extended_proof aggregator_circuit_wrapper< // Witness the verification key _nested_vk->generate_r1cs_witness(nested_vk); + // Witness hash of verification keypair + _nested_vk_hash_gadget->generate_r1cs_witness(); + // Pass the input values (in npp) to the aggregator gadget. _aggregator_gadget->generate_r1cs_witness(nested_inputs); diff --git a/libzecale/tests/aggregator/aggregator_dummy_test.cpp b/libzecale/tests/aggregator/aggregator_dummy_test.cpp index 2ca06617..577db827 100644 --- a/libzecale/tests/aggregator/aggregator_dummy_test.cpp +++ b/libzecale/tests/aggregator/aggregator_dummy_test.cpp @@ -10,9 +10,12 @@ #include "libzecale/tests/circuits/dummy_application.hpp" #include +#include using namespace libzecale; +template using hash = libzeth::BLAKE2s_256>; + namespace { @@ -45,14 +48,19 @@ template< typename nverifierT, size_t batch_size> void test_aggregator_with_batch( + const size_t num_inputs_per_nested_proof, const typename nverifierT::snark::keypair &nkp, const proof_batch< libzecale::other_curve, typename nverifierT::snark, batch_size> &batch, const typename wsnarkT::keypair &wkeypair, - aggregator_circuit_wrapper - &aggregator, + aggregator_circuit_wrapper< + wppT, + wsnarkT, + nverifierT, + hash, + batch_size> &aggregator, const std::array, batch_size> &expected_results) { using npp = libzecale::other_curve; @@ -70,6 +78,12 @@ void test_aggregator_with_batch( wpf.get_primary_inputs(); size_t winput_idx = 0; + // Check the nested vk hash + libff::Fr expect_nested_vk_hash = + verification_key_hash_gadget>:: + compute_hash(nkp.vk, num_inputs_per_nested_proof); + ASSERT_EQ(expect_nested_vk_hash, winputs[winput_idx++]); + for (size_t proof_idx = 0; proof_idx < batch_size; ++proof_idx) { // Check that each input from the batch appears as expected in the // nested primary input list. @@ -111,13 +125,19 @@ void test_aggregate_dummy_application() npf2.write_json(std::cout); // Wrapper keypair - aggregator_circuit_wrapper + aggregator_circuit_wrapper< + wppT, + wsnarkT, + nverifierT, + hash, + batch_size> aggregator(public_inputs_per_proof); const typename wsnarkT::keypair wkeypair = aggregator.generate_trusted_setup(); // Create and check a batched proof. test_aggregator_with_batch( + public_inputs_per_proof, nkp, {{&npf1, &npf2}}, wkeypair, @@ -159,13 +179,19 @@ void test_aggregate_dummy_application_with_invalid_proof() npf2_invalid.write_json(std::cout); // Wrapper keypair - aggregator_circuit_wrapper + aggregator_circuit_wrapper< + wppT, + wsnarkT, + nverifierT, + hash, + batch_size> aggregator(public_inputs_per_proof); const typename wsnarkT::keypair wkeypair = aggregator.generate_trusted_setup(); // Create and check a batched proof test_aggregator_with_batch( + public_inputs_per_proof, nkp, {{&npf1, &npf2_invalid}}, wkeypair, diff --git a/libzecale/tests/aggregator/aggregator_test.cpp b/libzecale/tests/aggregator/aggregator_test.cpp index b1545725..6e24ee77 100644 --- a/libzecale/tests/aggregator/aggregator_test.cpp +++ b/libzecale/tests/aggregator/aggregator_test.cpp @@ -196,8 +196,12 @@ libzeth::extended_proof generate_valid_zeth_proof( /// We use the same SNARK for simplicity. template bool test_valid_aggregation_batch_proofs( - aggregator_circuit_wrapper - &aggregator_prover, + aggregator_circuit_wrapper< + wppT, + wsnarkT, + nverifierT, + hash, + batch_size> &aggregator_prover, typename wsnarkT::keypair &aggregator_keypair, typename nverifierT::snark::keypair &zeth_keypair, const std::array< @@ -206,9 +210,6 @@ bool test_valid_aggregation_batch_proofs( typename nverifierT::snark> *, batch_size> &nested_proofs) { - using npp = libzecale::other_curve; - using nsnark = typename nverifierT::snark; - libff::enter_block("Generate Aggregate proof", true); libzeth::extended_proof ext_proof = aggregator_prover.prove( // This should cause a crash because the primary inputs are @@ -277,7 +278,12 @@ void aggregator_test() std::cout << "[DEBUG] Before creation of the Aggregator prover" << std::endl; - aggregator_circuit_wrapper + aggregator_circuit_wrapper< + wppT, + wsnarkT, + nverifierT, + hash, + batch_size> aggregator_prover(num_zeth_inputs); std::cout << "[DEBUG] Before gen Aggregator setup" << std::endl; typename wsnarkT::keypair aggregator_keypair =