diff options
author | Jeffrey Wilcke <jeffrey@ethereum.org> | 2015-03-27 04:08:15 +0800 |
---|---|---|
committer | Jeffrey Wilcke <jeffrey@ethereum.org> | 2015-03-27 04:08:15 +0800 |
commit | 829240c3252d9da09c9000e42b0686425a313e8b (patch) | |
tree | 5b5fe5aeb443a8fd8a325743490b8fb416a903d3 | |
parent | 658204bafcba6332e979aee690dc5cff6e46fb42 (diff) | |
parent | 7577d1261403dbabdb30e21415d34b4e5da466ec (diff) | |
download | dexon-829240c3252d9da09c9000e42b0686425a313e8b.tar dexon-829240c3252d9da09c9000e42b0686425a313e8b.tar.gz dexon-829240c3252d9da09c9000e42b0686425a313e8b.tar.bz2 dexon-829240c3252d9da09c9000e42b0686425a313e8b.tar.lz dexon-829240c3252d9da09c9000e42b0686425a313e8b.tar.xz dexon-829240c3252d9da09c9000e42b0686425a313e8b.tar.zst dexon-829240c3252d9da09c9000e42b0686425a313e8b.zip |
Merge pull request #550 from ethersphere/frontier/cli-key
import/export accounts
-rw-r--r-- | accounts/account_manager.go | 37 | ||||
-rw-r--r-- | blockpool/status_test.go | 83 | ||||
-rw-r--r-- | blockpool/test/util.go | 12 | ||||
-rw-r--r-- | cmd/ethereum/js.go | 6 | ||||
-rw-r--r-- | cmd/ethereum/js_test.go | 8 | ||||
-rw-r--r-- | cmd/ethereum/main.go | 203 | ||||
-rw-r--r-- | cmd/mist/bindings.go | 7 | ||||
-rw-r--r-- | cmd/mist/gui.go | 5 | ||||
-rw-r--r-- | cmd/utils/flags.go | 20 | ||||
-rw-r--r-- | common/path.go | 30 | ||||
-rw-r--r-- | common/path_test.go | 47 | ||||
-rw-r--r-- | crypto/crypto.go | 7 | ||||
-rw-r--r-- | crypto/key.go | 18 | ||||
-rw-r--r-- | jsre/jsre_test.go | 8 |
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(ð.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) |