diff --git a/cmd/ronin/dbcmd.go b/cmd/ronin/dbcmd.go index 56fe03a3b..b8d673318 100644 --- a/cmd/ronin/dbcmd.go +++ b/cmd/ronin/dbcmd.go @@ -18,6 +18,7 @@ package main import ( "bytes" + "encoding/json" "errors" "fmt" "os" @@ -32,6 +33,7 @@ import ( "github.com/ethereum/go-ethereum/cmd/utils" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" + v2 "github.com/ethereum/go-ethereum/consensus/consortium/v2" "github.com/ethereum/go-ethereum/console/prompt" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/ethdb" @@ -69,6 +71,7 @@ Remove blockchain and state databases`, dbDumpFreezerIndex, dbImportCmd, dbExportCmd, + dbPruneConsortiumSnapshotCmd, }, } dbInspectCmd = &cli.Command{ @@ -243,6 +246,20 @@ WARNING: This is a low-level operation which may cause database corruption!`, }, Description: "Exports the specified chain data to an RLP encoded stream, optionally gzip-compressed.", } + dbPruneConsortiumSnapshotCmd = &cli.Command{ + Name: "prune-consortium-snapshot", + Usage: "Prune all snapshots except the latest ones", + Action: pruneSnapshot, + Category: "MISCELLANEOUS COMMANDS", + Flags: []cli.Flag{ + utils.SnapshotKeepAfterPruningFlag, + utils.DataDirFlag, + }, + Description: ` +Prune all consortium snapshots except the latest ones. The number of snapshots to keep +can be specified via "--snapshot.keep-after-pruning" flag. The default value is 10. +`, + } ) func removeDB(ctx *cli.Context) error { @@ -695,3 +712,60 @@ func exportChaindata(ctx *cli.Context) error { db := utils.MakeChainDatabase(ctx, stack, true) return utils.ExportChaindata(ctx.Args().Get(1), kind, exporter(db), stop) } + +func pruneSnapshot(ctx *cli.Context) error { + keep := ctx.Int(utils.SnapshotKeepAfterPruningFlag.Name) + if keep < 0 { + log.Error("Invalid keep value", "keep", keep) + return errors.New("invalid keep value") + } + log.Info("Snapshot pruning", "latest snapshots keep: ", keep) + // Open the chain database + stack, _ := makeConfigNode(ctx) + defer stack.Close() + + db := utils.MakeChainDatabase(ctx, stack, false) + + // Get all snapshots (hash, block number) from the database + nSnapshots := 0 + snapshots := make(map[common.Hash]uint64) + it := db.NewIterator(rawdb.ConsortiumSnapshotPrefix, nil) + defer it.Release() + for it.Next() { + snap := new(v2.Snapshot) + if err := json.Unmarshal(it.Value(), snap); err != nil { + return err + } + snapshots[snap.Hash] = snap.Number + nSnapshots++ + } + log.Info("Found all snapshots", "nSnapshots", nSnapshots) + + // Sort the snapshots by block number + hashes := make([]common.Hash, 0, nSnapshots) + for hash := range snapshots { + hashes = append(hashes, hash) + } + sort.Slice(hashes, func(i, j int) bool { + return snapshots[hashes[i]] < snapshots[hashes[j]] + }) + + // Prune the snapshots + if nSnapshots < keep { + keep = nSnapshots + } + nSnapshotsPrune := nSnapshots - keep + batch := db.NewBatch() + for i := 0; i < nSnapshotsPrune; i++ { + if err := batch.Delete(append(rawdb.ConsortiumSnapshotPrefix, hashes[i][:]...)); err != nil { + log.Error("Failed to delete snapshot", "hash", hashes[i], "err", err) + return err + } + } + if err := batch.Write(); err != nil { + log.Error("Failed to write batch", "err", err) + return err + } + log.Info("Pruned snapshots", "snapshots", nSnapshotsPrune) + return nil +} diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index 1a5377eeb..bb7980c9e 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -1108,6 +1108,12 @@ var ( Usage: "List of mock stake amounts which are reflect 1:1 with mock.validators", Category: flags.MockCategory, } + + SnapshotKeepAfterPruningFlag = &cli.IntFlag{ + Name: "snapshot.keep-after-pruning", + Usage: "The number of lastest snapshots to keep after pruning (default 200 * 144 = 28800 ~ 1 day)", + Value: 200 * 144, + } ) // MakeDataDir retrieves the currently requested data directory, terminating