Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
update_check.test.cpp
Go to the documentation of this file.
2
17
18#include <gmock/gmock.h>
19#include <gtest/gtest.h>
20
22
23namespace bb::avm2::simulation {
24
25using ::testing::_;
26using ::testing::ElementsAre;
27using ::testing::NiceMock;
28using ::testing::Return;
29using ::testing::ReturnRef;
30using ::testing::SizeIs;
31using ::testing::StrictMock;
32using ::testing::TestWithParam;
33
35
36namespace {
37
38TEST(AvmSimulationUpdateCheck, NeverWritten)
39{
40 uint64_t current_timestamp = 100;
42 instance.current_contract_class_id = instance.original_contract_class_id;
44 FF delayed_public_mutable_slot =
46 FF delayed_public_mutable_hash_slot = delayed_public_mutable_slot + UPDATES_DELAYED_PUBLIC_MUTABLE_VALUES_LEN;
47
48 TreeStates tree_states = {};
49 tree_states.public_data_tree.tree.root = 42;
50
51 NiceMock<MockPoseidon2> poseidon2;
52 NiceMock<MockHighLevelMerkleDB> merkle_db;
53 StrictMock<MockLowLevelMerkleDB> low_level_merkle_db;
54 StrictMock<MockRangeCheck> range_check;
55 StrictMock<MockGreaterThan> greater_than;
56
58 GlobalVariables globals{ .timestamp = current_timestamp };
60
61 EXPECT_CALL(
63 storage_read(AztecAddress(CONTRACT_INSTANCE_REGISTRY_CONTRACT_ADDRESS), delayed_public_mutable_hash_slot))
64 .WillRepeatedly(Return(FF(0)));
65 EXPECT_CALL(merkle_db, get_tree_state()).WillRepeatedly(Return(tree_states));
66 EXPECT_CALL(poseidon2, hash(_)).WillRepeatedly([](const std::vector<FF>& input) { return poseidon2::hash(input); });
67
68 update_check.check_current_class_id(derived_address, instance);
69
70 EXPECT_THAT(event_emitter.dump_events(),
71 ElementsAre(UpdateCheckEvent{
72 .address = derived_address,
73 .current_class_id = instance.current_contract_class_id,
74 .original_class_id = instance.original_contract_class_id,
75 .public_data_tree_root = tree_states.public_data_tree.tree.root,
76 .current_timestamp = current_timestamp,
77 .update_hash = 0,
78 .update_preimage_metadata = 0,
79 .update_preimage_pre_class_id = 0,
80 .update_preimage_post_class_id = 0,
81 .delayed_public_mutable_slot = delayed_public_mutable_slot,
82 }));
83
84 // Negative: class id must be original class id
85 instance.current_contract_class_id = instance.current_contract_class_id + 1;
86 EXPECT_THROW_WITH_MESSAGE(update_check.check_current_class_id(derived_address, instance),
87 "Current class id.*does not match expected class id.*");
88}
89
90struct TestParams {
97};
98
99std::vector<TestParams> hash_nonzero_tests = {
100 TestParams{ // Hash is not zero, but scheduled value change is zeroed out
101 // Only delay has been touched
102 .original_class_id = 27,
103 .current_class_id = 27,
104 .should_throw = false },
105 TestParams{ // Hash is not zero, but scheduled value change is zeroed out
106 // Only delay has been touched
107 .original_class_id = 27,
108 .current_class_id = 28,
109 .should_throw = true },
110 TestParams{ .original_class_id = 27,
111 .current_class_id = 2,
112 .update_pre_class = 2, // From 2
113 .update_post_class = 3, // To 3
114 .update_timestamp_of_change = 101, // At timestamp after current
115 .should_throw = false },
116 TestParams{ .original_class_id = 27,
117 .current_class_id = 2,
118 .update_pre_class = 2, // From 2
119 .update_post_class = 3, // To 3
120 .update_timestamp_of_change = 99, // At timestamp before current
121 .should_throw = true },
122 TestParams{ .original_class_id = 27,
123 .current_class_id = 3,
124 .update_pre_class = 2, // From 2
125 .update_post_class = 3, // To 3
126 .update_timestamp_of_change = 99, // At timestamp before current
127 .should_throw = false },
128 TestParams{ .original_class_id = 27,
129 .current_class_id = 3,
130 .update_pre_class = 2, // From 2
131 .update_post_class = 3, // To 3
132 .update_timestamp_of_change = 101, // At timestamp after current
133 .should_throw = true },
134 TestParams{ .original_class_id = 27,
135 .current_class_id = 3,
136 .update_pre_class = 2, // From 2
137 .update_post_class = 3, // To 3
138 .update_timestamp_of_change = 100, // At current (past) timestamp
139 .should_throw = false },
140 TestParams{ .original_class_id = 27,
141 .current_class_id = 2,
142 .update_pre_class = 2, // From 2
143 .update_post_class = 3, // To 3
144 .update_timestamp_of_change = 100, // At current (past) timestamp
145 .should_throw = true },
146 TestParams{ .original_class_id = 1,
147 .current_class_id = 1,
148 .update_pre_class = 0, // From original
149 .update_post_class = 3, // To 3
150 .update_timestamp_of_change = 101, // At timestamp after current
151 .should_throw = false },
152 TestParams{ .original_class_id = 1,
153 .current_class_id = 3,
154 .update_pre_class = 0, // From original
155 .update_post_class = 3, // To 3
156 .update_timestamp_of_change = 101, // At timestamp after current
157 .should_throw = true },
158};
159
160class UpdateCheckHashNonzeroTest : public TestWithParam<TestParams> {};
161
162TEST_P(UpdateCheckHashNonzeroTest, WithHash)
163{
164 const auto& param = GetParam();
165
166 uint64_t current_timestamp = 100;
167 ContractInstance instance = testing::random_contract_instance();
168 instance.current_contract_class_id = param.current_class_id;
169 instance.original_contract_class_id = param.original_class_id;
170
172 FF delayed_public_mutable_slot =
174 FF delayed_public_mutable_hash_slot = delayed_public_mutable_slot + UPDATES_DELAYED_PUBLIC_MUTABLE_VALUES_LEN;
175 FF delayed_public_mutable_leaf_slot = poseidon2::hash(
176 { DOM_SEP__PUBLIC_LEAF_SLOT, CONTRACT_INSTANCE_REGISTRY_CONTRACT_ADDRESS, delayed_public_mutable_hash_slot });
177
178 FF update_metadata = FF(static_cast<uint64_t>(123) << 32) + param.update_timestamp_of_change;
179 std::vector<FF> update_preimage = { update_metadata, param.update_pre_class, param.update_post_class };
180 std::vector<FF> update_preimage_slots;
181
182 for (size_t i = 0; i < update_preimage.size(); ++i) {
185 delayed_public_mutable_slot + i });
186 update_preimage_slots.push_back(leaf_slot);
187 }
188
189 FF update_hash = poseidon2::hash(update_preimage);
190
191 TreeStates tree_states = {};
192 tree_states.public_data_tree.tree.root = 42;
193
194 NiceMock<MockPoseidon2> poseidon2;
195 NiceMock<MockHighLevelMerkleDB> merkle_db;
196 NiceMock<MockLowLevelMerkleDB> mock_low_level_merkle_db;
197 NiceMock<MockRangeCheck> range_check;
198
199 NoopEventEmitter<FieldGreaterThanEvent> field_gt_event_emitter;
200 FieldGreaterThan mock_field_gt(range_check, field_gt_event_emitter);
201 NoopEventEmitter<GreaterThanEvent> greater_than_event_emitter;
202 GreaterThan greater_than(mock_field_gt, range_check, greater_than_event_emitter);
203
204 EventEmitter<UpdateCheckEvent> event_emitter;
205 GlobalVariables globals{ .timestamp = current_timestamp };
206 UpdateCheck update_check(poseidon2, range_check, greater_than, merkle_db, event_emitter, globals);
207
208 EXPECT_CALL(
209 merkle_db,
210 storage_read(AztecAddress(CONTRACT_INSTANCE_REGISTRY_CONTRACT_ADDRESS), delayed_public_mutable_hash_slot))
211 .WillRepeatedly(Return(update_hash));
212 EXPECT_CALL(merkle_db, get_tree_state()).WillRepeatedly(Return(tree_states));
213 EXPECT_CALL(merkle_db, as_unconstrained()).WillRepeatedly(ReturnRef(mock_low_level_merkle_db));
214
215 EXPECT_CALL(mock_low_level_merkle_db, get_low_indexed_leaf(world_state::MerkleTreeId::PUBLIC_DATA_TREE, _))
216 .WillRepeatedly([&](world_state::MerkleTreeId, const FF& leaf_slot) {
217 for (size_t i = 0; i < update_preimage_slots.size(); ++i) {
218 if (leaf_slot == update_preimage_slots[i]) {
219 return GetLowIndexedLeafResponse(true, static_cast<uint64_t>(i));
220 }
221 }
222 throw std::runtime_error("Leaf not found");
223 });
224
225 EXPECT_CALL(mock_low_level_merkle_db, get_leaf_preimage_public_data_tree(_))
226 .WillRepeatedly([&](const uint64_t& index) {
228 PublicDataLeafValue(FF(index) + delayed_public_mutable_leaf_slot, update_preimage[index]), 0, 0);
229 });
230
231 EXPECT_CALL(poseidon2, hash(_)).WillRepeatedly([](const std::vector<FF>& input) { return poseidon2::hash(input); });
232
233 EXPECT_CALL(range_check, assert_range(_, _)).WillRepeatedly([](const uint128_t& value, const uint8_t& range) {
234 if (range > 128) {
235 throw std::runtime_error("Range checks aren't supported for bit-sizes > 128");
236 }
237 if (range == 128) {
238 return;
239 }
240 if (value > (static_cast<uint128_t>(1) << range)) {
241 throw std::runtime_error("Value is out of range");
242 }
243 });
244
245 if (param.should_throw) {
246 EXPECT_THROW_WITH_MESSAGE(update_check.check_current_class_id(derived_address, instance),
247 "Current class id.*does not match expected class id.*");
248 EXPECT_THAT(event_emitter.dump_events(), SizeIs(0));
249 } else {
250 update_check.check_current_class_id(derived_address, instance);
251 EXPECT_THAT(event_emitter.dump_events(),
252 ElementsAre(UpdateCheckEvent{
253 .address = derived_address,
254 .current_class_id = instance.current_contract_class_id,
255 .original_class_id = instance.original_contract_class_id,
256 .public_data_tree_root = tree_states.public_data_tree.tree.root,
257 .current_timestamp = current_timestamp,
258 .update_hash = update_hash,
259 .update_preimage_metadata = update_metadata,
260 .update_preimage_pre_class_id = param.update_pre_class,
261 .update_preimage_post_class_id = param.update_post_class,
262 .delayed_public_mutable_slot = delayed_public_mutable_slot,
263 }));
264 }
265}
266
267INSTANTIATE_TEST_SUITE_P(AvmSimulationUpdateCheck, UpdateCheckHashNonzeroTest, ::testing::ValuesIn(hash_nonzero_tests));
268
269TEST(AvmSimulationUpdateCheck, HashMismatch)
270{
271 uint64_t current_timestamp = 100;
272 ContractInstance instance = testing::random_contract_instance();
273 instance.current_contract_class_id = instance.original_contract_class_id;
275 FF delayed_public_mutable_slot =
277 FF delayed_public_mutable_hash_slot = delayed_public_mutable_slot + UPDATES_DELAYED_PUBLIC_MUTABLE_VALUES_LEN;
278 FF delayed_public_mutable_leaf_slot = poseidon2::hash(
279 { DOM_SEP__PUBLIC_LEAF_SLOT, CONTRACT_INSTANCE_REGISTRY_CONTRACT_ADDRESS, delayed_public_mutable_hash_slot });
280
281 TreeSnapshots trees = {};
282
283 NiceMock<MockPoseidon2> poseidon2;
284 NiceMock<MockHighLevelMerkleDB> merkle_db;
285 NiceMock<MockLowLevelMerkleDB> mock_low_level_merkle_db;
286 StrictMock<MockRangeCheck> range_check;
287 StrictMock<MockGreaterThan> greater_than;
288
289 EventEmitter<UpdateCheckEvent> event_emitter;
290 GlobalVariables globals{ .timestamp = current_timestamp };
291 UpdateCheck update_check(poseidon2, range_check, greater_than, merkle_db, event_emitter, globals);
292
293 EXPECT_CALL(
294 merkle_db,
295 storage_read(AztecAddress(CONTRACT_INSTANCE_REGISTRY_CONTRACT_ADDRESS), delayed_public_mutable_hash_slot))
296 .WillRepeatedly(Return(FF(27)));
297 EXPECT_CALL(mock_low_level_merkle_db, get_tree_roots()).WillRepeatedly(Return(trees));
298 EXPECT_CALL(merkle_db, as_unconstrained()).WillRepeatedly(ReturnRef(mock_low_level_merkle_db));
299
300 EXPECT_CALL(mock_low_level_merkle_db, get_low_indexed_leaf(world_state::MerkleTreeId::PUBLIC_DATA_TREE, _))
301 .WillRepeatedly([&](world_state::MerkleTreeId, const FF& leaf_slot) {
302 return GetLowIndexedLeafResponse(true, static_cast<uint64_t>(leaf_slot - delayed_public_mutable_leaf_slot));
303 });
304
305 EXPECT_CALL(mock_low_level_merkle_db, get_leaf_preimage_public_data_tree(_))
306 .WillRepeatedly([&](const uint64_t& index) {
308 PublicDataLeafValue(FF(index) + delayed_public_mutable_leaf_slot, 0), 0, 0);
309 });
310
311 EXPECT_CALL(poseidon2, hash(_)).WillRepeatedly([](const std::vector<FF>& input) { return poseidon2::hash(input); });
312
313 EXPECT_THROW_WITH_MESSAGE(update_check.check_current_class_id(derived_address, instance),
314 "Stored hash does not match preimage hash");
315 EXPECT_THAT(event_emitter.dump_events(), SizeIs(0));
316}
317
318} // namespace
319
320} // namespace bb::avm2::simulation
GreaterThan greater_than
#define EXPECT_THROW_WITH_MESSAGE(code, expectedMessageRegex)
Definition assert.hpp:193
std::shared_ptr< Napi::ThreadSafeFunction > instance
#define UPDATES_DELAYED_PUBLIC_MUTABLE_VALUES_LEN
#define DOM_SEP__PUBLIC_STORAGE_MAP_SLOT
#define UPDATED_CLASS_IDS_SLOT
#define DOM_SEP__PUBLIC_LEAF_SLOT
#define CONTRACT_INSTANCE_REGISTRY_CONTRACT_ADDRESS
RangeCheck range_check
StrictMock< MockHighLevelMerkleDB > merkle_db
Native Poseidon2 hash function implementation.
Definition poseidon2.hpp:22
static FF hash(const std::vector< FF > &input)
Hashes a vector of field elements.
StrictMock< MockFieldGreaterThan > mock_field_gt
EventEmitter< DataCopyEvent > event_emitter
StrictMock< MockUpdateCheck > update_check
TEST_P(AvmRecursiveTestsParameterized, TwoLayerAvmRecursion)
A test of the Two Layer AVM recursive verifier.
INSTANTIATE_TEST_SUITE_P(PaddingVariants, AvmRecursiveTestsParameterized, ::testing::Values(false, true), [](const auto &info) { return info.param ? "Padded" :"Unpadded";})
AVM range check gadget for witness generation.
crypto::Poseidon2< crypto::Poseidon2Bn254ScalarFieldParams > poseidon2
IndexedLeaf< PublicDataLeafValue > PublicDataTreeLeafPreimage
::bb::crypto::merkle_tree::PublicDataLeafValue PublicDataLeafValue
Definition db.hpp:38
FF compute_contract_address(const ContractInstance &contract_instance)
ContractInstance random_contract_instance()
Definition fixtures.cpp:159
AvmFlavorSettings::FF FF
Definition field.hpp:10
TEST(BoomerangMegaCircuitBuilder, BasicCircuit)
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
unsigned __int128 uint128_t
Definition serialize.hpp:45
NoopEventEmitter< GreaterThanEvent > greater_than_event_emitter
NoopEventEmitter< FieldGreaterThanEvent > field_gt_event_emitter
bool should_throw
FF update_timestamp_of_change
FF original_class_id
FF current_class_id
FF update_pre_class
FF update_post_class
AppendOnlyTreeSnapshot tree