github.com/nitinawathare/ethereumassignment3@v0.0.0-20211021213010-f07344c2b868/go-ethereum/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  	err = initSwarmNode(bzzconfig, stack, ctx, &cfg)
   302  	if err != nil {
   303  		return err
   304  	}
   305  	//register BZZ as node.Service in the ethereum node
   306  	registerBzzService(bzzconfig, stack)
   307  	//start the node
   308  	utils.StartNode(stack)
   309  
   310  	go func() {
   311  		sigc := make(chan os.Signal, 1)
   312  		signal.Notify(sigc, syscall.SIGTERM)
   313  		defer signal.Stop(sigc)
   314  		<-sigc
   315  		log.Info("Got sigterm, shutting swarm down...")
   316  		stack.Stop()
   317  	}()
   318  
   319  	// add swarm bootnodes, because swarm doesn't use p2p package's discovery discv5
   320  	go func() {
   321  		s := stack.Server()
   322  
   323  		for _, n := range cfg.P2P.BootstrapNodes {
   324  			s.AddPeer(n)
   325  		}
   326  	}()
   327  
   328  	stack.Wait()
   329  	return nil
   330  }
   331  
   332  func registerBzzService(bzzconfig *bzzapi.Config, stack *node.Node) {
   333  	//define the swarm service boot function
   334  	boot := func(_ *node.ServiceContext) (node.Service, error) {
   335  		var nodeStore *mock.NodeStore
   336  		if bzzconfig.GlobalStoreAPI != "" {
   337  			// connect to global store
   338  			client, err := rpc.Dial(bzzconfig.GlobalStoreAPI)
   339  			if err != nil {
   340  				return nil, fmt.Errorf("global store: %v", err)
   341  			}
   342  			globalStore := mockrpc.NewGlobalStore(client)
   343  			// create a node store for this swarm key on global store
   344  			nodeStore = globalStore.NewNodeStore(common.HexToAddress(bzzconfig.BzzKey))
   345  		}
   346  		return swarm.NewSwarm(bzzconfig, nodeStore)
   347  	}
   348  	//register within the ethereum node
   349  	if err := stack.Register(boot); err != nil {
   350  		utils.Fatalf("Failed to register the Swarm service: %v", err)
   351  	}
   352  }
   353  
   354  func getAccount(bzzaccount string, ctx *cli.Context, stack *node.Node) *ecdsa.PrivateKey {
   355  	//an account is mandatory
   356  	if bzzaccount == "" {
   357  		utils.Fatalf(SwarmErrNoBZZAccount)
   358  	}
   359  	// Try to load the arg as a hex key file.
   360  	if key, err := crypto.LoadECDSA(bzzaccount); err == nil {
   361  		log.Info("Swarm account key loaded", "address", crypto.PubkeyToAddress(key.PublicKey))
   362  		return key
   363  	}
   364  	// Otherwise try getting it from the keystore.
   365  	am := stack.AccountManager()
   366  	ks := am.Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore)
   367  
   368  	return decryptStoreAccount(ks, bzzaccount, utils.MakePasswordList(ctx))
   369  }
   370  
   371  // getPrivKey returns the private key of the specified bzzaccount
   372  // Used only by client commands, such as `feed`
   373  func getPrivKey(ctx *cli.Context) *ecdsa.PrivateKey {
   374  	// booting up the swarm node just as we do in bzzd action
   375  	bzzconfig, err := buildConfig(ctx)
   376  	if err != nil {
   377  		utils.Fatalf("unable to configure swarm: %v", err)
   378  	}
   379  	cfg := defaultNodeConfig
   380  	if _, err := os.Stat(bzzconfig.Path); err == nil {
   381  		cfg.DataDir = bzzconfig.Path
   382  	}
   383  	utils.SetNodeConfig(ctx, &cfg)
   384  	stack, err := node.New(&cfg)
   385  	if err != nil {
   386  		utils.Fatalf("can't create node: %v", err)
   387  	}
   388  	defer stack.Close()
   389  
   390  	return getAccount(bzzconfig.BzzAccount, ctx, stack)
   391  }
   392  
   393  func decryptStoreAccount(ks *keystore.KeyStore, account string, passwords []string) *ecdsa.PrivateKey {
   394  	var a accounts.Account
   395  	var err error
   396  	if common.IsHexAddress(account) {
   397  		a, err = ks.Find(accounts.Account{Address: common.HexToAddress(account)})
   398  	} else if ix, ixerr := strconv.Atoi(account); ixerr == nil && ix > 0 {
   399  		if accounts := ks.Accounts(); len(accounts) > ix {
   400  			a = accounts[ix]
   401  		} else {
   402  			err = fmt.Errorf("index %d higher than number of accounts %d", ix, len(accounts))
   403  		}
   404  	} else {
   405  		utils.Fatalf("Can't find swarm account key %s", account)
   406  	}
   407  	if err != nil {
   408  		utils.Fatalf("Can't find swarm account key: %v - Is the provided bzzaccount(%s) from the right datadir/Path?", err, account)
   409  	}
   410  	keyjson, err := ioutil.ReadFile(a.URL.Path)
   411  	if err != nil {
   412  		utils.Fatalf("Can't load swarm account key: %v", err)
   413  	}
   414  	for i := 0; i < 3; i++ {
   415  		password := getPassPhrase(fmt.Sprintf("Unlocking swarm account %s [%d/3]", a.Address.Hex(), i+1), i, passwords)
   416  		key, err := keystore.DecryptKey(keyjson, password)
   417  		if err == nil {
   418  			return key.PrivateKey
   419  		}
   420  	}
   421  	utils.Fatalf("Can't decrypt swarm account key")
   422  	return nil
   423  }
   424  
   425  // getPassPhrase retrieves the password associated with bzz account, either by fetching
   426  // from a list of pre-loaded passwords, or by requesting it interactively from user.
   427  func getPassPhrase(prompt string, i int, passwords []string) string {
   428  	// non-interactive
   429  	if len(passwords) > 0 {
   430  		if i < len(passwords) {
   431  			return passwords[i]
   432  		}
   433  		return passwords[len(passwords)-1]
   434  	}
   435  
   436  	// fallback to interactive mode
   437  	if prompt != "" {
   438  		fmt.Println(prompt)
   439  	}
   440  	password, err := console.Stdin.PromptPassword("Passphrase: ")
   441  	if err != nil {
   442  		utils.Fatalf("Failed to read passphrase: %v", err)
   443  	}
   444  	return password
   445  }
   446  
   447  // addDefaultHelpSubcommand scans through defined CLI commands and adds
   448  // a basic help subcommand to each
   449  // if a help command is already defined, it will take precedence over the default.
   450  func addDefaultHelpSubcommands(commands []cli.Command) {
   451  	for i := range commands {
   452  		cmd := &commands[i]
   453  		if cmd.Subcommands != nil {
   454  			cmd.Subcommands = append(cmd.Subcommands, defaultSubcommandHelp)
   455  			addDefaultHelpSubcommands(cmd.Subcommands)
   456  		}
   457  	}
   458  }
   459  
   460  func setSwarmBootstrapNodes(ctx *cli.Context, cfg *node.Config) {
   461  	if ctx.GlobalIsSet(utils.BootnodesFlag.Name) || ctx.GlobalIsSet(utils.BootnodesV4Flag.Name) {
   462  		return
   463  	}
   464  
   465  	cfg.P2P.BootstrapNodes = []*enode.Node{}
   466  
   467  	for _, url := range SwarmBootnodes {
   468  		node, err := enode.ParseV4(url)
   469  		if err != nil {
   470  			log.Error("Bootstrap URL invalid", "enode", url, "err", err)
   471  		}
   472  		cfg.P2P.BootstrapNodes = append(cfg.P2P.BootstrapNodes, node)
   473  	}
   474  
   475  }