aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJeffrey Wilcke <jeffrey@ethereum.org>2015-03-27 04:08:15 +0800
committerJeffrey Wilcke <jeffrey@ethereum.org>2015-03-27 04:08:15 +0800
commit829240c3252d9da09c9000e42b0686425a313e8b (patch)
tree5b5fe5aeb443a8fd8a325743490b8fb416a903d3
parent658204bafcba6332e979aee690dc5cff6e46fb42 (diff)
parent7577d1261403dbabdb30e21415d34b4e5da466ec (diff)
downloadgo-tangerine-829240c3252d9da09c9000e42b0686425a313e8b.tar.gz
go-tangerine-829240c3252d9da09c9000e42b0686425a313e8b.tar.zst
go-tangerine-829240c3252d9da09c9000e42b0686425a313e8b.zip
Merge pull request #550 from ethersphere/frontier/cli-key
import/export accounts
-rw-r--r--accounts/account_manager.go37
-rw-r--r--blockpool/status_test.go83
-rw-r--r--blockpool/test/util.go12
-rw-r--r--cmd/ethereum/js.go6
-rw-r--r--cmd/ethereum/js_test.go8
-rw-r--r--cmd/ethereum/main.go203
-rw-r--r--cmd/mist/bindings.go7
-rw-r--r--cmd/mist/gui.go5
-rw-r--r--cmd/utils/flags.go20
-rw-r--r--common/path.go30
-rw-r--r--common/path_test.go47
-rw-r--r--crypto/crypto.go7
-rw-r--r--crypto/key.go18
-rw-r--r--jsre/jsre_test.go8
14 files changed, 308 insertions, 183 deletions
diff --git a/accounts/account_manager.go b/accounts/account_manager.go
index 646dc8376..34a2c4891 100644
--- a/accounts/account_manager.go
+++ b/accounts/account_manager.go
@@ -36,9 +36,8 @@ import (
"bytes"
"crypto/ecdsa"
crand "crypto/rand"
- "os"
-
"errors"
+ "os"
"sync"
"time"
@@ -208,3 +207,37 @@ func zeroKey(k *ecdsa.PrivateKey) {
b[i] = 0
}
}
+
+// USE WITH CAUTION = this will save an unencrypted private key on disk
+// no cli or js interface
+func (am *Manager) Export(path string, addr []byte, keyAuth string) error {
+ key, err := am.keyStore.GetKey(addr, keyAuth)
+ if err != nil {
+ return err
+ }
+ return crypto.SaveECDSA(path, key.PrivateKey)
+}
+
+func (am *Manager) Import(path string, keyAuth string) (Account, error) {
+ privateKeyECDSA, err := crypto.LoadECDSA(path)
+ if err != nil {
+ return Account{}, err
+ }
+ key := crypto.NewKeyFromECDSA(privateKeyECDSA)
+ if err = am.keyStore.StoreKey(key, keyAuth); err != nil {
+ return Account{}, err
+ }
+ return Account{Address: key.Address}, nil
+}
+
+func (am *Manager) ImportPreSaleKey(keyJSON []byte, password string) (acc Account, err error) {
+ var key *crypto.Key
+ key, err = crypto.ImportPreSaleKey(am.keyStore, keyJSON, password)
+ if err != nil {
+ return
+ }
+ if err = am.keyStore.StoreKey(key, password); err != nil {
+ return
+ }
+ return Account{Address: key.Address}, nil
+}
diff --git a/blockpool/status_test.go b/blockpool/status_test.go
index cbaa8bb55..a87b99d7c 100644
--- a/blockpool/status_test.go
+++ b/blockpool/status_test.go
@@ -1,7 +1,7 @@
package blockpool
import (
- // "fmt"
+ "fmt"
"testing"
"time"
@@ -45,17 +45,15 @@ func getStatusValues(s *Status) []int {
func checkStatus(t *testing.T, bp *BlockPool, syncing bool, expected []int) (err error) {
s := bp.Status()
if s.Syncing != syncing {
- t.Errorf("status for Syncing incorrect. expected %v, got %v", syncing, s.Syncing)
+ err = fmt.Errorf("status for Syncing incorrect. expected %v, got %v", syncing, s.Syncing)
+ return
}
got := getStatusValues(s)
for i, v := range expected {
- if i == 0 || i == 7 {
- continue //hack
- }
err = test.CheckInt(statusFields[i], got[i], v, t)
// fmt.Printf("%v: %v (%v)\n", statusFields[i], got[i], v)
if err != nil {
- return err
+ return
}
}
return
@@ -63,6 +61,25 @@ func checkStatus(t *testing.T, bp *BlockPool, syncing bool, expected []int) (err
func TestBlockPoolStatus(t *testing.T) {
test.LogInit()
+ var err error
+ n := 3
+ for n > 0 {
+ n--
+ err = testBlockPoolStatus(t)
+ if err != nil {
+ t.Log(err)
+ continue
+ } else {
+ return
+ }
+ }
+ if err != nil {
+ t.Errorf("no pass out of 3: %v", err)
+ }
+}
+
+func testBlockPoolStatus(t *testing.T) (err error) {
+
_, blockPool, blockPoolTester := newTestBlockPool(t)
blockPoolTester.blockChain[0] = nil
blockPoolTester.initRefBlockChain(12)
@@ -70,6 +87,7 @@ func TestBlockPoolStatus(t *testing.T) {
delete(blockPoolTester.refBlockChain, 6)
blockPool.Start()
+ defer blockPool.Stop()
blockPoolTester.tds = make(map[int]int)
blockPoolTester.tds[9] = 1
blockPoolTester.tds[11] = 3
@@ -79,73 +97,67 @@ func TestBlockPoolStatus(t *testing.T) {
peer2 := blockPoolTester.newPeer("peer2", 2, 6)
peer3 := blockPoolTester.newPeer("peer3", 3, 11)
peer4 := blockPoolTester.newPeer("peer4", 1, 9)
- // peer1 := blockPoolTester.newPeer("peer1", 1, 9)
- // peer2 := blockPoolTester.newPeer("peer2", 2, 6)
- // peer3 := blockPoolTester.newPeer("peer3", 3, 11)
- // peer4 := blockPoolTester.newPeer("peer4", 1, 9)
peer2.blocksRequestsMap = peer1.blocksRequestsMap
var expected []int
- var err error
expected = []int{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
- err = checkStatus(t, blockPool, false, expected)
+ err = checkStatus(nil, blockPool, false, expected)
if err != nil {
return
}
peer1.AddPeer()
expected = []int{0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0}
- err = checkStatus(t, blockPool, true, expected)
+ err = checkStatus(nil, blockPool, true, expected)
if err != nil {
return
}
peer1.serveBlocks(8, 9)
- expected = []int{0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 0}
- // err = checkStatus(t, blockPool, true, expected)
+ expected = []int{1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 0}
+ err = checkStatus(nil, blockPool, true, expected)
if err != nil {
return
}
peer1.serveBlockHashes(9, 8, 7, 3, 2)
expected = []int{6, 5, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 0}
- // expected = []int{5, 5, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 0}
- err = checkStatus(t, blockPool, true, expected)
+ err = checkStatus(nil, blockPool, true, expected)
if err != nil {
return
}
peer1.serveBlocks(3, 7, 8)
expected = []int{6, 5, 3, 3, 0, 1, 0, 0, 1, 1, 1, 1, 0}
- err = checkStatus(t, blockPool, true, expected)
+ err = checkStatus(nil, blockPool, true, expected)
if err != nil {
return
}
peer1.serveBlocks(2, 3)
expected = []int{6, 5, 4, 4, 0, 1, 0, 0, 1, 1, 1, 1, 0}
- err = checkStatus(t, blockPool, true, expected)
+ err = checkStatus(nil, blockPool, true, expected)
if err != nil {
return
}
peer4.AddPeer()
expected = []int{6, 5, 4, 4, 0, 2, 0, 0, 2, 2, 1, 1, 0}
- err = checkStatus(t, blockPool, true, expected)
+ err = checkStatus(nil, blockPool, true, expected)
if err != nil {
return
}
peer4.sendBlockHashes(12, 11)
expected = []int{6, 5, 4, 4, 0, 2, 0, 0, 2, 2, 1, 1, 0}
- err = checkStatus(t, blockPool, true, expected)
+ err = checkStatus(nil, blockPool, true, expected)
if err != nil {
return
}
peer2.AddPeer()
expected = []int{6, 5, 4, 4, 0, 3, 0, 0, 3, 3, 1, 2, 0}
- err = checkStatus(t, blockPool, true, expected)
+ err = checkStatus(nil, blockPool, true, expected)
if err != nil {
return
}
@@ -153,76 +165,76 @@ func TestBlockPoolStatus(t *testing.T) {
peer2.serveBlocks(5, 6)
peer2.serveBlockHashes(6, 5, 4, 3, 2)
expected = []int{10, 8, 5, 5, 0, 3, 1, 0, 3, 3, 2, 2, 0}
- err = checkStatus(t, blockPool, true, expected)
+ err = checkStatus(nil, blockPool, true, expected)
if err != nil {
return
}
peer2.serveBlocks(2, 3, 4)
expected = []int{10, 8, 6, 6, 0, 3, 1, 0, 3, 3, 2, 2, 0}
- err = checkStatus(t, blockPool, true, expected)
+ err = checkStatus(nil, blockPool, true, expected)
if err != nil {
return
}
blockPool.RemovePeer("peer2")
expected = []int{10, 8, 6, 6, 0, 3, 1, 0, 3, 2, 2, 2, 0}
- err = checkStatus(t, blockPool, true, expected)
+ err = checkStatus(nil, blockPool, true, expected)
if err != nil {
return
}
peer1.serveBlockHashes(2, 1, 0)
expected = []int{11, 9, 6, 6, 0, 3, 1, 0, 3, 2, 2, 2, 0}
- err = checkStatus(t, blockPool, true, expected)
+ err = checkStatus(nil, blockPool, true, expected)
if err != nil {
return
}
peer1.serveBlocks(1, 2)
expected = []int{11, 9, 7, 7, 0, 3, 1, 0, 3, 2, 2, 2, 0}
- err = checkStatus(t, blockPool, true, expected)
+ err = checkStatus(nil, blockPool, true, expected)
if err != nil {
return
}
peer1.serveBlocks(4, 5)
expected = []int{11, 9, 8, 8, 0, 3, 1, 0, 3, 2, 2, 2, 0}
- err = checkStatus(t, blockPool, true, expected)
+ err = checkStatus(nil, blockPool, true, expected)
if err != nil {
return
}
peer3.AddPeer()
expected = []int{11, 9, 8, 8, 0, 4, 1, 0, 4, 3, 2, 3, 0}
- err = checkStatus(t, blockPool, true, expected)
+ err = checkStatus(nil, blockPool, true, expected)
if err != nil {
return
}
peer3.serveBlocks(10, 11)
expected = []int{12, 9, 9, 9, 0, 4, 1, 0, 4, 3, 3, 3, 0}
- err = checkStatus(t, blockPool, true, expected)
+ err = checkStatus(nil, blockPool, true, expected)
if err != nil {
return
}
peer3.serveBlockHashes(11, 10, 9)
expected = []int{14, 11, 9, 9, 0, 4, 1, 0, 4, 3, 3, 3, 0}
- err = checkStatus(t, blockPool, true, expected)
+ err = checkStatus(nil, blockPool, true, expected)
if err != nil {
return
}
peer4.sendBlocks(11, 12)
expected = []int{14, 11, 9, 9, 0, 4, 1, 0, 4, 3, 4, 3, 1}
- err = checkStatus(t, blockPool, true, expected)
+ err = checkStatus(nil, blockPool, true, expected)
if err != nil {
return
}
peer3.serveBlocks(9, 10)
expected = []int{14, 11, 10, 10, 0, 4, 1, 0, 4, 3, 4, 3, 1}
- err = checkStatus(t, blockPool, true, expected)
+ err = checkStatus(nil, blockPool, true, expected)
if err != nil {
return
}
@@ -231,10 +243,9 @@ func TestBlockPoolStatus(t *testing.T) {
blockPool.Wait(waitTimeout)
time.Sleep(200 * time.Millisecond)
expected = []int{14, 3, 11, 3, 8, 4, 1, 8, 4, 3, 4, 3, 1}
- err = checkStatus(t, blockPool, false, expected)
+ err = checkStatus(nil, blockPool, false, expected)
if err != nil {
return
}
-
- blockPool.Stop()
+ return nil
}
diff --git a/blockpool/test/util.go b/blockpool/test/util.go
index 0349493c3..930601278 100644
--- a/blockpool/test/util.go
+++ b/blockpool/test/util.go
@@ -10,16 +10,20 @@ import (
func CheckInt(name string, got int, expected int, t *testing.T) (err error) {
if got != expected {
- t.Errorf("status for %v incorrect. expected %v, got %v", name, expected, got)
- err = fmt.Errorf("")
+ err = fmt.Errorf("status for %v incorrect. expected %v, got %v", name, expected, got)
+ if t != nil {
+ t.Error(err)
+ }
}
return
}
func CheckDuration(name string, got time.Duration, expected time.Duration, t *testing.T) (err error) {
if got != expected {
- t.Errorf("status for %v incorrect. expected %v, got %v", name, expected, got)
- err = fmt.Errorf("")
+ err = fmt.Errorf("status for %v incorrect. expected %v, got %v", name, expected, got)
+ if t != nil {
+ t.Error(err)
+ }
}
return
}
diff --git a/cmd/ethereum/js.go b/cmd/ethereum/js.go
index 1f0033daa..8e88a1c54 100644
--- a/cmd/ethereum/js.go
+++ b/cmd/ethereum/js.go
@@ -67,14 +67,14 @@ type jsre struct {
prompter
}
-func newJSRE(ethereum *eth.Ethereum, libPath string) *jsre {
+func newJSRE(ethereum *eth.Ethereum, libPath string, interactive bool) *jsre {
js := &jsre{ethereum: ethereum, ps1: "> "}
js.xeth = xeth.New(ethereum, js)
js.re = re.New(libPath)
js.apiBindings()
js.adminBindings()
- if !liner.TerminalSupported() {
+ if !liner.TerminalSupported() || !interactive {
js.prompter = dumbterm{bufio.NewReader(os.Stdin)}
} else {
lr := liner.NewLiner()
@@ -102,7 +102,7 @@ func (js *jsre) apiBindings() {
jethObj := t.Object()
jethObj.Set("send", jeth.Send)
- err := js.re.Compile("bignum.js", re.BigNumber_JS)
+ err := js.re.Compile("bignumber.js", re.BigNumber_JS)
if err != nil {
utils.Fatalf("Error loading bignumber.js: %v", err)
}
diff --git a/cmd/ethereum/js_test.go b/cmd/ethereum/js_test.go
index a6058b318..5b962f621 100644
--- a/cmd/ethereum/js_test.go
+++ b/cmd/ethereum/js_test.go
@@ -2,6 +2,7 @@ package main
import (
"fmt"
+ "io/ioutil"
"os"
"path"
"testing"
@@ -9,7 +10,6 @@ import (
"github.com/robertkrimen/otto"
"github.com/ethereum/go-ethereum/accounts"
- "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/eth"
)
@@ -30,8 +30,8 @@ func testJEthRE(t *testing.T) (repl *jsre, ethereum *eth.Ethereum, err error) {
}
// FIXME: this does not work ATM
ks := crypto.NewKeyStorePlain("/tmp/eth/keys")
- common.WriteFile("/tmp/eth/keys/e273f01c99144c438695e10f24926dc1f9fbf62d/e273f01c99144c438695e10f24926dc1f9fbf62d",
- []byte(`{"Id":"RhRXD+fNRKS4jx+7ZfEsNA==","Address":"4nPwHJkUTEOGleEPJJJtwfn79i0=","PrivateKey":"h4ACVpe74uIvi5Cg/2tX/Yrm2xdr3J7QoMbMtNX2CNc="}`))
+ ioutil.WriteFile("/tmp/eth/keys/e273f01c99144c438695e10f24926dc1f9fbf62d/e273f01c99144c438695e10f24926dc1f9fbf62d",
+ []byte(`{"Id":"RhRXD+fNRKS4jx+7ZfEsNA==","Address":"4nPwHJkUTEOGleEPJJJtwfn79i0=","PrivateKey":"h4ACVpe74uIvi5Cg/2tX/Yrm2xdr3J7QoMbMtNX2CNc="}`), os.ModePerm)
port++
ethereum, err = eth.New(&eth.Config{
@@ -47,7 +47,7 @@ func testJEthRE(t *testing.T) (repl *jsre, ethereum *eth.Ethereum, err error) {
return
}
assetPath := path.Join(os.Getenv("GOPATH"), "src", "github.com", "ethereum", "go-ethereum", "cmd", "mist", "assets", "ext")
- repl = newJSRE(ethereum, assetPath)
+ repl = newJSRE(ethereum, assetPath, false)
return
}
diff --git a/cmd/ethereum/main.go b/cmd/ethereum/main.go
index 2f417aacb..42321e8bc 100644
--- a/cmd/ethereum/main.go
+++ b/cmd/ethereum/main.go
@@ -23,14 +23,15 @@ package main
import (
"bufio"
"fmt"
+ "io/ioutil"
"os"
"runtime"
"strconv"
- "strings"
"time"
"github.com/codegangsta/cli"
"github.com/ethereum/ethash"
+ "github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/cmd/utils"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/state"
@@ -74,10 +75,44 @@ Regular users do not need to execute it.
The output of this command is supposed to be machine-readable.
`,
},
+
+ {
+ Name: "wallet",
+ Usage: "ethereum presale wallet",
+ Subcommands: []cli.Command{
+ {
+ Action: importWallet,
+ Name: "import",
+ Usage: "import ethereum presale wallet",
+ },
+ },
+ },
{
Action: accountList,
Name: "account",
Usage: "manage accounts",
+ Description: `
+
+Manage accounts lets you create new accounts, list all existing accounts,
+import a private key into a new account.
+
+It supports interactive mode, when you are prompted for password as well as
+non-interactive mode where passwords are supplied via a given password file.
+Non-interactive mode is only meant for scripted use on test networks or known
+safe environments.
+
+Make sure you remember the password you gave when creating a new account (with
+either new or import). Without it you are not able to unlock your account.
+
+Note that exporting your key in unencrypted format is NOT supported.
+
+Keys are stored under <DATADIR>/keys.
+It is safe to transfer the entire directory or the individual keys therein
+between ethereum nodes.
+Make sure you backup your keys regularly.
+
+And finally. DO NOT FORGET YOUR PASSWORD.
+`,
Subcommands: []cli.Command{
{
Action: accountList,
@@ -88,6 +123,51 @@ The output of this command is supposed to be machine-readable.
Action: accountCreate,
Name: "new",
Usage: "create a new account",
+ Description: `
+
+ ethereum account new
+
+Creates a new account. Prints the address.
+
+The account is saved in encrypted format, you are prompted for a passphrase.
+
+You must remember this passphrase to unlock your account in the future.
+
+For non-interactive use the passphrase can be specified with the --password flag:
+
+ ethereum --password <passwordfile> account new
+
+Note, this is meant to be used for testing only, it is a bad idea to save your
+password to file or expose in any other way.
+ `,
+ },
+ {
+ Action: accountImport,
+ Name: "import",
+ Usage: "import a private key into a new account",
+ Description: `
+
+ ethereum account import <keyfile>
+
+Imports an unencrypted private key from <keyfile> and creates a new account.
+Prints the address.
+
+The keyfile is assumed to contain an unencrypted private key in canonical EC
+raw bytes format.
+
+The account is saved in encrypted format, you are prompted for a passphrase.
+
+You must remember this passphrase to unlock your account in the future.
+
+For non-interactive use the passphrase can be specified with the -password flag:
+
+ ethereum --password <passwordfile> account import <keyfile>
+
+Note:
+As you can directly copy your encrypted accounts to another ethereum instance,
+this import mechanism is not needed when you transfer an account between
+nodes.
+ `,
},
},
},
@@ -105,16 +185,18 @@ Use "ethereum dump 0" to dump the genesis block.
Name: "console",
Usage: `Ethereum Console: interactive JavaScript environment`,
Description: `
-Console is an interactive shell for the Ethereum JavaScript runtime environment which exposes a node admin interface as well as the DAPP JavaScript API.
+Console is an interactive shell for the Ethereum JavaScript runtime environment
+which exposes a node admin interface as well as the DAPP JavaScript API.
See https://github.com/ethereum/go-ethereum/wiki/Frontier-Console
`,
},
{
Action: execJSFiles,
Name: "js",
- Usage: `executes the given JavaScript files in the Ethereum Frontier JavaScript VM`,
+ Usage: `executes the given JavaScript files in the Ethereum JavaScript VM`,
Description: `
-The Ethereum JavaScript VM exposes a node admin interface as well as the DAPP JavaScript API. See https://github.com/ethereum/go-ethereum/wiki/Frontier-Console
+The Ethereum JavaScript VM exposes a node admin interface as well as the DAPP
+JavaScript API. See https://github.com/ethereum/go-ethereum/wiki/Javascipt-Console
`,
},
{
@@ -130,6 +212,7 @@ The Ethereum JavaScript VM exposes a node admin interface as well as the DAPP Ja
}
app.Flags = []cli.Flag{
utils.UnlockedAccountFlag,
+ utils.PasswordFileFlag,
utils.BootnodesFlag,
utils.DataDirFlag,
utils.JSpathFlag,
@@ -146,7 +229,6 @@ The Ethereum JavaScript VM exposes a node admin interface as well as the DAPP Ja
utils.RPCEnabledFlag,
utils.RPCListenAddrFlag,
utils.RPCPortFlag,
- utils.UnencryptedKeysFlag,
utils.VMDebugFlag,
utils.ProtocolVersionFlag,
utils.NetworkIdFlag,
@@ -194,7 +276,7 @@ func console(ctx *cli.Context) {
}
startEth(ctx, ethereum)
- repl := newJSRE(ethereum, ctx.String(utils.JSpathFlag.Name))
+ repl := newJSRE(ethereum, ctx.String(utils.JSpathFlag.Name), true)
repl.interactive()
ethereum.Stop()
@@ -209,7 +291,7 @@ func execJSFiles(ctx *cli.Context) {
}
startEth(ctx, ethereum)
- repl := newJSRE(ethereum, ctx.String(utils.JSpathFlag.Name))
+ repl := newJSRE(ethereum, ctx.String(utils.JSpathFlag.Name), false)
for _, file := range ctx.Args() {
repl.exec(file)
}
@@ -218,22 +300,36 @@ func execJSFiles(ctx *cli.Context) {
ethereum.WaitForShutdown()
}
+func unlockAccount(ctx *cli.Context, am *accounts.Manager, account string) (passphrase string) {
+ var err error
+ // Load startup keys. XXX we are going to need a different format
+ // Attempt to unlock the account
+ passphrase = getPassPhrase(ctx, "", false)
+ accbytes := common.FromHex(account)
+ if len(accbytes) == 0 {
+ utils.Fatalf("Invalid account address '%s'", account)
+ }
+ err = am.Unlock(accbytes, passphrase)
+ if err != nil {
+ utils.Fatalf("Unlock account failed '%v'", err)
+ }
+ return
+}
+
func startEth(ctx *cli.Context, eth *eth.Ethereum) {
utils.StartEthereum(eth)
+ am := eth.AccountManager()
- // Load startup keys. XXX we are going to need a different format
account := ctx.GlobalString(utils.UnlockedAccountFlag.Name)
if len(account) > 0 {
- split := strings.Split(account, ":")
- if len(split) != 2 {
- utils.Fatalf("Illegal 'unlock' format (address:password)")
- }
- am := eth.AccountManager()
- // Attempt to unlock the account
- err := am.Unlock(common.FromHex(split[0]), split[1])
- if err != nil {
- utils.Fatalf("Unlock account failed '%v'", err)
+ if account == "coinbase" {
+ accbytes, err := am.Coinbase()
+ if err != nil {
+ utils.Fatalf("no coinbase account: %v", err)
+ }
+ account = common.ToHex(accbytes)
}
+ unlockAccount(ctx, am, account)
}
// Start auxiliary services if enabled.
if ctx.GlobalBool(utils.RPCEnabledFlag.Name) {
@@ -255,30 +351,77 @@ func accountList(ctx *cli.Context) {
}
}
-func accountCreate(ctx *cli.Context) {
- am := utils.GetAccountManager(ctx)
- passphrase := ""
- if !ctx.GlobalBool(utils.UnencryptedKeysFlag.Name) {
- fmt.Println("The new account will be encrypted with a passphrase.")
- fmt.Println("Please enter a passphrase now.")
+func getPassPhrase(ctx *cli.Context, desc string, confirmation bool) (passphrase string) {
+ passfile := ctx.GlobalString(utils.PasswordFileFlag.Name)
+ if len(passfile) == 0 {
+ fmt.Println(desc)
auth, err := readPassword("Passphrase: ", true)
if err != nil {
utils.Fatalf("%v", err)
}
- confirm, err := readPassword("Repeat Passphrase: ", false)
- if err != nil {
- utils.Fatalf("%v", err)
- }
- if auth != confirm {
- utils.Fatalf("Passphrases did not match.")
+ if confirmation {
+ confirm, err := readPassword("Repeat Passphrase: ", false)
+ if err != nil {
+ utils.Fatalf("%v", err)
+ }
+ if auth != confirm {
+ utils.Fatalf("Passphrases did not match.")
+ }
}
passphrase = auth
+
+ } else {
+ passbytes, err := ioutil.ReadFile(passfile)
+ if err != nil {
+ utils.Fatalf("Unable to read password file '%s': %v", passfile, err)
+ }
+ passphrase = string(passbytes)
}
+ return
+}
+
+func accountCreate(ctx *cli.Context) {
+ am := utils.GetAccountManager(ctx)
+ passphrase := getPassPhrase(ctx, "Your new account is locked with a password. Please give a password. Do not forget this password.", true)
acct, err := am.NewAccount(passphrase)
if err != nil {
utils.Fatalf("Could not create the account: %v", err)
}
- fmt.Printf("Address: %x\n", acct.Address)
+ fmt.Printf("Address: %x\n", acct)
+}
+
+func importWallet(ctx *cli.Context) {
+ keyfile := ctx.Args().First()
+ if len(keyfile) == 0 {
+ utils.Fatalf("keyfile must be given as argument")
+ }
+ keyJson, err := ioutil.ReadFile(keyfile)
+ if err != nil {
+ utils.Fatalf("Could not read wallet file: %v", err)
+ }
+
+ am := utils.GetAccountManager(ctx)
+ passphrase := getPassPhrase(ctx, "", false)
+
+ acct, err := am.ImportPreSaleKey(keyJson, passphrase)
+ if err != nil {
+ utils.Fatalf("Could not create the account: %v", err)
+ }
+ fmt.Printf("Address: %x\n", acct)
+}
+
+func accountImport(ctx *cli.Context) {
+ keyfile := ctx.Args().First()
+ if len(keyfile) == 0 {
+ utils.Fatalf("keyfile must be given as argument")
+ }
+ am := utils.GetAccountManager(ctx)
+ passphrase := getPassPhrase(ctx, "Your new account is locked with a password. Please give a password. Do not forget this password.", true)
+ acct, err := am.Import(keyfile, passphrase)
+ if err != nil {
+ utils.Fatalf("Could not create the account: %v", err)
+ }
+ fmt.Printf("Address: %x\n", acct)
}
func importchain(ctx *cli.Context) {
diff --git a/cmd/mist/bindings.go b/cmd/mist/bindings.go
index 8a9ec7cb1..e7ce50c35 100644
--- a/cmd/mist/bindings.go
+++ b/cmd/mist/bindings.go
@@ -22,13 +22,14 @@ package main
import (
"encoding/json"
+ "io/ioutil"
"os"
"strconv"
"github.com/ethereum/go-ethereum/cmd/utils"
"github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/state"
+ "github.com/ethereum/go-ethereum/core/types"
)
type plugin struct {
@@ -46,14 +47,14 @@ func (self *Gui) AddPlugin(pluginPath string) {
self.plugins[pluginPath] = plugin{Name: pluginPath, Path: pluginPath}
json, _ := json.MarshalIndent(self.plugins, "", " ")
- common.WriteFile(self.eth.DataDir+"/plugins.json", json)
+ ioutil.WriteFile(self.eth.DataDir+"/plugins.json", json, os.ModePerm)
}
func (self *Gui) RemovePlugin(pluginPath string) {
delete(self.plugins, pluginPath)
json, _ := json.MarshalIndent(self.plugins, "", " ")
- common.WriteFile(self.eth.DataDir+"/plugins.json", json)
+ ioutil.WriteFile(self.eth.DataDir+"/plugins.json", json, os.ModePerm)
}
func (self *Gui) DumpState(hash, path string) {
diff --git a/cmd/mist/gui.go b/cmd/mist/gui.go
index 08f02f833..d37d6f81b 100644
--- a/cmd/mist/gui.go
+++ b/cmd/mist/gui.go
@@ -25,6 +25,7 @@ import "C"
import (
"encoding/json"
"fmt"
+ "io/ioutil"
"math/big"
"path"
"runtime"
@@ -91,8 +92,8 @@ func NewWindow(ethereum *eth.Ethereum) *Gui {
plugins: make(map[string]plugin),
serviceEvents: make(chan ServEv, 1),
}
- data, _ := common.ReadAllFile(path.Join(ethereum.DataDir, "plugins.json"))
- json.Unmarshal([]byte(data), &gui.plugins)
+ data, _ := ioutil.ReadFile(path.Join(ethereum.DataDir, "plugins.json"))
+ json.Unmarshal(data, &gui.plugins)
return gui
}
diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go
index 94b043d73..f948cdb06 100644
--- a/cmd/utils/flags.go
+++ b/cmd/utils/flags.go
@@ -97,14 +97,15 @@ var (
Usage: "Enable mining",
}
- // key settings
- UnencryptedKeysFlag = cli.BoolFlag{
- Name: "unencrypted-keys",
- Usage: "disable private key disk encryption (for testing)",
- }
UnlockedAccountFlag = cli.StringFlag{
Name: "unlock",
- Usage: "Unlock a given account untill this programs exits (address:password)",
+ Usage: "unlock the account given until this program exits (prompts for password). '--unlock coinbase' unlocks the primary (coinbase) account",
+ Value: "",
+ }
+ PasswordFileFlag = cli.StringFlag{
+ Name: "password",
+ Usage: "Path to password file for (un)locking an existing account.",
+ Value: "",
}
// logging and debug settings
@@ -243,12 +244,7 @@ func GetChain(ctx *cli.Context) (*core.ChainManager, common.Database, common.Dat
func GetAccountManager(ctx *cli.Context) *accounts.Manager {
dataDir := ctx.GlobalString(DataDirFlag.Name)
- var ks crypto.KeyStore2
- if ctx.GlobalBool(UnencryptedKeysFlag.Name) {
- ks = crypto.NewKeyStorePlain(path.Join(dataDir, "plainkeys"))
- } else {
- ks = crypto.NewKeyStorePassphrase(path.Join(dataDir, "keys"))
- }
+ ks := crypto.NewKeyStorePassphrase(path.Join(dataDir, "keys"))
return accounts.NewManager(ks)
}
diff --git a/common/path.go b/common/path.go
index d38b1fd5b..a74a0d5bd 100644
--- a/common/path.go
+++ b/common/path.go
@@ -2,7 +2,6 @@ package common
import (
"fmt"
- "io/ioutil"
"os"
"os/user"
"path"
@@ -43,35 +42,6 @@ func FileExist(filePath string) bool {
return true
}
-func ReadAllFile(filePath string) (string, error) {
- file, err := os.Open(filePath)
- if err != nil {
- return "", err
- }
-
- data, err := ioutil.ReadAll(file)
- if err != nil {
- return "", err
- }
-
- return string(data), nil
-}
-
-func WriteFile(filePath string, content []byte) error {
- fh, err := os.OpenFile(filePath, os.O_TRUNC|os.O_RDWR|os.O_CREATE, os.ModePerm)
- if err != nil {
- return err
- }
- defer fh.Close()
-
- _, err = fh.Write(content)
- if err != nil {
- return err
- }
-
- return nil
-}
-
func AbsolutePath(Datadir string, filename string) string {
if path.IsAbs(filename) {
return filename
diff --git a/common/path_test.go b/common/path_test.go
index c831d1a57..4b90c543b 100644
--- a/common/path_test.go
+++ b/common/path_test.go
@@ -2,56 +2,11 @@ package common
import (
"os"
- "testing"
+ // "testing"
checker "gopkg.in/check.v1"
)
-func TestGoodFile(t *testing.T) {
- goodpath := "~/goethereumtest.pass"
- path := ExpandHomePath(goodpath)
- contentstring := "3.14159265358979323846"
-
- err := WriteFile(path, []byte(contentstring))
- if err != nil {
- t.Error("Could not write file")
- }
-
- if !FileExist(path) {
- t.Error("File not found at", path)
- }
-
- v, err := ReadAllFile(path)
- if err != nil {
- t.Error("Could not read file", path)
- }
- if v != contentstring {
- t.Error("Expected", contentstring, "Got", v)
- }
-
-}
-
-func TestBadFile(t *testing.T) {
- badpath := "/this/path/should/not/exist/goethereumtest.fail"
- path := ExpandHomePath(badpath)
- contentstring := "3.14159265358979323846"
-
- err := WriteFile(path, []byte(contentstring))
- if err == nil {
- t.Error("Wrote file, but should not be able to", path)
- }
-
- if FileExist(path) {
- t.Error("Found file, but should not be able to", path)
- }
-
- v, err := ReadAllFile(path)
- if err == nil {
- t.Error("Read file, but should not be able to", v)
- }
-
-}
-
type CommonSuite struct{}
var _ = checker.Suite(&CommonSuite{})
diff --git a/crypto/crypto.go b/crypto/crypto.go
index c3d47b629..442942c6c 100644
--- a/crypto/crypto.go
+++ b/crypto/crypto.go
@@ -9,6 +9,7 @@ import (
"crypto/sha256"
"fmt"
"io"
+ "io/ioutil"
"os"
"encoding/hex"
@@ -139,6 +140,12 @@ func LoadECDSA(file string) (*ecdsa.PrivateKey, error) {
return ToECDSA(buf), nil
}
+// SaveECDSA saves a secp256k1 private key to the given file with restrictive
+// permissions
+func SaveECDSA(file string, key *ecdsa.PrivateKey) error {
+ return ioutil.WriteFile(file, FromECDSA(key), 0600)
+}
+
func GenerateKey() (*ecdsa.PrivateKey, error) {
return ecdsa.GenerateKey(S256(), rand.Reader)
}
diff --git a/crypto/key.go b/crypto/key.go
index 9dbf37467..0b84bfec1 100644
--- a/crypto/key.go
+++ b/crypto/key.go
@@ -85,6 +85,16 @@ func (k *Key) UnmarshalJSON(j []byte) (err error) {
return err
}
+func NewKeyFromECDSA(privateKeyECDSA *ecdsa.PrivateKey) *Key {
+ id := uuid.NewRandom()
+ key := &Key{
+ Id: id,
+ Address: PubkeyToAddress(privateKeyECDSA.PublicKey),
+ PrivateKey: privateKeyECDSA,
+ }
+ return key
+}
+
func NewKey(rand io.Reader) *Key {
randBytes := make([]byte, 64)
_, err := rand.Read(randBytes)
@@ -97,11 +107,5 @@ func NewKey(rand io.Reader) *Key {
panic("key generation: ecdsa.GenerateKey failed: " + err.Error())
}
- id := uuid.NewRandom()
- key := &Key{
- Id: id,
- Address: PubkeyToAddress(privateKeyECDSA.PublicKey),
- PrivateKey: privateKeyECDSA,
- }
- return key
+ return NewKeyFromECDSA(privateKeyECDSA)
}
diff --git a/jsre/jsre_test.go b/jsre/jsre_test.go
index 8a771dae8..667ed4bdc 100644
--- a/jsre/jsre_test.go
+++ b/jsre/jsre_test.go
@@ -2,9 +2,9 @@ package jsre
import (
"github.com/robertkrimen/otto"
+ "io/ioutil"
+ "os"
"testing"
-
- "github.com/ethereum/go-ethereum/common"
)
type testNativeObjectBinding struct {
@@ -26,7 +26,7 @@ func (no *testNativeObjectBinding) TestMethod(call otto.FunctionCall) otto.Value
func TestExec(t *testing.T) {
jsre := New("/tmp")
- common.WriteFile("/tmp/test.js", []byte(`msg = "testMsg"`))
+ ioutil.WriteFile("/tmp/test.js", []byte(`msg = "testMsg"`), os.ModePerm)
err := jsre.Exec("test.js")
if err != nil {
t.Errorf("expected no error, got %v", err)
@@ -64,7 +64,7 @@ func TestBind(t *testing.T) {
func TestLoadScript(t *testing.T) {
jsre := New("/tmp")
- common.WriteFile("/tmp/test.js", []byte(`msg = "testMsg"`))
+ ioutil.WriteFile("/tmp/test.js", []byte(`msg = "testMsg"`), os.ModePerm)
_, err := jsre.Run(`loadScript("test.js")`)
if err != nil {
t.Errorf("expected no error, got %v", err)