Skip to content

Commit d1113c3

Browse files
committed
Implement EIP-5920
1 parent 68f3a1f commit d1113c3

File tree

3 files changed

+80
-0
lines changed

3 files changed

+80
-0
lines changed

src/ethereum/osaka/utils/address.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@
2222
from ethereum.utils.byte import left_pad_zero_bytes
2323

2424
from ..fork_types import Address
25+
from ..vm.exceptions import InvalidParameter
26+
27+
MAX_ADDRESS = Address(b"\xff" * 20)
2528

2629

2730
def to_address(data: Union[Uint, U256]) -> Address:
@@ -41,6 +44,31 @@ def to_address(data: Union[Uint, U256]) -> Address:
4144
return Address(data.to_be_bytes32()[-20:])
4245

4346

47+
def to_address_without_mask(data: U256) -> Address:
48+
"""
49+
Convert a Uint or U256 value to a valid address (20 bytes).
50+
Raises a `ValueError` if the data is larger than `MAX_ADDRESS
51+
52+
Parameters
53+
----------
54+
data :
55+
The string to be converted to bytes.
56+
57+
Raises
58+
------
59+
ValueError
60+
If `data` is larger than `MAX_ADDRESS`.
61+
62+
Returns
63+
-------
64+
address : `Address`
65+
The obtained address.
66+
"""
67+
if data > U256.from_be_bytes(MAX_ADDRESS):
68+
raise InvalidParameter("Address is too large")
69+
return Address(data.to_be_bytes32()[-20:])
70+
71+
4472
def compute_contract_address(address: Address, nonce: Uint) -> Address:
4573
"""
4674
Computes address of the new account that needs to be created.

src/ethereum/osaka/vm/instructions/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,7 @@ class Ops(enum.Enum):
210210
DELEGATECALL = 0xF4
211211
CREATE2 = 0xF5
212212
STATICCALL = 0xFA
213+
PAY = 0xFC
213214
REVERT = 0xFD
214215
SELFDESTRUCT = 0xFF
215216

@@ -361,6 +362,7 @@ class Ops(enum.Enum):
361362
Ops.DELEGATECALL: system_instructions.delegatecall,
362363
Ops.SELFDESTRUCT: system_instructions.selfdestruct,
363364
Ops.STATICCALL: system_instructions.staticcall,
365+
Ops.PAY: system_instructions.pay,
364366
Ops.REVERT: system_instructions.revert,
365367
Ops.CREATE2: system_instructions.create2,
366368
}

src/ethereum/osaka/vm/instructions/system.py

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
compute_contract_address,
3232
compute_create2_contract_address,
3333
to_address,
34+
to_address_without_mask,
3435
)
3536
from ...vm.eoa_delegation import access_delegation
3637
from .. import (
@@ -742,3 +743,52 @@ def revert(evm: Evm) -> None:
742743

743744
# PROGRAM COUNTER
744745
pass
746+
747+
748+
def pay(evm: Evm) -> None:
749+
"""
750+
Transfer ether to an account.
751+
752+
Parameters
753+
----------
754+
evm :
755+
The current EVM frame.
756+
"""
757+
# STACK
758+
to = to_address_without_mask(pop(evm.stack))
759+
value = pop(evm.stack)
760+
761+
# GAS
762+
if to in evm.accessed_addresses:
763+
access_gas_cost = GAS_WARM_ACCESS
764+
else:
765+
evm.accessed_addresses.add(to)
766+
access_gas_cost = GAS_COLD_ACCOUNT_ACCESS
767+
768+
create_gas_cost = (
769+
Uint(0)
770+
if is_account_alive(evm.message.block_env.state, to) or value == 0
771+
else GAS_NEW_ACCOUNT
772+
)
773+
774+
transfer_gas_cost = Uint(0) if value == U256(0) else GAS_CALL_VALUE
775+
776+
charge_gas(evm, access_gas_cost + create_gas_cost + transfer_gas_cost)
777+
778+
# OPERATION
779+
if evm.message.is_static:
780+
raise WriteInStaticContext("Cannot PAY in static context")
781+
782+
try:
783+
move_ether(
784+
evm.message.block_env.state,
785+
evm.message.current_target,
786+
to,
787+
value,
788+
)
789+
push(evm.stack, U256(1))
790+
except AssertionError:
791+
push(evm.stack, U256(0))
792+
793+
# PROGRAM COUNTER
794+
evm.pc += Uint(1)

0 commit comments

Comments
 (0)