dbcmd.go 14 KB

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