diff --git a/.circleci/config.yml b/.circleci/config.yml index f304262d43..e48d899e9c 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -108,6 +108,12 @@ jobs: - image: circleci/python:3.5 environment: TOXENV: py35-native-state-byzantium + py35-native-state-constantinople: + <<: *common + docker: + - image: circleci/python:3.5 + environment: + TOXENV: py35-native-state-constantinople py35-native-state-frontier: <<: *common docker: @@ -175,6 +181,12 @@ jobs: - image: circleci/python:3.6 environment: TOXENV: py36-native-state-byzantium + py36-native-state-constantinople: + <<: *common + docker: + - image: circleci/python:3.6 + environment: + TOXENV: py36-native-state-constantinople py36-native-state-frontier: <<: *common docker: @@ -205,6 +217,12 @@ jobs: - image: circleci/python:3.6 environment: TOXENV: py36-rpc-state-byzantium + py36-rpc-state-constantinople: + <<: *common + docker: + - image: circleci/python:3.6 + environment: + TOXENV: py36-rpc-state-constantinople py36-rpc-state-frontier: <<: *common docker: @@ -340,11 +358,13 @@ workflows: - py37-beacon - py36-native-state-byzantium + - py36-native-state-constantinople - py36-native-state-frontier - py36-native-state-homestead - py36-native-state-eip150 - py36-native-state-eip158 - py36-rpc-state-byzantium + - py36-rpc-state-constantinople - py36-rpc-state-frontier - py36-rpc-state-homestead - py36-rpc-state-eip150 @@ -364,6 +384,7 @@ workflows: - py36-beacon - py35-native-state-byzantium + - py35-native-state-constantinople - py35-native-state-frontier - py35-native-state-homestead - py35-native-state-eip150 diff --git a/eth/__init__.py b/eth/__init__.py index 21a35c0c6b..0da2b8e042 100644 --- a/eth/__init__.py +++ b/eth/__init__.py @@ -21,7 +21,7 @@ # # Ensure we can reach 1024 frames of recursion # -EVM_RECURSION_LIMIT = 1024 * 10 +EVM_RECURSION_LIMIT = 1024 * 12 sys.setrecursionlimit(max(EVM_RECURSION_LIMIT, sys.getrecursionlimit())) diff --git a/eth/tools/fixtures/helpers.py b/eth/tools/fixtures/helpers.py index 06c0c15b3d..0340f9e32e 100644 --- a/eth/tools/fixtures/helpers.py +++ b/eth/tools/fixtures/helpers.py @@ -40,6 +40,7 @@ BaseVM, ) from eth.vm.forks import ( + ConstantinopleVM, ByzantiumVM, TangerineWhistleVM, FrontierVM, @@ -123,6 +124,10 @@ def chain_vm_configuration(fixture: Dict[str, Any]) -> Iterable[Tuple[int, Type[ return ( (0, ByzantiumVM), ) + elif network == 'Constantinople': + return ( + (0, ConstantinopleVM), + ) elif network == 'FrontierToHomesteadAt5': HomesteadVM = BaseHomesteadVM.configure(support_dao_fork=False) return ( diff --git a/eth/vm/logic/system.py b/eth/vm/logic/system.py index 7852e046ee..bb75c5f723 100644 --- a/eth/vm/logic/system.py +++ b/eth/vm/logic/system.py @@ -20,12 +20,9 @@ ceil32, ) from eth.vm import mnemonics -from eth.vm.computation import ( - BaseComputation -) -from eth.vm.opcode import ( - Opcode, -) +from eth.vm.computation import BaseComputation +from eth.vm.message import Message +from eth.vm.opcode import Opcode from .call import max_child_gas_eip150 @@ -193,13 +190,16 @@ def __call__(self, computation: BaseComputation) -> None: code=call_data, create_address=contract_address, ) + self.apply_create_message(computation, child_msg) + def apply_create_message(self, computation: BaseComputation, child_msg: Message) -> None: child_computation = computation.apply_child_computation(child_msg) if child_computation.is_error: computation.stack_push(0) else: - computation.stack_push(contract_address) + computation.stack_push(child_msg.storage_address) + computation.return_gas(child_computation.get_gas_remaining()) @@ -240,3 +240,22 @@ def generate_contract_address(self, stack_data.salt, call_data ) + + def apply_create_message(self, computation: BaseComputation, child_msg: Message) -> None: + # We need to ensure that creation operates on empty storage **and** + # that if the initialization code fails that we revert the account back + # to its original state root. + snapshot = computation.state.snapshot() + + computation.state.account_db.delete_storage(child_msg.storage_address) + + child_computation = computation.apply_child_computation(child_msg) + + if child_computation.is_error: + computation.state.revert(snapshot) + computation.stack_push(0) + else: + computation.state.commit(snapshot) + computation.stack_push(child_msg.storage_address) + + computation.return_gas(child_computation.get_gas_remaining()) diff --git a/fixtures b/fixtures index 95a3092038..420f443477 160000 --- a/fixtures +++ b/fixtures @@ -1 +1 @@ -Subproject commit 95a309203890e6244c6d4353ca411671973c13b5 +Subproject commit 420f443477caa8516f1f9ee8122fafc3415c0f34 diff --git a/tests/conftest.py b/tests/conftest.py index e298dc4d39..4c439532e7 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -23,10 +23,13 @@ # Uncomment this to have logs from tests written to a file. This is useful for # debugging when you need to dump the VM output from test runs. """ +import datetime +import logging +import os +from eth.tools.logging import TRACE_LEVEL_NUM + @pytest.yield_fixture(autouse=True) def _file_logging(request): - import datetime - import os logger = logging.getLogger('eth') diff --git a/tests/json-fixtures/test_blockchain.py b/tests/json-fixtures/test_blockchain.py index 1aa902890a..5d03c8a3dc 100644 --- a/tests/json-fixtures/test_blockchain.py +++ b/tests/json-fixtures/test_blockchain.py @@ -45,6 +45,11 @@ # The result is in conflict with the yellow-paper: # * https://github.com/ethereum/py-evm/pull/1224#issuecomment-418800369 ('GeneralStateTests/stRevertTest/RevertInCreateInInit_d0g0v0.json', 'RevertInCreateInInit_d0g0v0_Byzantium'), # noqa: E501 + ('GeneralStateTests/stRevertTest/RevertInCreateInInit_d0g0v0.json', 'RevertInCreateInInit_d0g0v0_Constantinople'), # noqa: E501 + # The CREATE2 variant seems to have been derived from the one above - it, too, + # has a "synthetic" state, on which py-evm flips. + # * https://github.com/ethereum/py-evm/pull/1181#issuecomment-446330609 + ('GeneralStateTests/stCreate2/RevertInCreateInInitCreate2_d0g0v0.json', 'RevertInCreateInInitCreate2_d0g0v0_Constantinople'), # noqa: E501 } @@ -84,8 +89,6 @@ def fixture(fixture_data): fixture_key, normalize_blockchain_fixtures, ) - if fixture['network'] == 'Constantinople': - pytest.skip('Constantinople VM rules not yet supported') return fixture diff --git a/tests/json-fixtures/test_state.py b/tests/json-fixtures/test_state.py index bac660cfca..18b246922b 100644 --- a/tests/json-fixtures/test_state.py +++ b/tests/json-fixtures/test_state.py @@ -28,12 +28,14 @@ HomesteadVM, SpuriousDragonVM, ByzantiumVM, + ConstantinopleVM, ) from eth.vm.forks.tangerine_whistle.state import TangerineWhistleState from eth.vm.forks.frontier.state import FrontierState from eth.vm.forks.homestead.state import HomesteadState from eth.vm.forks.spurious_dragon.state import SpuriousDragonState from eth.vm.forks.byzantium.state import ByzantiumState +from eth.vm.forks.constantinople.state import ConstantinopleState from eth.rlp.headers import ( BlockHeader, @@ -144,6 +146,7 @@ def expand_fixtures_forks(all_fixtures): # The result is in conflict with the yellow-paper: # * https://github.com/ethereum/py-evm/pull/1224#issuecomment-418800369 ('stRevertTest/RevertInCreateInInit.json', 'RevertInCreateInInit', 'Byzantium', 0), + ('stRevertTest/RevertInCreateInInit.json', 'RevertInCreateInInit', 'Constantinople', 0), } @@ -245,6 +248,10 @@ def get_prev_hashes_testing(self, last_block_hash, db): __name__='ByzantiumStateForTesting', get_ancestor_hash=get_block_hash_for_testing, ) +ConstantinopleStateForTesting = ConstantinopleState.configure( + __name__='ConstantinopleStateForTesting', + get_ancestor_hash=get_block_hash_for_testing, +) FrontierVMForTesting = FrontierVM.configure( __name__='FrontierVMForTesting', @@ -271,6 +278,11 @@ def get_prev_hashes_testing(self, last_block_hash, db): _state_class=ByzantiumStateForTesting, get_prev_hashes=get_prev_hashes_testing, ) +ConstantinopleVMForTesting = ConstantinopleVM.configure( + __name__='ConstantinopleVMForTesting', + _state_class=ConstantinopleStateForTesting, + get_prev_hashes=get_prev_hashes_testing, +) @pytest.fixture @@ -287,7 +299,7 @@ def fixture_vm_class(fixture_data): elif fork_name == ForkName.Byzantium: return ByzantiumVMForTesting elif fork_name == ForkName.Constantinople: - pytest.skip("Constantinople VM has not been implemented") + return ConstantinopleVMForTesting elif fork_name == ForkName.Metropolis: pytest.skip("Metropolis VM has not been implemented") else: diff --git a/tests/json-fixtures/test_transactions.py b/tests/json-fixtures/test_transactions.py index 26d7a18395..3051bb2cdc 100644 --- a/tests/json-fixtures/test_transactions.py +++ b/tests/json-fixtures/test_transactions.py @@ -28,6 +28,9 @@ from eth.vm.forks.byzantium.transactions import ( ByzantiumTransaction ) +from eth.vm.forks.constantinople.transactions import ( + ConstantinopleTransaction +) from eth_typing.enums import ( ForkName @@ -92,7 +95,7 @@ def fixture_transaction_class(fixture_data): elif fork_name == ForkName.Byzantium: return ByzantiumTransaction elif fork_name == ForkName.Constantinople: - pytest.skip("Constantinople Transaction class has not been implemented") + return ConstantinopleTransaction elif fork_name == ForkName.Metropolis: pytest.skip("Metropolis Transaction class has not been implemented") else: diff --git a/tox.ini b/tox.ini index 86e48878c8..e84b3d86c4 100644 --- a/tox.ini +++ b/tox.ini @@ -3,7 +3,7 @@ envlist= py{35,36}-{core,database,transactions,vm,native-blockchain} py{36}-{benchmark,p2p,trinity,lightchain_integration,beacon} py{36}-rpc-blockchain - py{36}-rpc-state-{frontier,homestead,eip150,eip158,byzantium,quadratic} + py{36}-rpc-state-{frontier,homestead,eip150,eip158,byzantium,constantinople,quadratic} py{35,36}-native-state-{frontier,homestead,eip150,eip158,byzantium,constantinople,metropolis} py37-{core,trinity,trinity-integration,beacon} py{35,36}-lint @@ -32,6 +32,8 @@ commands= beacon: pytest {posargs:tests/beacon/} # The following test seems to consume a lot of memory. Restricting to 3 processes reduces crashes rpc-state-byzantium: pytest -n3 {posargs:tests/trinity/json-fixtures-over-rpc/test_rpc_fixtures.py -k 'GeneralStateTests and not stQuadraticComplexityTest and Byzantium'} + # Uncomment the next line + modify test_rpc_fixtures.py when Constantinople is included in the mainnet config + # rpc-state-constantinople: pytest -n3 {posargs:tests/trinity/json-fixtures-over-rpc/test_rpc_fixtures.py -k 'GeneralStateTests and not stQuadraticComplexityTest and Constantinople'} rpc-state-quadratic: pytest {posargs:tests/trinity/json-fixtures-over-rpc/test_rpc_fixtures.py -k 'GeneralStateTests and stQuadraticComplexityTest'} transactions: pytest {posargs:tests/json-fixtures/test_transactions.py} vm: pytest {posargs:tests/json-fixtures/test_virtual_machine.py}