Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
mega_circuit_builder.test.cpp
Go to the documentation of this file.
6#include <gtest/gtest.h>
7
8using namespace bb;
9
10namespace {
12}
13namespace bb {
14
15TEST(MegaCircuitBuilder, CopyConstructor)
16{
18 fr a = fr::one();
19 builder.add_public_variable(a);
20
21 for (size_t i = 0; i < 16; ++i) {
22 for (size_t j = 0; j < 16; ++j) {
23 uint64_t left = static_cast<uint64_t>(j);
24 uint64_t right = static_cast<uint64_t>(i);
25 uint32_t left_idx = builder.add_variable(fr(left));
26 uint32_t right_idx = builder.add_variable(fr(right));
27 uint32_t result_idx = builder.add_variable(fr(left ^ right));
28
29 uint32_t add_idx = builder.add_variable(fr(left) + fr(right) + builder.get_variable(result_idx));
30 builder.create_big_add_gate(
31 { left_idx, right_idx, result_idx, add_idx, fr(1), fr(1), fr(1), fr(-1), fr(0) });
32 }
33 }
34
35 // Compute a simple point accumulation natively
36 auto P1 = g1::affine_element::random_element();
37 auto P2 = g1::affine_element::random_element();
38 auto z = fr::random_element();
39
40 // Add gates corresponding to the above operations
41 builder.queue_ecc_add_accum(P1);
42 builder.queue_ecc_mul_accum(P2, z);
43 builder.queue_ecc_eq();
44
45 bool result = CircuitChecker::check(builder);
46 EXPECT_EQ(result, true);
47
48 MegaCircuitBuilder duplicate_builder{ builder };
49
50 EXPECT_EQ(duplicate_builder, builder);
51 EXPECT_TRUE(CircuitChecker::check(duplicate_builder));
52}
53
55{
57 fr a = fr::one();
58 builder.add_public_variable(a);
59 bool result = CircuitChecker::check(builder);
60 EXPECT_EQ(result, true);
61}
62
71{
72 const size_t CHUNK_SIZE = stdlib::NUM_LIMB_BITS_IN_FIELD_SIMULATION * 2;
73
75
76 // Compute a simple point accumulation natively
77 auto P1 = g1::affine_element::random_element();
78 auto P2 = g1::affine_element::random_element();
79 auto z = fr::random_element();
80 auto P_expected = P1 + P2 * z;
81
82 // Add gates corresponding to the above operations
83 builder.queue_ecc_add_accum(P1);
84 builder.queue_ecc_mul_accum(P2, z);
85
86 // Add equality op gates based on the internal accumulator
87 auto eq_op_tuple = builder.queue_ecc_eq();
88
89 // Check that we can reconstruct the coordinates of P_expected from the data in variables
90 auto P_result_x_lo = uint256_t(builder.get_variable(eq_op_tuple.x_lo));
91 auto P_result_x_hi = uint256_t(builder.get_variable(eq_op_tuple.x_hi));
92 auto P_result_x = P_result_x_lo + (P_result_x_hi << CHUNK_SIZE);
93 auto P_result_y_lo = uint256_t(builder.get_variable(eq_op_tuple.y_lo));
94 auto P_result_y_hi = uint256_t(builder.get_variable(eq_op_tuple.y_hi));
95 auto P_result_y = P_result_y_lo + (P_result_y_hi << CHUNK_SIZE);
96 EXPECT_EQ(P_result_x, uint256_t(P_expected.x));
97 EXPECT_EQ(P_result_y, uint256_t(P_expected.y));
98
99 // Check that the accumulator in the op queue has been reset to 0
100 auto accumulator = builder.op_queue->get_accumulator();
101 EXPECT_EQ(accumulator, g1::affine_point_at_infinity);
102
103 // Check number of ecc op "gates"/rows = 3 ops * 2 rows per op = 6
104 EXPECT_EQ(builder.blocks.ecc_op.size(), 6);
105
106 // Check that the expected op codes have been correctly recorded in the 1st op wires pointed by circuit indices
107 auto opcode_wire_indexes = builder.blocks.ecc_op.w_l();
108 EXPECT_EQ(builder.get_variable(opcode_wire_indexes[0]), (EccOpCode{ .add = true }).value());
109 EXPECT_EQ(builder.get_variable(opcode_wire_indexes[2]), (EccOpCode{ .mul = true }).value());
110 EXPECT_EQ(builder.get_variable(opcode_wire_indexes[4]), (EccOpCode{ .eq = true, .reset = true }).value());
111
112 // Check that we can reconstruct the coordinates of P1 from the op_wires
113 auto P1_x_lo = uint256_t(builder.get_variable(builder.blocks.ecc_op.w_r()[0]));
114 auto P1_x_hi = uint256_t(builder.get_variable(builder.blocks.ecc_op.w_o()[0]));
115 auto P1_x = P1_x_lo + (P1_x_hi << CHUNK_SIZE);
116 EXPECT_EQ(P1_x, uint256_t(P1.x));
117 auto P1_y_lo = uint256_t(builder.get_variable(builder.blocks.ecc_op.w_4()[0]));
118 auto P1_y_hi = uint256_t(builder.get_variable(builder.blocks.ecc_op.w_r()[1]));
119 auto P1_y = P1_y_lo + (P1_y_hi << CHUNK_SIZE);
120 EXPECT_EQ(P1_y, uint256_t(P1.y));
121
122 // Check that we can reconstruct the coordinates of P2 from the op_wires
123 auto P2_x_lo = uint256_t(builder.get_variable(builder.blocks.ecc_op.w_r()[2]));
124 auto P2_x_hi = uint256_t(builder.get_variable(builder.blocks.ecc_op.w_o()[2]));
125 auto P2_x = P2_x_lo + (P2_x_hi << CHUNK_SIZE);
126 EXPECT_EQ(P2_x, uint256_t(P2.x));
127 auto P2_y_lo = uint256_t(builder.get_variable(builder.blocks.ecc_op.w_4()[2]));
128 auto P2_y_hi = uint256_t(builder.get_variable(builder.blocks.ecc_op.w_r()[3]));
129 auto P2_y = P2_y_lo + (P2_y_hi << CHUNK_SIZE);
130 EXPECT_EQ(P2_y, uint256_t(P2.y));
131}
132
137TEST(MegaCircuitBuilder, GoblinEccOpQueueUltraOps)
138{
139 // Construct a simple circuit with op gates
141
142 // Compute a simple point accumulation natively
143 auto P1 = g1::affine_element::random_element();
144 auto P2 = g1::affine_element::random_element();
145 auto z = fr::random_element();
146
147 // Add gates corresponding to the above operations
148 builder.queue_ecc_add_accum(P1);
149 builder.queue_ecc_mul_accum(P2, z);
150 builder.queue_ecc_eq();
151
152 // Merge the ops of the incoming circuit
153 builder.op_queue->merge();
154
155 // Check that the ultra ops recorded in the EccOpQueue match the ops recorded in the wires
156 auto ultra_ops = builder.op_queue->construct_current_ultra_ops_subtable_columns();
157 for (size_t i = 1; i < 4; ++i) {
158 for (size_t j = 0; j < builder.blocks.ecc_op.size(); ++j) {
159 auto op_wire_val = builder.get_variable(builder.blocks.ecc_op.wires[i][j]);
160 auto ultra_op_val = ultra_ops[i][j];
161 ASSERT_EQ(op_wire_val, ultra_op_val);
162 }
163 }
164}
165
169TEST(MegaCircuitBuilder, EccOpBlockIsFirstInTrace)
170{
172
173 // Add ECC ops
174 auto P1 = g1::affine_element::random_element();
175 builder.queue_ecc_add_accum(P1);
176 builder.queue_ecc_eq();
177
178 // Add some arithmetic gates (goes to a different block)
179 auto a = builder.add_variable(fr::random_element());
180 auto b = builder.add_variable(fr::random_element());
181 auto c = builder.add_variable(builder.get_variable(a) + builder.get_variable(b));
182 builder.create_add_gate({ a, b, c, 1, 1, -1, 0 });
183
184 builder.finalize_circuit();
185 builder.blocks.compute_offsets(MegaFlavor::TRACE_OFFSET);
186
187 // Verify ecc_op block starts at offset TRACE_OFFSET + NUM_ZERO_ROWS = NUM_ZERO_ROWS for non-ZK Mega.
188 EXPECT_EQ(builder.blocks.ecc_op.trace_offset(), MegaFlavor::TRACE_OFFSET + NUM_ZERO_ROWS);
189
190 // Verify no other non-empty block starts before ecc_op ends
191 size_t ecc_op_end = builder.blocks.ecc_op.trace_end();
192 for (auto& block : builder.blocks.get()) {
193 if (&block != &builder.blocks.ecc_op && block.size() > 0) {
194 EXPECT_GE(block.trace_offset(), ecc_op_end) << "Block starts before ecc_op ends";
195 }
196 }
197
198 EXPECT_TRUE(CircuitChecker::check(builder));
199}
200
204TEST(MegaCircuitBuilder, EmptyCircuitFinalization)
205{
207
208 // Completely empty circuit - no gates added
209 EXPECT_EQ(builder.blocks.ecc_op.size(), 0);
210 EXPECT_FALSE(builder.circuit_finalized);
211
212 builder.finalize_circuit();
213
214 EXPECT_TRUE(builder.circuit_finalized);
215
216 // After finalization: only zero_idx arithmetic gates and the corresponding public inputs remain.
217 // No dummy ecc ops, databus entries, or other gate types are added.
218 EXPECT_EQ(builder.blocks.arithmetic.size(), 4); // zero_idx setup
219 EXPECT_EQ(builder.blocks.pub_inputs.size(), 0);
220 EXPECT_EQ(builder.blocks.ecc_op.size(), 0);
221 EXPECT_EQ(builder.blocks.busread.size(), 0);
222 EXPECT_EQ(builder.blocks.lookup.size(), 0);
223 EXPECT_EQ(builder.blocks.delta_range.size(), 0);
224 EXPECT_EQ(builder.blocks.elliptic.size(), 0);
225 EXPECT_EQ(builder.blocks.memory.size(), 0);
226 EXPECT_EQ(builder.blocks.nnf.size(), 0);
227 EXPECT_EQ(builder.blocks.poseidon2_external.size(), 0);
228 EXPECT_EQ(builder.get_calldata(BusId::KERNEL_CALLDATA).size(), 0);
229 EXPECT_EQ(builder.get_calldata(BusId::APP_CALLDATA).size(), 0);
230 EXPECT_EQ(builder.get_return_data().size(), 0);
231
232 EXPECT_TRUE(CircuitChecker::check(builder));
233}
234
238TEST(MegaCircuitBuilder, DatabusOutOfBoundsReadFails)
239{
241
242 // Add single entry to calldata
243 auto val = builder.add_variable(fr(42));
244 builder.add_public_calldata(BusId::KERNEL_CALLDATA, val);
245
246 // Try to read at index 1 (out of bounds - only index 0 exists)
247 auto bad_idx = builder.add_variable(fr(1));
248
249 // This should trigger an assertion in read_calldata
250 EXPECT_THROW(builder.read_calldata(BusId::KERNEL_CALLDATA, bad_idx), std::runtime_error);
251}
252
253} // namespace bb
static constexpr size_t TRACE_OFFSET
static bool check(const Builder &circuit)
Check the witness satisifies the circuit.
static constexpr affine_element affine_point_at_infinity
Definition group.hpp:51
AluTraceBuilder builder
Definition alu.test.cpp:124
FF a
FF b
numeric::RNG & engine
RNG & get_debug_randomness(bool reset, std::uint_fast64_t seed)
Definition engine.cpp:245
Entry point for Barretenberg command-line interface.
Definition api.hpp:5
field< Bn254FrParams > fr
Definition fr.hpp:155
TEST(BoomerangMegaCircuitBuilder, BasicCircuit)
MegaCircuitBuilder_< field< Bn254FrParams > > MegaCircuitBuilder
Defines the opcodes for ECC operations used in both the Ultra and ECCVM formats. There are three opco...
static constexpr field one()
static field random_element(numeric::RNG *engine=nullptr) noexcept