2022-06-10 15:18:43 +00:00
package commands
import (
"context"
"fmt"
2022-08-10 12:04:13 +00:00
"time"
2022-08-09 03:00:36 +00:00
"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"
2022-06-10 15:18:43 +00:00
"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
}
2022-08-09 03:00:36 +00:00
// GetLogs implements erigon_getLogs. Return an array of logs that matches the filter conditions
2022-08-30 02:49:05 +00:00
func ( api * ErigonImpl ) GetLogs ( ctx context . Context , crit filters . FilterCriteria ) ( types . ErigonLogs , error ) {
2022-08-09 03:00:36 +00:00
start := time . Now ( )
var begin , end uint64
2022-08-30 02:49:05 +00:00
erigonLogs := types . ErigonLogs { }
2022-08-09 03:00:36 +00:00
tx , beginErr := api . db . BeginRo ( ctx )
if beginErr != nil {
2022-08-30 02:49:05 +00:00
return erigonLogs , beginErr
2022-08-09 03:00:36 +00:00
}
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 {
2022-08-25 05:24:01 +00:00
fromTxNum = api . _txNums . MinOf ( begin )
2022-08-09 03:00:36 +00:00
}
2022-08-25 05:24:01 +00:00
toTxNum = api . _txNums . MaxOf ( end ) // end is an inclusive bound
2022-08-09 03:00:36 +00:00
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 {
2022-08-30 02:49:05 +00:00
return erigonLogs , nil
2022-08-09 03:00:36 +00:00
}
var lastBlockNum uint64
var lastBlockHash common . Hash
var lastHeader * types . Header
var lastSigner * types . Signer
var lastRules * params . Rules
2022-08-27 08:21:02 +00:00
stateReader := state . NewHistoryReader23 ( ac , nil /* ReadIndices */ )
2022-08-09 03:00:36 +00:00
iter := txNumbers . Iterator ( )
for iter . HasNext ( ) {
txNum := iter . Next ( )
// Find block number
2022-08-25 05:24:01 +00:00
ok , blockNum := api . _txNums . Find ( txNum )
if ! ok {
return nil , nil
}
2022-08-09 03:00:36 +00:00
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 {
2022-08-25 05:24:01 +00:00
startTxNum = api . _txNums . MinOf ( blockNum )
2022-08-09 03:00:36 +00:00
}
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 )
2022-08-30 02:49:05 +00:00
for i , log := range filtered {
2022-08-09 03:00:36 +00:00
log . BlockNumber = blockNum
log . BlockHash = lastBlockHash
log . TxHash = txHash
log . Index = 0
2022-08-30 02:49:05 +00:00
erigonLogs [ i ] . Log = * log
erigonLogs [ i ] . Timestamp = timestamp
2022-08-09 03:00:36 +00:00
}
}
stats := api . _agg . GetAndResetStats ( )
log . Info ( "Finished" , "duration" , time . Since ( start ) , "history queries" , stats . HistoryQueries , "ef search duration" , stats . EfSearchTime )
2022-08-30 02:49:05 +00:00
return erigonLogs , nil
2022-08-09 03:00:36 +00:00
}
2022-06-10 15:18:43 +00:00
// 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
// }