github.com/core-coin/go-core/v2@v2.1.9/les/retrieve.go (about)

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