mirror of
https://github.com/luxfi/evm.git
synced 2026-09-24 00:27:44 +00:00
fix(core/genesis): set WithdrawalsHash on Cancun-active genesis headers
A genesis header with Cancun active (CancunTime=0) populated the Cancun fields (ParentBeaconRoot/ExcessBlobGas/BlobGasUsed) but left WithdrawalsHash nil, because the assignment was guarded by num.Sign()!=0 (skipped at block 0). RLP encodes post-merge fields positionally, so a nil WithdrawalsHash followed by present Cancun fields yields a header ReadHeader cannot decode: rlp: input string too short for common.Hash, decoding into ...WithdrawalsHash NewHeaderChain then fabricates a fake genesis header, which has the wrong state root, so block-building on a fresh L1 fails and the chain produces zero blocks. Cancun implies Shanghai, so a Cancun header MUST carry WithdrawalsHash. Set it (EmptyWithdrawalsHash) whenever Cancun is active and it's nil. Chains preserving a pre-withdrawals genesis hash opt out via SkipPostMergeFields (short-circuits the whole post-merge block), so they're unaffected. Verified on lqd devnet (chainId 8675312): genesis header round-trips, balance readable at 0x0, self-transfer mines block 1.
This commit is contained in:
+15
-4
@@ -471,6 +471,21 @@ func (g *Genesis) toBlock(db ethdb.Database, triedb *triedb.Database) *types.Blo
|
||||
withdrawals = make([]*types.Withdrawal, 0)
|
||||
}
|
||||
if conf.IsCancun(num, g.Timestamp) {
|
||||
// Cancun implies Shanghai: a Cancun header MUST carry a
|
||||
// WithdrawalsHash because the RLP codec reads it positionally
|
||||
// before the Cancun fields below. The `num.Sign() != 0` guard
|
||||
// above omits it at genesis, yielding an undecodable header
|
||||
// ("rlp: input string too short for common.Hash, decoding into
|
||||
// ...WithdrawalsHash") — which makes NewHeaderChain fabricate a
|
||||
// fake genesis header and the chain produce zero blocks. Set it
|
||||
// here so the genesis header round-trips. Chains that must keep a
|
||||
// pre-withdrawals genesis hash opt out entirely via SkipPostMergeFields.
|
||||
if head.WithdrawalsHash == nil {
|
||||
head.WithdrawalsHash = &types.EmptyWithdrawalsHash
|
||||
if withdrawals == nil {
|
||||
withdrawals = make([]*types.Withdrawal, 0)
|
||||
}
|
||||
}
|
||||
// EIP-4788: The parentBeaconBlockRoot of the genesis block is always
|
||||
// the zero hash. This is because the genesis block does not have a parent
|
||||
// by definition.
|
||||
@@ -564,10 +579,6 @@ func (g *Genesis) Commit(db ethdb.Database, triedb *triedb.Database) (*types.Blo
|
||||
// Write chain config
|
||||
customrawdb.WriteChainConfig(db, hash, config)
|
||||
|
||||
// Verify the block can be read back - remove debug code later
|
||||
// canonHash := rawdb.ReadCanonicalHash(db, 0)
|
||||
// log.Info("After write check", "canonical_hash", canonHash, "block_hash", block.Hash(), "match", canonHash == block.Hash())
|
||||
|
||||
return block, nil
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user