Skip to content

Implement EIP-5920 #1225

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

Open
wants to merge 2 commits into
base: devnets/osaka/0
Choose a base branch
from

Conversation

gurukamath
Copy link
Collaborator

@gurukamath gurukamath commented May 16, 2025

(closes #1181 )

What was wrong?

The current Osaka branch does not have the PAY opcode implementation which has been CFI'd for Osaka

Related to Issue #1181

How was it fixed?

Implement EIP-5920

Cute Animal Picture

Cute Animals - 1 of 1

@gurukamath gurukamath force-pushed the eips/osaka/eip-5920 branch from d1113c3 to 6dd18e4 Compare May 16, 2025 07:56
Copy link
Member

@jochem-brouwer jochem-brouwer left a comment

Choose a reason for hiding this comment

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

I think this matches the spec! I got some minor suggestions though 😄 👍

@@ -22,6 +22,9 @@
from ethereum.utils.byte import left_pad_zero_bytes

from ..fork_types import Address
from ..vm.exceptions import InvalidParameter

MAX_ADDRESS = Address(b"\xff" * 20)
Copy link
Member

Choose a reason for hiding this comment

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

As optimization you could add MAX_ADDRESS_UINT256 here as a constant

address : `Address`
The obtained address.
"""
if data > U256.from_be_bytes(MAX_ADDRESS):
Copy link
Member

Choose a reason for hiding this comment

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

And then query the MAX_ADDRESS_UINT256 here 😄 👍


# OPERATION
if evm.message.is_static:
raise WriteInStaticContext("Cannot PAY in static context")
Copy link
Member

Choose a reason for hiding this comment

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

In the spec, this check is done before any other operation (so also before popping any value from stack). I don't think moving this around in this method would yield a different outcome, but it would match the spec better to do this check prior to anything else. So I would suggest moving this check up.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yes. This does not make a difference to the outcome since all the gas in consumed and warmed addresses are discarded upon error. However, I implemented it in this order since that makes it consistent with the implementation of CALL* and other methods where you check if the frame is static.

if to in evm.accessed_addresses:
access_gas_cost = GAS_WARM_ACCESS
else:
evm.accessed_addresses.add(to)
Copy link
Member

Choose a reason for hiding this comment

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

If the method encounters an error later, is the to address removed from warm addresses if it got added here?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

It will continue to be a part of the warm addresses in the current execution frame but will not be added to the warm addresses of the parent frame (contrast incorporate_child_on_success vs incorporate_child_on_error here). This means the warmed up address play no further role in the execution.

If we are in the initial frame, the warmed addresses are not explicitly removed. However, since an error is basically the end of the execution in that case, the warmed addresses play not further role here either.

@gurukamath gurukamath linked an issue May 19, 2025 that may be closed by this pull request
@gurukamath gurukamath changed the base branch from forks/osaka to devnets/osaka/0 May 19, 2025 15:26
Comment on lines +762 to +766
if to in evm.accessed_addresses:
access_gas_cost = GAS_WARM_ACCESS
else:
evm.accessed_addresses.add(to)
access_gas_cost = GAS_COLD_ACCOUNT_ACCESS
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
if to in evm.accessed_addresses:
access_gas_cost = GAS_WARM_ACCESS
else:
evm.accessed_addresses.add(to)
access_gas_cost = GAS_COLD_ACCOUNT_ACCESS
access_gas_cost = GAS_WARM_ACCESS
if to not in evm.accessed_addresses:
access_gas_cost = GAS_COLD_ACCOUNT_ACCESS
evm.accessed_addresses.add(to)

Personally I find this more readable, but I'm weird.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This is just the way we define access_gas_cost in other opcodes. I do not see a big difference one way or another, so am ok with either representation

Comment on lines +782 to +791
try:
move_ether(
evm.message.block_env.state,
evm.message.current_target,
to,
value,
)
push(evm.stack, U256(1))
except AssertionError:
push(evm.stack, U256(0))
Copy link
Contributor

Choose a reason for hiding this comment

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

We should not be using exception handling.

Suggested change
try:
move_ether(
evm.message.block_env.state,
evm.message.current_target,
to,
value,
)
push(evm.stack, U256(1))
except AssertionError:
push(evm.stack, U256(0))
sender_balance = get_account(
evm.message.block_env.state, evm.message.current_target
).balance
if sender_balance < value:
push(evm.stack, U256(0))
else:
move_ether(
evm.message.block_env.state,
evm.message.current_target,
to,
value,
)
push(evm.stack, U256(1))

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I would have also prefered to not catch a general AssertionError but since the check is already performed in move_ether, I think we should just take advantage of that. Perhaps, raising a more specific exception in move_ether like InsufficientBalance and trying to catch that is a better way to go.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Implement EIP-5920
3 participants