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 }