On net2040, every block from 21,600 on carries a block-level access list hash (EIP-7928) in its header. The amsterdamBlock row of the fork table is 21,600 for net2040 and "unscheduled" for mainnet and Apothem (fork table). An xOneRS node builds the list while it runs each block and refuses the block if the hash differs. Since PR #1103 (2026-11-15), two RPC methods return the list itself. This post shows the header fields on real blocks and explains the check.
What changes in the header at block 21,600?
We read blocks 21,599, 21,600 and 177,963 from an xOneRS net2040 node and from the geth-XDC reference node on 2026-11-15 (read-only eth_getBlockByNumber):
| Field | Block 21,599 | Block 21,600 | Block 177,963 |
|---|---|---|---|
blockAccessListHash |
absent | 0x2be18aa6…5906 |
0x96d589e3…6aba |
withdrawalsRoot |
absent | 0x56e81f17…b421 (empty trie) |
the same |
blobGasUsed, excessBlobGas |
absent | 0x0, 0x0 |
the same |
parentBeaconBlockRoot |
absent | zero | zero |
requestsHash |
absent | 0xe3b0c442…b855 (empty) |
the same |
The block hashes and the access list hashes were equal on both nodes for 21,600 and 177,963. Block 21,600 has no transactions; block 177,963 has one, and the replay notes call it the "first 0x88 call under EIP-7928" (docs/ops/net2040-follow.md).
XDC has no beacon chain, withdrawals, blobs or execution requests, so from Amsterdam these fields hold their canonical empty values, and the body carries an empty withdrawals list. A producer that leaves them out makes a block every node refuses: the Gate D rehearsal hit exactly that (docs/ops/gate-d-rehearsal.md).
What is in a block access list?
From the block processing order spec (spec/execution/block-processing-order.md, step 8a):
- Index 0 is the EIP-2935 write (the parent hash into history storage), index
i + 1is transactioni, and indexn + 1is the post phase. - A transaction lists every account and every slot it reads, whether it exists or not, with the value before the transaction.
- At the end of the transaction, a slot whose value changed is a write of its new value; otherwise it is a read. A balance, nonce or code that differs from its value before the transaction is a write too.
The node builds this list while the block runs, hashes it, and compares the hash with blockAccessListHash. Any difference refuses the block. An early xOne build listed plain transfers only; the follow notes record the fix that made the executor list every account and slot a transaction reads.
How do I read a block's access list?
On a build from main after PR #1103:
| Method | Returns |
|---|---|
eth_getBlockAccessList(block) |
The access list as JSON |
eth_getRawBlockAccessList(block) |
The encoded list |
The node recomputes the list from the parent state and refuses to answer unless its hash matches the header. Blocks before the fork, and unknown blocks, give null. If the parent state needed for the replay is pruned, the answer is the typed unsupported history error, so read old blocks from an archive node. The older build we queried answers -32601 for these methods.
Why does it matter for an operator?
Most operators never read the list. It matters in two cases:
- A node that refuses blocks after a fork. If a client builds the list differently from geth-XDC, it refuses every Amsterdam block with a transaction. The refused block's evidence shows it; see invalid-block evidence.
- A producer on net2040. A block without the Amsterdam fields, or with a wrong list hash, is refused by every node, its own included.
Apothem and mainnet have no Amsterdam block scheduled, so none of this applies there yet.
Frequently asked questions
When did net2040 activate EIP-7928 block access lists?
At block 21,600, the amsterdamBlock row of net2040. Mainnet and Apothem have it unscheduled.
Does an xOne node check the block access list?
Yes. It builds the list while it executes the block and refuses the block if its hash differs from the header's blockAccessListHash.
How do I get the access list of a block over RPC?
eth_getBlockAccessList or eth_getRawBlockAccessList, on an xOneRS build from main after PR #1103. They return null before the fork.
Why are withdrawals and blob fields in XDC headers?
The Amsterdam header format includes them. XDC fills them with their empty values.
Sources
- docs/spec/fork-table.md, spec/execution/block-processing-order.md, docs/ops/net2040-follow.md, docs/ops/gate-d-rehearsal.md
- PR #1103
eth_getBlockByNumberfor blocks 21,599, 21,600 and 177,963 on an xOneRS net2040 node and the geth-XDC reference, 2026-11-15.
This article describes the source and evidence at its review date. Check the linked repository for current implementation and qualification status.
Explore the engineering evidenceEdit this page on GitHub opens the Markdown source (content/blog/net2040-block-access-lists/index.md). GitHub may ask you to sign in. Report a correction opens a prefilled GitHub issue for this page.