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