github.com/okex/exchain@v1.8.0/libs/tendermint/blockchain/v1/pool.go (about)

     1  package v1
     2  
     3  import (
     4  	"sort"
     5  
     6  	"github.com/okex/exchain/libs/tendermint/libs/log"
     7  	"github.com/okex/exchain/libs/tendermint/p2p"
     8  	"github.com/okex/exchain/libs/tendermint/types"
     9  )
    10  
    11  // BlockPool keeps track of the fast sync peers, block requests and block responses.
    12  type BlockPool struct {
    13  	logger log.Logger
    14  	// Set of peers that have sent status responses, with height bigger than pool.Height
    15  	peers map[p2p.ID]*BpPeer
    16  	// Set of block heights and the corresponding peers from where a block response is expected or has been received.
    17  	blocks map[int64]p2p.ID
    18  
    19  	plannedRequests   map[int64]struct{} // list of blocks to be assigned peers for blockRequest
    20  	nextRequestHeight int64              // next height to be added to plannedRequests
    21  
    22  	Height        int64 // height of next block to execute
    23  	MaxPeerHeight int64 // maximum height of all peers
    24  	toBcR         bcReactor
    25  }
    26  
    27  // NewBlockPool creates a new BlockPool.
    28  func NewBlockPool(height int64, toBcR bcReactor) *BlockPool {
    29  	return &BlockPool{
    30  		Height:            height,
    31  		MaxPeerHeight:     0,
    32  		peers:             make(map[p2p.ID]*BpPeer),
    33  		blocks:            make(map[int64]p2p.ID),
    34  		plannedRequests:   make(map[int64]struct{}),
    35  		nextRequestHeight: height,
    36  		toBcR:             toBcR,
    37  	}
    38  }
    39  
    40  // SetLogger sets the logger of the pool.
    41  func (pool *BlockPool) SetLogger(l log.Logger) {
    42  	pool.logger = l
    43  }
    44  
    45  // ReachedMaxHeight check if the pool has reached the maximum peer height.
    46  func (pool *BlockPool) ReachedMaxHeight() bool {
    47  	return pool.Height >= pool.MaxPeerHeight
    48  }
    49  
    50  func (pool *BlockPool) rescheduleRequest(peerID p2p.ID, height int64) {
    51  	pool.logger.Info("reschedule requests made to peer for height ", "peerID", peerID, "height", height)
    52  	pool.plannedRequests[height] = struct{}{}
    53  	delete(pool.blocks, height)
    54  	pool.peers[peerID].RemoveBlock(height)
    55  }
    56  
    57  // Updates the pool's max height. If no peers are left MaxPeerHeight is set to 0.
    58  func (pool *BlockPool) updateMaxPeerHeight() {
    59  	var newMax int64
    60  	for _, peer := range pool.peers {
    61  		peerHeight := peer.Height
    62  		if peerHeight > newMax {
    63  			newMax = peerHeight
    64  		}
    65  	}
    66  	pool.MaxPeerHeight = newMax
    67  }
    68  
    69  // UpdatePeer adds a new peer or updates an existing peer with a new base and height.
    70  // If a peer is short it is not added.
    71  func (pool *BlockPool) UpdatePeer(peerID p2p.ID, base int64, height int64) error {
    72  
    73  	peer := pool.peers[peerID]
    74  
    75  	if peer == nil {
    76  		if height < pool.Height {
    77  			pool.logger.Info("Peer height too small",
    78  				"peer", peerID, "height", height, "fsm_height", pool.Height)
    79  			return errPeerTooShort
    80  		}
    81  		// Add new peer.
    82  		peer = NewBpPeer(peerID, base, height, pool.toBcR.sendPeerError, nil)
    83  		peer.SetLogger(pool.logger.With("peer", peerID))
    84  		pool.peers[peerID] = peer
    85  		pool.logger.Info("added peer", "peerID", peerID, "base", base, "height", height, "num_peers", len(pool.peers))
    86  	} else {
    87  		// Check if peer is lowering its height. This is not allowed.
    88  		if height < peer.Height {
    89  			pool.RemovePeer(peerID, errPeerLowersItsHeight)
    90  			return errPeerLowersItsHeight
    91  		}
    92  		// Update existing peer.
    93  		peer.Base = base
    94  		peer.Height = height
    95  	}
    96  
    97  	// Update the pool's MaxPeerHeight if needed.
    98  	pool.updateMaxPeerHeight()
    99  
   100  	return nil
   101  }
   102  
   103  // Cleans and deletes the peer. Recomputes the max peer height.
   104  func (pool *BlockPool) deletePeer(peer *BpPeer) {
   105  	if peer == nil {
   106  		return
   107  	}
   108  	peer.Cleanup()
   109  	delete(pool.peers, peer.ID)
   110  
   111  	if peer.Height == pool.MaxPeerHeight {
   112  		pool.updateMaxPeerHeight()
   113  	}
   114  }
   115  
   116  // RemovePeer removes the blocks and requests from the peer, reschedules them and deletes the peer.
   117  func (pool *BlockPool) RemovePeer(peerID p2p.ID, err error) {
   118  	peer := pool.peers[peerID]
   119  	if peer == nil {
   120  		return
   121  	}
   122  	pool.logger.Info("removing peer", "peerID", peerID, "error", err)
   123  
   124  	// Reschedule the block requests made to the peer, or received and not processed yet.
   125  	// Note that some of the requests may be removed further down.
   126  	for h := range pool.peers[peerID].blocks {
   127  		pool.rescheduleRequest(peerID, h)
   128  	}
   129  
   130  	oldMaxPeerHeight := pool.MaxPeerHeight
   131  	// Delete the peer. This operation may result in the pool's MaxPeerHeight being lowered.
   132  	pool.deletePeer(peer)
   133  
   134  	// Check if the pool's MaxPeerHeight has been lowered.
   135  	// This may happen if the tallest peer has been removed.
   136  	if oldMaxPeerHeight > pool.MaxPeerHeight {
   137  		// Remove any planned requests for heights over the new MaxPeerHeight.
   138  		for h := range pool.plannedRequests {
   139  			if h > pool.MaxPeerHeight {
   140  				delete(pool.plannedRequests, h)
   141  			}
   142  		}
   143  		// Adjust the nextRequestHeight to the new max plus one.
   144  		if pool.nextRequestHeight > pool.MaxPeerHeight {
   145  			pool.nextRequestHeight = pool.MaxPeerHeight + 1
   146  		}
   147  	}
   148  }
   149  
   150  func (pool *BlockPool) removeShortPeers() {
   151  	for _, peer := range pool.peers {
   152  		if peer.Height < pool.Height {
   153  			pool.RemovePeer(peer.ID, nil)
   154  		}
   155  	}
   156  }
   157  
   158  func (pool *BlockPool) removeBadPeers() {
   159  	pool.removeShortPeers()
   160  	for _, peer := range pool.peers {
   161  		if err := peer.CheckRate(); err != nil {
   162  			pool.RemovePeer(peer.ID, err)
   163  			pool.toBcR.sendPeerError(err, peer.ID)
   164  		}
   165  	}
   166  }
   167  
   168  // MakeNextRequests creates more requests if the block pool is running low.
   169  func (pool *BlockPool) MakeNextRequests(maxNumRequests int) {
   170  	heights := pool.makeRequestBatch(maxNumRequests)
   171  	if len(heights) != 0 {
   172  		pool.logger.Info("makeNextRequests will make following requests",
   173  			"number", len(heights), "heights", heights)
   174  	}
   175  
   176  	for _, height := range heights {
   177  		h := int64(height)
   178  		if !pool.sendRequest(h) {
   179  			// If a good peer was not found for sending the request at height h then return,
   180  			// as it shouldn't be possible to find a peer for h+1.
   181  			return
   182  		}
   183  		delete(pool.plannedRequests, h)
   184  	}
   185  }
   186  
   187  // Makes a batch of requests sorted by height such that the block pool has up to maxNumRequests entries.
   188  func (pool *BlockPool) makeRequestBatch(maxNumRequests int) []int {
   189  	pool.removeBadPeers()
   190  	// At this point pool.requests may include heights for requests to be redone due to removal of peers:
   191  	// - peers timed out or were removed by switch
   192  	// - FSM timed out on waiting to advance the block execution due to missing blocks at h or h+1
   193  	// Determine the number of requests needed by subtracting the number of requests already made from the maximum
   194  	// allowed
   195  	numNeeded := maxNumRequests - len(pool.blocks)
   196  	for len(pool.plannedRequests) < numNeeded {
   197  		if pool.nextRequestHeight > pool.MaxPeerHeight {
   198  			break
   199  		}
   200  		pool.plannedRequests[pool.nextRequestHeight] = struct{}{}
   201  		pool.nextRequestHeight++
   202  	}
   203  
   204  	heights := make([]int, 0, len(pool.plannedRequests))
   205  	for k := range pool.plannedRequests {
   206  		heights = append(heights, int(k))
   207  	}
   208  	sort.Ints(heights)
   209  	return heights
   210  }
   211  
   212  func (pool *BlockPool) sendRequest(height int64) bool {
   213  	for _, peer := range pool.peers {
   214  		if peer.NumPendingBlockRequests >= maxRequestsPerPeer {
   215  			continue
   216  		}
   217  		if peer.Base > height || peer.Height < height {
   218  			continue
   219  		}
   220  
   221  		err := pool.toBcR.sendBlockRequest(peer.ID, height)
   222  		if err == errNilPeerForBlockRequest {
   223  			// Switch does not have this peer, remove it and continue to look for another peer.
   224  			pool.logger.Error("switch does not have peer..removing peer selected for height", "peer",
   225  				peer.ID, "height", height)
   226  			pool.RemovePeer(peer.ID, err)
   227  			continue
   228  		}
   229  
   230  		if err == errSendQueueFull {
   231  			pool.logger.Error("peer queue is full", "peer", peer.ID, "height", height)
   232  			continue
   233  		}
   234  
   235  		pool.logger.Info("assigned request to peer", "peer", peer.ID, "height", height)
   236  
   237  		pool.blocks[height] = peer.ID
   238  		peer.RequestSent(height)
   239  
   240  		return true
   241  	}
   242  	pool.logger.Error("could not find peer to send request for block at height", "height", height)
   243  	return false
   244  }
   245  
   246  // AddBlock validates that the block comes from the peer it was expected from and stores it in the 'blocks' map.
   247  func (pool *BlockPool) AddBlock(peerID p2p.ID, block *types.Block, blockSize int) error {
   248  	peer, ok := pool.peers[peerID]
   249  	if !ok {
   250  		pool.logger.Error("block from unknown peer", "height", block.Height, "peer", peerID)
   251  		return errBadDataFromPeer
   252  	}
   253  	if wantPeerID, ok := pool.blocks[block.Height]; ok && wantPeerID != peerID {
   254  		pool.logger.Error("block received from wrong peer", "height", block.Height,
   255  			"peer", peerID, "expected_peer", wantPeerID)
   256  		return errBadDataFromPeer
   257  	}
   258  
   259  	return peer.AddBlock(block, blockSize)
   260  }
   261  
   262  // BlockData stores the peer responsible to deliver a block and the actual block if delivered.
   263  type BlockData struct {
   264  	block *types.Block
   265  	peer  *BpPeer
   266  }
   267  
   268  // BlockAndPeerAtHeight retrieves the block and delivery peer at specified height.
   269  // Returns errMissingBlock if a block was not found
   270  func (pool *BlockPool) BlockAndPeerAtHeight(height int64) (bData *BlockData, err error) {
   271  	peerID := pool.blocks[height]
   272  	peer := pool.peers[peerID]
   273  	if peer == nil {
   274  		return nil, errMissingBlock
   275  	}
   276  
   277  	block, err := peer.BlockAtHeight(height)
   278  	if err != nil {
   279  		return nil, err
   280  	}
   281  
   282  	return &BlockData{peer: peer, block: block}, nil
   283  }
   284  
   285  // FirstTwoBlocksAndPeers returns the blocks and the delivery peers at pool's height H and H+1.
   286  func (pool *BlockPool) FirstTwoBlocksAndPeers() (first, second *BlockData, err error) {
   287  	first, err = pool.BlockAndPeerAtHeight(pool.Height)
   288  	second, err2 := pool.BlockAndPeerAtHeight(pool.Height + 1)
   289  	if err == nil {
   290  		err = err2
   291  	}
   292  	return
   293  }
   294  
   295  // InvalidateFirstTwoBlocks removes the peers that sent us the first two blocks, blocks are removed by RemovePeer().
   296  func (pool *BlockPool) InvalidateFirstTwoBlocks(err error) {
   297  	first, err1 := pool.BlockAndPeerAtHeight(pool.Height)
   298  	second, err2 := pool.BlockAndPeerAtHeight(pool.Height + 1)
   299  
   300  	if err1 == nil {
   301  		pool.RemovePeer(first.peer.ID, err)
   302  	}
   303  	if err2 == nil {
   304  		pool.RemovePeer(second.peer.ID, err)
   305  	}
   306  }
   307  
   308  // ProcessedCurrentHeightBlock performs cleanup after a block is processed. It removes block at pool height and
   309  // the peers that are now short.
   310  func (pool *BlockPool) ProcessedCurrentHeightBlock() {
   311  	peerID, peerOk := pool.blocks[pool.Height]
   312  	if peerOk {
   313  		pool.peers[peerID].RemoveBlock(pool.Height)
   314  	}
   315  	delete(pool.blocks, pool.Height)
   316  	pool.logger.Debug("removed block at height", "height", pool.Height)
   317  	pool.Height++
   318  	pool.removeShortPeers()
   319  }
   320  
   321  // RemovePeerAtCurrentHeights checks if a block at pool's height H exists and if not, it removes the
   322  // delivery peer and returns. If a block at height H exists then the check and peer removal is done for H+1.
   323  // This function is called when the FSM is not able to make progress for some time.
   324  // This happens if either the block H or H+1 have not been delivered.
   325  func (pool *BlockPool) RemovePeerAtCurrentHeights(err error) {
   326  	peerID := pool.blocks[pool.Height]
   327  	peer, ok := pool.peers[peerID]
   328  	if ok {
   329  		if _, err := peer.BlockAtHeight(pool.Height); err != nil {
   330  			pool.logger.Info("remove peer that hasn't sent block at pool.Height",
   331  				"peer", peerID, "height", pool.Height)
   332  			pool.RemovePeer(peerID, err)
   333  			return
   334  		}
   335  	}
   336  	peerID = pool.blocks[pool.Height+1]
   337  	peer, ok = pool.peers[peerID]
   338  	if ok {
   339  		if _, err := peer.BlockAtHeight(pool.Height + 1); err != nil {
   340  			pool.logger.Info("remove peer that hasn't sent block at pool.Height+1",
   341  				"peer", peerID, "height", pool.Height+1)
   342  			pool.RemovePeer(peerID, err)
   343  			return
   344  		}
   345  	}
   346  }
   347  
   348  // Cleanup performs pool and peer cleanup
   349  func (pool *BlockPool) Cleanup() {
   350  	for id, peer := range pool.peers {
   351  		peer.Cleanup()
   352  		delete(pool.peers, id)
   353  	}
   354  	pool.plannedRequests = make(map[int64]struct{})
   355  	pool.blocks = make(map[int64]p2p.ID)
   356  	pool.nextRequestHeight = 0
   357  	pool.Height = 0
   358  	pool.MaxPeerHeight = 0
   359  }
   360  
   361  // NumPeers returns the number of peers in the pool
   362  func (pool *BlockPool) NumPeers() int {
   363  	return len(pool.peers)
   364  }
   365  
   366  // NeedsBlocks returns true if more blocks are required.
   367  func (pool *BlockPool) NeedsBlocks() bool {
   368  	return len(pool.blocks) < maxNumRequests
   369  }