Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
api_ultra_honk.test.cpp
Go to the documentation of this file.
1#include "api_ultra_honk.hpp"
11#include <chrono>
12#include <cstddef>
13#include <cstdlib>
14#include <filesystem>
15#include <gtest/gtest.h>
16#include <math.h>
17#include <sstream>
18#include <string_view>
19
20namespace bb {
21std::vector<uint8_t> compress(const std::vector<uint8_t>& input);
22std::vector<uint8_t> decompress(const void* bytes, size_t size);
23} // namespace bb
24
25using namespace bb;
26
27namespace {
28// Create a unique temporary directory for each test run
29// Uniqueness needed because tests are run in parallel and write to same file names.
30std::filesystem::path get_test_dir(const std::string_view& test_name)
31{
32 std::filesystem::path temp_dir = "tmp_api_ultra_honk_test";
33 std::filesystem::create_directories(temp_dir);
34 std::filesystem::create_directories(temp_dir / test_name);
35 return temp_dir / test_name;
36}
37
38// Create test data
39std::pair<std::filesystem::path, std::filesystem::path> create_test_circuit_files(const std::filesystem::path& test_dir)
40{
42
43 auto bytecode_path = test_dir / "circuit.gz";
44 auto witness_path = test_dir / "witness.gz";
45
46 write_file(bytecode_path, bb::compress(bytecode));
47 write_file(witness_path, bb::compress(witness));
48
49 return { bytecode_path, witness_path };
50}
51
52} // namespace
53
54class ApiUltraHonkTest : public ::testing::Test {
55 protected:
57
58 void SetUp() override
59 {
60 const auto* info = ::testing::UnitTest::GetInstance()->current_test_info();
61 test_dir = get_test_dir(info->name());
62 }
63
64 void TearDown() override
65 {
66 if (std::filesystem::exists(test_dir)) {
67 std::filesystem::remove_all(test_dir);
68 }
69 }
70
71 std::filesystem::path test_dir;
72};
73
74TEST_F(ApiUltraHonkTest, ProveAndVerify)
75{
76 auto [bytecode_path, witness_path] = create_test_circuit_files(test_dir);
77
78 API::Flags flags;
79 flags.oracle_hash_type = "poseidon2"; // Set default oracle hash type
80
81 UltraHonkAPI api;
82
83 // Generate VK first
84 auto vk_output_path = test_dir / "vk";
85 std::filesystem::create_directories(vk_output_path);
86 api.write_vk(flags, bytecode_path, vk_output_path);
87 EXPECT_TRUE(std::filesystem::exists(vk_output_path / "vk"));
88
89 // Generate proof
90 auto proof_output_dir = test_dir / "proof";
91 std::filesystem::create_directories(proof_output_dir);
92 api.prove(flags, bytecode_path, witness_path, vk_output_path / "vk", proof_output_dir);
93
94 // Check that proof files were created
95 EXPECT_TRUE(std::filesystem::exists(proof_output_dir / "proof"));
96 EXPECT_TRUE(std::filesystem::exists(proof_output_dir / "public_inputs"));
97
98 // Verify the proof
99 bool verified =
100 api.verify(flags, proof_output_dir / "public_inputs", proof_output_dir / "proof", vk_output_path / "vk");
101 EXPECT_TRUE(verified);
102}
103
104TEST_F(ApiUltraHonkTest, ProveWithWriteVk)
105{
106 auto [bytecode_path, witness_path] = create_test_circuit_files(test_dir);
107
108 API::Flags flags;
109 flags.oracle_hash_type = "poseidon2";
110 flags.write_vk = true;
111
112 UltraHonkAPI api;
113
114 // Generate proof with write_vk flag (will compute and write VK)
115 auto proof_output_dir = test_dir / "proof";
116 std::filesystem::create_directories(proof_output_dir);
117 api.prove(flags, bytecode_path, witness_path, "", proof_output_dir);
118
119 // Check that proof and VK files were created
120 EXPECT_TRUE(std::filesystem::exists(proof_output_dir / "proof"));
121 EXPECT_TRUE(std::filesystem::exists(proof_output_dir / "public_inputs"));
122 EXPECT_TRUE(std::filesystem::exists(proof_output_dir / "vk"));
123 EXPECT_TRUE(std::filesystem::exists(proof_output_dir / "vk_hash"));
124
125 // Verify the proof
126 bool verified =
127 api.verify(flags, proof_output_dir / "public_inputs", proof_output_dir / "proof", proof_output_dir / "vk");
128 EXPECT_TRUE(verified);
129}
130
131TEST_F(ApiUltraHonkTest, ProveAndVerifyWithFields)
132{
133 auto [bytecode_path, witness_path] = create_test_circuit_files(test_dir);
134
135 // First generate VK for the prove step
136 API::Flags vk_flags;
137 vk_flags.oracle_hash_type = "poseidon2";
138
139 UltraHonkAPI api;
140
141 auto vk_output_path = test_dir / "vk";
142 std::filesystem::create_directories(vk_output_path);
143 api.write_vk(vk_flags, bytecode_path, vk_output_path);
144 EXPECT_TRUE(std::filesystem::exists(vk_output_path / "vk"));
145
146 // Now test proof generation
147 API::Flags flags;
148 flags.oracle_hash_type = "poseidon2";
149
150 // Generate proof
151 auto proof_output_dir = test_dir / "proof";
152 std::filesystem::create_directories(proof_output_dir);
153 api.prove(flags, bytecode_path, witness_path, vk_output_path / "vk", proof_output_dir);
154
155 // Check that proof files were created
156 EXPECT_TRUE(std::filesystem::exists(proof_output_dir / "proof"));
157 EXPECT_TRUE(std::filesystem::exists(proof_output_dir / "public_inputs"));
158}
159
160TEST_F(ApiUltraHonkTest, ProveWithDifferentSettings)
161{
162 auto [bytecode_path, witness_path] = create_test_circuit_files(test_dir);
163
164 // Test different oracle hash types
165 const std::vector<std::pair<std::string, bool>> test_cases = { { "poseidon2",
166 false }, // oracle_hash_type, disable_zk
167 { "poseidon2", true },
168 { "keccak", false },
169 { "keccak", true } };
170
171 for (const auto& [oracle_hash_type, disable_zk] : test_cases) {
172 API::Flags flags;
173 flags.oracle_hash_type = oracle_hash_type;
174 flags.disable_zk = disable_zk;
175 flags.write_vk = true;
176
177 auto case_dir = test_dir / (oracle_hash_type + "_" + (disable_zk ? "no_zk" : "zk"));
178 std::filesystem::create_directories(case_dir);
179
180 UltraHonkAPI api;
181
182 // Generate proof
183 api.prove(flags, bytecode_path, witness_path, "", case_dir);
184
185 // Verify the proof
186 bool verified = api.verify(flags, case_dir / "public_inputs", case_dir / "proof", case_dir / "vk");
187 EXPECT_TRUE(verified) << "Failed with oracle_hash_type=" << oracle_hash_type << ", disable_zk=" << disable_zk;
188 }
189}
190
192{
193 auto [bytecode_path, witness_path] = create_test_circuit_files(test_dir);
194 API::Flags flags;
195 flags.oracle_hash_type = "poseidon2";
196
197 UltraHonkAPI api;
198 api.write_vk(flags, bytecode_path, test_dir);
199
200 // Test against bbapi::CircuitComputeVk
201 auto bytecode = read_file(bytecode_path);
202 auto expected_vk =
203 bbapi::CircuitComputeVk({ .circuit = { .bytecode = bb::decompress(bytecode.data(), bytecode.size()) },
204 .settings = { .oracle_hash_type = flags.oracle_hash_type } })
205 .execute();
206
207 info("after write_vk, expected_vk size: {}", expected_vk.bytes.size());
208 EXPECT_EQ(expected_vk.bytes, read_file(test_dir / "vk"));
209 EXPECT_EQ(expected_vk.hash, read_file(test_dir / "vk_hash"));
210
211 // Verify round-trip: decode the VK and check that to_field_elements() matches
212 auto vk_from_bytes = from_buffer<UltraFlavor::VerificationKey>(expected_vk.bytes);
213 auto vk_from_file = from_buffer<UltraFlavor::VerificationKey>(read_file(test_dir / "vk"));
214 EXPECT_EQ(vk_from_bytes.to_field_elements(), vk_from_file.to_field_elements());
215}
216
217// NOTE: very light test
218TEST_F(ApiUltraHonkTest, GatesWithOpcodesSmokeTest)
219{
220 auto [bytecode_path, witness_path] = create_test_circuit_files(test_dir);
221
222 // Capture stdout
223 testing::internal::CaptureStdout();
224
225 API::Flags flags;
226 flags.oracle_hash_type = "poseidon2";
227 flags.include_gates_per_opcode = true;
228 UltraHonkAPI api;
229 api.gates(flags, bytecode_path);
230
231 std::string output = testing::internal::GetCapturedStdout();
232
233 // Check that output contains per-opcode information
234 EXPECT_TRUE(output.find("gates_per_opcode") != std::string::npos);
235}
236
237TEST_F(ApiUltraHonkTest, VerifyWithMissingVkGivesActionableError)
238{
239 auto [bytecode_path, witness_path] = create_test_circuit_files(test_dir);
240
241 API::Flags flags;
242 flags.oracle_hash_type = "poseidon2";
243 flags.write_vk = true;
244
245 UltraHonkAPI api;
246
247 // Generate proof with vk
248 auto proof_output_dir = test_dir / "proof";
249 std::filesystem::create_directories(proof_output_dir);
250 api.prove(flags, bytecode_path, witness_path, "", proof_output_dir);
251
252 // Try to verify with a non-existent vk path
253 auto nonexistent_vk_path = test_dir / "nonexistent_vk";
254 try {
255 api.verify(flags, proof_output_dir / "public_inputs", proof_output_dir / "proof", nonexistent_vk_path);
256 FAIL() << "Expected an exception to be thrown";
257 } catch (const std::runtime_error& e) {
258 std::string error_msg = e.what();
259 // Check that the error message contains actionable guidance
260 EXPECT_TRUE(error_msg.find("--write_vk") != std::string::npos)
261 << "Error message should mention --write_vk flag. Got: " << error_msg;
262 EXPECT_TRUE(error_msg.find("bb write_vk") != std::string::npos)
263 << "Error message should mention bb write_vk command. Got: " << error_msg;
264 EXPECT_TRUE(error_msg.find("--vk_path") != std::string::npos)
265 << "Error message should mention --vk_path option. Got: " << error_msg;
266 }
267}
TEST_F(ApiUltraHonkTest, ProveAndVerify)
std::shared_ptr< Napi::ThreadSafeFunction > bytecode
UltraHonk-specific command definitions for the Barretenberg RPC API.
void TearDown() override
static void SetUpTestSuite()
std::filesystem::path test_dir
void prove(const Flags &flags, const std::filesystem::path &bytecode_path, const std::filesystem::path &witness_path, const std::filesystem::path &vk_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
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
void gates(const Flags &flags, const std::filesystem::path &bytecode_path) override
void info(Args... args)
Definition log.hpp:89
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::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 > decompress(const void *bytes, size_t size)
Decompress bytecode and witness fields from ivc-inputs.msgpack.
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
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
bool disable_zk
Definition api.hpp:13
std::string oracle_hash_type
Definition api.hpp:19