diff --git a/barretenberg/cpp/src/barretenberg/client_ivc/client_ivc.cpp b/barretenberg/cpp/src/barretenberg/client_ivc/client_ivc.cpp index 8f889fc8b2c9..22c0738a15c1 100644 --- a/barretenberg/cpp/src/barretenberg/client_ivc/client_ivc.cpp +++ b/barretenberg/cpp/src/barretenberg/client_ivc/client_ivc.cpp @@ -171,14 +171,14 @@ void ClientIVC::accumulate(ClientCircuit& circuit, const std::shared_ptr(circuit)); // Construct the proving key for circuit - std::shared_ptr proving_key; - if (!initialized) { - proving_key = std::make_shared(circuit, trace_settings); - trace_usage_tracker = ExecutionTraceUsageTracker(trace_settings); - } else { - proving_key = std::make_shared(circuit, trace_settings); - } + std::shared_ptr proving_key = std::make_shared(circuit, trace_settings); + // The commitment key is initialised with the number of points determined by the trace_settings' dyadic size. If a + // circuit overflows past the dyadic size the commitment key will not have enough points so we need to increase it + if (proving_key->proving_key.circuit_size > trace_settings.dyadic_size()) { + bn254_commitment_key = std::make_shared>(proving_key->proving_key.circuit_size); + goblin.commitment_key = bn254_commitment_key; + } proving_key->proving_key.commitment_key = bn254_commitment_key; vinfo("getting honk vk... precomputed?: ", precomputed_vk); @@ -192,7 +192,8 @@ void ClientIVC::accumulate(ClientCircuit& circuit, const std::shared_ptr oink_prover{ proving_key }; vinfo("computing oink proof..."); @@ -210,8 +211,8 @@ void ClientIVC::accumulate(ClientCircuit& circuit, const std::shared_ptr>(trace_settings.dyadic_size()) diff --git a/barretenberg/cpp/src/barretenberg/client_ivc/client_ivc.test.cpp b/barretenberg/cpp/src/barretenberg/client_ivc/client_ivc.test.cpp index 8cc6d540c6a0..ec0d253ce93f 100644 --- a/barretenberg/cpp/src/barretenberg/client_ivc/client_ivc.test.cpp +++ b/barretenberg/cpp/src/barretenberg/client_ivc/client_ivc.test.cpp @@ -2,9 +2,9 @@ #include "barretenberg/client_ivc/test_bench_shared.hpp" #include "barretenberg/goblin/goblin.hpp" #include "barretenberg/goblin/mock_circuits.hpp" +#include "barretenberg/protogalaxy/folding_test_utils.hpp" #include "barretenberg/stdlib_circuit_builders/mega_circuit_builder.hpp" #include "barretenberg/stdlib_circuit_builders/ultra_circuit_builder.hpp" - #include using namespace bb; @@ -403,5 +403,62 @@ TEST_F(ClientIVCTests, StructuredTraceOverflow) log2_num_gates += 1; } + EXPECT_TRUE(ivc.prove_and_verify()); +}; + +/** + * @brief Test dynamic structured trace overflow block mechanism + * @details Tests the case where the required overflow capacity is not known until runtime. Accumulates two circuits, + * the second of which overflows the trace but not enough to change the dyadic circuit size and thus there is no need + * for a virtual size increase of the first key. + * + */ +TEST_F(ClientIVCTests, DynamicOverflow) +{ + // Define trace settings with zero overflow capacity + ClientIVC ivc{ { SMALL_TEST_STRUCTURE_FOR_OVERFLOWS, /*overflow_capacity=*/0 } }; + + MockCircuitProducer circuit_producer; + + const size_t NUM_CIRCUITS = 2; + + // define parameters for two circuits; the first fits within the structured trace, the second overflows + const std::vector log2_num_arith_gates = { 14, 16 }; + // Accumulate + for (size_t idx = 0; idx < NUM_CIRCUITS; ++idx) { + auto circuit = circuit_producer.create_next_circuit(ivc, log2_num_arith_gates[idx]); + ivc.accumulate(circuit); + } + + EXPECT_EQ(check_accumulator_target_sum_manual(ivc.fold_output.accumulator), true); + EXPECT_TRUE(ivc.prove_and_verify()); +}; + +/** + * @brief Test dynamic trace overflow where the dyadic circuit size also increases + * @details Accumulates two circuits, the second of which overflows the trace structure and leads to an increased dyadic + * circuit size. This requires the virtual size of the polynomials in the first key to be increased accordingly which + * should be handled automatically in PG/ClientIvc. + * + */ +TEST_F(ClientIVCTests, DynamicOverflowCircuitSizeChange) +{ + uint32_t overflow_capacity = 0; + // uint32_t overflow_capacity = 1 << 1; + ClientIVC ivc{ { SMALL_TEST_STRUCTURE_FOR_OVERFLOWS, overflow_capacity } }; + + MockCircuitProducer circuit_producer; + + const size_t NUM_CIRCUITS = 2; + + // define parameters for two circuits; the first fits within the structured trace, the second overflows + const std::vector log2_num_arith_gates = { 14, 18 }; + // Accumulate + for (size_t idx = 0; idx < NUM_CIRCUITS; ++idx) { + auto circuit = circuit_producer.create_next_circuit(ivc, log2_num_arith_gates[idx]); + ivc.accumulate(circuit); + } + + EXPECT_EQ(check_accumulator_target_sum_manual(ivc.fold_output.accumulator), true); EXPECT_TRUE(ivc.prove_and_verify()); }; \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/commitment_schemes/kzg/kzg.hpp b/barretenberg/cpp/src/barretenberg/commitment_schemes/kzg/kzg.hpp index 955a9da9fe1f..12e8606f8aa8 100644 --- a/barretenberg/cpp/src/barretenberg/commitment_schemes/kzg/kzg.hpp +++ b/barretenberg/cpp/src/barretenberg/commitment_schemes/kzg/kzg.hpp @@ -1,6 +1,6 @@ #pragma once -#include "../claim.hpp" +#include "barretenberg/commitment_schemes/claim.hpp" #include "barretenberg/commitment_schemes/commitment_key.hpp" #include "barretenberg/commitment_schemes/utils/batch_mul_native.hpp" #include "barretenberg/commitment_schemes/verification_key.hpp" diff --git a/barretenberg/cpp/src/barretenberg/goblin/mock_circuits.hpp b/barretenberg/cpp/src/barretenberg/goblin/mock_circuits.hpp index e2d1c5987993..03c86a55b083 100644 --- a/barretenberg/cpp/src/barretenberg/goblin/mock_circuits.hpp +++ b/barretenberg/cpp/src/barretenberg/goblin/mock_circuits.hpp @@ -18,6 +18,22 @@ namespace bb { +/** + * @brief An arbitrary but small-ish structuring that can be used for testing with non-trivial circuits in cases when + * they overflow + */ +static constexpr TraceStructure SMALL_TEST_STRUCTURE_FOR_OVERFLOWS{ .ecc_op = 1 << 14, + .pub_inputs = 1 << 14, + .busread = 1 << 14, + .arithmetic = 1 << 15, + .delta_range = 1 << 14, + .elliptic = 1 << 14, + .aux = 1 << 14, + .poseidon2_external = 1 << 14, + .poseidon2_internal = 1 << 15, + .lookup = 1 << 14, + .overflow = 0 }; + class GoblinMockCircuits { public: using Curve = curve::BN254; diff --git a/barretenberg/cpp/src/barretenberg/plonk_honk_shared/execution_trace/execution_trace_usage_tracker.hpp b/barretenberg/cpp/src/barretenberg/plonk_honk_shared/execution_trace/execution_trace_usage_tracker.hpp index 7b9b1fadb6bc..683a20a3d635 100644 --- a/barretenberg/cpp/src/barretenberg/plonk_honk_shared/execution_trace/execution_trace_usage_tracker.hpp +++ b/barretenberg/cpp/src/barretenberg/plonk_honk_shared/execution_trace/execution_trace_usage_tracker.hpp @@ -31,6 +31,21 @@ struct ExecutionTraceUsageTracker { size_t max_databus_size = 0; size_t max_tables_size = 0; + // For printing only. Must match the order of the members in the arithmetization + static constexpr std::array block_labels{ "ecc_op", + "pub_inputs", + "busread", + "arithmetic", + "delta_range", + "elliptic", + "aux", + "poseidon2_external", + "poseidon2_internal", + "lookup", + "overflow", + "databus_table_data", + "lookup_table_data" }; + TraceSettings trace_settings; ExecutionTraceUsageTracker(const TraceSettings& trace_settings = TraceSettings{}) @@ -72,8 +87,12 @@ struct ExecutionTraceUsageTracker { } // The active ranges must also include the rows where the actual databus and lookup table data are stored. - // (Note: lookup tables are constructed at the end of the trace; databus data is constructed at the start). - size_t dyadic_circuit_size = fixed_sizes.get_structured_dyadic_size(); + // (Note: lookup tables are constructed at the end of the trace; databus data is constructed at the start) so we + // need to determine the dyadic size for this. We call the size function on the current circuit which will have + // the same fixed block sizes but might also have an overflow block potentially influencing the dyadic circuit + // size. + // TODO(https://github.com/AztecProtocol/barretenberg/issues/1160) + const size_t dyadic_circuit_size = circuit.blocks.get_structured_dyadic_size(); // TODO(https://github.com/AztecProtocol/barretenberg/issues/1152): should be able to use simply Range{ 0, // max_databus_size } but this breaks for certain choices of num_threads. @@ -98,24 +117,13 @@ struct ExecutionTraceUsageTracker { }); } - // For printing only. Must match the order of the members in the arithmetization - std::vector block_labels{ "ecc_op", - "pub_inputs", - "busread", - "arithmetic", - "delta_range", - "elliptic", - "aux", - "poseidon2_external", - "poseidon2_internal", - "lookup", - "overflow" }; - void print() { info("Minimum required block sizes for structured trace: "); - for (auto [label, max_size] : zip_view(block_labels, max_sizes.get())) { - std::cout << std::left << std::setw(20) << (label + ":") << max_size << std::endl; + size_t idx = 0; + for (auto max_size : max_sizes.get()) { + std::cout << std::left << std::setw(20) << block_labels[idx] << ": " << max_size << std::endl; + idx++; } info(""); } @@ -124,8 +132,18 @@ struct ExecutionTraceUsageTracker { { info("Active regions of accumulator: "); for (auto [label, range] : zip_view(block_labels, active_ranges)) { - std::cout << std::left << std::setw(20) << (label + ":") << "(" << range.first << ", " << range.second - << ")" << std::endl; + std::cout << std::left << std::setw(20) << label << ": (" << range.first << ", " << range.second << ")" + << std::endl; + } + info(""); + } + + void print_previous_active_ranges() + { + info("Active regions of previous accumulator: "); + for (auto [label, range] : zip_view(block_labels, previous_active_ranges)) { + std::cout << std::left << std::setw(20) << label << ": (" << range.first << ", " << range.second << ")" + << std::endl; } info(""); } diff --git a/barretenberg/cpp/src/barretenberg/plonk_honk_shared/execution_trace/mega_execution_trace.hpp b/barretenberg/cpp/src/barretenberg/plonk_honk_shared/execution_trace/mega_execution_trace.hpp index 44d6dce49454..a24d4131a93e 100644 --- a/barretenberg/cpp/src/barretenberg/plonk_honk_shared/execution_trace/mega_execution_trace.hpp +++ b/barretenberg/cpp/src/barretenberg/plonk_honk_shared/execution_trace/mega_execution_trace.hpp @@ -30,10 +30,17 @@ template struct MegaTraceBlockData { std::vector get_labels() const { - return { "ecc_op", "pub_inputs", "busread", - "arithmetic", "delta_range", "elliptic", - "aux", "poseidon2_external", "poseidon2_internal", - "lookup" }; + return { "ecc_op", + "pub_inputs", + "busread", + "arithmetic", + "delta_range", + "elliptic", + "aux", + "poseidon2_external", + "poseidon2_internal", + "lookup", + "overflow" }; } auto get() @@ -220,10 +227,10 @@ class MegaExecutionTraceBlocks : public MegaTraceBlockData { info(""); } - size_t get_structured_dyadic_size() + size_t get_structured_dyadic_size() const { size_t total_size = 1; // start at 1 because the 0th row is unused for selectors for Honk - for (auto block : this->get()) { + for (const auto& block : this->get()) { total_size += block.get_fixed_size(); } diff --git a/barretenberg/cpp/src/barretenberg/plonk_honk_shared/library/grand_product_delta.hpp b/barretenberg/cpp/src/barretenberg/plonk_honk_shared/library/grand_product_delta.hpp index b967dc0e93a9..49aeb0edd77c 100644 --- a/barretenberg/cpp/src/barretenberg/plonk_honk_shared/library/grand_product_delta.hpp +++ b/barretenberg/cpp/src/barretenberg/plonk_honk_shared/library/grand_product_delta.hpp @@ -48,6 +48,9 @@ typename Flavor::FF compute_public_input_delta(std::span class RelationChecker { + public: + /** + * @brief Check that the provided polynomials satisfy all relations for a given Flavor + */ + static void check_all([[maybe_unused]] const auto& polynomials, [[maybe_unused]] const auto& params) + { + // default; do nothing + } + + /** + * @brief Check that a single specified relation is satisfied for a set of polynomials + * + * @tparam Relation a linearly independent Relation to be checked + * @param polynomials prover polynomials + * @param params a RelationParameters instance + */ + template + static void check(const auto& polynomials, + const auto& params, + bool is_linearly_independent, + std::string label = "Relation") + { + // Define the appropriate accumulator type for the relation and initialize to zero + typename Relation::SumcheckArrayOfValuesOverSubrelations result; + for (auto& element : result) { + element = 0; + } + + for (size_t i = 0; i < polynomials.w_l.virtual_size(); i++) { + if (is_linearly_independent) { + // Evaluate each constraint in the relation and check that each is satisfied + Relation::accumulate(result, polynomials.get_row(i), params, 1); + size_t subrelation_idx = 0; + for (auto& element : result) { + if (element != 0) { + info("RelationChecker: ", + label, + " relation (subrelation idx: ", + subrelation_idx, + ") failed at row idx: ", + i, + "."); + ASSERT(false); + } + subrelation_idx++; + } + } + } + + if (!is_linearly_independent) { + // Result accumulated across entire execution trace should be zero + for (auto& element : result) { + if (element != 0) { + info("RelationChecker: ", label, " relation (linearly indep.) failed."); + ASSERT(false); + } + } + } + } +}; + +// Specialization for Ultra +template <> class RelationChecker : public RelationChecker { + using Base = RelationChecker; + + public: + static void check_all(const auto& polynomials, const auto& params) + { + using FF = UltraFlavor::FF; + + // Linearly independent relations (must be satisfied at each row) + Base::check>(polynomials, params, true, "UltraArithmetic"); + Base::check>(polynomials, params, true, "UltraPermutation"); + Base::check>(polynomials, params, true, "DeltaRangeConstraint"); + Base::check>(polynomials, params, true, "Elliptic"); + Base::check>(polynomials, params, true, "Auxiliary"); + Base::check>(polynomials, params, true, "Poseidon2External"); + Base::check>(polynomials, params, true, "Poseidon2Internal"); + + // Linearly dependent relations (must be satisfied as a sum across all rows) + Base::check>(polynomials, params, false, "LogDerivLookup"); + } +}; + +// Specialization for Mega +template <> class RelationChecker : public RelationChecker { + using Base = RelationChecker; + + public: + static void check_all(const auto& polynomials, const auto& params) + { + // Check relations that are shared with Ultra + RelationChecker::check_all(polynomials, params); + + using FF = MegaFlavor::FF; + + // Linearly independent relations (must be satisfied at each row) + Base::check>(polynomials, params, true, "EccOpQueue"); + + // Linearly dependent relations (must be satisfied as a sum across all rows) + Base::check>(polynomials, params, false, "DatabusLookup"); + } +}; + +} // namespace bb \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/polynomials/polynomial.hpp b/barretenberg/cpp/src/barretenberg/polynomials/polynomial.hpp index 3747b1bf2c73..79ee95fe7ef7 100644 --- a/barretenberg/cpp/src/barretenberg/polynomials/polynomial.hpp +++ b/barretenberg/cpp/src/barretenberg/polynomials/polynomial.hpp @@ -249,6 +249,7 @@ template class Polynomial { std::size_t size() const { return coefficients_.size(); } std::size_t virtual_size() const { return coefficients_.virtual_size(); } + void increase_virtual_size(const size_t size_in) { coefficients_.increase_virtual_size(size_in); }; Fr* data() { return coefficients_.data(); } const Fr* data() const { return coefficients_.data(); } diff --git a/barretenberg/cpp/src/barretenberg/polynomials/shared_shifted_virtual_zeroes_array.hpp b/barretenberg/cpp/src/barretenberg/polynomials/shared_shifted_virtual_zeroes_array.hpp index 7dd50a99c963..191080edbe8a 100644 --- a/barretenberg/cpp/src/barretenberg/polynomials/shared_shifted_virtual_zeroes_array.hpp +++ b/barretenberg/cpp/src/barretenberg/polynomials/shared_shifted_virtual_zeroes_array.hpp @@ -1,6 +1,7 @@ #pragma once #include "barretenberg/common/assert.hpp" +#include "barretenberg/common/log.hpp" #include #include @@ -47,6 +48,14 @@ template struct SharedShiftedVirtualZeroesArray { const T& get(size_t index, size_t virtual_padding = 0) const { static const T zero{}; + if (index >= virtual_size_ + virtual_padding) { + info("BAD GET(): index = ", + index, + ", virtual_size_ = ", + virtual_size_, + ", virtual_padding = ", + virtual_padding); + } ASSERT(index < virtual_size_ + virtual_padding); if (index >= start_ && index < end_) { return data()[index - start_]; @@ -68,6 +77,12 @@ template struct SharedShiftedVirtualZeroesArray { // Getter for consistency with size(); size_t virtual_size() const { return virtual_size_; } + void increase_virtual_size(const size_t new_virtual_size) + { + ASSERT(new_virtual_size >= virtual_size_); // shrinking is not allowed + virtual_size_ = new_virtual_size; + } + T& operator[](size_t index) { ASSERT(index >= start_ && index < end_); diff --git a/barretenberg/cpp/src/barretenberg/protogalaxy/folding_test_utils.hpp b/barretenberg/cpp/src/barretenberg/protogalaxy/folding_test_utils.hpp new file mode 100644 index 000000000000..cd478da264f7 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/protogalaxy/folding_test_utils.hpp @@ -0,0 +1,37 @@ +#include "barretenberg/polynomials/gate_separator.hpp" +#include "barretenberg/protogalaxy/protogalaxy_prover.hpp" +#include "barretenberg/protogalaxy/protogalaxy_prover_internal.hpp" +#include "barretenberg/protogalaxy/protogalaxy_verifier.hpp" +#include "barretenberg/ultra_honk/decider_prover.hpp" +#include "barretenberg/ultra_honk/decider_verifier.hpp" + +namespace bb { + +/** + * @brief Utility to manually compute the target sum of an accumulator and compare it to the one produced in Protogalxy + * to attest correctness. + * + * @details As we create a ProtogalaxyProverInternal object with an empty execution trace tracker and no active_ranges + * set, compute_row_evaluations will operate on all rows. + */ +template +static bool check_accumulator_target_sum_manual(const std::shared_ptr>& accumulator) +{ + using DeciderProvingKeys = DeciderProvingKeys_; + using PGInternal = ProtogalaxyProverInternal; + + const size_t accumulator_size = accumulator->proving_key.circuit_size; + PGInternal pg_internal; + const auto expected_honk_evals = pg_internal.compute_row_evaluations( + accumulator->proving_key.polynomials, accumulator->alphas, accumulator->relation_parameters); + // Construct pow(\vec{betas*}) as in the paper + GateSeparatorPolynomial expected_gate_separators(accumulator->gate_challenges, accumulator->gate_challenges.size()); + + // Compute the corresponding target sum and create a dummy accumulator + typename Flavor::FF expected_target_sum{ 0 }; + for (size_t idx = 0; idx < accumulator_size; idx++) { + expected_target_sum += expected_honk_evals[idx] * expected_gate_separators[idx]; + } + return accumulator->target_sum == expected_target_sum; +} +} // namespace bb \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy.test.cpp b/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy.test.cpp index 29f80b807f07..bcbd70090d65 100644 --- a/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy.test.cpp +++ b/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy.test.cpp @@ -1,5 +1,6 @@ #include "barretenberg/goblin/mock_circuits.hpp" #include "barretenberg/polynomials/gate_separator.hpp" +#include "barretenberg/protogalaxy/folding_test_utils.hpp" #include "barretenberg/protogalaxy/protogalaxy_prover.hpp" #include "barretenberg/protogalaxy/protogalaxy_prover_internal.hpp" #include "barretenberg/protogalaxy/protogalaxy_verifier.hpp" @@ -80,9 +81,10 @@ template class ProtogalaxyTests : public testing::Test { static std::tuple, std::shared_ptr> fold_and_verify( const std::vector>& proving_keys, - const std::vector>& verification_keys) + const std::vector>& verification_keys, + ExecutionTraceUsageTracker trace_usage_tracker = ExecutionTraceUsageTracker{}) { - FoldingProver folding_prover(proving_keys); + FoldingProver folding_prover(proving_keys, trace_usage_tracker); FoldingVerifier folding_verifier(verification_keys); auto [prover_accumulator, folding_proof] = folding_prover.prove(); @@ -90,27 +92,8 @@ template class ProtogalaxyTests : public testing::Test { return { prover_accumulator, verifier_accumulator }; } - static void check_accumulator_target_sum_manual(std::shared_ptr& accumulator, - bool expected_result) - { - size_t accumulator_size = accumulator->proving_key.circuit_size; - PGInternal pg_internal; - auto expected_honk_evals = pg_internal.compute_row_evaluations( - accumulator->proving_key.polynomials, accumulator->alphas, accumulator->relation_parameters); - // Construct pow(\vec{betas*}) as in the paper - GateSeparatorPolynomial expected_gate_separators(accumulator->gate_challenges, - accumulator->gate_challenges.size()); - - // Compute the corresponding target sum and create a dummy accumulator - FF expected_target_sum{ 0 }; - for (size_t idx = 0; idx < accumulator_size; idx++) { - expected_target_sum += expected_honk_evals[idx] * expected_gate_separators[idx]; - } - EXPECT_EQ(accumulator->target_sum == expected_target_sum, expected_result); - } - - static void decide_and_verify(std::shared_ptr& prover_accumulator, - std::shared_ptr& verifier_accumulator, + static void decide_and_verify(const std::shared_ptr& prover_accumulator, + const std::shared_ptr& verifier_accumulator, bool expected_result) { DeciderProver decider_prover(prover_accumulator); @@ -331,7 +314,7 @@ template class ProtogalaxyTests : public testing::Test { // Perform prover and verifier folding auto [prover_accumulator, verifier_accumulator] = fold_and_verify(get<0>(keys), get<1>(keys)); - check_accumulator_target_sum_manual(prover_accumulator, true); + EXPECT_TRUE(check_accumulator_target_sum_manual(prover_accumulator)); // Run decider decide_and_verify(prover_accumulator, verifier_accumulator, true); @@ -414,9 +397,8 @@ template class ProtogalaxyTests : public testing::Test { auto [prover_accumulator, verifier_accumulator] = fold_and_verify(get<0>(keys), get<1>(keys)); // Expect failure in manual target sum check and decider - bool expected_result = false; - check_accumulator_target_sum_manual(prover_accumulator, expected_result); - decide_and_verify(prover_accumulator, verifier_accumulator, expected_result); + EXPECT_FALSE(check_accumulator_target_sum_manual(prover_accumulator)); + decide_and_verify(prover_accumulator, verifier_accumulator, false); } /** @@ -427,12 +409,12 @@ template class ProtogalaxyTests : public testing::Test { { TupleOfKeys insts = construct_keys(2); auto [prover_accumulator, verifier_accumulator] = fold_and_verify(get<0>(insts), get<1>(insts)); - check_accumulator_target_sum_manual(prover_accumulator, true); + EXPECT_TRUE(check_accumulator_target_sum_manual(prover_accumulator)); TupleOfKeys insts_2 = construct_keys(1); // just one key pair auto [prover_accumulator_2, verifier_accumulator_2] = fold_and_verify({ prover_accumulator, get<0>(insts_2)[0] }, { verifier_accumulator, get<1>(insts_2)[0] }); - check_accumulator_target_sum_manual(prover_accumulator_2, true); + EXPECT_TRUE(check_accumulator_target_sum_manual(prover_accumulator_2)); decide_and_verify(prover_accumulator_2, verifier_accumulator_2, true); } @@ -443,21 +425,62 @@ template class ProtogalaxyTests : public testing::Test { */ static void test_full_protogalaxy_structured_trace() { - TraceSettings trace_settings{ SMALL_TEST_STRUCTURE }; + TraceSettings trace_settings{ SMALL_TEST_STRUCTURE_FOR_OVERFLOWS }; TupleOfKeys keys_1 = construct_keys(2, trace_settings); auto [prover_accumulator, verifier_accumulator] = fold_and_verify(get<0>(keys_1), get<1>(keys_1)); - check_accumulator_target_sum_manual(prover_accumulator, true); + EXPECT_TRUE(check_accumulator_target_sum_manual(prover_accumulator)); TupleOfKeys keys_2 = construct_keys(1, trace_settings); // just one key pair auto [prover_accumulator_2, verifier_accumulator_2] = fold_and_verify({ prover_accumulator, get<0>(keys_2)[0] }, { verifier_accumulator, get<1>(keys_2)[0] }); - check_accumulator_target_sum_manual(prover_accumulator_2, true); + EXPECT_TRUE(check_accumulator_target_sum_manual(prover_accumulator_2)); info(prover_accumulator_2->proving_key.circuit_size); decide_and_verify(prover_accumulator_2, verifier_accumulator_2, true); } + /** + * @brief Testing folding a larger circuit into a smaller one by increasing the virtual size of the first. + * @details Fold two circuits using a structured trace, where the second overflows the trace such that the dyadic + * size is doubled. The virtual size of the polynomials in the first key is increased internally in the PG prover to + * match the size of the second. + * + */ + static void test_fold_with_virtual_size_expansion() + { + uint32_t overflow_capacity = 0; // consider the case where the overflow is not known until runtime + TraceSettings trace_settings{ SMALL_TEST_STRUCTURE_FOR_OVERFLOWS, overflow_capacity }; + ExecutionTraceUsageTracker trace_usage_tracker = ExecutionTraceUsageTracker(trace_settings); + + std::vector> decider_pks; + std::vector> decider_vks; + + // define parameters for two circuits; the first fits within the structured trace, the second overflows + const std::vector log2_num_gates = { 14, 18 }; + for (size_t i = 0; i < 2; ++i) { + MegaCircuitBuilder builder; + + MockCircuits::add_arithmetic_gates(builder, 1 << log2_num_gates[i]); + + auto decider_proving_key = std::make_shared(builder, trace_settings); + trace_usage_tracker.update(builder); + auto verification_key = std::make_shared(decider_proving_key->proving_key); + auto decider_verification_key = std::make_shared(verification_key); + decider_pks.push_back(decider_proving_key); + decider_vks.push_back(decider_verification_key); + } + + // Ensure the dyadic size of the first key is strictly less than that of the second + EXPECT_TRUE(decider_pks[0]->proving_key.circuit_size < decider_pks[1]->proving_key.circuit_size); + + // The size discrepency should be automatically handled by the PG prover via a virtual size increase + const auto [prover_accumulator, verifier_accumulator] = + fold_and_verify(decider_pks, decider_vks, trace_usage_tracker); + EXPECT_TRUE(check_accumulator_target_sum_manual(prover_accumulator)); + decide_and_verify(prover_accumulator, verifier_accumulator, true); + } + /** * @brief Testing two valid rounds of folding followed by the decider for a structured trace. * @details Here we're interested in folding inhomogeneous circuits, i.e. circuits with different numbers of @@ -488,7 +511,7 @@ template class ProtogalaxyTests : public testing::Test { // Fold the first two pairs auto [prover_accumulator, verifier_accumulator] = fold_and_verify(get<0>(keys_1), get<1>(keys_1)); - check_accumulator_target_sum_manual(prover_accumulator, true); + EXPECT_TRUE(check_accumulator_target_sum_manual(prover_accumulator)); // Construct the decider key pair for the third circuit TupleOfKeys keys_2; @@ -497,7 +520,7 @@ template class ProtogalaxyTests : public testing::Test { // Fold 3rd pair of keys into their respective accumulators auto [prover_accumulator_2, verifier_accumulator_2] = fold_and_verify({ prover_accumulator, get<0>(keys_2)[0] }, { verifier_accumulator, get<1>(keys_2)[0] }); - check_accumulator_target_sum_manual(prover_accumulator_2, true); + EXPECT_TRUE(check_accumulator_target_sum_manual(prover_accumulator_2)); info(prover_accumulator_2->proving_key.circuit_size); // Decide on final accumulator @@ -512,7 +535,7 @@ template class ProtogalaxyTests : public testing::Test { { TupleOfKeys insts = construct_keys(2); auto [prover_accumulator, verifier_accumulator] = fold_and_verify(get<0>(insts), get<1>(insts)); - check_accumulator_target_sum_manual(prover_accumulator, true); + EXPECT_TRUE(check_accumulator_target_sum_manual(prover_accumulator)); // Tamper with a commitment verifier_accumulator->witness_commitments.w_l = Projective(Affine::random_element()); @@ -520,7 +543,7 @@ template class ProtogalaxyTests : public testing::Test { TupleOfKeys insts_2 = construct_keys(1); // just one decider key pair auto [prover_accumulator_2, verifier_accumulator_2] = fold_and_verify({ prover_accumulator, get<0>(insts_2)[0] }, { verifier_accumulator, get<1>(insts_2)[0] }); - check_accumulator_target_sum_manual(prover_accumulator_2, true); + EXPECT_TRUE(check_accumulator_target_sum_manual(prover_accumulator_2)); decide_and_verify(prover_accumulator_2, verifier_accumulator_2, false); } @@ -534,11 +557,11 @@ template class ProtogalaxyTests : public testing::Test { { TupleOfKeys insts = construct_keys(2); auto [prover_accumulator, verifier_accumulator] = fold_and_verify(get<0>(insts), get<1>(insts)); - check_accumulator_target_sum_manual(prover_accumulator, true); + EXPECT_TRUE(check_accumulator_target_sum_manual(prover_accumulator)); // Tamper with an accumulator polynomial prover_accumulator->proving_key.polynomials.w_l.at(1) = FF::random_element(); - check_accumulator_target_sum_manual(prover_accumulator, false); + EXPECT_FALSE(check_accumulator_target_sum_manual(prover_accumulator)); TupleOfKeys insts_2 = construct_keys(1); // just one decider key pair auto [prover_accumulator_2, verifier_accumulator_2] = @@ -558,8 +581,7 @@ template class ProtogalaxyTests : public testing::Test { auto [prover_accumulator, folding_proof] = folding_prover.prove(); auto verifier_accumulator = folding_verifier.verify_folding_proof(folding_proof); - check_accumulator_target_sum_manual(prover_accumulator, true); - + EXPECT_TRUE(check_accumulator_target_sum_manual(prover_accumulator)); decide_and_verify(prover_accumulator, verifier_accumulator, true); } }; @@ -612,6 +634,12 @@ TYPED_TEST(ProtogalaxyTests, FullProtogalaxyStructuredTrace) { TestFixture::test_full_protogalaxy_structured_trace(); } + +TYPED_TEST(ProtogalaxyTests, VirtualSizeExpansion) +{ + TestFixture::test_fold_with_virtual_size_expansion(); +} + TYPED_TEST(ProtogalaxyTests, FullProtogalaxyStructuredTraceInhomogeneous) { TestFixture::test_full_protogalaxy_structured_trace_inhomogeneous_circuits(); diff --git a/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy_prover_impl.hpp b/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy_prover_impl.hpp index bd7c0ee1701c..6fbd2e47dc0a 100644 --- a/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy_prover_impl.hpp +++ b/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy_prover_impl.hpp @@ -1,5 +1,6 @@ #pragma once #include "barretenberg/common/op_count.hpp" +#include "barretenberg/plonk_honk_shared/relation_checker.hpp" #include "barretenberg/protogalaxy/protogalaxy_prover_internal.hpp" #include "barretenberg/protogalaxy/prover_verifier_shared.hpp" #include "barretenberg/relations/relation_parameters.hpp" @@ -128,6 +129,21 @@ FoldingResult ProtogalaxyProver_target_sum = perturbator_evaluation * lagranges[0] + vanishing_polynomial_at_challenge * combiner_quotient.evaluate(combiner_challenge); + // Check whether the incoming key has a larger trace overflow than the accumulator. If so, the memory structure of + // the accumulator polynomials will not be sufficient to contain the contribution from the incoming polynomials. The + // solution is to simply reverse the order or the terms in the linear combination by swapping the polynomials and + // the lagrange coefficients between the accumulator and the incoming key. + if (keys[1]->overflow_size > result.accumulator->overflow_size) { + ASSERT(DeciderProvingKeys::NUM == 2); // this mechanism is not supported for the folding of multiple keys + // DEBUG: At this point the virtual sizes of the polynomials should already agree + ASSERT(result.accumulator->proving_key.polynomials.w_l.virtual_size() == + keys[1]->proving_key.polynomials.w_l.virtual_size()); + std::swap(result.accumulator->proving_key.polynomials, keys[1]->proving_key.polynomials); // swap the polys + std::swap(lagranges[0], lagranges[1]); // swap the lagrange coefficients so the sum is unchanged + std::swap(result.accumulator->proving_key.circuit_size, keys[1]->proving_key.circuit_size); // swap circuit size + std::swap(result.accumulator->proving_key.log_circuit_size, keys[1]->proving_key.log_circuit_size); + } + // Fold the proving key polynomials for (auto& poly : result.accumulator->proving_key.polynomials.get_unshifted()) { poly *= lagranges[0]; @@ -161,14 +177,22 @@ FoldingResult ProtogalaxyProver_proving_key.circuit_size != keys_to_fold[idx + 1]->proving_key.circuit_size) { - info("ProtogalaxyProver: circuit size mismatch!"); - info("DeciderPK ", idx, " size = ", keys_to_fold[idx]->proving_key.circuit_size); - info("DeciderPK ", idx + 1, " size = ", keys_to_fold[idx + 1]->proving_key.circuit_size); - ASSERT(false); + size_t max_circuit_size = 0; + for (size_t idx = 0; idx < DeciderProvingKeys::NUM; ++idx) { + max_circuit_size = std::max(max_circuit_size, keys_to_fold[idx]->proving_key.circuit_size); + } + for (size_t idx = 0; idx < DeciderProvingKeys::NUM; ++idx) { + if (keys_to_fold[idx]->proving_key.circuit_size != max_circuit_size) { + info("ProtogalaxyProver: circuit size mismatch - increasing virtual size of key ", + idx, + " from ", + keys_to_fold[idx]->proving_key.circuit_size, + " to ", + max_circuit_size); + keys_to_fold[idx]->proving_key.polynomials.increase_polynomials_virtual_size(max_circuit_size); } } + run_oink_prover_on_each_incomplete_key(); vinfo("oink prover on each incomplete key"); diff --git a/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy_prover_internal.hpp b/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy_prover_internal.hpp index 4c5d6a9a57a1..efad2d0e0726 100644 --- a/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy_prover_internal.hpp +++ b/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy_prover_internal.hpp @@ -135,7 +135,8 @@ template class ProtogalaxyProverInternal { std::vector linearly_dependent_contribution_accumulators(num_threads); // Distribute the execution trace rows across threads so that each handles an equal number of active rows - trace_usage_tracker.construct_thread_ranges(num_threads, polynomial_size, /*use_prev_accumulator=*/true); + trace_usage_tracker.construct_thread_ranges( + num_threads, polynomial_size, /*use_prev_accumulator_tracker=*/true); parallel_for(num_threads, [&](size_t thread_idx) { const size_t start = trace_usage_tracker.thread_ranges[thread_idx].first; @@ -143,7 +144,7 @@ template class ProtogalaxyProverInternal { for (size_t idx = start; idx < end; idx++) { // The contribution is only non-trivial at a given row if the accumulator is active at that row - if (trace_usage_tracker.check_is_active(idx, /*use_prev_accumulator=*/true)) { + if (trace_usage_tracker.check_is_active(idx, true)) { const AllValues row = polynomials.get_row(idx); // Evaluate all subrelations on given row. Separator is 1 since we are not summing across rows here. const RelationEvaluations evals = @@ -343,7 +344,9 @@ template class ProtogalaxyProverInternal { constexpr bool skip_zero_computations = std::same_as; // Determine the number of threads over which to distribute the work - const size_t common_polynomial_size = keys[0]->proving_key.circuit_size; + // The polynomial size is given by the virtual size since the computation includes + // the incoming key which could have nontrivial values on the larger domain in case of overflow. + const size_t common_polynomial_size = keys[0]->proving_key.polynomials.w_l.virtual_size(); const size_t num_threads = compute_num_threads(common_polynomial_size); // Univariates are optimised for usual PG, but we need the unoptimised version for tests (it's a version that diff --git a/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy_verifier.cpp b/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy_verifier.cpp index b0a8a27a6e8c..3d7cbec843d6 100644 --- a/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy_verifier.cpp +++ b/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy_verifier.cpp @@ -107,6 +107,11 @@ std::shared_ptr ProtogalaxyVerifier next_accumulator->verification_key = std::make_shared(*accumulator->verification_key); next_accumulator->is_accumulator = true; + // Set the accumulator circuit size data based on the max of the keys being accumulated + const size_t accumulator_log_circuit_size = keys_to_fold.get_max_log_circuit_size(); + next_accumulator->verification_key->log_circuit_size = accumulator_log_circuit_size; + next_accumulator->verification_key->circuit_size = 1 << accumulator_log_circuit_size; + // Compute next folding parameters const auto [vanishing_polynomial_at_challenge, lagranges] = compute_vanishing_polynomial_and_lagrange_evaluations(combiner_challenge); diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/biggroup/biggroup.test.cpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/biggroup/biggroup.test.cpp index fd54166a9a9b..037a470649b3 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/biggroup/biggroup.test.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/biggroup/biggroup.test.cpp @@ -60,7 +60,6 @@ template class stdlib_biggroup : public testing::Test { { Builder builder; affine_element input_a(element::random_element()); - affine_element input_b(element::random_element()); element_ct a = element_ct::from_witness(&builder, input_a); a.set_origin_tag(next_submitted_value_origin_tag); @@ -76,6 +75,7 @@ template class stdlib_biggroup : public testing::Test { EXPECT_EQ(a.get_origin_tag(), first_second_third_merged_tag); #ifndef NDEBUG + affine_element input_b(element::random_element()); // Working with instant death tagged element causes an exception element_ct b = element_ct::from_witness(&builder, input_b); b.set_origin_tag(instant_death_tag); diff --git a/barretenberg/cpp/src/barretenberg/stdlib/protogalaxy_verifier/protogalaxy_recursive_verifier.cpp b/barretenberg/cpp/src/barretenberg/stdlib/protogalaxy_verifier/protogalaxy_recursive_verifier.cpp index 566a8d83397a..10739d2aa45f 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/protogalaxy_verifier/protogalaxy_recursive_verifier.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/protogalaxy_verifier/protogalaxy_recursive_verifier.cpp @@ -169,6 +169,11 @@ std::shared_ptr ProtogalaxyRecursiv perturbator_evaluation * lagranges[0] + vanishing_polynomial_at_challenge * combiner_quotient_at_challenge; accumulator->gate_challenges = update_gate_challenges(perturbator_challenge, accumulator->gate_challenges, deltas); + // Set the accumulator circuit size data based on the max of the keys being accumulated + const size_t accumulator_log_circuit_size = keys_to_fold.get_max_log_circuit_size(); + accumulator->verification_key->log_circuit_size = accumulator_log_circuit_size; + accumulator->verification_key->circuit_size = 1 << accumulator_log_circuit_size; + // Fold the relation parameters for (auto [combination, to_combine] : zip_view(accumulator->alphas, keys_to_fold.get_alphas())) { combination = linear_combination(to_combine, lagranges); diff --git a/barretenberg/cpp/src/barretenberg/stdlib/protogalaxy_verifier/recursive_decider_verification_keys.hpp b/barretenberg/cpp/src/barretenberg/stdlib/protogalaxy_verifier/recursive_decider_verification_keys.hpp index 6e75211df15f..9a2361b7a9bf 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/protogalaxy_verifier/recursive_decider_verification_keys.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/protogalaxy_verifier/recursive_decider_verification_keys.hpp @@ -35,6 +35,22 @@ template struct RecursiveDeciderVerific idx++; } } + + /** + * @brief Get the max log circuit size from the set of decider verification keys + * + * @return size_t + */ + size_t get_max_log_circuit_size() const + { + size_t max_log_circuit_size{ 0 }; + for (auto key : _data) { + max_log_circuit_size = + std::max(max_log_circuit_size, static_cast(key->verification_key->log_circuit_size)); + } + return max_log_circuit_size; + } + /** * @brief Get the precomputed commitments grouped by commitment index * @example If the commitments are grouped as in diff --git a/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/mega_flavor.hpp b/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/mega_flavor.hpp index aaac39d7a009..f7110df3ee09 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/mega_flavor.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/mega_flavor.hpp @@ -421,6 +421,13 @@ class MegaFlavor { shifted = to_be_shifted.shifted(); } } + + void increase_polynomials_virtual_size(const size_t size_in) + { + for (auto& polynomial : this->get_all()) { + polynomial.increase_virtual_size(size_in); + } + } }; /** @@ -1037,4 +1044,4 @@ class MegaFlavor { using Transcript = Transcript_; }; -} // namespace bb +} // namespace bb \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/ultra_flavor.hpp b/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/ultra_flavor.hpp index 8d69028950ff..6ff45fb338d5 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/ultra_flavor.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/ultra_flavor.hpp @@ -343,6 +343,13 @@ class UltraFlavor { shifted = to_be_shifted.shifted(); } } + + void increase_polynomials_virtual_size(const size_t size_in) + { + for (auto& polynomial : this->get_all()) { + polynomial.increase_virtual_size(size_in); + } + } }; /** * @brief The proving key is responsible for storing the polynomials used by the prover. diff --git a/barretenberg/cpp/src/barretenberg/ultra_honk/decider_keys.hpp b/barretenberg/cpp/src/barretenberg/ultra_honk/decider_keys.hpp index 49b72bbd0e29..770cede82977 100644 --- a/barretenberg/cpp/src/barretenberg/ultra_honk/decider_keys.hpp +++ b/barretenberg/cpp/src/barretenberg/ultra_honk/decider_keys.hpp @@ -108,6 +108,21 @@ template struct DeciderVerificationKeys_ { } }; + /** + * @brief Get the max log circuit size from the set of decider verification keys + * + * @return size_t + */ + size_t get_max_log_circuit_size() const + { + size_t max_log_circuit_size{ 0 }; + for (auto key : _data) { + max_log_circuit_size = + std::max(max_log_circuit_size, static_cast(key->verification_key->log_circuit_size)); + } + return max_log_circuit_size; + } + /** * @brief Get the precomputed commitments grouped by commitment index * @example If the commitments are grouped as in diff --git a/barretenberg/cpp/src/barretenberg/ultra_honk/decider_proving_key.hpp b/barretenberg/cpp/src/barretenberg/ultra_honk/decider_proving_key.hpp index c706a8e69ad3..7e114c40a3f4 100644 --- a/barretenberg/cpp/src/barretenberg/ultra_honk/decider_proving_key.hpp +++ b/barretenberg/cpp/src/barretenberg/ultra_honk/decider_proving_key.hpp @@ -48,6 +48,8 @@ template class DeciderProvingKey_ { size_t final_active_wire_idx{ 0 }; // idx of last non-trivial wire value in the trace size_t dyadic_circuit_size{ 0 }; // final power-of-2 circuit size + size_t overflow_size{ 0 }; // size of the structured execution trace overflow + DeciderProvingKey_(Circuit& circuit, TraceSettings trace_settings = {}, std::shared_ptr commitment_key = nullptr) @@ -67,6 +69,7 @@ template class DeciderProvingKey_ { circuit.blocks.set_fixed_block_sizes(trace_settings); // The structuring is set circuit.blocks.summarize(); move_structured_trace_overflow_to_overflow_block(circuit); + overflow_size = circuit.blocks.overflow.size(); dyadic_circuit_size = compute_structured_dyadic_size(circuit); // set the dyadic size accordingly } else { dyadic_circuit_size = compute_dyadic_size(circuit); // set dyadic size directly from circuit block sizes @@ -102,6 +105,7 @@ template class DeciderProvingKey_ { proving_key = ProvingKey(dyadic_circuit_size, circuit.public_inputs.size(), commitment_key); // If not using structured trace OR if using structured trace but overflow has occurred (overflow block in // use), allocate full size polys + // is_structured = false; if ((IsMegaFlavor && !is_structured) || (is_structured && circuit.blocks.has_overflow)) { // Allocate full size polynomials proving_key.polynomials = typename Flavor::ProverPolynomials(dyadic_circuit_size); @@ -256,7 +260,7 @@ template class DeciderProvingKey_ { proving_key.polynomials.lagrange_first = Polynomial( /* size=*/1, /*virtual size=*/dyadic_circuit_size, /*start_idx=*/0); - // Even though lagrange_last has a singe non-zero element, we cannot set its size to 0 as different + // Even though lagrange_last has a single non-zero element, we cannot set its size to 0 as different // keys being folded might have lagrange_last set at different indexes and folding does not work // correctly unless the polynomial is allocated in the correct range to accomodate this proving_key.polynomials.lagrange_last = Polynomial( diff --git a/barretenberg/cpp/src/barretenberg/ultra_honk/mega_honk.test.cpp b/barretenberg/cpp/src/barretenberg/ultra_honk/mega_honk.test.cpp index 132b84f5aad2..0804bb3c97bf 100644 --- a/barretenberg/cpp/src/barretenberg/ultra_honk/mega_honk.test.cpp +++ b/barretenberg/cpp/src/barretenberg/ultra_honk/mega_honk.test.cpp @@ -5,6 +5,7 @@ #include "barretenberg/circuit_checker/circuit_checker.hpp" #include "barretenberg/common/log.hpp" #include "barretenberg/goblin/mock_circuits.hpp" +#include "barretenberg/plonk_honk_shared/relation_checker.hpp" #include "barretenberg/stdlib_circuit_builders/mega_circuit_builder.hpp" #include "barretenberg/stdlib_circuit_builders/ultra_circuit_builder.hpp" #include "barretenberg/ultra_honk/merge_prover.hpp" @@ -128,6 +129,56 @@ TYPED_TEST(MegaHonkTests, BasicStructured) EXPECT_TRUE(verifier.verify_proof(proof)); } +/** + * @brief Test that increasing the virtual size of a valid set of prover polynomials still results in a valid Megahonk + * proof + * + */ +TYPED_TEST(MegaHonkTests, DynamicVirtualSizeIncrease) +{ + using Flavor = TypeParam; + typename Flavor::CircuitBuilder builder; + using Prover = UltraProver_; + using Verifier = UltraVerifier_; + + GoblinMockCircuits::construct_simple_circuit(builder); + + auto builder_copy = builder; + + // Construct and verify Honk proof using a structured trace + TraceSettings trace_settings{ SMALL_TEST_STRUCTURE_FOR_OVERFLOWS }; + auto proving_key = std::make_shared>(builder, trace_settings); + auto proving_key_copy = std::make_shared>(builder_copy, trace_settings); + auto circuit_size = proving_key->proving_key.circuit_size; + + auto doubled_circuit_size = 2 * circuit_size; + proving_key_copy->proving_key.polynomials.increase_polynomials_virtual_size(doubled_circuit_size); + // TODO(https://github.com/AztecProtocol/barretenberg/issues/1158) + // proving_key_copy->proving_key.circuit_size = doubled_circuit_size; + + Prover prover(proving_key); + auto verification_key = std::make_shared(proving_key->proving_key); + + Prover prover_copy(proving_key_copy); + auto verification_key_copy = std::make_shared(proving_key_copy->proving_key); + + for (auto [entry, entry_copy] : zip_view(verification_key->get_all(), verification_key_copy->get_all())) { + EXPECT_EQ(entry, entry_copy); + } + + Verifier verifier(verification_key); + auto proof = prover.construct_proof(); + + RelationChecker::check_all(proving_key->proving_key.polynomials, proving_key->relation_parameters); + EXPECT_TRUE(verifier.verify_proof(proof)); + + Verifier verifier_copy(verification_key_copy); + auto proof_copy = prover_copy.construct_proof(); + + RelationChecker::check_all(proving_key->proving_key.polynomials, proving_key->relation_parameters); + EXPECT_TRUE(verifier_copy.verify_proof(proof_copy)); +} + /** * @brief Test proof construction/verification for a circuit with ECC op gates, public inputs, and basic arithmetic * gates @@ -314,3 +365,49 @@ TYPED_TEST(MegaHonkTests, StructuredTraceOverflow) EXPECT_TRUE(builder.blocks.has_overflow); } } + +/** + * @brief A sanity check that a simple std::swap on a ProverPolynomials object works as expected + * @details Constuct two valid proving keys. Tamper with the prover_polynomials of one key then swap the + * prover_polynomials of the two keys. The key who received the tampered polys leads to a failed verification while the + * other succeeds. + * + */ +TYPED_TEST(MegaHonkTests, PolySwap) +{ + using Flavor = TypeParam; + using Builder = Flavor::CircuitBuilder; + + TraceSettings trace_settings{ SMALL_TEST_STRUCTURE_FOR_OVERFLOWS }; + + // Construct a simple circuit and make a copy of it + Builder builder; + GoblinMockCircuits::construct_simple_circuit(builder); + auto builder_copy = builder; + + // Construct two identical proving keys + auto proving_key_1 = std::make_shared(builder, trace_settings); + auto proving_key_2 = std::make_shared(builder_copy, trace_settings); + + // Tamper with the polys of pkey 1 in such a way that verification should fail + proving_key_1->proving_key.polynomials.w_l.at(5) = 10; + + // Swap the polys of the two proving keys; result should be pkey 1 is valid and pkey 2 should fail + std::swap(proving_key_1->proving_key.polynomials, proving_key_2->proving_key.polynomials); + + { // Verification based on pkey 1 should succeed + typename TestFixture::Prover prover(proving_key_1); + auto verification_key = std::make_shared(proving_key_1->proving_key); + typename TestFixture::Verifier verifier(verification_key); + auto proof = prover.construct_proof(); + EXPECT_TRUE(verifier.verify_proof(proof)); + } + + { // Verification based on pkey 2 should fail + typename TestFixture::Prover prover(proving_key_2); + auto verification_key = std::make_shared(proving_key_2->proving_key); + typename TestFixture::Verifier verifier(verification_key); + auto proof = prover.construct_proof(); + EXPECT_FALSE(verifier.verify_proof(proof)); + } +}