From e2532e9c94647fb9148eec95f6c5fc68500382dc Mon Sep 17 00:00:00 2001 From: Christoph Burgdorf Date: Wed, 15 Aug 2018 09:28:15 +0200 Subject: [PATCH] Implement Create2 opcode for Constantinople Closes #1106 --- eth/utils/address.py | 16 +++++ eth/vm/forks/byzantium/opcodes.py | 1 - eth/vm/forks/constantinople/opcodes.py | 6 ++ eth/vm/logic/system.py | 82 +++++++++++++++++++++----- eth/vm/mnemonics.py | 1 + eth/vm/opcode_values.py | 1 + 6 files changed, 91 insertions(+), 16 deletions(-) diff --git a/eth/utils/address.py b/eth/utils/address.py index ec90d8a7ad..f72237db5e 100644 --- a/eth/utils/address.py +++ b/eth/utils/address.py @@ -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') @@ -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:] + ) diff --git a/eth/vm/forks/byzantium/opcodes.py b/eth/vm/forks/byzantium/opcodes.py index 5059f390d0..37d83bdb49 100644 --- a/eth/vm/forks/byzantium/opcodes.py +++ b/eth/vm/forks/byzantium/opcodes.py @@ -103,7 +103,6 @@ def inner(computation): mnemonic=mnemonics.CREATE, gas_cost=constants.GAS_CREATE, )(), - # TODO: CREATE2 # # Storage # diff --git a/eth/vm/forks/constantinople/opcodes.py b/eth/vm/forks/constantinople/opcodes.py index 039f279ccd..3975b06eb9 100644 --- a/eth/vm/forks/constantinople/opcodes.py +++ b/eth/vm/forks/constantinople/opcodes.py @@ -19,6 +19,7 @@ from eth.vm.logic import ( arithmetic, context, + system, ) from eth.vm.opcode import ( as_opcode @@ -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( diff --git a/eth/vm/logic/system.py b/eth/vm/logic/system.py index 98ee909271..75ce97ea3b 100644 --- a/eth/vm/logic/system.py +++ b/eth/vm/logic/system.py @@ -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, @@ -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_data: 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) @@ -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, @@ -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_data: CreateOpcodeStackData, + call_data: bytes, + computation: BaseComputation) -> Address: + + return generate_safe_contract_address( + computation.msg.storage_address, + stack_data.salt, + call_data + ) diff --git a/eth/vm/mnemonics.py b/eth/vm/mnemonics.py index e3bb17b728..85a5f81eec 100644 --- a/eth/vm/mnemonics.py +++ b/eth/vm/mnemonics.py @@ -163,6 +163,7 @@ # System # CREATE = 'CREATE' +CREATE2 = 'CREATE2' CALL = 'CALL' CALLCODE = 'CALLCODE' STATICCALL = 'STATICCALL' diff --git a/eth/vm/opcode_values.py b/eth/vm/opcode_values.py index 33f8fe135c..5c2a84f9a2 100644 --- a/eth/vm/opcode_values.py +++ b/eth/vm/opcode_values.py @@ -183,6 +183,7 @@ # System # CREATE = 0xf0 +CREATE2 = 0xf5 CALL = 0xf1 CALLCODE = 0xf2 RETURN = 0xf3