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