Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
api_client_ivc.cpp
Go to the documentation of this file.
1#include "api_client_ivc.hpp"
16#include <algorithm>
17#include <sstream>
18#include <stdexcept>
19
20namespace bb {
21namespace { // anonymous namespace
22
32void write_standalone_vk(std::vector<uint8_t> bytecode,
33 const std::filesystem::path& output_path,
34 bool use_structured_trace = true)
35{
36 auto trace_settings = use_structured_trace ? TraceSettings{ AZTEC_TRACE_STRUCTURE } : TraceSettings{};
38 .circuit = { .name = "standalone_circuit", .bytecode = std::move(bytecode) }
39 }.execute({ .trace_settings = trace_settings });
40
41 bool is_stdout = output_path == "-";
42 if (is_stdout) {
43 write_bytes_to_stdout(response.bytes);
44 } else {
45 write_file(output_path / "vk", response.bytes);
46 }
47}
48void write_civc_vk(std::vector<uint8_t> bytecode, const std::filesystem::path& output_dir)
49{
50 // compute the hiding kernel's vk
51 info("ClientIVC: computing IVC vk for hiding kernel circuit");
52 auto response =
53 bbapi::ClientIvcComputeIvcVk{ .circuit{ .bytecode = std::move(bytecode) } }.execute({ .trace_settings = {} });
54 const bool output_to_stdout = output_dir == "-";
55 if (output_to_stdout) {
56 write_bytes_to_stdout(response.bytes);
57 } else {
58 write_file(output_dir / "vk", response.bytes);
59 }
60}
61} // anonymous namespace
62
63void ClientIVCAPI::prove(const Flags& flags,
64 const std::filesystem::path& input_path,
65 const std::filesystem::path& output_dir)
66{
67 BB_BENCH_NAME("ClientIVCAPI::prove");
68 bbapi::BBApiRequest request;
70
71 bbapi::ClientIvcStart{ .num_circuits = raw_steps.size() }.execute(request);
72 info("ClientIVC: starting with ",
73 raw_steps.size(),
74 " circuits",
75 bbapi::USE_SUMCHECK_IVC ? " (using SumcheckClientIVC)" : "");
76 for (const auto& step : raw_steps) {
78 .circuit = { .name = step.function_name, .bytecode = step.bytecode, .verification_key = step.vk }
79 }.execute(request);
80
81 // NOLINTNEXTLINE(bugprone-unchecked-optional-access): we know the optional has been set here.
82 info("ClientIVC: accumulating " + step.function_name);
83 bbapi::ClientIvcAccumulate{ .witness = step.witness }.execute(request);
84 }
85
86 auto proof = bbapi::ClientIvcProve{}.execute(request).proof;
87
88 // We'd like to use the `write` function that UltraHonkAPI uses, but there are missing functions for creating
89 // std::string representations of vks that don't feel worth implementing
90 const bool output_to_stdout = output_dir == "-";
91
92 const auto write_proof = [&]() {
93 const auto buf = to_buffer(proof.to_field_elements());
94 if (output_to_stdout) {
95 vinfo("writing ClientIVC proof to stdout");
97 } else {
98 vinfo("writing ClientIVC proof in directory ", output_dir);
99 write_file(output_dir / "proof", buf);
100 }
101 };
102
103 write_proof();
104
105 if (flags.write_vk) {
106 vinfo("writing ClientIVC vk in directory ", output_dir);
107 // write CIVC vk using the bytecode of the hiding circuit (the last step of the execution)
108 write_civc_vk(raw_steps[raw_steps.size() - 1].bytecode, output_dir);
109 }
110}
111
112bool ClientIVCAPI::verify([[maybe_unused]] const Flags& flags,
113 [[maybe_unused]] const std::filesystem::path& public_inputs_path,
114 const std::filesystem::path& proof_path,
115 const std::filesystem::path& vk_path)
116{
117 BB_BENCH_NAME("ClientIVCAPI::verify");
118 auto proof_fields = many_from_buffer<fr>(read_file(proof_path));
119 auto proof = ClientIVC::Proof::from_field_elements(proof_fields);
120
121 auto vk_buffer = read_file(vk_path);
122
123 auto response = bbapi::ClientIvcVerify{ .proof = std::move(proof), .vk = std::move(vk_buffer) }.execute();
124 return response.valid;
125}
126
127// WORKTODO(bbapi) remove this
128bool ClientIVCAPI::prove_and_verify(const std::filesystem::path& input_path)
129{
132
134 // Construct the hiding kernel as the final step of the IVC
135
136 auto proof = ivc->prove();
137 const bool verified = ClientIVC::verify(proof, ivc->get_vk());
138 return verified;
139}
140
141void ClientIVCAPI::gates(const Flags& flags, const std::filesystem::path& bytecode_path)
142{
143 BB_BENCH_NAME("ClientIVCAPI::gates");
144 gate_count_for_ivc(bytecode_path, flags.include_gates_per_opcode);
145}
146
147void ClientIVCAPI::write_solidity_verifier([[maybe_unused]] const Flags& flags,
148 [[maybe_unused]] const std::filesystem::path& output_path,
149 [[maybe_unused]] const std::filesystem::path& vk_path)
150{
151 BB_BENCH_NAME("ClientIVCAPI::write_solidity_verifier");
152 throw_or_abort("API function contract not implemented");
153}
154
155bool ClientIVCAPI::check_precomputed_vks(const Flags& flags, const std::filesystem::path& input_path)
156{
157 BB_BENCH_NAME("ClientIVCAPI::check_precomputed_vks");
158 bbapi::BBApiRequest request;
160
161 bool check_failed = false;
162 for (auto& step : raw_steps) {
163 if (step.vk.empty()) {
164 info("FAIL: Expected precomputed vk for function ", step.function_name);
165 return false;
166 }
168 .circuit = { .name = step.function_name, .bytecode = step.bytecode, .verification_key = step.vk }
169 }.execute();
170
171 if (!response.valid) {
172 if (!flags.update_inputs) {
173 return false;
174 }
175 step.vk = response.actual_vk;
176 check_failed = true;
177 }
178 }
179 if (check_failed) {
181 return false;
182 }
183 return true;
184}
185
187 const std::filesystem::path& bytecode_path,
188 const std::filesystem::path& output_path)
189{
190 BB_BENCH_NAME("ClientIVCAPI::write_vk");
191 auto bytecode = get_bytecode(bytecode_path);
192 if (flags.verifier_type == "ivc") {
193 write_civc_vk(bytecode, output_path);
194 } else if (flags.verifier_type == "standalone") {
195 write_standalone_vk(bytecode, output_path);
196 } else if (flags.verifier_type == "standalone_hiding") {
197 // write the VK for the hiding kernel which DOES NOT utilize a structured trace
198 write_standalone_vk(bytecode, output_path, false);
199 } else {
200 const std::string msg = std::string("Can't write vk for verifier type ") + flags.verifier_type;
201 throw_or_abort(msg);
202 }
203}
204
205bool ClientIVCAPI::check([[maybe_unused]] const Flags& flags,
206 [[maybe_unused]] const std::filesystem::path& bytecode_path,
207 [[maybe_unused]] const std::filesystem::path& witness_path)
208{
209 throw_or_abort("API function check_witness not implemented");
210 return false;
211}
212
213void gate_count_for_ivc(const std::string& bytecode_path, bool include_gates_per_opcode)
214{
215 BB_BENCH_NAME("gate_count_for_ivc");
216 // All circuit reports will be built into the std::string below
217 std::string functions_string = "{\"functions\": [\n ";
218
219 bbapi::BBApiRequest request{ .trace_settings = { AZTEC_TRACE_STRUCTURE } };
220
221 auto bytecode = get_bytecode(bytecode_path);
222 auto response = bbapi::ClientIvcStats{ .circuit = { .name = "ivc_circuit", .bytecode = std::move(bytecode) },
223 .include_gates_per_opcode = include_gates_per_opcode }
224 .execute(request);
225
226 // Build the circuit report. It always has one function, corresponding to the ACIR constraint systems.
227 // NOTE: can be reconsidered
228 std::string gates_per_opcode_str;
229 if (include_gates_per_opcode && !response.gates_per_opcode.empty()) {
230 for (size_t j = 0; j < response.gates_per_opcode.size(); j++) {
231 gates_per_opcode_str += std::to_string(response.gates_per_opcode[j]);
232 if (j != response.gates_per_opcode.size() - 1) {
233 gates_per_opcode_str += ",";
234 }
235 }
236 }
237 auto result_string = format(
238 "{\n \"acir_opcodes\": ",
239 response.acir_opcodes,
240 ",\n \"circuit_size\": ",
241 response.circuit_size,
242 (include_gates_per_opcode ? format(",\n \"gates_per_opcode\": [", gates_per_opcode_str, "]") : ""),
243 "\n }");
244 functions_string = format(functions_string, result_string);
245 std::cout << format(functions_string, "\n]}");
246}
247
248} // namespace bb
void write_bytes_to_stdout(const std::vector< uint8_t > &data)
Writes raw bytes of the vector to stdout.
Definition log.hpp:21
#define BB_BENCH_NAME(name)
Definition bb_bench.hpp:218
void gates(const Flags &flags, const std::filesystem::path &bytecode_path) override
bool check(const Flags &flags, const std::filesystem::path &bytecode_path, const std::filesystem::path &witness_path) override
bool verify(const Flags &flags, const std::filesystem::path &public_inputs_path, const std::filesystem::path &proof_path, const std::filesystem::path &vk_path) override
bool check_precomputed_vks(const Flags &flags, const std::filesystem::path &input_path)
bool prove_and_verify(const std::filesystem::path &input_path)
void write_solidity_verifier(const Flags &flags, const std::filesystem::path &output_path, const std::filesystem::path &vk_path) override
void prove(const Flags &flags, const std::filesystem::path &input_path, const std::filesystem::path &output_dir)
void write_vk(const Flags &flags, const std::filesystem::path &bytecode_path, const std::filesystem::path &output_path) override
static bool verify(const Proof &proof, const VerificationKey &vk)
std::string format(Args... args)
Definition log.hpp:21
#define vinfo(...)
Definition log.hpp:79
void info(Args... args)
Definition log.hpp:74
uint8_t const * buf
Definition data_store.hpp:9
std::vector< uint8_t > get_bytecode(const std::string &bytecodePath)
bool USE_SUMCHECK_IVC
Global flag to control whether to use SumcheckClientIVC instead of ClientIVC.
Entry point for Barretenberg command-line interface.
void gate_count_for_ivc(const std::string &bytecode_path, bool include_gates_per_opcode)
std::vector< uint8_t > read_file(const std::string &filename, size_t bytes=0)
Definition file_io.hpp:29
void write_file(const std::string &filename, std::vector< uint8_t > const &data)
Definition file_io.hpp:58
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
std::string to_string(bb::avm2::ValueTag tag)
std::vector< uint8_t > to_buffer(T const &value)
bool include_gates_per_opcode
Definition api.hpp:23
bool write_vk
Definition api.hpp:22
bool update_inputs
Definition api.hpp:26
std::string verifier_type
Definition api.hpp:20
static Proof from_field_elements(const std::vector< ClientIVC::FF > &fields)
static void compress_and_save(std::vector< PrivateExecutionStepRaw > &&steps, const std::filesystem::path &output_path)
static std::vector< PrivateExecutionStepRaw > load_and_decompress(const std::filesystem::path &input_path)
std::shared_ptr< ClientIVC > accumulate()
void parse(std::vector< PrivateExecutionStepRaw > &&steps)
std::string name
Human-readable name for the circuit.
std::string name
Human-readable name for the circuit.
std::vector< uint8_t > bytecode
Serialized bytecode representation of the circuit.
Accumulate the previously loaded circuit into the IVC proof.
std::vector< uint8_t > witness
Serialized witness data for the last loaded circuit.
Verify that a precomputed verification key matches the circuit.
CircuitInput circuit
Circuit with its precomputed verification key.
Compute IVC verification key for the complete proof.
Compute standalone verification key for a circuit.
Load a circuit into the ClientIVC instance for accumulation.
CircuitInput circuit
Circuit to be loaded with its bytecode and verification key.
ClientIVC::Proof proof
Complete IVC proof for all accumulated circuits.
Generate a proof for all accumulated circuits.
Response execute(BBApiRequest &request) &&
Initialize a new ClientIVC instance for incremental proof accumulation.
Get gate counts for a circuit.
CircuitInputNoVK circuit
The circuit to analyze.
Verify a ClientIVC proof with its verification key.
ClientIVC::Proof proof
The ClientIVC proof to verify.
void throw_or_abort(std::string const &err)