Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
honk_recursion_constraint.test.cpp
Go to the documentation of this file.
2#include "acir_format.hpp"
9#include "proof_surgeon.hpp"
10
11#include <gtest/gtest.h>
12#include <vector>
13
14using namespace acir_format;
15using namespace bb;
16
17template <typename RecursiveFlavor> class AcirHonkRecursionConstraint : public ::testing::Test {
18
19 public:
20 using InnerFlavor = typename RecursiveFlavor::NativeFlavor;
21 using InnerBuilder = typename InnerFlavor::CircuitBuilder;
24 using InnerVerificationKey = typename InnerFlavor::VerificationKey;
26 using OuterBuilder = typename RecursiveFlavor::CircuitBuilder;
33 using OuterVerificationKey = typename OuterFlavor::VerificationKey;
35
37 {
46 RangeConstraint range_a{
47 .witness = 0,
48 .num_bits = 32,
49 };
50 RangeConstraint range_b{
51 .witness = 1,
52 .num_bits = 32,
53 };
54
55 LogicConstraint logic_constraint{
58 .result = 2,
59 .num_bits = 32,
60 .is_xor_gate = 1,
61 };
62 poly_triple expr_a{
63 .a = 2,
64 .b = 3,
65 .c = 0,
66 .q_m = 0,
67 .q_l = 1,
68 .q_r = -1,
69 .q_o = 0,
70 .q_c = -10,
71 };
72 poly_triple expr_b{
73 .a = 3,
74 .b = 4,
75 .c = 5,
76 .q_m = 1,
77 .q_l = 0,
78 .q_r = 0,
79 .q_o = -1,
80 .q_c = 0,
81 };
82 poly_triple expr_c{
83 .a = 3,
84 .b = 5,
85 .c = 3,
86 .q_m = 1,
87 .q_l = 0,
88 .q_r = 0,
89 .q_o = -1,
90 .q_c = 0,
91
92 };
93 poly_triple expr_d{
94 .a = 5,
95 .b = 0,
96 .c = 0,
97 .q_m = 0,
98 .q_l = -1,
99 .q_r = 0,
100 .q_o = 0,
101 .q_c = 1,
102 };
103
104 AcirFormat constraint_system{
105 .varnum = 6,
106 .num_acir_opcodes = 7,
107 .public_inputs = { 1, 2 },
108 .logic_constraints = { logic_constraint },
109 .range_constraints = { range_a, range_b },
110 .poly_triple_constraints = { expr_a, expr_b, expr_c, expr_d },
111 .original_opcode_indices = create_empty_original_opcode_indices(),
112 };
113 mock_opcode_indices(constraint_system);
114
115 uint256_t inverse_of_five = fr(5).invert();
116 WitnessVector witness{
117 5, 10, 15, 5, inverse_of_five, 1,
118 };
119 uint32_t honk_recursion = 0;
120 if constexpr (IsAnyOf<InnerFlavor, UltraFlavor, UltraZKFlavor>) {
121 honk_recursion = 1;
122 } else if constexpr (IsAnyOf<InnerFlavor, UltraRollupFlavor>) {
123 honk_recursion = 2;
124 }
125 ProgramMetadata metadata{ .recursive = true, .honk_recursion = honk_recursion };
126 AcirProgram program{ constraint_system, witness };
127 auto builder = create_circuit(program, metadata);
128 return builder;
129 }
130
139 template <typename BuilderType>
140 BuilderType create_outer_circuit(std::vector<InnerBuilder>& inner_circuits, bool dummy_witnesses = false)
141 {
142 std::vector<RecursionConstraint> honk_recursion_constraints;
143
144 SlabVector<fr> witness;
145
146 for (auto& inner_circuit : inner_circuits) {
147
148 auto proving_key = std::make_shared<InnerDeciderProvingKey>(inner_circuit);
149 auto verification_key = std::make_shared<InnerVerificationKey>(proving_key->get_precomputed());
150 InnerProver prover(proving_key, verification_key);
151 InnerVerifier verifier(verification_key);
152 auto inner_proof = prover.construct_proof();
153
154 std::vector<bb::fr> key_witnesses = verification_key->to_field_elements();
155 fr key_hash_witness = verification_key->hash();
156 std::vector<fr> proof_witnesses = inner_proof;
157
158 // Compute the number of public inputs to extract (the ones from the circuit) and the proof type based on
159 // the Flavor
160 auto [num_public_inputs_to_extract, proof_type] = [&]() -> std::pair<size_t, acir_format::PROOF_TYPE> {
161 size_t num_public_inputs_to_extract = inner_circuit.num_public_inputs();
162 if constexpr (HasIPAAccumulator<InnerFlavor>) {
163 return { num_public_inputs_to_extract - RollupIO::PUBLIC_INPUTS_SIZE, ROLLUP_HONK };
164 } else if constexpr (InnerFlavor::HasZK) {
165 return { num_public_inputs_to_extract - DefaultIO::PUBLIC_INPUTS_SIZE, HONK_ZK };
166 } else {
167 return { num_public_inputs_to_extract - DefaultIO::PUBLIC_INPUTS_SIZE, HONK };
168 }
169 }();
170
171 auto [key_indices, key_hash_index, proof_indices, inner_public_inputs] =
173 witness, proof_witnesses, key_witnesses, key_hash_witness, num_public_inputs_to_extract);
174
175 RecursionConstraint honk_recursion_constraint{
176 .key = key_indices,
177 .proof = proof_indices,
178 .public_inputs = inner_public_inputs,
179 .key_hash = key_hash_index,
180 .proof_type = proof_type,
181 };
182 honk_recursion_constraints.push_back(honk_recursion_constraint);
183 }
184
185 AcirFormat constraint_system{};
186 constraint_system.varnum = static_cast<uint32_t>(witness.size());
187 constraint_system.num_acir_opcodes = static_cast<uint32_t>(honk_recursion_constraints.size());
188 constraint_system.honk_recursion_constraints = honk_recursion_constraints;
189 constraint_system.original_opcode_indices = create_empty_original_opcode_indices();
190
191 mock_opcode_indices(constraint_system);
192 uint32_t honk_recursion = 0;
193 if constexpr (IsAnyOf<InnerFlavor, UltraFlavor, UltraZKFlavor>) {
194 honk_recursion = 1;
195 } else if constexpr (IsAnyOf<InnerFlavor, UltraRollupFlavor>) {
196 honk_recursion = 2;
197 }
198 ProgramMetadata metadata{ .honk_recursion = honk_recursion };
199 if (dummy_witnesses) {
200 witness = {}; // set it all to 0
201 }
202 AcirProgram program{ constraint_system, witness };
203 BuilderType outer_circuit = create_circuit<BuilderType>(program, metadata);
204
205 return outer_circuit;
206 }
207
209 const std::shared_ptr<OuterVerificationKey>& verification_key,
210 const HonkProof& proof)
211 {
213
214 bool result = false;
215
216 if constexpr (HasIPAAccumulator<RecursiveFlavor>) {
217 VerifierCommitmentKey<curve::Grumpkin> ipa_verification_key(1 << CONST_ECCVM_LOG_N);
218 OuterVerifier verifier(verification_key, ipa_verification_key);
219 result = verifier.template verify_proof<IO>(proof, proving_key->ipa_proof).result;
220 } else {
221 OuterVerifier verifier(verification_key);
222 result = verifier.template verify_proof<IO>(proof).result;
223 }
224
225 return result;
226 }
227
228 protected:
230};
231
232using Flavors = testing::Types<UltraRecursiveFlavor_<UltraCircuitBuilder>,
237
239
240TYPED_TEST(AcirHonkRecursionConstraint, TestHonkRecursionConstraintVKGeneration)
241{
243 layer_1_circuits.push_back(TestFixture::create_inner_circuit());
244
245 auto layer_2_circuit =
246 TestFixture::template create_outer_circuit<typename TestFixture::OuterBuilder>(layer_1_circuits);
247
248 auto layer_2_circuit_with_dummy_witnesses =
249 TestFixture::template create_outer_circuit<typename TestFixture::OuterBuilder>(layer_1_circuits,
250 /*dummy_witnesses=*/true);
251
252 auto proving_key = std::make_shared<typename TestFixture::OuterDeciderProvingKey>(layer_2_circuit);
253 auto verification_key =
255
256 auto proving_key_dummy =
257 std::make_shared<typename TestFixture::OuterDeciderProvingKey>(layer_2_circuit_with_dummy_witnesses);
258 auto verification_key_dummy =
259 std::make_shared<typename TestFixture::OuterVerificationKey>(proving_key_dummy->get_precomputed());
260
261 // Compare the two vks
262 EXPECT_EQ(*verification_key_dummy, *verification_key);
263}
264
265TYPED_TEST(AcirHonkRecursionConstraint, TestBasicSingleHonkRecursionConstraint)
266{
268 layer_1_circuits.push_back(TestFixture::create_inner_circuit());
269
270 auto layer_2_circuit =
271 TestFixture::template create_outer_circuit<typename TestFixture::OuterBuilder>(layer_1_circuits);
272
273 info("estimate finalized circuit gates = ", layer_2_circuit.get_estimated_num_finalized_gates());
274
275 auto proving_key = std::make_shared<typename TestFixture::OuterDeciderProvingKey>(layer_2_circuit);
276 auto verification_key =
278 typename TestFixture::OuterProver prover(proving_key, verification_key);
279 info("prover gates = ", proving_key->dyadic_size());
280 auto proof = prover.construct_proof();
281
282 EXPECT_EQ(TestFixture::verify_proof(proving_key, verification_key, proof), true);
283}
284
285TYPED_TEST(AcirHonkRecursionConstraint, TestBasicDoubleHonkRecursionConstraints)
286{
288 layer_1_circuits.push_back(TestFixture::create_inner_circuit());
289
290 layer_1_circuits.push_back(TestFixture::create_inner_circuit());
291
292 auto layer_2_circuit =
293 TestFixture::template create_outer_circuit<typename TestFixture::OuterBuilder>(layer_1_circuits);
294
295 info("circuit gates = ", layer_2_circuit.get_estimated_num_finalized_gates());
296
297 auto proving_key = std::make_shared<typename TestFixture::OuterDeciderProvingKey>(layer_2_circuit);
298 auto verification_key =
300 typename TestFixture::OuterProver prover(proving_key, verification_key);
301 info("prover gates = ", proving_key->dyadic_size());
302 auto proof = prover.construct_proof();
303
304 EXPECT_EQ(TestFixture::verify_proof(proving_key, verification_key, proof), true);
305}
306
307TYPED_TEST(AcirHonkRecursionConstraint, TestOneOuterRecursiveCircuit)
308{
343 layer_1_circuits.push_back(TestFixture::create_inner_circuit());
344 info("created first inner circuit");
345
347 layer_2_circuits.push_back(TestFixture::create_inner_circuit());
348 info("created second inner circuit");
349
350 layer_2_circuits.push_back(
351 TestFixture::template create_outer_circuit<typename TestFixture::InnerBuilder>(layer_1_circuits));
352 info("created first outer circuit");
353
354 auto layer_3_circuit =
355 TestFixture::template create_outer_circuit<typename TestFixture::OuterBuilder>(layer_2_circuits);
356 info("created second outer circuit");
357 info("number of gates in layer 3 = ", layer_3_circuit.get_estimated_num_finalized_gates());
358
359 auto proving_key = std::make_shared<typename TestFixture::OuterDeciderProvingKey>(layer_3_circuit);
360 auto verification_key =
362 typename TestFixture::OuterProver prover(proving_key, verification_key);
363 info("prover gates = ", proving_key->dyadic_size());
364 auto proof = prover.construct_proof();
365
366 EXPECT_EQ(TestFixture::verify_proof(proving_key, verification_key, proof), true);
367}
368
386TYPED_TEST(AcirHonkRecursionConstraint, TestFullRecursiveComposition)
387{
389 layer_b_1_circuits.push_back(TestFixture::create_inner_circuit());
390 info("created first inner circuit");
391
393 layer_b_2_circuits.push_back(TestFixture::create_inner_circuit());
394 info("created second inner circuit");
395
396 std::vector<Builder> layer_2_circuits;
397 layer_2_circuits.push_back(
398 TestFixture::template create_outer_circuit<typename TestFixture::InnerBuilder>(layer_b_1_circuits));
399 info("created first outer circuit");
400
401 layer_2_circuits.push_back(
402 TestFixture::template create_outer_circuit<typename TestFixture::InnerBuilder>(layer_b_2_circuits));
403 info("created second outer circuit");
404
405 auto layer_3_circuit =
406 TestFixture::template create_outer_circuit<typename TestFixture::OuterBuilder>(layer_2_circuits);
407 info("created third outer circuit");
408 info("number of gates in layer 3 circuit = ", layer_3_circuit.get_estimated_num_finalized_gates());
409
410 auto proving_key = std::make_shared<typename TestFixture::OuterDeciderProvingKey>(layer_3_circuit);
411 auto verification_key =
413 typename TestFixture::OuterProver prover(proving_key, verification_key);
414 info("prover gates = ", proving_key->dyadic_size());
415 auto proof = prover.construct_proof();
416
417 EXPECT_EQ(TestFixture::verify_proof(proving_key, verification_key, proof), true);
418}
acir_format::AcirFormatOriginalOpcodeIndices create_empty_original_opcode_indices()
void mock_opcode_indices(acir_format::AcirFormat &constraint_system)
typename InnerFlavor::VerificationKey InnerVerificationKey
std::conditional_t< IsMegaBuilder< OuterBuilder >, MegaFlavor, std::conditional_t< HasIPAAccumulator< InnerFlavor >, UltraRollupFlavor, UltraFlavor > > OuterFlavor
typename RecursiveFlavor::CircuitBuilder OuterBuilder
typename InnerFlavor::CircuitBuilder InnerBuilder
BuilderType create_outer_circuit(std::vector< InnerBuilder > &inner_circuits, bool dummy_witnesses=false)
Create a circuit that recursively verifies one or more circuits.
typename OuterFlavor::VerificationKey OuterVerificationKey
typename RecursiveFlavor::NativeFlavor InnerFlavor
bool verify_proof(const std::shared_ptr< OuterDeciderProvingKey > &proving_key, const std::shared_ptr< OuterVerificationKey > &verification_key, const HonkProof &proof)
static RecursionWitnessData populate_recursion_witness_data(bb::SlabVector< FF > &witness, std::vector< FF > &proof_witnesses, const std::vector< FF > &key_witnesses, const FF &key_hash_witness, const size_t num_public_inputs_to_extract)
Populate a witness vector with key, proof, and public inputs; track witness indices for each componen...
A DeciderProvingKey is normally constructed from a finalized circuit and it contains all the informat...
Manages the data that is propagated on the public inputs of an application/function circuit.
static constexpr size_t PUBLIC_INPUTS_SIZE
The data that is propagated on the public inputs of a rollup circuit.
static constexpr size_t PUBLIC_INPUTS_SIZE
The recursive counterpart to the "native" Ultra flavor.
The recursive counterpart to the "native" UltraRollupFlavor.
The recursive counterpart to the Ultra flavor with ZK.
Representation of the Grumpkin Verifier Commitment Key inside a bn254 circuit.
void info(Args... args)
Definition log.hpp:70
AluTraceBuilder builder
Definition alu.test.cpp:123
UltraCircuitBuilder create_circuit(AcirProgram &program, const ProgramMetadata &metadata)
Specialization for creating an Ultra circuit from an acir program.
bb::SlabVector< bb::fr > WitnessVector
std::filesystem::path bb_crs_path()
void init_file_crs_factory(const std::filesystem::path &path)
testing::Types< UltraRecursiveFlavor_< UltraCircuitBuilder > > Flavors
TYPED_TEST_SUITE(BoomerangRecursiveVerifierTest, Flavors)
Entry point for Barretenberg command-line interface.
std::vector< fr > HonkProof
Definition proof.hpp:15
field< Bn254FrParams > fr
Definition fr.hpp:174
std::vector< T, bb::ContainerSlabAllocator< T > > SlabVector
A vector that uses the slab allocator.
TYPED_TEST(ShpleminiTest, CorrectnessOfMultivariateClaimBatching)
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
WitnessOrConstant< bb::fr > a
RecursionConstraint struct contains information required to recursively verify a proof!
static WitnessOrConstant from_index(uint32_t index)
constexpr field invert() const noexcept