func fetchHeaders(chain *blockchain.BlockChain, blockHashes []chainhash.Hash) ([]wire.BlockHeader, error) {
	headers := make([]wire.BlockHeader, 0, len(blockHashes))
	for i := range blockHashes {
		header, err := chain.FetchHeader(&blockHashes[i])
		if err != nil {
			return nil, err
		}
		headers = append(headers, header)
	}
	return headers, nil
}
fetchHeaders fetches and decodes headers from the db for each hash in
blockHashes.
package main
import (
	"bytes"
	"crypto/rand"
	"encoding/binary"
	"errors"
	"fmt"
	"math"
	"net"
	"runtime"
	"strconv"
	"strings"
	"sync"
	"sync/atomic"
	"time"
	"github.com/bitgo/prova/addrmgr"
	"github.com/bitgo/prova/blockchain"
	"github.com/bitgo/prova/blockchain/indexers"
	"github.com/bitgo/prova/chaincfg"
	"github.com/bitgo/prova/chaincfg/chainhash"
	"github.com/bitgo/prova/connmgr"
	"github.com/bitgo/prova/database"
	"github.com/bitgo/prova/mempool"
	"github.com/bitgo/prova/mining"
	"github.com/bitgo/prova/mining/cpuminer"
	"github.com/bitgo/prova/peer"
	"github.com/bitgo/prova/provautil"
	"github.com/bitgo/prova/provautil/bloom"
	"github.com/bitgo/prova/txscript"
	"github.com/bitgo/prova/wire"
)
const (
	defaultServices = wire.SFNodeNetwork | wire.SFNodeBloom
	defaultRequiredServices = wire.SFNodeNetwork
	defaultTargetOutbound = 8
	connectionRetryInterval = time.Second * 5
)
var (
	userAgentName = "Prova"
	userAgentVersion = fmt.Sprintf("%d.%d.%d", appMajor, appMinor, appPatch)
)
type onionAddr struct {
	addr string
}
func (oa *onionAddr) String() string {
	return oa.addr
}
func (oa *onionAddr) Network() string {
	return "onion"
}
var _ net.Addr = (*onionAddr)(nil)
type broadcastMsg struct {
	message      wire.Message
	excludePeers []*serverPeer
}
type broadcastInventoryAdd relayMsg
type broadcastInventoryDel *wire.InvVect
type relayMsg struct {
	invVect *wire.InvVect
	data    interface{}
}
type updatePeerHeightsMsg struct {
	newHash    *chainhash.Hash
	newHeight  uint32
	originPeer *serverPeer
}
type peerState struct {
	inboundPeers    map[int32]*serverPeer
	outboundPeers   map[int32]*serverPeer
	persistentPeers map[int32]*serverPeer
	banned          map[string]time.Time
	outboundGroups  map[string]int
}
func (ps *peerState) Count() int {
	return len(ps.inboundPeers) + len(ps.outboundPeers) +
		len(ps.persistentPeers)
}
func (ps *peerState) forAllOutboundPeers(closure func(sp *serverPeer)) {
	for _, e := range ps.outboundPeers {
		closure(e)
	}
	for _, e := range ps.persistentPeers {
		closure(e)
	}
}
func (ps *peerState) forAllPeers(closure func(sp *serverPeer)) {
	for _, e := range ps.inboundPeers {
		closure(e)
	}
	ps.forAllOutboundPeers(closure)
}
type server struct {
	bytesReceived uint64  
	bytesSent     uint64  
	started       int32
	shutdown      int32
	shutdownSched int32
	chainParams          *chaincfg.Params
	addrManager          *addrmgr.AddrManager
	connManager          *connmgr.ConnManager
	sigCache             *txscript.SigCache
	hashCache            *txscript.HashCache
	rpcServer            *rpcServer
	blockManager         *blockManager
	txMemPool            *mempool.TxPool
	cpuMiner             *cpuminer.CPUMiner
	modifyRebroadcastInv chan interface{}
	newPeers             chan *serverPeer
	donePeers            chan *serverPeer
	banPeers             chan *serverPeer
	query                chan interface{}
	relayInv             chan relayMsg
	broadcast            chan broadcastMsg
	peerHeightsUpdate    chan updatePeerHeightsMsg
	wg                   sync.WaitGroup
	quit                 chan struct{}
	nat                  NAT
	db                   database.DB
	timeSource           blockchain.MedianTimeSource
	services             wire.ServiceFlag
	txIndex   *indexers.TxIndex
	addrIndex *indexers.AddrIndex
}
type serverPeer struct {
	feeFilter int64
	*peer.Peer
	connReq         *connmgr.ConnReq
	server          *server
	persistent      bool
	continueHash    *chainhash.Hash
	relayMtx        sync.Mutex
	disableRelayTx  bool
	sentAddrs       bool
	requestQueue    []*wire.InvVect
	requestedTxns   map[chainhash.Hash]struct{}
	requestedBlocks map[chainhash.Hash]struct{}
	filter          *bloom.Filter
	knownAddresses  map[string]struct{}
	banScore        connmgr.DynamicBanScore
	quit            chan struct{}
	txProcessed    chan struct{}
	blockProcessed chan struct{}
}
func newServerPeer(s *server, isPersistent bool) *serverPeer {
	return &serverPeer{
		server:          s,
		persistent:      isPersistent,
		requestedTxns:   make(map[chainhash.Hash]struct{}),
		requestedBlocks: make(map[chainhash.Hash]struct{}),
		filter:          bloom.LoadFilter(nil),
		knownAddresses:  make(map[string]struct{}),
		quit:            make(chan struct{}),
		txProcessed:     make(chan struct{}, 1),
		blockProcessed:  make(chan struct{}, 1),
	}
}
func (sp *serverPeer) newestBlock() (*chainhash.Hash, uint32, error) {
	best := sp.server.blockManager.chain.BestSnapshot()
	return best.Hash, best.Height, nil
}
func (sp *serverPeer) addKnownAddresses(addresses []*wire.NetAddress) {
	for _, na := range addresses {
		sp.knownAddresses[addrmgr.NetAddressKey(na)] = struct{}{}
	}
}
func (sp *serverPeer) addressKnown(na *wire.NetAddress) bool {
	_, exists := sp.knownAddresses[addrmgr.NetAddressKey(na)]
	return exists
}
func (sp *serverPeer) setDisableRelayTx(disable bool) {
	sp.relayMtx.Lock()
	sp.disableRelayTx = disable
	sp.relayMtx.Unlock()
}
func (sp *serverPeer) relayTxDisabled() bool {
	sp.relayMtx.Lock()
	isDisabled := sp.disableRelayTx
	sp.relayMtx.Unlock()
	return isDisabled
}
func (sp *serverPeer) pushAddrMsg(addresses []*wire.NetAddress) {
	addrs := make([]*wire.NetAddress, 0, len(addresses))
	for _, addr := range addresses {
		if !sp.addressKnown(addr) {
			addrs = append(addrs, addr)
		}
	}
	known, err := sp.PushAddrMsg(addrs)
	if err != nil {
		peerLog.Errorf("Can't push address message to %s: %v", sp.Peer, err)
		sp.Disconnect()
		return
	}
	sp.addKnownAddresses(known)
}
func (sp *serverPeer) addBanScore(persistent, transient uint32, reason string) {
	if cfg.DisableBanning {
		return
	}
	warnThreshold := cfg.BanThreshold >> 1
	if transient == 0 && persistent == 0 {
		score := sp.banScore.Int()
		if score > warnThreshold {
			peerLog.Warnf("Misbehaving peer %s: %s -- ban score is %d, "+
				"it was not increased this time", sp, reason, score)
		}
		return
	}
	score := sp.banScore.Increase(persistent, transient)
	if score > warnThreshold {
		peerLog.Warnf("Misbehaving peer %s: %s -- ban score increased to %d",
			sp, reason, score)
		if score > cfg.BanThreshold {
			peerLog.Warnf("Misbehaving peer %s -- banning and disconnecting",
				sp)
			sp.server.BanPeer(sp)
			sp.Disconnect()
		}
	}
}
func (sp *serverPeer) OnVersion(_ *peer.Peer, msg *wire.MsgVersion) {
	sp.server.timeSource.AddTimeSample(sp.Addr(), msg.Timestamp)
	sp.server.blockManager.NewPeer(sp)
	sp.setDisableRelayTx(msg.DisableRelayTx)
	if !cfg.SimNet {
		addrManager := sp.server.addrManager
		if !sp.Inbound() {
			if !cfg.DisableListen   {
				lna := addrManager.GetBestLocalAddress(sp.NA())
				if addrmgr.IsRoutable(lna) {
					addresses := []*wire.NetAddress{lna}
					sp.pushAddrMsg(addresses)
				}
			}
			hasTimestamp := sp.ProtocolVersion() >=
				wire.NetAddressTimeVersion
			if addrManager.NeedMoreAddresses() && hasTimestamp {
				sp.QueueMessage(wire.NewMsgGetAddr(), nil)
			}
			addrManager.Good(sp.NA())
		}
	}
	sp.server.AddPeer(sp)
}
func (sp *serverPeer) OnMemPool(_ *peer.Peer, msg *wire.MsgMemPool) {
	if sp.server.services&wire.SFNodeBloom != wire.SFNodeBloom {
		peerLog.Debugf("peer %v sent mempool request with bloom "+
			"filtering disabled -- disconnecting", sp)
		sp.Disconnect()
		return
	}
	sp.addBanScore(0, 33, "mempool")
	txMemPool := sp.server.txMemPool
	txDescs := txMemPool.TxDescs()
	invMsg := wire.NewMsgInvSizeHint(uint(len(txDescs)))
	for _, txDesc := range txDescs {
		if !sp.filter.IsLoaded() || sp.filter.MatchTxAndUpdate(txDesc.Tx) {
			iv := wire.NewInvVect(wire.InvTypeTx, txDesc.Tx.Hash())
			invMsg.AddInvVect(iv)
			if len(invMsg.InvList)+1 > wire.MaxInvPerMsg {
				break
			}
		}
	}
	if len(invMsg.InvList) > 0 {
		sp.QueueMessage(invMsg, nil)
	}
}
func (sp *serverPeer) OnTx(_ *peer.Peer, msg *wire.MsgTx) {
	if cfg.BlocksOnly {
		peerLog.Tracef("Ignoring tx %v from %v - blocksonly enabled",
			msg.TxHash(), sp)
		return
	}
	tx := provautil.NewTx(msg)
	iv := wire.NewInvVect(wire.InvTypeTx, tx.Hash())
	sp.AddKnownInventory(iv)
	sp.server.blockManager.QueueTx(tx, sp)
	<-sp.txProcessed
}
func (sp *serverPeer) OnBlock(_ *peer.Peer, msg *wire.MsgBlock, buf []byte) {
	block := provautil.NewBlockFromBlockAndBytes(msg, buf)
	iv := wire.NewInvVect(wire.InvTypeBlock, block.Hash())
	sp.AddKnownInventory(iv)
	sp.server.blockManager.QueueBlock(block, sp)
	<-sp.blockProcessed
}
func (sp *serverPeer) OnInv(_ *peer.Peer, msg *wire.MsgInv) {
	if !cfg.BlocksOnly {
		if len(msg.InvList) > 0 {
			sp.server.blockManager.QueueInv(msg, sp)
		}
		return
	}
	newInv := wire.NewMsgInvSizeHint(uint(len(msg.InvList)))
	for _, invVect := range msg.InvList {
		if invVect.Type == wire.InvTypeTx {
			peerLog.Tracef("Ignoring tx %v in inv from %v -- "+
				"blocksonly enabled", invVect.Hash, sp)
			if sp.ProtocolVersion() >= wire.BIP0037Version {
				peerLog.Infof("Peer %v is announcing "+
					"transactions -- disconnecting", sp)
				sp.Disconnect()
				return
			}
			continue
		}
		err := newInv.AddInvVect(invVect)
		if err != nil {
			peerLog.Errorf("Failed to add inventory vector: %v", err)
			break
		}
	}
	if len(newInv.InvList) > 0 {
		sp.server.blockManager.QueueInv(newInv, sp)
	}
}
func (sp *serverPeer) OnGetData(_ *peer.Peer, msg *wire.MsgGetData) {
	numAdded := 0
	notFound := wire.NewMsgNotFound()
	length := len(msg.InvList)
	sp.addBanScore(0, uint32(length)*99/wire.MaxInvPerMsg, "getdata")
	var waitChan chan struct{}
	doneChan := make(chan struct{}, 1)
	for i, iv := range msg.InvList {
		var c chan struct{}
		if i == length-1 && len(notFound.InvList) == 0 {
			c = doneChan
		} else if (i+1)%3 == 0 {
			c = make(chan struct{}, 1)
		}
		var err error
		switch iv.Type {
		case wire.InvTypeTx:
			err = sp.server.pushTxMsg(sp, &iv.Hash, c, waitChan)
		case wire.InvTypeBlock:
			err = sp.server.pushBlockMsg(sp, &iv.Hash, c, waitChan)
		case wire.InvTypeFilteredBlock:
			err = sp.server.pushMerkleBlockMsg(sp, &iv.Hash, c, waitChan)
		default:
			peerLog.Warnf("Unknown type in inventory request %d",
				iv.Type)
			continue
		}
		if err != nil {
			notFound.AddInvVect(iv)
			if i == len(msg.InvList)-1 && c != nil {
				<-c
			}
		}
		numAdded++
		waitChan = c
	}
	if len(notFound.InvList) != 0 {
		sp.QueueMessage(notFound, doneChan)
	}
	if numAdded > 0 {
		<-doneChan
	}
}
func (sp *serverPeer) OnGetBlocks(_ *peer.Peer, msg *wire.MsgGetBlocks) {
	chain := sp.server.blockManager.chain
	endIdx := uint32(math.MaxUint32)
	if !msg.HashStop.IsEqual(&zeroHash) {
		height, err := chain.BlockHeightByHash(&msg.HashStop)
		if err == nil {
			endIdx = height + 1
		}
	}
	startIdx := uint32(1)
	for _, hash := range msg.BlockLocatorHashes {
		height, err := chain.BlockHeightByHash(hash)
		if err == nil {
			startIdx = height + 1
			break
		}
	}
	autoContinue := false
	if endIdx-startIdx > wire.MaxBlocksPerMsg {
		endIdx = startIdx + wire.MaxBlocksPerMsg
		autoContinue = true
	}
	hashList, err := chain.HeightRange(startIdx, endIdx)
	if err != nil {
		peerLog.Warnf("Block lookup failed: %v", err)
		return
	}
	invMsg := wire.NewMsgInv()
	for i := range hashList {
		iv := wire.NewInvVect(wire.InvTypeBlock, &hashList[i])
		invMsg.AddInvVect(iv)
	}
	if len(invMsg.InvList) > 0 {
		invListLen := len(invMsg.InvList)
		if autoContinue && invListLen == wire.MaxBlocksPerMsg {
			continueHash := invMsg.InvList[invListLen-1].Hash
			sp.continueHash = &continueHash
		}
		sp.QueueMessage(invMsg, nil)
	}
}
func (s *server) locateBlocks(locators []*chainhash.Hash, hashStop *chainhash.Hash) ([]chainhash.Hash, error) {
	chain := s.blockManager.chain
	endIdx := uint32(math.MaxUint32)
	height, err := chain.BlockHeightByHash(hashStop)
	if err == nil {
		endIdx = height + 1
	}
	if len(locators) == 0 {
		if endIdx == math.MaxInt32 {
			return nil, nil
		}
		return []chainhash.Hash{*hashStop}, nil
	}
	startIdx := uint32(1)
	for _, loc := range locators {
		height, err := chain.BlockHeightByHash(loc)
		if err == nil {
			startIdx = height + 1
			break
		}
	}
	if endIdx-startIdx > wire.MaxBlockHeadersPerMsg {
		endIdx = startIdx + wire.MaxBlockHeadersPerMsg
	}
	return chain.HeightRange(startIdx, endIdx)
}