Skip to content

Commit d4b0198

Browse files
committed
Implement Create2 opcode for Constantinople
Closes ethereum#1106
1 parent be43653 commit d4b0198

File tree

6 files changed

+88
-16
lines changed

6 files changed

+88
-16
lines changed

eth/utils/address.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,11 @@
33
from eth_hash.auto import keccak
44
from eth_typing import Address
55

6+
from eth.utils.numeric import (
7+
int_to_big_endian,
8+
)
9+
10+
611
def force_bytes_to_address(value: bytes) -> Address:
712
trimmed_value = value[-20:]
813
padded_value = trimmed_value.rjust(20, b'\x00')
@@ -11,3 +16,11 @@ def force_bytes_to_address(value: bytes) -> Address:
1116

1217
def generate_contract_address(address: Address, nonce: bytes) -> Address:
1318
return Address(keccak(rlp.encode([address, nonce]))[-20:])
19+
20+
21+
def generate_safe_contract_address(address: Address,
22+
salt: int,
23+
call_data: bytes) -> Address:
24+
return force_bytes_to_address(
25+
keccak(b'\xff' + address + int_to_big_endian(salt) + keccak(call_data))
26+
)

eth/vm/forks/byzantium/opcodes.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,6 @@ def inner(computation):
103103
mnemonic=mnemonics.CREATE,
104104
gas_cost=constants.GAS_CREATE,
105105
)(),
106-
# TODO: CREATE2
107106
#
108107
# Storage
109108
#

eth/vm/forks/constantinople/opcodes.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
from eth.vm.logic import (
2020
arithmetic,
2121
context,
22+
system,
2223
)
2324
from eth.vm.opcode import (
2425
as_opcode
@@ -46,6 +47,11 @@
4647
mnemonic=mnemonics.EXTCODEHASH,
4748
gas_cost=GAS_EXTCODEHASH_EIP1052,
4849
),
50+
opcode_values.CREATE2: system.Create2.configure(
51+
__name__='opcode:CREATE2',
52+
mnemonic=mnemonics.CREATE2,
53+
gas_cost=constants.GAS_CREATE,
54+
)(),
4955
}
5056

5157
CONSTANTINOPLE_OPCODES = merge(

eth/vm/logic/system.py

Lines changed: 67 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
from eth.utils.address import (
1313
force_bytes_to_address,
1414
generate_contract_address,
15+
generate_safe_contract_address,
1516
)
1617
from eth.utils.hexadecimal import (
1718
encode_hex,
@@ -100,44 +101,72 @@ def _selfdestruct(computation: BaseComputation, beneficiary: Address) -> None:
100101
raise Halt('SELFDESTRUCT')
101102

102103

104+
class CreateOpcodeStackData:
105+
106+
def __init__(self,
107+
endowment: int,
108+
memory_start: int,
109+
memory_length: int,
110+
salt: int = None) -> None:
111+
112+
self.endowment = endowment
113+
self.memory_start = memory_start
114+
self.memory_length = memory_length
115+
self.salt = salt
116+
117+
103118
class Create(Opcode):
104119

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

108-
def __call__(self, computation: BaseComputation) -> None:
109-
computation.consume_gas(self.gas_cost, reason=self.mnemonic)
123+
def generate_contract_address(self,
124+
stack_data: CreateOpcodeStackData,
125+
call_data: bytes,
126+
computation: BaseComputation) -> Address:
110127

111-
value, start_position, size = computation.stack_pop(
128+
creation_nonce = computation.state.account_db.get_nonce(computation.msg.storage_address)
129+
computation.state.account_db.increment_nonce(computation.msg.storage_address)
130+
131+
contract_address = generate_contract_address(
132+
computation.msg.storage_address,
133+
creation_nonce,
134+
)
135+
136+
return contract_address
137+
138+
def get_stack_data(self, computation: BaseComputation) -> CreateOpcodeStackData:
139+
endowment, memory_start, memory_length = computation.stack_pop(
112140
num_items=3,
113141
type_hint=constants.UINT256,
114142
)
115143

116-
computation.extend_memory(start_position, size)
144+
return CreateOpcodeStackData(endowment, memory_start, memory_length)
145+
146+
def __call__(self, computation: BaseComputation) -> None:
147+
computation.consume_gas(self.gas_cost, reason=self.mnemonic)
148+
149+
stack_data = self.get_stack_data(computation)
150+
151+
computation.extend_memory(stack_data.memory_start, stack_data.memory_length)
117152

118153
insufficient_funds = computation.state.account_db.get_balance(
119154
computation.msg.storage_address
120-
) < value
155+
) < stack_data.endowment
121156
stack_too_deep = computation.msg.depth + 1 > constants.STACK_DEPTH_LIMIT
122157

123158
if insufficient_funds or stack_too_deep:
124159
computation.stack_push(0)
125160
return
126161

127-
call_data = computation.memory_read(start_position, size)
162+
call_data = computation.memory_read(stack_data.memory_start, stack_data.memory_length)
128163

129164
create_msg_gas = self.max_child_gas_modifier(
130165
computation.get_gas_remaining()
131166
)
132-
computation.consume_gas(create_msg_gas, reason="CREATE")
167+
computation.consume_gas(create_msg_gas, reason=self.mnemonic)
133168

134-
creation_nonce = computation.state.account_db.get_nonce(computation.msg.storage_address)
135-
computation.state.account_db.increment_nonce(computation.msg.storage_address)
136-
137-
contract_address = generate_contract_address(
138-
computation.msg.storage_address,
139-
creation_nonce,
140-
)
169+
contract_address = self.generate_contract_address(stack_data, call_data, computation)
141170

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

@@ -152,7 +181,7 @@ def __call__(self, computation: BaseComputation) -> None:
152181
child_msg = computation.prepare_child_message(
153182
gas=create_msg_gas,
154183
to=constants.CREATE_CONTRACT_ADDRESS,
155-
value=value,
184+
value=stack_data.endowment,
156185
data=b'',
157186
code=call_data,
158187
create_address=contract_address,
@@ -177,3 +206,26 @@ def __call__(self, computation: BaseComputation) -> None:
177206
if computation.msg.is_static:
178207
raise WriteProtection("Cannot modify state while inside of a STATICCALL context")
179208
return super().__call__(computation)
209+
210+
211+
class Create2(CreateByzantium):
212+
213+
def get_stack_data(self, computation: BaseComputation) -> CreateOpcodeStackData:
214+
215+
endowment, memory_start, memory_length, salt = computation.stack_pop(
216+
num_items=4,
217+
type_hint=constants.UINT256,
218+
)
219+
220+
return CreateOpcodeStackData(endowment, memory_start, memory_length, salt)
221+
222+
def generate_contract_address(self,
223+
stack_data: CreateOpcodeStackData,
224+
call_data: bytes,
225+
computation: BaseComputation) -> Address:
226+
227+
return generate_safe_contract_address(
228+
computation.msg.storage_address,
229+
stack_data.salt,
230+
call_data
231+
)

eth/vm/mnemonics.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,7 @@
163163
# System
164164
#
165165
CREATE = 'CREATE'
166+
CREATE2 = 'CREATE2'
166167
CALL = 'CALL'
167168
CALLCODE = 'CALLCODE'
168169
STATICCALL = 'STATICCALL'

eth/vm/opcode_values.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,7 @@
183183
# System
184184
#
185185
CREATE = 0xf0
186+
CREATE2 = 0xf5
186187
CALL = 0xf1
187188
CALLCODE = 0xf2
188189
RETURN = 0xf3

0 commit comments

Comments
 (0)