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