From bc45e5c6de3052a4c853387dea0af5cd9207f1f7 Mon Sep 17 00:00:00 2001 From: Gustav Simonsson Date: Thu, 26 Feb 2015 13:22:09 +0100 Subject: [PATCH] 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 --- cmd/mist/assets/qml/main.qml | 5 +++++ cmd/mist/bindings.go | 4 ++-- cmd/mist/gui.go | 7 ++++++ cmd/mist/ui_lib.go | 1 + core/types/transaction.go | 8 +++++++ eth/backend.go | 41 +++++++++++++++++++++--------------- javascript/types.go | 4 ++-- rpc/api.go | 30 +++++--------------------- rpc/args.go | 1 + xeth/xeth.go | 40 ++++++++++++++++++++++++----------- 10 files changed, 83 insertions(+), 58 deletions(-) diff --git a/cmd/mist/assets/qml/main.qml b/cmd/mist/assets/qml/main.qml index f9ee6939d..7f72d35f4 100644 --- a/cmd/mist/assets/qml/main.qml +++ b/cmd/mist/assets/qml/main.qml @@ -190,6 +190,11 @@ ApplicationWindow { } } + MenuItem { + text: "Generate key" + shortcut: "Ctrl+k" + onTriggered: gui.generateKey() + } } Menu { diff --git a/cmd/mist/bindings.go b/cmd/mist/bindings.go index f21aa3135..fd89eb7e2 100644 --- a/cmd/mist/bindings.go +++ b/cmd/mist/bindings.go @@ -49,7 +49,7 @@ func (gui *Gui) LogPrint(level logger.LogLevel, msg string) { } */ } -func (gui *Gui) Transact(recipient, value, gas, gasPrice, d string) (string, error) { +func (gui *Gui) Transact(from, recipient, value, gas, gasPrice, d string) (string, error) { var data string if len(recipient) == 0 { code, err := ethutil.Compile(d, false) @@ -61,7 +61,7 @@ func (gui *Gui) Transact(recipient, value, gas, gasPrice, d string) (string, err data = ethutil.Bytes2Hex(utils.FormatTransactionData(d)) } - return gui.xeth.Transact(recipient, value, gas, gasPrice, data) + return gui.xeth.Transact(from, recipient, value, gas, gasPrice, data) } // functions that allow Gui to implement interface guilogger.LogSystem diff --git a/cmd/mist/gui.go b/cmd/mist/gui.go index 869b689dd..bc6e9ed53 100644 --- a/cmd/mist/gui.go +++ b/cmd/mist/gui.go @@ -175,6 +175,13 @@ func (gui *Gui) showWallet(context *qml.Context) (*qml.Window, error) { func (gui *Gui) ImportKey(filePath string) { } +func (gui *Gui) GenerateKey() { + _, err := gui.eth.AccountManager().NewAccount("hurr") + if err != nil { + // TODO: UI feedback? + } +} + func (gui *Gui) showKeyImport(context *qml.Context) (*qml.Window, error) { context.SetVar("lib", gui) component, err := gui.engine.LoadFile(gui.uiLib.AssetPath("qml/first_run.qml")) diff --git a/cmd/mist/ui_lib.go b/cmd/mist/ui_lib.go index 098e8fca5..af78f0c10 100644 --- a/cmd/mist/ui_lib.go +++ b/cmd/mist/ui_lib.go @@ -171,6 +171,7 @@ func (self *UiLib) Transact(params map[string]interface{}) (string, error) { object := mapToTxParams(params) return self.XEth.Transact( + object["from"], object["to"], object["value"], object["gas"], diff --git a/core/types/transaction.go b/core/types/transaction.go index 7a1d6104e..7d34c86f4 100644 --- a/core/types/transaction.go +++ b/core/types/transaction.go @@ -129,6 +129,7 @@ func (tx *Transaction) sender() []byte { return crypto.Sha3(pubkey[1:])[12:] } +// TODO: deprecate after new accounts & key stores are integrated func (tx *Transaction) Sign(privk []byte) error { sig := tx.Signature(privk) @@ -140,6 +141,13 @@ func (tx *Transaction) Sign(privk []byte) error { return nil } +func (tx *Transaction) SetSignatureValues(sig []byte) error { + tx.R = sig[:32] + tx.S = sig[32:64] + tx.V = uint64(sig[64] + 27) + return nil +} + func (tx *Transaction) SignECDSA(key *ecdsa.PrivateKey) error { return tx.Sign(crypto.FromECDSA(key)) } diff --git a/eth/backend.go b/eth/backend.go index 1c711a775..02e7e2746 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -8,6 +8,7 @@ import ( "strings" "github.com/ethereum/ethash" + "github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/blockpool" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/crypto" @@ -117,6 +118,7 @@ type Ethereum struct { txPool *core.TxPool chainManager *core.ChainManager blockPool *blockpool.BlockPool + accountManager *accounts.AccountManager whisper *whisper.Whisper net *p2p.Server @@ -176,9 +178,13 @@ func New(config *Config) (*Ethereum, error) { DataDir: config.DataDir, } + // TODO: add config flag and case on plain/protected key store + ks := crypto.NewKeyStorePlain(ethutil.DefaultDataDir()) + am := accounts.NewAccountManager(ks, 300000) // keys unlocked for 300s + eth.accountManager = &am + eth.chainManager = core.NewChainManager(db, eth.EventMux()) pow := ethash.New(eth.chainManager) - eth.txPool = core.NewTxPool(eth.EventMux()) eth.blockProcessor = core.NewBlockProcessor(db, pow, eth.txPool, eth.chainManager, eth.EventMux()) eth.chainManager.SetProcessor(eth.blockProcessor) @@ -215,22 +221,23 @@ func New(config *Config) (*Ethereum, error) { return eth, nil } -func (s *Ethereum) KeyManager() *crypto.KeyManager { return s.keyManager } -func (s *Ethereum) Logger() logger.LogSystem { return s.logger } -func (s *Ethereum) Name() string { return s.net.Name } -func (s *Ethereum) ChainManager() *core.ChainManager { return s.chainManager } -func (s *Ethereum) BlockProcessor() *core.BlockProcessor { return s.blockProcessor } -func (s *Ethereum) TxPool() *core.TxPool { return s.txPool } -func (s *Ethereum) BlockPool() *blockpool.BlockPool { return s.blockPool } -func (s *Ethereum) Whisper() *whisper.Whisper { return s.whisper } -func (s *Ethereum) EventMux() *event.TypeMux { return s.eventMux } -func (s *Ethereum) Db() ethutil.Database { return s.db } -func (s *Ethereum) Miner() *miner.Miner { return s.miner } -func (s *Ethereum) IsListening() bool { return true } // Always listening -func (s *Ethereum) PeerCount() int { return s.net.PeerCount() } -func (s *Ethereum) Peers() []*p2p.Peer { return s.net.Peers() } -func (s *Ethereum) MaxPeers() int { return s.net.MaxPeers } -func (s *Ethereum) Coinbase() []byte { return nil } // TODO +func (s *Ethereum) KeyManager() *crypto.KeyManager { return s.keyManager } +func (s *Ethereum) Logger() logger.LogSystem { return s.logger } +func (s *Ethereum) Name() string { return s.net.Name } +func (s *Ethereum) AccountManager() *accounts.AccountManager { return s.accountManager } +func (s *Ethereum) ChainManager() *core.ChainManager { return s.chainManager } +func (s *Ethereum) BlockProcessor() *core.BlockProcessor { return s.blockProcessor } +func (s *Ethereum) TxPool() *core.TxPool { return s.txPool } +func (s *Ethereum) BlockPool() *blockpool.BlockPool { return s.blockPool } +func (s *Ethereum) Whisper() *whisper.Whisper { return s.whisper } +func (s *Ethereum) EventMux() *event.TypeMux { return s.eventMux } +func (s *Ethereum) Db() ethutil.Database { return s.db } +func (s *Ethereum) Miner() *miner.Miner { return s.miner } +func (s *Ethereum) IsListening() bool { return true } // Always listening +func (s *Ethereum) PeerCount() int { return s.net.PeerCount() } +func (s *Ethereum) Peers() []*p2p.Peer { return s.net.Peers() } +func (s *Ethereum) MaxPeers() int { return s.net.MaxPeers } +func (s *Ethereum) Coinbase() []byte { return nil } // TODO // Start the ethereum func (s *Ethereum) Start() error { diff --git a/javascript/types.go b/javascript/types.go index 77e209d19..e07267c8f 100644 --- a/javascript/types.go +++ b/javascript/types.go @@ -70,8 +70,8 @@ func (self *JSEthereum) GetStateObject(addr string) otto.Value { return self.toVal(&JSStateObject{self.XEth.State().SafeGet(addr), self}) } -func (self *JSEthereum) Transact(key, recipient, valueStr, gasStr, gasPriceStr, dataStr string) otto.Value { - r, err := self.XEth.Transact(recipient, valueStr, gasStr, gasPriceStr, dataStr) +func (self *JSEthereum) Transact(fromStr, recipient, valueStr, gasStr, gasPriceStr, dataStr string) otto.Value { + r, err := self.XEth.Transact(fromStr, recipient, valueStr, gasStr, gasPriceStr, dataStr) if err != nil { fmt.Println(err) diff --git a/rpc/api.go b/rpc/api.go index 28024c206..b622945eb 100644 --- a/rpc/api.go +++ b/rpc/api.go @@ -252,38 +252,18 @@ func (p *EthereumApi) GetBlock(args *GetBlockArgs, reply *interface{}) error { } func (p *EthereumApi) Transact(args *NewTxArgs, reply *interface{}) error { - if len(args.Gas) == 0 { + // TODO: align default values to have the same type, e.g. not depend on + // ethutil.Value conversions later on + if ethutil.Big(args.Gas).Cmp(big.NewInt(0)) == 0 { args.Gas = defaultGas.String() } - if len(args.GasPrice) == 0 { + if ethutil.Big(args.GasPrice).Cmp(big.NewInt(0)) == 0 { args.GasPrice = defaultGasPrice.String() } - // TODO if no_private_key then - //if _, exists := p.register[args.From]; exists { - // p.register[args.From] = append(p.register[args.From], args) - //} else { - /* - account := accounts.Get(fromHex(args.From)) - if account != nil { - if account.Unlocked() { - if !unlockAccount(account) { - return - } - } - - result, _ := account.Transact(fromHex(args.To), fromHex(args.Value), fromHex(args.Gas), fromHex(args.GasPrice), fromHex(args.Data)) - if len(result) > 0 { - *reply = toHex(result) - } - } else if _, exists := p.register[args.From]; exists { - p.register[ags.From] = append(p.register[args.From], args) - } - */ - result, _ := p.xeth().Transact( /* TODO specify account */ args.To, args.Value, args.Gas, args.GasPrice, args.Data) + result, _ := p.xeth().Transact(args.From, args.To, args.Value, args.Gas, args.GasPrice, args.Data) *reply = result - //} return nil } diff --git a/rpc/args.go b/rpc/args.go index ea8489585..ec3359a4a 100644 --- a/rpc/args.go +++ b/rpc/args.go @@ -24,6 +24,7 @@ func (obj *GetBlockArgs) UnmarshalJSON(b []byte) (err error) { type NewTxArgs struct { From string `json:"from"` + Pass string `json:"pass"` To string `json:"to"` Value string `json:"value"` Gas string `json:"gas"` 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