Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
acir_format.test.cpp
Go to the documentation of this file.
1#include <gtest/gtest.h>
2#include <memory>
3#include <vector>
4
5#include "acir_format.hpp"
10
12
13using namespace bb;
14using namespace bb::crypto;
15using namespace acir_format;
16
17template <typename Builder> class AcirFormatTests : public ::testing::Test {
18 protected:
20};
21
22using BuilderTypes = testing::Types<UltraCircuitBuilder, MegaCircuitBuilder>;
24
25TYPED_TEST(AcirFormatTests, ExpressionWithOnlyConstantTermFails)
26{
27 // Test that circuit construction fails if we have an expression with only a constant term. This is expected
28 // behavior: an expression with only a constant term represent either:
29 // 1) an unsatisfied constraint if the constant term is non-zero
30 // 2) a zero constraint if the constant term is zero
31 // In both cases, we should not construct a circuit as either the circuit is not satisfiable, or there is zero gate.
33 Acir::Circuit circuit{
35 .public_parameters = {},
36 .return_values = {},
37 };
38
39 EXPECT_THROW_WITH_MESSAGE(circuit_serde_to_acir_format(circuit, /*is_mega=*/false), "circuit is unsatisfiable");
40}
41
42TYPED_TEST(AcirFormatTests, ExpressionWithCancellingCoefficientsFails)
43{
44 // Test that circuit construction fails if we have an expression where all linear terms cancel out. This is expected
45 // behavior as such an expression would result in a zero gate.
47 { bb::fr(-1).to_buffer(), Acir::Witness{ 0 } } },
48 .q_c = bb::fr::zero().to_buffer() };
49 Acir::Circuit circuit{
51 .public_parameters = {},
52 .return_values = {},
53 };
54
56 "acir_format::assert_zero_to_constraints: produced a SingleArithmetic zero gate.");
57}
58
60{
61 // Test that public inputs are handled correctly.
62 WitnessVector witnesses = { 2, 4, 6, 8, 10, 12 };
63
64 // 8 - 6 - 2 = 0
66 { bb::fr(-1).to_buffer(), Acir::Witness{ 2 } } },
67 .q_c = bb::fr(-2).to_buffer() };
68
69 Acir::Circuit circuit{
71 .public_parameters =
73 .return_values = Acir::PublicInputs{ .value = { Acir::Witness{ .value = 4 }, Acir::Witness{ .value = 5 } } },
74 };
75
76 AcirFormat acir_format = circuit_serde_to_acir_format(circuit, /*is_mega=*/false);
77 BB_ASSERT_EQ(acir_format.public_inputs, std::vector<uint32_t>({ 0, 1, 4, 5 }));
78
79 AcirProgram program{ acir_format, witnesses };
80 auto builder = create_circuit<TypeParam>(program, {});
81
82 for (size_t idx = 0; idx < acir_format.public_inputs.size(); ++idx) {
83 uint32_t pub_input_idx = acir_format.public_inputs[idx];
84 EXPECT_EQ(pub_input_idx, builder.public_inputs()[idx]);
85 EXPECT_EQ(witnesses[pub_input_idx], builder.get_variable(pub_input_idx));
86 }
87}
88
89// A circuit with one shared-wire two-product (bilinear) AssertZero and two single-witness equality
90// AssertZeros is lowered differently per flavor. Ultra (is_mega = false) has no bilinear_batched_eq gate: the
91// two-product opcode becomes a big_quad (two gates) and each equality a single quad. Mega
92// (is_mega = true) classifies the two-product opcode into one BilinearConstraint and pairs the two
93// equalities into one BatchedEqCheckConstraint. This checks both the counts and the resulting constraint structure.
94TEST(AcirFormatBilinearBatchedEqTest, UltraMegaArithmetizationDifference)
95{
96 constexpr uint32_t w1 = 1;
97 constexpr uint32_t w2 = 2;
98 constexpr uint32_t w3 = 3;
99 constexpr uint32_t w4 = 4;
100 constexpr uint32_t w5 = 5;
101
102 // Bilinear opcode: q_m·w1·w2 + q_5·w1·w3 + q_l·w1 + q_c = 0 (the two products share wire w1).
103 const bb::fr q_m = bb::fr(2);
104 const bb::fr q_5 = bb::fr(3);
105 const bb::fr q_l = bb::fr(5);
106 const bb::fr q_c = bb::fr(7);
107 Acir::Expression bilinear_expr;
108 bilinear_expr.mul_terms.push_back(std::make_tuple(q_m.to_buffer(), Acir::Witness{ w1 }, Acir::Witness{ w2 }));
109 bilinear_expr.mul_terms.push_back(std::make_tuple(q_5.to_buffer(), Acir::Witness{ w1 }, Acir::Witness{ w3 }));
110 bilinear_expr.linear_combinations.push_back(std::make_tuple(q_l.to_buffer(), Acir::Witness{ w1 }));
111 bilinear_expr.q_c = q_c.to_buffer();
112
113 // Two single-witness equalities: e0·w4 + k0 = 0 and e1·w5 + k1 = 0.
114 const bb::fr e0 = bb::fr(11);
115 const bb::fr k0 = bb::fr(13);
116 const bb::fr e1 = bb::fr(17);
117 const bb::fr k1 = bb::fr(19);
118 Acir::Expression eq1_expr;
119 eq1_expr.linear_combinations.push_back(std::make_tuple(e0.to_buffer(), Acir::Witness{ w4 }));
120 eq1_expr.q_c = k0.to_buffer();
121 Acir::Expression eq2_expr;
122 eq2_expr.linear_combinations.push_back(std::make_tuple(e1.to_buffer(), Acir::Witness{ w5 }));
123 eq2_expr.q_c = k1.to_buffer();
124
125 Acir::Circuit circuit{
126 .opcodes = { Acir::Opcode{ Acir::Opcode::AssertZero{ .value = bilinear_expr } },
128 Acir::Opcode{ Acir::Opcode::AssertZero{ .value = eq2_expr } } },
129 .public_parameters = {},
130 .return_values = {},
131 };
132
133 // --- Ultra: standard arithmetic gates only ---
134 {
135 AcirFormat af = circuit_serde_to_acir_format(circuit, /*is_mega=*/false);
136
137 EXPECT_TRUE(af.bilinear_constraints.empty());
138 EXPECT_TRUE(af.batched_eq_check_constraints.empty());
139 ASSERT_EQ(af.big_quad_constraints.size(), 1U);
140 ASSERT_EQ(af.quad_constraints.size(), 2U);
141
142 // The two-product opcode splits into a big_quad of two gates: one per product, with the linear
143 // term and the constant landing on the first gate. Both products carry w1 on wire a.
144 const BigQuadConstraint& big = af.big_quad_constraints[0];
145 ASSERT_EQ(big.size(), 2U);
146 EXPECT_EQ(big[0].a, w1);
147 EXPECT_EQ(big[0].b, w2);
148 EXPECT_EQ(big[0].mul_scaling, q_m);
149 EXPECT_EQ(big[0].a_scaling, q_l);
150 EXPECT_EQ(big[0].const_scaling, q_c);
151 EXPECT_EQ(big[1].a, w1);
152 EXPECT_EQ(big[1].b, w3);
153 EXPECT_EQ(big[1].mul_scaling, q_5);
154
155 // Each equality is a single width-4 gate: coefficient on a_scaling, constant on const_scaling.
156 EXPECT_EQ(af.quad_constraints[0].a, w4);
157 EXPECT_EQ(af.quad_constraints[0].a_scaling, e0);
158 EXPECT_EQ(af.quad_constraints[0].const_scaling, k0);
159 EXPECT_EQ(af.quad_constraints[1].a, w5);
160 EXPECT_EQ(af.quad_constraints[1].a_scaling, e1);
161 EXPECT_EQ(af.quad_constraints[1].const_scaling, k1);
162 }
163
164 // --- Mega: one bilinear row + one batched row ---
165 {
166 AcirFormat af = circuit_serde_to_acir_format(circuit, /*is_mega=*/true);
167
168 EXPECT_TRUE(af.quad_constraints.empty());
169 EXPECT_TRUE(af.big_quad_constraints.empty());
170 ASSERT_EQ(af.bilinear_constraints.size(), 1U);
171 ASSERT_EQ(af.batched_eq_check_constraints.size(), 1U);
172
173 // Bilinear row: products on (a, b) = (w1, w2) via q_m and (a, c) = (w1, w3) via q_5, sharing
174 // wire a = w1; the linear term on w1 lands on q_l; q_c carries the constant; the other linear
175 // selectors are zero. With no linear-only fourth wire, d is the IS_CONSTANT sentinel.
176 const BilinearConstraint& bilinear = af.bilinear_constraints[0];
177 EXPECT_EQ(bilinear.a, w1);
178 EXPECT_EQ(bilinear.b, w2);
179 EXPECT_EQ(bilinear.c, w3);
180 EXPECT_EQ(bilinear.d, bb::stdlib::IS_CONSTANT);
181 EXPECT_EQ(bilinear.q_m, q_m);
182 EXPECT_EQ(bilinear.q_5, q_5);
183 EXPECT_EQ(bilinear.q_l, q_l);
184 EXPECT_EQ(bilinear.q_r, bb::fr::zero());
185 EXPECT_EQ(bilinear.q_o, bb::fr::zero());
186 EXPECT_EQ(bilinear.q_4, bb::fr::zero());
187 EXPECT_EQ(bilinear.q_c, q_c);
188
189 // BatchedEq row: the two equalities are paired — half 1 = q_l·w4 + q_c (the e0/k0 equality), half 2 =
190 // q_o·w5 + q_m (the e1/k1 equality). The unused second witness of each half is IS_CONSTANT.
192 EXPECT_EQ(batched_eq.a, w4);
193 EXPECT_EQ(batched_eq.b, bb::stdlib::IS_CONSTANT);
194 EXPECT_EQ(batched_eq.c, w5);
195 EXPECT_EQ(batched_eq.d, bb::stdlib::IS_CONSTANT);
196 EXPECT_EQ(batched_eq.q_l, e0);
197 EXPECT_EQ(batched_eq.q_r, bb::fr::zero());
198 EXPECT_EQ(batched_eq.q_o, e1);
199 EXPECT_EQ(batched_eq.q_4, bb::fr::zero());
200 EXPECT_EQ(batched_eq.q_c, k0);
201 EXPECT_EQ(batched_eq.q_m, k1);
202 }
203}
204
205// Ultra circuit should throw if it encounters Bilinear or BatchedEqCheck gates
206TEST(AcirFormatBilinearBatchedEqTest, UltraThrowsOnBilinearAndBatchedEqCheckGates)
207{
208 constexpr uint32_t w1 = 1;
209 constexpr uint32_t w2 = 2;
210 constexpr uint32_t w3 = 3;
211 constexpr uint32_t w4 = 4;
212 constexpr uint32_t w5 = 5;
213
214 {
215 // Bilinear opcode: q_m·w1·w2 + q_5·w1·w3 + q_l·w1 + q_c = 0 (the two products share wire w1).
216 const bb::fr q_m = bb::fr(2);
217 const bb::fr q_5 = bb::fr(3);
218 const bb::fr q_l = bb::fr(5);
219 const bb::fr q_c = bb::fr(7);
220 Acir::Expression bilinear_expr;
221 bilinear_expr.mul_terms.push_back(std::make_tuple(q_m.to_buffer(), Acir::Witness{ w1 }, Acir::Witness{ w2 }));
222 bilinear_expr.mul_terms.push_back(std::make_tuple(q_5.to_buffer(), Acir::Witness{ w1 }, Acir::Witness{ w3 }));
223 bilinear_expr.linear_combinations.push_back(std::make_tuple(q_l.to_buffer(), Acir::Witness{ w1 }));
224 bilinear_expr.q_c = q_c.to_buffer();
225
226 Acir::Circuit circuit{
227 .opcodes = { Acir::Opcode{ Acir::Opcode::AssertZero{ .value = bilinear_expr } } },
228 .public_parameters = {},
229 .return_values = {},
230 };
231
232 AcirFormat af = circuit_serde_to_acir_format(circuit, /*is_mega=*/true);
233
234 EXPECT_TRUE(af.quad_constraints.empty());
235 EXPECT_TRUE(af.big_quad_constraints.empty());
236 ASSERT_EQ(af.bilinear_constraints.size(), 1U);
237 ASSERT_EQ(af.batched_eq_check_constraints.size(), 0U);
238
241 "Bilinear constraints should only be present when using MegaCircuitBuilder.");
242 }
243
244 {
245 // Two single-witness equalities: e0·w4 + k0 = 0 and e1·w5 + k1 = 0.
246 const bb::fr e0 = bb::fr(11);
247 const bb::fr k0 = bb::fr(13);
248 const bb::fr e1 = bb::fr(17);
249 const bb::fr k1 = bb::fr(19);
250 Acir::Expression eq1_expr;
251 eq1_expr.linear_combinations.push_back(std::make_tuple(e0.to_buffer(), Acir::Witness{ w4 }));
252 eq1_expr.q_c = k0.to_buffer();
253 Acir::Expression eq2_expr;
254 eq2_expr.linear_combinations.push_back(std::make_tuple(e1.to_buffer(), Acir::Witness{ w5 }));
255 eq2_expr.q_c = k1.to_buffer();
256
257 Acir::Circuit circuit{
258 .opcodes = { Acir::Opcode{ Acir::Opcode::AssertZero{ .value = eq1_expr } },
259 Acir::Opcode{ Acir::Opcode::AssertZero{ .value = eq2_expr } } },
260 .public_parameters = {},
261 .return_values = {},
262 };
263
264 AcirFormat af = circuit_serde_to_acir_format(circuit, /*is_mega=*/true);
265
266 EXPECT_TRUE(af.quad_constraints.empty());
267 EXPECT_TRUE(af.big_quad_constraints.empty());
268 ASSERT_EQ(af.bilinear_constraints.size(), 0U);
269 ASSERT_EQ(af.batched_eq_check_constraints.size(), 1U);
270
273 "BatchedEq constraints should only be present when using MegaCircuitBuilder.");
274 }
275}
#define BB_ASSERT_EQ(actual, expected,...)
Definition assert.hpp:83
#define EXPECT_THROW_WITH_MESSAGE(code, expectedMessageRegex)
Definition assert.hpp:224
static void SetUpTestSuite()
Constraint representing a polynomial of degree 1 or 2 that does not fit into a standard UltraHonk ari...
AluTraceBuilder builder
Definition alu.test.cpp:124
FF a
FF b
AcirFormat circuit_serde_to_acir_format(Acir::Circuit const &circuit, bool is_mega)
Convert an Acir::Circuit into an AcirFormat by processing all the opcodes.
std::vector< bb::fr > WitnessVector
void build_constraints(Builder &builder, AcirFormat &constraints, const ProgramMetadata &metadata)
Add to the builder the constraints contained in an AcirFormat instance.
std::filesystem::path bb_crs_path()
void init_file_crs_factory(const std::filesystem::path &path)
Entry point for Barretenberg command-line interface.
Definition api.hpp:5
TYPED_TEST_SUITE(CommitmentKeyTest, Curves)
field< Bn254FrParams > fr
Definition fr.hpp:155
TYPED_TEST(CommitmentKeyTest, CommitToZeroPoly)
TEST(BoomerangMegaCircuitBuilder, BasicCircuit)
::testing::Types< UltraCircuitBuilder, MegaCircuitBuilder > BuilderTypes
std::vector< Acir::Opcode > opcodes
Definition acir.hpp:7232
std::vector< std::tuple< std::vector< uint8_t >, Acir::Witness > > linear_combinations
Definition acir.hpp:5856
std::vector< uint8_t > q_c
Definition acir.hpp:5857
std::vector< std::tuple< std::vector< uint8_t >, Acir::Witness, Acir::Witness > > mul_terms
Definition acir.hpp:5855
Acir::Expression value
Definition acir.hpp:6330
std::vector< Acir::Witness > value
Definition acir.hpp:7213
uint32_t value
Definition acir.hpp:4233
Barretenberg's representation of ACIR constraints.
std::vector< QuadConstraint > quad_constraints
std::vector< BatchedEqCheckConstraint > batched_eq_check_constraints
std::vector< BilinearConstraint > bilinear_constraints
std::vector< BigQuadConstraint > big_quad_constraints
Struct containing both the constraints to be added to the circuit and the witness vector.
BatchedEq constraint — BATCHED_EQ mode of the bilinear_batched_eq gate (see bilinear_or_batched_eq_ch...
Bilinear constraint — BILINEAR mode of the bilinear_batched_eq gate (see bilinear_or_batched_eq_check...
static constexpr field one()
BB_INLINE std::vector< uint8_t > to_buffer() const
static constexpr field zero()