dbcmd.go 9.5 KB

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