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