Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
bbapi_ultra_honk.cpp
Go to the documentation of this file.
14
15namespace bb::bbapi {
16
18{
19 return acir_format::ProgramMetadata{ .has_ipa_claim = IO::HasIPA };
20}
21
22template <typename Flavor, typename IO, typename Circuit = typename Flavor::CircuitBuilder>
23Circuit _compute_circuit(std::vector<uint8_t>&& bytecode, std::vector<uint8_t>&& witness)
24{
25 const acir_format::ProgramMetadata metadata = _create_program_metadata<IO>();
28 };
29
30 if (!witness.empty()) {
31 program.witness = acir_format::witness_buf_to_witness_vector(std::move(witness));
32 }
33 return acir_format::create_circuit<Circuit>(program, metadata);
34}
35
36template <typename Flavor, typename IO>
38 std::vector<uint8_t>&& witness)
39{
40 // Measure function time and debug print
41 auto initial_time = std::chrono::high_resolution_clock::now();
42 typename Flavor::CircuitBuilder builder = _compute_circuit<Flavor, IO>(std::move(bytecode), std::move(witness));
44 auto final_time = std::chrono::high_resolution_clock::now();
45 auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(final_time - initial_time);
46 info("CircuitProve: Proving key computed in ", duration.count(), " ms");
47
48 // Validate consistency between IO type and IPA proof presence
49 // IO::HasIPA indicates the circuit type requires IPA accumulation (rollup circuits)
50 // prover_instance->ipa_proof contains the actual IPA proof data from the circuit
51 if constexpr (IO::HasIPA) {
52 BB_ASSERT(!prover_instance->ipa_proof.empty(),
53 "RollupIO circuit expected IPA proof but none was provided. "
54 "Ensure the circuit includes IPA accumulation data.");
55 } else {
56 BB_ASSERT(prover_instance->ipa_proof.empty(),
57 "Non-rollup circuit should not have IPA proof. "
58 "Use ipa_accumulation=true in settings for rollup circuits.");
59 }
60
61 return prover_instance;
62}
63
64template <typename Flavor, typename IO>
65bool _verify(const std::vector<uint8_t>& vk_bytes,
66 const std::vector<uint256_t>& public_inputs,
67 const std::vector<uint256_t>& proof)
68{
70 using VKAndHash = typename Flavor::VKAndHash;
71 using Verifier = UltraVerifier_<Flavor, IO>;
72
73 // Validate VK size upfront before deserialization
74 const size_t expected_vk_size = VerificationKey::calc_num_data_types() * sizeof(bb::fr);
75 if (vk_bytes.size() != expected_vk_size) {
76 info(
77 "Proof verification failed: invalid VK size. Expected ", expected_vk_size, " bytes, got ", vk_bytes.size());
78 return false;
79 }
80
81 std::shared_ptr<VerificationKey> vk = std::make_shared<VerificationKey>(from_buffer<VerificationKey>(vk_bytes));
82 auto vk_and_hash = std::make_shared<VKAndHash>(vk);
83 Verifier verifier{ vk_and_hash };
84
85 // Validate proof size
86 const size_t log_n = verifier.compute_log_n();
87 const size_t expected_size = ProofLength::Honk<Flavor>::template expected_proof_size<IO>(log_n);
88 if (proof.size() != expected_size) {
89 info("Proof verification failed: invalid proof size. Expected ", expected_size, ", got ", proof.size());
90 return false;
91 }
92
93 auto complete_proof = concatenate_proof<Flavor>(public_inputs, proof);
94 bool verified = verifier.verify_proof(complete_proof).result;
95
96 if (verified) {
97 info("Proof verified successfully");
98 } else {
99 info("Proof verification failed");
100 }
101
102 return verified;
103}
104
105template <typename Flavor, typename IO>
107 std::vector<uint8_t>&& witness,
108 std::vector<uint8_t>&& vk_bytes)
109{
110 using Proof = typename Flavor::Transcript::Proof;
112
113 auto prover_instance = _compute_prover_instance<Flavor, IO>(std::move(bytecode), std::move(witness));
114
115 // Create or deserialize VK
116 std::shared_ptr<VerificationKey> vk;
117 if (vk_bytes.empty()) {
118 info("WARNING: computing verification key while proving. Pass in a precomputed vk for better performance.");
119 vk = std::make_shared<VerificationKey>(prover_instance->get_precomputed());
120 } else {
121 validate_vk_size<VerificationKey>(vk_bytes);
122 vk = std::make_shared<VerificationKey>(from_buffer<VerificationKey>(vk_bytes));
123 }
124
125 // Construct proof
126 UltraProver_<Flavor> prover{ prover_instance, vk };
127 Proof full_proof = prover.construct_proof();
128
129 // Compute where to split (inner public inputs vs everything else)
130 size_t num_public_inputs = prover.num_public_inputs();
131 BB_ASSERT_GTE(num_public_inputs, IO::PUBLIC_INPUTS_SIZE, "Public inputs should contain the expected IO structure.");
132 size_t num_inner_public_inputs = num_public_inputs - IO::PUBLIC_INPUTS_SIZE;
133
134 // Optimization: if vk not provided, include it in response
135 CircuitComputeVk::Response vk_response;
136 if (vk_bytes.empty()) {
137 vk_response = { .bytes = to_buffer(*vk), .fields = vk_to_uint256_fields(*vk), .hash = to_buffer(vk->hash()) };
138 }
139
140 // Split proof: inner public inputs at front, rest is the "proof"
141 CircuitProve::Response response{
143 std::vector<uint256_t>{ full_proof.begin(),
144 full_proof.begin() + static_cast<std::ptrdiff_t>(num_inner_public_inputs) },
145 .proof = std::vector<uint256_t>{ full_proof.begin() + static_cast<std::ptrdiff_t>(num_inner_public_inputs),
146 full_proof.end() },
147 .vk = std::move(vk_response)
148 };
149
150 // Sanity-check the generated proof
151 if (!_verify<Flavor, IO>(to_buffer(*vk), response.public_inputs, response.proof)) {
152 throw_or_abort("Failed to verify the generated proof!");
153 }
154
155 return response;
156}
157
159{
160 BB_BENCH_NAME(MSGPACK_SCHEMA_NAME);
161 return dispatch_by_settings(settings, [&]<typename Flavor, typename IO>() {
162 return _prove<Flavor, IO>(std::move(circuit.bytecode), std::move(witness), std::move(circuit.verification_key));
163 });
164}
165
167{
168 BB_BENCH_NAME(MSGPACK_SCHEMA_NAME);
169 return dispatch_by_settings(settings, [&]<typename Flavor, typename IO>() {
170 auto prover_instance = _compute_prover_instance<Flavor, IO>(std::move(circuit.bytecode), {});
171 auto vk = std::make_shared<typename Flavor::VerificationKey>(prover_instance->get_precomputed());
173 .fields = vk_to_uint256_fields(*vk),
174 .hash = to_buffer(vk->hash()) };
175 });
176}
177
178template <typename Flavor, typename IO>
179CircuitStats::Response _stats(std::vector<uint8_t>&& bytecode, bool include_gates_per_opcode)
180{
181 using Circuit = typename Flavor::CircuitBuilder;
182 // Parse the circuit to get gate count information
184
185 acir_format::ProgramMetadata metadata = _create_program_metadata<IO>();
186 metadata.collect_gates_per_opcode = include_gates_per_opcode;
187 CircuitStats::Response response;
188 response.num_acir_opcodes = static_cast<uint32_t>(constraint_system.num_acir_opcodes);
189
190 acir_format::AcirProgram program{ std::move(constraint_system), {} };
191 auto builder = acir_format::create_circuit<Circuit>(program, metadata);
192 builder.finalize_circuit();
193
194 response.num_gates = static_cast<uint32_t>(builder.get_finalized_total_circuit_size());
195 response.num_gates_dyadic = static_cast<uint32_t>(builder.get_circuit_subgroup_size(response.num_gates));
196 // note: will be empty if collect_gates_per_opcode is false
197 response.gates_per_opcode =
198 std::vector<uint32_t>(program.constraints.gates_per_opcode.begin(), program.constraints.gates_per_opcode.end());
199
200 return response;
201}
202
204{
205 BB_BENCH_NAME(MSGPACK_SCHEMA_NAME);
206 return dispatch_by_settings(settings, [&]<typename Flavor, typename IO>() {
207 return _stats<Flavor, IO>(std::move(circuit.bytecode), include_gates_per_opcode);
208 });
209}
210
212{
213 BB_BENCH_NAME(MSGPACK_SCHEMA_NAME);
214 bool verified = dispatch_by_settings(settings, [&]<typename Flavor, typename IO>() {
215 return _verify<Flavor, IO>(verification_key, public_inputs, proof);
216 });
217 return { verified };
218}
219
221{
222 BB_BENCH_NAME(MSGPACK_SCHEMA_NAME);
223
225 validate_vk_size<VK>(verification_key);
226
227 // Standard UltraHonk flavors
228 auto vk = from_buffer<VK>(verification_key);
229 std::vector<bb::fr> fields;
230 fields = vk.to_field_elements();
231
232 return { std::move(fields) };
233}
234
236{
237 BB_BENCH_NAME(MSGPACK_SCHEMA_NAME);
238
240 validate_vk_size<VK>(verification_key);
241
242 // MegaFlavor for private function verification keys
243 auto vk = from_buffer<VK>(verification_key);
244 std::vector<bb::fr> fields;
245 fields = vk.to_field_elements();
246
247 return { std::move(fields) };
248}
249
251{
252 BB_BENCH_NAME(MSGPACK_SCHEMA_NAME);
253
255 validate_vk_size<VK>(verification_key);
256
257 auto vk = from_buffer<VK>(verification_key);
258 return { vk.to_field_elements() };
259}
260
262{
263 BB_BENCH_NAME(MSGPACK_SCHEMA_NAME);
264
266 validate_vk_size<VK>(verification_key);
267
268 auto vk = from_buffer<VK>(verification_key);
269 return { vk.to_field_elements() };
270}
271
273{
274 BB_BENCH_NAME(MSGPACK_SCHEMA_NAME);
275
277 validate_vk_size<VK>(verification_key);
278
279 auto vk = from_buffer<VK>(verification_key);
280 return { vk.to_field_elements() };
281}
282
284{
285 BB_BENCH_NAME(MSGPACK_SCHEMA_NAME);
287 validate_vk_size<VK>(verification_key);
288
289 auto vk = std::make_shared<VK>(from_buffer<VK>(verification_key));
290
291 std::string contract = settings.disable_zk ? get_honk_solidity_verifier(vk) : get_honk_zk_solidity_verifier(vk);
292
293// If in wasm, we dont include the optimized solidity verifier - due to its large bundle size
294// This will run generate twice, but this should only be run before deployment and not frequently
295#ifndef __wasm__
296 if (settings.optimized_solidity_verifier) {
297 contract = settings.disable_zk ? get_optimized_honk_solidity_verifier(vk)
299 }
300#endif
301
302 return { std::move(contract) };
303}
304
305} // namespace bb::bbapi
#define BB_ASSERT(expression,...)
Definition assert.hpp:70
#define BB_ASSERT_GTE(left, right,...)
Definition assert.hpp:128
#define BB_BENCH_NAME(name)
Definition bb_bench.hpp:264
Shared type definitions for the Barretenberg RPC API.
UltraHonk-specific command definitions for the Barretenberg RPC API.
ECCVMCircuitBuilder CircuitBuilder
FixedVKAndHash_< PrecomputedEntities< Commitment >, BF, ECCVMHardcodedVKAndHash > VerificationKey
The verification key stores commitments to the precomputed polynomials used by the verifier.
NativeVerificationKey_< PrecomputedEntities< Commitment >, Codec, HashFunction, CommitmentKey > VerificationKey
NativeVerificationKey_< PrecomputedEntities< Commitment >, Codec, HashFunction, CommitmentKey > VerificationKey
The verification key stores commitments to the precomputed (non-witness) polynomials used by the veri...
NativeVerificationKey_< PrecomputedEntities< Commitment >, Codec, HashFunction, CommitmentKey > VerificationKey
NativeVerificationKey_< PrecomputedEntities< Commitment >, Codec, HashFunction, CommitmentKey > VerificationKey
Base Native verification key class.
Definition flavor.hpp:138
static constexpr size_t calc_num_data_types()
Calculate the number of field elements needed for serialization.
Definition flavor.hpp:202
NativeVerificationKey_< PrecomputedEntities< Commitment >, Codec, HashFunction, CommitmentKey > VerificationKey
The verification key stores commitments to the precomputed (non-witness) polynomials used by the veri...
NativeVerificationKey_< PrecomputedEntities< Commitment >, Codec, HashFunction, CommitmentKey > VerificationKey
#define info(...)
Definition log.hpp:93
#define BB_UNUSED
AluTraceBuilder builder
Definition alu.test.cpp:124
std::vector< uint8_t > bytecode
std::string get_honk_solidity_verifier(auto const &verification_key)
std::string get_optimized_honk_solidity_verifier(auto const &verification_key)
std::string get_honk_zk_solidity_verifier(auto const &verification_key)
std::string get_optimized_honk_zk_solidity_verifier(auto const &verification_key)
WitnessVector witness_buf_to_witness_vector(std::vector< uint8_t > &&buf)
Convert a buffer representing a witness vector into Barretenberg's internal WitnessVector format.
AcirFormat circuit_buf_to_acir_format(std::vector< uint8_t > &&buf, bool is_mega)
Convert a buffer representing a circuit into Barretenberg's internal AcirFormat representation.
std::shared_ptr< ProverInstance_< Flavor > > _compute_prover_instance(std::vector< uint8_t > &&bytecode, std::vector< uint8_t > &&witness)
bool _verify(const std::vector< uint8_t > &vk_bytes, const std::vector< uint256_t > &public_inputs, const std::vector< uint256_t > &proof)
acir_format::ProgramMetadata _create_program_metadata()
Circuit _compute_circuit(std::vector< uint8_t > &&bytecode, std::vector< uint8_t > &&witness)
CircuitStats::Response _stats(std::vector< uint8_t > &&bytecode, bool include_gates_per_opcode)
std::vector< uint256_t > vk_to_uint256_fields(const VK &vk)
Convert VK to uint256 field elements, handling flavor-specific return types.
CircuitProve::Response _prove(std::vector< uint8_t > &&bytecode, std::vector< uint8_t > &&witness, std::vector< uint8_t > &&vk_bytes)
auto dispatch_by_settings(const ProofSystemSettings &settings, Operation &&operation)
Dispatch to the correct Flavor and IO type based on proof system settings.
field< Bn254FrParams > fr
Definition fr.hpp:155
VerifierCommitmentKey< Curve > vk
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
std::vector< uint8_t > to_buffer(T const &value)
Struct containing both the constraints to be added to the circuit and the witness vector.
Metadata required to create a circuit.
Full Honk proof layout (used by UltraVerifier).
Response execute(const BBApiRequest &request={}) &&
Contains proof and public inputs. Both are given as vectors of fields. To be used for verification....
std::vector< uint256_t > public_inputs
Response execute(const BBApiRequest &request={}) &&
std::vector< uint32_t > gates_per_opcode
Response execute(const BBApiRequest &request={}) &&
Response execute(const BBApiRequest &request={}) &&
Response execute(const BBApiRequest &request={}) &&
Response execute(const BBApiRequest &request={}) &&
Response execute(const BBApiRequest &request={}) &&
Response execute(const BBApiRequest &request={}) &&
Response execute(const BBApiRequest &request={}) &&
Response execute(const BBApiRequest &request={}) &&
void throw_or_abort(std::string const &err)