erigon-pulse/cmd/rpcdaemon22/commands/erigon_receipts.go

239 lines
6.8 KiB
Go
Raw Normal View History

package commands
import (
"context"
"fmt"
2022-08-10 12:04:13 +00:00
"time"
"github.com/RoaringBitmap/roaring/roaring64"
"github.com/ledgerwatch/erigon/core"
"github.com/ledgerwatch/erigon/core/rawdb"
"github.com/ledgerwatch/erigon/core/state"
"github.com/ledgerwatch/erigon/core/vm"
"github.com/ledgerwatch/erigon/eth/filters"
"github.com/ledgerwatch/erigon/params"
"github.com/ledgerwatch/erigon/rpc"
"github.com/ledgerwatch/erigon/turbo/transactions"
"github.com/ledgerwatch/log/v3"
"github.com/ledgerwatch/erigon/common"
"github.com/ledgerwatch/erigon/core/types"
)
// 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 erigon_getLogs. Return an array of logs that matches the filter conditions
func (api *ErigonImpl) GetLogs(ctx context.Context, crit filters.FilterCriteria) (types.ErigonLogs, error) {
start := time.Now()
var begin, end uint64
erigonLogs := types.ErigonLogs{}
tx, beginErr := api.db.BeginRo(ctx)
if beginErr != nil {
return erigonLogs, 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 := 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)
}
chainConfig, err := api.chainConfig(tx)
if err != nil {
return nil, err
}
var fromTxNum, toTxNum uint64
if begin > 0 {
fromTxNum = api._txNums.MinOf(begin)
}
toTxNum = api._txNums.MaxOf(end) // end is an inclusive bound
txNumbers := roaring64.New()
txNumbers.AddRange(fromTxNum, toTxNum) // [min,max)
ac := api._agg.MakeContext()
topicsBitmap, err := getTopicsBitmap(ac, tx, crit.Topics, fromTxNum, toTxNum)
if err != nil {
return nil, err
}
if topicsBitmap != nil {
txNumbers.And(topicsBitmap)
}
var addrBitmap *roaring64.Bitmap
for _, addr := range crit.Addresses {
var bitmapForORing roaring64.Bitmap
it := ac.LogAddrIterator(addr.Bytes(), fromTxNum, toTxNum, nil)
for it.HasNext() {
bitmapForORing.Add(it.Next())
}
if addrBitmap == nil {
addrBitmap = &bitmapForORing
continue
}
addrBitmap = roaring64.Or(addrBitmap, &bitmapForORing)
}
if addrBitmap != nil {
txNumbers.And(addrBitmap)
}
if txNumbers.GetCardinality() == 0 {
return erigonLogs, nil
}
var lastBlockNum uint64
var lastBlockHash common.Hash
var lastHeader *types.Header
var lastSigner *types.Signer
var lastRules *params.Rules
stateReader := state.NewHistoryReader23(ac, nil /* ReadIndices */)
iter := txNumbers.Iterator()
for iter.HasNext() {
txNum := iter.Next()
// Find block number
ok, blockNum := api._txNums.Find(txNum)
if !ok {
return nil, nil
}
if blockNum > lastBlockNum {
if lastHeader, err = api._blockReader.HeaderByNumber(ctx, nil, blockNum); err != nil {
return nil, err
}
lastBlockNum = blockNum
lastBlockHash = lastHeader.Hash()
lastSigner = types.MakeSigner(chainConfig, blockNum)
lastRules = chainConfig.Rules(blockNum)
}
var startTxNum uint64
if blockNum > 0 {
startTxNum = api._txNums.MinOf(blockNum)
}
txIndex := txNum - startTxNum - 1
//fmt.Printf("txNum=%d, blockNum=%d, txIndex=%d\n", txNum, blockNum, txIndex)
txn, err := api._txnReader.TxnByIdxInBlock(ctx, nil, blockNum, int(txIndex))
if err != nil {
return nil, err
}
timestamp := uint64(txn.Time().Unix())
txHash := txn.Hash()
msg, err := txn.AsMessage(*lastSigner, lastHeader.BaseFee, lastRules)
if err != nil {
return nil, err
}
contractHasTEVM := func(contractHash common.Hash) (bool, error) { return false, nil }
blockCtx, txCtx := transactions.GetEvmContext(msg, lastHeader, true /* requireCanonical */, tx, contractHasTEVM, api._blockReader)
stateReader.SetTxNum(txNum)
vmConfig := vm.Config{}
vmConfig.SkipAnalysis = core.SkipAnalysis(chainConfig, blockNum)
ibs := state.New(stateReader)
evm := vm.NewEVM(blockCtx, txCtx, ibs, chainConfig, vmConfig)
gp := new(core.GasPool).AddGas(msg.Gas())
ibs.Prepare(txHash, lastBlockHash, int(txIndex))
_, err = core.ApplyMessage(evm, msg, gp, true /* refunds */, false /* gasBailout */)
if err != nil {
return nil, err
}
filtered := filterLogs(ibs.GetLogs(txHash), crit.Addresses, crit.Topics)
for i, log := range filtered {
log.BlockNumber = blockNum
log.BlockHash = lastBlockHash
log.TxHash = txHash
log.Index = 0
erigonLogs[i].Log = *log
erigonLogs[i].Timestamp = timestamp
}
}
stats := api._agg.GetAndResetStats()
log.Info("Finished", "duration", time.Since(start), "history queries", stats.HistoryQueries, "ef search duration", stats.EfSearchTime)
return erigonLogs, 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
// }