From 617faeda3acc26e731ec5aebb86f917a22d22dac Mon Sep 17 00:00:00 2001 From: Rafael Matias Date: Tue, 2 Jul 2019 15:39:38 +0200 Subject: [PATCH 01/12] cmd/swarm: don't require bzzaccount flag --- cmd/swarm/main.go | 39 +++++++++++++++++++++++++++++---------- 1 file changed, 29 insertions(+), 10 deletions(-) diff --git a/cmd/swarm/main.go b/cmd/swarm/main.go index a3d91a8a00..7372083bb2 100644 --- a/cmd/swarm/main.go +++ b/cmd/swarm/main.go @@ -353,19 +353,38 @@ func registerBzzService(bzzconfig *bzzapi.Config, stack *node.Node) { func getAccount(bzzaccount string, ctx *cli.Context, stack *node.Node) *ecdsa.PrivateKey { //an account is mandatory - if bzzaccount == "" { - utils.Fatalf(SwarmErrNoBZZAccount) - } - // Try to load the arg as a hex key file. - if key, err := crypto.LoadECDSA(bzzaccount); err == nil { - log.Info("Swarm account key loaded", "address", crypto.PubkeyToAddress(key.PublicKey)) - return key - } - // Otherwise try getting it from the keystore. am := stack.AccountManager() ks := am.Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore) - return decryptStoreAccount(ks, bzzaccount, utils.MakePasswordList(ctx)) + if bzzaccount == "" { + // Check the local keystore for existing accounts + accounts := ks.Accounts() + if len(accounts) > 0 { + // Default to the first account if none was set + bzzaccount = accounts[0].Address.Hex() + } else { + // Create an account + account, err := ks.NewAccount("") + if err != nil { + utils.Fatalf("failed creating an account: %v", err) + } + bzzaccount = account.Address.Hex() + } + } else { + // Try to load the arg as a hex key file. + if key, err := crypto.LoadECDSA(bzzaccount); err == nil { + log.Info("Swarm account key loaded", "address", crypto.PubkeyToAddress(key.PublicKey)) + return key + } + } + // Otherwise try getting it from the keystore + var passwords []string + if path := ctx.GlobalString(utils.PasswordFileFlag.Name); path == "" { + passwords = []string{""} + } else { + passwords = utils.MakePasswordList(ctx) + } + return decryptStoreAccount(ks, bzzaccount, passwords) } // getPrivKey returns the private key of the specified bzzaccount From 2bd70d41f1089ed03f9b76d5f480bd06f331763c Mon Sep 17 00:00:00 2001 From: Rafael Matias Date: Tue, 2 Jul 2019 15:42:27 +0200 Subject: [PATCH 02/12] docker: remove setup script because bzzaccount is not required anymore --- Dockerfile | 3 +-- Dockerfile.alltools | 3 +-- docker/run.sh | 26 -------------------------- 3 files changed, 2 insertions(+), 30 deletions(-) delete mode 100755 docker/run.sh diff --git a/Dockerfile b/Dockerfile index dc493da14d..75b9373599 100644 --- a/Dockerfile +++ b/Dockerfile @@ -10,5 +10,4 @@ FROM alpine:3.9 RUN apk --no-cache add ca-certificates && update-ca-certificates COPY --from=builder /swarm/build/bin/swarm /usr/local/bin/ COPY --from=geth /usr/local/bin/geth /usr/local/bin/ -COPY docker/run.sh /run.sh -ENTRYPOINT ["/run.sh"] +ENTRYPOINT ["/usr/local/bin/swarm"] diff --git a/Dockerfile.alltools b/Dockerfile.alltools index 594333e00c..31837e3db9 100644 --- a/Dockerfile.alltools +++ b/Dockerfile.alltools @@ -10,6 +10,5 @@ FROM alpine:3.9 RUN apk --no-cache add ca-certificates COPY --from=builder /swarm/build/bin/* /usr/local/bin/ COPY --from=geth /usr/local/bin/geth /usr/local/bin/ -COPY docker/run.sh /run.sh COPY docker/run-smoke.sh /run-smoke.sh -ENTRYPOINT ["/run.sh"] +ENTRYPOINT ["/usr/local/bin/swarm"] diff --git a/docker/run.sh b/docker/run.sh deleted file mode 100755 index 4be91ba82f..0000000000 --- a/docker/run.sh +++ /dev/null @@ -1,26 +0,0 @@ -#!/bin/sh - -set -o errexit -set -o pipefail -set -o nounset - -PASSWORD=${PASSWORD:-} -DATADIR=${DATADIR:-/root/.ethereum/} - -if [ "$PASSWORD" == "" ]; then echo "Password must be set, in order to use swarm non-interactively." && exit 1; fi - -echo $PASSWORD > /password - -KEYFILE=`find $DATADIR | grep UTC | head -n 1` || true -if [ ! -f "$KEYFILE" ]; then echo "No keyfile found. Generating..." && geth --datadir $DATADIR --password /password account new; fi -KEYFILE=`find $DATADIR | grep UTC | head -n 1` || true -if [ ! -f "$KEYFILE" ]; then echo "Could not find nor generate a BZZ keyfile." && exit 1; else echo "Found keyfile $KEYFILE"; fi - -VERSION=`swarm version` -echo "Running Swarm:" -echo $VERSION - -export BZZACCOUNT="`echo -n $KEYFILE | tail -c 40`" || true -if [ "$BZZACCOUNT" == "" ]; then echo "Could not parse BZZACCOUNT from keyfile." && exit 1; fi - -exec swarm --bzzaccount=$BZZACCOUNT --password /password --datadir $DATADIR $@ 2>&1 From eeaafd50afb574fa80f32dae9b36350d8a1afec4 Mon Sep 17 00:00:00 2001 From: Rafael Matias Date: Wed, 3 Jul 2019 10:20:26 +0200 Subject: [PATCH 03/12] cmd/swarm: manage account creation/selection when bzzflag is not defined --- cmd/swarm/access.go | 2 +- cmd/swarm/download.go | 2 +- cmd/swarm/main.go | 53 +++++++++++++++++++++++++++---------------- 3 files changed, 36 insertions(+), 21 deletions(-) diff --git a/cmd/swarm/access.go b/cmd/swarm/access.go index 19cae7f445..735b1d7c5b 100644 --- a/cmd/swarm/access.go +++ b/cmd/swarm/access.go @@ -106,7 +106,7 @@ func accessNewPass(ctx *cli.Context) { accessKey []byte err error ref = args[0] - password = getPassPhrase("", 0, makePasswordList(ctx)) + password = getPassPhrase("", false, 0, makePasswordList(ctx)) dryRun = ctx.Bool(SwarmDryRunFlag.Name) ) accessKey, ae, err = api.DoPassword(ctx, password, salt) diff --git a/cmd/swarm/download.go b/cmd/swarm/download.go index b0fd03755f..97cada64cb 100644 --- a/cmd/swarm/download.go +++ b/cmd/swarm/download.go @@ -101,7 +101,7 @@ func download(ctx *cli.Context) { return nil } if passwords := makePasswordList(ctx); passwords != nil { - password := getPassPhrase(fmt.Sprintf("Downloading %s is restricted", uri), 0, passwords) + password := getPassPhrase(fmt.Sprintf("Downloading %s is restricted", uri), false, 0, passwords) err = dl(password) } else { err = dl("") diff --git a/cmd/swarm/main.go b/cmd/swarm/main.go index 7372083bb2..5738c31a13 100644 --- a/cmd/swarm/main.go +++ b/cmd/swarm/main.go @@ -359,17 +359,29 @@ func getAccount(bzzaccount string, ctx *cli.Context, stack *node.Node) *ecdsa.Pr if bzzaccount == "" { // Check the local keystore for existing accounts accounts := ks.Accounts() - if len(accounts) > 0 { - // Default to the first account if none was set - bzzaccount = accounts[0].Address.Hex() - } else { + + switch l := len(accounts); l { + case 0: // Create an account - account, err := ks.NewAccount("") + log.Info("It seems like that you don't have a account yet. Creating one...") + password := getPassPhrase("Your new account is locked with a password. Please give a password. Do not forget this password.", true, 0, utils.MakePasswordList(ctx)) + account, err := ks.NewAccount(password) if err != nil { utils.Fatalf("failed creating an account: %v", err) } bzzaccount = account.Address.Hex() + case 1: + // Use existing account + bzzaccount = accounts[0].Address.Hex() + default: + // Inform user about multiple accounts + log.Info(fmt.Sprintf("Multiple (%d) accounts were found in your keystore.", l)) + for _, a := range accounts { + log.Info(fmt.Sprintf("Account: %s", a.Address.Hex())) + } + utils.Fatalf(fmt.Sprintf("Please choose one of the accounts by runing swarm with the --%s flag.", SwarmAccountFlag.Name)) } + } else { // Try to load the arg as a hex key file. if key, err := crypto.LoadECDSA(bzzaccount); err == nil { @@ -377,14 +389,9 @@ func getAccount(bzzaccount string, ctx *cli.Context, stack *node.Node) *ecdsa.Pr return key } } + // Otherwise try getting it from the keystore - var passwords []string - if path := ctx.GlobalString(utils.PasswordFileFlag.Name); path == "" { - passwords = []string{""} - } else { - passwords = utils.MakePasswordList(ctx) - } - return decryptStoreAccount(ks, bzzaccount, passwords) + return decryptStoreAccount(ks, bzzaccount, utils.MakePasswordList(ctx)) } // getPrivKey returns the private key of the specified bzzaccount @@ -431,7 +438,7 @@ func decryptStoreAccount(ks *keystore.KeyStore, account string, passwords []stri utils.Fatalf("Can't load swarm account key: %v", err) } for i := 0; i < 3; i++ { - password := getPassPhrase(fmt.Sprintf("Unlocking swarm account %s [%d/3]", a.Address.Hex(), i+1), i, passwords) + password := getPassPhrase(fmt.Sprintf("Unlocking swarm account %s [%d/3]", a.Address.Hex(), i+1), false, i, passwords) key, err := keystore.DecryptKey(keyjson, password) if err == nil { return key.PrivateKey @@ -441,18 +448,17 @@ func decryptStoreAccount(ks *keystore.KeyStore, account string, passwords []stri return nil } -// getPassPhrase retrieves the password associated with bzz account, either by fetching -// from a list of pre-loaded passwords, or by requesting it interactively from user. -func getPassPhrase(prompt string, i int, passwords []string) string { - // non-interactive +// getPassPhrase retrieves the password associated with a bzzaccount, either fetched +// from a list of preloaded passphrases, or requested interactively from the user. +func getPassPhrase(prompt string, confirmation bool, i int, passwords []string) string { + // If a list of passwords was supplied, retrieve from them if len(passwords) > 0 { if i < len(passwords) { return passwords[i] } return passwords[len(passwords)-1] } - - // fallback to interactive mode + // Otherwise prompt the user for the password if prompt != "" { fmt.Println(prompt) } @@ -460,6 +466,15 @@ func getPassPhrase(prompt string, i int, passwords []string) string { if err != nil { utils.Fatalf("Failed to read passphrase: %v", err) } + if confirmation { + confirm, err := console.Stdin.PromptPassword("Repeat passphrase: ") + if err != nil { + utils.Fatalf("Failed to read passphrase confirmation: %v", err) + } + if password != confirm { + utils.Fatalf("Passphrases do not match") + } + } return password } From 2525bf363eff26f226a37e785c84224d37aac31e Mon Sep 17 00:00:00 2001 From: Rafael Matias Date: Wed, 3 Jul 2019 12:28:55 +0200 Subject: [PATCH 04/12] cmd/swarm: no bzzaccount flag tests --- cmd/swarm/config.go | 3 +- cmd/swarm/config_test.go | 112 +++++++++++++++++++++++++++++++++++++-- cmd/swarm/main.go | 16 +++--- 3 files changed, 119 insertions(+), 12 deletions(-) diff --git a/cmd/swarm/config.go b/cmd/swarm/config.go index 067e8b9406..582804be4e 100644 --- a/cmd/swarm/config.go +++ b/cmd/swarm/config.go @@ -126,7 +126,8 @@ func buildConfig(ctx *cli.Context) (config *bzzapi.Config, err error) { func initSwarmNode(config *bzzapi.Config, stack *node.Node, ctx *cli.Context, nodeconfig *node.Config) error { //at this point, all vars should be set in the Config //get the account for the provided swarm account - prvkey := getAccount(config.BzzAccount, ctx, stack) + bzzaccount, prvkey := getAccount(config.BzzAccount, ctx, stack) + config.BzzAccount = bzzaccount //set the resolved config path (geth --datadir) config.Path = expandPath(stack.InstanceDir()) //finally, initialize the configuration diff --git a/cmd/swarm/config_test.go b/cmd/swarm/config_test.go index d0020d3a59..63a00c6dae 100644 --- a/cmd/swarm/config_test.go +++ b/cmd/swarm/config_test.go @@ -56,15 +56,117 @@ func TestConfigFailsSwapEnabledNoSwapApi(t *testing.T) { swarm.ExpectExit() } -func TestConfigFailsNoBzzAccount(t *testing.T) { +func TestEmptyBzzAccountFlagMultipleAccounts(t *testing.T) { + dir, err := ioutil.TempDir("", "bzztest") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(dir) + + // Create two accounts on disk + getTestAccount(t, dir) + getTestAccount(t, dir) + + node := &testNode{Dir: dir} + + // assign ports + httpPort, err := assignTCPPort() + if err != nil { + t.Fatal(err) + } + flags := []string{ fmt.Sprintf("--%s", SwarmNetworkIdFlag.Name), "42", - fmt.Sprintf("--%s", SwarmPortFlag.Name), "54545", + fmt.Sprintf("--%s", SwarmPortFlag.Name), httpPort, + fmt.Sprintf("--%s", utils.DataDirFlag.Name), dir, } - swarm := runSwarm(t, flags...) - swarm.Expect("Fatal: " + SwarmErrNoBZZAccount + "\n") - swarm.ExpectExit() + node.Cmd = runSwarm(t, flags...) + + node.Cmd.Expect(fmt.Sprintf("Fatal: Please choose one of the accounts by running swarm with the --%s flag.", SwarmAccountFlag.Name)) +} + +func TestEmptyBzzAccountFlagSingleAccount(t *testing.T) { + dir, err := ioutil.TempDir("", "bzztest") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(dir) + + conf, account := getTestAccount(t, dir) + node := &testNode{Dir: dir} + + // assign ports + httpPort, err := assignTCPPort() + if err != nil { + t.Fatal(err) + } + + flags := []string{ + fmt.Sprintf("--%s", SwarmNetworkIdFlag.Name), "42", + fmt.Sprintf("--%s", SwarmPortFlag.Name), httpPort, + fmt.Sprintf("--%s", utils.DataDirFlag.Name), dir, + } + + node.Cmd = runSwarm(t, flags...) + node.Cmd.InputLine(testPassphrase) + defer func() { + if t.Failed() { + node.Shutdown() + } + }() + // wait for the node to start + for start := time.Now(); time.Since(start) < 10*time.Second; time.Sleep(50 * time.Millisecond) { + node.Client, err = rpc.Dial(conf.IPCEndpoint()) + if err == nil { + break + } + } + if node.Client == nil { + t.Fatal(err) + } + + // load info + var info swarm.Info + if err := node.Client.Call(&info, "bzz_info"); err != nil { + t.Fatal(err) + } + + if info.BzzAccount != account.Address.Hex() { + t.Fatalf("Expected account to be %s, got %s", account.Address.Hex(), info.BzzAccount) + } + +} + +func TestEmptyBzzAccountFlagNoAccountWrongPassword(t *testing.T) { + dir, err := ioutil.TempDir("", "bzztestlol") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(dir) + + node := &testNode{Dir: dir} + + // assign ports + httpPort, err := assignTCPPort() + if err != nil { + t.Fatal(err) + } + + flags := []string{ + fmt.Sprintf("--%s", SwarmNetworkIdFlag.Name), "42", + fmt.Sprintf("--%s", SwarmPortFlag.Name), httpPort, + fmt.Sprintf("--%s", utils.DataDirFlag.Name), dir, + } + + node.Cmd = runSwarm(t, flags...) + + // Set password + node.Cmd.InputLine("goodpassword") + // Confirm password + node.Cmd.InputLine("wrongpassword") + + node.Cmd.ExpectRegexp("Fatal: Passphrases do not match") } func TestConfigCmdLineOverrides(t *testing.T) { diff --git a/cmd/swarm/main.go b/cmd/swarm/main.go index 5738c31a13..265abd71cc 100644 --- a/cmd/swarm/main.go +++ b/cmd/swarm/main.go @@ -351,7 +351,8 @@ func registerBzzService(bzzconfig *bzzapi.Config, stack *node.Node) { } } -func getAccount(bzzaccount string, ctx *cli.Context, stack *node.Node) *ecdsa.PrivateKey { +//getAccount returns the bzzaddress and the private key associated to that address +func getAccount(bzzaccount string, ctx *cli.Context, stack *node.Node) (string, *ecdsa.PrivateKey) { //an account is mandatory am := stack.AccountManager() ks := am.Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore) @@ -379,19 +380,20 @@ func getAccount(bzzaccount string, ctx *cli.Context, stack *node.Node) *ecdsa.Pr for _, a := range accounts { log.Info(fmt.Sprintf("Account: %s", a.Address.Hex())) } - utils.Fatalf(fmt.Sprintf("Please choose one of the accounts by runing swarm with the --%s flag.", SwarmAccountFlag.Name)) + utils.Fatalf(fmt.Sprintf("Please choose one of the accounts by running swarm with the --%s flag.", SwarmAccountFlag.Name)) } } else { // Try to load the arg as a hex key file. if key, err := crypto.LoadECDSA(bzzaccount); err == nil { - log.Info("Swarm account key loaded", "address", crypto.PubkeyToAddress(key.PublicKey)) - return key + bzzaccount := crypto.PubkeyToAddress(key.PublicKey).Hex() + log.Info("Swarm account key loaded", "address", bzzaccount) + return bzzaccount, key } } // Otherwise try getting it from the keystore - return decryptStoreAccount(ks, bzzaccount, utils.MakePasswordList(ctx)) + return bzzaccount, decryptStoreAccount(ks, bzzaccount, utils.MakePasswordList(ctx)) } // getPrivKey returns the private key of the specified bzzaccount @@ -413,7 +415,9 @@ func getPrivKey(ctx *cli.Context) *ecdsa.PrivateKey { } defer stack.Close() - return getAccount(bzzconfig.BzzAccount, ctx, stack) + bzzaccount, privkey := getAccount(bzzconfig.BzzAccount, ctx, stack) + bzzconfig.BzzAccount = bzzaccount + return privkey } func decryptStoreAccount(ks *keystore.KeyStore, account string, passwords []string) *ecdsa.PrivateKey { From 8871f0a0fabc9680dd341eab031f4b637168053a Mon Sep 17 00:00:00 2001 From: Rafael Matias Date: Wed, 3 Jul 2019 14:34:04 +0200 Subject: [PATCH 05/12] cmd/swarm: use random network ports on tests --- cmd/swarm/config_test.go | 41 ++++++++++++++-------------------------- 1 file changed, 14 insertions(+), 27 deletions(-) diff --git a/cmd/swarm/config_test.go b/cmd/swarm/config_test.go index 63a00c6dae..35d6db0187 100644 --- a/cmd/swarm/config_test.go +++ b/cmd/swarm/config_test.go @@ -48,6 +48,7 @@ func TestConfigFailsSwapEnabledNoSwapApi(t *testing.T) { flags := []string{ fmt.Sprintf("--%s", SwarmNetworkIdFlag.Name), "42", fmt.Sprintf("--%s", SwarmPortFlag.Name), "54545", + fmt.Sprintf("--%s", utils.ListenPortFlag.Name), "0", fmt.Sprintf("--%s", SwarmSwapEnabledFlag.Name), } @@ -69,21 +70,16 @@ func TestEmptyBzzAccountFlagMultipleAccounts(t *testing.T) { node := &testNode{Dir: dir} - // assign ports - httpPort, err := assignTCPPort() - if err != nil { - t.Fatal(err) - } - flags := []string{ fmt.Sprintf("--%s", SwarmNetworkIdFlag.Name), "42", - fmt.Sprintf("--%s", SwarmPortFlag.Name), httpPort, + fmt.Sprintf("--%s", SwarmPortFlag.Name), "0", + fmt.Sprintf("--%s", utils.ListenPortFlag.Name), "0", fmt.Sprintf("--%s", utils.DataDirFlag.Name), dir, } node.Cmd = runSwarm(t, flags...) - node.Cmd.Expect(fmt.Sprintf("Fatal: Please choose one of the accounts by running swarm with the --%s flag.", SwarmAccountFlag.Name)) + node.Cmd.ExpectRegexp(fmt.Sprintf("Please choose one of the accounts by running swarm with the --%s flag.", SwarmAccountFlag.Name)) } func TestEmptyBzzAccountFlagSingleAccount(t *testing.T) { @@ -96,25 +92,20 @@ func TestEmptyBzzAccountFlagSingleAccount(t *testing.T) { conf, account := getTestAccount(t, dir) node := &testNode{Dir: dir} - // assign ports - httpPort, err := assignTCPPort() - if err != nil { - t.Fatal(err) - } - flags := []string{ fmt.Sprintf("--%s", SwarmNetworkIdFlag.Name), "42", - fmt.Sprintf("--%s", SwarmPortFlag.Name), httpPort, + fmt.Sprintf("--%s", SwarmPortFlag.Name), "0", + fmt.Sprintf("--%s", utils.ListenPortFlag.Name), "0", fmt.Sprintf("--%s", utils.DataDirFlag.Name), dir, } node.Cmd = runSwarm(t, flags...) - node.Cmd.InputLine(testPassphrase) defer func() { - if t.Failed() { - node.Shutdown() - } + node.Shutdown() }() + + node.Cmd.InputLine(testPassphrase) + // wait for the node to start for start := time.Now(); time.Since(start) < 10*time.Second; time.Sleep(50 * time.Millisecond) { node.Client, err = rpc.Dial(conf.IPCEndpoint()) @@ -147,15 +138,10 @@ func TestEmptyBzzAccountFlagNoAccountWrongPassword(t *testing.T) { node := &testNode{Dir: dir} - // assign ports - httpPort, err := assignTCPPort() - if err != nil { - t.Fatal(err) - } - flags := []string{ fmt.Sprintf("--%s", SwarmNetworkIdFlag.Name), "42", - fmt.Sprintf("--%s", SwarmPortFlag.Name), httpPort, + fmt.Sprintf("--%s", SwarmPortFlag.Name), "0", + fmt.Sprintf("--%s", utils.ListenPortFlag.Name), "0", fmt.Sprintf("--%s", utils.DataDirFlag.Name), dir, } @@ -166,7 +152,7 @@ func TestEmptyBzzAccountFlagNoAccountWrongPassword(t *testing.T) { // Confirm password node.Cmd.InputLine("wrongpassword") - node.Cmd.ExpectRegexp("Fatal: Passphrases do not match") + node.Cmd.ExpectRegexp("Passphrases do not match") } func TestConfigCmdLineOverrides(t *testing.T) { @@ -188,6 +174,7 @@ func TestConfigCmdLineOverrides(t *testing.T) { flags := []string{ fmt.Sprintf("--%s", SwarmNetworkIdFlag.Name), "42", fmt.Sprintf("--%s", SwarmPortFlag.Name), httpPort, + fmt.Sprintf("--%s", utils.ListenPortFlag.Name), "0", fmt.Sprintf("--%s", SwarmSyncDisabledFlag.Name), fmt.Sprintf("--%s", CorsStringFlag.Name), "*", fmt.Sprintf("--%s", SwarmAccountFlag.Name), account.Address.String(), From cd1ed5529951f7697e332081130b95f8ae6c3f46 Mon Sep 17 00:00:00 2001 From: Rafael Matias Date: Wed, 3 Jul 2019 15:19:10 +0200 Subject: [PATCH 06/12] cmd/swarm: fix typo --- cmd/swarm/main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/swarm/main.go b/cmd/swarm/main.go index 265abd71cc..f1e5567180 100644 --- a/cmd/swarm/main.go +++ b/cmd/swarm/main.go @@ -364,7 +364,7 @@ func getAccount(bzzaccount string, ctx *cli.Context, stack *node.Node) (string, switch l := len(accounts); l { case 0: // Create an account - log.Info("It seems like that you don't have a account yet. Creating one...") + log.Info("You don't have an account yet. Creating one...") password := getPassPhrase("Your new account is locked with a password. Please give a password. Do not forget this password.", true, 0, utils.MakePasswordList(ctx)) account, err := ks.NewAccount(password) if err != nil { From 121267da874f032dfdd055b1a03d291266600994 Mon Sep 17 00:00:00 2001 From: Rafael Matias Date: Wed, 3 Jul 2019 16:32:10 +0200 Subject: [PATCH 07/12] cmd/swarm: add --bzzkeyhex flag --- cmd/swarm/config.go | 1 + cmd/swarm/config_test.go | 65 ++++++++++++++++++++++++++++++++++++++-- cmd/swarm/flags.go | 5 ++++ cmd/swarm/main.go | 15 +++++++++- 4 files changed, 83 insertions(+), 3 deletions(-) diff --git a/cmd/swarm/config.go b/cmd/swarm/config.go index 582804be4e..713c56a644 100644 --- a/cmd/swarm/config.go +++ b/cmd/swarm/config.go @@ -61,6 +61,7 @@ var ( const ( SwarmEnvChequebookAddr = "SWARM_CHEQUEBOOK_ADDR" SwarmEnvAccount = "SWARM_ACCOUNT" + SwarmEnvBzzKeyHex = "SWARM_BZZ_KEY_HEX" SwarmEnvListenAddr = "SWARM_LISTEN_ADDR" SwarmEnvPort = "SWARM_PORT" SwarmEnvNetworkID = "SWARM_NETWORK_ID" diff --git a/cmd/swarm/config_test.go b/cmd/swarm/config_test.go index 35d6db0187..5242549a46 100644 --- a/cmd/swarm/config_test.go +++ b/cmd/swarm/config_test.go @@ -17,6 +17,7 @@ package main import ( + "encoding/hex" "fmt" "io" "io/ioutil" @@ -28,6 +29,8 @@ import ( "github.com/docker/docker/pkg/reexec" "github.com/ethereum/go-ethereum/cmd/utils" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/rpc" "github.com/ethersphere/swarm" "github.com/ethersphere/swarm/api" @@ -57,6 +60,65 @@ func TestConfigFailsSwapEnabledNoSwapApi(t *testing.T) { swarm.ExpectExit() } +func TestBzzKeyFlag(t *testing.T) { + key, err := crypto.GenerateKey() + if err != nil { + t.Fatal("unable to generate key") + } + hexKey := hex.EncodeToString(crypto.FromECDSA(key)) + bzzaccount := crypto.PubkeyToAddress(key.PublicKey).Hex() + + dir, err := ioutil.TempDir("", "bzztest") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(dir) + + conf := &node.Config{ + DataDir: dir, + IPCPath: "bzzd.ipc", + NoUSB: true, + } + + node := &testNode{Dir: dir} + + flags := []string{ + fmt.Sprintf("--%s", SwarmNetworkIdFlag.Name), "42", + fmt.Sprintf("--%s", SwarmPortFlag.Name), "0", + fmt.Sprintf("--%s", utils.ListenPortFlag.Name), "0", + fmt.Sprintf("--%s", utils.DataDirFlag.Name), dir, + fmt.Sprintf("--%s", SwarmBzzKeyHexFlag.Name), hexKey, + } + + node.Cmd = runSwarm(t, flags...) + defer func() { + node.Shutdown() + }() + + node.Cmd.InputLine(testPassphrase) + + // wait for the node to start + for start := time.Now(); time.Since(start) < 10*time.Second; time.Sleep(50 * time.Millisecond) { + node.Client, err = rpc.Dial(conf.IPCEndpoint()) + if err == nil { + break + } + } + if node.Client == nil { + t.Fatal(err) + } + + // load info + var info swarm.Info + if err := node.Client.Call(&info, "bzz_info"); err != nil { + t.Fatal(err) + } + + if info.BzzAccount != bzzaccount { + t.Fatalf("Expected account to be %s, got %s", bzzaccount, info.BzzAccount) + } + +} func TestEmptyBzzAccountFlagMultipleAccounts(t *testing.T) { dir, err := ioutil.TempDir("", "bzztest") if err != nil { @@ -126,11 +188,10 @@ func TestEmptyBzzAccountFlagSingleAccount(t *testing.T) { if info.BzzAccount != account.Address.Hex() { t.Fatalf("Expected account to be %s, got %s", account.Address.Hex(), info.BzzAccount) } - } func TestEmptyBzzAccountFlagNoAccountWrongPassword(t *testing.T) { - dir, err := ioutil.TempDir("", "bzztestlol") + dir, err := ioutil.TempDir("", "bzztest") if err != nil { t.Fatal(err) } diff --git a/cmd/swarm/flags.go b/cmd/swarm/flags.go index 6093149e3c..0bb0fe2543 100644 --- a/cmd/swarm/flags.go +++ b/cmd/swarm/flags.go @@ -30,6 +30,11 @@ var ( Usage: "Swarm account key file", EnvVar: SwarmEnvAccount, } + SwarmBzzKeyHexFlag = cli.StringFlag{ + Name: "bzzkeyhex", + Usage: "BzzAccount key in hex (for testing)", + EnvVar: SwarmEnvBzzKeyHex, + } SwarmListenAddrFlag = cli.StringFlag{ Name: "httpaddr", Usage: "Swarm HTTP API listening interface", diff --git a/cmd/swarm/main.go b/cmd/swarm/main.go index f1e5567180..c7f2025165 100644 --- a/cmd/swarm/main.go +++ b/cmd/swarm/main.go @@ -184,6 +184,7 @@ func init() { SwarmListenAddrFlag, SwarmPortFlag, SwarmAccountFlag, + SwarmBzzKeyHexFlag, SwarmNetworkIdFlag, ChequebookAddrFlag, // upload flags @@ -353,7 +354,19 @@ func registerBzzService(bzzconfig *bzzapi.Config, stack *node.Node) { //getAccount returns the bzzaddress and the private key associated to that address func getAccount(bzzaccount string, ctx *cli.Context, stack *node.Node) (string, *ecdsa.PrivateKey) { - //an account is mandatory + + // Handle bzzkeyhex + if hexkey := ctx.GlobalString(SwarmBzzKeyHexFlag.Name); hexkey != "" { + key, err := crypto.HexToECDSA(hexkey) + if err != nil { + utils.Fatalf("failed using %s: %v", SwarmBzzKeyHexFlag.Name, err) + } + bzzaccount := crypto.PubkeyToAddress(key.PublicKey).Hex() + log.Info(fmt.Sprintf("Swarm account key loaded from %s", SwarmBzzKeyHexFlag.Name), "address", bzzaccount) + return bzzaccount, key + } + + // Handle bzzaccount am := stack.AccountManager() ks := am.Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore) From 1bb045d94399acf8eb13f8345932a9620d38611c Mon Sep 17 00:00:00 2001 From: Rafael Matias Date: Wed, 3 Jul 2019 16:57:39 +0200 Subject: [PATCH 08/12] cmd/swarm: use different ipc paths for tests --- cmd/swarm/config_test.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cmd/swarm/config_test.go b/cmd/swarm/config_test.go index 5242549a46..9a67b498df 100644 --- a/cmd/swarm/config_test.go +++ b/cmd/swarm/config_test.go @@ -76,7 +76,7 @@ func TestBzzKeyFlag(t *testing.T) { conf := &node.Config{ DataDir: dir, - IPCPath: "bzzd.ipc", + IPCPath: fmt.Sprintf("bzzd-%s.ipc", bzzaccount), NoUSB: true, } @@ -87,6 +87,7 @@ func TestBzzKeyFlag(t *testing.T) { fmt.Sprintf("--%s", SwarmPortFlag.Name), "0", fmt.Sprintf("--%s", utils.ListenPortFlag.Name), "0", fmt.Sprintf("--%s", utils.DataDirFlag.Name), dir, + fmt.Sprintf("--%s", utils.IPCPathFlag.Name), conf.IPCPath, fmt.Sprintf("--%s", SwarmBzzKeyHexFlag.Name), hexKey, } @@ -159,6 +160,7 @@ func TestEmptyBzzAccountFlagSingleAccount(t *testing.T) { fmt.Sprintf("--%s", SwarmPortFlag.Name), "0", fmt.Sprintf("--%s", utils.ListenPortFlag.Name), "0", fmt.Sprintf("--%s", utils.DataDirFlag.Name), dir, + fmt.Sprintf("--%s", utils.IPCPathFlag.Name), conf.IPCPath, } node.Cmd = runSwarm(t, flags...) From 1bf639559a341cc863b1485cb634a48d97e6b9f6 Mon Sep 17 00:00:00 2001 From: Rafael Matias Date: Wed, 3 Jul 2019 17:24:58 +0200 Subject: [PATCH 09/12] readme: update example on how to run swarm --- README.md | 58 +++++++++++++++++++++++-------------------------------- 1 file changed, 24 insertions(+), 34 deletions(-) diff --git a/README.md b/README.md index a13c391ff7..08037cd651 100644 --- a/README.md +++ b/README.md @@ -9,26 +9,26 @@ Swarm is a distributed storage platform and content distribution service, a nati ## Table of Contents -- [Building the source](#building-the-source) -- [Running Swarm](#running-swarm) - - [Verifying that your local Swarm node is running](#verifying-that-your-local-swarm-node-is-running) - - [Ethereum Name Service resolution](#ethereum-name-service-resolution) -- [Documentation](#documentation) -- [Docker](#docker) - - [Docker tags](#docker-tags) - - [Environment variables](#environment-variables) - - [Swarm command line arguments](#swarm-command-line-arguments) -- [Developers Guide](#developers-guide) - - [Go Environment](#go-environment) - - [Vendored Dependencies](#vendored-dependencies) - - [Testing](#testing) - - [Profiling Swarm](#profiling-swarm) - - [Metrics and Instrumentation in Swarm](#metrics-and-instrumentation-in-swarm) - - [Visualizing metrics](#visualizing-metrics) -- [Public Gateways](#public-gateways) -- [Swarm Dapps](#swarm-dapps) -- [Contributing](#contributing) -- [License](#license) +- [Building the source](#Building-the-source) +- [Running Swarm](#Running-Swarm) + - [Verifying that your local Swarm node is running](#Verifying-that-your-local-Swarm-node-is-running) + - [Ethereum Name Service resolution](#Ethereum-Name-Service-resolution) +- [Documentation](#Documentation) +- [Docker](#Docker) + - [Docker tags](#Docker-tags) + - [Environment variables](#Environment-variables) + - [Swarm command line arguments](#Swarm-command-line-arguments) +- [Developers Guide](#Developers-Guide) + - [Go Environment](#Go-Environment) + - [Vendored Dependencies](#Vendored-Dependencies) + - [Testing](#Testing) + - [Profiling Swarm](#Profiling-Swarm) + - [Metrics and Instrumentation in Swarm](#Metrics-and-Instrumentation-in-Swarm) + - [Visualizing metrics](#Visualizing-metrics) +- [Public Gateways](#Public-Gateways) +- [Swarm Dapps](#Swarm-Dapps) +- [Contributing](#Contributing) +- [License](#License) ## Building the source @@ -53,15 +53,11 @@ $ go install github.com/ethersphere/swarm/cmd/swarm ## Running Swarm -Going through all the possible command line flags is out of scope here, but we've enumerated a few common parameter combos to get you up to speed quickly on how you can run your own Swarm node. - -To run Swarm you need an Ethereum account. Download and install [Geth](https://geth.ethereum.org) if you don't have it on your system. You can create a new Ethereum account by running the following command: - ```bash -$ geth account new +$ swarm ``` -You will be prompted for a password: +If you don't have an account yet, then you will be prompted to create one and secure it with a password: ``` Your new account is locked with a password. Please give a password. Do not forget this password. @@ -69,18 +65,12 @@ Passphrase: Repeat passphrase: ``` -Once you have specified the password, the output will be the Ethereum address representing that account. For example: - -``` -Address: {2f1cd699b0bf461dcfbf0098ad8f5587b038f0f1} -``` - -Using this account, connect to Swarm with +If you have multiple accounts created, then you'll have to choose one of the accounts by using the `--bzzaccount` flag. ```bash $ swarm --bzzaccount -# in our example +# example $ swarm --bzzaccount 2f1cd699b0bf461dcfbf0098ad8f5587b038f0f1 ``` From fe3b841b2e1f58ee2cebff1606082271a22dae11 Mon Sep 17 00:00:00 2001 From: Rafael Matias Date: Wed, 3 Jul 2019 18:01:40 +0200 Subject: [PATCH 10/12] cmd/swarm: rename getAccount -> getOrCreateAccount --- cmd/swarm/config.go | 7 ++-- cmd/swarm/config_test.go | 1 - cmd/swarm/main.go | 83 ++++++++++++++++++++-------------------- 3 files changed, 46 insertions(+), 45 deletions(-) diff --git a/cmd/swarm/config.go b/cmd/swarm/config.go index 713c56a644..2f3efa726e 100644 --- a/cmd/swarm/config.go +++ b/cmd/swarm/config.go @@ -17,6 +17,7 @@ package main import ( + "crypto/ecdsa" "errors" "fmt" "io" @@ -125,10 +126,10 @@ func buildConfig(ctx *cli.Context) (config *bzzapi.Config, err error) { //finally, after the configuration build phase is finished, initialize func initSwarmNode(config *bzzapi.Config, stack *node.Node, ctx *cli.Context, nodeconfig *node.Config) error { - //at this point, all vars should be set in the Config //get the account for the provided swarm account - bzzaccount, prvkey := getAccount(config.BzzAccount, ctx, stack) - config.BzzAccount = bzzaccount + var prvkey *ecdsa.PrivateKey + config.BzzAccount, prvkey = getOrCreateAccount(ctx, stack) + //config.BzzAccount = bzzaccount //set the resolved config path (geth --datadir) config.Path = expandPath(stack.InstanceDir()) //finally, initialize the configuration diff --git a/cmd/swarm/config_test.go b/cmd/swarm/config_test.go index 9a67b498df..551f96bf2d 100644 --- a/cmd/swarm/config_test.go +++ b/cmd/swarm/config_test.go @@ -118,7 +118,6 @@ func TestBzzKeyFlag(t *testing.T) { if info.BzzAccount != bzzaccount { t.Fatalf("Expected account to be %s, got %s", bzzaccount, info.BzzAccount) } - } func TestEmptyBzzAccountFlagMultipleAccounts(t *testing.T) { dir, err := ioutil.TempDir("", "bzztest") diff --git a/cmd/swarm/main.go b/cmd/swarm/main.go index c7f2025165..b57b02847c 100644 --- a/cmd/swarm/main.go +++ b/cmd/swarm/main.go @@ -352,61 +352,62 @@ func registerBzzService(bzzconfig *bzzapi.Config, stack *node.Node) { } } -//getAccount returns the bzzaddress and the private key associated to that address -func getAccount(bzzaccount string, ctx *cli.Context, stack *node.Node) (string, *ecdsa.PrivateKey) { +// getOrCreateAccount returns the address and associated private key for a bzzaccount +// If no account exists, it will create an account for you. +func getOrCreateAccount(ctx *cli.Context, stack *node.Node) (string, *ecdsa.PrivateKey) { + var bzzaddr string - // Handle bzzkeyhex + // Check if a key was provided if hexkey := ctx.GlobalString(SwarmBzzKeyHexFlag.Name); hexkey != "" { key, err := crypto.HexToECDSA(hexkey) if err != nil { utils.Fatalf("failed using %s: %v", SwarmBzzKeyHexFlag.Name, err) } - bzzaccount := crypto.PubkeyToAddress(key.PublicKey).Hex() - log.Info(fmt.Sprintf("Swarm account key loaded from %s", SwarmBzzKeyHexFlag.Name), "address", bzzaccount) - return bzzaccount, key + bzzaddr := crypto.PubkeyToAddress(key.PublicKey).Hex() + log.Info(fmt.Sprintf("Swarm account key loaded from %s", SwarmBzzKeyHexFlag.Name), "address", bzzaddr) + return bzzaddr, key } - // Handle bzzaccount am := stack.AccountManager() ks := am.Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore) - if bzzaccount == "" { - // Check the local keystore for existing accounts - accounts := ks.Accounts() - - switch l := len(accounts); l { - case 0: - // Create an account - log.Info("You don't have an account yet. Creating one...") - password := getPassPhrase("Your new account is locked with a password. Please give a password. Do not forget this password.", true, 0, utils.MakePasswordList(ctx)) - account, err := ks.NewAccount(password) - if err != nil { - utils.Fatalf("failed creating an account: %v", err) - } - bzzaccount = account.Address.Hex() - case 1: - // Use existing account - bzzaccount = accounts[0].Address.Hex() - default: - // Inform user about multiple accounts - log.Info(fmt.Sprintf("Multiple (%d) accounts were found in your keystore.", l)) - for _, a := range accounts { - log.Info(fmt.Sprintf("Account: %s", a.Address.Hex())) - } - utils.Fatalf(fmt.Sprintf("Please choose one of the accounts by running swarm with the --%s flag.", SwarmAccountFlag.Name)) + // Check if an address was provided + if bzzaddr = ctx.GlobalString(SwarmAccountFlag.Name); bzzaddr != "" { + // Try to load the arg as a hex key file. + if key, err := crypto.LoadECDSA(bzzaddr); err == nil { + bzzaddr := crypto.PubkeyToAddress(key.PublicKey).Hex() + log.Info("Swarm account key loaded", "address", bzzaddr) + return bzzaddr, key } + return bzzaddr, decryptStoreAccount(ks, bzzaddr, utils.MakePasswordList(ctx)) + } - } else { - // Try to load the arg as a hex key file. - if key, err := crypto.LoadECDSA(bzzaccount); err == nil { - bzzaccount := crypto.PubkeyToAddress(key.PublicKey).Hex() - log.Info("Swarm account key loaded", "address", bzzaccount) - return bzzaccount, key + // No address or key were provided + accounts := ks.Accounts() + + switch l := len(accounts); l { + case 0: + // Create an account + log.Info("You don't have an account yet. Creating one...") + password := getPassPhrase("Your new account is locked with a password. Please give a password. Do not forget this password.", true, 0, utils.MakePasswordList(ctx)) + account, err := ks.NewAccount(password) + if err != nil { + utils.Fatalf("failed creating an account: %v", err) + } + bzzaddr = account.Address.Hex() + case 1: + // Use existing account + bzzaddr = accounts[0].Address.Hex() + default: + // Inform user about multiple accounts + log.Info(fmt.Sprintf("Multiple (%d) accounts were found in your keystore.", l)) + for _, a := range accounts { + log.Info(fmt.Sprintf("Account: %s", a.Address.Hex())) } + utils.Fatalf(fmt.Sprintf("Please choose one of the accounts by running swarm with the --%s flag.", SwarmAccountFlag.Name)) } - // Otherwise try getting it from the keystore - return bzzaccount, decryptStoreAccount(ks, bzzaccount, utils.MakePasswordList(ctx)) + return bzzaddr, decryptStoreAccount(ks, bzzaddr, utils.MakePasswordList(ctx)) } // getPrivKey returns the private key of the specified bzzaccount @@ -428,8 +429,8 @@ func getPrivKey(ctx *cli.Context) *ecdsa.PrivateKey { } defer stack.Close() - bzzaccount, privkey := getAccount(bzzconfig.BzzAccount, ctx, stack) - bzzconfig.BzzAccount = bzzaccount + var privkey *ecdsa.PrivateKey + bzzconfig.BzzAccount, privkey = getOrCreateAccount(ctx, stack) return privkey } From da05f2e62c061675e4dc61d0d02e9a25fa8e422e Mon Sep 17 00:00:00 2001 From: Rafael Matias Date: Thu, 4 Jul 2019 09:26:23 +0200 Subject: [PATCH 11/12] cmd/swarm: remove unneeded comment --- cmd/swarm/config.go | 1 - 1 file changed, 1 deletion(-) diff --git a/cmd/swarm/config.go b/cmd/swarm/config.go index 2f3efa726e..52832d619d 100644 --- a/cmd/swarm/config.go +++ b/cmd/swarm/config.go @@ -129,7 +129,6 @@ func initSwarmNode(config *bzzapi.Config, stack *node.Node, ctx *cli.Context, no //get the account for the provided swarm account var prvkey *ecdsa.PrivateKey config.BzzAccount, prvkey = getOrCreateAccount(ctx, stack) - //config.BzzAccount = bzzaccount //set the resolved config path (geth --datadir) config.Path = expandPath(stack.InstanceDir()) //finally, initialize the configuration From ef7655d8eef07cc0fec08ad9013451fd4a2a2592 Mon Sep 17 00:00:00 2001 From: Rafael Matias Date: Thu, 4 Jul 2019 10:03:56 +0200 Subject: [PATCH 12/12] cmd/swarm: use shorter ipc path for test --- cmd/swarm/config_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/swarm/config_test.go b/cmd/swarm/config_test.go index 551f96bf2d..09d4bfff9b 100644 --- a/cmd/swarm/config_test.go +++ b/cmd/swarm/config_test.go @@ -76,7 +76,7 @@ func TestBzzKeyFlag(t *testing.T) { conf := &node.Config{ DataDir: dir, - IPCPath: fmt.Sprintf("bzzd-%s.ipc", bzzaccount), + IPCPath: "testbzzkeyflag.ipc", NoUSB: true, }