func (pool *BlockPool) BlockAndPeerAtHeight(height int64) (bData *BlockData, err error) {
	peerID := pool.blocks[height]
	peer := pool.peers[peerID]
	if peer == nil {
		return nil, errMissingBlock
	}
	block, err := peer.BlockAtHeight(height)
	if err != nil {
		return nil, err
	}
	return &BlockData{peer: peer, block: block}, nil
}
BlockAndPeerAtHeight retrieves the block and delivery peer at specified height.
Returns errMissingBlock if a block was not found
package v1
import (
	"sort"
	"github.com/line/ostracon/libs/log"
	"github.com/line/ostracon/p2p"
	"github.com/line/ostracon/types"
)
type BlockPool struct {
	logger log.Logger
	peers map[p2p.ID]*BpPeer
	blocks map[int64]p2p.ID
	plannedRequests   map[int64]struct{}  
	nextRequestHeight int64               
	Height        int64  
	MaxPeerHeight int64  
	toBcR         bcReactor
}
func NewBlockPool(height int64, toBcR bcReactor) *BlockPool {
	return &BlockPool{
		Height:            height,
		MaxPeerHeight:     0,
		peers:             make(map[p2p.ID]*BpPeer),
		blocks:            make(map[int64]p2p.ID),
		plannedRequests:   make(map[int64]struct{}),
		nextRequestHeight: height,
		toBcR:             toBcR,
	}
}
func (pool *BlockPool) SetLogger(l log.Logger) {
	pool.logger = l
}
func (pool *BlockPool) ReachedMaxHeight() bool {
	return pool.Height >= pool.MaxPeerHeight
}
func (pool *BlockPool) rescheduleRequest(peerID p2p.ID, height int64) {
	pool.logger.Info("reschedule requests made to peer for height ", "peerID", peerID, "height", height)
	pool.plannedRequests[height] = struct{}{}
	delete(pool.blocks, height)
	pool.peers[peerID].RemoveBlock(height)
}
func (pool *BlockPool) updateMaxPeerHeight() {
	var newMax int64
	for _, peer := range pool.peers {
		peerHeight := peer.Height
		if peerHeight > newMax {
			newMax = peerHeight
		}
	}
	pool.MaxPeerHeight = newMax
}
func (pool *BlockPool) UpdatePeer(peerID p2p.ID, base int64, height int64) error {
	peer := pool.peers[peerID]
	if peer == nil {
		if height < pool.Height {
			pool.logger.Info("Peer height too small",
				"peer", peerID, "height", height, "fsm_height", pool.Height)
			return errPeerTooShort
		}
		peer = NewBpPeer(peerID, base, height, pool.toBcR.sendPeerError, nil)
		peer.SetLogger(pool.logger.With("peer", peerID))
		pool.peers[peerID] = peer
		pool.logger.Info("added peer", "peerID", peerID, "base", base, "height", height, "num_peers", len(pool.peers))
	} else {
		if height < peer.Height {
			pool.RemovePeer(peerID, errPeerLowersItsHeight)
			return errPeerLowersItsHeight
		}
		peer.Base = base
		peer.Height = height
	}
	pool.updateMaxPeerHeight()
	return nil
}
func (pool *BlockPool) deletePeer(peer *BpPeer) {
	if peer == nil {
		return
	}
	peer.Cleanup()
	delete(pool.peers, peer.ID)
	if peer.Height == pool.MaxPeerHeight {
		pool.updateMaxPeerHeight()
	}
}
func (pool *BlockPool) RemovePeer(peerID p2p.ID, err error) {
	peer := pool.peers[peerID]
	if peer == nil {
		return
	}
	pool.logger.Info("removing peer", "peerID", peerID, "error", err)
	for h := range pool.peers[peerID].blocks {
		pool.rescheduleRequest(peerID, h)
	}
	oldMaxPeerHeight := pool.MaxPeerHeight
	pool.deletePeer(peer)
	if oldMaxPeerHeight > pool.MaxPeerHeight {
		for h := range pool.plannedRequests {
			if h > pool.MaxPeerHeight {
				delete(pool.plannedRequests, h)
			}
		}
		if pool.nextRequestHeight > pool.MaxPeerHeight {
			pool.nextRequestHeight = pool.MaxPeerHeight + 1
		}
	}
}
func (pool *BlockPool) removeShortPeers() {
	for _, peer := range pool.peers {
		if peer.Height < pool.Height {
			pool.RemovePeer(peer.ID, nil)
		}
	}
}
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)
		}
	}
}
func (pool *BlockPool) MakeNextRequests(maxNumRequests int) {
	heights := pool.makeRequestBatch(maxNumRequests)
	if len(heights) != 0 {
		pool.logger.Info("makeNextRequests will make following requests",
			"number", len(heights), "heights", heights)
	}
	for _, height := range heights {
		h := int64(height)
		if !pool.sendRequest(h) {
			return
		}
		delete(pool.plannedRequests, h)
	}
}
func (pool *BlockPool) makeRequestBatch(maxNumRequests int) []int {
	pool.removeBadPeers()
	numNeeded := maxNumRequests - len(pool.blocks)
	for len(pool.plannedRequests) < numNeeded {
		if pool.nextRequestHeight > pool.MaxPeerHeight {
			break
		}
		pool.plannedRequests[pool.nextRequestHeight] = struct{}{}
		pool.nextRequestHeight++
	}
	heights := make([]int, 0, len(pool.plannedRequests))
	for k := range pool.plannedRequests {
		heights = append(heights, int(k))
	}
	sort.Ints(heights)
	return heights
}
func (pool *BlockPool) sendRequest(height int64) bool {
	for _, peer := range pool.peers {
		if peer.NumPendingBlockRequests >= maxRequestsPerPeer {
			continue
		}
		if peer.Base > height || peer.Height < height {
			continue
		}
		err := pool.toBcR.sendBlockRequest(peer.ID, height)
		if err == errNilPeerForBlockRequest {
			pool.logger.Error("switch does not have peer..removing peer selected for height", "peer",
				peer.ID, "height", height)
			pool.RemovePeer(peer.ID, err)
			continue
		}
		if err == errSendQueueFull {
			pool.logger.Error("peer queue is full", "peer", peer.ID, "height", height)
			continue
		}
		pool.logger.Info("assigned request to peer", "peer", peer.ID, "height", height)
		pool.blocks[height] = peer.ID
		peer.RequestSent(height)
		return true
	}
	pool.logger.Error("could not find peer to send request for block at height", "height", height)
	return false
}
func (pool *BlockPool) AddBlock(peerID p2p.ID, block *types.Block, blockSize int) error {
	peer, ok := pool.peers[peerID]
	if !ok {
		pool.logger.Error("block from unknown peer", "height", block.Height, "peer", peerID)
		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)
		return errBadDataFromPeer
	}
	return peer.AddBlock(block, blockSize)
}
type BlockData struct {
	block *types.Block
	peer  *BpPeer
}