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 bool ipa_accumulation,
131 const std::vector<uint8_t>& vk_bytes,
132 const std::vector<uint256_t>& public_inputs,
133 const std::vector<uint256_t>& proof)
134{
136 using Verifier = UltraVerifier_<Flavor>;
137 using Transcript = typename Flavor::Transcript;
138 using DataType = typename Transcript::DataType;
139 using Proof = typename Transcript::Proof;
140
141 std::shared_ptr<VerificationKey> vk = std::make_shared<VerificationKey>(from_buffer<VerificationKey>(vk_bytes));
142
143 // concatenate public inputs and proof
144 std::vector<DataType> complete_proof;
145 complete_proof.reserve(public_inputs.size() + proof.size());
146 complete_proof.insert(complete_proof.end(), public_inputs.begin(), public_inputs.end());
147 complete_proof.insert(complete_proof.end(), proof.begin(), proof.end());
148
149 VerifierCommitmentKey<curve::Grumpkin> ipa_verification_key;
150 if constexpr (HasIPAAccumulator<Flavor>) {
151 if (ipa_accumulation) {
152 ipa_verification_key = VerifierCommitmentKey<curve::Grumpkin>(1 << CONST_ECCVM_LOG_N);
153 }
154 }
155
156 Verifier verifier{ vk, ipa_verification_key };
157
158 bool verified = false;
159 if constexpr (HasIPAAccumulator<Flavor>) {
160 const size_t HONK_PROOF_LENGTH = Flavor::PROOF_LENGTH_WITHOUT_PUB_INPUTS() - IPA_PROOF_LENGTH;
161 const size_t num_public_inputs = static_cast<size_t>(vk->num_public_inputs);
162 // The extra calculation is for the IPA proof length.
163 BB_ASSERT_EQ(complete_proof.size(),
164 HONK_PROOF_LENGTH + IPA_PROOF_LENGTH + num_public_inputs,
165 "Honk proof has incorrect length while verifying.");
166 const std::ptrdiff_t honk_proof_with_pub_inputs_length =
167 static_cast<std::ptrdiff_t>(HONK_PROOF_LENGTH + num_public_inputs);
168 auto ipa_proof = Proof(complete_proof.begin() + honk_proof_with_pub_inputs_length, complete_proof.end());
169 auto honk_proof = Proof(complete_proof.begin(), complete_proof.begin() + honk_proof_with_pub_inputs_length);
170 verified = verifier.template verify_proof<RollupIO>(complete_proof, ipa_proof).result;
171 } else {
172 verified = verifier.template verify_proof<DefaultIO>(complete_proof).result;
173 }
174
175 if (verified) {
176 info("Proof verified successfully");
177 } else {
178 info("Proof verification failed");
179 }
180
181 return verified;
182}
183
185{
186 BB_BENCH_NAME(MSGPACK_SCHEMA_NAME);
187 // if the ipa accumulation flag is set we are using the UltraRollupFlavor
188 if (settings.ipa_accumulation) {
189 return _prove<UltraRollupFlavor>(
190 std::move(circuit.bytecode), std::move(witness), std::move(circuit.verification_key));
191 }
192 if (settings.oracle_hash_type == "poseidon2" && !settings.disable_zk) {
193 // if we are not disabling ZK and the oracle hash type is poseidon2, we are using the UltraZKFlavor
194 return _prove<UltraZKFlavor>(
195 std::move(circuit.bytecode), std::move(witness), std::move(circuit.verification_key));
196 }
197 if (settings.oracle_hash_type == "poseidon2" && settings.disable_zk) {
198 // if we are disabling ZK and the oracle hash type is poseidon2, we are using the UltraFlavor
199 return _prove<UltraFlavor>(
200 std::move(circuit.bytecode), std::move(witness), std::move(circuit.verification_key));
201 }
202 if (settings.oracle_hash_type == "keccak" && !settings.disable_zk) {
203 // if we are not disabling ZK and the oracle hash type is keccak, we are using the UltraKeccakZKFlavor
204 return _prove<UltraKeccakZKFlavor>(
205 std::move(circuit.bytecode), std::move(witness), std::move(circuit.verification_key));
206 }
207 if (settings.oracle_hash_type == "keccak" && settings.disable_zk) {
208 return _prove<UltraKeccakFlavor>(
209 std::move(circuit.bytecode), std::move(witness), std::move(circuit.verification_key));
210#ifdef STARKNET_GARAGA_FLAVORS
211 }
212 if (settings.oracle_hash_type == "starknet" && settings.disable_zk) {
213 return _prove<UltraStarknetFlavor>(
214 std::move(circuit.bytecode), std::move(witness), std::move(circuit.verification_key()));
215 }
216 if (settings.oracle_hash_type == "starknet" && !settings.disable_zk) {
217 return _prove<UltraStarknetZKFlavor>(
218 std::move(circuit.bytecode), std::move(witness), std::move(circuit.verification_key()));
219#endif
220 }
221 throw_or_abort("Invalid proving options specified in CircuitProve!");
222}
223
225{
226 BB_BENCH_NAME(MSGPACK_SCHEMA_NAME);
227 std::vector<uint8_t> vk_bytes;
228 std::vector<uint256_t> vk_fields;
229 std::vector<uint8_t> vk_hash_bytes;
230
231 // Helper lambda to compute VK, fields, and hash for a given flavor
232 auto compute_vk_and_fields = [&]<typename Flavor>() {
233 auto prover_instance = _compute_prover_instance<Flavor>(std::move(circuit.bytecode), {});
234 auto vk = std::make_shared<typename Flavor::VerificationKey>(prover_instance->get_precomputed());
235 vk_bytes = to_buffer(*vk);
237 vk_fields = vk->to_field_elements();
238 } else {
239 // For other flavors, we use field elements
240 auto uint256_elements = vk->to_field_elements();
241 vk_fields.reserve(uint256_elements.size());
242 vk_fields.insert(vk_fields.end(), uint256_elements.begin(), uint256_elements.end());
243 }
244 vk_hash_bytes = to_buffer(vk->hash());
245 };
246
247 if (settings.ipa_accumulation) {
248 compute_vk_and_fields.template operator()<UltraRollupFlavor>();
249 } else if (settings.oracle_hash_type == "poseidon2" && !settings.disable_zk) {
250 compute_vk_and_fields.template operator()<UltraZKFlavor>();
251 } else if (settings.oracle_hash_type == "poseidon2" && settings.disable_zk) {
252 compute_vk_and_fields.template operator()<UltraFlavor>();
253 } else if (settings.oracle_hash_type == "keccak" && !settings.disable_zk) {
254 compute_vk_and_fields.template operator()<UltraKeccakZKFlavor>();
255 } else if (settings.oracle_hash_type == "keccak" && settings.disable_zk) {
256 compute_vk_and_fields.template operator()<UltraKeccakFlavor>();
257#ifdef STARKNET_GARAGA_FLAVORS
258 } else if (settings.oracle_hash_type == "starknet" && !settings.disable_zk) {
259 compute_vk_and_fields.template operator()<UltraStarknetZKFlavor>();
260 } else if (settings.oracle_hash_type == "starknet" && settings.disable_zk) {
261 compute_vk_and_fields.template operator()<UltraStarknetFlavor>();
262#endif
263 } else {
264 throw_or_abort("invalid proof type in _write_vk");
265 }
266
267 return { .bytes = std::move(vk_bytes), .fields = std::move(vk_fields), .hash = std::move(vk_hash_bytes) };
268}
269
270template <typename Flavor, typename Circuit = typename Flavor::CircuitBuilder>
271CircuitStats::Response _stats(std::vector<uint8_t>&& bytecode, bool include_gates_per_opcode)
272{
273 // Parse the circuit to get gate count information
275
276 acir_format::ProgramMetadata metadata = _create_program_metadata<Flavor>();
277 metadata.collect_gates_per_opcode = include_gates_per_opcode;
278 CircuitStats::Response response;
279 response.num_acir_opcodes = static_cast<uint32_t>(constraint_system.num_acir_opcodes);
280
281 acir_format::AcirProgram program{ std::move(constraint_system) };
282 auto builder = acir_format::create_circuit<Circuit>(program, metadata);
283 builder.finalize_circuit(/*ensure_nonzero=*/true);
284
285 response.num_gates = static_cast<uint32_t>(builder.get_finalized_total_circuit_size());
286 response.num_gates_dyadic = static_cast<uint32_t>(builder.get_circuit_subgroup_size(response.num_gates));
287 // note: will be empty if collect_gates_per_opcode is false
288 response.gates_per_opcode = std::move(program.constraints.gates_per_opcode);
289
290 return response;
291}
292
294{
295 BB_BENCH_NAME(MSGPACK_SCHEMA_NAME);
296 // if the ipa accumulation flag is set we are using the UltraRollupFlavor
297 if (settings.ipa_accumulation) {
298 return _stats<UltraRollupFlavor>(std::move(circuit.bytecode), include_gates_per_opcode);
299 }
300 if (settings.oracle_hash_type == "poseidon2" && !settings.disable_zk) {
301 // if we are not disabling ZK and the oracle hash type is poseidon2, we are using the UltraZKFlavor
302 return _stats<UltraZKFlavor>(std::move(circuit.bytecode), include_gates_per_opcode);
303 }
304 if (settings.oracle_hash_type == "poseidon2" && settings.disable_zk) {
305 // if we are disabling ZK and the oracle hash type is poseidon2, we are using the UltraFlavor
306 return _stats<UltraFlavor>(std::move(circuit.bytecode), include_gates_per_opcode);
307 }
308 if (settings.oracle_hash_type == "keccak" && !settings.disable_zk) {
309 // if we are not disabling ZK and the oracle hash type is keccak, we are using the UltraKeccakZKFlavor
310 return _stats<UltraKeccakZKFlavor>(std::move(circuit.bytecode), include_gates_per_opcode);
311 }
312 if (settings.oracle_hash_type == "keccak" && settings.disable_zk) {
313 return _stats<UltraKeccakFlavor>(std::move(circuit.bytecode), include_gates_per_opcode);
314#ifdef STARKNET_GARAGA_FLAVORS
315 }
316 if (settings.oracle_hash_type == "starknet" && settings.disable_zk) {
317 return _stats<UltraStarknetFlavor>(std::move(circuit.bytecode), include_gates_per_opcode);
318 }
319 if (settings.oracle_hash_type == "starknet" && !settings.disable_zk) {
320 return _stats<UltraStarknetZKFlavor>(std::move(circuit.bytecode), include_gates_per_opcode);
321#endif
322 }
323 throw_or_abort("Invalid proving options specified in CircuitStats!");
324}
325
327{
328 BB_BENCH_NAME(MSGPACK_SCHEMA_NAME);
329 const bool ipa_accumulation = settings.ipa_accumulation;
330 bool verified = false;
331
332 // if the ipa accumulation flag is set we are using the UltraRollupFlavor
333 if (ipa_accumulation) {
334 verified = _verify<UltraRollupFlavor>(ipa_accumulation, verification_key, public_inputs, proof);
335 } else if (settings.oracle_hash_type == "poseidon2" && !settings.disable_zk) {
336 verified = _verify<UltraZKFlavor>(ipa_accumulation, verification_key, public_inputs, proof);
337 } else if (settings.oracle_hash_type == "poseidon2" && settings.disable_zk) {
338 verified = _verify<UltraFlavor>(ipa_accumulation, verification_key, public_inputs, proof);
339 } else if (settings.oracle_hash_type == "keccak" && !settings.disable_zk) {
340 verified = _verify<UltraKeccakZKFlavor>(ipa_accumulation, verification_key, public_inputs, proof);
341 } else if (settings.oracle_hash_type == "keccak" && settings.disable_zk) {
342 verified = _verify<UltraKeccakFlavor>(ipa_accumulation, verification_key, public_inputs, proof);
343#ifdef STARKNET_GARAGA_FLAVORS
344 } else if (settings.oracle_hash_type == "starknet" && !settings.disable_zk) {
345 verified = _verify<UltraStarknetZKFlavor>(ipa_accumulation, verification_key, public_inputs, proof);
346 } else if (settings.oracle_hash_type == "starknet" && settings.disable_zk) {
347 verified = _verify<UltraStarknetFlavor>(ipa_accumulation, verification_key, public_inputs, proof);
348#endif
349 } else {
350 throw_or_abort("invalid proof type in _verify");
351 }
352
353 return { verified };
354}
355
357{
358 BB_BENCH_NAME(MSGPACK_SCHEMA_NAME);
359 std::vector<bb::fr> fields;
360
361 // Standard UltraHonk flavors
362 auto vk = from_buffer<UltraFlavor::VerificationKey>(verification_key);
363 fields = vk.to_field_elements();
364
365 return { std::move(fields) };
366}
367
369{
370 BB_BENCH_NAME(MSGPACK_SCHEMA_NAME);
371 std::vector<bb::fr> fields;
372
373 // MegaFlavor for private function verification keys
374 auto vk = from_buffer<MegaFlavor::VerificationKey>(verification_key);
375 fields = vk.to_field_elements();
376
377 return { std::move(fields) };
378}
379
381{
382 BB_BENCH_NAME(MSGPACK_SCHEMA_NAME);
384 auto vk = std::make_shared<VK>(from_buffer<VK>(verification_key));
385
386 std::string contract = settings.disable_zk ? get_honk_solidity_verifier(vk) : get_honk_zk_solidity_verifier(vk);
387
388// If in wasm, we dont include the optimized solidity verifier - due to its large bundle size
389// This will run generate twice, but this should only be run before deployment and not frequently
390#ifndef __wasm__
391 if (settings.disable_zk && settings.optimized_solidity_verifier) {
393 }
394#endif
395
396 return { std::move(contract) };
397}
398
399} // namespace bb::bbapi
#define BB_ASSERT_GTE(left, right,...)
Definition assert.hpp:122
#define BB_ASSERT_EQ(actual, expected,...)
Definition assert.hpp:77
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.
static constexpr size_t PUBLIC_INPUTS_SIZE
The verification key is responsible for storing the commitments to the precomputed (non-witnessk) pol...
static constexpr size_t PROOF_LENGTH_WITHOUT_PUB_INPUTS
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:75
#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 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={}) &&
Response execute(const BBApiRequest &request={}) &&
void throw_or_abort(std::string const &err)