Skip to content

Commit 05cebda

Browse files
authored
fix: minor issues in bigfield (#22216)
Fixes minor issues found by claudebox in bigfield. Mostly tightens things around range constraints and adds/updates comments.
1 parent e882aec commit 05cebda

5 files changed

Lines changed: 33 additions & 28 deletions

File tree

barretenberg/cpp/scripts/test_chonk_standalone_vks_havent_changed.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ script_path="$root/barretenberg/cpp/scripts/test_chonk_standalone_vks_havent_cha
2121
# - Generate a hash for versioning: sha256sum bb-chonk-inputs.tar.gz
2222
# - 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
2323
# Note: In case of the "Test suite failed to run ... Unexpected token 'with' " error, need to run: docker pull aztecprotocol/build:3.0
24-
pinned_short_hash="aafc0a7e"
24+
pinned_short_hash="7f8e5859"
2525
pinned_chonk_inputs_url="https://aztec-ci-artifacts.s3.us-east-2.amazonaws.com/protocol/bb-chonk-inputs-${pinned_short_hash}.tar.gz"
2626

2727
function update_pinned_hash_in_script {

barretenberg/cpp/src/barretenberg/dsl/acir_format/gate_count_constants.hpp

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ template <typename Builder> inline constexpr size_t AES128_ENCRYPTION = 1559 + Z
3636
// contain only 2 of the values set for ECCVM (hence the difference of two gates between Ultra and Mega builders).
3737
template <typename Builder> inline constexpr size_t ECDSA_SECP256K1 = 42837 + ZERO_GATE;
3838
template <typename Builder>
39-
inline constexpr size_t ECDSA_SECP256R1 = 72612 + ZERO_GATE + (IsMegaBuilder<Builder> ? 2 : 0);
39+
inline constexpr size_t ECDSA_SECP256R1 = 72611 + ZERO_GATE + (IsMegaBuilder<Builder> ? 2 : 0);
4040

4141
template <typename Builder> inline constexpr size_t BLAKE2S = 2952 + ZERO_GATE + MEGA_OFFSET<Builder>;
4242
template <typename Builder> inline constexpr size_t BLAKE3 = 2158 + ZERO_GATE + MEGA_OFFSET<Builder>;
@@ -55,7 +55,7 @@ template <typename Builder> inline constexpr size_t ASSERT_EQUALITY = ZERO_GATE
5555
// Honk Recursion Constants
5656
// ========================================
5757

58-
inline constexpr size_t ROOT_ROLLUP_GATE_COUNT = 12904952;
58+
inline constexpr size_t ROOT_ROLLUP_GATE_COUNT = 12904885;
5959

6060
template <typename RecursiveFlavor>
6161
constexpr std::tuple<size_t, size_t> HONK_RECURSION_CONSTANTS(
@@ -67,18 +67,18 @@ constexpr std::tuple<size_t, size_t> HONK_RECURSION_CONSTANTS(
6767
if constexpr (std::is_same_v<RecursiveFlavor, bb::UltraRecursiveFlavor_<UltraCircuitBuilder>>) {
6868
switch (mode) {
6969
case PredicateTestCase::ConstantTrue:
70-
return std::make_tuple(681794, 0);
70+
return std::make_tuple(681762, 0);
7171
case PredicateTestCase::WitnessTrue:
7272
case PredicateTestCase::WitnessFalse:
73-
return std::make_tuple(682851, 0);
73+
return std::make_tuple(682819, 0);
7474
}
7575
} else if constexpr (std::is_same_v<RecursiveFlavor, bb::UltraZKRecursiveFlavor_<UltraCircuitBuilder>>) {
7676
switch (mode) {
7777
case PredicateTestCase::ConstantTrue:
78-
return std::make_tuple(703951, 0);
78+
return std::make_tuple(703917, 0);
7979
case PredicateTestCase::WitnessTrue:
8080
case PredicateTestCase::WitnessFalse:
81-
return std::make_tuple(705104, 0);
81+
return std::make_tuple(705070, 0);
8282
}
8383
} else if constexpr (std::is_same_v<RecursiveFlavor, bb::UltraRecursiveFlavor_<MegaCircuitBuilder>>) {
8484
switch (mode) {
@@ -100,7 +100,7 @@ constexpr std::tuple<size_t, size_t> HONK_RECURSION_CONSTANTS(
100100
if (mode != PredicateTestCase::ConstantTrue) {
101101
bb::assert_failure("Unhandled mode in MegaZKRecursiveFlavor.");
102102
}
103-
return std::make_tuple(781933, 0);
103+
return std::make_tuple(781910, 0);
104104
} else {
105105
bb::assert_failure("Unhandled recursive flavor.");
106106
}
@@ -113,7 +113,7 @@ constexpr std::tuple<size_t, size_t> HONK_RECURSION_CONSTANTS(
113113
// ========================================
114114

115115
// Gate count for Chonk recursive verification (Ultra with RollupIO)
116-
inline constexpr size_t CHONK_RECURSION_GATES = 1491513;
116+
inline constexpr size_t CHONK_RECURSION_GATES = 1491408;
117117

118118
// ========================================
119119
// Hypernova Recursion Constants
@@ -147,7 +147,7 @@ inline constexpr size_t HIDING_KERNEL_ULTRA_OPS = 127;
147147
// ========================================
148148

149149
// Gate count for ECCVM recursive verifier (Ultra-arithmetized)
150-
inline constexpr size_t ECCVM_RECURSIVE_VERIFIER_GATE_COUNT = 224206;
150+
inline constexpr size_t ECCVM_RECURSIVE_VERIFIER_GATE_COUNT = 224162;
151151

152152
// ========================================
153153
// Goblin AVM Recursive Verifier Constants

barretenberg/cpp/src/barretenberg/relations/non_native_field_relation.hpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ template <typename FF_> class NonNativeFieldRelationImpl {
9292

9393
// Bigfield Product Gate 2 (selected by q_2 * q_4):
9494
// Computes cross-term contributions in limb multiplication.
95-
// Formula: (w_1 * w_2') + (w_1' * w_2) + (w_1 * w_4 + w_2 * w_3 - w_3') * 2^68 - w_3 - w_4' = 0
95+
// Formula: (w_1 * w_2') + (w_1' * w_2) + (w_1 * w_4 + w_2 * w_3 - w_3') * 2^68 - w_4' = 0
9696
// where primed values (') denote shifted wires from the next row.
9797
auto limb_subproduct = w_1_m * w_2_shift_m + w_1_shift_m * w_2_m;
9898
auto non_native_field_gate_2_m = (w_1_m * w_4_m + w_2_m * w_3_m - w_3_shift_m);
@@ -103,7 +103,7 @@ template <typename FF_> class NonNativeFieldRelationImpl {
103103

104104
// Bigfield Product Gate 1 (selected by q_2 * q_3):
105105
// Accumulates limb products with 2^68 scaling for high-order terms.
106-
// Formula: (w_1 * w_2') + (w_1' * w_2) * 2^68 + (w_1' * w_2') - w_3 - w_4 = 0
106+
// Formula: (w_1 * w_2' + w_1' * w_2) * 2^68 + (w_1' * w_2') - w_3 - w_4 = 0
107107
limb_subproduct *= LIMB_SIZE;
108108
limb_subproduct += (w_1_shift_m * w_2_shift_m);
109109
auto non_native_field_gate_1_m = limb_subproduct;

barretenberg/cpp/src/barretenberg/stdlib/primitives/bigfield/bigfield_impl.hpp

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -273,7 +273,7 @@ template <typename Builder, typename T> bigfield<Builder, T>::bigfield(const byt
273273
const auto res = bigfield::unsafe_construct_from_limbs(limb0, limb1, limb2, limb3, true);
274274

275275
const auto num_last_limb_bits = 256 - (NUM_LIMB_BITS * 3);
276-
res.binary_basis_limbs[3].maximum_value = (uint64_t(1) << num_last_limb_bits);
276+
res.binary_basis_limbs[3].maximum_value = (uint64_t(1) << num_last_limb_bits) - 1;
277277
*this = res;
278278
set_origin_tag(bytes.get_origin_tag());
279279
}
@@ -1992,14 +1992,14 @@ void bigfield<Builder, T>::unsafe_assert_less_than(const uint256_t& upper_limit,
19921992
r2.get_witness_index(),
19931993
r3.get_witness_index(),
19941994
static_cast<size_t>(NUM_LIMB_BITS),
1995-
static_cast<size_t>(NUM_LIMB_BITS),
1995+
static_cast<size_t>(NUM_LAST_LIMB_BITS),
19961996
msg == "bigfield::unsafe_assert_less_than" ? "bigfield::unsafe_assert_less_than: r2 or r3 too large" : msg);
19971997
}
19981998

19991999
// check elements are equal mod p by proving their integer difference is a multiple of p.
20002000
// This relies on the minus operator for a-b increasing a by a multiple of p large enough so diff is non-negative
20012001
// When one of the elements is a constant and another is a witness we check equality of limbs, so if the witness
2002-
// bigfield element is in an unreduced form, it needs to be reduced first. We don't have automatice reduced form
2002+
// bigfield element is in an unreduced form, it needs to be reduced first. We don't have automatic reduced form
20032003
// detection for now, so it is up to the circuit writer to detect this
20042004
template <typename Builder, typename T>
20052005
void bigfield<Builder, T>::assert_equal(const bigfield& other, std::string const& msg) const
@@ -2010,13 +2010,16 @@ void bigfield<Builder, T>::assert_equal(const bigfield& other, std::string const
20102010
BB_ASSERT_EQ(get_value(), other.get_value(), "We expect constants to be less than the target modulus");
20112011
return;
20122012
} else if (other.is_constant()) {
2013-
// NOTE(https://github.com/AztecProtocol/barretenberg/issues/998): This can lead to a situation where
2014-
// an honest prover cannot satisfy the constraints, because `this` is not reduced, but `other` is, i.e.,
2015-
// `this` = kp + r and `other` = r
2016-
// where k is a positive integer. In such a case, the prover cannot satisfy the constraints
2017-
// because the limb-differences would not be 0 mod r. Therefore, an honest prover needs to make sure that
2018-
// `this` is reduced before calling this method. Also `other` should never be greater than the modulus by
2019-
// design. As a precaution, we assert that the circuit-constant `other` is less than the modulus.
2013+
// NOTE(https://github.com/AztecProtocol/barretenberg/issues/998): This does a limb-wise integer
2014+
// comparison, so `this` must already be in reduced form (value in [0, p)) before calling this method.
2015+
// If `this = kp + r` and `other = r`, the limbs differ and an honest prover cannot satisfy the
2016+
// constraints. Callers are responsible for calling self_reduce() first when necessary; we omit it
2017+
// here to avoid adding spurious gates in the common case where `this` is already reduced.
2018+
// `other` should never exceed the modulus by design; we assert this as a precaution.
2019+
BB_ASSERT_LT(get_value(),
2020+
modulus_u512,
2021+
"bigfield::assert_equal: 'this' is not reduced (value >= p). Call self_reduce() before comparing "
2022+
"against a constant.");
20202023
BB_ASSERT_LT(other.get_value(), modulus_u512);
20212024
field_t<Builder> t0 = (binary_basis_limbs[0].element - other.binary_basis_limbs[0].element);
20222025
field_t<Builder> t1 = (binary_basis_limbs[1].element - other.binary_basis_limbs[1].element);
@@ -2072,8 +2075,10 @@ void bigfield<Builder, T>::assert_equal(const bigfield& other, std::string const
20722075

20732076
// construct a proof that points are different mod p, when they are different mod r
20742077
// WARNING: This method doesn't have perfect completeness - for points equal mod r (or with certain difference kp
2075-
// mod r) but different mod p, you can't construct a proof. The chances of an honest prover running afoul of this
2076-
// condition are extremely small (TODO: compute probability) Note also that the number of constraints depends on how
2078+
// mod r) but different mod p, you can't construct a proof. The failure probability is at most
2079+
// (L + R + 1) / r where L = floor(a.max / p), R = floor(b.max / p), r = native field size (~2^254).
2080+
// With max bounded by 2^256 - 1 and p >= 2^249, we get L,R <= 127, so probability < 2^{-246}.
2081+
// Note also that the number of constraints depends on how
20772082
// much the values have overflown beyond p e.g. due to an addition chain The function is based on the following.
20782083
// Suppose a-b = 0 mod p. Then a-b = k*p for k in a range [-R,L] for largest L and R such that L*p>= a, R*p>=b.
20792084
// And also a-b = k*p mod r for such k. Thus we can verify a-b is non-zero mod p by taking the product of such values
@@ -2147,7 +2152,7 @@ template <typename Builder, typename T> void bigfield<Builder, T>::self_reduce()
21472152

21482153
BB_ASSERT_LT((uint1024_t(1) << maximum_quotient_bits) * uint1024_t(modulus_u512) + DEFAULT_MAXIMUM_REMAINDER,
21492154
get_maximum_crt_product());
2150-
quotient.binary_basis_limbs[0] = Limb(quotient_limb, uint256_t(1) << maximum_quotient_bits);
2155+
quotient.binary_basis_limbs[0] = Limb(quotient_limb, (uint256_t(1) << maximum_quotient_bits) - 1);
21512156
quotient.binary_basis_limbs[1] = Limb(field_t<Builder>::from_witness_index(context, context->zero_idx()), 0);
21522157
quotient.binary_basis_limbs[2] = Limb(field_t<Builder>::from_witness_index(context, context->zero_idx()), 0);
21532158
quotient.binary_basis_limbs[3] = Limb(field_t<Builder>::from_witness_index(context, context->zero_idx()), 0);

barretenberg/cpp/src/barretenberg/stdlib/primitives/field/field_conversion.test.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -356,7 +356,7 @@ TYPED_TEST(stdlib_field_conversion, GateCountScalarDeserialization)
356356
TYPED_TEST(stdlib_field_conversion, GateCountBigfieldDeserialization)
357357
{
358358
// Deserializing a single bigfield element is expensive due to creating new ranges for range constraints
359-
this->template check_deserialization_gate_count<fq<TypeParam>>([] { return bb::fq::random_element(); }, 3515);
359+
this->template check_deserialization_gate_count<fq<TypeParam>>([] { return bb::fq::random_element(); }, 3513);
360360
}
361361

362362
/**
@@ -365,7 +365,7 @@ TYPED_TEST(stdlib_field_conversion, GateCountBigfieldDeserialization)
365365
*/
366366
TYPED_TEST(stdlib_field_conversion, GateCountMultipleBigfieldDeserialization)
367367
{
368-
this->template check_deserialization_gate_count<fq<TypeParam>>([] { return bb::fq::random_element(); }, 3914, 10);
368+
this->template check_deserialization_gate_count<fq<TypeParam>>([] { return bb::fq::random_element(); }, 3913, 10);
369369
}
370370

371371
/**
@@ -389,7 +389,7 @@ TYPED_TEST(stdlib_field_conversion, GateCountMultipleBN254PointDeserialization)
389389
{
390390
using Builder = TypeParam;
391391

392-
constexpr uint32_t expected = std::is_same_v<Builder, bb::UltraCircuitBuilder> ? 5751 : 0;
392+
constexpr uint32_t expected = std::is_same_v<Builder, bb::UltraCircuitBuilder> ? 5746 : 0;
393393
this->template check_deserialization_gate_count<bn254_element<Builder>>(
394394
[] { return curve::BN254::AffineElement::random_element(); }, expected, 10);
395395
}

0 commit comments

Comments
 (0)