@@ -18,9 +18,11 @@ package main
18
18
19
19
import (
20
20
"bytes"
21
+ "encoding/binary"
21
22
"encoding/json"
22
23
"errors"
23
24
"fmt"
25
+ "math"
24
26
"os"
25
27
"slices"
26
28
"time"
@@ -33,6 +35,7 @@ import (
33
35
"github.com/ethereum/go-ethereum/core/state/snapshot"
34
36
"github.com/ethereum/go-ethereum/core/types"
35
37
"github.com/ethereum/go-ethereum/crypto"
38
+ "github.com/ethereum/go-ethereum/ethdb"
36
39
"github.com/ethereum/go-ethereum/log"
37
40
"github.com/ethereum/go-ethereum/rlp"
38
41
"github.com/ethereum/go-ethereum/trie"
@@ -63,6 +66,29 @@ two version states are available: genesis and the specific one.
63
66
The default pruning target is the HEAD-127 state.
64
67
65
68
WARNING: it's only supported in hash mode(--state.scheme=hash)".
69
+ ` ,
70
+ },
71
+ {
72
+ Name : "insecure-prune-pathdb" ,
73
+ Usage : "Prune all pathdb state data, it will break storage for fullnode, only suitable for fast node " +
74
+ "who do not need trie storage at all" ,
75
+ ArgsUsage : "" ,
76
+ Action : prunePathDB ,
77
+ Category : "MISCELLANEOUS COMMANDS" ,
78
+ Flags : []cli.Flag {
79
+ utils .DataDirFlag ,
80
+ utils .AncientFlag ,
81
+ },
82
+ Description : `
83
+ will prune all historical trie state data except genesis block.
84
+ All trie nodes will be deleted from the database.
85
+
86
+ It expects the genesis file as argument.
87
+
88
+ WARNING: It's necessary to delete the trie clean cache after the pruning.
89
+ If you specify another directory for the trie clean cache via "--cache.trie.journal"
90
+ during the use of Geth, please also specify it here for correct deletion. Otherwise
91
+ the trie clean cache with default directory will be deleted.
66
92
` ,
67
93
},
68
94
{
@@ -165,6 +191,99 @@ the expected order for the overlay tree migration.
165
191
}
166
192
)
167
193
194
+ // rangeCompactionThreshold is the minimal deleted entry number for
195
+ // triggering range compaction. It's a quite arbitrary number but just
196
+ // to avoid triggering range compaction because of small deletion.
197
+ const rangeCompactionThreshold = 1000000
198
+
199
+ func prunePathDB (ctx * cli.Context ) error {
200
+ stack , _ := makeConfigNode (ctx )
201
+ defer stack .Close ()
202
+
203
+ pruneDB := utils .MakeChainDatabase (ctx , stack , false )
204
+ defer pruneDB .Close ()
205
+
206
+ var (
207
+ count int
208
+ size common.StorageSize
209
+ pstart = time .Now ()
210
+ logged = time .Now ()
211
+ batch = pruneDB .NewBatch ()
212
+ iter = pruneDB .NewIterator (nil , nil )
213
+ stateIDPrefix = []byte ("L" )
214
+ )
215
+ for iter .Next () {
216
+ key := iter .Key ()
217
+ doDelete := false
218
+ switch {
219
+ case bytes .HasPrefix (key , stateIDPrefix ) && len (key ) == len (stateIDPrefix )+ common .HashLength :
220
+ doDelete = true
221
+ case rawdb .IsAccountTrieNode (key ):
222
+ doDelete = true
223
+ case rawdb .IsStorageTrieNode (key ):
224
+ doDelete = true
225
+ default :
226
+ }
227
+ if doDelete {
228
+ count += 1
229
+ size += common .StorageSize (len (key ) + len (iter .Value ()))
230
+ batch .Delete (key )
231
+
232
+ var eta time.Duration // Realistically will never remain uninited
233
+ if done := binary .BigEndian .Uint64 (key [:8 ]); done > 0 {
234
+ var (
235
+ left = math .MaxUint64 - binary .BigEndian .Uint64 (key [:8 ])
236
+ speed = done / uint64 (time .Since (pstart )/ time .Millisecond + 1 ) + 1 // +1s to avoid division by zero
237
+ )
238
+ eta = time .Duration (left / speed ) * time .Millisecond
239
+ }
240
+ if time .Since (logged ) > 8 * time .Second {
241
+ log .Info ("Pruning state data" , "nodes" , count , "size" , size ,
242
+ "elapsed" , common .PrettyDuration (time .Since (pstart )), "eta" , common .PrettyDuration (eta ))
243
+ logged = time .Now ()
244
+ }
245
+ // Recreate the iterator after every batch commit in order
246
+ // to allow the underlying compactor to delete the entries.
247
+ if batch .ValueSize () >= ethdb .IdealBatchSize {
248
+ batch .Write ()
249
+ batch .Reset ()
250
+
251
+ iter .Release ()
252
+ iter = pruneDB .NewIterator (nil , key )
253
+ }
254
+ }
255
+ }
256
+ if batch .ValueSize () > 0 {
257
+ batch .Write ()
258
+ batch .Reset ()
259
+ }
260
+ iter .Release ()
261
+ log .Info ("Pruned path db data" , "nodes" , count , "size" , size , "elapsed" , common .PrettyDuration (time .Since (pstart )))
262
+
263
+ // Start compactions, will remove the deleted data from the disk immediately.
264
+ // Note for small pruning, the compaction is skipped.
265
+ if count >= rangeCompactionThreshold {
266
+ cstart := time .Now ()
267
+ for b := 0x00 ; b <= 0xf0 ; b += 0x10 {
268
+ var (
269
+ start = []byte {byte (b )}
270
+ end = []byte {byte (b + 0x10 )}
271
+ )
272
+ if b == 0xf0 {
273
+ end = nil
274
+ }
275
+ log .Info ("Compacting database" , "range" , fmt .Sprintf ("%#x-%#x" , start , end ), "elapsed" , common .PrettyDuration (time .Since (cstart )))
276
+ if err := pruneDB .Compact (start , end ); err != nil {
277
+ log .Error ("Database compaction failed" , "error" , err )
278
+ return err
279
+ }
280
+ }
281
+ log .Info ("Database compaction finished" , "elapsed" , common .PrettyDuration (time .Since (cstart )))
282
+ }
283
+
284
+ return nil
285
+ }
286
+
168
287
// Deprecation: this command should be deprecated once the hash-based
169
288
// scheme is deprecated.
170
289
func pruneState (ctx * cli.Context ) error {
@@ -174,9 +293,6 @@ func pruneState(ctx *cli.Context) error {
174
293
chaindb := utils .MakeChainDatabase (ctx , stack , false )
175
294
defer chaindb .Close ()
176
295
177
- if rawdb .ReadStateScheme (chaindb ) != rawdb .HashScheme {
178
- log .Crit ("Offline pruning is not required for path scheme" )
179
- }
180
296
prunerconfig := pruner.Config {
181
297
Datadir : stack .ResolvePath ("" ),
182
298
BloomSize : ctx .Uint64 (utils .BloomFilterSizeFlag .Name ),
@@ -230,7 +346,7 @@ func verifyState(ctx *cli.Context) error {
230
346
NoBuild : true ,
231
347
AsyncBuild : false ,
232
348
}
233
- snaptree , err := snapshot .New (snapConfig , chaindb , triedb , headBlock .Root ())
349
+ snaptree , err := snapshot .New (snapConfig , chaindb , triedb , headBlock .Root (), false )
234
350
if err != nil {
235
351
log .Error ("Failed to open snapshot tree" , "err" , err )
236
352
return err
@@ -561,7 +677,7 @@ func dumpState(ctx *cli.Context) error {
561
677
NoBuild : true ,
562
678
AsyncBuild : false ,
563
679
}
564
- snaptree , err := snapshot .New (snapConfig , db , triedb , root )
680
+ snaptree , err := snapshot .New (snapConfig , db , triedb , root , false )
565
681
if err != nil {
566
682
return err
567
683
}
@@ -658,7 +774,7 @@ func snapshotExportPreimages(ctx *cli.Context) error {
658
774
NoBuild : true ,
659
775
AsyncBuild : false ,
660
776
}
661
- snaptree , err := snapshot .New (snapConfig , chaindb , triedb , root )
777
+ snaptree , err := snapshot .New (snapConfig , chaindb , triedb , root , false )
662
778
if err != nil {
663
779
return err
664
780
}
0 commit comments