github.com/dominant-strategies/go-quai@v0.28.2/eth/downloader/peer.go (about)

     1  // Copyright 2015 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  // Contains the active peer-set of the downloader, maintaining both failures
    18  // as well as reputation metrics to prioritize the block retrievals.
    19  
    20  package downloader
    21  
    22  import (
    23  	"errors"
    24  	"math/big"
    25  	"sort"
    26  	"sync"
    27  	"sync/atomic"
    28  	"time"
    29  
    30  	"github.com/dominant-strategies/go-quai/common"
    31  	"github.com/dominant-strategies/go-quai/eth/protocols/eth"
    32  	"github.com/dominant-strategies/go-quai/event"
    33  	"github.com/dominant-strategies/go-quai/log"
    34  	"github.com/dominant-strategies/go-quai/p2p/msgrate"
    35  )
    36  
    37  const (
    38  	maxLackingHashes = 4096 // Maximum number of entries allowed on the list or lacking items
    39  )
    40  
    41  var (
    42  	errAlreadyFetching   = errors.New("already fetching blocks from peer")
    43  	errAlreadyRegistered = errors.New("peer is already registered")
    44  	errNotRegistered     = errors.New("peer is not registered")
    45  )
    46  
    47  // peerConnection represents an active peer from which hashes and blocks are retrieved.
    48  type peerConnection struct {
    49  	id string // Unique identifier of the peer
    50  
    51  	headerIdle int32 // Current header activity state of the peer (idle = 0, active = 1)
    52  	blockIdle  int32 // Current block activity state of the peer (idle = 0, active = 1)
    53  
    54  	headerStarted time.Time // Time instance when the last header fetch was started
    55  	blockStarted  time.Time // Time instance when the last block (body) fetch was started
    56  
    57  	rates   *msgrate.Tracker         // Tracker to hone in on the number of items retrievable per second
    58  	lacking map[common.Hash]struct{} // Set of hashes not to request (didn't have previously)
    59  
    60  	peer Peer
    61  
    62  	version uint       // Eth protocol version number to switch strategies
    63  	log     log.Logger // Contextual logger to add extra infos to peer logs
    64  	lock    sync.RWMutex
    65  }
    66  
    67  // LightPeer encapsulates the methods required to synchronise with a remote light peer.
    68  type LightPeer interface {
    69  	Head() (common.Hash, *big.Int, *big.Int, time.Time)
    70  	RequestHeadersByHash(common.Hash, int, uint64, bool, bool) error
    71  	RequestHeadersByNumber(uint64, int, uint64, uint64, bool, bool) error
    72  }
    73  
    74  // Peer encapsulates the methods required to synchronise with a remote full peer.
    75  type Peer interface {
    76  	LightPeer
    77  	RequestBodies([]common.Hash) error
    78  }
    79  
    80  // newPeerConnection creates a new downloader peer.
    81  func newPeerConnection(id string, version uint, peer Peer, logger log.Logger) *peerConnection {
    82  	return &peerConnection{
    83  		id:      id,
    84  		lacking: make(map[common.Hash]struct{}),
    85  		peer:    peer,
    86  		version: version,
    87  		log:     logger,
    88  	}
    89  }
    90  
    91  // Reset clears the internal state of a peer entity.
    92  func (p *peerConnection) Reset() {
    93  	p.lock.Lock()
    94  	defer p.lock.Unlock()
    95  
    96  	atomic.StoreInt32(&p.headerIdle, 0)
    97  	atomic.StoreInt32(&p.blockIdle, 0)
    98  
    99  	p.lacking = make(map[common.Hash]struct{})
   100  }
   101  
   102  // PeerConnection ID returns the unique identifier of the peer.
   103  func (p *peerConnection) ID() string {
   104  	return p.id
   105  }
   106  
   107  // Tracker returns the message rate trackers of the peer.
   108  func (p *peerConnection) Tracker() *msgrate.Tracker {
   109  	return p.rates
   110  }
   111  
   112  // Peer returns the underlying peer entity.
   113  func (p *peerConnection) Peer() Peer {
   114  	return p.peer
   115  }
   116  
   117  // FetchHeaders sends a header retrieval request to the remote peer.
   118  func (p *peerConnection) FetchHeaders(from uint64, count int) error {
   119  	// Short circuit if the peer is already fetching
   120  	if !atomic.CompareAndSwapInt32(&p.headerIdle, 0, 1) {
   121  		return errAlreadyFetching
   122  	}
   123  	p.headerStarted = time.Now()
   124  
   125  	// In the case of prime the required amount is the PrimeSKeletonDist which is the
   126  	// distance between the skeleton headers.
   127  	if common.NodeLocation.Context() == common.PRIME_CTX {
   128  		// Issue the header retrieval request (absolute upwards without gaps)
   129  		go p.peer.RequestHeadersByNumber(from, PrimeSkeletonDist, 1, 0, false, true)
   130  	} else {
   131  		// Issue the header retrieval request (absolute upwards without gaps)
   132  		go p.peer.RequestHeadersByNumber(from, count, 1, 0, false, true)
   133  	}
   134  
   135  	return nil
   136  }
   137  
   138  // FetchBodies sends a block body retrieval request to the remote peer.
   139  func (p *peerConnection) FetchBodies(request *fetchRequest) error {
   140  	// Short circuit if the peer is already fetching
   141  	if !atomic.CompareAndSwapInt32(&p.blockIdle, 0, 1) {
   142  		return errAlreadyFetching
   143  	}
   144  	p.blockStarted = time.Now()
   145  
   146  	go func() {
   147  		// Convert the header set to a retrievable slice
   148  		hashes := make([]common.Hash, 0, len(request.Headers))
   149  		for _, header := range request.Headers {
   150  			hashes = append(hashes, header.Hash())
   151  		}
   152  		p.peer.RequestBodies(hashes)
   153  	}()
   154  
   155  	return nil
   156  }
   157  
   158  // SetHeadersIdle sets the peer to idle, allowing it to execute new header retrieval
   159  // requests. Its estimated header retrieval throughput is updated with that measured
   160  // just now.
   161  func (p *peerConnection) SetHeadersIdle(delivered int, deliveryTime time.Time) {
   162  	p.rates.Update(eth.BlockHeadersMsg, deliveryTime.Sub(p.headerStarted), delivered)
   163  	atomic.StoreInt32(&p.headerIdle, 0)
   164  }
   165  
   166  // SetBodiesIdle sets the peer to idle, allowing it to execute block body retrieval
   167  // requests. Its estimated body retrieval throughput is updated with that measured
   168  // just now.
   169  func (p *peerConnection) SetBodiesIdle(delivered int, deliveryTime time.Time) {
   170  	p.rates.Update(eth.BlockBodiesMsg, deliveryTime.Sub(p.blockStarted), delivered)
   171  	atomic.StoreInt32(&p.blockIdle, 0)
   172  }
   173  
   174  // HeaderCapacity retrieves the peers header download allowance based on its
   175  // previously discovered throughput.
   176  func (p *peerConnection) HeaderCapacity(targetRTT time.Duration) int {
   177  	cap := p.rates.Capacity(eth.BlockHeadersMsg, targetRTT)
   178  	if cap > MaxHeaderFetch {
   179  		cap = MaxHeaderFetch
   180  	}
   181  	return cap
   182  }
   183  
   184  // BlockCapacity retrieves the peers block download allowance based on its
   185  // previously discovered throughput.
   186  func (p *peerConnection) BlockCapacity(targetRTT time.Duration) int {
   187  	cap := p.rates.Capacity(eth.BlockBodiesMsg, targetRTT)
   188  	if cap > MaxBlockFetch {
   189  		cap = MaxBlockFetch
   190  	}
   191  	return cap
   192  }
   193  
   194  // MarkLacking appends a new entity to the set of items (blocks)
   195  // that a peer is known not to have (i.e. have been requested before). If the
   196  // set reaches its maximum allowed capacity, items are randomly dropped off.
   197  func (p *peerConnection) MarkLacking(hash common.Hash) {
   198  	p.lock.Lock()
   199  	defer p.lock.Unlock()
   200  
   201  	for len(p.lacking) >= maxLackingHashes {
   202  		for drop := range p.lacking {
   203  			delete(p.lacking, drop)
   204  			break
   205  		}
   206  	}
   207  	p.lacking[hash] = struct{}{}
   208  }
   209  
   210  // Lacks retrieves whether the hash of a blockchain item is on the peers lacking
   211  // list (i.e. whether we know that the peer does not have it).
   212  func (p *peerConnection) Lacks(hash common.Hash) bool {
   213  	p.lock.RLock()
   214  	defer p.lock.RUnlock()
   215  
   216  	_, ok := p.lacking[hash]
   217  	return ok
   218  }
   219  
   220  // peerSet represents the collection of active peer participating in the chain
   221  // download procedure.
   222  type peerSet struct {
   223  	peers map[string]*peerConnection
   224  	rates *msgrate.Trackers // Set of rate trackers to give the sync a common beat
   225  
   226  	newPeerFeed  event.Feed
   227  	peerDropFeed event.Feed
   228  
   229  	lock sync.RWMutex
   230  }
   231  
   232  // newPeerSet creates a new peer set top track the active download sources.
   233  func newPeerSet() *peerSet {
   234  	return &peerSet{
   235  		peers: make(map[string]*peerConnection),
   236  		rates: msgrate.NewTrackers(&log.Log),
   237  	}
   238  }
   239  
   240  // SubscribeNewPeers subscribes to peer arrival events.
   241  func (ps *peerSet) SubscribeNewPeers(ch chan<- *peerConnection) event.Subscription {
   242  	return ps.newPeerFeed.Subscribe(ch)
   243  }
   244  
   245  // SubscribePeerDrops subscribes to peer departure events.
   246  func (ps *peerSet) SubscribePeerDrops(ch chan<- *peerConnection) event.Subscription {
   247  	return ps.peerDropFeed.Subscribe(ch)
   248  }
   249  
   250  // Reset iterates over the current peer set, and resets each of the known peers
   251  // to prepare for a next batch of block retrieval.
   252  func (ps *peerSet) Reset() {
   253  	ps.lock.RLock()
   254  	defer ps.lock.RUnlock()
   255  
   256  	for _, peer := range ps.peers {
   257  		peer.Reset()
   258  	}
   259  }
   260  
   261  // Register injects a new peer into the working set, or returns an error if the
   262  // peer is already known.
   263  //
   264  // The method also sets the starting throughput values of the new peer to the
   265  // average of all existing peers, to give it a realistic chance of being used
   266  // for data retrievals.
   267  func (ps *peerSet) Register(p *peerConnection) error {
   268  	// Register the new peer with some meaningful defaults
   269  	ps.lock.Lock()
   270  	if _, ok := ps.peers[p.id]; ok {
   271  		ps.lock.Unlock()
   272  		return errAlreadyRegistered
   273  	}
   274  	p.rates = msgrate.NewTracker(ps.rates.MeanCapacities(), ps.rates.MedianRoundTrip())
   275  	if err := ps.rates.Track(p.id, p.rates); err != nil {
   276  		return err
   277  	}
   278  	ps.peers[p.id] = p
   279  	ps.lock.Unlock()
   280  
   281  	ps.newPeerFeed.Send(p)
   282  	return nil
   283  }
   284  
   285  // Unregister removes a remote peer from the active set, disabling any further
   286  // actions to/from that particular entity.
   287  func (ps *peerSet) Unregister(id string) error {
   288  	ps.lock.Lock()
   289  	p, ok := ps.peers[id]
   290  	if !ok {
   291  		ps.lock.Unlock()
   292  		return errNotRegistered
   293  	}
   294  	delete(ps.peers, id)
   295  	ps.rates.Untrack(id)
   296  	ps.lock.Unlock()
   297  
   298  	ps.peerDropFeed.Send(p)
   299  	return nil
   300  }
   301  
   302  // Peer retrieves the registered peer with the given id.
   303  func (ps *peerSet) Peer(id string) *peerConnection {
   304  	ps.lock.RLock()
   305  	defer ps.lock.RUnlock()
   306  
   307  	return ps.peers[id]
   308  }
   309  
   310  // Len returns if the current number of peers in the set.
   311  func (ps *peerSet) Len() int {
   312  	ps.lock.RLock()
   313  	defer ps.lock.RUnlock()
   314  
   315  	return len(ps.peers)
   316  }
   317  
   318  // AllPeers retrieves a flat list of all the peers within the set.
   319  func (ps *peerSet) AllPeers() []*peerConnection {
   320  	ps.lock.RLock()
   321  	defer ps.lock.RUnlock()
   322  
   323  	list := make([]*peerConnection, 0, len(ps.peers))
   324  	for _, p := range ps.peers {
   325  		list = append(list, p)
   326  	}
   327  	return list
   328  }
   329  
   330  // HeaderIdlePeers retrieves a flat list of all the currently header-idle peers
   331  // within the active peer set, ordered by their reputation.
   332  func (ps *peerSet) HeaderIdlePeers() ([]*peerConnection, int) {
   333  	idle := func(p *peerConnection) bool {
   334  		return atomic.LoadInt32(&p.headerIdle) == 0
   335  	}
   336  	throughput := func(p *peerConnection) int {
   337  		return p.rates.Capacity(eth.BlockHeadersMsg, time.Second)
   338  	}
   339  	return ps.idlePeers(eth.QUAI1, eth.QUAI2, idle, throughput)
   340  }
   341  
   342  // BodyIdlePeers retrieves a flat list of all the currently body-idle peers within
   343  // the active peer set, ordered by their reputation.
   344  func (ps *peerSet) BodyIdlePeers() ([]*peerConnection, int) {
   345  	idle := func(p *peerConnection) bool {
   346  		return atomic.LoadInt32(&p.blockIdle) == 0
   347  	}
   348  	throughput := func(p *peerConnection) int {
   349  		return p.rates.Capacity(eth.BlockBodiesMsg, time.Second)
   350  	}
   351  	return ps.idlePeers(eth.QUAI1, eth.QUAI2, idle, throughput)
   352  }
   353  
   354  // idlePeers retrieves a flat list of all currently idle peers satisfying the
   355  // protocol version constraints, using the provided function to check idleness.
   356  // The resulting set of peers are sorted by their capacity.
   357  func (ps *peerSet) idlePeers(minProtocol, maxProtocol uint, idleCheck func(*peerConnection) bool, capacity func(*peerConnection) int) ([]*peerConnection, int) {
   358  	ps.lock.RLock()
   359  	defer ps.lock.RUnlock()
   360  
   361  	var (
   362  		total = 0
   363  		idle  = make([]*peerConnection, 0, len(ps.peers))
   364  		tps   = make([]int, 0, len(ps.peers))
   365  	)
   366  	for _, p := range ps.peers {
   367  		if p.version >= minProtocol && p.version <= maxProtocol {
   368  			if idleCheck(p) {
   369  				idle = append(idle, p)
   370  				tps = append(tps, capacity(p))
   371  			}
   372  			total++
   373  		}
   374  	}
   375  
   376  	// And sort them
   377  	sortPeers := &peerCapacitySort{idle, tps}
   378  	sort.Sort(sortPeers)
   379  	return sortPeers.p, total
   380  }
   381  
   382  // peerCapacitySort implements sort.Interface.
   383  // It sorts peer connections by capacity (descending).
   384  type peerCapacitySort struct {
   385  	p  []*peerConnection
   386  	tp []int
   387  }
   388  
   389  func (ps *peerCapacitySort) Len() int {
   390  	return len(ps.p)
   391  }
   392  
   393  func (ps *peerCapacitySort) Less(i, j int) bool {
   394  	return ps.tp[i] > ps.tp[j]
   395  }
   396  
   397  func (ps *peerCapacitySort) Swap(i, j int) {
   398  	ps.p[i], ps.p[j] = ps.p[j], ps.p[i]
   399  	ps.tp[i], ps.tp[j] = ps.tp[j], ps.tp[i]
   400  }