package commands import ( "context" "fmt" "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 // }