Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/ethereum_test_forks/base_fork.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""
Abstract base class for Ethereum forks
"""

from abc import ABC, ABCMeta, abstractmethod
from typing import Any, List, Mapping, Optional, Protocol, Type

Expand Down Expand Up @@ -55,7 +56,7 @@ def fork(cls, block_number: int = 0, timestamp: int = 0) -> str:
# Header information abstract methods
@classmethod
@abstractmethod
def header_base_fee_required(cls, block_number: int = 0, timestamp: int = 0) -> bool:
def header_base_fee_per_gas_required(cls, block_number: int = 0, timestamp: int = 0) -> bool:
"""
Returns true if the header must contain base fee
"""
Expand Down
5 changes: 3 additions & 2 deletions src/ethereum_test_forks/forks/forks.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""
All Ethereum fork class definitions.
"""

from typing import List, Mapping, Optional

from ..base_fork import BaseFork
Expand All @@ -20,7 +21,7 @@ def fork(cls, block_number: int = 0, timestamp: int = 0) -> str:
return cls.name()

@classmethod
def header_base_fee_required(cls, block_number: int = 0, timestamp: int = 0) -> bool:
def header_base_fee_per_gas_required(cls, block_number: int = 0, timestamp: int = 0) -> bool:
"""
At genesis, header must not contain base fee
"""
Expand Down Expand Up @@ -231,7 +232,7 @@ class London(Berlin):
"""

@classmethod
def header_base_fee_required(cls, block_number: int = 0, timestamp: int = 0) -> bool:
def header_base_fee_per_gas_required(cls, block_number: int = 0, timestamp: int = 0) -> bool:
"""
Base Fee is required starting from London.
"""
Expand Down
16 changes: 8 additions & 8 deletions src/ethereum_test_forks/tests/test_forks.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,8 @@ def test_transition_forks():
assert MergeToShanghaiAtTime15k.fork(0, 15_000) == "Shanghai"
assert MergeToShanghaiAtTime15k.fork() == "Shanghai"

assert BerlinToLondonAt5.header_base_fee_required(4, 0) is False
assert BerlinToLondonAt5.header_base_fee_required(5, 0) is True
assert BerlinToLondonAt5.header_base_fee_per_gas_required(4, 0) is False
assert BerlinToLondonAt5.header_base_fee_per_gas_required(5, 0) is True

assert MergeToShanghaiAtTime15k.header_withdrawals_required(0, 14_999) is False
assert MergeToShanghaiAtTime15k.header_withdrawals_required(0, 15_000) is True
Expand Down Expand Up @@ -81,15 +81,15 @@ def test_forks():
assert f"{MergeToShanghaiAtTime15k}" == "MergeToShanghaiAtTime15k"

# Test some fork properties
assert Berlin.header_base_fee_required(0, 0) is False
assert London.header_base_fee_required(0, 0) is True
assert Merge.header_base_fee_required(0, 0) is True
assert Berlin.header_base_fee_per_gas_required(0, 0) is False
assert London.header_base_fee_per_gas_required(0, 0) is True
assert Merge.header_base_fee_per_gas_required(0, 0) is True
# Default values of normal forks if the genesis block
assert Merge.header_base_fee_required() is True
assert Merge.header_base_fee_per_gas_required() is True

# Transition forks too
assert cast(Fork, BerlinToLondonAt5).header_base_fee_required(4, 0) is False
assert cast(Fork, BerlinToLondonAt5).header_base_fee_required(5, 0) is True
assert cast(Fork, BerlinToLondonAt5).header_base_fee_per_gas_required(4, 0) is False
assert cast(Fork, BerlinToLondonAt5).header_base_fee_per_gas_required(5, 0) is True
assert cast(Fork, MergeToShanghaiAtTime15k).header_withdrawals_required(0, 14_999) is False
assert cast(Fork, MergeToShanghaiAtTime15k).header_withdrawals_required(0, 15_000) is True
assert cast(Fork, MergeToShanghaiAtTime15k).header_withdrawals_required() is True
Expand Down
97 changes: 37 additions & 60 deletions src/ethereum_test_tools/common/types.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""
Useful types for generating Ethereum tests.
"""

from copy import copy, deepcopy
from dataclasses import dataclass, fields, replace
from itertools import count
Expand Down Expand Up @@ -874,7 +875,7 @@ def from_withdrawal(cls, w: Withdrawal) -> "FixtureWithdrawal":
return cls(**kwargs)


DEFAULT_BASE_FEE = 7
DEFAULT_BASE_FEE_PER_GAS = 7


@dataclass(kw_only=True)
Expand Down Expand Up @@ -949,7 +950,7 @@ class Environment:
to_json=True,
),
)
base_fee: Optional[NumberConvertible] = field(
base_fee_per_gas: Optional[NumberConvertible] = field(
default=None,
json_encoder=JSONEncoder.Field(
name="currentBaseFee",
Expand All @@ -970,7 +971,7 @@ class Environment:
cast_type=Number,
),
)
parent_base_fee: Optional[NumberConvertible] = field(
parent_base_fee_per_gas: Optional[NumberConvertible] = field(
default=None,
json_encoder=JSONEncoder.Field(
name="parentBaseFee",
Expand Down Expand Up @@ -1048,13 +1049,15 @@ def from_parent_header(parent: "FixtureHeader") -> "Environment":
return Environment(
parent_difficulty=parent.difficulty,
parent_timestamp=parent.timestamp,
parent_base_fee=parent.base_fee,
parent_base_fee_per_gas=parent.base_fee_per_gas,
parent_blob_gas_used=parent.blob_gas_used,
parent_excess_blob_gas=parent.excess_blob_gas,
parent_gas_used=parent.gas_used,
parent_gas_limit=parent.gas_limit,
parent_ommers_hash=parent.ommers_hash,
block_hashes={parent.number: parent.hash if parent.hash is not None else 0},
block_hashes={
parent.number: parent.block_hash if parent.block_hash is not None else 0
},
)

def parent_hash(self) -> bytes:
Expand All @@ -1075,13 +1078,15 @@ def apply_new_parent(self, new_parent: "FixtureHeader") -> "Environment":
env = copy(self)
env.parent_difficulty = new_parent.difficulty
env.parent_timestamp = new_parent.timestamp
env.parent_base_fee = new_parent.base_fee
env.parent_base_fee_per_gas = new_parent.base_fee_per_gas
env.parent_blob_gas_used = new_parent.blob_gas_used
env.parent_excess_blob_gas = new_parent.excess_blob_gas
env.parent_gas_used = new_parent.gas_used
env.parent_gas_limit = new_parent.gas_limit
env.parent_ommers_hash = new_parent.ommers_hash
env.block_hashes[new_parent.number] = new_parent.hash if new_parent.hash is not None else 0
env.block_hashes[new_parent.number] = (
new_parent.block_hash if new_parent.block_hash is not None else 0
)
return env

def set_fork_requirements(self, fork: Fork, in_place: bool = False) -> "Environment":
Expand All @@ -1098,11 +1103,11 @@ def set_fork_requirements(self, fork: Fork, in_place: bool = False) -> "Environm
res.withdrawals = []

if (
fork.header_base_fee_required(number, timestamp)
and res.base_fee is None
and res.parent_base_fee is None
fork.header_base_fee_per_gas_required(number, timestamp)
and res.base_fee_per_gas is None
and res.parent_base_fee_per_gas is None
):
res.base_fee = DEFAULT_BASE_FEE
res.base_fee_per_gas = DEFAULT_BASE_FEE_PER_GAS

if fork.header_zero_difficulty_required(number, timestamp):
res.difficulty = 0
Expand Down Expand Up @@ -1919,22 +1924,22 @@ class Header:
coinbase: Optional[FixedSizeBytesConvertible] = None
state_root: Optional[FixedSizeBytesConvertible] = None
transactions_root: Optional[FixedSizeBytesConvertible] = None
receipt_root: Optional[FixedSizeBytesConvertible] = None
bloom: Optional[FixedSizeBytesConvertible] = None
receipts_root: Optional[FixedSizeBytesConvertible] = None
logs_bloom: Optional[FixedSizeBytesConvertible] = None
difficulty: Optional[NumberConvertible] = None
number: Optional[NumberConvertible] = None
gas_limit: Optional[NumberConvertible] = None
gas_used: Optional[NumberConvertible] = None
timestamp: Optional[NumberConvertible] = None
extra_data: Optional[BytesConvertible] = None
mix_digest: Optional[FixedSizeBytesConvertible] = None
prev_randao: Optional[FixedSizeBytesConvertible] = None
nonce: Optional[FixedSizeBytesConvertible] = None
base_fee: Optional[NumberConvertible | Removable] = None
base_fee_per_gas: Optional[NumberConvertible | Removable] = None
withdrawals_root: Optional[FixedSizeBytesConvertible | Removable] = None
blob_gas_used: Optional[NumberConvertible | Removable] = None
excess_blob_gas: Optional[NumberConvertible | Removable] = None
beacon_root: Optional[FixedSizeBytesConvertible | Removable] = None
hash: Optional[FixedSizeBytesConvertible] = None
block_hash: Optional[FixedSizeBytesConvertible] = None

REMOVE_FIELD: ClassVar[Removable] = Removable()
"""
Expand Down Expand Up @@ -2085,14 +2090,14 @@ class FixtureHeader:
),
json_encoder=JSONEncoder.Field(name="transactionsTrie"),
)
receipt_root: Hash = header_field(
receipts_root: Hash = header_field(
source=HeaderFieldSource(
parse_type=Hash,
source_transition_tool="receiptsRoot",
),
json_encoder=JSONEncoder.Field(name="receiptTrie"),
)
bloom: Bloom = header_field(
logs_bloom: Bloom = header_field(
source=HeaderFieldSource(
parse_type=Bloom,
source_transition_tool="logsBloom",
Expand Down Expand Up @@ -2144,7 +2149,7 @@ class FixtureHeader:
),
json_encoder=JSONEncoder.Field(name="extraData"),
)
mix_digest: Hash = header_field(
prev_randao: Hash = header_field(
source=HeaderFieldSource(
parse_type=Hash,
source_environment="prev_randao",
Expand All @@ -2159,13 +2164,13 @@ class FixtureHeader:
),
json_encoder=JSONEncoder.Field(),
)
base_fee: Optional[int] = header_field(
base_fee_per_gas: Optional[int] = header_field(
default=None,
source=HeaderFieldSource(
parse_type=Number,
fork_requirement_check="header_base_fee_required",
fork_requirement_check="header_base_fee_per_gas_required",
source_transition_tool="currentBaseFee",
source_environment="base_fee",
source_environment="base_fee_per_gas",
),
json_encoder=JSONEncoder.Field(name="baseFeePerGas", cast_type=ZeroPaddedHexNumber),
)
Expand Down Expand Up @@ -2205,7 +2210,7 @@ class FixtureHeader:
),
json_encoder=JSONEncoder.Field(name="parentBeaconBlockRoot"),
)
hash: Optional[Hash] = header_field(
block_hash: Optional[Hash] = header_field(
default=None,
source=HeaderFieldSource(
required=False,
Expand Down Expand Up @@ -2314,19 +2319,19 @@ def build(
self.coinbase,
self.state_root,
self.transactions_root,
self.receipt_root,
self.bloom,
self.receipts_root,
self.logs_bloom,
Uint(int(self.difficulty)),
Uint(int(self.number)),
Uint(int(self.gas_limit)),
Uint(int(self.gas_used)),
Uint(int(self.timestamp)),
self.extra_data,
self.mix_digest,
self.prev_randao,
self.nonce,
]
if self.base_fee is not None:
header.append(Uint(int(self.base_fee)))
if self.base_fee_per_gas is not None:
header.append(Uint(int(self.base_fee_per_gas)))
if self.withdrawals_root is not None:
header.append(self.withdrawals_root)
if self.blob_gas_used is not None:
Expand Down Expand Up @@ -2413,8 +2418,8 @@ def set_environment(self, env: Environment) -> Environment:
new_env.gas_limit = (
self.gas_limit if self.gas_limit is not None else environment_default.gas_limit
)
if not isinstance(self.base_fee, Removable):
new_env.base_fee = self.base_fee
if not isinstance(self.base_fee_per_gas, Removable):
new_env.base_fee_per_gas = self.base_fee_per_gas
new_env.withdrawals = self.withdrawals
if not isinstance(self.excess_blob_gas, Removable):
new_env.excess_blob_gas = self.excess_blob_gas
Expand Down Expand Up @@ -2486,34 +2491,6 @@ class FixtureExecutionPayload(FixtureHeader):
),
)

# Fields with different names
coinbase: Address = field(
json_encoder=JSONEncoder.Field(
name="feeRecipient",
)
)
receipt_root: Hash = field(
json_encoder=JSONEncoder.Field(
name="receiptsRoot",
),
)
bloom: Bloom = field(
json_encoder=JSONEncoder.Field(
name="logsBloom",
)
)
mix_digest: Hash = field(
json_encoder=JSONEncoder.Field(
name="prevRandao",
),
)
hash: Optional[Hash] = field(
default=None,
json_encoder=JSONEncoder.Field(
name="blockHash",
),
)

# Fields with different formatting
number: int = field(
json_encoder=JSONEncoder.Field(
Expand All @@ -2524,7 +2501,7 @@ class FixtureExecutionPayload(FixtureHeader):
gas_limit: int = field(json_encoder=JSONEncoder.Field(name="gasLimit", cast_type=HexNumber))
gas_used: int = field(json_encoder=JSONEncoder.Field(name="gasUsed", cast_type=HexNumber))
timestamp: int = field(json_encoder=JSONEncoder.Field(cast_type=HexNumber))
base_fee: Optional[int] = field(
base_fee_per_gas: Optional[int] = field(
default=None,
json_encoder=JSONEncoder.Field(name="baseFeePerGas", cast_type=HexNumber),
)
Expand Down Expand Up @@ -2694,7 +2671,7 @@ def _withdrawals_encoder(withdrawals: List[Withdrawal]) -> List[FixtureWithdrawa
cast_type=Number,
),
)
txs: Optional[List[Transaction]] = field(
transactions: Optional[List[Transaction]] = field(
default=None,
json_encoder=JSONEncoder.Field(
name="transactions",
Expand Down
12 changes: 12 additions & 0 deletions src/ethereum_test_tools/consume/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
"""
Consume methods and types used for EEST based test runners (consumers).
"""

from .engine_rpc import EngineRPC
from .eth_rpc import BlockNumberType, EthRPC

__all__ = (
"BlockNumberType",
"EthRPC",
"EngineRPC",
)
48 changes: 48 additions & 0 deletions src/ethereum_test_tools/consume/base_rpc.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
"""
Base JSON-RPC class and helper functions for EEST based hive simulators.
"""

import time

import jwt
Copy link
Collaborator

Choose a reason for hiding this comment

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

We need to add PyJWT==2.8.0 (is this the right one?) to setup.cfg.

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 could not even get it to work lol. How do you set HIVE_SIMULATOR in the vscode config?
I wonder is we should set this automatically, right now its manual:

set -x HIVE_SIMULATOR http://127.0.0.1:3000

I think one problem right now is on the src/pytest_plugins/consume/consume.py file with the pytest_configure method, because I don't think this file should try to generate the test cases, and instead this functionality should be moved to a custom pytest_generate_tests in each plugin, consume_direct, consume_via_rlp and consume_via_engine_api.

Nice! This sounds like the move forward - I think currently its this way as we can apply each test case for every consume method.

import requests

# from jwt import encode


class BaseRPC:
"""
Represents a base RPC class for every RPC call used within EEST based hive simulators.
"""

def __init__(self, client):
self.client = client
self.url = f"http://{client.ip}:8551"
self.jwt_secret = (
b"secretsecretsecretsecretsecretse" # oh wow, guess its not a secret anymore
)

def generate_jwt_token(self):
"""
Generates a JWT token based on the issued at timestamp and JWT secret.
"""
iat = int(time.time())
return jwt.encode({"iat": iat}, self.jwt_secret, algorithm="HS256")

def post_request(self, method, params):
"""
Sends a JSON-RPC POST request to the client RPC server at port 8551.
"""
payload = {
"jsonrpc": "2.0",
"method": method,
"params": params,
"id": 1,
}
headers = {
"Content-Type": "application/json",
"Authorization": f"Bearer {self.generate_jwt_token()}",
}
response = requests.post(self.url, json=payload, headers=headers)
response.raise_for_status()
return response.json().get("result")
Loading