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
31void write_standalone_vk(const std::string& output_format,
32 const std::filesystem::path& bytecode_path,
33 const std::filesystem::path& output_path)
34{
35 auto bytecode = get_bytecode(bytecode_path);
37 .circuit = { .name = "standalone_circuit", .bytecode = std::move(bytecode) }
38 }.execute();
39
40 bool wrote_file = false;
41 bool is_stdout = output_path == "-";
42 auto write_fn = [&](const std::filesystem::path& path, const auto& data) {
43 if (is_stdout) {
45 } else {
46 write_file(path, data);
47 }
48 };
49 if (output_format == "bytes_and_fields" && is_stdout) {
50 throw_or_abort("Cannot write to stdout in bytes_and_fields format.");
51 }
52 if (output_format == "bytes" || output_format == "bytes_and_fields") {
53 write_fn(output_path / "vk", response.bytes);
54 wrote_file = true;
55 }
56 if (output_format == "fields" || output_format == "bytes_and_fields") {
57 std::string json = field_elements_to_json(response.fields);
58 write_fn(output_path / "vk_fields.json", std::vector<uint8_t>(json.begin(), json.end()));
59 wrote_file = true;
60 }
61 if (!wrote_file) {
62 throw_or_abort("Unsupported output format for standalone vk: " + output_format);
63 }
64}
65
66std::vector<uint8_t> write_civc_vk(const std::string& output_format,
67 std::vector<uint8_t> bytecode,
68 const std::filesystem::path& output_dir)
69{
70 if (output_format != "bytes") {
71 throw_or_abort("Unsupported output format for ClientIVC vk: " + output_format);
72 }
73 // compute the hiding kernel's vk
74 info("ClientIVC: computing IVC vk for hiding kernel circuit");
75 auto response = bbapi::ClientIvcComputeIvcVk{
76 .circuit{ .name = "standalone_circuit", .bytecode = std::move(bytecode) }
77 }.execute({ .trace_settings = {} });
78 auto civc_vk_bytes = response.bytes;
79 const bool output_to_stdout = output_dir == "-";
80 if (output_to_stdout) {
81 write_bytes_to_stdout(civc_vk_bytes);
82 } else {
83 write_file(output_dir / "vk", civc_vk_bytes);
84 }
85 return civc_vk_bytes;
86}
87
88} // anonymous namespace
89
90void ClientIVCAPI::prove(const Flags& flags,
91 const std::filesystem::path& input_path,
92 const std::filesystem::path& output_dir)
93{
94
95 bbapi::BBApiRequest request;
97
98 bbapi::ClientIvcStart{ .num_circuits = raw_steps.size() }.execute(request);
99 info("ClientIVC: starting with ", raw_steps.size(), " circuits");
100 for (const auto& step : raw_steps) {
102 .circuit = { .name = step.function_name, .bytecode = step.bytecode, .verification_key = step.vk }
103 }.execute(request);
104
105 // NOLINTNEXTLINE(bugprone-unchecked-optional-access): we know the optional has been set here.
106 info("ClientIVC: accumulating " + step.function_name);
107 bbapi::ClientIvcAccumulate{ .witness = step.witness }.execute(request);
108 }
109
110 auto proof = bbapi::ClientIvcProve{}.execute(request).proof;
111
112 // We'd like to use the `write` function that UltraHonkAPI uses, but there are missing functions for creating
113 // std::string representations of vks that don't feel worth implementing
114 const bool output_to_stdout = output_dir == "-";
115
116 const auto write_proof = [&]() {
117 const auto buf = to_buffer(proof);
118 if (output_to_stdout) {
119 vinfo("writing ClientIVC proof to stdout");
121 } else {
122 vinfo("writing ClientIVC proof in directory ", output_dir);
123 proof.to_file_msgpack(output_dir / "proof");
124 }
125 };
126
127 write_proof();
128
129 if (flags.write_vk) {
130 vinfo("writing ClientIVC vk in directory ", output_dir);
131 // we get the bytecode of the hiding circuit (the last step of the execution)
132 auto vk_buf = write_civc_vk("bytes", raw_steps[raw_steps.size() - 1].bytecode, output_dir);
133 auto vk = from_buffer<ClientIVC::VerificationKey>(vk_buf);
134 }
135}
136
137bool ClientIVCAPI::verify([[maybe_unused]] const Flags& flags,
138 [[maybe_unused]] const std::filesystem::path& public_inputs_path,
139 const std::filesystem::path& proof_path,
140 const std::filesystem::path& vk_path)
141{
142 auto proof = ClientIVC::Proof::from_file_msgpack(proof_path);
143 auto vk_buffer = read_file(vk_path);
144 auto response = bbapi::ClientIvcVerify{ .proof = std::move(proof), .vk = std::move(vk_buffer) }.execute();
145 return response.valid;
146}
147
148// WORKTODO(bbapi) remove this
149bool ClientIVCAPI::prove_and_verify(const std::filesystem::path& input_path)
150{
153
155 // Construct the hiding kernel as the final step of the IVC
156
157 const bool verified = ivc->prove_and_verify();
158 return verified;
159}
160
161void ClientIVCAPI::gates(const Flags& flags, const std::filesystem::path& bytecode_path)
162{
163 gate_count_for_ivc(bytecode_path, flags.include_gates_per_opcode);
164}
165
166void ClientIVCAPI::write_solidity_verifier([[maybe_unused]] const Flags& flags,
167 [[maybe_unused]] const std::filesystem::path& output_path,
168 [[maybe_unused]] const std::filesystem::path& vk_path)
169{
170 throw_or_abort("API function contract not implemented");
171}
172
173bool ClientIVCAPI::check_precomputed_vks(const Flags& flags, const std::filesystem::path& input_path)
174{
175 bbapi::BBApiRequest request;
177
178 bool check_failed = false;
179 for (auto& step : raw_steps) {
180 if (step.vk.empty()) {
181 info("FAIL: Expected precomputed vk for function ", step.function_name);
182 return false;
183 }
185 .circuit = { .name = step.function_name, .bytecode = step.bytecode, .verification_key = step.vk }
186 }.execute();
187
188 if (!response.valid) {
189 if (!flags.update_inputs) {
190 return false;
191 }
192 step.vk = response.actual_vk;
193 check_failed = true;
194 }
195 }
196 if (check_failed) {
198 return false;
199 }
200 return true;
201}
202
204 const std::filesystem::path& bytecode_path,
205 const std::filesystem::path& output_path)
206{
207
208 if (flags.verifier_type == "ivc") {
209 auto bytecode = get_bytecode(bytecode_path);
210 write_civc_vk(flags.output_format, bytecode, output_path);
211 } else if (flags.verifier_type == "standalone") {
212 write_standalone_vk(flags.output_format, bytecode_path, output_path);
213 } else {
214 const std::string msg = std::string("Can't write vk for verifier type ") + flags.verifier_type;
215 throw_or_abort(msg);
216 }
217}
218
219bool ClientIVCAPI::check([[maybe_unused]] const Flags& flags,
220 [[maybe_unused]] const std::filesystem::path& bytecode_path,
221 [[maybe_unused]] const std::filesystem::path& witness_path)
222{
223 throw_or_abort("API function check_witness not implemented");
224 return false;
225}
226
227void gate_count_for_ivc(const std::string& bytecode_path, bool include_gates_per_opcode)
228{
229 // All circuit reports will be built into the std::string below
230 std::string functions_string = "{\"functions\": [\n ";
231
232 bbapi::BBApiRequest request{ .trace_settings = { AZTEC_TRACE_STRUCTURE } };
233
234 auto bytecode = get_bytecode(bytecode_path);
235 auto response = bbapi::ClientIvcStats{ .circuit = { .name = "ivc_circuit", .bytecode = std::move(bytecode) },
236 .include_gates_per_opcode = include_gates_per_opcode }
237 .execute(request);
238
239 // Build the circuit report. It always has one function, corresponding to the ACIR constraint systems.
240 // NOTE: can be reconsidered
241 std::string gates_per_opcode_str;
242 if (include_gates_per_opcode && !response.gates_per_opcode.empty()) {
243 for (size_t j = 0; j < response.gates_per_opcode.size(); j++) {
244 gates_per_opcode_str += std::to_string(response.gates_per_opcode[j]);
245 if (j != response.gates_per_opcode.size() - 1) {
246 gates_per_opcode_str += ",";
247 }
248 }
249 }
250 auto result_string = format(
251 "{\n \"acir_opcodes\": ",
252 response.acir_opcodes,
253 ",\n \"circuit_size\": ",
254 response.circuit_size,
255 (include_gates_per_opcode ? format(",\n \"gates_per_opcode\": [", gates_per_opcode_str, "]") : ""),
256 "\n }");
257 functions_string = format(functions_string, result_string);
258 std::cout << format(functions_string, "\n]}");
259}
260
261void write_arbitrary_valid_client_ivc_proof_and_vk_to_file(const std::filesystem::path& output_dir)
262{
263
264 PrivateFunctionExecutionMockCircuitProducer circuit_producer{ /*num_app_circuits=*/1 };
265 const size_t NUM_CIRCUITS = circuit_producer.total_num_circuits;
266 ClientIVC ivc{ NUM_CIRCUITS, { AZTEC_TRACE_STRUCTURE } };
267
268 // Construct and accumulate a series of mocked private function execution circuits
269 for (size_t idx = 0; idx < NUM_CIRCUITS; ++idx) {
270 circuit_producer.construct_and_accumulate_next_circuit(ivc);
271 }
272
273 ClientIVC::Proof proof = ivc.prove();
274
275 // Write the proof and verification keys into the working directory in 'binary' format
276 vinfo("writing ClientIVC proof and vk...");
277 proof.to_file_msgpack(output_dir / "proof");
278
279 write_file(output_dir / "vk", to_buffer(ivc.get_vk()));
280}
281
282} // 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
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
The IVC scheme used by the aztec client for private function execution.
std::string format(Args... args)
Definition log.hpp:20
void vinfo(Args... args)
Definition log.hpp:76
void info(Args... args)
Definition log.hpp:70
const std::vector< FF > data
uint8_t const * buf
Definition data_store.hpp:9
std::vector< uint8_t > get_bytecode(const std::string &bytecodePath)
Entry point for Barretenberg command-line interface.
void write_arbitrary_valid_client_ivc_proof_and_vk_to_file(const std::filesystem::path &output_dir)
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
std::string field_elements_to_json(const std::vector< Fr > &fields)
Definition file_io.hpp:90
VerifierCommitmentKey< Curve > vk
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:24
bool write_vk
Definition api.hpp:23
bool update_inputs
Definition api.hpp:26
std::string verifier_type
Definition api.hpp:21
std::string output_format
Definition api.hpp:20
A full proof for the IVC scheme containing a Mega proof showing correctness of the hiding circuit (wh...
void to_file_msgpack(const std::string &filename) const
static Proof from_file_msgpack(const std::string &filename)
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.
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)