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>;
33template <typename Builder> using PairingPoints = bb::stdlib::recursion::PairingPoints<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].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].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 bool has_valid_witness_assignments,
112 size_t proof_size,
113 size_t public_inputs_size,
114 const std::vector<field_ct<typename Flavor::CircuitBuilder>>& vk_fields,
115 const std::vector<field_ct<typename Flavor::CircuitBuilder>>& proof_fields)
116{
120 // Populate the key fields and proof fields with dummy values to prevent issues (e.g. points must be on curve).
121 if (!has_valid_witness_assignments) {
122 // In the constraint, the agg object public inputs are still contained in the proof. To get the 'raw' size of
123 // the proof and public_inputs we subtract and add the corresponding amount from the respective sizes.
124 size_t size_of_proof_with_no_pub_inputs = proof_size - IO::PUBLIC_INPUTS_SIZE;
125 size_t total_num_public_inputs = public_inputs_size + IO::PUBLIC_INPUTS_SIZE;
126 // Set a dummy vkey and proof in the builder
127 create_dummy_vkey_and_proof<Flavor>(
128 builder, size_of_proof_with_no_pub_inputs, total_num_public_inputs, vk_fields, proof_fields);
129 // Generate a mock place holder proof, vk and vk hash, to keep the circuit the same independent of whether a
130 // witness is provided or not.
131 uint32_t pub_inputs_offset = Flavor::NativeFlavor::has_zero_row ? 1 : 0;
132
133 // Generate mock honk vk
134 auto honk_vk = create_mock_honk_vk<typename Flavor::NativeFlavor, IO>(
135 1 << Flavor::VIRTUAL_LOG_N, pub_inputs_offset, public_inputs_size);
136 place_holder_vk_fields = honk_vk->to_field_elements();
137 place_holder_proof = create_mock_honk_proof<typename Flavor::NativeFlavor, IO>(public_inputs_size);
138 place_holder_vk_hash = honk_vk->hash();
139 } else {
140 // If we have an actual witness, the place holder proof and VK should be an actual verifiable honk proof and
141 // honk vk. Otherwise the recursive verification would fail if the predicate is false.
142 auto [place_holder_honk_proof, place_holder_vk] =
143 construct_honk_proof_for_simple_circuit<typename Flavor::NativeFlavor>(public_inputs_size);
144 place_holder_proof = place_holder_honk_proof;
145 place_holder_vk_fields = place_holder_vk->to_field_elements();
146 place_holder_vk_hash = place_holder_vk->hash();
147 }
148}
149
150} // namespace
151
161template <typename Flavor>
163 typename Flavor::CircuitBuilder& builder, const RecursionConstraint& input, bool has_valid_witness_assignments)
164 requires(IsRecursiveFlavor<Flavor> && IsUltraHonk<typename Flavor::NativeFlavor>)
165{
166 using Builder = typename Flavor::CircuitBuilder;
168 using RecursiveVerificationKey = Flavor::VerificationKey;
169 using RecursiveVKAndHash = Flavor::VKAndHash;
174
175 ASSERT(input.proof_type == HONK || input.proof_type == HONK_ZK || HasIPAAccumulator<Flavor>);
176 BB_ASSERT_EQ(input.proof_type == ROLLUP_HONK || input.proof_type == ROOT_ROLLUP_HONK, HasIPAAccumulator<Flavor>);
177
178 // Construct an in-circuit representation of the verification key.
179 // For now, the v-key is a circuit constant and is fixed for the circuit.
180 // (We may need a separate recursion opcode for this to vary, or add more config witnesses to this opcode)
182
183 // Create circuit type for vkey hash.
184 auto vk_hash = field_ct<Builder>::from_witness_index(&builder, input.key_hash);
185
186 // Create witness indices for the proof with public inputs reinserted
187 std::vector<uint32_t> proof_indices =
190
191 // Recursion constraints come with a predicate (e.g. when the black-box call is done in an if conditional depending
192 // on a witness value in a Noir circuit) To keep the circuit constants (selectors and copy constraints) the same
193 // independent of value of the conditional, we create a place holder proof, vk and vk_hash and conditionally select
194 // between the two (in circuit) depending on the predicate value.
195 std::vector<fr> place_holder_vk_fields;
196 HonkProof place_holder_proof;
197
198 field_ct<Builder> place_holder_vk_hash;
199
200 place_holder_proof_and_vk<Flavor>(builder,
201 place_holder_vk_fields,
202 place_holder_proof,
203 place_holder_vk_hash,
204 has_valid_witness_assignments,
205 input.proof.size(),
206 input.public_inputs.size(),
207 vk_fields,
208 proof_fields);
209
210 if (!input.predicate.is_constant) {
211 bool_ct predicate_witness = bool_ct::from_witness_index_unsafe(&builder, input.predicate.index);
212 stdlib::Proof<Builder> result_proof;
214 result_proof.reserve(proof_fields.size());
215 result_vk.reserve(vk_fields.size());
216 // Replace the proof by the placeholder proof in case the predicate is 1
217 for (uint32_t i = 0; i < proof_fields.size(); ++i) {
218 field_ct<Builder> place_holder_proof_witness =
219 field_ct<Builder>::from_witness(&builder, place_holder_proof[i]);
220 // Note: we have essentially created a dangling witness. However, this is not a problem given the
221 // context. When this witness is being conditionally assigned, we are in a scenario where we are using
222 // an place holder proof instead of the real proof. Hence, it is not a soundness issue if we use the
223 // specific place holder proof generated by construct_honk_proof_for_simple_circuit or another one. We
224 // manually unset the free_witness_tag to ensure the automatic tooling does not catch this as a free
225 // witness
226 place_holder_proof_witness.unset_free_witness_tag();
227 auto valid_proof =
228 field_ct<Builder>::conditional_assign(predicate_witness, proof_fields[i], place_holder_proof_witness);
229 result_proof.push_back(valid_proof);
230 }
231
232 // Replace the VK with the placeholder vk in case the predicate is 1
233 for (uint32_t i = 0; i < vk_fields.size(); ++i) {
234 field_ct<Builder> place_holder_vk_witness =
235 field_ct<Builder>::from_witness(&builder, place_holder_vk_fields[i]);
236 // Same as above. This witness being a free witness is not a soundness issue
237 place_holder_vk_witness.unset_free_witness_tag();
238 auto valid_vk =
239 field_ct<Builder>::conditional_assign(predicate_witness, vk_fields[i], place_holder_vk_witness);
240 result_vk.push_back(valid_vk);
241 }
243 predicate_witness, vk_hash, field_ct<Builder>::from_witness(&builder, place_holder_vk_hash.get_value()));
244 proof_fields = result_proof;
245 vk_fields = result_vk;
246 }
247
248 // Recursively verify the proof
249 auto vkey = std::make_shared<RecursiveVerificationKey>(vk_fields);
250 auto vk_and_hash = std::make_shared<RecursiveVKAndHash>(vkey, vk_hash);
251 RecursiveVerifier verifier(&builder, vk_and_hash);
252 UltraRecursiveVerifierOutput<Builder> verifier_output = verifier.template verify_proof<IO>(proof_fields);
253 // TODO(https://github.com/AztecProtocol/barretenberg/issues/996): investigate whether assert_equal on public inputs
254 // is important, like what the plonk recursion constraint does.
255 return verifier_output;
256}
257
260 const RecursionConstraint& input,
261 bool has_valid_witness_assignments);
262
265 const RecursionConstraint& input,
266 bool has_valid_witness_assignments);
267
270 const RecursionConstraint& input,
271 bool has_valid_witness_assignments);
272
275 const RecursionConstraint& input,
276 bool has_valid_witness_assignments);
277
280 const RecursionConstraint& input,
281 bool has_valid_witness_assignments);
282
283} // namespace acir_format
#define BB_ASSERT_EQ(actual, expected,...)
Definition assert.hpp:88
#define ASSERT(expression,...)
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.
The verification key is responsible for storing the commitments to the precomputed (non-witness) poly...
MegaCircuitBuilder CircuitBuilder
static constexpr size_t VIRTUAL_LOG_N
static constexpr bool has_zero_row
An object storing two bn254 points that represent the inputs to a pairing check.
The recursive counterpart to the "native" Ultra flavor.
The recursive counterpart to the "native" UltraRollupFlavor.
The recursive counterpart to the Ultra flavor with ZK.
A simple wrapper around a vector of stdlib field elements representing a proof.
Definition proof.hpp:19
Implements boolean logic in-circuit.
Definition bool.hpp:59
static bool_t from_witness_index_unsafe(Builder *ctx, uint32_t witness_index)
Create a bool_t from a witness index that is known to contain a constrained bool value.
Definition bool.cpp:96
static field_t from_witness_index(Builder *ctx, uint32_t witness_index)
Definition field.cpp:61
static field_t conditional_assign(const bool_t< Builder > &predicate, const field_t &lhs, const field_t &rhs)
If predicate == true then return lhs, else return rhs.
Definition field.cpp:886
void unset_free_witness_tag() const
Unset the free witness flag for the field element's tag.
Definition field.hpp:343
bb::fr get_value() const
Given a := *this, compute its value given by a.v * a.mul + a.add.
Definition field.cpp:829
static field_t from_witness(Builder *ctx, const bb::fr &input)
Definition field.hpp:432
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.
AluTraceBuilder builder
Definition alu.test.cpp:123
ssize_t offset
Definition engine.cpp:36
Base class templates for structures that contain data parameterized by the fundamental polynomials of...
void create_dummy_vkey_and_proof(Builder &builder, size_t proof_size, size_t public_inputs_size, const std::vector< field_ct > &key_fields, const std::vector< field_ct > &proof_fields)
Creates a dummy vkey and proof object.
HonkRecursionConstraintOutput< typename Flavor::CircuitBuilder > create_honk_recursion_constraints(typename Flavor::CircuitBuilder &builder, const RecursionConstraint &input, bool has_valid_witness_assignments)
Add constraints required to recursively verify an UltraHonk proof.
Entry point for Barretenberg command-line interface.
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.