Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
bbapi.test.cpp
Go to the documentation of this file.
10#include "msgpack/v3/sbuffer_decl.hpp"
11#include <gtest/gtest.h>
12
13using namespace bb;
14
15// Template for testing roundtrip serialization
16template <typename T> class BBApiSerializationTest : public ::testing::Test {};
17
18// Enumerate each command type
19using Commands = ::testing::Types<bbapi::CircuitProve,
32
33// Typed test suites
34template <typename T> class BBApiMsgpack : public ::testing::Test {};
35
37
38// Test roundtrip serialization for UltraHonk commands
39TYPED_TEST(BBApiMsgpack, DefaultConstructorRoundtrip)
40{
41 TypeParam command{};
42 auto [actual_command, expected_command] = msgpack_roundtrip(command);
43 EXPECT_EQ(actual_command, expected_command);
44
45 typename TypeParam::Response response{};
46 auto [actual_response, expected_response] = msgpack_roundtrip(response);
47 EXPECT_EQ(actual_response, expected_response);
49}
50
51// Regression tests for input validation at API boundaries.
52// These ensure non-canonical field encodings and trailing bytes are rejected.
53
54TEST(BBApiInputValidation, NonCanonicalPublicInputRejected)
55{
56 using Flavor = UltraFlavor;
57 // A value >= BN254 scalar field modulus should be rejected
58 uint256_t non_canonical = fr::modulus + 1;
59 std::vector<uint256_t> bad_public_inputs = { non_canonical };
60 std::vector<uint256_t> proof = { uint256_t(0) };
61
62 EXPECT_THROW(bbapi::concatenate_proof<Flavor>(bad_public_inputs, proof), std::runtime_error);
63}
64
65TEST(BBApiInputValidation, NonCanonicalProofElementRejected)
66{
67 using Flavor = UltraFlavor;
68 // The modulus itself is non-canonical (valid range is [0, modulus))
69 uint256_t non_canonical = fr::modulus;
70 std::vector<uint256_t> public_inputs = { uint256_t(42) };
71 std::vector<uint256_t> bad_proof = { non_canonical };
72
73 EXPECT_THROW(bbapi::concatenate_proof<Flavor>(public_inputs, bad_proof), std::runtime_error);
74}
75
76TEST(BBApiInputValidation, CanonicalValuesAccepted)
77{
78 using Flavor = UltraFlavor;
79 // modulus - 1 is the largest canonical value
80 uint256_t max_canonical = fr::modulus - 1;
81 std::vector<uint256_t> public_inputs = { uint256_t(0), max_canonical };
82 std::vector<uint256_t> proof = { uint256_t(1) };
83
84 EXPECT_NO_THROW(bbapi::concatenate_proof<Flavor>(public_inputs, proof));
85}
86
87TEST(BBApiInputValidation, TrailingBytesInBinaryInputRejected)
88{
89 // A buffer that is not a multiple of 32 bytes should be rejected
90 std::vector<uint8_t> buf(32 + 1, 0); // 33 bytes = 1 field element + 1 trailing byte
91 EXPECT_THROW(many_from_buffer_exact<uint256_t>(buf, "test input"), std::runtime_error);
92}
93
94TEST(BBApiInputValidation, ExactBinaryInputAccepted)
95{
96 // A buffer that is exactly 2 field elements should parse fine
97 std::vector<uint8_t> buf(64, 0);
98 EXPECT_NO_THROW(many_from_buffer_exact<uint256_t>(buf, "test input"));
99 auto result = many_from_buffer_exact<uint256_t>(buf, "test input");
100 EXPECT_EQ(result.size(), 2UL);
101}
102
103TEST(BBApiInputValidation, VkWithTrailingBytesRejectedOnProveSide)
104{
106 const size_t expected_size = VK::calc_num_data_types() * sizeof(bb::fr);
107 // One extra byte beyond the expected VK size
108 std::vector<uint8_t> bad_vk(expected_size + 1, 0);
109 EXPECT_THROW(bbapi::validate_vk_size<VK>(bad_vk), std::runtime_error);
110}
111
112TEST(BBApiInputValidation, VkWithCorrectSizeAccepted)
113{
115 const size_t expected_size = VK::calc_num_data_types() * sizeof(bb::fr);
116 std::vector<uint8_t> good_vk(expected_size, 0);
117 EXPECT_NO_THROW(bbapi::validate_vk_size<VK>(good_vk));
118}
119
120// Helper: pack a vector of PrivateExecutionStepRaw into a byte buffer via msgpack.
121namespace {
122std::vector<uint8_t> pack_steps(const std::vector<PrivateExecutionStepRaw>& steps)
123{
124 std::stringstream ss;
125 msgpack::pack(ss, steps);
126 const std::string s = ss.str();
127 return { s.begin(), s.end() };
128}
129} // namespace
130
131TEST(BBApiInputValidation, MsgpackParseUncompressedAcceptsCleanInput)
132{
134 .bytecode = { 0xCA, 0xFE }, .witness = { 0xBE, 0xEF }, .vk = {}, .function_name = "test_fn"
135 };
136
137 auto buf = pack_steps({ step });
139
140 ASSERT_EQ(result.size(), 1);
141 EXPECT_EQ(result[0].bytecode, step.bytecode);
142 EXPECT_EQ(result[0].witness, step.witness);
143 EXPECT_EQ(result[0].function_name, "test_fn");
144}
145
146TEST(BBApiInputValidation, MsgpackParseUncompressedRejectsTrailingData)
147{
148 PrivateExecutionStepRaw step{ .bytecode = {}, .witness = {}, .vk = {}, .function_name = "x" };
149
150 auto buf = pack_steps({ step });
151 buf.push_back(0x00);
152
153 EXPECT_THROW(PrivateExecutionStepRaw::parse_uncompressed(buf), std::invalid_argument);
154}
155
156TEST(BBApiInputValidation, MsgpackLoadAcceptsCleanFile)
157{
158 PrivateExecutionStepRaw step{ .bytecode = { 1, 2, 3 }, .witness = { 4, 5 }, .vk = {}, .function_name = "file_fn" };
159
160 auto buf = pack_steps({ step });
161
162 auto tmp = std::filesystem::temp_directory_path() / "bb_test_clean.msgpack";
163 std::ofstream out(tmp, std::ios::binary);
164 out.write(reinterpret_cast<const char*>(buf.data()), static_cast<std::streamsize>(buf.size()));
165 out.close();
166
167 auto result = PrivateExecutionStepRaw::load(tmp);
168 std::filesystem::remove(tmp);
169
170 ASSERT_EQ(result.size(), 1);
171 EXPECT_EQ(result[0].function_name, "file_fn");
172}
173
174TEST(BBApiInputValidation, MsgpackLoadRejectsTrailingData)
175{
176 PrivateExecutionStepRaw step{ .bytecode = {}, .witness = {}, .vk = {}, .function_name = "x" };
177
178 auto buf = pack_steps({ step });
179 buf.push_back(0x00);
180
181 auto tmp = std::filesystem::temp_directory_path() / "bb_test_tailed.msgpack";
182 std::ofstream out(tmp, std::ios::binary);
183 out.write(reinterpret_cast<const char*>(buf.data()), static_cast<std::streamsize>(buf.size()));
184 out.close();
185
186 EXPECT_THROW(PrivateExecutionStepRaw::load(tmp), std::invalid_argument);
187 std::filesystem::remove(tmp);
188}
189
190// Regression tests for AesEncrypt/AesDecrypt input validation.
191// Without these guards, a socket client could force a ~4 GB allocation via the
192// uint32_t `length` field, or pass `length != plaintext.size()` and silently
193// encrypt zero-padded tail bytes.
194TEST(BBApiInputValidation, AesEncryptRejectsLengthMismatch)
195{
196 bbapi::BBApiRequest request{};
197 bbapi::AesEncrypt cmd{ .plaintext = std::vector<uint8_t>(16, 0), .iv = {}, .key = {}, .length = 32 };
198 EXPECT_THROW_OR_ABORT(std::move(cmd).execute(request), ".*length must equal plaintext.*");
199}
200
201TEST(BBApiInputValidation, AesEncryptRejectsNonBlockAlignedLength)
202{
203 bbapi::BBApiRequest request{};
204 bbapi::AesEncrypt cmd{ .plaintext = std::vector<uint8_t>(17, 0), .iv = {}, .key = {}, .length = 17 };
205 EXPECT_THROW_OR_ABORT(std::move(cmd).execute(request), ".*multiple of 16.*");
206}
207
208TEST(BBApiInputValidation, AesDecryptRejectsLengthMismatch)
209{
210 bbapi::BBApiRequest request{};
211 bbapi::AesDecrypt cmd{ .ciphertext = std::vector<uint8_t>(16, 0), .iv = {}, .key = {}, .length = 32 };
212 EXPECT_THROW_OR_ABORT(std::move(cmd).execute(request), ".*length must equal ciphertext.*");
213}
214
215TEST(BBApiInputValidation, AesDecryptRejectsNonBlockAlignedLength)
216{
217 bbapi::BBApiRequest request{};
218 bbapi::AesDecrypt cmd{ .ciphertext = std::vector<uint8_t>(17, 0), .iv = {}, .key = {}, .length = 17 };
219 EXPECT_THROW_OR_ABORT(std::move(cmd).execute(request), ".*multiple of 16.*");
220}
#define EXPECT_THROW_OR_ABORT(statement, matcher)
Definition assert.hpp:192
std::shared_ptr< Napi::ThreadSafeFunction > bytecode
TYPED_TEST_SUITE(BBApiMsgpack, Commands)
TEST(BBApiInputValidation, NonCanonicalPublicInputRejected)
TYPED_TEST(BBApiMsgpack, DefaultConstructorRoundtrip)
::testing::Types< bbapi::CircuitProve, bbapi::CircuitComputeVk, bbapi::CircuitStats, bbapi::CircuitVerify, bbapi::VkAsFields, bbapi::CircuitWriteSolidityVerifier, bbapi::ChonkStart, bbapi::ChonkLoad, bbapi::ChonkAccumulate, bbapi::ChonkProve, bbapi::ChonkComputeVk, bbapi::ChonkCheckPrecomputedVk, bbapi::ChonkBatchVerify > Commands
Cryptographic primitives command definitions for the Barretenberg RPC API.
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
std::string msgpack_schema_to_string(const auto &obj)
Print's an object's derived msgpack schema as a string.
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)
AES-128 CBC decryption.
std::vector< uint8_t > ciphertext
AES-128 CBC encryption.
std::vector< uint8_t > plaintext
Accumulate the previously loaded circuit into the IVC proof.
Batch-verify multiple Chonk proofs with a single IPA SRS MSM.
Verify that a precomputed verification key matches the circuit.
Compute MegaHonk verification key for a circuit to be accumulated in Chonk.
Load a circuit into the Chonk instance for accumulation.
Generate a proof for all accumulated circuits.
Initialize a new Chonk instance for incremental proof accumulation.
Represents a request to generate a proof. Currently, UltraHonk is the only proving system supported b...
Consolidated command for retrieving circuit information. Combines gate count, circuit size,...
Verify a proof against a verification key and public inputs.
Command to generate Solidity verifier contract.
Convert a verification key to field elements representation. WORKTODO(bbapi): this should become most...
static constexpr uint256_t modulus
std::pair< T, T > msgpack_roundtrip(const T &object)