1#include <gmock/gmock.h>
2#include <gtest/gtest.h>
34using tracegen::ExecutionTraceBuilder;
35using tracegen::IndexedTreeCheckTraceBuilder;
36using tracegen::PublicDataTreeTraceBuilder;
37using tracegen::TestTraceContainer;
40using simulation::EventEmitter;
41using simulation::IndexedTreeCheck;
43using simulation::MockExecutionIdManager;
44using simulation::MockFieldGreaterThan;
45using simulation::MockMerkleCheck;
46using simulation::MockPoseidon2;
47using simulation::PublicDataTreeCheck;
52using simulation::WrittenPublicDataSlotsTreeCheck;
55using testing::NiceMock;
63TEST(SStoreConstrainingTest, PositiveTest)
65 TestTraceContainer
trace({
66 { { C::execution_sel_execute_sstore, 1 },
67 { C::execution_sel_gas_sstore, 1 },
68 { C::execution_dynamic_da_gas_factor, 1 },
69 { C::execution_register_0_, 27 },
70 { C::execution_register_1_, 42 },
71 { C::execution_prev_written_public_data_slots_tree_size, 5 },
72 { C::execution_max_data_writes_reached, 0 },
73 { C::execution_remaining_data_writes_inv,
75 { C::execution_sel_write_public_data, 1 },
78 check_relation<sstore>(trace);
81TEST(SStoreConstrainingTest, NegativeDynamicL2GasIsZero)
83 TestTraceContainer
trace({ {
84 { C::execution_sel_execute_sstore, 1 },
85 { C::execution_dynamic_l2_gas_factor, 1 },
90TEST(SStoreConstrainingTest, MaxDataWritesReached)
92 TestTraceContainer
trace({
94 { C::execution_sel_execute_sstore, 1 },
95 { C::execution_prev_written_public_data_slots_tree_size,
97 { C::execution_remaining_data_writes_inv, 0 },
98 { C::execution_max_data_writes_reached, 1 },
103 trace.
set(C::execution_max_data_writes_reached, 0, 0);
106 "SSTORE_MAX_DATA_WRITES_REACHED");
109TEST(SStoreConstrainingTest, OpcodeError)
111 TestTraceContainer
trace({
113 { C::execution_sel_execute_sstore, 1 },
114 { C::execution_dynamic_da_gas_factor, 1 },
115 { C::execution_max_data_writes_reached, 1 },
116 { C::execution_sel_opcode_error, 1 },
119 { C::execution_sel_execute_sstore, 1 },
120 { C::execution_dynamic_da_gas_factor, 0 },
121 { C::execution_max_data_writes_reached, 0 },
122 { C::execution_is_static, 1 },
123 { C::execution_sel_opcode_error, 1 },
126 { C::execution_sel_execute_sstore, 1 },
127 { C::execution_dynamic_da_gas_factor, 0 },
128 { C::execution_max_data_writes_reached, 1 },
129 { C::execution_sel_opcode_error, 0 },
134 trace.
set(C::execution_dynamic_da_gas_factor, 0, 0);
137 "OPCODE_ERROR_IF_OVERFLOW_OR_STATIC");
139 trace.
set(C::execution_dynamic_da_gas_factor, 0, 1);
141 trace.
set(C::execution_is_static, 1, 0);
144 "OPCODE_ERROR_IF_OVERFLOW_OR_STATIC");
147TEST(SStoreConstrainingTest, TreeStateNotChangedOnError)
149 TestTraceContainer
trace({ {
150 { C::execution_sel_execute_sstore, 1 },
151 { C::execution_prev_public_data_tree_root, 27 },
152 { C::execution_prev_public_data_tree_size, 5 },
153 { C::execution_prev_written_public_data_slots_tree_root, 28 },
154 { C::execution_prev_written_public_data_slots_tree_size, 6 },
155 { C::execution_public_data_tree_root, 27 },
156 { C::execution_public_data_tree_size, 5 },
157 { C::execution_written_public_data_slots_tree_root, 28 },
158 { C::execution_written_public_data_slots_tree_size, 6 },
159 { C::execution_sel_opcode_error, 1 },
162 check_relation<sstore>(trace,
169 trace.
set(C::execution_written_public_data_slots_tree_root, 0, 29);
171 "SSTORE_WRITTEN_SLOTS_ROOT_NOT_CHANGED");
174 trace.
set(C::execution_written_public_data_slots_tree_size, 0, 7);
176 "SSTORE_WRITTEN_SLOTS_SIZE_NOT_CHANGED");
179 trace.
set(C::execution_public_data_tree_root, 0, 29);
181 "SSTORE_PUBLIC_DATA_TREE_ROOT_NOT_CHANGED");
184 trace.
set(C::execution_public_data_tree_size, 0, 7);
186 "SSTORE_PUBLIC_DATA_TREE_SIZE_NOT_CHANGED");
191TEST(SStoreConstrainingTest, NegativeGhostRowStorageWrite_RelationsOnly)
194 TestTraceContainer
trace({
196 { C::execution_sel_execute_sstore, 0 },
197 { C::execution_sel_write_public_data, 1 },
198 { C::execution_register_0_, 999 },
199 { C::execution_register_1_, 666 },
200 { C::execution_contract_address, 0xDEADBEEF },
201 { C::execution_sel_opcode_error, 0 },
210TEST(SStoreConstrainingTest, Interactions)
213 NiceMock<MockFieldGreaterThan>
field_gt;
217 EventEmitter<IndexedTreeCheckEvent> indexed_tree_check_emitter;
224 EventEmitter<PublicDataTreeCheckEvent> public_data_tree_check_event_emitter;
225 PublicDataTreeCheck public_data_tree_check(
234 uint64_t low_leaf_index = 30;
235 std::vector<FF> low_leaf_sibling_path = { 1, 2, 3, 4, 5 };
237 AppendOnlyTreeSnapshot public_data_tree_before = AppendOnlyTreeSnapshot{
239 .next_available_leaf_index = 128,
243 EXPECT_CALL(
poseidon2, hash(_)).WillRepeatedly([](
const std::vector<FF>&
inputs) {
246 EXPECT_CALL(
field_gt, ff_gt(_, _)).WillRepeatedly([](
const FF&
a,
const FF&
b) {
250 EXPECT_CALL(merkle_check,
write)
251 .WillRepeatedly([]([[maybe_unused]] uint64_t domain_separator,
252 [[maybe_unused]]
FF current_leaf,
256 [[maybe_unused]]
FF prev_root) {
262 auto public_data_tree_after = public_data_tree_check.write(
slot,
267 low_leaf_sibling_path,
268 public_data_tree_before,
274 TestTraceContainer
trace({
276 { C::execution_sel_execute_sstore, 1 },
277 { C::execution_contract_address, contract_address },
278 { C::execution_sel_gas_sstore, 1 },
282 { C::execution_dynamic_da_gas_factor, 1 },
283 { C::execution_register_0_,
value },
284 { C::execution_register_1_,
slot },
285 { C::execution_max_data_writes_reached, 0 },
286 { C::execution_remaining_data_writes_inv,
288 written_slots_tree_before.next_available_leaf_index)
291 { C::execution_sel_write_public_data, 1 },
292 { C::execution_prev_public_data_tree_root, public_data_tree_before.root },
293 { C::execution_prev_public_data_tree_size, public_data_tree_before.next_available_leaf_index },
294 { C::execution_public_data_tree_root, public_data_tree_after.root },
295 { C::execution_public_data_tree_size, public_data_tree_after.next_available_leaf_index },
296 { C::execution_prev_written_public_data_slots_tree_root, written_slots_tree_before.root },
297 { C::execution_prev_written_public_data_slots_tree_size,
298 written_slots_tree_before.next_available_leaf_index },
299 { C::execution_written_public_data_slots_tree_root, written_slots_tree_after.root },
300 { C::execution_written_public_data_slots_tree_size, written_slots_tree_after.next_available_leaf_index },
304 PublicDataTreeTraceBuilder public_data_tree_trace_builder;
305 public_data_tree_trace_builder.process(public_data_tree_check_event_emitter.dump_events(),
trace);
307 IndexedTreeCheckTraceBuilder written_slots_tree_trace_builder;
308 written_slots_tree_trace_builder.process(indexed_tree_check_emitter.dump_events(),
trace);
310 check_relation<sstore>(trace);
328TEST(SStoreConstrainingTest, NegativeFullAttackWithAllTraces)
331 NiceMock<MockFieldGreaterThan>
field_gt;
335 EventEmitter<IndexedTreeCheckEvent> indexed_tree_check_emitter;
341 EventEmitter<PublicDataTreeCheckEvent> public_data_tree_check_event_emitter;
342 PublicDataTreeCheck public_data_tree_check(
352 uint64_t low_leaf_index = 30;
353 std::vector<FF> low_leaf_sibling_path = { 1, 2, 3, 4, 5 };
355 AppendOnlyTreeSnapshot public_data_tree_before = AppendOnlyTreeSnapshot{
357 .next_available_leaf_index = 128,
361 EXPECT_CALL(
poseidon2, hash(_)).WillRepeatedly([](
const std::vector<FF>&
inputs) {
364 EXPECT_CALL(
field_gt, ff_gt(_, _)).WillRepeatedly([](
const FF&
a,
const FF&
b) {
367 EXPECT_CALL(merkle_check,
write)
368 .WillRepeatedly([]([[maybe_unused]] uint64_t domain_separator,
369 [[maybe_unused]]
FF current_leaf,
373 [[maybe_unused]]
FF prev_root) {
379 auto public_data_tree_after = public_data_tree_check.write(
slot,
384 low_leaf_sibling_path,
385 public_data_tree_before,
392 TestTraceContainer
trace;
393 PublicDataTreeTraceBuilder public_data_tree_trace_builder;
394 public_data_tree_trace_builder.process(public_data_tree_check_event_emitter.dump_events(),
trace);
396 IndexedTreeCheckTraceBuilder written_slots_tree_trace_builder;
397 written_slots_tree_trace_builder.process(indexed_tree_check_emitter.dump_events(),
trace);
405 { C::execution_clk, 0 },
406 { C::precomputed_first_row, 1 },
407 { C::execution_sel_execute_sstore, 0 },
408 { C::execution_sel_write_public_data, 1 },
409 { C::execution_contract_address, contract_address },
410 { C::execution_register_0_,
value },
411 { C::execution_register_1_,
slot },
412 { C::execution_sel_opcode_error, 0 },
413 { C::execution_discard, 0 },
414 { C::execution_prev_public_data_tree_root, public_data_tree_before.root },
415 { C::execution_prev_public_data_tree_size, public_data_tree_before.next_available_leaf_index },
416 { C::execution_public_data_tree_root, public_data_tree_after.root },
417 { C::execution_public_data_tree_size, public_data_tree_after.next_available_leaf_index },
418 { C::execution_prev_written_public_data_slots_tree_root, written_slots_tree_before.root },
419 { C::execution_prev_written_public_data_slots_tree_size,
420 written_slots_tree_before.next_available_leaf_index },
421 { C::execution_written_public_data_slots_tree_root, written_slots_tree_after.root },
422 { C::execution_written_public_data_slots_tree_size, written_slots_tree_after.next_available_leaf_index },
#define EXPECT_THROW_WITH_MESSAGE(code, expectedMessageRegex)
#define AVM_EXEC_OP_ID_SSTORE
#define DOM_SEP__WRITTEN_SLOTS_MERKLE
#define AVM_WRITTEN_PUBLIC_DATA_SLOTS_TREE_HEIGHT
#define AVM_WRITTEN_PUBLIC_DATA_SLOTS_TREE_INITIAL_SIZE
#define DOM_SEP__PUBLIC_LEAF_SLOT
#define MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX
FieldGreaterThan field_gt
IndexedTreeCheck indexed_tree_check
static constexpr size_t SR_DYN_L2_GAS_IS_ZERO
static constexpr size_t SR_SSTORE_WRITTEN_SLOTS_SIZE_NOT_CHANGED
static constexpr size_t SR_OPCODE_ERROR_IF_OVERFLOW_OR_STATIC
static constexpr size_t SR_SSTORE_MAX_DATA_WRITES_REACHED
static constexpr size_t SR_SSTORE_WRITTEN_SLOTS_ROOT_NOT_CHANGED
static constexpr size_t SR_SSTORE_PUBLIC_DATA_TREE_SIZE_NOT_CHANGED
static constexpr size_t SR_SSTORE_PUBLIC_DATA_TREE_ROOT_NOT_CHANGED
void set(Column col, uint32_t row, const FF &value)
static FF hash(const std::vector< FF > &input)
Hashes a vector of field elements.
ExecutionIdManager execution_id_manager
IndexedTreeLeafData low_leaf
void check_multipermutation_interaction(tracegen::TestTraceContainer &trace)
void check_interaction(tracegen::TestTraceContainer &trace)
TEST(AvmFixedVKTests, FixedVKCommitments)
Test that the fixed VK commitments agree with the ones computed from precomputed columns.
std::variant< PublicDataTreeReadWriteEvent, CheckPointEventType > PublicDataTreeCheckEvent
crypto::Poseidon2< crypto::Poseidon2Bn254ScalarFieldParams > poseidon2
IndexedLeaf< PublicDataLeafValue > PublicDataTreeLeafPreimage
std::variant< IndexedTreeReadWriteEvent, CheckPointEventType > IndexedTreeCheckEvent
::bb::crypto::merkle_tree::PublicDataLeafValue PublicDataLeafValue
WrittenPublicDataSlotsTree build_public_data_slots_tree()
FF unconstrained_root_from_path(uint64_t domain_separator, const FF &leaf_value, const uint64_t leaf_index, std::span< const FF > path)
FF unconstrained_compute_leaf_slot(const AztecAddress &contract_address, const FF &slot)
lookup_settings< lookup_execution_check_written_storage_slot_settings_ > lookup_execution_check_written_storage_slot_settings
permutation_settings< perm_tx_balance_update_settings_ > perm_tx_balance_update_settings
permutation_settings< perm_sstore_storage_write_settings_ > perm_sstore_storage_write_settings
lookup_settings< lookup_sstore_record_written_storage_slot_settings_ > lookup_sstore_record_written_storage_slot_settings
void write(B &buf, field2< base_field, Params > const &value)
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
constexpr field invert() const noexcept
NiceMock< MockExecution > execution
NiceMock< MockWrittenPublicDataSlotsTreeCheck > written_public_data_slots_tree_check