Skip to content

Commit

Permalink
Changes from comments on PR ethereum#285:
Browse files Browse the repository at this point in the history
- Refactor block evm guessing logic into utility methods
- Use constants for transaction types for clarity
- Keep integer for transaction type internally, normalize to hex
  (as clients do) on the way out.
  • Loading branch information
fselmo committed Apr 5, 2024
1 parent b3155bd commit 61739e8
Show file tree
Hide file tree
Showing 7 changed files with 168 additions and 76 deletions.
8 changes: 7 additions & 1 deletion eth_tester/backends/mock/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@
from eth_tester.backends.mock.common import (
calculate_effective_gas_price,
)
from eth_tester.constants import (
LEGACY_TX_TYPE,
)
from eth_tester.utils.transactions import (
extract_transaction_type,
)
Expand Down Expand Up @@ -52,7 +55,10 @@ def serialize_full_transaction(transaction, block, transaction_index, is_pending
partial(assoc, key="type", value=extract_transaction_type(transaction)),
)

if int(serialized_transaction["type"], 16) > 0:
if int(serialized_transaction["type"], 16) > LEGACY_TX_TYPE:
# if the transaction is not a legacy (type=0) transaction, `y_parity` is
# the correct signature field but clients commonly return both `v` and
# `y_parity`.
serialized_transaction = assoc(
serialized_transaction,
"y_parity",
Expand Down
9 changes: 6 additions & 3 deletions eth_tester/backends/pyevm/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
merge_genesis_overrides,
)
from eth_tester.constants import (
BLOB_TX_TYPE,
DYNAMIC_FEE_TRANSACTION_PARAMS,
)
from eth_tester.exceptions import (
Expand Down Expand Up @@ -91,6 +92,7 @@
Revert as EVMRevert,
)
from eth.vm.forks import (
LATEST_VM,
CancunVM,
ParisVM,
ShanghaiVM,
Expand Down Expand Up @@ -240,7 +242,7 @@ def setup_tester_chain(
)

if vm_configuration is None:
vm_config = ((0, CancunVM),)
vm_config = ((0, LATEST_VM),)
else:
if len(vm_configuration) > 0:
_genesis_block_num, genesis_vm = vm_configuration[0]
Expand Down Expand Up @@ -751,8 +753,9 @@ def _create_type_aware_unsigned_transaction(self, normalized_txn):
def send_raw_transaction(self, raw_transaction):
vm = _get_vm_for_block_number(self.chain, "latest")

if raw_transaction[0] == 3:
# Blob transactions are handled differently
# first byte of rlp-encoded raw transaction indicates the transaction type
if raw_transaction[0] == BLOB_TX_TYPE:
# Blob transactions (type=3) are handled differently
blob_transaction = BlobTransaction.from_bytes(HexBytes(raw_transaction))

# Blob data is handled in consensus layer, not sent to execution layer.
Expand Down
109 changes: 60 additions & 49 deletions eth_tester/backends/pyevm/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
)

from .utils import (
is_cancun_block,
is_london_block,
is_shanghai_block,
is_supported_pyevm_version_available,
)

Expand All @@ -22,7 +25,11 @@
TypedTransaction = None

from eth_tester.constants import (
ACCESS_LIST_TX_TYPE,
BLOB_TX_TYPE,
DYNAMIC_FEE_TX_TYPE,
GAS_PER_BLOB,
LEGACY_TX_TYPE,
)
from eth_tester.exceptions import (
ValidationError,
Expand Down Expand Up @@ -76,25 +83,18 @@ def serialize_block(block, full_transaction, is_pending):
"uncles": [uncle.hash for uncle in block.uncles],
}

if hasattr(block.header, "base_fee_per_gas"):
# london
# london
if is_london_block(block):
base_fee = block.header.base_fee_per_gas
block_info.update({"base_fee_per_gas": base_fee})

if hasattr(block.header, "withdrawals_root"):
# shanghai
# shanghai
if is_shanghai_block(block):
block_info.update({"withdrawals": serialize_block_withdrawals(block)})
block_info.update({"withdrawals_root": block.header.withdrawals_root})

if all(
hasattr(block.header, cancun_attr)
for cancun_attr in (
"blob_gas_used",
"excess_blob_gas",
"parent_beacon_block_root",
)
):
# cancun
# cancun
if is_cancun_block(block):
block_info.update(
{"parent_beacon_block_root": block.header.parent_beacon_block_root}
)
Expand Down Expand Up @@ -124,45 +124,54 @@ def serialize_transaction(block, transaction, transaction_index, is_pending):
"gas": transaction.gas,
"data": transaction.data,
}
if int(txn_type, 16) in (0, 1):
type_specific_params = {"gas_price": transaction.gas_price}

if _field_in_transaction(transaction, "access_list"):
# access list transaction
type_specific_params = merge(
type_specific_params,
{
"chain_id": transaction.chain_id,
"access_list": transaction.access_list or (),
},
)
elif int(txn_type, 16) >= 2:
# dynamic fee transaction
type_specific_params = {}
if txn_type in (LEGACY_TX_TYPE, ACCESS_LIST_TX_TYPE):
type_specific_params = {
"chain_id": transaction.chain_id,
"max_fee_per_gas": transaction.max_fee_per_gas,
"max_priority_fee_per_gas": transaction.max_priority_fee_per_gas,
"access_list": transaction.access_list or (),
# TODO: Sometime in 2022 the inclusion of gas_price may be removed from
# dynamic fee transactions and we can get rid of this behavior.
# https://github.com/ethereum/execution-specs/pull/251
"gas_price": (
transaction.max_fee_per_gas
if is_pending
else _calculate_effective_gas_price(transaction, block, txn_type)
),
"gas_price": transaction.gas_price,
}
if int(txn_type, 16) == 3:
# blob transaction

if txn_type > LEGACY_TX_TYPE:
# handle access list transaction type and above
type_specific_params = merge(
type_specific_params,
{
"chain_id": transaction.chain_id,
"access_list": transaction.access_list or (),
},
)

if txn_type >= DYNAMIC_FEE_TX_TYPE:
# handle dynamic fee transaction type and above
type_specific_params = merge(
type_specific_params,
{
"max_fee_per_blob_gas": transaction.max_fee_per_blob_gas,
"blob_versioned_hashes": transaction.blob_versioned_hashes,
"max_fee_per_gas": transaction.max_fee_per_gas,
"max_priority_fee_per_gas": transaction.max_priority_fee_per_gas,
# TODO: Sometime in 2022 the inclusion of gas_price may be removed
# from dynamic fee transactions and we can get rid of this
# behavior. https://github.com/ethereum/execution-specs/pull/251
"gas_price": (
transaction.max_fee_per_gas
if is_pending
else _calculate_effective_gas_price(
transaction, block, txn_type
)
),
},
)
if txn_type == BLOB_TX_TYPE:
# handle blob transaction (txn_type=3)
type_specific_params = merge(
type_specific_params,
{
"max_fee_per_blob_gas": transaction.max_fee_per_blob_gas,
"blob_versioned_hashes": transaction.blob_versioned_hashes,
},
)
else:
raise ValidationError("Invariant: code path should be unreachable")
if txn_type != LEGACY_TX_TYPE:
raise ValidationError("Invariant: code path should be unreachable")

# the signature fields are commonly the last fields in a node's JSON-RPC response
signed_tx_params = {
Expand All @@ -174,7 +183,9 @@ def serialize_transaction(block, transaction, transaction_index, is_pending):
"s": transaction.s,
"r": transaction.r,
}
if txn_type != "0x0":
if txn_type > LEGACY_TX_TYPE:
# If anything other than a legacy tx, `y_parity` is the correct signature field.
# Clients commonly return both `v` and `y_parity` for these transactions.
signed_tx_params["y_parity"] = signed_tx_params["v"]

return merge(common_transaction_params, type_specific_params, signed_tx_params)
Expand Down Expand Up @@ -240,7 +251,7 @@ def serialize_transaction_receipt(
"type": _txn_type,
}

if int(_txn_type, 16) == 3:
if _txn_type == BLOB_TX_TYPE:
# blob transaction
blob_gas_used = GAS_PER_BLOB * len(transaction.blob_versioned_hashes)
receipt_fields["blob_gas_used"] = blob_gas_used
Expand All @@ -265,10 +276,10 @@ def serialize_log(block, transaction, transaction_index, log, log_index, is_pend

def _extract_transaction_type(transaction):
if isinstance(transaction, TypedTransaction):
return hex(transaction.type_id)
return transaction.type_id

# legacy transactions are now considered "0x0" type
return "0x0"
# legacy transactions are now considered type=0
return 0


def _calculate_effective_gas_price(transaction, block, transaction_type):
Expand All @@ -277,7 +288,7 @@ def _calculate_effective_gas_price(transaction, block, transaction_type):
transaction.max_fee_per_gas,
transaction.max_priority_fee_per_gas + block.header.base_fee_per_gas,
)
if int(transaction_type, 16) >= 2
if transaction_type >= 2
else transaction.gas_price
)

Expand Down
63 changes: 63 additions & 0 deletions eth_tester/backends/pyevm/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,15 @@
absolute_import,
)

from typing import (
Any,
Dict,
Union,
)

from eth.abc import (
BlockAPI,
)
import pkg_resources
from semantic_version import (
Version,
Expand All @@ -21,3 +30,57 @@ def get_pyevm_version():
def is_supported_pyevm_version_available():
version = get_pyevm_version()
return version and version >= Version("0.5.0")


# --- network utils --- #


def is_london_block(block: Union[Dict[str, Any], BlockAPI]) -> bool:
if isinstance(block, BlockAPI):
try:
# it's not enough to check hasattr because the attribute could be
# defined in earlier VM's while raising an AttributeError until implemented
return block.header.base_fee_per_gas is not None
except AttributeError:
return False

elif isinstance(block, dict) and "base_fee_per_gas" in block:
return True

return False


def is_shanghai_block(block: Union[Dict[str, Any], BlockAPI]) -> bool:
if isinstance(block, BlockAPI):
try:
# it's not enough to check hasattr because the attribute could be
# defined in earlier VM's while raising an AttributeError until implemented
return block.header.withdrawals_root is not None
except AttributeError:
return False
elif isinstance(block, dict) and "withdrawals_root" in block:
return True

return False


def is_cancun_block(block: Union[Dict[str, Any], BlockAPI]) -> bool:
if isinstance(block, BlockAPI):
try:
# it's not enough to check hasattr because the attribute could be
# defined in earlier VM's while raising an AttributeError until implemented
return block.header.parent_beacon_block_root is not None
except AttributeError:
return False

elif isinstance(block, dict) and all(
cancun_field in block
for cancun_field in (
"parent_beacon_block_root",
"blob_gas_used",
"excess_blob_gas",
)
):
return True

return False
13 changes: 10 additions & 3 deletions eth_tester/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,9 +74,16 @@
)
SECPK1_G = (SECPK1_Gx, SECPK1_Gy)

#
# EIP CONSTANTS
#

# -- KNOWN TRANSACTION TYPES -- #
LEGACY_TX_TYPE = 0
ACCESS_LIST_TX_TYPE = 1
DYNAMIC_FEE_TX_TYPE = 2
BLOB_TX_TYPE = 3


# -- EIP CONSTANTS -- #

# EIP-1559
DYNAMIC_FEE_TRANSACTION_PARAMS = ("max_fee_per_gas", "max_priority_fee_per_gas")

Expand Down
7 changes: 5 additions & 2 deletions eth_tester/normalization/outbound.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@
assoc,
dissoc,
)
from web3._utils.method_formatters import (
to_hex_if_integer,
)

from ..utils.encoding import (
int_to_32byte_big_endian,
Expand Down Expand Up @@ -56,7 +59,7 @@ def _normalize_outbound_access_list(access_list):


TRANSACTION_NORMALIZERS = {
"type": identity,
"type": to_hex_if_integer,
"blob_versioned_hashes": partial(
normalize_array,
normalizer=partial(
Expand Down Expand Up @@ -214,7 +217,7 @@ def _normalize_contract_address(receipt):
"state_root": identity,
"status": identity,
"to": to_empty_or_checksum_address,
"type": identity,
"type": to_hex_if_integer,
"base_fee_per_gas": identity,
"blob_gas_used": identity,
"blob_gas_price": identity,
Expand Down
Loading

0 comments on commit 61739e8

Please sign in to comment.