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: Planned, auditors: [], commit: }
3// external_1: { status: not started, auditors: [], commit: }
4// external_2: { status: not started, auditors: [], commit: }
5// =====================
6
12
13namespace acir_format {
14
15template <>
19 std::vector<size_t>& gates_per_opcode,
20 [[maybe_unused]] const std::shared_ptr<IVCBase>& ivc_base,
21 const std::pair<std::vector<RecursionConstraint>, std::vector<size_t>>& honk_recursion_data,
22 const std::pair<std::vector<RecursionConstraint>, std::vector<size_t>>& avm_recursion_data,
23 const std::pair<std::vector<RecursionConstraint>, std::vector<size_t>>& hn_recursion_data,
24 const std::pair<std::vector<RecursionConstraint>, std::vector<size_t>>& chonk_recursion_data)
25{
26 bool has_honk_recursion_constraints = !honk_recursion_data.first.empty();
27 bool has_avm_recursion_constraints = !avm_recursion_data.first.empty();
28 bool has_hn_recursion_constraints = !hn_recursion_data.first.empty();
29 bool has_chonk_recursion_constraints = !chonk_recursion_data.first.empty();
30
31 // We shouldn't have both honk recursion constraints and HN recursion constraints.
32 BB_ASSERT(!has_honk_recursion_constraints || !has_hn_recursion_constraints,
33 "Invalid circuit: both honk and ivc recursion constraints present.");
34 // AVM constraints are not handled when using MegaBuilder
35 BB_ASSERT(!has_avm_recursion_constraints,
36 "Invalid circuit: avm recursion constraints are not supported with MegaBuilder.");
37
38 // Chonk constraints are not handled when using MegaBuilder
39 BB_ASSERT(!has_chonk_recursion_constraints,
40 "Invalid circuit: chonk recursion constraints are not supported with MegaBuilder.");
42
43 for (const auto& [constraint, opcode_idx] : zip_view(honk_recursion_data.first, honk_recursion_data.second)) {
45
46 if (constraint.proof_type == HONK_ZK) {
47 honk_recursion_constraint =
48 create_honk_recursion_constraints<UltraZKRecursiveFlavor_<MegaCircuitBuilder>>(builder, constraint);
49 } else if (constraint.proof_type == HONK) {
50 honk_recursion_constraint =
51 create_honk_recursion_constraints<UltraRecursiveFlavor_<MegaCircuitBuilder>>(builder, constraint);
52 } else if (constraint.proof_type == ROLLUP_HONK || constraint.proof_type == ROOT_ROLLUP_HONK) {
53 bb::assert_failure("Rollup Honk proof type not supported on MegaBuilder");
54 } else {
55 bb::assert_failure("Invalid Honk proof type");
56 }
57
58 output.update(honk_recursion_constraint, /*update_ipa_data=*/false); // Update output
59 gate_counter.track_diff(gates_per_opcode, opcode_idx); // Track gate count
60 }
61
62 if (has_hn_recursion_constraints) {
63 process_hn_recursion_constraints(builder, gate_counter, gates_per_opcode, hn_recursion_data, ivc_base);
64 }
65
66 return output;
67}
68
69template <>
73 std::vector<size_t>& gates_per_opcode,
74 [[maybe_unused]] const std::shared_ptr<IVCBase>& ivc_base,
75 const std::pair<std::vector<RecursionConstraint>, std::vector<size_t>>& honk_recursion_data,
76 const std::pair<std::vector<RecursionConstraint>, std::vector<size_t>>& avm_recursion_data,
77 const std::pair<std::vector<RecursionConstraint>, std::vector<size_t>>& hn_recursion_data,
78 const std::pair<std::vector<RecursionConstraint>, std::vector<size_t>>& chonk_recursion_data)
79{
80 bool has_honk_recursion_constraints = !honk_recursion_data.first.empty();
81 bool has_avm_recursion_constraints = !avm_recursion_data.first.empty();
82 bool has_hn_recursion_constraints = !hn_recursion_data.first.empty();
83 bool has_chonk_recursion_constraints = !chonk_recursion_data.first.empty();
84
85 BB_ASSERT(!has_hn_recursion_constraints,
86 "Invalid circuit: HN recursion constraints are present with UltraBuilder.");
87 BB_ASSERT(!(has_chonk_recursion_constraints && has_honk_recursion_constraints),
88 "Invalid circuit: both honk and chonk recursion constraints are present.");
89 if (has_chonk_recursion_constraints && has_avm_recursion_constraints) {
90 vinfo("WARNING: both chonk and avm recursion constraints are present. While we support this combination, we "
91 "expect to see it only in a mock circuit.");
92 }
93
95
96 for (const auto& [constraint, opcode_idx] : zip_view(honk_recursion_data.first, honk_recursion_data.second)) {
98
99 if (constraint.proof_type == HONK_ZK) {
100 honk_recursion_constraint =
101 create_honk_recursion_constraints<UltraZKRecursiveFlavor_<UltraCircuitBuilder>>(builder, constraint);
102 } else if (constraint.proof_type == HONK) {
103 honk_recursion_constraint =
104 create_honk_recursion_constraints<UltraRecursiveFlavor_<UltraCircuitBuilder>>(builder, constraint);
105 } else if (constraint.proof_type == ROLLUP_HONK || constraint.proof_type == ROOT_ROLLUP_HONK) {
106 honk_recursion_constraint =
107 create_honk_recursion_constraints<UltraRollupRecursiveFlavor_<UltraCircuitBuilder>>(builder,
108 constraint);
109 } else {
110 bb::assert_failure("Invalid Honk proof type");
111 }
112
113 // Update output
114 output.update(honk_recursion_constraint,
115 /*update_ipa_data=*/constraint.proof_type == ROLLUP_HONK ||
116 constraint.proof_type == ROOT_ROLLUP_HONK);
117 output.is_root_rollup = constraint.proof_type == ROOT_ROLLUP_HONK;
118
119 gate_counter.track_diff(gates_per_opcode, opcode_idx);
120 }
121 BB_ASSERT(!(output.is_root_rollup && output.nested_ipa_claims.size() != 2),
122 "Root rollup must accumulate two IPA proofs.");
123
124 for (const auto& [constraint, opcode_idx] : zip_view(chonk_recursion_data.first, chonk_recursion_data.second)) {
127
128 // Update the output
129 output.update(honk_output, /*update_ipa_data=*/true);
130
131 gate_counter.track_diff(gates_per_opcode, opcode_idx);
132 }
133
134 for (const auto& [constraint, opcode_idx] : zip_view(avm_recursion_data.first, avm_recursion_data.second)) {
137
138 // Update the output
139 output.update(honk_output, /*update_ipa_data=*/true);
140
141 gate_counter.track_diff(gates_per_opcode, opcode_idx);
142 }
143
144 return output;
145}
146
150 std::vector<size_t>& gates_per_opcode,
151 const std::pair<std::vector<RecursionConstraint>, std::vector<size_t>>& hn_recursion_data,
152 const std::shared_ptr<IVCBase>& ivc_base)
153{
154 using StdlibVerificationKey = Chonk::RecursiveVerificationKey;
155 using StdlibVKAndHash = Chonk::RecursiveVKAndHash;
156 using StdlibFF = Chonk::RecursiveFlavor::FF;
157
158 // Lambda template to handle both Chonk and Chonk with the same code
159 auto process_with_ivc = [&]<typename IVCType>(const std::shared_ptr<IVCType>& ivc) {
160 // We expect the length of the internal verification queue to match the number of ivc recursion constraints
161 BB_ASSERT_EQ(hn_recursion_data.first.size(),
162 ivc->verification_queue.size(),
163 "WARNING: Mismatch in number of recursive verifications during kernel creation!");
164
165 // If no witness is provided, populate the VK and public inputs in the recursion constraint with dummy values so
166 // that the present kernel circuit is constructed correctly. (Used for constructing VKs without witnesses).
167 if (builder.is_write_vk_mode()) {
168 // Create stdlib representations of each {proof, vkey} pair to be recursively verified
169 for (auto [constraint, queue_entry] : zip_view(hn_recursion_data.first, ivc->verification_queue)) {
170 populate_dummy_vk_in_constraint(builder, queue_entry.honk_vk, constraint.key);
171 builder.set_variable(constraint.key_hash, queue_entry.honk_vk->hash());
172 }
173 }
174
175 // Construct a stdlib verification key for each constraint based on the verification key witness indices
176 // therein
178 stdlib_vk_and_hashs.reserve(hn_recursion_data.first.size());
179 for (const auto& constraint : hn_recursion_data.first) {
180 stdlib_vk_and_hashs.push_back(std::make_shared<StdlibVKAndHash>(
182 StdlibVerificationKey::from_witness_indices(builder, constraint.key)),
183 StdlibFF::from_witness_index(&builder, constraint.key_hash)));
184 }
185 // Create stdlib representations of each {proof, vkey} pair to be recursively verified
186 ivc->instantiate_stdlib_verification_queue(builder, stdlib_vk_and_hashs);
187
188 // TODO(https://github.com/AztecProtocol/barretenberg/issues/1597): Are there public inputs in the queue_entry
189 // proofs?
190 // Connect the public_input witnesses in each constraint to the corresponding public input witnesses in
191 // the internal verification queue. This ensures that the witnesses utilized in constraints generated based on
192 // acir are properly connected to the constraints generated herein via the ivc scheme (e.g. recursive
193 // verifications).
194 for (auto [constraint, queue_entry] : zip_view(hn_recursion_data.first, ivc->stdlib_verification_queue)) {
195 std::vector<StdlibFF> public_inputs_from_proof(queue_entry.proof.begin(),
196 queue_entry.proof.begin() +
197 static_cast<ptrdiff_t>(constraint.public_inputs.size()));
198
199 for (const auto [proof_public_input, constraint_public_input_idx] :
200 zip_view(public_inputs_from_proof, constraint.public_inputs)) {
201 const StdlibFF constraint_public_input =
202 StdlibFF::from_witness_index(&builder, constraint_public_input_idx);
203 proof_public_input.assert_equal(constraint_public_input);
204 }
205 }
206
207 // Complete the kernel circuit with all required recursive verifications, databus consistency checks etc.
208 ivc->complete_kernel_circuit_logic(builder);
209
210 // Note: we can't easily track the gate contribution from each individual hn_recursion_constraint since they
211 // are handled simultaneously in the above function call; instead we track the total contribution
212 gate_counter.track_diff(gates_per_opcode, hn_recursion_data.second.at(0));
213 };
214
215 // If an ivc instance is not provided, we mock one with the state required to construct the recursion
216 // constraints present in the program. This is for when we write_vk.
217 if (ivc_base == nullptr) {
218 auto mock_ivc = create_mock_chonk_from_constraints(hn_recursion_data.first);
219 process_with_ivc(mock_ivc);
220 } else {
221 auto sumcheck_ivc = std::static_pointer_cast<Chonk>(ivc_base);
222 process_with_ivc(sumcheck_ivc);
223 }
224}
225
226} // namespace acir_format
#define BB_ASSERT(expression,...)
Definition assert.hpp:80
#define BB_ASSERT_EQ(actual, expected,...)
Definition assert.hpp:93
Utility class for tracking the gate count of acir constraints.
void track_diff(std::vector< size_t > &gates_per_opcode, size_t opcode_index)
RecursiveFlavor::VerificationKey RecursiveVerificationKey
Definition chonk.hpp:62
RecursiveFlavor::VKAndHash RecursiveVKAndHash
Definition chonk.hpp:63
typename Curve::ScalarField FF
#define vinfo(...)
Definition log.hpp:94
AluTraceBuilder builder
Definition alu.test.cpp:124
HonkRecursionConstraintOutput< bb::UltraCircuitBuilder > create_avm2_recursion_constraints_goblin(bb::UltraCircuitBuilder &builder, const RecursionConstraint &input)
Stub implementation for AVM2 recursion constraints.
void populate_dummy_vk_in_constraint(MegaCircuitBuilder &builder, const std::shared_ptr< MegaFlavor::VerificationKey > &mock_verification_key, const std::vector< uint32_t > &key_witness_indices)
Populate VK witness fields from a recursion constraint from a provided VerificationKey.
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< IVCBase > &ivc_base)
Process HyperNova recursion constraints and complete kernel logic.
std::shared_ptr< Chonk > create_mock_chonk_from_constraints(const std::vector< RecursionConstraint > &constraints)
Create an IVC object with mocked state corresponding to a set of IVC recursion constraints.
HonkRecursionConstraintsOutput< MegaCircuitBuilder > create_recursion_constraints(MegaCircuitBuilder &builder, GateCounter< MegaCircuitBuilder > &gate_counter, std::vector< size_t > &gates_per_opcode, const std::shared_ptr< IVCBase > &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)
HonkRecursionConstraintOutput< bb::UltraCircuitBuilder > 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) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
Container for the output of multiple recursive verifications.
std::vector< OpeningClaim< stdlib::grumpkin< Builder > > > nested_ipa_claims
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.