Skip to content

Commit 1ede827

Browse files
committed
chore: sanitise inf = (0,0) rep in StandardAffinePoint class
1 parent 50d4a19 commit 1ede827

5 files changed

Lines changed: 46 additions & 69 deletions

File tree

barretenberg/cpp/src/barretenberg/vm2/common/standard_affine_point.hpp

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -9,17 +9,15 @@ namespace bb::avm2 {
99
* AVM bytecode expects the representation of points to be triplets, the two coordinates and an is_infinity boolean.
1010
* Furthermore, its representation of infinity is inherited from noir's and is expected to be 0,0,true.
1111
* BB, however, uses only the two coordinates to represent points. Infinity in barretenberg is represented as (P+1)/2,0.
12-
* This class is a wrapper of the BB representation, needed to operate with points, that allows to extract the standard
13-
* representation that AVM bytecode expects.
14-
* NOTE: When constructing infinity from BB's two element representation, is_infinity() will be true but the coordinates
15-
* will remain (P+1)/2,0.
16-
* NOTE: When constructing infinity via BaseFields, input coordinates are maintained and can be any values, so may
17-
* mismatch the underlying AffinePoint. Always check is_infinity() before ECC operations on coordinates. See test
18-
* InfinityPreservesRawCoordinates for an example.
12+
* This class is a wrapper of the BB representation, needed to operate with points, that allows us to extract the
13+
* standard representation that AVM bytecode expects.
14+
*
15+
* NOTE: When constructing infinity from BB's two element representation, we keep the original AffinePoint so operations
16+
* can use it in the background, but set extractable coordinates to be our represention of (0,0).
17+
* NOTE: When constructing infinity via BaseFields, input coordinates are overwritten to our representation of (0,0)
18+
* if the input is_infinity is true, so will mismatch the underlying AffinePoint's coordinates.
1919
*
2020
* TODO(#AVM-266): Remove is_infinity flag from point representation.
21-
* Now that the is_inf flag has been removed from noir (noir-lang/noir/#11926) we should consider a point to be
22-
* infinity iff its coordinates are (0, 0). This likely means changing our handling of underlying coordinates below.
2321
*/
2422
template <typename AffinePoint> class StandardAffinePoint {
2523
public:
@@ -30,14 +28,16 @@ template <typename AffinePoint> class StandardAffinePoint {
3028

3129
constexpr StandardAffinePoint(AffinePoint val) noexcept
3230
: point(val)
33-
, x_coord(val.x)
34-
, y_coord(val.y)
31+
, x_coord(val.is_point_at_infinity() ? zero : val.x)
32+
, y_coord(val.is_point_at_infinity() ? zero : val.y)
3533
{}
3634

35+
// TODO(MW): Do we want to silently discard input x, y if is_infinity = true?
36+
// Or, discard is_infinity and set point to AffinePoint::infinity() iff x = y = 0?
3737
constexpr StandardAffinePoint(BaseField x, BaseField y, bool is_infinity) noexcept
3838
: point(is_infinity ? AffinePoint::infinity() : AffinePoint(x, y))
39-
, x_coord(x)
40-
, y_coord(y)
39+
, x_coord(is_infinity ? zero : x)
40+
, y_coord(is_infinity ? zero : y)
4141
{}
4242

4343
constexpr StandardAffinePoint operator+(const StandardAffinePoint& other) const noexcept
@@ -80,7 +80,7 @@ template <typename AffinePoint> class StandardAffinePoint {
8080

8181
// Always returns the raw coordinates, when an operation results in infinity these will be (0,0).
8282
// If a point at infinity is constructed with non-zero coordinates, we likely want to preserve those.
83-
// TODO(#AVM-266): Now that Noir uses (0, 0) <==> is_infinite, the above may not be true.
83+
// TODO(MW): Clarify - docs here no longer true
8484
constexpr const BaseField& x() const noexcept { return x_coord; }
8585

8686
constexpr const BaseField& y() const noexcept { return y_coord; }
@@ -98,6 +98,7 @@ template <typename AffinePoint> class StandardAffinePoint {
9898
}
9999

100100
private:
101+
// TODO(MW): Clarify - docs here no longer true
101102
// The affine point for operations, this will always match the raw coordinates unless the point is infinity.
102103
// In that case, the point will be set to barretenberg's infinity representation - which is not (0,0).
103104
AffinePoint point;

barretenberg/cpp/src/barretenberg/vm2/common/standard_affine_point.test.cpp

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,18 +10,24 @@ using EmbeddedCurvePoint = StandardAffinePoint<grumpkin::g1::affine_element>;
1010
using Fr = grumpkin::fr;
1111
using Fq = grumpkin::fq;
1212

13-
TEST(StandardAffinePointTest, InfinityPreservesRawCoordinates)
13+
TEST(StandardAffinePointTest, InfinityDiscardsRawCoordinates)
1414
{
15+
// NOTE: As of #AVM-248, we moved from preserving raw coordinates in
16+
// infinity points to our (0,0) representation when using x() and y().
17+
// The underlying AffinePoint is set to AffinePoint::infinity() for
18+
// bb operations.
19+
1520
// When constructing an infinity point with non-zero coordinates,
16-
// x() and y() should return the raw coordinates
21+
// x() and y() should return our standard representation.
1722
Fq raw_x = 1;
1823
Fq raw_y = 2;
1924

25+
// Note that raw x and y are silently discarded.
2026
EmbeddedCurvePoint point(raw_x, raw_y, /*is_infinity=*/true);
2127

2228
EXPECT_TRUE(point.is_infinity());
23-
EXPECT_EQ(point.x(), raw_x);
24-
EXPECT_EQ(point.y(), raw_y);
29+
EXPECT_TRUE(point.x().is_zero());
30+
EXPECT_TRUE(point.y().is_zero());
2531
}
2632

2733
TEST(StandardAffinePointTest, NormalPointCoordinates)
@@ -80,18 +86,16 @@ TEST(StandardAffinePointTest, StaticInfinityHasZeroCoordinates)
8086
EXPECT_TRUE(inf.y().is_zero());
8187
}
8288

83-
TEST(StandardAffinePointTest, NegatingInfinityPreservesRawCoordinates)
89+
TEST(StandardAffinePointTest, NegatingInfinity)
8490
{
85-
// Negating an infinity point should preserve its raw coordinates
86-
Fq raw_x = 1;
87-
Fq raw_y = 2;
88-
EmbeddedCurvePoint inf(raw_x, raw_y, /*is_infinity=*/true);
91+
// Negating an infinity point should return (0,0,true)
92+
EmbeddedCurvePoint inf(0, 0, /*is_infinity=*/true);
8993

9094
auto neg_inf = -inf;
9195

9296
EXPECT_TRUE(neg_inf.is_infinity());
93-
EXPECT_EQ(neg_inf.x(), raw_x);
94-
EXPECT_EQ(neg_inf.y(), raw_y);
97+
EXPECT_TRUE(neg_inf.x().is_zero());
98+
EXPECT_TRUE(neg_inf.y().is_zero());
9599
}
96100

97101
} // namespace

barretenberg/cpp/src/barretenberg/vm2/constraining/relations/ecc.test.cpp

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1374,14 +1374,13 @@ TEST(EccAddMemoryConstrainingTest, InfinityRepresentations)
13741374

13751375
// Point P is infinity
13761376
EmbeddedCurvePoint inf = EmbeddedCurvePoint::infinity();
1377-
// EmbeddedCurvePoint preserves raw coordinates (see StandardAffinePointTest)
1377+
// EmbeddedCurvePoint always sets extractable coordinates as (0,0) and the underlying point as
1378+
// AffinePoint::infinity() for input infinity points.
13781379
EmbeddedCurvePoint inf_bb = EmbeddedCurvePoint(avm2::AffinePoint::infinity());
1379-
EmbeddedCurvePoint inf_alt = EmbeddedCurvePoint(1, 2, true);
1380+
EmbeddedCurvePoint inf_alt = EmbeddedCurvePoint(0, 7, true);
1381+
EXPECT_EQ(inf_bb, inf_alt);
13801382
TestTraceContainer trace;
13811383

1382-
// Internal add() expects normalized points:
1383-
EXPECT_THROW_WITH_MESSAGE(ecc_simulator.add(inf, inf_alt), "normalized");
1384-
13851384
// The circuit correctly assigns double_op = true when doubling inf:
13861385
ecc_simulator.add(memory, inf, inf_bb, dst_address);
13871386

@@ -1415,7 +1414,7 @@ TEST(EccAddMemoryConstrainingTest, InfinityRepresentations)
14151414
trace.set(C::ecc_add_mem_p_is_inf, 0, 0);
14161415
EXPECT_THROW_WITH_MESSAGE(check_relation<mem_aware_ecc>(trace, mem_aware_ecc::SR_P_CURVE_EQN), "P_CURVE_EQN");
14171416

1418-
// If is_if is set, the coordinates must be (0, 0):
1417+
// If is_inf is set, the coordinates must be (0, 0):
14191418
trace.set(C::ecc_add_mem_q_x, 0, 1);
14201419
trace.set(C::ecc_add_mem_q_y, 0, 2);
14211420
EXPECT_THROW_WITH_MESSAGE(check_relation<mem_aware_ecc>(trace, mem_aware_ecc::SR_Q_INF_X_CHECK), "Q_INF_X_CHECK");

barretenberg/cpp/src/barretenberg/vm2/simulation/gadgets/ecc.cpp

Lines changed: 7 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ class InternalEccException : public std::runtime_error {
1818
* @brief Adds Grumpkin curve points P and Q and emits an EccAddEvent.
1919
* Corresponds to the non-memory aware subtrace ecc.pil.
2020
*
21-
* @throws Unexpected exception if points are not on the curve or not normalized.
21+
* @throws Unexpected exception if points are not on the curve.
2222
* Note: This function assumes that the points p and q are on the curve. You should only use this function internally
2323
* if you can guarantee this. Otherwise it is called via the opcode ECADD, see the overloaded function Ecc::add (which
2424
* performs the curve check).
@@ -32,14 +32,6 @@ EmbeddedCurvePoint Ecc::add(const EmbeddedCurvePoint& p, const EmbeddedCurvePoin
3232
// Check if points are on the curve. These will throw an unexpected exception if they fail.
3333
BB_ASSERT(p.on_curve(), "Point p is not on the curve");
3434
BB_ASSERT(q.on_curve(), "Point q is not on the curve");
35-
// TODO(#AVM-266): Remove is_infinity flag from point representation.
36-
// Check if the points are normalized (infinity points must be (0, 0, true)).
37-
if (p.is_infinity()) {
38-
BB_ASSERT((p.x() == 0) && (p.y() == 0), "Point p is not normalized");
39-
}
40-
if (q.is_infinity()) {
41-
BB_ASSERT((q.x() == 0) && (q.y() == 0), "Point q is not normalized");
42-
}
4335

4436
EmbeddedCurvePoint result = p + q;
4537
add_events.emit({ .p = p, .q = q, .result = result });
@@ -71,14 +63,11 @@ EmbeddedCurvePoint Ecc::scalar_mul(const EmbeddedCurvePoint& point, const FF& sc
7163
// Emits ToRadixEvent, see #[TO_RADIX] in scalar_mul.pil.
7264
auto bits = to_radix.to_le_bits(scalar, 254).first;
7365

74-
// Normalize input infinity point (infinity points must be (0, 0, true)).
75-
EmbeddedCurvePoint point_input = point.is_infinity() ? EmbeddedCurvePoint::infinity() : point;
76-
7766
// First iteration does conditional assignment instead of addition. Note: in circuit we perform reverse aggregation,
7867
// so the corresponding constraints for below are gated by 'end'.
7968

8069
// See 'Temp Computation' section in scalar_mul.pil.
81-
EmbeddedCurvePoint temp = point_input;
70+
EmbeddedCurvePoint temp = point;
8271
bool bit = bits[0];
8372

8473
// See 'Result Computation' section in scalar_mul.pil.
@@ -95,10 +84,8 @@ EmbeddedCurvePoint Ecc::scalar_mul(const EmbeddedCurvePoint& point, const FF& sc
9584
}
9685
intermediate_states[i] = { result, temp, bit };
9786
}
98-
scalar_mul_events.emit({ .point = point_input,
99-
.scalar = scalar,
100-
.intermediate_states = std::move(intermediate_states),
101-
.result = result });
87+
scalar_mul_events.emit(
88+
{ .point = point, .scalar = scalar, .intermediate_states = std::move(intermediate_states), .result = result });
10289
return result;
10390
}
10491

@@ -136,20 +123,14 @@ void Ecc::add(MemoryInterface& memory,
136123
throw InternalEccException("dst address out of range");
137124
}
138125

139-
// TODO(#AVM-266): Note: bb infinity points (is_inf=true with x=(P+1)/2, y=0) pass on_curve() without
140-
// throwing here, but would throw in the circuit. We assume here input points follow the Noir convention
141-
// of (x=0, y=0) <==> is_infinity.
126+
// TODO(#AVM-266): Remove is_infinity flag from point representation. We assume here input
127+
// points follow the Noir convention of (x=0, y=0) <==> is_infinity.
142128
if (!p.on_curve() || !q.on_curve()) {
143129
throw InternalEccException("One of the points is not on the curve");
144130
}
145131

146-
// Normalize input infinity points.
147-
EmbeddedCurvePoint p_input = p.is_infinity() ? EmbeddedCurvePoint::infinity() : p;
148-
EmbeddedCurvePoint q_input = q.is_infinity() ? EmbeddedCurvePoint::infinity() : q;
149-
150132
// Emits EccAddEvent, see #[INPUT_OUTPUT_ECC_ADD] in ecc_mem.pil.
151-
EmbeddedCurvePoint result =
152-
add(p_input, q_input); // Cannot throw since we have checked on_curve() and normalized.
133+
EmbeddedCurvePoint result = add(p, q); // Cannot throw since we have checked on_curve().
153134

154135
// Emits MemoryEvents, see #[WRITE_MEM_i] for i = 0, 1, 2, in ecc_mem.pil.
155136
memory.set(dst_address, MemoryValue::from<FF>(result.x()));

barretenberg/cpp/src/barretenberg/vm2/tracegen/ecc_trace.cpp

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -299,14 +299,6 @@ void EccTraceBuilder::process_add_with_memory(
299299

300300
bool error = dst_out_of_range_err || !p_is_on_curve || !q_is_on_curve;
301301

302-
// TODO(#AVM-266): Remove is_infinity flag from point representation. For now, we derive is_inf by
303-
// checking coordinates in-circuit and ignoring the flag read from memory. Below, we do the 'reverse'
304-
// and set derive coordinates as (0, 0) if is_inf is true. This allows us to handle bb inf points until
305-
// the flag is removed.
306-
// Normalized points, ensures that input infinity points are represented by (0, 0) in the ecc subtrace.
307-
EmbeddedCurvePoint p_n = event.p.is_infinity() ? EmbeddedCurvePoint::infinity() : event.p;
308-
EmbeddedCurvePoint q_n = event.q.is_infinity() ? EmbeddedCurvePoint::infinity() : event.q;
309-
310302
trace.set(
311303
row,
312304
{ {
@@ -331,14 +323,14 @@ void EccTraceBuilder::process_add_with_memory(
331323
{ C::ecc_add_mem_dst_addr_1_, dst_addr + 1 },
332324
{ C::ecc_add_mem_dst_addr_2_, dst_addr + 2 },
333325
// Input - Point P
334-
{ C::ecc_add_mem_p_x, p_n.x() },
335-
{ C::ecc_add_mem_p_y, p_n.y() },
326+
{ C::ecc_add_mem_p_x, event.p.x() },
327+
{ C::ecc_add_mem_p_y, event.p.y() },
336328
{ C::ecc_add_mem_p_is_inf,
337329
event.p.is_infinity() ? 1 : 0 }, // TODO(#AVM-266): If committed, Will be p.x() == 0 && p.y() == 0
338330
{ C::ecc_add_mem_p_is_inf_, event.p.is_infinity() ? 1 : 0 }, // TODO(#AVM-266): Remove is_infinity flag
339331
// Input - Point Q
340-
{ C::ecc_add_mem_q_x, q_n.x() },
341-
{ C::ecc_add_mem_q_y, q_n.y() },
332+
{ C::ecc_add_mem_q_x, event.q.x() },
333+
{ C::ecc_add_mem_q_y, event.q.y() },
342334
{ C::ecc_add_mem_q_is_inf,
343335
event.q.is_infinity() ? 1 : 0 }, // TODO(#AVM-266): If committed, Will be q.x() == 0 && q.y() == 0
344336
{ C::ecc_add_mem_q_is_inf_, event.q.is_infinity() ? 1 : 0 }, // TODO(#AVM-266): Remove is_infinity flag

0 commit comments

Comments
 (0)