github.com/divan/go-ethereum@v1.8.14-0.20180820134928-1de9ada4016d/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/discover" 41 "github.com/ethereum/go-ethereum/swarm" 42 bzzapi "github.com/ethereum/go-ethereum/swarm/api" 43 swarmmetrics "github.com/ethereum/go-ethereum/swarm/metrics" 44 "github.com/ethereum/go-ethereum/swarm/tracing" 45 sv "github.com/ethereum/go-ethereum/swarm/version" 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 ) 71 72 var ( 73 ChequebookAddrFlag = cli.StringFlag{ 74 Name: "chequebook", 75 Usage: "chequebook contract address", 76 EnvVar: SWARM_ENV_CHEQUEBOOK_ADDR, 77 } 78 SwarmAccountFlag = cli.StringFlag{ 79 Name: "bzzaccount", 80 Usage: "Swarm account key file", 81 EnvVar: SWARM_ENV_ACCOUNT, 82 } 83 SwarmListenAddrFlag = cli.StringFlag{ 84 Name: "httpaddr", 85 Usage: "Swarm HTTP API listening interface", 86 EnvVar: SWARM_ENV_LISTEN_ADDR, 87 } 88 SwarmPortFlag = cli.StringFlag{ 89 Name: "bzzport", 90 Usage: "Swarm local http api port", 91 EnvVar: SWARM_ENV_PORT, 92 } 93 SwarmNetworkIdFlag = cli.IntFlag{ 94 Name: "bzznetworkid", 95 Usage: "Network identifier (integer, default 3=swarm testnet)", 96 EnvVar: SWARM_ENV_NETWORK_ID, 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 SwarmSyncDisabledFlag = cli.BoolTFlag{ 109 Name: "nosync", 110 Usage: "Disable swarm syncing", 111 EnvVar: SWARM_ENV_SYNC_DISABLE, 112 } 113 SwarmSyncUpdateDelay = cli.DurationFlag{ 114 Name: "sync-update-delay", 115 Usage: "Duration for sync subscriptions update after no new peers are added (default 15s)", 116 EnvVar: SWARM_ENV_SYNC_UPDATE_DELAY, 117 } 118 SwarmLightNodeEnabled = cli.BoolFlag{ 119 Name: "lightnode", 120 Usage: "Enable Swarm LightNode (default false)", 121 EnvVar: SWARM_ENV_LIGHT_NODE_ENABLE, 122 } 123 SwarmDeliverySkipCheckFlag = cli.BoolFlag{ 124 Name: "delivery-skip-check", 125 Usage: "Skip chunk delivery check (default false)", 126 EnvVar: SWARM_ENV_DELIVERY_SKIP_CHECK, 127 } 128 EnsAPIFlag = cli.StringSliceFlag{ 129 Name: "ens-api", 130 Usage: "ENS API endpoint for a TLD and with contract address, can be repeated, format [tld:][contract-addr@]url", 131 EnvVar: SWARM_ENV_ENS_API, 132 } 133 SwarmApiFlag = cli.StringFlag{ 134 Name: "bzzapi", 135 Usage: "Swarm HTTP endpoint", 136 Value: "http://127.0.0.1:8500", 137 } 138 SwarmRecursiveFlag = cli.BoolFlag{ 139 Name: "recursive", 140 Usage: "Upload directories recursively", 141 } 142 SwarmWantManifestFlag = cli.BoolTFlag{ 143 Name: "manifest", 144 Usage: "Automatic manifest upload (default true)", 145 } 146 SwarmUploadDefaultPath = cli.StringFlag{ 147 Name: "defaultpath", 148 Usage: "path to file served for empty url path (none)", 149 } 150 SwarmAccessGrantKeyFlag = cli.StringFlag{ 151 Name: "grant-key", 152 Usage: "grants a given public key access to an ACT", 153 } 154 SwarmAccessGrantKeysFlag = cli.StringFlag{ 155 Name: "grant-keys", 156 Usage: "grants a given list of public keys in the following file (separated by line breaks) access to an ACT", 157 } 158 SwarmUpFromStdinFlag = cli.BoolFlag{ 159 Name: "stdin", 160 Usage: "reads data to be uploaded from stdin", 161 } 162 SwarmUploadMimeType = cli.StringFlag{ 163 Name: "mime", 164 Usage: "Manually specify MIME type", 165 } 166 SwarmEncryptedFlag = cli.BoolFlag{ 167 Name: "encrypt", 168 Usage: "use encrypted upload", 169 } 170 SwarmAccessPasswordFlag = cli.StringFlag{ 171 Name: "password", 172 Usage: "Password", 173 EnvVar: SWARM_ACCESS_PASSWORD, 174 } 175 SwarmDryRunFlag = cli.BoolFlag{ 176 Name: "dry-run", 177 Usage: "dry-run", 178 } 179 CorsStringFlag = cli.StringFlag{ 180 Name: "corsdomain", 181 Usage: "Domain on which to send Access-Control-Allow-Origin header (multiple domains can be supplied separated by a ',')", 182 EnvVar: SWARM_ENV_CORS, 183 } 184 SwarmStorePath = cli.StringFlag{ 185 Name: "store.path", 186 Usage: "Path to leveldb chunk DB (default <$GETH_ENV_DIR>/swarm/bzz-<$BZZ_KEY>/chunks)", 187 EnvVar: SWARM_ENV_STORE_PATH, 188 } 189 SwarmStoreCapacity = cli.Uint64Flag{ 190 Name: "store.size", 191 Usage: "Number of chunks (5M is roughly 20-25GB) (default 5000000)", 192 EnvVar: SWARM_ENV_STORE_CAPACITY, 193 } 194 SwarmStoreCacheCapacity = cli.UintFlag{ 195 Name: "store.cache.size", 196 Usage: "Number of recent chunks cached in memory (default 5000)", 197 EnvVar: SWARM_ENV_STORE_CACHE_CAPACITY, 198 } 199 SwarmResourceMultihashFlag = cli.BoolFlag{ 200 Name: "multihash", 201 Usage: "Determines how to interpret data for a resource update. If not present, data will be interpreted as raw, literal data that will be included in the resource", 202 } 203 SwarmResourceNameFlag = cli.StringFlag{ 204 Name: "name", 205 Usage: "User-defined name for the new resource", 206 } 207 SwarmResourceDataOnCreateFlag = cli.StringFlag{ 208 Name: "data", 209 Usage: "Initializes the resource with the given hex-encoded data. Data must be prefixed by 0x", 210 } 211 ) 212 213 //declare a few constant error messages, useful for later error check comparisons in test 214 var ( 215 SWARM_ERR_NO_BZZACCOUNT = "bzzaccount option is required but not set; check your config file, command line or environment variables" 216 SWARM_ERR_SWAP_SET_NO_API = "SWAP is enabled but --swap-api is not set" 217 ) 218 219 // this help command gets added to any subcommand that does not define it explicitly 220 var defaultSubcommandHelp = cli.Command{ 221 Action: func(ctx *cli.Context) { cli.ShowCommandHelpAndExit(ctx, "", 1) }, 222 CustomHelpTemplate: helpTemplate, 223 Name: "help", 224 Usage: "shows this help", 225 Hidden: true, 226 } 227 228 var defaultNodeConfig = node.DefaultConfig 229 230 // This init function sets defaults so cmd/swarm can run alongside geth. 231 func init() { 232 defaultNodeConfig.Name = clientIdentifier 233 defaultNodeConfig.Version = sv.VersionWithCommit(gitCommit) 234 defaultNodeConfig.P2P.ListenAddr = ":30399" 235 defaultNodeConfig.IPCPath = "bzzd.ipc" 236 // Set flag defaults for --help display. 237 utils.ListenPortFlag.Value = 30399 238 } 239 240 var app = utils.NewApp(gitCommit, "Ethereum Swarm") 241 242 // This init function creates the cli.App. 243 func init() { 244 app.Action = bzzd 245 app.HideVersion = true // we have a command to print the version 246 app.Copyright = "Copyright 2013-2016 The go-ethereum Authors" 247 app.Commands = []cli.Command{ 248 { 249 Action: version, 250 CustomHelpTemplate: helpTemplate, 251 Name: "version", 252 Usage: "Print version numbers", 253 Description: "The output of this command is supposed to be machine-readable", 254 }, 255 { 256 Action: upload, 257 CustomHelpTemplate: helpTemplate, 258 Name: "up", 259 Usage: "uploads a file or directory to swarm using the HTTP API", 260 ArgsUsage: "<file>", 261 Flags: []cli.Flag{SwarmEncryptedFlag}, 262 Description: "uploads a file or directory to swarm using the HTTP API and prints the root hash", 263 }, 264 { 265 CustomHelpTemplate: helpTemplate, 266 Name: "access", 267 Usage: "encrypts a reference and embeds it into a root manifest", 268 ArgsUsage: "<ref>", 269 Description: "encrypts a reference and embeds it into a root manifest", 270 Subcommands: []cli.Command{ 271 { 272 CustomHelpTemplate: helpTemplate, 273 Name: "new", 274 Usage: "encrypts a reference and embeds it into a root manifest", 275 ArgsUsage: "<ref>", 276 Description: "encrypts a reference and embeds it into a root access manifest and prints the resulting manifest", 277 Subcommands: []cli.Command{ 278 { 279 Action: accessNewPass, 280 CustomHelpTemplate: helpTemplate, 281 Flags: []cli.Flag{ 282 utils.PasswordFileFlag, 283 SwarmDryRunFlag, 284 }, 285 Name: "pass", 286 Usage: "encrypts a reference with a password and embeds it into a root manifest", 287 ArgsUsage: "<ref>", 288 Description: "encrypts a reference and embeds it into a root access manifest and prints the resulting manifest", 289 }, 290 { 291 Action: accessNewPK, 292 CustomHelpTemplate: helpTemplate, 293 Flags: []cli.Flag{ 294 utils.PasswordFileFlag, 295 SwarmDryRunFlag, 296 SwarmAccessGrantKeyFlag, 297 }, 298 Name: "pk", 299 Usage: "encrypts a reference with the node's private key and a given grantee's public key and embeds it into a root manifest", 300 ArgsUsage: "<ref>", 301 Description: "encrypts a reference and embeds it into a root access manifest and prints the resulting manifest", 302 }, 303 { 304 Action: accessNewACT, 305 CustomHelpTemplate: helpTemplate, 306 Flags: []cli.Flag{ 307 SwarmAccessGrantKeysFlag, 308 SwarmDryRunFlag, 309 }, 310 Name: "act", 311 Usage: "encrypts a reference with the node's private key and a given grantee's public key and embeds it into a root manifest", 312 ArgsUsage: "<ref>", 313 Description: "encrypts a reference and embeds it into a root access manifest and prints the resulting manifest", 314 }, 315 }, 316 }, 317 }, 318 }, 319 { 320 CustomHelpTemplate: helpTemplate, 321 Name: "resource", 322 Usage: "(Advanced) Create and update Mutable Resources", 323 ArgsUsage: "<create|update|info>", 324 Description: "Works with Mutable Resource Updates", 325 Subcommands: []cli.Command{ 326 { 327 Action: resourceCreate, 328 CustomHelpTemplate: helpTemplate, 329 Name: "create", 330 Usage: "creates a new Mutable Resource", 331 ArgsUsage: "<frequency>", 332 Description: "creates a new Mutable Resource", 333 Flags: []cli.Flag{SwarmResourceNameFlag, SwarmResourceDataOnCreateFlag, SwarmResourceMultihashFlag}, 334 }, 335 { 336 Action: resourceUpdate, 337 CustomHelpTemplate: helpTemplate, 338 Name: "update", 339 Usage: "updates the content of an existing Mutable Resource", 340 ArgsUsage: "<Manifest Address or ENS domain> <0x Hex data>", 341 Description: "updates the content of an existing Mutable Resource", 342 Flags: []cli.Flag{SwarmResourceMultihashFlag}, 343 }, 344 { 345 Action: resourceInfo, 346 CustomHelpTemplate: helpTemplate, 347 Name: "info", 348 Usage: "obtains information about an existing Mutable Resource", 349 ArgsUsage: "<Manifest Address or ENS domain>", 350 Description: "obtains information about an existing Mutable Resource", 351 }, 352 }, 353 }, 354 { 355 Action: list, 356 CustomHelpTemplate: helpTemplate, 357 Name: "ls", 358 Usage: "list files and directories contained in a manifest", 359 ArgsUsage: "<manifest> [<prefix>]", 360 Description: "Lists files and directories contained in a manifest", 361 }, 362 { 363 Action: hash, 364 CustomHelpTemplate: helpTemplate, 365 Name: "hash", 366 Usage: "print the swarm hash of a file or directory", 367 ArgsUsage: "<file>", 368 Description: "Prints the swarm hash of file or directory", 369 }, 370 { 371 Action: download, 372 Name: "down", 373 Flags: []cli.Flag{SwarmRecursiveFlag, SwarmAccessPasswordFlag}, 374 Usage: "downloads a swarm manifest or a file inside a manifest", 375 ArgsUsage: " <uri> [<dir>]", 376 Description: `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.`, 377 }, 378 { 379 Name: "manifest", 380 CustomHelpTemplate: helpTemplate, 381 Usage: "perform operations on swarm manifests", 382 ArgsUsage: "COMMAND", 383 Description: "Updates a MANIFEST by adding/removing/updating the hash of a path.\nCOMMAND could be: add, update, remove", 384 Subcommands: []cli.Command{ 385 { 386 Action: manifestAdd, 387 CustomHelpTemplate: helpTemplate, 388 Name: "add", 389 Usage: "add a new path to the manifest", 390 ArgsUsage: "<MANIFEST> <path> <hash>", 391 Description: "Adds a new path to the manifest", 392 }, 393 { 394 Action: manifestUpdate, 395 CustomHelpTemplate: helpTemplate, 396 Name: "update", 397 Usage: "update the hash for an already existing path in the manifest", 398 ArgsUsage: "<MANIFEST> <path> <newhash>", 399 Description: "Update the hash for an already existing path in the manifest", 400 }, 401 { 402 Action: manifestRemove, 403 CustomHelpTemplate: helpTemplate, 404 Name: "remove", 405 Usage: "removes a path from the manifest", 406 ArgsUsage: "<MANIFEST> <path>", 407 Description: "Removes a path from the manifest", 408 }, 409 }, 410 }, 411 { 412 Name: "fs", 413 CustomHelpTemplate: helpTemplate, 414 Usage: "perform FUSE operations", 415 ArgsUsage: "fs COMMAND", 416 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", 417 Subcommands: []cli.Command{ 418 { 419 Action: mount, 420 CustomHelpTemplate: helpTemplate, 421 Name: "mount", 422 Flags: []cli.Flag{utils.IPCPathFlag}, 423 Usage: "mount a swarm hash to a mount point", 424 ArgsUsage: "swarm fs mount --ipcpath <path to bzzd.ipc> <manifest hash> <mount point>", 425 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", 426 }, 427 { 428 Action: unmount, 429 CustomHelpTemplate: helpTemplate, 430 Name: "unmount", 431 Flags: []cli.Flag{utils.IPCPathFlag}, 432 Usage: "unmount a swarmfs mount", 433 ArgsUsage: "swarm fs unmount --ipcpath <path to bzzd.ipc> <mount point>", 434 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", 435 }, 436 { 437 Action: listMounts, 438 CustomHelpTemplate: helpTemplate, 439 Name: "list", 440 Flags: []cli.Flag{utils.IPCPathFlag}, 441 Usage: "list swarmfs mounts", 442 ArgsUsage: "swarm fs list --ipcpath <path to bzzd.ipc>", 443 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", 444 }, 445 }, 446 }, 447 { 448 Name: "db", 449 CustomHelpTemplate: helpTemplate, 450 Usage: "manage the local chunk database", 451 ArgsUsage: "db COMMAND", 452 Description: "Manage the local chunk database", 453 Subcommands: []cli.Command{ 454 { 455 Action: dbExport, 456 CustomHelpTemplate: helpTemplate, 457 Name: "export", 458 Usage: "export a local chunk database as a tar archive (use - to send to stdout)", 459 ArgsUsage: "<chunkdb> <file>", 460 Description: ` 461 Export a local chunk database as a tar archive (use - to send to stdout). 462 463 swarm db export ~/.ethereum/swarm/bzz-KEY/chunks chunks.tar 464 465 The export may be quite large, consider piping the output through the Unix 466 pv(1) tool to get a progress bar: 467 468 swarm db export ~/.ethereum/swarm/bzz-KEY/chunks - | pv > chunks.tar 469 `, 470 }, 471 { 472 Action: dbImport, 473 CustomHelpTemplate: helpTemplate, 474 Name: "import", 475 Usage: "import chunks from a tar archive into a local chunk database (use - to read from stdin)", 476 ArgsUsage: "<chunkdb> <file>", 477 Description: `Import chunks from a tar archive into a local chunk database (use - to read from stdin). 478 479 swarm db import ~/.ethereum/swarm/bzz-KEY/chunks chunks.tar 480 481 The import may be quite large, consider piping the input through the Unix 482 pv(1) tool to get a progress bar: 483 484 pv chunks.tar | swarm db import ~/.ethereum/swarm/bzz-KEY/chunks -`, 485 }, 486 { 487 Action: dbClean, 488 CustomHelpTemplate: helpTemplate, 489 Name: "clean", 490 Usage: "remove corrupt entries from a local chunk database", 491 ArgsUsage: "<chunkdb>", 492 Description: "Remove corrupt entries from a local chunk database", 493 }, 494 }, 495 }, 496 497 // See config.go 498 DumpConfigCommand, 499 } 500 501 // append a hidden help subcommand to all commands that have subcommands 502 // if a help command was already defined above, that one will take precedence. 503 addDefaultHelpSubcommands(app.Commands) 504 505 sort.Sort(cli.CommandsByName(app.Commands)) 506 507 app.Flags = []cli.Flag{ 508 utils.IdentityFlag, 509 utils.DataDirFlag, 510 utils.BootnodesFlag, 511 utils.KeyStoreDirFlag, 512 utils.ListenPortFlag, 513 utils.NoDiscoverFlag, 514 utils.DiscoveryV5Flag, 515 utils.NetrestrictFlag, 516 utils.NodeKeyFileFlag, 517 utils.NodeKeyHexFlag, 518 utils.MaxPeersFlag, 519 utils.NATFlag, 520 utils.IPCDisabledFlag, 521 utils.IPCPathFlag, 522 utils.PasswordFileFlag, 523 // bzzd-specific flags 524 CorsStringFlag, 525 EnsAPIFlag, 526 SwarmTomlConfigPathFlag, 527 SwarmSwapEnabledFlag, 528 SwarmSwapAPIFlag, 529 SwarmSyncDisabledFlag, 530 SwarmSyncUpdateDelay, 531 SwarmLightNodeEnabled, 532 SwarmDeliverySkipCheckFlag, 533 SwarmListenAddrFlag, 534 SwarmPortFlag, 535 SwarmAccountFlag, 536 SwarmNetworkIdFlag, 537 ChequebookAddrFlag, 538 // upload flags 539 SwarmApiFlag, 540 SwarmRecursiveFlag, 541 SwarmWantManifestFlag, 542 SwarmUploadDefaultPath, 543 SwarmUpFromStdinFlag, 544 SwarmUploadMimeType, 545 // storage flags 546 SwarmStorePath, 547 SwarmStoreCapacity, 548 SwarmStoreCacheCapacity, 549 } 550 rpcFlags := []cli.Flag{ 551 utils.WSEnabledFlag, 552 utils.WSListenAddrFlag, 553 utils.WSPortFlag, 554 utils.WSApiFlag, 555 utils.WSAllowedOriginsFlag, 556 } 557 app.Flags = append(app.Flags, rpcFlags...) 558 app.Flags = append(app.Flags, debug.Flags...) 559 app.Flags = append(app.Flags, swarmmetrics.Flags...) 560 app.Flags = append(app.Flags, tracing.Flags...) 561 app.Before = func(ctx *cli.Context) error { 562 runtime.GOMAXPROCS(runtime.NumCPU()) 563 if err := debug.Setup(ctx, ""); err != nil { 564 return err 565 } 566 swarmmetrics.Setup(ctx) 567 tracing.Setup(ctx) 568 return nil 569 } 570 app.After = func(ctx *cli.Context) error { 571 debug.Exit() 572 return nil 573 } 574 } 575 576 func main() { 577 if err := app.Run(os.Args); err != nil { 578 fmt.Fprintln(os.Stderr, err) 579 os.Exit(1) 580 } 581 } 582 583 func version(ctx *cli.Context) error { 584 fmt.Println(strings.Title(clientIdentifier)) 585 fmt.Println("Version:", sv.VersionWithMeta) 586 if gitCommit != "" { 587 fmt.Println("Git Commit:", gitCommit) 588 } 589 fmt.Println("Go Version:", runtime.Version()) 590 fmt.Println("OS:", runtime.GOOS) 591 return nil 592 } 593 594 func bzzd(ctx *cli.Context) error { 595 //build a valid bzzapi.Config from all available sources: 596 //default config, file config, command line and env vars 597 598 bzzconfig, err := buildConfig(ctx) 599 if err != nil { 600 utils.Fatalf("unable to configure swarm: %v", err) 601 } 602 603 cfg := defaultNodeConfig 604 605 //pss operates on ws 606 cfg.WSModules = append(cfg.WSModules, "pss") 607 608 //geth only supports --datadir via command line 609 //in order to be consistent within swarm, if we pass --datadir via environment variable 610 //or via config file, we get the same directory for geth and swarm 611 if _, err := os.Stat(bzzconfig.Path); err == nil { 612 cfg.DataDir = bzzconfig.Path 613 } 614 615 //optionally set the bootnodes before configuring the node 616 setSwarmBootstrapNodes(ctx, &cfg) 617 //setup the ethereum node 618 utils.SetNodeConfig(ctx, &cfg) 619 stack, err := node.New(&cfg) 620 if err != nil { 621 utils.Fatalf("can't create node: %v", err) 622 } 623 624 //a few steps need to be done after the config phase is completed, 625 //due to overriding behavior 626 initSwarmNode(bzzconfig, stack, ctx) 627 //register BZZ as node.Service in the ethereum node 628 registerBzzService(bzzconfig, stack) 629 //start the node 630 utils.StartNode(stack) 631 632 go func() { 633 sigc := make(chan os.Signal, 1) 634 signal.Notify(sigc, syscall.SIGTERM) 635 defer signal.Stop(sigc) 636 <-sigc 637 log.Info("Got sigterm, shutting swarm down...") 638 stack.Stop() 639 }() 640 641 stack.Wait() 642 return nil 643 } 644 645 func registerBzzService(bzzconfig *bzzapi.Config, stack *node.Node) { 646 //define the swarm service boot function 647 boot := func(_ *node.ServiceContext) (node.Service, error) { 648 // In production, mockStore must be always nil. 649 return swarm.NewSwarm(bzzconfig, nil) 650 } 651 //register within the ethereum node 652 if err := stack.Register(boot); err != nil { 653 utils.Fatalf("Failed to register the Swarm service: %v", err) 654 } 655 } 656 657 func getAccount(bzzaccount string, ctx *cli.Context, stack *node.Node) *ecdsa.PrivateKey { 658 //an account is mandatory 659 if bzzaccount == "" { 660 utils.Fatalf(SWARM_ERR_NO_BZZACCOUNT) 661 } 662 // Try to load the arg as a hex key file. 663 if key, err := crypto.LoadECDSA(bzzaccount); err == nil { 664 log.Info("Swarm account key loaded", "address", crypto.PubkeyToAddress(key.PublicKey)) 665 return key 666 } 667 // Otherwise try getting it from the keystore. 668 am := stack.AccountManager() 669 ks := am.Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore) 670 671 return decryptStoreAccount(ks, bzzaccount, utils.MakePasswordList(ctx)) 672 } 673 674 // getPrivKey returns the private key of the specified bzzaccount 675 // Used only by client commands, such as `resource` 676 func getPrivKey(ctx *cli.Context) *ecdsa.PrivateKey { 677 // booting up the swarm node just as we do in bzzd action 678 bzzconfig, err := buildConfig(ctx) 679 if err != nil { 680 utils.Fatalf("unable to configure swarm: %v", err) 681 } 682 cfg := defaultNodeConfig 683 if _, err := os.Stat(bzzconfig.Path); err == nil { 684 cfg.DataDir = bzzconfig.Path 685 } 686 utils.SetNodeConfig(ctx, &cfg) 687 stack, err := node.New(&cfg) 688 if err != nil { 689 utils.Fatalf("can't create node: %v", err) 690 } 691 return getAccount(bzzconfig.BzzAccount, ctx, stack) 692 } 693 694 func decryptStoreAccount(ks *keystore.KeyStore, account string, passwords []string) *ecdsa.PrivateKey { 695 var a accounts.Account 696 var err error 697 if common.IsHexAddress(account) { 698 a, err = ks.Find(accounts.Account{Address: common.HexToAddress(account)}) 699 } else if ix, ixerr := strconv.Atoi(account); ixerr == nil && ix > 0 { 700 if accounts := ks.Accounts(); len(accounts) > ix { 701 a = accounts[ix] 702 } else { 703 err = fmt.Errorf("index %d higher than number of accounts %d", ix, len(accounts)) 704 } 705 } else { 706 utils.Fatalf("Can't find swarm account key %s", account) 707 } 708 if err != nil { 709 utils.Fatalf("Can't find swarm account key: %v - Is the provided bzzaccount(%s) from the right datadir/Path?", err, account) 710 } 711 keyjson, err := ioutil.ReadFile(a.URL.Path) 712 if err != nil { 713 utils.Fatalf("Can't load swarm account key: %v", err) 714 } 715 for i := 0; i < 3; i++ { 716 password := getPassPhrase(fmt.Sprintf("Unlocking swarm account %s [%d/3]", a.Address.Hex(), i+1), i, passwords) 717 key, err := keystore.DecryptKey(keyjson, password) 718 if err == nil { 719 return key.PrivateKey 720 } 721 } 722 utils.Fatalf("Can't decrypt swarm account key") 723 return nil 724 } 725 726 // getPassPhrase retrieves the password associated with bzz account, either by fetching 727 // from a list of pre-loaded passwords, or by requesting it interactively from user. 728 func getPassPhrase(prompt string, i int, passwords []string) string { 729 // non-interactive 730 if len(passwords) > 0 { 731 if i < len(passwords) { 732 return passwords[i] 733 } 734 return passwords[len(passwords)-1] 735 } 736 737 // fallback to interactive mode 738 if prompt != "" { 739 fmt.Println(prompt) 740 } 741 password, err := console.Stdin.PromptPassword("Passphrase: ") 742 if err != nil { 743 utils.Fatalf("Failed to read passphrase: %v", err) 744 } 745 return password 746 } 747 748 // addDefaultHelpSubcommand scans through defined CLI commands and adds 749 // a basic help subcommand to each 750 // if a help command is already defined, it will take precedence over the default. 751 func addDefaultHelpSubcommands(commands []cli.Command) { 752 for i := range commands { 753 cmd := &commands[i] 754 if cmd.Subcommands != nil { 755 cmd.Subcommands = append(cmd.Subcommands, defaultSubcommandHelp) 756 addDefaultHelpSubcommands(cmd.Subcommands) 757 } 758 } 759 } 760 761 func setSwarmBootstrapNodes(ctx *cli.Context, cfg *node.Config) { 762 if ctx.GlobalIsSet(utils.BootnodesFlag.Name) || ctx.GlobalIsSet(utils.BootnodesV4Flag.Name) { 763 return 764 } 765 766 cfg.P2P.BootstrapNodes = []*discover.Node{} 767 768 for _, url := range SwarmBootnodes { 769 node, err := discover.ParseNode(url) 770 if err != nil { 771 log.Error("Bootstrap URL invalid", "enode", url, "err", err) 772 } 773 cfg.P2P.BootstrapNodes = append(cfg.P2P.BootstrapNodes, node) 774 } 775 log.Debug("added default swarm bootnodes", "length", len(cfg.P2P.BootstrapNodes)) 776 }