github.com/ethereumproject/go-ethereum@v5.5.2+incompatible/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 23 "github.com/ethereumproject/go-ethereum/common" 24 "github.com/ethereumproject/go-ethereum/core" 25 "github.com/ethereumproject/go-ethereum/core/types" 26 "github.com/ethereumproject/go-ethereum/core/vm" 27 "github.com/ethereumproject/go-ethereum/ethdb" 28 ) 29 30 type AccountChange struct { 31 Address, StateAddress []byte 32 } 33 34 // Filtering interface 35 type Filter struct { 36 created time.Time 37 38 db ethdb.Database 39 begin, end int64 40 addresses []common.Address 41 topics [][]common.Hash 42 43 BlockCallback func(*types.Block, vm.Logs) 44 TransactionCallback func(*types.Transaction) 45 LogCallback func(*vm.Log, bool) 46 } 47 48 // Create a new filter which uses a bloom filter on blocks to figure out whether a particular block 49 // is interesting or not. 50 func New(db ethdb.Database) *Filter { 51 return &Filter{db: db} 52 } 53 54 // Set the earliest and latest block for filtering. 55 // -1 = latest block (i.e., the current block) 56 // hash = particular hash from-to 57 func (self *Filter) SetBeginBlock(begin int64) { 58 self.begin = begin 59 } 60 61 func (self *Filter) SetEndBlock(end int64) { 62 self.end = end 63 } 64 65 func (self *Filter) SetAddresses(addr []common.Address) { 66 self.addresses = addr 67 } 68 69 func (self *Filter) SetTopics(topics [][]common.Hash) { 70 self.topics = topics 71 } 72 73 // Run filters logs with the current parameters set 74 func (self *Filter) Find() vm.Logs { 75 latestBlock := core.GetBlock(self.db, core.GetHeadBlockHash(self.db)) 76 if latestBlock == nil { 77 return vm.Logs{} 78 } 79 var beginBlockNo uint64 = uint64(self.begin) 80 if self.begin == -1 { 81 beginBlockNo = latestBlock.NumberU64() 82 } 83 var endBlockNo uint64 = uint64(self.end) 84 if self.end == -1 { 85 endBlockNo = latestBlock.NumberU64() 86 } 87 88 // if no addresses are present we can't make use of fast search which 89 // uses the mipmap bloom filters to check for fast inclusion and uses 90 // higher range probability in order to ensure at least a false positive 91 if len(self.addresses) == 0 { 92 return self.getLogs(beginBlockNo, endBlockNo) 93 } 94 return self.mipFind(beginBlockNo, endBlockNo, 0) 95 } 96 97 func (self *Filter) mipFind(start, end uint64, depth int) (logs vm.Logs) { 98 level := core.MIPMapLevels[depth] 99 // normalise numerator so we can work in level specific batches and 100 // work with the proper range checks 101 for num := start / level * level; num <= end; num += level { 102 // find addresses in bloom filters 103 bloom := core.GetMipmapBloom(self.db, num, level) 104 for _, addr := range self.addresses { 105 if types.BloomLookup(bloom, addr[:]) { 106 // range check normalised values and make sure that 107 // we're resolving the correct range instead of the 108 // normalised values. 109 start := uint64(math.Max(float64(num), float64(start))) 110 end := uint64(math.Min(float64(num+level-1), float64(end))) 111 if depth+1 == len(core.MIPMapLevels) { 112 logs = append(logs, self.getLogs(start, end)...) 113 } else { 114 logs = append(logs, self.mipFind(start, end, depth+1)...) 115 } 116 // break so we don't check the same range for each 117 // possible address. Checks on multiple addresses 118 // are handled further down the stack. 119 break 120 } 121 } 122 } 123 124 return logs 125 } 126 127 func (self *Filter) getLogs(start, end uint64) (logs vm.Logs) { 128 for i := start; i <= end; i++ { 129 var block *types.Block 130 hash := core.GetCanonicalHash(self.db, i) 131 if hash != (common.Hash{}) { 132 block = core.GetBlock(self.db, hash) 133 } 134 if block == nil { // block not found/written 135 return logs 136 } 137 138 // Use bloom filtering to see if this block is interesting given the 139 // current parameters 140 if self.bloomFilter(block) { 141 // Get the logs of the block 142 var ( 143 receipts = core.GetBlockReceipts(self.db, block.Hash()) 144 unfiltered vm.Logs 145 ) 146 for _, receipt := range receipts { 147 unfiltered = append(unfiltered, receipt.Logs...) 148 } 149 logs = append(logs, self.FilterLogs(unfiltered)...) 150 } 151 } 152 153 return logs 154 } 155 156 func includes(addresses []common.Address, a common.Address) bool { 157 for _, addr := range addresses { 158 if addr == a { 159 return true 160 } 161 } 162 163 return false 164 } 165 166 func (self *Filter) FilterLogs(logs vm.Logs) vm.Logs { 167 var ret vm.Logs 168 169 // Filter the logs for interesting stuff 170 Logs: 171 for _, log := range logs { 172 if len(self.addresses) > 0 && !includes(self.addresses, log.Address) { 173 continue 174 } 175 176 logTopics := make([]common.Hash, len(self.topics)) 177 copy(logTopics, log.Topics) 178 179 // If the to filtered topics is greater than the amount of topics in 180 // logs, skip. 181 if len(self.topics) > len(log.Topics) { 182 continue Logs 183 } 184 185 for i, topics := range self.topics { 186 var match bool 187 for _, topic := range topics { 188 // common.Hash{} is a match all (wildcard) 189 if (topic == common.Hash{}) || log.Topics[i] == topic { 190 match = true 191 break 192 } 193 } 194 195 if !match { 196 continue Logs 197 } 198 199 } 200 201 ret = append(ret, log) 202 } 203 204 return ret 205 } 206 207 func (self *Filter) bloomFilter(block *types.Block) bool { 208 if len(self.addresses) > 0 { 209 var included bool 210 for _, addr := range self.addresses { 211 if types.BloomLookup(block.Bloom(), addr[:]) { 212 included = true 213 break 214 } 215 } 216 217 if !included { 218 return false 219 } 220 } 221 222 for _, sub := range self.topics { 223 var included bool 224 for _, topic := range sub { 225 if (topic == common.Hash{}) || types.BloomLookup(block.Bloom(), topic[:]) { 226 included = true 227 break 228 } 229 } 230 if !included { 231 return false 232 } 233 } 234 235 return true 236 }