diff --git a/barretenberg/cpp/scripts/benchmark.sh b/barretenberg/cpp/scripts/benchmark.sh index 2c51a0c0485..93b96377173 100755 --- a/barretenberg/cpp/scripts/benchmark.sh +++ b/barretenberg/cpp/scripts/benchmark.sh @@ -2,6 +2,7 @@ set -eu BENCHMARK=${1:-goblin_bench} +COMMAND=${2:-./bin/$BENCHMARK} # Move above script dir. cd $(dirname $0)/.. @@ -11,4 +12,6 @@ cmake --preset clang16 cmake --build --preset clang16 --target $BENCHMARK cd build -./bin/$BENCHMARK \ No newline at end of file +# Consistency with _wasm.sh targets / shorter $COMMAND. +cp ./bin/$BENCHMARK . +$COMMAND \ No newline at end of file diff --git a/barretenberg/cpp/scripts/benchmark_wasm.sh b/barretenberg/cpp/scripts/benchmark_wasm.sh index f4288604f7a..a7565485bdf 100755 --- a/barretenberg/cpp/scripts/benchmark_wasm.sh +++ b/barretenberg/cpp/scripts/benchmark_wasm.sh @@ -2,6 +2,7 @@ set -eu BENCHMARK=${1:-goblin_bench} +COMMAND=${2:-./bin/$BENCHMARK} # Move above script dir. cd $(dirname $0)/.. @@ -11,4 +12,6 @@ cmake --preset wasm-bench cmake --build --preset wasm-bench --target $BENCHMARK cd build-wasm-bench -wasmtime run -Wthreads=y -Sthreads=y ./bin/$BENCHMARK \ No newline at end of file +# Consistency with _wasm.sh targets / shorter $COMMAND. +cp ./bin/$BENCHMARK . +wasmtime run -Wthreads=y -Sthreads=y $COMMAND \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/benchmark/goblin_bench/CMakeLists.txt b/barretenberg/cpp/src/barretenberg/benchmark/goblin_bench/CMakeLists.txt index b7bd08f19cb..41d407c044d 100644 --- a/barretenberg/cpp/src/barretenberg/benchmark/goblin_bench/CMakeLists.txt +++ b/barretenberg/cpp/src/barretenberg/benchmark/goblin_bench/CMakeLists.txt @@ -1 +1 @@ -barretenberg_module(goblin_bench ultra_honk eccvm stdlib_recursion) \ No newline at end of file +barretenberg_module(goblin_bench ultra_honk eccvm stdlib_recursion stdlib_sha256 stdlib_merkle_tree stdlib_primitives) diff --git a/barretenberg/cpp/src/barretenberg/benchmark/goblin_bench/goblin.bench.cpp b/barretenberg/cpp/src/barretenberg/benchmark/goblin_bench/goblin.bench.cpp index 4389074c83b..9c8fd4e51b7 100644 --- a/barretenberg/cpp/src/barretenberg/benchmark/goblin_bench/goblin.bench.cpp +++ b/barretenberg/cpp/src/barretenberg/benchmark/goblin_bench/goblin.bench.cpp @@ -11,128 +11,143 @@ using namespace benchmark; using namespace bb; namespace { -void goblin_full(State& state) noexcept -{ - bb::srs::init_crs_factory("../srs_db/ignition"); - bb::srs::init_grumpkin_crs_factory("../srs_db/grumpkin"); - Goblin goblin; +class GoblinBench : public benchmark::Fixture { + public: + Goblin::AccumulationOutput kernel_accum; - // Construct an initial circuit; its proof will be recursively verified by the first kernel - GoblinUltraCircuitBuilder initial_circuit{ goblin.op_queue }; - GoblinMockCircuits::construct_simple_initial_circuit(initial_circuit); - Goblin::AccumulationOutput kernel_input = goblin.accumulate(initial_circuit); + // Number of function circuits to accumulate(based on Zacs target numbers) + static constexpr size_t NUM_ITERATIONS_MEDIUM_COMPLEXITY = 6; - Goblin::Proof proof; - for (auto _ : state) { - // Construct a series of simple Goblin circuits; generate and verify their proofs - size_t NUM_CIRCUITS = 1 << static_cast(state.range(0)); + void SetUp([[maybe_unused]] const ::benchmark::State& state) override + { + bb::srs::init_crs_factory("../srs_db/ignition"); + bb::srs::init_grumpkin_crs_factory("../srs_db/grumpkin"); + } + + /** + * @brief Perform a specified number of function circuit accumulation rounds + * @details Each round "accumulates" a mock function circuit and a mock kernel circuit. Each round thus consists of + * the generation of two circuits, two UGH proofs and two Merge proofs. To match the sizes called out in the spec + * (https://github.com/AztecProtocol/aztec-packages/blob/master/yellow-paper/docs/cryptography/performance-targets.md) + * we set the size of the function circuit to be 2^17 except for the first one which is 2^19. + * + * @param state + */ + void perform_goblin_accumulation_rounds(State& state, Goblin& goblin) + { + auto NUM_CIRCUITS = static_cast(state.range(0)); for (size_t circuit_idx = 0; circuit_idx < NUM_CIRCUITS; ++circuit_idx) { - // Construct a circuit with logic resembling that of the "kernel circuit" - GoblinUltraCircuitBuilder circuit_builder{ goblin.op_queue }; - GoblinMockCircuits::construct_mock_kernel_circuit(circuit_builder, kernel_input); - // Construct proof of the current kernel circuit to be recursively verified by the next one - kernel_input = goblin.accumulate(circuit_builder); - } + // Construct and accumulate a mock function circuit + GoblinUltraCircuitBuilder function_circuit{ goblin.op_queue }; + // On the first iteration construct a "large" function circuit (2^19), otherwise medium (2^17) + GoblinMockCircuits::construct_mock_function_circuit(function_circuit, /*large=*/circuit_idx == 0); + auto function_accum = goblin.accumulate(function_circuit); - proof = goblin.prove(); - // Verify the final ultra proof + // Construct and accumulate the mock kernel circuit + // Note: in first round, kernel_accum is empty since there is no previous kernel to recursively verify + GoblinUltraCircuitBuilder circuit_builder{ goblin.op_queue }; + GoblinMockCircuits::construct_mock_kernel_circuit(circuit_builder, function_accum, kernel_accum); + kernel_accum = goblin.accumulate(circuit_builder); + } } - honk::GoblinUltraVerifier ultra_verifier{ kernel_input.verification_key }; - ultra_verifier.verify_proof(kernel_input.proof); - // Verify the goblin proof (eccvm, translator, merge) - goblin.verify(proof); -} +}; -void goblin_accumulate(State& state) noexcept +/** + * @brief Benchmark the full Goblin IVC protocol + * + */ +BENCHMARK_DEFINE_F(GoblinBench, GoblinFull)(benchmark::State& state) { - bb::srs::init_crs_factory("../srs_db/ignition"); - bb::srs::init_grumpkin_crs_factory("../srs_db/grumpkin"); - Goblin goblin; - // Construct an initial circuit; its proof will be recursively verified by the first kernel - GoblinUltraCircuitBuilder initial_circuit{ goblin.op_queue }; - GoblinMockCircuits::construct_simple_initial_circuit(initial_circuit); - Goblin::AccumulationOutput kernel_input = goblin.accumulate(initial_circuit); + // TODO(https://github.com/AztecProtocol/barretenberg/issues/723): Simply populate the OpQueue with some data + // and corresponding commitments so the merge protocol has "prev" data into which it can accumulate + GoblinMockCircuits::perform_op_queue_interactions_for_mock_first_circuit(goblin.op_queue); - // Construct a series of simple Goblin circuits; generate and verify their proofs - size_t NUM_CIRCUITS = 1 << static_cast(state.range(0)); for (auto _ : state) { - for (size_t circuit_idx = 0; circuit_idx < NUM_CIRCUITS; ++circuit_idx) { - // Construct a circuit with logic resembling that of the "kernel circuit" - GoblinUltraCircuitBuilder circuit_builder{ goblin.op_queue }; - GoblinMockCircuits::construct_mock_kernel_circuit(circuit_builder, kernel_input); + // Perform a specified number of iterations of function/kernel accumulation + perform_goblin_accumulation_rounds(state, goblin); - // Construct proof of the current kernel circuit to be recursively verified by the next one - kernel_input = goblin.accumulate(circuit_builder); - } + // Construct proofs for ECCVM and Translator + goblin.prove(); } } -void goblin_eccvm_prove(State& state) noexcept +/** + * @brief Benchmark only the accumulation rounds + * + */ +BENCHMARK_DEFINE_F(GoblinBench, GoblinAccumulate)(benchmark::State& state) { - bb::srs::init_crs_factory("../srs_db/ignition"); - bb::srs::init_grumpkin_crs_factory("../srs_db/grumpkin"); - Goblin goblin; - // Construct an initial circuit; its proof will be recursively verified by the first kernel - GoblinUltraCircuitBuilder initial_circuit{ goblin.op_queue }; - GoblinMockCircuits::construct_simple_initial_circuit(initial_circuit); - Goblin::AccumulationOutput kernel_input = goblin.accumulate(initial_circuit); - - // Construct a series of simple Goblin circuits; generate and verify their proofs - size_t NUM_CIRCUITS = 1 << static_cast(state.range(0)); - for (size_t circuit_idx = 0; circuit_idx < NUM_CIRCUITS; ++circuit_idx) { - // Construct a circuit with logic resembling that of the "kernel circuit" - GoblinUltraCircuitBuilder circuit_builder{ goblin.op_queue }; - GoblinMockCircuits::construct_mock_kernel_circuit(circuit_builder, kernel_input); + // TODO(https://github.com/AztecProtocol/barretenberg/issues/723) + GoblinMockCircuits::perform_op_queue_interactions_for_mock_first_circuit(goblin.op_queue); - // Construct proof of the current kernel circuit to be recursively verified by the next one - kernel_input = goblin.accumulate(circuit_builder); + // Perform a specified number of iterations of function/kernel accumulation + for (auto _ : state) { + perform_goblin_accumulation_rounds(state, goblin); } +} + +/** + * @brief Benchmark only the ECCVM component + * + */ +BENCHMARK_DEFINE_F(GoblinBench, GoblinECCVMProve)(benchmark::State& state) +{ + Goblin goblin; + // TODO(https://github.com/AztecProtocol/barretenberg/issues/723) + GoblinMockCircuits::perform_op_queue_interactions_for_mock_first_circuit(goblin.op_queue); + + // Perform a specified number of iterations of function/kernel accumulation + perform_goblin_accumulation_rounds(state, goblin); + + // Prove ECCVM only for (auto _ : state) { goblin.prove_eccvm(); } } -void goblin_translator_prove(State& state) noexcept +/** + * @brief Benchmark only the Translator component + * + */ +BENCHMARK_DEFINE_F(GoblinBench, GoblinTranslatorProve)(benchmark::State& state) { - bb::srs::init_crs_factory("../srs_db/ignition"); - bb::srs::init_grumpkin_crs_factory("../srs_db/grumpkin"); - Goblin goblin; - // Construct an initial circuit; its proof will be recursively verified by the first kernel - GoblinUltraCircuitBuilder initial_circuit{ goblin.op_queue }; - GoblinMockCircuits::construct_simple_initial_circuit(initial_circuit); - Goblin::AccumulationOutput kernel_input = goblin.accumulate(initial_circuit); + // TODO(https://github.com/AztecProtocol/barretenberg/issues/723) + GoblinMockCircuits::perform_op_queue_interactions_for_mock_first_circuit(goblin.op_queue); - // Construct a series of simple Goblin circuits; generate and verify their proofs - size_t NUM_CIRCUITS = 1 << static_cast(state.range(0)); - for (size_t circuit_idx = 0; circuit_idx < NUM_CIRCUITS; ++circuit_idx) { - // Construct a circuit with logic resembling that of the "kernel circuit" - GoblinUltraCircuitBuilder circuit_builder{ goblin.op_queue }; - GoblinMockCircuits::construct_mock_kernel_circuit(circuit_builder, kernel_input); - - // Construct proof of the current kernel circuit to be recursively verified by the next one - kernel_input = goblin.accumulate(circuit_builder); - } + // Perform a specified number of iterations of function/kernel accumulation + perform_goblin_accumulation_rounds(state, goblin); + // Prove ECCVM (unmeasured) and Translator (measured) goblin.prove_eccvm(); for (auto _ : state) { goblin.prove_translator(); } } -} // namespace +#define ARGS \ + Arg(GoblinBench::NUM_ITERATIONS_MEDIUM_COMPLEXITY) \ + ->Arg(1 << 0) \ + ->Arg(1 << 1) \ + ->Arg(1 << 2) \ + ->Arg(1 << 3) \ + ->Arg(1 << 4) \ + ->Arg(1 << 5) \ + ->Arg(1 << 6) + +BENCHMARK_REGISTER_F(GoblinBench, GoblinFull)->Unit(benchmark::kMillisecond)->ARGS; +BENCHMARK_REGISTER_F(GoblinBench, GoblinAccumulate)->Unit(benchmark::kMillisecond)->ARGS; +BENCHMARK_REGISTER_F(GoblinBench, GoblinECCVMProve)->Unit(benchmark::kMillisecond)->ARGS; +BENCHMARK_REGISTER_F(GoblinBench, GoblinTranslatorProve)->Unit(benchmark::kMillisecond)->ARGS; -BENCHMARK(goblin_full)->Unit(kMillisecond)->DenseRange(0, 7); -BENCHMARK(goblin_accumulate)->Unit(kMillisecond)->DenseRange(0, 7); -BENCHMARK(goblin_eccvm_prove)->Unit(kMillisecond)->DenseRange(0, 7); -BENCHMARK(goblin_translator_prove)->Unit(kMillisecond)->DenseRange(0, 7); +} // namespace BENCHMARK_MAIN(); diff --git a/barretenberg/cpp/src/barretenberg/benchmark/ultra_bench/mock_proofs.hpp b/barretenberg/cpp/src/barretenberg/benchmark/ultra_bench/mock_proofs.hpp index 8e7284729b6..369a5bd8576 100644 --- a/barretenberg/cpp/src/barretenberg/benchmark/ultra_bench/mock_proofs.hpp +++ b/barretenberg/cpp/src/barretenberg/benchmark/ultra_bench/mock_proofs.hpp @@ -2,6 +2,7 @@ #include #include +#include "barretenberg/goblin/mock_circuits.hpp" #include "barretenberg/plonk/composer/standard_composer.hpp" #include "barretenberg/plonk/composer/ultra_composer.hpp" #include "barretenberg/proof_system/types/circuit_type.hpp" @@ -46,121 +47,6 @@ template void generate_basic_arithmetic_circuit(Builder& buil } } -/** - * @brief Generate test circuit with specified number of sha256 hashes - * - * @param builder - * @param num_iterations - */ -template void generate_sha256_test_circuit(Builder& builder, size_t num_iterations) -{ - std::string in; - in.resize(32); - stdlib::packed_byte_array input(&builder, in); - for (size_t i = 0; i < num_iterations; i++) { - input = stdlib::sha256(input); - } -} - -/** - * @brief Generate test circuit with specified number of keccak hashes - * - * @param builder - * @param num_iterations - */ -template void generate_keccak_test_circuit(Builder& builder, size_t num_iterations) -{ - std::string in = "abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz01"; - - stdlib::byte_array input(&builder, in); - for (size_t i = 0; i < num_iterations; i++) { - input = stdlib::keccak::hash(input); - } -} - -/** - * @brief Generate test circuit with specified number of ecdsa verifications - * - * @param builder - * @param num_iterations - */ -template void generate_ecdsa_verification_test_circuit(Builder& builder, size_t num_iterations) -{ - using curve = stdlib::secp256k1; - using fr = typename curve::fr; - using fq = typename curve::fq; - using g1 = typename curve::g1; - - std::string message_string = "Instructions unclear, ask again later."; - - crypto::ecdsa_key_pair account; - for (size_t i = 0; i < num_iterations; i++) { - // Generate unique signature for each iteration - account.private_key = curve::fr::random_element(); - account.public_key = curve::g1::one * account.private_key; - - crypto::ecdsa_signature signature = - crypto::ecdsa_construct_signature(message_string, account); - - bool first_result = - crypto::ecdsa_verify_signature(message_string, account.public_key, signature); - static_cast(first_result); // TODO(Cody): This is not used anywhere. - - std::vector rr(signature.r.begin(), signature.r.end()); - std::vector ss(signature.s.begin(), signature.s.end()); - uint8_t vv = signature.v; - - typename curve::g1_bigfr_ct public_key = curve::g1_bigfr_ct::from_witness(&builder, account.public_key); - - stdlib::ecdsa_signature sig{ typename curve::byte_array_ct(&builder, rr), - typename curve::byte_array_ct(&builder, ss), - stdlib::uint8(&builder, vv) }; - - typename curve::byte_array_ct message(&builder, message_string); - - // Verify ecdsa signature - stdlib::ecdsa_verify_signature(message, public_key, sig); - } -} - -/** - * @brief Generate test circuit with specified number of merkle membership checks - * - * @param builder - * @param num_iterations - */ -template void generate_merkle_membership_test_circuit(Builder& builder, size_t num_iterations) -{ - using namespace stdlib; - using field_ct = field_t; - using witness_ct = witness_t; - using witness_ct = witness_t; - using MemStore = merkle_tree::MemoryStore; - using MerkleTree_ct = merkle_tree::MerkleTree; - - MemStore store; - const size_t tree_depth = 7; - auto merkle_tree = MerkleTree_ct(store, tree_depth); - - for (size_t i = 0; i < num_iterations; i++) { - // For each iteration update and check the membership of a different value - size_t idx = i; - size_t value = i * 2; - merkle_tree.update_element(idx, value); - - field_ct root_ct = witness_ct(&builder, merkle_tree.root()); - auto idx_ct = field_ct(witness_ct(&builder, fr(idx))).decompose_into_bits(); - auto value_ct = field_ct(value); - - merkle_tree::check_membership( - root_ct, merkle_tree::create_witness_hash_path(builder, merkle_tree.get_hash_path(idx)), value_ct, idx_ct); - } -} - // ultrahonk inline honk::UltraProver get_prover(honk::UltraComposer& composer, void (*test_circuit_function)(honk::UltraComposer::CircuitBuilder&, size_t), diff --git a/barretenberg/cpp/src/barretenberg/benchmark/ultra_bench/ultra_honk.bench.cpp b/barretenberg/cpp/src/barretenberg/benchmark/ultra_bench/ultra_honk.bench.cpp index fb99554b940..c0320b135a3 100644 --- a/barretenberg/cpp/src/barretenberg/benchmark/ultra_bench/ultra_honk.bench.cpp +++ b/barretenberg/cpp/src/barretenberg/benchmark/ultra_bench/ultra_honk.bench.cpp @@ -29,21 +29,17 @@ static void construct_proof_ultrahonk_power_of_2(State& state) noexcept } // Define benchmarks -BENCHMARK_CAPTURE(construct_proof_ultrahonk, - sha256, - &bb::mock_proofs::generate_sha256_test_circuit) +BENCHMARK_CAPTURE(construct_proof_ultrahonk, sha256, &stdlib::generate_sha256_test_circuit) ->Unit(kMillisecond); -BENCHMARK_CAPTURE(construct_proof_ultrahonk, - keccak, - &bb::mock_proofs::generate_keccak_test_circuit) +BENCHMARK_CAPTURE(construct_proof_ultrahonk, keccak, &stdlib::generate_keccak_test_circuit) ->Unit(kMillisecond); BENCHMARK_CAPTURE(construct_proof_ultrahonk, ecdsa_verification, - &bb::mock_proofs::generate_ecdsa_verification_test_circuit) + &stdlib::generate_ecdsa_verification_test_circuit) ->Unit(kMillisecond); BENCHMARK_CAPTURE(construct_proof_ultrahonk, merkle_membership, - &bb::mock_proofs::generate_merkle_membership_test_circuit) + &stdlib::generate_merkle_membership_test_circuit) ->Unit(kMillisecond); BENCHMARK(construct_proof_ultrahonk_power_of_2) diff --git a/barretenberg/cpp/src/barretenberg/benchmark/ultra_bench/ultra_honk_rounds.bench.cpp b/barretenberg/cpp/src/barretenberg/benchmark/ultra_bench/ultra_honk_rounds.bench.cpp index c931892ec4b..18066f4d370 100644 --- a/barretenberg/cpp/src/barretenberg/benchmark/ultra_bench/ultra_honk_rounds.bench.cpp +++ b/barretenberg/cpp/src/barretenberg/benchmark/ultra_bench/ultra_honk_rounds.bench.cpp @@ -56,7 +56,7 @@ BBERG_PROFILE static void test_round(State& state, size_t index) noexcept honk::UltraComposer composer; // TODO(https://github.com/AztecProtocol/barretenberg/issues/761) benchmark both sparse and dense circuits honk::UltraProver prover = bb::mock_proofs::get_prover( - composer, &bb::mock_proofs::generate_ecdsa_verification_test_circuit, 10); + composer, &bb::stdlib::generate_ecdsa_verification_test_circuit, 10); test_round_inner(state, prover, index); state.ResumeTiming(); // NOTE: google bench is very finnicky, must end in ResumeTiming() for correctness diff --git a/barretenberg/cpp/src/barretenberg/benchmark/ultra_bench/ultra_plonk.bench.cpp b/barretenberg/cpp/src/barretenberg/benchmark/ultra_bench/ultra_plonk.bench.cpp index ea3df79e241..6682aeb5d90 100644 --- a/barretenberg/cpp/src/barretenberg/benchmark/ultra_bench/ultra_plonk.bench.cpp +++ b/barretenberg/cpp/src/barretenberg/benchmark/ultra_bench/ultra_plonk.bench.cpp @@ -27,21 +27,17 @@ static void construct_proof_ultraplonk_power_of_2(State& state) noexcept } // Define benchmarks -BENCHMARK_CAPTURE(construct_proof_ultraplonk, - sha256, - &bb::mock_proofs::generate_sha256_test_circuit) +BENCHMARK_CAPTURE(construct_proof_ultraplonk, sha256, &stdlib::generate_sha256_test_circuit) ->Unit(kMillisecond); -BENCHMARK_CAPTURE(construct_proof_ultraplonk, - keccak, - &bb::mock_proofs::generate_keccak_test_circuit) +BENCHMARK_CAPTURE(construct_proof_ultraplonk, keccak, &stdlib::generate_keccak_test_circuit) ->Unit(kMillisecond); BENCHMARK_CAPTURE(construct_proof_ultraplonk, ecdsa_verification, - &bb::mock_proofs::generate_ecdsa_verification_test_circuit) + &stdlib::generate_ecdsa_verification_test_circuit) ->Unit(kMillisecond); BENCHMARK_CAPTURE(construct_proof_ultraplonk, merkle_membership, - &bb::mock_proofs::generate_merkle_membership_test_circuit) + &stdlib::generate_merkle_membership_test_circuit) ->Unit(kMillisecond); BENCHMARK(construct_proof_ultraplonk_power_of_2) diff --git a/barretenberg/cpp/src/barretenberg/benchmark/ultra_bench/ultra_plonk_rounds.bench.cpp b/barretenberg/cpp/src/barretenberg/benchmark/ultra_bench/ultra_plonk_rounds.bench.cpp index ca0d1bd021c..45e82fe144e 100644 --- a/barretenberg/cpp/src/barretenberg/benchmark/ultra_bench/ultra_plonk_rounds.bench.cpp +++ b/barretenberg/cpp/src/barretenberg/benchmark/ultra_bench/ultra_plonk_rounds.bench.cpp @@ -55,7 +55,7 @@ BBERG_PROFILE static void test_round(State& state, size_t index) noexcept plonk::UltraComposer composer; // TODO: https://github.com/AztecProtocol/barretenberg/issues/761 benchmark both sparse and dense circuits plonk::UltraProver prover = bb::mock_proofs::get_prover( - composer, &bb::mock_proofs::generate_ecdsa_verification_test_circuit, 10); + composer, &bb::stdlib::generate_ecdsa_verification_test_circuit, 10); test_round_inner(state, prover, index); // NOTE: google bench is very finnicky, must end in ResumeTiming() for correctness state.ResumeTiming(); diff --git a/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.hpp b/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.hpp index 3a874566602..666ac255aa4 100644 --- a/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.hpp +++ b/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.hpp @@ -49,7 +49,7 @@ template class IPA { "The poly_degree should be positive and a power of two"); auto a_vec = polynomial; - auto srs_elements = ck->srs->get_monomial_points(); + auto* srs_elements = ck->srs->get_monomial_points(); std::vector G_vec_local(poly_degree); // The SRS stored in the commitment key is the result after applying the pippenger point table so the @@ -254,7 +254,7 @@ template class IPA { /*finite_field_additions_per_iteration=*/0, /*finite_field_multiplications_per_iteration=*/log_poly_degree); - auto srs_elements = vk->srs->get_monomial_points(); + auto* srs_elements = vk->srs->get_monomial_points(); // Copy the G_vector to local memory. std::vector G_vec_local(poly_degree); diff --git a/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.test.cpp b/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.test.cpp index 6aafab2fd15..e68f79f4861 100644 --- a/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.test.cpp +++ b/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.test.cpp @@ -34,7 +34,7 @@ TEST_F(IPATest, CommitOnManyZeroCoeffPolyWorks) } p[3] = Fr::one(); GroupElement commitment = this->commit(p); - auto srs_elements = this->ck()->srs->get_monomial_points(); + auto* srs_elements = this->ck()->srs->get_monomial_points(); GroupElement expected = srs_elements[0] * p[0]; // The SRS stored in the commitment key is the result after applying the pippenger point table so the // values at odd indices contain the point {srs[i-1].x * beta, srs[i-1].y}, where beta is the endomorphism @@ -50,7 +50,7 @@ TEST_F(IPATest, Commit) constexpr size_t n = 128; auto poly = this->random_polynomial(n); GroupElement commitment = this->commit(poly); - auto srs_elements = this->ck()->srs->get_monomial_points(); + auto* srs_elements = this->ck()->srs->get_monomial_points(); GroupElement expected = srs_elements[0] * poly[0]; // The SRS stored in the commitment key is the result after applying the pippenger point table so the // values at odd indices contain the point {srs[i-1].x * beta, srs[i-1].y}, where beta is the endomorphism diff --git a/barretenberg/cpp/src/barretenberg/goblin/CMakeLists.txt b/barretenberg/cpp/src/barretenberg/goblin/CMakeLists.txt index adaa9814aed..ee4b04affe8 100644 --- a/barretenberg/cpp/src/barretenberg/goblin/CMakeLists.txt +++ b/barretenberg/cpp/src/barretenberg/goblin/CMakeLists.txt @@ -1 +1 @@ -barretenberg_module(goblin stdlib_recursion ultra_honk eccvm translator_vm) \ No newline at end of file +barretenberg_module(goblin stdlib_recursion ultra_honk eccvm translator_vm stdlib_sha256 stdlib_merkle_tree stdlib_primitives) \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/goblin/full_goblin_recursion.test.cpp b/barretenberg/cpp/src/barretenberg/goblin/full_goblin_recursion.test.cpp index 5049b197e0a..04968199532 100644 --- a/barretenberg/cpp/src/barretenberg/goblin/full_goblin_recursion.test.cpp +++ b/barretenberg/cpp/src/barretenberg/goblin/full_goblin_recursion.test.cpp @@ -44,7 +44,7 @@ TEST_F(GoblinRecursionTests, Pseudo) for (size_t circuit_idx = 0; circuit_idx < NUM_CIRCUITS; ++circuit_idx) { // Construct a circuit with logic resembling that of the "kernel circuit" GoblinUltraBuilder circuit_builder{ goblin.op_queue }; - GoblinMockCircuits::construct_mock_kernel_circuit(circuit_builder, kernel_input); + GoblinMockCircuits::construct_mock_kernel_circuit(circuit_builder, kernel_input, kernel_input); // Construct proof of the current kernel circuit to be recursively verified by the next one kernel_input = goblin.accumulate(circuit_builder); diff --git a/barretenberg/cpp/src/barretenberg/goblin/mock_circuits.hpp b/barretenberg/cpp/src/barretenberg/goblin/mock_circuits.hpp index 78fd8903fc9..9fe3ff7cd4c 100644 --- a/barretenberg/cpp/src/barretenberg/goblin/mock_circuits.hpp +++ b/barretenberg/cpp/src/barretenberg/goblin/mock_circuits.hpp @@ -1,10 +1,18 @@ #pragma once #include "barretenberg/commitment_schemes/commitment_key.hpp" +#include "barretenberg/crypto/ecdsa/ecdsa.hpp" #include "barretenberg/flavor/goblin_ultra.hpp" #include "barretenberg/goblin/goblin.hpp" #include "barretenberg/proof_system/circuit_builder/goblin_ultra_circuit_builder.hpp" #include "barretenberg/srs/global_crs.hpp" +#include "barretenberg/stdlib/encryption/ecdsa/ecdsa.hpp" +#include "barretenberg/stdlib/hash/sha256/sha256.hpp" +#include "barretenberg/stdlib/merkle_tree/membership.hpp" +#include "barretenberg/stdlib/merkle_tree/memory_store.hpp" +#include "barretenberg/stdlib/merkle_tree/merkle_tree.hpp" +#include "barretenberg/stdlib/primitives/curves/secp256k1.hpp" +#include "barretenberg/stdlib/primitives/packed_byte_array/packed_byte_array.hpp" #include "barretenberg/stdlib/recursion/honk/verifier/ultra_recursive_verifier.hpp" namespace bb { @@ -23,10 +31,16 @@ class GoblinMockCircuits { using KernelInput = Goblin::AccumulationOutput; static constexpr size_t NUM_OP_QUEUE_COLUMNS = Flavor::NUM_WIRES; + /** + * @brief Populate a builder with a specified number of arithmetic gates; includes a PI + * + * @param builder + * @param num_gates + */ static void construct_arithmetic_circuit(GoblinUltraBuilder& builder, size_t num_gates = 1) { - // Add some arithmetic gates that utilize public inputs - for (size_t i = 0; i < num_gates; ++i) { + // For good measure, include a gate with some public inputs + { FF a = FF::random_element(); FF b = FF::random_element(); FF c = FF::random_element(); @@ -36,10 +50,28 @@ class GoblinMockCircuits { 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) }); + } + // Add arbitrary arithmetic gates to obtain a total of num_gates-many gates + for (size_t i = 0; i < num_gates - 1; ++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 some arbitrary goblinized ECC ops + * + * @param builder + */ static void construct_goblin_ecc_op_circuit(GoblinUltraBuilder& builder) { // Add a mul accum op and an equality op @@ -49,14 +81,40 @@ class GoblinMockCircuits { builder.queue_ecc_eq(); } + /** + * @brief Populate a builder with some arbitrary but nontrivial constraints + * @details Although the details of the circuit constructed here are arbitrary, the intent is to mock something a + * bit more realistic than a circuit comprised entirely of arithmetic gates. E.g. the circuit should respond + * realistically to efforts to parallelize circuit construction. + * + * @param builder + * @param large If true, construct a "large" circuit (2^19), else a medium circuit (2^17) + */ + static void construct_mock_function_circuit(GoblinUltraBuilder& builder, bool large = false) + { + // Determine number of times to execute the below operations that constitute the mock circuit logic. Note that + // the circuit size does not scale linearly with number of iterations due to e.g. amortization of lookup costs + const size_t NUM_ITERATIONS_LARGE = 13; // results in circuit size 2^19 (521327 gates) + const size_t NUM_ITERATIONS_MEDIUM = 3; // results in circuit size 2^17 (124843 gates) + const size_t NUM_ITERATIONS = large ? NUM_ITERATIONS_LARGE : NUM_ITERATIONS_MEDIUM; + + stdlib::generate_sha256_test_circuit(builder, NUM_ITERATIONS); // min gates: ~39k + stdlib::generate_ecdsa_verification_test_circuit(builder, NUM_ITERATIONS); // min gates: ~41k + stdlib::generate_merkle_membership_test_circuit(builder, NUM_ITERATIONS); // min gates: ~29k + + // Note: its not clear whether goblin ops will be supported for function circuits initially but currently UGH + // can only be used if some op gates are included so for now we'll assume each function circuit has some. + construct_goblin_ecc_op_circuit(builder); + } + /** * @brief Mock the interactions of a simple curcuit with the op_queue * @todo The transcript aggregation protocol in the Goblin proof system can not yet support an empty "previous * transcript" (see issue #723) because the corresponding commitments are zero / the point at infinity. This * function mocks the interactions with the op queue of a fictional "first" circuit. This way, when we go to - * generate a proof over our first "real" circuit, the transcript aggregation protocol can proceed nominally. The - * mock data is valid in the sense that it can be processed by all stages of Goblin as if it came from a genuine - * circuit. + * generate a proof over our first "real" circuit, the transcript aggregation protocol can proceed nominally. + * The mock data is valid in the sense that it can be processed by all stages of Goblin as if it came from a + * genuine circuit. * * * @param op_queue @@ -102,27 +160,42 @@ class GoblinMockCircuits { // queues the result of the preceding ECC builder.queue_ecc_eq(); // should be eq and reset - construct_arithmetic_circuit(builder); + construct_arithmetic_circuit(builder, 1 << 10); } /** * @brief Construct a mock kernel circuit - * @details This circuit contains (1) some basic/arbitrary arithmetic gates, (2) a genuine recursive verification of - * the proof provided as input. It does not contain any other real kernel logic. + * @details This circuit contains (1) some arbitrary operations representing general kernel logic, (2) recursive + * verification of a function circuit proof, and optionally (3) recursive verification of a previous kernel circuit + * proof. The arbitrary kernel logic is structured to bring the final dyadic circuit size of the kernel to 2^17. * + * TODO(https://github.com/AztecProtocol/barretenberg/issues/801): Pairing point aggregation not implemented * @param builder - * @param kernel_input A proof to be recursively verified and the corresponding native verification key + * @param function_accum {proof, vkey} for function circuit to be recursively verified + * @param prev_kernel_accum {proof, vkey} for previous kernel circuit to be recursively verified */ - static void construct_mock_kernel_circuit(GoblinUltraBuilder& builder, KernelInput& kernel_input) + static void construct_mock_kernel_circuit(GoblinUltraBuilder& builder, + const KernelInput& function_accum, + const KernelInput& prev_kernel_accum) { - // Generic operations e.g. state updates (just arith gates for now) - GoblinMockCircuits::construct_arithmetic_circuit(builder, /*num_gates=*/1 << 4); - - // Execute recursive aggregation of previous kernel proof - RecursiveVerifier verifier{ &builder, kernel_input.verification_key }; - // TODO(https://github.com/AztecProtocol/barretenberg/issues/801): Aggregation - auto pairing_points = verifier.verify_proof(kernel_input.proof); // app function proof - pairing_points = verifier.verify_proof(kernel_input.proof); // previous kernel proof + // Add operations representing general kernel logic e.g. state updates. Note: these are structured to make the + // kernel "full" within the dyadic size 2^17 (130914 gates) + const size_t NUM_MERKLE_CHECKS = 45; + const size_t NUM_ECDSA_VERIFICATIONS = 1; + const size_t NUM_SHA_HASHES = 1; + stdlib::generate_merkle_membership_test_circuit(builder, NUM_MERKLE_CHECKS); + stdlib::generate_ecdsa_verification_test_circuit(builder, NUM_ECDSA_VERIFICATIONS); + stdlib::generate_sha256_test_circuit(builder, NUM_SHA_HASHES); + + // Execute recursive aggregation of function proof + RecursiveVerifier verifier1{ &builder, function_accum.verification_key }; + verifier1.verify_proof(function_accum.proof); + + // Execute recursive aggregation of previous kernel proof if one exists + if (!prev_kernel_accum.proof.proof_data.empty()) { + RecursiveVerifier verifier2{ &builder, prev_kernel_accum.verification_key }; + verifier2.verify_proof(prev_kernel_accum.proof); + } } }; -} // namespace bb \ No newline at end of file +} // namespace bb diff --git a/barretenberg/cpp/src/barretenberg/goblin/mock_circuits_pinning.test.cpp b/barretenberg/cpp/src/barretenberg/goblin/mock_circuits_pinning.test.cpp new file mode 100644 index 00000000000..1135a559b92 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/goblin/mock_circuits_pinning.test.cpp @@ -0,0 +1,61 @@ +#include "barretenberg/goblin/goblin.hpp" +#include "barretenberg/goblin/mock_circuits.hpp" +#include "barretenberg/proof_system/circuit_builder/goblin_ultra_circuit_builder.hpp" +#include "barretenberg/ultra_honk/ultra_composer.hpp" +#include + +using namespace bb; +using namespace bb::honk; + +/** + * @brief For benchmarking, we want to be sure that our mocking functions create circuits of a known size. We control + * this, to the degree that matters for proof construction time, using these "pinning tests" that fix values. + * + */ +class MockCircuits : public ::testing::Test { + protected: + static void SetUpTestSuite() { srs::init_crs_factory("../srs_db/ignition"); } +}; + +TEST_F(MockCircuits, PinFunctionSizes) +{ + const auto run_test = [](bool large) { + Goblin goblin; + GoblinUltraCircuitBuilder app_circuit{ goblin.op_queue }; + GoblinMockCircuits::construct_mock_function_circuit(app_circuit, large); + GoblinUltraComposer composer; + auto instance = composer.create_instance(app_circuit); + if (large) { + EXPECT_EQ(instance->proving_key->log_circuit_size, 19); + } else { + EXPECT_EQ(instance->proving_key->log_circuit_size, 17); + }; + }; + run_test(true); + run_test(false); +} + +TEST_F(MockCircuits, PinKernelSizes) +{ + const auto run_test = [](bool large) { + { + Goblin goblin; + GoblinMockCircuits::perform_op_queue_interactions_for_mock_first_circuit(goblin.op_queue); + Goblin::AccumulationOutput kernel_accum; + GoblinUltraCircuitBuilder app_circuit{ goblin.op_queue }; + GoblinMockCircuits::construct_mock_function_circuit(app_circuit, large); + auto function_accum = goblin.accumulate(app_circuit); + GoblinUltraCircuitBuilder kernel_circuit{ goblin.op_queue }; + GoblinMockCircuits::construct_mock_kernel_circuit(kernel_circuit, function_accum, kernel_accum); + GoblinUltraComposer composer; + auto instance = composer.create_instance(kernel_circuit); + if (large) { + EXPECT_EQ(instance->proving_key->log_circuit_size, 17); + } else { + EXPECT_EQ(instance->proving_key->log_circuit_size, 17); + }; + } + }; + run_test(true); + run_test(false); +} \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/srs/global_crs.cpp b/barretenberg/cpp/src/barretenberg/srs/global_crs.cpp index c8c73f98e36..fcfbac3e30c 100644 --- a/barretenberg/cpp/src/barretenberg/srs/global_crs.cpp +++ b/barretenberg/cpp/src/barretenberg/srs/global_crs.cpp @@ -21,16 +21,23 @@ void init_crs_factory(std::vector const& points, g2::affine_ // Initializes crs from a file path this we use in the entire codebase void init_crs_factory(std::string crs_path) { + if (crs_factory != nullptr) { + return; + } #ifdef WASMTIME_ENV_HACK static_cast(crs_path); // We only need this codepath in wasmtime because the SRS cannot be loaded in our usual ways // and we don't need a real CRS for our purposes. // TODO(https://github.com/AztecProtocol/barretenberg/issues/837): make this a real SRS. + std::cout << "WASMTIME_ENV_HACK: started generating fake bn254 curve" << std::endl; std::vector points; - for (int i = 0; i < 262144; i++) { + // 2**19 points + points.reserve(1 << 19); + for (int i = 0; i < (1 << 19); i++) { points.push_back(g1::affine_element::random_element()); } init_crs_factory(points, g2::affine_element{ fq::random_element(), fq::random_element() }); + std::cout << "WASMTIME_ENV_HACK: finished generating fake bn254 curve" << std::endl; #else crs_factory = std::make_shared>(crs_path); #endif @@ -44,15 +51,22 @@ void init_grumpkin_crs_factory(std::vector const void init_grumpkin_crs_factory(std::string crs_path) { + if (grumpkin_crs_factory != nullptr) { + return; + } #ifdef WASMTIME_ENV_HACK // We only need this codepath in wasmtime because the SRS cannot be loaded in our usual ways // and we don't need a real CRS for our purposes. // TODO(https://github.com/AztecProtocol/barretenberg/issues/837): make this a real SRS. static_cast(crs_path); + std::cout << "WASMTIME_ENV_HACK: started generating fake grumpkin curve" << std::endl; std::vector points; - for (int i = 0; i < 262144; i++) { + // 2**18 points + points.reserve(1 << 18); + for (int i = 0; i < (1 << 18); i++) { points.push_back(curve::Grumpkin::AffineElement::random_element()); } + std::cout << "WASMTIME_ENV_HACK: finished generating fake grumpkin curve" << std::endl; init_grumpkin_crs_factory(points); #else grumpkin_crs_factory = std::make_shared>(crs_path); diff --git a/barretenberg/cpp/src/barretenberg/srs/scalar_multiplication.test.cpp b/barretenberg/cpp/src/barretenberg/srs/scalar_multiplication.test.cpp index 9ed7c2e4048..c43bb64a60d 100644 --- a/barretenberg/cpp/src/barretenberg/srs/scalar_multiplication.test.cpp +++ b/barretenberg/cpp/src/barretenberg/srs/scalar_multiplication.test.cpp @@ -70,7 +70,7 @@ TYPED_TEST(ScalarMultiplicationTests, ReduceBucketsSimple) TestFixture::read_transcript_g2(TestFixture::SRS_PATH); } auto crs = srs::factories::FileProverCrs(num_points / 2, TestFixture::SRS_PATH); - auto monomials = crs.get_monomial_points(); + auto* monomials = crs.get_monomial_points(); std::vector point_schedule(bb::scalar_multiplication::point_table_size(num_points / 2)); std::array bucket_empty_status; diff --git a/barretenberg/cpp/src/barretenberg/stdlib/encryption/ecdsa/ecdsa.hpp b/barretenberg/cpp/src/barretenberg/stdlib/encryption/ecdsa/ecdsa.hpp index 1c2649ed1b3..7ac633dd71c 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/encryption/ecdsa/ecdsa.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/encryption/ecdsa/ecdsa.hpp @@ -41,6 +41,8 @@ static ecdsa_signature ecdsa_from_witness(Builder* ctx, const crypto::e return out; } +template void generate_ecdsa_verification_test_circuit(Builder& builder, size_t num_iterations); + } // namespace bb::stdlib #include "./ecdsa_impl.hpp" \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/stdlib/encryption/ecdsa/ecdsa_impl.hpp b/barretenberg/cpp/src/barretenberg/stdlib/encryption/ecdsa/ecdsa_impl.hpp index fa67089fd4e..ee0078f0f33 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/encryption/ecdsa/ecdsa_impl.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/encryption/ecdsa/ecdsa_impl.hpp @@ -3,6 +3,7 @@ #include "barretenberg/stdlib/encryption/ecdsa/ecdsa.hpp" #include "barretenberg/stdlib/hash/sha256/sha256.hpp" #include "barretenberg/stdlib/primitives//bit_array/bit_array.hpp" +#include "barretenberg/stdlib/primitives/curves/secp256k1.hpp" namespace bb::stdlib { @@ -221,4 +222,54 @@ bool_t ecdsa_verify_signature_noassert(const stdlib::byte_array void generate_ecdsa_verification_test_circuit(Builder& builder, size_t num_iterations) +{ + using curve = stdlib::secp256k1; + using fr = typename curve::fr; + using fq = typename curve::fq; + using g1 = typename curve::g1; + + std::string message_string = "Instructions unclear, ask again later."; + + crypto::ecdsa_key_pair account; + for (size_t i = 0; i < num_iterations; i++) { + // Generate unique signature for each iteration + account.private_key = curve::fr::random_element(); + account.public_key = curve::g1::one * account.private_key; + + crypto::ecdsa_signature signature = + crypto::ecdsa_construct_signature(message_string, account); + + bool first_result = + crypto::ecdsa_verify_signature(message_string, account.public_key, signature); + static_cast(first_result); // TODO(Cody): This is not used anywhere. + + std::vector rr(signature.r.begin(), signature.r.end()); + std::vector ss(signature.s.begin(), signature.s.end()); + uint8_t vv = signature.v; + + typename curve::g1_bigfr_ct public_key = curve::g1_bigfr_ct::from_witness(&builder, account.public_key); + + stdlib::ecdsa_signature sig{ typename curve::byte_array_ct(&builder, rr), + typename curve::byte_array_ct(&builder, ss), + stdlib::uint8(&builder, vv) }; + + typename curve::byte_array_ct message(&builder, message_string); + + // Verify ecdsa signature + stdlib::ecdsa_verify_signature(message, public_key, sig); + } +} + } // namespace bb::stdlib \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/stdlib/hash/keccak/keccak.cpp b/barretenberg/cpp/src/barretenberg/stdlib/hash/keccak/keccak.cpp index 2392853e335..9543603660f 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/hash/keccak/keccak.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/hash/keccak/keccak.cpp @@ -888,7 +888,27 @@ stdlib::byte_array keccak::sponge_squeeze_for_permutation_opco } return result; } + +/** + * @brief Generate a simple keccak circuit for testing purposes + * + * @tparam Builder + * @param builder + * @param num_iterations number of hashes to perform + */ +template void generate_keccak_test_circuit(Builder& builder, size_t num_iterations) +{ + std::string in = "abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz01"; + + stdlib::byte_array input(&builder, in); + for (size_t i = 0; i < num_iterations; i++) { + input = stdlib::keccak::hash(input); + } +} + template class keccak; template class keccak; +template void generate_keccak_test_circuit(bb::UltraCircuitBuilder&, size_t); +template void generate_keccak_test_circuit(bb::GoblinUltraCircuitBuilder&, size_t); } // namespace bb::stdlib diff --git a/barretenberg/cpp/src/barretenberg/stdlib/hash/keccak/keccak.hpp b/barretenberg/cpp/src/barretenberg/stdlib/hash/keccak/keccak.hpp index b4a9ae8f8ec..bd399af7a39 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/hash/keccak/keccak.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/hash/keccak/keccak.hpp @@ -201,4 +201,6 @@ template class keccak { Builder* context); }; +template void generate_keccak_test_circuit(Builder& builder, size_t num_iterations); + } // namespace bb::stdlib diff --git a/barretenberg/cpp/src/barretenberg/stdlib/hash/sha256/sha256.cpp b/barretenberg/cpp/src/barretenberg/stdlib/hash/sha256/sha256.cpp index 57264c24d06..a685f0af145 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/hash/sha256/sha256.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/hash/sha256/sha256.cpp @@ -178,6 +178,23 @@ template packed_byte_array sha256(const packed_byte_ return packed_byte_array(output, 4); } +/** + * @brief Generate a simple sha256 circuit for testing purposes + * + * @tparam Builder + * @param builder + * @param num_iterations number of hashes to perform + */ +template void generate_sha256_test_circuit(Builder& builder, size_t num_iterations) +{ + std::string in; + in.resize(32); + stdlib::packed_byte_array input(&builder, in); + for (size_t i = 0; i < num_iterations; i++) { + input = stdlib::sha256(input); + } +} + template byte_array sha256_block(const byte_array& input); template byte_array sha256_block(const byte_array& input); template byte_array sha256_block(const byte_array& input); @@ -186,4 +203,6 @@ template packed_byte_array sha256( template packed_byte_array sha256(const packed_byte_array& input); template packed_byte_array sha256( const packed_byte_array& input); +template void generate_sha256_test_circuit(bb::UltraCircuitBuilder&, size_t); +template void generate_sha256_test_circuit(bb::GoblinUltraCircuitBuilder&, size_t); } // namespace bb::stdlib diff --git a/barretenberg/cpp/src/barretenberg/stdlib/hash/sha256/sha256.hpp b/barretenberg/cpp/src/barretenberg/stdlib/hash/sha256/sha256.hpp index 2593fe10fc1..8f88ce2e946 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/hash/sha256/sha256.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/hash/sha256/sha256.hpp @@ -23,4 +23,6 @@ template field_t sha256_to_field(const packed_byte_a return slices[1] + (slices[0] * (uint256_t(1) << 128)); } +template void generate_sha256_test_circuit(Builder& builder, size_t num_iterations); + } // namespace bb::stdlib diff --git a/barretenberg/cpp/src/barretenberg/stdlib/merkle_tree/membership.hpp b/barretenberg/cpp/src/barretenberg/stdlib/merkle_tree/membership.hpp index f72dab1960f..ddb743b6e8e 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/merkle_tree/membership.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/merkle_tree/membership.hpp @@ -1,5 +1,7 @@ #pragma once #include "barretenberg/stdlib/hash/pedersen/pedersen.hpp" +#include "barretenberg/stdlib/merkle_tree/memory_store.hpp" +#include "barretenberg/stdlib/merkle_tree/merkle_tree.hpp" #include "barretenberg/stdlib/primitives/byte_array/byte_array.hpp" #include "barretenberg/stdlib/primitives/field/field.hpp" #include "hash_path.hpp" @@ -315,3 +317,39 @@ void batch_update_membership(field_t const& new_root, } } // namespace bb::stdlib::merkle_tree + +namespace bb::stdlib { +/** + * @brief Generate a simple merkle tree membership circuit for testing purposes + * + * @tparam Builder + * @param builder + * @param num_iterations number of membership checks to perform + */ +template static void generate_merkle_membership_test_circuit(Builder& builder, size_t num_iterations) +{ + using namespace stdlib; + using field_ct = field_t; + using witness_ct = witness_t; + using MemStore = merkle_tree::MemoryStore; + using MerkleTree_ct = merkle_tree::MerkleTree; + + MemStore store; + const size_t tree_depth = 7; + auto merkle_tree = MerkleTree_ct(store, tree_depth); + + for (size_t i = 0; i < num_iterations; i++) { + // For each iteration update and check the membership of a different value + size_t idx = i; + size_t value = i * 2; + merkle_tree.update_element(idx, value); + + field_ct root_ct = witness_ct(&builder, merkle_tree.root()); + auto idx_ct = field_ct(witness_ct(&builder, fr(idx))).decompose_into_bits(); + auto value_ct = field_ct(value); + + merkle_tree::check_membership( + root_ct, merkle_tree::create_witness_hash_path(builder, merkle_tree.get_hash_path(idx)), value_ct, idx_ct); + } +} +} // namespace bb::stdlib \ No newline at end of file