Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
honk_recursion_constraint.cpp
Go to the documentation of this file.
1// === AUDIT STATUS ===
2// internal: { status: not started, auditors: [], date: YYYY-MM-DD }
3// external_1: { status: not started, auditors: [], date: YYYY-MM-DD }
4// external_2: { status: not started, auditors: [], date: YYYY-MM-DD }
5// =====================
6
22#include "proof_surgeon.hpp"
24
25#include <cstddef>
26
27namespace acir_format {
28
29using namespace bb;
30using namespace bb::stdlib::recursion::honk;
31template <typename Builder> using field_ct = stdlib::field_t<Builder>;
32template <typename Builder> using bn254 = stdlib::bn254<Builder>;
34
35namespace {
48template <typename Flavor>
50 size_t proof_size,
51 size_t public_inputs_size,
52 const std::vector<field_ct<typename Flavor::CircuitBuilder>>& key_fields,
53 const std::vector<field_ct<typename Flavor::CircuitBuilder>>& proof_fields)
54 requires IsRecursiveFlavor<Flavor>
55{
56 using Builder = typename Flavor::CircuitBuilder;
57 using NativeFlavor = typename Flavor::NativeFlavor;
61
62 // Set vkey->circuit_size correctly based on the proof size
63 BB_ASSERT_EQ(proof_size, NativeFlavor::PROOF_LENGTH_WITHOUT_PUB_INPUTS());
64
65 size_t num_inner_public_inputs = public_inputs_size - IO::PUBLIC_INPUTS_SIZE;
66 uint32_t pub_inputs_offset = NativeFlavor::has_zero_row ? 1 : 0;
67
68 // Generate mock honk vk
69 auto honk_vk = create_mock_honk_vk<typename Flavor::NativeFlavor, IO>(
70 1 << Flavor::VIRTUAL_LOG_N, pub_inputs_offset, num_inner_public_inputs);
71
72 size_t offset = 0;
73
74 // Set honk vk in builder
75 for (auto& vk_element : honk_vk->to_field_elements()) {
76 builder.set_variable(key_fields[offset].get_witness_index(), vk_element);
77 offset++;
78 }
79
80 // Generate dummy honk proof
81 bb::HonkProof honk_proof = create_mock_honk_proof<typename Flavor::NativeFlavor, IO>(num_inner_public_inputs);
82
83 offset = 0;
84 // Set honk proof in builder
85 for (auto& proof_element : honk_proof) {
86 builder.set_variable(proof_fields[offset].get_witness_index(), proof_element);
87 offset++;
88 }
89
90 BB_ASSERT_EQ(offset, proof_size + public_inputs_size);
91}
92
106template <typename Flavor>
107void place_holder_proof_and_vk(typename Flavor::CircuitBuilder& builder,
108 std::vector<fr>& place_holder_vk_fields,
109 HonkProof& place_holder_proof,
110 field_ct<typename Flavor::CircuitBuilder>& place_holder_vk_hash,
111 size_t proof_size,
112 size_t public_inputs_size,
113 const std::vector<field_ct<typename Flavor::CircuitBuilder>>& vk_fields,
114 const std::vector<field_ct<typename Flavor::CircuitBuilder>>& proof_fields)
115{
119 // Populate the key fields and proof fields with dummy values to prevent issues (e.g. points must be on curve).
120 if (builder.is_write_vk_mode()) {
121 // In the constraint, the agg object public inputs are still contained in the proof. To get the 'raw' size of
122 // the proof and public_inputs we subtract and add the corresponding amount from the respective sizes.
123 size_t size_of_proof_with_no_pub_inputs = proof_size - IO::PUBLIC_INPUTS_SIZE;
124 size_t total_num_public_inputs = public_inputs_size + IO::PUBLIC_INPUTS_SIZE;
125 // Set a dummy vkey and proof in the builder
126 create_dummy_vkey_and_proof<Flavor>(
127 builder, size_of_proof_with_no_pub_inputs, total_num_public_inputs, vk_fields, proof_fields);
128 // Generate a mock place holder proof, vk and vk hash, to keep the circuit the same independent of whether a
129 // witness is provided or not.
130 uint32_t pub_inputs_offset = Flavor::NativeFlavor::has_zero_row ? 1 : 0;
131
132 // Generate mock honk vk
133 auto honk_vk = create_mock_honk_vk<typename Flavor::NativeFlavor, IO>(
134 1 << Flavor::VIRTUAL_LOG_N, pub_inputs_offset, public_inputs_size);
135 place_holder_vk_fields = honk_vk->to_field_elements();
136 place_holder_proof = create_mock_honk_proof<typename Flavor::NativeFlavor, IO>(public_inputs_size);
137 place_holder_vk_hash = honk_vk->hash();
138 } else {
139 // If we have an actual witness, the place holder proof and VK should be an actual verifiable honk proof and
140 // honk vk. Otherwise the recursive verification would fail if the predicate is false.
141 auto [place_holder_honk_proof, place_holder_vk] =
142 construct_honk_proof_for_simple_circuit<typename Flavor::NativeFlavor>(public_inputs_size);
143 place_holder_proof = place_holder_honk_proof;
144 place_holder_vk_fields = place_holder_vk->to_field_elements();
145 place_holder_vk_hash = place_holder_vk->hash();
146 }
147}
148
149} // namespace
150
160template <typename Flavor>
162 typename Flavor::CircuitBuilder& builder, const RecursionConstraint& input)
163 requires(IsRecursiveFlavor<Flavor> && IsUltraHonk<typename Flavor::NativeFlavor>)
164{
165 using Builder = typename Flavor::CircuitBuilder;
166 using bool_ct = bb::stdlib::bool_t<Builder>;
167 using RecursiveVerificationKey = Flavor::VerificationKey;
168 using RecursiveVKAndHash = Flavor::VKAndHash;
173
174 BB_ASSERT(input.proof_type == HONK || input.proof_type == HONK_ZK || HasIPAAccumulator<Flavor>);
175 BB_ASSERT_EQ(input.proof_type == ROLLUP_HONK || input.proof_type == ROOT_ROLLUP_HONK, HasIPAAccumulator<Flavor>);
176
177 // Construct an in-circuit representation of the verification key.
178 // For now, the v-key is a circuit constant and is fixed for the circuit.
179 // (We may need a separate recursion opcode for this to vary, or add more config witnesses to this opcode)
181
182 // Create circuit type for vkey hash.
183 auto vk_hash = field_ct<Builder>::from_witness_index(&builder, input.key_hash);
184
185 // Create witness indices for the proof with public inputs reinserted
186 std::vector<uint32_t> proof_indices =
189
190 // Recursion constraints come with a predicate (e.g. when the black-box call is done in an if conditional depending
191 // on a witness value in a Noir circuit) To keep the circuit constants (selectors and copy constraints) the same
192 // independent of value of the conditional, we create a place holder proof, vk and vk_hash and conditionally select
193 // between the two (in circuit) depending on the predicate value.
194 std::vector<fr> place_holder_vk_fields;
195 HonkProof place_holder_proof;
196
197 field_ct<Builder> place_holder_vk_hash;
198
199 place_holder_proof_and_vk<Flavor>(builder,
200 place_holder_vk_fields,
201 place_holder_proof,
202 place_holder_vk_hash,
203 input.proof.size(),
204 input.public_inputs.size(),
205 vk_fields,
206 proof_fields);
207
208 if (!input.predicate.is_constant) {
209 bool_ct predicate_witness = bool_ct::from_witness_index_unsafe(&builder, input.predicate.index);
210 stdlib::Proof<Builder> result_proof;
212 result_proof.reserve(proof_fields.size());
213 result_vk.reserve(vk_fields.size());
214 // Replace the proof by the placeholder proof in case the predicate is 1
215 for (uint32_t i = 0; i < proof_fields.size(); ++i) {
216 field_ct<Builder> place_holder_proof_witness =
217 field_ct<Builder>::from_witness(&builder, place_holder_proof[i]);
218 // Note: we have essentially created a dangling witness. However, this is not a problem given the
219 // context. When this witness is being conditionally assigned, we are in a scenario where we are using
220 // an place holder proof instead of the real proof. Hence, it is not a soundness issue if we use the
221 // specific place holder proof generated by construct_honk_proof_for_simple_circuit or another one. We
222 // manually unset the free_witness_tag to ensure the automatic tooling does not catch this as a free
223 // witness
224 place_holder_proof_witness.unset_free_witness_tag();
225 auto valid_proof =
226 field_ct<Builder>::conditional_assign(predicate_witness, proof_fields[i], place_holder_proof_witness);
227 result_proof.push_back(valid_proof);
228 }
229
230 // Replace the VK with the placeholder vk in case the predicate is 1
231 for (uint32_t i = 0; i < vk_fields.size(); ++i) {
232 field_ct<Builder> place_holder_vk_witness =
233 field_ct<Builder>::from_witness(&builder, place_holder_vk_fields[i]);
234 // Same as above. This witness being a free witness is not a soundness issue
235 place_holder_vk_witness.unset_free_witness_tag();
236 auto valid_vk =
237 field_ct<Builder>::conditional_assign(predicate_witness, vk_fields[i], place_holder_vk_witness);
238 result_vk.push_back(valid_vk);
239 }
241 predicate_witness, vk_hash, field_ct<Builder>::from_witness(&builder, place_holder_vk_hash.get_value()));
242 proof_fields = result_proof;
243 vk_fields = result_vk;
244 }
245
246 // Recursively verify the proof
247 auto vkey = std::make_shared<RecursiveVerificationKey>(vk_fields);
248 auto vk_and_hash = std::make_shared<RecursiveVKAndHash>(vkey, vk_hash);
249 RecursiveVerifier verifier(&builder, vk_and_hash);
250 UltraRecursiveVerifierOutput<Builder> verifier_output = verifier.template verify_proof<IO>(proof_fields);
251
252#ifndef NDEBUG
253 native_verification_debug<Flavor>(vkey, proof_fields);
254#endif
255
256 // TODO(https://github.com/AztecProtocol/barretenberg/issues/996): investigate whether
257 // assert_equal on public inputs is important, like what the plonk recursion constraint does.
258 return verifier_output;
259}
260
261#ifndef NDEBUG
265template <typename Flavor>
268{
269 using NativeVerificationKey = typename Flavor::NativeFlavor::VerificationKey;
271
272 auto native_vkey = std::make_shared<NativeVerificationKey>(vkey->get_value());
273 HonkProof native_proof = proof_fields.get_value();
274
275 HonkProof honk_proof;
276 HonkProof ipa_proof;
277 if constexpr (HasIPAAccumulator<Flavor>) {
278 honk_proof = HonkProof(native_proof.begin(), native_proof.end() - IPA_PROOF_LENGTH);
279 ipa_proof = HonkProof(native_proof.end() - IPA_PROOF_LENGTH, native_proof.end());
280 } else {
281 honk_proof = native_proof;
282 }
283
285 native_vkey, VerifierCommitmentKey<curve::Grumpkin>(1 << CONST_ECCVM_LOG_N));
286 bool is_valid_proof(native_verifier.template verify_proof<NativeIO>(honk_proof, ipa_proof));
287
288 info("===== HONK RECURSION CONSTRAINT DEBUG INFO =====");
289 std::string flavor;
290 if constexpr (HasIPAAccumulator<Flavor>) {
291 flavor = "Ultra Rollup Flavor";
292 } else if constexpr (Flavor::HasZK) {
293 flavor = "Ultra ZK Flavor";
294 } else {
295 flavor = "Ultra Flavor";
296 }
297 info("Flavor used: ", flavor);
298 info("Honk recursion constraint: input proof verifies natively: ", is_valid_proof ? "true" : "false");
299 info("===== END OF HONK RECURSION CONSTRAINT DEBUG INFO =====");
300}
301#endif
302
303#define INSTANTIATE_HONK_RECURSION_CONSTRAINT(Flavor) \
304 template HonkRecursionConstraintOutput<typename Flavor::CircuitBuilder> create_honk_recursion_constraints<Flavor>( \
305 typename Flavor::CircuitBuilder & builder, const RecursionConstraint& input);
306
312
313#undef INSTANTIATE_HONK_RECURSION_CONSTRAINT
314
315#ifndef NDEBUG
316#define INSTANTIATE_NATIVE_VERIFICATION_DEBUG(Flavor) \
317 template void native_verification_debug<Flavor>(const std::shared_ptr<typename Flavor::VerificationKey>, \
318 const bb::stdlib::Proof<typename Flavor::CircuitBuilder>&);
319
325
326#undef INSTANTIATE_NATIVE_VERIFICATION_DEBUG
327
328#endif
329
330} // namespace acir_format
#define BB_ASSERT(expression,...)
Definition assert.hpp:67
#define BB_ASSERT_EQ(actual, expected,...)
Definition assert.hpp:77
static std::vector< uint32_t > create_indices_for_reconstructed_proof(const std::vector< uint32_t > &proof_in, const std::vector< uint32_t > &public_inputs)
Reconstruct a bberg style proof from a acir style proof + public inputs.
Manages the data that is propagated on the public inputs of an application/function circuit.
The verification key is responsible for storing the commitments to the precomputed (non-witnessk) pol...
static constexpr bool HasZK
ECCVMCircuitBuilder CircuitBuilder
An object storing two EC points that represent the inputs to a pairing check.
The data that is propagated on the public inputs of a rollup circuit.
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.
A simple wrapper around a vector of stdlib field elements representing a proof.
Definition proof.hpp:19
HonkProof get_value() const
Definition proof.hpp:38
Implements boolean logic in-circuit.
Definition bool.hpp:59
static field_t from_witness_index(Builder *ctx, uint32_t witness_index)
Definition field.cpp:62
void unset_free_witness_tag() const
Unset the free witness flag for the field element's tag.
Definition field.hpp:356
bb::fr get_value() const
Given a := *this, compute its value given by a.v * a.mul + a.add.
Definition field.cpp:828
static field_t from_witness(Builder *ctx, const bb::fr &input)
Definition field.hpp:454
static field_t conditional_assign(const bool_t< Builder > &predicate, const field_t &lhs, const field_t &rhs)
Definition field.hpp:372
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.
void info(Args... args)
Definition log.hpp:75
AluTraceBuilder builder
Definition alu.test.cpp:124
ssize_t offset
Definition engine.cpp:36
Base class templates for structures that contain data parameterized by the fundamental polynomials of...
#define INSTANTIATE_NATIVE_VERIFICATION_DEBUG(Flavor)
#define INSTANTIATE_HONK_RECURSION_CONSTRAINT(Flavor)
HonkRecursionConstraintOutput< typename Flavor::CircuitBuilder > create_honk_recursion_constraints(typename Flavor::CircuitBuilder &builder, const RecursionConstraint &input)
Add constraints required to recursively verify an UltraHonk proof.
void native_verification_debug(const std::shared_ptr< typename Flavor::VerificationKey > vkey, const bb::stdlib::Proof< typename Flavor::CircuitBuilder > &proof_fields)
Natively verify the stdlib proof for debugging.
void create_dummy_vkey_and_proof(UltraCircuitBuilder &builder, size_t proof_size, size_t public_inputs_size, const std::vector< stdlib::field_t< UltraCircuitBuilder > > &key_fields, const std::vector< stdlib::field_t< UltraCircuitBuilder > > &proof_fields)
Creates a dummy vkey and proof object.
Entry point for Barretenberg command-line interface.
Definition api.hpp:5
std::vector< fr > HonkProof
Definition proof.hpp:15
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
RecursionConstraint struct contains information required to recursively verify a proof!
static std::vector< bb::stdlib::field_t< Builder > > fields_from_witnesses(Builder &builder, const std::vector< uint32_t > &witness_indices)
An object storing two EC points that represent the inputs to a pairing check.