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