github.com/aidoskuneen/adk-node@v0.0.0-20220315131952-2e32567cb7f4/adkgo-node/dbcmd.go (about) 1 // Copyright 2021 The adkgo Authors 2 // This file is part of adkgo. 3 // 4 // adkgo 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 // adkgo 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 adkgo. 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/aidoskuneen/adk-node/cmd/utils" 28 "github.com/aidoskuneen/adk-node/common" 29 "github.com/aidoskuneen/adk-node/common/hexutil" 30 "github.com/aidoskuneen/adk-node/console/prompt" 31 "github.com/aidoskuneen/adk-node/core/rawdb" 32 "github.com/aidoskuneen/adk-node/ethdb" 33 "github.com/aidoskuneen/adk-node/log" 34 "github.com/aidoskuneen/adk-node/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 }, 66 } 67 dbInspectCmd = cli.Command{ 68 Action: utils.MigrateFlags(inspect), 69 Name: "inspect", 70 ArgsUsage: "<prefix> <start>", 71 Flags: []cli.Flag{ 72 utils.DataDirFlag, 73 utils.SyncModeFlag, 74 utils.MainnetFlag, 75 utils.RopstenFlag, 76 utils.RinkebyFlag, 77 utils.GoerliFlag, 78 }, 79 Usage: "Inspect the storage size for each type of data in the database", 80 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.`, 81 } 82 dbStatCmd = cli.Command{ 83 Action: utils.MigrateFlags(dbStats), 84 Name: "stats", 85 Usage: "Print leveldb statistics", 86 Flags: []cli.Flag{ 87 utils.DataDirFlag, 88 utils.SyncModeFlag, 89 utils.MainnetFlag, 90 utils.RopstenFlag, 91 utils.RinkebyFlag, 92 utils.GoerliFlag, 93 }, 94 } 95 dbCompactCmd = cli.Command{ 96 Action: utils.MigrateFlags(dbCompact), 97 Name: "compact", 98 Usage: "Compact leveldb database. WARNING: May take a very long time", 99 Flags: []cli.Flag{ 100 utils.DataDirFlag, 101 utils.SyncModeFlag, 102 utils.MainnetFlag, 103 utils.RopstenFlag, 104 utils.RinkebyFlag, 105 utils.GoerliFlag, 106 utils.CacheFlag, 107 utils.CacheDatabaseFlag, 108 }, 109 Description: `This command performs a database compaction. 110 WARNING: This operation may take a very long time to finish, and may cause database 111 corruption if it is aborted during execution'!`, 112 } 113 dbGetCmd = cli.Command{ 114 Action: utils.MigrateFlags(dbGet), 115 Name: "get", 116 Usage: "Show the value of a database key", 117 ArgsUsage: "<hex-encoded key>", 118 Flags: []cli.Flag{ 119 utils.DataDirFlag, 120 utils.SyncModeFlag, 121 utils.MainnetFlag, 122 utils.RopstenFlag, 123 utils.RinkebyFlag, 124 utils.GoerliFlag, 125 }, 126 Description: "This command looks up the specified database key from the database.", 127 } 128 dbDeleteCmd = cli.Command{ 129 Action: utils.MigrateFlags(dbDelete), 130 Name: "delete", 131 Usage: "Delete a database key (WARNING: may corrupt your database)", 132 ArgsUsage: "<hex-encoded key>", 133 Flags: []cli.Flag{ 134 utils.DataDirFlag, 135 utils.SyncModeFlag, 136 utils.MainnetFlag, 137 utils.RopstenFlag, 138 utils.RinkebyFlag, 139 utils.GoerliFlag, 140 }, 141 Description: `This command deletes the specified database key from the database. 142 WARNING: This is a low-level operation which may cause database corruption!`, 143 } 144 dbPutCmd = cli.Command{ 145 Action: utils.MigrateFlags(dbPut), 146 Name: "put", 147 Usage: "Set the value of a database key (WARNING: may corrupt your database)", 148 ArgsUsage: "<hex-encoded key> <hex-encoded value>", 149 Flags: []cli.Flag{ 150 utils.DataDirFlag, 151 utils.SyncModeFlag, 152 utils.MainnetFlag, 153 utils.RopstenFlag, 154 utils.RinkebyFlag, 155 utils.GoerliFlag, 156 }, 157 Description: `This command sets a given database key to the given value. 158 WARNING: This is a low-level operation which may cause database corruption!`, 159 } 160 dbGetSlotsCmd = cli.Command{ 161 Action: utils.MigrateFlags(dbDumpTrie), 162 Name: "dumptrie", 163 Usage: "Show the storage key/values of a given storage trie", 164 ArgsUsage: "<hex-encoded storage trie root> <hex-encoded start (optional)> <int max elements (optional)>", 165 Flags: []cli.Flag{ 166 utils.DataDirFlag, 167 utils.SyncModeFlag, 168 utils.MainnetFlag, 169 utils.RopstenFlag, 170 utils.RinkebyFlag, 171 utils.GoerliFlag, 172 }, 173 Description: "This command looks up the specified database key from the database.", 174 } 175 dbDumpFreezerIndex = cli.Command{ 176 Action: utils.MigrateFlags(freezerInspect), 177 Name: "freezer-index", 178 Usage: "Dump out the index of a given freezer type", 179 ArgsUsage: "<type> <start (int)> <end (int)>", 180 Flags: []cli.Flag{ 181 utils.DataDirFlag, 182 utils.SyncModeFlag, 183 utils.MainnetFlag, 184 utils.RopstenFlag, 185 utils.RinkebyFlag, 186 utils.GoerliFlag, 187 }, 188 Description: "This command displays information about the freezer index.", 189 } 190 ) 191 192 func removeDB(ctx *cli.Context) error { 193 stack, config := makeConfigNode(ctx) 194 195 // Remove the full node state database 196 path := stack.ResolvePath("chaindata") 197 if common.FileExist(path) { 198 confirmAndRemoveDB(path, "full node state database") 199 } else { 200 log.Info("Full node state database missing", "path", path) 201 } 202 // Remove the full node ancient database 203 path = config.Eth.DatabaseFreezer 204 switch { 205 case path == "": 206 path = filepath.Join(stack.ResolvePath("chaindata"), "ancient") 207 case !filepath.IsAbs(path): 208 path = config.Node.ResolvePath(path) 209 } 210 if common.FileExist(path) { 211 confirmAndRemoveDB(path, "full node ancient database") 212 } else { 213 log.Info("Full node ancient database missing", "path", path) 214 } 215 // Remove the light node database 216 path = stack.ResolvePath("lightchaindata") 217 if common.FileExist(path) { 218 confirmAndRemoveDB(path, "light node database") 219 } else { 220 log.Info("Light node database missing", "path", path) 221 } 222 return nil 223 } 224 225 // confirmAndRemoveDB prompts the user for a last confirmation and removes the 226 // folder if accepted. 227 func confirmAndRemoveDB(database string, kind string) { 228 confirm, err := prompt.Stdin.PromptConfirm(fmt.Sprintf("Remove %s (%s)?", kind, database)) 229 switch { 230 case err != nil: 231 utils.Fatalf("%v", err) 232 case !confirm: 233 log.Info("Database deletion skipped", "path", database) 234 default: 235 start := time.Now() 236 filepath.Walk(database, func(path string, info os.FileInfo, err error) error { 237 // If we're at the top level folder, recurse into 238 if path == database { 239 return nil 240 } 241 // Delete all the files, but not subfolders 242 if !info.IsDir() { 243 os.Remove(path) 244 return nil 245 } 246 return filepath.SkipDir 247 }) 248 log.Info("Database successfully deleted", "path", database, "elapsed", common.PrettyDuration(time.Since(start))) 249 } 250 } 251 252 func inspect(ctx *cli.Context) error { 253 var ( 254 prefix []byte 255 start []byte 256 ) 257 if ctx.NArg() > 2 { 258 return fmt.Errorf("Max 2 arguments: %v", ctx.Command.ArgsUsage) 259 } 260 if ctx.NArg() >= 1 { 261 if d, err := hexutil.Decode(ctx.Args().Get(0)); err != nil { 262 return fmt.Errorf("failed to hex-decode 'prefix': %v", err) 263 } else { 264 prefix = d 265 } 266 } 267 if ctx.NArg() >= 2 { 268 if d, err := hexutil.Decode(ctx.Args().Get(1)); err != nil { 269 return fmt.Errorf("failed to hex-decode 'start': %v", err) 270 } else { 271 start = d 272 } 273 } 274 stack, _ := makeConfigNode(ctx) 275 defer stack.Close() 276 277 db := utils.MakeChainDatabase(ctx, stack, true) 278 defer db.Close() 279 280 return rawdb.InspectDatabase(db, prefix, start) 281 } 282 283 func showLeveldbStats(db ethdb.Stater) { 284 if stats, err := db.Stat("leveldb.stats"); err != nil { 285 log.Warn("Failed to read database stats", "error", err) 286 } else { 287 fmt.Println(stats) 288 } 289 if ioStats, err := db.Stat("leveldb.iostats"); err != nil { 290 log.Warn("Failed to read database iostats", "error", err) 291 } else { 292 fmt.Println(ioStats) 293 } 294 } 295 296 func dbStats(ctx *cli.Context) error { 297 stack, _ := makeConfigNode(ctx) 298 defer stack.Close() 299 300 db := utils.MakeChainDatabase(ctx, stack, true) 301 defer db.Close() 302 303 showLeveldbStats(db) 304 return nil 305 } 306 307 func dbCompact(ctx *cli.Context) error { 308 stack, _ := makeConfigNode(ctx) 309 defer stack.Close() 310 311 db := utils.MakeChainDatabase(ctx, stack, false) 312 defer db.Close() 313 314 log.Info("Stats before compaction") 315 showLeveldbStats(db) 316 317 log.Info("Triggering compaction") 318 if err := db.Compact(nil, nil); err != nil { 319 log.Info("Compact err", "error", err) 320 return err 321 } 322 log.Info("Stats after compaction") 323 showLeveldbStats(db) 324 return nil 325 } 326 327 // dbGet shows the value of a given database key 328 func dbGet(ctx *cli.Context) error { 329 if ctx.NArg() != 1 { 330 return fmt.Errorf("required arguments: %v", ctx.Command.ArgsUsage) 331 } 332 stack, _ := makeConfigNode(ctx) 333 defer stack.Close() 334 335 db := utils.MakeChainDatabase(ctx, stack, true) 336 defer db.Close() 337 338 key, err := hexutil.Decode(ctx.Args().Get(0)) 339 if err != nil { 340 log.Info("Could not decode the key", "error", err) 341 return err 342 } 343 data, err := db.Get(key) 344 if err != nil { 345 log.Info("Get operation failed", "error", err) 346 return err 347 } 348 fmt.Printf("key %#x: %#x\n", key, data) 349 return nil 350 } 351 352 // dbDelete deletes a key from the database 353 func dbDelete(ctx *cli.Context) error { 354 if ctx.NArg() != 1 { 355 return fmt.Errorf("required arguments: %v", ctx.Command.ArgsUsage) 356 } 357 stack, _ := makeConfigNode(ctx) 358 defer stack.Close() 359 360 db := utils.MakeChainDatabase(ctx, stack, false) 361 defer db.Close() 362 363 key, err := hexutil.Decode(ctx.Args().Get(0)) 364 if err != nil { 365 log.Info("Could not decode the key", "error", err) 366 return err 367 } 368 data, err := db.Get(key) 369 if err == nil { 370 fmt.Printf("Previous value: %#x\n", data) 371 } 372 if err = db.Delete(key); err != nil { 373 log.Info("Delete operation returned an error", "error", err) 374 return err 375 } 376 return nil 377 } 378 379 // dbPut overwrite a value in the database 380 func dbPut(ctx *cli.Context) error { 381 if ctx.NArg() != 2 { 382 return fmt.Errorf("required arguments: %v", ctx.Command.ArgsUsage) 383 } 384 stack, _ := makeConfigNode(ctx) 385 defer stack.Close() 386 387 db := utils.MakeChainDatabase(ctx, stack, false) 388 defer db.Close() 389 390 var ( 391 key []byte 392 value []byte 393 data []byte 394 err error 395 ) 396 key, err = hexutil.Decode(ctx.Args().Get(0)) 397 if err != nil { 398 log.Info("Could not decode the key", "error", err) 399 return err 400 } 401 value, err = hexutil.Decode(ctx.Args().Get(1)) 402 if err != nil { 403 log.Info("Could not decode the value", "error", err) 404 return err 405 } 406 data, err = db.Get(key) 407 if err == nil { 408 fmt.Printf("Previous value: %#x\n", data) 409 } 410 return db.Put(key, value) 411 } 412 413 // dbDumpTrie shows the key-value slots of a given storage trie 414 func dbDumpTrie(ctx *cli.Context) error { 415 if ctx.NArg() < 1 { 416 return fmt.Errorf("required arguments: %v", ctx.Command.ArgsUsage) 417 } 418 stack, _ := makeConfigNode(ctx) 419 defer stack.Close() 420 421 db := utils.MakeChainDatabase(ctx, stack, true) 422 defer db.Close() 423 var ( 424 root []byte 425 start []byte 426 max = int64(-1) 427 err error 428 ) 429 if root, err = hexutil.Decode(ctx.Args().Get(0)); err != nil { 430 log.Info("Could not decode the root", "error", err) 431 return err 432 } 433 stRoot := common.BytesToHash(root) 434 if ctx.NArg() >= 2 { 435 if start, err = hexutil.Decode(ctx.Args().Get(1)); err != nil { 436 log.Info("Could not decode the seek position", "error", err) 437 return err 438 } 439 } 440 if ctx.NArg() >= 3 { 441 if max, err = strconv.ParseInt(ctx.Args().Get(2), 10, 64); err != nil { 442 log.Info("Could not decode the max count", "error", err) 443 return err 444 } 445 } 446 theTrie, err := trie.New(stRoot, trie.NewDatabase(db)) 447 if err != nil { 448 return err 449 } 450 var count int64 451 it := trie.NewIterator(theTrie.NodeIterator(start)) 452 for it.Next() { 453 if max > 0 && count == max { 454 fmt.Printf("Exiting after %d values\n", count) 455 break 456 } 457 fmt.Printf(" %d. key %#x: %#x\n", count, it.Key, it.Value) 458 count++ 459 } 460 return it.Err 461 } 462 463 func freezerInspect(ctx *cli.Context) error { 464 var ( 465 start, end int64 466 disableSnappy bool 467 err error 468 ) 469 if ctx.NArg() < 3 { 470 return fmt.Errorf("required arguments: %v", ctx.Command.ArgsUsage) 471 } 472 kind := ctx.Args().Get(0) 473 if noSnap, ok := rawdb.FreezerNoSnappy[kind]; !ok { 474 var options []string 475 for opt := range rawdb.FreezerNoSnappy { 476 options = append(options, opt) 477 } 478 sort.Strings(options) 479 return fmt.Errorf("Could read freezer-type '%v'. Available options: %v", kind, options) 480 } else { 481 disableSnappy = noSnap 482 } 483 if start, err = strconv.ParseInt(ctx.Args().Get(1), 10, 64); err != nil { 484 log.Info("Could read start-param", "error", err) 485 return err 486 } 487 if end, err = strconv.ParseInt(ctx.Args().Get(2), 10, 64); err != nil { 488 log.Info("Could read count param", "error", err) 489 return err 490 } 491 stack, _ := makeConfigNode(ctx) 492 defer stack.Close() 493 path := filepath.Join(stack.ResolvePath("chaindata"), "ancient") 494 log.Info("Opening freezer", "location", path, "name", kind) 495 if f, err := rawdb.NewFreezerTable(path, kind, disableSnappy); err != nil { 496 return err 497 } else { 498 f.DumpIndex(start, end) 499 } 500 return nil 501 }