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  }