github.com/daragao/go-ethereum@v1.8.14-0.20180809141559-45eaef243198/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  	"fmt"
    22  	"io/ioutil"
    23  	"os"
    24  	"os/signal"
    25  	"runtime"
    26  	"sort"
    27  	"strconv"
    28  	"strings"
    29  	"syscall"
    30  
    31  	"github.com/ethereum/go-ethereum/accounts"
    32  	"github.com/ethereum/go-ethereum/accounts/keystore"
    33  	"github.com/ethereum/go-ethereum/cmd/utils"
    34  	"github.com/ethereum/go-ethereum/common"
    35  	"github.com/ethereum/go-ethereum/console"
    36  	"github.com/ethereum/go-ethereum/crypto"
    37  	"github.com/ethereum/go-ethereum/internal/debug"
    38  	"github.com/ethereum/go-ethereum/log"
    39  	"github.com/ethereum/go-ethereum/node"
    40  	"github.com/ethereum/go-ethereum/p2p"
    41  	"github.com/ethereum/go-ethereum/p2p/discover"
    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  	testbetBootNodes = []string{
    72  		"enode://ec8ae764f7cb0417bdfb009b9d0f18ab3818a3a4e8e7c67dd5f18971a93510a2e6f43cd0b69a27e439a9629457ea804104f37c85e41eed057d3faabbf7744cdf@13.74.157.139:30429",
    73  		"enode://c2e1fceb3bf3be19dff71eec6cccf19f2dbf7567ee017d130240c670be8594bc9163353ca55dd8df7a4f161dd94b36d0615c17418b5a3cdcbb4e9d99dfa4de37@13.74.157.139:30430",
    74  		"enode://fe29b82319b734ce1ec68b84657d57145fee237387e63273989d354486731e59f78858e452ef800a020559da22dcca759536e6aa5517c53930d29ce0b1029286@13.74.157.139:30431",
    75  		"enode://1d7187e7bde45cf0bee489ce9852dd6d1a0d9aa67a33a6b8e6db8a4fbc6fcfa6f0f1a5419343671521b863b187d1c73bad3603bae66421d157ffef357669ddb8@13.74.157.139:30432",
    76  		"enode://0e4cba800f7b1ee73673afa6a4acead4018f0149d2e3216be3f133318fd165b324cd71b81fbe1e80deac8dbf56e57a49db7be67f8b9bc81bd2b7ee496434fb5d@13.74.157.139:30433",
    77  	}
    78  )
    79  
    80  var (
    81  	ChequebookAddrFlag = cli.StringFlag{
    82  		Name:   "chequebook",
    83  		Usage:  "chequebook contract address",
    84  		EnvVar: SWARM_ENV_CHEQUEBOOK_ADDR,
    85  	}
    86  	SwarmAccountFlag = cli.StringFlag{
    87  		Name:   "bzzaccount",
    88  		Usage:  "Swarm account key file",
    89  		EnvVar: SWARM_ENV_ACCOUNT,
    90  	}
    91  	SwarmListenAddrFlag = cli.StringFlag{
    92  		Name:   "httpaddr",
    93  		Usage:  "Swarm HTTP API listening interface",
    94  		EnvVar: SWARM_ENV_LISTEN_ADDR,
    95  	}
    96  	SwarmPortFlag = cli.StringFlag{
    97  		Name:   "bzzport",
    98  		Usage:  "Swarm local http api port",
    99  		EnvVar: SWARM_ENV_PORT,
   100  	}
   101  	SwarmNetworkIdFlag = cli.IntFlag{
   102  		Name:   "bzznetworkid",
   103  		Usage:  "Network identifier (integer, default 3=swarm testnet)",
   104  		EnvVar: SWARM_ENV_NETWORK_ID,
   105  	}
   106  	SwarmSwapEnabledFlag = cli.BoolFlag{
   107  		Name:   "swap",
   108  		Usage:  "Swarm SWAP enabled (default false)",
   109  		EnvVar: SWARM_ENV_SWAP_ENABLE,
   110  	}
   111  	SwarmSwapAPIFlag = cli.StringFlag{
   112  		Name:   "swap-api",
   113  		Usage:  "URL of the Ethereum API provider to use to settle SWAP payments",
   114  		EnvVar: SWARM_ENV_SWAP_API,
   115  	}
   116  	SwarmSyncDisabledFlag = cli.BoolTFlag{
   117  		Name:   "nosync",
   118  		Usage:  "Disable swarm syncing",
   119  		EnvVar: SWARM_ENV_SYNC_DISABLE,
   120  	}
   121  	SwarmSyncUpdateDelay = cli.DurationFlag{
   122  		Name:   "sync-update-delay",
   123  		Usage:  "Duration for sync subscriptions update after no new peers are added (default 15s)",
   124  		EnvVar: SWARM_ENV_SYNC_UPDATE_DELAY,
   125  	}
   126  	SwarmLightNodeEnabled = cli.BoolFlag{
   127  		Name:   "lightnode",
   128  		Usage:  "Enable Swarm LightNode (default false)",
   129  		EnvVar: SWARM_ENV_LIGHT_NODE_ENABLE,
   130  	}
   131  	SwarmDeliverySkipCheckFlag = cli.BoolFlag{
   132  		Name:   "delivery-skip-check",
   133  		Usage:  "Skip chunk delivery check (default false)",
   134  		EnvVar: SWARM_ENV_DELIVERY_SKIP_CHECK,
   135  	}
   136  	EnsAPIFlag = cli.StringSliceFlag{
   137  		Name:   "ens-api",
   138  		Usage:  "ENS API endpoint for a TLD and with contract address, can be repeated, format [tld:][contract-addr@]url",
   139  		EnvVar: SWARM_ENV_ENS_API,
   140  	}
   141  	SwarmApiFlag = cli.StringFlag{
   142  		Name:  "bzzapi",
   143  		Usage: "Swarm HTTP endpoint",
   144  		Value: "http://127.0.0.1:8500",
   145  	}
   146  	SwarmRecursiveFlag = cli.BoolFlag{
   147  		Name:  "recursive",
   148  		Usage: "Upload directories recursively",
   149  	}
   150  	SwarmWantManifestFlag = cli.BoolTFlag{
   151  		Name:  "manifest",
   152  		Usage: "Automatic manifest upload (default true)",
   153  	}
   154  	SwarmUploadDefaultPath = cli.StringFlag{
   155  		Name:  "defaultpath",
   156  		Usage: "path to file served for empty url path (none)",
   157  	}
   158  	SwarmUpFromStdinFlag = cli.BoolFlag{
   159  		Name:  "stdin",
   160  		Usage: "reads data to be uploaded from stdin",
   161  	}
   162  	SwarmUploadMimeType = cli.StringFlag{
   163  		Name:  "mime",
   164  		Usage: "Manually specify MIME type",
   165  	}
   166  	SwarmEncryptedFlag = cli.BoolFlag{
   167  		Name:  "encrypt",
   168  		Usage: "use encrypted upload",
   169  	}
   170  	CorsStringFlag = cli.StringFlag{
   171  		Name:   "corsdomain",
   172  		Usage:  "Domain on which to send Access-Control-Allow-Origin header (multiple domains can be supplied separated by a ',')",
   173  		EnvVar: SWARM_ENV_CORS,
   174  	}
   175  	SwarmStorePath = cli.StringFlag{
   176  		Name:   "store.path",
   177  		Usage:  "Path to leveldb chunk DB (default <$GETH_ENV_DIR>/swarm/bzz-<$BZZ_KEY>/chunks)",
   178  		EnvVar: SWARM_ENV_STORE_PATH,
   179  	}
   180  	SwarmStoreCapacity = cli.Uint64Flag{
   181  		Name:   "store.size",
   182  		Usage:  "Number of chunks (5M is roughly 20-25GB) (default 5000000)",
   183  		EnvVar: SWARM_ENV_STORE_CAPACITY,
   184  	}
   185  	SwarmStoreCacheCapacity = cli.UintFlag{
   186  		Name:   "store.cache.size",
   187  		Usage:  "Number of recent chunks cached in memory (default 5000)",
   188  		EnvVar: SWARM_ENV_STORE_CACHE_CAPACITY,
   189  	}
   190  	SwarmResourceMultihashFlag = cli.BoolFlag{
   191  		Name:  "multihash",
   192  		Usage: "Determines how to interpret data for a resource update. If not present, data will be interpreted as raw, literal data that will be included in the resource",
   193  	}
   194  	SwarmResourceNameFlag = cli.StringFlag{
   195  		Name:  "name",
   196  		Usage: "User-defined name for the new resource",
   197  	}
   198  	SwarmResourceDataOnCreateFlag = cli.StringFlag{
   199  		Name:  "data",
   200  		Usage: "Initializes the resource with the given hex-encoded data. Data must be prefixed by 0x",
   201  	}
   202  )
   203  
   204  //declare a few constant error messages, useful for later error check comparisons in test
   205  var (
   206  	SWARM_ERR_NO_BZZACCOUNT   = "bzzaccount option is required but not set; check your config file, command line or environment variables"
   207  	SWARM_ERR_SWAP_SET_NO_API = "SWAP is enabled but --swap-api is not set"
   208  )
   209  
   210  // this help command gets added to any subcommand that does not define it explicitly
   211  var defaultSubcommandHelp = cli.Command{
   212  	Action:             func(ctx *cli.Context) { cli.ShowCommandHelpAndExit(ctx, "", 1) },
   213  	CustomHelpTemplate: helpTemplate,
   214  	Name:               "help",
   215  	Usage:              "shows this help",
   216  	Hidden:             true,
   217  }
   218  
   219  var defaultNodeConfig = node.DefaultConfig
   220  
   221  // This init function sets defaults so cmd/swarm can run alongside geth.
   222  func init() {
   223  	defaultNodeConfig.Name = clientIdentifier
   224  	defaultNodeConfig.Version = sv.VersionWithCommit(gitCommit)
   225  	defaultNodeConfig.P2P.ListenAddr = ":30399"
   226  	defaultNodeConfig.IPCPath = "bzzd.ipc"
   227  	// Set flag defaults for --help display.
   228  	utils.ListenPortFlag.Value = 30399
   229  }
   230  
   231  var app = utils.NewApp(gitCommit, "Ethereum Swarm")
   232  
   233  // This init function creates the cli.App.
   234  func init() {
   235  	app.Action = bzzd
   236  	app.HideVersion = true // we have a command to print the version
   237  	app.Copyright = "Copyright 2013-2016 The go-ethereum Authors"
   238  	app.Commands = []cli.Command{
   239  		{
   240  			Action:             version,
   241  			CustomHelpTemplate: helpTemplate,
   242  			Name:               "version",
   243  			Usage:              "Print version numbers",
   244  			Description:        "The output of this command is supposed to be machine-readable",
   245  		},
   246  		{
   247  			Action:             upload,
   248  			CustomHelpTemplate: helpTemplate,
   249  			Name:               "up",
   250  			Usage:              "uploads a file or directory to swarm using the HTTP API",
   251  			ArgsUsage:          "<file>",
   252  			Flags:              []cli.Flag{SwarmEncryptedFlag},
   253  			Description:        "uploads a file or directory to swarm using the HTTP API and prints the root hash",
   254  		},
   255  		{
   256  			CustomHelpTemplate: helpTemplate,
   257  			Name:               "resource",
   258  			Usage:              "(Advanced) Create and update Mutable Resources",
   259  			ArgsUsage:          "<create|update|info>",
   260  			Description:        "Works with Mutable Resource Updates",
   261  			Subcommands: []cli.Command{
   262  				{
   263  					Action:             resourceCreate,
   264  					CustomHelpTemplate: helpTemplate,
   265  					Name:               "create",
   266  					Usage:              "creates a new Mutable Resource",
   267  					ArgsUsage:          "<frequency>",
   268  					Description:        "creates a new Mutable Resource",
   269  					Flags:              []cli.Flag{SwarmResourceNameFlag, SwarmResourceDataOnCreateFlag, SwarmResourceMultihashFlag},
   270  				},
   271  				{
   272  					Action:             resourceUpdate,
   273  					CustomHelpTemplate: helpTemplate,
   274  					Name:               "update",
   275  					Usage:              "updates the content of an existing Mutable Resource",
   276  					ArgsUsage:          "<Manifest Address or ENS domain> <0x Hex data>",
   277  					Description:        "updates the content of an existing Mutable Resource",
   278  					Flags:              []cli.Flag{SwarmResourceMultihashFlag},
   279  				},
   280  				{
   281  					Action:             resourceInfo,
   282  					CustomHelpTemplate: helpTemplate,
   283  					Name:               "info",
   284  					Usage:              "obtains information about an existing Mutable Resource",
   285  					ArgsUsage:          "<Manifest Address or ENS domain>",
   286  					Description:        "obtains information about an existing Mutable Resource",
   287  				},
   288  			},
   289  		},
   290  		{
   291  			Action:             list,
   292  			CustomHelpTemplate: helpTemplate,
   293  			Name:               "ls",
   294  			Usage:              "list files and directories contained in a manifest",
   295  			ArgsUsage:          "<manifest> [<prefix>]",
   296  			Description:        "Lists files and directories contained in a manifest",
   297  		},
   298  		{
   299  			Action:             hash,
   300  			CustomHelpTemplate: helpTemplate,
   301  			Name:               "hash",
   302  			Usage:              "print the swarm hash of a file or directory",
   303  			ArgsUsage:          "<file>",
   304  			Description:        "Prints the swarm hash of file or directory",
   305  		},
   306  		{
   307  			Action:    download,
   308  			Name:      "down",
   309  			Flags:     []cli.Flag{SwarmRecursiveFlag},
   310  			Usage:     "downloads a swarm manifest or a file inside a manifest",
   311  			ArgsUsage: " <uri> [<dir>]",
   312  			Description: `
   313  Downloads a swarm bzz uri to the given dir. When no dir is provided, working directory is assumed. --recursive flag is expected when downloading a manifest with multiple entries.
   314  `,
   315  		},
   316  
   317  		{
   318  			Name:               "manifest",
   319  			CustomHelpTemplate: helpTemplate,
   320  			Usage:              "perform operations on swarm manifests",
   321  			ArgsUsage:          "COMMAND",
   322  			Description:        "Updates a MANIFEST by adding/removing/updating the hash of a path.\nCOMMAND could be: add, update, remove",
   323  			Subcommands: []cli.Command{
   324  				{
   325  					Action:             add,
   326  					CustomHelpTemplate: helpTemplate,
   327  					Name:               "add",
   328  					Usage:              "add a new path to the manifest",
   329  					ArgsUsage:          "<MANIFEST> <path> <hash> [<content-type>]",
   330  					Description:        "Adds a new path to the manifest",
   331  				},
   332  				{
   333  					Action:             update,
   334  					CustomHelpTemplate: helpTemplate,
   335  					Name:               "update",
   336  					Usage:              "update the hash for an already existing path in the manifest",
   337  					ArgsUsage:          "<MANIFEST> <path> <newhash> [<newcontent-type>]",
   338  					Description:        "Update the hash for an already existing path in the manifest",
   339  				},
   340  				{
   341  					Action:             remove,
   342  					CustomHelpTemplate: helpTemplate,
   343  					Name:               "remove",
   344  					Usage:              "removes a path from the manifest",
   345  					ArgsUsage:          "<MANIFEST> <path>",
   346  					Description:        "Removes a path from the manifest",
   347  				},
   348  			},
   349  		},
   350  		{
   351  			Name:               "fs",
   352  			CustomHelpTemplate: helpTemplate,
   353  			Usage:              "perform FUSE operations",
   354  			ArgsUsage:          "fs COMMAND",
   355  			Description:        "Performs FUSE operations by mounting/unmounting/listing mount points. This assumes you already have a Swarm node running locally. For all operation you must reference the correct path to bzzd.ipc in order to communicate with the node",
   356  			Subcommands: []cli.Command{
   357  				{
   358  					Action:             mount,
   359  					CustomHelpTemplate: helpTemplate,
   360  					Name:               "mount",
   361  					Flags:              []cli.Flag{utils.IPCPathFlag},
   362  					Usage:              "mount a swarm hash to a mount point",
   363  					ArgsUsage:          "swarm fs mount --ipcpath <path to bzzd.ipc> <manifest hash> <mount point>",
   364  					Description:        "Mounts a Swarm manifest hash to a given mount point. This assumes you already have a Swarm node running locally. You must reference the correct path to your bzzd.ipc file",
   365  				},
   366  				{
   367  					Action:             unmount,
   368  					CustomHelpTemplate: helpTemplate,
   369  					Name:               "unmount",
   370  					Flags:              []cli.Flag{utils.IPCPathFlag},
   371  					Usage:              "unmount a swarmfs mount",
   372  					ArgsUsage:          "swarm fs unmount --ipcpath <path to bzzd.ipc> <mount point>",
   373  					Description:        "Unmounts a swarmfs mount residing at <mount point>. This assumes you already have a Swarm node running locally. You must reference the correct path to your bzzd.ipc file",
   374  				},
   375  				{
   376  					Action:             listMounts,
   377  					CustomHelpTemplate: helpTemplate,
   378  					Name:               "list",
   379  					Flags:              []cli.Flag{utils.IPCPathFlag},
   380  					Usage:              "list swarmfs mounts",
   381  					ArgsUsage:          "swarm fs list --ipcpath <path to bzzd.ipc>",
   382  					Description:        "Lists all mounted swarmfs volumes. This assumes you already have a Swarm node running locally. You must reference the correct path to your bzzd.ipc file",
   383  				},
   384  			},
   385  		},
   386  		{
   387  			Name:               "db",
   388  			CustomHelpTemplate: helpTemplate,
   389  			Usage:              "manage the local chunk database",
   390  			ArgsUsage:          "db COMMAND",
   391  			Description:        "Manage the local chunk database",
   392  			Subcommands: []cli.Command{
   393  				{
   394  					Action:             dbExport,
   395  					CustomHelpTemplate: helpTemplate,
   396  					Name:               "export",
   397  					Usage:              "export a local chunk database as a tar archive (use - to send to stdout)",
   398  					ArgsUsage:          "<chunkdb> <file>",
   399  					Description: `
   400  Export a local chunk database as a tar archive (use - to send to stdout).
   401  
   402      swarm db export ~/.ethereum/swarm/bzz-KEY/chunks chunks.tar
   403  
   404  The export may be quite large, consider piping the output through the Unix
   405  pv(1) tool to get a progress bar:
   406  
   407      swarm db export ~/.ethereum/swarm/bzz-KEY/chunks - | pv > chunks.tar
   408  `,
   409  				},
   410  				{
   411  					Action:             dbImport,
   412  					CustomHelpTemplate: helpTemplate,
   413  					Name:               "import",
   414  					Usage:              "import chunks from a tar archive into a local chunk database (use - to read from stdin)",
   415  					ArgsUsage:          "<chunkdb> <file>",
   416  					Description: `
   417  Import chunks from a tar archive into a local chunk database (use - to read from stdin).
   418  
   419      swarm db import ~/.ethereum/swarm/bzz-KEY/chunks chunks.tar
   420  
   421  The import may be quite large, consider piping the input through the Unix
   422  pv(1) tool to get a progress bar:
   423  
   424      pv chunks.tar | swarm db import ~/.ethereum/swarm/bzz-KEY/chunks -
   425  `,
   426  				},
   427  				{
   428  					Action:             dbClean,
   429  					CustomHelpTemplate: helpTemplate,
   430  					Name:               "clean",
   431  					Usage:              "remove corrupt entries from a local chunk database",
   432  					ArgsUsage:          "<chunkdb>",
   433  					Description:        "Remove corrupt entries from a local chunk database",
   434  				},
   435  			},
   436  		},
   437  
   438  		// See config.go
   439  		DumpConfigCommand,
   440  	}
   441  
   442  	// append a hidden help subcommand to all commands that have subcommands
   443  	// if a help command was already defined above, that one will take precedence.
   444  	addDefaultHelpSubcommands(app.Commands)
   445  
   446  	sort.Sort(cli.CommandsByName(app.Commands))
   447  
   448  	app.Flags = []cli.Flag{
   449  		utils.IdentityFlag,
   450  		utils.DataDirFlag,
   451  		utils.BootnodesFlag,
   452  		utils.KeyStoreDirFlag,
   453  		utils.ListenPortFlag,
   454  		utils.NoDiscoverFlag,
   455  		utils.DiscoveryV5Flag,
   456  		utils.NetrestrictFlag,
   457  		utils.NodeKeyFileFlag,
   458  		utils.NodeKeyHexFlag,
   459  		utils.MaxPeersFlag,
   460  		utils.NATFlag,
   461  		utils.IPCDisabledFlag,
   462  		utils.IPCPathFlag,
   463  		utils.PasswordFileFlag,
   464  		// bzzd-specific flags
   465  		CorsStringFlag,
   466  		EnsAPIFlag,
   467  		SwarmTomlConfigPathFlag,
   468  		SwarmSwapEnabledFlag,
   469  		SwarmSwapAPIFlag,
   470  		SwarmSyncDisabledFlag,
   471  		SwarmSyncUpdateDelay,
   472  		SwarmLightNodeEnabled,
   473  		SwarmDeliverySkipCheckFlag,
   474  		SwarmListenAddrFlag,
   475  		SwarmPortFlag,
   476  		SwarmAccountFlag,
   477  		SwarmNetworkIdFlag,
   478  		ChequebookAddrFlag,
   479  		// upload flags
   480  		SwarmApiFlag,
   481  		SwarmRecursiveFlag,
   482  		SwarmWantManifestFlag,
   483  		SwarmUploadDefaultPath,
   484  		SwarmUpFromStdinFlag,
   485  		SwarmUploadMimeType,
   486  		// storage flags
   487  		SwarmStorePath,
   488  		SwarmStoreCapacity,
   489  		SwarmStoreCacheCapacity,
   490  	}
   491  	rpcFlags := []cli.Flag{
   492  		utils.WSEnabledFlag,
   493  		utils.WSListenAddrFlag,
   494  		utils.WSPortFlag,
   495  		utils.WSApiFlag,
   496  		utils.WSAllowedOriginsFlag,
   497  	}
   498  	app.Flags = append(app.Flags, rpcFlags...)
   499  	app.Flags = append(app.Flags, debug.Flags...)
   500  	app.Flags = append(app.Flags, swarmmetrics.Flags...)
   501  	app.Flags = append(app.Flags, tracing.Flags...)
   502  	app.Before = func(ctx *cli.Context) error {
   503  		runtime.GOMAXPROCS(runtime.NumCPU())
   504  		if err := debug.Setup(ctx, ""); err != nil {
   505  			return err
   506  		}
   507  		swarmmetrics.Setup(ctx)
   508  		tracing.Setup(ctx)
   509  		return nil
   510  	}
   511  	app.After = func(ctx *cli.Context) error {
   512  		debug.Exit()
   513  		return nil
   514  	}
   515  }
   516  
   517  func main() {
   518  	if err := app.Run(os.Args); err != nil {
   519  		fmt.Fprintln(os.Stderr, err)
   520  		os.Exit(1)
   521  	}
   522  }
   523  
   524  func version(ctx *cli.Context) error {
   525  	fmt.Println(strings.Title(clientIdentifier))
   526  	fmt.Println("Version:", sv.VersionWithMeta)
   527  	if gitCommit != "" {
   528  		fmt.Println("Git Commit:", gitCommit)
   529  	}
   530  	fmt.Println("Go Version:", runtime.Version())
   531  	fmt.Println("OS:", runtime.GOOS)
   532  	return nil
   533  }
   534  
   535  func bzzd(ctx *cli.Context) error {
   536  	//build a valid bzzapi.Config from all available sources:
   537  	//default config, file config, command line and env vars
   538  	bzzconfig, err := buildConfig(ctx)
   539  	if err != nil {
   540  		utils.Fatalf("unable to configure swarm: %v", err)
   541  	}
   542  
   543  	cfg := defaultNodeConfig
   544  
   545  	//pss operates on ws
   546  	cfg.WSModules = append(cfg.WSModules, "pss")
   547  
   548  	//geth only supports --datadir via command line
   549  	//in order to be consistent within swarm, if we pass --datadir via environment variable
   550  	//or via config file, we get the same directory for geth and swarm
   551  	if _, err := os.Stat(bzzconfig.Path); err == nil {
   552  		cfg.DataDir = bzzconfig.Path
   553  	}
   554  	//setup the ethereum node
   555  	utils.SetNodeConfig(ctx, &cfg)
   556  	stack, err := node.New(&cfg)
   557  	if err != nil {
   558  		utils.Fatalf("can't create node: %v", err)
   559  	}
   560  	//a few steps need to be done after the config phase is completed,
   561  	//due to overriding behavior
   562  	initSwarmNode(bzzconfig, stack, ctx)
   563  	//register BZZ as node.Service in the ethereum node
   564  	registerBzzService(bzzconfig, stack)
   565  	//start the node
   566  	utils.StartNode(stack)
   567  
   568  	go func() {
   569  		sigc := make(chan os.Signal, 1)
   570  		signal.Notify(sigc, syscall.SIGTERM)
   571  		defer signal.Stop(sigc)
   572  		<-sigc
   573  		log.Info("Got sigterm, shutting swarm down...")
   574  		stack.Stop()
   575  	}()
   576  
   577  	// Add bootnodes as initial peers.
   578  	if bzzconfig.BootNodes != "" {
   579  		bootnodes := strings.Split(bzzconfig.BootNodes, ",")
   580  		injectBootnodes(stack.Server(), bootnodes)
   581  	} else {
   582  		if bzzconfig.NetworkID == 3 {
   583  			injectBootnodes(stack.Server(), testbetBootNodes)
   584  		}
   585  	}
   586  
   587  	stack.Wait()
   588  	return nil
   589  }
   590  
   591  func registerBzzService(bzzconfig *bzzapi.Config, stack *node.Node) {
   592  	//define the swarm service boot function
   593  	boot := func(_ *node.ServiceContext) (node.Service, error) {
   594  		// In production, mockStore must be always nil.
   595  		return swarm.NewSwarm(bzzconfig, nil)
   596  	}
   597  	//register within the ethereum node
   598  	if err := stack.Register(boot); err != nil {
   599  		utils.Fatalf("Failed to register the Swarm service: %v", err)
   600  	}
   601  }
   602  
   603  func getAccount(bzzaccount string, ctx *cli.Context, stack *node.Node) *ecdsa.PrivateKey {
   604  	//an account is mandatory
   605  	if bzzaccount == "" {
   606  		utils.Fatalf(SWARM_ERR_NO_BZZACCOUNT)
   607  	}
   608  	// Try to load the arg as a hex key file.
   609  	if key, err := crypto.LoadECDSA(bzzaccount); err == nil {
   610  		log.Info("Swarm account key loaded", "address", crypto.PubkeyToAddress(key.PublicKey))
   611  		return key
   612  	}
   613  	// Otherwise try getting it from the keystore.
   614  	am := stack.AccountManager()
   615  	ks := am.Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore)
   616  
   617  	return decryptStoreAccount(ks, bzzaccount, utils.MakePasswordList(ctx))
   618  }
   619  
   620  // getPrivKey returns the private key of the specified bzzaccount
   621  // Used only by client commands, such as `resource`
   622  func getPrivKey(ctx *cli.Context) *ecdsa.PrivateKey {
   623  	// booting up the swarm node just as we do in bzzd action
   624  	bzzconfig, err := buildConfig(ctx)
   625  	if err != nil {
   626  		utils.Fatalf("unable to configure swarm: %v", err)
   627  	}
   628  	cfg := defaultNodeConfig
   629  	if _, err := os.Stat(bzzconfig.Path); err == nil {
   630  		cfg.DataDir = bzzconfig.Path
   631  	}
   632  	utils.SetNodeConfig(ctx, &cfg)
   633  	stack, err := node.New(&cfg)
   634  	if err != nil {
   635  		utils.Fatalf("can't create node: %v", err)
   636  	}
   637  	return getAccount(bzzconfig.BzzAccount, ctx, stack)
   638  }
   639  
   640  func decryptStoreAccount(ks *keystore.KeyStore, account string, passwords []string) *ecdsa.PrivateKey {
   641  	var a accounts.Account
   642  	var err error
   643  	if common.IsHexAddress(account) {
   644  		a, err = ks.Find(accounts.Account{Address: common.HexToAddress(account)})
   645  	} else if ix, ixerr := strconv.Atoi(account); ixerr == nil && ix > 0 {
   646  		if accounts := ks.Accounts(); len(accounts) > ix {
   647  			a = accounts[ix]
   648  		} else {
   649  			err = fmt.Errorf("index %d higher than number of accounts %d", ix, len(accounts))
   650  		}
   651  	} else {
   652  		utils.Fatalf("Can't find swarm account key %s", account)
   653  	}
   654  	if err != nil {
   655  		utils.Fatalf("Can't find swarm account key: %v - Is the provided bzzaccount(%s) from the right datadir/Path?", err, account)
   656  	}
   657  	keyjson, err := ioutil.ReadFile(a.URL.Path)
   658  	if err != nil {
   659  		utils.Fatalf("Can't load swarm account key: %v", err)
   660  	}
   661  	for i := 0; i < 3; i++ {
   662  		password := getPassPhrase(fmt.Sprintf("Unlocking swarm account %s [%d/3]", a.Address.Hex(), i+1), i, passwords)
   663  		key, err := keystore.DecryptKey(keyjson, password)
   664  		if err == nil {
   665  			return key.PrivateKey
   666  		}
   667  	}
   668  	utils.Fatalf("Can't decrypt swarm account key")
   669  	return nil
   670  }
   671  
   672  // getPassPhrase retrieves the password associated with bzz account, either by fetching
   673  // from a list of pre-loaded passwords, or by requesting it interactively from user.
   674  func getPassPhrase(prompt string, i int, passwords []string) string {
   675  	// non-interactive
   676  	if len(passwords) > 0 {
   677  		if i < len(passwords) {
   678  			return passwords[i]
   679  		}
   680  		return passwords[len(passwords)-1]
   681  	}
   682  
   683  	// fallback to interactive mode
   684  	if prompt != "" {
   685  		fmt.Println(prompt)
   686  	}
   687  	password, err := console.Stdin.PromptPassword("Passphrase: ")
   688  	if err != nil {
   689  		utils.Fatalf("Failed to read passphrase: %v", err)
   690  	}
   691  	return password
   692  }
   693  
   694  func injectBootnodes(srv *p2p.Server, nodes []string) {
   695  	for _, url := range nodes {
   696  		n, err := discover.ParseNode(url)
   697  		if err != nil {
   698  			log.Error("Invalid swarm bootnode", "err", err)
   699  			continue
   700  		}
   701  		srv.AddPeer(n)
   702  	}
   703  }
   704  
   705  // addDefaultHelpSubcommand scans through defined CLI commands and adds
   706  // a basic help subcommand to each
   707  // if a help command is already defined, it will take precedence over the default.
   708  func addDefaultHelpSubcommands(commands []cli.Command) {
   709  	for i := range commands {
   710  		cmd := &commands[i]
   711  		if cmd.Subcommands != nil {
   712  			cmd.Subcommands = append(cmd.Subcommands, defaultSubcommandHelp)
   713  			addDefaultHelpSubcommands(cmd.Subcommands)
   714  		}
   715  	}
   716  }