func (sc *schedule) setPeerHeight(peerID p2p.ID, height int64) error {
	peer, ok := sc.peers[peerID]
	if !ok {
		return fmt.Errorf("Can't find peer %s", peerID)
	}
	if peer.state == peerStateRemoved {
		return fmt.Errorf("Cannot set peer height for a peer in peerStateRemoved")
	}
	if height < peer.height {
		return fmt.Errorf("Cannot move peer height lower. from %d to %d", peer.height, height)
	}
	peer.height = height
	peer.state = peerStateReady
	for i := sc.minHeight(); i <= height; i++ {
		if sc.getStateAtHeight(i) == blockStateUnknown {
			sc.setStateAtHeight(i, blockStateNew)
		}
	}
	return nil
}
TODO - keep track of highest height
package v2
import (
	"fmt"
	"math"
	"math/rand"
	"time"
	"github.com/tendermint/tendermint/p2p"
)
type blockState int
const (
	blockStateUnknown blockState = iota
	blockStateNew
	blockStatePending
	blockStateReceived
	blockStateProcessed
)
func (e blockState) String() string {
	switch e {
	case blockStateUnknown:
		return "Unknown"
	case blockStateNew:
		return "New"
	case blockStatePending:
		return "Pending"
	case blockStateReceived:
		return "Received"
	case blockStateProcessed:
		return "Processed"
	default:
		return fmt.Sprintf("unknown blockState: %d", e)
	}
}
type peerState int
const (
	peerStateNew = iota
	peerStateReady
	peerStateRemoved
)
func (e peerState) String() string {
	switch e {
	case peerStateNew:
		return "New"
	case peerStateReady:
		return "Ready"
	case peerStateRemoved:
		return "Removed"
	default:
		return fmt.Sprintf("unknown peerState: %d", e)
	}
}
type scPeer struct {
	peerID      p2p.ID
	state       peerState
	height      int64
	lastTouched time.Time
	lastRate    int64
}
func newScPeer(peerID p2p.ID) *scPeer {
	return &scPeer{
		peerID:      peerID,
		state:       peerStateNew,
		height:      -1,
		lastTouched: time.Time{},
	}
}
type schedule struct {
	initHeight int64
	blockStates map[int64]blockState
	peers map[p2p.ID]*scPeer
	pendingBlocks map[int64]p2p.ID
	pendingTime map[int64]time.Time
	receivedBlocks map[int64]p2p.ID
}
func newSchedule(initHeight int64) *schedule {
	sc := schedule{
		initHeight:     initHeight,
		blockStates:    make(map[int64]blockState),
		peers:          make(map[p2p.ID]*scPeer),
		pendingBlocks:  make(map[int64]p2p.ID),
		pendingTime:    make(map[int64]time.Time),
		receivedBlocks: make(map[int64]p2p.ID),
	}
	sc.setStateAtHeight(initHeight, blockStateNew)
	return &sc
}
func (sc *schedule) addPeer(peerID p2p.ID) error {
	if _, ok := sc.peers[peerID]; ok {
		return fmt.Errorf("Cannot add duplicate peer %s", peerID)
	}
	sc.peers[peerID] = newScPeer(peerID)
	return nil
}
func (sc *schedule) touchPeer(peerID p2p.ID, time time.Time) error {
	peer, ok := sc.peers[peerID]
	if !ok {
		return fmt.Errorf("Couldn't find peer %s", peerID)
	}
	if peer.state == peerStateRemoved {
		return fmt.Errorf("Tried to touch peer in peerStateRemoved")
	}
	peer.lastTouched = time
	return nil
}
func (sc *schedule) removePeer(peerID p2p.ID) error {
	peer, ok := sc.peers[peerID]
	if !ok {
		return fmt.Errorf("Couldn't find peer %s", peerID)
	}
	if peer.state == peerStateRemoved {
		return fmt.Errorf("Tried to remove peer %s in peerStateRemoved", peerID)
	}
	for height, pendingPeerID := range sc.pendingBlocks {
		if pendingPeerID == peerID {
			sc.setStateAtHeight(height, blockStateNew)
			delete(sc.pendingTime, height)
			delete(sc.pendingBlocks, height)
		}
	}
	for height, rcvPeerID := range sc.receivedBlocks {
		if rcvPeerID == peerID {
			sc.setStateAtHeight(height, blockStateNew)
			delete(sc.receivedBlocks, height)
		}
	}
	peer.state = peerStateRemoved
	return nil
}