github.com/jincm/wesharechain@v0.0.0-20210122032815-1537409ce26a/chain/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 SwarmErrNoBZZAccount = "bzzaccount option is required but not set; check your config file, command line or environment variables" 80 SwarmErrSwapSetNoAPI = "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 //geth 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 geth 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 //disable dynamic dialing from p2p/discovery 291 cfg.P2P.NoDial = true 292 293 stack, err := node.New(&cfg) 294 if err != nil { 295 utils.Fatalf("can't create node: %v", err) 296 } 297 defer stack.Close() 298 299 //a few steps need to be done after the config phase is completed, 300 //due to overriding behavior 301 initSwarmNode(bzzconfig, stack, ctx) 302 //register BZZ as node.Service in the ethereum node 303 registerBzzService(bzzconfig, stack) 304 //start the node 305 utils.StartNode(stack) 306 307 go func() { 308 sigc := make(chan os.Signal, 1) 309 signal.Notify(sigc, syscall.SIGTERM) 310 defer signal.Stop(sigc) 311 <-sigc 312 log.Info("Got sigterm, shutting swarm down...") 313 stack.Stop() 314 }() 315 316 // add swarm bootnodes, because swarm doesn't use p2p package's discovery discv5 317 go func() { 318 s := stack.Server() 319 320 for _, n := range cfg.P2P.BootstrapNodes { 321 s.AddPeer(n) 322 } 323 }() 324 325 stack.Wait() 326 return nil 327 } 328 329 func registerBzzService(bzzconfig *bzzapi.Config, stack *node.Node) { 330 //define the swarm service boot function 331 boot := func(_ *node.ServiceContext) (node.Service, error) { 332 var nodeStore *mock.NodeStore 333 if bzzconfig.GlobalStoreAPI != "" { 334 // connect to global store 335 client, err := rpc.Dial(bzzconfig.GlobalStoreAPI) 336 if err != nil { 337 return nil, fmt.Errorf("global store: %v", err) 338 } 339 globalStore := mockrpc.NewGlobalStore(client) 340 // create a node store for this swarm key on global store 341 nodeStore = globalStore.NewNodeStore(common.HexToAddress(bzzconfig.BzzKey)) 342 } 343 return swarm.NewSwarm(bzzconfig, nodeStore) 344 } 345 //register within the ethereum node 346 if err := stack.Register(boot); err != nil { 347 utils.Fatalf("Failed to register the Swarm service: %v", err) 348 } 349 } 350 351 func getAccount(bzzaccount string, ctx *cli.Context, stack *node.Node) *ecdsa.PrivateKey { 352 //an account is mandatory 353 if bzzaccount == "" { 354 utils.Fatalf(SwarmErrNoBZZAccount) 355 } 356 // Try to load the arg as a hex key file. 357 if key, err := crypto.LoadECDSA(bzzaccount); err == nil { 358 log.Info("Swarm account key loaded", "address", crypto.PubkeyToAddress(key.PublicKey)) 359 return key 360 } 361 // Otherwise try getting it from the keystore. 362 am := stack.AccountManager() 363 ks := am.Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore) 364 365 return decryptStoreAccount(ks, bzzaccount, utils.MakePasswordList(ctx)) 366 } 367 368 // getPrivKey returns the private key of the specified bzzaccount 369 // Used only by client commands, such as `feed` 370 func getPrivKey(ctx *cli.Context) *ecdsa.PrivateKey { 371 // booting up the swarm node just as we do in bzzd action 372 bzzconfig, err := buildConfig(ctx) 373 if err != nil { 374 utils.Fatalf("unable to configure swarm: %v", err) 375 } 376 cfg := defaultNodeConfig 377 if _, err := os.Stat(bzzconfig.Path); err == nil { 378 cfg.DataDir = bzzconfig.Path 379 } 380 utils.SetNodeConfig(ctx, &cfg) 381 stack, err := node.New(&cfg) 382 if err != nil { 383 utils.Fatalf("can't create node: %v", err) 384 } 385 defer stack.Close() 386 387 return getAccount(bzzconfig.BzzAccount, ctx, stack) 388 } 389 390 func decryptStoreAccount(ks *keystore.KeyStore, account string, passwords []string) *ecdsa.PrivateKey { 391 var a accounts.Account 392 var err error 393 if common.IsHexAddress(account) { 394 a, err = ks.Find(accounts.Account{Address: common.HexToAddress(account)}) 395 } else if ix, ixerr := strconv.Atoi(account); ixerr == nil && ix > 0 { 396 if accounts := ks.Accounts(); len(accounts) > ix { 397 a = accounts[ix] 398 } else { 399 err = fmt.Errorf("index %d higher than number of accounts %d", ix, len(accounts)) 400 } 401 } else { 402 utils.Fatalf("Can't find swarm account key %s", account) 403 } 404 if err != nil { 405 utils.Fatalf("Can't find swarm account key: %v - Is the provided bzzaccount(%s) from the right datadir/Path?", err, account) 406 } 407 keyjson, err := ioutil.ReadFile(a.URL.Path) 408 if err != nil { 409 utils.Fatalf("Can't load swarm account key: %v", err) 410 } 411 for i := 0; i < 3; i++ { 412 password := getPassPhrase(fmt.Sprintf("Unlocking swarm account %s [%d/3]", a.Address.Hex(), i+1), i, passwords) 413 key, err := keystore.DecryptKey(keyjson, password) 414 if err == nil { 415 return key.PrivateKey 416 } 417 } 418 utils.Fatalf("Can't decrypt swarm account key") 419 return nil 420 } 421 422 // getPassPhrase retrieves the password associated with bzz account, either by fetching 423 // from a list of pre-loaded passwords, or by requesting it interactively from user. 424 func getPassPhrase(prompt string, i int, passwords []string) string { 425 // non-interactive 426 if len(passwords) > 0 { 427 if i < len(passwords) { 428 return passwords[i] 429 } 430 return passwords[len(passwords)-1] 431 } 432 433 // fallback to interactive mode 434 if prompt != "" { 435 fmt.Println(prompt) 436 } 437 password, err := console.Stdin.PromptPassword("Passphrase: ") 438 if err != nil { 439 utils.Fatalf("Failed to read passphrase: %v", err) 440 } 441 return password 442 } 443 444 // addDefaultHelpSubcommand scans through defined CLI commands and adds 445 // a basic help subcommand to each 446 // if a help command is already defined, it will take precedence over the default. 447 func addDefaultHelpSubcommands(commands []cli.Command) { 448 for i := range commands { 449 cmd := &commands[i] 450 if cmd.Subcommands != nil { 451 cmd.Subcommands = append(cmd.Subcommands, defaultSubcommandHelp) 452 addDefaultHelpSubcommands(cmd.Subcommands) 453 } 454 } 455 } 456 457 func setSwarmBootstrapNodes(ctx *cli.Context, cfg *node.Config) { 458 if ctx.GlobalIsSet(utils.BootnodesFlag.Name) || ctx.GlobalIsSet(utils.BootnodesV4Flag.Name) { 459 return 460 } 461 462 cfg.P2P.BootstrapNodes = []*enode.Node{} 463 464 for _, url := range SwarmBootnodes { 465 node, err := enode.ParseV4(url) 466 if err != nil { 467 log.Error("Bootstrap URL invalid", "enode", url, "err", err) 468 } 469 cfg.P2P.BootstrapNodes = append(cfg.P2P.BootstrapNodes, node) 470 } 471 472 }