Skip to content

Commit 21fdf9c

Browse files
update xen tests
1 parent 965908d commit 21fdf9c

File tree

1 file changed

+53
-78
lines changed

1 file changed

+53
-78
lines changed

tests/benchmark/mainnet/test_state_xen.py

Lines changed: 53 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -12,23 +12,20 @@
1212
However, with enough funds (to bloat a contract state), this is thus not the worst scenario.
1313
"""
1414

15-
import math
16-
1715
import pytest
1816

19-
from ethereum_test_base_types.composite_types import Account
20-
from ethereum_test_forks.helpers import Fork
17+
from ethereum_test_forks import Fork
2118
from ethereum_test_tools import (
19+
Account,
2220
Alloc,
2321
Block,
2422
BlockchainTestFiller,
23+
Environment,
2524
Hash,
2625
Transaction,
2726
While,
2827
)
2928
from ethereum_test_tools import Macros as Om
30-
from ethereum_test_types.block_types import Environment
31-
from ethereum_test_types.helpers import compute_create2_address
3229
from ethereum_test_vm import Opcodes as Op
3330

3431

@@ -98,6 +95,7 @@ def test_xen_approve(
9895
pre=pre,
9996
post={}, # TODO: add sanity checks (succesful tx execution and no out-of-gas)
10097
blocks=blocks,
98+
skip_gas_used_validation=True,
10199
)
102100

103101

@@ -182,6 +180,8 @@ def test_xen_approve_change_existing_slots(
182180

183181
current_address += address_incr
184182

183+
# TODO: insert unrelated spam to USDT to bust cache
184+
185185
attack_calldata = Hash(start_address) + approval_value_overwrite
186186

187187
attack_tx = Transaction(
@@ -198,15 +198,13 @@ def test_xen_approve_change_existing_slots(
198198
pre=pre,
199199
post={}, # TODO: add sanity checks (succesful tx execution and no out-of-gas)
200200
blocks=blocks,
201+
skip_gas_used_validation=True,
201202
)
202203

203204

204205
# TODO split this code in all situations: 0->1, 1->2, 1->0
205206
@pytest.mark.valid_from("Frontier")
206-
def test_xen_approve_delete_existing_slots(
207-
blockchain_test: BlockchainTestFiller,
208-
pre: Alloc,
209-
):
207+
def test_xen_approve_delete_existing_slots(blockchain_test: BlockchainTestFiller, pre: Alloc):
210208
"""
211209
Uses the `approve(address,uint256)` method of XEN (ERC20) close to the maximum amount
212210
of slots which could be edited (as opposed to be created) within a single block/transaction.
@@ -297,7 +295,7 @@ def test_xen_approve_delete_existing_slots(
297295
approval_value_fresh = Hash(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE)
298296
approval_value_overwrite = Hash(0)
299297

300-
block_count = 10
298+
block_count = 100
301299

302300
for _ in range(block_count):
303301
setup_calldata = Hash(current_address) + approval_value_fresh
@@ -386,6 +384,7 @@ def test_xen_approve_delete_existing_slots(
386384
pre=pre,
387385
post={}, # TODO: add sanity checks (succesful tx execution and no out-of-gas)
388386
blocks=blocks,
387+
skip_gas_used_validation=True,
389388
)
390389

391390

@@ -401,8 +400,9 @@ def test_xen_approve_delete_existing_slots(
401400
# TODO: set correct fork, XEN might reject on historical forks due to e.g. non-existent opcodes
402401
# NOTE: deploy both XEN (0x06450dEe7FD2Fb8E39061434BAbCFC05599a6Fb8)
403402
# and Math (0x4bBA9B6B49f3dFA6615f079E9d66B0AA68B04A4d) in prestate for the Mainnet scenario!
403+
# NOTE: this requires the timestamp hack, where each new payload moves by at least 24*60*60+1
404+
# seconds (fix me later if we have the tooling for this)
404405
@pytest.mark.valid_from("Frontier")
405-
@pytest.mark.skip(reason="TEMP: disabled test") # TODO fixme
406406
def test_xen_claimrank_and_mint(
407407
blockchain_test: BlockchainTestFiller,
408408
fork: Fork,
@@ -422,9 +422,6 @@ def test_xen_claimrank_and_mint(
422422
# timestamp = 12 TODO: disabled, this is likely not part of EEST to support
423423
# A special CL has to perform edited newPayloads such that we can edit the timestamp
424424

425-
# TODO: adjust this to the right amount of the actual performance test block
426-
num_xen = 1
427-
428425
# NOTE: these contracts MUST be specified for this test to work
429426
# TODO: check how/if EEST enforces this
430427
xen_contract = 0x06450DEE7FD2FB8E39061434BABCFC05599A6FB8
@@ -435,58 +432,40 @@ def test_xen_claimrank_and_mint(
435432

436433
# This is after (!!) deployment (so step 2, not 1): claimMintReward()
437434
calldata_claim_mint_reward = bytes.fromhex("52c7f8dc")
438-
after_initcode_callata = Om.MSTORE(bytes.fromhex("52c7f8dc")) + Op.CALL(
439-
address=(xen_contract),
440-
args_size=len(calldata_claim_mint_reward),
441-
)
442-
443435
# Calldata for claimRank(1)
444436
calldata_claim_rank = bytes.fromhex(
445437
"9ff054df0000000000000000000000000000000000000000000000000000000000000001"
446438
)
439+
after_initcode_callata = (
440+
Om.MSTORE(bytes.fromhex("52c7f8dc"), offset=0)
441+
+ Op.CALL(
442+
address=(xen_contract),
443+
args_size=len(calldata_claim_mint_reward),
444+
)
445+
+ Om.MSTORE(calldata_claim_rank, offset=0)
446+
+ Op.CALL(
447+
address=(xen_contract),
448+
args_size=len(calldata_claim_rank),
449+
)
450+
)
447451

448452
# claimRank(1) and deposits the code to claimMintReward() if this contract is called
453+
# + claimRank(1) again
449454
initcode = (
450-
Om.MSTORE(calldata_claim_rank)
455+
Om.MSTORE(calldata_claim_rank, offset=0)
451456
+ Op.CALL(
452457
address=(xen_contract),
453458
args_size=len(calldata_claim_rank),
454459
)
455-
+ Om.MSTORE(after_initcode_callata)
460+
+ Om.MSTORE(after_initcode_callata, offset=0)
456461
+ Op.RETURN(0, len(after_initcode_callata))
457462
)
458463

459464
# Template code that will be used to deploy a large number of contracts.
460465
initcode_address = pre.deploy_contract(code=initcode)
461466

462-
# Calculate the number of contracts that can be deployed with the available gas.
463-
gas_costs = fork.gas_costs()
464-
intrinsic_gas_cost_calc = fork.transaction_intrinsic_cost_calculator()
465-
loop_cost = (
466-
gas_costs.G_KECCAK_256 # KECCAK static cost
467-
+ math.ceil(85 / 32) * gas_costs.G_KECCAK_256_WORD # KECCAK dynamic cost for CREATE2
468-
+ gas_costs.G_VERY_LOW * 3 # ~MSTOREs+ADDs
469-
+ gas_costs.G_COLD_ACCOUNT_ACCESS # CALL to self-destructing contract
470-
+ gas_costs.G_SELF_DESTRUCT
471-
+ 63 # ~Gluing opcodes
472-
)
473-
final_storage_gas = (
474-
gas_costs.G_STORAGE_RESET + gas_costs.G_COLD_SLOAD + (gas_costs.G_VERY_LOW * 2)
475-
)
476-
memory_expansion_cost = fork().memory_expansion_gas_calculator()(new_bytes=96)
477-
base_costs = (
478-
intrinsic_gas_cost_calc()
479-
+ (gas_costs.G_VERY_LOW * 12) # 8 PUSHs + 4 MSTOREs
480-
+ final_storage_gas
481-
+ memory_expansion_cost
482-
)
483-
num_contracts = num_xen # TODO: edit this to construct as much contracts as possible to
484467
# `claimMintReward()` as the performance test.
485-
expected_benchmark_gas_used = num_contracts * loop_cost + base_costs
486468

487-
# Create a factory that deployes a new SELFDESTRUCT contract instance pre-funded depending on
488-
# the value_bearing parameter. We use CREATE2 so the caller contract can easily reproduce
489-
# the addresses in a loop for CALLs.
490469
factory_code = (
491470
Op.EXTCODECOPY(
492471
address=initcode_address,
@@ -508,18 +487,17 @@ def test_xen_claimrank_and_mint(
508487

509488
factory_address = pre.deploy_contract(code=factory_code)
510489

511-
factory_caller_code = Op.CALLDATALOAD(0) + While(
490+
factory_gas_threshold = 400_000
491+
492+
factory_caller_code = While(
512493
body=Op.POP(Op.CALL(address=factory_address)),
513-
condition=Op.PUSH1(1) + Op.SWAP1 + Op.SUB + Op.DUP1 + Op.ISZERO + Op.ISZERO,
494+
condition=Op.GT(Op.GAS, factory_gas_threshold),
514495
)
515496
factory_caller_address = pre.deploy_contract(code=factory_caller_code)
516497

517-
contracts_deployment_tx = Transaction(
518-
to=factory_caller_address,
519-
gas_limit=attack_gas_limit,
520-
data=Hash(num_contracts),
521-
sender=pre.fund_eoa(),
522-
)
498+
sender = pre.fund_eoa()
499+
500+
gas_threshold = 400_000
523501

524502
code = (
525503
# Setup memory for later CREATE2 address generation loop.
@@ -528,55 +506,52 @@ def test_xen_claimrank_and_mint(
528506
+ Op.MSTORE8(32 - 20 - 1, 0xFF)
529507
+ Op.MSTORE(32, 0) # NOTE: this memory location is used as start index of the contracts.
530508
+ Op.MSTORE(64, initcode.keccak256())
531-
+ Op.CALLDATALOAD(0)
532509
# Main loop
533510
+ While(
534511
body=Op.POP(Op.CALL(address=Op.SHA3(32 - 20 - 1, 85)))
535512
+ Op.MSTORE(32, Op.ADD(Op.MLOAD(32), 1)),
536513
# Loop over `CALLDATALOAD` contracts
537-
condition=Op.PUSH1(1) + Op.SWAP1 + Op.SUB + Op.DUP1 + Op.ISZERO + Op.ISZERO,
514+
condition=Op.GT(Op.GAS, gas_threshold),
538515
)
539516
+ Op.SSTORE(0, 42) # Done for successful tx execution assertion below.
540517
)
541518
assert len(code) <= fork.max_code_size()
542519

543520
# The 0 storage slot is initialize to avoid creation costs in SSTORE above.
544521
code_addr = pre.deploy_contract(code=code, storage={0: 1})
545-
sender = pre.fund_eoa()
546-
sender_nonce = 0
547522

548523
post = {
549-
factory_address: Account(storage={0: num_contracts}),
550524
code_addr: Account(storage={0: 42}), # Check for successful execution.
551525
}
552-
deployed_contract_addresses = []
553-
for i in range(num_contracts):
554-
deployed_contract_address = compute_create2_address(
555-
address=factory_address,
556-
salt=i,
557-
initcode=initcode,
558-
)
559-
post[deployed_contract_address] = Account(nonce=1)
560-
deployed_contract_addresses.append(deployed_contract_address)
526+
# deployed_contract_addresses = []
527+
# for i in range(num_contracts):
528+
# deployed_contract_address = compute_create2_address(
529+
# address=factory_address,
530+
# salt=i,
531+
## initcode=initcode,
532+
# )
533+
# post[deployed_contract_address] = Account(nonce=1)
534+
# deployed_contract_addresses.append(deployed_contract_address)
561535

562-
setup_block = Block(txs=[contracts_deployment_tx])
563-
564-
blocks = [setup_block]
536+
blocks = []
537+
for _ in range(20):
538+
contracts_deployment_tx = Transaction(
539+
to=factory_caller_address,
540+
gas_limit=attack_gas_limit,
541+
sender=sender,
542+
)
543+
blocks.append(Block(txs=[contracts_deployment_tx]))
565544

566545
# for _ in range(24*60*60):
567546
# blocks.append(Block(txs=[Transaction(sender=sender,to=sender, nonce=sender_nonce)]))
568547
# sender_nonce = sender_nonce + 1
569548

570549
opcode_tx = Transaction(
571550
to=code_addr,
572-
data=Hash(num_contracts),
573551
gas_limit=attack_gas_limit,
574552
sender=sender,
575-
nonce=sender_nonce,
576553
)
577554

578-
sender_nonce = sender_nonce + 1
579-
580555
attack_block = Block(
581556
txs=[opcode_tx],
582557
# NOTE: timestamp has no effect in `uv execute remote`. Forcing test to produce 24*60*60 blocks.
@@ -593,5 +568,5 @@ def test_xen_claimrank_and_mint(
593568
post=post,
594569
blocks=blocks,
595570
exclude_full_post_state_in_output=True,
596-
expected_benchmark_gas_used=expected_benchmark_gas_used,
571+
skip_gas_used_validation=True,
597572
)

0 commit comments

Comments
 (0)