github.com/letterj/go-ethereum@v1.8.22-0.20190204142846-520024dfd689/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 "encoding/hex" 22 "fmt" 23 "io/ioutil" 24 "os" 25 "os/signal" 26 "runtime" 27 "sort" 28 "strconv" 29 "strings" 30 "syscall" 31 32 "github.com/ethereum/go-ethereum/accounts" 33 "github.com/ethereum/go-ethereum/accounts/keystore" 34 "github.com/ethereum/go-ethereum/cmd/utils" 35 "github.com/ethereum/go-ethereum/common" 36 "github.com/ethereum/go-ethereum/console" 37 "github.com/ethereum/go-ethereum/crypto" 38 "github.com/ethereum/go-ethereum/internal/debug" 39 "github.com/ethereum/go-ethereum/log" 40 "github.com/ethereum/go-ethereum/node" 41 "github.com/ethereum/go-ethereum/p2p/enode" 42 "github.com/ethereum/go-ethereum/swarm" 43 bzzapi "github.com/ethereum/go-ethereum/swarm/api" 44 swarmmetrics "github.com/ethereum/go-ethereum/swarm/metrics" 45 "github.com/ethereum/go-ethereum/swarm/tracing" 46 sv "github.com/ethereum/go-ethereum/swarm/version" 47 48 "gopkg.in/urfave/cli.v1" 49 ) 50 51 const clientIdentifier = "swarm" 52 const helpTemplate = `NAME: 53 {{.HelpName}} - {{.Usage}} 54 55 USAGE: 56 {{if .UsageText}}{{.UsageText}}{{else}}{{.HelpName}}{{if .VisibleFlags}} [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}}{{if .Category}} 57 58 CATEGORY: 59 {{.Category}}{{end}}{{if .Description}} 60 61 DESCRIPTION: 62 {{.Description}}{{end}}{{if .VisibleFlags}} 63 64 OPTIONS: 65 {{range .VisibleFlags}}{{.}} 66 {{end}}{{end}} 67 ` 68 69 // Git SHA1 commit hash of the release (set via linker flags) 70 // this variable will be assigned if corresponding parameter is passed with install, but not with test 71 // e.g.: go install -ldflags "-X main.gitCommit=ed1312d01b19e04ef578946226e5d8069d5dfd5a" ./cmd/swarm 72 var gitCommit string 73 74 //declare a few constant error messages, useful for later error check comparisons in test 75 var ( 76 SWARM_ERR_NO_BZZACCOUNT = "bzzaccount option is required but not set; check your config file, command line or environment variables" 77 SWARM_ERR_SWAP_SET_NO_API = "SWAP is enabled but --swap-api is not set" 78 ) 79 80 // this help command gets added to any subcommand that does not define it explicitly 81 var defaultSubcommandHelp = cli.Command{ 82 Action: func(ctx *cli.Context) { cli.ShowCommandHelpAndExit(ctx, "", 1) }, 83 CustomHelpTemplate: helpTemplate, 84 Name: "help", 85 Usage: "shows this help", 86 Hidden: true, 87 } 88 89 var defaultNodeConfig = node.DefaultConfig 90 91 // This init function sets defaults so cmd/swarm can run alongside geth. 92 func init() { 93 sv.GitCommit = gitCommit 94 defaultNodeConfig.Name = clientIdentifier 95 defaultNodeConfig.Version = sv.VersionWithCommit(gitCommit) 96 defaultNodeConfig.P2P.ListenAddr = ":30399" 97 defaultNodeConfig.IPCPath = "bzzd.ipc" 98 // Set flag defaults for --help display. 99 utils.ListenPortFlag.Value = 30399 100 } 101 102 var app = utils.NewApp("", "Ethereum Swarm") 103 104 // This init function creates the cli.App. 105 func init() { 106 app.Action = bzzd 107 app.Version = sv.ArchiveVersion(gitCommit) 108 app.Copyright = "Copyright 2013-2016 The go-ethereum Authors" 109 app.Commands = []cli.Command{ 110 { 111 Action: version, 112 CustomHelpTemplate: helpTemplate, 113 Name: "version", 114 Usage: "Print version numbers", 115 Description: "The output of this command is supposed to be machine-readable", 116 }, 117 { 118 Action: keys, 119 CustomHelpTemplate: helpTemplate, 120 Name: "print-keys", 121 Flags: []cli.Flag{SwarmCompressedFlag}, 122 Usage: "Print public key information", 123 Description: "The output of this command is supposed to be machine-readable", 124 }, 125 // See upload.go 126 upCommand, 127 // See access.go 128 accessCommand, 129 // See feeds.go 130 feedCommand, 131 // See list.go 132 listCommand, 133 // See hash.go 134 hashCommand, 135 // See download.go 136 downloadCommand, 137 // See manifest.go 138 manifestCommand, 139 // See fs.go 140 fsCommand, 141 // See db.go 142 dbCommand, 143 // See config.go 144 DumpConfigCommand, 145 } 146 147 // append a hidden help subcommand to all commands that have subcommands 148 // if a help command was already defined above, that one will take precedence. 149 addDefaultHelpSubcommands(app.Commands) 150 151 sort.Sort(cli.CommandsByName(app.Commands)) 152 153 app.Flags = []cli.Flag{ 154 utils.IdentityFlag, 155 utils.DataDirFlag, 156 utils.BootnodesFlag, 157 utils.KeyStoreDirFlag, 158 utils.ListenPortFlag, 159 utils.DiscoveryV5Flag, 160 utils.NetrestrictFlag, 161 utils.NodeKeyFileFlag, 162 utils.NodeKeyHexFlag, 163 utils.MaxPeersFlag, 164 utils.NATFlag, 165 utils.IPCDisabledFlag, 166 utils.IPCPathFlag, 167 utils.PasswordFileFlag, 168 // bzzd-specific flags 169 CorsStringFlag, 170 EnsAPIFlag, 171 SwarmTomlConfigPathFlag, 172 SwarmSwapEnabledFlag, 173 SwarmSwapAPIFlag, 174 SwarmSyncDisabledFlag, 175 SwarmSyncUpdateDelay, 176 SwarmMaxStreamPeerServersFlag, 177 SwarmLightNodeEnabled, 178 SwarmDeliverySkipCheckFlag, 179 SwarmListenAddrFlag, 180 SwarmPortFlag, 181 SwarmAccountFlag, 182 SwarmNetworkIdFlag, 183 ChequebookAddrFlag, 184 // upload flags 185 SwarmApiFlag, 186 SwarmRecursiveFlag, 187 SwarmWantManifestFlag, 188 SwarmUploadDefaultPath, 189 SwarmUpFromStdinFlag, 190 SwarmUploadMimeType, 191 // bootnode mode 192 SwarmBootnodeModeFlag, 193 // storage flags 194 SwarmStorePath, 195 SwarmStoreCapacity, 196 SwarmStoreCacheCapacity, 197 } 198 rpcFlags := []cli.Flag{ 199 utils.WSEnabledFlag, 200 utils.WSListenAddrFlag, 201 utils.WSPortFlag, 202 utils.WSApiFlag, 203 utils.WSAllowedOriginsFlag, 204 } 205 app.Flags = append(app.Flags, rpcFlags...) 206 app.Flags = append(app.Flags, debug.Flags...) 207 app.Flags = append(app.Flags, swarmmetrics.Flags...) 208 app.Flags = append(app.Flags, tracing.Flags...) 209 app.Before = func(ctx *cli.Context) error { 210 runtime.GOMAXPROCS(runtime.NumCPU()) 211 if err := debug.Setup(ctx, ""); err != nil { 212 return err 213 } 214 swarmmetrics.Setup(ctx) 215 tracing.Setup(ctx) 216 return nil 217 } 218 app.After = func(ctx *cli.Context) error { 219 debug.Exit() 220 return nil 221 } 222 } 223 224 func main() { 225 if err := app.Run(os.Args); err != nil { 226 fmt.Fprintln(os.Stderr, err) 227 os.Exit(1) 228 } 229 } 230 231 func keys(ctx *cli.Context) error { 232 privateKey := getPrivKey(ctx) 233 pubkey := crypto.FromECDSAPub(&privateKey.PublicKey) 234 pubkeyhex := hex.EncodeToString(pubkey) 235 pubCompressed := hex.EncodeToString(crypto.CompressPubkey(&privateKey.PublicKey)) 236 bzzkey := crypto.Keccak256Hash(pubkey).Hex() 237 238 if !ctx.Bool(SwarmCompressedFlag.Name) { 239 fmt.Println(fmt.Sprintf("bzzkey=%s", bzzkey[2:])) 240 fmt.Println(fmt.Sprintf("publicKey=%s", pubkeyhex)) 241 } 242 fmt.Println(fmt.Sprintf("publicKeyCompressed=%s", pubCompressed)) 243 244 return nil 245 } 246 247 func version(ctx *cli.Context) error { 248 fmt.Println(strings.Title(clientIdentifier)) 249 fmt.Println("Version:", sv.VersionWithMeta) 250 if gitCommit != "" { 251 fmt.Println("Git Commit:", gitCommit) 252 } 253 fmt.Println("Go Version:", runtime.Version()) 254 fmt.Println("OS:", runtime.GOOS) 255 return nil 256 } 257 258 func bzzd(ctx *cli.Context) error { 259 //build a valid bzzapi.Config from all available sources: 260 //default config, file config, command line and env vars 261 262 bzzconfig, err := buildConfig(ctx) 263 if err != nil { 264 utils.Fatalf("unable to configure swarm: %v", err) 265 } 266 267 cfg := defaultNodeConfig 268 269 //pss operates on ws 270 cfg.WSModules = append(cfg.WSModules, "pss") 271 272 //geth only supports --datadir via command line 273 //in order to be consistent within swarm, if we pass --datadir via environment variable 274 //or via config file, we get the same directory for geth and swarm 275 if _, err := os.Stat(bzzconfig.Path); err == nil { 276 cfg.DataDir = bzzconfig.Path 277 } 278 279 //optionally set the bootnodes before configuring the node 280 setSwarmBootstrapNodes(ctx, &cfg) 281 //setup the ethereum node 282 utils.SetNodeConfig(ctx, &cfg) 283 284 //always disable discovery from p2p package - swarm discovery is done with the `hive` protocol 285 cfg.P2P.NoDiscovery = true 286 287 stack, err := node.New(&cfg) 288 if err != nil { 289 utils.Fatalf("can't create node: %v", err) 290 } 291 292 //a few steps need to be done after the config phase is completed, 293 //due to overriding behavior 294 initSwarmNode(bzzconfig, stack, ctx) 295 //register BZZ as node.Service in the ethereum node 296 registerBzzService(bzzconfig, stack) 297 //start the node 298 utils.StartNode(stack) 299 300 go func() { 301 sigc := make(chan os.Signal, 1) 302 signal.Notify(sigc, syscall.SIGTERM) 303 defer signal.Stop(sigc) 304 <-sigc 305 log.Info("Got sigterm, shutting swarm down...") 306 stack.Stop() 307 }() 308 309 // add swarm bootnodes, because swarm doesn't use p2p package's discovery discv5 310 go func() { 311 s := stack.Server() 312 313 for _, n := range cfg.P2P.BootstrapNodes { 314 s.AddPeer(n) 315 } 316 }() 317 318 stack.Wait() 319 return nil 320 } 321 322 func registerBzzService(bzzconfig *bzzapi.Config, stack *node.Node) { 323 //define the swarm service boot function 324 boot := func(_ *node.ServiceContext) (node.Service, error) { 325 // In production, mockStore must be always nil. 326 return swarm.NewSwarm(bzzconfig, nil) 327 } 328 //register within the ethereum node 329 if err := stack.Register(boot); err != nil { 330 utils.Fatalf("Failed to register the Swarm service: %v", err) 331 } 332 } 333 334 func getAccount(bzzaccount string, ctx *cli.Context, stack *node.Node) *ecdsa.PrivateKey { 335 //an account is mandatory 336 if bzzaccount == "" { 337 utils.Fatalf(SWARM_ERR_NO_BZZACCOUNT) 338 } 339 // Try to load the arg as a hex key file. 340 if key, err := crypto.LoadECDSA(bzzaccount); err == nil { 341 log.Info("Swarm account key loaded", "address", crypto.PubkeyToAddress(key.PublicKey)) 342 return key 343 } 344 // Otherwise try getting it from the keystore. 345 am := stack.AccountManager() 346 ks := am.Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore) 347 348 return decryptStoreAccount(ks, bzzaccount, utils.MakePasswordList(ctx)) 349 } 350 351 // getPrivKey returns the private key of the specified bzzaccount 352 // Used only by client commands, such as `feed` 353 func getPrivKey(ctx *cli.Context) *ecdsa.PrivateKey { 354 // booting up the swarm node just as we do in bzzd action 355 bzzconfig, err := buildConfig(ctx) 356 if err != nil { 357 utils.Fatalf("unable to configure swarm: %v", err) 358 } 359 cfg := defaultNodeConfig 360 if _, err := os.Stat(bzzconfig.Path); err == nil { 361 cfg.DataDir = bzzconfig.Path 362 } 363 utils.SetNodeConfig(ctx, &cfg) 364 stack, err := node.New(&cfg) 365 if err != nil { 366 utils.Fatalf("can't create node: %v", err) 367 } 368 return getAccount(bzzconfig.BzzAccount, ctx, stack) 369 } 370 371 func decryptStoreAccount(ks *keystore.KeyStore, account string, passwords []string) *ecdsa.PrivateKey { 372 var a accounts.Account 373 var err error 374 if common.IsHexAddress(account) { 375 a, err = ks.Find(accounts.Account{Address: common.HexToAddress(account)}) 376 } else if ix, ixerr := strconv.Atoi(account); ixerr == nil && ix > 0 { 377 if accounts := ks.Accounts(); len(accounts) > ix { 378 a = accounts[ix] 379 } else { 380 err = fmt.Errorf("index %d higher than number of accounts %d", ix, len(accounts)) 381 } 382 } else { 383 utils.Fatalf("Can't find swarm account key %s", account) 384 } 385 if err != nil { 386 utils.Fatalf("Can't find swarm account key: %v - Is the provided bzzaccount(%s) from the right datadir/Path?", err, account) 387 } 388 keyjson, err := ioutil.ReadFile(a.URL.Path) 389 if err != nil { 390 utils.Fatalf("Can't load swarm account key: %v", err) 391 } 392 for i := 0; i < 3; i++ { 393 password := getPassPhrase(fmt.Sprintf("Unlocking swarm account %s [%d/3]", a.Address.Hex(), i+1), i, passwords) 394 key, err := keystore.DecryptKey(keyjson, password) 395 if err == nil { 396 return key.PrivateKey 397 } 398 } 399 utils.Fatalf("Can't decrypt swarm account key") 400 return nil 401 } 402 403 // getPassPhrase retrieves the password associated with bzz account, either by fetching 404 // from a list of pre-loaded passwords, or by requesting it interactively from user. 405 func getPassPhrase(prompt string, i int, passwords []string) string { 406 // non-interactive 407 if len(passwords) > 0 { 408 if i < len(passwords) { 409 return passwords[i] 410 } 411 return passwords[len(passwords)-1] 412 } 413 414 // fallback to interactive mode 415 if prompt != "" { 416 fmt.Println(prompt) 417 } 418 password, err := console.Stdin.PromptPassword("Passphrase: ") 419 if err != nil { 420 utils.Fatalf("Failed to read passphrase: %v", err) 421 } 422 return password 423 } 424 425 // addDefaultHelpSubcommand scans through defined CLI commands and adds 426 // a basic help subcommand to each 427 // if a help command is already defined, it will take precedence over the default. 428 func addDefaultHelpSubcommands(commands []cli.Command) { 429 for i := range commands { 430 cmd := &commands[i] 431 if cmd.Subcommands != nil { 432 cmd.Subcommands = append(cmd.Subcommands, defaultSubcommandHelp) 433 addDefaultHelpSubcommands(cmd.Subcommands) 434 } 435 } 436 } 437 438 func setSwarmBootstrapNodes(ctx *cli.Context, cfg *node.Config) { 439 if ctx.GlobalIsSet(utils.BootnodesFlag.Name) || ctx.GlobalIsSet(utils.BootnodesV4Flag.Name) { 440 return 441 } 442 443 cfg.P2P.BootstrapNodes = []*enode.Node{} 444 445 for _, url := range SwarmBootnodes { 446 node, err := enode.ParseV4(url) 447 if err != nil { 448 log.Error("Bootstrap URL invalid", "enode", url, "err", err) 449 } 450 cfg.P2P.BootstrapNodes = append(cfg.P2P.BootstrapNodes, node) 451 } 452 log.Debug("added default swarm bootnodes", "length", len(cfg.P2P.BootstrapNodes)) 453 }