Skip to content

Commit

Permalink
add more test cases to cover indexer
Browse files Browse the repository at this point in the history
  • Loading branch information
beer-1 committed Nov 22, 2024
1 parent 7c0dcf6 commit de36cfd
Show file tree
Hide file tree
Showing 8 changed files with 289 additions and 13 deletions.
4 changes: 3 additions & 1 deletion app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -602,7 +602,9 @@ func (app *MinitiaApp) Close() error {
return err
}

app.evmIndexer.Stop()
if app.evmIndexer != nil {
app.evmIndexer.Stop()
}

Check warning on line 607 in app/app.go

View check run for this annotation

Codecov / codecov/patch

app/app.go#L605-L607

Added lines #L605 - L607 were not covered by tests

return nil
}
Expand Down
60 changes: 60 additions & 0 deletions indexer/abci_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package indexer_test

import (
"math/big"
"sync"
"testing"

"github.com/stretchr/testify/require"
Expand All @@ -16,6 +17,7 @@ import (

func Test_ListenFinalizeBlock(t *testing.T) {
app, indexer, addrs, privKeys := setupIndexer(t)
defer app.Close()

tx, evmTxHash := generateCreateERC20Tx(t, app, privKeys[0])
finalizeReq, finalizeRes := executeTxs(t, app, tx)
Expand Down Expand Up @@ -56,4 +58,62 @@ func Test_ListenFinalizeBlock(t *testing.T) {
evmTx, err = indexer.TxByHash(ctx, evmTxHash)
require.NoError(t, err)
require.NotNil(t, evmTx)

// check the block header is indexed
header, err := indexer.BlockHeaderByNumber(ctx, uint64(finalizeReq.Height))
require.NoError(t, err)
require.NotNil(t, header)
require.Equal(t, finalizeReq.Height, header.Number.Int64())

}

func Test_ListenFinalizeBlock_Subscribe(t *testing.T) {
app, indexer, _, privKeys := setupIndexer(t)
defer app.Close()

blockChan, logsChan, pendChan := indexer.Subscribe()
close(pendChan)

tx, evmTxHash := generateCreateERC20Tx(t, app, privKeys[0])
finalizeReq, finalizeRes := executeTxs(t, app, tx)
checkTxResult(t, finalizeRes.TxResults[0], true)

events := finalizeRes.TxResults[0].Events
createEvent := events[len(events)-3]
require.Equal(t, evmtypes.EventTypeContractCreated, createEvent.GetType())

contractAddr, err := hexutil.Decode(createEvent.Attributes[0].Value)
require.NoError(t, err)

// listen finalize block
ctx, err := app.CreateQueryContext(0, false)
require.NoError(t, err)

wg := sync.WaitGroup{}
wg.Add(2)
go func() {
for {
select {
case block := <-blockChan:
require.NotNil(t, block)
require.Equal(t, finalizeReq.Height, block.Number.Int64())
wg.Done()
case logs := <-logsChan:
require.NotNil(t, logs)

for _, log := range logs {
if log.Address == common.BytesToAddress(contractAddr) {
require.Equal(t, evmTxHash, log.TxHash)
require.Equal(t, uint64(finalizeReq.Height), log.BlockNumber)
wg.Done()
}
}
}
}
}()

err = indexer.ListenFinalizeBlock(ctx.WithBlockGasMeter(storetypes.NewInfiniteGasMeter()), *finalizeReq, *finalizeRes)
require.NoError(t, err)

wg.Wait()
}
2 changes: 1 addition & 1 deletion indexer/indexer.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ type EVMIndexer interface {

// tx receipt
TxReceiptByHash(ctx context.Context, hash common.Hash) (*coretypes.Receipt, error)
IterateBlockTxRecepts(ctx context.Context, blockHeight uint64, cb func(tx *coretypes.Receipt) (bool, error)) error
IterateBlockTxReceipts(ctx context.Context, blockHeight uint64, cb func(tx *coretypes.Receipt) (bool, error)) error

// block
BlockHashToNumber(ctx context.Context, hash common.Hash) (uint64, error)
Expand Down
44 changes: 44 additions & 0 deletions indexer/mempool_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package indexer_test

import (
"sync"
"testing"

"github.com/stretchr/testify/require"

"github.com/cosmos/cosmos-sdk/types/mempool"
)

func Test_Mempool_Subscribe(t *testing.T) {
app, indexer, _, privKeys := setupIndexer(t)
defer app.Close()

blockChan, logsChan, pendChan := indexer.Subscribe()
close(blockChan)
close(logsChan)

tx, evmTxHash := generateCreateERC20Tx(t, app, privKeys[0])

wg := sync.WaitGroup{}
wg.Add(1)
go func() {
pendingTx := <-pendChan
require.NotNil(t, pendingTx)
require.Equal(t, evmTxHash, pendingTx.Hash)
wg.Done()
}()

noopMempool := &mempool.NoOpMempool{}
mempool := indexer.MempoolWrapper(noopMempool)

// insert tx into mempool
ctx, err := app.CreateQueryContext(0, false)
require.NoError(t, err)
err = mempool.Insert(ctx, tx)
require.NoError(t, err)

rpcTx := indexer.TxInMempool(evmTxHash)
require.Equal(t, evmTxHash, rpcTx.Hash)

wg.Wait()
}
2 changes: 1 addition & 1 deletion indexer/reader.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ func (e *EVMIndexerImpl) IterateBlockTxs(ctx context.Context, blockHeight uint64
}

// IterateBlockTxs implements EVMIndexer.
func (e *EVMIndexerImpl) IterateBlockTxRecepts(ctx context.Context, blockHeight uint64, cb func(tx *coretypes.Receipt) (bool, error)) error {
func (e *EVMIndexerImpl) IterateBlockTxReceipts(ctx context.Context, blockHeight uint64, cb func(tx *coretypes.Receipt) (bool, error)) error {
return e.BlockAndIndexToTxHashMap.Walk(ctx, collections.NewPrefixedPairRange[uint64, uint64](blockHeight), func(key collections.Pair[uint64, uint64], txHashBz []byte) (bool, error) {
txHash := common.BytesToHash(txHashBz)
txRecept, err := e.TxReceiptByHash(ctx, txHash)
Expand Down
154 changes: 154 additions & 0 deletions indexer/reader_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
package indexer_test

import (
"math/big"
"testing"

"github.com/stretchr/testify/require"

cmttypes "github.com/cometbft/cometbft/types"

storetypes "cosmossdk.io/store/types"

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
coretypes "github.com/ethereum/go-ethereum/core/types"

rpctypes "github.com/initia-labs/minievm/jsonrpc/types"
evmtypes "github.com/initia-labs/minievm/x/evm/types"
)

func Test_Reader(t *testing.T) {
app, indexer, addrs, privKeys := setupIndexer(t)
defer app.Close()

tx, evmTxHash := generateCreateERC20Tx(t, app, privKeys[0])
finalizeReq, finalizeRes := executeTxs(t, app, tx)
checkTxResult(t, finalizeRes.TxResults[0], true)

events := finalizeRes.TxResults[0].Events
createEvent := events[len(events)-3]
require.Equal(t, evmtypes.EventTypeContractCreated, createEvent.GetType())

contractAddr, err := hexutil.Decode(createEvent.Attributes[0].Value)
require.NoError(t, err)

// listen finalize block
ctx, err := app.CreateQueryContext(0, false)
require.NoError(t, err)

err = indexer.ListenFinalizeBlock(ctx.WithBlockGasMeter(storetypes.NewInfiniteGasMeter()), *finalizeReq, *finalizeRes)
require.NoError(t, err)

// check the tx is indexed
evmTx, err := indexer.TxByHash(ctx, evmTxHash)
require.NoError(t, err)
require.NotNil(t, evmTx)

// mint 1_000_000 tokens to the first address
tx, evmTxHash = generateMintERC20Tx(t, app, privKeys[0], common.BytesToAddress(contractAddr), addrs[0], new(big.Int).SetUint64(1_000_000_000_000))
tx2, evmTxHash2 := generateTransferERC20Tx(t, app, privKeys[0], common.BytesToAddress(contractAddr), addrs[1], new(big.Int).SetUint64(1_000_000), 2)
finalizeReq, finalizeRes = executeTxs(t, app, tx, tx2)
checkTxResult(t, finalizeRes.TxResults[0], true)
checkTxResult(t, finalizeRes.TxResults[1], true)

txBytes, err := app.TxEncode(tx)
require.NoError(t, err)
txBytes2, err := app.TxEncode(tx2)
require.NoError(t, err)

cmtTx := cmttypes.Tx(txBytes)
cosmosTxHash := cmtTx.Hash()
cmtTx2 := cmttypes.Tx(txBytes2)
cosmosTxHash2 := cmtTx2.Hash()

// listen finalize block
ctx, err = app.CreateQueryContext(0, false)
require.NoError(t, err)

err = indexer.ListenFinalizeBlock(ctx.WithBlockGasMeter(storetypes.NewInfiniteGasMeter()), *finalizeReq, *finalizeRes)
require.NoError(t, err)

// check the tx is indexed
evmTx, err = indexer.TxByHash(ctx, evmTxHash)
require.NoError(t, err)
require.NotNil(t, evmTx)
evmTx, err = indexer.TxByHash(ctx, evmTxHash2)
require.NoError(t, err)
require.NotNil(t, evmTx)

// check the block header is indexed
header, err := indexer.BlockHeaderByNumber(ctx, uint64(finalizeReq.Height))
require.NoError(t, err)
require.NotNil(t, header)
require.Equal(t, finalizeReq.Height, header.Number.Int64())

// check tx hash by block and index
txHash, err := indexer.TxHashByBlockAndIndex(ctx, uint64(finalizeReq.Height), 1)
require.NoError(t, err)
require.Equal(t, evmTxHash, txHash)

txHash, err = indexer.TxHashByBlockAndIndex(ctx, uint64(finalizeReq.Height), 2)
require.NoError(t, err)
require.Equal(t, evmTxHash2, txHash)

// iterate block txs
count := 0
err = indexer.IterateBlockTxs(ctx, uint64(finalizeReq.Height), func(tx *rpctypes.RPCTransaction) (bool, error) {
count++
if count == 1 {
require.Equal(t, evmTxHash, tx.Hash)
} else if count == 2 {
require.Equal(t, evmTxHash2, tx.Hash)
}
return false, nil
})
require.NoError(t, err)
require.Equal(t, 2, count)

// receipt by hash
receipt1, err := indexer.TxReceiptByHash(ctx, evmTxHash)
require.NoError(t, err)
require.NotNil(t, receipt1)

// receipt by hash
receipt2, err := indexer.TxReceiptByHash(ctx, evmTxHash2)
require.NoError(t, err)
require.NotNil(t, receipt2)

// iterate block tx receipts
count = 0
err = indexer.IterateBlockTxReceipts(ctx, uint64(finalizeReq.Height), func(receipt *coretypes.Receipt) (bool, error) {
count++
if count == 1 {
require.Equal(t, receipt1, receipt)
} else if count == 2 {
require.Equal(t, receipt2, receipt)
}
return false, nil
})
require.NoError(t, err)

// block hash to number
blockNumber, err := indexer.BlockHashToNumber(ctx, header.Hash())
require.NoError(t, err)
require.Equal(t, uint64(finalizeReq.Height), blockNumber)

// cosmos tx hash
hash, err := indexer.CosmosTxHashByTxHash(ctx, evmTxHash)
require.NoError(t, err)
require.Equal(t, cosmosTxHash, hash)

hash, err = indexer.CosmosTxHashByTxHash(ctx, evmTxHash2)
require.NoError(t, err)
require.Equal(t, cosmosTxHash2, hash)

// tx hash by cosmos tx hash
txHash, err = indexer.TxHashByCosmosTxHash(ctx, cosmosTxHash)
require.NoError(t, err)
require.Equal(t, evmTxHash, txHash)

txHash, err = indexer.TxHashByCosmosTxHash(ctx, cosmosTxHash2)
require.NoError(t, err)
require.Equal(t, evmTxHash2, txHash)
}
34 changes: 25 additions & 9 deletions indexer/tx_helper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,21 +56,27 @@ func generateTx(
privKey *ecdsa.PrivateKey,
to *common.Address,
inputBz []byte,
seqNum ...uint64,
) (sdk.Tx, common.Hash) {
ctx, err := app.CreateQueryContext(0, false)
require.NoError(t, err)

gasLimit := new(big.Int).SetUint64(1_000_000)
gasPrice := new(big.Int).SetUint64(1_000_000_000)

cosmosKey := ethsecp256k1.PrivKey{Key: crypto.FromECDSA(privKey)}
addrBz := cosmosKey.PubKey().Address()
nonce, err := app.AccountKeeper.GetSequence(ctx, sdk.AccAddress(addrBz))
require.NoError(t, err)
nonce := uint64(0)
if len(seqNum) == 0 {
cosmosKey := ethsecp256k1.PrivKey{Key: crypto.FromECDSA(privKey)}
addrBz := cosmosKey.PubKey().Address()
nonce, err = app.AccountKeeper.GetSequence(ctx, sdk.AccAddress(addrBz))
require.NoError(t, err)
} else {
nonce = seqNum[0]
}

ethChainID := evmtypes.ConvertCosmosChainIDToEthereumChainID(ctx.ChainID())
ethTx := coretypes.NewTx(&coretypes.DynamicFeeTx{
ChainID: evmtypes.ConvertCosmosChainIDToEthereumChainID(ctx.ChainID()),
ChainID: ethChainID,
Nonce: nonce,
GasTipCap: big.NewInt(0),
GasFeeCap: gasPrice,
Expand All @@ -92,7 +98,7 @@ func generateTx(
return sdkTx, signedTx.Hash()
}

func generateCreateERC20Tx(t *testing.T, app *minitiaapp.MinitiaApp, privKey *ecdsa.PrivateKey) (sdk.Tx, common.Hash) {
func generateCreateERC20Tx(t *testing.T, app *minitiaapp.MinitiaApp, privKey *ecdsa.PrivateKey, seqNum ...uint64) (sdk.Tx, common.Hash) {
ctx, err := app.CreateQueryContext(0, false)
require.NoError(t, err)

Expand All @@ -105,17 +111,27 @@ func generateCreateERC20Tx(t *testing.T, app *minitiaapp.MinitiaApp, privKey *ec
inputBz, err := abi.Pack("createERC20", "foo", "foo", uint8(6))
require.NoError(t, err)

return generateTx(t, app, privKey, &ethFactoryAddr, inputBz)
return generateTx(t, app, privKey, &ethFactoryAddr, inputBz, seqNum...)
}

func generateMintERC20Tx(t *testing.T, app *minitiaapp.MinitiaApp, privKey *ecdsa.PrivateKey, erc20Addr, recipient common.Address, amount *big.Int) (sdk.Tx, common.Hash) {
func generateMintERC20Tx(t *testing.T, app *minitiaapp.MinitiaApp, privKey *ecdsa.PrivateKey, erc20Addr, recipient common.Address, amount *big.Int, seqNum ...uint64) (sdk.Tx, common.Hash) {
abi, err := erc20.Erc20MetaData.GetAbi()
require.NoError(t, err)

inputBz, err := abi.Pack("mint", recipient, amount)
require.NoError(t, err)

return generateTx(t, app, privKey, &erc20Addr, inputBz)
return generateTx(t, app, privKey, &erc20Addr, inputBz, seqNum...)
}

func generateTransferERC20Tx(t *testing.T, app *minitiaapp.MinitiaApp, privKey *ecdsa.PrivateKey, erc20Addr, recipient common.Address, amount *big.Int, seqNum ...uint64) (sdk.Tx, common.Hash) {
abi, err := erc20.Erc20MetaData.GetAbi()
require.NoError(t, err)

inputBz, err := abi.Pack("transfer", recipient, amount)
require.NoError(t, err)

return generateTx(t, app, privKey, &erc20Addr, inputBz, seqNum...)
}

// execute txs and finalize block and commit block
Expand Down
2 changes: 1 addition & 1 deletion jsonrpc/backend/tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -454,7 +454,7 @@ func (b *JSONRPCBackend) getBlockReceipts(blockNumber uint64) ([]*coretypes.Rece
}

recepts := []*coretypes.Receipt{}
err = b.app.EVMIndexer().IterateBlockTxRecepts(queryCtx, blockNumber, func(recept *coretypes.Receipt) (bool, error) {
err = b.app.EVMIndexer().IterateBlockTxReceipts(queryCtx, blockNumber, func(recept *coretypes.Receipt) (bool, error) {

Check warning on line 457 in jsonrpc/backend/tx.go

View check run for this annotation

Codecov / codecov/patch

jsonrpc/backend/tx.go#L457

Added line #L457 was not covered by tests
recepts = append(recepts, recept)
return false, nil
})
Expand Down

0 comments on commit de36cfd

Please sign in to comment.