github.com/fff-chain/go-fff@v0.0.0-20220726032732-1c84420b8a99/cmd/geth/dbcmd.go (about) 1 // Copyright 2020 The go-ethereum Authors 2 // This file is part of go-ethereum. 3 // 4 // go-ethereum is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // go-ethereum is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU General Public License for more details. 13 // 14 // You should have received a copy of the GNU General Public License 15 // along with go-ethereum. If not, see <http://www.gnu.org/licenses/>. 16 17 package main 18 19 import ( 20 "fmt" 21 "os" 22 "path/filepath" 23 "sort" 24 "strconv" 25 "time" 26 27 "github.com/fff-chain/go-fff/cmd/utils" 28 "github.com/fff-chain/go-fff/common" 29 "github.com/fff-chain/go-fff/common/hexutil" 30 "github.com/fff-chain/go-fff/console/prompt" 31 "github.com/fff-chain/go-fff/core/rawdb" 32 "github.com/fff-chain/go-fff/ethdb" 33 "github.com/fff-chain/go-fff/log" 34 "github.com/fff-chain/go-fff/trie" 35 "gopkg.in/urfave/cli.v1" 36 ) 37 38 var ( 39 removedbCommand = cli.Command{ 40 Action: utils.MigrateFlags(removeDB), 41 Name: "removedb", 42 Usage: "Remove blockchain and state databases", 43 ArgsUsage: "", 44 Flags: []cli.Flag{ 45 utils.DataDirFlag, 46 }, 47 Category: "DATABASE COMMANDS", 48 Description: ` 49 Remove blockchain and state databases`, 50 } 51 dbCommand = cli.Command{ 52 Name: "db", 53 Usage: "Low level database operations", 54 ArgsUsage: "", 55 Category: "DATABASE COMMANDS", 56 Subcommands: []cli.Command{ 57 dbInspectCmd, 58 dbStatCmd, 59 dbCompactCmd, 60 dbGetCmd, 61 dbDeleteCmd, 62 dbPutCmd, 63 dbGetSlotsCmd, 64 dbDumpFreezerIndex, 65 ancientInspectCmd, 66 }, 67 } 68 dbInspectCmd = cli.Command{ 69 Action: utils.MigrateFlags(inspect), 70 Name: "inspect", 71 ArgsUsage: "<prefix> <start>", 72 Flags: []cli.Flag{ 73 utils.DataDirFlag, 74 utils.SyncModeFlag, 75 utils.MainnetFlag, 76 utils.RopstenFlag, 77 utils.RinkebyFlag, 78 utils.GoerliFlag, 79 utils.YoloV3Flag, 80 }, 81 Usage: "Inspect the storage size for each type of data in the database", 82 Description: `This commands iterates the entire database. If the optional 'prefix' and 'start' arguments are provided, then the iteration is limited to the given subset of data.`, 83 } 84 dbStatCmd = cli.Command{ 85 Action: utils.MigrateFlags(dbStats), 86 Name: "stats", 87 Usage: "Print leveldb statistics", 88 Flags: []cli.Flag{ 89 utils.DataDirFlag, 90 utils.SyncModeFlag, 91 utils.MainnetFlag, 92 utils.RopstenFlag, 93 utils.RinkebyFlag, 94 utils.GoerliFlag, 95 utils.YoloV3Flag, 96 }, 97 } 98 dbCompactCmd = cli.Command{ 99 Action: utils.MigrateFlags(dbCompact), 100 Name: "compact", 101 Usage: "Compact leveldb database. WARNING: May take a very long time", 102 Flags: []cli.Flag{ 103 utils.DataDirFlag, 104 utils.SyncModeFlag, 105 utils.MainnetFlag, 106 utils.RopstenFlag, 107 utils.RinkebyFlag, 108 utils.GoerliFlag, 109 utils.YoloV3Flag, 110 utils.CacheFlag, 111 utils.CacheDatabaseFlag, 112 }, 113 Description: `This command performs a database compaction. 114 WARNING: This operation may take a very long time to finish, and may cause database 115 corruption if it is aborted during execution'!`, 116 } 117 dbGetCmd = cli.Command{ 118 Action: utils.MigrateFlags(dbGet), 119 Name: "get", 120 Usage: "Show the value of a database key", 121 ArgsUsage: "<hex-encoded key>", 122 Flags: []cli.Flag{ 123 utils.DataDirFlag, 124 utils.SyncModeFlag, 125 utils.MainnetFlag, 126 utils.RopstenFlag, 127 utils.RinkebyFlag, 128 utils.GoerliFlag, 129 utils.YoloV3Flag, 130 }, 131 Description: "This command looks up the specified database key from the database.", 132 } 133 dbDeleteCmd = cli.Command{ 134 Action: utils.MigrateFlags(dbDelete), 135 Name: "delete", 136 Usage: "Delete a database key (WARNING: may corrupt your database)", 137 ArgsUsage: "<hex-encoded key>", 138 Flags: []cli.Flag{ 139 utils.DataDirFlag, 140 utils.SyncModeFlag, 141 utils.MainnetFlag, 142 utils.RopstenFlag, 143 utils.RinkebyFlag, 144 utils.GoerliFlag, 145 utils.YoloV3Flag, 146 }, 147 Description: `This command deletes the specified database key from the database. 148 WARNING: This is a low-level operation which may cause database corruption!`, 149 } 150 dbPutCmd = cli.Command{ 151 Action: utils.MigrateFlags(dbPut), 152 Name: "put", 153 Usage: "Set the value of a database key (WARNING: may corrupt your database)", 154 ArgsUsage: "<hex-encoded key> <hex-encoded value>", 155 Flags: []cli.Flag{ 156 utils.DataDirFlag, 157 utils.SyncModeFlag, 158 utils.MainnetFlag, 159 utils.RopstenFlag, 160 utils.RinkebyFlag, 161 utils.GoerliFlag, 162 utils.YoloV3Flag, 163 }, 164 Description: `This command sets a given database key to the given value. 165 WARNING: This is a low-level operation which may cause database corruption!`, 166 } 167 dbGetSlotsCmd = cli.Command{ 168 Action: utils.MigrateFlags(dbDumpTrie), 169 Name: "dumptrie", 170 Usage: "Show the storage key/values of a given storage trie", 171 ArgsUsage: "<hex-encoded storage trie root> <hex-encoded start (optional)> <int max elements (optional)>", 172 Flags: []cli.Flag{ 173 utils.DataDirFlag, 174 utils.SyncModeFlag, 175 utils.MainnetFlag, 176 utils.RopstenFlag, 177 utils.RinkebyFlag, 178 utils.GoerliFlag, 179 utils.YoloV3Flag, 180 }, 181 Description: "This command looks up the specified database key from the database.", 182 } 183 dbDumpFreezerIndex = cli.Command{ 184 Action: utils.MigrateFlags(freezerInspect), 185 Name: "freezer-index", 186 Usage: "Dump out the index of a given freezer type", 187 ArgsUsage: "<type> <start (int)> <end (int)>", 188 Flags: []cli.Flag{ 189 utils.DataDirFlag, 190 utils.SyncModeFlag, 191 utils.MainnetFlag, 192 utils.RopstenFlag, 193 utils.RinkebyFlag, 194 utils.GoerliFlag, 195 utils.YoloV3Flag, 196 }, 197 Description: "This command displays information about the freezer index.", 198 } 199 ancientInspectCmd = cli.Command{ 200 Action: utils.MigrateFlags(ancientInspect), 201 Name: "inspect-reserved-oldest-blocks", 202 Flags: []cli.Flag{ 203 utils.DataDirFlag, 204 }, 205 Usage: "Inspect the ancientStore information", 206 Description: `This commands will read current offset from kvdb, which is the current offset and starting BlockNumber 207 of ancientStore, will also displays the reserved number of blocks in ancientStore `, 208 } 209 ) 210 211 func removeDB(ctx *cli.Context) error { 212 stack, config := makeConfigNode(ctx) 213 214 // Remove the full node state database 215 path := stack.ResolvePath("chaindata") 216 if common.FileExist(path) { 217 confirmAndRemoveDB(path, "full node state database") 218 } else { 219 log.Info("Full node state database missing", "path", path) 220 } 221 // Remove the full node ancient database 222 path = config.Eth.DatabaseFreezer 223 switch { 224 case path == "": 225 path = filepath.Join(stack.ResolvePath("chaindata"), "ancient") 226 case !filepath.IsAbs(path): 227 path = config.Node.ResolvePath(path) 228 } 229 if common.FileExist(path) { 230 confirmAndRemoveDB(path, "full node ancient database") 231 } else { 232 log.Info("Full node ancient database missing", "path", path) 233 } 234 // Remove the light node database 235 path = stack.ResolvePath("lightchaindata") 236 if common.FileExist(path) { 237 confirmAndRemoveDB(path, "light node database") 238 } else { 239 log.Info("Light node database missing", "path", path) 240 } 241 return nil 242 } 243 244 // confirmAndRemoveDB prompts the user for a last confirmation and removes the 245 // folder if accepted. 246 func confirmAndRemoveDB(database string, kind string) { 247 confirm, err := prompt.Stdin.PromptConfirm(fmt.Sprintf("Remove %s (%s)?", kind, database)) 248 switch { 249 case err != nil: 250 utils.Fatalf("%v", err) 251 case !confirm: 252 log.Info("Database deletion skipped", "path", database) 253 default: 254 start := time.Now() 255 filepath.Walk(database, func(path string, info os.FileInfo, err error) error { 256 // If we're at the top level folder, recurse into 257 if path == database { 258 return nil 259 } 260 // Delete all the files, but not subfolders 261 if !info.IsDir() { 262 os.Remove(path) 263 return nil 264 } 265 return filepath.SkipDir 266 }) 267 log.Info("Database successfully deleted", "path", database, "elapsed", common.PrettyDuration(time.Since(start))) 268 } 269 } 270 271 func inspect(ctx *cli.Context) error { 272 var ( 273 prefix []byte 274 start []byte 275 ) 276 if ctx.NArg() > 2 { 277 return fmt.Errorf("Max 2 arguments: %v", ctx.Command.ArgsUsage) 278 } 279 if ctx.NArg() >= 1 { 280 if d, err := hexutil.Decode(ctx.Args().Get(0)); err != nil { 281 return fmt.Errorf("failed to hex-decode 'prefix': %v", err) 282 } else { 283 prefix = d 284 } 285 } 286 if ctx.NArg() >= 2 { 287 if d, err := hexutil.Decode(ctx.Args().Get(1)); err != nil { 288 return fmt.Errorf("failed to hex-decode 'start': %v", err) 289 } else { 290 start = d 291 } 292 } 293 stack, _ := makeConfigNode(ctx) 294 defer stack.Close() 295 296 db := utils.MakeChainDatabase(ctx, stack, true, false) 297 defer db.Close() 298 299 return rawdb.InspectDatabase(db, prefix, start) 300 } 301 302 func ancientInspect(ctx *cli.Context) error { 303 stack, _ := makeConfigNode(ctx) 304 defer stack.Close() 305 306 db := utils.MakeChainDatabase(ctx, stack, true, true) 307 defer db.Close() 308 return rawdb.AncientInspect(db) 309 } 310 311 func showLeveldbStats(db ethdb.Stater) { 312 if stats, err := db.Stat("leveldb.stats"); err != nil { 313 log.Warn("Failed to read database stats", "error", err) 314 } else { 315 fmt.Println(stats) 316 } 317 if ioStats, err := db.Stat("leveldb.iostats"); err != nil { 318 log.Warn("Failed to read database iostats", "error", err) 319 } else { 320 fmt.Println(ioStats) 321 } 322 } 323 324 func dbStats(ctx *cli.Context) error { 325 stack, _ := makeConfigNode(ctx) 326 defer stack.Close() 327 328 db := utils.MakeChainDatabase(ctx, stack, true, false) 329 defer db.Close() 330 331 showLeveldbStats(db) 332 return nil 333 } 334 335 func dbCompact(ctx *cli.Context) error { 336 stack, _ := makeConfigNode(ctx) 337 defer stack.Close() 338 339 db := utils.MakeChainDatabase(ctx, stack, false, false) 340 defer db.Close() 341 342 log.Info("Stats before compaction") 343 showLeveldbStats(db) 344 345 log.Info("Triggering compaction") 346 if err := db.Compact(nil, nil); err != nil { 347 log.Info("Compact err", "error", err) 348 return err 349 } 350 log.Info("Stats after compaction") 351 showLeveldbStats(db) 352 return nil 353 } 354 355 // dbGet shows the value of a given database key 356 func dbGet(ctx *cli.Context) error { 357 if ctx.NArg() != 1 { 358 return fmt.Errorf("required arguments: %v", ctx.Command.ArgsUsage) 359 } 360 stack, _ := makeConfigNode(ctx) 361 defer stack.Close() 362 363 db := utils.MakeChainDatabase(ctx, stack, true, false) 364 defer db.Close() 365 366 key, err := hexutil.Decode(ctx.Args().Get(0)) 367 if err != nil { 368 log.Info("Could not decode the key", "error", err) 369 return err 370 } 371 data, err := db.Get(key) 372 if err != nil { 373 log.Info("Get operation failed", "error", err) 374 return err 375 } 376 fmt.Printf("key %#x: %#x\n", key, data) 377 return nil 378 } 379 380 // dbDelete deletes a key from the database 381 func dbDelete(ctx *cli.Context) error { 382 if ctx.NArg() != 1 { 383 return fmt.Errorf("required arguments: %v", ctx.Command.ArgsUsage) 384 } 385 stack, _ := makeConfigNode(ctx) 386 defer stack.Close() 387 388 db := utils.MakeChainDatabase(ctx, stack, false, false) 389 defer db.Close() 390 391 key, err := hexutil.Decode(ctx.Args().Get(0)) 392 if err != nil { 393 log.Info("Could not decode the key", "error", err) 394 return err 395 } 396 data, err := db.Get(key) 397 if err == nil { 398 fmt.Printf("Previous value: %#x\n", data) 399 } 400 if err = db.Delete(key); err != nil { 401 log.Info("Delete operation returned an error", "error", err) 402 return err 403 } 404 return nil 405 } 406 407 // dbPut overwrite a value in the database 408 func dbPut(ctx *cli.Context) error { 409 if ctx.NArg() != 2 { 410 return fmt.Errorf("required arguments: %v", ctx.Command.ArgsUsage) 411 } 412 stack, _ := makeConfigNode(ctx) 413 defer stack.Close() 414 415 db := utils.MakeChainDatabase(ctx, stack, false, false) 416 defer db.Close() 417 418 var ( 419 key []byte 420 value []byte 421 data []byte 422 err error 423 ) 424 key, err = hexutil.Decode(ctx.Args().Get(0)) 425 if err != nil { 426 log.Info("Could not decode the key", "error", err) 427 return err 428 } 429 value, err = hexutil.Decode(ctx.Args().Get(1)) 430 if err != nil { 431 log.Info("Could not decode the value", "error", err) 432 return err 433 } 434 data, err = db.Get(key) 435 if err == nil { 436 fmt.Printf("Previous value: %#x\n", data) 437 } 438 return db.Put(key, value) 439 } 440 441 // dbDumpTrie shows the key-value slots of a given storage trie 442 func dbDumpTrie(ctx *cli.Context) error { 443 if ctx.NArg() < 1 { 444 return fmt.Errorf("required arguments: %v", ctx.Command.ArgsUsage) 445 } 446 stack, _ := makeConfigNode(ctx) 447 defer stack.Close() 448 449 db := utils.MakeChainDatabase(ctx, stack, true, false) 450 defer db.Close() 451 var ( 452 root []byte 453 start []byte 454 max = int64(-1) 455 err error 456 ) 457 if root, err = hexutil.Decode(ctx.Args().Get(0)); err != nil { 458 log.Info("Could not decode the root", "error", err) 459 return err 460 } 461 stRoot := common.BytesToHash(root) 462 if ctx.NArg() >= 2 { 463 if start, err = hexutil.Decode(ctx.Args().Get(1)); err != nil { 464 log.Info("Could not decode the seek position", "error", err) 465 return err 466 } 467 } 468 if ctx.NArg() >= 3 { 469 if max, err = strconv.ParseInt(ctx.Args().Get(2), 10, 64); err != nil { 470 log.Info("Could not decode the max count", "error", err) 471 return err 472 } 473 } 474 theTrie, err := trie.New(stRoot, trie.NewDatabase(db)) 475 if err != nil { 476 return err 477 } 478 var count int64 479 it := trie.NewIterator(theTrie.NodeIterator(start)) 480 for it.Next() { 481 if max > 0 && count == max { 482 fmt.Printf("Exiting after %d values\n", count) 483 break 484 } 485 fmt.Printf(" %d. key %#x: %#x\n", count, it.Key, it.Value) 486 count++ 487 } 488 return it.Err 489 } 490 491 func freezerInspect(ctx *cli.Context) error { 492 var ( 493 start, end int64 494 disableSnappy bool 495 err error 496 ) 497 if ctx.NArg() < 3 { 498 return fmt.Errorf("required arguments: %v", ctx.Command.ArgsUsage) 499 } 500 kind := ctx.Args().Get(0) 501 if noSnap, ok := rawdb.FreezerNoSnappy[kind]; !ok { 502 var options []string 503 for opt := range rawdb.FreezerNoSnappy { 504 options = append(options, opt) 505 } 506 sort.Strings(options) 507 return fmt.Errorf("Could read freezer-type '%v'. Available options: %v", kind, options) 508 } else { 509 disableSnappy = noSnap 510 } 511 if start, err = strconv.ParseInt(ctx.Args().Get(1), 10, 64); err != nil { 512 log.Info("Could read start-param", "error", err) 513 return err 514 } 515 if end, err = strconv.ParseInt(ctx.Args().Get(2), 10, 64); err != nil { 516 log.Info("Could read count param", "error", err) 517 return err 518 } 519 stack, _ := makeConfigNode(ctx) 520 defer stack.Close() 521 path := filepath.Join(stack.ResolvePath("chaindata"), "ancient") 522 log.Info("Opening freezer", "location", path, "name", kind) 523 if f, err := rawdb.NewFreezerTable(path, kind, disableSnappy); err != nil { 524 return err 525 } else { 526 f.DumpIndex(start, end) 527 } 528 return nil 529 }