Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test: synthetic eth txs unit tests #2342

Merged
merged 24 commits into from
Jun 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
* [2266](https://github.com/zeta-chain/node/pull/2266) - try fixing E2E test `crosschain_swap` failure `btc transaction not signed`
* [2294](https://github.com/zeta-chain/node/pull/2294) - add and fix existing ethermint rpc unit test
* [2329](https://github.com/zeta-chain/node/pull/2329) - fix TODOs in rpc unit tests
* [2342](https://github.com/zeta-chain/node/pull/2342) - extend rpc unit tests with testing extension to include synthetic ethereum txs
* [2299](https://github.com/zeta-chain/node/pull/2299) - add `zetae2e` command to deploy test contracts

### Fixes
Expand Down
53 changes: 53 additions & 0 deletions rpc/backend/backend_suite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"testing"

dbm "github.com/cometbft/cometbft-db"
abci "github.com/cometbft/cometbft/abci/types"
tmrpctypes "github.com/cometbft/cometbft/rpc/core/types"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/crypto/keyring"
Expand Down Expand Up @@ -35,6 +36,30 @@ type BackendTestSuite struct {
signer keyring.Signer
}

// testTx is a dummy implementation of cosmos Tx used for testing.
type testTx struct {
}

func (tx testTx) GetMsgs() []sdk.Msg { return nil }
func (tx testTx) GetSigners() []sdk.AccAddress { return nil }

func (tx testTx) ValidateBasic() error { return nil }
func (t testTx) ProtoMessage() { panic("not implemented") }
func (t testTx) Reset() { panic("not implemented") }

func (t testTx) String() string { panic("not implemented") }

func (t testTx) Bytes() []byte { panic("not implemented") }

func (t testTx) VerifySignature(msg []byte, sig []byte) bool { panic("not implemented") }

func (t testTx) Type() string { panic("not implemented") }

var (
_ sdk.Tx = (*testTx)(nil)
_ sdk.Msg = (*testTx)(nil)
)

func TestBackendTestSuite(t *testing.T) {
suite.Run(t, new(BackendTestSuite))
}
Expand Down Expand Up @@ -118,6 +143,34 @@ func (suite *BackendTestSuite) buildEthereumTx() (*evmtypes.MsgEthereumTx, []byt
return msgEthereumTx, bz
}

func (suite *BackendTestSuite) buildSyntheticTxResult(txHash string) ([]byte, abci.ResponseDeliverTx) {
testTx := &testTx{}
txBuilder := suite.backend.clientCtx.TxConfig.NewTxBuilder()
txBuilder.SetSignatures()
txBuilder.SetMsgs(testTx)
bz, _ := suite.backend.clientCtx.TxConfig.TxEncoder()(txBuilder.GetTx())
return bz, abci.ResponseDeliverTx{
Code: 0,
Events: []abci.Event{
{Type: evmtypes.EventTypeEthereumTx, Attributes: []abci.EventAttribute{
{Key: "ethereumTxHash", Value: txHash},
{Key: "txIndex", Value: "8888"},
{Key: "amount", Value: "1000"},
{Key: "txGasUsed", Value: "21000"},
{Key: "txHash", Value: ""},
{Key: "recipient", Value: "0x775b87ef5D82ca211811C1a02CE0fE0CA3a455d7"},
}},
{
Type: "message", Attributes: []abci.EventAttribute{
{Key: "sender", Value: "0x735b14BB79463307AAcBED86DAf3322B1e6226aB"},
{Key: "txType", Value: "88"},
{Key: "txNonce", Value: "1"},
},
},
},
}
}

// buildFormattedBlock returns a formatted block for testing
func (suite *BackendTestSuite) buildFormattedBlock(
blockRes *tmrpctypes.ResultBlockResults,
Expand Down
119 changes: 113 additions & 6 deletions rpc/backend/blocks_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (

sdkmath "cosmossdk.io/math"
"github.com/cometbft/cometbft/abci/types"
"github.com/cometbft/cometbft/libs/bytes"
tmrpctypes "github.com/cometbft/cometbft/rpc/core/types"
tmtypes "github.com/cometbft/cometbft/types"
sdk "github.com/cosmos/cosmos-sdk/types"
Expand All @@ -19,6 +20,7 @@ import (

"github.com/zeta-chain/zetacore/rpc/backend/mocks"
ethrpc "github.com/zeta-chain/zetacore/rpc/types"
"github.com/zeta-chain/zetacore/testutil/sample"
)

func (suite *BackendTestSuite) TestBlockNumber() {
Expand Down Expand Up @@ -140,7 +142,7 @@ func (suite *BackendTestSuite) TestGetBlockByNumber() {
func(blockNum ethrpc.BlockNumber, baseFee sdkmath.Int, validator sdk.AccAddress, txBz []byte) {
height := blockNum.Int64()
client := suite.backend.clientCtx.Client.(*mocks.Client)
resBlock, _ = RegisterBlock(client, height, txBz)
resBlock, _ = RegisterBlock(client, height, []tmtypes.Tx{txBz})
RegisterBlockResultsError(client, blockNum.Int64())
},
true,
Expand All @@ -157,7 +159,7 @@ func (suite *BackendTestSuite) TestGetBlockByNumber() {
func(blockNum ethrpc.BlockNumber, baseFee sdkmath.Int, validator sdk.AccAddress, txBz []byte) {
height := blockNum.Int64()
client := suite.backend.clientCtx.Client.(*mocks.Client)
resBlock, _ = RegisterBlock(client, height, txBz)
resBlock, _ = RegisterBlock(client, height, []tmtypes.Tx{txBz})
blockRes, _ = RegisterBlockResults(client, blockNum.Int64())
RegisterConsensusParams(client, height)

Expand All @@ -179,7 +181,7 @@ func (suite *BackendTestSuite) TestGetBlockByNumber() {
func(blockNum ethrpc.BlockNumber, baseFee sdkmath.Int, validator sdk.AccAddress, txBz []byte) {
height := blockNum.Int64()
client := suite.backend.clientCtx.Client.(*mocks.Client)
resBlock, _ = RegisterBlock(client, height, txBz)
resBlock, _ = RegisterBlock(client, height, []tmtypes.Tx{txBz})
blockRes, _ = RegisterBlockResults(client, blockNum.Int64())
RegisterConsensusParams(client, height)

Expand Down Expand Up @@ -497,7 +499,7 @@ func (suite *BackendTestSuite) TestGetBlockTransactionCountByNumber() {
func(blockNum ethrpc.BlockNumber) {
height := blockNum.Int64()
client := suite.backend.clientCtx.Client.(*mocks.Client)
RegisterBlock(client, height, bz)
RegisterBlock(client, height, []tmtypes.Tx{bz})
RegisterBlockResults(client, height)
},
hexutil.Uint(1),
Expand Down Expand Up @@ -1268,7 +1270,7 @@ func (suite *BackendTestSuite) TestHeaderByNumber() {
func(blockNum ethrpc.BlockNumber, baseFee sdkmath.Int) {
height := blockNum.Int64()
client := suite.backend.clientCtx.Client.(*mocks.Client)
expResultBlock, _ = RegisterBlock(client, height, bz)
expResultBlock, _ = RegisterBlock(client, height, []tmtypes.Tx{bz})
RegisterBlockResults(client, height)

queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient)
Expand Down Expand Up @@ -1471,7 +1473,7 @@ func (suite *BackendTestSuite) TestEthBlockByNumber() {
func(blockNum ethrpc.BlockNumber) {
height := blockNum.Int64()
client := suite.backend.clientCtx.Client.(*mocks.Client)
RegisterBlock(client, height, bz)
RegisterBlock(client, height, []tmtypes.Tx{bz})

RegisterBlockResults(client, blockNum.Int64())
queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient)
Expand Down Expand Up @@ -1613,3 +1615,108 @@ func (suite *BackendTestSuite) TestEthBlockFromTendermintBlock() {
})
}
}

func (suite *BackendTestSuite) TestEthAndSyntheticMsgsFromTendermintBlock() {
// synthetic tx
hash := sample.Hash().Hex()
tx, txRes := suite.buildSyntheticTxResult(hash)

// real tx
msgEthereumTx, _ := suite.buildEthereumTx()
realTx := suite.signAndEncodeEthTx(msgEthereumTx)

suite.backend.indexer = nil
// block contains block real and synthetic tx
emptyBlock := tmtypes.MakeBlock(1, []tmtypes.Tx{realTx, tx}, nil, nil)
emptyBlock.ChainID = ChainID
blockHash := common.BigToHash(big.NewInt(1)).Bytes()
resBlock := &tmrpctypes.ResultBlock{Block: emptyBlock, BlockID: tmtypes.BlockID{Hash: bytes.HexBytes(blockHash)}}
blockRes := &tmrpctypes.ResultBlockResults{
Height: 1,
TxsResults: []*types.ResponseDeliverTx{{}, &txRes},
}

// both real and synthetic should be returned
msgs, additionals := suite.backend.EthMsgsFromTendermintBlock(resBlock, blockRes)
suite.Require().Equal(2, len(msgs))
suite.Require().Equal(2, len(additionals))

suite.Require().Nil(additionals[0])
suite.Require().NotNil(additionals[1])

suite.Require().Equal(msgEthereumTx.Hash, msgs[0].Hash)
suite.Require().Equal(hash, msgs[1].Hash)
}

func (suite *BackendTestSuite) TestEthAndSyntheticEthBlockByNumber() {
// synthetic tx
hash := sample.Hash().Hex()
tx, txRes := suite.buildSyntheticTxResult(hash)

// real tx
msgEthereumTx, _ := suite.buildEthereumTx()
realTx := suite.signAndEncodeEthTx(msgEthereumTx)

suite.backend.indexer = nil
client := suite.backend.clientCtx.Client.(*mocks.Client)
queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient)
// block contains block real and synthetic tx
RegisterBlock(client, 1, []tmtypes.Tx{realTx, tx})
RegisterBlockResultsWithTxResults(client, 1, []*types.ResponseDeliverTx{{}, &txRes})
RegisterBaseFee(queryClient, sdk.NewInt(1))

// only real should be returned
block, err := suite.backend.EthBlockByNumber(1)
suite.Require().NoError(err)
suite.Require().Equal(1, len(block.Transactions()))
suite.Require().Equal(msgEthereumTx.Hash, block.Transactions()[0].Hash().String())
}

func (suite *BackendTestSuite) TestEthAndSyntheticGetBlockByNumber() {
// synthetic tx
hash := sample.Hash().Hex()
tx, txRes := suite.buildSyntheticTxResult(hash)

// real tx
msgEthereumTx, _ := suite.buildEthereumTx()
realTx := suite.signAndEncodeEthTx(msgEthereumTx)

suite.backend.indexer = nil
client := suite.backend.clientCtx.Client.(*mocks.Client)
queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient)
// block contains block real and synthetic tx
RegisterBlock(client, 1, []tmtypes.Tx{realTx, tx})
RegisterBlockResultsWithTxResults(client, 1, []*types.ResponseDeliverTx{{}, &txRes})
RegisterBaseFee(queryClient, sdk.NewInt(1))
RegisterValidatorAccount(queryClient, sdk.AccAddress(common.Address{}.Bytes()))
RegisterConsensusParams(client, 1)

// both real and synthetic should be returned
block, err := suite.backend.GetBlockByNumber(1, false)
suite.Require().NoError(err)

transactions := block["transactions"].([]interface{})
suite.Require().Equal(2, len(transactions))
suite.Require().Equal(common.HexToHash(msgEthereumTx.Hash), transactions[0])
suite.Require().Equal(common.HexToHash(hash), transactions[1])

// both real and synthetic should be returned
block, err = suite.backend.GetBlockByNumber(1, true)
suite.Require().NoError(err)

transactions = block["transactions"].([]interface{})
suite.Require().Equal(2, len(transactions))
resRealTx := transactions[0].(*ethrpc.RPCTransaction)
suite.Require().Equal(common.HexToHash(msgEthereumTx.Hash), resRealTx.Hash)
resSyntheticTx := transactions[1].(*ethrpc.RPCTransaction)
suite.Require().Equal(common.HexToHash(hash), resSyntheticTx.Hash)

suite.Require().Equal(hash, resSyntheticTx.Hash.Hex())
suite.Require().Equal("0x735b14BB79463307AAcBED86DAf3322B1e6226aB", resSyntheticTx.From.Hex())
suite.Require().Equal("0x775b87ef5D82ca211811C1a02CE0fE0CA3a455d7", resSyntheticTx.To.Hex())
suite.Require().Equal("0x58", resSyntheticTx.Type.String())
suite.Require().Equal("0x1", resSyntheticTx.Nonce.String())
suite.Require().Nil(resSyntheticTx.V)
suite.Require().Nil(resSyntheticTx.R)
suite.Require().Nil(resSyntheticTx.S)
}
5 changes: 3 additions & 2 deletions rpc/backend/call_tx_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"fmt"
"math/big"

tmtypes "github.com/cometbft/cometbft/types"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
Expand Down Expand Up @@ -400,7 +401,7 @@ func (suite *BackendTestSuite) TestDoCall() {
func() {
client := suite.backend.clientCtx.Client.(*mocks.Client)
queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient)
RegisterBlock(client, 1, bz)
RegisterBlock(client, 1, []tmtypes.Tx{bz})
RegisterEthCallError(
queryClient,
&evmtypes.EthCallRequest{Args: argsBz, ChainId: suite.backend.chainID.Int64()},
Expand All @@ -416,7 +417,7 @@ func (suite *BackendTestSuite) TestDoCall() {
func() {
client := suite.backend.clientCtx.Client.(*mocks.Client)
queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient)
RegisterBlock(client, 1, bz)
RegisterBlock(client, 1, []tmtypes.Tx{bz})
RegisterEthCall(
queryClient,
&evmtypes.EthCallRequest{Args: argsBz, ChainId: suite.backend.chainID.Int64()},
Expand Down
34 changes: 16 additions & 18 deletions rpc/backend/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package backend

import (
"context"
"math/big"
"testing"

abci "github.com/cometbft/cometbft/abci/types"
Expand Down Expand Up @@ -38,6 +39,12 @@ func RegisterTxSearch(client *mocks.Client, query string, txBz []byte) {
Return(&tmrpctypes.ResultTxSearch{Txs: resulTxs, TotalCount: 1}, nil)
}

func RegisterTxSearchWithTxResult(client *mocks.Client, query string, txBz []byte, res abci.ResponseDeliverTx) {
resulTxs := []*tmrpctypes.ResultTx{{Tx: txBz, Height: 1, TxResult: res}}
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)
Expand Down Expand Up @@ -94,36 +101,26 @@ func RegisterStatusError(client *mocks.Client) {
}

// Block
func RegisterBlockMultipleTxs(
client *mocks.Client,
height int64,
txs []types.Tx,
) (*tmrpctypes.ResultBlock, error) {
block := types.MakeBlock(height, txs, nil, nil)
block.ChainID = ChainID
resBlock := &tmrpctypes.ResultBlock{Block: block}
client.On("Block", rpc.ContextWithHeight(height), mock.AnythingOfType("*int64")).Return(resBlock, nil)
return resBlock, nil
}

func RegisterBlock(
client *mocks.Client,
height int64,
tx []byte,
txs []types.Tx,
) (*tmrpctypes.ResultBlock, error) {
// without tx
if tx == nil {
if len(txs) == 0 {
emptyBlock := types.MakeBlock(height, []types.Tx{}, nil, nil)
emptyBlock.ChainID = ChainID
resBlock := &tmrpctypes.ResultBlock{Block: emptyBlock}
blockHash := common.BigToHash(big.NewInt(height)).Bytes()
resBlock := &tmrpctypes.ResultBlock{Block: emptyBlock, BlockID: types.BlockID{Hash: bytes.HexBytes(blockHash)}}
client.On("Block", rpc.ContextWithHeight(height), mock.AnythingOfType("*int64")).Return(resBlock, nil)
return resBlock, nil
}

// with tx
block := types.MakeBlock(height, []types.Tx{tx}, nil, nil)
block := types.MakeBlock(height, txs, nil, nil)
block.ChainID = ChainID
resBlock := &tmrpctypes.ResultBlock{Block: block}
blockHash := common.BigToHash(big.NewInt(height)).Bytes()
resBlock := &tmrpctypes.ResultBlock{Block: block, BlockID: types.BlockID{Hash: bytes.HexBytes(blockHash)}}
client.On("Block", rpc.ContextWithHeight(height), mock.AnythingOfType("*int64")).Return(resBlock, nil)
return resBlock, nil
}
Expand Down Expand Up @@ -154,7 +151,8 @@ func TestRegisterBlock(t *testing.T) {

emptyBlock := types.MakeBlock(height, []types.Tx{}, nil, nil)
emptyBlock.ChainID = ChainID
resBlock := &tmrpctypes.ResultBlock{Block: emptyBlock}
blockHash := common.BigToHash(big.NewInt(height)).Bytes()
resBlock := &tmrpctypes.ResultBlock{Block: emptyBlock, BlockID: types.BlockID{Hash: blockHash}}
require.Equal(t, resBlock, res)
require.NoError(t, err)
}
Expand Down
15 changes: 12 additions & 3 deletions rpc/backend/evm_query_client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,14 +39,23 @@ func RegisterTraceTransactionWithPredecessors(
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, ChainId: 7001}).
queryClient.On(
"TraceTx",
rpc.ContextWithHeight(1),
&evmtypes.QueryTraceTxRequest{
Msg: msgEthTx,
BlockHash: "0000000000000000000000000000000000000000000000000000000000000001",
BlockNumber: 1,
Predecessors: predecessors,
ChainId: 7001,
},
).
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, Predecessors: []*evmtypes.MsgEthereumTx{}, ChainId: 7001}).
queryClient.On("TraceTx", rpc.ContextWithHeight(1), &evmtypes.QueryTraceTxRequest{Msg: msgEthTx, BlockHash: "0000000000000000000000000000000000000000000000000000000000000001", BlockNumber: 1, Predecessors: []*evmtypes.MsgEthereumTx{}, ChainId: 7001}).
Return(&evmtypes.QueryTraceTxResponse{Data: data}, nil)
}

Expand Down
Loading
Loading