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: completed, auditors: [Federico], commit: 8b4e1279ef130eeb18bce9ce2a9f0fa39a243697}
3// external_1: { status: not started, auditors: [], commit: }
4// external_2: { status: not started, auditors: [], commit: }
5// =====================
6
21
22#include <cstddef>
23
24namespace acir_format {
25
26using namespace bb;
27using namespace bb::stdlib::recursion::honk;
28
29template <typename Flavor, typename IO>
32 requires(IsRecursiveFlavor<Flavor> && IsUltraHonk<typename Flavor::NativeFlavor>)
33{
36 using bool_ct = bb::stdlib::bool_t<Builder>;
37 using RecursiveVerificationKey = Flavor::VerificationKey;
38 using RecursiveVKAndHash = Flavor::VKAndHash;
39 using RecursiveVerifier = bb::UltraVerifier_<Flavor, IO>;
40 using NativeFlavor = Flavor::NativeFlavor;
41 using NativeVerificationKey = NativeFlavor::VerificationKey;
42
43 BB_ASSERT(input.proof_type == HONK || input.proof_type == HONK_ZK || input.proof_type == ROLLUP_HONK ||
44 input.proof_type == ROOT_ROLLUP_HONK,
45 "create_honk_recursion_constraints: Only HONK, HONK_ZK, ROLLUP_HONK, ROOT_ROLLUP_HONK proof types are "
46 "supported.");
47 BB_ASSERT_EQ(input.proof_type == ROLLUP_HONK || input.proof_type == ROOT_ROLLUP_HONK,
48 IO::HasIPA,
49 "create_honk_recursion_constraints: ROLLUP_HONK and ROOT_ROLLUP_HONK must be recursively verified "
50 "using an IO type with HasIPA=true.");
51
52 BB_ASSERT_EQ(input.proof.size(),
53 ProofLength::Honk<NativeFlavor>::template expected_proof_size<IO>(NativeFlavor::VIRTUAL_LOG_N),
54 "create_honk_recursion_constraints: ACIR proof size mismatch.");
55
56 // Step 1.
57 // Construct in-circuit representations of the recursion data
59 field_ct vk_hash = field_ct::from_witness_index(&builder, input.key_hash);
60 std::vector<field_ct> proof_fields =
61 fields_from_witnesses(builder, add_public_inputs_to_proof(input.proof, input.public_inputs));
62 bool_ct predicate(to_field_ct(input.predicate, builder)); // Constructor enforces predicate = 0 or 1
63
64 // Determine the stdlib IO type for creating mock proofs/VKs (need add_default method)
65 using StdlibIO = std::conditional_t<IO::HasIPA,
68
69 // Construct a Honk proof and vk with the correct number of public inputs.
70 // If we are in a write vk scenario, the proof and vk are not necessarily valid
71 const auto [honk_proof_to_be_set,
72 honk_vk_to_be_set] = [&]() -> std::pair<HonkProof, std::shared_ptr<NativeVerificationKey>> {
73 if (builder.is_write_vk_mode()) {
74 return std::make_pair(
75 create_mock_honk_proof<NativeFlavor, StdlibIO>(/*acir_public_inputs_size=*/input.public_inputs.size()),
76 create_mock_honk_vk<NativeFlavor, StdlibIO>(
77 /*dyadic_size=*/1 << NativeFlavor::VIRTUAL_LOG_N,
78 /*acir_public_inputs_size=*/input.public_inputs.size()));
79 }
80
81 return construct_arbitrary_valid_honk_proof_and_vk<NativeFlavor, StdlibIO>(
82 /*acir_public_inputs_size=*/input.public_inputs.size());
83 }();
84
85 // Step 2.
86 if (builder.is_write_vk_mode()) {
87 // Set honk vk in builder
88 populate_fields(builder, vk_fields, honk_vk_to_be_set->to_field_elements());
89
90 // Set honk proof in builder
91 populate_fields(builder, proof_fields, honk_proof_to_be_set);
92 }
93
94 // Step 3.
95 if (!predicate.is_constant()) {
96 // If the predicate is a witness, we conditionally assign a valid vk, proof and vk hash so that verification
97 // succeeds. Note: in doing this, we create some new witnesses that are only used in the conditional assignment.
98 // It would be optimal to hard-code these values in the selectors, but due to the randomness needed to generate
99 // valid ZK proofs, we cannot do that without adding a dependency of the VKs on the witness values. Note that
100 // the new witnesses are used only in the recursive verification when the predicate is set to true, so they
101 // don't create a soundness issue and can be filled with anything - as long as they contain a valid vk, proof
102 // and vk hash
103 for (auto [vk_witness, vk_element] : zip_view(vk_fields, honk_vk_to_be_set->to_field_elements())) {
104 field_ct valid_vk_witness = field_ct::from_witness(&builder, vk_element);
105 valid_vk_witness.unset_free_witness_tag(); // Avoid tooling catching this as a free witness
106 vk_witness = field_ct::conditional_assign(predicate, vk_witness, valid_vk_witness);
107 }
108
109 for (auto [proof_witness, proof_element] : zip_view(proof_fields, honk_proof_to_be_set)) {
110 field_ct valid_proof_witness = field_ct::from_witness(&builder, proof_element);
111 valid_proof_witness.unset_free_witness_tag(); // Avoid tooling catching this as a free witness
112 proof_witness = field_ct::conditional_assign(predicate, proof_witness, valid_proof_witness);
113 }
114
115 field_ct valid_vk_hash = field_ct::from_witness(&builder, honk_vk_to_be_set->hash());
116 valid_vk_hash.unset_free_witness_tag();
117 vk_hash = field_ct::conditional_assign(predicate, vk_hash, valid_vk_hash);
118 }
119
120 // Recursively verify the proof
121 auto vkey = std::make_shared<RecursiveVerificationKey>(vk_fields);
122 auto vk_and_hash = std::make_shared<RecursiveVKAndHash>(vkey, vk_hash);
123 RecursiveVerifier verifier(vk_and_hash);
124 UltraRecursiveVerifierOutput<Builder> verifier_output = verifier.verify_proof(proof_fields);
125
126#ifndef NDEBUG
127 native_verification_debug<Flavor, IO>(vkey, vk_hash.get_value(), proof_fields);
128#endif
129
130 return verifier_output;
131}
132
133#ifndef NDEBUG
137template <typename Flavor, typename IO>
139 const typename Flavor::NativeFlavor::FF vkey_hash,
141{
142 using NativeVerificationKey = typename Flavor::NativeFlavor::VerificationKey;
143 // Use RollupIO for native verification if IO::HasIPA is true, otherwise DefaultIO
145
146 auto native_vkey = std::make_shared<NativeVerificationKey>(vkey->get_value());
147 auto native_vk_and_hash = std::make_shared<typename Flavor::NativeFlavor::VKAndHash>(native_vkey, vkey_hash);
148 const bool vkey_and_hash_match = native_vkey->hash() == vkey_hash;
149 HonkProof native_proof = proof_fields.get_value();
150
151 UltraVerifier_<typename Flavor::NativeFlavor, NativeIO> native_verifier(native_vk_and_hash);
152 bool is_valid_proof = native_verifier.verify_proof(native_proof).result;
153
154 info("===== HONK RECURSION CONSTRAINT DEBUG INFO =====");
155 std::string flavor;
156 if constexpr (IO::HasIPA) {
157 flavor = "Ultra Flavor with IPA (Rollup)";
158 } else if constexpr (Flavor::HasZK) {
159 flavor = "Ultra ZK Flavor";
160 } else {
161 flavor = "Ultra Flavor";
162 }
163 info("Flavor used: ", flavor);
164 info("Honk recursion constraint: native vk hash matches witness vk hash: ", vkey_and_hash_match ? "true" : "false");
165 info("Honk recursion constraint: input proof verifies natively: ", is_valid_proof ? "true" : "false");
166 info("===== END OF HONK RECURSION CONSTRAINT DEBUG INFO =====");
167}
168#endif
169
170#define INSTANTIATE_HONK_RECURSION_CONSTRAINT(Flavor, IO) \
171 template HonkRecursionConstraintOutput<typename Flavor::CircuitBuilder> \
172 create_honk_recursion_constraints<Flavor, IO>(typename Flavor::CircuitBuilder & builder, \
173 const RecursionConstraint& input);
174
181 stdlib::recursion::honk::DefaultIO<MegaCircuitBuilder>)
183 stdlib::recursion::honk::DefaultIO<UltraCircuitBuilder>)
184
185#undef INSTANTIATE_HONK_RECURSION_CONSTRAINT
186
187#ifndef NDEBUG
188#define INSTANTIATE_NATIVE_VERIFICATION_DEBUG(Flavor, IO) \
189 template void native_verification_debug<Flavor, IO>(const std::shared_ptr<typename Flavor::VerificationKey>, \
190 const typename Flavor::NativeFlavor::FF vkey_hash, \
191 const bb::stdlib::Proof<typename Flavor::CircuitBuilder>&);
192
199 stdlib::recursion::honk::DefaultIO<MegaCircuitBuilder>)
201 stdlib::recursion::honk::DefaultIO<UltraCircuitBuilder>)
202
203#undef INSTANTIATE_NATIVE_VERIFICATION_DEBUG
204
205#endif
206
207} // namespace acir_format
#define BB_ASSERT(expression,...)
Definition assert.hpp:70
#define BB_ASSERT_EQ(actual, expected,...)
Definition assert.hpp:83
Manages the data that is propagated on the public inputs of an application/function circuit.
ECCVMCircuitBuilder CircuitBuilder
The recursive counterpart to the "native" Ultra flavor.
Output verify_proof(const Proof &proof)
Perform ultra verification.
The recursive counterpart to UltraZKFlavor.
AvmFlavorSettings::FF FF
Definition flavor.hpp:43
FixedVKAndHash_< PrecomputedEntities< Commitment >, FF, typename constraining::AvmHardCodedVKAndHash > VerificationKey
Verification key of the AVM. It is fixed and reconstructed from precomputed values.
Definition flavor.hpp:227
static constexpr bool HasZK
Definition flavor.hpp:57
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:60
static field_t from_witness_index(Builder *ctx, uint32_t witness_index)
Definition field.cpp:67
void unset_free_witness_tag() const
Unset the free witness flag for the field element's tag.
Definition field.hpp:369
bb::fr get_value() const
Given a := *this, compute its value given by a.v * a.mul + a.add.
Definition field.cpp:838
static field_t from_witness(Builder *ctx, const bb::fr &input)
Definition field.hpp:467
static field_t conditional_assign(const bool_t< Builder > &predicate, const field_t &lhs, const field_t &rhs)
Definition field.hpp:385
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 info(...)
Definition log.hpp:93
AluTraceBuilder builder
Definition alu.test.cpp:124
Base class templates shared across Honk flavors.
stdlib::recursion::honk::DefaultIO< MegaCircuitBuilder > INSTANTIATE_NATIVE_VERIFICATION_DEBUG(UltraZKRecursiveFlavor_< MegaCircuitBuilder >, stdlib::recursion::honk::DefaultIO< MegaCircuitBuilder >) INSTANTIATE_NATIVE_VERIFICATION_DEBUG(UltraZKRecursiveFlavor_< UltraCircuitBuilder >
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
void native_verification_debug(const std::shared_ptr< typename Flavor::VerificationKey > vkey, const typename Flavor::NativeFlavor::FF vkey_hash, const bb::stdlib::Proof< typename Flavor::CircuitBuilder > &proof_fields)
Natively verify the stdlib proof for debugging.
std::vector< uint32_t > add_public_inputs_to_proof(const std::vector< uint32_t > &proof_in, const std::vector< uint32_t > &public_inputs)
Reconstruct a barretenberg style proof from an ACIR style proof + public inputs.
Definition utils.cpp:40
HonkRecursionConstraintOutput< typename Flavor::CircuitBuilder > create_honk_recursion_constraints(typename Flavor::CircuitBuilder &builder, const RecursionConstraint &input)
Add to the builder the constraints to recursively verify a Honk proof.
stdlib::recursion::honk::DefaultIO< MegaCircuitBuilder > INSTANTIATE_HONK_RECURSION_CONSTRAINT(UltraZKRecursiveFlavor_< MegaCircuitBuilder >, stdlib::recursion::honk::DefaultIO< MegaCircuitBuilder >) INSTANTIATE_HONK_RECURSION_CONSTRAINT(UltraZKRecursiveFlavor_< UltraCircuitBuilder >
bb::stdlib::field_t< Builder > to_field_ct(const WitnessOrConstant< typename Builder::FF > &input, Builder &builder)
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.
Full Honk proof layout (used by UltraVerifier).
Output type for recursive ultra verification.