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