-
Notifications
You must be signed in to change notification settings - Fork 92
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
test opcode programs in different scenarios
- Loading branch information
Showing
10 changed files
with
564 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
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,3 @@ | ||
""" | ||
Scenarios common import | ||
""" |
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,37 @@ | ||
""" | ||
Define Scenario class for test_scenarios test | ||
""" | ||
from typing import Optional | ||
|
||
from ethereum_test_forks import Byzantium, Constantinople, Fork, Frontier, Homestead | ||
from ethereum_test_tools import Address | ||
from ethereum_test_tools.vm.opcode import Opcode | ||
from ethereum_test_tools.vm.opcode import Opcodes as Op | ||
|
||
|
||
class Scenario: | ||
""" | ||
Describe test scenario that will be run in test and it's conditions | ||
""" | ||
|
||
name: str | ||
code: Address | ||
fork: Fork | ||
not_reverting: bool | ||
|
||
def __init__(self, name: str, code: Address, fork: Fork, reverts: bool = False): | ||
self.name = name | ||
self.code = code | ||
self.fork = fork | ||
self.not_reverting = not reverts | ||
|
||
|
||
def get_valid_fork_for_call(call: Opcode, second_call: Optional[Opcode] = Op.CALL) -> Fork: | ||
"""Return fork for which this call will be valid""" | ||
if call == Op.CREATE2 or second_call == Op.CREATE2: | ||
return Constantinople | ||
if call == Op.STATICCALL or second_call == Op.STATICCALL: | ||
return Byzantium | ||
if call == Op.DELEGATECALL or second_call == Op.DELEGATECALL: | ||
return Homestead | ||
return Frontier |
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,3 @@ | ||
""" | ||
Scenarios common import | ||
""" |
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,81 @@ | ||
""" | ||
Define a program for scenario test that executes all frontier opcodes and entangles it's result | ||
""" | ||
import pytest | ||
|
||
from ethereum_test_tools import Bytecode | ||
from ethereum_test_tools.vm.opcode import Opcodes as Op | ||
|
||
|
||
def make_all_opcode_program() -> Bytecode: | ||
"""Make a program that call each Frontier opcode and tangles it's results""" | ||
code: Bytecode = ( | ||
Op.MSTORE(0, Op.ADD(1, 1)) # 2 | ||
+ Op.MSTORE(0, Op.MUL(3, Op.MLOAD(0))) # 6 | ||
+ Op.MSTORE(0, Op.SUB(Op.MLOAD(0), 1)) # 5 | ||
+ Op.MSTORE(0, Op.DIV(Op.MLOAD(0), 2)) # 2 | ||
+ Op.MSTORE(0, Op.SDIV(Op.MLOAD(0), 2)) # 1 | ||
+ Op.MSTORE(0, Op.ADD(Op.MLOAD(0), 19)) # 20 | ||
+ Op.MSTORE(0, Op.SMOD(Op.MLOAD(0), 3)) # 2 | ||
+ Op.MSTORE(0, Op.ADDMOD(Op.MLOAD(0), 3, 2)) # 1 | ||
+ Op.MSTORE(0, Op.MULMOD(Op.MLOAD(0), 10, 2)) # 0 | ||
+ Op.MSTORE(0, Op.ADD(Op.MLOAD(0), 3)) # 3 | ||
+ Op.MSTORE(0, Op.EXP(Op.MLOAD(0), 2)) # 9 | ||
+ Op.MSTORE(0, Op.SUB(Op.MLOAD(0), 9)) # 0 | ||
+ Op.MSTORE(0, Op.SIGNEXTEND(Op.MLOAD(0), 0xFF)) # MAX | ||
+ Op.MSTORE(0, Op.LT(11, Op.MLOAD(0))) # 1 | ||
+ Op.MSTORE(0, Op.GT(0, Op.MLOAD(0))) # 0 | ||
+ Op.MSTORE(0, Op.SLT(Op.SUB(0, 1), Op.MLOAD(0))) # 1 | ||
+ Op.MSTORE(0, Op.SGT(Op.MLOAD(0), Op.SUB(0, 1))) # 1 | ||
+ Op.MSTORE(0, Op.EQ(Op.MLOAD(0), 0)) # 0 | ||
+ Op.MSTORE(0, Op.ISZERO(Op.MLOAD(0))) # 1 | ||
+ Op.MSTORE(0, Op.AND(Op.MLOAD(0), 1)) # 1 | ||
+ Op.MSTORE(0, Op.OR(Op.MLOAD(0), 0)) # 1 | ||
+ Op.MSTORE(0, Op.XOR(Op.MLOAD(0), 2)) # 3 | ||
+ Op.MSTORE(0, Op.NOT(Op.MLOAD(0))) # 0xFFF....FFC | ||
+ Op.MSTORE(0, Op.BYTE(31, Op.MLOAD(0))) # 0xFC | ||
+ Op.MSTORE(0, Op.SHA3(0, 32)) # 0x371f36870d18f32a11fea0f144b021c8b407bb50f8e... | ||
+ Op.MSTORE(0, Op.DIV(Op.MLOAD(0), Op.ADD(Op.CALLVALUE, 1))) # same | ||
+ Op.MSTORE(0, Op.DIV(Op.MLOAD(0), Op.GASPRICE)) # 0x5831f0d814f4b8434ffdce4ed44... | ||
+ Op.MSTORE(0, Op.SUB(Op.MLOAD(0), Op.PUSH1(1))) | ||
+ Op.MSTORE(0, Op.SUB(Op.MLOAD(0), Op.PUSH2(0x0FFF))) | ||
+ Op.MSTORE(0, Op.SUB(Op.MLOAD(0), Op.PUSH3(0x0FFFFF))) | ||
+ Op.MSTORE(0, Op.SUB(Op.MLOAD(0), Op.PUSH4(0x0FFFFFFF))) | ||
+ Op.MSTORE(0, Op.SUB(Op.MLOAD(0), Op.PUSH5(0x0FFFFFFFFF))) | ||
+ Op.MSTORE(0, Op.SUB(Op.MLOAD(0), Op.PUSH6(0x0FFFFFFFFFFF))) | ||
+ Op.MSTORE(0, Op.SUB(Op.MLOAD(0), Op.PUSH7(0x0FFFFFFFFFFFFF))) | ||
+ Op.MSTORE(0, Op.SUB(Op.MLOAD(0), Op.PUSH8(0x0FFFFFFFFFFFFFFF))) | ||
+ Op.MSTORE(0, Op.SUB(Op.MLOAD(0), Op.PUSH9(0x0FFFFFFFFFFFFFFFFF))) | ||
+ Op.MSTORE(0, Op.SUB(Op.MLOAD(0), Op.PUSH10(0x0FFFFFFFFFFFFFFFFFFF))) | ||
+ Op.MSTORE(0, Op.SUB(Op.MLOAD(0), Op.PUSH11(0x0FFFFFFFFFFFFFFFFFFFFF))) | ||
+ Op.MSTORE(0, Op.SUB(Op.MLOAD(0), Op.PUSH12(0x0FFFFFFFFFFFFFFFFFFFFFFF))) | ||
+ Op.MSTORE(0, Op.SUB(Op.MLOAD(0), Op.PUSH13(0x0FFFFFFFFFFFFFFFFFFFFFFFFF))) | ||
+ Op.MSTORE(0, Op.SUB(Op.MLOAD(0), Op.PUSH14(0x0FFFFFFFFFFFFFFFFFFFFFFFFFFF))) | ||
+ Op.MSTORE(0, Op.SUB(Op.MLOAD(0), Op.PUSH15(0x0FFFFFFFFFFFFFFFFFFFFFFFFFFFFF))) | ||
+ Op.MSTORE(0, Op.SUB(Op.MLOAD(0), Op.PUSH16(0x0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF))) | ||
+ Op.MSTORE(0, Op.SUB(Op.MLOAD(0), Op.PUSH17(0x0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF))) | ||
+ Op.MSTORE(0, Op.SUB(Op.MLOAD(0), Op.PUSH18(0x0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF))) | ||
+ Op.MSTORE(0, Op.SUB(Op.MLOAD(0), Op.PUSH19(0x0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF))) | ||
+ Op.MSTORE(0, Op.SUB(Op.MLOAD(0), Op.PUSH20(0x0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF))) | ||
+ Op.MSTORE(0, Op.SUB(Op.MLOAD(0), Op.PUSH21(0x0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF))) | ||
+ Op.MSTORE(0, Op.SUB(Op.MLOAD(0), Op.PUSH22(0x0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF))) | ||
+ Op.MSTORE(0, Op.SUB(Op.MLOAD(0), Op.PUSH23(0x0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF))) | ||
+ Op.MSTORE(0, Op.SUB(Op.MLOAD(0), Op.PUSH24(0x0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF))) | ||
+ Op.MSTORE(0, Op.SUB(Op.MLOAD(0), Op.PUSH25(0x0FFFFFFFFFFFFFFFFFFFFFFFFFFFFF))) | ||
+ Op.MSTORE(0, Op.SUB(Op.MLOAD(0), Op.PUSH26(0x0FFFFFFFFFFFFFFFFFFFFFFFFFFF))) | ||
+ Op.MSTORE(0, Op.SUB(Op.MLOAD(0), Op.PUSH27(0x0FFFFFFFFFFFFFFFFFFFFFFFFF))) | ||
+ Op.MSTORE(0, Op.SUB(Op.MLOAD(0), Op.PUSH28(0x0FFFFFFFFFFFFFFFFFFFFFFF))) | ||
+ Op.MSTORE(0, Op.SUB(Op.MLOAD(0), Op.PUSH29(0x0FFFFFFFFFFFFFFFFFFFFF))) | ||
+ Op.MSTORE(0, Op.SUB(Op.MLOAD(0), Op.PUSH30(0x0FFFFFFFFFFFFFFFFFFF))) | ||
+ Op.MSTORE(0, Op.SUB(Op.MLOAD(0), Op.PUSH31(0x0FFFFFFFFFFFFFFFFF))) | ||
+ Op.MSTORE(0, Op.SUB(Op.MLOAD(0), Op.PUSH32(0x0FFFFFFFFFFFFFFF))) | ||
+ Op.RETURN(0, 32) | ||
) | ||
return code | ||
|
||
|
||
program_all_frontier_opcodes = pytest.param( | ||
make_all_opcode_program(), | ||
0x5831F0D814F4B8434FFDCE4DD24B00D8B7A3F67F8C316EC51A4D9EEC535AD4A, | ||
id="program_ALL_FRONTIER_OPCODES", | ||
) |
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,3 @@ | ||
""" | ||
Scenarios common import | ||
""" |
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,63 @@ | ||
""" | ||
Define Scenario that will put a given program in all call contexts | ||
""" | ||
from typing import List | ||
|
||
from ethereum_test_tools import Address, Alloc | ||
from ethereum_test_tools.vm.opcode import Opcode | ||
from ethereum_test_tools.vm.opcode import Opcodes as Op | ||
|
||
from ..common import Scenario, get_valid_fork_for_call | ||
|
||
|
||
def scenarios_call_combinations(pre: Alloc, operation_contract: Address) -> List[Scenario]: | ||
""" | ||
Generate Scenarios for call combinations | ||
This is the actual test case. We take code that we want to test at operation_contract | ||
and put it in the context of call combinations. | ||
Example: scenario_contract -> [callcode -> delegatecall]* -> code | ||
We assume that code always returns it's result | ||
That we pass as return value in scenario_contract for the post state verification | ||
""" | ||
list: List[Scenario] = [] | ||
max_scenario_gas = 500000 | ||
first_calls: List[Opcode] = [Op.CALLCODE, Op.DELEGATECALL, Op.STATICCALL] | ||
second_calls: List[Opcode] = [Op.CALL, Op.CALLCODE, Op.DELEGATECALL, Op.STATICCALL, Op.NOOP] | ||
|
||
for first_call in first_calls: | ||
for second_call in second_calls: | ||
if second_call == Op.NOOP: | ||
"""Only one call""" | ||
scenario_contract = pre.deploy_contract( | ||
code=first_call(gas=max_scenario_gas, address=operation_contract, ret_size=32) | ||
+ Op.RETURN(0, 32) | ||
) | ||
list.append( | ||
Scenario( | ||
name=f"scenario_{first_call}", | ||
code=scenario_contract, | ||
fork=get_valid_fork_for_call(first_call), | ||
) | ||
) | ||
else: | ||
"""Call combination""" | ||
sub_contract = pre.deploy_contract( | ||
code=second_call( | ||
gas=Op.SUB(Op.GAS, 100000), address=operation_contract, ret_size=32 | ||
) | ||
+ Op.RETURN(0, 32) | ||
) | ||
scenario_contract = pre.deploy_contract( | ||
code=first_call(gas=Op.SUB(Op.GAS, 100000), address=sub_contract, ret_size=32) | ||
+ Op.RETURN(0, 32) | ||
) | ||
list.append( | ||
Scenario( | ||
name=f"scenario_{first_call}_{second_call}", | ||
code=scenario_contract, | ||
fork=get_valid_fork_for_call(first_call, second_call), | ||
) | ||
) | ||
|
||
return list |
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,65 @@ | ||
""" | ||
Define Scenario that will put a given program in create contexts | ||
""" | ||
from typing import List | ||
|
||
from ethereum_test_forks import Constantinople, Frontier | ||
from ethereum_test_tools import Address, Alloc, Bytecode | ||
from ethereum_test_tools.vm.opcode import Macros as Om | ||
from ethereum_test_tools.vm.opcode import Opcode | ||
from ethereum_test_tools.vm.opcode import Opcodes as Op | ||
|
||
from ..common import Scenario, get_valid_fork_for_call | ||
|
||
|
||
def scenarios_create_combinations(pre: Alloc, operation_contract: Address) -> List[Scenario]: | ||
"""Generate Scenarios for create combinations""" | ||
list: List[Scenario] = [] | ||
max_scenario_gas = 100000 | ||
create_types: List[Opcode] = [Op.CREATE, Op.CREATE2] | ||
|
||
# run code in create constructor | ||
for create in create_types: | ||
salt = [0] if create == Op.CREATE2 else [] | ||
|
||
# the code result in init code will be actually code of a deployed contract | ||
scenario_contract = pre.deploy_contract( | ||
code=Op.EXTCODECOPY(operation_contract, 0, 0, Op.EXTCODESIZE(operation_contract)) | ||
+ Op.MSTORE(0, create(0, 0, Op.EXTCODESIZE(operation_contract), *salt)) | ||
+ Op.EXTCODECOPY(Op.MLOAD(0), 0, 0, 32) | ||
+ Op.RETURN(0, 32) | ||
) | ||
list.append( | ||
Scenario( | ||
name=f"scenario_{create}_constructor", | ||
code=scenario_contract, | ||
fork=Frontier if create == Op.CREATE else Constantinople, | ||
) | ||
) | ||
|
||
# create a contract with test code and call it | ||
deploy_code = Bytecode( | ||
Op.EXTCODECOPY(operation_contract, 0, 0, Op.EXTCODESIZE(operation_contract)) | ||
+ Op.RETURN(0, Op.EXTCODESIZE(operation_contract)) | ||
) | ||
deploy_code_size: int = int(len(deploy_code.hex()) / 2) | ||
call_types: List[Opcode] = [Op.CALL, Op.CALLCODE, Op.DELEGATECALL, Op.STATICCALL] | ||
|
||
for create in create_types: | ||
for call in call_types: | ||
salt = [0] if create == Op.CREATE2 else [] | ||
scenario_contract = pre.deploy_contract( | ||
code=Om.MSTORE(deploy_code, 0) | ||
+ Op.MSTORE(0, create(0, 0, deploy_code_size, *salt)) | ||
+ call(gas=max_scenario_gas, address=Op.MLOAD(0), ret_size=32) | ||
+ Op.RETURN(0, 32) | ||
) | ||
list.append( | ||
Scenario( | ||
name=f"scenario_{create}_then_{call}", | ||
code=scenario_contract, | ||
fork=get_valid_fork_for_call(call, create), | ||
) | ||
) | ||
|
||
return list |
Oops, something went wrong.