From bc45e5c6de3052a4c853387dea0af5cd9207f1f7 Mon Sep 17 00:00:00 2001 From: Gustav Simonsson Date: Thu, 26 Feb 2015 13:22:09 +0100 Subject: Integrate eth_accounts and eth_transact to use new account manager * Add from to eth_transact / xeth.Transact and add static pass in lieu of integrating with native Mist window for user passphrase entry * Make eth_accounts return AccountManager.Accounts() * Add a Generate Key menu item in Mist --- xeth/xeth.go | 40 ++++++++++++++++++++++++++++------------ 1 file changed, 28 insertions(+), 12 deletions(-) (limited to 'xeth') diff --git a/xeth/xeth.go b/xeth/xeth.go index 677d40fd5..91bd35f8e 100644 --- a/xeth/xeth.go +++ b/xeth/xeth.go @@ -7,8 +7,8 @@ package xeth import ( "bytes" "encoding/json" - "fmt" + "github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" @@ -27,6 +27,7 @@ var pipelogger = logger.NewLogger("XETH") type Backend interface { BlockProcessor() *core.BlockProcessor ChainManager() *core.ChainManager + AccountManager() *accounts.AccountManager TxPool() *core.TxPool PeerCount() int IsListening() bool @@ -42,6 +43,7 @@ type XEth struct { eth Backend blockProcessor *core.BlockProcessor chainManager *core.ChainManager + accountManager *accounts.AccountManager state *State whisper *Whisper miner *miner.Miner @@ -52,6 +54,7 @@ func New(eth Backend) *XEth { eth: eth, blockProcessor: eth.BlockProcessor(), chainManager: eth.ChainManager(), + accountManager: eth.AccountManager(), whisper: NewWhisper(eth.Whisper()), miner: eth.Miner(), } @@ -106,7 +109,13 @@ func (self *XEth) Block(v interface{}) *Block { } func (self *XEth) Accounts() []string { - return []string{toHex(self.eth.KeyManager().Address())} + // TODO: check err? + accounts, _ := self.eth.AccountManager().Accounts() + accountAddresses := make([]string, len(accounts)) + for i, ac := range accounts { + accountAddresses[i] = toHex(ac.Address) + } + return accountAddresses } func (self *XEth) PeerCount() int { @@ -266,17 +275,19 @@ func (self *XEth) Call(toStr, valueStr, gasStr, gasPriceStr, dataStr string) (st return toHex(res), nil } -func (self *XEth) Transact(toStr, valueStr, gasStr, gasPriceStr, codeStr string) (string, error) { +func (self *XEth) Transact(fromStr, toStr, valueStr, gasStr, gasPriceStr, codeStr string) (string, error) { + var ( + from []byte to []byte value = ethutil.NewValue(valueStr) gas = ethutil.NewValue(gasStr) price = ethutil.NewValue(gasPriceStr) data []byte - key = self.eth.KeyManager().KeyPair() contractCreation bool ) + from = fromHex(fromStr) data = fromHex(codeStr) to = fromHex(toStr) if len(to) == 0 { @@ -290,21 +301,26 @@ func (self *XEth) Transact(toStr, valueStr, gasStr, gasPriceStr, codeStr string) tx = types.NewTransactionMessage(to, value.BigInt(), gas.BigInt(), price.BigInt(), data) } - var err error - state := self.eth.ChainManager().TxState() - if balance := state.GetBalance(key.Address()); balance.Cmp(tx.Value()) < 0 { - return "", fmt.Errorf("insufficient balance. balance=%v tx=%v", balance, tx.Value()) - } - nonce := state.GetNonce(key.Address()) + state := self.chainManager.TransState() + nonce := state.GetNonce(from) tx.SetNonce(nonce) - tx.Sign(key.PrivateKey) + sig, err := self.accountManager.Sign(&accounts.Account{Address: from}, tx.Hash()) + if err != nil { + return "", err + } + tx.SetSignatureValues(sig) err = self.eth.TxPool().Add(tx) if err != nil { return "", err } - state.SetNonce(key.Address(), nonce+1) + state.SetNonce(from, nonce+1) + + if contractCreation { + addr := core.AddressFromMessage(tx) + pipelogger.Infof("Contract addr %x\n", addr) + } if types.IsContractAddr(to) { return toHex(core.AddressFromMessage(tx)), nil -- cgit From d66f93cecdbae6a88bfb710e0d95d62340bf2460 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Sat, 7 Mar 2015 12:38:33 +0100 Subject: accounts, core, eth, xeth: use account manager for everything The account manager is now responsible for picking the default account and the coinbase. --- xeth/xeth.go | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) (limited to 'xeth') diff --git a/xeth/xeth.go b/xeth/xeth.go index 91bd35f8e..1ad62a7bf 100644 --- a/xeth/xeth.go +++ b/xeth/xeth.go @@ -32,7 +32,6 @@ type Backend interface { PeerCount() int IsListening() bool Peers() []*p2p.Peer - KeyManager() *crypto.KeyManager Db() ethutil.Database EventMux() *event.TypeMux Whisper() *whisper.Whisper @@ -142,7 +141,8 @@ func (self *XEth) IsListening() bool { } func (self *XEth) Coinbase() string { - return toHex(self.eth.KeyManager().Address()) + cb, _ := self.eth.AccountManager().Coinbase() + return toHex(cb) } func (self *XEth) NumberToHuman(balance string) string { @@ -251,10 +251,13 @@ func (self *XEth) Call(toStr, valueStr, gasStr, gasPriceStr, dataStr string) (st gasPriceStr = "1" } + acct, err := self.accountManager.Default() + if err != nil { + return "", err + } var ( statedb = self.State().State() //self.chainManager.TransState() - key = self.eth.KeyManager().KeyPair() - from = statedb.GetOrNewStateObject(key.Address()) + from = statedb.GetOrNewStateObject(acct.Address) block = self.chainManager.CurrentBlock() to = statedb.GetOrNewStateObject(fromHex(toStr)) data = fromHex(dataStr) @@ -264,9 +267,12 @@ func (self *XEth) Call(toStr, valueStr, gasStr, gasPriceStr, dataStr string) (st ) msg := types.NewTransactionMessage(fromHex(toStr), value, gas, price, data) - msg.Sign(key.PrivateKey) + sig, err := self.accountManager.Sign(acct, msg.Hash()) + if err != nil { + return "", err + } + msg.SetSignatureValues(sig) vmenv := core.NewEnv(statedb, self.chainManager, msg, block) - res, err := vmenv.Call(from, to.Address(), data, gas, price, value) if err != nil { return "", err -- cgit From fda7b4c79d070f1cb4f5d7ef5b4d077d9dcf2774 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Sun, 8 Mar 2015 00:18:13 +0100 Subject: accounts: use pointers consistently Account is now always a non-pointer. This will be important once the manager starts remembering accounts. AccountManager is now always a pointer because it contains locks and locks cannot be copied. --- xeth/xeth.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'xeth') diff --git a/xeth/xeth.go b/xeth/xeth.go index 1ad62a7bf..187aa8c0f 100644 --- a/xeth/xeth.go +++ b/xeth/xeth.go @@ -311,7 +311,7 @@ func (self *XEth) Transact(fromStr, toStr, valueStr, gasStr, gasPriceStr, codeSt nonce := state.GetNonce(from) tx.SetNonce(nonce) - sig, err := self.accountManager.Sign(&accounts.Account{Address: from}, tx.Hash()) + sig, err := self.accountManager.Sign(accounts.Account{Address: from}, tx.Hash()) if err != nil { return "", err } -- cgit From fb53a9362e1238d8edb466d77427dc3cbb13eb20 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Sun, 8 Mar 2015 01:52:49 +0100 Subject: accounts: AccountManager -> Manager --- xeth/xeth.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'xeth') diff --git a/xeth/xeth.go b/xeth/xeth.go index 187aa8c0f..afe680f34 100644 --- a/xeth/xeth.go +++ b/xeth/xeth.go @@ -27,7 +27,7 @@ var pipelogger = logger.NewLogger("XETH") type Backend interface { BlockProcessor() *core.BlockProcessor ChainManager() *core.ChainManager - AccountManager() *accounts.AccountManager + AccountManager() *accounts.Manager TxPool() *core.TxPool PeerCount() int IsListening() bool @@ -42,7 +42,7 @@ type XEth struct { eth Backend blockProcessor *core.BlockProcessor chainManager *core.ChainManager - accountManager *accounts.AccountManager + accountManager *accounts.Manager state *State whisper *Whisper miner *miner.Miner -- cgit From b4fa94c4b1459e71d4f11a3178cf56edf2b4aed3 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Mon, 9 Mar 2015 18:04:40 +0100 Subject: xeth: don't sign transactions for tx call This should make calls faster and removes interaction with account manager. --- xeth/xeth.go | 57 +++++++++++++++++++++++++++++++-------------------------- 1 file changed, 31 insertions(+), 26 deletions(-) (limited to 'xeth') diff --git a/xeth/xeth.go b/xeth/xeth.go index afe680f34..956b8cd01 100644 --- a/xeth/xeth.go +++ b/xeth/xeth.go @@ -7,6 +7,7 @@ package xeth import ( "bytes" "encoding/json" + "math/big" "github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/core" @@ -243,7 +244,7 @@ func (self *XEth) PushTx(encodedTx string) (string, error) { return toHex(tx.Hash()), nil } -func (self *XEth) Call(toStr, valueStr, gasStr, gasPriceStr, dataStr string) (string, error) { +func (self *XEth) Call(fromStr, toStr, valueStr, gasStr, gasPriceStr, dataStr string) (string, error) { if len(gasStr) == 0 { gasStr = "100000" } @@ -251,34 +252,20 @@ func (self *XEth) Call(toStr, valueStr, gasStr, gasPriceStr, dataStr string) (st gasPriceStr = "1" } - acct, err := self.accountManager.Default() - if err != nil { - return "", err - } - var ( - statedb = self.State().State() //self.chainManager.TransState() - from = statedb.GetOrNewStateObject(acct.Address) - block = self.chainManager.CurrentBlock() - to = statedb.GetOrNewStateObject(fromHex(toStr)) - data = fromHex(dataStr) - gas = ethutil.Big(gasStr) - price = ethutil.Big(gasPriceStr) - value = ethutil.Big(valueStr) - ) - - msg := types.NewTransactionMessage(fromHex(toStr), value, gas, price, data) - sig, err := self.accountManager.Sign(acct, msg.Hash()) - if err != nil { - return "", err + statedb := self.State().State() //self.chainManager.TransState() + msg := callmsg{ + from: statedb.GetOrNewStateObject(fromHex(fromStr)), + to: fromHex(toStr), + gas: ethutil.Big(gasStr), + gasPrice: ethutil.Big(gasPriceStr), + value: ethutil.Big(valueStr), + data: fromHex(dataStr), } - msg.SetSignatureValues(sig) + block := self.chainManager.CurrentBlock() vmenv := core.NewEnv(statedb, self.chainManager, msg, block) - res, err := vmenv.Call(from, to.Address(), data, gas, price, value) - if err != nil { - return "", err - } - return toHex(res), nil + res, err := vmenv.Call(msg.from, msg.to, msg.data, msg.gas, msg.gasPrice, msg.value) + return toHex(res), err } func (self *XEth) Transact(fromStr, toStr, valueStr, gasStr, gasPriceStr, codeStr string) (string, error) { @@ -334,3 +321,21 @@ func (self *XEth) Transact(fromStr, toStr, valueStr, gasStr, gasPriceStr, codeSt return toHex(tx.Hash()), nil } + +// callmsg is the message type used for call transations. +type callmsg struct { + from *state.StateObject + to []byte + gas, gasPrice *big.Int + value *big.Int + data []byte +} + +// accessor boilerplate to implement core.Message +func (m callmsg) From() []byte { return m.from.Address() } +func (m callmsg) Nonce() uint64 { return m.from.Nonce() } +func (m callmsg) To() []byte { return m.to } +func (m callmsg) GasPrice() *big.Int { return m.gasPrice } +func (m callmsg) Gas() *big.Int { return m.gas } +func (m callmsg) Value() *big.Int { return m.value } +func (m callmsg) Data() []byte { return m.data } -- cgit From 395da0e7c1accfaeda6527a473e9a31b11fe88a8 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Tue, 10 Mar 2015 02:00:06 +0100 Subject: xeth: use Frontend interface to unlock accounts The interface has moved to package xeth because that's where it is actually used. --- xeth/xeth.go | 78 ++++++++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 55 insertions(+), 23 deletions(-) (limited to 'xeth') diff --git a/xeth/xeth.go b/xeth/xeth.go index a0491506b..f8b537321 100644 --- a/xeth/xeth.go +++ b/xeth/xeth.go @@ -1,12 +1,10 @@ +// eXtended ETHereum package xeth -/* - * eXtended ETHereum - */ - import ( "bytes" "encoding/json" + "fmt" "math/big" "github.com/ethereum/go-ethereum/accounts" @@ -19,7 +17,6 @@ import ( "github.com/ethereum/go-ethereum/miner" "github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/state" - "github.com/ethereum/go-ethereum/ui" "github.com/ethereum/go-ethereum/whisper" ) @@ -41,6 +38,26 @@ type Backend interface { Miner() *miner.Miner } +// Frontend should be implemented by users of XEth. Its methods are +// called whenever XEth makes a decision that requires user input. +type Frontend interface { + // UnlockAccount is called when a transaction needs to be signed + // but the key corresponding to the transaction's sender is + // locked. + // + // It should unlock the account with the given address and return + // true if unlocking succeeded. + UnlockAccount(address []byte) bool + + // This is called for all transactions inititated through + // Transact. It should prompt the user to confirm the transaction + // and return true if the transaction was acknowledged. + // + // ConfirmTransaction is not used for Call transactions + // because they cannot change any state. + ConfirmTransaction(tx *types.Transaction) bool +} + type XEth struct { eth Backend blockProcessor *core.BlockProcessor @@ -50,15 +67,20 @@ type XEth struct { whisper *Whisper miner *miner.Miner - frontend ui.Interface + frontend Frontend } -type TmpFrontend struct{} +// dummyFrontend is a non-interactive frontend that allows all +// transactions but cannot not unlock any keys. +type dummyFrontend struct{} -func (TmpFrontend) UnlockAccount([]byte) bool { panic("UNLOCK ACCOUNT") } -func (TmpFrontend) ConfirmTransaction(*types.Transaction) bool { panic("CONFIRM TRANSACTION") } +func (dummyFrontend) UnlockAccount([]byte) bool { return false } +func (dummyFrontend) ConfirmTransaction(*types.Transaction) bool { return true } -func New(eth Backend, frontend ui.Interface) *XEth { +// New creates an XEth that uses the given frontend. +// If a nil Frontend is provided, a default frontend which +// confirms all transactions will be used. +func New(eth Backend, frontend Frontend) *XEth { xeth := &XEth{ eth: eth, blockProcessor: eth.BlockProcessor(), @@ -66,14 +88,12 @@ func New(eth Backend, frontend ui.Interface) *XEth { accountManager: eth.AccountManager(), whisper: NewWhisper(eth.Whisper()), miner: eth.Miner(), + frontend: frontend, } - if frontend == nil { - xeth.frontend = TmpFrontend{} + xeth.frontend = dummyFrontend{} } - xeth.state = NewState(xeth, xeth.chainManager.TransState()) - return xeth } @@ -283,7 +303,6 @@ func (self *XEth) Call(fromStr, toStr, valueStr, gasStr, gasPriceStr, dataStr st } func (self *XEth) Transact(fromStr, toStr, valueStr, gasStr, gasPriceStr, codeStr string) (string, error) { - var ( from []byte to []byte @@ -310,16 +329,12 @@ func (self *XEth) Transact(fromStr, toStr, valueStr, gasStr, gasPriceStr, codeSt state := self.chainManager.TransState() nonce := state.GetNonce(from) - tx.SetNonce(nonce) - sig, err := self.accountManager.Sign(accounts.Account{Address: from}, tx.Hash()) - if err != nil { + + if err := self.sign(tx, from, false); err != nil { return "", err } - tx.SetSignatureValues(sig) - - err = self.eth.TxPool().Add(tx) - if err != nil { + if err := self.eth.TxPool().Add(tx); err != nil { return "", err } state.SetNonce(from, nonce+1) @@ -332,10 +347,27 @@ func (self *XEth) Transact(fromStr, toStr, valueStr, gasStr, gasPriceStr, codeSt if types.IsContractAddr(to) { return toHex(core.AddressFromMessage(tx)), nil } - return toHex(tx.Hash()), nil } +func (self *XEth) sign(tx *types.Transaction, from []byte, didUnlock bool) error { + sig, err := self.accountManager.Sign(accounts.Account{Address: from}, tx.Hash()) + if err == accounts.ErrLocked { + if didUnlock { + return fmt.Errorf("sender account still locked after successful unlock") + } + if !self.frontend.UnlockAccount(from) { + return fmt.Errorf("could not unlock sender account") + } + // retry signing, the account should now be unlocked. + self.sign(tx, from, true) + } else if err != nil { + return err + } + tx.SetSignatureValues(sig) + return nil +} + // callmsg is the message type used for call transations. type callmsg struct { from *state.StateObject -- cgit From 0f67f1e9de856404136dcdce78fc0ab93bab6a84 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Tue, 10 Mar 2015 14:55:00 +0100 Subject: xeth: fix signing transaction after unlock --- xeth/xeth.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'xeth') diff --git a/xeth/xeth.go b/xeth/xeth.go index f8b537321..60262bf17 100644 --- a/xeth/xeth.go +++ b/xeth/xeth.go @@ -360,7 +360,7 @@ func (self *XEth) sign(tx *types.Transaction, from []byte, didUnlock bool) error return fmt.Errorf("could not unlock sender account") } // retry signing, the account should now be unlocked. - self.sign(tx, from, true) + return self.sign(tx, from, true) } else if err != nil { return err } -- cgit