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
14
15namespace bb::avm2::tracegen {
16
17namespace {
18
19FF compute_lambda(bool double_predicate,
20 bool add_predicate,
21 bool result_is_infinity,
22 const EmbeddedCurvePoint& p,
23 const EmbeddedCurvePoint& q)
24{
25 // When doubling infinity lambda must be zero
26 // If not, we'd be inverting zero here
27 if (!result_is_infinity && double_predicate) {
28 return (p.x() * p.x() * 3) / (p.y() * 2);
29 }
30 if (add_predicate) {
31 return (q.y() - p.y()) / (q.x() - p.x());
32 }
33 return 0;
34}
35
36// Helper to compute the (re-formulated) Grumpkin curve equation: y^2 - (x^3 - 17)
37FF compute_curve_eqn_diff(const EmbeddedCurvePoint& p)
38{
39 if (p.is_infinity()) {
40 // We consider the curve equation to be trivially satisfied for the infinity point.
41 return FF::zero();
42 }
43 // The curve equation is y^2 = x^3 - 17
44 const FF y2 = p.y() * p.y();
45 const FF x3 = p.x() * p.x() * p.x();
46 return y2 - (x3 - FF(17));
47}
48
49} // namespace
50
53{
54 using C = Column;
55
56 uint32_t row = 0;
57 for (const auto& event : events) {
58 EmbeddedCurvePoint p = event.p;
59 EmbeddedCurvePoint q = event.q;
60 EmbeddedCurvePoint result = event.result;
61
62 bool x_match = p.x() == q.x();
63 bool y_match = p.y() == q.y();
64
65 bool double_predicate = (x_match && y_match);
66 bool add_predicate = (!x_match && !y_match);
67 // If x match but the y's don't, the result is the infinity point when adding;
68 bool infinity_predicate = (x_match && !y_match);
69 // The result is also the infinity point if
70 // (1) we hit the infinity predicate and neither p nor q are the infinity point
71 // (2) or both p and q are the infinity point
72 bool result_is_infinity = infinity_predicate && (!p.is_infinity() && !q.is_infinity());
73 result_is_infinity = result_is_infinity || (p.is_infinity() && q.is_infinity());
74
75 bool use_computed_result = !infinity_predicate && (!p.is_infinity() && !q.is_infinity());
76
77 assert(result_is_infinity == result.is_infinity() && "Inconsistent infinity result assumption");
78
79 FF lambda = compute_lambda(double_predicate, add_predicate, result_is_infinity, p, q);
80
81 trace.set(row,
82 { {
83 { C::ecc_sel, 1 },
84 // Point P
85 { C::ecc_p_x, p.x() },
86 { C::ecc_p_y, p.y() },
87 { C::ecc_p_is_inf, p.is_infinity() },
88 // Point Q
89 { C::ecc_q_x, q.x() },
90 { C::ecc_q_y, q.y() },
91 { C::ecc_q_is_inf, q.is_infinity() },
92 // Resulting point
93 { C::ecc_r_x, result.x() },
94 { C::ecc_r_y, result.y() },
95 { C::ecc_r_is_inf, result.is_infinity() },
96
97 // Temporary result boolean to decrease relation degree
98 { C::ecc_use_computed_result, use_computed_result },
99
100 // Check coordinates to detect edge cases (double, add and infinity)
101 { C::ecc_x_match, x_match },
102 { C::ecc_inv_x_diff, x_match ? FF::zero() : (q.x() - p.x()).invert() },
103 { C::ecc_y_match, y_match },
104 { C::ecc_inv_y_diff, y_match ? FF::zero() : (q.y() - p.y()).invert() },
105
106 // Witness for doubling operation
107 { C::ecc_double_op, double_predicate },
108 { C::ecc_inv_2_p_y, !result_is_infinity && double_predicate ? (p.y() * 2).invert() : FF::zero() },
109
110 // Witness for add operation
111 { C::ecc_add_op, add_predicate },
112 // This is a witness for the result(r) being the point at infinity
113 // It is used to constrain that ecc_r_is_inf is correctly set.
114 { C::ecc_result_infinity, result_is_infinity },
115
116 { C::ecc_lambda, lambda },
117 } });
118
119 row++;
120 }
121}
122
125{
126 using C = Column;
127
128 uint32_t row = 1; // We start from row 1 because this trace contains shifted columns.
129 for (const auto& event : events) {
130 size_t num_intermediate_states = event.intermediate_states.size();
131 EmbeddedCurvePoint point = event.point;
132
133 for (size_t i = 0; i < num_intermediate_states; ++i) {
134 // This trace uses reverse aggregation, so we need to process the bits in reverse
135 size_t intermediate_state_idx = num_intermediate_states - i - 1;
136
137 // The first bit processed is the end of the trace for the event
138 bool is_start = i == 0;
139
140 bool is_end = intermediate_state_idx == 0;
141
142 simulation::ScalarMulIntermediateState state = event.intermediate_states[intermediate_state_idx];
143 if (is_start) {
144 assert(state.res == event.result);
145 }
146 EmbeddedCurvePoint res = state.res;
147
148 EmbeddedCurvePoint temp = state.temp;
149 bool bit = state.bit;
150
151 trace.set(row,
152 { { { C::scalar_mul_sel, 1 },
153 { C::scalar_mul_scalar, event.scalar },
154 { C::scalar_mul_point_x, point.x() },
155 { C::scalar_mul_point_y, point.y() },
156 { C::scalar_mul_point_inf, point.is_infinity() },
157 { C::scalar_mul_res_x, res.x() },
158 { C::scalar_mul_res_y, res.y() },
159 { C::scalar_mul_res_inf, res.is_infinity() },
160 { C::scalar_mul_start, is_start },
161 { C::scalar_mul_end, is_end },
162 { C::scalar_mul_not_end, !is_end },
163 { C::scalar_mul_bit, bit },
164 { C::scalar_mul_bit_idx, intermediate_state_idx },
165 { C::scalar_mul_temp_x, temp.x() },
166 { C::scalar_mul_temp_y, temp.y() },
167 { C::scalar_mul_temp_inf, temp.is_infinity() },
168 {
169 C::scalar_mul_should_add,
170 (!is_end) && bit,
171 },
172 { C::scalar_mul_bit_radix, 2 } } });
173
174 row++;
175 }
176 }
177}
178
181{
182 using C = Column;
183
184 uint32_t row = 0;
185 for (const auto& event : events) {
186 uint64_t dst_addr = static_cast<uint64_t>(event.dst_address);
187
188 // Error handling, check if the destination address is out of range.
189 // The max write address is dst_addr + 2, since we write 3 values (x, y, is_inf).
190 bool dst_out_of_range_err = dst_addr + 2 > AVM_HIGHEST_MEM_ADDRESS;
191
192 // Error handling, check if the points are on the curve.
193 bool p_is_on_curve = event.p.on_curve();
194 FF p_is_on_curve_eqn = compute_curve_eqn_diff(event.p);
195 FF p_is_on_curve_eqn_inv = p_is_on_curve ? FF::zero() : p_is_on_curve_eqn.invert();
196
197 bool q_is_on_curve = event.q.on_curve();
198 FF q_is_on_curve_eqn = compute_curve_eqn_diff(event.q);
199 FF q_is_on_curve_eqn_inv = q_is_on_curve ? FF::zero() : q_is_on_curve_eqn.invert();
200
201 bool error = dst_out_of_range_err || !p_is_on_curve || !q_is_on_curve;
202
203 trace.set(row,
204 { {
205 { C::ecc_add_mem_sel, 1 },
206 { C::ecc_add_mem_execution_clk, event.execution_clk },
207 { C::ecc_add_mem_space_id, event.space_id },
208 // Error handling - dst out of range
209 { C::ecc_add_mem_max_mem_addr, AVM_HIGHEST_MEM_ADDRESS },
210 { C::ecc_add_mem_sel_dst_out_of_range_err, dst_out_of_range_err ? 1 : 0 },
211 // Error handling - p is not on curve
212 { C::ecc_add_mem_sel_p_not_on_curve_err, !p_is_on_curve ? 1 : 0 },
213 { C::ecc_add_mem_p_is_on_curve_eqn, p_is_on_curve_eqn },
214 { C::ecc_add_mem_p_is_on_curve_eqn_inv, p_is_on_curve_eqn_inv },
215 // Error handling - q is not on curve
216 { C::ecc_add_mem_sel_q_not_on_curve_err, !q_is_on_curve ? 1 : 0 },
217 { C::ecc_add_mem_q_is_on_curve_eqn, q_is_on_curve_eqn },
218 { C::ecc_add_mem_q_is_on_curve_eqn_inv, q_is_on_curve_eqn_inv },
219 // Consolidated error
220 { C::ecc_add_mem_err, error ? 1 : 0 },
221 // Memory Writes
222 { C::ecc_add_mem_dst_addr_0_, dst_addr },
223 { C::ecc_add_mem_dst_addr_1_, dst_addr + 1 },
224 { C::ecc_add_mem_dst_addr_2_, dst_addr + 2 },
225 // Input - Point P
226 { C::ecc_add_mem_p_x, event.p.x() },
227 { C::ecc_add_mem_p_y, event.p.y() },
228 { C::ecc_add_mem_p_is_inf, event.p.is_infinity() ? 1 : 0 },
229 // Input - Point Q
230 { C::ecc_add_mem_q_x, event.q.x() },
231 { C::ecc_add_mem_q_y, event.q.y() },
232 { C::ecc_add_mem_q_is_inf, event.q.is_infinity() ? 1 : 0 },
233 // Output
234 { C::ecc_add_mem_sel_should_exec, error ? 0 : 1 },
235 { C::ecc_add_mem_res_x, event.result.x() },
236 { C::ecc_add_mem_res_y, event.result.y() },
237 { C::ecc_add_mem_res_is_inf, event.result.is_infinity() },
238 } });
239
240 row++;
241 }
242}
243
247 .add<lookup_scalar_mul_add_settings, InteractionType::LookupGeneric>()
249 // Memory Aware Interactions
250 // Comparison
251 .add<lookup_ecc_mem_check_dst_addr_in_range_settings, InteractionType::LookupGeneric>()
252 // Lookup into ECC Add Subtrace
254 // These should be permutations (Write to Mem)
255 .add<lookup_ecc_mem_write_mem_0_settings, InteractionType::LookupGeneric>()
257 .add<lookup_ecc_mem_write_mem_2_settings, InteractionType::LookupGeneric>()
258 // Dispatch Permutation
260
261} // 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:51
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
permutation_settings< perm_ecc_mem_dispatch_exec_ecc_add_settings_ > perm_ecc_mem_dispatch_exec_ecc_add_settings
lookup_settings< lookup_ecc_mem_write_mem_1_settings_ > lookup_ecc_mem_write_mem_1_settings
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()