Skip to content

Commit 1f3bf0c

Browse files
committed
triedb/pathdb: generalize the history indexer
It's a pre-requisite for shipping full archive node, reusing the existing history indexing mechanism for trienode history.
1 parent ada22c6 commit 1f3bf0c

11 files changed

+465
-342
lines changed

triedb/pathdb/database.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,7 @@ func New(diskdb ethdb.Database, config *Config, isVerkle bool) *Database {
189189
}
190190
// TODO (rjl493456442) disable the background indexing in read-only mode
191191
if db.stateFreezer != nil && db.config.EnableStateIndexing {
192-
db.stateIndexer = newHistoryIndexer(db.diskdb, db.stateFreezer, db.tree.bottom().stateID())
192+
db.stateIndexer = newHistoryIndexer(db.diskdb, db.stateFreezer, db.tree.bottom().stateID(), typeStateHistory)
193193
log.Info("Enabled state history indexing")
194194
}
195195
fields := config.fields()
@@ -245,7 +245,7 @@ func (db *Database) repairHistory() error {
245245
}
246246
// Truncate the extra state histories above in freezer in case it's not
247247
// aligned with the disk layer. It might happen after a unclean shutdown.
248-
pruned, err := truncateFromHead(db.stateFreezer, id)
248+
pruned, err := truncateFromHead(db.stateFreezer, typeStateHistory, id)
249249
if err != nil {
250250
log.Crit("Failed to truncate extra state histories", "err", err)
251251
}
@@ -448,7 +448,7 @@ func (db *Database) Enable(root common.Hash) error {
448448
// 2. Re-initialize the indexer so it starts indexing from the new state root.
449449
if db.stateIndexer != nil && db.stateFreezer != nil && db.config.EnableStateIndexing {
450450
db.stateIndexer.close()
451-
db.stateIndexer = newHistoryIndexer(db.diskdb, db.stateFreezer, db.tree.bottom().stateID())
451+
db.stateIndexer = newHistoryIndexer(db.diskdb, db.stateFreezer, db.tree.bottom().stateID(), typeStateHistory)
452452
log.Info("Re-enabled state history indexing")
453453
}
454454
log.Info("Rebuilt trie database", "root", root)
@@ -502,7 +502,7 @@ func (db *Database) Recover(root common.Hash) error {
502502
if err := db.diskdb.SyncKeyValue(); err != nil {
503503
return err
504504
}
505-
_, err := truncateFromHead(db.stateFreezer, dl.stateID())
505+
_, err := truncateFromHead(db.stateFreezer, typeStateHistory, dl.stateID())
506506
if err != nil {
507507
return err
508508
}

triedb/pathdb/disklayer.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -378,7 +378,7 @@ func (dl *diskLayer) writeStateHistory(diff *diffLayer) (bool, error) {
378378
log.Debug("Skip tail truncation", "persistentID", persistentID, "tailID", tail+1, "headID", diff.stateID(), "limit", limit)
379379
return true, nil
380380
}
381-
pruned, err := truncateFromTail(dl.db.stateFreezer, newFirst-1)
381+
pruned, err := truncateFromTail(dl.db.stateFreezer, typeStateHistory, newFirst-1)
382382
if err != nil {
383383
return false, err
384384
}

triedb/pathdb/history.go

Lines changed: 141 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,19 +19,155 @@ package pathdb
1919
import (
2020
"errors"
2121
"fmt"
22+
"iter"
2223

24+
"github.com/ethereum/go-ethereum/common"
2325
"github.com/ethereum/go-ethereum/ethdb"
2426
"github.com/ethereum/go-ethereum/log"
2527
)
2628

29+
// historyType represents the category of historical data.
30+
type historyType uint8
31+
32+
const (
33+
// typeStateHistory indicates history data related to account or storage changes.
34+
typeStateHistory historyType = 0
35+
)
36+
37+
// String returns the string format representation.
38+
func (h historyType) String() string {
39+
switch h {
40+
case typeStateHistory:
41+
return "state"
42+
default:
43+
return fmt.Sprintf("unknown type: %d", h)
44+
}
45+
}
46+
47+
// elementType represents the category of state element.
48+
type elementType uint8
49+
50+
const (
51+
typeAccount elementType = 0 // represents the account data
52+
typeStorage elementType = 1 // represents the storage slot data
53+
)
54+
55+
// String returns the string format representation.
56+
func (e elementType) String() string {
57+
switch e {
58+
case typeAccount:
59+
return "account"
60+
case typeStorage:
61+
return "storage"
62+
default:
63+
return fmt.Sprintf("unknown element type: %d", e)
64+
}
65+
}
66+
67+
// toHistoryType maps an element type to its corresponding history type.
68+
func toHistoryType(typ elementType) historyType {
69+
if typ == typeAccount || typ == typeStorage {
70+
return typeStateHistory
71+
}
72+
panic(fmt.Sprintf("unknown element type %v", typ))
73+
}
74+
75+
// stateIdent represents the identifier of a state element, which can be
76+
// an account or a storage slot.
77+
type stateIdent struct {
78+
typ elementType
79+
80+
// The hash of the account address. This is used instead of the raw account
81+
// address is to align the traversal order with the Merkle-Patricia-Trie.
82+
addressHash common.Hash
83+
84+
// The hash of the storage slot key. This is used instead of the raw slot key
85+
// because, in legacy state histories (prior to the Cancun fork), the slot
86+
// identifier is the hash of the key, and the original key (preimage) cannot
87+
// be recovered. To maintain backward compatibility, the key hash is used.
88+
//
89+
// Meanwhile, using the storage key hash also preserve the traversal order
90+
// with Merkle-Patricia-Trie.
91+
//
92+
// This field is null if the identifier refers to an account or a trie node.
93+
storageHash common.Hash
94+
}
95+
96+
// String returns the string format state identifier.
97+
func (ident stateIdent) String() string {
98+
if ident.typ == typeAccount {
99+
return ident.addressHash.Hex()
100+
}
101+
return ident.addressHash.Hex() + ident.storageHash.Hex()
102+
}
103+
104+
// newAccountIdent constructs a state identifier for an account.
105+
func newAccountIdent(addressHash common.Hash) stateIdent {
106+
return stateIdent{
107+
typ: typeAccount,
108+
addressHash: addressHash,
109+
}
110+
}
111+
112+
// newStorageIdent constructs a state identifier for a storage slot.
113+
// The address denotes the address hash of the associated account;
114+
// the storageHash denotes the hash of the raw storage slot key;
115+
func newStorageIdent(addressHash common.Hash, storageHash common.Hash) stateIdent {
116+
return stateIdent{
117+
typ: typeStorage,
118+
addressHash: addressHash,
119+
storageHash: storageHash,
120+
}
121+
}
122+
123+
// stateIdentQuery is the extension of stateIdent by adding the account address
124+
// and raw storage key.
125+
type stateIdentQuery struct {
126+
stateIdent
127+
128+
address common.Address
129+
storageKey common.Hash
130+
}
131+
132+
// newAccountIdentQuery constructs a state identifier for an account.
133+
func newAccountIdentQuery(address common.Address, addressHash common.Hash) stateIdentQuery {
134+
return stateIdentQuery{
135+
stateIdent: newAccountIdent(addressHash),
136+
address: address,
137+
}
138+
}
139+
140+
// newStorageIdentQuery constructs a state identifier for a storage slot.
141+
// the address denotes the address of the associated account;
142+
// the addressHash denotes the address hash of the associated account;
143+
// the storageKey denotes the raw storage slot key;
144+
// the storageHash denotes the hash of the raw storage slot key;
145+
func newStorageIdentQuery(address common.Address, addressHash common.Hash, storageKey common.Hash, storageHash common.Hash) stateIdentQuery {
146+
return stateIdentQuery{
147+
stateIdent: newStorageIdent(addressHash, storageHash),
148+
address: address,
149+
storageKey: storageKey,
150+
}
151+
}
152+
153+
// history defines the interface of historical data, implemented by stateHistory
154+
// and trienodeHistory (in the near future).
155+
type history interface {
156+
// typ returns the historical data type held in the history.
157+
typ() historyType
158+
159+
// forEach returns an iterator to traverse the state entries in the history.
160+
forEach() iter.Seq[stateIdent]
161+
}
162+
27163
var (
28164
errHeadTruncationOutOfRange = errors.New("history head truncation out of range")
29165
errTailTruncationOutOfRange = errors.New("history tail truncation out of range")
30166
)
31167

32168
// truncateFromHead removes excess elements from the head of the freezer based
33169
// on the given parameters. It returns the number of items that were removed.
34-
func truncateFromHead(store ethdb.AncientStore, nhead uint64) (int, error) {
170+
func truncateFromHead(store ethdb.AncientStore, typ historyType, nhead uint64) (int, error) {
35171
ohead, err := store.Ancients()
36172
if err != nil {
37173
return 0, err
@@ -40,11 +176,11 @@ func truncateFromHead(store ethdb.AncientStore, nhead uint64) (int, error) {
40176
if err != nil {
41177
return 0, err
42178
}
43-
log.Info("Truncating from head", "ohead", ohead, "tail", otail, "nhead", nhead)
179+
log.Info("Truncating from head", "type", typ.String(), "ohead", ohead, "tail", otail, "nhead", nhead)
44180

45181
// Ensure that the truncation target falls within the valid range.
46182
if ohead < nhead || nhead < otail {
47-
return 0, fmt.Errorf("%w, tail: %d, head: %d, target: %d", errHeadTruncationOutOfRange, otail, ohead, nhead)
183+
return 0, fmt.Errorf("%w, %s, tail: %d, head: %d, target: %d", errHeadTruncationOutOfRange, typ, otail, ohead, nhead)
48184
}
49185
// Short circuit if nothing to truncate.
50186
if ohead == nhead {
@@ -61,7 +197,7 @@ func truncateFromHead(store ethdb.AncientStore, nhead uint64) (int, error) {
61197

62198
// truncateFromTail removes excess elements from the end of the freezer based
63199
// on the given parameters. It returns the number of items that were removed.
64-
func truncateFromTail(store ethdb.AncientStore, ntail uint64) (int, error) {
200+
func truncateFromTail(store ethdb.AncientStore, typ historyType, ntail uint64) (int, error) {
65201
ohead, err := store.Ancients()
66202
if err != nil {
67203
return 0, err
@@ -72,7 +208,7 @@ func truncateFromTail(store ethdb.AncientStore, ntail uint64) (int, error) {
72208
}
73209
// Ensure that the truncation target falls within the valid range.
74210
if otail > ntail || ntail > ohead {
75-
return 0, fmt.Errorf("%w, tail: %d, head: %d, target: %d", errTailTruncationOutOfRange, otail, ohead, ntail)
211+
return 0, fmt.Errorf("%w, %s, tail: %d, head: %d, target: %d", errTailTruncationOutOfRange, typ, otail, ohead, ntail)
76212
}
77213
// Short circuit if nothing to truncate.
78214
if otail == ntail {

0 commit comments

Comments
 (0)