diff --git a/triedb/pathdb/layertree.go b/triedb/pathdb/layertree.go index ec45257db5..0d7ca5a5b4 100644 --- a/triedb/pathdb/layertree.go +++ b/triedb/pathdb/layertree.go @@ -151,6 +151,15 @@ func (tree *layerTree) add(root common.Hash, parentRoot common.Hash, block uint6 if root == parentRoot { return errors.New("layer cycle") } + // If a layer with this root already exists, skip the insertion. Fork blocks + // can produce the same state root as the canonical block (same parent, same + // coinbase, zero txs); overwriting tree.layers[root] would corrupt the parent + // chain for any child layers already built on top of the existing one, and + // appending a duplicate root to the lookup indices causes accountTip/storageTip + // to resolve the wrong layer. + if tree.get(root) != nil { + return nil + } parent := tree.get(parentRoot) if parent == nil { return fmt.Errorf("triedb parent [%#x] layer missing", parentRoot) diff --git a/triedb/pathdb/layertree_test.go b/triedb/pathdb/layertree_test.go index a74c6eb045..285ca67b6c 100644 --- a/triedb/pathdb/layertree_test.go +++ b/triedb/pathdb/layertree_test.go @@ -575,6 +575,40 @@ func TestDescendant(t *testing.T) { } } +func TestDuplicateRootLookup(t *testing.T) { + // Chain: + // C1->C2->C3 (HEAD) + tr := newTestLayerTree() // base = 0x1 + tr.add(common.Hash{0x2}, common.Hash{0x1}, 1, NewNodeSetWithOrigin(nil, nil), + NewStateSetWithOrigin(randomAccountSet("0xa"), randomStorageSet([]string{"0xa"}, [][]string{{"0x1"}}, nil), nil, nil, false)) + tr.add(common.Hash{0x3}, common.Hash{0x2}, 2, NewNodeSetWithOrigin(nil, nil), + NewStateSetWithOrigin(randomAccountSet("0xa"), randomStorageSet([]string{"0xa"}, [][]string{{"0x1"}}, nil), nil, nil, false)) + + // A fork block with the same state root as C2; inserting it must not + // pollute the lookup history for the canonical descendant C3. + tr.add(common.Hash{0x2}, common.Hash{0x1}, 1, NewNodeSetWithOrigin(nil, nil), + NewStateSetWithOrigin(randomAccountSet("0xa"), randomStorageSet([]string{"0xa"}, [][]string{{"0x1"}}, nil), nil, nil, false)) + if n := tr.len(); n != 3 { + t.Fatalf("duplicate root insert changed layer count, got %d, want 3", n) + } + + l, err := tr.lookupAccount(common.HexToHash("0xa"), common.Hash{0x3}) + if err != nil { + t.Fatalf("account lookup failed: %v", err) + } + if l.rootHash() != (common.Hash{0x3}) { + t.Errorf("unexpected account tip, want %x, got %x", common.Hash{0x3}, l.rootHash()) + } + + l, err = tr.lookupStorage(common.HexToHash("0xa"), common.HexToHash("0x1"), common.Hash{0x3}) + if err != nil { + t.Fatalf("storage lookup failed: %v", err) + } + if l.rootHash() != (common.Hash{0x3}) { + t.Errorf("unexpected storage tip, want %x, got %x", common.Hash{0x3}, l.rootHash()) + } +} + func TestAccountLookup(t *testing.T) { // Chain: // C1->C2->C3->C4 (HEAD)