601 lines
16 KiB
Go
601 lines
16 KiB
Go
package backend
|
|
|
|
import (
|
|
"fmt"
|
|
"math/big"
|
|
|
|
"github.com/cerc-io/laconicd/indexer"
|
|
"github.com/cerc-io/laconicd/rpc/backend/mocks"
|
|
rpctypes "github.com/cerc-io/laconicd/rpc/types"
|
|
ethermint "github.com/cerc-io/laconicd/types"
|
|
evmtypes "github.com/cerc-io/laconicd/x/evm/types"
|
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
|
"github.com/ethereum/go-ethereum/common"
|
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
|
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"
|
|
)
|
|
|
|
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,
|
|
ðermint.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{}
|
|
},
|
|
"",
|
|
ðermint.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)
|
|
}
|
|
})
|
|
}
|
|
}
|