package peer

import (
	
	
	

	
	
	

	ma 

	
)

var _ record.Record = (*PeerRecord)(nil)

func init() {
	record.RegisterType(&PeerRecord{})
}

// PeerRecordEnvelopeDomain is the domain string used for peer records contained in an Envelope.
const PeerRecordEnvelopeDomain = "libp2p-peer-record"

// PeerRecordEnvelopePayloadType is the type hint used to identify peer records in an Envelope.
// Defined in https://github.com/multiformats/multicodec/blob/master/table.csv
// with name "libp2p-peer-record".
var PeerRecordEnvelopePayloadType = []byte{0x03, 0x01}

// PeerRecord contains information that is broadly useful to share with other peers,
// either through a direct exchange (as in the libp2p identify protocol), or through
// a Peer Routing provider, such as a DHT.
//
// Currently, a PeerRecord contains the public listen addresses for a peer, but this
// is expected to expand to include other information in the future.
//
// PeerRecords are ordered in time by their Seq field. Newer PeerRecords must have
// greater Seq values than older records. The NewPeerRecord function will create
// a PeerRecord with a timestamp-based Seq value. The other PeerRecord fields should
// be set by the caller:
//
//	rec := peer.NewPeerRecord()
//	rec.PeerID = aPeerID
//	rec.Addrs = someAddrs
//
// Alternatively, you can construct a PeerRecord struct directly and use the TimestampSeq
// helper to set the Seq field:
//
//	rec := peer.PeerRecord{PeerID: aPeerID, Addrs: someAddrs, Seq: peer.TimestampSeq()}
//
// Failing to set the Seq field will not result in an error, however, a PeerRecord with a
// Seq value of zero may be ignored or rejected by other peers.
//
// PeerRecords are intended to be shared with other peers inside a signed
// routing.Envelope, and PeerRecord implements the routing.Record interface
// to facilitate this.
//
// To share a PeerRecord, first call Sign to wrap the record in an Envelope
// and sign it with the local peer's private key:
//
//	rec := &PeerRecord{PeerID: myPeerId, Addrs: myAddrs}
//	envelope, err := rec.Sign(myPrivateKey)
//
// The resulting record.Envelope can be marshalled to a []byte and shared
// publicly. As a convenience, the MarshalSigned method will produce the
// Envelope and marshal it to a []byte in one go:
//
//	rec := &PeerRecord{PeerID: myPeerId, Addrs: myAddrs}
//	recordBytes, err := rec.MarshalSigned(myPrivateKey)
//
// To validate and unmarshal a signed PeerRecord from a remote peer,
// "consume" the containing envelope, which will return both the
// routing.Envelope and the inner Record. The Record must be cast to
// a PeerRecord pointer before use:
//
//	envelope, untypedRecord, err := ConsumeEnvelope(envelopeBytes, PeerRecordEnvelopeDomain)
//	if err != nil {
//	  handleError(err)
//	  return
//	}
//	peerRec := untypedRecord.(*PeerRecord)
type PeerRecord struct {
	// PeerID is the ID of the peer this record pertains to.
	PeerID ID

	// Addrs contains the public addresses of the peer this record pertains to.
	Addrs []ma.Multiaddr

	// Seq is a monotonically-increasing sequence counter that's used to order
	// PeerRecords in time. The interval between Seq values is unspecified,
	// but newer PeerRecords MUST have a greater Seq value than older records
	// for the same peer.
	Seq uint64
}

// NewPeerRecord returns a PeerRecord with a timestamp-based sequence number.
// The returned record is otherwise empty and should be populated by the caller.
func () *PeerRecord {
	return &PeerRecord{Seq: TimestampSeq()}
}

// PeerRecordFromAddrInfo creates a PeerRecord from an AddrInfo struct.
// The returned record will have a timestamp-based sequence number.
func ( AddrInfo) *PeerRecord {
	 := NewPeerRecord()
	.PeerID = .ID
	.Addrs = .Addrs
	return 
}

// PeerRecordFromProtobuf creates a PeerRecord from a protobuf PeerRecord
// struct.
func ( *pb.PeerRecord) (*PeerRecord, error) {
	 := &PeerRecord{}

	var  ID
	if  := .UnmarshalBinary(.PeerId);  != nil {
		return nil, 
	}

	.PeerID = 
	.Addrs = addrsFromProtobuf(.Addresses)
	.Seq = .Seq

	return , nil
}

var (
	lastTimestampMu sync.Mutex
	lastTimestamp   uint64
)

// TimestampSeq is a helper to generate a timestamp-based sequence number for a PeerRecord.
func () uint64 {
	 := uint64(time.Now().UnixNano())
	lastTimestampMu.Lock()
	defer lastTimestampMu.Unlock()
	// Not all clocks are strictly increasing, but we need these sequence numbers to be strictly
	// increasing.
	if  <= lastTimestamp {
		 = lastTimestamp + 1
	}
	lastTimestamp = 
	return 
}

// Domain is used when signing and validating PeerRecords contained in Envelopes.
// It is constant for all PeerRecord instances.
func ( *PeerRecord) () string {
	return PeerRecordEnvelopeDomain
}

// Codec is a binary identifier for the PeerRecord type. It is constant for all PeerRecord instances.
func ( *PeerRecord) () []byte {
	return PeerRecordEnvelopePayloadType
}

// UnmarshalRecord parses a PeerRecord from a byte slice.
// This method is called automatically when consuming a record.Envelope
// whose PayloadType indicates that it contains a PeerRecord.
// It is generally not necessary or recommended to call this method directly.
func ( *PeerRecord) ( []byte) ( error) {
	if  == nil {
		return fmt.Errorf("cannot unmarshal PeerRecord to nil receiver")
	}

	defer func() { catch.HandlePanic(recover(), &, "libp2p peer record unmarshal") }()

	var  pb.PeerRecord
	 = proto.Unmarshal(, &)
	if  != nil {
		return 
	}

	,  := PeerRecordFromProtobuf(&)
	if  != nil {
		return 
	}
	* = *

	return nil
}

// MarshalRecord serializes a PeerRecord to a byte slice.
// This method is called automatically when constructing a routing.Envelope
// using Seal or PeerRecord.Sign.
func ( *PeerRecord) () ( []byte,  error) {
	defer func() { catch.HandlePanic(recover(), &, "libp2p peer record marshal") }()

	,  := .ToProtobuf()
	if  != nil {
		return nil, 
	}
	return proto.Marshal()
}

// Equal returns true if the other PeerRecord is identical to this one.
func ( *PeerRecord) ( *PeerRecord) bool {
	if  == nil {
		return  == nil
	}
	if .PeerID != .PeerID {
		return false
	}
	if .Seq != .Seq {
		return false
	}
	if len(.Addrs) != len(.Addrs) {
		return false
	}
	for  := range .Addrs {
		if !.Addrs[].Equal(.Addrs[]) {
			return false
		}
	}
	return true
}

// ToProtobuf returns the equivalent Protocol Buffer struct object of a PeerRecord.
func ( *PeerRecord) () (*pb.PeerRecord, error) {
	,  := .PeerID.MarshalBinary()
	if  != nil {
		return nil, 
	}
	return &pb.PeerRecord{
		PeerId:    ,
		Addresses: addrsToProtobuf(.Addrs),
		Seq:       .Seq,
	}, nil
}

func addrsFromProtobuf( []*pb.PeerRecord_AddressInfo) []ma.Multiaddr {
	 := make([]ma.Multiaddr, 0, len())
	for ,  := range  {
		,  := ma.NewMultiaddrBytes(.Multiaddr)
		if  != nil {
			continue
		}
		 = append(, )
	}
	return 
}

func addrsToProtobuf( []ma.Multiaddr) []*pb.PeerRecord_AddressInfo {
	 := make([]*pb.PeerRecord_AddressInfo, 0, len())
	for ,  := range  {
		 = append(, &pb.PeerRecord_AddressInfo{Multiaddr: .Bytes()})
	}
	return 
}