Skip to content

Commit

Permalink
Implement EXTCODEHASH opcode for Constantinople
Browse files Browse the repository at this point in the history
Closes #719
  • Loading branch information
cburgdorf committed Aug 8, 2018
1 parent 269efb7 commit 9695e6e
Show file tree
Hide file tree
Showing 6 changed files with 82 additions and 4 deletions.
1 change: 1 addition & 0 deletions eth/vm/forks/constantinople/constants.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
GAS_EXTCODEHASH_EIP1052 = 400
11 changes: 10 additions & 1 deletion eth/vm/forks/constantinople/opcodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,12 @@
from eth.vm.forks.byzantium.opcodes import (
BYZANTIUM_OPCODES
)
from eth.vm.forks.constantinople.constants import (
GAS_EXTCODEHASH_EIP1052
)
from eth.vm.logic import (
arithmetic
arithmetic,
context,
)
from eth.vm.opcode import (
as_opcode
Expand All @@ -37,6 +41,11 @@
mnemonic=mnemonics.SAR,
gas_cost=constants.GAS_VERYLOW,
),
opcode_values.EXTCODEHASH: as_opcode(
logic_fn=context.extcodehash,
mnemonic=mnemonics.EXTCODEHASH,
gas_cost=GAS_EXTCODEHASH_EIP1052,
),
}

CONSTANTINOPLE_OPCODES = merge(
Expand Down
14 changes: 14 additions & 0 deletions eth/vm/logic/context.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,20 @@ def extcodecopy(computation):
computation.memory_write(mem_start_position, size, padded_code_bytes)


def extcodehash(computation):
"""
Return the code hash for a given address.
EIP: https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1052.md
"""
account = force_bytes_to_address(computation.stack_pop(type_hint=constants.BYTES))
account_db = computation.state.account_db

if not account_db.account_exists(account):
computation.stack_push(constants.NULL_BYTE)
else:
computation.stack_push(account_db.get_code_hash(account))


def returndatasize(computation):
size = len(computation.return_data)
computation.stack_push(size)
Expand Down
1 change: 1 addition & 0 deletions eth/vm/mnemonics.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
GASPRICE = 'GASPRICE'
EXTCODESIZE = 'EXTCODESIZE'
EXTCODECOPY = 'EXTCODECOPY'
EXTCODEHASH = 'EXTCODEHASH'
RETURNDATASIZE = 'RETURNDATASIZE'
RETURNDATACOPY = 'RETURNDATACOPY'
#
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 @@ -56,6 +56,7 @@
GASPRICE = 0x3a
EXTCODESIZE = 0x3b
EXTCODECOPY = 0x3c
EXTCODEHASH = 0x3f
RETURNDATASIZE = 0x3d
RETURNDATACOPY = 0x3e

Expand Down
58 changes: 55 additions & 3 deletions tests/core/opcodes/test_opcodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,18 @@
to_canonical_address,
int_to_big_endian,
)

from eth import (
constants
)
from eth.db.backends.memory import (
MemoryDB
)
from eth.db.chain import (
ChainDB
)
from eth.rlp.headers import (
BlockHeader,
)
from eth.utils.padding import (
pad32
)
Expand All @@ -24,16 +32,23 @@
HomesteadVM,
FrontierVM,
)

from eth.vm.message import (
Message,
)


NORMALIZED_ADDRESS_A = "0x0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6"
NORMALIZED_ADDRESS_B = "0xcd1722f3947def4cf144679da39c4c32bdc35681"
ADDRESS_WITH_CODE = ("0xddd722f3947def4cf144679da39c4c32bdc35681", b'pseudocode')
EMPTY_ADDRESS_IN_STATE = NORMALIZED_ADDRESS_A
ADDRESS_NOT_IN_STATE = NORMALIZED_ADDRESS_B
CANONICAL_ADDRESS_A = to_canonical_address("0x0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6")
CANONICAL_ADDRESS_B = to_canonical_address("0xcd1722f3947def4cf144679da39c4c32bdc35681")
GENESIS_HEADER = BlockHeader(
difficulty=constants.GENESIS_DIFFICULTY,
block_number=constants.GENESIS_BLOCK_NUMBER,
gas_limit=constants.GENESIS_GAS_LIMIT,
)


def prepare_computation(vm_class):
Expand All @@ -52,11 +67,17 @@ def prepare_computation(vm_class):
origin=CANONICAL_ADDRESS_B,
)

vm = vm_class(GENESIS_HEADER, ChainDB(MemoryDB()))

computation = vm_class._state_class.computation_class(
state=None,
state=vm.state,
message=message,
transaction_context=tx_context,
)

computation.state.account_db.touch_account(decode_hex(EMPTY_ADDRESS_IN_STATE))
computation.state.account_db.set_code(decode_hex(ADDRESS_WITH_CODE[0]), ADDRESS_WITH_CODE[1])

return computation


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

result = computation.stack_pop(type_hint=constants.UINT256)
assert encode_hex(pad32(int_to_big_endian(result))) == expected


@pytest.mark.parametrize(
'vm_class, address, expected',
(
(
ConstantinopleVM,
ADDRESS_NOT_IN_STATE,
'0x0000000000000000000000000000000000000000000000000000000000000000',
),
(
ConstantinopleVM,
EMPTY_ADDRESS_IN_STATE,
'0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470',
),
(
ConstantinopleVM,
ADDRESS_WITH_CODE[0],
# equivalent to encode_hex(keccak(ADDRESS_WITH_CODE[1])),
'0xb6f5188e2984211a0de167a56a92d85bee084d7a469d97a59e1e2b573dbb4301'
),
)
)
def test_extcodehash(vm_class, address, expected):
computation = prepare_computation(vm_class)

computation.stack_push(decode_hex(address))
computation.opcodes[opcode_values.EXTCODEHASH](computation)

result = computation.stack_pop(type_hint=constants.BYTES)
assert encode_hex(pad32(result)) == expected

0 comments on commit 9695e6e

Please sign in to comment.