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