github.com/Elemental-core/elementalcore@v0.0.0-20191206075037-63891242267a/cmd/swarm/main.go (about)

     1  // Copyright 2016 The elementalcore Authors
     2  // This file is part of elementalcore.
     3  //
     4  // elementalcore 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  // elementalcore 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 elementalcore. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package main
    18  
    19  import (
    20  	"context"
    21  	"crypto/ecdsa"
    22  	"fmt"
    23  	"io/ioutil"
    24  	"math/big"
    25  	"os"
    26  	"os/signal"
    27  	"runtime"
    28  	"sort"
    29  	"strconv"
    30  	"strings"
    31  	"syscall"
    32  	"time"
    33  
    34  	"github.com/Elemental-core/elementalcore/accounts"
    35  	"github.com/Elemental-core/elementalcore/accounts/keystore"
    36  	"github.com/Elemental-core/elementalcore/cmd/utils"
    37  	"github.com/Elemental-core/elementalcore/common"
    38  	"github.com/Elemental-core/elementalcore/console"
    39  	"github.com/Elemental-core/elementalcore/contracts/ens"
    40  	"github.com/Elemental-core/elementalcore/crypto"
    41  	"github.com/Elemental-core/elementalcore/ethclient"
    42  	"github.com/Elemental-core/elementalcore/internal/debug"
    43  	"github.com/Elemental-core/elementalcore/log"
    44  	"github.com/Elemental-core/elementalcore/node"
    45  	"github.com/Elemental-core/elementalcore/p2p"
    46  	"github.com/Elemental-core/elementalcore/p2p/discover"
    47  	"github.com/Elemental-core/elementalcore/params"
    48  	"github.com/Elemental-core/elementalcore/rpc"
    49  	"github.com/Elemental-core/elementalcore/swarm"
    50  	bzzapi "github.com/Elemental-core/elementalcore/swarm/api"
    51  	"gopkg.in/urfave/cli.v1"
    52  )
    53  
    54  const clientIdentifier = "swarm"
    55  
    56  var (
    57  	gitCommit        string // Git SHA1 commit hash of the release (set via linker flags)
    58  	testbetBootNodes = []string{
    59  		"enode://ec8ae764f7cb0417bdfb009b9d0f18ab3818a3a4e8e7c67dd5f18971a93510a2e6f43cd0b69a27e439a9629457ea804104f37c85e41eed057d3faabbf7744cdf@13.74.157.139:30429",
    60  		"enode://c2e1fceb3bf3be19dff71eec6cccf19f2dbf7567ee017d130240c670be8594bc9163353ca55dd8df7a4f161dd94b36d0615c17418b5a3cdcbb4e9d99dfa4de37@13.74.157.139:30430",
    61  		"enode://fe29b82319b734ce1ec68b84657d57145fee237387e63273989d354486731e59f78858e452ef800a020559da22dcca759536e6aa5517c53930d29ce0b1029286@13.74.157.139:30431",
    62  		"enode://1d7187e7bde45cf0bee489ce9852dd6d1a0d9aa67a33a6b8e6db8a4fbc6fcfa6f0f1a5419343671521b863b187d1c73bad3603bae66421d157ffef357669ddb8@13.74.157.139:30432",
    63  		"enode://0e4cba800f7b1ee73673afa6a4acead4018f0149d2e3216be3f133318fd165b324cd71b81fbe1e80deac8dbf56e57a49db7be67f8b9bc81bd2b7ee496434fb5d@13.74.157.139:30433",
    64  	}
    65  )
    66  
    67  var (
    68  	ChequebookAddrFlag = cli.StringFlag{
    69  		Name:  "chequebook",
    70  		Usage: "chequebook contract address",
    71  	}
    72  	SwarmAccountFlag = cli.StringFlag{
    73  		Name:  "bzzaccount",
    74  		Usage: "Swarm account key file",
    75  	}
    76  	SwarmListenAddrFlag = cli.StringFlag{
    77  		Name:  "httpaddr",
    78  		Usage: "Swarm HTTP API listening interface",
    79  	}
    80  	SwarmPortFlag = cli.StringFlag{
    81  		Name:  "bzzport",
    82  		Usage: "Swarm local http api port",
    83  	}
    84  	SwarmNetworkIdFlag = cli.IntFlag{
    85  		Name:  "bzznetworkid",
    86  		Usage: "Network identifier (integer, default 3=swarm testnet)",
    87  	}
    88  	SwarmConfigPathFlag = cli.StringFlag{
    89  		Name:  "bzzconfig",
    90  		Usage: "Swarm config file path (datadir/bzz)",
    91  	}
    92  	SwarmSwapEnabledFlag = cli.BoolFlag{
    93  		Name:  "swap",
    94  		Usage: "Swarm SWAP enabled (default false)",
    95  	}
    96  	SwarmSwapAPIFlag = cli.StringFlag{
    97  		Name:  "swap-api",
    98  		Usage: "URL of the Ethereum API provider to use to settle SWAP payments",
    99  	}
   100  	SwarmSyncEnabledFlag = cli.BoolTFlag{
   101  		Name:  "sync",
   102  		Usage: "Swarm Syncing enabled (default true)",
   103  	}
   104  	EnsAPIFlag = cli.StringFlag{
   105  		Name:  "ens-api",
   106  		Usage: "URL of the Ethereum API provider to use for ENS record lookups",
   107  		Value: node.DefaultIPCEndpoint("geth"),
   108  	}
   109  	EnsAddrFlag = cli.StringFlag{
   110  		Name:  "ens-addr",
   111  		Usage: "ENS contract address (default is detected as testnet or mainnet using --ens-api)",
   112  	}
   113  	SwarmApiFlag = cli.StringFlag{
   114  		Name:  "bzzapi",
   115  		Usage: "Swarm HTTP endpoint",
   116  		Value: "http://127.0.0.1:8500",
   117  	}
   118  	SwarmRecursiveUploadFlag = cli.BoolFlag{
   119  		Name:  "recursive",
   120  		Usage: "Upload directories recursively",
   121  	}
   122  	SwarmWantManifestFlag = cli.BoolTFlag{
   123  		Name:  "manifest",
   124  		Usage: "Automatic manifest upload",
   125  	}
   126  	SwarmUploadDefaultPath = cli.StringFlag{
   127  		Name:  "defaultpath",
   128  		Usage: "path to file served for empty url path (none)",
   129  	}
   130  	SwarmUpFromStdinFlag = cli.BoolFlag{
   131  		Name:  "stdin",
   132  		Usage: "reads data to be uploaded from stdin",
   133  	}
   134  	SwarmUploadMimeType = cli.StringFlag{
   135  		Name:  "mime",
   136  		Usage: "force mime type",
   137  	}
   138  	CorsStringFlag = cli.StringFlag{
   139  		Name:  "corsdomain",
   140  		Usage: "Domain on which to send Access-Control-Allow-Origin header (multiple domains can be supplied separated by a ',')",
   141  	}
   142  
   143  	// the following flags are deprecated and should be removed in the future
   144  	DeprecatedEthAPIFlag = cli.StringFlag{
   145  		Name:  "ethapi",
   146  		Usage: "DEPRECATED: please use --ens-api and --swap-api",
   147  	}
   148  )
   149  
   150  var defaultNodeConfig = node.DefaultConfig
   151  
   152  // This init function sets defaults so cmd/swarm can run alongside geth.
   153  func init() {
   154  	defaultNodeConfig.Name = clientIdentifier
   155  	defaultNodeConfig.Version = params.VersionWithCommit(gitCommit)
   156  	defaultNodeConfig.P2P.ListenAddr = ":30399"
   157  	defaultNodeConfig.IPCPath = "bzzd.ipc"
   158  	// Set flag defaults for --help display.
   159  	utils.ListenPortFlag.Value = 30399
   160  }
   161  
   162  var app = utils.NewApp(gitCommit, "Ethereum Swarm")
   163  
   164  // This init function creates the cli.App.
   165  func init() {
   166  	app.Action = bzzd
   167  	app.HideVersion = true // we have a command to print the version
   168  	app.Copyright = "Copyright 2013-2016 The elementalcore Authors"
   169  	app.Commands = []cli.Command{
   170  		{
   171  			Action:    version,
   172  			Name:      "version",
   173  			Usage:     "Print version numbers",
   174  			ArgsUsage: " ",
   175  			Description: `
   176  The output of this command is supposed to be machine-readable.
   177  `,
   178  		},
   179  		{
   180  			Action:    upload,
   181  			Name:      "up",
   182  			Usage:     "upload a file or directory to swarm using the HTTP API",
   183  			ArgsUsage: " <file>",
   184  			Description: `
   185  "upload a file or directory to swarm using the HTTP API and prints the root hash",
   186  `,
   187  		},
   188  		{
   189  			Action:    list,
   190  			Name:      "ls",
   191  			Usage:     "list files and directories contained in a manifest",
   192  			ArgsUsage: " <manifest> [<prefix>]",
   193  			Description: `
   194  Lists files and directories contained in a manifest.
   195  `,
   196  		},
   197  		{
   198  			Action:    hash,
   199  			Name:      "hash",
   200  			Usage:     "print the swarm hash of a file or directory",
   201  			ArgsUsage: " <file>",
   202  			Description: `
   203  Prints the swarm hash of file or directory.
   204  `,
   205  		},
   206  		{
   207  			Name:      "manifest",
   208  			Usage:     "update a MANIFEST",
   209  			ArgsUsage: "manifest COMMAND",
   210  			Description: `
   211  Updates a MANIFEST by adding/removing/updating the hash of a path.
   212  `,
   213  			Subcommands: []cli.Command{
   214  				{
   215  					Action:    add,
   216  					Name:      "add",
   217  					Usage:     "add a new path to the manifest",
   218  					ArgsUsage: "<MANIFEST> <path> <hash> [<content-type>]",
   219  					Description: `
   220  Adds a new path to the manifest
   221  `,
   222  				},
   223  				{
   224  					Action:    update,
   225  					Name:      "update",
   226  					Usage:     "update the hash for an already existing path in the manifest",
   227  					ArgsUsage: "<MANIFEST> <path> <newhash> [<newcontent-type>]",
   228  					Description: `
   229  Update the hash for an already existing path in the manifest
   230  `,
   231  				},
   232  				{
   233  					Action:    remove,
   234  					Name:      "remove",
   235  					Usage:     "removes a path from the manifest",
   236  					ArgsUsage: "<MANIFEST> <path>",
   237  					Description: `
   238  Removes a path from the manifest
   239  `,
   240  				},
   241  			},
   242  		},
   243  		{
   244  			Name:      "db",
   245  			Usage:     "manage the local chunk database",
   246  			ArgsUsage: "db COMMAND",
   247  			Description: `
   248  Manage the local chunk database.
   249  `,
   250  			Subcommands: []cli.Command{
   251  				{
   252  					Action:    dbExport,
   253  					Name:      "export",
   254  					Usage:     "export a local chunk database as a tar archive (use - to send to stdout)",
   255  					ArgsUsage: "<chunkdb> <file>",
   256  					Description: `
   257  Export a local chunk database as a tar archive (use - to send to stdout).
   258  
   259      swarm db export ~/.ethereum/swarm/bzz-KEY/chunks chunks.tar
   260  
   261  The export may be quite large, consider piping the output through the Unix
   262  pv(1) tool to get a progress bar:
   263  
   264      swarm db export ~/.ethereum/swarm/bzz-KEY/chunks - | pv > chunks.tar
   265  `,
   266  				},
   267  				{
   268  					Action:    dbImport,
   269  					Name:      "import",
   270  					Usage:     "import chunks from a tar archive into a local chunk database (use - to read from stdin)",
   271  					ArgsUsage: "<chunkdb> <file>",
   272  					Description: `
   273  Import chunks from a tar archive into a local chunk database (use - to read from stdin).
   274  
   275      swarm db import ~/.ethereum/swarm/bzz-KEY/chunks chunks.tar
   276  
   277  The import may be quite large, consider piping the input through the Unix
   278  pv(1) tool to get a progress bar:
   279  
   280      pv chunks.tar | swarm db import ~/.ethereum/swarm/bzz-KEY/chunks -
   281  `,
   282  				},
   283  				{
   284  					Action:    dbClean,
   285  					Name:      "clean",
   286  					Usage:     "remove corrupt entries from a local chunk database",
   287  					ArgsUsage: "<chunkdb>",
   288  					Description: `
   289  Remove corrupt entries from a local chunk database.
   290  `,
   291  				},
   292  			},
   293  		},
   294  		{
   295  			Action: func(ctx *cli.Context) {
   296  				utils.Fatalf("ERROR: 'swarm cleandb' has been removed, please use 'swarm db clean'.")
   297  			},
   298  			Name:      "cleandb",
   299  			Usage:     "DEPRECATED: use 'swarm db clean'",
   300  			ArgsUsage: " ",
   301  			Description: `
   302  DEPRECATED: use 'swarm db clean'.
   303  `,
   304  		},
   305  	}
   306  	sort.Sort(cli.CommandsByName(app.Commands))
   307  
   308  	app.Flags = []cli.Flag{
   309  		utils.IdentityFlag,
   310  		utils.DataDirFlag,
   311  		utils.BootnodesFlag,
   312  		utils.KeyStoreDirFlag,
   313  		utils.ListenPortFlag,
   314  		utils.NoDiscoverFlag,
   315  		utils.DiscoveryV5Flag,
   316  		utils.NetrestrictFlag,
   317  		utils.NodeKeyFileFlag,
   318  		utils.NodeKeyHexFlag,
   319  		utils.MaxPeersFlag,
   320  		utils.NATFlag,
   321  		utils.IPCDisabledFlag,
   322  		utils.IPCPathFlag,
   323  		utils.PasswordFileFlag,
   324  		// bzzd-specific flags
   325  		CorsStringFlag,
   326  		EnsAPIFlag,
   327  		EnsAddrFlag,
   328  		SwarmConfigPathFlag,
   329  		SwarmSwapEnabledFlag,
   330  		SwarmSwapAPIFlag,
   331  		SwarmSyncEnabledFlag,
   332  		SwarmListenAddrFlag,
   333  		SwarmPortFlag,
   334  		SwarmAccountFlag,
   335  		SwarmNetworkIdFlag,
   336  		ChequebookAddrFlag,
   337  		// upload flags
   338  		SwarmApiFlag,
   339  		SwarmRecursiveUploadFlag,
   340  		SwarmWantManifestFlag,
   341  		SwarmUploadDefaultPath,
   342  		SwarmUpFromStdinFlag,
   343  		SwarmUploadMimeType,
   344  		//deprecated flags
   345  		DeprecatedEthAPIFlag,
   346  	}
   347  	app.Flags = append(app.Flags, debug.Flags...)
   348  	app.Before = func(ctx *cli.Context) error {
   349  		runtime.GOMAXPROCS(runtime.NumCPU())
   350  		return debug.Setup(ctx)
   351  	}
   352  	app.After = func(ctx *cli.Context) error {
   353  		debug.Exit()
   354  		return nil
   355  	}
   356  }
   357  
   358  func main() {
   359  	if err := app.Run(os.Args); err != nil {
   360  		fmt.Fprintln(os.Stderr, err)
   361  		os.Exit(1)
   362  	}
   363  }
   364  
   365  func version(ctx *cli.Context) error {
   366  	fmt.Println(strings.Title(clientIdentifier))
   367  	fmt.Println("Version:", params.Version)
   368  	if gitCommit != "" {
   369  		fmt.Println("Git Commit:", gitCommit)
   370  	}
   371  	fmt.Println("Network Id:", ctx.GlobalInt(utils.NetworkIdFlag.Name))
   372  	fmt.Println("Go Version:", runtime.Version())
   373  	fmt.Println("OS:", runtime.GOOS)
   374  	fmt.Printf("GOPATH=%s\n", os.Getenv("GOPATH"))
   375  	fmt.Printf("GOROOT=%s\n", runtime.GOROOT())
   376  	return nil
   377  }
   378  
   379  func bzzd(ctx *cli.Context) error {
   380  	// exit if the deprecated --ethapi flag is set
   381  	if ctx.GlobalString(DeprecatedEthAPIFlag.Name) != "" {
   382  		utils.Fatalf("--ethapi is no longer a valid command line flag, please use --ens-api and/or --swap-api.")
   383  	}
   384  
   385  	cfg := defaultNodeConfig
   386  	utils.SetNodeConfig(ctx, &cfg)
   387  	stack, err := node.New(&cfg)
   388  	if err != nil {
   389  		utils.Fatalf("can't create node: %v", err)
   390  	}
   391  
   392  	registerBzzService(ctx, stack)
   393  	utils.StartNode(stack)
   394  
   395  	go func() {
   396  		sigc := make(chan os.Signal, 1)
   397  		signal.Notify(sigc, syscall.SIGTERM)
   398  		defer signal.Stop(sigc)
   399  		<-sigc
   400  		log.Info("Got sigterm, shutting swarm down...")
   401  		stack.Stop()
   402  	}()
   403  
   404  	networkId := ctx.GlobalUint64(SwarmNetworkIdFlag.Name)
   405  	// Add bootnodes as initial peers.
   406  	if ctx.GlobalIsSet(utils.BootnodesFlag.Name) {
   407  		bootnodes := strings.Split(ctx.GlobalString(utils.BootnodesFlag.Name), ",")
   408  		injectBootnodes(stack.Server(), bootnodes)
   409  	} else {
   410  		if networkId == 3 {
   411  			injectBootnodes(stack.Server(), testbetBootNodes)
   412  		}
   413  	}
   414  
   415  	stack.Wait()
   416  	return nil
   417  }
   418  
   419  // detectEnsAddr determines the ENS contract address by getting both the
   420  // version and genesis hash using the client and matching them to either
   421  // mainnet or testnet addresses
   422  func detectEnsAddr(client *rpc.Client) (common.Address, error) {
   423  	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
   424  	defer cancel()
   425  
   426  	var version string
   427  	if err := client.CallContext(ctx, &version, "net_version"); err != nil {
   428  		return common.Address{}, err
   429  	}
   430  
   431  	block, err := ethclient.NewClient(client).BlockByNumber(ctx, big.NewInt(0))
   432  	if err != nil {
   433  		return common.Address{}, err
   434  	}
   435  
   436  	switch {
   437  
   438  	case version == "1" && block.Hash() == params.MainnetGenesisHash:
   439  		log.Info("using Mainnet ENS contract address", "addr", ens.MainNetAddress)
   440  		return ens.MainNetAddress, nil
   441  
   442  	default:
   443  		return common.Address{}, fmt.Errorf("unknown version and genesis hash: %s %s", version, block.Hash())
   444  	}
   445  }
   446  
   447  func registerBzzService(ctx *cli.Context, stack *node.Node) {
   448  	prvkey := getAccount(ctx, stack)
   449  
   450  	chbookaddr := common.HexToAddress(ctx.GlobalString(ChequebookAddrFlag.Name))
   451  	bzzdir := ctx.GlobalString(SwarmConfigPathFlag.Name)
   452  	if bzzdir == "" {
   453  		bzzdir = stack.InstanceDir()
   454  	}
   455  
   456  	bzzconfig, err := bzzapi.NewConfig(bzzdir, chbookaddr, prvkey, ctx.GlobalUint64(SwarmNetworkIdFlag.Name))
   457  	if err != nil {
   458  		utils.Fatalf("unable to configure swarm: %v", err)
   459  	}
   460  	bzzport := ctx.GlobalString(SwarmPortFlag.Name)
   461  	if len(bzzport) > 0 {
   462  		bzzconfig.Port = bzzport
   463  	}
   464  	if bzzaddr := ctx.GlobalString(SwarmListenAddrFlag.Name); bzzaddr != "" {
   465  		bzzconfig.ListenAddr = bzzaddr
   466  	}
   467  	swapEnabled := ctx.GlobalBool(SwarmSwapEnabledFlag.Name)
   468  	syncEnabled := ctx.GlobalBoolT(SwarmSyncEnabledFlag.Name)
   469  
   470  	swapapi := ctx.GlobalString(SwarmSwapAPIFlag.Name)
   471  	if swapEnabled && swapapi == "" {
   472  		utils.Fatalf("SWAP is enabled but --swap-api is not set")
   473  	}
   474  
   475  	ensapi := ctx.GlobalString(EnsAPIFlag.Name)
   476  	ensAddr := ctx.GlobalString(EnsAddrFlag.Name)
   477  
   478  	cors := ctx.GlobalString(CorsStringFlag.Name)
   479  
   480  	boot := func(ctx *node.ServiceContext) (node.Service, error) {
   481  		var swapClient *ethclient.Client
   482  		if swapapi != "" {
   483  			log.Info("connecting to SWAP API", "url", swapapi)
   484  			swapClient, err = ethclient.Dial(swapapi)
   485  			if err != nil {
   486  				return nil, fmt.Errorf("error connecting to SWAP API %s: %s", swapapi, err)
   487  			}
   488  		}
   489  
   490  		var ensClient *ethclient.Client
   491  		if ensapi != "" {
   492  			log.Info("connecting to ENS API", "url", ensapi)
   493  			client, err := rpc.Dial(ensapi)
   494  			if err != nil {
   495  				return nil, fmt.Errorf("error connecting to ENS API %s: %s", ensapi, err)
   496  			}
   497  			ensClient = ethclient.NewClient(client)
   498  
   499  			if ensAddr != "" {
   500  				bzzconfig.EnsRoot = common.HexToAddress(ensAddr)
   501  			} else {
   502  				ensAddr, err := detectEnsAddr(client)
   503  				if err == nil {
   504  					bzzconfig.EnsRoot = ensAddr
   505  				} else {
   506  					log.Warn(fmt.Sprintf("could not determine ENS contract address, using default %s", bzzconfig.EnsRoot), "err", err)
   507  				}
   508  			}
   509  		}
   510  
   511  		return swarm.NewSwarm(ctx, swapClient, ensClient, bzzconfig, swapEnabled, syncEnabled, cors)
   512  	}
   513  	if err := stack.Register(boot); err != nil {
   514  		utils.Fatalf("Failed to register the Swarm service: %v", err)
   515  	}
   516  }
   517  
   518  func getAccount(ctx *cli.Context, stack *node.Node) *ecdsa.PrivateKey {
   519  	keyid := ctx.GlobalString(SwarmAccountFlag.Name)
   520  
   521  	if keyid == "" {
   522  		utils.Fatalf("Option %q is required", SwarmAccountFlag.Name)
   523  	}
   524  	// Try to load the arg as a hex key file.
   525  	if key, err := crypto.LoadECDSA(keyid); err == nil {
   526  		log.Info("Swarm account key loaded", "address", crypto.PubkeyToAddress(key.PublicKey))
   527  		return key
   528  	}
   529  	// Otherwise try getting it from the keystore.
   530  	am := stack.AccountManager()
   531  	ks := am.Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore)
   532  
   533  	return decryptStoreAccount(ks, keyid, utils.MakePasswordList(ctx))
   534  }
   535  
   536  func decryptStoreAccount(ks *keystore.KeyStore, account string, passwords []string) *ecdsa.PrivateKey {
   537  	var a accounts.Account
   538  	var err error
   539  	if common.IsHexAddress(account) {
   540  		a, err = ks.Find(accounts.Account{Address: common.HexToAddress(account)})
   541  	} else if ix, ixerr := strconv.Atoi(account); ixerr == nil && ix > 0 {
   542  		if accounts := ks.Accounts(); len(accounts) > ix {
   543  			a = accounts[ix]
   544  		} else {
   545  			err = fmt.Errorf("index %d higher than number of accounts %d", ix, len(accounts))
   546  		}
   547  	} else {
   548  		utils.Fatalf("Can't find swarm account key %s", account)
   549  	}
   550  	if err != nil {
   551  		utils.Fatalf("Can't find swarm account key: %v", err)
   552  	}
   553  	keyjson, err := ioutil.ReadFile(a.URL.Path)
   554  	if err != nil {
   555  		utils.Fatalf("Can't load swarm account key: %v", err)
   556  	}
   557  	for i := 0; i < 3; i++ {
   558  		password := getPassPhrase(fmt.Sprintf("Unlocking swarm account %s [%d/3]", a.Address.Hex(), i+1), i, passwords)
   559  		key, err := keystore.DecryptKey(keyjson, password)
   560  		if err == nil {
   561  			return key.PrivateKey
   562  		}
   563  	}
   564  	utils.Fatalf("Can't decrypt swarm account key")
   565  	return nil
   566  }
   567  
   568  // getPassPhrase retrieves the password associated with bzz account, either by fetching
   569  // from a list of pre-loaded passwords, or by requesting it interactively from user.
   570  func getPassPhrase(prompt string, i int, passwords []string) string {
   571  	// non-interactive
   572  	if len(passwords) > 0 {
   573  		if i < len(passwords) {
   574  			return passwords[i]
   575  		}
   576  		return passwords[len(passwords)-1]
   577  	}
   578  
   579  	// fallback to interactive mode
   580  	if prompt != "" {
   581  		fmt.Println(prompt)
   582  	}
   583  	password, err := console.Stdin.PromptPassword("Passphrase: ")
   584  	if err != nil {
   585  		utils.Fatalf("Failed to read passphrase: %v", err)
   586  	}
   587  	return password
   588  }
   589  
   590  func injectBootnodes(srv *p2p.Server, nodes []string) {
   591  	for _, url := range nodes {
   592  		n, err := discover.ParseNode(url)
   593  		if err != nil {
   594  			log.Error("Invalid swarm bootnode", "err", err)
   595  			continue
   596  		}
   597  		srv.AddPeer(n)
   598  	}
   599  }