Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add create2 #269

Merged
merged 15 commits into from
Jan 18, 2018
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions evm/computation.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
)
from evm.vm.message import (
Message,
ShardingMessage,
)
from evm.vm.stack import (
Stack,
Expand Down Expand Up @@ -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
#
Expand Down
1 change: 1 addition & 0 deletions evm/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
8 changes: 8 additions & 0 deletions evm/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
71 changes: 71 additions & 0 deletions evm/logic/system.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -190,3 +191,73 @@ 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):
Copy link
Contributor

@hwwhww hwwhww Jan 14, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason for inheriting CreateEIP150 instead of CreateByzantium?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CreateByzantium and Create2 both override __call__ function so I make Create2 inherit from CreateEIP150 and copy the is_static check from CreateByzantium.

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,
)
_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)

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)
1 change: 1 addition & 0 deletions evm/mnemonics.py
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,7 @@
# System
#
CREATE = 'CREATE'
CREATE2 = 'CREATE2'
CALL = 'CALL'
CALLCODE = 'CALLCODE'
STATICCALL = 'STATICCALL'
Expand Down
1 change: 1 addition & 0 deletions evm/opcode_values.py
Original file line number Diff line number Diff line change
Expand Up @@ -186,4 +186,5 @@
DELEGATECALL = 0xf4
STATICCALL = 0xfa
REVERT = 0xfd
CREATE2 = 0xfe
SELFDESTRUCT = 0xff
2 changes: 1 addition & 1 deletion evm/utils/address.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
21 changes: 18 additions & 3 deletions evm/vm/forks/sharding/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

from evm.exceptions import (
ContractCreationCollision,
IncorrectContractCreationAddress,
)

from evm.vm.message import (
Expand All @@ -14,7 +15,7 @@
)

from evm.utils.address import (
generate_create2_contract_address,
generate_CREATE2_contract_address,
)
from evm.utils.hexadecimal import (
encode_hex,
Expand Down Expand Up @@ -52,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,
)
Expand Down Expand Up @@ -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)
Expand Down
12 changes: 11 additions & 1 deletion evm/vm/forks/sharding/opcodes.py
Original file line number Diff line number Diff line change
@@ -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 = {
Expand All @@ -18,10 +20,18 @@
mnemonic=mnemonics.SIGHASH,
gas_cost=0,
),
opcode_values.CREATE2: system.Create2.configure(
name='opcode:CREATE2',
mnemonic=mnemonics.CREATE2,
gas_cost=constants.GAS_CREATE2,
)(),
}


SHARDING_OPCODES = merge(
copy.deepcopy(BYZANTIUM_OPCODES),
NEW_OPCODES
)


SHARDING_OPCODES.pop(opcode_values.CREATE)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of popping this value out, can we do the following a few lines up.

from cytoolz import dissoc

SHARDING_OPCODES = merge(
     dissoc(copy.deepcopy(BYZANTIUM_OPCODES), opcode_values.CREATE),
     NEW_OPCODES
 )

8 changes: 7 additions & 1 deletion evm/vm/forks/sharding/transactions.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from evm.constants import (
GAS_TX,
GAS_TXCREATE,
GAS_TXDATAZERO,
GAS_TXDATANONZERO,
)
Expand Down Expand Up @@ -38,8 +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
num_non_zero_bytes * GAS_TXDATANONZERO +
create_cost
)
33 changes: 24 additions & 9 deletions tests/core/fixtures.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
)


Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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': {},
Expand Down
2 changes: 1 addition & 1 deletion tests/core/transaction-utils/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@
"gas_price": 1000000000000,
"access_list": "",
"code": "0x6060604052341561000f57600080fd5b5b60ca8061001e6000396000f30060606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063500d6847146047578063cd580ff314606d575b600080fd5b3415605157600080fd5b6057608d565b6040518082815260200191505060405180910390f35b3415607757600080fd5b608b60048080359060200190919050506093565b005b60005481565b806000819055505b505600a165627a7a7230582080a774dd085c3fe22daee6e793ecd8ed1d4edebd37b2a82fbab37f5f1aacf2a80029", # noqa: E501
"intrinsic_gas": 37304,
"intrinsic_gas": 69304,
},
]

Expand Down
Loading