github.com/beyonderyue/gochain@v2.2.26+incompatible/cmd/swarm/main.go (about)

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