Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
api_chonk.test.cpp
Go to the documentation of this file.
1#include "api_chonk.hpp"
18#include <chrono>
19#include <cstddef>
20#include <cstdlib>
21#include <filesystem>
22#include <gtest/gtest.h>
23#include <sstream>
24#include <string_view>
25
26using namespace bb;
27
28namespace {
29// Create a unique temporary directory for each test run
30// Uniqueness needed because tests are run in parallel and write to same file names.
31std::filesystem::path get_test_dir(const std::string_view& test_name)
32{
33 std::filesystem::path temp_dir = "tmp_api_chonk_test";
34 std::filesystem::create_directories(temp_dir);
35 std::filesystem::create_directories(temp_dir / test_name);
36 return temp_dir / test_name;
37}
38
39// TODO(https://github.com/AztecProtocol/barretenberg/issues/1509): expand these test to accomodate a more realistic
40// Chonk flow in order to re-enable the ProveAndVerify* tests in this file
41void create_test_private_execution_steps(const std::filesystem::path& output_path)
42{
43 using namespace acir_format;
44
45 // First create a simple app circuit
46 auto [app_bytecode, app_witness_data] = acir_bincode_mocks::create_simple_circuit_bytecode();
47
48 // Get the VK for the app circuit
49 bbapi::BBApiRequest request;
50
51 auto app_vk_response =
52 bbapi::ChonkComputeStandaloneVk{ .circuit = { .name = "app_circuit", .bytecode = app_bytecode } }.execute();
53
54 // Decode VK to get field elements
55 auto app_vk = from_buffer<MegaFlavor::VerificationKey>(app_vk_response.bytes);
56 auto app_vk_fields = app_vk.to_field_elements();
57
58 // Now create a kernel circuit that verifies the app circuit
59 auto kernel_bytecode = acir_bincode_mocks::create_simple_kernel(app_vk_fields.size(), /*is_init_kernel=*/true);
60 auto kernel_witness_data = acir_bincode_mocks::create_kernel_witness(app_vk_fields);
61
62 auto kernel_vk_response = bbapi::ChonkComputeStandaloneVk{
63 .circuit = { .name = "kernel_circuit", .bytecode = kernel_bytecode }
64 }.execute();
65 auto kernel_vk = kernel_vk_response.bytes;
66
67 // Create PrivateExecutionStepRaw for the kernel
69 raw_steps.push_back({ .bytecode = app_bytecode,
70 .witness = app_witness_data,
71 .vk = app_vk_response.bytes,
72 .function_name = "app_function" });
73 raw_steps.push_back({ .bytecode = kernel_bytecode,
74 .witness = kernel_witness_data,
75 .vk = kernel_vk,
76 .function_name = "kernel_function" });
77
79}
80} // namespace
81
82class ChonkAPITests : public ::testing::Test {
83 protected:
85
86 void SetUp() override
87 {
88 const auto* info = ::testing::UnitTest::GetInstance()->current_test_info();
89 test_dir = get_test_dir(info->name());
90 }
91
92 void TearDown() override
93 {
94 if (std::filesystem::exists(test_dir)) {
95 std::filesystem::remove_all(test_dir);
96 }
97 }
98
99 std::filesystem::path test_dir;
100};
101
102namespace bb {
103std::vector<uint8_t> compress(const std::vector<uint8_t>& input);
104} // namespace bb
105
106// Helper to get an IVC verification key for testing
107Chonk::MegaVerificationKey get_ivc_vk(const std::filesystem::path& test_dir)
108{
109 auto [app_bytecode, app_witness_data] = acir_bincode_mocks::create_simple_circuit_bytecode();
110 bbapi::BBApiRequest request;
111 auto app_vk_response =
112 bbapi::ChonkComputeStandaloneVk{ .circuit = { .name = "app_circuit", .bytecode = app_bytecode } }.execute();
113
114 // Decode to get the field count
115 auto app_vk = from_buffer<MegaFlavor::VerificationKey>(app_vk_response.bytes);
116 size_t vk_field_count = app_vk.to_field_elements().size();
117
118 // Create a kernel circuit with the correct VK size
119 auto bytecode = acir_bincode_mocks::create_simple_kernel(vk_field_count, /*is_init_kernel=*/false);
120 std::filesystem::path bytecode_path = test_dir / "circuit.acir";
121 write_file(bytecode_path, bb::compress(bytecode));
122
123 ChonkAPI::Flags write_vk_flags;
124 write_vk_flags.verifier_type = "ivc";
125
126 ChonkAPI api;
127 api.write_vk(write_vk_flags, bytecode_path, test_dir);
128
129 auto buffer = read_file(test_dir / "vk");
130 return from_buffer<Chonk::MegaVerificationKey>(buffer);
131};
132
133// Test the ChonkAPI::prove flow, making sure --write_vk
134// returns the same output as our ivc VK generation.
135TEST_F(ChonkAPITests, DISABLED_ProveAndVerifyFileBasedFlow)
136{
137 auto ivc_vk = get_ivc_vk(test_dir);
138
139 // Create test input file
140 std::filesystem::path input_path = test_dir / "input.msgpack";
141 create_test_private_execution_steps(input_path);
142
143 std::filesystem::path output_dir = test_dir / "output";
144 std::filesystem::create_directories(output_dir);
145
146 // Helper lambda to create proof and VK files
147 auto create_proof_and_vk = [&]() {
148 ChonkAPI::Flags flags;
149 flags.write_vk = true;
150 ChonkAPI api;
151 api.prove(flags, input_path, output_dir);
152 };
153
154 // Helper lambda to verify VK equivalence
155 auto verify_vk_equivalence = [&](const std::filesystem::path& vk1_path, const Chonk::MegaVerificationKey& vk2) {
156 auto vk1_data = read_file(vk1_path);
157 auto vk1 = from_buffer<Chonk::MegaVerificationKey>(vk1_data);
158 ASSERT_EQ(vk1, vk2);
159 };
160
161 // Helper lambda to verify proof
162 auto verify_proof = [&]() {
163 std::filesystem::path proof_path = output_dir / "proof";
164 std::filesystem::path vk_path = output_dir / "vk";
165 std::filesystem::path public_inputs_path; // Not used for Chonk
166
167 ChonkAPI::Flags flags;
168 ChonkAPI verify_api;
169 return verify_api.verify(flags, public_inputs_path, proof_path, vk_path);
170 };
171
172 // Execute test steps
173 create_proof_and_vk();
174 verify_vk_equivalence(output_dir / "vk", ivc_vk);
175 // Test verify command
176 EXPECT_TRUE(verify_proof());
177}
178
179// WORKTODO(bbapi): Expand on this.
180TEST_F(ChonkAPITests, WriteVkFieldsSmokeTest)
181{
182 // Create a simple circuit bytecode
184
185 // Compress and write bytecode to file
186 std::filesystem::path bytecode_path = test_dir / "circuit.acir";
187 write_file(bytecode_path, bb::compress(bytecode));
188
189 // Test write_vk
190 ChonkAPI::Flags flags;
191 flags.verifier_type = "standalone";
192
193 ChonkAPI api;
194 api.write_vk(flags, bytecode_path, test_dir);
195
196 // Verify the binary VK file was created
197 EXPECT_TRUE(std::filesystem::exists(test_dir / "vk"));
198}
199
200TEST_F(ChonkAPITests, WriteIVCVkSmokeTest)
201{
202 // Create a simple circuit bytecode
204
205 // Compress and write bytecode to file
206 std::filesystem::path bytecode_path = test_dir / "circuit.acir";
207 write_file(bytecode_path, bb::compress(bytecode));
208
209 // Set flags for VK generation
210 ChonkAPI::Flags flags;
211 flags.verifier_type = "ivc";
212
213 // Call write_vk
214 ChonkAPI api;
215 api.write_vk(flags, bytecode_path, test_dir);
216
217 // Check that VK file exists and is non-empty
218 std::filesystem::path vk_path = test_dir / "vk";
219 ASSERT_TRUE(std::filesystem::exists(vk_path));
220 auto vk_data = read_file(vk_path);
221 ASSERT_FALSE(vk_data.empty());
222}
223
224// TODO(https://github.com/AztecProtocol/barretenberg/issues/1461): Make this test actually test # gates
225TEST_F(ChonkAPITests, GatesCommandSmokeTest)
226{
227 // Create a simple circuit bytecode
229
230 // Write compressed bytecode to file
231 std::filesystem::path bytecode_path = test_dir / "circuit.acir";
232 write_file(bytecode_path, bb::compress(bytecode));
233
234 ChonkAPI::Flags flags;
235 flags.include_gates_per_opcode = true;
236
237 // Redirect stdout to a stringstream
238 std::ostringstream captured_output;
239 std::streambuf* old_cout = std::cout.rdbuf(captured_output.rdbuf());
240
241 ChonkAPI api;
242 api.gates(flags, bytecode_path);
243
244 // Restore stdout
245 std::cout.rdbuf(old_cout);
246 std::string output = captured_output.str();
247
248 // We rudimentarily output to this pattern:
249 // {"functions": [
250 // {
251 // "acir_opcodes": 1,
252 // "circuit_size": *,
253 // "gates_per_opcode": [*]
254 // }
255 // ]}
256 EXPECT_NE(output.find("\"functions\": ["), std::string::npos);
257 EXPECT_NE(output.find("\"acir_opcodes\": 1"), std::string::npos);
258 EXPECT_NE(output.find("\"circuit_size\": "), std::string::npos);
259 EXPECT_NE(output.find("\"gates_per_opcode\": ["), std::string::npos);
260}
261
262// Test prove_and_verify for our example IVC flow.
263TEST_F(ChonkAPITests, DISABLED_ProveAndVerifyCommand)
264{
265 // Create test input file
266 std::filesystem::path input_path = test_dir / "input.msgpack";
267 create_test_private_execution_steps(input_path);
268
269 ChonkAPI api;
270 EXPECT_TRUE(api.prove_and_verify(input_path));
271}
272
273// Check a case where precomputed VKs match
274TEST_F(ChonkAPITests, CheckPrecomputedVks)
275{
276 // Create test input file with precomputed VKs
277 std::filesystem::path input_path = test_dir / "input_with_vks.msgpack";
278 create_test_private_execution_steps(input_path);
279
280 ChonkAPI api;
281 EXPECT_TRUE(api.check_precomputed_vks(ChonkAPI::Flags{}, input_path));
282}
283
284// Check a case where precomputed VKs don't match
285TEST_F(ChonkAPITests, CheckPrecomputedVksMismatch)
286{
287 using namespace acir_format;
288
289 // Create a simple circuit
291
292 bbapi::BBApiRequest request;
293 auto vk_response =
294 bbapi::ChonkComputeStandaloneVk{ .circuit = { .name = "simple_circuit", .bytecode = bytecode } }.execute();
295 size_t vk_size = from_buffer<MegaFlavor::VerificationKey>(vk_response.bytes).to_field_elements().size();
296
297 // Create a WRONG verification key (use a different circuit)
298 auto different_bytecode = acir_bincode_mocks::create_simple_kernel(vk_size, /*is_init_kernel=*/true);
299 auto vk_response2 = bbapi::ChonkComputeStandaloneVk{
300 .circuit = { .name = "different_circuit", .bytecode = different_bytecode }
301 }.execute();
302 auto vk = vk_response2.bytes;
303
304 // Create PrivateExecutionStepRaw with wrong VK
307 step.bytecode = bytecode;
308 step.witness = witness_data;
309 step.vk = std::move(vk); // Wrong VK
310 step.function_name = "test_function";
311 raw_steps.push_back(std::move(step));
312
313 // Write to file using compress_and_save
314 std::filesystem::path input_path = test_dir / "input_wrong_vks.msgpack";
316
317 // Should fail because VK doesn't match
318 ChonkAPI api;
319 bool result = api.check_precomputed_vks(ChonkAPI::Flags{}, input_path);
320 EXPECT_FALSE(result);
321
322 // Check with --vk_policy=rewrite should still fail but update the VK in the input.
323 result = api.check_precomputed_vks(ChonkAPI::Flags{ .vk_policy = "rewrite" }, input_path);
324 EXPECT_FALSE(result);
325
326 // Check again and it should succeed with the updated VK.
327 result = api.check_precomputed_vks(ChonkAPI::Flags{}, input_path);
328 EXPECT_TRUE(result);
329}
Chonk::MegaVerificationKey get_ivc_vk(const std::filesystem::path &test_dir)
TEST_F(ChonkAPITests, DISABLED_ProveAndVerifyFileBasedFlow)
std::shared_ptr< Napi::ThreadSafeFunction > bytecode
static void SetUpTestSuite()
std::filesystem::path test_dir
void TearDown() override
void SetUp() override
CLI API for Chonk (Aztec's client-side proving).
Definition api_chonk.hpp:33
bool prove_and_verify(const std::filesystem::path &input_path)
Test/debug function: prove and verify in one call (bypasses serialization).
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
Verify a Chonk proof against a verification key.
void write_vk(const Flags &flags, const std::filesystem::path &bytecode_path, const std::filesystem::path &output_path) override
Compute and write a verification key.
void prove(const Flags &flags, const std::filesystem::path &input_path, const std::filesystem::path &output_dir)
Main production entry point: generate a Chonk proof from private execution steps.
Definition api_chonk.cpp:70
void gates(const Flags &flags, const std::filesystem::path &bytecode_path) override
Output gate count statistics for a circuit.
bool check_precomputed_vks(const Flags &flags, const std::filesystem::path &input_path)
Validate that precomputed VKs in ivc-inputs.msgpack match computed VKs.
Base Native verification key class.
Definition flavor.hpp:141
void info(Args... args)
Definition log.hpp:89
uint8_t buffer[RANDOM_BUFFER_SIZE]
Definition engine.cpp:34
std::pair< std::vector< uint8_t >, std::vector< uint8_t > > create_simple_circuit_bytecode(size_t num_constraints=1)
Helper function to create a minimal circuit bytecode and witness for testing.
std::vector< uint8_t > create_simple_kernel(size_t vk_size, bool is_init_kernel)
Create a simple kernel circuit for IVC testing.
std::vector< uint8_t > create_kernel_witness(const std::vector< bb::fr > &app_vk_fields)
Create a kernel witness for IVC testing.
std::filesystem::path bb_crs_path()
void init_file_crs_factory(const std::filesystem::path &path)
Entry point for Barretenberg command-line interface.
Definition api.hpp:5
std::vector< uint8_t > compress(const std::vector< uint8_t > &input)
Save modified ivc-inputs.msgpack when VKs are rewritten.
std::vector< uint8_t > read_file(const std::string &filename, size_t bytes=0)
Definition file_io.hpp:30
void write_file(const std::string &filename, std::vector< uint8_t > const &data)
Definition file_io.hpp:59
VerifierCommitmentKey< Curve > vk
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
bool include_gates_per_opcode
Definition api.hpp:24
bool write_vk
Definition api.hpp:23
std::string verifier_type
Definition api.hpp:21
std::string vk_policy
Definition api.hpp:27
This is the msgpack encoding of the objects returned by the following typescript: const stepToStruct ...
static void compress_and_save(std::vector< PrivateExecutionStepRaw > &&steps, const std::filesystem::path &output_path)
Compute standalone verification key for a circuit.
std::string name
Human-readable name for the circuit.