From 69eba2d0cd4df1e1cf9446be0c40b6f74900e74a Mon Sep 17 00:00:00 2001 From: zac-williamson Date: Thu, 13 Apr 2023 17:29:41 +0200 Subject: [PATCH 01/41] Added recursion constraint into dsl + tests --- .../dsl/acir_format/acir_format.cpp | 23 +++ .../dsl/acir_format/acir_format.hpp | 3 + .../dsl/acir_format/recursion_constraint.cpp | 77 ++++++++ .../dsl/acir_format/recursion_constraint.hpp | 64 +++++++ .../acir_format/recursion_constraint.test.cpp | 172 ++++++++++++++++++ 5 files changed, 339 insertions(+) create mode 100644 cpp/src/barretenberg/dsl/acir_format/recursion_constraint.cpp create mode 100644 cpp/src/barretenberg/dsl/acir_format/recursion_constraint.hpp create mode 100644 cpp/src/barretenberg/dsl/acir_format/recursion_constraint.test.cpp diff --git a/cpp/src/barretenberg/dsl/acir_format/acir_format.cpp b/cpp/src/barretenberg/dsl/acir_format/acir_format.cpp index c702ec980a..2b7b9526b6 100644 --- a/cpp/src/barretenberg/dsl/acir_format/acir_format.cpp +++ b/cpp/src/barretenberg/dsl/acir_format/acir_format.cpp @@ -86,6 +86,11 @@ void create_circuit(Composer& composer, const acir_format& constraint_system) for (const auto& constraint : constraint_system.hash_to_field_constraints) { create_hash_to_field_constraints(composer, constraint); } + + // Add recursion constraints + for (const auto& constraint : constraint_system.recursion_constraints) { + create_recursion_constraints(composer, constraint); + } } Composer create_circuit(const acir_format& constraint_system, @@ -165,6 +170,11 @@ Composer create_circuit(const acir_format& constraint_system, create_hash_to_field_constraints(composer, constraint); } + // Add recursion constraints + for (const auto& constraint : constraint_system.recursion_constraints) { + create_recursion_constraints(composer, constraint); + } + return composer; } @@ -249,6 +259,10 @@ Composer create_circuit_with_witness(const acir_format& constraint_system, create_hash_to_field_constraints(composer, constraint); } + // Add recursion constraints + for (const auto& constraint : constraint_system.recursion_constraints) { + create_recursion_constraints(composer, constraint); + } return composer; } Composer create_circuit_with_witness(const acir_format& constraint_system, std::vector witness) @@ -330,6 +344,10 @@ Composer create_circuit_with_witness(const acir_format& constraint_system, std:: create_hash_to_field_constraints(composer, constraint); } + // Add recursion constraints + for (const auto& constraint : constraint_system.recursion_constraints) { + create_recursion_constraints(composer, constraint); + } return composer; } void create_circuit_with_witness(Composer& composer, const acir_format& constraint_system, std::vector witness) @@ -408,6 +426,11 @@ void create_circuit_with_witness(Composer& composer, const acir_format& constrai for (const auto& constraint : constraint_system.hash_to_field_constraints) { create_hash_to_field_constraints(composer, constraint); } + + // Add recursion constraints + for (const auto& constraint : constraint_system.recursion_constraints) { + create_recursion_constraints(composer, constraint); + } } } // namespace acir_format diff --git a/cpp/src/barretenberg/dsl/acir_format/acir_format.hpp b/cpp/src/barretenberg/dsl/acir_format/acir_format.hpp index 55d5ee969e..12592c6a32 100644 --- a/cpp/src/barretenberg/dsl/acir_format/acir_format.hpp +++ b/cpp/src/barretenberg/dsl/acir_format/acir_format.hpp @@ -7,6 +7,7 @@ #include "schnorr_verify.hpp" #include "ecdsa_secp256k1.hpp" #include "merkle_membership_constraint.hpp" +#include "recursion_constraint.hpp" #include "pedersen.hpp" #include "hash_to_field.hpp" #include "barretenberg/stdlib/types/types.hpp" @@ -29,6 +30,8 @@ struct acir_format { std::vector hash_to_field_constraints; std::vector pedersen_constraints; std::vector merkle_membership_constraints; + std::vector recursion_constraints; + // A standard plonk arithmetic constraint, as defined in the poly_triple struct, consists of selector values // for q_M,q_L,q_R,q_O,q_C and indices of three variables taking the role of left, right and output wire std::vector constraints; diff --git a/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.cpp b/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.cpp new file mode 100644 index 0000000000..b6abe2e5fc --- /dev/null +++ b/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.cpp @@ -0,0 +1,77 @@ +#include "recursion_constraint.hpp" +#include "barretenberg/plonk/proof_system/verification_key/verification_key.hpp" +#include "barretenberg/stdlib/recursion/aggregation_state/aggregation_state.hpp" +#include "barretenberg/stdlib/recursion/verifier/verifier.hpp" + +using namespace proof_system::plonk::stdlib::types; + +using verification_key_ct = proof_system::plonk::stdlib::recursion::verification_key; +using aggregation_state_ct = proof_system::plonk::stdlib::recursion::aggregation_state; +using noir_recursive_settings = proof_system::plonk::stdlib::recursion::recursive_ultra_verifier_settings; +namespace acir_format { + +/** + * @brief Add constraints required to recursively verify an UltraPlonk proof + * + * @param composer + * @param input + */ +void create_recursion_constraints(Composer& composer, const RecursionConstraint& input) +{ + // The env_crs should be ok, we use this when generating the constraint system in dsl + auto env_crs = std::make_unique(); + + // Construct an in-circuit representation of the verification key. + // For now, the v-key is a circuit constant and is fixed for the circuit. + // (We may need a separate recursion opcode for this to vary, or add more config witnesses to this opcode) + auto data_clone = input.verification_key_data; + std::shared_ptr key = + std::make_shared(std::move(data_clone), env_crs->get_verifier_crs()); + std::shared_ptr circuit_key = verification_key_ct::from_constants(&composer, key); + const auto& aggregation_input = input.input_aggregation_object; + aggregation_state_ct previous_aggregation; + + // If we have previously recursively verified proofs, `is_aggregation_object_nonzero = true` + // For now this is a complile-time constant i.e. whether this is true/false is fixed for the circuit! + if (input.is_aggregation_object_nonzero) { + std::array aggregation_elements; + for (size_t i = 0; i < 4; ++i) { + aggregation_elements[i] = + bn254::fq_ct(field_ct::from_witness_index(&composer, aggregation_input[4 * i]), + field_ct::from_witness_index(&composer, aggregation_input[4 * i + 1]), + field_ct::from_witness_index(&composer, aggregation_input[4 * i + 2]), + field_ct::from_witness_index(&composer, aggregation_input[4 * i + 3])); + aggregation_elements[i].assert_is_in_field(); + } + // If we have a previous aggregation object, assign it to `previous_aggregation` so that it is included + // in stdlib::recursion::verify_proof + previous_aggregation.P0 = bn254::g1_ct(aggregation_elements[0], aggregation_elements[1]); + previous_aggregation.P1 = bn254::g1_ct(aggregation_elements[2], aggregation_elements[3]); + previous_aggregation.has_data = true; + } else { + previous_aggregation.has_data = false; + } + + transcript::Manifest manifest = Composer::create_unrolled_manifest(1); + + // recursively verify the proof + aggregation_state_ct result = proof_system::plonk::stdlib::recursion::verify_proof( + &composer, circuit_key, manifest, input.proof, previous_aggregation); + + // Assign the output aggregation object to the proof public inputs (16 field elements representing two G1 points) + result.add_proof_outputs_as_public_inputs(); + + ASSERT(result.public_inputs.size() == 1); + + // Assign the `public_input` field to the public input of the inner proof + result.public_inputs[0].assert_equal(field_ct::from_witness_index(&composer, input.public_input)); + + // Assign the recursive proof outputs to `output_aggregation_object` + for (size_t i = 0; i < result.proof_witness_indices.size(); ++i) { + const auto lhs = field_ct::from_witness_index(&composer, result.proof_witness_indices[i]); + const auto rhs = field_ct::from_witness_index(&composer, input.output_aggregation_object[i]); + lhs.assert_equal(rhs); + } +} + +} // namespace acir_format diff --git a/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.hpp b/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.hpp new file mode 100644 index 0000000000..c67dacb10f --- /dev/null +++ b/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.hpp @@ -0,0 +1,64 @@ +#pragma once +#include +#include "barretenberg/stdlib/types/types.hpp" +#include "barretenberg/plonk/proof_system//verification_key/verification_key.hpp" + +namespace acir_format { + +/** + * @brief RecursionConstraint struct contains information required to recursively verify a proof! + * + * @details The recursive verifier algorithm produces an 'aggregation object' representing 2 G1 points, expressed as 16 + * witness values. The smart contract Verifier must be aware of this aggregation object in order to complete the full + * recursive verification. If the circuit verifies more than 1 proof, the recursion algorithm will update a pre-existing + * aggregation object (`input_aggregation_object`). + * + * @details We currently require that the inner circuit being verified only has a single public input. If more are + * required, the outer circuit can hash them down to 1 input. + * + * @param verification_key_data The inner circuit vkey. Is converted into circuit witness values (internal to the + * backend) + * @param proof The plonk proof. Is converted into circuit witness values (internal to the backend) + * @param is_aggregation_object_nonzero A flag to tell us whether the circuit has already recursively verified proofs + * (and therefore an aggregation object is present) + * @param public_input The index of the single public input + * @param input_aggregation_object Witness indices of pre-existing aggregation object (if it exists) + * @param output_aggregation_object Witness indecies of the aggregation object produced by recursive verification + */ +struct RecursionConstraint { + static constexpr size_t AGGREGATION_OBJECT_SIZE = 16; // 16 field elements + plonk::verification_key_data verification_key_data; + plonk::proof proof; + bool is_aggregation_object_nonzero; + uint32_t public_input; + std::array input_aggregation_object; + std::array output_aggregation_object; + + friend bool operator==(RecursionConstraint const& lhs, RecursionConstraint const& rhs) = default; +}; + +void create_recursion_constraints(plonk::stdlib::types::Composer& composer, const RecursionConstraint& input); + +template inline void read(B& buf, RecursionConstraint& constraint) +{ + using serialize::read; + read(buf, constraint.verification_key_data); + read(buf, constraint.proof); + read(buf, constraint.is_aggregation_object_nonzero); + read(buf, constraint.public_input); + read(buf, constraint.input_aggregation_object); + read(buf, constraint.output_aggregation_object); +} + +template inline void write(B& buf, RecursionConstraint const& constraint) +{ + using serialize::write; + write(buf, constraint.verification_key_data); + write(buf, constraint.proof); + write(buf, constraint.is_aggregation_object_nonzero); + write(buf, constraint.public_input); + write(buf, constraint.input_aggregation_object); + write(buf, constraint.output_aggregation_object); +} + +} // namespace acir_format diff --git a/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.test.cpp b/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.test.cpp new file mode 100644 index 0000000000..911e36c8eb --- /dev/null +++ b/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.test.cpp @@ -0,0 +1,172 @@ +#include "acir_format.hpp" +#include "recursion_constraint.hpp" +#include "barretenberg/plonk/proof_system/types/proof.hpp" +#include "barretenberg/plonk/proof_system/verification_key/verification_key.hpp" + +#include +#include + +using namespace proof_system::plonk::stdlib::types; + +Composer create_inner_circuit() +{ + /** + * constraints produced by Noir program: + * fn main(x : u32, y : pub u32) { + * let z = x ^ y; + * + * constrain z != 10; + * } + **/ + acir_format::RangeConstraint range_a{ + .witness = 1, + .num_bits = 32, + }; + acir_format::RangeConstraint range_b{ + .witness = 2, + .num_bits = 32, + }; + + acir_format::LogicConstraint logic_constraint{ + .a = 1, + .b = 2, + .result = 3, + .num_bits = 32, + .is_xor_gate = 1, + }; + poly_triple expr_a{ + .a = 3, + .b = 4, + .c = 0, + .q_m = 0, + .q_l = 1, + .q_r = -1, + .q_o = 0, + .q_c = -10, + }; + poly_triple expr_b{ + .a = 4, + .b = 5, + .c = 6, + .q_m = 1, + .q_l = 0, + .q_r = 0, + .q_o = -1, + .q_c = 0, + }; + poly_triple expr_c{ + .a = 4, + .b = 6, + .c = 4, + .q_m = 1, + .q_l = 0, + .q_r = 0, + .q_o = -1, + .q_c = 0, + + }; + poly_triple expr_d{ + .a = 6, + .b = 0, + .c = 0, + .q_m = 0, + .q_l = -1, + .q_r = 0, + .q_o = 0, + .q_c = 1, + }; + // EXPR [ (1, _4, _5) (-1, _6) 0 ] + // EXPR [ (1, _4, _6) (-1, _4) 0 ] + // EXPR [ (-1, _6) 1 ] + + acir_format::acir_format constraint_system{ + .varnum = 7, + .public_inputs = { 2 }, + .fixed_base_scalar_mul_constraints = {}, + .logic_constraints = { logic_constraint }, + .range_constraints = { range_a, range_b }, + .schnorr_constraints = {}, + .ecdsa_constraints = {}, + .sha256_constraints = {}, + .blake2s_constraints = {}, + .hash_to_field_constraints = {}, + .pedersen_constraints = {}, + .merkle_membership_constraints = {}, + .constraints = { expr_a, expr_b, expr_c, expr_d }, + }; + + uint256_t inverse_of_five = fr(5).invert(); + auto composer = acir_format::create_circuit_with_witness(constraint_system, + { + 5, + 10, + 15, + 5, + inverse_of_five, + 1, + }); + + return composer; +} + +TEST(RecursionConstraint, TestRecursionConstraint) +{ + auto inner_composer = create_inner_circuit(); + + auto inner_prover = inner_composer.create_prover(); + auto inner_proof = inner_prover.construct_proof(); + auto inner_verifier = inner_composer.create_verifier(); + + std::vector keybuf; + write(keybuf, *(inner_verifier.key)); + + std::array output_vars; + for (size_t i = 0; i < 16; ++i) { + // variable idx 1 = public input + // variable idx 2-18 = output_vars + output_vars[i] = (static_cast(i + 2)); + } + verification_key_data keydata; + uint8_t const* vk_buf = &keybuf[0]; + read(vk_buf, keydata); + + acir_format::RecursionConstraint recursion_constraint{ + .verification_key_data = keydata, + .proof = inner_proof, + .is_aggregation_object_nonzero = false, + .public_input = 1, + .input_aggregation_object = {}, + .output_aggregation_object = output_vars, + }; + + std::vector witness; + for (size_t i = 0; i < 18; ++i) { + witness.emplace_back(0); + } + + acir_format::acir_format constraint_system{ + .varnum = 18, + .public_inputs = { 1 }, + .fixed_base_scalar_mul_constraints = {}, + .logic_constraints = {}, + .range_constraints = {}, + .schnorr_constraints = {}, + .ecdsa_constraints = {}, + .sha256_constraints = {}, + .blake2s_constraints = {}, + .hash_to_field_constraints = {}, + .pedersen_constraints = {}, + .merkle_membership_constraints = {}, + .recursion_constraints = { recursion_constraint }, + .constraints = {}, + }; + + auto composer = acir_format::create_circuit_with_witness(constraint_system, witness); + auto prover = composer.create_prover(); + + auto proof = prover.construct_proof(); + auto verifier = composer.create_verifier(); + EXPECT_EQ(verifier.verify_proof(proof), true); + + EXPECT_EQ(composer.get_variable(1), 10); +} From 7a80056b0e7ae8e150c719295d83f0c1bea7d614 Mon Sep 17 00:00:00 2001 From: zac-williamson Date: Fri, 14 Apr 2023 00:15:35 +0200 Subject: [PATCH 02/41] fix dsl ecdsa constraint. Added ecdsa dsl tests --- .../dsl/acir_format/ecdsa_secp256k1.cpp | 23 ++- .../dsl/acir_format/ecdsa_secp256k1.test.cpp | 145 ++++++++++++++++++ .../stdlib/encryption/ecdsa/ecdsa.hpp | 5 + .../stdlib/encryption/ecdsa/ecdsa.test.cpp | 87 +++++++++++ .../stdlib/encryption/ecdsa/ecdsa_impl.hpp | 87 ++++++++++- 5 files changed, 338 insertions(+), 9 deletions(-) create mode 100644 cpp/src/barretenberg/dsl/acir_format/ecdsa_secp256k1.test.cpp diff --git a/cpp/src/barretenberg/dsl/acir_format/ecdsa_secp256k1.cpp b/cpp/src/barretenberg/dsl/acir_format/ecdsa_secp256k1.cpp index d81a670cb0..291245d3d5 100644 --- a/cpp/src/barretenberg/dsl/acir_format/ecdsa_secp256k1.cpp +++ b/cpp/src/barretenberg/dsl/acir_format/ecdsa_secp256k1.cpp @@ -102,17 +102,24 @@ void create_ecdsa_verify_constraints(Composer& composer, const EcdsaSecp256k1Con pub_key_x_fq.assert_is_in_field(); pub_key_y_fq.assert_is_in_field(); - secp256k1_ct::g1_bigfr_ct public_key = secp256k1_ct::g1_bigfr_ct(pub_key_x_fq, pub_key_y_fq); + for (size_t i = 0; i < 32; ++i) { + sig.r[i].assert_equal(field_ct::from_witness_index(&composer, input.signature[i])); + sig.s[i].assert_equal(field_ct::from_witness_index(&composer, input.signature[i + 32])); + pub_key_x_byte_arr[i].assert_equal(field_ct::from_witness_index(&composer, input.pub_x_indices[i])); + pub_key_y_byte_arr[i].assert_equal(field_ct::from_witness_index(&composer, input.pub_y_indices[i])); + } + for (size_t i = 0; i < input.message.size(); ++i) { + message[i].assert_equal(field_ct::from_witness_index(&composer, input.message[i])); + } - bool_ct signature_result = stdlib::ecdsa::verify_signature(message, public_key, sig); - + bool_ct signature_result = + stdlib::ecdsa::verify_signature_noassert(message, public_key, sig); bool_ct signature_result_normalized = signature_result.normalize(); - composer.assert_equal(signature_result_normalized.witness_index, input.result); } diff --git a/cpp/src/barretenberg/dsl/acir_format/ecdsa_secp256k1.test.cpp b/cpp/src/barretenberg/dsl/acir_format/ecdsa_secp256k1.test.cpp new file mode 100644 index 0000000000..57f9af0a1c --- /dev/null +++ b/cpp/src/barretenberg/dsl/acir_format/ecdsa_secp256k1.test.cpp @@ -0,0 +1,145 @@ +#include "acir_format.hpp" +#include "ecdsa_secp256k1.hpp" +#include "barretenberg/plonk/proof_system/types/proof.hpp" +#include "barretenberg/plonk/proof_system/verification_key/verification_key.hpp" +#include "barretenberg/crypto/ecdsa/ecdsa.hpp" + +#include +#include + +using namespace proof_system::plonk::stdlib::types; +using curve = stdlib::secp256k1; + +size_t generate_ecdsa_constraint(acir_format::EcdsaSecp256k1Constraint& ecdsa_constraint, + std::vector& witness_values) +{ + std::string message_string = "Instructions unclear, ask again later."; + + crypto::ecdsa::key_pair account; + account.private_key = curve::fr::random_element(); + account.public_key = curve::g1::one * account.private_key; + + crypto::ecdsa::signature signature = + crypto::ecdsa::construct_signature(message_string, account); + + uint256_t pub_x_value = account.public_key.x; + uint256_t pub_y_value = account.public_key.y; + + std::vector message_in; + std::vector pub_x_indices_in; + std::vector pub_y_indices_in; + std::vector signature_in; + size_t offset = 1; + for (size_t i = 0; i < message_string.size(); ++i) { + message_in.emplace_back(i + offset); + const auto byte = static_cast(message_string[i]); + witness_values.emplace_back(byte); + } + offset += message_in.size(); + + for (size_t i = 0; i < 32; ++i) { + pub_x_indices_in.emplace_back(i + offset); + witness_values.emplace_back(pub_x_value.slice(248 - i * 8, 256 - i * 8)); + } + offset += pub_x_indices_in.size(); + for (size_t i = 0; i < 32; ++i) { + pub_y_indices_in.emplace_back(i + offset); + witness_values.emplace_back(pub_y_value.slice(248 - i * 8, 256 - i * 8)); + } + offset += pub_y_indices_in.size(); + for (size_t i = 0; i < 32; ++i) { + signature_in.emplace_back(i + offset); + witness_values.emplace_back(signature.r[i]); + } + offset += signature.r.size(); + for (size_t i = 0; i < 32; ++i) { + signature_in.emplace_back(i + offset); + witness_values.emplace_back(signature.s[i]); + } + offset += signature.s.size(); + + witness_values.emplace_back(1); + const auto result_in = static_cast(offset); + offset += 1; + witness_values.emplace_back(1); + + ecdsa_constraint = acir_format::EcdsaSecp256k1Constraint{ + .message = message_in, + .pub_x_indices = pub_x_indices_in, + .pub_y_indices = pub_y_indices_in, + .result = result_in, + .signature = signature_in, + }; + return offset; +} + +TEST(ECDSASecp256k1, TestECDSAConstraintSucceed) +{ + acir_format::EcdsaSecp256k1Constraint ecdsa_constraint; + std::vector witness_values; + size_t num_variables = generate_ecdsa_constraint(ecdsa_constraint, witness_values); + acir_format::acir_format constraint_system{ + .varnum = static_cast(num_variables), + .public_inputs = {}, + .fixed_base_scalar_mul_constraints = {}, + .logic_constraints = {}, + .range_constraints = {}, + .schnorr_constraints = {}, + .ecdsa_constraints = { ecdsa_constraint }, + .sha256_constraints = {}, + .blake2s_constraints = {}, + .hash_to_field_constraints = {}, + .pedersen_constraints = {}, + .merkle_membership_constraints = {}, + .recursion_constraints = {}, + .constraints = {}, + }; + + auto composer = acir_format::create_circuit_with_witness(constraint_system, witness_values); + + EXPECT_EQ(composer.get_variable(ecdsa_constraint.result), 1); + auto prover = composer.create_prover(); + + auto proof = prover.construct_proof(); + auto verifier = composer.create_verifier(); + EXPECT_EQ(verifier.verify_proof(proof), true); +} + +TEST(ECDSASecp256k1, TestECDSAConstraintFail) +{ + acir_format::EcdsaSecp256k1Constraint ecdsa_constraint; + std::vector witness_values; + size_t num_variables = generate_ecdsa_constraint(ecdsa_constraint, witness_values); + + // set result value to be false + witness_values[witness_values.size() - 1] = 0; + + // tamper with signature + witness_values[witness_values.size() - 20] += 1; + + acir_format::acir_format constraint_system{ + .varnum = static_cast(num_variables), + .public_inputs = {}, + .fixed_base_scalar_mul_constraints = {}, + .logic_constraints = {}, + .range_constraints = {}, + .schnorr_constraints = {}, + .ecdsa_constraints = { ecdsa_constraint }, + .sha256_constraints = {}, + .blake2s_constraints = {}, + .hash_to_field_constraints = {}, + .pedersen_constraints = {}, + .merkle_membership_constraints = {}, + .recursion_constraints = {}, + .constraints = {}, + }; + + auto composer = acir_format::create_circuit_with_witness(constraint_system, witness_values); + + EXPECT_EQ(composer.get_variable(ecdsa_constraint.result), 0); + auto prover = composer.create_prover(); + + auto proof = prover.construct_proof(); + auto verifier = composer.create_verifier(); + EXPECT_EQ(verifier.verify_proof(proof), true); +} diff --git a/cpp/src/barretenberg/stdlib/encryption/ecdsa/ecdsa.hpp b/cpp/src/barretenberg/stdlib/encryption/ecdsa/ecdsa.hpp index 1864557af4..e4fa1b217a 100644 --- a/cpp/src/barretenberg/stdlib/encryption/ecdsa/ecdsa.hpp +++ b/cpp/src/barretenberg/stdlib/encryption/ecdsa/ecdsa.hpp @@ -18,6 +18,11 @@ bool_t verify_signature(const stdlib::byte_array& message, const G1& public_key, const signature& sig); +template +bool_t verify_signature_noassert(const stdlib::byte_array& message, + const G1& public_key, + const signature& sig); + template static signature from_witness(Composer* ctx, const crypto::ecdsa::signature& input) { diff --git a/cpp/src/barretenberg/stdlib/encryption/ecdsa/ecdsa.test.cpp b/cpp/src/barretenberg/stdlib/encryption/ecdsa/ecdsa.test.cpp index b1edc24d84..91101df01e 100644 --- a/cpp/src/barretenberg/stdlib/encryption/ecdsa/ecdsa.test.cpp +++ b/cpp/src/barretenberg/stdlib/encryption/ecdsa/ecdsa.test.cpp @@ -54,4 +54,91 @@ TEST(stdlib_ecdsa, verify_signature) bool proof_result = verifier.verify_proof(proof); EXPECT_EQ(proof_result, true); } + +TEST(stdlib_ecdsa, verify_signature_noassert_succeed) +{ + Composer composer = Composer(); + + // whaaablaghaaglerijgeriij + std::string message_string = "Instructions unclear, ask again later."; + + crypto::ecdsa::key_pair account; + account.private_key = curve::fr::random_element(); + account.public_key = curve::g1::one * account.private_key; + + crypto::ecdsa::signature signature = + crypto::ecdsa::construct_signature(message_string, account); + + bool first_result = crypto::ecdsa::verify_signature( + message_string, account.public_key, signature); + EXPECT_EQ(first_result, true); + + curve::g1_bigfr_ct public_key = curve::g1_bigfr_ct::from_witness(&composer, account.public_key); + + std::vector rr(signature.r.begin(), signature.r.end()); + std::vector ss(signature.s.begin(), signature.s.end()); + + stdlib::ecdsa::signature sig{ curve::byte_array_ct(&composer, rr), curve::byte_array_ct(&composer, ss) }; + + curve::byte_array_ct message(&composer, message_string); + + curve::bool_ct signature_result = + stdlib::ecdsa::verify_signature_noassert( + message, public_key, sig); + + EXPECT_EQ(signature_result.get_value(), true); + + std::cerr << "composer gates = " << composer.get_num_gates() << std::endl; + benchmark_info("UltraComposer", "ECDSA", "Signature Verification Test", "Gate Count", composer.get_num_gates()); + auto prover = composer.create_prover(); + auto verifier = composer.create_verifier(); + auto proof = prover.construct_proof(); + bool proof_result = verifier.verify_proof(proof); + EXPECT_EQ(proof_result, true); +} + +TEST(stdlib_ecdsa, verify_signature_noassert_fail) +{ + Composer composer = Composer(); + + // whaaablaghaaglerijgeriij + std::string message_string = "Instructions unclear, ask again later."; + + crypto::ecdsa::key_pair account; + account.private_key = curve::fr::random_element(); + account.public_key = curve::g1::one * account.private_key; + + crypto::ecdsa::signature signature = + crypto::ecdsa::construct_signature(message_string, account); + + // tamper w. signature to make fail + signature.r[0] += 1; + + bool first_result = crypto::ecdsa::verify_signature( + message_string, account.public_key, signature); + EXPECT_EQ(first_result, false); + + curve::g1_bigfr_ct public_key = curve::g1_bigfr_ct::from_witness(&composer, account.public_key); + + std::vector rr(signature.r.begin(), signature.r.end()); + std::vector ss(signature.s.begin(), signature.s.end()); + + stdlib::ecdsa::signature sig{ curve::byte_array_ct(&composer, rr), curve::byte_array_ct(&composer, ss) }; + + curve::byte_array_ct message(&composer, message_string); + + curve::bool_ct signature_result = + stdlib::ecdsa::verify_signature_noassert( + message, public_key, sig); + + EXPECT_EQ(signature_result.get_value(), false); + + std::cerr << "composer gates = " << composer.get_num_gates() << std::endl; + benchmark_info("UltraComposer", "ECDSA", "Signature Verification Test", "Gate Count", composer.get_num_gates()); + auto prover = composer.create_prover(); + auto verifier = composer.create_verifier(); + auto proof = prover.construct_proof(); + bool proof_result = verifier.verify_proof(proof); + EXPECT_EQ(proof_result, true); +} } // namespace test_stdlib_ecdsa diff --git a/cpp/src/barretenberg/stdlib/encryption/ecdsa/ecdsa_impl.hpp b/cpp/src/barretenberg/stdlib/encryption/ecdsa/ecdsa_impl.hpp index 12355453d6..411afa2c1d 100644 --- a/cpp/src/barretenberg/stdlib/encryption/ecdsa/ecdsa_impl.hpp +++ b/cpp/src/barretenberg/stdlib/encryption/ecdsa/ecdsa_impl.hpp @@ -6,6 +6,19 @@ namespace proof_system::plonk { namespace stdlib { namespace ecdsa { +/** + * @brief Verify ECDSA signature. Produces unsatisfiable constraints if signature fails + * + * @tparam Composer + * @tparam Curve + * @tparam Fq + * @tparam Fr + * @tparam G1 + * @param message + * @param public_key + * @param sig + * @return bool_t + */ template bool_t verify_signature(const stdlib::byte_array& message, const G1& public_key, @@ -62,10 +75,82 @@ bool_t verify_signature(const stdlib::byte_array& message, result_mod_r.binary_basis_limbs[2].element.assert_equal(r.binary_basis_limbs[2].element); result_mod_r.binary_basis_limbs[3].element.assert_equal(r.binary_basis_limbs[3].element); result_mod_r.prime_basis_limb.assert_equal(r.prime_basis_limb); - return bool_t(ctx, true); } +/** + * @brief Verify ECDSA signature. Returns 0 if signature fails (i.e. does not produce unsatisfiable constraints) + * + * @tparam Composer + * @tparam Curve + * @tparam Fq + * @tparam Fr + * @tparam G1 + * @param message + * @param public_key + * @param sig + * @return bool_t + */ +template +bool_t verify_signature_noassert(const stdlib::byte_array& message, + const G1& public_key, + const signature& sig) +{ + Composer* ctx = message.get_context() ? message.get_context() : public_key.x.context; + + stdlib::byte_array hashed_message = + static_cast>(stdlib::sha256(message)); + + Fr z(hashed_message); + z.assert_is_in_field(); + + Fr r(sig.r); + // force r to be < secp256k1 group modulus, so we can compare with `result_mod_r` below + r.assert_is_in_field(); + + Fr s(sig.s); + + // r and s should not be zero + r.assert_is_not_equal(Fr::zero()); + s.assert_is_not_equal(Fr::zero()); + + Fr u1 = z / s; + Fr u2 = r / s; + + G1 result; + if constexpr (Composer::type == ComposerType::PLOOKUP) { + ASSERT(Curve::type == proof_system::CurveType::SECP256K1); + public_key.validate_on_curve(); + result = G1::secp256k1_ecdsa_mul(public_key, u1, u2); + } else { + result = G1::batch_mul({ G1::one(ctx), public_key }, { u1, u2 }); + } + result.x.self_reduce(); + + // transfer Fq value x to an Fr element and reduce mod r + Fr result_mod_r(ctx, 0); + result_mod_r.binary_basis_limbs[0].element = result.x.binary_basis_limbs[0].element; + result_mod_r.binary_basis_limbs[1].element = result.x.binary_basis_limbs[1].element; + result_mod_r.binary_basis_limbs[2].element = result.x.binary_basis_limbs[2].element; + result_mod_r.binary_basis_limbs[3].element = result.x.binary_basis_limbs[3].element; + result_mod_r.binary_basis_limbs[0].maximum_value = result.x.binary_basis_limbs[0].maximum_value; + result_mod_r.binary_basis_limbs[1].maximum_value = result.x.binary_basis_limbs[1].maximum_value; + result_mod_r.binary_basis_limbs[2].maximum_value = result.x.binary_basis_limbs[2].maximum_value; + result_mod_r.binary_basis_limbs[3].maximum_value = result.x.binary_basis_limbs[3].maximum_value; + + result_mod_r.prime_basis_limb = result.x.prime_basis_limb; + + result_mod_r.assert_is_in_field(); + + bool_t output(ctx, true); + output &= result_mod_r.binary_basis_limbs[0].element == (r.binary_basis_limbs[0].element); + output &= result_mod_r.binary_basis_limbs[1].element == (r.binary_basis_limbs[1].element); + output &= result_mod_r.binary_basis_limbs[2].element == (r.binary_basis_limbs[2].element); + output &= result_mod_r.binary_basis_limbs[3].element == (r.binary_basis_limbs[3].element); + output &= result_mod_r.prime_basis_limb == (r.prime_basis_limb); + return output; +} + } // namespace ecdsa } // namespace stdlib } // namespace proof_system::plonk \ No newline at end of file From 2436dcf59516a1e49286ba602236dbe5016c8b67 Mon Sep 17 00:00:00 2001 From: zac-williamson Date: Sun, 16 Apr 2023 11:15:25 +0200 Subject: [PATCH 03/41] changed dsl recursion to pass proof/key as witnesses --- .../dsl/acir_format/recursion_constraint.cpp | 16 +++-- .../dsl/acir_format/recursion_constraint.hpp | 8 +-- .../acir_format/recursion_constraint.test.cpp | 42 ++++++++++--- .../dsl/acir_proofs/acir_proofs.cpp | 12 ++++ .../verification_key/verification_key.cpp | 60 +++++++++++++++++++ .../verification_key/verification_key.hpp | 5 ++ .../recursion/transcript/transcript.hpp | 48 +++++++++++++++ .../verification_key/verification_key.hpp | 57 ++++++++++++++++++ .../stdlib/recursion/verifier/verifier.hpp | 43 ++++++++++++- .../transcript/transcript_wrappers.hpp | 40 +++++++++++++ 10 files changed, 312 insertions(+), 19 deletions(-) diff --git a/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.cpp b/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.cpp index b6abe2e5fc..373ee3c15a 100644 --- a/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.cpp +++ b/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.cpp @@ -24,10 +24,6 @@ void create_recursion_constraints(Composer& composer, const RecursionConstraint& // Construct an in-circuit representation of the verification key. // For now, the v-key is a circuit constant and is fixed for the circuit. // (We may need a separate recursion opcode for this to vary, or add more config witnesses to this opcode) - auto data_clone = input.verification_key_data; - std::shared_ptr key = - std::make_shared(std::move(data_clone), env_crs->get_verifier_crs()); - std::shared_ptr circuit_key = verification_key_ct::from_constants(&composer, key); const auto& aggregation_input = input.input_aggregation_object; aggregation_state_ct previous_aggregation; @@ -53,10 +49,20 @@ void create_recursion_constraints(Composer& composer, const RecursionConstraint& } transcript::Manifest manifest = Composer::create_unrolled_manifest(1); + std::vector key_fields; + key_fields.reserve(input.key.size()); + for (const auto& idx : input.key) { + key_fields.emplace_back(field_ct::from_witness_index(&composer, idx)); + } + std::vector proof_fields; + proof_fields.reserve(input.proof.size()); + for (const auto& idx : input.proof) { + proof_fields.emplace_back(field_ct::from_witness_index(&composer, idx)); + } // recursively verify the proof aggregation_state_ct result = proof_system::plonk::stdlib::recursion::verify_proof( - &composer, circuit_key, manifest, input.proof, previous_aggregation); + &composer, manifest, env_crs->get_verifier_crs(), key_fields, proof_fields, previous_aggregation); // Assign the output aggregation object to the proof public inputs (16 field elements representing two G1 points) result.add_proof_outputs_as_public_inputs(); diff --git a/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.hpp b/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.hpp index c67dacb10f..0e794f3f73 100644 --- a/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.hpp +++ b/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.hpp @@ -27,8 +27,8 @@ namespace acir_format { */ struct RecursionConstraint { static constexpr size_t AGGREGATION_OBJECT_SIZE = 16; // 16 field elements - plonk::verification_key_data verification_key_data; - plonk::proof proof; + std::vector key; + std::vector proof; bool is_aggregation_object_nonzero; uint32_t public_input; std::array input_aggregation_object; @@ -42,7 +42,7 @@ void create_recursion_constraints(plonk::stdlib::types::Composer& composer, cons template inline void read(B& buf, RecursionConstraint& constraint) { using serialize::read; - read(buf, constraint.verification_key_data); + read(buf, constraint.key); read(buf, constraint.proof); read(buf, constraint.is_aggregation_object_nonzero); read(buf, constraint.public_input); @@ -53,7 +53,7 @@ template inline void read(B& buf, RecursionConstraint& constraint) template inline void write(B& buf, RecursionConstraint const& constraint) { using serialize::write; - write(buf, constraint.verification_key_data); + write(buf, constraint.key); write(buf, constraint.proof); write(buf, constraint.is_aggregation_object_nonzero); write(buf, constraint.public_input); diff --git a/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.test.cpp b/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.test.cpp index 911e36c8eb..7154fdd23e 100644 --- a/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.test.cpp +++ b/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.test.cpp @@ -117,8 +117,8 @@ TEST(RecursionConstraint, TestRecursionConstraint) auto inner_proof = inner_prover.construct_proof(); auto inner_verifier = inner_composer.create_verifier(); - std::vector keybuf; - write(keybuf, *(inner_verifier.key)); + // std::vector keybuf; + // write(keybuf, *(inner_verifier.key)); std::array output_vars; for (size_t i = 0; i < 16; ++i) { @@ -126,13 +126,31 @@ TEST(RecursionConstraint, TestRecursionConstraint) // variable idx 2-18 = output_vars output_vars[i] = (static_cast(i + 2)); } - verification_key_data keydata; - uint8_t const* vk_buf = &keybuf[0]; - read(vk_buf, keydata); + // verification_key_data keydata; + // uint8_t const* vk_buf = &keybuf[0]; + // read(vk_buf, keydata); + transcript::StandardTranscript transcript( + inner_proof.proof_data, Composer::create_manifest(1), transcript::HashType::PlookupPedersenBlake3s, 16); + + const std::vector proof_witnesses = transcript.export_transcript_in_recursion_format(); + const std::vector key_witnesses = inner_verifier.key->export_transcript_in_recursion_format(); + + std::vector proof_indices; + const size_t proof_size = proof_witnesses.size(); + + for (size_t i = 0; i < proof_size; ++i) { + proof_indices.emplace_back(static_cast(i + 18)); + } + + std::vector key_indices; + const size_t key_size = key_witnesses.size(); + for (size_t i = 0; i < key_size; ++i) { + key_indices.emplace_back(static_cast(i + 18 + proof_size)); + } acir_format::RecursionConstraint recursion_constraint{ - .verification_key_data = keydata, - .proof = inner_proof, + .key = key_indices, + .proof = proof_indices, .is_aggregation_object_nonzero = false, .public_input = 1, .input_aggregation_object = {}, @@ -140,12 +158,18 @@ TEST(RecursionConstraint, TestRecursionConstraint) }; std::vector witness; - for (size_t i = 0; i < 18; ++i) { + for (size_t i = 0; i < 17; ++i) { witness.emplace_back(0); } + for (const auto& wit : proof_witnesses) { + witness.emplace_back(wit); + } + for (const auto& wit : key_witnesses) { + witness.emplace_back(wit); + } acir_format::acir_format constraint_system{ - .varnum = 18, + .varnum = static_cast(witness.size() + 1), .public_inputs = { 1 }, .fixed_base_scalar_mul_constraints = {}, .logic_constraints = {}, diff --git a/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.cpp b/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.cpp index 21585f6df4..9b899aa69f 100644 --- a/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.cpp +++ b/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.cpp @@ -93,6 +93,18 @@ size_t init_verification_key(void* pippenger, uint8_t const* g2x, uint8_t const* return buffer.size(); } +// void serialize_proof_into_field_elements(uint8_t** proof_data_buf) +// { +// } + +// void serialize_verification_key_into_field_elements(uint8_t** vk_buf) +// { +// plonk::verification_key key; +// read(vk_buf, key); + +// // do the serialize thing... +// } + size_t new_proof(void* pippenger, uint8_t const* g2x, uint8_t const* pk_buf, diff --git a/cpp/src/barretenberg/plonk/proof_system/verification_key/verification_key.cpp b/cpp/src/barretenberg/plonk/proof_system/verification_key/verification_key.cpp index f6cf92bd73..56f4a52cf5 100644 --- a/cpp/src/barretenberg/plonk/proof_system/verification_key/verification_key.cpp +++ b/cpp/src/barretenberg/plonk/proof_system/verification_key/verification_key.cpp @@ -107,6 +107,31 @@ verification_key::verification_key(const size_t num_gates, , polynomial_manifest(composer_type) {} +verification_key::verification_key(const std::vector& key_as_fields, + std::shared_ptr const& crs, + uint32_t composer_type_) +{ + composer_type = composer_type_; + polynomial_manifest = PolynomialManifest(composer_type); + reference_string = crs; + // std::cout << "key as field = " << key_as_fields[3] << std::endl; + // for (const auto& f : key_as_fields) { + // std::cout << "f : " << f << std::endl; + // } + circuit_size = static_cast(uint256_t(key_as_fields[3])); + + std::cout << "circuit size = " << circuit_size << std::endl; + + log_circuit_size = numeric::get_msb(circuit_size); + num_public_inputs = static_cast(uint256_t(key_as_fields[4])); + contains_recursive_proof = static_cast(uint256_t(key_as_fields[5])); + domain = evaluation_domain(circuit_size); + + // ADD THE COMITMENTS DERP DERP DERP + // AS WELL AS RECURSIVE PROOF PUBLIC INPUT INDICES? + // +} + verification_key::verification_key(verification_key_data&& data, std::shared_ptr const& crs) : composer_type(data.composer_type) , circuit_size(data.circuit_size) @@ -178,4 +203,39 @@ sha256::hash verification_key::sha256_hash() return sha256::sha256(to_buffer(vk_data)); } +std::vector verification_key::export_transcript_in_recursion_format() +{ + std::vector output; + output.emplace_back(domain.root); + output.emplace_back(domain.domain); + output.emplace_back(domain.generator); + output.emplace_back(circuit_size); + output.emplace_back(num_public_inputs); + output.emplace_back(contains_recursive_proof); + + for (auto& commitment_entry : commitments) { + std::cout << "key = " << commitment_entry.first << std::endl; + std::cout << "value = " << commitment_entry.second << std::endl; + } + for (const auto& descriptor : polynomial_manifest.get()) { + if (descriptor.source == PolynomialSource::SELECTOR || descriptor.source == PolynomialSource::PERMUTATION) { + std::cout << "key = " << descriptor.commitment_label << std::endl; + const auto element = commitments.at(std::string(descriptor.commitment_label)); + std::cout << "read" << std::endl; + const uint256_t x = element.x; + const uint256_t y = element.y; + const barretenberg::fr x_lo = x.slice(0, 136); + const barretenberg::fr x_hi = x.slice(136, 272); + const barretenberg::fr y_lo = y.slice(0, 136); + const barretenberg::fr y_hi = y.slice(136, 272); + output.emplace_back(x_lo); + output.emplace_back(x_hi); + output.emplace_back(y_lo); + output.emplace_back(y_hi); + } + } + + return output; +} + } // namespace proof_system::plonk diff --git a/cpp/src/barretenberg/plonk/proof_system/verification_key/verification_key.hpp b/cpp/src/barretenberg/plonk/proof_system/verification_key/verification_key.hpp index 709eea6ee7..3165ed9923 100644 --- a/cpp/src/barretenberg/plonk/proof_system/verification_key/verification_key.hpp +++ b/cpp/src/barretenberg/plonk/proof_system/verification_key/verification_key.hpp @@ -55,6 +55,10 @@ struct verification_key { const size_t num_inputs, std::shared_ptr const& crs, uint32_t composer_type); + verification_key(const std::vector& key_as_fields, + std::shared_ptr const& crs, + uint32_t composer_type_); + verification_key(const verification_key& other); verification_key(verification_key&& other); verification_key& operator=(verification_key&& other); @@ -62,6 +66,7 @@ struct verification_key { ~verification_key() = default; sha256::hash sha256_hash(); + std::vector export_transcript_in_recursion_format(); uint32_t composer_type; size_t circuit_size; diff --git a/cpp/src/barretenberg/stdlib/recursion/transcript/transcript.hpp b/cpp/src/barretenberg/stdlib/recursion/transcript/transcript.hpp index 73055fb376..db3d95c51b 100644 --- a/cpp/src/barretenberg/stdlib/recursion/transcript/transcript.hpp +++ b/cpp/src/barretenberg/stdlib/recursion/transcript/transcript.hpp @@ -48,6 +48,54 @@ template class Transcript { // } } + /** + * @brief Construct a new Transcript object using a proof represented as a field_pt vector + * + * N.B. If proof is represented as a uint8_t vector, Transcript will convert into witnesses in-situ. + * Use this constructor method if the proof is *already present* as circuit witnesses! + * @param in_context + * @param input_manifest + * @param field_buffer + * @param num_public_inputs + */ + Transcript(Composer* in_context, + const transcript::Manifest input_manifest, + const std::vector& field_buffer, + const size_t num_public_inputs) + : context(in_context) + , transcript_base(input_manifest, transcript::HashType::PedersenBlake3s, 16) + , current_challenge(in_context) + { + size_t count = 0; + + const auto num_rounds = input_manifest.get_num_rounds(); + for (size_t i = 0; i < num_rounds; ++i) { + for (auto manifest_element : input_manifest.get_round_manifest(i).elements) { + if (!manifest_element.derived_by_verifier) { + if (manifest_element.num_bytes == 32 && manifest_element.name != "public_inputs") { + add_field_element(manifest_element.name, field_buffer[count++]); + } else if (manifest_element.num_bytes == 64 && manifest_element.name != "public_inputs") { + const auto x_lo = field_buffer[count++]; + const auto x_hi = field_buffer[count++]; + const auto y_lo = field_buffer[count++]; + const auto y_hi = field_buffer[count++]; + fq_pt x(x_lo, x_hi); + fq_pt y(y_lo, y_hi); + group_pt element(x, y); + add_group_element(manifest_element.name, element); + } else { + ASSERT(manifest_element.name == "public_inputs"); + std::vector public_inputs; + for (size_t i = 0; i < num_public_inputs; ++i) { + public_inputs.emplace_back(field_buffer[count++]); + } + add_field_element_vector(manifest_element.name, public_inputs); + } + } + } + } + } + transcript::Manifest get_manifest() const { return transcript_base.get_manifest(); } int check_field_element_cache(const std::string& element_name) const diff --git a/cpp/src/barretenberg/stdlib/recursion/verification_key/verification_key.hpp b/cpp/src/barretenberg/stdlib/recursion/verification_key/verification_key.hpp index 57b8a39207..2c982f6074 100644 --- a/cpp/src/barretenberg/stdlib/recursion/verification_key/verification_key.hpp +++ b/cpp/src/barretenberg/stdlib/recursion/verification_key/verification_key.hpp @@ -25,6 +25,19 @@ namespace stdlib { namespace recursion { template struct evaluation_domain { + static evaluation_domain from_field_pt_vector(const std::vector>& fields) + { + evaluation_domain domain; + domain.root = fields[0]; + domain.root_inverse = domain.root.invert(); + domain.domain = fields[1]; + domain.domain_inverse = domain.domain.invert(); + domain.generator = fields[2]; + domain.generator_inverse = domain.generator.invert(); + domain.size = domain.domain; + return domain; + } + static evaluation_domain from_witness(Composer* ctx, const barretenberg::evaluation_domain& input) { evaluation_domain domain; @@ -105,6 +118,50 @@ template struct evaluation_domain { */ template struct verification_key { using Composer = typename Curve::Composer; + + static std::shared_ptr from_field_pt_vector(Composer* ctx, + std::shared_ptr const& crs, + const std::vector>& fields) + { + std::vector fields_raw; + for (const auto& f : fields) { + fields_raw.emplace_back(f.get_value()); + std::cout << "f: " << f.get_value() << std::endl; + } + std::cout << "a" << std::endl; + std::shared_ptr key = std::make_shared(); + std::cout << "b" << std::endl; + key->context = ctx; + key->base_key = std::make_shared(fields_raw, crs, Composer::type); + std::cout << "c" << std::endl; + + key->reference_string = key->base_key->reference_string; + key->polynomial_manifest = PolynomialManifest(Composer::type); + key->domain = evaluation_domain::from_field_pt_vector({ fields[0], fields[1], fields[2] }); + + key->n = fields[3]; + key->num_public_inputs = fields[4]; + key->contains_recursive_proof = bool_t(fields[5]); + + size_t count = 6; + for (const auto& descriptor : key->polynomial_manifest.get()) { + if (descriptor.source == PolynomialSource::SELECTOR || descriptor.source == PolynomialSource::PERMUTATION) { + + const auto x_lo = fields[count++]; + const auto x_hi = fields[count++]; + const auto y_lo = fields[count++]; + const auto y_hi = fields[count++]; + const typename Curve::fq_ct x(x_lo, x_hi); + const typename Curve::fq_ct y(y_lo, y_hi); + const typename Curve::g1_ct element(x, y); + + key->commitments.insert({ std::string(descriptor.commitment_label), element }); + } + } + std::cout << "d" << std::endl; + return key; + } + static std::shared_ptr from_witness(Composer* ctx, const std::shared_ptr& input_key) { diff --git a/cpp/src/barretenberg/stdlib/recursion/verifier/verifier.hpp b/cpp/src/barretenberg/stdlib/recursion/verifier/verifier.hpp index aa185e34d7..5c59bf9580 100644 --- a/cpp/src/barretenberg/stdlib/recursion/verifier/verifier.hpp +++ b/cpp/src/barretenberg/stdlib/recursion/verifier/verifier.hpp @@ -182,6 +182,48 @@ aggregation_state verify_proof(typename Curve::Composer* context, const transcript::Manifest& manifest, const plonk::proof& proof, const aggregation_state previous_output = aggregation_state()) +{ + using Composer = typename Curve::Composer; + + key->program_width = program_settings::program_width; + + Transcript transcript = Transcript(context, proof.proof_data, manifest); + + return verify_proof_(context, key, transcript, previous_output); +} + +/** + * Refer to src/barretenberg/plonk/proof_system/verifier/verifier.cpp verify_proof() for the native implementation, + * which includes detailed comments. + */ +template +aggregation_state verify_proof(typename Curve::Composer* context, + const transcript::Manifest& manifest, + std::shared_ptr const& crs, + const std::vector& key, + const std::vector& proof, + const aggregation_state previous_output = aggregation_state()) +{ + using Composer = typename Curve::Composer; + + std::shared_ptr> vkey = verification_key::from_field_pt_vector(context, crs, key); + vkey->program_width = program_settings::program_width; + + const size_t num_public_inputs = static_cast(uint256_t(vkey->num_public_inputs.get_value()).data[0]); + Transcript transcript = Transcript(context, manifest, proof, num_public_inputs); + + return verify_proof_(context, vkey, transcript, previous_output); +} + +/** + * Refer to src/barretenberg/plonk/proof_system/verifier/verifier.cpp verify_proof() for the native implementation, + * which includes detailed comments. + */ +template +aggregation_state verify_proof_(typename Curve::Composer* context, + std::shared_ptr> key, + Transcript& transcript, + const aggregation_state previous_output = aggregation_state()) { using fr_ct = typename Curve::fr_ct; using fq_ct = typename Curve::fq_ct; @@ -190,7 +232,6 @@ aggregation_state verify_proof(typename Curve::Composer* context, key->program_width = program_settings::program_width; - Transcript transcript = Transcript(context, proof.proof_data, manifest); std::map kate_g1_elements; std::map kate_fr_elements_at_zeta; std::map kate_fr_elements_at_zeta_large; diff --git a/cpp/src/barretenberg/transcript/transcript_wrappers.hpp b/cpp/src/barretenberg/transcript/transcript_wrappers.hpp index bd33eff800..ab0b02d4c6 100644 --- a/cpp/src/barretenberg/transcript/transcript_wrappers.hpp +++ b/cpp/src/barretenberg/transcript/transcript_wrappers.hpp @@ -56,6 +56,46 @@ class StandardTranscript : public Transcript { // TODO(luke): temporary function for debugging barretenberg::fr get_mock_challenge() { return barretenberg::fr::random_element(); }; + + /** + * @brief Returns transcript represented as a vector of barretenberg::fr. + * Used to represent recursive proofs (i.e. proof represented as circuit-native field elements) + * + * @return std::vector + */ + std::vector export_transcript_in_recursion_format() + { + std::vector fields; + const auto num_rounds = get_manifest().get_num_rounds(); + for (size_t i = 0; i < num_rounds; ++i) { + for (auto manifest_element : get_manifest().get_round_manifest(i).elements) { + if (!manifest_element.derived_by_verifier) { + if (manifest_element.num_bytes == 32 && manifest_element.name != "public_inputs") { + fields.emplace_back(get_field_element(manifest_element.name)); + } else if (manifest_element.num_bytes == 64 && manifest_element.name != "public_inputs") { + const auto group_element = get_group_element(manifest_element.name); + const uint256_t x = group_element.x; + const uint256_t y = group_element.y; + const barretenberg::fr x_lo = x.slice(0, 136); + const barretenberg::fr x_hi = x.slice(136, 272); + const barretenberg::fr y_lo = y.slice(0, 136); + const barretenberg::fr y_hi = y.slice(136, 272); + fields.emplace_back(x_lo); + fields.emplace_back(x_hi); + fields.emplace_back(y_lo); + fields.emplace_back(y_hi); + } else { + ASSERT(manifest_element.name == "public_inputs"); + const auto public_inputs_vector = get_field_element_vector(manifest_element.name); + for (const auto& ele : public_inputs_vector) { + fields.emplace_back(ele); + } + } + } + } + } + return fields; + } }; } // namespace transcript From 292f6265b862847af71df13e5c5eecaf73954ad6 Mon Sep 17 00:00:00 2001 From: zac-williamson Date: Sun, 16 Apr 2023 11:40:42 +0200 Subject: [PATCH 04/41] recursive verification key no longer encapsulates a native key recursive verification key no longer encapsulates a reference string (neither were fundamentally needed!) --- .../dsl/acir_format/recursion_constraint.cpp | 3 +- .../acir_format/recursion_constraint.test.cpp | 2 +- .../verification_key/verification_key.cpp | 47 ++++++----------- .../verification_key/verification_key.hpp | 5 +- .../verification_key/verification_key.hpp | 51 ++++++++----------- .../stdlib/recursion/verifier/verifier.hpp | 41 +++++++-------- 6 files changed, 57 insertions(+), 92 deletions(-) diff --git a/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.cpp b/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.cpp index 373ee3c15a..4c9d08a32a 100644 --- a/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.cpp +++ b/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.cpp @@ -1,5 +1,4 @@ #include "recursion_constraint.hpp" -#include "barretenberg/plonk/proof_system/verification_key/verification_key.hpp" #include "barretenberg/stdlib/recursion/aggregation_state/aggregation_state.hpp" #include "barretenberg/stdlib/recursion/verifier/verifier.hpp" @@ -62,7 +61,7 @@ void create_recursion_constraints(Composer& composer, const RecursionConstraint& // recursively verify the proof aggregation_state_ct result = proof_system::plonk::stdlib::recursion::verify_proof( - &composer, manifest, env_crs->get_verifier_crs(), key_fields, proof_fields, previous_aggregation); + &composer, manifest, key_fields, proof_fields, previous_aggregation); // Assign the output aggregation object to the proof public inputs (16 field elements representing two G1 points) result.add_proof_outputs_as_public_inputs(); diff --git a/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.test.cpp b/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.test.cpp index 7154fdd23e..f36446e3db 100644 --- a/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.test.cpp +++ b/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.test.cpp @@ -134,7 +134,7 @@ TEST(RecursionConstraint, TestRecursionConstraint) inner_proof.proof_data, Composer::create_manifest(1), transcript::HashType::PlookupPedersenBlake3s, 16); const std::vector proof_witnesses = transcript.export_transcript_in_recursion_format(); - const std::vector key_witnesses = inner_verifier.key->export_transcript_in_recursion_format(); + const std::vector key_witnesses = inner_verifier.key->export_key_in_recursion_format(); std::vector proof_indices; const size_t proof_size = proof_witnesses.size(); diff --git a/cpp/src/barretenberg/plonk/proof_system/verification_key/verification_key.cpp b/cpp/src/barretenberg/plonk/proof_system/verification_key/verification_key.cpp index 56f4a52cf5..7ba7af5175 100644 --- a/cpp/src/barretenberg/plonk/proof_system/verification_key/verification_key.cpp +++ b/cpp/src/barretenberg/plonk/proof_system/verification_key/verification_key.cpp @@ -107,31 +107,6 @@ verification_key::verification_key(const size_t num_gates, , polynomial_manifest(composer_type) {} -verification_key::verification_key(const std::vector& key_as_fields, - std::shared_ptr const& crs, - uint32_t composer_type_) -{ - composer_type = composer_type_; - polynomial_manifest = PolynomialManifest(composer_type); - reference_string = crs; - // std::cout << "key as field = " << key_as_fields[3] << std::endl; - // for (const auto& f : key_as_fields) { - // std::cout << "f : " << f << std::endl; - // } - circuit_size = static_cast(uint256_t(key_as_fields[3])); - - std::cout << "circuit size = " << circuit_size << std::endl; - - log_circuit_size = numeric::get_msb(circuit_size); - num_public_inputs = static_cast(uint256_t(key_as_fields[4])); - contains_recursive_proof = static_cast(uint256_t(key_as_fields[5])); - domain = evaluation_domain(circuit_size); - - // ADD THE COMITMENTS DERP DERP DERP - // AS WELL AS RECURSIVE PROOF PUBLIC INPUT INDICES? - // -} - verification_key::verification_key(verification_key_data&& data, std::shared_ptr const& crs) : composer_type(data.composer_type) , circuit_size(data.circuit_size) @@ -203,7 +178,14 @@ sha256::hash verification_key::sha256_hash() return sha256::sha256(to_buffer(vk_data)); } -std::vector verification_key::export_transcript_in_recursion_format() +/** + * @brief When recursively verifying proofs, we represent the verification key using field elements. + * This method exports the key formatted in the manner our recursive verifier expects. + * NOTE: only used by the dsl at the moment. Might be cleaner to make this a dsl function? + * + * @return std::vector + */ +std::vector verification_key::export_key_in_recursion_format() { std::vector output; output.emplace_back(domain.root); @@ -212,16 +194,17 @@ std::vector verification_key::export_transcript_in_recursion_f output.emplace_back(circuit_size); output.emplace_back(num_public_inputs); output.emplace_back(contains_recursive_proof); - - for (auto& commitment_entry : commitments) { - std::cout << "key = " << commitment_entry.first << std::endl; - std::cout << "value = " << commitment_entry.second << std::endl; + for (size_t i = 0; i < 16; ++i) { + if (recursive_proof_public_input_indices.size() > i) { + output.emplace_back(recursive_proof_public_input_indices[i]); + } else { + output.emplace_back(0); + ASSERT(contains_recursive_proof == false); + } } for (const auto& descriptor : polynomial_manifest.get()) { if (descriptor.source == PolynomialSource::SELECTOR || descriptor.source == PolynomialSource::PERMUTATION) { - std::cout << "key = " << descriptor.commitment_label << std::endl; const auto element = commitments.at(std::string(descriptor.commitment_label)); - std::cout << "read" << std::endl; const uint256_t x = element.x; const uint256_t y = element.y; const barretenberg::fr x_lo = x.slice(0, 136); diff --git a/cpp/src/barretenberg/plonk/proof_system/verification_key/verification_key.hpp b/cpp/src/barretenberg/plonk/proof_system/verification_key/verification_key.hpp index 3165ed9923..aa240f52c9 100644 --- a/cpp/src/barretenberg/plonk/proof_system/verification_key/verification_key.hpp +++ b/cpp/src/barretenberg/plonk/proof_system/verification_key/verification_key.hpp @@ -55,9 +55,6 @@ struct verification_key { const size_t num_inputs, std::shared_ptr const& crs, uint32_t composer_type); - verification_key(const std::vector& key_as_fields, - std::shared_ptr const& crs, - uint32_t composer_type_); verification_key(const verification_key& other); verification_key(verification_key&& other); @@ -66,7 +63,7 @@ struct verification_key { ~verification_key() = default; sha256::hash sha256_hash(); - std::vector export_transcript_in_recursion_format(); + std::vector export_key_in_recursion_format(); uint32_t composer_type; size_t circuit_size; diff --git a/cpp/src/barretenberg/stdlib/recursion/verification_key/verification_key.hpp b/cpp/src/barretenberg/stdlib/recursion/verification_key/verification_key.hpp index 2c982f6074..c297973dbb 100644 --- a/cpp/src/barretenberg/stdlib/recursion/verification_key/verification_key.hpp +++ b/cpp/src/barretenberg/stdlib/recursion/verification_key/verification_key.hpp @@ -120,30 +120,32 @@ template struct verification_key { using Composer = typename Curve::Composer; static std::shared_ptr from_field_pt_vector(Composer* ctx, - std::shared_ptr const& crs, const std::vector>& fields) { std::vector fields_raw; - for (const auto& f : fields) { - fields_raw.emplace_back(f.get_value()); - std::cout << "f: " << f.get_value() << std::endl; - } - std::cout << "a" << std::endl; std::shared_ptr key = std::make_shared(); - std::cout << "b" << std::endl; key->context = ctx; - key->base_key = std::make_shared(fields_raw, crs, Composer::type); - std::cout << "c" << std::endl; - key->reference_string = key->base_key->reference_string; key->polynomial_manifest = PolynomialManifest(Composer::type); key->domain = evaluation_domain::from_field_pt_vector({ fields[0], fields[1], fields[2] }); key->n = fields[3]; key->num_public_inputs = fields[4]; - key->contains_recursive_proof = bool_t(fields[5]); - size_t count = 6; + // NOTE: For now `contains_recursive_proof` and `recursive_proof_public_input_indices` need to be circuit + // constants! + key->contains_recursive_proof = static_cast(uint256_t(fields[5].get_value())); + for (size_t i = 0; i < 16; ++i) { + const uint32_t idx = static_cast(uint256_t(fields[6 + i].get_value())); + key->recursive_proof_public_input_indices.emplace_back(idx); + } + // Apply constraints to force the recursive proof information to be circuit constants + fields[5].assert_equal(key->contains_recursive_proof); + for (size_t i = 0; i < 16; ++i) { + fields[6 + i].assert_equal(key->recursive_proof_public_input_indices[i]); + } + + size_t count = 22; for (const auto& descriptor : key->polynomial_manifest.get()) { if (descriptor.source == PolynomialSource::SELECTOR || descriptor.source == PolynomialSource::PERMUTATION) { @@ -158,7 +160,6 @@ template struct verification_key { key->commitments.insert({ std::string(descriptor.commitment_label), element }); } } - std::cout << "d" << std::endl; return key; } @@ -168,16 +169,14 @@ template struct verification_key { std::shared_ptr key = std::make_shared(); // Native data: key->context = ctx; - key->base_key = input_key; - key->reference_string = input_key->reference_string; key->polynomial_manifest = input_key->polynomial_manifest; // Circuit types: key->n = witness_t(ctx, barretenberg::fr(input_key->circuit_size)); key->num_public_inputs = witness_t(ctx, input_key->num_public_inputs); key->domain = evaluation_domain::from_witness(ctx, input_key->domain); - key->contains_recursive_proof = witness_t(ctx, input_key->contains_recursive_proof); - + key->contains_recursive_proof = input_key->contains_recursive_proof; + key->recursive_proof_public_input_indices = input_key->recursive_proof_public_input_indices; for (const auto& [tag, value] : input_key->commitments) { key->commitments.insert({ tag, Curve::g1_ct::from_witness(ctx, value) }); } @@ -190,15 +189,13 @@ template struct verification_key { { std::shared_ptr key = std::make_shared(); key->context = ctx; - key->base_key = input_key; key->n = field_t(ctx, input_key->circuit_size); key->num_public_inputs = field_t(ctx, input_key->num_public_inputs); - key->contains_recursive_proof = bool_t(ctx, input_key->contains_recursive_proof); + key->contains_recursive_proof = input_key->contains_recursive_proof; + key->recursive_proof_public_input_indices = input_key->recursive_proof_public_input_indices; key->domain = evaluation_domain::from_constants(ctx, input_key->domain); - key->reference_string = input_key->reference_string; - for (const auto& [tag, value] : input_key->commitments) { key->commitments.insert({ tag, typename Curve::g1_ct(value) }); } @@ -321,23 +318,17 @@ template struct verification_key { field_t num_public_inputs; field_t z_pow_n; - // NOTE: This does not strictly need to be a circuit type. It can be used to check in the circuit - // if a proof contains any aggregated state. - bool_t contains_recursive_proof; - evaluation_domain domain; std::map commitments; // Native data: - std::shared_ptr reference_string; - PolynomialManifest polynomial_manifest; - + // Used to check in the circuit if a proof contains any aggregated state. + bool contains_recursive_proof = false; + std::vector recursive_proof_public_input_indices; size_t program_width = 4; - - std::shared_ptr base_key; Composer* context; }; diff --git a/cpp/src/barretenberg/stdlib/recursion/verifier/verifier.hpp b/cpp/src/barretenberg/stdlib/recursion/verifier/verifier.hpp index 5c59bf9580..f3b4004218 100644 --- a/cpp/src/barretenberg/stdlib/recursion/verifier/verifier.hpp +++ b/cpp/src/barretenberg/stdlib/recursion/verifier/verifier.hpp @@ -199,14 +199,13 @@ aggregation_state verify_proof(typename Curve::Composer* context, template aggregation_state verify_proof(typename Curve::Composer* context, const transcript::Manifest& manifest, - std::shared_ptr const& crs, const std::vector& key, const std::vector& proof, const aggregation_state previous_output = aggregation_state()) { using Composer = typename Curve::Composer; - std::shared_ptr> vkey = verification_key::from_field_pt_vector(context, crs, key); + std::shared_ptr> vkey = verification_key::from_field_pt_vector(context, key); vkey->program_width = program_settings::program_width; const size_t num_public_inputs = static_cast(uint256_t(vkey->num_public_inputs.get_value()).data[0]); @@ -366,10 +365,6 @@ aggregation_state verify_proof_(typename Curve::Composer* context, rhs_scalars.push_back(random_separator); } - // Check if recursive proof information is correctly set. - key->contains_recursive_proof.assert_equal(key->base_key->contains_recursive_proof, - "contains_recursive_proof is incorrectly set"); - /** * N.B. if this key contains a recursive proof, then ALL potential verification keys being verified by the outer *circuit must ALSO contain a recursive proof (this is not a concern if the key is being generated from circuit @@ -377,7 +372,7 @@ aggregation_state verify_proof_(typename Curve::Composer* context, *code path should be used with extreme caution if the verification key is not being generated from circuit *constants **/ - if (key->base_key->contains_recursive_proof) { + if (key->contains_recursive_proof) { const auto public_inputs = transcript.get_field_element_vector("public_inputs"); const auto recover_fq_from_public_inputs = [&public_inputs](const size_t idx0, const size_t idx1, const size_t idx2, const size_t idx3) { @@ -390,22 +385,22 @@ aggregation_state verify_proof_(typename Curve::Composer* context, fr_ct recursion_separator_challenge = transcript.get_challenge_field_element("separator", 2); - const auto x0 = recover_fq_from_public_inputs(key->base_key->recursive_proof_public_input_indices[0], - key->base_key->recursive_proof_public_input_indices[1], - key->base_key->recursive_proof_public_input_indices[2], - key->base_key->recursive_proof_public_input_indices[3]); - const auto y0 = recover_fq_from_public_inputs(key->base_key->recursive_proof_public_input_indices[4], - key->base_key->recursive_proof_public_input_indices[5], - key->base_key->recursive_proof_public_input_indices[6], - key->base_key->recursive_proof_public_input_indices[7]); - const auto x1 = recover_fq_from_public_inputs(key->base_key->recursive_proof_public_input_indices[8], - key->base_key->recursive_proof_public_input_indices[9], - key->base_key->recursive_proof_public_input_indices[10], - key->base_key->recursive_proof_public_input_indices[11]); - const auto y1 = recover_fq_from_public_inputs(key->base_key->recursive_proof_public_input_indices[12], - key->base_key->recursive_proof_public_input_indices[13], - key->base_key->recursive_proof_public_input_indices[14], - key->base_key->recursive_proof_public_input_indices[15]); + const auto x0 = recover_fq_from_public_inputs(key->recursive_proof_public_input_indices[0], + key->recursive_proof_public_input_indices[1], + key->recursive_proof_public_input_indices[2], + key->recursive_proof_public_input_indices[3]); + const auto y0 = recover_fq_from_public_inputs(key->recursive_proof_public_input_indices[4], + key->recursive_proof_public_input_indices[5], + key->recursive_proof_public_input_indices[6], + key->recursive_proof_public_input_indices[7]); + const auto x1 = recover_fq_from_public_inputs(key->recursive_proof_public_input_indices[8], + key->recursive_proof_public_input_indices[9], + key->recursive_proof_public_input_indices[10], + key->recursive_proof_public_input_indices[11]); + const auto y1 = recover_fq_from_public_inputs(key->recursive_proof_public_input_indices[12], + key->recursive_proof_public_input_indices[13], + key->recursive_proof_public_input_indices[14], + key->recursive_proof_public_input_indices[15]); opening_elements.push_back(g1_ct(x0, y0)); opening_scalars.push_back(recursion_separator_challenge); From bc02eff9b63c92453575846d8b409ea5c6639e33 Mon Sep 17 00:00:00 2001 From: zac-williamson Date: Sun, 16 Apr 2023 16:53:40 +0200 Subject: [PATCH 05/41] added serialization test for acir_proofs --- .../dsl/acir_proofs/acir_proofs.test.cpp | 140 ++++++++++++++++++ 1 file changed, 140 insertions(+) create mode 100644 cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.test.cpp diff --git a/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.test.cpp b/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.test.cpp new file mode 100644 index 0000000000..ce28fe62df --- /dev/null +++ b/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.test.cpp @@ -0,0 +1,140 @@ +#include "acir_proofs.hpp" +#include "../acir_format/acir_format.hpp" +#include "barretenberg/srs/io.hpp" +#include +#include + +using namespace proof_system::plonk::stdlib::types; + +void create_inner_circuit(acir_format::acir_format& constraint_system, std::vector& witness) +{ + /** + * constraints produced by Noir program: + * fn main(x : u32, y : pub u32) { + * let z = x ^ y; + * + * constrain z != 10; + * } + **/ + acir_format::RangeConstraint range_a{ + .witness = 1, + .num_bits = 32, + }; + acir_format::RangeConstraint range_b{ + .witness = 2, + .num_bits = 32, + }; + + acir_format::LogicConstraint logic_constraint{ + .a = 1, + .b = 2, + .result = 3, + .num_bits = 32, + .is_xor_gate = 1, + }; + poly_triple expr_a{ + .a = 3, + .b = 4, + .c = 0, + .q_m = 0, + .q_l = 1, + .q_r = -1, + .q_o = 0, + .q_c = -10, + }; + poly_triple expr_b{ + .a = 4, + .b = 5, + .c = 6, + .q_m = 1, + .q_l = 0, + .q_r = 0, + .q_o = -1, + .q_c = 0, + }; + poly_triple expr_c{ + .a = 4, + .b = 6, + .c = 4, + .q_m = 1, + .q_l = 0, + .q_r = 0, + .q_o = -1, + .q_c = 0, + + }; + poly_triple expr_d{ + .a = 6, + .b = 0, + .c = 0, + .q_m = 0, + .q_l = -1, + .q_r = 0, + .q_o = 0, + .q_c = 1, + }; + + constraint_system = acir_format::acir_format{ + .varnum = 7, + .public_inputs = { 2 }, + .fixed_base_scalar_mul_constraints = {}, + .logic_constraints = { logic_constraint }, + .range_constraints = { range_a, range_b }, + .schnorr_constraints = {}, + .ecdsa_constraints = {}, + .sha256_constraints = {}, + .blake2s_constraints = {}, + .hash_to_field_constraints = {}, + .pedersen_constraints = {}, + .merkle_membership_constraints = {}, + .constraints = { expr_a, expr_b, expr_c, expr_d }, + }; + + uint256_t inverse_of_five = fr(5).invert(); + + witness.emplace_back(5); + witness.emplace_back(10); + witness.emplace_back(15); + witness.emplace_back(5); + witness.emplace_back(inverse_of_five); + witness.emplace_back(1); +} + +static acir_format::acir_format constraint_system; +static std::vector witness; + +TEST(AcirProofs, TestSerialization) +{ + create_inner_circuit(constraint_system, witness); + + std::vector witness_buf; // = to_buffer(witness); + std::vector constraint_system_buf; // + write(constraint_system_buf, constraint_system); + write(witness_buf, witness); + + uint32_t total_circuit_size = acir_proofs::get_total_circuit_size(&constraint_system_buf[0]); + uint32_t pow2_size = 1 << (numeric::get_msb(total_circuit_size) + 1); + auto env_crs = std::make_unique(); + auto verifier_crs = env_crs->get_verifier_crs(); + + uint8_t const* pk_buf = nullptr; + acir_proofs::init_proving_key(&constraint_system_buf[0], &pk_buf); + barretenberg::g2::affine_element g2x = verifier_crs->get_g2x(); + + auto* pippenger_ptr_base = new scalar_multiplication::Pippenger(env_load_prover_crs(pow2_size + 1), pow2_size + 1); + void* pippenger_ptr = reinterpret_cast(pippenger_ptr_base); + + std::vector g2x_buffer(128); + io::write_g2_elements_to_buffer(&g2x, (char*)(&g2x_buffer[0]), 1); + + uint8_t const* vk_buf = nullptr; + acir_proofs::init_verification_key(pippenger_ptr, &g2x_buffer[0], pk_buf, &vk_buf); + + uint8_t* proof_data_buf = nullptr; + size_t proof_length = acir_proofs::new_proof( + pippenger_ptr, &g2x_buffer[0], pk_buf, &constraint_system_buf[0], &witness_buf[0], &proof_data_buf); + + bool verified = acir_proofs::verify_proof( + &g2x_buffer[0], vk_buf, &constraint_system_buf[0], proof_data_buf, static_cast(proof_length)); + EXPECT_EQ(verified, true); +} From d9656e9c5fcfca6e3a8df7fbab352f531e0f4fec Mon Sep 17 00:00:00 2001 From: zac-williamson Date: Tue, 18 Apr 2023 13:46:51 +0200 Subject: [PATCH 06/41] Added serialization methods into dsl for recursive proof composition Added passing serialization tests Fixed format of RecursionConstraint to be compatible with existing ACIR formatting --- .../dsl/acir_format/acir_format.cpp | 10 +- .../dsl/acir_format/acir_format.hpp | 2 + .../dsl/acir_format/recursion_constraint.cpp | 62 +++++- .../dsl/acir_format/recursion_constraint.hpp | 16 +- .../acir_format/recursion_constraint.test.cpp | 4 - .../dsl/acir_proofs/acir_proofs.cpp | 92 +++++++-- .../dsl/acir_proofs/acir_proofs.hpp | 6 + .../dsl/acir_proofs/acir_proofs.test.cpp | 192 +++++++++++++++++- .../types/polynomial_manifest.hpp | 2 +- .../verification_key/verification_key.cpp | 45 ++++ .../verification_key/verification_key.hpp | 2 + .../verification_key/verification_key.hpp | 13 +- .../stdlib/recursion/verifier/verifier.hpp | 5 +- .../transcript/transcript_wrappers.hpp | 43 +++- 14 files changed, 453 insertions(+), 41 deletions(-) diff --git a/cpp/src/barretenberg/dsl/acir_format/acir_format.cpp b/cpp/src/barretenberg/dsl/acir_format/acir_format.cpp index 2b7b9526b6..65313934a6 100644 --- a/cpp/src/barretenberg/dsl/acir_format/acir_format.cpp +++ b/cpp/src/barretenberg/dsl/acir_format/acir_format.cpp @@ -89,7 +89,7 @@ void create_circuit(Composer& composer, const acir_format& constraint_system) // Add recursion constraints for (const auto& constraint : constraint_system.recursion_constraints) { - create_recursion_constraints(composer, constraint); + create_recursion_constraints(composer, constraint); } } @@ -172,7 +172,7 @@ Composer create_circuit(const acir_format& constraint_system, // Add recursion constraints for (const auto& constraint : constraint_system.recursion_constraints) { - create_recursion_constraints(composer, constraint); + create_recursion_constraints(composer, constraint); } return composer; @@ -261,7 +261,7 @@ Composer create_circuit_with_witness(const acir_format& constraint_system, // Add recursion constraints for (const auto& constraint : constraint_system.recursion_constraints) { - create_recursion_constraints(composer, constraint); + create_recursion_constraints(composer, constraint); } return composer; } @@ -346,7 +346,7 @@ Composer create_circuit_with_witness(const acir_format& constraint_system, std:: // Add recursion constraints for (const auto& constraint : constraint_system.recursion_constraints) { - create_recursion_constraints(composer, constraint); + create_recursion_constraints(composer, constraint); } return composer; } @@ -429,7 +429,7 @@ void create_circuit_with_witness(Composer& composer, const acir_format& constrai // Add recursion constraints for (const auto& constraint : constraint_system.recursion_constraints) { - create_recursion_constraints(composer, constraint); + create_recursion_constraints(composer, constraint); } } diff --git a/cpp/src/barretenberg/dsl/acir_format/acir_format.hpp b/cpp/src/barretenberg/dsl/acir_format/acir_format.hpp index 12592c6a32..950de43460 100644 --- a/cpp/src/barretenberg/dsl/acir_format/acir_format.hpp +++ b/cpp/src/barretenberg/dsl/acir_format/acir_format.hpp @@ -73,6 +73,7 @@ template inline void read(B& buf, acir_format& data) read(buf, data.pedersen_constraints); read(buf, data.hash_to_field_constraints); read(buf, data.fixed_base_scalar_mul_constraints); + read(buf, data.recursion_constraints); read(buf, data.constraints); } @@ -91,6 +92,7 @@ template inline void write(B& buf, acir_format const& data) write(buf, data.pedersen_constraints); write(buf, data.hash_to_field_constraints); write(buf, data.fixed_base_scalar_mul_constraints); + write(buf, data.recursion_constraints); write(buf, data.constraints); } diff --git a/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.cpp b/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.cpp index 4c9d08a32a..d6af1512f3 100644 --- a/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.cpp +++ b/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.cpp @@ -1,6 +1,8 @@ #include "recursion_constraint.hpp" +#include "barretenberg/plonk/proof_system/verification_key/verification_key.hpp" #include "barretenberg/stdlib/recursion/aggregation_state/aggregation_state.hpp" #include "barretenberg/stdlib/recursion/verifier/verifier.hpp" +#include "barretenberg/transcript/transcript_wrappers.hpp" using namespace proof_system::plonk::stdlib::types; @@ -9,14 +11,54 @@ using aggregation_state_ct = proof_system::plonk::stdlib::recursion::aggregation using noir_recursive_settings = proof_system::plonk::stdlib::recursion::recursive_ultra_verifier_settings; namespace acir_format { +void generate_dummy_proof() {} /** * @brief Add constraints required to recursively verify an UltraPlonk proof * * @param composer * @param input + * @tparam has_valid_witness_assignment. Do we have witnesses or are we just generating keys? + * @tparam inner_proof_contains_recursive_proof. Do we expect the inner proof to also have performed recursive + * verification? We need to know this at circuit-compile time. + * + * @note We currently only support RecursionConstraint where inner_proof_contains_recursive_proof = false. + * We would either need a separate ACIR opcode where inner_proof_contains_recursive_proof = true, + * or we need non-witness data to be provided as metadata in the ACIR opcode */ +template void create_recursion_constraints(Composer& composer, const RecursionConstraint& input) { + + // If we do not have a witness, we must ensure that our dummy witness will not trigger + // on-curve errors and inverting-zero errors + { + // get a fake key/proof that satisfies on-curve + inversion-zero checks + const std::vector dummy_key = plonk::verification_key::export_dummy_key_in_recursion_format( + PolynomialManifest(Composer::type), inner_proof_contains_recursive_proof); + const auto manifest = Composer::create_unrolled_manifest(1); + const std::vector dummy_proof = + transcript::StandardTranscript::export_dummy_transcript_in_recursion_format(manifest); + for (size_t i = 0; i < input.proof.size(); ++i) { + const auto proof_field_idx = input.proof[i]; + // if we do NOT have a witness assignment (i.e. are just building the proving/verification keys), + // we add our dummy proof values as Composer variables. + // if we DO have a valid witness assignment, we use the real witness assignment + barretenberg::fr dummy_field = + has_valid_witness_assignment ? composer.get_variable(proof_field_idx) : dummy_proof[i]; + // Create a copy constraint between our dummy field and the witness index provided by RecursionConstraint. + // This will make the RecursionConstraint idx equal to `dummy_field`. + // In the case of a valid witness assignment, this does nothing (as dummy_field = real value) + // In the case of no valid witness assignment, this makes sure that the RecursionConstraint witness indices + // will not trigger basic errors (check inputs are on-curve, check we are not inverting 0) + composer.assert_equal(composer.add_variable(dummy_field), proof_field_idx); + } + for (size_t i = 0; i < input.key.size(); ++i) { + const auto key_field_idx = input.key[i]; + barretenberg::fr dummy_field = + has_valid_witness_assignment ? composer.get_variable(key_field_idx) : dummy_key[i]; + composer.assert_equal(composer.add_variable(dummy_field), key_field_idx); + } + } // The env_crs should be ok, we use this when generating the constraint system in dsl auto env_crs = std::make_unique(); @@ -28,7 +70,11 @@ void create_recursion_constraints(Composer& composer, const RecursionConstraint& // If we have previously recursively verified proofs, `is_aggregation_object_nonzero = true` // For now this is a complile-time constant i.e. whether this is true/false is fixed for the circuit! - if (input.is_aggregation_object_nonzero) { + bool inner_aggregation_indices_all_zero = true; + for (const auto& idx : aggregation_input) { + inner_aggregation_indices_all_zero &= (idx == 0); + } + if (!inner_aggregation_indices_all_zero) { std::array aggregation_elements; for (size_t i = 0; i < 4; ++i) { aggregation_elements[i] = @@ -60,10 +106,11 @@ void create_recursion_constraints(Composer& composer, const RecursionConstraint& } // recursively verify the proof - aggregation_state_ct result = proof_system::plonk::stdlib::recursion::verify_proof( - &composer, manifest, key_fields, proof_fields, previous_aggregation); - - // Assign the output aggregation object to the proof public inputs (16 field elements representing two G1 points) + aggregation_state_ct result = proof_system::plonk::stdlib::recursion:: + verify_proof( + &composer, manifest, key_fields, proof_fields, previous_aggregation); + // Assign the output aggregation object to the proof public inputs (16 field elements representing two + // G1 points) result.add_proof_outputs_as_public_inputs(); ASSERT(result.public_inputs.size() == 1); @@ -79,4 +126,9 @@ void create_recursion_constraints(Composer& composer, const RecursionConstraint& } } +template void create_recursion_constraints(plonk::stdlib::types::Composer&, const RecursionConstraint&); +template void create_recursion_constraints(plonk::stdlib::types::Composer&, const RecursionConstraint&); +template void create_recursion_constraints(plonk::stdlib::types::Composer&, const RecursionConstraint&); +template void create_recursion_constraints(plonk::stdlib::types::Composer&, const RecursionConstraint&); + } // namespace acir_format diff --git a/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.hpp b/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.hpp index 0e794f3f73..f94ec453d4 100644 --- a/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.hpp +++ b/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.hpp @@ -24,12 +24,14 @@ namespace acir_format { * @param public_input The index of the single public input * @param input_aggregation_object Witness indices of pre-existing aggregation object (if it exists) * @param output_aggregation_object Witness indecies of the aggregation object produced by recursive verification + * + * @note If input_aggregation_object witness indices are all zero, we interpret this to mean that the inner proof does + * NOT contai */ struct RecursionConstraint { static constexpr size_t AGGREGATION_OBJECT_SIZE = 16; // 16 field elements std::vector key; std::vector proof; - bool is_aggregation_object_nonzero; uint32_t public_input; std::array input_aggregation_object; std::array output_aggregation_object; @@ -37,14 +39,23 @@ struct RecursionConstraint { friend bool operator==(RecursionConstraint const& lhs, RecursionConstraint const& rhs) = default; }; +template void create_recursion_constraints(plonk::stdlib::types::Composer& composer, const RecursionConstraint& input); +extern template void create_recursion_constraints(plonk::stdlib::types::Composer&, + const RecursionConstraint&); +extern template void create_recursion_constraints(plonk::stdlib::types::Composer&, + const RecursionConstraint&); +extern template void create_recursion_constraints(plonk::stdlib::types::Composer&, + const RecursionConstraint&); +extern template void create_recursion_constraints(plonk::stdlib::types::Composer&, + const RecursionConstraint&); + template inline void read(B& buf, RecursionConstraint& constraint) { using serialize::read; read(buf, constraint.key); read(buf, constraint.proof); - read(buf, constraint.is_aggregation_object_nonzero); read(buf, constraint.public_input); read(buf, constraint.input_aggregation_object); read(buf, constraint.output_aggregation_object); @@ -55,7 +66,6 @@ template inline void write(B& buf, RecursionConstraint const& const using serialize::write; write(buf, constraint.key); write(buf, constraint.proof); - write(buf, constraint.is_aggregation_object_nonzero); write(buf, constraint.public_input); write(buf, constraint.input_aggregation_object); write(buf, constraint.output_aggregation_object); diff --git a/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.test.cpp b/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.test.cpp index f36446e3db..b5d3a9fa69 100644 --- a/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.test.cpp +++ b/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.test.cpp @@ -126,9 +126,6 @@ TEST(RecursionConstraint, TestRecursionConstraint) // variable idx 2-18 = output_vars output_vars[i] = (static_cast(i + 2)); } - // verification_key_data keydata; - // uint8_t const* vk_buf = &keybuf[0]; - // read(vk_buf, keydata); transcript::StandardTranscript transcript( inner_proof.proof_data, Composer::create_manifest(1), transcript::HashType::PlookupPedersenBlake3s, 16); @@ -151,7 +148,6 @@ TEST(RecursionConstraint, TestRecursionConstraint) acir_format::RecursionConstraint recursion_constraint{ .key = key_indices, .proof = proof_indices, - .is_aggregation_object_nonzero = false, .public_input = 1, .input_aggregation_object = {}, .output_aggregation_object = output_vars, diff --git a/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.cpp b/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.cpp index 9b899aa69f..733b652463 100644 --- a/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.cpp +++ b/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.cpp @@ -52,6 +52,8 @@ size_t init_proving_key(uint8_t const* constraint_system_buf, uint8_t const** pk { auto constraint_system = from_buffer(constraint_system_buf); + // constraint_system.recursion_constraints[0]. + // We know that we don't actually need any CRS to create a proving key, so just feed in a nothing. // Hacky, but, right now it needs *something*. auto crs_factory = std::make_unique(); @@ -93,17 +95,81 @@ size_t init_verification_key(void* pippenger, uint8_t const* g2x, uint8_t const* return buffer.size(); } -// void serialize_proof_into_field_elements(uint8_t** proof_data_buf) -// { -// } - -// void serialize_verification_key_into_field_elements(uint8_t** vk_buf) -// { -// plonk::verification_key key; -// read(vk_buf, key); +/** + * @brief Takes in a proof buffer and converts into a vector of field elements. + * The Recursion opcode requires the proof serialized as a vector of witnesses. + * Use this method to get the witness values! + * + * @param proof_data_buf + * @param serialized_proof_data_buf + */ +size_t serialize_proof_into_field_elements(uint8_t const* proof_data_buf, + uint8_t** serialized_proof_data_buf, + size_t proof_data_length) +{ + plonk::proof proof = { std::vector(proof_data_buf, &proof_data_buf[proof_data_length]) }; + + transcript::StandardTranscript transcript( + proof.proof_data, Composer::create_manifest(1), transcript::HashType::PlookupPedersenBlake3s, 16); + + std::vector output = transcript.export_transcript_in_recursion_format(); + + // NOTE: this output buffer will always have a fixed size! Maybe just precompute? + const size_t output_size_bytes = output.size() * sizeof(barretenberg::fr); + auto raw_buf = (uint8_t*)malloc(output_size_bytes); + // NOTE: currently we dump the fr values into memory in Mongtomery form + // This is so we don't have to re-convert into Montgomery form when we read these back in. + // I think this makes it easier to handle the data in barretenberg-sys / aztec-backend. + // If this is not the case, the commented out serialize code will convert out of Montgomery form before writing to + // the buffer + // for (size_t i = 0; i < output.size(); ++i) { + // barretenberg::fr::serialize_to_buffer(output[i], &raw_buf[i * 32]); + // } + // for (size_t i = 0; i < output.size(); ++i) { + // barretenberg::fr::serialize_to_buffer(output[i], &raw_buf[i * 32]); + // } + memcpy(raw_buf, (void*)output.data(), output_size_bytes); + *serialized_proof_data_buf = raw_buf; + + return output_size_bytes; +} -// // do the serialize thing... -// } +/** + * @brief Takes in a verification key buffer and converts into a vector of field elements. + * The Recursion opcode requires the vk serialized as a vector of witnesses. + * Use this method to get the witness values! + * + * @param vk_buf + * @param serialized_vk_buf + */ +size_t serialize_verification_key_into_field_elements(uint8_t const* g2x, + uint8_t const* vk_buf, + uint8_t** serialized_vk_buf) +{ + auto crs = std::make_shared(g2x); + plonk::verification_key_data vk_data; + read(vk_buf, vk_data); + auto vkey = std::make_shared(std::move(vk_data), crs); + + std::vector output = vkey->export_key_in_recursion_format(); + + // NOTE: this output buffer will always have a fixed size! Maybe just precompute? + const size_t output_size_bytes = output.size() * sizeof(barretenberg::fr); + auto raw_buf = (uint8_t*)malloc(output_size_bytes); + // NOTE: currently we dump the fr values into memory in Mongtomery form + // This is so we don't have to re-convert into Montgomery form when we read these back in. + // I think this makes it easier to handle the data in barretenberg-sys / aztec-backend. + // If this is not the case, the commented out serialize code will convert out of Montgomery form before writing to + // the buffer + // for (size_t i = 0; i < output.size(); ++i) { + // barretenberg::fr::serialize_to_buffer(output[i], &raw_buf[i * 32]); + // } + memcpy(raw_buf, (void*)output.data(), output_size_bytes); + + *serialized_vk_buf = raw_buf; + + return output_size_bytes; +} size_t new_proof(void* pippenger, uint8_t const* g2x, @@ -125,12 +191,12 @@ size_t new_proof(void* pippenger, reinterpret_cast(pippenger), g2x); proving_key->reference_string = crs_factory->get_prover_crs(proving_key->circuit_size); - Composer composer(proving_key, nullptr); + auto env_crs = std::make_unique(); - create_circuit_with_witness(composer, constraint_system, witness); + auto composer = acir_format::create_circuit_with_witness(constraint_system, witness); auto prover = composer.create_prover(); - + auto verifier = composer.create_verifier(); auto heapProver = new stdlib::types::Prover(std::move(prover)); auto& proof_data = heapProver->construct_proof().proof_data; *proof_data_buf = proof_data.data(); diff --git a/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.hpp b/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.hpp index 4bd126aae9..180da3fec8 100644 --- a/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.hpp +++ b/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.hpp @@ -8,6 +8,12 @@ uint32_t get_exact_circuit_size(uint8_t const* constraint_system_buf); uint32_t get_total_circuit_size(uint8_t const* constraint_system_buf); size_t init_proving_key(uint8_t const* constraint_system_buf, uint8_t const** pk_buf); size_t init_verification_key(void* pippenger, uint8_t const* g2x, uint8_t const* pk_buf, uint8_t const** vk_buf); +size_t serialize_verification_key_into_field_elements(uint8_t const* g2x, + uint8_t const* vk_buf, + uint8_t** serialized_vk_buf); +size_t serialize_proof_into_field_elements(uint8_t const* proof_data_buf, + uint8_t** serialized_proof_data_buf, + size_t proof_data_length); size_t new_proof(void* pippenger, uint8_t const* g2x, uint8_t const* pk_buf, diff --git a/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.test.cpp b/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.test.cpp index ce28fe62df..a31ad76d52 100644 --- a/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.test.cpp +++ b/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.test.cpp @@ -100,11 +100,10 @@ void create_inner_circuit(acir_format::acir_format& constraint_system, std::vect witness.emplace_back(1); } -static acir_format::acir_format constraint_system; -static std::vector witness; - TEST(AcirProofs, TestSerialization) { + acir_format::acir_format constraint_system; + std::vector witness; create_inner_circuit(constraint_system, witness); std::vector witness_buf; // = to_buffer(witness); @@ -138,3 +137,190 @@ TEST(AcirProofs, TestSerialization) &g2x_buffer[0], vk_buf, &constraint_system_buf[0], proof_data_buf, static_cast(proof_length)); EXPECT_EQ(verified, true); } + +TEST(AcirProofs, TestSerializationWithRecursion) +{ + uint8_t* proof_data_fields = nullptr; + uint8_t* vk_fields = nullptr; + size_t proof_fields_size = 0; + size_t vk_fields_size = 0; + + // inner circuit + { + acir_format::acir_format constraint_system; + std::vector witness; + create_inner_circuit(constraint_system, witness); + + std::vector witness_buf; // = to_buffer(witness); + std::vector constraint_system_buf; // + write(constraint_system_buf, constraint_system); + write(witness_buf, witness); + + uint8_t const* pk_buf = nullptr; + acir_proofs::init_proving_key(&constraint_system_buf[0], &pk_buf); + + uint32_t total_circuit_size = acir_proofs::get_total_circuit_size(&constraint_system_buf[0]); + uint32_t pow2_size = 1 << (numeric::get_msb(total_circuit_size) + 1); + auto env_crs = std::make_unique(); + auto verifier_crs = env_crs->get_verifier_crs(); + barretenberg::g2::affine_element g2x = verifier_crs->get_g2x(); + + auto* pippenger_ptr_base = + new scalar_multiplication::Pippenger(env_load_prover_crs(pow2_size + 1), pow2_size + 1); + void* pippenger_ptr = reinterpret_cast(pippenger_ptr_base); + + std::vector g2x_buffer(128); + io::write_g2_elements_to_buffer(&g2x, (char*)(&g2x_buffer[0]), 1); + + uint8_t const* vk_buf = nullptr; + acir_proofs::init_verification_key(pippenger_ptr, &g2x_buffer[0], pk_buf, &vk_buf); + + uint8_t* proof_data_buf = nullptr; + size_t proof_length = acir_proofs::new_proof( + pippenger_ptr, &g2x_buffer[0], pk_buf, &constraint_system_buf[0], &witness_buf[0], &proof_data_buf); + proof_fields_size = + acir_proofs::serialize_proof_into_field_elements(proof_data_buf, &proof_data_fields, proof_length); + vk_fields_size = + acir_proofs::serialize_verification_key_into_field_elements(&g2x_buffer[0], vk_buf, &vk_fields); + + bool verified = acir_proofs::verify_proof( + &g2x_buffer[0], vk_buf, &constraint_system_buf[0], proof_data_buf, static_cast(proof_length)); + EXPECT_EQ(verified, true); + + delete pippenger_ptr_base; + // free((void*)vk_buf); + // free((void*)pk_buf); + } + // outer circuit + { + std::vector proof_witnesses(proof_fields_size / 32); + std::vector key_witnesses(vk_fields_size / 32); + memcpy(proof_witnesses.data(), (void*)proof_data_fields, proof_fields_size); + memcpy(&key_witnesses[0], (void*)vk_fields, vk_fields_size); + + std::vector proof_indices; + + const size_t proof_size = proof_witnesses.size(); + + for (size_t i = 0; i < proof_size; ++i) { + proof_indices.emplace_back(static_cast(i + 18)); + } + + std::vector key_indices; + const size_t key_size = key_witnesses.size(); + for (size_t i = 0; i < key_size; ++i) { + key_indices.emplace_back(static_cast(i + 18 + proof_size)); + } + + std::array output_vars; + for (size_t i = 0; i < acir_format::RecursionConstraint::AGGREGATION_OBJECT_SIZE; ++i) { + // variable idx 1 = public input + // variable idx 2-18 = output_vars + output_vars[i] = (static_cast(i + 2)); + } + acir_format::RecursionConstraint recursion_constraint{ + .key = key_indices, + .proof = proof_indices, + .public_input = 1, + .input_aggregation_object = {}, + .output_aggregation_object = output_vars, + }; + + // { + // std::vector witness; + // for (size_t i = 0; i < 17; ++i) { + // witness.emplace_back(0); + // } + // for (const auto& wit : proof_witnesses) { + // witness.emplace_back(wit); + // } + // for (const auto& wit : key_witnesses) { + // witness.emplace_back(wit); + // } + + // acir_format::acir_format constraint_system{ + // .varnum = static_cast(witness.size() + 1), + // .public_inputs = { 1 }, + // .fixed_base_scalar_mul_constraints = {}, + // .logic_constraints = {}, + // .range_constraints = {}, + // .schnorr_constraints = {}, + // .ecdsa_constraints = {}, + // .sha256_constraints = {}, + // .blake2s_constraints = {}, + // .hash_to_field_constraints = {}, + // .pedersen_constraints = {}, + // .merkle_membership_constraints = {}, + // .recursion_constraints = { recursion_constraint }, + // .constraints = {}, + // }; + // auto composer = acir_format::create_circuit_with_witness(constraint_system, witness); + // auto prover = composer.create_prover(); + + // auto proof = prover.construct_proof(); + // auto verifier = composer.create_verifier(); + // EXPECT_EQ(verifier.verify_proof(proof), true); + + // EXPECT_EQ(composer.get_variable(1), 10); + + // } + std::vector witness; + for (size_t i = 0; i < 17; ++i) { + witness.emplace_back(0); + } + for (const auto& wit : proof_witnesses) { + witness.emplace_back(wit); + } + for (const auto& wit : key_witnesses) { + witness.emplace_back(wit); + } + + acir_format::acir_format constraint_system{ + .varnum = static_cast(witness.size() + 1), + .public_inputs = { 1 }, + .fixed_base_scalar_mul_constraints = {}, + .logic_constraints = {}, + .range_constraints = {}, + .schnorr_constraints = {}, + .ecdsa_constraints = {}, + .sha256_constraints = {}, + .blake2s_constraints = {}, + .hash_to_field_constraints = {}, + .pedersen_constraints = {}, + .merkle_membership_constraints = {}, + .recursion_constraints = { recursion_constraint }, + .constraints = {}, + }; + + std::vector witness_buf; + std::vector constraint_system_buf; + write(constraint_system_buf, constraint_system); + write(witness_buf, witness); + uint8_t const* pk_buf = nullptr; + acir_proofs::init_proving_key(&constraint_system_buf[0], &pk_buf); + + uint32_t total_circuit_size = acir_proofs::get_total_circuit_size(&constraint_system_buf[0]); + uint32_t pow2_size = 1 << (numeric::get_msb(total_circuit_size) + 1); + auto env_crs = std::make_unique(); + auto verifier_crs = env_crs->get_verifier_crs(); + barretenberg::g2::affine_element g2x = verifier_crs->get_g2x(); + + auto* pippenger_ptr_base = + new scalar_multiplication::Pippenger(env_load_prover_crs(pow2_size + 1), pow2_size + 1); + void* pippenger_ptr = reinterpret_cast(pippenger_ptr_base); + + std::vector g2x_buffer(128); + io::write_g2_elements_to_buffer(&g2x, (char*)(&g2x_buffer[0]), 1); + + uint8_t const* vk_buf = nullptr; + acir_proofs::init_verification_key(pippenger_ptr, &g2x_buffer[0], pk_buf, &vk_buf); + + uint8_t* proof_data_buf = nullptr; + size_t proof_length = acir_proofs::new_proof( + pippenger_ptr, &g2x_buffer[0], pk_buf, &constraint_system_buf[0], &witness_buf[0], &proof_data_buf); + + bool verified = acir_proofs::verify_proof( + &g2x_buffer[0], vk_buf, &constraint_system_buf[0], proof_data_buf, static_cast(proof_length)); + EXPECT_EQ(verified, true); + } +} diff --git a/cpp/src/barretenberg/plonk/proof_system/types/polynomial_manifest.hpp b/cpp/src/barretenberg/plonk/proof_system/types/polynomial_manifest.hpp index aefd348ff9..65911eba38 100644 --- a/cpp/src/barretenberg/plonk/proof_system/types/polynomial_manifest.hpp +++ b/cpp/src/barretenberg/plonk/proof_system/types/polynomial_manifest.hpp @@ -200,7 +200,7 @@ class PolynomialManifest { }; } - const std::vector& get() { return manifest; }; + const std::vector& get() const { return manifest; }; size_t size() const { return manifest.size(); } diff --git a/cpp/src/barretenberg/plonk/proof_system/verification_key/verification_key.cpp b/cpp/src/barretenberg/plonk/proof_system/verification_key/verification_key.cpp index 7ba7af5175..d355e6fc7e 100644 --- a/cpp/src/barretenberg/plonk/proof_system/verification_key/verification_key.cpp +++ b/cpp/src/barretenberg/plonk/proof_system/verification_key/verification_key.cpp @@ -221,4 +221,49 @@ std::vector verification_key::export_key_in_recursion_format() return output; } +/** + * @brief When recursively verifying proofs, we represent the verification key using field elements. + * This method exports the key formatted in the manner our recursive verifier expects. + * A dummy key is used when building a circuit without a valid witness assignment. + * We want the transcript to contain valid G1 points to prevent on-curve errors being thrown. + * We want a non-zero circuit size as this element will be inverted by the circuit + * and we do not want an "inverting 0" error thrown + * + * @return std::vector + */ +std::vector verification_key::export_dummy_key_in_recursion_format( + const PolynomialManifest& polynomial_manifest, const bool contains_recursive_proof) +{ + std::vector output; + output.emplace_back(1); // domain.domain (will be inverted) + output.emplace_back(1); // domain.root (will be inverted) + output.emplace_back(1); // domain.generator (will be inverted) + + output.emplace_back(1); // circuit size + output.emplace_back(1); // num public inputs + + output.emplace_back(contains_recursive_proof); // contains_recursive_proof + for (size_t i = 0; i < 16; ++i) { + output.emplace_back(0); // recursive_proof_public_input_indices + } + + for (const auto& descriptor : polynomial_manifest.get()) { + if (descriptor.source == PolynomialSource::SELECTOR || descriptor.source == PolynomialSource::PERMUTATION) { + const auto element = barretenberg::g1::affine_one; + const uint256_t x = element.x; + const uint256_t y = element.y; + const barretenberg::fr x_lo = x.slice(0, 136); + const barretenberg::fr x_hi = x.slice(136, 272); + const barretenberg::fr y_lo = y.slice(0, 136); + const barretenberg::fr y_hi = y.slice(136, 272); + output.emplace_back(x_lo); + output.emplace_back(x_hi); + output.emplace_back(y_lo); + output.emplace_back(y_hi); + } + } + + return output; +} + } // namespace proof_system::plonk diff --git a/cpp/src/barretenberg/plonk/proof_system/verification_key/verification_key.hpp b/cpp/src/barretenberg/plonk/proof_system/verification_key/verification_key.hpp index aa240f52c9..974ada27b1 100644 --- a/cpp/src/barretenberg/plonk/proof_system/verification_key/verification_key.hpp +++ b/cpp/src/barretenberg/plonk/proof_system/verification_key/verification_key.hpp @@ -64,6 +64,8 @@ struct verification_key { sha256::hash sha256_hash(); std::vector export_key_in_recursion_format(); + static std::vector export_dummy_key_in_recursion_format( + const PolynomialManifest& polynomial_manifest, bool contains_recursive_proof = 0); uint32_t composer_type; size_t circuit_size; diff --git a/cpp/src/barretenberg/stdlib/recursion/verification_key/verification_key.hpp b/cpp/src/barretenberg/stdlib/recursion/verification_key/verification_key.hpp index c297973dbb..48ce7c81ae 100644 --- a/cpp/src/barretenberg/stdlib/recursion/verification_key/verification_key.hpp +++ b/cpp/src/barretenberg/stdlib/recursion/verification_key/verification_key.hpp @@ -29,6 +29,7 @@ template struct evaluation_domain { { evaluation_domain domain; domain.root = fields[0]; + domain.root_inverse = domain.root.invert(); domain.domain = fields[1]; domain.domain_inverse = domain.domain.invert(); @@ -119,8 +120,11 @@ template struct evaluation_domain { template struct verification_key { using Composer = typename Curve::Composer; - static std::shared_ptr from_field_pt_vector(Composer* ctx, - const std::vector>& fields) + template + static std::shared_ptr from_field_pt_vector( + Composer* ctx, + const std::vector>& fields, + std::array recursive_proof_public_input_indices = {}) { std::vector fields_raw; std::shared_ptr key = std::make_shared(); @@ -140,9 +144,9 @@ template struct verification_key { key->recursive_proof_public_input_indices.emplace_back(idx); } // Apply constraints to force the recursive proof information to be circuit constants - fields[5].assert_equal(key->contains_recursive_proof); + fields[5].assert_equal(inner_proof_contains_recursive_proof); for (size_t i = 0; i < 16; ++i) { - fields[6 + i].assert_equal(key->recursive_proof_public_input_indices[i]); + fields[6 + i].assert_equal(recursive_proof_public_input_indices[i]); } size_t count = 22; @@ -160,6 +164,7 @@ template struct verification_key { key->commitments.insert({ std::string(descriptor.commitment_label), element }); } } + return key; } diff --git a/cpp/src/barretenberg/stdlib/recursion/verifier/verifier.hpp b/cpp/src/barretenberg/stdlib/recursion/verifier/verifier.hpp index f3b4004218..b841491bfd 100644 --- a/cpp/src/barretenberg/stdlib/recursion/verifier/verifier.hpp +++ b/cpp/src/barretenberg/stdlib/recursion/verifier/verifier.hpp @@ -196,7 +196,7 @@ aggregation_state verify_proof(typename Curve::Composer* context, * Refer to src/barretenberg/plonk/proof_system/verifier/verifier.cpp verify_proof() for the native implementation, * which includes detailed comments. */ -template +template aggregation_state verify_proof(typename Curve::Composer* context, const transcript::Manifest& manifest, const std::vector& key, @@ -205,7 +205,8 @@ aggregation_state verify_proof(typename Curve::Composer* context, { using Composer = typename Curve::Composer; - std::shared_ptr> vkey = verification_key::from_field_pt_vector(context, key); + std::shared_ptr> vkey = + verification_key::template from_field_pt_vector(context, key); vkey->program_width = program_settings::program_width; const size_t num_public_inputs = static_cast(uint256_t(vkey->num_public_inputs.get_value()).data[0]); diff --git a/cpp/src/barretenberg/transcript/transcript_wrappers.hpp b/cpp/src/barretenberg/transcript/transcript_wrappers.hpp index ab0b02d4c6..ad849889cc 100644 --- a/cpp/src/barretenberg/transcript/transcript_wrappers.hpp +++ b/cpp/src/barretenberg/transcript/transcript_wrappers.hpp @@ -68,7 +68,7 @@ class StandardTranscript : public Transcript { std::vector fields; const auto num_rounds = get_manifest().get_num_rounds(); for (size_t i = 0; i < num_rounds; ++i) { - for (auto manifest_element : get_manifest().get_round_manifest(i).elements) { + for (const auto& manifest_element : get_manifest().get_round_manifest(i).elements) { if (!manifest_element.derived_by_verifier) { if (manifest_element.num_bytes == 32 && manifest_element.name != "public_inputs") { fields.emplace_back(get_field_element(manifest_element.name)); @@ -96,6 +96,47 @@ class StandardTranscript : public Transcript { } return fields; } + + /** + * @brief Get a dummy fake proof for recursion. All elliptic curve group elements are still valid points to prevent + * errors being thrown. + * + * @param manifest + * @return std::vector + */ + static std::vector export_dummy_transcript_in_recursion_format(const Manifest& manifest) + { + std::vector fields; + const auto num_rounds = manifest.get_num_rounds(); + for (size_t i = 0; i < num_rounds; ++i) { + for (const auto& manifest_element : manifest.get_round_manifest(i).elements) { + if (!manifest_element.derived_by_verifier) { + if (manifest_element.num_bytes == 32 && manifest_element.name != "public_inputs") { + fields.emplace_back(0); + } else if (manifest_element.num_bytes == 64 && manifest_element.name != "public_inputs") { + const auto group_element = barretenberg::g1::affine_one; + const uint256_t x = group_element.x; + const uint256_t y = group_element.y; + const barretenberg::fr x_lo = x.slice(0, 136); + const barretenberg::fr x_hi = x.slice(136, 272); + const barretenberg::fr y_lo = y.slice(0, 136); + const barretenberg::fr y_hi = y.slice(136, 272); + fields.emplace_back(x_lo); + fields.emplace_back(x_hi); + fields.emplace_back(y_lo); + fields.emplace_back(y_hi); + } else { + ASSERT(manifest_element.name == "public_inputs"); + const size_t num_public_inputs = manifest_element.num_bytes / 32; + for (size_t j = 0; j < num_public_inputs; ++j) { + fields.emplace_back(0); + } + } + } + } + } + return fields; + } }; } // namespace transcript From 209f81335bd7e3b2f33f5eb5ec4a3b351180b589 Mon Sep 17 00:00:00 2001 From: zac-williamson Date: Tue, 18 Apr 2023 14:54:08 +0200 Subject: [PATCH 07/41] added verificaiton key hash into RecursionConstraint Can be used by backend to force recursive verification key to be a specific value (it is represented via witnesses and therefore is not by-default constraint to represent a specific circuit) --- .../dsl/acir_format/recursion_constraint.cpp | 15 +++- .../dsl/acir_format/recursion_constraint.hpp | 3 + .../acir_format/recursion_constraint.test.cpp | 9 ++- .../dsl/acir_proofs/acir_proofs.cpp | 14 +++- .../dsl/acir_proofs/acir_proofs.hpp | 3 +- .../dsl/acir_proofs/acir_proofs.test.cpp | 81 ++++++++----------- .../verification_key/verification_key.cpp | 10 +++ .../stdlib/recursion/verifier/verifier.hpp | 23 ------ 8 files changed, 77 insertions(+), 81 deletions(-) diff --git a/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.cpp b/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.cpp index d6af1512f3..895c27f99c 100644 --- a/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.cpp +++ b/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.cpp @@ -9,6 +9,8 @@ using namespace proof_system::plonk::stdlib::types; using verification_key_ct = proof_system::plonk::stdlib::recursion::verification_key; using aggregation_state_ct = proof_system::plonk::stdlib::recursion::aggregation_state; using noir_recursive_settings = proof_system::plonk::stdlib::recursion::recursive_ultra_verifier_settings; +using Transcript_ct = proof_system::plonk::stdlib::recursion::Transcript; + namespace acir_format { void generate_dummy_proof() {} @@ -106,9 +108,16 @@ void create_recursion_constraints(Composer& composer, const RecursionConstraint& } // recursively verify the proof - aggregation_state_ct result = proof_system::plonk::stdlib::recursion:: - verify_proof( - &composer, manifest, key_fields, proof_fields, previous_aggregation); + std::shared_ptr vkey = + verification_key_ct::template from_field_pt_vector(&composer, key_fields); + vkey->program_width = noir_recursive_settings::program_width; + Transcript_ct transcript(&composer, manifest, proof_fields, 1); + aggregation_state_ct result = proof_system::plonk::stdlib::recursion::verify_proof_( + &composer, vkey, transcript, previous_aggregation); + + // Assign correct witness value to the verification key hash + vkey->compress().assert_equal(field_ct::from_witness_index(&composer, input.key_hash)); + // Assign the output aggregation object to the proof public inputs (16 field elements representing two // G1 points) result.add_proof_outputs_as_public_inputs(); diff --git a/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.hpp b/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.hpp index f94ec453d4..3a24727d01 100644 --- a/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.hpp +++ b/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.hpp @@ -33,6 +33,7 @@ struct RecursionConstraint { std::vector key; std::vector proof; uint32_t public_input; + uint32_t key_hash; std::array input_aggregation_object; std::array output_aggregation_object; @@ -57,6 +58,7 @@ template inline void read(B& buf, RecursionConstraint& constraint) read(buf, constraint.key); read(buf, constraint.proof); read(buf, constraint.public_input); + read(buf, constraint.key_hash); read(buf, constraint.input_aggregation_object); read(buf, constraint.output_aggregation_object); } @@ -67,6 +69,7 @@ template inline void write(B& buf, RecursionConstraint const& const write(buf, constraint.key); write(buf, constraint.proof); write(buf, constraint.public_input); + write(buf, constraint.key_hash); write(buf, constraint.input_aggregation_object); write(buf, constraint.output_aggregation_object); } diff --git a/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.test.cpp b/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.test.cpp index b5d3a9fa69..cfd564f7f6 100644 --- a/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.test.cpp +++ b/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.test.cpp @@ -124,7 +124,7 @@ TEST(RecursionConstraint, TestRecursionConstraint) for (size_t i = 0; i < 16; ++i) { // variable idx 1 = public input // variable idx 2-18 = output_vars - output_vars[i] = (static_cast(i + 2)); + output_vars[i] = (static_cast(i + 3)); } transcript::StandardTranscript transcript( @@ -137,24 +137,25 @@ TEST(RecursionConstraint, TestRecursionConstraint) const size_t proof_size = proof_witnesses.size(); for (size_t i = 0; i < proof_size; ++i) { - proof_indices.emplace_back(static_cast(i + 18)); + proof_indices.emplace_back(static_cast(i + 19)); } std::vector key_indices; const size_t key_size = key_witnesses.size(); for (size_t i = 0; i < key_size; ++i) { - key_indices.emplace_back(static_cast(i + 18 + proof_size)); + key_indices.emplace_back(static_cast(i + 19 + proof_size)); } acir_format::RecursionConstraint recursion_constraint{ .key = key_indices, .proof = proof_indices, .public_input = 1, + .key_hash = 2, .input_aggregation_object = {}, .output_aggregation_object = output_vars, }; std::vector witness; - for (size_t i = 0; i < 17; ++i) { + for (size_t i = 0; i < 18; ++i) { witness.emplace_back(0); } for (const auto& wit : proof_witnesses) { diff --git a/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.cpp b/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.cpp index 733b652463..4d01512eca 100644 --- a/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.cpp +++ b/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.cpp @@ -144,7 +144,8 @@ size_t serialize_proof_into_field_elements(uint8_t const* proof_data_buf, */ size_t serialize_verification_key_into_field_elements(uint8_t const* g2x, uint8_t const* vk_buf, - uint8_t** serialized_vk_buf) + uint8_t** serialized_vk_buf, + uint8_t** serialized_vk_hash_buf) { auto crs = std::make_shared(g2x); plonk::verification_key_data vk_data; @@ -154,7 +155,8 @@ size_t serialize_verification_key_into_field_elements(uint8_t const* g2x, std::vector output = vkey->export_key_in_recursion_format(); // NOTE: this output buffer will always have a fixed size! Maybe just precompute? - const size_t output_size_bytes = output.size() * sizeof(barretenberg::fr); + // Cut off 32 bytes as last element is the verification key hash which is not part of the key :o + const size_t output_size_bytes = output.size() * sizeof(barretenberg::fr) - 32; auto raw_buf = (uint8_t*)malloc(output_size_bytes); // NOTE: currently we dump the fr values into memory in Mongtomery form // This is so we don't have to re-convert into Montgomery form when we read these back in. @@ -164,10 +166,16 @@ size_t serialize_verification_key_into_field_elements(uint8_t const* g2x, // for (size_t i = 0; i < output.size(); ++i) { // barretenberg::fr::serialize_to_buffer(output[i], &raw_buf[i * 32]); // } - memcpy(raw_buf, (void*)output.data(), output_size_bytes); + // copy all but the vkey hash into raw_buf + memcpy(raw_buf, (void*)output.data(), output_size_bytes); *serialized_vk_buf = raw_buf; + // copy the vkey hash into vk_hash_raw_buf + auto vk_hash_raw_buf = (uint8_t*)malloc(32); + memcpy(vk_hash_raw_buf, (void*)&output[output.size() - 1], 32); + *serialized_vk_hash_buf = vk_hash_raw_buf; + return output_size_bytes; } diff --git a/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.hpp b/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.hpp index 180da3fec8..ced41993c3 100644 --- a/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.hpp +++ b/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.hpp @@ -10,7 +10,8 @@ size_t init_proving_key(uint8_t const* constraint_system_buf, uint8_t const** pk size_t init_verification_key(void* pippenger, uint8_t const* g2x, uint8_t const* pk_buf, uint8_t const** vk_buf); size_t serialize_verification_key_into_field_elements(uint8_t const* g2x, uint8_t const* vk_buf, - uint8_t** serialized_vk_buf); + uint8_t** serialized_vk_buf, + uint8_t** serialized_vk_hash_buf); size_t serialize_proof_into_field_elements(uint8_t const* proof_data_buf, uint8_t** serialized_proof_data_buf, size_t proof_data_length); diff --git a/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.test.cpp b/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.test.cpp index a31ad76d52..de011de21e 100644 --- a/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.test.cpp +++ b/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.test.cpp @@ -142,6 +142,7 @@ TEST(AcirProofs, TestSerializationWithRecursion) { uint8_t* proof_data_fields = nullptr; uint8_t* vk_fields = nullptr; + uint8_t* vk_hash_buf = nullptr; size_t proof_fields_size = 0; size_t vk_fields_size = 0; @@ -180,92 +181,70 @@ TEST(AcirProofs, TestSerializationWithRecursion) pippenger_ptr, &g2x_buffer[0], pk_buf, &constraint_system_buf[0], &witness_buf[0], &proof_data_buf); proof_fields_size = acir_proofs::serialize_proof_into_field_elements(proof_data_buf, &proof_data_fields, proof_length); - vk_fields_size = - acir_proofs::serialize_verification_key_into_field_elements(&g2x_buffer[0], vk_buf, &vk_fields); + vk_fields_size = acir_proofs::serialize_verification_key_into_field_elements( + &g2x_buffer[0], vk_buf, &vk_fields, &vk_hash_buf); bool verified = acir_proofs::verify_proof( &g2x_buffer[0], vk_buf, &constraint_system_buf[0], proof_data_buf, static_cast(proof_length)); EXPECT_EQ(verified, true); delete pippenger_ptr_base; - // free((void*)vk_buf); - // free((void*)pk_buf); + free((void*)vk_buf); + free((void*)pk_buf); + free((void*)proof_data_buf); } // outer circuit { + fr vk_hash_value; std::vector proof_witnesses(proof_fields_size / 32); std::vector key_witnesses(vk_fields_size / 32); memcpy(proof_witnesses.data(), (void*)proof_data_fields, proof_fields_size); memcpy(&key_witnesses[0], (void*)vk_fields, vk_fields_size); + memcpy(&vk_hash_value, (void*)vk_hash_buf, 32); std::vector proof_indices; const size_t proof_size = proof_witnesses.size(); for (size_t i = 0; i < proof_size; ++i) { - proof_indices.emplace_back(static_cast(i + 18)); + proof_indices.emplace_back(static_cast(i + 19)); } std::vector key_indices; const size_t key_size = key_witnesses.size(); for (size_t i = 0; i < key_size; ++i) { - key_indices.emplace_back(static_cast(i + 18 + proof_size)); + key_indices.emplace_back(static_cast(i + 19 + proof_size)); } std::array output_vars; for (size_t i = 0; i < acir_format::RecursionConstraint::AGGREGATION_OBJECT_SIZE; ++i) { // variable idx 1 = public input // variable idx 2-18 = output_vars - output_vars[i] = (static_cast(i + 2)); + output_vars[i] = (static_cast(i + 3)); } acir_format::RecursionConstraint recursion_constraint{ .key = key_indices, .proof = proof_indices, .public_input = 1, + .key_hash = 2, .input_aggregation_object = {}, .output_aggregation_object = output_vars, }; - // { - // std::vector witness; - // for (size_t i = 0; i < 17; ++i) { - // witness.emplace_back(0); - // } - // for (const auto& wit : proof_witnesses) { - // witness.emplace_back(wit); - // } - // for (const auto& wit : key_witnesses) { - // witness.emplace_back(wit); - // } - - // acir_format::acir_format constraint_system{ - // .varnum = static_cast(witness.size() + 1), - // .public_inputs = { 1 }, - // .fixed_base_scalar_mul_constraints = {}, - // .logic_constraints = {}, - // .range_constraints = {}, - // .schnorr_constraints = {}, - // .ecdsa_constraints = {}, - // .sha256_constraints = {}, - // .blake2s_constraints = {}, - // .hash_to_field_constraints = {}, - // .pedersen_constraints = {}, - // .merkle_membership_constraints = {}, - // .recursion_constraints = { recursion_constraint }, - // .constraints = {}, - // }; - // auto composer = acir_format::create_circuit_with_witness(constraint_system, witness); - // auto prover = composer.create_prover(); - - // auto proof = prover.construct_proof(); - // auto verifier = composer.create_verifier(); - // EXPECT_EQ(verifier.verify_proof(proof), true); - - // EXPECT_EQ(composer.get_variable(1), 10); - - // } + // Add a constraint that fixes the vk hash to be the expected value! + poly_triple vk_equality_constraint{ + .a = recursion_constraint.key_hash, + .b = 0, + .c = 0, + .q_m = 0, + .q_l = 1, + .q_r = 0, + .q_o = 0, + .q_c = -vk_hash_value, + }; + std::vector witness; - for (size_t i = 0; i < 17; ++i) { + for (size_t i = 0; i < 18; ++i) { witness.emplace_back(0); } for (const auto& wit : proof_witnesses) { @@ -289,7 +268,7 @@ TEST(AcirProofs, TestSerializationWithRecursion) .pedersen_constraints = {}, .merkle_membership_constraints = {}, .recursion_constraints = { recursion_constraint }, - .constraints = {}, + .constraints = { vk_equality_constraint }, }; std::vector witness_buf; @@ -322,5 +301,13 @@ TEST(AcirProofs, TestSerializationWithRecursion) bool verified = acir_proofs::verify_proof( &g2x_buffer[0], vk_buf, &constraint_system_buf[0], proof_data_buf, static_cast(proof_length)); EXPECT_EQ(verified, true); + + delete pippenger_ptr_base; + free((void*)vk_buf); + free((void*)pk_buf); + free((void*)proof_data_buf); + free((void*)proof_data_fields); + free((void*)vk_fields); + free((void*)vk_hash_buf); } } diff --git a/cpp/src/barretenberg/plonk/proof_system/verification_key/verification_key.cpp b/cpp/src/barretenberg/plonk/proof_system/verification_key/verification_key.cpp index d355e6fc7e..743f563770 100644 --- a/cpp/src/barretenberg/plonk/proof_system/verification_key/verification_key.cpp +++ b/cpp/src/barretenberg/plonk/proof_system/verification_key/verification_key.cpp @@ -218,6 +218,15 @@ std::vector verification_key::export_key_in_recursion_format() } } + verification_key_data vkey_data{ + .composer_type = composer_type, + .circuit_size = static_cast(circuit_size), + .num_public_inputs = static_cast(num_public_inputs), + .commitments = commitments, + .contains_recursive_proof = contains_recursive_proof, + .recursive_proof_public_input_indices = recursive_proof_public_input_indices, + }; + output.emplace_back(vkey_data.compress_native(0)); // key_hash return output; } @@ -262,6 +271,7 @@ std::vector verification_key::export_dummy_key_in_recursion_fo output.emplace_back(y_hi); } } + output.emplace_back(0); // key_hash return output; } diff --git a/cpp/src/barretenberg/stdlib/recursion/verifier/verifier.hpp b/cpp/src/barretenberg/stdlib/recursion/verifier/verifier.hpp index b841491bfd..a99325cc86 100644 --- a/cpp/src/barretenberg/stdlib/recursion/verifier/verifier.hpp +++ b/cpp/src/barretenberg/stdlib/recursion/verifier/verifier.hpp @@ -192,29 +192,6 @@ aggregation_state verify_proof(typename Curve::Composer* context, return verify_proof_(context, key, transcript, previous_output); } -/** - * Refer to src/barretenberg/plonk/proof_system/verifier/verifier.cpp verify_proof() for the native implementation, - * which includes detailed comments. - */ -template -aggregation_state verify_proof(typename Curve::Composer* context, - const transcript::Manifest& manifest, - const std::vector& key, - const std::vector& proof, - const aggregation_state previous_output = aggregation_state()) -{ - using Composer = typename Curve::Composer; - - std::shared_ptr> vkey = - verification_key::template from_field_pt_vector(context, key); - vkey->program_width = program_settings::program_width; - - const size_t num_public_inputs = static_cast(uint256_t(vkey->num_public_inputs.get_value()).data[0]); - Transcript transcript = Transcript(context, manifest, proof, num_public_inputs); - - return verify_proof_(context, vkey, transcript, previous_output); -} - /** * Refer to src/barretenberg/plonk/proof_system/verifier/verifier.cpp verify_proof() for the native implementation, * which includes detailed comments. From 6496228698d9cfb1ac2eebc28518f495096bf3e3 Mon Sep 17 00:00:00 2001 From: zac-williamson Date: Tue, 18 Apr 2023 20:13:47 +0200 Subject: [PATCH 08/41] fixed compiler errors in stdlib_recursion_tests --- .../dsl/acir_format/recursion_constraint.hpp | 2 +- .../recursion/verifier/verifier.test.cpp | 22 +++++++++++++------ .../verifier/verifier_turbo.test.cpp | 20 ++++++++++++----- 3 files changed, 30 insertions(+), 14 deletions(-) diff --git a/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.hpp b/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.hpp index 3a24727d01..45abd037c2 100644 --- a/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.hpp +++ b/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.hpp @@ -26,7 +26,7 @@ namespace acir_format { * @param output_aggregation_object Witness indecies of the aggregation object produced by recursive verification * * @note If input_aggregation_object witness indices are all zero, we interpret this to mean that the inner proof does - * NOT contai + * NOT contain */ struct RecursionConstraint { static constexpr size_t AGGREGATION_OBJECT_SIZE = 16; // 16 field elements diff --git a/cpp/src/barretenberg/stdlib/recursion/verifier/verifier.test.cpp b/cpp/src/barretenberg/stdlib/recursion/verifier/verifier.test.cpp index 82e8042b02..ad28907bfb 100644 --- a/cpp/src/barretenberg/stdlib/recursion/verifier/verifier.test.cpp +++ b/cpp/src/barretenberg/stdlib/recursion/verifier/verifier.test.cpp @@ -277,6 +277,7 @@ template class stdlib_verifier : public testing::Test { public: static void test_recursive_proof_composition() { + auto env_crs = std::make_unique(); InnerComposer inner_composer = InnerComposer("../srs_db/ignition"); OuterComposer outer_composer = OuterComposer("../srs_db/ignition"); std::vector inner_inputs{ barretenberg::fr::random_element(), @@ -294,7 +295,7 @@ template class stdlib_verifier : public testing::Test { P[1].y = barretenberg::fq(circuit_output.aggregation_state.P1.y.get_value().lo); barretenberg::fq12 inner_proof_result = barretenberg::pairing::reduced_ate_pairing_batch_precomputed( - P, circuit_output.verification_key->reference_string->get_precomputed_g2_lines(), 2); + P, env_crs->get_verifier_crs()->get_precomputed_g2_lines(), 2); EXPECT_EQ(circuit_output.aggregation_state.public_inputs[0].get_value(), inner_inputs[0]); EXPECT_EQ(circuit_output.aggregation_state.public_inputs[1].get_value(), inner_inputs[1]); @@ -326,6 +327,8 @@ template class stdlib_verifier : public testing::Test { static void test_recursive_proof_composition_ultra_no_tables() { + auto env_crs = std::make_unique(); + InnerComposer inner_composer = InnerComposer("../srs_db/ignition"); OuterComposer outer_composer = OuterComposer("../srs_db/ignition"); @@ -342,7 +345,7 @@ template class stdlib_verifier : public testing::Test { P[1].y = barretenberg::fq(circuit_output.aggregation_state.P1.y.get_value().lo); barretenberg::fq12 inner_proof_result = barretenberg::pairing::reduced_ate_pairing_batch_precomputed( - P, circuit_output.verification_key->reference_string->get_precomputed_g2_lines(), 2); + P, env_crs->get_verifier_crs()->get_precomputed_g2_lines(), 2); EXPECT_EQ(inner_proof_result, barretenberg::fq12::one()); @@ -375,6 +378,7 @@ template class stdlib_verifier : public testing::Test { return; // We only care about running this test for turbo and ultra outer circuits, since in practice the // only circuits which verify >1 proof are ultra or turbo circuits. Standard uses so many gates // (16m) that it's a waste of time testing it. + auto env_crs = std::make_unique(); InnerComposer inner_composer_a = InnerComposer("../srs_db/ignition"); InnerComposer inner_composer_b = InnerComposer("../srs_db/ignition"); @@ -396,7 +400,7 @@ template class stdlib_verifier : public testing::Test { P[1].x = barretenberg::fq(circuit_output.aggregation_state.P1.x.get_value().lo); P[1].y = barretenberg::fq(circuit_output.aggregation_state.P1.y.get_value().lo); barretenberg::fq12 inner_proof_result = barretenberg::pairing::reduced_ate_pairing_batch_precomputed( - P, circuit_output.verification_key->reference_string->get_precomputed_g2_lines(), 2); + P, env_crs->get_verifier_crs()->get_precomputed_g2_lines(), 2); EXPECT_EQ(circuit_output.aggregation_state.public_inputs[0].get_value(), inner_inputs[0]); EXPECT_EQ(circuit_output.aggregation_state.public_inputs[1].get_value(), inner_inputs[1]); @@ -434,6 +438,7 @@ template class stdlib_verifier : public testing::Test { std::vector inner_inputs_b{ barretenberg::fr::random_element(), barretenberg::fr::random_element(), barretenberg::fr::random_element() }; + auto env_crs = std::make_unique(); create_inner_circuit(inner_composer_a, inner_inputs_a); create_alternate_inner_circuit(inner_composer_b, inner_inputs_b); @@ -447,7 +452,7 @@ template class stdlib_verifier : public testing::Test { P[1].x = barretenberg::fq(circuit_output.aggregation_state.P1.x.get_value().lo); P[1].y = barretenberg::fq(circuit_output.aggregation_state.P1.y.get_value().lo); barretenberg::fq12 inner_proof_result = barretenberg::pairing::reduced_ate_pairing_batch_precomputed( - P, circuit_output.verification_key->reference_string->get_precomputed_g2_lines(), 2); + P, env_crs->get_verifier_crs()->get_precomputed_g2_lines(), 2); EXPECT_EQ(circuit_output.aggregation_state.public_inputs[0].get_value(), inner_inputs_a[0]); EXPECT_EQ(circuit_output.aggregation_state.public_inputs[1].get_value(), inner_inputs_a[1]); @@ -487,6 +492,7 @@ template class stdlib_verifier : public testing::Test { auto circuit_output = create_outer_circuit_with_variable_inner_circuit(inner_composer_a, inner_composer_b, outer_composer, false); g1::affine_element P[2]; + auto env_crs = std::make_unique(); P[0].x = barretenberg::fq(circuit_output.aggregation_state.P0.x.get_value().lo); P[0].y = barretenberg::fq(circuit_output.aggregation_state.P0.y.get_value().lo); @@ -494,7 +500,7 @@ template class stdlib_verifier : public testing::Test { P[1].y = barretenberg::fq(circuit_output.aggregation_state.P1.y.get_value().lo); barretenberg::fq12 inner_proof_result = barretenberg::pairing::reduced_ate_pairing_batch_precomputed( - P, circuit_output.verification_key->reference_string->get_precomputed_g2_lines(), 2); + P, env_crs->get_verifier_crs()->get_precomputed_g2_lines(), 2); EXPECT_EQ(circuit_output.aggregation_state.public_inputs[0].get_value(), inner_inputs_b[0]); EXPECT_EQ(circuit_output.aggregation_state.public_inputs[1].get_value(), inner_inputs_b[1]); @@ -529,6 +535,7 @@ template class stdlib_verifier : public testing::Test { create_inner_circuit(inner_composer_a, inner_inputs_a); create_alternate_inner_circuit(inner_composer_b, inner_inputs_b); + auto env_crs = std::make_unique(); auto circuit_output = create_outer_circuit_with_variable_inner_circuit( inner_composer_a, inner_composer_b, outer_composer, true, true); @@ -539,7 +546,7 @@ template class stdlib_verifier : public testing::Test { P[1].x = barretenberg::fq(circuit_output.aggregation_state.P1.x.get_value().lo); P[1].y = barretenberg::fq(circuit_output.aggregation_state.P1.y.get_value().lo); barretenberg::fq12 inner_proof_result = barretenberg::pairing::reduced_ate_pairing_batch_precomputed( - P, circuit_output.verification_key->reference_string->get_precomputed_g2_lines(), 2); + P, env_crs->get_verifier_crs()->get_precomputed_g2_lines(), 2); EXPECT_EQ(circuit_output.aggregation_state.public_inputs[0].get_value(), inner_inputs_a[0]); EXPECT_EQ(circuit_output.aggregation_state.public_inputs[1].get_value(), inner_inputs_a[1]); @@ -561,6 +568,7 @@ template class stdlib_verifier : public testing::Test { static void test_recursive_proof_composition_with_constant_verification_key() { + auto env_crs = std::make_unique(); InnerComposer inner_composer_a = InnerComposer("../srs_db/ignition"); InnerComposer inner_composer_b = InnerComposer("../srs_db/ignition"); OuterComposer outer_composer = OuterComposer("../srs_db/ignition"); @@ -584,7 +592,7 @@ template class stdlib_verifier : public testing::Test { P[1].x = barretenberg::fq(circuit_output.aggregation_state.P1.x.get_value().lo); P[1].y = barretenberg::fq(circuit_output.aggregation_state.P1.y.get_value().lo); barretenberg::fq12 inner_proof_result = barretenberg::pairing::reduced_ate_pairing_batch_precomputed( - P, circuit_output.verification_key->reference_string->get_precomputed_g2_lines(), 2); + P, env_crs->get_verifier_crs()->get_precomputed_g2_lines(), 2); EXPECT_EQ(circuit_output.aggregation_state.public_inputs[0].get_value(), inner_inputs_a[0]); EXPECT_EQ(circuit_output.aggregation_state.public_inputs[1].get_value(), inner_inputs_a[1]); diff --git a/cpp/src/barretenberg/stdlib/recursion/verifier/verifier_turbo.test.cpp b/cpp/src/barretenberg/stdlib/recursion/verifier/verifier_turbo.test.cpp index 1f4410148b..fee90bd030 100644 --- a/cpp/src/barretenberg/stdlib/recursion/verifier/verifier_turbo.test.cpp +++ b/cpp/src/barretenberg/stdlib/recursion/verifier/verifier_turbo.test.cpp @@ -165,6 +165,7 @@ template class stdlib_verifier_turbo : public testing:: public: static void test_recursive_proof_composition() { + auto env_crs = std::make_unique(); InnerComposer inner_composer = InnerComposer("../srs_db/ignition"); OuterComposer outer_composer = OuterComposer("../srs_db/ignition"); std::vector inner_inputs{ barretenberg::fr::random_element(), @@ -181,7 +182,7 @@ template class stdlib_verifier_turbo : public testing:: P[1].x = barretenberg::fq(circuit_output.aggregation_state.P1.x.get_value().lo); P[1].y = barretenberg::fq(circuit_output.aggregation_state.P1.y.get_value().lo); barretenberg::fq12 inner_proof_result = barretenberg::pairing::reduced_ate_pairing_batch_precomputed( - P, circuit_output.verification_key->reference_string->get_precomputed_g2_lines(), 2); + P, env_crs->get_verifier_crs()->get_precomputed_g2_lines(), 2); EXPECT_EQ(circuit_output.aggregation_state.public_inputs[0].get_value(), inner_inputs[0]); EXPECT_EQ(circuit_output.aggregation_state.public_inputs[1].get_value(), inner_inputs[1]); @@ -209,6 +210,8 @@ template class stdlib_verifier_turbo : public testing:: static void test_double_verification() { + auto env_crs = std::make_unique(); + if constexpr (std::is_same::value) return; // We only care about running this test for turbo and ultra outer circuits, since in practice the // only circuits which verify >1 proof are ultra or turbo circuits. Standard uses so many gates @@ -234,7 +237,7 @@ template class stdlib_verifier_turbo : public testing:: P[1].x = barretenberg::fq(circuit_output.aggregation_state.P1.x.get_value().lo); P[1].y = barretenberg::fq(circuit_output.aggregation_state.P1.y.get_value().lo); barretenberg::fq12 inner_proof_result = barretenberg::pairing::reduced_ate_pairing_batch_precomputed( - P, circuit_output.verification_key->reference_string->get_precomputed_g2_lines(), 2); + P, env_crs->get_verifier_crs()->get_precomputed_g2_lines(), 2); EXPECT_EQ(circuit_output.aggregation_state.public_inputs[0].get_value(), inner_inputs[0]); EXPECT_EQ(circuit_output.aggregation_state.public_inputs[1].get_value(), inner_inputs[1]); @@ -262,6 +265,8 @@ template class stdlib_verifier_turbo : public testing:: // variable circuits static void test_recursive_proof_composition_with_variable_verification_key_a() { + auto env_crs = std::make_unique(); + InnerComposer inner_composer_a = InnerComposer("../srs_db/ignition"); InnerComposer inner_composer_b = InnerComposer("../srs_db/ignition"); OuterComposer outer_composer = OuterComposer("../srs_db/ignition"); @@ -285,7 +290,7 @@ template class stdlib_verifier_turbo : public testing:: P[1].x = barretenberg::fq(circuit_output.aggregation_state.P1.x.get_value().lo); P[1].y = barretenberg::fq(circuit_output.aggregation_state.P1.y.get_value().lo); barretenberg::fq12 inner_proof_result = barretenberg::pairing::reduced_ate_pairing_batch_precomputed( - P, circuit_output.verification_key->reference_string->get_precomputed_g2_lines(), 2); + P, env_crs->get_verifier_crs()->get_precomputed_g2_lines(), 2); EXPECT_EQ(circuit_output.aggregation_state.public_inputs[0].get_value(), inner_inputs_a[0]); EXPECT_EQ(circuit_output.aggregation_state.public_inputs[1].get_value(), inner_inputs_a[1]); @@ -309,6 +314,7 @@ template class stdlib_verifier_turbo : public testing:: // variable circuits static void test_recursive_proof_composition_with_variable_verification_key_b() { + auto env_crs = std::make_unique(); InnerComposer inner_composer_a = InnerComposer("../srs_db/ignition"); InnerComposer inner_composer_b = InnerComposer("../srs_db/ignition"); OuterComposer outer_composer = OuterComposer("../srs_db/ignition"); @@ -332,7 +338,7 @@ template class stdlib_verifier_turbo : public testing:: P[1].y = barretenberg::fq(circuit_output.aggregation_state.P1.y.get_value().lo); barretenberg::fq12 inner_proof_result = barretenberg::pairing::reduced_ate_pairing_batch_precomputed( - P, circuit_output.verification_key->reference_string->get_precomputed_g2_lines(), 2); + P, env_crs->get_verifier_crs()->get_precomputed_g2_lines(), 2); EXPECT_EQ(circuit_output.aggregation_state.public_inputs[0].get_value(), inner_inputs_b[0]); EXPECT_EQ(circuit_output.aggregation_state.public_inputs[1].get_value(), inner_inputs_b[1]); @@ -354,6 +360,7 @@ template class stdlib_verifier_turbo : public testing:: static void test_recursive_proof_composition_with_variable_verification_key_failure_case() { + auto env_crs = std::make_unique(); InnerComposer inner_composer_a = InnerComposer("../srs_db/ignition"); InnerComposer inner_composer_b = InnerComposer("../srs_db/ignition"); OuterComposer outer_composer = OuterComposer("../srs_db/ignition"); @@ -377,7 +384,7 @@ template class stdlib_verifier_turbo : public testing:: P[1].x = barretenberg::fq(circuit_output.aggregation_state.P1.x.get_value().lo); P[1].y = barretenberg::fq(circuit_output.aggregation_state.P1.y.get_value().lo); barretenberg::fq12 inner_proof_result = barretenberg::pairing::reduced_ate_pairing_batch_precomputed( - P, circuit_output.verification_key->reference_string->get_precomputed_g2_lines(), 2); + P, env_crs->get_verifier_crs()->get_precomputed_g2_lines(), 2); EXPECT_EQ(circuit_output.aggregation_state.public_inputs[0].get_value(), inner_inputs_a[0]); EXPECT_EQ(circuit_output.aggregation_state.public_inputs[1].get_value(), inner_inputs_a[1]); @@ -399,6 +406,7 @@ template class stdlib_verifier_turbo : public testing:: static void test_recursive_proof_composition_with_constant_verification_key() { + auto env_crs = std::make_unique(); InnerComposer inner_composer_a = InnerComposer("../srs_db/ignition"); InnerComposer inner_composer_b = InnerComposer("../srs_db/ignition"); OuterComposer outer_composer = OuterComposer("../srs_db/ignition"); @@ -422,7 +430,7 @@ template class stdlib_verifier_turbo : public testing:: P[1].x = barretenberg::fq(circuit_output.aggregation_state.P1.x.get_value().lo); P[1].y = barretenberg::fq(circuit_output.aggregation_state.P1.y.get_value().lo); barretenberg::fq12 inner_proof_result = barretenberg::pairing::reduced_ate_pairing_batch_precomputed( - P, circuit_output.verification_key->reference_string->get_precomputed_g2_lines(), 2); + P, env_crs->get_verifier_crs()->get_precomputed_g2_lines(), 2); EXPECT_EQ(circuit_output.aggregation_state.public_inputs[0].get_value(), inner_inputs_a[0]); EXPECT_EQ(circuit_output.aggregation_state.public_inputs[1].get_value(), inner_inputs_a[1]); From 5f21e220586ada20a376684b6e52cd6d5d4be90b Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Tue, 25 Apr 2023 14:14:03 +0100 Subject: [PATCH 09/41] feat(dsl)!: Noir recursion updates (#379) * merge master in zw/noir-recursion * add dsl files to commit * namespace stuff and debugging * c bind functions * constraint test and comment removal * revert some changes to RecursionConstraint while debugging serialization issues * dispaly error when trying to use create_circuit_with_witness * is_recursive flag as part of new_proof and verify_proof, new RecursiveProver type in dsl, and serialization changes for recursion acir_proofs methods * remove debug output from TestSerializationWithRecursion --- cpp/CMakeLists.txt | 8 +- .../crypto/generators/generator_data.cpp | 10 +- .../dsl/acir_format/acir_format.cpp | 3 +- .../dsl/acir_format/acir_format.hpp | 23 +- .../dsl/acir_format/acir_format.test.cpp | 52 +- .../dsl/acir_format/blake2s_constraint.cpp | 2 - .../dsl/acir_format/blake2s_constraint.hpp | 4 +- .../dsl/acir_format/ecdsa_secp256k1.cpp | 4 +- .../dsl/acir_format/ecdsa_secp256k1.hpp | 4 +- .../dsl/acir_format/ecdsa_secp256k1.test.cpp | 4 +- .../dsl/acir_format/fixed_base_scalar_mul.cpp | 2 - .../dsl/acir_format/fixed_base_scalar_mul.hpp | 4 +- .../dsl/acir_format/hash_to_field.cpp | 6 +- .../dsl/acir_format/hash_to_field.hpp | 4 +- .../dsl/acir_format/logic_constraint.cpp | 4 +- .../dsl/acir_format/logic_constraint.hpp | 13 +- .../merkle_membership_constraint.cpp | 7 +- .../merkle_membership_constraint.hpp | 5 +- .../barretenberg/dsl/acir_format/pedersen.cpp | 9 +- .../barretenberg/dsl/acir_format/pedersen.hpp | 4 +- .../dsl/acir_format/recursion_constraint.cpp | 19 +- .../dsl/acir_format/recursion_constraint.hpp | 19 +- .../acir_format/recursion_constraint.test.cpp | 10 +- .../dsl/acir_format/schnorr_verify.cpp | 9 +- .../dsl/acir_format/schnorr_verify.hpp | 4 +- .../dsl/acir_format/sha256_constraint.cpp | 3 +- .../dsl/acir_format/sha256_constraint.hpp | 4 +- .../dsl/acir_proofs/acir_proofs.cpp | 106 +- .../dsl/acir_proofs/acir_proofs.hpp | 11 +- .../dsl/acir_proofs/acir_proofs.test.cpp | 40 +- .../barretenberg/dsl/acir_proofs/c_bind.cpp | 32 +- .../barretenberg/dsl/acir_proofs/c_bind.hpp | 18 +- cpp/src/barretenberg/dsl/types.hpp | 74 ++ cpp/src/barretenberg/ecc/fields/field.hpp | 4 + .../standard_honk_composer_helper.cpp | 1 + .../ultra_honk_composer_helper.cpp | 356 ++++++ .../ultra_honk_composer_helper.hpp | 72 ++ .../composer/standard_honk_composer.test.cpp | 103 -- .../honk/composer/ultra_honk_composer.hpp | 470 ++++++++ .../composer/ultra_honk_composer.test.cpp | 1036 +++++++++++++++++ cpp/src/barretenberg/honk/flavor/flavor.hpp | 56 +- .../honk/proof_system/prover_library.cpp | 10 +- .../honk/proof_system/prover_library.test.cpp | 34 +- .../honk/proof_system/ultra_prover.cpp | 56 + .../honk/proof_system/ultra_prover.hpp | 63 + .../honk/proof_system/verifier.cpp | 1 - .../honk/proof_system/verifier.test.cpp | 1 - .../grand_product_computation_relation.hpp | 87 ++ .../grand_product_initialization_relation.hpp | 44 + .../honk/sumcheck/relations/relation.hpp | 1 - ...test.cpp => relation_consistency.test.cpp} | 232 +++- .../relations/relation_correctness.test.cpp | 258 ++++ .../relations/ultra_arithmetic_relation.hpp | 87 ++ .../ultra_arithmetic_relation_secondary.hpp | 67 ++ .../proofs/join_split/c_bind.cpp | 32 +- .../join_split/compute_circuit_data.cpp | 3 +- .../proofs/join_split/join_split.cpp | 16 +- .../proofs/join_split/join_split.hpp | 5 +- .../proofs/join_split/join_split.test.cpp | 22 +- .../proofs/join_split/join_split_circuit.cpp | 17 +- .../proofs/join_split/join_split_circuit.hpp | 13 +- .../proofs/join_split/join_split_tx.hpp | 8 +- .../proofs/mock/mock_circuit.test.cpp | 4 +- .../notes/circuit/account/account_note.hpp | 5 +- .../proofs/notes/circuit/account/commit.hpp | 6 +- .../proofs/notes/circuit/asset_id.cpp | 4 +- .../proofs/notes/circuit/asset_id.hpp | 4 +- .../proofs/notes/circuit/bridge_call_data.hpp | 4 +- .../proofs/notes/circuit/claim/claim_note.hpp | 5 +- .../claim/complete_partial_commitment.hpp | 5 +- .../notes/circuit/claim/compute_nullifier.hpp | 5 +- .../claim/create_partial_commitment.hpp | 4 +- .../notes/circuit/claim/witness_data.hpp | 5 +- .../value/complete_partial_commitment.hpp | 6 +- .../notes/circuit/value/compute_nullifier.cpp | 4 +- .../notes/circuit/value/compute_nullifier.hpp | 5 +- .../circuit/value/compute_nullifier.test.cpp | 6 +- .../value/create_partial_commitment.hpp | 6 +- .../proofs/notes/circuit/value/value_note.hpp | 4 +- .../notes/circuit/value/value_note.test.cpp | 8 +- .../notes/circuit/value/witness_data.hpp | 5 +- .../barretenberg/join_split_example/types.hpp | 50 + .../plonk/composer/ultra_composer.cpp | 58 + .../plonk/composer/ultra_composer.hpp | 3 + .../plonk/composer/ultra_composer.test.cpp | 256 ++-- .../kate_commitment_scheme.cpp | 1 + .../kate_commitment_scheme.hpp | 1 + .../plonk/proof_system/constants.hpp | 12 +- .../plonk/proof_system/prover/c_bind.cpp | 5 +- .../proof_system/prover/c_bind_unrolled.cpp | 110 -- .../plonk/proof_system/prover/prover.cpp | 2 + .../plonk/proof_system/prover/prover.hpp | 1 + .../types/polynomial_manifest.hpp | 2 +- .../proof_system/types/program_settings.hpp | 16 + .../proof_system/types/prover_settings.hpp | 8 + .../verification_key.test.cpp | 27 +- .../plonk/proof_system/verifier/verifier.cpp | 1 + .../plonk/proof_system/verifier/verifier.hpp | 3 +- .../composer/permutation_helper.hpp | 54 +- .../encryption/schnorr/schnorr.test.cpp | 45 +- .../stdlib/hash/blake2s/blake2s.test.cpp | 32 +- .../stdlib/hash/sha256/sha256.bench.cpp | 18 +- .../stdlib/hash/sha256/sha256.test.cpp | 88 +- .../barretenberg/stdlib/merkle_tree/hash.hpp | 24 +- .../stdlib/merkle_tree/hash.test.cpp | 30 +- .../stdlib/merkle_tree/membership.test.cpp | 42 +- .../stdlib/merkle_tree/merkle_tree.test.cpp | 12 +- .../primitives/biggroup/biggroup.test.cpp | 1 - .../primitives/byte_array/byte_array.test.cpp | 3 +- .../stdlib/primitives/group/group.test.cpp | 7 +- .../packed_byte_array.test.cpp | 11 +- .../primitives/safe_uint/safe_uint.test.cpp | 4 +- .../verification_key.test.cpp | 12 +- cpp/src/barretenberg/stdlib/types/types.hpp | 100 -- 114 files changed, 3775 insertions(+), 1035 deletions(-) create mode 100644 cpp/src/barretenberg/dsl/types.hpp create mode 100644 cpp/src/barretenberg/honk/composer/composer_helper/ultra_honk_composer_helper.cpp create mode 100644 cpp/src/barretenberg/honk/composer/composer_helper/ultra_honk_composer_helper.hpp create mode 100644 cpp/src/barretenberg/honk/composer/ultra_honk_composer.hpp create mode 100644 cpp/src/barretenberg/honk/composer/ultra_honk_composer.test.cpp create mode 100644 cpp/src/barretenberg/honk/proof_system/ultra_prover.cpp create mode 100644 cpp/src/barretenberg/honk/proof_system/ultra_prover.hpp rename cpp/src/barretenberg/honk/sumcheck/relations/{relation.test.cpp => relation_consistency.test.cpp} (57%) create mode 100644 cpp/src/barretenberg/honk/sumcheck/relations/relation_correctness.test.cpp create mode 100644 cpp/src/barretenberg/honk/sumcheck/relations/ultra_arithmetic_relation.hpp create mode 100644 cpp/src/barretenberg/honk/sumcheck/relations/ultra_arithmetic_relation_secondary.hpp create mode 100644 cpp/src/barretenberg/join_split_example/types.hpp delete mode 100644 cpp/src/barretenberg/plonk/proof_system/prover/c_bind_unrolled.cpp delete mode 100644 cpp/src/barretenberg/stdlib/types/types.hpp diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt index 582d728537..4f719a0335 100644 --- a/cpp/CMakeLists.txt +++ b/cpp/CMakeLists.txt @@ -22,14 +22,8 @@ option(SERIALIZE_CANARY "Build with serialize canary" OFF) option(ENABLE_ASAN "Address sanitizer for debugging tricky memory corruption" OFF) option(ENABLE_HEAVY_TESTS "Enable heavy tests when collecting coverage" OFF) option(INSTALL_BARRETENBERG "Enable installation of barretenberg. (Projects embedding barretenberg may want to turn this OFF.)" ON) -option(USE_TURBO "Enable the use of TurboPlonk in barretenberg." OFF) -if(USE_TURBO) - message(STATUS "Building barretenberg for TurboPlonk Composer.") - add_definitions(-DUSE_TURBO) -else() - message(STATUS "Building barretenberg for UltraPlonk Composer.") -endif() +message(STATUS "Building barretenberg for UltraPlonk Composer.") if(CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64" OR CMAKE_SYSTEM_PROCESSOR MATCHES "arm64") message(STATUS "Compiling for ARM.") diff --git a/cpp/src/barretenberg/crypto/generators/generator_data.cpp b/cpp/src/barretenberg/crypto/generators/generator_data.cpp index 7970dfe659..1ad6127923 100644 --- a/cpp/src/barretenberg/crypto/generators/generator_data.cpp +++ b/cpp/src/barretenberg/crypto/generators/generator_data.cpp @@ -6,18 +6,14 @@ namespace { // The number of unique base points with default main index with precomputed ladders #ifdef __wasm__ -constexpr size_t num_default_generators = 64; -constexpr size_t num_generators_per_hash_index = 16; -constexpr size_t num_hash_indices = 32; -// TODO need to resolve memory out of bounds when these are too high +constexpr size_t num_default_generators = 32; #else constexpr size_t num_default_generators = 2048; -constexpr size_t num_hash_indices = 32; -constexpr size_t num_generators_per_hash_index = 128; #endif constexpr size_t hash_indices_generator_offset = 2048; - +constexpr size_t num_hash_indices = 16; +constexpr size_t num_generators_per_hash_index = 8; constexpr size_t num_indexed_generators = num_hash_indices * num_generators_per_hash_index; constexpr size_t size_of_generator_data_array = hash_indices_generator_offset + num_indexed_generators; constexpr size_t num_generator_types = 3; diff --git a/cpp/src/barretenberg/dsl/acir_format/acir_format.cpp b/cpp/src/barretenberg/dsl/acir_format/acir_format.cpp index 65313934a6..e4f524869c 100644 --- a/cpp/src/barretenberg/dsl/acir_format/acir_format.cpp +++ b/cpp/src/barretenberg/dsl/acir_format/acir_format.cpp @@ -1,8 +1,6 @@ #include "acir_format.hpp" #include "barretenberg/common/log.hpp" -using namespace proof_system::plonk::stdlib::types; - namespace acir_format { void read_witness(Composer& composer, std::vector witness) @@ -114,6 +112,7 @@ Composer create_circuit(const acir_format& constraint_system, composer.add_variable(0); } } + // Add arithmetic gates for (const auto& constraint : constraint_system.constraints) { composer.create_poly_gate(constraint); diff --git a/cpp/src/barretenberg/dsl/acir_format/acir_format.hpp b/cpp/src/barretenberg/dsl/acir_format/acir_format.hpp index 950de43460..39940e7f3a 100644 --- a/cpp/src/barretenberg/dsl/acir_format/acir_format.hpp +++ b/cpp/src/barretenberg/dsl/acir_format/acir_format.hpp @@ -10,7 +10,7 @@ #include "recursion_constraint.hpp" #include "pedersen.hpp" #include "hash_to_field.hpp" -#include "barretenberg/stdlib/types/types.hpp" +#include "barretenberg/dsl/types.hpp" namespace acir_format { @@ -39,23 +39,20 @@ struct acir_format { friend bool operator==(acir_format const& lhs, acir_format const& rhs) = default; }; -void read_witness(plonk::stdlib::types::Composer& composer, std::vector witness); +void read_witness(Composer& composer, std::vector witness); -void create_circuit(plonk::stdlib::types::Composer& composer, const acir_format& constraint_system); +void create_circuit(Composer& composer, const acir_format& constraint_system); -plonk::stdlib::types::Composer create_circuit(const acir_format& constraint_system, - std::unique_ptr&& crs_factory); +Composer create_circuit(const acir_format& constraint_system, + std::unique_ptr&& crs_factory); -plonk::stdlib::types::Composer create_circuit_with_witness(const acir_format& constraint_system, - std::vector witness, - std::unique_ptr&& crs_factory); +Composer create_circuit_with_witness(const acir_format& constraint_system, + std::vector witness, + std::unique_ptr&& crs_factory); -plonk::stdlib::types::Composer create_circuit_with_witness(const acir_format& constraint_system, - std::vector witness); +Composer create_circuit_with_witness(const acir_format& constraint_system, std::vector witness); -void create_circuit_with_witness(plonk::stdlib::types::Composer& composer, - const acir_format& constraint_system, - std::vector witness); +void create_circuit_with_witness(Composer& composer, const acir_format& constraint_system, std::vector witness); // Serialisation template inline void read(B& buf, acir_format& data) diff --git a/cpp/src/barretenberg/dsl/acir_format/acir_format.test.cpp b/cpp/src/barretenberg/dsl/acir_format/acir_format.test.cpp index ff957c6b9b..5c17c353c1 100644 --- a/cpp/src/barretenberg/dsl/acir_format/acir_format.test.cpp +++ b/cpp/src/barretenberg/dsl/acir_format/acir_format.test.cpp @@ -4,6 +4,46 @@ #include #include "barretenberg/common/streams.hpp" +TEST(acir_format, test_a_single_constraint_no_pub_inputs) +{ + + poly_triple constraint{ + .a = 1, + .b = 2, + .c = 3, + .q_m = 0, + .q_l = 1, + .q_r = 1, + .q_o = -1, + .q_c = 0, + }; + + acir_format::acir_format constraint_system{ + .varnum = 4, + .public_inputs = {}, + .fixed_base_scalar_mul_constraints = {}, + .logic_constraints = {}, + .range_constraints = {}, + .schnorr_constraints = {}, + .ecdsa_constraints = {}, + .sha256_constraints = {}, + .blake2s_constraints = {}, + .hash_to_field_constraints = {}, + .pedersen_constraints = {}, + .merkle_membership_constraints = {}, + .constraints = { constraint }, + }; + + auto composer = acir_format::create_circuit_with_witness(constraint_system, { 0, 0, 1 }); + + auto prover = composer.create_ultra_with_keccak_prover(); + auto proof = prover.construct_proof(); + + auto verifier = composer.create_ultra_with_keccak_verifier(); + + EXPECT_EQ(verifier.verify_proof(proof), false); +} + TEST(acir_format, test_logic_gate_from_noir_circuit) { /** @@ -105,10 +145,10 @@ TEST(acir_format, test_logic_gate_from_noir_circuit) std::cout << "made composer" << std::endl; - auto prover = composer.create_prover(); + auto prover = composer.create_ultra_with_keccak_prover(); auto proof = prover.construct_proof(); - auto verifier = composer.create_verifier(); + auto verifier = composer.create_ultra_with_keccak_verifier(); EXPECT_EQ(verifier.verify_proof(proof), true); } @@ -174,10 +214,10 @@ TEST(acir_format, test_schnorr_verify_pass) 67, 16, 37, 128, 85, 76, 19, 253, 30, 77, 192, 53, 138, 205, 69, 33, 236, 163, 83, 194, 84, 137, 184, 221, 176, 121, 179, 27, 63, 70, 54, 16, 176, 250, 39, 239, 1, 0, 0, 0 }); - auto prover = composer.create_prover(); + auto prover = composer.create_ultra_with_keccak_prover(); auto proof = prover.construct_proof(); - auto verifier = composer.create_verifier(); + auto verifier = composer.create_ultra_with_keccak_verifier(); EXPECT_EQ(verifier.verify_proof(proof), true); } @@ -243,10 +283,10 @@ TEST(acir_format, test_schnorr_verify_small_range) 67, 16, 37, 128, 85, 76, 19, 253, 30, 77, 192, 53, 138, 205, 69, 33, 236, 163, 83, 194, 84, 137, 184, 221, 176, 121, 179, 27, 63, 70, 54, 16, 176, 250, 39, 239, 1, 0, 0, 0 }); - auto prover = composer.create_prover(); + auto prover = composer.create_ultra_with_keccak_prover(); auto proof = prover.construct_proof(); - auto verifier = composer.create_verifier(); + auto verifier = composer.create_ultra_with_keccak_verifier(); EXPECT_EQ(verifier.verify_proof(proof), true); } \ No newline at end of file diff --git a/cpp/src/barretenberg/dsl/acir_format/blake2s_constraint.cpp b/cpp/src/barretenberg/dsl/acir_format/blake2s_constraint.cpp index 03deb176f6..8cd00269af 100644 --- a/cpp/src/barretenberg/dsl/acir_format/blake2s_constraint.cpp +++ b/cpp/src/barretenberg/dsl/acir_format/blake2s_constraint.cpp @@ -1,8 +1,6 @@ #include "blake2s_constraint.hpp" #include "round.hpp" -using namespace proof_system::plonk::stdlib::types; - namespace acir_format { void create_blake2s_constraints(Composer& composer, const Blake2sConstraint& constraint) diff --git a/cpp/src/barretenberg/dsl/acir_format/blake2s_constraint.hpp b/cpp/src/barretenberg/dsl/acir_format/blake2s_constraint.hpp index 643eb1e39b..f68bf2019a 100644 --- a/cpp/src/barretenberg/dsl/acir_format/blake2s_constraint.hpp +++ b/cpp/src/barretenberg/dsl/acir_format/blake2s_constraint.hpp @@ -1,7 +1,7 @@ #pragma once #include #include -#include "barretenberg/stdlib/types/types.hpp" +#include "barretenberg/dsl/types.hpp" namespace acir_format { @@ -19,7 +19,7 @@ struct Blake2sConstraint { friend bool operator==(Blake2sConstraint const& lhs, Blake2sConstraint const& rhs) = default; }; -void create_blake2s_constraints(plonk::stdlib::types::Composer& composer, const Blake2sConstraint& constraint); +void create_blake2s_constraints(Composer& composer, const Blake2sConstraint& constraint); template inline void read(B& buf, Blake2sInput& constraint) { diff --git a/cpp/src/barretenberg/dsl/acir_format/ecdsa_secp256k1.cpp b/cpp/src/barretenberg/dsl/acir_format/ecdsa_secp256k1.cpp index 291245d3d5..87a873dc16 100644 --- a/cpp/src/barretenberg/dsl/acir_format/ecdsa_secp256k1.cpp +++ b/cpp/src/barretenberg/dsl/acir_format/ecdsa_secp256k1.cpp @@ -2,10 +2,10 @@ #include "barretenberg/crypto/ecdsa/ecdsa.hpp" #include "barretenberg/stdlib/encryption/ecdsa/ecdsa.hpp" -using namespace proof_system::plonk::stdlib::types; - namespace acir_format { +using namespace proof_system::plonk; + crypto::ecdsa::signature ecdsa_convert_signature(Composer& composer, std::vector signature) { diff --git a/cpp/src/barretenberg/dsl/acir_format/ecdsa_secp256k1.hpp b/cpp/src/barretenberg/dsl/acir_format/ecdsa_secp256k1.hpp index 9fee1b035a..55d8f6c16d 100644 --- a/cpp/src/barretenberg/dsl/acir_format/ecdsa_secp256k1.hpp +++ b/cpp/src/barretenberg/dsl/acir_format/ecdsa_secp256k1.hpp @@ -1,6 +1,6 @@ #pragma once #include -#include "barretenberg/stdlib/types/types.hpp" +#include "barretenberg/dsl/types.hpp" namespace acir_format { @@ -28,7 +28,7 @@ struct EcdsaSecp256k1Constraint { friend bool operator==(EcdsaSecp256k1Constraint const& lhs, EcdsaSecp256k1Constraint const& rhs) = default; }; -void create_ecdsa_verify_constraints(plonk::stdlib::types::Composer& composer, const EcdsaSecp256k1Constraint& input); +void create_ecdsa_verify_constraints(Composer& composer, const EcdsaSecp256k1Constraint& input); template inline void read(B& buf, EcdsaSecp256k1Constraint& constraint) { diff --git a/cpp/src/barretenberg/dsl/acir_format/ecdsa_secp256k1.test.cpp b/cpp/src/barretenberg/dsl/acir_format/ecdsa_secp256k1.test.cpp index 57f9af0a1c..875fc4d589 100644 --- a/cpp/src/barretenberg/dsl/acir_format/ecdsa_secp256k1.test.cpp +++ b/cpp/src/barretenberg/dsl/acir_format/ecdsa_secp256k1.test.cpp @@ -7,8 +7,8 @@ #include #include -using namespace proof_system::plonk::stdlib::types; -using curve = stdlib::secp256k1; +using namespace proof_system::plonk; +using curve = stdlib::secp256k1; size_t generate_ecdsa_constraint(acir_format::EcdsaSecp256k1Constraint& ecdsa_constraint, std::vector& witness_values) diff --git a/cpp/src/barretenberg/dsl/acir_format/fixed_base_scalar_mul.cpp b/cpp/src/barretenberg/dsl/acir_format/fixed_base_scalar_mul.cpp index 81251944e6..453ab56f47 100644 --- a/cpp/src/barretenberg/dsl/acir_format/fixed_base_scalar_mul.cpp +++ b/cpp/src/barretenberg/dsl/acir_format/fixed_base_scalar_mul.cpp @@ -1,7 +1,5 @@ #include "fixed_base_scalar_mul.hpp" -using namespace proof_system::plonk::stdlib::types; - namespace acir_format { void create_fixed_base_constraint(Composer& composer, const FixedBaseScalarMul& input) diff --git a/cpp/src/barretenberg/dsl/acir_format/fixed_base_scalar_mul.hpp b/cpp/src/barretenberg/dsl/acir_format/fixed_base_scalar_mul.hpp index 2b61305a5c..80e52e03db 100644 --- a/cpp/src/barretenberg/dsl/acir_format/fixed_base_scalar_mul.hpp +++ b/cpp/src/barretenberg/dsl/acir_format/fixed_base_scalar_mul.hpp @@ -1,6 +1,6 @@ #pragma once #include -#include "barretenberg/stdlib/types/types.hpp" +#include "barretenberg/dsl/types.hpp" namespace acir_format { @@ -12,7 +12,7 @@ struct FixedBaseScalarMul { friend bool operator==(FixedBaseScalarMul const& lhs, FixedBaseScalarMul const& rhs) = default; }; -void create_fixed_base_constraint(plonk::stdlib::types::Composer& composer, const FixedBaseScalarMul& input); +void create_fixed_base_constraint(Composer& composer, const FixedBaseScalarMul& input); template inline void read(B& buf, FixedBaseScalarMul& constraint) { diff --git a/cpp/src/barretenberg/dsl/acir_format/hash_to_field.cpp b/cpp/src/barretenberg/dsl/acir_format/hash_to_field.cpp index 998b414cd4..afc3e2cf9f 100644 --- a/cpp/src/barretenberg/dsl/acir_format/hash_to_field.cpp +++ b/cpp/src/barretenberg/dsl/acir_format/hash_to_field.cpp @@ -1,10 +1,10 @@ #include "hash_to_field.hpp" #include "round.hpp" -using namespace proof_system::plonk::stdlib::types; - namespace acir_format { +using namespace proof_system::plonk; + void create_hash_to_field_constraints(Composer& composer, const HashToFieldConstraint constraint) { @@ -30,7 +30,7 @@ void create_hash_to_field_constraints(Composer& composer, const HashToFieldConst // Hash To Field using blake2s. // Note: It does not need to be blake2s in the future - byte_array_ct out_bytes = proof_system::plonk::stdlib::blake2s(arr); + byte_array_ct out_bytes = stdlib::blake2s(arr); field_ct out(out_bytes); field_ct normalised_out = out.normalize(); diff --git a/cpp/src/barretenberg/dsl/acir_format/hash_to_field.hpp b/cpp/src/barretenberg/dsl/acir_format/hash_to_field.hpp index 275ce3fd0d..504493ce3e 100644 --- a/cpp/src/barretenberg/dsl/acir_format/hash_to_field.hpp +++ b/cpp/src/barretenberg/dsl/acir_format/hash_to_field.hpp @@ -1,7 +1,7 @@ #pragma once #include #include -#include "barretenberg/stdlib/types/types.hpp" +#include "barretenberg/dsl/types.hpp" namespace acir_format { @@ -19,7 +19,7 @@ struct HashToFieldConstraint { friend bool operator==(HashToFieldConstraint const& lhs, HashToFieldConstraint const& rhs) = default; }; -void create_hash_to_field_constraints(plonk::stdlib::types::Composer& composer, HashToFieldConstraint constraint); +void create_hash_to_field_constraints(Composer& composer, HashToFieldConstraint constraint); template inline void read(B& buf, HashToFieldInput& constraint) { diff --git a/cpp/src/barretenberg/dsl/acir_format/logic_constraint.cpp b/cpp/src/barretenberg/dsl/acir_format/logic_constraint.cpp index ca14b7cbc4..09990fe04b 100644 --- a/cpp/src/barretenberg/dsl/acir_format/logic_constraint.cpp +++ b/cpp/src/barretenberg/dsl/acir_format/logic_constraint.cpp @@ -1,10 +1,10 @@ #include "logic_constraint.hpp" #include "barretenberg/stdlib/primitives/logic/logic.hpp" -using namespace proof_system::plonk::stdlib::types; - namespace acir_format { +using namespace proof_system::plonk; + void create_logic_gate(Composer& composer, const uint32_t a, const uint32_t b, diff --git a/cpp/src/barretenberg/dsl/acir_format/logic_constraint.hpp b/cpp/src/barretenberg/dsl/acir_format/logic_constraint.hpp index 0a194838ad..964f84f5af 100644 --- a/cpp/src/barretenberg/dsl/acir_format/logic_constraint.hpp +++ b/cpp/src/barretenberg/dsl/acir_format/logic_constraint.hpp @@ -1,6 +1,6 @@ #pragma once #include -#include "barretenberg/stdlib/types/types.hpp" +#include "barretenberg/dsl/types.hpp" namespace acir_format { @@ -14,16 +14,11 @@ struct LogicConstraint { friend bool operator==(LogicConstraint const& lhs, LogicConstraint const& rhs) = default; }; -void create_logic_gate(plonk::stdlib::types::Composer& composer, - uint32_t a, - uint32_t b, - uint32_t result, - size_t num_bits, - bool is_xor_gate); +void create_logic_gate(Composer& composer, uint32_t a, uint32_t b, uint32_t result, size_t num_bits, bool is_xor_gate); -void xor_gate(plonk::stdlib::types::Composer& composer, uint32_t a, uint32_t b, uint32_t result); +void xor_gate(Composer& composer, uint32_t a, uint32_t b, uint32_t result); -void and_gate(plonk::stdlib::types::Composer& composer, uint32_t a, uint32_t b, uint32_t result); +void and_gate(Composer& composer, uint32_t a, uint32_t b, uint32_t result); template inline void read(B& buf, LogicConstraint& constraint) { diff --git a/cpp/src/barretenberg/dsl/acir_format/merkle_membership_constraint.cpp b/cpp/src/barretenberg/dsl/acir_format/merkle_membership_constraint.cpp index 3f30a61fcb..780373f5be 100644 --- a/cpp/src/barretenberg/dsl/acir_format/merkle_membership_constraint.cpp +++ b/cpp/src/barretenberg/dsl/acir_format/merkle_membership_constraint.cpp @@ -1,9 +1,6 @@ #include "merkle_membership_constraint.hpp" #include "barretenberg/stdlib/merkle_tree/membership.hpp" -using namespace proof_system::plonk::stdlib::types; -using namespace proof_system::plonk::stdlib::merkle_tree; - namespace acir_format { void create_merkle_check_membership_constraint(Composer& composer, const MerkleMembershipConstraint& input) @@ -22,7 +19,7 @@ void create_merkle_check_membership_constraint(Composer& composer, const MerkleM // We are given the HashPath as a Vec // We want to first convert it into a Vec<(fr, fr)> then cast this to hash_path // struct which requires the method create_witness_hashpath - hash_path hash_path; + hash_path_ct hash_path; // In Noir we accept a hash path that only contains one hash per tree level // It is ok to reuse the leaf as it will be overridden in check_subtree_membership when computing the current root @@ -39,7 +36,7 @@ void create_merkle_check_membership_constraint(Composer& composer, const MerkleM } } - auto exists = check_subtree_membership(root, hash_path, leaf, index_bits, 0); + auto exists = plonk::stdlib::merkle_tree::check_subtree_membership(root, hash_path, leaf, index_bits, 0); composer.assert_equal_constant(exists.witness_index, fr::one()); } diff --git a/cpp/src/barretenberg/dsl/acir_format/merkle_membership_constraint.hpp b/cpp/src/barretenberg/dsl/acir_format/merkle_membership_constraint.hpp index 1fa1481e88..25bfcd0404 100644 --- a/cpp/src/barretenberg/dsl/acir_format/merkle_membership_constraint.hpp +++ b/cpp/src/barretenberg/dsl/acir_format/merkle_membership_constraint.hpp @@ -1,6 +1,6 @@ #pragma once #include -#include "barretenberg/stdlib/types/types.hpp" +#include "barretenberg/dsl/types.hpp" namespace acir_format { @@ -14,8 +14,7 @@ struct MerkleMembershipConstraint { friend bool operator==(MerkleMembershipConstraint const& lhs, MerkleMembershipConstraint const& rhs) = default; }; -void create_merkle_check_membership_constraint(plonk::stdlib::types::Composer& composer, - const MerkleMembershipConstraint& input); +void create_merkle_check_membership_constraint(Composer& composer, const MerkleMembershipConstraint& input); template inline void read(B& buf, MerkleMembershipConstraint& constraint) { diff --git a/cpp/src/barretenberg/dsl/acir_format/pedersen.cpp b/cpp/src/barretenberg/dsl/acir_format/pedersen.cpp index 3ac7951ec9..d272a3a8ed 100644 --- a/cpp/src/barretenberg/dsl/acir_format/pedersen.cpp +++ b/cpp/src/barretenberg/dsl/acir_format/pedersen.cpp @@ -1,9 +1,9 @@ #include "pedersen.hpp" -using namespace proof_system::plonk::stdlib::types; - namespace acir_format { +using namespace proof_system::plonk; + void create_pedersen_constraint(Composer& composer, const PedersenConstraint& input) { std::vector scalars; @@ -13,12 +13,9 @@ void create_pedersen_constraint(Composer& composer, const PedersenConstraint& in field_ct scalar_as_field = field_ct::from_witness_index(&composer, scalar); scalars.push_back(scalar_as_field); } -#ifdef USE_TURBO - auto point = pedersen_commitment::commit(scalars); -#else + // TODO: Does Noir need additive homomorphic Pedersen hash? If so, using plookup version won't help. auto point = stdlib::pedersen_plookup_commitment::commit(scalars); -#endif composer.assert_equal(point.x.witness_index, input.result_x); composer.assert_equal(point.y.witness_index, input.result_y); diff --git a/cpp/src/barretenberg/dsl/acir_format/pedersen.hpp b/cpp/src/barretenberg/dsl/acir_format/pedersen.hpp index b801d2a8f2..3144359b2f 100644 --- a/cpp/src/barretenberg/dsl/acir_format/pedersen.hpp +++ b/cpp/src/barretenberg/dsl/acir_format/pedersen.hpp @@ -1,6 +1,6 @@ #pragma once #include -#include "barretenberg/stdlib/types/types.hpp" +#include "barretenberg/dsl/types.hpp" namespace acir_format { @@ -13,7 +13,7 @@ struct PedersenConstraint { friend bool operator==(PedersenConstraint const& lhs, PedersenConstraint const& rhs) = default; }; -void create_pedersen_constraint(plonk::stdlib::types::Composer& composer, const PedersenConstraint& input); +void create_pedersen_constraint(Composer& composer, const PedersenConstraint& input); template inline void read(B& buf, PedersenConstraint& constraint) { diff --git a/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.cpp b/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.cpp index 895c27f99c..441b47c2d8 100644 --- a/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.cpp +++ b/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.cpp @@ -4,15 +4,10 @@ #include "barretenberg/stdlib/recursion/verifier/verifier.hpp" #include "barretenberg/transcript/transcript_wrappers.hpp" -using namespace proof_system::plonk::stdlib::types; - -using verification_key_ct = proof_system::plonk::stdlib::recursion::verification_key; -using aggregation_state_ct = proof_system::plonk::stdlib::recursion::aggregation_state; -using noir_recursive_settings = proof_system::plonk::stdlib::recursion::recursive_ultra_verifier_settings; -using Transcript_ct = proof_system::plonk::stdlib::recursion::Transcript; - namespace acir_format { +using namespace proof_system::plonk; + void generate_dummy_proof() {} /** * @brief Add constraints required to recursively verify an UltraPlonk proof @@ -61,8 +56,6 @@ void create_recursion_constraints(Composer& composer, const RecursionConstraint& composer.assert_equal(composer.add_variable(dummy_field), key_field_idx); } } - // The env_crs should be ok, we use this when generating the constraint system in dsl - auto env_crs = std::make_unique(); // Construct an in-circuit representation of the verification key. // For now, the v-key is a circuit constant and is fixed for the circuit. @@ -135,9 +128,9 @@ void create_recursion_constraints(Composer& composer, const RecursionConstraint& } } -template void create_recursion_constraints(plonk::stdlib::types::Composer&, const RecursionConstraint&); -template void create_recursion_constraints(plonk::stdlib::types::Composer&, const RecursionConstraint&); -template void create_recursion_constraints(plonk::stdlib::types::Composer&, const RecursionConstraint&); -template void create_recursion_constraints(plonk::stdlib::types::Composer&, const RecursionConstraint&); +template void create_recursion_constraints(Composer&, const RecursionConstraint&); +template void create_recursion_constraints(Composer&, const RecursionConstraint&); +template void create_recursion_constraints(Composer&, const RecursionConstraint&); +template void create_recursion_constraints(Composer&, const RecursionConstraint&); } // namespace acir_format diff --git a/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.hpp b/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.hpp index 45abd037c2..cfc0116349 100644 --- a/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.hpp +++ b/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.hpp @@ -1,7 +1,8 @@ #pragma once #include -#include "barretenberg/stdlib/types/types.hpp" -#include "barretenberg/plonk/proof_system//verification_key/verification_key.hpp" +// #include "barretenberg/stdlib/types/types.hpp" +#include "barretenberg/dsl/types.hpp" +#include "barretenberg/plonk/proof_system/verification_key/verification_key.hpp" namespace acir_format { @@ -41,16 +42,12 @@ struct RecursionConstraint { }; template -void create_recursion_constraints(plonk::stdlib::types::Composer& composer, const RecursionConstraint& input); +void create_recursion_constraints(Composer& composer, const RecursionConstraint& input); -extern template void create_recursion_constraints(plonk::stdlib::types::Composer&, - const RecursionConstraint&); -extern template void create_recursion_constraints(plonk::stdlib::types::Composer&, - const RecursionConstraint&); -extern template void create_recursion_constraints(plonk::stdlib::types::Composer&, - const RecursionConstraint&); -extern template void create_recursion_constraints(plonk::stdlib::types::Composer&, - const RecursionConstraint&); +extern template void create_recursion_constraints(Composer&, const RecursionConstraint&); +extern template void create_recursion_constraints(Composer&, const RecursionConstraint&); +extern template void create_recursion_constraints(Composer&, const RecursionConstraint&); +extern template void create_recursion_constraints(Composer&, const RecursionConstraint&); template inline void read(B& buf, RecursionConstraint& constraint) { diff --git a/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.test.cpp b/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.test.cpp index cfd564f7f6..2b23dcfa07 100644 --- a/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.test.cpp +++ b/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.test.cpp @@ -6,9 +6,9 @@ #include #include -using namespace proof_system::plonk::stdlib::types; +using namespace proof_system::plonk; -Composer create_inner_circuit() +acir_format::Composer create_inner_circuit() { /** * constraints produced by Noir program: @@ -127,8 +127,10 @@ TEST(RecursionConstraint, TestRecursionConstraint) output_vars[i] = (static_cast(i + 3)); } - transcript::StandardTranscript transcript( - inner_proof.proof_data, Composer::create_manifest(1), transcript::HashType::PlookupPedersenBlake3s, 16); + transcript::StandardTranscript transcript(inner_proof.proof_data, + acir_format::Composer::create_manifest(1), + transcript::HashType::PlookupPedersenBlake3s, + 16); const std::vector proof_witnesses = transcript.export_transcript_in_recursion_format(); const std::vector key_witnesses = inner_verifier.key->export_key_in_recursion_format(); diff --git a/cpp/src/barretenberg/dsl/acir_format/schnorr_verify.cpp b/cpp/src/barretenberg/dsl/acir_format/schnorr_verify.cpp index f92176bd08..cb717ed99a 100644 --- a/cpp/src/barretenberg/dsl/acir_format/schnorr_verify.cpp +++ b/cpp/src/barretenberg/dsl/acir_format/schnorr_verify.cpp @@ -1,10 +1,11 @@ #include "schnorr_verify.hpp" +#include "barretenberg/stdlib/encryption/schnorr/schnorr.hpp" #include "barretenberg/crypto/schnorr/schnorr.hpp" -using namespace proof_system::plonk::stdlib::types; - namespace acir_format { +using namespace proof_system::plonk::stdlib; + crypto::schnorr::signature convert_signature(Composer& composer, std::vector signature) { @@ -82,9 +83,9 @@ void create_schnorr_verify_constraints(Composer& composer, const SchnorrConstrai point_ct pub_key{ witness_ct(&composer, pubkey_value_x), witness_ct(&composer, pubkey_value_y) }; - schnorr::signature_bits sig = stdlib::schnorr::convert_signature(&composer, new_sig); + schnorr_signature_bits_ct sig = schnorr::convert_signature(&composer, new_sig); - bool_ct signature_result = stdlib::schnorr::signature_verification_result(message, pub_key, sig); + bool_ct signature_result = schnorr::signature_verification_result(message, pub_key, sig); bool_ct signature_result_normalized = signature_result.normalize(); diff --git a/cpp/src/barretenberg/dsl/acir_format/schnorr_verify.hpp b/cpp/src/barretenberg/dsl/acir_format/schnorr_verify.hpp index f8e6b9c53f..7b6b984294 100644 --- a/cpp/src/barretenberg/dsl/acir_format/schnorr_verify.hpp +++ b/cpp/src/barretenberg/dsl/acir_format/schnorr_verify.hpp @@ -1,6 +1,6 @@ #pragma once #include -#include "barretenberg/stdlib/types/types.hpp" +#include "barretenberg/dsl/types.hpp" namespace acir_format { @@ -25,7 +25,7 @@ struct SchnorrConstraint { friend bool operator==(SchnorrConstraint const& lhs, SchnorrConstraint const& rhs) = default; }; -void create_schnorr_verify_constraints(plonk::stdlib::types::Composer& composer, const SchnorrConstraint& input); +void create_schnorr_verify_constraints(Composer& composer, const SchnorrConstraint& input); template inline void read(B& buf, SchnorrConstraint& constraint) { diff --git a/cpp/src/barretenberg/dsl/acir_format/sha256_constraint.cpp b/cpp/src/barretenberg/dsl/acir_format/sha256_constraint.cpp index e54697d4e7..07c8166bd1 100644 --- a/cpp/src/barretenberg/dsl/acir_format/sha256_constraint.cpp +++ b/cpp/src/barretenberg/dsl/acir_format/sha256_constraint.cpp @@ -1,8 +1,7 @@ #include "sha256_constraint.hpp" #include "round.hpp" #include "barretenberg/stdlib/hash/sha256/sha256.hpp" - -using namespace proof_system::plonk::stdlib::types; +#include "barretenberg/dsl/types.hpp" namespace acir_format { diff --git a/cpp/src/barretenberg/dsl/acir_format/sha256_constraint.hpp b/cpp/src/barretenberg/dsl/acir_format/sha256_constraint.hpp index dec3bca40a..eccb1521ab 100644 --- a/cpp/src/barretenberg/dsl/acir_format/sha256_constraint.hpp +++ b/cpp/src/barretenberg/dsl/acir_format/sha256_constraint.hpp @@ -1,7 +1,7 @@ #pragma once #include #include -#include "barretenberg/stdlib/types/types.hpp" +#include "barretenberg/dsl/types.hpp" namespace acir_format { @@ -21,7 +21,7 @@ struct Sha256Constraint { // This function does not work (properly) because the stdlib:sha256 function is not working correctly for 512 bits // pair -void create_sha256_constraints(plonk::stdlib::types::Composer& composer, const Sha256Constraint& constraint); +void create_sha256_constraints(Composer& composer, const Sha256Constraint& constraint); template inline void read(B& buf, Sha256Input& constraint) { diff --git a/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.cpp b/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.cpp index 4d01512eca..4580792808 100644 --- a/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.cpp +++ b/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.cpp @@ -2,12 +2,10 @@ #include "acir_proofs.hpp" #include "barretenberg/plonk/proof_system/proving_key/serialize.hpp" #include "barretenberg/dsl/acir_format/acir_format.hpp" -#include "barretenberg/stdlib/types/types.hpp" +#include "barretenberg/dsl/types.hpp" #include "barretenberg/srs/reference_string/pippenger_reference_string.hpp" #include "barretenberg/plonk/proof_system/verification_key/sol_gen.hpp" -using namespace proof_system::plonk::stdlib::types; - namespace acir_proofs { size_t get_solidity_verifier(uint8_t const* g2x, uint8_t const* vk_buf, uint8_t** output_buf) @@ -79,9 +77,9 @@ size_t init_verification_key(void* pippenger, uint8_t const* g2x, uint8_t const* reinterpret_cast(pippenger), g2x); proving_key->reference_string = crs_factory->get_prover_crs(proving_key->circuit_size); - Composer composer(proving_key, nullptr); + acir_format::Composer composer(proving_key, nullptr); auto verification_key = - plonk::stdlib::types::Composer::compute_verification_key_base(proving_key, crs_factory->get_verifier_crs()); + acir_format::Composer::compute_verification_key_base(proving_key, crs_factory->get_verifier_crs()); // The composer_type has not yet been set. We need to set the composer_type for when we later read in and // construct the verification key so that we have the correct polynomial manifest @@ -110,25 +108,18 @@ size_t serialize_proof_into_field_elements(uint8_t const* proof_data_buf, plonk::proof proof = { std::vector(proof_data_buf, &proof_data_buf[proof_data_length]) }; transcript::StandardTranscript transcript( - proof.proof_data, Composer::create_manifest(1), transcript::HashType::PlookupPedersenBlake3s, 16); + proof.proof_data, acir_format::Composer::create_manifest(1), transcript::HashType::PlookupPedersenBlake3s, 16); std::vector output = transcript.export_transcript_in_recursion_format(); // NOTE: this output buffer will always have a fixed size! Maybe just precompute? const size_t output_size_bytes = output.size() * sizeof(barretenberg::fr); auto raw_buf = (uint8_t*)malloc(output_size_bytes); - // NOTE: currently we dump the fr values into memory in Mongtomery form - // This is so we don't have to re-convert into Montgomery form when we read these back in. - // I think this makes it easier to handle the data in barretenberg-sys / aztec-backend. - // If this is not the case, the commented out serialize code will convert out of Montgomery form before writing to - // the buffer - // for (size_t i = 0; i < output.size(); ++i) { - // barretenberg::fr::serialize_to_buffer(output[i], &raw_buf[i * 32]); - // } - // for (size_t i = 0; i < output.size(); ++i) { - // barretenberg::fr::serialize_to_buffer(output[i], &raw_buf[i * 32]); - // } - memcpy(raw_buf, (void*)output.data(), output_size_bytes); + + // The serialization code below will convert out of Montgomery form before writing to the buffer + for (size_t i = 0; i < output.size(); ++i) { + barretenberg::fr::serialize_to_buffer(output[i], &raw_buf[i * 32]); + } *serialized_proof_data_buf = raw_buf; return output_size_bytes; @@ -158,23 +149,17 @@ size_t serialize_verification_key_into_field_elements(uint8_t const* g2x, // Cut off 32 bytes as last element is the verification key hash which is not part of the key :o const size_t output_size_bytes = output.size() * sizeof(barretenberg::fr) - 32; auto raw_buf = (uint8_t*)malloc(output_size_bytes); - // NOTE: currently we dump the fr values into memory in Mongtomery form - // This is so we don't have to re-convert into Montgomery form when we read these back in. - // I think this makes it easier to handle the data in barretenberg-sys / aztec-backend. - // If this is not the case, the commented out serialize code will convert out of Montgomery form before writing to - // the buffer - // for (size_t i = 0; i < output.size(); ++i) { - // barretenberg::fr::serialize_to_buffer(output[i], &raw_buf[i * 32]); - // } - - // copy all but the vkey hash into raw_buf - memcpy(raw_buf, (void*)output.data(), output_size_bytes); + + // The serialization code below will convert out of Montgomery form before writing to the buffer + for (size_t i = 0; i < output.size(); ++i) { + barretenberg::fr::serialize_to_buffer(output[i], &raw_buf[i * 32]); + } + + // copy the vkey into serialized_vk_buf *serialized_vk_buf = raw_buf; - // copy the vkey hash into vk_hash_raw_buf - auto vk_hash_raw_buf = (uint8_t*)malloc(32); - memcpy(vk_hash_raw_buf, (void*)&output[output.size() - 1], 32); - *serialized_vk_hash_buf = vk_hash_raw_buf; + // copy the vkey hash into serialized_vk_hash_buf + *serialized_vk_hash_buf = &raw_buf[(output.size() - 1) * 32]; return output_size_bytes; } @@ -184,7 +169,8 @@ size_t new_proof(void* pippenger, uint8_t const* pk_buf, uint8_t const* constraint_system_buf, uint8_t const* witness_buf, - uint8_t** proof_data_buf) + uint8_t** proof_data_buf, + bool is_recursive) { auto constraint_system = from_buffer(constraint_system_buf); @@ -199,21 +185,33 @@ size_t new_proof(void* pippenger, reinterpret_cast(pippenger), g2x); proving_key->reference_string = crs_factory->get_prover_crs(proving_key->circuit_size); - auto env_crs = std::make_unique(); - - auto composer = acir_format::create_circuit_with_witness(constraint_system, witness); - - auto prover = composer.create_prover(); - auto verifier = composer.create_verifier(); - auto heapProver = new stdlib::types::Prover(std::move(prover)); - auto& proof_data = heapProver->construct_proof().proof_data; - *proof_data_buf = proof_data.data(); - - return proof_data.size(); + // TODO: either need a context flag for recursive proofs or a new_recursive_proof method that uses regular + // UltraProver + acir_format::Composer composer(proving_key, nullptr); + + create_circuit_with_witness(composer, constraint_system, witness); + + if (is_recursive) { + auto prover = composer.create_prover(); + auto heapProver = new acir_format::RecursiveProver(std::move(prover)); + auto& proof_data = heapProver->construct_proof().proof_data; + *proof_data_buf = proof_data.data(); + return proof_data.size(); + } else { + auto prover = composer.create_ultra_with_keccak_prover(); + auto heapProver = new acir_format::Prover(std::move(prover)); + auto& proof_data = heapProver->construct_proof().proof_data; + *proof_data_buf = proof_data.data(); + return proof_data.size(); + } } -bool verify_proof( - uint8_t const* g2x, uint8_t const* vk_buf, uint8_t const* constraint_system_buf, uint8_t* proof, uint32_t length) +bool verify_proof(uint8_t const* g2x, + uint8_t const* vk_buf, + uint8_t const* constraint_system_buf, + uint8_t* proof, + uint32_t length, + bool is_recursive) { bool verified = false; @@ -226,13 +224,21 @@ bool verify_proof( read(vk_buf, vk_data); auto verification_key = std::make_shared(std::move(vk_data), crs); - Composer composer(nullptr, verification_key); + acir_format::Composer composer(nullptr, verification_key); create_circuit(composer, constraint_system); plonk::proof pp = { std::vector(proof, proof + length) }; - auto verifier = composer.create_verifier(); + // for inner circuit use new prover and verifier method for outer circuit use the normal prover and verifier + // TODO: either need a context flag for recursive verify or a new_recursive_verify_proof method that uses + // regular UltraVerifier + if (is_recursive) { + auto verifier = composer.create_verifier(); + verified = verifier.verify_proof(pp); + } else { + auto verifier = composer.create_ultra_with_keccak_verifier(); + verified = verifier.verify_proof(pp); + } - verified = verifier.verify_proof(pp); #ifndef __wasm__ } catch (const std::exception& e) { verified = false; diff --git a/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.hpp b/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.hpp index ced41993c3..95556f11a7 100644 --- a/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.hpp +++ b/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.hpp @@ -20,8 +20,13 @@ size_t new_proof(void* pippenger, uint8_t const* pk_buf, uint8_t const* constraint_system_buf, uint8_t const* witness_buf, - uint8_t** proof_data_buf); -bool verify_proof( - uint8_t const* g2x, uint8_t const* vk_buf, uint8_t const* constraint_system_buf, uint8_t* proof, uint32_t length); + uint8_t** proof_data_buf, + bool is_recursive); +bool verify_proof(uint8_t const* g2x, + uint8_t const* vk_buf, + uint8_t const* constraint_system_buf, + uint8_t* proof, + uint32_t length, + bool is_recursive); } // namespace acir_proofs diff --git a/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.test.cpp b/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.test.cpp index de011de21e..b344086ca3 100644 --- a/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.test.cpp +++ b/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.test.cpp @@ -4,7 +4,7 @@ #include #include -using namespace proof_system::plonk::stdlib::types; +using namespace proof_system::plonk; void create_inner_circuit(acir_format::acir_format& constraint_system, std::vector& witness) { @@ -131,10 +131,10 @@ TEST(AcirProofs, TestSerialization) uint8_t* proof_data_buf = nullptr; size_t proof_length = acir_proofs::new_proof( - pippenger_ptr, &g2x_buffer[0], pk_buf, &constraint_system_buf[0], &witness_buf[0], &proof_data_buf); + pippenger_ptr, &g2x_buffer[0], pk_buf, &constraint_system_buf[0], &witness_buf[0], &proof_data_buf, true); bool verified = acir_proofs::verify_proof( - &g2x_buffer[0], vk_buf, &constraint_system_buf[0], proof_data_buf, static_cast(proof_length)); + &g2x_buffer[0], vk_buf, &constraint_system_buf[0], proof_data_buf, static_cast(proof_length), true); EXPECT_EQ(verified, true); } @@ -178,14 +178,18 @@ TEST(AcirProofs, TestSerializationWithRecursion) uint8_t* proof_data_buf = nullptr; size_t proof_length = acir_proofs::new_proof( - pippenger_ptr, &g2x_buffer[0], pk_buf, &constraint_system_buf[0], &witness_buf[0], &proof_data_buf); + pippenger_ptr, &g2x_buffer[0], pk_buf, &constraint_system_buf[0], &witness_buf[0], &proof_data_buf, true); proof_fields_size = acir_proofs::serialize_proof_into_field_elements(proof_data_buf, &proof_data_fields, proof_length); vk_fields_size = acir_proofs::serialize_verification_key_into_field_elements( &g2x_buffer[0], vk_buf, &vk_fields, &vk_hash_buf); - bool verified = acir_proofs::verify_proof( - &g2x_buffer[0], vk_buf, &constraint_system_buf[0], proof_data_buf, static_cast(proof_length)); + bool verified = acir_proofs::verify_proof(&g2x_buffer[0], + vk_buf, + &constraint_system_buf[0], + proof_data_buf, + static_cast(proof_length), + true); EXPECT_EQ(verified, true); delete pippenger_ptr_base; @@ -198,14 +202,17 @@ TEST(AcirProofs, TestSerializationWithRecursion) fr vk_hash_value; std::vector proof_witnesses(proof_fields_size / 32); std::vector key_witnesses(vk_fields_size / 32); - memcpy(proof_witnesses.data(), (void*)proof_data_fields, proof_fields_size); - memcpy(&key_witnesses[0], (void*)vk_fields, vk_fields_size); - memcpy(&vk_hash_value, (void*)vk_hash_buf, 32); + for (size_t i = 0; i < proof_fields_size / 32; i++) { + proof_witnesses[i] = barretenberg::fr::serialize_from_buffer(&proof_data_fields[i * 32]); + } + for (size_t i = 0; i < vk_fields_size / 32; i++) { + key_witnesses[i] = barretenberg::fr::serialize_from_buffer(&vk_fields[i * 32]); + } + vk_hash_value = barretenberg::fr::serialize_from_buffer(vk_hash_buf); std::vector proof_indices; const size_t proof_size = proof_witnesses.size(); - for (size_t i = 0; i < proof_size; ++i) { proof_indices.emplace_back(static_cast(i + 19)); } @@ -296,10 +303,14 @@ TEST(AcirProofs, TestSerializationWithRecursion) uint8_t* proof_data_buf = nullptr; size_t proof_length = acir_proofs::new_proof( - pippenger_ptr, &g2x_buffer[0], pk_buf, &constraint_system_buf[0], &witness_buf[0], &proof_data_buf); - - bool verified = acir_proofs::verify_proof( - &g2x_buffer[0], vk_buf, &constraint_system_buf[0], proof_data_buf, static_cast(proof_length)); + pippenger_ptr, &g2x_buffer[0], pk_buf, &constraint_system_buf[0], &witness_buf[0], &proof_data_buf, false); + + bool verified = acir_proofs::verify_proof(&g2x_buffer[0], + vk_buf, + &constraint_system_buf[0], + proof_data_buf, + static_cast(proof_length), + false); EXPECT_EQ(verified, true); delete pippenger_ptr_base; @@ -308,6 +319,5 @@ TEST(AcirProofs, TestSerializationWithRecursion) free((void*)proof_data_buf); free((void*)proof_data_fields); free((void*)vk_fields); - free((void*)vk_hash_buf); } } diff --git a/cpp/src/barretenberg/dsl/acir_proofs/c_bind.cpp b/cpp/src/barretenberg/dsl/acir_proofs/c_bind.cpp index 4051aada3a..ac26be3fd7 100644 --- a/cpp/src/barretenberg/dsl/acir_proofs/c_bind.cpp +++ b/cpp/src/barretenberg/dsl/acir_proofs/c_bind.cpp @@ -35,19 +35,41 @@ WASM_EXPORT size_t acir_proofs_init_verification_key(void* pippenger, return acir_proofs::init_verification_key(pippenger, g2x, pk_buf, vk_buf); } +WASM_EXPORT size_t acir_serialize_verification_key_into_field_elements(uint8_t const* g2x, + uint8_t const* vk_buf, + uint8_t** serialized_vk_buf, + uint8_t** serialized_vk_hash_buf) +{ + return acir_proofs::serialize_verification_key_into_field_elements( + g2x, vk_buf, serialized_vk_buf, serialized_vk_hash_buf); +} +WASM_EXPORT size_t acir_serialize_proof_into_field_elements(uint8_t const* proof_data_buf, + uint8_t** serialized_proof_data_buf, + size_t proof_data_length) +{ + return acir_proofs::serialize_proof_into_field_elements( + proof_data_buf, serialized_proof_data_buf, proof_data_length); +} + WASM_EXPORT size_t acir_proofs_new_proof(void* pippenger, uint8_t const* g2x, uint8_t const* pk_buf, uint8_t const* constraint_system_buf, uint8_t const* witness_buf, - uint8_t** proof_data_buf) + uint8_t** proof_data_buf, + bool is_recursive) { - return acir_proofs::new_proof(pippenger, g2x, pk_buf, constraint_system_buf, witness_buf, proof_data_buf); + return acir_proofs::new_proof( + pippenger, g2x, pk_buf, constraint_system_buf, witness_buf, proof_data_buf, is_recursive); } -WASM_EXPORT bool acir_proofs_verify_proof( - uint8_t const* g2x, uint8_t const* vk_buf, uint8_t const* constraint_system_buf, uint8_t* proof, uint32_t length) +WASM_EXPORT bool acir_proofs_verify_proof(uint8_t const* g2x, + uint8_t const* vk_buf, + uint8_t const* constraint_system_buf, + uint8_t* proof, + uint32_t length, + bool is_recursive) { - return acir_proofs::verify_proof(g2x, vk_buf, constraint_system_buf, proof, length); + return acir_proofs::verify_proof(g2x, vk_buf, constraint_system_buf, proof, length, is_recursive); } } diff --git a/cpp/src/barretenberg/dsl/acir_proofs/c_bind.hpp b/cpp/src/barretenberg/dsl/acir_proofs/c_bind.hpp index a628c928cb..39591b37d1 100644 --- a/cpp/src/barretenberg/dsl/acir_proofs/c_bind.hpp +++ b/cpp/src/barretenberg/dsl/acir_proofs/c_bind.hpp @@ -14,12 +14,24 @@ WASM_EXPORT size_t acir_proofs_init_verification_key(void* pippenger, uint8_t const* g2x, uint8_t const* pk_buf, uint8_t const** vk_buf); +WASM_EXPORT size_t acir_serialize_verification_key_into_field_elements(uint8_t const* g2x, + uint8_t const* vk_buf, + uint8_t** serialized_vk_buf, + uint8_t** serialized_vk_hash_buf); +WASM_EXPORT size_t acir_serialize_proof_into_field_elements(uint8_t const* proof_data_buf, + uint8_t** serialized_proof_data_buf, + size_t proof_data_length); WASM_EXPORT size_t acir_proofs_new_proof(void* pippenger, uint8_t const* g2x, uint8_t const* pk_buf, uint8_t const* constraint_system_buf, uint8_t const* witness_buf, - uint8_t** proof_data_buf); -WASM_EXPORT bool acir_proofs_verify_proof( - uint8_t const* g2x, uint8_t const* vk_buf, uint8_t const* constraint_system_buf, uint8_t* proof, uint32_t length); + uint8_t** proof_data_buf, + bool is_recursive); +WASM_EXPORT bool acir_proofs_verify_proof(uint8_t const* g2x, + uint8_t const* vk_buf, + uint8_t const* constraint_system_buf, + uint8_t* proof, + uint32_t length, + bool is_recursive); } diff --git a/cpp/src/barretenberg/dsl/types.hpp b/cpp/src/barretenberg/dsl/types.hpp new file mode 100644 index 0000000000..be741363f9 --- /dev/null +++ b/cpp/src/barretenberg/dsl/types.hpp @@ -0,0 +1,74 @@ +#pragma once +#include "barretenberg/plonk/composer/ultra_composer.hpp" + +#include "barretenberg/plonk/proof_system/prover/prover.hpp" +#include "barretenberg/stdlib/primitives/bigfield/bigfield.hpp" +#include "barretenberg/stdlib/primitives/biggroup/biggroup.hpp" +#include "barretenberg/stdlib/primitives/bit_array/bit_array.hpp" +#include "barretenberg/stdlib/primitives/bool/bool.hpp" +#include "barretenberg/stdlib/primitives/packed_byte_array/packed_byte_array.hpp" +#include "barretenberg/stdlib/primitives/byte_array/byte_array.hpp" +#include "barretenberg/stdlib/primitives/uint/uint.hpp" +#include "barretenberg/stdlib/primitives/witness/witness.hpp" +#include "barretenberg/stdlib/primitives/bigfield/bigfield.hpp" +#include "barretenberg/stdlib/primitives/biggroup/biggroup.hpp" +#include "barretenberg/stdlib/commitment/pedersen/pedersen.hpp" +#include "barretenberg/stdlib/commitment/pedersen/pedersen_plookup.hpp" +#include "barretenberg/stdlib/merkle_tree/hash_path.hpp" +#include "barretenberg/stdlib/encryption/schnorr/schnorr.hpp" +#include "barretenberg/stdlib/primitives/curves/bn254.hpp" +#include "barretenberg/stdlib/primitives/curves/secp256k1.hpp" +#include "barretenberg/stdlib/primitives/memory/rom_table.hpp" +#include "barretenberg/stdlib/recursion/verifier/program_settings.hpp" +#include "barretenberg/stdlib/recursion/verification_key/verification_key.hpp" +#include "barretenberg/stdlib/recursion/aggregation_state/aggregation_state.hpp" + +namespace acir_format { + +using Composer = plonk::UltraComposer; + +using Prover = std::conditional_t< + std::same_as, + plonk::UltraWithKeccakProver, + std::conditional_t, plonk::TurboProver, plonk::Prover>>; + +using Verifier = std::conditional_t< + std::same_as, + plonk::UltraWithKeccakVerifier, + std::conditional_t, plonk::TurboVerifier, plonk::Verifier>>; + +using RecursiveProver = plonk::UltraProver; + +using witness_ct = proof_system::plonk::stdlib::witness_t; +using public_witness_ct = proof_system::plonk::stdlib::public_witness_t; +using bool_ct = proof_system::plonk::stdlib::bool_t; +using byte_array_ct = proof_system::plonk::stdlib::byte_array; +using packed_byte_array_ct = proof_system::plonk::stdlib::packed_byte_array; +using field_ct = proof_system::plonk::stdlib::field_t; +using suint_ct = proof_system::plonk::stdlib::safe_uint_t; +using uint8_ct = proof_system::plonk::stdlib::uint8; +using uint16_ct = proof_system::plonk::stdlib::uint16; +using uint32_ct = proof_system::plonk::stdlib::uint32; +using uint64_ct = proof_system::plonk::stdlib::uint64; +using bit_array_ct = proof_system::plonk::stdlib::bit_array; +using fq_ct = proof_system::plonk::stdlib::bigfield; +using biggroup_ct = proof_system::plonk::stdlib::element; +using point_ct = proof_system::plonk::stdlib::point; +using pedersen_commitment = proof_system::plonk::stdlib::pedersen_commitment; +using group_ct = proof_system::plonk::stdlib::group; +using bn254 = proof_system::plonk::stdlib::bn254; +using secp256k1_ct = proof_system::plonk::stdlib::secp256k1; + +using hash_path_ct = proof_system::plonk::stdlib::merkle_tree::hash_path; + +using schnorr_signature_bits_ct = proof_system::plonk::stdlib::schnorr::signature_bits; + +// Ultra-composer specific typesv +using rom_table_ct = proof_system::plonk::stdlib::rom_table; + +using verification_key_ct = proof_system::plonk::stdlib::recursion::verification_key; +using aggregation_state_ct = proof_system::plonk::stdlib::recursion::aggregation_state; +using noir_recursive_settings = proof_system::plonk::stdlib::recursion::recursive_ultra_verifier_settings; +using Transcript_ct = proof_system::plonk::stdlib::recursion::Transcript; + +} // namespace acir_format diff --git a/cpp/src/barretenberg/ecc/fields/field.hpp b/cpp/src/barretenberg/ecc/fields/field.hpp index 66c59ea0d2..80a87f46af 100644 --- a/cpp/src/barretenberg/ecc/fields/field.hpp +++ b/cpp/src/barretenberg/ecc/fields/field.hpp @@ -25,6 +25,10 @@ namespace barretenberg { template struct alignas(32) field { public: // We don't initialize data by default since we'd lose a lot of time on pointless initializations. + // Other alternatives have been noted, such as casting to get around constructors where they matter, + // however it is felt that sanitizer tools (e.g. MSAN) can detect garbage well, whereas doing + // hacky casts where needed would require rework to critical algos like MSM, FFT, Sumcheck. + // Instead, the recommended solution is use an explicit = 0 where initialization is important. field() noexcept {} constexpr field(const uint256_t& input) noexcept diff --git a/cpp/src/barretenberg/honk/composer/composer_helper/standard_honk_composer_helper.cpp b/cpp/src/barretenberg/honk/composer/composer_helper/standard_honk_composer_helper.cpp index 8c61e02a5d..6867d9af37 100644 --- a/cpp/src/barretenberg/honk/composer/composer_helper/standard_honk_composer_helper.cpp +++ b/cpp/src/barretenberg/honk/composer/composer_helper/standard_honk_composer_helper.cpp @@ -165,6 +165,7 @@ StandardProver StandardHonkComposerHelper::create_prover( compute_witness(circuit_constructor); size_t num_sumcheck_rounds(circuit_proving_key->log_circuit_size); + // TODO(luke): what is this manifest? Remove? auto manifest = Flavor::create_manifest(circuit_constructor.public_inputs.size(), num_sumcheck_rounds); StandardProver output_state(std::move(wire_polynomials), circuit_proving_key); diff --git a/cpp/src/barretenberg/honk/composer/composer_helper/ultra_honk_composer_helper.cpp b/cpp/src/barretenberg/honk/composer/composer_helper/ultra_honk_composer_helper.cpp new file mode 100644 index 0000000000..1ef6fdcba0 --- /dev/null +++ b/cpp/src/barretenberg/honk/composer/composer_helper/ultra_honk_composer_helper.cpp @@ -0,0 +1,356 @@ +#include "ultra_honk_composer_helper.hpp" +#include "barretenberg/honk/proof_system/ultra_prover.hpp" +#include "barretenberg/plonk/proof_system/types/program_settings.hpp" +#include "barretenberg/plonk/proof_system/types/prover_settings.hpp" +// #include "barretenberg/plonk/proof_system/verifier/verifier.hpp" +#include "barretenberg/proof_system/circuit_constructors/ultra_circuit_constructor.hpp" +#include "barretenberg/proof_system/composer/permutation_helper.hpp" +#include "barretenberg/plonk/proof_system/commitment_scheme/kate_commitment_scheme.hpp" + +#include +#include +#include +#include + +namespace proof_system::honk { + +/** + * @brief Compute witness polynomials + * + * TODO(luke): The wire polynomials are returned directly whereas the sorted list polys are added to the proving + * key. This should be made consistent once Cody's Flavor work is settled. + */ +template +void UltraHonkComposerHelper::compute_witness(CircuitConstructor& circuit_constructor) +{ + if (computed_witness) { + return; + } + + size_t tables_size = 0; + size_t lookups_size = 0; + for (const auto& table : circuit_constructor.lookup_tables) { + tables_size += table.size; + lookups_size += table.lookup_gates.size(); + } + + const size_t filled_gates = circuit_constructor.num_gates + circuit_constructor.public_inputs.size(); + const size_t total_num_gates = std::max(filled_gates, tables_size + lookups_size); + + const size_t subgroup_size = circuit_constructor.get_circuit_subgroup_size(total_num_gates + NUM_RESERVED_GATES); + + // Pad the wires (pointers to `witness_indices` of the `variables` vector). + // Note: the remaining NUM_RESERVED_GATES indices are padded with zeros within `compute_witness_base` (called + // next). + for (size_t i = filled_gates; i < total_num_gates; ++i) { + circuit_constructor.w_l.emplace_back(circuit_constructor.zero_idx); + circuit_constructor.w_r.emplace_back(circuit_constructor.zero_idx); + circuit_constructor.w_o.emplace_back(circuit_constructor.zero_idx); + circuit_constructor.w_4.emplace_back(circuit_constructor.zero_idx); + } + + // TODO(#340)(luke): within compute_witness_base, the 3rd argument is used in the calculation of the dyadic circuit + // size (subgroup_size). Here (and in other split composers) we're passing in NUM_RANDOMIZED_GATES, but elsewhere, + // e.g. directly above, we use NUM_RESERVED_GATES in a similar role. Therefore, these two constants must be equal + // for everything to be consistent. What we should do is compute the dyadic circuit size once and for all then pass + // that around rather than computing in multiple places. + wire_polynomials = compute_witness_base(circuit_constructor, total_num_gates, NUM_RANDOMIZED_GATES); + + polynomial s_1(subgroup_size); + polynomial s_2(subgroup_size); + polynomial s_3(subgroup_size); + polynomial s_4(subgroup_size); + polynomial z_lookup(subgroup_size + 1); // Only instantiated in this function; nothing assigned. + + // Save space for adding random scalars in the s polynomial later. The subtracted 1 allows us to insert a `1` at the + // end, to ensure the evaluations (and hence coefficients) aren't all 0. See ComposerBase::compute_proving_key_base + // for further explanation, as a similar trick is done there. + size_t count = subgroup_size - tables_size - lookups_size - s_randomness - 1; + for (size_t i = 0; i < count; ++i) { + s_1[i] = 0; + s_2[i] = 0; + s_3[i] = 0; + s_4[i] = 0; + } + + for (auto& table : circuit_constructor.lookup_tables) { + const fr table_index(table.table_index); + auto& lookup_gates = table.lookup_gates; + for (size_t i = 0; i < table.size; ++i) { + if (table.use_twin_keys) { + lookup_gates.push_back({ + { + table.column_1[i].from_montgomery_form().data[0], + table.column_2[i].from_montgomery_form().data[0], + }, + { + table.column_3[i], + 0, + }, + }); + } else { + lookup_gates.push_back({ + { + table.column_1[i].from_montgomery_form().data[0], + 0, + }, + { + table.column_2[i], + table.column_3[i], + }, + }); + } + } + +#ifdef NO_TBB + std::sort(lookup_gates.begin(), lookup_gates.end()); +#else + std::sort(std::execution::par_unseq, lookup_gates.begin(), lookup_gates.end()); +#endif + + for (const auto& entry : lookup_gates) { + const auto components = entry.to_sorted_list_components(table.use_twin_keys); + s_1[count] = components[0]; + s_2[count] = components[1]; + s_3[count] = components[2]; + s_4[count] = table_index; + ++count; + } + } + + // Initialise the `s_randomness` positions in the s polynomials with 0. + // These will be the positions where we will be adding random scalars to add zero knowledge + // to plookup (search for `Blinding` in plonk/proof_system/widgets/random_widgets/plookup_widget_impl.hpp + // ProverPlookupWidget::compute_sorted_list_polynomial()) + for (size_t i = 0; i < s_randomness; ++i) { + s_1[count] = 0; + s_2[count] = 0; + s_3[count] = 0; + s_4[count] = 0; + ++count; + } + + // TODO(luke): Adding these to the key for now but this is inconsistent since these are 'witness' polys. Need + // to see what becomes of the proving key before making a decision here. + circuit_proving_key->polynomial_store.put("s_1_lagrange", std::move(s_1)); + circuit_proving_key->polynomial_store.put("s_2_lagrange", std::move(s_2)); + circuit_proving_key->polynomial_store.put("s_3_lagrange", std::move(s_3)); + circuit_proving_key->polynomial_store.put("s_4_lagrange", std::move(s_4)); + + computed_witness = true; +} + +template +UltraProver UltraHonkComposerHelper::create_prover(CircuitConstructor& circuit_constructor) +{ + finalize_circuit(circuit_constructor); + + compute_proving_key(circuit_constructor); + compute_witness(circuit_constructor); + + UltraProver output_state(std::move(wire_polynomials), circuit_proving_key); + + return output_state; +} + +// /** +// * Create verifier: compute verification key, +// * initialize verifier with it and an initial manifest and initialize commitment_scheme. +// * +// * @return The verifier. +// * */ +// // TODO(Cody): This should go away altogether. +// template +// plonk::UltraVerifier UltraHonkComposerHelper::create_verifier( +// const CircuitConstructor& circuit_constructor) +// { +// auto verification_key = compute_verification_key(circuit_constructor); + +// plonk::UltraVerifier output_state(circuit_verification_key, +// create_manifest(circuit_constructor.public_inputs.size())); + +// std::unique_ptr> kate_commitment_scheme = +// std::make_unique>(); + +// output_state.commitment_scheme = std::move(kate_commitment_scheme); + +// return output_state; +// } + +template +std::shared_ptr UltraHonkComposerHelper::compute_proving_key( + const CircuitConstructor& circuit_constructor) +{ + if (circuit_proving_key) { + return circuit_proving_key; + } + + size_t tables_size = 0; + size_t lookups_size = 0; + for (const auto& table : circuit_constructor.lookup_tables) { + tables_size += table.size; + lookups_size += table.lookup_gates.size(); + } + + const size_t minimum_circuit_size = tables_size + lookups_size; + const size_t num_randomized_gates = NUM_RANDOMIZED_GATES; + // Initialize circuit_proving_key + // TODO(#229)(Kesha): replace composer types. + circuit_proving_key = initialize_proving_key( + circuit_constructor, crs_factory_.get(), minimum_circuit_size, num_randomized_gates, ComposerType::PLOOKUP); + + construct_lagrange_selector_forms(circuit_constructor, circuit_proving_key.get()); + + // TODO(#217)(luke): Naively enforcing non-zero selectors for Honk will result in some relations not being + // satisfied. + // enforce_nonzero_polynomial_selectors(circuit_constructor, circuit_proving_key.get()); + + compute_honk_generalized_sigma_permutations(circuit_constructor, + circuit_proving_key.get()); + + compute_first_and_last_lagrange_polynomials(circuit_proving_key.get()); + + const size_t subgroup_size = circuit_proving_key->circuit_size; + + polynomial poly_q_table_column_1(subgroup_size); + polynomial poly_q_table_column_2(subgroup_size); + polynomial poly_q_table_column_3(subgroup_size); + polynomial poly_q_table_column_4(subgroup_size); + + size_t offset = subgroup_size - tables_size - s_randomness - 1; + + // Create lookup selector polynomials which interpolate each table column. + // Our selector polys always need to interpolate the full subgroup size, so here we offset so as to + // put the table column's values at the end. (The first gates are for non-lookup constraints). + // [0, ..., 0, ...table, 0, 0, 0, x] + // ^^^^^^^^^ ^^^^^^^^ ^^^^^^^ ^nonzero to ensure uniqueness and to avoid infinity commitments + // | table randomness + // ignored, as used for regular constraints and padding to the next power of 2. + + for (size_t i = 0; i < offset; ++i) { + poly_q_table_column_1[i] = 0; + poly_q_table_column_2[i] = 0; + poly_q_table_column_3[i] = 0; + poly_q_table_column_4[i] = 0; + } + + for (const auto& table : circuit_constructor.lookup_tables) { + const fr table_index(table.table_index); + + for (size_t i = 0; i < table.size; ++i) { + poly_q_table_column_1[offset] = table.column_1[i]; + poly_q_table_column_2[offset] = table.column_2[i]; + poly_q_table_column_3[offset] = table.column_3[i]; + poly_q_table_column_4[offset] = table_index; + ++offset; + } + } + + // Initialise the last `s_randomness` positions in table polynomials with 0. We don't need to actually randomise + // the table polynomials. + for (size_t i = 0; i < s_randomness; ++i) { + poly_q_table_column_1[offset] = 0; + poly_q_table_column_2[offset] = 0; + poly_q_table_column_3[offset] = 0; + poly_q_table_column_4[offset] = 0; + ++offset; + } + + // // In the case of using UltraPlonkComposer for a circuit which does _not_ make use of any lookup tables, all + // four + // // table columns would be all zeros. This would result in these polys' commitments all being the point at + // infinity + // // (which is bad because our point arithmetic assumes we'll never operate on the point at infinity). To avoid + // this, + // // we set the last evaluation of each poly to be nonzero. The last `num_roots_cut_out_of_vanishing_poly = 4` + // // evaluations are ignored by constraint checks; we arbitrarily choose the very-last evaluation to be nonzero. + // See + // // ComposerBase::compute_proving_key_base for further explanation, as a similar trick is done there. We could + // // have chosen `1` for each such evaluation here, but that would have resulted in identical commitments for + // // all four columns. We don't want to have equal commitments, because biggroup operations assume no points are + // // equal, so if we tried to verify an ultra proof in a circuit, the biggroup operations would fail. To combat + // // this, we just choose distinct values: + size_t num_selectors = circuit_constructor.num_selectors; + ASSERT(offset == subgroup_size - 1); + auto unique_last_value = num_selectors + 1; // Note: in compute_proving_key_base, moments earlier, each selector + // vector was given a unique last value from 1..num_selectors. So we + // avoid those values and continue the count, to ensure uniqueness. + poly_q_table_column_1[subgroup_size - 1] = unique_last_value; + poly_q_table_column_2[subgroup_size - 1] = ++unique_last_value; + poly_q_table_column_3[subgroup_size - 1] = ++unique_last_value; + poly_q_table_column_4[subgroup_size - 1] = ++unique_last_value; + + circuit_proving_key->polynomial_store.put("table_value_1_lagrange", std::move(poly_q_table_column_1)); + circuit_proving_key->polynomial_store.put("table_value_2_lagrange", std::move(poly_q_table_column_2)); + circuit_proving_key->polynomial_store.put("table_value_3_lagrange", std::move(poly_q_table_column_3)); + circuit_proving_key->polynomial_store.put("table_value_4_lagrange", std::move(poly_q_table_column_4)); + + // Copy memory read/write record data into proving key. Prover needs to know which gates contain a read/write + // 'record' witness on the 4th wire. This wire value can only be fully computed once the first 3 wire polynomials + // have been committed to. The 4th wire on these gates will be a random linear combination of the first 3 wires, + // using the plookup challenge `eta` + std::copy(circuit_constructor.memory_read_records.begin(), + circuit_constructor.memory_read_records.end(), + std::back_inserter(circuit_proving_key->memory_read_records)); + std::copy(circuit_constructor.memory_write_records.begin(), + circuit_constructor.memory_write_records.end(), + std::back_inserter(circuit_proving_key->memory_write_records)); + + circuit_proving_key->recursive_proof_public_input_indices = + std::vector(recursive_proof_public_input_indices.begin(), recursive_proof_public_input_indices.end()); + + circuit_proving_key->contains_recursive_proof = contains_recursive_proof; + + return circuit_proving_key; +} + +// /** +// * Compute verification key consisting of selector precommitments. +// * +// * @return Pointer to created circuit verification key. +// * */ +// template +// std::shared_ptr UltraHonkComposerHelper::compute_verification_key( +// const CircuitConstructor& circuit_constructor) +// { +// if (circuit_verification_key) { +// return circuit_verification_key; +// } + +// if (!circuit_proving_key) { +// compute_proving_key(circuit_constructor); +// } +// circuit_verification_key = compute_verification_key_common(circuit_proving_key, +// crs_factory_->get_verifier_crs()); + +// circuit_verification_key->composer_type = type; // Invariably plookup for this class. + +// // See `add_recusrive_proof()` for how this recursive data is assigned. +// circuit_verification_key->recursive_proof_public_input_indices = +// std::vector(recursive_proof_public_input_indices.begin(), +// recursive_proof_public_input_indices.end()); + +// circuit_verification_key->contains_recursive_proof = contains_recursive_proof; + +// return circuit_verification_key; +// } + +// template +// void UltraHonkComposerHelper::add_table_column_selector_poly_to_proving_key( +// polynomial& selector_poly_lagrange_form, const std::string& tag) +// { +// polynomial selector_poly_lagrange_form_copy(selector_poly_lagrange_form, circuit_proving_key->small_domain.size); + +// selector_poly_lagrange_form.ifft(circuit_proving_key->small_domain); +// auto& selector_poly_coeff_form = selector_poly_lagrange_form; + +// polynomial selector_poly_coset_form(selector_poly_coeff_form, circuit_proving_key->circuit_size * 4); +// selector_poly_coset_form.coset_fft(circuit_proving_key->large_domain); + +// circuit_proving_key->polynomial_store.put(tag, std::move(selector_poly_coeff_form)); +// circuit_proving_key->polynomial_store.put(tag + "_lagrange", std::move(selector_poly_lagrange_form_copy)); +// circuit_proving_key->polynomial_store.put(tag + "_fft", std::move(selector_poly_coset_form)); +// } + +template class UltraHonkComposerHelper; +} // namespace proof_system::honk diff --git a/cpp/src/barretenberg/honk/composer/composer_helper/ultra_honk_composer_helper.hpp b/cpp/src/barretenberg/honk/composer/composer_helper/ultra_honk_composer_helper.hpp new file mode 100644 index 0000000000..de309735e6 --- /dev/null +++ b/cpp/src/barretenberg/honk/composer/composer_helper/ultra_honk_composer_helper.hpp @@ -0,0 +1,72 @@ +#pragma once + +#include "barretenberg/proof_system/composer/composer_helper_lib.hpp" +#include "barretenberg/plonk/composer/splitting_tmp/composer_helper/composer_helper_lib.hpp" +#include "barretenberg/srs/reference_string/file_reference_string.hpp" +#include "barretenberg/plonk/proof_system/proving_key/proving_key.hpp" +#include "barretenberg/honk/proof_system/ultra_prover.hpp" +// #include "barretenberg/plonk/proof_system/verifier/verifier.hpp" + +#include +#include +#include +#include + +namespace proof_system::honk { +// TODO(Kesha): change initializations to specify this parameter +// Cody: What does this mean? +template class UltraHonkComposerHelper { + public: + // TODO(#340)(luke): In the split composers, NUM_RANDOMIZED_GATES has replaced NUM_RESERVED_GATES (in some places) + // to determine the next-power-of-2 circuit size. (There are some places in this composer that still use + // NUM_RESERVED_GATES). Therefore for consistency within this composer itself, and consistency with the original + // Ultra Composer, this value must match that of NUM_RESERVED_GATES. This issue needs to be reconciled + // simultaneously here and in the other split composers. + static constexpr size_t NUM_RANDOMIZED_GATES = 4; // equal to the number of multilinear evaluations leaked + static constexpr size_t program_width = CircuitConstructor::program_width; + std::vector wire_polynomials; + std::shared_ptr circuit_proving_key; + std::shared_ptr circuit_verification_key; + // TODO(#218)(kesha): we need to put this into the commitment key, so that the composer doesn't have to handle srs + // at all + std::shared_ptr crs_factory_; + + std::vector recursive_proof_public_input_indices; + bool contains_recursive_proof = false; + bool computed_witness = false; + + // This variable controls the amount with which the lookup table and witness values need to be shifted + // above to make room for adding randomness into the permutation and witness polynomials in the plookup widget. + // This must be (num_roots_cut_out_of_the_vanishing_polynomial - 1), since the variable num_roots_cut_out_of_ + // vanishing_polynomial cannot be trivially fetched here, I am directly setting this to 4 - 1 = 3. + static constexpr size_t s_randomness = 3; + + explicit UltraHonkComposerHelper(std::shared_ptr crs_factory) + : crs_factory_(std::move(crs_factory)) + {} + + UltraHonkComposerHelper(std::shared_ptr p_key, std::shared_ptr v_key) + : circuit_proving_key(std::move(p_key)) + , circuit_verification_key(std::move(v_key)) + {} + + UltraHonkComposerHelper(UltraHonkComposerHelper&& other) noexcept = default; + UltraHonkComposerHelper(UltraHonkComposerHelper const& other) noexcept = default; + UltraHonkComposerHelper& operator=(UltraHonkComposerHelper&& other) noexcept = default; + UltraHonkComposerHelper& operator=(UltraHonkComposerHelper const& other) noexcept = default; + ~UltraHonkComposerHelper() = default; + + void finalize_circuit(CircuitConstructor& circuit_constructor) { circuit_constructor.finalize_circuit(); }; + + std::shared_ptr compute_proving_key(const CircuitConstructor& circuit_constructor); + // std::shared_ptr compute_verification_key(const CircuitConstructor& circuit_constructor); + + void compute_witness(CircuitConstructor& circuit_constructor); + + UltraProver create_prover(CircuitConstructor& circuit_constructor); + // UltraVerifier create_verifier(const CircuitConstructor& circuit_constructor); + + void add_table_column_selector_poly_to_proving_key(polynomial& small, const std::string& tag); +}; + +} // namespace proof_system::honk diff --git a/cpp/src/barretenberg/honk/composer/standard_honk_composer.test.cpp b/cpp/src/barretenberg/honk/composer/standard_honk_composer.test.cpp index 84717e21a4..8ab126ba01 100644 --- a/cpp/src/barretenberg/honk/composer/standard_honk_composer.test.cpp +++ b/cpp/src/barretenberg/honk/composer/standard_honk_composer.test.cpp @@ -304,109 +304,6 @@ TEST(StandardHonkComposer, VerificationKeyCreation) composer.circuit_constructor.selectors.size() + composer.num_wires * 2 + 2); } -/** - * @brief A test taking sumcheck relations and applying them to the witness and selector polynomials to ensure that the - * realtions are correct. - * - * TODO(Kesha): We'll have to update this function once we add zk, since the relation will be incorrect for he first few - * indices - * - */ -TEST(StandardHonkComposer, SumcheckRelationCorrectness) -{ - // Create a composer and a dummy circuit with a few gates - StandardHonkComposer composer = StandardHonkComposer(); - static const size_t num_wires = StandardHonkComposer::num_wires; - fr a = fr::one(); - // Using the public variable to check that public_input_delta is computed and added to the relation correctly - uint32_t a_idx = composer.add_public_variable(a); - fr b = fr::one(); - fr c = a + b; - fr d = a + c; - uint32_t b_idx = composer.add_variable(b); - uint32_t c_idx = composer.add_variable(c); - uint32_t d_idx = composer.add_variable(d); - for (size_t i = 0; i < 16; i++) { - composer.create_add_gate({ a_idx, b_idx, c_idx, fr::one(), fr::one(), fr::neg_one(), fr::zero() }); - composer.create_add_gate({ d_idx, c_idx, a_idx, fr::one(), fr::neg_one(), fr::neg_one(), fr::zero() }); - } - // Create a prover (it will compute proving key and witness) - auto prover = composer.create_prover(); - - // Generate beta and gamma - fr beta = fr::random_element(); - fr gamma = fr::random_element(); - - // Compute public input delta - const auto public_inputs = composer.circuit_constructor.get_public_inputs(); - auto public_input_delta = - honk::compute_public_input_delta(public_inputs, beta, gamma, prover.key->circuit_size); - - sumcheck::RelationParameters params{ - .beta = beta, - .gamma = gamma, - .public_input_delta = public_input_delta, - }; - - constexpr size_t num_polynomials = proof_system::honk::StandardArithmetization::NUM_POLYNOMIALS; - // Compute grand product polynomial - polynomial z_perm_poly = - prover_library::compute_permutation_grand_product(prover.key, prover.wire_polynomials, beta, gamma); - - // Create an array of spans to the underlying polynomials to more easily - // get the transposition. - // Ex: polynomial_spans[3][i] returns the i-th coefficient of the third polynomial - // in the list below - std::array, num_polynomials> evaluations_array; - - using POLYNOMIAL = proof_system::honk::StandardArithmetization::POLYNOMIAL; - evaluations_array[POLYNOMIAL::W_L] = prover.wire_polynomials[0]; - evaluations_array[POLYNOMIAL::W_R] = prover.wire_polynomials[1]; - evaluations_array[POLYNOMIAL::W_O] = prover.wire_polynomials[2]; - evaluations_array[POLYNOMIAL::Z_PERM] = z_perm_poly; - evaluations_array[POLYNOMIAL::Z_PERM_SHIFT] = z_perm_poly.shifted(); - evaluations_array[POLYNOMIAL::Q_M] = prover.key->polynomial_store.get("q_m_lagrange"); - evaluations_array[POLYNOMIAL::Q_L] = prover.key->polynomial_store.get("q_1_lagrange"); - evaluations_array[POLYNOMIAL::Q_R] = prover.key->polynomial_store.get("q_2_lagrange"); - evaluations_array[POLYNOMIAL::Q_O] = prover.key->polynomial_store.get("q_3_lagrange"); - evaluations_array[POLYNOMIAL::Q_C] = prover.key->polynomial_store.get("q_c_lagrange"); - evaluations_array[POLYNOMIAL::SIGMA_1] = prover.key->polynomial_store.get("sigma_1_lagrange"); - evaluations_array[POLYNOMIAL::SIGMA_2] = prover.key->polynomial_store.get("sigma_2_lagrange"); - evaluations_array[POLYNOMIAL::SIGMA_3] = prover.key->polynomial_store.get("sigma_3_lagrange"); - evaluations_array[POLYNOMIAL::ID_1] = prover.key->polynomial_store.get("id_1_lagrange"); - evaluations_array[POLYNOMIAL::ID_2] = prover.key->polynomial_store.get("id_2_lagrange"); - evaluations_array[POLYNOMIAL::ID_3] = prover.key->polynomial_store.get("id_3_lagrange"); - evaluations_array[POLYNOMIAL::LAGRANGE_FIRST] = prover.key->polynomial_store.get("L_first_lagrange"); - evaluations_array[POLYNOMIAL::LAGRANGE_LAST] = prover.key->polynomial_store.get("L_last_lagrange"); - - // Construct the round for applying sumcheck relations and results for storing computed results - auto relations = std::tuple(honk::sumcheck::ArithmeticRelation(), - honk::sumcheck::GrandProductComputationRelation(), - honk::sumcheck::GrandProductInitializationRelation()); - - fr result = 0; - for (size_t i = 0; i < prover.key->circuit_size; i++) { - // Compute an array containing all the evaluations at a given row i - std::array evaluations_at_index_i; - for (size_t j = 0; j < num_polynomials; ++j) { - evaluations_at_index_i[j] = evaluations_array[j][i]; - } - - // For each relation, call the `accumulate_relation_evaluation` over all witness/selector values at the - // i-th row/vertex of the hypercube. - // We use ASSERT_EQ instead of EXPECT_EQ so that the tests stops at the first index at which the result is not - // 0, since result = 0 + C(transposed), which we expect will equal 0. - std::get<0>(relations).add_full_relation_value_contribution(result, evaluations_at_index_i, params); - ASSERT_EQ(result, 0); - - std::get<1>(relations).add_full_relation_value_contribution(result, evaluations_at_index_i, params); - ASSERT_EQ(result, 0); - - std::get<2>(relations).add_full_relation_value_contribution(result, evaluations_at_index_i, params); - ASSERT_EQ(result, 0); - } -} - TEST(StandardHonkComposer, BaseCase) { auto composer = StandardHonkComposer(); diff --git a/cpp/src/barretenberg/honk/composer/ultra_honk_composer.hpp b/cpp/src/barretenberg/honk/composer/ultra_honk_composer.hpp new file mode 100644 index 0000000000..5fa40b4626 --- /dev/null +++ b/cpp/src/barretenberg/honk/composer/ultra_honk_composer.hpp @@ -0,0 +1,470 @@ +#pragma once +#include "barretenberg/plonk/composer/plookup_tables/plookup_tables.hpp" +#include "barretenberg/honk/proof_system/ultra_prover.hpp" +#include "barretenberg/proof_system/circuit_constructors/ultra_circuit_constructor.hpp" +#include "barretenberg/honk/composer/composer_helper/ultra_honk_composer_helper.hpp" +#include + +namespace proof_system::honk { + +class UltraHonkComposer { + + public: + // An instantiation of the circuit constructor that only depends on arithmetization, not on the proof system + UltraCircuitConstructor circuit_constructor; + // Composer helper contains all proof-related material that is separate from circuit creation such as: + // 1) Proving and verification keys + // 2) CRS + // 3) Converting variables to witness vectors/polynomials + UltraHonkComposerHelper composer_helper; + size_t& num_gates; + + UltraHonkComposer() + : UltraHonkComposer("../srs_db/ignition", 0){}; + + UltraHonkComposer(std::string const& crs_path, const size_t size_hint) + : UltraHonkComposer(std::unique_ptr(new FileReferenceStringFactory(crs_path)), + size_hint){}; + + UltraHonkComposer(std::shared_ptr const& crs_factory, const size_t size_hint) + : circuit_constructor(size_hint) + , composer_helper(crs_factory) + , num_gates(circuit_constructor.num_gates){}; + + UltraHonkComposer(std::shared_ptr const& p_key, + std::shared_ptr const& v_key, + size_t size_hint = 0); + UltraHonkComposer(UltraHonkComposer&& other) = default; + UltraHonkComposer& operator=(UltraHonkComposer&& other) = delete; + ~UltraHonkComposer() = default; + + uint32_t get_zero_idx() { return circuit_constructor.zero_idx; } + + uint32_t add_variable(const barretenberg::fr& in) { return circuit_constructor.add_variable(in); } + + barretenberg::fr get_variable(const uint32_t index) const { return circuit_constructor.get_variable(index); } + + void finalize_circuit() { circuit_constructor.finalize_circuit(); }; + + UltraProver create_prover() { return composer_helper.create_prover(circuit_constructor); }; + // UltraVerifier create_verifier() { return composer_helper.create_verifier(circuit_constructor); }; + + void create_add_gate(const add_triple& in) { circuit_constructor.create_add_gate(in); } + + void create_big_add_gate(const add_quad& in, const bool use_next_gate_w_4 = false) + { + circuit_constructor.create_big_add_gate(in, use_next_gate_w_4); + }; + + // void create_big_add_gate_with_bit_extraction(const add_quad& in); + // void create_big_mul_gate(const mul_quad& in); + // void create_balanced_add_gate(const add_quad& in); + + // void create_mul_gate(const mul_triple& in) override; + // void create_bool_gate(const uint32_t a) override; + // void create_poly_gate(const poly_triple& in) override; + void create_ecc_add_gate(const ecc_add_gate& in) { circuit_constructor.create_ecc_add_gate(in); }; + + // void fix_witness(const uint32_t witness_index, const barretenberg::fr& witness_value); + + // void add_recursive_proof(const std::vector& proof_output_witness_indices) + // { + // if (contains_recursive_proof) { + // failure("added recursive proof when one already exists"); + // } + // contains_recursive_proof = true; + + // for (const auto& idx : proof_output_witness_indices) { + // set_public_input(idx); + // recursive_proof_public_input_indices.push_back((uint32_t)(public_inputs.size() - 1)); + // } + // } + + void create_new_range_constraint(const uint32_t variable_index, + const uint64_t target_range, + std::string const msg = "create_new_range_constraint") + { + circuit_constructor.create_new_range_constraint(variable_index, target_range, msg); + }; + // void create_range_constraint(const uint32_t variable_index, const size_t num_bits, std::string const& msg) + // { + // if (num_bits <= DEFAULT_PLOOKUP_RANGE_BITNUM) { + // /** + // * N.B. if `variable_index` is not used in any arithmetic constraints, this will create an unsatisfiable + // * circuit! + // * this range constraint will increase the size of the 'sorted set' of range-constrained integers + // by 1. + // * The 'non-sorted set' of range-constrained integers is a subset of the wire indices of all + // arithmetic + // * gates. No arithemtic gate => size imbalance between sorted and non-sorted sets. Checking for this + // * and throwing an error would require a refactor of the Composer to catelog all 'orphan' variables + // not + // * assigned to gates. + // **/ + // create_new_range_constraint(variable_index, 1ULL << num_bits, msg); + // } else { + // decompose_into_default_range(variable_index, num_bits, DEFAULT_PLOOKUP_RANGE_BITNUM, msg); + // } + // } + + // accumulator_triple create_logic_constraint(const uint32_t a, + // const uint32_t b, + // const size_t num_bits, + // bool is_xor_gate); + // accumulator_triple create_and_constraint(const uint32_t a, const uint32_t b, const size_t num_bits); + // accumulator_triple create_xor_constraint(const uint32_t a, const uint32_t b, const size_t num_bits); + + // uint32_t put_constant_variable(const barretenberg::fr& variable); + + // size_t get_num_constant_gates() const override { return 0; } + + // /** + // * @brief Get the final number of gates in a circuit, which consists of the sum of: + // * 1) Current number number of actual gates + // * 2) Number of public inputs, as we'll need to add a gate for each of them + // * 3) Number of Rom array-associated gates + // * 4) NUmber of range-list associated gates + // * + // * + // * @param count return arument, number of existing gates + // * @param rangecount return argument, extra gates due to range checks + // * @param romcount return argument, extra gates due to rom reads + // * @param ramcount return argument, extra gates due to ram read/writes + // */ + // void get_num_gates_split_into_components(size_t& count, + // size_t& rangecount, + // size_t& romcount, + // size_t& ramcount) const + // { + // count = num_gates; + // // each ROM gate adds +1 extra gate due to the rom reads being copied to a sorted list set + // for (size_t i = 0; i < rom_arrays.size(); ++i) { + // for (size_t j = 0; j < rom_arrays[i].state.size(); ++j) { + // if (rom_arrays[i].state[j][0] == UNINITIALIZED_MEMORY_RECORD) { + // romcount += 2; + // } + // } + // romcount += (rom_arrays[i].records.size()); + // romcount += 1; // we add an addition gate after procesing a rom array + // } + + // constexpr size_t gate_width = ultra_settings::program_width; + // // each RAM gate adds +2 extra gates due to the ram reads being copied to a sorted list set, + // // as well as an extra gate to validate timestamps + // std::vector ram_timestamps; + // std::vector ram_range_sizes; + // std::vector ram_range_exists; + // for (size_t i = 0; i < ram_arrays.size(); ++i) { + // for (size_t j = 0; j < ram_arrays[i].state.size(); ++j) { + // if (ram_arrays[i].state[j] == UNINITIALIZED_MEMORY_RECORD) { + // ramcount += NUMBER_OF_GATES_PER_RAM_ACCESS; + // } + // } + // ramcount += (ram_arrays[i].records.size() * NUMBER_OF_GATES_PER_RAM_ACCESS); + // ramcount += NUMBER_OF_ARITHMETIC_GATES_PER_RAM_ARRAY; // we add an addition gate after procesing a ram + // array + + // // there will be 'max_timestamp' number of range checks, need to calculate. + // const auto max_timestamp = ram_arrays[i].access_count - 1; + + // // if a range check of length `max_timestamp` already exists, we are double counting. + // // We record `ram_timestamps` to detect and correct for this error when we process range lists. + // ram_timestamps.push_back(max_timestamp); + // size_t padding = (gate_width - (max_timestamp % gate_width)) % gate_width; + // if (max_timestamp == gate_width) + // padding += gate_width; + // const size_t ram_range_check_list_size = max_timestamp + padding; + + // size_t ram_range_check_gate_count = (ram_range_check_list_size / gate_width); + // ram_range_check_gate_count += 1; // we need to add 1 extra addition gates for every distinct range list + + // ram_range_sizes.push_back(ram_range_check_gate_count); + // ram_range_exists.push_back(false); + // // rangecount += ram_range_check_gate_count; + // } + // for (const auto& list : range_lists) { + // auto list_size = list.second.variable_indices.size(); + // size_t padding = (gate_width - (list.second.variable_indices.size() % gate_width)) % gate_width; + // if (list.second.variable_indices.size() == gate_width) + // padding += gate_width; + // list_size += padding; + + // for (size_t i = 0; i < ram_timestamps.size(); ++i) { + // if (list.second.target_range == ram_timestamps[i]) { + // ram_range_exists[i] = true; + // } + // } + // rangecount += (list_size / gate_width); + // rangecount += 1; // we need to add 1 extra addition gates for every distinct range list + // } + // // update rangecount to include the ram range checks the composer will eventually be creating + // for (size_t i = 0; i < ram_range_sizes.size(); ++i) { + // if (!ram_range_exists[i]) { + // rangecount += ram_range_sizes[i]; + // } + // } + // } + + // /** + // * @brief Get the final number of gates in a circuit, which consists of the sum of: + // * 1) Current number number of actual gates + // * 2) Number of public inputs, as we'll need to add a gate for each of them + // * 3) Number of Rom array-associated gates + // * 4) NUmber of range-list associated gates + // * + // * @return size_t + // */ + // virtual size_t get_num_gates() const override + // { + // // if circuit finalised already added extra gates + // if (circuit_finalised) { + // return num_gates; + // } + // size_t count = 0; + // size_t rangecount = 0; + // size_t romcount = 0; + // size_t ramcount = 0; + // get_num_gates_split_into_components(count, rangecount, romcount, ramcount); + // return count + romcount + ramcount + rangecount; + // } + + // virtual void print_num_gates() const override + // { + // size_t count = 0; + // size_t rangecount = 0; + // size_t romcount = 0; + // size_t ramcount = 0; + + // get_num_gates_split_into_components(count, rangecount, romcount, ramcount); + + // size_t total = count + romcount + ramcount + rangecount; + // std::cout << "gates = " << total << " (arith " << count << ", rom " << romcount << ", ram " << ramcount + // << ", range " << rangecount << "), pubinp = " << public_inputs.size() << std::endl; + // } + + void assert_equal(const uint32_t a_variable_idx, + const uint32_t b_variable_idx, + std::string const& msg = "assert_equal") + { + circuit_constructor.assert_equal(a_variable_idx, b_variable_idx, msg); + } + + // void assert_equal_constant(const uint32_t a_idx, + // const barretenberg::fr& b, + // std::string const& msg = "assert equal constant") + // { + // if (variables[a_idx] != b && !failed()) { + // failure(msg); + // } + // auto b_idx = put_constant_variable(b); + // assert_equal(a_idx, b_idx, msg); + // } + + // /** + // * Plookup Methods + // **/ + // void add_table_column_selector_poly_to_proving_key(polynomial& small, const std::string& tag); + // void initialize_precomputed_table( + // const plookup::BasicTableId id, + // bool (*generator)(std::vector&, + // std::vector&, + // std::vector&), + // std::array (*get_values_from_key)(const std::array)); + + // plookup::BasicTable& get_table(const plookup::BasicTableId id); + // plookup::MultiTable& create_table(const plookup::MultiTableId id); + + plookup::ReadData create_gates_from_plookup_accumulators( + const plookup::MultiTableId& id, + const plookup::ReadData& read_values, + const uint32_t key_a_index, + std::optional key_b_index = std::nullopt) + { + return circuit_constructor.create_gates_from_plookup_accumulators(id, read_values, key_a_index, key_b_index); + }; + + // /** + // * Generalized Permutation Methods + // **/ + std::vector decompose_into_default_range( + const uint32_t variable_index, + const uint64_t num_bits, + const uint64_t target_range_bitnum = DEFAULT_PLOOKUP_RANGE_BITNUM, + std::string const& msg = "decompose_into_default_range") + { + return circuit_constructor.decompose_into_default_range(variable_index, num_bits, target_range_bitnum, msg); + }; + // std::vector decompose_into_default_range_better_for_oddlimbnum( + // const uint32_t variable_index, + // const size_t num_bits, + // std::string const& msg = "decompose_into_default_range_better_for_oddlimbnum"); + void create_dummy_constraints(const std::vector& variable_index) + { + circuit_constructor.create_dummy_constraints(variable_index); + }; + void create_sort_constraint(const std::vector& variable_index) + { + circuit_constructor.create_sort_constraint(variable_index); + }; + void create_sort_constraint_with_edges(const std::vector& variable_index, + const barretenberg::fr& start, + const barretenberg::fr& end) + { + circuit_constructor.create_sort_constraint_with_edges(variable_index, start, end); + }; + + void assign_tag(const uint32_t variable_index, const uint32_t tag) + { + circuit_constructor.assign_tag(variable_index, tag); + } + + // void assign_tag(const uint32_t variable_index, const uint32_t tag) + // { + // ASSERT(tag <= current_tag); + // ASSERT(real_variable_tags[real_variable_index[variable_index]] == DUMMY_TAG); + // real_variable_tags[real_variable_index[variable_index]] = tag; + // } + + uint32_t create_tag(const uint32_t tag_index, const uint32_t tau_index) + { + return circuit_constructor.create_tag(tag_index, tau_index); + } + + // uint32_t get_new_tag() + // { + // current_tag++; + // return current_tag; + // } + + // RangeList create_range_list(const uint64_t target_range); + // void process_range_list(const RangeList& list); + // void process_range_lists(); + + // /** + // * Custom Gate Selectors + // **/ + // void apply_aux_selectors(const AUX_SELECTORS type); + + // /** + // * Non Native Field Arithmetic + // **/ + void range_constrain_two_limbs(const uint32_t lo_idx, + const uint32_t hi_idx, + const size_t lo_limb_bits = DEFAULT_NON_NATIVE_FIELD_LIMB_BITS, + const size_t hi_limb_bits = DEFAULT_NON_NATIVE_FIELD_LIMB_BITS) + { + circuit_constructor.range_constrain_two_limbs(lo_idx, hi_idx, lo_limb_bits, hi_limb_bits); + }; + // std::array decompose_non_native_field_double_width_limb( + // const uint32_t limb_idx, const size_t num_limb_bits = (2 * DEFAULT_NON_NATIVE_FIELD_LIMB_BITS)); + std::array evaluate_non_native_field_multiplication( + const non_native_field_witnesses& input, const bool range_constrain_quotient_and_remainder = true) + { + return circuit_constructor.evaluate_non_native_field_multiplication(input, + range_constrain_quotient_and_remainder); + }; + // std::array evaluate_partial_non_native_field_multiplication(const non_native_field_witnesses& + // input); typedef std::pair scaled_witness; typedef std::tuple add_simple; std::array evaluate_non_native_field_subtraction( + // add_simple limb0, + // add_simple limb1, + // add_simple limb2, + // add_simple limb3, + // std::tuple limbp); + // std::array evaluate_non_native_field_addition(add_simple limb0, + // add_simple limb1, + // add_simple limb2, + // add_simple limb3, + // std::tuple + // limbp); + + // /** + // * Memory + // **/ + + size_t create_RAM_array(const size_t array_size) { return circuit_constructor.create_RAM_array(array_size); }; + size_t create_ROM_array(const size_t array_size) { return circuit_constructor.create_ROM_array(array_size); }; + + void set_ROM_element(const size_t rom_id, const size_t index_value, const uint32_t value_witness) + { + circuit_constructor.set_ROM_element(rom_id, index_value, value_witness); + }; + // void set_ROM_element_pair(const size_t rom_id, + // const size_t index_value, + // const std::array& value_witnesses); + uint32_t read_ROM_array(const size_t rom_id, const uint32_t index_witness) + { + return circuit_constructor.read_ROM_array(rom_id, index_witness); + }; + // std::array read_ROM_array_pair(const size_t rom_id, const uint32_t index_witness); + // void create_ROM_gate(RomRecord& record); + // void create_sorted_ROM_gate(RomRecord& record); + // void process_ROM_array(const size_t rom_id, const size_t gate_offset_from_public_inputs); + // void process_ROM_arrays(const size_t gate_offset_from_public_inputs); + + // void create_RAM_gate(RamRecord& record); + // void create_sorted_RAM_gate(RamRecord& record); + // void create_final_sorted_RAM_gate(RamRecord& record, const size_t ram_array_size); + + // size_t create_RAM_array(const size_t array_size); + void init_RAM_element(const size_t ram_id, const size_t index_value, const uint32_t value_witness) + { + circuit_constructor.init_RAM_element(ram_id, index_value, value_witness); + }; + uint32_t read_RAM_array(const size_t ram_id, const uint32_t index_witness) + { + return circuit_constructor.read_RAM_array(ram_id, index_witness); + }; + void write_RAM_array(const size_t ram_id, const uint32_t index_witness, const uint32_t value_witness) + { + circuit_constructor.write_RAM_array(ram_id, index_witness, value_witness); + }; + // void process_RAM_array(const size_t ram_id, const size_t gate_offset_from_public_inputs); + // void process_RAM_arrays(const size_t gate_offset_from_public_inputs); + + // /** + // * Member Variables + // **/ + + // uint32_t zero_idx = 0; + bool circuit_finalised = false; + + // // This variable controls the amount with which the lookup table and witness values need to be shifted + // // above to make room for adding randomness into the permutation and witness polynomials in the plookup widget. + // // This must be (num_roots_cut_out_of_the_vanishing_polynomial - 1), since the variable num_roots_cut_out_of_ + // // vanishing_polynomial cannot be trivially fetched here, I am directly setting this to 4 - 1 = 3. + // static constexpr size_t s_randomness = 3; + + // // these are variables that we have used a gate on, to enforce that they are equal to a defined value + // std::map constant_variable_indices; + + // std::vector lookup_tables; + // std::vector lookup_multi_tables; + // std::map range_lists; // DOCTODO: explain this. + + // /** + // * @brief Each entry in ram_arrays represents an independent RAM table. + // * RamTranscript tracks the current table state, + // * as well as the 'records' produced by each read and write operation. + // * Used in `compute_proving_key` to generate consistency check gates required to validate the RAM read/write + // history + // */ + // std::vector ram_arrays; + + // /** + // * @brief Each entry in ram_arrays represents an independent ROM table. + // * RomTranscript tracks the current table state, + // * as well as the 'records' produced by each read operation. + // * Used in `compute_proving_key` to generate consistency check gates required to validate the ROM read history + // */ + // std::vector rom_arrays; + + // // Stores gate index of ROM and RAM reads (required by proving key) + // std::vector memory_read_records; + // // Stores gate index of RAM writes (required by proving key) + // std::vector memory_write_records; + + // std::vector recursive_proof_public_input_indices; + // bool contains_recursive_proof = false; +}; +} // namespace proof_system::honk diff --git a/cpp/src/barretenberg/honk/composer/ultra_honk_composer.test.cpp b/cpp/src/barretenberg/honk/composer/ultra_honk_composer.test.cpp new file mode 100644 index 0000000000..0c8f1baefc --- /dev/null +++ b/cpp/src/barretenberg/honk/composer/ultra_honk_composer.test.cpp @@ -0,0 +1,1036 @@ +#include "ultra_honk_composer.hpp" +#include "barretenberg/common/log.hpp" +#include "barretenberg/honk/proof_system/ultra_prover.hpp" +#include "barretenberg/honk/sumcheck/relations/relation.hpp" +#include "barretenberg/numeric/uint256/uint256.hpp" +#include "barretenberg/honk/flavor/flavor.hpp" +#include +#include +#include "barretenberg/honk/proof_system/prover.hpp" +#include "barretenberg/honk/sumcheck/sumcheck_round.hpp" +#include "barretenberg/honk/sumcheck/relations/grand_product_computation_relation.hpp" +#include "barretenberg/honk/sumcheck/relations/grand_product_initialization_relation.hpp" +#include "barretenberg/honk/utils/public_inputs.hpp" + +// TODO(luke): TEMPORARY; for testing only (comparison with Ultra Plonk composers) +#include "barretenberg/plonk/composer/ultra_composer.hpp" +#include "barretenberg/plonk/composer/splitting_tmp/ultra_plonk_composer.hpp" +#include "barretenberg/plonk/proof_system/prover/prover.hpp" + +#include +#include + +using namespace proof_system::honk; + +namespace test_ultra_honk_composer { + +std::vector add_variables(auto& composer, std::vector variables) +{ + std::vector res; + for (size_t i = 0; i < variables.size(); i++) { + res.emplace_back(composer.add_variable(variables[i])); + } + return res; +} + +/** + * @brief TEMPORARY method for checking consistency of polynomials computed by Ultra Plonk/Honk composers + * + * @param honk_prover + * @param plonk_prover + */ +// NOTE: Currently checking exact consistency for witness polynomials (wires, sorted lists) and table polys. +// The permutation polys are computed differently between plonk and honk so we do not expect consistency. +// Equality is checked on all selectors but we ignore the final entry since we do not enforce non-zero selectors in +// Honk. +void verify_consistency(honk::UltraProver& honk_prover, plonk::UltraProver& plonk_prover) +{ + auto& honk_store = honk_prover.key->polynomial_store; + auto& plonk_store = plonk_prover.key->polynomial_store; + + // Check that all selectors agree (aside from the final element which will differ due to not enforcing non-zero + // selectors in Honk). + for (auto& entry : honk_store) { + std::string key = entry.first; + bool is_selector = (key.find("q_") != std::string::npos) || (key.find("table_type") != std::string::npos); + if (plonk_store.contains(key) && is_selector) { + // check equality for all but final entry + for (size_t i = 0; i < honk_store.get(key).size() - 1; ++i) { + ASSERT_EQ(honk_store.get(key)[i], plonk_store.get(key)[i]); + } + } + } + + // Check that sorted witness-table and table polys agree + for (auto& entry : honk_store) { + std::string key = entry.first; + bool is_sorted_table = (key.find("s_") != std::string::npos); + bool is_table = (key.find("table_value_") != std::string::npos); + if (plonk_store.contains(key) && (is_sorted_table || is_table)) { + ASSERT_EQ(honk_store.get(key), plonk_store.get(key)); + } + } + + // Check that all wires agree + // Note: for Honk, wires are owned directly by the prover. For Plonk they are stored in the key. + for (size_t i = 0; i < 4; ++i) { + std::string label = "w_" + std::to_string(i + 1) + "_lagrange"; + ASSERT_EQ(honk_prover.wire_polynomials[i], plonk_prover.key->polynomial_store.get(label)); + } +} + +/** + * @brief TEMPORARY (verbose) method for checking consistency of polynomials computed by Ultra Plonk/Honk composers + * + * @param honk_prover + * @param plonk_prover + */ +void check_consistency(honk::UltraProver& honk_prover, plonk::UltraProver& plonk_prover) +{ + auto& honk_store = honk_prover.key->polynomial_store; + auto& plonk_store = plonk_prover.key->polynomial_store; + for (auto& entry : honk_store) { + std::string key = entry.first; + if (plonk_store.contains(key)) { + + bool polys_equal = (honk_store.get(key) == plonk_store.get(key)); + if (polys_equal) { + info("Equal: ", key); + } + if (!polys_equal) { + info("UNEQUAL: ", key); + } + } + } + + for (size_t i = 0; i < 4; ++i) { + std::string label = "w_" + std::to_string(i + 1) + "_lagrange"; + bool wire_equal = (honk_prover.wire_polynomials[i] == plonk_prover.key->polynomial_store.get(label)); + if (wire_equal) { + info("Wire Equal: ", i); + } + if (!wire_equal) { + info("Wire UNEQUAL: ", i); + } + } + + // std::string label = "w_1_lagrange"; + // for (size_t i = 0; i < plonk_store.get(label).size(); ++i) { + // auto val_honk = honk_prover.wire_polynomials[0][i]; + // // auto val_honk = honk_store.get(label)[i]; + // auto val_plonk = plonk_store.get(label)[i]; + // if (val_honk != val_plonk) { + // info("UNEQUAL index = ", i); + // info("honk: ",val_honk); + // info("plonk: ", val_plonk); + // } + // } +} + +TEST(UltraHonkComposer, create_gates_from_plookup_accumulators) +{ + auto honk_composer = UltraHonkComposer(); + auto plonk_composer = proof_system::plonk::UltraComposer(); + + barretenberg::fr input_value = fr::random_element(); + { + + const fr input_hi = uint256_t(input_value).slice(126, 256); + const fr input_lo = uint256_t(input_value).slice(0, 126); + const auto input_hi_index = honk_composer.add_variable(input_hi); + const auto input_lo_index = honk_composer.add_variable(input_lo); + + const auto sequence_data_hi = + plookup::get_lookup_accumulators(plookup::MultiTableId::PEDERSEN_LEFT_HI, input_hi); + const auto sequence_data_lo = + plookup::get_lookup_accumulators(plookup::MultiTableId::PEDERSEN_LEFT_LO, input_lo); + + const auto lookup_witnesses_hi = honk_composer.create_gates_from_plookup_accumulators( + plookup::MultiTableId::PEDERSEN_LEFT_HI, sequence_data_hi, input_hi_index); + const auto lookup_witnesses_lo = honk_composer.create_gates_from_plookup_accumulators( + plookup::MultiTableId::PEDERSEN_LEFT_LO, sequence_data_lo, input_lo_index); + } + { + const fr input_hi = uint256_t(input_value).slice(126, 256); + const fr input_lo = uint256_t(input_value).slice(0, 126); + const auto input_hi_index = plonk_composer.add_variable(input_hi); + const auto input_lo_index = plonk_composer.add_variable(input_lo); + + const auto sequence_data_hi = + plookup::get_lookup_accumulators(plookup::MultiTableId::PEDERSEN_LEFT_HI, input_hi); + const auto sequence_data_lo = + plookup::get_lookup_accumulators(plookup::MultiTableId::PEDERSEN_LEFT_LO, input_lo); + + const auto lookup_witnesses_hi = plonk_composer.create_gates_from_plookup_accumulators( + plookup::MultiTableId::PEDERSEN_LEFT_HI, sequence_data_hi, input_hi_index); + const auto lookup_witnesses_lo = plonk_composer.create_gates_from_plookup_accumulators( + plookup::MultiTableId::PEDERSEN_LEFT_LO, sequence_data_lo, input_lo_index); + } + + auto honk_prover = honk_composer.create_prover(); + auto plonk_prover = plonk_composer.create_prover(); + + verify_consistency(honk_prover, plonk_prover); +} + +/** + * @brief Build UltraHonkComposer + * + */ +TEST(UltraHonkComposer, test_no_lookup_proof) +{ + auto honk_composer = UltraHonkComposer(); + auto plonk_composer = proof_system::plonk::UltraComposer(); + + size_t MM = 4; + for (size_t i = 0; i < MM; ++i) { + for (size_t j = 0; j < MM; ++j) { + uint64_t left = static_cast(j); + uint64_t right = static_cast(i); + uint32_t left_idx = honk_composer.add_variable(fr(left)); + uint32_t right_idx = honk_composer.add_variable(fr(right)); + uint32_t result_idx = honk_composer.add_variable(fr(left ^ right)); + + uint32_t add_idx = + honk_composer.add_variable(fr(left) + fr(right) + honk_composer.get_variable(result_idx)); + honk_composer.create_big_add_gate( + { left_idx, right_idx, result_idx, add_idx, fr(1), fr(1), fr(1), fr(-1), fr(0) }); + } + } + + for (size_t i = 0; i < MM; ++i) { + for (size_t j = 0; j < MM; ++j) { + uint64_t left = static_cast(j); + uint64_t right = static_cast(i); + uint32_t left_idx = plonk_composer.add_variable(fr(left)); + uint32_t right_idx = plonk_composer.add_variable(fr(right)); + uint32_t result_idx = plonk_composer.add_variable(fr(left ^ right)); + + uint32_t add_idx = + plonk_composer.add_variable(fr(left) + fr(right) + plonk_composer.get_variable(result_idx)); + plonk_composer.create_big_add_gate( + { left_idx, right_idx, result_idx, add_idx, fr(1), fr(1), fr(1), fr(-1), fr(0) }); + } + } + + auto honk_prover = honk_composer.create_prover(); + auto plonk_prover = plonk_composer.create_prover(); + + verify_consistency(honk_prover, plonk_prover); +} + +TEST(UltraHonkComposer, test_elliptic_gate) +{ + typedef grumpkin::g1::affine_element affine_element; + typedef grumpkin::g1::element element; + + auto honk_composer = UltraHonkComposer(); + auto plonk_composer = proof_system::plonk::UltraComposer(); + + { + affine_element p1 = crypto::generators::get_generator_data({ 0, 0 }).generator; + affine_element p2 = crypto::generators::get_generator_data({ 0, 1 }).generator; + affine_element p3(element(p1) + element(p2)); + + uint32_t x1 = honk_composer.add_variable(p1.x); + uint32_t y1 = honk_composer.add_variable(p1.y); + uint32_t x2 = honk_composer.add_variable(p2.x); + uint32_t y2 = honk_composer.add_variable(p2.y); + uint32_t x3 = honk_composer.add_variable(p3.x); + uint32_t y3 = honk_composer.add_variable(p3.y); + + ecc_add_gate gate{ x1, y1, x2, y2, x3, y3, 1, 1 }; + honk_composer.create_ecc_add_gate(gate); + + grumpkin::fq beta = grumpkin::fq::cube_root_of_unity(); + affine_element p2_endo = p2; + p2_endo.x *= beta; + p3 = affine_element(element(p1) + element(p2_endo)); + x3 = honk_composer.add_variable(p3.x); + y3 = honk_composer.add_variable(p3.y); + gate = ecc_add_gate{ x1, y1, x2, y2, x3, y3, beta, 1 }; + honk_composer.create_ecc_add_gate(gate); + + p2_endo.x *= beta; + p3 = affine_element(element(p1) - element(p2_endo)); + x3 = honk_composer.add_variable(p3.x); + y3 = honk_composer.add_variable(p3.y); + gate = ecc_add_gate{ x1, y1, x2, y2, x3, y3, beta.sqr(), -1 }; + honk_composer.create_ecc_add_gate(gate); + } + { + affine_element p1 = crypto::generators::get_generator_data({ 0, 0 }).generator; + affine_element p2 = crypto::generators::get_generator_data({ 0, 1 }).generator; + affine_element p3(element(p1) + element(p2)); + + uint32_t x1 = plonk_composer.add_variable(p1.x); + uint32_t y1 = plonk_composer.add_variable(p1.y); + uint32_t x2 = plonk_composer.add_variable(p2.x); + uint32_t y2 = plonk_composer.add_variable(p2.y); + uint32_t x3 = plonk_composer.add_variable(p3.x); + uint32_t y3 = plonk_composer.add_variable(p3.y); + + ecc_add_gate gate{ x1, y1, x2, y2, x3, y3, 1, 1 }; + plonk_composer.create_ecc_add_gate(gate); + + grumpkin::fq beta = grumpkin::fq::cube_root_of_unity(); + affine_element p2_endo = p2; + p2_endo.x *= beta; + p3 = affine_element(element(p1) + element(p2_endo)); + x3 = plonk_composer.add_variable(p3.x); + y3 = plonk_composer.add_variable(p3.y); + gate = ecc_add_gate{ x1, y1, x2, y2, x3, y3, beta, 1 }; + plonk_composer.create_ecc_add_gate(gate); + + p2_endo.x *= beta; + p3 = affine_element(element(p1) - element(p2_endo)); + x3 = plonk_composer.add_variable(p3.x); + y3 = plonk_composer.add_variable(p3.y); + gate = ecc_add_gate{ x1, y1, x2, y2, x3, y3, beta.sqr(), -1 }; + plonk_composer.create_ecc_add_gate(gate); + } + + auto honk_prover = honk_composer.create_prover(); + auto plonk_prover = plonk_composer.create_prover(); + + verify_consistency(honk_prover, plonk_prover); +} + +TEST(UltraHonkComposer, non_trivial_tag_permutation) +{ + auto honk_composer = UltraHonkComposer(); + auto plonk_composer = proof_system::plonk::UltraComposer(); + + fr a = fr::random_element(); + { + fr b = -a; + + auto a_idx = honk_composer.add_variable(a); + auto b_idx = honk_composer.add_variable(b); + auto c_idx = honk_composer.add_variable(b); + auto d_idx = honk_composer.add_variable(a); + + honk_composer.create_add_gate( + { a_idx, b_idx, honk_composer.get_zero_idx(), fr::one(), fr::one(), fr::zero(), fr::zero() }); + honk_composer.create_add_gate( + { c_idx, d_idx, honk_composer.get_zero_idx(), fr::one(), fr::one(), fr::zero(), fr::zero() }); + + honk_composer.create_tag(1, 2); + honk_composer.create_tag(2, 1); + + honk_composer.assign_tag(a_idx, 1); + honk_composer.assign_tag(b_idx, 1); + honk_composer.assign_tag(c_idx, 2); + honk_composer.assign_tag(d_idx, 2); + } + { + fr b = -a; + + auto a_idx = plonk_composer.add_variable(a); + auto b_idx = plonk_composer.add_variable(b); + auto c_idx = plonk_composer.add_variable(b); + auto d_idx = plonk_composer.add_variable(a); + + plonk_composer.create_add_gate( + { a_idx, b_idx, plonk_composer.zero_idx, fr::one(), fr::one(), fr::zero(), fr::zero() }); + plonk_composer.create_add_gate( + { c_idx, d_idx, plonk_composer.zero_idx, fr::one(), fr::one(), fr::zero(), fr::zero() }); + + plonk_composer.create_tag(1, 2); + plonk_composer.create_tag(2, 1); + + plonk_composer.assign_tag(a_idx, 1); + plonk_composer.assign_tag(b_idx, 1); + plonk_composer.assign_tag(c_idx, 2); + plonk_composer.assign_tag(d_idx, 2); + } + + auto honk_prover = honk_composer.create_prover(); + auto plonk_prover = plonk_composer.create_prover(); + + verify_consistency(honk_prover, plonk_prover); +} + +TEST(UltraHonkComposer, non_trivial_tag_permutation_and_cycles) +{ + auto honk_composer = UltraHonkComposer(); + auto plonk_composer = proof_system::plonk::UltraComposer(); + + fr a = fr::random_element(); + { + fr c = -a; + + auto a_idx = honk_composer.add_variable(a); + auto b_idx = honk_composer.add_variable(a); + honk_composer.assert_equal(a_idx, b_idx); + auto c_idx = honk_composer.add_variable(c); + auto d_idx = honk_composer.add_variable(c); + honk_composer.assert_equal(c_idx, d_idx); + auto e_idx = honk_composer.add_variable(a); + auto f_idx = honk_composer.add_variable(a); + honk_composer.assert_equal(e_idx, f_idx); + auto g_idx = honk_composer.add_variable(c); + auto h_idx = honk_composer.add_variable(c); + honk_composer.assert_equal(g_idx, h_idx); + + honk_composer.create_tag(1, 2); + honk_composer.create_tag(2, 1); + + honk_composer.assign_tag(a_idx, 1); + honk_composer.assign_tag(c_idx, 1); + honk_composer.assign_tag(e_idx, 2); + honk_composer.assign_tag(g_idx, 2); + + honk_composer.create_add_gate( + { b_idx, a_idx, honk_composer.get_zero_idx(), fr::one(), fr::neg_one(), fr::zero(), fr::zero() }); + honk_composer.create_add_gate( + { c_idx, g_idx, honk_composer.get_zero_idx(), fr::one(), -fr::one(), fr::zero(), fr::zero() }); + honk_composer.create_add_gate( + { e_idx, f_idx, honk_composer.get_zero_idx(), fr::one(), -fr::one(), fr::zero(), fr::zero() }); + } + { + fr c = -a; + + auto a_idx = plonk_composer.add_variable(a); + auto b_idx = plonk_composer.add_variable(a); + plonk_composer.assert_equal(a_idx, b_idx); + auto c_idx = plonk_composer.add_variable(c); + auto d_idx = plonk_composer.add_variable(c); + plonk_composer.assert_equal(c_idx, d_idx); + auto e_idx = plonk_composer.add_variable(a); + auto f_idx = plonk_composer.add_variable(a); + plonk_composer.assert_equal(e_idx, f_idx); + auto g_idx = plonk_composer.add_variable(c); + auto h_idx = plonk_composer.add_variable(c); + plonk_composer.assert_equal(g_idx, h_idx); + + plonk_composer.create_tag(1, 2); + plonk_composer.create_tag(2, 1); + + plonk_composer.assign_tag(a_idx, 1); + plonk_composer.assign_tag(c_idx, 1); + plonk_composer.assign_tag(e_idx, 2); + plonk_composer.assign_tag(g_idx, 2); + + plonk_composer.create_add_gate( + { b_idx, a_idx, plonk_composer.zero_idx, fr::one(), fr::neg_one(), fr::zero(), fr::zero() }); + plonk_composer.create_add_gate( + { c_idx, g_idx, plonk_composer.zero_idx, fr::one(), -fr::one(), fr::zero(), fr::zero() }); + plonk_composer.create_add_gate( + { e_idx, f_idx, plonk_composer.zero_idx, fr::one(), -fr::one(), fr::zero(), fr::zero() }); + } + + auto honk_prover = honk_composer.create_prover(); + auto plonk_prover = plonk_composer.create_prover(); + + verify_consistency(honk_prover, plonk_prover); +} + +TEST(UltraHonkComposer, bad_tag_permutation) +{ + auto honk_composer = UltraHonkComposer(); + auto plonk_composer = proof_system::plonk::UltraComposer(); + + fr a = fr::random_element(); + { + fr b = -a; + + auto a_idx = honk_composer.add_variable(a); + auto b_idx = honk_composer.add_variable(b); + auto c_idx = honk_composer.add_variable(b); + auto d_idx = honk_composer.add_variable(a + 1); + + honk_composer.create_add_gate({ a_idx, b_idx, honk_composer.get_zero_idx(), 1, 1, 0, 0 }); + honk_composer.create_add_gate({ c_idx, d_idx, honk_composer.get_zero_idx(), 1, 1, 0, -1 }); + + honk_composer.create_tag(1, 2); + honk_composer.create_tag(2, 1); + + honk_composer.assign_tag(a_idx, 1); + honk_composer.assign_tag(b_idx, 1); + honk_composer.assign_tag(c_idx, 2); + honk_composer.assign_tag(d_idx, 2); + } + { + fr b = -a; + + auto a_idx = plonk_composer.add_variable(a); + auto b_idx = plonk_composer.add_variable(b); + auto c_idx = plonk_composer.add_variable(b); + auto d_idx = plonk_composer.add_variable(a + 1); + + plonk_composer.create_add_gate({ a_idx, b_idx, plonk_composer.zero_idx, 1, 1, 0, 0 }); + plonk_composer.create_add_gate({ c_idx, d_idx, plonk_composer.zero_idx, 1, 1, 0, -1 }); + + plonk_composer.create_tag(1, 2); + plonk_composer.create_tag(2, 1); + + plonk_composer.assign_tag(a_idx, 1); + plonk_composer.assign_tag(b_idx, 1); + plonk_composer.assign_tag(c_idx, 2); + plonk_composer.assign_tag(d_idx, 2); + } + + auto honk_prover = honk_composer.create_prover(); + auto plonk_prover = plonk_composer.create_prover(); + + verify_consistency(honk_prover, plonk_prover); +} + +TEST(UltraHonkComposer, sort_widget) +{ + auto honk_composer = UltraHonkComposer(); + auto plonk_composer = proof_system::plonk::UltraComposer(); + + { + fr a = fr::one(); + fr b = fr(2); + fr c = fr(3); + fr d = fr(4); + + auto a_idx = honk_composer.add_variable(a); + auto b_idx = honk_composer.add_variable(b); + auto c_idx = honk_composer.add_variable(c); + auto d_idx = honk_composer.add_variable(d); + honk_composer.create_sort_constraint({ a_idx, b_idx, c_idx, d_idx }); + } + { + fr a = fr::one(); + fr b = fr(2); + fr c = fr(3); + fr d = fr(4); + + auto a_idx = plonk_composer.add_variable(a); + auto b_idx = plonk_composer.add_variable(b); + auto c_idx = plonk_composer.add_variable(c); + auto d_idx = plonk_composer.add_variable(d); + plonk_composer.create_sort_constraint({ a_idx, b_idx, c_idx, d_idx }); + } + + auto honk_prover = honk_composer.create_prover(); + auto plonk_prover = plonk_composer.create_prover(); + + verify_consistency(honk_prover, plonk_prover); +} + +TEST(UltraHonkComposer, sort_with_edges_gate) +{ + auto honk_composer = UltraHonkComposer(); + auto plonk_composer = proof_system::plonk::UltraComposer(); + + { + auto idx = add_variables(honk_composer, { 1, 2, 5, 6, 7, 10, 11, 13, 16, 17, 20, 22, 22, 25, + 26, 29, 29, 32, 32, 33, 35, 38, 39, 39, 42, 42, 43, 45 }); + + honk_composer.create_sort_constraint_with_edges(idx, 1, 29); + } + { + auto idx = add_variables(plonk_composer, { 1, 2, 5, 6, 7, 10, 11, 13, 16, 17, 20, 22, 22, 25, + 26, 29, 29, 32, 32, 33, 35, 38, 39, 39, 42, 42, 43, 45 }); + + plonk_composer.create_sort_constraint_with_edges(idx, 1, 29); + } + + auto honk_prover = honk_composer.create_prover(); + auto plonk_prover = plonk_composer.create_prover(); + + verify_consistency(honk_prover, plonk_prover); +} + +TEST(UltraHonkComposer, range_constraint) +{ + auto honk_composer = UltraHonkComposer(); + auto plonk_composer = proof_system::plonk::UltraComposer(); + + { + auto indices = + add_variables(honk_composer, { 1, 0, 3, 80, 5, 6, 29, 8, 15, 11, 32, 21, 42, 79, 16, 10, 3, 26, 13, 14 }); + for (size_t i = 0; i < indices.size(); i++) { + honk_composer.create_new_range_constraint(indices[i], 79); + } + honk_composer.create_dummy_constraints(indices); + } + { + auto indices = + add_variables(plonk_composer, { 1, 0, 3, 80, 5, 6, 29, 8, 15, 11, 32, 21, 42, 79, 16, 10, 3, 26, 13, 14 }); + for (size_t i = 0; i < indices.size(); i++) { + plonk_composer.create_new_range_constraint(indices[i], 79); + } + plonk_composer.create_dummy_constraints(indices); + } + + auto honk_prover = honk_composer.create_prover(); + auto plonk_prover = plonk_composer.create_prover(); + + verify_consistency(honk_prover, plonk_prover); +} + +TEST(UltraHonkComposer, range_with_gates) +{ + + auto honk_composer = UltraHonkComposer(); + auto plonk_composer = proof_system::plonk::UltraComposer(); + + { + auto idx = add_variables(honk_composer, { 1, 2, 3, 4, 5, 6, 7, 8 }); + for (size_t i = 0; i < idx.size(); i++) { + honk_composer.create_new_range_constraint(idx[i], 8); + } + + honk_composer.create_add_gate( + { idx[0], idx[1], honk_composer.get_zero_idx(), fr::one(), fr::one(), fr::zero(), -3 }); + honk_composer.create_add_gate( + { idx[2], idx[3], honk_composer.get_zero_idx(), fr::one(), fr::one(), fr::zero(), -7 }); + honk_composer.create_add_gate( + { idx[4], idx[5], honk_composer.get_zero_idx(), fr::one(), fr::one(), fr::zero(), -11 }); + honk_composer.create_add_gate( + { idx[6], idx[7], honk_composer.get_zero_idx(), fr::one(), fr::one(), fr::zero(), -15 }); + } + { + auto idx = add_variables(plonk_composer, { 1, 2, 3, 4, 5, 6, 7, 8 }); + for (size_t i = 0; i < idx.size(); i++) { + plonk_composer.create_new_range_constraint(idx[i], 8); + } + + plonk_composer.create_add_gate( + { idx[0], idx[1], plonk_composer.zero_idx, fr::one(), fr::one(), fr::zero(), -3 }); + plonk_composer.create_add_gate( + { idx[2], idx[3], plonk_composer.zero_idx, fr::one(), fr::one(), fr::zero(), -7 }); + plonk_composer.create_add_gate( + { idx[4], idx[5], plonk_composer.zero_idx, fr::one(), fr::one(), fr::zero(), -11 }); + plonk_composer.create_add_gate( + { idx[6], idx[7], plonk_composer.zero_idx, fr::one(), fr::one(), fr::zero(), -15 }); + } + + auto honk_prover = honk_composer.create_prover(); + auto plonk_prover = plonk_composer.create_prover(); + + verify_consistency(honk_prover, plonk_prover); +} + +TEST(UltraHonkComposer, range_with_gates_where_range_is_not_a_power_of_two) +{ + auto honk_composer = UltraHonkComposer(); + auto plonk_composer = proof_system::plonk::UltraComposer(); + + { + auto idx = add_variables(honk_composer, { 1, 2, 3, 4, 5, 6, 7, 8 }); + for (size_t i = 0; i < idx.size(); i++) { + honk_composer.create_new_range_constraint(idx[i], 12); + } + + honk_composer.create_add_gate( + { idx[0], idx[1], honk_composer.get_zero_idx(), fr::one(), fr::one(), fr::zero(), -3 }); + honk_composer.create_add_gate( + { idx[2], idx[3], honk_composer.get_zero_idx(), fr::one(), fr::one(), fr::zero(), -7 }); + honk_composer.create_add_gate( + { idx[4], idx[5], honk_composer.get_zero_idx(), fr::one(), fr::one(), fr::zero(), -11 }); + honk_composer.create_add_gate( + { idx[6], idx[7], honk_composer.get_zero_idx(), fr::one(), fr::one(), fr::zero(), -15 }); + } + { + auto idx = add_variables(plonk_composer, { 1, 2, 3, 4, 5, 6, 7, 8 }); + for (size_t i = 0; i < idx.size(); i++) { + plonk_composer.create_new_range_constraint(idx[i], 12); + } + + plonk_composer.create_add_gate( + { idx[0], idx[1], plonk_composer.zero_idx, fr::one(), fr::one(), fr::zero(), -3 }); + plonk_composer.create_add_gate( + { idx[2], idx[3], plonk_composer.zero_idx, fr::one(), fr::one(), fr::zero(), -7 }); + plonk_composer.create_add_gate( + { idx[4], idx[5], plonk_composer.zero_idx, fr::one(), fr::one(), fr::zero(), -11 }); + plonk_composer.create_add_gate( + { idx[6], idx[7], plonk_composer.zero_idx, fr::one(), fr::one(), fr::zero(), -15 }); + } + + auto honk_prover = honk_composer.create_prover(); + auto plonk_prover = plonk_composer.create_prover(); + + verify_consistency(honk_prover, plonk_prover); +} + +TEST(UltraHonkComposer, sort_widget_complex) +{ + auto honk_composer = UltraHonkComposer(); + auto plonk_composer = proof_system::plonk::UltraComposer(); + + { + std::vector a = { 1, 3, 4, 7, 7, 8, 16, 14, 15, 15, 18, 19, 21, 21, 24, 25, 26, 27, 30, 32 }; + std::vector ind; + for (size_t i = 0; i < a.size(); i++) + ind.emplace_back(honk_composer.add_variable(a[i])); + honk_composer.create_sort_constraint(ind); + } + { + std::vector a = { 1, 3, 4, 7, 7, 8, 16, 14, 15, 15, 18, 19, 21, 21, 24, 25, 26, 27, 30, 32 }; + std::vector ind; + for (size_t i = 0; i < a.size(); i++) + ind.emplace_back(plonk_composer.add_variable(a[i])); + plonk_composer.create_sort_constraint(ind); + } + + auto honk_prover = honk_composer.create_prover(); + auto plonk_prover = plonk_composer.create_prover(); + + verify_consistency(honk_prover, plonk_prover); +} + +TEST(UltraHonkComposer, composed_range_constraint) +{ + auto honk_composer = UltraHonkComposer(); + auto plonk_composer = proof_system::plonk::UltraComposer(); + + auto c = fr::random_element(); + { + auto d = uint256_t(c).slice(0, 133); + auto e = fr(d); + auto a_idx = honk_composer.add_variable(fr(e)); + honk_composer.create_add_gate( + { a_idx, honk_composer.get_zero_idx(), honk_composer.get_zero_idx(), 1, 0, 0, -fr(e) }); + honk_composer.decompose_into_default_range(a_idx, 134); + } + { + auto d = uint256_t(c).slice(0, 133); + auto e = fr(d); + auto a_idx = plonk_composer.add_variable(fr(e)); + plonk_composer.create_add_gate({ a_idx, plonk_composer.zero_idx, plonk_composer.zero_idx, 1, 0, 0, -fr(e) }); + plonk_composer.decompose_into_default_range(a_idx, 134); + } + + auto honk_prover = honk_composer.create_prover(); + auto plonk_prover = plonk_composer.create_prover(); + + verify_consistency(honk_prover, plonk_prover); +} + +TEST(UltraHonkComposer, non_native_field_multiplication) +{ + auto honk_composer = UltraHonkComposer(); + auto plonk_composer = proof_system::plonk::UltraComposer(); + + fq a = fq::random_element(); + fq b = fq::random_element(); + { + uint256_t modulus = fq::modulus; + + uint1024_t a_big = uint512_t(uint256_t(a)); + uint1024_t b_big = uint512_t(uint256_t(b)); + uint1024_t p_big = uint512_t(uint256_t(modulus)); + + uint1024_t q_big = (a_big * b_big) / p_big; + uint1024_t r_big = (a_big * b_big) % p_big; + + uint256_t q(q_big.lo.lo); + uint256_t r(r_big.lo.lo); + + const auto split_into_limbs = [&](const uint512_t& input) { + constexpr size_t NUM_BITS = 68; + std::array limbs; + limbs[0] = input.slice(0, NUM_BITS).lo; + limbs[1] = input.slice(NUM_BITS * 1, NUM_BITS * 2).lo; + limbs[2] = input.slice(NUM_BITS * 2, NUM_BITS * 3).lo; + limbs[3] = input.slice(NUM_BITS * 3, NUM_BITS * 4).lo; + limbs[4] = fr(input.lo); + return limbs; + }; + + const auto get_limb_witness_indices = [&](const std::array& limbs) { + std::array limb_indices; + limb_indices[0] = honk_composer.add_variable(limbs[0]); + limb_indices[1] = honk_composer.add_variable(limbs[1]); + limb_indices[2] = honk_composer.add_variable(limbs[2]); + limb_indices[3] = honk_composer.add_variable(limbs[3]); + limb_indices[4] = honk_composer.add_variable(limbs[4]); + return limb_indices; + }; + const uint512_t BINARY_BASIS_MODULUS = uint512_t(1) << (68 * 4); + auto modulus_limbs = split_into_limbs(BINARY_BASIS_MODULUS - uint512_t(modulus)); + + const auto a_indices = get_limb_witness_indices(split_into_limbs(uint256_t(a))); + const auto b_indices = get_limb_witness_indices(split_into_limbs(uint256_t(b))); + const auto q_indices = get_limb_witness_indices(split_into_limbs(uint256_t(q))); + const auto r_indices = get_limb_witness_indices(split_into_limbs(uint256_t(r))); + + proof_system::non_native_field_witnesses inputs{ + a_indices, b_indices, q_indices, r_indices, modulus_limbs, fr(uint256_t(modulus)), + }; + const auto [lo_1_idx, hi_1_idx] = honk_composer.evaluate_non_native_field_multiplication(inputs); + honk_composer.range_constrain_two_limbs(lo_1_idx, hi_1_idx, 70, 70); + } + { + uint256_t modulus = fq::modulus; + + uint1024_t a_big = uint512_t(uint256_t(a)); + uint1024_t b_big = uint512_t(uint256_t(b)); + uint1024_t p_big = uint512_t(uint256_t(modulus)); + + uint1024_t q_big = (a_big * b_big) / p_big; + uint1024_t r_big = (a_big * b_big) % p_big; + + uint256_t q(q_big.lo.lo); + uint256_t r(r_big.lo.lo); + + const auto split_into_limbs = [&](const uint512_t& input) { + constexpr size_t NUM_BITS = 68; + std::array limbs; + limbs[0] = input.slice(0, NUM_BITS).lo; + limbs[1] = input.slice(NUM_BITS * 1, NUM_BITS * 2).lo; + limbs[2] = input.slice(NUM_BITS * 2, NUM_BITS * 3).lo; + limbs[3] = input.slice(NUM_BITS * 3, NUM_BITS * 4).lo; + limbs[4] = fr(input.lo); + return limbs; + }; + + const auto get_limb_witness_indices = [&](const std::array& limbs) { + std::array limb_indices; + limb_indices[0] = plonk_composer.add_variable(limbs[0]); + limb_indices[1] = plonk_composer.add_variable(limbs[1]); + limb_indices[2] = plonk_composer.add_variable(limbs[2]); + limb_indices[3] = plonk_composer.add_variable(limbs[3]); + limb_indices[4] = plonk_composer.add_variable(limbs[4]); + return limb_indices; + }; + const uint512_t BINARY_BASIS_MODULUS = uint512_t(1) << (68 * 4); + auto modulus_limbs = split_into_limbs(BINARY_BASIS_MODULUS - uint512_t(modulus)); + + const auto a_indices = get_limb_witness_indices(split_into_limbs(uint256_t(a))); + const auto b_indices = get_limb_witness_indices(split_into_limbs(uint256_t(b))); + const auto q_indices = get_limb_witness_indices(split_into_limbs(uint256_t(q))); + const auto r_indices = get_limb_witness_indices(split_into_limbs(uint256_t(r))); + + proof_system::plonk::UltraComposer::non_native_field_witnesses inputs{ + a_indices, b_indices, q_indices, r_indices, modulus_limbs, fr(uint256_t(modulus)), + }; + const auto [lo_1_idx, hi_1_idx] = plonk_composer.evaluate_non_native_field_multiplication(inputs); + plonk_composer.range_constrain_two_limbs(lo_1_idx, hi_1_idx, 70, 70); + } + + auto honk_prover = honk_composer.create_prover(); + auto plonk_prover = plonk_composer.create_prover(); + + verify_consistency(honk_prover, plonk_prover); +} + +TEST(UltraHonkComposer, rom) +{ + auto honk_composer = UltraHonkComposer(); + auto plonk_composer = proof_system::plonk::UltraComposer(); + + auto a = fr::random_element(); + auto b = fr::random_element(); + auto c = fr::random_element(); + auto d = fr::random_element(); + auto e = fr::random_element(); + auto f = fr::random_element(); + auto g = fr::random_element(); + auto h = fr::random_element(); + { + uint32_t rom_values[8]{ + honk_composer.add_variable(a), honk_composer.add_variable(b), honk_composer.add_variable(c), + honk_composer.add_variable(d), honk_composer.add_variable(e), honk_composer.add_variable(f), + honk_composer.add_variable(g), honk_composer.add_variable(h), + }; + + size_t rom_id = honk_composer.create_ROM_array(8); + + for (size_t i = 0; i < 8; ++i) { + honk_composer.set_ROM_element(rom_id, i, rom_values[i]); + } + + uint32_t a_idx = honk_composer.read_ROM_array(rom_id, honk_composer.add_variable(5)); + EXPECT_EQ(a_idx != rom_values[5], true); + uint32_t b_idx = honk_composer.read_ROM_array(rom_id, honk_composer.add_variable(4)); + uint32_t c_idx = honk_composer.read_ROM_array(rom_id, honk_composer.add_variable(1)); + + const auto d_value = + honk_composer.get_variable(a_idx) + honk_composer.get_variable(b_idx) + honk_composer.get_variable(c_idx); + uint32_t d_idx = honk_composer.add_variable(d_value); + + honk_composer.create_big_add_gate({ + a_idx, + b_idx, + c_idx, + d_idx, + 1, + 1, + 1, + -1, + 0, + }); + } + { + uint32_t rom_values[8]{ + plonk_composer.add_variable(a), plonk_composer.add_variable(b), plonk_composer.add_variable(c), + plonk_composer.add_variable(d), plonk_composer.add_variable(e), plonk_composer.add_variable(f), + plonk_composer.add_variable(g), plonk_composer.add_variable(h), + }; + + size_t rom_id = plonk_composer.create_ROM_array(8); + + for (size_t i = 0; i < 8; ++i) { + plonk_composer.set_ROM_element(rom_id, i, rom_values[i]); + } + + uint32_t a_idx = plonk_composer.read_ROM_array(rom_id, plonk_composer.add_variable(5)); + EXPECT_EQ(a_idx != rom_values[5], true); + uint32_t b_idx = plonk_composer.read_ROM_array(rom_id, plonk_composer.add_variable(4)); + uint32_t c_idx = plonk_composer.read_ROM_array(rom_id, plonk_composer.add_variable(1)); + + const auto d_value = plonk_composer.get_variable(a_idx) + plonk_composer.get_variable(b_idx) + + plonk_composer.get_variable(c_idx); + uint32_t d_idx = plonk_composer.add_variable(d_value); + + plonk_composer.create_big_add_gate({ + a_idx, + b_idx, + c_idx, + d_idx, + 1, + 1, + 1, + -1, + 0, + }); + } + + auto honk_prover = honk_composer.create_prover(); + auto plonk_prover = plonk_composer.create_prover(); + + check_consistency(honk_prover, plonk_prover); + verify_consistency(honk_prover, plonk_prover); +} + +TEST(UltraHonkComposer, ram) +{ + auto honk_composer = UltraHonkComposer(); + auto plonk_composer = proof_system::plonk::UltraComposer(); + + auto a = fr::random_element(); + auto b = fr::random_element(); + auto c = fr::random_element(); + auto d = fr::random_element(); + auto e = fr::random_element(); + auto f = fr::random_element(); + auto g = fr::random_element(); + auto h = fr::random_element(); + { + uint32_t ram_values[8]{ + honk_composer.add_variable(a), honk_composer.add_variable(b), honk_composer.add_variable(c), + honk_composer.add_variable(d), honk_composer.add_variable(e), honk_composer.add_variable(f), + honk_composer.add_variable(g), honk_composer.add_variable(h), + }; + + size_t ram_id = honk_composer.create_RAM_array(8); + + for (size_t i = 0; i < 8; ++i) { + honk_composer.init_RAM_element(ram_id, i, ram_values[i]); + } + + uint32_t a_idx = honk_composer.read_RAM_array(ram_id, honk_composer.add_variable(5)); + EXPECT_EQ(a_idx != ram_values[5], true); + + uint32_t b_idx = honk_composer.read_RAM_array(ram_id, honk_composer.add_variable(4)); + uint32_t c_idx = honk_composer.read_RAM_array(ram_id, honk_composer.add_variable(1)); + + honk_composer.write_RAM_array(ram_id, honk_composer.add_variable(4), honk_composer.add_variable(500)); + uint32_t d_idx = honk_composer.read_RAM_array(ram_id, honk_composer.add_variable(4)); + + EXPECT_EQ(honk_composer.get_variable(d_idx), 500); + + // ensure these vars get used in another arithmetic gate + const auto e_value = honk_composer.get_variable(a_idx) + honk_composer.get_variable(b_idx) + + honk_composer.get_variable(c_idx) + honk_composer.get_variable(d_idx); + uint32_t e_idx = honk_composer.add_variable(e_value); + + honk_composer.create_big_add_gate( + { + a_idx, + b_idx, + c_idx, + d_idx, + -1, + -1, + -1, + -1, + 0, + }, + true); + honk_composer.create_big_add_gate( + { + honk_composer.get_zero_idx(), + honk_composer.get_zero_idx(), + honk_composer.get_zero_idx(), + e_idx, + 0, + 0, + 0, + 0, + 0, + }, + false); + } + { + uint32_t ram_values[8]{ + plonk_composer.add_variable(a), plonk_composer.add_variable(b), plonk_composer.add_variable(c), + plonk_composer.add_variable(d), plonk_composer.add_variable(e), plonk_composer.add_variable(f), + plonk_composer.add_variable(g), plonk_composer.add_variable(h), + }; + + size_t ram_id = plonk_composer.create_RAM_array(8); + + for (size_t i = 0; i < 8; ++i) { + plonk_composer.init_RAM_element(ram_id, i, ram_values[i]); + } + + uint32_t a_idx = plonk_composer.read_RAM_array(ram_id, plonk_composer.add_variable(5)); + EXPECT_EQ(a_idx != ram_values[5], true); + + uint32_t b_idx = plonk_composer.read_RAM_array(ram_id, plonk_composer.add_variable(4)); + uint32_t c_idx = plonk_composer.read_RAM_array(ram_id, plonk_composer.add_variable(1)); + + plonk_composer.write_RAM_array(ram_id, plonk_composer.add_variable(4), plonk_composer.add_variable(500)); + uint32_t d_idx = plonk_composer.read_RAM_array(ram_id, plonk_composer.add_variable(4)); + + EXPECT_EQ(plonk_composer.get_variable(d_idx), 500); + + // ensure these vars get used in another arithmetic gate + const auto e_value = plonk_composer.get_variable(a_idx) + plonk_composer.get_variable(b_idx) + + plonk_composer.get_variable(c_idx) + plonk_composer.get_variable(d_idx); + uint32_t e_idx = plonk_composer.add_variable(e_value); + + plonk_composer.create_big_add_gate( + { + a_idx, + b_idx, + c_idx, + d_idx, + -1, + -1, + -1, + -1, + 0, + }, + true); + plonk_composer.create_big_add_gate( + { + plonk_composer.zero_idx, + plonk_composer.zero_idx, + plonk_composer.zero_idx, + e_idx, + 0, + 0, + 0, + 0, + 0, + }, + false); + } + + auto honk_prover = honk_composer.create_prover(); + auto plonk_prover = plonk_composer.create_prover(); + + verify_consistency(honk_prover, plonk_prover); +} + +} // namespace test_ultra_honk_composer diff --git a/cpp/src/barretenberg/honk/flavor/flavor.hpp b/cpp/src/barretenberg/honk/flavor/flavor.hpp index ad47783e1d..c420ed64aa 100644 --- a/cpp/src/barretenberg/honk/flavor/flavor.hpp +++ b/cpp/src/barretenberg/honk/flavor/flavor.hpp @@ -14,7 +14,7 @@ struct StandardArithmetization { * This separation must be maintained to allow for programmatic access, but the ordering of the * polynomials can be permuted within each category if necessary. Polynomials can also be added * or removed (assuming consistency with the prover algorithm) but the constants describing the - * number of poynomials in each category must be manually updated. + * number of polynomials in each category must be manually updated. * */ enum POLYNOMIAL { @@ -187,4 +187,58 @@ struct StandardHonk { return output; } }; + +struct UltraArithmetization { + /** + * @brief All of the multivariate polynomials used by the Ultra Honk Prover. + * @details The polynomials are broken into three categories: precomputed, witness, and shifted. + * This separation must be maintained to allow for programmatic access, but the ordering of the + * polynomials can be permuted within each category if necessary. Polynomials can also be added + * or removed (assuming consistency with the prover algorithm) but the constants describing the + * number of polynomials in each category must be manually updated. + * + */ + enum POLYNOMIAL { + /* --- PRECOMPUTED POLYNOMIALS --- */ + Q_C, + Q_L, + Q_R, + Q_O, + Q_4, + Q_M, + QARITH, + QSORT, + QELLIPTIC, + QAUX, + QLOOKUPTYPE, + SIGMA_1, + SIGMA_2, + SIGMA_3, + SIGMA_4, + ID_1, + ID_2, + ID_3, + ID_4, + LAGRANGE_FIRST, + LAGRANGE_LAST, // = LAGRANGE_N-1 whithout ZK, but can be less + /* --- WITNESS POLYNOMIALS --- */ + W_L, + W_R, + W_O, + W_4, + S_1, + S_2, + S_3, + S_4, + Z_PERM, + Z_LOOKUP, + /* --- SHIFTED POLYNOMIALS --- */ + W_1_SHIFT, + W_4_SHIFT, + Z_PERM_SHIFT, + Z_LOOKUP_SHIFT, + /* --- --- */ + COUNT // for programmatic determination of NUM_POLYNOMIALS + }; +}; } // namespace proof_system::honk diff --git a/cpp/src/barretenberg/honk/proof_system/prover_library.cpp b/cpp/src/barretenberg/honk/proof_system/prover_library.cpp index d99a2947a4..753bee2b92 100644 --- a/cpp/src/barretenberg/honk/proof_system/prover_library.cpp +++ b/cpp/src/barretenberg/honk/proof_system/prover_library.cpp @@ -1,6 +1,7 @@ #include "prover_library.hpp" #include "barretenberg/plonk/proof_system/types/prover_settings.hpp" #include +#include namespace proof_system::honk::prover_library { @@ -55,19 +56,18 @@ Polynomial compute_permutation_grand_product(std::shared_ptr // Populate wire and permutation polynomials std::array, program_width> wires; std::array, program_width> sigmas; + std::array, program_width> ids; for (size_t i = 0; i < program_width; ++i) { - std::string sigma_id = "sigma_" + std::to_string(i + 1) + "_lagrange"; wires[i] = wire_polynomials[i]; - sigmas[i] = key->polynomial_store.get(sigma_id); + sigmas[i] = key->polynomial_store.get("sigma_" + std::to_string(i + 1) + "_lagrange"); + ids[i] = key->polynomial_store.get("id_" + std::to_string(i + 1) + "_lagrange"); } // Step (1) // TODO(#222)(kesha): Change the order to engage automatic prefetching and get rid of redundant computation for (size_t i = 0; i < key->circuit_size; ++i) { for (size_t k = 0; k < program_width; ++k) { - // Note(luke): this idx could be replaced by proper ID polys if desired - Fr idx = k * key->circuit_size + i; - numerator_accumulator[k][i] = wires[k][i] + (idx * beta) + gamma; // w_k(i) + β.(k*n+i) + γ + numerator_accumulator[k][i] = wires[k][i] + (ids[k][i] * beta) + gamma; // w_k(i) + β.id_k(i) + γ denominator_accumulator[k][i] = wires[k][i] + (sigmas[k][i] * beta) + gamma; // w_k(i) + β.σ_k(i) + γ } } diff --git a/cpp/src/barretenberg/honk/proof_system/prover_library.test.cpp b/cpp/src/barretenberg/honk/proof_system/prover_library.test.cpp index 0902129370..f62380ebc9 100644 --- a/cpp/src/barretenberg/honk/proof_system/prover_library.test.cpp +++ b/cpp/src/barretenberg/honk/proof_system/prover_library.test.cpp @@ -13,7 +13,6 @@ using namespace proof_system::honk; namespace prover_library_tests { -// field is named Fscalar here because of clash with the Fr template class ProverLibraryTests : public testing::Test { using Polynomial = barretenberg::Polynomial; @@ -60,13 +59,17 @@ template class ProverLibraryTests : public testing::Test { // can simply be random. We're not interested in the particular properties of the result. std::vector wires; std::vector sigmas; + std::vector ids; for (size_t i = 0; i < program_width; ++i) { wires.emplace_back(get_random_polynomial(num_gates)); sigmas.emplace_back(get_random_polynomial(num_gates)); + ids.emplace_back(get_random_polynomial(num_gates)); - // Add sigma polys to proving_key; to be used by the prover in constructing it's own z_perm - std::string sigma_id = "sigma_" + std::to_string(i + 1) + "_lagrange"; - proving_key->polynomial_store.put(sigma_id, Polynomial{ sigmas[i] }); + // Add sigma/ID polys to proving_key; to be used by the prover in constructing it's own z_perm + std::string sigma_label = "sigma_" + std::to_string(i + 1) + "_lagrange"; + proving_key->polynomial_store.put(sigma_label, Polynomial{ sigmas[i] }); + std::string id_label = "id_" + std::to_string(i + 1) + "_lagrange"; + proving_key->polynomial_store.put(id_label, Polynomial{ ids[i] }); } // Get random challenges @@ -109,8 +112,7 @@ template class ProverLibraryTests : public testing::Test { // Step (1) for (size_t i = 0; i < proving_key->circuit_size; ++i) { for (size_t k = 0; k < program_width; ++k) { - FF idx = k * proving_key->circuit_size + i; // id_k[i] - numererator_accum[k][i] = wires[k][i] + (idx * beta) + gamma; // w_k(i) + β.(k*n+i) + γ + numererator_accum[k][i] = wires[k][i] + (ids[k][i] * beta) + gamma; // w_k(i) + β.id_k(i) + γ denominator_accum[k][i] = wires[k][i] + (sigmas[k][i] * beta) + gamma; // w_k(i) + β.σ_k(i) + γ } } @@ -206,8 +208,8 @@ template class ProverLibraryTests : public testing::Test { // ∏(s_k + βs_{k+1} + γ(1 + β)) // // in a way that is simple to read (but inefficient). See prover library method for more details. - const Fr eta_sqr = eta.sqr(); - const Fr eta_cube = eta_sqr * eta; + const FF eta_sqr = eta.sqr(); + const FF eta_cube = eta_sqr * eta; std::array accumulators; for (size_t i = 0; i < 4; ++i) { @@ -219,12 +221,12 @@ template class ProverLibraryTests : public testing::Test { // Note: block_mask is used for efficient modulus, i.e. i % N := i & (N-1), for N = 2^k const size_t block_mask = circuit_size - 1; // Initialize 't(X)' to be used in an expression of the form t(X) + β*t(Xω) - Fr table_i = tables[0][0] + tables[1][0] * eta + tables[2][0] * eta_sqr + tables[3][0] * eta_cube; + FF table_i = tables[0][0] + tables[1][0] * eta + tables[2][0] * eta_sqr + tables[3][0] * eta_cube; for (size_t i = 0; i < circuit_size; ++i) { size_t shift_idx = (i + 1) & block_mask; // f = (w_1 + q_2*w_1(Xω)) + η(w_2 + q_m*w_2(Xω)) + η²(w_3 + q_c*w_3(Xω)) + η³q_index. - Fr f_i = (wires[0][i] + wires[0][shift_idx] * column_1_step_size[i]) + + FF f_i = (wires[0][i] + wires[0][shift_idx] * column_1_step_size[i]) + (wires[1][i] + wires[1][shift_idx] * column_2_step_size[i]) * eta + (wires[2][i] + wires[2][shift_idx] * column_3_step_size[i]) * eta_sqr + eta_cube * lookup_index_selector[i]; @@ -233,17 +235,17 @@ template class ProverLibraryTests : public testing::Test { accumulators[0][i] = lookup_selector[i] * f_i + gamma; // t = t_1 + ηt_2 + η²t_3 + η³t_4 - Fr table_i_plus_1 = tables[0][shift_idx] + eta * tables[1][shift_idx] + eta_sqr * tables[2][shift_idx] + + FF table_i_plus_1 = tables[0][shift_idx] + eta * tables[1][shift_idx] + eta_sqr * tables[2][shift_idx] + eta_cube * tables[3][shift_idx]; // t + βt(Xω) + γ(1 + β) - accumulators[1][i] = table_i + table_i_plus_1 * beta + gamma * (Fr::one() + beta); + accumulators[1][i] = table_i + table_i_plus_1 * beta + gamma * (FF::one() + beta); // (1 + β) - accumulators[2][i] = Fr::one() + beta; + accumulators[2][i] = FF::one() + beta; // s + βs(Xω) + γ(1 + β) - accumulators[3][i] = s_lagrange[i] + beta * s_lagrange[shift_idx] + gamma * (Fr::one() + beta); + accumulators[3][i] = s_lagrange[i] + beta * s_lagrange[shift_idx] + gamma * (FF::one() + beta); // Set t(X_i) for next iteration table_i = table_i_plus_1; @@ -303,8 +305,8 @@ template class ProverLibraryTests : public testing::Test { prover_library::compute_sorted_list_accumulator(proving_key, sorted_list_polynomials, eta); // Method 2: Compute local sorted list accumulator simply and inefficiently - const Fr eta_sqr = eta.sqr(); - const Fr eta_cube = eta_sqr * eta; + const FF eta_sqr = eta.sqr(); + const FF eta_cube = eta_sqr * eta; // Compute s = s_1 + η*s_2 + η²*s_3 + η³*s_4 Polynomial sorted_list_accumulator_expected{ sorted_list_polynomials[0] }; diff --git a/cpp/src/barretenberg/honk/proof_system/ultra_prover.cpp b/cpp/src/barretenberg/honk/proof_system/ultra_prover.cpp new file mode 100644 index 0000000000..769e744a6d --- /dev/null +++ b/cpp/src/barretenberg/honk/proof_system/ultra_prover.cpp @@ -0,0 +1,56 @@ +#include "ultra_prover.hpp" +#include +#include +#include "barretenberg/honk/proof_system/prover_library.hpp" +#include "barretenberg/honk/sumcheck/sumcheck.hpp" +#include +#include "barretenberg/honk/sumcheck/polynomials/univariate.hpp" // will go away +#include "barretenberg/honk/utils/power_polynomial.hpp" +#include "barretenberg/honk/pcs/commitment_key.hpp" +#include +#include +#include +#include +#include "barretenberg/ecc/curves/bn254/fr.hpp" +#include "barretenberg/ecc/curves/bn254/g1.hpp" +#include "barretenberg/honk/sumcheck/relations/arithmetic_relation.hpp" +#include "barretenberg/honk/sumcheck/relations/grand_product_computation_relation.hpp" +#include "barretenberg/honk/sumcheck/relations/grand_product_initialization_relation.hpp" +#include "barretenberg/polynomials/polynomial.hpp" +#include "barretenberg/honk/flavor/flavor.hpp" +#include "barretenberg/transcript/transcript_wrappers.hpp" +#include +#include "barretenberg/honk/pcs/claim.hpp" + +namespace proof_system::honk { + +/** + * Create UltraHonkProver from proving key, witness and manifest. + * + * @param input_key Proving key. + * @param input_manifest Input manifest + * + * @tparam settings Settings class. + * */ +template +UltraHonkProver::UltraHonkProver(std::vector&& wire_polys, + std::shared_ptr input_key) + : wire_polynomials(wire_polys) + , key(input_key) + , queue(key, transcript) +{} + +template plonk::proof& UltraHonkProver::export_proof() +{ + proof.proof_data = transcript.proof_data; + return proof; +} + +template plonk::proof& UltraHonkProver::construct_proof() +{ + return export_proof(); +} + +template class UltraHonkProver; + +} // namespace proof_system::honk diff --git a/cpp/src/barretenberg/honk/proof_system/ultra_prover.hpp b/cpp/src/barretenberg/honk/proof_system/ultra_prover.hpp new file mode 100644 index 0000000000..b645e5863f --- /dev/null +++ b/cpp/src/barretenberg/honk/proof_system/ultra_prover.hpp @@ -0,0 +1,63 @@ +#pragma once +#include "barretenberg/ecc/curves/bn254/fr.hpp" +#include "barretenberg/honk/pcs/shplonk/shplonk.hpp" +#include "barretenberg/polynomials/polynomial.hpp" +#include "barretenberg/honk/flavor/flavor.hpp" +#include +#include "barretenberg/plonk/proof_system/proving_key/proving_key.hpp" +#include "barretenberg/honk/pcs/commitment_key.hpp" +#include "barretenberg/plonk/proof_system/types/proof.hpp" +#include "barretenberg/plonk/proof_system/types/program_settings.hpp" +#include "barretenberg/honk/pcs/gemini/gemini.hpp" +#include "barretenberg/honk/pcs/shplonk/shplonk_single.hpp" +#include "barretenberg/honk/pcs/kzg/kzg.hpp" +#include "barretenberg/honk/transcript/transcript.hpp" +#include "barretenberg/honk/sumcheck/sumcheck.hpp" +#include "barretenberg/honk/sumcheck/sumcheck_output.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include "barretenberg/honk/pcs/claim.hpp" +#include "barretenberg/honk/proof_system/prover_library.hpp" + +namespace proof_system::honk { + +// TODO(luke): The naming here is awkward. The Standard Honk prover is called "Prover" and aliased as StandardProver. To +// be consistent with that convention outside of the prover class itself, I've called this class UltraHonkProver and use +// the alias UltraProver externally. Resolve. +template class UltraHonkProver { + + using Fr = barretenberg::fr; + using Polynomial = barretenberg::Polynomial; + using Commitment = barretenberg::g1::affine_element; + using POLYNOMIAL = proof_system::honk::StandardArithmetization::POLYNOMIAL; + + public: + UltraHonkProver(std::vector&& wire_polys, + std::shared_ptr input_key = nullptr); + + plonk::proof& export_proof(); + plonk::proof& construct_proof(); + + ProverTranscript transcript; + + std::vector wire_polynomials; + + std::shared_ptr key; + + work_queue queue; + + private: + plonk::proof proof; +}; + +extern template class UltraHonkProver; + +using UltraProver = UltraHonkProver; + +} // namespace proof_system::honk diff --git a/cpp/src/barretenberg/honk/proof_system/verifier.cpp b/cpp/src/barretenberg/honk/proof_system/verifier.cpp index 26e4b7e033..b26f10fa47 100644 --- a/cpp/src/barretenberg/honk/proof_system/verifier.cpp +++ b/cpp/src/barretenberg/honk/proof_system/verifier.cpp @@ -3,7 +3,6 @@ #include #include #include "barretenberg/honk/transcript/transcript.hpp" -#include "barretenberg/plonk/proof_system/constants.hpp" #include "./verifier.hpp" #include "barretenberg/plonk/proof_system/public_inputs/public_inputs.hpp" #include "barretenberg/ecc/curves/bn254/fr.hpp" diff --git a/cpp/src/barretenberg/honk/proof_system/verifier.test.cpp b/cpp/src/barretenberg/honk/proof_system/verifier.test.cpp index b98d39e893..903ea36adb 100644 --- a/cpp/src/barretenberg/honk/proof_system/verifier.test.cpp +++ b/cpp/src/barretenberg/honk/proof_system/verifier.test.cpp @@ -1,5 +1,4 @@ #include "barretenberg/numeric/bitop/get_msb.hpp" -#include "barretenberg/plonk/proof_system/constants.hpp" #include "barretenberg/polynomials/polynomial.hpp" #include "barretenberg/honk/flavor/flavor.hpp" #include "prover.hpp" diff --git a/cpp/src/barretenberg/honk/sumcheck/relations/grand_product_computation_relation.hpp b/cpp/src/barretenberg/honk/sumcheck/relations/grand_product_computation_relation.hpp index 5ecdedf61d..61f799e030 100644 --- a/cpp/src/barretenberg/honk/sumcheck/relations/grand_product_computation_relation.hpp +++ b/cpp/src/barretenberg/honk/sumcheck/relations/grand_product_computation_relation.hpp @@ -90,4 +90,91 @@ template class GrandProductComputationRelation { (w_2 + beta * sigma_2 + gamma) * (w_3 + beta * sigma_3 + gamma)); }; }; + +// TODO(luke): With Cody's Flavor work it should be easier to create a simple templated relation +// for handling arbitrary width. For now I'm duplicating the width 3 logic for width 4. +template class UltraGrandProductComputationRelation { + public: + // 1 + polynomial degree of this relation + static constexpr size_t RELATION_LENGTH = 6; + using MULTIVARIATE = proof_system::honk::UltraArithmetization::POLYNOMIAL; + + /** + * @brief Compute contribution of the permutation relation for a given edge (internal function) + * + * @details This the relation confirms faithful calculation of the grand + * product polynomial Z_perm. + * + * @param evals transformed to `evals + C(extended_edges(X)...)*scaling_factor` + * @param extended_edges an std::array containing the fully extended Univariate edges. + * @param parameters contains beta, gamma, and public_input_delta, .... + * @param scaling_factor optional term to scale the evaluation before adding to evals. + */ + inline void add_edge_contribution(Univariate& evals, + const auto& extended_edges, + const RelationParameters& relation_parameters, + const FF& scaling_factor) const + { + const auto& beta = relation_parameters.beta; + const auto& gamma = relation_parameters.gamma; + const auto& public_input_delta = relation_parameters.public_input_delta; + + auto w_1 = UnivariateView(extended_edges[MULTIVARIATE::W_L]); + auto w_2 = UnivariateView(extended_edges[MULTIVARIATE::W_R]); + auto w_3 = UnivariateView(extended_edges[MULTIVARIATE::W_O]); + auto w_4 = UnivariateView(extended_edges[MULTIVARIATE::W_4]); + auto sigma_1 = UnivariateView(extended_edges[MULTIVARIATE::SIGMA_1]); + auto sigma_2 = UnivariateView(extended_edges[MULTIVARIATE::SIGMA_2]); + auto sigma_3 = UnivariateView(extended_edges[MULTIVARIATE::SIGMA_3]); + auto sigma_4 = UnivariateView(extended_edges[MULTIVARIATE::SIGMA_4]); + auto id_1 = UnivariateView(extended_edges[MULTIVARIATE::ID_1]); + auto id_2 = UnivariateView(extended_edges[MULTIVARIATE::ID_2]); + auto id_3 = UnivariateView(extended_edges[MULTIVARIATE::ID_3]); + auto id_4 = UnivariateView(extended_edges[MULTIVARIATE::ID_4]); + auto z_perm = UnivariateView(extended_edges[MULTIVARIATE::Z_PERM]); + auto z_perm_shift = UnivariateView(extended_edges[MULTIVARIATE::Z_PERM_SHIFT]); + auto lagrange_first = UnivariateView(extended_edges[MULTIVARIATE::LAGRANGE_FIRST]); + auto lagrange_last = UnivariateView(extended_edges[MULTIVARIATE::LAGRANGE_LAST]); + + // Contribution (1) + evals += (((z_perm + lagrange_first) * (w_1 + id_1 * beta + gamma) * (w_2 + id_2 * beta + gamma) * + (w_3 + id_3 * beta + gamma) * (w_4 + id_4 * beta + gamma)) - + ((z_perm_shift + lagrange_last * public_input_delta) * (w_1 + sigma_1 * beta + gamma) * + (w_2 + sigma_2 * beta + gamma) * (w_3 + sigma_3 * beta + gamma) * (w_4 + sigma_4 * beta + gamma))) * + scaling_factor; + }; + + void add_full_relation_value_contribution(FF& full_honk_relation_value, + auto& purported_evaluations, + const RelationParameters& relation_parameters) const + { + const auto& beta = relation_parameters.beta; + const auto& gamma = relation_parameters.gamma; + const auto& public_input_delta = relation_parameters.public_input_delta; + + auto w_1 = purported_evaluations[MULTIVARIATE::W_L]; + auto w_2 = purported_evaluations[MULTIVARIATE::W_R]; + auto w_3 = purported_evaluations[MULTIVARIATE::W_O]; + auto w_4 = purported_evaluations[MULTIVARIATE::W_4]; + auto sigma_1 = purported_evaluations[MULTIVARIATE::SIGMA_1]; + auto sigma_2 = purported_evaluations[MULTIVARIATE::SIGMA_2]; + auto sigma_3 = purported_evaluations[MULTIVARIATE::SIGMA_3]; + auto sigma_4 = purported_evaluations[MULTIVARIATE::SIGMA_4]; + auto id_1 = purported_evaluations[MULTIVARIATE::ID_1]; + auto id_2 = purported_evaluations[MULTIVARIATE::ID_2]; + auto id_3 = purported_evaluations[MULTIVARIATE::ID_3]; + auto id_4 = purported_evaluations[MULTIVARIATE::ID_4]; + auto z_perm = purported_evaluations[MULTIVARIATE::Z_PERM]; + auto z_perm_shift = purported_evaluations[MULTIVARIATE::Z_PERM_SHIFT]; + auto lagrange_first = purported_evaluations[MULTIVARIATE::LAGRANGE_FIRST]; + auto lagrange_last = purported_evaluations[MULTIVARIATE::LAGRANGE_LAST]; + + // Contribution (1) + full_honk_relation_value += + ((z_perm + lagrange_first) * (w_1 + beta * id_1 + gamma) * (w_2 + beta * id_2 + gamma) * + (w_3 + beta * id_3 + gamma) * (w_4 + beta * id_4 + gamma) - + (z_perm_shift + lagrange_last * public_input_delta) * (w_1 + beta * sigma_1 + gamma) * + (w_2 + beta * sigma_2 + gamma) * (w_3 + beta * sigma_3 + gamma) * (w_4 + beta * sigma_4 + gamma)); + }; +}; } // namespace proof_system::honk::sumcheck diff --git a/cpp/src/barretenberg/honk/sumcheck/relations/grand_product_initialization_relation.hpp b/cpp/src/barretenberg/honk/sumcheck/relations/grand_product_initialization_relation.hpp index 788cec7f52..6aa7d9f3b2 100644 --- a/cpp/src/barretenberg/honk/sumcheck/relations/grand_product_initialization_relation.hpp +++ b/cpp/src/barretenberg/honk/sumcheck/relations/grand_product_initialization_relation.hpp @@ -45,4 +45,48 @@ template class GrandProductInitializationRelation { full_honk_relation_value += lagrange_last * z_perm_shift; }; }; + +// TODO(luke): The only difference between the Ultra relation and the Standard version is the enum +// used to refer into the edge polynomials. Seems desireable to not duplicate the code here but +// leaving this as is until Codys Flavor work is settled. +template class UltraGrandProductInitializationRelation { + public: + // 1 + polynomial degree of this relation + static constexpr size_t RELATION_LENGTH = 3; + using MULTIVARIATE = UltraArithmetization::POLYNOMIAL; // could just get from StandardArithmetization + + /** + * @brief Add contribution of the permutation relation for a given edge + * + * @details There are 2 relations associated with enforcing the wire copy relations + * This file handles the relation Z_perm_shift(n_last) = 0 via the relation: + * + * C(X) = L_LAST(X) * Z_perm_shift(X) + * + * @param evals transformed to `evals + C(extended_edges(X)...)*scaling_factor` + * @param extended_edges an std::array containing the fully extended Univariate edges. + * @param parameters contains beta, gamma, and public_input_delta, .... + * @param scaling_factor optional term to scale the evaluation before adding to evals. + */ + void add_edge_contribution(Univariate& evals, + const auto& extended_edges, + const RelationParameters&, + const FF& scaling_factor) const + { + auto z_perm_shift = UnivariateView(extended_edges[MULTIVARIATE::Z_PERM_SHIFT]); + auto lagrange_last = UnivariateView(extended_edges[MULTIVARIATE::LAGRANGE_LAST]); + + evals += (lagrange_last * z_perm_shift) * scaling_factor; + }; + + void add_full_relation_value_contribution(FF& full_honk_relation_value, + auto& purported_evaluations, + const RelationParameters&) const + { + auto z_perm_shift = purported_evaluations[MULTIVARIATE::Z_PERM_SHIFT]; + auto lagrange_last = purported_evaluations[MULTIVARIATE::LAGRANGE_LAST]; + + full_honk_relation_value += lagrange_last * z_perm_shift; + }; +}; } // namespace proof_system::honk::sumcheck diff --git a/cpp/src/barretenberg/honk/sumcheck/relations/relation.hpp b/cpp/src/barretenberg/honk/sumcheck/relations/relation.hpp index a7860faeeb..e03f09f990 100644 --- a/cpp/src/barretenberg/honk/sumcheck/relations/relation.hpp +++ b/cpp/src/barretenberg/honk/sumcheck/relations/relation.hpp @@ -2,7 +2,6 @@ namespace proof_system::honk::sumcheck { -// TODO(#226)(Adrian): Remove zeta, alpha as they are not used by the relations. template struct RelationParameters { FF beta; FF gamma; diff --git a/cpp/src/barretenberg/honk/sumcheck/relations/relation.test.cpp b/cpp/src/barretenberg/honk/sumcheck/relations/relation_consistency.test.cpp similarity index 57% rename from cpp/src/barretenberg/honk/sumcheck/relations/relation.test.cpp rename to cpp/src/barretenberg/honk/sumcheck/relations/relation_consistency.test.cpp index 6b7e1ac521..5557d7d47c 100644 --- a/cpp/src/barretenberg/honk/sumcheck/relations/relation.test.cpp +++ b/cpp/src/barretenberg/honk/sumcheck/relations/relation_consistency.test.cpp @@ -1,3 +1,5 @@ +#include "barretenberg/honk/sumcheck/relations/ultra_arithmetic_relation.hpp" +#include "barretenberg/honk/sumcheck/relations/ultra_arithmetic_relation_secondary.hpp" #include "relation.hpp" #include "barretenberg/honk/flavor/flavor.hpp" #include "arithmetic_relation.hpp" @@ -9,6 +11,7 @@ #include "barretenberg/ecc/curves/bn254/fr.hpp" #include "barretenberg/numeric/random/engine.hpp" +#include #include using namespace proof_system::honk::sumcheck; /** @@ -21,13 +24,11 @@ using namespace proof_system::honk::sumcheck; points), * extends them (using barycentric formula) to six evaluation points, and stores them to an array of polynomials. */ -static const size_t input_univariate_length = 2; -static constexpr size_t FULL_RELATION_LENGTH = 5; -static const size_t NUM_POLYNOMIALS = proof_system::honk::StandardArithmetization::NUM_POLYNOMIALS; +static const size_t INPUT_UNIVARIATE_LENGTH = 2; namespace proof_system::honk_relation_tests { -template class SumcheckRelation : public testing::Test { +template class RelationConsistency : public testing::Test { public: template using Univariate = Univariate; template using UnivariateView = UnivariateView; @@ -35,11 +36,12 @@ template class SumcheckRelation : public testing::Test { // TODO(#225)(Adrian): Accept FULL_RELATION_LENGTH as a template parameter for this function only, so that the test // can decide to which degree the polynomials must be extended. Possible accept an existing list of "edges" and // extend them to the degree. + template static std::array, NUM_POLYNOMIALS> compute_mock_extended_edges( - std::array, NUM_POLYNOMIALS>& input_univariates) + std::array, NUM_POLYNOMIALS>& input_univariates) { - BarycentricData barycentric_2_to_max = - BarycentricData(); + BarycentricData barycentric_2_to_max = + BarycentricData(); std::array, NUM_POLYNOMIALS> extended_univariates; for (size_t i = 0; i < NUM_POLYNOMIALS; ++i) { extended_univariates[i] = barycentric_2_to_max.extend(input_univariates[i]); @@ -136,6 +138,7 @@ template class SumcheckRelation : public testing::Test { * @param extended_edges * @param relation_parameters */ + template static void validate_evaluations( const Univariate& expected_evals, const auto relation, @@ -166,31 +169,35 @@ template class SumcheckRelation : public testing::Test { }; }; using FieldTypes = testing::Types; -TYPED_TEST_SUITE(SumcheckRelation, FieldTypes); +TYPED_TEST_SUITE(RelationConsistency, FieldTypes); #define SUMCHECK_RELATION_TYPE_ALIASES using FF = TypeParam; -TYPED_TEST(SumcheckRelation, ArithmeticRelation) +TYPED_TEST(RelationConsistency, ArithmeticRelation) { SUMCHECK_RELATION_TYPE_ALIASES using MULTIVARIATE = honk::StandardArithmetization::POLYNOMIAL; + + static constexpr size_t FULL_RELATION_LENGTH = 5; + static const size_t NUM_POLYNOMIALS = proof_system::honk::StandardArithmetization::NUM_POLYNOMIALS; + const auto relation_parameters = TestFixture::compute_mock_relation_parameters(); auto run_test = [&relation_parameters](bool is_random_input) { std::array, NUM_POLYNOMIALS> extended_edges; - std::array, NUM_POLYNOMIALS> input_polynomials; + std::array, NUM_POLYNOMIALS> input_polynomials; if (!is_random_input) { // evaluation form, i.e. input_univariate(0) = 1, input_univariate(1) = 2,.. The polynomial is x+1. for (size_t i = 0; i < NUM_POLYNOMIALS; ++i) { - input_polynomials[i] = Univariate({ 1, 2 }); + input_polynomials[i] = Univariate({ 1, 2 }); } - extended_edges = TestFixture::compute_mock_extended_edges(input_polynomials); + extended_edges = TestFixture::template compute_mock_extended_edges(input_polynomials); } else { // input_univariates are random polynomials of degree one for (size_t i = 0; i < NUM_POLYNOMIALS; ++i) { input_polynomials[i] = - Univariate({ FF::random_element(), FF::random_element() }); + Univariate({ FF::random_element(), FF::random_element() }); } - extended_edges = TestFixture::compute_mock_extended_edges(input_polynomials); + extended_edges = TestFixture::template compute_mock_extended_edges(input_polynomials); }; auto relation = ArithmeticRelation(); // Manually compute the expected edge contribution @@ -207,33 +214,37 @@ TYPED_TEST(SumcheckRelation, ArithmeticRelation) // Ensure that expression changes are detected. // expected_evals, length 4, extends to { { 5, 22, 57, 116, 205} } for input polynomial {1, 2} auto expected_evals = (q_m * w_r * w_l) + (q_r * w_r) + (q_l * w_l) + (q_o * w_o) + (q_c); - TestFixture::validate_evaluations(expected_evals, relation, extended_edges, relation_parameters); + TestFixture::template validate_evaluations(expected_evals, relation, extended_edges, relation_parameters); }; run_test(/* is_random_input=*/true); run_test(/* is_random_input=*/false); }; -TYPED_TEST(SumcheckRelation, GrandProductComputationRelation) +TYPED_TEST(RelationConsistency, GrandProductComputationRelation) { SUMCHECK_RELATION_TYPE_ALIASES using MULTIVARIATE = honk::StandardArithmetization::POLYNOMIAL; + + static constexpr size_t FULL_RELATION_LENGTH = 5; + static const size_t NUM_POLYNOMIALS = proof_system::honk::StandardArithmetization::NUM_POLYNOMIALS; + const auto relation_parameters = TestFixture::compute_mock_relation_parameters(); auto run_test = [&relation_parameters](bool is_random_input) { std::array, NUM_POLYNOMIALS> extended_edges; - std::array, NUM_POLYNOMIALS> input_polynomials; + std::array, NUM_POLYNOMIALS> input_polynomials; if (!is_random_input) { // evaluation form, i.e. input_univariate(0) = 1, input_univariate(1) = 2,.. The polynomial is x+1. for (size_t i = 0; i < NUM_POLYNOMIALS; ++i) { - input_polynomials[i] = Univariate({ 1, 2 }); + input_polynomials[i] = Univariate({ 1, 2 }); } - extended_edges = TestFixture::compute_mock_extended_edges(input_polynomials); + extended_edges = TestFixture::template compute_mock_extended_edges(input_polynomials); } else { // input_univariates are random polynomials of degree one for (size_t i = 0; i < NUM_POLYNOMIALS; ++i) { input_polynomials[i] = - Univariate({ FF::random_element(), FF::random_element() }); + Univariate({ FF::random_element(), FF::random_element() }); } - extended_edges = TestFixture::compute_mock_extended_edges(input_polynomials); + extended_edges = TestFixture::template compute_mock_extended_edges(input_polynomials); }; auto relation = GrandProductComputationRelation(); @@ -269,33 +280,37 @@ TYPED_TEST(SumcheckRelation, GrandProductComputationRelation) (z_perm_shift + lagrange_last * public_input_delta) * (w_1 + sigma_1 * beta + gamma) * (w_2 + sigma_2 * beta + gamma) * (w_3 + sigma_3 * beta + gamma); - TestFixture::validate_evaluations(expected_evals, relation, extended_edges, relation_parameters); + TestFixture::template validate_evaluations(expected_evals, relation, extended_edges, relation_parameters); }; run_test(/* is_random_input=*/true); run_test(/* is_random_input=*/false); }; -TYPED_TEST(SumcheckRelation, GrandProductInitializationRelation) +TYPED_TEST(RelationConsistency, GrandProductInitializationRelation) { SUMCHECK_RELATION_TYPE_ALIASES using MULTIVARIATE = honk::StandardArithmetization::POLYNOMIAL; + + static constexpr size_t FULL_RELATION_LENGTH = 5; + static const size_t NUM_POLYNOMIALS = proof_system::honk::StandardArithmetization::NUM_POLYNOMIALS; + const auto relation_parameters = TestFixture::compute_mock_relation_parameters(); auto run_test = [&relation_parameters](bool is_random_input) { std::array, NUM_POLYNOMIALS> extended_edges; - std::array, NUM_POLYNOMIALS> input_polynomials; + std::array, NUM_POLYNOMIALS> input_polynomials; if (!is_random_input) { // evaluation form, i.e. input_univariate(0) = 1, input_univariate(1) = 2,.. The polynomial is x+1. for (size_t i = 0; i < NUM_POLYNOMIALS; ++i) { - input_polynomials[i] = Univariate({ 1, 2 }); + input_polynomials[i] = Univariate({ 1, 2 }); } - extended_edges = TestFixture::compute_mock_extended_edges(input_polynomials); + extended_edges = TestFixture::template compute_mock_extended_edges(input_polynomials); } else { // input_univariates are random polynomials of degree one for (size_t i = 0; i < NUM_POLYNOMIALS; ++i) { input_polynomials[i] = - Univariate({ FF::random_element(), FF::random_element() }); + Univariate({ FF::random_element(), FF::random_element() }); } - extended_edges = TestFixture::compute_mock_extended_edges(input_polynomials); + extended_edges = TestFixture::template compute_mock_extended_edges(input_polynomials); }; auto relation = GrandProductInitializationRelation(); const auto& z_perm_shift = extended_edges[MULTIVARIATE::Z_PERM_SHIFT]; @@ -305,10 +320,169 @@ TYPED_TEST(SumcheckRelation, GrandProductInitializationRelation) // expected_evals, lenght 3 (coeff form = x^2 + x), extends to { { 0, 2, 6, 12, 20 } } auto expected_evals = z_perm_shift * lagrange_last; - TestFixture::validate_evaluations(expected_evals, relation, extended_edges, relation_parameters); + TestFixture::template validate_evaluations(expected_evals, relation, extended_edges, relation_parameters); }; run_test(/* is_random_input=*/true); run_test(/* is_random_input=*/false); }; +TYPED_TEST(RelationConsistency, UltraArithmeticRelation) +{ + SUMCHECK_RELATION_TYPE_ALIASES + using MULTIVARIATE = honk::UltraArithmetization::POLYNOMIAL; + + static constexpr size_t FULL_RELATION_LENGTH = 6; + static const size_t NUM_POLYNOMIALS = proof_system::honk::UltraArithmetization::COUNT; + + const auto relation_parameters = TestFixture::compute_mock_relation_parameters(); + std::array, NUM_POLYNOMIALS> extended_edges; + std::array, NUM_POLYNOMIALS> input_polynomials; + + // input_univariates are random polynomials of degree one + for (size_t i = 0; i < NUM_POLYNOMIALS; ++i) { + input_polynomials[i] = Univariate({ FF::random_element(), FF::random_element() }); + } + extended_edges = TestFixture::template compute_mock_extended_edges(input_polynomials); + + auto relation = UltraArithmeticRelation(); + + // Extract the extended edges for manual computation of relation contribution + const auto& w_1 = extended_edges[MULTIVARIATE::W_L]; + const auto& w_2 = extended_edges[MULTIVARIATE::W_R]; + const auto& w_3 = extended_edges[MULTIVARIATE::W_O]; + const auto& w_4 = extended_edges[MULTIVARIATE::W_4]; + const auto& w_4_shift = extended_edges[MULTIVARIATE::W_4_SHIFT]; + const auto& q_m = extended_edges[MULTIVARIATE::Q_M]; + const auto& q_l = extended_edges[MULTIVARIATE::Q_L]; + const auto& q_r = extended_edges[MULTIVARIATE::Q_R]; + const auto& q_o = extended_edges[MULTIVARIATE::Q_O]; + const auto& q_4 = extended_edges[MULTIVARIATE::Q_4]; + const auto& q_c = extended_edges[MULTIVARIATE::Q_C]; + const auto& q_arith = extended_edges[MULTIVARIATE::QARITH]; + + static const FF neg_half = FF(-2).invert(); + + auto expected_evals = (q_arith - 3) * (q_m * w_2 * w_1) * neg_half; + expected_evals += (q_l * w_1) + (q_r * w_2) + (q_o * w_3) + (q_4 * w_4) + q_c; + expected_evals += (q_arith - 1) * w_4_shift; + expected_evals *= q_arith; + + TestFixture::template validate_evaluations(expected_evals, relation, extended_edges, relation_parameters); +}; + +TYPED_TEST(RelationConsistency, UltraArithmeticRelationSecondary) +{ + SUMCHECK_RELATION_TYPE_ALIASES + using MULTIVARIATE = honk::UltraArithmetization::POLYNOMIAL; + + static constexpr size_t FULL_RELATION_LENGTH = 6; + static const size_t NUM_POLYNOMIALS = proof_system::honk::UltraArithmetization::COUNT; + + const auto relation_parameters = TestFixture::compute_mock_relation_parameters(); + std::array, NUM_POLYNOMIALS> extended_edges; + std::array, NUM_POLYNOMIALS> input_polynomials; + + // input_univariates are random polynomials of degree one + for (size_t i = 0; i < NUM_POLYNOMIALS; ++i) { + input_polynomials[i] = Univariate({ FF::random_element(), FF::random_element() }); + } + extended_edges = TestFixture::template compute_mock_extended_edges(input_polynomials); + + auto relation = UltraArithmeticRelationSecondary(); + + // Extract the extended edges for manual computation of relation contribution + const auto& w_1 = extended_edges[MULTIVARIATE::W_L]; + const auto& w_4 = extended_edges[MULTIVARIATE::W_4]; + const auto& w_1_shift = extended_edges[MULTIVARIATE::W_1_SHIFT]; + const auto& q_m = extended_edges[MULTIVARIATE::Q_M]; + const auto& q_arith = extended_edges[MULTIVARIATE::QARITH]; + + auto expected_evals = (w_1 + w_4 - w_1_shift + q_m); + expected_evals *= (q_arith - 2) * (q_arith - 1) * q_arith; + + TestFixture::template validate_evaluations(expected_evals, relation, extended_edges, relation_parameters); +}; + +TYPED_TEST(RelationConsistency, UltraGrandProductInitializationRelation) +{ + SUMCHECK_RELATION_TYPE_ALIASES + using MULTIVARIATE = honk::UltraArithmetization::POLYNOMIAL; + + static constexpr size_t FULL_RELATION_LENGTH = 6; + static const size_t NUM_POLYNOMIALS = proof_system::honk::UltraArithmetization::COUNT; + + const auto relation_parameters = TestFixture::compute_mock_relation_parameters(); + std::array, NUM_POLYNOMIALS> extended_edges; + std::array, NUM_POLYNOMIALS> input_polynomials; + + // input_univariates are random polynomials of degree one + for (size_t i = 0; i < NUM_POLYNOMIALS; ++i) { + input_polynomials[i] = Univariate({ FF::random_element(), FF::random_element() }); + } + extended_edges = TestFixture::template compute_mock_extended_edges(input_polynomials); + + auto relation = UltraGrandProductInitializationRelation(); + + // Extract the extended edges for manual computation of relation contribution + const auto& z_perm_shift = extended_edges[MULTIVARIATE::Z_PERM_SHIFT]; + const auto& lagrange_last = extended_edges[MULTIVARIATE::LAGRANGE_LAST]; + + // Compute the expected result using a simple to read version of the relation expression + auto expected_evals = z_perm_shift * lagrange_last; + + TestFixture::template validate_evaluations(expected_evals, relation, extended_edges, relation_parameters); +}; + +TYPED_TEST(RelationConsistency, UltraGrandProductComputationRelation) +{ + SUMCHECK_RELATION_TYPE_ALIASES + using MULTIVARIATE = honk::UltraArithmetization::POLYNOMIAL; + + static constexpr size_t FULL_RELATION_LENGTH = 6; + static const size_t NUM_POLYNOMIALS = proof_system::honk::UltraArithmetization::COUNT; + + const auto relation_parameters = TestFixture::compute_mock_relation_parameters(); + std::array, NUM_POLYNOMIALS> extended_edges; + std::array, NUM_POLYNOMIALS> input_polynomials; + + // input_univariates are random polynomials of degree one + for (size_t i = 0; i < NUM_POLYNOMIALS; ++i) { + input_polynomials[i] = Univariate({ FF::random_element(), FF::random_element() }); + } + extended_edges = TestFixture::template compute_mock_extended_edges(input_polynomials); + + auto relation = UltraGrandProductComputationRelation(); + + const auto& beta = relation_parameters.beta; + const auto& gamma = relation_parameters.gamma; + const auto& public_input_delta = relation_parameters.public_input_delta; + + // Extract the extended edges for manual computation of relation contribution + const auto& w_1 = extended_edges[MULTIVARIATE::W_L]; + const auto& w_2 = extended_edges[MULTIVARIATE::W_R]; + const auto& w_3 = extended_edges[MULTIVARIATE::W_O]; + const auto& w_4 = extended_edges[MULTIVARIATE::W_4]; + const auto& sigma_1 = extended_edges[MULTIVARIATE::SIGMA_1]; + const auto& sigma_2 = extended_edges[MULTIVARIATE::SIGMA_2]; + const auto& sigma_3 = extended_edges[MULTIVARIATE::SIGMA_3]; + const auto& sigma_4 = extended_edges[MULTIVARIATE::SIGMA_4]; + const auto& id_1 = extended_edges[MULTIVARIATE::ID_1]; + const auto& id_2 = extended_edges[MULTIVARIATE::ID_2]; + const auto& id_3 = extended_edges[MULTIVARIATE::ID_3]; + const auto& id_4 = extended_edges[MULTIVARIATE::ID_4]; + const auto& z_perm = extended_edges[MULTIVARIATE::Z_PERM]; + const auto& z_perm_shift = extended_edges[MULTIVARIATE::Z_PERM_SHIFT]; + const auto& lagrange_first = extended_edges[MULTIVARIATE::LAGRANGE_FIRST]; + const auto& lagrange_last = extended_edges[MULTIVARIATE::LAGRANGE_LAST]; + + // Compute the expected result using a simple to read version of the relation expression + auto expected_evals = (z_perm + lagrange_first) * (w_1 + id_1 * beta + gamma) * (w_2 + id_2 * beta + gamma) * + (w_3 + id_3 * beta + gamma) * (w_4 + id_4 * beta + gamma) - + (z_perm_shift + lagrange_last * public_input_delta) * (w_1 + sigma_1 * beta + gamma) * + (w_2 + sigma_2 * beta + gamma) * (w_3 + sigma_3 * beta + gamma) * + (w_4 + sigma_4 * beta + gamma); + + TestFixture::template validate_evaluations(expected_evals, relation, extended_edges, relation_parameters); +}; + } // namespace proof_system::honk_relation_tests diff --git a/cpp/src/barretenberg/honk/sumcheck/relations/relation_correctness.test.cpp b/cpp/src/barretenberg/honk/sumcheck/relations/relation_correctness.test.cpp new file mode 100644 index 0000000000..f5556a31da --- /dev/null +++ b/cpp/src/barretenberg/honk/sumcheck/relations/relation_correctness.test.cpp @@ -0,0 +1,258 @@ +#include "barretenberg/honk/composer/ultra_honk_composer.hpp" +#include "barretenberg/honk/composer/standard_honk_composer.hpp" +#include "barretenberg/honk/sumcheck/relations/relation.hpp" +#include "barretenberg/honk/sumcheck/relations/ultra_arithmetic_relation.hpp" +#include "barretenberg/honk/sumcheck/relations/ultra_arithmetic_relation_secondary.hpp" +#include "barretenberg/numeric/uint256/uint256.hpp" +#include "barretenberg/honk/flavor/flavor.hpp" +#include +#include "barretenberg/honk/proof_system/prover.hpp" +#include "barretenberg/honk/sumcheck/sumcheck_round.hpp" +#include "barretenberg/honk/sumcheck/relations/grand_product_computation_relation.hpp" +#include "barretenberg/honk/sumcheck/relations/grand_product_initialization_relation.hpp" +#include "barretenberg/honk/utils/public_inputs.hpp" + +#include + +using namespace proof_system::honk; + +namespace test_honk_relations { + +/** + * @brief Test the correctness of the Standard Honk relations + * + * @details Check that the constraints encoded by the relations are satisfied by the polynomials produced by the + * Standard Honk Composer for a real circuit. + * + * TODO(Kesha): We'll have to update this function once we add zk, since the relation will be incorrect for he first few + * indices + * + */ +TEST(RelationCorrectness, StandardRelationCorrectness) +{ + // Create a composer and a dummy circuit with a few gates + auto composer = StandardHonkComposer(); + static const size_t num_wires = StandardHonkComposer::num_wires; + fr a = fr::one(); + // Using the public variable to check that public_input_delta is computed and added to the relation correctly + uint32_t a_idx = composer.add_public_variable(a); + fr b = fr::one(); + fr c = a + b; + fr d = a + c; + uint32_t b_idx = composer.add_variable(b); + uint32_t c_idx = composer.add_variable(c); + uint32_t d_idx = composer.add_variable(d); + for (size_t i = 0; i < 16; i++) { + composer.create_add_gate({ a_idx, b_idx, c_idx, fr::one(), fr::one(), fr::neg_one(), fr::zero() }); + composer.create_add_gate({ d_idx, c_idx, a_idx, fr::one(), fr::neg_one(), fr::neg_one(), fr::zero() }); + } + // Create a prover (it will compute proving key and witness) + auto prover = composer.create_prover(); + + // Generate beta and gamma + fr beta = fr::random_element(); + fr gamma = fr::random_element(); + + // Compute public input delta + const auto public_inputs = composer.circuit_constructor.get_public_inputs(); + auto public_input_delta = + honk::compute_public_input_delta(public_inputs, beta, gamma, prover.key->circuit_size); + + sumcheck::RelationParameters params{ + .beta = beta, + .gamma = gamma, + .public_input_delta = public_input_delta, + }; + + constexpr size_t num_polynomials = proof_system::honk::StandardArithmetization::NUM_POLYNOMIALS; + // Compute grand product polynomial + polynomial z_perm_poly = + prover_library::compute_permutation_grand_product(prover.key, prover.wire_polynomials, beta, gamma); + + // Create an array of spans to the underlying polynomials to more easily + // get the transposition. + // Ex: polynomial_spans[3][i] returns the i-th coefficient of the third polynomial + // in the list below + std::array, num_polynomials> evaluations_array; + + using POLYNOMIAL = proof_system::honk::StandardArithmetization::POLYNOMIAL; + evaluations_array[POLYNOMIAL::W_L] = prover.wire_polynomials[0]; + evaluations_array[POLYNOMIAL::W_R] = prover.wire_polynomials[1]; + evaluations_array[POLYNOMIAL::W_O] = prover.wire_polynomials[2]; + evaluations_array[POLYNOMIAL::Z_PERM] = z_perm_poly; + evaluations_array[POLYNOMIAL::Z_PERM_SHIFT] = z_perm_poly.shifted(); + evaluations_array[POLYNOMIAL::Q_M] = prover.key->polynomial_store.get("q_m_lagrange"); + evaluations_array[POLYNOMIAL::Q_L] = prover.key->polynomial_store.get("q_1_lagrange"); + evaluations_array[POLYNOMIAL::Q_R] = prover.key->polynomial_store.get("q_2_lagrange"); + evaluations_array[POLYNOMIAL::Q_O] = prover.key->polynomial_store.get("q_3_lagrange"); + evaluations_array[POLYNOMIAL::Q_C] = prover.key->polynomial_store.get("q_c_lagrange"); + evaluations_array[POLYNOMIAL::SIGMA_1] = prover.key->polynomial_store.get("sigma_1_lagrange"); + evaluations_array[POLYNOMIAL::SIGMA_2] = prover.key->polynomial_store.get("sigma_2_lagrange"); + evaluations_array[POLYNOMIAL::SIGMA_3] = prover.key->polynomial_store.get("sigma_3_lagrange"); + evaluations_array[POLYNOMIAL::ID_1] = prover.key->polynomial_store.get("id_1_lagrange"); + evaluations_array[POLYNOMIAL::ID_2] = prover.key->polynomial_store.get("id_2_lagrange"); + evaluations_array[POLYNOMIAL::ID_3] = prover.key->polynomial_store.get("id_3_lagrange"); + evaluations_array[POLYNOMIAL::LAGRANGE_FIRST] = prover.key->polynomial_store.get("L_first_lagrange"); + evaluations_array[POLYNOMIAL::LAGRANGE_LAST] = prover.key->polynomial_store.get("L_last_lagrange"); + + // Construct the round for applying sumcheck relations and results for storing computed results + auto relations = std::tuple(honk::sumcheck::ArithmeticRelation(), + honk::sumcheck::GrandProductComputationRelation(), + honk::sumcheck::GrandProductInitializationRelation()); + + fr result = 0; + for (size_t i = 0; i < prover.key->circuit_size; i++) { + // Compute an array containing all the evaluations at a given row i + std::array evaluations_at_index_i; + for (size_t j = 0; j < num_polynomials; ++j) { + evaluations_at_index_i[j] = evaluations_array[j][i]; + } + + // For each relation, call the `accumulate_relation_evaluation` over all witness/selector values at the + // i-th row/vertex of the hypercube. + // We use ASSERT_EQ instead of EXPECT_EQ so that the tests stops at the first index at which the result is not + // 0, since result = 0 + C(transposed), which we expect will equal 0. + std::get<0>(relations).add_full_relation_value_contribution(result, evaluations_at_index_i, params); + ASSERT_EQ(result, 0); + + std::get<1>(relations).add_full_relation_value_contribution(result, evaluations_at_index_i, params); + ASSERT_EQ(result, 0); + + std::get<2>(relations).add_full_relation_value_contribution(result, evaluations_at_index_i, params); + ASSERT_EQ(result, 0); + } +} + +/** + * @brief Test the correctness of the Ultra Honk relations + * + * @details Check that the constraints encoded by the relations are satisfied by the polynomials produced by the + * Ultra Honk Composer for a real circuit. + * + * TODO(Kesha): We'll have to update this function once we add zk, since the relation will be incorrect for he first few + * indices + * + */ +// TODO(luke): Increase variety of gates in the test circuit to fully stress the relations, e.g. create_big_add_gate. +// NOTE(luke): More relations will be added as they are implemented for Ultra Honk +TEST(RelationCorrectness, UltraRelationCorrectness) +{ + // Create a composer and a dummy circuit with a few gates + auto composer = UltraHonkComposer(); + static const size_t num_wires = 4; + fr a = fr::one(); + // Using the public variable to check that public_input_delta is computed and added to the relation correctly + // TODO(luke): add method "add_public_variable" to UH composer + // uint32_t a_idx = composer.add_public_variable(a); + uint32_t a_idx = composer.add_variable(a); + fr b = fr::one(); + fr c = a + b; + fr d = a + c; + uint32_t b_idx = composer.add_variable(b); + uint32_t c_idx = composer.add_variable(c); + uint32_t d_idx = composer.add_variable(d); + for (size_t i = 0; i < 1; i++) { + composer.create_add_gate({ a_idx, b_idx, c_idx, fr::one(), fr::one(), fr::neg_one(), fr::zero() }); + composer.create_add_gate({ d_idx, c_idx, a_idx, fr::one(), fr::neg_one(), fr::neg_one(), fr::zero() }); + } + // Create a prover (it will compute proving key and witness) + auto prover = composer.create_prover(); + + // Generate beta and gamma + fr beta = fr::random_element(); + fr gamma = fr::random_element(); + + // Compute public input delta + const auto public_inputs = composer.circuit_constructor.get_public_inputs(); + auto public_input_delta = + honk::compute_public_input_delta(public_inputs, beta, gamma, prover.key->circuit_size); + + info("public_input_delta = ", public_input_delta); + + sumcheck::RelationParameters params{ + .beta = beta, + .gamma = gamma, + .public_input_delta = public_input_delta, + }; + + constexpr size_t num_polynomials = proof_system::honk::UltraArithmetization::COUNT; + // Compute grand product polynomial + auto z_perm_poly = + prover_library::compute_permutation_grand_product(prover.key, prover.wire_polynomials, beta, gamma); + + // Create an array of spans to the underlying polynomials to more easily + // get the transposition. + // Ex: polynomial_spans[3][i] returns the i-th coefficient of the third polynomial + // in the list below + std::array, num_polynomials> evaluations_array; + + using POLYNOMIAL = proof_system::honk::UltraArithmetization::POLYNOMIAL; + evaluations_array[POLYNOMIAL::W_L] = prover.wire_polynomials[0]; + evaluations_array[POLYNOMIAL::W_R] = prover.wire_polynomials[1]; + evaluations_array[POLYNOMIAL::W_O] = prover.wire_polynomials[2]; + evaluations_array[POLYNOMIAL::W_4] = prover.wire_polynomials[3]; + evaluations_array[POLYNOMIAL::W_1_SHIFT] = prover.wire_polynomials[0].shifted(); + evaluations_array[POLYNOMIAL::W_4_SHIFT] = prover.wire_polynomials[3].shifted(); + evaluations_array[POLYNOMIAL::S_1] = prover.key->polynomial_store.get("s_1_lagrange"); + evaluations_array[POLYNOMIAL::S_2] = prover.key->polynomial_store.get("s_2_lagrange"); + evaluations_array[POLYNOMIAL::S_3] = prover.key->polynomial_store.get("s_3_lagrange"); + evaluations_array[POLYNOMIAL::S_4] = prover.key->polynomial_store.get("s_4_lagrange"); + evaluations_array[POLYNOMIAL::Z_PERM] = z_perm_poly; + evaluations_array[POLYNOMIAL::Z_PERM_SHIFT] = z_perm_poly.shifted(); + evaluations_array[POLYNOMIAL::Z_LOOKUP] = z_perm_poly; + evaluations_array[POLYNOMIAL::Z_LOOKUP_SHIFT] = z_perm_poly.shifted(); + evaluations_array[POLYNOMIAL::Q_M] = prover.key->polynomial_store.get("q_m_lagrange"); + evaluations_array[POLYNOMIAL::Q_L] = prover.key->polynomial_store.get("q_1_lagrange"); + evaluations_array[POLYNOMIAL::Q_R] = prover.key->polynomial_store.get("q_2_lagrange"); + evaluations_array[POLYNOMIAL::Q_O] = prover.key->polynomial_store.get("q_3_lagrange"); + evaluations_array[POLYNOMIAL::Q_C] = prover.key->polynomial_store.get("q_c_lagrange"); + evaluations_array[POLYNOMIAL::Q_4] = prover.key->polynomial_store.get("q_4_lagrange"); + evaluations_array[POLYNOMIAL::QARITH] = prover.key->polynomial_store.get("q_arith_lagrange"); + evaluations_array[POLYNOMIAL::QSORT] = prover.key->polynomial_store.get("q_sort_lagrange"); + evaluations_array[POLYNOMIAL::QELLIPTIC] = prover.key->polynomial_store.get("q_elliptic_lagrange"); + evaluations_array[POLYNOMIAL::QAUX] = prover.key->polynomial_store.get("q_aux_lagrange"); + evaluations_array[POLYNOMIAL::QLOOKUPTYPE] = prover.key->polynomial_store.get("table_type_lagrange"); + evaluations_array[POLYNOMIAL::SIGMA_1] = prover.key->polynomial_store.get("sigma_1_lagrange"); + evaluations_array[POLYNOMIAL::SIGMA_2] = prover.key->polynomial_store.get("sigma_2_lagrange"); + evaluations_array[POLYNOMIAL::SIGMA_3] = prover.key->polynomial_store.get("sigma_3_lagrange"); + evaluations_array[POLYNOMIAL::SIGMA_4] = prover.key->polynomial_store.get("sigma_4_lagrange"); + evaluations_array[POLYNOMIAL::ID_1] = prover.key->polynomial_store.get("id_1_lagrange"); + evaluations_array[POLYNOMIAL::ID_2] = prover.key->polynomial_store.get("id_2_lagrange"); + evaluations_array[POLYNOMIAL::ID_3] = prover.key->polynomial_store.get("id_3_lagrange"); + evaluations_array[POLYNOMIAL::ID_4] = prover.key->polynomial_store.get("id_4_lagrange"); + evaluations_array[POLYNOMIAL::LAGRANGE_FIRST] = prover.key->polynomial_store.get("L_first_lagrange"); + evaluations_array[POLYNOMIAL::LAGRANGE_LAST] = prover.key->polynomial_store.get("L_last_lagrange"); + + // Construct the round for applying sumcheck relations and results for storing computed results + auto relations = std::tuple(honk::sumcheck::UltraArithmeticRelation(), + honk::sumcheck::UltraArithmeticRelationSecondary(), + honk::sumcheck::UltraGrandProductInitializationRelation(), + honk::sumcheck::UltraGrandProductComputationRelation()); + + fr result = 0; + for (size_t i = 0; i < prover.key->circuit_size; i++) { + // Compute an array containing all the evaluations at a given row i + std::array evaluations_at_index_i; + for (size_t j = 0; j < num_polynomials; ++j) { + evaluations_at_index_i[j] = evaluations_array[j][i]; + } + + // For each relation, call the `accumulate_relation_evaluation` over all witness/selector values at the + // i-th row/vertex of the hypercube. We use ASSERT_EQ instead of EXPECT_EQ so that the tests stops at + // the first index at which the result is not 0, since result = 0 + C(transposed), which we expect will + // equal 0. + std::get<0>(relations).add_full_relation_value_contribution(result, evaluations_at_index_i, params); + ASSERT_EQ(result, 0); + + std::get<1>(relations).add_full_relation_value_contribution(result, evaluations_at_index_i, params); + ASSERT_EQ(result, 0); + + std::get<2>(relations).add_full_relation_value_contribution(result, evaluations_at_index_i, params); + ASSERT_EQ(result, 0); + + std::get<3>(relations).add_full_relation_value_contribution(result, evaluations_at_index_i, params); + ASSERT_EQ(result, 0); + } +} + +} // namespace test_honk_relations diff --git a/cpp/src/barretenberg/honk/sumcheck/relations/ultra_arithmetic_relation.hpp b/cpp/src/barretenberg/honk/sumcheck/relations/ultra_arithmetic_relation.hpp new file mode 100644 index 0000000000..2df6efc2c7 --- /dev/null +++ b/cpp/src/barretenberg/honk/sumcheck/relations/ultra_arithmetic_relation.hpp @@ -0,0 +1,87 @@ +#pragma once +#include +#include + +#include "barretenberg/honk/flavor/flavor.hpp" +#include "../polynomials/univariate.hpp" +#include "relation.hpp" + +namespace proof_system::honk::sumcheck { + +template class UltraArithmeticRelation { + public: + // 1 + polynomial degree of this relation + static constexpr size_t RELATION_LENGTH = 6; // degree(q_arith^2 * q_m * w_r * w_l) = 5 + using MULTIVARIATE = UltraArithmetization::POLYNOMIAL; + + /** + * @brief Expression for the Ultra Arithmetic gate. + * @details The relation is defined as C(extended_edges(X)...) = + * q_arith * + * [ -1/2(q_arith - 3)(q_m * w_r * w_l) + + * (q_l * w_l) + (q_r * w_r) + (q_o * w_o) + (q_4 * w_4) + q_c + + * (q_arith - 1)w_4_shift ] + * + * @param evals transformed to `evals + C(extended_edges(X)...)*scaling_factor` + * @param extended_edges an std::array containing the fully extended Univariate edges. + * @param parameters contains beta, gamma, and public_input_delta, .... + * @param scaling_factor optional term to scale the evaluation before adding to evals. + */ + void add_edge_contribution(Univariate& evals, + const auto& extended_edges, + const RelationParameters&, + const FF& scaling_factor) const + { + // OPTIMIZATION?: Karatsuba in general, at least for some degrees? + // See https://hackmd.io/xGLuj6biSsCjzQnYN-pEiA?both + + auto w_l = UnivariateView(extended_edges[MULTIVARIATE::W_L]); + auto w_r = UnivariateView(extended_edges[MULTIVARIATE::W_R]); + auto w_o = UnivariateView(extended_edges[MULTIVARIATE::W_O]); + auto w_4 = UnivariateView(extended_edges[MULTIVARIATE::W_4]); + auto w_4_shift = UnivariateView(extended_edges[MULTIVARIATE::W_4_SHIFT]); + auto q_m = UnivariateView(extended_edges[MULTIVARIATE::Q_M]); + auto q_l = UnivariateView(extended_edges[MULTIVARIATE::Q_L]); + auto q_r = UnivariateView(extended_edges[MULTIVARIATE::Q_R]); + auto q_o = UnivariateView(extended_edges[MULTIVARIATE::Q_O]); + auto q_4 = UnivariateView(extended_edges[MULTIVARIATE::Q_4]); + auto q_c = UnivariateView(extended_edges[MULTIVARIATE::Q_C]); + auto q_arith = UnivariateView(extended_edges[MULTIVARIATE::QARITH]); + + static const FF neg_half = FF(-2).invert(); + + auto tmp = (q_arith - 3) * (q_m * w_r * w_l) * neg_half; + tmp += (q_l * w_l) + (q_r * w_r) + (q_o * w_o) + (q_4 * w_4) + q_c; + tmp += (q_arith - 1) * w_4_shift; + tmp *= q_arith; + tmp *= scaling_factor; + evals += tmp; + }; + + void add_full_relation_value_contribution(FF& full_honk_relation_value, + const auto& purported_evaluations, + const RelationParameters&) const + { + auto w_l = purported_evaluations[MULTIVARIATE::W_L]; + auto w_r = purported_evaluations[MULTIVARIATE::W_R]; + auto w_o = purported_evaluations[MULTIVARIATE::W_O]; + auto w_4 = purported_evaluations[MULTIVARIATE::W_4]; + auto w_4_shift = purported_evaluations[MULTIVARIATE::W_4_SHIFT]; + auto q_m = purported_evaluations[MULTIVARIATE::Q_M]; + auto q_l = purported_evaluations[MULTIVARIATE::Q_L]; + auto q_r = purported_evaluations[MULTIVARIATE::Q_R]; + auto q_o = purported_evaluations[MULTIVARIATE::Q_O]; + auto q_4 = purported_evaluations[MULTIVARIATE::Q_4]; + auto q_c = purported_evaluations[MULTIVARIATE::Q_C]; + auto q_arith = purported_evaluations[MULTIVARIATE::QARITH]; + + static const FF neg_half = FF(-2).invert(); + + auto tmp = (q_arith - 3) * (q_m * w_r * w_l) * neg_half; + tmp += (q_l * w_l) + (q_r * w_r) + (q_o * w_o) + (q_4 * w_4) + q_c; + tmp += (q_arith - 1) * w_4_shift; + tmp *= q_arith; + full_honk_relation_value += tmp; + }; +}; +} // namespace proof_system::honk::sumcheck diff --git a/cpp/src/barretenberg/honk/sumcheck/relations/ultra_arithmetic_relation_secondary.hpp b/cpp/src/barretenberg/honk/sumcheck/relations/ultra_arithmetic_relation_secondary.hpp new file mode 100644 index 0000000000..78c01db356 --- /dev/null +++ b/cpp/src/barretenberg/honk/sumcheck/relations/ultra_arithmetic_relation_secondary.hpp @@ -0,0 +1,67 @@ +#pragma once +#include +#include + +#include "barretenberg/honk/flavor/flavor.hpp" +#include "../polynomials/univariate.hpp" +#include "relation.hpp" + +namespace proof_system::honk::sumcheck { + +template class UltraArithmeticRelationSecondary { + public: + // 1 + polynomial degree of this relation + static constexpr size_t RELATION_LENGTH = 5; // degree(q_arith^3 * w_l) = 4 + using MULTIVARIATE = UltraArithmetization::POLYNOMIAL; + + /** + * @brief Expression for the Ultra Arithmetic gate. + * @details The relation is defined as C(extended_edges(X)...) = + * q_arith * + * (q_arith - 2) * (q_arith - 1) * (w_l + w_4 - w_l_shift + q_m) + * + * @param evals transformed to `evals + C(extended_edges(X)...)*scaling_factor` + * @param extended_edges an std::array containing the fully extended Univariate edges. + * @param parameters contains beta, gamma, and public_input_delta, .... + * @param scaling_factor optional term to scale the evaluation before adding to evals. + */ + void add_edge_contribution(Univariate& evals, + const auto& extended_edges, + const RelationParameters&, + const FF& scaling_factor) const + { + // OPTIMIZATION?: Karatsuba in general, at least for some degrees? + // See https://hackmd.io/xGLuj6biSsCjzQnYN-pEiA?both + + auto w_l = UnivariateView(extended_edges[MULTIVARIATE::W_L]); + auto w_4 = UnivariateView(extended_edges[MULTIVARIATE::W_4]); + auto w_l_shift = UnivariateView(extended_edges[MULTIVARIATE::W_1_SHIFT]); + auto q_m = UnivariateView(extended_edges[MULTIVARIATE::Q_M]); + auto q_arith = UnivariateView(extended_edges[MULTIVARIATE::QARITH]); + + auto tmp = w_l + w_4 - w_l_shift + q_m; + tmp *= (q_arith - 2); + tmp *= (q_arith - 1); + tmp *= q_arith; + tmp *= scaling_factor; + evals += tmp; + }; + + void add_full_relation_value_contribution(FF& full_honk_relation_value, + const auto& purported_evaluations, + const RelationParameters&) const + { + auto w_l = purported_evaluations[MULTIVARIATE::W_L]; + auto w_4 = purported_evaluations[MULTIVARIATE::W_4]; + auto w_l_shift = purported_evaluations[MULTIVARIATE::W_1_SHIFT]; + auto q_m = purported_evaluations[MULTIVARIATE::Q_M]; + auto q_arith = purported_evaluations[MULTIVARIATE::QARITH]; + + auto tmp = w_l + w_4 - w_l_shift + q_m; + tmp *= (q_arith - 2); + tmp *= (q_arith - 1); + tmp *= q_arith; + full_honk_relation_value += tmp; + }; +}; +} // namespace proof_system::honk::sumcheck diff --git a/cpp/src/barretenberg/join_split_example/proofs/join_split/c_bind.cpp b/cpp/src/barretenberg/join_split_example/proofs/join_split/c_bind.cpp index cb35a19d27..5ee4b90fad 100644 --- a/cpp/src/barretenberg/join_split_example/proofs/join_split/c_bind.cpp +++ b/cpp/src/barretenberg/join_split_example/proofs/join_split/c_bind.cpp @@ -1,3 +1,6 @@ +#include +#include + #include "c_bind.h" #include "join_split.hpp" #include "compute_signing_data.hpp" @@ -5,14 +8,12 @@ #include "barretenberg/common/streams.hpp" #include "barretenberg/common/mem.hpp" #include "barretenberg/common/container.hpp" -#include #include "barretenberg/ecc/curves/grumpkin/grumpkin.hpp" #include "barretenberg/srs/reference_string/pippenger_reference_string.hpp" #include "barretenberg/plonk/proof_system/proving_key/serialize.hpp" -#include +#include "barretenberg/join_split_example/types.hpp" using namespace barretenberg; -using namespace proof_system::plonk::stdlib::types; using namespace join_split_example::proofs::join_split; #define WASM_EXPORT __attribute__((visibility("default"))) @@ -42,22 +43,10 @@ WASM_EXPORT void join_split__release_key() WASM_EXPORT uint32_t join_split__get_new_proving_key_data(uint8_t** output) { -// Computing the size of the serialized key is non trivial. We know it's ~331mb. -// Allocate a buffer large enough to hold it, and abort if we overflow. -// This is to keep memory usage down. -#ifdef USE_TURBO - size_t total_buf_len = 350 * 1024 * 1024; - auto raw_buf = (uint8_t*)malloc(total_buf_len); - auto raw_buf_end = raw_buf; - write(raw_buf_end, *get_proving_key()); - *output = raw_buf; - auto len = static_cast(raw_buf_end - raw_buf); - if (len > total_buf_len) { - info("Buffer overflow serializing proving key."); - std::abort(); - } - return len; -#else + // Computing the size of the serialized key is non trivial. We know it's ~331mb. + // Allocate a buffer large enough to hold it, and abort if we overflow. + // This is to keep memory usage down. + auto proving_key = get_proving_key(); auto buffer = to_buffer(*proving_key); auto raw_buf = (uint8_t*)malloc(buffer.size()); @@ -65,7 +54,6 @@ WASM_EXPORT uint32_t join_split__get_new_proving_key_data(uint8_t** output) *output = raw_buf; return static_cast(buffer.size()); -#endif } WASM_EXPORT void join_split__init_verification_key(void* pippenger, uint8_t const* g2x) @@ -103,13 +91,13 @@ WASM_EXPORT void* join_split__new_prover(uint8_t const* join_split_buf, bool moc { auto tx = from_buffer(join_split_buf); auto prover = new_join_split_prover(tx, mock); - auto heapProver = new stdlib::types::Prover(std::move(prover)); + auto heapProver = new join_split_example::Prover(std::move(prover)); return heapProver; } WASM_EXPORT void join_split__delete_prover(void* prover) { - delete reinterpret_cast(prover); + delete reinterpret_cast(prover); } WASM_EXPORT bool join_split__verify_proof(uint8_t* proof, uint32_t length) diff --git a/cpp/src/barretenberg/join_split_example/proofs/join_split/compute_circuit_data.cpp b/cpp/src/barretenberg/join_split_example/proofs/join_split/compute_circuit_data.cpp index 47fc4deec2..037799660b 100644 --- a/cpp/src/barretenberg/join_split_example/proofs/join_split/compute_circuit_data.cpp +++ b/cpp/src/barretenberg/join_split_example/proofs/join_split/compute_circuit_data.cpp @@ -3,13 +3,14 @@ #include "sign_join_split_tx.hpp" #include "../notes/native/index.hpp" #include "barretenberg/stdlib/merkle_tree/hash_path.hpp" +#include "barretenberg/join_split_example/types.hpp" namespace join_split_example { namespace proofs { namespace join_split { using namespace join_split_example::proofs::join_split; -using namespace proof_system::plonk::stdlib::types; +using namespace proof_system::plonk::stdlib; using namespace join_split_example::proofs::notes::native; using namespace proof_system::plonk::stdlib::merkle_tree; diff --git a/cpp/src/barretenberg/join_split_example/proofs/join_split/join_split.cpp b/cpp/src/barretenberg/join_split_example/proofs/join_split/join_split.cpp index 44567b5b26..95aee064e2 100644 --- a/cpp/src/barretenberg/join_split_example/proofs/join_split/join_split.cpp +++ b/cpp/src/barretenberg/join_split_example/proofs/join_split/join_split.cpp @@ -2,6 +2,7 @@ #include "join_split_circuit.hpp" #include "compute_circuit_data.hpp" #include "barretenberg/plonk/proof_system/commitment_scheme/kate_commitment_scheme.hpp" +#include "barretenberg/join_split_example/types.hpp" namespace join_split_example { namespace proofs { @@ -23,7 +24,7 @@ void init_proving_key(std::shared_ptr cons join_split_tx tx = noop_tx(); if (!mock) { - stdlib::types::Composer composer(crs_factory); + Composer composer(crs_factory); join_split_circuit(composer, tx); proving_key = composer.compute_proving_key(); } else { @@ -55,8 +56,7 @@ void init_verification_key(std::unique_ptr // Patch the 'nothing' reference string fed to init_proving_key. proving_key->reference_string = crs_factory->get_prover_crs(proving_key->circuit_size + 1); - verification_key = - plonk::stdlib::types::Composer::compute_verification_key_base(proving_key, crs_factory->get_verifier_crs()); + verification_key = Composer::compute_verification_key_base(proving_key, crs_factory->get_verifier_crs()); } void init_verification_key(std::shared_ptr const& crs, @@ -65,7 +65,7 @@ void init_verification_key(std::shared_ptr(std::move(vk_data), crs); } -stdlib::types::Prover new_join_split_prover(join_split_tx const& tx, bool mock) +Prover new_join_split_prover(join_split_tx const& tx, bool mock) { Composer composer(proving_key, nullptr); join_split_circuit(composer, tx); @@ -90,16 +90,10 @@ stdlib::types::Prover new_join_split_prover(join_split_tx const& tx, bool mock) bool verify_proof(plonk::proof const& proof) { - plonk::stdlib::types::Verifier verifier(verification_key, - Composer::create_manifest(verification_key->num_public_inputs)); + Verifier verifier(verification_key, Composer::create_manifest(verification_key->num_public_inputs)); -#ifdef USE_TURBO - std::unique_ptr> kate_commitment_scheme = - std::make_unique>(); -#else std::unique_ptr> kate_commitment_scheme = std::make_unique>(); -#endif verifier.commitment_scheme = std::move(kate_commitment_scheme); return verifier.verify_proof(proof); diff --git a/cpp/src/barretenberg/join_split_example/proofs/join_split/join_split.hpp b/cpp/src/barretenberg/join_split_example/proofs/join_split/join_split.hpp index 6c9b4ac5ab..a6c103a4a0 100644 --- a/cpp/src/barretenberg/join_split_example/proofs/join_split/join_split.hpp +++ b/cpp/src/barretenberg/join_split_example/proofs/join_split/join_split.hpp @@ -1,14 +1,13 @@ #pragma once #include "join_split_tx.hpp" #include "barretenberg/srs/reference_string/mem_reference_string.hpp" -#include "barretenberg/stdlib/types/types.hpp" +#include "barretenberg/join_split_example/types.hpp" namespace join_split_example { namespace proofs { namespace join_split { using namespace proof_system::plonk::stdlib::merkle_tree; -using namespace proof_system::plonk::stdlib::types; void init_proving_key(std::shared_ptr const& crs_factory, bool mock); @@ -22,7 +21,7 @@ void init_verification_key(std::unique_ptr void init_verification_key(std::shared_ptr const& crs, plonk::verification_key_data&& vk_data); -stdlib::types::Prover new_join_split_prover(join_split_tx const& tx, bool mock); +Prover new_join_split_prover(join_split_tx const& tx, bool mock); bool verify_proof(plonk::proof const& proof); diff --git a/cpp/src/barretenberg/join_split_example/proofs/join_split/join_split.test.cpp b/cpp/src/barretenberg/join_split_example/proofs/join_split/join_split.test.cpp index c729f91434..25b802cd62 100644 --- a/cpp/src/barretenberg/join_split_example/proofs/join_split/join_split.test.cpp +++ b/cpp/src/barretenberg/join_split_example/proofs/join_split/join_split.test.cpp @@ -1,3 +1,5 @@ +#include + #include "../../constants.hpp" #include "../inner_proof_data/inner_proof_data.hpp" #include "index.hpp" @@ -5,9 +7,9 @@ #include "join_split_circuit.hpp" #include "barretenberg/common/streams.hpp" #include "barretenberg/common/test.hpp" -#include #include "barretenberg/plonk/proof_system/proving_key/serialize.hpp" #include "barretenberg/stdlib/merkle_tree/index.hpp" +#include "barretenberg/join_split_example/types.hpp" namespace join_split_example::proofs::join_split { @@ -122,7 +124,7 @@ constexpr bool CIRCUIT_CHANGE_EXPECTED = false; #endif using namespace barretenberg; -using namespace proof_system::plonk::stdlib::types; +using namespace proof_system::plonk::stdlib; using namespace proof_system::plonk::stdlib::merkle_tree; using namespace join_split_example::proofs::notes::native; using key_pair = join_split_example::fixtures::grumpkin_key_pair; @@ -800,17 +802,12 @@ TEST_F(join_split_tests, test_0_input_notes_and_detect_circuit_change) EXPECT_TRUE(result.valid); -// The below part detects any changes in the join-split circuit -#ifdef USE_TURBO - constexpr uint32_t CIRCUIT_GATE_COUNT = 59175; - constexpr uint32_t GATES_NEXT_POWER_OF_TWO = 65536; - const uint256_t VK_HASH("095cbe8f1b09690713d5161708b5ea77119575884e3cfab14f7364b9f1ba7faa"); -#else + // The below part detects any changes in the join-split circuit + constexpr uint32_t CIRCUIT_GATE_COUNT = 185573; constexpr uint32_t GATES_NEXT_POWER_OF_TWO = 524288; - const uint256_t VK_HASH("21389d5392ee23ffc96984689150b63d62113678b1ba127346a0ec72df842354"); + const uint256_t VK_HASH("13eb88883e80efb9bf306af2962cd1a49e9fa1b0bfb2d4b563b95217a17bcc74"); -#endif auto number_of_gates_js = result.number_of_gates; auto vk_hash_js = get_verification_key()->sha256_hash(); @@ -2623,11 +2620,8 @@ TEST_F(join_split_tests, serialized_proving_key_size) { uint8_t* ptr; auto len = join_split__get_new_proving_key_data(&ptr); -#ifdef USE_TURBO - EXPECT_LE(len, 2 * 170 * 1024 * 1024); -#else + EXPECT_LE(len, 2315258552); -#endif } } // namespace join_split_example::proofs::join_split diff --git a/cpp/src/barretenberg/join_split_example/proofs/join_split/join_split_circuit.cpp b/cpp/src/barretenberg/join_split_example/proofs/join_split/join_split_circuit.cpp index a0061cde88..c1ea2af1da 100644 --- a/cpp/src/barretenberg/join_split_example/proofs/join_split/join_split_circuit.cpp +++ b/cpp/src/barretenberg/join_split_example/proofs/join_split/join_split_circuit.cpp @@ -6,6 +6,7 @@ #include "../notes/circuit/claim/claim_note.hpp" #include "verify_signature.hpp" #include "barretenberg/stdlib/merkle_tree/membership.hpp" +#include "barretenberg/join_split_example/types.hpp" namespace join_split_example { namespace proofs { @@ -22,7 +23,7 @@ using namespace proof_system::plonk::stdlib::merkle_tree; */ field_ct process_input_note(field_ct const& account_private_key, field_ct const& merkle_root, - merkle_tree::hash_path const& hash_path, + hash_path_ct const& hash_path, suint_ct const& index, value::value_note const& note, bool_ct is_propagated, @@ -31,7 +32,7 @@ field_ct process_input_note(field_ct const& account_private_key, const bool_ct valid_value = note.value == 0 || is_note_in_use; valid_value.assert_equal(true, "padding note non zero"); - const bool_ct exists = merkle_tree::check_membership( + const bool_ct exists = proof_system::plonk::stdlib::merkle_tree::check_membership( merkle_root, hash_path, note.commitment, index.value.decompose_into_bits(DATA_TREE_DEPTH)); const bool_ct valid = exists || is_propagated || !is_note_in_use; valid.assert_equal(true, "input note not a member"); @@ -216,10 +217,10 @@ join_split_outputs join_split_circuit_component(join_split_inputs const& inputs) const auto account_alias_hash = inputs.alias_hash; const auto account_note_data = account::account_note(account_alias_hash.value, account_public_key, signer); const bool_ct signing_key_exists = - merkle_tree::check_membership(inputs.merkle_root, - inputs.account_note_path, - account_note_data.commitment, - inputs.account_note_index.value.decompose_into_bits(DATA_TREE_DEPTH)); + stdlib::merkle_tree::check_membership(inputs.merkle_root, + inputs.account_note_path, + account_note_data.commitment, + inputs.account_note_index.value.decompose_into_bits(DATA_TREE_DEPTH)); (signing_key_exists || !inputs.account_required).assert_equal(true, "account check_membership failed"); } @@ -287,8 +288,8 @@ void join_split_circuit(Composer& composer, join_split_tx const& tx) .signing_pub_key = stdlib::create_point_witness(composer, tx.signing_pub_key), .signature = stdlib::schnorr::convert_signature(&composer, tx.signature), .merkle_root = witness_ct(&composer, tx.old_data_root), - .input_path1 = merkle_tree::create_witness_hash_path(composer, tx.input_path[0]), - .input_path2 = merkle_tree::create_witness_hash_path(composer, tx.input_path[1]), + .input_path1 = stdlib::merkle_tree::create_witness_hash_path(composer, tx.input_path[0]), + .input_path2 = stdlib::merkle_tree::create_witness_hash_path(composer, tx.input_path[1]), .account_note_index = suint_ct(witness_ct(&composer, tx.account_note_index), DATA_TREE_DEPTH, "account_note_index"), .account_note_path = merkle_tree::create_witness_hash_path(composer, tx.account_note_path), diff --git a/cpp/src/barretenberg/join_split_example/proofs/join_split/join_split_circuit.hpp b/cpp/src/barretenberg/join_split_example/proofs/join_split/join_split_circuit.hpp index b8821989b4..bde45a710c 100644 --- a/cpp/src/barretenberg/join_split_example/proofs/join_split/join_split_circuit.hpp +++ b/cpp/src/barretenberg/join_split_example/proofs/join_split/join_split_circuit.hpp @@ -3,15 +3,14 @@ #include "../notes/circuit/value/witness_data.hpp" #include "../notes/circuit/claim/witness_data.hpp" #include "barretenberg/crypto/schnorr/schnorr.hpp" -#include "barretenberg/stdlib/types/types.hpp" +#include "barretenberg/join_split_example/types.hpp" namespace join_split_example { namespace proofs { namespace join_split { -using namespace proof_system::plonk::stdlib::types; - struct join_split_inputs { + field_ct proof_id; suint_ct public_value; field_ct public_owner; @@ -25,12 +24,12 @@ struct join_split_inputs { notes::circuit::value::witness_data output_note2; notes::circuit::claim::partial_claim_note_witness_data partial_claim_note; point_ct signing_pub_key; - stdlib::schnorr::signature_bits signature; + schnorr::signature_bits signature; field_ct merkle_root; - merkle_tree::hash_path input_path1; - merkle_tree::hash_path input_path2; + hash_path_ct input_path1; + hash_path_ct input_path2; suint_ct account_note_index; - merkle_tree::hash_path account_note_path; + hash_path_ct account_note_path; field_ct account_private_key; suint_ct alias_hash; bool_ct account_required; diff --git a/cpp/src/barretenberg/join_split_example/proofs/join_split/join_split_tx.hpp b/cpp/src/barretenberg/join_split_example/proofs/join_split/join_split_tx.hpp index 4dae8f30e3..fe309b8dac 100644 --- a/cpp/src/barretenberg/join_split_example/proofs/join_split/join_split_tx.hpp +++ b/cpp/src/barretenberg/join_split_example/proofs/join_split/join_split_tx.hpp @@ -3,14 +3,12 @@ #include "../notes/native/value/value_note.hpp" #include "barretenberg/crypto/schnorr/schnorr.hpp" #include "barretenberg/stdlib/merkle_tree/hash_path.hpp" -#include "barretenberg/stdlib/types/types.hpp" +#include "barretenberg/join_split_example/types.hpp" namespace join_split_example { namespace proofs { namespace join_split { -using namespace proof_system::plonk::stdlib::types; - struct join_split_tx { uint32_t proof_id; uint256_t public_value; @@ -19,7 +17,7 @@ struct join_split_tx { uint32_t num_input_notes; std::array input_index; barretenberg::fr old_data_root; - std::array input_path; + std::array input_path; std::array input_note; std::array output_note; @@ -29,7 +27,7 @@ struct join_split_tx { barretenberg::fr alias_hash; bool account_required; uint32_t account_note_index; - merkle_tree::fr_hash_path account_note_path; + proof_system::plonk::stdlib::merkle_tree::fr_hash_path account_note_path; grumpkin::g1::affine_element signing_pub_key; barretenberg::fr backward_link; // 0: no link, otherwise: any commitment. diff --git a/cpp/src/barretenberg/join_split_example/proofs/mock/mock_circuit.test.cpp b/cpp/src/barretenberg/join_split_example/proofs/mock/mock_circuit.test.cpp index 16004e9787..6960962095 100644 --- a/cpp/src/barretenberg/join_split_example/proofs/mock/mock_circuit.test.cpp +++ b/cpp/src/barretenberg/join_split_example/proofs/mock/mock_circuit.test.cpp @@ -1,9 +1,9 @@ #include "mock_circuit.hpp" #include "../join_split/join_split_tx.hpp" #include "barretenberg/common/test.hpp" -#include "barretenberg/stdlib/types/types.hpp" +#include "barretenberg/join_split_example/types.hpp" -using namespace proof_system::plonk::stdlib::types; +using namespace proof_system::plonk::stdlib; namespace rollup { namespace proofs { diff --git a/cpp/src/barretenberg/join_split_example/proofs/notes/circuit/account/account_note.hpp b/cpp/src/barretenberg/join_split_example/proofs/notes/circuit/account/account_note.hpp index 334b2edfc3..30390eb103 100644 --- a/cpp/src/barretenberg/join_split_example/proofs/notes/circuit/account/account_note.hpp +++ b/cpp/src/barretenberg/join_split_example/proofs/notes/circuit/account/account_note.hpp @@ -1,6 +1,7 @@ #pragma once -#include "barretenberg/stdlib/types/types.hpp" +#include "barretenberg/join_split_example/types.hpp" #include "commit.hpp" +#include "barretenberg/join_split_example/types.hpp" namespace join_split_example { namespace proofs { @@ -8,8 +9,6 @@ namespace notes { namespace circuit { namespace account { -using namespace proof_system::plonk::stdlib::types; - struct account_note { field_ct account_alias_hash; point_ct account_public_key; diff --git a/cpp/src/barretenberg/join_split_example/proofs/notes/circuit/account/commit.hpp b/cpp/src/barretenberg/join_split_example/proofs/notes/circuit/account/commit.hpp index 497644a951..dbe568197a 100644 --- a/cpp/src/barretenberg/join_split_example/proofs/notes/circuit/account/commit.hpp +++ b/cpp/src/barretenberg/join_split_example/proofs/notes/circuit/account/commit.hpp @@ -1,6 +1,8 @@ #pragma once -#include "barretenberg/stdlib/types/types.hpp" +#include "barretenberg/stdlib/commitment/pedersen/pedersen.hpp" +#include "barretenberg/stdlib/primitives/point/point.hpp" #include "../../constants.hpp" +#include "barretenberg/join_split_example/types.hpp" namespace join_split_example { namespace proofs { @@ -8,7 +10,7 @@ namespace notes { namespace circuit { namespace account { -using namespace proof_system::plonk::stdlib::types; +using namespace proof_system::plonk::stdlib; inline auto commit(field_ct const& account_alias_hash, point_ct const& account_public_key, diff --git a/cpp/src/barretenberg/join_split_example/proofs/notes/circuit/asset_id.cpp b/cpp/src/barretenberg/join_split_example/proofs/notes/circuit/asset_id.cpp index 5163231bc5..6a7a559b70 100644 --- a/cpp/src/barretenberg/join_split_example/proofs/notes/circuit/asset_id.cpp +++ b/cpp/src/barretenberg/join_split_example/proofs/notes/circuit/asset_id.cpp @@ -1,9 +1,9 @@ -#include "barretenberg/stdlib/types/types.hpp" +#include "barretenberg/join_split_example/types.hpp" #include "../constants.hpp" namespace join_split_example::proofs::notes::circuit { -using namespace proof_system::plonk::stdlib::types; +using namespace proof_system::plonk::stdlib; std::pair deflag_asset_id(suint_ct const& asset_id) { diff --git a/cpp/src/barretenberg/join_split_example/proofs/notes/circuit/asset_id.hpp b/cpp/src/barretenberg/join_split_example/proofs/notes/circuit/asset_id.hpp index 3c67f16695..89e244f39b 100644 --- a/cpp/src/barretenberg/join_split_example/proofs/notes/circuit/asset_id.hpp +++ b/cpp/src/barretenberg/join_split_example/proofs/notes/circuit/asset_id.hpp @@ -1,9 +1,9 @@ #pragma once -#include "barretenberg/stdlib/types/types.hpp" +#include "barretenberg/join_split_example/types.hpp" namespace join_split_example::proofs::notes::circuit { -using namespace proof_system::plonk::stdlib::types; +using namespace proof_system::plonk::stdlib; std::pair deflag_asset_id(suint_ct const& asset_id); diff --git a/cpp/src/barretenberg/join_split_example/proofs/notes/circuit/bridge_call_data.hpp b/cpp/src/barretenberg/join_split_example/proofs/notes/circuit/bridge_call_data.hpp index f5b1bbd319..f8ea941167 100644 --- a/cpp/src/barretenberg/join_split_example/proofs/notes/circuit/bridge_call_data.hpp +++ b/cpp/src/barretenberg/join_split_example/proofs/notes/circuit/bridge_call_data.hpp @@ -1,5 +1,5 @@ #pragma once -#include "barretenberg/stdlib/types/types.hpp" +#include "barretenberg/join_split_example/types.hpp" #include "../native/bridge_call_data.hpp" #include "./asset_id.hpp" #include "../constants.hpp" @@ -9,7 +9,7 @@ namespace proofs { namespace notes { namespace circuit { -using namespace proof_system::plonk::stdlib::types; +using namespace proof_system::plonk::stdlib; constexpr uint32_t input_asset_id_a_shift = DEFI_BRIDGE_ADDRESS_ID_LEN; constexpr uint32_t input_asset_id_b_shift = input_asset_id_a_shift + DEFI_BRIDGE_INPUT_A_ASSET_ID_LEN; diff --git a/cpp/src/barretenberg/join_split_example/proofs/notes/circuit/claim/claim_note.hpp b/cpp/src/barretenberg/join_split_example/proofs/notes/circuit/claim/claim_note.hpp index 8021265a43..8a23ff9f93 100644 --- a/cpp/src/barretenberg/join_split_example/proofs/notes/circuit/claim/claim_note.hpp +++ b/cpp/src/barretenberg/join_split_example/proofs/notes/circuit/claim/claim_note.hpp @@ -1,5 +1,6 @@ #pragma once -#include "barretenberg/stdlib/types/types.hpp" +#include "barretenberg/join_split_example/types.hpp" + #include "../bridge_call_data.hpp" #include "witness_data.hpp" #include "../value/create_partial_commitment.hpp" @@ -12,7 +13,7 @@ namespace notes { namespace circuit { namespace claim { -using namespace proof_system::plonk::stdlib::types; +using namespace proof_system::plonk::stdlib; struct partial_claim_note { suint_ct deposit_value; diff --git a/cpp/src/barretenberg/join_split_example/proofs/notes/circuit/claim/complete_partial_commitment.hpp b/cpp/src/barretenberg/join_split_example/proofs/notes/circuit/claim/complete_partial_commitment.hpp index 9b1010ab58..fb20d7fef9 100644 --- a/cpp/src/barretenberg/join_split_example/proofs/notes/circuit/claim/complete_partial_commitment.hpp +++ b/cpp/src/barretenberg/join_split_example/proofs/notes/circuit/claim/complete_partial_commitment.hpp @@ -1,6 +1,7 @@ #pragma once -#include "barretenberg/stdlib/types/types.hpp" +#include "barretenberg/join_split_example/types.hpp" #include "../../constants.hpp" +#include "barretenberg/stdlib/commitment/pedersen/pedersen.hpp" namespace join_split_example { namespace proofs { @@ -8,7 +9,7 @@ namespace notes { namespace circuit { namespace claim { -using namespace proof_system::plonk::stdlib::types; +using namespace proof_system::plonk::stdlib; inline auto complete_partial_commitment(field_ct const& partial_commitment, field_ct const& interaction_nonce, diff --git a/cpp/src/barretenberg/join_split_example/proofs/notes/circuit/claim/compute_nullifier.hpp b/cpp/src/barretenberg/join_split_example/proofs/notes/circuit/claim/compute_nullifier.hpp index 304f9f6205..2ffb181bb2 100644 --- a/cpp/src/barretenberg/join_split_example/proofs/notes/circuit/claim/compute_nullifier.hpp +++ b/cpp/src/barretenberg/join_split_example/proofs/notes/circuit/claim/compute_nullifier.hpp @@ -1,5 +1,6 @@ #pragma once -#include "barretenberg/stdlib/types/types.hpp" + +#include "barretenberg/join_split_example/types.hpp" #include "barretenberg/stdlib/hash/pedersen/pedersen.hpp" #include "../../constants.hpp" @@ -9,8 +10,6 @@ namespace notes { namespace circuit { namespace claim { -using namespace proof_system::plonk::stdlib::types; - inline field_ct compute_nullifier(field_ct const& note_commitment) { return pedersen_commitment::compress(std::vector{ note_commitment }, diff --git a/cpp/src/barretenberg/join_split_example/proofs/notes/circuit/claim/create_partial_commitment.hpp b/cpp/src/barretenberg/join_split_example/proofs/notes/circuit/claim/create_partial_commitment.hpp index 30eb7adb7a..36c37a8e78 100644 --- a/cpp/src/barretenberg/join_split_example/proofs/notes/circuit/claim/create_partial_commitment.hpp +++ b/cpp/src/barretenberg/join_split_example/proofs/notes/circuit/claim/create_partial_commitment.hpp @@ -1,5 +1,5 @@ #pragma once -#include "barretenberg/stdlib/types/types.hpp" +#include "barretenberg/join_split_example/types.hpp" #include "barretenberg/stdlib/hash/pedersen/pedersen.hpp" #include "../../constants.hpp" @@ -9,7 +9,7 @@ namespace notes { namespace circuit { namespace claim { -using namespace proof_system::plonk::stdlib::types; +using namespace proof_system::plonk::stdlib; inline auto create_partial_commitment(field_ct const& deposit_value, field_ct const& bridge_call_data, diff --git a/cpp/src/barretenberg/join_split_example/proofs/notes/circuit/claim/witness_data.hpp b/cpp/src/barretenberg/join_split_example/proofs/notes/circuit/claim/witness_data.hpp index b49bddd3bb..e5fc35fb39 100644 --- a/cpp/src/barretenberg/join_split_example/proofs/notes/circuit/claim/witness_data.hpp +++ b/cpp/src/barretenberg/join_split_example/proofs/notes/circuit/claim/witness_data.hpp @@ -1,5 +1,6 @@ #pragma once -#include "barretenberg/stdlib/types/types.hpp" + +#include "barretenberg/join_split_example/types.hpp" #include "../../native/claim/claim_note.hpp" #include "../../native/claim/claim_note_tx_data.hpp" #include "../../constants.hpp" @@ -11,7 +12,7 @@ namespace notes { namespace circuit { namespace claim { -using namespace proof_system::plonk::stdlib::types; +using namespace proof_system::plonk::stdlib; /** * Convert native claim note data into circuit witness data. diff --git a/cpp/src/barretenberg/join_split_example/proofs/notes/circuit/value/complete_partial_commitment.hpp b/cpp/src/barretenberg/join_split_example/proofs/notes/circuit/value/complete_partial_commitment.hpp index e5bc84b994..1ec344d17a 100644 --- a/cpp/src/barretenberg/join_split_example/proofs/notes/circuit/value/complete_partial_commitment.hpp +++ b/cpp/src/barretenberg/join_split_example/proofs/notes/circuit/value/complete_partial_commitment.hpp @@ -1,15 +1,15 @@ #pragma once -#include "barretenberg/stdlib/types/types.hpp" + +#include "barretenberg/join_split_example/types.hpp" #include "barretenberg/stdlib/commitment/pedersen/pedersen.hpp" #include "../../constants.hpp" - namespace join_split_example { namespace proofs { namespace notes { namespace circuit { namespace value { -using namespace proof_system::plonk::stdlib::types; +using namespace proof_system::plonk::stdlib; inline auto complete_partial_commitment(field_ct const& value_note_partial_commitment, suint_ct const& value, diff --git a/cpp/src/barretenberg/join_split_example/proofs/notes/circuit/value/compute_nullifier.cpp b/cpp/src/barretenberg/join_split_example/proofs/notes/circuit/value/compute_nullifier.cpp index 30dafca6b6..6f73dd418f 100644 --- a/cpp/src/barretenberg/join_split_example/proofs/notes/circuit/value/compute_nullifier.cpp +++ b/cpp/src/barretenberg/join_split_example/proofs/notes/circuit/value/compute_nullifier.cpp @@ -1,6 +1,6 @@ #include "compute_nullifier.hpp" #include "../../constants.hpp" -#include "barretenberg/stdlib/types/types.hpp" +#include "barretenberg/join_split_example/types.hpp" namespace join_split_example { namespace proofs { @@ -8,7 +8,7 @@ namespace notes { namespace circuit { using namespace barretenberg; -using namespace proof_system::plonk::stdlib::types; +using namespace proof_system::plonk::stdlib; field_ct compute_nullifier(field_ct const& note_commitment, field_ct const& account_private_key, diff --git a/cpp/src/barretenberg/join_split_example/proofs/notes/circuit/value/compute_nullifier.hpp b/cpp/src/barretenberg/join_split_example/proofs/notes/circuit/value/compute_nullifier.hpp index 3271035abb..85a14dd416 100644 --- a/cpp/src/barretenberg/join_split_example/proofs/notes/circuit/value/compute_nullifier.hpp +++ b/cpp/src/barretenberg/join_split_example/proofs/notes/circuit/value/compute_nullifier.hpp @@ -1,14 +1,11 @@ #pragma once -#include "barretenberg/stdlib/types/types.hpp" +#include "barretenberg/join_split_example/types.hpp" namespace join_split_example { namespace proofs { namespace notes { namespace circuit { -using namespace barretenberg; -using namespace proof_system::plonk::stdlib::types; - field_ct compute_nullifier(field_ct const& note_commitment, field_ct const& account_private_key, bool_ct const& is_note_in_use); diff --git a/cpp/src/barretenberg/join_split_example/proofs/notes/circuit/value/compute_nullifier.test.cpp b/cpp/src/barretenberg/join_split_example/proofs/notes/circuit/value/compute_nullifier.test.cpp index 063ef93edd..88198aa7c5 100644 --- a/cpp/src/barretenberg/join_split_example/proofs/notes/circuit/value/compute_nullifier.test.cpp +++ b/cpp/src/barretenberg/join_split_example/proofs/notes/circuit/value/compute_nullifier.test.cpp @@ -4,10 +4,10 @@ #include "./value_note.hpp" #include "../../native/value/compute_nullifier.hpp" #include "../../native/value/value_note.hpp" -#include "barretenberg/stdlib/types/types.hpp" +#include "barretenberg/join_split_example/types.hpp" +namespace join_split_example { using namespace join_split_example::proofs::notes; -using namespace proof_system::plonk::stdlib::types; TEST(compute_nullifier_circuit, native_consistency) { @@ -18,7 +18,6 @@ TEST(compute_nullifier_circuit, native_consistency) native::value::value_note{ 100, 0, 0, user.owner.public_key, user.note_secret, 0, fr::random_element() }; auto native_commitment = native_input_note.commit(); auto native_nullifier = native::compute_nullifier(native_commitment, priv_key, true); - Composer composer; auto circuit_witness_data = circuit::value::witness_data(composer, native_input_note); auto circuit_input_note = circuit::value::value_note(circuit_witness_data); @@ -27,3 +26,4 @@ TEST(compute_nullifier_circuit, native_consistency) EXPECT_EQ(circuit_nullifier.get_value(), native_nullifier); } +} // namespace join_split_example \ No newline at end of file diff --git a/cpp/src/barretenberg/join_split_example/proofs/notes/circuit/value/create_partial_commitment.hpp b/cpp/src/barretenberg/join_split_example/proofs/notes/circuit/value/create_partial_commitment.hpp index f01324ee34..ac6104c65a 100644 --- a/cpp/src/barretenberg/join_split_example/proofs/notes/circuit/value/create_partial_commitment.hpp +++ b/cpp/src/barretenberg/join_split_example/proofs/notes/circuit/value/create_partial_commitment.hpp @@ -1,6 +1,6 @@ #pragma once -#include "barretenberg/stdlib/types/types.hpp" -#include "barretenberg/stdlib/hash/pedersen/pedersen.hpp" +#include "barretenberg/stdlib/commitment/pedersen/pedersen.hpp" +#include "barretenberg/join_split_example/types.hpp" #include "../../constants.hpp" namespace join_split_example { @@ -9,8 +9,6 @@ namespace notes { namespace circuit { namespace value { -using namespace proof_system::plonk::stdlib::types; - inline auto create_partial_commitment(field_ct const& secret, point_ct const& owner, bool_ct const& account_required, diff --git a/cpp/src/barretenberg/join_split_example/proofs/notes/circuit/value/value_note.hpp b/cpp/src/barretenberg/join_split_example/proofs/notes/circuit/value/value_note.hpp index a4f8cc04c8..9f2d47a6c6 100644 --- a/cpp/src/barretenberg/join_split_example/proofs/notes/circuit/value/value_note.hpp +++ b/cpp/src/barretenberg/join_split_example/proofs/notes/circuit/value/value_note.hpp @@ -1,5 +1,5 @@ #pragma once -#include "barretenberg/stdlib/types/types.hpp" +#include "barretenberg/join_split_example/types.hpp" #include "witness_data.hpp" #include "commit.hpp" @@ -9,7 +9,7 @@ namespace notes { namespace circuit { namespace value { -using namespace proof_system::plonk::stdlib::types; +using namespace proof_system::plonk::stdlib; struct value_note { point_ct owner; diff --git a/cpp/src/barretenberg/join_split_example/proofs/notes/circuit/value/value_note.test.cpp b/cpp/src/barretenberg/join_split_example/proofs/notes/circuit/value/value_note.test.cpp index e243b35fb8..55b65c9949 100644 --- a/cpp/src/barretenberg/join_split_example/proofs/notes/circuit/value/value_note.test.cpp +++ b/cpp/src/barretenberg/join_split_example/proofs/notes/circuit/value/value_note.test.cpp @@ -2,13 +2,14 @@ #include "../../../../fixtures/user_context.hpp" #include "../../native/value/value_note.hpp" #include "../../constants.hpp" +#include "barretenberg/join_split_example/types.hpp" #include +namespace join_split_example { using namespace barretenberg; -using namespace proof_system::plonk::stdlib::types; +using namespace proof_system::plonk::stdlib; using namespace join_split_example::proofs::notes; using namespace join_split_example::proofs::notes::circuit::value; - TEST(value_note, commits) { auto user = join_split_example::fixtures::create_user_context(); @@ -106,4 +107,5 @@ TEST(value_note, commit_with_oversized_asset_id_fails) bool proof_result = verifier.verify_proof(proof); EXPECT_EQ(proof_result, false); -} \ No newline at end of file +} +} // namespace join_split_example \ No newline at end of file diff --git a/cpp/src/barretenberg/join_split_example/proofs/notes/circuit/value/witness_data.hpp b/cpp/src/barretenberg/join_split_example/proofs/notes/circuit/value/witness_data.hpp index ff0cbc0cc2..367f0e704e 100644 --- a/cpp/src/barretenberg/join_split_example/proofs/notes/circuit/value/witness_data.hpp +++ b/cpp/src/barretenberg/join_split_example/proofs/notes/circuit/value/witness_data.hpp @@ -1,7 +1,6 @@ #pragma once -#include "barretenberg/stdlib/types/types.hpp" #include "../../native/value/value_note.hpp" -#include "../../constants.hpp" +#include "barretenberg/join_split_example/types.hpp" namespace join_split_example { namespace proofs { @@ -9,7 +8,7 @@ namespace notes { namespace circuit { namespace value { -using namespace proof_system::plonk::stdlib::types; +using namespace proof_system::plonk::stdlib; struct witness_data { point_ct owner; diff --git a/cpp/src/barretenberg/join_split_example/types.hpp b/cpp/src/barretenberg/join_split_example/types.hpp new file mode 100644 index 0000000000..65b6cca423 --- /dev/null +++ b/cpp/src/barretenberg/join_split_example/types.hpp @@ -0,0 +1,50 @@ +#pragma once + +#include "barretenberg/plonk/composer/standard_composer.hpp" +#include "barretenberg/plonk/composer/turbo_composer.hpp" +#include "barretenberg/plonk/composer/ultra_composer.hpp" + +#include "barretenberg/plonk/proof_system/prover/prover.hpp" +#include "barretenberg/stdlib/primitives/bool/bool.hpp" +#include "barretenberg/stdlib/primitives/byte_array/byte_array.hpp" +#include "barretenberg/stdlib/primitives/uint/uint.hpp" +#include "barretenberg/stdlib/primitives/witness/witness.hpp" +#include "barretenberg/stdlib/commitment/pedersen/pedersen.hpp" +#include "barretenberg/stdlib/commitment/pedersen/pedersen_plookup.hpp" +#include "barretenberg/stdlib/merkle_tree/hash_path.hpp" +#include "barretenberg/stdlib/encryption/schnorr/schnorr.hpp" +#include "barretenberg/stdlib/primitives/curves/bn254.hpp" + +namespace join_split_example { + +using Composer = plonk::UltraComposer; + +using Prover = std::conditional_t< + std::same_as, + plonk::UltraProver, + std::conditional_t, plonk::TurboProver, plonk::Prover>>; + +using Verifier = std::conditional_t< + std::same_as, + plonk::UltraVerifier, + std::conditional_t, plonk::TurboVerifier, plonk::Verifier>>; + +using witness_ct = proof_system::plonk::stdlib::witness_t; +using public_witness_ct = proof_system::plonk::stdlib::public_witness_t; +using bool_ct = proof_system::plonk::stdlib::bool_t; +using byte_array_ct = proof_system::plonk::stdlib::byte_array; +using field_ct = proof_system::plonk::stdlib::field_t; +using suint_ct = proof_system::plonk::stdlib::safe_uint_t; +using uint32_ct = proof_system::plonk::stdlib::uint32; +using point_ct = proof_system::plonk::stdlib::point; +using pedersen_commitment = proof_system::plonk::stdlib::pedersen_commitment; +using group_ct = proof_system::plonk::stdlib::group; +using bn254 = proof_system::plonk::stdlib::bn254; + +using hash_path_ct = proof_system::plonk::stdlib::merkle_tree::hash_path; + +namespace schnorr { +using signature_bits = proof_system::plonk::stdlib::schnorr::signature_bits; +} // namespace schnorr + +} // namespace join_split_example \ No newline at end of file diff --git a/cpp/src/barretenberg/plonk/composer/ultra_composer.cpp b/cpp/src/barretenberg/plonk/composer/ultra_composer.cpp index 597b2ddea0..8782f6ab61 100644 --- a/cpp/src/barretenberg/plonk/composer/ultra_composer.cpp +++ b/cpp/src/barretenberg/plonk/composer/ultra_composer.cpp @@ -890,6 +890,50 @@ UltraToStandardProver UltraComposer::create_ultra_to_standard_prover() return output_state; } +/** + * @brief Uses slightly different settings from the UltraProver. + */ +UltraWithKeccakProver UltraComposer::create_ultra_with_keccak_prover() +{ + compute_proving_key(); + compute_witness(); + + UltraWithKeccakProver output_state(circuit_proving_key, create_manifest(public_inputs.size())); + + std::unique_ptr> permutation_widget = + std::make_unique>(circuit_proving_key.get()); + + std::unique_ptr> plookup_widget = + std::make_unique>(circuit_proving_key.get()); + + std::unique_ptr> arithmetic_widget = + std::make_unique>(circuit_proving_key.get()); + + std::unique_ptr> sort_widget = + std::make_unique>(circuit_proving_key.get()); + + std::unique_ptr> elliptic_widget = + std::make_unique>(circuit_proving_key.get()); + + std::unique_ptr> auxiliary_widget = + std::make_unique>(circuit_proving_key.get()); + + output_state.random_widgets.emplace_back(std::move(permutation_widget)); + output_state.random_widgets.emplace_back(std::move(plookup_widget)); + + output_state.transition_widgets.emplace_back(std::move(arithmetic_widget)); + output_state.transition_widgets.emplace_back(std::move(sort_widget)); + output_state.transition_widgets.emplace_back(std::move(elliptic_widget)); + output_state.transition_widgets.emplace_back(std::move(auxiliary_widget)); + + std::unique_ptr> kate_commitment_scheme = + std::make_unique>(); + + output_state.commitment_scheme = std::move(kate_commitment_scheme); + + return output_state; +} + UltraVerifier UltraComposer::create_verifier() { compute_verification_key(); @@ -918,6 +962,20 @@ UltraToStandardVerifier UltraComposer::create_ultra_to_standard_verifier() return output_state; } +UltraWithKeccakVerifier UltraComposer::create_ultra_with_keccak_verifier() +{ + compute_verification_key(); + + UltraWithKeccakVerifier output_state(circuit_verification_key, create_manifest(public_inputs.size())); + + std::unique_ptr> kate_commitment_scheme = + std::make_unique>(); + + output_state.commitment_scheme = std::move(kate_commitment_scheme); + + return output_state; +} + void UltraComposer::initialize_precomputed_table( const plookup::BasicTableId id, bool (*generator)(std::vector&, std ::vector&, std::vector&), diff --git a/cpp/src/barretenberg/plonk/composer/ultra_composer.hpp b/cpp/src/barretenberg/plonk/composer/ultra_composer.hpp index 6cdc969423..b3157639fd 100644 --- a/cpp/src/barretenberg/plonk/composer/ultra_composer.hpp +++ b/cpp/src/barretenberg/plonk/composer/ultra_composer.hpp @@ -160,6 +160,9 @@ class UltraComposer : public ComposerBase { UltraToStandardProver create_ultra_to_standard_prover(); UltraToStandardVerifier create_ultra_to_standard_verifier(); + UltraWithKeccakProver create_ultra_with_keccak_prover(); + UltraWithKeccakVerifier create_ultra_with_keccak_verifier(); + void create_add_gate(const add_triple& in) override; void create_big_add_gate(const add_quad& in, const bool use_next_gate_w_4 = false); diff --git a/cpp/src/barretenberg/plonk/composer/ultra_composer.test.cpp b/cpp/src/barretenberg/plonk/composer/ultra_composer.test.cpp index 8219f9183c..eef80fa56c 100644 --- a/cpp/src/barretenberg/plonk/composer/ultra_composer.test.cpp +++ b/cpp/src/barretenberg/plonk/composer/ultra_composer.test.cpp @@ -10,7 +10,7 @@ using namespace barretenberg; using namespace proof_system; -namespace proof_system::plonk { +namespace proof_system::plonk::test_ultra_composer { namespace { auto& engine = numeric::random::get_debug_engine(); @@ -27,7 +27,39 @@ std::vector add_variables(UltraComposer& composer, std::vector var } return res; } -TEST(ultra_composer, create_gates_from_plookup_accumulators) + +template class ultra_composer : public ::testing::Test { + public: + void prove_and_verify(UltraComposer& composer, bool expected_result) + { + if constexpr (T::use_keccak) { + auto prover = composer.create_ultra_with_keccak_prover(); + auto verifier = composer.create_ultra_with_keccak_verifier(); + auto proof = prover.construct_proof(); + bool verified = verifier.verify_proof(proof); + EXPECT_EQ(verified, expected_result); + } else { + auto prover = composer.create_prover(); + auto verifier = composer.create_verifier(); + auto proof = prover.construct_proof(); + bool verified = verifier.verify_proof(proof); + EXPECT_EQ(verified, expected_result); + } + }; +}; + +struct UseKeccak32Bytes { + static constexpr bool use_keccak = true; +}; + +struct UsePlookupPedersen16Bytes { + static constexpr bool use_keccak = false; +}; + +using BooleanTypes = ::testing::Types; +TYPED_TEST_SUITE(ultra_composer, BooleanTypes); + +TYPED_TEST(ultra_composer, create_gates_from_plookup_accumulators) { UltraComposer composer = UltraComposer(); @@ -106,16 +138,10 @@ TEST(ultra_composer, create_gates_from_plookup_accumulators) EXPECT_EQ(composer.get_variable(lookup_witnesses_hi[ColumnIdx::C3][i]), expected_y[i + num_lookups_lo]); } - auto prover = composer.create_prover(); - auto verifier = composer.create_verifier(); - auto proof = prover.construct_proof(); - - bool result = verifier.verify_proof(proof); - - EXPECT_EQ(result, true); + TestFixture::prove_and_verify(composer, /*expected_result=*/true); } -TEST(ultra_composer, test_no_lookup_proof) +TYPED_TEST(ultra_composer, test_no_lookup_proof) { UltraComposer composer = UltraComposer(); @@ -133,17 +159,10 @@ TEST(ultra_composer, test_no_lookup_proof) } } - auto prover = composer.create_prover(); - - auto verifier = composer.create_verifier(); - - auto proof = prover.construct_proof(); - - bool result = verifier.verify_proof(proof); // instance, prover.reference_string.SRS_T2); - EXPECT_EQ(result, true); + TestFixture::prove_and_verify(composer, /*expected_result=*/true); } -TEST(ultra_composer, test_elliptic_gate) +TYPED_TEST(ultra_composer, test_elliptic_gate) { typedef grumpkin::g1::affine_element affine_element; typedef grumpkin::g1::element element; @@ -180,17 +199,10 @@ TEST(ultra_composer, test_elliptic_gate) gate = ecc_add_gate{ x1, y1, x2, y2, x3, y3, beta.sqr(), -1 }; composer.create_ecc_add_gate(gate); - auto prover = composer.create_prover(); - - auto verifier = composer.create_verifier(); - - auto proof = prover.construct_proof(); - - bool result = verifier.verify_proof(proof); // instance, prover.reference_string.SRS_T2); - EXPECT_EQ(result, true); + TestFixture::prove_and_verify(composer, /*expected_result=*/true); } -TEST(ultra_composer, non_trivial_tag_permutation) +TYPED_TEST(ultra_composer, non_trivial_tag_permutation) { UltraComposer composer = UltraComposer(); fr a = fr::random_element(); @@ -215,15 +227,11 @@ TEST(ultra_composer, non_trivial_tag_permutation) // composer.create_add_gate({ a_idx, b_idx, composer.zero_idx, fr::one(), fr::neg_one(), fr::zero(), fr::zero() }); // composer.create_add_gate({ a_idx, b_idx, composer.zero_idx, fr::one(), fr::neg_one(), fr::zero(), fr::zero() }); // composer.create_add_gate({ a_idx, b_idx, composer.zero_idx, fr::one(), fr::neg_one(), fr::zero(), fr::zero() }); - auto prover = composer.create_prover(); - auto verifier = composer.create_verifier(); - - proof proof = prover.construct_proof(); - bool result = verifier.verify_proof(proof); // instance, prover.reference_string.SRS_T2); - EXPECT_EQ(result, true); + TestFixture::prove_and_verify(composer, /*expected_result=*/true); } -TEST(ultra_composer, non_trivial_tag_permutation_and_cycles) + +TYPED_TEST(ultra_composer, non_trivial_tag_permutation_and_cycles) { UltraComposer composer = UltraComposer(); fr a = fr::random_element(); @@ -257,17 +265,11 @@ TEST(ultra_composer, non_trivial_tag_permutation_and_cycles) // composer.create_add_gate({ a_idx, b_idx, composer.zero_idx, fr::one(), fr::neg_one(), fr::zero(), fr::zero() }); // composer.create_add_gate({ a_idx, b_idx, composer.zero_idx, fr::one(), fr::neg_one(), fr::zero(), fr::zero() }); // composer.create_add_gate({ a_idx, b_idx, composer.zero_idx, fr::one(), fr::neg_one(), fr::zero(), fr::zero() }); - auto prover = composer.create_prover(); - - auto verifier = composer.create_verifier(); - - proof proof = prover.construct_proof(); - bool result = verifier.verify_proof(proof); // instance, prover.reference_string.SRS_T2); - EXPECT_EQ(result, true); + TestFixture::prove_and_verify(composer, /*expected_result=*/true); } -TEST(ultra_composer, bad_tag_permutation) +TYPED_TEST(ultra_composer, bad_tag_permutation) { UltraComposer composer = UltraComposer(); fr a = fr::random_element(); @@ -288,16 +290,12 @@ TEST(ultra_composer, bad_tag_permutation) composer.assign_tag(b_idx, 1); composer.assign_tag(c_idx, 2); composer.assign_tag(d_idx, 2); - auto prover = composer.create_prover(); - auto verifier = composer.create_verifier(); - proof proof = prover.construct_proof(); - bool result = verifier.verify_proof(proof); // instance, prover.reference_string.SRS_T2); - EXPECT_EQ(result, false); + TestFixture::prove_and_verify(composer, /*expected_result=*/false); } // same as above but with turbocomposer to check reason of failue is really tag mismatch -TEST(ultra_composer, bad_tag_turbo_permutation) +TYPED_TEST(ultra_composer, bad_tag_turbo_permutation) { UltraComposer composer = UltraComposer(); fr a = fr::random_element(); @@ -317,13 +315,10 @@ TEST(ultra_composer, bad_tag_turbo_permutation) auto prover = composer.create_prover(); auto verifier = composer.create_verifier(); - proof proof = prover.construct_proof(); - - bool result = verifier.verify_proof(proof); // instance, prover.reference_string.SRS_T2); - EXPECT_EQ(result, true); + TestFixture::prove_and_verify(composer, /*expected_result=*/true); } -TEST(ultra_composer, sort_widget) +TYPED_TEST(ultra_composer, sort_widget) { UltraComposer composer = UltraComposer(); fr a = fr::one(); @@ -336,16 +331,11 @@ TEST(ultra_composer, sort_widget) auto c_idx = composer.add_variable(c); auto d_idx = composer.add_variable(d); composer.create_sort_constraint({ a_idx, b_idx, c_idx, d_idx }); - auto prover = composer.create_prover(); - auto verifier = composer.create_verifier(); - - proof proof = prover.construct_proof(); - bool result = verifier.verify_proof(proof); // instance, prover.reference_string.SRS_T2); - EXPECT_EQ(result, true); + TestFixture::prove_and_verify(composer, /*expected_result=*/true); } -TEST(ultra_composer, sort_with_edges_gate) +TYPED_TEST(ultra_composer, sort_with_edges_gate) { fr a = fr::one(); @@ -368,13 +358,8 @@ TEST(ultra_composer, sort_with_edges_gate) auto g_idx = composer.add_variable(g); auto h_idx = composer.add_variable(h); composer.create_sort_constraint_with_edges({ a_idx, b_idx, c_idx, d_idx, e_idx, f_idx, g_idx, h_idx }, a, h); - auto prover = composer.create_prover(); - auto verifier = composer.create_verifier(); - proof proof = prover.construct_proof(); - - bool result = verifier.verify_proof(proof); // instance, prover.reference_string.SRS_T2); - EXPECT_EQ(result, true); + TestFixture::prove_and_verify(composer, /*expected_result=*/true); } { @@ -407,13 +392,8 @@ TEST(ultra_composer, sort_with_edges_gate) auto g_idx = composer.add_variable(g); auto h_idx = composer.add_variable(h); composer.create_sort_constraint_with_edges({ a_idx, b_idx, c_idx, d_idx, e_idx, f_idx, g_idx, h_idx }, b, h); - auto prover = composer.create_prover(); - auto verifier = composer.create_verifier(); - - proof proof = prover.construct_proof(); - bool result = verifier.verify_proof(proof); // instance, prover.reference_string.SRS_T2); - EXPECT_EQ(result, false); + TestFixture::prove_and_verify(composer, /*expected_result=*/false); } { UltraComposer composer = UltraComposer(); @@ -426,26 +406,16 @@ TEST(ultra_composer, sort_with_edges_gate) auto h_idx = composer.add_variable(h); auto b2_idx = composer.add_variable(fr(15)); composer.create_sort_constraint_with_edges({ a_idx, b2_idx, c_idx, d_idx, e_idx, f_idx, g_idx, h_idx }, b, h); - auto prover = composer.create_prover(); - auto verifier = composer.create_verifier(); - - proof proof = prover.construct_proof(); - bool result = verifier.verify_proof(proof); // instance, prover.reference_string.SRS_T2); - EXPECT_EQ(result, false); + TestFixture::prove_and_verify(composer, /*expected_result=*/false); } { UltraComposer composer = UltraComposer(); auto idx = add_variables(composer, { 1, 2, 5, 6, 7, 10, 11, 13, 16, 17, 20, 22, 22, 25, 26, 29, 29, 32, 32, 33, 35, 38, 39, 39, 42, 42, 43, 45 }); composer.create_sort_constraint_with_edges(idx, 1, 45); - auto prover = composer.create_prover(); - auto verifier = composer.create_verifier(); - proof proof = prover.construct_proof(); - - bool result = verifier.verify_proof(proof); // instance, prover.reference_string.SRS_T2); - EXPECT_EQ(result, true); + TestFixture::prove_and_verify(composer, /*expected_result=*/true); } { UltraComposer composer = UltraComposer(); @@ -453,16 +423,12 @@ TEST(ultra_composer, sort_with_edges_gate) 26, 29, 29, 32, 32, 33, 35, 38, 39, 39, 42, 42, 43, 45 }); composer.create_sort_constraint_with_edges(idx, 1, 29); - auto prover = composer.create_prover(); - auto verifier = composer.create_verifier(); - proof proof = prover.construct_proof(); - bool result = verifier.verify_proof(proof); // instance, prover.reference_string.SRS_T2); - EXPECT_EQ(result, false); + TestFixture::prove_and_verify(composer, /*expected_result=*/false); } } -TEST(ultra_composer, range_constraint) +TYPED_TEST(ultra_composer, range_constraint) { { UltraComposer composer = UltraComposer(); @@ -488,13 +454,8 @@ TEST(ultra_composer, range_constraint) } // auto ind = {a_idx,b_idx,c_idx,d_idx,e_idx,f_idx,g_idx,h_idx}; composer.create_dummy_constraints(indices); - auto prover = composer.create_prover(); - auto verifier = composer.create_verifier(); - proof proof = prover.construct_proof(); - - bool result = verifier.verify_proof(proof); - EXPECT_EQ(result, true); + TestFixture::prove_and_verify(composer, /*expected_result=*/true); } { UltraComposer composer = UltraComposer(); @@ -503,13 +464,8 @@ TEST(ultra_composer, range_constraint) composer.create_new_range_constraint(indices[i], 8); } composer.create_sort_constraint(indices); - auto prover = composer.create_prover(); - auto verifier = composer.create_verifier(); - - proof proof = prover.construct_proof(); - bool result = verifier.verify_proof(proof); - EXPECT_EQ(result, false); + TestFixture::prove_and_verify(composer, /*expected_result=*/false); } { UltraComposer composer = UltraComposer(); @@ -519,13 +475,8 @@ TEST(ultra_composer, range_constraint) composer.create_new_range_constraint(indices[i], 128); } composer.create_dummy_constraints(indices); - auto prover = composer.create_prover(); - auto verifier = composer.create_verifier(); - proof proof = prover.construct_proof(); - - bool result = verifier.verify_proof(proof); - EXPECT_EQ(result, true); + TestFixture::prove_and_verify(composer, /*expected_result=*/true); } { UltraComposer composer = UltraComposer(); @@ -551,17 +502,12 @@ TEST(ultra_composer, range_constraint) composer.create_new_range_constraint(indices[i], 79); } composer.create_dummy_constraints(indices); - auto prover = composer.create_prover(); - auto verifier = composer.create_verifier(); - - proof proof = prover.construct_proof(); - bool result = verifier.verify_proof(proof); - EXPECT_EQ(result, false); + TestFixture::prove_and_verify(composer, /*expected_result=*/false); } } -TEST(ultra_composer, range_with_gates) +TYPED_TEST(ultra_composer, range_with_gates) { UltraComposer composer = UltraComposer(); @@ -574,15 +520,11 @@ TEST(ultra_composer, range_with_gates) composer.create_add_gate({ idx[2], idx[3], composer.zero_idx, fr::one(), fr::one(), fr::zero(), -7 }); composer.create_add_gate({ idx[4], idx[5], composer.zero_idx, fr::one(), fr::one(), fr::zero(), -11 }); composer.create_add_gate({ idx[6], idx[7], composer.zero_idx, fr::one(), fr::one(), fr::zero(), -15 }); - auto prover = composer.create_prover(); - auto verifier = composer.create_verifier(); - proof proof = prover.construct_proof(); - bool result = verifier.verify_proof(proof); - EXPECT_EQ(result, true); + TestFixture::prove_and_verify(composer, /*expected_result=*/true); } -TEST(ultra_composer, range_with_gates_where_range_is_not_a_power_of_two) +TYPED_TEST(ultra_composer, range_with_gates_where_range_is_not_a_power_of_two) { UltraComposer composer = UltraComposer(); auto idx = add_variables(composer, { 1, 2, 3, 4, 5, 6, 7, 8 }); @@ -594,15 +536,11 @@ TEST(ultra_composer, range_with_gates_where_range_is_not_a_power_of_two) composer.create_add_gate({ idx[2], idx[3], composer.zero_idx, fr::one(), fr::one(), fr::zero(), -7 }); composer.create_add_gate({ idx[4], idx[5], composer.zero_idx, fr::one(), fr::one(), fr::zero(), -11 }); composer.create_add_gate({ idx[6], idx[7], composer.zero_idx, fr::one(), fr::one(), fr::zero(), -15 }); - auto prover = composer.create_prover(); - auto verifier = composer.create_verifier(); - proof proof = prover.construct_proof(); - bool result = verifier.verify_proof(proof); - EXPECT_EQ(result, true); + TestFixture::prove_and_verify(composer, /*expected_result=*/true); } -TEST(ultra_composer, sort_widget_complex) +TYPED_TEST(ultra_composer, sort_widget_complex) { { @@ -628,16 +566,11 @@ TEST(ultra_composer, sort_widget_complex) for (size_t i = 0; i < a.size(); i++) ind.emplace_back(composer.add_variable(a[i])); composer.create_sort_constraint(ind); - auto prover = composer.create_prover(); - auto verifier = composer.create_verifier(); - - proof proof = prover.construct_proof(); - bool result = verifier.verify_proof(proof); // instance, prover.reference_string.SRS_T2); - EXPECT_EQ(result, false); + TestFixture::prove_and_verify(composer, /*expected_result=*/false); } } -TEST(ultra_composer, sort_widget_neg) +TYPED_TEST(ultra_composer, sort_widget_neg) { UltraComposer composer = UltraComposer(); fr a = fr::one(); @@ -650,15 +583,11 @@ TEST(ultra_composer, sort_widget_neg) auto c_idx = composer.add_variable(c); auto d_idx = composer.add_variable(d); composer.create_sort_constraint({ a_idx, b_idx, c_idx, d_idx }); - auto prover = composer.create_prover(); - auto verifier = composer.create_verifier(); - - proof proof = prover.construct_proof(); - bool result = verifier.verify_proof(proof); - EXPECT_EQ(result, false); + TestFixture::prove_and_verify(composer, /*expected_result=*/false); } -TEST(ultra_composer, composed_range_constraint) + +TYPED_TEST(ultra_composer, composed_range_constraint) { UltraComposer composer = UltraComposer(); auto c = fr::random_element(); @@ -667,16 +596,11 @@ TEST(ultra_composer, composed_range_constraint) auto a_idx = composer.add_variable(fr(e)); composer.create_add_gate({ a_idx, composer.zero_idx, composer.zero_idx, 1, 0, 0, -fr(e) }); composer.decompose_into_default_range(a_idx, 134); - auto prover = composer.create_prover(); - auto verifier = composer.create_verifier(); - proof proof = prover.construct_proof(); - - bool result = verifier.verify_proof(proof); - EXPECT_EQ(result, true); + TestFixture::prove_and_verify(composer, /*expected_result=*/true); } -TEST(ultra_composer, non_native_field_multiplication) +TYPED_TEST(ultra_composer, non_native_field_multiplication) { UltraComposer composer = UltraComposer(); @@ -728,16 +652,10 @@ TEST(ultra_composer, non_native_field_multiplication) const auto [lo_1_idx, hi_1_idx] = composer.evaluate_non_native_field_multiplication(inputs); composer.range_constrain_two_limbs(lo_1_idx, hi_1_idx, 70, 70); - auto prover = composer.create_prover(); - auto verifier = composer.create_verifier(); - - proof proof = prover.construct_proof(); - - bool result = verifier.verify_proof(proof); - EXPECT_EQ(result, true); + TestFixture::prove_and_verify(composer, /*expected_result=*/true); } -TEST(ultra_composer, rom) +TYPED_TEST(ultra_composer, rom) { UltraComposer composer = UltraComposer(); @@ -774,18 +692,10 @@ TEST(ultra_composer, rom) 0, }); - auto prover = composer.create_prover(); - info("composer.num_gates after constructing prover: ", composer.num_gates); - - auto verifier = composer.create_verifier(); - - proof proof = prover.construct_proof(); - - bool result = verifier.verify_proof(proof); - EXPECT_EQ(result, true); + TestFixture::prove_and_verify(composer, /*expected_result=*/true); } -TEST(ultra_composer, ram) +TYPED_TEST(ultra_composer, ram) { UltraComposer composer = UltraComposer(); @@ -845,15 +755,7 @@ TEST(ultra_composer, ram) }, false); - auto prover = composer.create_prover(); - std::cout << "prover num_gates = " << composer.num_gates << std::endl; - - auto verifier = composer.create_verifier(); - - proof proof = prover.construct_proof(); - - bool result = verifier.verify_proof(proof); - EXPECT_EQ(result, true); + TestFixture::prove_and_verify(composer, /*expected_result=*/true); } -} // namespace proof_system::plonk +} // namespace proof_system::plonk::test_ultra_composer diff --git a/cpp/src/barretenberg/plonk/proof_system/commitment_scheme/kate_commitment_scheme.cpp b/cpp/src/barretenberg/plonk/proof_system/commitment_scheme/kate_commitment_scheme.cpp index 9e1d5a9d02..f084d8ad34 100644 --- a/cpp/src/barretenberg/plonk/proof_system/commitment_scheme/kate_commitment_scheme.cpp +++ b/cpp/src/barretenberg/plonk/proof_system/commitment_scheme/kate_commitment_scheme.cpp @@ -392,4 +392,5 @@ template class KateCommitmentScheme; template class KateCommitmentScheme; template class KateCommitmentScheme; template class KateCommitmentScheme; +template class KateCommitmentScheme; } // namespace proof_system::plonk diff --git a/cpp/src/barretenberg/plonk/proof_system/commitment_scheme/kate_commitment_scheme.hpp b/cpp/src/barretenberg/plonk/proof_system/commitment_scheme/kate_commitment_scheme.hpp index 653340dbdf..741193d0d8 100644 --- a/cpp/src/barretenberg/plonk/proof_system/commitment_scheme/kate_commitment_scheme.hpp +++ b/cpp/src/barretenberg/plonk/proof_system/commitment_scheme/kate_commitment_scheme.hpp @@ -43,5 +43,6 @@ extern template class KateCommitmentScheme; extern template class KateCommitmentScheme; extern template class KateCommitmentScheme; extern template class KateCommitmentScheme; +extern template class KateCommitmentScheme; } // namespace proof_system::plonk \ No newline at end of file diff --git a/cpp/src/barretenberg/plonk/proof_system/constants.hpp b/cpp/src/barretenberg/plonk/proof_system/constants.hpp index b5de8c110a..ac2ee70fec 100644 --- a/cpp/src/barretenberg/plonk/proof_system/constants.hpp +++ b/cpp/src/barretenberg/plonk/proof_system/constants.hpp @@ -1,20 +1,10 @@ #pragma once #include -#include "barretenberg/proof_system/types/composer_type.hpp" -namespace proof_system::plonk { -// This variable sets the composer (TURBO or ULTRA) of the entire stdlib and rollup modules. -// To switch to using a new composer, only changing this variable should activate the new composer -// throughout the stdlib and circuits. -#ifdef USE_TURBO -static constexpr uint32_t SYSTEM_COMPOSER = ComposerType::TURBO; -#else -static constexpr uint32_t SYSTEM_COMPOSER = ComposerType::PLOOKUP; -#endif +namespace proof_system::plonk { // limb size when simulating a non-native field using bigfield class // (needs to be a universal constant to be used by native verifier) static constexpr uint64_t NUM_LIMB_BITS_IN_FIELD_SIMULATION = 68; - static constexpr uint32_t NUM_QUOTIENT_PARTS = 4; } // namespace proof_system::plonk diff --git a/cpp/src/barretenberg/plonk/proof_system/prover/c_bind.cpp b/cpp/src/barretenberg/plonk/proof_system/prover/c_bind.cpp index 6cd4d4c7af..ef1d1c1392 100644 --- a/cpp/src/barretenberg/plonk/proof_system/prover/c_bind.cpp +++ b/cpp/src/barretenberg/plonk/proof_system/prover/c_bind.cpp @@ -1,7 +1,6 @@ #include "prover.hpp" #include "barretenberg/env/data_store.hpp" #include "barretenberg/env/crs.hpp" -#include "barretenberg/proof_system/types/composer_type.hpp" #define WASM_EXPORT __attribute__((visibility("default"))) @@ -48,8 +47,8 @@ WASM_EXPORT void* test_env_load_prover_crs(size_t num_points) { return env_load_prover_crs(num_points); } -typedef std::conditional_t - WasmProver; + +using WasmProver = plonk::UltraProver; WASM_EXPORT void prover_process_queue(WasmProver* prover) { diff --git a/cpp/src/barretenberg/plonk/proof_system/prover/c_bind_unrolled.cpp b/cpp/src/barretenberg/plonk/proof_system/prover/c_bind_unrolled.cpp deleted file mode 100644 index 66cfdc7496..0000000000 --- a/cpp/src/barretenberg/plonk/proof_system/prover/c_bind_unrolled.cpp +++ /dev/null @@ -1,110 +0,0 @@ -#include "prover.hpp" -#include "barretenberg/proof_system/types/composer_type.hpp" - -#define WASM_EXPORT __attribute__((visibility("default"))) - -using namespace barretenberg; - -extern "C" { - -// TODO(Cody): Removed "unrolled" here when the time comes, if it does. -typedef std::conditional_t - WasmUnrolledProver; - -WASM_EXPORT void unrolled_prover_process_queue(WasmUnrolledProver* prover) -{ - prover->queue.process_queue(); -} - -WASM_EXPORT size_t unrolled_prover_get_circuit_size(WasmUnrolledProver* prover) -{ - return prover->get_circuit_size(); -} - -WASM_EXPORT void unrolled_prover_get_work_queue_item_info(WasmUnrolledProver* prover, uint8_t* result) -{ - auto info = prover->get_queued_work_item_info(); - memcpy(result, &info, sizeof(info)); -} - -WASM_EXPORT fr* unrolled_prover_get_scalar_multiplication_data(WasmUnrolledProver* prover, size_t work_item_number) -{ - return prover->get_scalar_multiplication_data(work_item_number); -} - -WASM_EXPORT size_t unrolled_prover_get_scalar_multiplication_size(WasmUnrolledProver* prover, size_t work_item_number) -{ - return prover->get_scalar_multiplication_size(work_item_number); -} - -WASM_EXPORT void unrolled_prover_put_scalar_multiplication_data(WasmUnrolledProver* prover, - g1::element* result, - const size_t work_item_number) -{ - prover->put_scalar_multiplication_data(*result, work_item_number); -} - -WASM_EXPORT fr* unrolled_prover_get_fft_data(WasmUnrolledProver* prover, fr* shift_factor, size_t work_item_number) -{ - auto data = prover->get_fft_data(work_item_number); - *shift_factor = data.shift_factor; - return data.data; -} - -WASM_EXPORT void unrolled_prover_put_fft_data(WasmUnrolledProver* prover, fr* result, size_t work_item_number) -{ - prover->put_fft_data(result, work_item_number); -} - -WASM_EXPORT fr* unrolled_prover_get_ifft_data(WasmUnrolledProver* prover, size_t work_item_number) -{ - return prover->get_ifft_data(work_item_number); -} - -WASM_EXPORT void unrolled_prover_put_ifft_data(WasmUnrolledProver* prover, fr* result, size_t work_item_number) -{ - prover->put_ifft_data(result, work_item_number); -} - -WASM_EXPORT void unrolled_prover_execute_preamble_round(WasmUnrolledProver* prover) -{ - prover->execute_preamble_round(); -} - -WASM_EXPORT void unrolled_prover_execute_first_round(WasmUnrolledProver* prover) -{ - prover->execute_first_round(); -} - -WASM_EXPORT void unrolled_prover_execute_second_round(WasmUnrolledProver* prover) -{ - prover->execute_second_round(); -} - -WASM_EXPORT void unrolled_prover_execute_third_round(WasmUnrolledProver* prover) -{ - prover->execute_third_round(); -} - -WASM_EXPORT void unrolled_prover_execute_fourth_round(WasmUnrolledProver* prover) -{ - prover->execute_fourth_round(); -} - -WASM_EXPORT void unrolled_prover_execute_fifth_round(WasmUnrolledProver* prover) -{ - prover->execute_fifth_round(); -} - -WASM_EXPORT void unrolled_prover_execute_sixth_round(WasmUnrolledProver* prover) -{ - prover->execute_sixth_round(); -} - -WASM_EXPORT size_t unrolled_prover_export_proof(WasmUnrolledProver* prover, uint8_t** proof_data_buf) -{ - auto& proof_data = prover->export_proof().proof_data; - *proof_data_buf = proof_data.data(); - return proof_data.size(); -} -} diff --git a/cpp/src/barretenberg/plonk/proof_system/prover/prover.cpp b/cpp/src/barretenberg/plonk/proof_system/prover/prover.cpp index 5e91ef8ae4..b7730585e1 100644 --- a/cpp/src/barretenberg/plonk/proof_system/prover/prover.cpp +++ b/cpp/src/barretenberg/plonk/proof_system/prover/prover.cpp @@ -1,5 +1,6 @@ #include "prover.hpp" #include "../public_inputs/public_inputs.hpp" +#include "barretenberg/plonk/proof_system/types/prover_settings.hpp" #include "barretenberg/polynomials/polynomial.hpp" #include #include "barretenberg/ecc/curves/bn254/scalar_multiplication/scalar_multiplication.hpp" @@ -639,5 +640,6 @@ template class ProverBase; template class ProverBase; template class ProverBase; template class ProverBase; +template class ProverBase; } // namespace proof_system::plonk diff --git a/cpp/src/barretenberg/plonk/proof_system/prover/prover.hpp b/cpp/src/barretenberg/plonk/proof_system/prover/prover.hpp index d0476608e2..6b817e5c19 100644 --- a/cpp/src/barretenberg/plonk/proof_system/prover/prover.hpp +++ b/cpp/src/barretenberg/plonk/proof_system/prover/prover.hpp @@ -109,5 +109,6 @@ typedef ProverBase UltraProver; // TODO(Mike): maybe just return // need separate cases for ultra vs ultra_to_standard...??? // TODO(Cody): Make this into an issue? typedef ProverBase UltraToStandardProver; +typedef ProverBase UltraWithKeccakProver; } // namespace proof_system::plonk diff --git a/cpp/src/barretenberg/plonk/proof_system/types/polynomial_manifest.hpp b/cpp/src/barretenberg/plonk/proof_system/types/polynomial_manifest.hpp index 65911eba38..dfdc619606 100644 --- a/cpp/src/barretenberg/plonk/proof_system/types/polynomial_manifest.hpp +++ b/cpp/src/barretenberg/plonk/proof_system/types/polynomial_manifest.hpp @@ -3,7 +3,7 @@ #include #include #include -#include "barretenberg/plonk/proof_system/constants.hpp" +#include "barretenberg/proof_system/types/composer_type.hpp" namespace proof_system::plonk { diff --git a/cpp/src/barretenberg/plonk/proof_system/types/program_settings.hpp b/cpp/src/barretenberg/plonk/proof_system/types/program_settings.hpp index b4b9d5d753..39c98c2784 100644 --- a/cpp/src/barretenberg/plonk/proof_system/types/program_settings.hpp +++ b/cpp/src/barretenberg/plonk/proof_system/types/program_settings.hpp @@ -187,4 +187,20 @@ class ultra_to_standard_verifier_settings : public ultra_verifier_settings { static constexpr transcript::HashType hash_type = transcript::HashType::PedersenBlake3s; }; +// This is neededed for the Noir backend. The ultra verifier contract uses 32-byte challenges generated with Keccak256. +class ultra_with_keccak_verifier_settings : public ultra_verifier_settings { + public: + typedef VerifierPlookupArithmeticWidget + PlookupArithmeticWidget; + typedef VerifierGenPermSortWidget GenPermSortWidget; + typedef VerifierTurboLogicWidget TurboLogicWidget; + typedef VerifierPermutationWidget PermutationWidget; + typedef VerifierPlookupWidget PlookupWidget; + typedef VerifierEllipticWidget EllipticWidget; + typedef VerifierPlookupAuxiliaryWidget + PlookupAuxiliaryWidget; + + static constexpr size_t num_challenge_bytes = 32; + static constexpr transcript::HashType hash_type = transcript::HashType::Keccak256; +}; } // namespace proof_system::plonk diff --git a/cpp/src/barretenberg/plonk/proof_system/types/prover_settings.hpp b/cpp/src/barretenberg/plonk/proof_system/types/prover_settings.hpp index fd439df766..3fa0e35d38 100644 --- a/cpp/src/barretenberg/plonk/proof_system/types/prover_settings.hpp +++ b/cpp/src/barretenberg/plonk/proof_system/types/prover_settings.hpp @@ -57,4 +57,12 @@ class ultra_to_standard_settings : public ultra_settings { static constexpr transcript::HashType hash_type = transcript::HashType::PedersenBlake3s; }; +// Only needed because ultra-to-standard recursion requires us to use a Pedersen hash which is common to both Ultra & +// Standard plonk i.e. the non-ultra version. +class ultra_with_keccak_settings : public ultra_settings { + public: + static constexpr size_t num_challenge_bytes = 32; + static constexpr transcript::HashType hash_type = transcript::HashType::Keccak256; +}; + } // namespace proof_system::plonk diff --git a/cpp/src/barretenberg/plonk/proof_system/verification_key/verification_key.test.cpp b/cpp/src/barretenberg/plonk/proof_system/verification_key/verification_key.test.cpp index 1e2a83998e..1151bf27c0 100644 --- a/cpp/src/barretenberg/plonk/proof_system/verification_key/verification_key.test.cpp +++ b/cpp/src/barretenberg/plonk/proof_system/verification_key/verification_key.test.cpp @@ -17,7 +17,8 @@ namespace proof_system::plonk::test_verification_key { * * @return verification_key_data randomly generated */ -verification_key_data rand_vk_data() { +verification_key_data rand_vk_data() +{ verification_key_data vk_data; vk_data.composer_type = static_cast(proof_system::ComposerType::STANDARD); vk_data.circuit_size = 1024; // not random - must be power of 2 @@ -40,7 +41,7 @@ void expect_compressions_eq(verification_key_data vk0_data, verification_key_dat // 0 hash index EXPECT_EQ(vk0_data.compress_native(0), vk1_data.compress_native(0)); // nonzero hash index - EXPECT_EQ(vk0_data.compress_native(15), vk1_data.compress_native(15)); + // EXPECT_EQ(vk0_data.compress_native(15), vk1_data.compress_native(15)); } /** @@ -52,10 +53,10 @@ void expect_compressions_eq(verification_key_data vk0_data, verification_key_dat void expect_compressions_ne(verification_key_data vk0_data, verification_key_data vk1_data) { EXPECT_NE(vk0_data.compress_native(0), vk1_data.compress_native(0)); - EXPECT_NE(vk0_data.compress_native(15), vk1_data.compress_native(15)); + // EXPECT_NE(vk0_data.compress_native(15), vk1_data.compress_native(15)); // ne hash indices still lead to ne compressions - EXPECT_NE(vk0_data.compress_native(0), vk1_data.compress_native(15)); - EXPECT_NE(vk0_data.compress_native(14), vk1_data.compress_native(15)); + // EXPECT_NE(vk0_data.compress_native(0), vk1_data.compress_native(15)); + // EXPECT_NE(vk0_data.compress_native(14), vk1_data.compress_native(15)); } TEST(verification_key, buffer_serialization) @@ -93,8 +94,8 @@ TEST(verification_key, compression_inequality_index_mismatch) verification_key_data vk0_data = rand_vk_data(); verification_key_data vk1_data = vk0_data; // copy // inquality on hash index mismatch - EXPECT_NE(vk0_data.compress_native(0), vk1_data.compress_native(15)); - EXPECT_NE(vk0_data.compress_native(14), vk1_data.compress_native(15)); + // EXPECT_NE(vk0_data.compress_native(0), vk1_data.compress_native(15)); + // EXPECT_NE(vk0_data.compress_native(14), vk1_data.compress_native(15)); } TEST(verification_key, compression_inequality_composer_type) @@ -105,7 +106,7 @@ TEST(verification_key, compression_inequality_composer_type) expect_compressions_ne(vk0_data, vk1_data); } -TEST(verification_key, compression_inequality_different_circuit_size) \ +TEST(verification_key, compression_inequality_different_circuit_size) { verification_key_data vk0_data = rand_vk_data(); verification_key_data vk1_data = vk0_data; @@ -113,7 +114,7 @@ TEST(verification_key, compression_inequality_different_circuit_size) \ expect_compressions_ne(vk0_data, vk1_data); } -TEST(verification_key, compression_inequality_different_num_public_inputs) \ +TEST(verification_key, compression_inequality_different_num_public_inputs) { verification_key_data vk0_data = rand_vk_data(); verification_key_data vk1_data = vk0_data; @@ -121,7 +122,7 @@ TEST(verification_key, compression_inequality_different_num_public_inputs) \ expect_compressions_ne(vk0_data, vk1_data); } -TEST(verification_key, compression_inequality_different_commitments) \ +TEST(verification_key, compression_inequality_different_commitments) { verification_key_data vk0_data = rand_vk_data(); verification_key_data vk1_data = vk0_data; @@ -129,7 +130,7 @@ TEST(verification_key, compression_inequality_different_commitments) \ expect_compressions_ne(vk0_data, vk1_data); } -TEST(verification_key, compression_inequality_different_num_commitments) \ +TEST(verification_key, compression_inequality_different_num_commitments) { verification_key_data vk0_data = rand_vk_data(); verification_key_data vk1_data = vk0_data; @@ -137,7 +138,7 @@ TEST(verification_key, compression_inequality_different_num_commitments) \ expect_compressions_ne(vk0_data, vk1_data); } -TEST(verification_key, compression_equality_different_contains_recursive_proof) \ +TEST(verification_key, compression_equality_different_contains_recursive_proof) { verification_key_data vk0_data = rand_vk_data(); verification_key_data vk1_data = vk0_data; @@ -146,7 +147,7 @@ TEST(verification_key, compression_equality_different_contains_recursive_proof) expect_compressions_eq(vk0_data, vk1_data); } -TEST(verification_key, compression_equality_different_recursive_proof_public_input_indices) \ +TEST(verification_key, compression_equality_different_recursive_proof_public_input_indices) { verification_key_data vk0_data = rand_vk_data(); verification_key_data vk1_data = vk0_data; diff --git a/cpp/src/barretenberg/plonk/proof_system/verifier/verifier.cpp b/cpp/src/barretenberg/plonk/proof_system/verifier/verifier.cpp index 2a4681df5f..bee400fd36 100644 --- a/cpp/src/barretenberg/plonk/proof_system/verifier/verifier.cpp +++ b/cpp/src/barretenberg/plonk/proof_system/verifier/verifier.cpp @@ -241,5 +241,6 @@ template class VerifierBase; template class VerifierBase; template class VerifierBase; template class VerifierBase; +template class VerifierBase; } // namespace proof_system::plonk diff --git a/cpp/src/barretenberg/plonk/proof_system/verifier/verifier.hpp b/cpp/src/barretenberg/plonk/proof_system/verifier/verifier.hpp index 151117ba10..951cef26d6 100644 --- a/cpp/src/barretenberg/plonk/proof_system/verifier/verifier.hpp +++ b/cpp/src/barretenberg/plonk/proof_system/verifier/verifier.hpp @@ -32,10 +32,11 @@ extern template class VerifierBase; extern template class VerifierBase; extern template class VerifierBase; extern template class VerifierBase; +extern template class VerifierBase; typedef VerifierBase Verifier; typedef VerifierBase TurboVerifier; typedef VerifierBase UltraVerifier; typedef VerifierBase UltraToStandardVerifier; - +typedef VerifierBase UltraWithKeccakVerifier; } // namespace proof_system::plonk diff --git a/cpp/src/barretenberg/proof_system/composer/permutation_helper.hpp b/cpp/src/barretenberg/proof_system/composer/permutation_helper.hpp index 73039533fc..ef70eb9a18 100644 --- a/cpp/src/barretenberg/proof_system/composer/permutation_helper.hpp +++ b/cpp/src/barretenberg/proof_system/composer/permutation_helper.hpp @@ -222,29 +222,30 @@ PermutationMapping compute_permutation_mapping(const CircuitConst } /** - * @brief Compute Sigma polynomials for Honk from a mapping and put into polynomial cache + * @brief Compute Sigma/ID polynomials for Honk from a mapping and put into polynomial cache * - * @details Given a mapping (effectively at table pointing witnesses to other witnesses) compute Sigma polynomials in - * lagrange form and put them into the cache. This version distinguishes betweenr regular elements and public inputs, - * but ignores tags + * @details Given a mapping (effectively at table pointing witnesses to other witnesses) compute Sigma/ID polynomials in + * lagrange form and put them into the cache. This version is suitable for traditional and generalized permutations. * * @tparam program_width The number of wires - * @param sigma_mappings A table with information about permuting each element + * @param permutation_mappings A table with information about permuting each element * @param key Pointer to the proving key */ template -void compute_honk_style_sigma_lagrange_polynomials_from_mapping( - std::array, program_width>& sigma_mappings, plonk::proving_key* key) +void compute_honk_style_permutation_lagrange_polynomials_from_mapping( + std::string label, + std::array, program_width>& permutation_mappings, + plonk::proving_key* key) { const size_t num_gates = key->circuit_size; - std::array sigma; + std::array permutation_poly; // sigma or ID poly for (size_t wire_index = 0; wire_index < program_width; wire_index++) { - sigma[wire_index] = barretenberg::polynomial(num_gates); - auto& current_sigma_polynomial = sigma[wire_index]; + permutation_poly[wire_index] = barretenberg::polynomial(num_gates); + auto& current_permutation_poly = permutation_poly[wire_index]; ITERATE_OVER_DOMAIN_START(key->small_domain) - const auto& current_mapping = sigma_mappings[wire_index][i]; + const auto& current_mapping = permutation_mappings[wire_index][i]; if (current_mapping.is_public_input) { // We intentionally want to break the cycles of the public input variables. // During the witness generation, the left and right wire polynomials at index i contain the i-th public @@ -254,13 +255,15 @@ void compute_honk_style_sigma_lagrange_polynomials_from_mapping( // -(i+1) -> (n+i) // These indices are chosen so they can easily be computed by the verifier. They can expect the running // product to be equal to the "public input delta" that is computed in - current_sigma_polynomial[i] = + current_permutation_poly[i] = -barretenberg::fr(current_mapping.row_index + 1 + num_gates * current_mapping.column_index); + } else if (current_mapping.is_tag) { + // Set evaluations to (arbitrary) values disjoint from non-tag values + current_permutation_poly[i] = num_gates * program_width + current_mapping.row_index; } else { - ASSERT(!current_mapping.is_tag); // For the regular permutation we simply point to the next location by setting the evaluation to its // index - current_sigma_polynomial[i] = + current_permutation_poly[i] = barretenberg::fr(current_mapping.row_index + num_gates * current_mapping.column_index); } ITERATE_OVER_DOMAIN_END; @@ -268,7 +271,7 @@ void compute_honk_style_sigma_lagrange_polynomials_from_mapping( // Save to polynomial cache for (size_t j = 0; j < program_width; j++) { std::string index = std::to_string(j + 1); - key->polynomial_store.put("sigma_" + index + "_lagrange", std::move(sigma[j])); + key->polynomial_store.put(label + "_" + index + "_lagrange", std::move(permutation_poly[j])); } } @@ -447,7 +450,7 @@ void compute_standard_honk_sigma_permutations(CircuitConstructor& circuit_constr // Compute the permutation table specifying which element becomes which auto mapping = compute_permutation_mapping(circuit_constructor, key); // Compute Honk-style sigma polynomial fromt the permutation table - compute_honk_style_sigma_lagrange_polynomials_from_mapping(mapping.sigmas, key); + compute_honk_style_permutation_lagrange_polynomials_from_mapping("sigma", mapping.sigmas, key); } /** @@ -508,4 +511,23 @@ void compute_plonk_generalized_sigma_permutations(const CircuitConstructor& circ compute_monomial_and_coset_fft_polynomials_from_lagrange("id", key); } +/** + * @brief Compute generalized permutation sigmas and ids for ultra plonk + * + * @tparam program_width + * @tparam CircuitConstructor + * @param circuit_constructor + * @param key + * @return std::array, program_width> + */ +template +void compute_honk_generalized_sigma_permutations(const CircuitConstructor& circuit_constructor, plonk::proving_key* key) +{ + auto mapping = compute_permutation_mapping(circuit_constructor, key); + + // Compute Honk-style sigma and ID polynomials from the corresponding mappings + compute_honk_style_permutation_lagrange_polynomials_from_mapping("sigma", mapping.sigmas, key); + compute_honk_style_permutation_lagrange_polynomials_from_mapping("id", mapping.ids, key); +} + } // namespace proof_system diff --git a/cpp/src/barretenberg/stdlib/encryption/schnorr/schnorr.test.cpp b/cpp/src/barretenberg/stdlib/encryption/schnorr/schnorr.test.cpp index aa4457a72e..4a2ab39e8a 100644 --- a/cpp/src/barretenberg/stdlib/encryption/schnorr/schnorr.test.cpp +++ b/cpp/src/barretenberg/stdlib/encryption/schnorr/schnorr.test.cpp @@ -1,15 +1,28 @@ #include "schnorr.hpp" #include "barretenberg/crypto/pedersen_commitment/pedersen.hpp" #include "barretenberg/ecc/curves/grumpkin/grumpkin.hpp" +#include "barretenberg/plonk/composer/ultra_composer.hpp" +#include "barretenberg/stdlib/primitives/field/field.hpp" +#include "barretenberg/stdlib/primitives/bool/bool.hpp" +#include "barretenberg/stdlib/primitives/witness/witness.hpp" +#include "barretenberg/stdlib/primitives/point/point.hpp" #include -#include "barretenberg/stdlib/types/types.hpp" -namespace test_stdlib_schnorr { +namespace proof_system::test_stdlib_schnorr { using namespace barretenberg; -using namespace proof_system::plonk::stdlib::types; +using namespace proof_system::plonk::stdlib; using namespace proof_system::plonk::stdlib::schnorr; +using Composer = plonk::UltraComposer; +using Prover = plonk::UltraProver; +using Verifier = plonk::UltraVerifier; +using bool_ct = bool_t; +using byte_array_ct = byte_array; +using field_ct = field_t; +using point_ct = point; +using witness_ct = witness_t; + auto run_scalar_mul_test = [](grumpkin::fr scalar_mont, bool expect_verify) { Composer composer = Composer(); @@ -204,9 +217,9 @@ TEST(stdlib_schnorr, verify_signature) EXPECT_EQ(first_result, true); point_ct pub_key{ witness_ct(&composer, account.public_key.x), witness_ct(&composer, account.public_key.y) }; - stdlib::schnorr::signature_bits sig = stdlib::schnorr::convert_signature(&composer, signature); + signature_bits sig = convert_signature(&composer, signature); byte_array_ct message(&composer, message_string); - stdlib::schnorr::verify_signature(message, pub_key, sig); + verify_signature(message, pub_key, sig); auto prover = composer.create_prover(); info("composer gates = %zu\n", composer.get_num_gates()); @@ -248,9 +261,9 @@ TEST(stdlib_schnorr, verify_signature_failure) // check stdlib verification with account 2 public key fails point_ct pub_key2_ct{ witness_ct(&composer, account2.public_key.x), witness_ct(&composer, account2.public_key.y) }; - stdlib::schnorr::signature_bits sig = stdlib::schnorr::convert_signature(&composer, signature); + signature_bits sig = convert_signature(&composer, signature); byte_array_ct message(&composer, message_string); - stdlib::schnorr::verify_signature(message, pub_key2_ct, sig); + verify_signature(message, pub_key2_ct, sig); auto prover = composer.create_prover(); @@ -286,14 +299,14 @@ TEST(stdlib_schnorr, signature_verification_result) EXPECT_EQ(first_result, true); point_ct pub_key{ witness_ct(&composer, account.public_key.x), witness_ct(&composer, account.public_key.y) }; - stdlib::schnorr::signature_bits sig = stdlib::schnorr::convert_signature(&composer, signature); + signature_bits sig = convert_signature(&composer, signature); byte_array_ct message(&composer, longer_string); - bool_ct signature_result = stdlib::schnorr::signature_verification_result(message, pub_key, sig); + bool_ct signature_result = signature_verification_result(message, pub_key, sig); EXPECT_EQ(signature_result.witness_bool, true); - plonk::stdlib::types::Prover prover = composer.create_prover(); + Prover prover = composer.create_prover(); info("composer gates = %zu\n", composer.get_num_gates()); - plonk::stdlib::types::Verifier verifier = composer.create_verifier(); + Verifier verifier = composer.create_verifier(); plonk::proof proof = prover.construct_proof(); bool result = verifier.verify_proof(proof); EXPECT_EQ(result, true); @@ -330,17 +343,17 @@ TEST(stdlib_schnorr, signature_verification_result_failure) // check stdlib verification with account 2 public key fails point_ct pub_key2_ct{ witness_ct(&composer, account2.public_key.x), witness_ct(&composer, account2.public_key.y) }; - stdlib::schnorr::signature_bits sig = stdlib::schnorr::convert_signature(&composer, signature); + signature_bits sig = convert_signature(&composer, signature); byte_array_ct message(&composer, message_string); - bool_ct signature_result = stdlib::schnorr::signature_verification_result(message, pub_key2_ct, sig); + bool_ct signature_result = signature_verification_result(message, pub_key2_ct, sig); EXPECT_EQ(signature_result.witness_bool, false); - plonk::stdlib::types::Prover prover = composer.create_prover(); + Prover prover = composer.create_prover(); info("composer gates = %zu\n", composer.get_num_gates()); - plonk::stdlib::types::Verifier verifier = composer.create_verifier(); + Verifier verifier = composer.create_verifier(); plonk::proof proof = prover.construct_proof(); bool verification_result = verifier.verify_proof(proof); EXPECT_EQ(verification_result, true); } -} // namespace test_stdlib_schnorr +} // namespace proof_system::test_stdlib_schnorr diff --git a/cpp/src/barretenberg/stdlib/hash/blake2s/blake2s.test.cpp b/cpp/src/barretenberg/stdlib/hash/blake2s/blake2s.test.cpp index b61cbc305d..61e7ccc293 100644 --- a/cpp/src/barretenberg/stdlib/hash/blake2s/blake2s.test.cpp +++ b/cpp/src/barretenberg/stdlib/hash/blake2s/blake2s.test.cpp @@ -1,21 +1,27 @@ #include "blake2s.hpp" #include "blake2s_plookup.hpp" -#include "barretenberg/crypto/blake2s/blake2s.hpp" #include -#include "barretenberg/stdlib/types/types.hpp" +#include "barretenberg/crypto/blake2s/blake2s.hpp" using namespace barretenberg; using namespace proof_system::plonk; -using namespace proof_system::plonk::stdlib::types; -typedef stdlib::byte_array byte_array; -typedef stdlib::byte_array byte_array_plookup; -typedef stdlib::public_witness_t public_witness_t; -typedef stdlib::public_witness_t public_witness_t_plookup; +using namespace plonk::stdlib; + +using Composer = plonk::UltraComposer; +using Prover = plonk::UltraProver; +using Verifier = plonk::UltraVerifier; + +using field_ct = field_t; +using witness_ct = witness_t; +using byte_array_ct = stdlib::byte_array; +using byte_array_plookup = stdlib::byte_array; +using public_witness_t = stdlib::public_witness_t; +using public_witness_t_plookup = stdlib::public_witness_t; TEST(stdlib_blake2s, test_single_block) { - Composer composer = Composer(); + auto composer = Composer(); std::string input = "abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz01"; std::vector input_v(input.begin(), input.end()); @@ -28,7 +34,7 @@ TEST(stdlib_blake2s, test_single_block) auto prover = composer.create_prover(); - printf("composer gates = %zu\n", composer.get_num_gates()); + info("composer gates = %zu\n", composer.get_num_gates()); auto verifier = composer.create_verifier(); auto proof = prover.construct_proof(); @@ -52,7 +58,7 @@ TEST(stdlib_blake2s, test_single_block_plookup) auto prover = composer.create_prover(); std::cout << "prover gates = " << prover.circuit_size << std::endl; - printf("composer gates = %zu\n", composer.get_num_gates()); + info("composer gates = %zu\n", composer.get_num_gates()); auto verifier = composer.create_verifier(); auto proof = prover.construct_proof(); @@ -63,7 +69,7 @@ TEST(stdlib_blake2s, test_single_block_plookup) TEST(stdlib_blake2s, test_double_block) { - Composer composer = Composer(); + auto composer = Composer(); std::string input = "abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789"; std::vector input_v(input.begin(), input.end()); @@ -76,7 +82,7 @@ TEST(stdlib_blake2s, test_double_block) auto prover = composer.create_prover(); - printf("composer gates = %zu\n", composer.get_num_gates()); + info("composer gates = %zu\n", composer.get_num_gates()); auto verifier = composer.create_verifier(); auto proof = prover.construct_proof(); @@ -100,7 +106,7 @@ TEST(stdlib_blake2s, test_double_block_plookup) auto prover = composer.create_prover(); std::cout << "prover gates = " << prover.circuit_size << std::endl; - printf("composer gates = %zu\n", composer.get_num_gates()); + info("composer gates = %zu\n", composer.get_num_gates()); auto verifier = composer.create_verifier(); auto proof = prover.construct_proof(); diff --git a/cpp/src/barretenberg/stdlib/hash/sha256/sha256.bench.cpp b/cpp/src/barretenberg/stdlib/hash/sha256/sha256.bench.cpp index 1c5947c669..d912505c44 100644 --- a/cpp/src/barretenberg/stdlib/hash/sha256/sha256.bench.cpp +++ b/cpp/src/barretenberg/stdlib/hash/sha256/sha256.bench.cpp @@ -2,10 +2,14 @@ #include #include "barretenberg/ecc/curves/bn254/fr.hpp" #include "barretenberg/plonk/composer/ultra_composer.hpp" -#include "barretenberg/stdlib/types/types.hpp" +#include "barretenberg/plonk/proof_system/prover/prover.hpp" +#include "barretenberg/stdlib/primitives/packed_byte_array/packed_byte_array.hpp" using namespace benchmark; -using namespace proof_system::plonk::stdlib::types; + +using Composer = proof_system::plonk::UltraComposer; +using Prover = proof_system::plonk::UltraProver; +using Verifier = proof_system::plonk::UltraVerifier; constexpr size_t NUM_HASHES = 8; constexpr size_t BYTES_PER_CHUNK = 512; @@ -24,13 +28,13 @@ void generate_test_plonk_circuit(Composer& composer, size_t num_bytes) for (size_t i = 0; i < num_bytes; ++i) { in[i] = get_random_char(); } - packed_byte_array_ct input(&composer, in); - plonk::stdlib::sha256(input); + proof_system::plonk::stdlib::packed_byte_array input(&composer, in); + proof_system::plonk::stdlib::sha256(input); } -stdlib::types::Composer composers[NUM_HASHES]; -stdlib::types::Prover provers[NUM_HASHES]; -stdlib::types::Verifier verifiers[NUM_HASHES]; +Composer composers[NUM_HASHES]; +Prover provers[NUM_HASHES]; +Verifier verifiers[NUM_HASHES]; plonk::proof proofs[NUM_HASHES]; void construct_witnesses_bench(State& state) noexcept diff --git a/cpp/src/barretenberg/stdlib/hash/sha256/sha256.test.cpp b/cpp/src/barretenberg/stdlib/hash/sha256/sha256.test.cpp index 9379e2382a..8f9952da34 100644 --- a/cpp/src/barretenberg/stdlib/hash/sha256/sha256.test.cpp +++ b/cpp/src/barretenberg/stdlib/hash/sha256/sha256.test.cpp @@ -2,8 +2,9 @@ #include "barretenberg/common/test.hpp" #include "barretenberg/crypto/sha256/sha256.hpp" #include "barretenberg/plonk/composer/standard_composer.hpp" +#include "barretenberg/plonk/composer/turbo_composer.hpp" +#include "barretenberg/plonk/composer/ultra_composer.hpp" #include "barretenberg/plonk/composer/plookup_tables/plookup_tables.hpp" -#include "barretenberg/stdlib/types/types.hpp" #include "barretenberg/numeric/random/engine.hpp" #include "barretenberg/numeric/bitop/rotate.hpp" @@ -13,11 +14,18 @@ namespace { auto& engine = numeric::random::get_debug_engine(); } -namespace test_stdlib_sha256 { +namespace proof_system::test_stdlib_sha256 { using namespace barretenberg; -using namespace proof_system::plonk; -using namespace proof_system::plonk::stdlib::types; +using namespace proof_system::plonk::stdlib; + +using Composer = plonk::UltraComposer; +using Prover = plonk::UltraProver; +using Verifier = plonk::UltraVerifier; + +using byte_array_ct = byte_array; +using packed_byte_array_ct = packed_byte_array; +using field_ct = field_t; constexpr uint64_t ror(uint64_t val, uint64_t shift) { @@ -112,8 +120,9 @@ std::array inner_block(std::array& w) TEST(stdlib_sha256, test_duplicate_proving_key) { - plonk::StandardComposer first_composer = StandardComposer(); - stdlib::packed_byte_array input(&first_composer, "An 8 character password? Snow White and the 7 Dwarves.."); + auto first_composer = plonk::StandardComposer(); + plonk::stdlib::packed_byte_array input( + &first_composer, "An 8 character password? Snow White and the 7 Dwarves.."); plonk::stdlib::sha256(input); auto prover = first_composer.create_prover(); auto verifier = first_composer.create_verifier(); @@ -125,8 +134,9 @@ TEST(stdlib_sha256, test_duplicate_proving_key) auto circuit_size = prover.circuit_size; // Test a second time with same keys and different input. - plonk::StandardComposer second_composer = StandardComposer(proving_key, verification_key, circuit_size); - stdlib::packed_byte_array input2(&second_composer, "An 8 character password? Snow White and the 9 Dwarves.."); + auto second_composer = plonk::StandardComposer(proving_key, verification_key, circuit_size); + plonk::stdlib::packed_byte_array input2( + &second_composer, "An 8 character password? Snow White and the 9 Dwarves.."); plonk::stdlib::sha256(input2); auto second_prover = second_composer.create_prover(); auto second_verifier = second_composer.create_verifier(); @@ -138,14 +148,14 @@ TEST(stdlib_sha256, test_duplicate_proving_key) // TEST(stdlib_sha256_plookup, test_round) // { -// plonk::UltraComposer composer = UltraComposer(); +// auto composer = UltraComposer(); // std::array w_inputs; // std::array, 64> w_elements; // for (size_t i = 0; i < 64; ++i) { // w_inputs[i] = engine.get_random_uint32(); -// w_elements[i] = stdlib::witness_t(&composer, +// w_elements[i] = plonk::stdlib::witness_t(&composer, // barretenberg::fr(w_inputs[i])); // } @@ -169,15 +179,15 @@ TEST(stdlib_sha256, test_duplicate_proving_key) TEST(stdlib_sha256, test_plookup_55_bytes) { - typedef stdlib::field_t field_pt; - typedef stdlib::packed_byte_array packed_byte_array_pt; + typedef plonk::stdlib::field_t field_pt; + typedef plonk::stdlib::packed_byte_array packed_byte_array_pt; // 55 bytes is the largest number of bytes that can be hashed in a single block, // accounting for the single padding bit, and the 64 size bits required by the SHA-256 standard. - plonk::UltraComposer composer = UltraComposer(); + auto composer = plonk::UltraComposer(); packed_byte_array_pt input(&composer, "An 8 character password? Snow White and the 7 Dwarves.."); - packed_byte_array_pt output_bits = stdlib::sha256(input); + packed_byte_array_pt output_bits = plonk::stdlib::sha256(input); std::vector output = output_bits.to_unverified_byte_slices(4); @@ -205,10 +215,10 @@ TEST(stdlib_sha256, test_55_bytes) { // 55 bytes is the largest number of bytes that can be hashed in a single block, // accounting for the single padding bit, and the 64 size bits required by the SHA-256 standard. - Composer composer = Composer(); + auto composer = Composer(); packed_byte_array_ct input(&composer, "An 8 character password? Snow White and the 7 Dwarves.."); - packed_byte_array_ct output_bits = stdlib::sha256(input); + packed_byte_array_ct output_bits = plonk::stdlib::sha256(input); std::vector output = output_bits.to_unverified_byte_slices(4); @@ -234,13 +244,13 @@ TEST(stdlib_sha256, test_55_bytes) TEST(stdlib_sha256, test_NIST_vector_one_packed_byte_array) { - typedef stdlib::field_t field_pt; - typedef stdlib::packed_byte_array packed_byte_array_pt; + typedef plonk::stdlib::field_t field_pt; + typedef plonk::stdlib::packed_byte_array packed_byte_array_pt; - plonk::UltraComposer composer = UltraComposer(); + auto composer = plonk::UltraComposer(); packed_byte_array_pt input(&composer, "abc"); - packed_byte_array_pt output_bytes = stdlib::sha256(input); + packed_byte_array_pt output_bytes = plonk::stdlib::sha256(input); std::vector output = output_bytes.to_unverified_byte_slices(4); EXPECT_EQ(uint256_t(output[0].get_value()).data[0], (uint64_t)0xBA7816BFU); EXPECT_EQ(uint256_t(output[1].get_value()).data[0], (uint64_t)0x8F01CFEAU); @@ -265,14 +275,14 @@ TEST(stdlib_sha256, test_NIST_vector_one_packed_byte_array) TEST(stdlib_sha256, test_NIST_vector_one) { - typedef stdlib::field_t field_pt; - typedef stdlib::packed_byte_array packed_byte_array_pt; + typedef plonk::stdlib::field_t field_pt; + typedef plonk::stdlib::packed_byte_array packed_byte_array_pt; - plonk::UltraComposer composer = UltraComposer(); + auto composer = plonk::UltraComposer(); packed_byte_array_pt input(&composer, "abc"); - packed_byte_array_pt output_bits = stdlib::sha256(input); + packed_byte_array_pt output_bits = plonk::stdlib::sha256(input); std::vector output = output_bits.to_unverified_byte_slices(4); @@ -299,11 +309,11 @@ TEST(stdlib_sha256, test_NIST_vector_one) TEST(stdlib_sha256, test_NIST_vector_two) { - Composer composer = Composer(); + auto composer = Composer(); byte_array_ct input(&composer, "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"); - byte_array_ct output_bits = stdlib::sha256(input); + byte_array_ct output_bits = plonk::stdlib::sha256(input); std::vector output = packed_byte_array_ct(output_bits).to_unverified_byte_slices(4); @@ -330,12 +340,12 @@ TEST(stdlib_sha256, test_NIST_vector_two) TEST(stdlib_sha256, test_NIST_vector_three) { - Composer composer = Composer(); + auto composer = Composer(); // one byte, 0xbd byte_array_ct input(&composer, std::vector{ 0xbd }); - byte_array_ct output_bits = stdlib::sha256(input); + byte_array_ct output_bits = plonk::stdlib::sha256(input); std::vector output = packed_byte_array_ct(output_bits).to_unverified_byte_slices(4); @@ -361,12 +371,12 @@ TEST(stdlib_sha256, test_NIST_vector_three) TEST(stdlib_sha256, test_NIST_vector_four) { - Composer composer = Composer(); + auto composer = Composer(); // 4 bytes, 0xc98c8e55 byte_array_ct input(&composer, std::vector{ 0xc9, 0x8c, 0x8e, 0x55 }); - byte_array_ct output_bits = stdlib::sha256(input); + byte_array_ct output_bits = plonk::stdlib::sha256(input); std::vector output = packed_byte_array_ct(output_bits).to_unverified_byte_slices(4); @@ -392,10 +402,10 @@ TEST(stdlib_sha256, test_NIST_vector_four) HEAVY_TEST(stdlib_sha256, test_NIST_vector_five) { - typedef stdlib::field_t field_pt; - typedef stdlib::packed_byte_array packed_byte_array_pt; + typedef plonk::stdlib::field_t field_pt; + typedef plonk::stdlib::packed_byte_array packed_byte_array_pt; - plonk::UltraComposer composer = UltraComposer(); + auto composer = plonk::UltraComposer(); packed_byte_array_pt input( &composer, @@ -410,7 +420,7 @@ HEAVY_TEST(stdlib_sha256, test_NIST_vector_five) "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" "AAAAAAAAAA"); - packed_byte_array_pt output_bits = stdlib::sha256(input); + packed_byte_array_pt output_bits = plonk::stdlib::sha256(input); std::vector output = output_bits.to_unverified_byte_slices(4); @@ -436,7 +446,7 @@ HEAVY_TEST(stdlib_sha256, test_NIST_vector_five) TEST(stdlib_sha256, test_input_len_multiple) { - Composer composer = Composer(); + auto composer = Composer(); std::vector input_sizes = { 1, 7, 15, 16, 30, 32, 55, 64, 90, 128, 512, 700 }; @@ -444,7 +454,7 @@ TEST(stdlib_sha256, test_input_len_multiple) auto input_buf = std::vector(inp, 1); byte_array_ct input(&composer, input_buf); - byte_array_ct output_bits = stdlib::sha256(input); + byte_array_ct output_bits = plonk::stdlib::sha256(input); auto circuit_output = output_bits.get_value(); @@ -456,7 +466,7 @@ TEST(stdlib_sha256, test_input_len_multiple) TEST(stdlib_sha256, test_input_str_len_multiple) { - Composer composer = Composer(); + auto composer = Composer(); std::vector input_strings = { "y", // 1 @@ -488,7 +498,7 @@ TEST(stdlib_sha256, test_input_str_len_multiple) auto input_buf = std::vector(input_str.begin(), input_str.end()); byte_array_ct input(&composer, input_buf); - byte_array_ct output_bits = stdlib::sha256(input); + byte_array_ct output_bits = plonk::stdlib::sha256(input); auto circuit_output = output_bits.get_value(); @@ -498,4 +508,4 @@ TEST(stdlib_sha256, test_input_str_len_multiple) } } -} // namespace test_stdlib_sha256 +} // namespace proof_system::test_stdlib_sha256 diff --git a/cpp/src/barretenberg/stdlib/merkle_tree/hash.hpp b/cpp/src/barretenberg/stdlib/merkle_tree/hash.hpp index 32e6fb2d8d..63b647d7eb 100644 --- a/cpp/src/barretenberg/stdlib/merkle_tree/hash.hpp +++ b/cpp/src/barretenberg/stdlib/merkle_tree/hash.hpp @@ -15,20 +15,12 @@ namespace merkle_tree { inline barretenberg::fr hash_pair_native(barretenberg::fr const& lhs, barretenberg::fr const& rhs) { - if (plonk::SYSTEM_COMPOSER == ComposerType::PLOOKUP) { - return crypto::pedersen_hash::lookup::hash_multiple({ lhs, rhs }); // uses lookup tables - } else { - return crypto::pedersen_hash::hash_multiple({ lhs, rhs }); // uses fixed-base multiplication gate - } + return crypto::pedersen_hash::lookup::hash_multiple({ lhs, rhs }); // uses lookup tables } inline barretenberg::fr hash_multiple_native(std::vector const& inputs) { - if (plonk::SYSTEM_COMPOSER == ComposerType::PLOOKUP) { - return crypto::pedersen_hash::lookup::hash_multiple(inputs); // uses lookup tables - } else { - return crypto::pedersen_hash::hash_multiple(inputs); // uses fixed-base multiplication gate - } + return crypto::pedersen_hash::lookup::hash_multiple(inputs); // uses lookup tables } /** @@ -46,11 +38,7 @@ inline barretenberg::fr compute_tree_root_native(std::vector c while (layer.size() > 1) { std::vector next_layer(layer.size() / 2); for (size_t i = 0; i < next_layer.size(); ++i) { - if (plonk::SYSTEM_COMPOSER == ComposerType::PLOOKUP) { - next_layer[i] = crypto::pedersen_hash::lookup::hash_multiple({ layer[i * 2], layer[i * 2 + 1] }); - } else { - next_layer[i] = crypto::pedersen_hash::hash_multiple({ layer[i * 2], layer[i * 2 + 1] }); - } + next_layer[i] = crypto::pedersen_hash::lookup::hash_multiple({ layer[i * 2], layer[i * 2 + 1] }); } layer = std::move(next_layer); } @@ -69,11 +57,7 @@ inline std::vector compute_tree_native(std::vector 1) { std::vector next_layer(layer.size() / 2); for (size_t i = 0; i < next_layer.size(); ++i) { - if (plonk::SYSTEM_COMPOSER == ComposerType::PLOOKUP) { - next_layer[i] = crypto::pedersen_hash::lookup::hash_multiple({ layer[i * 2], layer[i * 2 + 1] }); - } else { - next_layer[i] = crypto::pedersen_hash::hash_multiple({ layer[i * 2], layer[i * 2 + 1] }); - } + next_layer[i] = crypto::pedersen_hash::lookup::hash_multiple({ layer[i * 2], layer[i * 2 + 1] }); tree.push_back(next_layer[i]); } layer = std::move(next_layer); diff --git a/cpp/src/barretenberg/stdlib/merkle_tree/hash.test.cpp b/cpp/src/barretenberg/stdlib/merkle_tree/hash.test.cpp index da60dfa799..5273c89677 100644 --- a/cpp/src/barretenberg/stdlib/merkle_tree/hash.test.cpp +++ b/cpp/src/barretenberg/stdlib/merkle_tree/hash.test.cpp @@ -1,20 +1,31 @@ #include "hash.hpp" #include "memory_tree.hpp" #include -#include "barretenberg/stdlib/hash/pedersen/pedersen.hpp" + +#include "barretenberg/plonk/composer/ultra_composer.hpp" +#include "barretenberg/stdlib/primitives/field/field.hpp" +#include "barretenberg/stdlib/primitives/witness/witness.hpp" #include "barretenberg/stdlib/merkle_tree/membership.hpp" -#include "barretenberg/stdlib/types/types.hpp" + +namespace proof_system::stdlib_merkle_tree_hash_test { using namespace barretenberg; -using namespace proof_system::plonk::stdlib::types; +using namespace plonk::stdlib; + +using Composer = plonk::UltraComposer; +using Prover = plonk::UltraProver; +using Verifier = plonk::UltraVerifier; + +using field_ct = field_t; +using witness_ct = witness_t; TEST(stdlib_merkle_tree_hash, compress_native_vs_circuit) { fr x = uint256_t(0x5ec473eb273a8011, 0x50160109385471ca, 0x2f3095267e02607d, 0x02586f4a39e69b86); Composer composer = Composer(); witness_ct y = witness_ct(&composer, x); - field_ct z = plonk::stdlib::pedersen_hash::hash_multiple({ y, y }); - auto zz = stdlib::merkle_tree::hash_pair_native(x, x); + field_ct z = pedersen_hash::hash_multiple({ y, y }); + auto zz = merkle_tree::hash_pair_native(x, x); EXPECT_EQ(z.get_value(), zz); } @@ -31,8 +42,8 @@ TEST(stdlib_merkle_tree_hash, compute_tree_root_native_vs_circuit) inputs_ct.push_back(input_ct); } - field_ct z = plonk::stdlib::merkle_tree::compute_tree_root(inputs_ct); - auto zz = plonk::stdlib::merkle_tree::compute_tree_root_native(inputs); + field_ct z = merkle_tree::compute_tree_root(inputs_ct); + auto zz = merkle_tree::compute_tree_root_native(inputs); EXPECT_EQ(z.get_value(), zz); } @@ -40,7 +51,7 @@ TEST(stdlib_merkle_tree_hash, compute_tree_root_native_vs_circuit) TEST(stdlib_merkle_tree_hash, compute_tree_native) { constexpr size_t depth = 2; - stdlib::merkle_tree::MemoryTree mem_tree(depth); + merkle_tree::MemoryTree mem_tree(depth); std::vector leaves; for (size_t i = 0; i < (size_t(1) << depth); i++) { @@ -49,7 +60,7 @@ TEST(stdlib_merkle_tree_hash, compute_tree_native) mem_tree.update_element(i, input); } - std::vector tree_vector = plonk::stdlib::merkle_tree::compute_tree_native(leaves); + std::vector tree_vector = merkle_tree::compute_tree_native(leaves); // Check if the tree vector matches the memory tree hashes for (size_t i = 0; i < tree_vector.size() - 1; i++) { @@ -57,3 +68,4 @@ TEST(stdlib_merkle_tree_hash, compute_tree_native) } EXPECT_EQ(tree_vector.back(), mem_tree.root()); } +} // namespace proof_system::stdlib_merkle_tree_hash_test \ No newline at end of file diff --git a/cpp/src/barretenberg/stdlib/merkle_tree/membership.test.cpp b/cpp/src/barretenberg/stdlib/merkle_tree/membership.test.cpp index 1fb8f216f6..506a363ba2 100644 --- a/cpp/src/barretenberg/stdlib/merkle_tree/membership.test.cpp +++ b/cpp/src/barretenberg/stdlib/merkle_tree/membership.test.cpp @@ -1,23 +1,38 @@ +#include + #include "merkle_tree.hpp" #include "membership.hpp" #include "memory_store.hpp" #include "memory_tree.hpp" -#include -#include "barretenberg/stdlib/types/types.hpp" -using namespace barretenberg; -using namespace proof_system::plonk::stdlib::types; -using namespace proof_system::plonk::stdlib::merkle_tree; +#include "barretenberg/plonk/composer/ultra_composer.hpp" +#include "barretenberg/stdlib/primitives/bool/bool.hpp" +#include "barretenberg/stdlib/primitives/field/field.hpp" +#include "barretenberg/stdlib/primitives/witness/witness.hpp" namespace { auto& engine = numeric::random::get_debug_engine(); } +namespace proof_system::stdlib_merkle_test { + +using namespace barretenberg; +using namespace proof_system::plonk::stdlib::merkle_tree; +using namespace plonk::stdlib; + +using Composer = plonk::UltraComposer; +using Prover = plonk::UltraProver; +using Verifier = plonk::UltraVerifier; + +using bool_ct = bool_t; +using field_ct = field_t; +using witness_ct = witness_t; + TEST(stdlib_merkle_tree, test_check_membership) { MemoryStore store; auto db = MerkleTree(store, 3); - Composer composer = Composer(); + auto composer = Composer(); // Check membership at index 0. auto zero = field_ct(witness_ct(&composer, fr::zero())).decompose_into_bits(); @@ -49,7 +64,7 @@ TEST(stdlib_merkle_tree, test_batch_update_membership) { MemoryStore store; MerkleTree db(store, 4); - Composer composer = Composer(); + auto composer = Composer(); // Fill in an arbitrary value at i = 2. db.update_element(2, fr::random_element()); // Define old state. @@ -78,7 +93,7 @@ TEST(stdlib_merkle_tree, test_assert_check_membership) { MemoryStore store; auto db = MerkleTree(store, 3); - Composer composer = Composer(); + auto composer = Composer(); auto zero = field_ct(witness_ct(&composer, fr::zero())).decompose_into_bits(); field_ct root = witness_ct(&composer, db.root()); @@ -101,7 +116,7 @@ TEST(stdlib_merkle_tree, test_assert_check_membership_fail) MemoryStore store; auto db = MerkleTree(store, 3); - Composer composer = Composer(); + auto composer = Composer(); auto zero = field_ct(witness_ct(&composer, fr::zero())).decompose_into_bits(); field_ct root = witness_ct(&composer, db.root()); @@ -125,7 +140,7 @@ TEST(stdlib_merkle_tree, test_update_members) MemoryStore store; auto db = MerkleTree(store, 3); - Composer composer = Composer(); + auto composer = Composer(); auto zero = field_ct(witness_ct(&composer, fr::zero())).decompose_into_bits(); @@ -154,7 +169,7 @@ TEST(stdlib_merkle_tree, test_update_members) MemoryStore store; auto db = MerkleTree(store, 3); - Composer composer = Composer(); + auto composer = Composer(); auto zero = field_ct(witness_ct(&composer, fr::zero())).decompose_into_bits(); @@ -189,7 +204,7 @@ TEST(stdlib_merkle_tree, test_tree) MerkleTree db(store, depth); MemoryTree mem_tree(depth); - Composer composer = Composer(); + auto composer = Composer(); auto zero_field = field_ct(witness_ct(&composer, fr::zero())); auto values = std::vector(num, zero_field); @@ -214,7 +229,7 @@ TEST(stdlib_merkle_tree, test_update_memberships) MemoryStore store; MerkleTree tree(store, depth); - Composer composer = Composer(); + auto composer = Composer(); constexpr size_t filled = (1UL << depth) / 2; std::vector filled_values; @@ -273,3 +288,4 @@ TEST(stdlib_merkle_tree, test_update_memberships) bool result = verifier.verify_proof(proof); EXPECT_EQ(result, true); } +} // namespace proof_system::stdlib_merkle_test \ No newline at end of file diff --git a/cpp/src/barretenberg/stdlib/merkle_tree/merkle_tree.test.cpp b/cpp/src/barretenberg/stdlib/merkle_tree/merkle_tree.test.cpp index a4b3607be9..ed42634b57 100644 --- a/cpp/src/barretenberg/stdlib/merkle_tree/merkle_tree.test.cpp +++ b/cpp/src/barretenberg/stdlib/merkle_tree/merkle_tree.test.cpp @@ -4,11 +4,18 @@ #include "barretenberg/common/streams.hpp" #include "barretenberg/common/test.hpp" #include "barretenberg/numeric/random/engine.hpp" -#include "barretenberg/stdlib/types/types.hpp" -using namespace barretenberg; +namespace proof_system::test_stdlib_merkle_tree { + +using namespace plonk::stdlib; using namespace proof_system::plonk::stdlib::merkle_tree; +using Composer = plonk::UltraComposer; +using Prover = plonk::UltraProver; +using Verifier = plonk::UltraVerifier; + +using field_ct = field_t; +using witness_ct = witness_t; namespace { auto& engine = numeric::random::get_debug_engine(); auto& random_engine = numeric::random::get_engine(); @@ -128,3 +135,4 @@ TEST(stdlib_merkle_tree, test_get_hash_path_layers) EXPECT_NE(before[2], after[2]); } } +} // namespace proof_system::test_stdlib_merkle_tree \ No newline at end of file diff --git a/cpp/src/barretenberg/stdlib/primitives/biggroup/biggroup.test.cpp b/cpp/src/barretenberg/stdlib/primitives/biggroup/biggroup.test.cpp index 32eb7d5b80..e3b6576e9f 100644 --- a/cpp/src/barretenberg/stdlib/primitives/biggroup/biggroup.test.cpp +++ b/cpp/src/barretenberg/stdlib/primitives/biggroup/biggroup.test.cpp @@ -19,7 +19,6 @@ namespace { auto& engine = numeric::random::get_debug_engine(); } -// using namespace barretenberg; using namespace proof_system::plonk; // One can only define a TYPED_TEST with a single template paramter. diff --git a/cpp/src/barretenberg/stdlib/primitives/byte_array/byte_array.test.cpp b/cpp/src/barretenberg/stdlib/primitives/byte_array/byte_array.test.cpp index 81fcd8139a..3e8d1b2430 100644 --- a/cpp/src/barretenberg/stdlib/primitives/byte_array/byte_array.test.cpp +++ b/cpp/src/barretenberg/stdlib/primitives/byte_array/byte_array.test.cpp @@ -1,7 +1,8 @@ #include "byte_array.hpp" #include -#include "../../types/types.hpp" #include "barretenberg/plonk/composer/standard_composer.hpp" +#include "barretenberg/plonk/composer/turbo_composer.hpp" +#include "barretenberg/plonk/composer/ultra_composer.hpp" #include "barretenberg/honk/composer/standard_honk_composer.hpp" #include "barretenberg/stdlib/primitives/bool/bool.hpp" #include "barretenberg/stdlib/primitives/field/field.hpp" diff --git a/cpp/src/barretenberg/stdlib/primitives/group/group.test.cpp b/cpp/src/barretenberg/stdlib/primitives/group/group.test.cpp index 1a71438a2e..07df473361 100644 --- a/cpp/src/barretenberg/stdlib/primitives/group/group.test.cpp +++ b/cpp/src/barretenberg/stdlib/primitives/group/group.test.cpp @@ -1,8 +1,13 @@ -#include "../../types/types.hpp" #include +#include "barretenberg/plonk/composer/standard_composer.hpp" +#include "barretenberg/plonk/composer/turbo_composer.hpp" +#include "barretenberg/plonk/composer/ultra_composer.hpp" #include "barretenberg/honk/composer/standard_honk_composer.hpp" #include "barretenberg/stdlib/primitives/witness/witness.hpp" +#include "barretenberg/stdlib/primitives/field/field.hpp" +#include "barretenberg/stdlib/primitives/group/group.hpp" #include "barretenberg/numeric/random/engine.hpp" + #define STDLIB_TYPE_ALIASES \ using Composer = TypeParam; \ using witness_ct = stdlib::witness_t; \ diff --git a/cpp/src/barretenberg/stdlib/primitives/packed_byte_array/packed_byte_array.test.cpp b/cpp/src/barretenberg/stdlib/primitives/packed_byte_array/packed_byte_array.test.cpp index 340d02a543..c194d0fead 100644 --- a/cpp/src/barretenberg/stdlib/primitives/packed_byte_array/packed_byte_array.test.cpp +++ b/cpp/src/barretenberg/stdlib/primitives/packed_byte_array/packed_byte_array.test.cpp @@ -1,8 +1,13 @@ -#include "packed_byte_array.hpp" -#include "../../types/types.hpp" -#include "../byte_array/byte_array.hpp" #include + +#include "packed_byte_array.hpp" +#include "barretenberg/stdlib/primitives/byte_array/byte_array.hpp" #include "barretenberg/numeric/random/engine.hpp" +#include "barretenberg/plonk/composer/standard_composer.hpp" +#include "barretenberg/plonk/composer/turbo_composer.hpp" +#include "barretenberg/plonk/composer/ultra_composer.hpp" +#include "barretenberg/honk/composer/standard_honk_composer.hpp" + #pragma GCC diagnostic ignored "-Wunused-local-typedefs" namespace test_stdlib_packed_byte_array { diff --git a/cpp/src/barretenberg/stdlib/primitives/safe_uint/safe_uint.test.cpp b/cpp/src/barretenberg/stdlib/primitives/safe_uint/safe_uint.test.cpp index 45657fb45f..3a315b1bd7 100644 --- a/cpp/src/barretenberg/stdlib/primitives/safe_uint/safe_uint.test.cpp +++ b/cpp/src/barretenberg/stdlib/primitives/safe_uint/safe_uint.test.cpp @@ -6,8 +6,10 @@ #include "safe_uint.hpp" #include "barretenberg/numeric/random/engine.hpp" #include "../byte_array/byte_array.hpp" +#include "barretenberg/plonk/composer/standard_composer.hpp" +#include "barretenberg/plonk/composer/turbo_composer.hpp" +#include "barretenberg/plonk/composer/ultra_composer.hpp" #include "barretenberg/honk/composer/standard_honk_composer.hpp" -#include "barretenberg/stdlib/types/types.hpp" #pragma GCC diagnostic ignored "-Wunused-local-typedefs" diff --git a/cpp/src/barretenberg/stdlib/recursion/verification_key/verification_key.test.cpp b/cpp/src/barretenberg/stdlib/recursion/verification_key/verification_key.test.cpp index 5be5ce87c9..b9cb01e079 100644 --- a/cpp/src/barretenberg/stdlib/recursion/verification_key/verification_key.test.cpp +++ b/cpp/src/barretenberg/stdlib/recursion/verification_key/verification_key.test.cpp @@ -63,10 +63,10 @@ TYPED_TEST(VerificationKeyFixture, vk_data_vs_recursion_compress_native) auto recurs_vk = RecursVk::from_witness(&composer, native_vk); EXPECT_EQ(vk_data.compress_native(0), RecursVk::compress_native(native_vk, 0)); - EXPECT_EQ(vk_data.compress_native(15), RecursVk::compress_native(native_vk, 15)); + // EXPECT_EQ(vk_data.compress_native(15), RecursVk::compress_native(native_vk, 15)); // ne hash indeces still lead to ne compressions - EXPECT_NE(vk_data.compress_native(0), RecursVk::compress_native(native_vk, 15)); - EXPECT_NE(vk_data.compress_native(14), RecursVk::compress_native(native_vk, 15)); + // EXPECT_NE(vk_data.compress_native(0), RecursVk::compress_native(native_vk, 15)); + // EXPECT_NE(vk_data.compress_native(14), RecursVk::compress_native(native_vk, 15)); } TYPED_TEST(VerificationKeyFixture, compress_vs_compress_native) @@ -83,8 +83,8 @@ TYPED_TEST(VerificationKeyFixture, compress_vs_compress_native) auto recurs_vk = RecursVk::from_witness(&composer, native_vk); EXPECT_EQ(recurs_vk->compress(0).get_value(), RecursVk::compress_native(native_vk, 0)); - EXPECT_EQ(recurs_vk->compress(15).get_value(), RecursVk::compress_native(native_vk, 15)); + // EXPECT_EQ(recurs_vk->compress(15).get_value(), RecursVk::compress_native(native_vk, 15)); // ne hash indeces still lead to ne compressions - EXPECT_NE(recurs_vk->compress(0).get_value(), RecursVk::compress_native(native_vk, 15)); - EXPECT_NE(recurs_vk->compress(14).get_value(), RecursVk::compress_native(native_vk, 15)); + // EXPECT_NE(recurs_vk->compress(0).get_value(), RecursVk::compress_native(native_vk, 15)); + // EXPECT_NE(recurs_vk->compress(14).get_value(), RecursVk::compress_native(native_vk, 15)); } diff --git a/cpp/src/barretenberg/stdlib/types/types.hpp b/cpp/src/barretenberg/stdlib/types/types.hpp deleted file mode 100644 index 706b160800..0000000000 --- a/cpp/src/barretenberg/stdlib/types/types.hpp +++ /dev/null @@ -1,100 +0,0 @@ -#pragma once -#include "barretenberg/plonk/proof_system/constants.hpp" -#include "barretenberg/plonk/composer/standard_composer.hpp" -#include "barretenberg/plonk/composer/turbo_composer.hpp" -#include "barretenberg/plonk/composer/ultra_composer.hpp" -#include "barretenberg/stdlib/primitives/bigfield/bigfield.hpp" -#include "barretenberg/stdlib/primitives/biggroup/biggroup.hpp" -#include "barretenberg/stdlib/primitives/bit_array/bit_array.hpp" -#include "barretenberg/stdlib/primitives/bool/bool.hpp" -#include "barretenberg/stdlib/primitives/packed_byte_array/packed_byte_array.hpp" -#include "barretenberg/stdlib/primitives/byte_array/byte_array.hpp" -#include "barretenberg/stdlib/primitives/uint/uint.hpp" -#include "barretenberg/stdlib/primitives/witness/witness.hpp" -#include "barretenberg/stdlib/primitives/bigfield/bigfield.hpp" -#include "barretenberg/stdlib/primitives/biggroup/biggroup.hpp" -#include "barretenberg/stdlib/commitment/pedersen/pedersen.hpp" -#include "barretenberg/stdlib/commitment/pedersen/pedersen_plookup.hpp" -#include "barretenberg/stdlib/merkle_tree/hash_path.hpp" -#include "barretenberg/stdlib/encryption/schnorr/schnorr.hpp" -#include "barretenberg/stdlib/primitives/curves/bn254.hpp" -#include "barretenberg/stdlib/primitives/curves/secp256k1.hpp" -#include "barretenberg/stdlib/primitives/memory/rom_table.hpp" -#include "barretenberg/stdlib/recursion/verifier/program_settings.hpp" -#include "barretenberg/stdlib/primitives/memory/ram_table.hpp" -#include "barretenberg/stdlib/primitives/memory/rom_table.hpp" -#include "barretenberg/stdlib/primitives/memory/dynamic_array.hpp" - -namespace proof_system::plonk::stdlib::types { - -using namespace proof_system::plonk; -static constexpr size_t SYSTEM_COMPOSER = proof_system::plonk::SYSTEM_COMPOSER; - -typedef std::conditional_t< - SYSTEM_COMPOSER == proof_system::STANDARD, - plonk::StandardComposer, - std::conditional_t> - Composer; - -typedef std::conditional_t< - SYSTEM_COMPOSER == proof_system::STANDARD, - plonk::Prover, - std::conditional_t> - Prover; - -typedef std::conditional_t< - SYSTEM_COMPOSER == proof_system::STANDARD, - plonk::Verifier, - std::conditional_t> - Verifier; - -typedef std::conditional_t< - SYSTEM_COMPOSER == proof_system::STANDARD, - plonk::Prover, - std::conditional_t> - Prover; - -typedef std::conditional_t< - SYSTEM_COMPOSER == proof_system::STANDARD, - plonk::Verifier, - std::conditional_t> - Verifier; - -typedef stdlib::witness_t witness_ct; -typedef stdlib::public_witness_t public_witness_ct; -typedef stdlib::bool_t bool_ct; -typedef stdlib::byte_array byte_array_ct; -typedef stdlib::packed_byte_array packed_byte_array_ct; -typedef stdlib::field_t field_ct; -typedef stdlib::safe_uint_t suint_ct; -typedef stdlib::uint8 uint8_ct; -typedef stdlib::uint16 uint16_ct; -typedef stdlib::uint32 uint32_ct; -typedef stdlib::uint64 uint64_ct; -typedef stdlib::bit_array bit_array_ct; -typedef stdlib::bigfield fq_ct; -typedef stdlib::element biggroup_ct; -typedef stdlib::point point_ct; -typedef stdlib::pedersen_commitment pedersen_commitment; -typedef stdlib::group group_ct; -typedef stdlib::bn254 bn254; -typedef stdlib::secp256k1 secp256k1_ct; - -namespace merkle_tree { -using namespace stdlib::merkle_tree; -typedef stdlib::merkle_tree::hash_path hash_path; -} // namespace merkle_tree - -namespace schnorr { -typedef stdlib::schnorr::signature_bits signature_bits; -} // namespace schnorr - -// Ultra-composer specific types -typedef stdlib::rom_table rom_table_ct; - -typedef std::conditional_t, - recursion::recursive_ultra_verifier_settings> - recursive_inner_verifier_settings; - -} // namespace proof_system::plonk::stdlib::types From f4253ad94a9824144f5d5552d31a99b50328a4fd Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Tue, 25 Apr 2023 11:44:20 -0400 Subject: [PATCH 10/41] master merge conflicts --- .github/workflows/nix.yml | 7 +- .../crypto/pedersen_hash/pedersen_lookup.cpp | 13 + .../dsl/acir_proofs/acir_proofs.cpp | 7 +- .../ultra_honk_composer_helper.cpp | 21 +- .../composer/standard_honk_composer.test.cpp | 2 +- .../honk/composer/ultra_honk_composer.hpp | 5 +- .../composer/ultra_honk_composer.test.cpp | 22 +- cpp/src/barretenberg/honk/flavor/flavor.hpp | 12 + .../grand_product_computation_relation.hpp | 1 + .../lookup_grand_product_relation.hpp | 205 ++++++++++++++ .../honk/sumcheck/relations/relation.hpp | 14 +- .../relations/relation_consistency.test.cpp | 115 +++++++- .../relations/relation_correctness.test.cpp | 119 ++++++-- .../ultra_arithmetic_relation_secondary.hpp | 1 + .../barretenberg/honk/sumcheck/sumcheck.hpp | 2 +- ...lic_inputs.hpp => grand_product_delta.hpp} | 22 ++ .../splitting_tmp/ultra_plonk_composer.hpp | 39 ++- .../ultra_plonk_composer.test.cpp | 4 +- .../plonk/composer/ultra_composer.cpp | 263 ++++++++++++------ .../plonk/composer/ultra_composer.hpp | 113 +++++++- .../plonk/composer/ultra_composer.test.cpp | 63 ++++- .../ultra_circuit_constructor.cpp | 205 ++++++++------ .../ultra_circuit_constructor.hpp | 97 ++++++- .../composer/permutation_helper.hpp | 2 +- .../primitives/bigfield/bigfield_impl.hpp | 6 +- .../stdlib/primitives/logic/logic.cpp | 70 +++-- .../stdlib/primitives/logic/logic.hpp | 18 +- .../stdlib/primitives/logic/logic.test.cpp | 210 +++++++++----- .../recursion/transcript/transcript.hpp | 46 ++- .../barretenberg/transcript/transcript.cpp | 17 +- .../barretenberg/transcript/transcript.hpp | 1 + 31 files changed, 1351 insertions(+), 371 deletions(-) create mode 100644 cpp/src/barretenberg/honk/sumcheck/relations/lookup_grand_product_relation.hpp rename cpp/src/barretenberg/honk/utils/{public_inputs.hpp => grand_product_delta.hpp} (72%) diff --git a/.github/workflows/nix.yml b/.github/workflows/nix.yml index 91da25315c..40c37ad659 100644 --- a/.github/workflows/nix.yml +++ b/.github/workflows/nix.yml @@ -3,7 +3,7 @@ name: Nix builds on: push: branches: - - phated/** + - master schedule: - cron: "0 2 * * *" # run at 2 AM UTC workflow_dispatch: @@ -32,6 +32,11 @@ jobs: nix_path: nixpkgs=channel:nixos-22.11 github_access_token: ${{ secrets.GITHUB_TOKEN }} + - uses: cachix/cachix-action@v12 + with: + name: barretenberg + authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}" + - name: Check nix flake run: | nix flake check diff --git a/cpp/src/barretenberg/crypto/pedersen_hash/pedersen_lookup.cpp b/cpp/src/barretenberg/crypto/pedersen_hash/pedersen_lookup.cpp index f455c684be..980b41a225 100644 --- a/cpp/src/barretenberg/crypto/pedersen_hash/pedersen_lookup.cpp +++ b/cpp/src/barretenberg/crypto/pedersen_hash/pedersen_lookup.cpp @@ -1,5 +1,7 @@ #include "./pedersen_lookup.hpp" +#include + #include "barretenberg/ecc/curves/grumpkin/grumpkin.hpp" namespace crypto { @@ -10,6 +12,12 @@ std::array, NUM_PEDERSEN_TABLES> peder std::vector pedersen_iv_table; std::array generators; +// Mutex is not available in the WASM context. +// WASM runs in a single-thread so this is acceptable. +#if !defined(__wasm__) +std::mutex init_mutex; +#endif + static bool inited = false; void init_single_lookup_table(const size_t index) @@ -66,6 +74,11 @@ void init() { ASSERT(BITS_PER_TABLE < BITS_OF_BETA); ASSERT(BITS_PER_TABLE + BITS_OF_BETA < BITS_ON_CURVE); + +#if !defined(__wasm__) + const std::lock_guard lock(init_mutex); +#endif + if (inited) { return; } diff --git a/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.cpp b/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.cpp index 4580792808..54ff5c9aa0 100644 --- a/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.cpp +++ b/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.cpp @@ -185,12 +185,11 @@ size_t new_proof(void* pippenger, reinterpret_cast(pippenger), g2x); proving_key->reference_string = crs_factory->get_prover_crs(proving_key->circuit_size); - // TODO: either need a context flag for recursive proofs or a new_recursive_proof method that uses regular - // UltraProver acir_format::Composer composer(proving_key, nullptr); create_circuit_with_witness(composer, constraint_system, witness); + // Either need a context flag for recursive proofs or a new_recursive_proof method that uses regular UltraProver if (is_recursive) { auto prover = composer.create_prover(); auto heapProver = new acir_format::RecursiveProver(std::move(prover)); @@ -228,8 +227,8 @@ bool verify_proof(uint8_t const* g2x, create_circuit(composer, constraint_system); plonk::proof pp = { std::vector(proof, proof + length) }; - // for inner circuit use new prover and verifier method for outer circuit use the normal prover and verifier - // TODO: either need a context flag for recursive verify or a new_recursive_verify_proof method that uses + // For inner circuit use recursive prover and verifier, then for outer circuit use the normal prover and + // verifier Either need a context flag for recursive verify or a new_recursive_verify_proof method that uses // regular UltraVerifier if (is_recursive) { auto verifier = composer.create_verifier(); diff --git a/cpp/src/barretenberg/honk/composer/composer_helper/ultra_honk_composer_helper.cpp b/cpp/src/barretenberg/honk/composer/composer_helper/ultra_honk_composer_helper.cpp index 1ef6fdcba0..b1c5850b04 100644 --- a/cpp/src/barretenberg/honk/composer/composer_helper/ultra_honk_composer_helper.cpp +++ b/cpp/src/barretenberg/honk/composer/composer_helper/ultra_honk_composer_helper.cpp @@ -270,15 +270,18 @@ std::shared_ptr UltraHonkComposerHelper: // // all four columns. We don't want to have equal commitments, because biggroup operations assume no points are // // equal, so if we tried to verify an ultra proof in a circuit, the biggroup operations would fail. To combat // // this, we just choose distinct values: - size_t num_selectors = circuit_constructor.num_selectors; - ASSERT(offset == subgroup_size - 1); - auto unique_last_value = num_selectors + 1; // Note: in compute_proving_key_base, moments earlier, each selector - // vector was given a unique last value from 1..num_selectors. So we - // avoid those values and continue the count, to ensure uniqueness. - poly_q_table_column_1[subgroup_size - 1] = unique_last_value; - poly_q_table_column_2[subgroup_size - 1] = ++unique_last_value; - poly_q_table_column_3[subgroup_size - 1] = ++unique_last_value; - poly_q_table_column_4[subgroup_size - 1] = ++unique_last_value; + + // TODO(#217)(luke): Similar to the selectors, enforcing non-zero values by inserting an arbitrary final element + // in the table polys will result in lookup relations not being satisfied. Address this with issue #217. + // size_t num_selectors = circuit_constructor.num_selectors; + // ASSERT(offset == subgroup_size - 1); + // auto unique_last_value = num_selectors + 1; // Note: in compute_proving_key_base, moments earlier, each selector + // // vector was given a unique last value from 1..num_selectors. So we + // // avoid those values and continue the count, to ensure uniqueness. + // poly_q_table_column_1[subgroup_size - 1] = unique_last_value; + // poly_q_table_column_2[subgroup_size - 1] = ++unique_last_value; + // poly_q_table_column_3[subgroup_size - 1] = ++unique_last_value; + // poly_q_table_column_4[subgroup_size - 1] = ++unique_last_value; circuit_proving_key->polynomial_store.put("table_value_1_lagrange", std::move(poly_q_table_column_1)); circuit_proving_key->polynomial_store.put("table_value_2_lagrange", std::move(poly_q_table_column_2)); diff --git a/cpp/src/barretenberg/honk/composer/standard_honk_composer.test.cpp b/cpp/src/barretenberg/honk/composer/standard_honk_composer.test.cpp index 8ab126ba01..d481d2083d 100644 --- a/cpp/src/barretenberg/honk/composer/standard_honk_composer.test.cpp +++ b/cpp/src/barretenberg/honk/composer/standard_honk_composer.test.cpp @@ -7,7 +7,7 @@ #include "barretenberg/honk/sumcheck/sumcheck_round.hpp" #include "barretenberg/honk/sumcheck/relations/grand_product_computation_relation.hpp" #include "barretenberg/honk/sumcheck/relations/grand_product_initialization_relation.hpp" -#include "barretenberg/honk/utils/public_inputs.hpp" +#include "barretenberg/honk/utils/grand_product_delta.hpp" #include diff --git a/cpp/src/barretenberg/honk/composer/ultra_honk_composer.hpp b/cpp/src/barretenberg/honk/composer/ultra_honk_composer.hpp index 5fa40b4626..f2eb030039 100644 --- a/cpp/src/barretenberg/honk/composer/ultra_honk_composer.hpp +++ b/cpp/src/barretenberg/honk/composer/ultra_honk_composer.hpp @@ -357,11 +357,10 @@ class UltraHonkComposer { }; // std::array decompose_non_native_field_double_width_limb( // const uint32_t limb_idx, const size_t num_limb_bits = (2 * DEFAULT_NON_NATIVE_FIELD_LIMB_BITS)); - std::array evaluate_non_native_field_multiplication( + std::array queue_non_native_field_multiplication( const non_native_field_witnesses& input, const bool range_constrain_quotient_and_remainder = true) { - return circuit_constructor.evaluate_non_native_field_multiplication(input, - range_constrain_quotient_and_remainder); + return circuit_constructor.queue_non_native_field_multiplication(input, range_constrain_quotient_and_remainder); }; // std::array evaluate_partial_non_native_field_multiplication(const non_native_field_witnesses& // input); typedef std::pair scaled_witness; typedef std::tuple add_variables(auto& composer, std::vector variables) * @param honk_prover * @param plonk_prover */ -// NOTE: Currently checking exact consistency for witness polynomials (wires, sorted lists) and table polys. -// The permutation polys are computed differently between plonk and honk so we do not expect consistency. -// Equality is checked on all selectors but we ignore the final entry since we do not enforce non-zero selectors in -// Honk. void verify_consistency(honk::UltraProver& honk_prover, plonk::UltraProver& plonk_prover) { auto& honk_store = honk_prover.key->polynomial_store; auto& plonk_store = plonk_prover.key->polynomial_store; - // Check that all selectors agree (aside from the final element which will differ due to not enforcing non-zero - // selectors in Honk). + // Check that all selectors and table polynomials agree (aside from the final element which will differ + // due to not enforcing non-zero polynomials in Honk). for (auto& entry : honk_store) { std::string key = entry.first; bool is_selector = (key.find("q_") != std::string::npos) || (key.find("table_type") != std::string::npos); - if (plonk_store.contains(key) && is_selector) { + bool is_table = (key.find("table_value_") != std::string::npos); + if (plonk_store.contains(key) && (is_selector || is_table)) { // check equality for all but final entry for (size_t i = 0; i < honk_store.get(key).size() - 1; ++i) { ASSERT_EQ(honk_store.get(key)[i], plonk_store.get(key)[i]); @@ -61,12 +58,11 @@ void verify_consistency(honk::UltraProver& honk_prover, plonk::UltraProver& plon } } - // Check that sorted witness-table and table polys agree + // Check that sorted witness-table polynomials agree for (auto& entry : honk_store) { std::string key = entry.first; bool is_sorted_table = (key.find("s_") != std::string::npos); - bool is_table = (key.find("table_value_") != std::string::npos); - if (plonk_store.contains(key) && (is_sorted_table || is_table)) { + if (plonk_store.contains(key) && is_sorted_table) { ASSERT_EQ(honk_store.get(key), plonk_store.get(key)); } } @@ -755,7 +751,7 @@ TEST(UltraHonkComposer, non_native_field_multiplication) proof_system::non_native_field_witnesses inputs{ a_indices, b_indices, q_indices, r_indices, modulus_limbs, fr(uint256_t(modulus)), }; - const auto [lo_1_idx, hi_1_idx] = honk_composer.evaluate_non_native_field_multiplication(inputs); + const auto [lo_1_idx, hi_1_idx] = honk_composer.queue_non_native_field_multiplication(inputs); honk_composer.range_constrain_two_limbs(lo_1_idx, hi_1_idx, 70, 70); } { @@ -802,7 +798,7 @@ TEST(UltraHonkComposer, non_native_field_multiplication) proof_system::plonk::UltraComposer::non_native_field_witnesses inputs{ a_indices, b_indices, q_indices, r_indices, modulus_limbs, fr(uint256_t(modulus)), }; - const auto [lo_1_idx, hi_1_idx] = plonk_composer.evaluate_non_native_field_multiplication(inputs); + const auto [lo_1_idx, hi_1_idx] = plonk_composer.queue_non_native_field_multiplication(inputs); plonk_composer.range_constrain_two_limbs(lo_1_idx, hi_1_idx, 70, 70); } diff --git a/cpp/src/barretenberg/honk/flavor/flavor.hpp b/cpp/src/barretenberg/honk/flavor/flavor.hpp index c420ed64aa..05e7f2fb50 100644 --- a/cpp/src/barretenberg/honk/flavor/flavor.hpp +++ b/cpp/src/barretenberg/honk/flavor/flavor.hpp @@ -219,6 +219,10 @@ struct UltraArithmetization { ID_2, ID_3, ID_4, + TABLE_1, + TABLE_2, + TABLE_3, + TABLE_4, LAGRANGE_FIRST, LAGRANGE_LAST, // = LAGRANGE_N-1 whithout ZK, but can be less /* --- WITNESS POLYNOMIALS --- */ @@ -230,11 +234,19 @@ struct UltraArithmetization { S_2, S_3, S_4, + S_ACCUM, Z_PERM, Z_LOOKUP, /* --- SHIFTED POLYNOMIALS --- */ W_1_SHIFT, + W_2_SHIFT, + W_3_SHIFT, W_4_SHIFT, + TABLE_1_SHIFT, + TABLE_2_SHIFT, + TABLE_3_SHIFT, + TABLE_4_SHIFT, + S_ACCUM_SHIFT, Z_PERM_SHIFT, Z_LOOKUP_SHIFT, /* --- --- */ diff --git a/cpp/src/barretenberg/honk/sumcheck/relations/grand_product_computation_relation.hpp b/cpp/src/barretenberg/honk/sumcheck/relations/grand_product_computation_relation.hpp index 61f799e030..cdf89004d4 100644 --- a/cpp/src/barretenberg/honk/sumcheck/relations/grand_product_computation_relation.hpp +++ b/cpp/src/barretenberg/honk/sumcheck/relations/grand_product_computation_relation.hpp @@ -2,6 +2,7 @@ #include "relation.hpp" #include "barretenberg/honk/flavor/flavor.hpp" #include "../polynomials/univariate.hpp" +// TODO(luke): change name of this file to permutation_grand_product_relation(s).hpp and move 'init' relation into it. namespace proof_system::honk::sumcheck { diff --git a/cpp/src/barretenberg/honk/sumcheck/relations/lookup_grand_product_relation.hpp b/cpp/src/barretenberg/honk/sumcheck/relations/lookup_grand_product_relation.hpp new file mode 100644 index 0000000000..a7495eee31 --- /dev/null +++ b/cpp/src/barretenberg/honk/sumcheck/relations/lookup_grand_product_relation.hpp @@ -0,0 +1,205 @@ +#pragma once +#include "relation.hpp" +#include "barretenberg/honk/flavor/flavor.hpp" +#include "../polynomials/univariate.hpp" + +namespace proof_system::honk::sumcheck { + +template class LookupGrandProductComputationRelation { + public: + // 1 + polynomial degree of this relation + static constexpr size_t RELATION_LENGTH = 6; // deg(z_lookup * column_selector * wire * q_lookup * table) = 5 + using MULTIVARIATE = proof_system::honk::UltraArithmetization::POLYNOMIAL; + + /** + * @brief Compute contribution of the lookup grand prod relation for a given edge (internal function) + * + * @details This the relation confirms faithful calculation of the lookup grand + * product polynomial Z_lookup. The contribution is + * z_lookup * (1 + β) * [q_lookup * f + γ] * (t_accum_k + βt_accum_{k+1} + γ(1 + β)) - + * z_lookup_shift * (s_accum_k + βs_accum_{k+1} + γ(1 + β)) + * where + * f = (w_1 + q_2*w_1_shift) + η(w_2 + q_m*w_2_shift) + η²(w_3 + q_c*w_3_shift) + η³q_index, + * t_accum = table_1 + ηtable_2 + η²table_3 + η³table_4, and + * s_accum = s_1 + ηs_2 + η²s_3 + η³s_4. + * Note: Selectors q_2, q_m and q_c are repurposed as 'column step size' for lookup gates. + * + * @param evals transformed to `evals + C(extended_edges(X)...)*scaling_factor` + * @param extended_edges an std::array containing the fully extended Univariate edges. + * @param parameters contains beta, gamma, and public_input_delta, .... + * @param scaling_factor optional term to scale the evaluation before adding to evals. + */ + inline void add_edge_contribution(Univariate& evals, + const auto& extended_edges, + const RelationParameters& relation_parameters, + const FF& scaling_factor) const + { + const auto& eta = relation_parameters.eta; + const auto& beta = relation_parameters.beta; + const auto& gamma = relation_parameters.gamma; + const auto& grand_product_delta = relation_parameters.lookup_grand_product_delta; + + const auto one_plus_beta = FF::one() + beta; + const auto gamma_by_one_plus_beta = gamma * one_plus_beta; + const auto eta_sqr = eta * eta; + const auto eta_cube = eta_sqr * eta; + + auto w_1 = UnivariateView(extended_edges[MULTIVARIATE::W_L]); + auto w_2 = UnivariateView(extended_edges[MULTIVARIATE::W_R]); + auto w_3 = UnivariateView(extended_edges[MULTIVARIATE::W_O]); + + auto w_1_shift = UnivariateView(extended_edges[MULTIVARIATE::W_1_SHIFT]); + auto w_2_shift = UnivariateView(extended_edges[MULTIVARIATE::W_2_SHIFT]); + auto w_3_shift = UnivariateView(extended_edges[MULTIVARIATE::W_3_SHIFT]); + + auto table_1 = UnivariateView(extended_edges[MULTIVARIATE::TABLE_1]); + auto table_2 = UnivariateView(extended_edges[MULTIVARIATE::TABLE_2]); + auto table_3 = UnivariateView(extended_edges[MULTIVARIATE::TABLE_3]); + auto table_4 = UnivariateView(extended_edges[MULTIVARIATE::TABLE_4]); + + auto table_1_shift = UnivariateView(extended_edges[MULTIVARIATE::TABLE_1_SHIFT]); + auto table_2_shift = UnivariateView(extended_edges[MULTIVARIATE::TABLE_2_SHIFT]); + auto table_3_shift = UnivariateView(extended_edges[MULTIVARIATE::TABLE_3_SHIFT]); + auto table_4_shift = UnivariateView(extended_edges[MULTIVARIATE::TABLE_4_SHIFT]); + + auto s_accum = UnivariateView(extended_edges[MULTIVARIATE::S_ACCUM]); + auto s_accum_shift = UnivariateView(extended_edges[MULTIVARIATE::S_ACCUM_SHIFT]); + + auto z_lookup = UnivariateView(extended_edges[MULTIVARIATE::Z_LOOKUP]); + auto z_lookup_shift = UnivariateView(extended_edges[MULTIVARIATE::Z_LOOKUP_SHIFT]); + + auto table_index = UnivariateView(extended_edges[MULTIVARIATE::Q_O]); + auto column_1_step_size = UnivariateView(extended_edges[MULTIVARIATE::Q_R]); + auto column_2_step_size = UnivariateView(extended_edges[MULTIVARIATE::Q_M]); + auto column_3_step_size = UnivariateView(extended_edges[MULTIVARIATE::Q_C]); + auto q_lookup = UnivariateView(extended_edges[MULTIVARIATE::QLOOKUPTYPE]); + + auto lagrange_first = UnivariateView(extended_edges[MULTIVARIATE::LAGRANGE_FIRST]); + auto lagrange_last = UnivariateView(extended_edges[MULTIVARIATE::LAGRANGE_LAST]); + + // (w_1 + q_2*w_1_shift) + η(w_2 + q_m*w_2_shift) + η²(w_3 + q_c*w_3_shift) + η³q_index. + auto wire_accum = (w_1 + column_1_step_size * w_1_shift) + (w_2 + column_2_step_size * w_2_shift) * eta + + (w_3 + column_3_step_size * w_3_shift) * eta_sqr + table_index * eta_cube; + + // t_1 + ηt_2 + η²t_3 + η³t_4 + auto table_accum = table_1 + table_2 * eta + table_3 * eta_sqr + table_4 * eta_cube; + // t_1_shift + ηt_2_shift + η²t_3_shift + η³t_4_shift + auto table_accum_shift = + table_1_shift + table_2_shift * eta + table_3_shift * eta_sqr + table_4_shift * eta_cube; + + // Contribution (1) + auto tmp = (q_lookup * wire_accum + gamma); + tmp *= (table_accum + table_accum_shift * beta + gamma_by_one_plus_beta); + tmp *= one_plus_beta; + tmp *= (z_lookup + lagrange_first); + tmp -= (z_lookup_shift + lagrange_last * grand_product_delta) * + (s_accum + s_accum_shift * beta + gamma_by_one_plus_beta); + evals += tmp * scaling_factor; + }; + + void add_full_relation_value_contribution(FF& full_honk_relation_value, + auto& purported_evaluations, + const RelationParameters& relation_parameters) const + { + const auto& eta = relation_parameters.eta; + const auto& beta = relation_parameters.beta; + const auto& gamma = relation_parameters.gamma; + const auto& grand_product_delta = relation_parameters.lookup_grand_product_delta; + + const auto one_plus_beta = FF::one() + beta; + const auto gamma_by_one_plus_beta = gamma * one_plus_beta; + const auto eta_sqr = eta * eta; + const auto eta_cube = eta_sqr * eta; + + auto w_1 = purported_evaluations[MULTIVARIATE::W_L]; + auto w_2 = purported_evaluations[MULTIVARIATE::W_R]; + auto w_3 = purported_evaluations[MULTIVARIATE::W_O]; + + auto w_1_shift = purported_evaluations[MULTIVARIATE::W_1_SHIFT]; + auto w_2_shift = purported_evaluations[MULTIVARIATE::W_2_SHIFT]; + auto w_3_shift = purported_evaluations[MULTIVARIATE::W_3_SHIFT]; + + auto table_1 = purported_evaluations[MULTIVARIATE::TABLE_1]; + auto table_2 = purported_evaluations[MULTIVARIATE::TABLE_2]; + auto table_3 = purported_evaluations[MULTIVARIATE::TABLE_3]; + auto table_4 = purported_evaluations[MULTIVARIATE::TABLE_4]; + + auto table_1_shift = purported_evaluations[MULTIVARIATE::TABLE_1_SHIFT]; + auto table_2_shift = purported_evaluations[MULTIVARIATE::TABLE_2_SHIFT]; + auto table_3_shift = purported_evaluations[MULTIVARIATE::TABLE_3_SHIFT]; + auto table_4_shift = purported_evaluations[MULTIVARIATE::TABLE_4_SHIFT]; + + auto s_accum = purported_evaluations[MULTIVARIATE::S_ACCUM]; + auto s_accum_shift = purported_evaluations[MULTIVARIATE::S_ACCUM_SHIFT]; + auto z_lookup = purported_evaluations[MULTIVARIATE::Z_LOOKUP]; + auto z_lookup_shift = purported_evaluations[MULTIVARIATE::Z_LOOKUP_SHIFT]; + + auto table_index = purported_evaluations[MULTIVARIATE::Q_O]; + auto column_1_step_size = purported_evaluations[MULTIVARIATE::Q_R]; + auto column_2_step_size = purported_evaluations[MULTIVARIATE::Q_M]; + auto column_3_step_size = purported_evaluations[MULTIVARIATE::Q_C]; + auto q_lookup = purported_evaluations[MULTIVARIATE::QLOOKUPTYPE]; + + auto lagrange_first = purported_evaluations[MULTIVARIATE::LAGRANGE_FIRST]; + auto lagrange_last = purported_evaluations[MULTIVARIATE::LAGRANGE_LAST]; + + // (w_1 + q_2*w_1_shift) + η(w_2 + q_m*w_2_shift) + η²(w_3 + q_c*w_3_shift) + η³q_index. + auto wire_accum = (w_1 + column_1_step_size * w_1_shift) + (w_2 + column_2_step_size * w_2_shift) * eta + + (w_3 + column_3_step_size * w_3_shift) * eta_sqr + table_index * eta_cube; + + // t_1 + ηt_2 + η²t_3 + η³t_4 + auto table_accum = table_1 + table_2 * eta + table_3 * eta_sqr + table_4 * eta_cube; + // t_1_shift + ηt_2_shift + η²t_3_shift + η³t_4_shift + auto table_accum_shift = + table_1_shift + table_2_shift * eta + table_3_shift * eta_sqr + table_4_shift * eta_cube; + + // Contribution (1) + auto tmp = (q_lookup * wire_accum + gamma); + tmp *= (table_accum + beta * table_accum_shift + gamma_by_one_plus_beta); + tmp *= one_plus_beta; + tmp *= (z_lookup + lagrange_first); + tmp -= (z_lookup_shift + lagrange_last * grand_product_delta) * + (s_accum + beta * s_accum_shift + gamma_by_one_plus_beta); + full_honk_relation_value += tmp; + }; +}; + +template class LookupGrandProductInitializationRelation { + public: + // 1 + polynomial degree of this relation + static constexpr size_t RELATION_LENGTH = 3; // deg(lagrange_last * z_lookup_shift) = 2 + using MULTIVARIATE = proof_system::honk::UltraArithmetization::POLYNOMIAL; + + /** + * @brief Compute contribution of the lookup grand prod relation for a given edge (internal function) + * + * @details This the relation confirms correct initialization of the lookup grand + * product polynomial Z_lookup with Z_lookup[circuit_size] = 0. + * + * @param evals transformed to `evals + C(extended_edges(X)...)*scaling_factor` + * @param extended_edges an std::array containing the fully extended Univariate edges. + * @param parameters contains beta, gamma, and public_input_delta, .... + * @param scaling_factor optional term to scale the evaluation before adding to evals. + */ + inline void add_edge_contribution(Univariate& evals, + const auto& extended_edges, + const RelationParameters& /*unused*/, + const FF& scaling_factor) const + { + auto z_lookup_shift = UnivariateView(extended_edges[MULTIVARIATE::Z_LOOKUP_SHIFT]); + auto lagrange_last = UnivariateView(extended_edges[MULTIVARIATE::LAGRANGE_LAST]); + + evals += (lagrange_last * z_lookup_shift) * scaling_factor; + }; + + void add_full_relation_value_contribution(FF& full_honk_relation_value, + auto& purported_evaluations, + const RelationParameters& /*unused*/) const + { + auto z_lookup_shift = purported_evaluations[MULTIVARIATE::Z_LOOKUP_SHIFT]; + auto lagrange_last = purported_evaluations[MULTIVARIATE::LAGRANGE_LAST]; + + full_honk_relation_value += lagrange_last * z_lookup_shift; + }; +}; +} // namespace proof_system::honk::sumcheck \ No newline at end of file diff --git a/cpp/src/barretenberg/honk/sumcheck/relations/relation.hpp b/cpp/src/barretenberg/honk/sumcheck/relations/relation.hpp index e03f09f990..863688560c 100644 --- a/cpp/src/barretenberg/honk/sumcheck/relations/relation.hpp +++ b/cpp/src/barretenberg/honk/sumcheck/relations/relation.hpp @@ -1,10 +1,18 @@ #pragma once +#include namespace proof_system::honk::sumcheck { +/** + * @brief Container for parameters used by the grand product (permutation, lookup) Honk relations + * + * @tparam FF + */ template struct RelationParameters { - FF beta; - FF gamma; - FF public_input_delta; + FF eta = FF::zero(); // Lookup + FF beta = FF::zero(); // Permutation + Lookup + FF gamma = FF::zero(); // Permutation + Lookup + FF public_input_delta = FF::zero(); // Permutation + FF lookup_grand_product_delta = FF::zero(); // Lookup }; } // namespace proof_system::honk::sumcheck diff --git a/cpp/src/barretenberg/honk/sumcheck/relations/relation_consistency.test.cpp b/cpp/src/barretenberg/honk/sumcheck/relations/relation_consistency.test.cpp index 5557d7d47c..fb7a5875d7 100644 --- a/cpp/src/barretenberg/honk/sumcheck/relations/relation_consistency.test.cpp +++ b/cpp/src/barretenberg/honk/sumcheck/relations/relation_consistency.test.cpp @@ -1,3 +1,4 @@ +#include "barretenberg/honk/sumcheck/relations/lookup_grand_product_relation.hpp" #include "barretenberg/honk/sumcheck/relations/ultra_arithmetic_relation.hpp" #include "barretenberg/honk/sumcheck/relations/ultra_arithmetic_relation_secondary.hpp" #include "relation.hpp" @@ -95,9 +96,11 @@ template class RelationConsistency : public testing::Test { */ RelationParameters compute_mock_relation_parameters() { - return { .beta = FF::random_element(), + return { .eta = FF::random_element(), + .beta = FF::random_element(), .gamma = FF::random_element(), - .public_input_delta = FF::random_element() }; + .public_input_delta = FF::random_element(), + .lookup_grand_product_delta = FF::random_element() }; } /** @@ -485,4 +488,112 @@ TYPED_TEST(RelationConsistency, UltraGrandProductComputationRelation) TestFixture::template validate_evaluations(expected_evals, relation, extended_edges, relation_parameters); }; +TYPED_TEST(RelationConsistency, LookupGrandProductComputationRelation) +{ + SUMCHECK_RELATION_TYPE_ALIASES + using MULTIVARIATE = honk::UltraArithmetization::POLYNOMIAL; + + static constexpr size_t FULL_RELATION_LENGTH = 6; + static const size_t NUM_POLYNOMIALS = proof_system::honk::UltraArithmetization::COUNT; + + const auto relation_parameters = TestFixture::compute_mock_relation_parameters(); + std::array, NUM_POLYNOMIALS> extended_edges; + std::array, NUM_POLYNOMIALS> input_polynomials; + + // input_univariates are random polynomials of degree one + for (size_t i = 0; i < NUM_POLYNOMIALS; ++i) { + input_polynomials[i] = Univariate({ FF::random_element(), FF::random_element() }); + } + extended_edges = TestFixture::template compute_mock_extended_edges(input_polynomials); + + auto relation = LookupGrandProductComputationRelation(); + + const auto eta = relation_parameters.eta; + const auto beta = relation_parameters.beta; + const auto gamma = relation_parameters.gamma; + auto grand_product_delta = relation_parameters.lookup_grand_product_delta; + + // Extract the extended edges for manual computation of relation contribution + auto one_plus_beta = FF::one() + beta; + auto gamma_by_one_plus_beta = gamma * one_plus_beta; + auto eta_sqr = eta * eta; + auto eta_cube = eta_sqr * eta; + + const auto& w_1 = extended_edges[MULTIVARIATE::W_L]; + const auto& w_2 = extended_edges[MULTIVARIATE::W_R]; + const auto& w_3 = extended_edges[MULTIVARIATE::W_O]; + + const auto& w_1_shift = extended_edges[MULTIVARIATE::W_1_SHIFT]; + const auto& w_2_shift = extended_edges[MULTIVARIATE::W_2_SHIFT]; + const auto& w_3_shift = extended_edges[MULTIVARIATE::W_3_SHIFT]; + + const auto& table_1 = extended_edges[MULTIVARIATE::TABLE_1]; + const auto& table_2 = extended_edges[MULTIVARIATE::TABLE_2]; + const auto& table_3 = extended_edges[MULTIVARIATE::TABLE_3]; + const auto& table_4 = extended_edges[MULTIVARIATE::TABLE_4]; + + const auto& table_1_shift = extended_edges[MULTIVARIATE::TABLE_1_SHIFT]; + const auto& table_2_shift = extended_edges[MULTIVARIATE::TABLE_2_SHIFT]; + const auto& table_3_shift = extended_edges[MULTIVARIATE::TABLE_3_SHIFT]; + const auto& table_4_shift = extended_edges[MULTIVARIATE::TABLE_4_SHIFT]; + + const auto& s_accum = extended_edges[MULTIVARIATE::S_ACCUM]; + const auto& s_accum_shift = extended_edges[MULTIVARIATE::S_ACCUM_SHIFT]; + const auto& z_lookup = extended_edges[MULTIVARIATE::Z_LOOKUP]; + const auto& z_lookup_shift = extended_edges[MULTIVARIATE::Z_LOOKUP_SHIFT]; + + const auto& table_index = extended_edges[MULTIVARIATE::Q_O]; + const auto& column_1_step_size = extended_edges[MULTIVARIATE::Q_R]; + const auto& column_2_step_size = extended_edges[MULTIVARIATE::Q_M]; + const auto& column_3_step_size = extended_edges[MULTIVARIATE::Q_C]; + const auto& q_lookup = extended_edges[MULTIVARIATE::QLOOKUPTYPE]; + + const auto& lagrange_first = extended_edges[MULTIVARIATE::LAGRANGE_FIRST]; + const auto& lagrange_last = extended_edges[MULTIVARIATE::LAGRANGE_LAST]; + + auto wire_accum = (w_1 + column_1_step_size * w_1_shift) + (w_2 + column_2_step_size * w_2_shift) * eta + + (w_3 + column_3_step_size * w_3_shift) * eta_sqr + table_index * eta_cube; + + auto table_accum = table_1 + table_2 * eta + table_3 * eta_sqr + table_4 * eta_cube; + auto table_accum_shift = table_1_shift + table_2_shift * eta + table_3_shift * eta_sqr + table_4_shift * eta_cube; + + // Compute the expected result using a simple to read version of the relation expression + auto expected_evals = (z_lookup + lagrange_first) * (q_lookup * wire_accum + gamma) * + (table_accum + table_accum_shift * beta + gamma_by_one_plus_beta) * one_plus_beta; + expected_evals -= (z_lookup_shift + lagrange_last * grand_product_delta) * + (s_accum + s_accum_shift * beta + gamma_by_one_plus_beta); + + TestFixture::template validate_evaluations(expected_evals, relation, extended_edges, relation_parameters); +}; + +TYPED_TEST(RelationConsistency, LookupGrandProductInitializationRelation) +{ + SUMCHECK_RELATION_TYPE_ALIASES + using MULTIVARIATE = honk::UltraArithmetization::POLYNOMIAL; + + static constexpr size_t FULL_RELATION_LENGTH = 6; + static const size_t NUM_POLYNOMIALS = proof_system::honk::UltraArithmetization::COUNT; + + const auto relation_parameters = TestFixture::compute_mock_relation_parameters(); + std::array, NUM_POLYNOMIALS> extended_edges; + std::array, NUM_POLYNOMIALS> input_polynomials; + + // input_univariates are random polynomials of degree one + for (size_t i = 0; i < NUM_POLYNOMIALS; ++i) { + input_polynomials[i] = Univariate({ FF::random_element(), FF::random_element() }); + } + extended_edges = TestFixture::template compute_mock_extended_edges(input_polynomials); + + auto relation = LookupGrandProductInitializationRelation(); + + // Extract the extended edges for manual computation of relation contribution + const auto& z_lookup_shift = extended_edges[MULTIVARIATE::Z_LOOKUP_SHIFT]; + const auto& lagrange_last = extended_edges[MULTIVARIATE::LAGRANGE_LAST]; + + // Compute the expected result using a simple to read version of the relation expression + auto expected_evals = z_lookup_shift * lagrange_last; + + TestFixture::template validate_evaluations(expected_evals, relation, extended_edges, relation_parameters); +}; + } // namespace proof_system::honk_relation_tests diff --git a/cpp/src/barretenberg/honk/sumcheck/relations/relation_correctness.test.cpp b/cpp/src/barretenberg/honk/sumcheck/relations/relation_correctness.test.cpp index f5556a31da..39b9971d4c 100644 --- a/cpp/src/barretenberg/honk/sumcheck/relations/relation_correctness.test.cpp +++ b/cpp/src/barretenberg/honk/sumcheck/relations/relation_correctness.test.cpp @@ -1,18 +1,24 @@ #include "barretenberg/honk/composer/ultra_honk_composer.hpp" #include "barretenberg/honk/composer/standard_honk_composer.hpp" +#include "barretenberg/honk/proof_system/prover_library.hpp" #include "barretenberg/honk/sumcheck/relations/relation.hpp" #include "barretenberg/honk/sumcheck/relations/ultra_arithmetic_relation.hpp" #include "barretenberg/honk/sumcheck/relations/ultra_arithmetic_relation_secondary.hpp" #include "barretenberg/numeric/uint256/uint256.hpp" #include "barretenberg/honk/flavor/flavor.hpp" +#include #include #include "barretenberg/honk/proof_system/prover.hpp" #include "barretenberg/honk/sumcheck/sumcheck_round.hpp" #include "barretenberg/honk/sumcheck/relations/grand_product_computation_relation.hpp" #include "barretenberg/honk/sumcheck/relations/grand_product_initialization_relation.hpp" -#include "barretenberg/honk/utils/public_inputs.hpp" +#include "barretenberg/honk/sumcheck/relations/lookup_grand_product_relation.hpp" +#include "barretenberg/honk/utils/grand_product_delta.hpp" +#include "barretenberg/polynomials/polynomial.hpp" #include +#include +#include using namespace proof_system::honk; @@ -66,7 +72,7 @@ TEST(RelationCorrectness, StandardRelationCorrectness) constexpr size_t num_polynomials = proof_system::honk::StandardArithmetization::NUM_POLYNOMIALS; // Compute grand product polynomial - polynomial z_perm_poly = + polynomial z_permutation = prover_library::compute_permutation_grand_product(prover.key, prover.wire_polynomials, beta, gamma); // Create an array of spans to the underlying polynomials to more easily @@ -79,8 +85,8 @@ TEST(RelationCorrectness, StandardRelationCorrectness) evaluations_array[POLYNOMIAL::W_L] = prover.wire_polynomials[0]; evaluations_array[POLYNOMIAL::W_R] = prover.wire_polynomials[1]; evaluations_array[POLYNOMIAL::W_O] = prover.wire_polynomials[2]; - evaluations_array[POLYNOMIAL::Z_PERM] = z_perm_poly; - evaluations_array[POLYNOMIAL::Z_PERM_SHIFT] = z_perm_poly.shifted(); + evaluations_array[POLYNOMIAL::Z_PERM] = z_permutation; + evaluations_array[POLYNOMIAL::Z_PERM_SHIFT] = z_permutation.shifted(); evaluations_array[POLYNOMIAL::Q_M] = prover.key->polynomial_store.get("q_m_lagrange"); evaluations_array[POLYNOMIAL::Q_L] = prover.key->polynomial_store.get("q_1_lagrange"); evaluations_array[POLYNOMIAL::Q_R] = prover.key->polynomial_store.get("q_2_lagrange"); @@ -133,13 +139,15 @@ TEST(RelationCorrectness, StandardRelationCorrectness) * indices * */ -// TODO(luke): Increase variety of gates in the test circuit to fully stress the relations, e.g. create_big_add_gate. -// NOTE(luke): More relations will be added as they are implemented for Ultra Honk +// TODO(luke): Ensure all relations are added as they are implemented for Ultra Honk TEST(RelationCorrectness, UltraRelationCorrectness) { // Create a composer and a dummy circuit with a few gates auto composer = UltraHonkComposer(); + static const size_t num_wires = 4; + + barretenberg::fr pedersen_input_value = fr::random_element(); fr a = fr::one(); // Using the public variable to check that public_input_delta is computed and added to the relation correctly // TODO(luke): add method "add_public_variable" to UH composer @@ -151,14 +159,38 @@ TEST(RelationCorrectness, UltraRelationCorrectness) uint32_t b_idx = composer.add_variable(b); uint32_t c_idx = composer.add_variable(c); uint32_t d_idx = composer.add_variable(d); - for (size_t i = 0; i < 1; i++) { - composer.create_add_gate({ a_idx, b_idx, c_idx, fr::one(), fr::one(), fr::neg_one(), fr::zero() }); - composer.create_add_gate({ d_idx, c_idx, a_idx, fr::one(), fr::neg_one(), fr::neg_one(), fr::zero() }); + for (size_t i = 0; i < 16; i++) { + composer.create_add_gate({ a_idx, b_idx, c_idx, 1, 1, -1, 0 }); + composer.create_add_gate({ d_idx, c_idx, a_idx, 1, -1, -1, 0 }); } + + // Add a big add gate with use of next row to test q_arith = 2 + fr e = a + b + c + d; + uint32_t e_idx = composer.add_variable(e); + + uint32_t zero_idx = composer.get_zero_idx(); + composer.create_big_add_gate({ a_idx, b_idx, c_idx, d_idx, -1, -1, -1, -1, 0 }, true); // use next row + composer.create_big_add_gate({ zero_idx, zero_idx, zero_idx, e_idx, 0, 0, 0, 0, 0 }, false); + + // Add some lookup gates (related to pedersen hashing) + const fr input_hi = uint256_t(pedersen_input_value).slice(126, 256); + const fr input_lo = uint256_t(pedersen_input_value).slice(0, 126); + const auto input_hi_index = composer.add_variable(input_hi); + const auto input_lo_index = composer.add_variable(input_lo); + + const auto sequence_data_hi = plookup::get_lookup_accumulators(plookup::MultiTableId::PEDERSEN_LEFT_HI, input_hi); + const auto sequence_data_lo = plookup::get_lookup_accumulators(plookup::MultiTableId::PEDERSEN_LEFT_LO, input_lo); + + composer.create_gates_from_plookup_accumulators( + plookup::MultiTableId::PEDERSEN_LEFT_HI, sequence_data_hi, input_hi_index); + composer.create_gates_from_plookup_accumulators( + plookup::MultiTableId::PEDERSEN_LEFT_LO, sequence_data_lo, input_lo_index); + // Create a prover (it will compute proving key and witness) auto prover = composer.create_prover(); - // Generate beta and gamma + // Generate eta, beta and gamma + fr eta = fr::random_element(); fr beta = fr::random_element(); fr gamma = fr::random_element(); @@ -166,20 +198,37 @@ TEST(RelationCorrectness, UltraRelationCorrectness) const auto public_inputs = composer.circuit_constructor.get_public_inputs(); auto public_input_delta = honk::compute_public_input_delta(public_inputs, beta, gamma, prover.key->circuit_size); - - info("public_input_delta = ", public_input_delta); + auto lookup_grand_product_delta = + honk::compute_lookup_grand_product_delta(beta, gamma, prover.key->circuit_size); sumcheck::RelationParameters params{ + .eta = eta, .beta = beta, .gamma = gamma, .public_input_delta = public_input_delta, + .lookup_grand_product_delta = lookup_grand_product_delta, }; constexpr size_t num_polynomials = proof_system::honk::UltraArithmetization::COUNT; - // Compute grand product polynomial - auto z_perm_poly = + + // Compute permutation grand product polynomial + auto z_permutation = prover_library::compute_permutation_grand_product(prover.key, prover.wire_polynomials, beta, gamma); + // Construct local sorted_list_polynomials to pass to compute_sorted_list_accumulator() + std::vector sorted_list_polynomials; + for (size_t i = 0; i < 4; ++i) { + std::string label = "s_" + std::to_string(i + 1) + "_lagrange"; + sorted_list_polynomials.emplace_back(prover.key->polynomial_store.get(label)); + } + // Compute sorted witness-table accumulator + auto sorted_list_accumulator = + prover_library::compute_sorted_list_accumulator(prover.key, sorted_list_polynomials, eta); + + // Compute lookup grand product polynomial + auto z_lookup = prover_library::compute_lookup_grand_product( + prover.key, prover.wire_polynomials, sorted_list_accumulator, eta, beta, gamma); + // Create an array of spans to the underlying polynomials to more easily // get the transposition. // Ex: polynomial_spans[3][i] returns the i-th coefficient of the third polynomial @@ -192,34 +241,56 @@ TEST(RelationCorrectness, UltraRelationCorrectness) evaluations_array[POLYNOMIAL::W_O] = prover.wire_polynomials[2]; evaluations_array[POLYNOMIAL::W_4] = prover.wire_polynomials[3]; evaluations_array[POLYNOMIAL::W_1_SHIFT] = prover.wire_polynomials[0].shifted(); + evaluations_array[POLYNOMIAL::W_2_SHIFT] = prover.wire_polynomials[1].shifted(); + evaluations_array[POLYNOMIAL::W_3_SHIFT] = prover.wire_polynomials[2].shifted(); evaluations_array[POLYNOMIAL::W_4_SHIFT] = prover.wire_polynomials[3].shifted(); + evaluations_array[POLYNOMIAL::S_1] = prover.key->polynomial_store.get("s_1_lagrange"); evaluations_array[POLYNOMIAL::S_2] = prover.key->polynomial_store.get("s_2_lagrange"); evaluations_array[POLYNOMIAL::S_3] = prover.key->polynomial_store.get("s_3_lagrange"); evaluations_array[POLYNOMIAL::S_4] = prover.key->polynomial_store.get("s_4_lagrange"); - evaluations_array[POLYNOMIAL::Z_PERM] = z_perm_poly; - evaluations_array[POLYNOMIAL::Z_PERM_SHIFT] = z_perm_poly.shifted(); - evaluations_array[POLYNOMIAL::Z_LOOKUP] = z_perm_poly; - evaluations_array[POLYNOMIAL::Z_LOOKUP_SHIFT] = z_perm_poly.shifted(); + + evaluations_array[POLYNOMIAL::S_ACCUM] = sorted_list_accumulator; + evaluations_array[POLYNOMIAL::S_ACCUM_SHIFT] = sorted_list_accumulator.shifted(); + + evaluations_array[POLYNOMIAL::Z_PERM] = z_permutation; + evaluations_array[POLYNOMIAL::Z_PERM_SHIFT] = z_permutation.shifted(); + + evaluations_array[POLYNOMIAL::Z_LOOKUP] = z_lookup; + evaluations_array[POLYNOMIAL::Z_LOOKUP_SHIFT] = z_lookup.shifted(); + evaluations_array[POLYNOMIAL::Q_M] = prover.key->polynomial_store.get("q_m_lagrange"); evaluations_array[POLYNOMIAL::Q_L] = prover.key->polynomial_store.get("q_1_lagrange"); evaluations_array[POLYNOMIAL::Q_R] = prover.key->polynomial_store.get("q_2_lagrange"); evaluations_array[POLYNOMIAL::Q_O] = prover.key->polynomial_store.get("q_3_lagrange"); - evaluations_array[POLYNOMIAL::Q_C] = prover.key->polynomial_store.get("q_c_lagrange"); evaluations_array[POLYNOMIAL::Q_4] = prover.key->polynomial_store.get("q_4_lagrange"); + evaluations_array[POLYNOMIAL::Q_C] = prover.key->polynomial_store.get("q_c_lagrange"); evaluations_array[POLYNOMIAL::QARITH] = prover.key->polynomial_store.get("q_arith_lagrange"); evaluations_array[POLYNOMIAL::QSORT] = prover.key->polynomial_store.get("q_sort_lagrange"); evaluations_array[POLYNOMIAL::QELLIPTIC] = prover.key->polynomial_store.get("q_elliptic_lagrange"); evaluations_array[POLYNOMIAL::QAUX] = prover.key->polynomial_store.get("q_aux_lagrange"); evaluations_array[POLYNOMIAL::QLOOKUPTYPE] = prover.key->polynomial_store.get("table_type_lagrange"); + evaluations_array[POLYNOMIAL::SIGMA_1] = prover.key->polynomial_store.get("sigma_1_lagrange"); evaluations_array[POLYNOMIAL::SIGMA_2] = prover.key->polynomial_store.get("sigma_2_lagrange"); evaluations_array[POLYNOMIAL::SIGMA_3] = prover.key->polynomial_store.get("sigma_3_lagrange"); evaluations_array[POLYNOMIAL::SIGMA_4] = prover.key->polynomial_store.get("sigma_4_lagrange"); + evaluations_array[POLYNOMIAL::ID_1] = prover.key->polynomial_store.get("id_1_lagrange"); evaluations_array[POLYNOMIAL::ID_2] = prover.key->polynomial_store.get("id_2_lagrange"); evaluations_array[POLYNOMIAL::ID_3] = prover.key->polynomial_store.get("id_3_lagrange"); evaluations_array[POLYNOMIAL::ID_4] = prover.key->polynomial_store.get("id_4_lagrange"); + + evaluations_array[POLYNOMIAL::TABLE_1] = prover.key->polynomial_store.get("table_value_1_lagrange"); + evaluations_array[POLYNOMIAL::TABLE_2] = prover.key->polynomial_store.get("table_value_2_lagrange"); + evaluations_array[POLYNOMIAL::TABLE_3] = prover.key->polynomial_store.get("table_value_3_lagrange"); + evaluations_array[POLYNOMIAL::TABLE_4] = prover.key->polynomial_store.get("table_value_4_lagrange"); + + evaluations_array[POLYNOMIAL::TABLE_1_SHIFT] = prover.key->polynomial_store.get("table_value_1_lagrange").shifted(); + evaluations_array[POLYNOMIAL::TABLE_2_SHIFT] = prover.key->polynomial_store.get("table_value_2_lagrange").shifted(); + evaluations_array[POLYNOMIAL::TABLE_3_SHIFT] = prover.key->polynomial_store.get("table_value_3_lagrange").shifted(); + evaluations_array[POLYNOMIAL::TABLE_4_SHIFT] = prover.key->polynomial_store.get("table_value_4_lagrange").shifted(); + evaluations_array[POLYNOMIAL::LAGRANGE_FIRST] = prover.key->polynomial_store.get("L_first_lagrange"); evaluations_array[POLYNOMIAL::LAGRANGE_LAST] = prover.key->polynomial_store.get("L_last_lagrange"); @@ -227,7 +298,9 @@ TEST(RelationCorrectness, UltraRelationCorrectness) auto relations = std::tuple(honk::sumcheck::UltraArithmeticRelation(), honk::sumcheck::UltraArithmeticRelationSecondary(), honk::sumcheck::UltraGrandProductInitializationRelation(), - honk::sumcheck::UltraGrandProductComputationRelation()); + honk::sumcheck::UltraGrandProductComputationRelation(), + honk::sumcheck::LookupGrandProductComputationRelation(), + honk::sumcheck::LookupGrandProductInitializationRelation()); fr result = 0; for (size_t i = 0; i < prover.key->circuit_size; i++) { @@ -252,6 +325,12 @@ TEST(RelationCorrectness, UltraRelationCorrectness) std::get<3>(relations).add_full_relation_value_contribution(result, evaluations_at_index_i, params); ASSERT_EQ(result, 0); + + std::get<4>(relations).add_full_relation_value_contribution(result, evaluations_at_index_i, params); + ASSERT_EQ(result, 0); + + std::get<5>(relations).add_full_relation_value_contribution(result, evaluations_at_index_i, params); + ASSERT_EQ(result, 0); } } diff --git a/cpp/src/barretenberg/honk/sumcheck/relations/ultra_arithmetic_relation_secondary.hpp b/cpp/src/barretenberg/honk/sumcheck/relations/ultra_arithmetic_relation_secondary.hpp index 78c01db356..0189d779f3 100644 --- a/cpp/src/barretenberg/honk/sumcheck/relations/ultra_arithmetic_relation_secondary.hpp +++ b/cpp/src/barretenberg/honk/sumcheck/relations/ultra_arithmetic_relation_secondary.hpp @@ -6,6 +6,7 @@ #include "../polynomials/univariate.hpp" #include "relation.hpp" +// TODO(luke): Move this into ultra_arithmetic_relation.hpp. namespace proof_system::honk::sumcheck { template class UltraArithmeticRelationSecondary { diff --git a/cpp/src/barretenberg/honk/sumcheck/sumcheck.hpp b/cpp/src/barretenberg/honk/sumcheck/sumcheck.hpp index 837922feb2..7107dd9526 100644 --- a/cpp/src/barretenberg/honk/sumcheck/sumcheck.hpp +++ b/cpp/src/barretenberg/honk/sumcheck/sumcheck.hpp @@ -3,7 +3,7 @@ #include #include "barretenberg/honk/sumcheck/relations/relation.hpp" #include "barretenberg/honk/transcript/transcript.hpp" -#include "barretenberg/honk/utils/public_inputs.hpp" +#include "barretenberg/honk/utils/grand_product_delta.hpp" #include "barretenberg/common/throw_or_abort.hpp" #include "sumcheck_round.hpp" #include "polynomials/univariate.hpp" diff --git a/cpp/src/barretenberg/honk/utils/public_inputs.hpp b/cpp/src/barretenberg/honk/utils/grand_product_delta.hpp similarity index 72% rename from cpp/src/barretenberg/honk/utils/public_inputs.hpp rename to cpp/src/barretenberg/honk/utils/grand_product_delta.hpp index 52c66cff94..674ab15c2a 100644 --- a/cpp/src/barretenberg/honk/utils/public_inputs.hpp +++ b/cpp/src/barretenberg/honk/utils/grand_product_delta.hpp @@ -54,4 +54,26 @@ Field compute_public_input_delta(std::span public_inputs, return numerator / denominator; } +/** + * @brief Compute lookup grand product delta + * + * @details Similar to how incorporation of public inputs into the permutation grand product results in + * z_permutation(X_n) = \Delta_{PI}, the structure of the lookup grand product polynomial results in + * z_lookup(X_n) = (γ(1 + β))^n = \Delta_{lookup}. This is a side effect of the way in which we + * incorporate the original plookup construction (for which z_lookup(X_n) = 1) into plonk/honk. + * See https://hackmd.io/@aztec-network/ByjS5GplK? for a more detailed explanation. + * + * @tparam Field + * @param beta + * @param gamma + * @param domain_size dyadic circuit size + * @return Field + */ +template +Field compute_lookup_grand_product_delta(const Field& beta, const Field& gamma, const size_t domain_size) +{ + Field gamma_by_one_plus_beta = gamma * (Field(1) + beta); // γ(1 + β) + return gamma_by_one_plus_beta.pow(domain_size); // (γ(1 + β))^n +} + } // namespace proof_system::honk \ No newline at end of file diff --git a/cpp/src/barretenberg/plonk/composer/splitting_tmp/ultra_plonk_composer.hpp b/cpp/src/barretenberg/plonk/composer/splitting_tmp/ultra_plonk_composer.hpp index ccc3ece628..dd51d0aa3d 100644 --- a/cpp/src/barretenberg/plonk/composer/splitting_tmp/ultra_plonk_composer.hpp +++ b/cpp/src/barretenberg/plonk/composer/splitting_tmp/ultra_plonk_composer.hpp @@ -133,18 +133,18 @@ class UltraPlonkComposer { // * 1) Current number number of actual gates // * 2) Number of public inputs, as we'll need to add a gate for each of them // * 3) Number of Rom array-associated gates - // * 4) NUmber of range-list associated gates + // * 4) Number of range-list associated gates + // * 5) Number of non-native field multiplication gates. // * // * // * @param count return arument, number of existing gates // * @param rangecount return argument, extra gates due to range checks // * @param romcount return argument, extra gates due to rom reads // * @param ramcount return argument, extra gates due to ram read/writes + // * @param nnfcount return argument, extra gates due to queued non native field gates // */ - // void get_num_gates_split_into_components(size_t& count, - // size_t& rangecount, - // size_t& romcount, - // size_t& ramcount) const + // void get_num_gates_split_into_components( + // size_t& count, size_t& rangecount, size_t& romcount, size_t& ramcount, size_t& nnfcount) const // { // count = num_gates; // // each ROM gate adds +1 extra gate due to the rom reads being copied to a sorted list set @@ -213,17 +213,27 @@ class UltraPlonkComposer { // rangecount += ram_range_sizes[i]; // } // } + // std::vector nnf_copy(cached_non_native_field_multiplications); + // // update nnfcount + // std::sort(nnf_copy.begin(), nnf_copy.end()); + + // auto last = std::unique(nnf_copy.begin(), nnf_copy.end()); + // const size_t num_nnf_ops = static_cast(std::distance(nnf_copy.begin(), last)); + // nnfcount = num_nnf_ops * GATES_PER_NON_NATIVE_FIELD_MULTIPLICATION_ARITHMETIC; // } + // // /** // * @brief Get the final number of gates in a circuit, which consists of the sum of: // * 1) Current number number of actual gates // * 2) Number of public inputs, as we'll need to add a gate for each of them // * 3) Number of Rom array-associated gates - // * 4) NUmber of range-list associated gates + // * 4) Number of range-list associated gates + // * 5) Number of non-native field multiplication gates. // * // * @return size_t // */ + // // virtual size_t get_num_gates() const override // { // // if circuit finalised already added extra gates @@ -234,8 +244,9 @@ class UltraPlonkComposer { // size_t rangecount = 0; // size_t romcount = 0; // size_t ramcount = 0; - // get_num_gates_split_into_components(count, rangecount, romcount, ramcount); - // return count + romcount + ramcount + rangecount; + // size_t nnfcount = 0; + // get_num_gates_split_into_components(count, rangecount, romcount, ramcount, nnfcount); + // return count + romcount + ramcount + rangecount + nnfcount; // } // virtual void print_num_gates() const override @@ -244,12 +255,13 @@ class UltraPlonkComposer { // size_t rangecount = 0; // size_t romcount = 0; // size_t ramcount = 0; - - // get_num_gates_split_into_components(count, rangecount, romcount, ramcount); + // size_t nnfcount = 0; + // get_num_gates_split_into_components(count, rangecount, romcount, ramcount, nnfcount); // size_t total = count + romcount + ramcount + rangecount; // std::cout << "gates = " << total << " (arith " << count << ", rom " << romcount << ", ram " << ramcount - // << ", range " << rangecount << "), pubinp = " << public_inputs.size() << std::endl; + // << ", range " << rangecount << ", non native field gates " << nnfcount + // << "), pubinp = " << public_inputs.size() << std::endl; // } void assert_equal(const uint32_t a_variable_idx, @@ -367,11 +379,10 @@ class UltraPlonkComposer { }; // std::array decompose_non_native_field_double_width_limb( // const uint32_t limb_idx, const size_t num_limb_bits = (2 * DEFAULT_NON_NATIVE_FIELD_LIMB_BITS)); - std::array evaluate_non_native_field_multiplication( + std::array queue_non_native_field_multiplication( const non_native_field_witnesses& input, const bool range_constrain_quotient_and_remainder = true) { - return circuit_constructor.evaluate_non_native_field_multiplication(input, - range_constrain_quotient_and_remainder); + return circuit_constructor.queue_non_native_field_multiplication(input, range_constrain_quotient_and_remainder); }; // std::array evaluate_partial_non_native_field_multiplication(const non_native_field_witnesses& // input); typedef std::pair scaled_witness; typedef std::tuple UltraComposer::compute_proving_key() * our circuit is finalised, and we must not to execute these functions again. */ if (!circuit_finalised) { + process_non_native_field_multiplications(); process_ROM_arrays(public_inputs.size()); process_RAM_arrays(public_inputs.size()); process_range_lists(); @@ -1216,18 +1217,65 @@ void UltraComposer::create_new_range_constraint(const uint32_t variable_index, range_lists.insert({ target_range, create_range_list(target_range) }); } + const auto existing_tag = real_variable_tags[real_variable_index[variable_index]]; auto& list = range_lists[target_range]; - assign_tag(variable_index, list.range_tag); - list.variable_indices.emplace_back(variable_index); + + // If the variable's tag matches the target range list's tag, do nothing. + if (existing_tag != list.range_tag) { + // If the variable is 'untagged' (i.e., it has the dummy tag), assign it the appropriate tag. + // Otherwise, find the range for which the variable has already been tagged. + if (existing_tag != DUMMY_TAG) { + bool found_tag = false; + for (const auto& r : range_lists) { + if (r.second.range_tag == existing_tag) { + found_tag = true; + if (r.first < target_range) { + // The variable already has a more restrictive range check, so do nothing. + return; + } else { + // The range constraint we are trying to impose is more restrictive than the existing range + // constraint. It would be difficult to remove an existing range check. Instead deep-copy the + // variable and apply a range check to new variable + const uint32_t copied_witness = add_variable(get_variable(variable_index)); + create_add_gate({ .a = variable_index, + .b = copied_witness, + .c = zero_idx, + .a_scaling = 1, + .b_scaling = -1, + .c_scaling = 0, + .const_scaling = 0 }); + // Recurse with new witness that has no tag attached. + create_new_range_constraint(copied_witness, target_range, msg); + return; + } + } + } + ASSERT(found_tag == true); + } + assign_tag(variable_index, list.range_tag); + list.variable_indices.emplace_back(variable_index); + } } -void UltraComposer::process_range_list(const RangeList& list) +void UltraComposer::process_range_list(RangeList& list) { assert_valid_variables(list.variable_indices); ASSERT(list.variable_indices.size() > 0); + + // replace witness index in variable_indices with the real variable index i.e. if a copy constraint has been + // applied on a variable after it was range constrained, this makes sure the indices in list point to the updated + // index in the range list so the set equivalence does not fail + for (uint32_t& x : list.variable_indices) { + x = real_variable_index[x]; + } + // remove duplicate witness indices to prevent the sorted list set size being wrong! + std::sort(list.variable_indices.begin(), list.variable_indices.end()); + auto back_iterator = std::unique(list.variable_indices.begin(), list.variable_indices.end()); + list.variable_indices.erase(back_iterator, list.variable_indices.end()); + // go over variables - // for each variable, create mirror variable with same value - with tau tag + // iterate over each variable and create mirror variable with same value - with tau tag // need to make sure that, in original list, increments of at most 3 std::vector sorted_list; sorted_list.reserve(list.variable_indices.size()); @@ -1262,7 +1310,7 @@ void UltraComposer::process_range_list(const RangeList& list) void UltraComposer::process_range_lists() { - for (const auto& i : range_lists) + for (auto& i : range_lists) process_range_list(i.second); } @@ -1811,18 +1859,22 @@ std::array UltraComposer::decompose_non_native_field_double_width_l } /** - * NON NATIVE FIELD MULTIPLICATION CUSTOM GATE SEQUENCE + * @brief Queue up non-native field multiplication data. + * + * @details The data queued represents a non-native field multiplication identity a * b = q * p + r, + * where a, b, q, r are all emulated non-native field elements that are each split across 4 distinct witness variables. * - * This method will evaluate the equation (a * b = q * p + r) - * Where a, b, q, r are all emulated non-native field elements that are each split across 4 distinct witness variables + * Without this queue some functions, such as proof_system::plonk::stdlib::element::double_montgomery_ladder, would + * duplicate non-native field operations, which can be quite expensive. We queue up these operations, and remove + * duplicates in the circuit finishing stage of the proving key computation. * * The non-native field modulus, p, is a circuit constant * * The return value are the witness indices of the two remainder limbs `lo_1, hi_2` * - * N.B. this method does NOT evaluate the prime field component of non-native field multiplications + * N.B.: This method does NOT evaluate the prime field component of non-native field multiplications. **/ -std::array UltraComposer::evaluate_non_native_field_multiplication( +std::array UltraComposer::queue_non_native_field_multiplication( const non_native_field_witnesses& input, const bool range_constrain_quotient_and_remainder) { @@ -1854,8 +1906,6 @@ std::array UltraComposer::evaluate_non_native_field_multiplication( constexpr barretenberg::fr LIMB_SHIFT = uint256_t(1) << DEFAULT_NON_NATIVE_FIELD_LIMB_BITS; constexpr barretenberg::fr LIMB_SHIFT_2 = uint256_t(1) << (2 * DEFAULT_NON_NATIVE_FIELD_LIMB_BITS); constexpr barretenberg::fr LIMB_SHIFT_3 = uint256_t(1) << (3 * DEFAULT_NON_NATIVE_FIELD_LIMB_BITS); - constexpr barretenberg::fr LIMB_RSHIFT = - barretenberg::fr(1) / barretenberg::fr(uint256_t(1) << DEFAULT_NON_NATIVE_FIELD_LIMB_BITS); constexpr barretenberg::fr LIMB_RSHIFT_2 = barretenberg::fr(1) / barretenberg::fr(uint256_t(1) << (2 * DEFAULT_NON_NATIVE_FIELD_LIMB_BITS)); @@ -1904,82 +1954,127 @@ std::array UltraComposer::evaluate_non_native_field_multiplication( range_constrain_two_limbs(input.q[2], input.q[3]); } - // product gate 1 - // (lo_0 + q_0(p_0 + p_1*2^b) + q_1(p_0*2^b) - (r_1)2^b)2^-2b - lo_1 = 0 - create_big_add_gate({ input.q[0], - input.q[1], - input.r[1], - lo_1_idx, - input.neg_modulus[0] + input.neg_modulus[1] * LIMB_SHIFT, - input.neg_modulus[0] * LIMB_SHIFT, - -LIMB_SHIFT, - -LIMB_SHIFT.sqr(), - 0 }, - true); + // Add witnesses into the multiplication cache + // (when finalising the circuit, we will remove duplicates; several dups produced by biggroup.hpp methods) + cached_non_native_field_multiplication cache_entry{ + .a = input.a, + .b = input.b, + .q = input.q, + .r = input.r, + .cross_terms = { lo_0_idx, lo_1_idx, hi_0_idx, hi_1_idx, hi_2_idx, hi_3_idx }, + .neg_modulus = input.neg_modulus, + }; + cached_non_native_field_multiplications.emplace_back(cache_entry); - w_l.emplace_back(input.a[1]); - w_r.emplace_back(input.b[1]); - w_o.emplace_back(input.r[0]); - w_4.emplace_back(lo_0_idx); - apply_aux_selectors(AUX_SELECTORS::NON_NATIVE_FIELD_1); - ++num_gates; - w_l.emplace_back(input.a[0]); - w_r.emplace_back(input.b[0]); - w_o.emplace_back(input.a[3]); - w_4.emplace_back(input.b[3]); - apply_aux_selectors(AUX_SELECTORS::NON_NATIVE_FIELD_2); - ++num_gates; - w_l.emplace_back(input.a[2]); - w_r.emplace_back(input.b[2]); - w_o.emplace_back(input.r[3]); - w_4.emplace_back(hi_0_idx); - apply_aux_selectors(AUX_SELECTORS::NON_NATIVE_FIELD_3); - ++num_gates; - w_l.emplace_back(input.a[1]); - w_r.emplace_back(input.b[1]); - w_o.emplace_back(input.r[2]); - w_4.emplace_back(hi_1_idx); - apply_aux_selectors(AUX_SELECTORS::NONE); - ++num_gates; + return std::array{ lo_1_idx, hi_3_idx }; +} - /** - * product gate 6 - * - * hi_2 - hi_1 - lo_1 - q[2](p[1].2^b + p[0]) - q[3](p[0].2^b) = 0 - * - **/ - create_big_add_gate( - { - input.q[2], - input.q[3], - lo_1_idx, - hi_1_idx, - -input.neg_modulus[1] * LIMB_SHIFT - input.neg_modulus[0], - -input.neg_modulus[0] * LIMB_SHIFT, - -1, - -1, - 0, - }, - true); +/** + * @brief Called in `compute_proving_key` when finalizing circuit. + * Iterates over the cached_non_native_field_multiplication objects, + * removes duplicates, and instantiates the remainder as constraints` + */ +void UltraComposer::process_non_native_field_multiplications() +{ + std::sort(cached_non_native_field_multiplications.begin(), cached_non_native_field_multiplications.end()); - /** - * product gate 7 - * - * hi_3 - (hi_2 - q[0](p[3].2^b + p[2]) - q[1](p[2].2^b + p[1])).2^-2b - **/ - create_big_add_gate({ - hi_3_idx, - input.q[0], - input.q[1], - hi_2_idx, - -1, - input.neg_modulus[3] * LIMB_RSHIFT + input.neg_modulus[2] * LIMB_RSHIFT_2, - input.neg_modulus[2] * LIMB_RSHIFT + input.neg_modulus[1] * LIMB_RSHIFT_2, - LIMB_RSHIFT_2, - 0, - }); + auto last = + std::unique(cached_non_native_field_multiplications.begin(), cached_non_native_field_multiplications.end()); - return std::array{ lo_1_idx, hi_3_idx }; + auto it = cached_non_native_field_multiplications.begin(); + + constexpr barretenberg::fr LIMB_SHIFT = uint256_t(1) << DEFAULT_NON_NATIVE_FIELD_LIMB_BITS; + constexpr barretenberg::fr LIMB_RSHIFT = + barretenberg::fr(1) / barretenberg::fr(uint256_t(1) << DEFAULT_NON_NATIVE_FIELD_LIMB_BITS); + constexpr barretenberg::fr LIMB_RSHIFT_2 = + barretenberg::fr(1) / barretenberg::fr(uint256_t(1) << (2 * DEFAULT_NON_NATIVE_FIELD_LIMB_BITS)); + + // iterate over the cached items and create constraints + while (it != last) { + const auto input = *it; + const uint32_t lo_0_idx = input.cross_terms.lo_0_idx; + const uint32_t lo_1_idx = input.cross_terms.lo_1_idx; + const uint32_t hi_0_idx = input.cross_terms.hi_0_idx; + const uint32_t hi_1_idx = input.cross_terms.hi_1_idx; + const uint32_t hi_2_idx = input.cross_terms.hi_2_idx; + const uint32_t hi_3_idx = input.cross_terms.hi_3_idx; + + // product gate 1 + // (lo_0 + q_0(p_0 + p_1*2^b) + q_1(p_0*2^b) - (r_1)2^b)2^-2b - lo_1 = 0 + create_big_add_gate({ input.q[0], + input.q[1], + input.r[1], + lo_1_idx, + input.neg_modulus[0] + input.neg_modulus[1] * LIMB_SHIFT, + input.neg_modulus[0] * LIMB_SHIFT, + -LIMB_SHIFT, + -LIMB_SHIFT.sqr(), + 0 }, + true); + + w_l.emplace_back(input.a[1]); + w_r.emplace_back(input.b[1]); + w_o.emplace_back(input.r[0]); + w_4.emplace_back(lo_0_idx); + apply_aux_selectors(AUX_SELECTORS::NON_NATIVE_FIELD_1); + ++num_gates; + w_l.emplace_back(input.a[0]); + w_r.emplace_back(input.b[0]); + w_o.emplace_back(input.a[3]); + w_4.emplace_back(input.b[3]); + apply_aux_selectors(AUX_SELECTORS::NON_NATIVE_FIELD_2); + ++num_gates; + w_l.emplace_back(input.a[2]); + w_r.emplace_back(input.b[2]); + w_o.emplace_back(input.r[3]); + w_4.emplace_back(hi_0_idx); + apply_aux_selectors(AUX_SELECTORS::NON_NATIVE_FIELD_3); + ++num_gates; + w_l.emplace_back(input.a[1]); + w_r.emplace_back(input.b[1]); + w_o.emplace_back(input.r[2]); + w_4.emplace_back(hi_1_idx); + apply_aux_selectors(AUX_SELECTORS::NONE); + ++num_gates; + + /** + * product gate 6 + * + * hi_2 - hi_1 - lo_1 - q[2](p[1].2^b + p[0]) - q[3](p[0].2^b) = 0 + * + **/ + create_big_add_gate( + { + input.q[2], + input.q[3], + lo_1_idx, + hi_1_idx, + -input.neg_modulus[1] * LIMB_SHIFT - input.neg_modulus[0], + -input.neg_modulus[0] * LIMB_SHIFT, + -1, + -1, + 0, + }, + true); + + /** + * product gate 7 + * + * hi_3 - (hi_2 - q[0](p[3].2^b + p[2]) - q[1](p[2].2^b + p[1])).2^-2b + **/ + create_big_add_gate({ + hi_3_idx, + input.q[0], + input.q[1], + hi_2_idx, + -1, + input.neg_modulus[3] * LIMB_RSHIFT + input.neg_modulus[2] * LIMB_RSHIFT_2, + input.neg_modulus[2] * LIMB_RSHIFT + input.neg_modulus[1] * LIMB_RSHIFT_2, + LIMB_RSHIFT_2, + 0, + }); + ++it; + } } /** diff --git a/cpp/src/barretenberg/plonk/composer/ultra_composer.hpp b/cpp/src/barretenberg/plonk/composer/ultra_composer.hpp index b3157639fd..d8988e3fca 100644 --- a/cpp/src/barretenberg/plonk/composer/ultra_composer.hpp +++ b/cpp/src/barretenberg/plonk/composer/ultra_composer.hpp @@ -7,6 +7,14 @@ namespace proof_system::plonk { +/** + * @brief UltraPlonK: a flavor of PlonK with plookup tables, efficient range constraints, RAM, ROM, and more! + * + * @warning The proof of PlonK's correctness uses the fact that verifier challenges are generated by a hash function + * standing in for a random oracle. UltraPlonK currently uses a hash function (a custom Pedersen hash that uses lookup + * tables) that is known to violate this random oracle assumption. We plan to switch to an algebraic hash function that + * is believed to have the random oracle property in a future upgrade. + */ class UltraComposer : public ComposerBase { public: @@ -27,7 +35,8 @@ class UltraComposer : public ComposerBase { static constexpr uint32_t UNINITIALIZED_MEMORY_RECORD = UINT32_MAX; static constexpr size_t NUMBER_OF_GATES_PER_RAM_ACCESS = 2; static constexpr size_t NUMBER_OF_ARITHMETIC_GATES_PER_RAM_ARRAY = 1; - + // number of gates created per non-native field operation in process_non_native_field_multiplications + static constexpr size_t GATES_PER_NON_NATIVE_FIELD_MULTIPLICATION_ARITHMETIC = 7; struct non_native_field_witnesses { // first 4 array elements = limbs // 5th element = prime basis limb @@ -39,6 +48,66 @@ class UltraComposer : public ComposerBase { barretenberg::fr modulus; }; + struct non_native_field_multiplication_cross_terms { + uint32_t lo_0_idx; + uint32_t lo_1_idx; + uint32_t hi_0_idx; + uint32_t hi_1_idx; + uint32_t hi_2_idx; + uint32_t hi_3_idx; + }; + + /** + * @brief Used to store instructions to create non_native_field_multiplication gates. + * We want to cache these (and remove duplicates) as the stdlib code can end up multiplying the same inputs + * repeatedly. + */ + struct cached_non_native_field_multiplication { + std::array a; + std::array b; + std::array q; + std::array r; + non_native_field_multiplication_cross_terms cross_terms; + std::array neg_modulus; + + bool operator==(const cached_non_native_field_multiplication& other) const + { + bool valid = true; + for (size_t i = 0; i < 5; ++i) { + valid = valid && (a[i] == other.a[i]); + valid = valid && (b[i] == other.b[i]); + valid = valid && (q[i] == other.q[i]); + valid = valid && (r[i] == other.r[i]); + } + return valid; + } + bool operator<(const cached_non_native_field_multiplication& other) const + { + if (a < other.a) { + return true; + } + if (a == other.a) { + if (b < other.b) { + return true; + } + if (b == other.b) { + if (q < other.q) { + return true; + } + if (q == other.q) { + if (r < other.r) { + return true; + } + } + } + } + return false; + } + }; + + std::vector cached_non_native_field_multiplications; + void process_non_native_field_multiplications(); + enum AUX_SELECTORS { NONE, LIMB_ACCUMULATE_1, @@ -58,6 +127,8 @@ class UltraComposer : public ComposerBase { uint64_t target_range; uint32_t range_tag; uint32_t tau_tag; + // contains the list of variable indices that are range constrained to target_range + // as ordered in the vector of variable indoces from the composer std::vector variable_indices; }; @@ -242,18 +313,18 @@ class UltraComposer : public ComposerBase { * 1) Current number number of actual gates * 2) Number of public inputs, as we'll need to add a gate for each of them * 3) Number of Rom array-associated gates - * 4) NUmber of range-list associated gates + * 4) Number of range-list associated gates + * 5) Number of non-native field multiplication gates. * * * @param count return arument, number of existing gates * @param rangecount return argument, extra gates due to range checks * @param romcount return argument, extra gates due to rom reads * @param ramcount return argument, extra gates due to ram read/writes + * @param nnfcount return argument, extra gates due to queued non native field gates */ - void get_num_gates_split_into_components(size_t& count, - size_t& rangecount, - size_t& romcount, - size_t& ramcount) const + void get_num_gates_split_into_components( + size_t& count, size_t& rangecount, size_t& romcount, size_t& ramcount, size_t& nnfcount) const { count = num_gates; // each ROM gate adds +1 extra gate due to the rom reads being copied to a sorted list set @@ -321,6 +392,13 @@ class UltraComposer : public ComposerBase { rangecount += ram_range_sizes[i]; } } + std::vector nnf_copy(cached_non_native_field_multiplications); + // update nnfcount + std::sort(nnf_copy.begin(), nnf_copy.end()); + + auto last = std::unique(nnf_copy.begin(), nnf_copy.end()); + const size_t num_nnf_ops = static_cast(std::distance(nnf_copy.begin(), last)); + nnfcount = num_nnf_ops * GATES_PER_NON_NATIVE_FIELD_MULTIPLICATION_ARITHMETIC; } /** @@ -328,7 +406,8 @@ class UltraComposer : public ComposerBase { * 1) Current number number of actual gates * 2) Number of public inputs, as we'll need to add a gate for each of them * 3) Number of Rom array-associated gates - * 4) NUmber of range-list associated gates + * 4) Number of range-list associated gates + * 5) Number of non-native field multiplication gates. * * @return size_t */ @@ -342,8 +421,9 @@ class UltraComposer : public ComposerBase { size_t rangecount = 0; size_t romcount = 0; size_t ramcount = 0; - get_num_gates_split_into_components(count, rangecount, romcount, ramcount); - return count + romcount + ramcount + rangecount; + size_t nnfcount = 0; + get_num_gates_split_into_components(count, rangecount, romcount, ramcount, nnfcount); + return count + romcount + ramcount + rangecount + nnfcount; } virtual size_t get_total_circuit_size() const override @@ -366,12 +446,13 @@ class UltraComposer : public ComposerBase { size_t rangecount = 0; size_t romcount = 0; size_t ramcount = 0; - - get_num_gates_split_into_components(count, rangecount, romcount, ramcount); + size_t nnfcount = 0; + get_num_gates_split_into_components(count, rangecount, romcount, ramcount, nnfcount); size_t total = count + romcount + ramcount + rangecount; std::cout << "gates = " << total << " (arith " << count << ", rom " << romcount << ", ram " << ramcount - << ", range " << rangecount << "), pubinp = " << public_inputs.size() << std::endl; + << ", range " << rangecount << ", non native field gates " << nnfcount + << "), pubinp = " << public_inputs.size() << std::endl; } void assert_equal_constant(const uint32_t a_idx, @@ -425,6 +506,10 @@ class UltraComposer : public ComposerBase { void assign_tag(const uint32_t variable_index, const uint32_t tag) { ASSERT(tag <= current_tag); + // If we've already assigned this tag to this variable, return (can happen due to copy constraints) + if (real_variable_tags[real_variable_index[variable_index]] == tag) { + return; + } ASSERT(real_variable_tags[real_variable_index[variable_index]] == DUMMY_TAG); real_variable_tags[real_variable_index[variable_index]] = tag; } @@ -443,7 +528,7 @@ class UltraComposer : public ComposerBase { } RangeList create_range_list(const uint64_t target_range); - void process_range_list(const RangeList& list); + void process_range_list(RangeList& list); void process_range_lists(); /** @@ -460,7 +545,7 @@ class UltraComposer : public ComposerBase { const size_t hi_limb_bits = DEFAULT_NON_NATIVE_FIELD_LIMB_BITS); std::array decompose_non_native_field_double_width_limb( const uint32_t limb_idx, const size_t num_limb_bits = (2 * DEFAULT_NON_NATIVE_FIELD_LIMB_BITS)); - std::array evaluate_non_native_field_multiplication( + std::array queue_non_native_field_multiplication( const non_native_field_witnesses& input, const bool range_constrain_quotient_and_remainder = true); std::array evaluate_partial_non_native_field_multiplication(const non_native_field_witnesses& input); typedef std::pair scaled_witness; diff --git a/cpp/src/barretenberg/plonk/composer/ultra_composer.test.cpp b/cpp/src/barretenberg/plonk/composer/ultra_composer.test.cpp index eef80fa56c..9745d397e2 100644 --- a/cpp/src/barretenberg/plonk/composer/ultra_composer.test.cpp +++ b/cpp/src/barretenberg/plonk/composer/ultra_composer.test.cpp @@ -649,7 +649,7 @@ TYPED_TEST(ultra_composer, non_native_field_multiplication) UltraComposer::non_native_field_witnesses inputs{ a_indices, b_indices, q_indices, r_indices, modulus_limbs, fr(uint256_t(modulus)), }; - const auto [lo_1_idx, hi_1_idx] = composer.evaluate_non_native_field_multiplication(inputs); + const auto [lo_1_idx, hi_1_idx] = composer.queue_non_native_field_multiplication(inputs); composer.range_constrain_two_limbs(lo_1_idx, hi_1_idx, 70, 70); TestFixture::prove_and_verify(composer, /*expected_result=*/true); @@ -758,4 +758,65 @@ TYPED_TEST(ultra_composer, ram) TestFixture::prove_and_verify(composer, /*expected_result=*/true); } +TYPED_TEST(ultra_composer, range_checks_on_duplicates) +{ + UltraComposer composer = UltraComposer(); + + uint32_t a = composer.add_variable(100); + uint32_t b = composer.add_variable(100); + uint32_t c = composer.add_variable(100); + uint32_t d = composer.add_variable(100); + + composer.assert_equal(a, b); + composer.assert_equal(a, c); + composer.assert_equal(a, d); + + composer.create_new_range_constraint(a, 1000); + composer.create_new_range_constraint(b, 1001); + composer.create_new_range_constraint(c, 999); + composer.create_new_range_constraint(d, 1000); + + composer.create_big_add_gate( + { + a, + b, + c, + d, + 0, + 0, + 0, + 0, + 0, + }, + false); + + TestFixture::prove_and_verify(composer, /*expected_result=*/true); +} + +// Ensure copy constraints added on variables smaller than 2^14, which have been previously +// range constrained, do not break the set equivalence checks because of indices mismatch. +// 2^14 is DEFAULT_PLOOKUP_RANGE_BITNUM i.e. the maximum size before a variable gets sliced +// before range constraints are applied to it. +TEST(ultra_composer, range_constraint_small_variable) +{ + auto composer = UltraComposer(); + uint16_t mask = (1 << 8) - 1; + int a = engine.get_random_uint16() & mask; + uint32_t a_idx = composer.add_variable(fr(a)); + uint32_t b_idx = composer.add_variable(fr(a)); + ASSERT_NE(a_idx, b_idx); + uint32_t c_idx = composer.add_variable(fr(a)); + ASSERT_NE(c_idx, b_idx); + composer.create_range_constraint(b_idx, 8, "bad range"); + composer.assert_equal(a_idx, b_idx); + composer.create_range_constraint(c_idx, 8, "bad range"); + composer.assert_equal(a_idx, c_idx); + + auto prover = composer.create_prover(); + auto proof = prover.construct_proof(); + auto verifier = composer.create_verifier(); + bool result = verifier.verify_proof(proof); + EXPECT_EQ(result, true); +} + } // namespace proof_system::plonk::test_ultra_composer diff --git a/cpp/src/barretenberg/proof_system/circuit_constructors/ultra_circuit_constructor.cpp b/cpp/src/barretenberg/proof_system/circuit_constructors/ultra_circuit_constructor.cpp index d606ab173d..425efbdb58 100644 --- a/cpp/src/barretenberg/proof_system/circuit_constructors/ultra_circuit_constructor.cpp +++ b/cpp/src/barretenberg/proof_system/circuit_constructors/ultra_circuit_constructor.cpp @@ -33,6 +33,7 @@ void UltraCircuitConstructor::finalize_circuit() * our circuit is finalised, and we must not to execute these functions again. */ if (!circuit_finalised) { + process_non_native_field_multiplications(); process_ROM_arrays(public_inputs.size()); process_RAM_arrays(public_inputs.size()); process_range_lists(); @@ -1236,18 +1237,22 @@ std::array UltraCircuitConstructor::decompose_non_native_field_doub } /** - * NON NATIVE FIELD MULTIPLICATION CUSTOM GATE SEQUENCE + * @brief Queue up non-native field multiplication data. * - * This method will evaluate the equation (a * b = q * p + r) - * Where a, b, q, r are all emulated non-native field elements that are each split across 4 distinct witness variables + * @details The data queued represents a non-native field multiplication identity a * b = q * p + r, + * where a, b, q, r are all emulated non-native field elements that are each split across 4 distinct witness variables. + * + * Without this queue some functions, such as proof_system::plonk::stdlib::element::double_montgomery_ladder, would + * duplicate non-native field operations, which can be quite expensive. We queue up these operations, and remove + * duplicates in the circuit finishing stage of the proving key computation. * * The non-native field modulus, p, is a circuit constant * * The return value are the witness indices of the two remainder limbs `lo_1, hi_2` * - * N.B. this method does NOT evaluate the prime field component of non-native field multiplications + * N.B.: This method does NOT evaluate the prime field component of non-native field multiplications. **/ -std::array UltraCircuitConstructor::evaluate_non_native_field_multiplication( +std::array UltraCircuitConstructor::queue_non_native_field_multiplication( const non_native_field_witnesses& input, const bool range_constrain_quotient_and_remainder) { @@ -1279,8 +1284,6 @@ std::array UltraCircuitConstructor::evaluate_non_native_field_multi constexpr barretenberg::fr LIMB_SHIFT = uint256_t(1) << DEFAULT_NON_NATIVE_FIELD_LIMB_BITS; constexpr barretenberg::fr LIMB_SHIFT_2 = uint256_t(1) << (2 * DEFAULT_NON_NATIVE_FIELD_LIMB_BITS); constexpr barretenberg::fr LIMB_SHIFT_3 = uint256_t(1) << (3 * DEFAULT_NON_NATIVE_FIELD_LIMB_BITS); - constexpr barretenberg::fr LIMB_RSHIFT = - barretenberg::fr(1) / barretenberg::fr(uint256_t(1) << DEFAULT_NON_NATIVE_FIELD_LIMB_BITS); constexpr barretenberg::fr LIMB_RSHIFT_2 = barretenberg::fr(1) / barretenberg::fr(uint256_t(1) << (2 * DEFAULT_NON_NATIVE_FIELD_LIMB_BITS)); @@ -1328,83 +1331,127 @@ std::array UltraCircuitConstructor::evaluate_non_native_field_multi range_constrain_two_limbs(input.q[0], input.q[1]); range_constrain_two_limbs(input.q[2], input.q[3]); } + // Add witnesses into the multiplication cache + // (when finalising the circuit, we will remove duplicates; several dups produced by biggroup.hpp methods) + cached_non_native_field_multiplication cache_entry{ + .a = input.a, + .b = input.b, + .q = input.q, + .r = input.r, + .cross_terms = { lo_0_idx, lo_1_idx, hi_0_idx, hi_1_idx, hi_2_idx, hi_3_idx }, + .neg_modulus = input.neg_modulus, + }; + cached_non_native_field_multiplications.emplace_back(cache_entry); - // product gate 1 - // (lo_0 + q_0(p_0 + p_1*2^b) + q_1(p_0*2^b) - (r_1)2^b)2^-2b - lo_1 = 0 - create_big_add_gate({ input.q[0], - input.q[1], - input.r[1], - lo_1_idx, - input.neg_modulus[0] + input.neg_modulus[1] * LIMB_SHIFT, - input.neg_modulus[0] * LIMB_SHIFT, - -LIMB_SHIFT, - -LIMB_SHIFT.sqr(), - 0 }, - true); + return std::array{ lo_1_idx, hi_3_idx }; +} - w_l.emplace_back(input.a[1]); - w_r.emplace_back(input.b[1]); - w_o.emplace_back(input.r[0]); - w_4.emplace_back(lo_0_idx); - apply_aux_selectors(AUX_SELECTORS::NON_NATIVE_FIELD_1); - ++num_gates; - w_l.emplace_back(input.a[0]); - w_r.emplace_back(input.b[0]); - w_o.emplace_back(input.a[3]); - w_4.emplace_back(input.b[3]); - apply_aux_selectors(AUX_SELECTORS::NON_NATIVE_FIELD_2); - ++num_gates; - w_l.emplace_back(input.a[2]); - w_r.emplace_back(input.b[2]); - w_o.emplace_back(input.r[3]); - w_4.emplace_back(hi_0_idx); - apply_aux_selectors(AUX_SELECTORS::NON_NATIVE_FIELD_3); - ++num_gates; - w_l.emplace_back(input.a[1]); - w_r.emplace_back(input.b[1]); - w_o.emplace_back(input.r[2]); - w_4.emplace_back(hi_1_idx); - apply_aux_selectors(AUX_SELECTORS::NONE); - ++num_gates; +/** + * @brief Called in `compute_proving_key` when finalizing circuit. + * Iterates over the cached_non_native_field_multiplication objects, + * removes duplicates, and instantiates the remainder as constraints` + */ +void UltraCircuitConstructor::process_non_native_field_multiplications() +{ + std::sort(cached_non_native_field_multiplications.begin(), cached_non_native_field_multiplications.end()); - /** - * product gate 6 - * - * hi_2 - hi_1 - lo_1 - q[2](p[1].2^b + p[0]) - q[3](p[0].2^b) = 0 - * - **/ - create_big_add_gate( - { - input.q[2], - input.q[3], - lo_1_idx, - hi_1_idx, - -input.neg_modulus[1] * LIMB_SHIFT - input.neg_modulus[0], - -input.neg_modulus[0] * LIMB_SHIFT, - -1, - -1, - 0, - }, - true); + auto last = + std::unique(cached_non_native_field_multiplications.begin(), cached_non_native_field_multiplications.end()); - /** - * product gate 7 - * - * hi_3 - (hi_2 - q[0](p[3].2^b + p[2]) - q[1](p[2].2^b + p[1])).2^-2b - **/ - create_big_add_gate({ - hi_3_idx, - input.q[0], - input.q[1], - hi_2_idx, - -1, - input.neg_modulus[3] * LIMB_RSHIFT + input.neg_modulus[2] * LIMB_RSHIFT_2, - input.neg_modulus[2] * LIMB_RSHIFT + input.neg_modulus[1] * LIMB_RSHIFT_2, - LIMB_RSHIFT_2, - 0, - }); + auto it = cached_non_native_field_multiplications.begin(); - return std::array{ lo_1_idx, hi_3_idx }; + constexpr barretenberg::fr LIMB_SHIFT = uint256_t(1) << DEFAULT_NON_NATIVE_FIELD_LIMB_BITS; + constexpr barretenberg::fr LIMB_RSHIFT = + barretenberg::fr(1) / barretenberg::fr(uint256_t(1) << DEFAULT_NON_NATIVE_FIELD_LIMB_BITS); + constexpr barretenberg::fr LIMB_RSHIFT_2 = + barretenberg::fr(1) / barretenberg::fr(uint256_t(1) << (2 * DEFAULT_NON_NATIVE_FIELD_LIMB_BITS)); + + // iterate over the cached items and create constraints + while (it != last) { + const auto input = *it; + const uint32_t lo_0_idx = input.cross_terms.lo_0_idx; + const uint32_t lo_1_idx = input.cross_terms.lo_1_idx; + const uint32_t hi_0_idx = input.cross_terms.hi_0_idx; + const uint32_t hi_1_idx = input.cross_terms.hi_1_idx; + const uint32_t hi_2_idx = input.cross_terms.hi_2_idx; + const uint32_t hi_3_idx = input.cross_terms.hi_3_idx; + + // product gate 1 + // (lo_0 + q_0(p_0 + p_1*2^b) + q_1(p_0*2^b) - (r_1)2^b)2^-2b - lo_1 = 0 + create_big_add_gate({ input.q[0], + input.q[1], + input.r[1], + lo_1_idx, + input.neg_modulus[0] + input.neg_modulus[1] * LIMB_SHIFT, + input.neg_modulus[0] * LIMB_SHIFT, + -LIMB_SHIFT, + -LIMB_SHIFT.sqr(), + 0 }, + true); + + w_l.emplace_back(input.a[1]); + w_r.emplace_back(input.b[1]); + w_o.emplace_back(input.r[0]); + w_4.emplace_back(lo_0_idx); + apply_aux_selectors(AUX_SELECTORS::NON_NATIVE_FIELD_1); + ++num_gates; + w_l.emplace_back(input.a[0]); + w_r.emplace_back(input.b[0]); + w_o.emplace_back(input.a[3]); + w_4.emplace_back(input.b[3]); + apply_aux_selectors(AUX_SELECTORS::NON_NATIVE_FIELD_2); + ++num_gates; + w_l.emplace_back(input.a[2]); + w_r.emplace_back(input.b[2]); + w_o.emplace_back(input.r[3]); + w_4.emplace_back(hi_0_idx); + apply_aux_selectors(AUX_SELECTORS::NON_NATIVE_FIELD_3); + ++num_gates; + w_l.emplace_back(input.a[1]); + w_r.emplace_back(input.b[1]); + w_o.emplace_back(input.r[2]); + w_4.emplace_back(hi_1_idx); + apply_aux_selectors(AUX_SELECTORS::NONE); + ++num_gates; + + /** + * product gate 6 + * + * hi_2 - hi_1 - lo_1 - q[2](p[1].2^b + p[0]) - q[3](p[0].2^b) = 0 + * + **/ + create_big_add_gate( + { + input.q[2], + input.q[3], + lo_1_idx, + hi_1_idx, + -input.neg_modulus[1] * LIMB_SHIFT - input.neg_modulus[0], + -input.neg_modulus[0] * LIMB_SHIFT, + -1, + -1, + 0, + }, + true); + + /** + * product gate 7 + * + * hi_3 - (hi_2 - q[0](p[3].2^b + p[2]) - q[1](p[2].2^b + p[1])).2^-2b + **/ + create_big_add_gate({ + hi_3_idx, + input.q[0], + input.q[1], + hi_2_idx, + -1, + input.neg_modulus[3] * LIMB_RSHIFT + input.neg_modulus[2] * LIMB_RSHIFT_2, + input.neg_modulus[2] * LIMB_RSHIFT + input.neg_modulus[1] * LIMB_RSHIFT_2, + LIMB_RSHIFT_2, + 0, + }); + ++it; + } } /** diff --git a/cpp/src/barretenberg/proof_system/circuit_constructors/ultra_circuit_constructor.hpp b/cpp/src/barretenberg/proof_system/circuit_constructors/ultra_circuit_constructor.hpp index 383dccb6f0..2e4803fa44 100644 --- a/cpp/src/barretenberg/proof_system/circuit_constructors/ultra_circuit_constructor.hpp +++ b/cpp/src/barretenberg/proof_system/circuit_constructors/ultra_circuit_constructor.hpp @@ -29,6 +29,8 @@ static constexpr size_t DEFAULT_NON_NATIVE_FIELD_LIMB_BITS = 68; static constexpr uint32_t UNINITIALIZED_MEMORY_RECORD = UINT32_MAX; static constexpr size_t NUMBER_OF_GATES_PER_RAM_ACCESS = 2; static constexpr size_t NUMBER_OF_ARITHMETIC_GATES_PER_RAM_ARRAY = 1; +// number of gates created per non-native field operation in process_non_native_field_multiplications +static constexpr size_t GATES_PER_NON_NATIVE_FIELD_MULTIPLICATION_ARITHMETIC = 7; struct non_native_field_witnesses { // first 4 array elements = limbs @@ -41,6 +43,63 @@ struct non_native_field_witnesses { barretenberg::fr modulus; }; +struct non_native_field_multiplication_cross_terms { + uint32_t lo_0_idx; + uint32_t lo_1_idx; + uint32_t hi_0_idx; + uint32_t hi_1_idx; + uint32_t hi_2_idx; + uint32_t hi_3_idx; +}; + +/** + * @brief Used to store instructions to create non_native_field_multiplication gates. + * We want to cache these (and remove duplicates) as the stdlib code can end up multiplying the same inputs + * repeatedly. + */ +struct cached_non_native_field_multiplication { + std::array a; + std::array b; + std::array q; + std::array r; + non_native_field_multiplication_cross_terms cross_terms; + std::array neg_modulus; + + bool operator==(const cached_non_native_field_multiplication& other) const + { + bool valid = true; + for (size_t i = 0; i < 5; ++i) { + valid = valid && (a[i] == other.a[i]); + valid = valid && (b[i] == other.b[i]); + valid = valid && (q[i] == other.q[i]); + valid = valid && (r[i] == other.r[i]); + } + return valid; + } + bool operator<(const cached_non_native_field_multiplication& other) const + { + if (a < other.a) { + return true; + } + if (a == other.a) { + if (b < other.b) { + return true; + } + if (b == other.b) { + if (q < other.q) { + return true; + } + if (q == other.q) { + if (r < other.r) { + return true; + } + } + } + } + return false; + } +}; + enum AUX_SELECTORS { NONE, LIMB_ACCUMULATE_1, @@ -201,6 +260,10 @@ class UltraCircuitConstructor : public CircuitConstructorBase memory_write_records; + std::vector cached_non_native_field_multiplications; + + void process_non_native_field_multiplications(); + bool circuit_finalised = false; UltraCircuitConstructor(const size_t size_hint = 0) @@ -286,18 +349,18 @@ class UltraCircuitConstructor : public CircuitConstructorBase nnf_copy(cached_non_native_field_multiplications); + // // update nnfcount + // std::sort(nnf_copy.begin(), nnf_copy.end()); + + // auto last = std::unique(nnf_copy.begin(), nnf_copy.end()); + // const size_t num_nnf_ops = static_cast(std::distance(nnf_copy.begin(), last)); + // nnfcount = num_nnf_ops * GATES_PER_NON_NATIVE_FIELD_MULTIPLICATION_ARITHMETIC; // } // /** @@ -373,7 +443,8 @@ class UltraCircuitConstructor : public CircuitConstructorBase + // product to be equal to the "public input delta" that is computed in current_permutation_poly[i] = -barretenberg::fr(current_mapping.row_index + 1 + num_gates * current_mapping.column_index); } else if (current_mapping.is_tag) { diff --git a/cpp/src/barretenberg/stdlib/primitives/bigfield/bigfield_impl.hpp b/cpp/src/barretenberg/stdlib/primitives/bigfield/bigfield_impl.hpp index b32ec8d77e..d324bcc4af 100644 --- a/cpp/src/barretenberg/stdlib/primitives/bigfield/bigfield_impl.hpp +++ b/cpp/src/barretenberg/stdlib/primitives/bigfield/bigfield_impl.hpp @@ -1982,7 +1982,7 @@ void bigfield::unsafe_evaluate_multiply_add(const bigfield& input_left, modulus, }; // N.B. this method also evaluates the prime field component of the non-native field mul - const auto [lo_idx, hi_idx] = ctx->evaluate_non_native_field_multiplication(witnesses, false); + const auto [lo_idx, hi_idx] = ctx->queue_non_native_field_multiplication(witnesses, false); barretenberg::fr neg_prime = -barretenberg::fr(uint256_t(target_basis.modulus)); field_t::evaluate_polynomial_identity(left.prime_basis_limb, @@ -2267,7 +2267,7 @@ void bigfield::unsafe_evaluate_multiple_multiply_add(const std::vector> limb_0_accumulator; std::vector> limb_2_accumulator; std::vector> prime_limb_accumulator; @@ -2416,7 +2416,7 @@ void bigfield::unsafe_evaluate_multiple_multiply_add(const std::vectorevaluate_non_native_field_multiplication(witnesses, false); + const auto [lo_1_idx, hi_1_idx] = ctx->queue_non_native_field_multiplication(witnesses, false); barretenberg::fr neg_prime = -barretenberg::fr(uint256_t(target_basis.modulus)); diff --git a/cpp/src/barretenberg/stdlib/primitives/logic/logic.cpp b/cpp/src/barretenberg/stdlib/primitives/logic/logic.cpp index 47d1d848f0..033ec3b9d2 100644 --- a/cpp/src/barretenberg/stdlib/primitives/logic/logic.cpp +++ b/cpp/src/barretenberg/stdlib/primitives/logic/logic.cpp @@ -2,13 +2,19 @@ #include "../composers/composers.hpp" #include "../plookup/plookup.hpp" +#include "barretenberg/common/assert.hpp" +#include "barretenberg/numeric/uint256/uint256.hpp" +#include "barretenberg/stdlib/primitives/field/field.hpp" +#include namespace proof_system::plonk::stdlib { /** * @brief A logical AND or XOR over a variable number of bits. * - * @details Defaults to basic Composer method if not using plookup-compatible composer + * @details Defaults to basic Composer method if not using plookup-compatible composer. If the left and right operands + * are larger than num_bit, the result will be truncated to num_bits. However, the two operands could be + * range-constrained to num_bits before the call which would remove the need to range constrain inside this function. * * @tparam Composer * @param a @@ -18,10 +24,17 @@ namespace proof_system::plonk::stdlib { * @return field_t */ template -field_t logic::create_logic_constraint(field_pt& a, field_pt& b, size_t num_bits, bool is_xor_gate) +field_t logic::create_logic_constraint( + field_pt& a, + field_pt& b, + size_t num_bits, + bool is_xor_gate, + const std::function(uint256_t, uint256_t, size_t)>& get_chunk) { - // can't extend past field size! + // ensure the number of bits doesn't exceed field size and is not negatove ASSERT(num_bits < 254); + ASSERT(num_bits > 0); + if (a.is_constant() && b.is_constant()) { uint256_t a_native(a.get_value()); uint256_t b_native(b.get_value()); @@ -31,59 +44,72 @@ field_t logic::create_logic_constraint(field_pt& a, field_pt if (a.is_constant() && !b.is_constant()) { Composer* ctx = b.get_context(); uint256_t a_native(a.get_value()); - field_t a_witness = field_pt::from_witness_index(ctx, ctx->put_constant_variable(a_native)); - return create_logic_constraint(a_witness, b, num_bits, is_xor_gate); + field_pt a_witness = field_pt::from_witness_index(ctx, ctx->put_constant_variable(a_native)); + return create_logic_constraint(a_witness, b, num_bits, is_xor_gate, get_chunk); } if (!a.is_constant() && b.is_constant()) { Composer* ctx = a.get_context(); uint256_t b_native(b.get_value()); field_pt b_witness = field_pt::from_witness_index(ctx, ctx->put_constant_variable(b_native)); - return create_logic_constraint(a, b_witness, num_bits, is_xor_gate); + return create_logic_constraint(a, b_witness, num_bits, is_xor_gate, get_chunk); } if constexpr (Composer::type == ComposerType::PLOOKUP) { Composer* ctx = a.get_context(); const size_t num_chunks = (num_bits / 32) + ((num_bits % 32 == 0) ? 0 : 1); - uint256_t left(a.get_value()); - uint256_t right(b.get_value()); + auto left((uint256_t)a.get_value()); + auto right((uint256_t)b.get_value()); + + field_pt a_accumulator(barretenberg::fr::zero()); + field_pt b_accumulator(barretenberg::fr::zero()); field_pt res(ctx, 0); for (size_t i = 0; i < num_chunks; ++i) { - uint256_t left_chunk = left & ((uint256_t(1) << 32) - 1); - uint256_t right_chunk = right & ((uint256_t(1) << 32) - 1); - - const field_pt a_chunk = witness_pt(ctx, left_chunk); - const field_pt b_chunk = witness_pt(ctx, right_chunk); + size_t chunk_size = (i != num_chunks - 1) ? 32 : num_bits - i * 32; + auto [left_chunk, right_chunk] = get_chunk(left, right, chunk_size); + field_pt a_chunk = witness_pt(ctx, left_chunk); + field_pt b_chunk = witness_pt(ctx, right_chunk); field_pt result_chunk = 0; if (is_xor_gate) { result_chunk = stdlib::plookup_read::read_from_2_to_1_table(plookup::MultiTableId::UINT32_XOR, a_chunk, b_chunk); + } else { result_chunk = stdlib::plookup_read::read_from_2_to_1_table(plookup::MultiTableId::UINT32_AND, a_chunk, b_chunk); } - uint256_t scaling_factor = uint256_t(1) << (32 * i); - res += result_chunk * scaling_factor; + auto scaling_factor = uint256_t(1) << (32 * i); + a_accumulator += a_chunk * scaling_factor; + b_accumulator += b_chunk * scaling_factor; - if (i == num_chunks - 1) { - const size_t final_num_bits = num_bits - (i * 32); - if (final_num_bits != 32) { - ctx->create_range_constraint(a_chunk.witness_index, final_num_bits, "bad range on a"); - ctx->create_range_constraint(b_chunk.witness_index, final_num_bits, "bad range on b"); - } + if (chunk_size != 32) { + ctx->create_range_constraint( + a_chunk.witness_index, chunk_size, "stdlib logic: bad range on final chunk of left operand"); + ctx->create_range_constraint( + b_chunk.witness_index, chunk_size, "stdlib logic: bad range on final chunk of right operand"); } + res += result_chunk * scaling_factor; + left = left >> 32; right = right >> 32; } + field_pt a_slice = a.slice(static_cast(num_bits - 1), 0)[1]; + field_pt b_slice = b.slice(static_cast(num_bits - 1), 0)[1]; + a_slice.assert_equal(a_accumulator, "stdlib logic: failed to reconstruct left operand"); + b_slice.assert_equal(b_accumulator, "stdlib logic: failed to reconstruct right operand"); return res; } else { + // If the composer doesn't have lookups we call the expensive logic constraint gate + // which creates constraints for each bit. We only create constraints up to num_bits. Composer* ctx = a.get_context(); + field_pt a_slice = a.slice(static_cast(num_bits - 1), 0)[1]; + field_pt b_slice = b.slice(static_cast(num_bits - 1), 0)[1]; auto accumulator_triple = ctx->create_logic_constraint( - a.normalize().get_witness_index(), b.normalize().get_witness_index(), num_bits, is_xor_gate); + a_slice.normalize().get_witness_index(), b_slice.normalize().get_witness_index(), num_bits, is_xor_gate); auto out_idx = accumulator_triple.out[accumulator_triple.out.size() - 1]; return field_t::from_witness_index(ctx, out_idx); } diff --git a/cpp/src/barretenberg/stdlib/primitives/logic/logic.hpp b/cpp/src/barretenberg/stdlib/primitives/logic/logic.hpp index 2b8b99d560..1ec450de00 100644 --- a/cpp/src/barretenberg/stdlib/primitives/logic/logic.hpp +++ b/cpp/src/barretenberg/stdlib/primitives/logic/logic.hpp @@ -1,17 +1,31 @@ #pragma once +#include "barretenberg/numeric/uint256/uint256.hpp" #include "barretenberg/stdlib/primitives/composers/composers_fwd.hpp" #include "barretenberg/stdlib/primitives/field/field.hpp" #include "barretenberg/stdlib/primitives/witness/witness.hpp" +#include +#include +#include namespace proof_system::plonk::stdlib { template class logic { - private: + public: using field_pt = field_t; using witness_pt = witness_t; public: - static field_pt create_logic_constraint(field_pt& a, field_pt& b, size_t num_bits, bool is_xor_gate); + static field_pt create_logic_constraint( + field_pt& a, + field_pt& b, + size_t num_bits, + bool is_xor_gate, + const std::function(uint256_t, uint256_t, size_t)>& get_chunk = + [](uint256_t left, uint256_t right, size_t chunk_size) { + uint256_t left_chunk = left & ((uint256_t(1) << chunk_size) - 1); + uint256_t right_chunk = right & ((uint256_t(1) << chunk_size) - 1); + return std::make_pair(left_chunk, right_chunk); + }); }; EXTERN_STDLIB_TYPE(logic); diff --git a/cpp/src/barretenberg/stdlib/primitives/logic/logic.test.cpp b/cpp/src/barretenberg/stdlib/primitives/logic/logic.test.cpp index bf1dd73e5f..188a32980c 100644 --- a/cpp/src/barretenberg/stdlib/primitives/logic/logic.test.cpp +++ b/cpp/src/barretenberg/stdlib/primitives/logic/logic.test.cpp @@ -1,6 +1,7 @@ #include "../bool/bool.hpp" +#include "barretenberg/numeric/uint256/uint256.hpp" +#include "barretenberg/proof_system/types/composer_type.hpp" #include "logic.hpp" -#include "barretenberg/plonk/proof_system/constants.hpp" #include #include "barretenberg/honk/composer/standard_honk_composer.hpp" #include "barretenberg/plonk/composer/standard_composer.hpp" @@ -8,6 +9,15 @@ #include "barretenberg/plonk/composer/turbo_composer.hpp" #include "barretenberg/numeric/random/engine.hpp" +#pragma GCC diagnostic ignored "-Wunused-local-typedefs" + +#define STDLIB_TYPE_ALIASES \ + using Composer = TypeParam; \ + using witness_ct = stdlib::witness_t; \ + using field_ct = stdlib::field_t; \ + using bool_ct = stdlib::bool_t; \ + using public_witness_ct = stdlib::public_witness_t; + namespace test_stdlib_logic { namespace { @@ -19,81 +29,137 @@ template void ignore_unused(T&) {} // use to ignore unused variables i using namespace barretenberg; using namespace proof_system::plonk; -template class stdlib_logic : public testing::Test { - typedef stdlib::bool_t bool_ct; - typedef stdlib::field_t field_ct; - typedef stdlib::witness_t witness_ct; - typedef stdlib::public_witness_t public_witness_ct; - - public: - /** - * @brief Test logic - */ - static void test_logic() - { - Composer composer; - auto run_test = [&](size_t num_bits) { - uint256_t mask = (uint256_t(1) << num_bits) - 1; - - uint256_t a = engine.get_random_uint256() & mask; - uint256_t b = engine.get_random_uint256() & mask; - - uint256_t and_expected = a & b; - uint256_t xor_expected = a ^ b; - - field_ct x = witness_ct(&composer, a); - field_ct y = witness_ct(&composer, b); - - field_ct x_const(&composer, a); - field_ct y_const(&composer, b); - field_ct and_result = stdlib::logic::create_logic_constraint(x, y, num_bits, false); - field_ct xor_result = stdlib::logic::create_logic_constraint(x, y, num_bits, true); - - field_ct and_result_left_constant = - stdlib::logic::create_logic_constraint(x_const, y, num_bits, false); - field_ct xor_result_left_constant = - stdlib::logic::create_logic_constraint(x_const, y, num_bits, true); - - field_ct and_result_right_constant = - stdlib::logic::create_logic_constraint(x, y_const, num_bits, false); - field_ct xor_result_right_constant = - stdlib::logic::create_logic_constraint(x, y_const, num_bits, true); - - field_ct and_result_both_constant = - stdlib::logic::create_logic_constraint(x_const, y_const, num_bits, false); - field_ct xor_result_both_constant = - stdlib::logic::create_logic_constraint(x_const, y_const, num_bits, true); - - EXPECT_EQ(uint256_t(and_result.get_value()), and_expected); - EXPECT_EQ(uint256_t(and_result_left_constant.get_value()), and_expected); - EXPECT_EQ(uint256_t(and_result_right_constant.get_value()), and_expected); - EXPECT_EQ(uint256_t(and_result_both_constant.get_value()), and_expected); - - EXPECT_EQ(uint256_t(xor_result.get_value()), xor_expected); - EXPECT_EQ(uint256_t(xor_result_left_constant.get_value()), xor_expected); - EXPECT_EQ(uint256_t(xor_result_right_constant.get_value()), xor_expected); - EXPECT_EQ(uint256_t(xor_result_both_constant.get_value()), xor_expected); - }; - - for (size_t i = 8; i < 248; i += 8) { - run_test(i); - } - auto prover = composer.create_prover(); - plonk::proof proof = prover.construct_proof(); - auto verifier = composer.create_verifier(); - bool result = verifier.verify_proof(proof); +template class LogicTest : public testing::Test {}; - EXPECT_EQ(result, true); - } -}; +using ComposerTypes = + ::testing::Types; + +TYPED_TEST_SUITE(LogicTest, ComposerTypes); + +TYPED_TEST(LogicTest, TestCorrectLogic) +{ + STDLIB_TYPE_ALIASES + + auto run_test = [](size_t num_bits, Composer& composer) { + uint256_t mask = (uint256_t(1) << num_bits) - 1; + + uint256_t a = engine.get_random_uint256() & mask; + uint256_t b = engine.get_random_uint256() & mask; + + uint256_t and_expected = a & b; + uint256_t xor_expected = a ^ b; + + field_ct x = witness_ct(&composer, a); + field_ct y = witness_ct(&composer, b); + + field_ct x_const(&composer, a); + field_ct y_const(&composer, b); + + field_ct and_result = stdlib::logic::create_logic_constraint(x, y, num_bits, false); + field_ct xor_result = stdlib::logic::create_logic_constraint(x, y, num_bits, true); + + field_ct and_result_left_constant = + stdlib::logic::create_logic_constraint(x_const, y, num_bits, false); + field_ct xor_result_left_constant = + stdlib::logic::create_logic_constraint(x_const, y, num_bits, true); -typedef testing::Types - ComposerTypes; + field_ct and_result_right_constant = + stdlib::logic::create_logic_constraint(x, y_const, num_bits, false); + field_ct xor_result_right_constant = + stdlib::logic::create_logic_constraint(x, y_const, num_bits, true); -TYPED_TEST_SUITE(stdlib_logic, ComposerTypes); + field_ct and_result_both_constant = + stdlib::logic::create_logic_constraint(x_const, y_const, num_bits, false); + field_ct xor_result_both_constant = + stdlib::logic::create_logic_constraint(x_const, y_const, num_bits, true); -TYPED_TEST(stdlib_logic, test_logic) + EXPECT_EQ(uint256_t(and_result.get_value()), and_expected); + EXPECT_EQ(uint256_t(and_result_left_constant.get_value()), and_expected); + EXPECT_EQ(uint256_t(and_result_right_constant.get_value()), and_expected); + EXPECT_EQ(uint256_t(and_result_both_constant.get_value()), and_expected); + + EXPECT_EQ(uint256_t(xor_result.get_value()), xor_expected); + EXPECT_EQ(uint256_t(xor_result_left_constant.get_value()), xor_expected); + EXPECT_EQ(uint256_t(xor_result_right_constant.get_value()), xor_expected); + EXPECT_EQ(uint256_t(xor_result_both_constant.get_value()), xor_expected); + }; + + auto composer = Composer(); + for (size_t i = 8; i < 248; i += 8) { + run_test(8, composer); + } + auto prover = composer.create_prover(); + plonk::proof proof = prover.construct_proof(); + auto verifier = composer.create_verifier(); + bool result = verifier.verify_proof(proof); + EXPECT_EQ(result, true); +} + +// Tests the constraints will fail if the operands are larger than expected even though the result contains the correct +// number of bits when using the UltraComposer This is because the range constraints on the right and left operand are +// not being satisfied. +TYPED_TEST(LogicTest, LargeOperands) +{ + STDLIB_TYPE_ALIASES + auto composer = Composer(); + + uint256_t mask = (uint256_t(1) << 48) - 1; + uint256_t a = engine.get_random_uint256() & mask; + uint256_t b = engine.get_random_uint256() & mask; + + uint256_t expected_mask = (uint256_t(1) << 40) - 1; + uint256_t and_expected = (a & b) & expected_mask; + uint256_t xor_expected = (a ^ b) & expected_mask; + + field_ct x = witness_ct(&composer, a); + field_ct y = witness_ct(&composer, b); + + field_ct xor_result = stdlib::logic::create_logic_constraint(x, y, 40, true); + field_ct and_result = stdlib::logic::create_logic_constraint(x, y, 40, false); + EXPECT_EQ(uint256_t(and_result.get_value()), and_expected); + EXPECT_EQ(uint256_t(xor_result.get_value()), xor_expected); + + auto prover = composer.create_prover(); + plonk::proof proof = prover.construct_proof(); + auto verifier = composer.create_verifier(); + bool result = verifier.verify_proof(proof); + EXPECT_EQ(result, true); +} + +// Ensures that malicious witnesses which produce the same result are detected. This potential security issue cannot +// happen if the composer doesn't support lookup gates because constraints will be created for each bit of the left and +// right operand. +TYPED_TEST(LogicTest, DifferentWitnessSameResult) { - TestFixture::test_logic(); + + STDLIB_TYPE_ALIASES + auto composer = Composer(); + if (Composer::type == ComposerType::PLOOKUP) { + uint256_t a = 3758096391; + uint256_t b = 2147483649; + field_ct x = witness_ct(&composer, uint256_t(a)); + field_ct y = witness_ct(&composer, uint256_t(b)); + + uint256_t xor_expected = a ^ b; + const std::function(uint256_t, uint256_t, size_t)>& get_bad_chunk = + [](uint256_t left, uint256_t right, size_t chunk_size) { + (void)left; + (void)right; + (void)chunk_size; + auto left_chunk = uint256_t(2684354565); + auto right_chunk = uint256_t(3221225475); + return std::make_pair(left_chunk, right_chunk); + }; + + field_ct xor_result = stdlib::logic::create_logic_constraint(x, y, 32, true, get_bad_chunk); + EXPECT_EQ(uint256_t(xor_result.get_value()), xor_expected); + + auto prover = composer.create_prover(); + plonk::proof proof = prover.construct_proof(); + auto verifier = composer.create_verifier(); + bool result = verifier.verify_proof(proof); + EXPECT_EQ(result, false); + } } + } // namespace test_stdlib_logic \ No newline at end of file diff --git a/cpp/src/barretenberg/stdlib/recursion/transcript/transcript.hpp b/cpp/src/barretenberg/stdlib/recursion/transcript/transcript.hpp index db3d95c51b..a411e64837 100644 --- a/cpp/src/barretenberg/stdlib/recursion/transcript/transcript.hpp +++ b/cpp/src/barretenberg/stdlib/recursion/transcript/transcript.hpp @@ -183,7 +183,7 @@ template class Transcript { } const size_t bytes_per_element = 31; - // split work_element into 2 limbs and insert into element_buffer + // split element into 2 limbs and insert into element_buffer // each entry in element_buffer is 31 bytes const auto split = [&](field_pt& work_element, std::vector& element_buffer, @@ -301,8 +301,23 @@ template class Transcript { } byte_array compressed_buffer(T0); - byte_array base_hash = stdlib::blake3s(compressed_buffer); - + // TODO(@zac-williamson) make this a Poseidon hash + byte_array base_hash; + if constexpr (Composer::type == ComposerType::PLOOKUP) { + std::vector compression_buffer; + field_pt working_element(context); + size_t byte_counter = 0; + split(working_element, compression_buffer, field_pt(compressed_buffer), byte_counter, 32); + if (byte_counter != 0) { + const uint256_t down_shift = uint256_t(1) << uint256_t((bytes_per_element - byte_counter) * 8); + working_element = working_element / barretenberg::fr(down_shift); + working_element = working_element.normalize(); + compression_buffer.push_back(working_element); + } + base_hash = stdlib::pedersen_plookup_commitment::compress(compression_buffer); + } else { + base_hash = stdlib::blake3s(compressed_buffer); + } byte_array first(field_pt(0), 16); first.write(base_hash.slice(0, 16)); round_challenges.push_back(first); @@ -313,11 +328,30 @@ template class Transcript { round_challenges.push_back(second); } + // This block of code only executes for num_challenges > 2, which (currently) only happens in the nu round when + // we need to generate short scalars. In this case, we generate 32-byte challenges and split them in half to get + // the relevant challenges. for (size_t i = 2; i < num_challenges; i += 2) { byte_array rolling_buffer = base_hash; - rolling_buffer.write(byte_array(field_pt(i / 2), 1)); - byte_array hash_output = stdlib::blake3s(rolling_buffer); - + byte_array hash_output; + if constexpr (Composer::type == ComposerType::PLOOKUP) { + // TODO(@zac-williamson) make this a Poseidon hash not a Pedersen hash + std::vector compression_buffer; + field_pt working_element(context); + size_t byte_counter = 0; + split(working_element, compression_buffer, field_pt(rolling_buffer), byte_counter, 32); + split(working_element, compression_buffer, field_pt(field_pt(i / 2)), byte_counter, 1); + if (byte_counter != 0) { + const uint256_t down_shift = uint256_t(1) << uint256_t((bytes_per_element - byte_counter) * 8); + working_element = working_element / barretenberg::fr(down_shift); + working_element = working_element.normalize(); + compression_buffer.push_back(working_element); + } + hash_output = stdlib::pedersen_plookup_commitment::compress(compression_buffer); + } else { + rolling_buffer.write(byte_array(field_pt(i / 2), 1)); + hash_output = stdlib::blake3s(rolling_buffer); + } byte_array hi(field_pt(0), 16); hi.write(hash_output.slice(0, 16)); round_challenges.push_back(hi); diff --git a/cpp/src/barretenberg/transcript/transcript.cpp b/cpp/src/barretenberg/transcript/transcript.cpp index 91349628b9..9c448949c9 100644 --- a/cpp/src/barretenberg/transcript/transcript.cpp +++ b/cpp/src/barretenberg/transcript/transcript.cpp @@ -54,6 +54,19 @@ std::array Blake3sHasher::hash(std::ve return result; } +std::array Blake3sHasher::hash_plookup(std::vector const& buffer) +{ + // TODO(@zac-williamson) Change to call a Poseidon hash and create a PoseidonHasher + // (not making the name change right now as it will break concurrent work w. getting recursion working in Noir) + // We also need to implement a Poseidon gadget + std::vector compressed_buffer = crypto::pedersen_commitment::lookup::compress_native(buffer); + std::array result; + for (size_t i = 0; i < PRNG_OUTPUT_SIZE; ++i) { + result[i] = compressed_buffer[i]; + } + return result; +} + Transcript::Transcript(const std::vector& input_transcript, const Manifest input_manifest, const HashType hash_type, @@ -219,7 +232,7 @@ void Transcript::apply_fiat_shamir(const std::string& challenge_name /*, const b } case HashType::PlookupPedersenBlake3s: { std::vector compressed_buffer = crypto::pedersen_commitment::lookup::compress_native(buffer); - base_hash = Blake3sHasher::hash(compressed_buffer); + base_hash = Blake3sHasher::hash_plookup(compressed_buffer); break; } default: { @@ -269,7 +282,7 @@ void Transcript::apply_fiat_shamir(const std::string& challenge_name /*, const b break; } case HashType::PlookupPedersenBlake3s: { - hash_output = Blake3sHasher::hash(rolling_buffer); + hash_output = Blake3sHasher::hash_plookup(rolling_buffer); break; } default: { diff --git a/cpp/src/barretenberg/transcript/transcript.hpp b/cpp/src/barretenberg/transcript/transcript.hpp index 43d47e3972..92c73c77e4 100644 --- a/cpp/src/barretenberg/transcript/transcript.hpp +++ b/cpp/src/barretenberg/transcript/transcript.hpp @@ -22,6 +22,7 @@ struct Blake3sHasher { static constexpr size_t PRNG_OUTPUT_SIZE = 32; static std::array hash(std::vector const& input); + static std::array hash_plookup(std::vector const& input); }; enum HashType { Keccak256, PedersenBlake3s, PlookupPedersenBlake3s }; From 25f55742a84533a0e566da26356a755e5e68389f Mon Sep 17 00:00:00 2001 From: vezenovm Date: Tue, 25 Apr 2023 18:11:40 +0000 Subject: [PATCH 11/41] EnvReferenceString undefined reference to error workaround --- cpp/src/barretenberg/env/crs.cpp | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/cpp/src/barretenberg/env/crs.cpp b/cpp/src/barretenberg/env/crs.cpp index 80561d1c09..47e6b52b0a 100644 --- a/cpp/src/barretenberg/env/crs.cpp +++ b/cpp/src/barretenberg/env/crs.cpp @@ -3,17 +3,19 @@ #include "crs.hpp" #include "barretenberg/srs/reference_string/file_reference_string.hpp" +// TODO: Will get `undefined reference to `bbmalloc'` error when linking #include "barretenberg/ecc/curves/bn254/scalar_multiplication/c_bind.hpp" +#include "barretenberg/common/mem.hpp" const int NUM_POINTS_IN_TRANSCRIPT = 5040001; - extern "C" { /** * @brief In WASM, loads the verifier reference string. * Used in native code to quickly create an in-memory reference string. */ -uint8_t* env_load_verifier_crs() { +uint8_t* env_load_verifier_crs() +{ std::ifstream transcript; transcript.open("../srs_db/ignition/monomial/transcript00.dat", std::ifstream::binary); // We need two g2 points, each 64 bytes. @@ -22,7 +24,7 @@ uint8_t* env_load_verifier_crs() { transcript.seekg(28 + NUM_POINTS_IN_TRANSCRIPT * 64); transcript.read((char*)g2_points.data(), (std::streamsize)g2_points_size); transcript.close(); - auto* g2_points_copy = (uint8_t*)bbmalloc(g2_points_size); + auto* g2_points_copy = (uint8_t*)aligned_alloc(64, g2_points_size); memcpy(g2_points_copy, g2_points.data(), g2_points_size); return g2_points_copy; } @@ -33,20 +35,20 @@ uint8_t* env_load_verifier_crs() { * In native code, not intended to be used. * @param num_points The number of points to load. */ -uint8_t* env_load_prover_crs(size_t num_points) { +uint8_t* env_load_prover_crs(size_t num_points) +{ // Note: This implementation is only meant to be instructive. // This should only be used in c-binds to implement the C++ abstractions. std::ifstream transcript; transcript.open("../srs_db/ignition/monomial/transcript00.dat", std::ifstream::binary); // Each g1 point is 64 bytes. - size_t g1_points_size = (num_points) * 64; + size_t g1_points_size = (num_points)*64; std::vector g1_points(g1_points_size); transcript.seekg(28); transcript.read((char*)g1_points.data(), (std::streamsize)g1_points_size); transcript.close(); - auto* g1_points_copy = (uint8_t*)bbmalloc(g1_points_size); + auto* g1_points_copy = (uint8_t*)aligned_alloc(64, g1_points_size); memcpy(g1_points_copy, g1_points.data(), g1_points_size); return g1_points_copy; } - } \ No newline at end of file From 1c8cc772b123088e81a8589d5df50deb963f84dd Mon Sep 17 00:00:00 2001 From: vezenovm Date: Tue, 25 Apr 2023 18:23:02 +0000 Subject: [PATCH 12/41] add missing recursion_constraint field from acir_format tests --- cpp/src/barretenberg/dsl/acir_format/acir_format.test.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/cpp/src/barretenberg/dsl/acir_format/acir_format.test.cpp b/cpp/src/barretenberg/dsl/acir_format/acir_format.test.cpp index 5c17c353c1..5c6534750a 100644 --- a/cpp/src/barretenberg/dsl/acir_format/acir_format.test.cpp +++ b/cpp/src/barretenberg/dsl/acir_format/acir_format.test.cpp @@ -31,6 +31,7 @@ TEST(acir_format, test_a_single_constraint_no_pub_inputs) .hash_to_field_constraints = {}, .pedersen_constraints = {}, .merkle_membership_constraints = {}, + .recursion_constraints = {}, .constraints = { constraint }, }; @@ -129,6 +130,7 @@ TEST(acir_format, test_logic_gate_from_noir_circuit) .hash_to_field_constraints = {}, .pedersen_constraints = {}, .merkle_membership_constraints = {}, + .recursion_constraints = {}, .constraints = { expr_a, expr_b, expr_c, expr_d }, }; @@ -193,6 +195,7 @@ TEST(acir_format, test_schnorr_verify_pass) .hash_to_field_constraints = {}, .pedersen_constraints = {}, .merkle_membership_constraints = {}, + .recursion_constraints = {}, .constraints = { poly_triple{ .a = schnorr_constraint.result, .b = schnorr_constraint.result, @@ -262,6 +265,7 @@ TEST(acir_format, test_schnorr_verify_small_range) .hash_to_field_constraints = {}, .pedersen_constraints = {}, .merkle_membership_constraints = {}, + .recursion_constraints = {}, .constraints = { poly_triple{ .a = schnorr_constraint.result, .b = schnorr_constraint.result, From 6109828cb940f8a1155d0f605b88037a504d44cb Mon Sep 17 00:00:00 2001 From: vezenovm Date: Tue, 25 Apr 2023 18:26:51 +0000 Subject: [PATCH 13/41] add recursion_constraints field back in acir_proofs.test inner circuit --- cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.test.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.test.cpp b/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.test.cpp index b344086ca3..cb73eea86e 100644 --- a/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.test.cpp +++ b/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.test.cpp @@ -87,6 +87,7 @@ void create_inner_circuit(acir_format::acir_format& constraint_system, std::vect .hash_to_field_constraints = {}, .pedersen_constraints = {}, .merkle_membership_constraints = {}, + .recursion_constraints = {}, .constraints = { expr_a, expr_b, expr_c, expr_d }, }; From 8358741f881a3b28534f0f9f7584d59266cedc86 Mon Sep 17 00:00:00 2001 From: vezenovm Date: Tue, 25 Apr 2023 18:29:50 +0000 Subject: [PATCH 14/41] fix inner circuit in RecursionConstraint.TestRecursionConstraint --- .../barretenberg/dsl/acir_format/recursion_constraint.test.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.test.cpp b/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.test.cpp index 2b23dcfa07..1b49c0f128 100644 --- a/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.test.cpp +++ b/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.test.cpp @@ -92,6 +92,7 @@ acir_format::Composer create_inner_circuit() .hash_to_field_constraints = {}, .pedersen_constraints = {}, .merkle_membership_constraints = {}, + .recursion_constraints = {}, .constraints = { expr_a, expr_b, expr_c, expr_d }, }; From 551277ba5d73667bbc3921585e7da2c5b9ada2b8 Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Tue, 25 Apr 2023 15:23:26 -0400 Subject: [PATCH 15/41] Empty-Commit From ce46c01cf5d716810062ce848d0e78f6d04e0a28 Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Thu, 27 Apr 2023 23:19:50 -0400 Subject: [PATCH 16/41] add verify_recursive_proof method for simulating recursive verification in the ACVM --- .../dsl/acir_proofs/acir_proofs.cpp | 91 ++++++++++++++++++- .../dsl/acir_proofs/acir_proofs.hpp | 7 ++ .../dsl/acir_proofs/acir_proofs.test.cpp | 2 + .../barretenberg/dsl/acir_proofs/c_bind.cpp | 16 ++++ .../barretenberg/dsl/acir_proofs/c_bind.hpp | 7 ++ 5 files changed, 121 insertions(+), 2 deletions(-) diff --git a/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.cpp b/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.cpp index 54ff5c9aa0..26ba502cd5 100644 --- a/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.cpp +++ b/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.cpp @@ -5,6 +5,7 @@ #include "barretenberg/dsl/types.hpp" #include "barretenberg/srs/reference_string/pippenger_reference_string.hpp" #include "barretenberg/plonk/proof_system/verification_key/sol_gen.hpp" +#include "barretenberg/stdlib/recursion/verifier/verifier.hpp" namespace acir_proofs { @@ -228,8 +229,9 @@ bool verify_proof(uint8_t const* g2x, plonk::proof pp = { std::vector(proof, proof + length) }; // For inner circuit use recursive prover and verifier, then for outer circuit use the normal prover and - // verifier Either need a context flag for recursive verify or a new_recursive_verify_proof method that uses - // regular UltraVerifier + // verifier. + // Either need a context flag for recursive verify or a new_recursive_verify_proof method that uses regular + // UltraVerifier if (is_recursive) { auto verifier = composer.create_verifier(); verified = verifier.verify_proof(pp); @@ -247,4 +249,89 @@ bool verify_proof(uint8_t const* g2x, return verified; } +/** + * @brief Enables simulation of recursion in the ACVM. In order to be able to simulate execute of ACIR, + * a DSL needs to be able to insert into a circuit's witness the correct output aggregation state of a recursive + * verification. The resulting aggergation state is what will be compared against in the recursion constraint. + * + * @param proof_buf - The proof to be verified + * @param proof_length - The length of the proof to enable reading the proof from buffer + * @param vk_buf - The verification key of the circuit being verified + * @param vk_length - The length of the verification eky to enable reading the proof from buffer + * @param public_inputs_buf - The public inputs of the proof being verified. To be included when construct the input + * aggregation state + * @param input_aggregation_obj_buf - The fields represnting the two G1 points that must be fed into a pairing. + * This will be empty if this is the first proof to be recursively. + * @param output_aggregation_obj_buf - Two G1 points that the top-level verifier needs to run a pairing upon to complete + * verification + */ +size_t verify_recursive_proof(uint8_t const* proof_buf, + uint32_t proof_length, + uint8_t const* vk_buf, + uint32_t vk_length, + uint8_t const* public_inputs_buf, + uint8_t const* input_aggregation_obj_buf, + uint8_t** output_aggregation_obj_buf) +{ + // TODO: not doing anything with public_inputs_buf right now because we only have one layer of recursion + // and the previous aggregation state will be empty. When arbitrary depth recursion is available we will have to + // construct the correct input aggregation_state_ct + (void)public_inputs_buf; + (void)input_aggregation_obj_buf; + + acir_format::aggregation_state_ct previous_aggregation; + previous_aggregation.has_data = false; + + std::vector proof_fields(proof_length / 32); + std::vector key_fields(vk_length / 32); + for (size_t i = 0; i < proof_length / 32; i++) { + proof_fields[i] = acir_format::field_ct(barretenberg::fr::serialize_from_buffer(&proof_buf[i * 32])); + } + for (size_t i = 0; i < vk_length / 32; i++) { + key_fields[i] = acir_format::field_ct(barretenberg::fr::serialize_from_buffer(&vk_buf[i * 32])); + } + + acir_format::Composer composer; + + transcript::Manifest manifest = acir_format::Composer::create_unrolled_manifest(1); + // We currently only support RecursionConstraint where inner_proof_contains_recursive_proof = false. + // We would either need a separate ACIR opcode where inner_proof_contains_recursive_proof = true, + // or we need non-witness data to be provided as metadata in the ACIR opcode + std::shared_ptr vkey = + acir_format::verification_key_ct::template from_field_pt_vector(&composer, key_fields); + vkey->program_width = acir_format::noir_recursive_settings::program_width; + acir_format::Transcript_ct transcript(&composer, manifest, proof_fields, 1); + acir_format::aggregation_state_ct result = + proof_system::plonk::stdlib::recursion::verify_proof_( + &composer, vkey, transcript, previous_aggregation); + + // just writing the output aggregation G1 elements, and no public inputs, proof witnesses, or any other data + const size_t output_size_bytes = 16 * sizeof(barretenberg::fr); + auto raw_buf = (uint8_t*)malloc(output_size_bytes); + + for (size_t i = 0; i < 4; ++i) { + auto field = result.P0.x.binary_basis_limbs[i].element.get_value(); + barretenberg::fr::serialize_to_buffer(field, &raw_buf[i * 32]); + } + + for (size_t i = 4; i < 8; ++i) { + auto field = result.P0.y.binary_basis_limbs[i % 4].element.get_value(); + barretenberg::fr::serialize_to_buffer(field, &raw_buf[i * 32]); + } + + for (size_t i = 8; i < 12; ++i) { + auto field = result.P1.x.binary_basis_limbs[i % 4].element.get_value(); + barretenberg::fr::serialize_to_buffer(field, &raw_buf[i * 32]); + } + + for (size_t i = 12; i < 16; ++i) { + auto field = result.P1.y.binary_basis_limbs[i % 4].element.get_value(); + barretenberg::fr::serialize_to_buffer(field, &raw_buf[i * 32]); + } + + *output_aggregation_obj_buf = raw_buf; + + return output_size_bytes; +} + } // namespace acir_proofs diff --git a/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.hpp b/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.hpp index 95556f11a7..e37756a5d6 100644 --- a/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.hpp +++ b/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.hpp @@ -28,5 +28,12 @@ bool verify_proof(uint8_t const* g2x, uint8_t* proof, uint32_t length, bool is_recursive); +size_t verify_recursive_proof(uint8_t const* proof_buf, + uint32_t proof_length, + uint8_t const* vk_buf, + uint32_t vk_length, + uint8_t const* public_inputs_buf, + uint8_t const* input_aggregation_obj_buf, + uint8_t** output_aggregation_obj_buf); } // namespace acir_proofs diff --git a/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.test.cpp b/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.test.cpp index cb73eea86e..4aaa74c59b 100644 --- a/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.test.cpp +++ b/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.test.cpp @@ -251,6 +251,8 @@ TEST(AcirProofs, TestSerializationWithRecursion) .q_c = -vk_hash_value, }; + std::cout << "vk_hash_value: " << vk_hash_value << std::endl; + std::vector witness; for (size_t i = 0; i < 18; ++i) { witness.emplace_back(0); diff --git a/cpp/src/barretenberg/dsl/acir_proofs/c_bind.cpp b/cpp/src/barretenberg/dsl/acir_proofs/c_bind.cpp index ac26be3fd7..5ff7b29d96 100644 --- a/cpp/src/barretenberg/dsl/acir_proofs/c_bind.cpp +++ b/cpp/src/barretenberg/dsl/acir_proofs/c_bind.cpp @@ -72,4 +72,20 @@ WASM_EXPORT bool acir_proofs_verify_proof(uint8_t const* g2x, { return acir_proofs::verify_proof(g2x, vk_buf, constraint_system_buf, proof, length, is_recursive); } +WASM_EXPORT size_t acir_proofs_verify_recursive_proof(uint8_t const* proof_buf, + uint32_t proof_length, + uint8_t const* vk_buf, + uint32_t vk_length, + uint8_t const* public_inputs_buf, + uint8_t const* input_aggregation_obj_buf, + uint8_t** output_aggregation_obj_buf) +{ + return acir_proofs::verify_recursive_proof(proof_buf, + proof_length, + vk_buf, + vk_length, + public_inputs_buf, + input_aggregation_obj_buf, + output_aggregation_obj_buf); +} } diff --git a/cpp/src/barretenberg/dsl/acir_proofs/c_bind.hpp b/cpp/src/barretenberg/dsl/acir_proofs/c_bind.hpp index 39591b37d1..0db08d5573 100644 --- a/cpp/src/barretenberg/dsl/acir_proofs/c_bind.hpp +++ b/cpp/src/barretenberg/dsl/acir_proofs/c_bind.hpp @@ -34,4 +34,11 @@ WASM_EXPORT bool acir_proofs_verify_proof(uint8_t const* g2x, uint8_t* proof, uint32_t length, bool is_recursive); +WASM_EXPORT size_t acir_proofs_verify_recursive_proof(uint8_t const* proof_buf, + uint32_t proof_length, + uint8_t const* vk_buf, + uint32_t vk_length, + uint8_t const* public_inputs_buf, + uint8_t const* input_aggregation_obj_buf, + uint8_t** output_aggregation_obj_buf); } From 11e97beb64195b6b0d1b432bc9bd62e6cb77dbc5 Mon Sep 17 00:00:00 2001 From: vezenovm Date: Fri, 28 Apr 2023 20:20:14 +0000 Subject: [PATCH 17/41] add ecc module to env package to fix linking of ennv_load_prover_crs and env_load_verifier_crs --- cpp/src/barretenberg/env/CMakeLists.txt | 2 +- cpp/src/barretenberg/env/crs.cpp | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/cpp/src/barretenberg/env/CMakeLists.txt b/cpp/src/barretenberg/env/CMakeLists.txt index f4a8428e63..e084984b85 100644 --- a/cpp/src/barretenberg/env/CMakeLists.txt +++ b/cpp/src/barretenberg/env/CMakeLists.txt @@ -1 +1 @@ -barretenberg_module(env) \ No newline at end of file +barretenberg_module(env ecc) \ No newline at end of file diff --git a/cpp/src/barretenberg/env/crs.cpp b/cpp/src/barretenberg/env/crs.cpp index 47e6b52b0a..923435057d 100644 --- a/cpp/src/barretenberg/env/crs.cpp +++ b/cpp/src/barretenberg/env/crs.cpp @@ -3,9 +3,7 @@ #include "crs.hpp" #include "barretenberg/srs/reference_string/file_reference_string.hpp" -// TODO: Will get `undefined reference to `bbmalloc'` error when linking #include "barretenberg/ecc/curves/bn254/scalar_multiplication/c_bind.hpp" -#include "barretenberg/common/mem.hpp" const int NUM_POINTS_IN_TRANSCRIPT = 5040001; @@ -24,7 +22,7 @@ uint8_t* env_load_verifier_crs() transcript.seekg(28 + NUM_POINTS_IN_TRANSCRIPT * 64); transcript.read((char*)g2_points.data(), (std::streamsize)g2_points_size); transcript.close(); - auto* g2_points_copy = (uint8_t*)aligned_alloc(64, g2_points_size); + auto* g2_points_copy = (uint8_t*)bbmalloc(g2_points_size); memcpy(g2_points_copy, g2_points.data(), g2_points_size); return g2_points_copy; } @@ -47,7 +45,7 @@ uint8_t* env_load_prover_crs(size_t num_points) transcript.seekg(28); transcript.read((char*)g1_points.data(), (std::streamsize)g1_points_size); transcript.close(); - auto* g1_points_copy = (uint8_t*)aligned_alloc(64, g1_points_size); + auto* g1_points_copy = (uint8_t*)bbmalloc(g1_points_size); memcpy(g1_points_copy, g1_points.data(), g1_points_size); return g1_points_copy; } From 941b27544c948bdb0c3ca0ae353dbea49c42e9c3 Mon Sep 17 00:00:00 2001 From: vezenovm Date: Mon, 1 May 2023 15:31:57 +0000 Subject: [PATCH 18/41] add back reference strinng to stdlib recursion key to pass sol verifier generation tests --- cpp/src/barretenberg/solidity_helpers/CMakeLists.txt | 2 +- .../solidity_helpers/circuits/recursive_circuit.hpp | 5 ++--- .../stdlib/recursion/verification_key/verification_key.hpp | 4 +++- .../barretenberg/stdlib/recursion/verifier/verifier.test.cpp | 4 +++- 4 files changed, 9 insertions(+), 6 deletions(-) diff --git a/cpp/src/barretenberg/solidity_helpers/CMakeLists.txt b/cpp/src/barretenberg/solidity_helpers/CMakeLists.txt index 085b5297e5..8ce6666afa 100644 --- a/cpp/src/barretenberg/solidity_helpers/CMakeLists.txt +++ b/cpp/src/barretenberg/solidity_helpers/CMakeLists.txt @@ -1,4 +1,4 @@ -barretenberg_module(stdlib_solidity_helpers plonk proof_system transcript crypto_pedersen_commitment polynomials crypto_sha256 ecc crypto_blake3s stdlib_primitives stdlib_pedersen_commitment stdlib_blake3s stdlib_blake2s) +barretenberg_module(stdlib_solidity_helpers plonk proof_system transcript crypto_pedersen_commitment polynomials crypto_sha256 ecc crypto_blake3s stdlib_primitives stdlib_pedersen_commitment stdlib_blake3s stdlib_blake2s srs) add_executable(solidity_key_gen key_gen.cpp) diff --git a/cpp/src/barretenberg/solidity_helpers/circuits/recursive_circuit.hpp b/cpp/src/barretenberg/solidity_helpers/circuits/recursive_circuit.hpp index 1226db4ae6..9f2f7786c3 100644 --- a/cpp/src/barretenberg/solidity_helpers/circuits/recursive_circuit.hpp +++ b/cpp/src/barretenberg/solidity_helpers/circuits/recursive_circuit.hpp @@ -106,12 +106,11 @@ template class RecursiveCircuit { public: static OuterComposer generate(std::string srs_path, uint256_t inputs[]) { - auto env_crs = std::make_unique(); - InnerComposer inner_composer = InnerComposer(srs_path); OuterComposer outer_composer = OuterComposer(srs_path); create_inner_circuit_no_tables(inner_composer, inputs); + auto circuit_output = create_outer_circuit(inner_composer, outer_composer); g1::affine_element P[2]; @@ -121,7 +120,7 @@ template class RecursiveCircuit { P[1].y = barretenberg::fq(circuit_output.aggregation_state.P1.y.get_value().lo); barretenberg::fq12 inner_proof_result = barretenberg::pairing::reduced_ate_pairing_batch_precomputed( - P, env_crs->get_verifier_crs()->get_precomputed_g2_lines(), 2); + P, circuit_output.verification_key->reference_string->get_precomputed_g2_lines(), 2); if (inner_proof_result != barretenberg::fq12::one()) { throw_or_abort("inner proof result != 1"); diff --git a/cpp/src/barretenberg/stdlib/recursion/verification_key/verification_key.hpp b/cpp/src/barretenberg/stdlib/recursion/verification_key/verification_key.hpp index 48ce7c81ae..2cd486bbe1 100644 --- a/cpp/src/barretenberg/stdlib/recursion/verification_key/verification_key.hpp +++ b/cpp/src/barretenberg/stdlib/recursion/verification_key/verification_key.hpp @@ -174,6 +174,7 @@ template struct verification_key { std::shared_ptr key = std::make_shared(); // Native data: key->context = ctx; + key->reference_string = input_key->reference_string; key->polynomial_manifest = input_key->polynomial_manifest; // Circuit types: @@ -205,6 +206,7 @@ template struct verification_key { key->commitments.insert({ tag, typename Curve::g1_ct(value) }); } + key->reference_string = input_key->reference_string; key->polynomial_manifest = input_key->polynomial_manifest; return key; @@ -328,7 +330,7 @@ template struct verification_key { std::map commitments; // Native data: - + std::shared_ptr reference_string; PolynomialManifest polynomial_manifest; // Used to check in the circuit if a proof contains any aggregated state. bool contains_recursive_proof = false; diff --git a/cpp/src/barretenberg/stdlib/recursion/verifier/verifier.test.cpp b/cpp/src/barretenberg/stdlib/recursion/verifier/verifier.test.cpp index ad28907bfb..9b57f7f8a6 100644 --- a/cpp/src/barretenberg/stdlib/recursion/verifier/verifier.test.cpp +++ b/cpp/src/barretenberg/stdlib/recursion/verifier/verifier.test.cpp @@ -287,13 +287,15 @@ template class stdlib_verifier : public testing::Test { create_inner_circuit(inner_composer, inner_inputs); auto circuit_output = create_outer_circuit(inner_composer, outer_composer); + std::cout << "circuit_output: " << circuit_output.aggregation_state << std::endl; g1::affine_element P[2]; P[0].x = barretenberg::fq(circuit_output.aggregation_state.P0.x.get_value().lo); P[0].y = barretenberg::fq(circuit_output.aggregation_state.P0.y.get_value().lo); P[1].x = barretenberg::fq(circuit_output.aggregation_state.P1.x.get_value().lo); P[1].y = barretenberg::fq(circuit_output.aggregation_state.P1.y.get_value().lo); - + auto g2_lines = env_crs->get_verifier_crs()->get_precomputed_g2_lines(); + std::cout << "precompute g2 lines: " << g2_lines << std::endl; barretenberg::fq12 inner_proof_result = barretenberg::pairing::reduced_ate_pairing_batch_precomputed( P, env_crs->get_verifier_crs()->get_precomputed_g2_lines(), 2); From 6d86e39d4bc5f7acdcae4e5b41be48de57137d37 Mon Sep 17 00:00:00 2001 From: vezenovm Date: Mon, 1 May 2023 15:33:44 +0000 Subject: [PATCH 19/41] remove prints from recursive verifier test --- .../barretenberg/stdlib/recursion/verifier/verifier.test.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/cpp/src/barretenberg/stdlib/recursion/verifier/verifier.test.cpp b/cpp/src/barretenberg/stdlib/recursion/verifier/verifier.test.cpp index 9b57f7f8a6..ad28907bfb 100644 --- a/cpp/src/barretenberg/stdlib/recursion/verifier/verifier.test.cpp +++ b/cpp/src/barretenberg/stdlib/recursion/verifier/verifier.test.cpp @@ -287,15 +287,13 @@ template class stdlib_verifier : public testing::Test { create_inner_circuit(inner_composer, inner_inputs); auto circuit_output = create_outer_circuit(inner_composer, outer_composer); - std::cout << "circuit_output: " << circuit_output.aggregation_state << std::endl; g1::affine_element P[2]; P[0].x = barretenberg::fq(circuit_output.aggregation_state.P0.x.get_value().lo); P[0].y = barretenberg::fq(circuit_output.aggregation_state.P0.y.get_value().lo); P[1].x = barretenberg::fq(circuit_output.aggregation_state.P1.x.get_value().lo); P[1].y = barretenberg::fq(circuit_output.aggregation_state.P1.y.get_value().lo); - auto g2_lines = env_crs->get_verifier_crs()->get_precomputed_g2_lines(); - std::cout << "precompute g2 lines: " << g2_lines << std::endl; + barretenberg::fq12 inner_proof_result = barretenberg::pairing::reduced_ate_pairing_batch_precomputed( P, env_crs->get_verifier_crs()->get_precomputed_g2_lines(), 2); From 5a458d65e41cbc27e0a03cda759685f8f678dcf1 Mon Sep 17 00:00:00 2001 From: vezenovm Date: Wed, 3 May 2023 18:54:55 +0000 Subject: [PATCH 20/41] fix dirty free for when serializing vk to fields, was working on macbook so not caught earlier, but need more clarity on ubuntu --- cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.cpp | 7 +++++-- cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.test.cpp | 2 -- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.cpp b/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.cpp index 26ba502cd5..527d944b61 100644 --- a/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.cpp +++ b/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.cpp @@ -149,18 +149,21 @@ size_t serialize_verification_key_into_field_elements(uint8_t const* g2x, // NOTE: this output buffer will always have a fixed size! Maybe just precompute? // Cut off 32 bytes as last element is the verification key hash which is not part of the key :o const size_t output_size_bytes = output.size() * sizeof(barretenberg::fr) - 32; + auto raw_buf = (uint8_t*)malloc(output_size_bytes); + auto vk_hash_raw_buf = (uint8_t*)malloc(32); // The serialization code below will convert out of Montgomery form before writing to the buffer - for (size_t i = 0; i < output.size(); ++i) { + for (size_t i = 0; i < output.size() - 1; ++i) { barretenberg::fr::serialize_to_buffer(output[i], &raw_buf[i * 32]); } + barretenberg::fr::serialize_to_buffer(output[output.size() - 1], vk_hash_raw_buf); // copy the vkey into serialized_vk_buf *serialized_vk_buf = raw_buf; // copy the vkey hash into serialized_vk_hash_buf - *serialized_vk_hash_buf = &raw_buf[(output.size() - 1) * 32]; + *serialized_vk_hash_buf = vk_hash_raw_buf; return output_size_bytes; } diff --git a/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.test.cpp b/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.test.cpp index 91ccff081a..bba475b798 100644 --- a/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.test.cpp +++ b/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.test.cpp @@ -251,8 +251,6 @@ TEST(AcirProofs, TestSerializationWithRecursion) .q_c = -vk_hash_value, }; - std::cout << "vk_hash_value: " << vk_hash_value << std::endl; - std::vector witness; for (size_t i = 0; i < 18; ++i) { witness.emplace_back(0); From 07a22f6e3be6d9b2be90c9b8f663299be33f0786 Mon Sep 17 00:00:00 2001 From: vezenovm Date: Fri, 5 May 2023 20:06:11 +0000 Subject: [PATCH 21/41] fix ecdsa tests after master merge --- .../stdlib/encryption/ecdsa/ecdsa.test.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/cpp/src/barretenberg/stdlib/encryption/ecdsa/ecdsa.test.cpp b/cpp/src/barretenberg/stdlib/encryption/ecdsa/ecdsa.test.cpp index 78ac01571f..ca1f4a4940 100644 --- a/cpp/src/barretenberg/stdlib/encryption/ecdsa/ecdsa.test.cpp +++ b/cpp/src/barretenberg/stdlib/encryption/ecdsa/ecdsa.test.cpp @@ -80,8 +80,11 @@ TEST(stdlib_ecdsa, verify_signature_noassert_succeed) std::vector rr(signature.r.begin(), signature.r.end()); std::vector ss(signature.s.begin(), signature.s.end()); + uint8_t vv = signature.v; - stdlib::ecdsa::signature sig{ curve::byte_array_ct(&composer, rr), curve::byte_array_ct(&composer, ss) }; + stdlib::ecdsa::signature sig{ curve::byte_array_ct(&composer, rr), + curve::byte_array_ct(&composer, ss), + stdlib::uint8(&composer, vv) }; curve::byte_array_ct message(&composer, message_string); @@ -125,8 +128,11 @@ TEST(stdlib_ecdsa, verify_signature_noassert_fail) std::vector rr(signature.r.begin(), signature.r.end()); std::vector ss(signature.s.begin(), signature.s.end()); + uint8_t vv = signature.v; - stdlib::ecdsa::signature sig{ curve::byte_array_ct(&composer, rr), curve::byte_array_ct(&composer, ss) }; + stdlib::ecdsa::signature sig{ curve::byte_array_ct(&composer, rr), + curve::byte_array_ct(&composer, ss), + stdlib::uint8(&composer, vv) }; curve::byte_array_ct message(&composer, message_string); From 74a8327b40ee59cc858f9cb325c380b5ed905fb1 Mon Sep 17 00:00:00 2001 From: vezenovm Date: Fri, 5 May 2023 20:15:14 +0000 Subject: [PATCH 22/41] missing keccak constraints fields in acir format and proofs tests --- cpp/src/barretenberg/dsl/acir_format/ecdsa_secp256k1.test.cpp | 2 ++ .../barretenberg/dsl/acir_format/recursion_constraint.test.cpp | 2 ++ cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.test.cpp | 2 ++ 3 files changed, 6 insertions(+) diff --git a/cpp/src/barretenberg/dsl/acir_format/ecdsa_secp256k1.test.cpp b/cpp/src/barretenberg/dsl/acir_format/ecdsa_secp256k1.test.cpp index 9045bab14b..57ce16d248 100644 --- a/cpp/src/barretenberg/dsl/acir_format/ecdsa_secp256k1.test.cpp +++ b/cpp/src/barretenberg/dsl/acir_format/ecdsa_secp256k1.test.cpp @@ -88,6 +88,7 @@ TEST(ECDSASecp256k1, TestECDSAConstraintSucceed) .ecdsa_constraints = { ecdsa_constraint }, .sha256_constraints = {}, .blake2s_constraints = {}, + .keccak_constraints = {}, .hash_to_field_constraints = {}, .pedersen_constraints = {}, .compute_merkle_root_constraints = {}, @@ -127,6 +128,7 @@ TEST(ECDSASecp256k1, TestECDSAConstraintFail) .ecdsa_constraints = { ecdsa_constraint }, .sha256_constraints = {}, .blake2s_constraints = {}, + .keccak_constraints = {}, .hash_to_field_constraints = {}, .pedersen_constraints = {}, .compute_merkle_root_constraints = {}, diff --git a/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.test.cpp b/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.test.cpp index 63b051c478..d68444bc20 100644 --- a/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.test.cpp +++ b/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.test.cpp @@ -89,6 +89,7 @@ acir_format::Composer create_inner_circuit() .ecdsa_constraints = {}, .sha256_constraints = {}, .blake2s_constraints = {}, + .keccak_constraints = {}, .hash_to_field_constraints = {}, .pedersen_constraints = {}, .compute_merkle_root_constraints = {}, @@ -178,6 +179,7 @@ TEST(RecursionConstraint, TestRecursionConstraint) .ecdsa_constraints = {}, .sha256_constraints = {}, .blake2s_constraints = {}, + .keccak_constraints = {}, .hash_to_field_constraints = {}, .pedersen_constraints = {}, .compute_merkle_root_constraints = {}, diff --git a/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.test.cpp b/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.test.cpp index bba475b798..5159cdee9a 100644 --- a/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.test.cpp +++ b/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.test.cpp @@ -84,6 +84,7 @@ void create_inner_circuit(acir_format::acir_format& constraint_system, std::vect .ecdsa_constraints = {}, .sha256_constraints = {}, .blake2s_constraints = {}, + .keccak_constraints = {}, .hash_to_field_constraints = {}, .pedersen_constraints = {}, .compute_merkle_root_constraints = {}, @@ -272,6 +273,7 @@ TEST(AcirProofs, TestSerializationWithRecursion) .ecdsa_constraints = {}, .sha256_constraints = {}, .blake2s_constraints = {}, + .keccak_constraints = {}, .hash_to_field_constraints = {}, .pedersen_constraints = {}, .compute_merkle_root_constraints = {}, From 18cfb94505ce5e5c65feb7d361a6d29bdb2f7de5 Mon Sep 17 00:00:00 2001 From: vezenovm Date: Fri, 5 May 2023 20:19:47 +0000 Subject: [PATCH 23/41] one more missing keccak constraint --- cpp/src/barretenberg/dsl/acir_format/acir_format.test.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/cpp/src/barretenberg/dsl/acir_format/acir_format.test.cpp b/cpp/src/barretenberg/dsl/acir_format/acir_format.test.cpp index 13833e0003..ad8506481a 100644 --- a/cpp/src/barretenberg/dsl/acir_format/acir_format.test.cpp +++ b/cpp/src/barretenberg/dsl/acir_format/acir_format.test.cpp @@ -28,6 +28,7 @@ TEST(acir_format, test_a_single_constraint_no_pub_inputs) .ecdsa_constraints = {}, .sha256_constraints = {}, .blake2s_constraints = {}, + .keccak_constraints = {}, .hash_to_field_constraints = {}, .pedersen_constraints = {}, .compute_merkle_root_constraints = {}, From 155903e1c129576d054e332d2d06208051e854a8 Mon Sep 17 00:00:00 2001 From: vezenovm Date: Mon, 15 May 2023 18:21:12 +0000 Subject: [PATCH 24/41] mismatched acir format structs for gcc build --- cpp/src/barretenberg/dsl/acir_format/acir_format.test.cpp | 1 + cpp/src/barretenberg/dsl/acir_format/block_constraint.test.cpp | 1 + cpp/src/barretenberg/dsl/acir_format/ecdsa_secp256k1.test.cpp | 1 + .../barretenberg/dsl/acir_format/recursion_constraint.test.cpp | 1 + cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.test.cpp | 1 + 5 files changed, 5 insertions(+) diff --git a/cpp/src/barretenberg/dsl/acir_format/acir_format.test.cpp b/cpp/src/barretenberg/dsl/acir_format/acir_format.test.cpp index cd1e4c8e5a..e7c9a53264 100644 --- a/cpp/src/barretenberg/dsl/acir_format/acir_format.test.cpp +++ b/cpp/src/barretenberg/dsl/acir_format/acir_format.test.cpp @@ -33,6 +33,7 @@ TEST(acir_format, test_a_single_constraint_no_pub_inputs) .hash_to_field_constraints = {}, .pedersen_constraints = {}, .compute_merkle_root_constraints = {}, + .block_constraints = {}, .recursion_constraints = {}, .constraints = { constraint }, }; diff --git a/cpp/src/barretenberg/dsl/acir_format/block_constraint.test.cpp b/cpp/src/barretenberg/dsl/acir_format/block_constraint.test.cpp index 66ab73e84a..ae122849cd 100644 --- a/cpp/src/barretenberg/dsl/acir_format/block_constraint.test.cpp +++ b/cpp/src/barretenberg/dsl/acir_format/block_constraint.test.cpp @@ -117,6 +117,7 @@ TEST(up_ram, TestBlockConstraint) .pedersen_constraints = {}, .compute_merkle_root_constraints = {}, .block_constraints = { block }, + .recursion_constraints = {}, .constraints = {}, }; diff --git a/cpp/src/barretenberg/dsl/acir_format/ecdsa_secp256k1.test.cpp b/cpp/src/barretenberg/dsl/acir_format/ecdsa_secp256k1.test.cpp index b405fcdb55..f0b1aab787 100644 --- a/cpp/src/barretenberg/dsl/acir_format/ecdsa_secp256k1.test.cpp +++ b/cpp/src/barretenberg/dsl/acir_format/ecdsa_secp256k1.test.cpp @@ -135,6 +135,7 @@ TEST(ECDSASecp256k1, TestECDSACompilesForVerifier) .pedersen_constraints = {}, .compute_merkle_root_constraints = {}, .block_constraints = {}, + .recursion_constraints = {}, .constraints = {}, }; auto crs_factory = std::make_unique(); diff --git a/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.test.cpp b/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.test.cpp index d68444bc20..c9904740fe 100644 --- a/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.test.cpp +++ b/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.test.cpp @@ -93,6 +93,7 @@ acir_format::Composer create_inner_circuit() .hash_to_field_constraints = {}, .pedersen_constraints = {}, .compute_merkle_root_constraints = {}, + .block_constraints = {}, .recursion_constraints = {}, .constraints = { expr_a, expr_b, expr_c, expr_d }, }; diff --git a/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.test.cpp b/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.test.cpp index 5159cdee9a..00f3b3c26c 100644 --- a/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.test.cpp +++ b/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.test.cpp @@ -88,6 +88,7 @@ void create_inner_circuit(acir_format::acir_format& constraint_system, std::vect .hash_to_field_constraints = {}, .pedersen_constraints = {}, .compute_merkle_root_constraints = {}, + .block_constraints = {}, .recursion_constraints = {}, .constraints = { expr_a, expr_b, expr_c, expr_d }, }; From 683a8759f80890b718e17ceaf6af1b12684a9003 Mon Sep 17 00:00:00 2001 From: vezenovm Date: Mon, 15 May 2023 18:43:58 +0000 Subject: [PATCH 25/41] missing block constraints in acir tests --- .../barretenberg/dsl/acir_format/recursion_constraint.test.cpp | 1 + cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.test.cpp | 1 + 2 files changed, 2 insertions(+) diff --git a/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.test.cpp b/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.test.cpp index c9904740fe..53fe69b979 100644 --- a/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.test.cpp +++ b/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.test.cpp @@ -184,6 +184,7 @@ TEST(RecursionConstraint, TestRecursionConstraint) .hash_to_field_constraints = {}, .pedersen_constraints = {}, .compute_merkle_root_constraints = {}, + .block_constraints = {}, .recursion_constraints = { recursion_constraint }, .constraints = {}, }; diff --git a/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.test.cpp b/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.test.cpp index 00f3b3c26c..08e4e8fbea 100644 --- a/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.test.cpp +++ b/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.test.cpp @@ -278,6 +278,7 @@ TEST(AcirProofs, TestSerializationWithRecursion) .hash_to_field_constraints = {}, .pedersen_constraints = {}, .compute_merkle_root_constraints = {}, + .block_constraints = {}, .recursion_constraints = { recursion_constraint }, .constraints = { vk_equality_constraint }, }; From 7ecb574fd52fcc99de862b7ca75d7d0d9e8a5673 Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Tue, 16 May 2023 18:20:32 +0100 Subject: [PATCH 26/41] feat(dsl)!: Arbitrary depth recursion (#433) * merge conflicts and small updates to get acir_proofs test passing with arbitrary depth cahnges * inline with mv/noir-recursion and working double recursion acir_proofs serialization test * cleanup and working towards supporting full nested proofs, still some bugs in acir_proofs test * full recursive composition test working in acir_proofs * use two public inptus for inner circuit * delete commented out unnecessary acir proofs method * made dummy transcript points unique to prevent point collisions * handle nested proofs when exporting dummy transcript, export recursive proof indices when init vkey, fix full recursive comp test in acir_proofs * update bindings on verify_recursive_proof to accept num public inputs * missing acir_format field in tests * missing one more correct acir_format struct in recursion constraint test * cleanup and additional comment for recursion_constraint * fix up comment in recursion_constraint * remove unnecesary comments when we were including proof outputs as public inputs in the recursion constraint --------- Co-authored-by: zac-williamson --- .../dsl/acir_format/acir_format.cpp | 70 ++- .../dsl/acir_format/recursion_constraint.cpp | 39 +- .../dsl/acir_format/recursion_constraint.hpp | 33 +- .../acir_format/recursion_constraint.test.cpp | 209 ++++++--- .../dsl/acir_proofs/acir_proofs.cpp | 78 +++- .../dsl/acir_proofs/acir_proofs.hpp | 5 +- .../dsl/acir_proofs/acir_proofs.test.cpp | 415 +++++++++++------- .../barretenberg/dsl/acir_proofs/c_bind.cpp | 9 +- .../barretenberg/dsl/acir_proofs/c_bind.hpp | 5 +- .../honk/proof_system/ultra_prover.hpp | 3 +- .../plonk/composer/composer_base.hpp | 15 + .../plonk/composer/ultra_composer.hpp | 17 + .../verification_key/verification_key.cpp | 10 +- .../verification_key/verification_key.hpp | 13 +- .../transcript/transcript_wrappers.hpp | 38 +- 15 files changed, 670 insertions(+), 289 deletions(-) diff --git a/cpp/src/barretenberg/dsl/acir_format/acir_format.cpp b/cpp/src/barretenberg/dsl/acir_format/acir_format.cpp index 28343d3b1f..97ce130665 100644 --- a/cpp/src/barretenberg/dsl/acir_format/acir_format.cpp +++ b/cpp/src/barretenberg/dsl/acir_format/acir_format.cpp @@ -95,8 +95,18 @@ void create_circuit(Composer& composer, const acir_format& constraint_system) } // Add recursion constraints - for (const auto& constraint : constraint_system.recursion_constraints) { - create_recursion_constraints(composer, constraint); + for (size_t i = 0; i < constraint_system.recursion_constraints.size(); ++i) { + auto& constraint = constraint_system.recursion_constraints[i]; + create_recursion_constraints(composer, constraint); + + // make sure the verification key records the public input indices of the final recursion output + // (N.B. up to the ACIR description to make sure that the final output aggregation object wires are public + // inputs!) + if (i == constraint_system.recursion_constraints.size() - 1) { + std::vector proof_output_witness_indices(constraint.output_aggregation_object.begin(), + constraint.output_aggregation_object.end()); + composer.set_recursive_proof(proof_output_witness_indices); + } } } @@ -189,8 +199,18 @@ Composer create_circuit(const acir_format& constraint_system, } // Add recursion constraints - for (const auto& constraint : constraint_system.recursion_constraints) { - create_recursion_constraints(composer, constraint); + for (size_t i = 0; i < constraint_system.recursion_constraints.size(); ++i) { + auto& constraint = constraint_system.recursion_constraints[i]; + create_recursion_constraints(composer, constraint); + + // make sure the verification key records the public input indices of the final recursion output + // (N.B. up to the ACIR description to make sure that the final output aggregation object wires are public + // inputs!) + if (i == constraint_system.recursion_constraints.size() - 1) { + std::vector proof_output_witness_indices(constraint.output_aggregation_object.begin(), + constraint.output_aggregation_object.end()); + composer.set_recursive_proof(proof_output_witness_indices); + } } return composer; @@ -288,8 +308,18 @@ Composer create_circuit_with_witness(const acir_format& constraint_system, } // Add recursion constraints - for (const auto& constraint : constraint_system.recursion_constraints) { - create_recursion_constraints(composer, constraint); + for (size_t i = 0; i < constraint_system.recursion_constraints.size(); ++i) { + auto& constraint = constraint_system.recursion_constraints[i]; + create_recursion_constraints(composer, constraint); + + // make sure the verification key records the public input indices of the final recursion output + // (N.B. up to the ACIR description to make sure that the final output aggregation object wires are public + // inputs!) + if (i == constraint_system.recursion_constraints.size() - 1) { + std::vector proof_output_witness_indices(constraint.output_aggregation_object.begin(), + constraint.output_aggregation_object.end()); + composer.set_recursive_proof(proof_output_witness_indices); + } } return composer; @@ -384,8 +414,18 @@ Composer create_circuit_with_witness(const acir_format& constraint_system, std:: } // Add recursion constraints - for (const auto& constraint : constraint_system.recursion_constraints) { - create_recursion_constraints(composer, constraint); + for (size_t i = 0; i < constraint_system.recursion_constraints.size(); ++i) { + auto& constraint = constraint_system.recursion_constraints[i]; + create_recursion_constraints(composer, constraint); + + // make sure the verification key records the public input indices of the final recursion output + // (N.B. up to the ACIR description to make sure that the final output aggregation object wires are public + // inputs!) + if (i == constraint_system.recursion_constraints.size() - 1) { + std::vector proof_output_witness_indices(constraint.output_aggregation_object.begin(), + constraint.output_aggregation_object.end()); + composer.set_recursive_proof(proof_output_witness_indices); + } } return composer; @@ -478,8 +518,18 @@ void create_circuit_with_witness(Composer& composer, const acir_format& constrai } // Add recursion constraints - for (const auto& constraint : constraint_system.recursion_constraints) { - create_recursion_constraints(composer, constraint); + for (size_t i = 0; i < constraint_system.recursion_constraints.size(); ++i) { + auto& constraint = constraint_system.recursion_constraints[i]; + create_recursion_constraints(composer, constraint); + + // make sure the verification key records the public input indices of the final recursion output + // (N.B. up to the ACIR description to make sure that the final output aggregation object wires are public + // inputs!) + if (i == constraint_system.recursion_constraints.size() - 1) { + std::vector proof_output_witness_indices(constraint.output_aggregation_object.begin(), + constraint.output_aggregation_object.end()); + composer.set_recursive_proof(proof_output_witness_indices); + } } } diff --git a/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.cpp b/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.cpp index 441b47c2d8..1fcf95f594 100644 --- a/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.cpp +++ b/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.cpp @@ -22,9 +22,15 @@ void generate_dummy_proof() {} * We would either need a separate ACIR opcode where inner_proof_contains_recursive_proof = true, * or we need non-witness data to be provided as metadata in the ACIR opcode */ -template +template void create_recursion_constraints(Composer& composer, const RecursionConstraint& input) { + const auto& nested_aggregation_indices = input.nested_aggregation_object; + bool nested_aggregation_indices_all_zero = true; + for (const auto& idx : nested_aggregation_indices) { + nested_aggregation_indices_all_zero &= (idx == 0); + } + const bool inner_proof_contains_recursive_proof = !nested_aggregation_indices_all_zero; // If we do not have a witness, we must ensure that our dummy witness will not trigger // on-curve errors and inverting-zero errors @@ -32,9 +38,10 @@ void create_recursion_constraints(Composer& composer, const RecursionConstraint& // get a fake key/proof that satisfies on-curve + inversion-zero checks const std::vector dummy_key = plonk::verification_key::export_dummy_key_in_recursion_format( PolynomialManifest(Composer::type), inner_proof_contains_recursive_proof); - const auto manifest = Composer::create_unrolled_manifest(1); + const auto manifest = Composer::create_unrolled_manifest(input.public_inputs.size()); const std::vector dummy_proof = - transcript::StandardTranscript::export_dummy_transcript_in_recursion_format(manifest); + transcript::StandardTranscript::export_dummy_transcript_in_recursion_format( + manifest, inner_proof_contains_recursive_proof); for (size_t i = 0; i < input.proof.size(); ++i) { const auto proof_field_idx = input.proof[i]; // if we do NOT have a witness assignment (i.e. are just building the proving/verification keys), @@ -88,12 +95,14 @@ void create_recursion_constraints(Composer& composer, const RecursionConstraint& previous_aggregation.has_data = false; } - transcript::Manifest manifest = Composer::create_unrolled_manifest(1); + transcript::Manifest manifest = Composer::create_unrolled_manifest(input.public_inputs.size()); + std::vector key_fields; key_fields.reserve(input.key.size()); for (const auto& idx : input.key) { key_fields.emplace_back(field_ct::from_witness_index(&composer, idx)); } + std::vector proof_fields; proof_fields.reserve(input.proof.size()); for (const auto& idx : input.proof) { @@ -101,24 +110,22 @@ void create_recursion_constraints(Composer& composer, const RecursionConstraint& } // recursively verify the proof - std::shared_ptr vkey = - verification_key_ct::template from_field_pt_vector(&composer, key_fields); + std::shared_ptr vkey = verification_key_ct::from_field_pt_vector( + &composer, key_fields, inner_proof_contains_recursive_proof, nested_aggregation_indices); vkey->program_width = noir_recursive_settings::program_width; - Transcript_ct transcript(&composer, manifest, proof_fields, 1); + Transcript_ct transcript(&composer, manifest, proof_fields, input.public_inputs.size()); aggregation_state_ct result = proof_system::plonk::stdlib::recursion::verify_proof_( &composer, vkey, transcript, previous_aggregation); // Assign correct witness value to the verification key hash vkey->compress().assert_equal(field_ct::from_witness_index(&composer, input.key_hash)); - // Assign the output aggregation object to the proof public inputs (16 field elements representing two - // G1 points) - result.add_proof_outputs_as_public_inputs(); - - ASSERT(result.public_inputs.size() == 1); + ASSERT(result.public_inputs.size() == input.public_inputs.size()); // Assign the `public_input` field to the public input of the inner proof - result.public_inputs[0].assert_equal(field_ct::from_witness_index(&composer, input.public_input)); + for (size_t i = 0; i < input.public_inputs.size(); ++i) { + result.public_inputs[i].assert_equal(field_ct::from_witness_index(&composer, input.public_inputs[i])); + } // Assign the recursive proof outputs to `output_aggregation_object` for (size_t i = 0; i < result.proof_witness_indices.size(); ++i) { @@ -128,9 +135,7 @@ void create_recursion_constraints(Composer& composer, const RecursionConstraint& } } -template void create_recursion_constraints(Composer&, const RecursionConstraint&); -template void create_recursion_constraints(Composer&, const RecursionConstraint&); -template void create_recursion_constraints(Composer&, const RecursionConstraint&); -template void create_recursion_constraints(Composer&, const RecursionConstraint&); +template void create_recursion_constraints(Composer&, const RecursionConstraint&); +template void create_recursion_constraints(Composer&, const RecursionConstraint&); } // namespace acir_format diff --git a/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.hpp b/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.hpp index cfc0116349..0a0ca53d12 100644 --- a/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.hpp +++ b/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.hpp @@ -24,40 +24,52 @@ namespace acir_format { * (and therefore an aggregation object is present) * @param public_input The index of the single public input * @param input_aggregation_object Witness indices of pre-existing aggregation object (if it exists) - * @param output_aggregation_object Witness indecies of the aggregation object produced by recursive verification + * @param output_aggregation_object Witness indices of the aggregation object produced by recursive verification + * @param nested_aggregation_object Public input indices of an aggregation object inside the proof. * * @note If input_aggregation_object witness indices are all zero, we interpret this to mean that the inner proof does - * NOT contain + * NOT contain a previously recursively verified proof + * @note nested_aggregation_object is used for cases where the proof being verified contains an aggregation object in + * its public inputs! If this is the case, we record the public input locations in `nested_aggregation_object`. If the + * inner proof is of a circuit that does not have a nested aggregation object, these values are all zero. + * + * To outline the interaction between the input_aggergation_object and the nested_aggregation_object take the following + * example: If we have a circuit that verifies 2 proofs A and B, the recursion constraint for B will have an + * input_aggregation_object that points to the aggregation output produced by verifying A. If circuit B also verifies a + * proof, in the above example the recursion constraint for verifying B will have a nested object that describes the + * aggregation object in B’s public inputs as well as an input aggregation object that points to the object produced by + * the previous recursion constraint in the circuit (the one that verifies A) + * */ struct RecursionConstraint { static constexpr size_t AGGREGATION_OBJECT_SIZE = 16; // 16 field elements std::vector key; std::vector proof; - uint32_t public_input; + std::vector public_inputs; uint32_t key_hash; std::array input_aggregation_object; std::array output_aggregation_object; + std::array nested_aggregation_object; friend bool operator==(RecursionConstraint const& lhs, RecursionConstraint const& rhs) = default; }; -template +template void create_recursion_constraints(Composer& composer, const RecursionConstraint& input); -extern template void create_recursion_constraints(Composer&, const RecursionConstraint&); -extern template void create_recursion_constraints(Composer&, const RecursionConstraint&); -extern template void create_recursion_constraints(Composer&, const RecursionConstraint&); -extern template void create_recursion_constraints(Composer&, const RecursionConstraint&); +extern template void create_recursion_constraints(Composer&, const RecursionConstraint&); +extern template void create_recursion_constraints(Composer&, const RecursionConstraint&); template inline void read(B& buf, RecursionConstraint& constraint) { using serialize::read; read(buf, constraint.key); read(buf, constraint.proof); - read(buf, constraint.public_input); + read(buf, constraint.public_inputs); read(buf, constraint.key_hash); read(buf, constraint.input_aggregation_object); read(buf, constraint.output_aggregation_object); + read(buf, constraint.nested_aggregation_object); } template inline void write(B& buf, RecursionConstraint const& constraint) @@ -65,10 +77,11 @@ template inline void write(B& buf, RecursionConstraint const& const using serialize::write; write(buf, constraint.key); write(buf, constraint.proof); - write(buf, constraint.public_input); + write(buf, constraint.public_inputs); write(buf, constraint.key_hash); write(buf, constraint.input_aggregation_object); write(buf, constraint.output_aggregation_object); + write(buf, constraint.nested_aggregation_object); } } // namespace acir_format diff --git a/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.test.cpp b/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.test.cpp index 53fe69b979..c6b511d662 100644 --- a/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.test.cpp +++ b/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.test.cpp @@ -75,13 +75,10 @@ acir_format::Composer create_inner_circuit() .q_o = 0, .q_c = 1, }; - // EXPR [ (1, _4, _5) (-1, _6) 0 ] - // EXPR [ (1, _4, _6) (-1, _4) 0 ] - // EXPR [ (-1, _6) 1 ] acir_format::acir_format constraint_system{ .varnum = 7, - .public_inputs = { 2 }, + .public_inputs = { 2, 3 }, .fixed_base_scalar_mul_constraints = {}, .logic_constraints = { logic_constraint }, .range_constraints = { range_a, range_b }, @@ -112,67 +109,101 @@ acir_format::Composer create_inner_circuit() return composer; } -TEST(RecursionConstraint, TestRecursionConstraint) +/** + * @brief Create a circuit that recursively verifies one or more inner circuits + * + * @param inner_composers + * @return acir_format::Composer + */ +acir_format::Composer create_outer_circuit(std::vector& inner_composers) { - auto inner_composer = create_inner_circuit(); + std::vector recursion_constraints; - auto inner_prover = inner_composer.create_prover(); - auto inner_proof = inner_prover.construct_proof(); - auto inner_verifier = inner_composer.create_verifier(); + // witness count starts at 1 (Composer reserves 1st witness to be the zero-valued zero_idx) + size_t witness_offset = 1; + std::array output_aggregation_object; + std::vector witness; - // std::vector keybuf; - // write(keybuf, *(inner_verifier.key)); + for (size_t i = 0; i < inner_composers.size(); ++i) { + const bool has_input_aggregation_object = i > 0; - std::array output_vars; - for (size_t i = 0; i < 16; ++i) { - // variable idx 1 = public input - // variable idx 2-18 = output_vars - output_vars[i] = (static_cast(i + 3)); - } + auto& inner_composer = inner_composers[i]; + auto inner_prover = inner_composer.create_prover(); + auto inner_proof = inner_prover.construct_proof(); + auto inner_verifier = inner_composer.create_verifier(); - transcript::StandardTranscript transcript(inner_proof.proof_data, - acir_format::Composer::create_manifest(1), - transcript::HashType::PlookupPedersenBlake3s, - 16); + const bool has_nested_proof = inner_verifier.key->contains_recursive_proof; + const size_t num_inner_public_inputs = inner_composer.get_num_public_inputs(); - const std::vector proof_witnesses = transcript.export_transcript_in_recursion_format(); - const std::vector key_witnesses = inner_verifier.key->export_key_in_recursion_format(); + transcript::StandardTranscript transcript(inner_proof.proof_data, + acir_format::Composer::create_manifest(num_inner_public_inputs), + transcript::HashType::PlookupPedersenBlake3s, + 16); - std::vector proof_indices; - const size_t proof_size = proof_witnesses.size(); + const std::vector proof_witnesses = transcript.export_transcript_in_recursion_format(); + const std::vector key_witnesses = inner_verifier.key->export_key_in_recursion_format(); - for (size_t i = 0; i < proof_size; ++i) { - proof_indices.emplace_back(static_cast(i + 19)); - } + const uint32_t key_hash_start_idx = static_cast(witness_offset); + const uint32_t public_input_start_idx = key_hash_start_idx + 1; + const uint32_t output_aggregation_object_start_idx = + static_cast(public_input_start_idx + num_inner_public_inputs + (has_nested_proof ? 16 : 0)); + const uint32_t proof_indices_start_idx = output_aggregation_object_start_idx + 16; + const uint32_t key_indices_start_idx = static_cast(proof_indices_start_idx + proof_witnesses.size()); - std::vector key_indices; - const size_t key_size = key_witnesses.size(); - for (size_t i = 0; i < key_size; ++i) { - key_indices.emplace_back(static_cast(i + 19 + proof_size)); - } - acir_format::RecursionConstraint recursion_constraint{ - .key = key_indices, - .proof = proof_indices, - .public_input = 1, - .key_hash = 2, - .input_aggregation_object = {}, - .output_aggregation_object = output_vars, - }; + std::vector proof_indices; + std::vector key_indices; + std::vector inner_public_inputs; + std::array input_aggregation_object = {}; + std::array nested_aggregation_object = {}; + if (has_input_aggregation_object) { + input_aggregation_object = output_aggregation_object; + } + for (size_t i = 0; i < 16; ++i) { + output_aggregation_object[i] = (static_cast(i + output_aggregation_object_start_idx)); + } + if (has_nested_proof) { + for (size_t i = 0; i < 16; ++i) { + nested_aggregation_object[i] = inner_composer.recursive_proof_public_input_indices[i]; + } + } + for (size_t i = 0; i < proof_witnesses.size(); ++i) { + proof_indices.emplace_back(static_cast(i + proof_indices_start_idx)); + } + const size_t key_size = key_witnesses.size(); + for (size_t i = 0; i < key_size; ++i) { + key_indices.emplace_back(static_cast(i + key_indices_start_idx)); + } + for (size_t i = 0; i < num_inner_public_inputs; ++i) { + inner_public_inputs.push_back(static_cast(i + public_input_start_idx)); + } - std::vector witness; - for (size_t i = 0; i < 18; ++i) { - witness.emplace_back(0); - } - for (const auto& wit : proof_witnesses) { - witness.emplace_back(wit); - } - for (const auto& wit : key_witnesses) { - witness.emplace_back(wit); + acir_format::RecursionConstraint recursion_constraint{ + .key = key_indices, + .proof = proof_indices, + .public_inputs = inner_public_inputs, + .key_hash = key_hash_start_idx, + .input_aggregation_object = input_aggregation_object, + .output_aggregation_object = output_aggregation_object, + .nested_aggregation_object = nested_aggregation_object, + }; + recursion_constraints.push_back(recursion_constraint); + for (size_t i = 0; i < proof_indices_start_idx - witness_offset; ++i) { + witness.emplace_back(0); + } + for (const auto& wit : proof_witnesses) { + witness.emplace_back(wit); + } + for (const auto& wit : key_witnesses) { + witness.emplace_back(wit); + } + witness_offset = key_indices_start_idx + key_witnesses.size(); } + std::vector public_inputs(output_aggregation_object.begin(), output_aggregation_object.end()); + acir_format::acir_format constraint_system{ .varnum = static_cast(witness.size() + 1), - .public_inputs = { 1 }, + .public_inputs = public_inputs, .fixed_base_scalar_mul_constraints = {}, .logic_constraints = {}, .range_constraints = {}, @@ -185,16 +216,84 @@ TEST(RecursionConstraint, TestRecursionConstraint) .pedersen_constraints = {}, .compute_merkle_root_constraints = {}, .block_constraints = {}, - .recursion_constraints = { recursion_constraint }, + .recursion_constraints = recursion_constraints, .constraints = {}, }; auto composer = acir_format::create_circuit_with_witness(constraint_system, witness); - auto prover = composer.create_prover(); + return composer; +} + +TEST(RecursionConstraint, TestBasicDoubleRecursionConstraints) +{ + std::vector layer_1_composers; + layer_1_composers.push_back(create_inner_circuit()); + + layer_1_composers.push_back(create_inner_circuit()); + + auto layer_2_composer = create_outer_circuit(layer_1_composers); + + std::cout << "composer gates = " << layer_2_composer.get_num_gates() << std::endl; + auto prover = layer_2_composer.create_ultra_with_keccak_prover(); + std::cout << "prover gates = " << prover.circuit_size << std::endl; auto proof = prover.construct_proof(); - auto verifier = composer.create_verifier(); + auto verifier = layer_2_composer.create_ultra_with_keccak_verifier(); EXPECT_EQ(verifier.verify_proof(proof), true); +} - EXPECT_EQ(composer.get_variable(1), 10); +TEST(RecursionConstraint, TestFullRecursionConstraints) +{ + /** + * We want to test the following: + * 1. circuit that verifies a proof of another circuit + * 2. the above, but the inner circuit contains a recursive proof output that we have to aggregate + * 3. the above, but the outer circuit verifies 2 proofs, the aggregation outputs from the 2 proofs (+ the recursive + * proof output from 2) are aggregated together + * + * A = basic circuit + * B = circuit that verifies proof of A + * C = circuit that verifies proof of B and a proof of A + * + * Layer 1 = proof of A + * Layer 2 = verifies proof of A and proof of B + * Layer 3 = verifies proof of C + * + * Attempt at a visual graphic + * =========================== + * + * C + * ^ + * | + * | - B + * ^ ^ + * | | + * | -A + * | + * - A + * + * =========================== + * + * Final aggregation object contains aggregated proofs for 2 instances of A and 1 instance of B + */ + std::vector layer_1_composers; + layer_1_composers.push_back(create_inner_circuit()); + std::cout << "created first inner circuit\n"; + std::vector layer_2_composers; + + layer_2_composers.push_back(create_inner_circuit()); + std::cout << "created second inner circuit\n"; + + layer_2_composers.push_back(create_outer_circuit(layer_1_composers)); + std::cout << "created first outer circuit\n"; + + auto layer_3_composer = create_outer_circuit(layer_2_composers); + std::cout << "created second outer circuit\n"; + + std::cout << "composer gates = " << layer_3_composer.get_num_gates() << std::endl; + auto prover = layer_3_composer.create_ultra_with_keccak_prover(); + std::cout << "prover gates = " << prover.circuit_size << std::endl; + auto proof = prover.construct_proof(); + auto verifier = layer_3_composer.create_ultra_with_keccak_verifier(); + EXPECT_EQ(verifier.verify_proof(proof), true); } diff --git a/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.cpp b/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.cpp index 527d944b61..4310342f7e 100644 --- a/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.cpp +++ b/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.cpp @@ -51,12 +51,11 @@ size_t init_proving_key(uint8_t const* constraint_system_buf, uint8_t const** pk { auto constraint_system = from_buffer(constraint_system_buf); - // constraint_system.recursion_constraints[0]. - // We know that we don't actually need any CRS to create a proving key, so just feed in a nothing. // Hacky, but, right now it needs *something*. auto crs_factory = std::make_unique(); auto composer = create_circuit(constraint_system, std::move(crs_factory)); + auto proving_key = composer.compute_proving_key(); auto buffer = to_buffer(*proving_key); @@ -86,6 +85,18 @@ size_t init_verification_key(void* pippenger, uint8_t const* g2x, uint8_t const* // construct the verification key so that we have the correct polynomial manifest verification_key->composer_type = proof_system::ComposerType::PLOOKUP; + // Set the recursive proof indices as this is not done in `compute_verification_key_base` + verification_key->contains_recursive_proof = proving_key->contains_recursive_proof; + for (size_t i = 0; i < 16; ++i) { + if (proving_key->recursive_proof_public_input_indices.size() > i) { + verification_key->recursive_proof_public_input_indices.emplace_back( + proving_key->recursive_proof_public_input_indices[i]); + } else { + verification_key->recursive_proof_public_input_indices.emplace_back(0); + ASSERT(verification_key->contains_recursive_proof == false); + } + } + auto buffer = to_buffer(*verification_key); auto raw_buf = (uint8_t*)malloc(buffer.size()); memcpy(raw_buf, (void*)buffer.data(), buffer.size()); @@ -104,12 +115,15 @@ size_t init_verification_key(void* pippenger, uint8_t const* g2x, uint8_t const* */ size_t serialize_proof_into_field_elements(uint8_t const* proof_data_buf, uint8_t** serialized_proof_data_buf, - size_t proof_data_length) + size_t proof_data_length, + size_t num_inner_public_inputs) { plonk::proof proof = { std::vector(proof_data_buf, &proof_data_buf[proof_data_length]) }; - transcript::StandardTranscript transcript( - proof.proof_data, acir_format::Composer::create_manifest(1), transcript::HashType::PlookupPedersenBlake3s, 16); + transcript::StandardTranscript transcript(proof.proof_data, + acir_format::Composer::create_manifest(num_inner_public_inputs), + transcript::HashType::PlookupPedersenBlake3s, + 16); std::vector output = transcript.export_transcript_in_recursion_format(); @@ -143,7 +157,6 @@ size_t serialize_verification_key_into_field_elements(uint8_t const* g2x, plonk::verification_key_data vk_data; read(vk_buf, vk_data); auto vkey = std::make_shared(std::move(vk_data), crs); - std::vector output = vkey->export_key_in_recursion_format(); // NOTE: this output buffer will always have a fixed size! Maybe just precompute? @@ -272,18 +285,36 @@ size_t verify_recursive_proof(uint8_t const* proof_buf, uint32_t proof_length, uint8_t const* vk_buf, uint32_t vk_length, - uint8_t const* public_inputs_buf, + uint32_t num_public_inputs, uint8_t const* input_aggregation_obj_buf, uint8_t** output_aggregation_obj_buf) { - // TODO: not doing anything with public_inputs_buf right now because we only have one layer of recursion - // and the previous aggregation state will be empty. When arbitrary depth recursion is available we will have to - // construct the correct input aggregation_state_ct - (void)public_inputs_buf; - (void)input_aggregation_obj_buf; + + bool inner_aggregation_all_zero = true; + std::vector aggregation_input(16); + for (size_t i = 0; i < 16; i++) { + aggregation_input[i] = barretenberg::fr::serialize_from_buffer(&input_aggregation_obj_buf[i * 32]); + inner_aggregation_all_zero &= (aggregation_input[i].is_zero()); + } acir_format::aggregation_state_ct previous_aggregation; - previous_aggregation.has_data = false; + if (!inner_aggregation_all_zero) { + std::array aggregation_elements; + for (size_t i = 0; i < 4; ++i) { + aggregation_elements[i] = acir_format::bn254::fq_ct(acir_format::field_ct(aggregation_input[4 * i]), + acir_format::field_ct(aggregation_input[4 * i + 1]), + acir_format::field_ct(aggregation_input[4 * i + 2]), + acir_format::field_ct(aggregation_input[4 * i + 3])); + aggregation_elements[i].assert_is_in_field(); + } + // If we have a previous aggregation object, assign it to `previous_aggregation` so that it is included + // in stdlib::recursion::verify_proof + previous_aggregation.P0 = acir_format::bn254::g1_ct(aggregation_elements[0], aggregation_elements[1]); + previous_aggregation.P1 = acir_format::bn254::g1_ct(aggregation_elements[2], aggregation_elements[3]); + previous_aggregation.has_data = true; + } else { + previous_aggregation.has_data = false; + } std::vector proof_fields(proof_length / 32); std::vector key_fields(vk_length / 32); @@ -296,19 +327,30 @@ size_t verify_recursive_proof(uint8_t const* proof_buf, acir_format::Composer composer; - transcript::Manifest manifest = acir_format::Composer::create_unrolled_manifest(1); + transcript::Manifest manifest = acir_format::Composer::create_unrolled_manifest(num_public_inputs); // We currently only support RecursionConstraint where inner_proof_contains_recursive_proof = false. // We would either need a separate ACIR opcode where inner_proof_contains_recursive_proof = true, // or we need non-witness data to be provided as metadata in the ACIR opcode - std::shared_ptr vkey = - acir_format::verification_key_ct::template from_field_pt_vector(&composer, key_fields); + // recursively verify the proof + std::array nested_aggregation_object = {}; + for (size_t i = 6; i < 22; ++i) { + nested_aggregation_object[i - 6] = uint32_t(key_fields[i].get_value()); + } + bool nested_aggregation_indices_all_zero = true; + for (const auto& idx : nested_aggregation_object) { + nested_aggregation_indices_all_zero &= (idx == 0); + } + const bool inner_proof_contains_recursive_proof = !nested_aggregation_indices_all_zero; + std::shared_ptr vkey = acir_format::verification_key_ct::from_field_pt_vector( + &composer, key_fields, inner_proof_contains_recursive_proof, nested_aggregation_object); vkey->program_width = acir_format::noir_recursive_settings::program_width; - acir_format::Transcript_ct transcript(&composer, manifest, proof_fields, 1); + acir_format::Transcript_ct transcript(&composer, manifest, proof_fields, num_public_inputs); acir_format::aggregation_state_ct result = proof_system::plonk::stdlib::recursion::verify_proof_( &composer, vkey, transcript, previous_aggregation); - // just writing the output aggregation G1 elements, and no public inputs, proof witnesses, or any other data + // Just write the output aggregation G1 elements, and no public inputs, proof witnesses, or any other data + // as this should all be available elsewhere const size_t output_size_bytes = 16 * sizeof(barretenberg::fr); auto raw_buf = (uint8_t*)malloc(output_size_bytes); diff --git a/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.hpp b/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.hpp index e37756a5d6..cad0d1a79f 100644 --- a/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.hpp +++ b/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.hpp @@ -14,7 +14,8 @@ size_t serialize_verification_key_into_field_elements(uint8_t const* g2x, uint8_t** serialized_vk_hash_buf); size_t serialize_proof_into_field_elements(uint8_t const* proof_data_buf, uint8_t** serialized_proof_data_buf, - size_t proof_data_length); + size_t proof_data_length, + size_t num_inner_public_inputs); size_t new_proof(void* pippenger, uint8_t const* g2x, uint8_t const* pk_buf, @@ -32,7 +33,7 @@ size_t verify_recursive_proof(uint8_t const* proof_buf, uint32_t proof_length, uint8_t const* vk_buf, uint32_t vk_length, - uint8_t const* public_inputs_buf, + uint32_t num_public_inputs, uint8_t const* input_aggregation_obj_buf, uint8_t** output_aggregation_obj_buf); diff --git a/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.test.cpp b/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.test.cpp index 08e4e8fbea..a5554d1b82 100644 --- a/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.test.cpp +++ b/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.test.cpp @@ -76,7 +76,7 @@ void create_inner_circuit(acir_format::acir_format& constraint_system, std::vect constraint_system = acir_format::acir_format{ .varnum = 7, - .public_inputs = { 2 }, + .public_inputs = { 2, 3 }, .fixed_base_scalar_mul_constraints = {}, .logic_constraints = { logic_constraint }, .range_constraints = { range_a, range_b }, @@ -141,7 +141,13 @@ TEST(AcirProofs, TestSerialization) EXPECT_EQ(verified, true); } -TEST(AcirProofs, TestSerializationWithRecursion) +struct RecursiveCircuitData { + std::vector key_witnesses; + std::vector proof_witnesses; + size_t num_public_inputs; +}; + +RecursiveCircuitData fetch_recursive_circuit_data(acir_format::acir_format constraint_system, std::vector witness) { uint8_t* proof_data_fields = nullptr; uint8_t* vk_fields = nullptr; @@ -149,112 +155,148 @@ TEST(AcirProofs, TestSerializationWithRecursion) size_t proof_fields_size = 0; size_t vk_fields_size = 0; - // inner circuit - { - acir_format::acir_format constraint_system; - std::vector witness; - create_inner_circuit(constraint_system, witness); - - std::vector witness_buf; // = to_buffer(witness); - std::vector constraint_system_buf; // - write(constraint_system_buf, constraint_system); - write(witness_buf, witness); - - uint8_t const* pk_buf = nullptr; - acir_proofs::init_proving_key(&constraint_system_buf[0], &pk_buf); - - uint32_t total_circuit_size = acir_proofs::get_total_circuit_size(&constraint_system_buf[0]); - uint32_t pow2_size = 1 << (numeric::get_msb(total_circuit_size) + 1); - auto env_crs = std::make_unique(); - auto verifier_crs = env_crs->get_verifier_crs(); - barretenberg::g2::affine_element g2x = verifier_crs->get_g2x(); - - auto* pippenger_ptr_base = - new scalar_multiplication::Pippenger(env_load_prover_crs(pow2_size + 1), pow2_size + 1); - void* pippenger_ptr = reinterpret_cast(pippenger_ptr_base); - - std::vector g2x_buffer(128); - io::write_g2_elements_to_buffer(&g2x, (char*)(&g2x_buffer[0]), 1); - - uint8_t const* vk_buf = nullptr; - acir_proofs::init_verification_key(pippenger_ptr, &g2x_buffer[0], pk_buf, &vk_buf); - - uint8_t* proof_data_buf = nullptr; - size_t proof_length = acir_proofs::new_proof( - pippenger_ptr, &g2x_buffer[0], pk_buf, &constraint_system_buf[0], &witness_buf[0], &proof_data_buf, true); - proof_fields_size = - acir_proofs::serialize_proof_into_field_elements(proof_data_buf, &proof_data_fields, proof_length); - vk_fields_size = acir_proofs::serialize_verification_key_into_field_elements( - &g2x_buffer[0], vk_buf, &vk_fields, &vk_hash_buf); - - bool verified = acir_proofs::verify_proof(&g2x_buffer[0], - vk_buf, - &constraint_system_buf[0], - proof_data_buf, - static_cast(proof_length), - true); - EXPECT_EQ(verified, true); - - delete pippenger_ptr_base; - free((void*)vk_buf); - free((void*)pk_buf); - free((void*)proof_data_buf); + std::vector witness_buf; // = to_buffer(witness); + std::vector constraint_system_buf; // + write(constraint_system_buf, constraint_system); + write(witness_buf, witness); + + uint8_t const* pk_buf = nullptr; + acir_proofs::init_proving_key(&constraint_system_buf[0], &pk_buf); + + uint32_t total_circuit_size = acir_proofs::get_total_circuit_size(&constraint_system_buf[0]); + uint32_t pow2_size = 1 << (numeric::get_msb(total_circuit_size) + 1); + auto env_crs = std::make_unique(); + auto verifier_crs = env_crs->get_verifier_crs(); + barretenberg::g2::affine_element g2x = verifier_crs->get_g2x(); + + auto* pippenger_ptr_base = new scalar_multiplication::Pippenger(env_load_prover_crs(pow2_size + 1), pow2_size + 1); + void* pippenger_ptr = reinterpret_cast(pippenger_ptr_base); + + std::vector g2x_buffer(128); + io::write_g2_elements_to_buffer(&g2x, (char*)(&g2x_buffer[0]), 1); + + uint8_t const* vk_buf = nullptr; + acir_proofs::init_verification_key(pippenger_ptr, &g2x_buffer[0], pk_buf, &vk_buf); + + uint8_t* proof_data_buf = nullptr; + size_t proof_length = acir_proofs::new_proof( + pippenger_ptr, &g2x_buffer[0], pk_buf, &constraint_system_buf[0], &witness_buf[0], &proof_data_buf, true); + + auto num_public_inputs = constraint_system.public_inputs.size(); + proof_fields_size = acir_proofs::serialize_proof_into_field_elements( + proof_data_buf, &proof_data_fields, proof_length, num_public_inputs); + vk_fields_size = + acir_proofs::serialize_verification_key_into_field_elements(&g2x_buffer[0], vk_buf, &vk_fields, &vk_hash_buf); + + bool verified = acir_proofs::verify_proof( + &g2x_buffer[0], vk_buf, &constraint_system_buf[0], proof_data_buf, static_cast(proof_length), true); + EXPECT_EQ(verified, true); + + delete pippenger_ptr_base; + free((void*)vk_buf); + free((void*)pk_buf); + free((void*)proof_data_buf); + + fr vk_hash_value; + std::vector proof_witnesses(proof_fields_size / 32); + std::vector key_witnesses((vk_fields_size / 32) + 1); + for (size_t i = 0; i < proof_fields_size / 32; i++) { + proof_witnesses[i] = barretenberg::fr::serialize_from_buffer(&proof_data_fields[i * 32]); + } + for (size_t i = 0; i < vk_fields_size / 32; i++) { + key_witnesses[i] = barretenberg::fr::serialize_from_buffer(&vk_fields[i * 32]); } - // outer circuit - { - fr vk_hash_value; - std::vector proof_witnesses(proof_fields_size / 32); - std::vector key_witnesses(vk_fields_size / 32); - for (size_t i = 0; i < proof_fields_size / 32; i++) { - proof_witnesses[i] = barretenberg::fr::serialize_from_buffer(&proof_data_fields[i * 32]); - } - for (size_t i = 0; i < vk_fields_size / 32; i++) { - key_witnesses[i] = barretenberg::fr::serialize_from_buffer(&vk_fields[i * 32]); - } - vk_hash_value = barretenberg::fr::serialize_from_buffer(vk_hash_buf); - std::vector proof_indices; + free((void*)proof_data_fields); + free((void*)vk_fields); - const size_t proof_size = proof_witnesses.size(); - for (size_t i = 0; i < proof_size; ++i) { - proof_indices.emplace_back(static_cast(i + 19)); - } + vk_hash_value = barretenberg::fr::serialize_from_buffer(vk_hash_buf); + key_witnesses[vk_fields_size / 32] = vk_hash_value; + auto inner_circuit = RecursiveCircuitData{ key_witnesses, proof_witnesses, num_public_inputs }; + return inner_circuit; +} + +RecursiveCircuitData fetch_inner_circuit_data() +{ + acir_format::acir_format constraint_system; + std::vector witness; + create_inner_circuit(constraint_system, witness); + + return fetch_recursive_circuit_data(constraint_system, witness); +} + +/** + * @brief Create a circuit that recursively verifies one or more inner circuits + * + * @param inner_composers + * @return acir_format::Composer + */ +std::pair> create_outer_circuit( + std::vector& inner_circuits) +{ + std::vector recursion_constraints; + + // witness count starts at 1 (Composer reserves 1st witness to be the zero-valued zero_idx) + size_t witness_offset = 1; + std::array output_aggregation_object; + std::vector witness; + + for (size_t i = 0; i < inner_circuits.size(); ++i) { + const bool has_input_aggregation_object = i > 0; + + auto& inner_circuit = inner_circuits[i]; + const std::vector proof_witnesses = inner_circuit.proof_witnesses; + const std::vector key_witnesses = inner_circuit.key_witnesses; + + const bool has_nested_proof = uint32_t(key_witnesses[5]); + const size_t num_inner_public_inputs = inner_circuit.num_public_inputs; + + const uint32_t key_hash_start_idx = static_cast(witness_offset); + const uint32_t public_input_start_idx = key_hash_start_idx + 1; + const uint32_t output_aggregation_object_start_idx = + static_cast(public_input_start_idx + num_inner_public_inputs + (has_nested_proof ? 16 : 0)); + const uint32_t proof_indices_start_idx = output_aggregation_object_start_idx + 16; + const uint32_t key_indices_start_idx = static_cast(proof_indices_start_idx + proof_witnesses.size()); + + std::vector proof_indices; std::vector key_indices; + std::vector inner_public_inputs; + std::array input_aggregation_object = {}; + std::array nested_aggregation_object = {}; + if (has_input_aggregation_object) { + input_aggregation_object = output_aggregation_object; + } + for (size_t i = 0; i < 16; ++i) { + output_aggregation_object[i] = (static_cast(i + output_aggregation_object_start_idx)); + } + if (has_nested_proof) { + for (size_t i = 6; i < 22; ++i) { + nested_aggregation_object[i - 6] = uint32_t(key_witnesses[i]); + } + } + for (size_t i = 0; i < proof_witnesses.size(); ++i) { + proof_indices.emplace_back(static_cast(i + proof_indices_start_idx)); + } const size_t key_size = key_witnesses.size(); for (size_t i = 0; i < key_size; ++i) { - key_indices.emplace_back(static_cast(i + 19 + proof_size)); + key_indices.emplace_back(static_cast(i + key_indices_start_idx)); } - - std::array output_vars; - for (size_t i = 0; i < acir_format::RecursionConstraint::AGGREGATION_OBJECT_SIZE; ++i) { - // variable idx 1 = public input - // variable idx 2-18 = output_vars - output_vars[i] = (static_cast(i + 3)); + for (size_t i = 0; i < num_inner_public_inputs; ++i) { + inner_public_inputs.push_back(static_cast(i + public_input_start_idx)); } + acir_format::RecursionConstraint recursion_constraint{ .key = key_indices, .proof = proof_indices, - .public_input = 1, - .key_hash = 2, - .input_aggregation_object = {}, - .output_aggregation_object = output_vars, + .public_inputs = inner_public_inputs, + .key_hash = key_hash_start_idx, + .input_aggregation_object = input_aggregation_object, + .output_aggregation_object = output_aggregation_object, + .nested_aggregation_object = nested_aggregation_object, }; - - // Add a constraint that fixes the vk hash to be the expected value! - poly_triple vk_equality_constraint{ - .a = recursion_constraint.key_hash, - .b = 0, - .c = 0, - .q_m = 0, - .q_l = 1, - .q_r = 0, - .q_o = 0, - .q_c = -vk_hash_value, - }; - - std::vector witness; - for (size_t i = 0; i < 18; ++i) { + recursion_constraints.push_back(recursion_constraint); + for (size_t i = 0; i < proof_indices_start_idx - witness_offset; ++i) { witness.emplace_back(0); } for (const auto& wit : proof_witnesses) { @@ -263,66 +305,129 @@ TEST(AcirProofs, TestSerializationWithRecursion) for (const auto& wit : key_witnesses) { witness.emplace_back(wit); } + witness_offset = key_indices_start_idx + key_witnesses.size(); + } - acir_format::acir_format constraint_system{ - .varnum = static_cast(witness.size() + 1), - .public_inputs = { 1 }, - .fixed_base_scalar_mul_constraints = {}, - .logic_constraints = {}, - .range_constraints = {}, - .schnorr_constraints = {}, - .ecdsa_constraints = {}, - .sha256_constraints = {}, - .blake2s_constraints = {}, - .keccak_constraints = {}, - .hash_to_field_constraints = {}, - .pedersen_constraints = {}, - .compute_merkle_root_constraints = {}, - .block_constraints = {}, - .recursion_constraints = { recursion_constraint }, - .constraints = { vk_equality_constraint }, - }; + std::vector public_inputs(output_aggregation_object.begin(), output_aggregation_object.end()); - std::vector witness_buf; - std::vector constraint_system_buf; - write(constraint_system_buf, constraint_system); - write(witness_buf, witness); - uint8_t const* pk_buf = nullptr; - acir_proofs::init_proving_key(&constraint_system_buf[0], &pk_buf); - - uint32_t total_circuit_size = acir_proofs::get_total_circuit_size(&constraint_system_buf[0]); - uint32_t pow2_size = 1 << (numeric::get_msb(total_circuit_size) + 1); - auto env_crs = std::make_unique(); - auto verifier_crs = env_crs->get_verifier_crs(); - barretenberg::g2::affine_element g2x = verifier_crs->get_g2x(); - - auto* pippenger_ptr_base = - new scalar_multiplication::Pippenger(env_load_prover_crs(pow2_size + 1), pow2_size + 1); - void* pippenger_ptr = reinterpret_cast(pippenger_ptr_base); - - std::vector g2x_buffer(128); - io::write_g2_elements_to_buffer(&g2x, (char*)(&g2x_buffer[0]), 1); - - uint8_t const* vk_buf = nullptr; - acir_proofs::init_verification_key(pippenger_ptr, &g2x_buffer[0], pk_buf, &vk_buf); - - uint8_t* proof_data_buf = nullptr; - size_t proof_length = acir_proofs::new_proof( - pippenger_ptr, &g2x_buffer[0], pk_buf, &constraint_system_buf[0], &witness_buf[0], &proof_data_buf, false); - - bool verified = acir_proofs::verify_proof(&g2x_buffer[0], - vk_buf, - &constraint_system_buf[0], - proof_data_buf, - static_cast(proof_length), - false); - EXPECT_EQ(verified, true); - - delete pippenger_ptr_base; - free((void*)vk_buf); - free((void*)pk_buf); - free((void*)proof_data_buf); - free((void*)proof_data_fields); - free((void*)vk_fields); - } + acir_format::acir_format constraint_system{ + .varnum = static_cast(witness.size() + 1), + .public_inputs = public_inputs, + .fixed_base_scalar_mul_constraints = {}, + .logic_constraints = {}, + .range_constraints = {}, + .schnorr_constraints = {}, + .ecdsa_constraints = {}, + .sha256_constraints = {}, + .blake2s_constraints = {}, + .keccak_constraints = {}, + .hash_to_field_constraints = {}, + .pedersen_constraints = {}, + .compute_merkle_root_constraints = {}, + .block_constraints = {}, + .recursion_constraints = recursion_constraints, + .constraints = {}, + }; + + return std::make_pair(constraint_system, witness); +} + +// Working double recursion test +TEST(AcirProofs, TestSerializationWithBasicDoubleRecursion) +{ + std::vector inner_circuits; + auto a = fetch_inner_circuit_data(); + inner_circuits.push_back(a); + + auto b = fetch_inner_circuit_data(); + inner_circuits.push_back(b); + + auto c = create_outer_circuit(inner_circuits); + + std::vector witness_buf; + std::vector constraint_system_buf; + write(constraint_system_buf, c.first); + write(witness_buf, c.second); + uint8_t const* pk_buf = nullptr; + acir_proofs::init_proving_key(&constraint_system_buf[0], &pk_buf); + + uint32_t total_circuit_size = acir_proofs::get_total_circuit_size(&constraint_system_buf[0]); + uint32_t pow2_size = 1 << (numeric::get_msb(total_circuit_size) + 1); + auto env_crs = std::make_unique(); + auto verifier_crs = env_crs->get_verifier_crs(); + barretenberg::g2::affine_element g2x = verifier_crs->get_g2x(); + + auto* pippenger_ptr_base = new scalar_multiplication::Pippenger(env_load_prover_crs(pow2_size + 1), pow2_size + 1); + void* pippenger_ptr = reinterpret_cast(pippenger_ptr_base); + + std::vector g2x_buffer(128); + io::write_g2_elements_to_buffer(&g2x, (char*)(&g2x_buffer[0]), 1); + + uint8_t const* vk_buf = nullptr; + acir_proofs::init_verification_key(pippenger_ptr, &g2x_buffer[0], pk_buf, &vk_buf); + + uint8_t* proof_data_buf = nullptr; + size_t proof_length = acir_proofs::new_proof( + pippenger_ptr, &g2x_buffer[0], pk_buf, &constraint_system_buf[0], &witness_buf[0], &proof_data_buf, false); + + bool verified = acir_proofs::verify_proof( + &g2x_buffer[0], vk_buf, &constraint_system_buf[0], proof_data_buf, static_cast(proof_length), false); + EXPECT_EQ(verified, true); + + delete pippenger_ptr_base; + free((void*)vk_buf); + free((void*)pk_buf); + free((void*)proof_data_buf); +} + +TEST(AcirProofs, TestSerializationWithFullRecursiveComposition) +{ + std::vector layer_1_circuits; + auto a_data_circuit = fetch_inner_circuit_data(); + layer_1_circuits.push_back(a_data_circuit); + + std::vector layer_2_circuits; + auto a2_circuit = fetch_inner_circuit_data(); + layer_2_circuits.push_back(a2_circuit); + + auto b_cs_and_witness = create_outer_circuit(layer_1_circuits); + auto b_circuit = fetch_recursive_circuit_data(b_cs_and_witness.first, b_cs_and_witness.second); + layer_2_circuits.push_back(b_circuit); + + auto c_cs_and_witness = create_outer_circuit(layer_2_circuits); + + std::vector witness_buf; + std::vector constraint_system_buf; + write(constraint_system_buf, c_cs_and_witness.first); + write(witness_buf, c_cs_and_witness.second); + uint8_t const* pk_buf = nullptr; + acir_proofs::init_proving_key(&constraint_system_buf[0], &pk_buf); + + uint32_t total_circuit_size = acir_proofs::get_total_circuit_size(&constraint_system_buf[0]); + uint32_t pow2_size = 1 << (numeric::get_msb(total_circuit_size) + 1); + auto env_crs = std::make_unique(); + auto verifier_crs = env_crs->get_verifier_crs(); + barretenberg::g2::affine_element g2x = verifier_crs->get_g2x(); + + auto* pippenger_ptr_base = new scalar_multiplication::Pippenger(env_load_prover_crs(pow2_size + 1), pow2_size + 1); + void* pippenger_ptr = reinterpret_cast(pippenger_ptr_base); + + std::vector g2x_buffer(128); + io::write_g2_elements_to_buffer(&g2x, (char*)(&g2x_buffer[0]), 1); + + uint8_t const* vk_buf = nullptr; + acir_proofs::init_verification_key(pippenger_ptr, &g2x_buffer[0], pk_buf, &vk_buf); + + uint8_t* proof_data_buf = nullptr; + size_t proof_length = acir_proofs::new_proof( + pippenger_ptr, &g2x_buffer[0], pk_buf, &constraint_system_buf[0], &witness_buf[0], &proof_data_buf, false); + + bool verified = acir_proofs::verify_proof( + &g2x_buffer[0], vk_buf, &constraint_system_buf[0], proof_data_buf, static_cast(proof_length), false); + EXPECT_EQ(verified, true); + + delete pippenger_ptr_base; + free((void*)vk_buf); + free((void*)pk_buf); + free((void*)proof_data_buf); } diff --git a/cpp/src/barretenberg/dsl/acir_proofs/c_bind.cpp b/cpp/src/barretenberg/dsl/acir_proofs/c_bind.cpp index 5ff7b29d96..62621d00be 100644 --- a/cpp/src/barretenberg/dsl/acir_proofs/c_bind.cpp +++ b/cpp/src/barretenberg/dsl/acir_proofs/c_bind.cpp @@ -45,10 +45,11 @@ WASM_EXPORT size_t acir_serialize_verification_key_into_field_elements(uint8_t c } WASM_EXPORT size_t acir_serialize_proof_into_field_elements(uint8_t const* proof_data_buf, uint8_t** serialized_proof_data_buf, - size_t proof_data_length) + size_t proof_data_length, + size_t num_inner_public_inputs) { return acir_proofs::serialize_proof_into_field_elements( - proof_data_buf, serialized_proof_data_buf, proof_data_length); + proof_data_buf, serialized_proof_data_buf, proof_data_length, num_inner_public_inputs); } WASM_EXPORT size_t acir_proofs_new_proof(void* pippenger, @@ -76,7 +77,7 @@ WASM_EXPORT size_t acir_proofs_verify_recursive_proof(uint8_t const* proof_buf, uint32_t proof_length, uint8_t const* vk_buf, uint32_t vk_length, - uint8_t const* public_inputs_buf, + uint32_t num_public_inputs, uint8_t const* input_aggregation_obj_buf, uint8_t** output_aggregation_obj_buf) { @@ -84,7 +85,7 @@ WASM_EXPORT size_t acir_proofs_verify_recursive_proof(uint8_t const* proof_buf, proof_length, vk_buf, vk_length, - public_inputs_buf, + num_public_inputs, input_aggregation_obj_buf, output_aggregation_obj_buf); } diff --git a/cpp/src/barretenberg/dsl/acir_proofs/c_bind.hpp b/cpp/src/barretenberg/dsl/acir_proofs/c_bind.hpp index 0db08d5573..452f41d1ca 100644 --- a/cpp/src/barretenberg/dsl/acir_proofs/c_bind.hpp +++ b/cpp/src/barretenberg/dsl/acir_proofs/c_bind.hpp @@ -20,7 +20,8 @@ WASM_EXPORT size_t acir_serialize_verification_key_into_field_elements(uint8_t c uint8_t** serialized_vk_hash_buf); WASM_EXPORT size_t acir_serialize_proof_into_field_elements(uint8_t const* proof_data_buf, uint8_t** serialized_proof_data_buf, - size_t proof_data_length); + size_t proof_data_length, + size_t num_inner_public_inputs); WASM_EXPORT size_t acir_proofs_new_proof(void* pippenger, uint8_t const* g2x, uint8_t const* pk_buf, @@ -38,7 +39,7 @@ WASM_EXPORT size_t acir_proofs_verify_recursive_proof(uint8_t const* proof_buf, uint32_t proof_length, uint8_t const* vk_buf, uint32_t vk_length, - uint8_t const* public_inputs_buf, + uint32_t num_public_inputs, uint8_t const* input_aggregation_obj_buf, uint8_t** output_aggregation_obj_buf); } diff --git a/cpp/src/barretenberg/honk/proof_system/ultra_prover.hpp b/cpp/src/barretenberg/honk/proof_system/ultra_prover.hpp index 9bbe7314f2..bcf665711c 100644 --- a/cpp/src/barretenberg/honk/proof_system/ultra_prover.hpp +++ b/cpp/src/barretenberg/honk/proof_system/ultra_prover.hpp @@ -13,8 +13,7 @@ namespace proof_system::honk { // We won't compile this class with honk::flavor::Standard, but we will like want to compile it (at least for testing) // with a flavor that uses the curve Grumpkin, or a flavor that does/does not have zk, etc. -template -concept UltraFlavor = IsAnyOf; +template concept UltraFlavor = IsAnyOf; template class UltraProver_ { using FF = typename Flavor::FF; diff --git a/cpp/src/barretenberg/plonk/composer/composer_base.hpp b/cpp/src/barretenberg/plonk/composer/composer_base.hpp index a3e57bc383..b1109d2d4d 100644 --- a/cpp/src/barretenberg/plonk/composer/composer_base.hpp +++ b/cpp/src/barretenberg/plonk/composer/composer_base.hpp @@ -186,6 +186,21 @@ class ComposerBase { barretenberg::fr get_public_input(const uint32_t index) const { return get_variable(public_inputs[index]); } + uint32_t get_public_input_index(const uint32_t witness_index) const + { + bool found = false; + uint32_t result = static_cast(-1); + for (size_t i = 0; i < public_inputs.size(); ++i) { + if (real_variable_index[public_inputs[i]] == real_variable_index[witness_index]) { + found = true; + result = static_cast(i); + break; + } + } + ASSERT(found == true); + return result; + } + std::vector get_public_inputs() const { std::vector result; diff --git a/cpp/src/barretenberg/plonk/composer/ultra_composer.hpp b/cpp/src/barretenberg/plonk/composer/ultra_composer.hpp index 6f22b40bb0..6a73f5f2bc 100644 --- a/cpp/src/barretenberg/plonk/composer/ultra_composer.hpp +++ b/cpp/src/barretenberg/plonk/composer/ultra_composer.hpp @@ -261,6 +261,23 @@ class UltraComposer : public ComposerBase { } } + /** + * @brief Update recursive_proof_public_input_indices with existing public inputs that represent a recursive proof + * + * @param proof_output_witness_indices + */ + void set_recursive_proof(const std::vector& proof_output_witness_indices) + { + if (contains_recursive_proof) { + failure("added recursive proof when one already exists"); + } + contains_recursive_proof = true; + for (size_t i = 0; i < proof_output_witness_indices.size(); ++i) { + recursive_proof_public_input_indices.push_back( + get_public_input_index(real_variable_index[proof_output_witness_indices[i]])); + } + } + void create_new_range_constraint(const uint32_t variable_index, const uint64_t target_range, std::string const msg = "create_new_range_constraint"); diff --git a/cpp/src/barretenberg/plonk/proof_system/verification_key/verification_key.cpp b/cpp/src/barretenberg/plonk/proof_system/verification_key/verification_key.cpp index 743f563770..f99fb5aed7 100644 --- a/cpp/src/barretenberg/plonk/proof_system/verification_key/verification_key.cpp +++ b/cpp/src/barretenberg/plonk/proof_system/verification_key/verification_key.cpp @@ -258,7 +258,14 @@ std::vector verification_key::export_dummy_key_in_recursion_fo for (const auto& descriptor : polynomial_manifest.get()) { if (descriptor.source == PolynomialSource::SELECTOR || descriptor.source == PolynomialSource::PERMUTATION) { - const auto element = barretenberg::g1::affine_one; + // the std::biggroup class creates unsatisfiable constraints when identical points are added/subtracted. + // (when verifying zk proofs this is acceptable as we make sure verification key points are not identical. + // And prover points should contain randomness for an honest Prover). + // This check can also trigger a runtime error due to causing 0 to be inverted. + // When creating dummy verification key points we must be mindful of the above and make sure that each + // transcript point is unique. + auto scalar = barretenberg::fr::random_element(); + const auto element = barretenberg::g1::affine_element(barretenberg::g1::one * scalar); const uint256_t x = element.x; const uint256_t y = element.y; const barretenberg::fr x_lo = x.slice(0, 136); @@ -271,6 +278,7 @@ std::vector verification_key::export_dummy_key_in_recursion_fo output.emplace_back(y_hi); } } + output.emplace_back(0); // key_hash return output; diff --git a/cpp/src/barretenberg/stdlib/recursion/verification_key/verification_key.hpp b/cpp/src/barretenberg/stdlib/recursion/verification_key/verification_key.hpp index 2cd486bbe1..818f1f9d9a 100644 --- a/cpp/src/barretenberg/stdlib/recursion/verification_key/verification_key.hpp +++ b/cpp/src/barretenberg/stdlib/recursion/verification_key/verification_key.hpp @@ -120,10 +120,10 @@ template struct evaluation_domain { template struct verification_key { using Composer = typename Curve::Composer; - template static std::shared_ptr from_field_pt_vector( Composer* ctx, const std::vector>& fields, + bool inner_proof_contains_recursive_proof = false, std::array recursive_proof_public_input_indices = {}) { std::vector fields_raw; @@ -138,15 +138,10 @@ template struct verification_key { // NOTE: For now `contains_recursive_proof` and `recursive_proof_public_input_indices` need to be circuit // constants! - key->contains_recursive_proof = static_cast(uint256_t(fields[5].get_value())); + key->contains_recursive_proof = inner_proof_contains_recursive_proof; for (size_t i = 0; i < 16; ++i) { - const uint32_t idx = static_cast(uint256_t(fields[6 + i].get_value())); - key->recursive_proof_public_input_indices.emplace_back(idx); - } - // Apply constraints to force the recursive proof information to be circuit constants - fields[5].assert_equal(inner_proof_contains_recursive_proof); - for (size_t i = 0; i < 16; ++i) { - fields[6 + i].assert_equal(recursive_proof_public_input_indices[i]); + auto x = recursive_proof_public_input_indices[i]; + key->recursive_proof_public_input_indices.emplace_back(x); } size_t count = 22; diff --git a/cpp/src/barretenberg/transcript/transcript_wrappers.hpp b/cpp/src/barretenberg/transcript/transcript_wrappers.hpp index ad849889cc..2fe0830c37 100644 --- a/cpp/src/barretenberg/transcript/transcript_wrappers.hpp +++ b/cpp/src/barretenberg/transcript/transcript_wrappers.hpp @@ -104,7 +104,8 @@ class StandardTranscript : public Transcript { * @param manifest * @return std::vector */ - static std::vector export_dummy_transcript_in_recursion_format(const Manifest& manifest) + static std::vector export_dummy_transcript_in_recursion_format( + const Manifest& manifest, const bool contains_recursive_proof) { std::vector fields; const auto num_rounds = manifest.get_num_rounds(); @@ -114,7 +115,14 @@ class StandardTranscript : public Transcript { if (manifest_element.num_bytes == 32 && manifest_element.name != "public_inputs") { fields.emplace_back(0); } else if (manifest_element.num_bytes == 64 && manifest_element.name != "public_inputs") { - const auto group_element = barretenberg::g1::affine_one; + // the std::biggroup class creates unsatisfiable constraints when identical points are + // added/subtracted. + // (when verifying zk proofs this is acceptable as we make sure verification key points are not + // identical. And prover points should contain randomness for an honest Prover). This check can + // also trigger a runtime error due to causing 0 to be inverted. When creating dummy proof + // points we must be mindful of the above and make sure that each point is unique. + auto scalar = barretenberg::fr::random_element(); + const auto group_element = barretenberg::g1::affine_element(barretenberg::g1::one * scalar); const uint256_t x = group_element.x; const uint256_t y = group_element.y; const barretenberg::fr x_lo = x.slice(0, 136); @@ -128,8 +136,30 @@ class StandardTranscript : public Transcript { } else { ASSERT(manifest_element.name == "public_inputs"); const size_t num_public_inputs = manifest_element.num_bytes / 32; - for (size_t j = 0; j < num_public_inputs; ++j) { - fields.emplace_back(0); + // If we have a recursive proofs the public inputs must describe an aggregation object that + // is composed of two valid G1 points on the curve. Without this conditional we will get a + // runtime error that we are attempting to invert 0. + if (contains_recursive_proof) { + ASSERT(num_public_inputs == 16); + for (size_t k = 0; k < num_public_inputs / 4; ++k) { + auto scalar = barretenberg::fr::random_element(); + const auto group_element = + barretenberg::g1::affine_element(barretenberg::g1::one * scalar); + const uint256_t x = group_element.x; + const uint256_t y = group_element.y; + const barretenberg::fr x_lo = x.slice(0, 136); + const barretenberg::fr x_hi = x.slice(136, 272); + const barretenberg::fr y_lo = y.slice(0, 136); + const barretenberg::fr y_hi = y.slice(136, 272); + fields.emplace_back(x_lo); + fields.emplace_back(x_hi); + fields.emplace_back(y_lo); + fields.emplace_back(y_hi); + } + } else { + for (size_t j = 0; j < num_public_inputs; ++j) { + fields.emplace_back(0); + } } } } From a2f6cbbd5bc78d69d36b4a5dd1458572b1946edd Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Mon, 22 May 2023 10:33:10 -0400 Subject: [PATCH 27/41] move export key into dsl package --- .../dsl/acir_format/recursion_constraint.cpp | 110 +++++++++++++++++- .../dsl/acir_format/recursion_constraint.hpp | 6 + .../acir_format/recursion_constraint.test.cpp | 3 +- .../dsl/acir_proofs/acir_proofs.cpp | 2 +- 4 files changed, 117 insertions(+), 4 deletions(-) diff --git a/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.cpp b/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.cpp index 1fcf95f594..d47e493427 100644 --- a/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.cpp +++ b/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.cpp @@ -36,8 +36,8 @@ void create_recursion_constraints(Composer& composer, const RecursionConstraint& // on-curve errors and inverting-zero errors { // get a fake key/proof that satisfies on-curve + inversion-zero checks - const std::vector dummy_key = plonk::verification_key::export_dummy_key_in_recursion_format( - PolynomialManifest(Composer::type), inner_proof_contains_recursive_proof); + const std::vector dummy_key = export_dummy_key_in_recursion_format(PolynomialManifest(Composer::type), + inner_proof_contains_recursive_proof); const auto manifest = Composer::create_unrolled_manifest(input.public_inputs.size()); const std::vector dummy_proof = transcript::StandardTranscript::export_dummy_transcript_in_recursion_format( @@ -138,4 +138,110 @@ void create_recursion_constraints(Composer& composer, const RecursionConstraint& template void create_recursion_constraints(Composer&, const RecursionConstraint&); template void create_recursion_constraints(Composer&, const RecursionConstraint&); +/** + * @brief When recursively verifying proofs, we represent the verification key using field elements. + * This method exports the key formatted in the manner our recursive verifier expects. + * NOTE: only used by the dsl at the moment. Might be cleaner to make this a dsl function? + * + * @return std::vector + */ +std::vector export_key_in_recursion_format(std::shared_ptr const& vkey) +{ + std::vector output; + output.emplace_back(vkey->domain.root); + output.emplace_back(vkey->domain.domain); + output.emplace_back(vkey->domain.generator); + output.emplace_back(vkey->circuit_size); + output.emplace_back(vkey->num_public_inputs); + output.emplace_back(vkey->contains_recursive_proof); + for (size_t i = 0; i < 16; ++i) { + if (vkey->recursive_proof_public_input_indices.size() > i) { + output.emplace_back(vkey->recursive_proof_public_input_indices[i]); + } else { + output.emplace_back(0); + ASSERT(vkey->contains_recursive_proof == false); + } + } + for (const auto& descriptor : vkey->polynomial_manifest.get()) { + if (descriptor.source == PolynomialSource::SELECTOR || descriptor.source == PolynomialSource::PERMUTATION) { + const auto element = vkey->commitments.at(std::string(descriptor.commitment_label)); + const uint256_t x = element.x; + const uint256_t y = element.y; + const barretenberg::fr x_lo = x.slice(0, 136); + const barretenberg::fr x_hi = x.slice(136, 272); + const barretenberg::fr y_lo = y.slice(0, 136); + const barretenberg::fr y_hi = y.slice(136, 272); + output.emplace_back(x_lo); + output.emplace_back(x_hi); + output.emplace_back(y_lo); + output.emplace_back(y_hi); + } + } + + verification_key_data vkey_data{ + .composer_type = vkey->composer_type, + .circuit_size = static_cast(vkey->circuit_size), + .num_public_inputs = static_cast(vkey->num_public_inputs), + .commitments = vkey->commitments, + .contains_recursive_proof = vkey->contains_recursive_proof, + .recursive_proof_public_input_indices = vkey->recursive_proof_public_input_indices, + }; + output.emplace_back(vkey_data.compress_native(0)); // key_hash + return output; +} + +/** + * @brief When recursively verifying proofs, we represent the verification key using field elements. + * This method exports the key formatted in the manner our recursive verifier expects. + * A dummy key is used when building a circuit without a valid witness assignment. + * We want the transcript to contain valid G1 points to prevent on-curve errors being thrown. + * We want a non-zero circuit size as this element will be inverted by the circuit + * and we do not want an "inverting 0" error thrown + * + * @return std::vector + */ +std::vector export_dummy_key_in_recursion_format(const PolynomialManifest& polynomial_manifest, + const bool contains_recursive_proof) +{ + std::vector output; + output.emplace_back(1); // domain.domain (will be inverted) + output.emplace_back(1); // domain.root (will be inverted) + output.emplace_back(1); // domain.generator (will be inverted) + + output.emplace_back(1); // circuit size + output.emplace_back(1); // num public inputs + + output.emplace_back(contains_recursive_proof); // contains_recursive_proof + for (size_t i = 0; i < 16; ++i) { + output.emplace_back(0); // recursive_proof_public_input_indices + } + + for (const auto& descriptor : polynomial_manifest.get()) { + if (descriptor.source == PolynomialSource::SELECTOR || descriptor.source == PolynomialSource::PERMUTATION) { + // the std::biggroup class creates unsatisfiable constraints when identical points are added/subtracted. + // (when verifying zk proofs this is acceptable as we make sure verification key points are not identical. + // And prover points should contain randomness for an honest Prover). + // This check can also trigger a runtime error due to causing 0 to be inverted. + // When creating dummy verification key points we must be mindful of the above and make sure that each + // transcript point is unique. + auto scalar = barretenberg::fr::random_element(); + const auto element = barretenberg::g1::affine_element(barretenberg::g1::one * scalar); + const uint256_t x = element.x; + const uint256_t y = element.y; + const barretenberg::fr x_lo = x.slice(0, 136); + const barretenberg::fr x_hi = x.slice(136, 272); + const barretenberg::fr y_lo = y.slice(0, 136); + const barretenberg::fr y_hi = y.slice(136, 272); + output.emplace_back(x_lo); + output.emplace_back(x_hi); + output.emplace_back(y_lo); + output.emplace_back(y_hi); + } + } + + output.emplace_back(0); // key_hash + + return output; +} + } // namespace acir_format diff --git a/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.hpp b/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.hpp index 0a0ca53d12..15af19039f 100644 --- a/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.hpp +++ b/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.hpp @@ -6,6 +6,8 @@ namespace acir_format { +using namespace proof_system::plonk; + /** * @brief RecursionConstraint struct contains information required to recursively verify a proof! * @@ -60,6 +62,10 @@ void create_recursion_constraints(Composer& composer, const RecursionConstraint& extern template void create_recursion_constraints(Composer&, const RecursionConstraint&); extern template void create_recursion_constraints(Composer&, const RecursionConstraint&); +std::vector export_key_in_recursion_format(std::shared_ptr const& vkey); +std::vector export_dummy_key_in_recursion_format(const PolynomialManifest& polynomial_manifest, + bool contains_recursive_proof = 0); + template inline void read(B& buf, RecursionConstraint& constraint) { using serialize::read; diff --git a/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.test.cpp b/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.test.cpp index c6b511d662..2f4436c2a3 100644 --- a/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.test.cpp +++ b/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.test.cpp @@ -141,7 +141,8 @@ acir_format::Composer create_outer_circuit(std::vector& i 16); const std::vector proof_witnesses = transcript.export_transcript_in_recursion_format(); - const std::vector key_witnesses = inner_verifier.key->export_key_in_recursion_format(); + const std::vector key_witnesses = + acir_format::export_key_in_recursion_format(inner_verifier.key); const uint32_t key_hash_start_idx = static_cast(witness_offset); const uint32_t public_input_start_idx = key_hash_start_idx + 1; diff --git a/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.cpp b/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.cpp index 4310342f7e..690aec5e7a 100644 --- a/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.cpp +++ b/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.cpp @@ -157,7 +157,7 @@ size_t serialize_verification_key_into_field_elements(uint8_t const* g2x, plonk::verification_key_data vk_data; read(vk_buf, vk_data); auto vkey = std::make_shared(std::move(vk_data), crs); - std::vector output = vkey->export_key_in_recursion_format(); + std::vector output = acir_format::export_key_in_recursion_format(vkey); // NOTE: this output buffer will always have a fixed size! Maybe just precompute? // Cut off 32 bytes as last element is the verification key hash which is not part of the key :o From b56d3ca9e3914abc959e82942f9c728fecc9335b Mon Sep 17 00:00:00 2001 From: vezenovm Date: Mon, 22 May 2023 14:34:24 +0000 Subject: [PATCH 28/41] chore: remove unused export key in recursion format from main proof system classes --- .../verification_key/verification_key.cpp | 106 ------------------ .../verification_key/verification_key.hpp | 3 - 2 files changed, 109 deletions(-) diff --git a/cpp/src/barretenberg/plonk/proof_system/verification_key/verification_key.cpp b/cpp/src/barretenberg/plonk/proof_system/verification_key/verification_key.cpp index f99fb5aed7..f6cf92bd73 100644 --- a/cpp/src/barretenberg/plonk/proof_system/verification_key/verification_key.cpp +++ b/cpp/src/barretenberg/plonk/proof_system/verification_key/verification_key.cpp @@ -178,110 +178,4 @@ sha256::hash verification_key::sha256_hash() return sha256::sha256(to_buffer(vk_data)); } -/** - * @brief When recursively verifying proofs, we represent the verification key using field elements. - * This method exports the key formatted in the manner our recursive verifier expects. - * NOTE: only used by the dsl at the moment. Might be cleaner to make this a dsl function? - * - * @return std::vector - */ -std::vector verification_key::export_key_in_recursion_format() -{ - std::vector output; - output.emplace_back(domain.root); - output.emplace_back(domain.domain); - output.emplace_back(domain.generator); - output.emplace_back(circuit_size); - output.emplace_back(num_public_inputs); - output.emplace_back(contains_recursive_proof); - for (size_t i = 0; i < 16; ++i) { - if (recursive_proof_public_input_indices.size() > i) { - output.emplace_back(recursive_proof_public_input_indices[i]); - } else { - output.emplace_back(0); - ASSERT(contains_recursive_proof == false); - } - } - for (const auto& descriptor : polynomial_manifest.get()) { - if (descriptor.source == PolynomialSource::SELECTOR || descriptor.source == PolynomialSource::PERMUTATION) { - const auto element = commitments.at(std::string(descriptor.commitment_label)); - const uint256_t x = element.x; - const uint256_t y = element.y; - const barretenberg::fr x_lo = x.slice(0, 136); - const barretenberg::fr x_hi = x.slice(136, 272); - const barretenberg::fr y_lo = y.slice(0, 136); - const barretenberg::fr y_hi = y.slice(136, 272); - output.emplace_back(x_lo); - output.emplace_back(x_hi); - output.emplace_back(y_lo); - output.emplace_back(y_hi); - } - } - - verification_key_data vkey_data{ - .composer_type = composer_type, - .circuit_size = static_cast(circuit_size), - .num_public_inputs = static_cast(num_public_inputs), - .commitments = commitments, - .contains_recursive_proof = contains_recursive_proof, - .recursive_proof_public_input_indices = recursive_proof_public_input_indices, - }; - output.emplace_back(vkey_data.compress_native(0)); // key_hash - return output; -} - -/** - * @brief When recursively verifying proofs, we represent the verification key using field elements. - * This method exports the key formatted in the manner our recursive verifier expects. - * A dummy key is used when building a circuit without a valid witness assignment. - * We want the transcript to contain valid G1 points to prevent on-curve errors being thrown. - * We want a non-zero circuit size as this element will be inverted by the circuit - * and we do not want an "inverting 0" error thrown - * - * @return std::vector - */ -std::vector verification_key::export_dummy_key_in_recursion_format( - const PolynomialManifest& polynomial_manifest, const bool contains_recursive_proof) -{ - std::vector output; - output.emplace_back(1); // domain.domain (will be inverted) - output.emplace_back(1); // domain.root (will be inverted) - output.emplace_back(1); // domain.generator (will be inverted) - - output.emplace_back(1); // circuit size - output.emplace_back(1); // num public inputs - - output.emplace_back(contains_recursive_proof); // contains_recursive_proof - for (size_t i = 0; i < 16; ++i) { - output.emplace_back(0); // recursive_proof_public_input_indices - } - - for (const auto& descriptor : polynomial_manifest.get()) { - if (descriptor.source == PolynomialSource::SELECTOR || descriptor.source == PolynomialSource::PERMUTATION) { - // the std::biggroup class creates unsatisfiable constraints when identical points are added/subtracted. - // (when verifying zk proofs this is acceptable as we make sure verification key points are not identical. - // And prover points should contain randomness for an honest Prover). - // This check can also trigger a runtime error due to causing 0 to be inverted. - // When creating dummy verification key points we must be mindful of the above and make sure that each - // transcript point is unique. - auto scalar = barretenberg::fr::random_element(); - const auto element = barretenberg::g1::affine_element(barretenberg::g1::one * scalar); - const uint256_t x = element.x; - const uint256_t y = element.y; - const barretenberg::fr x_lo = x.slice(0, 136); - const barretenberg::fr x_hi = x.slice(136, 272); - const barretenberg::fr y_lo = y.slice(0, 136); - const barretenberg::fr y_hi = y.slice(136, 272); - output.emplace_back(x_lo); - output.emplace_back(x_hi); - output.emplace_back(y_lo); - output.emplace_back(y_hi); - } - } - - output.emplace_back(0); // key_hash - - return output; -} - } // namespace proof_system::plonk diff --git a/cpp/src/barretenberg/plonk/proof_system/verification_key/verification_key.hpp b/cpp/src/barretenberg/plonk/proof_system/verification_key/verification_key.hpp index 974ada27b1..1e5dca35d2 100644 --- a/cpp/src/barretenberg/plonk/proof_system/verification_key/verification_key.hpp +++ b/cpp/src/barretenberg/plonk/proof_system/verification_key/verification_key.hpp @@ -63,9 +63,6 @@ struct verification_key { ~verification_key() = default; sha256::hash sha256_hash(); - std::vector export_key_in_recursion_format(); - static std::vector export_dummy_key_in_recursion_format( - const PolynomialManifest& polynomial_manifest, bool contains_recursive_proof = 0); uint32_t composer_type; size_t circuit_size; From 97ede2ef96ebb19214bc2a4e4ac4ed4fedc31e10 Mon Sep 17 00:00:00 2001 From: vezenovm Date: Mon, 22 May 2023 14:46:47 +0000 Subject: [PATCH 29/41] pr review: moved from_witness comment and renamed from_field_pt_vector --- .../dsl/acir_format/recursion_constraint.cpp | 2 +- .../barretenberg/dsl/acir_proofs/acir_proofs.cpp | 2 +- .../verification_key/verification_key.hpp | 16 ++++++++-------- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.cpp b/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.cpp index d47e493427..52c4bf3e94 100644 --- a/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.cpp +++ b/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.cpp @@ -110,7 +110,7 @@ void create_recursion_constraints(Composer& composer, const RecursionConstraint& } // recursively verify the proof - std::shared_ptr vkey = verification_key_ct::from_field_pt_vector( + std::shared_ptr vkey = verification_key_ct::from_field_elements( &composer, key_fields, inner_proof_contains_recursive_proof, nested_aggregation_indices); vkey->program_width = noir_recursive_settings::program_width; Transcript_ct transcript(&composer, manifest, proof_fields, input.public_inputs.size()); diff --git a/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.cpp b/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.cpp index 690aec5e7a..8173cd4af0 100644 --- a/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.cpp +++ b/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.cpp @@ -341,7 +341,7 @@ size_t verify_recursive_proof(uint8_t const* proof_buf, nested_aggregation_indices_all_zero &= (idx == 0); } const bool inner_proof_contains_recursive_proof = !nested_aggregation_indices_all_zero; - std::shared_ptr vkey = acir_format::verification_key_ct::from_field_pt_vector( + std::shared_ptr vkey = acir_format::verification_key_ct::from_field_elements( &composer, key_fields, inner_proof_contains_recursive_proof, nested_aggregation_object); vkey->program_width = acir_format::noir_recursive_settings::program_width; acir_format::Transcript_ct transcript(&composer, manifest, proof_fields, num_public_inputs); diff --git a/cpp/src/barretenberg/stdlib/recursion/verification_key/verification_key.hpp b/cpp/src/barretenberg/stdlib/recursion/verification_key/verification_key.hpp index 818f1f9d9a..a8dcdc5787 100644 --- a/cpp/src/barretenberg/stdlib/recursion/verification_key/verification_key.hpp +++ b/cpp/src/barretenberg/stdlib/recursion/verification_key/verification_key.hpp @@ -25,7 +25,7 @@ namespace stdlib { namespace recursion { template struct evaluation_domain { - static evaluation_domain from_field_pt_vector(const std::vector>& fields) + static evaluation_domain from_field_elements(const std::vector>& fields) { evaluation_domain domain; domain.root = fields[0]; @@ -112,15 +112,10 @@ template struct evaluation_domain { uint32 size; }; -/** - * @brief Converts a 'native' verification key into a standard library type, instantiating the `input_key` parameter as - * circuit variables. This allows the recursive verifier to accept arbitrary verification keys, where the circuit being - * verified is not fixed as part of the recursive circuit. - */ template struct verification_key { using Composer = typename Curve::Composer; - static std::shared_ptr from_field_pt_vector( + static std::shared_ptr from_field_elements( Composer* ctx, const std::vector>& fields, bool inner_proof_contains_recursive_proof = false, @@ -131,7 +126,7 @@ template struct verification_key { key->context = ctx; key->polynomial_manifest = PolynomialManifest(Composer::type); - key->domain = evaluation_domain::from_field_pt_vector({ fields[0], fields[1], fields[2] }); + key->domain = evaluation_domain::from_field_elements({ fields[0], fields[1], fields[2] }); key->n = fields[3]; key->num_public_inputs = fields[4]; @@ -163,6 +158,11 @@ template struct verification_key { return key; } + /** + * @brief Converts a 'native' verification key into a standard library type, instantiating the `input_key` parameter + * as circuit variables. This allows the recursive verifier to accept arbitrary verification keys, where the circuit + * being verified is not fixed as part of the recursive circuit. + */ static std::shared_ptr from_witness(Composer* ctx, const std::shared_ptr& input_key) { From c64d8b5aa965c5f203151ec531c25a096e53a450 Mon Sep 17 00:00:00 2001 From: vezenovm Date: Mon, 22 May 2023 15:09:58 +0000 Subject: [PATCH 30/41] moved export transcript in recursion format to DSL package --- .../dsl/acir_format/recursion_constraint.cpp | 113 +++++++++++++++++- .../dsl/acir_format/recursion_constraint.hpp | 4 + .../acir_format/recursion_constraint.test.cpp | 3 +- .../dsl/acir_proofs/acir_proofs.cpp | 2 +- .../transcript/transcript_wrappers.hpp | 111 ----------------- 5 files changed, 118 insertions(+), 115 deletions(-) diff --git a/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.cpp b/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.cpp index 52c4bf3e94..f6bc8795ed 100644 --- a/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.cpp +++ b/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.cpp @@ -40,8 +40,7 @@ void create_recursion_constraints(Composer& composer, const RecursionConstraint& inner_proof_contains_recursive_proof); const auto manifest = Composer::create_unrolled_manifest(input.public_inputs.size()); const std::vector dummy_proof = - transcript::StandardTranscript::export_dummy_transcript_in_recursion_format( - manifest, inner_proof_contains_recursive_proof); + export_dummy_transcript_in_recursion_format(manifest, inner_proof_contains_recursive_proof); for (size_t i = 0; i < input.proof.size(); ++i) { const auto proof_field_idx = input.proof[i]; // if we do NOT have a witness assignment (i.e. are just building the proving/verification keys), @@ -244,4 +243,114 @@ std::vector export_dummy_key_in_recursion_format(const Polynom return output; } +/** + * @brief Returns transcript represented as a vector of barretenberg::fr. + * Used to represent recursive proofs (i.e. proof represented as circuit-native field elements) + * + * @return std::vector + */ +std::vector export_transcript_in_recursion_format(const transcript::StandardTranscript& transcript) +{ + std::vector fields; + const auto num_rounds = transcript.get_manifest().get_num_rounds(); + for (size_t i = 0; i < num_rounds; ++i) { + for (const auto& manifest_element : transcript.get_manifest().get_round_manifest(i).elements) { + if (!manifest_element.derived_by_verifier) { + if (manifest_element.num_bytes == 32 && manifest_element.name != "public_inputs") { + fields.emplace_back(transcript.get_field_element(manifest_element.name)); + } else if (manifest_element.num_bytes == 64 && manifest_element.name != "public_inputs") { + const auto group_element = transcript.get_group_element(manifest_element.name); + const uint256_t x = group_element.x; + const uint256_t y = group_element.y; + const barretenberg::fr x_lo = x.slice(0, 136); + const barretenberg::fr x_hi = x.slice(136, 272); + const barretenberg::fr y_lo = y.slice(0, 136); + const barretenberg::fr y_hi = y.slice(136, 272); + fields.emplace_back(x_lo); + fields.emplace_back(x_hi); + fields.emplace_back(y_lo); + fields.emplace_back(y_hi); + } else { + ASSERT(manifest_element.name == "public_inputs"); + const auto public_inputs_vector = transcript.get_field_element_vector(manifest_element.name); + for (const auto& ele : public_inputs_vector) { + fields.emplace_back(ele); + } + } + } + } + } + return fields; +} + +/** + * @brief Get a dummy fake proof for recursion. All elliptic curve group elements are still valid points to prevent + * errors being thrown. + * + * @param manifest + * @return std::vector + */ +std::vector export_dummy_transcript_in_recursion_format(const transcript::Manifest& manifest, + const bool contains_recursive_proof) +{ + std::vector fields; + const auto num_rounds = manifest.get_num_rounds(); + for (size_t i = 0; i < num_rounds; ++i) { + for (const auto& manifest_element : manifest.get_round_manifest(i).elements) { + if (!manifest_element.derived_by_verifier) { + if (manifest_element.num_bytes == 32 && manifest_element.name != "public_inputs") { + fields.emplace_back(0); + } else if (manifest_element.num_bytes == 64 && manifest_element.name != "public_inputs") { + // the std::biggroup class creates unsatisfiable constraints when identical points are + // added/subtracted. + // (when verifying zk proofs this is acceptable as we make sure verification key points are not + // identical. And prover points should contain randomness for an honest Prover). This check can + // also trigger a runtime error due to causing 0 to be inverted. When creating dummy proof + // points we must be mindful of the above and make sure that each point is unique. + auto scalar = barretenberg::fr::random_element(); + const auto group_element = barretenberg::g1::affine_element(barretenberg::g1::one * scalar); + const uint256_t x = group_element.x; + const uint256_t y = group_element.y; + const barretenberg::fr x_lo = x.slice(0, 136); + const barretenberg::fr x_hi = x.slice(136, 272); + const barretenberg::fr y_lo = y.slice(0, 136); + const barretenberg::fr y_hi = y.slice(136, 272); + fields.emplace_back(x_lo); + fields.emplace_back(x_hi); + fields.emplace_back(y_lo); + fields.emplace_back(y_hi); + } else { + ASSERT(manifest_element.name == "public_inputs"); + const size_t num_public_inputs = manifest_element.num_bytes / 32; + // If we have a recursive proofs the public inputs must describe an aggregation object that + // is composed of two valid G1 points on the curve. Without this conditional we will get a + // runtime error that we are attempting to invert 0. + if (contains_recursive_proof) { + ASSERT(num_public_inputs == 16); + for (size_t k = 0; k < num_public_inputs / 4; ++k) { + auto scalar = barretenberg::fr::random_element(); + const auto group_element = barretenberg::g1::affine_element(barretenberg::g1::one * scalar); + const uint256_t x = group_element.x; + const uint256_t y = group_element.y; + const barretenberg::fr x_lo = x.slice(0, 136); + const barretenberg::fr x_hi = x.slice(136, 272); + const barretenberg::fr y_lo = y.slice(0, 136); + const barretenberg::fr y_hi = y.slice(136, 272); + fields.emplace_back(x_lo); + fields.emplace_back(x_hi); + fields.emplace_back(y_lo); + fields.emplace_back(y_hi); + } + } else { + for (size_t j = 0; j < num_public_inputs; ++j) { + fields.emplace_back(0); + } + } + } + } + } + } + return fields; +} + } // namespace acir_format diff --git a/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.hpp b/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.hpp index 15af19039f..615f23a553 100644 --- a/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.hpp +++ b/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.hpp @@ -66,6 +66,10 @@ std::vector export_key_in_recursion_format(std::shared_ptr export_dummy_key_in_recursion_format(const PolynomialManifest& polynomial_manifest, bool contains_recursive_proof = 0); +std::vector export_transcript_in_recursion_format(const transcript::StandardTranscript& transcript); +std::vector export_dummy_transcript_in_recursion_format(const transcript::Manifest& manifest, + const bool contains_recursive_proof); + template inline void read(B& buf, RecursionConstraint& constraint) { using serialize::read; diff --git a/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.test.cpp b/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.test.cpp index 2f4436c2a3..82e6135622 100644 --- a/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.test.cpp +++ b/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.test.cpp @@ -140,7 +140,8 @@ acir_format::Composer create_outer_circuit(std::vector& i transcript::HashType::PlookupPedersenBlake3s, 16); - const std::vector proof_witnesses = transcript.export_transcript_in_recursion_format(); + const std::vector proof_witnesses = + acir_format::export_transcript_in_recursion_format(transcript); const std::vector key_witnesses = acir_format::export_key_in_recursion_format(inner_verifier.key); diff --git a/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.cpp b/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.cpp index 8173cd4af0..319c74a34f 100644 --- a/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.cpp +++ b/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.cpp @@ -125,7 +125,7 @@ size_t serialize_proof_into_field_elements(uint8_t const* proof_data_buf, transcript::HashType::PlookupPedersenBlake3s, 16); - std::vector output = transcript.export_transcript_in_recursion_format(); + std::vector output = acir_format::export_transcript_in_recursion_format(transcript); // NOTE: this output buffer will always have a fixed size! Maybe just precompute? const size_t output_size_bytes = output.size() * sizeof(barretenberg::fr); diff --git a/cpp/src/barretenberg/transcript/transcript_wrappers.hpp b/cpp/src/barretenberg/transcript/transcript_wrappers.hpp index 2fe0830c37..bd33eff800 100644 --- a/cpp/src/barretenberg/transcript/transcript_wrappers.hpp +++ b/cpp/src/barretenberg/transcript/transcript_wrappers.hpp @@ -56,117 +56,6 @@ class StandardTranscript : public Transcript { // TODO(luke): temporary function for debugging barretenberg::fr get_mock_challenge() { return barretenberg::fr::random_element(); }; - - /** - * @brief Returns transcript represented as a vector of barretenberg::fr. - * Used to represent recursive proofs (i.e. proof represented as circuit-native field elements) - * - * @return std::vector - */ - std::vector export_transcript_in_recursion_format() - { - std::vector fields; - const auto num_rounds = get_manifest().get_num_rounds(); - for (size_t i = 0; i < num_rounds; ++i) { - for (const auto& manifest_element : get_manifest().get_round_manifest(i).elements) { - if (!manifest_element.derived_by_verifier) { - if (manifest_element.num_bytes == 32 && manifest_element.name != "public_inputs") { - fields.emplace_back(get_field_element(manifest_element.name)); - } else if (manifest_element.num_bytes == 64 && manifest_element.name != "public_inputs") { - const auto group_element = get_group_element(manifest_element.name); - const uint256_t x = group_element.x; - const uint256_t y = group_element.y; - const barretenberg::fr x_lo = x.slice(0, 136); - const barretenberg::fr x_hi = x.slice(136, 272); - const barretenberg::fr y_lo = y.slice(0, 136); - const barretenberg::fr y_hi = y.slice(136, 272); - fields.emplace_back(x_lo); - fields.emplace_back(x_hi); - fields.emplace_back(y_lo); - fields.emplace_back(y_hi); - } else { - ASSERT(manifest_element.name == "public_inputs"); - const auto public_inputs_vector = get_field_element_vector(manifest_element.name); - for (const auto& ele : public_inputs_vector) { - fields.emplace_back(ele); - } - } - } - } - } - return fields; - } - - /** - * @brief Get a dummy fake proof for recursion. All elliptic curve group elements are still valid points to prevent - * errors being thrown. - * - * @param manifest - * @return std::vector - */ - static std::vector export_dummy_transcript_in_recursion_format( - const Manifest& manifest, const bool contains_recursive_proof) - { - std::vector fields; - const auto num_rounds = manifest.get_num_rounds(); - for (size_t i = 0; i < num_rounds; ++i) { - for (const auto& manifest_element : manifest.get_round_manifest(i).elements) { - if (!manifest_element.derived_by_verifier) { - if (manifest_element.num_bytes == 32 && manifest_element.name != "public_inputs") { - fields.emplace_back(0); - } else if (manifest_element.num_bytes == 64 && manifest_element.name != "public_inputs") { - // the std::biggroup class creates unsatisfiable constraints when identical points are - // added/subtracted. - // (when verifying zk proofs this is acceptable as we make sure verification key points are not - // identical. And prover points should contain randomness for an honest Prover). This check can - // also trigger a runtime error due to causing 0 to be inverted. When creating dummy proof - // points we must be mindful of the above and make sure that each point is unique. - auto scalar = barretenberg::fr::random_element(); - const auto group_element = barretenberg::g1::affine_element(barretenberg::g1::one * scalar); - const uint256_t x = group_element.x; - const uint256_t y = group_element.y; - const barretenberg::fr x_lo = x.slice(0, 136); - const barretenberg::fr x_hi = x.slice(136, 272); - const barretenberg::fr y_lo = y.slice(0, 136); - const barretenberg::fr y_hi = y.slice(136, 272); - fields.emplace_back(x_lo); - fields.emplace_back(x_hi); - fields.emplace_back(y_lo); - fields.emplace_back(y_hi); - } else { - ASSERT(manifest_element.name == "public_inputs"); - const size_t num_public_inputs = manifest_element.num_bytes / 32; - // If we have a recursive proofs the public inputs must describe an aggregation object that - // is composed of two valid G1 points on the curve. Without this conditional we will get a - // runtime error that we are attempting to invert 0. - if (contains_recursive_proof) { - ASSERT(num_public_inputs == 16); - for (size_t k = 0; k < num_public_inputs / 4; ++k) { - auto scalar = barretenberg::fr::random_element(); - const auto group_element = - barretenberg::g1::affine_element(barretenberg::g1::one * scalar); - const uint256_t x = group_element.x; - const uint256_t y = group_element.y; - const barretenberg::fr x_lo = x.slice(0, 136); - const barretenberg::fr x_hi = x.slice(136, 272); - const barretenberg::fr y_lo = y.slice(0, 136); - const barretenberg::fr y_hi = y.slice(136, 272); - fields.emplace_back(x_lo); - fields.emplace_back(x_hi); - fields.emplace_back(y_lo); - fields.emplace_back(y_hi); - } - } else { - for (size_t j = 0; j < num_public_inputs; ++j) { - fields.emplace_back(0); - } - } - } - } - } - } - return fields; - } }; } // namespace transcript From f3baab0fea65f6e04a4ecac96af37a13a5e76fc9 Mon Sep 17 00:00:00 2001 From: vezenovm Date: Mon, 22 May 2023 18:28:28 +0000 Subject: [PATCH 31/41] move order of acir functions --- .../dsl/acir_proofs/acir_proofs.cpp | 152 +++++++++--------- .../dsl/acir_proofs/acir_proofs.hpp | 18 ++- .../barretenberg/dsl/acir_proofs/c_bind.cpp | 33 ++-- .../barretenberg/dsl/acir_proofs/c_bind.hpp | 18 ++- cpp/src/barretenberg/dsl/types.hpp | 2 +- 5 files changed, 113 insertions(+), 110 deletions(-) diff --git a/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.cpp b/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.cpp index 319c74a34f..877921f217 100644 --- a/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.cpp +++ b/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.cpp @@ -105,82 +105,6 @@ size_t init_verification_key(void* pippenger, uint8_t const* g2x, uint8_t const* return buffer.size(); } -/** - * @brief Takes in a proof buffer and converts into a vector of field elements. - * The Recursion opcode requires the proof serialized as a vector of witnesses. - * Use this method to get the witness values! - * - * @param proof_data_buf - * @param serialized_proof_data_buf - */ -size_t serialize_proof_into_field_elements(uint8_t const* proof_data_buf, - uint8_t** serialized_proof_data_buf, - size_t proof_data_length, - size_t num_inner_public_inputs) -{ - plonk::proof proof = { std::vector(proof_data_buf, &proof_data_buf[proof_data_length]) }; - - transcript::StandardTranscript transcript(proof.proof_data, - acir_format::Composer::create_manifest(num_inner_public_inputs), - transcript::HashType::PlookupPedersenBlake3s, - 16); - - std::vector output = acir_format::export_transcript_in_recursion_format(transcript); - - // NOTE: this output buffer will always have a fixed size! Maybe just precompute? - const size_t output_size_bytes = output.size() * sizeof(barretenberg::fr); - auto raw_buf = (uint8_t*)malloc(output_size_bytes); - - // The serialization code below will convert out of Montgomery form before writing to the buffer - for (size_t i = 0; i < output.size(); ++i) { - barretenberg::fr::serialize_to_buffer(output[i], &raw_buf[i * 32]); - } - *serialized_proof_data_buf = raw_buf; - - return output_size_bytes; -} - -/** - * @brief Takes in a verification key buffer and converts into a vector of field elements. - * The Recursion opcode requires the vk serialized as a vector of witnesses. - * Use this method to get the witness values! - * - * @param vk_buf - * @param serialized_vk_buf - */ -size_t serialize_verification_key_into_field_elements(uint8_t const* g2x, - uint8_t const* vk_buf, - uint8_t** serialized_vk_buf, - uint8_t** serialized_vk_hash_buf) -{ - auto crs = std::make_shared(g2x); - plonk::verification_key_data vk_data; - read(vk_buf, vk_data); - auto vkey = std::make_shared(std::move(vk_data), crs); - std::vector output = acir_format::export_key_in_recursion_format(vkey); - - // NOTE: this output buffer will always have a fixed size! Maybe just precompute? - // Cut off 32 bytes as last element is the verification key hash which is not part of the key :o - const size_t output_size_bytes = output.size() * sizeof(barretenberg::fr) - 32; - - auto raw_buf = (uint8_t*)malloc(output_size_bytes); - auto vk_hash_raw_buf = (uint8_t*)malloc(32); - - // The serialization code below will convert out of Montgomery form before writing to the buffer - for (size_t i = 0; i < output.size() - 1; ++i) { - barretenberg::fr::serialize_to_buffer(output[i], &raw_buf[i * 32]); - } - barretenberg::fr::serialize_to_buffer(output[output.size() - 1], vk_hash_raw_buf); - - // copy the vkey into serialized_vk_buf - *serialized_vk_buf = raw_buf; - - // copy the vkey hash into serialized_vk_hash_buf - *serialized_vk_hash_buf = vk_hash_raw_buf; - - return output_size_bytes; -} - size_t new_proof(void* pippenger, uint8_t const* g2x, uint8_t const* pk_buf, @@ -379,4 +303,80 @@ size_t verify_recursive_proof(uint8_t const* proof_buf, return output_size_bytes; } +/** + * @brief Takes in a proof buffer and converts into a vector of field elements. + * The Recursion opcode requires the proof serialized as a vector of witnesses. + * Use this method to get the witness values! + * + * @param proof_data_buf + * @param serialized_proof_data_buf + */ +size_t serialize_proof_into_field_elements(uint8_t const* proof_data_buf, + uint8_t** serialized_proof_data_buf, + size_t proof_data_length, + size_t num_inner_public_inputs) +{ + plonk::proof proof = { std::vector(proof_data_buf, &proof_data_buf[proof_data_length]) }; + + transcript::StandardTranscript transcript(proof.proof_data, + acir_format::Composer::create_manifest(num_inner_public_inputs), + transcript::HashType::PlookupPedersenBlake3s, + 16); + + std::vector output = acir_format::export_transcript_in_recursion_format(transcript); + + // NOTE: this output buffer will always have a fixed size! Maybe just precompute? + const size_t output_size_bytes = output.size() * sizeof(barretenberg::fr); + auto raw_buf = (uint8_t*)malloc(output_size_bytes); + + // The serialization code below will convert out of Montgomery form before writing to the buffer + for (size_t i = 0; i < output.size(); ++i) { + barretenberg::fr::serialize_to_buffer(output[i], &raw_buf[i * 32]); + } + *serialized_proof_data_buf = raw_buf; + + return output_size_bytes; +} + +/** + * @brief Takes in a verification key buffer and converts into a vector of field elements. + * The Recursion opcode requires the vk serialized as a vector of witnesses. + * Use this method to get the witness values! + * + * @param vk_buf + * @param serialized_vk_buf + */ +size_t serialize_verification_key_into_field_elements(uint8_t const* g2x, + uint8_t const* vk_buf, + uint8_t** serialized_vk_buf, + uint8_t** serialized_vk_hash_buf) +{ + auto crs = std::make_shared(g2x); + plonk::verification_key_data vk_data; + read(vk_buf, vk_data); + auto vkey = std::make_shared(std::move(vk_data), crs); + std::vector output = acir_format::export_key_in_recursion_format(vkey); + + // NOTE: this output buffer will always have a fixed size! Maybe just precompute? + // Cut off 32 bytes as last element is the verification key hash which is not part of the key :o + const size_t output_size_bytes = output.size() * sizeof(barretenberg::fr) - 32; + + auto raw_buf = (uint8_t*)malloc(output_size_bytes); + auto vk_hash_raw_buf = (uint8_t*)malloc(32); + + // The serialization code below will convert out of Montgomery form before writing to the buffer + for (size_t i = 0; i < output.size() - 1; ++i) { + barretenberg::fr::serialize_to_buffer(output[i], &raw_buf[i * 32]); + } + barretenberg::fr::serialize_to_buffer(output[output.size() - 1], vk_hash_raw_buf); + + // copy the vkey into serialized_vk_buf + *serialized_vk_buf = raw_buf; + + // copy the vkey hash into serialized_vk_hash_buf + *serialized_vk_hash_buf = vk_hash_raw_buf; + + return output_size_bytes; +} + } // namespace acir_proofs diff --git a/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.hpp b/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.hpp index cad0d1a79f..24df5e4f3e 100644 --- a/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.hpp +++ b/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.hpp @@ -8,14 +8,6 @@ uint32_t get_exact_circuit_size(uint8_t const* constraint_system_buf); uint32_t get_total_circuit_size(uint8_t const* constraint_system_buf); size_t init_proving_key(uint8_t const* constraint_system_buf, uint8_t const** pk_buf); size_t init_verification_key(void* pippenger, uint8_t const* g2x, uint8_t const* pk_buf, uint8_t const** vk_buf); -size_t serialize_verification_key_into_field_elements(uint8_t const* g2x, - uint8_t const* vk_buf, - uint8_t** serialized_vk_buf, - uint8_t** serialized_vk_hash_buf); -size_t serialize_proof_into_field_elements(uint8_t const* proof_data_buf, - uint8_t** serialized_proof_data_buf, - size_t proof_data_length, - size_t num_inner_public_inputs); size_t new_proof(void* pippenger, uint8_t const* g2x, uint8_t const* pk_buf, @@ -29,6 +21,8 @@ bool verify_proof(uint8_t const* g2x, uint8_t* proof, uint32_t length, bool is_recursive); + +// Recursion specific methods size_t verify_recursive_proof(uint8_t const* proof_buf, uint32_t proof_length, uint8_t const* vk_buf, @@ -36,5 +30,13 @@ size_t verify_recursive_proof(uint8_t const* proof_buf, uint32_t num_public_inputs, uint8_t const* input_aggregation_obj_buf, uint8_t** output_aggregation_obj_buf); +size_t serialize_verification_key_into_field_elements(uint8_t const* g2x, + uint8_t const* vk_buf, + uint8_t** serialized_vk_buf, + uint8_t** serialized_vk_hash_buf); +size_t serialize_proof_into_field_elements(uint8_t const* proof_data_buf, + uint8_t** serialized_proof_data_buf, + size_t proof_data_length, + size_t num_inner_public_inputs); } // namespace acir_proofs diff --git a/cpp/src/barretenberg/dsl/acir_proofs/c_bind.cpp b/cpp/src/barretenberg/dsl/acir_proofs/c_bind.cpp index 62621d00be..34b735c67e 100644 --- a/cpp/src/barretenberg/dsl/acir_proofs/c_bind.cpp +++ b/cpp/src/barretenberg/dsl/acir_proofs/c_bind.cpp @@ -35,23 +35,6 @@ WASM_EXPORT size_t acir_proofs_init_verification_key(void* pippenger, return acir_proofs::init_verification_key(pippenger, g2x, pk_buf, vk_buf); } -WASM_EXPORT size_t acir_serialize_verification_key_into_field_elements(uint8_t const* g2x, - uint8_t const* vk_buf, - uint8_t** serialized_vk_buf, - uint8_t** serialized_vk_hash_buf) -{ - return acir_proofs::serialize_verification_key_into_field_elements( - g2x, vk_buf, serialized_vk_buf, serialized_vk_hash_buf); -} -WASM_EXPORT size_t acir_serialize_proof_into_field_elements(uint8_t const* proof_data_buf, - uint8_t** serialized_proof_data_buf, - size_t proof_data_length, - size_t num_inner_public_inputs) -{ - return acir_proofs::serialize_proof_into_field_elements( - proof_data_buf, serialized_proof_data_buf, proof_data_length, num_inner_public_inputs); -} - WASM_EXPORT size_t acir_proofs_new_proof(void* pippenger, uint8_t const* g2x, uint8_t const* pk_buf, @@ -89,4 +72,20 @@ WASM_EXPORT size_t acir_proofs_verify_recursive_proof(uint8_t const* proof_buf, input_aggregation_obj_buf, output_aggregation_obj_buf); } +WASM_EXPORT size_t acir_serialize_verification_key_into_field_elements(uint8_t const* g2x, + uint8_t const* vk_buf, + uint8_t** serialized_vk_buf, + uint8_t** serialized_vk_hash_buf) +{ + return acir_proofs::serialize_verification_key_into_field_elements( + g2x, vk_buf, serialized_vk_buf, serialized_vk_hash_buf); +} +WASM_EXPORT size_t acir_serialize_proof_into_field_elements(uint8_t const* proof_data_buf, + uint8_t** serialized_proof_data_buf, + size_t proof_data_length, + size_t num_inner_public_inputs) +{ + return acir_proofs::serialize_proof_into_field_elements( + proof_data_buf, serialized_proof_data_buf, proof_data_length, num_inner_public_inputs); +} } diff --git a/cpp/src/barretenberg/dsl/acir_proofs/c_bind.hpp b/cpp/src/barretenberg/dsl/acir_proofs/c_bind.hpp index 452f41d1ca..5e42747b57 100644 --- a/cpp/src/barretenberg/dsl/acir_proofs/c_bind.hpp +++ b/cpp/src/barretenberg/dsl/acir_proofs/c_bind.hpp @@ -14,14 +14,6 @@ WASM_EXPORT size_t acir_proofs_init_verification_key(void* pippenger, uint8_t const* g2x, uint8_t const* pk_buf, uint8_t const** vk_buf); -WASM_EXPORT size_t acir_serialize_verification_key_into_field_elements(uint8_t const* g2x, - uint8_t const* vk_buf, - uint8_t** serialized_vk_buf, - uint8_t** serialized_vk_hash_buf); -WASM_EXPORT size_t acir_serialize_proof_into_field_elements(uint8_t const* proof_data_buf, - uint8_t** serialized_proof_data_buf, - size_t proof_data_length, - size_t num_inner_public_inputs); WASM_EXPORT size_t acir_proofs_new_proof(void* pippenger, uint8_t const* g2x, uint8_t const* pk_buf, @@ -35,6 +27,8 @@ WASM_EXPORT bool acir_proofs_verify_proof(uint8_t const* g2x, uint8_t* proof, uint32_t length, bool is_recursive); + +// Recursion specific methods WASM_EXPORT size_t acir_proofs_verify_recursive_proof(uint8_t const* proof_buf, uint32_t proof_length, uint8_t const* vk_buf, @@ -42,4 +36,12 @@ WASM_EXPORT size_t acir_proofs_verify_recursive_proof(uint8_t const* proof_buf, uint32_t num_public_inputs, uint8_t const* input_aggregation_obj_buf, uint8_t** output_aggregation_obj_buf); +WASM_EXPORT size_t acir_serialize_verification_key_into_field_elements(uint8_t const* g2x, + uint8_t const* vk_buf, + uint8_t** serialized_vk_buf, + uint8_t** serialized_vk_hash_buf); +WASM_EXPORT size_t acir_serialize_proof_into_field_elements(uint8_t const* proof_data_buf, + uint8_t** serialized_proof_data_buf, + size_t proof_data_length, + size_t num_inner_public_inputs); } diff --git a/cpp/src/barretenberg/dsl/types.hpp b/cpp/src/barretenberg/dsl/types.hpp index 192fc10fe9..f9823984bf 100644 --- a/cpp/src/barretenberg/dsl/types.hpp +++ b/cpp/src/barretenberg/dsl/types.hpp @@ -64,7 +64,7 @@ using hash_path_ct = proof_system::plonk::stdlib::merkle_tree::hash_path; -// Ultra-composer specific typesv +// Ultra-composer specific types using rom_table_ct = proof_system::plonk::stdlib::rom_table; using ram_table_ct = proof_system::plonk::stdlib::ram_table; From a793b2dbc9a17c4beddee1bddc984e97b7c790c8 Mon Sep 17 00:00:00 2001 From: vezenovm Date: Mon, 22 May 2023 18:28:58 +0000 Subject: [PATCH 32/41] remove ecc bb_module declaration in env package --- cpp/src/barretenberg/env/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/src/barretenberg/env/CMakeLists.txt b/cpp/src/barretenberg/env/CMakeLists.txt index e084984b85..f4a8428e63 100644 --- a/cpp/src/barretenberg/env/CMakeLists.txt +++ b/cpp/src/barretenberg/env/CMakeLists.txt @@ -1 +1 @@ -barretenberg_module(env ecc) \ No newline at end of file +barretenberg_module(env) \ No newline at end of file From f9542663b34bf5c10d439898f782309a86a66c46 Mon Sep 17 00:00:00 2001 From: vezenovm Date: Mon, 22 May 2023 19:15:37 +0000 Subject: [PATCH 33/41] chore: remove usage of magic numbers when slicing g1::affine_element into barretenberg::fr --- .../dsl/acir_format/recursion_constraint.cpp | 90 +++++++++---------- .../dsl/acir_format/recursion_constraint.hpp | 11 +++ 2 files changed, 51 insertions(+), 50 deletions(-) diff --git a/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.cpp b/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.cpp index f6bc8795ed..92e2e52de1 100644 --- a/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.cpp +++ b/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.cpp @@ -8,6 +8,9 @@ namespace acir_format { using namespace proof_system::plonk; +static constexpr uint64_t TWO_LIMBS_BITS_IN_FIELD_SIMULATION = NUM_LIMB_BITS_IN_FIELD_SIMULATION * 2; +static constexpr uint64_t FOUR_LIMBS_BITS_IN_FIELD_SIMULATION = NUM_LIMB_BITS_IN_FIELD_SIMULATION * 4; + void generate_dummy_proof() {} /** * @brief Add constraints required to recursively verify an UltraPlonk proof @@ -164,16 +167,11 @@ std::vector export_key_in_recursion_format(std::shared_ptrpolynomial_manifest.get()) { if (descriptor.source == PolynomialSource::SELECTOR || descriptor.source == PolynomialSource::PERMUTATION) { const auto element = vkey->commitments.at(std::string(descriptor.commitment_label)); - const uint256_t x = element.x; - const uint256_t y = element.y; - const barretenberg::fr x_lo = x.slice(0, 136); - const barretenberg::fr x_hi = x.slice(136, 272); - const barretenberg::fr y_lo = y.slice(0, 136); - const barretenberg::fr y_hi = y.slice(136, 272); - output.emplace_back(x_lo); - output.emplace_back(x_hi); - output.emplace_back(y_lo); - output.emplace_back(y_hi); + auto g1_as_fields = export_g1_affine_element_as_fields(element); + output.emplace_back(g1_as_fields.x_lo); + output.emplace_back(g1_as_fields.x_hi); + output.emplace_back(g1_as_fields.y_lo); + output.emplace_back(g1_as_fields.y_hi); } } @@ -225,16 +223,11 @@ std::vector export_dummy_key_in_recursion_format(const Polynom // transcript point is unique. auto scalar = barretenberg::fr::random_element(); const auto element = barretenberg::g1::affine_element(barretenberg::g1::one * scalar); - const uint256_t x = element.x; - const uint256_t y = element.y; - const barretenberg::fr x_lo = x.slice(0, 136); - const barretenberg::fr x_hi = x.slice(136, 272); - const barretenberg::fr y_lo = y.slice(0, 136); - const barretenberg::fr y_hi = y.slice(136, 272); - output.emplace_back(x_lo); - output.emplace_back(x_hi); - output.emplace_back(y_lo); - output.emplace_back(y_hi); + auto g1_as_fields = export_g1_affine_element_as_fields(element); + output.emplace_back(g1_as_fields.x_lo); + output.emplace_back(g1_as_fields.x_hi); + output.emplace_back(g1_as_fields.y_lo); + output.emplace_back(g1_as_fields.y_hi); } } @@ -260,16 +253,11 @@ std::vector export_transcript_in_recursion_format(const transc fields.emplace_back(transcript.get_field_element(manifest_element.name)); } else if (manifest_element.num_bytes == 64 && manifest_element.name != "public_inputs") { const auto group_element = transcript.get_group_element(manifest_element.name); - const uint256_t x = group_element.x; - const uint256_t y = group_element.y; - const barretenberg::fr x_lo = x.slice(0, 136); - const barretenberg::fr x_hi = x.slice(136, 272); - const barretenberg::fr y_lo = y.slice(0, 136); - const barretenberg::fr y_hi = y.slice(136, 272); - fields.emplace_back(x_lo); - fields.emplace_back(x_hi); - fields.emplace_back(y_lo); - fields.emplace_back(y_hi); + auto g1_as_fields = export_g1_affine_element_as_fields(group_element); + fields.emplace_back(g1_as_fields.x_lo); + fields.emplace_back(g1_as_fields.x_hi); + fields.emplace_back(g1_as_fields.y_lo); + fields.emplace_back(g1_as_fields.y_hi); } else { ASSERT(manifest_element.name == "public_inputs"); const auto public_inputs_vector = transcript.get_field_element_vector(manifest_element.name); @@ -309,16 +297,11 @@ std::vector export_dummy_transcript_in_recursion_format(const // points we must be mindful of the above and make sure that each point is unique. auto scalar = barretenberg::fr::random_element(); const auto group_element = barretenberg::g1::affine_element(barretenberg::g1::one * scalar); - const uint256_t x = group_element.x; - const uint256_t y = group_element.y; - const barretenberg::fr x_lo = x.slice(0, 136); - const barretenberg::fr x_hi = x.slice(136, 272); - const barretenberg::fr y_lo = y.slice(0, 136); - const barretenberg::fr y_hi = y.slice(136, 272); - fields.emplace_back(x_lo); - fields.emplace_back(x_hi); - fields.emplace_back(y_lo); - fields.emplace_back(y_hi); + auto g1_as_fields = export_g1_affine_element_as_fields(group_element); + fields.emplace_back(g1_as_fields.x_lo); + fields.emplace_back(g1_as_fields.x_hi); + fields.emplace_back(g1_as_fields.y_lo); + fields.emplace_back(g1_as_fields.y_hi); } else { ASSERT(manifest_element.name == "public_inputs"); const size_t num_public_inputs = manifest_element.num_bytes / 32; @@ -330,16 +313,11 @@ std::vector export_dummy_transcript_in_recursion_format(const for (size_t k = 0; k < num_public_inputs / 4; ++k) { auto scalar = barretenberg::fr::random_element(); const auto group_element = barretenberg::g1::affine_element(barretenberg::g1::one * scalar); - const uint256_t x = group_element.x; - const uint256_t y = group_element.y; - const barretenberg::fr x_lo = x.slice(0, 136); - const barretenberg::fr x_hi = x.slice(136, 272); - const barretenberg::fr y_lo = y.slice(0, 136); - const barretenberg::fr y_hi = y.slice(136, 272); - fields.emplace_back(x_lo); - fields.emplace_back(x_hi); - fields.emplace_back(y_lo); - fields.emplace_back(y_hi); + auto g1_as_fields = export_g1_affine_element_as_fields(group_element); + fields.emplace_back(g1_as_fields.x_lo); + fields.emplace_back(g1_as_fields.x_hi); + fields.emplace_back(g1_as_fields.y_lo); + fields.emplace_back(g1_as_fields.y_hi); } } else { for (size_t j = 0; j < num_public_inputs; ++j) { @@ -353,4 +331,16 @@ std::vector export_dummy_transcript_in_recursion_format(const return fields; } +G1AsFields export_g1_affine_element_as_fields(const barretenberg::g1::affine_element& group_element) +{ + const uint256_t x = group_element.x; + const uint256_t y = group_element.y; + const barretenberg::fr x_lo = x.slice(0, TWO_LIMBS_BITS_IN_FIELD_SIMULATION); + const barretenberg::fr x_hi = x.slice(TWO_LIMBS_BITS_IN_FIELD_SIMULATION, FOUR_LIMBS_BITS_IN_FIELD_SIMULATION); + const barretenberg::fr y_lo = y.slice(0, TWO_LIMBS_BITS_IN_FIELD_SIMULATION); + const barretenberg::fr y_hi = y.slice(TWO_LIMBS_BITS_IN_FIELD_SIMULATION, FOUR_LIMBS_BITS_IN_FIELD_SIMULATION); + + return G1AsFields{ x_lo, x_hi, y_lo, y_hi }; +} + } // namespace acir_format diff --git a/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.hpp b/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.hpp index 615f23a553..d98b0dbda8 100644 --- a/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.hpp +++ b/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.hpp @@ -70,6 +70,17 @@ std::vector export_transcript_in_recursion_format(const transc std::vector export_dummy_transcript_in_recursion_format(const transcript::Manifest& manifest, const bool contains_recursive_proof); +// In order to interact with a recursive aggregation state inside of a circuit, we need to represent its internal G1 +// elements as field elements. This happens in multiple locations when creating a recursion constraint. The struct and +// method below export a g1 affine element as fields to use as part of the recursive circuit. +struct G1AsFields { + barretenberg::fr x_lo; + barretenberg::fr x_hi; + barretenberg::fr y_lo; + barretenberg::fr y_hi; +}; +G1AsFields export_g1_affine_element_as_fields(const barretenberg::g1::affine_element& group_element); + template inline void read(B& buf, RecursionConstraint& constraint) { using serialize::read; From 0f0d519d973f347a0c3e79fe6347c2d7a4cdfb27 Mon Sep 17 00:00:00 2001 From: vezenovm Date: Tue, 23 May 2023 01:56:54 +0000 Subject: [PATCH 34/41] introduce NUM_AGGREGATION_ELEMENTS constant and use it through recursion constraint --- .../dsl/acir_format/recursion_constraint.cpp | 11 +++++---- .../dsl/acir_format/recursion_constraint.hpp | 8 +++++-- .../dsl/acir_proofs/acir_proofs.cpp | 23 +++++++++++-------- 3 files changed, 26 insertions(+), 16 deletions(-) diff --git a/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.cpp b/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.cpp index 92e2e52de1..564bfd9d68 100644 --- a/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.cpp +++ b/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.cpp @@ -8,6 +8,9 @@ namespace acir_format { using namespace proof_system::plonk; +// `NUM_LIMB_BITS_IN_FIELD_SIMULATION` is the limb size when simulating a non-native field using the bigfield class +// A aggregation object is two acir_format::g1_ct types where each coordinate in a point is a non-native field. +// Each field is represented as four limbs. We split those limbs in half when serializing to/from buffer. static constexpr uint64_t TWO_LIMBS_BITS_IN_FIELD_SIMULATION = NUM_LIMB_BITS_IN_FIELD_SIMULATION * 2; static constexpr uint64_t FOUR_LIMBS_BITS_IN_FIELD_SIMULATION = NUM_LIMB_BITS_IN_FIELD_SIMULATION * 4; @@ -156,7 +159,7 @@ std::vector export_key_in_recursion_format(std::shared_ptrcircuit_size); output.emplace_back(vkey->num_public_inputs); output.emplace_back(vkey->contains_recursive_proof); - for (size_t i = 0; i < 16; ++i) { + for (size_t i = 0; i < RecursionConstraint::AGGREGATION_OBJECT_SIZE; ++i) { if (vkey->recursive_proof_public_input_indices.size() > i) { output.emplace_back(vkey->recursive_proof_public_input_indices[i]); } else { @@ -209,7 +212,7 @@ std::vector export_dummy_key_in_recursion_format(const Polynom output.emplace_back(1); // num public inputs output.emplace_back(contains_recursive_proof); // contains_recursive_proof - for (size_t i = 0; i < 16; ++i) { + for (size_t i = 0; i < RecursionConstraint::AGGREGATION_OBJECT_SIZE; ++i) { output.emplace_back(0); // recursive_proof_public_input_indices } @@ -309,8 +312,8 @@ std::vector export_dummy_transcript_in_recursion_format(const // is composed of two valid G1 points on the curve. Without this conditional we will get a // runtime error that we are attempting to invert 0. if (contains_recursive_proof) { - ASSERT(num_public_inputs == 16); - for (size_t k = 0; k < num_public_inputs / 4; ++k) { + ASSERT(num_public_inputs == RecursionConstraint::AGGREGATION_OBJECT_SIZE); + for (size_t k = 0; k < RecursionConstraint::NUM_AGGREGATION_ELEMENTS; ++k) { auto scalar = barretenberg::fr::random_element(); const auto group_element = barretenberg::g1::affine_element(barretenberg::g1::one * scalar); auto g1_as_fields = export_g1_affine_element_as_fields(group_element); diff --git a/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.hpp b/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.hpp index d98b0dbda8..4d9b428166 100644 --- a/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.hpp +++ b/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.hpp @@ -1,6 +1,5 @@ #pragma once #include -// #include "barretenberg/stdlib/types/types.hpp" #include "barretenberg/dsl/types.hpp" #include "barretenberg/plonk/proof_system/verification_key/verification_key.hpp" @@ -44,7 +43,12 @@ using namespace proof_system::plonk; * */ struct RecursionConstraint { - static constexpr size_t AGGREGATION_OBJECT_SIZE = 16; // 16 field elements + // An aggregation state is represented by two G1 affine elements. Each G1 point has + // two field element coordinates (x, y). Thus, four field elements + static constexpr size_t NUM_AGGREGATION_ELEMENTS = 4; + // Four limbs are used when simulating a non-native field using the bigfield class + static constexpr size_t AGGREGATION_OBJECT_SIZE = + NUM_AGGREGATION_ELEMENTS * NUM_QUOTIENT_PARTS; // 16 field elements std::vector key; std::vector proof; std::vector public_inputs; diff --git a/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.cpp b/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.cpp index 877921f217..8ccd5d13b3 100644 --- a/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.cpp +++ b/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.cpp @@ -87,7 +87,7 @@ size_t init_verification_key(void* pippenger, uint8_t const* g2x, uint8_t const* // Set the recursive proof indices as this is not done in `compute_verification_key_base` verification_key->contains_recursive_proof = proving_key->contains_recursive_proof; - for (size_t i = 0; i < 16; ++i) { + for (size_t i = 0; i < acir_format::RecursionConstraint::AGGREGATION_OBJECT_SIZE; ++i) { if (proving_key->recursive_proof_public_input_indices.size() > i) { verification_key->recursive_proof_public_input_indices.emplace_back( proving_key->recursive_proof_public_input_indices[i]); @@ -213,22 +213,24 @@ size_t verify_recursive_proof(uint8_t const* proof_buf, uint8_t const* input_aggregation_obj_buf, uint8_t** output_aggregation_obj_buf) { + const size_t NUM_AGGREGATION_ELEMENTS = acir_format::RecursionConstraint::NUM_AGGREGATION_ELEMENTS; bool inner_aggregation_all_zero = true; - std::vector aggregation_input(16); - for (size_t i = 0; i < 16; i++) { + std::vector aggregation_input(acir_format::RecursionConstraint::AGGREGATION_OBJECT_SIZE); + for (size_t i = 0; i < acir_format::RecursionConstraint::AGGREGATION_OBJECT_SIZE; i++) { aggregation_input[i] = barretenberg::fr::serialize_from_buffer(&input_aggregation_obj_buf[i * 32]); inner_aggregation_all_zero &= (aggregation_input[i].is_zero()); } acir_format::aggregation_state_ct previous_aggregation; if (!inner_aggregation_all_zero) { - std::array aggregation_elements; - for (size_t i = 0; i < 4; ++i) { - aggregation_elements[i] = acir_format::bn254::fq_ct(acir_format::field_ct(aggregation_input[4 * i]), - acir_format::field_ct(aggregation_input[4 * i + 1]), - acir_format::field_ct(aggregation_input[4 * i + 2]), - acir_format::field_ct(aggregation_input[4 * i + 3])); + std::array aggregation_elements; + for (size_t i = 0; i < NUM_AGGREGATION_ELEMENTS; ++i) { + aggregation_elements[i] = + acir_format::bn254::fq_ct(acir_format::field_ct(aggregation_input[NUM_AGGREGATION_ELEMENTS * i]), + acir_format::field_ct(aggregation_input[NUM_AGGREGATION_ELEMENTS * i + 1]), + acir_format::field_ct(aggregation_input[NUM_AGGREGATION_ELEMENTS * i + 2]), + acir_format::field_ct(aggregation_input[NUM_AGGREGATION_ELEMENTS * i + 3])); aggregation_elements[i].assert_is_in_field(); } // If we have a previous aggregation object, assign it to `previous_aggregation` so that it is included @@ -275,7 +277,8 @@ size_t verify_recursive_proof(uint8_t const* proof_buf, // Just write the output aggregation G1 elements, and no public inputs, proof witnesses, or any other data // as this should all be available elsewhere - const size_t output_size_bytes = 16 * sizeof(barretenberg::fr); + const size_t output_size_bytes = + acir_format::RecursionConstraint::AGGREGATION_OBJECT_SIZE * sizeof(barretenberg::fr); auto raw_buf = (uint8_t*)malloc(output_size_bytes); for (size_t i = 0; i < 4; ++i) { From 294c1fae20bfeed4e1d58250ad13e78009cedfba Mon Sep 17 00:00:00 2001 From: vezenovm Date: Tue, 23 May 2023 02:16:56 +0000 Subject: [PATCH 35/41] nit ASSERT(result != -1) in get_public_input_index --- cpp/src/barretenberg/plonk/composer/composer_base.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/src/barretenberg/plonk/composer/composer_base.hpp b/cpp/src/barretenberg/plonk/composer/composer_base.hpp index b1109d2d4d..cd34897239 100644 --- a/cpp/src/barretenberg/plonk/composer/composer_base.hpp +++ b/cpp/src/barretenberg/plonk/composer/composer_base.hpp @@ -197,7 +197,7 @@ class ComposerBase { break; } } - ASSERT(found == true); + ASSERT(result != -1); return result; } From 517ed2c77bac56f0dc324fcd1f43412aaab537b7 Mon Sep 17 00:00:00 2001 From: vezenovm Date: Tue, 23 May 2023 02:45:36 +0000 Subject: [PATCH 36/41] remove unused found var --- cpp/src/barretenberg/plonk/composer/composer_base.hpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/cpp/src/barretenberg/plonk/composer/composer_base.hpp b/cpp/src/barretenberg/plonk/composer/composer_base.hpp index cd34897239..3933b6dca0 100644 --- a/cpp/src/barretenberg/plonk/composer/composer_base.hpp +++ b/cpp/src/barretenberg/plonk/composer/composer_base.hpp @@ -188,11 +188,9 @@ class ComposerBase { uint32_t get_public_input_index(const uint32_t witness_index) const { - bool found = false; uint32_t result = static_cast(-1); for (size_t i = 0; i < public_inputs.size(); ++i) { if (real_variable_index[public_inputs[i]] == real_variable_index[witness_index]) { - found = true; result = static_cast(i); break; } From 92159e0b899e8bc23a9b57c8fc852b15c476f0dd Mon Sep 17 00:00:00 2001 From: vezenovm Date: Tue, 23 May 2023 03:22:29 +0000 Subject: [PATCH 37/41] cast -1 --- cpp/src/barretenberg/plonk/composer/composer_base.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/src/barretenberg/plonk/composer/composer_base.hpp b/cpp/src/barretenberg/plonk/composer/composer_base.hpp index 3933b6dca0..8c8a92656b 100644 --- a/cpp/src/barretenberg/plonk/composer/composer_base.hpp +++ b/cpp/src/barretenberg/plonk/composer/composer_base.hpp @@ -195,7 +195,7 @@ class ComposerBase { break; } } - ASSERT(result != -1); + ASSERT(result != static_cast(-1)); return result; } From d8ee38821e5c8fd0fdc99aeca9a6af4e40b3b3fa Mon Sep 17 00:00:00 2001 From: vezenovm Date: Tue, 23 May 2023 16:17:57 +0000 Subject: [PATCH 38/41] fix up verify_recursive_proof and add a test for it --- .../dsl/acir_proofs/acir_proofs.cpp | 17 +++-- .../dsl/acir_proofs/acir_proofs.test.cpp | 76 +++++++++++++++++++ 2 files changed, 86 insertions(+), 7 deletions(-) diff --git a/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.cpp b/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.cpp index 8ccd5d13b3..62dc6460d4 100644 --- a/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.cpp +++ b/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.cpp @@ -242,22 +242,25 @@ size_t verify_recursive_proof(uint8_t const* proof_buf, previous_aggregation.has_data = false; } + acir_format::Composer composer; + std::vector proof_fields(proof_length / 32); std::vector key_fields(vk_length / 32); for (size_t i = 0; i < proof_length / 32; i++) { - proof_fields[i] = acir_format::field_ct(barretenberg::fr::serialize_from_buffer(&proof_buf[i * 32])); + // TODO(maxim): The stdlib pairing primitives fetch the context from the elements being used in the pairing + // computation When attempting to simulate recursive verification without a full circuit where the context of + // these elements are not set we will get a seg fault. Using `witness_ct` here provides a workaround where these + // elements will have their context set. We should enable the native verifier to return an aggregation state so + // that we can avoid creating an unnecessary circuit and this workaround + proof_fields[i] = acir_format::field_ct( + acir_format::witness_ct(&composer, barretenberg::fr::serialize_from_buffer(&proof_buf[i * 32]))); } for (size_t i = 0; i < vk_length / 32; i++) { key_fields[i] = acir_format::field_ct(barretenberg::fr::serialize_from_buffer(&vk_buf[i * 32])); } - acir_format::Composer composer; - transcript::Manifest manifest = acir_format::Composer::create_unrolled_manifest(num_public_inputs); - // We currently only support RecursionConstraint where inner_proof_contains_recursive_proof = false. - // We would either need a separate ACIR opcode where inner_proof_contains_recursive_proof = true, - // or we need non-witness data to be provided as metadata in the ACIR opcode - // recursively verify the proof + std::array nested_aggregation_object = {}; for (size_t i = 6; i < 22; ++i) { nested_aggregation_object[i - 6] = uint32_t(key_fields[i].get_value()); diff --git a/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.test.cpp b/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.test.cpp index a5554d1b82..536d9a7342 100644 --- a/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.test.cpp +++ b/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.test.cpp @@ -141,6 +141,82 @@ TEST(AcirProofs, TestSerialization) EXPECT_EQ(verified, true); } +TEST(AcirProofs, TestVerifyRecursiveProofPass) +{ + uint8_t* proof_data_fields = nullptr; + uint8_t* vk_fields = nullptr; + uint8_t* vk_hash_buf = nullptr; + size_t proof_fields_size = 0; + size_t vk_fields_size = 0; + + acir_format::acir_format constraint_system; + std::vector witness; + create_inner_circuit(constraint_system, witness); + + std::vector witness_buf; + std::vector constraint_system_buf; + write(constraint_system_buf, constraint_system); + write(witness_buf, witness); + + uint32_t total_circuit_size = acir_proofs::get_total_circuit_size(&constraint_system_buf[0]); + uint32_t pow2_size = 1 << (numeric::get_msb(total_circuit_size) + 1); + auto env_crs = std::make_unique(); + auto verifier_crs = env_crs->get_verifier_crs(); + + uint8_t const* pk_buf = nullptr; + acir_proofs::init_proving_key(&constraint_system_buf[0], &pk_buf); + barretenberg::g2::affine_element g2x = verifier_crs->get_g2x(); + + auto* pippenger_ptr_base = new scalar_multiplication::Pippenger(env_load_prover_crs(pow2_size + 1), pow2_size + 1); + void* pippenger_ptr = reinterpret_cast(pippenger_ptr_base); + + std::vector g2x_buffer(128); + io::write_g2_elements_to_buffer(&g2x, (char*)(&g2x_buffer[0]), 1); + + uint8_t const* vk_buf = nullptr; + acir_proofs::init_verification_key(pippenger_ptr, &g2x_buffer[0], pk_buf, &vk_buf); + + uint8_t* proof_data_buf = nullptr; + size_t proof_length = acir_proofs::new_proof( + pippenger_ptr, &g2x_buffer[0], pk_buf, &constraint_system_buf[0], &witness_buf[0], &proof_data_buf, true); + + auto num_public_inputs = constraint_system.public_inputs.size(); + proof_fields_size = acir_proofs::serialize_proof_into_field_elements( + proof_data_buf, &proof_data_fields, proof_length, num_public_inputs); + vk_fields_size = + acir_proofs::serialize_verification_key_into_field_elements(&g2x_buffer[0], vk_buf, &vk_fields, &vk_hash_buf); + + bool verified = acir_proofs::verify_proof( + &g2x_buffer[0], vk_buf, &constraint_system_buf[0], proof_data_buf, static_cast(proof_length), true); + EXPECT_EQ(verified, true); + + delete pippenger_ptr_base; + free((void*)vk_buf); + free((void*)pk_buf); + free((void*)proof_data_buf); + + const size_t output_size_bytes = + acir_format::RecursionConstraint::AGGREGATION_OBJECT_SIZE * sizeof(barretenberg::fr); + auto input_agg_buf = (uint8_t*)malloc(output_size_bytes); + + for (size_t i = 0; i < 16; ++i) { + auto field = barretenberg::fr::zero(); + barretenberg::fr::serialize_to_buffer(field, &input_agg_buf[i * 32]); + } + + uint8_t* output_agg_buf = nullptr; + acir_proofs::verify_recursive_proof(proof_data_fields, + static_cast(proof_fields_size), + vk_fields, + static_cast(vk_fields_size), + static_cast(num_public_inputs), + input_agg_buf, + &output_agg_buf); + + free((void*)proof_data_fields); + free((void*)vk_fields); +} + struct RecursiveCircuitData { std::vector key_witnesses; std::vector proof_witnesses; From cf9bdd63315d82c97c5b8689efa495660814bf64 Mon Sep 17 00:00:00 2001 From: vezenovm Date: Tue, 23 May 2023 16:27:42 +0000 Subject: [PATCH 39/41] moved from tempalte for has_valid_witness_assignments to a flag --- cpp/src/barretenberg/dsl/acir_format/acir_format.cpp | 10 +++++----- .../dsl/acir_format/recursion_constraint.cpp | 12 +++++------- .../dsl/acir_format/recursion_constraint.hpp | 8 +++----- 3 files changed, 13 insertions(+), 17 deletions(-) diff --git a/cpp/src/barretenberg/dsl/acir_format/acir_format.cpp b/cpp/src/barretenberg/dsl/acir_format/acir_format.cpp index 97ce130665..f356af7d46 100644 --- a/cpp/src/barretenberg/dsl/acir_format/acir_format.cpp +++ b/cpp/src/barretenberg/dsl/acir_format/acir_format.cpp @@ -97,7 +97,7 @@ void create_circuit(Composer& composer, const acir_format& constraint_system) // Add recursion constraints for (size_t i = 0; i < constraint_system.recursion_constraints.size(); ++i) { auto& constraint = constraint_system.recursion_constraints[i]; - create_recursion_constraints(composer, constraint); + create_recursion_constraints(composer, constraint); // make sure the verification key records the public input indices of the final recursion output // (N.B. up to the ACIR description to make sure that the final output aggregation object wires are public @@ -201,7 +201,7 @@ Composer create_circuit(const acir_format& constraint_system, // Add recursion constraints for (size_t i = 0; i < constraint_system.recursion_constraints.size(); ++i) { auto& constraint = constraint_system.recursion_constraints[i]; - create_recursion_constraints(composer, constraint); + create_recursion_constraints(composer, constraint); // make sure the verification key records the public input indices of the final recursion output // (N.B. up to the ACIR description to make sure that the final output aggregation object wires are public @@ -310,7 +310,7 @@ Composer create_circuit_with_witness(const acir_format& constraint_system, // Add recursion constraints for (size_t i = 0; i < constraint_system.recursion_constraints.size(); ++i) { auto& constraint = constraint_system.recursion_constraints[i]; - create_recursion_constraints(composer, constraint); + create_recursion_constraints(composer, constraint, true); // make sure the verification key records the public input indices of the final recursion output // (N.B. up to the ACIR description to make sure that the final output aggregation object wires are public @@ -416,7 +416,7 @@ Composer create_circuit_with_witness(const acir_format& constraint_system, std:: // Add recursion constraints for (size_t i = 0; i < constraint_system.recursion_constraints.size(); ++i) { auto& constraint = constraint_system.recursion_constraints[i]; - create_recursion_constraints(composer, constraint); + create_recursion_constraints(composer, constraint, true); // make sure the verification key records the public input indices of the final recursion output // (N.B. up to the ACIR description to make sure that the final output aggregation object wires are public @@ -520,7 +520,7 @@ void create_circuit_with_witness(Composer& composer, const acir_format& constrai // Add recursion constraints for (size_t i = 0; i < constraint_system.recursion_constraints.size(); ++i) { auto& constraint = constraint_system.recursion_constraints[i]; - create_recursion_constraints(composer, constraint); + create_recursion_constraints(composer, constraint, true); // make sure the verification key records the public input indices of the final recursion output // (N.B. up to the ACIR description to make sure that the final output aggregation object wires are public diff --git a/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.cpp b/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.cpp index 564bfd9d68..32cb775cd3 100644 --- a/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.cpp +++ b/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.cpp @@ -28,8 +28,9 @@ void generate_dummy_proof() {} * We would either need a separate ACIR opcode where inner_proof_contains_recursive_proof = true, * or we need non-witness data to be provided as metadata in the ACIR opcode */ -template -void create_recursion_constraints(Composer& composer, const RecursionConstraint& input) +void create_recursion_constraints(Composer& composer, + const RecursionConstraint& input, + bool has_valid_witness_assignments) { const auto& nested_aggregation_indices = input.nested_aggregation_object; bool nested_aggregation_indices_all_zero = true; @@ -53,7 +54,7 @@ void create_recursion_constraints(Composer& composer, const RecursionConstraint& // we add our dummy proof values as Composer variables. // if we DO have a valid witness assignment, we use the real witness assignment barretenberg::fr dummy_field = - has_valid_witness_assignment ? composer.get_variable(proof_field_idx) : dummy_proof[i]; + has_valid_witness_assignments ? composer.get_variable(proof_field_idx) : dummy_proof[i]; // Create a copy constraint between our dummy field and the witness index provided by RecursionConstraint. // This will make the RecursionConstraint idx equal to `dummy_field`. // In the case of a valid witness assignment, this does nothing (as dummy_field = real value) @@ -64,7 +65,7 @@ void create_recursion_constraints(Composer& composer, const RecursionConstraint& for (size_t i = 0; i < input.key.size(); ++i) { const auto key_field_idx = input.key[i]; barretenberg::fr dummy_field = - has_valid_witness_assignment ? composer.get_variable(key_field_idx) : dummy_key[i]; + has_valid_witness_assignments ? composer.get_variable(key_field_idx) : dummy_key[i]; composer.assert_equal(composer.add_variable(dummy_field), key_field_idx); } } @@ -140,9 +141,6 @@ void create_recursion_constraints(Composer& composer, const RecursionConstraint& } } -template void create_recursion_constraints(Composer&, const RecursionConstraint&); -template void create_recursion_constraints(Composer&, const RecursionConstraint&); - /** * @brief When recursively verifying proofs, we represent the verification key using field elements. * This method exports the key formatted in the manner our recursive verifier expects. diff --git a/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.hpp b/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.hpp index 4d9b428166..f99dc21230 100644 --- a/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.hpp +++ b/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.hpp @@ -60,11 +60,9 @@ struct RecursionConstraint { friend bool operator==(RecursionConstraint const& lhs, RecursionConstraint const& rhs) = default; }; -template -void create_recursion_constraints(Composer& composer, const RecursionConstraint& input); - -extern template void create_recursion_constraints(Composer&, const RecursionConstraint&); -extern template void create_recursion_constraints(Composer&, const RecursionConstraint&); +void create_recursion_constraints(Composer& composer, + const RecursionConstraint& input, + bool has_valid_witness_assignments = false); std::vector export_key_in_recursion_format(std::shared_ptr const& vkey); std::vector export_dummy_key_in_recursion_format(const PolynomialManifest& polynomial_manifest, From 209667624f706be9106acab2cc0f7bfbdc7fa793 Mon Sep 17 00:00:00 2001 From: vezenovm Date: Tue, 23 May 2023 16:31:46 +0000 Subject: [PATCH 40/41] chore: add comments to AcirProofs.TestVerifyRecursiveProofPass --- cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.test.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.test.cpp b/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.test.cpp index 536d9a7342..fadbe87d42 100644 --- a/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.test.cpp +++ b/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.test.cpp @@ -141,6 +141,8 @@ TEST(AcirProofs, TestSerialization) EXPECT_EQ(verified, true); } +// Test that the method `verify_recursive_proof` can successfully simulate +// an output aggregation state TEST(AcirProofs, TestVerifyRecursiveProofPass) { uint8_t* proof_data_fields = nullptr; From 5f551ea7b55bfd3b5fb1f7c225b0b26bbf8ab2d9 Mon Sep 17 00:00:00 2001 From: zac-williamson Date: Fri, 26 May 2023 11:59:24 +0100 Subject: [PATCH 41/41] minor formatting changes to remove files from PR --- .../plonk/proof_system/verification_key/verification_key.hpp | 1 - .../barretenberg/solidity_helpers/circuits/recursive_circuit.hpp | 1 - 2 files changed, 2 deletions(-) diff --git a/cpp/src/barretenberg/plonk/proof_system/verification_key/verification_key.hpp b/cpp/src/barretenberg/plonk/proof_system/verification_key/verification_key.hpp index f7449fd963..0ee17d60e6 100644 --- a/cpp/src/barretenberg/plonk/proof_system/verification_key/verification_key.hpp +++ b/cpp/src/barretenberg/plonk/proof_system/verification_key/verification_key.hpp @@ -65,7 +65,6 @@ struct verification_key { const size_t num_inputs, std::shared_ptr const& crs, uint32_t composer_type); - verification_key(const verification_key& other); verification_key(verification_key&& other); verification_key& operator=(verification_key&& other); diff --git a/cpp/src/barretenberg/solidity_helpers/circuits/recursive_circuit.hpp b/cpp/src/barretenberg/solidity_helpers/circuits/recursive_circuit.hpp index 693418182f..7274600b84 100644 --- a/cpp/src/barretenberg/solidity_helpers/circuits/recursive_circuit.hpp +++ b/cpp/src/barretenberg/solidity_helpers/circuits/recursive_circuit.hpp @@ -110,7 +110,6 @@ template class RecursiveCircuit { OuterComposer outer_composer = OuterComposer(srs_path); create_inner_circuit_no_tables(inner_composer, inputs); - auto circuit_output = create_outer_circuit(inner_composer, outer_composer); g1::affine_element P[2];