@@ -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
96116namespace 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 {
0 commit comments