// Copyright 2016 The go-ethereum Authors // This file is part of go-ethereum. // // go-ethereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // go-ethereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with go-ethereum. If not, see . package main import ( "crypto/ecdsa" "fmt" "io/ioutil" "os" "runtime" "strconv" "strings" "github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/cmd/utils" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/console" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/ethclient" "github.com/ethereum/go-ethereum/internal/debug" "github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/logger/glog" "github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/p2p/discover" "github.com/ethereum/go-ethereum/swarm" bzzapi "github.com/ethereum/go-ethereum/swarm/api" "github.com/ethereum/go-ethereum/swarm/network" "gopkg.in/urfave/cli.v1" ) const ( clientIdentifier = "swarm" versionString = "0.2" ) var ( gitCommit string // Git SHA1 commit hash of the release (set via linker flags) app = utils.NewApp(gitCommit, "Ethereum Swarm") testbetBootNodes = []string{ "enode://330dce4992f5ec50a4a6f9e16bf35b9d8ee739236b6530e7846fcb058ed24b666e6027513a4b921fd2ec40ff22f6ddec0937bcbc697a8937d9cde83d8fde8a06@13.79.165.39:30403", "enode://7ea1cc1723b4d51f08f76f6380f5f9faa92232313c6568e79ab1e1a98148f7549a3f8d05b2e6f94c10796e015a224ed2e46df7033077d63a888eeec52fae6fd2@13.79.165.39:30404", "enode://4363f21af9e94e32b3ad22d88ed04f6e5bcf9407b7cb38a61216c57bbdcb9d5c5beb4f9aac1b78049e8d3a516097f5b92fc116928e35c92e48fc9b68086b78f5@13.79.165.39:30405", "enode://79b616c70d309b27319461219032ff7f5901c3e522bb5d5b084e4372666f7f006803387e81563bd1bb06937a4adb8efa16c0ea86e921edf49a09e58ac3a90845@13.79.165.39:30406", "enode://83b4df39d90720193717ccd5476feca81a962a31090fc440a9085941fcbfcd765285edb00c549286be33703d9bfb2723576dc85d705c9bfe1a74ee454d61f2cc@13.79.165.39:30407", } ) var ( ChequebookAddrFlag = cli.StringFlag{ Name: "chequebook", Usage: "chequebook contract address", } SwarmAccountFlag = cli.StringFlag{ Name: "bzzaccount", Usage: "Swarm account key file", } SwarmPortFlag = cli.StringFlag{ Name: "bzzport", Usage: "Swarm local http api port", } SwarmNetworkIdFlag = cli.IntFlag{ Name: "bzznetworkid", Usage: "Network identifier (integer, default 3=swarm testnet)", Value: network.NetworkId, } SwarmConfigPathFlag = cli.StringFlag{ Name: "bzzconfig", Usage: "Swarm config file path (datadir/bzz)", } SwarmSwapEnabled = cli.BoolFlag{ Name: "swap", Usage: "Swarm SWAP enabled (default false)", } SwarmSyncEnabled = cli.BoolTFlag{ Name: "sync", Usage: "Swarm Syncing enabled (default true)", } EthAPI = cli.StringFlag{ Name: "ethapi", Usage: "URL of the Ethereum API provider", Value: node.DefaultIPCEndpoint("geth"), } SwarmApiFlag = cli.StringFlag{ Name: "bzzapi", Usage: "Swarm HTTP endpoint", Value: "http://127.0.0.1:8500", } SwarmRecursiveUploadFlag = cli.BoolFlag{ Name: "recursive", Usage: "Upload directories recursively", } SwarmWantManifestFlag = cli.BoolTFlag{ Name: "manifest", Usage: "Automatic manifest upload", } SwarmUploadDefaultPath = cli.StringFlag{ Name: "defaultpath", Usage: "path to file served for empty url path (none)", } ) func init() { // Override flag defaults so bzzd can run alongside geth. utils.ListenPortFlag.Value = 30399 utils.IPCPathFlag.Value = utils.DirectoryString{Value: "bzzd.ipc"} utils.IPCApiFlag.Value = "admin, bzz, chequebook, debug, rpc, web3" // Set up the cli app. app.Action = bzzd app.HideVersion = true // we have a command to print the version app.Copyright = "Copyright 2013-2016 The go-ethereum Authors" app.Commands = []cli.Command{ cli.Command{ Action: version, Name: "version", Usage: "Print version numbers", ArgsUsage: " ", Description: ` The output of this command is supposed to be machine-readable. `, }, cli.Command{ Action: upload, Name: "up", Usage: "upload a file or directory to swarm using the HTTP API", ArgsUsage: " ", Description: ` "upload a file or directory to swarm using the HTTP API and prints the root hash", `, }, cli.Command{ Action: hash, Name: "hash", Usage: "print the swarm hash of a file or directory", ArgsUsage: " ", Description: ` Prints the swarm hash of file or directory. `, }, } app.Flags = []cli.Flag{ utils.IdentityFlag, utils.DataDirFlag, utils.BootnodesFlag, utils.KeyStoreDirFlag, utils.ListenPortFlag, utils.NoDiscoverFlag, utils.DiscoveryV5Flag, utils.NetrestrictFlag, utils.NodeKeyFileFlag, utils.NodeKeyHexFlag, utils.MaxPeersFlag, utils.NATFlag, utils.IPCDisabledFlag, utils.IPCApiFlag, utils.IPCPathFlag, // bzzd-specific flags EthAPI, SwarmConfigPathFlag, SwarmSwapEnabled, SwarmSyncEnabled, SwarmPortFlag, SwarmAccountFlag, SwarmNetworkIdFlag, ChequebookAddrFlag, // upload flags SwarmApiFlag, SwarmRecursiveUploadFlag, SwarmWantManifestFlag, SwarmUploadDefaultPath, } app.Flags = append(app.Flags, debug.Flags...) app.Before = func(ctx *cli.Context) error { runtime.GOMAXPROCS(runtime.NumCPU()) return debug.Setup(ctx) } app.After = func(ctx *cli.Context) error { debug.Exit() return nil } } func main() { if err := app.Run(os.Args); err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) } } func version(ctx *cli.Context) error { fmt.Println(strings.Title(clientIdentifier)) fmt.Println("Version:", versionString) if gitCommit != "" { fmt.Println("Git Commit:", gitCommit) } fmt.Println("Network Id:", ctx.GlobalInt(utils.NetworkIdFlag.Name)) fmt.Println("Go Version:", runtime.Version()) fmt.Println("OS:", runtime.GOOS) fmt.Printf("GOPATH=%s\n", os.Getenv("GOPATH")) fmt.Printf("GOROOT=%s\n", runtime.GOROOT()) return nil } func bzzd(ctx *cli.Context) error { stack := utils.MakeNode(ctx, clientIdentifier, gitCommit) registerBzzService(ctx, stack) utils.StartNode(stack) networkId := ctx.GlobalUint64(SwarmNetworkIdFlag.Name) // Add bootnodes as initial peers. if ctx.GlobalIsSet(utils.BootnodesFlag.Name) { bootnodes := strings.Split(ctx.GlobalString(utils.BootnodesFlag.Name), ",") injectBootnodes(stack.Server(), bootnodes) } else { if networkId == 3 { injectBootnodes(stack.Server(), testbetBootNodes) } } stack.Wait() return nil } func registerBzzService(ctx *cli.Context, stack *node.Node) { prvkey := getAccount(ctx, stack) chbookaddr := common.HexToAddress(ctx.GlobalString(ChequebookAddrFlag.Name)) bzzdir := ctx.GlobalString(SwarmConfigPathFlag.Name) if bzzdir == "" { bzzdir = stack.InstanceDir() } bzzconfig, err := bzzapi.NewConfig(bzzdir, chbookaddr, prvkey, ctx.GlobalUint64(SwarmNetworkIdFlag.Name)) if err != nil { utils.Fatalf("unable to configure swarm: %v", err) } bzzport := ctx.GlobalString(SwarmPortFlag.Name) if len(bzzport) > 0 { bzzconfig.Port = bzzport } swapEnabled := ctx.GlobalBool(SwarmSwapEnabled.Name) syncEnabled := ctx.GlobalBoolT(SwarmSyncEnabled.Name) ethapi := ctx.GlobalString(EthAPI.Name) boot := func(ctx *node.ServiceContext) (node.Service, error) { var client *ethclient.Client if len(ethapi) > 0 { client, err = ethclient.Dial(ethapi) if err != nil { utils.Fatalf("Can't connect: %v", err) } } return swarm.NewSwarm(ctx, client, bzzconfig, swapEnabled, syncEnabled) } if err := stack.Register(boot); err != nil { utils.Fatalf("Failed to register the Swarm service: %v", err) } } func getAccount(ctx *cli.Context, stack *node.Node) *ecdsa.PrivateKey { keyid := ctx.GlobalString(SwarmAccountFlag.Name) if keyid == "" { utils.Fatalf("Option %q is required", SwarmAccountFlag.Name) } // Try to load the arg as a hex key file. if key, err := crypto.LoadECDSA(keyid); err == nil { glog.V(logger.Info).Infof("swarm account key loaded: %#x", crypto.PubkeyToAddress(key.PublicKey)) return key } // Otherwise try getting it from the keystore. return decryptStoreAccount(stack.AccountManager(), keyid) } func decryptStoreAccount(accman *accounts.Manager, account string) *ecdsa.PrivateKey { var a accounts.Account var err error if common.IsHexAddress(account) { a, err = accman.Find(accounts.Account{Address: common.HexToAddress(account)}) } else if ix, ixerr := strconv.Atoi(account); ixerr == nil { a, err = accman.AccountByIndex(ix) } else { utils.Fatalf("Can't find swarm account key %s", account) } if err != nil { utils.Fatalf("Can't find swarm account key: %v", err) } keyjson, err := ioutil.ReadFile(a.File) if err != nil { utils.Fatalf("Can't load swarm account key: %v", err) } for i := 1; i <= 3; i++ { passphrase := promptPassphrase(fmt.Sprintf("Unlocking swarm account %s [%d/3]", a.Address.Hex(), i)) key, err := accounts.DecryptKey(keyjson, passphrase) if err == nil { return key.PrivateKey } } utils.Fatalf("Can't decrypt swarm account key") return nil } func promptPassphrase(prompt string) string { if prompt != "" { fmt.Println(prompt) } password, err := console.Stdin.PromptPassword("Passphrase: ") if err != nil { utils.Fatalf("Failed to read passphrase: %v", err) } return password } func injectBootnodes(srv *p2p.Server, nodes []string) { for _, url := range nodes { n, err := discover.ParseNode(url) if err != nil { glog.Errorf("invalid bootnode %q", err) continue } srv.AddPeer(n) } }