mirror of
https://github.com/libp2p/go-libp2p-core.git
synced 2025-02-11 06:50:08 +08:00
* add SignedEnvelope type * use struct for SignedEnvelope instead of exposing protobuf directly * doc comments for envelopes * tests for SignedEnvelopes * add helpers to make routing records for Host * fix doc comment * go fmt * add method to peerstore to retrieve signed routing records * update to match spec changes * just use nanoseconds * use proto3 & rename fields to match spec changes * use proto3 for routing records * make envelope fields private & validate on unmarshal * use buffer pool for envelope signatures * tests for RoutingState * go fmt * rename Equals -> Equal, add some comments * use test helpers * get rid of unsigned RoutingState struct, only expose SignedRoutingState * rm batching SignedRoutingStates accessor in peerstore the datastore peerstore implementation doesn't support batched reads, so it's no more efficient to get a bunch of states at once than it is to call SignedRoutingState multiple times. * whitespace * expose struct fields & remove accessors * use camelCase in protos for consistency * use multiformats uvarint for length-prefixes * remove payloadType check when unmarhaling * rm stray ref to golang/protobuf * define CertifiedAddrBook to avoid breaking API change * add events for updated addresses and routing state * remove SignedRoutingStateFromHost helper moving this to go-libp2p * add routing state records, extend peerstore API * fix: rebuild protos with new gogofaster generator * filter private addrs from signed routing records * envelope: use byte slices from pool; adjust interface. * move envelope to record package. * move protobuf files; adjust imports everywhere. * rename RoutingStateRecord -> PeerRecord also removes embedded reference to Envelope from the record, as that was confusing. as a result, the CertifiedAddrBook now accepts/returns record.SignedEnvelope instead of a specialized type. * hoist Seq from PeerRecord to SignedEnvelope * test that PeerRecords can't be signed by wrong key * commit go.sum * add Seq field to envelope signature * fix proto_path in Makefile * fix import ordering * comments for PeerRecord proto message also removes the seq field from PeerMessage proto, since it was moved to the SignedEnvelope * use Record type for envelope payloads * rename SignedEnvelope -> Envelope, unmarshal payload in ConsumeEnvelope * return buffer to pool before early return * doc comments * rename CertifiedAddrBook methods, update comments * cache unmarshalled Record payload inside Envelope * doc comments * store reflect.Type when registering Record * Revert "return buffer to pool before early return"8d8da386f2
misread this - unsigned will be nil if there's an error, so it was right the way it was * use a DefaultRecord for unregistered PayloadTypes instead of returning an error if we don't have a registered Record for a given PayloadType, we can have a catch-all DefaultRecord type that just preserves the original payload as a []byte * cleanup DefaultRecord code a bit - removes unused error return from blankRecordForPayloadType - just references instead of copying in DefaultRecord.UnmarshalRecord I figure this is likely safe, since we'll be unmarshalling from the payload of an Envelope, which shouldn't get altered after it's created. * use explicit payloadType in MakeEnvelopeWithRecord * Revert DefaultRecord commitsae3bc7bdfb
a26c845a76
* doc comments * move Seq field back to PeerRecord * make diffs optional in EvtLocalAddressesUpdated * more envelope tests * replace MakeEnvelope with record.Seal also: - add Domain and Codec fields to Record interface * fix import * add interface check * rename ProcessPeerRecord -> ConsumePeerRecord also, adds bool `accepted` return value * rename event field, add doc comment * peer record protobuf: fix field casing. * record protobuf: add docs and fix casing. * cleanup: group imports. * nit: split test/utils.go => test/{addrs,errors}.go. Co-authored-by: Raúl Kripalani <raul.kripalani@gmail.com>
209 lines
6.4 KiB
Go
209 lines
6.4 KiB
Go
package peer
|
|
|
|
import (
|
|
"fmt"
|
|
"time"
|
|
|
|
pb "github.com/libp2p/go-libp2p-core/peer/pb"
|
|
"github.com/libp2p/go-libp2p-core/record"
|
|
|
|
ma "github.com/multiformats/go-multiaddr"
|
|
|
|
"github.com/gogo/protobuf/proto"
|
|
)
|
|
|
|
var _ record.Record = (*PeerRecord)(nil)
|
|
|
|
func init() {
|
|
record.RegisterType(&PeerRecord{})
|
|
}
|
|
|
|
// The domain string used for peer records contained in a Envelope.
|
|
const PeerRecordEnvelopeDomain = "libp2p-peer-record"
|
|
|
|
// The type hint used to identify peer records in a 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 a 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 NewPeerRecord() *PeerRecord {
|
|
return &PeerRecord{Seq: TimestampSeq()}
|
|
}
|
|
|
|
// PeerRecordFromAddrInfo creates a PeerRecord from an AddrInfo struct.
|
|
// The returned record will have a timestamp-based sequence number.
|
|
func PeerRecordFromAddrInfo(info AddrInfo) *PeerRecord {
|
|
rec := NewPeerRecord()
|
|
rec.PeerID = info.ID
|
|
rec.Addrs = info.Addrs
|
|
return rec
|
|
}
|
|
|
|
// TimestampSeq is a helper to generate a timestamp-based sequence number for a PeerRecord.
|
|
func TimestampSeq() uint64 {
|
|
return uint64(time.Now().UnixNano())
|
|
}
|
|
|
|
// Domain is used when signing and validating PeerRecords contained in Envelopes.
|
|
// It is constant for all PeerRecord instances.
|
|
func (r *PeerRecord) Domain() string {
|
|
return PeerRecordEnvelopeDomain
|
|
}
|
|
|
|
// Codec is a binary identifier for the PeerRecord type. It is constant for all PeerRecord instances.
|
|
func (r *PeerRecord) Codec() []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 (r *PeerRecord) UnmarshalRecord(bytes []byte) error {
|
|
if r == nil {
|
|
return fmt.Errorf("cannot unmarshal PeerRecord to nil receiver")
|
|
}
|
|
|
|
var msg pb.PeerRecord
|
|
err := proto.Unmarshal(bytes, &msg)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
var id ID
|
|
err = id.UnmarshalBinary(msg.PeerId)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
r.PeerID = id
|
|
r.Addrs = addrsFromProtobuf(msg.Addresses)
|
|
r.Seq = msg.Seq
|
|
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 (r *PeerRecord) MarshalRecord() ([]byte, error) {
|
|
idBytes, err := r.PeerID.MarshalBinary()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
msg := pb.PeerRecord{
|
|
PeerId: idBytes,
|
|
Addresses: addrsToProtobuf(r.Addrs),
|
|
Seq: r.Seq,
|
|
}
|
|
return proto.Marshal(&msg)
|
|
}
|
|
|
|
// Equal returns true if the other PeerRecord is identical to this one.
|
|
func (r *PeerRecord) Equal(other *PeerRecord) bool {
|
|
if other == nil {
|
|
return r == nil
|
|
}
|
|
if r.PeerID != other.PeerID {
|
|
return false
|
|
}
|
|
if r.Seq != other.Seq {
|
|
return false
|
|
}
|
|
if len(r.Addrs) != len(other.Addrs) {
|
|
return false
|
|
}
|
|
for i, _ := range r.Addrs {
|
|
if !r.Addrs[i].Equal(other.Addrs[i]) {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
func addrsFromProtobuf(addrs []*pb.PeerRecord_AddressInfo) []ma.Multiaddr {
|
|
var out []ma.Multiaddr
|
|
for _, addr := range addrs {
|
|
a, err := ma.NewMultiaddrBytes(addr.Multiaddr)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
out = append(out, a)
|
|
}
|
|
return out
|
|
}
|
|
|
|
func addrsToProtobuf(addrs []ma.Multiaddr) []*pb.PeerRecord_AddressInfo {
|
|
var out []*pb.PeerRecord_AddressInfo
|
|
for _, addr := range addrs {
|
|
out = append(out, &pb.PeerRecord_AddressInfo{Multiaddr: addr.Bytes()})
|
|
}
|
|
return out
|
|
}
|