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
20
21#include <cstddef>
22
23namespace acir_format {
24
25using namespace bb;
26using namespace bb::stdlib::recursion::honk;
27
28template <typename Flavor, typename IO>
31 requires(IsRecursiveFlavor<Flavor> && IsUltraHonk<typename Flavor::NativeFlavor>)
32{
35 using bool_ct = bb::stdlib::bool_t<Builder>;
36 using RecursiveVerificationKey = Flavor::VerificationKey;
37 using RecursiveVKAndHash = Flavor::VKAndHash;
38 using RecursiveVerifier = bb::UltraVerifier_<Flavor, IO>;
39 using NativeFlavor = Flavor::NativeFlavor;
40 using NativeVerificationKey = NativeFlavor::VerificationKey;
41
42 BB_ASSERT(input.proof_type == HONK || input.proof_type == HONK_ZK || input.proof_type == ROLLUP_HONK ||
43 input.proof_type == ROOT_ROLLUP_HONK,
44 "create_honk_recursion_constraints: Only HONK, HONK_ZK, ROLLUP_HONK, ROOT_ROLLUP_HONK proof types are "
45 "supported.");
46 BB_ASSERT_EQ(input.proof_type == ROLLUP_HONK || input.proof_type == ROOT_ROLLUP_HONK,
47 IO::HasIPA,
48 "create_honk_recursion_constraints: ROLLUP_HONK and ROOT_ROLLUP_HONK must be recursively verified "
49 "using an IO type with HasIPA=true.");
50
51 // Step 1.
52 // Construct in-circuit representations of the recursion data
54 field_ct vk_hash = field_ct::from_witness_index(&builder, input.key_hash);
55 std::vector<field_ct> proof_fields =
56 fields_from_witnesses(builder, add_public_inputs_to_proof(input.proof, input.public_inputs));
57 bool_ct predicate(to_field_ct(input.predicate, builder)); // Constructor enforces predicate = 0 or 1
58
59 // Determine the stdlib IO type for creating mock proofs/VKs (need add_default method)
60 using StdlibIO = std::conditional_t<IO::HasIPA,
63
64 // Construct a Honk proof and vk with the correct number of public inputs.
65 // If we are in a write vk scenario, the proof and vk are not necessarily valid
66 const auto [honk_proof_to_be_set,
67 honk_vk_to_be_set] = [&]() -> std::pair<HonkProof, std::shared_ptr<NativeVerificationKey>> {
68 if (builder.is_write_vk_mode()) {
69 return std::make_pair(
70 create_mock_honk_proof<NativeFlavor, StdlibIO>(/*acir_public_inputs_size=*/input.public_inputs.size()),
71 create_mock_honk_vk<NativeFlavor, StdlibIO>(
72 /*dyadic_size=*/1 << NativeFlavor::VIRTUAL_LOG_N,
73 /*acir_public_inputs_size=*/input.public_inputs.size()));
74 }
75
76 return construct_arbitrary_valid_honk_proof_and_vk<NativeFlavor, StdlibIO>(
77 /*acir_public_inputs_size=*/input.public_inputs.size());
78 }();
79
80 // Step 2.
81 if (builder.is_write_vk_mode()) {
82 // Set honk vk in builder
83 populate_fields(builder, vk_fields, honk_vk_to_be_set->to_field_elements());
84
85 // Set honk proof in builder
86 populate_fields(builder, proof_fields, honk_proof_to_be_set);
87 }
88
89 // Step 3.
90 if (!predicate.is_constant()) {
91 // If the predicate is a witness, we conditionally assign a valid vk, proof and vk hash so that verification
92 // succeeds. Note: in doing this, we create some new witnesses that are only used in the conditional assignment.
93 // It would be optimal to hard-code these values in the selectors, but due to the randomness needed to generate
94 // valid ZK proofs, we cannot do that without adding a dependency of the VKs on the witness values. Note that
95 // the new witnesses are used only in the recursive verification when the predicate is set to true, so they
96 // don't create a soundness issue and can be filled with anything - as long as they contain a valid vk, proof
97 // and vk hash
98 for (auto [vk_witness, vk_element] : zip_view(vk_fields, honk_vk_to_be_set->to_field_elements())) {
99 field_ct valid_vk_witness = field_ct::from_witness(&builder, vk_element);
100 valid_vk_witness.unset_free_witness_tag(); // Avoid tooling catching this as a free witness
101 vk_witness = field_ct::conditional_assign(predicate, vk_witness, valid_vk_witness);
102 }
103
104 for (auto [proof_witness, proof_element] : zip_view(proof_fields, honk_proof_to_be_set)) {
105 field_ct valid_proof_witness = field_ct::from_witness(&builder, proof_element);
106 valid_proof_witness.unset_free_witness_tag(); // Avoid tooling catching this as a free witness
107 proof_witness = field_ct::conditional_assign(predicate, proof_witness, valid_proof_witness);
108 }
109
110 field_ct valid_vk_hash = field_ct::from_witness(&builder, honk_vk_to_be_set->hash());
111 valid_vk_hash.unset_free_witness_tag();
112 vk_hash = field_ct::conditional_assign(predicate, vk_hash, valid_vk_hash);
113 }
114
115 // Recursively verify the proof
116 auto vkey = std::make_shared<RecursiveVerificationKey>(vk_fields);
117 auto vk_and_hash = std::make_shared<RecursiveVKAndHash>(vkey, vk_hash);
118 RecursiveVerifier verifier(vk_and_hash);
119 UltraRecursiveVerifierOutput<Builder> verifier_output = verifier.verify_proof(proof_fields);
120
121#ifndef NDEBUG
122 native_verification_debug<Flavor, IO>(vkey, vk_hash.get_value(), proof_fields);
123#endif
124
125 return verifier_output;
126}
127
128#ifndef NDEBUG
132template <typename Flavor, typename IO>
134 const typename Flavor::NativeFlavor::FF vkey_hash,
136{
137 using NativeVerificationKey = typename Flavor::NativeFlavor::VerificationKey;
138 // Use RollupIO for native verification if IO::HasIPA is true, otherwise DefaultIO
140
141 auto native_vkey = std::make_shared<NativeVerificationKey>(vkey->get_value());
142 auto native_vk_and_hash = std::make_shared<typename Flavor::NativeFlavor::VKAndHash>(native_vkey, vkey_hash);
143 const bool vkey_and_hash_match = native_vkey->hash() == vkey_hash;
144 HonkProof native_proof = proof_fields.get_value();
145
146 UltraVerifier_<typename Flavor::NativeFlavor, NativeIO> native_verifier(native_vk_and_hash);
147 bool is_valid_proof = native_verifier.verify_proof(native_proof).result;
148
149 info("===== HONK RECURSION CONSTRAINT DEBUG INFO =====");
150 std::string flavor;
151 if constexpr (IO::HasIPA) {
152 flavor = "Ultra Flavor with IPA (Rollup)";
153 } else if constexpr (Flavor::HasZK) {
154 flavor = "Ultra ZK Flavor";
155 } else {
156 flavor = "Ultra Flavor";
157 }
158 info("Flavor used: ", flavor);
159 info("Honk recursion constraint: native vk hash matches witness vk hash: ", vkey_and_hash_match ? "true" : "false");
160 info("Honk recursion constraint: input proof verifies natively: ", is_valid_proof ? "true" : "false");
161 info("===== END OF HONK RECURSION CONSTRAINT DEBUG INFO =====");
162}
163#endif
164
165#define INSTANTIATE_HONK_RECURSION_CONSTRAINT(Flavor, IO) \
166 template HonkRecursionConstraintOutput<typename Flavor::CircuitBuilder> \
167 create_honk_recursion_constraints<Flavor, IO>(typename Flavor::CircuitBuilder & builder, \
168 const RecursionConstraint& input);
169
176 stdlib::recursion::honk::DefaultIO<MegaCircuitBuilder>)
178 stdlib::recursion::honk::DefaultIO<UltraCircuitBuilder>)
179
180#undef INSTANTIATE_HONK_RECURSION_CONSTRAINT
181
182#ifndef NDEBUG
183#define INSTANTIATE_NATIVE_VERIFICATION_DEBUG(Flavor, IO) \
184 template void native_verification_debug<Flavor, IO>(const std::shared_ptr<typename Flavor::VerificationKey>, \
185 const typename Flavor::NativeFlavor::FF vkey_hash, \
186 const bb::stdlib::Proof<typename Flavor::CircuitBuilder>&);
187
194 stdlib::recursion::honk::DefaultIO<MegaCircuitBuilder>)
196 stdlib::recursion::honk::DefaultIO<UltraCircuitBuilder>)
197
198#undef INSTANTIATE_NATIVE_VERIFICATION_DEBUG
199
200#endif
201
202} // 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:226
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:368
bb::fr get_value() const
Given a := *this, compute its value given by a.v * a.mul + a.add.
Definition field.cpp:836
static field_t from_witness(Builder *ctx, const bb::fr &input)
Definition field.hpp:466
static field_t conditional_assign(const bool_t< Builder > &predicate, const field_t &lhs, const field_t &rhs)
Definition field.hpp:384
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.
Output type for recursive ultra verification.