github.com/theQRL/go-zond@v0.1.1/les/retrieve.go (about) 1 // Copyright 2017 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 les 18 19 import ( 20 "context" 21 "errors" 22 "sync" 23 "time" 24 25 "github.com/theQRL/go-zond/light" 26 ) 27 28 var ( 29 retryQueue = time.Millisecond * 100 30 hardRequestTimeout = time.Second * 10 31 ) 32 33 // retrieveManager is a layer on top of requestDistributor which takes care of 34 // matching replies by request ID and handles timeouts and resends if necessary. 35 type retrieveManager struct { 36 dist *requestDistributor 37 peers *serverPeerSet 38 softRequestTimeout func() time.Duration 39 40 lock sync.RWMutex 41 sentReqs map[uint64]*sentReq 42 } 43 44 // validatorFunc is a function that processes a reply message 45 type validatorFunc func(distPeer, *Msg) error 46 47 // sentReq represents a request sent and tracked by retrieveManager 48 type sentReq struct { 49 rm *retrieveManager 50 req *distReq 51 id uint64 52 validate validatorFunc 53 54 eventsCh chan reqPeerEvent 55 stopCh chan struct{} 56 stopped bool 57 err error 58 59 lock sync.RWMutex // protect access to sentTo map 60 sentTo map[distPeer]sentReqToPeer 61 62 lastReqQueued bool // last request has been queued but not sent 63 lastReqSentTo distPeer // if not nil then last request has been sent to given peer but not timed out 64 reqSrtoCount int // number of requests that reached soft (but not hard) timeout 65 } 66 67 // sentReqToPeer notifies the request-from-peer goroutine (tryRequest) about a response 68 // delivered by the given peer. Only one delivery is allowed per request per peer, 69 // after which delivered is set to true, the validity of the response is sent on the 70 // valid channel and no more responses are accepted. 71 type sentReqToPeer struct { 72 delivered, frozen bool 73 event chan int 74 } 75 76 // reqPeerEvent is sent by the request-from-peer goroutine (tryRequest) to the 77 // request state machine (retrieveLoop) through the eventsCh channel. 78 type reqPeerEvent struct { 79 event int 80 peer distPeer 81 } 82 83 const ( 84 rpSent = iota // if peer == nil, not sent (no suitable peers) 85 rpSoftTimeout 86 rpHardTimeout 87 rpDeliveredValid 88 rpDeliveredInvalid 89 rpNotDelivered 90 ) 91 92 // newRetrieveManager creates the retrieve manager 93 func newRetrieveManager(peers *serverPeerSet, dist *requestDistributor, srto func() time.Duration) *retrieveManager { 94 return &retrieveManager{ 95 peers: peers, 96 dist: dist, 97 sentReqs: make(map[uint64]*sentReq), 98 softRequestTimeout: srto, 99 } 100 } 101 102 // retrieve sends a request (to multiple peers if necessary) and waits for an answer 103 // that is delivered through the deliver function and successfully validated by the 104 // validator callback. It returns when a valid answer is delivered or the context is 105 // cancelled. 106 func (rm *retrieveManager) retrieve(ctx context.Context, reqID uint64, req *distReq, val validatorFunc, shutdown chan struct{}) error { 107 sentReq := rm.sendReq(reqID, req, val) 108 select { 109 case <-sentReq.stopCh: 110 case <-ctx.Done(): 111 sentReq.stop(ctx.Err()) 112 case <-shutdown: 113 sentReq.stop(errors.New("client is shutting down")) 114 } 115 return sentReq.getError() 116 } 117 118 // sendReq starts a process that keeps trying to retrieve a valid answer for a 119 // request from any suitable peers until stopped or succeeded. 120 func (rm *retrieveManager) sendReq(reqID uint64, req *distReq, val validatorFunc) *sentReq { 121 r := &sentReq{ 122 rm: rm, 123 req: req, 124 id: reqID, 125 sentTo: make(map[distPeer]sentReqToPeer), 126 stopCh: make(chan struct{}), 127 eventsCh: make(chan reqPeerEvent, 10), 128 validate: val, 129 } 130 131 canSend := req.canSend 132 req.canSend = func(p distPeer) bool { 133 // add an extra check to canSend: the request has not been sent to the same peer before 134 r.lock.RLock() 135 _, sent := r.sentTo[p] 136 r.lock.RUnlock() 137 return !sent && canSend(p) 138 } 139 140 request := req.request 141 req.request = func(p distPeer) func() { 142 // before actually sending the request, put an entry into the sentTo map 143 r.lock.Lock() 144 r.sentTo[p] = sentReqToPeer{delivered: false, frozen: false, event: make(chan int, 1)} 145 r.lock.Unlock() 146 return request(p) 147 } 148 rm.lock.Lock() 149 rm.sentReqs[reqID] = r 150 rm.lock.Unlock() 151 152 go r.retrieveLoop() 153 return r 154 } 155 156 // deliver is called by the LES protocol manager to deliver reply messages to waiting requests 157 func (rm *retrieveManager) deliver(peer distPeer, msg *Msg) error { 158 rm.lock.RLock() 159 req, ok := rm.sentReqs[msg.ReqID] 160 rm.lock.RUnlock() 161 162 if ok { 163 return req.deliver(peer, msg) 164 } 165 return errResp(ErrUnexpectedResponse, "reqID = %v", msg.ReqID) 166 } 167 168 // frozen is called by the LES protocol manager when a server has suspended its service and we 169 // should not expect an answer for the requests already sent there 170 func (rm *retrieveManager) frozen(peer distPeer) { 171 rm.lock.RLock() 172 defer rm.lock.RUnlock() 173 174 for _, req := range rm.sentReqs { 175 req.frozen(peer) 176 } 177 } 178 179 // reqStateFn represents a state of the retrieve loop state machine 180 type reqStateFn func() reqStateFn 181 182 // retrieveLoop is the retrieval state machine event loop 183 func (r *sentReq) retrieveLoop() { 184 go r.tryRequest() 185 r.lastReqQueued = true 186 state := r.stateRequesting 187 188 for state != nil { 189 state = state() 190 } 191 192 r.rm.lock.Lock() 193 delete(r.rm.sentReqs, r.id) 194 r.rm.lock.Unlock() 195 } 196 197 // stateRequesting: a request has been queued or sent recently; when it reaches soft timeout, 198 // a new request is sent to a new peer 199 func (r *sentReq) stateRequesting() reqStateFn { 200 select { 201 case ev := <-r.eventsCh: 202 r.update(ev) 203 switch ev.event { 204 case rpSent: 205 if ev.peer == nil { 206 // request send failed, no more suitable peers 207 if r.waiting() { 208 // we are already waiting for sent requests which may succeed so keep waiting 209 return r.stateNoMorePeers 210 } 211 // nothing to wait for, no more peers to ask, return with error 212 r.stop(light.ErrNoPeers) 213 // no need to go to stopped state because waiting() already returned false 214 return nil 215 } 216 case rpSoftTimeout: 217 // last request timed out, try asking a new peer 218 go r.tryRequest() 219 r.lastReqQueued = true 220 return r.stateRequesting 221 case rpDeliveredInvalid, rpNotDelivered: 222 // if it was the last sent request (set to nil by update) then start a new one 223 if !r.lastReqQueued && r.lastReqSentTo == nil { 224 go r.tryRequest() 225 r.lastReqQueued = true 226 } 227 return r.stateRequesting 228 case rpDeliveredValid: 229 r.stop(nil) 230 return r.stateStopped 231 } 232 return r.stateRequesting 233 case <-r.stopCh: 234 return r.stateStopped 235 } 236 } 237 238 // stateNoMorePeers: could not send more requests because no suitable peers are available. 239 // Peers may become suitable for a certain request later or new peers may appear so we 240 // keep trying. 241 func (r *sentReq) stateNoMorePeers() reqStateFn { 242 select { 243 case <-time.After(retryQueue): 244 go r.tryRequest() 245 r.lastReqQueued = true 246 return r.stateRequesting 247 case ev := <-r.eventsCh: 248 r.update(ev) 249 if ev.event == rpDeliveredValid { 250 r.stop(nil) 251 return r.stateStopped 252 } 253 if r.waiting() { 254 return r.stateNoMorePeers 255 } 256 r.stop(light.ErrNoPeers) 257 return nil 258 case <-r.stopCh: 259 return r.stateStopped 260 } 261 } 262 263 // stateStopped: request succeeded or cancelled, just waiting for some peers 264 // to either answer or time out hard 265 func (r *sentReq) stateStopped() reqStateFn { 266 for r.waiting() { 267 r.update(<-r.eventsCh) 268 } 269 return nil 270 } 271 272 // update updates the queued/sent flags and timed out peers counter according to the event 273 func (r *sentReq) update(ev reqPeerEvent) { 274 switch ev.event { 275 case rpSent: 276 r.lastReqQueued = false 277 r.lastReqSentTo = ev.peer 278 case rpSoftTimeout: 279 r.lastReqSentTo = nil 280 r.reqSrtoCount++ 281 case rpHardTimeout: 282 r.reqSrtoCount-- 283 case rpDeliveredValid, rpDeliveredInvalid, rpNotDelivered: 284 if ev.peer == r.lastReqSentTo { 285 r.lastReqSentTo = nil 286 } else { 287 r.reqSrtoCount-- 288 } 289 } 290 } 291 292 // waiting returns true if the retrieval mechanism is waiting for an answer from 293 // any peer 294 func (r *sentReq) waiting() bool { 295 return r.lastReqQueued || r.lastReqSentTo != nil || r.reqSrtoCount > 0 296 } 297 298 // tryRequest tries to send the request to a new peer and waits for it to either 299 // succeed or time out if it has been sent. It also sends the appropriate reqPeerEvent 300 // messages to the request's event channel. 301 func (r *sentReq) tryRequest() { 302 sent := r.rm.dist.queue(r.req) 303 var p distPeer 304 select { 305 case p = <-sent: 306 case <-r.stopCh: 307 if r.rm.dist.cancel(r.req) { 308 p = nil 309 } else { 310 p = <-sent 311 } 312 } 313 314 r.eventsCh <- reqPeerEvent{rpSent, p} 315 if p == nil { 316 return 317 } 318 319 hrto := false 320 321 r.lock.RLock() 322 s, ok := r.sentTo[p] 323 r.lock.RUnlock() 324 if !ok { 325 panic(nil) 326 } 327 328 defer func() { 329 pp, ok := p.(*serverPeer) 330 if hrto && ok { 331 pp.Log().Debug("Request timed out hard") 332 if r.rm.peers != nil { 333 r.rm.peers.unregister(pp.id) 334 } 335 } 336 }() 337 338 select { 339 case event := <-s.event: 340 if event == rpNotDelivered { 341 r.lock.Lock() 342 delete(r.sentTo, p) 343 r.lock.Unlock() 344 } 345 r.eventsCh <- reqPeerEvent{event, p} 346 return 347 case <-time.After(r.rm.softRequestTimeout()): 348 r.eventsCh <- reqPeerEvent{rpSoftTimeout, p} 349 } 350 351 select { 352 case event := <-s.event: 353 if event == rpNotDelivered { 354 r.lock.Lock() 355 delete(r.sentTo, p) 356 r.lock.Unlock() 357 } 358 r.eventsCh <- reqPeerEvent{event, p} 359 case <-time.After(hardRequestTimeout): 360 hrto = true 361 r.eventsCh <- reqPeerEvent{rpHardTimeout, p} 362 } 363 } 364 365 // deliver a reply belonging to this request 366 func (r *sentReq) deliver(peer distPeer, msg *Msg) error { 367 r.lock.Lock() 368 defer r.lock.Unlock() 369 370 s, ok := r.sentTo[peer] 371 if !ok || s.delivered { 372 return errResp(ErrUnexpectedResponse, "reqID = %v", msg.ReqID) 373 } 374 if s.frozen { 375 return nil 376 } 377 valid := r.validate(peer, msg) == nil 378 r.sentTo[peer] = sentReqToPeer{delivered: true, frozen: false, event: s.event} 379 if valid { 380 s.event <- rpDeliveredValid 381 } else { 382 s.event <- rpDeliveredInvalid 383 } 384 if !valid { 385 return errResp(ErrInvalidResponse, "reqID = %v", msg.ReqID) 386 } 387 return nil 388 } 389 390 // frozen sends a "not delivered" event to the peer event channel belonging to the 391 // given peer if the request has been sent there, causing the state machine to not 392 // expect an answer and potentially even send the request to the same peer again 393 // when canSend allows it. 394 func (r *sentReq) frozen(peer distPeer) { 395 r.lock.Lock() 396 defer r.lock.Unlock() 397 398 s, ok := r.sentTo[peer] 399 if ok && !s.delivered && !s.frozen { 400 r.sentTo[peer] = sentReqToPeer{delivered: false, frozen: true, event: s.event} 401 s.event <- rpNotDelivered 402 } 403 } 404 405 // stop stops the retrieval process and sets an error code that will be returned 406 // by getError 407 func (r *sentReq) stop(err error) { 408 r.lock.Lock() 409 if !r.stopped { 410 r.stopped = true 411 r.err = err 412 close(r.stopCh) 413 } 414 r.lock.Unlock() 415 } 416 417 // getError returns any retrieval error (either internally generated or set by the 418 // stop function) after stopCh has been closed 419 func (r *sentReq) getError() error { 420 return r.err 421 }