@@ -19,19 +19,155 @@ package pathdb
19
19
import (
20
20
"errors"
21
21
"fmt"
22
+ "iter"
22
23
24
+ "github.com/ethereum/go-ethereum/common"
23
25
"github.com/ethereum/go-ethereum/ethdb"
24
26
"github.com/ethereum/go-ethereum/log"
25
27
)
26
28
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
+
27
163
var (
28
164
errHeadTruncationOutOfRange = errors .New ("history head truncation out of range" )
29
165
errTailTruncationOutOfRange = errors .New ("history tail truncation out of range" )
30
166
)
31
167
32
168
// truncateFromHead removes excess elements from the head of the freezer based
33
169
// 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 ) {
35
171
ohead , err := store .Ancients ()
36
172
if err != nil {
37
173
return 0 , err
@@ -40,11 +176,11 @@ func truncateFromHead(store ethdb.AncientStore, nhead uint64) (int, error) {
40
176
if err != nil {
41
177
return 0 , err
42
178
}
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 )
44
180
45
181
// Ensure that the truncation target falls within the valid range.
46
182
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 )
48
184
}
49
185
// Short circuit if nothing to truncate.
50
186
if ohead == nhead {
@@ -61,7 +197,7 @@ func truncateFromHead(store ethdb.AncientStore, nhead uint64) (int, error) {
61
197
62
198
// truncateFromTail removes excess elements from the end of the freezer based
63
199
// 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 ) {
65
201
ohead , err := store .Ancients ()
66
202
if err != nil {
67
203
return 0 , err
@@ -72,7 +208,7 @@ func truncateFromTail(store ethdb.AncientStore, ntail uint64) (int, error) {
72
208
}
73
209
// Ensure that the truncation target falls within the valid range.
74
210
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 )
76
212
}
77
213
// Short circuit if nothing to truncate.
78
214
if otail == ntail {
0 commit comments