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