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  }