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