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  }