dbcmd.go 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382
  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. "time"
  22. "github.com/ethereum/go-ethereum/cmd/utils"
  23. "github.com/ethereum/go-ethereum/common"
  24. "github.com/ethereum/go-ethereum/common/hexutil"
  25. "github.com/ethereum/go-ethereum/console/prompt"
  26. "github.com/ethereum/go-ethereum/core/rawdb"
  27. "github.com/ethereum/go-ethereum/ethdb"
  28. "github.com/ethereum/go-ethereum/log"
  29. "gopkg.in/urfave/cli.v1"
  30. )
  31. var (
  32. removedbCommand = cli.Command{
  33. Action: utils.MigrateFlags(removeDB),
  34. Name: "removedb",
  35. Usage: "Remove blockchain and state databases",
  36. ArgsUsage: "",
  37. Flags: []cli.Flag{
  38. utils.DataDirFlag,
  39. },
  40. Category: "DATABASE COMMANDS",
  41. Description: `
  42. Remove blockchain and state databases`,
  43. }
  44. dbCommand = cli.Command{
  45. Name: "db",
  46. Usage: "Low level database operations",
  47. ArgsUsage: "",
  48. Category: "DATABASE COMMANDS",
  49. Subcommands: []cli.Command{
  50. dbInspectCmd,
  51. dbStatCmd,
  52. dbCompactCmd,
  53. dbGetCmd,
  54. dbDeleteCmd,
  55. dbPutCmd,
  56. },
  57. }
  58. dbInspectCmd = cli.Command{
  59. Action: utils.MigrateFlags(inspect),
  60. Name: "inspect",
  61. ArgsUsage: "<prefix> <start>",
  62. Flags: []cli.Flag{
  63. utils.DataDirFlag,
  64. utils.SyncModeFlag,
  65. utils.MainnetFlag,
  66. utils.RopstenFlag,
  67. utils.RinkebyFlag,
  68. utils.GoerliFlag,
  69. utils.YoloV3Flag,
  70. },
  71. Usage: "Inspect the storage size for each type of data in the database",
  72. 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.`,
  73. }
  74. dbStatCmd = cli.Command{
  75. Action: utils.MigrateFlags(dbStats),
  76. Name: "stats",
  77. Usage: "Print leveldb statistics",
  78. Flags: []cli.Flag{
  79. utils.DataDirFlag,
  80. utils.SyncModeFlag,
  81. utils.MainnetFlag,
  82. utils.RopstenFlag,
  83. utils.RinkebyFlag,
  84. utils.GoerliFlag,
  85. utils.YoloV3Flag,
  86. },
  87. }
  88. dbCompactCmd = cli.Command{
  89. Action: utils.MigrateFlags(dbCompact),
  90. Name: "compact",
  91. Usage: "Compact leveldb database. WARNING: May take a very long time",
  92. Flags: []cli.Flag{
  93. utils.DataDirFlag,
  94. utils.SyncModeFlag,
  95. utils.MainnetFlag,
  96. utils.RopstenFlag,
  97. utils.RinkebyFlag,
  98. utils.GoerliFlag,
  99. utils.YoloV3Flag,
  100. utils.CacheFlag,
  101. utils.CacheDatabaseFlag,
  102. },
  103. Description: `This command performs a database compaction.
  104. WARNING: This operation may take a very long time to finish, and may cause database
  105. corruption if it is aborted during execution'!`,
  106. }
  107. dbGetCmd = cli.Command{
  108. Action: utils.MigrateFlags(dbGet),
  109. Name: "get",
  110. Usage: "Show the value of a database key",
  111. ArgsUsage: "<hex-encoded key>",
  112. Flags: []cli.Flag{
  113. utils.DataDirFlag,
  114. utils.SyncModeFlag,
  115. utils.MainnetFlag,
  116. utils.RopstenFlag,
  117. utils.RinkebyFlag,
  118. utils.GoerliFlag,
  119. utils.YoloV3Flag,
  120. },
  121. Description: "This command looks up the specified database key from the database.",
  122. }
  123. dbDeleteCmd = cli.Command{
  124. Action: utils.MigrateFlags(dbDelete),
  125. Name: "delete",
  126. Usage: "Delete a database key (WARNING: may corrupt your database)",
  127. ArgsUsage: "<hex-encoded key>",
  128. Flags: []cli.Flag{
  129. utils.DataDirFlag,
  130. utils.SyncModeFlag,
  131. utils.MainnetFlag,
  132. utils.RopstenFlag,
  133. utils.RinkebyFlag,
  134. utils.GoerliFlag,
  135. utils.YoloV3Flag,
  136. },
  137. Description: `This command deletes the specified database key from the database.
  138. WARNING: This is a low-level operation which may cause database corruption!`,
  139. }
  140. dbPutCmd = cli.Command{
  141. Action: utils.MigrateFlags(dbPut),
  142. Name: "put",
  143. Usage: "Set the value of a database key (WARNING: may corrupt your database)",
  144. ArgsUsage: "<hex-encoded key> <hex-encoded value>",
  145. Flags: []cli.Flag{
  146. utils.DataDirFlag,
  147. utils.SyncModeFlag,
  148. utils.MainnetFlag,
  149. utils.RopstenFlag,
  150. utils.RinkebyFlag,
  151. utils.GoerliFlag,
  152. utils.YoloV3Flag,
  153. },
  154. Description: `This command sets a given database key to the given value.
  155. WARNING: This is a low-level operation which may cause database corruption!`,
  156. }
  157. )
  158. func removeDB(ctx *cli.Context) error {
  159. stack, config := makeConfigNode(ctx)
  160. // Remove the full node state database
  161. path := stack.ResolvePath("chaindata")
  162. if common.FileExist(path) {
  163. confirmAndRemoveDB(path, "full node state database")
  164. } else {
  165. log.Info("Full node state database missing", "path", path)
  166. }
  167. // Remove the full node ancient database
  168. path = config.Eth.DatabaseFreezer
  169. switch {
  170. case path == "":
  171. path = filepath.Join(stack.ResolvePath("chaindata"), "ancient")
  172. case !filepath.IsAbs(path):
  173. path = config.Node.ResolvePath(path)
  174. }
  175. if common.FileExist(path) {
  176. confirmAndRemoveDB(path, "full node ancient database")
  177. } else {
  178. log.Info("Full node ancient database missing", "path", path)
  179. }
  180. // Remove the light node database
  181. path = stack.ResolvePath("lightchaindata")
  182. if common.FileExist(path) {
  183. confirmAndRemoveDB(path, "light node database")
  184. } else {
  185. log.Info("Light node database missing", "path", path)
  186. }
  187. return nil
  188. }
  189. // confirmAndRemoveDB prompts the user for a last confirmation and removes the
  190. // folder if accepted.
  191. func confirmAndRemoveDB(database string, kind string) {
  192. confirm, err := prompt.Stdin.PromptConfirm(fmt.Sprintf("Remove %s (%s)?", kind, database))
  193. switch {
  194. case err != nil:
  195. utils.Fatalf("%v", err)
  196. case !confirm:
  197. log.Info("Database deletion skipped", "path", database)
  198. default:
  199. start := time.Now()
  200. filepath.Walk(database, func(path string, info os.FileInfo, err error) error {
  201. // If we're at the top level folder, recurse into
  202. if path == database {
  203. return nil
  204. }
  205. // Delete all the files, but not subfolders
  206. if !info.IsDir() {
  207. os.Remove(path)
  208. return nil
  209. }
  210. return filepath.SkipDir
  211. })
  212. log.Info("Database successfully deleted", "path", database, "elapsed", common.PrettyDuration(time.Since(start)))
  213. }
  214. }
  215. func inspect(ctx *cli.Context) error {
  216. var (
  217. prefix []byte
  218. start []byte
  219. )
  220. if ctx.NArg() > 2 {
  221. return fmt.Errorf("Max 2 arguments: %v", ctx.Command.ArgsUsage)
  222. }
  223. if ctx.NArg() >= 1 {
  224. if d, err := hexutil.Decode(ctx.Args().Get(0)); err != nil {
  225. return fmt.Errorf("failed to hex-decode 'prefix': %v", err)
  226. } else {
  227. prefix = d
  228. }
  229. }
  230. if ctx.NArg() >= 2 {
  231. if d, err := hexutil.Decode(ctx.Args().Get(1)); err != nil {
  232. return fmt.Errorf("failed to hex-decode 'start': %v", err)
  233. } else {
  234. start = d
  235. }
  236. }
  237. stack, _ := makeConfigNode(ctx)
  238. defer stack.Close()
  239. db := utils.MakeChainDatabase(ctx, stack, true)
  240. defer db.Close()
  241. return rawdb.InspectDatabase(db, prefix, start)
  242. }
  243. func showLeveldbStats(db ethdb.Stater) {
  244. if stats, err := db.Stat("leveldb.stats"); err != nil {
  245. log.Warn("Failed to read database stats", "error", err)
  246. } else {
  247. fmt.Println(stats)
  248. }
  249. if ioStats, err := db.Stat("leveldb.iostats"); err != nil {
  250. log.Warn("Failed to read database iostats", "error", err)
  251. } else {
  252. fmt.Println(ioStats)
  253. }
  254. }
  255. func dbStats(ctx *cli.Context) error {
  256. stack, _ := makeConfigNode(ctx)
  257. defer stack.Close()
  258. db := utils.MakeChainDatabase(ctx, stack, true)
  259. defer db.Close()
  260. showLeveldbStats(db)
  261. return nil
  262. }
  263. func dbCompact(ctx *cli.Context) error {
  264. stack, _ := makeConfigNode(ctx)
  265. defer stack.Close()
  266. db := utils.MakeChainDatabase(ctx, stack, false)
  267. defer db.Close()
  268. log.Info("Stats before compaction")
  269. showLeveldbStats(db)
  270. log.Info("Triggering compaction")
  271. if err := db.Compact(nil, nil); err != nil {
  272. log.Info("Compact err", "error", err)
  273. return err
  274. }
  275. log.Info("Stats after compaction")
  276. showLeveldbStats(db)
  277. return nil
  278. }
  279. // dbGet shows the value of a given database key
  280. func dbGet(ctx *cli.Context) error {
  281. if ctx.NArg() != 1 {
  282. return fmt.Errorf("required arguments: %v", ctx.Command.ArgsUsage)
  283. }
  284. stack, _ := makeConfigNode(ctx)
  285. defer stack.Close()
  286. db := utils.MakeChainDatabase(ctx, stack, true)
  287. defer db.Close()
  288. key, err := hexutil.Decode(ctx.Args().Get(0))
  289. if err != nil {
  290. log.Info("Could not decode the key", "error", err)
  291. return err
  292. }
  293. data, err := db.Get(key)
  294. if err != nil {
  295. log.Info("Get operation failed", "error", err)
  296. return err
  297. }
  298. fmt.Printf("key %#x: %#x\n", key, data)
  299. return nil
  300. }
  301. // dbDelete deletes a key from the database
  302. func dbDelete(ctx *cli.Context) error {
  303. if ctx.NArg() != 1 {
  304. return fmt.Errorf("required arguments: %v", ctx.Command.ArgsUsage)
  305. }
  306. stack, _ := makeConfigNode(ctx)
  307. defer stack.Close()
  308. db := utils.MakeChainDatabase(ctx, stack, false)
  309. defer db.Close()
  310. key, err := hexutil.Decode(ctx.Args().Get(0))
  311. if err != nil {
  312. log.Info("Could not decode the key", "error", err)
  313. return err
  314. }
  315. data, err := db.Get(key)
  316. if err == nil {
  317. fmt.Printf("Previous value: %#x\n", data)
  318. }
  319. if err = db.Delete(key); err != nil {
  320. log.Info("Delete operation returned an error", "error", err)
  321. return err
  322. }
  323. return nil
  324. }
  325. // dbPut overwrite a value in the database
  326. func dbPut(ctx *cli.Context) error {
  327. if ctx.NArg() != 2 {
  328. return fmt.Errorf("required arguments: %v", ctx.Command.ArgsUsage)
  329. }
  330. stack, _ := makeConfigNode(ctx)
  331. defer stack.Close()
  332. db := utils.MakeChainDatabase(ctx, stack, false)
  333. defer db.Close()
  334. var (
  335. key []byte
  336. value []byte
  337. data []byte
  338. err error
  339. )
  340. key, err = hexutil.Decode(ctx.Args().Get(0))
  341. if err != nil {
  342. log.Info("Could not decode the key", "error", err)
  343. return err
  344. }
  345. value, err = hexutil.Decode(ctx.Args().Get(1))
  346. if err != nil {
  347. log.Info("Could not decode the value", "error", err)
  348. return err
  349. }
  350. data, err = db.Get(key)
  351. if err == nil {
  352. fmt.Printf("Previous value: %#x\n", data)
  353. }
  354. return db.Put(key, value)
  355. }