Skip to content

Commit 4b2496b

Browse files
notnotrajunotnotraju
andauthored
fix: edge case in the ECCVM related to splitting scalars (#16816)
Addresses [Issue 1529](AztecProtocol/barretenberg#1529), which found an edge case which caused the ECCVM prover to fail. There was just a `<=128` where there should have been a `<128`. Adds a bit of testing for the size of the `splitting_scalar_with_endomorphism`method. --------- Co-authored-by: notnotraju <[email protected]>
1 parent 52fa964 commit 4b2496b

File tree

4 files changed

+59
-22
lines changed

4 files changed

+59
-22
lines changed

barretenberg/cpp/src/barretenberg/ecc/curves/bn254/fq.test.cpp

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -385,6 +385,9 @@ TEST(fq, SplitIntoEndomorphismScalars)
385385
k1.self_to_montgomery_form();
386386
k2.self_to_montgomery_form();
387387

388+
EXPECT_LT(uint256_t(k1).get_msb(), 128);
389+
EXPECT_LT(uint256_t(k2).get_msb(), 128);
390+
388391
result = k2 * fq::cube_root_of_unity();
389392
result = k1 - result;
390393

@@ -407,6 +410,37 @@ TEST(fq, SplitIntoEndomorphismScalarsSimple)
407410
k1.self_to_montgomery_form();
408411
k2.self_to_montgomery_form();
409412

413+
EXPECT_LT(uint256_t(k1).get_msb(), 128);
414+
EXPECT_LT(uint256_t(k2).get_msb(), 128);
415+
416+
fq beta = fq::cube_root_of_unity();
417+
result = k2 * beta;
418+
result = k1 - result;
419+
420+
result.self_from_montgomery_form();
421+
for (size_t i = 0; i < 4; ++i) {
422+
EXPECT_EQ(result.data[i], k.data[i]);
423+
}
424+
}
425+
426+
TEST(fq, SplitIntoEndomorphismEdgeCase)
427+
{
428+
429+
fq input = { 0, 0, 1, 0 }; // 2^128
430+
fq k = { 0, 0, 0, 0 };
431+
fq k1 = { 0, 0, 0, 0 };
432+
fq k2 = { 0, 0, 0, 0 };
433+
fq::__copy(input, k);
434+
435+
fq::split_into_endomorphism_scalars(k, k1, k2);
436+
437+
fq result{ 0, 0, 0, 0 };
438+
k1.self_to_montgomery_form();
439+
k2.self_to_montgomery_form();
440+
441+
EXPECT_LT(uint256_t(k1).get_msb(), 128);
442+
EXPECT_LT(uint256_t(k2).get_msb(), 128);
443+
410444
fq beta = fq::cube_root_of_unity();
411445
result = k2 * beta;
412446
result = k1 - result;

barretenberg/cpp/src/barretenberg/eccvm/eccvm.fuzzer.cpp

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -186,27 +186,6 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* Data, size_t Size)
186186

187187
// Use pre-computed scalar selected by scalar_indices
188188
Fr scalar = precomputed_scalars[op.scalar_index % precomputed_scalars.size()];
189-
// TODO(@Rumata888): remove this if we fix the completeness issue
190-
// Convert scalar to endomorphism scalars and check that none exceed 128 bits
191-
auto converted = scalar.from_montgomery_form();
192-
uint256_t converted_u256(scalar);
193-
uint256_t k1_u256, k2_u256;
194-
Fr z_1 = 0;
195-
Fr z_2 = 0;
196-
197-
if (converted_u256.get_msb() <= 128) {
198-
k1_u256 = converted_u256;
199-
k2_u256 = 0;
200-
} else {
201-
bb::fr::split_into_endomorphism_scalars(converted, z_1, z_2);
202-
k1_u256 = uint256_t(z_1.to_montgomery_form());
203-
k2_u256 = uint256_t(z_2.to_montgomery_form());
204-
}
205-
206-
if (k1_u256.get_msb() >= 128 || k2_u256.get_msb() >= 128) {
207-
// Skip this operation if endomorphism scalars are too large
208-
continue;
209-
}
210189

211190
typename G1::element point_to_multiply = points[generator_index];
212191
if (should_negate) {

barretenberg/cpp/src/barretenberg/eccvm/eccvm.test.cpp

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,30 @@ TEST_F(ECCVMTests, Zeroes)
127127

128128
ASSERT_TRUE(verified);
129129
}
130+
TEST_F(ECCVMTests, ScalarEdgeCase)
131+
{
132+
using Curve = curve::BN254;
133+
using G1 = Curve::Element;
134+
using Fr = Curve::ScalarField;
135+
136+
std::shared_ptr<ECCOpQueue> op_queue = std::make_shared<ECCOpQueue>();
137+
G1 a = G1::one();
138+
139+
op_queue->mul_accumulate(a, Fr(uint256_t(1) << 128));
140+
op_queue->eq_and_reset();
141+
op_queue->merge();
142+
ECCVMCircuitBuilder builder{ op_queue };
143+
144+
std::shared_ptr<Transcript> prover_transcript = std::make_shared<Transcript>();
145+
ECCVMProver prover(builder, prover_transcript);
146+
ECCVMProof proof = prover.construct_proof();
147+
148+
std::shared_ptr<Transcript> verifier_transcript = std::make_shared<Transcript>();
149+
ECCVMVerifier verifier(verifier_transcript);
150+
bool verified = verifier.verify_proof(proof);
151+
152+
ASSERT_TRUE(verified);
153+
}
130154
/**
131155
* @brief Check that size of a ECCVM proof matches the corresponding constant
132156
*@details If this test FAILS, then the following (non-exhaustive) list should probably be updated as well:

barretenberg/cpp/src/barretenberg/op_queue/ecc_op_queue.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -311,7 +311,7 @@ class ECCOpQueue {
311311
Fr z_2 = 0;
312312
auto converted = scalar.from_montgomery_form();
313313
uint256_t converted_u256(scalar);
314-
if (converted_u256.get_msb() <= 128) {
314+
if (converted_u256.get_msb() < 128) {
315315
ultra_op.z_1 = scalar;
316316
ultra_op.z_2 = 0;
317317
} else {

0 commit comments

Comments
 (0)