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