Skip to content

Commit

Permalink
Merge pull request #990 from SamWilsn/sync-fixes-part-1
Browse files Browse the repository at this point in the history
Sync fixes part 1
  • Loading branch information
petertdavies authored Sep 10, 2024
2 parents 33629d5 + c5a2735 commit 299b780
Show file tree
Hide file tree
Showing 9 changed files with 313 additions and 105 deletions.
1 change: 1 addition & 0 deletions src/ethereum/dao_fork/vm/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ def incorporate_child_on_success(evm: Evm, child_evm: Evm) -> None:
evm.gas_left += child_evm.gas_left
evm.logs += child_evm.logs
evm.refund_counter += child_evm.refund_counter
evm.accounts_to_delete.update(child_evm.accounts_to_delete)


def incorporate_child_on_error(evm: Evm, child_evm: Evm) -> None:
Expand Down
12 changes: 10 additions & 2 deletions src/ethereum/dao_fork/vm/interpreter.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
A straightforward interpreter that executes EVM code.
"""
from dataclasses import dataclass
from typing import Optional, Set, Tuple, Union
from typing import Optional, Set, Tuple

from ethereum.base_types import U256, Bytes0, Uint
from ethereum.trace import (
Expand All @@ -32,6 +32,7 @@
account_has_code_or_nonce,
begin_transaction,
commit_transaction,
destroy_storage,
move_ether,
rollback_transaction,
set_code,
Expand Down Expand Up @@ -69,7 +70,7 @@ class MessageCallOutput:

gas_left: Uint
refund_counter: U256
logs: Union[Tuple[()], Tuple[Log, ...]]
logs: Tuple[Log, ...]
accounts_to_delete: Set[Address]
error: Optional[Exception]

Expand Down Expand Up @@ -147,6 +148,13 @@ def process_create_message(message: Message, env: Environment) -> Evm:
# take snapshot of state before processing the message
begin_transaction(env.state)

# If the address where the account is being created has storage, it is
# destroyed. This can only happen in the following highly unlikely
# circumstances:
# * The address created by two `CREATE` calls collide.
# * The first `CREATE` left empty code.
destroy_storage(env.state, message.current_target)

evm = process_message(message, env)
if not evm.error:
contract_code = evm.output
Expand Down
106 changes: 80 additions & 26 deletions src/ethereum/genesis.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,21 @@
import json
import pkgutil
from dataclasses import dataclass
from typing import Any, Dict, Type
from typing import Any, Callable, Dict, Generic, Type, TypeVar

from ethereum import rlp
from ethereum.base_types import (
U64,
U256,
Bytes,
Bytes8,
Bytes32,
FixedBytes,
Uint,
slotted_freezable,
)
from ethereum.crypto.hash import Hash32
from ethereum.utils import has_field
from ethereum.utils.hexadecimal import (
hex_to_bytes,
hex_to_bytes8,
Expand Down Expand Up @@ -128,8 +131,44 @@ def hex_or_base_10_str_to_u256(balance: str) -> U256:
return U256(int(balance))


AddressT = TypeVar("AddressT", bound=FixedBytes)
AccountT = TypeVar("AccountT")
StateT = TypeVar("StateT")
TrieT = TypeVar("TrieT")
BloomT = TypeVar("BloomT")
HeaderT = TypeVar("HeaderT")
BlockT = TypeVar("BlockT")


@slotted_freezable
@dataclass
class GenesisFork(
Generic[AddressT, AccountT, StateT, TrieT, BloomT, HeaderT, BlockT]
):
"""
Pointers to the various types and functions required to build a genesis
block.
"""

Address: Type[FixedBytes]
Account: Callable[[Uint, U256, bytes], AccountT]
Trie: Callable[[bool, object], TrieT]
Bloom: Type[FixedBytes]
Header: Type[HeaderT]
Block: Type[BlockT]
hex_to_address: Callable[[str], AddressT]
set_account: Callable[[StateT, AddressT, AccountT], object]
set_storage: Callable[[StateT, AddressT, Bytes32, U256], object]
state_root: Callable[[StateT], Hash32]
root: Callable[[TrieT], object]


def add_genesis_block(
hardfork: Any, chain: Any, genesis: GenesisConfiguration
hardfork: GenesisFork[
AddressT, AccountT, StateT, TrieT, BloomT, HeaderT, BlockT
],
chain: Any,
genesis: GenesisConfiguration,
) -> None:
"""
Adds the genesis block to an empty blockchain.
Expand Down Expand Up @@ -168,35 +207,33 @@ def add_genesis_block(
[EIP-161]: https://eips.ethereum.org/EIPS/eip-161
"""
Address: Type[FixedBytes] = hardfork.fork_types.Address
Address: Type[FixedBytes] = hardfork.Address
assert issubclass(Address, FixedBytes)

for address, account in genesis.initial_accounts.items():
address = hardfork.utils.hexadecimal.hex_to_address(address)
hardfork.state.set_account(
for hex_address, account in genesis.initial_accounts.items():
address = hardfork.hex_to_address(hex_address)
hardfork.set_account(
chain.state,
address,
hardfork.fork_types.Account(
hardfork.Account(
Uint(int(account.get("nonce", "0"))),
hex_or_base_10_str_to_u256(account.get("balance", 0)),
hex_to_bytes(account.get("code", "0x")),
),
)
for key, value in account.get("storage", {}).items():
hardfork.state.set_storage(
chain.state, address, hex_to_bytes32(key), hex_to_uint(value)
hardfork.set_storage(
chain.state, address, hex_to_bytes32(key), hex_to_u256(value)
)

fields = {
"parent_hash": hardfork.fork_types.Hash32(b"\0" * 32),
"parent_hash": Hash32(b"\0" * 32),
"ommers_hash": rlp.rlp_hash(()),
"coinbase": Address(b"\0" * Address.LENGTH),
"state_root": hardfork.state.state_root(chain.state),
"transactions_root": hardfork.trie.root(
hardfork.trie.Trie(False, None)
),
"receipt_root": hardfork.trie.root(hardfork.trie.Trie(False, None)),
"bloom": hardfork.fork_types.Bloom(b"\0" * 256),
"state_root": hardfork.state_root(chain.state),
"transactions_root": hardfork.root(hardfork.Trie(False, None)),
"receipt_root": hardfork.root(hardfork.Trie(False, None)),
"bloom": hardfork.Bloom(b"\0" * 256),
"difficulty": genesis.difficulty,
"number": Uint(0),
"gas_limit": genesis.gas_limit,
Expand All @@ -206,21 +243,38 @@ def add_genesis_block(
"nonce": genesis.nonce,
}

if hasattr(hardfork.blocks.Header, "mix_digest"):
fields["mix_digest"] = hardfork.fork_types.Hash32(b"\0" * 32)
if has_field(hardfork.Header, "mix_digest"):
fields["mix_digest"] = Hash32(b"\0" * 32)
else:
fields["prev_randao"] = hardfork.fork_types.Hash32(b"\0" * 32)
fields["prev_randao"] = Hash32(b"\0" * 32)

if hasattr(hardfork.blocks.Header, "base_fee_per_gas"):
if has_field(hardfork.Header, "base_fee_per_gas"):
fields["base_fee_per_gas"] = Uint(10**9)

genesis_header = hardfork.blocks.Header(**fields)
if has_field(hardfork.Header, "withdrawals_root"):
fields["withdrawals_root"] = hardfork.root(hardfork.Trie(False, None))

genesis_block = hardfork.blocks.Block(
header=genesis_header,
transactions=(),
ommers=(),
)
if has_field(hardfork.Header, "blob_gas_used"):
fields["blob_gas_used"] = U64(0)

if has_field(hardfork.Header, "excess_blob_gas"):
fields["excess_blob_gas"] = U64(0)

if has_field(hardfork.Header, "parent_beacon_block_root"):
fields["parent_beacon_block_root"] = Hash32(b"\0" * 32)

genesis_header = hardfork.Header(**fields)

block_fields = {
"header": genesis_header,
"transactions": (),
"ommers": (),
}

if has_field(hardfork.Block, "withdrawals"):
block_fields["withdrawals"] = ()

genesis_block = hardfork.Block(**block_fields)

chain.blocks.append(genesis_block)
chain.chain_id = genesis.chain_id
15 changes: 15 additions & 0 deletions src/ethereum/utils/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,18 @@
"""
Utility functions used in this specification.
"""

from dataclasses import fields
from typing import Any


def has_field(class_: Any, name: str) -> bool:
"""
Returns `True` if `class_` has a field with the given `name`.
"""
try:
all_fields = fields(class_)
except TypeError:
return False

return any(x.name == name for x in all_fields)
9 changes: 0 additions & 9 deletions src/ethereum_spec_tools/forks.py
Original file line number Diff line number Diff line change
Expand Up @@ -275,15 +275,6 @@ def module(self, name: str) -> Any:
"""
return importlib.import_module(self.mod.__name__ + "." + name)

def optimized_module(self, name: str) -> Any:
"""
Import if necessary, and return the given module belonging to this hard
fork's optimized implementation.
"""
assert self.mod.__name__.startswith("ethereum.")
module = "ethereum_optimized" + self.mod.__name__[8:] + "." + name
return importlib.import_module(module)

def iter_modules(self) -> Iterator[ModuleInfo]:
"""
Iterate through the (sub-)modules describing this hardfork.
Expand Down
27 changes: 21 additions & 6 deletions src/ethereum_spec_tools/lint/lints/glacier_forks_hygiene.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,15 @@
walk_sources,
)

EXCEPTIONAL_FILES = [
("dao_fork", ".dao"),
]

EXCEPTIONAL_DIFFS = [
# The DAO Fork has an irregular state transition and minor changes to the
# graffiti near the fork block.
("dao_fork", ".fork", "apply_fork"),
("dao_fork", ".fork", "validate_header"),
# There are some differences between london and arrow_glacier
# in terms of how the fork block is handled.
("arrow_glacier", ".fork", "calculate_base_fee_per_gas"),
Expand Down Expand Up @@ -51,7 +59,11 @@ def lint(
Walks the sources for each hardfork and emits Diagnostic messages.
"""
fork_name = forks[position].short_name
if not fork_name.endswith("_glacier") or position == 0:
if position == 0:
# Nothing to compare against!
return []

if fork_name != "dao_fork" and not fork_name.endswith("_glacier"):
# Nothing to compare against or non-glacier fork!
return []

Expand All @@ -62,18 +74,21 @@ def lint(

all_files = set(all_previous.keys()) | set(all_current.keys())
for file in all_files:
if (fork_name, file) in EXCEPTIONAL_FILES:
continue

if file not in all_previous:
add_diagnostic(
diagnostics,
f"the file `{file}` is added to `{fork_name}`."
f"the file `{file}` is added to `{fork_name}`. "
"Glacier forks may only differ in difficulty block.",
)
continue

if file not in all_current:
add_diagnostic(
diagnostics,
f"the file `{file}` is deleted from `{fork_name}`."
f"the file `{file}` is deleted from `{fork_name}`. "
"Glacier forks may only differ in difficulty block.",
)
continue
Expand Down Expand Up @@ -108,7 +123,7 @@ def compare(
except KeyError:
add_diagnostic(
diagnostics,
f"{item} in {name} has been added."
f"{item} in {name} has been added. "
"Glacier forks may only differ in difficulty block.",
)
continue
Expand All @@ -118,7 +133,7 @@ def compare(
except KeyError:
add_diagnostic(
diagnostics,
f"{item} in {name} has been deleted."
f"{item} in {name} has been deleted. "
"Glacier forks may only differ in difficulty block.",
)
continue
Expand All @@ -129,7 +144,7 @@ def compare(
if not compare_ast(previous_item, current_item):
add_diagnostic(
diagnostics,
f"`{item}` in `{name}` has changed."
f"`{item}` in `{name}` has changed. "
"Glacier forks may only differ in difficulty block.",
)

Expand Down
Loading

0 comments on commit 299b780

Please sign in to comment.