diff --git a/barretenberg/cpp/CLAUDE.md b/barretenberg/cpp/CLAUDE.md index 5c4408d85f16..2ff9e9c257e2 100644 --- a/barretenberg/cpp/CLAUDE.md +++ b/barretenberg/cpp/CLAUDE.md @@ -32,11 +32,17 @@ Bootstrap modes: ninja NOTE: DO NOT add the -j flag, default is optimal. where test is based on what you're working on: - - `./bin/ultra_honk_tests` - Ultra Honk circuit tests - - `./bin/chonk_tests` - Chonk tests - - `./bin/api_tests` - API/CLI tests - - `./bin/stdlib_*_tests` - Standard library tests - - `./bin/crypto_*_tests` - Cryptographic primitive tests + - `ultra_honk_tests` - Ultra Honk circuit tests + - `chonk_tests` - Chonk tests + - `dsl_tests` - ACIR/DSL tests (acir_format/, mock_verifier_inputs) + - `hypernova_tests` - HyperNova folding tests + - `eccvm_tests` - ECCVM circuit tests + - `translator_vm_tests` - Translator circuit tests + - `goblin_tests` - Goblin tests + - `stdlib_*_tests` - Standard library tests + - `crypto_*_tests` - Cryptographic primitive tests + + To find test targets: `ninja -t targets | grep "_tests:" | grep -v cmake` ### Barretenberg module components: diff --git a/barretenberg/cpp/scripts/test_chonk_standalone_vks_havent_changed.sh b/barretenberg/cpp/scripts/test_chonk_standalone_vks_havent_changed.sh index 29a2d87a43dd..3ba600c5bd0c 100755 --- a/barretenberg/cpp/scripts/test_chonk_standalone_vks_havent_changed.sh +++ b/barretenberg/cpp/scripts/test_chonk_standalone_vks_havent_changed.sh @@ -13,7 +13,7 @@ cd .. # - Generate a hash for versioning: sha256sum bb-chonk-inputs.tar.gz # - Upload the compressed results: aws s3 cp bb-chonk-inputs.tar.gz s3://aztec-ci-artifacts/protocol/bb-chonk-inputs-[hash(0:8)].tar.gz # Note: In case of the "Test suite failed to run ... Unexpected token 'with' " error, need to run: docker pull aztecprotocol/build:3.0 -pinned_short_hash="e3757861" +pinned_short_hash="474b8a97" pinned_chonk_inputs_url="https://aztec-ci-artifacts.s3.us-east-2.amazonaws.com/protocol/bb-chonk-inputs-${pinned_short_hash}.tar.gz" script_path="$(cd "$(dirname "${BASH_SOURCE[0]}")/scripts" && pwd)/$(basename "${BASH_SOURCE[0]}")" diff --git a/barretenberg/cpp/src/barretenberg/chonk/chonk_proof.cpp b/barretenberg/cpp/src/barretenberg/chonk/chonk_proof.cpp index f27555809337..36c7982be641 100644 --- a/barretenberg/cpp/src/barretenberg/chonk/chonk_proof.cpp +++ b/barretenberg/cpp/src/barretenberg/chonk/chonk_proof.cpp @@ -52,7 +52,7 @@ ChonkProof_ ChonkProof_::from_field_elements(const std // ECCVM proof start_idx = end_idx; - end_idx += static_cast(ECCVMFlavor::PROOF_LENGTH_WITHOUT_PUB_INPUTS); + end_idx += static_cast(ECCVMFlavor::PROOF_LENGTH); goblin_proof.eccvm_proof.insert(goblin_proof.eccvm_proof.end(), start_idx, end_idx); // IPA proof @@ -62,7 +62,7 @@ ChonkProof_ ChonkProof_::from_field_elements(const std // Translator proof start_idx = end_idx; - end_idx += static_cast(TranslatorFlavor::PROOF_LENGTH_WITHOUT_PUB_INPUTS); + end_idx += static_cast(TranslatorFlavor::PROOF_LENGTH); goblin_proof.translator_proof.insert(goblin_proof.translator_proof.end(), start_idx, end_idx); return ChonkProof_{ std::move(mega_proof), std::move(goblin_proof) }; diff --git a/barretenberg/cpp/src/barretenberg/chonk/chonk_proof.hpp b/barretenberg/cpp/src/barretenberg/chonk/chonk_proof.hpp index a2affdd74173..f1a6fee1f650 100644 --- a/barretenberg/cpp/src/barretenberg/chonk/chonk_proof.hpp +++ b/barretenberg/cpp/src/barretenberg/chonk/chonk_proof.hpp @@ -9,6 +9,7 @@ #include "barretenberg/common/serialize.hpp" #include "barretenberg/common/streams.hpp" #include "barretenberg/goblin/goblin.hpp" +#include "barretenberg/honk/proof_length.hpp" #include "barretenberg/serialize/msgpack_impl.hpp" #include "barretenberg/stdlib/primitives/field/field.hpp" #include "barretenberg/stdlib_circuit_builders/mega_circuit_builder.hpp" @@ -31,15 +32,19 @@ template struct ChonkProof_ { HonkProof mega_proof; // MegaZK proof of the hiding kernel circuit GoblinProof goblin_proof; // Goblin proof (Merge + ECCVM + IPA + Translator) + // Hiding kernel proof length (MegaZK with fixed circuit size) + static constexpr size_t HIDING_KERNEL_PROOF_LENGTH_WITHOUT_PUBLIC_INPUTS = + ProofLength::Honk::LENGTH_WITHOUT_PUB_INPUTS(MegaZKFlavor::VIRTUAL_LOG_N); + /** * @brief The size of a Chonk proof without backend-added public inputs */ static constexpr size_t PROOF_LENGTH_WITHOUT_PUB_INPUTS = - /*mega_proof*/ MegaZKFlavor::PROOF_LENGTH_WITHOUT_PUB_INPUTS() + + /*mega_proof*/ HIDING_KERNEL_PROOF_LENGTH_WITHOUT_PUBLIC_INPUTS + /*merge_proof*/ MERGE_PROOF_SIZE + - /*eccvm proof*/ ECCVMFlavor::PROOF_LENGTH_WITHOUT_PUB_INPUTS + + /*eccvm proof*/ ECCVMFlavor::PROOF_LENGTH + /*ipa proof*/ IPA_PROOF_LENGTH + - /*translator*/ TranslatorFlavor::PROOF_LENGTH_WITHOUT_PUB_INPUTS; + /*translator*/ TranslatorFlavor::PROOF_LENGTH; /** * @brief The size of a Chonk proof with backend-added public inputs: HidingKernelIO @@ -47,9 +52,6 @@ template struct ChonkProof_ { static constexpr size_t PROOF_LENGTH = PROOF_LENGTH_WITHOUT_PUB_INPUTS + /*public_inputs*/ bb::HidingKernelIO::PUBLIC_INPUTS_SIZE; - static constexpr size_t HIDING_KERNEL_PROOF_LENGTH_WITHOUT_PUBLIC_INPUTS = - MegaZKFlavor::PROOF_LENGTH_WITHOUT_PUB_INPUTS(); - // Default constructor ChonkProof_() = default; diff --git a/barretenberg/cpp/src/barretenberg/circuit_checker/ultra_circuit_checker.hpp b/barretenberg/cpp/src/barretenberg/circuit_checker/ultra_circuit_checker.hpp index 2546b3efb968..43ef54a47ecc 100644 --- a/barretenberg/cpp/src/barretenberg/circuit_checker/ultra_circuit_checker.hpp +++ b/barretenberg/cpp/src/barretenberg/circuit_checker/ultra_circuit_checker.hpp @@ -151,10 +151,10 @@ class UltraCircuitChecker { * @brief Struct for managing memory record data for ensuring RAM/ROM correctness */ struct MemoryCheckData { - // randomness for constructing wire 4 mem records + // randomness for constructing wire 4 mem records (eta powers) FF eta = FF::random_element(); - FF eta_two = FF::random_element(); - FF eta_three = FF::random_element(); + FF eta_two = eta * eta; // eta² + FF eta_three = eta_two * eta; // eta³ std::unordered_set read_record_gates; // row indices for gates containing RAM/ROM read mem record std::unordered_set write_record_gates; // row indices for gates containing RAM/ROM write mem record diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/gate_count_constants.hpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/gate_count_constants.hpp index 3ce67a24edc7..d851bc5160da 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/gate_count_constants.hpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/gate_count_constants.hpp @@ -55,7 +55,7 @@ template inline constexpr size_t ASSERT_EQUALITY = ZERO_GATE // Honk Recursion Constants // ======================================== -inline constexpr size_t ROOT_ROLLUP_GATE_COUNT = 12995063; +inline constexpr size_t ROOT_ROLLUP_GATE_COUNT = 12994866; template constexpr std::tuple HONK_RECURSION_CONSTANTS( @@ -67,48 +67,48 @@ constexpr std::tuple HONK_RECURSION_CONSTANTS( if constexpr (std::is_same_v>) { switch (mode) { case PredicateTestCase::ConstantTrue: - return std::make_tuple(726841, 0); + return std::make_tuple(726742, 0); case PredicateTestCase::WitnessTrue: case PredicateTestCase::WitnessFalse: - return std::make_tuple(727992, 0); + return std::make_tuple(727893, 0); } } else if constexpr (std::is_same_v>) { switch (mode) { case PredicateTestCase::ConstantTrue: - return std::make_tuple(770505, 0); + return std::make_tuple(770407, 0); case PredicateTestCase::WitnessTrue: case PredicateTestCase::WitnessFalse: - return std::make_tuple(771758, 0); + return std::make_tuple(771660, 0); } } else if constexpr (std::is_same_v>) { switch (mode) { case PredicateTestCase::ConstantTrue: - return std::make_tuple(727159, 0); + return std::make_tuple(727060, 0); case PredicateTestCase::WitnessTrue: case PredicateTestCase::WitnessFalse: - return std::make_tuple(728458, 0); + return std::make_tuple(728359, 0); } } else if constexpr (std::is_same_v>) { switch (mode) { case PredicateTestCase::ConstantTrue: - return std::make_tuple(23177, 76); + return std::make_tuple(23079, 76); case PredicateTestCase::WitnessTrue: case PredicateTestCase::WitnessFalse: - return std::make_tuple(24328, 76); + return std::make_tuple(24230, 76); } } else if constexpr (std::is_same_v>) { switch (mode) { case PredicateTestCase::ConstantTrue: - return std::make_tuple(28048, 80); + return std::make_tuple(27951, 80); case PredicateTestCase::WitnessTrue: case PredicateTestCase::WitnessFalse: - return std::make_tuple(29301, 80); + return std::make_tuple(29204, 80); } } else if constexpr (std::is_same_v>) { if (mode != PredicateTestCase::ConstantTrue) { bb::assert_failure("Unhandled mode in MegaZKRecursiveFlavor."); } - return std::make_tuple(817286, 0); + return std::make_tuple(817187, 0); } else { bb::assert_failure("Unhandled recursive flavor."); } @@ -121,7 +121,7 @@ constexpr std::tuple HONK_RECURSION_CONSTANTS( // ======================================== // Gate count for Chonk recursive verification (UltraRollup builder) -inline constexpr size_t CHONK_RECURSION_GATES = 2385456; +inline constexpr size_t CHONK_RECURSION_GATES = 2385358; // ======================================== // Hypernova Recursion Constants @@ -131,22 +131,22 @@ inline constexpr size_t CHONK_RECURSION_GATES = 2385456; inline constexpr size_t MSM_ROWS_OFFSET = 2; // Init kernel gate counts (verifies OINK proof) -inline constexpr size_t INIT_KERNEL_GATE_COUNT = 25828; +inline constexpr size_t INIT_KERNEL_GATE_COUNT = 25731; inline constexpr size_t INIT_KERNEL_ECC_ROWS = 848 + MSM_ROWS_OFFSET; inline constexpr size_t INIT_KERNEL_ULTRA_OPS = 89; // Inner kernel gate counts (verifies HN proof for previous kernel + HN for app) -inline constexpr size_t INNER_KERNEL_GATE_COUNT_HN = 60601; +inline constexpr size_t INNER_KERNEL_GATE_COUNT_HN = 60407; inline constexpr size_t INNER_KERNEL_ECC_ROWS = 1700 + MSM_ROWS_OFFSET; inline constexpr size_t INNER_KERNEL_ULTRA_OPS = 179; // Tail kernel gate counts (verifies HN_TAIL proof) -inline constexpr size_t TAIL_KERNEL_GATE_COUNT = 33758; +inline constexpr size_t TAIL_KERNEL_GATE_COUNT = 33660; inline constexpr size_t TAIL_KERNEL_ECC_ROWS = 914 + MSM_ROWS_OFFSET; inline constexpr size_t TAIL_KERNEL_ULTRA_OPS = 96; // Hiding kernel gate counts (verifies HN_FINAL proof) -inline constexpr size_t HIDING_KERNEL_GATE_COUNT = 36897; +inline constexpr size_t HIDING_KERNEL_GATE_COUNT = 36800; inline constexpr size_t HIDING_KERNEL_ECC_ROWS = 1341 + MSM_ROWS_OFFSET; inline constexpr size_t HIDING_KERNEL_ULTRA_OPS = 124; diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/mock_verifier_inputs.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/mock_verifier_inputs.cpp index 3566059ec615..624e136fafe7 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/mock_verifier_inputs.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/mock_verifier_inputs.cpp @@ -80,14 +80,14 @@ HonkProof create_mock_multilinear_batch_proof() using FF = typename Flavor::FF; HonkProof proof; - // Populate mock witness accumulator commitments - populate_field_elements_for_mock_commitments(proof, Flavor::NUM_WITNESS_ENTITIES / 2); + // Populate mock accumulator commitments (non_shifted + shifted) + populate_field_elements_for_mock_commitments(proof, Flavor::NUM_ACCUMULATOR_COMMITMENTS); // Accumulator multivariate challenges populate_field_elements(proof, Flavor::VIRTUAL_LOG_N); - // Witness accumulator polynomial evaluations - populate_field_elements(proof, Flavor::NUM_WITNESS_ENTITIES / 2); + // Accumulator polynomial evaluations (non_shifted + shifted) + populate_field_elements(proof, Flavor::NUM_ACCUMULATOR_EVALUATIONS); // Sumcheck proof HonkProof sumcheck_proof = create_mock_sumcheck_proof(); @@ -428,7 +428,7 @@ HonkProof create_mock_eccvm_proof() // 27. Shplonk populate_field_elements_for_mock_commitments(proof, /*num_commitments=*/1); - BB_ASSERT_EQ(proof.size(), ECCVMFlavor::PROOF_LENGTH_WITHOUT_PUB_INPUTS); + BB_ASSERT_EQ(proof.size(), ECCVMFlavor::PROOF_LENGTH); return proof; } @@ -468,7 +468,7 @@ HonkProof create_mock_translator_proof() HonkProof decider_proof = create_mock_decider_proof(); proof.insert(proof.end(), decider_proof.begin(), decider_proof.end()); - BB_ASSERT_EQ(proof.size(), TranslatorFlavor::PROOF_LENGTH_WITHOUT_PUB_INPUTS); + BB_ASSERT_EQ(proof.size(), TranslatorFlavor::PROOF_LENGTH); return proof; } diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/mock_verifier_inputs.test.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/mock_verifier_inputs.test.cpp index 4ac7b37d8d08..61be7e9b6f48 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/mock_verifier_inputs.test.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/mock_verifier_inputs.test.cpp @@ -1,288 +1,203 @@ #include "barretenberg/dsl/acir_format/mock_verifier_inputs.hpp" -#include "acir_format.hpp" -#include "barretenberg/chonk/chonk.hpp" -#include "barretenberg/chonk/chonk_verifier.hpp" -#include "barretenberg/eccvm/eccvm_flavor.hpp" -#include "barretenberg/flavor/mega_flavor.hpp" -#include "barretenberg/flavor/multilinear_batching_flavor.hpp" -#include "barretenberg/flavor/ultra_flavor.hpp" -#include "barretenberg/flavor/ultra_rollup_flavor.hpp" -#include "barretenberg/flavor/ultra_zk_flavor.hpp" -#include "barretenberg/goblin/mock_circuits.hpp" -#include "barretenberg/honk/types/public_inputs_type.hpp" -#include "barretenberg/stdlib/special_public_inputs/special_public_inputs.hpp" -#include "barretenberg/ultra_honk/prover_instance.hpp" -#include "barretenberg/ultra_honk/ultra_prover.hpp" -#include "barretenberg/ultra_honk/ultra_verifier.hpp" -#include "honk_recursion_constraint.hpp" +#include "barretenberg/honk/proof_length.hpp" #include -#include using namespace acir_format; using namespace bb; -template class MockVerifierInputsTest : public ::testing::Test { +class MockVerifierInputsTest : public ::testing::Test { public: static void SetUpTestSuite() { bb::srs::init_file_crs_factory(bb::srs::bb_crs_path()); } }; -using FlavorTypes = testing::Types; - -TYPED_TEST_SUITE(MockVerifierInputsTest, FlavorTypes); +// Static assertions for proof length constants +static_assert(MERGE_PROOF_SIZE == 42, "Merge proof size changed"); +static_assert(ECCVMFlavor::PROOF_LENGTH == 608, "ECCVM proof size changed"); +static_assert(IPA_PROOF_LENGTH == 64, "IPA proof size changed"); +static_assert(TranslatorFlavor::PROOF_LENGTH == 786, "Translator proof size changed"); + +static_assert(ProofLength::Oink::LENGTH_WITHOUT_PUB_INPUTS == 96, "Mega Oink proof size changed"); +static_assert(ProofLength::Oink::LENGTH_WITHOUT_PUB_INPUTS == 32, "Ultra Oink proof size changed"); +static_assert(ProofLength::Oink::LENGTH_WITHOUT_PUB_INPUTS == 36, "UltraZK Oink proof size changed"); +static_assert(ProofLength::Oink::LENGTH_WITHOUT_PUB_INPUTS == 32, + "UltraRollup Oink proof size changed"); + +static_assert(ProofLength::Honk::LENGTH_WITHOUT_PUB_INPUTS(MegaFlavor::VIRTUAL_LOG_N) == 433, + "Mega Honk proof size changed"); +static_assert(ProofLength::Honk::LENGTH_WITHOUT_PUB_INPUTS(MegaZKFlavor::VIRTUAL_LOG_N) == 407, + "MegaZK Honk (hiding kernel) proof size changed"); +static_assert(ProofLength::Honk::LENGTH_WITHOUT_PUB_INPUTS(UltraFlavor::VIRTUAL_LOG_N) == 441, + "Ultra Honk proof size changed"); +static_assert(ProofLength::Honk::LENGTH_WITHOUT_PUB_INPUTS(UltraZKFlavor::VIRTUAL_LOG_N) == 492, + "UltraZK Honk proof size changed"); +// UltraRollupFlavor has same base Honk proof length as UltraFlavor (IPA is handled separately) +static_assert(ProofLength::Honk::LENGTH_WITHOUT_PUB_INPUTS(UltraRollupFlavor::VIRTUAL_LOG_N) == 441, + "UltraRollup Honk proof size changed"); + +static_assert(ProofLength::MultilinearBatching::LENGTH == 121, + "MultilinearBatching proof size changed"); + +static_assert(ChonkProof::PROOF_LENGTH_WITHOUT_PUB_INPUTS == 1907, "Chonk proof size changed"); /** - * @brief Check that the size of a mock merge proof matches expectation + * @brief Check that mock merge proof has the expected size */ -TEST(MockVerifierInputsTest, MockMergeProofSize) +TEST_F(MockVerifierInputsTest, MockMergeProofSize) { - size_t CURRENT_MERGE_PROOF_SIZE = 42; - EXPECT_EQ(CURRENT_MERGE_PROOF_SIZE, MERGE_PROOF_SIZE) << "The length of the Merge proof changed."; - Goblin::MergeProof merge_proof = create_mock_merge_proof(); EXPECT_EQ(merge_proof.size(), MERGE_PROOF_SIZE); } /** - * @brief Check that the size of a mock pre-ipa proof matches expectation + * @brief Check that mock ECCVM proof has the expected size */ -TEST(MockVerifierInputsTest, MockPreIpaProofSize) +TEST_F(MockVerifierInputsTest, MockPreIpaProofSize) { - size_t CURRENT_ECCVM_PROOF_SIZE = 608; - EXPECT_EQ(ECCVMFlavor::PROOF_LENGTH_WITHOUT_PUB_INPUTS, CURRENT_ECCVM_PROOF_SIZE) - << "The length of the Pre-IPA proof changed."; - HonkProof eccvm_proof = create_mock_eccvm_proof(); - EXPECT_EQ(eccvm_proof.size(), ECCVMFlavor::PROOF_LENGTH_WITHOUT_PUB_INPUTS); + EXPECT_EQ(eccvm_proof.size(), ECCVMFlavor::PROOF_LENGTH); } /** - * @brief Check that the size of a mock ipa proof matches expectation + * @brief Check that mock IPA proof has the expected size */ -TEST(MockVerifierInputsTest, MockIPAProofSize) +TEST_F(MockVerifierInputsTest, MockIPAProofSize) { - size_t CURRENT_IPA_PROOF_SIZE = 64; - EXPECT_EQ(IPA_PROOF_LENGTH, CURRENT_IPA_PROOF_SIZE) << "The length of the IPA proof changed."; - HonkProof ipa_proof = create_mock_ipa_proof(); EXPECT_EQ(ipa_proof.size(), IPA_PROOF_LENGTH); } /** - * @brief Check that the size of a mock translator proof matches expectation + * @brief Check that mock Translator proof has the expected size */ -TEST(MockVerifierInputsTest, MockTranslatorProofSize) +TEST_F(MockVerifierInputsTest, MockTranslatorProofSize) { - size_t CURRENT_TRANSLATOR_PROOF_SIZE = 786; - EXPECT_EQ(TranslatorFlavor::PROOF_LENGTH_WITHOUT_PUB_INPUTS, CURRENT_TRANSLATOR_PROOF_SIZE) - << "The length of the Translator proof changed."; - HonkProof translator_proof = create_mock_translator_proof(); - EXPECT_EQ(translator_proof.size(), TranslatorFlavor::PROOF_LENGTH_WITHOUT_PUB_INPUTS); + EXPECT_EQ(translator_proof.size(), TranslatorFlavor::PROOF_LENGTH); } /** - * @brief Check that the size of a mock Oink proof matches expectation for MegaFlavor - * + * @brief Check that mock Oink proofs have the expected size for MegaFlavor */ -TEST(MockVerifierInputsTest, MockMegaOinkProofSize) +TEST_F(MockVerifierInputsTest, MockMegaOinkProofSize) { using Flavor = MegaFlavor; using Builder = MegaCircuitBuilder; + constexpr size_t OINK_LENGTH = ProofLength::Oink::LENGTH_WITHOUT_PUB_INPUTS; - size_t CURRENT_OINK_PROOF_SIZE_WITHOUT_PUB_INPUTS = 96; - EXPECT_EQ(Flavor::OINK_PROOF_LENGTH_WITHOUT_PUB_INPUTS, CURRENT_OINK_PROOF_SIZE_WITHOUT_PUB_INPUTS) - << "The length of the Mega Oink proof changed."; + HonkProof app_proof = create_mock_oink_proof(); + EXPECT_EQ(app_proof.size(), OINK_LENGTH + stdlib::recursion::honk::AppIO::PUBLIC_INPUTS_SIZE); - { - // AppIO - const size_t NUM_PUBLIC_INPUTS = stdlib::recursion::honk::AppIO::PUBLIC_INPUTS_SIZE; - HonkProof honk_proof = create_mock_oink_proof(); - EXPECT_EQ(honk_proof.size(), Flavor::OINK_PROOF_LENGTH_WITHOUT_PUB_INPUTS + NUM_PUBLIC_INPUTS); - } + HonkProof kernel_proof = create_mock_oink_proof(); + EXPECT_EQ(kernel_proof.size(), OINK_LENGTH + stdlib::recursion::honk::KernelIO::PUBLIC_INPUTS_SIZE); - { - // KernelIO - const size_t NUM_PUBLIC_INPUTS = stdlib::recursion::honk::KernelIO::PUBLIC_INPUTS_SIZE; - HonkProof honk_proof = create_mock_oink_proof(); - EXPECT_EQ(honk_proof.size(), Flavor::OINK_PROOF_LENGTH_WITHOUT_PUB_INPUTS + NUM_PUBLIC_INPUTS); - } - - { - // HidingKernelIO - const size_t NUM_PUBLIC_INPUTS = stdlib::recursion::honk::HidingKernelIO::PUBLIC_INPUTS_SIZE; - HonkProof honk_proof = create_mock_oink_proof>(); - EXPECT_EQ(honk_proof.size(), Flavor::OINK_PROOF_LENGTH_WITHOUT_PUB_INPUTS + NUM_PUBLIC_INPUTS); - } + HonkProof hiding_proof = create_mock_oink_proof>(); + EXPECT_EQ(hiding_proof.size(), OINK_LENGTH + stdlib::recursion::honk::HidingKernelIO::PUBLIC_INPUTS_SIZE); } /** - * @brief Check that the size of a mock Oink proof matches expectation for Ultra flavors - * + * @brief Check that mock Oink proofs have the expected size for Ultra flavors */ -TYPED_TEST(MockVerifierInputsTest, MockUltraOinkProofSize) +TEST_F(MockVerifierInputsTest, MockUltraOinkProofSize) { - using Flavor = TypeParam; - using Builder = Flavor::CircuitBuilder; - using IO = std::conditional_t, - stdlib::recursion::honk::RollupIO, - stdlib::recursion::honk::DefaultIO>; - - if (!std::is_same_v) { - // Base Ultra flavors have 8 witness entities, ZK flavors have 9 (includes gemini_masking_poly) - size_t CURRENT_OINK_PROOF_SIZE_WITHOUT_PUB_INPUTS = Flavor::HasZK ? 36 : 32; - EXPECT_EQ(Flavor::OINK_PROOF_LENGTH_WITHOUT_PUB_INPUTS, CURRENT_OINK_PROOF_SIZE_WITHOUT_PUB_INPUTS) - << "The length of the Ultra Oink proof changed."; - - const size_t NUM_PUBLIC_INPUTS = IO::PUBLIC_INPUTS_SIZE; - HonkProof honk_proof = create_mock_oink_proof(); - EXPECT_EQ(honk_proof.size(), Flavor::OINK_PROOF_LENGTH_WITHOUT_PUB_INPUTS + NUM_PUBLIC_INPUTS); - } else { - GTEST_SKIP(); + { + using Flavor = UltraFlavor; + using IO = stdlib::recursion::honk::DefaultIO; + HonkProof proof = create_mock_oink_proof(); + EXPECT_EQ(proof.size(), ProofLength::Oink::LENGTH_WITHOUT_PUB_INPUTS + IO::PUBLIC_INPUTS_SIZE); + } + { + using Flavor = UltraZKFlavor; + using IO = stdlib::recursion::honk::DefaultIO; + HonkProof proof = create_mock_oink_proof(); + EXPECT_EQ(proof.size(), ProofLength::Oink::LENGTH_WITHOUT_PUB_INPUTS + IO::PUBLIC_INPUTS_SIZE); + } + { + using Flavor = UltraRollupFlavor; + using IO = stdlib::recursion::honk::RollupIO; + HonkProof proof = create_mock_oink_proof(); + EXPECT_EQ(proof.size(), ProofLength::Oink::LENGTH_WITHOUT_PUB_INPUTS + IO::PUBLIC_INPUTS_SIZE); } } /** - * @brief Check that the size of a mock Decider proof matches expectation based on Flavor - * + * @brief Check that mock Honk proofs have the expected size for MegaFlavor */ -TYPED_TEST(MockVerifierInputsTest, MockDeciderProofSize) +TEST_F(MockVerifierInputsTest, MockMegaHonkProofSize) { - using Flavor = TypeParam; + using Flavor = MegaFlavor; + using Builder = MegaCircuitBuilder; + constexpr size_t HONK_LENGTH = ProofLength::Honk::LENGTH_WITHOUT_PUB_INPUTS(Flavor::VIRTUAL_LOG_N); - if (!std::is_same_v) { - size_t CURRENT_DECIDER_ULTRAZK_PROOF_SIZE = IsMegaFlavor ? 337 : 409; - EXPECT_EQ(Flavor::DECIDER_PROOF_LENGTH(), CURRENT_DECIDER_ULTRAZK_PROOF_SIZE) - << "The length of the Decider UltraZK proof changed."; + HonkProof app_proof = create_mock_honk_proof(); + EXPECT_EQ(app_proof.size(), HONK_LENGTH + stdlib::recursion::honk::AppIO::PUBLIC_INPUTS_SIZE); - HonkProof honk_proof = create_mock_decider_proof(); - EXPECT_EQ(honk_proof.size(), Flavor::DECIDER_PROOF_LENGTH()); - } else { - GTEST_SKIP(); - } + HonkProof kernel_proof = create_mock_honk_proof(); + EXPECT_EQ(kernel_proof.size(), HONK_LENGTH + stdlib::recursion::honk::KernelIO::PUBLIC_INPUTS_SIZE); + + HonkProof hiding_proof = create_mock_honk_proof>(); + EXPECT_EQ(hiding_proof.size(), HONK_LENGTH + stdlib::recursion::honk::HidingKernelIO::PUBLIC_INPUTS_SIZE); } /** - * @brief Check that the size of a mock Honk proof matches expectation based for MegaFlavor - * + * @brief Check that mock Honk proofs have the expected size for Ultra flavors */ -TEST(MockVerifierInputsTest, MockMegaHonkProofSize) +TEST_F(MockVerifierInputsTest, MockUltraHonkProofSize) { - using Flavor = MegaFlavor; - using Builder = MegaCircuitBuilder; - - // If this value changes, we need to update the corresponding constants in noir and in yarn-project. Also, we need - // to update the Prover.toml file for rollup-tx-private to reflect the new length of the MegaHonk proof. - size_t CURRENT_MEGA_PROOF_SIZE_WITHOUT_PUB_INPUTS = 433; - EXPECT_EQ(Flavor::PROOF_LENGTH_WITHOUT_PUB_INPUTS(), CURRENT_MEGA_PROOF_SIZE_WITHOUT_PUB_INPUTS) - << "The length of the Mega Honk proof changed."; - { - // AppIO - const size_t NUM_PUBLIC_INPUTS = stdlib::recursion::honk::AppIO::PUBLIC_INPUTS_SIZE; - HonkProof honk_proof = create_mock_honk_proof(); - EXPECT_EQ(honk_proof.size(), Flavor::PROOF_LENGTH_WITHOUT_PUB_INPUTS() + NUM_PUBLIC_INPUTS); + using Flavor = UltraFlavor; + using IO = stdlib::recursion::honk::DefaultIO; + HonkProof proof = create_mock_honk_proof(); + EXPECT_EQ(proof.size(), + ProofLength::Honk::LENGTH_WITHOUT_PUB_INPUTS(Flavor::VIRTUAL_LOG_N) + IO::PUBLIC_INPUTS_SIZE); } - { - // KernelIO - const size_t NUM_PUBLIC_INPUTS = stdlib::recursion::honk::KernelIO::PUBLIC_INPUTS_SIZE; - HonkProof honk_proof = create_mock_honk_proof(); - EXPECT_EQ(honk_proof.size(), Flavor::PROOF_LENGTH_WITHOUT_PUB_INPUTS() + NUM_PUBLIC_INPUTS); + using Flavor = UltraZKFlavor; + using IO = stdlib::recursion::honk::DefaultIO; + HonkProof proof = create_mock_honk_proof(); + EXPECT_EQ(proof.size(), + ProofLength::Honk::LENGTH_WITHOUT_PUB_INPUTS(Flavor::VIRTUAL_LOG_N) + IO::PUBLIC_INPUTS_SIZE); } - { - // HidingKernelIO - const size_t NUM_PUBLIC_INPUTS = stdlib::recursion::honk::HidingKernelIO::PUBLIC_INPUTS_SIZE; - HonkProof honk_proof = create_mock_honk_proof>(); - EXPECT_EQ(honk_proof.size(), Flavor::PROOF_LENGTH_WITHOUT_PUB_INPUTS() + NUM_PUBLIC_INPUTS); - } -} - -/** - * @brief Check that the size of a mock Honk proof matches expectation for Ultra flavors - * - */ -TYPED_TEST(MockVerifierInputsTest, MockUltraHonkProofSize) -{ - using Flavor = TypeParam; - using Builder = Flavor::CircuitBuilder; - using IO = std::conditional_t, - stdlib::recursion::honk::RollupIO, - stdlib::recursion::honk::DefaultIO>; - - if (!std::is_same_v) { - // If this value changes, we need to update the corresponding constants in noir and in yarn-project. Also, we - // need to update the relevant Prover.toml files to reflect the new length of the Ultra Honk proof. - size_t CURRENT_ULTRA_HONK_PROOF_SIZE_WITHOUT_PUB_INPUTS = 0; - if constexpr (std::is_same_v) { - CURRENT_ULTRA_HONK_PROOF_SIZE_WITHOUT_PUB_INPUTS = 441; - } else if constexpr (std::is_same_v) { - CURRENT_ULTRA_HONK_PROOF_SIZE_WITHOUT_PUB_INPUTS = 505; - } else if constexpr (std::is_same_v) { - CURRENT_ULTRA_HONK_PROOF_SIZE_WITHOUT_PUB_INPUTS = 492; - } - const size_t NUM_PUBLIC_INPUTS = IO::PUBLIC_INPUTS_SIZE; - HonkProof honk_proof = create_mock_honk_proof(); - EXPECT_EQ(honk_proof.size(), Flavor::PROOF_LENGTH_WITHOUT_PUB_INPUTS() + NUM_PUBLIC_INPUTS); - EXPECT_EQ(Flavor::PROOF_LENGTH_WITHOUT_PUB_INPUTS(), CURRENT_ULTRA_HONK_PROOF_SIZE_WITHOUT_PUB_INPUTS) - << "The length of the Ultra Honk proof changed."; - } else { - GTEST_SKIP(); + using Flavor = UltraRollupFlavor; + using IO = stdlib::recursion::honk::RollupIO; + HonkProof proof = create_mock_honk_proof(); + // UltraRollupFlavor has HasIPAAccumulator=true, so proof includes IPA_PROOF_LENGTH + constexpr size_t expected = ProofLength::Honk::LENGTH_WITHOUT_PUB_INPUTS(Flavor::VIRTUAL_LOG_N) + + IO::PUBLIC_INPUTS_SIZE + IPA_PROOF_LENGTH; + EXPECT_EQ(proof.size(), expected); } } -/** - * @brief Check that the size of a mock AVM proof matches expectation - * - */ // TODO(@fcarreiro): Re-enable this test once proof size is fixed. -TEST(MockVerifierInputsTest, DISABLED_MockAVMProofSize) +TEST_F(MockVerifierInputsTest, DISABLED_MockAVMProofSize) { - size_t CURRENT_AVM_PROOF_SIZE_WITHOUT_PUB_INPUTS = 16040; const HonkProof avm_proof = create_mock_avm_proof_without_pub_inputs(/*add_padding=*/false); - EXPECT_EQ(avm_proof.size(), CURRENT_AVM_PROOF_SIZE_WITHOUT_PUB_INPUTS) << "The length of the AVM proof changed."; + EXPECT_EQ(avm_proof.size(), 16040); } -/** - * @brief Check that the size of a padded mock AVM proof matches expectation - * - */ -TEST(MockVerifierInputsTest, MockAVMProofSizePadded) +TEST_F(MockVerifierInputsTest, MockAVMProofSizePadded) { - size_t CURRENT_PADDED_AVM_PROOF_SIZE_WITHOUT_PUB_INPUTS = 16200; const HonkProof padded_avm_proof = create_mock_avm_proof_without_pub_inputs(/*add_padding=*/true); - EXPECT_EQ(padded_avm_proof.size(), CURRENT_PADDED_AVM_PROOF_SIZE_WITHOUT_PUB_INPUTS) - << "The length of the padded AVM proof changed."; + EXPECT_EQ(padded_avm_proof.size(), 16200); } /** - * @brief Check that the size of a mock Chonk proof matches expectation - * + * @brief Check that mock Chonk proof has the expected size */ -TEST(MockVerifierInputsTest, MockChonkProofSize) +TEST_F(MockVerifierInputsTest, MockChonkProofSize) { using Builder = MegaCircuitBuilder; - - // If this value changes, we need to update the corresponding constants in noir and in yarn-project. Also, we need - // to update the Prover.toml file for rollup-tx-private to reflect the new length of the Chonk proof. - size_t CURRENT_CHONK_PROOF_SIZE_WITHOUT_PUB_INPUTS = 1907; HonkProof chonk_proof = create_mock_chonk_proof(); EXPECT_EQ(chonk_proof.size(), ChonkProof::PROOF_LENGTH); - EXPECT_EQ(chonk_proof.size(), - CURRENT_CHONK_PROOF_SIZE_WITHOUT_PUB_INPUTS + - stdlib::recursion::honk::HidingKernelIO::PUBLIC_INPUTS_SIZE) - << "The length of the Chonk proof changed."; } /** - * @brief Check that the size of a mock MultiLinearBatching proof matches expectation + * @brief Check that mock MultilinearBatching proof has the expected size */ -TEST(MockVerifierInputsTest, MockMultilinearBatchingProofSize) +TEST_F(MockVerifierInputsTest, MockMultilinearBatchingProofSize) { - size_t CURRENT_MULTILINEAR_BATCHING_PROOF_SIZE_WITHOUT_PUB_INPUTS = 121; + using Flavor = MultilinearBatchingFlavor; HonkProof batching_proof = create_mock_multilinear_batch_proof(); - EXPECT_EQ(batching_proof.size(), MultilinearBatchingFlavor::PROOF_LENGTH_WITHOUT_PUB_INPUTS()); - EXPECT_EQ(batching_proof.size(), CURRENT_MULTILINEAR_BATCHING_PROOF_SIZE_WITHOUT_PUB_INPUTS) - << "The length of the MultiLinearBatching proof changed."; + EXPECT_EQ(batching_proof.size(), ProofLength::MultilinearBatching::LENGTH); } diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/honk_contract.hpp b/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/honk_contract.hpp index a4ba36152f59..fbcbda5b7a84 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/honk_contract.hpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/honk_contract.hpp @@ -15,7 +15,7 @@ static const char HONK_CONTRACT_SOURCE[] = R"( pragma solidity ^0.8.27; interface IVerifier { - function verify(bytes calldata _proof, bytes32[] calldata _publicInputs) external returns (bool); + function verify(bytes calldata _proof, bytes32[] calldata _publicInputs) external view returns (bool); } type Fr is uint256; @@ -68,7 +68,7 @@ library FrLib { mstore(add(free, 0x20), 0x20) mstore(add(free, 0x40), 0x20) mstore(add(free, 0x60), v) - mstore(add(free, 0x80), sub(MODULUS, 2)) + mstore(add(free, 0x80), sub(MODULUS, 2)) mstore(add(free, 0xa0), MODULUS) let success := staticcall(gas(), 0x05, free, 0xc0, 0x00, 0x20) if iszero(success) { @@ -92,7 +92,7 @@ library FrLib { mstore(add(free, 0x20), 0x20) mstore(add(free, 0x40), 0x20) mstore(add(free, 0x60), b) - mstore(add(free, 0x80), v) + mstore(add(free, 0x80), v) mstore(add(free, 0xa0), MODULUS) let success := staticcall(gas(), 0x05, free, 0xc0, 0x00, 0x20) if iszero(success) { @@ -284,8 +284,6 @@ library Honk { struct RelationParameters { // challenges Fr eta; - Fr etaTwo; - Fr etaThree; Fr beta; Fr gamma; // derived @@ -409,10 +407,9 @@ library TranscriptLib { uint256 publicInputsSize, Fr previousChallenge ) internal pure returns (Honk.RelationParameters memory rp, Fr nextPreviousChallenge) { - (rp.eta, rp.etaTwo, rp.etaThree, previousChallenge) = - generateEtaChallenge(proof, publicInputs, vkHash, publicInputsSize); + (rp.eta, previousChallenge) = generateEtaChallenge(proof, publicInputs, vkHash, publicInputsSize); - (rp.beta, rp.gamma, nextPreviousChallenge) = generateBetaAndGammaChallenges(previousChallenge, proof); + (rp.beta, rp.gamma, nextPreviousChallenge) = generateBetaGammaChallenges(previousChallenge, proof); } function generateEtaChallenge( @@ -420,7 +417,7 @@ library TranscriptLib { bytes32[] calldata publicInputs, uint256 vkHash, uint256 publicInputsSize - ) internal pure returns (Fr eta, Fr etaTwo, Fr etaThree, Fr previousChallenge) { + ) internal pure returns (Fr eta, Fr previousChallenge) { bytes32[] memory round0 = new bytes32[](1 + publicInputsSize + 6); round0[0] = bytes32(vkHash); @@ -441,14 +438,10 @@ library TranscriptLib { round0[1 + publicInputsSize + 5] = bytes32(proof.w3.y); previousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(round0))); - (eta, etaTwo) = splitChallenge(previousChallenge); - - previousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(Fr.unwrap(previousChallenge)))); - Fr unused; - (etaThree, unused) = splitChallenge(previousChallenge); + (eta,) = splitChallenge(previousChallenge); } - function generateBetaAndGammaChallenges(Fr previousChallenge, Honk.Proof memory proof) + function generateBetaGammaChallenges(Fr previousChallenge, Honk.Proof memory proof) internal pure returns (Fr beta, Fr gamma, Fr nextPreviousChallenge) @@ -780,19 +773,23 @@ library RelationsLib { Fr read_term; // Calculate the write term (the table accumulation) + // write_term = table_1 + γ + table_2 * β + table_3 * β² + table_4 * β³ { - write_term = wire(p, WIRE.TABLE_1) + rp.gamma + (wire(p, WIRE.TABLE_2) * rp.eta) - + (wire(p, WIRE.TABLE_3) * rp.etaTwo) + (wire(p, WIRE.TABLE_4) * rp.etaThree); + Fr beta_sqr = rp.beta * rp.beta; + write_term = wire(p, WIRE.TABLE_1) + rp.gamma + (wire(p, WIRE.TABLE_2) * rp.beta) + + (wire(p, WIRE.TABLE_3) * beta_sqr) + (wire(p, WIRE.TABLE_4) * beta_sqr * rp.beta); } - // Calculate the write term + // Calculate the read term + // read_term = derived_entry_1 + γ + derived_entry_2 * β + derived_entry_3 * β² + q_index * β³ { + Fr beta_sqr = rp.beta * rp.beta; Fr derived_entry_1 = wire(p, WIRE.W_L) + rp.gamma + (wire(p, WIRE.Q_R) * wire(p, WIRE.W_L_SHIFT)); Fr derived_entry_2 = wire(p, WIRE.W_R) + wire(p, WIRE.Q_M) * wire(p, WIRE.W_R_SHIFT); Fr derived_entry_3 = wire(p, WIRE.W_O) + wire(p, WIRE.Q_C) * wire(p, WIRE.W_O_SHIFT); - read_term = derived_entry_1 + (derived_entry_2 * rp.eta) + (derived_entry_3 * rp.etaTwo) - + (wire(p, WIRE.Q_O) * rp.etaThree); + read_term = derived_entry_1 + (derived_entry_2 * rp.beta) + (derived_entry_3 * beta_sqr) + + (wire(p, WIRE.Q_O) * beta_sqr * rp.beta); } Fr read_inverse = wire(p, WIRE.LOOKUP_INVERSES) * write_term; @@ -986,6 +983,10 @@ library RelationsLib { ) internal pure { MemParams memory ap; + // Compute eta powers locally + Fr eta_two = rp.eta * rp.eta; + Fr eta_three = eta_two * rp.eta; + /** * MEMORY * @@ -1027,8 +1028,8 @@ library RelationsLib { * * For ROM gates, qc = 0 */ - ap.memory_record_check = wire(p, WIRE.W_O) * rp.etaThree; - ap.memory_record_check = ap.memory_record_check + (wire(p, WIRE.W_R) * rp.etaTwo); + ap.memory_record_check = wire(p, WIRE.W_O) * eta_three; + ap.memory_record_check = ap.memory_record_check + (wire(p, WIRE.W_R) * eta_two); ap.memory_record_check = ap.memory_record_check + (wire(p, WIRE.W_L) * rp.eta); ap.memory_record_check = ap.memory_record_check + wire(p, WIRE.Q_C); ap.partial_record_check = ap.memory_record_check; // used in RAM consistency check; deg 1 or 4 @@ -1088,8 +1089,8 @@ library RelationsLib { // reverse order we could re-use `ap.partial_record_check` 1 - ((w3' * eta + w2') * eta + w1') * eta // deg 1 or 4 - ap.next_gate_access_type = wire(p, WIRE.W_O_SHIFT) * rp.etaThree; - ap.next_gate_access_type = ap.next_gate_access_type + (wire(p, WIRE.W_R_SHIFT) * rp.etaTwo); + ap.next_gate_access_type = wire(p, WIRE.W_O_SHIFT) * eta_three; + ap.next_gate_access_type = ap.next_gate_access_type + (wire(p, WIRE.W_R_SHIFT) * eta_two); ap.next_gate_access_type = ap.next_gate_access_type + (wire(p, WIRE.W_L_SHIFT) * rp.eta); ap.next_gate_access_type = wire(p, WIRE.W_4_SHIFT) - ap.next_gate_access_type; diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/honk_optimized_contract.hpp b/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/honk_optimized_contract.hpp index b9cf87f0391b..8cb47176608f 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/honk_optimized_contract.hpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/honk_optimized_contract.hpp @@ -492,7 +492,7 @@ contract HonkVerifier is IVerifier { uint256 internal constant G1_Y_LOCATION = 0x80; uint256 internal constant SCALAR_LOCATION = 0xa0; - uint256 internal constant LOWER_127_MASK = 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; // 127 bits + uint256 internal constant LOWER_127_MASK = 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; // Group order uint256 internal constant Q = 21888242871839275222246405745257275088696311157297823662689037894645226208583; // EC group order @@ -542,7 +542,11 @@ contract HonkVerifier is IVerifier { constructor() {} - function verify(bytes calldata, /*proof*/ bytes32[] calldata /*public_inputs*/ ) + function verify( + bytes calldata, + /*proof*/ + bytes32[] calldata /*public_inputs*/ + ) public view override @@ -666,19 +670,16 @@ contract HonkVerifier is IVerifier { // 0x2e0 = 1 * 32 bytes + 3 * 64 bytes for (w1,w2,w3) + 0x200 for pairing points let eta_input_length := add(0x2e0, public_inputs_size) + // Get single eta challenge and compute powers (eta, eta², eta³) let prev_challenge := mod(keccak256(0x00, eta_input_length), p) mstore(0x00, prev_challenge) let eta := and(prev_challenge, LOWER_127_MASK) - let etaTwo := shr(127, prev_challenge) + let eta_two := mulmod(eta, eta, p) + let eta_three := mulmod(eta_two, eta, p) mstore(ETA_CHALLENGE, eta) - mstore(ETA_TWO_CHALLENGE, etaTwo) - - prev_challenge := mod(keccak256(0x00, 0x20), p) - - mstore(0x00, prev_challenge) - let eta_three := and(prev_challenge, LOWER_127_MASK) + mstore(ETA_TWO_CHALLENGE, eta_two) mstore(ETA_THREE_CHALLENGE, eta_three) /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ @@ -1352,7 +1353,9 @@ contract HonkVerifier is IVerifier { addmod( mload(QM_EVAL_LOC), addmod( - sub(p, mload(W1_SHIFT_EVAL_LOC)), addmod(mload(W1_EVAL_LOC), mload(W4_EVAL_LOC), p), p + sub(p, mload(W1_SHIFT_EVAL_LOC)), + addmod(mload(W1_EVAL_LOC), mload(W4_EVAL_LOC), p), + p ), p ), @@ -1399,55 +1402,51 @@ contract HonkVerifier is IVerifier { p ) let numerator := mulmod(t1, t2, p) - t1 := - mulmod( - add(add(mload(W1_EVAL_LOC), gamma), mulmod(beta, mload(SIGMA1_EVAL_LOC), p)), - add(add(mload(W2_EVAL_LOC), gamma), mulmod(beta, mload(SIGMA2_EVAL_LOC), p)), - p - ) - t2 := - mulmod( - add(add(mload(W3_EVAL_LOC), gamma), mulmod(beta, mload(SIGMA3_EVAL_LOC), p)), - add(add(mload(W4_EVAL_LOC), gamma), mulmod(beta, mload(SIGMA4_EVAL_LOC), p)), - p - ) + t1 := mulmod( + add(add(mload(W1_EVAL_LOC), gamma), mulmod(beta, mload(SIGMA1_EVAL_LOC), p)), + add(add(mload(W2_EVAL_LOC), gamma), mulmod(beta, mload(SIGMA2_EVAL_LOC), p)), + p + ) + t2 := mulmod( + add(add(mload(W3_EVAL_LOC), gamma), mulmod(beta, mload(SIGMA3_EVAL_LOC), p)), + add(add(mload(W4_EVAL_LOC), gamma), mulmod(beta, mload(SIGMA4_EVAL_LOC), p)), + p + ) let denominator := mulmod(t1, t2, p) { let acc := mulmod(addmod(mload(Z_PERM_EVAL_LOC), mload(LAGRANGE_FIRST_EVAL_LOC), p), numerator, p) - acc := - addmod( - acc, - sub( - p, - mulmod( - addmod( - mload(Z_PERM_SHIFT_EVAL_LOC), - mulmod( - mload(LAGRANGE_LAST_EVAL_LOC), - mload(PUBLIC_INPUTS_DELTA_NUMERATOR_CHALLENGE), - p - ), + acc := addmod( + acc, + sub( + p, + mulmod( + addmod( + mload(Z_PERM_SHIFT_EVAL_LOC), + mulmod( + mload(LAGRANGE_LAST_EVAL_LOC), + mload(PUBLIC_INPUTS_DELTA_NUMERATOR_CHALLENGE), p ), - denominator, p - ) - ), - p - ) + ), + denominator, + p + ) + ), + p + ) acc := mulmod(acc, mload(POW_PARTIAL_EVALUATION_LOC), p) mstore(SUBRELATION_EVAL_2_LOC, acc) - acc := - mulmod( - mulmod(mload(LAGRANGE_LAST_EVAL_LOC), mload(Z_PERM_SHIFT_EVAL_LOC), p), - mload(POW_PARTIAL_EVALUATION_LOC), - p - ) + acc := mulmod( + mulmod(mload(LAGRANGE_LAST_EVAL_LOC), mload(Z_PERM_SHIFT_EVAL_LOC), p), + mload(POW_PARTIAL_EVALUATION_LOC), + p + ) mstore(SUBRELATION_EVAL_3_LOC, acc) } } @@ -1455,49 +1454,59 @@ contract HonkVerifier is IVerifier { /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* LOGUP WIDGET EVALUATION */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ + // Note: Using beta powers for column batching and gamma for offset ensures soundness + // beta and gamma must be independent challenges (they come from splitting the same hash) { - let eta := mload(ETA_CHALLENGE) - let eta_two := mload(ETA_TWO_CHALLENGE) - let eta_three := mload(ETA_THREE_CHALLENGE) - - let beta := mload(BETA_CHALLENGE) let gamma := mload(GAMMA_CHALLENGE) + let beta := mload(BETA_CHALLENGE) + // Compute beta powers inline (β², β³) for lookup column batching + let beta_sqr := mulmod(beta, beta, p) + let beta_cube := mulmod(beta_sqr, beta, p) + // write_term = table_1 + γ + table_2 * β + table_3 * β² + table_4 * β³ let t0 := - addmod(addmod(mload(TABLE1_EVAL_LOC), gamma, p), mulmod(mload(TABLE2_EVAL_LOC), eta, p), p) + addmod(addmod(mload(TABLE1_EVAL_LOC), gamma, p), mulmod(mload(TABLE2_EVAL_LOC), beta, p), p) let t1 := - addmod(mulmod(mload(TABLE3_EVAL_LOC), eta_two, p), mulmod(mload(TABLE4_EVAL_LOC), eta_three, p), p) - let write_term := addmod(t0, t1, p) - - t0 := addmod( - addmod(mload(W1_EVAL_LOC), gamma, p), mulmod(mload(QR_EVAL_LOC), mload(W1_SHIFT_EVAL_LOC), p), p + mulmod(mload(TABLE3_EVAL_LOC), beta_sqr, p), + mulmod(mload(TABLE4_EVAL_LOC), beta_cube, p), + p ) + let write_term := addmod(t0, t1, p) + + // read_term = derived_entry_1 + γ + derived_entry_2 * β + derived_entry_3 * β² + q_index * β³ + t0 := addmod( + addmod(mload(W1_EVAL_LOC), gamma, p), + mulmod(mload(QR_EVAL_LOC), mload(W1_SHIFT_EVAL_LOC), p), + p + ) t1 := addmod(mload(W2_EVAL_LOC), mulmod(mload(QM_EVAL_LOC), mload(W2_SHIFT_EVAL_LOC), p), p) let t2 := addmod(mload(W3_EVAL_LOC), mulmod(mload(QC_EVAL_LOC), mload(W3_SHIFT_EVAL_LOC), p), p) - let read_term := addmod(t0, mulmod(t1, eta, p), p) - read_term := addmod(read_term, mulmod(t2, eta_two, p), p) - read_term := addmod(read_term, mulmod(mload(QO_EVAL_LOC), eta_three, p), p) + let read_term := addmod(t0, mulmod(t1, beta, p), p) + read_term := addmod(read_term, mulmod(t2, beta_sqr, p), p) + read_term := addmod(read_term, mulmod(mload(QO_EVAL_LOC), beta_cube, p), p) let read_inverse := mulmod(mload(LOOKUP_INVERSES_EVAL_LOC), write_term, p) let write_inverse := mulmod(mload(LOOKUP_INVERSES_EVAL_LOC), read_term, p) let inverse_exists_xor := addmod(mload(LOOKUP_READ_TAGS_EVAL_LOC), mload(QLOOKUP_EVAL_LOC), p) - inverse_exists_xor := - addmod( - inverse_exists_xor, - sub(p, mulmod(mload(LOOKUP_READ_TAGS_EVAL_LOC), mload(QLOOKUP_EVAL_LOC), p)), - p - ) + inverse_exists_xor := addmod( + inverse_exists_xor, + sub(p, mulmod(mload(LOOKUP_READ_TAGS_EVAL_LOC), mload(QLOOKUP_EVAL_LOC), p)), + p + ) let accumulator_none := mulmod(mulmod(read_term, write_term, p), mload(LOOKUP_INVERSES_EVAL_LOC), p) accumulator_none := addmod(accumulator_none, sub(p, inverse_exists_xor), p) accumulator_none := mulmod(accumulator_none, mload(POW_PARTIAL_EVALUATION_LOC), p) let accumulator_one := mulmod(mload(QLOOKUP_EVAL_LOC), read_inverse, p) - accumulator_one := - addmod(accumulator_one, sub(p, mulmod(mload(LOOKUP_READ_COUNTS_EVAL_LOC), write_inverse, p)), p) + accumulator_one := addmod( + accumulator_one, + sub(p, mulmod(mload(LOOKUP_READ_COUNTS_EVAL_LOC), write_inverse, p)), + p + ) let read_tag := mload(LOOKUP_READ_TAGS_EVAL_LOC) let read_tag_boolean_relation := mulmod(read_tag, addmod(read_tag, P_SUB_1, p), p) @@ -1592,8 +1601,11 @@ contract HonkVerifier is IVerifier { let y_diff := mulmod(mload(EC_Y_2), mload(EC_Q_SIGN), p) y_diff := addmod(y_diff, sub(p, mload(EC_Y_1)), p) let y_add_identity := mulmod(y1_plus_y3, x_diff, p) - y_add_identity := - addmod(y_add_identity, mulmod(addmod(mload(EC_X_3), sub(p, mload(EC_X_1)), p), y_diff, p), p) + y_add_identity := addmod( + y_add_identity, + mulmod(addmod(mload(EC_X_3), sub(p, mload(EC_X_1)), p), y_diff, p), + p + ) let eval := mulmod(y_add_identity, mload(POW_PARTIAL_EVALUATION_LOC), p) eval := mulmod(eval, mload(QELLIPTIC_EVAL_LOC), p) @@ -1625,17 +1637,18 @@ contract HonkVerifier is IVerifier { mulmod(addmod(addmod(mload(EC_X_1), mload(EC_X_1), p), mload(EC_X_1), p), mload(EC_X_1), p) let y_double_identity := mulmod(x1_sqr_mul_3, addmod(mload(EC_X_1), sub(p, mload(EC_X_3)), p), p) - y_double_identity := - addmod( - y_double_identity, - sub( - p, - mulmod( - addmod(mload(EC_Y_1), mload(EC_Y_1), p), addmod(mload(EC_Y_1), mload(EC_Y_3), p), p - ) - ), - p - ) + y_double_identity := addmod( + y_double_identity, + sub( + p, + mulmod( + addmod(mload(EC_Y_1), mload(EC_Y_1), p), + addmod(mload(EC_Y_1), mload(EC_Y_3), p), + p + ) + ), + p + ) let acc := mulmod(y_double_identity, mload(POW_PARTIAL_EVALUATION_LOC), p) acc := mulmod(mulmod(acc, mload(QELLIPTIC_EVAL_LOC), p), mload(EC_Q_IS_DOUBLE), p) @@ -1704,10 +1717,16 @@ contract HonkVerifier is IVerifier { */ // TODO(md): update these - formula has changed with lower degree let memory_record_check := mulmod(mload(W3_EVAL_LOC), mload(ETA_THREE_CHALLENGE), p) - memory_record_check := - addmod(memory_record_check, mulmod(mload(W2_EVAL_LOC), mload(ETA_TWO_CHALLENGE), p), p) - memory_record_check := - addmod(memory_record_check, mulmod(mload(W1_EVAL_LOC), mload(ETA_CHALLENGE), p), p) + memory_record_check := addmod( + memory_record_check, + mulmod(mload(W2_EVAL_LOC), mload(ETA_TWO_CHALLENGE), p), + p + ) + memory_record_check := addmod( + memory_record_check, + mulmod(mload(W1_EVAL_LOC), mload(ETA_CHALLENGE), p), + p + ) memory_record_check := addmod(memory_record_check, mload(QC_EVAL_LOC), p) let partial_record_check := memory_record_check @@ -1811,12 +1830,16 @@ contract HonkVerifier is IVerifier { * next_gate_access_type = w_4_omega - next_gate_access_type; */ let next_gate_access_type := mulmod(mload(W3_SHIFT_EVAL_LOC), mload(ETA_THREE_CHALLENGE), p) - next_gate_access_type := - addmod( - next_gate_access_type, mulmod(mload(W2_SHIFT_EVAL_LOC), mload(ETA_TWO_CHALLENGE), p), p - ) - next_gate_access_type := - addmod(next_gate_access_type, mulmod(mload(W1_SHIFT_EVAL_LOC), mload(ETA_CHALLENGE), p), p) + next_gate_access_type := addmod( + next_gate_access_type, + mulmod(mload(W2_SHIFT_EVAL_LOC), mload(ETA_TWO_CHALLENGE), p), + p + ) + next_gate_access_type := addmod( + next_gate_access_type, + mulmod(mload(W1_SHIFT_EVAL_LOC), mload(ETA_CHALLENGE), p), + p + ) next_gate_access_type := addmod(mload(W4_SHIFT_EVAL_LOC), sub(p, next_gate_access_type), p) // value_delta = w_3_omega - w_3 @@ -1899,33 +1922,32 @@ contract HonkVerifier is IVerifier { * auxiliary_identity *= alpha_base; */ let memory_identity := mload(AUX_ROM_CONSISTENCY_CHECK_IDENTITY) - memory_identity := - addmod( - memory_identity, - mulmod( - RAM_TIMESTAMP_CHECK_IDENTITY, mulmod(mload(Q4_EVAL_LOC), mload(QL_EVAL_LOC), p), p - ), + memory_identity := addmod( + memory_identity, + mulmod( + RAM_TIMESTAMP_CHECK_IDENTITY, + mulmod(mload(Q4_EVAL_LOC), mload(QL_EVAL_LOC), p), p - ) + ), + p + ) - memory_identity := - addmod( - memory_identity, - mulmod( - mload(AUX_MEMORY_CHECK_IDENTITY), - mulmod(mload(QM_EVAL_LOC), mload(QL_EVAL_LOC), p), - p - ), + memory_identity := addmod( + memory_identity, + mulmod( + mload(AUX_MEMORY_CHECK_IDENTITY), + mulmod(mload(QM_EVAL_LOC), mload(QL_EVAL_LOC), p), p - ) + ), + p + ) memory_identity := addmod(memory_identity, mload(AUX_RAM_CONSISTENCY_CHECK_IDENTITY), p) - memory_identity := - mulmod( - memory_identity, - mulmod(mload(QMEMORY_EVAL_LOC), mload(POW_PARTIAL_EVALUATION_LOC), p), - p - ) + memory_identity := mulmod( + memory_identity, + mulmod(mload(QMEMORY_EVAL_LOC), mload(POW_PARTIAL_EVALUATION_LOC), p), + p + ) mstore(SUBRELATION_EVAL_13_LOC, memory_identity) } } @@ -1977,8 +1999,11 @@ contract HonkVerifier is IVerifier { non_native_field_gate_2 := mulmod(non_native_field_gate_2, mload(Q4_EVAL_LOC), p) limb_subproduct := mulmod(limb_subproduct, LIMB_SIZE, p) - limb_subproduct := - addmod(limb_subproduct, mulmod(mload(W1_SHIFT_EVAL_LOC), mload(W2_SHIFT_EVAL_LOC), p), p) + limb_subproduct := addmod( + limb_subproduct, + mulmod(mload(W1_SHIFT_EVAL_LOC), mload(W2_SHIFT_EVAL_LOC), p), + p + ) let non_native_field_gate_1 := mulmod( @@ -1999,7 +2024,11 @@ contract HonkVerifier is IVerifier { ) let non_native_field_identity := mulmod( - addmod(addmod(non_native_field_gate_1, non_native_field_gate_2, p), non_native_field_gate_3, p), + addmod( + addmod(non_native_field_gate_1, non_native_field_gate_2, p), + non_native_field_gate_3, + p + ), mload(QR_EVAL_LOC), p ) @@ -2060,8 +2089,11 @@ contract HonkVerifier is IVerifier { limb_accumulator_identity := mulmod(limb_accumulator_identity, mload(QO_EVAL_LOC), p) let nnf_identity := addmod(mload(AUX_NON_NATIVE_FIELD_IDENTITY), limb_accumulator_identity, p) - nnf_identity := - mulmod(nnf_identity, mulmod(mload(QNNF_EVAL_LOC), mload(POW_PARTIAL_EVALUATION_LOC), p), p) + nnf_identity := mulmod( + nnf_identity, + mulmod(mload(QNNF_EVAL_LOC), mload(POW_PARTIAL_EVALUATION_LOC), p), + p + ) mstore(SUBRELATION_EVAL_19_LOC, nnf_identity) } @@ -2191,60 +2223,141 @@ contract HonkVerifier is IVerifier { // accumulator = accumulator + evaluations[i] * subrelationChallenges[i - 1]; // } - accumulator := - addmod(accumulator, mulmod(mload(SUBRELATION_EVAL_1_LOC), mload(ALPHA_CHALLENGE_0), p), p) - accumulator := - addmod(accumulator, mulmod(mload(SUBRELATION_EVAL_2_LOC), mload(ALPHA_CHALLENGE_1), p), p) - accumulator := - addmod(accumulator, mulmod(mload(SUBRELATION_EVAL_3_LOC), mload(ALPHA_CHALLENGE_2), p), p) - accumulator := - addmod(accumulator, mulmod(mload(SUBRELATION_EVAL_4_LOC), mload(ALPHA_CHALLENGE_3), p), p) - accumulator := - addmod(accumulator, mulmod(mload(SUBRELATION_EVAL_5_LOC), mload(ALPHA_CHALLENGE_4), p), p) - accumulator := - addmod(accumulator, mulmod(mload(SUBRELATION_EVAL_6_LOC), mload(ALPHA_CHALLENGE_5), p), p) - accumulator := - addmod(accumulator, mulmod(mload(SUBRELATION_EVAL_7_LOC), mload(ALPHA_CHALLENGE_6), p), p) - accumulator := - addmod(accumulator, mulmod(mload(SUBRELATION_EVAL_8_LOC), mload(ALPHA_CHALLENGE_7), p), p) - accumulator := - addmod(accumulator, mulmod(mload(SUBRELATION_EVAL_9_LOC), mload(ALPHA_CHALLENGE_8), p), p) - accumulator := - addmod(accumulator, mulmod(mload(SUBRELATION_EVAL_10_LOC), mload(ALPHA_CHALLENGE_9), p), p) - accumulator := - addmod(accumulator, mulmod(mload(SUBRELATION_EVAL_11_LOC), mload(ALPHA_CHALLENGE_10), p), p) - accumulator := - addmod(accumulator, mulmod(mload(SUBRELATION_EVAL_12_LOC), mload(ALPHA_CHALLENGE_11), p), p) - accumulator := - addmod(accumulator, mulmod(mload(SUBRELATION_EVAL_13_LOC), mload(ALPHA_CHALLENGE_12), p), p) - accumulator := - addmod(accumulator, mulmod(mload(SUBRELATION_EVAL_14_LOC), mload(ALPHA_CHALLENGE_13), p), p) - accumulator := - addmod(accumulator, mulmod(mload(SUBRELATION_EVAL_15_LOC), mload(ALPHA_CHALLENGE_14), p), p) - accumulator := - addmod(accumulator, mulmod(mload(SUBRELATION_EVAL_16_LOC), mload(ALPHA_CHALLENGE_15), p), p) - accumulator := - addmod(accumulator, mulmod(mload(SUBRELATION_EVAL_17_LOC), mload(ALPHA_CHALLENGE_16), p), p) - accumulator := - addmod(accumulator, mulmod(mload(SUBRELATION_EVAL_18_LOC), mload(ALPHA_CHALLENGE_17), p), p) - accumulator := - addmod(accumulator, mulmod(mload(SUBRELATION_EVAL_19_LOC), mload(ALPHA_CHALLENGE_18), p), p) - accumulator := - addmod(accumulator, mulmod(mload(SUBRELATION_EVAL_20_LOC), mload(ALPHA_CHALLENGE_19), p), p) - accumulator := - addmod(accumulator, mulmod(mload(SUBRELATION_EVAL_21_LOC), mload(ALPHA_CHALLENGE_20), p), p) - accumulator := - addmod(accumulator, mulmod(mload(SUBRELATION_EVAL_22_LOC), mload(ALPHA_CHALLENGE_21), p), p) - accumulator := - addmod(accumulator, mulmod(mload(SUBRELATION_EVAL_23_LOC), mload(ALPHA_CHALLENGE_22), p), p) - accumulator := - addmod(accumulator, mulmod(mload(SUBRELATION_EVAL_24_LOC), mload(ALPHA_CHALLENGE_23), p), p) - accumulator := - addmod(accumulator, mulmod(mload(SUBRELATION_EVAL_25_LOC), mload(ALPHA_CHALLENGE_24), p), p) - accumulator := - addmod(accumulator, mulmod(mload(SUBRELATION_EVAL_26_LOC), mload(ALPHA_CHALLENGE_25), p), p) - accumulator := - addmod(accumulator, mulmod(mload(SUBRELATION_EVAL_27_LOC), mload(ALPHA_CHALLENGE_26), p), p) + accumulator := addmod( + accumulator, + mulmod(mload(SUBRELATION_EVAL_1_LOC), mload(ALPHA_CHALLENGE_0), p), + p + ) + accumulator := addmod( + accumulator, + mulmod(mload(SUBRELATION_EVAL_2_LOC), mload(ALPHA_CHALLENGE_1), p), + p + ) + accumulator := addmod( + accumulator, + mulmod(mload(SUBRELATION_EVAL_3_LOC), mload(ALPHA_CHALLENGE_2), p), + p + ) + accumulator := addmod( + accumulator, + mulmod(mload(SUBRELATION_EVAL_4_LOC), mload(ALPHA_CHALLENGE_3), p), + p + ) + accumulator := addmod( + accumulator, + mulmod(mload(SUBRELATION_EVAL_5_LOC), mload(ALPHA_CHALLENGE_4), p), + p + ) + accumulator := addmod( + accumulator, + mulmod(mload(SUBRELATION_EVAL_6_LOC), mload(ALPHA_CHALLENGE_5), p), + p + ) + accumulator := addmod( + accumulator, + mulmod(mload(SUBRELATION_EVAL_7_LOC), mload(ALPHA_CHALLENGE_6), p), + p + ) + accumulator := addmod( + accumulator, + mulmod(mload(SUBRELATION_EVAL_8_LOC), mload(ALPHA_CHALLENGE_7), p), + p + ) + accumulator := addmod( + accumulator, + mulmod(mload(SUBRELATION_EVAL_9_LOC), mload(ALPHA_CHALLENGE_8), p), + p + ) + accumulator := addmod( + accumulator, + mulmod(mload(SUBRELATION_EVAL_10_LOC), mload(ALPHA_CHALLENGE_9), p), + p + ) + accumulator := addmod( + accumulator, + mulmod(mload(SUBRELATION_EVAL_11_LOC), mload(ALPHA_CHALLENGE_10), p), + p + ) + accumulator := addmod( + accumulator, + mulmod(mload(SUBRELATION_EVAL_12_LOC), mload(ALPHA_CHALLENGE_11), p), + p + ) + accumulator := addmod( + accumulator, + mulmod(mload(SUBRELATION_EVAL_13_LOC), mload(ALPHA_CHALLENGE_12), p), + p + ) + accumulator := addmod( + accumulator, + mulmod(mload(SUBRELATION_EVAL_14_LOC), mload(ALPHA_CHALLENGE_13), p), + p + ) + accumulator := addmod( + accumulator, + mulmod(mload(SUBRELATION_EVAL_15_LOC), mload(ALPHA_CHALLENGE_14), p), + p + ) + accumulator := addmod( + accumulator, + mulmod(mload(SUBRELATION_EVAL_16_LOC), mload(ALPHA_CHALLENGE_15), p), + p + ) + accumulator := addmod( + accumulator, + mulmod(mload(SUBRELATION_EVAL_17_LOC), mload(ALPHA_CHALLENGE_16), p), + p + ) + accumulator := addmod( + accumulator, + mulmod(mload(SUBRELATION_EVAL_18_LOC), mload(ALPHA_CHALLENGE_17), p), + p + ) + accumulator := addmod( + accumulator, + mulmod(mload(SUBRELATION_EVAL_19_LOC), mload(ALPHA_CHALLENGE_18), p), + p + ) + accumulator := addmod( + accumulator, + mulmod(mload(SUBRELATION_EVAL_20_LOC), mload(ALPHA_CHALLENGE_19), p), + p + ) + accumulator := addmod( + accumulator, + mulmod(mload(SUBRELATION_EVAL_21_LOC), mload(ALPHA_CHALLENGE_20), p), + p + ) + accumulator := addmod( + accumulator, + mulmod(mload(SUBRELATION_EVAL_22_LOC), mload(ALPHA_CHALLENGE_21), p), + p + ) + accumulator := addmod( + accumulator, + mulmod(mload(SUBRELATION_EVAL_23_LOC), mload(ALPHA_CHALLENGE_22), p), + p + ) + accumulator := addmod( + accumulator, + mulmod(mload(SUBRELATION_EVAL_24_LOC), mload(ALPHA_CHALLENGE_23), p), + p + ) + accumulator := addmod( + accumulator, + mulmod(mload(SUBRELATION_EVAL_25_LOC), mload(ALPHA_CHALLENGE_24), p), + p + ) + accumulator := addmod( + accumulator, + mulmod(mload(SUBRELATION_EVAL_26_LOC), mload(ALPHA_CHALLENGE_25), p), + p + ) + accumulator := addmod( + accumulator, + mulmod(mload(SUBRELATION_EVAL_27_LOC), mload(ALPHA_CHALLENGE_26), p), + p + ) let sumcheck_valid := eq(accumulator, mload(FINAL_ROUND_TARGET_LOC)) @@ -2284,7 +2397,8 @@ contract HonkVerifier is IVerifier { ) mstore( - POS_INVERTED_DENOM_0_LOC, addmod(eval_challenge, sub(p, mload(POWERS_OF_EVALUATION_CHALLENGE_0_LOC)), p) + POS_INVERTED_DENOM_0_LOC, + addmod(eval_challenge, sub(p, mload(POWERS_OF_EVALUATION_CHALLENGE_0_LOC)), p) ) mstore(NEG_INVERTED_DENOM_0_LOC, addmod(eval_challenge, mload(POWERS_OF_EVALUATION_CHALLENGE_0_LOC), p)) @@ -2374,18 +2488,17 @@ contract HonkVerifier is IVerifier { unshifted_scalar := addmod(pos_inverted_denominator, mulmod(shplonk_nu, neg_inverted_denominator, p), p) // accumulator takes the value of `INVERTED_GEMINI_DENOMINATOR_0` here - shifted_scalar := - mulmod( - accumulator, // (1 / gemini_r_challenge) - // (inverse_vanishing_evals[0]) - (shplonk_nu * inverse_vanishing_evals[1]) - addmod( - pos_inverted_denominator, - // - (shplonk_nu * inverse_vanishing_evals[1]) - sub(p, mulmod(shplonk_nu, neg_inverted_denominator, p)), - p - ), + shifted_scalar := mulmod( + accumulator, // (1 / gemini_r_challenge) + // (inverse_vanishing_evals[0]) - (shplonk_nu * inverse_vanishing_evals[1]) + addmod( + pos_inverted_denominator, + // - (shplonk_nu * inverse_vanishing_evals[1]) + sub(p, mulmod(shplonk_nu, neg_inverted_denominator, p)), p - ) + ), + p + ) } // TODO: Write a comment that describes the process of accumulating commitments and scalars @@ -2456,8 +2569,11 @@ contract HonkVerifier is IVerifier { // 9: QELLIPTIC_EVAL_LOC mstore(BATCH_SCALAR_10_LOC, mulmod(neg_unshifted_scalar, batching_challenge, p)) - batched_evaluation := - addmod(batched_evaluation, mulmod(mload(QELLIPTIC_EVAL_LOC), batching_challenge, p), p) + batched_evaluation := addmod( + batched_evaluation, + mulmod(mload(QELLIPTIC_EVAL_LOC), batching_challenge, p), + p + ) batching_challenge := mulmod(batching_challenge, rho, p) // 10: QMEMORY_EVAL_LOC @@ -2472,14 +2588,20 @@ contract HonkVerifier is IVerifier { // 12: QPOSEIDON2_EXTERNAL_EVAL_LOC mstore(BATCH_SCALAR_13_LOC, mulmod(neg_unshifted_scalar, batching_challenge, p)) - batched_evaluation := - addmod(batched_evaluation, mulmod(mload(QPOSEIDON2_EXTERNAL_EVAL_LOC), batching_challenge, p), p) + batched_evaluation := addmod( + batched_evaluation, + mulmod(mload(QPOSEIDON2_EXTERNAL_EVAL_LOC), batching_challenge, p), + p + ) batching_challenge := mulmod(batching_challenge, rho, p) // 13: QPOSEIDON2_INTERNAL_EVAL_LOC mstore(BATCH_SCALAR_14_LOC, mulmod(neg_unshifted_scalar, batching_challenge, p)) - batched_evaluation := - addmod(batched_evaluation, mulmod(mload(QPOSEIDON2_INTERNAL_EVAL_LOC), batching_challenge, p), p) + batched_evaluation := addmod( + batched_evaluation, + mulmod(mload(QPOSEIDON2_INTERNAL_EVAL_LOC), batching_challenge, p), + p + ) batching_challenge := mulmod(batching_challenge, rho, p) // 14: SIGMA1_EVAL_LOC @@ -2544,14 +2666,20 @@ contract HonkVerifier is IVerifier { // 26: LAGRANGE_FIRST_EVAL_LOC mstore(BATCH_SCALAR_27_LOC, mulmod(neg_unshifted_scalar, batching_challenge, p)) - batched_evaluation := - addmod(batched_evaluation, mulmod(mload(LAGRANGE_FIRST_EVAL_LOC), batching_challenge, p), p) + batched_evaluation := addmod( + batched_evaluation, + mulmod(mload(LAGRANGE_FIRST_EVAL_LOC), batching_challenge, p), + p + ) batching_challenge := mulmod(batching_challenge, rho, p) // 27: LAGRANGE_LAST_EVAL_LOC mstore(BATCH_SCALAR_28_LOC, mulmod(neg_unshifted_scalar, batching_challenge, p)) - batched_evaluation := - addmod(batched_evaluation, mulmod(mload(LAGRANGE_LAST_EVAL_LOC), batching_challenge, p), p) + batched_evaluation := addmod( + batched_evaluation, + mulmod(mload(LAGRANGE_LAST_EVAL_LOC), batching_challenge, p), + p + ) batching_challenge := mulmod(batching_challenge, rho, p) // 28: W1_EVAL_LOC @@ -2581,20 +2709,29 @@ contract HonkVerifier is IVerifier { // 33: LOOKUP_INVERSES_EVAL_LOC mstore(BATCH_SCALAR_34_LOC, mulmod(neg_unshifted_scalar, batching_challenge, p)) - batched_evaluation := - addmod(batched_evaluation, mulmod(mload(LOOKUP_INVERSES_EVAL_LOC), batching_challenge, p), p) + batched_evaluation := addmod( + batched_evaluation, + mulmod(mload(LOOKUP_INVERSES_EVAL_LOC), batching_challenge, p), + p + ) batching_challenge := mulmod(batching_challenge, rho, p) // 34: LOOKUP_READ_COUNTS_EVAL_LOC mstore(BATCH_SCALAR_35_LOC, mulmod(neg_unshifted_scalar, batching_challenge, p)) - batched_evaluation := - addmod(batched_evaluation, mulmod(mload(LOOKUP_READ_COUNTS_EVAL_LOC), batching_challenge, p), p) + batched_evaluation := addmod( + batched_evaluation, + mulmod(mload(LOOKUP_READ_COUNTS_EVAL_LOC), batching_challenge, p), + p + ) batching_challenge := mulmod(batching_challenge, rho, p) // 35: LOOKUP_READ_TAGS_EVAL_LOC mstore(BATCH_SCALAR_36_LOC, mulmod(neg_unshifted_scalar, batching_challenge, p)) - batched_evaluation := - addmod(batched_evaluation, mulmod(mload(LOOKUP_READ_TAGS_EVAL_LOC), batching_challenge, p), p) + batched_evaluation := addmod( + batched_evaluation, + mulmod(mload(LOOKUP_READ_TAGS_EVAL_LOC), batching_challenge, p), + p + ) batching_challenge := mulmod(batching_challenge, rho, p) // Unrolled for NUMBER_OF_SHIFTED_ENTITIES = 5 @@ -2641,8 +2778,11 @@ contract HonkVerifier is IVerifier { BATCH_SCALAR_33_LOC, addmod(mload(BATCH_SCALAR_33_LOC), mulmod(neg_shifted_scalar, batching_challenge, p), p) ) - batched_evaluation := - addmod(batched_evaluation, mulmod(mload(Z_PERM_SHIFT_EVAL_LOC), batching_challenge, p), p) + batched_evaluation := addmod( + batched_evaluation, + mulmod(mload(Z_PERM_SHIFT_EVAL_LOC), batching_challenge, p), + p + ) batching_challenge := mulmod(batching_challenge, rho, p) mstore(BATCHED_EVALUATION_LOC, batched_evaluation) @@ -2667,17 +2807,18 @@ contract HonkVerifier is IVerifier { // (challengePower * (ONE - u) - u) let chall_pow_times_1_minus_u := mulmod(chall_pow, addmod(1, sub(p, sum_check_u), p), p) - batchedEvalRoundAcc := - addmod( - batchedEvalRoundAcc, - sub( - p, - mulmod( - mload(mload(GEMINI_A_LOC)), addmod(chall_pow_times_1_minus_u, sub(p, sum_check_u), p), p - ) - ), - p - ) + batchedEvalRoundAcc := addmod( + batchedEvalRoundAcc, + sub( + p, + mulmod( + mload(mload(GEMINI_A_LOC)), + addmod(chall_pow_times_1_minus_u, sub(p, sum_check_u), p), + p + ) + ), + p + ) batchedEvalRoundAcc := mulmod(batchedEvalRoundAcc, mload(inverted_chall_pow_minus_u_loc), p) @@ -2696,12 +2837,11 @@ contract HonkVerifier is IVerifier { { let shplonk_nu := mload(SHPLONK_NU_CHALLENGE) - constant_term_acc := - addmod( - constant_term_acc, - mulmod(mload(GEMINI_A_EVAL_0), mulmod(shplonk_nu, mload(NEG_INVERTED_DENOM_0_LOC), p), p), - p - ) + constant_term_acc := addmod( + constant_term_acc, + mulmod(mload(GEMINI_A_EVAL_0), mulmod(shplonk_nu, mload(NEG_INVERTED_DENOM_0_LOC), p), p), + p + ) let shplonk_nu_sqr := mulmod(shplonk_nu, shplonk_nu, p) batching_challenge := shplonk_nu_sqr @@ -2724,8 +2864,11 @@ contract HonkVerifier is IVerifier { mstore(scalars_loc, addmod(sub(p, scaling_factor_neg), sub(p, scaling_factor_pos), p)) let accum_contribution := mulmod(scaling_factor_neg, mload(mload(SS_GEMINI_EVALS_LOC)), p) - accum_contribution := - addmod(accum_contribution, mulmod(scaling_factor_pos, mload(fold_pos_evals_loc), p), p) + accum_contribution := addmod( + accum_contribution, + mulmod(scaling_factor_pos, mload(fold_pos_evals_loc), p), + p + ) constant_term_acc := addmod(constant_term_acc, accum_contribution, p) @@ -2751,8 +2894,10 @@ contract HonkVerifier is IVerifier { let y := mload(SHPLONK_Q_Y_LOC) let xx := mulmod(x, x, q) // validate on curve - precomp_success_flag := - and(eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q)), precomp_success_flag) + precomp_success_flag := and( + eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q)), + precomp_success_flag + ) } mcopy(G1_LOCATION, SHPLONK_Q_X_LOC, 0x40) precomp_success_flag := staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR, 0x40) @@ -2764,278 +2909,408 @@ contract HonkVerifier is IVerifier { // Acumulator = acumulator + scalar[1] * vk[0] mcopy(G1_LOCATION, Q_M_X_LOC, 0x40) mstore(SCALAR_LOCATION, mload(BATCH_SCALAR_1_LOC)) - precomp_success_flag := - and(precomp_success_flag, staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40)) - precomp_success_flag := - and(precomp_success_flag, staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40)) + precomp_success_flag := and( + precomp_success_flag, + staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40) + ) + precomp_success_flag := and( + precomp_success_flag, + staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40) + ) // Accumulator = accumulator + scalar[2] * vk[1] mcopy(G1_LOCATION, Q_C_X_LOC, 0x40) mstore(SCALAR_LOCATION, mload(BATCH_SCALAR_2_LOC)) - precomp_success_flag := - and(precomp_success_flag, staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40)) - precomp_success_flag := - and(precomp_success_flag, staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40)) + precomp_success_flag := and( + precomp_success_flag, + staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40) + ) + precomp_success_flag := and( + precomp_success_flag, + staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40) + ) // Accumulator = accumulator + scalar[3] * vk[2] mcopy(G1_LOCATION, Q_L_X_LOC, 0x40) mstore(SCALAR_LOCATION, mload(BATCH_SCALAR_3_LOC)) - precomp_success_flag := - and(precomp_success_flag, staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40)) - precomp_success_flag := - and(precomp_success_flag, staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40)) + precomp_success_flag := and( + precomp_success_flag, + staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40) + ) + precomp_success_flag := and( + precomp_success_flag, + staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40) + ) // Accumulator = accumulator + scalar[4] * vk[3] mcopy(G1_LOCATION, Q_R_X_LOC, 0x40) mstore(SCALAR_LOCATION, mload(BATCH_SCALAR_4_LOC)) - precomp_success_flag := - and(precomp_success_flag, staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40)) - precomp_success_flag := - and(precomp_success_flag, staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40)) + precomp_success_flag := and( + precomp_success_flag, + staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40) + ) + precomp_success_flag := and( + precomp_success_flag, + staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40) + ) // Accumulator = accumulator + scalar[5] * vk[4] mcopy(G1_LOCATION, Q_O_X_LOC, 0x40) mstore(SCALAR_LOCATION, mload(BATCH_SCALAR_5_LOC)) - precomp_success_flag := - and(precomp_success_flag, staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40)) - precomp_success_flag := - and(precomp_success_flag, staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40)) + precomp_success_flag := and( + precomp_success_flag, + staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40) + ) + precomp_success_flag := and( + precomp_success_flag, + staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40) + ) // Accumulator = accumulator + scalar[6] * vk[5] mcopy(G1_LOCATION, Q_4_X_LOC, 0x40) mstore(SCALAR_LOCATION, mload(BATCH_SCALAR_6_LOC)) - precomp_success_flag := - and(precomp_success_flag, staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40)) - precomp_success_flag := - and(precomp_success_flag, staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40)) + precomp_success_flag := and( + precomp_success_flag, + staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40) + ) + precomp_success_flag := and( + precomp_success_flag, + staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40) + ) // Accumulator = accumulator + scalar[7] * vk[6] mcopy(G1_LOCATION, Q_LOOKUP_X_LOC, 0x40) mstore(SCALAR_LOCATION, mload(BATCH_SCALAR_7_LOC)) - precomp_success_flag := - and(precomp_success_flag, staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40)) - precomp_success_flag := - and(precomp_success_flag, staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40)) + precomp_success_flag := and( + precomp_success_flag, + staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40) + ) + precomp_success_flag := and( + precomp_success_flag, + staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40) + ) // Accumulator = accumulator + scalar[8] * vk[7] mcopy(G1_LOCATION, Q_ARITH_X_LOC, 0x40) mstore(SCALAR_LOCATION, mload(BATCH_SCALAR_8_LOC)) - precomp_success_flag := - and(precomp_success_flag, staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40)) - precomp_success_flag := - and(precomp_success_flag, staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40)) + precomp_success_flag := and( + precomp_success_flag, + staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40) + ) + precomp_success_flag := and( + precomp_success_flag, + staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40) + ) // Accumulator = accumulator + scalar[9] * vk[8] mcopy(G1_LOCATION, Q_DELTA_RANGE_X_LOC, 0x40) mstore(SCALAR_LOCATION, mload(BATCH_SCALAR_9_LOC)) - precomp_success_flag := - and(precomp_success_flag, staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40)) - precomp_success_flag := - and(precomp_success_flag, staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40)) + precomp_success_flag := and( + precomp_success_flag, + staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40) + ) + precomp_success_flag := and( + precomp_success_flag, + staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40) + ) // Accumulator = accumulator + scalar[10] * vk[9] mcopy(G1_LOCATION, Q_ELLIPTIC_X_LOC, 0x40) mstore(SCALAR_LOCATION, mload(BATCH_SCALAR_10_LOC)) - precomp_success_flag := - and(precomp_success_flag, staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40)) - precomp_success_flag := - and(precomp_success_flag, staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40)) + precomp_success_flag := and( + precomp_success_flag, + staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40) + ) + precomp_success_flag := and( + precomp_success_flag, + staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40) + ) // Accumulator = accumulator + scalar[11] * vk[10] mcopy(G1_LOCATION, Q_MEMORY_X_LOC, 0x40) mstore(SCALAR_LOCATION, mload(BATCH_SCALAR_11_LOC)) - precomp_success_flag := - and(precomp_success_flag, staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40)) - precomp_success_flag := - and(precomp_success_flag, staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40)) + precomp_success_flag := and( + precomp_success_flag, + staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40) + ) + precomp_success_flag := and( + precomp_success_flag, + staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40) + ) // Accumulator = accumulator + scalar[12] * vk[11] mcopy(G1_LOCATION, Q_NNF_X_LOC, 0x40) mstore(SCALAR_LOCATION, mload(BATCH_SCALAR_12_LOC)) - precomp_success_flag := - and(precomp_success_flag, staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40)) - precomp_success_flag := - and(precomp_success_flag, staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40)) + precomp_success_flag := and( + precomp_success_flag, + staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40) + ) + precomp_success_flag := and( + precomp_success_flag, + staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40) + ) // Accumulator = accumulator + scalar[13] * vk[12] mcopy(G1_LOCATION, Q_POSEIDON_2_EXTERNAL_X_LOC, 0x40) mstore(SCALAR_LOCATION, mload(BATCH_SCALAR_13_LOC)) - precomp_success_flag := - and(precomp_success_flag, staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40)) - precomp_success_flag := - and(precomp_success_flag, staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40)) + precomp_success_flag := and( + precomp_success_flag, + staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40) + ) + precomp_success_flag := and( + precomp_success_flag, + staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40) + ) // Accumulator = accumulator + scalar[14] * vk[13] mcopy(G1_LOCATION, Q_POSEIDON_2_INTERNAL_X_LOC, 0x40) mstore(SCALAR_LOCATION, mload(BATCH_SCALAR_14_LOC)) - precomp_success_flag := - and(precomp_success_flag, staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40)) - precomp_success_flag := - and(precomp_success_flag, staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40)) + precomp_success_flag := and( + precomp_success_flag, + staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40) + ) + precomp_success_flag := and( + precomp_success_flag, + staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40) + ) // Accumulator = accumulator + scalar[15] * vk[14] mcopy(G1_LOCATION, SIGMA_1_X_LOC, 0x40) mstore(SCALAR_LOCATION, mload(BATCH_SCALAR_15_LOC)) - precomp_success_flag := - and(precomp_success_flag, staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40)) - precomp_success_flag := - and(precomp_success_flag, staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40)) + precomp_success_flag := and( + precomp_success_flag, + staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40) + ) + precomp_success_flag := and( + precomp_success_flag, + staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40) + ) // Accumulator = accumulator + scalar[16] * vk[15] mcopy(G1_LOCATION, SIGMA_2_X_LOC, 0x40) mstore(SCALAR_LOCATION, mload(BATCH_SCALAR_16_LOC)) - precomp_success_flag := - and(precomp_success_flag, staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40)) - precomp_success_flag := - and(precomp_success_flag, staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40)) + precomp_success_flag := and( + precomp_success_flag, + staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40) + ) + precomp_success_flag := and( + precomp_success_flag, + staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40) + ) // Accumulator = accumulator + scalar[17] * vk[16] mcopy(G1_LOCATION, SIGMA_3_X_LOC, 0x40) mstore(SCALAR_LOCATION, mload(BATCH_SCALAR_17_LOC)) - precomp_success_flag := - and(precomp_success_flag, staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40)) - precomp_success_flag := - and(precomp_success_flag, staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40)) + precomp_success_flag := and( + precomp_success_flag, + staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40) + ) + precomp_success_flag := and( + precomp_success_flag, + staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40) + ) // Accumulator = accumulator + scalar[18] * vk[17] mcopy(G1_LOCATION, SIGMA_4_X_LOC, 0x40) mstore(SCALAR_LOCATION, mload(BATCH_SCALAR_18_LOC)) - precomp_success_flag := - and(precomp_success_flag, staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40)) - precomp_success_flag := - and(precomp_success_flag, staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40)) + precomp_success_flag := and( + precomp_success_flag, + staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40) + ) + precomp_success_flag := and( + precomp_success_flag, + staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40) + ) // Accumulator = accumulator + scalar[19] * vk[18] mcopy(G1_LOCATION, ID_1_X_LOC, 0x40) mstore(SCALAR_LOCATION, mload(BATCH_SCALAR_19_LOC)) - precomp_success_flag := - and(precomp_success_flag, staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40)) - precomp_success_flag := - and(precomp_success_flag, staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40)) + precomp_success_flag := and( + precomp_success_flag, + staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40) + ) + precomp_success_flag := and( + precomp_success_flag, + staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40) + ) // Accumulator = accumulator + scalar[20] * vk[19] mcopy(G1_LOCATION, ID_2_X_LOC, 0x40) mstore(SCALAR_LOCATION, mload(BATCH_SCALAR_20_LOC)) - precomp_success_flag := - and(precomp_success_flag, staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40)) - precomp_success_flag := - and(precomp_success_flag, staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40)) + precomp_success_flag := and( + precomp_success_flag, + staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40) + ) + precomp_success_flag := and( + precomp_success_flag, + staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40) + ) // Accumulator = accumulator + scalar[21] * vk[20] mcopy(G1_LOCATION, ID_3_X_LOC, 0x40) mstore(SCALAR_LOCATION, mload(BATCH_SCALAR_21_LOC)) - precomp_success_flag := - and(precomp_success_flag, staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40)) - precomp_success_flag := - and(precomp_success_flag, staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40)) + precomp_success_flag := and( + precomp_success_flag, + staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40) + ) + precomp_success_flag := and( + precomp_success_flag, + staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40) + ) // Accumulator = accumulator + scalar[22] * vk[21] mcopy(G1_LOCATION, ID_4_X_LOC, 0x40) mstore(SCALAR_LOCATION, mload(BATCH_SCALAR_22_LOC)) - precomp_success_flag := - and(precomp_success_flag, staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40)) - precomp_success_flag := - and(precomp_success_flag, staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40)) + precomp_success_flag := and( + precomp_success_flag, + staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40) + ) + precomp_success_flag := and( + precomp_success_flag, + staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40) + ) // Accumulator = accumulator + scalar[23] * vk[22] mcopy(G1_LOCATION, TABLE_1_X_LOC, 0x40) mstore(SCALAR_LOCATION, mload(BATCH_SCALAR_23_LOC)) - precomp_success_flag := - and(precomp_success_flag, staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40)) - precomp_success_flag := - and(precomp_success_flag, staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40)) + precomp_success_flag := and( + precomp_success_flag, + staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40) + ) + precomp_success_flag := and( + precomp_success_flag, + staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40) + ) // Accumulator = accumulator + scalar[24] * vk[23] mcopy(G1_LOCATION, TABLE_2_X_LOC, 0x40) mstore(SCALAR_LOCATION, mload(BATCH_SCALAR_24_LOC)) - precomp_success_flag := - and(precomp_success_flag, staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40)) - precomp_success_flag := - and(precomp_success_flag, staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40)) + precomp_success_flag := and( + precomp_success_flag, + staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40) + ) + precomp_success_flag := and( + precomp_success_flag, + staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40) + ) // Accumulator = accumulator + scalar[25] * vk[24] mcopy(G1_LOCATION, TABLE_3_X_LOC, 0x40) mstore(SCALAR_LOCATION, mload(BATCH_SCALAR_25_LOC)) - precomp_success_flag := - and(precomp_success_flag, staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40)) - precomp_success_flag := - and(precomp_success_flag, staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40)) + precomp_success_flag := and( + precomp_success_flag, + staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40) + ) + precomp_success_flag := and( + precomp_success_flag, + staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40) + ) // Accumulator = accumulator + scalar[26] * vk[25] mcopy(G1_LOCATION, TABLE_4_X_LOC, 0x40) mstore(SCALAR_LOCATION, mload(BATCH_SCALAR_26_LOC)) - precomp_success_flag := - and(precomp_success_flag, staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40)) - precomp_success_flag := - and(precomp_success_flag, staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40)) + precomp_success_flag := and( + precomp_success_flag, + staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40) + ) + precomp_success_flag := and( + precomp_success_flag, + staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40) + ) // Accumulator = accumulator + scalar[27] * vk[26] mcopy(G1_LOCATION, LAGRANGE_FIRST_X_LOC, 0x40) mstore(SCALAR_LOCATION, mload(BATCH_SCALAR_27_LOC)) - precomp_success_flag := - and(precomp_success_flag, staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40)) - precomp_success_flag := - and(precomp_success_flag, staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40)) + precomp_success_flag := and( + precomp_success_flag, + staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40) + ) + precomp_success_flag := and( + precomp_success_flag, + staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40) + ) // Accumulator = accumulator + scalar[28] * vk[27] mcopy(G1_LOCATION, LAGRANGE_LAST_X_LOC, 0x40) mstore(SCALAR_LOCATION, mload(BATCH_SCALAR_28_LOC)) - precomp_success_flag := - and(precomp_success_flag, staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40)) - precomp_success_flag := - and(precomp_success_flag, staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40)) + precomp_success_flag := and( + precomp_success_flag, + staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40) + ) + precomp_success_flag := and( + precomp_success_flag, + staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40) + ) { let x := mload(W_L_X_LOC) let y := mload(W_L_Y_LOC) let xx := mulmod(x, x, q) // validate on curve - precomp_success_flag := - and(eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q)), precomp_success_flag) + precomp_success_flag := and( + eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q)), + precomp_success_flag + ) } // Accumulate proof points // Accumulator = accumulator + scalar[29] * w_l mcopy(G1_LOCATION, W_L_X_LOC, 0x40) mstore(SCALAR_LOCATION, mload(BATCH_SCALAR_29_LOC)) - precomp_success_flag := - and(precomp_success_flag, staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40)) - precomp_success_flag := - and(precomp_success_flag, staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40)) + precomp_success_flag := and( + precomp_success_flag, + staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40) + ) + precomp_success_flag := and( + precomp_success_flag, + staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40) + ) { let x := mload(W_R_X_LOC) let y := mload(W_R_Y_LOC) let xx := mulmod(x, x, q) // validate on curve - precomp_success_flag := - and(eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q)), precomp_success_flag) + precomp_success_flag := and( + eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q)), + precomp_success_flag + ) } // Accumulator = accumulator + scalar[30] * w_r mcopy(G1_LOCATION, W_R_X_LOC, 0x40) mstore(SCALAR_LOCATION, mload(BATCH_SCALAR_30_LOC)) - precomp_success_flag := - and(precomp_success_flag, staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40)) - precomp_success_flag := - and(precomp_success_flag, staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40)) + precomp_success_flag := and( + precomp_success_flag, + staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40) + ) + precomp_success_flag := and( + precomp_success_flag, + staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40) + ) { let x := mload(W_O_X_LOC) let y := mload(W_O_Y_LOC) let xx := mulmod(x, x, q) // validate on curve - precomp_success_flag := - and(eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q)), precomp_success_flag) + precomp_success_flag := and( + eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q)), + precomp_success_flag + ) } // Accumulator = accumulator + scalar[31] * w_o mcopy(G1_LOCATION, W_O_X_LOC, 0x40) mstore(SCALAR_LOCATION, mload(BATCH_SCALAR_31_LOC)) - precomp_success_flag := - and(precomp_success_flag, staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40)) - precomp_success_flag := - and(precomp_success_flag, staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40)) + precomp_success_flag := and( + precomp_success_flag, + staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40) + ) + precomp_success_flag := and( + precomp_success_flag, + staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40) + ) // Accumulator = accumulator + scalar[32] * w_4 { @@ -3043,79 +3318,109 @@ contract HonkVerifier is IVerifier { let y := mload(W_4_Y_LOC) let xx := mulmod(x, x, q) // validate on curve - precomp_success_flag := - and(eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q)), precomp_success_flag) + precomp_success_flag := and( + eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q)), + precomp_success_flag + ) } mcopy(G1_LOCATION, W_4_X_LOC, 0x40) mstore(SCALAR_LOCATION, mload(BATCH_SCALAR_32_LOC)) - precomp_success_flag := - and(precomp_success_flag, staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40)) - precomp_success_flag := - and(precomp_success_flag, staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40)) + precomp_success_flag := and( + precomp_success_flag, + staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40) + ) + precomp_success_flag := and( + precomp_success_flag, + staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40) + ) { let x := mload(Z_PERM_X_LOC) let y := mload(Z_PERM_Y_LOC) let xx := mulmod(x, x, q) // validate on curve - precomp_success_flag := - and(eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q)), precomp_success_flag) + precomp_success_flag := and( + eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q)), + precomp_success_flag + ) } // Accumulator = accumulator + scalar[33] * z_perm mcopy(G1_LOCATION, Z_PERM_X_LOC, 0x40) mstore(SCALAR_LOCATION, mload(BATCH_SCALAR_33_LOC)) - precomp_success_flag := - and(precomp_success_flag, staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40)) - precomp_success_flag := - and(precomp_success_flag, staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40)) + precomp_success_flag := and( + precomp_success_flag, + staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40) + ) + precomp_success_flag := and( + precomp_success_flag, + staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40) + ) { let x := mload(LOOKUP_INVERSES_X_LOC) let y := mload(LOOKUP_INVERSES_Y_LOC) let xx := mulmod(x, x, q) // validate on curve - precomp_success_flag := - and(eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q)), precomp_success_flag) + precomp_success_flag := and( + eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q)), + precomp_success_flag + ) } // Accumulator = accumulator + scalar[34] * lookup_inverses mcopy(G1_LOCATION, LOOKUP_INVERSES_X_LOC, 0x40) mstore(SCALAR_LOCATION, mload(BATCH_SCALAR_34_LOC)) - precomp_success_flag := - and(precomp_success_flag, staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40)) - precomp_success_flag := - and(precomp_success_flag, staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40)) + precomp_success_flag := and( + precomp_success_flag, + staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40) + ) + precomp_success_flag := and( + precomp_success_flag, + staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40) + ) { let x := mload(LOOKUP_READ_COUNTS_X_LOC) let y := mload(LOOKUP_READ_COUNTS_Y_LOC) let xx := mulmod(x, x, q) // validate on curve - precomp_success_flag := - and(eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q)), precomp_success_flag) + precomp_success_flag := and( + eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q)), + precomp_success_flag + ) } // Accumulator = accumulator + scalar[35] * lookup_read_counts mcopy(G1_LOCATION, LOOKUP_READ_COUNTS_X_LOC, 0x40) mstore(SCALAR_LOCATION, mload(BATCH_SCALAR_35_LOC)) - precomp_success_flag := - and(precomp_success_flag, staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40)) - precomp_success_flag := - and(precomp_success_flag, staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40)) + precomp_success_flag := and( + precomp_success_flag, + staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40) + ) + precomp_success_flag := and( + precomp_success_flag, + staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40) + ) { let x := mload(LOOKUP_READ_TAGS_X_LOC) let y := mload(LOOKUP_READ_TAGS_Y_LOC) let xx := mulmod(x, x, q) // validate on curve - precomp_success_flag := - and(eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q)), precomp_success_flag) + precomp_success_flag := and( + eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q)), + precomp_success_flag + ) } // Accumulator = accumulator + scalar[36] * lookup_read_tags mcopy(G1_LOCATION, LOOKUP_READ_TAGS_X_LOC, 0x40) mstore(SCALAR_LOCATION, mload(BATCH_SCALAR_36_LOC)) - precomp_success_flag := - and(precomp_success_flag, staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40)) - precomp_success_flag := - and(precomp_success_flag, staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40)) + precomp_success_flag := and( + precomp_success_flag, + staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40) + ) + precomp_success_flag := and( + precomp_success_flag, + staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40) + ) // Accumulate these LOG_N scalars with the gemini fold univariates { @@ -3131,10 +3436,14 @@ contract HonkVerifier is IVerifier { mstore(G1_LOCATION, 0x01) mstore(G1_Y_LOCATION, 0x02) mstore(SCALAR_LOCATION, constant_term_acc) - precomp_success_flag := - and(precomp_success_flag, staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40)) - precomp_success_flag := - and(precomp_success_flag, staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40)) + precomp_success_flag := and( + precomp_success_flag, + staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40) + ) + precomp_success_flag := and( + precomp_success_flag, + staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40) + ) // Accumlate final quotient commitment into shplonk check // Accumulator = accumulator + shplonkZ * quotient commitment @@ -3143,16 +3452,22 @@ contract HonkVerifier is IVerifier { let y := mload(KZG_QUOTIENT_Y_LOC) let xx := mulmod(x, x, q) // validate on curve - precomp_success_flag := - and(eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q)), precomp_success_flag) + precomp_success_flag := and( + eq(mulmod(y, y, q), addmod(mulmod(x, xx, q), 3, q)), + precomp_success_flag + ) } mcopy(G1_LOCATION, KZG_QUOTIENT_X_LOC, 0x40) mstore(SCALAR_LOCATION, mload(SHPLONK_Z_CHALLENGE)) - precomp_success_flag := - and(precomp_success_flag, staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40)) - precomp_success_flag := - and(precomp_success_flag, staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40)) + precomp_success_flag := and( + precomp_success_flag, + staticcall(gas(), 7, G1_LOCATION, 0x60, ACCUMULATOR_2, 0x40) + ) + precomp_success_flag := and( + precomp_success_flag, + staticcall(gas(), 6, ACCUMULATOR, 0x80, ACCUMULATOR, 0x40) + ) } if iszero(precomp_success_flag) { diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/honk_zk_contract.hpp b/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/honk_zk_contract.hpp index 7c42cd599347..28cda8f9a693 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/honk_zk_contract.hpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/honk_zk_contract.hpp @@ -15,7 +15,7 @@ static const char HONK_ZK_CONTRACT_SOURCE[] = R"( pragma solidity ^0.8.27; interface IVerifier { - function verify(bytes calldata _proof, bytes32[] calldata _publicInputs) external returns (bool); + function verify(bytes calldata _proof, bytes32[] calldata _publicInputs) external view returns (bool); } type Fr is uint256; @@ -68,7 +68,7 @@ library FrLib { mstore(add(free, 0x20), 0x20) mstore(add(free, 0x40), 0x20) mstore(add(free, 0x60), v) - mstore(add(free, 0x80), sub(MODULUS, 2)) + mstore(add(free, 0x80), sub(MODULUS, 2)) mstore(add(free, 0xa0), MODULUS) let success := staticcall(gas(), 0x05, free, 0xc0, 0x00, 0x20) if iszero(success) { @@ -92,7 +92,7 @@ library FrLib { mstore(add(free, 0x20), 0x20) mstore(add(free, 0x40), 0x20) mstore(add(free, 0x60), b) - mstore(add(free, 0x80), v) + mstore(add(free, 0x80), v) mstore(add(free, 0xa0), MODULUS) let success := staticcall(gas(), 0x05, free, 0xc0, 0x00, 0x20) if iszero(success) { @@ -284,8 +284,6 @@ library Honk { struct RelationParameters { // challenges Fr eta; - Fr etaTwo; - Fr etaThree; Fr beta; Fr gamma; // derived @@ -411,10 +409,9 @@ library ZKTranscriptLib { uint256 publicInputsSize, Fr previousChallenge ) internal pure returns (Honk.RelationParameters memory rp, Fr nextPreviousChallenge) { - (rp.eta, rp.etaTwo, rp.etaThree, previousChallenge) = - generateEtaChallenge(proof, publicInputs, vkHash, publicInputsSize); + (rp.eta, previousChallenge) = generateEtaChallenge(proof, publicInputs, vkHash, publicInputsSize); - (rp.beta, rp.gamma, nextPreviousChallenge) = generateBetaAndGammaChallenges(previousChallenge, proof); + (rp.beta, rp.gamma, nextPreviousChallenge) = generateBetaGammaChallenges(previousChallenge, proof); } function generateEtaChallenge( @@ -422,7 +419,7 @@ library ZKTranscriptLib { bytes32[] calldata publicInputs, uint256 vkHash, uint256 publicInputsSize - ) internal pure returns (Fr eta, Fr etaTwo, Fr etaThree, Fr previousChallenge) { + ) internal pure returns (Fr eta, Fr previousChallenge) { // Size: 1 (vkHash) + publicInputsSize + 8 (geminiMask(2) + 3 wires(6)) bytes32[] memory round0 = new bytes32[](1 + publicInputsSize + 8); round0[0] = bytes32(vkHash); @@ -448,13 +445,10 @@ library ZKTranscriptLib { round0[1 + publicInputsSize + 7] = bytes32(proof.w3.y); previousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(round0))); - (eta, etaTwo) = splitChallenge(previousChallenge); - previousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(Fr.unwrap(previousChallenge)))); - - (etaThree,) = splitChallenge(previousChallenge); + (eta,) = splitChallenge(previousChallenge); } - function generateBetaAndGammaChallenges(Fr previousChallenge, Honk.ZKProof memory proof) + function generateBetaGammaChallenges(Fr previousChallenge, Honk.ZKProof memory proof) internal pure returns (Fr beta, Fr gamma, Fr nextPreviousChallenge) @@ -835,19 +829,23 @@ library RelationsLib { Fr read_term; // Calculate the write term (the table accumulation) + // write_term = table_1 + γ + table_2 * β + table_3 * β² + table_4 * β³ { - write_term = wire(p, WIRE.TABLE_1) + rp.gamma + (wire(p, WIRE.TABLE_2) * rp.eta) - + (wire(p, WIRE.TABLE_3) * rp.etaTwo) + (wire(p, WIRE.TABLE_4) * rp.etaThree); + Fr beta_sqr = rp.beta * rp.beta; + write_term = wire(p, WIRE.TABLE_1) + rp.gamma + (wire(p, WIRE.TABLE_2) * rp.beta) + + (wire(p, WIRE.TABLE_3) * beta_sqr) + (wire(p, WIRE.TABLE_4) * beta_sqr * rp.beta); } - // Calculate the write term + // Calculate the read term + // read_term = derived_entry_1 + γ + derived_entry_2 * β + derived_entry_3 * β² + q_index * β³ { + Fr beta_sqr = rp.beta * rp.beta; Fr derived_entry_1 = wire(p, WIRE.W_L) + rp.gamma + (wire(p, WIRE.Q_R) * wire(p, WIRE.W_L_SHIFT)); Fr derived_entry_2 = wire(p, WIRE.W_R) + wire(p, WIRE.Q_M) * wire(p, WIRE.W_R_SHIFT); Fr derived_entry_3 = wire(p, WIRE.W_O) + wire(p, WIRE.Q_C) * wire(p, WIRE.W_O_SHIFT); - read_term = derived_entry_1 + (derived_entry_2 * rp.eta) + (derived_entry_3 * rp.etaTwo) - + (wire(p, WIRE.Q_O) * rp.etaThree); + read_term = derived_entry_1 + (derived_entry_2 * rp.beta) + (derived_entry_3 * beta_sqr) + + (wire(p, WIRE.Q_O) * beta_sqr * rp.beta); } Fr read_inverse = wire(p, WIRE.LOOKUP_INVERSES) * write_term; @@ -1041,6 +1039,10 @@ library RelationsLib { ) internal pure { MemParams memory ap; + // Compute eta powers locally + Fr eta_two = rp.eta * rp.eta; + Fr eta_three = eta_two * rp.eta; + /** * MEMORY * @@ -1082,8 +1084,8 @@ library RelationsLib { * * For ROM gates, qc = 0 */ - ap.memory_record_check = wire(p, WIRE.W_O) * rp.etaThree; - ap.memory_record_check = ap.memory_record_check + (wire(p, WIRE.W_R) * rp.etaTwo); + ap.memory_record_check = wire(p, WIRE.W_O) * eta_three; + ap.memory_record_check = ap.memory_record_check + (wire(p, WIRE.W_R) * eta_two); ap.memory_record_check = ap.memory_record_check + (wire(p, WIRE.W_L) * rp.eta); ap.memory_record_check = ap.memory_record_check + wire(p, WIRE.Q_C); ap.partial_record_check = ap.memory_record_check; // used in RAM consistency check; deg 1 or 4 @@ -1143,8 +1145,8 @@ library RelationsLib { // reverse order we could re-use `ap.partial_record_check` 1 - ((w3' * eta + w2') * eta + w1') * eta // deg 1 or 4 - ap.next_gate_access_type = wire(p, WIRE.W_O_SHIFT) * rp.etaThree; - ap.next_gate_access_type = ap.next_gate_access_type + (wire(p, WIRE.W_R_SHIFT) * rp.etaTwo); + ap.next_gate_access_type = wire(p, WIRE.W_O_SHIFT) * eta_three; + ap.next_gate_access_type = ap.next_gate_access_type + (wire(p, WIRE.W_R_SHIFT) * eta_two); ap.next_gate_access_type = ap.next_gate_access_type + (wire(p, WIRE.W_L_SHIFT) * rp.eta); ap.next_gate_access_type = wire(p, WIRE.W_4_SHIFT) - ap.next_gate_access_type; diff --git a/barretenberg/cpp/src/barretenberg/eccvm/eccvm.test.cpp b/barretenberg/cpp/src/barretenberg/eccvm/eccvm.test.cpp index 2a392abe00d2..f23a255b6f1b 100644 --- a/barretenberg/cpp/src/barretenberg/eccvm/eccvm.test.cpp +++ b/barretenberg/cpp/src/barretenberg/eccvm/eccvm.test.cpp @@ -278,7 +278,7 @@ TEST_F(ECCVMTests, ProofLengthCheck) std::shared_ptr prover_transcript = std::make_shared(); ECCVMProver prover(builder, prover_transcript); auto [proof, opening_claim] = prover.construct_proof(); - EXPECT_EQ(proof.size(), ECCVMFlavor::PROOF_LENGTH_WITHOUT_PUB_INPUTS); + EXPECT_EQ(proof.size(), ECCVMFlavor::PROOF_LENGTH); } TEST_F(ECCVMTests, BaseCaseFixedSize) diff --git a/barretenberg/cpp/src/barretenberg/eccvm/eccvm_flavor.hpp b/barretenberg/cpp/src/barretenberg/eccvm/eccvm_flavor.hpp index 2290258ae919..320a8dadcb8c 100644 --- a/barretenberg/cpp/src/barretenberg/eccvm/eccvm_flavor.hpp +++ b/barretenberg/cpp/src/barretenberg/eccvm/eccvm_flavor.hpp @@ -122,7 +122,7 @@ class ECCVMFlavor { static constexpr size_t num_frs_fq = FrCodec::calc_num_fields(); // Proof length formula - static constexpr size_t PROOF_LENGTH_WITHOUT_PUB_INPUTS = + static constexpr size_t PROOF_LENGTH = /* 1. NUM_WITNESS_ENTITIES commitments */ ((NUM_WITNESS_ENTITIES + NUM_MASKING_POLYNOMIALS) * num_frs_comm) + /* 2. Libra concatenation commitment*/ (num_frs_comm) + /* 3. Libra sum */ (num_frs_fq) + diff --git a/barretenberg/cpp/src/barretenberg/flavor/mega_avm_flavor.hpp b/barretenberg/cpp/src/barretenberg/flavor/mega_avm_flavor.hpp index 1f13baf048fa..1c4eb2aaaa73 100644 --- a/barretenberg/cpp/src/barretenberg/flavor/mega_avm_flavor.hpp +++ b/barretenberg/cpp/src/barretenberg/flavor/mega_avm_flavor.hpp @@ -20,21 +20,10 @@ class MegaAvmFlavor : public bb::MegaFlavor { // Override VIRTUAL_LOG_N for the AVM recursive verifier circuit static constexpr size_t VIRTUAL_LOG_N = MEGA_AVM_LOG_N; - // Override proof length calculations to use AVM VIRTUAL_LOG_N static constexpr size_t FINAL_PCS_MSM_SIZE(size_t log_n = VIRTUAL_LOG_N) { return NUM_UNSHIFTED_ENTITIES + log_n + 2; } - - static constexpr size_t DECIDER_PROOF_LENGTH(size_t virtual_log_n = VIRTUAL_LOG_N) - { - return MegaFlavor::DECIDER_PROOF_LENGTH(virtual_log_n); - } - - static constexpr size_t PROOF_LENGTH_WITHOUT_PUB_INPUTS(size_t virtual_log_n = VIRTUAL_LOG_N) - { - return MegaFlavor::PROOF_LENGTH_WITHOUT_PUB_INPUTS(virtual_log_n); - } }; } // namespace bb diff --git a/barretenberg/cpp/src/barretenberg/flavor/mega_flavor.hpp b/barretenberg/cpp/src/barretenberg/flavor/mega_flavor.hpp index 46d491ba8bc6..efa5e17f68b3 100644 --- a/barretenberg/cpp/src/barretenberg/flavor/mega_flavor.hpp +++ b/barretenberg/cpp/src/barretenberg/flavor/mega_flavor.hpp @@ -108,26 +108,6 @@ class MegaFlavor { static constexpr size_t num_frs_comm = FrCodec::calc_num_fields(); static constexpr size_t num_frs_fr = FrCodec::calc_num_fields(); - // Proof length formula methods - static constexpr size_t OINK_PROOF_LENGTH_WITHOUT_PUB_INPUTS = - /* 1. NUM_WITNESS_ENTITIES commitments */ (NUM_WITNESS_ENTITIES * num_frs_comm); - - static constexpr size_t DECIDER_PROOF_LENGTH(size_t virtual_log_n = VIRTUAL_LOG_N) - { - return /* 2. virtual_log_n sumcheck univariates */ - (virtual_log_n * BATCHED_RELATION_PARTIAL_LENGTH * num_frs_fr) + - /* 3. NUM_ALL_ENTITIES sumcheck evaluations */ (NUM_ALL_ENTITIES * num_frs_fr) + - /* 4. virtual_log_n - 1 Gemini Fold commitments */ ((virtual_log_n - 1) * num_frs_comm) + - /* 5. virtual_log_n Gemini a evaluations */ (virtual_log_n * num_frs_fr) + - /* 6. Shplonk Q commitment */ (num_frs_comm) + - /* 7. KZG W commitment */ (num_frs_comm); - } - - static constexpr size_t PROOF_LENGTH_WITHOUT_PUB_INPUTS(size_t virtual_log_n = VIRTUAL_LOG_N) - { - return OINK_PROOF_LENGTH_WITHOUT_PUB_INPUTS + DECIDER_PROOF_LENGTH(virtual_log_n); - } - // A challenge whose powers are used to batch subrelation contributions during Sumcheck static constexpr size_t NUM_SUBRELATIONS = compute_number_of_subrelations(); using SubrelationSeparator = FF; diff --git a/barretenberg/cpp/src/barretenberg/flavor/mega_recursive_flavor.hpp b/barretenberg/cpp/src/barretenberg/flavor/mega_recursive_flavor.hpp index 09c4bcd47807..f3d092bfdace 100644 --- a/barretenberg/cpp/src/barretenberg/flavor/mega_recursive_flavor.hpp +++ b/barretenberg/cpp/src/barretenberg/flavor/mega_recursive_flavor.hpp @@ -43,8 +43,9 @@ template class MegaRecursiveFlavor_ { using FF = typename Curve::ScalarField; using Commitment = typename Curve::Element; using NativeFlavor = MegaFlavor; - + using Codec = stdlib::StdlibCodec; using Transcript = StdlibTranscript; + static constexpr size_t VIRTUAL_LOG_N = MegaFlavor::VIRTUAL_LOG_N; // indicates when evaluating sumcheck, edges can be left as degree-1 monomials static constexpr bool USE_SHORT_MONOMIALS = MegaFlavor::USE_SHORT_MONOMIALS; @@ -65,6 +66,8 @@ template class MegaRecursiveFlavor_ { static constexpr size_t NUM_PRECOMPUTED_ENTITIES = MegaFlavor::NUM_PRECOMPUTED_ENTITIES; // The total number of witness entities not including shifts. static constexpr size_t NUM_WITNESS_ENTITIES = MegaFlavor::NUM_WITNESS_ENTITIES; + static constexpr size_t NUM_SHIFTED_ENTITIES = MegaFlavor::NUM_SHIFTED_ENTITIES; + static constexpr size_t NUM_UNSHIFTED_ENTITIES = MegaFlavor::NUM_UNSHIFTED_ENTITIES; // define the tuple of Relations that comprise the Sumcheck relation // Reuse the Relations from Mega diff --git a/barretenberg/cpp/src/barretenberg/flavor/mega_zk_flavor.hpp b/barretenberg/cpp/src/barretenberg/flavor/mega_zk_flavor.hpp index e50144712ad2..1ed4bf260250 100644 --- a/barretenberg/cpp/src/barretenberg/flavor/mega_zk_flavor.hpp +++ b/barretenberg/cpp/src/barretenberg/flavor/mega_zk_flavor.hpp @@ -47,10 +47,6 @@ class MegaZKFlavor : public bb::MegaFlavor { return NUM_UNSHIFTED_ENTITIES + log_n + 2 + NUM_LIBRA_COMMITMENTS; } - // Override OINK_PROOF_LENGTH to include gemini_masking_poly commitment (sent via commit_to_masking_poly) - static constexpr size_t OINK_PROOF_LENGTH_WITHOUT_PUB_INPUTS = - /* 1. NUM_WITNESS_ENTITIES commitments (includes gemini_masking_poly) */ (NUM_WITNESS_ENTITIES * num_frs_comm); - using AllValues = MegaFlavor::AllValues_; using ProverPolynomials = MegaFlavor::ProverPolynomials_; using PartiallyEvaluatedMultivariates = MegaFlavor::PartiallyEvaluatedMultivariates_; @@ -60,27 +56,6 @@ class MegaZKFlavor : public bb::MegaFlavor { template using ProverUnivariates = AllEntities>; using ExtendedEdges = ProverUnivariates; - // Proof length formula - static constexpr size_t PROOF_LENGTH_WITHOUT_PUB_INPUTS(size_t virtual_log_n = VIRTUAL_LOG_N) - { - return /* 1. NUM_WITNESS_ENTITIES commitments */ (NUM_WITNESS_ENTITIES * num_frs_comm) + - /* 2. Libra concatenation commitment*/ (num_frs_comm) + - /* 3. Libra sum */ (num_frs_fr) + - /* 4. virtual_log_n sumcheck univariates */ - (virtual_log_n * BATCHED_RELATION_PARTIAL_LENGTH * num_frs_fr) + - /* 5. NUM_ALL_ENTITIES sumcheck evaluations*/ (NUM_ALL_ENTITIES * num_frs_fr) + - /* 6. Libra claimed evaluation */ (num_frs_fr) + - /* 7. Libra grand sum commitment */ (num_frs_comm) + - /* 8. Libra quotient commitment */ (num_frs_comm) + - /* 9. virtual_log_n - 1 Gemini Fold commitments */ - ((virtual_log_n - 1) * num_frs_comm) + - /* 10. virtual_log_n Gemini a evaluations */ - (virtual_log_n * num_frs_fr) + - /* 11. NUM_SMALL_IPA_EVALUATIONS libra evals */ (NUM_SMALL_IPA_EVALUATIONS * num_frs_fr) + - /* 12. Shplonk Q commitment */ (num_frs_comm) + - /* 13. KZG W commitment */ (num_frs_comm); - } - using Transcript = NativeTranscript; using VKAndHash = MegaFlavor::VKAndHash; }; diff --git a/barretenberg/cpp/src/barretenberg/flavor/mega_zk_recursive_flavor.hpp b/barretenberg/cpp/src/barretenberg/flavor/mega_zk_recursive_flavor.hpp index 31513f8b3931..96fdd607093b 100644 --- a/barretenberg/cpp/src/barretenberg/flavor/mega_zk_recursive_flavor.hpp +++ b/barretenberg/cpp/src/barretenberg/flavor/mega_zk_recursive_flavor.hpp @@ -44,14 +44,10 @@ template class MegaZKRecursiveFlavor_ : public MegaRecurs static constexpr bool HasZK = true; - // The number of entities added for ZK (gemini_masking_poly) - static constexpr size_t NUM_MASKING_POLYNOMIALS = 1; - - static constexpr size_t VIRTUAL_LOG_N = MegaZKFlavor::VIRTUAL_LOG_N; - - // NUM_ALL_ENTITIES includes gemini_masking_poly - static constexpr size_t NUM_ALL_ENTITIES = - MegaRecursiveFlavor_::NUM_ALL_ENTITIES + NUM_MASKING_POLYNOMIALS; + // Get constants from NativeFlavor to ensure consistency + static constexpr size_t VIRTUAL_LOG_N = NativeFlavor::VIRTUAL_LOG_N; + static constexpr size_t NUM_WITNESS_ENTITIES = NativeFlavor::NUM_WITNESS_ENTITIES; + static constexpr size_t NUM_ALL_ENTITIES = NativeFlavor::NUM_ALL_ENTITIES; // BATCHED_RELATION_PARTIAL_LENGTH = algebraic degree of sumcheck relation *after* multiplying by the `pow_zeta` // random polynomial e.g. For \sum(x) [A(x) * B(x) + C(x)] * PowZeta(X), relation length = 2 and random relation diff --git a/barretenberg/cpp/src/barretenberg/flavor/multilinear_batching_flavor.hpp b/barretenberg/cpp/src/barretenberg/flavor/multilinear_batching_flavor.hpp index 2ab2dd24883d..d7949288a8e4 100644 --- a/barretenberg/cpp/src/barretenberg/flavor/multilinear_batching_flavor.hpp +++ b/barretenberg/cpp/src/barretenberg/flavor/multilinear_batching_flavor.hpp @@ -31,6 +31,7 @@ class MultilinearBatchingFlavor { using CommitmentKey = bb::CommitmentKey; using VerifierCommitmentKey = bb::VerifierCommitmentKey; using Transcript = NativeTranscript; + using Codec = FrCodec; // An upper bound on the size of the MultilinearBatching-circuits. `CONST_FOLDING_LOG_N` bounds the log circuit // sizes in the Chonk context. @@ -43,15 +44,18 @@ class MultilinearBatchingFlavor { // To achieve fixed proof size and that the recursive verifier circuit is constant, we are using padding in Sumcheck // and Shplemini static constexpr bool USE_PADDING = true; - // The number of multivariate polynomials on which a sumcheck prover sumcheck operates (including shifts). We often - // need containers of this size to hold related data, so we choose a name more agnostic than `NUM_POLYNOMIALS`. + // ============ PROOF STRUCTURE CONSTANTS ============ + // Number of accumulator commitments sent in proof (non_shifted + shifted). + // Note: instance commitments are computed by verifier from Oink witness commitments. + // Note: eq polynomials are computed from challenges, not committed. + static constexpr size_t NUM_ACCUMULATOR_COMMITMENTS = 2; + // Number of accumulator evaluations sent in proof (non_shifted + shifted). + static constexpr size_t NUM_ACCUMULATOR_EVALUATIONS = 2; + + // ============ SUMCHECK CONSTANTS ============ + // Total polynomials in sumcheck: 4 unshifted + 2 shifted views. static constexpr size_t NUM_ALL_ENTITIES = 6; - // The total number of witness entities not including shifts. - static constexpr size_t NUM_WITNESS_ENTITIES = 4; - // The number of shifted witness entities including derived witness entities static constexpr size_t NUM_SHIFTED_ENTITIES = 2; - // Number of accumulator evaluations sent in the proof (non_shifted + shifted) - static constexpr size_t NUM_ACCUMULATOR_EVALUATIONS = 2; // define the tuple of Relations that comprise the Sumcheck relation // Note: made generic for use in MegaRecursive. @@ -71,57 +75,32 @@ class MultilinearBatchingFlavor { static constexpr size_t NUM_SUBRELATIONS = compute_number_of_subrelations(); using SubrelationSeparator = FF; - static constexpr size_t num_frs_comm = FrCodec::calc_num_fields(); - static constexpr size_t num_frs_fr = FrCodec::calc_num_fields(); - - static constexpr size_t PROOF_LENGTH_WITHOUT_PUB_INPUTS() - { - return /*accumulator commitments*/ (NUM_WITNESS_ENTITIES / 2 * num_frs_comm) + - /*multivariate challenges*/ (VIRTUAL_LOG_N * num_frs_fr) + - /*witness evaluations*/ (NUM_WITNESS_ENTITIES / 2 * num_frs_fr) + - /*sumcheck univariates*/ (VIRTUAL_LOG_N * BATCHED_RELATION_PARTIAL_LENGTH * num_frs_fr) + - /*sumcheck evaluations*/ (NUM_ALL_ENTITIES * num_frs_fr); - } - - /** - * @brief Container for all witness polynomials used/constructed by the prover. - * @details Shifts are not included here since they do not occupy their own memory. - */ - template class WitnessEntities { - public: - DEFINE_FLAVOR_MEMBERS(DataType, - batched_unshifted_accumulator, // column 0: batched unshifted poly for accumulator - batched_unshifted_instance, // column 1: batched unshifted poly for instance - eq_accumulator, // column 2: eq(u, r_acc) - selects accumulator eval point - eq_instance); // column 3: eq(u, r_inst) - selects instance eval point - }; - - /** - * @brief Class for ShiftedEntities, containing the shifted witness polynomials. - */ - template class ShiftedEntities { - public: - DEFINE_FLAVOR_MEMBERS(DataType, - batched_shifted_accumulator, // column 0: batched shifted poly for accumulator - batched_shifted_instance // column 1: batched shifted poly for instance - ); - }; - /** - * @brief A base class labelling all entities (for instance, all of the polynomials used by the prover during - * sumcheck) in this Honk variant along with particular subsets of interest - * @details Used to build containers for: the prover's polynomial during sumcheck; the sumcheck's folded + * @brief All polynomials used in multilinear batching sumcheck. + * @details Used to build containers for: the prover's polynomials during sumcheck; the sumcheck's folded * polynomials; the univariates constructed during sumcheck; the evaluations produced by sumcheck. * - * Symbolically we have: AllEntities = WitnessEntities + ShiftedEntities. + * Layout: + * - batched_unshifted_accumulator: commitment SENT in proof + * - batched_unshifted_instance: commitment computed by verifier from Oink witness commitments + * - eq_accumulator/eq_instance: computed from challenges by both prover and verifier (not committed) + * - batched_shifted_*: shifted views of the batched polynomials */ - template - class AllEntities : public WitnessEntities, public ShiftedEntities { + template class AllEntities { public: - DEFINE_COMPOUND_GET_ALL(WitnessEntities, ShiftedEntities) - - auto get_unshifted() { return WitnessEntities::get_all(); }; - auto get_shifted() { return ShiftedEntities::get_all(); }; + DEFINE_FLAVOR_MEMBERS(DataType, + batched_unshifted_accumulator, // Accumulator's batched unshifted poly (committed) + batched_unshifted_instance, // Instance's batched unshifted poly (verifier computes) + eq_accumulator, // eq(u, r_acc) selector (derived from challenges) + eq_instance, // eq(u, r_inst) selector (derived from challenges) + batched_shifted_accumulator, // Accumulator's batched shifted poly + batched_shifted_instance); // Instance's batched shifted poly + + auto get_unshifted() + { + return RefArray{ batched_unshifted_accumulator, batched_unshifted_instance, eq_accumulator, eq_instance }; + }; + auto get_shifted() { return RefArray{ batched_shifted_accumulator, batched_shifted_instance }; }; }; /** @@ -248,28 +227,6 @@ class MultilinearBatchingFlavor { * @brief A container for univariates produced during the hot loop in sumcheck. */ using ExtendedEdges = ProverUnivariates; - - /** - * @brief A container for the witness commitments. - */ - using WitnessCommitments = WitnessEntities; - - /** - * @brief A container for commitment labels. - * @note It's debatable whether this should inherit from AllEntities. since most entries are not strictly needed. It - * has, however, been useful during debugging to have these labels available. - * - */ - class CommitmentLabels : public AllEntities { - public: - CommitmentLabels() - { - batched_unshifted_accumulator = "BATCHED_UNSHIFTED_ACCUMULATOR"; - batched_unshifted_instance = "BATCHED_UNSHIFTED_INSTANCE"; - batched_shifted_accumulator = "BATCHED_SHIFTED_ACCUMULATOR"; - batched_shifted_instance = "BATCHED_SHIFTED_INSTANCE"; - }; - }; }; // Type alias for external usage diff --git a/barretenberg/cpp/src/barretenberg/flavor/multilinear_batching_recursive_flavor.hpp b/barretenberg/cpp/src/barretenberg/flavor/multilinear_batching_recursive_flavor.hpp index e4cbf5fb7ec4..6b48dbc98da8 100644 --- a/barretenberg/cpp/src/barretenberg/flavor/multilinear_batching_recursive_flavor.hpp +++ b/barretenberg/cpp/src/barretenberg/flavor/multilinear_batching_recursive_flavor.hpp @@ -35,9 +35,9 @@ class MultilinearBatchingRecursiveFlavor { // need containers of this size to hold related data, so we choose a name more agnostic than `NUM_POLYNOMIALS`. static constexpr size_t NUM_ALL_ENTITIES = NativeFlavor::NUM_ALL_ENTITIES; - // The total number of witness entities not including shifts. - static constexpr size_t NUM_WITNESS_ENTITIES = NativeFlavor::NUM_WITNESS_ENTITIES; - // Number of accumulator evaluations sent in the proof (non_shifted + shifted) + // Number of accumulator commitments sent in proof (non_shifted + shifted). + static constexpr size_t NUM_ACCUMULATOR_COMMITMENTS = NativeFlavor::NUM_ACCUMULATOR_COMMITMENTS; + // Number of accumulator evaluations sent in proof (non_shifted + shifted). static constexpr size_t NUM_ACCUMULATOR_EVALUATIONS = NativeFlavor::NUM_ACCUMULATOR_EVALUATIONS; // define the tuple of Relations that comprise the Sumcheck relation @@ -60,8 +60,6 @@ class MultilinearBatchingRecursiveFlavor { using Base = NativeFlavor::AllEntities; using Base::Base; }; - - using CommitmentLabels = NativeFlavor::CommitmentLabels; }; } // namespace bb diff --git a/barretenberg/cpp/src/barretenberg/flavor/ultra_flavor.hpp b/barretenberg/cpp/src/barretenberg/flavor/ultra_flavor.hpp index a0ef4a6d1e0f..c29ee609a3fe 100644 --- a/barretenberg/cpp/src/barretenberg/flavor/ultra_flavor.hpp +++ b/barretenberg/cpp/src/barretenberg/flavor/ultra_flavor.hpp @@ -118,26 +118,6 @@ class UltraFlavor { static constexpr size_t num_frs_comm = FrCodec::calc_num_fields(); static constexpr size_t num_frs_fr = FrCodec::calc_num_fields(); - // Proof length formula methods - static constexpr size_t OINK_PROOF_LENGTH_WITHOUT_PUB_INPUTS = - /* 1. NUM_WITNESS_ENTITIES commitments */ (NUM_WITNESS_ENTITIES * num_frs_comm); - - static constexpr size_t DECIDER_PROOF_LENGTH(size_t virtual_log_n = VIRTUAL_LOG_N) - { - return /* 2. virtual_log_n sumcheck univariates */ - (virtual_log_n * BATCHED_RELATION_PARTIAL_LENGTH * num_frs_fr) + - /* 3. NUM_ALL_ENTITIES sumcheck evaluations */ (NUM_ALL_ENTITIES * num_frs_fr) + - /* 4. virtual_log_n - 1 Gemini Fold commitments */ ((virtual_log_n - 1) * num_frs_comm) + - /* 5. virtual_log_n Gemini a evaluations */ (virtual_log_n * num_frs_fr) + - /* 6. Shplonk Q commitment */ (num_frs_comm) + - /* 7. KZG W commitment */ (num_frs_comm); - } - - static constexpr size_t PROOF_LENGTH_WITHOUT_PUB_INPUTS(size_t virtual_log_n = VIRTUAL_LOG_N) - { - return OINK_PROOF_LENGTH_WITHOUT_PUB_INPUTS + DECIDER_PROOF_LENGTH(virtual_log_n); - } - static constexpr bool is_decider = true; /** diff --git a/barretenberg/cpp/src/barretenberg/flavor/ultra_keccak_flavor.hpp b/barretenberg/cpp/src/barretenberg/flavor/ultra_keccak_flavor.hpp index 51a989cf9ae8..e0fabbf276df 100644 --- a/barretenberg/cpp/src/barretenberg/flavor/ultra_keccak_flavor.hpp +++ b/barretenberg/cpp/src/barretenberg/flavor/ultra_keccak_flavor.hpp @@ -18,30 +18,6 @@ class UltraKeccakFlavor : public bb::UltraFlavor { static constexpr bool USE_PADDING = false; - // Override as proof length is different - static constexpr size_t num_elements_comm = U256Codec::calc_num_fields(); - static constexpr size_t num_elements_fr = U256Codec::calc_num_fields(); - - // Proof length formula methods - static constexpr size_t OINK_PROOF_LENGTH_WITHOUT_PUB_INPUTS = - /* 1. NUM_WITNESS_ENTITIES commitments */ (NUM_WITNESS_ENTITIES * num_elements_comm); - - static constexpr size_t DECIDER_PROOF_LENGTH(size_t virtual_log_n = VIRTUAL_LOG_N) - { - return /* 2. virtual_log_n sumcheck univariates */ - (virtual_log_n * BATCHED_RELATION_PARTIAL_LENGTH * num_elements_fr) + - /* 3. NUM_ALL_ENTITIES sumcheck evaluations */ (NUM_ALL_ENTITIES * num_elements_fr) + - /* 4. virtual_log_n - 1 Gemini Fold commitments */ ((virtual_log_n - 1) * num_elements_comm) + - /* 5. virtual_log_n Gemini a evaluations */ (virtual_log_n * num_elements_fr) + - /* 6. Shplonk Q commitment */ (num_elements_comm) + - /* 7. KZG W commitment */ (num_elements_comm); - } - - static constexpr size_t PROOF_LENGTH_WITHOUT_PUB_INPUTS(size_t virtual_log_n = VIRTUAL_LOG_N) - { - return OINK_PROOF_LENGTH_WITHOUT_PUB_INPUTS + DECIDER_PROOF_LENGTH(virtual_log_n); - } - using VerificationKey = NativeVerificationKey_, Codec, HashFunction, CommitmentKey>; // Specialize for Ultra (general case used in UltraRecursive). diff --git a/barretenberg/cpp/src/barretenberg/flavor/ultra_keccak_zk_flavor.hpp b/barretenberg/cpp/src/barretenberg/flavor/ultra_keccak_zk_flavor.hpp index de3b9504d92e..5682987dc5f4 100644 --- a/barretenberg/cpp/src/barretenberg/flavor/ultra_keccak_zk_flavor.hpp +++ b/barretenberg/cpp/src/barretenberg/flavor/ultra_keccak_zk_flavor.hpp @@ -43,11 +43,6 @@ class UltraKeccakZKFlavor : public UltraKeccakFlavor { return NUM_UNSHIFTED_ENTITIES + log_n + 2 + NUM_LIBRA_COMMITMENTS; } - // Override OINK_PROOF_LENGTH to include gemini_masking_poly commitment (sent via commit_to_masking_poly) - static constexpr size_t OINK_PROOF_LENGTH_WITHOUT_PUB_INPUTS = - /* 1. NUM_WITNESS_ENTITIES commitments (includes gemini_masking_poly) */ (NUM_WITNESS_ENTITIES * - num_elements_comm); - using AllValues = UltraFlavor::AllValues_; using ProverPolynomials = UltraFlavor::ProverPolynomials_; using PartiallyEvaluatedMultivariates = UltraFlavor::PartiallyEvaluatedMultivariates_; @@ -57,27 +52,6 @@ class UltraKeccakZKFlavor : public UltraKeccakFlavor { template using ProverUnivariates = AllEntities>; using ExtendedEdges = ProverUnivariates; - // Proof length formula method - static constexpr size_t PROOF_LENGTH_WITHOUT_PUB_INPUTS(size_t virtual_log_n = VIRTUAL_LOG_N) - { - return /* 1. NUM_WITNESS_ENTITIES commitments */ (NUM_WITNESS_ENTITIES * num_elements_comm) + - /* 2. Libra concatenation commitment*/ (num_elements_comm) + - /* 3. Libra sum */ (num_elements_fr) + - /* 4. virtual_log_n sumcheck univariates */ - (virtual_log_n * BATCHED_RELATION_PARTIAL_LENGTH * num_elements_fr) + - /* 5. NUM_ALL_ENTITIES sumcheck evaluations*/ (NUM_ALL_ENTITIES * num_elements_fr) + - /* 6. Libra claimed evaluation */ (num_elements_fr) + - /* 7. Libra grand sum commitment */ (num_elements_comm) + - /* 8. Libra quotient commitment */ (num_elements_comm) + - /* 9. virtual_log_n - 1 Gemini Fold commitments */ - ((virtual_log_n - 1) * num_elements_comm) + - /* 10. virtual_log_n Gemini a evaluations */ - (virtual_log_n * num_elements_fr) + - /* 11. NUM_SMALL_IPA_EVALUATIONS libra evals */ (NUM_SMALL_IPA_EVALUATIONS * num_elements_fr) + - /* 12. Shplonk Q commitment */ (num_elements_comm) + - /* 13. KZG W commitment */ (num_elements_comm); - } - using Transcript = UltraKeccakFlavor::Transcript; using VKAndHash = UltraKeccakFlavor::VKAndHash; }; diff --git a/barretenberg/cpp/src/barretenberg/flavor/ultra_recursive_flavor.hpp b/barretenberg/cpp/src/barretenberg/flavor/ultra_recursive_flavor.hpp index cdf488f410e7..e06c0fea7d9b 100644 --- a/barretenberg/cpp/src/barretenberg/flavor/ultra_recursive_flavor.hpp +++ b/barretenberg/cpp/src/barretenberg/flavor/ultra_recursive_flavor.hpp @@ -44,6 +44,7 @@ template class UltraRecursiveFlavor_ { using FF = typename Curve::ScalarField; using NativeFlavor = UltraFlavor; using NativeVerificationKey = NativeFlavor::VerificationKey; + using Codec = stdlib::StdlibCodec; using Transcript = StdlibTranscript; static constexpr size_t VIRTUAL_LOG_N = UltraFlavor::VIRTUAL_LOG_N; diff --git a/barretenberg/cpp/src/barretenberg/flavor/ultra_rollup_flavor.hpp b/barretenberg/cpp/src/barretenberg/flavor/ultra_rollup_flavor.hpp index f98d014ca025..a19a57206c54 100644 --- a/barretenberg/cpp/src/barretenberg/flavor/ultra_rollup_flavor.hpp +++ b/barretenberg/cpp/src/barretenberg/flavor/ultra_rollup_flavor.hpp @@ -12,14 +12,9 @@ namespace bb { /** * @brief UltraRollupFlavor extends UltraFlavor with IPA proof support. - * @details The only difference from UltraFlavor is that PROOF_LENGTH_WITHOUT_PUB_INPUTS includes IPA_PROOF_LENGTH. */ class UltraRollupFlavor : public bb::UltraFlavor { public: - static constexpr size_t PROOF_LENGTH_WITHOUT_PUB_INPUTS(size_t virtual_log_n = VIRTUAL_LOG_N) - { - return UltraFlavor::PROOF_LENGTH_WITHOUT_PUB_INPUTS(virtual_log_n) + IPA_PROOF_LENGTH; - } }; } // namespace bb diff --git a/barretenberg/cpp/src/barretenberg/flavor/ultra_rollup_recursive_flavor.hpp b/barretenberg/cpp/src/barretenberg/flavor/ultra_rollup_recursive_flavor.hpp index 16fbd61dae1e..70a72aca838c 100644 --- a/barretenberg/cpp/src/barretenberg/flavor/ultra_rollup_recursive_flavor.hpp +++ b/barretenberg/cpp/src/barretenberg/flavor/ultra_rollup_recursive_flavor.hpp @@ -13,9 +13,7 @@ namespace bb { /** * @brief The recursive counterpart to the "native" UltraRollupFlavor. * @details Nearly identical to UltraRecursiveFlavor_, but with NativeFlavor = UltraRollupFlavor. - * This distinction is needed for: - * 1. Concept checks (e.g., HasIPAAccumulator) that trigger different code paths - * 2. Access to UltraRollupFlavor-specific PROOF_LENGTH_WITHOUT_PUB_INPUTS + * This distinction is needed for concept checks (e.g., HasIPAAccumulator) that trigger different code paths. * * @tparam BuilderType Determines the arithmetization of the verifier circuit defined based on this flavor. */ diff --git a/barretenberg/cpp/src/barretenberg/flavor/ultra_zk_flavor.hpp b/barretenberg/cpp/src/barretenberg/flavor/ultra_zk_flavor.hpp index 925545c7f6df..3433ab3c86bf 100644 --- a/barretenberg/cpp/src/barretenberg/flavor/ultra_zk_flavor.hpp +++ b/barretenberg/cpp/src/barretenberg/flavor/ultra_zk_flavor.hpp @@ -51,10 +51,6 @@ class UltraZKFlavor : public UltraFlavor { return NUM_UNSHIFTED_ENTITIES + log_n + 2 + NUM_LIBRA_COMMITMENTS; } - // Override OINK_PROOF_LENGTH to include gemini_masking_poly commitment (sent via commit_to_masking_poly) - static constexpr size_t OINK_PROOF_LENGTH_WITHOUT_PUB_INPUTS = - /* 1. NUM_WITNESS_ENTITIES commitments (includes gemini_masking_poly) */ (NUM_WITNESS_ENTITIES * num_frs_comm); - using AllValues = UltraFlavor::AllValues_; using ProverPolynomials = UltraFlavor::ProverPolynomials_; using PartiallyEvaluatedMultivariates = UltraFlavor::PartiallyEvaluatedMultivariates_; @@ -63,26 +59,5 @@ class UltraZKFlavor : public UltraFlavor { // Override ProverUnivariates and ExtendedEdges to include gemini_masking_poly template using ProverUnivariates = AllEntities>; using ExtendedEdges = ProverUnivariates; - - // Proof length formula method - static constexpr size_t PROOF_LENGTH_WITHOUT_PUB_INPUTS(size_t virtual_log_n = CONST_PROOF_SIZE_LOG_N) - { - return /* 1. NUM_WITNESS_ENTITIES commitments */ (NUM_WITNESS_ENTITIES * num_frs_comm) + - /* 2. Libra concatenation commitment*/ (num_frs_comm) + - /* 3. Libra sum */ (num_frs_fr) + - /* 4. virtual_log_n sumcheck univariates */ - (virtual_log_n * BATCHED_RELATION_PARTIAL_LENGTH * num_frs_fr) + - /* 5. NUM_ALL_ENTITIES sumcheck evaluations*/ (NUM_ALL_ENTITIES * num_frs_fr) + - /* 6. Libra claimed evaluation */ (num_frs_fr) + - /* 7. Libra grand sum commitment */ (num_frs_comm) + - /* 8. Libra quotient commitment */ (num_frs_comm) + - /* 9. virtual_log_n - 1 Gemini Fold commitments */ - ((virtual_log_n - 1) * num_frs_comm) + - /* 10. virtual_log_n Gemini a evaluations */ - (virtual_log_n * num_frs_fr) + - /* 11. NUM_SMALL_IPA_EVALUATIONS libra evals */ (NUM_SMALL_IPA_EVALUATIONS * num_frs_fr) + - /* 12. Shplonk Q commitment */ (num_frs_comm) + - /* 13. KZG W commitment */ (num_frs_comm); - } }; } // namespace bb diff --git a/barretenberg/cpp/src/barretenberg/flavor/ultra_zk_recursive_flavor.hpp b/barretenberg/cpp/src/barretenberg/flavor/ultra_zk_recursive_flavor.hpp index 03fb78acb235..458196c66396 100644 --- a/barretenberg/cpp/src/barretenberg/flavor/ultra_zk_recursive_flavor.hpp +++ b/barretenberg/cpp/src/barretenberg/flavor/ultra_zk_recursive_flavor.hpp @@ -34,12 +34,9 @@ template class UltraZKRecursiveFlavor_ : public UltraRecu static constexpr bool HasZK = true; - // The number of entities added for ZK (gemini_masking_poly) - static constexpr size_t NUM_MASKING_POLYNOMIALS = 1; - - // NUM_ALL_ENTITIES includes gemini_masking_poly - static constexpr size_t NUM_ALL_ENTITIES = - UltraRecursiveFlavor_::NUM_ALL_ENTITIES + NUM_MASKING_POLYNOMIALS; + // Get constants from NativeFlavor to ensure consistency + static constexpr size_t NUM_WITNESS_ENTITIES = NativeFlavor::NUM_WITNESS_ENTITIES; + static constexpr size_t NUM_ALL_ENTITIES = NativeFlavor::NUM_ALL_ENTITIES; static constexpr size_t BATCHED_RELATION_PARTIAL_LENGTH = NativeFlavor::BATCHED_RELATION_PARTIAL_LENGTH; diff --git a/barretenberg/cpp/src/barretenberg/honk/proof_length.hpp b/barretenberg/cpp/src/barretenberg/honk/proof_length.hpp new file mode 100644 index 000000000000..356f06dd2fec --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/honk/proof_length.hpp @@ -0,0 +1,175 @@ +// === AUDIT STATUS === +// internal: { status: Planned, auditors: [], commit: } +// external_1: { status: not started, auditors: [], commit: } +// external_2: { status: not started, auditors: [], commit: } +// ===================== + +#pragma once + +#include "barretenberg/commitment_schemes/ipa/ipa.hpp" +#include "barretenberg/constants.hpp" +#include "barretenberg/ecc/fields/field_conversion.hpp" +#include "barretenberg/flavor/flavor.hpp" +#include + +namespace bb::ProofLength { + +/** + * @brief Codec constants computed from Flavor types. + * @details Uses Flavor::Codec to compute serialization sizes. All flavors define Codec: + * - Native flavors: FrCodec (or U256Codec for Keccak) + * - Recursive flavors: StdlibCodec + * Both codecs return the same values for calc_num_fields since proof serialization is identical. + */ +template struct CodecConstants { + using Codec = typename Flavor::Codec; + using Commitment = typename Flavor::Commitment; + using FF = typename Flavor::FF; + + static constexpr size_t num_frs_in_comm = Codec::template calc_num_fields(); + static constexpr size_t num_frs_in_scalar = Codec::template calc_num_fields(); +}; + +/** + * @brief Computes Oink proof length from flavor traits. + * @details Oink sends witness commitments (W_L, W_R, W_O, etc.). + * For ZK flavors, NUM_WITNESS_ENTITIES includes the Gemini masking polynomial commitment. + */ +template struct Oink : CodecConstants { + using CodecConstants::num_frs_in_comm; + + static constexpr size_t LENGTH_WITHOUT_PUB_INPUTS = Flavor::NUM_WITNESS_ENTITIES * num_frs_in_comm; +}; + +/** + * @brief Computes Sumcheck proof length from flavor traits. + * @details Sumcheck sends univariates (one per round) and final evaluations. + * ZK flavors add Libra masking data. + */ +template struct Sumcheck : CodecConstants { + using CodecConstants::num_frs_in_scalar; + using CodecConstants::num_frs_in_comm; + + static constexpr size_t LENGTH(size_t log_n) + { + size_t base_length = + /* univariates */ (log_n * Flavor::BATCHED_RELATION_PARTIAL_LENGTH * num_frs_in_scalar) + + /* evaluations */ (Flavor::NUM_ALL_ENTITIES * num_frs_in_scalar); + + if constexpr (Flavor::HasZK) { + // Libra adds: concatenation_commitment, grand_sum_commitment, quotient_commitment (3 comms) + // + Sum, claimed_evaluation (2 scalars) + return base_length + (3 * num_frs_in_comm) + (2 * num_frs_in_scalar); + } else { + return base_length; + } + } +}; + +/** + * @brief Computes Shplemini/PCS proof length from flavor traits. + * @details Shplemini reduces multivariate opening claims to a single KZG check. + * Contains: Gemini fold commitments, Gemini evaluations, Shplonk Q, KZG W. + * ZK flavors add small IPA evaluations for masking. + */ +template struct Shplemini : CodecConstants { + using CodecConstants::num_frs_in_scalar; + using CodecConstants::num_frs_in_comm; + + static constexpr size_t LENGTH(size_t log_n) + { + size_t base_length = + /* Gemini:FOLD_1..FOLD_{log_n-1} */ ((log_n - 1) * num_frs_in_comm) + + /* Gemini:a_1..a_{log_n} */ (log_n * num_frs_in_scalar) + + /* Shplonk:Q */ num_frs_in_comm + + /* KZG:W */ num_frs_in_comm; + + if constexpr (Flavor::HasZK) { + // ZK adds: Libra evaluations (concatenation, shifted_grand_sum, grand_sum, quotient) + return base_length + (NUM_SMALL_IPA_EVALUATIONS * num_frs_in_scalar); + } else { + return base_length; + } + } +}; + +/** + * @brief Full Honk proof layout (used by UltraVerifier). + * @details Honk proof = Oink + Sumcheck + Shplemini. + * Note: IPA proof is handled separately for rollup flavors (appended by prover, split by verifier). + */ +template struct Honk { + static constexpr size_t LENGTH_WITHOUT_PUB_INPUTS(size_t log_n) + { + return Oink::LENGTH_WITHOUT_PUB_INPUTS + Sumcheck::LENGTH(log_n) + + Shplemini::LENGTH(log_n); + } + + /** + * @brief Derive num_public_inputs from proof size. + * @param proof_size Total proof size in field elements + * @param log_n Log of circuit size (VIRTUAL_LOG_N for padded, vk->log_circuit_size for non-padded) + */ + static constexpr size_t derive_num_public_inputs(size_t proof_size, size_t log_n) + { + return proof_size - LENGTH_WITHOUT_PUB_INPUTS(log_n); + } +}; + +/** + * @brief Hypernova instance-to-accumulator proof layout. + * @details Used when converting a single instance to an accumulator (first circuit in folding). + * Contains: Oink + Sumcheck (no Shplemini - PCS is deferred). + * Note: gate challenge is derived via get_dyadic_powers_of_challenge, not sent. + */ +template struct HypernovaInstanceToAccum { + static constexpr size_t LENGTH_WITHOUT_PUB_INPUTS(size_t log_n) + { + return Oink::LENGTH_WITHOUT_PUB_INPUTS + Sumcheck::LENGTH(log_n); + } + + static constexpr size_t derive_num_public_inputs(size_t proof_size, size_t log_n) + { + return proof_size - LENGTH_WITHOUT_PUB_INPUTS(log_n); + } +}; + +/** + * @brief MultilinearBatching proof layout (used by HyperNova folding). + * @details Batches two accumulators (from previous fold + incoming instance) into one. + * Contains: accumulator commitments, multivariate challenges, evaluations, and sumcheck. + * Note: This protocol has no public inputs. + */ +template struct MultilinearBatching : CodecConstants { + using CodecConstants::num_frs_in_scalar; + using CodecConstants::num_frs_in_comm; + + static constexpr size_t LENGTH = + /* accumulator commitments (non_shifted + shifted) */ (Flavor::NUM_ACCUMULATOR_COMMITMENTS * num_frs_in_comm) + + /* multivariate challenges */ (Flavor::VIRTUAL_LOG_N * num_frs_in_scalar) + + /* accumulator evaluations (non_shifted + shifted) */ + (Flavor::NUM_ACCUMULATOR_EVALUATIONS * num_frs_in_scalar) + Sumcheck::LENGTH(Flavor::VIRTUAL_LOG_N); +}; + +/** + * @brief Hypernova folding proof layout. + * @details Used when folding an incoming instance with an existing accumulator. + * Contains: instance-to-accumulator proof (Oink + Sumcheck) + MultilinearBatching proof. + * Note: gate challenges are derived, not sent in the proof. + * @tparam Flavor The outer flavor (e.g., MegaFlavor) + * @tparam BatchingFlavor The batching flavor (e.g., MultilinearBatchingFlavor) + */ +template struct HypernovaFolding { + static constexpr size_t LENGTH_WITHOUT_PUB_INPUTS(size_t log_n) + { + return HypernovaInstanceToAccum::LENGTH_WITHOUT_PUB_INPUTS(log_n) + + MultilinearBatching::LENGTH; + } + + static constexpr size_t derive_num_public_inputs(size_t proof_size, size_t log_n) + { + return proof_size - LENGTH_WITHOUT_PUB_INPUTS(log_n); + } +}; + +} // namespace bb::ProofLength diff --git a/barretenberg/cpp/src/barretenberg/honk/proof_system/LOGUP_README.md b/barretenberg/cpp/src/barretenberg/honk/proof_system/LOGUP_README.md new file mode 100644 index 000000000000..bf1a3887fa95 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/honk/proof_system/LOGUP_README.md @@ -0,0 +1,94 @@ +# Log-Derivative Lookup (LogUp) + +Central reference for the log-derivative lookup argument in `logderiv_lookup_relation.hpp`. + +## Overview + +The log-derivative approach replaces grand-product-based lookups (like plookup) with a sum of inverses. Instead of computing a running product, we prove: + +$$\sum_i \left[ q_i \cdot \frac{1}{W_i} - c_i \cdot \frac{1}{R_i} \right] = 0$$ + +where $q$ is the lookup selector, $W$ is the **write** (table) term, $R$ is the **read** (wire) term, and $c$ is the read **count**. + +This is motivated by taking the derivative of the log of the grand product identity. + +## Term Encoding + +Using independent challenges $\gamma$ (offset) and $\beta$ (column batching): + +``` +W = γ + (table_1 + table_2·β + table_3·β² + table_4·β³) +R = γ + (derived_1 + derived_2·β + derived_3·β² + table_index·β³) +``` + +where `derived_i = w_i + step_size_i · w_i_shift` (see Accumulator Trick below). + +## Why β and γ Must Be Independent + +With independent challenges, each term $\gamma + P(\beta)$ where $P(\beta) = t_1 + t_2\beta + t_3\beta^2 + t_4\beta^3$ is **irreducible** in $\mathbb{F}[\gamma, \beta]$ (it's linear in $\gamma$). For the sum of fractions to equal zero, the numerators in the partial fraction decomposition must match—and since distinct table rows give coprime denominators, the read/write multiplicities must be equal with high probability. + +See [Haböck Section 3.4](https://eprint.iacr.org/2022/1530.pdf) for the full analysis. + +## Inverse Polynomial + +To avoid rational functions in the constraint system, we introduce witness polynomial $I$: + +$$I_i = \frac{1}{R_i \cdot W_i}$$ + +For efficiency, $I$ is only computed at rows where the lookup is active: +- A lookup gate exists ($q = 1$), OR +- Table data has been read (read_tag $= 1$) + +This makes computation proportional to the number of lookups, not circuit size. + +The indicator is: `inverse_exists` $= 1 - (1 - $ read_tag $)(1 - q)$ + +## Subrelations + +See [`logderiv_lookup_relation.hpp`](./logderiv_lookup_relation.hpp) for the implementation. + +| # | Relation | Purpose | +|---|----------|---------| +| 0 | $I \cdot R \cdot W - $ `inverse_exists` $= 0$ | Inverse correctness | +| 1 | $\sum[q \cdot I \cdot W - c \cdot I \cdot R] = 0$ | Lookup identity | +| 2 | read_tag$^2 - $ read_tag $= 0$ | Boolean check | + +**Subrelation 1 is "linearly dependent"**: it constrains a sum across the entire trace rather than holding independently at each row, so it's not multiplied by a scaling factor during accumulation. + +**Why the boolean check?** If read_tag isn't boolean, inverse_exists becomes linear in read_tag, allowing a malicious prover to set it to 0 even when $q = 1$, bypassing the inverse correctness check. + +**Security note:** Tampering with read_tags or read_counts only hurts the prover—it reduces the effective table size, making it harder to satisfy the lookup. + +## Accumulator Trick + +Instead of storing raw table values in wires, we store **accumulators**. The actual table entry is recovered as: + +$$d_i = w_i + s_i \cdot w_{i+1}$$ + +where $s_i$ is the step size. This handles the case where table entries represent limbs of values too large for direct lookup. By storing accumulators, we avoid additional gates to reconstruct full-size values from limbs. + +See `bb::plookup::get_lookup_accumulators()` for implementation details. + +## Challenge Flow + +| Round | Commits | Challenges | +|-------|---------|------------| +| 1 | $w_1, w_2, w_3$ | $\eta$ | +| 2 | $w_4$, read_counts, read_tags | $\beta, \gamma$ | +| 3 | $I$, $Z$ | $\alpha$ | + +$\beta$ and $\gamma$ are generated by splitting one hash output into two 127-bit values, ensuring independence. + +## Key Files + +| File | Contents | +|------|----------| +| `logderiv_lookup_relation.hpp` | Subrelations, `compute_read/write_term`, inverse computation | +| `relation_parameters.hpp` | Challenge storage ($\beta$, $\gamma$, $\beta^2$, $\beta^3$) | +| `oink_prover.cpp` | Challenge generation, inverse polynomial computation | +| `sol/src/honk/Relations.sol` | Solidity verifier | + +## References + +- [Haböck: Multivariate lookups based on logarithmic derivatives](https://eprint.iacr.org/2022/1530.pdf) — Section 3.4 covers challenge independence +- [plookup](https://eprint.iacr.org/2020/315.pdf) diff --git a/barretenberg/cpp/src/barretenberg/honk/proof_system/logderivative_library.hpp b/barretenberg/cpp/src/barretenberg/honk/proof_system/logderivative_library.hpp index 42a5052361f9..c6c6a8aca0b9 100644 --- a/barretenberg/cpp/src/barretenberg/honk/proof_system/logderivative_library.hpp +++ b/barretenberg/cpp/src/barretenberg/honk/proof_system/logderivative_library.hpp @@ -4,6 +4,9 @@ // external_2: { status: not started, auditors: [], commit: } // ===================== +// Generic log-derivative utilities for lookups and permutations. +// For the mathematical background, see relations/LOGDERIV_LOOKUP_RELATION_README.md + #pragma once #include "barretenberg/common/constexpr_utils.hpp" diff --git a/barretenberg/cpp/src/barretenberg/hypernova/hypernova_verifier.cpp b/barretenberg/cpp/src/barretenberg/hypernova/hypernova_verifier.cpp index 2139d1c0cf47..c2893178f4e5 100644 --- a/barretenberg/cpp/src/barretenberg/hypernova/hypernova_verifier.cpp +++ b/barretenberg/cpp/src/barretenberg/hypernova/hypernova_verifier.cpp @@ -5,6 +5,7 @@ // ===================== #include "barretenberg/hypernova/hypernova_verifier.hpp" +#include "barretenberg/honk/proof_length.hpp" #include "barretenberg/hypernova/hypernova_batching_challenges.hpp" namespace bb { @@ -58,14 +59,17 @@ HypernovaFoldingVerifier::Accumulator HypernovaFoldingVerifier:: template SumcheckOutput HypernovaFoldingVerifier::sumcheck_on_incoming_instance( - const std::shared_ptr& instance, const Proof& proof) + const std::shared_ptr& instance, + const Proof& proof, + size_t num_public_inputs) { BB_BENCH_NAME("HypernovaFoldingVerifier::sumcheck_on_incoming_instance"); vinfo("HypernovaFoldingVerifier: verifying Oink proof..."); // Complete the incoming verifier instance - OinkVerifier verifier{ instance, transcript }; transcript->load_proof(proof); + + OinkVerifier verifier{ instance, transcript, num_public_inputs }; verifier.verify(); instance->gate_challenges = transcript->template get_dyadic_powers_of_challenge( @@ -89,7 +93,11 @@ std::pair::Accumulator> Hypernov { BB_BENCH_NAME("HypernovaFoldingVerifier::instance_to_accumulator"); - auto sumcheck_output = sumcheck_on_incoming_instance(instance, proof); + // Derive num_public_inputs from proof size (instance-to-accum proof structure) + const size_t num_public_inputs = + ProofLength::HypernovaInstanceToAccum::derive_num_public_inputs(proof.size(), Flavor::VIRTUAL_LOG_N); + + auto sumcheck_output = sumcheck_on_incoming_instance(instance, proof, num_public_inputs); auto accumulator = sumcheck_output_to_accumulator(sumcheck_output, instance); @@ -112,7 +120,12 @@ std::tuple::Accumulator> H vinfo("HypernovaFoldingVerifier: verifying folding proof..."); - auto sumcheck_output = sumcheck_on_incoming_instance(instance, proof); + // Derive num_public_inputs from proof size (folding proof structure includes batching) + const size_t num_public_inputs = + ProofLength::HypernovaFolding::derive_num_public_inputs( + proof.size(), Flavor::VIRTUAL_LOG_N); + + auto sumcheck_output = sumcheck_on_incoming_instance(instance, proof, num_public_inputs); // Generate challenges to batch shifted and unshifted polynomials/commitments/evaluation const auto [unshifted_challenges, shifted_challenges] = diff --git a/barretenberg/cpp/src/barretenberg/hypernova/hypernova_verifier.hpp b/barretenberg/cpp/src/barretenberg/hypernova/hypernova_verifier.hpp index 539f8daa6011..554192bdf2bc 100644 --- a/barretenberg/cpp/src/barretenberg/hypernova/hypernova_verifier.hpp +++ b/barretenberg/cpp/src/barretenberg/hypernova/hypernova_verifier.hpp @@ -74,9 +74,11 @@ template class HypernovaFoldingVerifier { * * @details Executing this sumcheck we generate the random challenges at which the polynomial commitments have to be * opened. + * @param num_public_inputs Number of public inputs (derived from proof size by caller) */ SumcheckOutput sumcheck_on_incoming_instance(const std::shared_ptr& instance, - const Proof& proof); + const Proof& proof, + size_t num_public_inputs); /** * @brief Convert the output of the sumcheck run on the incoming instance into an accumulator. diff --git a/barretenberg/cpp/src/barretenberg/hypernova/hypernova_verifier.test.cpp b/barretenberg/cpp/src/barretenberg/hypernova/hypernova_verifier.test.cpp index fc37a8fd9f56..79a42f9ea188 100644 --- a/barretenberg/cpp/src/barretenberg/hypernova/hypernova_verifier.test.cpp +++ b/barretenberg/cpp/src/barretenberg/hypernova/hypernova_verifier.test.cpp @@ -171,8 +171,8 @@ class HypernovaFoldingVerifierTests : public ::testing::Test { size_t round = 0; - // Round 0: Oink preamble + wires + ECC ops + databus -> eta challenges - manifest.add_challenge(round, std::array{ "eta", "eta_two", "eta_three" }); + // Round 0: Oink preamble + wires + ECC ops + databus -> eta challenge + manifest.add_challenge(round, "eta"); manifest.add_entry(round, "vk_hash", 1); for (size_t i = 0; i < 4; ++i) { manifest.add_entry(round, "public_input_" + std::to_string(i), 1); diff --git a/barretenberg/cpp/src/barretenberg/relations/logderiv_lookup_relation.hpp b/barretenberg/cpp/src/barretenberg/relations/logderiv_lookup_relation.hpp index cbf50e4b9343..8493223fc061 100644 --- a/barretenberg/cpp/src/barretenberg/relations/logderiv_lookup_relation.hpp +++ b/barretenberg/cpp/src/barretenberg/relations/logderiv_lookup_relation.hpp @@ -17,49 +17,10 @@ namespace bb { /** - * @brief Log-derivative lookup argument relation for establishing lookup reads from tables with 3 or fewer columns - * @details - * lookup argument seeks to prove lookups from a column by establishing the following sum: - * - * \sum_{i=0}^{n-1} q_{logderiv_lookup}_i * (1 / write_term_i) + read_count_i * (1 / read_term_i) = 0 - * - * where write_term = table_col_1 + \gamma + table_col_2 * \eta_1 + table_col_3 * \eta_2 + table_index * \eta_3 - * and read_term = derived_table_entry_1 + \gamma + derived_table_entry_2 * \eta_1 + derived_table_entry_3 * \eta_2 - * + table_index * \eta_3, with derived_table_entry_i = w_i - col_step_size_i\cdot w_i_shift (read note for - explanation). - * This expression is motivated by taking the derivative of the log of a more conventional grand product style set - * equivalence argument (see e.g. https://eprint.iacr.org/2022/1530.pdf for details). - * - * In practice, we must rephrase this expression in terms of polynomials, one of which is a polynomial I containing - * (indirectly) the rational functions in the above expression: I_i = 1/[(read_term_i) * (write_term_i)]. This leads to - * two subrelations. The first demonstrates that the inverse polynomial I is correctly formed. The second is the primary - * lookup identity, where the rational functions are replaced by the use of the inverse polynomial I. These two - * subrelations can be expressed as follows: - * - * (1) I_i * (read_term_i) * (write_term_i) - 1 = 0 - * - * (2) \sum_{i=0}^{n-1} [q_{logderiv_lookup} * I_i * write_term_i + read_count_i * I_i * read_term_i] = 0 - * - * To not compute the inverse terms packed in I_i for indices not included in the sum we introduce a - * witness called inverse_exists, which is zero when either read_count_i is nonzero (a boolean called read_tag) or we - * have a read gate. This is represented by setting inverse_exists = 1- (1- read_tag)*(1- is_read_gate). Since - * is_read_gate is only dependent on selector values, we can assume that the verifier can check that it is boolean. - * However, if read_tag (which is a derived witness), is not constrained to be boolean, one can set the inverse_exists - * to 0, even when is_read_gate is 1, because inverse_exists is a linear function of read_tag then. Thus we have a third - * subrelation, that ensures that read_tag is a boolean value. - * (3) read_tag * read_tag - read_tag = 0 - * Further constraining of read_tags and read_counts is not required, since by tampering read_tags a malicious prover - can only skip a write_term. This is disadvantagous for the cheating prover as it reduces the size of the lookup table. - Hence, a malicious prover can not abuse this to prove an incorrect lookup. - * Note: Subrelation (2) is "linearly dependent" in the sense that it establishes that a sum - * across all rows of the exectution trace is zero, rather than that some expression holds independently at each row. - * Accordingly, this subrelation is not multiplied by a scaling factor at each accumulation step. - * @note The "real" table entries must be 'derived' from wire values since instead of storing actual values in wires we - * store successive accumulators, the differences of which are equal to entries in a table. This is an efficiency - * trick for the case where entires of the "real" table correspond to limbs of a value too large to be supported by the - * lookup table. This way we avoid using additional gates to reconstruct full size values from the limbs contained in - * tables. See the documentation in method bb::plookup::get_lookup_accumulators()). + * @brief Log-derivative lookup relation (LogUp) for tables with up to 3 columns. + * @details See LOGDERIV_LOOKUP_RELATION_README.md for full documentation. * + * IMPORTANT: γ and β must be independent challenges for soundness. */ template class LogDerivLookupRelationImpl { @@ -134,7 +95,7 @@ template class LogDerivLookupRelationImpl { return Accumulator(-(row_has_write * row_has_read) + row_has_write + row_has_read); } - // Compute table_1 + gamma + table_2 * eta + table_3 * eta_2 + table_4 * eta_3 + // Compute table_1 + gamma + table_2 * β + table_3 * β² + table_4 * β³ // table_1,2,3 correspond to the (maximum) three columns of the lookup table and table_4 is the unique identifier // of the lookup table table_index template @@ -144,9 +105,9 @@ template class LogDerivLookupRelationImpl { using CoefficientAccumulator = typename Accumulator::CoefficientAccumulator; const auto gamma = ParameterCoefficientAccumulator(params.gamma); - const auto eta = ParameterCoefficientAccumulator(params.eta); - const auto eta_two = ParameterCoefficientAccumulator(params.eta_two); - const auto eta_three = ParameterCoefficientAccumulator(params.eta_three); + const auto beta = ParameterCoefficientAccumulator(params.beta); + const auto beta_sqr = ParameterCoefficientAccumulator(params.beta_sqr); + const auto beta_cube = ParameterCoefficientAccumulator(params.beta_cube); auto table_1 = CoefficientAccumulator(in.table_1); auto table_2 = CoefficientAccumulator(in.table_2); @@ -154,7 +115,7 @@ template class LogDerivLookupRelationImpl { auto table_4 = CoefficientAccumulator(in.table_4); // degree 1 0 1 0 1 0 = 1 - auto result = (table_2 * eta) + (table_3 * eta_two) + (table_4 * eta_three); + auto result = (table_2 * beta) + (table_3 * beta_sqr) + (table_4 * beta_cube); result += table_1; result += gamma; return Accumulator(result); @@ -167,9 +128,9 @@ template class LogDerivLookupRelationImpl { using CoefficientAccumulator = typename Accumulator::CoefficientAccumulator; const auto gamma = ParameterCoefficientAccumulator(params.gamma); - const auto eta = ParameterCoefficientAccumulator(params.eta); - const auto eta_two = ParameterCoefficientAccumulator(params.eta_two); - const auto eta_three = ParameterCoefficientAccumulator(params.eta_three); + const auto beta = ParameterCoefficientAccumulator(params.beta); + const auto beta_sqr = ParameterCoefficientAccumulator(params.beta_sqr); + const auto beta_cube = ParameterCoefficientAccumulator(params.beta_cube); auto w_1 = CoefficientAccumulator(in.w_l); auto w_2 = CoefficientAccumulator(in.w_r); @@ -194,12 +155,12 @@ template class LogDerivLookupRelationImpl { // degree 1 1 1 = 2 auto derived_table_entry_3 = (negative_column_3_step_size * w_3_shift) + w_3; // 1 0 = 1 - auto table_index_entry = table_index * eta_three; + auto table_index_entry = table_index * beta_cube; - // (w_1 + \gamma q_2*w_1_shift) + η(w_2 + q_m*w_2_shift) + η₂(w_3 + q_c*w_3_shift) + η₃q_index. + // (w_1 + γ + q_2*w_1_shift) + β(w_2 + q_m*w_2_shift) + β²(w_3 + q_c*w_3_shift) + β³*q_index. // deg 2 or 3 // degree 2 0 2 0 = 2 - auto result = Accumulator(derived_table_entry_2) * eta + Accumulator(derived_table_entry_3) * eta_two; + auto result = Accumulator(derived_table_entry_2) * beta + Accumulator(derived_table_entry_3) * beta_sqr; result += Accumulator(derived_table_entry_1 + table_index_entry); return result; } diff --git a/barretenberg/cpp/src/barretenberg/relations/relation_parameters.hpp b/barretenberg/cpp/src/barretenberg/relations/relation_parameters.hpp index c70265cffb60..52aa6857c21e 100644 --- a/barretenberg/cpp/src/barretenberg/relations/relation_parameters.hpp +++ b/barretenberg/cpp/src/barretenberg/relations/relation_parameters.hpp @@ -21,16 +21,31 @@ template struct RelationParameters { static constexpr int NUM_BINARY_LIMBS_IN_GOBLIN_TRANSLATOR = 4; static constexpr int NUM_NATIVE_LIMBS_IN_GOBLIN_TRANSLATOR = 1; static constexpr int NUM_CHALLENGE_POWERS_IN_GOBLIN_TRANSLATOR = 4; - static constexpr int NUM_TO_FOLD = 6; - T eta{ 0 }; // Lookup + Aux Memory - T eta_two{ 0 }; // Lookup + Aux Memory - T eta_three{ 0 }; // Lookup + Aux Memory - T beta{ 0 }; // Permutation + Lookup - T gamma{ 0 }; // Permutation + Lookup + T eta{ 0 }; // Aux Memory (eta) + T eta_two{ 0 }; // Aux Memory (eta²) + T eta_three{ 0 }; // Aux Memory (eta³) + T beta{ 0 }; // Permutation + Lookup (column batching) + T gamma{ 0 }; // Permutation + Lookup + T public_input_delta{ 0 }; // Permutation T beta_sqr{ 0 }; T beta_cube{ 0 }; + + // Compute eta powers from a single eta challenge + void compute_eta_powers(const T& eta_challenge) + { + eta = eta_challenge; + eta_two = eta * eta; + eta_three = eta_two * eta; + } + + void compute_beta_powers(const T& beta_challenge) + { + beta = beta_challenge; + beta_sqr = beta * beta; + beta_cube = beta_sqr * beta; + } // `eccvm_set_permutation_delta` is used in the set membership gadget in eccvm/ecc_set_relation.hpp, specifically to // constrain (pc, round, wnaf_slice) to match between the MSM table and the Precomputed table. The number of rows we // add per short scalar `mul` is slightly less in the Precomputed table as in the MSM table, so to get the @@ -50,25 +65,11 @@ template struct RelationParameters { { T(0), T(0), T(0), T(0), T(0) }, { T(0), T(0), T(0), T(0), T(0) } } }; - RefArray get_to_fold() - { - return RefArray{ eta, eta_two, eta_three, beta, gamma, public_input_delta }; - } - - RefArray get_to_fold() const - { - return RefArray{ eta, eta_two, eta_three, beta, gamma, public_input_delta }; - } - static RelationParameters get_random() { RelationParameters result; - result.eta = T::random_element(); - result.eta_two = T::random_element(); - result.eta_three = T::random_element(); - result.beta = T::random_element(); - result.beta_sqr = result.beta * result.beta; - result.beta_cube = result.beta_sqr * result.beta; + result.compute_eta_powers(T::random_element()); // eta, eta_two = eta², eta_three = eta³ + result.compute_beta_powers(T::random_element()); // beta, beta_sqr = beta², beta_cube = beta³ result.gamma = T::random_element(); result.public_input_delta = T::random_element(); result.eccvm_set_permutation_delta = result.gamma * (result.gamma + result.beta_sqr) * diff --git a/barretenberg/cpp/src/barretenberg/stdlib/test_utils/tamper_proof.hpp b/barretenberg/cpp/src/barretenberg/stdlib/test_utils/tamper_proof.hpp index dabaed23ec4c..ba3e61e6908d 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/test_utils/tamper_proof.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/test_utils/tamper_proof.hpp @@ -3,6 +3,7 @@ #include "barretenberg/commitment_schemes/ipa/ipa.hpp" #include "barretenberg/flavor/flavor_concepts.hpp" #include "barretenberg/flavor/test_utils/proof_structures.hpp" +#include "barretenberg/honk/proof_length.hpp" namespace bb { @@ -16,15 +17,13 @@ enum class TamperType { /** * @brief Compute the proof length for re-exporting after tampering - * @details Excludes IPA proof length for flavors with IPA accumulator since it's added again by export_proof + * @details ProofLength::Honk excludes IPA (handled separately by prover/verifier for rollup flavors) + * @param num_public_inputs Number of public inputs in the proof + * @param log_n Log of circuit size (use VIRTUAL_LOG_N for padded flavors, actual log_dyadic_size for non-padded) */ -template size_t compute_proof_length_for_export(size_t num_public_inputs) +template size_t compute_proof_length_for_export(size_t num_public_inputs, size_t log_n) { - size_t num_frs = Flavor::PROOF_LENGTH_WITHOUT_PUB_INPUTS() + num_public_inputs; - if constexpr (HasIPAAccumulator) { - num_frs -= IPA_PROOF_LENGTH; - } - return num_frs; + return ProofLength::Honk::LENGTH_WITHOUT_PUB_INPUTS(log_n) + num_public_inputs; } /** @@ -71,7 +70,7 @@ void tamper_with_proof(InnerProver& inner_prover, ProofType& inner_proof, Tamper // Serialize back and re-export the tampered proof structured_proof.serialize(inner_prover.transcript->test_get_proof_data(), log_n); inner_prover.transcript->test_set_proof_parsing_state( - 0, compute_proof_length_for_export(num_public_inputs)); + 0, compute_proof_length_for_export(num_public_inputs, log_n)); inner_proof = inner_prover.export_proof(); } diff --git a/barretenberg/cpp/src/barretenberg/transcript/README.md b/barretenberg/cpp/src/barretenberg/transcript/README.md index 583a3968db8d..85b7f47b96d5 100644 --- a/barretenberg/cpp/src/barretenberg/transcript/README.md +++ b/barretenberg/cpp/src/barretenberg/transcript/README.md @@ -545,9 +545,9 @@ ROUND 0 - PREAMBLE (challenge_generation_phase=false, round_index=0) │ Phase: proof processing → challenge generation ▼ ┌──────────────────────────────────┐ -│ get_challenges("eta", "eta_two", │──► 1. Sanitize: FREE_WITNESS → CONSTANT (allow hashing) -│ "eta_three") │ 2. Hash: c₀ = Poseidon2(current_round_data) -└──────────────────────────────────┘ 3. Split to [127-bit, 127-bit] x3 +│ get_challenge("eta") │──► 1. Sanitize: FREE_WITNESS → CONSTANT (allow hashing) +└──────────────────────────────────┘ 2. Hash: c₀ = Poseidon2(current_round_data) + │ 3. Split to [127-bit, 127-bit] │ 4. Clear current_round_data │ 5. Set challenge_generation_phase = true ▼ 6. Assign tag: OriginTag(42, 0, is_submitted=false) diff --git a/barretenberg/cpp/src/barretenberg/translator_vm/translator.test.cpp b/barretenberg/cpp/src/barretenberg/translator_vm/translator.test.cpp index 8a0ad33e86b4..b054a68ae94c 100644 --- a/barretenberg/cpp/src/barretenberg/translator_vm/translator.test.cpp +++ b/barretenberg/cpp/src/barretenberg/translator_vm/translator.test.cpp @@ -298,7 +298,7 @@ TEST_F(TranslatorTests, ProofLengthCheck) // Generate proof auto proof = prover.construct_proof(); - EXPECT_EQ(proof.size(), TranslatorFlavor::PROOF_LENGTH_WITHOUT_PUB_INPUTS); + EXPECT_EQ(proof.size(), TranslatorFlavor::PROOF_LENGTH); } /** diff --git a/barretenberg/cpp/src/barretenberg/translator_vm/translator_flavor.hpp b/barretenberg/cpp/src/barretenberg/translator_vm/translator_flavor.hpp index e268c163cbe2..f05232e24036 100644 --- a/barretenberg/cpp/src/barretenberg/translator_vm/translator_flavor.hpp +++ b/barretenberg/cpp/src/barretenberg/translator_vm/translator_flavor.hpp @@ -202,7 +202,7 @@ class TranslatorFlavor { static constexpr size_t num_frs_fq = FrCodec::calc_num_fields(); // Proof length formula - static constexpr size_t PROOF_LENGTH_WITHOUT_PUB_INPUTS = + static constexpr size_t PROOF_LENGTH = /* 1. NUM_WITNESS_ENTITIES commitments (minus gemini_masking_poly sent separately, z_perm sent separately, and 4 op queue wires passed by merge protocol) */ ((NUM_WITNESS_ENTITIES - 3 - TranslatorFlavor::NUM_OP_QUEUE_WIRES) * num_frs_comm) + diff --git a/barretenberg/cpp/src/barretenberg/ultra_honk/mega_honk.test.cpp b/barretenberg/cpp/src/barretenberg/ultra_honk/mega_honk.test.cpp index 32ef87773d20..eef3dd2fe05b 100644 --- a/barretenberg/cpp/src/barretenberg/ultra_honk/mega_honk.test.cpp +++ b/barretenberg/cpp/src/barretenberg/ultra_honk/mega_honk.test.cpp @@ -5,6 +5,7 @@ #include "barretenberg/circuit_checker/circuit_checker.hpp" #include "barretenberg/common/log.hpp" #include "barretenberg/goblin/mock_circuits.hpp" +#include "barretenberg/honk/proof_length.hpp" #include "barretenberg/honk/relation_checker.hpp" #include "barretenberg/stdlib_circuit_builders/mega_circuit_builder.hpp" #include "barretenberg/stdlib_circuit_builders/ultra_circuit_builder.hpp" @@ -74,7 +75,9 @@ TYPED_TEST(MegaHonkTests, ProofLengthCheck) auto verification_key = std::make_shared(prover_instance->get_precomputed()); UltraProver_ prover(prover_instance, verification_key); HonkProof mega_proof = prover.construct_proof(); - EXPECT_EQ(mega_proof.size(), Flavor::PROOF_LENGTH_WITHOUT_PUB_INPUTS() + DefaultIO::PUBLIC_INPUTS_SIZE); + EXPECT_EQ(mega_proof.size(), + ProofLength::Honk::LENGTH_WITHOUT_PUB_INPUTS(Flavor::VIRTUAL_LOG_N) + + DefaultIO::PUBLIC_INPUTS_SIZE); } /** diff --git a/barretenberg/cpp/src/barretenberg/ultra_honk/mega_transcript.test.cpp b/barretenberg/cpp/src/barretenberg/ultra_honk/mega_transcript.test.cpp index 80d0f0690f29..233ce31f8aab 100644 --- a/barretenberg/cpp/src/barretenberg/ultra_honk/mega_transcript.test.cpp +++ b/barretenberg/cpp/src/barretenberg/ultra_honk/mega_transcript.test.cpp @@ -24,11 +24,11 @@ template class MegaTranscriptTests : public ::testing::Test { using Proof = typename Flavor::Transcript::Proof; using FF = Flavor::FF; - static Proof export_serialized_proof(Prover& prover, const size_t num_public_inputs) + static Proof export_serialized_proof(Prover& prover, const size_t num_public_inputs, const size_t log_n) { // reset internal variables needed for exporting the proof // Note: compute_proof_length_for_export excludes IPA proof length since export_proof appends it separately - size_t proof_length = compute_proof_length_for_export(num_public_inputs); + size_t proof_length = compute_proof_length_for_export(num_public_inputs, log_n); prover.transcript->test_set_proof_parsing_state(0, proof_length); return prover.export_proof(); } @@ -84,7 +84,7 @@ template class MegaTranscriptTests : public ::testing::Test { manifest_expected.add_entry(round, "RETURN_DATA", frs_per_G); manifest_expected.add_entry(round, "RETURN_DATA_READ_COUNTS", frs_per_G); manifest_expected.add_entry(round, "RETURN_DATA_READ_TAGS", frs_per_G); - manifest_expected.add_challenge(round, std::array{ "eta", "eta_two", "eta_three" }); + manifest_expected.add_challenge(round, "eta"); round++; manifest_expected.add_entry(round, "LOOKUP_READ_COUNTS", frs_per_G); @@ -330,7 +330,7 @@ TYPED_TEST(MegaTranscriptTests, StructureTest) prover.transcript->test_get_proof_data(), verification_key->num_public_inputs, virtual_log_n); proof_structure.serialize(prover.transcript->test_get_proof_data(), virtual_log_n); - proof = TestFixture::export_serialized_proof(prover, prover_instance->num_public_inputs()); + proof = TestFixture::export_serialized_proof(prover, prover_instance->num_public_inputs(), virtual_log_n); // we have changed nothing so proof is still valid Verifier verifier2(vk_and_hash); EXPECT_TRUE(verifier2.verify_proof(proof).result); @@ -338,13 +338,13 @@ TYPED_TEST(MegaTranscriptTests, StructureTest) Commitment one_group_val = Commitment::one(); FF rand_val = FF::random_element(); proof_structure.z_perm_comm = one_group_val * rand_val; // choose random object to modify - proof = TestFixture::export_serialized_proof(prover, prover_instance->num_public_inputs()); + proof = TestFixture::export_serialized_proof(prover, prover_instance->num_public_inputs(), virtual_log_n); // we have not serialized it back to the proof so it should still be fine Verifier verifier3(vk_and_hash); EXPECT_TRUE(verifier3.verify_proof(proof).result); proof_structure.serialize(prover.transcript->test_get_proof_data(), virtual_log_n); - proof = TestFixture::export_serialized_proof(prover, prover_instance->num_public_inputs()); + proof = TestFixture::export_serialized_proof(prover, prover_instance->num_public_inputs(), virtual_log_n); // the proof is now wrong after serializing it Verifier verifier4(vk_and_hash); EXPECT_FALSE(verifier4.verify_proof(proof).result); diff --git a/barretenberg/cpp/src/barretenberg/ultra_honk/oink_prover.cpp b/barretenberg/cpp/src/barretenberg/ultra_honk/oink_prover.cpp index 932c257a7106..7a045cbde844 100644 --- a/barretenberg/cpp/src/barretenberg/ultra_honk/oink_prover.cpp +++ b/barretenberg/cpp/src/barretenberg/ultra_honk/oink_prover.cpp @@ -143,19 +143,15 @@ template void OinkProver::execute_wire_commit template void OinkProver::execute_sorted_list_accumulator_round() { BB_BENCH_NAME("OinkProver::execute_sorted_list_accumulator_round"); - // Get eta challenges - auto [eta, eta_two, eta_three] = transcript->template get_challenges(std::array{ - domain_separator + "eta", domain_separator + "eta_two", domain_separator + "eta_three" }); - prover_instance->relation_parameters.eta = eta; - prover_instance->relation_parameters.eta_two = eta_two; - prover_instance->relation_parameters.eta_three = eta_three; + // Get eta challenge and compute powers (eta, eta², eta³) + prover_instance->relation_parameters.compute_eta_powers(transcript->template get_challenge("eta")); WitnessComputation::add_ram_rom_memory_records_to_wire_4(prover_instance->polynomials, prover_instance->memory_read_records, prover_instance->memory_write_records, - eta, - eta_two, - eta_three); + prover_instance->relation_parameters.eta, + prover_instance->relation_parameters.eta_two, + prover_instance->relation_parameters.eta_three); // Commit to lookup argument polynomials and the finalized (i.e. with memory records) fourth wire polynomial auto batch = prover_instance->commitment_key.start_batch(); @@ -182,7 +178,7 @@ template void OinkProver::execute_log_derivat BB_BENCH_NAME("OinkProver::execute_log_derivative_inverse_round"); auto [beta, gamma] = transcript->template get_challenges( std::array{ domain_separator + "beta", domain_separator + "gamma" }); - prover_instance->relation_parameters.beta = beta; + prover_instance->relation_parameters.compute_beta_powers(beta); prover_instance->relation_parameters.gamma = gamma; // Compute the inverses used in log-derivative lookup relations diff --git a/barretenberg/cpp/src/barretenberg/ultra_honk/oink_prover.test.cpp b/barretenberg/cpp/src/barretenberg/ultra_honk/oink_prover.test.cpp index 96a75c080800..145dfe0fbb1d 100644 --- a/barretenberg/cpp/src/barretenberg/ultra_honk/oink_prover.test.cpp +++ b/barretenberg/cpp/src/barretenberg/ultra_honk/oink_prover.test.cpp @@ -41,7 +41,6 @@ TEST_F(OinkTests, OinkProverIsDeterministic) // Store data generated by Oink prover auto alpha = prover_instance->alpha; - auto relation_parameters = prover_instance->relation_parameters; auto gate_challenges = prover_instance->gate_challenges; auto w4 = prover_instance->polynomials.w_4; auto lookup_read_counts = prover_instance->polynomials.lookup_read_counts; @@ -58,10 +57,7 @@ TEST_F(OinkTests, OinkProverIsDeterministic) // Check that the data hasn't changed BB_ASSERT_EQ(prover_instance->alpha, alpha); - for (auto [new_parameter, old_parameter] : - zip_view(prover_instance->relation_parameters.get_to_fold(), relation_parameters.get_to_fold())) { - BB_ASSERT_EQ(new_parameter, old_parameter); - }; + for (auto [new_challenge, old_challenge] : zip_view(prover_instance->gate_challenges, gate_challenges)) { BB_ASSERT_EQ(new_challenge, old_challenge); }; @@ -92,8 +88,8 @@ TEST_F(OinkTests, OinkProverCommitments) Flavor::VerifierCommitments prover_commitments(verification_key, prover_instance->commitments); auto transcript = std::make_shared(); - OinkVerifier verifier(verifier_instance, transcript); transcript->load_proof(proof); + OinkVerifier verifier(verifier_instance, transcript, verification_key->num_public_inputs); verifier.verify(); Flavor::VerifierCommitments verifier_commitments(verifier_instance->get_vk(), diff --git a/barretenberg/cpp/src/barretenberg/ultra_honk/oink_verifier.cpp b/barretenberg/cpp/src/barretenberg/ultra_honk/oink_verifier.cpp index 1049dcee6148..dd1e26d648df 100644 --- a/barretenberg/cpp/src/barretenberg/ultra_honk/oink_verifier.cpp +++ b/barretenberg/cpp/src/barretenberg/ultra_honk/oink_verifier.cpp @@ -65,12 +65,17 @@ template void OinkVerifier::execute_preamble_round() info("Recursive Ultra Verifier: VK Hash Mismatch"); } verifier_instance->vk_and_hash->hash.assert_equal(vk_hash); + + // Assert that the provided num_public_inputs matches VK's value (in-circuit constraint) + vk->num_public_inputs.assert_equal(FF(num_public_inputs), "OinkVerifier: num_public_inputs mismatch with VK"); } else { BB_ASSERT_EQ(verifier_instance->vk_and_hash->hash, vk_hash, "Native Ultra Verifier: VK Hash Mismatch"); + // Assert that the provided num_public_inputs matches VK's value + BB_ASSERT_EQ(num_public_inputs, + static_cast(vk->num_public_inputs), + "OinkVerifier: num_public_inputs mismatch with VK"); }; - size_t num_public_inputs = get_num_public_inputs(); - std::vector public_inputs; for (size_t i = 0; i < num_public_inputs; ++i) { auto public_input_i = @@ -113,12 +118,8 @@ template void OinkVerifier::execute_wire_commitments_r */ template void OinkVerifier::execute_sorted_list_accumulator_round() { - // Get eta challenges - auto [eta, eta_two, eta_three] = transcript->template get_challenges(std::array{ - domain_separator + "eta", domain_separator + "eta_two", domain_separator + "eta_three" }); - relation_parameters.eta = eta; - relation_parameters.eta_two = eta_two; - relation_parameters.eta_three = eta_three; + // Get eta challenge and compute powers (eta, eta², eta³) + relation_parameters.compute_eta_powers(transcript->template get_challenge("eta")); // Get commitments to lookup argument polynomials and fourth wire witness_comms.lookup_read_counts = @@ -134,10 +135,9 @@ template void OinkVerifier::execute_sorted_list_accumu */ template void OinkVerifier::execute_log_derivative_inverse_round() { - // Get permutation challenges auto [beta, gamma] = transcript->template get_challenges( std::array{ domain_separator + "beta", domain_separator + "gamma" }); - relation_parameters.beta = beta; + relation_parameters.compute_beta_powers(beta); relation_parameters.gamma = gamma; witness_comms.lookup_inverses = diff --git a/barretenberg/cpp/src/barretenberg/ultra_honk/oink_verifier.hpp b/barretenberg/cpp/src/barretenberg/ultra_honk/oink_verifier.hpp index 3a1b260eb6dd..249b8dd7076b 100644 --- a/barretenberg/cpp/src/barretenberg/ultra_honk/oink_verifier.hpp +++ b/barretenberg/cpp/src/barretenberg/ultra_honk/oink_verifier.hpp @@ -38,12 +38,18 @@ template class OinkVerifier { bb::RelationParameters relation_parameters; WitnessCommitments witness_comms; + // Number of public inputs - provided by caller, not derived from VK. + // This avoids .get_value() in recursive contexts and makes the dependency explicit. + size_t num_public_inputs; + OinkVerifier(const std::shared_ptr& verifier_instance, const std::shared_ptr& transcript, + size_t num_public_inputs, std::string domain_separator = "") : transcript(transcript) , verifier_instance(verifier_instance) , domain_separator(std::move(domain_separator)) + , num_public_inputs(num_public_inputs) {} void verify(); @@ -59,20 +65,5 @@ template class OinkVerifier { void execute_grand_product_computation_round(); SubrelationSeparator generate_alpha_round(); - - private: - /** - * @brief Helper to get number of public inputs, abstracting differences between native and recursive flavors - * @return Number of public inputs as size_t - */ - size_t get_num_public_inputs() const - { - auto vk = verifier_instance->get_vk(); - if constexpr (IsRecursiveFlavor) { - return static_cast(static_cast(vk->num_public_inputs.get_value())); - } else { - return static_cast(vk->num_public_inputs); - } - } }; } // namespace bb diff --git a/barretenberg/cpp/src/barretenberg/ultra_honk/ultra_honk.test.cpp b/barretenberg/cpp/src/barretenberg/ultra_honk/ultra_honk.test.cpp index 56379cc8aa24..c1862157ed98 100644 --- a/barretenberg/cpp/src/barretenberg/ultra_honk/ultra_honk.test.cpp +++ b/barretenberg/cpp/src/barretenberg/ultra_honk/ultra_honk.test.cpp @@ -1,4 +1,5 @@ #include "ultra_honk.test.hpp" +#include "barretenberg/honk/proof_length.hpp" #include "barretenberg/honk/relation_checker.hpp" #include @@ -46,7 +47,12 @@ TYPED_TEST(UltraHonkTests, ProofLengthCheck) UltraProver_ prover(prover_instance, verification_key); Proof ultra_proof = prover.construct_proof(); const size_t virtual_log_n = Flavor::USE_PADDING ? CONST_PROOF_SIZE_LOG_N : prover_instance->log_dyadic_size(); - size_t expected_proof_length = Flavor::PROOF_LENGTH_WITHOUT_PUB_INPUTS(virtual_log_n) + IO::PUBLIC_INPUTS_SIZE; + size_t expected_proof_length = + ProofLength::Honk::LENGTH_WITHOUT_PUB_INPUTS(virtual_log_n) + IO::PUBLIC_INPUTS_SIZE; + // UltraRollupFlavor has HasIPAAccumulator=true, so proof includes IPA_PROOF_LENGTH + if constexpr (HasIPAAccumulator) { + expected_proof_length += IPA_PROOF_LENGTH; + } EXPECT_EQ(ultra_proof.size(), expected_proof_length); } diff --git a/barretenberg/cpp/src/barretenberg/ultra_honk/ultra_transcript.test.cpp b/barretenberg/cpp/src/barretenberg/ultra_honk/ultra_transcript.test.cpp index f4eb22c3177b..8e7679666f99 100644 --- a/barretenberg/cpp/src/barretenberg/ultra_honk/ultra_transcript.test.cpp +++ b/barretenberg/cpp/src/barretenberg/ultra_honk/ultra_transcript.test.cpp @@ -97,7 +97,7 @@ template class UltraTranscriptTests : public ::testing::Test { manifest_expected.add_entry(round, "W_L", data_types_per_G); manifest_expected.add_entry(round, "W_R", data_types_per_G); manifest_expected.add_entry(round, "W_O", data_types_per_G); - manifest_expected.add_challenge(round, std::array{ "eta", "eta_two", "eta_three" }); + manifest_expected.add_challenge(round, "eta"); round++; manifest_expected.add_entry(round, "LOOKUP_READ_COUNTS", data_types_per_G); @@ -197,11 +197,11 @@ template class UltraTranscriptTests : public ::testing::Test { } } - Proof export_serialized_proof(Prover& prover, const size_t num_public_inputs) + Proof export_serialized_proof(Prover& prover, const size_t num_public_inputs, const size_t log_n) { // reset internal variables needed for exporting the proof // Note: compute_proof_length_for_export excludes IPA proof length since export_proof appends it separately - size_t proof_length = compute_proof_length_for_export(num_public_inputs); + size_t proof_length = compute_proof_length_for_export(num_public_inputs, log_n); prover.transcript->test_set_proof_parsing_state(0, proof_length); return prover.export_proof(); } @@ -338,7 +338,7 @@ TYPED_TEST(UltraTranscriptTests, StructureTest) prover.transcript->test_get_proof_data(), verification_key->num_public_inputs, virtual_log_n); proof_structure.serialize(prover.transcript->test_get_proof_data(), virtual_log_n); - proof = TestFixture::export_serialized_proof(prover, prover_instance->num_public_inputs()); + proof = TestFixture::export_serialized_proof(prover, prover_instance->num_public_inputs(), virtual_log_n); // we have changed nothing so proof is still valid typename TestFixture::Verifier verifier2(vk_and_hash); EXPECT_TRUE(verifier2.verify_proof(proof).result); @@ -346,13 +346,13 @@ TYPED_TEST(UltraTranscriptTests, StructureTest) Commitment one_group_val = Commitment::one(); FF rand_val = FF::random_element(); proof_structure.z_perm_comm = one_group_val * rand_val; // choose random object to modify - proof = TestFixture::export_serialized_proof(prover, prover_instance->num_public_inputs()); + proof = TestFixture::export_serialized_proof(prover, prover_instance->num_public_inputs(), virtual_log_n); // we have not serialized it back to the proof so it should still be fine typename TestFixture::Verifier verifier3(vk_and_hash); EXPECT_TRUE(verifier3.verify_proof(proof).result); proof_structure.serialize(prover.transcript->test_get_proof_data(), virtual_log_n); - proof = TestFixture::export_serialized_proof(prover, prover_instance->num_public_inputs()); + proof = TestFixture::export_serialized_proof(prover, prover_instance->num_public_inputs(), virtual_log_n); // the proof is now wrong after serializing it typename TestFixture::Verifier verifier4(vk_and_hash); EXPECT_FALSE(verifier4.verify_proof(proof).result); diff --git a/barretenberg/cpp/src/barretenberg/ultra_honk/ultra_verifier.cpp b/barretenberg/cpp/src/barretenberg/ultra_honk/ultra_verifier.cpp index 84ca05546c99..80fad6bab2d5 100644 --- a/barretenberg/cpp/src/barretenberg/ultra_honk/ultra_verifier.cpp +++ b/barretenberg/cpp/src/barretenberg/ultra_honk/ultra_verifier.cpp @@ -12,45 +12,47 @@ #include "barretenberg/flavor/mega_zk_recursive_flavor.hpp" #include "barretenberg/flavor/ultra_rollup_recursive_flavor.hpp" #include "barretenberg/flavor/ultra_zk_recursive_flavor.hpp" -#include "barretenberg/numeric/bitop/get_msb.hpp" -#include "barretenberg/special_public_inputs/special_public_inputs.hpp" +#include "barretenberg/honk/proof_length.hpp" #include "barretenberg/stdlib/primitives/padding_indicator_array/padding_indicator_array.hpp" -#include "barretenberg/stdlib/special_public_inputs/special_public_inputs.hpp" -#include "barretenberg/transcript/transcript.hpp" +#include "barretenberg/sumcheck/sumcheck.hpp" #include "barretenberg/ultra_honk/oink_verifier.hpp" namespace bb { /** - * @brief Compute log_n and padding indicator array based on flavor configuration - * @details Handles all combinations of native/recursive, ZK/non-ZK, and padding/no-padding - * @return PaddingData containing log_n and padding_indicator_array + * @brief Compute log_n based on flavor. + * @details Returns VIRTUAL_LOG_N for padded flavors, or VK's log_circuit_size otherwise. + * Called early in verification to derive num_public_inputs from proof size. */ -template -typename UltraVerifier_::PaddingData UltraVerifier_::process_padding() const +template size_t UltraVerifier_::compute_log_n() const { - using FF = typename Flavor::FF; - using Curve = typename Flavor::Curve; - - auto vk_ptr = verifier_instance->get_vk(); - - // Determine log_n based on whether padding is employed - const size_t log_n = [&]() { - if constexpr (Flavor::USE_PADDING) { - return static_cast(Flavor::VIRTUAL_LOG_N); - } else if constexpr (!IsRecursive) { - return static_cast(vk_ptr->log_circuit_size); - } - }(); + if constexpr (Flavor::USE_PADDING) { + return static_cast(Flavor::VIRTUAL_LOG_N); + } else { + // Non-padded: use actual circuit size from VK (native only) + return static_cast(verifier_instance->get_vk()->log_circuit_size); + } +} - // Construct the padding indicator array +/** + * @brief Compute padding indicator array based on flavor configuration. + * @details Must be called AFTER OinkVerifier::verify() so that VK fields are properly + * tagged through the transcript (for recursive ZK flavors). + * @param log_n The log circuit size (from compute_log_n) + * @return std::vector padding indicator array + */ +template +std::vector UltraVerifier_::compute_padding_indicator_array(size_t log_n) const +{ // - Non-ZK flavors: all 1s (no masking needed) // - ZK without padding: all 1s (log_n == log_circuit_size, no padded region) // - ZK with padding: computed to mask padded rounds (1s for real, 0s for padding) std::vector padding_indicator_array(log_n, FF{ 1 }); if constexpr (Flavor::HasZK && Flavor::USE_PADDING) { + auto vk_ptr = verifier_instance->get_vk(); if constexpr (IsRecursive) { // Recursive: use in-circuit computation via Lagrange polynomials + // Note: Must be called after OinkVerifier so log_circuit_size is properly tagged padding_indicator_array = stdlib::compute_padding_indicator_array(vk_ptr->log_circuit_size); } else { @@ -62,7 +64,7 @@ typename UltraVerifier_::PaddingData UltraVerifier_::pro } } - return PaddingData{ log_n, std::move(padding_indicator_array) }; + return padding_indicator_array; } /** @@ -74,24 +76,11 @@ std::pair::Proof, typename UltraVerifier_::split_rollup_proof(const Proof& combined_proof) const requires(HasIPAAccumulator) { - // Use VK's num_public_inputs which includes both IO and any additional circuit public inputs - auto vk_ptr = verifier_instance->get_vk(); - const size_t num_public_inputs = [&]() { - if constexpr (IsRecursive) { - return static_cast(uint32_t(vk_ptr->num_public_inputs.get_value())); - } else { - return static_cast(vk_ptr->num_public_inputs); - } - }(); - - // Calculate split point - const size_t HONK_PROOF_LENGTH = UltraRollupFlavor::PROOF_LENGTH_WITHOUT_PUB_INPUTS() - IPA_PROOF_LENGTH; - const std::ptrdiff_t honk_proof_with_pub_inputs_length = - static_cast(HONK_PROOF_LENGTH + num_public_inputs); + // IPA proof is appended at the end + const auto honk_proof_length = static_cast(combined_proof.size() - IPA_PROOF_LENGTH); - // Extract proofs - Proof honk_proof(combined_proof.begin(), combined_proof.begin() + honk_proof_with_pub_inputs_length); - Proof ipa_proof(combined_proof.begin() + honk_proof_with_pub_inputs_length, combined_proof.end()); + Proof honk_proof(combined_proof.begin(), combined_proof.begin() + honk_proof_length); + Proof ipa_proof(combined_proof.begin() + honk_proof_length, combined_proof.end()); return std::make_pair(honk_proof, ipa_proof); } @@ -124,20 +113,23 @@ template typename UltraVerifier_::ReductionResult UltraVerifier_::reduce_to_pairing_check( const typename UltraVerifier_::Proof& proof) { - using FF = typename Flavor::FF; - using PCS = typename Flavor::PCS; - using Curve = typename Flavor::Curve; using Shplemini = ShpleminiVerifier_; - using VerifierCommitments = typename Flavor::VerifierCommitments; using ClaimBatcher = ClaimBatcher_; using ClaimBatch = ClaimBatcher::Batch; transcript->load_proof(proof); - OinkVerifier oink_verifier{ verifier_instance, transcript }; + + // Compute log_n first (needed for proof layout calculation) + const size_t log_n = compute_log_n(); + + // Derive num_public_inputs from proof size using centralized proof layout + const size_t num_public_inputs = ProofLength::Honk::derive_num_public_inputs(proof.size(), log_n); + + OinkVerifier oink_verifier{ verifier_instance, transcript, num_public_inputs }; oink_verifier.verify(); - // Compute padding data (log_n and padding_indicator_array) - auto [log_n, padding_indicator_array] = process_padding(); + // Compute padding indicator array AFTER OinkVerifier so VK fields are properly tagged + auto padding_indicator_array = compute_padding_indicator_array(log_n); verifier_instance->gate_challenges = transcript->template get_dyadic_powers_of_challenge("Sumcheck:gate_challenge", log_n); diff --git a/barretenberg/cpp/src/barretenberg/ultra_honk/ultra_verifier.hpp b/barretenberg/cpp/src/barretenberg/ultra_honk/ultra_verifier.hpp index d3303bb2b078..d786d23f4c1c 100644 --- a/barretenberg/cpp/src/barretenberg/ultra_honk/ultra_verifier.hpp +++ b/barretenberg/cpp/src/barretenberg/ultra_honk/ultra_verifier.hpp @@ -11,11 +11,9 @@ #include "barretenberg/flavor/ultra_zk_recursive_flavor.hpp" #include "barretenberg/honk/proof_system/types/proof.hpp" #include "barretenberg/special_public_inputs/special_public_inputs.hpp" -#include "barretenberg/srs/global_crs.hpp" #include "barretenberg/stdlib/primitives/pairing_points.hpp" #include "barretenberg/stdlib/proof/proof.hpp" #include "barretenberg/stdlib/special_public_inputs/special_public_inputs.hpp" -#include "barretenberg/sumcheck/sumcheck.hpp" #include "barretenberg/ultra_honk/verifier_instance.hpp" namespace bb::stdlib::recursion::honk { @@ -84,7 +82,9 @@ template class UltraVerifier_ { using FF = typename Flavor::FF; using Commitment = typename Flavor::Commitment; using Curve = typename Flavor::Curve; + using PCS = typename Flavor::PCS; using VerificationKey = typename Flavor::VerificationKey; + using VerifierCommitments = typename Flavor::VerifierCommitments; using Transcript = typename Flavor::Transcript; using Instance = VerifierInstance_; @@ -116,15 +116,6 @@ template class UltraVerifier_ { bool reduction_succeeded = false; // Sumcheck and libra evaluation consistency checks }; - /** - * @brief Result of padding computation - * @details Contains virtual log_n and padding indicator array for sumcheck/shplemini - */ - struct PaddingData { - size_t log_n; - std::vector padding_indicator_array; - }; - /** * @brief A constructor for native and recursive verifiers * @param vk_and_hash Contains verification key and its hash @@ -149,15 +140,16 @@ template class UltraVerifier_ { } /** - * @brief Compute log_n and padding indicator array based on flavor configuration - * @details Handles all combinations of native/recursive, ZK/non-ZK, and padding/no-padding: - * - Non-ZK flavors: log_n from USE_PADDING, all 1s array - * - ZK without padding: log_n from VK, all 1s array - * - Native ZK with padding: VIRTUAL_LOG_N, simple loop comparison - * - Recursive ZK with padding: VIRTUAL_LOG_N, in-circuit Lagrange computation - * @return PaddingData containing log_n and padding_indicator_array + * @brief Compute log_n based on flavor. + * @details Returns VIRTUAL_LOG_N for padded flavors, or VK's log_circuit_size otherwise. + */ + size_t compute_log_n() const; + + /** + * @brief Compute padding indicator array based on flavor configuration. + * @details Must be called AFTER OinkVerifier::verify() so VK fields are properly tagged. */ - PaddingData process_padding() const; + std::vector compute_padding_indicator_array(size_t log_n) const; [[nodiscard("Reduction result should be verified")]] ReductionResult reduce_to_pairing_check(const Proof& proof); diff --git a/barretenberg/cpp/src/barretenberg/ultra_honk/witness_computation.cpp b/barretenberg/cpp/src/barretenberg/ultra_honk/witness_computation.cpp index a43f01a5d5ae..8d3a0ac1ce15 100644 --- a/barretenberg/cpp/src/barretenberg/ultra_honk/witness_computation.cpp +++ b/barretenberg/cpp/src/barretenberg/ultra_honk/witness_computation.cpp @@ -123,12 +123,10 @@ template void WitnessComputation::complete_prover_instance_for_test( const std::shared_ptr>& prover_inst) { - // Generate random eta, beta and gamma - prover_inst->relation_parameters.eta = FF::random_element(); - prover_inst->relation_parameters.eta = FF::random_element(); - prover_inst->relation_parameters.eta_two = FF::random_element(); - prover_inst->relation_parameters.eta_three = FF::random_element(); - prover_inst->relation_parameters.beta = FF::random_element(); + // Generate random eta, beta, gamma, compute powers + prover_inst->relation_parameters.compute_eta_powers(FF::random_element()); // eta, eta_two = eta², eta_three = eta³ + prover_inst->relation_parameters.compute_beta_powers( + FF::random_element()); // beta, beta_sqr = beta², beta_cube = beta³ prover_inst->relation_parameters.gamma = FF::random_element(); add_ram_rom_memory_records_to_wire_4(prover_inst->polynomials, diff --git a/barretenberg/sol/CLAUDE.md b/barretenberg/sol/CLAUDE.md new file mode 100644 index 000000000000..4372a5f63629 --- /dev/null +++ b/barretenberg/sol/CLAUDE.md @@ -0,0 +1,176 @@ +# Barretenberg Solidity Verifier - Development Guide + +## Overview + +This directory contains the Solidity implementation of the Honk verifier for on-chain proof verification. + +## Architecture + +### Core Verifier Files (src/honk/) + +| File | Purpose | +|------|---------| +| `HonkTypes.sol` | Defines structs: `VerificationKey`, `RelationParameters`, `Proof`, `G1Point` | +| `Fr.sol` | Field arithmetic library for BN254 scalar field | +| `Transcript.sol` | Fiat-Shamir challenge generation (non-ZK) | +| `ZKTranscript.sol` | Fiat-Shamir challenge generation (ZK variant) | +| `Relations.sol` | Constraint relation evaluations (arithmetic, permutation, lookup, memory, etc.) | +| `CommitmentScheme.sol` | KZG commitment verification | +| `BaseHonkVerifier.sol` | Main verifier logic (non-ZK) | +| `BaseZKHonkVerifier.sol` | Main verifier logic (ZK variant) | + +### Verification Keys (src/honk/keys/) + +Circuit-specific verification keys: +- `Add2HonkVerificationKey.sol` - Simple addition circuit +- `BlakeHonkVerificationKey.sol` - Blake hash circuit +- `EcdsaHonkVerificationKey.sol` - ECDSA verification circuit +- `RecursiveHonkVerificationKey.sol` - Recursive proof circuit + +### Optimized Verifier (src/honk/optimised/) + +- `blake-opt.sol` - Hand-optimized assembly verifier for Blake circuit +- `blake-opt.sol.template` - Template used to generate blake-opt.sol +- `generate_offsets.py` - Helper for memory layout + +### C++ Contract Templates (cpp/src/barretenberg/dsl/acir_proofs/) + +These hpp files contain embedded Solidity code used by bb CLI to generate verifiers: +- `honk_contract.hpp` - Standard Honk verifier template +- `honk_zk_contract.hpp` - ZK Honk verifier template +- `honk_optimized_contract.hpp` - Optimized verifier template + +## Key Scripts + +### Regeneration Scripts (scripts/) + +```bash +# Regenerate honk_contract.hpp and honk_zk_contract.hpp from Solidity sources +./scripts/copy_to_cpp.sh -f + +# Sync VK values from BlakeHonkVerificationKey.sol to blake-opt.sol +./scripts/sync_blake_opt_vk.sh + +# Copy blake-opt.sol to honk_optimized_contract.hpp +./scripts/copy_optimized_to_cpp.sh -f + +# Regenerate all VKs (requires rebuilt bb) +./scripts/init_honk.sh +``` + +### Test Scripts + +```bash +# Run all Solidity tests +forge test + +# Run specific test +forge test --match-test testValidProof + +# Run with verbosity +forge test -vvv +``` + +## Common Debugging Workflow + +### 1. SumcheckFailed Errors + +When tests fail with `SumcheckFailed()`: + +1. **Check challenge generation matches C++** + - Solidity: `Transcript.sol` / `ZKTranscript.sol` + - C++: `ultra_honk/oink_verifier.cpp`, `transcript/transcript.hpp` + +2. **Check relation formulas match C++** + - Solidity: `Relations.sol` + - C++: `relations/*.hpp` (e.g., `logderiv_lookup_relation.hpp`) + +3. **Verify struct fields match** + - Solidity: `HonkTypes.sol` → `RelationParameters` struct + - C++: `relations/relation_parameters.hpp` + +4. **Rebuild proof generator** + ```bash + cd ../cpp/build && ninja honk_solidity_proof_gen + ``` + +5. **Regenerate VKs if circuit changed** + ```bash + cd ../cpp/build && ninja honk_solidity_key_gen + cd ../../sol && ./scripts/init_honk.sh + ``` + +### 2. Challenge Splitting + +The Solidity verifier uses keccak256 and splits 254-bit hashes into two 127-bit challenges: + +```solidity +function splitChallenge(Fr challenge) returns (Fr first, Fr second) { + uint256 lo = uint256(challenge) & 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; // 127 bits + uint256 hi = uint256(challenge) >> 127; + return (Fr(lo), Fr(hi)); +} +``` + +C++ equivalent in `field_conversion.hpp`: +```cpp +const uint256_t lo = u.slice(0, 127); +const uint256_t hi = u.slice(127, 254); +return { bb::fr(lo), bb::fr(hi) }; +``` + +### 3. Eta/Beta Power Computation + +Current approach (simplified struct): +- `eta` is a single challenge from transcript +- `eta_two = eta * eta` computed locally in Relations.sol +- `eta_three = eta_two * eta` computed locally + +Same for beta powers used in lookup relation. + +### 4. File Regeneration Order + +When making changes to core Solidity files: + +1. Edit Solidity files (`HonkTypes.sol`, `Transcript.sol`, `Relations.sol`, etc.) +2. Run `./scripts/copy_to_cpp.sh -f` to update hpp files +3. Rebuild C++ if needed: `cd ../cpp/build && ninja honk_solidity_proof_gen` +4. Run tests: `forge test` + +For optimized verifier changes: +1. Edit `blake-opt.sol.template` +2. Run `./scripts/sync_blake_opt_vk.sh` to apply VK values +3. Run `./scripts/copy_optimized_to_cpp.sh -f` + +## Test Structure + +| Test File | Circuit | Description | +|-----------|---------|-------------| +| `Add2.t.sol` | add2 | Simple x + y = z | +| `Blake.t.sol` | blake | Blake2 hash | +| `blakeOpt.t.sol` | blake | Optimized verifier | +| `ECDSA.t.sol` | ecdsa | Signature verification | +| `Recursive.t.sol` | recursive | Recursive proof | +| `*ZK.t.sol` | * | ZK variants | + +## Relation Parameters + +The `RelationParameters` struct contains Fiat-Shamir challenges: + +```solidity +struct RelationParameters { + Fr eta; // Memory relation + Fr beta; // Permutation + Lookup + Fr gamma; // Permutation + Lookup + Fr publicInputsDelta; // Derived value +} +``` + +Powers (eta², eta³, β², β³) are computed locally where needed, not stored. + +## Debugging Tips + +1. **Compare hashes**: Add logging to compare challenge values between Solidity and C++ +2. **Isolate relations**: Comment out relation accumulations in `Relations.sol` to find the failing one +3. **Check wire mappings**: Ensure `WIRE` enum matches C++ `AllEntities` ordering +4. **Verify VK hash**: The `VK_HASH` constant must match what C++ computes diff --git a/barretenberg/sol/src/honk/HonkTypes.sol b/barretenberg/sol/src/honk/HonkTypes.sol index b8fbd0ef2d41..b691da0aaa6b 100644 --- a/barretenberg/sol/src/honk/HonkTypes.sol +++ b/barretenberg/sol/src/honk/HonkTypes.sol @@ -117,8 +117,6 @@ library Honk { struct RelationParameters { // challenges Fr eta; - Fr etaTwo; - Fr etaThree; Fr beta; Fr gamma; // derived diff --git a/barretenberg/sol/src/honk/Relations.sol b/barretenberg/sol/src/honk/Relations.sol index e888f474dee2..6fb92beceb90 100644 --- a/barretenberg/sol/src/honk/Relations.sol +++ b/barretenberg/sol/src/honk/Relations.sol @@ -140,19 +140,23 @@ library RelationsLib { Fr read_term; // Calculate the write term (the table accumulation) + // write_term = table_1 + γ + table_2 * β + table_3 * β² + table_4 * β³ { - write_term = wire(p, WIRE.TABLE_1) + rp.gamma + (wire(p, WIRE.TABLE_2) * rp.eta) - + (wire(p, WIRE.TABLE_3) * rp.etaTwo) + (wire(p, WIRE.TABLE_4) * rp.etaThree); + Fr beta_sqr = rp.beta * rp.beta; + write_term = wire(p, WIRE.TABLE_1) + rp.gamma + (wire(p, WIRE.TABLE_2) * rp.beta) + + (wire(p, WIRE.TABLE_3) * beta_sqr) + (wire(p, WIRE.TABLE_4) * beta_sqr * rp.beta); } - // Calculate the write term + // Calculate the read term + // read_term = derived_entry_1 + γ + derived_entry_2 * β + derived_entry_3 * β² + q_index * β³ { + Fr beta_sqr = rp.beta * rp.beta; Fr derived_entry_1 = wire(p, WIRE.W_L) + rp.gamma + (wire(p, WIRE.Q_R) * wire(p, WIRE.W_L_SHIFT)); Fr derived_entry_2 = wire(p, WIRE.W_R) + wire(p, WIRE.Q_M) * wire(p, WIRE.W_R_SHIFT); Fr derived_entry_3 = wire(p, WIRE.W_O) + wire(p, WIRE.Q_C) * wire(p, WIRE.W_O_SHIFT); - read_term = derived_entry_1 + (derived_entry_2 * rp.eta) + (derived_entry_3 * rp.etaTwo) - + (wire(p, WIRE.Q_O) * rp.etaThree); + read_term = derived_entry_1 + (derived_entry_2 * rp.beta) + (derived_entry_3 * beta_sqr) + + (wire(p, WIRE.Q_O) * beta_sqr * rp.beta); } Fr read_inverse = wire(p, WIRE.LOOKUP_INVERSES) * write_term; @@ -346,6 +350,10 @@ library RelationsLib { ) internal pure { MemParams memory ap; + // Compute eta powers locally + Fr eta_two = rp.eta * rp.eta; + Fr eta_three = eta_two * rp.eta; + /** * MEMORY * @@ -387,8 +395,8 @@ library RelationsLib { * * For ROM gates, qc = 0 */ - ap.memory_record_check = wire(p, WIRE.W_O) * rp.etaThree; - ap.memory_record_check = ap.memory_record_check + (wire(p, WIRE.W_R) * rp.etaTwo); + ap.memory_record_check = wire(p, WIRE.W_O) * eta_three; + ap.memory_record_check = ap.memory_record_check + (wire(p, WIRE.W_R) * eta_two); ap.memory_record_check = ap.memory_record_check + (wire(p, WIRE.W_L) * rp.eta); ap.memory_record_check = ap.memory_record_check + wire(p, WIRE.Q_C); ap.partial_record_check = ap.memory_record_check; // used in RAM consistency check; deg 1 or 4 @@ -449,8 +457,8 @@ library RelationsLib { // TODO(https://github.com/AztecProtocol/barretenberg/issues/757): If we sorted in // reverse order we could re-use `ap.partial_record_check` 1 - ((w3' * eta + w2') * eta + w1') * eta // deg 1 or 4 - ap.next_gate_access_type = wire(p, WIRE.W_O_SHIFT) * rp.etaThree; - ap.next_gate_access_type = ap.next_gate_access_type + (wire(p, WIRE.W_R_SHIFT) * rp.etaTwo); + ap.next_gate_access_type = wire(p, WIRE.W_O_SHIFT) * eta_three; + ap.next_gate_access_type = ap.next_gate_access_type + (wire(p, WIRE.W_R_SHIFT) * eta_two); ap.next_gate_access_type = ap.next_gate_access_type + (wire(p, WIRE.W_L_SHIFT) * rp.eta); ap.next_gate_access_type = wire(p, WIRE.W_4_SHIFT) - ap.next_gate_access_type; diff --git a/barretenberg/sol/src/honk/Transcript.sol b/barretenberg/sol/src/honk/Transcript.sol index 5d5163cb88d7..936bc6db227a 100644 --- a/barretenberg/sol/src/honk/Transcript.sol +++ b/barretenberg/sol/src/honk/Transcript.sol @@ -74,10 +74,9 @@ library TranscriptLib { uint256 publicInputsSize, Fr previousChallenge ) internal pure returns (Honk.RelationParameters memory rp, Fr nextPreviousChallenge) { - (rp.eta, rp.etaTwo, rp.etaThree, previousChallenge) = - generateEtaChallenge(proof, publicInputs, vkHash, publicInputsSize); + (rp.eta, previousChallenge) = generateEtaChallenge(proof, publicInputs, vkHash, publicInputsSize); - (rp.beta, rp.gamma, nextPreviousChallenge) = generateBetaAndGammaChallenges(previousChallenge, proof); + (rp.beta, rp.gamma, nextPreviousChallenge) = generateBetaGammaChallenges(previousChallenge, proof); } function generateEtaChallenge( @@ -85,7 +84,7 @@ library TranscriptLib { bytes32[] calldata publicInputs, uint256 vkHash, uint256 publicInputsSize - ) internal pure returns (Fr eta, Fr etaTwo, Fr etaThree, Fr previousChallenge) { + ) internal pure returns (Fr eta, Fr previousChallenge) { bytes32[] memory round0 = new bytes32[](1 + publicInputsSize + 6); round0[0] = bytes32(vkHash); @@ -106,14 +105,10 @@ library TranscriptLib { round0[1 + publicInputsSize + 5] = bytes32(proof.w3.y); previousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(round0))); - (eta, etaTwo) = splitChallenge(previousChallenge); - - previousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(Fr.unwrap(previousChallenge)))); - Fr unused; - (etaThree, unused) = splitChallenge(previousChallenge); + (eta,) = splitChallenge(previousChallenge); } - function generateBetaAndGammaChallenges(Fr previousChallenge, Honk.Proof memory proof) + function generateBetaGammaChallenges(Fr previousChallenge, Honk.Proof memory proof) internal pure returns (Fr beta, Fr gamma, Fr nextPreviousChallenge) diff --git a/barretenberg/sol/src/honk/ZKTranscript.sol b/barretenberg/sol/src/honk/ZKTranscript.sol index 5af7fc686b82..81286356831b 100644 --- a/barretenberg/sol/src/honk/ZKTranscript.sol +++ b/barretenberg/sol/src/honk/ZKTranscript.sol @@ -78,10 +78,9 @@ library ZKTranscriptLib { uint256 publicInputsSize, Fr previousChallenge ) internal pure returns (Honk.RelationParameters memory rp, Fr nextPreviousChallenge) { - (rp.eta, rp.etaTwo, rp.etaThree, previousChallenge) = - generateEtaChallenge(proof, publicInputs, vkHash, publicInputsSize); + (rp.eta, previousChallenge) = generateEtaChallenge(proof, publicInputs, vkHash, publicInputsSize); - (rp.beta, rp.gamma, nextPreviousChallenge) = generateBetaAndGammaChallenges(previousChallenge, proof); + (rp.beta, rp.gamma, nextPreviousChallenge) = generateBetaGammaChallenges(previousChallenge, proof); } function generateEtaChallenge( @@ -89,7 +88,7 @@ library ZKTranscriptLib { bytes32[] calldata publicInputs, uint256 vkHash, uint256 publicInputsSize - ) internal pure returns (Fr eta, Fr etaTwo, Fr etaThree, Fr previousChallenge) { + ) internal pure returns (Fr eta, Fr previousChallenge) { // Size: 1 (vkHash) + publicInputsSize + 8 (geminiMask(2) + 3 wires(6)) bytes32[] memory round0 = new bytes32[](1 + publicInputsSize + 8); round0[0] = bytes32(vkHash); @@ -115,13 +114,10 @@ library ZKTranscriptLib { round0[1 + publicInputsSize + 7] = bytes32(proof.w3.y); previousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(round0))); - (eta, etaTwo) = splitChallenge(previousChallenge); - previousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(Fr.unwrap(previousChallenge)))); - - (etaThree,) = splitChallenge(previousChallenge); + (eta,) = splitChallenge(previousChallenge); } - function generateBetaAndGammaChallenges(Fr previousChallenge, Honk.ZKProof memory proof) + function generateBetaGammaChallenges(Fr previousChallenge, Honk.ZKProof memory proof) internal pure returns (Fr beta, Fr gamma, Fr nextPreviousChallenge) diff --git a/barretenberg/sol/src/honk/optimised/blake-opt.sol.template b/barretenberg/sol/src/honk/optimised/blake-opt.sol.template index 040ecb98a28d..a2857144c82b 100644 --- a/barretenberg/sol/src/honk/optimised/blake-opt.sol.template +++ b/barretenberg/sol/src/honk/optimised/blake-opt.sol.template @@ -1148,19 +1148,16 @@ contract BlakeOptHonkVerifier is IVerifier { // 0x2e0 = 1 * 32 bytes + 3 * 64 bytes for (w1,w2,w3) + 0x200 for pairing points let eta_input_length := add(0x2e0, public_inputs_size) + // Get single eta challenge and compute powers (eta, eta², eta³) let prev_challenge := mod(keccak256(0x00, eta_input_length), p) mstore(0x00, prev_challenge) let eta := and(prev_challenge, LOWER_127_MASK) - let etaTwo := shr(127, prev_challenge) + let eta_two := mulmod(eta, eta, p) + let eta_three := mulmod(eta_two, eta, p) mstore(ETA_CHALLENGE, eta) - mstore(ETA_TWO_CHALLENGE, etaTwo) - - prev_challenge := mod(keccak256(0x00, 0x20), p) - - mstore(0x00, prev_challenge) - let eta_three := and(prev_challenge, LOWER_127_MASK) + mstore(ETA_TWO_CHALLENGE, eta_two) mstore(ETA_THREE_CHALLENGE, eta_three) /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ @@ -1935,24 +1932,27 @@ contract BlakeOptHonkVerifier is IVerifier { /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* LOGUP WIDGET EVALUATION */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ + // Note: Using beta powers for column batching and gamma for offset ensures soundness + // beta and gamma must be independent challenges (they come from splitting the same hash) { - let eta := mload(ETA_CHALLENGE) - let eta_two := mload(ETA_TWO_CHALLENGE) - let eta_three := mload(ETA_THREE_CHALLENGE) - - let beta := mload(BETA_CHALLENGE) let gamma := mload(GAMMA_CHALLENGE) + let beta := mload(BETA_CHALLENGE) + // Compute beta powers inline (β², β³) for lookup column batching + let beta_sqr := mulmod(beta, beta, p) + let beta_cube := mulmod(beta_sqr, beta, p) + // write_term = table_1 + γ + table_2 * β + table_3 * β² + table_4 * β³ let t0 := - addmod(addmod(mload(TABLE1_EVAL_LOC), gamma, p), mulmod(mload(TABLE2_EVAL_LOC), eta, p), p) + addmod(addmod(mload(TABLE1_EVAL_LOC), gamma, p), mulmod(mload(TABLE2_EVAL_LOC), beta, p), p) let t1 := addmod( - mulmod(mload(TABLE3_EVAL_LOC), eta_two, p), - mulmod(mload(TABLE4_EVAL_LOC), eta_three, p), + mulmod(mload(TABLE3_EVAL_LOC), beta_sqr, p), + mulmod(mload(TABLE4_EVAL_LOC), beta_cube, p), p ) let write_term := addmod(t0, t1, p) + // read_term = derived_entry_1 + γ + derived_entry_2 * β + derived_entry_3 * β² + q_index * β³ t0 := addmod( addmod(mload(W1_EVAL_LOC), gamma, p), mulmod(mload(QR_EVAL_LOC), mload(W1_SHIFT_EVAL_LOC), p), @@ -1961,9 +1961,9 @@ contract BlakeOptHonkVerifier is IVerifier { t1 := addmod(mload(W2_EVAL_LOC), mulmod(mload(QM_EVAL_LOC), mload(W2_SHIFT_EVAL_LOC), p), p) let t2 := addmod(mload(W3_EVAL_LOC), mulmod(mload(QC_EVAL_LOC), mload(W3_SHIFT_EVAL_LOC), p), p) - let read_term := addmod(t0, mulmod(t1, eta, p), p) - read_term := addmod(read_term, mulmod(t2, eta_two, p), p) - read_term := addmod(read_term, mulmod(mload(QO_EVAL_LOC), eta_three, p), p) + let read_term := addmod(t0, mulmod(t1, beta, p), p) + read_term := addmod(read_term, mulmod(t2, beta_sqr, p), p) + read_term := addmod(read_term, mulmod(mload(QO_EVAL_LOC), beta_cube, p), p) let read_inverse := mulmod(mload(LOOKUP_INVERSES_EVAL_LOC), write_term, p) let write_inverse := mulmod(mload(LOOKUP_INVERSES_EVAL_LOC), read_term, p)