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