Skip to content

Commit 5cc430b

Browse files
authored
feat: merge-train/barretenberg (#16345)
See [merge-train-readme.md](https://github.com/AztecProtocol/aztec-packages/blob/next/.github/workflows/merge-train-readme.md). BEGIN_COMMIT_OVERRIDE refactor(bbapi): ultrahonk unified api (#16090) chore: assert poseidon hashes only witnesses (#16341) chore: simplify is_accum (#16291) END_COMMIT_OVERRIDE
2 parents 6c594c4 + 8553395 commit 5cc430b

64 files changed

Lines changed: 1192 additions & 1119 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.test_patterns.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,10 @@ tests:
4949
error_regex: "Failed to fetch"
5050
owners:
5151
- *adam
52+
- regex: "barretenberg/acir_tests/scripts/run_test_browser.sh"
53+
error_regex: "Failed to fetch"
54+
owners:
55+
- *adam
5256
- regex: "barretenberg/acir_tests/scripts/run_test_browser.sh"
5357
error_regex: "RuntimeError: Out of bounds memory access"
5458
owners:

barretenberg/cpp/src/barretenberg/api/api_client_ivc.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
#include "barretenberg/api/file_io.hpp"
33
#include "barretenberg/api/get_bytecode.hpp"
44
#include "barretenberg/api/log.hpp"
5-
#include "barretenberg/api/write_prover_output.hpp"
65
#include "barretenberg/bbapi/bbapi.hpp"
76
#include "barretenberg/client_ivc/client_ivc.hpp"
87
#include "barretenberg/client_ivc/mock_circuit_producer.hpp"
@@ -257,7 +256,7 @@ void gate_count_for_ivc(const std::string& bytecode_path, bool include_gates_per
257256
bbapi::BBApiRequest request{ .trace_settings = { AZTEC_TRACE_STRUCTURE } };
258257

259258
auto bytecode = get_bytecode(bytecode_path);
260-
auto response = bbapi::ClientIvcGates{ .circuit = { .name = "ivc_circuit", .bytecode = std::move(bytecode) },
259+
auto response = bbapi::ClientIvcStats{ .circuit = { .name = "ivc_circuit", .bytecode = std::move(bytecode) },
261260
.include_gates_per_opcode = include_gates_per_opcode }
262261
.execute(request);
263262

barretenberg/cpp/src/barretenberg/api/api_ultra_honk.cpp

Lines changed: 205 additions & 226 deletions
Large diffs are not rendered by default.
Lines changed: 258 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,258 @@
1+
#include "api_ultra_honk.hpp"
2+
#include "barretenberg/api/file_io.hpp"
3+
#include "barretenberg/bbapi/bbapi_ultra_honk.hpp"
4+
#include "barretenberg/client_ivc/acir_bincode_mocks.hpp"
5+
#include "barretenberg/common/serialize.hpp"
6+
#include "barretenberg/dsl/acir_format/acir_format.hpp"
7+
#include "barretenberg/dsl/acir_format/acir_to_constraint_buf.hpp"
8+
#include "barretenberg/dsl/acir_format/proof_surgeon.hpp"
9+
#include "barretenberg/flavor/ultra_flavor.hpp"
10+
#include "barretenberg/flavor/ultra_rollup_flavor.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+
20+
namespace bb {
21+
std::vector<uint8_t> compress(const std::vector<uint8_t>& input);
22+
std::vector<uint8_t> decompress(const void* bytes, size_t size);
23+
} // namespace bb
24+
25+
using namespace bb;
26+
27+
namespace {
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.
30+
std::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
39+
std::pair<std::filesystem::path, std::filesystem::path> create_test_circuit_files(const std::filesystem::path& test_dir)
40+
{
41+
auto [bytecode, witness] = acir_bincode_mocks::create_simple_circuit_bytecode();
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+
54+
class ApiUltraHonkTest : public ::testing::Test {
55+
protected:
56+
static void SetUpTestSuite() { bb::srs::init_file_crs_factory(bb::srs::bb_crs_path()); }
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+
74+
TEST_F(ApiUltraHonkTest, ProveAndVerify)
75+
{
76+
auto [bytecode_path, witness_path] = create_test_circuit_files(test_dir);
77+
78+
API::Flags flags;
79+
flags.output_format = "bytes";
80+
flags.oracle_hash_type = "poseidon2"; // Set default oracle hash type
81+
82+
UltraHonkAPI api;
83+
84+
// Generate VK first
85+
auto vk_output_path = test_dir / "vk";
86+
std::filesystem::create_directories(vk_output_path);
87+
api.write_vk(flags, bytecode_path, vk_output_path);
88+
EXPECT_TRUE(std::filesystem::exists(vk_output_path / "vk"));
89+
90+
// Generate proof
91+
auto proof_output_dir = test_dir / "proof";
92+
std::filesystem::create_directories(proof_output_dir);
93+
api.prove(flags, bytecode_path, witness_path, vk_output_path / "vk", proof_output_dir);
94+
95+
// Check that proof files were created
96+
EXPECT_TRUE(std::filesystem::exists(proof_output_dir / "proof"));
97+
EXPECT_TRUE(std::filesystem::exists(proof_output_dir / "public_inputs"));
98+
99+
// Verify the proof
100+
bool verified =
101+
api.verify(flags, proof_output_dir / "public_inputs", proof_output_dir / "proof", vk_output_path / "vk");
102+
EXPECT_TRUE(verified);
103+
}
104+
105+
TEST_F(ApiUltraHonkTest, ProveWithWriteVk)
106+
{
107+
auto [bytecode_path, witness_path] = create_test_circuit_files(test_dir);
108+
109+
API::Flags flags;
110+
flags.output_format = "bytes_and_fields"; // Test both output formats
111+
flags.oracle_hash_type = "poseidon2";
112+
flags.write_vk = true;
113+
114+
UltraHonkAPI api;
115+
116+
// Generate proof with write_vk flag (will compute and write VK)
117+
auto proof_output_dir = test_dir / "proof";
118+
std::filesystem::create_directories(proof_output_dir);
119+
api.prove(flags, bytecode_path, witness_path, "", proof_output_dir);
120+
121+
// Check that proof and VK files were created
122+
EXPECT_TRUE(std::filesystem::exists(proof_output_dir / "proof"));
123+
EXPECT_TRUE(std::filesystem::exists(proof_output_dir / "public_inputs"));
124+
EXPECT_TRUE(std::filesystem::exists(proof_output_dir / "vk"));
125+
EXPECT_TRUE(std::filesystem::exists(proof_output_dir / "vk_hash"));
126+
EXPECT_TRUE(std::filesystem::exists(proof_output_dir / "vk_fields.json"));
127+
EXPECT_TRUE(std::filesystem::exists(proof_output_dir / "vk_hash_fields.json"));
128+
129+
// Verify the proof
130+
bool verified =
131+
api.verify(flags, proof_output_dir / "public_inputs", proof_output_dir / "proof", proof_output_dir / "vk");
132+
EXPECT_TRUE(verified);
133+
}
134+
135+
TEST_F(ApiUltraHonkTest, ProveAndVerifyWithFields)
136+
{
137+
auto [bytecode_path, witness_path] = create_test_circuit_files(test_dir);
138+
139+
// First generate VK in bytes format for the prove step
140+
API::Flags vk_flags;
141+
vk_flags.output_format = "bytes";
142+
vk_flags.oracle_hash_type = "poseidon2";
143+
144+
UltraHonkAPI api;
145+
146+
auto vk_output_path = test_dir / "vk";
147+
std::filesystem::create_directories(vk_output_path);
148+
api.write_vk(vk_flags, bytecode_path, vk_output_path);
149+
EXPECT_TRUE(std::filesystem::exists(vk_output_path / "vk"));
150+
151+
// Now test fields format for proof generation
152+
API::Flags flags;
153+
flags.output_format = "fields";
154+
flags.oracle_hash_type = "poseidon2";
155+
156+
// Generate proof with fields output
157+
auto proof_output_dir = test_dir / "proof";
158+
std::filesystem::create_directories(proof_output_dir);
159+
api.prove(flags, bytecode_path, witness_path, vk_output_path / "vk", proof_output_dir);
160+
161+
// Check that proof field files were created
162+
EXPECT_TRUE(std::filesystem::exists(proof_output_dir / "proof_fields.json"));
163+
EXPECT_TRUE(std::filesystem::exists(proof_output_dir / "public_inputs_fields.json"));
164+
}
165+
166+
TEST_F(ApiUltraHonkTest, ProveWithDifferentSettings)
167+
{
168+
auto [bytecode_path, witness_path] = create_test_circuit_files(test_dir);
169+
170+
// Test different oracle hash types
171+
const std::vector<std::pair<std::string, bool>> test_cases = { { "poseidon2",
172+
false }, // oracle_hash_type, disable_zk
173+
{ "poseidon2", true },
174+
{ "keccak", false },
175+
{ "keccak", true } };
176+
177+
for (const auto& [oracle_hash_type, disable_zk] : test_cases) {
178+
API::Flags flags;
179+
flags.output_format = "bytes";
180+
flags.oracle_hash_type = oracle_hash_type;
181+
flags.disable_zk = disable_zk;
182+
flags.write_vk = true;
183+
184+
auto case_dir = test_dir / (oracle_hash_type + "_" + (disable_zk ? "no_zk" : "zk"));
185+
std::filesystem::create_directories(case_dir);
186+
187+
UltraHonkAPI api;
188+
189+
// Generate proof
190+
api.prove(flags, bytecode_path, witness_path, "", case_dir);
191+
192+
// Verify the proof
193+
bool verified = api.verify(flags, case_dir / "public_inputs", case_dir / "proof", case_dir / "vk");
194+
EXPECT_TRUE(verified) << "Failed with oracle_hash_type=" << oracle_hash_type << ", disable_zk=" << disable_zk;
195+
}
196+
}
197+
198+
TEST_F(ApiUltraHonkTest, WriteVk)
199+
{
200+
auto [bytecode_path, witness_path] = create_test_circuit_files(test_dir);
201+
202+
// Smoke test fields format (no real verification)
203+
{
204+
API::Flags flags;
205+
flags.output_format = "fields";
206+
flags.oracle_hash_type = "poseidon2";
207+
208+
UltraHonkAPI api;
209+
api.write_vk(flags, bytecode_path, test_dir);
210+
211+
EXPECT_TRUE(std::filesystem::exists(test_dir / "vk_fields.json"));
212+
EXPECT_TRUE(std::filesystem::exists(test_dir / "vk_hash_fields.json"));
213+
214+
EXPECT_FALSE(std::filesystem::exists(test_dir / "vk"));
215+
EXPECT_FALSE(std::filesystem::exists(test_dir / "vk_hash"));
216+
}
217+
218+
// Test with bytes format, simple vk recalculation
219+
{
220+
API::Flags flags;
221+
flags.output_format = "bytes";
222+
flags.oracle_hash_type = "poseidon2";
223+
224+
UltraHonkAPI api;
225+
api.write_vk(flags, bytecode_path, test_dir);
226+
227+
// Test against bbapi::CircuitComputeVk
228+
auto bytecode = read_file(bytecode_path);
229+
auto expected_vk =
230+
bbapi::CircuitComputeVk({ .circuit = { .bytecode = bb::decompress(bytecode.data(), bytecode.size()) },
231+
.settings = { .oracle_hash_type = flags.oracle_hash_type } })
232+
.execute();
233+
234+
info("after write_vk, expected_vk size: {}", expected_vk.bytes.size());
235+
EXPECT_EQ(expected_vk.bytes, read_file(test_dir / "vk"));
236+
EXPECT_EQ(expected_vk.hash, read_file(test_dir / "vk_hash"));
237+
}
238+
}
239+
240+
// NOTE: very light test
241+
TEST_F(ApiUltraHonkTest, GatesWithOpcodesSmokeTest)
242+
{
243+
auto [bytecode_path, witness_path] = create_test_circuit_files(test_dir);
244+
245+
// Capture stdout
246+
testing::internal::CaptureStdout();
247+
248+
API::Flags flags;
249+
flags.oracle_hash_type = "poseidon2";
250+
flags.include_gates_per_opcode = true;
251+
UltraHonkAPI api;
252+
api.gates(flags, bytecode_path);
253+
254+
std::string output = testing::internal::GetCapturedStdout();
255+
256+
// Check that output contains per-opcode information
257+
EXPECT_TRUE(output.find("gates_per_opcode") != std::string::npos);
258+
}

barretenberg/cpp/src/barretenberg/api/file_io.hpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
#pragma once
22
#include "barretenberg/common/log.hpp"
33
#include "barretenberg/common/try_catch_shim.hpp"
4+
#include "barretenberg/ecc/curves/bn254/fr.hpp"
45
#include <cstdint>
56
#include <cstring>
67
#include <fcntl.h>
78
#include <fstream>
89
#include <ios>
910
#include <iostream>
11+
#include <sstream>
1012
#include <sys/stat.h>
1113
#include <unistd.h>
1214
#include <vector>
@@ -84,4 +86,18 @@ inline void write_file(const std::string& filename, std::vector<uint8_t> const&
8486
file.close();
8587
}
8688
}
89+
90+
template <typename Fr> inline std::string field_elements_to_json(const std::vector<Fr>& fields)
91+
{
92+
std::stringstream ss;
93+
ss << "[";
94+
for (size_t i = 0; i < fields.size(); ++i) {
95+
ss << '"' << fields[i] << '"';
96+
if (i != fields.size() - 1) {
97+
ss << ",";
98+
}
99+
}
100+
ss << "]";
101+
return ss.str();
102+
}
87103
} // namespace bb

0 commit comments

Comments
 (0)