-
Notifications
You must be signed in to change notification settings - Fork 72
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
1 changed file
with
216 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,216 @@ | ||
""" | ||
Call every possible opcode and test that the subcall is successful | ||
if the opcode is supported by the fork supports and fails otherwise. | ||
""" | ||
|
||
from typing import Dict, List, Tuple | ||
|
||
import pytest | ||
|
||
from ethereum_test_forks import Byzantium, Fork, Frontier, Homestead | ||
from ethereum_test_tools import ( | ||
Account, | ||
Address, | ||
Alloc, | ||
Bytecode, | ||
Environment, | ||
StateTestFiller, | ||
Transaction, | ||
) | ||
from ethereum_test_tools.vm.opcode import Opcode | ||
from ethereum_test_tools.vm.opcode import Opcodes as Op | ||
from ethereum_test_tools.vm.opcode import UndefinedOpcodes | ||
|
||
REFERENCE_SPEC_GIT_PATH = "N/A" | ||
REFERENCE_SPEC_VERSION = "N/A" | ||
|
||
|
||
class Scenario: | ||
""" | ||
Describe test scenario that will be run in test and it's conditions | ||
""" | ||
|
||
name: str | ||
code: Address | ||
fork: Fork | ||
|
||
def __init__(self, name: str, code: Address, fork: Fork): | ||
self.name = name | ||
self.code = code | ||
self.fork = fork | ||
|
||
|
||
@pytest.fixture | ||
def scenarios(pre: Alloc, operation: Bytecode) -> List[Scenario]: | ||
"""Define list of contracts that execute scenarios for a given operation""" | ||
list: List[Scenario] = [] | ||
operation_contract = pre.deploy_contract(code=operation) | ||
max_scenario_gas = 100000 | ||
|
||
"""Scenario: Call""" | ||
scenario_contract = pre.deploy_contract( | ||
code=Op.CALL(max_scenario_gas, operation_contract, 0, 0, 0, 0, 32) + Op.RETURN(0, 32) | ||
) | ||
list.append(Scenario(name="scenario_call", code=scenario_contract, fork=Frontier)) | ||
|
||
"""Scenario: Callcode""" | ||
scenario_contract = pre.deploy_contract( | ||
code=Op.CALLCODE(max_scenario_gas, operation_contract, 0, 0, 0, 0, 32) + Op.RETURN(0, 32) | ||
) | ||
list.append(Scenario(name="scenario_callcode", code=scenario_contract, fork=Frontier)) | ||
|
||
"""Scenario: DelegateCall""" | ||
scenario_contract = pre.deploy_contract( | ||
code=Op.DELEGATECALL(max_scenario_gas, operation_contract, 0, 0, 0, 32) + Op.RETURN(0, 32) | ||
) | ||
list.append(Scenario(name="scenario_delegatecall", code=scenario_contract, fork=Homestead)) | ||
|
||
"""Scenario: StaticCall""" | ||
scenario_contract = pre.deploy_contract( | ||
code=Op.STATICCALL(max_scenario_gas, operation_contract, 0, 0, 0, 32) + Op.RETURN(0, 32) | ||
) | ||
list.append(Scenario(name="scenario_staticcall", code=scenario_contract, fork=Byzantium)) | ||
|
||
"""Scenario: Callcode a contract that CALLs""" | ||
sub_contract = pre.deploy_contract( | ||
code=Op.CALL(max_scenario_gas, operation_contract, 0, 0, 0, 0, 32) + Op.RETURN(0, 32) | ||
) | ||
scenario_contract = pre.deploy_contract( | ||
code=Op.CALLCODE(max_scenario_gas, sub_contract, 0, 0, 0, 0, 32) + Op.RETURN(0, 32) | ||
) | ||
list.append(Scenario(name="scenario_callcode_call", code=scenario_contract, fork=Frontier)) | ||
|
||
"""Scenario: Delegatecall a contract that CALLs""" | ||
sub_contract = pre.deploy_contract( | ||
code=Op.CALL(max_scenario_gas, operation_contract, 0, 0, 0, 0, 32) + Op.RETURN(0, 32) | ||
) | ||
scenario_contract = pre.deploy_contract( | ||
code=Op.DELEGATECALL(max_scenario_gas, sub_contract, 0, 0, 0, 32) + Op.RETURN(0, 32) | ||
) | ||
list.append( | ||
Scenario(name="scenario_delegatecall_call", code=scenario_contract, fork=Homestead) | ||
) | ||
|
||
"""Scenario: Staticcall a contract that CALLs""" | ||
sub_contract = pre.deploy_contract( | ||
code=Op.CALL(max_scenario_gas, operation_contract, 0, 0, 0, 0, 32) + Op.RETURN(0, 32) | ||
) | ||
scenario_contract = pre.deploy_contract( | ||
code=Op.STATICCALL(max_scenario_gas, sub_contract, 0, 0, 0, 32) + Op.RETURN(0, 32) | ||
) | ||
list.append(Scenario(name="scenario_staticcall_call", code=scenario_contract, fork=Byzantium)) | ||
|
||
"""Scenario: Callcode a contract that CALLCODEs""" | ||
sub_contract = pre.deploy_contract( | ||
code=Op.CALLCODE(max_scenario_gas, operation_contract, 0, 0, 0, 0, 32) + Op.RETURN(0, 32) | ||
) | ||
scenario_contract = pre.deploy_contract( | ||
code=Op.CALLCODE(max_scenario_gas, sub_contract, 0, 0, 0, 0, 32) + Op.RETURN(0, 32) | ||
) | ||
list.append(Scenario(name="scenario_callcode_callcode", code=scenario_contract, fork=Frontier)) | ||
|
||
"""Scenario: Delegatecall a contract that CALLCODEs""" | ||
sub_contract = pre.deploy_contract( | ||
code=Op.CALLCODE(max_scenario_gas, operation_contract, 0, 0, 0, 0, 32) + Op.RETURN(0, 32) | ||
) | ||
scenario_contract = pre.deploy_contract( | ||
code=Op.DELEGATECALL(max_scenario_gas, sub_contract, 0, 0, 0, 32) + Op.RETURN(0, 32) | ||
) | ||
list.append( | ||
Scenario(name="scenario_delegatecall_callcode", code=scenario_contract, fork=Homestead) | ||
) | ||
|
||
"""Scenario: Staticcall a contract that CALLCODEs""" | ||
sub_contract = pre.deploy_contract( | ||
code=Op.CALLCODE(max_scenario_gas, operation_contract, 0, 0, 0, 0, 32) + Op.RETURN(0, 32) | ||
) | ||
scenario_contract = pre.deploy_contract( | ||
code=Op.STATICCALL(max_scenario_gas, sub_contract, 0, 0, 0, 32) + Op.RETURN(0, 32) | ||
) | ||
list.append( | ||
Scenario(name="scenario_staticcall_callcode", code=scenario_contract, fork=Byzantium) | ||
) | ||
""" | ||
// 13. 0xF1F4 CALL a contract that DELEGATECALLs a contract ... | ||
// 14. 0xF2F4 CALLCODE a contract that DELEGATECALLs ... | ||
// 15. 0xF4F4 DELEGATECALL ... DELEGATECALL | ||
// 16. 0xFAF4 STATICCALL ... DELEGATECALL | ||
// 17. 0xF1FA CALL a contract that STATICCALLs a contract ... | ||
// 18. 0xF2FA CALLCODE a contract that STATICCALLs ... | ||
// 19. 0xF4FA DELEGATECALL ... STATICCALL | ||
// 20. 0xFAFA STATICCALL ... STATICCALL | ||
// 21. 0x00FD Run the code, call a contract that reverts, then run again | ||
// 22. 0x00FE Run the code, call a contract that goes out of gas, then run again | ||
// 23. 0x00FF Run the code, call a contract that self-destructs, then run again | ||
// 24. 0x00F0 CREATE a contract, run the code in the constructor | ||
// 25. 0x00F5 CREATE2 a contract, run the code in the constructor | ||
// 26. 0xF0F1 CREATE a contract with the code and then CALL it | ||
// 27. 0xF5F1 CREATE2 a contract with the code and then CALL it | ||
// 28. 0xF0F2 CREATE a contract with the code and then CALLCODE it | ||
// 29. 0xF5F2 CREATE2 a contract with the code and then CALLCODE it | ||
// 30. 0xF0F4 CREATE a contract with the code and then DELEGATECALL it | ||
// 31. 0xF5F4 CREATE2 a contract with the code and then DELEGATECALL it | ||
// 32. 0xF0FA CREATE a contract with the code and then STATICCALL it | ||
// 33. 0xF5FA CREATE2 a contract with the code and then STATICCALL it | ||
// 34. 0x60BACCFA57 Call recurse to the limit | ||
""" | ||
|
||
return list | ||
|
||
|
||
@pytest.mark.valid_from("Frontier") | ||
@pytest.mark.parametrize( | ||
"operation, result", | ||
[ | ||
pytest.param(Op.MSTORE(0, Op.ADD(1, 1)) + Op.RETURN(0, 32), 2, id="ADD_1_1"), | ||
], | ||
) | ||
def test_diff_places( | ||
state_test: StateTestFiller, | ||
pre: Alloc, | ||
fork: Fork, | ||
result: int, | ||
scenarios, | ||
): | ||
""" | ||
Test given operation in different scenarios | ||
Verify that it's return value equal to expected result on every scenario, | ||
that is valid for the given fork | ||
""" | ||
code_worked = 1000 | ||
|
||
runner_contract = pre.deploy_contract( | ||
code=sum( | ||
Op.MSTORE(0, 0) | ||
+ Op.CALL(200000, scenario.code, 0, 0, 0, 0, 32) | ||
+ Op.SSTORE(index, Op.MLOAD(0)) | ||
for index, (scenario) in enumerate(scenarios) | ||
) | ||
+ Op.SSTORE(code_worked, 1) | ||
) | ||
post = { | ||
runner_contract: Account( | ||
storage={ | ||
**{ | ||
index: result if fork >= scenario.fork else 0 | ||
for index, (scenario) in enumerate(scenarios) | ||
}, | ||
code_worked: 1, | ||
} | ||
), | ||
} | ||
|
||
hint: Dict[int, str] = {} | ||
for index, (scenario) in enumerate(scenarios): | ||
hint[index] = scenario.name | ||
|
||
tx = Transaction( | ||
sender=pre.fund_eoa(), | ||
gas_limit=500_000_000, | ||
to=runner_contract, | ||
data=b"", | ||
value=0, | ||
protected=False, | ||
) | ||
|
||
state_test(env=Environment(), pre=pre, post=post, tx=tx, post_hint=hint) |