348 lines
10 KiB
Go
Raw Normal View History

package blockchain
2015-03-22 03:30:22 -07:00
import (
2019-04-13 09:23:43 -04:00
"sort"
2015-03-22 03:30:22 -07:00
2018-07-01 22:36:49 -04:00
"github.com/tendermint/tendermint/libs/log"
2018-01-03 11:29:19 +01:00
"github.com/tendermint/tendermint/p2p"
"github.com/tendermint/tendermint/types"
2015-03-22 03:30:22 -07:00
)
2019-04-13 09:23:43 -04:00
type blockPool struct {
logger log.Logger
// Set of peers that have sent status responses, with height bigger than pool.Height
2019-05-15 18:33:12 -04:00
peers map[p2p.ID]*bpPeer
// Set of block heights and the corresponding peers from where a block response is expected or has been received.
2019-04-13 09:23:43 -04:00
blocks map[int64]p2p.ID
2019-05-15 23:33:19 -04:00
plannedRequests map[int64]struct{} // list of blocks to be assigned peers for blockRequest
nextRequestHeight int64 // next height to be added to plannedRequests
2019-04-13 09:23:43 -04:00
Height int64 // height of next block to execute
MaxPeerHeight int64 // maximum height of all peers
2019-06-02 19:03:51 +02:00
toBcR bcReactor
2019-04-13 09:23:43 -04:00
}
// NewBlockPool creates a new blockPool.
2019-06-02 19:03:51 +02:00
func NewBlockPool(height int64, toBcR bcReactor) *blockPool {
2019-04-13 09:23:43 -04:00
return &blockPool{
peers: make(map[p2p.ID]*bpPeer),
MaxPeerHeight: 0,
2019-04-13 09:23:43 -04:00
blocks: make(map[int64]p2p.ID),
2019-05-15 23:33:19 -04:00
plannedRequests: make(map[int64]struct{}),
2019-04-13 09:23:43 -04:00
nextRequestHeight: height,
Height: height,
2019-04-13 09:23:43 -04:00
toBcR: toBcR,
}
2015-03-22 03:30:22 -07:00
}
// SetLogger sets the logger of the pool.
2019-06-02 19:03:51 +02:00
func (pool *blockPool) SetLogger(l log.Logger) {
2019-04-13 09:23:43 -04:00
pool.logger = l
2015-09-09 21:44:48 -07:00
}
// ReachedMaxHeight check if the pool has reached the maximum peer height.
2019-06-02 19:03:51 +02:00
func (pool *blockPool) ReachedMaxHeight() bool {
return pool.Height >= pool.MaxPeerHeight
2015-03-22 03:30:22 -07:00
}
2019-04-13 09:23:43 -04:00
func (pool *blockPool) rescheduleRequest(peerID p2p.ID, height int64) {
pool.logger.Info("reschedule requests made to peer for height ", "peerID", peerID, "height", height)
2019-05-15 23:33:19 -04:00
pool.plannedRequests[height] = struct{}{}
2019-04-13 09:23:43 -04:00
delete(pool.blocks, height)
2019-06-06 18:13:59 +02:00
pool.peers[peerID].RemoveBlock(height)
2019-04-13 09:23:43 -04:00
}
2015-03-24 11:02:30 -07:00
// Updates the pool's max height. If no peers are left MaxPeerHeight is set to 0.
2019-04-13 09:23:43 -04:00
func (pool *blockPool) updateMaxPeerHeight() {
var newMax int64
2019-04-13 09:23:43 -04:00
for _, peer := range pool.peers {
peerHeight := peer.Height
2019-06-02 19:03:51 +02:00
if peerHeight > newMax {
newMax = peerHeight
}
2015-03-22 03:30:22 -07:00
}
pool.MaxPeerHeight = newMax
2015-03-22 12:46:53 -07:00
}
// UpdatePeer adds a new peer or updates an existing peer with a new height.
// If a peer is too short it is not added.
2019-06-02 19:03:51 +02:00
func (pool *blockPool) UpdatePeer(peerID p2p.ID, height int64) error {
2019-04-13 09:23:43 -04:00
peer := pool.peers[peerID]
oldHeight := int64(0)
if peer != nil {
oldHeight = peer.Height
}
2019-05-15 23:33:19 -04:00
pool.logger.Info("updatePeer", "peerID", peerID, "height", height, "old_height", oldHeight)
2015-03-22 03:30:22 -07:00
2019-04-13 09:23:43 -04:00
if peer == nil {
if height < pool.Height {
pool.logger.Info("Peer height too small", "peer", peerID, "height", height, "fsm_height", pool.Height)
return errPeerTooShort
}
2019-04-13 09:23:43 -04:00
// Add new peer.
2019-06-02 19:03:51 +02:00
peer = NewBPPeer(peerID, height, pool.toBcR.sendPeerError, nil)
peer.SetLogger(pool.logger.With("peer", peerID))
pool.peers[peerID] = peer
2019-06-06 18:13:59 +02:00
pool.logger.Info("XXX added peer", "num_peers", len(pool.peers))
2019-04-13 09:23:43 -04:00
} else {
// Check if peer is lowering its height. This is not allowed.
if height < peer.Height {
2019-06-02 19:03:51 +02:00
pool.RemovePeer(peerID, errPeerLowersItsHeight)
return errPeerLowersItsHeight
2019-04-13 09:23:43 -04:00
}
// Update existing peer.
2019-06-06 18:13:59 +02:00
peer.Height = height
2015-03-24 11:02:30 -07:00
}
// Update the pool's MaxPeerHeight if needed. Note that for updates it can only increase or left unchanged.
2019-04-13 09:23:43 -04:00
pool.updateMaxPeerHeight()
2015-03-24 11:02:30 -07:00
2019-04-13 09:23:43 -04:00
return nil
2015-03-22 12:46:53 -07:00
}
2019-04-13 09:23:43 -04:00
// Stops the peer timer and deletes the peer. Recomputes the max peer height.
2019-05-15 18:33:12 -04:00
func (pool *blockPool) deletePeer(peer *bpPeer) {
if peer == nil {
return
}
peer.Cleanup()
delete(pool.peers, peer.ID)
if peer.Height == pool.MaxPeerHeight {
2019-05-15 18:33:12 -04:00
pool.updateMaxPeerHeight()
}
}
// RemovePeer removes any blocks and requests from the peer, reschedules them and deletes the peer.
2019-06-02 19:03:51 +02:00
func (pool *blockPool) RemovePeer(peerID p2p.ID, err error) {
2019-04-13 09:23:43 -04:00
peer := pool.peers[peerID]
if peer == nil {
return
}
2019-05-15 23:33:19 -04:00
pool.logger.Info("removing peer", "peerID", peerID, "error", err)
2019-05-15 18:33:12 -04:00
// Reschedule the block requests made to the peer, or received and not processed yet.
// Note that some of the requests may be removed further down.
2019-05-20 23:32:59 -04:00
for h := range pool.peers[peerID].blocks {
2019-04-13 09:23:43 -04:00
pool.rescheduleRequest(peerID, h)
}
2015-03-22 12:46:53 -07:00
oldMaxPeerHeight := pool.MaxPeerHeight
// Delete the peer. This operation may result in the pool's MaxPeerHeight being lowered.
2019-05-15 18:33:12 -04:00
pool.deletePeer(peer)
// Check if the pool's MaxPeerHeight has been lowered.
2019-05-15 18:33:12 -04:00
// This may happen if the tallest peer has been removed.
if oldMaxPeerHeight > pool.MaxPeerHeight {
// Remove any planned requests for heights over the new MaxPeerHeight.
2019-05-15 23:33:19 -04:00
for h := range pool.plannedRequests {
if h > pool.MaxPeerHeight {
2019-05-15 23:33:19 -04:00
delete(pool.plannedRequests, h)
2019-05-15 18:33:12 -04:00
}
}
// Adjust the nextRequestHeight to the new max plus one.
if pool.nextRequestHeight > pool.MaxPeerHeight {
pool.nextRequestHeight = pool.MaxPeerHeight + 1
2019-05-15 18:33:12 -04:00
}
}
2019-04-13 09:23:43 -04:00
}
2015-03-24 11:02:30 -07:00
2019-04-13 09:23:43 -04:00
func (pool *blockPool) removeShortPeers() {
for _, peer := range pool.peers {
if peer.Height < pool.Height {
pool.RemovePeer(peer.ID, nil)
}
2015-03-22 03:30:22 -07:00
}
}
2019-04-15 23:26:07 -04:00
func (pool *blockPool) removeBadPeers() {
pool.removeShortPeers()
for _, peer := range pool.peers {
if err := peer.CheckRate(); err != nil {
pool.RemovePeer(peer.ID, err)
pool.toBcR.sendPeerError(err, peer.ID)
2019-04-15 23:26:07 -04:00
}
}
}
// Makes a batch of requests sorted by height up to a specified maximum.
// The parameter 'maxNumRequests' includes the number of block requests already made.
func (pool *blockPool) makeRequestBatch(maxNumRequests int) []int {
2019-04-15 23:26:07 -04:00
pool.removeBadPeers()
2019-05-15 23:33:19 -04:00
// At this point pool.requests may include heights for requests to be redone due to removal of peers:
// - peers timed out or were removed by switch
// - FSM timed out on waiting to advance the block execution due to missing blocks at h or h+1
// Check if more requests should be tried by subtracting the number of requests already made from the maximum allowed
numNeeded := int(maxNumRequests) - len(pool.blocks)
for len(pool.plannedRequests) < numNeeded {
if pool.nextRequestHeight > pool.MaxPeerHeight {
2019-04-15 23:26:07 -04:00
break
}
2019-05-15 23:33:19 -04:00
pool.plannedRequests[pool.nextRequestHeight] = struct{}{}
2019-04-15 23:26:07 -04:00
pool.nextRequestHeight++
}
2019-05-15 23:33:19 -04:00
heights := make([]int, 0, len(pool.plannedRequests))
for k := range pool.plannedRequests {
2019-04-15 23:26:07 -04:00
heights = append(heights, int(k))
}
sort.Ints(heights)
return heights
}
func (pool *blockPool) MakeNextRequests(maxNumRequests int) {
heights := pool.makeRequestBatch(maxNumRequests)
pool.logger.Info("makeNextRequests will make following requests", "number", len(heights), "heights", heights)
2019-04-15 23:26:07 -04:00
for _, height := range heights {
h := int64(height)
if !pool.sendRequest(h) {
2019-05-15 23:33:19 -04:00
// If a good peer was not found for sending the request at height h then return,
// as it shouldn't be possible to find a peer for h+1.
2019-04-15 23:26:07 -04:00
return
}
2019-05-15 23:33:19 -04:00
delete(pool.plannedRequests, h)
2019-04-15 23:26:07 -04:00
}
}
func (pool *blockPool) sendRequest(height int64) bool {
2019-04-15 23:26:07 -04:00
for _, peer := range pool.peers {
if peer.NumPendingBlockRequests >= maxRequestsPerPeer {
2019-04-15 23:26:07 -04:00
continue
}
if peer.Height < height {
2019-04-15 23:26:07 -04:00
continue
}
2019-05-07 21:06:43 -04:00
err := pool.toBcR.sendBlockRequest(peer.ID, height)
2019-05-15 23:33:19 -04:00
if err == errNilPeerForBlockRequest {
// Switch does not have this peer, remove it and continue to look for another peer.
pool.logger.Error("switch does not have peer..removing peer selected for height", "peer", peer.ID, "height", height)
pool.RemovePeer(peer.ID, err)
continue
}
2019-05-15 23:33:19 -04:00
if err == errSendQueueFull {
pool.logger.Error("peer queue is full", "peer", peer.ID, "height", height)
2019-05-15 23:33:19 -04:00
continue
2019-04-15 23:26:07 -04:00
}
pool.logger.Info("assigned request to peer", "peer", peer.ID, "height", height)
2019-05-15 23:33:19 -04:00
pool.blocks[height] = peer.ID
peer.RequestSent(height)
2019-04-15 23:26:07 -04:00
return true
2019-04-15 23:26:07 -04:00
}
pool.logger.Error("could not find peer to send request for block at height", "height", height)
return false
2019-04-15 23:26:07 -04:00
}
2019-04-13 09:23:43 -04:00
// Validates that the block comes from the peer it was expected from and stores it in the 'blocks' map.
2019-06-02 19:03:51 +02:00
func (pool *blockPool) AddBlock(peerID p2p.ID, block *types.Block, blockSize int) error {
2019-05-15 18:33:12 -04:00
peer, ok := pool.peers[peerID]
if !ok {
pool.logger.Error("block from unknown peer", "height", block.Height, "peer", peerID)
2019-04-13 09:23:43 -04:00
return errBadDataFromPeer
}
if wantPeerID, ok := pool.blocks[block.Height]; ok && wantPeerID != peerID {
pool.logger.Error("block received from wrong peer", "height", block.Height,
"peer", peerID, "expected_peer", wantPeerID)
2019-04-13 09:23:43 -04:00
return errBadDataFromPeer
2015-03-22 03:30:22 -07:00
}
2019-06-06 18:13:59 +02:00
return peer.AddBlock(block, blockSize)
2015-03-24 11:02:30 -07:00
}
2019-06-02 19:03:51 +02:00
type blockData struct {
block *types.Block
peer *bpPeer
}
func (pool *blockPool) GetBlockAndPeerAtHeight(height int64) (bData *blockData, err error) {
2019-04-13 09:23:43 -04:00
peerID := pool.blocks[height]
peer := pool.peers[peerID]
if peer == nil {
2019-06-02 19:03:51 +02:00
return nil, errMissingBlock
2019-02-06 18:20:10 +04:00
}
2015-03-22 03:30:22 -07:00
block, err := peer.BlockAtHeight(height)
2019-06-02 19:03:51 +02:00
if err != nil {
return nil, err
2019-04-13 09:23:43 -04:00
}
2016-06-28 18:02:27 -07:00
2019-04-13 09:23:43 -04:00
return &blockData{peer: peer, block: block}, nil
2017-05-02 11:53:32 +04:00
2015-03-22 03:30:22 -07:00
}
2019-06-02 19:03:51 +02:00
func (pool *blockPool) GetNextTwoBlocks() (first, second *blockData, err error) {
first, err = pool.GetBlockAndPeerAtHeight(pool.Height)
second, err2 := pool.GetBlockAndPeerAtHeight(pool.Height + 1)
2019-04-13 09:23:43 -04:00
if err == nil {
err = err2
}
2019-04-13 09:23:43 -04:00
return
2017-05-02 11:53:32 +04:00
}
2019-06-02 19:03:51 +02:00
// Remove the peers that sent us the first two blocks, blocks are removed by RemovePeer().
func (pool *blockPool) InvalidateFirstTwoBlocks(err error) {
first, err1 := pool.GetBlockAndPeerAtHeight(pool.Height)
second, err2 := pool.GetBlockAndPeerAtHeight(pool.Height + 1)
2019-04-13 09:23:43 -04:00
if err1 == nil {
pool.RemovePeer(first.peer.ID, err)
}
2019-04-13 09:23:43 -04:00
if err2 == nil {
pool.RemovePeer(second.peer.ID, err)
}
}
2019-06-02 19:03:51 +02:00
func (pool *blockPool) ProcessedCurrentHeightBlock() {
peerID, peerOk := pool.blocks[pool.Height]
2019-04-13 09:23:43 -04:00
if peerOk {
2019-06-06 18:13:59 +02:00
pool.peers[peerID].RemoveBlock(pool.Height)
}
delete(pool.blocks, pool.Height)
pool.logger.Debug("processed and removed block at height", "height", pool.Height)
pool.Height++
2019-04-13 09:23:43 -04:00
pool.removeShortPeers()
}
// This function is called when the FSM is not able to make progress for a certain amount of time.
// This happens if the block at either pool.Height or pool.Height+1 has not been delivered during this time.
2019-06-02 19:03:51 +02:00
func (pool *blockPool) RemovePeerAtCurrentHeights(err error) {
peerID := pool.blocks[pool.Height]
peer, ok := pool.peers[peerID]
2019-06-02 19:03:51 +02:00
if ok {
if _, err := peer.BlockAtHeight(pool.Height); err != nil {
pool.logger.Info("removing peer that hasn't sent block at pool.Height",
"peer", peerID, "height", pool.Height)
2019-06-02 19:03:51 +02:00
pool.RemovePeer(peerID, err)
return
}
}
peerID = pool.blocks[pool.Height+1]
peer, ok = pool.peers[peerID]
2019-06-02 19:03:51 +02:00
if ok {
if _, err := peer.BlockAtHeight(pool.Height + 1); err != nil {
pool.logger.Info("removing peer that hasn't sent block at pool.Height+1",
"peer", peerID, "height", pool.Height+1)
2019-06-02 19:03:51 +02:00
pool.RemovePeer(peerID, err)
return
}
}
pool.logger.Info("no peers assigned to blocks at current height or blocks already delivered",
"height", pool.Height)
}
2019-06-02 19:03:51 +02:00
func (pool *blockPool) Cleanup() {
2019-04-13 09:23:43 -04:00
for _, peer := range pool.peers {
2019-06-02 19:03:51 +02:00
peer.Cleanup()
}
}
func (pool *blockPool) NumPeers() int {
return len(pool.peers)
}