func makedag(ctx *cli.Context) { utils.CheckLegalese(ctx.GlobalString(utils.DataDirFlag.Name)) args := ctx.Args() wrongArgs := func() { utils.Fatalf(`Usage: shift makedag <block number> <outputdir>`) } switch { case len(args) == 2: blockNum, err := strconv.ParseUint(args[0], 0, 64) dir := args[1] if err != nil { wrongArgs() } else { dir = filepath.Clean(dir) // seems to require a trailing slash if !strings.HasSuffix(dir, "/") { dir = dir + "/" } _, err = ioutil.ReadDir(dir) if err != nil { utils.Fatalf("Can't find dir") } fmt.Println("making DAG, this could take awhile...") ethash.MakeDAG(blockNum, dir) } default: wrongArgs() } }
func upgradeDB(ctx *cli.Context) { glog.Infoln("Upgrading blockchain database") chain, chainDb := utils.MakeChain(ctx) v, _ := chainDb.Get([]byte("BlockchainVersion")) bcVersion := int(common.NewValue(v).Uint()) if bcVersion == 0 { bcVersion = core.BlockChainVersion } // Export the current chain. filename := fmt.Sprintf("blockchain_%d_%s.chain", bcVersion, time.Now().Format("20060102_150405")) exportFile := filepath.Join(ctx.GlobalString(utils.DataDirFlag.Name), filename) if err := utils.ExportChain(chain, exportFile); err != nil { utils.Fatalf("Unable to export chain for reimport %s", err) } chainDb.Close() os.RemoveAll(filepath.Join(ctx.GlobalString(utils.DataDirFlag.Name), "chaindata")) // Import the chain file. chain, chainDb = utils.MakeChain(ctx) chainDb.Put([]byte("BlockchainVersion"), common.NewValue(core.BlockChainVersion).Bytes()) err := utils.ImportChain(chain, exportFile) chainDb.Close() if err != nil { utils.Fatalf("Import error %v (a backup is made in %s, use the import command to import it)", err, exportFile) } else { os.Remove(exportFile) glog.Infoln("Import finished") } }
func startEth(ctx *cli.Context, eth *eth.Ethereum) { // Start Ethereum itself utils.StartEthereum(eth) am := eth.AccountManager() account := ctx.GlobalString(utils.UnlockedAccountFlag.Name) accounts := strings.Split(account, " ") for i, account := range accounts { if len(account) > 0 { if account == "primary" { utils.Fatalf("the 'primary' keyword is deprecated. You can use integer indexes, but the indexes are not permanent, they can change if you add external keys, export your keys or copy your keystore to another node.") } unlockAccount(ctx, am, account, i) } } // Start auxiliary services if enabled. if !ctx.GlobalBool(utils.IPCDisabledFlag.Name) { if err := utils.StartIPC(eth, ctx); err != nil { utils.Fatalf("Error string IPC: %v", err) } } if ctx.GlobalBool(utils.RPCEnabledFlag.Name) { if err := utils.StartRPC(eth, ctx); err != nil { utils.Fatalf("Error starting RPC: %v", err) } } if ctx.GlobalBool(utils.MiningEnabledFlag.Name) { if err := eth.StartMining(ctx.GlobalInt(utils.MinerThreadsFlag.Name)); err != nil { utils.Fatalf("%v", err) } } }
func exportChain(ctx *cli.Context) { if len(ctx.Args()) < 1 { utils.Fatalf("This command requires an argument.") } chain, _ := utils.MakeChain(ctx) start := time.Now() var err error fp := ctx.Args().First() if len(ctx.Args()) < 3 { err = utils.ExportChain(chain, fp) } else { // This can be improved to allow for numbers larger than 9223372036854775807 first, ferr := strconv.ParseInt(ctx.Args().Get(1), 10, 64) last, lerr := strconv.ParseInt(ctx.Args().Get(2), 10, 64) if ferr != nil || lerr != nil { utils.Fatalf("Export error in parsing parameters: block number not an integer\n") } if first < 0 || last < 0 { utils.Fatalf("Export error: block number must be greater than 0\n") } err = utils.ExportAppendChain(chain, fp, uint64(first), uint64(last)) } if err != nil { utils.Fatalf("Export error: %v\n", err) } fmt.Printf("Export done in %v", time.Since(start)) }
// resolveMetrics takes a single of input metric pattern, and resolves it to one // or more canonical metric names. func resolveMetric(metrics map[string]interface{}, pattern string, path string) []string { results := []string{} // If a nested metric was requested, recurse optionally branching (via comma) parts := strings.SplitN(pattern, "/", 2) if len(parts) > 1 { for _, variation := range strings.Split(parts[0], ",") { if submetrics, ok := metrics[variation].(map[string]interface{}); !ok { utils.Fatalf("Failed to retrieve system metrics: %s", path+variation) return nil } else { results = append(results, resolveMetric(submetrics, parts[1], path+variation+"/")...) } } return results } // Depending what the last link is, return or expand for _, variation := range strings.Split(pattern, ",") { switch metric := metrics[variation].(type) { case float64: // Final metric value found, return as singleton results = append(results, path+variation) case map[string]interface{}: results = append(results, expandMetrics(metric, path+variation+"/")...) default: utils.Fatalf("Metric pattern resolved to unexpected type: %v", reflect.TypeOf(metric)) return nil } } return results }
func runBlockTest(ctx *cli.Context) { var ( file, testname string rpc bool ) args := ctx.Args() switch { case len(args) == 1: file = args[0] case len(args) == 2: file, testname = args[0], args[1] case len(args) == 3: file, testname = args[0], args[1] rpc = true default: utils.Fatalf(`Usage: shift blocktest <path-to-test-file> [ <test-name> [ "rpc" ] ]`) } bt, err := tests.LoadBlockTests(file) if err != nil { utils.Fatalf("%v", err) } // run all tests if no test name is specified if testname == "" { ecode := 0 for name, test := range bt { fmt.Printf("----------------- Running Block Test %q\n", name) ethereum, err := runOneBlockTest(ctx, test) if err != nil { fmt.Println(err) fmt.Println("FAIL") ecode = 1 } if ethereum != nil { ethereum.Stop() ethereum.WaitForShutdown() } } os.Exit(ecode) return } // otherwise, run the given test test, ok := bt[testname] if !ok { utils.Fatalf("Test file does not contain test named %q", testname) } ethereum, err := runOneBlockTest(ctx, test) if err != nil { utils.Fatalf("%v", err) } defer ethereum.Stop() if rpc { fmt.Println("Block Test post state validated, starting RPC interface.") startEth(ctx, ethereum) utils.StartRPC(ethereum, ctx) ethereum.WaitForShutdown() } }
func importChain(ctx *cli.Context) { if len(ctx.Args()) != 1 { utils.Fatalf("This command requires an argument.") } chain, chainDb := utils.MakeChain(ctx) start := time.Now() err := utils.ImportChain(chain, ctx.Args().First()) chainDb.Close() if err != nil { utils.Fatalf("Import error: %v", err) } fmt.Printf("Import done in %v", time.Since(start)) }
func accountImport(ctx *cli.Context) { utils.CheckLegalese(ctx.GlobalString(utils.DataDirFlag.Name)) keyfile := ctx.Args().First() if len(keyfile) == 0 { utils.Fatalf("keyfile must be given as argument") } am := utils.MakeAccountManager(ctx) passphrase := getPassPhrase(ctx, "Your new account is locked with a password. Please give a password. Do not forget this password.", true, 0) acct, err := am.Import(keyfile, passphrase) if err != nil { utils.Fatalf("Could not create the account: %v", err) } fmt.Printf("Address: %x\n", acct) }
func console(ctx *cli.Context) { utils.CheckLegalese(ctx.GlobalString(utils.DataDirFlag.Name)) cfg := utils.MakeEthConfig(ClientIdentifier, nodeNameVersion, ctx) ethereum, err := eth.New(cfg) if err != nil { utils.Fatalf("%v", err) } client := comms.NewInProcClient(codec.JSON) startEth(ctx, ethereum) repl := newJSRE( ethereum, ctx.GlobalString(utils.JSpathFlag.Name), ctx.GlobalString(utils.RPCCORSDomainFlag.Name), client, true, nil, ) if ctx.GlobalString(utils.ExecFlag.Name) != "" { repl.batch(ctx.GlobalString(utils.ExecFlag.Name)) } else { repl.welcome() repl.interactive() } ethereum.Stop() ethereum.WaitForShutdown() }
func attach(ctx *cli.Context) { utils.CheckLegalese(ctx.GlobalString(utils.DataDirFlag.Name)) var client comms.EthereumClient var err error if ctx.Args().Present() { client, err = comms.ClientFromEndpoint(ctx.Args().First(), codec.JSON) } else { cfg := comms.IpcConfig{ Endpoint: ctx.GlobalString(utils.IPCPathFlag.Name), } client, err = comms.NewIpcClient(cfg, codec.JSON) } if err != nil { utils.Fatalf("Unable to attach to shift node - %v", err) } repl := newLightweightJSRE( ctx.GlobalString(utils.JSpathFlag.Name), client, true, ) if ctx.GlobalString(utils.ExecFlag.Name) != "" { repl.batch(ctx.GlobalString(utils.ExecFlag.Name)) } else { repl.welcome() repl.interactive() } }
func accountUpdate(ctx *cli.Context) { utils.CheckLegalese(ctx.GlobalString(utils.DataDirFlag.Name)) am := utils.MakeAccountManager(ctx) arg := ctx.Args().First() if len(arg) == 0 { utils.Fatalf("account address or index must be given as argument") } addr, authFrom := unlockAccount(ctx, am, arg, 0) authTo := getPassPhrase(ctx, "Please give a new password. Do not forget this password.", true, 0) err := am.Update(common.HexToAddress(addr), authFrom, authTo) if err != nil { utils.Fatalf("Could not update the account: %v", err) } }
func execJSFiles(ctx *cli.Context) { utils.CheckLegalese(ctx.GlobalString(utils.DataDirFlag.Name)) cfg := utils.MakeEthConfig(ClientIdentifier, nodeNameVersion, ctx) ethereum, err := eth.New(cfg) if err != nil { utils.Fatalf("%v", err) } client := comms.NewInProcClient(codec.JSON) startEth(ctx, ethereum) repl := newJSRE( ethereum, ctx.GlobalString(utils.JSpathFlag.Name), ctx.GlobalString(utils.RPCCORSDomainFlag.Name), client, false, nil, ) for _, file := range ctx.Args() { repl.exec(file) } ethereum.Stop() ethereum.WaitForShutdown() }
func newLightweightJSRE(libPath string, client comms.EthereumClient, interactive bool) *jsre { js := &jsre{ps1: "> "} js.wait = make(chan *big.Int) js.client = client js.ds = docserver.New("/") // update state in separare forever blocks js.re = re.New(libPath) if err := js.apiBindings(js); err != nil { utils.Fatalf("Unable to initialize console - %v", err) } if !liner.TerminalSupported() || !interactive { js.prompter = dumbterm{bufio.NewReader(os.Stdin)} } else { lr := liner.NewLiner() js.withHistory(func(hist *os.File) { lr.ReadHistory(hist) }) lr.SetCtrlCAborts(true) js.loadAutoCompletion() lr.SetWordCompleter(apiWordCompleter) lr.SetTabCompletionStyle(liner.TabPrints) js.prompter = lr js.atexit = func() { js.withHistory(func(hist *os.File) { hist.Truncate(0); lr.WriteHistory(hist) }) lr.Close() close(js.wait) } } return js }
func accountCreate(ctx *cli.Context) { utils.CheckLegalese(ctx.GlobalString(utils.DataDirFlag.Name)) am := utils.MakeAccountManager(ctx) passphrase := getPassPhrase(ctx, "Your new account is locked with a password. Please give a password. Do not forget this password.", true, 0) acct, err := am.NewAccount(passphrase) if err != nil { utils.Fatalf("Could not create the account: %v", err) } fmt.Printf("Address: %x\n", acct) }
func accountList(ctx *cli.Context) { utils.CheckLegalese(ctx.GlobalString(utils.DataDirFlag.Name)) am := utils.MakeAccountManager(ctx) accts, err := am.Accounts() if err != nil { utils.Fatalf("Could not list accounts: %v", err) } for i, acct := range accts { fmt.Printf("Account #%d: %x\n", i, acct) } }
func importWallet(ctx *cli.Context) { utils.CheckLegalese(ctx.GlobalString(utils.DataDirFlag.Name)) keyfile := ctx.Args().First() if len(keyfile) == 0 { utils.Fatalf("keyfile must be given as argument") } keyJson, err := ioutil.ReadFile(keyfile) if err != nil { utils.Fatalf("Could not read wallet file: %v", err) } am := utils.MakeAccountManager(ctx) passphrase := getPassPhrase(ctx, "", false, 0) acct, err := am.ImportPreSaleKey(keyJson, passphrase) if err != nil { utils.Fatalf("Could not create the account: %v", err) } fmt.Printf("Address: %x\n", acct) }
func getPassPhrase(ctx *cli.Context, desc string, confirmation bool, i int) (passphrase string) { passfile := ctx.GlobalString(utils.PasswordFileFlag.Name) if len(passfile) == 0 { fmt.Println(desc) auth, err := utils.PromptPassword("Passphrase: ", true) if err != nil { utils.Fatalf("%v", err) } if confirmation { confirm, err := utils.PromptPassword("Repeat Passphrase: ", false) if err != nil { utils.Fatalf("%v", err) } if auth != confirm { utils.Fatalf("Passphrases did not match.") } } passphrase = auth } else { passbytes, err := ioutil.ReadFile(passfile) if err != nil { utils.Fatalf("Unable to read password file '%s': %v", passfile, err) } // this is backwards compatible if the same password unlocks several accounts // it also has the consequence that trailing newlines will not count as part // of the password, so --password <(echo -n 'pass') will now work without -n passphrases := strings.Split(string(passbytes), "\n") if i >= len(passphrases) { passphrase = passphrases[len(passphrases)-1] } else { passphrase = passphrases[i] } } return }
func removeDB(ctx *cli.Context) { confirm, err := utils.PromptConfirm("Remove local database?") if err != nil { utils.Fatalf("%v", err) } if confirm { fmt.Println("Removing chaindata...") start := time.Now() os.RemoveAll(filepath.Join(ctx.GlobalString(utils.DataDirFlag.Name), "chaindata")) fmt.Printf("Removed in %v\n", time.Since(start)) } else { fmt.Println("Operation aborted") } }
func run(ctx *cli.Context) { utils.CheckLegalese(ctx.GlobalString(utils.DataDirFlag.Name)) if ctx.GlobalBool(utils.OlympicFlag.Name) { utils.InitOlympic() } cfg := utils.MakeEthConfig(ClientIdentifier, nodeNameVersion, ctx) cfg.ExtraData = makeDefaultExtra() ethereum, err := eth.New(cfg) if err != nil { utils.Fatalf("%v", err) } startEth(ctx, ethereum) // this blocks the thread ethereum.WaitForShutdown() }
func dump(ctx *cli.Context) { chain, chainDb := utils.MakeChain(ctx) for _, arg := range ctx.Args() { var block *types.Block if hashish(arg) { block = chain.GetBlock(common.HexToHash(arg)) } else { num, _ := strconv.Atoi(arg) block = chain.GetBlockByNumber(uint64(num)) } if block == nil { fmt.Println("{}") utils.Fatalf("block not found") } else { state := state.New(block.Root(), chainDb) fmt.Printf("%s\n", state.Dump()) } } chainDb.Close() }
// expandMetrics expands the entire tree of metrics into a flat list of paths. func expandMetrics(metrics map[string]interface{}, path string) []string { // Iterate over all fields and expand individually list := []string{} for name, metric := range metrics { switch metric := metric.(type) { case float64: // Final metric value found, append to list list = append(list, path+name) case map[string]interface{}: // Tree of metrics found, expand recursively list = append(list, expandMetrics(metric, path+name+"/")...) default: utils.Fatalf("Metric pattern %s resolved to unexpected type: %v", path+name, reflect.TypeOf(metric)) return nil } } return list }
func newJSRE(ethereum *eth.Ethereum, libPath, corsDomain string, client comms.EthereumClient, interactive bool, f xeth.Frontend) *jsre { js := &jsre{ethereum: ethereum, ps1: "> "} // set default cors domain used by startRpc from CLI flag js.corsDomain = corsDomain if f == nil { f = js } js.ds = docserver.New("/") js.xeth = xeth.New(ethereum, f) js.wait = js.xeth.UpdateState() js.client = client if clt, ok := js.client.(*comms.InProcClient); ok { if offeredApis, err := api.ParseApiString(shared.AllApis, codec.JSON, js.xeth, ethereum); err == nil { clt.Initialize(api.Merge(offeredApis...)) } } // update state in separare forever blocks js.re = re.New(libPath) if err := js.apiBindings(f); err != nil { utils.Fatalf("Unable to connect - %v", err) } if !liner.TerminalSupported() || !interactive { js.prompter = dumbterm{bufio.NewReader(os.Stdin)} } else { lr := liner.NewLiner() js.withHistory(func(hist *os.File) { lr.ReadHistory(hist) }) lr.SetCtrlCAborts(true) js.loadAutoCompletion() lr.SetWordCompleter(apiWordCompleter) lr.SetTabCompletionStyle(liner.TabPrints) js.prompter = lr js.atexit = func() { js.withHistory(func(hist *os.File) { hist.Truncate(0); lr.WriteHistory(hist) }) lr.Close() close(js.wait) } } return js }
func unlockAccount(ctx *cli.Context, am *accounts.Manager, addr string, i int) (addrHex, auth string) { utils.CheckLegalese(ctx.GlobalString(utils.DataDirFlag.Name)) var err error addrHex, err = utils.ParamToAddress(addr, am) if err == nil { // Attempt to unlock the account 3 times attempts := 3 for tries := 0; tries < attempts; tries++ { msg := fmt.Sprintf("Unlocking account %s | Attempt %d/%d", addr, tries+1, attempts) auth = getPassPhrase(ctx, msg, false, i) err = am.Unlock(common.HexToAddress(addrHex), auth) if err == nil { break } } } if err != nil { utils.Fatalf("Unlock account failed '%v'", err) } fmt.Printf("Account '%s' unlocked.\n", addr) return }
func (js *jsre) apiBindings(f xeth.Frontend) error { apis, err := js.supportedApis() if err != nil { return err } apiNames := make([]string, 0, len(apis)) for a, _ := range apis { apiNames = append(apiNames, a) } apiImpl, err := api.ParseApiString(strings.Join(apiNames, ","), codec.JSON, js.xeth, js.ethereum) if err != nil { utils.Fatalf("Unable to determine supported api's: %v", err) } jeth := rpc.NewJeth(api.Merge(apiImpl...), js.re, js.client, f) js.re.Set("jeth", struct{}{}) t, _ := js.re.Get("jeth") jethObj := t.Object() jethObj.Set("send", jeth.Send) jethObj.Set("sendAsync", jeth.Send) err = js.re.Compile("bignumber.js", re.BigNumber_JS) if err != nil { utils.Fatalf("Error loading bignumber.js: %v", err) } err = js.re.Compile("ethereum.js", re.Web3_JS) if err != nil { utils.Fatalf("Error loading web3.js: %v", err) } _, err = js.re.Run("var web3 = require('web3');") if err != nil { utils.Fatalf("Error requiring web3: %v", err) } _, err = js.re.Run("web3.setProvider(jeth)") if err != nil { utils.Fatalf("Error setting web3 provider: %v", err) } // load only supported API's in javascript runtime shortcuts := "var eth = web3.eth; " for _, apiName := range apiNames { if apiName == shared.Web3ApiName { continue // manually mapped } if err = js.re.Compile(fmt.Sprintf("%s.js", apiName), api.Javascript(apiName)); err == nil { shortcuts += fmt.Sprintf("var %s = web3.%s; ", apiName, apiName) } else { utils.Fatalf("Error loading %s.js: %v", apiName, err) } } _, err = js.re.Run(shortcuts) if err != nil { utils.Fatalf("Error setting namespaces: %v", err) } js.re.Run(`var GlobalRegistrar = eth.contract(` + registrar.GlobalRegistrarAbi + `); registrar = GlobalRegistrar.at("` + registrar.GlobalRegistrarAddr + `");`) return nil }
// monitor starts a terminal UI based monitoring tool for the requested metrics. func monitor(ctx *cli.Context) { var ( client comms.EthereumClient err error ) // Attach to an Shift node over IPC or RPC endpoint := ctx.String(monitorCommandAttachFlag.Name) if client, err = comms.ClientFromEndpoint(endpoint, codec.JSON); err != nil { utils.Fatalf("Unable to attach to shift node: %v", err) } defer client.Close() xeth := rpc.NewXeth(client) // Retrieve all the available metrics and resolve the user pattens metrics, err := retrieveMetrics(xeth) if err != nil { utils.Fatalf("Failed to retrieve system metrics: %v", err) } monitored := resolveMetrics(metrics, ctx.Args()) if len(monitored) == 0 { list := expandMetrics(metrics, "") sort.Strings(list) if len(list) > 0 { utils.Fatalf("No metrics specified.\n\nAvailable:\n - %s", strings.Join(list, "\n - ")) } else { utils.Fatalf("No metrics collected by shift (--%s).\n", utils.MetricsEnabledFlag.Name) } } sort.Strings(monitored) if cols := len(monitored) / ctx.Int(monitorCommandRowsFlag.Name); cols > 6 { utils.Fatalf("Requested metrics (%d) spans more that 6 columns:\n - %s", len(monitored), strings.Join(monitored, "\n - ")) } // Create and configure the chart UI defaults if err := termui.Init(); err != nil { utils.Fatalf("Unable to initialize terminal UI: %v", err) } defer termui.Close() termui.UseTheme("helloworld") rows := len(monitored) if max := ctx.Int(monitorCommandRowsFlag.Name); rows > max { rows = max } cols := (len(monitored) + rows - 1) / rows for i := 0; i < rows; i++ { termui.Body.AddRows(termui.NewRow()) } // Create each individual data chart footer := termui.NewPar("") footer.HasBorder = true footer.Height = 3 charts := make([]*termui.LineChart, len(monitored)) units := make([]int, len(monitored)) data := make([][]float64, len(monitored)) for i := 0; i < len(monitored); i++ { charts[i] = createChart((termui.TermHeight() - footer.Height) / rows) row := termui.Body.Rows[i%rows] row.Cols = append(row.Cols, termui.NewCol(12/cols, 0, charts[i])) } termui.Body.AddRows(termui.NewRow(termui.NewCol(12, 0, footer))) refreshCharts(xeth, monitored, data, units, charts, ctx, footer) termui.Body.Align() termui.Render(termui.Body) // Watch for various system events, and periodically refresh the charts refresh := time.Tick(time.Duration(ctx.Int(monitorCommandRefreshFlag.Name)) * time.Second) for { select { case event := <-termui.EventCh(): if event.Type == termui.EventKey && event.Key == termui.KeyCtrlC { return } if event.Type == termui.EventResize { termui.Body.Width = termui.TermWidth() for _, chart := range charts { chart.Height = (termui.TermHeight() - footer.Height) / rows } termui.Body.Align() termui.Render(termui.Body) } case <-refresh: if refreshCharts(xeth, monitored, data, units, charts, ctx, footer) { termui.Body.Align() } termui.Render(termui.Body) } } }