diff --git a/rpc/api/eth.go b/rpc/api/eth.go index b84ae31da..db7a643d8 100644 --- a/rpc/api/eth.go +++ b/rpc/api/eth.go @@ -26,6 +26,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/natspec" "github.com/ethereum/go-ethereum/eth" + "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rpc/codec" "github.com/ethereum/go-ethereum/rpc/shared" "github.com/ethereum/go-ethereum/xeth" @@ -70,8 +71,10 @@ var ( "eth_getCode": (*ethApi).GetData, "eth_getNatSpec": (*ethApi).GetNatSpec, "eth_sign": (*ethApi).Sign, - "eth_sendRawTransaction": (*ethApi).SendRawTransaction, + "eth_sendRawTransaction": (*ethApi).SubmitTransaction, + "eth_submitTransaction": (*ethApi).SubmitTransaction, "eth_sendTransaction": (*ethApi).SendTransaction, + "eth_signTransaction": (*ethApi).SignTransaction, "eth_transact": (*ethApi).SendTransaction, "eth_estimateGas": (*ethApi).EstimateGas, "eth_call": (*ethApi).Call, @@ -285,7 +288,7 @@ func (self *ethApi) Sign(req *shared.Request) (interface{}, error) { return v, nil } -func (self *ethApi) SendRawTransaction(req *shared.Request) (interface{}, error) { +func (self *ethApi) SubmitTransaction(req *shared.Request) (interface{}, error) { args := new(NewDataArgs) if err := self.codec.Decode(req.Params, &args); err != nil { return nil, shared.NewDecodeParamError(err.Error()) @@ -298,6 +301,45 @@ func (self *ethApi) SendRawTransaction(req *shared.Request) (interface{}, error) return v, nil } +// JsonTransaction is returned as response by the JSON RPC. It contains the +// signed RLP encoded transaction as Raw and the signed transaction object as Tx. +type JsonTransaction struct { + Raw string `json:"raw"` + Tx *tx `json:"tx"` +} + +func (self *ethApi) SignTransaction(req *shared.Request) (interface{}, error) { + args := new(NewTxArgs) + if err := self.codec.Decode(req.Params, &args); err != nil { + return nil, shared.NewDecodeParamError(err.Error()) + } + + // nonce may be nil ("guess" mode) + var nonce string + if args.Nonce != nil { + nonce = args.Nonce.String() + } + + var gas, price string + if args.Gas != nil { + gas = args.Gas.String() + } + if args.GasPrice != nil { + price = args.GasPrice.String() + } + tx, err := self.xeth.SignTransaction(args.From, args.To, nonce, args.Value.String(), gas, price, args.Data) + if err != nil { + return nil, err + } + + data, err := rlp.EncodeToBytes(tx) + if err != nil { + return nil, err + } + + return JsonTransaction{"0x" + common.Bytes2Hex(data), newTx(tx)}, nil +} + func (self *ethApi) SendTransaction(req *shared.Request) (interface{}, error) { args := new(NewTxArgs) if err := self.codec.Decode(req.Params, &args); err != nil { diff --git a/rpc/api/eth_js.go b/rpc/api/eth_js.go index 75c103c9d..dfc104ad8 100644 --- a/rpc/api/eth_js.go +++ b/rpc/api/eth_js.go @@ -36,11 +36,23 @@ web3._extend({ params: 3, inputFormatter: [web3._extend.formatters.inputTransactionFormatter, web3._extend.utils.fromDecimal, web3._extend.utils.fromDecimal] }), - new web3._extend.Method({ - name: 'getNatSpec', - call: 'eth_getNatSpec', - params: 1, - inputFormatter: [web3._extend.formatters.inputTransactionFormatter] + new web3._extend.Method({ + name: 'getNatSpec', + call: 'eth_getNatSpec', + params: 1, + inputFormatter: [web3._extend.formatters.inputTransactionFormatter] + }), + new web3._extend.Method({ + name: 'signTransaction', + call: 'eth_signTransaction', + params: 1, + inputFormatter: [web3._extend.formatters.inputTransactionFormatter] + }), + new web3._extend.Method({ + name: 'submitTransaction', + call: 'eth_submitTransaction', + params: 1, + inputFormatter: [web3._extend.formatters.inputTransactionFormatter] }) ], properties: diff --git a/xeth/xeth.go b/xeth/xeth.go index ae9f1fe47..19c42a9a3 100644 --- a/xeth/xeth.go +++ b/xeth/xeth.go @@ -879,6 +879,60 @@ func (self *XEth) Frontend() Frontend { return self.frontend } +func (self *XEth) SignTransaction(fromStr, toStr, nonceStr, valueStr, gasStr, gasPriceStr, codeStr string) (*types.Transaction, error) { + if len(toStr) > 0 && toStr != "0x" && !isAddress(toStr) { + return nil, errors.New("Invalid address") + } + + var ( + from = common.HexToAddress(fromStr) + to = common.HexToAddress(toStr) + value = common.Big(valueStr) + gas *big.Int + price *big.Int + data []byte + contractCreation bool + ) + + if len(gasStr) == 0 { + gas = DefaultGas() + } else { + gas = common.Big(gasStr) + } + + if len(gasPriceStr) == 0 { + price = self.DefaultGasPrice() + } else { + price = common.Big(gasPriceStr) + } + + data = common.FromHex(codeStr) + if len(toStr) == 0 { + contractCreation = true + } + + var nonce uint64 + if len(nonceStr) != 0 { + nonce = common.Big(nonceStr).Uint64() + } else { + state := self.backend.TxPool().State() + nonce = state.GetNonce(from) + } + var tx *types.Transaction + if contractCreation { + tx = types.NewContractCreation(nonce, value, gas, price, data) + } else { + tx = types.NewTransaction(nonce, to, value, gas, price, data) + } + + signed, err := self.sign(tx, from, false) + if err != nil { + return nil, err + } + + return signed, nil +} + func (self *XEth) Transact(fromStr, toStr, nonceStr, valueStr, gasStr, gasPriceStr, codeStr string) (string, error) { // this minimalistic recoding is enough (works for natspec.js)