Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
bbapi.test.cpp
Go to the documentation of this file.
4#include "barretenberg/bbapi/generated/bb_types.hpp"
10#include "msgpack/v3/sbuffer_decl.hpp"
11#include <gtest/gtest.h>
12
13using namespace bb;
14
15namespace {
16// Wire (command, response) pairs for the serde roundtrip test below.
17template <typename Cmd, typename Resp> struct WirePair {
18 using CommandType = Cmd;
19 using ResponseType = Resp;
20};
21} // namespace
22
23// Template for testing roundtrip serialization on the codegen-emitted wire
24// types. The serde fidelity of every other command pair is covered by the
25// ipc-codegen golden + matrix tests; this suite is a sanity check that the
26// `SERIALIZATION_FIELDS`-generated msgpack adapter round-trips correctly.
27using WirePairs = ::testing::Types<
28 WirePair<bbapi::wire::CircuitProve, bbapi::wire::CircuitProveResponse>,
29 WirePair<bbapi::wire::CircuitComputeVk, bbapi::wire::CircuitComputeVkResponse>,
30 WirePair<bbapi::wire::CircuitStats, bbapi::wire::CircuitInfoResponse>,
31 WirePair<bbapi::wire::CircuitVerify, bbapi::wire::CircuitVerifyResponse>,
32 WirePair<bbapi::wire::VkAsFields, bbapi::wire::VkAsFieldsResponse>,
33 WirePair<bbapi::wire::CircuitWriteSolidityVerifier, bbapi::wire::CircuitWriteSolidityVerifierResponse>,
34 WirePair<bbapi::wire::ChonkStart, bbapi::wire::ChonkStartResponse>,
35 WirePair<bbapi::wire::ChonkLoad, bbapi::wire::ChonkLoadResponse>,
36 WirePair<bbapi::wire::ChonkAccumulate, bbapi::wire::ChonkAccumulateResponse>,
37 WirePair<bbapi::wire::ChonkProve, bbapi::wire::ChonkProveResponse>,
38 WirePair<bbapi::wire::ChonkComputeVk, bbapi::wire::ChonkComputeVkResponse>,
39 WirePair<bbapi::wire::ChonkCheckPrecomputedVk, bbapi::wire::ChonkCheckPrecomputedVkResponse>,
40 WirePair<bbapi::wire::ChonkBatchVerify, bbapi::wire::ChonkBatchVerifyResponse>>;
41
42template <typename T> class BBApiMsgpack : public ::testing::Test {};
43
45
46TYPED_TEST(BBApiMsgpack, DefaultConstructorRoundtrip)
47{
48 typename TypeParam::CommandType command{};
49 auto [actual_command, expected_command] = msgpack_roundtrip(command);
50 EXPECT_EQ(actual_command, expected_command);
51
52 typename TypeParam::ResponseType response{};
53 auto [actual_response, expected_response] = msgpack_roundtrip(response);
54 EXPECT_EQ(actual_response, expected_response);
55}
56
57// Regression tests for input validation at API boundaries.
58// These ensure non-canonical field encodings and trailing bytes are rejected.
59
60TEST(BBApiInputValidation, NonCanonicalPublicInputRejected)
61{
62 using Flavor = UltraFlavor;
63 // A value >= BN254 scalar field modulus should be rejected
64 uint256_t non_canonical = fr::modulus + 1;
65 std::vector<uint256_t> bad_public_inputs = { non_canonical };
66 std::vector<uint256_t> proof = { uint256_t(0) };
67
68 EXPECT_THROW(bbapi::concatenate_proof<Flavor>(bad_public_inputs, proof), std::runtime_error);
69}
70
71TEST(BBApiInputValidation, NonCanonicalProofElementRejected)
72{
73 using Flavor = UltraFlavor;
74 // The modulus itself is non-canonical (valid range is [0, modulus))
75 uint256_t non_canonical = fr::modulus;
76 std::vector<uint256_t> public_inputs = { uint256_t(42) };
77 std::vector<uint256_t> bad_proof = { non_canonical };
78
79 EXPECT_THROW(bbapi::concatenate_proof<Flavor>(public_inputs, bad_proof), std::runtime_error);
80}
81
82TEST(BBApiInputValidation, CanonicalValuesAccepted)
83{
84 using Flavor = UltraFlavor;
85 // modulus - 1 is the largest canonical value
86 uint256_t max_canonical = fr::modulus - 1;
87 std::vector<uint256_t> public_inputs = { uint256_t(0), max_canonical };
88 std::vector<uint256_t> proof = { uint256_t(1) };
89
90 EXPECT_NO_THROW(bbapi::concatenate_proof<Flavor>(public_inputs, proof));
91}
92
93TEST(BBApiInputValidation, TrailingBytesInBinaryInputRejected)
94{
95 // A buffer that is not a multiple of 32 bytes should be rejected
96 std::vector<uint8_t> buf(32 + 1, 0); // 33 bytes = 1 field element + 1 trailing byte
97 EXPECT_THROW(many_from_buffer_exact<uint256_t>(buf, "test input"), std::runtime_error);
98}
99
100TEST(BBApiInputValidation, ExactBinaryInputAccepted)
101{
102 // A buffer that is exactly 2 field elements should parse fine
103 std::vector<uint8_t> buf(64, 0);
104 EXPECT_NO_THROW(many_from_buffer_exact<uint256_t>(buf, "test input"));
105 auto result = many_from_buffer_exact<uint256_t>(buf, "test input");
106 EXPECT_EQ(result.size(), 2UL);
107}
108
109TEST(BBApiInputValidation, VkWithTrailingBytesRejectedOnProveSide)
110{
112 const size_t expected_size = VK::calc_num_data_types() * sizeof(bb::fr);
113 // One extra byte beyond the expected VK size
114 std::vector<uint8_t> bad_vk(expected_size + 1, 0);
115 EXPECT_THROW(bbapi::validate_vk_size<VK>(bad_vk), std::runtime_error);
116}
117
118TEST(BBApiInputValidation, VkWithCorrectSizeAccepted)
119{
121 const size_t expected_size = VK::calc_num_data_types() * sizeof(bb::fr);
122 std::vector<uint8_t> good_vk(expected_size, 0);
123 EXPECT_NO_THROW(bbapi::validate_vk_size<VK>(good_vk));
124}
125
126TEST(BBApiInputValidation, ChonkVerifyWrongVkSizeReturnsInvalid)
127{
128 bbapi::BBApiRequest request;
129 auto response = bbapi::handle_chonk_verify(request, bbapi::wire::ChonkVerify{ .proof = {}, .vk = { 0 } });
130 EXPECT_FALSE(response.valid);
131}
132
133TEST(BBApiInputValidation, ChonkVerifyFromFieldsWrongVkSizeReturnsInvalid)
134{
135 bbapi::BBApiRequest request;
136 auto response =
137 bbapi::handle_chonk_verify_from_fields(request, bbapi::wire::ChonkVerifyFromFields{ .proof = {}, .vk = { 0 } });
138 EXPECT_FALSE(response.valid);
139}
140
141TEST(BBApiInputValidation, ChonkBatchVerifyWrongVkSizeReturnsInvalid)
142{
143 bbapi::BBApiRequest request;
144 auto response = bbapi::handle_chonk_batch_verify(request,
145 bbapi::wire::ChonkBatchVerify{
146 .proofs = { bbapi::wire::ChonkProof{} },
147 .vks = { { 0 } },
148 });
149 EXPECT_FALSE(response.valid);
150}
151
152// Helper: pack a vector of PrivateExecutionStepRaw into a byte buffer via msgpack.
153namespace {
154std::vector<uint8_t> pack_steps(const std::vector<PrivateExecutionStepRaw>& steps)
155{
156 std::stringstream ss;
157 msgpack::pack(ss, steps);
158 const std::string s = ss.str();
159 return { s.begin(), s.end() };
160}
161} // namespace
162
163TEST(BBApiInputValidation, MsgpackParseUncompressedAcceptsCleanInput)
164{
166 .bytecode = { 0xCA, 0xFE }, .witness = { 0xBE, 0xEF }, .vk = {}, .function_name = "test_fn"
167 };
168
169 auto buf = pack_steps({ step });
171
172 ASSERT_EQ(result.size(), 1);
173 EXPECT_EQ(result[0].bytecode, step.bytecode);
174 EXPECT_EQ(result[0].witness, step.witness);
175 EXPECT_EQ(result[0].function_name, "test_fn");
176}
177
178TEST(BBApiInputValidation, MsgpackParseUncompressedRejectsTrailingData)
179{
180 PrivateExecutionStepRaw step{ .bytecode = {}, .witness = {}, .vk = {}, .function_name = "x" };
181
182 auto buf = pack_steps({ step });
183 buf.push_back(0x00);
184
185 EXPECT_THROW(PrivateExecutionStepRaw::parse_uncompressed(buf), std::invalid_argument);
186}
187
188TEST(BBApiInputValidation, MsgpackLoadAcceptsCleanFile)
189{
190 PrivateExecutionStepRaw step{ .bytecode = { 1, 2, 3 }, .witness = { 4, 5 }, .vk = {}, .function_name = "file_fn" };
191
192 auto buf = pack_steps({ step });
193
194 auto tmp = std::filesystem::temp_directory_path() / "bb_test_clean.msgpack";
195 std::ofstream out(tmp, std::ios::binary);
196 out.write(reinterpret_cast<const char*>(buf.data()), static_cast<std::streamsize>(buf.size()));
197 out.close();
198
199 auto result = PrivateExecutionStepRaw::load(tmp);
200 std::filesystem::remove(tmp);
201
202 ASSERT_EQ(result.size(), 1);
203 EXPECT_EQ(result[0].function_name, "file_fn");
204}
205
206TEST(BBApiInputValidation, MsgpackLoadRejectsTrailingData)
207{
208 PrivateExecutionStepRaw step{ .bytecode = {}, .witness = {}, .vk = {}, .function_name = "x" };
209
210 auto buf = pack_steps({ step });
211 buf.push_back(0x00);
212
213 auto tmp = std::filesystem::temp_directory_path() / "bb_test_tailed.msgpack";
214 std::ofstream out(tmp, std::ios::binary);
215 out.write(reinterpret_cast<const char*>(buf.data()), static_cast<std::streamsize>(buf.size()));
216 out.close();
217
218 EXPECT_THROW(PrivateExecutionStepRaw::load(tmp), std::invalid_argument);
219 std::filesystem::remove(tmp);
220}
221
222// Regression tests for AesEncrypt/AesDecrypt input validation.
223// Without these guards, a socket client could force a ~4 GB allocation via the
224// uint32_t `length` field, or pass `length != plaintext.size()` and silently
225// encrypt zero-padded tail bytes.
226TEST(BBApiInputValidation, AesEncryptRejectsLengthMismatch)
227{
228 bbapi::BBApiRequest request{};
229 bbapi::wire::AesEncrypt cmd{ .plaintext = std::vector<uint8_t>(16, 0), .iv = {}, .key = {}, .length = 32 };
230 EXPECT_THROW_OR_ABORT(bbapi::handle_aes_encrypt(request, std::move(cmd)), ".*length must equal plaintext.*");
231}
232
233TEST(BBApiInputValidation, AesEncryptRejectsNonBlockAlignedLength)
234{
235 bbapi::BBApiRequest request{};
236 bbapi::wire::AesEncrypt cmd{ .plaintext = std::vector<uint8_t>(17, 0), .iv = {}, .key = {}, .length = 17 };
237 EXPECT_THROW_OR_ABORT(bbapi::handle_aes_encrypt(request, std::move(cmd)), ".*multiple of 16.*");
238}
239
240TEST(BBApiInputValidation, AesDecryptRejectsLengthMismatch)
241{
242 bbapi::BBApiRequest request{};
243 bbapi::wire::AesDecrypt cmd{ .ciphertext = std::vector<uint8_t>(16, 0), .iv = {}, .key = {}, .length = 32 };
244 EXPECT_THROW_OR_ABORT(bbapi::handle_aes_decrypt(request, std::move(cmd)), ".*length must equal ciphertext.*");
245}
246
247TEST(BBApiInputValidation, AesDecryptRejectsNonBlockAlignedLength)
248{
249 bbapi::BBApiRequest request{};
250 bbapi::wire::AesDecrypt cmd{ .ciphertext = std::vector<uint8_t>(17, 0), .iv = {}, .key = {}, .length = 17 };
251 EXPECT_THROW_OR_ABORT(bbapi::handle_aes_decrypt(request, std::move(cmd)), ".*multiple of 16.*");
252}
#define EXPECT_THROW_OR_ABORT(statement, matcher)
Definition assert.hpp:192
std::shared_ptr< Napi::ThreadSafeFunction > bytecode
::testing::Types< WirePair< bbapi::wire::CircuitProve, bbapi::wire::CircuitProveResponse >, WirePair< bbapi::wire::CircuitComputeVk, bbapi::wire::CircuitComputeVkResponse >, WirePair< bbapi::wire::CircuitStats, bbapi::wire::CircuitInfoResponse >, WirePair< bbapi::wire::CircuitVerify, bbapi::wire::CircuitVerifyResponse >, WirePair< bbapi::wire::VkAsFields, bbapi::wire::VkAsFieldsResponse >, WirePair< bbapi::wire::CircuitWriteSolidityVerifier, bbapi::wire::CircuitWriteSolidityVerifierResponse >, WirePair< bbapi::wire::ChonkStart, bbapi::wire::ChonkStartResponse >, WirePair< bbapi::wire::ChonkLoad, bbapi::wire::ChonkLoadResponse >, WirePair< bbapi::wire::ChonkAccumulate, bbapi::wire::ChonkAccumulateResponse >, WirePair< bbapi::wire::ChonkProve, bbapi::wire::ChonkProveResponse >, WirePair< bbapi::wire::ChonkComputeVk, bbapi::wire::ChonkComputeVkResponse >, WirePair< bbapi::wire::ChonkCheckPrecomputedVk, bbapi::wire::ChonkCheckPrecomputedVkResponse >, WirePair< bbapi::wire::ChonkBatchVerify, bbapi::wire::ChonkBatchVerifyResponse > > WirePairs
TEST(BBApiInputValidation, NonCanonicalPublicInputRejected)
TYPED_TEST_SUITE(BBApiMsgpack, WirePairs)
TYPED_TEST(BBApiMsgpack, DefaultConstructorRoundtrip)
Non-template handler declarations for the bb service.
Shared type definitions for the Barretenberg RPC API.
NativeVerificationKey_< PrecomputedEntities< Commitment >, Codec, HashFunction, CommitmentKey > VerificationKey
The verification key stores commitments to the precomputed (non-witness) polynomials used by the veri...
Entry point for Barretenberg command-line interface.
Definition api.hpp:5
field< Bn254FrParams > fr
Definition fr.hpp:155
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
This is the msgpack encoding of the objects returned by the following typescript: const stepToStruct ...
static std::vector< PrivateExecutionStepRaw > parse_uncompressed(const std::vector< uint8_t > &buf)
static std::vector< PrivateExecutionStepRaw > load(const std::filesystem::path &input_path)
static constexpr uint256_t modulus
std::pair< T, T > msgpack_roundtrip(const T &object)