Skip to content

Commit

Permalink
cache on jsonrpc
Browse files Browse the repository at this point in the history
  • Loading branch information
beer-1 committed Oct 26, 2024
1 parent 63688ba commit 0ceec07
Show file tree
Hide file tree
Showing 9 changed files with 219 additions and 148 deletions.
3 changes: 1 addition & 2 deletions indexer/indexer.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,16 +29,15 @@ type EVMIndexer interface {

// tx
TxByHash(ctx context.Context, hash common.Hash) (*rpctypes.RPCTransaction, error)
TxByBlockAndIndex(ctx context.Context, blockHeight uint64, index uint64) (*rpctypes.RPCTransaction, error)
IterateBlockTxs(ctx context.Context, blockHeight uint64, cb func(tx *rpctypes.RPCTransaction) (bool, error)) error
TxHashByBlockAndIndex(ctx context.Context, blockHeight uint64, index uint64) (common.Hash, error)

// 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

// block
BlockHashToNumber(ctx context.Context, hash common.Hash) (uint64, error)
BlockHeaderByHash(ctx context.Context, hash common.Hash) (*coretypes.Header, error)
BlockHeaderByNumber(ctx context.Context, number uint64) (*coretypes.Header, error)

// cosmos tx hash
Expand Down
19 changes: 4 additions & 15 deletions indexer/reader.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,6 @@ import (
rpctypes "github.com/initia-labs/minievm/jsonrpc/types"
)

// BlockHeaderByHash implements EVMIndexer.
func (e *EVMIndexerImpl) BlockHeaderByHash(ctx context.Context, hash common.Hash) (*coretypes.Header, error) {
blockNumber, err := e.BlockHashToNumberMap.Get(ctx, hash.Bytes())
if err != nil {
return nil, err
}

return e.BlockHeaderByNumber(ctx, blockNumber)
}

// BlockHeaderByNumber implements EVMIndexer.
func (e *EVMIndexerImpl) BlockHeaderByNumber(ctx context.Context, blockNumber uint64) (*coretypes.Header, error) {
blockHeader, err := e.BlockHeaderMap.Get(ctx, blockNumber)
Expand All @@ -29,15 +19,14 @@ func (e *EVMIndexerImpl) BlockHeaderByNumber(ctx context.Context, blockNumber ui
return &blockHeader, nil
}

// TxByBlockAndIndex implements EVMIndexer.
func (e *EVMIndexerImpl) TxByBlockAndIndex(ctx context.Context, blockHeight uint64, index uint64) (*rpctypes.RPCTransaction, error) {
// TxHashByBlockAndIndex implements EVMIndexer.
func (e *EVMIndexerImpl) TxHashByBlockAndIndex(ctx context.Context, blockHeight uint64, index uint64) (common.Hash, error) {
txHashBz, err := e.BlockAndIndexToTxHashMap.Get(ctx, collections.Join(blockHeight, index))
if err != nil {
return nil, err
return common.Hash{}, err
}

txHash := common.BytesToHash(txHashBz)
return e.TxByHash(ctx, txHash)
return common.BytesToHash(txHashBz), nil
}

// TxByHash implements EVMIndexer.
Expand Down
44 changes: 38 additions & 6 deletions jsonrpc/backend/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ import (
"sync"
"time"

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/lru"
coretypes "github.com/ethereum/go-ethereum/core/types"
lrucache "github.com/hashicorp/golang-lru/v2"

"cosmossdk.io/log"
Expand All @@ -13,14 +16,24 @@ import (

"github.com/initia-labs/minievm/app"
"github.com/initia-labs/minievm/jsonrpc/config"
rpctypes "github.com/initia-labs/minievm/jsonrpc/types"
)

type JSONRPCBackend struct {
app *app.MinitiaApp
logger log.Logger

queuedTxs *lrucache.Cache[string, []byte]
historyCache *lrucache.Cache[cacheKey, processedFees]
historyCache *lru.Cache[cacheKey, processedFees]

headerCache *lru.Cache[uint64, *coretypes.Header]
blockTxsCache *lru.Cache[uint64, []*rpctypes.RPCTransaction]
blockRceiptsCache *lru.Cache[uint64, []*coretypes.Receipt]
blockHashCache *lru.Cache[common.Hash, uint64]

txLookupCache *lru.Cache[common.Hash, *rpctypes.RPCTransaction]
receiptsCache *lru.Cache[common.Hash, *coretypes.Receipt]
logsCache *lru.Cache[uint64, []*coretypes.Log]

mut sync.Mutex // mutex for accMuts
accMuts map[string]*AccMut
Expand All @@ -32,6 +45,13 @@ type JSONRPCBackend struct {
cfg config.JSONRPCConfig
}

const (
feeHistoryCacheSize = 2048
blockCacheLimit = 256
receiptsCacheLimit = 32
txLookupCacheLimit = 1024
)

// NewJSONRPCBackend creates a new JSONRPCBackend instance
func NewJSONRPCBackend(
app *app.MinitiaApp,
Expand All @@ -40,11 +60,14 @@ func NewJSONRPCBackend(
clientCtx client.Context,
cfg config.JSONRPCConfig,
) (*JSONRPCBackend, error) {
queuedTxs, err := lrucache.New[string, []byte](cfg.QueuedTransactionCap)
if err != nil {
return nil, err
if cfg.QueuedTransactionCap == 0 {
cfg.QueuedTransactionCap = config.DefaultQueuedTransactionCap
}
if cfg.LogCacheSize == 0 {
cfg.LogCacheSize = config.DefaultLogCacheSize
}
historyCache, err := lrucache.New[cacheKey, processedFees](2048)

queuedTxs, err := lrucache.New[string, []byte](cfg.QueuedTransactionCap)
if err != nil {
return nil, err
}
Expand All @@ -55,7 +78,16 @@ func NewJSONRPCBackend(
logger: logger,

queuedTxs: queuedTxs,
historyCache: historyCache,
historyCache: lru.NewCache[cacheKey, processedFees](feeHistoryCacheSize),

headerCache: lru.NewCache[uint64, *coretypes.Header](blockCacheLimit),
blockTxsCache: lru.NewCache[uint64, []*rpctypes.RPCTransaction](blockCacheLimit),
blockRceiptsCache: lru.NewCache[uint64, []*coretypes.Receipt](blockCacheLimit),
blockHashCache: lru.NewCache[common.Hash, uint64](blockCacheLimit),

txLookupCache: lru.NewCache[common.Hash, *rpctypes.RPCTransaction](txLookupCacheLimit),
receiptsCache: lru.NewCache[common.Hash, *coretypes.Receipt](receiptsCacheLimit),
logsCache: lru.NewCache[uint64, []*coretypes.Log](cfg.LogCacheSize),

accMuts: make(map[string]*AccMut),

Expand Down
57 changes: 32 additions & 25 deletions jsonrpc/backend/block.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,7 @@ func (b *JSONRPCBackend) BlockNumber() (hexutil.Uint64, error) {

func (b *JSONRPCBackend) resolveBlockNrOrHash(blockNrOrHash rpc.BlockNumberOrHash) (uint64, error) {
if blockHash, ok := blockNrOrHash.Hash(); ok {
queryCtx, err := b.getQueryCtx()
if err != nil {
return 0, err
}

return b.app.EVMIndexer().BlockHashToNumber(queryCtx, blockHash)
return b.blockNumberByHash(blockHash)
} else if blockNumber, ok := blockNrOrHash.Number(); !ok || blockNumber < 0 {
num, err := b.BlockNumber()
if err != nil {
Expand Down Expand Up @@ -63,6 +58,9 @@ func (b *JSONRPCBackend) GetHeaderByNumber(ethBlockNum rpc.BlockNumber) (*corety
if err != nil {
return nil, err
}
if header, ok := b.headerCache.Get(blockNumber); ok {
return header, nil
}

queryCtx, err := b.getQueryCtx()
if err != nil {
Expand All @@ -77,24 +75,21 @@ func (b *JSONRPCBackend) GetHeaderByNumber(ethBlockNum rpc.BlockNumber) (*corety
return nil, err
}

// cache the header
_ = b.headerCache.Add(blockNumber, header)
return header, nil
}

func (b *JSONRPCBackend) GetHeaderByHash(hash common.Hash) (*coretypes.Header, error) {
queryCtx, err := b.getQueryCtx()
if err != nil {
return nil, err
}

header, err := b.app.EVMIndexer().BlockHeaderByHash(queryCtx, hash)
blockNumber, err := b.resolveBlockNrOrHash(rpc.BlockNumberOrHash{BlockHash: &hash})
if err != nil && errors.Is(err, collections.ErrNotFound) {
return nil, nil
} else if err != nil {
b.logger.Error("failed to get block header by hash", "err", err)
b.logger.Error("failed to get block number by hash", "err", err)
return nil, err
}

return header, nil
return b.GetHeaderByNumber(rpc.BlockNumber(blockNumber))
}

func (b *JSONRPCBackend) GetBlockByNumber(ethBlockNum rpc.BlockNumber, fullTx bool) (map[string]interface{}, error) {
Expand All @@ -103,17 +98,13 @@ func (b *JSONRPCBackend) GetBlockByNumber(ethBlockNum rpc.BlockNumber, fullTx bo
return nil, err
}

queryCtx, err := b.getQueryCtx()
header, err := b.GetHeaderByNumber(ethBlockNum)
if err != nil {
b.logger.Error("failed to get block header by number", "err", err)
return nil, err
}

header, err := b.app.EVMIndexer().BlockHeaderByNumber(queryCtx, blockNumber)
if err != nil && errors.Is(err, collections.ErrNotFound) {
if header == nil {
return nil, nil
} else if err != nil {
b.logger.Error("failed to get block header by number", "err", err)
return nil, err
}

txs, err := b.getBlockTransactions(blockNumber)
Expand All @@ -125,17 +116,33 @@ func (b *JSONRPCBackend) GetBlockByNumber(ethBlockNum rpc.BlockNumber, fullTx bo
}

func (b *JSONRPCBackend) GetBlockByHash(hash common.Hash, fullTx bool) (map[string]interface{}, error) {
blockNumber, err := b.resolveBlockNrOrHash(rpc.BlockNumberOrHash{BlockHash: &hash})
if err != nil && errors.Is(err, collections.ErrNotFound) {
return nil, nil
} else if err != nil {
b.logger.Error("failed to get block number by hash", "err", err)
return nil, err
}
return b.GetBlockByNumber(rpc.BlockNumber(blockNumber), fullTx)
}

func (b *JSONRPCBackend) blockNumberByHash(hash common.Hash) (uint64, error) {
if number, ok := b.blockHashCache.Get(hash); ok {
return number, nil
}

queryCtx, err := b.getQueryCtx()
if err != nil {
return nil, err
return 0, err
}

blockNumber, err := b.app.EVMIndexer().BlockHashToNumber(queryCtx, hash)
number, err := b.app.EVMIndexer().BlockHashToNumber(queryCtx, hash)
if err != nil {
return nil, err
return 0, err
}

return b.GetBlockByNumber(rpc.BlockNumber(blockNumber), fullTx)
_ = b.blockHashCache.Add(hash, number)
return number, nil
}

func formatBlock(header *coretypes.Header, txs []*rpctypes.RPCTransaction, fullTx bool) map[string]interface{} {
Expand Down
2 changes: 1 addition & 1 deletion jsonrpc/backend/feehistory.go
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ func (b *JSONRPCBackend) FeeHistory(blocks uint64, unresolvedLastBlock rpc.Block
} else {
fees.header, fees.err = b.GetHeaderByNumber(rpc.BlockNumber(blockNumber))
if len(rewardPercentiles) != 0 && fees.err == nil {
fees.receipts, fees.err = b.getBLockReceipts(blockNumber)
fees.receipts, fees.err = b.getBlockReceipts(blockNumber)
if fees.err == nil {
var txs []*rpctypes.RPCTransaction
txs, fees.err = b.getBlockTransactions(blockNumber)
Expand Down
33 changes: 17 additions & 16 deletions jsonrpc/backend/filters.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,38 +2,37 @@ package backend

import (
coretypes "github.com/ethereum/go-ethereum/core/types"

rpctypes "github.com/initia-labs/minievm/jsonrpc/types"
"github.com/ethereum/go-ethereum/rpc"
)

// GetLogsByHeight returns all the logs from all the ethereum transactions in a block.
func (b *JSONRPCBackend) GetLogsByHeight(height uint64) ([]*coretypes.Log, error) {
blockLogs := []*coretypes.Log{}
if blockLogs, ok := b.logsCache.Get(height); ok {
return blockLogs, nil
}

queryCtx, err := b.getQueryCtx()
blockHeader, err := b.GetHeaderByNumber(rpc.BlockNumber(height))
if err != nil {
return nil, err
} else if blockHeader == nil {
return nil, nil
}

blockHeader, err := b.app.EVMIndexer().BlockHeaderByNumber(queryCtx, height)
txs, err := b.getBlockTransactions(height)
if err != nil {
return nil, err
}

txs := []*rpctypes.RPCTransaction{}
err = b.app.EVMIndexer().IterateBlockTxs(queryCtx, height, func(tx *rpctypes.RPCTransaction) (bool, error) {
txs = append(txs, tx)
return false, nil
})
receipts, err := b.getBlockReceipts(height)
if err != nil {
return nil, err
}
if len(txs) != len(receipts) {
return nil, NewInternalError("number of transactions and receipts do not match")
}

for _, tx := range txs {
receipt, err := b.app.EVMIndexer().TxReceiptByHash(queryCtx, tx.Hash)
if err != nil {
return nil, err
}
blockLogs := []*coretypes.Log{}
for i, tx := range txs {
receipt := receipts[i]
logs := receipt.Logs
for idx, log := range logs {
log.BlockHash = blockHeader.Hash()
Expand All @@ -45,5 +44,7 @@ func (b *JSONRPCBackend) GetLogsByHeight(height uint64) ([]*coretypes.Log, error
blockLogs = append(blockLogs, logs...)
}

// cache the logs
_ = b.logsCache.Add(height, blockLogs)
return blockLogs, nil
}
Loading

0 comments on commit 0ceec07

Please sign in to comment.