Skip to content
16 changes: 9 additions & 7 deletions src/ethereum/forks/monad_eight/fork.py
Original file line number Diff line number Diff line change
Expand Up @@ -434,7 +434,7 @@ def check_transaction(
NonceMismatchError :
If the nonce of the transaction is not equal to the sender's nonce.
InsufficientBalanceError :
If the sender's balance is not enough to pay for the transaction.
If the sender's balance is not enough to pay for the gas fees.
InvalidSenderError :
If the transaction is from an address that does not exist anymore.
PriorityFeeGreaterThanMaxFeeError :
Expand Down Expand Up @@ -495,12 +495,14 @@ def check_transaction(
tx.max_fee_per_gas - block_env.base_fee_per_gas,
)
effective_gas_price = priority_fee_per_gas + block_env.base_fee_per_gas
max_gas_fee = tx.gas * tx.max_fee_per_gas
else:
if tx.gas_price < block_env.base_fee_per_gas:
raise InvalidBlock
effective_gas_price = tx.gas_price
max_gas_fee = tx.gas * tx.gas_price

# Monad: the sender covers the gas fees the transaction is charged, at
# the effective gas price, and not its maximum fee.
gas_fee = tx.gas * effective_gas_price

if isinstance(tx, BlobTransaction):
if len(tx.blob_versioned_hashes) == 0:
Expand All @@ -517,9 +519,7 @@ def check_transaction(
"insufficient max fee per blob gas"
)

max_gas_fee += Uint(calculate_total_blob_gas(tx)) * Uint(
tx.max_fee_per_blob_gas
)
gas_fee += calculate_data_fee(block_env.excess_blob_gas, tx)
blob_versioned_hashes = tx.blob_versioned_hashes
else:
blob_versioned_hashes = ()
Expand All @@ -536,7 +536,9 @@ def check_transaction(
raise NonceMismatchError("nonce too low")
elif sender_account.nonce < Uint(tx.nonce):
raise NonceMismatchError("nonce too high")
if Uint(sender_account.balance) < max_gas_fee + Uint(tx.value):
# Monad: the value is not part of the inclusion check. A value the
# sender cannot cover fails the top-level frame at execution time.
if Uint(sender_account.balance) < gas_fee:
raise InsufficientBalanceError("insufficient sender balance")
sender_code = get_code(tx_state, sender_account.code_hash)
if sender_account.code_hash != EMPTY_CODE_HASH and not is_valid_delegation(
Expand Down
7 changes: 4 additions & 3 deletions src/ethereum/forks/monad_eight/state_tracker.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,10 @@ class BlockState:
)
code_writes: Dict[Hash32, Bytes] = field(default_factory=dict)
storage_clears: Set[Address] = field(default_factory=set)
# Monad: per-block-number set of EOAs that sent or were authorized in
# that block. Carried across blocks via t8n; used by the reserve
# balance exception window (RESERVE_BALANCE_DELAY_BLOCKS lookback).
# Monad: per-block-number set of EOAs that sent a transaction or
# signed an authorization, applied or not, in that block. Carried
# across blocks via t8n; used by the reserve balance exception window
# (RESERVE_BALANCE_DELAY_BLOCKS lookback).
_senders_authorities: Dict[Uint, Set[Address]] = field(
default_factory=dict
)
Expand Down
18 changes: 9 additions & 9 deletions src/ethereum/forks/monad_eight/vm/eoa_delegation.py
Original file line number Diff line number Diff line change
Expand Up @@ -174,15 +174,19 @@ def set_delegation(message: Message) -> U256:
tx_state = message.tx_env.state
refund_counter = U256(0)
for auth in message.tx_env.authorizations:
if auth.chain_id not in (message.block_env.chain_id, U256(0)):
try:
authority = recover_authority(auth)
except InvalidSignatureError:
continue

if auth.nonce >= U64.MAX_VALUE:
add_sender_authority(
message.block_env.state, message.block_env.number, authority
)

if auth.chain_id not in (message.block_env.chain_id, U256(0)):
continue

try:
authority = recover_authority(auth)
except InvalidSignatureError:
if auth.nonce >= U64.MAX_VALUE:
continue

message.accessed_addresses.add(authority)
Expand Down Expand Up @@ -211,10 +215,6 @@ def set_delegation(message: Message) -> U256:

increment_nonce(tx_state, authority)

add_sender_authority(
message.block_env.state, message.block_env.number, authority
)

if message.code_address is None:
raise InvalidBlock("Invalid type 4 transaction: no target")

Expand Down
13 changes: 13 additions & 0 deletions src/ethereum/forks/monad_eight/vm/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,19 @@ class RevertOnReserveBalance(EthereumException):
pass


class InsufficientBalanceForValue(EthereumException):
"""
Raised when the caller of the top-level frame cannot cover the
value it sends.

Monad includes a transaction once the sender's balance covers its
gas fees, so the value is checked on entering the frame. The frame
fails without consuming gas.
"""

pass


class StackUnderflowError(ExceptionalHalt):
"""
Occurs when a pop is executed on an empty stack.
Expand Down
7 changes: 7 additions & 0 deletions src/ethereum/forks/monad_eight/vm/interpreter.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
from .exceptions import (
AddressCollision,
ExceptionalHalt,
InsufficientBalanceForValue,
InvalidContractPrefix,
InvalidOpcode,
OutOfGasError,
Expand Down Expand Up @@ -277,6 +278,12 @@ def process_message(message: Message) -> Evm:
snapshot = copy_tx_state(tx_state)

if message.should_transfer_value and message.value != 0:
# Only the top-level frame can be short here: the call opcodes
# check the caller's balance before entering a frame, and the
# transaction is includable once its gas fees are covered.
if get_account(tx_state, message.caller).balance < message.value:
evm.error = InsufficientBalanceForValue()
return evm
move_ether(
tx_state,
message.caller,
Expand Down
16 changes: 9 additions & 7 deletions src/ethereum/forks/monad_eleven/fork.py
Original file line number Diff line number Diff line change
Expand Up @@ -447,7 +447,7 @@ def check_transaction(
NonceMismatchError :
If the nonce of the transaction is not equal to the sender's nonce.
InsufficientBalanceError :
If the sender's balance is not enough to pay for the transaction.
If the sender's balance is not enough to pay for the gas fees.
InvalidSenderError :
If the transaction is from an address that does not exist anymore.
PriorityFeeGreaterThanMaxFeeError :
Expand Down Expand Up @@ -510,12 +510,14 @@ def check_transaction(
tx.max_fee_per_gas - block_env.base_fee_per_gas,
)
effective_gas_price = priority_fee_per_gas + block_env.base_fee_per_gas
max_gas_fee = tx.gas * tx.max_fee_per_gas
else:
if tx.gas_price < block_env.base_fee_per_gas:
raise InvalidBlock
effective_gas_price = tx.gas_price
max_gas_fee = tx.gas * tx.gas_price

# Monad: the sender covers the gas fees the transaction is charged, at
# the effective gas price, and not its maximum fee.
gas_fee = tx.gas * effective_gas_price

if isinstance(tx, BlobTransaction):
blob_count = len(tx.blob_versioned_hashes)
Expand All @@ -537,9 +539,7 @@ def check_transaction(
"insufficient max fee per blob gas"
)

max_gas_fee += Uint(calculate_total_blob_gas(tx)) * Uint(
tx.max_fee_per_blob_gas
)
gas_fee += calculate_data_fee(block_env.excess_blob_gas, tx)
blob_versioned_hashes = tx.blob_versioned_hashes
else:
blob_versioned_hashes = ()
Expand All @@ -556,7 +556,9 @@ def check_transaction(
raise NonceMismatchError("nonce too low")
elif sender_account.nonce < Uint(tx.nonce):
raise NonceMismatchError("nonce too high")
if Uint(sender_account.balance) < max_gas_fee + Uint(tx.value):
# Monad: the value is not part of the inclusion check. A value the
# sender cannot cover fails the top-level frame at execution time.
if Uint(sender_account.balance) < gas_fee:
raise InsufficientBalanceError("insufficient sender balance")
sender_code = get_code(tx_state, sender_account.code_hash)
if sender_account.code_hash != EMPTY_CODE_HASH and not is_valid_delegation(
Expand Down
7 changes: 4 additions & 3 deletions src/ethereum/forks/monad_eleven/state_tracker.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,10 @@ class BlockState:
)
code_writes: Dict[Hash32, Bytes] = field(default_factory=dict)
storage_clears: Set[Address] = field(default_factory=set)
# Monad: per-block-number set of EOAs that sent or were authorized in
# that block. Carried across blocks via t8n; used by the reserve
# balance exception window (RESERVE_BALANCE_DELAY_BLOCKS lookback).
# Monad: per-block-number set of EOAs that sent a transaction or
# signed an authorization, applied or not, in that block. Carried
# across blocks via t8n; used by the reserve balance exception window
# (RESERVE_BALANCE_DELAY_BLOCKS lookback).
_senders_authorities: Dict[Uint, Set[Address]] = field(
default_factory=dict
)
Expand Down
18 changes: 9 additions & 9 deletions src/ethereum/forks/monad_eleven/vm/eoa_delegation.py
Original file line number Diff line number Diff line change
Expand Up @@ -174,15 +174,19 @@ def set_delegation(message: Message) -> U256:
tx_state = message.tx_env.state
refund_counter = U256(0)
for auth in message.tx_env.authorizations:
if auth.chain_id not in (message.block_env.chain_id, U256(0)):
try:
authority = recover_authority(auth)
except InvalidSignatureError:
continue

if auth.nonce >= U64.MAX_VALUE:
add_sender_authority(
message.block_env.state, message.block_env.number, authority
)

if auth.chain_id not in (message.block_env.chain_id, U256(0)):
continue

try:
authority = recover_authority(auth)
except InvalidSignatureError:
if auth.nonce >= U64.MAX_VALUE:
continue

message.accessed_addresses.add(authority)
Expand Down Expand Up @@ -211,10 +215,6 @@ def set_delegation(message: Message) -> U256:

increment_nonce(tx_state, authority)

add_sender_authority(
message.block_env.state, message.block_env.number, authority
)

if message.code_address is None:
raise InvalidBlock("Invalid type 4 transaction: no target")

Expand Down
13 changes: 13 additions & 0 deletions src/ethereum/forks/monad_eleven/vm/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,19 @@ class RevertOnReserveBalance(EthereumException):
pass


class InsufficientBalanceForValue(EthereumException):
"""
Raised when the caller of the top-level frame cannot cover the
value it sends.

Monad includes a transaction once the sender's balance covers its
gas fees, so the value is checked on entering the frame. The frame
fails without consuming gas.
"""

pass


class StackUnderflowError(ExceptionalHalt):
"""
Occurs when a pop is executed on an empty stack.
Expand Down
7 changes: 7 additions & 0 deletions src/ethereum/forks/monad_eleven/vm/interpreter.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
from .exceptions import (
AddressCollision,
ExceptionalHalt,
InsufficientBalanceForValue,
InvalidContractPrefix,
InvalidOpcode,
OutOfGasError,
Expand Down Expand Up @@ -404,6 +405,12 @@ def process_message(message: Message) -> Evm:
message.tx_env.tx_snapshot = snapshot

if message.should_transfer_value and message.value != 0:
# Only the top-level frame can be short here: the call opcodes
# check the caller's balance before entering a frame, and the
# transaction is includable once its gas fees are covered.
if get_account(tx_state, message.caller).balance < message.value:
evm.error = InsufficientBalanceForValue()
return evm
move_ether(
tx_state,
message.caller,
Expand Down
16 changes: 9 additions & 7 deletions src/ethereum/forks/monad_nine/fork.py
Original file line number Diff line number Diff line change
Expand Up @@ -444,7 +444,7 @@ def check_transaction(
NonceMismatchError :
If the nonce of the transaction is not equal to the sender's nonce.
InsufficientBalanceError :
If the sender's balance is not enough to pay for the transaction.
If the sender's balance is not enough to pay for the gas fees.
InvalidSenderError :
If the transaction is from an address that does not exist anymore.
PriorityFeeGreaterThanMaxFeeError :
Expand Down Expand Up @@ -507,12 +507,14 @@ def check_transaction(
tx.max_fee_per_gas - block_env.base_fee_per_gas,
)
effective_gas_price = priority_fee_per_gas + block_env.base_fee_per_gas
max_gas_fee = tx.gas * tx.max_fee_per_gas
else:
if tx.gas_price < block_env.base_fee_per_gas:
raise InvalidBlock
effective_gas_price = tx.gas_price
max_gas_fee = tx.gas * tx.gas_price

# Monad: the sender covers the gas fees the transaction is charged, at
# the effective gas price, and not its maximum fee.
gas_fee = tx.gas * effective_gas_price

if isinstance(tx, BlobTransaction):
blob_count = len(tx.blob_versioned_hashes)
Expand All @@ -534,9 +536,7 @@ def check_transaction(
"insufficient max fee per blob gas"
)

max_gas_fee += Uint(calculate_total_blob_gas(tx)) * Uint(
tx.max_fee_per_blob_gas
)
gas_fee += calculate_data_fee(block_env.excess_blob_gas, tx)
blob_versioned_hashes = tx.blob_versioned_hashes
else:
blob_versioned_hashes = ()
Expand All @@ -553,7 +553,9 @@ def check_transaction(
raise NonceMismatchError("nonce too low")
elif sender_account.nonce < Uint(tx.nonce):
raise NonceMismatchError("nonce too high")
if Uint(sender_account.balance) < max_gas_fee + Uint(tx.value):
# Monad: the value is not part of the inclusion check. A value the
# sender cannot cover fails the top-level frame at execution time.
if Uint(sender_account.balance) < gas_fee:
raise InsufficientBalanceError("insufficient sender balance")
sender_code = get_code(tx_state, sender_account.code_hash)
if sender_account.code_hash != EMPTY_CODE_HASH and not is_valid_delegation(
Expand Down
7 changes: 4 additions & 3 deletions src/ethereum/forks/monad_nine/state_tracker.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,10 @@ class BlockState:
)
code_writes: Dict[Hash32, Bytes] = field(default_factory=dict)
storage_clears: Set[Address] = field(default_factory=set)
# Monad: per-block-number set of EOAs that sent or were authorized in
# that block. Carried across blocks via t8n; used by the reserve
# balance exception window (RESERVE_BALANCE_DELAY_BLOCKS lookback).
# Monad: per-block-number set of EOAs that sent a transaction or
# signed an authorization, applied or not, in that block. Carried
# across blocks via t8n; used by the reserve balance exception window
# (RESERVE_BALANCE_DELAY_BLOCKS lookback).
_senders_authorities: Dict[Uint, Set[Address]] = field(
default_factory=dict
)
Expand Down
18 changes: 9 additions & 9 deletions src/ethereum/forks/monad_nine/vm/eoa_delegation.py
Original file line number Diff line number Diff line change
Expand Up @@ -174,15 +174,19 @@ def set_delegation(message: Message) -> U256:
tx_state = message.tx_env.state
refund_counter = U256(0)
for auth in message.tx_env.authorizations:
if auth.chain_id not in (message.block_env.chain_id, U256(0)):
try:
authority = recover_authority(auth)
except InvalidSignatureError:
continue

if auth.nonce >= U64.MAX_VALUE:
add_sender_authority(
message.block_env.state, message.block_env.number, authority
)

if auth.chain_id not in (message.block_env.chain_id, U256(0)):
continue

try:
authority = recover_authority(auth)
except InvalidSignatureError:
if auth.nonce >= U64.MAX_VALUE:
continue

message.accessed_addresses.add(authority)
Expand Down Expand Up @@ -211,10 +215,6 @@ def set_delegation(message: Message) -> U256:

increment_nonce(tx_state, authority)

add_sender_authority(
message.block_env.state, message.block_env.number, authority
)

if message.code_address is None:
raise InvalidBlock("Invalid type 4 transaction: no target")

Expand Down
13 changes: 13 additions & 0 deletions src/ethereum/forks/monad_nine/vm/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,19 @@ class RevertOnReserveBalance(EthereumException):
pass


class InsufficientBalanceForValue(EthereumException):
"""
Raised when the caller of the top-level frame cannot cover the
value it sends.

Monad includes a transaction once the sender's balance covers its
gas fees, so the value is checked on entering the frame. The frame
fails without consuming gas.
"""

pass


class StackUnderflowError(ExceptionalHalt):
"""
Occurs when a pop is executed on an empty stack.
Expand Down
Loading