Skip to content

Commit

Permalink
forks: Add fork-specific, non-inherited class variables (#366)
Browse files Browse the repository at this point in the history
  • Loading branch information
marioevz authored Dec 21, 2023
1 parent 19a02ec commit 5ce00e7
Show file tree
Hide file tree
Showing 13 changed files with 122 additions and 42 deletions.
47 changes: 41 additions & 6 deletions src/ethereum_test_forks/base_fork.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
Abstract base class for Ethereum forks
"""
from abc import ABC, ABCMeta, abstractmethod
from typing import Any, List, Mapping, Optional, Protocol, Type
from typing import Any, ClassVar, List, Mapping, Optional, Protocol, Type

from .base_decorators import prefer_transition_to_method

Expand Down Expand Up @@ -68,13 +68,23 @@ class BaseFork(ABC, metaclass=BaseForkMeta):
Must contain all the methods used by every fork.
"""

@classmethod
@abstractmethod
def fork(cls, block_number: int = 0, timestamp: int = 0) -> str:
_transition_tool_name: ClassVar[Optional[str]] = None
_blockchain_test_network_name: ClassVar[Optional[str]] = None
_solc_name: ClassVar[Optional[str]] = None

def __init_subclass__(
cls,
*,
transition_tool_name: Optional[str] = None,
blockchain_test_network_name: Optional[str] = None,
solc_name: Optional[str] = None,
) -> None:
"""
Returns fork name as it's meant to be passed to the transition tool for execution.
Initializes the new fork with values that don't carry over to subclass forks.
"""
pass
cls._transition_tool_name = transition_tool_name
cls._blockchain_test_network_name = blockchain_test_network_name
cls._solc_name = solc_name

# Header information abstract methods
@classmethod
Expand Down Expand Up @@ -216,6 +226,31 @@ def name(cls) -> str:
"""
return cls.__name__

@classmethod
@abstractmethod
def transition_tool_name(cls, block_number: int = 0, timestamp: int = 0) -> str:
"""
Returns fork name as it's meant to be passed to the transition tool for execution.
"""
pass

@classmethod
@abstractmethod
def solc_name(cls, block_number: int = 0, timestamp: int = 0) -> str:
"""
Returns fork name as it's meant to be passed to the solc compiler.
"""
pass

@classmethod
def blockchain_test_network_name(cls) -> str:
"""
Returns the network configuration name to be used in BlockchainTests for this fork.
"""
if cls._blockchain_test_network_name is not None:
return cls._blockchain_test_network_name
return cls.name()

@classmethod
def is_deployed(cls) -> bool:
"""
Expand Down
22 changes: 19 additions & 3 deletions src/ethereum_test_forks/forks/forks.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,21 @@ class Frontier(BaseFork):
"""

@classmethod
def fork(cls, block_number: int = 0, timestamp: int = 0) -> str:
def transition_tool_name(cls, block_number: int = 0, timestamp: int = 0) -> str:
"""
Returns fork name as it's meant to be passed to the transition tool for execution.
"""
if cls._transition_tool_name is not None:
return cls._transition_tool_name
return cls.name()

@classmethod
def solc_name(cls, block_number: int = 0, timestamp: int = 0) -> str:
"""
Returns fork name as it's meant to be passed to the solc compiler.
"""
if cls._solc_name is not None:
return cls._solc_name
return cls.name()

@classmethod
Expand Down Expand Up @@ -182,7 +193,7 @@ def get_reward(cls, block_number: int = 0, timestamp: int = 0) -> int:
return 2_000_000_000_000_000_000


class ConstantinopleFix(Constantinople):
class ConstantinopleFix(Constantinople, solc_name="Constantinople"):
"""
Constantinople Fix fork
"""
Expand Down Expand Up @@ -262,7 +273,12 @@ class GrayGlacier(ArrowGlacier):
pass


class Merge(London):
class Merge(
London,
transition_tool_name="Merge",
blockchain_test_network_name="Merge",
solc_name="Paris",
):
"""
Merge fork
"""
Expand Down
2 changes: 1 addition & 1 deletion src/ethereum_test_forks/forks/transition.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ class BerlinToLondonAt5(Berlin):


@transition_fork(to_fork=Shanghai, at_timestamp=15_000)
class MergeToShanghaiAtTime15k(Merge):
class MergeToShanghaiAtTime15k(Merge, blockchain_test_network_name="MergeToShanghaiAtTime15k"):
"""
Merge to Shanghai transition at Timestamp 15k
"""
Expand Down
20 changes: 14 additions & 6 deletions src/ethereum_test_forks/tests/test_forks.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,14 +36,14 @@ def test_transition_forks():
assert BerlinToLondonAt5.transitions_to() == London
assert BerlinToLondonAt5.transitions_from() == Berlin

assert BerlinToLondonAt5.fork(4, 0) == "Berlin"
assert BerlinToLondonAt5.fork(5, 0) == "London"
assert BerlinToLondonAt5.transition_tool_name(4, 0) == "Berlin"
assert BerlinToLondonAt5.transition_tool_name(5, 0) == "London"
# Default values of transition forks is the transition block
assert BerlinToLondonAt5.fork() == "London"
assert BerlinToLondonAt5.transition_tool_name() == "London"

assert MergeToShanghaiAtTime15k.fork(0, 14_999) == "Merge"
assert MergeToShanghaiAtTime15k.fork(0, 15_000) == "Shanghai"
assert MergeToShanghaiAtTime15k.fork() == "Shanghai"
assert MergeToShanghaiAtTime15k.transition_tool_name(0, 14_999) == "Merge"
assert MergeToShanghaiAtTime15k.transition_tool_name(0, 15_000) == "Shanghai"
assert MergeToShanghaiAtTime15k.transition_tool_name() == "Shanghai"

assert BerlinToLondonAt5.header_base_fee_required(4, 0) is False
assert BerlinToLondonAt5.header_base_fee_required(5, 0) is True
Expand Down Expand Up @@ -79,6 +79,14 @@ def test_forks():
assert f"{London}" == "London"
assert f"{MergeToShanghaiAtTime15k}" == "MergeToShanghaiAtTime15k"

# Merge name will be changed to paris, but we need to check the inheriting fork name is still
# the default
assert Merge.transition_tool_name() == "Merge"
assert Shanghai.transition_tool_name() == "Shanghai"
assert Merge.blockchain_test_network_name() == "Merge"
assert Shanghai.blockchain_test_network_name() == "Shanghai"
assert MergeToShanghaiAtTime15k.blockchain_test_network_name() == "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
Expand Down
9 changes: 8 additions & 1 deletion src/ethereum_test_forks/transition_base_fork.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,14 @@ def decorator(cls) -> Type[TransitionBaseClass]:
from_fork = cls.__bases__[0]
assert issubclass(from_fork, BaseFork)

class NewTransitionClass(cls, TransitionBaseClass, BaseFork): # type: ignore
class NewTransitionClass(
cls, # type: ignore
TransitionBaseClass,
BaseFork,
transition_tool_name=cls._transition_tool_name,
blockchain_test_network_name=cls._blockchain_test_network_name,
solc_name=cls._solc_name,
):
pass

NewTransitionClass.name = lambda: transition_name # type: ignore
Expand Down
10 changes: 2 additions & 8 deletions src/ethereum_test_tools/code/yul.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from pathlib import Path
from shutil import which
from subprocess import PIPE, run
from typing import Mapping, Optional, Sized, SupportsBytes, Tuple, Type, Union
from typing import Optional, Sized, SupportsBytes, Tuple, Type, Union

from semver import Version

Expand All @@ -33,13 +33,7 @@ def get_evm_version_from_fork(fork: Fork | None):
"""
if not fork:
return None
fork_to_evm_version_map: Mapping[str, str] = {
"Merge": "paris",
"ConstantinopleFix": "constantinople",
}
if fork.name() in fork_to_evm_version_map:
return fork_to_evm_version_map[fork.name()]
return fork.name().lower()
return fork.solc_name().lower()


class Yul(SupportsBytes, Sized):
Expand Down
12 changes: 9 additions & 3 deletions src/ethereum_test_tools/spec/blockchain_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,9 @@ def generate_block_data(
alloc=previous_alloc,
txs=to_json(txs),
env=to_json(env),
fork_name=fork.fork(block_number=Number(env.number), timestamp=Number(env.timestamp)),
fork_name=fork.transition_tool_name(
block_number=Number(env.number), timestamp=Number(env.timestamp)
),
chain_id=self.chain_id,
reward=fork.get_reward(Number(env.number), Number(env.timestamp)),
eips=eips,
Expand Down Expand Up @@ -199,11 +201,15 @@ def generate_block_data(

return header, rlp, txs, next_alloc, env

def network_info(self, fork, eips=None):
def network_info(self, fork: Fork, eips: Optional[List[int]] = None):
"""
Returns fixture network information for the fork & EIP/s.
"""
return "+".join([fork.name()] + [str(eip) for eip in eips]) if eips else fork.name()
return (
"+".join([fork.blockchain_test_network_name()] + [str(eip) for eip in eips])
if eips
else fork.blockchain_test_network_name()
)

def verify_post_state(self, t8n, alloc):
"""
Expand Down
32 changes: 23 additions & 9 deletions src/ethereum_test_tools/spec/state_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,13 +122,19 @@ def make_genesis(

def generate_fixture_data(
self, t8n: TransitionTool, fork: Fork, eips: Optional[List[int]] = None
) -> Tuple[FixtureHeader, Bytes, Alloc, List[Transaction], Dict, Dict[str, Any], str]:
) -> Tuple[FixtureHeader, Bytes, Alloc, List[Transaction], Dict, Dict[str, Any]]:
"""
Generate common fixture data for both make_fixture and make_hive_fixture.
"""
pre, genesis_rlp, genesis = self.make_genesis(t8n, fork)
network_info = (
"+".join([fork.name()] + [str(eip) for eip in eips]) if eips else fork.name()
transition_tool_name = fork.transition_tool_name(
block_number=Number(self.env.number),
timestamp=Number(self.env.timestamp),
)
fork_name = (
"+".join([transition_tool_name] + [str(eip) for eip in eips])
if eips
else transition_tool_name
)

self.env = self.env.apply_new_parent(genesis).set_fork_requirements(fork)
Expand All @@ -138,7 +144,7 @@ def generate_fixture_data(
alloc=to_json(pre),
txs=to_json(txs),
env=to_json(self.env),
fork_name=network_info,
fork_name=fork_name,
chain_id=self.chain_id,
reward=fork.get_reward(Number(self.env.number), Number(self.env.timestamp)),
eips=eips,
Expand All @@ -160,7 +166,17 @@ def generate_fixture_data(
print_traces(traces=t8n.get_traces())
raise e

return genesis, genesis_rlp, pre, txs, t8n_result, t8n_alloc, network_info
return genesis, genesis_rlp, pre, txs, t8n_result, t8n_alloc

def network_info(self, fork: Fork, eips: Optional[List[int]] = None):
"""
Returns fixture network information for the fork & EIP/s.
"""
return (
"+".join([fork.blockchain_test_network_name()] + [str(eip) for eip in eips])
if eips
else fork.blockchain_test_network_name()
)

def make_fixture(
self, t8n: TransitionTool, fork: Fork, eips: Optional[List[int]] = None
Expand All @@ -175,15 +191,14 @@ def make_fixture(
txs,
t8n_result,
t8n_alloc,
network_info,
) = self.generate_fixture_data(t8n, fork, eips)
header = FixtureHeader.collect(
fork=fork, transition_tool_result=t8n_result, environment=self.env
)
block, header.hash = header.build(txs=txs, ommers=[], withdrawals=self.env.withdrawals)

return Fixture(
fork=network_info,
fork=self.network_info(fork, eips),
genesis=genesis,
genesis_rlp=genesis_rlp,
blocks=[
Expand Down Expand Up @@ -214,7 +229,6 @@ def make_hive_fixture(
txs,
t8n_result,
t8n_alloc,
network_info,
) = self.generate_fixture_data(t8n, fork, eips)

header = FixtureHeader.collect(
Expand All @@ -232,7 +246,7 @@ def make_hive_fixture(
fcu_version = fork.engine_forkchoice_updated_version(header.number, header.timestamp)

return HiveFixture(
fork=network_info,
fork=self.network_info(fork, eips),
genesis=genesis,
payloads=[fixture_payload],
fcu_version=fcu_version,
Expand Down
2 changes: 1 addition & 1 deletion src/evm_transition_tool/besu.py
Original file line number Diff line number Diff line change
Expand Up @@ -181,4 +181,4 @@ def is_fork_supported(self, fork: Fork) -> bool:
"""
Returns True if the fork is supported by the tool
"""
return fork.fork() in self.help_string
return fork.transition_tool_name() in self.help_string
2 changes: 1 addition & 1 deletion src/evm_transition_tool/geth.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ def is_fork_supported(self, fork: Fork) -> bool:
If the fork is a transition fork, we want to check the fork it transitions to.
"""
return fork.fork() in self.help_string
return fork.transition_tool_name() in self.help_string

def verify_fixture(
self, fixture_format: FixtureFormats, fixture_path: Path, debug_output_path: Optional[Path]
Expand Down
2 changes: 1 addition & 1 deletion src/evm_transition_tool/nimbus.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,4 +57,4 @@ def is_fork_supported(self, fork: Fork) -> bool:
If the fork is a transition fork, we want to check the fork it transitions to.
"""
return fork.fork() in self.help_string
return fork.transition_tool_name() in self.help_string
2 changes: 1 addition & 1 deletion src/evm_transition_tool/tests/test_evaluate.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ def test_evm_t8n(t8n: TransitionTool, test_dir: str) -> None: # noqa: D103
alloc=alloc,
txs=txs,
env=env_json,
fork_name=Berlin.fork(
fork_name=Berlin.transition_tool_name(
block_number=int(env_json["currentNumber"], 0),
timestamp=int(env_json["currentTimestamp"], 0),
),
Expand Down
2 changes: 1 addition & 1 deletion src/evm_transition_tool/transition_tool.py
Original file line number Diff line number Diff line change
Expand Up @@ -576,7 +576,7 @@ def calc_state_root(
alloc=alloc,
txs=[],
env=env,
fork_name=fork.fork(block_number=0, timestamp=0),
fork_name=fork.transition_tool_name(block_number=0, timestamp=0),
debug_output_path=debug_output_path,
)
state_root = result.get("stateRoot")
Expand Down

0 comments on commit 5ce00e7

Please sign in to comment.