From 5f5d0aa4ff3988d81406f2e79e45a32610894c7b Mon Sep 17 00:00:00 2001 From: zelig Date: Sat, 10 Dec 2016 18:45:52 +0100 Subject: cmd/swarm: subsume cmd/bzz* as subcommands under swarm cmd/swarm: subsume cmd/bzz* under cmd/swarm as subcommands --- cmd/bzzd/main.go | 262 ----------------------------------------- cmd/bzzhash/main.go | 49 -------- cmd/bzzup/main.go | 161 -------------------------- cmd/swarm/hash.go | 48 ++++++++ cmd/swarm/main.go | 327 ++++++++++++++++++++++++++++++++++++++++++++++++++++ cmd/swarm/upload.go | 160 +++++++++++++++++++++++++ 6 files changed, 535 insertions(+), 472 deletions(-) delete mode 100644 cmd/bzzd/main.go delete mode 100644 cmd/bzzhash/main.go delete mode 100644 cmd/bzzup/main.go create mode 100644 cmd/swarm/hash.go create mode 100644 cmd/swarm/main.go create mode 100644 cmd/swarm/upload.go diff --git a/cmd/bzzd/main.go b/cmd/bzzd/main.go deleted file mode 100644 index 4bb2ca04a..000000000 --- a/cmd/bzzd/main.go +++ /dev/null @@ -1,262 +0,0 @@ -// 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 = "bzzd" - -var ( - gitCommit string // Git SHA1 commit hash of the release (set via linker flags) - app = utils.NewApp(gitCommit, "Ethereum Swarm server daemon") -) - -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 322=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"), - } -) - -var defaultBootnodes = []string{} - -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.Commands = nil - app.Action = bzzd - 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, - } - 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 bzzd(ctx *cli.Context) error { - stack := utils.MakeNode(ctx, clientIdentifier, gitCommit) - registerBzzService(ctx, stack) - utils.StartNode(stack) - - // Add bootnodes as initial peers. - if ctx.GlobalIsSet(utils.BootnodesFlag.Name) { - bootnodes := strings.Split(ctx.GlobalString(utils.BootnodesFlag.Name), ",") - injectBootnodes(stack.Server(), bootnodes) - } else { - injectBootnodes(stack.Server(), defaultBootnodes) - } - - 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 ethapi == "" { - err = fmt.Errorf("use ethapi flag to connect to a an eth client and talk to the blockchain") - } else { - 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) - } -} diff --git a/cmd/bzzhash/main.go b/cmd/bzzhash/main.go deleted file mode 100644 index 0ae99acc0..000000000 --- a/cmd/bzzhash/main.go +++ /dev/null @@ -1,49 +0,0 @@ -// 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 . - -// Command bzzhash computes a swarm tree hash. -package main - -import ( - "fmt" - "os" - "runtime" - - "github.com/ethereum/go-ethereum/swarm/storage" -) - -func main() { - runtime.GOMAXPROCS(runtime.NumCPU()) - - if len(os.Args) < 2 { - fmt.Println("Usage: bzzhash ") - os.Exit(0) - } - f, err := os.Open(os.Args[1]) - if err != nil { - fmt.Println("Error opening file " + os.Args[1]) - os.Exit(1) - } - - stat, _ := f.Stat() - chunker := storage.NewTreeChunker(storage.NewChunkerParams()) - key, err := chunker.Split(f, stat.Size(), nil, nil, nil) - if err != nil { - fmt.Fprintf(os.Stderr, "%v\n", err) - } else { - fmt.Printf("%v\n", key) - } -} diff --git a/cmd/bzzup/main.go b/cmd/bzzup/main.go deleted file mode 100644 index 7d251aadb..000000000 --- a/cmd/bzzup/main.go +++ /dev/null @@ -1,161 +0,0 @@ -// 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 . - -// Command bzzup uploads files to the swarm HTTP API. -package main - -import ( - "bytes" - "encoding/json" - "flag" - "fmt" - "io" - "io/ioutil" - "log" - "mime" - "net/http" - "os" - "path/filepath" - "strings" -) - -func main() { - var ( - bzzapiFlag = flag.String("bzzapi", "http://127.0.0.1:8500", "Swarm HTTP endpoint") - recursiveFlag = flag.Bool("recursive", false, "Upload directories recursively") - manifestFlag = flag.Bool("manifest", true, "Skip automatic manifest upload") - ) - log.SetOutput(os.Stderr) - log.SetFlags(0) - flag.Parse() - if flag.NArg() != 1 { - log.Fatal("need filename as the first and only argument") - } - - var ( - file = flag.Arg(0) - client = &client{api: *bzzapiFlag} - mroot manifest - ) - fi, err := os.Stat(file) - if err != nil { - log.Fatal(err) - } - if fi.IsDir() { - if !*recursiveFlag { - log.Fatal("argument is a directory and recursive upload is disabled") - } - mroot, err = client.uploadDirectory(file) - } else { - mroot, err = client.uploadFile(file, fi) - if *manifestFlag { - // Wrap the raw file entry in a proper manifest so both hashes get printed. - mroot = manifest{Entries: []manifest{mroot}} - } - } - if err != nil { - log.Fatalln("upload failed:", err) - } - if *manifestFlag { - hash, err := client.uploadManifest(mroot) - if err != nil { - log.Fatalln("manifest upload failed:", err) - } - mroot.Hash = hash - } - - // Print the manifest. This is the only output to stdout. - mrootJSON, _ := json.MarshalIndent(mroot, "", " ") - fmt.Println(string(mrootJSON)) -} - -// client wraps interaction with the swarm HTTP gateway. -type client struct { - api string -} - -// manifest is the JSON representation of a swarm manifest. -type manifest struct { - Hash string `json:"hash,omitempty"` - ContentType string `json:"contentType,omitempty"` - Path string `json:"path,omitempty"` - Entries []manifest `json:"entries,omitempty"` -} - -func (c *client) uploadFile(file string, fi os.FileInfo) (manifest, error) { - hash, err := c.uploadFileContent(file, fi) - m := manifest{ - Hash: hash, - ContentType: mime.TypeByExtension(filepath.Ext(fi.Name())), - } - return m, err -} - -func (c *client) uploadDirectory(dir string) (manifest, error) { - dirm := manifest{} - prefix := filepath.ToSlash(filepath.Clean(dir)) + "/" - err := filepath.Walk(dir, func(path string, fi os.FileInfo, err error) error { - if err != nil || fi.IsDir() { - return err - } - if !strings.HasPrefix(path, dir) { - return fmt.Errorf("path %s outside directory %s", path, dir) - } - entry, err := c.uploadFile(path, fi) - entry.Path = strings.TrimPrefix(filepath.ToSlash(filepath.Clean(path)), prefix) - dirm.Entries = append(dirm.Entries, entry) - return err - }) - return dirm, err -} - -func (c *client) uploadFileContent(file string, fi os.FileInfo) (string, error) { - fd, err := os.Open(file) - if err != nil { - return "", err - } - defer fd.Close() - log.Printf("uploading file %s (%d bytes)", file, fi.Size()) - return c.postRaw("application/octet-stream", fi.Size(), fd) -} - -func (c *client) uploadManifest(m manifest) (string, error) { - jsm, err := json.Marshal(m) - if err != nil { - panic(err) - } - log.Println("uploading manifest") - return c.postRaw("application/json", int64(len(jsm)), ioutil.NopCloser(bytes.NewReader(jsm))) -} - -func (c *client) postRaw(mimetype string, size int64, body io.ReadCloser) (string, error) { - req, err := http.NewRequest("POST", c.api+"/bzzr:/", body) - if err != nil { - return "", err - } - req.Header.Set("content-type", mimetype) - req.ContentLength = size - resp, err := http.DefaultClient.Do(req) - if err != nil { - return "", err - } - defer resp.Body.Close() - if resp.StatusCode >= 400 { - return "", fmt.Errorf("bad status: %s", resp.Status) - } - content, err := ioutil.ReadAll(resp.Body) - return string(content), err -} diff --git a/cmd/swarm/hash.go b/cmd/swarm/hash.go new file mode 100644 index 000000000..0a20bea82 --- /dev/null +++ b/cmd/swarm/hash.go @@ -0,0 +1,48 @@ +// 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 . + +// Command bzzhash computes a swarm tree hash. +package main + +import ( + "fmt" + "log" + "os" + + "github.com/ethereum/go-ethereum/swarm/storage" + "gopkg.in/urfave/cli.v1" +) + +func hash(ctx *cli.Context) { + args := ctx.Args() + if len(args) < 1 { + log.Fatal("Usage: swarm hash ") + } + f, err := os.Open(args[0]) + if err != nil { + fmt.Println("Error opening file " + args[1]) + os.Exit(1) + } + + stat, _ := f.Stat() + chunker := storage.NewTreeChunker(storage.NewChunkerParams()) + key, err := chunker.Split(f, stat.Size(), nil, nil, nil) + if err != nil { + log.Fatalf("%v\n", err) + } else { + fmt.Printf("%v\n", key) + } +} diff --git a/cmd/swarm/main.go b/cmd/swarm/main.go new file mode 100644 index 000000000..a76e29c3b --- /dev/null +++ b/cmd/swarm/main.go @@ -0,0 +1,327 @@ +// 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") +) + +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 322=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", + } +) + +var defaultBootnodes = []string{} + +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, + } + 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) + + // Add bootnodes as initial peers. + if ctx.GlobalIsSet(utils.BootnodesFlag.Name) { + bootnodes := strings.Split(ctx.GlobalString(utils.BootnodesFlag.Name), ",") + injectBootnodes(stack.Server(), bootnodes) + } else { + injectBootnodes(stack.Server(), defaultBootnodes) + } + + 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 ethapi == "" { + err = fmt.Errorf("use ethapi flag to connect to a an eth client and talk to the blockchain") + } else { + 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) + } +} diff --git a/cmd/swarm/upload.go b/cmd/swarm/upload.go new file mode 100644 index 000000000..0aceef20b --- /dev/null +++ b/cmd/swarm/upload.go @@ -0,0 +1,160 @@ +// 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 . + +// Command bzzup uploads files to the swarm HTTP API. +package main + +import ( + "bytes" + "encoding/json" + "fmt" + "io" + "io/ioutil" + "log" + "mime" + "net/http" + "os" + "path/filepath" + "strings" + + "gopkg.in/urfave/cli.v1" +) + +func upload(ctx *cli.Context) { + args := ctx.Args() + var ( + bzzapi = ctx.GlobalString(SwarmApiFlag.Name) + recursive = ctx.GlobalBool(SwarmRecursiveUploadFlag.Name) + wantManifest = ctx.GlobalBoolT(SwarmWantManifestFlag.Name) + ) + if len(args) != 1 { + log.Fatal("need filename as the first and only argument") + } + + var ( + file = args[0] + client = &client{api: bzzapi} + mroot manifest + ) + fi, err := os.Stat(file) + if err != nil { + log.Fatal(err) + } + if fi.IsDir() { + if !recursive { + log.Fatal("argument is a directory and recursive upload is disabled") + } + mroot, err = client.uploadDirectory(file) + } else { + mroot, err = client.uploadFile(file, fi) + if wantManifest { + // Wrap the raw file entry in a proper manifest so both hashes get printed. + mroot = manifest{Entries: []manifest{mroot}} + } + } + if err != nil { + log.Fatalln("upload failed:", err) + } + if wantManifest { + hash, err := client.uploadManifest(mroot) + if err != nil { + log.Fatalln("manifest upload failed:", err) + } + mroot.Hash = hash + } + + // Print the manifest. This is the only output to stdout. + mrootJSON, _ := json.MarshalIndent(mroot, "", " ") + fmt.Println(string(mrootJSON)) +} + +// client wraps interaction with the swarm HTTP gateway. +type client struct { + api string +} + +// manifest is the JSON representation of a swarm manifest. +type manifest struct { + Hash string `json:"hash,omitempty"` + ContentType string `json:"contentType,omitempty"` + Path string `json:"path,omitempty"` + Entries []manifest `json:"entries,omitempty"` +} + +func (c *client) uploadFile(file string, fi os.FileInfo) (manifest, error) { + hash, err := c.uploadFileContent(file, fi) + m := manifest{ + Hash: hash, + ContentType: mime.TypeByExtension(filepath.Ext(fi.Name())), + } + return m, err +} + +func (c *client) uploadDirectory(dir string) (manifest, error) { + dirm := manifest{} + prefix := filepath.ToSlash(filepath.Clean(dir)) + "/" + err := filepath.Walk(dir, func(path string, fi os.FileInfo, err error) error { + if err != nil || fi.IsDir() { + return err + } + if !strings.HasPrefix(path, dir) { + return fmt.Errorf("path %s outside directory %s", path, dir) + } + entry, err := c.uploadFile(path, fi) + entry.Path = strings.TrimPrefix(filepath.ToSlash(filepath.Clean(path)), prefix) + dirm.Entries = append(dirm.Entries, entry) + return err + }) + return dirm, err +} + +func (c *client) uploadFileContent(file string, fi os.FileInfo) (string, error) { + fd, err := os.Open(file) + if err != nil { + return "", err + } + defer fd.Close() + log.Printf("uploading file %s (%d bytes)", file, fi.Size()) + return c.postRaw("application/octet-stream", fi.Size(), fd) +} + +func (c *client) uploadManifest(m manifest) (string, error) { + jsm, err := json.Marshal(m) + if err != nil { + panic(err) + } + log.Println("uploading manifest") + return c.postRaw("application/json", int64(len(jsm)), ioutil.NopCloser(bytes.NewReader(jsm))) +} + +func (c *client) postRaw(mimetype string, size int64, body io.ReadCloser) (string, error) { + req, err := http.NewRequest("POST", c.api+"/bzzr:/", body) + if err != nil { + return "", err + } + req.Header.Set("content-type", mimetype) + req.ContentLength = size + resp, err := http.DefaultClient.Do(req) + if err != nil { + return "", err + } + defer resp.Body.Close() + if resp.StatusCode >= 400 { + return "", fmt.Errorf("bad status: %s", resp.Status) + } + content, err := ioutil.ReadAll(resp.Body) + return string(content), err +} -- cgit v1.2.3 From d7c398b638115ad724e9a1ce363ae78fe25968b7 Mon Sep 17 00:00:00 2001 From: zelig Date: Tue, 13 Dec 2016 09:18:18 +0100 Subject: build: add swarm command to binary packages, update README --- README.md | 4 +--- build/ci.go | 16 +++------------- 2 files changed, 4 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 770546010..66f59470f 100644 --- a/README.md +++ b/README.md @@ -39,9 +39,7 @@ The go-ethereum project comes with several wrappers/executables found in the `cm | `evm` | Developer utility version of the EVM (Ethereum Virtual Machine) that is capable of running bytecode snippets within a configurable environment and execution mode. Its purpose is to allow insolated, fine-grained debugging of EVM opcodes (e.g. `evm --code 60ff60ff --debug`). | | `gethrpctest` | Developer utility tool to support our [ethereum/rpc-test](https://github.com/ethereum/rpc-tests) test suite which validates baseline conformity to the [Ethereum JSON RPC](https://github.com/ethereum/wiki/wiki/JSON-RPC) specs. Please see the [test suite's readme](https://github.com/ethereum/rpc-tests/blob/master/README.md) for details. | | `rlpdump` | Developer utility tool to convert binary RLP ([Recursive Length Prefix](https://github.com/ethereum/wiki/wiki/RLP)) dumps (data encoding used by the Ethereum protocol both network as well as consensus wise) to user friendlier hierarchical representation (e.g. `rlpdump --hex CE0183FFFFFFC4C304050583616263`). | -| `bzzd` | swarm daemon. This is the entrypoint for the swarm network. `bzzd --help` for command line options. See https://swarm-guide.readthedocs.io for swarm documentation. | -| `bzzup` | swarm command line file uploader. `bzzup --help` for command line options | -| `bzzhash` | command to calculate the swarm hash of a file or directory. `bzzhash --help` for command line options | +| `swarm` | swarm daemon and tools. This is the entrypoint for the swarm network. `swarm --help` for command line options and subcommands. See https://swarm-guide.readthedocs.io for swarm documentation. | ## Running geth diff --git a/build/ci.go b/build/ci.go index 602eb8239..6f1978cf7 100644 --- a/build/ci.go +++ b/build/ci.go @@ -72,9 +72,7 @@ var ( executablePath("abigen"), executablePath("evm"), executablePath("geth"), - executablePath("bzzd"), - executablePath("bzzhash"), - executablePath("bzzup"), + executablePath("swarm"), executablePath("rlpdump"), } @@ -93,16 +91,8 @@ var ( Description: "Developer utility version of the EVM (Ethereum Virtual Machine) that is capable of running bytecode snippets within a configurable environment and execution mode.", }, { - Name: "bzzd", - Description: "Ethereum Swarm daemon", - }, - { - Name: "bzzup", - Description: "Ethereum Swarm command line file/directory uploader", - }, - { - Name: "bzzhash", - Description: "Ethereum Swarm file/directory hash calculator", + Name: "swarm", + Description: "Ethereum Swarm daemon and tools", }, { Name: "abigen", -- cgit v1.2.3 From 418104648894c2a96e08a1666010a0b43d3a400b Mon Sep 17 00:00:00 2001 From: zelig Date: Tue, 13 Dec 2016 09:24:33 +0100 Subject: swarm/network, cmd/swarm: swarm default network id is 3 (to match Ropsten) --- cmd/swarm/main.go | 2 +- swarm/network/protocol.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/swarm/main.go b/cmd/swarm/main.go index a76e29c3b..fb2ae8da3 100644 --- a/cmd/swarm/main.go +++ b/cmd/swarm/main.go @@ -68,7 +68,7 @@ var ( } SwarmNetworkIdFlag = cli.IntFlag{ Name: "bzznetworkid", - Usage: "Network identifier (integer, default 322=swarm testnet)", + Usage: "Network identifier (integer, default 3=swarm testnet)", Value: network.NetworkId, } SwarmConfigPathFlag = cli.StringFlag{ diff --git a/swarm/network/protocol.go b/swarm/network/protocol.go index a3ffd338f..4fffaac6d 100644 --- a/swarm/network/protocol.go +++ b/swarm/network/protocol.go @@ -51,7 +51,7 @@ const ( Version = 0 ProtocolLength = uint64(8) ProtocolMaxMsgSize = 10 * 1024 * 1024 - NetworkId = 322 + NetworkId = 3 ) const ( -- cgit v1.2.3 From d44f1a77eeb208a929add38ca4234d9f2a865a26 Mon Sep 17 00:00:00 2001 From: zelig Date: Tue, 13 Dec 2016 09:49:02 +0100 Subject: cmd/swarm: add default bootnodes for testnet 3 --- cmd/swarm/main.go | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/cmd/swarm/main.go b/cmd/swarm/main.go index fb2ae8da3..e968ad4c2 100644 --- a/cmd/swarm/main.go +++ b/cmd/swarm/main.go @@ -49,8 +49,15 @@ const ( ) var ( - gitCommit string // Git SHA1 commit hash of the release (set via linker flags) - app = utils.NewApp(gitCommit, "Ethereum Swarm") + 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 ( @@ -103,8 +110,6 @@ var ( } ) -var defaultBootnodes = []string{} - func init() { // Override flag defaults so bzzd can run alongside geth. utils.ListenPortFlag.Value = 30399 @@ -211,13 +216,15 @@ 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 { - injectBootnodes(stack.Server(), defaultBootnodes) + if networkId == 3 { + injectBootnodes(stack.Server(), testbetBootNodes) + } } stack.Wait() -- cgit v1.2.3 From 8e35f54931a0c5036d82f7b7db756d70290cf118 Mon Sep 17 00:00:00 2001 From: zelig Date: Tue, 13 Dec 2016 09:51:42 +0100 Subject: cmd/swarm: trim trailing slash from bzzapi url --- cmd/swarm/upload.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/swarm/upload.go b/cmd/swarm/upload.go index 0aceef20b..5393d4ef6 100644 --- a/cmd/swarm/upload.go +++ b/cmd/swarm/upload.go @@ -36,7 +36,7 @@ import ( func upload(ctx *cli.Context) { args := ctx.Args() var ( - bzzapi = ctx.GlobalString(SwarmApiFlag.Name) + bzzapi = strings.TrimRight(ctx.GlobalString(SwarmApiFlag.Name), "/") recursive = ctx.GlobalBool(SwarmRecursiveUploadFlag.Name) wantManifest = ctx.GlobalBoolT(SwarmWantManifestFlag.Name) ) -- cgit v1.2.3 From 080699f7df86f472a99d181c46605391dfd99967 Mon Sep 17 00:00:00 2001 From: zelig Date: Tue, 13 Dec 2016 10:52:22 +0100 Subject: cmd/swarm: ethapi not required --- cmd/swarm/main.go | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/cmd/swarm/main.go b/cmd/swarm/main.go index e968ad4c2..144dcc80c 100644 --- a/cmd/swarm/main.go +++ b/cmd/swarm/main.go @@ -254,13 +254,11 @@ func registerBzzService(ctx *cli.Context, stack *node.Node) { boot := func(ctx *node.ServiceContext) (node.Service, error) { var client *ethclient.Client - if ethapi == "" { - err = fmt.Errorf("use ethapi flag to connect to a an eth client and talk to the blockchain") - } else { + if len(ethapi) > 0 { client, err = ethclient.Dial(ethapi) - } - if err != nil { - utils.Fatalf("Can't connect: %v", err) + if err != nil { + utils.Fatalf("Can't connect: %v", err) + } } return swarm.NewSwarm(ctx, client, bzzconfig, swapEnabled, syncEnabled) } -- cgit v1.2.3 From 485748c4161f558bd1aa01c2e064c335208c9482 Mon Sep 17 00:00:00 2001 From: zelig Date: Tue, 13 Dec 2016 12:48:30 +0100 Subject: cmd/swarm: improve uploader output and add defaultpath option --- cmd/swarm/main.go | 5 ++++ cmd/swarm/upload.go | 85 +++++++++++++++++++++++++++++++++++++++-------------- 2 files changed, 68 insertions(+), 22 deletions(-) diff --git a/cmd/swarm/main.go b/cmd/swarm/main.go index 144dcc80c..1c8da43ae 100644 --- a/cmd/swarm/main.go +++ b/cmd/swarm/main.go @@ -108,6 +108,10 @@ var ( Name: "manifest", Usage: "Automatic manifest upload", } + SwarmUploadDefaultPath = cli.StringFlag{ + Name: "defaultpath", + Usage: "path to file served for empty url path (none)", + } ) func init() { @@ -179,6 +183,7 @@ Prints the swarm hash of file or directory. SwarmApiFlag, SwarmRecursiveUploadFlag, SwarmWantManifestFlag, + SwarmUploadDefaultPath, } app.Flags = append(app.Flags, debug.Flags...) app.Before = func(ctx *cli.Context) error { diff --git a/cmd/swarm/upload.go b/cmd/swarm/upload.go index 5393d4ef6..d048bbc40 100644 --- a/cmd/swarm/upload.go +++ b/cmd/swarm/upload.go @@ -27,6 +27,8 @@ import ( "mime" "net/http" "os" + "os/user" + "path" "path/filepath" "strings" @@ -39,6 +41,7 @@ func upload(ctx *cli.Context) { bzzapi = strings.TrimRight(ctx.GlobalString(SwarmApiFlag.Name), "/") recursive = ctx.GlobalBool(SwarmRecursiveUploadFlag.Name) wantManifest = ctx.GlobalBoolT(SwarmWantManifestFlag.Name) + defaultPath = ctx.GlobalString(SwarmUploadDefaultPath.Name) ) if len(args) != 1 { log.Fatal("need filename as the first and only argument") @@ -48,8 +51,9 @@ func upload(ctx *cli.Context) { file = args[0] client = &client{api: bzzapi} mroot manifest + entry manifestEntry ) - fi, err := os.Stat(file) + fi, err := os.Stat(expandPath(file)) if err != nil { log.Fatal(err) } @@ -57,28 +61,49 @@ func upload(ctx *cli.Context) { if !recursive { log.Fatal("argument is a directory and recursive upload is disabled") } - mroot, err = client.uploadDirectory(file) + mroot, err = client.uploadDirectory(file, defaultPath) } else { - mroot, err = client.uploadFile(file, fi) - if wantManifest { - // Wrap the raw file entry in a proper manifest so both hashes get printed. - mroot = manifest{Entries: []manifest{mroot}} - } + entry, err = client.uploadFile(file, fi) + mroot = manifest{[]manifestEntry{entry}} } if err != nil { log.Fatalln("upload failed:", err) } - if wantManifest { - hash, err := client.uploadManifest(mroot) - if err != nil { - log.Fatalln("manifest upload failed:", err) + if !wantManifest { + // Print the manifest. This is the only output to stdout. + mrootJSON, _ := json.MarshalIndent(mroot, "", " ") + fmt.Println(string(mrootJSON)) + return + } + hash, err := client.uploadManifest(mroot) + if err != nil { + log.Fatalln("manifest upload failed:", err) + } + fmt.Println(hash) +} + +// Expands a file path +// 1. replace tilde with users home dir +// 2. expands embedded environment variables +// 3. cleans the path, e.g. /a/b/../c -> /a/c +// Note, it has limitations, e.g. ~someuser/tmp will not be expanded +func expandPath(p string) string { + if strings.HasPrefix(p, "~/") || strings.HasPrefix(p, "~\\") { + if home := homeDir(); home != "" { + p = home + p[1:] } - mroot.Hash = hash } + return path.Clean(os.ExpandEnv(p)) +} - // Print the manifest. This is the only output to stdout. - mrootJSON, _ := json.MarshalIndent(mroot, "", " ") - fmt.Println(string(mrootJSON)) +func homeDir() string { + if home := os.Getenv("HOME"); home != "" { + return home + } + if usr, err := user.Current(); err == nil { + return usr.HomeDir + } + return "" } // client wraps interaction with the swarm HTTP gateway. @@ -86,25 +111,41 @@ type client struct { api string } +// manifest is the JSON representation of a swarm manifest. +type manifestEntry struct { + Hash string `json:"hash,omitempty"` + ContentType string `json:"contentType,omitempty"` + Path string `json:"path,omitempty"` +} + // manifest is the JSON representation of a swarm manifest. type manifest struct { - Hash string `json:"hash,omitempty"` - ContentType string `json:"contentType,omitempty"` - Path string `json:"path,omitempty"` - Entries []manifest `json:"entries,omitempty"` + Entries []manifestEntry `json:"entries,omitempty"` } -func (c *client) uploadFile(file string, fi os.FileInfo) (manifest, error) { +func (c *client) uploadFile(file string, fi os.FileInfo) (manifestEntry, error) { hash, err := c.uploadFileContent(file, fi) - m := manifest{ + m := manifestEntry{ Hash: hash, ContentType: mime.TypeByExtension(filepath.Ext(fi.Name())), } return m, err } -func (c *client) uploadDirectory(dir string) (manifest, error) { +func (c *client) uploadDirectory(dir string, defaultPath string) (manifest, error) { dirm := manifest{} + if len(defaultPath) > 0 { + fi, err := os.Stat(defaultPath) + if err != nil { + log.Fatal(err) + } + entry, err := c.uploadFile(defaultPath, fi) + if err != nil { + log.Fatal(err) + } + entry.Path = "" + dirm.Entries = append(dirm.Entries, entry) + } prefix := filepath.ToSlash(filepath.Clean(dir)) + "/" err := filepath.Walk(dir, func(path string, fi os.FileInfo, err error) error { if err != nil || fi.IsDir() { -- cgit v1.2.3 From 92224d27b124bda3748128a140d50ddf0fa295c1 Mon Sep 17 00:00:00 2001 From: zelig Date: Tue, 13 Dec 2016 13:08:41 +0100 Subject: cmd/swarm: testnet bootnodes IP address change --- cmd/swarm/main.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/cmd/swarm/main.go b/cmd/swarm/main.go index 1c8da43ae..04930760e 100644 --- a/cmd/swarm/main.go +++ b/cmd/swarm/main.go @@ -52,11 +52,11 @@ 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", + "enode://ec8ae764f7cb0417bdfb009b9d0f18ab3818a3a4e8e7c67dd5f18971a93510a2e6f43cd0b69a27e439a9629457ea804104f37c85e41eed057d3faabbf7744cdf@13.74.157.139:30429", + "enode://c2e1fceb3bf3be19dff71eec6cccf19f2dbf7567ee017d130240c670be8594bc9163353ca55dd8df7a4f161dd94b36d0615c17418b5a3cdcbb4e9d99dfa4de37@13.74.157.139:30430", + "enode://fe29b82319b734ce1ec68b84657d57145fee237387e63273989d354486731e59f78858e452ef800a020559da22dcca759536e6aa5517c53930d29ce0b1029286@13.74.157.139:30431", + "enode://1d7187e7bde45cf0bee489ce9852dd6d1a0d9aa67a33a6b8e6db8a4fbc6fcfa6f0f1a5419343671521b863b187d1c73bad3603bae66421d157ffef357669ddb8@13.74.157.139:30432", + "enode://0e4cba800f7b1ee73673afa6a4acead4018f0149d2e3216be3f133318fd165b324cd71b81fbe1e80deac8dbf56e57a49db7be67f8b9bc81bd2b7ee496434fb5d@13.74.157.139:30433", } ) -- cgit v1.2.3