github.com/SmartMeshFoundation/Spectrum@v0.0.0-20220621030607-452a266fee1e/les/retrieve.go (about)

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