Skip to content

Commit

Permalink
Implement Create2 opcode for Constantinople
Browse files Browse the repository at this point in the history
  • Loading branch information
cburgdorf committed Aug 16, 2018
1 parent 91962c9 commit d206216
Show file tree
Hide file tree
Showing 6 changed files with 91 additions and 16 deletions.
16 changes: 16 additions & 0 deletions eth/utils/address.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,14 @@
from eth_hash.auto import keccak
from eth_typing import Address

from eth.utils.hexadecimal import (
encode_hex,
)
from eth.utils.numeric import (
int_to_big_endian,
)


def force_bytes_to_address(value: bytes) -> Address:
trimmed_value = value[-20:]
padded_value = trimmed_value.rjust(20, b'\x00')
Expand All @@ -11,3 +19,11 @@ def force_bytes_to_address(value: bytes) -> Address:

def generate_contract_address(address: Address, nonce: bytes) -> Address:
return Address(keccak(rlp.encode([address, nonce]))[-20:])


def generate_safe_contract_address(address: Address,
salt: int,
call_data: bytes) -> Address:
return Address(
keccak(encode_hex('0xff') + address + int_to_big_endian(salt) + keccak(call_data))[-20:]
)
1 change: 0 additions & 1 deletion eth/vm/forks/byzantium/opcodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,6 @@ def inner(computation):
mnemonic=mnemonics.CREATE,
gas_cost=constants.GAS_CREATE,
)(),
# TODO: CREATE2
#
# Storage
#
Expand Down
6 changes: 6 additions & 0 deletions eth/vm/forks/constantinople/opcodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
from eth.vm.logic import (
arithmetic,
context,
system,
)
from eth.vm.opcode import (
as_opcode
Expand Down Expand Up @@ -46,6 +47,11 @@
mnemonic=mnemonics.EXTCODEHASH,
gas_cost=GAS_EXTCODEHASH_EIP1052,
),
opcode_values.CREATE2: system.Create2.configure(
__name__='opcode:CREATE2',
mnemonic=mnemonics.CREATE2,
gas_cost=constants.GAS_CREATE,
)(),
}

CONSTANTINOPLE_OPCODES = merge(
Expand Down
82 changes: 67 additions & 15 deletions eth/vm/logic/system.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from eth.utils.address import (
force_bytes_to_address,
generate_contract_address,
generate_safe_contract_address,
)
from eth.utils.hexadecimal import (
encode_hex,
Expand Down Expand Up @@ -100,44 +101,72 @@ def _selfdestruct(computation: BaseComputation, beneficiary: Address) -> None:
raise Halt('SELFDESTRUCT')


class CreateOpcodeStackData:

def __init__(self,
endowment: int,
memory_start: int,
memory_length: int,
salt: int = None) -> None:

self.endowment = endowment
self.memory_start = memory_start
self.memory_length = memory_length
self.salt = salt


class Create(Opcode):

def max_child_gas_modifier(self, gas: int) -> int:
return gas

def __call__(self, computation: BaseComputation) -> None:
computation.consume_gas(self.gas_cost, reason=self.mnemonic)
def generate_contract_address(self,
stack_frame: CreateOpcodeStackData,
call_data: bytes,
computation: BaseComputation) -> Address:

value, start_position, size = computation.stack_pop(
creation_nonce = computation.state.account_db.get_nonce(computation.msg.storage_address)
computation.state.account_db.increment_nonce(computation.msg.storage_address)

contract_address = generate_contract_address(
computation.msg.storage_address,
creation_nonce,
)

return contract_address

def get_stack_data(self, computation: BaseComputation) -> CreateOpcodeStackData:
endowment, memory_start, memory_length = computation.stack_pop(
num_items=3,
type_hint=constants.UINT256,
)

computation.extend_memory(start_position, size)
return CreateOpcodeStackData(endowment, memory_start, memory_length)

def __call__(self, computation: BaseComputation) -> None:
computation.consume_gas(self.gas_cost, reason=self.mnemonic)

stack_data = self.get_stack_data(computation)

computation.extend_memory(stack_data.memory_start, stack_data.memory_length)

insufficient_funds = computation.state.account_db.get_balance(
computation.msg.storage_address
) < value
) < stack_data.endowment
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)
call_data = computation.memory_read(stack_data.memory_start, stack_data.memory_length)

create_msg_gas = self.max_child_gas_modifier(
computation.get_gas_remaining()
)
computation.consume_gas(create_msg_gas, reason="CREATE")
computation.consume_gas(create_msg_gas, reason=self.mnemonic)

creation_nonce = computation.state.account_db.get_nonce(computation.msg.storage_address)
computation.state.account_db.increment_nonce(computation.msg.storage_address)

contract_address = generate_contract_address(
computation.msg.storage_address,
creation_nonce,
)
contract_address = self.generate_contract_address(stack_data, call_data, computation)

is_collision = computation.state.account_db.account_has_code_or_nonce(contract_address)

Expand All @@ -152,7 +181,7 @@ def __call__(self, computation: BaseComputation) -> None:
child_msg = computation.prepare_child_message(
gas=create_msg_gas,
to=constants.CREATE_CONTRACT_ADDRESS,
value=value,
value=stack_data.endowment,
data=b'',
code=call_data,
create_address=contract_address,
Expand All @@ -177,3 +206,26 @@ def __call__(self, computation: BaseComputation) -> None:
if computation.msg.is_static:
raise WriteProtection("Cannot modify state while inside of a STATICCALL context")
return super().__call__(computation)


class Create2(CreateByzantium):

def get_stack_data(self, computation: BaseComputation) -> CreateOpcodeStackData:

endowment, memory_start, memory_length, salt = computation.stack_pop(
num_items=4,
type_hint=constants.UINT256,
)

return CreateOpcodeStackData(endowment, memory_start, memory_length, salt)

def generate_contract_address(self,
stack_frame: CreateOpcodeStackData,
call_data: bytes,
computation: BaseComputation) -> Address:

return generate_safe_contract_address(
computation.msg.storage_address,
stack_frame.salt,
call_data
)
1 change: 1 addition & 0 deletions eth/vm/mnemonics.py
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,7 @@
# System
#
CREATE = 'CREATE'
CREATE2 = 'CREATE2'
CALL = 'CALL'
CALLCODE = 'CALLCODE'
STATICCALL = 'STATICCALL'
Expand Down
1 change: 1 addition & 0 deletions eth/vm/opcode_values.py
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,7 @@
# System
#
CREATE = 0xf0
CREATE2 = 0xf5
CALL = 0xf1
CALLCODE = 0xf2
RETURN = 0xf3
Expand Down

0 comments on commit d206216

Please sign in to comment.