main.go 17.5 KB
Newer Older
F
Felix Lange 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
// Copyright 2016 The go-ethereum Authors
// This file is part of go-ethereum.
//
// go-ethereum is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// go-ethereum is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.

package main

import (
20
	"context"
F
Felix Lange 已提交
21 22 23
	"crypto/ecdsa"
	"fmt"
	"io/ioutil"
24
	"math/big"
F
Felix Lange 已提交
25
	"os"
26
	"os/signal"
F
Felix Lange 已提交
27 28
	"runtime"
	"strconv"
29
	"strings"
30
	"syscall"
31
	"time"
F
Felix Lange 已提交
32 33

	"github.com/ethereum/go-ethereum/accounts"
34
	"github.com/ethereum/go-ethereum/accounts/keystore"
F
Felix Lange 已提交
35 36 37
	"github.com/ethereum/go-ethereum/cmd/utils"
	"github.com/ethereum/go-ethereum/common"
	"github.com/ethereum/go-ethereum/console"
38
	"github.com/ethereum/go-ethereum/contracts/ens"
F
Felix Lange 已提交
39 40 41
	"github.com/ethereum/go-ethereum/crypto"
	"github.com/ethereum/go-ethereum/ethclient"
	"github.com/ethereum/go-ethereum/internal/debug"
42
	"github.com/ethereum/go-ethereum/log"
F
Felix Lange 已提交
43 44 45
	"github.com/ethereum/go-ethereum/node"
	"github.com/ethereum/go-ethereum/p2p"
	"github.com/ethereum/go-ethereum/p2p/discover"
46
	"github.com/ethereum/go-ethereum/params"
47
	"github.com/ethereum/go-ethereum/rpc"
F
Felix Lange 已提交
48 49 50 51 52
	"github.com/ethereum/go-ethereum/swarm"
	bzzapi "github.com/ethereum/go-ethereum/swarm/api"
	"gopkg.in/urfave/cli.v1"
)

53
const clientIdentifier = "swarm"
F
Felix Lange 已提交
54 55

var (
56 57
	gitCommit        string // Git SHA1 commit hash of the release (set via linker flags)
	testbetBootNodes = []string{
58 59 60 61 62
		"enode://ec8ae764f7cb0417bdfb009b9d0f18ab3818a3a4e8e7c67dd5f18971a93510a2e6f43cd0b69a27e439a9629457ea804104f37c85e41eed057d3faabbf7744cdf@13.74.157.139:30429",
		"enode://c2e1fceb3bf3be19dff71eec6cccf19f2dbf7567ee017d130240c670be8594bc9163353ca55dd8df7a4f161dd94b36d0615c17418b5a3cdcbb4e9d99dfa4de37@13.74.157.139:30430",
		"enode://fe29b82319b734ce1ec68b84657d57145fee237387e63273989d354486731e59f78858e452ef800a020559da22dcca759536e6aa5517c53930d29ce0b1029286@13.74.157.139:30431",
		"enode://1d7187e7bde45cf0bee489ce9852dd6d1a0d9aa67a33a6b8e6db8a4fbc6fcfa6f0f1a5419343671521b863b187d1c73bad3603bae66421d157ffef357669ddb8@13.74.157.139:30432",
		"enode://0e4cba800f7b1ee73673afa6a4acead4018f0149d2e3216be3f133318fd165b324cd71b81fbe1e80deac8dbf56e57a49db7be67f8b9bc81bd2b7ee496434fb5d@13.74.157.139:30433",
63
	}
F
Felix Lange 已提交
64 65 66 67 68 69 70 71 72 73 74
)

var (
	ChequebookAddrFlag = cli.StringFlag{
		Name:  "chequebook",
		Usage: "chequebook contract address",
	}
	SwarmAccountFlag = cli.StringFlag{
		Name:  "bzzaccount",
		Usage: "Swarm account key file",
	}
75 76 77 78
	SwarmListenAddrFlag = cli.StringFlag{
		Name:  "httpaddr",
		Usage: "Swarm HTTP API listening interface",
	}
F
Felix Lange 已提交
79 80 81 82
	SwarmPortFlag = cli.StringFlag{
		Name:  "bzzport",
		Usage: "Swarm local http api port",
	}
83 84
	SwarmNetworkIdFlag = cli.IntFlag{
		Name:  "bzznetworkid",
85
		Usage: "Network identifier (integer, default 3=swarm testnet)",
86
	}
F
Felix Lange 已提交
87 88 89 90
	SwarmConfigPathFlag = cli.StringFlag{
		Name:  "bzzconfig",
		Usage: "Swarm config file path (datadir/bzz)",
	}
91
	SwarmSwapEnabledFlag = cli.BoolFlag{
92 93
		Name:  "swap",
		Usage: "Swarm SWAP enabled (default false)",
F
Felix Lange 已提交
94
	}
95 96 97 98
	SwarmSwapAPIFlag = cli.StringFlag{
		Name:  "swap-api",
		Usage: "URL of the Ethereum API provider to use to settle SWAP payments",
	}
99
	SwarmSyncEnabledFlag = cli.BoolTFlag{
100 101
		Name:  "sync",
		Usage: "Swarm Syncing enabled (default true)",
F
Felix Lange 已提交
102
	}
103 104 105
	EnsAPIFlag = cli.StringFlag{
		Name:  "ens-api",
		Usage: "URL of the Ethereum API provider to use for ENS record lookups",
F
Felix Lange 已提交
106 107
		Value: node.DefaultIPCEndpoint("geth"),
	}
108 109 110 111
	EnsAddrFlag = cli.StringFlag{
		Name:  "ens-addr",
		Usage: "ENS contract address (default is detected as testnet or mainnet using --ens-api)",
	}
112 113 114 115 116 117 118 119 120 121 122 123 124
	SwarmApiFlag = cli.StringFlag{
		Name:  "bzzapi",
		Usage: "Swarm HTTP endpoint",
		Value: "http://127.0.0.1:8500",
	}
	SwarmRecursiveUploadFlag = cli.BoolFlag{
		Name:  "recursive",
		Usage: "Upload directories recursively",
	}
	SwarmWantManifestFlag = cli.BoolTFlag{
		Name:  "manifest",
		Usage: "Automatic manifest upload",
	}
125 126 127 128
	SwarmUploadDefaultPath = cli.StringFlag{
		Name:  "defaultpath",
		Usage: "path to file served for empty url path (none)",
	}
129 130 131 132 133 134 135 136
	SwarmUpFromStdinFlag = cli.BoolFlag{
		Name:  "stdin",
		Usage: "reads data to be uploaded from stdin",
	}
	SwarmUploadMimeType = cli.StringFlag{
		Name:  "mime",
		Usage: "force mime type",
	}
137 138
	CorsStringFlag = cli.StringFlag{
		Name:  "corsdomain",
P
Péter Szilágyi 已提交
139
		Usage: "Domain on which to send Access-Control-Allow-Origin header (multiple domains can be supplied separated by a ',')",
140
	}
141 142 143 144 145 146

	// the following flags are deprecated and should be removed in the future
	DeprecatedEthAPIFlag = cli.StringFlag{
		Name:  "ethapi",
		Usage: "DEPRECATED: please use --ens-api and --swap-api",
	}
F
Felix Lange 已提交
147 148
)

149 150 151
var defaultNodeConfig = node.DefaultConfig

// This init function sets defaults so cmd/swarm can run alongside geth.
F
Felix Lange 已提交
152
func init() {
153 154 155 156 157
	defaultNodeConfig.Name = clientIdentifier
	defaultNodeConfig.Version = params.VersionWithCommit(gitCommit)
	defaultNodeConfig.P2P.ListenAddr = ":30399"
	defaultNodeConfig.IPCPath = "bzzd.ipc"
	// Set flag defaults for --help display.
F
Felix Lange 已提交
158
	utils.ListenPortFlag.Value = 30399
159
}
F
Felix Lange 已提交
160

161 162 163 164
var app = utils.NewApp(gitCommit, "Ethereum Swarm")

// This init function creates the cli.App.
func init() {
F
Felix Lange 已提交
165
	app.Action = bzzd
166 167 168
	app.HideVersion = true // we have a command to print the version
	app.Copyright = "Copyright 2013-2016 The go-ethereum Authors"
	app.Commands = []cli.Command{
F
Felix Lange 已提交
169
		{
170 171 172 173 174 175 176 177
			Action:    version,
			Name:      "version",
			Usage:     "Print version numbers",
			ArgsUsage: " ",
			Description: `
The output of this command is supposed to be machine-readable.
`,
		},
F
Felix Lange 已提交
178
		{
179 180 181 182 183 184
			Action:    upload,
			Name:      "up",
			Usage:     "upload a file or directory to swarm using the HTTP API",
			ArgsUsage: " <file>",
			Description: `
"upload a file or directory to swarm using the HTTP API and prints the root hash",
185 186 187 188 189 190 191 192 193
`,
		},
		{
			Action:    list,
			Name:      "ls",
			Usage:     "list files and directories contained in a manifest",
			ArgsUsage: " <manifest> [<prefix>]",
			Description: `
Lists files and directories contained in a manifest.
194 195
`,
		},
F
Felix Lange 已提交
196
		{
197 198 199 200 201 202 203 204
			Action:    hash,
			Name:      "hash",
			Usage:     "print the swarm hash of a file or directory",
			ArgsUsage: " <file>",
			Description: `
Prints the swarm hash of file or directory.
`,
		},
205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241
		{
			Name:      "manifest",
			Usage:     "update a MANIFEST",
			ArgsUsage: "manifest COMMAND",
			Description: `
Updates a MANIFEST by adding/removing/updating the hash of a path.
`,
			Subcommands: []cli.Command{
				{
					Action:    add,
					Name:      "add",
					Usage:     "add a new path to the manifest",
					ArgsUsage: "<MANIFEST> <path> <hash> [<content-type>]",
					Description: `
Adds a new path to the manifest
`,
				},
				{
					Action:    update,
					Name:      "update",
					Usage:     "update the hash for an already existing path in the manifest",
					ArgsUsage: "<MANIFEST> <path> <newhash> [<newcontent-type>]",
					Description: `
Update the hash for an already existing path in the manifest
`,
				},
				{
					Action:    remove,
					Name:      "remove",
					Usage:     "removes a path from the manifest",
					ArgsUsage: "<MANIFEST> <path>",
					Description: `
Removes a path from the manifest
`,
				},
			},
		},
242
		{
243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296
			Name:      "db",
			Usage:     "manage the local chunk database",
			ArgsUsage: "db COMMAND",
			Description: `
Manage the local chunk database.
`,
			Subcommands: []cli.Command{
				{
					Action:    dbExport,
					Name:      "export",
					Usage:     "export a local chunk database as a tar archive (use - to send to stdout)",
					ArgsUsage: "<chunkdb> <file>",
					Description: `
Export a local chunk database as a tar archive (use - to send to stdout).

    swarm db export ~/.ethereum/swarm/bzz-KEY/chunks chunks.tar

The export may be quite large, consider piping the output through the Unix
pv(1) tool to get a progress bar:

    swarm db export ~/.ethereum/swarm/bzz-KEY/chunks - | pv > chunks.tar
`,
				},
				{
					Action:    dbImport,
					Name:      "import",
					Usage:     "import chunks from a tar archive into a local chunk database (use - to read from stdin)",
					ArgsUsage: "<chunkdb> <file>",
					Description: `
Import chunks from a tar archive into a local chunk database (use - to read from stdin).

    swarm db import ~/.ethereum/swarm/bzz-KEY/chunks chunks.tar

The import may be quite large, consider piping the input through the Unix
pv(1) tool to get a progress bar:

    pv chunks.tar | swarm db import ~/.ethereum/swarm/bzz-KEY/chunks -
`,
				},
				{
					Action:    dbClean,
					Name:      "clean",
					Usage:     "remove corrupt entries from a local chunk database",
					ArgsUsage: "<chunkdb>",
					Description: `
Remove corrupt entries from a local chunk database.
`,
				},
			},
		},
		{
			Action: func(ctx *cli.Context) {
				utils.Fatalf("ERROR: 'swarm cleandb' has been removed, please use 'swarm db clean'.")
			},
297
			Name:      "cleandb",
298
			Usage:     "DEPRECATED: use 'swarm db clean'",
299 300
			ArgsUsage: " ",
			Description: `
301
DEPRECATED: use 'swarm db clean'.
302 303
`,
		},
304 305
	}

F
Felix Lange 已提交
306 307 308 309 310 311
	app.Flags = []cli.Flag{
		utils.IdentityFlag,
		utils.DataDirFlag,
		utils.BootnodesFlag,
		utils.KeyStoreDirFlag,
		utils.ListenPortFlag,
312 313
		utils.NoDiscoverFlag,
		utils.DiscoveryV5Flag,
314
		utils.NetrestrictFlag,
F
Felix Lange 已提交
315 316
		utils.NodeKeyFileFlag,
		utils.NodeKeyHexFlag,
317 318
		utils.MaxPeersFlag,
		utils.NATFlag,
F
Felix Lange 已提交
319 320
		utils.IPCDisabledFlag,
		utils.IPCPathFlag,
321
		utils.PasswordFileFlag,
F
Felix Lange 已提交
322
		// bzzd-specific flags
323
		CorsStringFlag,
324 325
		EnsAPIFlag,
		EnsAddrFlag,
F
Felix Lange 已提交
326
		SwarmConfigPathFlag,
327
		SwarmSwapEnabledFlag,
328
		SwarmSwapAPIFlag,
329
		SwarmSyncEnabledFlag,
330
		SwarmListenAddrFlag,
F
Felix Lange 已提交
331 332
		SwarmPortFlag,
		SwarmAccountFlag,
333
		SwarmNetworkIdFlag,
F
Felix Lange 已提交
334
		ChequebookAddrFlag,
335 336 337 338
		// upload flags
		SwarmApiFlag,
		SwarmRecursiveUploadFlag,
		SwarmWantManifestFlag,
339
		SwarmUploadDefaultPath,
340 341
		SwarmUpFromStdinFlag,
		SwarmUploadMimeType,
342 343
		//deprecated flags
		DeprecatedEthAPIFlag,
F
Felix Lange 已提交
344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362
	}
	app.Flags = append(app.Flags, debug.Flags...)
	app.Before = func(ctx *cli.Context) error {
		runtime.GOMAXPROCS(runtime.NumCPU())
		return debug.Setup(ctx)
	}
	app.After = func(ctx *cli.Context) error {
		debug.Exit()
		return nil
	}
}

func main() {
	if err := app.Run(os.Args); err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
}

363 364
func version(ctx *cli.Context) error {
	fmt.Println(strings.Title(clientIdentifier))
365
	fmt.Println("Version:", params.Version)
366 367 368 369 370 371 372 373 374 375 376
	if gitCommit != "" {
		fmt.Println("Git Commit:", gitCommit)
	}
	fmt.Println("Network Id:", ctx.GlobalInt(utils.NetworkIdFlag.Name))
	fmt.Println("Go Version:", runtime.Version())
	fmt.Println("OS:", runtime.GOOS)
	fmt.Printf("GOPATH=%s\n", os.Getenv("GOPATH"))
	fmt.Printf("GOROOT=%s\n", runtime.GOROOT())
	return nil
}

F
Felix Lange 已提交
377
func bzzd(ctx *cli.Context) error {
378 379 380 381 382
	// exit if the deprecated --ethapi flag is set
	if ctx.GlobalString(DeprecatedEthAPIFlag.Name) != "" {
		utils.Fatalf("--ethapi is no longer a valid command line flag, please use --ens-api and/or --swap-api.")
	}

383 384 385 386 387 388 389
	cfg := defaultNodeConfig
	utils.SetNodeConfig(ctx, &cfg)
	stack, err := node.New(&cfg)
	if err != nil {
		utils.Fatalf("can't create node: %v", err)
	}

F
Felix Lange 已提交
390 391
	registerBzzService(ctx, stack)
	utils.StartNode(stack)
392

393 394 395 396 397
	go func() {
		sigc := make(chan os.Signal, 1)
		signal.Notify(sigc, syscall.SIGTERM)
		defer signal.Stop(sigc)
		<-sigc
P
Péter Szilágyi 已提交
398
		log.Info("Got sigterm, shutting swarm down...")
399 400
		stack.Stop()
	}()
401

402
	networkId := ctx.GlobalUint64(SwarmNetworkIdFlag.Name)
F
Felix Lange 已提交
403 404
	// Add bootnodes as initial peers.
	if ctx.GlobalIsSet(utils.BootnodesFlag.Name) {
405 406
		bootnodes := strings.Split(ctx.GlobalString(utils.BootnodesFlag.Name), ",")
		injectBootnodes(stack.Server(), bootnodes)
F
Felix Lange 已提交
407
	} else {
408 409 410
		if networkId == 3 {
			injectBootnodes(stack.Server(), testbetBootNodes)
		}
F
Felix Lange 已提交
411 412 413 414 415 416
	}

	stack.Wait()
	return nil
}

417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435
// detectEnsAddr determines the ENS contract address by getting both the
// version and genesis hash using the client and matching them to either
// mainnet or testnet addresses
func detectEnsAddr(client *rpc.Client) (common.Address, error) {
	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()

	var version string
	if err := client.CallContext(ctx, &version, "net_version"); err != nil {
		return common.Address{}, err
	}

	block, err := ethclient.NewClient(client).BlockByNumber(ctx, big.NewInt(0))
	if err != nil {
		return common.Address{}, err
	}

	switch {

436
	case version == "1" && block.Hash() == params.MainnetGenesisHash:
437 438 439
		log.Info("using Mainnet ENS contract address", "addr", ens.MainNetAddress)
		return ens.MainNetAddress, nil

440
	case version == "3" && block.Hash() == params.TestnetGenesisHash:
441 442 443 444 445 446 447 448
		log.Info("using Testnet ENS contract address", "addr", ens.TestNetAddress)
		return ens.TestNetAddress, nil

	default:
		return common.Address{}, fmt.Errorf("unknown version and genesis hash: %s %s", version, block.Hash())
	}
}

F
Felix Lange 已提交
449 450 451 452 453 454 455 456
func registerBzzService(ctx *cli.Context, stack *node.Node) {
	prvkey := getAccount(ctx, stack)

	chbookaddr := common.HexToAddress(ctx.GlobalString(ChequebookAddrFlag.Name))
	bzzdir := ctx.GlobalString(SwarmConfigPathFlag.Name)
	if bzzdir == "" {
		bzzdir = stack.InstanceDir()
	}
457

458
	bzzconfig, err := bzzapi.NewConfig(bzzdir, chbookaddr, prvkey, ctx.GlobalUint64(SwarmNetworkIdFlag.Name))
F
Felix Lange 已提交
459
	if err != nil {
460
		utils.Fatalf("unable to configure swarm: %v", err)
F
Felix Lange 已提交
461 462 463 464 465
	}
	bzzport := ctx.GlobalString(SwarmPortFlag.Name)
	if len(bzzport) > 0 {
		bzzconfig.Port = bzzport
	}
466 467 468
	if bzzaddr := ctx.GlobalString(SwarmListenAddrFlag.Name); bzzaddr != "" {
		bzzconfig.ListenAddr = bzzaddr
	}
469 470
	swapEnabled := ctx.GlobalBool(SwarmSwapEnabledFlag.Name)
	syncEnabled := ctx.GlobalBoolT(SwarmSyncEnabledFlag.Name)
F
Felix Lange 已提交
471

472 473 474 475 476 477 478 479
	swapapi := ctx.GlobalString(SwarmSwapAPIFlag.Name)
	if swapEnabled && swapapi == "" {
		utils.Fatalf("SWAP is enabled but --swap-api is not set")
	}

	ensapi := ctx.GlobalString(EnsAPIFlag.Name)
	ensAddr := ctx.GlobalString(EnsAddrFlag.Name)

480
	cors := ctx.GlobalString(CorsStringFlag.Name)
F
Felix Lange 已提交
481 482

	boot := func(ctx *node.ServiceContext) (node.Service, error) {
483 484 485 486
		var swapClient *ethclient.Client
		if swapapi != "" {
			log.Info("connecting to SWAP API", "url", swapapi)
			swapClient, err = ethclient.Dial(swapapi)
Z
zelig 已提交
487
			if err != nil {
488
				return nil, fmt.Errorf("error connecting to SWAP API %s: %s", swapapi, err)
Z
zelig 已提交
489
			}
F
Felix Lange 已提交
490
		}
491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513

		var ensClient *ethclient.Client
		if ensapi != "" {
			log.Info("connecting to ENS API", "url", ensapi)
			client, err := rpc.Dial(ensapi)
			if err != nil {
				return nil, fmt.Errorf("error connecting to ENS API %s: %s", ensapi, err)
			}
			ensClient = ethclient.NewClient(client)

			if ensAddr != "" {
				bzzconfig.EnsRoot = common.HexToAddress(ensAddr)
			} else {
				ensAddr, err := detectEnsAddr(client)
				if err == nil {
					bzzconfig.EnsRoot = ensAddr
				} else {
					log.Warn(fmt.Sprintf("could not determine ENS contract address, using default %s", bzzconfig.EnsRoot), "err", err)
				}
			}
		}

		return swarm.NewSwarm(ctx, swapClient, ensClient, bzzconfig, swapEnabled, syncEnabled, cors)
F
Felix Lange 已提交
514 515
	}
	if err := stack.Register(boot); err != nil {
516
		utils.Fatalf("Failed to register the Swarm service: %v", err)
F
Felix Lange 已提交
517 518 519 520 521
	}
}

func getAccount(ctx *cli.Context, stack *node.Node) *ecdsa.PrivateKey {
	keyid := ctx.GlobalString(SwarmAccountFlag.Name)
522

F
Felix Lange 已提交
523
	if keyid == "" {
524
		utils.Fatalf("Option %q is required", SwarmAccountFlag.Name)
F
Felix Lange 已提交
525 526 527
	}
	// Try to load the arg as a hex key file.
	if key, err := crypto.LoadECDSA(keyid); err == nil {
P
Péter Szilágyi 已提交
528
		log.Info("Swarm account key loaded", "address", crypto.PubkeyToAddress(key.PublicKey))
F
Felix Lange 已提交
529 530 531
		return key
	}
	// Otherwise try getting it from the keystore.
532
	am := stack.AccountManager()
533
	ks := am.Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore)
534

535
	return decryptStoreAccount(ks, keyid, utils.MakePasswordList(ctx))
F
Felix Lange 已提交
536 537
}

538
func decryptStoreAccount(ks *keystore.KeyStore, account string, passwords []string) *ecdsa.PrivateKey {
F
Felix Lange 已提交
539 540 541
	var a accounts.Account
	var err error
	if common.IsHexAddress(account) {
542 543 544 545 546 547 548
		a, err = ks.Find(accounts.Account{Address: common.HexToAddress(account)})
	} else if ix, ixerr := strconv.Atoi(account); ixerr == nil && ix > 0 {
		if accounts := ks.Accounts(); len(accounts) > ix {
			a = accounts[ix]
		} else {
			err = fmt.Errorf("index %d higher than number of accounts %d", ix, len(accounts))
		}
F
Felix Lange 已提交
549
	} else {
550
		utils.Fatalf("Can't find swarm account key %s", account)
F
Felix Lange 已提交
551 552
	}
	if err != nil {
553
		utils.Fatalf("Can't find swarm account key: %v", err)
F
Felix Lange 已提交
554
	}
555
	keyjson, err := ioutil.ReadFile(a.URL.Path)
F
Felix Lange 已提交
556
	if err != nil {
557
		utils.Fatalf("Can't load swarm account key: %v", err)
F
Felix Lange 已提交
558
	}
559 560 561
	for i := 0; i < 3; i++ {
		password := getPassPhrase(fmt.Sprintf("Unlocking swarm account %s [%d/3]", a.Address.Hex(), i+1), i, passwords)
		key, err := keystore.DecryptKey(keyjson, password)
F
Felix Lange 已提交
562 563 564 565
		if err == nil {
			return key.PrivateKey
		}
	}
566
	utils.Fatalf("Can't decrypt swarm account key")
F
Felix Lange 已提交
567 568 569
	return nil
}

570 571 572 573 574 575 576 577 578 579 580 581
// getPassPhrase retrieves the password associated with bzz account, either by fetching
// from a list of pre-loaded passwords, or by requesting it interactively from user.
func getPassPhrase(prompt string, i int, passwords []string) string {
	// non-interactive
	if len(passwords) > 0 {
		if i < len(passwords) {
			return passwords[i]
		}
		return passwords[len(passwords)-1]
	}

	// fallback to interactive mode
F
Felix Lange 已提交
582 583 584 585 586
	if prompt != "" {
		fmt.Println(prompt)
	}
	password, err := console.Stdin.PromptPassword("Passphrase: ")
	if err != nil {
587
		utils.Fatalf("Failed to read passphrase: %v", err)
F
Felix Lange 已提交
588 589 590 591 592 593 594 595
	}
	return password
}

func injectBootnodes(srv *p2p.Server, nodes []string) {
	for _, url := range nodes {
		n, err := discover.ParseNode(url)
		if err != nil {
P
Péter Szilágyi 已提交
596
			log.Error("Invalid swarm bootnode", "err", err)
597
			continue
F
Felix Lange 已提交
598 599 600 601
		}
		srv.AddPeer(n)
	}
}