Skip to content

Commit

Permalink
Backport changes from prague (#1113)
Browse files Browse the repository at this point in the history
* enable testing for all forks

* catch invalid signatures

* refactor validate_transaction

* define accessed_addresses at the start

* update variable name to BLOB_BASE_FEE_UPDATE_FRACTION

* refund_counter definition

* create encode_receipt function

* create process_system_transaction

* update gas calc

* explicitly define code_address in call*

* fix decode_receipt
  • Loading branch information
gurukamath authored Feb 11, 2025
1 parent 8b6b2e0 commit 2c86028
Show file tree
Hide file tree
Showing 122 changed files with 1,023 additions and 618 deletions.
31 changes: 30 additions & 1 deletion src/ethereum/arrow_glacier/blocks.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,19 @@
from dataclasses import dataclass
from typing import Tuple, Union

from ethereum_rlp import rlp
from ethereum_types.bytes import Bytes, Bytes8, Bytes32
from ethereum_types.frozen import slotted_freezable
from ethereum_types.numeric import U256, Uint

from ..crypto.hash import Hash32
from .fork_types import Address, Bloom, Root
from .transactions import LegacyTransaction
from .transactions import (
AccessListTransaction,
FeeMarketTransaction,
LegacyTransaction,
Transaction,
)


@slotted_freezable
Expand Down Expand Up @@ -80,3 +86,26 @@ class Receipt:
cumulative_gas_used: Uint
bloom: Bloom
logs: Tuple[Log, ...]


def encode_receipt(tx: Transaction, receipt: Receipt) -> Union[Bytes, Receipt]:
"""
Encodes a receipt.
"""
if isinstance(tx, AccessListTransaction):
return b"\x01" + rlp.encode(receipt)
elif isinstance(tx, FeeMarketTransaction):
return b"\x02" + rlp.encode(receipt)
else:
return receipt


def decode_receipt(receipt: Union[Bytes, Receipt]) -> Receipt:
"""
Decodes a receipt.
"""
if isinstance(receipt, Bytes):
assert receipt[0] in (1, 2)
return rlp.decode_to(Receipt, receipt[1:])
else:
return receipt
15 changes: 4 additions & 11 deletions src/ethereum/arrow_glacier/fork.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
from ethereum.exceptions import InvalidBlock, InvalidSenderError

from . import vm
from .blocks import Block, Header, Log, Receipt
from .blocks import Block, Header, Log, Receipt, encode_receipt
from .bloom import logs_bloom
from .fork_types import Address, Bloom, Root
from .state import (
Expand All @@ -42,7 +42,6 @@
FeeMarketTransaction,
LegacyTransaction,
Transaction,
calculate_intrinsic_cost,
decode_transaction,
encode_transaction,
recover_sender,
Expand Down Expand Up @@ -472,12 +471,7 @@ def make_receipt(
logs=logs,
)

if isinstance(tx, AccessListTransaction):
return b"\x01" + rlp.encode(receipt)
elif isinstance(tx, FeeMarketTransaction):
return b"\x02" + rlp.encode(receipt)
else:
return receipt
return encode_receipt(tx, receipt)


@dataclass
Expand Down Expand Up @@ -775,8 +769,7 @@ def process_transaction(
logs : `Tuple[ethereum.blocks.Log, ...]`
Logs generated during execution.
"""
if not validate_transaction(tx):
raise InvalidBlock
intrinsic_gas = validate_transaction(tx)

sender = env.origin
sender_account = get_account(env.state, sender)
Expand All @@ -795,7 +788,7 @@ def process_transaction(

effective_gas_fee = tx.gas * env.gas_price

gas = tx.gas - calculate_intrinsic_cost(tx)
gas = tx.gas - intrinsic_gas
increment_nonce(env.state, sender)

sender_balance_after_gas_fee = (
Expand Down
24 changes: 15 additions & 9 deletions src/ethereum/arrow_glacier/transactions.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

from ethereum.crypto.elliptic_curve import SECP256K1N, secp256k1_recover
from ethereum.crypto.hash import Hash32, keccak256
from ethereum.exceptions import InvalidSignatureError
from ethereum.exceptions import InvalidBlock, InvalidSignatureError

from .exceptions import TransactionTypeError
from .fork_types import Address
Expand Down Expand Up @@ -119,7 +119,7 @@ def decode_transaction(tx: Union[LegacyTransaction, Bytes]) -> Transaction:
return tx


def validate_transaction(tx: Transaction) -> bool:
def validate_transaction(tx: Transaction) -> Uint:
"""
Verifies a transaction.
Expand All @@ -141,14 +141,20 @@ def validate_transaction(tx: Transaction) -> bool:
Returns
-------
verified : `bool`
True if the transaction can be executed, or False otherwise.
intrinsic_gas : `ethereum.base_types.Uint`
The intrinsic cost of the transaction.
Raises
------
InvalidBlock :
If the transaction is not valid.
"""
if calculate_intrinsic_cost(tx) > Uint(tx.gas):
return False
if tx.nonce >= U256(U64.MAX_VALUE):
return False
return True
intrinsic_gas = calculate_intrinsic_cost(tx)
if intrinsic_gas > tx.gas:
raise InvalidBlock
if U256(tx.nonce) >= U256(U64.MAX_VALUE):
raise InvalidBlock
return intrinsic_gas


def calculate_intrinsic_cost(tx: Transaction) -> Uint:
Expand Down
8 changes: 4 additions & 4 deletions src/ethereum/arrow_glacier/utils/message.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,10 @@ def prepare_message(
message: `ethereum.arrow_glacier.vm.Message`
Items containing contract creation or message call specific data.
"""
accessed_addresses = set()
accessed_addresses.add(caller)
accessed_addresses.update(PRE_COMPILED_CONTRACTS.keys())
accessed_addresses.update(preaccessed_addresses)
if isinstance(target, Bytes0):
current_target = compute_contract_address(
caller,
Expand All @@ -92,11 +96,7 @@ def prepare_message(
else:
raise AssertionError("Target must be address or empty bytes")

accessed_addresses = set()
accessed_addresses.add(current_target)
accessed_addresses.add(caller)
accessed_addresses.update(PRE_COMPILED_CONTRACTS.keys())
accessed_addresses.update(preaccessed_addresses)

return Message(
caller=caller,
Expand Down
28 changes: 17 additions & 11 deletions src/ethereum/arrow_glacier/vm/instructions/environment.py
Original file line number Diff line number Diff line change
Expand Up @@ -340,15 +340,17 @@ def extcodesize(evm: Evm) -> None:

# GAS
if address in evm.accessed_addresses:
charge_gas(evm, GAS_WARM_ACCESS)
access_gas_cost = GAS_WARM_ACCESS
else:
evm.accessed_addresses.add(address)
charge_gas(evm, GAS_COLD_ACCOUNT_ACCESS)
access_gas_cost = GAS_COLD_ACCOUNT_ACCESS

charge_gas(evm, access_gas_cost)

# OPERATION
# Non-existent accounts default to EMPTY_ACCOUNT, which has empty code.
codesize = U256(len(get_account(evm.env.state, address).code))
code = get_account(evm.env.state, address).code

codesize = U256(len(code))
push(evm.stack, codesize)

# PROGRAM COUNTER
Expand Down Expand Up @@ -379,16 +381,17 @@ def extcodecopy(evm: Evm) -> None:
)

if address in evm.accessed_addresses:
charge_gas(evm, GAS_WARM_ACCESS + copy_gas_cost + extend_memory.cost)
access_gas_cost = GAS_WARM_ACCESS
else:
evm.accessed_addresses.add(address)
charge_gas(
evm, GAS_COLD_ACCOUNT_ACCESS + copy_gas_cost + extend_memory.cost
)
access_gas_cost = GAS_COLD_ACCOUNT_ACCESS

charge_gas(evm, access_gas_cost + copy_gas_cost + extend_memory.cost)

# OPERATION
evm.memory += b"\x00" * extend_memory.expand_by
code = get_account(evm.env.state, address).code

value = buffer_read(code, code_start_index, size)
memory_write(evm.memory, memory_start_index, value)

Expand Down Expand Up @@ -465,18 +468,21 @@ def extcodehash(evm: Evm) -> None:

# GAS
if address in evm.accessed_addresses:
charge_gas(evm, GAS_WARM_ACCESS)
access_gas_cost = GAS_WARM_ACCESS
else:
evm.accessed_addresses.add(address)
charge_gas(evm, GAS_COLD_ACCOUNT_ACCESS)
access_gas_cost = GAS_COLD_ACCOUNT_ACCESS

charge_gas(evm, access_gas_cost)

# OPERATION
account = get_account(evm.env.state, address)

if account == EMPTY_ACCOUNT:
codehash = U256(0)
else:
codehash = U256.from_be_bytes(keccak256(account.code))
code = account.code
codehash = U256.from_be_bytes(keccak256(code))

push(evm.stack, codehash)

Expand Down
8 changes: 6 additions & 2 deletions src/ethereum/arrow_glacier/vm/instructions/system.py
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,8 @@ def call(evm: Evm) -> None:
evm.accessed_addresses.add(to)
access_gas_cost = GAS_COLD_ACCOUNT_ACCESS

code_address = to

create_gas_cost = (
Uint(0)
if is_account_alive(evm.env.state, to) or value == 0
Expand Down Expand Up @@ -373,7 +375,7 @@ def call(evm: Evm) -> None:
value,
evm.message.current_target,
to,
to,
code_address,
True,
False,
memory_input_start_position,
Expand Down Expand Up @@ -605,6 +607,8 @@ def staticcall(evm: Evm) -> None:
evm.accessed_addresses.add(to)
access_gas_cost = GAS_COLD_ACCOUNT_ACCESS

code_address = to

message_call_gas = calculate_message_call_gas(
U256(0),
gas,
Expand All @@ -622,7 +626,7 @@ def staticcall(evm: Evm) -> None:
U256(0),
evm.message.current_target,
to,
to,
code_address,
True,
True,
memory_input_start_position,
Expand Down
4 changes: 2 additions & 2 deletions src/ethereum/arrow_glacier/vm/interpreter.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ def process_message_call(
output : `MessageCallOutput`
Output of the message call
"""
refund_counter = U256(0)
if message.target == Bytes0(b""):
is_collision = account_has_code_or_nonce(
env.state, message.current_target
Expand All @@ -126,12 +127,11 @@ def process_message_call(
logs: Tuple[Log, ...] = ()
accounts_to_delete = set()
touched_accounts = set()
refund_counter = U256(0)
else:
logs = evm.logs
accounts_to_delete = evm.accounts_to_delete
touched_accounts = evm.touched_accounts
refund_counter = U256(evm.refund_counter)
refund_counter += U256(evm.refund_counter)

tx_end = TransactionEnd(
int(message.gas) - int(evm.gas_left), evm.output, evm.error
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

from ethereum.crypto.elliptic_curve import SECP256K1N, secp256k1_recover
from ethereum.crypto.hash import Hash32, keccak256
from ethereum.exceptions import InvalidSignatureError
from ethereum.utils.byte import left_pad_zero_bytes

from ...vm import Evm
Expand Down Expand Up @@ -53,7 +54,7 @@ def ecrecover(evm: Evm) -> None:

try:
public_key = secp256k1_recover(r, s, v - U256(27), message_hash)
except ValueError:
except InvalidSignatureError:
# unable to extract public key
return

Expand Down
24 changes: 23 additions & 1 deletion src/ethereum/berlin/blocks.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,14 @@
from dataclasses import dataclass
from typing import Tuple, Union

from ethereum_rlp import rlp
from ethereum_types.bytes import Bytes, Bytes8, Bytes32
from ethereum_types.frozen import slotted_freezable
from ethereum_types.numeric import U256, Uint

from ..crypto.hash import Hash32
from .fork_types import Address, Bloom, Root
from .transactions import LegacyTransaction
from .transactions import AccessListTransaction, LegacyTransaction, Transaction


@slotted_freezable
Expand Down Expand Up @@ -79,3 +80,24 @@ class Receipt:
cumulative_gas_used: Uint
bloom: Bloom
logs: Tuple[Log, ...]


def encode_receipt(tx: Transaction, receipt: Receipt) -> Union[Bytes, Receipt]:
"""
Encodes a receipt.
"""
if isinstance(tx, AccessListTransaction):
return b"\x01" + rlp.encode(receipt)
else:
return receipt


def decode_receipt(receipt: Union[Bytes, Receipt]) -> Receipt:
"""
Decodes a receipt.
"""
if isinstance(receipt, Bytes):
assert receipt[0] == 1
return rlp.decode_to(Receipt, receipt[1:])
else:
return receipt
13 changes: 4 additions & 9 deletions src/ethereum/berlin/fork.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
from ethereum.exceptions import InvalidBlock, InvalidSenderError

from . import vm
from .blocks import Block, Header, Log, Receipt
from .blocks import Block, Header, Log, Receipt, encode_receipt
from .bloom import logs_bloom
from .fork_types import Address, Bloom, Root
from .state import (
Expand All @@ -41,7 +41,6 @@
AccessListTransaction,
LegacyTransaction,
Transaction,
calculate_intrinsic_cost,
decode_transaction,
encode_transaction,
recover_sender,
Expand Down Expand Up @@ -374,10 +373,7 @@ def make_receipt(
logs=logs,
)

if isinstance(tx, AccessListTransaction):
return b"\x01" + rlp.encode(receipt)
else:
return receipt
return encode_receipt(tx, receipt)


@dataclass
Expand Down Expand Up @@ -669,8 +665,7 @@ def process_transaction(
logs : `Tuple[ethereum.blocks.Log, ...]`
Logs generated during execution.
"""
if not validate_transaction(tx):
raise InvalidBlock
intrinsic_gas = validate_transaction(tx)

sender = env.origin
sender_account = get_account(env.state, sender)
Expand All @@ -682,7 +677,7 @@ def process_transaction(
if sender_account.code != bytearray():
raise InvalidSenderError("not EOA")

gas = tx.gas - calculate_intrinsic_cost(tx)
gas = tx.gas - intrinsic_gas
increment_nonce(env.state, sender)
sender_balance_after_gas_fee = Uint(sender_account.balance) - gas_fee
set_account_balance(env.state, sender, U256(sender_balance_after_gas_fee))
Expand Down
Loading

0 comments on commit 2c86028

Please sign in to comment.