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_FALSE(retrieval_events_dump[0].instance_not_found_error);
113 EXPECT_FALSE(retrieval_events_dump[0].limit_error);
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 hashing_events_dump = hashing_events.dump_events();
149 EXPECT_THAT(hashing_events_dump, SizeIs(0)); // No hashing for deduplicated bytecode
150 decomposition_events_dump = decomposition_events.dump_events();
151 EXPECT_THAT(decomposition_events_dump, SizeIs(0)); // No decomposition for deduplicated retrieval
152
153 // Deduplication case 2: Different address with same bytecode
154 AztecAddress address2 = address1 + 1; // force a different address
155 ContractInstance instance2 = testing::random_contract_instance();
156 instance2.current_contract_class_id = instance1.current_contract_class_id + 1; // force a different class id
157
158 // Expected interactions for different address with same bytecode
159 EXPECT_CALL(retrieved_bytecodes_tree_check, get_snapshot()).Times(2);
160 EXPECT_CALL(contract_instance_manager, get_contract_instance(address2))
161 .WillOnce(Return(std::make_optional(instance2)));
162 EXPECT_CALL(retrieved_bytecodes_tree_check, contains(instance2.current_contract_class_id)).WillOnce(Return(true));
163 EXPECT_CALL(retrieved_bytecodes_tree_check, size()).WillOnce(Return(1));
164 EXPECT_CALL(retrieved_bytecodes_tree_check, insert(instance2.current_contract_class_id));
165
166 EXPECT_CALL(contract_db, get_contract_class(instance2.current_contract_class_id))
167 .WillOnce(Return(std::make_optional(klass))); // Same class/bytecode
168 EXPECT_CALL(contract_db, get_bytecode_commitment(instance2.current_contract_class_id))
169 .WillOnce(Return(std::make_optional(bytecode_commitment)));
170 // No hashing should occur since we've already processed this bytecode
171 EXPECT_CALL(merkle_db, get_tree_state()).WillOnce(Return(tree_states));
172
173 // Third retrieval with different address but same bytecode - should be deduplicated
174 BytecodeId result3 = tx_bytecode_manager.get_bytecode(address2);
175 EXPECT_EQ(result3, bytecode_commitment);
176
177 // Verify events after third retrieval - retrieval event emitted, but no hashing or decomposition
178 retrieval_events_dump = retrieval_events.dump_events();
179 EXPECT_THAT(retrieval_events_dump, SizeIs(1));
180 EXPECT_EQ(retrieval_events_dump[0].address, address2);
181 EXPECT_EQ(retrieval_events_dump[0].bytecode_id, bytecode_commitment);
182 hashing_events_dump = hashing_events.dump_events();
183 EXPECT_THAT(hashing_events_dump, SizeIs(0)); // No hashing for deduplicated bytecode
184 decomposition_events_dump = decomposition_events.dump_events();
185 EXPECT_THAT(decomposition_events_dump, SizeIs(0)); // No decomposition for deduplicated bytecode
186}
187
188TEST_F(BytecodeManagerTest, TooManyBytecodes)
189{
190 TxBytecodeManager tx_bytecode_manager(contract_db,
191 merkle_db,
193 range_check,
195 retrieved_bytecodes_tree_check,
199
201 ContractInstance instance1 = testing::random_contract_instance();
202 ContractClass klass = testing::random_contract_class();
203
204 EXPECT_CALL(retrieved_bytecodes_tree_check, get_snapshot());
205 EXPECT_CALL(merkle_db, get_tree_state());
206
207 EXPECT_CALL(contract_instance_manager, get_contract_instance(address1))
208 .WillOnce(Return(std::make_optional(instance1)));
209
210 EXPECT_CALL(retrieved_bytecodes_tree_check, contains(instance1.current_contract_class_id)).WillOnce(Return(false));
211 EXPECT_CALL(retrieved_bytecodes_tree_check, size()).WillOnce(Return(MAX_PUBLIC_CALLS_TO_UNIQUE_CONTRACT_CLASS_IDS));
212
213 // Base case: First retrieval - should do full processing
214 EXPECT_THROW_WITH_MESSAGE(tx_bytecode_manager.get_bytecode(address1),
215 "Can't retrieve more than " +
217
218 auto retrieval_events_dump = retrieval_events.dump_events();
219 EXPECT_THAT(retrieval_events_dump, SizeIs(1));
220 EXPECT_EQ(retrieval_events_dump[0].address, address1);
221 EXPECT_EQ(retrieval_events_dump[0].bytecode_id, 0);
222 EXPECT_FALSE(retrieval_events_dump[0].instance_not_found_error);
223 EXPECT_TRUE(retrieval_events_dump[0].limit_error);
224}
225
226// Test about a contract address nullifier not found error (contract address not in nullifier tree)
227TEST_F(BytecodeManagerTest, ContractAddressNullifierNotFoundError)
228{
229 StrictMock<MockUpdateCheck> update_check;
230 StrictMock<MockFieldGreaterThan> field_gt;
231 ProtocolContracts protocol_contracts = {};
232 EventEmitter<ContractInstanceRetrievalEvent> contract_retrieval_events;
233
234 ContractInstanceManager real_contract_instance_manager(
235 contract_db, merkle_db, update_check, field_gt, protocol_contracts, contract_retrieval_events);
236
237 TxBytecodeManager tx_bytecode_manager(contract_db,
238 merkle_db,
240 range_check,
241 real_contract_instance_manager,
242 retrieved_bytecodes_tree_check,
246
248 ContractInstance instance = testing::random_contract_instance();
249 EXPECT_CALL(contract_db, get_contract_instance(address)).WillOnce(Return(instance));
250 EXPECT_CALL(field_gt, ff_gt(FF(MAX_PROTOCOL_CONTRACTS), address - 1)).WillOnce(Return(false));
251 EXPECT_CALL(retrieved_bytecodes_tree_check, get_snapshot());
252 EXPECT_CALL(merkle_db, get_tree_state()).Times(2);
253 EXPECT_CALL(merkle_db, nullifier_exists(FF(CONTRACT_INSTANCE_REGISTRY_CONTRACT_ADDRESS), address))
254 .WillOnce(Return(false));
255
256 EXPECT_THROW_WITH_MESSAGE(tx_bytecode_manager.get_bytecode(address),
257 "Contract " + field_to_string(address) + " is not deployed");
258
259 auto retrieval_events_dump = retrieval_events.dump_events();
260 EXPECT_THAT(retrieval_events_dump, SizeIs(1));
261 EXPECT_EQ(retrieval_events_dump[0].address, address);
262 EXPECT_EQ(retrieval_events_dump[0].bytecode_id, 0);
263 EXPECT_TRUE(retrieval_events_dump[0].instance_not_found_error);
264 EXPECT_FALSE(retrieval_events_dump[0].limit_error);
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
FieldGreaterThan field_gt
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
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< MockRetrievedBytecodesTreeCheck > retrieved_bytecodes_tree_check
StrictMock< MockContractDB > contract_db
StrictMock< MockContractInstanceManager > contract_instance_manager
RangeCheck range_check
#define EXPECT_THROW_WITH_MESSAGE(code, expectedMessage)
Definition macros.hpp:7
void hash(State &state) noexcept
ContractClass random_contract_class(size_t bytecode_size)
Definition fixtures.cpp:175
ContractInstance random_contract_instance()
Definition fixtures.cpp:159
std::string field_to_string(const FF &ff)
Definition stringify.cpp:5
AvmFlavorSettings::FF FF
Definition field.hpp:10
TEST_F(IPATest, ChallengesAreZero)
Definition ipa.test.cpp:185
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