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>
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;
42 using RecursiveVerifier = bb::UltraVerifier_<Flavor, IO>;
43 using NativeFlavor = Flavor::NativeFlavor;
44 using NativeVerificationKey = NativeFlavor::VerificationKey;
45
46 BB_ASSERT(input.proof_type == HONK || input.proof_type == HONK_ZK || input.proof_type == ROLLUP_HONK ||
47 input.proof_type == ROOT_ROLLUP_HONK,
48 "create_honk_recursion_constraints: Only HONK, HONK_ZK, ROLLUP_HONK, ROOT_ROLLUP_HONK proof types are "
49 "supported.");
50 BB_ASSERT_EQ(input.proof_type == ROLLUP_HONK || input.proof_type == ROOT_ROLLUP_HONK,
51 HasIPAAccumulator<Flavor>,
52 "create_honk_recursion_constraints: ROLLUP_HONK and ROOT_ROLLUP_HONK must be recursively verified "
53 "using a Flavor with IPA accumulator.");
54
55 // Step 1.
56 // Construct in-circuit representations of the recursion data
58 field_ct vk_hash = field_ct::from_witness_index(&builder, input.key_hash);
59 std::vector<field_ct> proof_fields =
60 fields_from_witnesses(builder, add_public_inputs_to_proof(input.proof, input.public_inputs));
61 bool_ct predicate(to_field_ct(input.predicate, builder)); // Constructor enforces predicate = 0 or 1
62
63 // Construct a Honk proof and vk with the correct number of public inputs.
64 // If we are in a write vk scenario, the proof and vk are not necessarily valid
65 const auto [honk_proof_to_be_set,
66 honk_vk_to_be_set] = [&]() -> std::pair<HonkProof, std::shared_ptr<NativeVerificationKey>> {
67 if (builder.is_write_vk_mode()) {
68 return std::make_pair(
69 create_mock_honk_proof<NativeFlavor, IO>(/*acir_public_inputs_size=*/input.public_inputs.size()),
70 create_mock_honk_vk<NativeFlavor, IO>(
71 /*dyadic_size=*/1 << NativeFlavor::VIRTUAL_LOG_N,
72 /*pub_inputs_offset=*/NativeFlavor::has_zero_row ? 1 : 0,
73 /*acir_public_inputs_size=*/input.public_inputs.size()));
74 }
75
76 return construct_arbitrary_valid_honk_proof_and_vk<NativeFlavor>(
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>(vkey, vk_hash.get_value(), proof_fields);
123#endif
124
125 return verifier_output;
126}
127
128#ifndef NDEBUG
132template <typename Flavor>
134 const typename Flavor::NativeFlavor::FF vkey_hash,
136{
137 using NativeVerificationKey = typename Flavor::NativeFlavor::VerificationKey;
139
140 auto native_vkey = std::make_shared<NativeVerificationKey>(vkey->get_value());
141 auto native_vk_and_hash = std::make_shared<typename Flavor::NativeFlavor::VKAndHash>(native_vkey, vkey_hash);
142 const bool vkey_and_hash_match = native_vkey->hash() == vkey_hash;
143 HonkProof native_proof = proof_fields.get_value();
144
145 UltraVerifier_<typename Flavor::NativeFlavor, NativeIO> native_verifier(native_vk_and_hash);
146 bool is_valid_proof = native_verifier.verify_proof(native_proof).result;
147
148 info("===== HONK RECURSION CONSTRAINT DEBUG INFO =====");
149 std::string flavor;
150 if constexpr (HasIPAAccumulator<Flavor>) {
151 flavor = "Ultra Rollup Flavor";
152 } else if constexpr (Flavor::HasZK) {
153 flavor = "Ultra ZK Flavor";
154 } else {
155 flavor = "Ultra Flavor";
156 }
157 info("Flavor used: ", flavor);
158 info("Honk recursion constraint: native vk hash matches witness vk hash: ", vkey_and_hash_match ? "true" : "false");
159 info("Honk recursion constraint: input proof verifies natively: ", is_valid_proof ? "true" : "false");
160 info("===== END OF HONK RECURSION CONSTRAINT DEBUG INFO =====");
161}
162#endif
163
164#define INSTANTIATE_HONK_RECURSION_CONSTRAINT(Flavor) \
165 template HonkRecursionConstraintOutput<typename Flavor::CircuitBuilder> create_honk_recursion_constraints<Flavor>( \
166 typename Flavor::CircuitBuilder & builder, const RecursionConstraint& input);
167
173
174#undef INSTANTIATE_HONK_RECURSION_CONSTRAINT
175
176#ifndef NDEBUG
177#define INSTANTIATE_NATIVE_VERIFICATION_DEBUG(Flavor) \
178 template void native_verification_debug<Flavor>(const std::shared_ptr<typename Flavor::VerificationKey>, \
179 const typename Flavor::NativeFlavor::FF vkey_hash, \
180 const bb::stdlib::Proof<typename Flavor::CircuitBuilder>&);
181
187
188#undef INSTANTIATE_NATIVE_VERIFICATION_DEBUG
189
190#endif
191
192} // namespace acir_format
#define BB_ASSERT(expression,...)
Definition assert.hpp:80
#define BB_ASSERT_EQ(actual, expected,...)
Definition assert.hpp:93
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
typename Curve::ScalarField FF
ECCVMCircuitBuilder CircuitBuilder
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.
Output verify_proof(const Proof &proof)
Perform ultra verification.
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
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:89
AluTraceBuilder builder
Definition alu.test.cpp:124
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)
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.
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
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.
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
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.