Merge pull request #861 from obscuren/transaction_pool_fixes

core: transaction pool fixes & resending transactions
This commit is contained in:
Jeffrey Wilcke 2015-05-06 11:23:58 -07:00
commit 323216ed85
8 changed files with 133 additions and 7 deletions

@ -3,6 +3,7 @@ package main
import ( import (
"errors" "errors"
"fmt" "fmt"
"strconv"
"time" "time"
"github.com/ethereum/go-ethereum/cmd/utils" "github.com/ethereum/go-ethereum/cmd/utils"
@ -15,6 +16,7 @@ import (
"github.com/ethereum/go-ethereum/rpc" "github.com/ethereum/go-ethereum/rpc"
"github.com/ethereum/go-ethereum/xeth" "github.com/ethereum/go-ethereum/xeth"
"github.com/robertkrimen/otto" "github.com/robertkrimen/otto"
"gopkg.in/fatih/set.v0"
) )
/* /*
@ -22,6 +24,11 @@ node admin bindings
*/ */
func (js *jsre) adminBindings() { func (js *jsre) adminBindings() {
ethO, _ := js.re.Get("eth")
eth := ethO.Object()
eth.Set("pendingTransactions", js.pendingTransactions)
eth.Set("resend", js.resend)
js.re.Set("admin", struct{}{}) js.re.Set("admin", struct{}{})
t, _ := js.re.Get("admin") t, _ := js.re.Get("admin")
admin := t.Object() admin := t.Object()
@ -74,6 +81,70 @@ func (js *jsre) getBlock(call otto.FunctionCall) (*types.Block, error) {
return nil, errors.New("requires block number or block hash as argument") return nil, errors.New("requires block number or block hash as argument")
} }
func (js *jsre) pendingTransactions(call otto.FunctionCall) otto.Value {
txs := js.ethereum.TxPool().GetTransactions()
// grab the accounts from the account manager. This will help with determening which
// transactions should be returned.
accounts, err := js.ethereum.AccountManager().Accounts()
if err != nil {
fmt.Println(err)
return otto.UndefinedValue()
}
// Add the accouns to a new set
accountSet := set.New()
for _, account := range accounts {
accountSet.Add(common.BytesToAddress(account.Address))
}
//ltxs := make([]*tx, len(txs))
var ltxs []*tx
for _, tx := range txs {
// no need to check err
if from, _ := tx.From(); accountSet.Has(from) {
ltxs = append(ltxs, newTx(tx))
}
}
return js.re.ToVal(ltxs)
}
func (js *jsre) resend(call otto.FunctionCall) otto.Value {
if len(call.ArgumentList) == 0 {
fmt.Println("first argument must be a transaction")
return otto.FalseValue()
}
v, err := call.Argument(0).Export()
if err != nil {
fmt.Println(err)
return otto.FalseValue()
}
if tx, ok := v.(*tx); ok {
gl, gp := tx.GasLimit, tx.GasPrice
if len(call.ArgumentList) > 1 {
gp = call.Argument(1).String()
}
if len(call.ArgumentList) > 2 {
gl = call.Argument(2).String()
}
ret, err := js.xeth.Transact(tx.From, tx.To, tx.Nonce, tx.Value, gl, gp, tx.Data)
if err != nil {
fmt.Println(err)
return otto.FalseValue()
}
js.ethereum.TxPool().RemoveTransactions(types.Transactions{tx.tx})
return js.re.ToVal(ret)
}
fmt.Println("first argument must be a transaction")
return otto.FalseValue()
}
func (js *jsre) debugBlock(call otto.FunctionCall) otto.Value { func (js *jsre) debugBlock(call otto.FunctionCall) otto.Value {
block, err := js.getBlock(call) block, err := js.getBlock(call)
if err != nil { if err != nil {
@ -421,3 +492,35 @@ func (js *jsre) dumpBlock(call otto.FunctionCall) otto.Value {
return js.re.ToVal(dump) return js.re.ToVal(dump)
} }
// internal transaction type which will allow us to resend transactions using `eth.resend`
type tx struct {
tx *types.Transaction
To string
From string
Nonce string
Value string
Data string
GasLimit string
GasPrice string
}
func newTx(t *types.Transaction) *tx {
from, _ := t.From()
var to string
if t := t.To(); t != nil {
to = t.Hex()
}
return &tx{
tx: t,
To: to,
From: from.Hex(),
Value: t.Amount.String(),
Nonce: strconv.Itoa(int(t.Nonce())),
Data: "0x" + common.Bytes2Hex(t.Data()),
GasLimit: t.GasLimit.String(),
GasPrice: t.GasPrice().String(),
}
}

@ -40,7 +40,7 @@ type plugin struct {
func (gui *Gui) Transact(from, recipient, value, gas, gasPrice, d string) (string, error) { func (gui *Gui) Transact(from, recipient, value, gas, gasPrice, d string) (string, error) {
d = common.Bytes2Hex(utils.FormatTransactionData(d)) d = common.Bytes2Hex(utils.FormatTransactionData(d))
return gui.xeth.Transact(from, recipient, value, gas, gasPrice, d) return gui.xeth.Transact(from, recipient, "", value, gas, gasPrice, d)
} }
func (self *Gui) AddPlugin(pluginPath string) { func (self *Gui) AddPlugin(pluginPath string) {

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

@ -24,11 +24,11 @@ var HashRegContractAddress string = "0000000000000000000000000000000000000000000
func CreateContracts(xeth *xe.XEth, addr string) { func CreateContracts(xeth *xe.XEth, addr string) {
var err error var err error
URLHintContractAddress, err = xeth.Transact(addr, "", "100000000000", "1000000", "100000", ContractCodeURLhint) URLHintContractAddress, err = xeth.Transact(addr, "", "", "100000000000", "1000000", "100000", ContractCodeURLhint)
if err != nil { if err != nil {
panic(err) panic(err)
} }
HashRegContractAddress, err = xeth.Transact(addr, "", "100000000000", "1000000", "100000", ContractCodeHashReg) HashRegContractAddress, err = xeth.Transact(addr, "", "", "100000000000", "1000000", "100000", ContractCodeHashReg)
if err != nil { if err != nil {
panic(err) panic(err)
} }

@ -235,7 +235,7 @@ func (self *TxPool) RemoveTransactions(txs types.Transactions) {
defer self.mu.Unlock() defer self.mu.Unlock()
for _, tx := range txs { for _, tx := range txs {
delete(self.txs, tx.Hash()) self.removeTx(tx.Hash())
} }
} }

@ -173,7 +173,13 @@ func (api *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) err
return fmt.Errorf("Transaction not confirmed") return fmt.Errorf("Transaction not confirmed")
} }
v, err := api.xeth().Transact(args.From, args.To, args.Value.String(), args.Gas.String(), args.GasPrice.String(), args.Data) // nonce may be nil ("guess" mode)
var nonce string
if args.Nonce != nil {
nonce = args.Nonce.String()
}
v, err := api.xeth().Transact(args.From, args.To, nonce, args.Value.String(), args.Gas.String(), args.GasPrice.String(), args.Data)
if err != nil { if err != nil {
return err return err
} }

@ -157,6 +157,7 @@ func (args *GetBlockByNumberArgs) UnmarshalJSON(b []byte) (err error) {
type NewTxArgs struct { type NewTxArgs struct {
From string From string
To string To string
Nonce *big.Int
Value *big.Int Value *big.Int
Gas *big.Int Gas *big.Int
GasPrice *big.Int GasPrice *big.Int
@ -170,6 +171,7 @@ func (args *NewTxArgs) UnmarshalJSON(b []byte) (err error) {
var ext struct { var ext struct {
From string From string
To string To string
Nonce interface{}
Value interface{} Value interface{}
Gas interface{} Gas interface{}
GasPrice interface{} GasPrice interface{}
@ -200,6 +202,14 @@ func (args *NewTxArgs) UnmarshalJSON(b []byte) (err error) {
args.Data = ext.Data args.Data = ext.Data
var num *big.Int var num *big.Int
if ext.Nonce != nil {
num, err = numString(ext.Nonce)
if err != nil {
return err
}
}
args.Nonce = num
if ext.Value == nil { if ext.Value == nil {
num = big.NewInt(0) num = big.NewInt(0)
} else { } else {

@ -648,7 +648,7 @@ func (self *XEth) ConfirmTransaction(tx string) bool {
} }
func (self *XEth) Transact(fromStr, toStr, valueStr, gasStr, gasPriceStr, codeStr string) (string, error) { func (self *XEth) Transact(fromStr, toStr, nonceStr, valueStr, gasStr, gasPriceStr, codeStr string) (string, error) {
var ( var (
from = common.HexToAddress(fromStr) from = common.HexToAddress(fromStr)
to = common.HexToAddress(toStr) to = common.HexToAddress(toStr)
@ -704,7 +704,13 @@ func (self *XEth) Transact(fromStr, toStr, valueStr, gasStr, gasPriceStr, codeSt
} }
state := self.backend.ChainManager().TxState() state := self.backend.ChainManager().TxState()
nonce := state.NewNonce(from)
var nonce uint64
if len(nonceStr) != 0 {
nonce = common.Big(nonceStr).Uint64()
} else {
nonce = state.NewNonce(from)
}
tx.SetNonce(nonce) tx.SetNonce(nonce)
if err := self.sign(tx, from, false); err != nil { if err := self.sign(tx, from, false); err != nil {