github.com/jeffallen/go-ethereum@v1.1.4-0.20150910155051-571d3236c49c/core/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 core 18 19 import ( 20 "math" 21 22 "github.com/ethereum/go-ethereum/common" 23 "github.com/ethereum/go-ethereum/core/state" 24 "github.com/ethereum/go-ethereum/core/types" 25 "github.com/ethereum/go-ethereum/logger" 26 "github.com/ethereum/go-ethereum/logger/glog" 27 ) 28 29 type AccountChange struct { 30 Address, StateAddress []byte 31 } 32 33 // Filtering interface 34 type Filter struct { 35 eth Backend 36 earliest int64 37 latest int64 38 skip int 39 address []common.Address 40 max int 41 topics [][]common.Hash 42 43 BlockCallback func(*types.Block, state.Logs) 44 TransactionCallback func(*types.Transaction) 45 LogsCallback func(state.Logs) 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 NewFilter(eth Backend) *Filter { 51 return &Filter{eth: eth} 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) SetEarliestBlock(earliest int64) { 58 self.earliest = earliest 59 } 60 61 func (self *Filter) SetLatestBlock(latest int64) { 62 self.latest = latest 63 } 64 65 func (self *Filter) SetAddress(addr []common.Address) { 66 self.address = addr 67 } 68 69 func (self *Filter) SetTopics(topics [][]common.Hash) { 70 self.topics = topics 71 } 72 73 func (self *Filter) SetMax(max int) { 74 self.max = max 75 } 76 77 func (self *Filter) SetSkip(skip int) { 78 self.skip = skip 79 } 80 81 // Run filters logs with the current parameters set 82 func (self *Filter) Find() state.Logs { 83 earliestBlock := self.eth.ChainManager().CurrentBlock() 84 var earliestBlockNo uint64 = uint64(self.earliest) 85 if self.earliest == -1 { 86 earliestBlockNo = earliestBlock.NumberU64() 87 } 88 var latestBlockNo uint64 = uint64(self.latest) 89 if self.latest == -1 { 90 latestBlockNo = earliestBlock.NumberU64() 91 } 92 93 var ( 94 logs state.Logs 95 block = self.eth.ChainManager().GetBlockByNumber(latestBlockNo) 96 ) 97 98 done: 99 for i := 0; block != nil; i++ { 100 // Quit on latest 101 switch { 102 case block.NumberU64() == 0: 103 break done 104 case block.NumberU64() < earliestBlockNo: 105 break done 106 case self.max <= len(logs): 107 break done 108 } 109 110 // Use bloom filtering to see if this block is interesting given the 111 // current parameters 112 if self.bloomFilter(block) { 113 // Get the logs of the block 114 unfiltered, err := self.eth.BlockProcessor().GetLogs(block) 115 if err != nil { 116 glog.V(logger.Warn).Infoln("err: filter get logs ", err) 117 118 break 119 } 120 121 logs = append(logs, self.FilterLogs(unfiltered)...) 122 } 123 124 block = self.eth.ChainManager().GetBlock(block.ParentHash()) 125 } 126 127 skip := int(math.Min(float64(len(logs)), float64(self.skip))) 128 129 return logs[skip:] 130 } 131 132 func includes(addresses []common.Address, a common.Address) bool { 133 for _, addr := range addresses { 134 if addr != a { 135 return false 136 } 137 } 138 139 return true 140 } 141 142 func (self *Filter) FilterLogs(logs state.Logs) state.Logs { 143 var ret state.Logs 144 145 // Filter the logs for interesting stuff 146 Logs: 147 for _, log := range logs { 148 if len(self.address) > 0 && !includes(self.address, log.Address) { 149 continue 150 } 151 152 logTopics := make([]common.Hash, len(self.topics)) 153 copy(logTopics, log.Topics) 154 155 // If the to filtered topics is greater than the amount of topics in 156 // logs, skip. 157 if len(self.topics) > len(log.Topics) { 158 continue Logs 159 } 160 161 for i, topics := range self.topics { 162 var match bool 163 for _, topic := range topics { 164 // common.Hash{} is a match all (wildcard) 165 if (topic == common.Hash{}) || log.Topics[i] == topic { 166 match = true 167 break 168 } 169 } 170 171 if !match { 172 continue Logs 173 } 174 175 } 176 177 ret = append(ret, log) 178 } 179 180 return ret 181 } 182 183 func (self *Filter) bloomFilter(block *types.Block) bool { 184 if len(self.address) > 0 { 185 var included bool 186 for _, addr := range self.address { 187 if types.BloomLookup(block.Bloom(), addr) { 188 included = true 189 break 190 } 191 } 192 193 if !included { 194 return false 195 } 196 } 197 198 for _, sub := range self.topics { 199 var included bool 200 for _, topic := range sub { 201 if (topic == common.Hash{}) || types.BloomLookup(block.Bloom(), topic) { 202 included = true 203 break 204 } 205 } 206 if !included { 207 return false 208 } 209 } 210 211 return true 212 }