github.com/digdeepmining/go-atheios@v1.5.13-0.20180902133602-d5687a2e6f43/eth/filters/filter.go (about) 1 // Copyright 2014 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The go-ethereum library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 package filters 18 19 import ( 20 "math" 21 "time" 22 "math/big" 23 24 "github.com/atheioschain/go-atheios/common" 25 "github.com/atheioschain/go-atheios/core" 26 "github.com/atheioschain/go-atheios/core/types" 27 "github.com/atheioschain/go-atheios/ethdb" 28 "github.com/atheioschain/go-atheios/event" 29 "github.com/atheioschain/go-atheios/rpc" 30 "golang.org/x/net/context" 31 ) 32 33 type Backend interface { 34 ChainDb() ethdb.Database 35 EventMux() *event.TypeMux 36 HeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*types.Header, error) 37 GetReceipts(ctx context.Context, blockHash common.Hash) (types.Receipts, error) 38 } 39 40 // Filter can be used to retrieve and filter logs. 41 type Filter struct { 42 backend Backend 43 useMipMap bool 44 45 created time.Time 46 47 db ethdb.Database 48 begin, end int64 49 addresses []common.Address 50 topics [][]common.Hash 51 } 52 53 // New creates a new filter which uses a bloom filter on blocks to figure out whether 54 // a particular block is interesting or not. 55 // MipMaps allow past blocks to be searched much more efficiently, but are not available 56 // to light clients. 57 func New(backend Backend, useMipMap bool) *Filter { 58 return &Filter{ 59 backend: backend, 60 useMipMap: useMipMap, 61 db: backend.ChainDb(), 62 } 63 } 64 65 // SetBeginBlock sets the earliest block for filtering. 66 // -1 = latest block (i.e., the current block) 67 // hash = particular hash from-to 68 func (f *Filter) SetBeginBlock(begin int64) { 69 f.begin = begin 70 } 71 72 // SetEndBlock sets the latest block for filtering. 73 func (f *Filter) SetEndBlock(end int64) { 74 f.end = end 75 } 76 77 // SetAddresses matches only logs that are generated from addresses that are included 78 // in the given addresses. 79 func (f *Filter) SetAddresses(addr []common.Address) { 80 f.addresses = addr 81 } 82 83 // SetTopics matches only logs that have topics matching the given topics. 84 func (f *Filter) SetTopics(topics [][]common.Hash) { 85 f.topics = topics 86 } 87 88 // FindOnce searches the blockchain for matching log entries, returning 89 // all matching entries from the first block that contains matches, 90 // updating the start point of the filter accordingly. If no results are 91 // found, a nil slice is returned. 92 func (f *Filter) FindOnce(ctx context.Context) ([]*types.Log, error) { 93 head, _ := f.backend.HeaderByNumber(ctx, rpc.LatestBlockNumber) 94 if head == nil { 95 return nil, nil 96 } 97 headBlockNumber := head.Number.Uint64() 98 99 var beginBlockNo uint64 = uint64(f.begin) 100 if f.begin == -1 { 101 beginBlockNo = headBlockNumber 102 } 103 var endBlockNo uint64 = uint64(f.end) 104 if f.end == -1 { 105 endBlockNo = headBlockNumber 106 } 107 108 // if no addresses are present we can't make use of fast search which 109 // uses the mipmap bloom filters to check for fast inclusion and uses 110 // higher range probability in order to ensure at least a false positive 111 if !f.useMipMap || len(f.addresses) == 0 { 112 logs, blockNumber, err := f.getLogs(ctx, beginBlockNo, endBlockNo) 113 f.begin = int64(blockNumber + 1) 114 return logs, err 115 } 116 117 logs, blockNumber := f.mipFind(beginBlockNo, endBlockNo, 0) 118 f.begin = int64(blockNumber + 1) 119 return logs, nil 120 } 121 122 // Run filters logs with the current parameters set 123 func (f *Filter) Find(ctx context.Context) (logs []*types.Log, err error) { 124 for { 125 newLogs, err := f.FindOnce(ctx) 126 if len(newLogs) == 0 || err != nil { 127 return logs, err 128 } 129 logs = append(logs, newLogs...) 130 } 131 } 132 133 func (f *Filter) mipFind(start, end uint64, depth int) (logs []*types.Log, blockNumber uint64) { 134 level := core.MIPMapLevels[depth] 135 // normalise numerator so we can work in level specific batches and 136 // work with the proper range checks 137 for num := start / level * level; num <= end; num += level { 138 // find addresses in bloom filters 139 bloom := core.GetMipmapBloom(f.db, num, level) 140 // Don't bother checking the first time through the loop - we're probably picking 141 // up where a previous run left off. 142 first := true 143 for _, addr := range f.addresses { 144 if first || bloom.TestBytes(addr[:]) { 145 first = false 146 // range check normalised values and make sure that 147 // we're resolving the correct range instead of the 148 // normalised values. 149 start := uint64(math.Max(float64(num), float64(start))) 150 end := uint64(math.Min(float64(num+level-1), float64(end))) 151 if depth+1 == len(core.MIPMapLevels) { 152 l, blockNumber, _ := f.getLogs(context.Background(), start, end) 153 if len(l) > 0 { 154 return l, blockNumber 155 } 156 } else { 157 l, blockNumber := f.mipFind(start, end, depth+1) 158 if len(l) > 0 { 159 return l, blockNumber 160 } 161 } 162 } 163 } 164 } 165 166 return nil, end 167 } 168 169 func (f *Filter) getLogs(ctx context.Context, start, end uint64) (logs []*types.Log, blockNumber uint64, err error) { 170 for i := start; i <= end; i++ { 171 blockNumber := rpc.BlockNumber(i) 172 header, err := f.backend.HeaderByNumber(ctx, blockNumber) 173 if header == nil || err != nil { 174 return logs, end, err 175 } 176 177 // Use bloom filtering to see if this block is interesting given the 178 // current parameters 179 if f.bloomFilter(header.Bloom) { 180 // Get the logs of the block 181 receipts, err := f.backend.GetReceipts(ctx, header.Hash()) 182 if err != nil { 183 return nil, end, err 184 } 185 var unfiltered []*types.Log 186 for _, receipt := range receipts { 187 unfiltered = append(unfiltered, ([]*types.Log)(receipt.Logs)...) 188 } 189 logs = filterLogs(unfiltered, nil, nil, f.addresses, f.topics) 190 if len(logs) > 0 { 191 return logs, uint64(blockNumber), nil 192 } 193 } 194 } 195 196 return logs, end, nil 197 } 198 199 func includes(addresses []common.Address, a common.Address) bool { 200 for _, addr := range addresses { 201 if addr == a { 202 return true 203 } 204 } 205 206 return false 207 } 208 209 // filterLogs creates a slice of logs matching the given criteria. 210 func filterLogs(logs []*types.Log, fromBlock, toBlock *big.Int, addresses []common.Address, topics [][]common.Hash) []*types.Log { 211 var ret []*types.Log 212 Logs: 213 for _, log := range logs { 214 if fromBlock != nil && fromBlock.Int64() >= 0 && fromBlock.Uint64() > log.BlockNumber { 215 continue 216 } 217 if toBlock != nil && toBlock.Int64() >= 0 && toBlock.Uint64() < log.BlockNumber { 218 continue 219 } 220 221 if len(addresses) > 0 && !includes(addresses, log.Address) { 222 continue 223 } 224 225 logTopics := make([]common.Hash, len(topics)) 226 copy(logTopics, log.Topics) 227 228 // If the to filtered topics is greater than the amount of topics in logs, skip. 229 if len(topics) > len(log.Topics) { 230 continue Logs 231 } 232 233 for i, topics := range topics { 234 var match bool 235 for _, topic := range topics { 236 // common.Hash{} is a match all (wildcard) 237 if (topic == common.Hash{}) || log.Topics[i] == topic { 238 match = true 239 break 240 } 241 } 242 243 if !match { 244 continue Logs 245 } 246 } 247 ret = append(ret, log) 248 } 249 250 return ret 251 } 252 253 func (f *Filter) bloomFilter(bloom types.Bloom) bool { 254 return bloomFilter(bloom, f.addresses, f.topics) 255 } 256 257 func bloomFilter(bloom types.Bloom, addresses []common.Address, topics [][]common.Hash) bool { 258 if len(addresses) > 0 { 259 var included bool 260 for _, addr := range addresses { 261 if types.BloomLookup(bloom, addr) { 262 included = true 263 break 264 } 265 } 266 267 if !included { 268 return false 269 } 270 } 271 272 for _, sub := range topics { 273 var included bool 274 for _, topic := range sub { 275 if (topic == common.Hash{}) || types.BloomLookup(bloom, topic) { 276 included = true 277 break 278 } 279 } 280 if !included { 281 return false 282 } 283 } 284 285 return true 286 }