Skip to content
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 @@ -52,6 +52,7 @@ Ref: https://keepachangelog.com/en/1.0.0/
- (ante) [#64](https://github.com/EscanBE/evermint/pull/64) Add EvmTx sender override protection in ante and correct changelog
- (lint) [#83](https://github.com/EscanBE/evermint/pull/83) Update lint rules and fix lint issues
- (cleanup) [#92](https://github.com/EscanBE/evermint/pull/92) Cleanup un-used `x/evm` and `x/erc20` types
- (indexer) [#96](https://github.com/EscanBE/evermint/pull/96) Make EVMTxIndexer mandatory service, starts before Json-RPC

### Bug Fixes

Expand Down
60 changes: 57 additions & 3 deletions indexer/kv_indexer.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package indexer
import (
"fmt"
"github.com/EscanBE/evermint/v12/constants"
"sync"

errorsmod "cosmossdk.io/errors"
rpctypes "github.com/EscanBE/evermint/v12/rpc/types"
Expand Down Expand Up @@ -35,11 +36,23 @@ type KVIndexer struct {
db dbm.DB
logger log.Logger
clientCtx client.Context

mu *sync.RWMutex
ready bool
lastRequestIndexedBlock int64 // indexer does not index empty block so LastIndexedBlock() might be different from last request indexed block.
}

// NewKVIndexer creates the KVIndexer
func NewKVIndexer(db dbm.DB, logger log.Logger, clientCtx client.Context) *KVIndexer {
return &KVIndexer{db, logger, clientCtx}
return &KVIndexer{
db: db,
logger: logger,
clientCtx: clientCtx,
lastRequestIndexedBlock: -1,

mu: &sync.RWMutex{},
ready: false,
}
}

// IndexBlock indexes all ETH Txs of the block.
Expand Down Expand Up @@ -121,9 +134,28 @@ func (kv *KVIndexer) IndexBlock(block *tmtypes.Block, txResults []*abci.Response
if err := batch.Write(); err != nil {
return errorsmod.Wrapf(err, "IndexBlock %d, write batch", block.Height)
}

kv.mu.Lock()
defer kv.mu.Unlock()
if kv.lastRequestIndexedBlock < height {
kv.lastRequestIndexedBlock = height
}

return nil
}

func (kv *KVIndexer) Ready() {
kv.mu.Lock()
defer kv.mu.Unlock()
kv.ready = true
}

func (kv *KVIndexer) IsReady() bool {
kv.mu.RLock()
defer kv.mu.RUnlock()
return kv.ready
}

// LastIndexedBlock returns the last block number which was indexed and flushed into database.
// Returns -1 if db is empty.
func (kv *KVIndexer) LastIndexedBlock() (int64, error) {
Expand All @@ -137,6 +169,28 @@ func (kv *KVIndexer) FirstIndexedBlock() (int64, error) {

// GetByTxHash finds eth tx by eth tx hash
func (kv *KVIndexer) GetByTxHash(hash common.Hash) (*evertypes.TxResult, error) {
return kv.getByTxHash(hash)
}

// GetByBlockAndIndex finds eth tx by block number and eth tx index
func (kv *KVIndexer) GetByBlockAndIndex(blockNumber int64, txIndex int32) (*evertypes.TxResult, error) {
return kv.getByBlockAndIndex(blockNumber, txIndex)
}

// GetLastRequestIndexedBlock returns the block height of the latest success called to IndexBlock()
func (kv *KVIndexer) GetLastRequestIndexedBlock() (int64, error) {
kv.mu.RLock()
defer kv.mu.RUnlock()

if kv.lastRequestIndexedBlock == -1 {
return LoadLastBlock(kv.db)
}

return kv.lastRequestIndexedBlock, nil
}

// getByTxHash finds eth tx by eth tx hash
func (kv *KVIndexer) getByTxHash(hash common.Hash) (*evertypes.TxResult, error) {
bz, err := kv.db.Get(TxHashKey(hash))
if err != nil {
return nil, errorsmod.Wrapf(err, "GetByTxHash %s", hash.Hex())
Expand All @@ -152,15 +206,15 @@ func (kv *KVIndexer) GetByTxHash(hash common.Hash) (*evertypes.TxResult, error)
}

// GetByBlockAndIndex finds eth tx by block number and eth tx index
func (kv *KVIndexer) GetByBlockAndIndex(blockNumber int64, txIndex int32) (*evertypes.TxResult, error) {
func (kv *KVIndexer) getByBlockAndIndex(blockNumber int64, txIndex int32) (*evertypes.TxResult, error) {
bz, err := kv.db.Get(TxIndexKey(blockNumber, txIndex))
if err != nil {
return nil, errorsmod.Wrapf(err, "GetByBlockAndIndex %d %d", blockNumber, txIndex)
}
if len(bz) == 0 {
return nil, fmt.Errorf("tx not found, block: %d, eth-index: %d", blockNumber, txIndex)
}
return kv.GetByTxHash(common.BytesToHash(bz))
return kv.getByTxHash(common.BytesToHash(bz))
}

// TxHashKey returns the key for db entry: `tx hash -> tx result struct`
Expand Down
3 changes: 3 additions & 0 deletions indexer/kv_indexer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,9 @@ func TestKVIndexer(t *testing.T) {

err = idxer.IndexBlock(tc.block, tc.blockResult)
require.NoError(t, err)

idxer.Ready()

if !tc.expSuccess {
first, err := idxer.FirstIndexedBlock()
require.NoError(t, err)
Expand Down
2 changes: 2 additions & 0 deletions integration_test_util/chain_suite.go
Original file line number Diff line number Diff line change
Expand Up @@ -623,6 +623,8 @@ func (suite *ChainIntegrationTestSuite) triggerEvmIndexer(latestHeight int64, bl
err = suite.EvmTxIndexer.IndexBlock(tmBlk, tmAbciResponse.DeliverTxs)
suite.Require().NoErrorf(err, "failed to index block %d", ch)
}

suite.EvmTxIndexer.Ready()
}

// GetBlockStoreAndStateStore returns blockStore and stateStore if Tendermint is Enabled.
Expand Down
39 changes: 25 additions & 14 deletions rpc/backend/account_info_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,15 @@ import (
"fmt"
"math/big"

"github.com/EscanBE/evermint/v12/rpc/backend/mocks"
rpctypes "github.com/EscanBE/evermint/v12/rpc/types"
utiltx "github.com/EscanBE/evermint/v12/testutil/tx"
evmtypes "github.com/EscanBE/evermint/v12/x/evm/types"
tmrpcclient "github.com/cometbft/cometbft/rpc/client"
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"
"google.golang.org/grpc/metadata"

"github.com/EscanBE/evermint/v12/rpc/backend/mocks"
rpctypes "github.com/EscanBE/evermint/v12/rpc/types"
utiltx "github.com/EscanBE/evermint/v12/testutil/tx"
evmtypes "github.com/EscanBE/evermint/v12/x/evm/types"
)

func (suite *BackendTestSuite) TestGetCode() {
Expand Down Expand Up @@ -367,9 +365,8 @@ func (suite *BackendTestSuite) TestGetTransactionCount() {
false,
rpctypes.NewBlockNumber(big.NewInt(1)),
func(addr common.Address, bn rpctypes.BlockNumber) {
var header metadata.MD
queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient)
RegisterParams(queryClient, &header, 1)
indexer := suite.backend.indexer.(*mocks.EVMTxIndexer)
RegisterIndexerGetLastRequestIndexedBlock(indexer, 1)
},
true,
hexutil.Uint64(0),
Expand All @@ -379,22 +376,33 @@ func (suite *BackendTestSuite) TestGetTransactionCount() {
false,
rpctypes.NewBlockNumber(big.NewInt(10000)),
func(addr common.Address, bn rpctypes.BlockNumber) {
var header metadata.MD
queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient)
RegisterParams(queryClient, &header, 1)
indexer := suite.backend.indexer.(*mocks.EVMTxIndexer)
RegisterIndexerGetLastRequestIndexedBlock(indexer, 1)
},
false,
hexutil.Uint64(0),
},
{
name: "fail - indexer returns error",
accExists: false,
blockNum: rpctypes.NewBlockNumber(big.NewInt(10000)),
registerMock: func(addr common.Address, bn rpctypes.BlockNumber) {
indexer := suite.backend.indexer.(*mocks.EVMTxIndexer)
RegisterIndexerGetLastRequestIndexedBlockErr(indexer)
},
expPass: false,
expTxCount: 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(
Expand All @@ -403,10 +411,13 @@ func (suite *BackendTestSuite) TestGetTransactionCount() {
// tmrpcclient.ABCIQueryOptions{Height: int64(1), Prove: false},
// account,
// )
//
// indexer := suite.backend.indexer.(*mocks.EVMTxIndexer)
// RegisterIndexerGetLastRequestIndexedBlock(indexer, 1)
// },
// true,
// hexutil.Uint64(0),
// },
//},
}
for _, tc := range testCases {
suite.Run(fmt.Sprintf("Case %s", tc.name), func() {
Expand Down
4 changes: 4 additions & 0 deletions rpc/backend/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,10 @@ func NewBackend(
panic(err)
}

if indexer == nil {
panic("indexer is required")
}

return &Backend{
ctx: context.Background(),
clientCtx: clientCtx,
Expand Down
32 changes: 8 additions & 24 deletions rpc/backend/blocks.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,45 +2,29 @@ package backend

import (
"fmt"
tmrpcclient "github.com/cometbft/cometbft/rpc/client"
"math"
"math/big"
"strconv"

rpctypes "github.com/EscanBE/evermint/v12/rpc/types"
evmtypes "github.com/EscanBE/evermint/v12/x/evm/types"
tmrpcclient "github.com/cometbft/cometbft/rpc/client"
tmrpctypes "github.com/cometbft/cometbft/rpc/core/types"
sdk "github.com/cosmos/cosmos-sdk/types"
grpctypes "github.com/cosmos/cosmos-sdk/types/grpc"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
ethtypes "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/trie"
"github.com/pkg/errors"
"google.golang.org/grpc"
"google.golang.org/grpc/metadata"
"math"
"math/big"
)

// BlockNumber returns the current block number in abci app state. Because abci
// app state could lag behind from tendermint latest block, it's more stable for
// the client to use the latest block number in abci app state than tendermint
// rpc.
// BlockNumber returns the current block number, based on indexed block state of the EVMTxIndexer.
func (b *Backend) BlockNumber() (hexutil.Uint64, error) {
// do any grpc query, ignore the response and use the returned block height
var header metadata.MD
_, err := b.queryClient.Params(b.ctx, &evmtypes.QueryParamsRequest{}, grpc.Header(&header))
height, err := b.indexer.GetLastRequestIndexedBlock()
if err != nil {
return hexutil.Uint64(0), err
return 0, err
}

blockHeightHeader := header.Get(grpctypes.GRPCBlockHeightHeader)
if headerLen := len(blockHeightHeader); headerLen != 1 {
return 0, fmt.Errorf("unexpected '%s' gRPC header length; got %d, expected: %d", grpctypes.GRPCBlockHeightHeader, headerLen, 1)
}

height, err := strconv.ParseUint(blockHeightHeader[0], 10, 64)
if err != nil {
return 0, fmt.Errorf("failed to parse block height: %w", err)
if height < 1 {
return 0, fmt.Errorf("no block indexed yet")
}

if height > math.MaxInt64 {
Expand Down
Loading