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