laconicd-deprecated/rpc/backend/tx_info_test.go
Vladislav Varadinov 9e5f4aaa73
tests: Add unit tests for rpc client endpoints (#1409)
* test: add preliminary unit tests and additional mocks for chain_info, account_info and filters

* tests: added additional mocked client calls

* tests: bumped coverage of call_tx to 56% and chain_info to 77%

* tests: bumped call_tx coverage to 70.2% and added additional mock client calls

* tests: tx_info preliminary tests added for debugging.

* tests: added test coverage for sign_tx and additional mocks

* tests: tx_info test coverage bumped to 60.3%

* test: coverage for tracing_tests now at 72%

* tests: added fee makert query client mocks and bumped chain_info to 87.6% coverage.

* tests: failing Cosmos auth module account query.

* tests: added FeeMarket Params mock to call_tx_test

* cleanup some unused code

* tests: added helper function to test suite for signing a Tx and bumped coverage of tx_info to 71.2%

* test: commented GetAccount error case and bumped chain_info to 90.3% coverage

* test: cleanup of tests in node_info, sign_tx and account_info

* Clean up print

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

* Apply suggestions from code review

* fix import issues

Co-authored-by: Vladislav Varadinov <vlad@evmos.org>
Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com>
Co-authored-by: Freddy Caceres <facs95@gmail.com>
2022-11-17 01:35:15 +00:00

601 lines
16 KiB
Go

package backend
import (
"fmt"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/evmos/ethermint/indexer"
"github.com/evmos/ethermint/rpc/backend/mocks"
rpctypes "github.com/evmos/ethermint/rpc/types"
ethermint "github.com/evmos/ethermint/types"
evmtypes "github.com/evmos/ethermint/x/evm/types"
abci "github.com/tendermint/tendermint/abci/types"
tmlog "github.com/tendermint/tendermint/libs/log"
tmrpctypes "github.com/tendermint/tendermint/rpc/core/types"
"github.com/tendermint/tendermint/types"
dbm "github.com/tendermint/tm-db"
"google.golang.org/grpc/metadata"
"math/big"
)
func (suite *BackendTestSuite) TestGetTransactionByHash() {
msgEthereumTx, _ := suite.buildEthereumTx()
txHash := msgEthereumTx.AsTransaction().Hash()
txBz := suite.signAndEncodeEthTx(msgEthereumTx)
block := &types.Block{Header: types.Header{Height: 1, ChainID: "test"}, Data: types.Data{Txs: []types.Tx{txBz}}}
responseDeliver := []*abci.ResponseDeliverTx{
{
Code: 0,
Events: []abci.Event{
{Type: evmtypes.EventTypeEthereumTx, Attributes: []abci.EventAttribute{
{Key: []byte("ethereumTxHash"), Value: []byte(txHash.Hex())},
{Key: []byte("txIndex"), Value: []byte("0")},
{Key: []byte("amount"), Value: []byte("1000")},
{Key: []byte("txGasUsed"), Value: []byte("21000")},
{Key: []byte("txHash"), Value: []byte("")},
{Key: []byte("recipient"), Value: []byte("")},
}},
},
},
}
rpcTransaction, _ := rpctypes.NewRPCTransaction(msgEthereumTx.AsTransaction(), common.Hash{}, 0, 0, big.NewInt(1), suite.backend.chainID)
testCases := []struct {
name string
registerMock func()
tx *evmtypes.MsgEthereumTx
expRPCTx *rpctypes.RPCTransaction
expPass bool
}{
{
"fail - Block error",
func() {
client := suite.backend.clientCtx.Client.(*mocks.Client)
RegisterBlockError(client, 1)
},
msgEthereumTx,
rpcTransaction,
false,
},
{
"fail - Block Result error",
func() {
client := suite.backend.clientCtx.Client.(*mocks.Client)
RegisterBlock(client, 1, txBz)
RegisterBlockResultsError(client, 1)
},
msgEthereumTx,
nil,
true,
},
{
"pass - Base fee error",
func() {
client := suite.backend.clientCtx.Client.(*mocks.Client)
queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient)
RegisterBlock(client, 1, txBz)
RegisterBlockResults(client, 1)
RegisterBaseFeeError(queryClient)
},
msgEthereumTx,
rpcTransaction,
true,
},
{
"pass - Transaction found and returned",
func() {
client := suite.backend.clientCtx.Client.(*mocks.Client)
queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient)
RegisterBlock(client, 1, txBz)
RegisterBlockResults(client, 1)
RegisterBaseFee(queryClient, sdk.NewInt(1))
},
msgEthereumTx,
rpcTransaction,
true,
},
}
for _, tc := range testCases {
suite.Run(tc.name, func() {
suite.SetupTest() // reset
tc.registerMock()
db := dbm.NewMemDB()
suite.backend.indexer = indexer.NewKVIndexer(db, tmlog.NewNopLogger(), suite.backend.clientCtx)
err := suite.backend.indexer.IndexBlock(block, responseDeliver)
suite.Require().NoError(err)
rpcTx, err := suite.backend.GetTransactionByHash(common.HexToHash(tc.tx.Hash))
if tc.expPass {
suite.Require().NoError(err)
suite.Require().Equal(rpcTx, tc.expRPCTx)
} else {
suite.Require().Error(err)
}
})
}
}
func (suite *BackendTestSuite) TestGetTransactionsByHashPending() {
msgEthereumTx, bz := suite.buildEthereumTx()
rpcTransaction, _ := rpctypes.NewRPCTransaction(msgEthereumTx.AsTransaction(), common.Hash{}, 0, 0, big.NewInt(1), suite.backend.chainID)
testCases := []struct {
name string
registerMock func()
tx *evmtypes.MsgEthereumTx
expRPCTx *rpctypes.RPCTransaction
expPass bool
}{
{
"fail - Pending transactions returns error",
func() {
client := suite.backend.clientCtx.Client.(*mocks.Client)
RegisterUnconfirmedTxsError(client, nil)
},
msgEthereumTx,
nil,
true,
},
{
"fail - Tx not found return nil",
func() {
client := suite.backend.clientCtx.Client.(*mocks.Client)
RegisterUnconfirmedTxs(client, nil, nil)
},
msgEthereumTx,
nil,
true,
},
{
"pass - Tx found and returned",
func() {
client := suite.backend.clientCtx.Client.(*mocks.Client)
RegisterUnconfirmedTxs(client, nil, types.Txs{bz})
},
msgEthereumTx,
rpcTransaction,
true,
},
}
for _, tc := range testCases {
suite.Run(tc.name, func() {
suite.SetupTest() // reset
tc.registerMock()
rpcTx, err := suite.backend.getTransactionByHashPending(common.HexToHash(tc.tx.Hash))
if tc.expPass {
suite.Require().NoError(err)
suite.Require().Equal(rpcTx, tc.expRPCTx)
} else {
suite.Require().Error(err)
}
})
}
}
func (suite *BackendTestSuite) TestGetTxByEthHash() {
msgEthereumTx, bz := suite.buildEthereumTx()
rpcTransaction, _ := rpctypes.NewRPCTransaction(msgEthereumTx.AsTransaction(), common.Hash{}, 0, 0, big.NewInt(1), suite.backend.chainID)
testCases := []struct {
name string
registerMock func()
tx *evmtypes.MsgEthereumTx
expRPCTx *rpctypes.RPCTransaction
expPass bool
}{
{
"fail - Indexer disabled can't find transaction",
func() {
suite.backend.indexer = nil
client := suite.backend.clientCtx.Client.(*mocks.Client)
query := fmt.Sprintf("%s.%s='%s'", evmtypes.TypeMsgEthereumTx, evmtypes.AttributeKeyEthereumTxHash, common.HexToHash(msgEthereumTx.Hash).Hex())
RegisterTxSearch(client, query, bz)
},
msgEthereumTx,
rpcTransaction,
false,
},
}
for _, tc := range testCases {
suite.Run(tc.name, func() {
suite.SetupTest() // reset
tc.registerMock()
rpcTx, err := suite.backend.GetTxByEthHash(common.HexToHash(tc.tx.Hash))
if tc.expPass {
suite.Require().NoError(err)
suite.Require().Equal(rpcTx, tc.expRPCTx)
} else {
suite.Require().Error(err)
}
})
}
}
func (suite *BackendTestSuite) TestGetTransactionByBlockHashAndIndex() {
_, bz := suite.buildEthereumTx()
testCases := []struct {
name string
registerMock func()
blockHash common.Hash
expRPCTx *rpctypes.RPCTransaction
expPass bool
}{
{
"pass - block not found",
func() {
client := suite.backend.clientCtx.Client.(*mocks.Client)
RegisterBlockByHashError(client, common.Hash{}, bz)
},
common.Hash{},
nil,
true,
},
{
"pass - Block results error",
func() {
client := suite.backend.clientCtx.Client.(*mocks.Client)
RegisterBlockByHash(client, common.Hash{}, bz)
RegisterBlockResultsError(client, 1)
},
common.Hash{},
nil,
true,
},
}
for _, tc := range testCases {
suite.Run(tc.name, func() {
suite.SetupTest() // reset
tc.registerMock()
rpcTx, err := suite.backend.GetTransactionByBlockHashAndIndex(tc.blockHash, 1)
if tc.expPass {
suite.Require().NoError(err)
suite.Require().Equal(rpcTx, tc.expRPCTx)
} else {
suite.Require().Error(err)
}
})
}
}
func (suite *BackendTestSuite) TestGetTransactionByBlockAndIndex() {
msgEthTx, bz := suite.buildEthereumTx()
defaultBlock := types.MakeBlock(1, []types.Tx{bz}, nil, nil)
defaultResponseDeliverTx := []*abci.ResponseDeliverTx{
{
Code: 0,
Events: []abci.Event{
{Type: evmtypes.EventTypeEthereumTx, Attributes: []abci.EventAttribute{
{Key: []byte("ethereumTxHash"), Value: []byte(common.HexToHash(msgEthTx.Hash).Hex())},
{Key: []byte("txIndex"), Value: []byte("0")},
{Key: []byte("amount"), Value: []byte("1000")},
{Key: []byte("txGasUsed"), Value: []byte("21000")},
{Key: []byte("txHash"), Value: []byte("")},
{Key: []byte("recipient"), Value: []byte("")},
}},
},
},
}
txFromMsg, _ := rpctypes.NewTransactionFromMsg(
msgEthTx,
common.BytesToHash(defaultBlock.Hash().Bytes()),
1,
0,
big.NewInt(1),
suite.backend.chainID,
)
testCases := []struct {
name string
registerMock func()
block *tmrpctypes.ResultBlock
idx hexutil.Uint
expRPCTx *rpctypes.RPCTransaction
expPass bool
}{
{
"pass - block txs index out of bound ",
func() {
client := suite.backend.clientCtx.Client.(*mocks.Client)
RegisterBlockResults(client, 1)
},
&tmrpctypes.ResultBlock{Block: types.MakeBlock(1, []types.Tx{bz}, nil, nil)},
1,
nil,
true,
},
{
"pass - Can't fetch base fee",
func() {
queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient)
client := suite.backend.clientCtx.Client.(*mocks.Client)
RegisterBlockResults(client, 1)
RegisterBaseFeeError(queryClient)
},
&tmrpctypes.ResultBlock{Block: defaultBlock},
0,
txFromMsg,
true,
},
{
"pass - Gets Tx by transaction index",
func() {
queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient)
client := suite.backend.clientCtx.Client.(*mocks.Client)
db := dbm.NewMemDB()
suite.backend.indexer = indexer.NewKVIndexer(db, tmlog.NewNopLogger(), suite.backend.clientCtx)
txBz := suite.signAndEncodeEthTx(msgEthTx)
block := &types.Block{Header: types.Header{Height: 1, ChainID: "test"}, Data: types.Data{Txs: []types.Tx{txBz}}}
err := suite.backend.indexer.IndexBlock(block, defaultResponseDeliverTx)
suite.Require().NoError(err)
RegisterBlockResults(client, 1)
RegisterBaseFee(queryClient, sdk.NewInt(1))
},
&tmrpctypes.ResultBlock{Block: defaultBlock},
0,
txFromMsg,
true,
},
{
"pass - returns the Ethereum format transaction by the Ethereum hash",
func() {
queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient)
client := suite.backend.clientCtx.Client.(*mocks.Client)
RegisterBlockResults(client, 1)
RegisterBaseFee(queryClient, sdk.NewInt(1))
},
&tmrpctypes.ResultBlock{Block: defaultBlock},
0,
txFromMsg,
true,
},
}
for _, tc := range testCases {
suite.Run(tc.name, func() {
suite.SetupTest() // reset
tc.registerMock()
rpcTx, err := suite.backend.GetTransactionByBlockAndIndex(tc.block, tc.idx)
if tc.expPass {
suite.Require().NoError(err)
suite.Require().Equal(rpcTx, tc.expRPCTx)
} else {
suite.Require().Error(err)
}
})
}
}
func (suite *BackendTestSuite) TestGetTransactionByBlockNumberAndIndex() {
msgEthTx, bz := suite.buildEthereumTx()
defaultBlock := types.MakeBlock(1, []types.Tx{bz}, nil, nil)
txFromMsg, _ := rpctypes.NewTransactionFromMsg(
msgEthTx,
common.BytesToHash(defaultBlock.Hash().Bytes()),
1,
0,
big.NewInt(1),
suite.backend.chainID,
)
testCases := []struct {
name string
registerMock func()
blockNum rpctypes.BlockNumber
idx hexutil.Uint
expRPCTx *rpctypes.RPCTransaction
expPass bool
}{
{
"fail - block not found return nil",
func() {
client := suite.backend.clientCtx.Client.(*mocks.Client)
RegisterBlockError(client, 1)
},
0,
0,
nil,
true,
},
{
"pass - returns the transaction identified by block number and index",
func() {
client := suite.backend.clientCtx.Client.(*mocks.Client)
queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient)
RegisterBlock(client, 1, bz)
RegisterBlockResults(client, 1)
RegisterBaseFee(queryClient, sdk.NewInt(1))
},
0,
0,
txFromMsg,
true,
},
}
for _, tc := range testCases {
suite.Run(tc.name, func() {
suite.SetupTest() // reset
tc.registerMock()
rpcTx, err := suite.backend.GetTransactionByBlockNumberAndIndex(tc.blockNum, tc.idx)
if tc.expPass {
suite.Require().NoError(err)
suite.Require().Equal(rpcTx, tc.expRPCTx)
} else {
suite.Require().Error(err)
}
})
}
}
func (suite *BackendTestSuite) TestGetTransactionByTxIndex() {
_, bz := suite.buildEthereumTx()
testCases := []struct {
name string
registerMock func()
height int64
index uint
expTxResult *ethermint.TxResult
expPass bool
}{
{
"fail - Ethereum tx with query not found",
func() {
client := suite.backend.clientCtx.Client.(*mocks.Client)
suite.backend.indexer = nil
RegisterTxSearch(client, "tx.height=0 AND ethereum_tx.txIndex=0", bz)
},
0,
0,
&ethermint.TxResult{},
false,
},
}
for _, tc := range testCases {
suite.Run(tc.name, func() {
suite.SetupTest() // reset
tc.registerMock()
txResults, err := suite.backend.GetTxByTxIndex(tc.height, tc.index)
if tc.expPass {
suite.Require().NoError(err)
suite.Require().Equal(txResults, tc.expTxResult)
} else {
suite.Require().Error(err)
}
})
}
}
func (suite *BackendTestSuite) TestQueryTendermintTxIndexer() {
testCases := []struct {
name string
registerMock func()
txGetter func(*rpctypes.ParsedTxs) *rpctypes.ParsedTx
query string
expTxResult *ethermint.TxResult
expPass bool
}{
{
"fail - Ethereum tx with query not found",
func() {
client := suite.backend.clientCtx.Client.(*mocks.Client)
RegisterTxSearchEmpty(client, "")
},
func(txs *rpctypes.ParsedTxs) *rpctypes.ParsedTx {
return &rpctypes.ParsedTx{}
},
"",
&ethermint.TxResult{},
false,
},
}
for _, tc := range testCases {
suite.Run(tc.name, func() {
suite.SetupTest() // reset
tc.registerMock()
txResults, err := suite.backend.queryTendermintTxIndexer(tc.query, tc.txGetter)
if tc.expPass {
suite.Require().NoError(err)
suite.Require().Equal(txResults, tc.expTxResult)
} else {
suite.Require().Error(err)
}
})
}
}
func (suite *BackendTestSuite) TestGetTransactionReceipt() {
msgEthereumTx, _ := suite.buildEthereumTx()
txHash := msgEthereumTx.AsTransaction().Hash()
txBz := suite.signAndEncodeEthTx(msgEthereumTx)
testCases := []struct {
name string
registerMock func()
tx *evmtypes.MsgEthereumTx
block *types.Block
blockResult []*abci.ResponseDeliverTx
expTxReceipt map[string]interface{}
expPass bool
}{
{
"fail - Receipts do not match ",
func() {
var header metadata.MD
queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient)
client := suite.backend.clientCtx.Client.(*mocks.Client)
RegisterParams(queryClient, &header, 1)
RegisterParamsWithoutHeader(queryClient, 1)
RegisterBlock(client, 1, txBz)
RegisterBlockResults(client, 1)
},
msgEthereumTx,
&types.Block{Header: types.Header{Height: 1}, Data: types.Data{Txs: []types.Tx{txBz}}},
[]*abci.ResponseDeliverTx{
{
Code: 0,
Events: []abci.Event{
{Type: evmtypes.EventTypeEthereumTx, Attributes: []abci.EventAttribute{
{Key: []byte("ethereumTxHash"), Value: []byte(txHash.Hex())},
{Key: []byte("txIndex"), Value: []byte("0")},
{Key: []byte("amount"), Value: []byte("1000")},
{Key: []byte("txGasUsed"), Value: []byte("21000")},
{Key: []byte("txHash"), Value: []byte("")},
{Key: []byte("recipient"), Value: []byte("0x775b87ef5D82ca211811C1a02CE0fE0CA3a455d7")},
}},
},
},
},
map[string]interface{}(nil),
false,
},
}
for _, tc := range testCases {
suite.Run(tc.name, func() {
suite.SetupTest() // reset
tc.registerMock()
db := dbm.NewMemDB()
suite.backend.indexer = indexer.NewKVIndexer(db, tmlog.NewNopLogger(), suite.backend.clientCtx)
err := suite.backend.indexer.IndexBlock(tc.block, tc.blockResult)
suite.Require().NoError(err)
txReceipt, err := suite.backend.GetTransactionReceipt(common.HexToHash(tc.tx.Hash))
if tc.expPass {
suite.Require().NoError(err)
suite.Require().Equal(txReceipt, tc.expTxReceipt)
} else {
suite.Require().NotEqual(txReceipt, tc.expTxReceipt)
}
})
}
}