mirror of
https://github.com/libp2p/go-libp2p-core.git
synced 2025-03-15 11:10:08 +08:00
150 lines
4.7 KiB
Go
150 lines
4.7 KiB
Go
package routing
|
|
|
|
import (
|
|
"errors"
|
|
"github.com/gogo/protobuf/proto"
|
|
"github.com/libp2p/go-libp2p-core/crypto"
|
|
"github.com/libp2p/go-libp2p-core/peer"
|
|
ma "github.com/multiformats/go-multiaddr"
|
|
pb "github.com/libp2p/go-libp2p-core/routing/pb"
|
|
"time"
|
|
)
|
|
|
|
// The domain string used for routing state records contained in a SignedEnvelope.
|
|
const StateEnvelopeDomain = "libp2p-routing-record"
|
|
|
|
// The type hint used to identify routing state records in a SignedEnvelope.
|
|
// TODO: register multicodec
|
|
var StateEnvelopeTypeHint = []byte("/libp2p/routing-record")
|
|
|
|
// AnnotatedAddr will extend the Multiaddr type with additional metadata, as
|
|
// extensions are added to the routing state record spec. It's defined now to
|
|
// make refactoring simpler in the future.
|
|
type AnnotatedAddr struct {
|
|
ma.Multiaddr
|
|
}
|
|
|
|
// RoutingState contains a snapshot of public, transient state (e.g. addresses, supported protocols)
|
|
// for a peer at a given point in time, where "time" is defined by the sequence counter
|
|
// field Seq. Greater Seq values are later in time than lesser values, but there are no
|
|
// guarantees about the wall-clock time between any two Seq values.
|
|
//
|
|
// Note that Seq values are peer-specific and can only be compared for records with equal PeerIDs.
|
|
type RoutingState struct {
|
|
// PeerID is the ID of the peer this record pertains to.
|
|
PeerID peer.ID
|
|
|
|
// Seq is an increment-only sequence counter used to order RoutingState records in time.
|
|
Seq uint64
|
|
|
|
// Addresses contains the public addresses of the peer this record pertains to.
|
|
Addresses []*AnnotatedAddr
|
|
}
|
|
|
|
func RoutingStateFromAddrInfo(info *peer.AddrInfo) *RoutingState {
|
|
annotated := make([]*AnnotatedAddr, len(info.Addrs))
|
|
for i, a := range info.Addrs {
|
|
annotated[i] = &AnnotatedAddr{Multiaddr: a}
|
|
}
|
|
return &RoutingState{
|
|
PeerID: info.ID,
|
|
Seq: statelessSeqNo(),
|
|
Addresses: annotated,
|
|
}
|
|
}
|
|
|
|
// UnmarshalRoutingState unpacks a peer RoutingState record from a serialized protobuf representation.
|
|
func UnmarshalRoutingState(serialized []byte) (*RoutingState, error) {
|
|
msg := pb.RoutingStateRecord{}
|
|
err := proto.Unmarshal(serialized, &msg)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
id, err := peer.IDFromBytes(msg.PeerId)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &RoutingState{
|
|
PeerID: id,
|
|
Seq: msg.Seq,
|
|
Addresses: addrsFromProtobuf(msg.Addresses),
|
|
}, nil
|
|
}
|
|
|
|
// RoutingStateFromEnvelope unwraps a peer RoutingState record from a SignedEnvelope.
|
|
// This method will fail if the signature is invalid, or if the record
|
|
// belongs to a peer other than the one that signed the envelope.
|
|
func RoutingStateFromEnvelope(envelope *crypto.SignedEnvelope) (*RoutingState, error) {
|
|
msgBytes, err := envelope.Open(StateEnvelopeDomain)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
state, err := UnmarshalRoutingState(msgBytes)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if !state.PeerID.MatchesPublicKey(envelope.PublicKey) {
|
|
return nil, errors.New("peer id in routing state record does not match signing key")
|
|
}
|
|
return state, nil
|
|
}
|
|
|
|
// ToSignedEnvelope wraps a Marshal'd RoutingState record in a SignedEnvelope using the
|
|
// given private signing key.
|
|
func (s *RoutingState) ToSignedEnvelope(key crypto.PrivKey) (*crypto.SignedEnvelope, error) {
|
|
payload, err := s.Marshal()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return crypto.MakeEnvelope(key, StateEnvelopeDomain, StateEnvelopeTypeHint, payload)
|
|
}
|
|
|
|
// Marshal serializes a RoutingState record to protobuf and returns its byte representation.
|
|
func (s *RoutingState) Marshal() ([]byte, error) {
|
|
id, err := s.PeerID.MarshalBinary()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
msg := pb.RoutingStateRecord{
|
|
PeerId: id,
|
|
Seq: s.Seq,
|
|
Addresses: addrsToProtobuf(s.Addresses),
|
|
}
|
|
return proto.Marshal(&msg)
|
|
}
|
|
|
|
// Multiaddrs returns the addresses from a RoutingState record without any metadata annotations.
|
|
func (s *RoutingState) Multiaddrs() []ma.Multiaddr {
|
|
out := make([]ma.Multiaddr, len(s.Addresses))
|
|
for i, addr := range s.Addresses {
|
|
out[i] = addr.Multiaddr
|
|
}
|
|
return out
|
|
}
|
|
|
|
func statelessSeqNo() uint64 {
|
|
// use current time in milliseconds as seq number
|
|
// nanoseconds would overflow in 2262, but millis gives us
|
|
// a few hundred thousand years
|
|
return uint64(time.Now().UnixNano() / 1e6)
|
|
}
|
|
|
|
func addrsFromProtobuf(addrs []*pb.RoutingStateRecord_AddressInfo) []*AnnotatedAddr {
|
|
out := make([]*AnnotatedAddr, 0)
|
|
for _, addr := range addrs {
|
|
a, err := ma.NewMultiaddrBytes(addr.Multiaddr)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
out = append(out, &AnnotatedAddr{Multiaddr: a})
|
|
}
|
|
return out
|
|
}
|
|
|
|
func addrsToProtobuf(addrs []*AnnotatedAddr) []*pb.RoutingStateRecord_AddressInfo {
|
|
out := make([]*pb.RoutingStateRecord_AddressInfo, 0)
|
|
for _, addr := range addrs {
|
|
out = append(out, &pb.RoutingStateRecord_AddressInfo{Multiaddr: addr.Bytes()})
|
|
}
|
|
return out
|
|
} |