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
13
14#include <gmock/gmock.h>
15#include <gtest/gtest.h>
16
18
19namespace bb::avm2::simulation {
20
21using ::testing::_;
22using ::testing::ElementsAre;
23using ::testing::NiceMock;
24using ::testing::Return;
25using ::testing::ReturnRef;
26using ::testing::SizeIs;
27using ::testing::StrictMock;
28using ::testing::TestWithParam;
29
31
32namespace {
33
34TEST(AvmSimulationUpdateCheck, NeverWritten)
35{
36 uint64_t current_timestamp = 100;
38 instance.current_contract_class_id = instance.original_contract_class_id;
40 FF delayed_public_mutable_slot = poseidon2::hash({ UPDATED_CLASS_IDS_SLOT, derived_address });
41 FF delayed_public_mutable_hash_slot = delayed_public_mutable_slot + UPDATES_DELAYED_PUBLIC_MUTABLE_VALUES_LEN;
42
43 TreeStates tree_states = {};
44 tree_states.public_data_tree.tree.root = 42;
45
46 NiceMock<MockPoseidon2> poseidon2;
47 NiceMock<MockHighLevelMerkleDB> merkle_db;
48 StrictMock<MockLowLevelMerkleDB> low_level_merkle_db;
49 StrictMock<MockRangeCheck> range_check;
50 StrictMock<MockGreaterThan> greater_than;
51
53 GlobalVariables globals{ .timestamp = current_timestamp };
55
56 EXPECT_CALL(
58 storage_read(AztecAddress(CONTRACT_INSTANCE_REGISTRY_CONTRACT_ADDRESS), delayed_public_mutable_hash_slot))
59 .WillRepeatedly(Return(FF(0)));
60 EXPECT_CALL(merkle_db, get_tree_state()).WillRepeatedly(Return(tree_states));
61 EXPECT_CALL(poseidon2, hash(_)).WillRepeatedly([](const std::vector<FF>& input) { return poseidon2::hash(input); });
62
63 update_check.check_current_class_id(derived_address, instance);
64
65 EXPECT_THAT(event_emitter.dump_events(),
66 ElementsAre(UpdateCheckEvent{
67 .address = derived_address,
68 .current_class_id = instance.current_contract_class_id,
69 .original_class_id = instance.original_contract_class_id,
70 .public_data_tree_root = tree_states.public_data_tree.tree.root,
71 .current_timestamp = current_timestamp,
72 .update_hash = 0,
73 .update_preimage_metadata = 0,
74 .update_preimage_pre_class_id = 0,
75 .update_preimage_post_class_id = 0,
76 .delayed_public_mutable_slot = delayed_public_mutable_slot,
77 }));
78
79 // Negative: class id must be original class id
80 instance.current_contract_class_id = instance.current_contract_class_id + 1;
81 EXPECT_THROW_WITH_MESSAGE(update_check.check_current_class_id(derived_address, instance),
82 "Current class id.*does not match expected class id.*");
83}
84
85struct TestParams {
92};
93
94std::vector<TestParams> hash_nonzero_tests = {
95 TestParams{ // Hash is not zero, but scheduled value change is zeroed out
96 // Only delay has been touched
97 .original_class_id = 27,
98 .current_class_id = 27,
99 .should_throw = false },
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 = 28,
104 .should_throw = true },
105 TestParams{ .original_class_id = 27,
106 .current_class_id = 2,
107 .update_pre_class = 2, // From 2
108 .update_post_class = 3, // To 3
109 .update_timestamp_of_change = 101, // At timestamp after current
110 .should_throw = false },
111 TestParams{ .original_class_id = 27,
112 .current_class_id = 2,
113 .update_pre_class = 2, // From 2
114 .update_post_class = 3, // To 3
115 .update_timestamp_of_change = 99, // At timestamp before current
116 .should_throw = true },
117 TestParams{ .original_class_id = 27,
118 .current_class_id = 3,
119 .update_pre_class = 2, // From 2
120 .update_post_class = 3, // To 3
121 .update_timestamp_of_change = 99, // At timestamp before current
122 .should_throw = false },
123 TestParams{ .original_class_id = 27,
124 .current_class_id = 3,
125 .update_pre_class = 2, // From 2
126 .update_post_class = 3, // To 3
127 .update_timestamp_of_change = 101, // At timestamp after current
128 .should_throw = true },
129 TestParams{ .original_class_id = 27,
130 .current_class_id = 3,
131 .update_pre_class = 2, // From 2
132 .update_post_class = 3, // To 3
133 .update_timestamp_of_change = 100, // At current (past) timestamp
134 .should_throw = false },
135 TestParams{ .original_class_id = 27,
136 .current_class_id = 2,
137 .update_pre_class = 2, // From 2
138 .update_post_class = 3, // To 3
139 .update_timestamp_of_change = 100, // At current (past) timestamp
140 .should_throw = true },
141 TestParams{ .original_class_id = 1,
142 .current_class_id = 1,
143 .update_pre_class = 0, // From original
144 .update_post_class = 3, // To 3
145 .update_timestamp_of_change = 101, // At timestamp after current
146 .should_throw = false },
147 TestParams{ .original_class_id = 1,
148 .current_class_id = 3,
149 .update_pre_class = 0, // From original
150 .update_post_class = 3, // To 3
151 .update_timestamp_of_change = 101, // At timestamp after current
152 .should_throw = true },
153};
154
155class UpdateCheckHashNonzeroTest : public TestWithParam<TestParams> {};
156
157TEST_P(UpdateCheckHashNonzeroTest, WithHash)
158{
159 const auto& param = GetParam();
160
161 uint64_t current_timestamp = 100;
162 ContractInstance instance = testing::random_contract_instance();
163 instance.current_contract_class_id = param.current_class_id;
164 instance.original_contract_class_id = param.original_class_id;
165
167 FF delayed_public_mutable_slot = poseidon2::hash({ UPDATED_CLASS_IDS_SLOT, derived_address });
168 FF delayed_public_mutable_hash_slot = delayed_public_mutable_slot + UPDATES_DELAYED_PUBLIC_MUTABLE_VALUES_LEN;
169 FF delayed_public_mutable_leaf_slot = poseidon2::hash(
170 { DOM_SEP__PUBLIC_LEAF_INDEX, CONTRACT_INSTANCE_REGISTRY_CONTRACT_ADDRESS, delayed_public_mutable_hash_slot });
171
172 FF update_metadata = FF(static_cast<uint64_t>(123) << 32) + param.update_timestamp_of_change;
173 std::vector<FF> update_preimage = { update_metadata, param.update_pre_class, param.update_post_class };
174 std::vector<FF> update_preimage_slots;
175
176 for (size_t i = 0; i < update_preimage.size(); ++i) {
179 delayed_public_mutable_slot + i });
180 update_preimage_slots.push_back(leaf_slot);
181 }
182
183 FF update_hash = poseidon2::hash(update_preimage);
184
185 TreeStates tree_states = {};
186 tree_states.public_data_tree.tree.root = 42;
187
188 NiceMock<MockPoseidon2> poseidon2;
189 NiceMock<MockHighLevelMerkleDB> merkle_db;
190 NiceMock<MockLowLevelMerkleDB> mock_low_level_merkle_db;
191 NiceMock<MockRangeCheck> range_check;
192
193 NoopEventEmitter<FieldGreaterThanEvent> field_gt_event_emitter;
194 FieldGreaterThan mock_field_gt(range_check, field_gt_event_emitter);
195 NoopEventEmitter<GreaterThanEvent> greater_than_event_emitter;
196 GreaterThan greater_than(mock_field_gt, range_check, greater_than_event_emitter);
197
198 EventEmitter<UpdateCheckEvent> event_emitter;
199 GlobalVariables globals{ .timestamp = current_timestamp };
200 UpdateCheck update_check(poseidon2, range_check, greater_than, merkle_db, event_emitter, globals);
201
202 EXPECT_CALL(
203 merkle_db,
204 storage_read(AztecAddress(CONTRACT_INSTANCE_REGISTRY_CONTRACT_ADDRESS), delayed_public_mutable_hash_slot))
205 .WillRepeatedly(Return(update_hash));
206 EXPECT_CALL(merkle_db, get_tree_state()).WillRepeatedly(Return(tree_states));
207 EXPECT_CALL(merkle_db, as_unconstrained()).WillRepeatedly(ReturnRef(mock_low_level_merkle_db));
208
209 EXPECT_CALL(mock_low_level_merkle_db, get_low_indexed_leaf(world_state::MerkleTreeId::PUBLIC_DATA_TREE, _))
210 .WillRepeatedly([&](world_state::MerkleTreeId, const FF& leaf_slot) {
211 for (size_t i = 0; i < update_preimage_slots.size(); ++i) {
212 if (leaf_slot == update_preimage_slots[i]) {
213 return GetLowIndexedLeafResponse(true, static_cast<uint64_t>(i));
214 }
215 }
216 throw std::runtime_error("Leaf not found");
217 });
218
219 EXPECT_CALL(mock_low_level_merkle_db, get_leaf_preimage_public_data_tree(_))
220 .WillRepeatedly([&](const uint64_t& index) {
222 PublicDataLeafValue(FF(index) + delayed_public_mutable_leaf_slot, update_preimage[index]), 0, 0);
223 });
224
225 EXPECT_CALL(poseidon2, hash(_)).WillRepeatedly([](const std::vector<FF>& input) { return poseidon2::hash(input); });
226
227 EXPECT_CALL(range_check, assert_range(_, _)).WillRepeatedly([](const uint128_t& value, const uint8_t& range) {
228 if (range > 128) {
229 throw std::runtime_error("Range checks aren't supported for bit-sizes > 128");
230 }
231 if (range == 128) {
232 return;
233 }
234 if (value > (static_cast<uint128_t>(1) << range)) {
235 throw std::runtime_error("Value is out of range");
236 }
237 });
238
239 if (param.should_throw) {
240 EXPECT_THROW_WITH_MESSAGE(update_check.check_current_class_id(derived_address, instance),
241 "Current class id.*does not match expected class id.*");
242 EXPECT_THAT(event_emitter.dump_events(), SizeIs(0));
243 } else {
244 update_check.check_current_class_id(derived_address, instance);
245 EXPECT_THAT(event_emitter.dump_events(),
246 ElementsAre(UpdateCheckEvent{
247 .address = derived_address,
248 .current_class_id = instance.current_contract_class_id,
249 .original_class_id = instance.original_contract_class_id,
250 .public_data_tree_root = tree_states.public_data_tree.tree.root,
251 .current_timestamp = current_timestamp,
252 .update_hash = update_hash,
253 .update_preimage_metadata = update_metadata,
254 .update_preimage_pre_class_id = param.update_pre_class,
255 .update_preimage_post_class_id = param.update_post_class,
256 .delayed_public_mutable_slot = delayed_public_mutable_slot,
257 }));
258 }
259}
260
261INSTANTIATE_TEST_SUITE_P(AvmSimulationUpdateCheck, UpdateCheckHashNonzeroTest, ::testing::ValuesIn(hash_nonzero_tests));
262
263TEST(AvmSimulationUpdateCheck, HashMismatch)
264{
265 uint64_t current_timestamp = 100;
266 ContractInstance instance = testing::random_contract_instance();
267 instance.current_contract_class_id = instance.original_contract_class_id;
269 FF delayed_public_mutable_slot = poseidon2::hash({ UPDATED_CLASS_IDS_SLOT, derived_address });
270 FF delayed_public_mutable_hash_slot = delayed_public_mutable_slot + UPDATES_DELAYED_PUBLIC_MUTABLE_VALUES_LEN;
271 FF delayed_public_mutable_leaf_slot = poseidon2::hash(
272 { DOM_SEP__PUBLIC_LEAF_INDEX, CONTRACT_INSTANCE_REGISTRY_CONTRACT_ADDRESS, delayed_public_mutable_hash_slot });
273
274 TreeSnapshots trees = {};
275
276 NiceMock<MockPoseidon2> poseidon2;
277 NiceMock<MockHighLevelMerkleDB> merkle_db;
278 NiceMock<MockLowLevelMerkleDB> mock_low_level_merkle_db;
279 StrictMock<MockRangeCheck> range_check;
280 StrictMock<MockGreaterThan> greater_than;
281
282 EventEmitter<UpdateCheckEvent> event_emitter;
283 GlobalVariables globals{ .timestamp = current_timestamp };
284 UpdateCheck update_check(poseidon2, range_check, greater_than, merkle_db, event_emitter, globals);
285
286 EXPECT_CALL(
287 merkle_db,
288 storage_read(AztecAddress(CONTRACT_INSTANCE_REGISTRY_CONTRACT_ADDRESS), delayed_public_mutable_hash_slot))
289 .WillRepeatedly(Return(FF(27)));
290 EXPECT_CALL(mock_low_level_merkle_db, get_tree_roots()).WillRepeatedly(Return(trees));
291 EXPECT_CALL(merkle_db, as_unconstrained()).WillRepeatedly(ReturnRef(mock_low_level_merkle_db));
292
293 EXPECT_CALL(mock_low_level_merkle_db, get_low_indexed_leaf(world_state::MerkleTreeId::PUBLIC_DATA_TREE, _))
294 .WillRepeatedly([&](world_state::MerkleTreeId, const FF& leaf_slot) {
295 return GetLowIndexedLeafResponse(true, static_cast<uint64_t>(leaf_slot - delayed_public_mutable_leaf_slot));
296 });
297
298 EXPECT_CALL(mock_low_level_merkle_db, get_leaf_preimage_public_data_tree(_))
299 .WillRepeatedly([&](const uint64_t& index) {
301 PublicDataLeafValue(FF(index) + delayed_public_mutable_leaf_slot, 0), 0, 0);
302 });
303
304 EXPECT_CALL(poseidon2, hash(_)).WillRepeatedly([](const std::vector<FF>& input) { return poseidon2::hash(input); });
305
306 EXPECT_THROW_WITH_MESSAGE(update_check.check_current_class_id(derived_address, instance),
307 "Stored hash does not match preimage hash");
308 EXPECT_THAT(event_emitter.dump_events(), SizeIs(0));
309}
310
311} // namespace
312
313} // namespace bb::avm2::simulation
INSTANTIATE_TEST_SUITE_P(AcirTests, AcirIntegrationSingleTest, testing::Values("a_1327_concrete_in_generic", "a_1_mul", "a_2_div", "a_3_add", "a_4_sub", "a_5_over", "a_6", "a_6_array", "a_7", "a_7_function", "aes128_encrypt", "arithmetic_binary_operations", "array_dynamic", "array_dynamic_blackbox_input", "array_dynamic_main_output", "array_dynamic_nested_blackbox_input", "array_eq", "array_if_cond_simple", "array_len", "array_neq", "array_sort", "array_to_slice", "array_to_slice_constant_length", "assert", "assert_statement", "assign_ex", "bigint", "bit_and", "bit_not", "bit_shifts_comptime", "bit_shifts_runtime", "blake3", "bool_not", "bool_or", "break_and_continue", "brillig_acir_as_brillig", "brillig_array_eq", "brillig_array_to_slice", "brillig_arrays", "brillig_assert", "brillig_bit_shifts_runtime", "brillig_blake2s", "brillig_blake3", "brillig_calls", "brillig_calls_array", "brillig_calls_conditionals", "brillig_conditional", "brillig_cow", "brillig_cow_assign", "brillig_cow_regression", "brillig_ecdsa_secp256k1", "brillig_ecdsa_secp256r1", "brillig_embedded_curve", "brillig_fns_as_values", "brillig_hash_to_field", "brillig_identity_function", "brillig_keccak", "brillig_loop", "brillig_nested_arrays", "brillig_not", "brillig_oracle", "brillig_pedersen", "brillig_recursion", "brillig_references", "brillig_schnorr", "brillig_sha256", "brillig_signed_cmp", "brillig_signed_div", "brillig_slices", "brillig_to_be_bytes", "brillig_to_bits", "brillig_to_bytes_integration", "brillig_to_le_bytes", "brillig_top_level", "brillig_uninitialized_arrays", "brillig_wrapping", "cast_bool", "closures_mut_ref", "conditional_1", "conditional_2", "conditional_regression_421", "conditional_regression_547", "conditional_regression_661", "conditional_regression_short_circuit", "conditional_regression_underflow", "custom_entry", "databus", "debug_logs", "diamond_deps_0", "double_verify_nested_proof", "double_verify_proof", "ecdsa_secp256k1", "ecdsa_secp256r1", "ecdsa_secp256r1_3x", "eddsa", "embedded_curve_ops", "field_attribute", "generics", "global_consts", "hash_to_field", "hashmap", "higher_order_functions", "if_else_chain", "import", "inline_never_basic", "integer_array_indexing", "keccak256", "main_bool_arg", "main_return", "merkle_insert", "missing_closure_env", "modules", "modules_more", "modulus", "nested_array_dynamic", "nested_array_dynamic_simple", "nested_array_in_slice", "nested_arrays_from_brillig", "no_predicates_basic", "no_predicates_brillig", "no_predicates_numeric_generic_poseidon", "operator_overloading", "pedersen_check", "pedersen_commitment", "pedersen_hash", "poseidon_bn254_hash", "poseidonsponge_x5_254", "pred_eq", "prelude", "references", "regression", "regression_2660", "regression_3051", "regression_3394", "regression_3607", "regression_3889", "regression_4088", "regression_4124", "regression_4202", "regression_4449", "regression_4709", "regression_5045", "regression_capacity_tracker", "regression_mem_op_predicate", "regression_method_cannot_be_found", "regression_struct_array_conditional", "schnorr", "sha256", "sha2_byte", "side_effects_constrain_array", "signed_arithmetic", "signed_comparison", "signed_division", "simple_2d_array", "simple_add_and_ret_arr", "simple_array_param", "simple_bitwise", "simple_comparison", "simple_mut", "simple_not", "simple_print", "simple_program_addition", "simple_radix", "simple_shield", "simple_shift_left_right", "slice_coercion", "slice_dynamic_index", "slice_loop", "slices", "strings", "struct", "struct_array_inputs", "struct_fields_ordering", "struct_inputs", "submodules", "to_be_bytes", "to_bytes_consistent", "to_bytes_integration", "to_le_bytes", "trait_as_return_type", "trait_impl_base_type", "traits_in_crates_1", "traits_in_crates_2", "tuple_inputs", "tuples", "type_aliases", "u128", "u16_support", "unconstrained_empty", "unit_value", "unsafe_range_constraint", "witness_compression", "xor"))
TEST_P(AcirIntegrationSingleTest, DISABLED_ProveAndVerifyProgram)
GreaterThan greater_than
#define EXPECT_THROW_WITH_MESSAGE(code, expectedMessageRegex)
Definition assert.hpp:192
std::shared_ptr< Napi::ThreadSafeFunction > instance
#define UPDATES_DELAYED_PUBLIC_MUTABLE_VALUES_LEN
#define UPDATED_CLASS_IDS_SLOT
#define DOM_SEP__PUBLIC_LEAF_INDEX
#define CONTRACT_INSTANCE_REGISTRY_CONTRACT_ADDRESS
StrictMock< MockHighLevelMerkleDB > merkle_db
static FF hash(const std::vector< FF > &input)
Hashes a vector of field elements.
StrictMock< MockFieldGreaterThan > mock_field_gt
EventEmitter< DataCopyEvent > event_emitter
RangeCheck range_check
StrictMock< MockUpdateCheck > update_check
void hash(State &state) noexcept
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:44
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