Skip to content

Commit c38271a

Browse files
authored
chore: merge v5-next into merge-train/spartan-v5 (raw, conflict markers) (#24071)
## Raw merge — do not merge alone Raw `git merge --no-ff` of `v5-next` into `merge-train/spartan-v5` with conflict markers **committed**. This PR is red by design and exists only as the base of the stacked resolution PR. Review it together with the fix PR `cb/merge-train-spartan-v5-fix`. ### Parents - train `merge-train/spartan-v5` @ `d979a26bc060068ecc33c0b1d88a8dac7e941bef` - base `v5-next` @ `5fd61870609b88b14dfcd2924453bd81f77ef0b5` - merge-base `ab5413c72dc5377107943b8614130ec8050bf06c` ### Conflicted files (1) - `yarn-project/end-to-end/src/bench/client_flows/client_flows_benchmark.ts` Resolution + any regeneration lands in the stacked fix PR. --- *Created by [claudebox](https://claudebox.work/v2/sessions/0e582efb7d4723ed) · group: `slackbot`*
2 parents d979a26 + f89fa28 commit c38271a

122 files changed

Lines changed: 1975 additions & 1724 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

barretenberg/cpp/src/barretenberg/ecc/groups/affine_element.test.cpp

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,30 @@ template <typename G1> class TestAffineElement : public testing::Test {
247247
EXPECT_NE(P < Q, Q < P);
248248
}
249249

250+
// Regression test: from_compressed must reject non-canonical encodings (x_coordinate >= modulus).
251+
// Without the range check, Fq(x) silently reduces mod p, so distinct compressed bytestrings whose
252+
// x values differ by a multiple of p would decompress to the same point (encoding malleability).
253+
static void test_point_compression_non_canonical_x()
254+
{
255+
using Fq = typename G1::Fq;
256+
// x1 = 1 and x2 = 1 + p both fit in 255 bits because p_BN254 < 2^254 and p_Grumpkin < 2^254.
257+
// They are distinct as 255-bit integers but equal mod p.
258+
uint256_t x1 = uint256_t(1);
259+
uint256_t x2 = uint256_t(1) + Fq::modulus;
260+
ASSERT_NE(x1, x2);
261+
ASSERT_LT(x2, uint256_t(1) << 255);
262+
263+
affine_element pt1 = affine_element::from_compressed(x1);
264+
affine_element pt2 = affine_element::from_compressed(x2);
265+
266+
// Canonical input (x = 1) decompresses to a valid point on these curves.
267+
EXPECT_TRUE(pt1.on_curve());
268+
// Non-canonical input must return the (0, 0) sentinel rather than the same point as x1.
269+
EXPECT_EQ(pt2.x, Fq::zero());
270+
EXPECT_EQ(pt2.y, Fq::zero());
271+
EXPECT_NE(pt1, pt2);
272+
}
273+
250274
// Verify that from_compressed with an x that has no y on the curve returns the (0,0) sentinel.
251275
static void test_point_compression_invalid_x()
252276
{
@@ -563,6 +587,16 @@ TYPED_TEST(TestAffineElement, PointCompressionInvalidX)
563587
}
564588
}
565589

590+
// Regression test: from_compressed must reject non-canonical x >= modulus.
591+
TYPED_TEST(TestAffineElement, PointCompressionNonCanonicalX)
592+
{
593+
if constexpr (TypeParam::Fq::modulus.data[3] >= MODULUS_TOP_LIMB_LARGE_THRESHOLD) {
594+
GTEST_SKIP(); // from_compressed is not used on large-modulus curves
595+
} else {
596+
TestFixture::test_point_compression_non_canonical_x();
597+
}
598+
}
599+
566600
TEST(AffineElement, HashToCurve)
567601
{
568602
std::vector<std::tuple<std::vector<uint8_t>, grumpkin::g1::affine_element>> test_vectors;

barretenberg/cpp/src/barretenberg/ecc/groups/affine_element_impl.hpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,13 @@ constexpr affine_element<Fq, Fr, T> affine_element<Fq, Fr, T>::from_compressed(c
2424
x_coordinate.data[3] = x_coordinate.data[3] & (~UINT256_TOP_LIMB_MSB);
2525
bool y_bit = compressed.get_bit(255);
2626

27+
// Reject non-canonical encodings: the lower 255 bits encode x. If x_coordinate >= Fq::modulus,
28+
// Fq(x_coordinate) silently reduces mod p, so two distinct compressed bytestrings differing by
29+
// a multiple of p would decompress to the same point (encoding malleability).
30+
if (x_coordinate >= Fq::modulus) {
31+
return affine_element(Fq::zero(), Fq::zero());
32+
}
33+
2734
Fq x = Fq(x_coordinate);
2835
Fq y2 = (x.sqr() * x + T::b);
2936
if constexpr (T::has_a) {

barretenberg/cpp/src/barretenberg/eccvm/eccvm_fixed_vk.hpp

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,13 @@ class ECCVMHardcodedVKAndHash {
2525
using BF = curve::Grumpkin::BaseField;
2626

2727
// Precomputed VK hash (hash of all commitments below). Update via ECCVMTests::FixedVK if commitments change.
28-
static BF vk_hash() { return BF(uint256_t("0x103f7a122fe63075eb8b9c6019cbd3cbe55b5775400799b12c63b31643515ff3")); }
28+
static BF vk_hash() { return BF(uint256_t("0x032ece6c493f6e74ca8847127f189f8e759021e53df9820a88985e41e400fde2")); }
2929

3030
static std::vector<Commitment> get_all()
3131
{
3232
return { // lagrange_first (at row NUM_DISABLED_ROWS_IN_SUMCHECK)
33-
Commitment(uint256_t("0x040948748b49a15e319d8ae97062ce125445f612bbf4265776490dafe4a75aa7"),
34-
uint256_t("0x0674e7fcc6e6685f250a218ab444bef48b9772e3fb32425d579c4430f919828b")),
33+
Commitment(uint256_t("0x1227d829cc03e51a2cc68ca0e08381c02b78c484f4092a10584ef78381a31968"),
34+
uint256_t("0x10b233eb875fed8834f6235ed737946b68c0c7ed258053cd943dcc69212d90d6")),
3535

3636
// lagrange_second (hiding op row)
3737
Commitment(uint256_t("0x1976c760e4bde34db58394888baeda91f57bcdddf60aec28b721b10aac55f555"),
@@ -42,8 +42,8 @@ class ECCVMHardcodedVKAndHash {
4242
uint256_t("0x0d04425c0f370a7aaace3cf41f9b2380079c95fa26f9f83b5fd5e26813dbd289")),
4343

4444
// lagrange_last (at dyadic_size - 1)
45-
Commitment(uint256_t("0x07099c9989bd2212d634a00180d59f1dd1279c5c3d220583ad4acfbfb180ae60"),
46-
uint256_t("0x2fa02a4987281b3b310bb8d9724c36a20eec491acfd3be7e7f3dcf8d8bec8848"))
45+
Commitment(uint256_t("0x23a271e0e1d99d2a526cc8c06df7edf7c86e9cb985e577affb5a64b9daf24401"),
46+
uint256_t("0x12a74c457ae1f9bd6076c308f47a006deb82adfd576be1266cde1cdc0d008cd1"))
4747
};
4848
}
4949
};

barretenberg/cpp/src/barretenberg/srs/factories/crs_factory.test.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ void check_grumpkin_consistency(const fs::path& crs_download_path, size_t num_po
6161
// read G1
6262
std::vector<Grumpkin::AffineElement> points(num_points);
6363
auto data =
64-
read_file(bb::srs::bb_crs_path() / "grumpkin_g1.flat.dat", num_points * sizeof(Grumpkin::AffineElement));
64+
read_file(bb::srs::bb_crs_path() / "grumpkin_g1_v2.flat.dat", num_points * sizeof(Grumpkin::AffineElement));
6565

6666
for (size_t i = 0; i < num_points; ++i) {
6767
points[i] = from_buffer<Grumpkin::AffineElement>(data, i * sizeof(g1::affine_element));

barretenberg/cpp/src/barretenberg/srs/factories/get_grumpkin_crs.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ std::vector<curve::Grumpkin::AffineElement> get_grumpkin_g1_data(const std::file
1313
{
1414
std::filesystem::create_directories(path);
1515

16-
auto g1_path = path / "grumpkin_g1.flat.dat";
16+
auto g1_path = path / "grumpkin_g1_v2.flat.dat";
1717
auto lock_path = path / "crs.lock";
1818
// Acquire exclusive lock to prevent simultaneous generation/writes
1919
FileLockGuard lock(lock_path.string());

barretenberg/crs/bootstrap.sh

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -98,14 +98,14 @@ function build {
9898
download_with_fallback "$g2" "g2.dat"
9999
fi
100100

101-
# TODO: This grumpkin CRS in S3 still has the 28 byte header on it. Remove.
102-
# And if we ever need more than transcript00.dat, concatenate to single file like we did with bn254 above.
101+
# grumpkin_g1_v2.dat: regenerated after affine_element::from_compressed started rejecting
102+
# non-canonical x coordinates, so it must match binaries built with that fix.
103103
crs_size=$((2**18))
104104
crs_size_bytes=$((crs_size*64))
105-
gg1=$crs_path/grumpkin_g1.flat.dat
105+
gg1=$crs_path/grumpkin_g1_v2.flat.dat
106106
if [ ! -f "$gg1" ] || [ $(stat -c%s "$gg1") -lt $crs_size_bytes ]; then
107107
echo "Downloading grumpkin crs of size: ${crs_size} ($((crs_size_bytes/(1024*1024)))MB)"
108-
download_with_fallback "$gg1" "grumpkin_g1.dat" "bytes=0-$((crs_size_bytes-1))"
108+
download_with_fallback "$gg1" "grumpkin_g1_v2.dat" "bytes=0-$((crs_size_bytes-1))"
109109
fi
110110
}
111111

barretenberg/scripts/download_bb_crs.sh

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -56,12 +56,12 @@ if [ ! -f "$g2" ]; then
5656
download_with_fallback "$g2" "g2.dat"
5757
fi
5858

59-
# TODO: This grumpkin CRS in S3 still has the 28 byte header on it. Remove.
60-
# And if we ever need more than transcript00.dat, concatenate to single file like we did with bn254 above.
59+
# grumpkin_g1_v2.dat: regenerated after affine_element::from_compressed started rejecting
60+
# non-canonical x coordinates, so it must match binaries built with that fix.
6161
crs_size=$((2**18))
6262
crs_size_bytes=$((crs_size*64))
63-
gg1=$crs_path/grumpkin_g1.flat.dat
63+
gg1=$crs_path/grumpkin_g1_v2.flat.dat
6464
if [ ! -f "$gg1" ] || [ $(stat -c%s "$gg1") -lt $crs_size_bytes ]; then
6565
echo "Downloading grumpkin crs of size: ${crs_size} ($((crs_size_bytes/(1024*1024)))MB)"
66-
download_with_fallback "$gg1" "grumpkin_g1.dat" "bytes=0-$((crs_size_bytes-1))"
66+
download_with_fallback "$gg1" "grumpkin_g1_v2.dat" "bytes=0-$((crs_size_bytes-1))"
6767
fi

barretenberg/ts/src/crs/browser/cached_net_crs.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,13 +83,13 @@ export class CachedNetGrumpkinCrs {
8383
*/
8484
async init() {
8585
// Check if data is in IndexedDB
86-
const g1Data = await get('grumpkinG1Data');
86+
const g1Data = await get('grumpkinG1DataV2');
8787
const netGrumpkinCrs = new NetGrumpkinCrs(this.numPoints);
8888
const g1DataLength = this.numPoints * 64;
8989

9090
if (!g1Data || g1Data.length < g1DataLength) {
9191
this.g1Data = await netGrumpkinCrs.downloadG1Data();
92-
await set('grumpkinG1Data', this.g1Data);
92+
await set('grumpkinG1DataV2', this.g1Data);
9393
} else {
9494
this.g1Data = g1Data;
9595
}

barretenberg/ts/src/crs/net_crs.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -188,8 +188,8 @@ export class NetGrumpkinCrs {
188188
};
189189

190190
return await fetchWithFallback(
191-
`${CRS_PRIMARY_HOST}/grumpkin_g1.dat`,
192-
`${CRS_FALLBACK_HOST}/grumpkin_g1.dat`,
191+
`${CRS_PRIMARY_HOST}/grumpkin_g1_v2.dat`,
192+
`${CRS_FALLBACK_HOST}/grumpkin_g1_v2.dat`,
193193
options,
194194
);
195195
}

barretenberg/ts/src/crs/node/index.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ export class GrumpkinCrs {
130130
async init(): Promise<void> {
131131
mkdirSync(this.path, { recursive: true });
132132

133-
const g1FileSize = await stat(this.path + '/grumpkin_g1.flat.dat')
133+
const g1FileSize = await stat(this.path + '/grumpkin_g1_v2.flat.dat')
134134
.then(stats => stats.size)
135135
.catch(() => 0);
136136

@@ -143,7 +143,7 @@ export class GrumpkinCrs {
143143
const crs = new NetGrumpkinCrs(this.numPoints);
144144
const stream = await crs.streamG1Data();
145145

146-
await finished(Readable.fromWeb(stream as any).pipe(createWriteStream(this.path + '/grumpkin_g1.flat.dat')));
146+
await finished(Readable.fromWeb(stream as any).pipe(createWriteStream(this.path + '/grumpkin_g1_v2.flat.dat')));
147147
writeFileSync(this.path + '/grumpkin_size', String(crs.numPoints));
148148
}
149149

@@ -152,6 +152,6 @@ export class GrumpkinCrs {
152152
* @returns The points data.
153153
*/
154154
getG1Data(): Uint8Array {
155-
return readFileSync(this.path + '/grumpkin_g1.flat.dat');
155+
return readFileSync(this.path + '/grumpkin_g1_v2.flat.dat');
156156
}
157157
}

0 commit comments

Comments
 (0)