Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement EXTCODEHASH opcode for Constantinople #1147

Merged
merged 1 commit into from
Aug 8, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nitpick

The other opcodes are in byte order, maybe put this after RETURNDATACOPY?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was thinking back and forth for a minute when I wrote it but then settled with alphabetical order as that is the chosen order at most other places (e.g. in mnemonics). But now that I think about it again, would probably have made sense to use byte order here to stay consistent within this location.

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