Skip to content

Commit 25095fb

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 dfeccbd commit 25095fb

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,
@@ -2440,6 +2456,16 @@ class BPO5(BPO4, bpo_fork=True):
24402456
class Amsterdam(Osaka):
24412457
"""Amsterdam fork."""
24422458

2459+
@classmethod
2460+
def header_bal_hash_required(
2461+
cls, *, block_number: int = 0, timestamp: int = 0
2462+
) -> bool:
2463+
"""
2464+
From Amsterdam, header must contain block access list hash (EIP-7928).
2465+
"""
2466+
del block_number, timestamp
2467+
return True
2468+
24432469
@classmethod
24442470
def is_deployed(cls) -> bool:
24452471
"""Return True if this fork is deployed."""
@@ -2453,6 +2479,17 @@ def engine_new_payload_version(
24532479
del block_number, timestamp
24542480
return 5
24552481

2482+
@classmethod
2483+
def engine_execution_payload_block_access_list(
2484+
cls, *, block_number: int = 0, timestamp: int = 0
2485+
) -> bool:
2486+
"""
2487+
From Amsterdam, engine execution payload includes `block_access_list`
2488+
as a parameter.
2489+
"""
2490+
del block_number, timestamp
2491+
return True
2492+
24562493

24572494
class EOFv1(Prague, solc_name="cancun"):
24582495
"""EOF fork."""

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

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

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

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

711746
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)