Skip to content

Commit 7282b64

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 5eb4e7b commit 7282b64

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
@@ -203,6 +203,9 @@ class FixtureHeader(CamelModel):
203203
requests_hash: (
204204
Annotated[Hash, HeaderForkRequirement("requests")] | None
205205
) = Field(None)
206+
block_access_list_hash: (
207+
Annotated[Hash, HeaderForkRequirement("bal_hash")] | None
208+
) = Field(None, alias="blockAccessListHash")
206209

207210
fork: Fork | None = Field(None, exclude=True)
208211

@@ -287,6 +290,11 @@ def genesis(cls, fork: Fork, env: Environment, state_root: Hash) -> Self:
287290
"requests_hash": Requests()
288291
if fork.header_requests_required(block_number=0, timestamp=0)
289292
else None,
293+
"block_access_list_hash": (
294+
BlockAccessList().rlp_hash
295+
if fork.header_bal_hash_required(block_number=0, timestamp=0)
296+
else None
297+
),
290298
"fork": fork,
291299
}
292300
return cls(**environment_values, **extras)
@@ -416,6 +424,14 @@ def from_fixture_header(
416424
"Invalid header for engine_newPayload"
417425
)
418426

427+
if fork.engine_execution_payload_block_access_list(
428+
block_number=header.number, timestamp=header.timestamp
429+
):
430+
if block_access_list is None:
431+
raise ValueError(
432+
f"`block_access_list` is required in engine `ExecutionPayload` for >={fork}."
433+
)
434+
419435
execution_payload = FixtureExecutionPayload.from_fixture_header(
420436
header=header,
421437
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
@@ -351,6 +351,14 @@ def header_requests_required(
351351
"""Return true if the header must contain beacon chain requests."""
352352
pass
353353

354+
@classmethod
355+
@abstractmethod
356+
def header_bal_hash_required(
357+
cls, *, block_number: int = 0, timestamp: int = 0
358+
) -> bool:
359+
"""Return true if the header must contain block access list hash."""
360+
pass
361+
354362
# Gas related abstract methods
355363

356364
@classmethod
@@ -743,6 +751,17 @@ def engine_new_payload_target_blobs_per_block(
743751
"""
744752
pass
745753

754+
@classmethod
755+
@abstractmethod
756+
def engine_execution_payload_block_access_list(
757+
cls, *, block_number: int = 0, timestamp: int = 0
758+
) -> bool:
759+
"""
760+
Return `True` if the engine api version requires execution payload to
761+
include a `block_access_list`.
762+
"""
763+
pass
764+
746765
@classmethod
747766
@abstractmethod
748767
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
@@ -947,6 +947,14 @@ def header_requests_required(
947947
del block_number, timestamp
948948
return False
949949

950+
@classmethod
951+
def header_bal_hash_required(
952+
cls, *, block_number: int = 0, timestamp: int = 0
953+
) -> bool:
954+
"""At genesis, header must not contain block access list hash."""
955+
del block_number, timestamp
956+
return False
957+
950958
@classmethod
951959
def engine_new_payload_version(
952960
cls, *, block_number: int = 0, timestamp: int = 0
@@ -987,6 +995,14 @@ def engine_new_payload_requests(
987995
del block_number, timestamp
988996
return False
989997

998+
@classmethod
999+
def engine_execution_payload_block_access_list(
1000+
cls, *, block_number: int = 0, timestamp: int = 0
1001+
) -> bool:
1002+
"""At genesis, payloads do not have block access list."""
1003+
del block_number, timestamp
1004+
return False
1005+
9901006
@classmethod
9911007
def engine_new_payload_target_blobs_per_block(
9921008
cls,
@@ -3264,6 +3280,16 @@ class Amsterdam(BPO2):
32643280
# related Amsterdam specs change over time, and before Amsterdam is
32653281
# live on mainnet.
32663282

3283+
@classmethod
3284+
def header_bal_hash_required(
3285+
cls, *, block_number: int = 0, timestamp: int = 0
3286+
) -> bool:
3287+
"""
3288+
From Amsterdam, header must contain block access list hash (EIP-7928).
3289+
"""
3290+
del block_number, timestamp
3291+
return True
3292+
32673293
@classmethod
32683294
def is_deployed(cls) -> bool:
32693295
"""Return True if this fork is deployed."""
@@ -3277,6 +3303,17 @@ def engine_new_payload_version(
32773303
del block_number, timestamp
32783304
return 5
32793305

3306+
@classmethod
3307+
def engine_execution_payload_block_access_list(
3308+
cls, *, block_number: int = 0, timestamp: int = 0
3309+
) -> bool:
3310+
"""
3311+
From Amsterdam, engine execution payload includes `block_access_list`
3312+
as a parameter.
3313+
"""
3314+
del block_number, timestamp
3315+
return True
3316+
32803317

32813318
class EOFv1(Prague, solc_name="cancun"):
32823319
"""EOF fork."""

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

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -699,6 +699,24 @@ def generate_block_data(
699699
)
700700
requests_list = block.requests
701701

702+
if self.fork.header_bal_hash_required(
703+
block_number=header.number, timestamp=header.timestamp
704+
):
705+
assert (
706+
transition_tool_output.result.block_access_list is not None
707+
), (
708+
"Block access list is required for this block but was not provided "
709+
"by the transition tool"
710+
)
711+
712+
rlp = transition_tool_output.result.block_access_list.rlp
713+
computed_bal_hash = Hash(rlp.keccak256())
714+
assert computed_bal_hash == header.block_access_list_hash, (
715+
"Block access list hash in header does not match the "
716+
f"computed hash from BAL: {header.block_access_list_hash} "
717+
f"!= {computed_bal_hash}"
718+
)
719+
702720
if block.rlp_modifier is not None:
703721
# Modify any parameter specified in the `rlp_modifier` after
704722
# transition tool processing.
@@ -707,6 +725,23 @@ def generate_block_data(
707725
self.fork
708726
) # Deleted during `apply` because `exclude=True`
709727

728+
# Process block access list - apply transformer if present for invalid
729+
# tests
730+
t8n_bal = transition_tool_output.result.block_access_list
731+
bal = t8n_bal
732+
if (
733+
block.expected_block_access_list is not None
734+
and t8n_bal is not None
735+
):
736+
block.expected_block_access_list.verify_against(t8n_bal)
737+
738+
bal = block.expected_block_access_list.modify_if_invalid_test(
739+
t8n_bal
740+
)
741+
if bal != t8n_bal:
742+
# If the BAL was modified, update the header hash
743+
header.block_access_list_hash = Hash(bal.rlp.keccak256())
744+
710745
built_block = BuiltBlock(
711746
header=header,
712747
alloc=transition_tool_output.alloc,
@@ -720,7 +755,7 @@ def generate_block_data(
720755
expected_exception=block.exception,
721756
engine_api_error_code=block.engine_api_error_code,
722757
fork=self.fork,
723-
block_access_list=None,
758+
block_access_list=bal,
724759
)
725760

726761
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]
@@ -378,13 +385,26 @@ ignore = [
378385
"src/ethereum_spec_tools/evm_tools/t8n/evm_trace.py" = [
379386
"N815" # The traces must use camel case in JSON property names
380387
]
388+
"src/ethereum/forks/amsterdam/blocks.py" = [
389+
"E501" # Line too long - needed for long ref links
390+
]
391+
"src/ethereum/forks/amsterdam/block_access_lists/builder.py" = [
392+
"E501" # Line too long - needed for long ref links
393+
]
394+
"src/ethereum/forks/amsterdam/block_access_lists/rlp_utils.py" = [
395+
"E501" # Line too long - needed for long ref links
396+
]
381397
"tests/*" = ["ARG001"]
382398
"vulture_whitelist.py" = [
383399
"B018", # Useless expression (intentional for Vulture whitelisting)
384400
"F403", # Star imports needed for whitelisting
385401
"F405", # Undefined names from star imports
386402
]
387403

404+
[tool.ruff.lint.mccabe]
405+
# Set the maximum allowed cyclomatic complexity. C901 default is 10.
406+
max-complexity = 7
407+
388408
[tool.codespell]
389409
builtin = "clear,code,usage"
390410
# Version control & tooling, build artifacts, data files, test fixtures, temp files, lock files
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)