github.com/n1ghtfa1l/go-vnt@v0.6.4-alpha.6/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  	"context"
    21  	"crypto/ecdsa"
    22  	"fmt"
    23  	"io/ioutil"
    24  	"os"
    25  	"os/signal"
    26  	"runtime"
    27  	"sort"
    28  	"strconv"
    29  	"strings"
    30  	"syscall"
    31  
    32  	"github.com/vntchain/go-vnt/accounts"
    33  	"github.com/vntchain/go-vnt/accounts/keystore"
    34  	"github.com/vntchain/go-vnt/cmd/utils"
    35  	"github.com/vntchain/go-vnt/common"
    36  	"github.com/vntchain/go-vnt/console"
    37  	"github.com/vntchain/go-vnt/crypto"
    38  	"github.com/vntchain/go-vnt/internal/debug"
    39  	"github.com/vntchain/go-vnt/log"
    40  	"github.com/vntchain/go-vnt/node"
    41  	"github.com/vntchain/go-vnt/params"
    42  	"github.com/vntchain/go-vnt/swarm"
    43  	bzzapi "github.com/vntchain/go-vnt/swarm/api"
    44  	swarmmetrics "github.com/vntchain/go-vnt/swarm/metrics"
    45  	"github.com/vntchain/go-vnt/vntclient"
    46  	"github.com/vntchain/go-vnt/vntp2p"
    47  	cli "gopkg.in/urfave/cli.v1"
    48  )
    49  
    50  const clientIdentifier = "swarm"
    51  
    52  var (
    53  	gitCommit        string // Git SHA1 commit hash of the release (set via linker flags)
    54  	testbetBootNodes = []string{
    55  		"vnode://ec8ae764f7cb0417bdfb009b9d0f18ab3818a3a4e8e7c67dd5f18971a93510a2e6f43cd0b69a27e439a9629457ea804104f37c85e41eed057d3faabbf7744cdf@13.74.157.139:30429",
    56  		"vnode://c2e1fceb3bf3be19dff71eec6cccf19f2dbf7567ee017d130240c670be8594bc9163353ca55dd8df7a4f161dd94b36d0615c17418b5a3cdcbb4e9d99dfa4de37@13.74.157.139:30430",
    57  		"vnode://fe29b82319b734ce1ec68b84657d57145fee237387e63273989d354486731e59f78858e452ef800a020559da22dcca759536e6aa5517c53930d29ce0b1029286@13.74.157.139:30431",
    58  		"vnode://1d7187e7bde45cf0bee489ce9852dd6d1a0d9aa67a33a6b8e6db8a4fbc6fcfa6f0f1a5419343671521b863b187d1c73bad3603bae66421d157ffef357669ddb8@13.74.157.139:30432",
    59  		"vnode://0e4cba800f7b1ee73673afa6a4acead4018f0149d2e3216be3f133318fd165b324cd71b81fbe1e80deac8dbf56e57a49db7be67f8b9bc81bd2b7ee496434fb5d@13.74.157.139:30433",
    60  	}
    61  )
    62  
    63  var (
    64  	ChequebookAddrFlag = cli.StringFlag{
    65  		Name:   "chequebook",
    66  		Usage:  "chequebook contract address",
    67  		EnvVar: SWARM_ENV_CHEQUEBOOK_ADDR,
    68  	}
    69  	SwarmAccountFlag = cli.StringFlag{
    70  		Name:   "bzzaccount",
    71  		Usage:  "Swarm account key file",
    72  		EnvVar: SWARM_ENV_ACCOUNT,
    73  	}
    74  	SwarmListenAddrFlag = cli.StringFlag{
    75  		Name:   "httpaddr",
    76  		Usage:  "Swarm HTTP API listening interface",
    77  		EnvVar: SWARM_ENV_LISTEN_ADDR,
    78  	}
    79  	SwarmPortFlag = cli.StringFlag{
    80  		Name:   "bzzport",
    81  		Usage:  "Swarm local http api port",
    82  		EnvVar: SWARM_ENV_PORT,
    83  	}
    84  	SwarmNetworkIdFlag = cli.IntFlag{
    85  		Name:   "bzznetworkid",
    86  		Usage:  "Network identifier (integer, default 3=swarm testnet)",
    87  		EnvVar: SWARM_ENV_NETWORK_ID,
    88  	}
    89  	SwarmConfigPathFlag = cli.StringFlag{
    90  		Name:  "bzzconfig",
    91  		Usage: "DEPRECATED: please use --config path/to/TOML-file",
    92  	}
    93  	SwarmSwapEnabledFlag = cli.BoolFlag{
    94  		Name:   "swap",
    95  		Usage:  "Swarm SWAP enabled (default false)",
    96  		EnvVar: SWARM_ENV_SWAP_ENABLE,
    97  	}
    98  	SwarmSwapAPIFlag = cli.StringFlag{
    99  		Name:   "swap-api",
   100  		Usage:  "URL of the VNT API provider to use to settle SWAP payments",
   101  		EnvVar: SWARM_ENV_SWAP_API,
   102  	}
   103  	SwarmSyncEnabledFlag = cli.BoolTFlag{
   104  		Name:   "sync",
   105  		Usage:  "Swarm Syncing enabled (default true)",
   106  		EnvVar: SWARM_ENV_SYNC_ENABLE,
   107  	}
   108  	VnsAPIFlag = cli.StringSliceFlag{
   109  		Name:   "vns-api",
   110  		Usage:  "VNS API endpoint for a TLD and with contract address, can be repeated, format [tld:][contract-addr@]url",
   111  		EnvVar: SWARM_ENV_VNS_API,
   112  	}
   113  	SwarmApiFlag = cli.StringFlag{
   114  		Name:  "bzzapi",
   115  		Usage: "Swarm HTTP endpoint",
   116  		Value: "http://127.0.0.1:8500",
   117  	}
   118  	SwarmRecursiveUploadFlag = cli.BoolFlag{
   119  		Name:  "recursive",
   120  		Usage: "Upload directories recursively",
   121  	}
   122  	SwarmWantManifestFlag = cli.BoolTFlag{
   123  		Name:  "manifest",
   124  		Usage: "Automatic manifest upload",
   125  	}
   126  	SwarmUploadDefaultPath = cli.StringFlag{
   127  		Name:  "defaultpath",
   128  		Usage: "path to file served for empty url path (none)",
   129  	}
   130  	SwarmUpFromStdinFlag = cli.BoolFlag{
   131  		Name:  "stdin",
   132  		Usage: "reads data to be uploaded from stdin",
   133  	}
   134  	SwarmUploadMimeType = cli.StringFlag{
   135  		Name:  "mime",
   136  		Usage: "force mime type",
   137  	}
   138  	CorsStringFlag = cli.StringFlag{
   139  		Name:   "corsdomain",
   140  		Usage:  "Domain on which to send Access-Control-Allow-Origin header (multiple domains can be supplied separated by a ',')",
   141  		EnvVar: SWARM_ENV_CORS,
   142  	}
   143  
   144  	// the following flags are deprecated and should be removed in the future
   145  	DeprecatedVntAPIFlag = cli.StringFlag{
   146  		Name:  "vntapi",
   147  		Usage: "DEPRECATED: please use --vns-api and --swap-api",
   148  	}
   149  	DeprecatedVnsAddrFlag = cli.StringFlag{
   150  		Name:  "vns-addr",
   151  		Usage: "DEPRECATED: VNS contract address, please use --vns-api with contract address according to its format",
   152  	}
   153  )
   154  
   155  //declare a few constant error messages, useful for later error check comparisons in test
   156  var (
   157  	SWARM_ERR_NO_BZZACCOUNT   = "bzzaccount option is required but not set; check your config file, command line or environment variables"
   158  	SWARM_ERR_SWAP_SET_NO_API = "SWAP is enabled but --swap-api is not set"
   159  )
   160  
   161  var defaultNodeConfig = node.DefaultConfig
   162  
   163  // This init function sets defaults so cmd/swarm can run alongside gvnt.
   164  func init() {
   165  	defaultNodeConfig.Name = clientIdentifier
   166  	defaultNodeConfig.Version = params.VersionWithCommit(gitCommit)
   167  	defaultNodeConfig.P2P.ListenAddr = ":30399"
   168  	defaultNodeConfig.IPCPath = "bzzd.ipc"
   169  	// Set flag defaults for --help display.
   170  	utils.ListenPortFlag.Value = 30399
   171  }
   172  
   173  var app = utils.NewApp(gitCommit, "VNT Swarm")
   174  
   175  // This init function creates the cli.App.
   176  func init() {
   177  	app.Action = bzzd
   178  	app.HideVersion = true // we have a command to print the version
   179  	app.Copyright = "Copyright 2018-2019 The Vntchain Authors"
   180  	app.Commands = []cli.Command{
   181  		{
   182  			Action:    version,
   183  			Name:      "version",
   184  			Usage:     "Print version numbers",
   185  			ArgsUsage: " ",
   186  			Description: `
   187  The output of this command is supposed to be machine-readable.
   188  `,
   189  		},
   190  		{
   191  			Action:    upload,
   192  			Name:      "up",
   193  			Usage:     "upload a file or directory to swarm using the HTTP API",
   194  			ArgsUsage: " <file>",
   195  			Description: `
   196  "upload a file or directory to swarm using the HTTP API and prints the root hash",
   197  `,
   198  		},
   199  		{
   200  			Action:    list,
   201  			Name:      "ls",
   202  			Usage:     "list files and directories contained in a manifest",
   203  			ArgsUsage: " <manifest> [<prefix>]",
   204  			Description: `
   205  Lists files and directories contained in a manifest.
   206  `,
   207  		},
   208  		{
   209  			Action:    hash,
   210  			Name:      "hash",
   211  			Usage:     "print the swarm hash of a file or directory",
   212  			ArgsUsage: " <file>",
   213  			Description: `
   214  Prints the swarm hash of file or directory.
   215  `,
   216  		},
   217  		{
   218  			Name:      "manifest",
   219  			Usage:     "update a MANIFEST",
   220  			ArgsUsage: "manifest COMMAND",
   221  			Description: `
   222  Updates a MANIFEST by adding/removing/updating the hash of a path.
   223  `,
   224  			Subcommands: []cli.Command{
   225  				{
   226  					Action:    add,
   227  					Name:      "add",
   228  					Usage:     "add a new path to the manifest",
   229  					ArgsUsage: "<MANIFEST> <path> <hash> [<content-type>]",
   230  					Description: `
   231  Adds a new path to the manifest
   232  `,
   233  				},
   234  				{
   235  					Action:    update,
   236  					Name:      "update",
   237  					Usage:     "update the hash for an already existing path in the manifest",
   238  					ArgsUsage: "<MANIFEST> <path> <newhash> [<newcontent-type>]",
   239  					Description: `
   240  Update the hash for an already existing path in the manifest
   241  `,
   242  				},
   243  				{
   244  					Action:    remove,
   245  					Name:      "remove",
   246  					Usage:     "removes a path from the manifest",
   247  					ArgsUsage: "<MANIFEST> <path>",
   248  					Description: `
   249  Removes a path from the manifest
   250  `,
   251  				},
   252  			},
   253  		},
   254  		{
   255  			Name:      "db",
   256  			Usage:     "manage the local chunk database",
   257  			ArgsUsage: "db COMMAND",
   258  			Description: `
   259  Manage the local chunk database.
   260  `,
   261  			Subcommands: []cli.Command{
   262  				{
   263  					Action:    dbExport,
   264  					Name:      "export",
   265  					Usage:     "export a local chunk database as a tar archive (use - to send to stdout)",
   266  					ArgsUsage: "<chunkdb> <file>",
   267  					Description: `
   268  Export a local chunk database as a tar archive (use - to send to stdout).
   269  
   270      swarm db export ~/.vntchain/swarm/bzz-KEY/chunks chunks.tar
   271  
   272  The export may be quite large, consider piping the output through the Unix
   273  pv(1) tool to get a progress bar:
   274  
   275      swarm db export ~/.vntchain/swarm/bzz-KEY/chunks - | pv > chunks.tar
   276  `,
   277  				},
   278  				{
   279  					Action:    dbImport,
   280  					Name:      "import",
   281  					Usage:     "import chunks from a tar archive into a local chunk database (use - to read from stdin)",
   282  					ArgsUsage: "<chunkdb> <file>",
   283  					Description: `
   284  Import chunks from a tar archive into a local chunk database (use - to read from stdin).
   285  
   286      swarm db import ~/.vntchain/swarm/bzz-KEY/chunks chunks.tar
   287  
   288  The import may be quite large, consider piping the input through the Unix
   289  pv(1) tool to get a progress bar:
   290  
   291      pv chunks.tar | swarm db import ~/.vntchain/swarm/bzz-KEY/chunks -
   292  `,
   293  				},
   294  				{
   295  					Action:    dbClean,
   296  					Name:      "clean",
   297  					Usage:     "remove corrupt entries from a local chunk database",
   298  					ArgsUsage: "<chunkdb>",
   299  					Description: `
   300  Remove corrupt entries from a local chunk database.
   301  `,
   302  				},
   303  			},
   304  		},
   305  		{
   306  			Action: func(ctx *cli.Context) {
   307  				utils.Fatalf("ERROR: 'swarm cleandb' has been removed, please use 'swarm db clean'.")
   308  			},
   309  			Name:      "cleandb",
   310  			Usage:     "DEPRECATED: use 'swarm db clean'",
   311  			ArgsUsage: " ",
   312  			Description: `
   313  DEPRECATED: use 'swarm db clean'.
   314  `,
   315  		},
   316  		// See config.go
   317  		DumpConfigCommand,
   318  	}
   319  	sort.Sort(cli.CommandsByName(app.Commands))
   320  
   321  	app.Flags = []cli.Flag{
   322  		utils.IdentityFlag,
   323  		utils.DataDirFlag,
   324  		utils.BootnodesFlag,
   325  		utils.KeyStoreDirFlag,
   326  		utils.ListenPortFlag,
   327  		utils.NoDiscoverFlag,
   328  		utils.DiscoveryV5Flag,
   329  		utils.NetrestrictFlag,
   330  		utils.NodeKeyFileFlag,
   331  		utils.NodeKeyHexFlag,
   332  		utils.MaxPeersFlag,
   333  		utils.NATFlag,
   334  		utils.IPCDisabledFlag,
   335  		utils.IPCPathFlag,
   336  		utils.PasswordFileFlag,
   337  		// bzzd-specific flags
   338  		CorsStringFlag,
   339  		VnsAPIFlag,
   340  		SwarmTomlConfigPathFlag,
   341  		SwarmConfigPathFlag,
   342  		SwarmSwapEnabledFlag,
   343  		SwarmSwapAPIFlag,
   344  		SwarmSyncEnabledFlag,
   345  		SwarmListenAddrFlag,
   346  		SwarmPortFlag,
   347  		SwarmAccountFlag,
   348  		SwarmNetworkIdFlag,
   349  		ChequebookAddrFlag,
   350  		// upload flags
   351  		SwarmApiFlag,
   352  		SwarmRecursiveUploadFlag,
   353  		SwarmWantManifestFlag,
   354  		SwarmUploadDefaultPath,
   355  		SwarmUpFromStdinFlag,
   356  		SwarmUploadMimeType,
   357  		//deprecated flags
   358  		DeprecatedVntAPIFlag,
   359  		DeprecatedVnsAddrFlag,
   360  	}
   361  	app.Flags = append(app.Flags, debug.Flags...)
   362  	app.Flags = append(app.Flags, swarmmetrics.Flags...)
   363  	app.Before = func(ctx *cli.Context) error {
   364  		runtime.GOMAXPROCS(runtime.NumCPU())
   365  		if err := debug.Setup(ctx); err != nil {
   366  			return err
   367  		}
   368  		swarmmetrics.Setup(ctx)
   369  		return nil
   370  	}
   371  	app.After = func(ctx *cli.Context) error {
   372  		debug.Exit()
   373  		return nil
   374  	}
   375  }
   376  
   377  func main() {
   378  	if err := app.Run(os.Args); err != nil {
   379  		fmt.Fprintln(os.Stderr, err)
   380  		os.Exit(1)
   381  	}
   382  }
   383  
   384  func version(ctx *cli.Context) error {
   385  	fmt.Println(strings.Title(clientIdentifier))
   386  	fmt.Println("Version:", params.Version)
   387  	if gitCommit != "" {
   388  		fmt.Println("Git Commit:", gitCommit)
   389  	}
   390  	fmt.Println("Network Id:", ctx.GlobalInt(utils.NetworkIdFlag.Name))
   391  	fmt.Println("Go Version:", runtime.Version())
   392  	fmt.Println("OS:", runtime.GOOS)
   393  	fmt.Printf("GOPATH=%s\n", os.Getenv("GOPATH"))
   394  	fmt.Printf("GOROOT=%s\n", runtime.GOROOT())
   395  	return nil
   396  }
   397  
   398  func bzzd(ctx *cli.Context) error {
   399  	//build a valid bzzapi.Config from all available sources:
   400  	//default config, file config, command line and env vars
   401  	bzzconfig, err := buildConfig(ctx)
   402  	if err != nil {
   403  		utils.Fatalf("unable to configure swarm: %v", err)
   404  	}
   405  
   406  	cfg := defaultNodeConfig
   407  	//gvnt only supports --datadir via command line
   408  	//in order to be consistent within swarm, if we pass --datadir via environment variable
   409  	//or via config file, we get the same directory for gvnt and swarm
   410  	if _, err := os.Stat(bzzconfig.Path); err == nil {
   411  		cfg.DataDir = bzzconfig.Path
   412  	}
   413  	//setup the vntchain node
   414  	utils.SetNodeConfig(ctx, &cfg)
   415  	stack, err := node.New(&cfg)
   416  	if err != nil {
   417  		utils.Fatalf("can't create node: %v", err)
   418  	}
   419  	//a few steps need to be done after the config phase is completed,
   420  	//due to overriding behavior
   421  	initSwarmNode(bzzconfig, stack, ctx)
   422  	//register BZZ as node.Service in the vntchain node
   423  	registerBzzService(bzzconfig, ctx, stack)
   424  	//start the node
   425  	utils.StartNode(stack)
   426  
   427  	go func() {
   428  		sigc := make(chan os.Signal, 1)
   429  		signal.Notify(sigc, syscall.SIGTERM)
   430  		defer signal.Stop(sigc)
   431  		<-sigc
   432  		log.Info("Got sigterm, shutting swarm down...")
   433  		stack.Stop()
   434  	}()
   435  
   436  	// Add bootnodes as initial peers.
   437  	if bzzconfig.BootNodes != "" {
   438  		bootnodes := strings.Split(bzzconfig.BootNodes, ",")
   439  		injectBootnodes(stack.Server(), bootnodes)
   440  	} else {
   441  		if bzzconfig.NetworkId == 3 {
   442  			injectBootnodes(stack.Server(), testbetBootNodes)
   443  		}
   444  	}
   445  
   446  	stack.Wait()
   447  	return nil
   448  }
   449  
   450  func registerBzzService(bzzconfig *bzzapi.Config, ctx *cli.Context, stack *node.Node) {
   451  
   452  	//define the swarm service boot function
   453  	boot := func(ctx *node.ServiceContext) (node.Service, error) {
   454  		var swapClient *vntclient.Client
   455  		var err error
   456  		if bzzconfig.SwapApi != "" {
   457  			log.Info("connecting to SWAP API", "url", bzzconfig.SwapApi)
   458  			swapClient, err = vntclient.Dial(bzzconfig.SwapApi)
   459  			if err != nil {
   460  				return nil, fmt.Errorf("error connecting to SWAP API %s: %s", bzzconfig.SwapApi, err)
   461  			}
   462  		}
   463  
   464  		return swarm.NewSwarm(ctx, swapClient, bzzconfig)
   465  	}
   466  	//register within the vntchain node
   467  	if err := stack.Register(boot); err != nil {
   468  		utils.Fatalf("Failed to register the Swarm service: %v", err)
   469  	}
   470  }
   471  
   472  func getAccount(bzzaccount string, ctx *cli.Context, stack *node.Node) *ecdsa.PrivateKey {
   473  	//an account is mandatory
   474  	if bzzaccount == "" {
   475  		utils.Fatalf(SWARM_ERR_NO_BZZACCOUNT)
   476  	}
   477  	// Try to load the arg as a hex key file.
   478  	if key, err := crypto.LoadECDSA(bzzaccount); err == nil {
   479  		log.Info("Swarm account key loaded", "address", crypto.PubkeyToAddress(key.PublicKey))
   480  		return key
   481  	}
   482  	// Otherwise try getting it from the keystore.
   483  	am := stack.AccountManager()
   484  	ks := am.Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore)
   485  
   486  	return decryptStoreAccount(ks, bzzaccount, utils.MakePasswordList(ctx))
   487  }
   488  
   489  func decryptStoreAccount(ks *keystore.KeyStore, account string, passwords []string) *ecdsa.PrivateKey {
   490  	var a accounts.Account
   491  	var err error
   492  	if common.IsHexAddress(account) {
   493  		a, err = ks.Find(accounts.Account{Address: common.HexToAddress(account)})
   494  	} else if ix, ixerr := strconv.Atoi(account); ixerr == nil && ix > 0 {
   495  		if accounts := ks.Accounts(); len(accounts) > ix {
   496  			a = accounts[ix]
   497  		} else {
   498  			err = fmt.Errorf("index %d higher than number of accounts %d", ix, len(accounts))
   499  		}
   500  	} else {
   501  		utils.Fatalf("Can't find swarm account key %s", account)
   502  	}
   503  	if err != nil {
   504  		utils.Fatalf("Can't find swarm account key: %v - Is the provided bzzaccount(%s) from the right datadir/Path?", err, account)
   505  	}
   506  	keyjson, err := ioutil.ReadFile(a.URL.Path)
   507  	if err != nil {
   508  		utils.Fatalf("Can't load swarm account key: %v", err)
   509  	}
   510  	for i := 0; i < 3; i++ {
   511  		password := getPassPhrase(fmt.Sprintf("Unlocking swarm account %s [%d/3]", a.Address.Hex(), i+1), i, passwords)
   512  		key, err := keystore.DecryptKey(keyjson, password)
   513  		if err == nil {
   514  			return key.PrivateKey
   515  		}
   516  	}
   517  	utils.Fatalf("Can't decrypt swarm account key")
   518  	return nil
   519  }
   520  
   521  // getPassPhrase retrieves the password associated with bzz account, either by fetching
   522  // from a list of pre-loaded passwords, or by requesting it interactively from user.
   523  func getPassPhrase(prompt string, i int, passwords []string) string {
   524  	// non-interactive
   525  	if len(passwords) > 0 {
   526  		if i < len(passwords) {
   527  			return passwords[i]
   528  		}
   529  		return passwords[len(passwords)-1]
   530  	}
   531  
   532  	// fallback to interactive mode
   533  	if prompt != "" {
   534  		fmt.Println(prompt)
   535  	}
   536  	password, err := console.Stdin.PromptPassword("Passphrase: ")
   537  	if err != nil {
   538  		utils.Fatalf("Failed to read passphrase: %v", err)
   539  	}
   540  	return password
   541  }
   542  
   543  func injectBootnodes(srv *vntp2p.Server, nodes []string) {
   544  
   545  	for _, url := range nodes {
   546  		n, err := vntp2p.ParseNode(url)
   547  		if err != nil {
   548  			log.Error("Invalid swarm bootnode", "err", err)
   549  			continue
   550  		}
   551  		srv.AddPeer(context.Background(), n)
   552  	}
   553  }