@@ -121,6 +121,8 @@ func (ctx *genctx) storageOriginSet(rawStorageKey bool, t *tester) map[common.Ad
121121type tester struct {
122122 db * Database
123123 roots []common.Hash
124+ nodes []* trienode.MergedNodeSet
125+ states []* StateSetWithOrigin
124126 preimages map [common.Hash ][]byte
125127
126128 // current state set
@@ -135,12 +137,38 @@ type tester struct {
135137 snapNodes map [common.Hash ]* trienode.MergedNodeSet
136138}
137139
140+ // testerConfig holds configuration parameters for running a test scenario.
138141type testerConfig struct {
139- stateHistory uint64
140- isVerkle bool
141- layers int
142- enableIndex bool
143- journalDir string
142+ stateHistory uint64 // Number of historical states to retain
143+ layers int // Number of state transitions to generate for
144+ enableIndex bool // Enable state history indexing or not
145+ journalDir string // Directory path for persisting journal files
146+ isVerkle bool // Enables Verkle trie mode if true
147+
148+ writeBuffer * int // Optional, the size of memory allocated for write buffer
149+ trieCache * int // Optional, the size of memory allocated for trie cache
150+ stateCache * int // Optional, the size of memory allocated for state cache
151+ }
152+
153+ func (c * testerConfig ) trieCacheSize () int {
154+ if c .trieCache != nil {
155+ return * c .trieCache
156+ }
157+ return 256 * 1024
158+ }
159+
160+ func (c * testerConfig ) stateCacheSize () int {
161+ if c .stateCache != nil {
162+ return * c .stateCache
163+ }
164+ return 256 * 1024
165+ }
166+
167+ func (c * testerConfig ) writeBufferSize () int {
168+ if c .writeBuffer != nil {
169+ return * c .writeBuffer
170+ }
171+ return 256 * 1024
144172}
145173
146174func newTester (t * testing.T , config * testerConfig ) * tester {
@@ -149,9 +177,9 @@ func newTester(t *testing.T, config *testerConfig) *tester {
149177 db = New (disk , & Config {
150178 StateHistory : config .stateHistory ,
151179 EnableStateIndexing : config .enableIndex ,
152- TrieCleanSize : 256 * 1024 ,
153- StateCleanSize : 256 * 1024 ,
154- WriteBufferSize : 256 * 1024 ,
180+ TrieCleanSize : config . trieCacheSize () ,
181+ StateCleanSize : config . stateCacheSize () ,
182+ WriteBufferSize : config . writeBufferSize () ,
155183 NoAsyncFlush : true ,
156184 JournalDirectory : config .journalDir ,
157185 }, config .isVerkle )
@@ -177,6 +205,8 @@ func newTester(t *testing.T, config *testerConfig) *tester {
177205 panic (fmt .Errorf ("failed to update state changes, err: %w" , err ))
178206 }
179207 obj .roots = append (obj .roots , root )
208+ obj .nodes = append (obj .nodes , nodes )
209+ obj .states = append (obj .states , states )
180210 }
181211 return obj
182212}
@@ -200,6 +230,8 @@ func (t *tester) extend(layers int) {
200230 panic (fmt .Errorf ("failed to update state changes, err: %w" , err ))
201231 }
202232 t .roots = append (t .roots , root )
233+ t .nodes = append (t .nodes , nodes )
234+ t .states = append (t .states , states )
203235 }
204236}
205237
@@ -885,3 +917,107 @@ func copyStorages(set map[common.Hash]map[common.Hash][]byte) map[common.Hash]ma
885917 }
886918 return copied
887919}
920+
921+ func TestDatabaseIndexRecovery (t * testing.T ) {
922+ maxDiffLayers = 4
923+ defer func () {
924+ maxDiffLayers = 128
925+ }()
926+
927+ //log.SetDefault(log.NewLogger(log.NewTerminalHandlerWithLevel(os.Stderr, log.LevelDebug, true)))
928+ writeBuffer := 512 * 1024
929+ config := & testerConfig {
930+ layers : 64 ,
931+ enableIndex : true ,
932+ writeBuffer : & writeBuffer ,
933+ }
934+ env := newTester (t , config )
935+ defer env .release ()
936+
937+ // Ensure the buffer in disk layer is not empty
938+ var (
939+ bRoot = env .db .tree .bottom ().rootHash ()
940+ dRoot = crypto .Keccak256Hash (rawdb .ReadAccountTrieNode (env .db .diskdb , nil ))
941+ )
942+ for dRoot == bRoot {
943+ env .extend (1 )
944+
945+ bRoot = env .db .tree .bottom ().rootHash ()
946+ dRoot = crypto .Keccak256Hash (rawdb .ReadAccountTrieNode (env .db .diskdb , nil ))
947+ }
948+ waitIndexing (env .db )
949+
950+ var (
951+ dIndex int
952+ roots = env .roots
953+ hr = newHistoryReader (env .db .diskdb , env .db .stateFreezer )
954+ )
955+ for i , root := range roots {
956+ if root == dRoot {
957+ dIndex = i
958+ }
959+ if root == bRoot {
960+ break
961+ }
962+ if err := checkHistoricalState (env , root , uint64 (i + 1 ), hr ); err != nil {
963+ t .Fatal (err )
964+ }
965+ }
966+
967+ // Terminate the database and mutate the journal, it's for simulating
968+ // the unclean shutdown
969+ env .db .Journal (env .lastHash ())
970+ env .db .Close ()
971+
972+ // Mutate the journal in disk, it should be regarded as invalid
973+ blob := rawdb .ReadTrieJournal (env .db .diskdb )
974+ blob [0 ] = 0xa
975+ rawdb .WriteTrieJournal (env .db .diskdb , blob )
976+
977+ // Reload the database, the extra state histories should be removed
978+ env .db = New (env .db .diskdb , env .db .config , false )
979+
980+ for i := range roots {
981+ _ , err := readStateHistory (env .db .stateFreezer , uint64 (i + 1 ))
982+ if i <= dIndex && err != nil {
983+ t .Fatalf ("State history is not found, %d" , i )
984+ }
985+ if i > dIndex && err == nil {
986+ t .Fatalf ("Unexpected state history found, %d" , i )
987+ }
988+ }
989+ remain , err := env .db .IndexProgress ()
990+ if err != nil {
991+ t .Fatalf ("Failed to obtain the progress, %v" , err )
992+ }
993+ if remain == 0 {
994+ t .Fatalf ("Unexpected progress remain, %d" , remain )
995+ }
996+
997+ // Apply new states on top, ensuring state indexing can respond correctly
998+ for i := dIndex + 1 ; i < len (roots ); i ++ {
999+ if err := env .db .Update (roots [i ], roots [i - 1 ], uint64 (i ), env .nodes [i ], env .states [i ]); err != nil {
1000+ panic (fmt .Errorf ("failed to update state changes, err: %w" , err ))
1001+ }
1002+ }
1003+ remain , err = env .db .IndexProgress ()
1004+ if err != nil {
1005+ t .Fatalf ("Failed to obtain the progress, %v" , err )
1006+ }
1007+ if remain != 0 {
1008+ t .Fatalf ("Unexpected progress remain, %d" , remain )
1009+ }
1010+ waitIndexing (env .db )
1011+
1012+ // Ensure the truncated state histories become accessible
1013+ bRoot = env .db .tree .bottom ().rootHash ()
1014+ hr = newHistoryReader (env .db .diskdb , env .db .stateFreezer )
1015+ for i , root := range roots {
1016+ if root == bRoot {
1017+ break
1018+ }
1019+ if err := checkHistoricalState (env , root , uint64 (i + 1 ), hr ); err != nil {
1020+ t .Fatal (err )
1021+ }
1022+ }
1023+ }
0 commit comments