github.com/waltonchain/waltonchain_gwtc_src@v1.1.4-0.20201225072101-8a298c95a819/cmd/swarm/main.go (about)

     1  // Copyright 2016 The go-ethereum Authors
     2  // This file is part of go-wtc.
     3  //
     4  // go-wtc 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-wtc 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-wtc. 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/wtc/go-wtc/accounts"
    35  	"github.com/wtc/go-wtc/accounts/keystore"
    36  	"github.com/wtc/go-wtc/cmd/utils"
    37  	"github.com/wtc/go-wtc/common"
    38  	"github.com/wtc/go-wtc/console"
    39  	"github.com/wtc/go-wtc/contracts/ens"
    40  	"github.com/wtc/go-wtc/crypto"
    41  	"github.com/wtc/go-wtc/wtcclient"
    42  	"github.com/wtc/go-wtc/internal/debug"
    43  	"github.com/wtc/go-wtc/log"
    44  	"github.com/wtc/go-wtc/node"
    45  	"github.com/wtc/go-wtc/p2p"
    46  	"github.com/wtc/go-wtc/p2p/discover"
    47  	"github.com/wtc/go-wtc/params"
    48  	"github.com/wtc/go-wtc/rpc"
    49  	"github.com/wtc/go-wtc/swarm"
    50  	bzzapi "github.com/wtc/go-wtc/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 Wtc 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 Wtc API provider to use for ENS record lookups",
   107  		Value: node.DefaultIPCEndpoint("gwtc"),
   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 gwtc.
   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, "Wtc 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 go-ethereum 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 ~/.wtc/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 ~/.wtc/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 ~/.wtc/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 ~/.wtc/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 := wtcclient.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  	case version == "3" && block.Hash() == params.TestnetGenesisHash:
   443  		log.Info("using Testnet ENS contract address", "addr", ens.TestNetAddress)
   444  		return ens.TestNetAddress, nil
   445  
   446  	default:
   447  		return common.Address{}, fmt.Errorf("unknown version and genesis hash: %s %s", version, block.Hash())
   448  	}
   449  }
   450  
   451  func registerBzzService(ctx *cli.Context, stack *node.Node) {
   452  	prvkey := getAccount(ctx, stack)
   453  
   454  	chbookaddr := common.HexToAddress(ctx.GlobalString(ChequebookAddrFlag.Name))
   455  	bzzdir := ctx.GlobalString(SwarmConfigPathFlag.Name)
   456  	if bzzdir == "" {
   457  		bzzdir = stack.InstanceDir()
   458  	}
   459  
   460  	bzzconfig, err := bzzapi.NewConfig(bzzdir, chbookaddr, prvkey, ctx.GlobalUint64(SwarmNetworkIdFlag.Name))
   461  	if err != nil {
   462  		utils.Fatalf("unable to configure swarm: %v", err)
   463  	}
   464  	bzzport := ctx.GlobalString(SwarmPortFlag.Name)
   465  	if len(bzzport) > 0 {
   466  		bzzconfig.Port = bzzport
   467  	}
   468  	if bzzaddr := ctx.GlobalString(SwarmListenAddrFlag.Name); bzzaddr != "" {
   469  		bzzconfig.ListenAddr = bzzaddr
   470  	}
   471  	swapEnabled := ctx.GlobalBool(SwarmSwapEnabledFlag.Name)
   472  	syncEnabled := ctx.GlobalBoolT(SwarmSyncEnabledFlag.Name)
   473  
   474  	swapapi := ctx.GlobalString(SwarmSwapAPIFlag.Name)
   475  	if swapEnabled && swapapi == "" {
   476  		utils.Fatalf("SWAP is enabled but --swap-api is not set")
   477  	}
   478  
   479  	ensapi := ctx.GlobalString(EnsAPIFlag.Name)
   480  	ensAddr := ctx.GlobalString(EnsAddrFlag.Name)
   481  
   482  	cors := ctx.GlobalString(CorsStringFlag.Name)
   483  
   484  	boot := func(ctx *node.ServiceContext) (node.Service, error) {
   485  		var swapClient *wtcclient.Client
   486  		if swapapi != "" {
   487  			log.Info("connecting to SWAP API", "url", swapapi)
   488  			swapClient, err = wtcclient.Dial(swapapi)
   489  			if err != nil {
   490  				return nil, fmt.Errorf("error connecting to SWAP API %s: %s", swapapi, err)
   491  			}
   492  		}
   493  
   494  		var ensClient *wtcclient.Client
   495  		if ensapi != "" {
   496  			log.Info("connecting to ENS API", "url", ensapi)
   497  			client, err := rpc.Dial(ensapi)
   498  			if err != nil {
   499  				return nil, fmt.Errorf("error connecting to ENS API %s: %s", ensapi, err)
   500  			}
   501  			ensClient = wtcclient.NewClient(client)
   502  
   503  			if ensAddr != "" {
   504  				bzzconfig.EnsRoot = common.HexToAddress(ensAddr)
   505  			} else {
   506  				ensAddr, err := detectEnsAddr(client)
   507  				if err == nil {
   508  					bzzconfig.EnsRoot = ensAddr
   509  				} else {
   510  					log.Warn(fmt.Sprintf("could not determine ENS contract address, using default %s", bzzconfig.EnsRoot), "err", err)
   511  				}
   512  			}
   513  		}
   514  
   515  		return swarm.NewSwarm(ctx, swapClient, ensClient, bzzconfig, swapEnabled, syncEnabled, cors)
   516  	}
   517  	if err := stack.Register(boot); err != nil {
   518  		utils.Fatalf("Failed to register the Swarm service: %v", err)
   519  	}
   520  }
   521  
   522  func getAccount(ctx *cli.Context, stack *node.Node) *ecdsa.PrivateKey {
   523  	keyid := ctx.GlobalString(SwarmAccountFlag.Name)
   524  
   525  	if keyid == "" {
   526  		utils.Fatalf("Option %q is required", SwarmAccountFlag.Name)
   527  	}
   528  	// Try to load the arg as a hex key file.
   529  	if key, err := crypto.LoadECDSA(keyid); err == nil {
   530  		log.Info("Swarm account key loaded", "address", crypto.PubkeyToAddress(key.PublicKey))
   531  		return key
   532  	}
   533  	// Otherwise try getting it from the keystore.
   534  	am := stack.AccountManager()
   535  	ks := am.Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore)
   536  
   537  	return decryptStoreAccount(ks, keyid, utils.MakePasswordList(ctx))
   538  }
   539  
   540  func decryptStoreAccount(ks *keystore.KeyStore, account string, passwords []string) *ecdsa.PrivateKey {
   541  	var a accounts.Account
   542  	var err error
   543  	if common.IsHexAddress(account) {
   544  		a, err = ks.Find(accounts.Account{Address: common.HexToAddress(account)})
   545  	} else if ix, ixerr := strconv.Atoi(account); ixerr == nil && ix > 0 {
   546  		if accounts := ks.Accounts(); len(accounts) > ix {
   547  			a = accounts[ix]
   548  		} else {
   549  			err = fmt.Errorf("index %d higher than number of accounts %d", ix, len(accounts))
   550  		}
   551  	} else {
   552  		utils.Fatalf("Can't find swarm account key %s", account)
   553  	}
   554  	if err != nil {
   555  		utils.Fatalf("Can't find swarm account key: %v", err)
   556  	}
   557  	keyjson, err := ioutil.ReadFile(a.URL.Path)
   558  	if err != nil {
   559  		utils.Fatalf("Can't load swarm account key: %v", err)
   560  	}
   561  	for i := 0; i < 3; i++ {
   562  		password := getPassPhrase(fmt.Sprintf("Unlocking swarm account %s [%d/3]", a.Address.Hex(), i+1), i, passwords)
   563  		key, err := keystore.DecryptKey(keyjson, password)
   564  		if err == nil {
   565  			return key.PrivateKey
   566  		}
   567  	}
   568  	utils.Fatalf("Can't decrypt swarm account key")
   569  	return nil
   570  }
   571  
   572  // getPassPhrase retrieves the password associated with bzz account, either by fetching
   573  // from a list of pre-loaded passwords, or by requesting it interactively from user.
   574  func getPassPhrase(prompt string, i int, passwords []string) string {
   575  	// non-interactive
   576  	if len(passwords) > 0 {
   577  		if i < len(passwords) {
   578  			return passwords[i]
   579  		}
   580  		return passwords[len(passwords)-1]
   581  	}
   582  
   583  	// fallback to interactive mode
   584  	if prompt != "" {
   585  		fmt.Println(prompt)
   586  	}
   587  	password, err := console.Stdin.PromptPassword("Passphrase: ")
   588  	if err != nil {
   589  		utils.Fatalf("Failed to read passphrase: %v", err)
   590  	}
   591  	return password
   592  }
   593  
   594  func injectBootnodes(srv *p2p.Server, nodes []string) {
   595  	for _, url := range nodes {
   596  		n, err := discover.ParseNode(url)
   597  		if err != nil {
   598  			log.Error("Invalid swarm bootnode", "err", err)
   599  			continue
   600  		}
   601  		srv.AddPeer(n)
   602  	}
   603  }