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.
21#include <type_traits>
22#ifdef STARKNET_GARAGA_FLAVORS
23#include "barretenberg/flavor/ultra_starknet_flavor.hpp"
24#include "barretenberg/flavor/ultra_starknet_zk_flavor.hpp"
25#endif
26#include <iomanip>
27#include <sstream>
28
29namespace bb::bbapi {
30
32{
33 uint32_t honk_recursion = 0;
34
36 honk_recursion = 1;
37 } else if constexpr (IsAnyOf<Flavor, UltraRollupFlavor>) {
38 honk_recursion = 2;
39 }
40#ifdef STARKNET_GARAGA_FLAVORS
42 honk_recursion = 1;
43 }
44#endif
45
46 return acir_format::ProgramMetadata{ .honk_recursion = honk_recursion };
47}
48
49template <typename Flavor, typename Circuit = typename Flavor::CircuitBuilder>
50Circuit _compute_circuit(std::vector<uint8_t>&& bytecode, std::vector<uint8_t>&& witness)
51{
52 const acir_format::ProgramMetadata metadata = _create_program_metadata<Flavor>();
54
55 if (!witness.empty()) {
56 program.witness = acir_format::witness_buf_to_witness_data(std::move(witness));
57 }
58 return acir_format::create_circuit<Circuit>(program, metadata);
59}
60
61template <typename Flavor>
63 std::vector<uint8_t>&& witness)
64{
65 // Measure function time and debug print
66 auto initial_time = std::chrono::high_resolution_clock::now();
67 typename Flavor::CircuitBuilder builder = _compute_circuit<Flavor>(std::move(bytecode), std::move(witness));
68 auto decider_proving_key = std::make_shared<DeciderProvingKey_<Flavor>>(builder);
69 auto final_time = std::chrono::high_resolution_clock::now();
70 auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(final_time - initial_time);
71 info("CircuitProve: Proving key computed in ", duration.count(), " ms");
72 return decider_proving_key;
73}
74
75template <typename Flavor>
76CircuitProve::Response _prove(std::vector<uint8_t>&& bytecode,
77 std::vector<uint8_t>&& witness,
78 std::vector<uint8_t>&& vk_bytes)
79{
80 using Proof = typename Flavor::Transcript::Proof;
81
82 auto proving_key = _compute_proving_key<Flavor>(std::move(bytecode), std::move(witness));
84 if (vk_bytes.empty()) {
85 info("WARNING: computing verification key while proving. Pass in a precomputed vk for better performance.");
86 vk = std::make_shared<typename Flavor::VerificationKey>(proving_key->get_precomputed());
87 } else {
88 vk =
89 std::make_shared<typename Flavor::VerificationKey>(from_buffer<typename Flavor::VerificationKey>(vk_bytes));
90 }
91
92 UltraProver_<Flavor> prover{ proving_key, vk };
93
94 Proof concat_pi_and_proof = prover.construct_proof();
95 // Compute number of inner public inputs. Perform loose checks that the public inputs contain enough data.
96 auto num_inner_public_inputs = [&]() {
97 size_t num_public_inputs = prover.proving_key->num_public_inputs();
98 if constexpr (HasIPAAccumulator<Flavor>) {
99 BB_ASSERT_GTE(num_public_inputs,
101 "Public inputs should contain a pairing point accumulator and an IPA claim.");
102 return num_public_inputs - RollupIO::PUBLIC_INPUTS_SIZE;
103 } else {
104 BB_ASSERT_GTE(num_public_inputs,
106 "Public inputs should contain a pairing point accumulator.");
107 return num_public_inputs - DefaultIO::PUBLIC_INPUTS_SIZE;
108 }
109 }();
110 CircuitComputeVk::Response vk_response;
111 // Optimization over calling CircuitComputeVk separately - if vk not provided, we write it.
112 if (vk_bytes.empty()) {
113 auto vk_fields_direct = vk->to_field_elements();
114 std::vector<uint256_t> vk_fields;
115 // Handle discrepancy in type of 'to_field_elements'
116 if constexpr (std::is_same_v<decltype(vk_fields_direct), std::vector<uint256_t>>) {
117 vk_fields = std::move(vk_fields_direct);
118 } else {
119 vk_fields = std::vector<uint256_t>(vk_fields_direct.begin(), vk_fields_direct.end());
120 }
121 vk_response = { .bytes = vk_bytes.empty() ? to_buffer(vk) : vk_bytes,
122 .fields = std::move(vk_fields),
123 .hash = to_buffer(vk->hash()) };
124 }
125
126 // We split the inner public inputs, which are stored at the front of the proof, from the rest of the proof. Now,
127 // the "proof" refers to everything except the inner public inputs.
128 return { .public_inputs = std::vector<uint256_t>{ concat_pi_and_proof.begin(),
129 concat_pi_and_proof.begin() +
130 static_cast<std::ptrdiff_t>(num_inner_public_inputs) },
131 .proof = std::vector<uint256_t>{ concat_pi_and_proof.begin() +
132 static_cast<std::ptrdiff_t>(num_inner_public_inputs),
133 concat_pi_and_proof.end() },
134 .vk = std::move(vk_response) };
135}
136
137template <typename Flavor>
138bool _verify(const bool ipa_accumulation,
139 const std::vector<uint8_t>& vk_bytes,
140 const std::vector<uint256_t>& public_inputs,
141 const std::vector<uint256_t>& proof)
142{
144 using Verifier = UltraVerifier_<Flavor>;
145 using Transcript = typename Flavor::Transcript;
146 using DataType = typename Transcript::DataType;
147 using Proof = typename Transcript::Proof;
148
149 std::shared_ptr<VerificationKey> vk = std::make_shared<VerificationKey>(from_buffer<VerificationKey>(vk_bytes));
150
151 // concatenate public inputs and proof
152 std::vector<DataType> complete_proof;
153 complete_proof.reserve(public_inputs.size() + proof.size());
154 complete_proof.insert(complete_proof.end(), public_inputs.begin(), public_inputs.end());
155 complete_proof.insert(complete_proof.end(), proof.begin(), proof.end());
156
157 VerifierCommitmentKey<curve::Grumpkin> ipa_verification_key;
158 if constexpr (HasIPAAccumulator<Flavor>) {
159 if (ipa_accumulation) {
160 ipa_verification_key = VerifierCommitmentKey<curve::Grumpkin>(1 << CONST_ECCVM_LOG_N);
161 }
162 }
163
164 Verifier verifier{ vk, ipa_verification_key };
165
166 bool verified = false;
167 if constexpr (HasIPAAccumulator<Flavor>) {
168 const size_t HONK_PROOF_LENGTH = Flavor::PROOF_LENGTH_WITHOUT_PUB_INPUTS() - IPA_PROOF_LENGTH;
169 const size_t num_public_inputs = static_cast<size_t>(vk->num_public_inputs);
170 // The extra calculation is for the IPA proof length.
171 BB_ASSERT_EQ(complete_proof.size(),
172 HONK_PROOF_LENGTH + IPA_PROOF_LENGTH + num_public_inputs,
173 "Honk proof has incorrect length while verifying.");
174 const std::ptrdiff_t honk_proof_with_pub_inputs_length =
175 static_cast<std::ptrdiff_t>(HONK_PROOF_LENGTH + num_public_inputs);
176 auto ipa_proof = Proof(complete_proof.begin() + honk_proof_with_pub_inputs_length, complete_proof.end());
177 auto tube_honk_proof =
178 Proof(complete_proof.begin(), complete_proof.begin() + honk_proof_with_pub_inputs_length);
179 verified = verifier.template verify_proof<RollupIO>(complete_proof, ipa_proof).result;
180 } else {
181 verified = verifier.template verify_proof<DefaultIO>(complete_proof).result;
182 }
183
184 if (verified) {
185 info("Proof verified successfully");
186 } else {
187 info("Proof verification failed");
188 }
189
190 return verified;
191}
192
194{
195 // if the ipa accumulation flag is set we are using the UltraRollupFlavor
196 if (settings.ipa_accumulation) {
197 return _prove<UltraRollupFlavor>(
198 std::move(circuit.bytecode), std::move(witness), std::move(circuit.verification_key));
199 }
200 if (settings.oracle_hash_type == "poseidon2" && !settings.disable_zk) {
201 // if we are not disabling ZK and the oracle hash type is poseidon2, we are using the UltraZKFlavor
202 return _prove<UltraZKFlavor>(
203 std::move(circuit.bytecode), std::move(witness), std::move(circuit.verification_key));
204 }
205 if (settings.oracle_hash_type == "poseidon2" && settings.disable_zk) {
206 // if we are disabling ZK and the oracle hash type is poseidon2, we are using the UltraFlavor
207 return _prove<UltraFlavor>(
208 std::move(circuit.bytecode), std::move(witness), std::move(circuit.verification_key));
209 }
210 if (settings.oracle_hash_type == "keccak" && !settings.disable_zk) {
211 // if we are not disabling ZK and the oracle hash type is keccak, we are using the UltraKeccakZKFlavor
212 return _prove<UltraKeccakZKFlavor>(
213 std::move(circuit.bytecode), std::move(witness), std::move(circuit.verification_key));
214 }
215 if (settings.oracle_hash_type == "keccak" && settings.disable_zk) {
216 return _prove<UltraKeccakFlavor>(
217 std::move(circuit.bytecode), std::move(witness), std::move(circuit.verification_key));
218#ifdef STARKNET_GARAGA_FLAVORS
219 }
220 if (settings.oracle_hash_type == "starknet" && settings.disable_zk) {
221 return _prove<UltraStarknetFlavor>(
222 std::move(circuit.bytecode), std::move(witness), std::move(circuit.verification_key()));
223 }
224 if (settings.oracle_hash_type == "starknet" && !settings.disable_zk) {
225 return _prove<UltraStarknetZKFlavor>(
226 std::move(circuit.bytecode), std::move(witness), std::move(circuit.verification_key()));
227#endif
228 }
229 throw_or_abort("Invalid proving options specified in CircuitProve!");
230}
231
233{
234 std::vector<uint8_t> vk_bytes;
235 std::vector<uint256_t> vk_fields;
236 std::vector<uint8_t> vk_hash_bytes;
237
238 // Helper lambda to compute VK, fields, and hash for a given flavor
239 auto compute_vk_and_fields = [&]<typename Flavor>() {
240 auto proving_key = _compute_proving_key<Flavor>(std::move(circuit.bytecode), {});
241 auto vk = std::make_shared<typename Flavor::VerificationKey>(proving_key->get_precomputed());
242 vk_bytes = to_buffer(*vk);
244 vk_fields = vk->to_field_elements();
245 } else {
246 // For other flavors, we use field elements
247 auto uint256_elements = vk->to_field_elements();
248 vk_fields.reserve(uint256_elements.size());
249 vk_fields.insert(vk_fields.end(), uint256_elements.begin(), uint256_elements.end());
250 }
251 vk_hash_bytes = to_buffer(vk->hash());
252 };
253
254 if (settings.ipa_accumulation) {
255 compute_vk_and_fields.template operator()<UltraRollupFlavor>();
256 } else if (settings.oracle_hash_type == "poseidon2" && !settings.disable_zk) {
257 compute_vk_and_fields.template operator()<UltraZKFlavor>();
258 } else if (settings.oracle_hash_type == "poseidon2" && settings.disable_zk) {
259 compute_vk_and_fields.template operator()<UltraFlavor>();
260 } else if (settings.oracle_hash_type == "keccak" && !settings.disable_zk) {
261 compute_vk_and_fields.template operator()<UltraKeccakZKFlavor>();
262 } else if (settings.oracle_hash_type == "keccak" && settings.disable_zk) {
263 compute_vk_and_fields.template operator()<UltraKeccakFlavor>();
264#ifdef STARKNET_GARAGA_FLAVORS
265 } else if (settings.oracle_hash_type == "starknet" && !settings.disable_zk) {
266 compute_vk_and_fields.template operator()<UltraStarknetZKFlavor>();
267 } else if (settings.oracle_hash_type == "starknet" && settings.disable_zk) {
268 compute_vk_and_fields.template operator()<UltraStarknetFlavor>();
269#endif
270 } else {
271 throw_or_abort("invalid proof type in _write_vk");
272 }
273
274 return { .bytes = std::move(vk_bytes), .fields = std::move(vk_fields), .hash = std::move(vk_hash_bytes) };
275}
276
278{
279 // Parse the circuit to get gate count information
280 auto constraint_system = acir_format::circuit_buf_to_acir_format(std::vector<uint8_t>(circuit.bytecode));
281
282 acir_format::ProgramMetadata metadata = _create_program_metadata<UltraCircuitBuilder>();
283 metadata.collect_gates_per_opcode = include_gates_per_opcode;
284 CircuitStats::Response response;
285 response.num_acir_opcodes = static_cast<uint32_t>(constraint_system.num_acir_opcodes);
286
287 acir_format::AcirProgram program{ std::move(constraint_system) };
288 auto builder = acir_format::create_circuit<UltraCircuitBuilder>(program, metadata);
289 builder.finalize_circuit(/*ensure_nonzero=*/true);
290
291 response.num_gates = static_cast<uint32_t>(builder.get_finalized_total_circuit_size());
292 response.num_gates_dyadic = static_cast<uint32_t>(builder.get_circuit_subgroup_size(response.num_gates));
293 // note: will be empty if collect_gates_per_opcode is false
294 response.gates_per_opcode = std::move(program.constraints.gates_per_opcode);
295
296 return response;
297}
298
300{
301 const bool ipa_accumulation = settings.ipa_accumulation;
302 bool verified = false;
303
304 // if the ipa accumulation flag is set we are using the UltraRollupFlavor
305 if (ipa_accumulation) {
306 verified = _verify<UltraRollupFlavor>(ipa_accumulation, verification_key, public_inputs, proof);
307 } else if (settings.oracle_hash_type == "poseidon2" && !settings.disable_zk) {
308 verified = _verify<UltraZKFlavor>(ipa_accumulation, verification_key, public_inputs, proof);
309 } else if (settings.oracle_hash_type == "poseidon2" && settings.disable_zk) {
310 verified = _verify<UltraFlavor>(ipa_accumulation, verification_key, public_inputs, proof);
311 } else if (settings.oracle_hash_type == "keccak" && !settings.disable_zk) {
312 verified = _verify<UltraKeccakZKFlavor>(ipa_accumulation, verification_key, public_inputs, proof);
313 } else if (settings.oracle_hash_type == "keccak" && settings.disable_zk) {
314 verified = _verify<UltraKeccakFlavor>(ipa_accumulation, verification_key, public_inputs, proof);
315#ifdef STARKNET_GARAGA_FLAVORS
316 } else if (settings.oracle_hash_type == "starknet" && !settings.disable_zk) {
317 verified = _verify<UltraStarknetZKFlavor>(ipa_accumulation, verification_key, public_inputs, proof);
318 } else if (settings.oracle_hash_type == "starknet" && settings.disable_zk) {
319 verified = _verify<UltraStarknetFlavor>(ipa_accumulation, verification_key, public_inputs, proof);
320#endif
321 } else {
322 throw_or_abort("invalid proof type in _verify");
323 }
324
325 return { verified };
326}
327
329{
330 std::vector<bb::fr> fields;
331
332 // Standard UltraHonk flavors
333 auto vk = from_buffer<UltraFlavor::VerificationKey>(verification_key);
334 fields = vk.to_field_elements();
335
336 return { std::move(fields) };
337}
338
340{
342 auto vk = std::make_shared<VK>(from_buffer<VK>(verification_key));
343 std::string contract = settings.disable_zk ? get_honk_solidity_verifier(vk) : get_honk_zk_solidity_verifier(vk);
344
345 return { std::move(contract) };
346}
347
348} // namespace bb::bbapi
#define BB_ASSERT_GTE(left, right,...)
Definition assert.hpp:101
#define BB_ASSERT_EQ(actual, expected,...)
Definition assert.hpp:59
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...
TranscriptParams::DataType DataType
typename TranscriptParams::Proof 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:70
#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_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()
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)
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)
std::shared_ptr< DeciderProvingKey_< Flavor > > _compute_proving_key(std::vector< uint8_t > &&bytecode, std::vector< uint8_t > &&witness)
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)