From 23aab171b17d0dfb840621a74266496ac270b3e8 Mon Sep 17 00:00:00 2001 From: ledwards2225 <98505400+ledwards2225@users.noreply.github.com> Date: Fri, 26 Apr 2024 14:03:46 -0700 Subject: [PATCH] feat: naive structured execution trace (#5853) Adds logic for executing Honk/PG with a structured execution trace, i.e. one where each block (gate type) has a static fixed size. (This size is arbitrarily set to 2^10 for now). This is achieved by simply offsetting the placement of the gate data into the polynomials based on the fixed block size in `ExecutionTrace_` rather than densely packing the blocks. The logic is tested for full proof construction/verification and folding/deciding for both UH and UGH. This includes a PG test that demonstrates that the structured trace allows for folding "inhomogeneous" circuits, i.e. circuits with a differing number of constraints. Note: This is not the end of the story since without additional optimizations this approach would be extremely inefficient in terms of memory and computation. Some of these optimizations are described in the corresponding Milestone. `ClientIVCBench/Full/6 21623 ms 16526 ms 1` --- .../execution_trace/execution_trace.cpp | 14 ++- .../execution_trace/execution_trace.hpp | 11 +- .../src/barretenberg/goblin/mock_circuits.hpp | 11 +- .../arithmetization/arithmetization.hpp | 7 +- .../protogalaxy/protogalaxy.test.cpp | 112 +++++++++++++++--- .../stdlib_circuit_builders/mock_circuits.hpp | 67 ++++++++--- .../standard_circuit_builder.hpp | 1 + .../ultra_circuit_builder.hpp | 1 + .../sumcheck/instance/prover_instance.hpp | 31 ++++- .../ultra_honk/goblin_ultra_composer.test.cpp | 37 ++++++ .../ultra_honk/ultra_composer.test.cpp | 38 ++++-- 11 files changed, 268 insertions(+), 62 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/execution_trace/execution_trace.cpp b/barretenberg/cpp/src/barretenberg/execution_trace/execution_trace.cpp index b232fa00799..79442b91bea 100644 --- a/barretenberg/cpp/src/barretenberg/execution_trace/execution_trace.cpp +++ b/barretenberg/cpp/src/barretenberg/execution_trace/execution_trace.cpp @@ -6,10 +6,10 @@ namespace bb { template -void ExecutionTrace_::populate(Builder& builder, typename Flavor::ProvingKey& proving_key) +void ExecutionTrace_::populate(Builder& builder, typename Flavor::ProvingKey& proving_key, bool is_structured) { // Construct wire polynomials, selector polynomials, and copy cycles from raw circuit data - auto trace_data = construct_trace_data(builder, proving_key.circuit_size); + auto trace_data = construct_trace_data(builder, proving_key.circuit_size, is_structured); add_wires_and_selectors_to_proving_key(trace_data, builder, proving_key); @@ -69,7 +69,8 @@ void ExecutionTrace_::add_memory_records_to_proving_key(TraceData& trace template typename ExecutionTrace_::TraceData ExecutionTrace_::construct_trace_data(Builder& builder, - size_t dyadic_circuit_size) + size_t dyadic_circuit_size, + bool is_structured) { TraceData trace_data{ dyadic_circuit_size, builder }; @@ -113,7 +114,12 @@ typename ExecutionTrace_::TraceData ExecutionTrace_::construct_t trace_data.pub_inputs_offset = offset; } - offset += block_size; + // If the trace is structured, we populate the data from the next block at a fixed block size offset + if (is_structured) { + offset += builder.FIXED_BLOCK_SIZE; + } else { // otherwise, the next block starts immediately following the previous one + offset += block_size; + } } return trace_data; } diff --git a/barretenberg/cpp/src/barretenberg/execution_trace/execution_trace.hpp b/barretenberg/cpp/src/barretenberg/execution_trace/execution_trace.hpp index 3dd3899242e..ddbc4babffe 100644 --- a/barretenberg/cpp/src/barretenberg/execution_trace/execution_trace.hpp +++ b/barretenberg/cpp/src/barretenberg/execution_trace/execution_trace.hpp @@ -39,10 +39,16 @@ template class ExecutionTrace_ { /** * @brief Given a circuit, populate a proving key with wire polys, selector polys, and sigma/id polys + * @note By default, this method constructs an exectution trace that is sorted by gate type. Optionally, it + * constructs a trace that is both sorted and "structured" in the sense that each block/gate-type has a fixed amount + * of space within the wire polynomials, regardless of how many actual constraints of each type exist. This is + * useful primarily for folding since it guarantees that the set of relations that must be executed at each row is + * consistent across all instances. * * @param builder + * @param is_structured whether or not the trace is to be structured with a fixed block size */ - static void populate(Builder& builder, ProvingKey&); + static void populate(Builder& builder, ProvingKey&, bool is_structured = false); private: /** @@ -78,9 +84,10 @@ template class ExecutionTrace_ { * * @param builder * @param dyadic_circuit_size + * @param is_structured whether or not the trace is to be structured with a fixed block size * @return TraceData */ - static TraceData construct_trace_data(Builder& builder, size_t dyadic_circuit_size); + static TraceData construct_trace_data(Builder& builder, size_t dyadic_circuit_size, bool is_structured = false); /** * @brief Populate the public inputs block diff --git a/barretenberg/cpp/src/barretenberg/goblin/mock_circuits.hpp b/barretenberg/cpp/src/barretenberg/goblin/mock_circuits.hpp index cb87096063e..e6457060f6f 100644 --- a/barretenberg/cpp/src/barretenberg/goblin/mock_circuits.hpp +++ b/barretenberg/cpp/src/barretenberg/goblin/mock_circuits.hpp @@ -116,7 +116,7 @@ class GoblinMockCircuits { * * @param builder */ - static void construct_simple_circuit(GoblinUltraBuilder& builder) + static void add_some_ecc_op_gates(GoblinUltraBuilder& builder) { // Add some arbitrary ecc op gates for (size_t i = 0; i < 3; ++i) { @@ -127,7 +127,16 @@ class GoblinMockCircuits { } // queues the result of the preceding ECC builder.queue_ecc_eq(); // should be eq and reset + } + /** + * @brief Generate a simple test circuit with some ECC op gates and conventional arithmetic gates + * + * @param builder + */ + static void construct_simple_circuit(GoblinUltraBuilder& builder) + { + add_some_ecc_op_gates(builder); MockCircuits::construct_arithmetic_circuit(builder); } diff --git a/barretenberg/cpp/src/barretenberg/plonk_honk_shared/arithmetization/arithmetization.hpp b/barretenberg/cpp/src/barretenberg/plonk_honk_shared/arithmetization/arithmetization.hpp index 21490b283cf..c864af55e7c 100644 --- a/barretenberg/cpp/src/barretenberg/plonk_honk_shared/arithmetization/arithmetization.hpp +++ b/barretenberg/cpp/src/barretenberg/plonk_honk_shared/arithmetization/arithmetization.hpp @@ -119,6 +119,7 @@ template class UltraArith { public: static constexpr size_t NUM_WIRES = 4; static constexpr size_t NUM_SELECTORS = 11; + static constexpr size_t FIXED_BLOCK_SIZE = 1 << 10; // Size of each block in a structured trace (arbitrary for now) using FF = FF_; class UltraTraceBlock : public ExecutionTraceBlock { @@ -165,7 +166,7 @@ template class UltraArith { auto get() { return RefArray{ pub_inputs, arithmetic, delta_range, elliptic, aux, lookup }; } - void summarize() + void summarize() const { info("Gate blocks summary:"); info("pub inputs:\t", pub_inputs.size()); @@ -196,6 +197,8 @@ template class UltraHonkArith { public: static constexpr size_t NUM_WIRES = 4; static constexpr size_t NUM_SELECTORS = 14; + static constexpr size_t FIXED_BLOCK_SIZE = 1 << 10; // Size of each block in a structured trace (arbitrary for now) + using FF = FF_; class UltraHonkTraceBlock : public ExecutionTraceBlock { @@ -279,7 +282,7 @@ template class UltraHonkArith { aux, lookup, busread, poseidon_external, poseidon_internal }; } - void summarize() + void summarize() const { info("Gate blocks summary:"); info("goblin ecc op:\t", ecc_op.size()); diff --git a/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy.test.cpp b/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy.test.cpp index bf72f4ca5eb..2443df21615 100644 --- a/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy.test.cpp +++ b/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy.test.cpp @@ -4,6 +4,7 @@ #include "barretenberg/protogalaxy/decider_verifier.hpp" #include "barretenberg/protogalaxy/protogalaxy_prover.hpp" #include "barretenberg/protogalaxy/protogalaxy_verifier.hpp" +#include "barretenberg/stdlib_circuit_builders/mock_circuits.hpp" #include @@ -43,24 +44,27 @@ template class ProtoGalaxyTests : public testing::Test { static void construct_circuit(Builder& builder) { + MockCircuits::add_arithmetic_gates(builder); if constexpr (IsGoblinFlavor) { - GoblinMockCircuits::construct_simple_circuit(builder); - } else { - FF a = FF::random_element(); - FF b = FF::random_element(); - FF c = FF::random_element(); - FF d = a + b + c; - uint32_t a_idx = builder.add_public_variable(a); - uint32_t b_idx = builder.add_variable(b); - uint32_t c_idx = builder.add_variable(c); - 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) }); + GoblinMockCircuits::add_some_ecc_op_gates(builder); } } + // Construct prover and verifier instance for a provided circuit and add to tuple + static void construct_prover_and_verifier_instance(TupleOfInstances& instances, + Builder& builder, + bool structured = false) + { + + auto prover_instance = std::make_shared(builder, structured); + auto verification_key = std::make_shared(prover_instance->proving_key); + auto verifier_instance = std::make_shared(verification_key); + get<0>(instances).emplace_back(prover_instance); + get<1>(instances).emplace_back(verifier_instance); + } + // constructs num_insts number of prover and verifier instances - static TupleOfInstances construct_instances(size_t num_insts) + static TupleOfInstances construct_instances(size_t num_insts, bool structured = false) { TupleOfInstances instances; // TODO(https://github.com/AztecProtocol/barretenberg/issues/938): Parallelize this loop @@ -68,11 +72,7 @@ template class ProtoGalaxyTests : public testing::Test { auto builder = typename Flavor::CircuitBuilder(); construct_circuit(builder); - auto prover_instance = std::make_shared(builder); - auto verification_key = std::make_shared(prover_instance->proving_key); - auto verifier_instance = std::make_shared(verification_key); - get<0>(instances).emplace_back(prover_instance); - get<1>(instances).emplace_back(verifier_instance); + construct_prover_and_verifier_instance(instances, builder, structured); } return instances; } @@ -332,6 +332,73 @@ template class ProtoGalaxyTests : public testing::Test { decide_and_verify(prover_accumulator_2, verifier_accumulator_2, true); } + /** + * @brief Testing two valid rounds of folding followed by the decider for a structured trace. + * + */ + static void test_full_protogalaxy_structured_trace() + { + bool structured = true; + TupleOfInstances instances = construct_instances(2, structured); + + auto [prover_accumulator, verifier_accumulator] = fold_and_verify(get<0>(instances), get<1>(instances)); + check_accumulator_target_sum_manual(prover_accumulator, true); + + TupleOfInstances instances_2 = construct_instances(1, structured); // just one set of prover/verifier instances + + auto [prover_accumulator_2, verifier_accumulator_2] = fold_and_verify( + { prover_accumulator, get<0>(instances_2)[0] }, { verifier_accumulator, get<1>(instances_2)[0] }); + check_accumulator_target_sum_manual(prover_accumulator_2, true); + info(prover_accumulator_2->proving_key.circuit_size); + decide_and_verify(prover_accumulator_2, verifier_accumulator_2, 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 + * constraints, which should be automatically handled by the structured trace + * + */ + static void test_full_protogalaxy_structured_trace_inhomogeneous_circuits() + { + bool structured = true; + + // Construct three circuits to be folded, each with a different number of constraints + Builder builder1; + Builder builder2; + Builder builder3; + construct_circuit(builder1); + construct_circuit(builder2); + construct_circuit(builder3); + + // Create inhomogenous circuits by adding a different number of add gates to each + MockCircuits::add_arithmetic_gates(builder1, 10); + MockCircuits::add_arithmetic_gates(builder2, 100); + MockCircuits::add_arithmetic_gates(builder3, 1000); + + // Construct the Prover/Verifier instances for the first two circuits + TupleOfInstances instances; + construct_prover_and_verifier_instance(instances, builder1, structured); + construct_prover_and_verifier_instance(instances, builder2, structured); + + // Fold the first two instances + auto [prover_accumulator, verifier_accumulator] = fold_and_verify(get<0>(instances), get<1>(instances)); + check_accumulator_target_sum_manual(prover_accumulator, true); + + // Construct the Prover/Verifier instance for the third circuit + TupleOfInstances instances_2; + construct_prover_and_verifier_instance(instances_2, builder3, structured); + + // Fold 3rd instance into accumulator + auto [prover_accumulator_2, verifier_accumulator_2] = fold_and_verify( + { prover_accumulator, get<0>(instances_2)[0] }, { verifier_accumulator, get<1>(instances_2)[0] }); + check_accumulator_target_sum_manual(prover_accumulator_2, true); + info(prover_accumulator_2->proving_key.circuit_size); + + // Decide on final accumulator + decide_and_verify(prover_accumulator_2, verifier_accumulator_2, true); + } + /** * @brief Ensure tampering a commitment and then calling the decider causes the decider verification to fail. * @@ -431,6 +498,15 @@ TYPED_TEST(ProtoGalaxyTests, FullProtogalaxyTest) TestFixture::test_full_protogalaxy(); } +TYPED_TEST(ProtoGalaxyTests, FullProtogalaxyStructuredTrace) +{ + TestFixture::test_full_protogalaxy_structured_trace(); +} +TYPED_TEST(ProtoGalaxyTests, FullProtogalaxyStructuredTraceInhomogeneous) +{ + TestFixture::test_full_protogalaxy_structured_trace_inhomogeneous_circuits(); +} + TYPED_TEST(ProtoGalaxyTests, TamperedCommitment) { TestFixture::test_tampered_commitment(); diff --git a/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/mock_circuits.hpp b/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/mock_circuits.hpp index cb523ce7186..36937d8c347 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/mock_circuits.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/mock_circuits.hpp @@ -10,20 +10,16 @@ class MockCircuits { using Point = Curve::AffineElement; /** - * @brief Populate a builder with a specified number of arithmetic gates; includes a PI + * @brief Add a specified number of arithmetic gates (with public inputs) to the provided circuit * * @param builder * @param num_gates */ template - static void construct_arithmetic_circuit(Builder& builder, const size_t target_log2_dyadic_size = 4) + static void add_arithmetic_gates_with_public_inputs(Builder& builder, const size_t num_gates = 4) { - const size_t target_dyadic_size = 1 << target_log2_dyadic_size; - const size_t num_preamble_gates = builder.num_gates; - ASSERT(target_dyadic_size >= num_preamble_gates); - // For good measure, include a gate with some public inputs - if (target_dyadic_size > num_preamble_gates) { + for (size_t i = 0; i < num_gates; ++i) { FF a = FF::random_element(); FF b = FF::random_element(); FF c = FF::random_element(); @@ -35,6 +31,48 @@ class MockCircuits { builder.create_big_add_gate({ a_idx, b_idx, c_idx, d_idx, FF(1), FF(1), FF(1), FF(-1), FF(0) }); } + } + + /** + * @brief Add a specified number of arithmetic gates to the provided circuit + * + * @param builder + * @param num_gates + */ + template static void add_arithmetic_gates(Builder& builder, const size_t num_gates = 4) + { + // For good measure, include a gate with some public inputs + for (size_t i = 0; i < num_gates; ++i) { + FF a = FF::random_element(); + FF b = FF::random_element(); + FF c = FF::random_element(); + FF d = a + b + c; + uint32_t a_idx = builder.add_variable(a); + uint32_t b_idx = builder.add_variable(b); + uint32_t c_idx = builder.add_variable(c); + 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) }); + } + } + + /** + * @brief Populate a builder with a specified number of arithmetic gates; includes a PI + * + * @param builder + * @param num_gates + */ + template + static void construct_arithmetic_circuit(Builder& builder, const size_t target_log2_dyadic_size = 4) + { + const size_t target_dyadic_size = 1 << target_log2_dyadic_size; + const size_t num_preamble_gates = builder.num_gates; + ASSERT(target_dyadic_size >= num_preamble_gates); + + // For good measure, include a gate with some public inputs + if (target_dyadic_size > num_preamble_gates) { + add_arithmetic_gates_with_public_inputs(builder, 1); + } // A proper treatment of this would dynamically calculate how many gates to add given static information about // Builder, but a major overhaul of the execution trace is underway, so we just elect to use a hack. Namely, for @@ -46,19 +84,10 @@ class MockCircuits { // to prevent underflow of the loop upper limit; target size >= 16 should suffice ASSERT(target_dyadic_size > OFFSET_HACK + num_preamble_gates); - // Add arbitrary arithmetic gates to obtain a total of num_gates-many gates - FF a = FF::random_element(); - FF b = FF::random_element(); - FF c = FF::random_element(); - FF d = a + b + c; - uint32_t a_idx = builder.add_variable(a); - uint32_t b_idx = builder.add_variable(b); - uint32_t c_idx = builder.add_variable(c); - uint32_t d_idx = builder.add_variable(d); + size_t num_gates_to_add = target_dyadic_size - OFFSET_HACK - 1 - num_preamble_gates; - for (size_t i = 0; i < target_dyadic_size - OFFSET_HACK - 1 - num_preamble_gates; ++i) { - builder.create_big_add_gate({ a_idx, b_idx, c_idx, d_idx, FF(1), FF(1), FF(1), FF(-1), FF(0) }); - } + // Add arbitrary arithmetic gates to obtain a total of num_gates-many gates + add_arithmetic_gates(builder, num_gates_to_add); } /** diff --git a/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/standard_circuit_builder.hpp b/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/standard_circuit_builder.hpp index e6ede171ffa..a421a10adaa 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/standard_circuit_builder.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/standard_circuit_builder.hpp @@ -15,6 +15,7 @@ template class StandardCircuitBuilder_ : public CircuitBuilderBase using Arithmetization = StandardArith; using GateBlocks = typename Arithmetization::TraceBlocks; static constexpr size_t NUM_WIRES = Arithmetization::NUM_WIRES; + static constexpr size_t FIXED_BLOCK_SIZE = 0; // not used, for compatibility only // Keeping NUM_WIRES, at least temporarily, for backward compatibility static constexpr size_t program_width = Arithmetization::NUM_WIRES; static constexpr size_t num_selectors = Arithmetization::NUM_SELECTORS; diff --git a/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/ultra_circuit_builder.hpp b/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/ultra_circuit_builder.hpp index feecbf0938d..90dde82d76c 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/ultra_circuit_builder.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/ultra_circuit_builder.hpp @@ -33,6 +33,7 @@ class UltraCircuitBuilder_ : public CircuitBuilderBase class ProverInstance_ { std::vector gate_challenges; FF target_sum; - ProverInstance_(Circuit& circuit) + ProverInstance_(Circuit& circuit, bool is_structured = false) { BB_OP_COUNT_TIME_NAME("ProverInstance(Circuit&)"); circuit.add_gates_to_ensure_all_polys_are_non_zero(); circuit.finalize_circuit(); + // If using a structured trace, ensure that no block exceeds the fixed size + if (is_structured) { + for (auto& block : circuit.blocks.get()) { + ASSERT(block.size() <= circuit.FIXED_BLOCK_SIZE); + } + } + + // TODO(https://github.com/AztecProtocol/barretenberg/issues/905): This is adding ops to the op queue but NOT to + // the circuit, meaning the ECCVM/Translator will use different ops than the main circuit. This will lead to + // failure once https://github.com/AztecProtocol/barretenberg/issues/746 is resolved. if constexpr (IsGoblinFlavor) { circuit.op_queue->append_nonzero_ops(); } - dyadic_circuit_size = compute_dyadic_size(circuit); + if (is_structured) { // Compute dyadic size based on a structured trace with fixed block size + dyadic_circuit_size = compute_structured_dyadic_size(circuit); + } else { // Otherwise, compute conventional dyadic circuit size + dyadic_circuit_size = compute_dyadic_size(circuit); + } proving_key = std::move(ProvingKey(dyadic_circuit_size, circuit.public_inputs.size())); // Construct and add to proving key the wire, selector and copy constraint polynomials - Trace::populate(circuit, proving_key); + Trace::populate(circuit, proving_key, is_structured); // If Goblin, construct the databus polynomials if constexpr (IsGoblinFlavor) { @@ -95,6 +109,17 @@ template class ProverInstance_ { size_t compute_dyadic_size(Circuit&); + /** + * @brief Compute dyadic size based on a structured trace with fixed block size + * + */ + size_t compute_structured_dyadic_size(Circuit& builder) + { + size_t num_blocks = builder.blocks.get().size(); + size_t minimum_size = num_blocks * builder.FIXED_BLOCK_SIZE; + return builder.get_circuit_subgroup_size(minimum_size); + } + void construct_databus_polynomials(Circuit&) requires IsGoblinFlavor; diff --git a/barretenberg/cpp/src/barretenberg/ultra_honk/goblin_ultra_composer.test.cpp b/barretenberg/cpp/src/barretenberg/ultra_honk/goblin_ultra_composer.test.cpp index eabc2edbb27..dc5f8e76dc0 100644 --- a/barretenberg/cpp/src/barretenberg/ultra_honk/goblin_ultra_composer.test.cpp +++ b/barretenberg/cpp/src/barretenberg/ultra_honk/goblin_ultra_composer.test.cpp @@ -59,6 +59,43 @@ class GoblinUltraHonkComposerTests : public ::testing::Test { }; } // namespace +/** + * @brief Test proof construction/verification for a circuit with ECC op gates, public inputs, and basic arithmetic + * gates + * + */ +TEST_F(GoblinUltraHonkComposerTests, Basic) +{ + GoblinUltraCircuitBuilder builder; + + GoblinMockCircuits::construct_simple_circuit(builder); + + // Construct and verify Honk proof + bool honk_verified = construct_and_verify_honk_proof(builder); + EXPECT_TRUE(honk_verified); +} + +/** + * @brief Test proof construction/verification for a structured execution trace + * + */ +TEST_F(GoblinUltraHonkComposerTests, BasicStructured) +{ + GoblinUltraCircuitBuilder builder; + + GoblinMockCircuits::construct_simple_circuit(builder); + + // Construct and verify Honk proof using a structured trace + bool structured = true; + auto instance = std::make_shared>(builder, structured); + builder.blocks.summarize(); + GoblinUltraProver prover(instance); + auto verification_key = std::make_shared(instance->proving_key); + GoblinUltraVerifier verifier(verification_key); + auto proof = prover.construct_proof(); + EXPECT_TRUE(verifier.verify_proof(proof)); +} + /** * @brief Test proof construction/verification for a circuit with ECC op gates, public inputs, and basic arithmetic * gates diff --git a/barretenberg/cpp/src/barretenberg/ultra_honk/ultra_composer.test.cpp b/barretenberg/cpp/src/barretenberg/ultra_honk/ultra_composer.test.cpp index c6015ca160c..a4d5cf5e5d8 100644 --- a/barretenberg/cpp/src/barretenberg/ultra_honk/ultra_composer.test.cpp +++ b/barretenberg/cpp/src/barretenberg/ultra_honk/ultra_composer.test.cpp @@ -4,6 +4,7 @@ #include "barretenberg/plonk_honk_shared/library/grand_product_delta.hpp" #include "barretenberg/relations/permutation_relation.hpp" #include "barretenberg/relations/relation_parameters.hpp" +#include "barretenberg/stdlib_circuit_builders/mock_circuits.hpp" #include "barretenberg/stdlib_circuit_builders/plookup_tables/fixed_base/fixed_base.hpp" #include "barretenberg/stdlib_circuit_builders/plookup_tables/types.hpp" #include "barretenberg/stdlib_circuit_builders/ultra_circuit_builder.hpp" @@ -85,6 +86,29 @@ TEST_F(UltraHonkComposerTests, ANonZeroPolynomialIsAGoodPolynomial) } } +/** + * @brief Test proof construction/verification for a structured execution trace + * + */ +TEST_F(UltraHonkComposerTests, StructuredTrace) +{ + auto builder = UltraCircuitBuilder(); + size_t num_gates = 3; + + // Add some arbitrary arithmetic gates that utilize public inputs + MockCircuits::add_arithmetic_gates_with_public_inputs(builder, num_gates); + + // Construct an instance with a structured execution trace + bool structured = true; + auto instance = std::make_shared(builder, structured); + info(instance->proving_key.circuit_size); + UltraProver prover(instance); + auto verification_key = std::make_shared(instance->proving_key); + UltraVerifier verifier(verification_key); + auto proof = prover.construct_proof(); + EXPECT_TRUE(verifier.verify_proof(proof)); +} + /** * @brief Test simple circuit with public inputs * @@ -95,19 +119,7 @@ TEST_F(UltraHonkComposerTests, PublicInputs) size_t num_gates = 10; // Add some arbitrary arithmetic gates that utilize public inputs - for (size_t i = 0; i < num_gates; ++i) { - fr a = fr::random_element(); - uint32_t a_idx = builder.add_public_variable(a); - - fr b = fr::random_element(); - fr c = fr::random_element(); - fr d = a + b + c; - uint32_t b_idx = builder.add_variable(b); - uint32_t c_idx = builder.add_variable(c); - uint32_t d_idx = builder.add_variable(d); - - builder.create_big_add_gate({ a_idx, b_idx, c_idx, d_idx, fr(1), fr(1), fr(1), fr(-1), fr(0) }); - } + MockCircuits::add_arithmetic_gates_with_public_inputs(builder, num_gates); prove_and_verify(builder, /*expected_result=*/true); }