-
Notifications
You must be signed in to change notification settings - Fork 319
/
logfilter.go
119 lines (110 loc) · 3.01 KB
/
logfilter.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
package api
import (
"bytes"
"github.com/iotexproject/iotex-proto/golang/iotexapi"
"github.com/iotexproject/iotex-proto/golang/iotextypes"
"go.uber.org/zap"
"github.com/iotexproject/iotex-core/action"
"github.com/iotexproject/iotex-core/blockchain/block"
"github.com/iotexproject/iotex-core/pkg/log"
)
// LogFilter contains options for contract log filtering.
type LogFilter struct {
stream iotexapi.APIService_StreamLogsServer
errChan chan error
*iotexapi.LogsFilter
// FilterLogsRequest.Topics restricts matches to particular event topics. Each event has a list
// of topics. Topics matches a prefix of that list. An empty element slice matches any
// topic. Non-empty elements represent an alternative that matches any of the
// contained topics.
//
// Examples:
// {} or nil matches any topic list
// {{A}} matches topic A in first position
// {{}, {B}} matches any topic in first position, B in second position
// {{A}, {B}} matches topic A in first position, B in second position
// {{A, B}}, {C, D}} matches topic (A OR B) in first position, (C OR D) in second position
}
// NewLogFilter returns a new log filter
func NewLogFilter(in *iotexapi.LogsFilter, stream iotexapi.APIService_StreamLogsServer, errChan chan error) Responder {
return &LogFilter{
stream: stream,
errChan: errChan,
LogsFilter: in,
}
}
// Respond to new block
func (l *LogFilter) Respond(blk *block.Block) error {
logs := l.MatchLogs(blk.Receipts)
if len(logs) == 0 {
return nil
}
// send matched logs thru streaming API
for _, e := range logs {
if err := l.stream.Send(&iotexapi.StreamLogsResponse{Log: e}); err != nil {
l.errChan <- err
log.L().Info("error streaming the log",
zap.Uint64("height", e.BlkHeight),
zap.Error(err))
return err
}
}
return nil
}
// Exit send to error channel
func (l *LogFilter) Exit() {
l.errChan <- nil
}
// MatchLogs returns matching logs in a given block
func (l *LogFilter) MatchLogs(receipts []*action.Receipt) []*iotextypes.Log {
var logs []*iotextypes.Log
for _, r := range receipts {
for _, v := range r.Logs {
log := v.ConvertToLogPb()
if l.match(log) {
logs = append(logs, log)
}
}
}
return logs
}
// match checks if a given log matches the filter
func (l *LogFilter) match(log *iotextypes.Log) bool {
addrMatch := len(l.Address) == 0
if !addrMatch {
for _, e := range l.Address {
if e == log.ContractAddress {
addrMatch = true
break
}
}
}
if !addrMatch {
return false
}
if len(l.Topics) > len(log.Topics) {
// trying to match a prefix of log's topic list, so topics longer than that is consider invalid
return false
}
if len(l.Topics) == 0 {
// {} or nil matches any address or topic list
return true
}
for i, e := range l.Topics {
if e == nil || len(e.Topic) == 0 {
continue
}
target := log.Topics[i]
match := false
for _, v := range e.Topic {
if bytes.Compare(v, target) == 0 {
match = true
break
}
}
if !match {
return false
}
}
return true
}