github.com/oskarth/go-ethereum@v1.6.8-0.20191013093314-dac24a9d3494/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  }