github.com/alanchchen/go-ethereum@v1.6.6-0.20170601190819-6171d01b1195/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 "strconv" 27 "strings" 28 "syscall" 29 30 "github.com/ethereum/go-ethereum/accounts" 31 "github.com/ethereum/go-ethereum/accounts/keystore" 32 "github.com/ethereum/go-ethereum/cmd/utils" 33 "github.com/ethereum/go-ethereum/common" 34 "github.com/ethereum/go-ethereum/console" 35 "github.com/ethereum/go-ethereum/crypto" 36 "github.com/ethereum/go-ethereum/ethclient" 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 "gopkg.in/urfave/cli.v1" 46 ) 47 48 const clientIdentifier = "swarm" 49 50 var ( 51 gitCommit string // Git SHA1 commit hash of the release (set via linker flags) 52 testbetBootNodes = []string{ 53 "enode://ec8ae764f7cb0417bdfb009b9d0f18ab3818a3a4e8e7c67dd5f18971a93510a2e6f43cd0b69a27e439a9629457ea804104f37c85e41eed057d3faabbf7744cdf@13.74.157.139:30429", 54 "enode://c2e1fceb3bf3be19dff71eec6cccf19f2dbf7567ee017d130240c670be8594bc9163353ca55dd8df7a4f161dd94b36d0615c17418b5a3cdcbb4e9d99dfa4de37@13.74.157.139:30430", 55 "enode://fe29b82319b734ce1ec68b84657d57145fee237387e63273989d354486731e59f78858e452ef800a020559da22dcca759536e6aa5517c53930d29ce0b1029286@13.74.157.139:30431", 56 "enode://1d7187e7bde45cf0bee489ce9852dd6d1a0d9aa67a33a6b8e6db8a4fbc6fcfa6f0f1a5419343671521b863b187d1c73bad3603bae66421d157ffef357669ddb8@13.74.157.139:30432", 57 "enode://0e4cba800f7b1ee73673afa6a4acead4018f0149d2e3216be3f133318fd165b324cd71b81fbe1e80deac8dbf56e57a49db7be67f8b9bc81bd2b7ee496434fb5d@13.74.157.139:30433", 58 } 59 ) 60 61 var ( 62 ChequebookAddrFlag = cli.StringFlag{ 63 Name: "chequebook", 64 Usage: "chequebook contract address", 65 } 66 SwarmAccountFlag = cli.StringFlag{ 67 Name: "bzzaccount", 68 Usage: "Swarm account key file", 69 } 70 SwarmListenAddrFlag = cli.StringFlag{ 71 Name: "httpaddr", 72 Usage: "Swarm HTTP API listening interface", 73 } 74 SwarmPortFlag = cli.StringFlag{ 75 Name: "bzzport", 76 Usage: "Swarm local http api port", 77 } 78 SwarmNetworkIdFlag = cli.IntFlag{ 79 Name: "bzznetworkid", 80 Usage: "Network identifier (integer, default 3=swarm testnet)", 81 } 82 SwarmConfigPathFlag = cli.StringFlag{ 83 Name: "bzzconfig", 84 Usage: "Swarm config file path (datadir/bzz)", 85 } 86 SwarmSwapEnabledFlag = cli.BoolFlag{ 87 Name: "swap", 88 Usage: "Swarm SWAP enabled (default false)", 89 } 90 SwarmSyncEnabledFlag = cli.BoolTFlag{ 91 Name: "sync", 92 Usage: "Swarm Syncing enabled (default true)", 93 } 94 EthAPIFlag = cli.StringFlag{ 95 Name: "ethapi", 96 Usage: "URL of the Ethereum API provider", 97 Value: node.DefaultIPCEndpoint("geth"), 98 } 99 SwarmApiFlag = cli.StringFlag{ 100 Name: "bzzapi", 101 Usage: "Swarm HTTP endpoint", 102 Value: "http://127.0.0.1:8500", 103 } 104 SwarmRecursiveUploadFlag = cli.BoolFlag{ 105 Name: "recursive", 106 Usage: "Upload directories recursively", 107 } 108 SwarmWantManifestFlag = cli.BoolTFlag{ 109 Name: "manifest", 110 Usage: "Automatic manifest upload", 111 } 112 SwarmUploadDefaultPath = cli.StringFlag{ 113 Name: "defaultpath", 114 Usage: "path to file served for empty url path (none)", 115 } 116 SwarmUpFromStdinFlag = cli.BoolFlag{ 117 Name: "stdin", 118 Usage: "reads data to be uploaded from stdin", 119 } 120 SwarmUploadMimeType = cli.StringFlag{ 121 Name: "mime", 122 Usage: "force mime type", 123 } 124 CorsStringFlag = cli.StringFlag{ 125 Name: "corsdomain", 126 Usage: "Domain on which to send Access-Control-Allow-Origin header (multiple domains can be supplied separated by a ',')", 127 } 128 ) 129 130 var defaultNodeConfig = node.DefaultConfig 131 132 // This init function sets defaults so cmd/swarm can run alongside geth. 133 func init() { 134 defaultNodeConfig.Name = clientIdentifier 135 defaultNodeConfig.Version = params.VersionWithCommit(gitCommit) 136 defaultNodeConfig.P2P.ListenAddr = ":30399" 137 defaultNodeConfig.IPCPath = "bzzd.ipc" 138 // Set flag defaults for --help display. 139 utils.ListenPortFlag.Value = 30399 140 } 141 142 var app = utils.NewApp(gitCommit, "Ethereum Swarm") 143 144 // This init function creates the cli.App. 145 func init() { 146 app.Action = bzzd 147 app.HideVersion = true // we have a command to print the version 148 app.Copyright = "Copyright 2013-2016 The go-ethereum Authors" 149 app.Commands = []cli.Command{ 150 { 151 Action: version, 152 Name: "version", 153 Usage: "Print version numbers", 154 ArgsUsage: " ", 155 Description: ` 156 The output of this command is supposed to be machine-readable. 157 `, 158 }, 159 { 160 Action: upload, 161 Name: "up", 162 Usage: "upload a file or directory to swarm using the HTTP API", 163 ArgsUsage: " <file>", 164 Description: ` 165 "upload a file or directory to swarm using the HTTP API and prints the root hash", 166 `, 167 }, 168 { 169 Action: list, 170 Name: "ls", 171 Usage: "list files and directories contained in a manifest", 172 ArgsUsage: " <manifest> [<prefix>]", 173 Description: ` 174 Lists files and directories contained in a manifest. 175 `, 176 }, 177 { 178 Action: hash, 179 Name: "hash", 180 Usage: "print the swarm hash of a file or directory", 181 ArgsUsage: " <file>", 182 Description: ` 183 Prints the swarm hash of file or directory. 184 `, 185 }, 186 { 187 Name: "manifest", 188 Usage: "update a MANIFEST", 189 ArgsUsage: "manifest COMMAND", 190 Description: ` 191 Updates a MANIFEST by adding/removing/updating the hash of a path. 192 `, 193 Subcommands: []cli.Command{ 194 { 195 Action: add, 196 Name: "add", 197 Usage: "add a new path to the manifest", 198 ArgsUsage: "<MANIFEST> <path> <hash> [<content-type>]", 199 Description: ` 200 Adds a new path to the manifest 201 `, 202 }, 203 { 204 Action: update, 205 Name: "update", 206 Usage: "update the hash for an already existing path in the manifest", 207 ArgsUsage: "<MANIFEST> <path> <newhash> [<newcontent-type>]", 208 Description: ` 209 Update the hash for an already existing path in the manifest 210 `, 211 }, 212 { 213 Action: remove, 214 Name: "remove", 215 Usage: "removes a path from the manifest", 216 ArgsUsage: "<MANIFEST> <path>", 217 Description: ` 218 Removes a path from the manifest 219 `, 220 }, 221 }, 222 }, 223 { 224 Action: cleandb, 225 Name: "cleandb", 226 Usage: "Cleans database of corrupted entries", 227 ArgsUsage: " ", 228 Description: ` 229 Cleans database of corrupted entries. 230 `, 231 }, 232 } 233 234 app.Flags = []cli.Flag{ 235 utils.IdentityFlag, 236 utils.DataDirFlag, 237 utils.BootnodesFlag, 238 utils.KeyStoreDirFlag, 239 utils.ListenPortFlag, 240 utils.NoDiscoverFlag, 241 utils.DiscoveryV5Flag, 242 utils.NetrestrictFlag, 243 utils.NodeKeyFileFlag, 244 utils.NodeKeyHexFlag, 245 utils.MaxPeersFlag, 246 utils.NATFlag, 247 utils.IPCDisabledFlag, 248 utils.IPCPathFlag, 249 utils.PasswordFileFlag, 250 // bzzd-specific flags 251 CorsStringFlag, 252 EthAPIFlag, 253 SwarmConfigPathFlag, 254 SwarmSwapEnabledFlag, 255 SwarmSyncEnabledFlag, 256 SwarmListenAddrFlag, 257 SwarmPortFlag, 258 SwarmAccountFlag, 259 SwarmNetworkIdFlag, 260 ChequebookAddrFlag, 261 // upload flags 262 SwarmApiFlag, 263 SwarmRecursiveUploadFlag, 264 SwarmWantManifestFlag, 265 SwarmUploadDefaultPath, 266 SwarmUpFromStdinFlag, 267 SwarmUploadMimeType, 268 } 269 app.Flags = append(app.Flags, debug.Flags...) 270 app.Before = func(ctx *cli.Context) error { 271 runtime.GOMAXPROCS(runtime.NumCPU()) 272 return debug.Setup(ctx) 273 } 274 app.After = func(ctx *cli.Context) error { 275 debug.Exit() 276 return nil 277 } 278 } 279 280 func main() { 281 if err := app.Run(os.Args); err != nil { 282 fmt.Fprintln(os.Stderr, err) 283 os.Exit(1) 284 } 285 } 286 287 func version(ctx *cli.Context) error { 288 fmt.Println(strings.Title(clientIdentifier)) 289 fmt.Println("Version:", params.Version) 290 if gitCommit != "" { 291 fmt.Println("Git Commit:", gitCommit) 292 } 293 fmt.Println("Network Id:", ctx.GlobalInt(utils.NetworkIdFlag.Name)) 294 fmt.Println("Go Version:", runtime.Version()) 295 fmt.Println("OS:", runtime.GOOS) 296 fmt.Printf("GOPATH=%s\n", os.Getenv("GOPATH")) 297 fmt.Printf("GOROOT=%s\n", runtime.GOROOT()) 298 return nil 299 } 300 301 func bzzd(ctx *cli.Context) error { 302 cfg := defaultNodeConfig 303 utils.SetNodeConfig(ctx, &cfg) 304 stack, err := node.New(&cfg) 305 if err != nil { 306 utils.Fatalf("can't create node: %v", err) 307 } 308 309 registerBzzService(ctx, stack) 310 utils.StartNode(stack) 311 312 go func() { 313 sigc := make(chan os.Signal, 1) 314 signal.Notify(sigc, syscall.SIGTERM) 315 defer signal.Stop(sigc) 316 <-sigc 317 log.Info("Got sigterm, shutting swarm down...") 318 stack.Stop() 319 }() 320 321 networkId := ctx.GlobalUint64(SwarmNetworkIdFlag.Name) 322 // Add bootnodes as initial peers. 323 if ctx.GlobalIsSet(utils.BootnodesFlag.Name) { 324 bootnodes := strings.Split(ctx.GlobalString(utils.BootnodesFlag.Name), ",") 325 injectBootnodes(stack.Server(), bootnodes) 326 } else { 327 if networkId == 3 { 328 injectBootnodes(stack.Server(), testbetBootNodes) 329 } 330 } 331 332 stack.Wait() 333 return nil 334 } 335 336 func registerBzzService(ctx *cli.Context, stack *node.Node) { 337 prvkey := getAccount(ctx, stack) 338 339 chbookaddr := common.HexToAddress(ctx.GlobalString(ChequebookAddrFlag.Name)) 340 bzzdir := ctx.GlobalString(SwarmConfigPathFlag.Name) 341 if bzzdir == "" { 342 bzzdir = stack.InstanceDir() 343 } 344 345 bzzconfig, err := bzzapi.NewConfig(bzzdir, chbookaddr, prvkey, ctx.GlobalUint64(SwarmNetworkIdFlag.Name)) 346 if err != nil { 347 utils.Fatalf("unable to configure swarm: %v", err) 348 } 349 bzzport := ctx.GlobalString(SwarmPortFlag.Name) 350 if len(bzzport) > 0 { 351 bzzconfig.Port = bzzport 352 } 353 if bzzaddr := ctx.GlobalString(SwarmListenAddrFlag.Name); bzzaddr != "" { 354 bzzconfig.ListenAddr = bzzaddr 355 } 356 swapEnabled := ctx.GlobalBool(SwarmSwapEnabledFlag.Name) 357 syncEnabled := ctx.GlobalBoolT(SwarmSyncEnabledFlag.Name) 358 359 ethapi := ctx.GlobalString(EthAPIFlag.Name) 360 cors := ctx.GlobalString(CorsStringFlag.Name) 361 362 boot := func(ctx *node.ServiceContext) (node.Service, error) { 363 var client *ethclient.Client 364 if len(ethapi) > 0 { 365 client, err = ethclient.Dial(ethapi) 366 if err != nil { 367 utils.Fatalf("Can't connect: %v", err) 368 } 369 } else { 370 swapEnabled = false 371 } 372 return swarm.NewSwarm(ctx, client, bzzconfig, swapEnabled, syncEnabled, cors) 373 } 374 if err := stack.Register(boot); err != nil { 375 utils.Fatalf("Failed to register the Swarm service: %v", err) 376 } 377 } 378 379 func getAccount(ctx *cli.Context, stack *node.Node) *ecdsa.PrivateKey { 380 keyid := ctx.GlobalString(SwarmAccountFlag.Name) 381 382 if keyid == "" { 383 utils.Fatalf("Option %q is required", SwarmAccountFlag.Name) 384 } 385 // Try to load the arg as a hex key file. 386 if key, err := crypto.LoadECDSA(keyid); err == nil { 387 log.Info("Swarm account key loaded", "address", crypto.PubkeyToAddress(key.PublicKey)) 388 return key 389 } 390 // Otherwise try getting it from the keystore. 391 am := stack.AccountManager() 392 ks := am.Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore) 393 394 return decryptStoreAccount(ks, keyid, utils.MakePasswordList(ctx)) 395 } 396 397 func decryptStoreAccount(ks *keystore.KeyStore, account string, passwords []string) *ecdsa.PrivateKey { 398 var a accounts.Account 399 var err error 400 if common.IsHexAddress(account) { 401 a, err = ks.Find(accounts.Account{Address: common.HexToAddress(account)}) 402 } else if ix, ixerr := strconv.Atoi(account); ixerr == nil && ix > 0 { 403 if accounts := ks.Accounts(); len(accounts) > ix { 404 a = accounts[ix] 405 } else { 406 err = fmt.Errorf("index %d higher than number of accounts %d", ix, len(accounts)) 407 } 408 } else { 409 utils.Fatalf("Can't find swarm account key %s", account) 410 } 411 if err != nil { 412 utils.Fatalf("Can't find swarm account key: %v", err) 413 } 414 keyjson, err := ioutil.ReadFile(a.URL.Path) 415 if err != nil { 416 utils.Fatalf("Can't load swarm account key: %v", err) 417 } 418 for i := 0; i < 3; i++ { 419 password := getPassPhrase(fmt.Sprintf("Unlocking swarm account %s [%d/3]", a.Address.Hex(), i+1), i, passwords) 420 key, err := keystore.DecryptKey(keyjson, password) 421 if err == nil { 422 return key.PrivateKey 423 } 424 } 425 utils.Fatalf("Can't decrypt swarm account key") 426 return nil 427 } 428 429 // getPassPhrase retrieves the password associated with bzz account, either by fetching 430 // from a list of pre-loaded passwords, or by requesting it interactively from user. 431 func getPassPhrase(prompt string, i int, passwords []string) string { 432 // non-interactive 433 if len(passwords) > 0 { 434 if i < len(passwords) { 435 return passwords[i] 436 } 437 return passwords[len(passwords)-1] 438 } 439 440 // fallback to interactive mode 441 if prompt != "" { 442 fmt.Println(prompt) 443 } 444 password, err := console.Stdin.PromptPassword("Passphrase: ") 445 if err != nil { 446 utils.Fatalf("Failed to read passphrase: %v", err) 447 } 448 return password 449 } 450 451 func injectBootnodes(srv *p2p.Server, nodes []string) { 452 for _, url := range nodes { 453 n, err := discover.ParseNode(url) 454 if err != nil { 455 log.Error("Invalid swarm bootnode", "err", err) 456 continue 457 } 458 srv.AddPeer(n) 459 } 460 }