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
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 bool add_predicate = (!x_match && !y_match);
66 // If x match but the y's don't, the result is the infinity point when adding;
67 bool infinity_predicate = (x_match && !y_match);
68 // The result is also the infinity point if
69 // (1) we hit the infinity predicate and neither p nor q are the infinity point
70 // (2) or both p and q are the infinity point
71 bool result_is_infinity = infinity_predicate && (!p.is_infinity() && !q.is_infinity());
72 result_is_infinity = result_is_infinity || (p.is_infinity() && q.is_infinity());
73
74 bool use_computed_result = !infinity_predicate && (!p.is_infinity() && !q.is_infinity());
75
76 assert(result_is_infinity == result.is_infinity() && "Inconsistent infinity result assumption");
77
78 FF lambda = compute_lambda(double_predicate, add_predicate, result_is_infinity, p, q);
79
80 trace.set(row,
81 { {
82 { C::ecc_sel, 1 },
83 // Point P
84 { C::ecc_p_x, p.x() },
85 { C::ecc_p_y, p.y() },
86 { C::ecc_p_is_inf, p.is_infinity() },
87 // Point Q
88 { C::ecc_q_x, q.x() },
89 { C::ecc_q_y, q.y() },
90 { C::ecc_q_is_inf, q.is_infinity() },
91 // Resulting point
92 { C::ecc_r_x, result.x() },
93 { C::ecc_r_y, result.y() },
94 { C::ecc_r_is_inf, result.is_infinity() },
95
96 // Temporary result boolean to decrease relation degree
97 { C::ecc_use_computed_result, use_computed_result },
98
99 // Check coordinates to detect edge cases (double, add and infinity)
100 { C::ecc_x_match, x_match },
101 { C::ecc_inv_x_diff, q.x() - p.x() }, // Will be inverted in batch later
102 { C::ecc_y_match, y_match },
103 { C::ecc_inv_y_diff, q.y() - p.y() }, // Will be inverted in batch later
104
105 // Witness for doubling operation
106 { C::ecc_double_op, double_predicate },
107 { C::ecc_inv_2_p_y,
108 !result_is_infinity && double_predicate ? (p.y() * 2)
109 : FF::zero() }, // Will be inverted in batch later
110
111 // Witness for add operation
112 { C::ecc_add_op, add_predicate },
113 // This is a witness for the result(r) being the point at infinity
114 // It is used to constrain that ecc_r_is_inf is correctly set.
115 { C::ecc_result_infinity, result_is_infinity },
116
117 { C::ecc_lambda, lambda },
118 } });
119
120 row++;
121 }
122
123 // Batch invert the columns.
124 trace.invert_columns({ { C::ecc_inv_x_diff, C::ecc_inv_y_diff, C::ecc_inv_2_p_y } });
125}
126
129{
130 using C = Column;
131
132 uint32_t row = 1; // We start from row 1 because this trace contains shifted columns.
133 for (const auto& event : events) {
134 size_t num_intermediate_states = event.intermediate_states.size();
135 EmbeddedCurvePoint point = event.point;
136
137 for (size_t i = 0; i < num_intermediate_states; ++i) {
138 // This trace uses reverse aggregation, so we need to process the bits in reverse
139 size_t intermediate_state_idx = num_intermediate_states - i - 1;
140
141 // The first bit processed is the end of the trace for the event
142 bool is_start = i == 0;
143
144 bool is_end = intermediate_state_idx == 0;
145
146 simulation::ScalarMulIntermediateState state = event.intermediate_states[intermediate_state_idx];
147 if (is_start) {
148 assert(state.res == event.result);
149 }
150 EmbeddedCurvePoint res = state.res;
151
152 EmbeddedCurvePoint temp = state.temp;
153 bool bit = state.bit;
154
155 trace.set(row,
156 { { { C::scalar_mul_sel, 1 },
157 { C::scalar_mul_scalar, event.scalar },
158 { C::scalar_mul_point_x, point.x() },
159 { C::scalar_mul_point_y, point.y() },
160 { C::scalar_mul_point_inf, point.is_infinity() },
161 { C::scalar_mul_res_x, res.x() },
162 { C::scalar_mul_res_y, res.y() },
163 { C::scalar_mul_res_inf, res.is_infinity() },
164 { C::scalar_mul_start, is_start },
165 { C::scalar_mul_end, is_end },
166 { C::scalar_mul_not_end, !is_end },
167 { C::scalar_mul_bit, bit },
168 { C::scalar_mul_bit_idx, intermediate_state_idx },
169 { C::scalar_mul_temp_x, temp.x() },
170 { C::scalar_mul_temp_y, temp.y() },
171 { C::scalar_mul_temp_inf, temp.is_infinity() },
172 {
173 C::scalar_mul_should_add,
174 (!is_end) && bit,
175 },
176 { C::scalar_mul_bit_radix, 2 } } });
177
178 row++;
179 }
180 }
181}
182
185{
186 using C = Column;
187
188 uint32_t row = 0;
189 for (const auto& event : events) {
190 uint64_t dst_addr = static_cast<uint64_t>(event.dst_address);
191
192 // Error handling, check if the destination address is out of range.
193 // The max write address is dst_addr + 2, since we write 3 values (x, y, is_inf).
194 bool dst_out_of_range_err = dst_addr + 2 > AVM_HIGHEST_MEM_ADDRESS;
195
196 // Error handling, check if the points are on the curve.
197 // We do not use batch inversions as we do not need to invert in the happy path.
198 bool p_is_on_curve = event.p.on_curve();
199 FF p_is_on_curve_eqn = compute_curve_eqn_diff(event.p);
200 FF p_is_on_curve_eqn_inv = p_is_on_curve ? FF::zero() : p_is_on_curve_eqn.invert();
201
202 bool q_is_on_curve = event.q.on_curve();
203 FF q_is_on_curve_eqn = compute_curve_eqn_diff(event.q);
204 FF q_is_on_curve_eqn_inv = q_is_on_curve ? FF::zero() : q_is_on_curve_eqn.invert();
205
206 bool error = dst_out_of_range_err || !p_is_on_curve || !q_is_on_curve;
207
208 trace.set(row,
209 { {
210 { C::ecc_add_mem_sel, 1 },
211 { C::ecc_add_mem_execution_clk, event.execution_clk },
212 { C::ecc_add_mem_space_id, event.space_id },
213 // Error handling - dst out of range
214 { C::ecc_add_mem_max_mem_addr, AVM_HIGHEST_MEM_ADDRESS },
215 { C::ecc_add_mem_sel_dst_out_of_range_err, dst_out_of_range_err ? 1 : 0 },
216 // Error handling - p is not on curve
217 { C::ecc_add_mem_sel_p_not_on_curve_err, !p_is_on_curve ? 1 : 0 },
218 { C::ecc_add_mem_p_is_on_curve_eqn, p_is_on_curve_eqn },
219 { C::ecc_add_mem_p_is_on_curve_eqn_inv, p_is_on_curve_eqn_inv },
220 // Error handling - q is not on curve
221 { C::ecc_add_mem_sel_q_not_on_curve_err, !q_is_on_curve ? 1 : 0 },
222 { C::ecc_add_mem_q_is_on_curve_eqn, q_is_on_curve_eqn },
223 { C::ecc_add_mem_q_is_on_curve_eqn_inv, q_is_on_curve_eqn_inv },
224 // Consolidated error
225 { C::ecc_add_mem_err, error ? 1 : 0 },
226 // Memory Writes
227 { C::ecc_add_mem_dst_addr_0_, dst_addr },
228 { C::ecc_add_mem_dst_addr_1_, dst_addr + 1 },
229 { C::ecc_add_mem_dst_addr_2_, dst_addr + 2 },
230 // Input - Point P
231 { C::ecc_add_mem_p_x, event.p.x() },
232 { C::ecc_add_mem_p_y, event.p.y() },
233 { C::ecc_add_mem_p_is_inf, event.p.is_infinity() ? 1 : 0 },
234 // Input - Point Q
235 { C::ecc_add_mem_q_x, event.q.x() },
236 { C::ecc_add_mem_q_y, event.q.y() },
237 { C::ecc_add_mem_q_is_inf, event.q.is_infinity() ? 1 : 0 },
238 // Output
239 { C::ecc_add_mem_sel_should_exec, error ? 0 : 1 },
240 { C::ecc_add_mem_res_x, event.result.x() },
241 { C::ecc_add_mem_res_y, event.result.y() },
242 { C::ecc_add_mem_res_is_inf, event.result.is_infinity() },
243 } });
244
245 row++;
246 }
247}
248
252 .add<lookup_scalar_mul_add_settings, InteractionType::LookupGeneric>()
254 // Memory Aware Interactions
255 // Comparison
256 .add<lookup_ecc_mem_check_dst_addr_in_range_settings, InteractionType::LookupGeneric>(Column::gt_sel)
257 // Lookup into ECC Add Subtrace
259
260} // namespace bb::avm2::tracegen
#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
static constexpr field zero()