@@ -19,19 +19,155 @@ package pathdb
1919import (
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+
27163var (
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