Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
bytecode_manager.test.cpp
Go to the documentation of this file.
2
3#include <cstdint>
4#include <gmock/gmock.h>
5#include <gtest/gtest.h>
6#include <memory>
7#include <optional>
8#include <vector>
9
26
27using ::testing::_;
28using ::testing::Return;
29using ::testing::SizeIs;
30using ::testing::StrictMock;
31
32namespace bb::avm2::simulation {
33
34namespace {
35
36// Simple mock for ContractInstanceManagerInterface
37class MockContractInstanceManager : public ContractInstanceManagerInterface {
38 public:
39 MOCK_METHOD(std::optional<ContractInstance>, get_contract_instance, (const FF& contract_address), (override));
40};
41
42class BytecodeManagerTest : public ::testing::Test {
43 protected:
44 BytecodeManagerTest()
46 {}
47
48 StrictMock<MockContractDB> contract_db;
49 StrictMock<MockHighLevelMerkleDB> merkle_db;
50 StrictMock<MockPoseidon2> poseidon2;
51 StrictMock<MockRangeCheck> range_check;
52 StrictMock<MockContractInstanceManager> contract_instance_manager;
53 StrictMock<MockClassIdDerivation> class_id_derivation;
54 StrictMock<MockRetrievedBytecodesTreeCheck> retrieved_bytecodes_tree_check;
55
56 EventEmitter<BytecodeRetrievalEvent> retrieval_events;
57 EventEmitter<BytecodeDecompositionEvent> decomposition_events;
58 EventEmitter<InstructionFetchingEvent> instruction_fetching_events;
59 EventEmitter<BytecodeHashingEvent> hashing_events;
60 BytecodeHasher bytecode_hasher;
61};
62
63TEST_F(BytecodeManagerTest, RetrievalAndDeduplication)
64{
65 TxBytecodeManager tx_bytecode_manager(contract_db,
74
75 // Setup for base case
77 ContractInstance instance1 = testing::random_contract_instance();
78 ContractClass klass = testing::random_contract_class();
79 FF bytecode_commitment = FF::random_element();
80
81 // Expected interactions for first retrieval
82
83 EXPECT_CALL(retrieved_bytecodes_tree_check, get_snapshot()).Times(2);
84 EXPECT_CALL(contract_instance_manager, get_contract_instance(address1))
85 .WillOnce(Return(std::make_optional(instance1)));
86
87 EXPECT_CALL(retrieved_bytecodes_tree_check, contains(instance1.current_contract_class_id)).WillOnce(Return(false));
88 EXPECT_CALL(retrieved_bytecodes_tree_check, size()).WillOnce(Return(0));
89 EXPECT_CALL(retrieved_bytecodes_tree_check, insert(instance1.current_contract_class_id));
90
91 EXPECT_CALL(contract_db, get_contract_class(instance1.current_contract_class_id))
92 .WillOnce(Return(std::make_optional(klass)));
93 EXPECT_CALL(contract_db, get_bytecode_commitment(instance1.current_contract_class_id))
94 .WillRepeatedly(Return(std::make_optional(bytecode_commitment)));
95
96 // Let the real bytecode hasher run - it will emit hashing events
97 EXPECT_CALL(poseidon2, hash(_)).WillOnce(Return(bytecode_commitment));
98
99 TreeStates tree_states = {};
100 EXPECT_CALL(merkle_db, get_tree_state()).WillOnce(Return(tree_states));
101
102 // Base case: First retrieval - should do full processing
103 BytecodeId result1 = tx_bytecode_manager.get_bytecode(address1);
104 EXPECT_EQ(result1, bytecode_commitment);
105
106 // Verify events after first retrieval
107 // Verify retrieval events - should have exactly one retrieval event total
108 auto retrieval_events_dump = retrieval_events.dump_events();
109 EXPECT_THAT(retrieval_events_dump, SizeIs(1));
110 EXPECT_EQ(retrieval_events_dump[0].address, address1);
111 EXPECT_EQ(retrieval_events_dump[0].bytecode_id, bytecode_commitment);
112 EXPECT_TRUE(retrieval_events_dump[0].is_new_class);
113 EXPECT_FALSE(retrieval_events_dump[0].error.has_value());
114 // Verify hashing events - should have exactly one hashing event total
115 auto hashing_events_dump = hashing_events.dump_events();
116 EXPECT_THAT(hashing_events_dump, SizeIs(1));
117 EXPECT_EQ(hashing_events_dump[0].bytecode_id, bytecode_commitment);
118 // Verify decomposition events - should have exactly one decomposition event total
119 auto decomposition_events_dump = decomposition_events.dump_events();
120 EXPECT_THAT(decomposition_events_dump, SizeIs(1));
121 EXPECT_EQ(decomposition_events_dump[0].bytecode_id, bytecode_commitment);
122
123 // Deduplication case 1: Same address retrieval
124 // Expected interactions for second retrieval of same address
125 EXPECT_CALL(retrieved_bytecodes_tree_check, get_snapshot()).Times(2);
126 EXPECT_CALL(contract_instance_manager, get_contract_instance(address1))
127 .WillOnce(Return(std::make_optional(instance1)));
128 EXPECT_CALL(retrieved_bytecodes_tree_check, contains(instance1.current_contract_class_id)).WillOnce(Return(true));
129 EXPECT_CALL(retrieved_bytecodes_tree_check, size()).WillOnce(Return(1));
130 EXPECT_CALL(retrieved_bytecodes_tree_check, insert(instance1.current_contract_class_id));
131
132 EXPECT_CALL(contract_db, get_contract_class(instance1.current_contract_class_id))
133 .WillOnce(Return(std::make_optional(klass)));
134 // get_bytecode_commitment is called even for deduplicated retrievals
135 // (already set up with WillRepeatedly above)
136 // No hashing should occur for duplicate retrieval
137 EXPECT_CALL(merkle_db, get_tree_state()).WillOnce(Return(tree_states));
138
139 // Second retrieval of same address - should be deduplicated
140 BytecodeId result2 = tx_bytecode_manager.get_bytecode(address1);
141 EXPECT_EQ(result2, bytecode_commitment);
142
143 // Verify events after second retrieval - retrieval event emitted, but no hashing or decomposition
144 retrieval_events_dump = retrieval_events.dump_events();
145 EXPECT_THAT(retrieval_events_dump, SizeIs(1));
146 EXPECT_EQ(retrieval_events_dump[0].address, address1);
147 EXPECT_EQ(retrieval_events_dump[0].bytecode_id, bytecode_commitment);
148 EXPECT_FALSE(retrieval_events_dump[0].is_new_class);
149 hashing_events_dump = hashing_events.dump_events();
150 EXPECT_THAT(hashing_events_dump, SizeIs(0)); // No hashing for deduplicated bytecode
151 decomposition_events_dump = decomposition_events.dump_events();
152 EXPECT_THAT(decomposition_events_dump, SizeIs(0)); // No decomposition for deduplicated retrieval
153
154 // Deduplication case 2: Different address with same bytecode
155 AztecAddress address2 = address1 + 1; // force a different address
156 ContractInstance instance2 = testing::random_contract_instance();
157 instance2.current_contract_class_id = instance1.current_contract_class_id + 1; // force a different class id
158
159 // Expected interactions for different address with same bytecode
160 EXPECT_CALL(retrieved_bytecodes_tree_check, get_snapshot()).Times(2);
161 EXPECT_CALL(contract_instance_manager, get_contract_instance(address2))
162 .WillOnce(Return(std::make_optional(instance2)));
163 EXPECT_CALL(retrieved_bytecodes_tree_check, contains(instance2.current_contract_class_id)).WillOnce(Return(true));
164 EXPECT_CALL(retrieved_bytecodes_tree_check, size()).WillOnce(Return(1));
165 EXPECT_CALL(retrieved_bytecodes_tree_check, insert(instance2.current_contract_class_id));
166
167 EXPECT_CALL(contract_db, get_contract_class(instance2.current_contract_class_id))
168 .WillOnce(Return(std::make_optional(klass))); // Same class/bytecode
169 EXPECT_CALL(contract_db, get_bytecode_commitment(instance2.current_contract_class_id))
170 .WillOnce(Return(std::make_optional(bytecode_commitment)));
171 // No hashing should occur since we've already processed this bytecode
172 EXPECT_CALL(merkle_db, get_tree_state()).WillOnce(Return(tree_states));
173
174 // Third retrieval with different address but same bytecode - should be deduplicated
175 BytecodeId result3 = tx_bytecode_manager.get_bytecode(address2);
176 EXPECT_EQ(result3, bytecode_commitment);
177
178 // Verify events after third retrieval - retrieval event emitted, but no hashing or decomposition
179 retrieval_events_dump = retrieval_events.dump_events();
180 EXPECT_THAT(retrieval_events_dump, SizeIs(1));
181 EXPECT_EQ(retrieval_events_dump[0].address, address2);
182 EXPECT_EQ(retrieval_events_dump[0].bytecode_id, bytecode_commitment);
183 EXPECT_FALSE(retrieval_events_dump[0].is_new_class);
184 hashing_events_dump = hashing_events.dump_events();
185 EXPECT_THAT(hashing_events_dump, SizeIs(0)); // No hashing for deduplicated bytecode
186 decomposition_events_dump = decomposition_events.dump_events();
187 EXPECT_THAT(decomposition_events_dump, SizeIs(0)); // No decomposition for deduplicated bytecode
188}
189
190TEST_F(BytecodeManagerTest, TooManyBytecodes)
191{
192 TxBytecodeManager tx_bytecode_manager(contract_db,
193 merkle_db,
195 range_check,
201
203 ContractInstance instance1 = testing::random_contract_instance();
204 ContractClass klass = testing::random_contract_class();
205
206 EXPECT_CALL(retrieved_bytecodes_tree_check, get_snapshot());
207 EXPECT_CALL(merkle_db, get_tree_state());
208
209 EXPECT_CALL(contract_instance_manager, get_contract_instance(address1))
210 .WillOnce(Return(std::make_optional(instance1)));
211
212 EXPECT_CALL(retrieved_bytecodes_tree_check, contains(instance1.current_contract_class_id)).WillOnce(Return(false));
214
215 // Base case: First retrieval - should do full processing
216 EXPECT_THROW_WITH_MESSAGE(tx_bytecode_manager.get_bytecode(address1),
217 "Can't retrieve more than " +
219
220 auto retrieval_events_dump = retrieval_events.dump_events();
221 EXPECT_THAT(retrieval_events_dump, SizeIs(1));
222 EXPECT_EQ(retrieval_events_dump[0].address, address1);
223 EXPECT_EQ(retrieval_events_dump[0].bytecode_id, 0);
224 EXPECT_EQ(retrieval_events_dump[0].error, BytecodeRetrievalEventError::TOO_MANY_BYTECODES);
225}
226
227// Test about a contract address nullifier not found error (contract address not in nullifier tree)
228TEST_F(BytecodeManagerTest, ContractAddressNullifierNotFoundError)
229{
230 StrictMock<MockUpdateCheck> update_check;
231 StrictMock<MockFieldGreaterThan> field_gt;
232 ProtocolContracts protocol_contracts = {};
233 EventEmitter<ContractInstanceRetrievalEvent> contract_retrieval_events;
234
235 ContractInstanceManager real_contract_instance_manager(
236 contract_db, merkle_db, update_check, field_gt, protocol_contracts, contract_retrieval_events);
237
238 TxBytecodeManager tx_bytecode_manager(contract_db,
239 merkle_db,
241 range_check,
242 real_contract_instance_manager,
247
249 ContractInstance instance = testing::random_contract_instance();
250 EXPECT_CALL(contract_db, get_contract_instance(address)).WillOnce(Return(instance));
251 EXPECT_CALL(field_gt, ff_gt(FF(MAX_PROTOCOL_CONTRACTS), address - 1)).WillOnce(Return(false));
252 EXPECT_CALL(retrieved_bytecodes_tree_check, get_snapshot());
253 EXPECT_CALL(merkle_db, get_tree_state()).Times(2);
254 EXPECT_CALL(merkle_db, nullifier_exists(FF(CONTRACT_INSTANCE_REGISTRY_CONTRACT_ADDRESS), address))
255 .WillOnce(Return(false));
256
257 EXPECT_THROW_WITH_MESSAGE(tx_bytecode_manager.get_bytecode(address),
258 "Contract " + field_to_string(address) + " is not deployed");
259
260 auto retrieval_events_dump = retrieval_events.dump_events();
261 EXPECT_THAT(retrieval_events_dump, SizeIs(1));
262 EXPECT_EQ(retrieval_events_dump[0].address, address);
263 EXPECT_EQ(retrieval_events_dump[0].bytecode_id, 0);
264 EXPECT_EQ(retrieval_events_dump[0].error, BytecodeRetrievalEventError::INSTANCE_NOT_FOUND);
265
266 auto contract_retrieval_events_dump = contract_retrieval_events.dump_events();
267 EXPECT_THAT(contract_retrieval_events_dump, SizeIs(1));
268 EXPECT_EQ(contract_retrieval_events_dump[0].address, address);
269 EXPECT_FALSE(contract_retrieval_events_dump[0].exists);
270 EXPECT_FALSE(contract_retrieval_events_dump[0].is_protocol_contract);
271 EXPECT_EQ(contract_retrieval_events_dump[0].deployment_nullifier, address);
272 EXPECT_EQ(contract_retrieval_events_dump[0].contract_instance, ContractInstance{});
273}
274
275} // namespace
276} // namespace bb::avm2::simulation
#define EXPECT_THROW_WITH_MESSAGE(code, expectedMessageRegex)
Definition assert.hpp:193
std::shared_ptr< Napi::ThreadSafeFunction > instance
#define MAX_PUBLIC_CALLS_TO_UNIQUE_CONTRACT_CLASS_IDS
#define MAX_PROTOCOL_CONTRACTS
#define CONTRACT_INSTANCE_REGISTRY_CONTRACT_ADDRESS
FieldGreaterThan field_gt
RetrievedBytecodesTreeCheck retrieved_bytecodes_tree_check
RangeCheck range_check
BytecodeHasher bytecode_hasher
EventEmitter< BytecodeHashingEvent > hashing_events
EventEmitter< BytecodeDecompositionEvent > decomposition_events
StrictMock< MockHighLevelMerkleDB > merkle_db
EventEmitter< InstructionFetchingEvent > instruction_fetching_events
EventEmitter< BytecodeRetrievalEvent > retrieval_events
StrictMock< MockClassIdDerivation > class_id_derivation
StrictMock< MockContractDB > contract_db
StrictMock< MockContractInstanceManager > contract_instance_manager
Native Poseidon2 hash function implementation.
Definition poseidon2.hpp:22
StrictMock< MockUpdateCheck > update_check
AVM range check gadget for witness generation.
ContractClass random_contract_class(size_t bytecode_size)
Definition fixtures.cpp:175
ContractInstance random_contract_instance()
Definition fixtures.cpp:159
AvmFlavorSettings::FF FF
Definition field.hpp:10
std::string field_to_string(const FF &ff)
Definition stringify.cpp:5
TEST_F(IPATest, ChallengesAreZero)
Definition ipa.test.cpp:155
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
std::string to_string(bb::avm2::ValueTag tag)
static field random_element(numeric::RNG *engine=nullptr) noexcept