github.com/alexdevranger/node-1.8.27@v0.0.0-20221128213301-aa5841e41d2d/cmd/swarm/main.go (about)

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