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