mirror of
https://github.com/luxfi/evm.git
synced 2026-09-24 00:27:44 +00:00
loadLayers() previously computed Keccak256Hash(nil) as the disk layer root for an unwritten DB, producing 0xc5d2…a470 — the keccak of zero bytes, NOT the canonical empty-trie root (0x56e81f…b421) that core/Genesis.toBlock uses as parentRoot when committing genesis. Consequence: layertree.add(root, parentRoot=EmptyRootHash) couldn't find a parent in the layer tree (only Keccak256Hash(nil) was there) and panicked with "triedb parent layer missing". Every fresh-launched chain on this fork (lqd primary network's C-Chain, every Liquidity local stack since the path-DB switch) failed to commit its genesis block, leaving block 0's stateRoot pointing at an empty trie no matter what `alloc` the genesis specified. Symptom: eth_getBalance "latest" returned the genesis-allocation values from in-memory cache, but every block-build saw "address has 0" and looped on BAD BLOCK. Fix: when ReadAccountTrieNode returns nil (no persistent state yet), short-circuit `root` to types.EmptyRootHash. Matches go-ethereum upstream and the contract that Genesis.toBlock + layertree.add rely on. Adds journal_freshdb_test.go with two regression tests: - the disk layer's root MUST be EmptyRootHash on a fresh DB - Update() onto EmptyRootHash parent succeeds (the genesis path) Without this fix, any future refactor that re-introduces an unconditional Keccak256Hash(nil) silently breaks fresh chains again.
81 lines
3.1 KiB
Go
81 lines
3.1 KiB
Go
// Copyright (C) 2026, Lux Industries, Inc. All rights reserved.
|
|
// See the file LICENSE for licensing terms.
|
|
|
|
package pathdb
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/luxfi/geth/common"
|
|
"github.com/luxfi/geth/core/rawdb"
|
|
"github.com/luxfi/geth/core/types"
|
|
"github.com/luxfi/geth/trie/trienode"
|
|
"github.com/luxfi/geth/trie/triestate"
|
|
)
|
|
|
|
// TestLoadLayers_FreshDB_UsesEmptyRootHash asserts the disk layer's root on a
|
|
// pristine key-value store is types.EmptyRootHash, not Keccak256Hash(nil).
|
|
//
|
|
// Why this matters: core.Genesis.toBlock commits genesis state with parentRoot
|
|
// == EmptyRootHash. layertree.add looks up parentRoot in the layer tree before
|
|
// inserting the new layer; if the only existing layer is at Keccak256Hash(nil)
|
|
// (`0xc5d2…a470`), the lookup misses and the commit panics with
|
|
// "triedb parent layer missing". That single hash mismatch broke every
|
|
// fresh-launched chain on this fork.
|
|
//
|
|
// Regression guard: any future refactor that re-introduces
|
|
// `crypto.Keccak256Hash(data)` as the unconditional root computation for an
|
|
// empty disk node MUST update this test in tandem with the layer-tree
|
|
// initialization, or chains will silently fail to commit genesis again.
|
|
func TestLoadLayers_FreshDB_UsesEmptyRootHash(t *testing.T) {
|
|
memdb := rawdb.NewMemoryDatabase()
|
|
db := New(memdb, nil)
|
|
defer db.Close()
|
|
|
|
head := db.tree.get(types.EmptyRootHash)
|
|
if head == nil {
|
|
got := "<no layers in tree>"
|
|
// Walk the (small) layer set so the failure message tells you what
|
|
// root the tree DID install — most useful when the regression hits.
|
|
db.tree.lock.RLock()
|
|
for k := range db.tree.layers {
|
|
got = k.Hex()
|
|
break
|
|
}
|
|
db.tree.lock.RUnlock()
|
|
t.Fatalf("fresh DB disk layer should be at EmptyRootHash (%s); tree has root=%s",
|
|
types.EmptyRootHash.Hex(), got)
|
|
}
|
|
|
|
if head.rootHash() != types.EmptyRootHash {
|
|
t.Fatalf("disk layer root mismatch: got %s, want %s",
|
|
head.rootHash().Hex(), types.EmptyRootHash.Hex())
|
|
}
|
|
}
|
|
|
|
// TestLoadLayers_FreshDB_AddOnTopOfEmptyRootSucceeds end-to-end check:
|
|
// simulate the sequence the genesis-commit path takes on a fresh DB —
|
|
// add a new layer with parentRoot = EmptyRootHash. Before the fix this
|
|
// returned "triedb parent layer missing"; with the fix it succeeds.
|
|
func TestLoadLayers_FreshDB_AddOnTopOfEmptyRootSucceeds(t *testing.T) {
|
|
memdb := rawdb.NewMemoryDatabase()
|
|
db := New(memdb, nil)
|
|
defer db.Close()
|
|
|
|
// Construct a single trivial layer pretending to be the genesis state.
|
|
childRoot := common.HexToHash("0x000000000000000000000000000000000000000000000000000000000000beef")
|
|
|
|
// Empty payload — genesis with no allocations is similar in shape
|
|
// (the alloc map only adds account nodes; for this regression test we
|
|
// only care that parent lookup against EmptyRootHash succeeds).
|
|
emptyNodes := trienode.NewMergedNodeSet()
|
|
emptyStates := triestate.New(nil, nil, nil)
|
|
|
|
if err := db.Update(childRoot, types.EmptyRootHash, 0, emptyNodes, emptyStates); err != nil {
|
|
t.Fatalf("Update onto EmptyRootHash parent failed (the bug): %v", err)
|
|
}
|
|
if got := db.tree.get(childRoot); got == nil {
|
|
t.Fatalf("child layer at %s missing from tree after Update", childRoot.Hex())
|
|
}
|
|
}
|