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