From 78da04858170ced4c2045287129ab1d2645edf02 Mon Sep 17 00:00:00 2001 From: NIC619 Date: Thu, 11 Jan 2018 23:24:38 +0800 Subject: [PATCH 01/15] update sharding transaction intrinsic gas calculation --- evm/vm/forks/sharding/transactions.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/evm/vm/forks/sharding/transactions.py b/evm/vm/forks/sharding/transactions.py index 09bfb8c72f..03b59ba40d 100644 --- a/evm/vm/forks/sharding/transactions.py +++ b/evm/vm/forks/sharding/transactions.py @@ -1,5 +1,6 @@ from evm.constants import ( GAS_TX, + GAS_TXCREATE, GAS_TXDATAZERO, GAS_TXDATANONZERO, ) @@ -41,5 +42,6 @@ def _get_sharding_intrinsic_gas(transaction_data, transaction_code): return ( GAS_TX + num_zero_bytes * GAS_TXDATAZERO + - num_non_zero_bytes * GAS_TXDATANONZERO + num_non_zero_bytes * GAS_TXDATANONZERO + + GAS_TXCREATE ) From 1a192325978a88b528ab25e47f11c4755f144e4b Mon Sep 17 00:00:00 2001 From: NIC619 Date: Sun, 14 Jan 2018 21:49:53 +0800 Subject: [PATCH 02/15] add prepare_child_shard_message in BaseComputation --- evm/computation.py | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/evm/computation.py b/evm/computation.py index 2bc0adb7b3..3ba5dc6adb 100644 --- a/evm/computation.py +++ b/evm/computation.py @@ -35,6 +35,7 @@ ) from evm.vm.message import ( Message, + ShardingMessage, ) from evm.vm.stack import ( Stack, @@ -146,6 +147,31 @@ def prepare_child_message(self, ) return child_message + def prepare_child_sharding_message(self, + gas, + to, + value, + data, + code, + is_create, + **kwargs): + kwargs.setdefault('sender', self.msg.storage_address) + + child_sharding_message = ShardingMessage( + gas=gas, + gas_price=self.msg.gas_price, + origin=self.msg.origin, + sig_hash=self.msg.sig_hash, + to=to, + value=value, + data=data, + code=code, + depth=self.msg.depth + 1, + is_create=is_create, + **kwargs + ) + return child_sharding_message + # # Memory Management # From 18fc399ee91301607f75e8a04e6a0aaf9f19819f Mon Sep 17 00:00:00 2001 From: NIC619 Date: Thu, 11 Jan 2018 23:26:09 +0800 Subject: [PATCH 03/15] add CREATE2 address check in execute_transaction --- evm/exceptions.py | 8 ++++++++ evm/vm/forks/sharding/__init__.py | 17 ++++++++++++++++- 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/evm/exceptions.py b/evm/exceptions.py index f7b99a6da8..156cb2693c 100644 --- a/evm/exceptions.py +++ b/evm/exceptions.py @@ -112,6 +112,14 @@ class ContractCreationCollision(VMError): pass +class IncorrectContractCreationAddress(VMError): + """ + Error signaling that the address provided by transaction does not + match thecalculated contract creation address. + """ + pass + + class Revert(VMError): """ Error used by the REVERT opcode diff --git a/evm/vm/forks/sharding/__init__.py b/evm/vm/forks/sharding/__init__.py index 440f85bfe4..e35c4e80c7 100644 --- a/evm/vm/forks/sharding/__init__.py +++ b/evm/vm/forks/sharding/__init__.py @@ -4,6 +4,7 @@ from evm.exceptions import ( ContractCreationCollision, + IncorrectContractCreationAddress, ) from evm.vm.message import ( @@ -96,7 +97,21 @@ def _execute_sharding_transaction(vm, transaction): with vm.state.state_db(read_only=True) as state_db: is_collision = state_db.account_has_code_or_nonce(contract_address) - if is_collision: + # Check if contract address provided by transaction is correct + if contract_address != transaction.to: + computation = vm.get_computation(message) + computation._error = IncorrectContractCreationAddress( + "Contract address calculated: {0} but {1} is provided".format( + encode_hex(contract_address), + encode_hex(transaction.to), + ) + ) + vm.logger.debug( + "Contract address calculated: %s but %s is provided", + encode_hex(contract_address), + encode_hex(transaction.to), + ) + elif is_collision: # The address of the newly created contract has collided # with an existing contract address. computation = vm.get_computation(message) From 9e9ed3930423b9fdda1016e63e93bf246e13a5bb Mon Sep 17 00:00:00 2001 From: NIC619 Date: Thu, 11 Jan 2018 23:26:27 +0800 Subject: [PATCH 04/15] add CREATE2 opcode --- evm/constants.py | 1 + evm/logic/system.py | 67 ++++++++++++++++++++++++++++++++ evm/mnemonics.py | 1 + evm/opcode_values.py | 1 + evm/vm/forks/sharding/opcodes.py | 9 ++++- 5 files changed, 78 insertions(+), 1 deletion(-) diff --git a/evm/constants.py b/evm/constants.py index caf560977a..91f9b937d6 100644 --- a/evm/constants.py +++ b/evm/constants.py @@ -47,6 +47,7 @@ GAS_SELFDESTRUCT = 0 GAS_SELFDESTRUCT_NEWACCOUNT = 25000 GAS_CREATE = 32000 +GAS_CREATE2 = 32000 GAS_CALL = 40 GAS_CALLVALUE = 9000 GAS_CALLSTIPEND = 2300 diff --git a/evm/logic/system.py b/evm/logic/system.py index f4b00c987b..8863292cd8 100644 --- a/evm/logic/system.py +++ b/evm/logic/system.py @@ -12,6 +12,7 @@ from evm.utils.address import ( force_bytes_to_address, generate_contract_address, + generate_create2_contract_address, ) from evm.utils.hexadecimal import ( encode_hex, @@ -190,3 +191,69 @@ def __call__(self, computation): if computation.msg.is_static: raise WriteProtection("Cannot modify state while inside of a STATICCALL context") return super(CreateEIP150, self).__call__(computation) + + +class Create2(CreateEIP150): + def __call__(self, computation): + if computation.msg.is_static: + raise WriteProtection("Cannot modify state while inside of a STATICCALL context") + + value = computation.stack.pop(type_hint=constants.UINT256,) + salt = computation.stack.pop(type_hint=constants.BYTES,) + start_position, size = computation.stack.pop( + num_items=2, + type_hint=constants.UINT256, + ) + + computation.extend_memory(start_position, size) + + _state_db = computation.vm_state.state_db( + read_only=True, + access_list=computation.msg.access_list, + ) + with _state_db as state_db: + insufficient_funds = state_db.get_balance(computation.msg.storage_address) < value + stack_too_deep = computation.msg.depth + 1 > constants.STACK_DEPTH_LIMIT + + if insufficient_funds or stack_too_deep: + computation.stack.push(0) + return + + call_data = computation.memory.read(start_position, size) + + create_msg_gas = self.max_child_gas_modifier( + computation.gas_meter.gas_remaining + ) + computation.gas_meter.consume_gas(create_msg_gas, reason="CREATE") + + contract_address = generate_create2_contract_address( + salt, + call_data, + ) + with _state_db as state_db: + is_collision = state_db.account_has_code_or_nonce(contract_address) + + if is_collision: + computation.vm.logger.debug( + "Address collision while creating contract: %s", + encode_hex(contract_address), + ) + computation.stack.push(0) + return + + child_msg = computation.prepare_child_sharding_message( + gas=create_msg_gas, + to=contract_address, + value=value, + data=b'', + code=call_data, + is_create=True, + ) + + child_computation = computation.apply_child_computation(child_msg) + + if child_computation.is_error: + computation.stack.push(0) + else: + computation.stack.push(contract_address) + computation.gas_meter.return_gas(child_computation.gas_meter.gas_remaining) diff --git a/evm/mnemonics.py b/evm/mnemonics.py index 37b885e9a3..3c4358556e 100644 --- a/evm/mnemonics.py +++ b/evm/mnemonics.py @@ -160,6 +160,7 @@ # System # CREATE = 'CREATE' +CREATE2 = 'CREATE2' CALL = 'CALL' CALLCODE = 'CALLCODE' STATICCALL = 'STATICCALL' diff --git a/evm/opcode_values.py b/evm/opcode_values.py index 0b1df665f9..796f0718bb 100644 --- a/evm/opcode_values.py +++ b/evm/opcode_values.py @@ -186,4 +186,5 @@ DELEGATECALL = 0xf4 STATICCALL = 0xfa REVERT = 0xfd +CREATE2 = 0xfe SELFDESTRUCT = 0xff diff --git a/evm/vm/forks/sharding/opcodes.py b/evm/vm/forks/sharding/opcodes.py index 62f4e624ab..69db2f594c 100644 --- a/evm/vm/forks/sharding/opcodes.py +++ b/evm/vm/forks/sharding/opcodes.py @@ -1,15 +1,17 @@ import copy from cytoolz import merge +from evm import constants from evm import opcode_values from evm import mnemonics from evm.opcode import as_opcode from evm.logic import ( context, + system, ) -from evm.vm.forks.byzantium import BYZANTIUM_OPCODES +from evm.vm.forks.byzantium.opcodes import BYZANTIUM_OPCODES NEW_OPCODES = { @@ -18,6 +20,11 @@ mnemonic=mnemonics.SIGHASH, gas_cost=0, ), + opcode_values.CREATE2: system.Create2.configure( + name='opcode:CREATE2', + mnemonic=mnemonics.CREATE2, + gas_cost=constants.GAS_CREATE2, + )(), } From f77eb52c73c0adce4421307bd5c04102ea6b7ecc Mon Sep 17 00:00:00 2001 From: NIC619 Date: Sun, 14 Jan 2018 22:05:37 +0800 Subject: [PATCH 05/15] rename generate_create2_contract_address to generate_CREATE2_contract_address --- evm/logic/system.py | 4 ++-- evm/utils/address.py | 2 +- evm/vm/forks/sharding/__init__.py | 4 ++-- tests/core/vm/contract_fixture.py | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/evm/logic/system.py b/evm/logic/system.py index 8863292cd8..73f1d3cef8 100644 --- a/evm/logic/system.py +++ b/evm/logic/system.py @@ -12,7 +12,7 @@ from evm.utils.address import ( force_bytes_to_address, generate_contract_address, - generate_create2_contract_address, + generate_CREATE2_contract_address, ) from evm.utils.hexadecimal import ( encode_hex, @@ -226,7 +226,7 @@ def __call__(self, computation): ) computation.gas_meter.consume_gas(create_msg_gas, reason="CREATE") - contract_address = generate_create2_contract_address( + contract_address = generate_CREATE2_contract_address( salt, call_data, ) diff --git a/evm/utils/address.py b/evm/utils/address.py index 20dd8e0651..b29383bdb2 100644 --- a/evm/utils/address.py +++ b/evm/utils/address.py @@ -22,7 +22,7 @@ def generate_contract_address(address, nonce): return keccak(rlp.encode([address, nonce]))[-20:] -def generate_create2_contract_address(salt, code): +def generate_CREATE2_contract_address(salt, code): """ If contract is created by transaction, `salt` should be empty. If contract is created by contract, `salt` is set by the creator contract. diff --git a/evm/vm/forks/sharding/__init__.py b/evm/vm/forks/sharding/__init__.py index e35c4e80c7..d28a7c7afc 100644 --- a/evm/vm/forks/sharding/__init__.py +++ b/evm/vm/forks/sharding/__init__.py @@ -15,7 +15,7 @@ ) from evm.utils.address import ( - generate_create2_contract_address, + generate_CREATE2_contract_address, ) from evm.utils.hexadecimal import ( encode_hex, @@ -53,7 +53,7 @@ def _execute_sharding_transaction(vm, transaction): message_gas = transaction.gas - transaction.intrinsic_gas if transaction.code: - contract_address = generate_create2_contract_address( + contract_address = generate_CREATE2_contract_address( b'', transaction.code, ) diff --git a/tests/core/vm/contract_fixture.py b/tests/core/vm/contract_fixture.py index e7d7504b21..c3165e5d5a 100644 --- a/tests/core/vm/contract_fixture.py +++ b/tests/core/vm/contract_fixture.py @@ -1,4 +1,4 @@ -from evm.utils.address import generate_create2_contract_address +from evm.utils.address import generate_CREATE2_contract_address # contract code to be deployed contract_lll_code = ['seq', @@ -17,4 +17,4 @@ contract_bytecode = b'0x61003e567401000000000000000000000000000000000000000060205260003560205181101558575060006000600060006020356000356000f1155857005b61000461003e0361000460003961000461003e036000f3' # noqa: E501 # address where this contract will be deployed -contract_address = generate_create2_contract_address(b'', contract_bytecode) +contract_address = generate_CREATE2_contract_address(b'', contract_bytecode) From e61783746c724cc38019cd0999186730b4fa9c79 Mon Sep 17 00:00:00 2001 From: NIC619 Date: Mon, 15 Jan 2018 13:08:59 +0800 Subject: [PATCH 06/15] remove CREATE from sharding opcodes --- evm/vm/forks/sharding/opcodes.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/evm/vm/forks/sharding/opcodes.py b/evm/vm/forks/sharding/opcodes.py index 69db2f594c..04ad8689f0 100644 --- a/evm/vm/forks/sharding/opcodes.py +++ b/evm/vm/forks/sharding/opcodes.py @@ -32,3 +32,6 @@ copy.deepcopy(BYZANTIUM_OPCODES), NEW_OPCODES ) + + +SHARDING_OPCODES.pop(opcode_values.CREATE) From 9e8e9fe563930be6bb9adc92f3e145574da2364d Mon Sep 17 00:00:00 2001 From: NIC619 Date: Mon, 15 Jan 2018 13:09:15 +0800 Subject: [PATCH 07/15] fix sharding transaction intrinsic gas calculation --- evm/vm/forks/sharding/transactions.py | 6 +++++- tests/core/transaction-utils/conftest.py | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/evm/vm/forks/sharding/transactions.py b/evm/vm/forks/sharding/transactions.py index 03b59ba40d..ce81c059a6 100644 --- a/evm/vm/forks/sharding/transactions.py +++ b/evm/vm/forks/sharding/transactions.py @@ -39,9 +39,13 @@ def get_intrinsic_gas(self): def _get_sharding_intrinsic_gas(transaction_data, transaction_code): num_zero_bytes = transaction_data.count(b'\x00') + transaction_code.count(b'\x00') num_non_zero_bytes = len(transaction_data) + len(transaction_code) - num_zero_bytes + if transaction_code: + create_cost = GAS_TXCREATE + else: + create_cost = 0 return ( GAS_TX + num_zero_bytes * GAS_TXDATAZERO + num_non_zero_bytes * GAS_TXDATANONZERO + - GAS_TXCREATE + create_cost ) diff --git a/tests/core/transaction-utils/conftest.py b/tests/core/transaction-utils/conftest.py index 8e525798e1..133967ce8a 100644 --- a/tests/core/transaction-utils/conftest.py +++ b/tests/core/transaction-utils/conftest.py @@ -69,7 +69,7 @@ "gas_price": 1000000000000, "access_list": "", "code": "0x6060604052341561000f57600080fd5b5b60ca8061001e6000396000f30060606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063500d6847146047578063cd580ff314606d575b600080fd5b3415605157600080fd5b6057608d565b6040518082815260200191505060405180910390f35b3415607757600080fd5b608b60048080359060200190919050506093565b005b60005481565b806000819055505b505600a165627a7a7230582080a774dd085c3fe22daee6e793ecd8ed1d4edebd37b2a82fbab37f5f1aacf2a80029", # noqa: E501 - "intrinsic_gas": 37304, + "intrinsic_gas": 69304, }, ] From 849d9f89ec512e500ff3587fef9b5ca6a432c38a Mon Sep 17 00:00:00 2001 From: NIC619 Date: Mon, 15 Jan 2018 13:57:32 +0800 Subject: [PATCH 08/15] fix on cleaned up state_db context --- evm/logic/system.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/evm/logic/system.py b/evm/logic/system.py index 73f1d3cef8..6f41d38019 100644 --- a/evm/logic/system.py +++ b/evm/logic/system.py @@ -230,6 +230,10 @@ def __call__(self, computation): salt, call_data, ) + _state_db = computation.vm_state.state_db( + read_only=True, + access_list=computation.msg.access_list, + ) with _state_db as state_db: is_collision = state_db.account_has_code_or_nonce(contract_address) From ad81d761bfefb7ec710e1a2f67fa758c15829a1a Mon Sep 17 00:00:00 2001 From: NIC619 Date: Mon, 15 Jan 2018 13:57:55 +0800 Subject: [PATCH 09/15] add CREATE2 contract add test --- tests/core/fixtures.py | 33 ++++++++++++----- tests/core/vm/contract_fixture.py | 61 +++++++++++++++++++++++++------ tests/core/vm/test_sharding_vm.py | 57 +++++++++++++++++++++++++---- 3 files changed, 123 insertions(+), 28 deletions(-) diff --git a/tests/core/fixtures.py b/tests/core/fixtures.py index af4fcc5519..ac88196d2f 100644 --- a/tests/core/fixtures.py +++ b/tests/core/fixtures.py @@ -16,8 +16,10 @@ from evm.vm.forks.sharding import ShardingVM from tests.core.vm.contract_fixture import ( - contract_bytecode, - contract_address, + simple_transfer_contract_bytecode, + simple_transfer_contract_address, + CREATE2_contract_bytecode, + CREATE2_contract_address, ) @@ -83,11 +85,18 @@ def chain(chaindb): return chain -SHARD_CHAIN_CONTRACTS_FIXTURE = { - "contract_code": contract_bytecode, - "deployed_address": contract_address, - "initial_balance": 100000000, -} +SHARD_CHAIN_CONTRACTS_FIXTURES = [ + { + "contract_code": simple_transfer_contract_bytecode, + "deployed_address": simple_transfer_contract_address, + "initial_balance": 100000000, + }, + { + "contract_code": CREATE2_contract_bytecode, + "deployed_address": CREATE2_contract_address, + "initial_balance": 100000000, + }, +] @pytest.fixture @@ -133,8 +142,14 @@ def shard_chain_without_block_validation(): 'timestamp': 1501851927, } genesis_state = { - SHARD_CHAIN_CONTRACTS_FIXTURE["deployed_address"]: { - 'balance': SHARD_CHAIN_CONTRACTS_FIXTURE["initial_balance"], + SHARD_CHAIN_CONTRACTS_FIXTURES[0]["deployed_address"]: { + 'balance': SHARD_CHAIN_CONTRACTS_FIXTURES[0]["initial_balance"], + 'nonce': 0, + 'code': b'', + 'storage': {}, + }, + SHARD_CHAIN_CONTRACTS_FIXTURES[1]["deployed_address"]: { + 'balance': SHARD_CHAIN_CONTRACTS_FIXTURES[1]["initial_balance"], 'nonce': 0, 'code': b'', 'storage': {}, diff --git a/tests/core/vm/contract_fixture.py b/tests/core/vm/contract_fixture.py index c3165e5d5a..08ad6a3376 100644 --- a/tests/core/vm/contract_fixture.py +++ b/tests/core/vm/contract_fixture.py @@ -1,20 +1,57 @@ +from eth_utils import decode_hex + from evm.utils.address import generate_CREATE2_contract_address # contract code to be deployed -contract_lll_code = ['seq', - ['return', # noqa: E127 - 0, - ['lll', - ['seq', - ['mstore', 32, 1461501637330902918203684832716283019655932542976], # noqa: E501 - ['uclamplt', ['calldataload', 0], ['mload', 32]], - ['assert', ['call', 0, ['calldataload', 0], ['calldataload', 32], 0, 0, 0, 0]], # noqa: E501 - 'stop'], - 0]]] # noqa: E128 +simple_transfer_contract_lll_code = ['seq', + ['return', # noqa: E127 + 0, + ['lll', + ['seq', + ['mstore', 32, 1461501637330902918203684832716283019655932542976], # noqa: E501 + ['uclamplt', ['calldataload', 0], ['mload', 32]], # noqa: E501 + ['assert', ['call', 0, ['calldataload', 0], ['calldataload', 32], 0, 0, 0, 0]], # noqa: E501 + 'stop'], + 0]]] # noqa: E128 + + +# compiled byte code +simple_transfer_contract_bytecode = b'0x61003e567401000000000000000000000000000000000000000060205260003560205181101558575060006000600060006020356000356000f1155857005b61000461003e0361000460003961000461003e036000f3' # noqa: E501 + + +# address where this contract will be deployed +simple_transfer_contract_address = generate_CREATE2_contract_address( + b'', + decode_hex(simple_transfer_contract_bytecode) +) + + +simple_contract_factory_bytecode = b'\x61\xbe\xef\x60\x20\x52\x60\x02\x60\x3e\xf3' + + +# This contract will deploy a new contract and increment it's only storage variable 'nonce' +# every time it's invoked. +# Contract address: generate_CREATE2_contract_address(nonce, simple_contract_factory_bytecode) +# The bytecode of every new contract deployed is b'\xbe\xef' +CREATE2_contract_lll_code = ['seq', + ['return', # noqa: E127 + 0, + ['lll', + ['seq', + ['mstore', 32, ['sload', 0]], + ['mstore', 64, 118167469832824678325960435], + ['clamp_nonzero', ['create2', ['mload', 32], ['mload', 32], 85, 11]], # noqa: E501 + ['sstore', 0, ['add', ['mload', 32], 1]], + 'stop'], + 0]]] # noqa: E128 # compiled byte code -contract_bytecode = b'0x61003e567401000000000000000000000000000000000000000060205260003560205181101558575060006000600060006020356000356000f1155857005b61000461003e0361000460003961000461003e036000f3' # noqa: E501 +CREATE2_contract_bytecode = '0x610039566000546020526a61beef6020526002603ef3604052600b6055602051602051fe8061002957600080fd5b50600160205101600055005b61000461003903610004600039610004610039036000f3' # noqa: E501 + # address where this contract will be deployed -contract_address = generate_CREATE2_contract_address(b'', contract_bytecode) +CREATE2_contract_address = generate_CREATE2_contract_address( + b'', + decode_hex(CREATE2_contract_bytecode) +) diff --git a/tests/core/vm/test_sharding_vm.py b/tests/core/vm/test_sharding_vm.py index 28b423042a..0b447f607e 100644 --- a/tests/core/vm/test_sharding_vm.py +++ b/tests/core/vm/test_sharding_vm.py @@ -1,4 +1,9 @@ -from eth_utils import decode_hex +from eth_utils import ( + int_to_big_endian, + decode_hex, +) + +from evm.utils.address import generate_CREATE2_contract_address from tests.core.fixtures import ( # noqa: F401 shard_chain_without_block_validation, @@ -7,26 +12,64 @@ new_sharding_transaction, ) from tests.core.vm.contract_fixture import ( - contract_bytecode, - contract_address, + simple_transfer_contract_bytecode, + simple_transfer_contract_address, + simple_contract_factory_bytecode, + CREATE2_contract_bytecode, + CREATE2_contract_address, ) def test_sharding_transaction(shard_chain_without_block_validation): # noqa: F811 chain = shard_chain_without_block_validation - deploy_tx = new_sharding_transaction(contract_address, b'', 0, b'', b'', contract_bytecode) + # First test: simple ether transfer contract + first_deploy_tx = new_sharding_transaction( + simple_transfer_contract_address, + b'', + 0, + b'', + b'', + simple_transfer_contract_bytecode + ) vm = chain.get_vm() - computation = vm.apply_transaction(deploy_tx) + computation = vm.apply_transaction(first_deploy_tx) assert not computation.is_error # Transfer ether to recipient recipient = decode_hex('0xa94f5374fce5edbc8e2a8697c15331677e6ebf0c') amount = 100 - tx_initiator = contract_address + tx_initiator = simple_transfer_contract_address transfer_tx = new_sharding_transaction(tx_initiator, recipient, amount, b'', b'', b'') - computation = vm.execute_transaction(transfer_tx) + computation = vm.apply_transaction(transfer_tx) assert not computation.is_error with vm.state.state_db(read_only=True) as state_db: assert state_db.get_balance(recipient) == amount + + # Second test: contract that deploy new contract with CREATE2 + second_deploy_tx = new_sharding_transaction( + CREATE2_contract_address, + b'', + 0, + b'', + b'', + CREATE2_contract_bytecode + ) + + computation = vm.apply_transaction(second_deploy_tx) + assert not computation.is_error + + # Invoke the contract to deploy new contract + tx_initiator = CREATE2_contract_address + invoke_tx = new_sharding_transaction(tx_initiator, b'', 0, b'', b'', b'') + + computation = vm.apply_transaction(invoke_tx) + assert not computation.is_error + with vm.state.state_db(read_only=True) as state_db: + newly_deployed_contract_address = generate_CREATE2_contract_address( + int_to_big_endian(0), + simple_contract_factory_bytecode + ) + assert state_db.get_code(newly_deployed_contract_address) == b'\xbe\xef' + assert state_db.get_storage(CREATE2_contract_address, 0) == 1 From b8436453085d829213ed3399721825f297917e8f Mon Sep 17 00:00:00 2001 From: NIC619 Date: Tue, 16 Jan 2018 11:58:10 +0800 Subject: [PATCH 10/15] add edge case test cases for CREATE2 contract deployment --- tests/core/vm/test_sharding_vm.py | 42 ++++++++++++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/tests/core/vm/test_sharding_vm.py b/tests/core/vm/test_sharding_vm.py index 0b447f607e..9d7c6a8dad 100644 --- a/tests/core/vm/test_sharding_vm.py +++ b/tests/core/vm/test_sharding_vm.py @@ -20,7 +20,7 @@ ) -def test_sharding_transaction(shard_chain_without_block_validation): # noqa: F811 +def test_sharding_apply_transaction(shard_chain_without_block_validation): # noqa: F811 chain = shard_chain_without_block_validation # First test: simple ether transfer contract first_deploy_tx = new_sharding_transaction( @@ -44,6 +44,7 @@ def test_sharding_transaction(shard_chain_without_block_validation): # noqa: F8 computation = vm.apply_transaction(transfer_tx) assert not computation.is_error + assert vm.block.header.gas_used with vm.state.state_db(read_only=True) as state_db: assert state_db.get_balance(recipient) == amount @@ -59,6 +60,7 @@ def test_sharding_transaction(shard_chain_without_block_validation): # noqa: F8 computation = vm.apply_transaction(second_deploy_tx) assert not computation.is_error + assert vm.block.header.gas_used # Invoke the contract to deploy new contract tx_initiator = CREATE2_contract_address @@ -66,6 +68,7 @@ def test_sharding_transaction(shard_chain_without_block_validation): # noqa: F8 computation = vm.apply_transaction(invoke_tx) assert not computation.is_error + assert vm.block.header.gas_used with vm.state.state_db(read_only=True) as state_db: newly_deployed_contract_address = generate_CREATE2_contract_address( int_to_big_endian(0), @@ -73,3 +76,40 @@ def test_sharding_transaction(shard_chain_without_block_validation): # noqa: F8 ) assert state_db.get_code(newly_deployed_contract_address) == b'\xbe\xef' assert state_db.get_storage(CREATE2_contract_address, 0) == 1 + + +def test_CREATE2_deploy_contract_edge_cases(shard_chain_without_block_validation): # noqa: F811 + # First case: computed contract address not the same as provided in `transaction.to` + chain = shard_chain_without_block_validation + first_failed_deploy_tx = new_sharding_transaction( + simple_transfer_contract_address, + b'', + 0, + b'', + b'', + '0xf3', + ) + + vm = chain.get_vm() + computation = vm.apply_transaction(first_failed_deploy_tx) + assert computation.is_error + assert vm.block.header.gas_used + + # Next, complete deploying the contract + successful_deploy_tx = new_sharding_transaction( + simple_transfer_contract_address, + b'', + 0, + b'', + b'', + simple_transfer_contract_bytecode + ) + computation = vm.apply_transaction(successful_deploy_tx) + assert not computation.is_error + assert vm.block.header.gas_used + + # Second case: deploy to existing account + second_failed_deploy_tx = successful_deploy_tx + computation = vm.apply_transaction(second_failed_deploy_tx) + assert computation.is_error + assert vm.block.header.gas_used From e1998b20a9d712146862d861ebcfc1bf0aa9401a Mon Sep 17 00:00:00 2001 From: NIC619 Date: Tue, 16 Jan 2018 13:19:05 +0800 Subject: [PATCH 11/15] fix missing CREATE2 gas cost and logger naming --- evm/logic/system.py | 4 +++- evm/vm/forks/sharding/computation.py | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/evm/logic/system.py b/evm/logic/system.py index 6f41d38019..855757c442 100644 --- a/evm/logic/system.py +++ b/evm/logic/system.py @@ -198,6 +198,8 @@ def __call__(self, computation): if computation.msg.is_static: raise WriteProtection("Cannot modify state while inside of a STATICCALL context") + computation.gas_meter.consume_gas(self.gas_cost, reason=self.mnemonic) + value = computation.stack.pop(type_hint=constants.UINT256,) salt = computation.stack.pop(type_hint=constants.BYTES,) start_position, size = computation.stack.pop( @@ -224,7 +226,7 @@ def __call__(self, computation): create_msg_gas = self.max_child_gas_modifier( computation.gas_meter.gas_remaining ) - computation.gas_meter.consume_gas(create_msg_gas, reason="CREATE") + computation.gas_meter.consume_gas(create_msg_gas, reason="CREATE2") contract_address = generate_CREATE2_contract_address( salt, diff --git a/evm/vm/forks/sharding/computation.py b/evm/vm/forks/sharding/computation.py index a070b39570..432ca041f3 100644 --- a/evm/vm/forks/sharding/computation.py +++ b/evm/vm/forks/sharding/computation.py @@ -91,7 +91,7 @@ def apply_create_message(self): try: computation.gas_meter.consume_gas( contract_code_gas_cost, - reason="Write contract code for CREATE", + reason="Write contract code for CREATE2", ) except OutOfGas as err: # Different from Frontier: reverts state on gas failure while From 0268f652d3da3f5fa47566b27c2982275dbbbc72 Mon Sep 17 00:00:00 2001 From: NIC619 Date: Tue, 16 Jan 2018 13:24:47 +0800 Subject: [PATCH 12/15] update consumed gas check in test_sharding_vm.py --- tests/core/vm/test_sharding_vm.py | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/tests/core/vm/test_sharding_vm.py b/tests/core/vm/test_sharding_vm.py index 9d7c6a8dad..a3ac96115b 100644 --- a/tests/core/vm/test_sharding_vm.py +++ b/tests/core/vm/test_sharding_vm.py @@ -35,6 +35,9 @@ def test_sharding_apply_transaction(shard_chain_without_block_validation): # no vm = chain.get_vm() computation = vm.apply_transaction(first_deploy_tx) assert not computation.is_error + gas_used = vm.block.header.gas_used + assert gas_used > first_deploy_tx.intrinsic_gas + last_gas_used = gas_used # Transfer ether to recipient recipient = decode_hex('0xa94f5374fce5edbc8e2a8697c15331677e6ebf0c') @@ -44,7 +47,9 @@ def test_sharding_apply_transaction(shard_chain_without_block_validation): # no computation = vm.apply_transaction(transfer_tx) assert not computation.is_error - assert vm.block.header.gas_used + gas_used = vm.block.header.gas_used - last_gas_used + assert gas_used > transfer_tx.intrinsic_gas + last_gas_used = vm.block.header.gas_used with vm.state.state_db(read_only=True) as state_db: assert state_db.get_balance(recipient) == amount @@ -60,7 +65,9 @@ def test_sharding_apply_transaction(shard_chain_without_block_validation): # no computation = vm.apply_transaction(second_deploy_tx) assert not computation.is_error - assert vm.block.header.gas_used + gas_used = vm.block.header.gas_used - last_gas_used + assert gas_used > second_deploy_tx.intrinsic_gas + last_gas_used = vm.block.header.gas_used # Invoke the contract to deploy new contract tx_initiator = CREATE2_contract_address @@ -68,7 +75,8 @@ def test_sharding_apply_transaction(shard_chain_without_block_validation): # no computation = vm.apply_transaction(invoke_tx) assert not computation.is_error - assert vm.block.header.gas_used + gas_used = vm.block.header.gas_used - last_gas_used + assert gas_used > invoke_tx.intrinsic_gas with vm.state.state_db(read_only=True) as state_db: newly_deployed_contract_address = generate_CREATE2_contract_address( int_to_big_endian(0), @@ -93,7 +101,9 @@ def test_CREATE2_deploy_contract_edge_cases(shard_chain_without_block_validation vm = chain.get_vm() computation = vm.apply_transaction(first_failed_deploy_tx) assert computation.is_error - assert vm.block.header.gas_used + gas_used = vm.block.header.gas_used + assert gas_used > first_failed_deploy_tx.intrinsic_gas + last_gas_used = gas_used # Next, complete deploying the contract successful_deploy_tx = new_sharding_transaction( @@ -106,10 +116,13 @@ def test_CREATE2_deploy_contract_edge_cases(shard_chain_without_block_validation ) computation = vm.apply_transaction(successful_deploy_tx) assert not computation.is_error - assert vm.block.header.gas_used + gas_used = vm.block.header.gas_used - last_gas_used + assert gas_used > successful_deploy_tx.intrinsic_gas + last_gas_used = gas_used # Second case: deploy to existing account second_failed_deploy_tx = successful_deploy_tx computation = vm.apply_transaction(second_failed_deploy_tx) assert computation.is_error - assert vm.block.header.gas_used + gas_used = vm.block.header.gas_used - last_gas_used + assert gas_used > second_failed_deploy_tx.intrinsic_gas From 9fda6137723bdef35aebe40258c9ffecec92c3ea Mon Sep 17 00:00:00 2001 From: NIC619 Date: Tue, 16 Jan 2018 21:51:39 +0800 Subject: [PATCH 13/15] flake8'ed --- evm/logic/system.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/evm/logic/system.py b/evm/logic/system.py index 855757c442..53ec1fd0f4 100644 --- a/evm/logic/system.py +++ b/evm/logic/system.py @@ -199,7 +199,7 @@ def __call__(self, computation): raise WriteProtection("Cannot modify state while inside of a STATICCALL context") computation.gas_meter.consume_gas(self.gas_cost, reason=self.mnemonic) - + value = computation.stack.pop(type_hint=constants.UINT256,) salt = computation.stack.pop(type_hint=constants.BYTES,) start_position, size = computation.stack.pop( From fb421a9afa35b6a2e5eed85dcfaf5b5fc1ecc1df Mon Sep 17 00:00:00 2001 From: NIC619 Date: Wed, 17 Jan 2018 13:23:13 +0800 Subject: [PATCH 14/15] remove opcode by cytoolz.dissoc and improve function argument passing readability --- evm/vm/forks/sharding/opcodes.py | 10 +++--- tests/core/vm/test_sharding_vm.py | 56 +++++++++++++++++-------------- 2 files changed, 35 insertions(+), 31 deletions(-) diff --git a/evm/vm/forks/sharding/opcodes.py b/evm/vm/forks/sharding/opcodes.py index 04ad8689f0..b879464e7f 100644 --- a/evm/vm/forks/sharding/opcodes.py +++ b/evm/vm/forks/sharding/opcodes.py @@ -1,5 +1,8 @@ import copy -from cytoolz import merge +from cytoolz import ( + merge, + dissoc, +) from evm import constants from evm import opcode_values @@ -29,9 +32,6 @@ SHARDING_OPCODES = merge( - copy.deepcopy(BYZANTIUM_OPCODES), + dissoc(copy.deepcopy(BYZANTIUM_OPCODES), opcode_values.CREATE), NEW_OPCODES ) - - -SHARDING_OPCODES.pop(opcode_values.CREATE) diff --git a/tests/core/vm/test_sharding_vm.py b/tests/core/vm/test_sharding_vm.py index a3ac96115b..1bb53abd2f 100644 --- a/tests/core/vm/test_sharding_vm.py +++ b/tests/core/vm/test_sharding_vm.py @@ -3,6 +3,10 @@ decode_hex, ) +from evm.exceptions import ( + IncorrectContractCreationAddress, + ContractCreationCollision, +) from evm.utils.address import generate_CREATE2_contract_address from tests.core.fixtures import ( # noqa: F401 @@ -24,12 +28,12 @@ def test_sharding_apply_transaction(shard_chain_without_block_validation): # no chain = shard_chain_without_block_validation # First test: simple ether transfer contract first_deploy_tx = new_sharding_transaction( - simple_transfer_contract_address, - b'', - 0, - b'', - b'', - simple_transfer_contract_bytecode + tx_initiator=simple_transfer_contract_address, + data_destination=b'', + data_value=0, + data_msgdata=b'', + data_vrs=b'', + code=simple_transfer_contract_bytecode, ) vm = chain.get_vm() @@ -55,12 +59,12 @@ def test_sharding_apply_transaction(shard_chain_without_block_validation): # no # Second test: contract that deploy new contract with CREATE2 second_deploy_tx = new_sharding_transaction( - CREATE2_contract_address, - b'', - 0, - b'', - b'', - CREATE2_contract_bytecode + tx_initiator=CREATE2_contract_address, + data_destination=b'', + data_value=0, + data_msgdata=b'', + data_vrs=b'', + code=CREATE2_contract_bytecode, ) computation = vm.apply_transaction(second_deploy_tx) @@ -90,29 +94,29 @@ def test_CREATE2_deploy_contract_edge_cases(shard_chain_without_block_validation # First case: computed contract address not the same as provided in `transaction.to` chain = shard_chain_without_block_validation first_failed_deploy_tx = new_sharding_transaction( - simple_transfer_contract_address, - b'', - 0, - b'', - b'', - '0xf3', + tx_initiator=simple_transfer_contract_address, + data_destination=b'', + data_value=0, + data_msgdata=b'', + data_vrs=b'', + code='0xf3', ) vm = chain.get_vm() computation = vm.apply_transaction(first_failed_deploy_tx) - assert computation.is_error + assert isinstance(computation._error, IncorrectContractCreationAddress) gas_used = vm.block.header.gas_used assert gas_used > first_failed_deploy_tx.intrinsic_gas last_gas_used = gas_used # Next, complete deploying the contract successful_deploy_tx = new_sharding_transaction( - simple_transfer_contract_address, - b'', - 0, - b'', - b'', - simple_transfer_contract_bytecode + tx_initiator=simple_transfer_contract_address, + data_destination=b'', + data_value=0, + data_msgdata=b'', + data_vrs=b'', + code=simple_transfer_contract_bytecode, ) computation = vm.apply_transaction(successful_deploy_tx) assert not computation.is_error @@ -123,6 +127,6 @@ def test_CREATE2_deploy_contract_edge_cases(shard_chain_without_block_validation # Second case: deploy to existing account second_failed_deploy_tx = successful_deploy_tx computation = vm.apply_transaction(second_failed_deploy_tx) - assert computation.is_error + assert isinstance(computation._error, ContractCreationCollision) gas_used = vm.block.header.gas_used - last_gas_used assert gas_used > second_failed_deploy_tx.intrinsic_gas From 362d82ec5ed17402e91a76d9e3210c7001f79ca8 Mon Sep 17 00:00:00 2001 From: NIC619 Date: Wed, 17 Jan 2018 17:33:55 +0800 Subject: [PATCH 15/15] update state_db usage in CREATE2 based on #276 --- evm/computation.py | 2 +- evm/logic/system.py | 13 +++---------- 2 files changed, 4 insertions(+), 11 deletions(-) diff --git a/evm/computation.py b/evm/computation.py index 3ba5dc6adb..25f5d87fef 100644 --- a/evm/computation.py +++ b/evm/computation.py @@ -330,7 +330,7 @@ def get_gas_remaining(self): @contextmanager def state_db(self, read_only=False): - with self.vm.state_db(read_only, self.msg.access_list) as state_db: + with self.vm_state.state_db(read_only, self.msg.access_list) as state_db: yield state_db # diff --git a/evm/logic/system.py b/evm/logic/system.py index 53ec1fd0f4..64e7f93a3b 100644 --- a/evm/logic/system.py +++ b/evm/logic/system.py @@ -209,11 +209,7 @@ def __call__(self, computation): computation.extend_memory(start_position, size) - _state_db = computation.vm_state.state_db( - read_only=True, - access_list=computation.msg.access_list, - ) - with _state_db as state_db: + with computation.state_db(read_only=True) as state_db: insufficient_funds = state_db.get_balance(computation.msg.storage_address) < value stack_too_deep = computation.msg.depth + 1 > constants.STACK_DEPTH_LIMIT @@ -232,11 +228,8 @@ def __call__(self, computation): salt, call_data, ) - _state_db = computation.vm_state.state_db( - read_only=True, - access_list=computation.msg.access_list, - ) - with _state_db as state_db: + + with computation.state_db(read_only=True) as state_db: is_collision = state_db.account_has_code_or_nonce(contract_address) if is_collision: