Skip to content

Commit

Permalink
Merge pull request #269 from NIC619/add_create2
Browse files Browse the repository at this point in the history
Add create2
  • Loading branch information
NIC619 committed Jan 18, 2018
2 parents 2f5c87e + 362d82e commit 01a07cc
Show file tree
Hide file tree
Showing 15 changed files with 327 additions and 41 deletions.
28 changes: 27 additions & 1 deletion 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 Expand Up @@ -304,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

#
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
66 changes: 66 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,68 @@ 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")

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(
num_items=2,
type_hint=constants.UINT256,
)

computation.extend_memory(start_position, size)

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

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="CREATE2")

contract_address = generate_CREATE2_contract_address(
salt,
call_data,
)

with computation.state_db(read_only=True) 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
2 changes: 1 addition & 1 deletion evm/vm/forks/sharding/computation.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
16 changes: 13 additions & 3 deletions evm/vm/forks/sharding/opcodes.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,20 @@
import copy
from cytoolz import merge
from cytoolz import (
merge,
dissoc,
)

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 +23,15 @@
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),
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

0 comments on commit 01a07cc

Please sign in to comment.