github.com/luckypickle/go-ethereum-vet@v1.14.2/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/luckypickle/go-ethereum-vet/common/mclock" 30 "github.com/luckypickle/go-ethereum-vet/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 rpDeliveredValid: 221 r.stop(nil) 222 return r.stateStopped 223 } 224 return r.stateRequesting 225 case <-r.stopCh: 226 return r.stateStopped 227 } 228 } 229 230 // stateNoMorePeers: could not send more requests because no suitable peers are available. 231 // Peers may become suitable for a certain request later or new peers may appear so we 232 // keep trying. 233 func (r *sentReq) stateNoMorePeers() reqStateFn { 234 select { 235 case <-time.After(retryQueue): 236 go r.tryRequest() 237 r.lastReqQueued = true 238 return r.stateRequesting 239 case ev := <-r.eventsCh: 240 r.update(ev) 241 if ev.event == rpDeliveredValid { 242 r.stop(nil) 243 return r.stateStopped 244 } 245 return r.stateNoMorePeers 246 case <-r.stopCh: 247 return r.stateStopped 248 } 249 } 250 251 // stateStopped: request succeeded or cancelled, just waiting for some peers 252 // to either answer or time out hard 253 func (r *sentReq) stateStopped() reqStateFn { 254 for r.waiting() { 255 r.update(<-r.eventsCh) 256 } 257 return nil 258 } 259 260 // update updates the queued/sent flags and timed out peers counter according to the event 261 func (r *sentReq) update(ev reqPeerEvent) { 262 switch ev.event { 263 case rpSent: 264 r.lastReqQueued = false 265 r.lastReqSentTo = ev.peer 266 case rpSoftTimeout: 267 r.lastReqSentTo = nil 268 r.reqSrtoCount++ 269 case rpHardTimeout: 270 r.reqSrtoCount-- 271 case rpDeliveredValid, rpDeliveredInvalid: 272 if ev.peer == r.lastReqSentTo { 273 r.lastReqSentTo = nil 274 } else { 275 r.reqSrtoCount-- 276 } 277 } 278 } 279 280 // waiting returns true if the retrieval mechanism is waiting for an answer from 281 // any peer 282 func (r *sentReq) waiting() bool { 283 return r.lastReqQueued || r.lastReqSentTo != nil || r.reqSrtoCount > 0 284 } 285 286 // tryRequest tries to send the request to a new peer and waits for it to either 287 // succeed or time out if it has been sent. It also sends the appropriate reqPeerEvent 288 // messages to the request's event channel. 289 func (r *sentReq) tryRequest() { 290 sent := r.rm.dist.queue(r.req) 291 var p distPeer 292 select { 293 case p = <-sent: 294 case <-r.stopCh: 295 if r.rm.dist.cancel(r.req) { 296 p = nil 297 } else { 298 p = <-sent 299 } 300 } 301 302 r.eventsCh <- reqPeerEvent{rpSent, p} 303 if p == nil { 304 return 305 } 306 307 reqSent := mclock.Now() 308 srto, hrto := false, false 309 310 r.lock.RLock() 311 s, ok := r.sentTo[p] 312 r.lock.RUnlock() 313 if !ok { 314 panic(nil) 315 } 316 317 defer func() { 318 // send feedback to server pool and remove peer if hard timeout happened 319 pp, ok := p.(*peer) 320 if ok && r.rm.serverPool != nil { 321 respTime := time.Duration(mclock.Now() - reqSent) 322 r.rm.serverPool.adjustResponseTime(pp.poolEntry, respTime, srto) 323 } 324 if hrto { 325 pp.Log().Debug("Request timed out hard") 326 if r.rm.peers != nil { 327 r.rm.peers.Unregister(pp.id) 328 } 329 } 330 331 r.lock.Lock() 332 delete(r.sentTo, p) 333 r.lock.Unlock() 334 }() 335 336 select { 337 case ok := <-s.valid: 338 if ok { 339 r.eventsCh <- reqPeerEvent{rpDeliveredValid, p} 340 } else { 341 r.eventsCh <- reqPeerEvent{rpDeliveredInvalid, p} 342 } 343 return 344 case <-time.After(softRequestTimeout): 345 srto = true 346 r.eventsCh <- reqPeerEvent{rpSoftTimeout, p} 347 } 348 349 select { 350 case ok := <-s.valid: 351 if ok { 352 r.eventsCh <- reqPeerEvent{rpDeliveredValid, p} 353 } else { 354 r.eventsCh <- reqPeerEvent{rpDeliveredInvalid, p} 355 } 356 case <-time.After(hardRequestTimeout): 357 hrto = true 358 r.eventsCh <- reqPeerEvent{rpHardTimeout, p} 359 } 360 } 361 362 // deliver a reply belonging to this request 363 func (r *sentReq) deliver(peer distPeer, msg *Msg) error { 364 r.lock.Lock() 365 defer r.lock.Unlock() 366 367 s, ok := r.sentTo[peer] 368 if !ok || s.delivered { 369 return errResp(ErrUnexpectedResponse, "reqID = %v", msg.ReqID) 370 } 371 valid := r.validate(peer, msg) == nil 372 r.sentTo[peer] = sentReqToPeer{true, s.valid} 373 s.valid <- valid 374 if !valid { 375 return errResp(ErrInvalidResponse, "reqID = %v", msg.ReqID) 376 } 377 return nil 378 } 379 380 // stop stops the retrieval process and sets an error code that will be returned 381 // by getError 382 func (r *sentReq) stop(err error) { 383 r.lock.Lock() 384 if !r.stopped { 385 r.stopped = true 386 r.err = err 387 close(r.stopCh) 388 } 389 r.lock.Unlock() 390 } 391 392 // getError returns any retrieval error (either internally generated or set by the 393 // stop function) after stopCh has been closed 394 func (r *sentReq) getError() error { 395 return r.err 396 } 397 398 // genReqID generates a new random request ID 399 func genReqID() uint64 { 400 var rnd [8]byte 401 rand.Read(rnd[:]) 402 return binary.BigEndian.Uint64(rnd[:]) 403 }