func newPing(version uint32, networkID uint32, srcAddr *net.UDPAddr, dstAddr *net.UDPAddr) *pb.Ping {
	return &pb.Ping{
		Version:   version,
		NetworkId: networkID,
		Timestamp: time.Now().Unix(),
		SrcAddr:   srcAddr.IP.String(),
		SrcPort:   uint32(srcAddr.Port),
		DstAddr:   dstAddr.IP.String(),
	}
}
------ Message Constructors ------
package discover
import (
	"bytes"
	"errors"
	"fmt"
	"net"
	"sync"
	"time"
	pb "github.com/iotaledger/hive.go/autopeering/discover/proto"
	"github.com/iotaledger/hive.go/autopeering/peer"
	peerpb "github.com/iotaledger/hive.go/autopeering/peer/proto"
	"github.com/iotaledger/hive.go/autopeering/peer/service"
	"github.com/iotaledger/hive.go/autopeering/server"
	"github.com/iotaledger/hive.go/backoff"
	"github.com/iotaledger/hive.go/identity"
	"github.com/iotaledger/hive.go/logger"
	"github.com/iotaledger/hive.go/netutil"
	"github.com/iotaledger/hive.go/typeutils"
	"google.golang.org/protobuf/proto"
)
const (
	backoffInterval = 500 * time.Millisecond
	maxRetries      = 2
	logSends        = true
)
var retryPolicy = backoff.ExponentialBackOff(backoffInterval, 1.5).With(
	backoff.Jitter(0.5), backoff.MaxRetries(maxRetries))
type Protocol struct {
	server.Protocol
	loc     *peer.Local     
	version uint32          
	netID   uint32          
	log     *logger.Logger  
	mgr       *manager  
	running   *typeutils.AtomicBool
	closeOnce sync.Once
}
func New(local *peer.Local, version uint32, networkID uint32, opts ...Option) *Protocol {
	args := &options{
		log:         logger.NewNopLogger(),
		masterPeers: nil,
	}
	for _, opt := range opts {
		opt.apply(args)
	}
	p := &Protocol{
		loc:     local,
		version: version,
		netID:   networkID,
		log:     args.log,
		running: typeutils.NewAtomicBool(),
	}
	p.mgr = newManager(p, args.masterPeers, args.log.Named("mgr"))
	return p
}
func (p *Protocol) Start(s server.Sender) {
	p.Protocol.Sender = s
	p.mgr.start()
	p.log.Debug("discover started")
	p.running.Set()
}
func (p *Protocol) Close() {
	p.closeOnce.Do(func() {
		p.running.UnSet()
		p.mgr.close()
	})
}
func (p *Protocol) Events() Events {
	return p.mgr.events
}
func (p *Protocol) IsVerified(id identity.ID, ip net.IP) bool {
	return time.Since(p.loc.Database().LastPong(id, ip)) < PingExpiration
}
func (p *Protocol) EnsureVerified(peer *peer.Peer) error {
	if !p.hasVerified(peer.ID(), peer.IP()) {
		if err := p.Ping(peer); err != nil {
			return err
		}
		time.Sleep(server.ResponseTimeout)
	}
	return nil
}
func (p *Protocol) GetMasterPeers() []*peer.Peer {
	return unwrapPeers(p.mgr.masterPeers())
}
func (p *Protocol) GetVerifiedPeer(id identity.ID) *peer.Peer {
	for _, verified := range p.mgr.verifiedPeers() {
		if verified.ID() == id {
			return unwrapPeer(verified)
		}
	}
	from, err := p.local().Database().Peer(id)
	if err != nil {
		p.log.Warnw("invalid stored peer",
			"id", id,
			"err", err,
		)
		return nil
	}
	p.sendPing(from.Address(), from.ID())
	return from
}
func (p *Protocol) GetVerifiedPeers() []*peer.Peer {
	return unwrapPeers(p.mgr.verifiedPeers())
}
func (p *Protocol) HandleMessage(s *server.Server, fromAddr *net.UDPAddr, from *identity.Identity, data []byte) (bool, error) {
	if !p.running.IsSet() {
		return false, nil
	}
	switch pb.MType(data[0]) {
	case pb.MPing:
		m := new(pb.Ping)
		if err := proto.Unmarshal(data[1:], m); err != nil {
			return true, fmt.Errorf("invalid message: %w", err)
		}
		if p.validatePing(fromAddr, m) {
			p.handlePing(s, fromAddr, from, m, data)
		}
	case pb.MPong:
		m := new(pb.Pong)
		if err := proto.Unmarshal(data[1:], m); err != nil {
			return true, fmt.Errorf("invalid message: %w", err)
		}
		if p.validatePong(s, fromAddr, from.ID(), m) {
			p.handlePong(fromAddr, from, m)
		}
	case pb.MDiscoveryRequest:
		m := new(pb.DiscoveryRequest)
		if err := proto.Unmarshal(data[1:], m); err != nil {
			return true, fmt.Errorf("invalid message: %w", err)
		}
		if p.validateDiscoveryRequest(fromAddr, from.ID(), m) {
			p.handleDiscoveryRequest(s, from.ID(), data)
		}
	case pb.MDiscoveryResponse:
		m := new(pb.DiscoveryResponse)
		if err := proto.Unmarshal(data[1:], m); err != nil {
			return true, fmt.Errorf("invalid message: %w", err)
		}
		p.validateDiscoveryResponse(s, fromAddr, from.ID(), m)
	default:
		return false, nil
	}
	return true, nil
}
func (p *Protocol) local() *peer.Local {
	return p.loc
}
func (p *Protocol) localAddr() *net.UDPAddr {
	return &net.UDPAddr{
		IP:   p.loc.IP(),
		Port: p.loc.Services().Get(service.PeeringKey).Port(),
	}
}
func (p *Protocol) Ping(to *peer.Peer) error {
	return backoff.Retry(retryPolicy, func() error {
		err := <-p.sendPing(to.Address(), to.ID())
		if err != nil && !errors.Is(err, server.ErrTimeout) {
			return backoff.Permanent(err)
		}
		return err
	})
}
func (p *Protocol) sendPing(toAddr *net.UDPAddr, toID identity.ID) <-chan error {
	srcAddr := p.localAddr()
	srcAddr.IP = net.IPv4zero
	ping := newPing(p.version, p.netID, srcAddr, toAddr)
	data := marshal(ping)
	hash := server.PacketHash(data)
	callback := func(msg server.Message) bool {
		pong := msg.(*pb.Pong)
		serviceMap := pong.Services.GetMap()
		if serviceMap == nil {
			return false
		}
		peering := serviceMap[string(service.PeeringKey)]
		if peering == nil || int(peering.GetPort()) != toAddr.Port {
			return false
		}
		return bytes.Equal(pong.GetReqHash(), hash)
	}
	p.logSend(toAddr, ping)
	return p.Protocol.SendExpectingReply(toAddr, toID, data, pb.MPong, callback)
}
func (p *Protocol) DiscoveryRequest(to *peer.Peer) ([]*peer.Peer, error) {
	if err := p.EnsureVerified(to); err != nil {
		return nil, err
	}
	req := newDiscoveryRequest()
	data := marshal(req)
	hash := server.PacketHash(data)
	peers := make([]*peer.Peer, 0, MaxPeersInResponse)
	callback := func(m server.Message) bool {
		res := m.(*pb.DiscoveryResponse)
		if !bytes.Equal(res.GetReqHash(), hash) {
			return false
		}
		peers = peers[:0]
		for _, protoPeer := range res.GetPeers() {
			if p, _ := peer.FromProto(protoPeer); p != nil {
				peers = append(peers, p)
			}
		}
		return true
	}
	err := backoff.Retry(retryPolicy, func() error {
		p.logSend(to.Address(), req)
		err := <-p.Protocol.SendExpectingReply(to.Address(), to.ID(), data, pb.MDiscoveryResponse, callback)
		if err != nil && !errors.Is(err, server.ErrTimeout) {
			return backoff.Permanent(err)
		}
		return err
	})
	return peers, err
}
func (p *Protocol) hasVerified(id identity.ID, ip net.IP) bool {
	return time.Since(p.loc.Database().LastPing(id, ip)) < PingExpiration
}
func (p *Protocol) logSend(toAddr *net.UDPAddr, msg pb.Message) {
	if logSends {
		p.log.Debugw("send message", "type", msg.Name(), "addr", toAddr)
	}
}
func marshal(msg pb.Message) []byte {
	mType := msg.Type()
	if mType > 0xFF {
		panic("invalid message")
	}
	data, err := proto.Marshal(msg)
	if err != nil {
		panic("invalid message")
	}
	return append([]byte{byte(mType)}, data...)
}
func newPeer(identity *identity.Identity, network string, addr *net.UDPAddr) *peer.Peer {
	services := service.New()
	services.Update(service.PeeringKey, network, addr.Port)
	return peer.NewPeer(identity, addr.IP, services)
}