diff --git a/eth/vm/logic/system.py b/eth/vm/logic/system.py index 98ee909271..cf2f47f4dd 100644 --- a/eth/vm/logic/system.py +++ b/eth/vm/logic/system.py @@ -2,6 +2,10 @@ Address, ) +from eth_hash.auto import ( + keccak +) + from eth import constants from eth.exceptions import ( Halt, @@ -13,6 +17,9 @@ force_bytes_to_address, generate_contract_address, ) +from eth.utils.numeric import ( + int_to_big_endian, +) from eth.utils.hexadecimal import ( encode_hex, ) @@ -99,45 +106,71 @@ def _selfdestruct(computation: BaseComputation, beneficiary: Address) -> None: computation.register_account_for_deletion(beneficiary) raise Halt('SELFDESTRUCT') +class CreateStackFrame: + + 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: CreateStackFrame, + call_data: bytes, + computation: BaseComputation) -> Address: + + creation_nonce = computation.state.account_db.get_nonce(computation.msg.storage_address) + computation.state.account_db.increment_nonce(computation.msg.storage_address) - value, start_position, size = computation.stack_pop( + contract_address = generate_contract_address( + computation.msg.storage_address, + creation_nonce, + ) + + return contract_address + + def get_stack_frame(self, computation: BaseComputation) -> CreateStackFrame: + endowment, memory_start, memory_length = computation.stack_pop( num_items=3, type_hint=constants.UINT256, ) - computation.extend_memory(start_position, size) + return CreateStackFrame(endowment, memory_start, memory_length) + + def __call__(self, computation: BaseComputation) -> None: + computation.consume_gas(self.gas_cost, reason=self.mnemonic) + + stack_frame = self.get_stack_frame(computation) + + computation.extend_memory(stack_frame.memory_start, stack_frame.memory_length) insufficient_funds = computation.state.account_db.get_balance( computation.msg.storage_address - ) < value + ) < stack_frame.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_frame.memory_start, stack_frame.memory_length) create_msg_gas = self.max_child_gas_modifier( computation.get_gas_remaining() ) computation.consume_gas(create_msg_gas, reason="CREATE") - 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_frame, call_data, computation) is_collision = computation.state.account_db.account_has_code_or_nonce(contract_address) @@ -152,7 +185,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_frame.endowment, data=b'', code=call_data, create_address=contract_address, @@ -177,3 +210,29 @@ 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_frame(self, computation: BaseComputation) -> CreateStackFrame: + + endowment, memory_start, memory_length, salt = computation.stack_pop( + num_items=4, + type_hint=constants.UINT256, + ) + + return CreateStackFrame(endowment, memory_start, memory_length, salt) + + def generate_contract_address(self, + stack_frame: CreateStackFrame, + call_data: bytes, + computation: BaseComputation) -> Address: + + return Address( + keccak( + encode_hex('0xff') + + computation.msg.storage_address + + int_to_big_endian(stack_frame.salt) + + keccak(call_data) + )[-20:] + )