func (p *Peer) localVersionMsg() (*wire.MsgVersion, error) {
	var blockNum uint32
	if p.cfg.NewestBlock != nil {
		var err error
		_, blockNum, err = p.cfg.NewestBlock()
		if err != nil {
			return nil, err
		}
	}
	theirNA := p.na
	if p.cfg.Proxy != "" {
		proxyaddress, _, err := net.SplitHostPort(p.cfg.Proxy)
		if err != nil || p.na.IP.String() == proxyaddress {
			theirNA = wire.NewNetAddressIPPort(net.IP([]byte{0, 0, 0, 0}), 0, 0)
		}
	}
	ourNA := &wire.NetAddress{
		Services: p.cfg.Services,
	}
	nonce, err := wire.RandomUint64()
	if err != nil {
		return nil, err
	}
	sentNonces.Add(nonce)
	msg := wire.NewMsgVersion(ourNA, theirNA, nonce, blockNum)
	msg.AddUserAgent(p.cfg.UserAgentName, p.cfg.UserAgentVersion)
	msg.AddrYou.Services = wire.SFNodeNetwork
	msg.Services = p.cfg.Services
	msg.ProtocolVersion = int32(p.cfg.ProtocolVersion)
	msg.DisableRelayTx = p.cfg.DisableRelayTx
	return msg, nil
}
localVersionMsg creates a version message that can be used to send to the
remote peer.
package peer
import (
	"bytes"
	"container/list"
	"errors"
	"fmt"
	"io"
	"math/rand"
	"net"
	"strconv"
	"sync"
	"sync/atomic"
	"time"
	"github.com/bitgo/prova/blockchain"
	"github.com/bitgo/prova/chaincfg"
	"github.com/bitgo/prova/chaincfg/chainhash"
	"github.com/bitgo/prova/wire"
	"github.com/btcsuite/go-socks/socks"
	"github.com/davecgh/go-spew/spew"
)
const (
	MaxProtocolVersion = wire.FeeFilterVersion
	outputBufferSize = 50
	maxInvTrickleSize = 1000
	maxKnownInventory = 1000
	pingInterval = 2 * time.Minute
	negotiateTimeout = 30 * time.Second
	idleTimeout = 5 * time.Minute
	stallTickInterval = 15 * time.Second
	stallResponseTimeout = 30 * time.Second
	trickleTimeout = 8 * time.Second
)
var (
	nodeCount int32
	zeroHash chainhash.Hash
	sentNonces = newMruNonceMap(50)
	allowSelfConns bool
)
type MessageListeners struct {
	OnGetAddr func(p *Peer, msg *wire.MsgGetAddr)
	OnAddr func(p *Peer, msg *wire.MsgAddr)
	OnPing func(p *Peer, msg *wire.MsgPing)
	OnPong func(p *Peer, msg *wire.MsgPong)
	OnAlert func(p *Peer, msg *wire.MsgAlert)
	OnMemPool func(p *Peer, msg *wire.MsgMemPool)
	OnTx func(p *Peer, msg *wire.MsgTx)
	OnBlock func(p *Peer, msg *wire.MsgBlock, buf []byte)
	OnInv func(p *Peer, msg *wire.MsgInv)
	OnHeaders func(p *Peer, msg *wire.MsgHeaders)
	OnNotFound func(p *Peer, msg *wire.MsgNotFound)
	OnGetData func(p *Peer, msg *wire.MsgGetData)
	OnGetBlocks func(p *Peer, msg *wire.MsgGetBlocks)
	OnGetHeaders func(p *Peer, msg *wire.MsgGetHeaders)
	OnFeeFilter func(p *Peer, msg *wire.MsgFeeFilter)
	OnFilterAdd func(p *Peer, msg *wire.MsgFilterAdd)
	OnFilterClear func(p *Peer, msg *wire.MsgFilterClear)
	OnFilterLoad func(p *Peer, msg *wire.MsgFilterLoad)
	OnMerkleBlock func(p *Peer, msg *wire.MsgMerkleBlock)
	OnVersion func(p *Peer, msg *wire.MsgVersion)
	OnVerAck func(p *Peer, msg *wire.MsgVerAck)
	OnReject func(p *Peer, msg *wire.MsgReject)
	OnSendHeaders func(p *Peer, msg *wire.MsgSendHeaders)
	OnRead func(p *Peer, bytesRead int, msg wire.Message, err error)
	OnWrite func(p *Peer, bytesWritten int, msg wire.Message, err error)
}
type Config struct {
	NewestBlock HashFunc
	HostToNetAddress HostToNetAddrFunc
	Proxy string
	UserAgentName string
	UserAgentVersion string
	ChainParams *chaincfg.Params
	Services wire.ServiceFlag
	ProtocolVersion uint32
	DisableRelayTx bool
	Listeners MessageListeners
}
func minUint32(a, b uint32) uint32 {
	if a < b {
		return a
	}
	return b
}
func newNetAddress(addr net.Addr, services wire.ServiceFlag) (*wire.NetAddress, error) {
	if tcpAddr, ok := addr.(*net.TCPAddr); ok {
		ip := tcpAddr.IP
		port := uint16(tcpAddr.Port)
		na := wire.NewNetAddressIPPort(ip, port, services)
		return na, nil
	}
	if proxiedAddr, ok := addr.(*socks.ProxiedAddr); ok {
		ip := net.ParseIP(proxiedAddr.Host)
		if ip == nil {
			ip = net.ParseIP("0.0.0.0")
		}
		port := uint16(proxiedAddr.Port)
		na := wire.NewNetAddressIPPort(ip, port, services)
		return na, nil
	}
	host, portStr, err := net.SplitHostPort(addr.String())
	if err != nil {
		return nil, err
	}
	ip := net.ParseIP(host)
	port, err := strconv.ParseUint(portStr, 10, 16)
	if err != nil {
		return nil, err
	}
	na := wire.NewNetAddressIPPort(ip, uint16(port), services)
	return na, nil
}
type outMsg struct {
	msg      wire.Message
	doneChan chan<- struct{}
}
type stallControlCmd uint8
const (
	sccSendMessage stallControlCmd = iota
	sccReceiveMessage
	sccHandlerStart
	sccHandlerDone
)
type stallControlMsg struct {
	command stallControlCmd
	message wire.Message
}
type StatsSnap struct {
	ID             int32
	Addr           string
	Services       wire.ServiceFlag
	LastSend       time.Time
	LastRecv       time.Time
	BytesSent      uint64
	BytesRecv      uint64
	ConnTime       time.Time
	TimeOffset     int64
	Version        uint32
	UserAgent      string
	Inbound        bool
	StartingHeight uint32
	LastBlock      uint32
	LastPingNonce  uint64
	LastPingTime   time.Time
	LastPingMicros int64
}
type HashFunc func() (hash *chainhash.Hash, height uint32, err error)
type AddrFunc func(remoteAddr *wire.NetAddress) *wire.NetAddress
type HostToNetAddrFunc func(host string, port uint16,
	services wire.ServiceFlag) (*wire.NetAddress, error)
type Peer struct {
	bytesReceived uint64
	bytesSent     uint64
	lastRecv      int64
	lastSend      int64
	connected     int32
	disconnect    int32
	conn net.Conn
	addr    string
	cfg     Config
	inbound bool
	flagsMtx             sync.Mutex  
	na                   *wire.NetAddress
	id                   int32
	userAgent            string
	services             wire.ServiceFlag
	versionKnown         bool
	advertisedProtoVer   uint32  
	protocolVersion      uint32  
	sendHeadersPreferred bool    
	versionSent          bool
	verAckReceived       bool
	knownInventory     *mruInventoryMap
	prevGetBlocksMtx   sync.Mutex
	prevGetBlocksBegin *chainhash.Hash
	prevGetBlocksStop  *chainhash.Hash
	prevGetHdrsMtx     sync.Mutex
	prevGetHdrsBegin   *chainhash.Hash
	prevGetHdrsStop    *chainhash.Hash
	statsMtx           sync.RWMutex
	timeOffset         int64
	timeConnected      time.Time
	startingHeight     uint32
	lastBlock          uint32
	lastAnnouncedBlock *chainhash.Hash
	lastPingNonce      uint64     
	lastPingTime       time.Time  
	lastPingMicros     int64      
	stallControl  chan stallControlMsg
	outputQueue   chan outMsg
	sendQueue     chan outMsg
	sendDoneQueue chan struct{}
	outputInvChan chan *wire.InvVect
	inQuit        chan struct{}
	queueQuit     chan struct{}
	outQuit       chan struct{}
	quit          chan struct{}
}
func (p *Peer) String() string {
	return fmt.Sprintf("%s (%s)", p.addr, directionString(p.inbound))
}
func (p *Peer) UpdateLastBlockHeight(newHeight uint32) {
	p.statsMtx.Lock()
	log.Tracef("Updating last block height of peer %v from %v to %v",
		p.addr, p.lastBlock, newHeight)
	p.lastBlock = newHeight
	p.statsMtx.Unlock()
}
func (p *Peer) UpdateLastAnnouncedBlock(blkHash *chainhash.Hash) {
	log.Tracef("Updating last blk for peer %v, %v", p.addr, blkHash)
	p.statsMtx.Lock()
	p.lastAnnouncedBlock = blkHash
	p.statsMtx.Unlock()
}
func (p *Peer) AddKnownInventory(invVect *wire.InvVect) {
	p.knownInventory.Add(invVect)
}
func (p *Peer) StatsSnapshot() *StatsSnap {
	p.statsMtx.RLock()
	p.flagsMtx.Lock()
	id := p.id
	addr := p.addr
	userAgent := p.userAgent
	services := p.services
	protocolVersion := p.advertisedProtoVer
	p.flagsMtx.Unlock()
	statsSnap := &StatsSnap{
		ID:             id,
		Addr:           addr,
		UserAgent:      userAgent,
		Services:       services,
		LastSend:       p.LastSend(),
		LastRecv:       p.LastRecv(),
		BytesSent:      p.BytesSent(),
		BytesRecv:      p.BytesReceived(),
		ConnTime:       p.timeConnected,
		TimeOffset:     p.timeOffset,
		Version:        protocolVersion,
		Inbound:        p.inbound,
		StartingHeight: p.startingHeight,
		LastBlock:      p.lastBlock,
		LastPingNonce:  p.lastPingNonce,
		LastPingMicros: p.lastPingMicros,
		LastPingTime:   p.lastPingTime,
	}
	p.statsMtx.RUnlock()
	return statsSnap
}
func (p *Peer) ID() int32 {
	p.flagsMtx.Lock()
	id := p.id
	p.flagsMtx.Unlock()
	return id
}
func (p *Peer) NA() *wire.NetAddress {
	p.flagsMtx.Lock()
	na := p.na
	p.flagsMtx.Unlock()
	return na
}
func (p *Peer) Addr() string {
	return p.addr
}
func (p *Peer) Inbound() bool {
	return p.inbound
}
func (p *Peer) Services() wire.ServiceFlag {
	p.flagsMtx.Lock()
	services := p.services
	p.flagsMtx.Unlock()
	return services
}
func (p *Peer) UserAgent() string {
	p.flagsMtx.Lock()
	userAgent := p.userAgent
	p.flagsMtx.Unlock()
	return userAgent
}
func (p *Peer) LastAnnouncedBlock() *chainhash.Hash {
	p.statsMtx.RLock()
	lastAnnouncedBlock := p.lastAnnouncedBlock
	p.statsMtx.RUnlock()
	return lastAnnouncedBlock
}
func (p *Peer) LastPingNonce() uint64 {
	p.statsMtx.RLock()
	lastPingNonce := p.lastPingNonce
	p.statsMtx.RUnlock()
	return lastPingNonce
}
func (p *Peer) LastPingTime() time.Time {
	p.statsMtx.RLock()
	lastPingTime := p.lastPingTime
	p.statsMtx.RUnlock()
	return lastPingTime
}
func (p *Peer) LastPingMicros() int64 {
	p.statsMtx.RLock()
	lastPingMicros := p.lastPingMicros
	p.statsMtx.RUnlock()
	return lastPingMicros
}
func (p *Peer) VersionKnown() bool {
	p.flagsMtx.Lock()
	versionKnown := p.versionKnown
	p.flagsMtx.Unlock()
	return versionKnown
}
func (p *Peer) VerAckReceived() bool {
	p.flagsMtx.Lock()
	verAckReceived := p.verAckReceived
	p.flagsMtx.Unlock()
	return verAckReceived
}
func (p *Peer) ProtocolVersion() uint32 {
	p.flagsMtx.Lock()
	protocolVersion := p.protocolVersion
	p.flagsMtx.Unlock()
	return protocolVersion
}
func (p *Peer) LastBlock() uint32 {
	p.statsMtx.RLock()
	lastBlock := p.lastBlock
	p.statsMtx.RUnlock()
	return lastBlock
}
func (p *Peer) LastSend() time.Time {
	return time.Unix(atomic.LoadInt64(&p.lastSend), 0)
}
func (p *Peer) LastRecv() time.Time {
	return time.Unix(atomic.LoadInt64(&p.lastRecv), 0)
}
func (p *Peer) LocalAddr() net.Addr {
	var localAddr net.Addr
	if atomic.LoadInt32(&p.connected) != 0 {
		localAddr = p.conn.LocalAddr()
	}
	return localAddr
}
func (p *Peer) BytesSent() uint64 {
	return atomic.LoadUint64(&p.bytesSent)
}
func (p *Peer) BytesReceived() uint64 {
	return atomic.LoadUint64(&p.bytesReceived)
}
func (p *Peer) TimeConnected() time.Time {
	p.statsMtx.RLock()
	timeConnected := p.timeConnected
	p.statsMtx.RUnlock()
	return timeConnected
}
func (p *Peer) TimeOffset() int64 {
	p.statsMtx.RLock()
	timeOffset := p.timeOffset
	p.statsMtx.RUnlock()
	return timeOffset
}
func (p *Peer) StartingHeight() uint32 {
	p.statsMtx.RLock()
	startingHeight := p.startingHeight
	p.statsMtx.RUnlock()
	return startingHeight
}
func (p *Peer) WantsHeaders() bool {
	p.flagsMtx.Lock()
	sendHeadersPreferred := p.sendHeadersPreferred
	p.flagsMtx.Unlock()
	return sendHeadersPreferred
}