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); }