Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
6b23fd2
remove unused cycle_scalar interfaces and use bigfield in test suite
ledwards2225 Sep 12, 2025
4832bd6
remove option to skip primality in construct from bn254
ledwards2225 Sep 13, 2025
bf3eacb
use from_witness for consistency
ledwards2225 Sep 13, 2025
c6292e0
add split_at_unrestricted (bad name!)
ledwards2225 Sep 14, 2025
14c1400
add split method to field and bring back from witness for fuzzer build
ledwards2225 Sep 14, 2025
fb17bdf
bring back constructor for fuzzer
ledwards2225 Sep 14, 2025
ce5e3da
update plsit at naming and fix tests
ledwards2225 Sep 15, 2025
dd1401e
use split_at in create_from_bn254_scalar
ledwards2225 Sep 15, 2025
3035add
cleanup
ledwards2225 Sep 15, 2025
984cb44
Merge branch 'merge-train/barretenberg' into lde/cycle-group-5
ledwards2225 Sep 15, 2025
d1d440e
Merge branch 'next' into lde/cycle-group-5
ledwards2225 Sep 16, 2025
d9eb676
implement split unique in field utils
ledwards2225 Sep 17, 2025
7c563b0
Merge branch 'merge-train/barretenberg' into lde/cycle-group-5
ledwards2225 Sep 17, 2025
5da71f1
fix wasm build
ledwards2225 Sep 17, 2025
6001747
Merge branch 'merge-train/barretenberg' into lde/cycle-group-5
ledwards2225 Sep 18, 2025
e690fd2
revert test changes for now
ledwards2225 Sep 18, 2025
83be28b
add num gates pinning to cycle group tests
ledwards2225 Sep 18, 2025
1de5c17
use evaluate linear identity; no gate change
ledwards2225 Sep 18, 2025
50808c0
Merge branch 'merge-train/barretenberg' into lde/cycle-group-5
ledwards2225 Sep 18, 2025
12c79de
vk hash
ledwards2225 Sep 19, 2025
1b5047e
Merge branch 'merge-train/barretenberg' into lde/cycle-group-5
ledwards2225 Sep 19, 2025
739ad72
vk hash
ledwards2225 Sep 19, 2025
99c1cd1
try again
ledwards2225 Sep 19, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ template <typename RecursiveFlavor> class BoomerangRecursiveVerifierTest : publi
outer_circuit.finalize_circuit(false);
auto graph = cdg::StaticAnalyzer(outer_circuit);
auto connected_components = graph.find_connected_components();
EXPECT_EQ(connected_components.size(), 2);
EXPECT_EQ(connected_components.size(), 5);
info("Connected components: ", connected_components.size());
auto variables_in_one_gate = graph.show_variables_in_one_gate(outer_circuit);
EXPECT_EQ(variables_in_one_gate.size(), 2);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ class ECCVMRecursiveTests : public ::testing::Test {
}

// Check that the size of the recursive verifier is consistent with historical expectation
uint32_t NUM_GATES_EXPECTED = 213923;
uint32_t NUM_GATES_EXPECTED = 213307;

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gate count reduction here and in UH rec verifier is due to no longer calling validate_scalar_is_in_field() within transcript method split_challenge(). This used to happen under the hood by virtue of using cycle_scalar(const field_t&) to perform the hi/lo splitting. We now use field_t::split_at() which performs identical splitting but does not perform an "in field" check.

ASSERT_EQ(static_cast<uint32_t>(outer_circuit.get_num_finalized_gates()), NUM_GATES_EXPECTED)
<< "Ultra-arithmetized ECCVM Recursive verifier gate count changed! Update this value if you are sure this "
"is expected.";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -291,7 +291,7 @@ template <typename RecursiveFlavor> class RecursiveVerifierTest : public testing
}
// Check the size of the recursive verifier
if constexpr (std::same_as<RecursiveFlavor, MegaZKRecursiveFlavor_<UltraCircuitBuilder>>) {
uint32_t NUM_GATES_EXPECTED = 796978;
uint32_t NUM_GATES_EXPECTED = 796237;
ASSERT_EQ(static_cast<uint32_t>(outer_circuit.get_num_finalized_gates()), NUM_GATES_EXPECTED)
<< "MegaZKHonk Recursive verifier changed in Ultra gate count! Update this value if you "
"are sure this is expected.";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1260,14 +1260,43 @@ template <typename Builder> field_t<Builder> field_t<Builder>::accumulate(const
return total.normalize();
}

// WORKTODO: better name!
template <typename Builder>
std::pair<field_t<Builder>, field_t<Builder>> field_t<Builder>::split_at(const size_t lsb_index) const
{
// WORKTODO: is this right? depends on how bb::fr is implemented. ideally this would be some predefined constant..
const size_t max_bits = 254;
ASSERT(lsb_index < max_bits);

const uint256_t value(this->get_value());
const uint256_t lo_val = value.slice(0, lsb_index);
const uint256_t hi_val = value.slice(lsb_index, max_bits);

// If `this` is constant, return constants based on the native hi/lo values
if (this->is_constant()) {
return { field_t(lo_val), field_t(hi_val) };
}

// Otherwise, create hi/lo witnesses and constrain them to reconstruct `this` as field elements
field_t lo = from_witness(get_context(), lo_val);
field_t hi = from_witness(get_context(), hi_val);
const uint256_t shift = uint256_t(1) << lsb_index;
(lo + (hi * shift)).assert_equal(*this);

// Set the origin tag for the limbs to be that of the original field element
lo.set_origin_tag(get_origin_tag());
hi.set_origin_tag(get_origin_tag());
return { lo, hi };
}

/**
* @brief Splits the field element into (lo, hi), where:
* - lo contains bits [0, lsb_index)
* - hi contains bits [lsb_index, num_bits)
*/
template <typename Builder>
std::pair<field_t<Builder>, field_t<Builder>> field_t<Builder>::split_at(const size_t lsb_index,
const size_t num_bits) const
std::pair<field_t<Builder>, field_t<Builder>> field_t<Builder>::no_wrap_split_at(const size_t lsb_index,
const size_t num_bits) const
{
ASSERT(lsb_index < num_bits);
ASSERT(num_bits <= grumpkin::MAX_NO_WRAP_INTEGER_BIT_LENGTH);

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure why this constant is 252 - would be nice to add some documentation

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -388,7 +388,9 @@ template <typename Builder_> class field_t {

Builder* get_context() const { return context; }

std::pair<field_t<Builder>, field_t<Builder>> split_at(
std::pair<field_t<Builder>, field_t<Builder>> split_at(const size_t lsb_index) const;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

makes sense to me


std::pair<field_t<Builder>, field_t<Builder>> no_wrap_split_at(
const size_t lsb_index, const size_t num_bits = grumpkin::MAX_NO_WRAP_INTEGER_BIT_LENGTH) const;

bool_t<Builder> is_zero() const;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -930,7 +930,7 @@ template <typename Builder> class stdlib_field : public testing::Test {
// Lambda to check split_at functionality
auto check_split_at = [&](const field_ct& a, size_t start, size_t num_bits) {
const uint256_t a_native = a.get_value();
auto split_data = a.split_at(start, num_bits);
auto split_data = a.no_wrap_split_at(start, num_bits);
EXPECT_EQ(split_data.first.get_value(), a_native & ((uint256_t(1) << start) - 1));
EXPECT_EQ(split_data.second.get_value(), (a_native >> start) & ((uint256_t(1) << num_bits) - 1));

Expand Down Expand Up @@ -1424,7 +1424,7 @@ template <typename Builder> class stdlib_field : public testing::Test {

// Split preserves tags
const size_t num_bits = uint256_t(a.get_value()).get_msb() + 1;
auto split_data = a.split_at(num_bits / 2, num_bits);
auto split_data = a.no_wrap_split_at(num_bits / 2, num_bits);
EXPECT_EQ(split_data.first.get_origin_tag(), submitted_value_origin_tag);
EXPECT_EQ(split_data.second.get_origin_tag(), submitted_value_origin_tag);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@
using Group = typename Curve::Group; \
using bool_ct = stdlib::bool_t<Builder>; \
using witness_ct = stdlib::witness_t<Builder>; \
using cycle_scalar_ct = cycle_group_ct::cycle_scalar;
using cycle_scalar_ct = cycle_group_ct::cycle_scalar; \
using BigScalarField = typename cycle_scalar_ct::BigScalarField;

using namespace bb;

Expand Down Expand Up @@ -771,22 +772,26 @@ TYPED_TEST(CycleGroupTest, TestBatchMulGeneralMSM)
// 1: add entry where point, scalar are witnesses
expected += (element * scalar);
points.emplace_back(cycle_group_ct::from_witness(&builder, element));
scalars.emplace_back(cycle_group_ct::cycle_scalar::from_witness(&builder, scalar));

@ledwards2225 ledwards2225 Sep 14, 2025

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cycle_scalar::from_witness is not used anywhere in production so it seems better to test the main production use case instead which is cycle_scalars constructed from bigfield elements (i.e. in the ECCVM recursive verifier). I've updated all of the tests accordingly. Eventually I may expand them to use cycle_scalars constructed through all of the main production entrypoints

BigScalarField big_scalar1(&builder, scalar);
scalars.emplace_back(cycle_scalar_ct(big_scalar1));

// 2: add entry where point is constant, scalar is witness
expected += (element * scalar);
points.emplace_back(cycle_group_ct(element));
scalars.emplace_back(cycle_group_ct::cycle_scalar::from_witness(&builder, scalar));
BigScalarField big_scalar2(&builder, scalar);
scalars.emplace_back(cycle_scalar_ct(big_scalar2));

// 3: add entry where point is witness, scalar is constant
expected += (element * scalar);
points.emplace_back(cycle_group_ct::from_witness(&builder, element));
scalars.emplace_back(typename cycle_group_ct::cycle_scalar(scalar));
BigScalarField big_scalar3(&builder, scalar);
scalars.emplace_back(cycle_scalar_ct(big_scalar3));

// 4: add entry where point is constant, scalar is constant
expected += (element * scalar);
points.emplace_back(cycle_group_ct(element));
scalars.emplace_back(typename cycle_group_ct::cycle_scalar(scalar));
BigScalarField big_scalar4(&builder, scalar);
scalars.emplace_back(cycle_scalar_ct(big_scalar4));
}

// Here and in the following cases assign different tags to points and scalars and get the union of them back
Expand All @@ -813,10 +818,12 @@ TYPED_TEST(CycleGroupTest, TestBatchMulProducesInfinity)
auto element = TestFixture::generators[0];
typename Group::Fr scalar = Group::Fr::random_element(&engine);
points.emplace_back(cycle_group_ct::from_witness(&builder, element));
scalars.emplace_back(cycle_group_ct::cycle_scalar::from_witness(&builder, scalar));
BigScalarField big_scalar1(&builder, scalar);
scalars.emplace_back(cycle_scalar_ct(big_scalar1));

points.emplace_back(cycle_group_ct::from_witness(&builder, element));
scalars.emplace_back(cycle_group_ct::cycle_scalar::from_witness(&builder, -scalar));
BigScalarField big_scalar2(&builder, -scalar);
scalars.emplace_back(cycle_scalar_ct(big_scalar2));

const auto expected_tag = assign_and_merge_tags(points, scalars);

Expand All @@ -841,7 +848,8 @@ TYPED_TEST(CycleGroupTest, TestBatchMulMultiplyByZero)
auto element = TestFixture::generators[0];
typename Group::Fr scalar = 0;
points.emplace_back(cycle_group_ct::from_witness(&builder, element));
scalars.emplace_back(cycle_group_ct::cycle_scalar::from_witness(&builder, scalar));
BigScalarField big_scalar(&builder, scalar);
scalars.emplace_back(cycle_scalar_ct(big_scalar));

const auto expected_tag = assign_and_merge_tags(points, scalars);
auto result = cycle_group_ct::batch_mul(points, scalars);
Expand Down Expand Up @@ -869,14 +877,16 @@ TYPED_TEST(CycleGroupTest, TestBatchMulInputsAreInfinity)
cycle_group_ct point = cycle_group_ct::from_witness(&builder, element);
point.set_point_at_infinity(witness_ct(&builder, true));
points.emplace_back(point);
scalars.emplace_back(cycle_group_ct::cycle_scalar::from_witness(&builder, scalar));
BigScalarField big_scalar1(&builder, scalar);
scalars.emplace_back(cycle_scalar_ct(big_scalar1));
}
// is_infinity = constant
{
cycle_group_ct point = cycle_group_ct::from_witness(&builder, element);
point.set_point_at_infinity(true);
points.emplace_back(point);
scalars.emplace_back(cycle_group_ct::cycle_scalar::from_witness(&builder, scalar));
BigScalarField big_scalar2(&builder, scalar);
scalars.emplace_back(cycle_scalar_ct(big_scalar2));
}

const auto expected_tag = assign_and_merge_tags(points, scalars);
Expand Down Expand Up @@ -907,14 +917,16 @@ TYPED_TEST(CycleGroupTest, TestBatchMulFixedBaseInLookupTable)
// 1: add entry where point is constant, scalar is witness
expected += (element * scalar);
points.emplace_back(element);
scalars.emplace_back(cycle_group_ct::cycle_scalar::from_witness(&builder, scalar));
BigScalarField big_scalar(&builder, scalar);
scalars.emplace_back(cycle_scalar_ct(big_scalar));
scalars_native.emplace_back(uint256_t(scalar));

// 2: add entry where point is constant, scalar is constant
element = plookup::fixed_base::table::rhs_generator_point();
expected += (element * scalar);
points.emplace_back(element);
scalars.emplace_back(typename cycle_group_ct::cycle_scalar(scalar));
BigScalarField big_scalar_const(&builder, scalar);
scalars.emplace_back(cycle_scalar_ct(big_scalar_const));
scalars_native.emplace_back(uint256_t(scalar));
}
const auto expected_tag = assign_and_merge_tags(points, scalars);
Expand Down Expand Up @@ -946,22 +958,25 @@ TYPED_TEST(CycleGroupTest, TestBatchMulFixedBaseSomeInLookupTable)
// 1: add entry where point is constant, scalar is witness
expected += (element * scalar);
points.emplace_back(element);
scalars.emplace_back(cycle_group_ct::cycle_scalar::from_witness(&builder, scalar));
BigScalarField big_scalar(&builder, scalar);
scalars.emplace_back(cycle_scalar_ct(big_scalar));
scalars_native.emplace_back(scalar);

// 2: add entry where point is constant, scalar is constant
element = plookup::fixed_base::table::rhs_generator_point();
expected += (element * scalar);
points.emplace_back(element);
scalars.emplace_back(typename cycle_group_ct::cycle_scalar(scalar));
BigScalarField big_scalar_const(&builder, scalar);
scalars.emplace_back(cycle_scalar_ct(big_scalar_const));
scalars_native.emplace_back(scalar);

// 3: add entry where point is constant, scalar is witness
scalar = Group::Fr::random_element(&engine);
element = Group::one * Group::Fr::random_element(&engine);
expected += (element * scalar);
points.emplace_back(element);
scalars.emplace_back(cycle_group_ct::cycle_scalar::from_witness(&builder, scalar));
BigScalarField big_scalar2(&builder, scalar);
scalars.emplace_back(cycle_scalar_ct(big_scalar2));
scalars_native.emplace_back(scalar);
}
const auto expected_tag = assign_and_merge_tags(points, scalars);
Expand Down Expand Up @@ -989,11 +1004,13 @@ TYPED_TEST(CycleGroupTest, TestBatchMulFixedBaseZeroScalars)

// 1: add entry where point is constant, scalar is witness
points.emplace_back((element));
scalars.emplace_back(cycle_group_ct::cycle_scalar::from_witness(&builder, scalar));
BigScalarField big_scalar(&builder, scalar);
scalars.emplace_back(cycle_scalar_ct(big_scalar));

// 2: add entry where point is constant, scalar is constant
points.emplace_back((element));
scalars.emplace_back(typename cycle_group_ct::cycle_scalar(scalar));
BigScalarField big_scalar_const(&builder, scalar);
scalars.emplace_back(cycle_scalar_ct(big_scalar_const));
}
const auto expected_tag = assign_and_merge_tags(points, scalars);
auto result = cycle_group_ct::batch_mul(points, scalars);
Expand All @@ -1014,7 +1031,6 @@ TYPED_TEST(CycleGroupTest, TestMul)
// case 1, general MSM with inputs that are combinations of constant and witnesses
{
cycle_group_ct point;
typename cycle_group_ct::cycle_scalar scalar;
cycle_group_ct result;
for (size_t i = 0; i < num_muls; ++i) {
auto element = TestFixture::generators[i];
Expand All @@ -1023,7 +1039,8 @@ TYPED_TEST(CycleGroupTest, TestMul)

// 1: add entry where point, scalar are witnesses
point = (cycle_group_ct::from_witness(&builder, element));
scalar = (cycle_group_ct::cycle_scalar::from_witness(&builder, native_scalar));
BigScalarField big_scalar1(&builder, native_scalar);
cycle_scalar_ct scalar = (cycle_scalar_ct(big_scalar1));
point.set_origin_tag(submitted_value_origin_tag);
scalar.set_origin_tag(challenge_origin_tag);
result = point * scalar;
Expand All @@ -1032,16 +1049,21 @@ TYPED_TEST(CycleGroupTest, TestMul)

// 2: add entry where point is constant, scalar is witness
point = (cycle_group_ct(element));
scalar = (cycle_group_ct::cycle_scalar::from_witness(&builder, native_scalar));
BigScalarField big_scalar2(&builder, native_scalar);
scalar = (cycle_scalar_ct(big_scalar2));

EXPECT_EQ((result).get_value(), (expected_result));

// 3: add entry where point is witness, scalar is constant
point = (cycle_group_ct::from_witness(&builder, element));
BigScalarField big_scalar3(&builder, native_scalar);
scalar = (cycle_scalar_ct(big_scalar3));
EXPECT_EQ((result).get_value(), (expected_result));

// 4: add entry where point is constant, scalar is constant
point = (cycle_group_ct(element));
BigScalarField big_scalar4(&builder, native_scalar);
scalar = (cycle_scalar_ct(big_scalar4));
EXPECT_EQ((result).get_value(), (expected_result));
}
}
Expand Down Expand Up @@ -1156,7 +1178,8 @@ TYPED_TEST(CycleGroupTest, MixedLengthScalarsIsNotSupported)

// First scalar: 256 bits
uint256_t scalar1_value = uint256_t(123456789);
scalars.push_back(cycle_group_ct::cycle_scalar::from_witness(&builder, typename Curve::ScalarField(scalar1_value)));
BigScalarField big_scalar1(&builder, typename Curve::ScalarField(scalar1_value));
scalars.push_back(cycle_scalar_ct(big_scalar1));

// Second scalar: 128 bits
uint256_t scalar2_value = uint256_t(987654321);
Expand Down Expand Up @@ -1188,8 +1211,10 @@ TYPED_TEST(CycleGroupTest, TestFixedBaseBatchMul)
auto scalar1_val = Group::Fr::random_element(&engine);
auto scalar2_val = Group::Fr::random_element(&engine);

scalars.push_back(cycle_scalar_ct::from_witness(&builder, scalar1_val));
scalars.push_back(cycle_scalar_ct::from_witness(&builder, scalar2_val));
BigScalarField big_scalar1(&builder, scalar1_val);
BigScalarField big_scalar2(&builder, scalar2_val);
scalars.push_back(cycle_scalar_ct(big_scalar1));
scalars.push_back(cycle_scalar_ct(big_scalar2));
points.push_back(cycle_group_ct(lhs_generator)); // constant point
points.push_back(cycle_group_ct(rhs_generator)); // constant point

Expand Down
Loading
Loading