Skip to content

Commit 9695e6e

Browse files
committed
Implement EXTCODEHASH opcode for Constantinople
Closes #719
1 parent 269efb7 commit 9695e6e

File tree

6 files changed

+82
-4
lines changed

6 files changed

+82
-4
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
GAS_EXTCODEHASH_EIP1052 = 400

eth/vm/forks/constantinople/opcodes.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,12 @@
1313
from eth.vm.forks.byzantium.opcodes import (
1414
BYZANTIUM_OPCODES
1515
)
16+
from eth.vm.forks.constantinople.constants import (
17+
GAS_EXTCODEHASH_EIP1052
18+
)
1619
from eth.vm.logic import (
17-
arithmetic
20+
arithmetic,
21+
context,
1822
)
1923
from eth.vm.opcode import (
2024
as_opcode
@@ -37,6 +41,11 @@
3741
mnemonic=mnemonics.SAR,
3842
gas_cost=constants.GAS_VERYLOW,
3943
),
44+
opcode_values.EXTCODEHASH: as_opcode(
45+
logic_fn=context.extcodehash,
46+
mnemonic=mnemonics.EXTCODEHASH,
47+
gas_cost=GAS_EXTCODEHASH_EIP1052,
48+
),
4049
}
4150

4251
CONSTANTINOPLE_OPCODES = merge(

eth/vm/logic/context.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,20 @@ def extcodecopy(computation):
138138
computation.memory_write(mem_start_position, size, padded_code_bytes)
139139

140140

141+
def extcodehash(computation):
142+
"""
143+
Return the code hash for a given address.
144+
EIP: https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1052.md
145+
"""
146+
account = force_bytes_to_address(computation.stack_pop(type_hint=constants.BYTES))
147+
account_db = computation.state.account_db
148+
149+
if not account_db.account_exists(account):
150+
computation.stack_push(constants.NULL_BYTE)
151+
else:
152+
computation.stack_push(account_db.get_code_hash(account))
153+
154+
141155
def returndatasize(computation):
142156
size = len(computation.return_data)
143157
computation.stack_push(size)

eth/vm/mnemonics.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
GASPRICE = 'GASPRICE'
5151
EXTCODESIZE = 'EXTCODESIZE'
5252
EXTCODECOPY = 'EXTCODECOPY'
53+
EXTCODEHASH = 'EXTCODEHASH'
5354
RETURNDATASIZE = 'RETURNDATASIZE'
5455
RETURNDATACOPY = 'RETURNDATACOPY'
5556
#

eth/vm/opcode_values.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@
5656
GASPRICE = 0x3a
5757
EXTCODESIZE = 0x3b
5858
EXTCODECOPY = 0x3c
59+
EXTCODEHASH = 0x3f
5960
RETURNDATASIZE = 0x3d
6061
RETURNDATACOPY = 0x3e
6162

tests/core/opcodes/test_opcodes.py

Lines changed: 55 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,18 @@
66
to_canonical_address,
77
int_to_big_endian,
88
)
9-
109
from eth import (
1110
constants
1211
)
12+
from eth.db.backends.memory import (
13+
MemoryDB
14+
)
15+
from eth.db.chain import (
16+
ChainDB
17+
)
18+
from eth.rlp.headers import (
19+
BlockHeader,
20+
)
1321
from eth.utils.padding import (
1422
pad32
1523
)
@@ -24,16 +32,23 @@
2432
HomesteadVM,
2533
FrontierVM,
2634
)
27-
2835
from eth.vm.message import (
2936
Message,
3037
)
3138

3239

3340
NORMALIZED_ADDRESS_A = "0x0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6"
3441
NORMALIZED_ADDRESS_B = "0xcd1722f3947def4cf144679da39c4c32bdc35681"
42+
ADDRESS_WITH_CODE = ("0xddd722f3947def4cf144679da39c4c32bdc35681", b'pseudocode')
43+
EMPTY_ADDRESS_IN_STATE = NORMALIZED_ADDRESS_A
44+
ADDRESS_NOT_IN_STATE = NORMALIZED_ADDRESS_B
3545
CANONICAL_ADDRESS_A = to_canonical_address("0x0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6")
3646
CANONICAL_ADDRESS_B = to_canonical_address("0xcd1722f3947def4cf144679da39c4c32bdc35681")
47+
GENESIS_HEADER = BlockHeader(
48+
difficulty=constants.GENESIS_DIFFICULTY,
49+
block_number=constants.GENESIS_BLOCK_NUMBER,
50+
gas_limit=constants.GENESIS_GAS_LIMIT,
51+
)
3752

3853

3954
def prepare_computation(vm_class):
@@ -52,11 +67,17 @@ def prepare_computation(vm_class):
5267
origin=CANONICAL_ADDRESS_B,
5368
)
5469

70+
vm = vm_class(GENESIS_HEADER, ChainDB(MemoryDB()))
71+
5572
computation = vm_class._state_class.computation_class(
56-
state=None,
73+
state=vm.state,
5774
message=message,
5875
transaction_context=tx_context,
5976
)
77+
78+
computation.state.account_db.touch_account(decode_hex(EMPTY_ADDRESS_IN_STATE))
79+
computation.state.account_db.set_code(decode_hex(ADDRESS_WITH_CODE[0]), ADDRESS_WITH_CODE[1])
80+
6081
return computation
6182

6283

@@ -378,3 +399,34 @@ def test_sar(vm_class, val1, val2, expected):
378399

379400
result = computation.stack_pop(type_hint=constants.UINT256)
380401
assert encode_hex(pad32(int_to_big_endian(result))) == expected
402+
403+
404+
@pytest.mark.parametrize(
405+
'vm_class, address, expected',
406+
(
407+
(
408+
ConstantinopleVM,
409+
ADDRESS_NOT_IN_STATE,
410+
'0x0000000000000000000000000000000000000000000000000000000000000000',
411+
),
412+
(
413+
ConstantinopleVM,
414+
EMPTY_ADDRESS_IN_STATE,
415+
'0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470',
416+
),
417+
(
418+
ConstantinopleVM,
419+
ADDRESS_WITH_CODE[0],
420+
# equivalent to encode_hex(keccak(ADDRESS_WITH_CODE[1])),
421+
'0xb6f5188e2984211a0de167a56a92d85bee084d7a469d97a59e1e2b573dbb4301'
422+
),
423+
)
424+
)
425+
def test_extcodehash(vm_class, address, expected):
426+
computation = prepare_computation(vm_class)
427+
428+
computation.stack_push(decode_hex(address))
429+
computation.opcodes[opcode_values.EXTCODEHASH](computation)
430+
431+
result = computation.stack_pop(type_hint=constants.BYTES)
432+
assert encode_hex(pad32(result)) == expected

0 commit comments

Comments
 (0)