Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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 @@ -370,11 +370,12 @@ cycle_group<Builder> cycle_group<Builder>::_unconditional_add_or_subtract(const
bool is_addition,
const std::optional<AffineElement> hint) const
{
// This method should not be called on known points at infinity
BB_ASSERT(!this->is_constant_point_at_infinity(),
"cycle_group::_unconditional_add_or_subtract called on constant point at infinity");
BB_ASSERT(!other.is_constant_point_at_infinity(),
"cycle_group::_unconditional_add_or_subtract called on constant point at infinity");
// Reject point-at-infinity operands: the incomplete ecc_add_gate is degenerate at (0, 0) (the
// canonical witness representation of infinity) and admits forged outputs there.
this->is_point_at_infinity().assert_equal(
false, "cycle_group::_unconditional_add_or_subtract called on point at infinity");
other.is_point_at_infinity().assert_equal(
false, "cycle_group::_unconditional_add_or_subtract called on point at infinity");

auto context = get_context(other);

Expand Down Expand Up @@ -829,6 +830,8 @@ typename cycle_group<Builder>::batch_mul_internal_output cycle_group<Builder>::_

// Execute Straus algorithm in-circuit using the precomputed hints.
// If unconditional_add == false, accumulate x-coordinate differences to batch-validate no collisions.
// If unconditional_add == true (constant inputs), no per-slice check is added; soundness relies on the
// offset-generator domain separator keeping per-slice operands distinct.
field_t coordinate_check_product = 1;
for (size_t i = 0; i < num_rounds; ++i) {
// Double the accumulator ROM_TABLE_BITS times (except in first round)
Expand Down Expand Up @@ -1080,8 +1083,8 @@ typename cycle_group<Builder>::batch_mul_internal_output cycle_group<Builder>::_
for (size_t j = 0; j < num_points; ++j) {
const field_t scalar_slice = scalar_slices[j][num_rounds - i - 1];
const cycle_group point = point_tables[j].read(scalar_slice);
// Safe to use unconditional_add: all base points are constants hence linearly independent of offset
// generators
// No per-slice x-collision check; soundness relies on the offset-generator domain separator
// keeping `accumulator` distinct from any plookup table entry across the loop.
accumulator = accumulator.unconditional_add(point, *hint_ptr);
hint_ptr++;
}
Expand Down Expand Up @@ -1170,8 +1173,8 @@ cycle_group<Builder> cycle_group<Builder>::fixed_batch_mul(const std::vector<cyc
_fixed_base_plookup_batch_mul_internal(plookup_scalars, plookup_points, offset_generators, table_bits);
offset_accumulator += offset_generator_delta;

// Subtract offset. Since all points are constants and linearly independent of offset generators,
// we can safely use unconditional_add when constant_acc is non-trivial.
// Note: No collision check on the offset subtraction; soundness relies on the offset-generator domain
// separator keeping aggregate `offset_accumulator` distinct from `accumulator`.
cycle_group result;
if (!constant_acc.is_point_at_infinity()) {
result = accumulator.unconditional_add(AffineElement(-offset_accumulator));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,11 +95,15 @@ straus_lookup_table<Builder>::straus_lookup_table(Builder* context,
// Case 1: if the input point is constant, it is cheaper to fix the point as a witness and then derive the
// table, than it is to derive the table and fix its witnesses to be constant! (due to group additions = 1 gate,
// and fixing x/y coords to be constant = 2 gates)

// base_point == offset_generator collapses the first table add into a degenerate G + G ecc_add gate;
// the non-doubling relation is identically zero when operands match, so is satisfied by any result coordinates.
BB_ASSERT(base_point.get_value() != offset_generator.get_value(),
"straus_lookup_table case-1: base_point must not coincide with offset_generator");

modded_base_point = cycle_group<Builder>::from_constant_witness(_context, modded_base_point.get_value());
point_table[0] = cycle_group<Builder>::from_constant_witness(_context, offset_generator.get_value());
for (size_t i = 1; i < table_size; ++i) {
// Safe to use unconditional_add without collision checking due to constant points and inclusion of offset
// generator in table entries
point_table[i] = point_table[i - 1].unconditional_add(modded_base_point, get_hint(i - 1));
}
} else {
Expand Down
Loading