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
25
26using ::testing::_;
27using ::testing::Return;
28using ::testing::SizeIs;
29using ::testing::StrictMock;
30
31namespace bb::avm2::simulation {
32
33namespace {
34
35// Simple mock for ContractInstanceManagerInterface
36class MockContractInstanceManager : public ContractInstanceManagerInterface {
37 public:
38 MOCK_METHOD(std::optional<ContractInstance>, get_contract_instance, (const FF& contract_address), (override));
39};
40
41class BytecodeManagerTest : public ::testing::Test {
42 protected:
43 BytecodeManagerTest()
45 {}
46
47 StrictMock<MockContractDB> contract_db;
48 StrictMock<MockHighLevelMerkleDB> merkle_db;
49 StrictMock<MockPoseidon2> poseidon2;
50 StrictMock<MockRangeCheck> range_check;
51 StrictMock<MockContractInstanceManager> contract_instance_manager;
52 StrictMock<MockRetrievedBytecodesTreeCheck> retrieved_bytecodes_tree_check;
53
54 EventEmitter<BytecodeRetrievalEvent> retrieval_events;
55 EventEmitter<BytecodeDecompositionEvent> decomposition_events;
56 EventEmitter<InstructionFetchingEvent> instruction_fetching_events;
57 EventEmitter<BytecodeHashingEvent> hashing_events;
58 BytecodeHasher bytecode_hasher;
59};
60
61TEST_F(BytecodeManagerTest, RetrievalAndDeduplication)
62{
63 TxBytecodeManager tx_bytecode_manager(contract_db,
72
73 // Setup for base case
74 AztecAddress address1 = AztecAddress::random_element();
75 ContractInstance instance1 = testing::random_contract_instance();
76 ContractClass klass = testing::random_contract_class();
77
78 // Expected interactions for first retrieval
79
80 EXPECT_CALL(retrieved_bytecodes_tree_check, get_snapshot()).Times(2);
81 EXPECT_CALL(contract_instance_manager, get_contract_instance(address1))
82 .WillOnce(Return(std::make_optional(instance1)));
83
84 EXPECT_CALL(retrieved_bytecodes_tree_check, contains(instance1.current_class_id)).WillOnce(Return(false));
85 EXPECT_CALL(retrieved_bytecodes_tree_check, size()).WillOnce(Return(0));
86 EXPECT_CALL(retrieved_bytecodes_tree_check, insert(instance1.current_class_id));
87
88 EXPECT_CALL(contract_db, get_contract_class(instance1.current_class_id))
89 .WillOnce(Return(std::make_optional(klass)));
90
91 // Let the real bytecode hasher run - it will emit hashing events
92 EXPECT_CALL(poseidon2, hash(_)).WillOnce(Return(klass.public_bytecode_commitment));
93
94 TreeStates tree_states = {};
95 EXPECT_CALL(merkle_db, get_tree_state()).WillOnce(Return(tree_states));
96
97 // Base case: First retrieval - should do full processing
98 BytecodeId result1 = tx_bytecode_manager.get_bytecode(address1);
99 EXPECT_EQ(result1, klass.public_bytecode_commitment);
100
101 // Verify events after first retrieval
102 // Verify retrieval events - should have exactly one retrieval event total
103 auto retrieval_events_dump = retrieval_events.dump_events();
104 EXPECT_THAT(retrieval_events_dump, SizeIs(1));
105 EXPECT_EQ(retrieval_events_dump[0].address, address1);
106 EXPECT_EQ(retrieval_events_dump[0].bytecode_id, klass.public_bytecode_commitment);
107 EXPECT_FALSE(retrieval_events_dump[0].instance_not_found_error);
108 EXPECT_FALSE(retrieval_events_dump[0].limit_error);
109 // Verify hashing events - should have exactly one hashing event total
110 auto hashing_events_dump = hashing_events.dump_events();
111 EXPECT_THAT(hashing_events_dump, SizeIs(1));
112 EXPECT_EQ(hashing_events_dump[0].bytecode_id, klass.public_bytecode_commitment);
113 // Verify decomposition events - should have exactly one decomposition event total
114 auto decomposition_events_dump = decomposition_events.dump_events();
115 EXPECT_THAT(decomposition_events_dump, SizeIs(1));
116 EXPECT_EQ(decomposition_events_dump[0].bytecode_id, klass.public_bytecode_commitment);
117
118 // Deduplication case 1: Same address retrieval
119 // Expected interactions for second retrieval of same address
120 EXPECT_CALL(retrieved_bytecodes_tree_check, get_snapshot()).Times(2);
121 EXPECT_CALL(contract_instance_manager, get_contract_instance(address1))
122 .WillOnce(Return(std::make_optional(instance1)));
123 EXPECT_CALL(retrieved_bytecodes_tree_check, contains(instance1.current_class_id)).WillOnce(Return(true));
124 EXPECT_CALL(retrieved_bytecodes_tree_check, size()).WillOnce(Return(1));
125 EXPECT_CALL(retrieved_bytecodes_tree_check, insert(instance1.current_class_id));
126
127 EXPECT_CALL(contract_db, get_contract_class(instance1.current_class_id))
128 .WillOnce(Return(std::make_optional(klass)));
129 // No hashing should occur for duplicate retrieval
130 EXPECT_CALL(merkle_db, get_tree_state()).WillOnce(Return(tree_states));
131
132 // Second retrieval of same address - should be deduplicated
133 BytecodeId result2 = tx_bytecode_manager.get_bytecode(address1);
134 EXPECT_EQ(result2, klass.public_bytecode_commitment);
135
136 // Verify events after second retrieval - retrieval event emitted, but no hashing or decomposition
137 retrieval_events_dump = retrieval_events.dump_events();
138 EXPECT_THAT(retrieval_events_dump, SizeIs(1));
139 EXPECT_EQ(retrieval_events_dump[0].address, address1);
140 EXPECT_EQ(retrieval_events_dump[0].bytecode_id, klass.public_bytecode_commitment);
141 hashing_events_dump = hashing_events.dump_events();
142 EXPECT_THAT(hashing_events_dump, SizeIs(0)); // No hashing for deduplicated bytecode
143 decomposition_events_dump = decomposition_events.dump_events();
144 EXPECT_THAT(decomposition_events_dump, SizeIs(0)); // No decomposition for deduplicated retrieval
145
146 // Deduplication case 2: Different address with same bytecode
147 AztecAddress address2 = address1 + 1; // force a different address
148 ContractInstance instance2 = testing::random_contract_instance();
149 instance2.current_class_id = instance1.current_class_id + 1; // force a different class id
150
151 // Expected interactions for different address with same bytecode
152 EXPECT_CALL(retrieved_bytecodes_tree_check, get_snapshot()).Times(2);
153 EXPECT_CALL(contract_instance_manager, get_contract_instance(address2))
154 .WillOnce(Return(std::make_optional(instance2)));
155 EXPECT_CALL(retrieved_bytecodes_tree_check, contains(instance2.current_class_id)).WillOnce(Return(true));
156 EXPECT_CALL(retrieved_bytecodes_tree_check, size()).WillOnce(Return(1));
157 EXPECT_CALL(retrieved_bytecodes_tree_check, insert(instance2.current_class_id));
158
159 EXPECT_CALL(contract_db, get_contract_class(instance2.current_class_id))
160 .WillOnce(Return(std::make_optional(klass))); // Same class/bytecode
161 // No hashing should occur since we've already processed this bytecode
162 EXPECT_CALL(merkle_db, get_tree_state()).WillOnce(Return(tree_states));
163
164 // Third retrieval with different address but same bytecode - should be deduplicated
165 BytecodeId result3 = tx_bytecode_manager.get_bytecode(address2);
166 EXPECT_EQ(result3, klass.public_bytecode_commitment);
167
168 // Verify events after third retrieval - retrieval event emitted, but no hashing or decomposition
169 retrieval_events_dump = retrieval_events.dump_events();
170 EXPECT_THAT(retrieval_events_dump, SizeIs(1));
171 EXPECT_EQ(retrieval_events_dump[0].address, address2);
172 EXPECT_EQ(retrieval_events_dump[0].bytecode_id, klass.public_bytecode_commitment);
173 hashing_events_dump = hashing_events.dump_events();
174 EXPECT_THAT(hashing_events_dump, SizeIs(0)); // No hashing for deduplicated bytecode
175 decomposition_events_dump = decomposition_events.dump_events();
176 EXPECT_THAT(decomposition_events_dump, SizeIs(0)); // No decomposition for deduplicated bytecode
177}
178
179TEST_F(BytecodeManagerTest, TooManyBytecodes)
180{
181 TxBytecodeManager tx_bytecode_manager(contract_db,
182 merkle_db,
184 range_check,
186 retrieved_bytecodes_tree_check,
190
191 AztecAddress address1 = AztecAddress::random_element();
192 ContractInstance instance1 = testing::random_contract_instance();
193 ContractClass klass = testing::random_contract_class();
194
195 EXPECT_CALL(retrieved_bytecodes_tree_check, get_snapshot());
196 EXPECT_CALL(merkle_db, get_tree_state());
197
198 EXPECT_CALL(contract_instance_manager, get_contract_instance(address1))
199 .WillOnce(Return(std::make_optional(instance1)));
200
201 EXPECT_CALL(retrieved_bytecodes_tree_check, contains(instance1.current_class_id)).WillOnce(Return(false));
202 EXPECT_CALL(retrieved_bytecodes_tree_check, size()).WillOnce(Return(MAX_PUBLIC_CALLS_TO_UNIQUE_CONTRACT_CLASS_IDS));
203
204 // Base case: First retrieval - should do full processing
205 EXPECT_THROW_WITH_MESSAGE(tx_bytecode_manager.get_bytecode(address1),
206 "Can't retrieve more than " +
208
209 auto retrieval_events_dump = retrieval_events.dump_events();
210 EXPECT_THAT(retrieval_events_dump, SizeIs(1));
211 EXPECT_EQ(retrieval_events_dump[0].address, address1);
212 EXPECT_EQ(retrieval_events_dump[0].bytecode_id, 0);
213 EXPECT_FALSE(retrieval_events_dump[0].instance_not_found_error);
214 EXPECT_TRUE(retrieval_events_dump[0].limit_error);
215}
216
217// Test about a contract address nullifier not found error (contract address not in nullifier tree)
218TEST_F(BytecodeManagerTest, ContractAddressNullifierNotFoundError)
219{
220 StrictMock<MockUpdateCheck> update_check;
221 StrictMock<MockFieldGreaterThan> field_gt;
222 ProtocolContracts protocol_contracts = {};
223 EventEmitter<ContractInstanceRetrievalEvent> contract_retrieval_events;
224
225 ContractInstanceManager real_contract_instance_manager(
226 contract_db, merkle_db, update_check, field_gt, protocol_contracts, contract_retrieval_events);
227
228 TxBytecodeManager tx_bytecode_manager(contract_db,
229 merkle_db,
231 range_check,
232 real_contract_instance_manager,
233 retrieved_bytecodes_tree_check,
237
238 AztecAddress address = AztecAddress::random_element();
239 ContractInstance instance = testing::random_contract_instance();
240 EXPECT_CALL(contract_db, get_contract_instance(address)).WillOnce(Return(instance));
241 EXPECT_CALL(field_gt, ff_gt(FF(MAX_PROTOCOL_CONTRACTS), address - 1)).WillOnce(Return(false));
242 EXPECT_CALL(retrieved_bytecodes_tree_check, get_snapshot());
243 EXPECT_CALL(merkle_db, get_tree_state()).Times(2);
244 EXPECT_CALL(merkle_db, nullifier_exists(FF(CONTRACT_INSTANCE_REGISTRY_CONTRACT_ADDRESS), address))
245 .WillOnce(Return(false));
246
247 EXPECT_THROW_WITH_MESSAGE(tx_bytecode_manager.get_bytecode(address),
248 "Contract " + field_to_string(address) + " is not deployed");
249
250 auto retrieval_events_dump = retrieval_events.dump_events();
251 EXPECT_THAT(retrieval_events_dump, SizeIs(1));
252 EXPECT_EQ(retrieval_events_dump[0].address, address);
253 EXPECT_EQ(retrieval_events_dump[0].bytecode_id, 0);
254 EXPECT_TRUE(retrieval_events_dump[0].instance_not_found_error);
255 EXPECT_FALSE(retrieval_events_dump[0].limit_error);
256
257 auto contract_retrieval_events_dump = contract_retrieval_events.dump_events();
258 EXPECT_THAT(contract_retrieval_events_dump, SizeIs(1));
259 EXPECT_EQ(contract_retrieval_events_dump[0].address, address);
260 EXPECT_FALSE(contract_retrieval_events_dump[0].exists);
261 EXPECT_FALSE(contract_retrieval_events_dump[0].is_protocol_contract);
262 EXPECT_EQ(contract_retrieval_events_dump[0].deployment_nullifier, address);
263 EXPECT_EQ(contract_retrieval_events_dump[0].contract_instance, ContractInstance{});
264}
265
266} // namespace
267} // namespace bb::avm2::simulation
#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< 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:188
typename Flavor::FF FF
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
std::string to_string(bb::avm2::ValueTag tag)
FieldGreaterThan field_gt