Skip to content

Commit 1d85eb3

Browse files
committed
feat: WIP first pass at deriving is_inf from 0,0 coords
1 parent b261458 commit 1d85eb3

16 files changed

Lines changed: 207 additions & 158 deletions

File tree

barretenberg/cpp/pil/vm2/bytecode/address_derivation.pil

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ include "../scalar_mul.pil";
3434
* as an independent 'destination' trace which is purely responsible for address
3535
* computation.
3636
* This means we assume all public keys are not the point at infinity, and so use
37-
* precomputed.zero to represent each key's is_infinity flag (see TODO(#7529)).
37+
* precomputed.zero to represent each key's is_infinity flag (see TODO(#7529) and PR #22462).
3838
*
3939
* USAGE: To enforce that an address is correctly derived from all preimage members
4040
* (adapted from #[ADDRESS_DERIVATION] in contract_instance_retrieval.pil):
@@ -183,6 +183,8 @@ namespace address_derivation;
183183
// 3. Computation of public keys hash
184184
pol commit public_keys_hash;
185185

186+
// TODO(#AVM-266): Remove infinity flags from point representation. Note that we may still need to use
187+
// precomputed.zero in the hash preimages until address derivation removes them:
186188
// TODO(#7529): Remove all the 0s for is_infinity when removed from public_keys.nr
187189
// https://github.com/AztecProtocol/aztec-packages/issues/7529
188190
// TODO(#14031): Compress keys in public_keys_hash

barretenberg/cpp/pil/vm2/ecc.pil

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@
66
* Grumpkin Curve Eqn in SW form: Y^2 = X^3 − 17.
77
* Note: Grumpkin forms a 2-cycle with BN254, i.e the base field of one is the scalar field of the other and vice-versa.
88
*
9+
* Note that once TODO(#AVM-266) is complete, is_inf will no longer be part of our point representation and we must either:
10+
* - continue to rely on the 'calling' trace to inject a constrained is_inf, or
11+
* - derive is_inf (<==> (X, Y) == (INFINITY_X, INFINITY_Y)) within this trace.
12+
*
913
* USAGE: This is a non-memory aware subtrace used to constrain point addition as defined above. Each point can be looked up
1014
* by coordinates (lookup as defined in ecc_mem.pil):
1115
* #[INPUT_OUTPUT_ECC_ADD]

barretenberg/cpp/pil/vm2/ecc_mem.pil

Lines changed: 51 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ include "precomputed.pil";
88
* This handles the memory writes when the ECADD opcode is executed by user code.
99
* Given two points, P & Q, this trace constrains that both exist on the Grumpkin curve
1010
* and the claimed result point, R = P + Q, is written to memory addresses within range.
11+
* A point exists on the curve if it satisfies the curve equation (SW form: Y^2 = X^3 − 17)
12+
* or is the point at infinity, represented by (X, Y) = (0, 0).
1113
* The reads of P and Q are handled by the registers in the execution trace. The correctness
1214
* of point addition is handled by the ECC subtrace.
1315
*
@@ -37,25 +39,29 @@ include "precomputed.pil";
3739
* Opcode operands (relevant in EXECUTION when interacting with this gadget):
3840
* - rop[0]: p_x_addr
3941
* - rop[1]: p_y_addr
40-
* - rop[2]: p_is_inf_addr
42+
* - rop[2]: p_is_inf_addr (ignored)
4143
* - rop[3]: q_x_addr
4244
* - rop[4]: q_y_addr
43-
* - rop[5]: q_is_inf_addr
45+
* - rop[5]: q_is_inf_addr (ignored)
4446
* - rop[6]: dst_addr
4547
*
48+
* Note: The values at p_is_inf_addr, q_is_inf_addr are ignored by this circuit and will be removed
49+
* in TODO(#AVM-266). We instead derive whether the point is infinity by checking its coordinates
50+
* for our standard representation of (0, 0). See below for details on infinity points.
51+
*
4652
* Memory I/O:
4753
* - register[0]: M[p_x_addr] aka p_x (x coordinate of point P - read from memory by EXECUTION)
4854
* - p_x is tagged-checked by execution/registers to be FF based on instruction spec.
4955
* - register[1]: M[p_y_addr] aka p_y (y coordinate of point P - read from memory by EXECUTION)
5056
* - p_y is tagged-checked by execution/registers to be FF based on instruction spec.
5157
* - register[2]: M[p_is_inf_addr] aka p_is_inf (boolean flag if P is the point at infinity - read from memory by EXECUTION)
52-
* - p_is_inf is tagged-checked by execution/registers to be U1 based on instruction spec.
58+
* - Note: ignored by this circuit and will be removed in TODO(#AVM-266).
5359
* - register[3]: M[q_x_addr] aka q_x (x coordinate of point Q - read from memory by EXECUTION)
5460
* - q_x is tagged-checked by execution/registers to be FF based on instruction spec.
5561
* - register[4]: M[q_y_addr] aka q_y (y coordinate of point Q - read from memory by EXECUTION)
5662
* - q_y is tagged-checked by execution/registers to be FF based on instruction spec.
5763
* - register[5]: M[q_is_inf_addr] aka q_is_inf (boolean flag if Q is the point at infinity - read from memory by EXECUTION)
58-
* - q_is_inf is tagged-checked by execution/registers to be U1 based on instruction spec.
64+
* - Note: ignored by this circuit and will be removed in TODO(#AVM-266).
5965
* - M[rop[6]]: M[dst_addr] aka res_x (x coordinate of the resulting point RES - written by this gadget)
6066
* - guaranteed by this gadget to be FF.
6167
* - M[rop[6]+1]: M[dst_offset+1] aka res_y (y coordinate of the resulting point RES - written by this gadget)
@@ -68,7 +74,7 @@ include "precomputed.pil";
6874
* (1) DST_OUT_OF_BOUNDS_ACCESS: If the writes would access a memory address outside
6975
* of the max AVM memory address (AVM_HIGHEST_MEM_ADDRESS).
7076
* (2) POINT_NOT_ON_CURVE: If either of the inputs (embedded curve points) do not
71-
* satisfy the Grumpkin curve equation (SW form: Y^2 = X^3 − 17)
77+
* exist on the Grumpkin curve.
7278
*
7379
* TRACE SHAPE: This subtrace writes the values within 1 single row (i.e. 3 output columns)
7480
*
@@ -87,10 +93,24 @@ include "precomputed.pil";
8793
* (#[INPUT_OUTPUT_ECC_ADD]). See below for details on infinity points.
8894
* - gt.pil: To constrain that the maximum written memory address is within range (#[CHECK_DST_ADDR_IN_RANGE]).
8995
*
90-
* This subtrace is connected to the ECC subtrace via a lookup. ECC is used by
91-
* other subtraces internally (e.g., address derivation). We re-map any input infinity points to (0, 0)
92-
* so ECC correctly manages edge cases. Resulting infinity points are guaranteed to be (0, 0) by the
93-
* ECC subtrace (see #[OUTPUT_X_COORD] and #[OUTPUT_Y_COORD]).
96+
* This subtrace is connected to the ECC subtrace via a lookup. ECC is used by other subtraces internally
97+
* (e.g. address derivation). Now that the is_inf flag has been removed from noir (noir-lang/noir/#11926/) we consider
98+
* a point to be infinity iff its coordinates are (0, 0); the noir standard representation.
99+
*
100+
* Note that the point at infinity, O, does not have valid coordinates (a property of SW curves like Grumpkin). We represent it
101+
* as (0, 0) but any (ecc.INFINITY_X, ecc.INFINITY_Y) not satisfying the curve equation will be correctly handled in this trace.
102+
*
103+
* For now, we simply ignore any is_inf flags coming from memory (assigning and not reading the placeholder is_inf_), but
104+
* will eventually remove it from the operands TODO(#AVM-266).
105+
*
106+
* TODO(MW): Is ignoring i.e. leaving a memory operand unconstrained safe? Execution still reads them but will remove
107+
* p/q_is_inf_ if so.
108+
*
109+
* Until #AVM-266, the ECC subtrace still requires a correctly constrained is_inf flag for each point. We derive it within
110+
* this circuit by enforcing (0, 0) <==> is_inf for input points P and Q:
111+
* - (0, 0) ==> is_inf by #[P/Q_ON_CURVE_CHECK] (zero coordinates will fail this relation unless is_inf is set correctly).
112+
* - is_inf ==> (0, 0) by #[P/Q_INF_X/Y_CHECK].
113+
* Resulting infinity points are guaranteed to be (0, 0) by the ECC subtrace (see #[OUTPUT_X_COORD] and #[OUTPUT_Y_COORD]).
94114
*/
95115

96116
namespace ecc_add_mem;
@@ -110,9 +130,13 @@ namespace ecc_add_mem;
110130
dst_addr[1] = sel * (dst_addr[0] + 1);
111131
dst_addr[2] = sel * (dst_addr[0] + 2);
112132

113-
// p_is_inf, q_is_inf are constrained to be @boolean in the ecc subtrace (see #[INPUT_OUTPUT_ECC_ADD])
114-
pol commit p_x, p_y, p_is_inf;
115-
pol commit q_x, q_y, q_is_inf;
133+
// TODO(#AVM-266): Remove p_is_inf_, q_is_inf_ (currently placeholders for #[DISPATCH_TO_ECC_ADD]).
134+
pol commit p_x, p_y, p_is_inf_;
135+
pol commit q_x, q_y, q_is_inf_;
136+
137+
// TODO(#AVM-266): Remove p_is_inf, q_is_inf entirely.
138+
// Needs to be committed columns as they are used in the lookups
139+
pol commit p_is_inf, q_is_inf; // constrained to be @boolean in the ecc subtrace (see #[INPUT_OUTPUT_ECC_ADD])
116140

117141
////////////////////////////////////////////////
118142
// Error Handling - Out of Range Memory Access
@@ -142,6 +166,8 @@ namespace ecc_add_mem;
142166
pol commit sel_q_not_on_curve_err; // @boolean
143167
sel_q_not_on_curve_err * (1 - sel_q_not_on_curve_err) = 0;
144168

169+
// Note: The below additionally constrains that (X, Y) == (INFINITY_X, INFINITY_Y) ==> is_inf. See above description.
170+
145171
// Y^2 = X^3 − 17, re-formulate to Y^2 - (X^3 - 17) = 0
146172
pol commit p_is_on_curve_eqn; // Needs to be committed to reduce relation degrees
147173
pol P_X3 = p_x * p_x * p_x;
@@ -174,22 +200,23 @@ namespace ecc_add_mem;
174200
pol commit sel_should_exec; // @boolean (by definition)
175201
sel_should_exec = sel * (1 - err);
176202

177-
// Needs to be committed columns as they are used in the lookups
178-
pol commit p_x_n, p_y_n;
179-
pol commit q_x_n, q_y_n;
180-
181-
// We re-map input infinity points to (0, 0) for ecc.pil. Output infinities are already constrained to be (0, 0) in the subtrace.
182-
// Note that we cannot use p_x, p_y, etc. because they are read from memory in execution's #[DISPATCH_TO_ECC_ADD].
183-
sel_should_exec * (p_x_n - (1 - p_is_inf) * p_x - p_is_inf * ecc.INFINITY_X) = 0;
184-
sel_should_exec * (p_y_n - (1 - p_is_inf) * p_y - p_is_inf * ecc.INFINITY_Y) = 0;
185-
sel_should_exec * (q_x_n - (1 - q_is_inf) * q_x - q_is_inf * ecc.INFINITY_X) = 0;
186-
sel_should_exec * (q_y_n - (1 - q_is_inf) * q_y - q_is_inf * ecc.INFINITY_Y) = 0;
203+
// TODO(#AVM-266): Remove p_is_inf, q_is_inf entirely. For now, we ensure that the flag being set means that the coordinates
204+
// are set to our infinity representation: (INFINITY_X, INFINITY_Y) = (0, 0). The reverse ((INFINITY_X, INFINITY_Y) ==> is_inf)
205+
// is constrained by #[P/Q_ON_CURVE_CHECK]. Output infinities are already constrained to be (0, 0) in the subtrace.
206+
#[P_INF_X_CHECK]
207+
sel * p_is_inf * (p_x - ecc.INFINITY_X) = 0;
208+
#[P_INF_Y_CHECK]
209+
sel * p_is_inf * (p_y - ecc.INFINITY_Y) = 0;
210+
#[Q_INF_X_CHECK]
211+
sel * q_is_inf * (q_x - ecc.INFINITY_X) = 0;
212+
#[Q_INF_Y_CHECK]
213+
sel * q_is_inf * (q_y - ecc.INFINITY_Y) = 0;
187214

188215

189216
#[INPUT_OUTPUT_ECC_ADD]
190217
sel_should_exec {
191-
p_x_n, p_y_n, p_is_inf,
192-
q_x_n, q_y_n, q_is_inf,
218+
p_x, p_y, p_is_inf,
219+
q_x, q_y, q_is_inf,
193220
res_x, res_y, res_is_inf
194221
} in
195222
ecc.sel {

barretenberg/cpp/pil/vm2/execution.pil

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1082,6 +1082,7 @@ sel_exec_dispatch_keccakf1600 {
10821082
// ECADD DISPATCHING
10831083
// Each input point uses 3 registers:
10841084
// P = [x, y, is_inf] -> register[0..2], Q = [x, y, is_inf] -> register[3..5]
1085+
// TODO(#AVM-266): Remove is_inf and use 2 registers per point (for now, the 3rd register is read but ignored).
10851086
// The output point is written to memory internally inside the ecc_add_mem (ecc_mem.pil) trace, starting at:
10861087
// dst_addr[0] -> rop[6]
10871088
// Outputs (#[WRITE_MEM_x]) and memory write checks (#[CHECK_DST_ADDR_IN_RANGE]) are hence handled by the trace.
@@ -1108,11 +1109,11 @@ sel_exec_dispatch_ecc_add {
11081109
// Point P
11091110
ecc_add_mem.p_x,
11101111
ecc_add_mem.p_y,
1111-
ecc_add_mem.p_is_inf,
1112+
ecc_add_mem.p_is_inf_,
11121113
// Point Q
11131114
ecc_add_mem.q_x,
11141115
ecc_add_mem.q_y,
1115-
ecc_add_mem.q_is_inf,
1116+
ecc_add_mem.q_is_inf_,
11161117
// Dst address
11171118
ecc_add_mem.dst_addr[0],
11181119
// Error

barretenberg/cpp/pil/vm2/scalar_mul.pil

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,17 @@ include "precomputed.pil";
1717
*
1818
* The correctness of point addition (res + temp and temp + temp above) is constrained by a lookup into ecc.pil.
1919
*
20-
* PRECONDITIONS: Input P is a valid point on the Grumpkin curve, input s is FF (see below note).
20+
* PRECONDITIONS: Input P is a valid point on the Grumpkin curve, either satisfying the curve equation or the point
21+
* at infinity representation (such that is_inf <==> (X, Y) == (ecc.INFINITY_X, ecc.INFINITY_Y)), and input s is FF.
2122
* Note: Grumpkin forms a 2-cycle with BN254, i.e the base field of one is the scalar field of the other and vice-versa,
2223
* so the scalar is actually a Fq value. We treat it as a Fr (FF tagged) value in circuit. Since r < q, we cannot use an
2324
* invalid scalar here.
2425
*
25-
* USAGE: This is a non-memory aware subtrace used to constrain scalar point multiplicationas defined above. Each point can
26+
* Note that once TODO(#AVM-266) is complete, is_inf will no longer be part of our point representation and we must either:
27+
* - continue to rely on the 'calling' trace to inject a constrained is_inf, or
28+
* - derive is_inf (<==> (X, Y) == (ecc.INFINITY_X, ecc.INFINITY_Y)) within this trace.
29+
*
30+
* USAGE: This is a non-memory aware subtrace used to constrain scalar point multiplication as defined above. Each point can
2631
* be looked up by coordinates (lookup as defined in address_derivation.pil):
2732
* #[PREADDRESS_SCALAR_MUL]
2833
* sel {
@@ -76,12 +81,14 @@ namespace scalar_mul;
7681
// Point P in affine form
7782
pol commit point_x;
7883
pol commit point_y;
84+
// TODO(#AVM-266): Remove infinity flags from point representation.
7985
pol commit point_inf; // @boolean
8086
point_inf * (1 - point_inf) = 0;
8187

8288
// Point R = sP in affine form
8389
pol commit res_x;
8490
pol commit res_y;
91+
// TODO(#AVM-266): Remove infinity flags from point representation.
8592
pol commit res_inf; // @boolean (constrained to be @boolean by lookup in the ecc subtrace)
8693

8794
///////////////////////////////

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@ namespace bb::avm2 {
1616
* NOTE: When constructing infinity via BaseFields, input coordinates are maintained and can be any values, so may
1717
* mismatch the underlying AffinePoint. Always check is_infinity() before ECC operations on coordinates. See test
1818
* InfinityPreservesRawCoordinates for an example.
19+
*
20+
* 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.
1923
*/
2024
template <typename AffinePoint> class StandardAffinePoint {
2125
public:

0 commit comments

Comments
 (0)