1212However, with enough funds (to bloat a contract state), this is thus not the worst scenario.
1313"""
1414
15- import math
16-
1715import 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
2118from ethereum_test_tools import (
19+ Account ,
2220 Alloc ,
2321 Block ,
2422 BlockchainTestFiller ,
23+ Environment ,
2524 Hash ,
2625 Transaction ,
2726 While ,
2827)
2928from 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
3229from 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
406406def 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