Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
recursion_constraint.cpp
Go to the documentation of this file.
1// === AUDIT STATUS ===
2// internal: { status: Completed, auditors: [Federico], commit: }
3// external_1: { status: not started, auditors: [], commit: }
4// external_2: { status: not started, auditors: [], commit: }
5// =====================
6
13
14namespace acir_format {
15
16template <>
20 std::vector<size_t>& gates_per_opcode,
21 [[maybe_unused]] const std::shared_ptr<Chonk>& ivc_base,
22 const std::pair<std::vector<RecursionConstraint>, std::vector<size_t>>& honk_recursion_data,
23 const std::pair<std::vector<RecursionConstraint>, std::vector<size_t>>& avm_recursion_data,
24 const std::pair<std::vector<RecursionConstraint>, std::vector<size_t>>& hn_recursion_data,
25 const std::pair<std::vector<RecursionConstraint>, std::vector<size_t>>& chonk_recursion_data)
26{
27 bool has_honk_recursion_constraints = !honk_recursion_data.first.empty();
28 bool has_avm_recursion_constraints = !avm_recursion_data.first.empty();
29 bool has_hn_recursion_constraints = !hn_recursion_data.first.empty();
30 bool has_chonk_recursion_constraints = !chonk_recursion_data.first.empty();
31
32 // Schema invariants: validate constraint type combinations for MegaBuilder
33 BB_ASSERT(!(has_honk_recursion_constraints && has_hn_recursion_constraints),
34 "create_recursion_constraints: invalid circuit - both honk and HN recursion constraints present");
36 !has_avm_recursion_constraints,
37 "create_recursion_constraints: invalid circuit - AVM recursion constraints not supported with MegaBuilder");
38 BB_ASSERT(!has_chonk_recursion_constraints,
39 "create_recursion_constraints: invalid circuit - Chonk recursion constraints not supported with "
40 "MegaBuilder");
41
43
44 for (const auto& [constraint, opcode_idx] : zip_view(honk_recursion_data.first, honk_recursion_data.second)) {
46
47 if (constraint.proof_type == HONK_ZK) {
48 honk_recursion_constraint =
49 create_honk_recursion_constraints<UltraZKRecursiveFlavor_<MegaCircuitBuilder>,
51 constraint);
52 } else if (constraint.proof_type == HONK) {
53 honk_recursion_constraint =
54 create_honk_recursion_constraints<UltraRecursiveFlavor_<MegaCircuitBuilder>,
56 constraint);
57 } else if (constraint.proof_type == ROLLUP_HONK || constraint.proof_type == ROOT_ROLLUP_HONK) {
58 bb::assert_failure("Rollup Honk proof type not supported on MegaBuilder");
59 } else {
60 bb::assert_failure("Invalid Honk proof type");
61 }
62
63 output.update(honk_recursion_constraint, /*update_ipa_data=*/false); // Update output
64 gate_counter.track_diff(gates_per_opcode, opcode_idx); // Track gate count
65 }
66
67 if (has_hn_recursion_constraints) {
68 process_hn_recursion_constraints(builder, gate_counter, gates_per_opcode, hn_recursion_data, ivc_base);
69 }
70
71 return output;
72}
73
74template <>
78 std::vector<size_t>& gates_per_opcode,
79 [[maybe_unused]] const std::shared_ptr<Chonk>& ivc_base,
80 const std::pair<std::vector<RecursionConstraint>, std::vector<size_t>>& honk_recursion_data,
81 const std::pair<std::vector<RecursionConstraint>, std::vector<size_t>>& avm_recursion_data,
82 const std::pair<std::vector<RecursionConstraint>, std::vector<size_t>>& hn_recursion_data,
83 const std::pair<std::vector<RecursionConstraint>, std::vector<size_t>>& chonk_recursion_data)
84{
85 bool has_honk_recursion_constraints = !honk_recursion_data.first.empty();
86 bool has_avm_recursion_constraints = !avm_recursion_data.first.empty();
87 bool has_hn_recursion_constraints = !hn_recursion_data.first.empty();
88 bool has_chonk_recursion_constraints = !chonk_recursion_data.first.empty();
89
90 // Schema invariants: validate constraint type combinations for UltraBuilder
92 !has_hn_recursion_constraints,
93 "create_recursion_constraints: invalid circuit - HN recursion constraints not supported with UltraBuilder");
94 BB_ASSERT(!(has_chonk_recursion_constraints && has_honk_recursion_constraints),
95 "create_recursion_constraints: invalid circuit - both honk and chonk recursion constraints present");
96 if (has_chonk_recursion_constraints && has_avm_recursion_constraints) {
97 vinfo("WARNING: both chonk and avm recursion constraints are present. While we support this combination, we "
98 "expect to see it only in a mock circuit.");
99 }
100
102
103 for (const auto& [constraint, opcode_idx] : zip_view(honk_recursion_data.first, honk_recursion_data.second)) {
105
106 if (constraint.proof_type == HONK_ZK) {
107 honk_recursion_constraint =
108 create_honk_recursion_constraints<UltraZKRecursiveFlavor_<UltraCircuitBuilder>,
110 constraint);
111 } else if (constraint.proof_type == HONK) {
112 honk_recursion_constraint =
113 create_honk_recursion_constraints<UltraRecursiveFlavor_<UltraCircuitBuilder>,
115 constraint);
116 } else if (constraint.proof_type == ROLLUP_HONK || constraint.proof_type == ROOT_ROLLUP_HONK) {
117 // Use UltraRecursiveFlavor with RollupIO for rollup proofs (IO determines IPA handling)
118 honk_recursion_constraint =
119 create_honk_recursion_constraints<UltraRecursiveFlavor_<UltraCircuitBuilder>,
121 } else {
122 bb::assert_failure("Invalid Honk proof type");
123 }
124
125 // Update output
126 output.update(honk_recursion_constraint,
127 /*update_ipa_data=*/constraint.proof_type == ROLLUP_HONK ||
128 constraint.proof_type == ROOT_ROLLUP_HONK);
129 output.is_root_rollup = constraint.proof_type == ROOT_ROLLUP_HONK;
130
131 gate_counter.track_diff(gates_per_opcode, opcode_idx);
132 }
133 BB_ASSERT(
134 !(output.is_root_rollup && output.nested_ipa_claims.size() + output.nested_triple_ipa_openings.size() != 2),
135 "Root rollup must accumulate two IPA proofs.");
136
137 for (const auto& [constraint, opcode_idx] : zip_view(chonk_recursion_data.first, chonk_recursion_data.second)) {
139
141
142 gate_counter.track_diff(gates_per_opcode, opcode_idx);
143 }
144
145 for (const auto& [constraint, opcode_idx] : zip_view(avm_recursion_data.first, avm_recursion_data.second)) {
147
149
150 gate_counter.track_diff(gates_per_opcode, opcode_idx);
151 }
152
153 return output;
154}
155
159 std::vector<size_t>& gates_per_opcode,
160 const std::pair<std::vector<RecursionConstraint>, std::vector<size_t>>& hn_recursion_data,
161 const std::shared_ptr<Chonk>& ivc_base)
162{
163 using StdlibFF = Chonk::StdlibFF;
165 using AppStdlibVKAndHash = Chonk::AppRecursiveVKAndHash;
167 using KernelStdlibVKAndHash = Chonk::KernelRecursiveVKAndHash;
168
169 // Validate hn_recursion_data constraints/indices size match
170 BB_ASSERT_EQ(hn_recursion_data.first.size(),
171 hn_recursion_data.second.size(),
172 "process_hn_recursion_constraints: hn_recursion_data constraints/indices size mismatch");
173
174 // Lambda template to handle both Chonk and Chonk with the same code
175 auto process_with_ivc = [&]<typename IVCType>(const std::shared_ptr<IVCType>& ivc) {
176 // We expect the length of the internal verification queue to match the number of ivc recursion constraints
177 BB_ASSERT_EQ(hn_recursion_data.first.size(),
178 ivc->verification_queue.size(),
179 "process_hn_recursion_constraints: mismatch in number of recursive verifications during kernel "
180 "creation!");
181
182 // If no witness is provided, populate the VK and public inputs in the recursion constraint with dummy values so
183 // that the present kernel circuit is constructed correctly. (Used for constructing VKs without witnesses).
184 if (builder.is_write_vk_mode()) {
185 for (auto [constraint, queue_entry] : zip_view(hn_recursion_data.first, ivc->verification_queue)) {
186 auto key_fields = fields_from_witnesses(builder, constraint.key);
187 populate_fields(builder, key_fields, queue_entry.vk_to_field_elements());
188 builder.set_variable(constraint.key_hash, queue_entry.vk_hash());
189 }
190 }
191
192 std::vector<bb::StdlibCircuitVKAndHash> stdlib_vk_and_hashs;
193 stdlib_vk_and_hashs.reserve(hn_recursion_data.first.size());
194 for (auto [constraint, queue_entry] : zip_view(hn_recursion_data.first, ivc->verification_queue)) {
195 auto hash = StdlibFF::from_witness_index(&builder, constraint.key_hash);
196 if (queue_entry.is_kernel()) {
197 stdlib_vk_and_hashs.emplace_back(std::make_shared<KernelStdlibVKAndHash>(
198 std::make_shared<KernelStdlibVK>(KernelStdlibVK::from_witness_indices(builder, constraint.key)),
199 hash));
200 } else {
201 stdlib_vk_and_hashs.emplace_back(std::make_shared<AppStdlibVKAndHash>(
202 std::make_shared<AppStdlibVK>(AppStdlibVK::from_witness_indices(builder, constraint.key)), hash));
203 }
204 }
205 // Create stdlib representations of each {proof, vkey} pair to be recursively verified
206 ivc->instantiate_stdlib_verification_queue(builder, stdlib_vk_and_hashs);
207
208 // Verify stdlib queue size matches after instantiation (invariant check)
209 BB_ASSERT_EQ(ivc->stdlib_verification_queue.size(),
210 hn_recursion_data.first.size(),
211 "process_hn_recursion_constraints: stdlib_verification_queue size mismatch after instantiation");
212
213 // Validate constraints against stdlib verification queue entries
214 size_t group_index = 0;
215 for (auto [constraint, queue_entry] : zip_view(hn_recursion_data.first, ivc->stdlib_verification_queue)) {
216 // Cross-check the ACIR proof_type and the queued proof's kind against the type/kind implied by the
217 // IVC's circuit kinds and the entry's position in the kernel's group. The IVC's circuit kinds (not
218 // the proof_type) drive the verification logic; this is defense-in-depth that the two agree.
220 static_cast<PROOF_TYPE>(constraint.proof_type),
221 expected_proof_type(*ivc, group_index),
222 "process_hn_recursion_constraints: ACIR proof_type disagrees with circuit-kinds-derived state");
224 queue_entry.kind,
225 expected_group_entry_kind(*ivc, group_index),
226 "process_hn_recursion_constraints: queue entry kind disagrees with circuit-kinds-derived position");
227
228 // HN recursion constraints from Noir always have empty public_inputs - the public inputs are handled
229 // entirely by the IVC (KernelIO/AppIO). If this changes in the future, we need to implement binding
230 // between ACIR public inputs and proof public inputs.
231 BB_ASSERT(constraint.public_inputs.empty(),
232 "process_hn_recursion_constraints: unexpected non-empty public_inputs in HN constraint - "
233 "Noir HN constraints should have empty public_inputs (public inputs are handled by IVC IO)");
234
235 // Validate public input layout: IO region size must match VK's num_public_inputs.
236 const size_t expected_io_size = bb::dispatch_kind(
237 queue_entry.kind, [&]<bb::CircuitKind K>() -> size_t { return bb::io_for<K>::PUBLIC_INPUTS_SIZE; });
238 const size_t vk_num_public_inputs = queue_entry.vk_num_public_inputs();
239 BB_ASSERT_EQ(expected_io_size,
240 vk_num_public_inputs,
241 "process_hn_recursion_constraints: IO size mismatch with VK num_public_inputs");
242
243 // Sanity check: the proof vector stores public inputs at its start, so it must have at
244 // least num_public_inputs elements.
245 BB_ASSERT_GTE(queue_entry.proof.size(),
246 vk_num_public_inputs,
247 "process_hn_recursion_constraints: proof vector smaller than num_public_inputs - malformed "
248 "proof");
249 ++group_index;
250 }
251
252 // Complete the kernel circuit with all required recursive verifications, databus consistency checks etc.
253 ivc->complete_kernel_circuit_logic(builder);
254
255 // Note: we can't easily track the gate contribution from each individual hn_recursion_constraint since they
256 // are handled simultaneously in the above function call; instead we track the total contribution
257 gate_counter.track_diff(gates_per_opcode, hn_recursion_data.second.at(0));
258 };
259
260 // If an ivc instance is not provided, we mock one with the state required to construct the recursion
261 // constraints present in the program. This is for when we write_vk.
262 if (ivc_base == nullptr) {
263 process_with_ivc(create_mock_chonk_from_constraints(hn_recursion_data.first));
264 } else {
265 process_with_ivc(ivc_base);
266 }
267}
268
269} // namespace acir_format
#define BB_ASSERT(expression,...)
Definition assert.hpp:70
#define BB_ASSERT_GTE(left, right,...)
Definition assert.hpp:128
#define BB_ASSERT_EQ(actual, expected,...)
Definition assert.hpp:83
Utility class for tracking the gate count of acir constraints.
void track_diff(std::vector< size_t > &gates_per_opcode, size_t opcode_index)
KernelRecursiveFlavor::FF StdlibFF
Definition chonk.hpp:66
AppRecursiveFlavor::VKAndHash AppRecursiveVKAndHash
Definition chonk.hpp:72
KernelRecursiveFlavor::VKAndHash KernelRecursiveVKAndHash
Definition chonk.hpp:73
StdlibVerificationKey_< CircuitBuilder, NativeFlavor::PrecomputedEntities< Commitment >, NativeFlavor::VerificationKey > VerificationKey
StdlibVerificationKey_< CircuitBuilder, NativeFlavor::PrecomputedEntities< Commitment >, NativeFlavor::VerificationKey > VerificationKey
Manages the data that is propagated on the public inputs of an application/function circuit.
The data that is propagated on the public inputs of a rollup circuit.
#define vinfo(...)
Definition log.hpp:94
AluTraceBuilder builder
Definition alu.test.cpp:124
bb::CircuitKind expected_group_entry_kind(const Chonk &ivc, size_t group_index)
AvmRecursionConstraintOutput create_avm2_recursion_constraints_goblin(bb::UltraCircuitBuilder &builder, const RecursionConstraint &input)
Add constraints associated with recursive verification of an AVM2 proof using Goblin.
PROOF_TYPE expected_proof_type(const Chonk &ivc, size_t group_index)
void populate_fields(Builder &builder, const std::vector< field_t< Builder > > &fields, const std::vector< bb::fr > &values)
========== WRITE_VK UTILITIES ========== ///
Definition utils.cpp:78
std::vector< field_t< Builder > > fields_from_witnesses(Builder &builder, std::span< const uint32_t > witness_indices)
========== ACIR TO BARRETENBERG ========== ///
Definition utils.cpp:16
HonkRecursionConstraintsOutput< MegaCircuitBuilder > create_recursion_constraints(MegaCircuitBuilder &builder, GateCounter< MegaCircuitBuilder > &gate_counter, std::vector< size_t > &gates_per_opcode, const std::shared_ptr< Chonk > &ivc_base, const std::pair< std::vector< RecursionConstraint >, std::vector< size_t > > &honk_recursion_data, const std::pair< std::vector< RecursionConstraint >, std::vector< size_t > > &avm_recursion_data, const std::pair< std::vector< RecursionConstraint >, std::vector< size_t > > &hn_recursion_data, const std::pair< std::vector< RecursionConstraint >, std::vector< size_t > > &chonk_recursion_data)
std::shared_ptr< Chonk > create_mock_chonk_from_constraints(const std::vector< RecursionConstraint > &constraints)
Create a Chonk instance with mocked state corresponding to a set of IVC recursion constraints.
void process_hn_recursion_constraints(MegaCircuitBuilder &builder, GateCounter< MegaCircuitBuilder > &gate_counter, std::vector< size_t > &gates_per_opcode, const std::pair< std::vector< RecursionConstraint >, std::vector< size_t > > &hn_recursion_data, const std::shared_ptr< Chonk > &ivc_base)
Process HyperNova recursion constraints and complete kernel logic.
ChonkRecursionConstraintOutput create_chonk_recursion_constraints(bb::UltraCircuitBuilder &builder, const RecursionConstraint &input)
Add constraints associated with recursive verification of a Chonk proof.
void assert_failure(std::string const &err)
Definition assert.cpp:11
constexpr decltype(auto) dispatch_kind(CircuitKind kind, F &&f)
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
bb::ECCVMRecursiveVerifier::DeferredTripleIpaOpening triple_ipa_opening
stdlib::recursion::PairingPoints< stdlib::bn254< bb::UltraCircuitBuilder > > points_accumulator
bb::ECCVMRecursiveVerifier::DeferredTripleIpaOpening triple_ipa_opening
stdlib::recursion::PairingPoints< stdlib::bn254< bb::UltraCircuitBuilder > > points_accumulator
Container for the output of multiple recursive verifications.
std::vector< OpeningClaim< stdlib::grumpkin< Builder > > > nested_ipa_claims
void update_triple_ipa_opening(const stdlib::recursion::PairingPoints< stdlib::bn254< Builder > > &pairing_points, TripleIpaOpening triple_ipa_opening)
void update(const HonkRecursionConstraintOutput< Builder > &other, bool update_ipa_data)
Update the current output with another recursion constraint output.
Output type for recursive ultra verification.