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>
This commit is contained in:
Vladislav Varadinov 2022-11-17 02:35:15 +01:00 committed by GitHub
parent d450bed61c
commit 9e5f4aaa73
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 2764 additions and 46 deletions

View File

@ -103,6 +103,18 @@ func (suite *BackendTestSuite) TestGetProof() {
false, false,
&rpctypes.AccountResult{}, &rpctypes.AccountResult{},
}, },
{
"fail - Block doesn't exist)",
address1,
[]string{},
rpctypes.BlockNumberOrHash{BlockNumber: &blockNrInvalid},
func(bn rpctypes.BlockNumber, addr common.Address) {
client := suite.backend.clientCtx.Client.(*mocks.Client)
RegisterBlockError(client, bn.Int64())
},
false,
&rpctypes.AccountResult{},
},
{ {
"pass", "pass",
address1, address1,
@ -351,6 +363,27 @@ func (suite *BackendTestSuite) TestGetTransactionCount() {
true, true,
hexutil.Uint64(0), hexutil.Uint64(0),
}, },
// TODO: Error mocking the GetAccount call - problem with Any type
//{
// "pass - returns the number of transactions at the given address up to the given block number",
// true,
// rpctypes.NewBlockNumber(big.NewInt(1)),
// func(addr common.Address, bn rpctypes.BlockNumber) {
// client := suite.backend.clientCtx.Client.(*mocks.Client)
// account, err := suite.backend.clientCtx.AccountRetriever.GetAccount(suite.backend.clientCtx, suite.acc)
// suite.Require().NoError(err)
// request := &authtypes.QueryAccountRequest{Address: sdk.AccAddress(suite.acc.Bytes()).String()}
// requestMarshal, _ := request.Marshal()
// RegisterABCIQueryAccount(
// client,
// requestMarshal,
// tmrpcclient.ABCIQueryOptions{Height: int64(1), Prove: false},
// account,
// )
// },
// true,
// hexutil.Uint64(0),
//},
} }
for _, tc := range testCases { for _, tc := range testCases {
suite.Run(fmt.Sprintf("Case %s", tc.name), func() { suite.Run(fmt.Sprintf("Case %s", tc.name), func() {

View File

@ -6,7 +6,6 @@ import (
"time" "time"
"github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/flags"
"github.com/cosmos/cosmos-sdk/crypto/keyring" "github.com/cosmos/cosmos-sdk/crypto/keyring"
"github.com/cosmos/cosmos-sdk/server" "github.com/cosmos/cosmos-sdk/server"
sdk "github.com/cosmos/cosmos-sdk/types" sdk "github.com/cosmos/cosmos-sdk/types"
@ -16,12 +15,10 @@ import (
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rpc" "github.com/ethereum/go-ethereum/rpc"
"github.com/ethereum/go-ethereum/signer/core/apitypes" "github.com/ethereum/go-ethereum/signer/core/apitypes"
"github.com/evmos/ethermint/crypto/hd"
rpctypes "github.com/evmos/ethermint/rpc/types" rpctypes "github.com/evmos/ethermint/rpc/types"
"github.com/evmos/ethermint/server/config" "github.com/evmos/ethermint/server/config"
ethermint "github.com/evmos/ethermint/types" ethermint "github.com/evmos/ethermint/types"
evmtypes "github.com/evmos/ethermint/x/evm/types" evmtypes "github.com/evmos/ethermint/x/evm/types"
"github.com/spf13/viper"
"github.com/tendermint/tendermint/libs/log" "github.com/tendermint/tendermint/libs/log"
tmrpctypes "github.com/tendermint/tendermint/rpc/core/types" tmrpctypes "github.com/tendermint/tendermint/rpc/core/types"
) )
@ -162,23 +159,6 @@ func NewBackend(
panic(err) panic(err)
} }
algos, _ := clientCtx.Keyring.SupportedAlgorithms()
if !algos.Contains(hd.EthSecp256k1) {
kr, err := keyring.New(
sdk.KeyringServiceName(),
viper.GetString(flags.FlagKeyringBackend),
clientCtx.KeyringDir,
clientCtx.Input,
clientCtx.Codec,
hd.EthSecp256k1Option(),
)
if err != nil {
panic(err)
}
clientCtx = clientCtx.WithKeyring(kr)
}
return &Backend{ return &Backend{
ctx: context.Background(), ctx: context.Background(),
clientCtx: clientCtx, clientCtx: clientCtx,

View File

@ -3,6 +3,7 @@ package backend
import ( import (
"bufio" "bufio"
"fmt" "fmt"
"github.com/evmos/ethermint/crypto/ethsecp256k1"
"math/big" "math/big"
"os" "os"
"path/filepath" "path/filepath"
@ -33,6 +34,7 @@ type BackendTestSuite struct {
suite.Suite suite.Suite
backend *Backend backend *Backend
acc sdk.AccAddress acc sdk.AccAddress
signer keyring.Signer
} }
func TestBackendTestSuite(t *testing.T) { func TestBackendTestSuite(t *testing.T) {
@ -61,6 +63,10 @@ func (suite *BackendTestSuite) SetupTest() {
Seq: uint64(1), Seq: uint64(1),
} }
priv, err := ethsecp256k1.GenerateKey()
suite.signer = tests.NewSigner(priv)
suite.Require().NoError(err)
encodingConfig := encoding.MakeConfig(app.ModuleBasics) encodingConfig := encoding.MakeConfig(app.ModuleBasics)
clientCtx := client.Context{}.WithChainID("ethermint_9000-1"). clientCtx := client.Context{}.WithChainID("ethermint_9000-1").
WithHeight(1). WithHeight(1).
@ -70,13 +76,18 @@ func (suite *BackendTestSuite) SetupTest() {
WithAccountRetriever(client.TestAccountRetriever{Accounts: accounts}) WithAccountRetriever(client.TestAccountRetriever{Accounts: accounts})
allowUnprotectedTxs := false allowUnprotectedTxs := false
idxer := indexer.NewKVIndexer(dbm.NewMemDB(), ctx.Logger, clientCtx) idxer := indexer.NewKVIndexer(dbm.NewMemDB(), ctx.Logger, clientCtx)
suite.backend = NewBackend(ctx, ctx.Logger, clientCtx, allowUnprotectedTxs, idxer) suite.backend = NewBackend(ctx, ctx.Logger, clientCtx, allowUnprotectedTxs, idxer)
suite.backend.queryClient.QueryClient = mocks.NewEVMQueryClient(suite.T()) suite.backend.queryClient.QueryClient = mocks.NewEVMQueryClient(suite.T())
suite.backend.clientCtx.Client = mocks.NewClient(suite.T()) suite.backend.clientCtx.Client = mocks.NewClient(suite.T())
suite.backend.queryClient.FeeMarket = mocks.NewFeeMarketQueryClient(suite.T())
suite.backend.ctx = rpctypes.ContextWithHeight(1) suite.backend.ctx = rpctypes.ContextWithHeight(1)
// Add codec
encCfg := encoding.MakeConfig(app.ModuleBasics)
suite.backend.clientCtx.Codec = encCfg.Codec
} }
// buildEthereumTx returns an example legacy Ethereum transaction // buildEthereumTx returns an example legacy Ethereum transaction
@ -158,3 +169,25 @@ func (suite *BackendTestSuite) generateTestKeyring(clientDir string) (keyring.Ke
encCfg := encoding.MakeConfig(app.ModuleBasics) encCfg := encoding.MakeConfig(app.ModuleBasics)
return keyring.New(sdk.KeyringServiceName(), keyring.BackendTest, clientDir, buf, encCfg.Codec, []keyring.Option{hd.EthSecp256k1Option()}...) return keyring.New(sdk.KeyringServiceName(), keyring.BackendTest, clientDir, buf, encCfg.Codec, []keyring.Option{hd.EthSecp256k1Option()}...)
} }
func (suite *BackendTestSuite) signAndEncodeEthTx(msgEthereumTx *evmtypes.MsgEthereumTx) []byte {
from, priv := tests.NewAddrKey()
signer := tests.NewSigner(priv)
queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient)
RegisterParamsWithoutHeader(queryClient, 1)
ethSigner := ethtypes.LatestSigner(suite.backend.ChainConfig())
msgEthereumTx.From = from.String()
err := msgEthereumTx.Sign(ethSigner, signer)
suite.Require().NoError(err)
tx, err := msgEthereumTx.BuildTx(suite.backend.clientCtx.TxConfig.NewTxBuilder(), "aphoton")
suite.Require().NoError(err)
txEncoder := suite.backend.clientCtx.TxConfig.TxEncoder()
txBz, err := txEncoder(tx)
suite.Require().NoError(err)
return txBz
}

View File

@ -30,8 +30,8 @@ func (suite *BackendTestSuite) TestBlockNumber() {
{ {
"fail - invalid block header height", "fail - invalid block header height",
func() { func() {
height := int64(1)
var header metadata.MD var header metadata.MD
height := int64(1)
queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient) queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient)
RegisterParamsInvalidHeight(queryClient, &header, int64(height)) RegisterParamsInvalidHeight(queryClient, &header, int64(height))
}, },
@ -41,8 +41,8 @@ func (suite *BackendTestSuite) TestBlockNumber() {
{ {
"fail - invalid block header", "fail - invalid block header",
func() { func() {
height := int64(1)
var header metadata.MD var header metadata.MD
height := int64(1)
queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient) queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient)
RegisterParamsInvalidHeader(queryClient, &header, int64(height)) RegisterParamsInvalidHeader(queryClient, &header, int64(height))
}, },
@ -52,8 +52,8 @@ func (suite *BackendTestSuite) TestBlockNumber() {
{ {
"pass - app state header height 1", "pass - app state header height 1",
func() { func() {
height := int64(1)
var header metadata.MD var header metadata.MD
height := int64(1)
queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient) queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient)
RegisterParams(queryClient, &header, int64(height)) RegisterParams(queryClient, &header, int64(height))
}, },
@ -552,8 +552,8 @@ func (suite *BackendTestSuite) TestTendermintBlockByNumber() {
"fail - blockNum < 0 with app state height error", "fail - blockNum < 0 with app state height error",
ethrpc.BlockNumber(-1), ethrpc.BlockNumber(-1),
func(_ ethrpc.BlockNumber) { func(_ ethrpc.BlockNumber) {
appHeight := int64(1)
var header metadata.MD var header metadata.MD
appHeight := int64(1)
queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient) queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient)
RegisterParamsError(queryClient, &header, appHeight) RegisterParamsError(queryClient, &header, appHeight)
}, },
@ -564,8 +564,8 @@ func (suite *BackendTestSuite) TestTendermintBlockByNumber() {
"pass - blockNum < 0 with app state height >= 1", "pass - blockNum < 0 with app state height >= 1",
ethrpc.BlockNumber(-1), ethrpc.BlockNumber(-1),
func(blockNum ethrpc.BlockNumber) { func(blockNum ethrpc.BlockNumber) {
appHeight := int64(1)
var header metadata.MD var header metadata.MD
appHeight := int64(1)
queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient) queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient)
RegisterParams(queryClient, &header, appHeight) RegisterParams(queryClient, &header, appHeight)

484
rpc/backend/call_tx_test.go Normal file
View File

@ -0,0 +1,484 @@
package backend
import (
"encoding/json"
"fmt"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/rlp"
"github.com/evmos/ethermint/rpc/backend/mocks"
rpctypes "github.com/evmos/ethermint/rpc/types"
"github.com/evmos/ethermint/tests"
evmtypes "github.com/evmos/ethermint/x/evm/types"
"google.golang.org/grpc/metadata"
"math/big"
)
func (suite *BackendTestSuite) TestResend() {
txNonce := (hexutil.Uint64)(1)
baseFee := sdk.NewInt(1)
gasPrice := new(hexutil.Big)
toAddr := tests.GenerateAddress()
callArgs := evmtypes.TransactionArgs{
From: nil,
To: &toAddr,
Gas: nil,
GasPrice: nil,
MaxFeePerGas: gasPrice,
MaxPriorityFeePerGas: gasPrice,
Value: gasPrice,
Nonce: nil,
Input: nil,
Data: nil,
AccessList: nil,
}
testCases := []struct {
name string
registerMock func()
args evmtypes.TransactionArgs
gasPrice *hexutil.Big
gasLimit *hexutil.Uint64
expHash common.Hash
expPass bool
}{
{
"fail - Missing transaction nonce ",
func() {},
evmtypes.TransactionArgs{
Nonce: nil,
},
nil,
nil,
common.Hash{},
false,
},
{
"pass - Can't set Tx defaults BaseFee disabled",
func() {
var header metadata.MD
client := suite.backend.clientCtx.Client.(*mocks.Client)
queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient)
RegisterParams(queryClient, &header, 1)
RegisterBlock(client, 1, nil)
RegisterBlockResults(client, 1)
RegisterBaseFeeDisabled(queryClient)
},
evmtypes.TransactionArgs{
Nonce: &txNonce,
},
nil,
nil,
common.Hash{},
true,
},
{
"pass - Can't set Tx defaults ",
func() {
var header metadata.MD
client := suite.backend.clientCtx.Client.(*mocks.Client)
queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient)
feeMarketClient := suite.backend.queryClient.FeeMarket.(*mocks.FeeMarketQueryClient)
RegisterParams(queryClient, &header, 1)
RegisterFeeMarketParams(feeMarketClient, 1)
RegisterBlock(client, 1, nil)
RegisterBlockResults(client, 1)
RegisterBaseFee(queryClient, baseFee)
},
evmtypes.TransactionArgs{
Nonce: &txNonce,
},
nil,
nil,
common.Hash{},
true,
},
{
"pass - MaxFeePerGas is nil",
func() {
var header metadata.MD
client := suite.backend.clientCtx.Client.(*mocks.Client)
queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient)
RegisterParams(queryClient, &header, 1)
RegisterBlock(client, 1, nil)
RegisterBlockResults(client, 1)
RegisterBaseFeeDisabled(queryClient)
},
evmtypes.TransactionArgs{
Nonce: &txNonce,
MaxPriorityFeePerGas: nil,
GasPrice: nil,
MaxFeePerGas: nil,
},
nil,
nil,
common.Hash{},
true,
},
{
"fail - GasPrice and (MaxFeePerGas or MaxPriorityPerGas specified",
func() {},
evmtypes.TransactionArgs{
Nonce: &txNonce,
MaxPriorityFeePerGas: nil,
GasPrice: gasPrice,
MaxFeePerGas: gasPrice,
},
nil,
nil,
common.Hash{},
false,
},
{
"fail - Block error",
func() {
var header metadata.MD
client := suite.backend.clientCtx.Client.(*mocks.Client)
queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient)
RegisterParams(queryClient, &header, 1)
RegisterBlockError(client, 1)
},
evmtypes.TransactionArgs{
Nonce: &txNonce,
},
nil,
nil,
common.Hash{},
false,
},
{
"pass - MaxFeePerGas is nil",
func() {
var header metadata.MD
client := suite.backend.clientCtx.Client.(*mocks.Client)
queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient)
RegisterParams(queryClient, &header, 1)
RegisterBlock(client, 1, nil)
RegisterBlockResults(client, 1)
RegisterBaseFee(queryClient, baseFee)
},
evmtypes.TransactionArgs{
Nonce: &txNonce,
GasPrice: nil,
MaxPriorityFeePerGas: gasPrice,
MaxFeePerGas: gasPrice,
},
nil,
nil,
common.Hash{},
true,
},
{
"pass - Chain Id is nil",
func() {
var header metadata.MD
client := suite.backend.clientCtx.Client.(*mocks.Client)
queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient)
RegisterParams(queryClient, &header, 1)
RegisterBlock(client, 1, nil)
RegisterBlockResults(client, 1)
RegisterBaseFee(queryClient, baseFee)
},
evmtypes.TransactionArgs{
Nonce: &txNonce,
MaxPriorityFeePerGas: gasPrice,
ChainID: nil,
},
nil,
nil,
common.Hash{},
true,
},
{
"fail - Pending transactions error",
func() {
var header metadata.MD
client := suite.backend.clientCtx.Client.(*mocks.Client)
queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient)
RegisterBlock(client, 1, nil)
RegisterBlockResults(client, 1)
RegisterBaseFee(queryClient, baseFee)
RegisterEstimateGas(queryClient, callArgs)
RegisterParams(queryClient, &header, 1)
RegisterParamsWithoutHeader(queryClient, 1)
RegisterUnconfirmedTxsError(client, nil)
},
evmtypes.TransactionArgs{
Nonce: &txNonce,
To: &toAddr,
MaxFeePerGas: gasPrice,
MaxPriorityFeePerGas: gasPrice,
Gas: nil,
},
gasPrice,
nil,
common.Hash{},
false,
},
{
"fail - Not Ethereum txs",
func() {
var header metadata.MD
client := suite.backend.clientCtx.Client.(*mocks.Client)
queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient)
RegisterBlock(client, 1, nil)
RegisterBlockResults(client, 1)
RegisterBaseFee(queryClient, baseFee)
RegisterEstimateGas(queryClient, callArgs)
RegisterParams(queryClient, &header, 1)
RegisterParamsWithoutHeader(queryClient, 1)
RegisterUnconfirmedTxsEmpty(client, nil)
},
evmtypes.TransactionArgs{
Nonce: &txNonce,
To: &toAddr,
MaxFeePerGas: gasPrice,
MaxPriorityFeePerGas: gasPrice,
Gas: nil,
},
gasPrice,
nil,
common.Hash{},
false,
},
}
for _, tc := range testCases {
suite.Run(fmt.Sprintf("case %s", tc.name), func() {
suite.SetupTest() // reset test and queries
tc.registerMock()
hash, err := suite.backend.Resend(tc.args, tc.gasPrice, tc.gasLimit)
if tc.expPass {
suite.Require().Equal(tc.expHash, hash)
} else {
suite.Require().Error(err)
}
})
}
}
func (suite *BackendTestSuite) TestSendRawTransaction() {
ethTx, bz := suite.buildEthereumTx()
rlpEncodedBz, _ := rlp.EncodeToBytes(ethTx.AsTransaction())
cosmosTx, _ := ethTx.BuildTx(suite.backend.clientCtx.TxConfig.NewTxBuilder(), "aphoton")
txBytes, _ := suite.backend.clientCtx.TxConfig.TxEncoder()(cosmosTx)
testCases := []struct {
name string
registerMock func()
rawTx []byte
expHash common.Hash
expPass bool
}{
{
"fail - empty bytes",
func() {},
[]byte{},
common.Hash{},
false,
},
{
"fail - no RLP encoded bytes",
func() {},
bz,
common.Hash{},
false,
},
{
"fail - unprotected transactions",
func() {
suite.backend.allowUnprotectedTxs = false
},
rlpEncodedBz,
common.Hash{},
false,
},
{
"fail - failed to get evm params",
func() {
queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient)
suite.backend.allowUnprotectedTxs = true
RegisterParamsWithoutHeaderError(queryClient, 1)
},
rlpEncodedBz,
common.Hash{},
false,
},
{
"fail - failed to broadcast transaction",
func() {
client := suite.backend.clientCtx.Client.(*mocks.Client)
queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient)
suite.backend.allowUnprotectedTxs = true
RegisterParamsWithoutHeader(queryClient, 1)
RegisterBroadcastTxError(client, txBytes)
},
rlpEncodedBz,
common.HexToHash(ethTx.Hash),
false,
},
{
"pass - Gets the correct transaction hash of the eth transaction",
func() {
client := suite.backend.clientCtx.Client.(*mocks.Client)
queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient)
suite.backend.allowUnprotectedTxs = true
RegisterParamsWithoutHeader(queryClient, 1)
RegisterBroadcastTx(client, txBytes)
},
rlpEncodedBz,
common.HexToHash(ethTx.Hash),
true,
},
}
for _, tc := range testCases {
suite.Run(fmt.Sprintf("case %s", tc.name), func() {
suite.SetupTest() // reset test and queries
tc.registerMock()
hash, err := suite.backend.SendRawTransaction(tc.rawTx)
if tc.expPass {
suite.Require().Equal(tc.expHash, hash)
} else {
suite.Require().Error(err)
}
})
}
}
func (suite *BackendTestSuite) TestDoCall() {
_, bz := suite.buildEthereumTx()
gasPrice := (*hexutil.Big)(big.NewInt(1))
toAddr := tests.GenerateAddress()
callArgs := evmtypes.TransactionArgs{
From: nil,
To: &toAddr,
Gas: nil,
GasPrice: nil,
MaxFeePerGas: gasPrice,
MaxPriorityFeePerGas: gasPrice,
Value: gasPrice,
Input: nil,
Data: nil,
AccessList: nil,
}
argsBz, err := json.Marshal(callArgs)
suite.Require().NoError(err)
testCases := []struct {
name string
registerMock func()
blockNum rpctypes.BlockNumber
callArgs evmtypes.TransactionArgs
expEthTx *evmtypes.MsgEthereumTxResponse
expPass bool
}{
{
"fail - Invalid request",
func() {
client := suite.backend.clientCtx.Client.(*mocks.Client)
queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient)
RegisterBlock(client, 1, bz)
RegisterEthCallError(queryClient, &evmtypes.EthCallRequest{Args: argsBz})
},
rpctypes.BlockNumber(1),
callArgs,
&evmtypes.MsgEthereumTxResponse{},
false,
},
{
"pass - Returned transaction response",
func() {
client := suite.backend.clientCtx.Client.(*mocks.Client)
queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient)
RegisterBlock(client, 1, bz)
RegisterEthCall(queryClient, &evmtypes.EthCallRequest{Args: argsBz})
},
rpctypes.BlockNumber(1),
callArgs,
&evmtypes.MsgEthereumTxResponse{},
true,
},
}
for _, tc := range testCases {
suite.Run(fmt.Sprintf("case %s", tc.name), func() {
suite.SetupTest() // reset test and queries
tc.registerMock()
msgEthTx, err := suite.backend.DoCall(tc.callArgs, tc.blockNum)
if tc.expPass {
suite.Require().Equal(tc.expEthTx, msgEthTx)
} else {
suite.Require().Error(err)
}
})
}
}
func (suite *BackendTestSuite) TestGasPrice() {
defaultGasPrice := (*hexutil.Big)(big.NewInt(1))
testCases := []struct {
name string
registerMock func()
expGas *hexutil.Big
expPass bool
}{
{
"pass - get the default gas price",
func() {
var header metadata.MD
client := suite.backend.clientCtx.Client.(*mocks.Client)
queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient)
feeMarketClient := suite.backend.queryClient.FeeMarket.(*mocks.FeeMarketQueryClient)
RegisterFeeMarketParams(feeMarketClient, 1)
RegisterParams(queryClient, &header, 1)
RegisterBlock(client, 1, nil)
RegisterBlockResults(client, 1)
RegisterBaseFee(queryClient, sdk.NewInt(1))
},
defaultGasPrice,
true,
},
{
"fail - can't get gasFee, FeeMarketParams error",
func() {
var header metadata.MD
client := suite.backend.clientCtx.Client.(*mocks.Client)
queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient)
feeMarketClient := suite.backend.queryClient.FeeMarket.(*mocks.FeeMarketQueryClient)
RegisterFeeMarketParamsError(feeMarketClient, 1)
RegisterParams(queryClient, &header, 1)
RegisterBlock(client, 1, nil)
RegisterBlockResults(client, 1)
RegisterBaseFee(queryClient, sdk.NewInt(1))
},
defaultGasPrice,
false,
},
}
for _, tc := range testCases {
suite.Run(fmt.Sprintf("case %s", tc.name), func() {
suite.SetupTest() // reset test and queries
tc.registerMock()
gasPrice, err := suite.backend.GasPrice()
if tc.expPass {
suite.Require().Equal(tc.expGas, gasPrice)
} else {
suite.Require().Error(err)
}
})
}
}

View File

@ -4,6 +4,12 @@ import (
"fmt" "fmt"
"math/big" "math/big"
"github.com/ethereum/go-ethereum/common/hexutil"
ethrpc "github.com/ethereum/go-ethereum/rpc"
rpc "github.com/evmos/ethermint/rpc/types"
"github.com/evmos/ethermint/tests"
"google.golang.org/grpc/metadata"
sdk "github.com/cosmos/cosmos-sdk/types" sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/tendermint/tendermint/abci/types" "github.com/tendermint/tendermint/abci/types"
tmrpctypes "github.com/tendermint/tendermint/rpc/core/types" tmrpctypes "github.com/tendermint/tendermint/rpc/core/types"
@ -144,3 +150,297 @@ func (suite *BackendTestSuite) TestBaseFee() {
}) })
} }
} }
func (suite *BackendTestSuite) TestChainId() {
expChainId := (*hexutil.Big)(big.NewInt(9000))
testCases := []struct {
name string
registerMock func()
expChainId *hexutil.Big
expPass bool
}{
{
"pass - block is at or past the EIP-155 replay-protection fork block, return chainID from config ",
func() {
var header metadata.MD
queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient)
RegisterParamsInvalidHeight(queryClient, &header, int64(1))
},
expChainId,
true,
},
}
for _, tc := range testCases {
suite.Run(fmt.Sprintf("case %s", tc.name), func() {
suite.SetupTest() // reset test and queries
tc.registerMock()
chainId, err := suite.backend.ChainID()
if tc.expPass {
suite.Require().NoError(err)
suite.Require().Equal(tc.expChainId, chainId)
} else {
suite.Require().Error(err)
}
})
}
}
func (suite *BackendTestSuite) TestGetCoinbase() {
validatorAcc := sdk.AccAddress(tests.GenerateAddress().Bytes())
testCases := []struct {
name string
registerMock func()
accAddr sdk.AccAddress
expPass bool
}{
{
"fail - Can't retrieve status from node",
func() {
client := suite.backend.clientCtx.Client.(*mocks.Client)
RegisterStatusError(client)
},
validatorAcc,
false,
},
{
"fail - Can't query validator account",
func() {
client := suite.backend.clientCtx.Client.(*mocks.Client)
queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient)
RegisterStatus(client)
RegisterValidatorAccountError(queryClient)
},
validatorAcc,
false,
},
{
"pass - Gets coinbase account",
func() {
client := suite.backend.clientCtx.Client.(*mocks.Client)
queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient)
RegisterStatus(client)
RegisterValidatorAccount(queryClient, validatorAcc)
},
validatorAcc,
true,
},
}
for _, tc := range testCases {
suite.Run(fmt.Sprintf("case %s", tc.name), func() {
suite.SetupTest() // reset test and queries
tc.registerMock()
accAddr, err := suite.backend.GetCoinbase()
if tc.expPass {
suite.Require().Equal(tc.accAddr, accAddr)
} else {
suite.Require().Error(err)
}
})
}
}
func (suite *BackendTestSuite) TestSuggestGasTipCap() {
testCases := []struct {
name string
registerMock func()
baseFee *big.Int
expGasTipCap *big.Int
expPass bool
}{
{
"pass - London hardfork not enabled or feemarket not enabled ",
func() {},
nil,
big.NewInt(0),
true,
},
{
"pass - Gets the suggest gas tip cap ",
func() {},
nil,
big.NewInt(0),
true,
},
}
for _, tc := range testCases {
suite.Run(fmt.Sprintf("case %s", tc.name), func() {
suite.SetupTest() // reset test and queries
tc.registerMock()
maxDelta, err := suite.backend.SuggestGasTipCap(tc.baseFee)
if tc.expPass {
suite.Require().Equal(tc.expGasTipCap, maxDelta)
} else {
suite.Require().Error(err)
}
})
}
}
func (suite *BackendTestSuite) TestGlobalMinGasPrice() {
testCases := []struct {
name string
registerMock func()
expMinGasPrice sdk.Dec
expPass bool
}{
{
"fail - Can't get FeeMarket params",
func() {
feeMarketCleint := suite.backend.queryClient.FeeMarket.(*mocks.FeeMarketQueryClient)
RegisterFeeMarketParamsError(feeMarketCleint, int64(1))
},
sdk.ZeroDec(),
false,
},
}
for _, tc := range testCases {
suite.Run(fmt.Sprintf("case %s", tc.name), func() {
suite.SetupTest() // reset test and queries
tc.registerMock()
globalMinGasPrice, err := suite.backend.GlobalMinGasPrice()
if tc.expPass {
suite.Require().Equal(tc.expMinGasPrice, globalMinGasPrice)
} else {
suite.Require().Error(err)
}
})
}
}
func (suite *BackendTestSuite) TestFeeHistory() {
testCases := []struct {
name string
registerMock func(validator sdk.AccAddress)
userBlockCount ethrpc.DecimalOrHex
latestBlock ethrpc.BlockNumber
expFeeHistory *rpc.FeeHistoryResult
validator sdk.AccAddress
expPass bool
}{
{
"fail - can't get params ",
func(validator sdk.AccAddress) {
var header metadata.MD
queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient)
suite.backend.cfg.JSONRPC.FeeHistoryCap = 0
RegisterParamsError(queryClient, &header, ethrpc.BlockNumber(1).Int64())
},
1,
0,
nil,
nil,
false,
},
{
"fail - user block count higher than max block count ",
func(validator sdk.AccAddress) {
var header metadata.MD
queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient)
suite.backend.cfg.JSONRPC.FeeHistoryCap = 0
RegisterParams(queryClient, &header, ethrpc.BlockNumber(1).Int64())
},
1,
0,
nil,
nil,
false,
},
{
"fail - Tendermint block fetching error ",
func(validator sdk.AccAddress) {
client := suite.backend.clientCtx.Client.(*mocks.Client)
suite.backend.cfg.JSONRPC.FeeHistoryCap = 2
RegisterBlockError(client, ethrpc.BlockNumber(1).Int64())
},
1,
1,
nil,
nil,
false,
},
{
"fail - Eth block fetching error",
func(validator sdk.AccAddress) {
client := suite.backend.clientCtx.Client.(*mocks.Client)
suite.backend.cfg.JSONRPC.FeeHistoryCap = 2
RegisterBlock(client, ethrpc.BlockNumber(1).Int64(), nil)
RegisterBlockResultsError(client, 1)
},
1,
1,
nil,
nil,
true,
},
{
"fail - Invalid base fee",
func(validator sdk.AccAddress) {
//baseFee := sdk.NewInt(1)
queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient)
client := suite.backend.clientCtx.Client.(*mocks.Client)
suite.backend.cfg.JSONRPC.FeeHistoryCap = 2
RegisterBlock(client, ethrpc.BlockNumber(1).Int64(), nil)
RegisterBlockResults(client, 1)
RegisterBaseFeeError(queryClient)
RegisterValidatorAccount(queryClient, validator)
RegisterConsensusParams(client, 1)
},
1,
1,
nil,
sdk.AccAddress(tests.GenerateAddress().Bytes()),
false,
},
{
"pass - Valid FeeHistoryResults object",
func(validator sdk.AccAddress) {
baseFee := sdk.NewInt(1)
queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient)
client := suite.backend.clientCtx.Client.(*mocks.Client)
suite.backend.cfg.JSONRPC.FeeHistoryCap = 2
RegisterBlock(client, ethrpc.BlockNumber(1).Int64(), nil)
RegisterBlockResults(client, 1)
RegisterBaseFee(queryClient, baseFee)
RegisterValidatorAccount(queryClient, validator)
RegisterConsensusParams(client, 1)
},
1,
1,
&rpc.FeeHistoryResult{
OldestBlock: (*hexutil.Big)(big.NewInt(0)),
BaseFee: []*hexutil.Big{(*hexutil.Big)(big.NewInt(1))},
GasUsedRatio: []float64{0},
Reward: [][]*hexutil.Big{{(*hexutil.Big)(big.NewInt(0)), (*hexutil.Big)(big.NewInt(0)), (*hexutil.Big)(big.NewInt(0)), (*hexutil.Big)(big.NewInt(0))}},
},
sdk.AccAddress(tests.GenerateAddress().Bytes()),
true,
},
}
for _, tc := range testCases {
suite.Run(fmt.Sprintf("case %s", tc.name), func() {
suite.SetupTest() // reset test and queries
tc.registerMock(tc.validator)
feeHistory, err := suite.backend.FeeHistory(tc.userBlockCount, tc.latestBlock, []float64{25, 50, 75, 100})
if tc.expPass {
suite.Require().NoError(err)
suite.Require().Equal(feeHistory, tc.expFeeHistory)
} else {
suite.Require().Error(err)
}
})
}
}

View File

@ -2,12 +2,15 @@ package backend
import ( import (
"context" "context"
"testing" "github.com/cosmos/cosmos-sdk/client"
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
errortypes "github.com/cosmos/cosmos-sdk/types/errors" errortypes "github.com/cosmos/cosmos-sdk/types/errors"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/evmos/ethermint/rpc/backend/mocks" "github.com/evmos/ethermint/rpc/backend/mocks"
rpc "github.com/evmos/ethermint/rpc/types" rpc "github.com/evmos/ethermint/rpc/types"
evmtypes "github.com/evmos/ethermint/x/evm/types"
mock "github.com/stretchr/testify/mock" mock "github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
abci "github.com/tendermint/tendermint/abci/types" abci "github.com/tendermint/tendermint/abci/types"
@ -15,6 +18,7 @@ import (
tmrpcclient "github.com/tendermint/tendermint/rpc/client" tmrpcclient "github.com/tendermint/tendermint/rpc/client"
tmrpctypes "github.com/tendermint/tendermint/rpc/core/types" tmrpctypes "github.com/tendermint/tendermint/rpc/core/types"
"github.com/tendermint/tendermint/types" "github.com/tendermint/tendermint/types"
"testing"
) )
// Client defines a mocked object that implements the Tendermint JSON-RPC Client // Client defines a mocked object that implements the Tendermint JSON-RPC Client
@ -24,7 +28,74 @@ import (
// To use a mock method it has to be registered in a given test. // To use a mock method it has to be registered in a given test.
var _ tmrpcclient.Client = &mocks.Client{} var _ tmrpcclient.Client = &mocks.Client{}
// Tx Search
func RegisterTxSearch(client *mocks.Client, query string, txBz []byte) {
resulTxs := []*tmrpctypes.ResultTx{{Tx: txBz}}
client.On("TxSearch", rpc.ContextWithHeight(1), query, false, (*int)(nil), (*int)(nil), "").
Return(&tmrpctypes.ResultTxSearch{Txs: resulTxs, TotalCount: 1}, nil)
}
func RegisterTxSearchEmpty(client *mocks.Client, query string) {
client.On("TxSearch", rpc.ContextWithHeight(1), query, false, (*int)(nil), (*int)(nil), "").
Return(&tmrpctypes.ResultTxSearch{}, nil)
}
func RegisterTxSearchError(client *mocks.Client, query string) {
client.On("TxSearch", rpc.ContextWithHeight(1), query, false, (*int)(nil), (*int)(nil), "").
Return(nil, errortypes.ErrInvalidRequest)
}
// Broadcast Tx
func RegisterBroadcastTx(client *mocks.Client, tx types.Tx) {
client.On("BroadcastTxSync", context.Background(), tx).
Return(&tmrpctypes.ResultBroadcastTx{}, nil)
}
func RegisterBroadcastTxError(client *mocks.Client, tx types.Tx) {
client.On("BroadcastTxSync", context.Background(), tx).
Return(nil, errortypes.ErrInvalidRequest)
}
// Unconfirmed Transactions
func RegisterUnconfirmedTxs(client *mocks.Client, limit *int, txs []types.Tx) {
client.On("UnconfirmedTxs", rpc.ContextWithHeight(1), limit).
Return(&tmrpctypes.ResultUnconfirmedTxs{Txs: txs}, nil)
}
func RegisterUnconfirmedTxsEmpty(client *mocks.Client, limit *int) {
client.On("UnconfirmedTxs", rpc.ContextWithHeight(1), limit).
Return(&tmrpctypes.ResultUnconfirmedTxs{
Txs: make([]types.Tx, 2),
}, nil)
}
func RegisterUnconfirmedTxsError(client *mocks.Client, limit *int) {
client.On("UnconfirmedTxs", rpc.ContextWithHeight(1), limit).
Return(nil, errortypes.ErrInvalidRequest)
}
// Status
func RegisterStatus(client *mocks.Client) {
client.On("Status", rpc.ContextWithHeight(1)).
Return(&tmrpctypes.ResultStatus{}, nil)
}
func RegisterStatusError(client *mocks.Client) {
client.On("Status", rpc.ContextWithHeight(1)).
Return(nil, errortypes.ErrInvalidRequest)
}
// Block // Block
func RegisterBlockMultipleTxs(
client *mocks.Client,
height int64,
txs []types.Tx,
) (*tmrpctypes.ResultBlock, error) {
block := types.MakeBlock(height, txs, nil, nil)
resBlock := &tmrpctypes.ResultBlock{Block: block}
client.On("Block", rpc.ContextWithHeight(height), mock.AnythingOfType("*int64")).Return(resBlock, nil)
return resBlock, nil
}
func RegisterBlock( func RegisterBlock(
client *mocks.Client, client *mocks.Client,
height int64, height int64,
@ -34,16 +105,14 @@ func RegisterBlock(
if tx == nil { if tx == nil {
emptyBlock := types.MakeBlock(height, []types.Tx{}, nil, nil) emptyBlock := types.MakeBlock(height, []types.Tx{}, nil, nil)
resBlock := &tmrpctypes.ResultBlock{Block: emptyBlock} resBlock := &tmrpctypes.ResultBlock{Block: emptyBlock}
client.On("Block", rpc.ContextWithHeight(height), mock.AnythingOfType("*int64")). client.On("Block", rpc.ContextWithHeight(height), mock.AnythingOfType("*int64")).Return(resBlock, nil)
Return(resBlock, nil)
return resBlock, nil return resBlock, nil
} }
// with tx // with tx
block := types.MakeBlock(height, []types.Tx{tx}, nil, nil) block := types.MakeBlock(height, []types.Tx{tx}, nil, nil)
resBlock := &tmrpctypes.ResultBlock{Block: block} resBlock := &tmrpctypes.ResultBlock{Block: block}
client.On("Block", rpc.ContextWithHeight(height), mock.AnythingOfType("*int64")). client.On("Block", rpc.ContextWithHeight(height), mock.AnythingOfType("*int64")).Return(resBlock, nil)
Return(resBlock, nil)
return resBlock, nil return resBlock, nil
} }
@ -101,6 +170,26 @@ func TestRegisterConsensusParams(t *testing.T) {
} }
// BlockResults // BlockResults
func RegisterBlockResultsWithEventLog(client *mocks.Client, height int64) (*tmrpctypes.ResultBlockResults, error) {
res := &tmrpctypes.ResultBlockResults{
Height: height,
TxsResults: []*abci.ResponseDeliverTx{
{Code: 0, GasUsed: 0, Events: []abci.Event{{
Type: evmtypes.EventTypeTxLog,
Attributes: []abci.EventAttribute{{
Key: []byte(evmtypes.AttributeKeyTxLog),
Value: []byte{0x7b, 0x22, 0x74, 0x65, 0x73, 0x74, 0x22, 0x3a, 0x20, 0x22, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x22, 0x7d}, // Represents {"test": "hello"}
Index: true,
}},
}}},
},
}
client.On("BlockResults", rpc.ContextWithHeight(height), mock.AnythingOfType("*int64")).
Return(res, nil)
return res, nil
}
func RegisterBlockResults( func RegisterBlockResults(
client *mocks.Client, client *mocks.Client,
height int64, height int64,
@ -167,3 +256,22 @@ func RegisterABCIQueryWithOptions(client *mocks.Client, height int64, path strin
}, },
}, nil) }, nil)
} }
func RegisterABCIQueryWithOptionsError(clients *mocks.Client, path string, data bytes.HexBytes, opts tmrpcclient.ABCIQueryOptions) {
clients.On("ABCIQueryWithOptions", context.Background(), path, data, opts).
Return(nil, errortypes.ErrInvalidRequest)
}
func RegisterABCIQueryAccount(clients *mocks.Client, data bytes.HexBytes, opts tmrpcclient.ABCIQueryOptions, acc client.Account) {
baseAccount := authtypes.NewBaseAccount(acc.GetAddress(), acc.GetPubKey(), acc.GetAccountNumber(), acc.GetSequence())
accAny, _ := codectypes.NewAnyWithValue(baseAccount)
accResponse := authtypes.QueryAccountResponse{Account: accAny}
respBz, _ := accResponse.Marshal()
clients.On("ABCIQueryWithOptions", context.Background(), "/cosmos.auth.v1beta1.Query/Account", data, opts).
Return(&tmrpctypes.ResultABCIQuery{
Response: abci.ResponseQuery{
Value: respBz,
Height: 1,
},
}, nil)
}

View File

@ -1,10 +1,9 @@
package backend package backend
import ( import (
"context"
"encoding/json"
"fmt" "fmt"
"strconv"
"testing"
sdk "github.com/cosmos/cosmos-sdk/types" sdk "github.com/cosmos/cosmos-sdk/types"
errortypes "github.com/cosmos/cosmos-sdk/types/errors" errortypes "github.com/cosmos/cosmos-sdk/types/errors"
grpctypes "github.com/cosmos/cosmos-sdk/types/grpc" grpctypes "github.com/cosmos/cosmos-sdk/types/grpc"
@ -19,6 +18,8 @@ import (
"google.golang.org/grpc/codes" "google.golang.org/grpc/codes"
"google.golang.org/grpc/metadata" "google.golang.org/grpc/metadata"
"google.golang.org/grpc/status" "google.golang.org/grpc/status"
"strconv"
"testing"
) )
// QueryClient defines a mocked object that implements the ethermint GRPC // QueryClient defines a mocked object that implements the ethermint GRPC
@ -28,6 +29,38 @@ import (
// To use a mock method it has to be registered in a given test. // To use a mock method it has to be registered in a given test.
var _ evmtypes.QueryClient = &mocks.EVMQueryClient{} var _ evmtypes.QueryClient = &mocks.EVMQueryClient{}
// TraceTransaction
func RegisterTraceTransactionWithPredecessors(queryClient *mocks.EVMQueryClient, msgEthTx *evmtypes.MsgEthereumTx, predecessors []*evmtypes.MsgEthereumTx) {
data := []byte{0x7b, 0x22, 0x74, 0x65, 0x73, 0x74, 0x22, 0x3a, 0x20, 0x22, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x22, 0x7d}
queryClient.On("TraceTx", rpc.ContextWithHeight(1),
&evmtypes.QueryTraceTxRequest{Msg: msgEthTx, BlockNumber: 1, Predecessors: predecessors}).
Return(&evmtypes.QueryTraceTxResponse{Data: data}, nil)
}
func RegisterTraceTransaction(queryClient *mocks.EVMQueryClient, msgEthTx *evmtypes.MsgEthereumTx) {
data := []byte{0x7b, 0x22, 0x74, 0x65, 0x73, 0x74, 0x22, 0x3a, 0x20, 0x22, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x22, 0x7d}
queryClient.On("TraceTx", rpc.ContextWithHeight(1), &evmtypes.QueryTraceTxRequest{Msg: msgEthTx, BlockNumber: 1}).
Return(&evmtypes.QueryTraceTxResponse{Data: data}, nil)
}
func RegisterTraceTransactionError(queryClient *mocks.EVMQueryClient, msgEthTx *evmtypes.MsgEthereumTx) {
queryClient.On("TraceTx", rpc.ContextWithHeight(1), &evmtypes.QueryTraceTxRequest{Msg: msgEthTx, BlockNumber: 1}).
Return(nil, errortypes.ErrInvalidRequest)
}
// TraceBlock
func RegisterTraceBlock(queryClient *mocks.EVMQueryClient, txs []*evmtypes.MsgEthereumTx) {
data := []byte{0x7b, 0x22, 0x74, 0x65, 0x73, 0x74, 0x22, 0x3a, 0x20, 0x22, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x22, 0x7d}
queryClient.On("TraceBlock", rpc.ContextWithHeight(1),
&evmtypes.QueryTraceBlockRequest{Txs: txs, BlockNumber: 1, TraceConfig: &evmtypes.TraceConfig{}}).
Return(&evmtypes.QueryTraceBlockResponse{Data: data}, nil)
}
func RegisterTraceBlockError(queryClient *mocks.EVMQueryClient) {
queryClient.On("TraceBlock", rpc.ContextWithHeight(1), &evmtypes.QueryTraceBlockRequest{}).
Return(nil, errortypes.ErrInvalidRequest)
}
// Params // Params
func RegisterParams(queryClient *mocks.EVMQueryClient, header *metadata.MD, height int64) { func RegisterParams(queryClient *mocks.EVMQueryClient, header *metadata.MD, height int64) {
queryClient.On("Params", rpc.ContextWithHeight(height), &evmtypes.QueryParamsRequest{}, grpc.Header(header)). queryClient.On("Params", rpc.ContextWithHeight(height), &evmtypes.QueryParamsRequest{}, grpc.Header(header)).
@ -41,6 +74,11 @@ func RegisterParams(queryClient *mocks.EVMQueryClient, header *metadata.MD, heig
}) })
} }
func RegisterParamsWithoutHeader(queryClient *mocks.EVMQueryClient, height int64) {
queryClient.On("Params", rpc.ContextWithHeight(height), &evmtypes.QueryParamsRequest{}).
Return(&evmtypes.QueryParamsResponse{Params: evmtypes.DefaultParams()}, nil)
}
func RegisterParamsInvalidHeader(queryClient *mocks.EVMQueryClient, header *metadata.MD, height int64) { func RegisterParamsInvalidHeader(queryClient *mocks.EVMQueryClient, header *metadata.MD, height int64) {
queryClient.On("Params", rpc.ContextWithHeight(height), &evmtypes.QueryParamsRequest{}, grpc.Header(header)). queryClient.On("Params", rpc.ContextWithHeight(height), &evmtypes.QueryParamsRequest{}, grpc.Header(header)).
Return(&evmtypes.QueryParamsResponse{}, nil). Return(&evmtypes.QueryParamsResponse{}, nil).
@ -64,6 +102,11 @@ func RegisterParamsInvalidHeight(queryClient *mocks.EVMQueryClient, header *meta
}) })
} }
func RegisterParamsWithoutHeaderError(queryClient *mocks.EVMQueryClient, height int64) {
queryClient.On("Params", rpc.ContextWithHeight(height), &evmtypes.QueryParamsRequest{}).
Return(nil, errortypes.ErrInvalidRequest)
}
// Params returns error // Params returns error
func RegisterParamsError(queryClient *mocks.EVMQueryClient, header *metadata.MD, height int64) { func RegisterParamsError(queryClient *mocks.EVMQueryClient, header *metadata.MD, height int64) {
queryClient.On("Params", rpc.ContextWithHeight(height), &evmtypes.QueryParamsRequest{}, grpc.Header(header)). queryClient.On("Params", rpc.ContextWithHeight(height), &evmtypes.QueryParamsRequest{}, grpc.Header(header)).
@ -71,8 +114,9 @@ func RegisterParamsError(queryClient *mocks.EVMQueryClient, header *metadata.MD,
} }
func TestRegisterParams(t *testing.T) { func TestRegisterParams(t *testing.T) {
queryClient := mocks.NewEVMQueryClient(t)
var header metadata.MD var header metadata.MD
queryClient := mocks.NewEVMQueryClient(t)
height := int64(1) height := int64(1)
RegisterParams(queryClient, &header, height) RegisterParams(queryClient, &header, height)
@ -91,6 +135,26 @@ func TestRegisterParamsError(t *testing.T) {
require.Error(t, err) require.Error(t, err)
} }
// ETH Call
func RegisterEthCall(queryClient *mocks.EVMQueryClient, request *evmtypes.EthCallRequest) {
ctx, _ := context.WithCancel(rpc.ContextWithHeight(1))
queryClient.On("EthCall", ctx, request).
Return(&evmtypes.MsgEthereumTxResponse{}, nil)
}
func RegisterEthCallError(queryClient *mocks.EVMQueryClient, request *evmtypes.EthCallRequest) {
ctx, _ := context.WithCancel(rpc.ContextWithHeight(1))
queryClient.On("EthCall", ctx, request).
Return(nil, errortypes.ErrInvalidRequest)
}
// Estimate Gas
func RegisterEstimateGas(queryClient *mocks.EVMQueryClient, args evmtypes.TransactionArgs) {
bz, _ := json.Marshal(args)
queryClient.On("EstimateGas", rpc.ContextWithHeight(1), &evmtypes.EthCallRequest{Args: bz}).
Return(&evmtypes.EstimateGasResponse{}, nil)
}
// BaseFee // BaseFee
func RegisterBaseFee(queryClient *mocks.EVMQueryClient, baseFee sdk.Int) { func RegisterBaseFee(queryClient *mocks.EVMQueryClient, baseFee sdk.Int) {
queryClient.On("BaseFee", rpc.ContextWithHeight(1), &evmtypes.QueryBaseFeeRequest{}). queryClient.On("BaseFee", rpc.ContextWithHeight(1), &evmtypes.QueryBaseFeeRequest{}).
@ -137,12 +201,7 @@ func TestRegisterBaseFeeDisabled(t *testing.T) {
// ValidatorAccount // ValidatorAccount
func RegisterValidatorAccount(queryClient *mocks.EVMQueryClient, validator sdk.AccAddress) { func RegisterValidatorAccount(queryClient *mocks.EVMQueryClient, validator sdk.AccAddress) {
queryClient.On("ValidatorAccount", rpc.ContextWithHeight(1), &evmtypes.QueryValidatorAccountRequest{}). queryClient.On("ValidatorAccount", rpc.ContextWithHeight(1), &evmtypes.QueryValidatorAccountRequest{}).
Return( Return(&evmtypes.QueryValidatorAccountResponse{AccountAddress: validator.String()}, nil)
&evmtypes.QueryValidatorAccountResponse{
AccountAddress: validator.String(),
},
nil,
)
} }
func RegisterValidatorAccountError(queryClient *mocks.EVMQueryClient) { func RegisterValidatorAccountError(queryClient *mocks.EVMQueryClient) {

View File

@ -0,0 +1,21 @@
package backend
import (
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
"github.com/evmos/ethermint/rpc/backend/mocks"
rpc "github.com/evmos/ethermint/rpc/types"
feemarkettypes "github.com/evmos/ethermint/x/feemarket/types"
)
var _ feemarkettypes.QueryClient = &mocks.FeeMarketQueryClient{}
// Params
func RegisterFeeMarketParams(feeMarketClient *mocks.FeeMarketQueryClient, height int64) {
feeMarketClient.On("Params", rpc.ContextWithHeight(height), &feemarkettypes.QueryParamsRequest{}).
Return(&feemarkettypes.QueryParamsResponse{Params: feemarkettypes.DefaultParams()}, nil)
}
func RegisterFeeMarketParamsError(feeMarketClient *mocks.FeeMarketQueryClient, height int64) {
feeMarketClient.On("Params", rpc.ContextWithHeight(height), &feemarkettypes.QueryParamsRequest{}).
Return(nil, sdkerrors.ErrInvalidRequest)
}

View File

@ -15,7 +15,6 @@ func (b *Backend) GetLogs(hash common.Hash) ([][]*ethtypes.Log, error) {
if resBlock == nil { if resBlock == nil {
return nil, errors.Errorf("block not found for hash %s", hash) return nil, errors.Errorf("block not found for hash %s", hash)
} }
return b.GetLogsByHeight(&resBlock.Block.Header.Height) return b.GetLogsByHeight(&resBlock.Block.Header.Height)
} }

117
rpc/backend/filters_test.go Normal file
View File

@ -0,0 +1,117 @@
package backend
import (
"encoding/json"
"github.com/ethereum/go-ethereum/common"
ethtypes "github.com/ethereum/go-ethereum/core/types"
"github.com/evmos/ethermint/rpc/backend/mocks"
ethrpc "github.com/evmos/ethermint/rpc/types"
evmtypes "github.com/evmos/ethermint/x/evm/types"
tmtypes "github.com/tendermint/tendermint/types"
)
func (suite *BackendTestSuite) TestGetLogs() {
_, bz := suite.buildEthereumTx()
block := tmtypes.MakeBlock(1, []tmtypes.Tx{bz}, nil, nil)
logs := make([]*evmtypes.Log, 0, 1)
var log evmtypes.Log
json.Unmarshal([]byte{0x7b, 0x22, 0x74, 0x65, 0x73, 0x74, 0x22, 0x3a, 0x20, 0x22, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x22, 0x7d}, &log)
logs = append(logs, &log)
testCases := []struct {
name string
registerMock func(hash common.Hash)
blockHash common.Hash
expLogs [][]*ethtypes.Log
expPass bool
}{
{
"fail - no block with that hash",
func(hash common.Hash) {
client := suite.backend.clientCtx.Client.(*mocks.Client)
RegisterBlockByHashNotFound(client, hash, bz)
},
common.Hash{},
nil,
false,
},
{
"fail - error fetching block by hash",
func(hash common.Hash) {
client := suite.backend.clientCtx.Client.(*mocks.Client)
RegisterBlockByHashError(client, hash, bz)
},
common.Hash{},
nil,
false,
},
{
"fail - error getting block results",
func(hash common.Hash) {
client := suite.backend.clientCtx.Client.(*mocks.Client)
RegisterBlockByHash(client, hash, bz)
RegisterBlockResultsError(client, 1)
},
common.Hash{},
nil,
false,
},
{
"success - getting logs with block hash",
func(hash common.Hash) {
client := suite.backend.clientCtx.Client.(*mocks.Client)
RegisterBlockByHash(client, hash, bz)
RegisterBlockResultsWithEventLog(client, ethrpc.BlockNumber(1).Int64())
},
common.BytesToHash(block.Hash()),
[][]*ethtypes.Log{evmtypes.LogsToEthereum(logs)},
true,
},
}
for _, tc := range testCases {
suite.Run(tc.name, func() {
suite.SetupTest()
tc.registerMock(tc.blockHash)
logs, err := suite.backend.GetLogs(tc.blockHash)
if tc.expPass {
suite.Require().NoError(err)
suite.Require().Equal(tc.expLogs, logs)
} else {
suite.Require().Error(err)
}
})
}
}
func (suite *BackendTestSuite) TestBloomStatus() {
testCases := []struct {
name string
registerMock func()
expResult uint64
expPass bool
}{
{
"pass - returns the BloomBitsBlocks and the number of processed sections maintained",
func() {},
4096,
true,
},
}
for _, tc := range testCases {
suite.Run(tc.name, func() {
suite.SetupTest()
tc.registerMock()
bloom, _ := suite.backend.BloomStatus()
if tc.expPass {
suite.Require().Equal(tc.expResult, bloom)
}
})
}
}

View File

@ -0,0 +1,123 @@
// Code generated by mockery v2.14.1. DO NOT EDIT.
package mocks
import (
context "context"
grpc "google.golang.org/grpc"
mock "github.com/stretchr/testify/mock"
types "github.com/evmos/ethermint/x/feemarket/types"
)
// FeeMarketQueryClient is an autogenerated mock type for the QueryClient type
type FeeMarketQueryClient struct {
mock.Mock
}
// BaseFee provides a mock function with given fields: ctx, in, opts
func (_m *FeeMarketQueryClient) BaseFee(ctx context.Context, in *types.QueryBaseFeeRequest, opts ...grpc.CallOption) (*types.QueryBaseFeeResponse, error) {
_va := make([]interface{}, len(opts))
for _i := range opts {
_va[_i] = opts[_i]
}
var _ca []interface{}
_ca = append(_ca, ctx, in)
_ca = append(_ca, _va...)
ret := _m.Called(_ca...)
var r0 *types.QueryBaseFeeResponse
if rf, ok := ret.Get(0).(func(context.Context, *types.QueryBaseFeeRequest, ...grpc.CallOption) *types.QueryBaseFeeResponse); ok {
r0 = rf(ctx, in, opts...)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*types.QueryBaseFeeResponse)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, *types.QueryBaseFeeRequest, ...grpc.CallOption) error); ok {
r1 = rf(ctx, in, opts...)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// BlockGas provides a mock function with given fields: ctx, in, opts
func (_m *FeeMarketQueryClient) BlockGas(ctx context.Context, in *types.QueryBlockGasRequest, opts ...grpc.CallOption) (*types.QueryBlockGasResponse, error) {
_va := make([]interface{}, len(opts))
for _i := range opts {
_va[_i] = opts[_i]
}
var _ca []interface{}
_ca = append(_ca, ctx, in)
_ca = append(_ca, _va...)
ret := _m.Called(_ca...)
var r0 *types.QueryBlockGasResponse
if rf, ok := ret.Get(0).(func(context.Context, *types.QueryBlockGasRequest, ...grpc.CallOption) *types.QueryBlockGasResponse); ok {
r0 = rf(ctx, in, opts...)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*types.QueryBlockGasResponse)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, *types.QueryBlockGasRequest, ...grpc.CallOption) error); ok {
r1 = rf(ctx, in, opts...)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// Params provides a mock function with given fields: ctx, in, opts
func (_m *FeeMarketQueryClient) Params(ctx context.Context, in *types.QueryParamsRequest, opts ...grpc.CallOption) (*types.QueryParamsResponse, error) {
_va := make([]interface{}, len(opts))
for _i := range opts {
_va[_i] = opts[_i]
}
var _ca []interface{}
_ca = append(_ca, ctx, in)
_ca = append(_ca, _va...)
ret := _m.Called(_ca...)
var r0 *types.QueryParamsResponse
if rf, ok := ret.Get(0).(func(context.Context, *types.QueryParamsRequest, ...grpc.CallOption) *types.QueryParamsResponse); ok {
r0 = rf(ctx, in, opts...)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*types.QueryParamsResponse)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, *types.QueryParamsRequest, ...grpc.CallOption) error); ok {
r1 = rf(ctx, in, opts...)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
type mockConstructorTestingTNewQueryClient interface {
mock.TestingT
Cleanup(func())
}
// NewQueryClient creates a new instance of QueryClient. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
func NewFeeMarketQueryClient(t mockConstructorTestingTNewQueryClient) *FeeMarketQueryClient {
mock := &FeeMarketQueryClient{}
mock.Mock.Test(t)
t.Cleanup(func() { mock.AssertExpectations(t) })
return mock
}

View File

@ -0,0 +1,355 @@
package backend
import (
"fmt"
sdk "github.com/cosmos/cosmos-sdk/types"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/evmos/ethermint/crypto/ethsecp256k1"
"github.com/evmos/ethermint/rpc/backend/mocks"
ethermint "github.com/evmos/ethermint/types"
"github.com/spf13/viper"
tmrpcclient "github.com/tendermint/tendermint/rpc/client"
"math/big"
)
func (suite *BackendTestSuite) TestRPCMinGasPrice() {
testCases := []struct {
name string
registerMock func()
expMinGasPrice int64
expPass bool
}{
{
"pass - default gas price",
func() {
queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient)
RegisterParamsWithoutHeaderError(queryClient, 1)
},
ethermint.DefaultGasPrice,
true,
},
{
"pass - min gas price is 0",
func() {
queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient)
RegisterParamsWithoutHeader(queryClient, 1)
},
ethermint.DefaultGasPrice,
true,
},
}
for _, tc := range testCases {
suite.Run(fmt.Sprintf("case %s", tc.name), func() {
suite.SetupTest() // reset test and queries
tc.registerMock()
minPrice := suite.backend.RPCMinGasPrice()
if tc.expPass {
suite.Require().Equal(tc.expMinGasPrice, minPrice)
} else {
suite.Require().NotEqual(tc.expMinGasPrice, minPrice)
}
})
}
}
func (suite *BackendTestSuite) TestSetGasPrice() {
defaultGasPrice := (*hexutil.Big)(big.NewInt(1))
testCases := []struct {
name string
registerMock func()
gasPrice hexutil.Big
expOutput bool
}{
{
"pass - cannot get server config",
func() {
suite.backend.clientCtx.Viper = viper.New()
},
*defaultGasPrice,
false,
},
{
"pass - cannot find coin denom",
func() {
suite.backend.clientCtx.Viper = viper.New()
suite.backend.clientCtx.Viper.Set("telemetry.global-labels", []interface{}{})
},
*defaultGasPrice,
false,
},
}
for _, tc := range testCases {
suite.Run(fmt.Sprintf("case %s", tc.name), func() {
suite.SetupTest() // reset test and queries
tc.registerMock()
output := suite.backend.SetGasPrice(tc.gasPrice)
suite.Require().Equal(tc.expOutput, output)
})
}
}
// TODO: Combine these 2 into one test since the code is identical
func (suite *BackendTestSuite) TestListAccounts() {
testCases := []struct {
name string
registerMock func()
expAddr []common.Address
expPass bool
}{
{
"pass - returns empty address",
func() {},
[]common.Address{},
true,
},
}
for _, tc := range testCases {
suite.Run(fmt.Sprintf("case %s", tc.name), func() {
suite.SetupTest() // reset test and queries
tc.registerMock()
output, err := suite.backend.ListAccounts()
if tc.expPass {
suite.Require().NoError(err)
suite.Require().Equal(tc.expAddr, output)
} else {
suite.Require().Error(err)
}
})
}
}
func (suite *BackendTestSuite) TestAccounts() {
testCases := []struct {
name string
registerMock func()
expAddr []common.Address
expPass bool
}{
{
"pass - returns empty address",
func() {},
[]common.Address{},
true,
},
}
for _, tc := range testCases {
suite.Run(fmt.Sprintf("case %s", tc.name), func() {
suite.SetupTest() // reset test and queries
tc.registerMock()
output, err := suite.backend.Accounts()
if tc.expPass {
suite.Require().NoError(err)
suite.Require().Equal(tc.expAddr, output)
} else {
suite.Require().Error(err)
}
})
}
}
func (suite *BackendTestSuite) TestSyncing() {
testCases := []struct {
name string
registerMock func()
expResponse interface{}
expPass bool
}{
{
"fail - Can't get status",
func() {
client := suite.backend.clientCtx.Client.(*mocks.Client)
RegisterStatusError(client)
},
false,
false,
},
{
"pass - Node not catching up",
func() {
client := suite.backend.clientCtx.Client.(*mocks.Client)
RegisterStatus(client)
},
false,
true,
},
{
"pass - Node is catching up",
func() {
client := suite.backend.clientCtx.Client.(*mocks.Client)
RegisterStatus(client)
status, _ := client.Status(suite.backend.ctx)
status.SyncInfo.CatchingUp = true
},
map[string]interface{}{
"startingBlock": hexutil.Uint64(0),
"currentBlock": hexutil.Uint64(0),
},
true,
},
}
for _, tc := range testCases {
suite.Run(fmt.Sprintf("case %s", tc.name), func() {
suite.SetupTest() // reset test and queries
tc.registerMock()
output, err := suite.backend.Syncing()
if tc.expPass {
suite.Require().NoError(err)
suite.Require().Equal(tc.expResponse, output)
} else {
suite.Require().Error(err)
}
})
}
}
func (suite *BackendTestSuite) TestSetEtherbase() {
testCases := []struct {
name string
registerMock func()
etherbase common.Address
expResult bool
}{
{
"pass - Failed to get coinbase address",
func() {
client := suite.backend.clientCtx.Client.(*mocks.Client)
RegisterStatusError(client)
},
common.Address{},
false,
},
{
"pass - the minimum fee is not set",
func() {
client := suite.backend.clientCtx.Client.(*mocks.Client)
queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient)
RegisterStatus(client)
RegisterValidatorAccount(queryClient, suite.acc)
},
common.Address{},
false,
},
{
"fail - error querying for account ",
func() {
client := suite.backend.clientCtx.Client.(*mocks.Client)
queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient)
RegisterStatus(client)
RegisterValidatorAccount(queryClient, suite.acc)
c := sdk.NewDecCoin("aphoton", sdk.NewIntFromBigInt(big.NewInt(1)))
suite.backend.cfg.SetMinGasPrices(sdk.DecCoins{c})
delAddr, _ := suite.backend.GetCoinbase()
//account, _ := suite.backend.clientCtx.AccountRetriever.GetAccount(suite.backend.clientCtx, delAddr)
delCommonAddr := common.BytesToAddress(delAddr.Bytes())
request := &authtypes.QueryAccountRequest{Address: sdk.AccAddress(delCommonAddr.Bytes()).String()}
requestMarshal, _ := request.Marshal()
RegisterABCIQueryWithOptionsError(
client,
"/cosmos.auth.v1beta1.Query/Account",
requestMarshal,
tmrpcclient.ABCIQueryOptions{Height: int64(1), Prove: false},
)
},
common.Address{},
false,
},
// TODO: Finish this test case once ABCIQuery GetAccount is fixed
//{
// "pass - set the etherbase for the miner",
// func() {
// client := suite.backend.clientCtx.Client.(*mocks.Client)
// queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient)
// RegisterStatus(client)
// RegisterValidatorAccount(queryClient, suite.acc)
// c := sdk.NewDecCoin("aphoton", sdk.NewIntFromBigInt(big.NewInt(1)))
// suite.backend.cfg.SetMinGasPrices(sdk.DecCoins{c})
// delAddr, _ := suite.backend.GetCoinbase()
// account, _ := suite.backend.clientCtx.AccountRetriever.GetAccount(suite.backend.clientCtx, delAddr)
// delCommonAddr := common.BytesToAddress(delAddr.Bytes())
// request := &authtypes.QueryAccountRequest{Address: sdk.AccAddress(delCommonAddr.Bytes()).String()}
// requestMarshal, _ := request.Marshal()
// RegisterABCIQueryAccount(
// client,
// requestMarshal,
// tmrpcclient.ABCIQueryOptions{Height: int64(1), Prove: false},
// account,
// )
// },
// common.Address{},
// false,
//},
}
for _, tc := range testCases {
suite.Run(fmt.Sprintf("case %s", tc.name), func() {
suite.SetupTest() // reset test and queries
tc.registerMock()
output := suite.backend.SetEtherbase(tc.etherbase)
suite.Require().Equal(tc.expResult, output)
})
}
}
func (suite *BackendTestSuite) TestImportRawKey() {
priv, _ := ethsecp256k1.GenerateKey()
privHex := common.Bytes2Hex(priv.Bytes())
pubAddr := common.BytesToAddress(priv.PubKey().Address().Bytes())
testCases := []struct {
name string
registerMock func()
privKey string
password string
expAddr common.Address
expPass bool
}{
{
"fail - not a valid private key",
func() {},
"",
"",
common.Address{},
false,
},
{
"pass - returning correct address",
func() {},
privHex,
"",
pubAddr,
true,
},
}
for _, tc := range testCases {
suite.Run(fmt.Sprintf("case %s", tc.name), func() {
suite.SetupTest() // reset test and queries
tc.registerMock()
output, err := suite.backend.ImportRawKey(tc.privKey, tc.password)
if tc.expPass {
suite.Require().NoError(err)
suite.Require().Equal(tc.expAddr, output)
} else {
suite.Require().Error(err)
}
})
}
}

260
rpc/backend/sign_tx_test.go Normal file
View File

@ -0,0 +1,260 @@
package backend
import (
"fmt"
"github.com/cosmos/cosmos-sdk/crypto"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
ethtypes "github.com/ethereum/go-ethereum/core/types"
goethcrypto "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/signer/core/apitypes"
"github.com/evmos/ethermint/crypto/ethsecp256k1"
"github.com/evmos/ethermint/rpc/backend/mocks"
"github.com/evmos/ethermint/tests"
evmtypes "github.com/evmos/ethermint/x/evm/types"
"google.golang.org/grpc/metadata"
)
func (suite *BackendTestSuite) TestSendTransaction() {
gasPrice := new(hexutil.Big)
gas := hexutil.Uint64(1)
zeroGas := hexutil.Uint64(0)
toAddr := tests.GenerateAddress()
priv, _ := ethsecp256k1.GenerateKey()
from := common.BytesToAddress(priv.PubKey().Address().Bytes())
nonce := hexutil.Uint64(1)
baseFee := sdk.NewInt(1)
callArgsDefault := evmtypes.TransactionArgs{
From: &from,
To: &toAddr,
GasPrice: gasPrice,
Gas: &gas,
Nonce: &nonce,
}
hash := common.Hash{}
testCases := []struct {
name string
registerMock func()
args evmtypes.TransactionArgs
expHash common.Hash
expPass bool
}{
{
"fail - Can't find account in Keyring",
func() {},
evmtypes.TransactionArgs{},
hash,
false,
},
{
"fail - Block error can't set Tx defaults",
func() {
var header metadata.MD
queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient)
client := suite.backend.clientCtx.Client.(*mocks.Client)
armor := crypto.EncryptArmorPrivKey(priv, "", "eth_secp256k1")
suite.backend.clientCtx.Keyring.ImportPrivKey("test_key", armor, "")
RegisterParams(queryClient, &header, 1)
RegisterBlockError(client, 1)
},
callArgsDefault,
hash,
false,
},
{
"fail - Cannot validate transaction gas set to 0",
func() {
var header metadata.MD
queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient)
client := suite.backend.clientCtx.Client.(*mocks.Client)
armor := crypto.EncryptArmorPrivKey(priv, "", "eth_secp256k1")
suite.backend.clientCtx.Keyring.ImportPrivKey("test_key", armor, "")
RegisterParams(queryClient, &header, 1)
RegisterBlock(client, 1, nil)
RegisterBlockResults(client, 1)
RegisterBaseFee(queryClient, baseFee)
},
evmtypes.TransactionArgs{
From: &from,
To: &toAddr,
GasPrice: gasPrice,
Gas: &zeroGas,
Nonce: &nonce,
},
hash,
false,
},
{
"fail - Cannot broadcast transaction",
func() {
var header metadata.MD
queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient)
client := suite.backend.clientCtx.Client.(*mocks.Client)
armor := crypto.EncryptArmorPrivKey(priv, "", "eth_secp256k1")
suite.backend.clientCtx.Keyring.ImportPrivKey("test_key", armor, "")
RegisterParams(queryClient, &header, 1)
RegisterBlock(client, 1, nil)
RegisterBlockResults(client, 1)
RegisterBaseFee(queryClient, baseFee)
RegisterParamsWithoutHeader(queryClient, 1)
ethSigner := ethtypes.LatestSigner(suite.backend.ChainConfig())
msg := callArgsDefault.ToTransaction()
msg.Sign(ethSigner, suite.backend.clientCtx.Keyring)
tx, _ := msg.BuildTx(suite.backend.clientCtx.TxConfig.NewTxBuilder(), "aphoton")
txEncoder := suite.backend.clientCtx.TxConfig.TxEncoder()
txBytes, _ := txEncoder(tx)
RegisterBroadcastTxError(client, txBytes)
},
callArgsDefault,
common.Hash{},
false,
},
{
"pass - Return the transaction hash",
func() {
var header metadata.MD
queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient)
client := suite.backend.clientCtx.Client.(*mocks.Client)
armor := crypto.EncryptArmorPrivKey(priv, "", "eth_secp256k1")
suite.backend.clientCtx.Keyring.ImportPrivKey("test_key", armor, "")
RegisterParams(queryClient, &header, 1)
RegisterBlock(client, 1, nil)
RegisterBlockResults(client, 1)
RegisterBaseFee(queryClient, baseFee)
RegisterParamsWithoutHeader(queryClient, 1)
ethSigner := ethtypes.LatestSigner(suite.backend.ChainConfig())
msg := callArgsDefault.ToTransaction()
msg.Sign(ethSigner, suite.backend.clientCtx.Keyring)
tx, _ := msg.BuildTx(suite.backend.clientCtx.TxConfig.NewTxBuilder(), "aphoton")
txEncoder := suite.backend.clientCtx.TxConfig.TxEncoder()
txBytes, _ := txEncoder(tx)
RegisterBroadcastTx(client, txBytes)
},
callArgsDefault,
hash,
true,
},
}
for _, tc := range testCases {
suite.Run(fmt.Sprintf("case %s", tc.name), func() {
suite.SetupTest() // reset test and queries
tc.registerMock()
if tc.expPass {
// Sign the transaction and get the hash
queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient)
RegisterParamsWithoutHeader(queryClient, 1)
ethSigner := ethtypes.LatestSigner(suite.backend.ChainConfig())
msg := callArgsDefault.ToTransaction()
msg.Sign(ethSigner, suite.backend.clientCtx.Keyring)
tc.expHash = msg.AsTransaction().Hash()
}
responseHash, err := suite.backend.SendTransaction(tc.args)
if tc.expPass {
suite.Require().NoError(err)
suite.Require().Equal(tc.expHash, responseHash)
} else {
suite.Require().Error(err)
}
})
}
}
func (suite *BackendTestSuite) TestSign() {
from, priv := tests.NewAddrKey()
testCases := []struct {
name string
registerMock func()
fromAddr common.Address
inputBz hexutil.Bytes
expPass bool
}{
{
"fail - can't find key in Keyring",
func() {},
from,
nil,
false,
},
{
"pass - sign nil data",
func() {
armor := crypto.EncryptArmorPrivKey(priv, "", "eth_secp256k1")
suite.backend.clientCtx.Keyring.ImportPrivKey("test_key", armor, "")
},
from,
nil,
true,
},
}
for _, tc := range testCases {
suite.Run(fmt.Sprintf("case %s", tc.name), func() {
suite.SetupTest() // reset test and queries
tc.registerMock()
responseBz, err := suite.backend.Sign(tc.fromAddr, tc.inputBz)
if tc.expPass {
signature, _, err := suite.backend.clientCtx.Keyring.SignByAddress((sdk.AccAddress)(from.Bytes()), tc.inputBz)
signature[goethcrypto.RecoveryIDOffset] += 27
suite.Require().NoError(err)
suite.Require().Equal((hexutil.Bytes)(signature), responseBz)
} else {
suite.Require().Error(err)
}
})
}
}
func (suite *BackendTestSuite) TestSignTypedData() {
from, priv := tests.NewAddrKey()
testCases := []struct {
name string
registerMock func()
fromAddr common.Address
inputTypedData apitypes.TypedData
expPass bool
}{
{
"fail - can't find key in Keyring",
func() {},
from,
apitypes.TypedData{},
false,
},
{
"fail - empty TypeData",
func() {
armor := crypto.EncryptArmorPrivKey(priv, "", "eth_secp256k1")
suite.backend.clientCtx.Keyring.ImportPrivKey("test_key", armor, "")
},
from,
apitypes.TypedData{},
false,
},
// TODO: Generate a TypedData msg
}
for _, tc := range testCases {
suite.Run(fmt.Sprintf("case %s", tc.name), func() {
suite.SetupTest() // reset test and queries
tc.registerMock()
responseBz, err := suite.backend.SignTypedData(tc.fromAddr, tc.inputTypedData)
if tc.expPass {
sigHash, _, err := apitypes.TypedDataAndHash(tc.inputTypedData)
signature, _, err := suite.backend.clientCtx.Keyring.SignByAddress((sdk.AccAddress)(from.Bytes()), sigHash)
signature[goethcrypto.RecoveryIDOffset] += 27
suite.Require().NoError(err)
suite.Require().Equal((hexutil.Bytes)(signature), responseBz)
} else {
suite.Require().Error(err)
}
})
}
}

View File

@ -112,7 +112,7 @@ func (b *Backend) TraceTransaction(hash common.Hash, config *evmtypes.TraceConfi
return decodedResult, nil return decodedResult, nil
} }
// traceBlock configures a new tracer according to the provided configuration, and // TraceBlock configures a new tracer according to the provided configuration, and
// executes all the transactions contained within. The return value will be one item // executes all the transactions contained within. The return value will be one item
// per transaction, dependent on the requested tracer. // per transaction, dependent on the requested tracer.
func (b *Backend) TraceBlock(height rpctypes.BlockNumber, func (b *Backend) TraceBlock(height rpctypes.BlockNumber,

247
rpc/backend/tracing_test.go Normal file
View File

@ -0,0 +1,247 @@
package backend
import (
"fmt"
"github.com/cosmos/cosmos-sdk/crypto"
"github.com/ethereum/go-ethereum/common"
ethtypes "github.com/ethereum/go-ethereum/core/types"
"github.com/evmos/ethermint/crypto/ethsecp256k1"
"github.com/evmos/ethermint/indexer"
"github.com/evmos/ethermint/rpc/backend/mocks"
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"
tmtypes "github.com/tendermint/tendermint/types"
dbm "github.com/tendermint/tm-db"
)
func (suite *BackendTestSuite) TestTraceTransaction() {
msgEthereumTx, _ := suite.buildEthereumTx()
msgEthereumTx2, _ := suite.buildEthereumTx()
txHash := msgEthereumTx.AsTransaction().Hash()
txHash2 := msgEthereumTx2.AsTransaction().Hash()
priv, _ := ethsecp256k1.GenerateKey()
from := common.BytesToAddress(priv.PubKey().Address().Bytes())
queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient)
RegisterParamsWithoutHeader(queryClient, 1)
armor := crypto.EncryptArmorPrivKey(priv, "", "eth_secp256k1")
suite.backend.clientCtx.Keyring.ImportPrivKey("test_key", armor, "")
ethSigner := ethtypes.LatestSigner(suite.backend.ChainConfig())
txEncoder := suite.backend.clientCtx.TxConfig.TxEncoder()
msgEthereumTx.From = from.String()
msgEthereumTx.Sign(ethSigner, suite.signer)
tx, _ := msgEthereumTx.BuildTx(suite.backend.clientCtx.TxConfig.NewTxBuilder(), "aphoton")
txBz, _ := txEncoder(tx)
msgEthereumTx2.From = from.String()
msgEthereumTx2.Sign(ethSigner, suite.signer)
tx2, _ := msgEthereumTx.BuildTx(suite.backend.clientCtx.TxConfig.NewTxBuilder(), "aphoton")
txBz2, _ := txEncoder(tx2)
testCases := []struct {
name string
registerMock func()
block *types.Block
responseBlock []*abci.ResponseDeliverTx
expResult interface{}
expPass bool
}{
{
"fail - tx not found",
func() {},
&types.Block{Header: types.Header{Height: 1}, Data: types.Data{Txs: []types.Tx{}}},
[]*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")},
}},
},
},
},
nil,
false,
},
{
"fail - block not found",
func() {
//var header metadata.MD
client := suite.backend.clientCtx.Client.(*mocks.Client)
RegisterBlockError(client, 1)
},
&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{}{"test": "hello"},
false,
},
{
"pass - transaction found in a block with multiple transactions",
func() {
queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient)
client := suite.backend.clientCtx.Client.(*mocks.Client)
RegisterBlockMultipleTxs(client, 1, []types.Tx{txBz, txBz2})
RegisterTraceTransactionWithPredecessors(queryClient, msgEthereumTx, []*evmtypes.MsgEthereumTx{msgEthereumTx})
},
&types.Block{Header: types.Header{Height: 1}, Data: types.Data{Txs: []types.Tx{txBz, txBz2}}},
[]*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")},
}},
},
},
{
Code: 0,
Events: []abci.Event{
{Type: evmtypes.EventTypeEthereumTx, Attributes: []abci.EventAttribute{
{Key: []byte("ethereumTxHash"), Value: []byte(txHash2.Hex())},
{Key: []byte("txIndex"), Value: []byte("1")},
{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{}{"test": "hello"},
true,
},
{
"pass - transaction found",
func() {
queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient)
client := suite.backend.clientCtx.Client.(*mocks.Client)
RegisterBlock(client, 1, txBz)
RegisterTraceTransaction(queryClient, 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{}{"test": "hello"},
true,
},
}
for _, tc := range testCases {
suite.Run(fmt.Sprintf("case %s", tc.name), func() {
suite.SetupTest() // reset test and queries
tc.registerMock()
db := dbm.NewMemDB()
suite.backend.indexer = indexer.NewKVIndexer(db, tmlog.NewNopLogger(), suite.backend.clientCtx)
err := suite.backend.indexer.IndexBlock(tc.block, tc.responseBlock)
txResult, err := suite.backend.TraceTransaction(txHash, nil)
if tc.expPass {
suite.Require().NoError(err)
suite.Require().Equal(tc.expResult, txResult)
} else {
suite.Require().Error(err)
}
})
}
}
func (suite *BackendTestSuite) TestTraceBlock() {
msgEthTx, bz := suite.buildEthereumTx()
emptyBlock := tmtypes.MakeBlock(1, []tmtypes.Tx{}, nil, nil)
filledBlock := tmtypes.MakeBlock(1, []tmtypes.Tx{bz}, nil, nil)
resBlockEmpty := tmrpctypes.ResultBlock{Block: emptyBlock, BlockID: emptyBlock.LastBlockID}
resBlockFilled := tmrpctypes.ResultBlock{Block: filledBlock, BlockID: filledBlock.LastBlockID}
testCases := []struct {
name string
registerMock func()
expTraceResults []*evmtypes.TxTraceResult
resBlock *tmrpctypes.ResultBlock
config *evmtypes.TraceConfig
expPass bool
}{
{
"pass - no transaction returning empty array",
func() {},
[]*evmtypes.TxTraceResult{},
&resBlockEmpty,
&evmtypes.TraceConfig{},
true,
},
{
"fail - cannot unmarshal data",
func() {
queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient)
RegisterTraceBlock(queryClient, []*evmtypes.MsgEthereumTx{msgEthTx})
},
[]*evmtypes.TxTraceResult{},
&resBlockFilled,
&evmtypes.TraceConfig{},
false,
},
}
for _, tc := range testCases {
suite.Run(fmt.Sprintf("case %s", tc.name), func() {
suite.SetupTest() // reset test and queries
tc.registerMock()
traceResults, err := suite.backend.TraceBlock(1, tc.config, tc.resBlock)
if tc.expPass {
suite.Require().NoError(err)
suite.Require().Equal(tc.expTraceResults, traceResults)
} else {
suite.Require().Error(err)
}
})
}
}

View File

@ -342,7 +342,7 @@ func (b *Backend) queryTendermintTxIndexer(query string, txGetter func(*rpctypes
return rpctypes.ParseTxIndexerResult(txResult, tx, txGetter) return rpctypes.ParseTxIndexerResult(txResult, tx, txGetter)
} }
// getTransactionByBlockAndIndex is the common code shared by `GetTransactionByBlockNumberAndIndex` and `GetTransactionByBlockHashAndIndex`. // GetTransactionByBlockAndIndex is the common code shared by `GetTransactionByBlockNumberAndIndex` and `GetTransactionByBlockHashAndIndex`.
func (b *Backend) GetTransactionByBlockAndIndex(block *tmrpctypes.ResultBlock, idx hexutil.Uint) (*rpctypes.RPCTransaction, error) { func (b *Backend) GetTransactionByBlockAndIndex(block *tmrpctypes.ResultBlock, idx hexutil.Uint) (*rpctypes.RPCTransaction, error) {
blockRes, err := b.TendermintBlockResultByNumber(&block.Block.Height) blockRes, err := b.TendermintBlockResultByNumber(&block.Block.Height)
if err != nil { if err != nil {

600
rpc/backend/tx_info_test.go Normal file
View File

@ -0,0 +1,600 @@
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)
}
})
}
}

View File

@ -261,7 +261,6 @@ func GetLogsFromBlockResults(blockRes *tmrpctypes.ResultBlockResults) ([][]*etht
blockLogs = append(blockLogs, logs...) blockLogs = append(blockLogs, logs...)
} }
return blockLogs, nil return blockLogs, nil
} }