func (pool *BlockPool) AddBlock(peerID p2p.ID, block *types.Block, blockSize int) {
	pool.mtx.Lock()
	defer pool.mtx.Unlock()
	requester := pool.requesters[block.Height]
	if requester == nil {
		pool.Logger.Info("peer sent us a block we didn't expect", "peer", peerID, "curHeight", pool.height, "blockHeight", block.Height)
		diff := pool.height - block.Height
		if diff < 0 {
			diff *= -1
		}
		if diff > maxDiffBetweenCurrentAndReceivedBlockHeight {
			pool.sendError(errors.New("peer sent us a block we didn't expect with a height too far ahead/behind"), peerID)
		}
		return
	}
	if requester.setBlock(block, peerID) {
		atomic.AddInt32(&pool.numPending, -1)
		peer := pool.peers[peerID]
		if peer != nil {
			peer.decrPending(blockSize)
		}
	} else {
	}
}
TODO: ensure that blocks come in order for each peer.
package blockchain
import (
	"errors"
	"fmt"
	"math"
	"sync"
	"sync/atomic"
	"time"
	cmn "github.com/tendermint/tendermint/libs/common"
	flow "github.com/tendermint/tendermint/libs/flowrate"
	"github.com/tendermint/tendermint/libs/log"
	"github.com/tendermint/tendermint/p2p"
	"github.com/tendermint/tendermint/types"
)
const (
	requestIntervalMS         = 100
	maxTotalRequesters        = 1000
	maxPendingRequests        = maxTotalRequesters
	maxPendingRequestsPerPeer = 50
	minRecvRate = 7680
	maxDiffBetweenCurrentAndReceivedBlockHeight = 100
)
var peerTimeout = 15 * time.Second  
type BlockPool struct {
	cmn.BaseService
	startTime time.Time
	mtx sync.Mutex
	requesters map[int64]*bpRequester
	height     int64  
	peers         map[p2p.ID]*bpPeer
	maxPeerHeight int64
	numPending int32  
	requestsCh chan<- BlockRequest
	errorsCh   chan<- peerError
}
func NewBlockPool(start int64, requestsCh chan<- BlockRequest, errorsCh chan<- peerError) *BlockPool {
	bp := &BlockPool{
		peers: make(map[p2p.ID]*bpPeer),
		requesters: make(map[int64]*bpRequester),
		height:     start,
		numPending: 0,
		requestsCh: requestsCh,
		errorsCh:   errorsCh,
	}
	bp.BaseService = *cmn.NewBaseService(nil, "BlockPool", bp)
	return bp
}
func (pool *BlockPool) OnStart() error {
	go pool.makeRequestersRoutine()
	pool.startTime = time.Now()
	return nil
}
func (pool *BlockPool) OnStop() {}
func (pool *BlockPool) makeRequestersRoutine() {
	for {
		if !pool.IsRunning() {
			break
		}
		_, numPending, lenRequesters := pool.GetStatus()
		if numPending >= maxPendingRequests {
			time.Sleep(requestIntervalMS * time.Millisecond)
			pool.removeTimedoutPeers()
		} else if lenRequesters >= maxTotalRequesters {
			time.Sleep(requestIntervalMS * time.Millisecond)
			pool.removeTimedoutPeers()
		} else {
			pool.makeNextRequester()
		}
	}
}
func (pool *BlockPool) removeTimedoutPeers() {
	pool.mtx.Lock()
	defer pool.mtx.Unlock()
	for _, peer := range pool.peers {
		if !peer.didTimeout && peer.numPending > 0 {
			curRate := peer.recvMonitor.Status().CurRate
			if curRate != 0 && curRate < minRecvRate {
				err := errors.New("peer is not sending us data fast enough")
				pool.sendError(err, peer.id)
				pool.Logger.Error("SendTimeout", "peer", peer.id,
					"reason", err,
					"curRate", fmt.Sprintf("%d KB/s", curRate/1024),
					"minRate", fmt.Sprintf("%d KB/s", minRecvRate/1024))
				peer.didTimeout = true
			}
		}
		if peer.didTimeout {
			pool.removePeer(peer.id)
		}
	}
}
func (pool *BlockPool) GetStatus() (height int64, numPending int32, lenRequesters int) {
	pool.mtx.Lock()
	defer pool.mtx.Unlock()
	return pool.height, atomic.LoadInt32(&pool.numPending), len(pool.requesters)
}
func (pool *BlockPool) IsCaughtUp() bool {
	pool.mtx.Lock()
	defer pool.mtx.Unlock()
	if len(pool.peers) == 0 {
		pool.Logger.Debug("Blockpool has no peers")
		return false
	}
	receivedBlockOrTimedOut := (pool.height > 0 || time.Since(pool.startTime) > 5*time.Second)
	ourChainIsLongestAmongPeers := pool.maxPeerHeight == 0 || pool.height >= pool.maxPeerHeight
	isCaughtUp := receivedBlockOrTimedOut && ourChainIsLongestAmongPeers
	return isCaughtUp
}
func (pool *BlockPool) PeekTwoBlocks() (first *types.Block, second *types.Block) {
	pool.mtx.Lock()
	defer pool.mtx.Unlock()
	if r := pool.requesters[pool.height]; r != nil {
		first = r.getBlock()
	}
	if r := pool.requesters[pool.height+1]; r != nil {
		second = r.getBlock()
	}
	return
}
func (pool *BlockPool) PopRequest() {
	pool.mtx.Lock()
	defer pool.mtx.Unlock()
	if r := pool.requesters[pool.height]; r != nil {
		r.Stop()
		delete(pool.requesters, pool.height)
		pool.height++
	} else {
		panic(fmt.Sprintf("Expected requester to pop, got nothing at height %v", pool.height))
	}
}
func (pool *BlockPool) RedoRequest(height int64) p2p.ID {
	pool.mtx.Lock()
	defer pool.mtx.Unlock()
	request := pool.requesters[height]
	if request.block == nil {
		panic("Expected block to be non-nil")
	}
	pool.removePeer(request.peerID)
	return request.peerID
}