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