github.com/aigarnetwork/aigar@v0.0.0-20191115204914-d59a6eb70f8e/p2p/discover/lookup.go (about) 1 // Copyright 2018 The go-ethereum Authors 2 // Copyright 2019 The go-aigar Authors 3 // This file is part of the go-aigar library. 4 // 5 // The go-aigar library is free software: you can redistribute it and/or modify 6 // it under the terms of the GNU Lesser General Public License as published by 7 // the Free Software Foundation, either version 3 of the License, or 8 // (at your option) any later version. 9 // 10 // The go-aigar library is distributed in the hope that it will be useful, 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU Lesser General Public License for more details. 14 // 15 // You should have received a copy of the GNU Lesser General Public License 16 // along with the go-aigar library. If not, see <http://www.gnu.org/licenses/>. 17 18 package discover 19 20 import ( 21 "context" 22 23 "github.com/AigarNetwork/aigar/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 it.queries = 1 111 it.replyCh <- closest.entries 112 return true 113 } 114 115 // Ask the closest nodes that we haven't asked yet. 116 for i := 0; i < len(it.result.entries) && it.queries < alpha; i++ { 117 n := it.result.entries[i] 118 if !it.asked[n.ID()] { 119 it.asked[n.ID()] = true 120 it.queries++ 121 go it.query(n, it.replyCh) 122 } 123 } 124 // The lookup ends when no more nodes can be asked. 125 return it.queries > 0 126 } 127 128 func (it *lookup) query(n *node, reply chan<- []*node) { 129 fails := it.tab.db.FindFails(n.ID(), n.IP()) 130 r, err := it.queryfunc(n) 131 if err == errClosed { 132 // Avoid recording failures on shutdown. 133 reply <- nil 134 return 135 } else if len(r) == 0 { 136 fails++ 137 it.tab.db.UpdateFindFails(n.ID(), n.IP(), fails) 138 it.tab.log.Trace("Findnode failed", "id", n.ID(), "failcount", fails, "err", err) 139 if fails >= maxFindnodeFailures { 140 it.tab.log.Trace("Too many findnode failures, dropping", "id", n.ID(), "failcount", fails) 141 it.tab.delete(n) 142 } 143 } else if fails > 0 { 144 // Reset failure counter because it counts _consecutive_ failures. 145 it.tab.db.UpdateFindFails(n.ID(), n.IP(), 0) 146 } 147 148 // Grab as many nodes as possible. Some of them might not be alive anymore, but we'll 149 // just remove those again during revalidation. 150 for _, n := range r { 151 it.tab.addSeenNode(n) 152 } 153 reply <- r 154 } 155 156 // lookupIterator performs lookup operations and iterates over all seen nodes. 157 // When a lookup finishes, a new one is created through nextLookup. 158 type lookupIterator struct { 159 buffer []*node 160 nextLookup lookupFunc 161 ctx context.Context 162 cancel func() 163 lookup *lookup 164 } 165 166 type lookupFunc func(ctx context.Context) *lookup 167 168 func newLookupIterator(ctx context.Context, next lookupFunc) *lookupIterator { 169 ctx, cancel := context.WithCancel(ctx) 170 return &lookupIterator{ctx: ctx, cancel: cancel, nextLookup: next} 171 } 172 173 // Node returns the current node. 174 func (it *lookupIterator) Node() *enode.Node { 175 if len(it.buffer) == 0 { 176 return nil 177 } 178 return unwrapNode(it.buffer[0]) 179 } 180 181 // Next moves to the next node. 182 func (it *lookupIterator) Next() bool { 183 // Consume next node in buffer. 184 if len(it.buffer) > 0 { 185 it.buffer = it.buffer[1:] 186 } 187 // Advance the lookup to refill the buffer. 188 for len(it.buffer) == 0 { 189 if it.ctx.Err() != nil { 190 it.lookup = nil 191 it.buffer = nil 192 return false 193 } 194 if it.lookup == nil { 195 it.lookup = it.nextLookup(it.ctx) 196 continue 197 } 198 if !it.lookup.advance() { 199 it.lookup = nil 200 continue 201 } 202 it.buffer = it.lookup.replyBuffer 203 } 204 return true 205 } 206 207 // Close ends the iterator. 208 func (it *lookupIterator) Close() { 209 it.cancel() 210 }