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