github.com/devfans/go-ethereum@v1.5.10-0.20170326212234-7419d0c38291/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  	"strconv"
    27  	"strings"
    28  	"syscall"
    29  
    30  	"github.com/ethereum/go-ethereum/accounts"
    31  	"github.com/ethereum/go-ethereum/accounts/keystore"
    32  	"github.com/ethereum/go-ethereum/cmd/utils"
    33  	"github.com/ethereum/go-ethereum/common"
    34  	"github.com/ethereum/go-ethereum/console"
    35  	"github.com/ethereum/go-ethereum/crypto"
    36  	"github.com/ethereum/go-ethereum/ethclient"
    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  	"gopkg.in/urfave/cli.v1"
    45  )
    46  
    47  const (
    48  	clientIdentifier = "swarm"
    49  	versionString    = "0.2"
    50  )
    51  
    52  var (
    53  	gitCommit        string // Git SHA1 commit hash of the release (set via linker flags)
    54  	app              = utils.NewApp(gitCommit, "Ethereum Swarm")
    55  	testbetBootNodes = []string{
    56  		"enode://ec8ae764f7cb0417bdfb009b9d0f18ab3818a3a4e8e7c67dd5f18971a93510a2e6f43cd0b69a27e439a9629457ea804104f37c85e41eed057d3faabbf7744cdf@13.74.157.139:30429",
    57  		"enode://c2e1fceb3bf3be19dff71eec6cccf19f2dbf7567ee017d130240c670be8594bc9163353ca55dd8df7a4f161dd94b36d0615c17418b5a3cdcbb4e9d99dfa4de37@13.74.157.139:30430",
    58  		"enode://fe29b82319b734ce1ec68b84657d57145fee237387e63273989d354486731e59f78858e452ef800a020559da22dcca759536e6aa5517c53930d29ce0b1029286@13.74.157.139:30431",
    59  		"enode://1d7187e7bde45cf0bee489ce9852dd6d1a0d9aa67a33a6b8e6db8a4fbc6fcfa6f0f1a5419343671521b863b187d1c73bad3603bae66421d157ffef357669ddb8@13.74.157.139:30432",
    60  		"enode://0e4cba800f7b1ee73673afa6a4acead4018f0149d2e3216be3f133318fd165b324cd71b81fbe1e80deac8dbf56e57a49db7be67f8b9bc81bd2b7ee496434fb5d@13.74.157.139:30433",
    61  	}
    62  )
    63  
    64  var (
    65  	ChequebookAddrFlag = cli.StringFlag{
    66  		Name:  "chequebook",
    67  		Usage: "chequebook contract address",
    68  	}
    69  	SwarmAccountFlag = cli.StringFlag{
    70  		Name:  "bzzaccount",
    71  		Usage: "Swarm account key file",
    72  	}
    73  	SwarmPortFlag = cli.StringFlag{
    74  		Name:  "bzzport",
    75  		Usage: "Swarm local http api port",
    76  	}
    77  	SwarmNetworkIdFlag = cli.IntFlag{
    78  		Name:  "bzznetworkid",
    79  		Usage: "Network identifier (integer, default 3=swarm testnet)",
    80  	}
    81  	SwarmConfigPathFlag = cli.StringFlag{
    82  		Name:  "bzzconfig",
    83  		Usage: "Swarm config file path (datadir/bzz)",
    84  	}
    85  	SwarmSwapEnabledFlag = cli.BoolFlag{
    86  		Name:  "swap",
    87  		Usage: "Swarm SWAP enabled (default false)",
    88  	}
    89  	SwarmSyncEnabledFlag = cli.BoolTFlag{
    90  		Name:  "sync",
    91  		Usage: "Swarm Syncing enabled (default true)",
    92  	}
    93  	EthAPIFlag = cli.StringFlag{
    94  		Name:  "ethapi",
    95  		Usage: "URL of the Ethereum API provider",
    96  		Value: node.DefaultIPCEndpoint("geth"),
    97  	}
    98  	SwarmApiFlag = cli.StringFlag{
    99  		Name:  "bzzapi",
   100  		Usage: "Swarm HTTP endpoint",
   101  		Value: "http://127.0.0.1:8500",
   102  	}
   103  	SwarmRecursiveUploadFlag = cli.BoolFlag{
   104  		Name:  "recursive",
   105  		Usage: "Upload directories recursively",
   106  	}
   107  	SwarmWantManifestFlag = cli.BoolTFlag{
   108  		Name:  "manifest",
   109  		Usage: "Automatic manifest upload",
   110  	}
   111  	SwarmUploadDefaultPath = cli.StringFlag{
   112  		Name:  "defaultpath",
   113  		Usage: "path to file served for empty url path (none)",
   114  	}
   115  	CorsStringFlag = cli.StringFlag{
   116  		Name:  "corsdomain",
   117  		Usage: "Domain on which to send Access-Control-Allow-Origin header (multiple domains can be supplied separated by a ',')",
   118  	}
   119  )
   120  
   121  func init() {
   122  	// Override flag defaults so bzzd can run alongside geth.
   123  	utils.ListenPortFlag.Value = 30399
   124  	utils.IPCPathFlag.Value = utils.DirectoryString{Value: "bzzd.ipc"}
   125  	utils.IPCApiFlag.Value = "admin, bzz, chequebook, debug, rpc, swarmfs, web3"
   126  
   127  	// Set up the cli app.
   128  	app.Action = bzzd
   129  	app.HideVersion = true // we have a command to print the version
   130  	app.Copyright = "Copyright 2013-2016 The go-ethereum Authors"
   131  	app.Commands = []cli.Command{
   132  		{
   133  			Action:    version,
   134  			Name:      "version",
   135  			Usage:     "Print version numbers",
   136  			ArgsUsage: " ",
   137  			Description: `
   138  The output of this command is supposed to be machine-readable.
   139  `,
   140  		},
   141  		{
   142  			Action:    upload,
   143  			Name:      "up",
   144  			Usage:     "upload a file or directory to swarm using the HTTP API",
   145  			ArgsUsage: " <file>",
   146  			Description: `
   147  "upload a file or directory to swarm using the HTTP API and prints the root hash",
   148  `,
   149  		},
   150  		{
   151  			Action:    hash,
   152  			Name:      "hash",
   153  			Usage:     "print the swarm hash of a file or directory",
   154  			ArgsUsage: " <file>",
   155  			Description: `
   156  Prints the swarm hash of file or directory.
   157  `,
   158  		},
   159  		{
   160  			Name:      "manifest",
   161  			Usage:     "update a MANIFEST",
   162  			ArgsUsage: "manifest COMMAND",
   163  			Description: `
   164  Updates a MANIFEST by adding/removing/updating the hash of a path.
   165  `,
   166  			Subcommands: []cli.Command{
   167  				{
   168  					Action:    add,
   169  					Name:      "add",
   170  					Usage:     "add a new path to the manifest",
   171  					ArgsUsage: "<MANIFEST> <path> <hash> [<content-type>]",
   172  					Description: `
   173  Adds a new path to the manifest
   174  `,
   175  				},
   176  				{
   177  					Action:    update,
   178  					Name:      "update",
   179  					Usage:     "update the hash for an already existing path in the manifest",
   180  					ArgsUsage: "<MANIFEST> <path> <newhash> [<newcontent-type>]",
   181  					Description: `
   182  Update the hash for an already existing path in the manifest
   183  `,
   184  				},
   185  				{
   186  					Action:    remove,
   187  					Name:      "remove",
   188  					Usage:     "removes a path from the manifest",
   189  					ArgsUsage: "<MANIFEST> <path>",
   190  					Description: `
   191  Removes a path from the manifest
   192  `,
   193  				},
   194  			},
   195  		},
   196  		{
   197  			Action:    cleandb,
   198  			Name:      "cleandb",
   199  			Usage:     "Cleans database of corrupted entries",
   200  			ArgsUsage: " ",
   201  			Description: `
   202  Cleans database of corrupted entries.
   203  `,
   204  		},
   205  	}
   206  
   207  	app.Flags = []cli.Flag{
   208  		utils.IdentityFlag,
   209  		utils.DataDirFlag,
   210  		utils.BootnodesFlag,
   211  		utils.KeyStoreDirFlag,
   212  		utils.ListenPortFlag,
   213  		utils.NoDiscoverFlag,
   214  		utils.DiscoveryV5Flag,
   215  		utils.NetrestrictFlag,
   216  		utils.NodeKeyFileFlag,
   217  		utils.NodeKeyHexFlag,
   218  		utils.MaxPeersFlag,
   219  		utils.NATFlag,
   220  		utils.IPCDisabledFlag,
   221  		utils.IPCApiFlag,
   222  		utils.IPCPathFlag,
   223  		// bzzd-specific flags
   224  		CorsStringFlag,
   225  		EthAPIFlag,
   226  		SwarmConfigPathFlag,
   227  		SwarmSwapEnabledFlag,
   228  		SwarmSyncEnabledFlag,
   229  		SwarmPortFlag,
   230  		SwarmAccountFlag,
   231  		SwarmNetworkIdFlag,
   232  		ChequebookAddrFlag,
   233  		// upload flags
   234  		SwarmApiFlag,
   235  		SwarmRecursiveUploadFlag,
   236  		SwarmWantManifestFlag,
   237  		SwarmUploadDefaultPath,
   238  	}
   239  	app.Flags = append(app.Flags, debug.Flags...)
   240  	app.Before = func(ctx *cli.Context) error {
   241  		runtime.GOMAXPROCS(runtime.NumCPU())
   242  		return debug.Setup(ctx)
   243  	}
   244  	app.After = func(ctx *cli.Context) error {
   245  		debug.Exit()
   246  		return nil
   247  	}
   248  }
   249  
   250  func main() {
   251  	if err := app.Run(os.Args); err != nil {
   252  		fmt.Fprintln(os.Stderr, err)
   253  		os.Exit(1)
   254  	}
   255  }
   256  
   257  func version(ctx *cli.Context) error {
   258  	fmt.Println(strings.Title(clientIdentifier))
   259  	fmt.Println("Version:", versionString)
   260  	if gitCommit != "" {
   261  		fmt.Println("Git Commit:", gitCommit)
   262  	}
   263  	fmt.Println("Network Id:", ctx.GlobalInt(utils.NetworkIdFlag.Name))
   264  	fmt.Println("Go Version:", runtime.Version())
   265  	fmt.Println("OS:", runtime.GOOS)
   266  	fmt.Printf("GOPATH=%s\n", os.Getenv("GOPATH"))
   267  	fmt.Printf("GOROOT=%s\n", runtime.GOROOT())
   268  	return nil
   269  }
   270  
   271  func bzzd(ctx *cli.Context) error {
   272  	stack := utils.MakeNode(ctx, clientIdentifier, gitCommit)
   273  	registerBzzService(ctx, stack)
   274  	utils.StartNode(stack)
   275  	go func() {
   276  		sigc := make(chan os.Signal, 1)
   277  		signal.Notify(sigc, syscall.SIGTERM)
   278  		defer signal.Stop(sigc)
   279  		<-sigc
   280  		log.Info("Got sigterm, shutting swarm down...")
   281  		stack.Stop()
   282  	}()
   283  	networkId := ctx.GlobalUint64(SwarmNetworkIdFlag.Name)
   284  	// Add bootnodes as initial peers.
   285  	if ctx.GlobalIsSet(utils.BootnodesFlag.Name) {
   286  		bootnodes := strings.Split(ctx.GlobalString(utils.BootnodesFlag.Name), ",")
   287  		injectBootnodes(stack.Server(), bootnodes)
   288  	} else {
   289  		if networkId == 3 {
   290  			injectBootnodes(stack.Server(), testbetBootNodes)
   291  		}
   292  	}
   293  
   294  	stack.Wait()
   295  	return nil
   296  }
   297  
   298  func registerBzzService(ctx *cli.Context, stack *node.Node) {
   299  
   300  	prvkey := getAccount(ctx, stack)
   301  
   302  	chbookaddr := common.HexToAddress(ctx.GlobalString(ChequebookAddrFlag.Name))
   303  	bzzdir := ctx.GlobalString(SwarmConfigPathFlag.Name)
   304  	if bzzdir == "" {
   305  		bzzdir = stack.InstanceDir()
   306  	}
   307  
   308  	bzzconfig, err := bzzapi.NewConfig(bzzdir, chbookaddr, prvkey, ctx.GlobalUint64(SwarmNetworkIdFlag.Name))
   309  	if err != nil {
   310  		utils.Fatalf("unable to configure swarm: %v", err)
   311  	}
   312  	bzzport := ctx.GlobalString(SwarmPortFlag.Name)
   313  	if len(bzzport) > 0 {
   314  		bzzconfig.Port = bzzport
   315  	}
   316  	swapEnabled := ctx.GlobalBool(SwarmSwapEnabledFlag.Name)
   317  	syncEnabled := ctx.GlobalBoolT(SwarmSyncEnabledFlag.Name)
   318  
   319  	ethapi := ctx.GlobalString(EthAPIFlag.Name)
   320  	cors := ctx.GlobalString(CorsStringFlag.Name)
   321  
   322  	boot := func(ctx *node.ServiceContext) (node.Service, error) {
   323  		var client *ethclient.Client
   324  		if len(ethapi) > 0 {
   325  			client, err = ethclient.Dial(ethapi)
   326  			if err != nil {
   327  				utils.Fatalf("Can't connect: %v", err)
   328  			}
   329  		}
   330  		return swarm.NewSwarm(ctx, client, bzzconfig, swapEnabled, syncEnabled, cors)
   331  	}
   332  	if err := stack.Register(boot); err != nil {
   333  		utils.Fatalf("Failed to register the Swarm service: %v", err)
   334  	}
   335  }
   336  
   337  func getAccount(ctx *cli.Context, stack *node.Node) *ecdsa.PrivateKey {
   338  	keyid := ctx.GlobalString(SwarmAccountFlag.Name)
   339  
   340  	if keyid == "" {
   341  		utils.Fatalf("Option %q is required", SwarmAccountFlag.Name)
   342  	}
   343  	// Try to load the arg as a hex key file.
   344  	if key, err := crypto.LoadECDSA(keyid); err == nil {
   345  		log.Info("Swarm account key loaded", "address", crypto.PubkeyToAddress(key.PublicKey))
   346  		return key
   347  	}
   348  	// Otherwise try getting it from the keystore.
   349  	am := stack.AccountManager()
   350  	ks := am.Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore)
   351  
   352  	return decryptStoreAccount(ks, keyid)
   353  }
   354  
   355  func decryptStoreAccount(ks *keystore.KeyStore, account string) *ecdsa.PrivateKey {
   356  	var a accounts.Account
   357  	var err error
   358  	if common.IsHexAddress(account) {
   359  		a, err = ks.Find(accounts.Account{Address: common.HexToAddress(account)})
   360  	} else if ix, ixerr := strconv.Atoi(account); ixerr == nil && ix > 0 {
   361  		if accounts := ks.Accounts(); len(accounts) > ix {
   362  			a = accounts[ix]
   363  		} else {
   364  			err = fmt.Errorf("index %d higher than number of accounts %d", ix, len(accounts))
   365  		}
   366  	} else {
   367  		utils.Fatalf("Can't find swarm account key %s", account)
   368  	}
   369  	if err != nil {
   370  		utils.Fatalf("Can't find swarm account key: %v", err)
   371  	}
   372  	keyjson, err := ioutil.ReadFile(a.URL.Path)
   373  	if err != nil {
   374  		utils.Fatalf("Can't load swarm account key: %v", err)
   375  	}
   376  	for i := 1; i <= 3; i++ {
   377  		passphrase := promptPassphrase(fmt.Sprintf("Unlocking swarm account %s [%d/3]", a.Address.Hex(), i))
   378  		key, err := keystore.DecryptKey(keyjson, passphrase)
   379  		if err == nil {
   380  			return key.PrivateKey
   381  		}
   382  	}
   383  	utils.Fatalf("Can't decrypt swarm account key")
   384  	return nil
   385  }
   386  
   387  func promptPassphrase(prompt string) string {
   388  	if prompt != "" {
   389  		fmt.Println(prompt)
   390  	}
   391  	password, err := console.Stdin.PromptPassword("Passphrase: ")
   392  	if err != nil {
   393  		utils.Fatalf("Failed to read passphrase: %v", err)
   394  	}
   395  	return password
   396  }
   397  
   398  func injectBootnodes(srv *p2p.Server, nodes []string) {
   399  	for _, url := range nodes {
   400  		n, err := discover.ParseNode(url)
   401  		if err != nil {
   402  			log.Error("Invalid swarm bootnode", "err", err)
   403  			continue
   404  		}
   405  		srv.AddPeer(n)
   406  	}
   407  }