github.com/murrekatt/go-ethereum@v1.5.8-0.20170123175102-fc52f2c007fb/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  	"runtime"
    25  	"strconv"
    26  	"strings"
    27  
    28  	"github.com/ethereum/go-ethereum/accounts"
    29  	"github.com/ethereum/go-ethereum/cmd/utils"
    30  	"github.com/ethereum/go-ethereum/common"
    31  	"github.com/ethereum/go-ethereum/console"
    32  	"github.com/ethereum/go-ethereum/crypto"
    33  	"github.com/ethereum/go-ethereum/ethclient"
    34  	"github.com/ethereum/go-ethereum/internal/debug"
    35  	"github.com/ethereum/go-ethereum/logger"
    36  	"github.com/ethereum/go-ethereum/logger/glog"
    37  	"github.com/ethereum/go-ethereum/node"
    38  	"github.com/ethereum/go-ethereum/p2p"
    39  	"github.com/ethereum/go-ethereum/p2p/discover"
    40  	"github.com/ethereum/go-ethereum/swarm"
    41  	bzzapi "github.com/ethereum/go-ethereum/swarm/api"
    42  	"github.com/ethereum/go-ethereum/swarm/network"
    43  	"gopkg.in/urfave/cli.v1"
    44  )
    45  
    46  const (
    47  	clientIdentifier = "swarm"
    48  	versionString    = "0.2"
    49  )
    50  
    51  var (
    52  	gitCommit        string // Git SHA1 commit hash of the release (set via linker flags)
    53  	app              = utils.NewApp(gitCommit, "Ethereum Swarm")
    54  	testbetBootNodes = []string{
    55  		"enode://ec8ae764f7cb0417bdfb009b9d0f18ab3818a3a4e8e7c67dd5f18971a93510a2e6f43cd0b69a27e439a9629457ea804104f37c85e41eed057d3faabbf7744cdf@13.74.157.139:30429",
    56  		"enode://c2e1fceb3bf3be19dff71eec6cccf19f2dbf7567ee017d130240c670be8594bc9163353ca55dd8df7a4f161dd94b36d0615c17418b5a3cdcbb4e9d99dfa4de37@13.74.157.139:30430",
    57  		"enode://fe29b82319b734ce1ec68b84657d57145fee237387e63273989d354486731e59f78858e452ef800a020559da22dcca759536e6aa5517c53930d29ce0b1029286@13.74.157.139:30431",
    58  		"enode://1d7187e7bde45cf0bee489ce9852dd6d1a0d9aa67a33a6b8e6db8a4fbc6fcfa6f0f1a5419343671521b863b187d1c73bad3603bae66421d157ffef357669ddb8@13.74.157.139:30432",
    59  		"enode://0e4cba800f7b1ee73673afa6a4acead4018f0149d2e3216be3f133318fd165b324cd71b81fbe1e80deac8dbf56e57a49db7be67f8b9bc81bd2b7ee496434fb5d@13.74.157.139:30433",
    60  	}
    61  )
    62  
    63  var (
    64  	ChequebookAddrFlag = cli.StringFlag{
    65  		Name:  "chequebook",
    66  		Usage: "chequebook contract address",
    67  	}
    68  	SwarmAccountFlag = cli.StringFlag{
    69  		Name:  "bzzaccount",
    70  		Usage: "Swarm account key file",
    71  	}
    72  	SwarmPortFlag = cli.StringFlag{
    73  		Name:  "bzzport",
    74  		Usage: "Swarm local http api port",
    75  	}
    76  	SwarmNetworkIdFlag = cli.IntFlag{
    77  		Name:  "bzznetworkid",
    78  		Usage: "Network identifier (integer, default 3=swarm testnet)",
    79  		Value: network.NetworkId,
    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, 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  
   161  	app.Flags = []cli.Flag{
   162  		utils.IdentityFlag,
   163  		utils.DataDirFlag,
   164  		utils.BootnodesFlag,
   165  		utils.KeyStoreDirFlag,
   166  		utils.ListenPortFlag,
   167  		utils.NoDiscoverFlag,
   168  		utils.DiscoveryV5Flag,
   169  		utils.NetrestrictFlag,
   170  		utils.NodeKeyFileFlag,
   171  		utils.NodeKeyHexFlag,
   172  		utils.MaxPeersFlag,
   173  		utils.NATFlag,
   174  		utils.IPCDisabledFlag,
   175  		utils.IPCApiFlag,
   176  		utils.IPCPathFlag,
   177  		// bzzd-specific flags
   178  		CorsStringFlag,
   179  		EthAPIFlag,
   180  		SwarmConfigPathFlag,
   181  		SwarmSwapEnabledFlag,
   182  		SwarmSyncEnabledFlag,
   183  		SwarmPortFlag,
   184  		SwarmAccountFlag,
   185  		SwarmNetworkIdFlag,
   186  		ChequebookAddrFlag,
   187  		// upload flags
   188  		SwarmApiFlag,
   189  		SwarmRecursiveUploadFlag,
   190  		SwarmWantManifestFlag,
   191  		SwarmUploadDefaultPath,
   192  	}
   193  	app.Flags = append(app.Flags, debug.Flags...)
   194  	app.Before = func(ctx *cli.Context) error {
   195  		runtime.GOMAXPROCS(runtime.NumCPU())
   196  		return debug.Setup(ctx)
   197  	}
   198  	app.After = func(ctx *cli.Context) error {
   199  		debug.Exit()
   200  		return nil
   201  	}
   202  }
   203  
   204  func main() {
   205  	if err := app.Run(os.Args); err != nil {
   206  		fmt.Fprintln(os.Stderr, err)
   207  		os.Exit(1)
   208  	}
   209  }
   210  
   211  func version(ctx *cli.Context) error {
   212  	fmt.Println(strings.Title(clientIdentifier))
   213  	fmt.Println("Version:", versionString)
   214  	if gitCommit != "" {
   215  		fmt.Println("Git Commit:", gitCommit)
   216  	}
   217  	fmt.Println("Network Id:", ctx.GlobalInt(utils.NetworkIdFlag.Name))
   218  	fmt.Println("Go Version:", runtime.Version())
   219  	fmt.Println("OS:", runtime.GOOS)
   220  	fmt.Printf("GOPATH=%s\n", os.Getenv("GOPATH"))
   221  	fmt.Printf("GOROOT=%s\n", runtime.GOROOT())
   222  	return nil
   223  }
   224  
   225  func bzzd(ctx *cli.Context) error {
   226  	stack := utils.MakeNode(ctx, clientIdentifier, gitCommit)
   227  	registerBzzService(ctx, stack)
   228  	utils.StartNode(stack)
   229  	networkId := ctx.GlobalUint64(SwarmNetworkIdFlag.Name)
   230  	// Add bootnodes as initial peers.
   231  	if ctx.GlobalIsSet(utils.BootnodesFlag.Name) {
   232  		bootnodes := strings.Split(ctx.GlobalString(utils.BootnodesFlag.Name), ",")
   233  		injectBootnodes(stack.Server(), bootnodes)
   234  	} else {
   235  		if networkId == 3 {
   236  			injectBootnodes(stack.Server(), testbetBootNodes)
   237  		}
   238  	}
   239  
   240  	stack.Wait()
   241  	return nil
   242  }
   243  
   244  func registerBzzService(ctx *cli.Context, stack *node.Node) {
   245  	prvkey := getAccount(ctx, stack)
   246  
   247  	chbookaddr := common.HexToAddress(ctx.GlobalString(ChequebookAddrFlag.Name))
   248  	bzzdir := ctx.GlobalString(SwarmConfigPathFlag.Name)
   249  	if bzzdir == "" {
   250  		bzzdir = stack.InstanceDir()
   251  	}
   252  	bzzconfig, err := bzzapi.NewConfig(bzzdir, chbookaddr, prvkey, ctx.GlobalUint64(SwarmNetworkIdFlag.Name))
   253  	if err != nil {
   254  		utils.Fatalf("unable to configure swarm: %v", err)
   255  	}
   256  	bzzport := ctx.GlobalString(SwarmPortFlag.Name)
   257  	if len(bzzport) > 0 {
   258  		bzzconfig.Port = bzzport
   259  	}
   260  	swapEnabled := ctx.GlobalBool(SwarmSwapEnabledFlag.Name)
   261  	syncEnabled := ctx.GlobalBoolT(SwarmSyncEnabledFlag.Name)
   262  
   263  	ethapi := ctx.GlobalString(EthAPIFlag.Name)
   264  	cors := ctx.GlobalString(CorsStringFlag.Name)
   265  
   266  	boot := func(ctx *node.ServiceContext) (node.Service, error) {
   267  		var client *ethclient.Client
   268  		if len(ethapi) > 0 {
   269  			client, err = ethclient.Dial(ethapi)
   270  			if err != nil {
   271  				utils.Fatalf("Can't connect: %v", err)
   272  			}
   273  		}
   274  		return swarm.NewSwarm(ctx, client, bzzconfig, swapEnabled, syncEnabled, cors)
   275  	}
   276  	if err := stack.Register(boot); err != nil {
   277  		utils.Fatalf("Failed to register the Swarm service: %v", err)
   278  	}
   279  }
   280  
   281  func getAccount(ctx *cli.Context, stack *node.Node) *ecdsa.PrivateKey {
   282  	keyid := ctx.GlobalString(SwarmAccountFlag.Name)
   283  	if keyid == "" {
   284  		utils.Fatalf("Option %q is required", SwarmAccountFlag.Name)
   285  	}
   286  	// Try to load the arg as a hex key file.
   287  	if key, err := crypto.LoadECDSA(keyid); err == nil {
   288  		glog.V(logger.Info).Infof("swarm account key loaded: %#x", crypto.PubkeyToAddress(key.PublicKey))
   289  		return key
   290  	}
   291  	// Otherwise try getting it from the keystore.
   292  	return decryptStoreAccount(stack.AccountManager(), keyid)
   293  }
   294  
   295  func decryptStoreAccount(accman *accounts.Manager, account string) *ecdsa.PrivateKey {
   296  	var a accounts.Account
   297  	var err error
   298  	if common.IsHexAddress(account) {
   299  		a, err = accman.Find(accounts.Account{Address: common.HexToAddress(account)})
   300  	} else if ix, ixerr := strconv.Atoi(account); ixerr == nil {
   301  		a, err = accman.AccountByIndex(ix)
   302  	} else {
   303  		utils.Fatalf("Can't find swarm account key %s", account)
   304  	}
   305  	if err != nil {
   306  		utils.Fatalf("Can't find swarm account key: %v", err)
   307  	}
   308  	keyjson, err := ioutil.ReadFile(a.File)
   309  	if err != nil {
   310  		utils.Fatalf("Can't load swarm account key: %v", err)
   311  	}
   312  	for i := 1; i <= 3; i++ {
   313  		passphrase := promptPassphrase(fmt.Sprintf("Unlocking swarm account %s [%d/3]", a.Address.Hex(), i))
   314  		key, err := accounts.DecryptKey(keyjson, passphrase)
   315  		if err == nil {
   316  			return key.PrivateKey
   317  		}
   318  	}
   319  	utils.Fatalf("Can't decrypt swarm account key")
   320  	return nil
   321  }
   322  
   323  func promptPassphrase(prompt string) string {
   324  	if prompt != "" {
   325  		fmt.Println(prompt)
   326  	}
   327  	password, err := console.Stdin.PromptPassword("Passphrase: ")
   328  	if err != nil {
   329  		utils.Fatalf("Failed to read passphrase: %v", err)
   330  	}
   331  	return password
   332  }
   333  
   334  func injectBootnodes(srv *p2p.Server, nodes []string) {
   335  	for _, url := range nodes {
   336  		n, err := discover.ParseNode(url)
   337  		if err != nil {
   338  			glog.Errorf("invalid bootnode %q", err)
   339  			continue
   340  		}
   341  		srv.AddPeer(n)
   342  	}
   343  }