eth: add new RPC method (personal.) SignAndSendTransaction

This commit is contained in:
Bas van Kervel 2016-05-12 19:32:04 +02:00
parent e798e4fd75
commit 64a6c2c1b6
6 changed files with 165 additions and 71 deletions

@ -147,9 +147,21 @@ func (am *Manager) Sign(addr common.Address, hash []byte) (signature []byte, err
return crypto.Sign(hash, unlockedKey.PrivateKey) return crypto.Sign(hash, unlockedKey.PrivateKey)
} }
// SignWithPassphrase signs hash if the private key matching the given address can be
// decrypted with the given passphrase.
func (am *Manager) SignWithPassphrase(addr common.Address, passphrase string, hash []byte) (signature []byte, err error) {
_, key, err := am.getDecryptedKey(Account{Address: addr}, passphrase)
if err != nil {
return nil, err
}
defer zeroKey(key.PrivateKey)
return crypto.Sign(hash, key.PrivateKey)
}
// Unlock unlocks the given account indefinitely. // Unlock unlocks the given account indefinitely.
func (am *Manager) Unlock(a Account, keyAuth string) error { func (am *Manager) Unlock(a Account, passphrase string) error {
return am.TimedUnlock(a, keyAuth, 0) return am.TimedUnlock(a, passphrase, 0)
} }
// Lock removes the private key with the given address from memory. // Lock removes the private key with the given address from memory.

@ -81,6 +81,34 @@ func TestSign(t *testing.T) {
} }
} }
func TestSignWithPassphrase(t *testing.T) {
dir, am := tmpManager(t, true)
defer os.RemoveAll(dir)
pass := "passwd"
acc, err := am.NewAccount(pass)
if err != nil {
t.Fatal(err)
}
if _, unlocked := am.unlocked[acc.Address]; unlocked {
t.Fatal("expected account to be locked")
}
_, err = am.SignWithPassphrase(acc.Address, pass, testSigData)
if err != nil {
t.Fatal(err)
}
if _, unlocked := am.unlocked[acc.Address]; unlocked {
t.Fatal("expected account to be locked")
}
if _, err = am.SignWithPassphrase(acc.Address, "invalid passwd", testSigData); err == nil {
t.Fatal("expected SignHash to fail with invalid password")
}
}
func TestTimedUnlock(t *testing.T) { func TestTimedUnlock(t *testing.T) {
dir, am := tmpManager(t, true) dir, am := tmpManager(t, true)
defer os.RemoveAll(dir) defer os.RemoveAll(dir)

@ -41,7 +41,7 @@ import (
) )
var ( var (
passwordRegexp = regexp.MustCompile("personal.[nu]") passwordRegexp = regexp.MustCompile("personal.[nus]")
onlyws = regexp.MustCompile("^\\s*$") onlyws = regexp.MustCompile("^\\s*$")
exit = regexp.MustCompile("^\\s*exit\\s*;*\\s*$") exit = regexp.MustCompile("^\\s*exit\\s*;*\\s*$")
) )

@ -148,7 +148,7 @@ func (s *PublicEthereumAPI) Etherbase() (common.Address, error) {
return s.e.Etherbase() return s.e.Etherbase()
} }
// see Etherbase // Coinbase is the address that mining rewards will be send to (alias for Etherbase)
func (s *PublicEthereumAPI) Coinbase() (common.Address, error) { func (s *PublicEthereumAPI) Coinbase() (common.Address, error) {
return s.Etherbase() return s.Etherbase()
} }
@ -217,18 +217,17 @@ func (s *PublicMinerAPI) SubmitWork(nonce rpc.HexNumber, solution, digest common
// result[0], 32 bytes hex encoded current block header pow-hash // result[0], 32 bytes hex encoded current block header pow-hash
// result[1], 32 bytes hex encoded seed hash used for DAG // result[1], 32 bytes hex encoded seed hash used for DAG
// result[2], 32 bytes hex encoded boundary condition ("target"), 2^256/difficulty // result[2], 32 bytes hex encoded boundary condition ("target"), 2^256/difficulty
func (s *PublicMinerAPI) GetWork() ([]string, error) { func (s *PublicMinerAPI) GetWork() (work [3]string, err error) {
if !s.e.IsMining() { if !s.e.IsMining() {
if err := s.e.StartMining(0, ""); err != nil { if err := s.e.StartMining(0, ""); err != nil {
return nil, err return work, err
} }
} }
if work, err := s.agent.GetWork(); err == nil { if work, err = s.agent.GetWork(); err == nil {
return work[:], nil return
} else {
glog.Infof("%v\n", err)
} }
return nil, fmt.Errorf("mining not ready") glog.V(logger.Debug).Infof("%v", err)
return work, fmt.Errorf("mining not ready")
} }
// SubmitHashrate can be used for remote miners to submit their hash rate. This enables the node to report the combined // SubmitHashrate can be used for remote miners to submit their hash rate. This enables the node to report the combined
@ -423,14 +422,23 @@ func (s *PublicAccountAPI) Accounts() []accounts.Account {
} }
// PrivateAccountAPI provides an API to access accounts managed by this node. // PrivateAccountAPI provides an API to access accounts managed by this node.
// It offers methods to create, (un)lock en list accounts. // It offers methods to create, (un)lock en list accounts. Some methods accept
// passwords and are therefore considered private by default.
type PrivateAccountAPI struct { type PrivateAccountAPI struct {
am *accounts.Manager am *accounts.Manager
txPool *core.TxPool
txMu *sync.Mutex
gpo *GasPriceOracle
} }
// NewPrivateAccountAPI create a new PrivateAccountAPI. // NewPrivateAccountAPI create a new PrivateAccountAPI.
func NewPrivateAccountAPI(am *accounts.Manager) *PrivateAccountAPI { func NewPrivateAccountAPI(e *Ethereum) *PrivateAccountAPI {
return &PrivateAccountAPI{am} return &PrivateAccountAPI{
am: e.accountManager,
txPool: e.txPool,
txMu: &e.txMu,
gpo: e.gpo,
}
} }
// ListAccounts will return a list of addresses for accounts this node manages. // ListAccounts will return a list of addresses for accounts this node manages.
@ -452,6 +460,8 @@ func (s *PrivateAccountAPI) NewAccount(password string) (common.Address, error)
return common.Address{}, err return common.Address{}, err
} }
// ImportRawKey stores the given hex encoded ECDSA key into the key directory,
// encrypting it with the passphrase.
func (s *PrivateAccountAPI) ImportRawKey(privkey string, password string) (common.Address, error) { func (s *PrivateAccountAPI) ImportRawKey(privkey string, password string) (common.Address, error) {
hexkey, err := hex.DecodeString(privkey) hexkey, err := hex.DecodeString(privkey)
if err != nil { if err != nil {
@ -482,6 +492,34 @@ func (s *PrivateAccountAPI) LockAccount(addr common.Address) bool {
return s.am.Lock(addr) == nil return s.am.Lock(addr) == nil
} }
// SignAndSendTransaction will create a transaction from the given arguments and
// tries to sign it with the key associated with args.To. If the given passwd isn't
// able to decrypt the key it fails.
func (s *PrivateAccountAPI) SignAndSendTransaction(args SendTxArgs, passwd string) (common.Hash, error) {
args = prepareSendTxArgs(args, s.gpo)
s.txMu.Lock()
defer s.txMu.Unlock()
if args.Nonce == nil {
args.Nonce = rpc.NewHexNumber(s.txPool.State().GetNonce(args.From))
}
var tx *types.Transaction
if args.To == nil {
tx = types.NewContractCreation(args.Nonce.Uint64(), args.Value.BigInt(), args.Gas.BigInt(), args.GasPrice.BigInt(), common.FromHex(args.Data))
} else {
tx = types.NewTransaction(args.Nonce.Uint64(), *args.To, args.Value.BigInt(), args.Gas.BigInt(), args.GasPrice.BigInt(), common.FromHex(args.Data))
}
signature, err := s.am.SignWithPassphrase(args.From, passwd, tx.SigHash().Bytes())
if err != nil {
return common.Hash{}, err
}
return submitTransaction(s.txPool, tx, signature)
}
// PublicBlockChainAPI provides an API to access the Ethereum blockchain. // PublicBlockChainAPI provides an API to access the Ethereum blockchain.
// It offers only methods that operate on public data that is freely available to anyone. // It offers only methods that operate on public data that is freely available to anyone.
type PublicBlockChainAPI struct { type PublicBlockChainAPI struct {
@ -645,15 +683,14 @@ func (s *PublicBlockChainAPI) NewBlocks(ctx context.Context, args NewBlocksArgs)
// add a callback that is called on chain events which will format the block and notify the client // add a callback that is called on chain events which will format the block and notify the client
s.muNewBlockSubscriptions.Lock() s.muNewBlockSubscriptions.Lock()
s.newBlockSubscriptions[subscription.ID()] = func(e core.ChainEvent) error { s.newBlockSubscriptions[subscription.ID()] = func(e core.ChainEvent) error {
if notification, err := s.rpcOutputBlock(e.Block, args.IncludeTransactions, args.TransactionDetails); err == nil { notification, err := s.rpcOutputBlock(e.Block, args.IncludeTransactions, args.TransactionDetails)
if err == nil {
return subscription.Notify(notification) return subscription.Notify(notification)
} else {
glog.V(logger.Warn).Info("unable to format block %v\n", err)
} }
glog.V(logger.Warn).Info("unable to format block %v\n", err)
return nil return nil
} }
s.muNewBlockSubscriptions.Unlock() s.muNewBlockSubscriptions.Unlock()
return subscription, nil return subscription, nil
} }
@ -700,6 +737,7 @@ func (m callmsg) Gas() *big.Int { return m.gas }
func (m callmsg) Value() *big.Int { return m.value } func (m callmsg) Value() *big.Int { return m.value }
func (m callmsg) Data() []byte { return m.data } func (m callmsg) Data() []byte { return m.data }
// CallArgs represents the arguments for a call.
type CallArgs struct { type CallArgs struct {
From common.Address `json:"from"` From common.Address `json:"from"`
To *common.Address `json:"to"` To *common.Address `json:"to"`
@ -911,7 +949,7 @@ type PublicTransactionPoolAPI struct {
miner *miner.Miner miner *miner.Miner
am *accounts.Manager am *accounts.Manager
txPool *core.TxPool txPool *core.TxPool
txMu sync.Mutex txMu *sync.Mutex
muPendingTxSubs sync.Mutex muPendingTxSubs sync.Mutex
pendingTxSubs map[string]rpc.Subscription pendingTxSubs map[string]rpc.Subscription
} }
@ -925,6 +963,7 @@ func NewPublicTransactionPoolAPI(e *Ethereum) *PublicTransactionPoolAPI {
bc: e.blockchain, bc: e.blockchain,
am: e.accountManager, am: e.accountManager,
txPool: e.txPool, txPool: e.txPool,
txMu: &e.txMu,
miner: e.miner, miner: e.miner,
pendingTxSubs: make(map[string]rpc.Subscription), pendingTxSubs: make(map[string]rpc.Subscription),
} }
@ -1124,6 +1163,7 @@ func (s *PublicTransactionPoolAPI) sign(addr common.Address, tx *types.Transacti
return tx.WithSignature(signature) return tx.WithSignature(signature)
} }
// SendTxArgs represents the arguments to sumbit a new transaction into the transaction pool.
type SendTxArgs struct { type SendTxArgs struct {
From common.Address `json:"from"` From common.Address `json:"from"`
To *common.Address `json:"to"` To *common.Address `json:"to"`
@ -1134,18 +1174,47 @@ type SendTxArgs struct {
Nonce *rpc.HexNumber `json:"nonce"` Nonce *rpc.HexNumber `json:"nonce"`
} }
// SendTransaction will create a transaction for the given transaction argument, sign it and submit it to the // prepareSendTxArgs is a helper function that fills in default values for unspecified tx fields.
// transaction pool. func prepareSendTxArgs(args SendTxArgs, gpo *GasPriceOracle) SendTxArgs {
func (s *PublicTransactionPoolAPI) SendTransaction(args SendTxArgs) (common.Hash, error) {
if args.Gas == nil { if args.Gas == nil {
args.Gas = rpc.NewHexNumber(defaultGas) args.Gas = rpc.NewHexNumber(defaultGas)
} }
if args.GasPrice == nil { if args.GasPrice == nil {
args.GasPrice = rpc.NewHexNumber(s.gpo.SuggestPrice()) args.GasPrice = rpc.NewHexNumber(gpo.SuggestPrice())
} }
if args.Value == nil { if args.Value == nil {
args.Value = rpc.NewHexNumber(0) args.Value = rpc.NewHexNumber(0)
} }
return args
}
// submitTransaction is a helper function that submits tx to txPool and creates a log entry.
func submitTransaction(txPool *core.TxPool, tx *types.Transaction, signature []byte) (common.Hash, error) {
signedTx, err := tx.WithSignature(signature)
if err != nil {
return common.Hash{}, err
}
txPool.SetLocal(signedTx)
if err := txPool.Add(signedTx); err != nil {
return common.Hash{}, err
}
if signedTx.To() == nil {
from, _ := signedTx.From()
addr := crypto.CreateAddress(from, signedTx.Nonce())
glog.V(logger.Info).Infof("Tx(%s) created: %s\n", signedTx.Hash().Hex(), addr.Hex())
} else {
glog.V(logger.Info).Infof("Tx(%s) to: %s\n", signedTx.Hash().Hex(), tx.To().Hex())
}
return signedTx.Hash(), nil
}
// SendTransaction creates a transaction for the given argument, sign it and submit it to the
// transaction pool.
func (s *PublicTransactionPoolAPI) SendTransaction(args SendTxArgs) (common.Hash, error) {
args = prepareSendTxArgs(args, s.gpo)
s.txMu.Lock() s.txMu.Lock()
defer s.txMu.Unlock() defer s.txMu.Unlock()
@ -1155,32 +1224,18 @@ func (s *PublicTransactionPoolAPI) SendTransaction(args SendTxArgs) (common.Hash
} }
var tx *types.Transaction var tx *types.Transaction
contractCreation := (args.To == nil) if args.To == nil {
if contractCreation {
tx = types.NewContractCreation(args.Nonce.Uint64(), args.Value.BigInt(), args.Gas.BigInt(), args.GasPrice.BigInt(), common.FromHex(args.Data)) tx = types.NewContractCreation(args.Nonce.Uint64(), args.Value.BigInt(), args.Gas.BigInt(), args.GasPrice.BigInt(), common.FromHex(args.Data))
} else { } else {
tx = types.NewTransaction(args.Nonce.Uint64(), *args.To, args.Value.BigInt(), args.Gas.BigInt(), args.GasPrice.BigInt(), common.FromHex(args.Data)) tx = types.NewTransaction(args.Nonce.Uint64(), *args.To, args.Value.BigInt(), args.Gas.BigInt(), args.GasPrice.BigInt(), common.FromHex(args.Data))
} }
signedTx, err := s.sign(args.From, tx) signature, err := s.am.Sign(args.From, tx.SigHash().Bytes())
if err != nil { if err != nil {
return common.Hash{}, err return common.Hash{}, err
} }
s.txPool.SetLocal(signedTx) return submitTransaction(s.txPool, tx, signature)
if err := s.txPool.Add(signedTx); err != nil {
return common.Hash{}, err
}
if contractCreation {
addr := crypto.CreateAddress(args.From, args.Nonce.Uint64())
glog.V(logger.Info).Infof("Tx(%s) created: %s\n", signedTx.Hash().Hex(), addr.Hex())
} else {
glog.V(logger.Info).Infof("Tx(%s) to: %s\n", signedTx.Hash().Hex(), tx.To().Hex())
}
return signedTx.Hash(), nil
} }
// SendRawTransaction will add the signed transaction to the transaction pool. // SendRawTransaction will add the signed transaction to the transaction pool.
@ -1217,6 +1272,7 @@ func (s *PublicTransactionPoolAPI) Sign(addr common.Address, hash common.Hash) (
return common.ToHex(signature), error return common.ToHex(signature), error
} }
// SignTransactionArgs represents the arguments to sign a transaction.
type SignTransactionArgs struct { type SignTransactionArgs struct {
From common.Address From common.Address
To *common.Address To *common.Address
@ -1243,6 +1299,7 @@ type Tx struct {
Hash common.Hash `json:"hash"` Hash common.Hash `json:"hash"`
} }
// UnmarshalJSON parses JSON data into tx.
func (tx *Tx) UnmarshalJSON(b []byte) (err error) { func (tx *Tx) UnmarshalJSON(b []byte) (err error) {
req := struct { req := struct {
To *common.Address `json:"to"` To *common.Address `json:"to"`
@ -1283,8 +1340,7 @@ func (tx *Tx) UnmarshalJSON(b []byte) (err error) {
tx.GasPrice = rpc.NewHexNumber(int64(50000000000)) tx.GasPrice = rpc.NewHexNumber(int64(50000000000))
} }
contractCreation := (req.To == nil) if req.To == nil {
if contractCreation {
tx.tx = types.NewContractCreation(tx.Nonce.Uint64(), tx.Value.BigInt(), tx.GasLimit.BigInt(), tx.GasPrice.BigInt(), data) tx.tx = types.NewContractCreation(tx.Nonce.Uint64(), tx.Value.BigInt(), tx.GasLimit.BigInt(), tx.GasPrice.BigInt(), data)
} else { } else {
tx.tx = types.NewTransaction(tx.Nonce.Uint64(), *tx.To, tx.Value.BigInt(), tx.GasLimit.BigInt(), tx.GasPrice.BigInt(), data) tx.tx = types.NewTransaction(tx.Nonce.Uint64(), *tx.To, tx.Value.BigInt(), tx.GasLimit.BigInt(), tx.GasPrice.BigInt(), data)
@ -1293,6 +1349,7 @@ func (tx *Tx) UnmarshalJSON(b []byte) (err error) {
return nil return nil
} }
// SignTransactionResult represents a RLP encoded signed transaction.
type SignTransactionResult struct { type SignTransactionResult struct {
Raw string `json:"raw"` Raw string `json:"raw"`
Tx *Tx `json:"tx"` Tx *Tx `json:"tx"`
@ -1335,9 +1392,7 @@ func (s *PublicTransactionPoolAPI) SignTransaction(args SignTransactionArgs) (*S
} }
var tx *types.Transaction var tx *types.Transaction
contractCreation := (args.To == nil) if args.To == nil {
if contractCreation {
tx = types.NewContractCreation(args.Nonce.Uint64(), args.Value.BigInt(), args.Gas.BigInt(), args.GasPrice.BigInt(), common.FromHex(args.Data)) tx = types.NewContractCreation(args.Nonce.Uint64(), args.Value.BigInt(), args.Gas.BigInt(), args.GasPrice.BigInt(), common.FromHex(args.Data))
} else { } else {
tx = types.NewTransaction(args.Nonce.Uint64(), *args.To, args.Value.BigInt(), args.Gas.BigInt(), args.GasPrice.BigInt(), common.FromHex(args.Data)) tx = types.NewTransaction(args.Nonce.Uint64(), *args.To, args.Value.BigInt(), args.Gas.BigInt(), args.GasPrice.BigInt(), common.FromHex(args.Data))
@ -1360,7 +1415,7 @@ func (s *PublicTransactionPoolAPI) SignTransaction(args SignTransactionArgs) (*S
// the accounts this node manages. // the accounts this node manages.
func (s *PublicTransactionPoolAPI) PendingTransactions() []*RPCTransaction { func (s *PublicTransactionPoolAPI) PendingTransactions() []*RPCTransaction {
pending := s.txPool.GetTransactions() pending := s.txPool.GetTransactions()
transactions := make([]*RPCTransaction, 0) transactions := make([]*RPCTransaction, 0, len(pending))
for _, tx := range pending { for _, tx := range pending {
from, _ := tx.FromFrontier() from, _ := tx.FromFrontier()
if s.am.HasAddress(from) { if s.am.HasAddress(from) {
@ -1370,7 +1425,7 @@ func (s *PublicTransactionPoolAPI) PendingTransactions() []*RPCTransaction {
return transactions return transactions
} }
// NewPendingTransaction creates a subscription that is triggered each time a transaction enters the transaction pool // NewPendingTransactions creates a subscription that is triggered each time a transaction enters the transaction pool
// and is send from one of the transactions this nodes manages. // and is send from one of the transactions this nodes manages.
func (s *PublicTransactionPoolAPI) NewPendingTransactions(ctx context.Context) (rpc.Subscription, error) { func (s *PublicTransactionPoolAPI) NewPendingTransactions(ctx context.Context) (rpc.Subscription, error) {
notifier, supported := rpc.NotifierFromContext(ctx) notifier, supported := rpc.NotifierFromContext(ctx)
@ -1410,8 +1465,7 @@ func (s *PublicTransactionPoolAPI) Resend(tx Tx, gasPrice, gasLimit *rpc.HexNumb
} }
var newTx *types.Transaction var newTx *types.Transaction
contractCreation := (tx.tx.To() == nil) if tx.tx.To() == nil {
if contractCreation {
newTx = types.NewContractCreation(tx.tx.Nonce(), tx.tx.Value(), gasPrice.BigInt(), gasLimit.BigInt(), tx.tx.Data()) newTx = types.NewContractCreation(tx.tx.Nonce(), tx.tx.Value(), gasPrice.BigInt(), gasLimit.BigInt(), tx.tx.Data())
} else { } else {
newTx = types.NewTransaction(tx.tx.Nonce(), *tx.tx.To(), tx.tx.Value(), gasPrice.BigInt(), gasLimit.BigInt(), tx.tx.Data()) newTx = types.NewTransaction(tx.tx.Nonce(), *tx.tx.To(), tx.tx.Value(), gasPrice.BigInt(), gasLimit.BigInt(), tx.tx.Data())
@ -1612,7 +1666,7 @@ func (api *PrivateDebugAPI) ChaindbProperty(property string) (string, error) {
return ldb.LDB().GetProperty(property) return ldb.LDB().GetProperty(property)
} }
// BlockTraceResults is the returned value when replaying a block to check for // BlockTraceResult is the returned value when replaying a block to check for
// consensus results and full VM trace logs for all included transactions. // consensus results and full VM trace logs for all included transactions.
type BlockTraceResult struct { type BlockTraceResult struct {
Validated bool `json:"validated"` Validated bool `json:"validated"`
@ -1647,7 +1701,7 @@ func (api *PrivateDebugAPI) TraceBlockFromFile(file string, config *vm.Config) B
return api.TraceBlock(blockRlp, config) return api.TraceBlock(blockRlp, config)
} }
// TraceProcessBlock processes the block by canonical block number. // TraceBlockByNumber processes the block by canonical block number.
func (api *PrivateDebugAPI) TraceBlockByNumber(number uint64, config *vm.Config) BlockTraceResult { func (api *PrivateDebugAPI) TraceBlockByNumber(number uint64, config *vm.Config) BlockTraceResult {
// Fetch the block that we aim to reprocess // Fetch the block that we aim to reprocess
block := api.eth.BlockChain().GetBlockByNumber(number) block := api.eth.BlockChain().GetBlockByNumber(number)
@ -1752,15 +1806,6 @@ type structLogRes struct {
Storage map[string]string `json:"storage"` Storage map[string]string `json:"storage"`
} }
// VmLoggerOptions are the options used for debugging transactions and capturing
// specific data.
type VmLoggerOptions struct {
DisableMemory bool // disable memory capture
DisableStack bool // disable stack capture
DisableStorage bool // disable storage capture
FullStorage bool // show full storage (slow)
}
// formatLogs formats EVM returned structured logs for json output // formatLogs formats EVM returned structured logs for json output
func formatLogs(structLogs []vm.StructLog) []structLogRes { func formatLogs(structLogs []vm.StructLog) []structLogRes {
formattedStructLogs := make([]structLogRes, len(structLogs)) formattedStructLogs := make([]structLogRes, len(structLogs))
@ -1802,25 +1847,25 @@ func formatError(err error) string {
// TraceTransaction returns the structured logs created during the execution of EVM // TraceTransaction returns the structured logs created during the execution of EVM
// and returns them as a JSON object. // and returns them as a JSON object.
func (s *PrivateDebugAPI) TraceTransaction(txHash common.Hash, logger *vm.LogConfig) (*ExecutionResult, error) { func (api *PrivateDebugAPI) TraceTransaction(txHash common.Hash, logger *vm.LogConfig) (*ExecutionResult, error) {
if logger == nil { if logger == nil {
logger = new(vm.LogConfig) logger = new(vm.LogConfig)
} }
// Retrieve the tx from the chain and the containing block // Retrieve the tx from the chain and the containing block
tx, blockHash, _, txIndex := core.GetTransaction(s.eth.ChainDb(), txHash) tx, blockHash, _, txIndex := core.GetTransaction(api.eth.ChainDb(), txHash)
if tx == nil { if tx == nil {
return nil, fmt.Errorf("transaction %x not found", txHash) return nil, fmt.Errorf("transaction %x not found", txHash)
} }
block := s.eth.BlockChain().GetBlock(blockHash) block := api.eth.BlockChain().GetBlock(blockHash)
if block == nil { if block == nil {
return nil, fmt.Errorf("block %x not found", blockHash) return nil, fmt.Errorf("block %x not found", blockHash)
} }
// Create the state database to mutate and eventually trace // Create the state database to mutate and eventually trace
parent := s.eth.BlockChain().GetBlock(block.ParentHash()) parent := api.eth.BlockChain().GetBlock(block.ParentHash())
if parent == nil { if parent == nil {
return nil, fmt.Errorf("block parent %x not found", block.ParentHash()) return nil, fmt.Errorf("block parent %x not found", block.ParentHash())
} }
stateDb, err := state.New(parent.Root(), s.eth.ChainDb()) stateDb, err := state.New(parent.Root(), api.eth.ChainDb())
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -1841,7 +1886,7 @@ func (s *PrivateDebugAPI) TraceTransaction(txHash common.Hash, logger *vm.LogCon
} }
// Mutate the state if we haven't reached the tracing transaction yet // Mutate the state if we haven't reached the tracing transaction yet
if uint64(idx) < txIndex { if uint64(idx) < txIndex {
vmenv := core.NewEnv(stateDb, s.config, s.eth.BlockChain(), msg, block.Header(), vm.Config{}) vmenv := core.NewEnv(stateDb, api.config, api.eth.BlockChain(), msg, block.Header(), vm.Config{})
_, _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas())) _, _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas()))
if err != nil { if err != nil {
return nil, fmt.Errorf("mutation failed: %v", err) return nil, fmt.Errorf("mutation failed: %v", err)
@ -1849,7 +1894,7 @@ func (s *PrivateDebugAPI) TraceTransaction(txHash common.Hash, logger *vm.LogCon
continue continue
} }
// Otherwise trace the transaction and return // Otherwise trace the transaction and return
vmenv := core.NewEnv(stateDb, s.config, s.eth.BlockChain(), msg, block.Header(), vm.Config{Debug: true, Logger: *logger}) vmenv := core.NewEnv(stateDb, api.config, api.eth.BlockChain(), msg, block.Header(), vm.Config{Debug: true, Logger: *logger})
ret, gas, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas())) ret, gas, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas()))
if err != nil { if err != nil {
return nil, fmt.Errorf("tracing failed: %v", err) return nil, fmt.Errorf("tracing failed: %v", err)
@ -1863,6 +1908,7 @@ func (s *PrivateDebugAPI) TraceTransaction(txHash common.Hash, logger *vm.LogCon
return nil, errors.New("database inconsistency") return nil, errors.New("database inconsistency")
} }
// TraceCall executes a call and returns the amount of gas, created logs and optionally returned values.
func (s *PublicBlockChainAPI) TraceCall(args CallArgs, blockNr rpc.BlockNumber) (*ExecutionResult, error) { func (s *PublicBlockChainAPI) TraceCall(args CallArgs, blockNr rpc.BlockNumber) (*ExecutionResult, error) {
// Fetch the state associated with the block number // Fetch the state associated with the block number
stateDb, block, err := stateAndBlockByNumber(s.miner, s.bc, blockNr, s.chainDb) stateDb, block, err := stateAndBlockByNumber(s.miner, s.bc, blockNr, s.chainDb)
@ -1931,12 +1977,12 @@ func (s *PublicNetAPI) Listening() bool {
return true // always listening return true // always listening
} }
// Peercount returns the number of connected peers // PeerCount returns the number of connected peers
func (s *PublicNetAPI) PeerCount() *rpc.HexNumber { func (s *PublicNetAPI) PeerCount() *rpc.HexNumber {
return rpc.NewHexNumber(s.net.PeerCount()) return rpc.NewHexNumber(s.net.PeerCount())
} }
// ProtocolVersion returns the current ethereum protocol version. // Version returns the current ethereum protocol version.
func (s *PublicNetAPI) Version() string { func (s *PublicNetAPI) Version() string {
return fmt.Sprintf("%d", s.networkVersion) return fmt.Sprintf("%d", s.networkVersion)
} }

@ -26,6 +26,7 @@ import (
"path/filepath" "path/filepath"
"regexp" "regexp"
"strings" "strings"
"sync"
"time" "time"
"github.com/ethereum/ethash" "github.com/ethereum/ethash"
@ -113,6 +114,7 @@ type Ethereum struct {
// Handlers // Handlers
txPool *core.TxPool txPool *core.TxPool
txMu sync.Mutex
blockchain *core.BlockChain blockchain *core.BlockChain
accountManager *accounts.Manager accountManager *accounts.Manager
pow *ethash.Ethash pow *ethash.Ethash
@ -293,7 +295,7 @@ func (s *Ethereum) APIs() []rpc.API {
}, { }, {
Namespace: "personal", Namespace: "personal",
Version: "1.0", Version: "1.0",
Service: NewPrivateAccountAPI(s.accountManager), Service: NewPrivateAccountAPI(s),
Public: false, Public: false,
}, { }, {
Namespace: "eth", Namespace: "eth",

@ -431,6 +431,12 @@ web3._extend({
name: 'importRawKey', name: 'importRawKey',
call: 'personal_importRawKey', call: 'personal_importRawKey',
params: 2 params: 2
}),
new web3._extend.Method({
name: 'signAndSendTransaction',
call: 'personal_signAndSendTransaction',
params: 2,
inputFormatter: [web3._extend.formatters.inputTransactionFormatter, null]
}) })
] ]
}); });