dbcmd.go 13 KB

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