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.
25#include <type_traits>
26#ifdef STARKNET_GARAGA_FLAVORS
27#include "barretenberg/flavor/ultra_starknet_flavor.hpp"
28#include "barretenberg/flavor/ultra_starknet_zk_flavor.hpp"
29#endif
30#include <iomanip>
31#include <sstream>
32
33namespace bb::bbapi {
34
36{
37 bool constexpr has_ipa_claim = IsAnyOf<Flavor, UltraRollupFlavor>;
38
39 return acir_format::ProgramMetadata{ .has_ipa_claim = has_ipa_claim };
40}
41
42template <typename Flavor, typename Circuit = typename Flavor::CircuitBuilder>
43Circuit _compute_circuit(std::vector<uint8_t>&& bytecode, std::vector<uint8_t>&& witness)
44{
45 const acir_format::ProgramMetadata metadata = _create_program_metadata<Flavor>();
47
48 if (!witness.empty()) {
49 program.witness = acir_format::witness_buf_to_witness_vector(std::move(witness));
50 }
51 return acir_format::create_circuit<Circuit>(program, metadata);
52}
53
54template <typename Flavor>
56 std::vector<uint8_t>&& witness)
57{
58 // Measure function time and debug print
59 auto initial_time = std::chrono::high_resolution_clock::now();
60 typename Flavor::CircuitBuilder builder = _compute_circuit<Flavor>(std::move(bytecode), std::move(witness));
62 auto final_time = std::chrono::high_resolution_clock::now();
63 auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(final_time - initial_time);
64 info("CircuitProve: Proving key computed in ", duration.count(), " ms");
65 return prover_instance;
66}
67template <typename Flavor>
68CircuitProve::Response _prove(std::vector<uint8_t>&& bytecode,
69 std::vector<uint8_t>&& witness,
70 std::vector<uint8_t>&& vk_bytes)
71{
72 using Proof = typename Flavor::Transcript::Proof;
73
74 auto prover_instance = _compute_prover_instance<Flavor>(std::move(bytecode), std::move(witness));
76 if (vk_bytes.empty()) {
77 info("WARNING: computing verification key while proving. Pass in a precomputed vk for better performance.");
78 vk = std::make_shared<typename Flavor::VerificationKey>(prover_instance->get_precomputed());
79 } else {
80 vk =
81 std::make_shared<typename Flavor::VerificationKey>(from_buffer<typename Flavor::VerificationKey>(vk_bytes));
82 }
83
84 UltraProver_<Flavor> prover{ prover_instance, vk };
85
86 Proof concat_pi_and_proof = prover.construct_proof();
87 // Compute number of inner public inputs. Perform loose checks that the public inputs contain enough data.
88 auto num_inner_public_inputs = [&]() {
89 size_t num_public_inputs = prover.prover_instance->num_public_inputs();
90 if constexpr (HasIPAAccumulator<Flavor>) {
91 BB_ASSERT_GTE(num_public_inputs,
93 "Public inputs should contain a pairing point accumulator and an IPA claim.");
94 return num_public_inputs - RollupIO::PUBLIC_INPUTS_SIZE;
95 } else {
96 BB_ASSERT_GTE(num_public_inputs,
98 "Public inputs should contain a pairing point accumulator.");
99 return num_public_inputs - DefaultIO::PUBLIC_INPUTS_SIZE;
100 }
101 }();
102 CircuitComputeVk::Response vk_response;
103 // Optimization over calling CircuitComputeVk separately - if vk not provided, we write it.
104 if (vk_bytes.empty()) {
105 auto vk_fields_direct = vk->to_field_elements();
106 std::vector<uint256_t> vk_fields;
107 // Handle discrepancy in type of 'to_field_elements'
108 if constexpr (std::is_same_v<decltype(vk_fields_direct), std::vector<uint256_t>>) {
109 vk_fields = std::move(vk_fields_direct);
110 } else {
111 vk_fields = std::vector<uint256_t>(vk_fields_direct.begin(), vk_fields_direct.end());
112 }
113 vk_response = { .bytes = vk_bytes.empty() ? to_buffer(vk) : vk_bytes,
114 .fields = std::move(vk_fields),
115 .hash = to_buffer(vk->hash()) };
116 }
117
118 // We split the inner public inputs, which are stored at the front of the proof, from the rest of the proof. Now,
119 // the "proof" refers to everything except the inner public inputs.
120 return { .public_inputs = std::vector<uint256_t>{ concat_pi_and_proof.begin(),
121 concat_pi_and_proof.begin() +
122 static_cast<std::ptrdiff_t>(num_inner_public_inputs) },
123 .proof = std::vector<uint256_t>{ concat_pi_and_proof.begin() +
124 static_cast<std::ptrdiff_t>(num_inner_public_inputs),
125 concat_pi_and_proof.end() },
126 .vk = std::move(vk_response) };
127}
128
129template <typename Flavor>
130bool _verify(const std::vector<uint8_t>& vk_bytes,
131 const std::vector<uint256_t>& public_inputs,
132 const std::vector<uint256_t>& proof)
133{
135 using VKAndHash = typename Flavor::VKAndHash;
137 using Verifier = UltraVerifier_<Flavor, IO>;
138 using Transcript = typename Flavor::Transcript;
139 using DataType = typename Transcript::DataType;
140
141 std::shared_ptr<VerificationKey> vk = std::make_shared<VerificationKey>(from_buffer<VerificationKey>(vk_bytes));
142 auto vk_and_hash = std::make_shared<VKAndHash>(vk);
143
144 // concatenate public inputs and proof
145 std::vector<DataType> complete_proof;
146 complete_proof.reserve(public_inputs.size() + proof.size());
147 complete_proof.insert(complete_proof.end(), public_inputs.begin(), public_inputs.end());
148 complete_proof.insert(complete_proof.end(), proof.begin(), proof.end());
149
150 Verifier verifier{ vk_and_hash };
151
152 bool verified = verifier.verify_proof(complete_proof).result;
153
154 if (verified) {
155 info("Proof verified successfully");
156 } else {
157 info("Proof verification failed");
158 }
159
160 return verified;
161}
162
164{
165 BB_BENCH_NAME(MSGPACK_SCHEMA_NAME);
166 // if the ipa accumulation flag is set we are using the UltraRollupFlavor
167 if (settings.ipa_accumulation) {
168 return _prove<UltraRollupFlavor>(
169 std::move(circuit.bytecode), std::move(witness), std::move(circuit.verification_key));
170 }
171 if (settings.oracle_hash_type == "poseidon2" && !settings.disable_zk) {
172 // if we are not disabling ZK and the oracle hash type is poseidon2, we are using the UltraZKFlavor
173 return _prove<UltraZKFlavor>(
174 std::move(circuit.bytecode), std::move(witness), std::move(circuit.verification_key));
175 }
176 if (settings.oracle_hash_type == "poseidon2" && settings.disable_zk) {
177 // if we are disabling ZK and the oracle hash type is poseidon2, we are using the UltraFlavor
178 return _prove<UltraFlavor>(
179 std::move(circuit.bytecode), std::move(witness), std::move(circuit.verification_key));
180 }
181 if (settings.oracle_hash_type == "keccak" && !settings.disable_zk) {
182 // if we are not disabling ZK and the oracle hash type is keccak, we are using the UltraKeccakZKFlavor
183 return _prove<UltraKeccakZKFlavor>(
184 std::move(circuit.bytecode), std::move(witness), std::move(circuit.verification_key));
185 }
186 if (settings.oracle_hash_type == "keccak" && settings.disable_zk) {
187 return _prove<UltraKeccakFlavor>(
188 std::move(circuit.bytecode), std::move(witness), std::move(circuit.verification_key));
189#ifdef STARKNET_GARAGA_FLAVORS
190 }
191 if (settings.oracle_hash_type == "starknet" && settings.disable_zk) {
192 return _prove<UltraStarknetFlavor>(
193 std::move(circuit.bytecode), std::move(witness), std::move(circuit.verification_key()));
194 }
195 if (settings.oracle_hash_type == "starknet" && !settings.disable_zk) {
196 return _prove<UltraStarknetZKFlavor>(
197 std::move(circuit.bytecode), std::move(witness), std::move(circuit.verification_key()));
198#endif
199 }
200 throw_or_abort("Invalid proving options specified in CircuitProve!");
201}
202
204{
205 BB_BENCH_NAME(MSGPACK_SCHEMA_NAME);
206 std::vector<uint8_t> vk_bytes;
207 std::vector<uint256_t> vk_fields;
208 std::vector<uint8_t> vk_hash_bytes;
209
210 // Helper lambda to compute VK, fields, and hash for a given flavor
211 auto compute_vk_and_fields = [&]<typename Flavor>() {
212 auto prover_instance = _compute_prover_instance<Flavor>(std::move(circuit.bytecode), {});
213 auto vk = std::make_shared<typename Flavor::VerificationKey>(prover_instance->get_precomputed());
214 vk_bytes = to_buffer(*vk);
216 vk_fields = vk->to_field_elements();
217 } else {
218 // For other flavors, we use field elements
219 auto uint256_elements = vk->to_field_elements();
220 vk_fields.reserve(uint256_elements.size());
221 vk_fields.insert(vk_fields.end(), uint256_elements.begin(), uint256_elements.end());
222 }
223 vk_hash_bytes = to_buffer(vk->hash());
224 };
225
226 if (settings.ipa_accumulation) {
227 compute_vk_and_fields.template operator()<UltraRollupFlavor>();
228 } else if (settings.oracle_hash_type == "poseidon2" && !settings.disable_zk) {
229 compute_vk_and_fields.template operator()<UltraZKFlavor>();
230 } else if (settings.oracle_hash_type == "poseidon2" && settings.disable_zk) {
231 compute_vk_and_fields.template operator()<UltraFlavor>();
232 } else if (settings.oracle_hash_type == "keccak" && !settings.disable_zk) {
233 compute_vk_and_fields.template operator()<UltraKeccakZKFlavor>();
234 } else if (settings.oracle_hash_type == "keccak" && settings.disable_zk) {
235 compute_vk_and_fields.template operator()<UltraKeccakFlavor>();
236#ifdef STARKNET_GARAGA_FLAVORS
237 } else if (settings.oracle_hash_type == "starknet" && !settings.disable_zk) {
238 compute_vk_and_fields.template operator()<UltraStarknetZKFlavor>();
239 } else if (settings.oracle_hash_type == "starknet" && settings.disable_zk) {
240 compute_vk_and_fields.template operator()<UltraStarknetFlavor>();
241#endif
242 } else {
243 throw_or_abort("invalid proof type in _write_vk");
244 }
245
246 return { .bytes = std::move(vk_bytes), .fields = std::move(vk_fields), .hash = std::move(vk_hash_bytes) };
247}
248
249template <typename Flavor, typename Circuit = typename Flavor::CircuitBuilder>
250CircuitStats::Response _stats(std::vector<uint8_t>&& bytecode, bool include_gates_per_opcode)
251{
252 // Parse the circuit to get gate count information
254
255 acir_format::ProgramMetadata metadata = _create_program_metadata<Flavor>();
256 metadata.collect_gates_per_opcode = include_gates_per_opcode;
257 CircuitStats::Response response;
258 response.num_acir_opcodes = static_cast<uint32_t>(constraint_system.num_acir_opcodes);
259
260 acir_format::AcirProgram program{ std::move(constraint_system) };
261 auto builder = acir_format::create_circuit<Circuit>(program, metadata);
262 builder.finalize_circuit(/*ensure_nonzero=*/true);
263
264 response.num_gates = static_cast<uint32_t>(builder.get_finalized_total_circuit_size());
265 response.num_gates_dyadic = static_cast<uint32_t>(builder.get_circuit_subgroup_size(response.num_gates));
266 // note: will be empty if collect_gates_per_opcode is false
267 response.gates_per_opcode = std::move(program.constraints.gates_per_opcode);
268
269 return response;
270}
271
273{
274 BB_BENCH_NAME(MSGPACK_SCHEMA_NAME);
275 // if the ipa accumulation flag is set we are using the UltraRollupFlavor
276 if (settings.ipa_accumulation) {
277 return _stats<UltraRollupFlavor>(std::move(circuit.bytecode), include_gates_per_opcode);
278 }
279 if (settings.oracle_hash_type == "poseidon2" && !settings.disable_zk) {
280 // if we are not disabling ZK and the oracle hash type is poseidon2, we are using the UltraZKFlavor
281 return _stats<UltraZKFlavor>(std::move(circuit.bytecode), include_gates_per_opcode);
282 }
283 if (settings.oracle_hash_type == "poseidon2" && settings.disable_zk) {
284 // if we are disabling ZK and the oracle hash type is poseidon2, we are using the UltraFlavor
285 return _stats<UltraFlavor>(std::move(circuit.bytecode), include_gates_per_opcode);
286 }
287 if (settings.oracle_hash_type == "keccak" && !settings.disable_zk) {
288 // if we are not disabling ZK and the oracle hash type is keccak, we are using the UltraKeccakZKFlavor
289 return _stats<UltraKeccakZKFlavor>(std::move(circuit.bytecode), include_gates_per_opcode);
290 }
291 if (settings.oracle_hash_type == "keccak" && settings.disable_zk) {
292 return _stats<UltraKeccakFlavor>(std::move(circuit.bytecode), include_gates_per_opcode);
293#ifdef STARKNET_GARAGA_FLAVORS
294 }
295 if (settings.oracle_hash_type == "starknet" && settings.disable_zk) {
296 return _stats<UltraStarknetFlavor>(std::move(circuit.bytecode), include_gates_per_opcode);
297 }
298 if (settings.oracle_hash_type == "starknet" && !settings.disable_zk) {
299 return _stats<UltraStarknetZKFlavor>(std::move(circuit.bytecode), include_gates_per_opcode);
300#endif
301 }
302 throw_or_abort("Invalid proving options specified in CircuitStats!");
303}
304
306{
307 BB_BENCH_NAME(MSGPACK_SCHEMA_NAME);
308 const bool ipa_accumulation = settings.ipa_accumulation;
309 bool verified = false;
310
311 // if the ipa accumulation flag is set we are using the UltraRollupFlavor
312 if (ipa_accumulation) {
313 verified = _verify<UltraRollupFlavor>(verification_key, public_inputs, proof);
314 } else if (settings.oracle_hash_type == "poseidon2" && !settings.disable_zk) {
315 verified = _verify<UltraZKFlavor>(verification_key, public_inputs, proof);
316 } else if (settings.oracle_hash_type == "poseidon2" && settings.disable_zk) {
317 verified = _verify<UltraFlavor>(verification_key, public_inputs, proof);
318 } else if (settings.oracle_hash_type == "keccak" && !settings.disable_zk) {
319 verified = _verify<UltraKeccakZKFlavor>(verification_key, public_inputs, proof);
320 } else if (settings.oracle_hash_type == "keccak" && settings.disable_zk) {
321 verified = _verify<UltraKeccakFlavor>(verification_key, public_inputs, proof);
322#ifdef STARKNET_GARAGA_FLAVORS
323 } else if (settings.oracle_hash_type == "starknet" && !settings.disable_zk) {
324 verified = _verify<UltraStarknetZKFlavor>(verification_key, public_inputs, proof);
325 } else if (settings.oracle_hash_type == "starknet" && settings.disable_zk) {
326 verified = _verify<UltraStarknetFlavor>(verification_key, public_inputs, proof);
327#endif
328 } else {
329 throw_or_abort("invalid proof type in _verify");
330 }
331
332 return { verified };
333}
334
336{
337 BB_BENCH_NAME(MSGPACK_SCHEMA_NAME);
338 std::vector<bb::fr> fields;
339
340 // Standard UltraHonk flavors
341 auto vk = from_buffer<UltraFlavor::VerificationKey>(verification_key);
342 fields = vk.to_field_elements();
343
344 return { std::move(fields) };
345}
346
348{
349 BB_BENCH_NAME(MSGPACK_SCHEMA_NAME);
350 std::vector<bb::fr> fields;
351
352 // MegaFlavor for private function verification keys
353 auto vk = from_buffer<MegaFlavor::VerificationKey>(verification_key);
354 fields = vk.to_field_elements();
355
356 return { std::move(fields) };
357}
358
360{
361 BB_BENCH_NAME(MSGPACK_SCHEMA_NAME);
363 auto vk = std::make_shared<VK>(from_buffer<VK>(verification_key));
364
365 std::string contract = settings.disable_zk ? get_honk_solidity_verifier(vk) : get_honk_zk_solidity_verifier(vk);
366
367// If in wasm, we dont include the optimized solidity verifier - due to its large bundle size
368// This will run generate twice, but this should only be run before deployment and not frequently
369#ifndef __wasm__
370 if (settings.disable_zk && settings.optimized_solidity_verifier) {
372 }
373#endif
374
375 return { std::move(contract) };
376}
377
378} // namespace bb::bbapi
#define BB_ASSERT_GTE(left, right,...)
Definition assert.hpp:138
std::shared_ptr< Napi::ThreadSafeFunction > bytecode
#define BB_BENCH_NAME(name)
Definition bb_bench.hpp:219
Shared type definitions for the Barretenberg RPC API.
UltraHonk-specific command definitions for the Barretenberg RPC API.
Manages the data that is propagated on the public inputs of an application/function circuit.
static constexpr size_t PUBLIC_INPUTS_SIZE
The verification key is responsible for storing the commitments to the precomputed (non-witnessk) pol...
BaseTranscript< Codec, HashFunction > Transcript
The data that is propagated on the public inputs of a rollup circuit.
static constexpr size_t PUBLIC_INPUTS_SIZE
NativeVerificationKey_< PrecomputedEntities< Commitment >, Codec, HashFunction, CommitmentKey > VerificationKey
UltraRollupFlavor extends UltraFlavor with IPA proof support.
Child class of UltraFlavor that runs with ZK Sumcheck.
void info(Args... args)
Definition log.hpp:89
#define BB_UNUSED
AluTraceBuilder builder
Definition alu.test.cpp:124
ECCVMFlavor::Transcript Transcript
std::string get_honk_solidity_verifier(auto const &verification_key)
UltraKeccakFlavor::VerificationKey VerificationKey
std::string get_optimized_honk_solidity_verifier(auto const &verification_key)
std::string get_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)
Convert a buffer representing a circuit into Barretenberg's internal AcirFormat representation.
acir_format::ProgramMetadata _create_program_metadata()
CircuitStats::Response _stats(std::vector< uint8_t > &&bytecode, bool include_gates_per_opcode)
Circuit _compute_circuit(std::vector< uint8_t > &&bytecode, std::vector< uint8_t > &&witness)
CircuitProve::Response _prove(std::vector< uint8_t > &&bytecode, std::vector< uint8_t > &&witness, std::vector< uint8_t > &&vk_bytes)
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)
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.
Response execute(const BBApiRequest &request={}) &&
Contains proof and public inputs. Both are given as vectors of fields. To be used for verification....
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)