github.com/phillinzzz/newBsc@v1.1.6/p2p/discover/lookup.go (about) 1 // Copyright 2019 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 discover 18 19 import ( 20 "context" 21 "time" 22 23 "github.com/phillinzzz/newBsc/common/gopool" 24 "github.com/phillinzzz/newBsc/p2p/enode" 25 ) 26 27 // lookup performs a network search for nodes close to the given target. It approaches the 28 // target by querying nodes that are closer to it on each iteration. The given target does 29 // not need to be an actual node identifier. 30 type lookup struct { 31 tab *Table 32 queryfunc func(*node) ([]*node, error) 33 replyCh chan []*node 34 cancelCh <-chan struct{} 35 asked, seen map[enode.ID]bool 36 result nodesByDistance 37 replyBuffer []*node 38 queries int 39 } 40 41 type queryFunc func(*node) ([]*node, error) 42 43 func newLookup(ctx context.Context, tab *Table, target enode.ID, q queryFunc) *lookup { 44 it := &lookup{ 45 tab: tab, 46 queryfunc: q, 47 asked: make(map[enode.ID]bool), 48 seen: make(map[enode.ID]bool), 49 result: nodesByDistance{target: target}, 50 replyCh: make(chan []*node, alpha), 51 cancelCh: ctx.Done(), 52 queries: -1, 53 } 54 // Don't query further if we hit ourself. 55 // Unlikely to happen often in practice. 56 it.asked[tab.self().ID()] = true 57 return it 58 } 59 60 // run runs the lookup to completion and returns the closest nodes found. 61 func (it *lookup) run() []*enode.Node { 62 for it.advance() { 63 } 64 return unwrapNodes(it.result.entries) 65 } 66 67 // advance advances the lookup until any new nodes have been found. 68 // It returns false when the lookup has ended. 69 func (it *lookup) advance() bool { 70 for it.startQueries() { 71 select { 72 case nodes := <-it.replyCh: 73 it.replyBuffer = it.replyBuffer[:0] 74 for _, n := range nodes { 75 if n != nil && !it.seen[n.ID()] { 76 it.seen[n.ID()] = true 77 it.result.push(n, bucketSize) 78 it.replyBuffer = append(it.replyBuffer, n) 79 } 80 } 81 it.queries-- 82 if len(it.replyBuffer) > 0 { 83 return true 84 } 85 case <-it.cancelCh: 86 it.shutdown() 87 } 88 } 89 return false 90 } 91 92 func (it *lookup) shutdown() { 93 for it.queries > 0 { 94 <-it.replyCh 95 it.queries-- 96 } 97 it.queryfunc = nil 98 it.replyBuffer = nil 99 } 100 101 func (it *lookup) startQueries() bool { 102 if it.queryfunc == nil { 103 return false 104 } 105 106 // The first query returns nodes from the local table. 107 if it.queries == -1 { 108 closest := it.tab.findnodeByID(it.result.target, bucketSize, false) 109 // Avoid finishing the lookup too quickly if table is empty. It'd be better to wait 110 // for the table to fill in this case, but there is no good mechanism for that 111 // yet. 112 if len(closest.entries) == 0 { 113 it.slowdown() 114 } 115 it.queries = 1 116 it.replyCh <- closest.entries 117 return true 118 } 119 120 // Ask the closest nodes that we haven't asked yet. 121 for i := 0; i < len(it.result.entries) && it.queries < alpha; i++ { 122 n := it.result.entries[i] 123 if !it.asked[n.ID()] { 124 it.asked[n.ID()] = true 125 it.queries++ 126 gopool.Submit(func() { 127 it.query(n, it.replyCh) 128 }) 129 } 130 } 131 // The lookup ends when no more nodes can be asked. 132 return it.queries > 0 133 } 134 135 func (it *lookup) slowdown() { 136 sleep := time.NewTimer(1 * time.Second) 137 defer sleep.Stop() 138 select { 139 case <-sleep.C: 140 case <-it.tab.closeReq: 141 } 142 } 143 144 func (it *lookup) query(n *node, reply chan<- []*node) { 145 fails := it.tab.db.FindFails(n.ID(), n.IP()) 146 r, err := it.queryfunc(n) 147 if err == errClosed { 148 // Avoid recording failures on shutdown. 149 reply <- nil 150 return 151 } else if len(r) == 0 { 152 fails++ 153 it.tab.db.UpdateFindFails(n.ID(), n.IP(), fails) 154 // Remove the node from the local table if it fails to return anything useful too 155 // many times, but only if there are enough other nodes in the bucket. 156 dropped := false 157 if fails >= maxFindnodeFailures && it.tab.bucketLen(n.ID()) >= bucketSize/2 { 158 dropped = true 159 it.tab.delete(n) 160 } 161 it.tab.log.Trace("FINDNODE failed", "id", n.ID(), "failcount", fails, "dropped", dropped, "err", err) 162 } else if fails > 0 { 163 // Reset failure counter because it counts _consecutive_ failures. 164 it.tab.db.UpdateFindFails(n.ID(), n.IP(), 0) 165 } 166 167 // Grab as many nodes as possible. Some of them might not be alive anymore, but we'll 168 // just remove those again during revalidation. 169 for _, n := range r { 170 it.tab.addSeenNode(n) 171 } 172 reply <- r 173 } 174 175 // lookupIterator performs lookup operations and iterates over all seen nodes. 176 // When a lookup finishes, a new one is created through nextLookup. 177 type lookupIterator struct { 178 buffer []*node 179 nextLookup lookupFunc 180 ctx context.Context 181 cancel func() 182 lookup *lookup 183 } 184 185 type lookupFunc func(ctx context.Context) *lookup 186 187 func newLookupIterator(ctx context.Context, next lookupFunc) *lookupIterator { 188 ctx, cancel := context.WithCancel(ctx) 189 return &lookupIterator{ctx: ctx, cancel: cancel, nextLookup: next} 190 } 191 192 // Node returns the current node. 193 func (it *lookupIterator) Node() *enode.Node { 194 if len(it.buffer) == 0 { 195 return nil 196 } 197 return unwrapNode(it.buffer[0]) 198 } 199 200 // Next moves to the next node. 201 func (it *lookupIterator) Next() bool { 202 // Consume next node in buffer. 203 if len(it.buffer) > 0 { 204 it.buffer = it.buffer[1:] 205 } 206 // Advance the lookup to refill the buffer. 207 for len(it.buffer) == 0 { 208 if it.ctx.Err() != nil { 209 it.lookup = nil 210 it.buffer = nil 211 return false 212 } 213 if it.lookup == nil { 214 it.lookup = it.nextLookup(it.ctx) 215 continue 216 } 217 if !it.lookup.advance() { 218 it.lookup = nil 219 continue 220 } 221 it.buffer = it.lookup.replyBuffer 222 } 223 return true 224 } 225 226 // Close ends the iterator. 227 func (it *lookupIterator) Close() { 228 it.cancel() 229 }