github.com/diadata-org/diadata@v1.4.593/pkg/utils/ethtxfilter.go (about) 1 package utils 2 3 import ( 4 "context" 5 "math/big" 6 "sort" 7 8 "github.com/ethereum/go-ethereum" 9 "github.com/ethereum/go-ethereum/accounts/abi" 10 "github.com/ethereum/go-ethereum/common" 11 "github.com/ethereum/go-ethereum/core/types" 12 "github.com/ethereum/go-ethereum/ethclient" 13 ) 14 15 // EthTxFilterCriteria used for filtering transaction records 16 type EthTxFilterCriteria struct { 17 StartBlockNum uint64 // inclusive. filter transactions from the specific block 18 StartTxIndex uint // inclusive. filter transactions from specific index in the given StartBlockNum 19 LimitBlocks int // filter transactions in the specific number of blocks, zero means up to highest possible block 20 BehindHighestBlock int // stay behind the highest synched block if StartBlockNum+LimitBlocks in excess of the head 21 EvAddrs []common.Address // list of addresses from which transaction events should originate 22 Events []common.Hash // list of events from which transactions should contain 23 } 24 25 // EthTxFilterResult describes filter results and holds found transactions 26 type EthTxFilterResult struct { 27 Synced bool // it means the most recent inspected block matches the highest known block (see BehindHighestBlock) 28 NumBlocks int // number of blocks found 29 NumTXs int // number of transactions found 30 NumLogs int // number of log records found 31 TXs []*EthFilteredTx // list of found transactions 32 LastBlockNum uint64 // block number of most recent transaction inspected 33 } 34 35 // EthFilteredTx holds limited info for found transactions 36 type EthFilteredTx struct { 37 BlockNum uint64 38 BlockHash common.Hash 39 TXIndex uint 40 TXHash common.Hash 41 Logs []types.Log // list of matched log records if Events or EvAddrs were used, otherwise all logs of the transaction 42 } 43 44 // EthFilterTXs returns transactions filtered by log records 45 func EthFilterTXs(ctx context.Context, ethClient *ethclient.Client, filter EthTxFilterCriteria) (*EthTxFilterResult, error) { 46 startBlockNum, endBlockNum, synced, err := ethFilterTXsCalcEndBlockNum(ctx, ethClient, filter.StartBlockNum, uint64(filter.BehindHighestBlock), uint64(filter.LimitBlocks)) 47 if err != nil { 48 return nil, err 49 } 50 51 query := make([][]interface{}, 1) 52 query[0] = make([]interface{}, len(filter.Events)) 53 54 for i, ev := range filter.Events { 55 query[0][i] = ev 56 } 57 58 topics, err := abi.MakeTopics(query...) 59 if err != nil { 60 return nil, err 61 } 62 63 logs, err := ethClient.FilterLogs(ctx, ethereum.FilterQuery{ 64 Addresses: filter.EvAddrs, 65 Topics: topics, 66 FromBlock: new(big.Int).SetUint64(startBlockNum), 67 ToBlock: new(big.Int).SetUint64(endBlockNum), 68 }) 69 70 if err != nil { 71 return nil, err 72 } 73 74 txMap := make(map[common.Hash]*EthFilteredTx) 75 76 for _, log := range logs { 77 // skip if the log removed due to chain re-organization 78 if log.Removed { 79 continue 80 } 81 82 // skip if the event has already been passed 83 if log.BlockNumber == filter.StartBlockNum && log.TxIndex < filter.StartTxIndex { 84 continue 85 } 86 87 tx, ok := txMap[log.TxHash] 88 if !ok { 89 tx = &EthFilteredTx{ 90 BlockNum: log.BlockNumber, 91 BlockHash: log.BlockHash, 92 TXIndex: log.TxIndex, 93 TXHash: log.TxHash, 94 Logs: make([]types.Log, 0, 10), 95 } 96 97 txMap[log.TxHash] = tx 98 } 99 100 tx.Logs = append(tx.Logs, log) 101 } 102 103 result := &EthTxFilterResult{ 104 Synced: synced, 105 TXs: make([]*EthFilteredTx, 0, len(txMap)), 106 LastBlockNum: endBlockNum, 107 } 108 109 lastBlockNum := uint64(0) 110 for _, tx := range txMap { 111 if lastBlockNum != tx.BlockNum { 112 lastBlockNum = tx.BlockNum 113 result.NumBlocks++ 114 } 115 116 sort.Slice(tx.Logs, func(i, j int) bool { return tx.Logs[i].Index < tx.Logs[j].Index }) 117 118 result.NumTXs++ 119 result.NumLogs += len(tx.Logs) 120 121 result.TXs = append(result.TXs, tx) 122 } 123 124 sort.Slice(result.TXs, func(i, j int) bool { 125 if result.TXs[i].BlockNum < result.TXs[j].BlockNum { 126 return true 127 } 128 129 if result.TXs[i].BlockNum > result.TXs[j].BlockNum { 130 return false 131 } 132 133 return result.TXs[i].TXIndex < result.TXs[j].TXIndex 134 }) 135 136 return result, nil 137 } 138 139 func ethFilterTXsCalcEndBlockNum(ctx context.Context, ethClient *ethclient.Client, start, stayBehind, limit uint64) (uint64, uint64, bool, error) { 140 end, err := ethClient.BlockNumber(ctx) 141 if err != nil { 142 return 0, 0, false, err 143 } 144 145 syncProgress, err := ethClient.SyncProgress(ctx) 146 if err != nil { 147 return 0, 0, false, err 148 } 149 150 if syncProgress == nil { // means the connected node is synced 151 end -= stayBehind 152 } else { 153 max := syncProgress.HighestBlock - stayBehind 154 if syncProgress.CurrentBlock < max { 155 end = syncProgress.CurrentBlock 156 } 157 } 158 159 synced := true 160 161 if to := start + limit; limit != 0 && to < end { 162 synced = false 163 end = to 164 } 165 166 if start > end { 167 start = end 168 } 169 log.Infof("resulting start -- end: %v --- %v", start, end) 170 171 return start, end, synced, nil 172 }