github.com/bcskill/bcschain/v3@v3.4.9-beta2/cmd/gochain/chaincmd.go (about) 1 // Copyright 2015 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 17 package main 18 19 import ( 20 "context" 21 "encoding/json" 22 "fmt" 23 "os" 24 "runtime" 25 "strconv" 26 "sync/atomic" 27 "time" 28 29 "github.com/bcskill/bcschain/v3/console/prompt" 30 31 "github.com/bcskill/bcschain/v3/cmd/utils" 32 "github.com/bcskill/bcschain/v3/common" 33 "github.com/bcskill/bcschain/v3/core" 34 "github.com/bcskill/bcschain/v3/core/state" 35 "github.com/bcskill/bcschain/v3/core/types" 36 "github.com/bcskill/bcschain/v3/eth/downloader" 37 "github.com/bcskill/bcschain/v3/ethdb" 38 "github.com/bcskill/bcschain/v3/log" 39 "github.com/urfave/cli" 40 ) 41 42 var ( 43 initCommand = cli.Command{ 44 Action: utils.MigrateFlags(initGenesis), 45 Name: "init", 46 Usage: "Bootstrap and initialize a new genesis block", 47 ArgsUsage: "<genesisPath>", 48 Flags: []cli.Flag{ 49 utils.DataDirFlag, 50 utils.LightModeFlag, 51 }, 52 Category: "BLOCKCHAIN COMMANDS", 53 Description: ` 54 The init command initializes a new genesis block and definition for the network. 55 This is a destructive action and changes the network in which you will be 56 participating. 57 58 It expects the genesis file as argument.`, 59 } 60 importCommand = cli.Command{ 61 Action: utils.MigrateFlags(importChain), 62 Name: "import", 63 Usage: "Import a blockchain file", 64 ArgsUsage: "<filename> (<filename 2> ... <filename N>) ", 65 Flags: []cli.Flag{ 66 utils.DataDirFlag, 67 utils.CacheFlag, 68 utils.LightModeFlag, 69 utils.GCModeFlag, 70 utils.CacheDatabaseFlag, 71 utils.CacheGCFlag, 72 }, 73 Category: "BLOCKCHAIN COMMANDS", 74 Description: ` 75 The import command imports blocks from an RLP-encoded form. The form can be one file 76 with several RLP-encoded blocks, or several files can be used. 77 78 If only one file is used, import error will result in failure. If several files are used, 79 processing will proceed even if an individual RLP-file import failure occurs.`, 80 } 81 exportCommand = cli.Command{ 82 Action: utils.MigrateFlags(exportChain), 83 Name: "export", 84 Usage: "Export blockchain into file", 85 ArgsUsage: "<filename> [<blockNumFirst> <blockNumLast>]", 86 Flags: []cli.Flag{ 87 utils.DataDirFlag, 88 utils.CacheFlag, 89 utils.LightModeFlag, 90 }, 91 Category: "BLOCKCHAIN COMMANDS", 92 Description: ` 93 Requires a first argument of the file to write to. 94 Optional second and third arguments control the first and 95 last block to write. In this mode, the file will be appended 96 if already existing.`, 97 } 98 copydbCommand = cli.Command{ 99 Action: utils.MigrateFlags(copyDb), 100 Name: "copydb", 101 Usage: "Create a local chain from a target chaindata folder", 102 ArgsUsage: "<sourceChaindataDir>", 103 Flags: []cli.Flag{ 104 utils.DataDirFlag, 105 utils.CacheFlag, 106 utils.SyncModeFlag, 107 utils.FakePoWFlag, 108 utils.TestnetFlag, 109 }, 110 Category: "BLOCKCHAIN COMMANDS", 111 Description: ` 112 The first argument must be the directory containing the blockchain to download from`, 113 } 114 removedbCommand = cli.Command{ 115 Action: utils.MigrateFlags(removeDB), 116 Name: "removedb", 117 Usage: "Remove blockchain and state databases", 118 ArgsUsage: " ", 119 Flags: []cli.Flag{ 120 utils.DataDirFlag, 121 utils.LightModeFlag, 122 }, 123 Category: "BLOCKCHAIN COMMANDS", 124 Description: ` 125 Remove blockchain and state databases`, 126 } 127 dumpCommand = cli.Command{ 128 Action: utils.MigrateFlags(dump), 129 Name: "dump", 130 Usage: "Dump a specific block from storage", 131 ArgsUsage: "[<blockHash> | <blockNum>]...", 132 Flags: []cli.Flag{ 133 utils.DataDirFlag, 134 utils.CacheFlag, 135 utils.LightModeFlag, 136 }, 137 Category: "BLOCKCHAIN COMMANDS", 138 Description: ` 139 The arguments are interpreted as block numbers or hashes. 140 Use "ethereum dump 0" to dump the genesis block.`, 141 } 142 ) 143 144 // initGenesis will initialise the given JSON format genesis file and writes it as 145 // the zero'd block (i.e. genesis) or will fail hard if it can't succeed. 146 func initGenesis(ctx *cli.Context) error { 147 var genesis *core.Genesis 148 genesisPath := ctx.Args().First() 149 if len(genesisPath) > 0 { 150 file, err := os.Open(genesisPath) 151 if err != nil { 152 utils.Fatalf("Failed to read genesis file: %v", err) 153 } 154 defer file.Close() 155 156 genesis = new(core.Genesis) 157 if err := json.NewDecoder(file).Decode(genesis); err != nil { 158 utils.Fatalf("invalid genesis file: %v", err) 159 } 160 } else { 161 genesis = utils.MakeGenesis(ctx) 162 if genesis == nil { 163 utils.Fatalf("No default genesis available.") 164 } 165 } 166 // Open an initialise both full and light databases 167 stack := makeFullNode(ctx) 168 for _, name := range []string{"chaindata", "lightchaindata"} { 169 chaindb, err := stack.OpenDatabase(name, 0, 0) 170 if err != nil { 171 utils.Fatalf("Failed to open database: %v", err) 172 } 173 _, hash, err := core.SetupGenesisBlock(chaindb, genesis) 174 if err != nil { 175 utils.Fatalf("Failed to write genesis block: %v", err) 176 } 177 log.Info("Successfully wrote genesis state", "database", name, "hash", hash) 178 } 179 return nil 180 } 181 182 func importChain(ctx *cli.Context) error { 183 if len(ctx.Args()) < 1 { 184 utils.Fatalf("This command requires an argument.") 185 } 186 stack := makeFullNode(ctx) 187 chain, chainDb := utils.MakeChain(ctx, stack) 188 defer chainDb.Close() 189 190 // Start periodically gathering memory profiles 191 var peakMemAlloc, peakMemSys uint64 192 go func() { 193 stats := new(runtime.MemStats) 194 for { 195 runtime.ReadMemStats(stats) 196 if atomic.LoadUint64(&peakMemAlloc) < stats.Alloc { 197 atomic.StoreUint64(&peakMemAlloc, stats.Alloc) 198 } 199 if atomic.LoadUint64(&peakMemSys) < stats.Sys { 200 atomic.StoreUint64(&peakMemSys, stats.Sys) 201 } 202 time.Sleep(5 * time.Second) 203 } 204 }() 205 // Import the chain 206 start := time.Now() 207 208 if len(ctx.Args()) == 1 { 209 if err := utils.ImportChain(context.TODO(), chain, ctx.Args().First()); err != nil { 210 log.Error("Import error", "err", err) 211 } 212 } else { 213 for _, arg := range ctx.Args() { 214 if err := utils.ImportChain(context.TODO(), chain, arg); err != nil { 215 log.Error("Import error", "file", arg, "err", err) 216 } 217 } 218 } 219 chain.Stop() 220 fmt.Printf("Import done in %v.\n\n", time.Since(start)) 221 222 /* 223 // Output pre-compaction stats mostly to see the import trashing 224 db := chainDb.(*ethdb.LDBDatabase) 225 226 stats, err := db.LDB().GetProperty("leveldb.stats") 227 if err != nil { 228 utils.Fatalf("Failed to read database stats: %v", err) 229 } 230 fmt.Println(stats) 231 fmt.Printf("Trie cache misses: %d\n", trie.CacheMisses()) 232 fmt.Printf("Trie cache unloads: %d\n\n", trie.CacheUnloads()) 233 234 // Print the memory statistics used by the importing 235 mem := new(runtime.MemStats) 236 runtime.ReadMemStats(mem) 237 238 fmt.Printf("Object memory: %.3f MB current, %.3f MB peak\n", float64(mem.Alloc)/1024/1024, float64(atomic.LoadUint64(&peakMemAlloc))/1024/1024) 239 fmt.Printf("System memory: %.3f MB current, %.3f MB peak\n", float64(mem.Sys)/1024/1024, float64(atomic.LoadUint64(&peakMemSys))/1024/1024) 240 fmt.Printf("Allocations: %.3f million\n", float64(mem.Mallocs)/1000000) 241 fmt.Printf("GC pause: %v\n\n", time.Duration(mem.PauseTotalNs)) 242 243 if ctx.GlobalIsSet(utils.NoCompactionFlag.Name) { 244 return nil 245 } 246 247 // Compact the entire database to more accurately measure disk io and print the stats 248 start = time.Now() 249 fmt.Println("Compacting entire database...") 250 if err = db.LDB().CompactRange(util.Range{}); err != nil { 251 utils.Fatalf("Compaction failed: %v", err) 252 } 253 fmt.Printf("Compaction done in %v.\n\n", time.Since(start)) 254 255 stats, err = db.LDB().GetProperty("leveldb.stats") 256 if err != nil { 257 utils.Fatalf("Failed to read database stats: %v", err) 258 } 259 fmt.Println(stats) 260 */ 261 262 return nil 263 } 264 265 func exportChain(ctx *cli.Context) error { 266 if len(ctx.Args()) < 1 { 267 utils.Fatalf("This command requires an argument.") 268 } 269 stack := makeFullNode(ctx) 270 chain, _ := utils.MakeChain(ctx, stack) 271 start := time.Now() 272 273 var err error 274 fp := ctx.Args().First() 275 if len(ctx.Args()) < 3 { 276 err = utils.ExportChain(chain, fp) 277 } else { 278 // This can be improved to allow for numbers larger than 9223372036854775807 279 first, ferr := strconv.ParseInt(ctx.Args().Get(1), 10, 64) 280 last, lerr := strconv.ParseInt(ctx.Args().Get(2), 10, 64) 281 if ferr != nil || lerr != nil { 282 utils.Fatalf("Export error in parsing parameters: block number not an integer\n") 283 } 284 if first < 0 || last < 0 { 285 utils.Fatalf("Export error: block number must be greater than 0\n") 286 } 287 err = utils.ExportAppendChain(chain, fp, uint64(first), uint64(last)) 288 } 289 290 if err != nil { 291 utils.Fatalf("Export error: %v\n", err) 292 } 293 fmt.Printf("Export done in %v", time.Since(start)) 294 return nil 295 } 296 297 func copyDb(ctx *cli.Context) error { 298 // Ensure we have a source chain directory to copy 299 if len(ctx.Args()) != 1 { 300 utils.Fatalf("Source chaindata directory path argument missing") 301 } 302 // Initialize a new chain for the running node to sync into 303 stack := makeFullNode(ctx) 304 chain, chainDb := utils.MakeChain(ctx, stack) 305 306 syncmode := *utils.GlobalTextMarshaler(ctx, utils.SyncModeFlag.Name).(*downloader.SyncMode) 307 dl := downloader.New(syncmode, chainDb, new(core.InterfaceFeed), chain, nil, nil) 308 309 // Create a source peer to satisfy downloader requests from 310 db := ethdb.NewDB(ctx.Args().First()) 311 if err := db.Open(); err != nil { 312 return err 313 } 314 hc, err := core.NewHeaderChain(db, chain.Config(), chain.Engine(), func() bool { return false }) 315 if err != nil { 316 return err 317 } 318 peer := downloader.NewFakePeer("local", db, hc, dl) 319 if err = dl.RegisterPeer("local", 63, peer); err != nil { 320 return err 321 } 322 // Synchronise with the simulated peer 323 start := time.Now() 324 325 currentHeader := hc.CurrentHeader() 326 if err = dl.Synchronise("local", currentHeader.Hash(), hc.GetTd(currentHeader.Hash(), currentHeader.Number.Uint64()), syncmode); err != nil { 327 return err 328 } 329 for dl.Synchronising() { 330 time.Sleep(10 * time.Millisecond) 331 } 332 fmt.Printf("Database copy done in %v\n", time.Since(start)) 333 334 // Compact the entire database to remove any sync overhead 335 /* 336 start = time.Now() 337 fmt.Println("Compacting entire database...") 338 if err = chainDb.(*ethdb.LDBDatabase).LDB().CompactRange(util.Range{}); err != nil { 339 utils.Fatalf("Compaction failed: %v", err) 340 } 341 fmt.Printf("Compaction done in %v.\n\n", time.Since(start)) 342 */ 343 344 return nil 345 } 346 347 func removeDB(ctx *cli.Context) error { 348 stack, _ := makeConfigNode(ctx) 349 350 for _, name := range []string{"chaindata", "lightchaindata"} { 351 // Ensure the database exists in the first place 352 logger := log.New("database", name) 353 354 dbdir := stack.ResolvePath(name) 355 if !common.FileExist(dbdir) { 356 logger.Info("Database doesn't exist, skipping", "path", dbdir) 357 continue 358 } 359 // Confirm removal and execute 360 fmt.Println(dbdir) 361 confirm, err := prompt.Stdin.PromptConfirm("Remove this database?") 362 switch { 363 case err != nil: 364 utils.Fatalf("%v", err) 365 case !confirm: 366 logger.Warn("Database deletion aborted") 367 default: 368 start := time.Now() 369 os.RemoveAll(dbdir) 370 logger.Info("Database successfully deleted", "elapsed", common.PrettyDuration(time.Since(start))) 371 } 372 } 373 return nil 374 } 375 376 func dump(ctx *cli.Context) error { 377 stack := makeFullNode(ctx) 378 chain, chainDb := utils.MakeChain(ctx, stack) 379 for _, arg := range ctx.Args() { 380 var block *types.Block 381 if hashish(arg) { 382 block = chain.GetBlockByHash(common.HexToHash(arg)) 383 } else { 384 num, _ := strconv.Atoi(arg) 385 block = chain.GetBlockByNumber(uint64(num)) 386 } 387 if block == nil { 388 fmt.Println("{}") 389 utils.Fatalf("block not found") 390 } else { 391 state, err := state.New(block.Root(), state.NewDatabase(chainDb)) 392 if err != nil { 393 utils.Fatalf("could not create new state: %v", err) 394 } 395 fmt.Printf("%s\n", state.Dump()) 396 } 397 } 398 chainDb.Close() 399 return nil 400 } 401 402 // hashish returns true for strings that look like hashes. 403 func hashish(x string) bool { 404 _, err := strconv.Atoi(x) 405 return err != nil 406 }