diff --git a/barretenberg/cpp/bb-civc-inputs.tar.gz b/barretenberg/cpp/bb-civc-inputs.tar.gz deleted file mode 100644 index fc159645d6e3..000000000000 Binary files a/barretenberg/cpp/bb-civc-inputs.tar.gz and /dev/null differ diff --git a/barretenberg/cpp/src/barretenberg/client_ivc/client_ivc.cpp b/barretenberg/cpp/src/barretenberg/client_ivc/client_ivc.cpp index ef793b0b027f..76080b13ab41 100644 --- a/barretenberg/cpp/src/barretenberg/client_ivc/client_ivc.cpp +++ b/barretenberg/cpp/src/barretenberg/client_ivc/client_ivc.cpp @@ -9,6 +9,7 @@ #include "barretenberg/common/streams.hpp" #include "barretenberg/honk/proving_key_inspector.hpp" #include "barretenberg/serialize/msgpack_impl.hpp" +#include "barretenberg/special_public_inputs/special_public_inputs.hpp" #include "barretenberg/ultra_honk/oink_prover.hpp" namespace bb { @@ -202,6 +203,9 @@ void ClientIVC::complete_kernel_circuit_logic(ClientCircuit& circuit) // .., A_{i, n} (app) auto accumulation_recursive_transcript = std::make_shared(); + // Commitment to the previous state of the op_queue in the recursive verification + TableCommitments T_prev_commitments; + // Instantiate stdlib verifier inputs from their native counterparts if (stdlib_verification_queue.empty()) { instantiate_stdlib_verification_queue(circuit); @@ -219,6 +223,9 @@ void ClientIVC::complete_kernel_circuit_logic(ClientCircuit& circuit) // we can use `batch_mul` here to decrease the size of the `ECCOpQueue`, but must be cautious with FS security. points_accumulator.aggregate(pairing_points); + // Update commitment to the status of the op_queue + T_prev_commitments = merged_table_commitments; + stdlib_verification_queue.pop_front(); is_hiding_kernel = (verifier_input.type == QUEUE_TYPE::PG_FINAL); @@ -226,7 +233,7 @@ void ClientIVC::complete_kernel_circuit_logic(ClientCircuit& circuit) // Set the kernel output data to be propagated via the public inputs if (is_hiding_kernel) { - HidingKernelIO hiding_output{ points_accumulator }; + HidingKernelIO hiding_output{ points_accumulator, T_prev_commitments }; hiding_output.set_public(); } else { KernelIO kernel_output; @@ -437,7 +444,7 @@ std::shared_ptr ClientIVC::construct_hiding_circ complete_hiding_circuit_logic(stdlib_proof, stdlib_vk_and_hash, builder); fold_output.accumulator = nullptr; - HidingKernelIO hiding_output{ pairing_points }; + HidingKernelIO hiding_output{ pairing_points, merged_table_commitments }; hiding_output.set_public(); auto decider_pk = std::make_shared(builder, TraceSettings(), bn254_commitment_key); @@ -487,7 +494,7 @@ bool ClientIVC::verify(const Proof& proof, const VerificationKey& vk) std::shared_ptr civc_verifier_transcript = std::make_shared(); // Verify the hiding circuit proof MegaZKVerifier verifier{ vk.mega, /*ipa_verification_key=*/{}, civc_verifier_transcript }; - auto [mega_verified, _] = verifier.verify_proof(proof.mega_proof); + auto [mega_verified, T_prev_commitments] = verifier.verify_proof(proof.mega_proof); vinfo("Mega verified: ", mega_verified); // Extract the commitments to the subtable corresponding to the incoming circuit diff --git a/barretenberg/cpp/src/barretenberg/client_ivc/client_ivc.hpp b/barretenberg/cpp/src/barretenberg/client_ivc/client_ivc.hpp index 4560f5e1beac..5cf7a3ad7be9 100644 --- a/barretenberg/cpp/src/barretenberg/client_ivc/client_ivc.hpp +++ b/barretenberg/cpp/src/barretenberg/client_ivc/client_ivc.hpp @@ -75,7 +75,7 @@ class ClientIVC { using PairingPoints = stdlib::recursion::PairingPoints; using PublicPairingPoints = stdlib::PublicInputComponent; using KernelIO = bb::stdlib::recursion::honk::KernelIO; - using HidingKernelIO = bb::stdlib::recursion::honk::HidingKernelIO; + using HidingKernelIO = bb::stdlib::recursion::honk::HidingKernelIO; using AppIO = bb::stdlib::recursion::honk::AppIO; using StdlibProof = stdlib::Proof; using StdlibFF = RecursiveFlavor::FF; diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.cpp index 95392f8640e1..5ca330ecc3c3 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.cpp @@ -271,7 +271,7 @@ void build_constraints(Builder& builder, AcirProgram& program, const ProgramMeta // If its an app circuit that has no recursion constraints, add default pairing points to public inputs. if (constraint_system.honk_recursion_constraints.empty() && constraint_system.ivc_recursion_constraints.empty()) { - PairingPoints::add_default_to_public_inputs(builder); + stdlib::recursion::honk::AppIO::add_default(builder); } } else { HonkRecursionConstraintsOutput honk_output = diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/honk_recursion_constraint.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/honk_recursion_constraint.cpp index 8fcc98eacc7d..72654ad1c685 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/honk_recursion_constraint.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/honk_recursion_constraint.cpp @@ -18,6 +18,7 @@ #include "barretenberg/stdlib/primitives/circuit_builders/circuit_builders_fwd.hpp" #include "barretenberg/stdlib/primitives/curves/bn254.hpp" #include "barretenberg/stdlib/proof/proof.hpp" +#include "barretenberg/stdlib/special_public_inputs/special_public_inputs.hpp" #include "proof_surgeon.hpp" #include "recursion_constraint.hpp" @@ -215,7 +216,7 @@ void create_dummy_vkey_and_proof(typename Flavor::CircuitBuilder& builder, template HonkRecursionConstraintOutput create_honk_recursion_constraints( typename Flavor::CircuitBuilder& builder, const RecursionConstraint& input, bool has_valid_witness_assignments) - requires IsRecursiveFlavor + requires(IsRecursiveFlavor && IsUltraHonk) { using Builder = typename Flavor::CircuitBuilder; using RecursiveVerificationKey = Flavor::VerificationKey; @@ -253,13 +254,14 @@ HonkRecursionConstraintOutput create_honk_recur if (!has_valid_witness_assignments) { // In the constraint, the agg object public inputs are still contained in the proof. To get the 'raw' size of // the proof and public_inputs we subtract and add the corresponding amount from the respective sizes. - size_t size_of_proof_with_no_pub_inputs = input.proof.size() - bb::PAIRING_POINTS_SIZE; - if constexpr (HasIPAAccumulator) { - size_of_proof_with_no_pub_inputs -= bb::IPA_CLAIM_SIZE; - } - size_t total_num_public_inputs = input.public_inputs.size() + bb::PAIRING_POINTS_SIZE; + size_t size_of_proof_with_no_pub_inputs = input.proof.size(); + size_t total_num_public_inputs = input.public_inputs.size(); if constexpr (HasIPAAccumulator) { - total_num_public_inputs += bb::IPA_CLAIM_SIZE; + size_of_proof_with_no_pub_inputs -= RollupIO::PUBLIC_INPUTS_SIZE; + total_num_public_inputs += RollupIO::PUBLIC_INPUTS_SIZE; + } else { + size_of_proof_with_no_pub_inputs -= DefaultIO::PUBLIC_INPUTS_SIZE; + total_num_public_inputs += DefaultIO::PUBLIC_INPUTS_SIZE; } create_dummy_vkey_and_proof( diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/honk_recursion_constraint.hpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/honk_recursion_constraint.hpp index 8d172b524132..f71b6fa8d3d1 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/honk_recursion_constraint.hpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/honk_recursion_constraint.hpp @@ -27,6 +27,6 @@ template create_honk_recursion_constraints(typename Flavor::CircuitBuilder& builder, const RecursionConstraint& input, bool has_valid_witness_assignments = false) - requires IsRecursiveFlavor; + requires(IsRecursiveFlavor && IsUltraHonk); } // namespace acir_format diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/honk_recursion_constraint.test.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/honk_recursion_constraint.test.cpp index 93d40afdee5d..3ff63081ce98 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/honk_recursion_constraint.test.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/honk_recursion_constraint.test.cpp @@ -1,6 +1,7 @@ #include "honk_recursion_constraint.hpp" #include "acir_format.hpp" #include "acir_format_mocks.hpp" +#include "barretenberg/special_public_inputs/special_public_inputs.hpp" #include "barretenberg/ultra_honk/decider_proving_key.hpp" #include "barretenberg/ultra_honk/ultra_prover.hpp" #include "barretenberg/ultra_honk/ultra_verifier.hpp" @@ -173,14 +174,19 @@ template class AcirHonkRecursionConstraint : public : std::vector key_witnesses = verification_key->to_field_elements(); fr key_hash_witness = verification_key->hash(); std::vector proof_witnesses = inner_proof; - size_t num_public_inputs_to_extract = inner_circuit.num_public_inputs() - bb::PAIRING_POINTS_SIZE; - acir_format::PROOF_TYPE proof_type = acir_format::HONK; - if constexpr (HasIPAAccumulator) { - num_public_inputs_to_extract -= IPA_CLAIM_SIZE; - proof_type = ROLLUP_HONK; - } else if constexpr (InnerFlavor::HasZK) { - proof_type = HONK_ZK; - } + + // Compute the number of public inputs to extract (the ones from the circuit) and the proof type based on + // the Flavor + auto [num_public_inputs_to_extract, proof_type] = [&]() -> std::pair { + size_t num_public_inputs_to_extract = inner_circuit.num_public_inputs(); + if constexpr (HasIPAAccumulator) { + return { num_public_inputs_to_extract - RollupIO::PUBLIC_INPUTS_SIZE, ROLLUP_HONK }; + } else if constexpr (InnerFlavor::HasZK) { + return { num_public_inputs_to_extract - DefaultIO::PUBLIC_INPUTS_SIZE, HONK_ZK }; + } else { + return { num_public_inputs_to_extract - DefaultIO::PUBLIC_INPUTS_SIZE, HONK }; + } + }(); auto [key_indices, key_hash_index, proof_indices, inner_public_inputs] = ProofSurgeon::populate_recursion_witness_data( @@ -216,6 +222,16 @@ template class AcirHonkRecursionConstraint : public : AcirProgram program{ constraint_system, witness }; BuilderType outer_circuit = create_circuit(program, metadata); + // If BuilderType = Mega, then the proof of outer_circuit will be a Mega proof, and thus will be verified with + // a MegaVerifier = UltraVerifier. By design, the MegaVerifier expects the public inputs to be the + // ones of the HidingKernel, so we mock the missing part: ecc_op_tables + if constexpr (IsMegaBuilder) { + for (auto& commitment : + stdlib::recursion::honk::HidingKernelIO::default_ecc_op_tables(outer_circuit)) { + commitment.set_public(); + } + } + return outer_circuit; } @@ -223,7 +239,10 @@ template class AcirHonkRecursionConstraint : public : const std::shared_ptr& verification_key, const HonkProof& proof) { - if constexpr (IsUltraHonk) { + if constexpr (IsMegaFlavor) { + OuterVerifier verifier(verification_key); + return std::get<0>(verifier.verify_proof(proof)); + } else { if constexpr (HasIPAAccumulator) { VerifierCommitmentKey ipa_verification_key(1 << CONST_ECCVM_LOG_N); OuterVerifier verifier(verification_key, ipa_verification_key); @@ -232,9 +251,6 @@ template class AcirHonkRecursionConstraint : public : OuterVerifier verifier(verification_key); return verifier.verify_proof(proof); } - } else { - OuterVerifier verifier(verification_key); - return std::get<0>(verifier.verify_proof(proof)); } } diff --git a/barretenberg/cpp/src/barretenberg/ecc/curves/bn254/fq.hpp b/barretenberg/cpp/src/barretenberg/ecc/curves/bn254/fq.hpp index 45fdf9a46672..cdd28a5d90db 100644 --- a/barretenberg/cpp/src/barretenberg/ecc/curves/bn254/fq.hpp +++ b/barretenberg/cpp/src/barretenberg/ecc/curves/bn254/fq.hpp @@ -16,6 +16,8 @@ // NOLINTBEGIN(cppcoreguidelines-avoid-c-arrays) namespace bb { +// TODO(https://github.com/AztecProtocol/barretenberg/issues/1478): Can we define this constant as part of the +// parameters and make it avaiable via the interface of field? // A point in Fq is represented with 4 public inputs static constexpr size_t FQ_PUBLIC_INPUT_SIZE = 4; diff --git a/barretenberg/cpp/src/barretenberg/goblin/mock_circuits.hpp b/barretenberg/cpp/src/barretenberg/goblin/mock_circuits.hpp index db7ca5372f0f..d24fd0ca2924 100644 --- a/barretenberg/cpp/src/barretenberg/goblin/mock_circuits.hpp +++ b/barretenberg/cpp/src/barretenberg/goblin/mock_circuits.hpp @@ -19,6 +19,7 @@ #include "barretenberg/stdlib/honk_verifier/ultra_recursive_verifier.hpp" #include "barretenberg/stdlib/primitives/curves/secp256k1.hpp" #include "barretenberg/stdlib/protogalaxy_verifier/protogalaxy_recursive_verifier.hpp" +#include "barretenberg/stdlib/special_public_inputs/special_public_inputs.hpp" #include "barretenberg/stdlib_circuit_builders/mock_circuits.hpp" namespace bb { @@ -69,7 +70,6 @@ class GoblinMockCircuits { using RecursiveVerifierAccumulator = std::shared_ptr; using VerificationKey = Flavor::VerificationKey; - using PairingPoints = stdlib::recursion::PairingPoints; static constexpr size_t NUM_WIRES = Flavor::NUM_WIRES; struct KernelInput { @@ -104,7 +104,7 @@ class GoblinMockCircuits { // MegaHonk circuits (where we don't explicitly need to add goblin ops), in IVC merge proving happens prior to // folding where the absense of goblin ecc ops will result in zero commitments. MockCircuits::construct_goblin_ecc_op_circuit(builder); - PairingPoints::add_default_to_public_inputs(builder); + bb::stdlib::recursion::honk::AppIO::add_default(builder); } /** @@ -142,7 +142,9 @@ class GoblinMockCircuits { add_some_ecc_op_gates(builder); MockCircuits::construct_arithmetic_circuit(builder); - PairingPoints::add_default_to_public_inputs(builder); + // Flavor = bb::MegaFlavor, so the public inputs should be that of the HidingKernelIO (UltraVerifier + // expects the public inputs to be that of the HidingKernel) + bb::stdlib::recursion::honk::HidingKernelIO::add_default(builder); } /** diff --git a/barretenberg/cpp/src/barretenberg/goblin/mock_circuits_pinning.test.cpp b/barretenberg/cpp/src/barretenberg/goblin/mock_circuits_pinning.test.cpp index 01db50a6faca..72036b8a5d0f 100644 --- a/barretenberg/cpp/src/barretenberg/goblin/mock_circuits_pinning.test.cpp +++ b/barretenberg/cpp/src/barretenberg/goblin/mock_circuits_pinning.test.cpp @@ -42,7 +42,7 @@ TEST_F(MegaMockCircuitsPinning, SmallTestStructuredCircuitSize) { Goblin goblin; MegaCircuitBuilder app_circuit{ goblin.op_queue }; - GoblinMockCircuits::PairingPoints::add_default_to_public_inputs(app_circuit); + stdlib::recursion::honk::DefaultIO::add_default(app_circuit); TraceSettings trace_settings{ SMALL_TEST_STRUCTURE }; auto proving_key = std::make_shared(app_circuit, trace_settings); EXPECT_EQ(proving_key->log_dyadic_size(), 18); @@ -52,7 +52,7 @@ TEST_F(MegaMockCircuitsPinning, AztecStructuredCircuitSize) { Goblin goblin; MegaCircuitBuilder app_circuit{ goblin.op_queue }; - GoblinMockCircuits::PairingPoints::add_default_to_public_inputs(app_circuit); + stdlib::recursion::honk::DefaultIO::add_default(app_circuit); TraceSettings trace_settings{ AZTEC_TRACE_STRUCTURE }; auto proving_key = std::make_shared(app_circuit, trace_settings); EXPECT_EQ(proving_key->log_dyadic_size(), 18); diff --git a/barretenberg/cpp/src/barretenberg/honk/execution_trace/mega_execution_trace.hpp b/barretenberg/cpp/src/barretenberg/honk/execution_trace/mega_execution_trace.hpp index 1b7ff4cf2631..22eb29106845 100644 --- a/barretenberg/cpp/src/barretenberg/honk/execution_trace/mega_execution_trace.hpp +++ b/barretenberg/cpp/src/barretenberg/honk/execution_trace/mega_execution_trace.hpp @@ -255,7 +255,7 @@ class MegaExecutionTraceBlocks : public MegaTraceBlockData { static constexpr TraceStructure TINY_TEST_STRUCTURE{ .ecc_op = 18, .busread = 3, .lookup = 2, - .pub_inputs = 20, + .pub_inputs = 52, // Accomodate 4 + HidingKernelIO = 4 + 48 .arithmetic = 1 << 14, .delta_range = 5, .elliptic = 2, diff --git a/barretenberg/cpp/src/barretenberg/special_public_inputs/special_public_inputs.hpp b/barretenberg/cpp/src/barretenberg/special_public_inputs/special_public_inputs.hpp index 5b75269402f4..c5a8dd4a8394 100644 --- a/barretenberg/cpp/src/barretenberg/special_public_inputs/special_public_inputs.hpp +++ b/barretenberg/cpp/src/barretenberg/special_public_inputs/special_public_inputs.hpp @@ -41,6 +41,50 @@ class DefaultIO { } }; +/** + * @brief Manages the data that is propagated on the public inputs of of a hiding kernel circuit + */ +class HidingKernelIO { + public: + using FF = curve::BN254::ScalarField; + using G1 = curve::BN254::AffineElement; + + // Number of columns that jointly constitute the op_queue, should be the same as the number of wires in the + // MegaCircuitBuilder + static constexpr size_t NUM_WIRES = 4; + + // Number of bb::fr field elements used to represent a goblin element in the public inputs + // The element is of goblin type because the HidingKernel is always employed with a MegaBuilder + static constexpr size_t G1_PUBLIC_INPUTS_SIZE = FQ_PUBLIC_INPUT_SIZE * 2; + static constexpr size_t PUBLIC_INPUTS_SIZE = NUM_WIRES * G1_PUBLIC_INPUTS_SIZE + PAIRING_POINTS_SIZE; + + PairingPoints pairing_inputs; + std::array ecc_op_tables; + + /** + * @brief Reconstructs the IO components from a public inputs array. + * + * @param public_inputs Public inputs array containing the serialized kernel public inputs. + */ + void reconstruct_from_public(const std::vector& public_inputs) + { + // Assumes that the hiding-kernel-io public inputs are at the end of the public_inputs vector + uint32_t index = static_cast(public_inputs.size() - PUBLIC_INPUTS_SIZE); + + const std::span pairing_inputs_limbs(public_inputs.data() + index, + PAIRING_POINTS_SIZE); + index += PAIRING_POINTS_SIZE; + pairing_inputs = PairingPoints::reconstruct_from_public(pairing_inputs_limbs); + + for (auto& commitment : ecc_op_tables) { + const std::span ecc_op_table_limbs(public_inputs.data() + index, + G1_PUBLIC_INPUTS_SIZE); + commitment = G1::reconstruct_from_public(ecc_op_table_limbs); + index += G1_PUBLIC_INPUTS_SIZE; + } + } +}; + /** * @brief The data that is propagated on the public inputs of a rollup circuit */ diff --git a/barretenberg/cpp/src/barretenberg/stdlib/honk_verifier/ultra_recursive_verifier.cpp b/barretenberg/cpp/src/barretenberg/stdlib/honk_verifier/ultra_recursive_verifier.cpp index bb685a15dd31..a0617e73b7c3 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/honk_verifier/ultra_recursive_verifier.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/honk_verifier/ultra_recursive_verifier.cpp @@ -90,10 +90,10 @@ UltraRecursiveVerifier_::Output UltraRecursiveVerifier_::verify_ output.points_accumulator = inputs.pairing_inputs; output.ipa_claim = inputs.ipa_claim; } else if constexpr (IsMegaFlavor) { - DefaultIO inputs; // will be HidingKernelIO + HidingKernelIO inputs; inputs.reconstruct_from_public(public_inputs); output.points_accumulator = inputs.pairing_inputs; - // output.ecc_op_tables = inputs.ecc_op_tables; + output.ecc_op_tables = inputs.ecc_op_tables; } else { DefaultIO inputs; // pairing points inputs.reconstruct_from_public(public_inputs); diff --git a/barretenberg/cpp/src/barretenberg/stdlib/honk_verifier/ultra_recursive_verifier.test.cpp b/barretenberg/cpp/src/barretenberg/stdlib/honk_verifier/ultra_recursive_verifier.test.cpp index 7e23f793c4c9..0468e552938c 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/honk_verifier/ultra_recursive_verifier.test.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/honk_verifier/ultra_recursive_verifier.test.cpp @@ -3,6 +3,7 @@ #include "barretenberg/common/test.hpp" #include "barretenberg/flavor/flavor.hpp" #include "barretenberg/flavor/ultra_rollup_recursive_flavor.hpp" +#include "barretenberg/stdlib/special_public_inputs/special_public_inputs.hpp" #include "barretenberg/stdlib/test_utils/tamper_proof.hpp" #include "barretenberg/ultra_honk/ultra_prover.hpp" #include "barretenberg/ultra_honk/ultra_verifier.hpp" @@ -84,14 +85,14 @@ template class RecursiveVerifierTest : public testing builder.create_big_add_gate({ a_idx, b_idx, c_idx, d_idx, fr(1), fr(1), fr(1), fr(-1), fr(0) }); } - PairingPoints::add_default_to_public_inputs(builder); - - if constexpr (HasIPAAccumulator) { - auto [stdlib_opening_claim, ipa_proof] = - IPA>::create_fake_ipa_claim_and_proof(builder); - stdlib_opening_claim.set_public(); - builder.ipa_proof = ipa_proof; + if constexpr (IsMegaBuilder) { + HidingKernelIO::add_default(builder); + } else if constexpr (HasIPAAccumulator) { + RollupIO::add_default(builder); + } else { + DefaultIO::add_default(builder); } + return builder; } @@ -211,10 +212,26 @@ template class RecursiveVerifierTest : public testing verifier.transcript->enable_manifest(); VerifierOutput output = verifier.verify_proof(inner_proof); - output.points_accumulator.set_public(); - if constexpr (HasIPAAccumulator) { - output.ipa_claim.set_public(); + + // IO + if constexpr (IsMegaFlavor) { + HidingKernelIO inputs; + inputs.pairing_inputs = output.points_accumulator; + inputs.ecc_op_tables = HidingKernelIO::default_ecc_op_tables(outer_circuit); + inputs.set_public(); + } else if constexpr (HasIPAAccumulator) { + // HasIPAAccumulator requires RollUpIO + RollupIO inputs; + inputs.pairing_inputs = output.points_accumulator; + inputs.ipa_claim = output.ipa_claim; + inputs.set_public(); + + // Store ipa_proof outer_circuit.ipa_proof = output.ipa_proof.get_value(); + } else { + DefaultIO inputs; + inputs.pairing_inputs = output.points_accumulator; + inputs.set_public(); } // Check for a failure flag in the recursive verifier circuit @@ -261,23 +278,21 @@ template class RecursiveVerifierTest : public testing info("Recursive Verifier: num gates = ", outer_circuit.get_num_finalized_gates()); OuterProver prover(proving_key, verification_key); auto proof = prover.construct_proof(); - if constexpr (IsUltraHonk) { - if constexpr (HasIPAAccumulator) { - VerifierCommitmentKey ipa_verification_key = (1 << CONST_ECCVM_LOG_N); - OuterVerifier verifier(verification_key, ipa_verification_key); - ASSERT_TRUE(verifier.verify_proof(proof, proving_key->ipa_proof)); - } else { - OuterVerifier verifier(verification_key); - ASSERT_TRUE(verifier.verify_proof(proof)); - } - } else { + if constexpr (IsMegaFlavor) { OuterVerifier verifier(verification_key); ASSERT_TRUE(std::get<0>(verifier.verify_proof(proof))); + } else if constexpr (HasIPAAccumulator) { + VerifierCommitmentKey ipa_verification_key = (1 << CONST_ECCVM_LOG_N); + OuterVerifier verifier(verification_key, ipa_verification_key); + ASSERT_TRUE(verifier.verify_proof(proof, proving_key->ipa_proof)); + } else { + OuterVerifier verifier(verification_key); + ASSERT_TRUE(verifier.verify_proof(proof)); } } // Check the size of the recursive verifier if constexpr (std::same_as>) { - uint32_t NUM_GATES_EXPECTED = 873673; + uint32_t NUM_GATES_EXPECTED = 874803; ASSERT_EQ(static_cast(outer_circuit.get_num_finalized_gates()), NUM_GATES_EXPECTED) << "MegaZKHonk Recursive verifier changed in Ultra gate count! Update this value if you " "are sure this is expected."; diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/biggroup/biggroup.hpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/biggroup/biggroup.hpp index 9acda8a1bc9f..631e6e4842c4 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/biggroup/biggroup.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/biggroup/biggroup.hpp @@ -21,8 +21,9 @@ namespace bb::stdlib::element_default { // ( ͡° ͜ʖ ͡°) -template class element { +template class element { public: + using Builder = Builder_; using bool_ct = stdlib::bool_t; using biggroup_tag = element; // Facilitates a constexpr check IsBigGroup using BaseField = Fq; @@ -163,6 +164,17 @@ template class element { return element(x_fq, y_fq); } + static element point_at_infinity(Builder* ctx) + { + Fr zero = Fr::from_witness_index(ctx, ctx->zero_idx); + zero.unset_free_witness_tag(); + Fq x_fq(zero, zero); + Fq y_fq(zero, zero); + element result(x_fq, y_fq); + result.set_point_at_infinity(true); + return result; + } + element& operator=(const element& other); element& operator=(element&& other) noexcept; diff --git a/barretenberg/cpp/src/barretenberg/stdlib/protogalaxy_verifier/protogalaxy_recursive_verifier.test.cpp b/barretenberg/cpp/src/barretenberg/stdlib/protogalaxy_verifier/protogalaxy_recursive_verifier.test.cpp index 96271917f976..f920dcaf0c4d 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/protogalaxy_verifier/protogalaxy_recursive_verifier.test.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/protogalaxy_verifier/protogalaxy_recursive_verifier.test.cpp @@ -6,6 +6,7 @@ #include "barretenberg/stdlib/hash/blake3s/blake3s.hpp" #include "barretenberg/stdlib/hash/pedersen/pedersen.hpp" #include "barretenberg/stdlib/honk_verifier/decider_recursive_verifier.hpp" +#include "barretenberg/stdlib/special_public_inputs/special_public_inputs.hpp" #include "barretenberg/ultra_honk/decider_prover.hpp" #include "barretenberg/ultra_honk/ultra_prover.hpp" #include "barretenberg/ultra_honk/ultra_verifier.hpp" @@ -106,7 +107,8 @@ class ProtogalaxyRecursiveTests : public testing::Test { big_a* big_b; - stdlib::recursion::PairingPoints::add_default_to_public_inputs(builder); + // Must be HidingKernelIO as UltraVerifier expects the public inputs set by HidingKernelIO + stdlib::recursion::honk::HidingKernelIO::add_default(builder); }; static std::tuple, std::shared_ptr> @@ -257,7 +259,8 @@ class ProtogalaxyRecursiveTests : public testing::Test { // Check for a failure flag in the recursive verifier circuit { - stdlib::recursion::PairingPoints::add_default_to_public_inputs(folding_circuit); + // Must be HidingKernelIO as UltraVerifier expects the public inputs set by HidingKernelIO + stdlib::recursion::honk::HidingKernelIO::add_default(folding_circuit); // inefficiently check finalized size folding_circuit.finalize_circuit(/* ensure_nonzero= */ true); info("Folding Recursive Verifier: num gates finalized = ", folding_circuit.num_gates); @@ -345,7 +348,13 @@ class ProtogalaxyRecursiveTests : public testing::Test { OuterBuilder decider_circuit; DeciderRecursiveVerifier decider_verifier{ &decider_circuit, native_verifier_acc }; auto pairing_points = decider_verifier.verify_proof(decider_proof); - pairing_points.set_public(); + + // IO + HidingKernelIO inputs; + inputs.pairing_inputs = pairing_points; + inputs.ecc_op_tables = HidingKernelIO::default_ecc_op_tables(decider_circuit); + inputs.set_public(); + info("Decider Recursive Verifier: num gates = ", decider_circuit.num_gates); // Check for a failure flag in the recursive verifier circuit EXPECT_EQ(decider_circuit.failed(), false) << decider_circuit.err(); diff --git a/barretenberg/cpp/src/barretenberg/stdlib/special_public_inputs/special_public_inputs.hpp b/barretenberg/cpp/src/barretenberg/stdlib/special_public_inputs/special_public_inputs.hpp index fc23b615284e..13c306ec9610 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/special_public_inputs/special_public_inputs.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/special_public_inputs/special_public_inputs.hpp @@ -6,6 +6,7 @@ #pragma once +#include "barretenberg/commitment_schemes/ipa/ipa.hpp" #include "barretenberg/commitment_schemes/pairing_points.hpp" #include "barretenberg/stdlib/pairing_points.hpp" #include "barretenberg/stdlib/primitives/circuit_builders/circuit_builders_fwd.hpp" @@ -13,6 +14,13 @@ #include "barretenberg/stdlib/primitives/public_input_component/public_input_component.hpp" namespace bb::stdlib::recursion::honk { +// Default coordinates of commitment to an ecc op table +// These are the coordinates that come from committing to the ecc ops that are added to the op_queue by finalize_circuit +static constexpr bb::fq DEFAULT_ECC_COMMITMENT_X("0x08434fa4480433735e7aeaccecb911eb7a06165ad70e5ced6ac6848296e59279"); +static constexpr bb::fq DEFAULT_ECC_COMMITMENT_Y("0x0a13a1839ab95ef15be8d0710b2c8aa47cea0b0e62a8596e68cc0fd54a6ae73d"); +static constexpr bb::curve::BN254::AffineElement DEFAULT_ECC_COMMITMENT(DEFAULT_ECC_COMMITMENT_X, + DEFAULT_ECC_COMMITMENT_Y); + /** * @brief Manages the data that is propagated on the public inputs of a kernel circuit * @@ -118,6 +126,12 @@ template class DefaultIO { Builder* builder = pairing_inputs.P0.get_context(); builder->finalize_public_inputs(); } + + /** + * @brief Add default public inputs when they are not present + * + */ + static void add_default(Builder& builder) { PairingInputs::add_default_to_public_inputs(builder); }; }; /** @@ -126,9 +140,93 @@ template class DefaultIO { using AppIO = DefaultIO; // app IO is always Mega /** - * @brief The data that is propagated on the public inputs of a hiding kernel circuit + * @brief Manages the data that is propagated on the public inputs of a hiding kernel circuit */ -using HidingKernelIO = DefaultIO; // hiding kernel IO is always Mega +template class HidingKernelIO { + public: + using Builder = Builder_; + using Curve = stdlib::bn254; // curve is always bn254 + using G1 = Curve::Group; + using FF = Curve::ScalarField; + using PairingInputs = stdlib::recursion::PairingPoints; + + using PublicPoint = stdlib::PublicInputComponent; + using PublicPairingPoints = stdlib::PublicInputComponent; + + PairingInputs pairing_inputs; // Inputs {P0, P1} to an EC pairing check + std::array + ecc_op_tables; // commitments to merged tables obtained from final Merge verification + + // Total size of the IO public inputs + static constexpr size_t PUBLIC_INPUTS_SIZE = + PairingInputs::PUBLIC_INPUTS_SIZE + Builder::NUM_WIRES * G1::PUBLIC_INPUTS_SIZE; + + /** + * @brief Reconstructs the IO components from a public inputs array. + * + * @param public_inputs Public inputs array containing the serialized kernel public inputs. + */ + void reconstruct_from_public(const std::vector& public_inputs) + { + // Assumes that the app-io public inputs are at the end of the public_inputs vector + uint32_t index = static_cast(public_inputs.size() - PUBLIC_INPUTS_SIZE); + pairing_inputs = PublicPairingPoints::reconstruct(public_inputs, PublicComponentKey{ index }); + index += PairingInputs::PUBLIC_INPUTS_SIZE; + for (auto& commitment : ecc_op_tables) { + commitment = PublicPoint::reconstruct(public_inputs, PublicComponentKey{ index }); + index += G1::PUBLIC_INPUTS_SIZE; + } + } + + /** + * @brief Set each IO component to be a public input of the underlying circuit. + * + */ + void set_public() + { + pairing_inputs.set_public(); + for (auto& commitment : ecc_op_tables) { + commitment.set_public(); + } + + // Finalize the public inputs to ensure no more public inputs can be added hereafter. + Builder* builder = pairing_inputs.P0.get_context(); + builder->finalize_public_inputs(); + } + + static std::array empty_ecc_op_tables(Builder& builder) + { + std::array empty_tables; + for (auto& table_commitment : empty_tables) { + table_commitment = G1::point_at_infinity(&builder); + } + + return empty_tables; + } + + static std::array default_ecc_op_tables(Builder& builder) + { + std::array default_tables; + for (auto& table_commitment : default_tables) { + table_commitment = G1(DEFAULT_ECC_COMMITMENT); + table_commitment.convert_constant_to_fixed_witness(&builder); + } + + return default_tables; + } + + /** + * @brief Add default public inputs when they are not present + * + */ + static void add_default(Builder& builder) + { + PairingInputs::add_default_to_public_inputs(builder); + for (auto& table_commitment : default_ecc_op_tables(builder)) { + table_commitment.set_public(); + } + }; +}; /** * @brief The data that is propagated on the public inputs of a rollup circuit @@ -176,6 +274,18 @@ class RollupIO { Builder* builder = pairing_inputs.P0.get_context(); builder->finalize_public_inputs(); } + + /** + * @brief Add default public inputs when they are not present + * + */ + static void add_default(Builder& builder) + { + PairingInputs::add_default_to_public_inputs(builder); + auto [stdlib_opening_claim, ipa_proof] = IPA>::create_fake_ipa_claim_and_proof(builder); + stdlib_opening_claim.set_public(); + builder.ipa_proof = ipa_proof; + }; }; } // namespace bb::stdlib::recursion::honk diff --git a/barretenberg/cpp/src/barretenberg/stdlib/special_public_inputs/special_public_inputs.test.cpp b/barretenberg/cpp/src/barretenberg/stdlib/special_public_inputs/special_public_inputs.test.cpp index 6ca90e0c35fb..1ef8af5f68fd 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/special_public_inputs/special_public_inputs.test.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/special_public_inputs/special_public_inputs.test.cpp @@ -227,4 +227,97 @@ TEST_F(SpecialPublicInputsTests, RollUpIO) } } +// Demonstrates the basic functionality of the HidingKernelIO class for propagating public inputs between circuits +TEST_F(SpecialPublicInputsTests, HidingKernel) +{ + using Builder = MegaCircuitBuilder; + + // IO classes + using HidingIO = HidingKernelIO; + using HidingIONative = bb::HidingKernelIO; + + // Recursive types + using Curve = HidingIO::Curve; + using G1 = HidingIO::G1; + using FF = HidingIO::FF; + using PairingInputs = HidingIO::PairingInputs; + + // Native types + using G1Native = Curve::GroupNative::affine_element; + using FFNative = Curve::ScalarFieldNative; + + static constexpr size_t NUM_WIRES = Builder::NUM_WIRES; + + G1Native P0_val = G1Native::random_element(); + G1Native P1_val = G1Native::random_element(); + std::array ecc_op_tables_val; + for (auto& commitment : ecc_op_tables_val) { + commitment = G1Native::random_element(); + } + + // Store the public inputs of the first circuit to be used by the second + std::vector public_inputs; + + { // The first circuit propagates the kernel output via its public inputs + Builder builder; + + HidingIO hiding_output; + + // Set the output values + PairingInputs pairing_inputs{ G1::from_witness(&builder, P0_val), G1::from_witness(&builder, P1_val) }; + std::array ecc_op_tables; + for (size_t idx = 0; idx < NUM_WIRES; idx++) { + ecc_op_tables[idx] = G1::from_witness(&builder, ecc_op_tables_val[idx]); + } + + hiding_output.pairing_inputs = pairing_inputs; + hiding_output.ecc_op_tables = ecc_op_tables; + + // Propagate the kernel output via the public inputs + hiding_output.set_public(); + + // Store the public inputs from this circuit for use in the second circuit + for (const auto& idx : builder.public_inputs()) { + public_inputs.push_back(builder.get_variable(idx)); + } + } + + { + // The second circuit reconstructs the kernel inputs from the public inputs + Builder builder; + + // Construct the stdlib public inputs (e.g. as a recursive verifier would do upon receiving them in the + // proof) + std::vector stdlib_public_inputs; + stdlib_public_inputs.reserve(public_inputs.size()); + for (const auto& val : public_inputs) { + stdlib_public_inputs.push_back(FF::from_witness(&builder, val)); + } + + HidingIO hiding_input; + hiding_input.reconstruct_from_public(stdlib_public_inputs); + + // Ensure the reconstructed data matches the original values + EXPECT_EQ(hiding_input.pairing_inputs.P0.get_value(), P0_val); + EXPECT_EQ(hiding_input.pairing_inputs.P1.get_value(), P1_val); + for (auto [reconstructed_commitment, commitment] : zip_view(hiding_input.ecc_op_tables, ecc_op_tables_val)) { + EXPECT_EQ(reconstructed_commitment.get_value(), commitment); + } + } + + { + // Reconstruct the public inputs from native elements + HidingIONative hiding_input_native; + hiding_input_native.reconstruct_from_public(public_inputs); + + // Ensure the reconstructed data matches the original values + EXPECT_EQ(hiding_input_native.pairing_inputs.P0, P0_val); + EXPECT_EQ(hiding_input_native.pairing_inputs.P1, P1_val); + for (auto [reconstructed_commitment, commitment] : + zip_view(hiding_input_native.ecc_op_tables, ecc_op_tables_val)) { + EXPECT_EQ(reconstructed_commitment, commitment); + } + } +} + } // namespace bb::stdlib::recursion::honk diff --git a/barretenberg/cpp/src/barretenberg/ultra_honk/mega_transcript.test.cpp b/barretenberg/cpp/src/barretenberg/ultra_honk/mega_transcript.test.cpp index 2090cecf4cf1..1a27f64c2c9b 100644 --- a/barretenberg/cpp/src/barretenberg/ultra_honk/mega_transcript.test.cpp +++ b/barretenberg/cpp/src/barretenberg/ultra_honk/mega_transcript.test.cpp @@ -2,7 +2,7 @@ #include "barretenberg/flavor/flavor.hpp" #include "barretenberg/numeric/bitop/get_msb.hpp" #include "barretenberg/polynomials/univariate.hpp" -#include "barretenberg/stdlib/pairing_points.hpp" +#include "barretenberg/stdlib/special_public_inputs/special_public_inputs.hpp" #include "barretenberg/transcript/transcript.hpp" #include "barretenberg/ultra_honk/decider_proving_key.hpp" #include "barretenberg/ultra_honk/ultra_prover.hpp" @@ -36,6 +36,8 @@ template class MegaTranscriptTests : public ::testing::Test { using Commitment = typename Flavor::Commitment; TranscriptManifest manifest_expected; + size_t NUM_PUBLIC_INPUTS = + stdlib::recursion::honk::HidingKernelIO::PUBLIC_INPUTS_SIZE; size_t MAX_PARTIAL_RELATION_LENGTH = Flavor::BATCHED_RELATION_PARTIAL_LENGTH; size_t NUM_SUBRELATIONS = Flavor::NUM_SUBRELATIONS; @@ -47,7 +49,7 @@ template class MegaTranscriptTests : public ::testing::Test { size_t round = 0; manifest_expected.add_entry(round, "vk_hash", frs_per_Fr); manifest_expected.add_entry(round, "public_input_0", frs_per_Fr); - for (size_t i = 0; i < PAIRING_POINTS_SIZE; i++) { + for (size_t i = 0; i < NUM_PUBLIC_INPUTS; i++) { manifest_expected.add_entry(round, "public_input_" + std::to_string(1 + i), frs_per_Fr); } manifest_expected.add_entry(round, "W_L", frs_per_G); @@ -174,7 +176,7 @@ template class MegaTranscriptTests : public ::testing::Test { uint32_t d_idx = builder.add_variable(d); builder.create_big_add_gate({ a_idx, b_idx, c_idx, d_idx, FF(1), FF(1), FF(1), FF(-1), FF(0) }); - stdlib::recursion::PairingPoints::add_default_to_public_inputs(builder); + stdlib::recursion::honk::HidingKernelIO::add_default(builder); } }; TYPED_TEST_SUITE(MegaTranscriptTests, FlavorTypes); diff --git a/barretenberg/cpp/src/barretenberg/ultra_honk/ultra_verifier.cpp b/barretenberg/cpp/src/barretenberg/ultra_honk/ultra_verifier.cpp index 0ee7f6899194..ff108de41aba 100644 --- a/barretenberg/cpp/src/barretenberg/ultra_honk/ultra_verifier.cpp +++ b/barretenberg/cpp/src/barretenberg/ultra_honk/ultra_verifier.cpp @@ -99,17 +99,12 @@ std::pair::Commitment, Flavor:: auto [public_inputs, decider_output] = verify_internal(proof); // Reconstruct the public inputs - DefaultIO inputs; // Will be HidingKernelIO + HidingKernelIO inputs; inputs.reconstruct_from_public(public_inputs); decider_output.pairing_points.aggregate(inputs.pairing_inputs); - // Dummy vector, will be fetched from inputs once we have HidingKernelIO - std::array dummy; - for (auto& commitment : dummy) { - commitment = Commitment::one(); - } - return std::make_pair(decider_output.check(), dummy); + return std::make_pair(decider_output.check(), inputs.ecc_op_tables); } template class UltraVerifier_; diff --git a/barretenberg/cpp/src/barretenberg/vm2/constraining/recursion/goblin_avm_recursive_verifier.hpp b/barretenberg/cpp/src/barretenberg/vm2/constraining/recursion/goblin_avm_recursive_verifier.hpp index 9f8f4a687e11..2096b5570eb0 100644 --- a/barretenberg/cpp/src/barretenberg/vm2/constraining/recursion/goblin_avm_recursive_verifier.hpp +++ b/barretenberg/cpp/src/barretenberg/vm2/constraining/recursion/goblin_avm_recursive_verifier.hpp @@ -7,6 +7,7 @@ #include "barretenberg/stdlib/goblin_verifier/goblin_recursive_verifier.hpp" #include "barretenberg/stdlib/hash/poseidon2/poseidon2.hpp" #include "barretenberg/stdlib/honk_verifier/ultra_recursive_verifier.hpp" +#include "barretenberg/stdlib/special_public_inputs/special_public_inputs.hpp" #include "barretenberg/ultra_honk/ultra_prover.hpp" #include "barretenberg/ultra_honk/ultra_verifier.hpp" #include "barretenberg/vm2/constraining/recursion/recursive_flavor.hpp" @@ -171,6 +172,7 @@ class AvmGoblinRecursiveVerifier { using TranslatorVK = Goblin::TranslatorVerificationKey; using MegaVerificationKey = MegaFlavor::VerificationKey; using FF = AvmRecursiveFlavor::FF; + using IO = stdlib::recursion::honk::HidingKernelIO; // Instantiate Mega builder for the inner circuit (AVM2 proof recursive verifier) Goblin goblin; @@ -207,7 +209,14 @@ class AvmGoblinRecursiveVerifier { auto stdlib_key = std::make_shared(mega_builder, std::span(key_fields)); AvmRecursiveVerifier recursive_verifier{ mega_builder, stdlib_key }; MegaPairingPoints points_accumulator = recursive_verifier.verify_proof(mega_stdlib_proof, mega_public_inputs); - points_accumulator.set_public(); + + // Public inputs + IO inputs; + inputs.pairing_inputs = points_accumulator; + inputs.ecc_op_tables = stdlib::recursion::honk::HidingKernelIO::default_ecc_op_tables( + mega_builder); // There is only one layer of Goblin, so the verifier will set T_prev + // to the empty table and disregard this value + inputs.set_public(); // All prover components share a single transcript std::shared_ptr transcript = std::make_shared();