Skip to content

Commit de6a6e4

Browse files
refactor(zkevm): optimize legacy sstore/ssload operation
1 parent 851f9f5 commit de6a6e4

File tree

1 file changed

+48
-43
lines changed

1 file changed

+48
-43
lines changed

tests/zkevm/test_worst_stateful_opcodes.py

Lines changed: 48 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -185,21 +185,26 @@ def test_worst_address_state_warm(
185185
)
186186

187187

188-
class StorageAction:
189-
"""Enum for storage actions."""
188+
class LoadAction:
189+
"""Enum for sload actions."""
190190

191191
READ = 1
192-
WRITE_SAME_VALUE = 2
193-
WRITE_NEW_VALUE = 3
192+
193+
194+
class StoreAction:
195+
"""Enum for sstore actions."""
196+
197+
WRITE_SAME_VALUE = 1
198+
WRITE_NEW_VALUE = 2
194199

195200

196201
@pytest.mark.valid_from("Cancun")
197202
@pytest.mark.parametrize(
198203
"storage_action",
199204
[
200-
pytest.param(StorageAction.READ, id="SSLOAD"),
201-
pytest.param(StorageAction.WRITE_SAME_VALUE, id="SSTORE same value"),
202-
pytest.param(StorageAction.WRITE_NEW_VALUE, id="SSTORE new value"),
205+
pytest.param(LoadAction.READ, id="SSLOAD"),
206+
pytest.param(StoreAction.WRITE_SAME_VALUE, id="SSTORE same value"),
207+
pytest.param(StoreAction.WRITE_NEW_VALUE, id="SSTORE new value"),
203208
],
204209
)
205210
@pytest.mark.parametrize(
@@ -214,7 +219,7 @@ def test_worst_storage_access_cold(
214219
blockchain_test: BlockchainTestFiller,
215220
pre: Alloc,
216221
fork: Fork,
217-
storage_action: StorageAction,
222+
storage_action: LoadAction | StoreAction,
218223
absent_slots: bool,
219224
):
220225
"""Test running a block with as many cold storage slot accesses as possible."""
@@ -223,17 +228,17 @@ def test_worst_storage_access_cold(
223228
attack_gas_limit = Environment().gas_limit
224229

225230
cost = gas_costs.G_COLD_SLOAD # All accesses are always cold
226-
if storage_action == StorageAction.WRITE_NEW_VALUE:
231+
if storage_action == StoreAction.WRITE_NEW_VALUE:
227232
if not absent_slots:
228233
cost += gas_costs.G_STORAGE_RESET
229234
else:
230235
cost += gas_costs.G_STORAGE_SET
231-
elif storage_action == StorageAction.WRITE_SAME_VALUE:
236+
elif storage_action == StoreAction.WRITE_SAME_VALUE:
232237
if absent_slots:
233238
cost += gas_costs.G_STORAGE_SET
234239
else:
235240
cost += gas_costs.G_WARM_SLOAD
236-
elif storage_action == StorageAction.READ:
241+
elif storage_action == LoadAction.READ:
237242
cost += gas_costs.G_WARM_SLOAD
238243

239244
intrinsic_gas_cost_calc = fork.transaction_intrinsic_cost_calculator()
@@ -242,30 +247,29 @@ def test_worst_storage_access_cold(
242247
blocks = []
243248

244249
# Contract code
245-
execution_code_body = Bytecode()
246-
if storage_action == StorageAction.WRITE_SAME_VALUE:
247-
# All the storage slots in the contract are initialized to their index.
248-
# That is, storage slot `i` is initialized to `i`.
249-
execution_code_body = Op.SSTORE(Op.DUP1, Op.DUP1)
250-
elif storage_action == StorageAction.WRITE_NEW_VALUE:
251-
# The new value 2^256-1 is guaranteed to be different from the initial value.
252-
execution_code_body = Op.SSTORE(Op.DUP2, Op.NOT(0))
253-
elif storage_action == StorageAction.READ:
254-
execution_code_body = Op.POP(Op.SLOAD(Op.DUP1))
255-
256-
execution_code = Op.PUSH4(num_target_slots) + While(
257-
body=execution_code_body,
258-
condition=Op.PUSH1(1) + Op.SWAP1 + Op.SUB + Op.DUP1 + Op.ISZERO + Op.ISZERO,
259-
)
250+
execution_code = Bytecode()
251+
252+
if isinstance(storage_action, LoadAction):
253+
execution_code = sum(Op.SLOAD(i) for i in reversed(range(num_target_slots)))
254+
elif isinstance(storage_action, StoreAction):
255+
if storage_action == StoreAction.WRITE_SAME_VALUE:
256+
# All the storage slots in the contract are initialized to their index.
257+
# That is, storage slot `i` is initialized to `i`.
258+
execution_code += sum(Op.SSTORE(i, i) for i in reversed(range(num_target_slots)))
259+
elif storage_action == StoreAction.WRITE_NEW_VALUE:
260+
# The new value 2^256-1 is guaranteed to be different from the initial value.
261+
execution_code += sum(
262+
Op.SSTORE(i, Op.NOT(0)) for i in reversed(range(num_target_slots))
263+
)
264+
260265
execution_code_address = pre.deploy_contract(code=execution_code)
261266

262267
# Contract creation
263-
slots_init = Bytecode()
264-
if not absent_slots:
265-
slots_init = Op.PUSH4(num_target_slots) + While(
266-
body=Op.SSTORE(Op.DUP1, Op.DUP1),
267-
condition=Op.PUSH1(1) + Op.SWAP1 + Op.SUB + Op.DUP1 + Op.ISZERO + Op.ISZERO,
268-
)
268+
slots_init = (
269+
Bytecode()
270+
if absent_slots
271+
else sum(Op.SSTORE(i, i) for i in reversed(range(num_target_slots)))
272+
)
269273

270274
# To create the contract, we apply the slots_init code to initialize the storage slots
271275
# (int the case of absent_slots=False) and then copy the execution code to the contract.
@@ -310,16 +314,17 @@ def test_worst_storage_access_cold(
310314
@pytest.mark.parametrize(
311315
"storage_action",
312316
[
313-
pytest.param(StorageAction.READ, id="SLOAD"),
314-
pytest.param(StorageAction.WRITE_SAME_VALUE, id="SSTORE same value"),
315-
pytest.param(StorageAction.WRITE_NEW_VALUE, id="SSTORE new value"),
317+
pytest.param(LoadAction.READ, id="SLOAD"),
318+
pytest.param(StoreAction.WRITE_SAME_VALUE, id="SSTORE same value"),
319+
pytest.param(StoreAction.WRITE_NEW_VALUE, id="SSTORE new value"),
316320
],
317321
)
318322
@pytest.mark.slow()
319323
def test_worst_storage_access_warm(
320324
blockchain_test: BlockchainTestFiller,
321325
pre: Alloc,
322-
storage_action: StorageAction,
326+
storage_action: LoadAction | StoreAction,
327+
fork: Fork,
323328
):
324329
"""Test running a block with as many warm storage slot accesses as possible."""
325330
env = Environment(gas_limit=100_000_000_000)
@@ -332,16 +337,16 @@ def test_worst_storage_access_warm(
332337

333338
# Contract code
334339
execution_code_body = Bytecode()
335-
if storage_action == StorageAction.WRITE_SAME_VALUE:
340+
if storage_action == StoreAction.WRITE_SAME_VALUE:
336341
execution_code_body = Op.SSTORE(0, Op.DUP1)
337-
elif storage_action == StorageAction.WRITE_NEW_VALUE:
338-
execution_code_body = Op.PUSH1(1) + Op.ADD + Op.SSTORE(0, Op.DUP1)
339-
elif storage_action == StorageAction.READ:
342+
elif storage_action == StoreAction.WRITE_NEW_VALUE:
343+
execution_code_body = Op.SSTORE(0, Op.NOT(0))
344+
elif storage_action == LoadAction.READ:
340345
execution_code_body = Op.POP(Op.SLOAD(0))
341346

342-
execution_code = Op.PUSH1(storage_slot_initial_value) + While(
343-
body=execution_code_body,
344-
)
347+
calldata = Op.PUSH1(storage_slot_initial_value)
348+
execution_code = code_loop_precompile_call(calldata, execution_code_body, fork)
349+
345350
execution_code_address = pre.deploy_contract(code=execution_code)
346351

347352
creation_code = (

0 commit comments

Comments
 (0)