Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
serialization.test.cpp
Go to the documentation of this file.
1#include <gmock/gmock.h>
2#include <gtest/gtest.h>
3#include <stdexcept>
4
7
8namespace bb::avm2 {
9namespace {
10
13using simulation::InstrDeserializationError;
15using simulation::Instruction;
17
18// Testing serialization with some u8 variants
19TEST(SerializationTest, Not8RoundTrip)
20{
21 const Instruction instr = { .opcode = WireOpCode::NOT_8,
22 .addressing_mode = 5,
23 .operands = { Operand::from<uint8_t>(123), Operand::from<uint8_t>(45) } };
24 const auto decoded = deserialize_instruction(instr.serialize(), 0);
25 EXPECT_EQ(instr, decoded);
26}
27
28// Testing serialization with some u16 variants
29TEST(SerializationTest, Add16RoundTrip)
30{
31 const Instruction instr = {
32 .opcode = WireOpCode::ADD_16,
33 .addressing_mode = 3,
34 .operands = { Operand::from<uint16_t>(1000), Operand::from<uint16_t>(1001), Operand::from<uint16_t>(1002) }
35 };
36 const auto decoded = deserialize_instruction(instr.serialize(), 0);
37 EXPECT_EQ(instr, decoded);
38}
39
40// Testing serialization with a u32 variant
41TEST(SerializationTest, Jumpi32RoundTrip)
42{
43 const Instruction instr = { .opcode = WireOpCode::JUMPI_32,
44 .addressing_mode = 7,
45 .operands = { Operand::from<uint16_t>(12345), Operand::from<uint32_t>(678901234) } };
46 const auto decoded = deserialize_instruction(instr.serialize(), 0);
47 EXPECT_EQ(instr, decoded);
48}
49
50// Testing serialization with a u64 variant
51TEST(SerializationTest, Set64RoundTrip)
52{
53 const uint64_t value_64 = 0xABCDEF0123456789LLU;
54
55 const Instruction instr = { .opcode = WireOpCode::SET_64,
56 .addressing_mode = 2,
57 .operands = { Operand::from<uint16_t>(1002),
58 Operand::from<uint8_t>(static_cast<uint8_t>(MemoryTag::U64)),
59 Operand::from<uint64_t>(value_64) } };
60 const auto decoded = deserialize_instruction(instr.serialize(), 0);
61 EXPECT_EQ(instr, decoded);
62}
63
64// Testing serialization with a u128 variant
65TEST(SerializationTest, Set128RoundTrip)
66{
67 const uint128_t value_128 = (uint128_t{ 0x123456789ABCDEF0LLU } << 64) + uint128_t{ 0xABCDEF0123456789LLU };
68
69 const Instruction instr = { .opcode = WireOpCode::SET_128,
70 .addressing_mode = 2,
71 .operands = { Operand::from<uint16_t>(1002),
72 Operand::from<uint8_t>(static_cast<uint8_t>(MemoryTag::U128)),
73 Operand::from<uint128_t>(value_128) } };
74 const auto decoded = deserialize_instruction(instr.serialize(), 0);
75 EXPECT_EQ(instr, decoded);
76}
77
78// Testing serialization with ff variant
79TEST(SerializationTest, SetFFRoundTrip)
80{
81 const FF large_ff = FF::modulus - 981723;
82
83 const Instruction instr = { .opcode = WireOpCode::SET_FF,
84 .addressing_mode = 2,
85 .operands = { Operand::from<uint16_t>(1002),
86 Operand::from<uint8_t>(static_cast<uint8_t>(MemoryTag::FF)),
87 Operand::from<FF>(large_ff) } };
88 const auto decoded = deserialize_instruction(instr.serialize(), 0);
89 EXPECT_EQ(instr, decoded);
90}
91
92// Testing serialization with ff variant which is larger than the modulus.
93// Round trip would not work as multiple equivalent values over 256 bits map
94// to the same FF value.
95TEST(SerializationTest, DeserializeLargeFF)
96{
97 const uint256_t value_256 = FF::modulus + 145;
98
99 // We first serialize a "dummy" instruction and then substitute the immediate value encoded as the last 32 bytes.
100 const Instruction instr = { .opcode = WireOpCode::SET_FF,
101 .addressing_mode = 0,
102 .operands = { Operand::from<uint16_t>(1002),
103 Operand::from<uint8_t>(static_cast<uint8_t>(MemoryTag::U8)),
104 Operand::from<FF>(FF::modulus - 1) } };
105 auto serialized_instruction = instr.serialize();
106
107 const auto buf = to_buffer(value_256);
108 serialized_instruction.insert(serialized_instruction.end() - 32, buf.begin(), buf.end());
109
110 const auto decoded = deserialize_instruction(serialized_instruction, 0);
111 ASSERT_EQ(3, decoded.operands.size());
112 EXPECT_EQ(decoded.operands[2].as<FF>(), 145);
113}
114
115// Testing deserialization pc out of range error
116TEST(SerializationTest, PCOutOfRange)
117{
118 std::vector<uint8_t> bytecode;
119 bytecode.resize(35, 0);
120
121 try {
123 } catch (const InstrDeserializationError& error) {
124 EXPECT_EQ(error.type, InstrDeserializationEventError::PC_OUT_OF_RANGE);
125 EXPECT_TRUE(error.message.has_value());
126 }
127}
128
129// Testing deserialization wire opcode out of range error
130TEST(SerializationTest, OpcodeOutOfRange)
131{
132 std::vector<uint8_t> bytecode;
133 bytecode.push_back(static_cast<uint8_t>(WireOpCode::LAST_OPCODE_SENTINEL) + 1); // Invalid opcode
134
135 try {
137 } catch (const InstrDeserializationError& error) {
138 EXPECT_EQ(error.type, InstrDeserializationEventError::OPCODE_OUT_OF_RANGE);
139 EXPECT_TRUE(error.message.has_value());
140 }
141}
142
143// Testing deserialization instruction out of range error
144TEST(SerializationTest, InstructionOutOfRange)
145{
146 // Create a valid SET_16 instruction
147 Instruction instr = { .opcode = WireOpCode::SET_16,
148 .addressing_mode = 2,
149 .operands = { Operand::from<uint16_t>(1002),
150 Operand::from<uint8_t>(static_cast<uint8_t>(MemoryTag::U16)),
151 Operand::from<uint16_t>(12345) } };
152
153 auto bytecode = instr.serialize();
154
155 // Truncate the bytecode
156 bytecode.resize(bytecode.size() - 1);
157
158 try {
160 } catch (const InstrDeserializationError& error) {
161 EXPECT_EQ(error.type, InstrDeserializationEventError::INSTRUCTION_OUT_OF_RANGE);
162 EXPECT_TRUE(error.message.has_value());
163 }
164}
165
166// Testing check_tag with a valid instruction for wire opcode SET_128
167TEST(SerializationTest, CheckTagValid)
168{
169 Instruction instr = { .opcode = WireOpCode::SET_128,
170 .addressing_mode = 2,
171 .operands = { Operand::from<uint16_t>(1002),
172 Operand::from<uint8_t>(static_cast<uint8_t>(MemoryTag::U128)),
173 Operand::from<uint128_t>(12345) } };
174 EXPECT_TRUE(check_tag(instr));
175}
176
177// Testing check_tag with an invalid tag for wire opcode SET_128
178TEST(SerializationTest, CheckTagInvalid)
179{
180 Instruction instr = { .opcode = WireOpCode::SET_128,
181 .addressing_mode = 2,
182 .operands = { Operand::from<uint16_t>(1002),
183 Operand::from<uint8_t>(static_cast<uint8_t>(MemoryTag::MAX) + 1),
184 Operand::from<uint128_t>(12345) } };
185 EXPECT_FALSE(check_tag(instr));
186}
187
188// Testing check_tag with an invalid instruction for wire opcode SET_128, not enough operands
189TEST(SerializationTest, CheckTagInvalidNotEnoughOperands)
190{
191 Instruction instr = { .opcode = WireOpCode::SET_128,
192 .addressing_mode = 2,
193 .operands = { Operand::from<uint16_t>(1002) } };
194 // check_tag() throws as 'not enough operands' should have been caught by previous INSTRUCTION_OUT_OF_RANGE checks,
195 // where the correct wire format was used.
196 EXPECT_THROW(check_tag(instr), std::runtime_error);
197}
198
199// Testing check_tag with an invalid instruction for wire opcode SET_128, tag is not a byte
200TEST(SerializationTest, CheckTagInvalidTagNotByte)
201{
202 Instruction instr = { .opcode = WireOpCode::SET_128,
203 .addressing_mode = 2,
204 .operands = { Operand::from<uint16_t>(1002),
205 Operand::from<uint16_t>(static_cast<uint8_t>(MemoryTag::U128)),
206 Operand::from<uint128_t>(12345) } };
207 EXPECT_FALSE(check_tag(instr));
208}
209
210} // namespace
211} // namespace bb::avm2
std::shared_ptr< Napi::ThreadSafeFunction > bytecode
bool check_tag(const Instruction &instruction)
Checks whether the tag operand of an instruction is a valid MemoryTag. Called by bytecode managers du...
Instruction deserialize_instruction(std::span< const uint8_t > bytecode, size_t pos)
Attempts to deserialize the instruction at position pos in bytecode. Called by bytecode managers duri...
Instruction
Enumeration of VM instructions that can be executed.
TEST(BoomerangMegaCircuitBuilder, BasicCircuit)
std::vector< uint8_t > to_buffer(T const &value)
unsigned __int128 uint128_t
Definition serialize.hpp:45
static constexpr uint256_t modulus