Skip to content

Commit e2112cf

Browse files
committed
feat(specs): Implement EIP-7928: Block-Level Access Lists
fix(tests): Fix Amsterdam filling after rebase fix(specs): Fix issues with new ruff + mypy rules after rebase - bal -> block_access_list; re-add custom rlp encoding for block access list - bytes to uint - move away from method-style - Update EIP-7928 implementation: system contracts at index 0, migrate to RLP - System contracts (parent hash, beacon root) now use block_access_index 0 - Transactions use block_access_index 1 to len(transactions) - Post-execution changes use block_access_index len(transactions) + 1 - Migrated from SSZ to RLP encoding as per updated EIP-7928 spec - Updated all tests to match new API and structure - Replaced tx_index with block_access_index throughout codebase - add system contract logic - add markdown docstrings - update BAL format; address comments - ssz encoding and bal validation - six ssz types - bal tests - balspecs fix: do not track setting empty code to a new account (#19) fix: track implicit SLOAD within SSTORE for OOG cases (#18) refactor: Put back explicit acct tracking outside 7702 delegation path (#17) fix non-tracked 7702 authority for invalid delegations (#16) * fix non-tracked 7702 authority for invalid delegations * fix: lint issues * fix: track delegation target when loaded as call target * fix: track delegation target when loaded as call target from call opcodes * chore: fix issues with documentation generation Fix self-destruct cases with pre-execution balance cache / tracking * fix self-destruct implementation * fix self-destruct tracking balance * fix it in the bal finalization by filtering * add balance reset and fix tests * simplify pre-balance tracking not using snapshots fix duplicated code entries for in transaction self destruct fix self destruct in same transaction bug fix call/delagate call tracking bug fix zero-value transfer tracking (#6) * fix zero-value transfer tracking * fix reverted frame tracking * rename variables * fix missing addresses bug * fix: docs run & move imports to top of file refactor: move rlp_utils to block_access_lists; bal -> block_access_lists Some remaining fixes due to large refactor in `forks/osaka`: - Move BALs from amsterdam -> forks/amsterdam - rename: build -> build_block_access_list - fix docc issues move state change tracker to State correct system contract addresses Fixes to communicate with BALs EEST branch: - fix(bal): Initialize the state tracker before system contract calls - We were missing system contract calls to beacon roots and history contracts. This change initializes the state tracker before system contract calls and passes the tracker to these calls if post-Amsterdam. - fix(docs): Fix issues with toxenvs: lint, doc, json_infra - fix(t8n): Only initialize the bal_change_tracker for amsterdam - feat(fork criteria): Index upcoming forks for better ordering / fix issues - chore(forks): Fix issues from lint after rebase with Osaka latest - fix(setuptools): Update packages to include amsterdam - chore(lint): Fix 'tox -e static' issues - Fix bug in tracker Manually cherry-picked from e72991b Author: nerolation - chore(tests): Attempt to resolve issues with CI tests - chore(lint): fix issues from running ``tox -e static`` locally - refactor(bal): Send BAL as a list over t8n tool - fix(amsterdam): Add change tracker to state test in t8n - chore(lint,tests): Fix tests after moving bal from osaka -> amsterdam - chore(forks): Move bals from Osaka to Amsterdam - chore(lint): Fix lint issues - refactor(bal): Send the full bal object and bal_hash over t8n - If we send the full object over JSON, we can model_validate() on ESST. - If we send the hash, once we fill the pydantic model, we can get the rlp and the hash and validate that our objects match while only really validating the parts of the BAL we are interested in for each test. - chore: point to working eest branch - chore(bals): Remove unused SSZ utils.py The SSZ implementation is no longer needed as we are now using RLP - refactor(bals): Clean up BAL module types and imports - Bytes -> Bytes32 type for storage slots - Remove unused imports / fix imports / fix linting - Update function signatures to match tracker - fix(bals-tx-index): Track bal indexes in t8n Keep track of BAL index state in t8n
1 parent e597ce3 commit e2112cf

26 files changed

Lines changed: 2083 additions & 33 deletions

File tree

packages/testing/src/execution_testing/fixtures/blockchain.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,9 @@ class FixtureHeader(CamelModel):
199199
requests_hash: (
200200
Annotated[Hash, HeaderForkRequirement("requests")] | None
201201
) = Field(None)
202+
block_access_list_hash: (
203+
Annotated[Hash, HeaderForkRequirement("bal_hash")] | None
204+
) = Field(None, alias="blockAccessListHash")
202205

203206
fork: Fork | None = Field(None, exclude=True)
204207

@@ -283,6 +286,11 @@ def genesis(cls, fork: Fork, env: Environment, state_root: Hash) -> Self:
283286
"requests_hash": Requests()
284287
if fork.header_requests_required(block_number=0, timestamp=0)
285288
else None,
289+
"block_access_list_hash": (
290+
BlockAccessList().rlp_hash
291+
if fork.header_bal_hash_required(block_number=0, timestamp=0)
292+
else None
293+
),
286294
"fork": fork,
287295
}
288296
return cls(**environment_values, **extras)
@@ -408,6 +416,14 @@ def from_fixture_header(
408416
"Invalid header for engine_newPayload"
409417
)
410418

419+
if fork.engine_execution_payload_block_access_list(
420+
block_number=header.number, timestamp=header.timestamp
421+
):
422+
if block_access_list is None:
423+
raise ValueError(
424+
f"`block_access_list` is required in engine `ExecutionPayload` for >={fork}."
425+
)
426+
411427
execution_payload = FixtureExecutionPayload.from_fixture_header(
412428
header=header,
413429
transactions=transactions,

packages/testing/src/execution_testing/forks/base_fork.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -345,6 +345,14 @@ def header_requests_required(
345345
"""Return true if the header must contain beacon chain requests."""
346346
pass
347347

348+
@classmethod
349+
@abstractmethod
350+
def header_bal_hash_required(
351+
cls, *, block_number: int = 0, timestamp: int = 0
352+
) -> bool:
353+
"""Return true if the header must contain block access list hash."""
354+
pass
355+
348356
# Gas related abstract methods
349357

350358
@classmethod
@@ -710,6 +718,17 @@ def engine_new_payload_target_blobs_per_block(
710718
"""
711719
pass
712720

721+
@classmethod
722+
@abstractmethod
723+
def engine_execution_payload_block_access_list(
724+
cls, *, block_number: int = 0, timestamp: int = 0
725+
) -> bool:
726+
"""
727+
Return `True` if the engine api version requires execution payload to
728+
include a `block_access_list`.
729+
"""
730+
pass
731+
713732
@classmethod
714733
@abstractmethod
715734
def engine_payload_attribute_target_blobs_per_block(

packages/testing/src/execution_testing/forks/forks/forks.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -443,6 +443,14 @@ def header_requests_required(
443443
del block_number, timestamp
444444
return False
445445

446+
@classmethod
447+
def header_bal_hash_required(
448+
cls, *, block_number: int = 0, timestamp: int = 0
449+
) -> bool:
450+
"""At genesis, header must not contain block access list hash."""
451+
del block_number, timestamp
452+
return False
453+
446454
@classmethod
447455
def engine_new_payload_version(
448456
cls, *, block_number: int = 0, timestamp: int = 0
@@ -483,6 +491,14 @@ def engine_new_payload_requests(
483491
del block_number, timestamp
484492
return False
485493

494+
@classmethod
495+
def engine_execution_payload_block_access_list(
496+
cls, *, block_number: int = 0, timestamp: int = 0
497+
) -> bool:
498+
"""At genesis, payloads do not have block access list."""
499+
del block_number, timestamp
500+
return False
501+
486502
@classmethod
487503
def engine_new_payload_target_blobs_per_block(
488504
cls,
@@ -2462,6 +2478,16 @@ class BPO5(BPO4, bpo_fork=True):
24622478
class Amsterdam(Osaka):
24632479
"""Amsterdam fork."""
24642480

2481+
@classmethod
2482+
def header_bal_hash_required(
2483+
cls, *, block_number: int = 0, timestamp: int = 0
2484+
) -> bool:
2485+
"""
2486+
From Amsterdam, header must contain block access list hash (EIP-7928).
2487+
"""
2488+
del block_number, timestamp
2489+
return True
2490+
24652491
@classmethod
24662492
def is_deployed(cls) -> bool:
24672493
"""Return True if this fork is deployed."""
@@ -2475,6 +2501,17 @@ def engine_new_payload_version(
24752501
del block_number, timestamp
24762502
return 5
24772503

2504+
@classmethod
2505+
def engine_execution_payload_block_access_list(
2506+
cls, *, block_number: int = 0, timestamp: int = 0
2507+
) -> bool:
2508+
"""
2509+
From Amsterdam, engine execution payload includes `block_access_list`
2510+
as a parameter.
2511+
"""
2512+
del block_number, timestamp
2513+
return True
2514+
24782515

24792516
class EOFv1(Prague, solc_name="cancun"):
24802517
"""EOF fork."""

packages/testing/src/execution_testing/specs/blockchain.py

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -687,6 +687,24 @@ def generate_block_data(
687687
)
688688
requests_list = block.requests
689689

690+
if self.fork.header_bal_hash_required(
691+
block_number=header.number, timestamp=header.timestamp
692+
):
693+
assert (
694+
transition_tool_output.result.block_access_list is not None
695+
), (
696+
"Block access list is required for this block but was not provided "
697+
"by the transition tool"
698+
)
699+
700+
rlp = transition_tool_output.result.block_access_list.rlp
701+
computed_bal_hash = Hash(rlp.keccak256())
702+
assert computed_bal_hash == header.block_access_list_hash, (
703+
"Block access list hash in header does not match the "
704+
f"computed hash from BAL: {header.block_access_list_hash} "
705+
f"!= {computed_bal_hash}"
706+
)
707+
690708
if block.rlp_modifier is not None:
691709
# Modify any parameter specified in the `rlp_modifier` after
692710
# transition tool processing.
@@ -695,6 +713,23 @@ def generate_block_data(
695713
self.fork
696714
) # Deleted during `apply` because `exclude=True`
697715

716+
# Process block access list - apply transformer if present for invalid
717+
# tests
718+
t8n_bal = transition_tool_output.result.block_access_list
719+
bal = t8n_bal
720+
if (
721+
block.expected_block_access_list is not None
722+
and t8n_bal is not None
723+
):
724+
block.expected_block_access_list.verify_against(t8n_bal)
725+
726+
bal = block.expected_block_access_list.modify_if_invalid_test(
727+
t8n_bal
728+
)
729+
if bal != t8n_bal:
730+
# If the BAL was modified, update the header hash
731+
header.block_access_list_hash = Hash(bal.rlp.keccak256())
732+
698733
built_block = BuiltBlock(
699734
header=header,
700735
alloc=transition_tool_output.alloc,
@@ -708,7 +743,7 @@ def generate_block_data(
708743
expected_exception=block.exception,
709744
engine_api_error_code=block.engine_api_error_code,
710745
fork=self.fork,
711-
block_access_list=None,
746+
block_access_list=bal,
712747
)
713748

714749
try:

pyproject.toml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,13 @@ packages = [
142142
"ethereum.forks.osaka.vm.instructions",
143143
"ethereum.forks.osaka.vm.precompiled_contracts",
144144
"ethereum.forks.osaka.vm.precompiled_contracts.bls12_381",
145+
"ethereum.forks.amsterdam",
146+
"ethereum.forks.amsterdam.block_access_lists",
147+
"ethereum.forks.amsterdam.utils",
148+
"ethereum.forks.amsterdam.vm",
149+
"ethereum.forks.amsterdam.vm.instructions",
150+
"ethereum.forks.amsterdam.vm.precompiled_contracts",
151+
"ethereum.forks.amsterdam.vm.precompiled_contracts.bls12_381",
145152
]
146153

147154
[tool.setuptools.package-data]
@@ -372,13 +379,26 @@ ignore = [
372379
"src/ethereum_spec_tools/evm_tools/t8n/evm_trace.py" = [
373380
"N815" # The traces must use camel case in JSON property names
374381
]
382+
"src/ethereum/forks/amsterdam/blocks.py" = [
383+
"E501" # Line too long - needed for long ref links
384+
]
385+
"src/ethereum/forks/amsterdam/block_access_lists/builder.py" = [
386+
"E501" # Line too long - needed for long ref links
387+
]
388+
"src/ethereum/forks/amsterdam/block_access_lists/rlp_utils.py" = [
389+
"E501" # Line too long - needed for long ref links
390+
]
375391
"tests/*" = ["ARG001"]
376392
"vulture_whitelist.py" = [
377393
"B018", # Useless expression (intentional for Vulture whitelisting)
378394
"F403", # Star imports needed for whitelisting
379395
"F405", # Undefined names from star imports
380396
]
381397

398+
[tool.ruff.lint.mccabe]
399+
# Set the maximum allowed cyclomatic complexity. C901 default is 10.
400+
max-complexity = 7
401+
382402
[tool.codespell]
383403
builtin = "clear,code,usage" # Built-in dictionaries to use
384404
skip = [ # Don't check these files/folders
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
"""
2+
Block Access Lists (EIP-7928) implementation for Ethereum Amsterdam fork.
3+
"""
4+
5+
from .builder import (
6+
BlockAccessListBuilder,
7+
add_balance_change,
8+
add_code_change,
9+
add_nonce_change,
10+
add_storage_read,
11+
add_storage_write,
12+
add_touched_account,
13+
build_block_access_list,
14+
)
15+
from .rlp_utils import (
16+
compute_block_access_list_hash,
17+
rlp_encode_block_access_list,
18+
validate_block_access_list_against_execution,
19+
)
20+
from .tracker import (
21+
StateChangeTracker,
22+
begin_call_frame,
23+
commit_call_frame,
24+
rollback_call_frame,
25+
set_block_access_index,
26+
track_address_access,
27+
track_balance_change,
28+
track_code_change,
29+
track_nonce_change,
30+
track_storage_read,
31+
track_storage_write,
32+
)
33+
34+
__all__ = [
35+
"BlockAccessListBuilder",
36+
"StateChangeTracker",
37+
"add_balance_change",
38+
"add_code_change",
39+
"add_nonce_change",
40+
"add_storage_read",
41+
"add_storage_write",
42+
"add_touched_account",
43+
"begin_call_frame",
44+
"build_block_access_list",
45+
"commit_call_frame",
46+
"compute_block_access_list_hash",
47+
"rollback_call_frame",
48+
"set_block_access_index",
49+
"rlp_encode_block_access_list",
50+
"track_address_access",
51+
"track_balance_change",
52+
"track_code_change",
53+
"track_nonce_change",
54+
"track_storage_read",
55+
"track_storage_write",
56+
"validate_block_access_list_against_execution",
57+
]

0 commit comments

Comments
 (0)