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:
parent
8d51a70d6d
commit
1a48e09e78
@ -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
|
||||||
|
@ -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(
|
||||||
|
@ -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"])
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
}
|
||||||
|
@ -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)
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user