-
Notifications
You must be signed in to change notification settings - Fork 13
Expand file tree
/
Copy pathfilter.go
More file actions
196 lines (170 loc) · 4.91 KB
/
Copy pathfilter.go
File metadata and controls
196 lines (170 loc) · 4.91 KB
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
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
package logs
import (
"fmt"
"slices"
errs "github.com/onflow/flow-evm-gateway/models/errors"
"github.com/onflow/flow-evm-gateway/storage"
"github.com/onflow/go-ethereum/common"
gethTypes "github.com/onflow/go-ethereum/core/types"
"github.com/onflow/go-ethereum/eth/filters"
)
// RangeFilter matches all the indexed logs within the range defined as
// start and end block height. The start must be strictly smaller or equal than end value.
type RangeFilter struct {
start, end uint64
criteria filters.FilterCriteria
receipts storage.ReceiptIndexer
}
func NewRangeFilter(
start, end uint64,
criteria filters.FilterCriteria,
receipts storage.ReceiptIndexer,
) (*RangeFilter, error) {
// make sure that beginning number is not bigger than end
if start > end {
return nil, fmt.Errorf(
"%w: start block number (%d) must be smaller or equal to end block number (%d)",
errs.ErrInvalid,
start,
end,
)
}
return &RangeFilter{
start: start,
end: end,
criteria: criteria,
receipts: receipts,
}, nil
}
func (r *RangeFilter) Match() ([]*gethTypes.Log, error) {
bloomsHeight, err := r.receipts.BloomsForBlockRange(r.start, r.end)
if err != nil {
return nil, err
}
var bloomHeightMatches []uint64
var logs []*gethTypes.Log
// first filter all the logs based on whether a bloom matches,
// if bloom matches we fetch only that height later and do exact match
for _, bloomHeight := range bloomsHeight {
for _, bloom := range bloomHeight.Blooms {
if bloomMatch(*bloom, r.criteria) {
bloomHeightMatches = append(bloomHeightMatches, bloomHeight.Height)
// if there's a match we add the height and skip to next height
// even if there would be multiple matches for height we just want to have unique heights
break
}
}
}
// do exact matches only on subset of heights in the range that matched the bloom
for _, height := range bloomHeightMatches {
// todo do this concurrently but make sure order is correct
receipts, err := r.receipts.GetByBlockHeight(height)
if err != nil {
return nil, err
}
for _, receipt := range receipts {
for _, log := range receipt.Logs {
if ExactMatch(log, r.criteria) {
logs = append(logs, log)
}
}
}
}
return logs, nil
}
// todo add HeightFilter
// IDFilter matches all logs against the criteria found in a single block identified
// by the provided block ID.
type IDFilter struct {
id common.Hash
criteria filters.FilterCriteria
blocks storage.BlockIndexer
receipts storage.ReceiptIndexer
}
func NewIDFilter(
criteria filters.FilterCriteria,
blocks storage.BlockIndexer,
receipts storage.ReceiptIndexer,
) (*IDFilter, error) {
if criteria.BlockHash == nil {
return nil, fmt.Errorf("filter criteria should have a non-nil block hash")
}
return &IDFilter{
id: *criteria.BlockHash,
criteria: criteria,
blocks: blocks,
receipts: receipts,
}, nil
}
func (i *IDFilter) Match() ([]*gethTypes.Log, error) {
blk, err := i.blocks.GetByID(i.id)
if err != nil {
return nil, err
}
receipts, err := i.receipts.GetByBlockHeight(blk.Height)
if err != nil {
return nil, err
}
logs := make([]*gethTypes.Log, 0)
for _, receipt := range receipts {
for _, log := range receipt.Logs {
if ExactMatch(log, i.criteria) {
logs = append(logs, log)
}
}
}
return logs, nil
}
// ExactMatch checks the topic and address values of the log match the filter exactly.
func ExactMatch(log *gethTypes.Log, criteria filters.FilterCriteria) bool {
// check criteria doesn't have more topics than the log, but it can have less due to wildcards
if len(criteria.Topics) > len(log.Topics) {
return false
}
for i, sub := range criteria.Topics {
// wildcard matching all
if len(sub) == 0 {
continue
}
if !slices.Contains(sub, log.Topics[i]) {
return false
}
}
// no addresses is a wildcard to match all
if len(criteria.Addresses) == 0 {
return true
}
return slices.Contains(criteria.Addresses, log.Address)
}
// bloomMatch takes a bloom value and tests if the addresses and topics provided pass the bloom filter.
// This acts as a fast probabilistic test that might produce false-positives but not false-negatives.
// If true is returned we should further check against the exactMatch to really make sure the log is matched.
//
// source: https://github.com/ethereum/go-ethereum/blob/8d1db1601d3a9e4fd067558a49db6f0b879c9b48/eth/filters/filter.go#L395
func bloomMatch(bloom gethTypes.Bloom, criteria filters.FilterCriteria) bool {
if len(criteria.Addresses) > 0 {
var included bool
for _, addr := range criteria.Addresses {
if gethTypes.BloomLookup(bloom, addr) {
included = true
break
}
}
if !included {
return false
}
}
for _, sub := range criteria.Topics {
included := len(sub) == 0 // empty rule set == wildcard
for _, topic := range sub {
if gethTypes.BloomLookup(bloom, topic) {
included = true
break
}
}
if !included {
return false
}
}
return true
}