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

REFERENCE TABLEScroll to explore all columns →
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 + 1 is transaction i, and index n + 1 is 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:

REFERENCE TABLEScroll to explore all columns →
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