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
This commit is contained in:
Gustav Simonsson 2015-02-26 13:22:09 +01:00 committed by Felix Lange
parent e64f727529
commit bc45e5c6de
10 changed files with 83 additions and 58 deletions

View File

@ -190,6 +190,11 @@ ApplicationWindow {
} }
} }
MenuItem {
text: "Generate key"
shortcut: "Ctrl+k"
onTriggered: gui.generateKey()
}
} }
Menu { Menu {

View File

@ -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 var data string
if len(recipient) == 0 { if len(recipient) == 0 {
code, err := ethutil.Compile(d, false) 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)) 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 // functions that allow Gui to implement interface guilogger.LogSystem

View File

@ -175,6 +175,13 @@ func (gui *Gui) showWallet(context *qml.Context) (*qml.Window, error) {
func (gui *Gui) ImportKey(filePath string) { 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) { func (gui *Gui) showKeyImport(context *qml.Context) (*qml.Window, error) {
context.SetVar("lib", gui) context.SetVar("lib", gui)
component, err := gui.engine.LoadFile(gui.uiLib.AssetPath("qml/first_run.qml")) component, err := gui.engine.LoadFile(gui.uiLib.AssetPath("qml/first_run.qml"))

View File

@ -171,6 +171,7 @@ func (self *UiLib) Transact(params map[string]interface{}) (string, error) {
object := mapToTxParams(params) object := mapToTxParams(params)
return self.XEth.Transact( return self.XEth.Transact(
object["from"],
object["to"], object["to"],
object["value"], object["value"],
object["gas"], object["gas"],

View File

@ -129,6 +129,7 @@ func (tx *Transaction) sender() []byte {
return crypto.Sha3(pubkey[1:])[12:] return crypto.Sha3(pubkey[1:])[12:]
} }
// TODO: deprecate after new accounts & key stores are integrated
func (tx *Transaction) Sign(privk []byte) error { func (tx *Transaction) Sign(privk []byte) error {
sig := tx.Signature(privk) sig := tx.Signature(privk)
@ -140,6 +141,13 @@ func (tx *Transaction) Sign(privk []byte) error {
return nil 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 { func (tx *Transaction) SignECDSA(key *ecdsa.PrivateKey) error {
return tx.Sign(crypto.FromECDSA(key)) return tx.Sign(crypto.FromECDSA(key))
} }

View File

@ -8,6 +8,7 @@ import (
"strings" "strings"
"github.com/ethereum/ethash" "github.com/ethereum/ethash"
"github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/blockpool" "github.com/ethereum/go-ethereum/blockpool"
"github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
@ -117,6 +118,7 @@ type Ethereum struct {
txPool *core.TxPool txPool *core.TxPool
chainManager *core.ChainManager chainManager *core.ChainManager
blockPool *blockpool.BlockPool blockPool *blockpool.BlockPool
accountManager *accounts.AccountManager
whisper *whisper.Whisper whisper *whisper.Whisper
net *p2p.Server net *p2p.Server
@ -176,9 +178,13 @@ func New(config *Config) (*Ethereum, error) {
DataDir: config.DataDir, 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()) eth.chainManager = core.NewChainManager(db, eth.EventMux())
pow := ethash.New(eth.chainManager) pow := ethash.New(eth.chainManager)
eth.txPool = core.NewTxPool(eth.EventMux()) eth.txPool = core.NewTxPool(eth.EventMux())
eth.blockProcessor = core.NewBlockProcessor(db, pow, eth.txPool, eth.chainManager, eth.EventMux()) eth.blockProcessor = core.NewBlockProcessor(db, pow, eth.txPool, eth.chainManager, eth.EventMux())
eth.chainManager.SetProcessor(eth.blockProcessor) eth.chainManager.SetProcessor(eth.blockProcessor)
@ -215,22 +221,23 @@ func New(config *Config) (*Ethereum, error) {
return eth, nil return eth, nil
} }
func (s *Ethereum) KeyManager() *crypto.KeyManager { return s.keyManager } func (s *Ethereum) KeyManager() *crypto.KeyManager { return s.keyManager }
func (s *Ethereum) Logger() logger.LogSystem { return s.logger } func (s *Ethereum) Logger() logger.LogSystem { return s.logger }
func (s *Ethereum) Name() string { return s.net.Name } func (s *Ethereum) Name() string { return s.net.Name }
func (s *Ethereum) ChainManager() *core.ChainManager { return s.chainManager } func (s *Ethereum) AccountManager() *accounts.AccountManager { return s.accountManager }
func (s *Ethereum) BlockProcessor() *core.BlockProcessor { return s.blockProcessor } func (s *Ethereum) ChainManager() *core.ChainManager { return s.chainManager }
func (s *Ethereum) TxPool() *core.TxPool { return s.txPool } func (s *Ethereum) BlockProcessor() *core.BlockProcessor { return s.blockProcessor }
func (s *Ethereum) BlockPool() *blockpool.BlockPool { return s.blockPool } func (s *Ethereum) TxPool() *core.TxPool { return s.txPool }
func (s *Ethereum) Whisper() *whisper.Whisper { return s.whisper } func (s *Ethereum) BlockPool() *blockpool.BlockPool { return s.blockPool }
func (s *Ethereum) EventMux() *event.TypeMux { return s.eventMux } func (s *Ethereum) Whisper() *whisper.Whisper { return s.whisper }
func (s *Ethereum) Db() ethutil.Database { return s.db } func (s *Ethereum) EventMux() *event.TypeMux { return s.eventMux }
func (s *Ethereum) Miner() *miner.Miner { return s.miner } func (s *Ethereum) Db() ethutil.Database { return s.db }
func (s *Ethereum) IsListening() bool { return true } // Always listening func (s *Ethereum) Miner() *miner.Miner { return s.miner }
func (s *Ethereum) PeerCount() int { return s.net.PeerCount() } func (s *Ethereum) IsListening() bool { return true } // Always listening
func (s *Ethereum) Peers() []*p2p.Peer { return s.net.Peers() } func (s *Ethereum) PeerCount() int { return s.net.PeerCount() }
func (s *Ethereum) MaxPeers() int { return s.net.MaxPeers } func (s *Ethereum) Peers() []*p2p.Peer { return s.net.Peers() }
func (s *Ethereum) Coinbase() []byte { return nil } // TODO func (s *Ethereum) MaxPeers() int { return s.net.MaxPeers }
func (s *Ethereum) Coinbase() []byte { return nil } // TODO
// Start the ethereum // Start the ethereum
func (s *Ethereum) Start() error { func (s *Ethereum) Start() error {

View File

@ -70,8 +70,8 @@ func (self *JSEthereum) GetStateObject(addr string) otto.Value {
return self.toVal(&JSStateObject{self.XEth.State().SafeGet(addr), self}) return self.toVal(&JSStateObject{self.XEth.State().SafeGet(addr), self})
} }
func (self *JSEthereum) Transact(key, recipient, valueStr, gasStr, gasPriceStr, dataStr string) otto.Value { func (self *JSEthereum) Transact(fromStr, recipient, valueStr, gasStr, gasPriceStr, dataStr string) otto.Value {
r, err := self.XEth.Transact(recipient, valueStr, gasStr, gasPriceStr, dataStr) r, err := self.XEth.Transact(fromStr, recipient, valueStr, gasStr, gasPriceStr, dataStr)
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)

View File

@ -252,38 +252,18 @@ func (p *EthereumApi) GetBlock(args *GetBlockArgs, reply *interface{}) error {
} }
func (p *EthereumApi) Transact(args *NewTxArgs, 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() args.Gas = defaultGas.String()
} }
if len(args.GasPrice) == 0 { if ethutil.Big(args.GasPrice).Cmp(big.NewInt(0)) == 0 {
args.GasPrice = defaultGasPrice.String() args.GasPrice = defaultGasPrice.String()
} }
// TODO if no_private_key then result, _ := p.xeth().Transact(args.From, args.To, args.Value, args.Gas, args.GasPrice, args.Data)
//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)
*reply = result *reply = result
//}
return nil return nil
} }

View File

@ -24,6 +24,7 @@ func (obj *GetBlockArgs) UnmarshalJSON(b []byte) (err error) {
type NewTxArgs struct { type NewTxArgs struct {
From string `json:"from"` From string `json:"from"`
Pass string `json:"pass"`
To string `json:"to"` To string `json:"to"`
Value string `json:"value"` Value string `json:"value"`
Gas string `json:"gas"` Gas string `json:"gas"`

View File

@ -7,8 +7,8 @@ package xeth
import ( import (
"bytes" "bytes"
"encoding/json" "encoding/json"
"fmt"
"github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
@ -27,6 +27,7 @@ var pipelogger = logger.NewLogger("XETH")
type Backend interface { type Backend interface {
BlockProcessor() *core.BlockProcessor BlockProcessor() *core.BlockProcessor
ChainManager() *core.ChainManager ChainManager() *core.ChainManager
AccountManager() *accounts.AccountManager
TxPool() *core.TxPool TxPool() *core.TxPool
PeerCount() int PeerCount() int
IsListening() bool IsListening() bool
@ -42,6 +43,7 @@ type XEth struct {
eth Backend eth Backend
blockProcessor *core.BlockProcessor blockProcessor *core.BlockProcessor
chainManager *core.ChainManager chainManager *core.ChainManager
accountManager *accounts.AccountManager
state *State state *State
whisper *Whisper whisper *Whisper
miner *miner.Miner miner *miner.Miner
@ -52,6 +54,7 @@ func New(eth Backend) *XEth {
eth: eth, eth: eth,
blockProcessor: eth.BlockProcessor(), blockProcessor: eth.BlockProcessor(),
chainManager: eth.ChainManager(), chainManager: eth.ChainManager(),
accountManager: eth.AccountManager(),
whisper: NewWhisper(eth.Whisper()), whisper: NewWhisper(eth.Whisper()),
miner: eth.Miner(), miner: eth.Miner(),
} }
@ -106,7 +109,13 @@ func (self *XEth) Block(v interface{}) *Block {
} }
func (self *XEth) Accounts() []string { 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 { func (self *XEth) PeerCount() int {
@ -266,17 +275,19 @@ func (self *XEth) Call(toStr, valueStr, gasStr, gasPriceStr, dataStr string) (st
return toHex(res), nil 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 ( var (
from []byte
to []byte to []byte
value = ethutil.NewValue(valueStr) value = ethutil.NewValue(valueStr)
gas = ethutil.NewValue(gasStr) gas = ethutil.NewValue(gasStr)
price = ethutil.NewValue(gasPriceStr) price = ethutil.NewValue(gasPriceStr)
data []byte data []byte
key = self.eth.KeyManager().KeyPair()
contractCreation bool contractCreation bool
) )
from = fromHex(fromStr)
data = fromHex(codeStr) data = fromHex(codeStr)
to = fromHex(toStr) to = fromHex(toStr)
if len(to) == 0 { 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) tx = types.NewTransactionMessage(to, value.BigInt(), gas.BigInt(), price.BigInt(), data)
} }
var err error state := self.chainManager.TransState()
state := self.eth.ChainManager().TxState() nonce := state.GetNonce(from)
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())
tx.SetNonce(nonce) 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) err = self.eth.TxPool().Add(tx)
if err != nil { if err != nil {
return "", err 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) { if types.IsContractAddr(to) {
return toHex(core.AddressFromMessage(tx)), nil return toHex(core.AddressFromMessage(tx)), nil