func (p *Peer) BytesSent() uint64 {
	return atomic.LoadUint64(&p.bytesSent)
}
BytesSent returns the total number of bytes sent by the peer.

This function is safe for concurrent access.
package peer
import (
	"bytes"
	"container/list"
	"errors"
	"fmt"
	"io"
	"math/rand"
	"net"
	"strconv"
	"sync"
	"sync/atomic"
	"time"
	"github.com/blang/semver"
	"github.com/btcsuite/go-socks/socks"
	"github.com/davecgh/go-spew/spew"
	"github.com/soteria-dag/soterd/blockdag"
	"github.com/soteria-dag/soterd/chaincfg"
	"github.com/soteria-dag/soterd/chaincfg/chainhash"
	"github.com/soteria-dag/soterd/wire"
)
const (
	MaxProtocolVersion = wire.FeeFilterVersion
	DefaultTrickleInterval = 10 * time.Second
	MinAcceptableProtocolVersion = wire.MultipleAddressVersion
	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
)
var (
	nodeCount int32
	zeroHash chainhash.Hash
	sentNonces = newMruNonceMap(50)
	allowSelfConns bool
)
type MessageListeners struct {
	OnGetAddr func(p *Peer, msg *wire.MsgGetAddr)
	OnGetAddrCache func(p *Peer, msg *wire.MsgGetAddrCache)
	OnAddr func(p *Peer, msg *wire.MsgAddr)
	OnAddrCache func(p *Peer, msg *wire.MsgAddrCache)
	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)
	OnCFilter func(p *Peer, msg *wire.MsgCFilter)
	OnCFHeaders func(p *Peer, msg *wire.MsgCFHeaders)
	OnCFCheckpt func(p *Peer, msg *wire.MsgCFCheckpt)
	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)
	OnGetCFilters func(p *Peer, msg *wire.MsgGetCFilters)
	OnGetCFHeaders func(p *Peer, msg *wire.MsgGetCFHeaders)
	OnGetCFCheckpt func(p *Peer, msg *wire.MsgGetCFCheckpt)
	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) *wire.MsgReject
	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 semver.Version
	UserAgentComments []string
	ChainParams *chaincfg.Params
	Services wire.ServiceFlag
	ProtocolVersion uint32
	DisableRelayTx bool
	Listeners MessageListeners
	TrickleInterval time.Duration
}
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{}
	encoding wire.MessageEncoding
}
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 int32
	LastBlock      int32
	LastPingNonce  uint64
	LastPingTime   time.Time
	LastPingMicros int64
}
type HashFunc func() (hash *chainhash.Hash, height int32, 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    
	verAckReceived       bool
	witnessEnabled       bool
	wireEncoding wire.MessageEncoding
	knownInventory           *mruInventoryMap
	prevGetBlocksMtx         sync.Mutex
	prevGetBlocksStartHeight *int32
	prevGetBlocksStop        *chainhash.Hash
	prevGetHdrsMtx           sync.Mutex
	prevGetHdrsStartHeight   *int32
	prevGetHdrsStop          *chainhash.Hash
	statsMtx           sync.RWMutex
	timeOffset         int64
	timeConnected      time.Time
	startingHeight     int32
	maxBlockHeight     int32
	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{}
}
type PeerAddrs struct {
	Inbound []string
	Outbound []string
	Known []string
}
func NewPeerAddrs() PeerAddrs {
	return PeerAddrs{
		Inbound: make([]string, 0),
		Outbound: make([]string, 0),
		Known: make([]string, 0),
	}
}
func (p *Peer) String() string {
	return fmt.Sprintf("%s (%s)", p.addr, directionString(p.inbound))
}
func (p *Peer) UpdateMaxBlockHeight(newHeight int32) {
	p.statsMtx.Lock()
	log.Tracef("Updating last block height of peer %v from %v to %v",
		p.addr, p.maxBlockHeight, newHeight)
	p.maxBlockHeight = newHeight
	p.statsMtx.Unlock()
}
func (p *Peer) UpdateLastAnnouncedBlock(blkHash *chainhash.Hash) {
	log.Tracef("Updating last blkHash 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.maxBlockHeight,
		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) MaxBlockHeight() int32 {
	p.statsMtx.RLock()
	lastBlock := p.maxBlockHeight
	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
}