Explorar o código

Merge pull request #1328 from bas-vk/issue1327

Add pendingTransactions and resend
Jeffrey Wilcke %!s(int64=10) %!d(string=hai) anos
pai
achega
ba95e445e1
Modificáronse 5 ficheiros con 252 adicións e 8 borrados
  1. 3 2
      rpc/api/api_test.go
  2. 53 5
      rpc/api/eth.go
  3. 178 0
      rpc/api/eth_args.go
  4. 15 0
      rpc/api/eth_js.go
  5. 3 1
      rpc/api/utils.go

+ 3 - 2
rpc/api/api_test.go

@@ -76,8 +76,9 @@ func TestCompileSolidity(t *testing.T) {
 	expLanguageVersion := "0"
 	expSource := source
 
-	xeth := xeth.NewTest(&eth.Ethereum{}, nil)
-	api := NewEthApi(xeth, codec.JSON)
+	eth := &eth.Ethereum{}
+	xeth := xeth.NewTest(eth, nil)
+	api := NewEthApi(xeth, eth, codec.JSON)
 
 	var rpcRequest shared.Request
 	json.Unmarshal([]byte(jsonstr), &rpcRequest)

+ 53 - 5
rpc/api/eth.go

@@ -6,9 +6,12 @@ import (
 	"math/big"
 
 	"github.com/ethereum/go-ethereum/common"
+	"github.com/ethereum/go-ethereum/core/types"
+	"github.com/ethereum/go-ethereum/eth"
 	"github.com/ethereum/go-ethereum/rpc/codec"
 	"github.com/ethereum/go-ethereum/rpc/shared"
 	"github.com/ethereum/go-ethereum/xeth"
+	"gopkg.in/fatih/set.v0"
 )
 
 const (
@@ -18,9 +21,10 @@ const (
 // eth api provider
 // See https://github.com/ethereum/wiki/wiki/JSON-RPC
 type ethApi struct {
-	xeth    *xeth.XEth
-	methods map[string]ethhandler
-	codec   codec.ApiCoder
+	xeth     *xeth.XEth
+	ethereum *eth.Ethereum
+	methods  map[string]ethhandler
+	codec    codec.ApiCoder
 }
 
 // eth callback handler
@@ -71,12 +75,14 @@ var (
 		"eth_hashrate":                            (*ethApi).Hashrate,
 		"eth_getWork":                             (*ethApi).GetWork,
 		"eth_submitWork":                          (*ethApi).SubmitWork,
+		"eth_resend":                              (*ethApi).Resend,
+		"eth_pendingTransactions":                 (*ethApi).PendingTransactions,
 	}
 )
 
 // create new ethApi instance
-func NewEthApi(xeth *xeth.XEth, codec codec.Codec) *ethApi {
-	return &ethApi{xeth, ethMapping, codec.New(nil)}
+func NewEthApi(xeth *xeth.XEth, eth *eth.Ethereum, codec codec.Codec) *ethApi {
+	return &ethApi{xeth, eth, ethMapping, codec.New(nil)}
 }
 
 // collection with supported methods
@@ -548,3 +554,45 @@ func (self *ethApi) SubmitWork(req *shared.Request) (interface{}, error) {
 	}
 	return self.xeth.RemoteMining().SubmitWork(args.Nonce, common.HexToHash(args.Digest), common.HexToHash(args.Header)), nil
 }
+
+func (self *ethApi) Resend(req *shared.Request) (interface{}, error) {
+	args := new(ResendArgs)
+	if err := self.codec.Decode(req.Params, &args); err != nil {
+		return nil, shared.NewDecodeParamError(err.Error())
+	}
+
+	ret, err := self.xeth.Transact(args.Tx.From, args.Tx.To, args.Tx.Nonce, args.Tx.Value, args.GasLimit, args.GasPrice, args.Tx.Data)
+	if err != nil {
+		return nil, err
+	}
+
+	self.ethereum.TxPool().RemoveTransactions(types.Transactions{args.Tx.tx})
+
+	return ret, nil
+}
+
+func (self *ethApi) PendingTransactions(req *shared.Request) (interface{}, error) {
+	txs := self.ethereum.TxPool().GetTransactions()
+
+	// grab the accounts from the account manager. This will help with determining which
+	// transactions should be returned.
+	accounts, err := self.ethereum.AccountManager().Accounts()
+	if err != nil {
+		return nil, err
+	}
+
+	// Add the accouns to a new set
+	accountSet := set.New()
+	for _, account := range accounts {
+		accountSet.Add(account.Address)
+	}
+
+	var ltxs []*tx
+	for _, tx := range txs {
+		if from, _ := tx.From(); accountSet.Has(from) {
+			ltxs = append(ltxs, newTx(tx))
+		}
+	}
+
+	return ltxs, nil
+}

+ 178 - 0
rpc/api/eth_args.go

@@ -4,9 +4,12 @@ import (
 	"encoding/json"
 	"fmt"
 	"math/big"
+	"strconv"
+	"strings"
 
 	"github.com/ethereum/go-ethereum/common"
 	"github.com/ethereum/go-ethereum/core/state"
+	"github.com/ethereum/go-ethereum/core/types"
 	"github.com/ethereum/go-ethereum/rpc/shared"
 )
 
@@ -858,3 +861,178 @@ func (args *SubmitWorkArgs) UnmarshalJSON(b []byte) (err error) {
 
 	return nil
 }
+
+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.Value().String(),
+		Nonce:    strconv.Itoa(int(t.Nonce())),
+		Data:     "0x" + common.Bytes2Hex(t.Data()),
+		GasLimit: t.Gas().String(),
+		GasPrice: t.GasPrice().String(),
+	}
+}
+
+type ResendArgs struct {
+	Tx       *tx
+	GasPrice string
+	GasLimit string
+}
+
+func (tx *tx) UnmarshalJSON(b []byte) (err error) {
+	var fields map[string]interface{}
+	if err := json.Unmarshal(b, &fields); err != nil {
+		return shared.NewDecodeParamError(err.Error())
+	}
+
+	var (
+		nonce            uint64
+		to               common.Address
+		amount           = new(big.Int).Set(common.Big0)
+		gasLimit         = new(big.Int).Set(common.Big0)
+		gasPrice         = new(big.Int).Set(common.Big0)
+		data             []byte
+		contractCreation = true
+	)
+
+	if val, found := fields["To"]; found {
+		if strVal, ok := val.(string); ok && len(strVal) > 0 {
+			tx.To = strVal
+			to = common.HexToAddress(strVal)
+			contractCreation = false
+		}
+	}
+
+	if val, found := fields["From"]; found {
+		if strVal, ok := val.(string); ok {
+			tx.From = strVal
+		}
+	}
+
+	if val, found := fields["Nonce"]; found {
+		if strVal, ok := val.(string); ok {
+			tx.Nonce = strVal
+			if nonce, err = strconv.ParseUint(strVal, 10, 64); err != nil {
+				return shared.NewDecodeParamError(fmt.Sprintf("Unable to decode tx.Nonce - %v", err))
+			}
+		}
+	} else {
+		return shared.NewDecodeParamError("tx.Nonce not found")
+	}
+
+	var parseOk bool
+	if val, found := fields["Value"]; found {
+		if strVal, ok := val.(string); ok {
+			tx.Value = strVal
+			if _, parseOk = amount.SetString(strVal, 0); !parseOk {
+				return shared.NewDecodeParamError(fmt.Sprintf("Unable to decode tx.Amount - %v", err))
+			}
+		}
+	}
+
+	if val, found := fields["Data"]; found {
+		if strVal, ok := val.(string); ok {
+			tx.Data = strVal
+			if strings.HasPrefix(strVal, "0x") {
+				data = common.Hex2Bytes(strVal[2:])
+			} else {
+				data = common.Hex2Bytes(strVal)
+			}
+		}
+	}
+
+	if val, found := fields["GasLimit"]; found {
+		if strVal, ok := val.(string); ok {
+			tx.GasLimit = strVal
+			if _, parseOk = gasLimit.SetString(strVal, 0); !parseOk {
+				return shared.NewDecodeParamError(fmt.Sprintf("Unable to decode tx.GasLimit - %v", err))
+			}
+		}
+	}
+
+	if val, found := fields["GasPrice"]; found {
+		if strVal, ok := val.(string); ok {
+			tx.GasPrice = strVal
+			if _, parseOk = gasPrice.SetString(strVal, 0); !parseOk {
+				return shared.NewDecodeParamError(fmt.Sprintf("Unable to decode tx.GasPrice - %v", err))
+			}
+		}
+	}
+
+	if contractCreation {
+		tx.tx = types.NewContractCreation(nonce, amount, gasLimit, gasPrice, data)
+	} else {
+		tx.tx = types.NewTransaction(nonce, to, amount, gasLimit, gasPrice, data)
+	}
+
+	return nil
+}
+
+func (args *ResendArgs) UnmarshalJSON(b []byte) (err error) {
+	var obj []interface{}
+	if err = json.Unmarshal(b, &obj); err != nil {
+		return shared.NewDecodeParamError(err.Error())
+	}
+
+	if len(obj) < 1 {
+		return shared.NewInsufficientParamsError(len(obj), 1)
+	}
+
+	data, err := json.Marshal(obj[0])
+	if err != nil {
+		return shared.NewDecodeParamError("Unable to parse transaction object")
+	}
+
+	trans := new(tx)
+	err = json.Unmarshal(data, trans)
+	if err != nil {
+		return shared.NewDecodeParamError("Unable to parse transaction object")
+	}
+
+	if trans == nil || trans.tx == nil {
+		return shared.NewDecodeParamError("Unable to parse transaction object")
+	}
+
+	gasLimit, gasPrice := trans.GasLimit, trans.GasPrice
+
+	if len(obj) > 1 && obj[1] != nil {
+		if gp, ok := obj[1].(string); ok {
+			gasPrice = gp
+		} else {
+			return shared.NewInvalidTypeError("gasPrice", "not a string")
+		}
+	}
+	if len(obj) > 2 && obj[2] != nil {
+		if gl, ok := obj[2].(string); ok {
+			gasLimit = gl
+		} else {
+			return shared.NewInvalidTypeError("gasLimit", "not a string")
+		}
+	}
+
+	args.Tx = trans
+	args.GasPrice = gasPrice
+	args.GasLimit = gasLimit
+
+	return nil
+}

+ 15 - 0
rpc/api/eth_js.go

@@ -14,6 +14,21 @@ web3._extend({
 			params: 2,
 			inputFormatter: [web3._extend.formatters.formatInputString,web3._extend.formatters.formatInputString],
 			outputFormatter: web3._extend.formatters.formatOutputString
+		}),
+		new web3._extend.Method({
+			name: 'resend',
+			call: 'eth_resend',
+			params: 3,
+			inputFormatter: [function(obj) { return obj; },web3._extend.formatters.formatInputString,web3._extend.formatters.formatInputString],
+			outputFormatter: web3._extend.formatters.formatOutputString
+		})
+	],
+	properties:
+	[
+		new web3._extend.Property({
+			name: 'pendingTransactions',
+			getter: 'eth_pendingTransactions',
+			outputFormatter: function(obj) { return obj; }
 		})
 	]
 });

+ 3 - 1
rpc/api/utils.go

@@ -84,6 +84,8 @@ var (
 			"hashrate",
 			"getWork",
 			"submitWork",
+			"pendingTransactions",
+			"resend",
 		},
 		"miner": []string{
 			"hashrate",
@@ -149,7 +151,7 @@ func ParseApiString(apistr string, codec codec.Codec, xeth *xeth.XEth, eth *eth.
 		case shared.DbApiName:
 			apis[i] = NewDbApi(xeth, eth, codec)
 		case shared.EthApiName:
-			apis[i] = NewEthApi(xeth, codec)
+			apis[i] = NewEthApi(xeth, eth, codec)
 		case shared.MinerApiName:
 			apis[i] = NewMinerApi(eth, codec)
 		case shared.NetApiName: