diff --git a/indexer/indexer.go b/indexer/indexer.go index b7a15b5..e11dee9 100644 --- a/indexer/indexer.go +++ b/indexer/indexer.go @@ -34,6 +34,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 // block BlockHashToNumber(ctx context.Context, hash common.Hash) (uint64, error) diff --git a/indexer/reader.go b/indexer/reader.go index 20b6e04..e7f7793 100644 --- a/indexer/reader.go +++ b/indexer/reader.go @@ -63,6 +63,19 @@ 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 { + 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) + if err != nil { + return true, err + } + + return cb(txRecept) + }) +} + // TxReceiptByHash implements EVMIndexer. func (e *EVMIndexerImpl) TxReceiptByHash(ctx context.Context, hash common.Hash) (*coretypes.Receipt, error) { receipt, err := e.TxReceiptMap.Get(ctx, hash.Bytes()) diff --git a/jsonrpc/backend/block.go b/jsonrpc/backend/block.go index 0224fdf..c792df9 100644 --- a/jsonrpc/backend/block.go +++ b/jsonrpc/backend/block.go @@ -116,11 +116,7 @@ func (b *JSONRPCBackend) GetBlockByNumber(ethBlockNum rpc.BlockNumber, fullTx bo return nil, err } - txs := []*rpctypes.RPCTransaction{} - err = b.app.EVMIndexer().IterateBlockTxs(queryCtx, blockNumber, func(tx *rpctypes.RPCTransaction) (bool, error) { - txs = append(txs, tx) - return false, nil - }) + txs, err := b.getBlockTransactions(blockNumber) if err != nil { return nil, err } @@ -134,25 +130,12 @@ func (b *JSONRPCBackend) GetBlockByHash(hash common.Hash, fullTx bool) (map[stri return nil, err } - header, err := b.app.EVMIndexer().BlockHeaderByHash(queryCtx, 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) - return nil, err - } - - txs := []*rpctypes.RPCTransaction{} - blockNumber := header.Number.Uint64() - err = b.app.EVMIndexer().IterateBlockTxs(queryCtx, blockNumber, func(tx *rpctypes.RPCTransaction) (bool, error) { - txs = append(txs, tx) - return false, nil - }) + blockNumber, err := b.app.EVMIndexer().BlockHashToNumber(queryCtx, hash) if err != nil { return nil, err } - return formatBlock(header, txs, fullTx), nil + return b.GetBlockByNumber(rpc.BlockNumber(blockNumber), fullTx) } func formatBlock(header *coretypes.Header, txs []*rpctypes.RPCTransaction, fullTx bool) map[string]interface{} { diff --git a/jsonrpc/backend/tx.go b/jsonrpc/backend/tx.go index 9e4b4b2..a3137eb 100644 --- a/jsonrpc/backend/tx.go +++ b/jsonrpc/backend/tx.go @@ -358,6 +358,70 @@ func (b *JSONRPCBackend) PendingTransactions() ([]*rpctypes.RPCTransaction, erro return result, nil } +func (b *JSONRPCBackend) getBlockTransactions(blockNumber uint64) ([]*rpctypes.RPCTransaction, error) { + queryCtx, err := b.getQueryCtx() + if err != nil { + return nil, err + } + + txs := []*rpctypes.RPCTransaction{} + err = b.app.EVMIndexer().IterateBlockTxs(queryCtx, blockNumber, func(tx *rpctypes.RPCTransaction) (bool, error) { + txs = append(txs, tx) + return false, nil + }) + if err != nil { + return nil, err + } + + return txs, nil +} + +func (b *JSONRPCBackend) getBlockRecepts(blockNumber uint64) ([]*coretypes.Receipt, error) { + queryCtx, err := b.getQueryCtx() + if err != nil { + return nil, err + } + + recepts := []*coretypes.Receipt{} + err = b.app.EVMIndexer().IterateBlockTxRecepts(queryCtx, blockNumber, func(recept *coretypes.Receipt) (bool, error) { + recepts = append(recepts, recept) + return false, nil + }) + if err != nil { + return nil, err + } + + return recepts, nil +} + +func (b *JSONRPCBackend) GetBlockReceipts(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) ([]map[string]interface{}, error) { + blockNumber, err := b.resolveBlockNrOrHash(blockNrOrHash) + if err != nil { + return nil, err + } + + txs, err := b.getBlockTransactions(blockNumber) + if err != nil { + return nil, err + } + + receipts, err := b.getBlockRecepts(blockNumber) + if err != nil { + return nil, err + } + + if len(txs) != len(receipts) { + return nil, fmt.Errorf("receipts length mismatch: %d vs %d", len(txs), len(receipts)) + } + + result := make([]map[string]interface{}, len(receipts)) + for i, receipt := range receipts { + result[i] = marshalReceipt(receipt, txs[i]) + } + + return result, nil +} + // marshalReceipt marshals a transaction receipt into a JSON object. func marshalReceipt(receipt *coretypes.Receipt, tx *rpctypes.RPCTransaction) map[string]interface{} { fields := map[string]interface{}{ diff --git a/jsonrpc/namespaces/eth/api.go b/jsonrpc/namespaces/eth/api.go index 9e2087b..4234ba4 100644 --- a/jsonrpc/namespaces/eth/api.go +++ b/jsonrpc/namespaces/eth/api.go @@ -141,6 +141,11 @@ func (api *EthAPI) GetBlockByHash(hash common.Hash, fullTx bool) (map[string]int return api.backend.GetBlockByHash(hash, fullTx) } +func (api *EthAPI) GetBlockReceipts(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) ([]map[string]interface{}, error) { + api.logger.Debug("eth_getBlockReceipts", "block number or hash", blockNrOrHash) + return api.backend.GetBlockReceipts(ctx, blockNrOrHash) +} + // ************************************* // * Read Txs * // *************************************