From b5f70176517865cc1d6a99dbe095fd107d13e90e Mon Sep 17 00:00:00 2001 From: Suyash Bagad Date: Tue, 7 Apr 2026 16:09:14 +0200 Subject: [PATCH 1/9] fix: addresses logic audit findings (#22304) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #### L1: ACIR `num_bits` Validation Status: won't fix It is true that `create_logic_constraint` uses `BB_ASSERT` (which aborts) rather than throwing an exception when `num_bits` is invalid. `BB_ASSERT` is the standard pattern throughout barretenberg for structural invariant checks. Additionally, the `num_bits` value originates from Noir's `IntegerBitSize` enum, which restricts values to {1, 8, 16, 32, 64, 128} at the compiler level, so invalid values cannot reach this code through normal operation. #### L2: OriginTag Provenance Status: fixed 0f3ca1c6a8 `create_logic_constraint` was not propagating `OriginTag` when converting constant inputs to witnesses, or when returning the final result. Tags are now copied onto the witness before recursing (for both the left-constant and right-constant paths), and the final accumulated result is tagged with the merged tag of both operands. Note we do not propagate tags to the chunks because these are intermediate circuit variables that are not used outside of logic. #### L3: Builder Context Validation Status: fixed 249bfdc760 When both inputs are witnesses, the code extracted the builder context from only one operand without verifying both refer to the same builder. Added `validate_context(a.get_context(), b.get_context())` to assert consistency, matching the pattern used in other stdlib primitives. #### L4: Write-VK Mode Guard for Constant Inputs Status: fixed 5b3d4b857a In write-VK mode, when both inputs are constants, `create_logic_constraint` returns a constant `field_t`. The ACIR bridge then attempts `assert_equal` against a result witness that holds a dummy zero, causing a spurious failure. The fix uses `builder.set_variable()` to patch the result witness with the correct computed value before the equality check. #### I1: Witness Bounds Checks Are Debug-Only Status: acknowledged, will be fixed later The witness index bounds checks in `get_variable()` use `BB_ASSERT_DEBUG`, which is compiled out in release builds. This is known and it affects every circuit component that calls `get_variable()`, not just the logic gadget. We are discussing this the noir team if it can be exploited in any meaningful way. Regardless, we will enable these asserts in release mode soon after ensuring no significant hit in performance because of the asserts. #### I2: Oversized Plookup Table Status: won't fix The code always uses `UINT32_XOR`/`UINT32_AND` plookup tables even when the last chunk is smaller than 32 bits. Smaller tables (UINT8, UINT16) exist and would use fewer sub-lookups. However, this is a minor optimization that would change the circuit structure, which we want to avoid at this point. The existing explicit range constraint on the last chunk ensures correctness regardless of table size. #### I3: Operator Precedence Bug in Test Status: fixed — 2a597fb73b The test computed `1 << num_bits - 1` intending "all bits set" (`(1 << num_bits) - 1`), but C++ precedence evaluates it as `1 << (num_bits - 1)` (a single bit). For `num_bits = 8`, this gives `128` instead of `255`. Corrected the test. #### I4: Redundant Range Constraints Status: won't fix Yes there could be cases of redundant range constraints (on the noir side as well as barretenberg) but we prefer to have redundancy over missing a range constraint. This was a good find but we decided to not use the optimisation. --- .../dsl/acir_format/logic_constraint.cpp | 9 +++ .../dsl/acir_format/logic_constraint.test.cpp | 4 +- .../stdlib/primitives/logic/logic.cpp | 5 +- .../stdlib/primitives/logic/logic.test.cpp | 61 +++++++++++++++++++ 4 files changed, 76 insertions(+), 3 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/logic_constraint.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/logic_constraint.cpp index 83b66882f4ba..b8522cd06852 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/logic_constraint.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/logic_constraint.cpp @@ -23,6 +23,15 @@ void create_logic_gate(Builder& builder, field_ct right = to_field_ct(b, builder); field_ct computed_result = bb::stdlib::logic::create_logic_constraint(left, right, num_bits, is_xor_gate); + + // In write-VK mode the result witness holds a dummy zero. In certain cases, the computed result is non-zero so the + // assert_equal would spuriously fail. Patch the witness value so the downstream assertion sees the correct value. + // Eg. for an XOR gate, if the inputs are constants such that the result is a non-zero constant, the assert_equal + // will fail in write-VK mode since the result witness is initialized to zero. + if (builder.is_write_vk_mode()) { + builder.set_variable(result, computed_result.get_value()); + } + field_ct acir_result = field_ct::from_witness_index(&builder, result); computed_result.assert_equal(acir_result); } diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/logic_constraint.test.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/logic_constraint.test.cpp index 10b1b2c69229..96a408e13879 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/logic_constraint.test.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/logic_constraint.test.cpp @@ -73,8 +73,8 @@ class LogicConstraintTestingFunctions { return WitnessOrConstant::from_index(input_index); }; - bb::fr lhs = FF(static_cast(1) << num_bits - 1); // All bits from 0 to num_bits-1 are set - bb::fr rhs = FF(static_cast(1) << num_bits - 1); // All bits from 0 to num_bits-1 are set + bb::fr lhs = FF((static_cast(1) << num_bits) - 1); // All bits from 0 to num_bits-1 are set + bb::fr rhs = FF((static_cast(1) << num_bits) - 1); // All bits from 0 to num_bits-1 are set bb::fr result = is_xor_gate ? (static_cast(lhs) ^ static_cast(rhs)) : (static_cast(lhs) & static_cast(rhs)); diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/logic/logic.cpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/logic/logic.cpp index e9414e3ed14d..e286ff112657 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/logic/logic.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/logic/logic.cpp @@ -58,6 +58,7 @@ field_t logic::create_logic_constraint( Builder* ctx = b.get_context(); uint256_t a_native(a.get_value()); field_pt a_witness = field_pt::from_witness_index(ctx, ctx->put_constant_variable(a_native)); + a_witness.set_origin_tag(a.get_origin_tag()); return create_logic_constraint(a_witness, b, num_bits, is_xor_gate, get_chunk); } @@ -65,10 +66,11 @@ field_t logic::create_logic_constraint( Builder* ctx = a.get_context(); uint256_t b_native(b.get_value()); field_pt b_witness = field_pt::from_witness_index(ctx, ctx->put_constant_variable(b_native)); + b_witness.set_origin_tag(b.get_origin_tag()); return create_logic_constraint(a, b_witness, num_bits, is_xor_gate, get_chunk); } - Builder* ctx = a.get_context(); + Builder* ctx = validate_context(a.get_context(), b.get_context()); // We slice the input values into 32-bit chunks, and then use a multi-table lookup to compute the AND or XOR // of each chunk. Since we perform the lookup from 32-bit multi-tables, the lookup operation implicitly enforces a @@ -111,6 +113,7 @@ field_t logic::create_logic_constraint( a.assert_equal(a_accumulator, "stdlib logic: failed to reconstruct left operand"); b.assert_equal(b_accumulator, "stdlib logic: failed to reconstruct right operand"); + res.set_origin_tag(OriginTag(a.get_origin_tag(), b.get_origin_tag())); return res; } template class logic; diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/logic/logic.test.cpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/logic/logic.test.cpp index e7d4dd568300..1cbf428468d1 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/logic/logic.test.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/logic/logic.test.cpp @@ -143,3 +143,64 @@ TYPED_TEST(LogicTest, DifferentWitnessSameResult) bool result = CircuitChecker::check(builder); EXPECT_EQ(result, false); } + +// Regression test: OriginTag must propagate through all logic constraint paths. +TYPED_TEST(LogicTest, OriginTagPropagation) +{ + STDLIB_TYPE_ALIASES + STANDARD_TESTING_TAGS + + auto builder = Builder(); + uint256_t a_val = 0xAB; + uint256_t b_val = 0xCD; + + // Both witnesses + { + field_ct x = witness_ct(&builder, a_val); + field_ct y = witness_ct(&builder, b_val); + x.set_origin_tag(submitted_value_origin_tag); + y.set_origin_tag(challenge_origin_tag); + + field_ct and_result = stdlib::logic::create_logic_constraint(x, y, 8, false); + field_ct xor_result = stdlib::logic::create_logic_constraint(x, y, 8, true); + + EXPECT_EQ(and_result.get_origin_tag(), first_two_merged_tag); + EXPECT_EQ(xor_result.get_origin_tag(), first_two_merged_tag); + } + + // Left constant, right witness + { + field_ct x_const(&builder, a_val); + field_ct y = witness_ct(&builder, b_val); + x_const.set_origin_tag(submitted_value_origin_tag); + y.set_origin_tag(challenge_origin_tag); + + field_ct result = stdlib::logic::create_logic_constraint(x_const, y, 8, false); + EXPECT_EQ(result.get_origin_tag(), first_two_merged_tag); + } + + // Right constant, left witness + { + field_ct x = witness_ct(&builder, a_val); + field_ct y_const(&builder, b_val); + x.set_origin_tag(submitted_value_origin_tag); + y_const.set_origin_tag(challenge_origin_tag); + + field_ct result = stdlib::logic::create_logic_constraint(x, y_const, 8, false); + EXPECT_EQ(result.get_origin_tag(), first_two_merged_tag); + } + + // Both constants + { + field_ct x_const(&builder, a_val); + field_ct y_const(&builder, b_val); + x_const.set_origin_tag(submitted_value_origin_tag); + y_const.set_origin_tag(challenge_origin_tag); + + field_ct result = stdlib::logic::create_logic_constraint(x_const, y_const, 8, false); + EXPECT_EQ(result.get_origin_tag(), first_two_merged_tag); + } + + bool result = CircuitChecker::check(builder); + EXPECT_EQ(result, true); +} From c3efd60a26fb9a6546a7e1b7dc9ffa0a3346e601 Mon Sep 17 00:00:00 2001 From: Raju Krishnamoorthy Date: Tue, 7 Apr 2026 17:56:27 +0200 Subject: [PATCH 2/9] chore: fix asm_self_* field arithmetic signatures (#22353) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary - Remove incorrect `const` from the first parameter of the 5 `asm_self_*` field functions (mul, sqr, add, sub, reduce_once). These functions modify the parameter in-place via inline assembly but declared it `const field&`. ## Test plan - [x] `ecc_tests` — 836/836 passed Co-authored-by: notnotraju --- .../src/barretenberg/ecc/fields/field_declarations.hpp | 10 +++++----- .../cpp/src/barretenberg/ecc/fields/field_impl_x64.hpp | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/ecc/fields/field_declarations.hpp b/barretenberg/cpp/src/barretenberg/ecc/fields/field_declarations.hpp index 7a0a1298f452..fdbb7508dfa8 100644 --- a/barretenberg/cpp/src/barretenberg/ecc/fields/field_declarations.hpp +++ b/barretenberg/cpp/src/barretenberg/ecc/fields/field_declarations.hpp @@ -629,14 +629,14 @@ template struct alignas(32) field { BB_INLINE static field asm_sqr_with_coarse_reduction(const field& a) noexcept; BB_INLINE static field asm_add_with_coarse_reduction(const field& a, const field& b) noexcept; BB_INLINE static field asm_sub_with_coarse_reduction(const field& a, const field& b) noexcept; - BB_INLINE static void asm_self_mul_with_coarse_reduction(const field& a, const field& b) noexcept; - BB_INLINE static void asm_self_sqr_with_coarse_reduction(const field& a) noexcept; - BB_INLINE static void asm_self_add_with_coarse_reduction(const field& a, const field& b) noexcept; - BB_INLINE static void asm_self_sub_with_coarse_reduction(const field& a, const field& b) noexcept; + BB_INLINE static void asm_self_mul_with_coarse_reduction(field& a, const field& b) noexcept; + BB_INLINE static void asm_self_sqr_with_coarse_reduction(field& a) noexcept; + BB_INLINE static void asm_self_add_with_coarse_reduction(field& a, const field& b) noexcept; + BB_INLINE static void asm_self_sub_with_coarse_reduction(field& a, const field& b) noexcept; BB_INLINE static void asm_conditional_negate(field& r, uint64_t predicate) noexcept; BB_INLINE static field asm_reduce_once(const field& a) noexcept; - BB_INLINE static void asm_self_reduce_once(const field& a) noexcept; + BB_INLINE static void asm_self_reduce_once(field& a) noexcept; static constexpr uint64_t zero_reference = 0x00ULL; #endif constexpr field tonelli_shanks_sqrt() const noexcept; diff --git a/barretenberg/cpp/src/barretenberg/ecc/fields/field_impl_x64.hpp b/barretenberg/cpp/src/barretenberg/ecc/fields/field_impl_x64.hpp index 1ae46ef8acd3..9af0bf5f653f 100644 --- a/barretenberg/cpp/src/barretenberg/ecc/fields/field_impl_x64.hpp +++ b/barretenberg/cpp/src/barretenberg/ecc/fields/field_impl_x64.hpp @@ -46,7 +46,7 @@ template field field::asm_mul_with_coarse_reduction(const field& return r; } -template void field::asm_self_mul_with_coarse_reduction(const field& a, const field& b) noexcept +template void field::asm_self_mul_with_coarse_reduction(field& a, const field& b) noexcept { constexpr uint64_t r_inv = T::r_inv; constexpr uint64_t modulus_0 = modulus.data[0]; @@ -141,7 +141,7 @@ template field field::asm_sqr_with_coarse_reduction(const field& return r; } -template void field::asm_self_sqr_with_coarse_reduction(const field& a) noexcept +template void field::asm_self_sqr_with_coarse_reduction(field& a) noexcept { constexpr uint64_t r_inv = T::r_inv; constexpr uint64_t modulus_0 = modulus.data[0]; @@ -227,7 +227,7 @@ template field field::asm_add_with_coarse_reduction(const field& return r; } -template void field::asm_self_add_with_coarse_reduction(const field& a, const field& b) noexcept +template void field::asm_self_add_with_coarse_reduction(field& a, const field& b) noexcept { constexpr uint64_t twice_not_modulus_0 = twice_not_modulus.data[0]; constexpr uint64_t twice_not_modulus_1 = twice_not_modulus.data[1]; @@ -277,7 +277,7 @@ template field field::asm_sub_with_coarse_reduction(const field& return r; } -template void field::asm_self_sub_with_coarse_reduction(const field& a, const field& b) noexcept +template void field::asm_self_sub_with_coarse_reduction(field& a, const field& b) noexcept { constexpr uint64_t twice_modulus_0 = twice_modulus.data[0]; constexpr uint64_t twice_modulus_1 = twice_modulus.data[1]; @@ -353,7 +353,7 @@ template field field::asm_reduce_once(const field& a) noexcept return r; } -template void field::asm_self_reduce_once(const field& a) noexcept +template void field::asm_self_reduce_once(field& a) noexcept { constexpr uint64_t not_modulus_0 = not_modulus.data[0]; constexpr uint64_t not_modulus_1 = not_modulus.data[1]; From 43738384a081d0b49a3b4f8db7fa605ca3aa85e1 Mon Sep 17 00:00:00 2001 From: Jonathan Hao Date: Tue, 7 Apr 2026 17:59:57 +0100 Subject: [PATCH 3/9] fix: polynomials module audit response (#22361) ## Summary Addresses findings from the polynomials/ module security audit (https://github.com/AztecProtocol/barretenberg-claude/issues/2383). - **F1** (Low): Harden file-backed polynomial memory: restrict temp file permissions to `0600`, add `O_EXCL` to prevent race conditions, use `MAP_PRIVATE` instead of `MAP_SHARED` - **F4** (Info): Guard `compute_linear_polynomial_product` against `n=0` to prevent underflow - **F5** (Info): Clarify `UnivariateCoefficientBasis` docstring to distinguish Karatsuba precomputation from polynomial coefficients **Already fixed upstream:** - **F2** (Low): `factor_roots` empty polynomial guard (in #22282) - **F3** (Info): `get_scratch_space` thread safety via mutex (in #22306) --- .../src/barretenberg/polynomials/backing_memory.hpp | 4 ++-- .../polynomials/polynomial_arithmetic.cpp | 3 +++ .../polynomials/univariate_coefficient_basis.hpp | 11 +++++++---- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/polynomials/backing_memory.hpp b/barretenberg/cpp/src/barretenberg/polynomials/backing_memory.hpp index 2d9f92713100..36f827664ca8 100644 --- a/barretenberg/cpp/src/barretenberg/polynomials/backing_memory.hpp +++ b/barretenberg/cpp/src/barretenberg/polynomials/backing_memory.hpp @@ -159,7 +159,7 @@ template struct BackingMemory { std::string filename = temp_dir / ("poly-mmap-" + std::to_string(getpid()) + "-" + std::to_string(id)); - int fd = open(filename.c_str(), O_CREAT | O_RDWR | O_TRUNC, 0644); + int fd = open(filename.c_str(), O_CREAT | O_RDWR | O_TRUNC | O_EXCL, 0600); if (fd < 0) { current_storage_usage.fetch_sub(required_bytes); return false; @@ -172,7 +172,7 @@ template struct BackingMemory { return false; } - void* addr = mmap(nullptr, file_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + void* addr = mmap(nullptr, file_size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0); if (addr == MAP_FAILED) { close(fd); std::filesystem::remove(filename); diff --git a/barretenberg/cpp/src/barretenberg/polynomials/polynomial_arithmetic.cpp b/barretenberg/cpp/src/barretenberg/polynomials/polynomial_arithmetic.cpp index e72d63752dd7..c8ba8a34cb9d 100644 --- a/barretenberg/cpp/src/barretenberg/polynomials/polynomial_arithmetic.cpp +++ b/barretenberg/cpp/src/barretenberg/polynomials/polynomial_arithmetic.cpp @@ -212,6 +212,9 @@ template Fr compute_sum(const Fr* src, const size_t n) // This function computes the polynomial (x - a)(x - b)(x - c)... given n distinct roots (a, b, c, ...). template void compute_linear_polynomial_product(const Fr* roots, Fr* dest, const size_t n) { + if (n == 0) { + return; + } auto scratch_space_ptr = get_scratch_space(n); auto scratch_space = scratch_space_ptr.get(); diff --git a/barretenberg/cpp/src/barretenberg/polynomials/univariate_coefficient_basis.hpp b/barretenberg/cpp/src/barretenberg/polynomials/univariate_coefficient_basis.hpp index 4f78475368d9..217244046c8e 100644 --- a/barretenberg/cpp/src/barretenberg/polynomials/univariate_coefficient_basis.hpp +++ b/barretenberg/cpp/src/barretenberg/polynomials/univariate_coefficient_basis.hpp @@ -43,11 +43,14 @@ template class UnivariateCoef using value_type = Fr; // used to get the type of the elements consistently with std::array /** - * @brief coefficients is a length-3 array with the following representation: + * @brief Storage for polynomial coefficients (always 3 elements for uniform layout). * @details This class represents a polynomial P(X) = a0 + a1.X + a2.X^2 - * We define `coefficients[0] = a0` and `coefficients[1] = a1` - * If LENGTH == 2 AND `has_a0_plus_a1 = true` then `coefficients[2] = a0 + a1` - * If LENGTH == 3 then `coefficients[2] = a2` + * `coefficients[0] = a0`, `coefficients[1] = a1`. + * The meaning of `coefficients[2]` depends on the template parameters: + * - LENGTH == 2 AND has_a0_plus_a1 == true: coefficients[2] = a0 + a1 + * (precomputed for Karatsuba multiplication; NOT a polynomial coefficient) + * - LENGTH == 2 AND has_a0_plus_a1 == false: coefficients[2] is unused + * - LENGTH == 3: coefficients[2] = a2 */ std::array coefficients; From 97e38020cfe05a52004b8be7c4396148df1b8734 Mon Sep 17 00:00:00 2001 From: ledwards2225 <98505400+ledwards2225@users.noreply.github.com> Date: Tue, 7 Apr 2026 12:20:55 -0700 Subject: [PATCH 4/9] chore: UB updates (#22308) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes the following from UB meta [issue](https://github.com/AztecProtocol/barretenberg-claude/issues/2414): - F11 (zk_sumcheck_data.hpp) — Signed 1 << n is UB when n≥31; changed to 1UL << - F10 (rom_table.cpp, ram_table.cpp, twin_rom_table.cpp, databus.cpp) — OOB vector access after context->failure() soft fail; added early returns - F9 (field_declarations.hpp, field_impl_x64.hpp) — asm_self_* functions write through const& which is UB; changed to mutable ref --- .../stdlib/primitives/databus/databus.cpp | 2 ++ .../stdlib/primitives/memory/ram_table.cpp | 4 +-- .../stdlib/primitives/memory/rom_table.cpp | 3 ++- .../primitives/memory/rom_table.test.cpp | 25 +++++++++++++++++++ .../primitives/memory/twin_rom_table.cpp | 3 +++ .../sumcheck/zk_sumcheck_data.hpp | 2 +- 6 files changed, 35 insertions(+), 4 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/databus/databus.cpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/databus/databus.cpp index 850ab550ad94..ef826e3ad6b2 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/databus/databus.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/databus/databus.cpp @@ -45,7 +45,9 @@ field_t databus::bus_vector::operator[](const field_pt& index) // Ensure the read is valid auto raw_index = static_cast(uint256_t(index.get_value()).data[0]); if (raw_index >= length) { + // Set a failure when the index is out of bounds. Return early to avoid OOB vector access. context->failure("bus_vector: access out of bounds"); + return field_pt::from_witness_index(context, context->zero_idx()); } // The read index must be a witness; if constant, add it as a constant variable diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/memory/ram_table.cpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/memory/ram_table.cpp index 1c6d3e403fc8..9d1a29e7103b 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/memory/ram_table.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/memory/ram_table.cpp @@ -180,9 +180,9 @@ template field_t ram_table::read(const fiel const auto native_index = uint256_t(index.get_value()); if (native_index >= length) { - // set a failure when the index is out of bounds. another error will be thrown when we try to call - // `read_RAM_array`. + // Set a failure when the index is out of bounds. Return early to avoid OOB vector access. context->failure("ram_table: RAM array access out of bounds"); + return field_pt::from_witness_index(context, context->zero_idx()); } if (!check_indices_initialized()) { diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/memory/rom_table.cpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/memory/rom_table.cpp index e6fbf9a5486c..6efe0cb06a56 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/memory/rom_table.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/memory/rom_table.cpp @@ -178,7 +178,9 @@ template field_t rom_table::operator[](cons const auto native_index = uint256_t(index.get_value()); if (native_index >= length) { + // Set a failure when the index is out of bounds. Return early to avoid OOB vector access. context->failure("rom_table: ROM array access out of bounds"); + return field_pt::from_witness_index(context, context->zero_idx()); } uint32_t output_idx = context->read_ROM_array(rom_id, index.get_witness_index()); @@ -188,7 +190,6 @@ template field_t rom_table::operator[](cons // If the index is legitimate, restore the tag if (native_index < length) { - element.set_origin_tag(_tags[cast_index]); } return element; diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/memory/rom_table.test.cpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/memory/rom_table.test.cpp index 30fcebc646d7..f42d8900f89d 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/memory/rom_table.test.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/memory/rom_table.test.cpp @@ -167,3 +167,28 @@ TEST(RomTable, OobConstantIndexDoesNotCrashRegression) EXPECT_TRUE(builder.failed()); } + +/** + * @brief Regression: OOB witness-index read must soft-fail without OOB vector access. + */ +TEST(RomTable, OobWitnessIndexDoesNotCrashRegression) +{ + using Builder = UltraCircuitBuilder; + using field_ct = stdlib::field_t; + using witness_ct = stdlib::witness_t; + using rom_table_ct = stdlib::rom_table; + + Builder builder; + + std::vector table_values; + table_values.emplace_back(witness_ct(&builder, bb::fr(1))); + table_values.emplace_back(witness_ct(&builder, bb::fr(2))); + table_values.emplace_back(witness_ct(&builder, bb::fr(3))); + rom_table_ct table(table_values); + + // OOB witness index — should soft-fail, not crash + field_ct oob_index = witness_ct(&builder, bb::fr(100000000)); + table[oob_index]; + + EXPECT_TRUE(builder.failed()); +} diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/memory/twin_rom_table.cpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/memory/twin_rom_table.cpp index 6788a77a3252..57c783c8ba59 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/memory/twin_rom_table.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/memory/twin_rom_table.cpp @@ -150,7 +150,10 @@ std::array, 2> twin_rom_table::operator[](const field_ initialize_table(); if (uint256_t(index.get_value()) >= length) { + // Set a failure when the index is out of bounds. Return early to avoid OOB vector access. context->failure("twin_rom_table: ROM array access out of bounds"); + return { field_pt::from_witness_index(context, context->zero_idx()), + field_pt::from_witness_index(context, context->zero_idx()) }; } auto output_indices = context->read_ROM_array_pair(rom_id, index.get_witness_index()); diff --git a/barretenberg/cpp/src/barretenberg/sumcheck/zk_sumcheck_data.hpp b/barretenberg/cpp/src/barretenberg/sumcheck/zk_sumcheck_data.hpp index 607a0d868c6f..2bea47b140da 100644 --- a/barretenberg/cpp/src/barretenberg/sumcheck/zk_sumcheck_data.hpp +++ b/barretenberg/cpp/src/barretenberg/sumcheck/zk_sumcheck_data.hpp @@ -151,7 +151,7 @@ template struct ZKSumcheckData { } total_sum *= scaling_factor; - return { total_sum + constant_term * (static_cast(1) << libra_univariates.size()), scaling_factor }; + return { total_sum + constant_term * (1UL << libra_univariates.size()), scaling_factor }; } /** From 310b348bde70b4a59954fc0b90cedcab73982137 Mon Sep 17 00:00:00 2001 From: Aztec Bot <49558828+AztecBot@users.noreply.github.com> Date: Tue, 7 Apr 2026 15:40:54 -0400 Subject: [PATCH 5/9] fix: increase test timeout for heavy recursion tests in debug build (#22347) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary Fixes nightly barretenberg debug build failure (CI run https://github.com/AztecProtocol/aztec-packages/actions/runs/24066248159). **Root cause:** `HonkRecursionConstraintTestWithoutPredicate/2.GenerateVKFromConstraints` (root rollup circuit, the heaviest test variant) timed out at 601s against the 600s default timeout. Exit code 124. **Fix:** - Bump timeout from 600s to 900s for `HonkRecursionConstraintTest` and `ChonkRecursionConstraintTest` suites - Add `ChonkRecursionConstraintTest` to the CPUS=4:MEM=8g resource group Several other tests in these suites are also close to the 600s limit (503s, 519s), so this prevents future timeouts as debug build overhead grows. **Note:** PR #22346 (from earlier session) is a no-op — the logderivative fix it applies already exists on `next` via merge-train/barretenberg (#22324). The actual failure was this timeout. ClaudeBox log: https://claudebox.work/s/5f7d3bb56d7d756f?run=1 --- barretenberg/cpp/bootstrap.sh | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/barretenberg/cpp/bootstrap.sh b/barretenberg/cpp/bootstrap.sh index 8b6168b82290..d9c1735eee83 100755 --- a/barretenberg/cpp/bootstrap.sh +++ b/barretenberg/cpp/bootstrap.sh @@ -248,9 +248,13 @@ function test_cmds_native { local prefix=$hash # A little extra resource for these tests. # IPARecursiveTests fails with 2 threads. - if [[ "$test" =~ ^(AcirAvmRecursionConstraint|ChonkKernelCapacity|AvmRecursiveTests|IPARecursiveTests|HonkRecursionConstraintTest) ]]; then + if [[ "$test" =~ ^(AcirAvmRecursionConstraint|ChonkKernelCapacity|AvmRecursiveTests|IPARecursiveTests|HonkRecursionConstraintTest|ChonkRecursionConstraintTest) ]]; then prefix="$prefix:CPUS=4:MEM=8g" fi + # These tests routinely take 400-600s in debug builds; bump from the 600s default. + if [[ "$test" =~ ^(HonkRecursionConstraintTest|ChonkRecursionConstraintTest) ]]; then + prefix="$prefix:TIMEOUT=900s" + fi echo -e "$prefix barretenberg/cpp/scripts/run_test.sh $bin_name $test" done || (echo "Failed to list tests in $bin" && exit 1) done From 1248b0659932d6254c9adbff83f59a002d0bab20 Mon Sep 17 00:00:00 2001 From: ledwards2225 <98505400+ledwards2225@users.noreply.github.com> Date: Tue, 7 Apr 2026 14:26:17 -0700 Subject: [PATCH 6/9] chore: origin tag updates (#22307) Addresses missing origin tags identified in the meta issue [here](https://github.com/AztecProtocol/barretenberg-claude/issues/2456). These were all about adding tagging that was missing. Some of them led to failures once proper tests were added, others aren't triggered in current usage. --- .../stdlib/primitives/databus/databus.cpp | 14 ++++++- .../stdlib/primitives/databus/databus.hpp | 1 + .../stdlib/primitives/field/field.hpp | 1 + .../stdlib/primitives/field/field.test.cpp | 8 ++++ .../primitives/field/field_conversion.hpp | 4 +- .../stdlib/primitives/logic/logic.test.cpp | 37 +++++++++++++++++++ .../stdlib/primitives/plookup/plookup.cpp | 4 ++ 7 files changed, 67 insertions(+), 2 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/databus/databus.cpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/databus/databus.cpp index ef826e3ad6b2..549b1383c97d 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/databus/databus.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/databus/databus.cpp @@ -36,6 +36,12 @@ void databus::bus_vector::set_values(const std::vector& entri context->append_to_bus_vector(bus_idx, entries.back().get_witness_index()); } length = entries.size(); + + // Preserve tags to restore them in future reads (following the ROM/RAM pattern) + _tags.resize(entries_in.size()); + for (size_t i = 0; i < length; ++i) { + _tags[i] = entries_in[i].get_origin_tag(); + } } template @@ -60,7 +66,13 @@ field_t databus::bus_vector::operator[](const field_pt& index) // Read from the bus vector at the specified index. Creates a single read gate uint32_t output_idx = context->read_bus_vector(bus_idx, index_witness_idx); - return field_pt::from_witness_index(context, output_idx); + auto result = field_pt::from_witness_index(context, output_idx); + + // If the index is legitimate, restore the tag (following the ROM/RAM pattern) + if (raw_index < length) { + result.set_origin_tag(_tags[raw_index]); + } + return result; } template class databus; diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/databus/databus.hpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/databus/databus.hpp index d3ce5fa20593..92e6f3bdaa32 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/databus/databus.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/databus/databus.hpp @@ -52,6 +52,7 @@ template class databus { private: mutable std::vector entries; // bus vector entries + std::vector _tags; // origin tags for each entry (restored on read) size_t length = 0; BusId bus_idx; // Idx of column in bus mutable Builder* context = nullptr; diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/field/field.hpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/field/field.hpp index fa25cca7e73e..dc2890ee1bcf 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/field/field.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/field/field.hpp @@ -565,6 +565,7 @@ template class field_t { bool predicate_witness = uint256_t(a.get_value()) < uint256_t(b.get_value()); bool_t predicate(witness_t(ctx, predicate_witness)); + predicate.set_origin_tag(OriginTag(a.get_origin_tag(), b.get_origin_tag())); field_t predicate_valid = b.add_two(-(a) + range_constant - 1, -field_t(predicate) * range_constant); predicate_valid.create_range_constraint(num_bits); return predicate; diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/field/field.test.cpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/field/field.test.cpp index 0c93fd4d2b99..2d4a13800a60 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/field/field.test.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/field/field.test.cpp @@ -1435,6 +1435,14 @@ template class stdlib_field : public testing::Test { #ifndef NDEBUG EXPECT_THROW(q + q, std::runtime_error); #endif + + // ranged_less_than: check tag behavior + auto rlt_a = field_ct(witness_ct(&builder, uint256_t(50))); + auto rlt_b = field_ct(witness_ct(&builder, uint256_t(100))); + rlt_a.set_origin_tag(submitted_value_origin_tag); + rlt_b.set_origin_tag(challenge_origin_tag); + auto rlt_result = rlt_a.template ranged_less_than<8>(rlt_b); + EXPECT_EQ(rlt_result.get_origin_tag(), first_two_merged_tag); } void test_validate_context() diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/field/field_conversion.hpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/field/field_conversion.hpp index dd9d274508c7..6ae8a9f9d44c 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/field/field_conversion.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/field/field_conversion.hpp @@ -54,7 +54,9 @@ template class StdlibCodec { // All challenges must be circuit witnesses. BB_ASSERT(builder); BB_ASSERT(!challenge.is_constant()); - return T(challenge, fr::from_witness_index(builder, builder->zero_idx())); + auto high_limb = fr::from_witness_index(builder, builder->zero_idx()); + high_limb.set_origin_tag(challenge.get_origin_tag()); + return T(challenge, high_limb); } } diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/logic/logic.test.cpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/logic/logic.test.cpp index 1cbf428468d1..6d8bb4e2a923 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/logic/logic.test.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/logic/logic.test.cpp @@ -144,6 +144,43 @@ TYPED_TEST(LogicTest, DifferentWitnessSameResult) EXPECT_EQ(result, false); } +TYPED_TEST(LogicTest, OriginTagConsistency) +{ + STDLIB_TYPE_ALIASES + auto builder = Builder(); + + const size_t parent_id = 0; + const auto tag_a = OriginTag(parent_id, /*round_id=*/0, /*is_submitted=*/true); + const auto tag_b = OriginTag(parent_id, /*round_id=*/0, /*is_submitted=*/false); + const auto merged_tag = OriginTag(tag_a, tag_b); + + uint256_t a_val = 0x0f; + uint256_t b_val = 0xa3; + + // Witness-witness path + field_ct x = witness_ct(&builder, a_val); + field_ct y = witness_ct(&builder, b_val); + x.set_origin_tag(tag_a); + y.set_origin_tag(tag_b); + field_ct and_result = stdlib::logic::create_logic_constraint(x, y, 8, false); + EXPECT_EQ(and_result.get_origin_tag(), merged_tag); + + field_ct xor_result = stdlib::logic::create_logic_constraint(x, y, 8, true); + EXPECT_EQ(xor_result.get_origin_tag(), merged_tag); + + // Constant-witness path (left constant) + field_ct x_const(&builder, a_val); + x_const.set_origin_tag(tag_a); + field_ct and_result_lc = stdlib::logic::create_logic_constraint(x_const, y, 8, false); + EXPECT_EQ(and_result_lc.get_origin_tag(), merged_tag); + + // Constant-witness path (right constant) + field_ct y_const(&builder, b_val); + y_const.set_origin_tag(tag_b); + field_ct and_result_rc = stdlib::logic::create_logic_constraint(x, y_const, 8, false); + EXPECT_EQ(and_result_rc.get_origin_tag(), merged_tag); +} + // Regression test: OriginTag must propagate through all logic constraint paths. TYPED_TEST(LogicTest, OriginTagPropagation) { diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/plookup/plookup.cpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/plookup/plookup.cpp index 057e9f056e9e..22d6781a251f 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/plookup/plookup.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/plookup/plookup.cpp @@ -54,6 +54,7 @@ plookup::ReadData> plookup_read::get_lookup_accumulato const auto accumulator_witnesses = ctx->create_gates_from_plookup_accumulators(id, lookup_data, lhs_index, key_b_witness); + const auto merged_tag = OriginTag(key_a.get_origin_tag(), key_b.get_origin_tag()); for (size_t i = 0; i < lookup_data[ColumnIdx::C1].size(); ++i) { lookup[ColumnIdx::C1].emplace_back( field_t::from_witness_index(ctx, accumulator_witnesses[ColumnIdx::C1][i])); @@ -61,6 +62,9 @@ plookup::ReadData> plookup_read::get_lookup_accumulato field_t::from_witness_index(ctx, accumulator_witnesses[ColumnIdx::C2][i])); lookup[ColumnIdx::C3].emplace_back( field_t::from_witness_index(ctx, accumulator_witnesses[ColumnIdx::C3][i])); + lookup[ColumnIdx::C1].back().set_origin_tag(merged_tag); + lookup[ColumnIdx::C2].back().set_origin_tag(merged_tag); + lookup[ColumnIdx::C3].back().set_origin_tag(merged_tag); } } return lookup; From 920122bfa3b93ead9e0ed8978feb5be8a940dafd Mon Sep 17 00:00:00 2001 From: Raju Krishnamoorthy Date: Wed, 8 Apr 2026 11:36:39 +0200 Subject: [PATCH 7/9] =?UTF-8?q?chore:=20ECCVM=20QoL=20=E2=80=94=20operator?= =?UTF-8?q?=20precedence=20fix,=20assert=20correctness,=20documentation=20?= =?UTF-8?q?(#22351)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary Small quality-of-life improvements for ECCVM code clarity and correctness: - Fix operator precedence in `skip_entire_row` — a prover-side optimization where a misplaced parenthesis meant `lagrange_last[idx+1]` wasn't compared to 0. Hidden by the z_perm check that precedes it. - Fix `BB_ASSERT` → `BB_ASSERT_EQ` in precomputed_tables_builder — the assertion checked truthiness instead of equality, making it a no-op. Builder-only, no constraint impact. - Document `REPEATED_COMMITMENTS` offset convention, add `static_assert` pins and runtime tests (`RepeatedCommitmentsIndicesCorrect`) to catch entity layout drift. No behavioral change. ## Test plan - [x] `eccvm_tests` — 43/43 passed - [x] `goblin_tests` — 39/39 passed (1 pre-existing skip) - [x] `translator_vm_tests` — 48/48 passed - [x] `dsl_tests` — all passed --------- Co-authored-by: notnotraju --- .../commitment_schemes/shplonk/shplemini.hpp | 17 ++---- .../cpp/src/barretenberg/eccvm/eccvm.test.cpp | 39 ++++++++++++++ .../src/barretenberg/eccvm/eccvm_flavor.hpp | 30 +++++++++-- .../eccvm/precomputed_tables_builder.hpp | 2 +- .../translator_vm/translator.test.cpp | 54 +++++++++++++++++++ .../ultra_honk/mega_honk.test.cpp | 43 +++++++++++++++ .../ultra_honk/ultra_honk.test.cpp | 43 +++++++++++++++ 7 files changed, 210 insertions(+), 18 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/commitment_schemes/shplonk/shplemini.hpp b/barretenberg/cpp/src/barretenberg/commitment_schemes/shplonk/shplemini.hpp index 7f01ea9f37ee..654c099be139 100644 --- a/barretenberg/cpp/src/barretenberg/commitment_schemes/shplonk/shplemini.hpp +++ b/barretenberg/cpp/src/barretenberg/commitment_schemes/shplonk/shplemini.hpp @@ -517,19 +517,12 @@ template class ShpleminiVerifier_ { } // Erase the duplicate entries (higher-index range first to preserve lower indices) - auto erase_range = [&](size_t duplicate_start, size_t original_start, size_t count) { + // Each erase shifts elements down, so duplicate_start always points to the next duplicate; + // the original at original_start + i is unaffected since we erase higher-index ranges first. + // Commitment equality (original == duplicate) is verified by per-flavor + // RepeatedCommitmentsIndicesCorrect tests rather than at runtime here. + auto erase_range = [&](size_t duplicate_start, [[maybe_unused]] size_t original_start, size_t count) { for (size_t i = 0; i < count; ++i) { - // Verify the commitment being erased matches its original (native only). - // Each erase shifts elements down, so duplicate_start always points to the - // next duplicate; the original at original_start + i is unaffected since - // we erase higher-index ranges first. - if constexpr (!Curve::is_stdlib_type) { - if (commitments[duplicate_start] != commitments[original_start + i]) { - throw_or_abort("remove_repeated_commitments: commitment mismatch at duplicate index " + - std::to_string(duplicate_start) + " vs original index " + - std::to_string(original_start + i)); - } - } scalars.erase(scalars.begin() + static_cast(duplicate_start)); commitments.erase(commitments.begin() + static_cast(duplicate_start)); } diff --git a/barretenberg/cpp/src/barretenberg/eccvm/eccvm.test.cpp b/barretenberg/cpp/src/barretenberg/eccvm/eccvm.test.cpp index 6be40ddd3d63..ee42bbabd9d6 100644 --- a/barretenberg/cpp/src/barretenberg/eccvm/eccvm.test.cpp +++ b/barretenberg/cpp/src/barretenberg/eccvm/eccvm.test.cpp @@ -559,3 +559,42 @@ TEST_F(ECCVMTests, MaskingTailCommitments) EXPECT_NE(naive, structured.wire_comms[i]) << "Wire " << i << " commitment should be masked"; } } + +/** + * @brief Verify that REPEATED_COMMITMENTS indices correctly pair to-be-shifted and shifted commitments. + * @details Mirrors the Shplemini commitment vector construction: [Q, unshifted_comms..., to_be_shifted_comms...], + * then applies offset = HasZK ? 2 : 1, and checks commitments[original_start + offset + i] == + * commitments[duplicate_start + offset + i]. This is a one-time substitute for the runtime BB_ASSERTs + * in remove_repeated_commitments. + */ +TEST_F(ECCVMTests, RepeatedCommitmentsIndicesCorrect) +{ + ECCVMCircuitBuilder builder = generate_circuit(&engine); + auto pk = std::make_shared(builder); + pk->commitment_key = ECCVMFlavor::CommitmentKey(pk->circuit_size); + + auto unshifted = pk->polynomials.get_unshifted(); + auto to_be_shifted = pk->polynomials.get_to_be_shifted(); + + constexpr auto repeated = ECCVMFlavor::REPEATED_COMMITMENTS; + ASSERT_EQ(to_be_shifted.size(), repeated.first.count); + + // Build the commitment vector exactly as Shplemini does: [Q, unshifted..., to_be_shifted...] + const auto& ck = pk->commitment_key; + std::vector commitments; + commitments.push_back(ECCVMFlavor::Commitment::one()); // dummy Q + for (auto& poly : unshifted) { + commitments.push_back(ck.commit(poly)); + } + for (auto& poly : to_be_shifted) { + commitments.push_back(ck.commit(poly)); + } + + // Same offset logic as remove_repeated_commitments + constexpr size_t offset = ECCVMFlavor::HasZK ? 2 : 1; + for (size_t i = 0; i < repeated.first.count; i++) { + EXPECT_EQ(commitments[repeated.first.original_start + offset + i], + commitments[repeated.first.duplicate_start + offset + i]) + << "REPEATED_COMMITMENTS commitment mismatch at index " << i; + } +} diff --git a/barretenberg/cpp/src/barretenberg/eccvm/eccvm_flavor.hpp b/barretenberg/cpp/src/barretenberg/eccvm/eccvm_flavor.hpp index d518e24aa6a1..0a59e35ba7df 100644 --- a/barretenberg/cpp/src/barretenberg/eccvm/eccvm_flavor.hpp +++ b/barretenberg/cpp/src/barretenberg/eccvm/eccvm_flavor.hpp @@ -86,14 +86,34 @@ class ECCVMFlavor { static constexpr size_t NUM_SHIFTED_ENTITIES = 26; // The number of entities in DerivedWitnessEntities that are not going to be shifted. static constexpr size_t NUM_DERIVED_WITNESS_ENTITIES_NON_SHIFTED = 1; - // A container to be fed to ShpleminiVerifier to avoid redundant scalar muls, the first number is the index of the - // first witness to be shifted. + // Indices into the Shplemini commitments vector that identify which "to-be-shifted" witness commitments in the + // unshifted block are duplicated in the shifted block, so their scalar muls can be merged. + // + // Shplemini's remove_repeated_commitments uses offset = HasZK ? 2 : 1. For ECCVM (HasZK=true), offset=2 + // accounts for the Shplonk:Q commitment and the gemini_masking_poly that precede the Precomputed+Witness + // block in the commitments vector. The indices below are therefore relative to the start of + // {PrecomputedEntities + WitnessEntities} (i.e. they exclude MaskingEntities, which is covered by the offset). + // + // original_start: index of the first to-be-shifted entity within {Precomputed + Witness} + // = NUM_PRECOMPUTED + NUM_WIRE_NON_SHIFTED (= NUM_WITNESS - NUM_DERIVED_NON_SHIFTED - NUM_SHIFTED) + // duplicate_start: index where the shifted copies begin = NUM_PRECOMPUTED + NUM_WITNESS + static constexpr size_t NUM_WIRE_NON_SHIFTED = + NUM_WITNESS_ENTITIES - NUM_DERIVED_WITNESS_ENTITIES_NON_SHIFTED - NUM_SHIFTED_ENTITIES; static constexpr RepeatedCommitmentsData REPEATED_COMMITMENTS = - RepeatedCommitmentsData(NUM_PRECOMPUTED_ENTITIES + NUM_WITNESS_ENTITIES - - NUM_DERIVED_WITNESS_ENTITIES_NON_SHIFTED - NUM_SHIFTED_ENTITIES, + RepeatedCommitmentsData(NUM_PRECOMPUTED_ENTITIES + NUM_WIRE_NON_SHIFTED, NUM_PRECOMPUTED_ENTITIES + NUM_WITNESS_ENTITIES, NUM_SHIFTED_ENTITIES); + // Pin entity counts and REPEATED_COMMITMENTS indices so that any layout change triggers a compile error. + // The to-be-shifted witnesses must form a contiguous block starting at NUM_WIRE_NON_SHIFTED within WitnessEntities. + static_assert(NUM_WIRE_NON_SHIFTED == 60, "WireNonShiftedEntities size changed — update REPEATED_COMMITMENTS"); + static_assert(NUM_MASKING_POLYNOMIALS == 1, "MaskingEntities size changed — review REPEATED_COMMITMENTS offset"); + static_assert(REPEATED_COMMITMENTS.first.original_start == 64, + "REPEATED_COMMITMENTS original_start changed — verify Shplemini offset convention"); + static_assert(REPEATED_COMMITMENTS.first.duplicate_start == 91, + "REPEATED_COMMITMENTS duplicate_start changed — verify Shplemini offset convention"); + static_assert(REPEATED_COMMITMENTS.first.count == 26, "REPEATED_COMMITMENTS count changed"); + using GrandProductRelations = std::tuple>; // define the tuple of Relations that comprise the Sumcheck relation template @@ -1029,7 +1049,7 @@ class ECCVMFlavor { // 4: We also force that `transcript_op==0`. return (polynomials.z_perm[edge_idx] == polynomials.z_perm_shift[edge_idx]) && (polynomials.z_perm[edge_idx + 1] == polynomials.z_perm_shift[edge_idx + 1]) && - (polynomials.lagrange_last[edge_idx] == 0 && polynomials.lagrange_last[edge_idx + 1]) == 0 && + (polynomials.lagrange_last[edge_idx] == 0 && polynomials.lagrange_last[edge_idx + 1] == 0) && (polynomials.msm_transition[edge_idx] == 0 && polynomials.msm_transition[edge_idx + 1] == 0) && (polynomials.transcript_mul[edge_idx] == 0 && polynomials.transcript_mul[edge_idx + 1] == 0) && (polynomials.transcript_op[edge_idx] == 0 && polynomials.transcript_op[edge_idx + 1] == 0); diff --git a/barretenberg/cpp/src/barretenberg/eccvm/precomputed_tables_builder.hpp b/barretenberg/cpp/src/barretenberg/eccvm/precomputed_tables_builder.hpp index 1057d1506af4..fc5ba7341f00 100644 --- a/barretenberg/cpp/src/barretenberg/eccvm/precomputed_tables_builder.hpp +++ b/barretenberg/cpp/src/barretenberg/eccvm/precomputed_tables_builder.hpp @@ -116,7 +116,7 @@ class ECCVMPointTablePrecomputationBuilder { row.pc = entry.pc; if (last_row) { - BB_ASSERT(scalar_sum - entry.wnaf_skew, entry.scalar); + BB_ASSERT_EQ(scalar_sum - entry.wnaf_skew, entry.scalar); } // the last element of the `precomputed_table` field of a `ScalarMul` is the double of the point. row.precompute_double = entry.precomputed_table[bb::eccvm::POINT_TABLE_SIZE]; diff --git a/barretenberg/cpp/src/barretenberg/translator_vm/translator.test.cpp b/barretenberg/cpp/src/barretenberg/translator_vm/translator.test.cpp index 99a1c980722b..460167dec556 100644 --- a/barretenberg/cpp/src/barretenberg/translator_vm/translator.test.cpp +++ b/barretenberg/cpp/src/barretenberg/translator_vm/translator.test.cpp @@ -517,6 +517,60 @@ TEST_F(TranslatorTests, EvaluationPartition) * @details Start from all-zeros, call set_minicircuit_evaluations + complete_full_circuit_evaluations * with random inputs, and check that no entity remains zero (with overwhelming probability). */ +/** + * @brief Verify that REPEATED_COMMITMENTS indices correctly pair to-be-shifted and shifted commitments. + * @details The Translator has two duplicate ranges and uses get_pcs_unshifted()/get_pcs_to_be_shifted() + * instead of the standard get_unshifted()/get_to_be_shifted(). This test commits to all PCS-level polynomials + * and verifies the commitments at original and duplicate positions match. + */ +TEST_F(TranslatorTests, RepeatedCommitmentsIndicesCorrect) +{ + using Flavor = TranslatorFlavor; + using Commitment = Flavor::Commitment; + + fq batching_challenge_v = fq::random_element(); + fq evaluation_challenge_x = fq::random_element(); + CircuitBuilder circuit_builder = generate_test_circuit(batching_challenge_v, evaluation_challenge_x); + auto pk = std::make_shared(circuit_builder); + + pk->proving_key->commitment_key = Flavor::CommitmentKey(pk->proving_key->circuit_size); + + auto pcs_unshifted = pk->proving_key->polynomials.get_pcs_unshifted(); + auto pcs_to_be_shifted = pk->proving_key->polynomials.get_pcs_to_be_shifted(); + + // Commit to all PCS polynomials + const auto& ck = pk->proving_key->commitment_key; + std::vector unshifted_comms; + for (auto& poly : pcs_unshifted) { + unshifted_comms.push_back(ck.commit(poly)); + } + std::vector shifted_comms; + for (auto& poly : pcs_to_be_shifted) { + shifted_comms.push_back(ck.commit(poly)); + } + + // Build the commitment vector exactly as Shplemini does: [Q, pcs_unshifted..., pcs_to_be_shifted...] + std::vector commitments; + commitments.push_back(Commitment::one()); // dummy Q + commitments.insert(commitments.end(), unshifted_comms.begin(), unshifted_comms.end()); + commitments.insert(commitments.end(), shifted_comms.begin(), shifted_comms.end()); + + constexpr auto repeated = Flavor::REPEATED_COMMITMENTS; + // Same offset logic as remove_repeated_commitments + constexpr size_t offset = Flavor::HasZK ? 2 : 1; + + // Verify both ranges using the same indexing as remove_repeated_commitments + auto check_range = [&](const auto& range, const std::string& label) { + for (size_t i = 0; i < range.count; i++) { + EXPECT_EQ(commitments[range.original_start + offset + i], commitments[range.duplicate_start + offset + i]) + << label << " commitment mismatch at index " << i; + } + }; + + check_range(repeated.first, "Range 1"); + check_range(repeated.second, "Range 2"); +} + TEST_F(TranslatorTests, VerifierPopulatesAllEntities) { using Flavor = TranslatorFlavor; 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 3c9b29be4d25..e924778a594c 100644 --- a/barretenberg/cpp/src/barretenberg/ultra_honk/mega_honk.test.cpp +++ b/barretenberg/cpp/src/barretenberg/ultra_honk/mega_honk.test.cpp @@ -336,3 +336,46 @@ TYPED_TEST(MegaHonkTests, MaskingTailCommitments) } } } + +/** + * @brief Verify that REPEATED_COMMITMENTS indices correctly pair to-be-shifted and shifted commitments. + * @details Mirrors the Shplemini vector construction: [Q, unshifted..., to_be_shifted...] with + * offset = HasZK ? 2 : 1, then checks the same index pairs that remove_repeated_commitments asserts. + */ +TYPED_TEST(MegaHonkTests, RepeatedCommitmentsIndicesCorrect) +{ + using Flavor = TypeParam; + using Builder = typename Flavor::CircuitBuilder; + using DefaultIO = stdlib::recursion::honk::DefaultIO; + using CommitmentKey = typename Flavor::CommitmentKey; + using Commitment = typename Flavor::Commitment; + + auto builder = Builder{}; + DefaultIO::add_default(builder); + auto prover_instance = std::make_shared>(builder); + CommitmentKey ck(prover_instance->dyadic_size()); + + auto unshifted = prover_instance->polynomials.get_unshifted(); + auto to_be_shifted = prover_instance->polynomials.get_to_be_shifted(); + + constexpr auto repeated = Flavor::REPEATED_COMMITMENTS; + ASSERT_EQ(to_be_shifted.size(), repeated.first.count); + + // Build the commitment vector exactly as Shplemini does: [Q, unshifted..., to_be_shifted...] + std::vector commitments; + commitments.push_back(Commitment::one()); // dummy Q + for (auto& poly : unshifted) { + commitments.push_back(ck.commit(poly)); + } + for (auto& poly : to_be_shifted) { + commitments.push_back(ck.commit(poly)); + } + + // Same offset logic as remove_repeated_commitments + constexpr size_t offset = Flavor::HasZK ? 2 : 1; + for (size_t i = 0; i < repeated.first.count; i++) { + EXPECT_EQ(commitments[repeated.first.original_start + offset + i], + commitments[repeated.first.duplicate_start + offset + i]) + << "REPEATED_COMMITMENTS commitment mismatch at index " << i; + } +} 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 9841f13f488a..9f2903f835e3 100644 --- a/barretenberg/cpp/src/barretenberg/ultra_honk/ultra_honk.test.cpp +++ b/barretenberg/cpp/src/barretenberg/ultra_honk/ultra_honk.test.cpp @@ -527,3 +527,46 @@ TYPED_TEST(UltraHonkTests, MaskingTailCommitments) } } } + +/** + * @brief Verify that REPEATED_COMMITMENTS indices correctly pair to-be-shifted and shifted commitments. + * @details Mirrors the Shplemini vector construction: [Q, unshifted..., to_be_shifted...] with + * offset = HasZK ? 2 : 1, then checks the same index pairs that remove_repeated_commitments asserts. + */ +TYPED_TEST(UltraHonkTests, RepeatedCommitmentsIndicesCorrect) +{ + using Flavor = TypeParam; + using Builder = typename Flavor::CircuitBuilder; + using IO = typename TestFixture::IO; + using CommitmentKey = typename Flavor::CommitmentKey; + using Commitment = typename Flavor::Commitment; + + auto builder = Builder{}; + IO::add_default(builder); + auto prover_instance = std::make_shared>(builder); + CommitmentKey ck(prover_instance->dyadic_size()); + + auto unshifted = prover_instance->polynomials.get_unshifted(); + auto to_be_shifted = prover_instance->polynomials.get_to_be_shifted(); + + constexpr auto repeated = Flavor::REPEATED_COMMITMENTS; + ASSERT_EQ(to_be_shifted.size(), repeated.first.count); + + // Build the commitment vector exactly as Shplemini does: [Q, unshifted..., to_be_shifted...] + std::vector commitments; + commitments.push_back(Commitment::one()); // dummy Q + for (auto& poly : unshifted) { + commitments.push_back(ck.commit(poly)); + } + for (auto& poly : to_be_shifted) { + commitments.push_back(ck.commit(poly)); + } + + // Same offset logic as remove_repeated_commitments + constexpr size_t offset = Flavor::HasZK ? 2 : 1; + for (size_t i = 0; i < repeated.first.count; i++) { + EXPECT_EQ(commitments[repeated.first.original_start + offset + i], + commitments[repeated.first.duplicate_start + offset + i]) + << "REPEATED_COMMITMENTS commitment mismatch at index " << i; + } +} From 564da2b83372a35ebb0916cfa8769397b9020df8 Mon Sep 17 00:00:00 2001 From: Suyash Bagad Date: Wed, 8 Apr 2026 18:26:50 +0200 Subject: [PATCH 8/9] fix: minor bigfield fixes - take 2 (#22415) List of fixes (none of them should change circuits): - Adds a missing reconstruction constraint in `decompose_non_native_field_double_width_limb` to ensure the reconstructed value matches the original input. - Asserts `r_lo` is nonzero in `validate_split_in_field_unsafe`, closing a soundness gap where a zero low limb could pass unchecked. - Removes unreachable dead code in the carry bit computation path. - Changes `batch_mul` default to `with_edgecases=true` to avoid incorrect results on edge-case inputs. - Adds boundary tests for the byte array constructor. resolves https://github.com/AztecProtocol/barretenberg-claude/issues/2433 --- .../commitment_schemes/kzg/kzg.hpp | 5 +- .../stdlib/encryption/ecdsa/ecdsa_impl.hpp | 3 +- .../primitives/bigfield/bigfield.test.cpp | 55 +++++++++++++++++++ .../primitives/bigfield/bigfield_impl.hpp | 26 +++------ .../stdlib/primitives/biggroup/biggroup.hpp | 2 +- .../primitives/biggroup/biggroup.test.cpp | 5 +- .../primitives/biggroup/biggroup_impl.hpp | 2 +- .../stdlib/primitives/field/field_utils.cpp | 5 ++ .../stdlib/primitives/field/field_utils.hpp | 3 + 9 files changed, 80 insertions(+), 26 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/commitment_schemes/kzg/kzg.hpp b/barretenberg/cpp/src/barretenberg/commitment_schemes/kzg/kzg.hpp index 81e93b837ff6..0da24c2cc41f 100644 --- a/barretenberg/cpp/src/barretenberg/commitment_schemes/kzg/kzg.hpp +++ b/barretenberg/cpp/src/barretenberg/commitment_schemes/kzg/kzg.hpp @@ -91,7 +91,10 @@ template class KZG { quotient_commitment, GroupElement::one(builder) }; std::vector scalars = { one, claim.opening_pair.challenge, -claim.opening_pair.evaluation }; - P_0 = GroupElement::batch_mul(commitments, scalars); + + // Compute C + r*[W]_1 + (-v)*[1]_1 as a batch_mul, no need of edge case handling since we don't expect the + // points to be linearly dependent. + P_0 = GroupElement::batch_mul(commitments, scalars, /*max_num_bits=*/0, /*with_edgecases=*/false); } else { P_0 = claim.commitment; diff --git a/barretenberg/cpp/src/barretenberg/stdlib/encryption/ecdsa/ecdsa_impl.hpp b/barretenberg/cpp/src/barretenberg/stdlib/encryption/ecdsa/ecdsa_impl.hpp index c5d9190c810a..b3a18f9db4ef 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/encryption/ecdsa/ecdsa_impl.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/encryption/ecdsa/ecdsa_impl.hpp @@ -134,7 +134,8 @@ bool_t ecdsa_verify_signature(const stdlib::byte_array& hashed if ((corrected_public_key.get_value().x == Curve::GroupNative::affine_one.x) && (!builder->failed())) { builder->failure("ECDSA input validation: the public key is equal to plus or minus the generator point."); } - result = G1::batch_mul({ G1::one(builder), corrected_public_key }, { u1, u2 }); + result = G1::batch_mul( + { G1::one(builder), corrected_public_key }, { u1, u2 }, /*max_num_bits=*/0, /*with_edgecases=*/false); } // Step 7. diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/bigfield/bigfield.test.cpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/bigfield/bigfield.test.cpp index 991a13e2945a..b488aada59d6 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/bigfield/bigfield.test.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/bigfield/bigfield.test.cpp @@ -1472,6 +1472,57 @@ template class stdlib_bigfield : public testing::Test { EXPECT_EQ(result, true); } + // Test byte_array constructor with boundary values: 0, 1, p-1, and values >= p. + static void test_byte_array_constructor_boundary_values() + { + // Helper: convert a uint256_t to a 32-byte big-endian vector (same layout as fq_native::serialize_to_buffer) + const auto to_bytes = [](uint256_t v) { + std::vector buf(32, 0); + for (int i = 31; i >= 0; --i) { + buf[static_cast(i)] = static_cast(v.data[0] & 0xff); + v >>= 8; + } + return buf; + }; + + const uint256_t p = fq_ct::modulus; + + struct TestCase { + uint256_t value; + bool expect_less_than_p; // whether value < p + }; + + // Boundary values: 0, 1, p-1 (all valid); p (invalid — equals modulus) + // Nibble-boundary values: 0x0f...f (all low nibbles set), 0x10...0 (carry into high nibble) + const std::vector cases = { + { uint256_t(0), true }, + { uint256_t(1), true }, + { p - 1, true }, + // Nibble boundary: value whose split byte is 0x0f (lo nibble=0xf, hi nibble=0) + { uint256_t(0x0f) << (7 * 8), true }, // byte 7 from the right = overlap byte for limb0/limb1 + // Nibble boundary: value whose split byte is 0xf0 (lo nibble=0, hi nibble=0xf) + { uint256_t(0xf0) << (7 * 8), true }, + // p itself — a bigfield constructed from p bytes will have value == p, which is >= p + { p, false }, + }; + + for (const auto& tc : cases) { + auto builder = Builder(); + byte_array_ct arr(&builder, to_bytes(tc.value)); + fq_ct el(arr); + + // Check that the reconstructed value matches + EXPECT_EQ(el.get_value().lo, tc.value); + + // Verify is_less_than agrees with our expectation + auto cmp = el.is_less_than(p); + cmp.assert_equal(stdlib::bool_t(tc.expect_less_than_p)); + + EXPECT_TRUE(CircuitChecker::check(builder)); + EXPECT_FALSE(builder.failed()); + } + } + // This check tests if elements are reduced to fit quotient into range proof static void test_quotient_completeness() { @@ -2508,6 +2559,10 @@ TYPED_TEST(stdlib_bigfield, byte_array_constructors) { TestFixture::test_byte_array_constructors(); } +TYPED_TEST(stdlib_bigfield, byte_array_constructor_boundary_values) +{ + TestFixture::test_byte_array_constructor_boundary_values(); +} TYPED_TEST(stdlib_bigfield, quotient_completeness_regression) { TestFixture::test_quotient_completeness(); diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/bigfield/bigfield_impl.hpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/bigfield/bigfield_impl.hpp index 73f910b64290..cb5ee16bb524 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/bigfield/bigfield_impl.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/bigfield/bigfield_impl.hpp @@ -70,7 +70,6 @@ bigfield::bigfield(const field_t& low_bits_in, decompose_non_native_field_double_width_limb(context, low_bits_in.get_witness_index()); limb_0.witness_index = limb_witnesses[0]; limb_1.witness_index = limb_witnesses[1]; - field_t::evaluate_linear_identity(low_bits_in, -limb_0, -limb_1 * shift_1, field_t(0)); } else { uint256_t slice_0 = uint256_t(low_bits_in.additive_constant).slice(0, NUM_LIMB_BITS); uint256_t slice_1 = uint256_t(low_bits_in.additive_constant).slice(NUM_LIMB_BITS, 2 * NUM_LIMB_BITS); @@ -97,7 +96,6 @@ bigfield::bigfield(const field_t& low_bits_in, context, high_bits_in.get_witness_index(), static_cast(num_high_limb_bits)); limb_2.witness_index = limb_witnesses[0]; limb_3.witness_index = limb_witnesses[1]; - field_t::evaluate_linear_identity(high_bits_in, -limb_2, -limb_3 * shift_1, field_t(0)); } else { uint256_t slice_2 = uint256_t(high_bits_in.additive_constant).slice(0, NUM_LIMB_BITS); uint256_t slice_3 = uint256_t(high_bits_in.additive_constant).slice(NUM_LIMB_BITS, num_high_limb_bits); @@ -2263,13 +2261,6 @@ void bigfield::unsafe_evaluate_multiply_add(const bigfield& input_le uint64_t carry_lo_msb = max_lo_bits - (2 * NUM_LIMB_BITS); uint64_t carry_hi_msb = max_hi_bits - (2 * NUM_LIMB_BITS); - if (max_lo_bits < (2 * NUM_LIMB_BITS)) { - carry_lo_msb = 0; - } - if (max_hi_bits < (2 * NUM_LIMB_BITS)) { - carry_hi_msb = 0; - } - // The custom bigfield multiplication gate requires inputs are witnesses. // If we're using constant values, instantiate them as circuit variables // @@ -2717,13 +2708,6 @@ void bigfield::unsafe_evaluate_multiple_multiply_add(const std::vect uint64_t carry_lo_msb = max_lo_bits - (2 * NUM_LIMB_BITS); uint64_t carry_hi_msb = max_hi_bits - (2 * NUM_LIMB_BITS); - if (max_lo_bits < (2 * NUM_LIMB_BITS)) { - carry_lo_msb = 0; - } - if (max_hi_bits < (2 * NUM_LIMB_BITS)) { - carry_hi_msb = 0; - } - // if both the hi and lo output limbs have less than 70 bits, we can use our custom // limb accumulation gate (accumulates 2 field elements, each composed of 5 14-bit limbs, in 3 gates) if (carry_lo_msb <= 70 && carry_hi_msb <= 70) { @@ -2863,9 +2847,7 @@ std::pair bigfield::compute_partial_schoolbook /** * @brief Decompose a single witness into two limbs, range constrained to NUM_LIMB_BITS (68) and - * num_limb_bits - NUM_LIMB_BITS, respectively. - * - * @details Doesn't create gates constraining the limbs to each other. + * num_limb_bits - NUM_LIMB_BITS, respectively, and constrain low + hi * 2^NUM_LIMB_BITS == original. * * @param ctx The circuit context * @param limb_idx The index of the limb that will be decomposed @@ -2893,6 +2875,12 @@ std::array bigfield::decompose_non_native_field_double_ ctx->range_constrain_two_limbs( low_idx, hi_idx, lo_bits, hi_bits, "decompose_non_native_field_double_width_limb: limbs too large"); + // Constrain: original == low + hi * 2^NUM_LIMB_BITS + field_t original = field_t::from_witness_index(ctx, limb_idx); + field_t lo_field = field_t::from_witness_index(ctx, low_idx); + field_t hi_field = field_t::from_witness_index(ctx, hi_idx); + field_t::evaluate_linear_identity(original, -lo_field, -hi_field * shift_1, field_t(0)); + return std::array{ low_idx, hi_idx }; } diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/biggroup/biggroup.hpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/biggroup/biggroup.hpp index 7011c5026541..29beed45d588 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/biggroup/biggroup.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/biggroup/biggroup.hpp @@ -358,7 +358,7 @@ template class element { static element batch_mul(const std::vector& points, const std::vector& scalars, const size_t max_num_bits = 0, - const bool with_edgecases = false); + const bool with_edgecases = true); template ::value>> static element secp256k1_ecdsa_mul(const element& pubkey, const Fr& u1, const Fr& u2); diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/biggroup/biggroup.test.cpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/biggroup/biggroup.test.cpp index 21925abf6586..38f6989ff73f 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/biggroup/biggroup.test.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/biggroup/biggroup.test.cpp @@ -1386,12 +1386,11 @@ template class stdlib_biggroup : public testing::Test { points.emplace_back(point); } - // If with_edgecases = true, should handle linearly dependent points correctly + // Since with_edgecases = true by default, should handle linearly dependent points correctly // (offset generator is now a free witness sampled inside batch_mul) element_ct c = element_ct::batch_mul(points, scalars, - /*max_num_bits*/ 128, - /*with_edgecases*/ true); + /*max_num_bits*/ 128); element input_e = (element(input_P_a) * scalar_a); element input_f = (element(input_P_b) * scalar_b); element input_g = (element(input_P_c) * scalar_c); diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/biggroup/biggroup_impl.hpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/biggroup/biggroup_impl.hpp index 8dd2addc79af..229f34f60f9b 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/biggroup/biggroup_impl.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/biggroup/biggroup_impl.hpp @@ -1099,7 +1099,7 @@ element element::batch_mul_internal(const std::vecto * @param _points * @param _scalars * @param max_num_bits The max of the bit lengths of the scalars. - * @param with_edgecases Use when points are linearly dependent. Randomises them. + * @param with_edgecases Use when points are linearly dependent. Randomises them. Set to true by default. * @return element (canonical form) */ template diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/field/field_utils.cpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/field/field_utils.cpp index 6a2fd44efb88..eaacec72c803 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/field/field_utils.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/field/field_utils.cpp @@ -22,6 +22,11 @@ void validate_split_in_field_unsafe(const field_t& lo, const uint256_t r_lo = field_modulus.slice(0, lo_bits); const uint256_t r_hi = field_modulus.slice(lo_bits, field_modulus.get_msb() + 1); + // Precondition: r_lo must be nonzero. If r_lo == 0, the borrow logic below breaks. + // The fields of our concern (bn254 Fr/Fq, secp256k1 Fq) have nonzero low bits, so this + // precondition holds for all current uses. + BB_ASSERT(r_lo != 0, "validate_split_in_field_unsafe: low bits of modulus are zero, borrow logic is unsound"); + // Algorithm: Validate lo + hi * 2^lo_bits < field_modulus using borrow logic // // We want: value < modulus, i.e., value <= modulus - 1 diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/field/field_utils.hpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/field/field_utils.hpp index 3ee78cca7048..1c327f0f0888 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/field/field_utils.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/field/field_utils.hpp @@ -43,6 +43,9 @@ std::pair, field_t> split_unique(const field_t Date: Wed, 8 Apr 2026 18:36:19 +0200 Subject: [PATCH 9/9] chore: decouple perm arg boundary from shiftability assumptions (#22396) making `z_perm` boundary constraint less brittle, previously we implicitly used the fact that it's opened as a shifted poly in gemini implying `z_perm(0) = 0`. as a prerequisite for placing zk masking scalars at the top of the trace, we need to explicitly constrain `z_perm * Lagrange_first = 0` --- ...est_chonk_standalone_vks_havent_changed.sh | 2 +- .../dsl/acir_format/gate_count_constants.hpp | 32 +- .../dsl/acir_proofs/honk_contract.hpp | 60 +- .../acir_proofs/honk_optimized_contract.hpp | 77 ++- .../dsl/acir_proofs/honk_zk_contract.hpp | 60 +- .../eccvm/eccvm_relation_corruption.test.cpp | 58 ++ .../relations/PERMUTATION_ARGUMENT_README.md | 10 + .../relations/ecc_vm/ecc_set_relation.hpp | 6 +- .../ecc_vm/ecc_set_relation_impl.hpp | 8 + .../relations/permutation_relation.hpp | 16 +- .../translator_permutation_relation.hpp | 12 +- .../translator_permutation_relation_impl.hpp | 19 +- .../translator_relation_consistency.test.cpp | 4 + .../ultra_relation_consistency.test.cpp | 4 + .../barretenberg/translator_vm/RELATIONS.md | 25 +- .../translator_vm/relation_failure.test.cpp | 58 ++ .../ultra_honk/permutation.test.cpp | 103 +++ barretenberg/sol/src/honk/HonkTypes.sol | 2 +- barretenberg/sol/src/honk/Relations.sol | 58 +- .../src/honk/optimised/generate_offsets.py | 2 +- .../optimised/honk-optimized.sol.template | 629 +++++++++--------- 21 files changed, 792 insertions(+), 453 deletions(-) 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 d36494156b27..8797f2a56079 100755 --- a/barretenberg/cpp/scripts/test_chonk_standalone_vks_havent_changed.sh +++ b/barretenberg/cpp/scripts/test_chonk_standalone_vks_havent_changed.sh @@ -21,7 +21,7 @@ script_path="$root/barretenberg/cpp/scripts/test_chonk_standalone_vks_havent_cha # - 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="33a01e19" +pinned_short_hash="50947760" pinned_chonk_inputs_url="https://aztec-ci-artifacts.s3.us-east-2.amazonaws.com/protocol/bb-chonk-inputs-${pinned_short_hash}.tar.gz" function update_pinned_hash_in_script { 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 1daf3fa3d135..faad18e4cda0 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 = 12904885; +inline constexpr size_t ROOT_ROLLUP_GATE_COUNT = 12904895; template constexpr std::tuple HONK_RECURSION_CONSTANTS( @@ -67,40 +67,40 @@ constexpr std::tuple HONK_RECURSION_CONSTANTS( if constexpr (std::is_same_v>) { switch (mode) { case PredicateTestCase::ConstantTrue: - return std::make_tuple(681762, 0); + return std::make_tuple(681767, 0); case PredicateTestCase::WitnessTrue: case PredicateTestCase::WitnessFalse: - return std::make_tuple(682819, 0); + return std::make_tuple(682824, 0); } } else if constexpr (std::is_same_v>) { switch (mode) { case PredicateTestCase::ConstantTrue: - return std::make_tuple(703917, 0); + return std::make_tuple(703922, 0); case PredicateTestCase::WitnessTrue: case PredicateTestCase::WitnessFalse: - return std::make_tuple(705070, 0); + return std::make_tuple(705075, 0); } } else if constexpr (std::is_same_v>) { switch (mode) { case PredicateTestCase::ConstantTrue: - return std::make_tuple(20909, 73); + return std::make_tuple(20914, 73); case PredicateTestCase::WitnessTrue: case PredicateTestCase::WitnessFalse: - return std::make_tuple(21966, 73); + return std::make_tuple(21971, 73); } } else if constexpr (std::is_same_v>) { switch (mode) { case PredicateTestCase::ConstantTrue: - return std::make_tuple(25476, 77); + return std::make_tuple(25481, 77); case PredicateTestCase::WitnessTrue: case PredicateTestCase::WitnessFalse: - return std::make_tuple(26629, 77); + return std::make_tuple(26634, 77); } } else if constexpr (std::is_same_v>) { if (mode != PredicateTestCase::ConstantTrue) { bb::assert_failure("Unhandled mode in MegaZKRecursiveFlavor."); } - return std::make_tuple(781910, 0); + return std::make_tuple(781915, 0); } else { bb::assert_failure("Unhandled recursive flavor."); } @@ -113,7 +113,7 @@ constexpr std::tuple HONK_RECURSION_CONSTANTS( // ======================================== // Gate count for Chonk recursive verification (Ultra with RollupIO) -inline constexpr size_t CHONK_RECURSION_GATES = 1491408; +inline constexpr size_t CHONK_RECURSION_GATES = 1491593; // ======================================== // Hypernova Recursion Constants @@ -123,22 +123,22 @@ inline constexpr size_t CHONK_RECURSION_GATES = 1491408; inline constexpr size_t MSM_ROWS_OFFSET = 2; // Init kernel gate counts (verifies OINK proof) -inline constexpr size_t INIT_KERNEL_GATE_COUNT = 25027; +inline constexpr size_t INIT_KERNEL_GATE_COUNT = 25032; 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 = 60487; +inline constexpr size_t INNER_KERNEL_GATE_COUNT_HN = 60497; 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 = 32868; +inline constexpr size_t TAIL_KERNEL_GATE_COUNT = 32873; 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 = 36265; +inline constexpr size_t HIDING_KERNEL_GATE_COUNT = 36270; inline constexpr size_t HIDING_KERNEL_ECC_ROWS = 1407 + MSM_ROWS_OFFSET; inline constexpr size_t HIDING_KERNEL_ULTRA_OPS = 127; @@ -147,7 +147,7 @@ inline constexpr size_t HIDING_KERNEL_ULTRA_OPS = 127; // ======================================== // Gate count for ECCVM recursive verifier (Ultra-arithmetized) -inline constexpr size_t ECCVM_RECURSIVE_VERIFIER_GATE_COUNT = 224162; +inline constexpr size_t ECCVM_RECURSIVE_VERIFIER_GATE_COUNT = 224336; // ======================================== // Goblin AVM Recursive Verifier Constants 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 24fd53118bdc..91986df09c47 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/honk_contract.hpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/honk_contract.hpp @@ -198,7 +198,7 @@ function equal(Fr a, Fr b) pure returns (bool) { uint256 constant CONST_PROOF_SIZE_LOG_N = 25; -uint256 constant NUMBER_OF_SUBRELATIONS = 28; +uint256 constant NUMBER_OF_SUBRELATIONS = 29; uint256 constant BATCHED_RELATION_PARTIAL_LENGTH = 8; uint256 constant ZK_BATCHED_RELATION_PARTIAL_LENGTH = 9; uint256 constant NUMBER_OF_ENTITIES = 41; @@ -870,6 +870,12 @@ library RelationsLib { Fr acc = (wire(p, WIRE.LAGRANGE_LAST) * wire(p, WIRE.Z_PERM_SHIFT)) * domainSep; evals[3] = acc; } + + // Contribution 4: z_perm initialization check (lagrange_first * z_perm = 0) + { + Fr acc = (wire(p, WIRE.LAGRANGE_FIRST) * wire(p, WIRE.Z_PERM)) * domainSep; + evals[4] = acc; + } } function accumulateLogDerivativeLookupRelation( @@ -919,9 +925,9 @@ library RelationsLib { Fr read_tag_boolean_relation = read_tag * read_tag - read_tag; - evals[4] = accumulatorNone; - evals[5] = accumulatorOne; - evals[6] = read_tag_boolean_relation * domainSep; + evals[5] = accumulatorNone; + evals[6] = accumulatorOne; + evals[7] = read_tag_boolean_relation * domainSep; } function accumulateDeltaRangeRelation( @@ -947,7 +953,7 @@ library RelationsLib { acc = acc * (delta_1 + minus_three); acc = acc * wire(p, WIRE.Q_RANGE); acc = acc * domainSep; - evals[7] = acc; + evals[8] = acc; } // Contribution 7 @@ -958,7 +964,7 @@ library RelationsLib { acc = acc * (delta_2 + minus_three); acc = acc * wire(p, WIRE.Q_RANGE); acc = acc * domainSep; - evals[8] = acc; + evals[9] = acc; } // Contribution 8 @@ -969,7 +975,7 @@ library RelationsLib { acc = acc * (delta_3 + minus_three); acc = acc * wire(p, WIRE.Q_RANGE); acc = acc * domainSep; - evals[9] = acc; + evals[10] = acc; } // Contribution 9 @@ -980,7 +986,7 @@ library RelationsLib { acc = acc * (delta_4 + minus_three); acc = acc * wire(p, WIRE.Q_RANGE); acc = acc * domainSep; - evals[10] = acc; + evals[11] = acc; } } @@ -1015,7 +1021,7 @@ library RelationsLib { x_add_identity = x_add_identity * x_diff * x_diff; x_add_identity = x_add_identity - y2_sqr - y1_sqr + y1y2 + y1y2; - evals[11] = x_add_identity * partialEval * wire(p, WIRE.Q_ELLIPTIC) * (ONE - q_is_double); + evals[12] = x_add_identity * partialEval * wire(p, WIRE.Q_ELLIPTIC) * (ONE - q_is_double); } // Contribution 11 point addition, x-coordinate check @@ -1024,7 +1030,7 @@ library RelationsLib { Fr y1_plus_y3 = ep.y_1 + ep.y_3; Fr y_diff = ep.y_2 * q_sign - ep.y_1; Fr y_add_identity = y1_plus_y3 * x_diff + (ep.x_3 - ep.x_1) * y_diff; - evals[12] = y_add_identity * domainSep * wire(p, WIRE.Q_ELLIPTIC) * (ONE - q_is_double); + evals[13] = y_add_identity * domainSep * wire(p, WIRE.Q_ELLIPTIC) * (ONE - q_is_double); } // Contribution 10 point doubling, x-coordinate check @@ -1040,7 +1046,7 @@ library RelationsLib { ep.x_double_identity = (ep.x_3 + ep.x_1 + ep.x_1) * y1_sqr_mul_4 - x1_pow_4_mul_9; Fr acc = ep.x_double_identity * domainSep * wire(p, WIRE.Q_ELLIPTIC) * q_is_double; - evals[11] = evals[11] + acc; + evals[12] = evals[12] + acc; } // Contribution 11 point doubling, y-coordinate check @@ -1048,7 +1054,7 @@ library RelationsLib { { Fr x1_sqr_mul_3 = (ep.x_1 + ep.x_1 + ep.x_1) * ep.x_1; Fr y_double_identity = x1_sqr_mul_3 * (ep.x_1 - ep.x_3) - (ep.y_1 + ep.y_1) * (ep.y_1 + ep.y_3); - evals[12] = evals[12] + y_double_identity * domainSep * wire(p, WIRE.Q_ELLIPTIC) * q_is_double; + evals[13] = evals[13] + y_double_identity * domainSep * wire(p, WIRE.Q_ELLIPTIC) * q_is_double; } } @@ -1135,9 +1141,9 @@ library RelationsLib { ap.adjacent_values_match_if_adjacent_indices_match = (ap.index_delta * MINUS_ONE + ONE) * ap.record_delta; // deg 2 - evals[14] = ap.adjacent_values_match_if_adjacent_indices_match * (wire(p, WIRE.Q_L) * wire(p, WIRE.Q_R)) + evals[15] = ap.adjacent_values_match_if_adjacent_indices_match * (wire(p, WIRE.Q_L) * wire(p, WIRE.Q_R)) * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 5 - evals[15] = ap.index_is_monotonically_increasing * (wire(p, WIRE.Q_L) * wire(p, WIRE.Q_R)) + evals[16] = ap.index_is_monotonically_increasing * (wire(p, WIRE.Q_L) * wire(p, WIRE.Q_R)) * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 5 ap.ROM_consistency_check_identity = ap.memory_record_check * (wire(p, WIRE.Q_L) * wire(p, WIRE.Q_R)); // deg 3 or 7 @@ -1184,10 +1190,10 @@ library RelationsLib { ap.next_gate_access_type * ap.next_gate_access_type - ap.next_gate_access_type; // Putting it all together... - evals[16] = ap.adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation + evals[17] = ap.adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation * (wire(p, WIRE.Q_O)) * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 5 or 8 - evals[17] = ap.index_is_monotonically_increasing * (wire(p, WIRE.Q_O)) * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 - evals[18] = ap.next_gate_access_type_is_boolean * (wire(p, WIRE.Q_O)) * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 or 6 + evals[18] = ap.index_is_monotonically_increasing * (wire(p, WIRE.Q_O)) * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 + evals[19] = ap.next_gate_access_type_is_boolean * (wire(p, WIRE.Q_O)) * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 or 6 ap.RAM_consistency_check_identity = ap.access_check * (wire(p, WIRE.Q_O)); // deg 3 or 9 @@ -1218,7 +1224,7 @@ library RelationsLib { // (deg 3 or 9) + (deg 4) + (deg 3) ap.memory_identity = ap.memory_identity * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 or 10 - evals[13] = ap.memory_identity; + evals[14] = ap.memory_identity; } function accumulateNnfRelation( @@ -1294,7 +1300,7 @@ library RelationsLib { ap.nnf_identity = non_native_field_identity + limb_accumulator_identity; ap.nnf_identity = ap.nnf_identity * (wire(p, WIRE.Q_NNF) * domainSep); - evals[19] = ap.nnf_identity; + evals[20] = ap.nnf_identity; } function accumulatePoseidonExternalRelation( @@ -1330,13 +1336,13 @@ library RelationsLib { ep.v3 = ep.t2 + ep.v4; // u_1 + 3u_2 + 5u_3 + 7u_4 ep.q_pos_by_scaling = wire(p, WIRE.Q_POSEIDON2_EXTERNAL) * domainSep; - evals[20] = evals[20] + ep.q_pos_by_scaling * (ep.v1 - wire(p, WIRE.W_L_SHIFT)); + evals[21] = evals[21] + ep.q_pos_by_scaling * (ep.v1 - wire(p, WIRE.W_L_SHIFT)); - evals[21] = evals[21] + ep.q_pos_by_scaling * (ep.v2 - wire(p, WIRE.W_R_SHIFT)); + evals[22] = evals[22] + ep.q_pos_by_scaling * (ep.v2 - wire(p, WIRE.W_R_SHIFT)); - evals[22] = evals[22] + ep.q_pos_by_scaling * (ep.v3 - wire(p, WIRE.W_O_SHIFT)); + evals[23] = evals[23] + ep.q_pos_by_scaling * (ep.v3 - wire(p, WIRE.W_O_SHIFT)); - evals[23] = evals[23] + ep.q_pos_by_scaling * (ep.v4 - wire(p, WIRE.W_4_SHIFT)); + evals[24] = evals[24] + ep.q_pos_by_scaling * (ep.v4 - wire(p, WIRE.W_4_SHIFT)); } function accumulatePoseidonInternalRelation( @@ -1368,16 +1374,16 @@ library RelationsLib { ip.q_pos_by_scaling = wire(p, WIRE.Q_POSEIDON2_INTERNAL) * domainSep; ip.v1 = ip.u1 * INTERNAL_MATRIX_DIAGONAL[0] + ip.u_sum; - evals[24] = evals[24] + ip.q_pos_by_scaling * (ip.v1 - wire(p, WIRE.W_L_SHIFT)); + evals[25] = evals[25] + ip.q_pos_by_scaling * (ip.v1 - wire(p, WIRE.W_L_SHIFT)); ip.v2 = ip.u2 * INTERNAL_MATRIX_DIAGONAL[1] + ip.u_sum; - evals[25] = evals[25] + ip.q_pos_by_scaling * (ip.v2 - wire(p, WIRE.W_R_SHIFT)); + evals[26] = evals[26] + ip.q_pos_by_scaling * (ip.v2 - wire(p, WIRE.W_R_SHIFT)); ip.v3 = ip.u3 * INTERNAL_MATRIX_DIAGONAL[2] + ip.u_sum; - evals[26] = evals[26] + ip.q_pos_by_scaling * (ip.v3 - wire(p, WIRE.W_O_SHIFT)); + evals[27] = evals[27] + ip.q_pos_by_scaling * (ip.v3 - wire(p, WIRE.W_O_SHIFT)); ip.v4 = ip.u4 * INTERNAL_MATRIX_DIAGONAL[3] + ip.u_sum; - evals[27] = evals[27] + ip.q_pos_by_scaling * (ip.v4 - wire(p, WIRE.W_4_SHIFT)); + evals[28] = evals[28] + ip.q_pos_by_scaling * (ip.v4 - wire(p, WIRE.W_4_SHIFT)); } // Batch subrelation evaluations using precomputed powers of alpha 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 aa04b1fa054e..c736d98bbe93 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 @@ -15,7 +15,7 @@ inline std::string generate_memory_offsets(int log_n) { const int BATCHED_RELATION_PARTIAL_LENGTH = 8; - const int NUMBER_OF_SUBRELATIONS = 28; + const int NUMBER_OF_SUBRELATIONS = 29; const int NUMBER_OF_ALPHAS = NUMBER_OF_SUBRELATIONS - 1; const int START_POINTER = 0x1000; const int BARYCENTRIC_DOMAIN_SIZE = 8; @@ -397,7 +397,7 @@ interface IVerifier { -uint256 constant NUMBER_OF_SUBRELATIONS = 28; +uint256 constant NUMBER_OF_SUBRELATIONS = 29; uint256 constant BATCHED_RELATION_PARTIAL_LENGTH = 8; uint256 constant ZK_BATCHED_RELATION_PARTIAL_LENGTH = 9; uint256 constant NUMBER_OF_ENTITIES = 41; @@ -870,7 +870,7 @@ contract HonkVerifier is IVerifier { // Compute powers of alpha: alpha^2, alpha^3, ..., alpha^26 let alpha_off_set := ALPHA_CHALLENGE_1 - for {} lt(alpha_off_set, add(ALPHA_CHALLENGE_26, 0x20)) {} { + for {} lt(alpha_off_set, add(ALPHA_CHALLENGE_27, 0x20)) {} { let prev_alpha := mload(sub(alpha_off_set, 0x20)) mstore(alpha_off_set, mulmod(prev_alpha, alpha, p)) alpha_off_set := add(alpha_off_set, 0x20) @@ -1624,6 +1624,16 @@ contract HonkVerifier is IVerifier { ) mstore(SUBRELATION_EVAL_3_LOC, acc) } + + // Contribution 4: z_perm initialization (lagrange_first * z_perm = 0) + { + let acc := mulmod( + mulmod(mload(LAGRANGE_FIRST_EVAL_LOC), mload(Z_PERM_EVAL_LOC), p), + mload(POW_PARTIAL_EVALUATION_LOC), + p + ) + mstore(SUBRELATION_EVAL_4_LOC, acc) + } } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ @@ -1687,9 +1697,9 @@ contract HonkVerifier is IVerifier { let read_tag_boolean_relation := mulmod(read_tag, addmod(read_tag, sub(p, 1), p), p) read_tag_boolean_relation := mulmod(read_tag_boolean_relation, mload(POW_PARTIAL_EVALUATION_LOC), p) - mstore(SUBRELATION_EVAL_4_LOC, accumulator_none) - mstore(SUBRELATION_EVAL_5_LOC, accumulator_one) - mstore(SUBRELATION_EVAL_6_LOC, read_tag_boolean_relation) + mstore(SUBRELATION_EVAL_5_LOC, accumulator_none) + mstore(SUBRELATION_EVAL_6_LOC, accumulator_one) + mstore(SUBRELATION_EVAL_7_LOC, read_tag_boolean_relation) } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ @@ -1712,7 +1722,7 @@ contract HonkVerifier is IVerifier { acc := mulmod(acc, addmod(delta_1, minus_three, p), p) acc := mulmod(acc, mload(QRANGE_EVAL_LOC), p) acc := mulmod(acc, mload(POW_PARTIAL_EVALUATION_LOC), p) - mstore(SUBRELATION_EVAL_7_LOC, acc) + mstore(SUBRELATION_EVAL_8_LOC, acc) } { @@ -1722,7 +1732,7 @@ contract HonkVerifier is IVerifier { acc := mulmod(acc, addmod(delta_2, minus_three, p), p) acc := mulmod(acc, mload(QRANGE_EVAL_LOC), p) acc := mulmod(acc, mload(POW_PARTIAL_EVALUATION_LOC), p) - mstore(SUBRELATION_EVAL_8_LOC, acc) + mstore(SUBRELATION_EVAL_9_LOC, acc) } { @@ -1732,7 +1742,7 @@ contract HonkVerifier is IVerifier { acc := mulmod(acc, addmod(delta_3, minus_three, p), p) acc := mulmod(acc, mload(QRANGE_EVAL_LOC), p) acc := mulmod(acc, mload(POW_PARTIAL_EVALUATION_LOC), p) - mstore(SUBRELATION_EVAL_9_LOC, acc) + mstore(SUBRELATION_EVAL_10_LOC, acc) } { @@ -1742,7 +1752,7 @@ contract HonkVerifier is IVerifier { acc := mulmod(acc, addmod(delta_4, minus_three, p), p) acc := mulmod(acc, mload(QRANGE_EVAL_LOC), p) acc := mulmod(acc, mload(POW_PARTIAL_EVALUATION_LOC), p) - mstore(SUBRELATION_EVAL_10_LOC, acc) + mstore(SUBRELATION_EVAL_11_LOC, acc) } } @@ -1767,7 +1777,7 @@ contract HonkVerifier is IVerifier { let eval := mulmod(x_add_identity, mload(POW_PARTIAL_EVALUATION_LOC), p) eval := mulmod(eval, mload(QELLIPTIC_EVAL_LOC), p) eval := mulmod(eval, addmod(1, sub(p, mload(EC_Q_IS_DOUBLE)), p), p) - mstore(SUBRELATION_EVAL_11_LOC, eval) + mstore(SUBRELATION_EVAL_12_LOC, eval) } { @@ -1784,7 +1794,7 @@ contract HonkVerifier is IVerifier { let eval := mulmod(y_add_identity, mload(POW_PARTIAL_EVALUATION_LOC), p) eval := mulmod(eval, mload(QELLIPTIC_EVAL_LOC), p) eval := mulmod(eval, addmod(1, sub(p, mload(EC_Q_IS_DOUBLE)), p), p) - mstore(SUBRELATION_EVAL_12_LOC, eval) + mstore(SUBRELATION_EVAL_13_LOC, eval) } { @@ -1800,10 +1810,10 @@ contract HonkVerifier is IVerifier { let acc := mulmod(ep_x_double_identity, mload(POW_PARTIAL_EVALUATION_LOC), p) acc := mulmod(mulmod(acc, mload(QELLIPTIC_EVAL_LOC), p), mload(EC_Q_IS_DOUBLE), p) - acc := addmod(acc, mload(SUBRELATION_EVAL_11_LOC), p) + acc := addmod(acc, mload(SUBRELATION_EVAL_12_LOC), p) // Add to existing contribution - and double check that numbers here - mstore(SUBRELATION_EVAL_11_LOC, acc) + mstore(SUBRELATION_EVAL_12_LOC, acc) } { @@ -1826,10 +1836,10 @@ contract HonkVerifier is IVerifier { 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) - acc := addmod(acc, mload(SUBRELATION_EVAL_12_LOC), p) + acc := addmod(acc, mload(SUBRELATION_EVAL_13_LOC), p) // Add to existing contribution - and double check that numbers here - mstore(SUBRELATION_EVAL_12_LOC, acc) + mstore(SUBRELATION_EVAL_13_LOC, acc) } } @@ -1936,7 +1946,7 @@ contract HonkVerifier is IVerifier { mulmod(record_delta, addmod(1, sub(p, index_delta), p), p) mstore( - SUBRELATION_EVAL_14_LOC, + SUBRELATION_EVAL_15_LOC, mulmod( adjacent_values_match_if_adjacent_indices_match, mulmod( @@ -1954,7 +1964,7 @@ contract HonkVerifier is IVerifier { // ROM_CONSISTENCY_CHECK_2 mstore( - SUBRELATION_EVAL_15_LOC, + SUBRELATION_EVAL_16_LOC, mulmod( index_is_monotonically_increasing, mulmod( @@ -2050,7 +2060,7 @@ contract HonkVerifier is IVerifier { ) mstore( - SUBRELATION_EVAL_16_LOC, + SUBRELATION_EVAL_17_LOC, mulmod( adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation, scaled_activation_selector, @@ -2059,12 +2069,12 @@ contract HonkVerifier is IVerifier { ) mstore( - SUBRELATION_EVAL_17_LOC, + SUBRELATION_EVAL_18_LOC, mulmod(index_is_monotonically_increasing, scaled_activation_selector, p) ) mstore( - SUBRELATION_EVAL_18_LOC, + SUBRELATION_EVAL_19_LOC, mulmod(next_gate_access_type_is_boolean, scaled_activation_selector, p) ) @@ -2121,7 +2131,7 @@ contract HonkVerifier is IVerifier { mulmod(mload(QMEMORY_EVAL_LOC), mload(POW_PARTIAL_EVALUATION_LOC), p), p ) - mstore(SUBRELATION_EVAL_13_LOC, memory_identity) + mstore(SUBRELATION_EVAL_14_LOC, memory_identity) } } } @@ -2268,7 +2278,7 @@ contract HonkVerifier is IVerifier { p ) - mstore(SUBRELATION_EVAL_19_LOC, nnf_identity) + mstore(SUBRELATION_EVAL_20_LOC, nnf_identity) } /* @@ -2321,22 +2331,22 @@ contract HonkVerifier is IVerifier { mulmod(mload(QPOSEIDON2_EXTERNAL_EVAL_LOC), mload(POW_PARTIAL_EVALUATION_LOC), p) mstore( - SUBRELATION_EVAL_20_LOC, + SUBRELATION_EVAL_21_LOC, mulmod(q_pos_by_scaling, addmod(v1, sub(p, mload(W1_SHIFT_EVAL_LOC)), p), p) ) mstore( - SUBRELATION_EVAL_21_LOC, + SUBRELATION_EVAL_22_LOC, mulmod(q_pos_by_scaling, addmod(v2, sub(p, mload(W2_SHIFT_EVAL_LOC)), p), p) ) mstore( - SUBRELATION_EVAL_22_LOC, + SUBRELATION_EVAL_23_LOC, mulmod(q_pos_by_scaling, addmod(v3, sub(p, mload(W3_SHIFT_EVAL_LOC)), p), p) ) mstore( - SUBRELATION_EVAL_23_LOC, + SUBRELATION_EVAL_24_LOC, mulmod(q_pos_by_scaling, addmod(v4, sub(p, mload(W4_SHIFT_EVAL_LOC)), p), p) ) } @@ -2364,25 +2374,25 @@ contract HonkVerifier is IVerifier { let v1 := addmod(mulmod(u1, POS_INTERNAL_MATRIX_D_0, p), u_sum, p) mstore( - SUBRELATION_EVAL_24_LOC, + SUBRELATION_EVAL_25_LOC, mulmod(q_pos_by_scaling, addmod(v1, sub(p, mload(W1_SHIFT_EVAL_LOC)), p), p) ) let v2 := addmod(mulmod(u2, POS_INTERNAL_MATRIX_D_1, p), u_sum, p) mstore( - SUBRELATION_EVAL_25_LOC, + SUBRELATION_EVAL_26_LOC, mulmod(q_pos_by_scaling, addmod(v2, sub(p, mload(W2_SHIFT_EVAL_LOC)), p), p) ) let v3 := addmod(mulmod(u3, POS_INTERNAL_MATRIX_D_2, p), u_sum, p) mstore( - SUBRELATION_EVAL_26_LOC, + SUBRELATION_EVAL_27_LOC, mulmod(q_pos_by_scaling, addmod(v3, sub(p, mload(W3_SHIFT_EVAL_LOC)), p), p) ) let v4 := addmod(mulmod(u4, POS_INTERNAL_MATRIX_D_3, p), u_sum, p) mstore( - SUBRELATION_EVAL_27_LOC, + SUBRELATION_EVAL_28_LOC, mulmod(q_pos_by_scaling, addmod(v4, sub(p, mload(W4_SHIFT_EVAL_LOC)), p), p) ) } @@ -2531,6 +2541,11 @@ contract HonkVerifier is IVerifier { mulmod(mload(SUBRELATION_EVAL_27_LOC), mload(ALPHA_CHALLENGE_26), p), p ) + accumulator := addmod( + accumulator, + mulmod(mload(SUBRELATION_EVAL_28_LOC), mload(ALPHA_CHALLENGE_27), p), + p + ) let sumcheck_valid := eq(accumulator, mload(FINAL_ROUND_TARGET_LOC)) 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 0721e888c4dd..45330e978986 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 @@ -198,7 +198,7 @@ function equal(Fr a, Fr b) pure returns (bool) { uint256 constant CONST_PROOF_SIZE_LOG_N = 25; -uint256 constant NUMBER_OF_SUBRELATIONS = 28; +uint256 constant NUMBER_OF_SUBRELATIONS = 29; uint256 constant BATCHED_RELATION_PARTIAL_LENGTH = 8; uint256 constant ZK_BATCHED_RELATION_PARTIAL_LENGTH = 9; uint256 constant NUMBER_OF_ENTITIES = 41; @@ -926,6 +926,12 @@ library RelationsLib { Fr acc = (wire(p, WIRE.LAGRANGE_LAST) * wire(p, WIRE.Z_PERM_SHIFT)) * domainSep; evals[3] = acc; } + + // Contribution 4: z_perm initialization check (lagrange_first * z_perm = 0) + { + Fr acc = (wire(p, WIRE.LAGRANGE_FIRST) * wire(p, WIRE.Z_PERM)) * domainSep; + evals[4] = acc; + } } function accumulateLogDerivativeLookupRelation( @@ -975,9 +981,9 @@ library RelationsLib { Fr read_tag_boolean_relation = read_tag * read_tag - read_tag; - evals[4] = accumulatorNone; - evals[5] = accumulatorOne; - evals[6] = read_tag_boolean_relation * domainSep; + evals[5] = accumulatorNone; + evals[6] = accumulatorOne; + evals[7] = read_tag_boolean_relation * domainSep; } function accumulateDeltaRangeRelation( @@ -1003,7 +1009,7 @@ library RelationsLib { acc = acc * (delta_1 + minus_three); acc = acc * wire(p, WIRE.Q_RANGE); acc = acc * domainSep; - evals[7] = acc; + evals[8] = acc; } // Contribution 7 @@ -1014,7 +1020,7 @@ library RelationsLib { acc = acc * (delta_2 + minus_three); acc = acc * wire(p, WIRE.Q_RANGE); acc = acc * domainSep; - evals[8] = acc; + evals[9] = acc; } // Contribution 8 @@ -1025,7 +1031,7 @@ library RelationsLib { acc = acc * (delta_3 + minus_three); acc = acc * wire(p, WIRE.Q_RANGE); acc = acc * domainSep; - evals[9] = acc; + evals[10] = acc; } // Contribution 9 @@ -1036,7 +1042,7 @@ library RelationsLib { acc = acc * (delta_4 + minus_three); acc = acc * wire(p, WIRE.Q_RANGE); acc = acc * domainSep; - evals[10] = acc; + evals[11] = acc; } } @@ -1071,7 +1077,7 @@ library RelationsLib { x_add_identity = x_add_identity * x_diff * x_diff; x_add_identity = x_add_identity - y2_sqr - y1_sqr + y1y2 + y1y2; - evals[11] = x_add_identity * partialEval * wire(p, WIRE.Q_ELLIPTIC) * (ONE - q_is_double); + evals[12] = x_add_identity * partialEval * wire(p, WIRE.Q_ELLIPTIC) * (ONE - q_is_double); } // Contribution 11 point addition, x-coordinate check @@ -1080,7 +1086,7 @@ library RelationsLib { Fr y1_plus_y3 = ep.y_1 + ep.y_3; Fr y_diff = ep.y_2 * q_sign - ep.y_1; Fr y_add_identity = y1_plus_y3 * x_diff + (ep.x_3 - ep.x_1) * y_diff; - evals[12] = y_add_identity * domainSep * wire(p, WIRE.Q_ELLIPTIC) * (ONE - q_is_double); + evals[13] = y_add_identity * domainSep * wire(p, WIRE.Q_ELLIPTIC) * (ONE - q_is_double); } // Contribution 10 point doubling, x-coordinate check @@ -1096,7 +1102,7 @@ library RelationsLib { ep.x_double_identity = (ep.x_3 + ep.x_1 + ep.x_1) * y1_sqr_mul_4 - x1_pow_4_mul_9; Fr acc = ep.x_double_identity * domainSep * wire(p, WIRE.Q_ELLIPTIC) * q_is_double; - evals[11] = evals[11] + acc; + evals[12] = evals[12] + acc; } // Contribution 11 point doubling, y-coordinate check @@ -1104,7 +1110,7 @@ library RelationsLib { { Fr x1_sqr_mul_3 = (ep.x_1 + ep.x_1 + ep.x_1) * ep.x_1; Fr y_double_identity = x1_sqr_mul_3 * (ep.x_1 - ep.x_3) - (ep.y_1 + ep.y_1) * (ep.y_1 + ep.y_3); - evals[12] = evals[12] + y_double_identity * domainSep * wire(p, WIRE.Q_ELLIPTIC) * q_is_double; + evals[13] = evals[13] + y_double_identity * domainSep * wire(p, WIRE.Q_ELLIPTIC) * q_is_double; } } @@ -1191,9 +1197,9 @@ library RelationsLib { ap.adjacent_values_match_if_adjacent_indices_match = (ap.index_delta * MINUS_ONE + ONE) * ap.record_delta; // deg 2 - evals[14] = ap.adjacent_values_match_if_adjacent_indices_match * (wire(p, WIRE.Q_L) * wire(p, WIRE.Q_R)) + evals[15] = ap.adjacent_values_match_if_adjacent_indices_match * (wire(p, WIRE.Q_L) * wire(p, WIRE.Q_R)) * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 5 - evals[15] = ap.index_is_monotonically_increasing * (wire(p, WIRE.Q_L) * wire(p, WIRE.Q_R)) + evals[16] = ap.index_is_monotonically_increasing * (wire(p, WIRE.Q_L) * wire(p, WIRE.Q_R)) * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 5 ap.ROM_consistency_check_identity = ap.memory_record_check * (wire(p, WIRE.Q_L) * wire(p, WIRE.Q_R)); // deg 3 or 7 @@ -1240,10 +1246,10 @@ library RelationsLib { ap.next_gate_access_type * ap.next_gate_access_type - ap.next_gate_access_type; // Putting it all together... - evals[16] = ap.adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation + evals[17] = ap.adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation * (wire(p, WIRE.Q_O)) * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 5 or 8 - evals[17] = ap.index_is_monotonically_increasing * (wire(p, WIRE.Q_O)) * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 - evals[18] = ap.next_gate_access_type_is_boolean * (wire(p, WIRE.Q_O)) * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 or 6 + evals[18] = ap.index_is_monotonically_increasing * (wire(p, WIRE.Q_O)) * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 + evals[19] = ap.next_gate_access_type_is_boolean * (wire(p, WIRE.Q_O)) * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 or 6 ap.RAM_consistency_check_identity = ap.access_check * (wire(p, WIRE.Q_O)); // deg 3 or 9 @@ -1274,7 +1280,7 @@ library RelationsLib { // (deg 3 or 9) + (deg 4) + (deg 3) ap.memory_identity = ap.memory_identity * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 or 10 - evals[13] = ap.memory_identity; + evals[14] = ap.memory_identity; } function accumulateNnfRelation( @@ -1350,7 +1356,7 @@ library RelationsLib { ap.nnf_identity = non_native_field_identity + limb_accumulator_identity; ap.nnf_identity = ap.nnf_identity * (wire(p, WIRE.Q_NNF) * domainSep); - evals[19] = ap.nnf_identity; + evals[20] = ap.nnf_identity; } function accumulatePoseidonExternalRelation( @@ -1386,13 +1392,13 @@ library RelationsLib { ep.v3 = ep.t2 + ep.v4; // u_1 + 3u_2 + 5u_3 + 7u_4 ep.q_pos_by_scaling = wire(p, WIRE.Q_POSEIDON2_EXTERNAL) * domainSep; - evals[20] = evals[20] + ep.q_pos_by_scaling * (ep.v1 - wire(p, WIRE.W_L_SHIFT)); + evals[21] = evals[21] + ep.q_pos_by_scaling * (ep.v1 - wire(p, WIRE.W_L_SHIFT)); - evals[21] = evals[21] + ep.q_pos_by_scaling * (ep.v2 - wire(p, WIRE.W_R_SHIFT)); + evals[22] = evals[22] + ep.q_pos_by_scaling * (ep.v2 - wire(p, WIRE.W_R_SHIFT)); - evals[22] = evals[22] + ep.q_pos_by_scaling * (ep.v3 - wire(p, WIRE.W_O_SHIFT)); + evals[23] = evals[23] + ep.q_pos_by_scaling * (ep.v3 - wire(p, WIRE.W_O_SHIFT)); - evals[23] = evals[23] + ep.q_pos_by_scaling * (ep.v4 - wire(p, WIRE.W_4_SHIFT)); + evals[24] = evals[24] + ep.q_pos_by_scaling * (ep.v4 - wire(p, WIRE.W_4_SHIFT)); } function accumulatePoseidonInternalRelation( @@ -1424,16 +1430,16 @@ library RelationsLib { ip.q_pos_by_scaling = wire(p, WIRE.Q_POSEIDON2_INTERNAL) * domainSep; ip.v1 = ip.u1 * INTERNAL_MATRIX_DIAGONAL[0] + ip.u_sum; - evals[24] = evals[24] + ip.q_pos_by_scaling * (ip.v1 - wire(p, WIRE.W_L_SHIFT)); + evals[25] = evals[25] + ip.q_pos_by_scaling * (ip.v1 - wire(p, WIRE.W_L_SHIFT)); ip.v2 = ip.u2 * INTERNAL_MATRIX_DIAGONAL[1] + ip.u_sum; - evals[25] = evals[25] + ip.q_pos_by_scaling * (ip.v2 - wire(p, WIRE.W_R_SHIFT)); + evals[26] = evals[26] + ip.q_pos_by_scaling * (ip.v2 - wire(p, WIRE.W_R_SHIFT)); ip.v3 = ip.u3 * INTERNAL_MATRIX_DIAGONAL[2] + ip.u_sum; - evals[26] = evals[26] + ip.q_pos_by_scaling * (ip.v3 - wire(p, WIRE.W_O_SHIFT)); + evals[27] = evals[27] + ip.q_pos_by_scaling * (ip.v3 - wire(p, WIRE.W_O_SHIFT)); ip.v4 = ip.u4 * INTERNAL_MATRIX_DIAGONAL[3] + ip.u_sum; - evals[27] = evals[27] + ip.q_pos_by_scaling * (ip.v4 - wire(p, WIRE.W_4_SHIFT)); + evals[28] = evals[28] + ip.q_pos_by_scaling * (ip.v4 - wire(p, WIRE.W_4_SHIFT)); } // Batch subrelation evaluations using precomputed powers of alpha diff --git a/barretenberg/cpp/src/barretenberg/eccvm/eccvm_relation_corruption.test.cpp b/barretenberg/cpp/src/barretenberg/eccvm/eccvm_relation_corruption.test.cpp index 96004f8fb6a6..9baf1f3fb599 100644 --- a/barretenberg/cpp/src/barretenberg/eccvm/eccvm_relation_corruption.test.cpp +++ b/barretenberg/cpp/src/barretenberg/eccvm/eccvm_relation_corruption.test.cpp @@ -376,3 +376,61 @@ TEST_F(ECCVMRelationCorruptionTests, TranscriptNoOpRowRejectsAccumulatorNotEmpty "the row following a no-op"; EXPECT_TRUE(failures.contains(22)) << "Subrelation 22 (accumulator_infinity) should catch the corruption"; } + +/** + * @brief Test that z_perm must be zero at the lagrange_first row. + * + * @details The set relation grand product relies on z_perm[0] = 0 so that (z_perm + lagrange_first) + * evaluates to 1 at the first row. Sub-relation Z_PERM_INIT (lagrange_first * z_perm = 0) enforces this. + * + * We cross-check the lagrange_first position two ways: + * 1. Structurally: z_perm.start_index() - 1 (the zero row before the shiftable region) + * 2. By scanning the lagrange_first polynomial for its non-zero entry + */ +TEST_F(ECCVMRelationCorruptionTests, SetRelationFailsOnZPermNonZeroAtFirstRow) +{ + auto polynomials = build_valid_eccvm_msm_state(); + auto params = compute_full_relation_params(polynomials); + + // Baseline: set relation passes + auto baseline = RelationChecker::check>(polynomials, params, "ECCVMSetRelation"); + EXPECT_TRUE(baseline.empty()) << "Baseline set relation should pass"; + + // Derive expected lagrange_first position from z_perm shiftable structure + ASSERT_TRUE(polynomials.z_perm.is_shiftable()); + size_t structural_first_row = polynomials.z_perm.start_index() - 1; + + // Independently scan lagrange_first for its non-zero entry + const auto& lagrange_first = polynomials.lagrange_first; + size_t scanned_first_row = 0; + bool found = false; + for (size_t i = lagrange_first.start_index(); i < lagrange_first.end_index(); ++i) { + if (lagrange_first[i] != FF(0)) { + scanned_first_row = i; + found = true; + break; + } + } + ASSERT_TRUE(found) << "lagrange_first has no non-zero entry"; + ASSERT_EQ(structural_first_row, scanned_first_row) + << "lagrange_first position doesn't match z_perm shiftable structure"; + + const size_t first_row = scanned_first_row; + + // Expand to full polynomials so we can write at index 0 + polynomials.z_perm = polynomials.z_perm.full(); + polynomials.z_perm_shift = polynomials.z_perm_shift.full(); + + ASSERT_EQ(polynomials.z_perm[first_row], FF(0)); + + // Tamper: set z_perm to non-zero where lagrange_first is active + polynomials.z_perm.at(first_row) = FF(1); + + auto failures = RelationChecker::check>( + polynomials, params, "ECCVMSetRelation - After setting z_perm != 0 at lagrange_first"); + EXPECT_FALSE(failures.empty()) << "Set relation should fail after z_perm init corruption"; + EXPECT_TRUE(failures.contains(ECCVMSetRelationImpl::Z_PERM_INIT)) + << "Sub-relation Z_PERM_INIT should catch the corruption"; + EXPECT_EQ(failures.at(ECCVMSetRelationImpl::Z_PERM_INIT), first_row) + << "Failure should be at lagrange_first row"; +} diff --git a/barretenberg/cpp/src/barretenberg/relations/PERMUTATION_ARGUMENT_README.md b/barretenberg/cpp/src/barretenberg/relations/PERMUTATION_ARGUMENT_README.md index d659ac3ba1be..e00b162d6a40 100644 --- a/barretenberg/cpp/src/barretenberg/relations/PERMUTATION_ARGUMENT_README.md +++ b/barretenberg/cpp/src/barretenberg/relations/PERMUTATION_ARGUMENT_README.md @@ -87,6 +87,16 @@ $$ This ensures the grand product "closes". +### The Initialization Constraint (Subrelation 2) + +At the first row (where $L_0 = 1$), we require: + +$$ +L_0 \cdot Z_{\text{perm}} = 0 +$$ + +This explicitly enforces $Z_{\text{perm}}(0) = 0$, which is necessary for the grand product to start at $1$ (via the $Z_{\text{perm}} + L_0$ term in the recurrence). + ## The Generalized Permutation Argument: Tags and Multiset Equality The generalized permutation argument extends the basic copy-constraint mechanism to also enforce **multiset equality** between tagged sets of values. diff --git a/barretenberg/cpp/src/barretenberg/relations/ecc_vm/ecc_set_relation.hpp b/barretenberg/cpp/src/barretenberg/relations/ecc_vm/ecc_set_relation.hpp index bba0bfa71dda..30486e28c99e 100644 --- a/barretenberg/cpp/src/barretenberg/relations/ecc_vm/ecc_set_relation.hpp +++ b/barretenberg/cpp/src/barretenberg/relations/ecc_vm/ecc_set_relation.hpp @@ -30,12 +30,14 @@ template class ECCVMSetRelationImpl { enum SubrelationIndex : size_t { GRAND_PRODUCT = 0, LEFT_SHIFTABLE = 1, + Z_PERM_INIT = 2, NUM_SUBRELATIONS, }; - static constexpr std::array SUBRELATION_PARTIAL_LENGTHS{ + static constexpr std::array SUBRELATION_PARTIAL_LENGTHS{ 22, // grand product construction sub-relation - 3 // left-shiftable polynomial sub-relation + 3, // left-shiftable polynomial sub-relation + 3 // z_perm initialization sub-relation }; static_assert(NUM_SUBRELATIONS == SUBRELATION_PARTIAL_LENGTHS.size()); // prover optimization to allow for skipping the computation of sub-relations at certain points in sumcheck. diff --git a/barretenberg/cpp/src/barretenberg/relations/ecc_vm/ecc_set_relation_impl.hpp b/barretenberg/cpp/src/barretenberg/relations/ecc_vm/ecc_set_relation_impl.hpp index 22b8c0a9c903..bbed4bb5457f 100644 --- a/barretenberg/cpp/src/barretenberg/relations/ecc_vm/ecc_set_relation_impl.hpp +++ b/barretenberg/cpp/src/barretenberg/relations/ecc_vm/ecc_set_relation_impl.hpp @@ -501,5 +501,13 @@ void ECCVMSetRelationImpl::accumulate(ContainerOverSubrelations& accumulator // Contribution (2) std::get(accumulator) += lagrange_last_short * z_perm_shift_short * scaling_factor; + + // Contribution (3): Enforce z_perm starts at 0. The grand product initialization relies on + // z_perm[0] = 0 so that (z_perm + lagrange_first) evaluates to 1 at the first row. + using InitAccumulator = std::tuple_element_t; + using InitView = typename InitAccumulator::View; + const auto& lagrange_first_init = InitView(in.lagrange_first); + const auto& z_perm_init = InitView(in.z_perm); + std::get(accumulator) += lagrange_first_init * z_perm_init * scaling_factor; } } // namespace bb diff --git a/barretenberg/cpp/src/barretenberg/relations/permutation_relation.hpp b/barretenberg/cpp/src/barretenberg/relations/permutation_relation.hpp index eee4de6d8c7f..05bdee9e30f1 100644 --- a/barretenberg/cpp/src/barretenberg/relations/permutation_relation.hpp +++ b/barretenberg/cpp/src/barretenberg/relations/permutation_relation.hpp @@ -21,12 +21,15 @@ namespace bb { \left[ (w_1(\vec X) + \sigma_1(\vec X) \cdot \beta + \gamma) \cdot (w_2(\vec X) + \sigma_2(\vec X) \cdot \beta + \gamma) \cdot (w_3(\vec X) + \sigma_3 (\vec X) \cdot \beta + \gamma) \cdot (w_4 (\vec X) + \sigma_4(\vec X) \cdot \beta + \gamma)\right] &\ = 0 \f} and \f{align}{ L_{\text{last}}(\vec X)\cdot Z_{\text{perm, shifted}}(\vec X) = 0 \f} + and \f{align}{ L_{0}(\vec X)\cdot Z_{\text{perm}}(\vec X) = 0 \f} Here, \f$ \vec X = (X_0,\ldots, X_{d-1})\f$, and \f$L_{\text{last}}\f$ is "Lagrange last", i.e., the indicator function on the boolean hypercube which is 1 at the point whose corresponding index is the last row of the circuit where the wire polynomails (`w_l`, `w_r`, `w_o`, and `w_4`) take non-zero values. - + The third sub-relation enforces that \f$Z_{\text{perm}}\f$ is zero at the first row. The grand product + construction relies on this: the term \f$(Z_{\text{perm}} + L_0)\f$ evaluates to \f$1\f$ at the first row only + when \f$Z_{\text{perm}}(0) = 0\f$. * @tparam FF_ * @note `z_perm[1] == 1`. if `idx` is the unique index such that `lagrange_last[idx] == 1`, then `z_perm[y] == 0` for @@ -37,9 +40,10 @@ template class UltraPermutationRelationImpl { public: using FF = FF_; - static constexpr std::array SUBRELATION_PARTIAL_LENGTHS{ + static constexpr std::array SUBRELATION_PARTIAL_LENGTHS{ 6, // grand product construction sub-relation - 3 // left-shiftable polynomial sub-relation + 3, // left-shiftable polynomial sub-relation + 3 // z_perm initialization sub-relation }; /** @@ -199,6 +203,12 @@ template class UltraPermutationRelationImpl { using ShortAccumulator = std::tuple_element_t<1, ContainerOverSubrelations>; std::get<1>(accumulators) += ShortAccumulator((lagrange_last_m * z_perm_shift_m) * scaling_factor); + + // Contribution (3): Enforce z_perm starts at 0. The grand product initialization relies on + // z_perm[0] = 0 so that (z_perm + L_first) evaluates to 1 at the first row. + // Without this constraint, a cheating prover could set z_perm[0] to a non-zero value. + using InitAccumulator = std::tuple_element_t<2, ContainerOverSubrelations>; + std::get<2>(accumulators) += InitAccumulator((lagrange_first_m * z_perm_m) * scaling_factor); }; }; diff --git a/barretenberg/cpp/src/barretenberg/relations/translator_vm/translator_permutation_relation.hpp b/barretenberg/cpp/src/barretenberg/relations/translator_vm/translator_permutation_relation.hpp index 63661a8861fe..41d8bf4b5d64 100644 --- a/barretenberg/cpp/src/barretenberg/relations/translator_vm/translator_permutation_relation.hpp +++ b/barretenberg/cpp/src/barretenberg/relations/translator_vm/translator_permutation_relation.hpp @@ -15,9 +15,10 @@ template class TranslatorPermutationRelationImpl { // 1 + polynomial degree of this relation static constexpr size_t RELATION_LENGTH = 7; - static constexpr std::array SUBRELATION_PARTIAL_LENGTHS{ + static constexpr std::array SUBRELATION_PARTIAL_LENGTHS{ 7, // grand product construction sub-relation - 3 // left-shiftable polynomial sub-relation + 3, // left-shiftable polynomial sub-relation + 3 // z_perm initialization sub-relation }; /** @@ -86,9 +87,10 @@ template class TranslatorPermutationRelationImpl { /** * @brief Compute contribution of the goblin translator permutation relation for a given edge (internal function) * - * @details There are 2 relations associated with enforcing the set permutation relation - * This file handles the relation that confirms faithful calculation of the grand - * product polynomial Z_perm. + * @details There are 3 sub-relations associated with enforcing the set permutation relation. + * Sub-relation 0 confirms faithful calculation of the grand product polynomial Z_perm. + * Sub-relation 1 enforces Z_perm_shift = 0 at lagrange_last (grand product closure). + * Sub-relation 2 enforces Z_perm = 0 at lagrange_first (grand product initialization). * * C(in(X)...) = * ( z_perm(X) + lagrange_first(X) )*P(X) diff --git a/barretenberg/cpp/src/barretenberg/relations/translator_vm/translator_permutation_relation_impl.hpp b/barretenberg/cpp/src/barretenberg/relations/translator_vm/translator_permutation_relation_impl.hpp index 8718091ddca0..2baa7df8bf3e 100644 --- a/barretenberg/cpp/src/barretenberg/relations/translator_vm/translator_permutation_relation_impl.hpp +++ b/barretenberg/cpp/src/barretenberg/relations/translator_vm/translator_permutation_relation_impl.hpp @@ -12,9 +12,10 @@ namespace bb { /** * @brief Compute contribution of the goblin translator permutation relation for a given edge (internal function) * - * @details There are 2 relations associated with enforcing the set permutation relation - * This file handles the relation that confirms faithful calculation of the grand - * product polynomial Z_perm. + * @details There are 3 sub-relations associated with enforcing the set permutation relation. + * Sub-relation 0 confirms faithful calculation of the grand product polynomial Z_perm. + * Sub-relation 1 enforces Z_perm_shift = 0 at lagrange_last (grand product closure). + * Sub-relation 2 enforces Z_perm = 0 at lagrange_first (grand product initialization). * * C(in(X)...) = * ( z_perm(X) + lagrange_first(X) )*P(X) @@ -70,5 +71,17 @@ void TranslatorPermutationRelationImpl::accumulate(ContainerOverSubrelations // Contribution (2) std::get<1>(accumulators) += (lagrange_last * z_perm_shift) * scaling_factor; }(); + + [&]() { + using Accumulator = std::tuple_element_t<2, ContainerOverSubrelations>; + using View = typename Accumulator::View; + + const auto z_perm = View(in.z_perm); + const auto lagrange_first = View(in.lagrange_first); + + // Contribution (3): Enforce z_perm starts at 0. The grand product initialization relies on + // z_perm[0] = 0 so that (z_perm + lagrange_first) evaluates to 1 at the first row. + std::get<2>(accumulators) += (lagrange_first * z_perm) * scaling_factor; + }(); }; } // namespace bb diff --git a/barretenberg/cpp/src/barretenberg/relations/translator_vm/translator_relation_consistency.test.cpp b/barretenberg/cpp/src/barretenberg/relations/translator_vm/translator_relation_consistency.test.cpp index 2cdef5a95c7e..452fc82725aa 100644 --- a/barretenberg/cpp/src/barretenberg/relations/translator_vm/translator_relation_consistency.test.cpp +++ b/barretenberg/cpp/src/barretenberg/relations/translator_vm/translator_relation_consistency.test.cpp @@ -106,6 +106,10 @@ TEST_F(TranslatorRelationConsistency, PermutationRelation) auto contribution_2 = z_perm_shift * lagrange_last; expected_values[1] = contribution_2; + // (Contribution 3) + auto contribution_3 = lagrange_first * z_perm; + expected_values[2] = contribution_3; + validate_relation_execution(expected_values, input_elements, parameters); }; run_test(/*random_inputs=*/false); diff --git a/barretenberg/cpp/src/barretenberg/relations/ultra_relation_consistency.test.cpp b/barretenberg/cpp/src/barretenberg/relations/ultra_relation_consistency.test.cpp index d1739dc43d6f..57b606e70123 100644 --- a/barretenberg/cpp/src/barretenberg/relations/ultra_relation_consistency.test.cpp +++ b/barretenberg/cpp/src/barretenberg/relations/ultra_relation_consistency.test.cpp @@ -225,6 +225,10 @@ TEST_F(UltraRelationConsistency, UltraPermutationRelation) auto contribution_2 = z_perm_shift * lagrange_last; expected_values[1] = contribution_2; + // Contribution 3 + auto contribution_3 = lagrange_first * z_perm; + expected_values[2] = contribution_3; + validate_relation_execution(expected_values, input_elements, parameters); }; run_test(/*random_inputs=*/false); diff --git a/barretenberg/cpp/src/barretenberg/translator_vm/RELATIONS.md b/barretenberg/cpp/src/barretenberg/translator_vm/RELATIONS.md index f3f296895525..cad508b38258 100644 --- a/barretenberg/cpp/src/barretenberg/translator_vm/RELATIONS.md +++ b/barretenberg/cpp/src/barretenberg/translator_vm/RELATIONS.md @@ -439,10 +439,11 @@ The Permutation Relation is the foundation of all range constraints in the Trans If the two multisets are equal (i.e., one is a permutation of the other), then all values are valid. -The relation consists of 2 subrelations: +The relation consists of 3 subrelations: 1. Grand product identity (degree 7) 2. Finalization check (degree 3) +3. Initialization check (degree 3) #### Interaction with the Delta Range Constraints @@ -482,11 +483,9 @@ $$\boxed{\left( z_{\text{perm}} + L_{\text{first}} \right) \cdot \prod_{j=0}^{3} where: -- $L_{\text{first}}$: Lagrange polynomial for first row ($z_{\text{perm}}[0] = 0$ is enforced implicitly) +- $L_{\text{first}}$: Lagrange polynomial for first row ($z_{\text{perm}}[0] = 0$ is enforced by subrelation 3) - $L_{\text{last}}$: Lagrange polynomial for last row (we enforce $z_{\text{perm}}[\text{last}] = 0$ in subrelation 2) - $z_{\text{perm}}^{\text{shift}}$: Shifted grand product polynomial ($z_{\text{perm}}[i+1]$) - -Note that $z_{\text{perm}}[0] = 0$ follows implicitly from the fact that we are opening $z_{\text{perm}}$ and $z_{\text{perm}}^{\text{shift}}$ both at the same challenge. If the two multisets are equal: 1. At each step, the products telescope: contributions cancel out @@ -515,6 +514,24 @@ Active when: Last row only ($L_{\text{last}} = 1$) Degree: 2 (Lagrange × shifted polynomial) +--- + +### Subrelation 3: Initialization Check + +Purpose: Ensure the grand product polynomial starts at zero. + +$$\boxed{L_{\text{first}} \cdot z_{\text{perm}} = 0}$$ + +Interpretation: + +- At the first row, $L_{\text{first}} = 1$ +- $z_{\text{perm}}$ must be 0 at this row +- This is necessary for the $(z_{\text{perm}} + L_{\text{first}})$ term in subrelation 1 to evaluate to 1 + +Active when: First row only ($L_{\text{first}} = 1$) + +Degree: 2 (Lagrange × polynomial) + ## Delta Range Constraint Relation The Delta Range Constraint Relation works in tandem with the Permutation Relation to prove that the ordered (sorted) multiset is actually sorted and bounded correctly. diff --git a/barretenberg/cpp/src/barretenberg/translator_vm/relation_failure.test.cpp b/barretenberg/cpp/src/barretenberg/translator_vm/relation_failure.test.cpp index f0fedbe501d4..a26cf86e6c4e 100644 --- a/barretenberg/cpp/src/barretenberg/translator_vm/relation_failure.test.cpp +++ b/barretenberg/cpp/src/barretenberg/translator_vm/relation_failure.test.cpp @@ -267,6 +267,64 @@ TEST_F(TranslatorRelationFailureTests, PermutationFailsOnZPermCorruption) EXPECT_FALSE(failures.empty()) << "Permutation should fail after z_perm corruption"; } +/** + * @brief Test that z_perm must be zero at the lagrange_first row. + * + * @details The permutation grand product relies on z_perm[0] = 0 so that (z_perm + lagrange_first) + * evaluates to 1 at the first row. Sub-relation 2 (lagrange_first * z_perm = 0) enforces this. + * + * We cross-check the lagrange_first position two ways: + * 1. Structurally: z_perm.start_index() - 1 (the zero row before the shiftable region) + * 2. By scanning the lagrange_first polynomial for its non-zero entry + */ +TEST_F(TranslatorRelationFailureTests, PermutationFailsOnZPermNonZeroAtFirstRow) +{ + auto [key, params] = build_valid_translator_state(); + auto& pp = key.proving_key->polynomials; + + // Baseline: permutation relation passes + auto baseline = + RelationChecker::check>(pp, params, "TranslatorPermutationRelation"); + EXPECT_TRUE(baseline.empty()) << "Baseline permutation should pass"; + + // Derive expected lagrange_first position from z_perm shiftable structure + ASSERT_TRUE(pp.z_perm.is_shiftable()); + size_t structural_first_row = pp.z_perm.start_index() - 1; + + // Independently scan lagrange_first for its non-zero entry + const auto& lagrange_first = pp.lagrange_first; + size_t scanned_first_row = 0; + bool found = false; + for (size_t i = lagrange_first.start_index(); i < lagrange_first.end_index(); ++i) { + if (lagrange_first[i] != FF(0)) { + scanned_first_row = i; + found = true; + break; + } + } + ASSERT_TRUE(found) << "lagrange_first has no non-zero entry"; + ASSERT_EQ(structural_first_row, scanned_first_row) + << "lagrange_first position doesn't match z_perm shiftable structure"; + + const size_t first_row = scanned_first_row; + + // Expand to full polynomials so we can write at the zero row + pp.z_perm = pp.z_perm.full(); + pp.z_perm_shift = pp.z_perm_shift.full(); + + ASSERT_EQ(pp.z_perm[first_row], FF(0)); + + // Tamper: set z_perm to non-zero where lagrange_first is active + pp.z_perm.at(first_row) = FF(1); + + auto failures = RelationChecker::check>( + pp, params, "TranslatorPermutationRelation - After setting z_perm != 0 at lagrange_first"); + EXPECT_FALSE(failures.empty()) << "Permutation should fail after z_perm init corruption"; + // Sub-relation 2 (lagrange_first * z_perm = 0) should catch this + EXPECT_TRUE(failures.contains(2)) << "Sub-relation 2 (z_perm init) should catch the corruption"; + EXPECT_EQ(failures.at(2), static_cast(first_row)) << "Failure should be at lagrange_first row"; +} + /** * @brief Corrupt ordered poly at position circuit_size - MAX_RANDOM - 2, the last row where the delta * constraint is enforced. This is right before lagrange_real_last + lagrange_ordered_masking kicks in. diff --git a/barretenberg/cpp/src/barretenberg/ultra_honk/permutation.test.cpp b/barretenberg/cpp/src/barretenberg/ultra_honk/permutation.test.cpp index 664cbe392c87..59e27d7a1b90 100644 --- a/barretenberg/cpp/src/barretenberg/ultra_honk/permutation.test.cpp +++ b/barretenberg/cpp/src/barretenberg/ultra_honk/permutation.test.cpp @@ -255,6 +255,109 @@ TYPED_TEST(PermutationNonZKTests, ZPermZeroedOutFailure) EXPECT_FALSE(tampered_permutation_relation_failures.empty()); } +/** + * @brief Test that z_perm must be zero at the first row (where lagrange_first = 1). + * + * @details The permutation argument includes an initialization constraint: lagrange_first * z_perm = 0. + * The grand product relies on z_perm[0] = 0 so that (z_perm + lagrange_first) evaluates to 1 at row 0. + * + * This test: + * 1. Builds a valid circuit and verifies the permutation relation holds + * 2. Expands z_perm to a full polynomial to access the boundary at index 0 + * 3. Tampers with z_perm at index 0, making it non-zero + * 4. Verifies the permutation relation now fails (sub-relation 2: lagrange_first * z_perm = 0) + * + * @note This test excludes ZK flavors because we manually tamper with z_perm, which would + * conflict with the ZK masking applied to witness polynomials. + */ +TYPED_TEST(PermutationNonZKTests, ZPermNonZeroAtFirstRowFailure) +{ + using Flavor = TypeParam; + using Builder = typename Flavor::CircuitBuilder; + + using ProverInstance = ProverInstance_; + using VerificationKey = Flavor::VerificationKey; + + using Prover = TestFixture::Prover; + + Builder builder; + + auto a = fr::random_element(); + auto b = fr::random_element(); + auto c = a + b; + + uint32_t a_idx = builder.add_variable(a); + uint32_t a_copy_idx = builder.add_variable(a); + uint32_t b_idx = builder.add_variable(b); + uint32_t c_idx = builder.add_variable(c); + + builder.create_add_gate({ a_idx, b_idx, c_idx, 1, 1, -1, 0 }); + builder.create_add_gate({ a_copy_idx, b_idx, c_idx, 1, 1, -1, 0 }); + builder.assert_equal(a_copy_idx, a_idx); + + TestFixture::set_default_pairing_points_and_ipa_claim_and_proof(builder); + + auto prover_instance = std::make_shared(builder); + auto verification_key = std::make_shared(prover_instance->get_precomputed()); + + Prover prover(prover_instance, verification_key); + auto proof = prover.construct_proof(); + + // Verify the permutation relation holds before tampering. + auto permutation_relation_failures = RelationChecker::template check>( + prover_instance->polynomials, prover_instance->relation_parameters, "UltraPermutation - Before Tampering"); + EXPECT_TRUE(permutation_relation_failures.empty()); + + // Derive the expected lagrange_first position from the z_perm polynomial structure: + // z_perm is shiftable with start_index = NUM_ZERO_ROWS, so the zero row just before it + // is where lagrange_first must be active. Read this before expanding to full. + auto& z_perm = prover_instance->polynomials.z_perm; + auto& z_perm_shift = prover_instance->polynomials.z_perm_shift; + ASSERT_TRUE(z_perm.is_shiftable()); + size_t structural_first_row = z_perm.start_index() - 1; + + // z_perm is shiftable (start_index = 1), so z_perm[0] is a virtual zero not backed by memory. + // To tamper with it, we must expand to a full polynomial. We also expand z_perm_shift independently + // (it cannot be derived via shifted() from a full polynomial with start_index = 0). + prover_instance->polynomials.z_perm = z_perm.full(); + prover_instance->polynomials.z_perm_shift = z_perm_shift.full(); + + // Independently scan the lagrange_first polynomial for its non-zero entry. + const auto& lagrange_first = prover_instance->polynomials.lagrange_first; + size_t scanned_first_row = 0; + bool found = false; + for (size_t i = lagrange_first.start_index(); i < lagrange_first.end_index(); ++i) { + if (lagrange_first[i] != fr(0)) { + scanned_first_row = i; + found = true; + break; + } + } + ASSERT_TRUE(found) << "lagrange_first has no non-zero entry"; + + // Cross-check: both derivations must agree. + ASSERT_EQ(structural_first_row, scanned_first_row) + << "lagrange_first position doesn't match z_perm shiftable structure"; + + const size_t first_row = scanned_first_row; + + // Sanity check: z_perm is currently 0 at the lagrange_first row + ASSERT_EQ(prover_instance->polynomials.z_perm[first_row], fr(0)); + + // Tamper: set z_perm to non-zero where lagrange_first is active + prover_instance->polynomials.z_perm.at(first_row) = fr(1); + + // Verify the permutation relation now fails. + auto tampered_failures = RelationChecker::template check>( + prover_instance->polynomials, + prover_instance->relation_parameters, + "UltraPermutation - After setting z_perm != 0 at lagrange_first"); + EXPECT_FALSE(tampered_failures.empty()); + // Sub-relation 2 (lagrange_first * z_perm = 0) should fail at the lagrange_first row + ASSERT_TRUE(tampered_failures.contains(2)) << "Expected sub-relation 2 (z_perm init) to fail"; + ASSERT_EQ(tampered_failures.at(2), first_row) << "Expected failure at lagrange_first row"; +} + /** * @brief Test that z_perm_shift must be zero at the last row (where lagrange_last = 1). * diff --git a/barretenberg/sol/src/honk/HonkTypes.sol b/barretenberg/sol/src/honk/HonkTypes.sol index 15732484857a..e68c8d048017 100644 --- a/barretenberg/sol/src/honk/HonkTypes.sol +++ b/barretenberg/sol/src/honk/HonkTypes.sol @@ -6,7 +6,7 @@ import {Fr} from "./Fr.sol"; uint256 constant CONST_PROOF_SIZE_LOG_N = 25; -uint256 constant NUMBER_OF_SUBRELATIONS = 28; +uint256 constant NUMBER_OF_SUBRELATIONS = 29; uint256 constant BATCHED_RELATION_PARTIAL_LENGTH = 8; uint256 constant ZK_BATCHED_RELATION_PARTIAL_LENGTH = 9; uint256 constant NUMBER_OF_ENTITIES = 41; diff --git a/barretenberg/sol/src/honk/Relations.sol b/barretenberg/sol/src/honk/Relations.sol index a0abef6a7144..0f3ec0c6cb60 100644 --- a/barretenberg/sol/src/honk/Relations.sol +++ b/barretenberg/sol/src/honk/Relations.sol @@ -200,6 +200,12 @@ library RelationsLib { Fr acc = (wire(p, WIRE.LAGRANGE_LAST) * wire(p, WIRE.Z_PERM_SHIFT)) * domainSep; evals[3] = acc; } + + // Contribution 4: z_perm initialization check (lagrange_first * z_perm = 0) + { + Fr acc = (wire(p, WIRE.LAGRANGE_FIRST) * wire(p, WIRE.Z_PERM)) * domainSep; + evals[4] = acc; + } } function accumulateLogDerivativeLookupRelation( @@ -249,9 +255,9 @@ library RelationsLib { Fr read_tag_boolean_relation = read_tag * read_tag - read_tag; - evals[4] = accumulatorNone; - evals[5] = accumulatorOne; - evals[6] = read_tag_boolean_relation * domainSep; + evals[5] = accumulatorNone; + evals[6] = accumulatorOne; + evals[7] = read_tag_boolean_relation * domainSep; } function accumulateDeltaRangeRelation( @@ -277,7 +283,7 @@ library RelationsLib { acc = acc * (delta_1 + minus_three); acc = acc * wire(p, WIRE.Q_RANGE); acc = acc * domainSep; - evals[7] = acc; + evals[8] = acc; } // Contribution 7 @@ -288,7 +294,7 @@ library RelationsLib { acc = acc * (delta_2 + minus_three); acc = acc * wire(p, WIRE.Q_RANGE); acc = acc * domainSep; - evals[8] = acc; + evals[9] = acc; } // Contribution 8 @@ -299,7 +305,7 @@ library RelationsLib { acc = acc * (delta_3 + minus_three); acc = acc * wire(p, WIRE.Q_RANGE); acc = acc * domainSep; - evals[9] = acc; + evals[10] = acc; } // Contribution 9 @@ -310,7 +316,7 @@ library RelationsLib { acc = acc * (delta_4 + minus_three); acc = acc * wire(p, WIRE.Q_RANGE); acc = acc * domainSep; - evals[10] = acc; + evals[11] = acc; } } @@ -345,7 +351,7 @@ library RelationsLib { x_add_identity = x_add_identity * x_diff * x_diff; x_add_identity = x_add_identity - y2_sqr - y1_sqr + y1y2 + y1y2; - evals[11] = x_add_identity * partialEval * wire(p, WIRE.Q_ELLIPTIC) * (ONE - q_is_double); + evals[12] = x_add_identity * partialEval * wire(p, WIRE.Q_ELLIPTIC) * (ONE - q_is_double); } // Contribution 11 point addition, x-coordinate check @@ -354,7 +360,7 @@ library RelationsLib { Fr y1_plus_y3 = ep.y_1 + ep.y_3; Fr y_diff = ep.y_2 * q_sign - ep.y_1; Fr y_add_identity = y1_plus_y3 * x_diff + (ep.x_3 - ep.x_1) * y_diff; - evals[12] = y_add_identity * domainSep * wire(p, WIRE.Q_ELLIPTIC) * (ONE - q_is_double); + evals[13] = y_add_identity * domainSep * wire(p, WIRE.Q_ELLIPTIC) * (ONE - q_is_double); } // Contribution 10 point doubling, x-coordinate check @@ -370,7 +376,7 @@ library RelationsLib { ep.x_double_identity = (ep.x_3 + ep.x_1 + ep.x_1) * y1_sqr_mul_4 - x1_pow_4_mul_9; Fr acc = ep.x_double_identity * domainSep * wire(p, WIRE.Q_ELLIPTIC) * q_is_double; - evals[11] = evals[11] + acc; + evals[12] = evals[12] + acc; } // Contribution 11 point doubling, y-coordinate check @@ -378,7 +384,7 @@ library RelationsLib { { Fr x1_sqr_mul_3 = (ep.x_1 + ep.x_1 + ep.x_1) * ep.x_1; Fr y_double_identity = x1_sqr_mul_3 * (ep.x_1 - ep.x_3) - (ep.y_1 + ep.y_1) * (ep.y_1 + ep.y_3); - evals[12] = evals[12] + y_double_identity * domainSep * wire(p, WIRE.Q_ELLIPTIC) * q_is_double; + evals[13] = evals[13] + y_double_identity * domainSep * wire(p, WIRE.Q_ELLIPTIC) * q_is_double; } } @@ -465,9 +471,9 @@ library RelationsLib { ap.adjacent_values_match_if_adjacent_indices_match = (ap.index_delta * MINUS_ONE + ONE) * ap.record_delta; // deg 2 - evals[14] = ap.adjacent_values_match_if_adjacent_indices_match * (wire(p, WIRE.Q_L) * wire(p, WIRE.Q_R)) + evals[15] = ap.adjacent_values_match_if_adjacent_indices_match * (wire(p, WIRE.Q_L) * wire(p, WIRE.Q_R)) * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 5 - evals[15] = ap.index_is_monotonically_increasing * (wire(p, WIRE.Q_L) * wire(p, WIRE.Q_R)) + evals[16] = ap.index_is_monotonically_increasing * (wire(p, WIRE.Q_L) * wire(p, WIRE.Q_R)) * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 5 ap.ROM_consistency_check_identity = ap.memory_record_check * (wire(p, WIRE.Q_L) * wire(p, WIRE.Q_R)); // deg 3 or 7 @@ -515,10 +521,10 @@ library RelationsLib { ap.next_gate_access_type * ap.next_gate_access_type - ap.next_gate_access_type; // Putting it all together... - evals[16] = ap.adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation + evals[17] = ap.adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation * (wire(p, WIRE.Q_O)) * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 5 or 8 - evals[17] = ap.index_is_monotonically_increasing * (wire(p, WIRE.Q_O)) * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 - evals[18] = ap.next_gate_access_type_is_boolean * (wire(p, WIRE.Q_O)) * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 or 6 + evals[18] = ap.index_is_monotonically_increasing * (wire(p, WIRE.Q_O)) * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 + evals[19] = ap.next_gate_access_type_is_boolean * (wire(p, WIRE.Q_O)) * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 or 6 ap.RAM_consistency_check_identity = ap.access_check * (wire(p, WIRE.Q_O)); // deg 3 or 9 @@ -549,7 +555,7 @@ library RelationsLib { // (deg 3 or 9) + (deg 4) + (deg 3) ap.memory_identity = ap.memory_identity * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 or 10 - evals[13] = ap.memory_identity; + evals[14] = ap.memory_identity; } function accumulateNnfRelation( @@ -625,7 +631,7 @@ library RelationsLib { ap.nnf_identity = non_native_field_identity + limb_accumulator_identity; ap.nnf_identity = ap.nnf_identity * (wire(p, WIRE.Q_NNF) * domainSep); - evals[19] = ap.nnf_identity; + evals[20] = ap.nnf_identity; } function accumulatePoseidonExternalRelation( @@ -661,13 +667,13 @@ library RelationsLib { ep.v3 = ep.t2 + ep.v4; // u_1 + 3u_2 + 5u_3 + 7u_4 ep.q_pos_by_scaling = wire(p, WIRE.Q_POSEIDON2_EXTERNAL) * domainSep; - evals[20] = evals[20] + ep.q_pos_by_scaling * (ep.v1 - wire(p, WIRE.W_L_SHIFT)); + evals[21] = evals[21] + ep.q_pos_by_scaling * (ep.v1 - wire(p, WIRE.W_L_SHIFT)); - evals[21] = evals[21] + ep.q_pos_by_scaling * (ep.v2 - wire(p, WIRE.W_R_SHIFT)); + evals[22] = evals[22] + ep.q_pos_by_scaling * (ep.v2 - wire(p, WIRE.W_R_SHIFT)); - evals[22] = evals[22] + ep.q_pos_by_scaling * (ep.v3 - wire(p, WIRE.W_O_SHIFT)); + evals[23] = evals[23] + ep.q_pos_by_scaling * (ep.v3 - wire(p, WIRE.W_O_SHIFT)); - evals[23] = evals[23] + ep.q_pos_by_scaling * (ep.v4 - wire(p, WIRE.W_4_SHIFT)); + evals[24] = evals[24] + ep.q_pos_by_scaling * (ep.v4 - wire(p, WIRE.W_4_SHIFT)); } function accumulatePoseidonInternalRelation( @@ -699,16 +705,16 @@ library RelationsLib { ip.q_pos_by_scaling = wire(p, WIRE.Q_POSEIDON2_INTERNAL) * domainSep; ip.v1 = ip.u1 * INTERNAL_MATRIX_DIAGONAL[0] + ip.u_sum; - evals[24] = evals[24] + ip.q_pos_by_scaling * (ip.v1 - wire(p, WIRE.W_L_SHIFT)); + evals[25] = evals[25] + ip.q_pos_by_scaling * (ip.v1 - wire(p, WIRE.W_L_SHIFT)); ip.v2 = ip.u2 * INTERNAL_MATRIX_DIAGONAL[1] + ip.u_sum; - evals[25] = evals[25] + ip.q_pos_by_scaling * (ip.v2 - wire(p, WIRE.W_R_SHIFT)); + evals[26] = evals[26] + ip.q_pos_by_scaling * (ip.v2 - wire(p, WIRE.W_R_SHIFT)); ip.v3 = ip.u3 * INTERNAL_MATRIX_DIAGONAL[2] + ip.u_sum; - evals[26] = evals[26] + ip.q_pos_by_scaling * (ip.v3 - wire(p, WIRE.W_O_SHIFT)); + evals[27] = evals[27] + ip.q_pos_by_scaling * (ip.v3 - wire(p, WIRE.W_O_SHIFT)); ip.v4 = ip.u4 * INTERNAL_MATRIX_DIAGONAL[3] + ip.u_sum; - evals[27] = evals[27] + ip.q_pos_by_scaling * (ip.v4 - wire(p, WIRE.W_4_SHIFT)); + evals[28] = evals[28] + ip.q_pos_by_scaling * (ip.v4 - wire(p, WIRE.W_4_SHIFT)); } // Batch subrelation evaluations using precomputed powers of alpha diff --git a/barretenberg/sol/src/honk/optimised/generate_offsets.py b/barretenberg/sol/src/honk/optimised/generate_offsets.py index 8001478d9272..7916e4d9d047 100755 --- a/barretenberg/sol/src/honk/optimised/generate_offsets.py +++ b/barretenberg/sol/src/honk/optimised/generate_offsets.py @@ -187,7 +187,7 @@ def print_proof(pointer: int): BATCHED_RELATION_PARTIAL_LENGTH = 8 PROOF_SIZE_LOG_N = 15 NUMBER_OF_ENTITIES = 41 -NUMBER_OF_SUBRELATIONS = 28 +NUMBER_OF_SUBRELATIONS = 29 NUMBER_OF_ALPHAS = NUMBER_OF_SUBRELATIONS - 1 # For the meantime we will load the entire proof into memory here # however i predict that it will be more efficient to load in the sumcheck univars diff --git a/barretenberg/sol/src/honk/optimised/honk-optimized.sol.template b/barretenberg/sol/src/honk/optimised/honk-optimized.sol.template index 282eb771de07..a0f8e200572a 100644 --- a/barretenberg/sol/src/honk/optimised/honk-optimized.sol.template +++ b/barretenberg/sol/src/honk/optimised/honk-optimized.sol.template @@ -3,7 +3,7 @@ pragma solidity ^0.8.27; import {IVerifier} from "src/interfaces/IVerifier.sol"; -uint256 constant NUMBER_OF_SUBRELATIONS = 28; +uint256 constant NUMBER_OF_SUBRELATIONS = 29; uint256 constant BATCHED_RELATION_PARTIAL_LENGTH = 8; uint256 constant ZK_BATCHED_RELATION_PARTIAL_LENGTH = 9; uint256 constant NUMBER_OF_ENTITIES = 41; @@ -420,36 +420,37 @@ contract BlakeOptHonkVerifier is IVerifier { uint256 internal constant ALPHA_CHALLENGE_24 = 0x3860; uint256 internal constant ALPHA_CHALLENGE_25 = 0x3880; uint256 internal constant ALPHA_CHALLENGE_26 = 0x38a0; - uint256 internal constant GATE_CHALLENGE_0 = 0x38c0; - uint256 internal constant GATE_CHALLENGE_1 = 0x38e0; - uint256 internal constant GATE_CHALLENGE_2 = 0x3900; - uint256 internal constant GATE_CHALLENGE_3 = 0x3920; - uint256 internal constant GATE_CHALLENGE_4 = 0x3940; - uint256 internal constant GATE_CHALLENGE_5 = 0x3960; - uint256 internal constant GATE_CHALLENGE_6 = 0x3980; - uint256 internal constant GATE_CHALLENGE_7 = 0x39a0; - uint256 internal constant GATE_CHALLENGE_8 = 0x39c0; - uint256 internal constant GATE_CHALLENGE_9 = 0x39e0; - uint256 internal constant GATE_CHALLENGE_10 = 0x3a00; - uint256 internal constant GATE_CHALLENGE_11 = 0x3a20; - uint256 internal constant GATE_CHALLENGE_12 = 0x3a40; - uint256 internal constant GATE_CHALLENGE_13 = 0x3a60; - uint256 internal constant GATE_CHALLENGE_14 = 0x3a80; - uint256 internal constant SUM_U_CHALLENGE_0 = 0x3aa0; - uint256 internal constant SUM_U_CHALLENGE_1 = 0x3ac0; - uint256 internal constant SUM_U_CHALLENGE_2 = 0x3ae0; - uint256 internal constant SUM_U_CHALLENGE_3 = 0x3b00; - uint256 internal constant SUM_U_CHALLENGE_4 = 0x3b20; - uint256 internal constant SUM_U_CHALLENGE_5 = 0x3b40; - uint256 internal constant SUM_U_CHALLENGE_6 = 0x3b60; - uint256 internal constant SUM_U_CHALLENGE_7 = 0x3b80; - uint256 internal constant SUM_U_CHALLENGE_8 = 0x3ba0; - uint256 internal constant SUM_U_CHALLENGE_9 = 0x3bc0; - uint256 internal constant SUM_U_CHALLENGE_10 = 0x3be0; - uint256 internal constant SUM_U_CHALLENGE_11 = 0x3c00; - uint256 internal constant SUM_U_CHALLENGE_12 = 0x3c20; - uint256 internal constant SUM_U_CHALLENGE_13 = 0x3c40; - uint256 internal constant SUM_U_CHALLENGE_14 = 0x3c60; + uint256 internal constant ALPHA_CHALLENGE_27 = 0x38c0; + uint256 internal constant GATE_CHALLENGE_0 = 0x38e0; + uint256 internal constant GATE_CHALLENGE_1 = 0x3900; + uint256 internal constant GATE_CHALLENGE_2 = 0x3920; + uint256 internal constant GATE_CHALLENGE_3 = 0x3940; + uint256 internal constant GATE_CHALLENGE_4 = 0x3960; + uint256 internal constant GATE_CHALLENGE_5 = 0x3980; + uint256 internal constant GATE_CHALLENGE_6 = 0x39a0; + uint256 internal constant GATE_CHALLENGE_7 = 0x39c0; + uint256 internal constant GATE_CHALLENGE_8 = 0x39e0; + uint256 internal constant GATE_CHALLENGE_9 = 0x3a00; + uint256 internal constant GATE_CHALLENGE_10 = 0x3a20; + uint256 internal constant GATE_CHALLENGE_11 = 0x3a40; + uint256 internal constant GATE_CHALLENGE_12 = 0x3a60; + uint256 internal constant GATE_CHALLENGE_13 = 0x3a80; + uint256 internal constant GATE_CHALLENGE_14 = 0x3aa0; + uint256 internal constant SUM_U_CHALLENGE_0 = 0x3ac0; + uint256 internal constant SUM_U_CHALLENGE_1 = 0x3ae0; + uint256 internal constant SUM_U_CHALLENGE_2 = 0x3b00; + uint256 internal constant SUM_U_CHALLENGE_3 = 0x3b20; + uint256 internal constant SUM_U_CHALLENGE_4 = 0x3b40; + uint256 internal constant SUM_U_CHALLENGE_5 = 0x3b60; + uint256 internal constant SUM_U_CHALLENGE_6 = 0x3b80; + uint256 internal constant SUM_U_CHALLENGE_7 = 0x3ba0; + uint256 internal constant SUM_U_CHALLENGE_8 = 0x3bc0; + uint256 internal constant SUM_U_CHALLENGE_9 = 0x3be0; + uint256 internal constant SUM_U_CHALLENGE_10 = 0x3c00; + uint256 internal constant SUM_U_CHALLENGE_11 = 0x3c20; + uint256 internal constant SUM_U_CHALLENGE_12 = 0x3c40; + uint256 internal constant SUM_U_CHALLENGE_13 = 0x3c60; + uint256 internal constant SUM_U_CHALLENGE_14 = 0x3c80; /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* CHALLENGES - COMPLETE */ @@ -462,134 +463,134 @@ contract BlakeOptHonkVerifier is IVerifier { /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* SUMCHECK - RUNTIME MEMORY - BARYCENTRIC */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - uint256 internal constant BARYCENTRIC_LAGRANGE_DENOMINATOR_0_LOC = 0x3c80; - uint256 internal constant BARYCENTRIC_LAGRANGE_DENOMINATOR_1_LOC = 0x3ca0; - uint256 internal constant BARYCENTRIC_LAGRANGE_DENOMINATOR_2_LOC = 0x3cc0; - uint256 internal constant BARYCENTRIC_LAGRANGE_DENOMINATOR_3_LOC = 0x3ce0; - uint256 internal constant BARYCENTRIC_LAGRANGE_DENOMINATOR_4_LOC = 0x3d00; - uint256 internal constant BARYCENTRIC_LAGRANGE_DENOMINATOR_5_LOC = 0x3d20; - uint256 internal constant BARYCENTRIC_LAGRANGE_DENOMINATOR_6_LOC = 0x3d40; - uint256 internal constant BARYCENTRIC_LAGRANGE_DENOMINATOR_7_LOC = 0x3d60; - uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_0_0_LOC = 0x3d80; - uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_0_1_LOC = 0x3da0; - uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_0_2_LOC = 0x3dc0; - uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_0_3_LOC = 0x3de0; - uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_0_4_LOC = 0x3e00; - uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_0_5_LOC = 0x3e20; - uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_0_6_LOC = 0x3e40; - uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_0_7_LOC = 0x3e60; - uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_1_0_LOC = 0x3e80; - uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_1_1_LOC = 0x3ea0; - uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_1_2_LOC = 0x3ec0; - uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_1_3_LOC = 0x3ee0; - uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_1_4_LOC = 0x3f00; - uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_1_5_LOC = 0x3f20; - uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_1_6_LOC = 0x3f40; - uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_1_7_LOC = 0x3f60; - uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_2_0_LOC = 0x3f80; - uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_2_1_LOC = 0x3fa0; - uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_2_2_LOC = 0x3fc0; - uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_2_3_LOC = 0x3fe0; - uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_2_4_LOC = 0x4000; - uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_2_5_LOC = 0x4020; - uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_2_6_LOC = 0x4040; - uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_2_7_LOC = 0x4060; - uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_3_0_LOC = 0x4080; - uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_3_1_LOC = 0x40a0; - uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_3_2_LOC = 0x40c0; - uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_3_3_LOC = 0x40e0; - uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_3_4_LOC = 0x4100; - uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_3_5_LOC = 0x4120; - uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_3_6_LOC = 0x4140; - uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_3_7_LOC = 0x4160; - uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_4_0_LOC = 0x4180; - uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_4_1_LOC = 0x41a0; - uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_4_2_LOC = 0x41c0; - uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_4_3_LOC = 0x41e0; - uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_4_4_LOC = 0x4200; - uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_4_5_LOC = 0x4220; - uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_4_6_LOC = 0x4240; - uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_4_7_LOC = 0x4260; - uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_5_0_LOC = 0x4280; - uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_5_1_LOC = 0x42a0; - uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_5_2_LOC = 0x42c0; - uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_5_3_LOC = 0x42e0; - uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_5_4_LOC = 0x4300; - uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_5_5_LOC = 0x4320; - uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_5_6_LOC = 0x4340; - uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_5_7_LOC = 0x4360; - uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_6_0_LOC = 0x4380; - uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_6_1_LOC = 0x43a0; - uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_6_2_LOC = 0x43c0; - uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_6_3_LOC = 0x43e0; - uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_6_4_LOC = 0x4400; - uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_6_5_LOC = 0x4420; - uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_6_6_LOC = 0x4440; - uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_6_7_LOC = 0x4460; - uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_7_0_LOC = 0x4480; - uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_7_1_LOC = 0x44a0; - uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_7_2_LOC = 0x44c0; - uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_7_3_LOC = 0x44e0; - uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_7_4_LOC = 0x4500; - uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_7_5_LOC = 0x4520; - uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_7_6_LOC = 0x4540; - uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_7_7_LOC = 0x4560; - uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_8_0_LOC = 0x4580; - uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_8_1_LOC = 0x45a0; - uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_8_2_LOC = 0x45c0; - uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_8_3_LOC = 0x45e0; - uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_8_4_LOC = 0x4600; - uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_8_5_LOC = 0x4620; - uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_8_6_LOC = 0x4640; - uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_8_7_LOC = 0x4660; - uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_9_0_LOC = 0x4680; - uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_9_1_LOC = 0x46a0; - uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_9_2_LOC = 0x46c0; - uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_9_3_LOC = 0x46e0; - uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_9_4_LOC = 0x4700; - uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_9_5_LOC = 0x4720; - uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_9_6_LOC = 0x4740; - uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_9_7_LOC = 0x4760; - uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_10_0_LOC = 0x4780; - uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_10_1_LOC = 0x47a0; - uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_10_2_LOC = 0x47c0; - uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_10_3_LOC = 0x47e0; - uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_10_4_LOC = 0x4800; - uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_10_5_LOC = 0x4820; - uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_10_6_LOC = 0x4840; - uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_10_7_LOC = 0x4860; - uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_11_0_LOC = 0x4880; - uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_11_1_LOC = 0x48a0; - uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_11_2_LOC = 0x48c0; - uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_11_3_LOC = 0x48e0; - uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_11_4_LOC = 0x4900; - uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_11_5_LOC = 0x4920; - uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_11_6_LOC = 0x4940; - uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_11_7_LOC = 0x4960; - uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_12_0_LOC = 0x4980; - uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_12_1_LOC = 0x49a0; - uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_12_2_LOC = 0x49c0; - uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_12_3_LOC = 0x49e0; - uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_12_4_LOC = 0x4a00; - uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_12_5_LOC = 0x4a20; - uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_12_6_LOC = 0x4a40; - uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_12_7_LOC = 0x4a60; - uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_13_0_LOC = 0x4a80; - uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_13_1_LOC = 0x4aa0; - uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_13_2_LOC = 0x4ac0; - uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_13_3_LOC = 0x4ae0; - uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_13_4_LOC = 0x4b00; - uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_13_5_LOC = 0x4b20; - uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_13_6_LOC = 0x4b40; - uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_13_7_LOC = 0x4b60; - uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_14_0_LOC = 0x4b80; - uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_14_1_LOC = 0x4ba0; - uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_14_2_LOC = 0x4bc0; - uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_14_3_LOC = 0x4be0; - uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_14_4_LOC = 0x4c00; - uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_14_5_LOC = 0x4c20; - uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_14_6_LOC = 0x4c40; - uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_14_7_LOC = 0x4c60; + uint256 internal constant BARYCENTRIC_LAGRANGE_DENOMINATOR_0_LOC = 0x3ca0; + uint256 internal constant BARYCENTRIC_LAGRANGE_DENOMINATOR_1_LOC = 0x3cc0; + uint256 internal constant BARYCENTRIC_LAGRANGE_DENOMINATOR_2_LOC = 0x3ce0; + uint256 internal constant BARYCENTRIC_LAGRANGE_DENOMINATOR_3_LOC = 0x3d00; + uint256 internal constant BARYCENTRIC_LAGRANGE_DENOMINATOR_4_LOC = 0x3d20; + uint256 internal constant BARYCENTRIC_LAGRANGE_DENOMINATOR_5_LOC = 0x3d40; + uint256 internal constant BARYCENTRIC_LAGRANGE_DENOMINATOR_6_LOC = 0x3d60; + uint256 internal constant BARYCENTRIC_LAGRANGE_DENOMINATOR_7_LOC = 0x3d80; + uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_0_0_LOC = 0x3da0; + uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_0_1_LOC = 0x3dc0; + uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_0_2_LOC = 0x3de0; + uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_0_3_LOC = 0x3e00; + uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_0_4_LOC = 0x3e20; + uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_0_5_LOC = 0x3e40; + uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_0_6_LOC = 0x3e60; + uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_0_7_LOC = 0x3e80; + uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_1_0_LOC = 0x3ea0; + uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_1_1_LOC = 0x3ec0; + uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_1_2_LOC = 0x3ee0; + uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_1_3_LOC = 0x3f00; + uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_1_4_LOC = 0x3f20; + uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_1_5_LOC = 0x3f40; + uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_1_6_LOC = 0x3f60; + uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_1_7_LOC = 0x3f80; + uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_2_0_LOC = 0x3fa0; + uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_2_1_LOC = 0x3fc0; + uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_2_2_LOC = 0x3fe0; + uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_2_3_LOC = 0x4000; + uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_2_4_LOC = 0x4020; + uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_2_5_LOC = 0x4040; + uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_2_6_LOC = 0x4060; + uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_2_7_LOC = 0x4080; + uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_3_0_LOC = 0x40a0; + uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_3_1_LOC = 0x40c0; + uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_3_2_LOC = 0x40e0; + uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_3_3_LOC = 0x4100; + uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_3_4_LOC = 0x4120; + uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_3_5_LOC = 0x4140; + uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_3_6_LOC = 0x4160; + uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_3_7_LOC = 0x4180; + uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_4_0_LOC = 0x41a0; + uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_4_1_LOC = 0x41c0; + uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_4_2_LOC = 0x41e0; + uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_4_3_LOC = 0x4200; + uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_4_4_LOC = 0x4220; + uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_4_5_LOC = 0x4240; + uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_4_6_LOC = 0x4260; + uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_4_7_LOC = 0x4280; + uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_5_0_LOC = 0x42a0; + uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_5_1_LOC = 0x42c0; + uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_5_2_LOC = 0x42e0; + uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_5_3_LOC = 0x4300; + uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_5_4_LOC = 0x4320; + uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_5_5_LOC = 0x4340; + uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_5_6_LOC = 0x4360; + uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_5_7_LOC = 0x4380; + uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_6_0_LOC = 0x43a0; + uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_6_1_LOC = 0x43c0; + uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_6_2_LOC = 0x43e0; + uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_6_3_LOC = 0x4400; + uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_6_4_LOC = 0x4420; + uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_6_5_LOC = 0x4440; + uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_6_6_LOC = 0x4460; + uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_6_7_LOC = 0x4480; + uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_7_0_LOC = 0x44a0; + uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_7_1_LOC = 0x44c0; + uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_7_2_LOC = 0x44e0; + uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_7_3_LOC = 0x4500; + uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_7_4_LOC = 0x4520; + uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_7_5_LOC = 0x4540; + uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_7_6_LOC = 0x4560; + uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_7_7_LOC = 0x4580; + uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_8_0_LOC = 0x45a0; + uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_8_1_LOC = 0x45c0; + uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_8_2_LOC = 0x45e0; + uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_8_3_LOC = 0x4600; + uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_8_4_LOC = 0x4620; + uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_8_5_LOC = 0x4640; + uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_8_6_LOC = 0x4660; + uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_8_7_LOC = 0x4680; + uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_9_0_LOC = 0x46a0; + uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_9_1_LOC = 0x46c0; + uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_9_2_LOC = 0x46e0; + uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_9_3_LOC = 0x4700; + uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_9_4_LOC = 0x4720; + uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_9_5_LOC = 0x4740; + uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_9_6_LOC = 0x4760; + uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_9_7_LOC = 0x4780; + uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_10_0_LOC = 0x47a0; + uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_10_1_LOC = 0x47c0; + uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_10_2_LOC = 0x47e0; + uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_10_3_LOC = 0x4800; + uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_10_4_LOC = 0x4820; + uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_10_5_LOC = 0x4840; + uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_10_6_LOC = 0x4860; + uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_10_7_LOC = 0x4880; + uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_11_0_LOC = 0x48a0; + uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_11_1_LOC = 0x48c0; + uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_11_2_LOC = 0x48e0; + uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_11_3_LOC = 0x4900; + uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_11_4_LOC = 0x4920; + uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_11_5_LOC = 0x4940; + uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_11_6_LOC = 0x4960; + uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_11_7_LOC = 0x4980; + uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_12_0_LOC = 0x49a0; + uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_12_1_LOC = 0x49c0; + uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_12_2_LOC = 0x49e0; + uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_12_3_LOC = 0x4a00; + uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_12_4_LOC = 0x4a20; + uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_12_5_LOC = 0x4a40; + uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_12_6_LOC = 0x4a60; + uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_12_7_LOC = 0x4a80; + uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_13_0_LOC = 0x4aa0; + uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_13_1_LOC = 0x4ac0; + uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_13_2_LOC = 0x4ae0; + uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_13_3_LOC = 0x4b00; + uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_13_4_LOC = 0x4b20; + uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_13_5_LOC = 0x4b40; + uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_13_6_LOC = 0x4b60; + uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_13_7_LOC = 0x4b80; + uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_14_0_LOC = 0x4ba0; + uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_14_1_LOC = 0x4bc0; + uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_14_2_LOC = 0x4be0; + uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_14_3_LOC = 0x4c00; + uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_14_4_LOC = 0x4c20; + uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_14_5_LOC = 0x4c40; + uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_14_6_LOC = 0x4c60; + uint256 internal constant BARYCENTRIC_DENOMINATOR_INVERSES_14_7_LOC = 0x4c80; /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* SUMCHECK - RUNTIME MEMORY - BARYCENTRIC COMPLETE */ @@ -598,34 +599,35 @@ contract BlakeOptHonkVerifier is IVerifier { /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* SUMCHECK - RUNTIME MEMORY - SUBRELATION EVALUATIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - uint256 internal constant SUBRELATION_EVAL_0_LOC = 0x4c80; - uint256 internal constant SUBRELATION_EVAL_1_LOC = 0x4ca0; - uint256 internal constant SUBRELATION_EVAL_2_LOC = 0x4cc0; - uint256 internal constant SUBRELATION_EVAL_3_LOC = 0x4ce0; - uint256 internal constant SUBRELATION_EVAL_4_LOC = 0x4d00; - uint256 internal constant SUBRELATION_EVAL_5_LOC = 0x4d20; - uint256 internal constant SUBRELATION_EVAL_6_LOC = 0x4d40; - uint256 internal constant SUBRELATION_EVAL_7_LOC = 0x4d60; - uint256 internal constant SUBRELATION_EVAL_8_LOC = 0x4d80; - uint256 internal constant SUBRELATION_EVAL_9_LOC = 0x4da0; - uint256 internal constant SUBRELATION_EVAL_10_LOC = 0x4dc0; - uint256 internal constant SUBRELATION_EVAL_11_LOC = 0x4de0; - uint256 internal constant SUBRELATION_EVAL_12_LOC = 0x4e00; - uint256 internal constant SUBRELATION_EVAL_13_LOC = 0x4e20; - uint256 internal constant SUBRELATION_EVAL_14_LOC = 0x4e40; - uint256 internal constant SUBRELATION_EVAL_15_LOC = 0x4e60; - uint256 internal constant SUBRELATION_EVAL_16_LOC = 0x4e80; - uint256 internal constant SUBRELATION_EVAL_17_LOC = 0x4ea0; - uint256 internal constant SUBRELATION_EVAL_18_LOC = 0x4ec0; - uint256 internal constant SUBRELATION_EVAL_19_LOC = 0x4ee0; - uint256 internal constant SUBRELATION_EVAL_20_LOC = 0x4f00; - uint256 internal constant SUBRELATION_EVAL_21_LOC = 0x4f20; - uint256 internal constant SUBRELATION_EVAL_22_LOC = 0x4f40; - uint256 internal constant SUBRELATION_EVAL_23_LOC = 0x4f60; - uint256 internal constant SUBRELATION_EVAL_24_LOC = 0x4f80; - uint256 internal constant SUBRELATION_EVAL_25_LOC = 0x4fa0; - uint256 internal constant SUBRELATION_EVAL_26_LOC = 0x4fc0; - uint256 internal constant SUBRELATION_EVAL_27_LOC = 0x4fe0; + uint256 internal constant SUBRELATION_EVAL_0_LOC = 0x4ca0; + uint256 internal constant SUBRELATION_EVAL_1_LOC = 0x4cc0; + uint256 internal constant SUBRELATION_EVAL_2_LOC = 0x4ce0; + uint256 internal constant SUBRELATION_EVAL_3_LOC = 0x4d00; + uint256 internal constant SUBRELATION_EVAL_4_LOC = 0x4d20; + uint256 internal constant SUBRELATION_EVAL_5_LOC = 0x4d40; + uint256 internal constant SUBRELATION_EVAL_6_LOC = 0x4d60; + uint256 internal constant SUBRELATION_EVAL_7_LOC = 0x4d80; + uint256 internal constant SUBRELATION_EVAL_8_LOC = 0x4da0; + uint256 internal constant SUBRELATION_EVAL_9_LOC = 0x4dc0; + uint256 internal constant SUBRELATION_EVAL_10_LOC = 0x4de0; + uint256 internal constant SUBRELATION_EVAL_11_LOC = 0x4e00; + uint256 internal constant SUBRELATION_EVAL_12_LOC = 0x4e20; + uint256 internal constant SUBRELATION_EVAL_13_LOC = 0x4e40; + uint256 internal constant SUBRELATION_EVAL_14_LOC = 0x4e60; + uint256 internal constant SUBRELATION_EVAL_15_LOC = 0x4e80; + uint256 internal constant SUBRELATION_EVAL_16_LOC = 0x4ea0; + uint256 internal constant SUBRELATION_EVAL_17_LOC = 0x4ec0; + uint256 internal constant SUBRELATION_EVAL_18_LOC = 0x4ee0; + uint256 internal constant SUBRELATION_EVAL_19_LOC = 0x4f00; + uint256 internal constant SUBRELATION_EVAL_20_LOC = 0x4f20; + uint256 internal constant SUBRELATION_EVAL_21_LOC = 0x4f40; + uint256 internal constant SUBRELATION_EVAL_22_LOC = 0x4f60; + uint256 internal constant SUBRELATION_EVAL_23_LOC = 0x4f80; + uint256 internal constant SUBRELATION_EVAL_24_LOC = 0x4fa0; + uint256 internal constant SUBRELATION_EVAL_25_LOC = 0x4fc0; + uint256 internal constant SUBRELATION_EVAL_26_LOC = 0x4fe0; + uint256 internal constant SUBRELATION_EVAL_27_LOC = 0x5000; + uint256 internal constant SUBRELATION_EVAL_28_LOC = 0x5020; /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* SUMCHECK - RUNTIME MEMORY - SUBRELATION EVALUATIONS COMPLETE */ @@ -634,13 +636,13 @@ contract BlakeOptHonkVerifier is IVerifier { /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* SUMCHECK - RUNTIME MEMORY - SUBRELATION INTERMEDIATES */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - uint256 internal constant FINAL_ROUND_TARGET_LOC = 0x5000; - uint256 internal constant POW_PARTIAL_EVALUATION_LOC = 0x5020; - uint256 internal constant AUX_NON_NATIVE_FIELD_IDENTITY = 0x5040; - uint256 internal constant AUX_LIMB_ACCUMULATOR_IDENTITY = 0x5060; - uint256 internal constant AUX_RAM_CONSISTENCY_CHECK_IDENTITY = 0x5080; - uint256 internal constant AUX_ROM_CONSISTENCY_CHECK_IDENTITY = 0x50a0; - uint256 internal constant AUX_MEMORY_CHECK_IDENTITY = 0x50c0; + uint256 internal constant FINAL_ROUND_TARGET_LOC = 0x5040; + uint256 internal constant POW_PARTIAL_EVALUATION_LOC = 0x5060; + uint256 internal constant AUX_NON_NATIVE_FIELD_IDENTITY = 0x5080; + uint256 internal constant AUX_LIMB_ACCUMULATOR_IDENTITY = 0x50a0; + uint256 internal constant AUX_RAM_CONSISTENCY_CHECK_IDENTITY = 0x50c0; + uint256 internal constant AUX_ROM_CONSISTENCY_CHECK_IDENTITY = 0x50e0; + uint256 internal constant AUX_MEMORY_CHECK_IDENTITY = 0x5100; /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* SUMCHECK - RUNTIME MEMORY - COMPLETE */ @@ -654,21 +656,21 @@ contract BlakeOptHonkVerifier is IVerifier { /* SHPLEMINI - POWERS OF EVALUATION CHALLENGE */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// {{ UNROLL_SECTION_START POWERS_OF_EVALUATION_CHALLENGE }} - uint256 internal constant POWERS_OF_EVALUATION_CHALLENGE_0_LOC = 0x50e0; - uint256 internal constant POWERS_OF_EVALUATION_CHALLENGE_1_LOC = 0x5100; - uint256 internal constant POWERS_OF_EVALUATION_CHALLENGE_2_LOC = 0x5120; - uint256 internal constant POWERS_OF_EVALUATION_CHALLENGE_3_LOC = 0x5140; - uint256 internal constant POWERS_OF_EVALUATION_CHALLENGE_4_LOC = 0x5160; - uint256 internal constant POWERS_OF_EVALUATION_CHALLENGE_5_LOC = 0x5180; - uint256 internal constant POWERS_OF_EVALUATION_CHALLENGE_6_LOC = 0x51a0; - uint256 internal constant POWERS_OF_EVALUATION_CHALLENGE_7_LOC = 0x51c0; - uint256 internal constant POWERS_OF_EVALUATION_CHALLENGE_8_LOC = 0x51e0; - uint256 internal constant POWERS_OF_EVALUATION_CHALLENGE_9_LOC = 0x5200; - uint256 internal constant POWERS_OF_EVALUATION_CHALLENGE_10_LOC = 0x5220; - uint256 internal constant POWERS_OF_EVALUATION_CHALLENGE_11_LOC = 0x5240; - uint256 internal constant POWERS_OF_EVALUATION_CHALLENGE_12_LOC = 0x5260; - uint256 internal constant POWERS_OF_EVALUATION_CHALLENGE_13_LOC = 0x5280; - uint256 internal constant POWERS_OF_EVALUATION_CHALLENGE_14_LOC = 0x52a0; + uint256 internal constant POWERS_OF_EVALUATION_CHALLENGE_0_LOC = 0x5120; + uint256 internal constant POWERS_OF_EVALUATION_CHALLENGE_1_LOC = 0x5140; + uint256 internal constant POWERS_OF_EVALUATION_CHALLENGE_2_LOC = 0x5160; + uint256 internal constant POWERS_OF_EVALUATION_CHALLENGE_3_LOC = 0x5180; + uint256 internal constant POWERS_OF_EVALUATION_CHALLENGE_4_LOC = 0x51a0; + uint256 internal constant POWERS_OF_EVALUATION_CHALLENGE_5_LOC = 0x51c0; + uint256 internal constant POWERS_OF_EVALUATION_CHALLENGE_6_LOC = 0x51e0; + uint256 internal constant POWERS_OF_EVALUATION_CHALLENGE_7_LOC = 0x5200; + uint256 internal constant POWERS_OF_EVALUATION_CHALLENGE_8_LOC = 0x5220; + uint256 internal constant POWERS_OF_EVALUATION_CHALLENGE_9_LOC = 0x5240; + uint256 internal constant POWERS_OF_EVALUATION_CHALLENGE_10_LOC = 0x5260; + uint256 internal constant POWERS_OF_EVALUATION_CHALLENGE_11_LOC = 0x5280; + uint256 internal constant POWERS_OF_EVALUATION_CHALLENGE_12_LOC = 0x52a0; + uint256 internal constant POWERS_OF_EVALUATION_CHALLENGE_13_LOC = 0x52c0; + uint256 internal constant POWERS_OF_EVALUATION_CHALLENGE_14_LOC = 0x52e0; /// {{ UNROLL_SECTION_END POWERS_OF_EVALUATION_CHALLENGE }} /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ @@ -678,74 +680,74 @@ contract BlakeOptHonkVerifier is IVerifier { /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* SHPLEMINI - RUNTIME MEMORY - BATCH SCALARS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - uint256 internal constant BATCH_SCALAR_1_LOC = 0x52c0; - uint256 internal constant BATCH_SCALAR_2_LOC = 0x52e0; - uint256 internal constant BATCH_SCALAR_3_LOC = 0x5300; - uint256 internal constant BATCH_SCALAR_4_LOC = 0x5320; - uint256 internal constant BATCH_SCALAR_5_LOC = 0x5340; - uint256 internal constant BATCH_SCALAR_6_LOC = 0x5360; - uint256 internal constant BATCH_SCALAR_7_LOC = 0x5380; - uint256 internal constant BATCH_SCALAR_8_LOC = 0x53a0; - uint256 internal constant BATCH_SCALAR_9_LOC = 0x53c0; - uint256 internal constant BATCH_SCALAR_10_LOC = 0x53e0; - uint256 internal constant BATCH_SCALAR_11_LOC = 0x5400; - uint256 internal constant BATCH_SCALAR_12_LOC = 0x5420; - uint256 internal constant BATCH_SCALAR_13_LOC = 0x5440; - uint256 internal constant BATCH_SCALAR_14_LOC = 0x5460; - uint256 internal constant BATCH_SCALAR_15_LOC = 0x5480; - uint256 internal constant BATCH_SCALAR_16_LOC = 0x54a0; - uint256 internal constant BATCH_SCALAR_17_LOC = 0x54c0; - uint256 internal constant BATCH_SCALAR_18_LOC = 0x54e0; - uint256 internal constant BATCH_SCALAR_19_LOC = 0x5500; - uint256 internal constant BATCH_SCALAR_20_LOC = 0x5520; - uint256 internal constant BATCH_SCALAR_21_LOC = 0x5540; - uint256 internal constant BATCH_SCALAR_22_LOC = 0x5560; - uint256 internal constant BATCH_SCALAR_23_LOC = 0x5580; - uint256 internal constant BATCH_SCALAR_24_LOC = 0x55a0; - uint256 internal constant BATCH_SCALAR_25_LOC = 0x55c0; - uint256 internal constant BATCH_SCALAR_26_LOC = 0x55e0; - uint256 internal constant BATCH_SCALAR_27_LOC = 0x5600; - uint256 internal constant BATCH_SCALAR_28_LOC = 0x5620; - uint256 internal constant BATCH_SCALAR_29_LOC = 0x5640; - uint256 internal constant BATCH_SCALAR_30_LOC = 0x5660; - uint256 internal constant BATCH_SCALAR_31_LOC = 0x5680; - uint256 internal constant BATCH_SCALAR_32_LOC = 0x56a0; - uint256 internal constant BATCH_SCALAR_33_LOC = 0x56c0; - uint256 internal constant BATCH_SCALAR_34_LOC = 0x56e0; - uint256 internal constant BATCH_SCALAR_35_LOC = 0x5700; - uint256 internal constant BATCH_SCALAR_36_LOC = 0x5720; - uint256 internal constant BATCH_SCALAR_37_LOC = 0x5740; - uint256 internal constant BATCH_SCALAR_38_LOC = 0x5760; - uint256 internal constant BATCH_SCALAR_39_LOC = 0x5780; - uint256 internal constant BATCH_SCALAR_40_LOC = 0x57a0; - uint256 internal constant BATCH_SCALAR_41_LOC = 0x57c0; - uint256 internal constant BATCH_SCALAR_42_LOC = 0x57e0; - uint256 internal constant BATCH_SCALAR_43_LOC = 0x5800; - uint256 internal constant BATCH_SCALAR_44_LOC = 0x5820; - uint256 internal constant BATCH_SCALAR_45_LOC = 0x5840; - uint256 internal constant BATCH_SCALAR_46_LOC = 0x5860; - uint256 internal constant BATCH_SCALAR_47_LOC = 0x5880; - uint256 internal constant BATCH_SCALAR_48_LOC = 0x58a0; - uint256 internal constant BATCH_SCALAR_49_LOC = 0x58c0; - uint256 internal constant BATCH_SCALAR_50_LOC = 0x58e0; - uint256 internal constant BATCH_SCALAR_51_LOC = 0x5900; - uint256 internal constant BATCH_SCALAR_52_LOC = 0x5920; - uint256 internal constant BATCH_SCALAR_53_LOC = 0x5940; - uint256 internal constant BATCH_SCALAR_54_LOC = 0x5960; - uint256 internal constant BATCH_SCALAR_55_LOC = 0x5980; - uint256 internal constant BATCH_SCALAR_56_LOC = 0x59a0; - uint256 internal constant BATCH_SCALAR_57_LOC = 0x59c0; - uint256 internal constant BATCH_SCALAR_58_LOC = 0x59e0; - uint256 internal constant BATCH_SCALAR_59_LOC = 0x5a00; - uint256 internal constant BATCH_SCALAR_60_LOC = 0x5a20; - uint256 internal constant BATCH_SCALAR_61_LOC = 0x5a40; - uint256 internal constant BATCH_SCALAR_62_LOC = 0x5a60; - uint256 internal constant BATCH_SCALAR_63_LOC = 0x5a80; - uint256 internal constant BATCH_SCALAR_64_LOC = 0x5aa0; - uint256 internal constant BATCH_SCALAR_65_LOC = 0x5ac0; - uint256 internal constant BATCH_SCALAR_66_LOC = 0x5ae0; - uint256 internal constant BATCH_SCALAR_67_LOC = 0x5b00; - uint256 internal constant BATCH_SCALAR_68_LOC = 0x5b20; + uint256 internal constant BATCH_SCALAR_1_LOC = 0x5300; + uint256 internal constant BATCH_SCALAR_2_LOC = 0x5320; + uint256 internal constant BATCH_SCALAR_3_LOC = 0x5340; + uint256 internal constant BATCH_SCALAR_4_LOC = 0x5360; + uint256 internal constant BATCH_SCALAR_5_LOC = 0x5380; + uint256 internal constant BATCH_SCALAR_6_LOC = 0x53a0; + uint256 internal constant BATCH_SCALAR_7_LOC = 0x53c0; + uint256 internal constant BATCH_SCALAR_8_LOC = 0x53e0; + uint256 internal constant BATCH_SCALAR_9_LOC = 0x5400; + uint256 internal constant BATCH_SCALAR_10_LOC = 0x5420; + uint256 internal constant BATCH_SCALAR_11_LOC = 0x5440; + uint256 internal constant BATCH_SCALAR_12_LOC = 0x5460; + uint256 internal constant BATCH_SCALAR_13_LOC = 0x5480; + uint256 internal constant BATCH_SCALAR_14_LOC = 0x54a0; + uint256 internal constant BATCH_SCALAR_15_LOC = 0x54c0; + uint256 internal constant BATCH_SCALAR_16_LOC = 0x54e0; + uint256 internal constant BATCH_SCALAR_17_LOC = 0x5500; + uint256 internal constant BATCH_SCALAR_18_LOC = 0x5520; + uint256 internal constant BATCH_SCALAR_19_LOC = 0x5540; + uint256 internal constant BATCH_SCALAR_20_LOC = 0x5560; + uint256 internal constant BATCH_SCALAR_21_LOC = 0x5580; + uint256 internal constant BATCH_SCALAR_22_LOC = 0x55a0; + uint256 internal constant BATCH_SCALAR_23_LOC = 0x55c0; + uint256 internal constant BATCH_SCALAR_24_LOC = 0x55e0; + uint256 internal constant BATCH_SCALAR_25_LOC = 0x5600; + uint256 internal constant BATCH_SCALAR_26_LOC = 0x5620; + uint256 internal constant BATCH_SCALAR_27_LOC = 0x5640; + uint256 internal constant BATCH_SCALAR_28_LOC = 0x5660; + uint256 internal constant BATCH_SCALAR_29_LOC = 0x5680; + uint256 internal constant BATCH_SCALAR_30_LOC = 0x56a0; + uint256 internal constant BATCH_SCALAR_31_LOC = 0x56c0; + uint256 internal constant BATCH_SCALAR_32_LOC = 0x56e0; + uint256 internal constant BATCH_SCALAR_33_LOC = 0x5700; + uint256 internal constant BATCH_SCALAR_34_LOC = 0x5720; + uint256 internal constant BATCH_SCALAR_35_LOC = 0x5740; + uint256 internal constant BATCH_SCALAR_36_LOC = 0x5760; + uint256 internal constant BATCH_SCALAR_37_LOC = 0x5780; + uint256 internal constant BATCH_SCALAR_38_LOC = 0x57a0; + uint256 internal constant BATCH_SCALAR_39_LOC = 0x57c0; + uint256 internal constant BATCH_SCALAR_40_LOC = 0x57e0; + uint256 internal constant BATCH_SCALAR_41_LOC = 0x5800; + uint256 internal constant BATCH_SCALAR_42_LOC = 0x5820; + uint256 internal constant BATCH_SCALAR_43_LOC = 0x5840; + uint256 internal constant BATCH_SCALAR_44_LOC = 0x5860; + uint256 internal constant BATCH_SCALAR_45_LOC = 0x5880; + uint256 internal constant BATCH_SCALAR_46_LOC = 0x58a0; + uint256 internal constant BATCH_SCALAR_47_LOC = 0x58c0; + uint256 internal constant BATCH_SCALAR_48_LOC = 0x58e0; + uint256 internal constant BATCH_SCALAR_49_LOC = 0x5900; + uint256 internal constant BATCH_SCALAR_50_LOC = 0x5920; + uint256 internal constant BATCH_SCALAR_51_LOC = 0x5940; + uint256 internal constant BATCH_SCALAR_52_LOC = 0x5960; + uint256 internal constant BATCH_SCALAR_53_LOC = 0x5980; + uint256 internal constant BATCH_SCALAR_54_LOC = 0x59a0; + uint256 internal constant BATCH_SCALAR_55_LOC = 0x59c0; + uint256 internal constant BATCH_SCALAR_56_LOC = 0x59e0; + uint256 internal constant BATCH_SCALAR_57_LOC = 0x5a00; + uint256 internal constant BATCH_SCALAR_58_LOC = 0x5a20; + uint256 internal constant BATCH_SCALAR_59_LOC = 0x5a40; + uint256 internal constant BATCH_SCALAR_60_LOC = 0x5a60; + uint256 internal constant BATCH_SCALAR_61_LOC = 0x5a80; + uint256 internal constant BATCH_SCALAR_62_LOC = 0x5aa0; + uint256 internal constant BATCH_SCALAR_63_LOC = 0x5ac0; + uint256 internal constant BATCH_SCALAR_64_LOC = 0x5ae0; + uint256 internal constant BATCH_SCALAR_65_LOC = 0x5b00; + uint256 internal constant BATCH_SCALAR_66_LOC = 0x5b20; + uint256 internal constant BATCH_SCALAR_67_LOC = 0x5b40; + uint256 internal constant BATCH_SCALAR_68_LOC = 0x5b60; /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* SHPLEMINI - RUNTIME MEMORY - BATCH SCALARS COMPLETE */ @@ -1454,7 +1456,7 @@ contract BlakeOptHonkVerifier is IVerifier { // Compute powers of alpha: alpha^2, alpha^3, ..., alpha^26 let alpha_off_set := ALPHA_CHALLENGE_1 - for {} lt(alpha_off_set, add(ALPHA_CHALLENGE_26, 0x20)) {} { + for {} lt(alpha_off_set, add(ALPHA_CHALLENGE_27, 0x20)) {} { let prev_alpha := mload(sub(alpha_off_set, 0x20)) mstore(alpha_off_set, mulmod(prev_alpha, alpha, p)) alpha_off_set := add(alpha_off_set, 0x20) @@ -2863,6 +2865,16 @@ contract BlakeOptHonkVerifier is IVerifier { ) mstore(SUBRELATION_EVAL_3_LOC, acc) } + + // Contribution 4: z_perm initialization (lagrange_first * z_perm = 0) + { + let acc := mulmod( + mulmod(mload(LAGRANGE_FIRST_EVAL_LOC), mload(Z_PERM_EVAL_LOC), p), + mload(POW_PARTIAL_EVALUATION_LOC), + p + ) + mstore(SUBRELATION_EVAL_4_LOC, acc) + } } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ @@ -2926,9 +2938,9 @@ contract BlakeOptHonkVerifier is IVerifier { let read_tag_boolean_relation := mulmod(read_tag, addmod(read_tag, sub(p, 1), p), p) read_tag_boolean_relation := mulmod(read_tag_boolean_relation, mload(POW_PARTIAL_EVALUATION_LOC), p) - mstore(SUBRELATION_EVAL_4_LOC, accumulator_none) - mstore(SUBRELATION_EVAL_5_LOC, accumulator_one) - mstore(SUBRELATION_EVAL_6_LOC, read_tag_boolean_relation) + mstore(SUBRELATION_EVAL_5_LOC, accumulator_none) + mstore(SUBRELATION_EVAL_6_LOC, accumulator_one) + mstore(SUBRELATION_EVAL_7_LOC, read_tag_boolean_relation) } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ @@ -2951,7 +2963,7 @@ contract BlakeOptHonkVerifier is IVerifier { acc := mulmod(acc, addmod(delta_1, minus_three, p), p) acc := mulmod(acc, mload(QRANGE_EVAL_LOC), p) acc := mulmod(acc, mload(POW_PARTIAL_EVALUATION_LOC), p) - mstore(SUBRELATION_EVAL_7_LOC, acc) + mstore(SUBRELATION_EVAL_8_LOC, acc) } { @@ -2961,7 +2973,7 @@ contract BlakeOptHonkVerifier is IVerifier { acc := mulmod(acc, addmod(delta_2, minus_three, p), p) acc := mulmod(acc, mload(QRANGE_EVAL_LOC), p) acc := mulmod(acc, mload(POW_PARTIAL_EVALUATION_LOC), p) - mstore(SUBRELATION_EVAL_8_LOC, acc) + mstore(SUBRELATION_EVAL_9_LOC, acc) } { @@ -2971,7 +2983,7 @@ contract BlakeOptHonkVerifier is IVerifier { acc := mulmod(acc, addmod(delta_3, minus_three, p), p) acc := mulmod(acc, mload(QRANGE_EVAL_LOC), p) acc := mulmod(acc, mload(POW_PARTIAL_EVALUATION_LOC), p) - mstore(SUBRELATION_EVAL_9_LOC, acc) + mstore(SUBRELATION_EVAL_10_LOC, acc) } { @@ -2981,7 +2993,7 @@ contract BlakeOptHonkVerifier is IVerifier { acc := mulmod(acc, addmod(delta_4, minus_three, p), p) acc := mulmod(acc, mload(QRANGE_EVAL_LOC), p) acc := mulmod(acc, mload(POW_PARTIAL_EVALUATION_LOC), p) - mstore(SUBRELATION_EVAL_10_LOC, acc) + mstore(SUBRELATION_EVAL_11_LOC, acc) } } @@ -3006,7 +3018,7 @@ contract BlakeOptHonkVerifier is IVerifier { let eval := mulmod(x_add_identity, mload(POW_PARTIAL_EVALUATION_LOC), p) eval := mulmod(eval, mload(QELLIPTIC_EVAL_LOC), p) eval := mulmod(eval, addmod(1, sub(p, mload(EC_Q_IS_DOUBLE)), p), p) - mstore(SUBRELATION_EVAL_11_LOC, eval) + mstore(SUBRELATION_EVAL_12_LOC, eval) } { @@ -3023,7 +3035,7 @@ contract BlakeOptHonkVerifier is IVerifier { let eval := mulmod(y_add_identity, mload(POW_PARTIAL_EVALUATION_LOC), p) eval := mulmod(eval, mload(QELLIPTIC_EVAL_LOC), p) eval := mulmod(eval, addmod(1, sub(p, mload(EC_Q_IS_DOUBLE)), p), p) - mstore(SUBRELATION_EVAL_12_LOC, eval) + mstore(SUBRELATION_EVAL_13_LOC, eval) } { @@ -3039,10 +3051,10 @@ contract BlakeOptHonkVerifier is IVerifier { let acc := mulmod(ep_x_double_identity, mload(POW_PARTIAL_EVALUATION_LOC), p) acc := mulmod(mulmod(acc, mload(QELLIPTIC_EVAL_LOC), p), mload(EC_Q_IS_DOUBLE), p) - acc := addmod(acc, mload(SUBRELATION_EVAL_11_LOC), p) + acc := addmod(acc, mload(SUBRELATION_EVAL_12_LOC), p) // Add to existing contribution - and double check that numbers here - mstore(SUBRELATION_EVAL_11_LOC, acc) + mstore(SUBRELATION_EVAL_12_LOC, acc) } { @@ -3065,10 +3077,10 @@ contract BlakeOptHonkVerifier is IVerifier { 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) - acc := addmod(acc, mload(SUBRELATION_EVAL_12_LOC), p) + acc := addmod(acc, mload(SUBRELATION_EVAL_13_LOC), p) // Add to existing contribution - and double check that numbers here - mstore(SUBRELATION_EVAL_12_LOC, acc) + mstore(SUBRELATION_EVAL_13_LOC, acc) } } @@ -3175,7 +3187,7 @@ contract BlakeOptHonkVerifier is IVerifier { mulmod(record_delta, addmod(1, sub(p, index_delta), p), p) mstore( - SUBRELATION_EVAL_14_LOC, + SUBRELATION_EVAL_15_LOC, mulmod( adjacent_values_match_if_adjacent_indices_match, mulmod( @@ -3193,7 +3205,7 @@ contract BlakeOptHonkVerifier is IVerifier { // ROM_CONSISTENCY_CHECK_2 mstore( - SUBRELATION_EVAL_15_LOC, + SUBRELATION_EVAL_16_LOC, mulmod( index_is_monotonically_increasing, mulmod( @@ -3289,7 +3301,7 @@ contract BlakeOptHonkVerifier is IVerifier { ) mstore( - SUBRELATION_EVAL_16_LOC, + SUBRELATION_EVAL_17_LOC, mulmod( adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation, scaled_activation_selector, @@ -3298,12 +3310,12 @@ contract BlakeOptHonkVerifier is IVerifier { ) mstore( - SUBRELATION_EVAL_17_LOC, + SUBRELATION_EVAL_18_LOC, mulmod(index_is_monotonically_increasing, scaled_activation_selector, p) ) mstore( - SUBRELATION_EVAL_18_LOC, + SUBRELATION_EVAL_19_LOC, mulmod(next_gate_access_type_is_boolean, scaled_activation_selector, p) ) @@ -3360,7 +3372,7 @@ contract BlakeOptHonkVerifier is IVerifier { mulmod(mload(QMEMORY_EVAL_LOC), mload(POW_PARTIAL_EVALUATION_LOC), p), p ) - mstore(SUBRELATION_EVAL_13_LOC, memory_identity) + mstore(SUBRELATION_EVAL_14_LOC, memory_identity) } } } @@ -3507,7 +3519,7 @@ contract BlakeOptHonkVerifier is IVerifier { p ) - mstore(SUBRELATION_EVAL_19_LOC, nnf_identity) + mstore(SUBRELATION_EVAL_20_LOC, nnf_identity) } /* @@ -3560,22 +3572,22 @@ contract BlakeOptHonkVerifier is IVerifier { mulmod(mload(QPOSEIDON2_EXTERNAL_EVAL_LOC), mload(POW_PARTIAL_EVALUATION_LOC), p) mstore( - SUBRELATION_EVAL_20_LOC, + SUBRELATION_EVAL_21_LOC, mulmod(q_pos_by_scaling, addmod(v1, sub(p, mload(W1_SHIFT_EVAL_LOC)), p), p) ) mstore( - SUBRELATION_EVAL_21_LOC, + SUBRELATION_EVAL_22_LOC, mulmod(q_pos_by_scaling, addmod(v2, sub(p, mload(W2_SHIFT_EVAL_LOC)), p), p) ) mstore( - SUBRELATION_EVAL_22_LOC, + SUBRELATION_EVAL_23_LOC, mulmod(q_pos_by_scaling, addmod(v3, sub(p, mload(W3_SHIFT_EVAL_LOC)), p), p) ) mstore( - SUBRELATION_EVAL_23_LOC, + SUBRELATION_EVAL_24_LOC, mulmod(q_pos_by_scaling, addmod(v4, sub(p, mload(W4_SHIFT_EVAL_LOC)), p), p) ) } @@ -3603,25 +3615,25 @@ contract BlakeOptHonkVerifier is IVerifier { let v1 := addmod(mulmod(u1, POS_INTERNAL_MATRIX_D_0, p), u_sum, p) mstore( - SUBRELATION_EVAL_24_LOC, + SUBRELATION_EVAL_25_LOC, mulmod(q_pos_by_scaling, addmod(v1, sub(p, mload(W1_SHIFT_EVAL_LOC)), p), p) ) let v2 := addmod(mulmod(u2, POS_INTERNAL_MATRIX_D_1, p), u_sum, p) mstore( - SUBRELATION_EVAL_25_LOC, + SUBRELATION_EVAL_26_LOC, mulmod(q_pos_by_scaling, addmod(v2, sub(p, mload(W2_SHIFT_EVAL_LOC)), p), p) ) let v3 := addmod(mulmod(u3, POS_INTERNAL_MATRIX_D_2, p), u_sum, p) mstore( - SUBRELATION_EVAL_26_LOC, + SUBRELATION_EVAL_27_LOC, mulmod(q_pos_by_scaling, addmod(v3, sub(p, mload(W3_SHIFT_EVAL_LOC)), p), p) ) let v4 := addmod(mulmod(u4, POS_INTERNAL_MATRIX_D_3, p), u_sum, p) mstore( - SUBRELATION_EVAL_27_LOC, + SUBRELATION_EVAL_28_LOC, mulmod(q_pos_by_scaling, addmod(v4, sub(p, mload(W4_SHIFT_EVAL_LOC)), p), p) ) } @@ -3770,6 +3782,11 @@ contract BlakeOptHonkVerifier is IVerifier { mulmod(mload(SUBRELATION_EVAL_27_LOC), mload(ALPHA_CHALLENGE_26), p), p ) + accumulator := addmod( + accumulator, + mulmod(mload(SUBRELATION_EVAL_28_LOC), mload(ALPHA_CHALLENGE_27), p), + p + ) let sumcheck_valid := eq(accumulator, mload(FINAL_ROUND_TARGET_LOC))