github.com/nitinawathare/ethereumassignment3@v0.0.0-20211021213010-f07344c2b868/go-ethereum/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  	"crypto/rand"
    22  	"encoding/binary"
    23  	"fmt"
    24  	"sync"
    25  	"time"
    26  
    27  	"github.com/ethereum/go-ethereum/common/mclock"
    28  	"github.com/ethereum/go-ethereum/light"
    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  	lastReqQueued bool     // last request has been queued but not sent
    72  	lastReqSentTo distPeer // if not nil then last request has been sent to given peer 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, shutdown chan struct{}) error {
   115  	sentReq := rm.sendReq(reqID, req, val)
   116  	select {
   117  	case <-sentReq.stopCh:
   118  	case <-ctx.Done():
   119  		sentReq.stop(ctx.Err())
   120  	case <-shutdown:
   121  		sentReq.stop(fmt.Errorf("Client is shutting down"))
   122  	}
   123  	return sentReq.getError()
   124  }
   125  
   126  // sendReq starts a process that keeps trying to retrieve a valid answer for a
   127  // request from any suitable peers until stopped or succeeded.
   128  func (rm *retrieveManager) sendReq(reqID uint64, req *distReq, val validatorFunc) *sentReq {
   129  	r := &sentReq{
   130  		rm:       rm,
   131  		req:      req,
   132  		id:       reqID,
   133  		sentTo:   make(map[distPeer]sentReqToPeer),
   134  		stopCh:   make(chan struct{}),
   135  		eventsCh: make(chan reqPeerEvent, 10),
   136  		validate: val,
   137  	}
   138  
   139  	canSend := req.canSend
   140  	req.canSend = func(p distPeer) bool {
   141  		// add an extra check to canSend: the request has not been sent to the same peer before
   142  		r.lock.RLock()
   143  		_, sent := r.sentTo[p]
   144  		r.lock.RUnlock()
   145  		return !sent && canSend(p)
   146  	}
   147  
   148  	request := req.request
   149  	req.request = func(p distPeer) func() {
   150  		// before actually sending the request, put an entry into the sentTo map
   151  		r.lock.Lock()
   152  		r.sentTo[p] = sentReqToPeer{false, make(chan bool, 1)}
   153  		r.lock.Unlock()
   154  		return request(p)
   155  	}
   156  	rm.lock.Lock()
   157  	rm.sentReqs[reqID] = r
   158  	rm.lock.Unlock()
   159  
   160  	go r.retrieveLoop()
   161  	return r
   162  }
   163  
   164  // deliver is called by the LES protocol manager to deliver reply messages to waiting requests
   165  func (rm *retrieveManager) deliver(peer distPeer, msg *Msg) error {
   166  	rm.lock.RLock()
   167  	req, ok := rm.sentReqs[msg.ReqID]
   168  	rm.lock.RUnlock()
   169  
   170  	if ok {
   171  		return req.deliver(peer, msg)
   172  	}
   173  	return errResp(ErrUnexpectedResponse, "reqID = %v", msg.ReqID)
   174  }
   175  
   176  // reqStateFn represents a state of the retrieve loop state machine
   177  type reqStateFn func() reqStateFn
   178  
   179  // retrieveLoop is the retrieval state machine event loop
   180  func (r *sentReq) retrieveLoop() {
   181  	go r.tryRequest()
   182  	r.lastReqQueued = true
   183  	state := r.stateRequesting
   184  
   185  	for state != nil {
   186  		state = state()
   187  	}
   188  
   189  	r.rm.lock.Lock()
   190  	delete(r.rm.sentReqs, r.id)
   191  	r.rm.lock.Unlock()
   192  }
   193  
   194  // stateRequesting: a request has been queued or sent recently; when it reaches soft timeout,
   195  // a new request is sent to a new peer
   196  func (r *sentReq) stateRequesting() reqStateFn {
   197  	select {
   198  	case ev := <-r.eventsCh:
   199  		r.update(ev)
   200  		switch ev.event {
   201  		case rpSent:
   202  			if ev.peer == nil {
   203  				// request send failed, no more suitable peers
   204  				if r.waiting() {
   205  					// we are already waiting for sent requests which may succeed so keep waiting
   206  					return r.stateNoMorePeers
   207  				}
   208  				// nothing to wait for, no more peers to ask, return with error
   209  				r.stop(light.ErrNoPeers)
   210  				// no need to go to stopped state because waiting() already returned false
   211  				return nil
   212  			}
   213  		case rpSoftTimeout:
   214  			// last request timed out, try asking a new peer
   215  			go r.tryRequest()
   216  			r.lastReqQueued = true
   217  			return r.stateRequesting
   218  		case rpDeliveredInvalid:
   219  			// if it was the last sent request (set to nil by update) then start a new one
   220  			if !r.lastReqQueued && r.lastReqSentTo == nil {
   221  				go r.tryRequest()
   222  				r.lastReqQueued = true
   223  			}
   224  			return r.stateRequesting
   225  		case rpDeliveredValid:
   226  			r.stop(nil)
   227  			return r.stateStopped
   228  		}
   229  		return r.stateRequesting
   230  	case <-r.stopCh:
   231  		return r.stateStopped
   232  	}
   233  }
   234  
   235  // stateNoMorePeers: could not send more requests because no suitable peers are available.
   236  // Peers may become suitable for a certain request later or new peers may appear so we
   237  // keep trying.
   238  func (r *sentReq) stateNoMorePeers() reqStateFn {
   239  	select {
   240  	case <-time.After(retryQueue):
   241  		go r.tryRequest()
   242  		r.lastReqQueued = true
   243  		return r.stateRequesting
   244  	case ev := <-r.eventsCh:
   245  		r.update(ev)
   246  		if ev.event == rpDeliveredValid {
   247  			r.stop(nil)
   248  			return r.stateStopped
   249  		}
   250  		if r.waiting() {
   251  			return r.stateNoMorePeers
   252  		}
   253  		r.stop(light.ErrNoPeers)
   254  		return nil
   255  	case <-r.stopCh:
   256  		return r.stateStopped
   257  	}
   258  }
   259  
   260  // stateStopped: request succeeded or cancelled, just waiting for some peers
   261  // to either answer or time out hard
   262  func (r *sentReq) stateStopped() reqStateFn {
   263  	for r.waiting() {
   264  		r.update(<-r.eventsCh)
   265  	}
   266  	return nil
   267  }
   268  
   269  // update updates the queued/sent flags and timed out peers counter according to the event
   270  func (r *sentReq) update(ev reqPeerEvent) {
   271  	switch ev.event {
   272  	case rpSent:
   273  		r.lastReqQueued = false
   274  		r.lastReqSentTo = ev.peer
   275  	case rpSoftTimeout:
   276  		r.lastReqSentTo = nil
   277  		r.reqSrtoCount++
   278  	case rpHardTimeout:
   279  		r.reqSrtoCount--
   280  	case rpDeliveredValid, rpDeliveredInvalid:
   281  		if ev.peer == r.lastReqSentTo {
   282  			r.lastReqSentTo = nil
   283  		} else {
   284  			r.reqSrtoCount--
   285  		}
   286  	}
   287  }
   288  
   289  // waiting returns true if the retrieval mechanism is waiting for an answer from
   290  // any peer
   291  func (r *sentReq) waiting() bool {
   292  	return r.lastReqQueued || r.lastReqSentTo != nil || r.reqSrtoCount > 0
   293  }
   294  
   295  // tryRequest tries to send the request to a new peer and waits for it to either
   296  // succeed or time out if it has been sent. It also sends the appropriate reqPeerEvent
   297  // messages to the request's event channel.
   298  func (r *sentReq) tryRequest() {
   299  	sent := r.rm.dist.queue(r.req)
   300  	var p distPeer
   301  	select {
   302  	case p = <-sent:
   303  	case <-r.stopCh:
   304  		if r.rm.dist.cancel(r.req) {
   305  			p = nil
   306  		} else {
   307  			p = <-sent
   308  		}
   309  	}
   310  
   311  	r.eventsCh <- reqPeerEvent{rpSent, p}
   312  	if p == nil {
   313  		return
   314  	}
   315  
   316  	reqSent := mclock.Now()
   317  	srto, hrto := false, false
   318  
   319  	r.lock.RLock()
   320  	s, ok := r.sentTo[p]
   321  	r.lock.RUnlock()
   322  	if !ok {
   323  		panic(nil)
   324  	}
   325  
   326  	defer func() {
   327  		// send feedback to server pool and remove peer if hard timeout happened
   328  		pp, ok := p.(*peer)
   329  		if ok && r.rm.serverPool != nil {
   330  			respTime := time.Duration(mclock.Now() - reqSent)
   331  			r.rm.serverPool.adjustResponseTime(pp.poolEntry, respTime, srto)
   332  		}
   333  		if hrto {
   334  			pp.Log().Debug("Request timed out hard")
   335  			if r.rm.peers != nil {
   336  				r.rm.peers.Unregister(pp.id)
   337  			}
   338  		}
   339  
   340  		r.lock.Lock()
   341  		delete(r.sentTo, p)
   342  		r.lock.Unlock()
   343  	}()
   344  
   345  	select {
   346  	case ok := <-s.valid:
   347  		if ok {
   348  			r.eventsCh <- reqPeerEvent{rpDeliveredValid, p}
   349  		} else {
   350  			r.eventsCh <- reqPeerEvent{rpDeliveredInvalid, p}
   351  		}
   352  		return
   353  	case <-time.After(softRequestTimeout):
   354  		srto = true
   355  		r.eventsCh <- reqPeerEvent{rpSoftTimeout, p}
   356  	}
   357  
   358  	select {
   359  	case ok := <-s.valid:
   360  		if ok {
   361  			r.eventsCh <- reqPeerEvent{rpDeliveredValid, p}
   362  		} else {
   363  			r.eventsCh <- reqPeerEvent{rpDeliveredInvalid, p}
   364  		}
   365  	case <-time.After(hardRequestTimeout):
   366  		hrto = true
   367  		r.eventsCh <- reqPeerEvent{rpHardTimeout, p}
   368  	}
   369  }
   370  
   371  // deliver a reply belonging to this request
   372  func (r *sentReq) deliver(peer distPeer, msg *Msg) error {
   373  	r.lock.Lock()
   374  	defer r.lock.Unlock()
   375  
   376  	s, ok := r.sentTo[peer]
   377  	if !ok || s.delivered {
   378  		return errResp(ErrUnexpectedResponse, "reqID = %v", msg.ReqID)
   379  	}
   380  	valid := r.validate(peer, msg) == nil
   381  	r.sentTo[peer] = sentReqToPeer{true, s.valid}
   382  	s.valid <- valid
   383  	if !valid {
   384  		return errResp(ErrInvalidResponse, "reqID = %v", msg.ReqID)
   385  	}
   386  	return nil
   387  }
   388  
   389  // stop stops the retrieval process and sets an error code that will be returned
   390  // by getError
   391  func (r *sentReq) stop(err error) {
   392  	r.lock.Lock()
   393  	if !r.stopped {
   394  		r.stopped = true
   395  		r.err = err
   396  		close(r.stopCh)
   397  	}
   398  	r.lock.Unlock()
   399  }
   400  
   401  // getError returns any retrieval error (either internally generated or set by the
   402  // stop function) after stopCh has been closed
   403  func (r *sentReq) getError() error {
   404  	return r.err
   405  }
   406  
   407  // genReqID generates a new random request ID
   408  func genReqID() uint64 {
   409  	var rnd [8]byte
   410  	rand.Read(rnd[:])
   411  	return binary.BigEndian.Uint64(rnd[:])
   412  }