erigon-pulse/cmd/rpcdaemon/commands/erigon_receipts.go
2022-08-11 19:08:20 +03:00

229 lines
5.9 KiB
Go

package commands
import (
"bytes"
"context"
"encoding/binary"
"fmt"
"github.com/RoaringBitmap/roaring"
"github.com/ledgerwatch/erigon-lib/kv"
"github.com/ledgerwatch/erigon/common"
"github.com/ledgerwatch/erigon/common/dbutils"
"github.com/ledgerwatch/erigon/core/rawdb"
"github.com/ledgerwatch/erigon/core/types"
"github.com/ledgerwatch/erigon/eth/filters"
"github.com/ledgerwatch/erigon/ethdb/bitmapdb"
"github.com/ledgerwatch/erigon/ethdb/cbor"
"github.com/ledgerwatch/erigon/rpc"
"github.com/ledgerwatch/erigon/turbo/rpchelper"
)
// GetLogsByHash implements erigon_getLogsByHash. Returns an array of arrays of logs generated by the transactions in the block given by the block's hash.
func (api *ErigonImpl) GetLogsByHash(ctx context.Context, hash common.Hash) ([][]*types.Log, error) {
tx, err := api.db.BeginRo(ctx)
if err != nil {
return nil, err
}
defer tx.Rollback()
chainConfig, err := api.chainConfig(tx)
if err != nil {
return nil, err
}
block, err := api.blockByHashWithSenders(tx, hash)
if err != nil {
return nil, err
}
if block == nil {
return nil, nil
}
receipts, err := api.getReceipts(ctx, tx, chainConfig, block, block.Body().SendersFromTxs())
if err != nil {
return nil, fmt.Errorf("getReceipts error: %w", err)
}
logs := make([][]*types.Log, len(receipts))
for i, receipt := range receipts {
logs[i] = receipt.Logs
}
return logs, nil
}
// GetLogs implements eth_getLogs. Returns an array of logs matching a given filter object.
func (api *ErigonImpl) GetLogs(ctx context.Context, crit filters.FilterCriteria) ([]*types.Log, error) {
var begin, end uint64
logs := []*types.Log{}
tx, beginErr := api.db.BeginRo(ctx)
if beginErr != nil {
return logs, beginErr
}
defer tx.Rollback()
if crit.BlockHash != nil {
number := rawdb.ReadHeaderNumber(tx, *crit.BlockHash)
if number == nil {
return nil, fmt.Errorf("block not found: %x", *crit.BlockHash)
}
begin = *number
end = *number
} else {
// Convert the RPC block numbers into internal representations
latest, err := rpchelper.GetLatestBlockNumber(tx)
if err != nil {
return nil, err
}
begin = latest
if crit.FromBlock != nil {
if crit.FromBlock.Sign() >= 0 {
begin = crit.FromBlock.Uint64()
} else if !crit.FromBlock.IsInt64() || crit.FromBlock.Int64() != int64(rpc.LatestBlockNumber) {
return nil, fmt.Errorf("negative value for FromBlock: %v", crit.FromBlock)
}
}
end = latest
if crit.ToBlock != nil {
if crit.ToBlock.Sign() >= 0 {
end = crit.ToBlock.Uint64()
} else if !crit.ToBlock.IsInt64() || crit.ToBlock.Int64() != int64(rpc.LatestBlockNumber) {
return nil, fmt.Errorf("negative value for ToBlock: %v", crit.ToBlock)
}
}
}
if end < begin {
return nil, fmt.Errorf("end (%d) < begin (%d)", end, begin)
}
if end > roaring.MaxUint32 {
return nil, fmt.Errorf("end (%d) > MaxUint32", end)
}
blockNumbers := roaring.New()
blockNumbers.AddRange(begin, end+1) // [min,max)
topicsBitmap, err := getTopicsBitmap(tx, crit.Topics, uint32(begin), uint32(end))
if err != nil {
return nil, err
}
if topicsBitmap != nil {
blockNumbers.And(topicsBitmap)
}
var addrBitmap *roaring.Bitmap
for _, addr := range crit.Addresses {
m, err := bitmapdb.Get(tx, kv.LogAddressIndex, addr[:], uint32(begin), uint32(end))
if err != nil {
return nil, err
}
if addrBitmap == nil {
addrBitmap = m
continue
}
addrBitmap = roaring.Or(addrBitmap, m)
}
if addrBitmap != nil {
blockNumbers.And(addrBitmap)
}
if blockNumbers.GetCardinality() == 0 {
return logs, nil
}
iter := blockNumbers.Iterator()
for iter.HasNext() {
if err = ctx.Err(); err != nil {
return nil, err
}
blockNumber := uint64(iter.Next())
var logIndex uint
var txIndex uint
var blockLogs []*types.Log
err := tx.ForPrefix(kv.Log, dbutils.EncodeBlockNumber(blockNumber), func(k, v []byte) error {
var logs types.Logs
if err := cbor.Unmarshal(&logs, bytes.NewReader(v)); err != nil {
return fmt.Errorf("receipt unmarshal failed: %w", err)
}
for _, log := range logs {
log.Index = logIndex
logIndex++
}
filtered := filterLogs(logs, crit.Addresses, crit.Topics)
if len(filtered) == 0 {
return nil
}
txIndex = uint(binary.BigEndian.Uint32(k[8:]))
for _, log := range filtered {
log.TxIndex = txIndex
}
blockLogs = append(blockLogs, filtered...)
return nil
})
if err != nil {
return logs, err
}
if len(blockLogs) == 0 {
continue
}
header, err := api._blockReader.HeaderByNumber(ctx, tx, blockNumber)
if err != nil {
return nil, err
}
if header == nil {
return nil, fmt.Errorf("block header not found: %d", blockNumber)
}
timestamp := header.Time
blockHash, err := rawdb.ReadCanonicalHash(tx, blockNumber)
if err != nil {
return nil, err
}
body, err := api._blockReader.BodyWithTransactions(ctx, tx, blockHash, blockNumber)
if err != nil {
return nil, err
}
if body == nil {
return nil, fmt.Errorf("block not found %d", blockNumber)
}
for _, log := range blockLogs {
log.Timestamp = timestamp
log.BlockNumber = blockNumber
log.BlockHash = blockHash
log.TxHash = body.Transactions[log.TxIndex].Hash()
}
logs = append(logs, blockLogs...)
}
return logs, nil
}
// GetLogsByNumber implements erigon_getLogsByHash. Returns all the logs that appear in a block given the block's hash.
// func (api *ErigonImpl) GetLogsByNumber(ctx context.Context, number rpc.BlockNumber) ([][]*types.Log, error) {
// tx, err := api.db.Begin(ctx, false)
// if err != nil {
// return nil, err
// }
// defer tx.Rollback()
// number := rawdb.ReadHeaderNumber(tx, hash)
// if number == nil {
// return nil, fmt.Errorf("block not found: %x", hash)
// }
// receipts, err := getReceipts(ctx, tx, *number, hash)
// if err != nil {
// return nil, fmt.Errorf("getReceipts error: %w", err)
// }
// logs := make([][]*types.Log, len(receipts))
// for i, receipt := range receipts {
// logs[i] = receipt.Logs
// }
// return logs, nil
// }