Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
ecc_trace.cpp
Go to the documentation of this file.
2
3#include <cassert>
4#include <memory>
5
13
14namespace bb::avm2::tracegen {
15
16namespace {
17
18FF compute_lambda(bool double_predicate,
19 bool add_predicate,
20 bool result_is_infinity,
21 const EmbeddedCurvePoint& p,
22 const EmbeddedCurvePoint& q)
23{
24 // When doubling infinity lambda must be zero
25 // If not, we'd be inverting zero here
26 if (!result_is_infinity && double_predicate) {
27 return (p.x() * p.x() * 3) / (p.y() * 2);
28 }
29 if (add_predicate) {
30 return (q.y() - p.y()) / (q.x() - p.x());
31 }
32 return 0;
33}
34
35// Helper to compute the (re-formulated) Grumpkin curve equation: y^2 - (x^3 - 17)
36FF compute_curve_eqn_diff(const EmbeddedCurvePoint& p)
37{
38 if (p.is_infinity()) {
39 // We consider the curve equation to be trivially satisfied for the infinity point.
40 return FF::zero();
41 }
42 // The curve equation is y^2 = x^3 - 17
43 const FF y2 = p.y() * p.y();
44 const FF x3 = p.x() * p.x() * p.x();
45 return y2 - (x3 - FF(17));
46}
47
48} // namespace
49
51 TraceContainer& trace)
52{
53 using C = Column;
54
55 uint32_t row = 0;
56 for (const auto& event : events) {
57 EmbeddedCurvePoint p = event.p;
58 EmbeddedCurvePoint q = event.q;
59 EmbeddedCurvePoint result = event.result;
60
61 bool x_match = p.x() == q.x();
62 bool y_match = p.y() == q.y();
63
64 bool double_predicate = (x_match && y_match);
65 // add_predicate is true when x-coordinates differ (regardless of y-coordinates).
66 // PIL constraint: sel = double_op + add_op + INFINITY_PRED, where INFINITY_PRED = x_match * (1 - y_match).
67 // When x_match=0: double_op=0, INFINITY_PRED=0, so add_op must be 1.
68 bool add_predicate = !x_match;
69 // If x match but the y's don't, the result is the infinity point when adding;
70 bool infinity_predicate = (x_match && !y_match);
71 // The result is also the infinity point if
72 // (1) we hit the infinity predicate and neither p nor q are the infinity point
73 // (2) or both p and q are the infinity point
74 bool result_is_infinity = infinity_predicate && (!p.is_infinity() && !q.is_infinity());
75 result_is_infinity = result_is_infinity || (p.is_infinity() && q.is_infinity());
76
77 bool use_computed_result = !infinity_predicate && (!p.is_infinity() && !q.is_infinity());
78
79 BB_ASSERT_EQ(result_is_infinity, result.is_infinity(), "Inconsistent infinity result assumption");
80
81 FF lambda = compute_lambda(double_predicate, add_predicate, result_is_infinity, p, q);
82
83 trace.set(row,
84 { {
85 { C::ecc_sel, 1 },
86 // Point P
87 { C::ecc_p_x, p.x() },
88 { C::ecc_p_y, p.y() },
89 { C::ecc_p_is_inf, p.is_infinity() },
90 // Point Q
91 { C::ecc_q_x, q.x() },
92 { C::ecc_q_y, q.y() },
93 { C::ecc_q_is_inf, q.is_infinity() },
94 // Resulting point
95 { C::ecc_r_x, result.x() },
96 { C::ecc_r_y, result.y() },
97 { C::ecc_r_is_inf, result.is_infinity() },
98
99 // Temporary result boolean to decrease relation degree
100 { C::ecc_use_computed_result, use_computed_result },
101
102 // Check coordinates to detect edge cases (double, add and infinity)
103 { C::ecc_x_match, x_match },
104 { C::ecc_inv_x_diff, q.x() - p.x() }, // Will be inverted in batch later
105 { C::ecc_y_match, y_match },
106 { C::ecc_inv_y_diff, q.y() - p.y() }, // Will be inverted in batch later
107
108 // Witness for doubling operation
109 { C::ecc_double_op, double_predicate },
110 { C::ecc_inv_2_p_y,
111 !result_is_infinity && double_predicate ? (p.y() * 2)
112 : FF::zero() }, // Will be inverted in batch later
113
114 // Witness for add operation
115 { C::ecc_add_op, add_predicate },
116 // This is a witness for the result(r) being the point at infinity
117 // It is used to constrain that ecc_r_is_inf is correctly set.
118 { C::ecc_result_infinity, result_is_infinity },
119
120 { C::ecc_lambda, lambda },
121 } });
122
123 row++;
124 }
125
126 // Batch invert the columns.
127 trace.invert_columns({ { C::ecc_inv_x_diff, C::ecc_inv_y_diff, C::ecc_inv_2_p_y } });
128}
129
132{
133 using C = Column;
134
135 uint32_t row = 1; // We start from row 1 because this trace contains shifted columns.
136 for (const auto& event : events) {
137 size_t num_intermediate_states = event.intermediate_states.size();
138 EmbeddedCurvePoint point = event.point;
139
140 for (size_t i = 0; i < num_intermediate_states; ++i) {
141 // This trace uses reverse aggregation, so we need to process the bits in reverse
142 size_t intermediate_state_idx = num_intermediate_states - i - 1;
143
144 // The first bit processed is the end of the trace for the event
145 bool is_start = i == 0;
146
147 bool is_end = intermediate_state_idx == 0;
148
149 simulation::ScalarMulIntermediateState state = event.intermediate_states[intermediate_state_idx];
150 if (is_start) {
151 BB_ASSERT_EQ(state.res, event.result, "Inconsistent result assumption");
152 }
153 EmbeddedCurvePoint res = state.res;
154
155 EmbeddedCurvePoint temp = state.temp;
156 bool bit = state.bit;
157
158 trace.set(row,
159 { { { C::scalar_mul_sel, 1 },
160 { C::scalar_mul_scalar, event.scalar },
161 { C::scalar_mul_point_x, point.x() },
162 { C::scalar_mul_point_y, point.y() },
163 { C::scalar_mul_point_inf, point.is_infinity() },
164 { C::scalar_mul_res_x, res.x() },
165 { C::scalar_mul_res_y, res.y() },
166 { C::scalar_mul_res_inf, res.is_infinity() },
167 { C::scalar_mul_start, is_start },
168 { C::scalar_mul_end, is_end },
169 { C::scalar_mul_not_end, !is_end },
170 { C::scalar_mul_bit, bit },
171 { C::scalar_mul_bit_idx, intermediate_state_idx },
172 { C::scalar_mul_temp_x, temp.x() },
173 { C::scalar_mul_temp_y, temp.y() },
174 { C::scalar_mul_temp_inf, temp.is_infinity() },
175 {
176 C::scalar_mul_should_add,
177 (!is_end) && bit,
178 },
179 { C::scalar_mul_bit_radix, 2 } } });
180
181 row++;
182 }
183 }
184}
185
188{
189 using C = Column;
190
191 uint32_t row = 0;
192 for (const auto& event : events) {
193 uint64_t dst_addr = static_cast<uint64_t>(event.dst_address);
194
195 // Error handling, check if the destination address is out of range.
196 // The max write address is dst_addr + 2, since we write 3 values (x, y, is_inf).
197 bool dst_out_of_range_err = dst_addr + 2 > AVM_HIGHEST_MEM_ADDRESS;
198
199 // Error handling, check if the points are on the curve.
200 // We do not use batch inversions as we do not need to invert in the happy path.
201 bool p_is_on_curve = event.p.on_curve();
202 FF p_is_on_curve_eqn = compute_curve_eqn_diff(event.p);
203 FF p_is_on_curve_eqn_inv = p_is_on_curve ? FF::zero() : p_is_on_curve_eqn.invert();
204
205 bool q_is_on_curve = event.q.on_curve();
206 FF q_is_on_curve_eqn = compute_curve_eqn_diff(event.q);
207 FF q_is_on_curve_eqn_inv = q_is_on_curve ? FF::zero() : q_is_on_curve_eqn.invert();
208
209 bool error = dst_out_of_range_err || !p_is_on_curve || !q_is_on_curve;
210
211 // Normalized points, ensures that input infinity points are represented by (0, 0) in the ecc subtrace.
212 EmbeddedCurvePoint p_n = event.p.is_infinity() ? EmbeddedCurvePoint::infinity() : event.p;
213 EmbeddedCurvePoint q_n = event.q.is_infinity() ? EmbeddedCurvePoint::infinity() : event.q;
214
215 trace.set(row,
216 { {
217 { C::ecc_add_mem_sel, 1 },
218 { C::ecc_add_mem_execution_clk, event.execution_clk },
219 { C::ecc_add_mem_space_id, event.space_id },
220 // Error handling - dst out of range
221 { C::ecc_add_mem_max_mem_addr, AVM_HIGHEST_MEM_ADDRESS },
222 { C::ecc_add_mem_sel_dst_out_of_range_err, dst_out_of_range_err ? 1 : 0 },
223 // Error handling - p is not on curve
224 { C::ecc_add_mem_sel_p_not_on_curve_err, !p_is_on_curve ? 1 : 0 },
225 { C::ecc_add_mem_p_is_on_curve_eqn, p_is_on_curve_eqn },
226 { C::ecc_add_mem_p_is_on_curve_eqn_inv, p_is_on_curve_eqn_inv },
227 // Error handling - q is not on curve
228 { C::ecc_add_mem_sel_q_not_on_curve_err, !q_is_on_curve ? 1 : 0 },
229 { C::ecc_add_mem_q_is_on_curve_eqn, q_is_on_curve_eqn },
230 { C::ecc_add_mem_q_is_on_curve_eqn_inv, q_is_on_curve_eqn_inv },
231 // Consolidated error
232 { C::ecc_add_mem_err, error ? 1 : 0 },
233 // Memory Writes
234 { C::ecc_add_mem_dst_addr_0_, dst_addr },
235 { C::ecc_add_mem_dst_addr_1_, dst_addr + 1 },
236 { C::ecc_add_mem_dst_addr_2_, dst_addr + 2 },
237 // Input - Point P
238 { C::ecc_add_mem_p_x, event.p.x() },
239 { C::ecc_add_mem_p_y, event.p.y() },
240 { C::ecc_add_mem_p_is_inf, event.p.is_infinity() ? 1 : 0 },
241 // Input - Point Q
242 { C::ecc_add_mem_q_x, event.q.x() },
243 { C::ecc_add_mem_q_y, event.q.y() },
244 { C::ecc_add_mem_q_is_inf, event.q.is_infinity() ? 1 : 0 },
245 // Normalized input - Point P
246 { C::ecc_add_mem_p_x_n, p_n.x() },
247 { C::ecc_add_mem_p_y_n, p_n.y() },
248 // Normalized input - Point Q
249 { C::ecc_add_mem_q_x_n, q_n.x() },
250 { C::ecc_add_mem_q_y_n, q_n.y() },
251 // Output
252 { C::ecc_add_mem_sel_should_exec, error ? 0 : 1 },
253 { C::ecc_add_mem_res_x, event.result.x() },
254 { C::ecc_add_mem_res_y, event.result.y() },
255 { C::ecc_add_mem_res_is_inf, event.result.is_infinity() },
256 } });
257
258 row++;
259 }
260}
261
265 .add<lookup_scalar_mul_add_settings, InteractionType::LookupGeneric>()
267 // Memory Aware Interactions
268 // Comparison
269 .add<lookup_ecc_mem_check_dst_addr_in_range_settings, InteractionType::LookupGeneric>(Column::gt_sel)
270 // Lookup into ECC Add Subtrace
272
273} // namespace bb::avm2::tracegen
#define BB_ASSERT_EQ(actual, expected,...)
Definition assert.hpp:83
#define AVM_HIGHEST_MEM_ADDRESS
constexpr bool is_infinity() const noexcept
constexpr const BaseField & x() const noexcept
constexpr const BaseField & y() const noexcept
void process_add_with_memory(const simulation::EventEmitterInterface< simulation::EccAddMemoryEvent >::Container &events, TraceContainer &trace)
void process_add(const simulation::EventEmitterInterface< simulation::EccAddEvent >::Container &events, TraceContainer &trace)
Definition ecc_trace.cpp:50
void process_scalar_mul(const simulation::EventEmitterInterface< simulation::ScalarMulEvent >::Container &events, TraceContainer &trace)
static const InteractionDefinition interactions
Definition ecc_trace.hpp:23
InteractionDefinition & add(auto &&... args)
uint32_t dst_addr
TestTraceContainer trace
lookup_settings< lookup_ecc_mem_input_output_ecc_add_settings_ > lookup_ecc_mem_input_output_ecc_add_settings
lookup_settings< lookup_scalar_mul_double_settings_ > lookup_scalar_mul_double_settings
StandardAffinePoint< AvmFlavorSettings::EmbeddedCurve::AffineElement > EmbeddedCurvePoint
Definition field.hpp:12
lookup_settings< lookup_scalar_mul_to_radix_settings_ > lookup_scalar_mul_to_radix_settings
AvmFlavorSettings::FF FF
Definition field.hpp:10
simulation::PublicDataTreeReadWriteEvent event