rpc: implement eth_getPendingTransactions (#259)

* Implement `eth_getPendingTransactions`

Closes #244

* refactor repeatitive code

* Update ethereum/rpc/namespaces/eth/api.go

Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com>

* Update ethereum/rpc/namespaces/eth/api.go

Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com>

* Update ethereum/rpc/namespaces/eth/api.go

Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com>

* Update ethereum/rpc/namespaces/eth/api.go

Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com>

* test UnwrapEthereumMsg

Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com>
This commit is contained in:
yihuang 2021-07-13 01:42:53 +08:00 committed by GitHub
parent 8d51a70d6d
commit 1a48e09e78
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 138 additions and 110 deletions

View File

@ -612,40 +612,18 @@ func (e *PublicAPI) GetTransactionByHash(hash common.Hash) (*rpctypes.RPCTransac
} }
for _, tx := range txs { for _, tx := range txs {
if tx == nil { msg, err := evmtypes.UnwrapEthereumMsg(tx)
return nil, fmt.Errorf("invalid tx in mempool")
}
if len((*tx).GetMsgs()) != 1 {
continue
}
msg, ok := (*tx).GetMsgs()[0].(*evmtypes.MsgEthereumTx)
if !ok {
continue
}
txhash := msg.AsTransaction().Hash()
if txhash != hash {
continue
}
from, err := msg.GetSender(e.chainIDEpoch)
if err != nil { if err != nil {
return nil, err // not ethereum tx
continue
} }
data, err := evmtypes.UnpackTxData(msg.Data) rpctx, err := rpctypes.NewTransactionFromMsg(
if err != nil { msg,
return nil, fmt.Errorf("failed to unpack tx data: %w", err)
}
rpctx, err := rpctypes.NewTransactionFromData(
data,
from,
hash,
common.Hash{}, common.Hash{},
uint64(0), uint64(0),
uint64(0), uint64(0),
e.chainIDEpoch,
) )
if err != nil { if err != nil {
return nil, err return nil, err
@ -666,33 +644,18 @@ func (e *PublicAPI) GetTransactionByHash(hash common.Hash) (*rpctypes.RPCTransac
return nil, fmt.Errorf("failed to decode tx: %w", err) return nil, fmt.Errorf("failed to decode tx: %w", err)
} }
if len(tx.GetMsgs()) != 1 { msg, err := evmtypes.UnwrapEthereumMsg(&tx)
e.logger.Debugln("invalid tx")
return nil, fmt.Errorf("invalid tx type: %T", tx)
}
msg, ok := tx.GetMsgs()[0].(*evmtypes.MsgEthereumTx)
if !ok {
e.logger.Debugln("invalid tx")
return nil, fmt.Errorf("invalid tx type: %T", tx)
}
from, err := msg.GetSender(e.chainIDEpoch)
if err != nil { if err != nil {
e.logger.WithError(err).Debugln("invalid tx")
return nil, err return nil, err
} }
data, err := evmtypes.UnpackTxData(msg.Data) return rpctypes.NewTransactionFromMsg(
if err != nil { msg,
return nil, fmt.Errorf("failed to unpack tx data: %w", err)
}
return rpctypes.NewTransactionFromData(
data,
from,
hash,
common.BytesToHash(resBlock.Block.Hash()), common.BytesToHash(resBlock.Block.Hash()),
uint64(res.Height), uint64(res.Height),
uint64(res.Index), uint64(res.Index),
e.chainIDEpoch,
) )
} }
@ -719,31 +682,18 @@ func (e *PublicAPI) GetTransactionByBlockHashAndIndex(hash common.Hash, idx hexu
return nil, fmt.Errorf("failed to decode tx: %w", err) return nil, fmt.Errorf("failed to decode tx: %w", err)
} }
if len(tx.GetMsgs()) != 1 { msg, err := evmtypes.UnwrapEthereumMsg(&tx)
e.logger.Debugln("invalid tx")
return nil, fmt.Errorf("invalid tx type: %T", tx)
}
msg, ok := tx.GetMsgs()[0].(*evmtypes.MsgEthereumTx)
if !ok {
e.logger.Debugln("invalid tx")
return nil, fmt.Errorf("invalid tx type: %T", tx)
}
txHash := msg.AsTransaction().Hash()
txData, err := evmtypes.UnpackTxData(msg.Data)
if err != nil { if err != nil {
e.logger.WithError(err).Debugln("decoding failed") e.logger.WithError(err).Debugln("invalid tx")
return nil, fmt.Errorf("failed to unpack tx data: %w", err) return nil, err
} }
return rpctypes.NewTransactionFromData( return rpctypes.NewTransactionFromMsg(
txData, msg,
common.HexToAddress(msg.From),
txHash,
hash, hash,
uint64(resBlock.Block.Height), uint64(resBlock.Block.Height),
uint64(idx), uint64(idx),
e.chainIDEpoch,
) )
} }
@ -770,31 +720,18 @@ func (e *PublicAPI) GetTransactionByBlockNumberAndIndex(blockNum rpctypes.BlockN
return nil, fmt.Errorf("failed to decode tx: %w", err) return nil, fmt.Errorf("failed to decode tx: %w", err)
} }
if len(tx.GetMsgs()) != 1 { msg, err := evmtypes.UnwrapEthereumMsg(&tx)
e.logger.Debugln("invalid tx")
return nil, fmt.Errorf("invalid tx type: %T", tx)
}
msg, ok := tx.GetMsgs()[0].(*evmtypes.MsgEthereumTx)
if !ok {
e.logger.Debugln("invalid tx")
return nil, fmt.Errorf("invalid tx type: %T", tx)
}
txHash := msg.AsTransaction().Hash()
txData, err := evmtypes.UnpackTxData(msg.Data)
if err != nil { if err != nil {
e.logger.WithError(err).Debugln("decoding failed") e.logger.WithError(err).Debugln("invalid tx")
return nil, fmt.Errorf("failed to unpack tx data: %w", err) return nil, err
} }
return rpctypes.NewTransactionFromData( return rpctypes.NewTransactionFromMsg(
txData, msg,
common.HexToAddress(msg.From),
txHash,
common.BytesToHash(resBlock.Block.Hash()), common.BytesToHash(resBlock.Block.Hash()),
uint64(resBlock.Block.Height), uint64(resBlock.Block.Height),
uint64(idx), uint64(idx),
e.chainIDEpoch,
) )
} }
@ -820,15 +757,10 @@ func (e *PublicAPI) GetTransactionReceipt(hash common.Hash) (map[string]interfac
return nil, fmt.Errorf("failed to decode tx: %w", err) return nil, fmt.Errorf("failed to decode tx: %w", err)
} }
if len(tx.GetMsgs()) != 1 { msg, err := evmtypes.UnwrapEthereumMsg(&tx)
e.logger.Debugln("invalid tx") if err != nil {
return nil, fmt.Errorf("invalid tx type: %T", tx) e.logger.WithError(err).Debugln("invalid tx")
} return nil, err
msg, ok := tx.GetMsgs()[0].(*evmtypes.MsgEthereumTx)
if !ok {
e.logger.Debugln("invalid tx")
return nil, fmt.Errorf("invalid tx type: %T", tx)
} }
txData, err := evmtypes.UnpackTxData(msg.Data) txData, err := evmtypes.UnpackTxData(msg.Data)
@ -911,8 +843,34 @@ func (e *PublicAPI) GetTransactionReceipt(hash common.Hash) (map[string]interfac
func (e *PublicAPI) PendingTransactions() ([]*rpctypes.RPCTransaction, error) { func (e *PublicAPI) PendingTransactions() ([]*rpctypes.RPCTransaction, error) {
e.logger.Debugln("eth_getPendingTransactions") e.logger.Debugln("eth_getPendingTransactions")
// FIXME https://github.com/tharsis/ethermint/issues/244 txs, err := e.backend.PendingTransactions()
return []*rpctypes.RPCTransaction{}, nil if err != nil {
return nil, err
}
result := make([]*rpctypes.RPCTransaction, 0, len(txs))
for _, tx := range txs {
msg, err := evmtypes.UnwrapEthereumMsg(tx)
if err != nil {
// not valid ethereum tx
continue
}
rpctx, err := rpctypes.NewTransactionFromMsg(
msg,
common.Hash{},
uint64(0),
uint64(0),
e.chainIDEpoch,
)
if err != nil {
return nil, err
}
result = append(result, rpctx)
}
return result, nil
} }
// GetUncleByBlockHashAndIndex returns the uncle identified by hash and index. Always returns nil. // GetUncleByBlockHashAndIndex returns the uncle identified by hash and index. Always returns nil.
@ -1095,16 +1053,12 @@ func (e *PublicAPI) getAccountNonce(accAddr common.Address, pending bool, height
// add the uncommitted txs to the nonce counter // add the uncommitted txs to the nonce counter
// only supports `MsgEthereumTx` style tx // only supports `MsgEthereumTx` style tx
for _, tx := range pendingTxs { for _, tx := range pendingTxs {
if tx == nil { msg, err := evmtypes.UnwrapEthereumMsg(tx)
continue if err != nil {
} // not ethereum tx
if len((*tx).GetMsgs()) != 1 {
continue
}
msg, ok := (*tx).GetMsgs()[0].(*evmtypes.MsgEthereumTx)
if !ok {
continue continue
} }
sender, err := msg.GetSender(e.chainIDEpoch) sender, err := msg.GetSender(e.chainIDEpoch)
if err != nil { if err != nil {
continue continue

View File

@ -236,6 +236,27 @@ func ErrRevertedWith(data []byte) DataError {
} }
} }
// NewTransactionFromMsg returns a transaction that will serialize to the RPC
// representation, with the given location metadata set (if available).
func NewTransactionFromMsg(
msg *evmtypes.MsgEthereumTx,
blockHash common.Hash,
blockNumber, index uint64,
chainID *big.Int,
) (*RPCTransaction, error) {
from, err := msg.GetSender(chainID)
if err != nil {
return nil, err
}
data, err := evmtypes.UnpackTxData(msg.Data)
if err != nil {
return nil, fmt.Errorf("failed to unpack tx data: %w", err)
}
return NewTransactionFromData(data, from, msg.AsTransaction().Hash(), blockHash, blockNumber, index)
}
// NewTransactionFromData returns a transaction that will serialize to the RPC // NewTransactionFromData returns a transaction that will serialize to the RPC
// representation, with the given location metadata set (if available). // representation, with the given location metadata set (if available).
func NewTransactionFromData( func NewTransactionFromData(

View File

@ -282,6 +282,12 @@ func TestEth_Pending_GetTransactionByHash(t *testing.T) {
err = json.Unmarshal(rpcRes.Result, &pendingBlockTx) err = json.Unmarshal(rpcRes.Result, &pendingBlockTx)
require.NoError(t, err) require.NoError(t, err)
txsRes := Call(t, "eth_getPendingTransactions", []interface{}{})
var pendingTxs []map[string]interface{}
err = json.Unmarshal(txsRes.Result, &pendingTxs)
require.NoError(t, err)
require.NotEmpty(t, pendingTxs)
// verify the pending tx has all the correct fields from the tx sent. // verify the pending tx has all the correct fields from the tx sent.
require.NotEmpty(t, pendingBlockTx) require.NotEmpty(t, pendingBlockTx)
require.NotEmpty(t, pendingBlockTx["hash"]) require.NotEmpty(t, pendingBlockTx["hash"])

View File

@ -37,6 +37,7 @@ func NewTx(
gasLimit uint64, gasPrice *big.Int, input []byte, accesses *ethtypes.AccessList, gasLimit uint64, gasPrice *big.Int, input []byte, accesses *ethtypes.AccessList,
) *MsgEthereumTx { ) *MsgEthereumTx {
return newMsgEthereumTx(chainID, nonce, to, amount, gasLimit, gasPrice, input, accesses) return newMsgEthereumTx(chainID, nonce, to, amount, gasLimit, gasPrice, input, accesses)
} }
// NewTxContract returns a reference to a new Ethereum transaction // NewTxContract returns a reference to a new Ethereum transaction

View File

@ -1,6 +1,8 @@
package types package types
import ( import (
"fmt"
log "github.com/xlab/suplog" log "github.com/xlab/suplog"
"github.com/gogo/protobuf/proto" "github.com/gogo/protobuf/proto"
@ -50,3 +52,20 @@ func DecodeTransactionLogs(data []byte) (TransactionLogs, error) {
} }
return logs, nil return logs, nil
} }
// UnwrapEthereumMsg extract MsgEthereumTx from wrapping sdk.Tx
func UnwrapEthereumMsg(tx *sdk.Tx) (*MsgEthereumTx, error) {
if tx == nil {
return nil, fmt.Errorf("invalid tx: nil")
}
if len((*tx).GetMsgs()) != 1 {
return nil, fmt.Errorf("invalid tx type: %T", tx)
}
msg, ok := (*tx).GetMsgs()[0].(*MsgEthereumTx)
if !ok {
return nil, fmt.Errorf("invalid tx type: %T", tx)
}
return msg, nil
}

View File

@ -1,11 +1,17 @@
package types package types_test
import ( import (
"math/big"
"testing" "testing"
"github.com/cosmos/cosmos-sdk/client"
sdk "github.com/cosmos/cosmos-sdk/types" sdk "github.com/cosmos/cosmos-sdk/types"
authtx "github.com/cosmos/cosmos-sdk/x/auth/tx"
proto "github.com/gogo/protobuf/proto" proto "github.com/gogo/protobuf/proto"
"github.com/tharsis/ethermint/app"
"github.com/tharsis/ethermint/crypto/ethsecp256k1" "github.com/tharsis/ethermint/crypto/ethsecp256k1"
"github.com/tharsis/ethermint/encoding"
evmtypes "github.com/tharsis/ethermint/x/evm/types"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
@ -27,9 +33,9 @@ func GenerateEthAddress() ethcmn.Address {
func TestEvmDataEncoding(t *testing.T) { func TestEvmDataEncoding(t *testing.T) {
ret := []byte{0x5, 0x8} ret := []byte{0x5, 0x8}
data := &MsgEthereumTxResponse{ data := &evmtypes.MsgEthereumTxResponse{
Hash: common.BytesToHash([]byte("hash")).String(), Hash: common.BytesToHash([]byte("hash")).String(),
Logs: []*Log{{ Logs: []*evmtypes.Log{{
Data: []byte{1, 2, 3, 4}, Data: []byte{1, 2, 3, 4},
BlockNumber: 17, BlockNumber: 17,
}}, }},
@ -40,15 +46,36 @@ func TestEvmDataEncoding(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
txData := &sdk.TxMsgData{ txData := &sdk.TxMsgData{
Data: []*sdk.MsgData{{MsgType: TypeMsgEthereumTx, Data: enc}}, Data: []*sdk.MsgData{{MsgType: evmtypes.TypeMsgEthereumTx, Data: enc}},
} }
txDataBz, err := proto.Marshal(txData) txDataBz, err := proto.Marshal(txData)
require.NoError(t, err) require.NoError(t, err)
res, err := DecodeTxResponse(txDataBz) res, err := evmtypes.DecodeTxResponse(txDataBz)
require.NoError(t, err) require.NoError(t, err)
require.NotNil(t, res) require.NotNil(t, res)
require.Equal(t, data.Logs, res.Logs) require.Equal(t, data.Logs, res.Logs)
require.Equal(t, ret, res.Ret) require.Equal(t, ret, res.Ret)
} }
func TestUnwrapEthererumMsg(t *testing.T) {
_, err := evmtypes.UnwrapEthereumMsg(nil)
require.NotNil(t, err)
encodingConfig := encoding.MakeConfig(app.ModuleBasics)
clientCtx := client.Context{}.WithTxConfig(encodingConfig.TxConfig)
builder, _ := clientCtx.TxConfig.NewTxBuilder().(authtx.ExtensionOptionsTxBuilder)
tx := builder.GetTx().(sdk.Tx)
_, err = evmtypes.UnwrapEthereumMsg(&tx)
require.NotNil(t, err)
msg := evmtypes.NewTx(big.NewInt(1), 0, &common.Address{}, big.NewInt(0), 0, big.NewInt(0), []byte{}, nil)
err = builder.SetMsgs(msg)
tx = builder.GetTx().(sdk.Tx)
msg_, err := evmtypes.UnwrapEthereumMsg(&tx)
require.Nil(t, err)
require.Equal(t, msg_, msg)
}