feat: Waku v2 bridge

Issue #12610
This commit is contained in:
Michal Iskierko
2023-11-12 13:29:38 +01:00
parent 56e7bd01ca
commit 6d31343205
6716 changed files with 1982502 additions and 5891 deletions

View File

@@ -0,0 +1,57 @@
package canonicallog
import (
"fmt"
"math/rand"
"net"
"strings"
"github.com/libp2p/go-libp2p/core/peer"
logging "github.com/ipfs/go-log/v2"
"github.com/multiformats/go-multiaddr"
manet "github.com/multiformats/go-multiaddr/net"
)
var log = logging.WithSkip(logging.Logger("canonical-log"), 1)
// LogMisbehavingPeer is the canonical way to log a misbehaving peer.
// Protocols should use this to identify a misbehaving peer to allow the end
// user to easily identify these nodes across protocols and libp2p.
func LogMisbehavingPeer(p peer.ID, peerAddr multiaddr.Multiaddr, component string, err error, msg string) {
log.Warnf("CANONICAL_MISBEHAVING_PEER: peer=%s addr=%s component=%s err=%q msg=%q", p, peerAddr.String(), component, err, msg)
}
// LogMisbehavingPeerNetAddr is the canonical way to log a misbehaving peer.
// Protocols should use this to identify a misbehaving peer to allow the end
// user to easily identify these nodes across protocols and libp2p.
func LogMisbehavingPeerNetAddr(p peer.ID, peerAddr net.Addr, component string, originalErr error, msg string) {
ma, err := manet.FromNetAddr(peerAddr)
if err != nil {
log.Warnf("CANONICAL_MISBEHAVING_PEER: peer=%s net_addr=%s component=%s err=%q msg=%q", p, peerAddr.String(), component, originalErr, msg)
return
}
LogMisbehavingPeer(p, ma, component, originalErr, msg)
}
// LogPeerStatus logs any useful information about a peer. It takes in a sample
// rate and will only log one in every sampleRate messages (randomly). This is
// useful in surfacing events that are normal in isolation, but may be abnormal
// in large quantities. For example, a successful connection from an IP address
// is normal. 10,000 connections from that same IP address is not normal. libp2p
// itself does nothing besides emitting this log. Hook this up to another tool
// like fail2ban to action on the log.
func LogPeerStatus(sampleRate int, p peer.ID, peerAddr multiaddr.Multiaddr, keyVals ...string) {
if rand.Intn(sampleRate) == 0 {
keyValsStr := strings.Builder{}
for i, kOrV := range keyVals {
if i%2 == 0 {
fmt.Fprintf(&keyValsStr, " %v=", kOrV)
} else {
fmt.Fprintf(&keyValsStr, "%q", kOrV)
}
}
log.Infof("CANONICAL_PEER_STATUS: peer=%s addr=%s sample_rate=%v%s", p, peerAddr.String(), sampleRate, keyValsStr.String())
}
}

View File

@@ -0,0 +1,109 @@
package connmgr
import (
"io"
"time"
"github.com/libp2p/go-libp2p/core/peer"
)
// Decayer is implemented by connection managers supporting decaying tags. A
// decaying tag is one whose value automatically decays over time.
//
// The actual application of the decay behaviour is encapsulated in a
// user-provided decaying function (DecayFn). The function is called on every
// tick (determined by the interval parameter), and returns either the new value
// of the tag, or whether it should be erased altogether.
//
// We do not set values on a decaying tag. Rather, we "bump" decaying tags by a
// delta. This calls the BumpFn with the old value and the delta, to determine
// the new value.
//
// Such a pluggable design affords a great deal of flexibility and versatility.
// Behaviours that are straightforward to implement include:
//
// - Decay a tag by -1, or by half its current value, on every tick.
// - Every time a value is bumped, sum it to its current value.
// - Exponentially boost a score with every bump.
// - Sum the incoming score, but keep it within min, max bounds.
//
// Commonly used DecayFns and BumpFns are provided in this package.
type Decayer interface {
io.Closer
// RegisterDecayingTag creates and registers a new decaying tag, if and only
// if a tag with the supplied name doesn't exist yet. Otherwise, an error is
// returned.
//
// The caller provides the interval at which the tag is refreshed, as well
// as the decay function and the bump function. Refer to godocs on DecayFn
// and BumpFn for more info.
RegisterDecayingTag(name string, interval time.Duration, decayFn DecayFn, bumpFn BumpFn) (DecayingTag, error)
}
// DecayFn applies a decay to the peer's score. The implementation must call
// DecayFn at the interval supplied when registering the tag.
//
// It receives a copy of the decaying value, and returns the score after
// applying the decay, as well as a flag to signal if the tag should be erased.
type DecayFn func(value DecayingValue) (after int, rm bool)
// BumpFn applies a delta onto an existing score, and returns the new score.
//
// Non-trivial bump functions include exponential boosting, moving averages,
// ceilings, etc.
type BumpFn func(value DecayingValue, delta int) (after int)
// DecayingTag represents a decaying tag. The tag is a long-lived general
// object, used to operate on tag values for peers.
type DecayingTag interface {
// Name returns the name of the tag.
Name() string
// Interval is the effective interval at which this tag will tick. Upon
// registration, the desired interval may be overwritten depending on the
// decayer's resolution, and this method allows you to obtain the effective
// interval.
Interval() time.Duration
// Bump applies a delta to a tag value, calling its bump function. The bump
// will be applied asynchronously, and a non-nil error indicates a fault
// when queuing.
Bump(peer peer.ID, delta int) error
// Remove removes a decaying tag from a peer. The removal will be applied
// asynchronously, and a non-nil error indicates a fault when queuing.
Remove(peer peer.ID) error
// Close closes a decaying tag. The Decayer will stop tracking this tag,
// and the state of all peers in the Connection Manager holding this tag
// will be updated.
//
// The deletion is performed asynchronously.
//
// Once deleted, a tag should not be used, and further calls to Bump/Remove
// will error.
//
// Duplicate calls to Remove will not return errors, but a failure to queue
// the first actual removal, will (e.g. when the system is backlogged).
Close() error
}
// DecayingValue represents a value for a decaying tag.
type DecayingValue struct {
// Tag points to the tag this value belongs to.
Tag DecayingTag
// Peer is the peer ID to whom this value is associated.
Peer peer.ID
// Added is the timestamp when this value was added for the first time for
// a tag and a peer.
Added time.Time
// LastVisit is the timestamp of the last visit.
LastVisit time.Time
// Value is the current value of the tag.
Value int
}

View File

@@ -0,0 +1,89 @@
package connmgr
import (
ma "github.com/multiformats/go-multiaddr"
"github.com/libp2p/go-libp2p/core/control"
"github.com/libp2p/go-libp2p/core/network"
"github.com/libp2p/go-libp2p/core/peer"
)
// ConnectionGater can be implemented by a type that supports active
// inbound or outbound connection gating.
//
// ConnectionGaters are active, whereas ConnManagers tend to be passive.
//
// A ConnectionGater will be consulted during different states in the lifecycle
// of a connection being established/upgraded. Specific functions will be called
// throughout the process, to allow you to intercept the connection at that stage.
//
// InterceptPeerDial is called on an imminent outbound peer dial request, prior
// to the addresses of that peer being available/resolved. Blocking connections
// at this stage is typical for blacklisting scenarios.
//
// InterceptAddrDial is called on an imminent outbound dial to a peer on a
// particular address. Blocking connections at this stage is typical for
// address filtering.
//
// InterceptAccept is called as soon as a transport listener receives an
// inbound connection request, before any upgrade takes place. Transports who
// accept already secure and/or multiplexed connections (e.g. possibly QUIC)
// MUST call this method regardless, for correctness/consistency.
//
// InterceptSecured is called for both inbound and outbound connections,
// after a security handshake has taken place and we've authenticated the peer.
//
// InterceptUpgraded is called for inbound and outbound connections, after
// libp2p has finished upgrading the connection entirely to a secure,
// multiplexed channel.
//
// This interface can be used to implement *strict/active* connection management
// policies, such as hard limiting of connections once a maximum count has been
// reached, maintaining a peer blacklist, or limiting connections by transport
// quotas.
//
// EXPERIMENTAL: a DISCONNECT protocol/message will be supported in the future.
// This allows gaters and other components to communicate the intention behind
// a connection closure, to curtail potential reconnection attempts.
//
// For now, InterceptUpgraded can return a non-zero DisconnectReason when
// blocking a connection, but this interface is likely to change in the future
// as we solidify this feature. The reason why only this method can handle
// DisconnectReasons is that we require stream multiplexing capability to open a
// control protocol stream to transmit the message.
type ConnectionGater interface {
// InterceptPeerDial tests whether we're permitted to Dial the specified peer.
//
// This is called by the network.Network implementation when dialling a peer.
InterceptPeerDial(p peer.ID) (allow bool)
// InterceptAddrDial tests whether we're permitted to dial the specified
// multiaddr for the given peer.
//
// This is called by the network.Network implementation after it has
// resolved the peer's addrs, and prior to dialling each.
InterceptAddrDial(peer.ID, ma.Multiaddr) (allow bool)
// InterceptAccept tests whether an incipient inbound connection is allowed.
//
// This is called by the upgrader, or by the transport directly (e.g. QUIC,
// Bluetooth), straight after it has accepted a connection from its socket.
InterceptAccept(network.ConnMultiaddrs) (allow bool)
// InterceptSecured tests whether a given connection, now authenticated,
// is allowed.
//
// This is called by the upgrader, after it has performed the security
// handshake, and before it negotiates the muxer, or by the directly by the
// transport, at the exact same checkpoint.
InterceptSecured(network.Direction, peer.ID, network.ConnMultiaddrs) (allow bool)
// InterceptUpgraded tests whether a fully capable connection is allowed.
//
// At this point, the connection a multiplexer has been selected.
// When rejecting a connection, the gater can return a DisconnectReason.
// Refer to the godoc on the ConnectionGater type for more information.
//
// NOTE: the go-libp2p implementation currently IGNORES the disconnect reason.
InterceptUpgraded(network.Conn) (allow bool, reason control.DisconnectReason)
}

View File

@@ -0,0 +1,91 @@
// Package connmgr provides connection tracking and management interfaces for libp2p.
//
// The ConnManager interface exported from this package allows libp2p to enforce an
// upper bound on the total number of open connections. To avoid service disruptions,
// connections can be tagged with metadata and optionally "protected" to ensure that
// essential connections are not arbitrarily cut.
package connmgr
import (
"context"
"time"
"github.com/libp2p/go-libp2p/core/network"
"github.com/libp2p/go-libp2p/core/peer"
)
// SupportsDecay evaluates if the provided ConnManager supports decay, and if
// so, it returns the Decayer object. Refer to godocs on Decayer for more info.
func SupportsDecay(mgr ConnManager) (Decayer, bool) {
d, ok := mgr.(Decayer)
return d, ok
}
// ConnManager tracks connections to peers, and allows consumers to associate
// metadata with each peer.
//
// It enables connections to be trimmed based on implementation-defined
// heuristics. The ConnManager allows libp2p to enforce an upper bound on the
// total number of open connections.
//
// ConnManagers supporting decaying tags implement Decayer. Use the
// SupportsDecay function to safely cast an instance to Decayer, if supported.
type ConnManager interface {
// TagPeer tags a peer with a string, associating a weight with the tag.
TagPeer(peer.ID, string, int)
// Untag removes the tagged value from the peer.
UntagPeer(p peer.ID, tag string)
// UpsertTag updates an existing tag or inserts a new one.
//
// The connection manager calls the upsert function supplying the current
// value of the tag (or zero if inexistent). The return value is used as
// the new value of the tag.
UpsertTag(p peer.ID, tag string, upsert func(int) int)
// GetTagInfo returns the metadata associated with the peer,
// or nil if no metadata has been recorded for the peer.
GetTagInfo(p peer.ID) *TagInfo
// TrimOpenConns terminates open connections based on an implementation-defined
// heuristic.
TrimOpenConns(ctx context.Context)
// Notifee returns an implementation that can be called back to inform of
// opened and closed connections.
Notifee() network.Notifiee
// Protect protects a peer from having its connection(s) pruned.
//
// Tagging allows different parts of the system to manage protections without interfering with one another.
//
// Calls to Protect() with the same tag are idempotent. They are not refcounted, so after multiple calls
// to Protect() with the same tag, a single Unprotect() call bearing the same tag will revoke the protection.
Protect(id peer.ID, tag string)
// Unprotect removes a protection that may have been placed on a peer, under the specified tag.
//
// The return value indicates whether the peer continues to be protected after this call, by way of a different tag.
// See notes on Protect() for more info.
Unprotect(id peer.ID, tag string) (protected bool)
// IsProtected returns true if the peer is protected for some tag; if the tag is the empty string
// then it will return true if the peer is protected for any tag
IsProtected(id peer.ID, tag string) (protected bool)
// Close closes the connection manager and stops background processes.
Close() error
}
// TagInfo stores metadata associated with a peer.
type TagInfo struct {
FirstSeen time.Time
Value int
// Tags maps tag ids to the numerical values.
Tags map[string]int
// Conns maps connection ids (such as remote multiaddr) to their creation time.
Conns map[string]time.Time
}

View File

@@ -0,0 +1,24 @@
package connmgr
import (
"context"
"github.com/libp2p/go-libp2p/core/network"
"github.com/libp2p/go-libp2p/core/peer"
)
// NullConnMgr is a ConnMgr that provides no functionality.
type NullConnMgr struct{}
var _ ConnManager = (*NullConnMgr)(nil)
func (NullConnMgr) TagPeer(peer.ID, string, int) {}
func (NullConnMgr) UntagPeer(peer.ID, string) {}
func (NullConnMgr) UpsertTag(peer.ID, string, func(int) int) {}
func (NullConnMgr) GetTagInfo(peer.ID) *TagInfo { return &TagInfo{} }
func (NullConnMgr) TrimOpenConns(ctx context.Context) {}
func (NullConnMgr) Notifee() network.Notifiee { return network.GlobalNoopNotifiee }
func (NullConnMgr) Protect(peer.ID, string) {}
func (NullConnMgr) Unprotect(peer.ID, string) bool { return false }
func (NullConnMgr) IsProtected(peer.ID, string) bool { return false }
func (NullConnMgr) Close() error { return nil }

View File

@@ -0,0 +1,67 @@
package connmgr
import (
"math"
"time"
)
// DecayNone applies no decay.
func DecayNone() DecayFn {
return func(value DecayingValue) (_ int, rm bool) {
return value.Value, false
}
}
// DecayFixed subtracts from by the provided minuend, and deletes the tag when
// first reaching 0 or negative.
func DecayFixed(minuend int) DecayFn {
return func(value DecayingValue) (_ int, rm bool) {
v := value.Value - minuend
return v, v <= 0
}
}
// DecayLinear applies a fractional coefficient to the value of the current tag,
// rounding down via math.Floor. It erases the tag when the result is zero.
func DecayLinear(coef float64) DecayFn {
return func(value DecayingValue) (after int, rm bool) {
v := math.Floor(float64(value.Value) * coef)
return int(v), v <= 0
}
}
// DecayExpireWhenInactive expires a tag after a certain period of no bumps.
func DecayExpireWhenInactive(after time.Duration) DecayFn {
return func(value DecayingValue) (_ int, rm bool) {
rm = time.Until(value.LastVisit) >= after
return 0, rm
}
}
// BumpSumUnbounded adds the incoming value to the peer's score.
func BumpSumUnbounded() BumpFn {
return func(value DecayingValue, delta int) (after int) {
return value.Value + delta
}
}
// BumpSumBounded keeps summing the incoming score, keeping it within a
// [min, max] range.
func BumpSumBounded(min, max int) BumpFn {
return func(value DecayingValue, delta int) (after int) {
v := value.Value + delta
if v >= max {
return max
} else if v <= min {
return min
}
return v
}
}
// BumpOverwrite replaces the current value of the tag with the incoming one.
func BumpOverwrite() BumpFn {
return func(value DecayingValue, delta int) (after int) {
return delta
}
}

View File

@@ -0,0 +1,9 @@
package control
// DisconnectReason communicates the reason why a connection is being closed.
//
// A zero value stands for "no reason" / NA.
//
// This is an EXPERIMENTAL type. It will change in the future. Refer to the
// connmgr.ConnectionGater godoc for more info.
type DisconnectReason int

187
vendor/github.com/libp2p/go-libp2p/core/crypto/ecdsa.go generated vendored Normal file
View File

@@ -0,0 +1,187 @@
package crypto
import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/x509"
"encoding/asn1"
"errors"
"io"
"math/big"
pb "github.com/libp2p/go-libp2p/core/crypto/pb"
"github.com/libp2p/go-libp2p/core/internal/catch"
"github.com/minio/sha256-simd"
)
// ECDSAPrivateKey is an implementation of an ECDSA private key
type ECDSAPrivateKey struct {
priv *ecdsa.PrivateKey
}
// ECDSAPublicKey is an implementation of an ECDSA public key
type ECDSAPublicKey struct {
pub *ecdsa.PublicKey
}
// ECDSASig holds the r and s values of an ECDSA signature
type ECDSASig struct {
R, S *big.Int
}
var (
// ErrNotECDSAPubKey is returned when the public key passed is not an ecdsa public key
ErrNotECDSAPubKey = errors.New("not an ecdsa public key")
// ErrNilSig is returned when the signature is nil
ErrNilSig = errors.New("sig is nil")
// ErrNilPrivateKey is returned when a nil private key is provided
ErrNilPrivateKey = errors.New("private key is nil")
// ErrNilPublicKey is returned when a nil public key is provided
ErrNilPublicKey = errors.New("public key is nil")
// ECDSACurve is the default ecdsa curve used
ECDSACurve = elliptic.P256()
)
// GenerateECDSAKeyPair generates a new ecdsa private and public key
func GenerateECDSAKeyPair(src io.Reader) (PrivKey, PubKey, error) {
return GenerateECDSAKeyPairWithCurve(ECDSACurve, src)
}
// GenerateECDSAKeyPairWithCurve generates a new ecdsa private and public key with a specified curve
func GenerateECDSAKeyPairWithCurve(curve elliptic.Curve, src io.Reader) (PrivKey, PubKey, error) {
priv, err := ecdsa.GenerateKey(curve, src)
if err != nil {
return nil, nil, err
}
return &ECDSAPrivateKey{priv}, &ECDSAPublicKey{&priv.PublicKey}, nil
}
// ECDSAKeyPairFromKey generates a new ecdsa private and public key from an input private key
func ECDSAKeyPairFromKey(priv *ecdsa.PrivateKey) (PrivKey, PubKey, error) {
if priv == nil {
return nil, nil, ErrNilPrivateKey
}
return &ECDSAPrivateKey{priv}, &ECDSAPublicKey{&priv.PublicKey}, nil
}
// ECDSAPublicKeyFromPubKey generates a new ecdsa public key from an input public key
func ECDSAPublicKeyFromPubKey(pub ecdsa.PublicKey) (PubKey, error) {
return &ECDSAPublicKey{pub: &pub}, nil
}
// MarshalECDSAPrivateKey returns x509 bytes from a private key
func MarshalECDSAPrivateKey(ePriv ECDSAPrivateKey) (res []byte, err error) {
defer func() { catch.HandlePanic(recover(), &err, "ECDSA private-key marshal") }()
return x509.MarshalECPrivateKey(ePriv.priv)
}
// MarshalECDSAPublicKey returns x509 bytes from a public key
func MarshalECDSAPublicKey(ePub ECDSAPublicKey) (res []byte, err error) {
defer func() { catch.HandlePanic(recover(), &err, "ECDSA public-key marshal") }()
return x509.MarshalPKIXPublicKey(ePub.pub)
}
// UnmarshalECDSAPrivateKey returns a private key from x509 bytes
func UnmarshalECDSAPrivateKey(data []byte) (res PrivKey, err error) {
defer func() { catch.HandlePanic(recover(), &err, "ECDSA private-key unmarshal") }()
priv, err := x509.ParseECPrivateKey(data)
if err != nil {
return nil, err
}
return &ECDSAPrivateKey{priv}, nil
}
// UnmarshalECDSAPublicKey returns the public key from x509 bytes
func UnmarshalECDSAPublicKey(data []byte) (key PubKey, err error) {
defer func() { catch.HandlePanic(recover(), &err, "ECDSA public-key unmarshal") }()
pubIfc, err := x509.ParsePKIXPublicKey(data)
if err != nil {
return nil, err
}
pub, ok := pubIfc.(*ecdsa.PublicKey)
if !ok {
return nil, ErrNotECDSAPubKey
}
return &ECDSAPublicKey{pub}, nil
}
// Type returns the key type
func (ePriv *ECDSAPrivateKey) Type() pb.KeyType {
return pb.KeyType_ECDSA
}
// Raw returns x509 bytes from a private key
func (ePriv *ECDSAPrivateKey) Raw() (res []byte, err error) {
defer func() { catch.HandlePanic(recover(), &err, "ECDSA private-key marshal") }()
return x509.MarshalECPrivateKey(ePriv.priv)
}
// Equals compares two private keys
func (ePriv *ECDSAPrivateKey) Equals(o Key) bool {
return basicEquals(ePriv, o)
}
// Sign returns the signature of the input data
func (ePriv *ECDSAPrivateKey) Sign(data []byte) (sig []byte, err error) {
defer func() { catch.HandlePanic(recover(), &err, "ECDSA signing") }()
hash := sha256.Sum256(data)
r, s, err := ecdsa.Sign(rand.Reader, ePriv.priv, hash[:])
if err != nil {
return nil, err
}
return asn1.Marshal(ECDSASig{
R: r,
S: s,
})
}
// GetPublic returns a public key
func (ePriv *ECDSAPrivateKey) GetPublic() PubKey {
return &ECDSAPublicKey{&ePriv.priv.PublicKey}
}
// Type returns the key type
func (ePub *ECDSAPublicKey) Type() pb.KeyType {
return pb.KeyType_ECDSA
}
// Raw returns x509 bytes from a public key
func (ePub *ECDSAPublicKey) Raw() ([]byte, error) {
return x509.MarshalPKIXPublicKey(ePub.pub)
}
// Equals compares to public keys
func (ePub *ECDSAPublicKey) Equals(o Key) bool {
return basicEquals(ePub, o)
}
// Verify compares data to a signature
func (ePub *ECDSAPublicKey) Verify(data, sigBytes []byte) (success bool, err error) {
defer func() {
catch.HandlePanic(recover(), &err, "ECDSA signature verification")
// Just to be extra paranoid.
if err != nil {
success = false
}
}()
sig := new(ECDSASig)
if _, err := asn1.Unmarshal(sigBytes, sig); err != nil {
return false, err
}
hash := sha256.Sum256(data)
return ecdsa.Verify(ePub.pub, hash[:], sig.R, sig.S), nil
}

View File

@@ -0,0 +1,156 @@
package crypto
import (
"bytes"
"crypto/ed25519"
"crypto/subtle"
"errors"
"fmt"
"io"
pb "github.com/libp2p/go-libp2p/core/crypto/pb"
"github.com/libp2p/go-libp2p/core/internal/catch"
)
// Ed25519PrivateKey is an ed25519 private key.
type Ed25519PrivateKey struct {
k ed25519.PrivateKey
}
// Ed25519PublicKey is an ed25519 public key.
type Ed25519PublicKey struct {
k ed25519.PublicKey
}
// GenerateEd25519Key generates a new ed25519 private and public key pair.
func GenerateEd25519Key(src io.Reader) (PrivKey, PubKey, error) {
pub, priv, err := ed25519.GenerateKey(src)
if err != nil {
return nil, nil, err
}
return &Ed25519PrivateKey{
k: priv,
},
&Ed25519PublicKey{
k: pub,
},
nil
}
// Type of the private key (Ed25519).
func (k *Ed25519PrivateKey) Type() pb.KeyType {
return pb.KeyType_Ed25519
}
// Raw private key bytes.
func (k *Ed25519PrivateKey) Raw() ([]byte, error) {
// The Ed25519 private key contains two 32-bytes curve points, the private
// key and the public key.
// It makes it more efficient to get the public key without re-computing an
// elliptic curve multiplication.
buf := make([]byte, len(k.k))
copy(buf, k.k)
return buf, nil
}
func (k *Ed25519PrivateKey) pubKeyBytes() []byte {
return k.k[ed25519.PrivateKeySize-ed25519.PublicKeySize:]
}
// Equals compares two ed25519 private keys.
func (k *Ed25519PrivateKey) Equals(o Key) bool {
edk, ok := o.(*Ed25519PrivateKey)
if !ok {
return basicEquals(k, o)
}
return subtle.ConstantTimeCompare(k.k, edk.k) == 1
}
// GetPublic returns an ed25519 public key from a private key.
func (k *Ed25519PrivateKey) GetPublic() PubKey {
return &Ed25519PublicKey{k: k.pubKeyBytes()}
}
// Sign returns a signature from an input message.
func (k *Ed25519PrivateKey) Sign(msg []byte) (res []byte, err error) {
defer func() { catch.HandlePanic(recover(), &err, "ed15519 signing") }()
return ed25519.Sign(k.k, msg), nil
}
// Type of the public key (Ed25519).
func (k *Ed25519PublicKey) Type() pb.KeyType {
return pb.KeyType_Ed25519
}
// Raw public key bytes.
func (k *Ed25519PublicKey) Raw() ([]byte, error) {
return k.k, nil
}
// Equals compares two ed25519 public keys.
func (k *Ed25519PublicKey) Equals(o Key) bool {
edk, ok := o.(*Ed25519PublicKey)
if !ok {
return basicEquals(k, o)
}
return bytes.Equal(k.k, edk.k)
}
// Verify checks a signature against the input data.
func (k *Ed25519PublicKey) Verify(data []byte, sig []byte) (success bool, err error) {
defer func() {
catch.HandlePanic(recover(), &err, "ed15519 signature verification")
// To be safe.
if err != nil {
success = false
}
}()
return ed25519.Verify(k.k, data, sig), nil
}
// UnmarshalEd25519PublicKey returns a public key from input bytes.
func UnmarshalEd25519PublicKey(data []byte) (PubKey, error) {
if len(data) != 32 {
return nil, errors.New("expect ed25519 public key data size to be 32")
}
return &Ed25519PublicKey{
k: ed25519.PublicKey(data),
}, nil
}
// UnmarshalEd25519PrivateKey returns a private key from input bytes.
func UnmarshalEd25519PrivateKey(data []byte) (PrivKey, error) {
switch len(data) {
case ed25519.PrivateKeySize + ed25519.PublicKeySize:
// Remove the redundant public key. See issue #36.
redundantPk := data[ed25519.PrivateKeySize:]
pk := data[ed25519.PrivateKeySize-ed25519.PublicKeySize : ed25519.PrivateKeySize]
if subtle.ConstantTimeCompare(pk, redundantPk) == 0 {
return nil, errors.New("expected redundant ed25519 public key to be redundant")
}
// No point in storing the extra data.
newKey := make([]byte, ed25519.PrivateKeySize)
copy(newKey, data[:ed25519.PrivateKeySize])
data = newKey
case ed25519.PrivateKeySize:
default:
return nil, fmt.Errorf(
"expected ed25519 data size to be %d or %d, got %d",
ed25519.PrivateKeySize,
ed25519.PrivateKeySize+ed25519.PublicKeySize,
len(data),
)
}
return &Ed25519PrivateKey{
k: ed25519.PrivateKey(data),
}, nil
}

291
vendor/github.com/libp2p/go-libp2p/core/crypto/key.go generated vendored Normal file
View File

@@ -0,0 +1,291 @@
// Package crypto implements various cryptographic utilities used by libp2p.
// This includes a Public and Private key interface and key implementations
// for supported key algorithms.
package crypto
import (
"crypto/elliptic"
"crypto/rand"
"crypto/subtle"
"encoding/base64"
"errors"
"fmt"
"io"
"github.com/libp2p/go-libp2p/core/crypto/pb"
"google.golang.org/protobuf/proto"
)
//go:generate protoc --go_out=. --go_opt=Mpb/crypto.proto=./pb pb/crypto.proto
const (
// RSA is an enum for the supported RSA key type
RSA = iota
// Ed25519 is an enum for the supported Ed25519 key type
Ed25519
// Secp256k1 is an enum for the supported Secp256k1 key type
Secp256k1
// ECDSA is an enum for the supported ECDSA key type
ECDSA
)
var (
// ErrBadKeyType is returned when a key is not supported
ErrBadKeyType = errors.New("invalid or unsupported key type")
// KeyTypes is a list of supported keys
KeyTypes = []int{
RSA,
Ed25519,
Secp256k1,
ECDSA,
}
)
// PubKeyUnmarshaller is a func that creates a PubKey from a given slice of bytes
type PubKeyUnmarshaller func(data []byte) (PubKey, error)
// PrivKeyUnmarshaller is a func that creates a PrivKey from a given slice of bytes
type PrivKeyUnmarshaller func(data []byte) (PrivKey, error)
// PubKeyUnmarshallers is a map of unmarshallers by key type
var PubKeyUnmarshallers = map[pb.KeyType]PubKeyUnmarshaller{
pb.KeyType_RSA: UnmarshalRsaPublicKey,
pb.KeyType_Ed25519: UnmarshalEd25519PublicKey,
pb.KeyType_Secp256k1: UnmarshalSecp256k1PublicKey,
pb.KeyType_ECDSA: UnmarshalECDSAPublicKey,
}
// PrivKeyUnmarshallers is a map of unmarshallers by key type
var PrivKeyUnmarshallers = map[pb.KeyType]PrivKeyUnmarshaller{
pb.KeyType_RSA: UnmarshalRsaPrivateKey,
pb.KeyType_Ed25519: UnmarshalEd25519PrivateKey,
pb.KeyType_Secp256k1: UnmarshalSecp256k1PrivateKey,
pb.KeyType_ECDSA: UnmarshalECDSAPrivateKey,
}
// Key represents a crypto key that can be compared to another key
type Key interface {
// Equals checks whether two PubKeys are the same
Equals(Key) bool
// Raw returns the raw bytes of the key (not wrapped in the
// libp2p-crypto protobuf).
//
// This function is the inverse of {Priv,Pub}KeyUnmarshaler.
Raw() ([]byte, error)
// Type returns the protobuf key type.
Type() pb.KeyType
}
// PrivKey represents a private key that can be used to generate a public key and sign data
type PrivKey interface {
Key
// Cryptographically sign the given bytes
Sign([]byte) ([]byte, error)
// Return a public key paired with this private key
GetPublic() PubKey
}
// PubKey is a public key that can be used to verify data signed with the corresponding private key
type PubKey interface {
Key
// Verify that 'sig' is the signed hash of 'data'
Verify(data []byte, sig []byte) (bool, error)
}
// GenSharedKey generates the shared key from a given private key
type GenSharedKey func([]byte) ([]byte, error)
// GenerateKeyPair generates a private and public key
func GenerateKeyPair(typ, bits int) (PrivKey, PubKey, error) {
return GenerateKeyPairWithReader(typ, bits, rand.Reader)
}
// GenerateKeyPairWithReader returns a keypair of the given type and bit-size
func GenerateKeyPairWithReader(typ, bits int, src io.Reader) (PrivKey, PubKey, error) {
switch typ {
case RSA:
return GenerateRSAKeyPair(bits, src)
case Ed25519:
return GenerateEd25519Key(src)
case Secp256k1:
return GenerateSecp256k1Key(src)
case ECDSA:
return GenerateECDSAKeyPair(src)
default:
return nil, nil, ErrBadKeyType
}
}
// GenerateEKeyPair returns an ephemeral public key and returns a function that will compute
// the shared secret key. Used in the identify module.
//
// Focuses only on ECDH now, but can be made more general in the future.
func GenerateEKeyPair(curveName string) ([]byte, GenSharedKey, error) {
var curve elliptic.Curve
switch curveName {
case "P-256":
curve = elliptic.P256()
case "P-384":
curve = elliptic.P384()
case "P-521":
curve = elliptic.P521()
default:
return nil, nil, fmt.Errorf("unknown curve name")
}
priv, x, y, err := elliptic.GenerateKey(curve, rand.Reader)
if err != nil {
return nil, nil, err
}
pubKey := elliptic.Marshal(curve, x, y)
done := func(theirPub []byte) ([]byte, error) {
// Verify and unpack node's public key.
x, y := elliptic.Unmarshal(curve, theirPub)
if x == nil {
return nil, fmt.Errorf("malformed public key: %d %v", len(theirPub), theirPub)
}
if !curve.IsOnCurve(x, y) {
return nil, errors.New("invalid public key")
}
// Generate shared secret.
secret, _ := curve.ScalarMult(x, y, priv)
return secret.Bytes(), nil
}
return pubKey, done, nil
}
// UnmarshalPublicKey converts a protobuf serialized public key into its
// representative object
func UnmarshalPublicKey(data []byte) (PubKey, error) {
pmes := new(pb.PublicKey)
err := proto.Unmarshal(data, pmes)
if err != nil {
return nil, err
}
return PublicKeyFromProto(pmes)
}
// PublicKeyFromProto converts an unserialized protobuf PublicKey message
// into its representative object.
func PublicKeyFromProto(pmes *pb.PublicKey) (PubKey, error) {
um, ok := PubKeyUnmarshallers[pmes.GetType()]
if !ok {
return nil, ErrBadKeyType
}
data := pmes.GetData()
pk, err := um(data)
if err != nil {
return nil, err
}
switch tpk := pk.(type) {
case *RsaPublicKey:
tpk.cached, _ = proto.Marshal(pmes)
}
return pk, nil
}
// MarshalPublicKey converts a public key object into a protobuf serialized
// public key
func MarshalPublicKey(k PubKey) ([]byte, error) {
pbmes, err := PublicKeyToProto(k)
if err != nil {
return nil, err
}
return proto.Marshal(pbmes)
}
// PublicKeyToProto converts a public key object into an unserialized
// protobuf PublicKey message.
func PublicKeyToProto(k PubKey) (*pb.PublicKey, error) {
data, err := k.Raw()
if err != nil {
return nil, err
}
return &pb.PublicKey{
Type: k.Type().Enum(),
Data: data,
}, nil
}
// UnmarshalPrivateKey converts a protobuf serialized private key into its
// representative object
func UnmarshalPrivateKey(data []byte) (PrivKey, error) {
pmes := new(pb.PrivateKey)
err := proto.Unmarshal(data, pmes)
if err != nil {
return nil, err
}
um, ok := PrivKeyUnmarshallers[pmes.GetType()]
if !ok {
return nil, ErrBadKeyType
}
return um(pmes.GetData())
}
// MarshalPrivateKey converts a key object into its protobuf serialized form.
func MarshalPrivateKey(k PrivKey) ([]byte, error) {
data, err := k.Raw()
if err != nil {
return nil, err
}
return proto.Marshal(&pb.PrivateKey{
Type: k.Type().Enum(),
Data: data,
})
}
// ConfigDecodeKey decodes from b64 (for config file) to a byte array that can be unmarshalled.
func ConfigDecodeKey(b string) ([]byte, error) {
return base64.StdEncoding.DecodeString(b)
}
// ConfigEncodeKey encodes a marshalled key to b64 (for config file).
func ConfigEncodeKey(b []byte) string {
return base64.StdEncoding.EncodeToString(b)
}
// KeyEqual checks whether two Keys are equivalent (have identical byte representations).
func KeyEqual(k1, k2 Key) bool {
if k1 == k2 {
return true
}
return k1.Equals(k2)
}
func basicEquals(k1, k2 Key) bool {
if k1.Type() != k2.Type() {
return false
}
a, err := k1.Raw()
if err != nil {
return false
}
b, err := k2.Raw()
if err != nil {
return false
}
return subtle.ConstantTimeCompare(a, b) == 1
}

View File

@@ -0,0 +1,78 @@
package crypto
import (
"crypto"
"crypto/ecdsa"
"crypto/ed25519"
"crypto/rsa"
"github.com/decred/dcrd/dcrec/secp256k1/v4"
)
// KeyPairFromStdKey wraps standard library (and secp256k1) private keys in libp2p/go-libp2p/core/crypto keys
func KeyPairFromStdKey(priv crypto.PrivateKey) (PrivKey, PubKey, error) {
if priv == nil {
return nil, nil, ErrNilPrivateKey
}
switch p := priv.(type) {
case *rsa.PrivateKey:
return &RsaPrivateKey{*p}, &RsaPublicKey{k: p.PublicKey}, nil
case *ecdsa.PrivateKey:
return &ECDSAPrivateKey{p}, &ECDSAPublicKey{&p.PublicKey}, nil
case *ed25519.PrivateKey:
pubIfc := p.Public()
pub, _ := pubIfc.(ed25519.PublicKey)
return &Ed25519PrivateKey{*p}, &Ed25519PublicKey{pub}, nil
case *secp256k1.PrivateKey:
sPriv := Secp256k1PrivateKey(*p)
sPub := Secp256k1PublicKey(*p.PubKey())
return &sPriv, &sPub, nil
default:
return nil, nil, ErrBadKeyType
}
}
// PrivKeyToStdKey converts libp2p/go-libp2p/core/crypto private keys to standard library (and secp256k1) private keys
func PrivKeyToStdKey(priv PrivKey) (crypto.PrivateKey, error) {
if priv == nil {
return nil, ErrNilPrivateKey
}
switch p := priv.(type) {
case *RsaPrivateKey:
return &p.sk, nil
case *ECDSAPrivateKey:
return p.priv, nil
case *Ed25519PrivateKey:
return &p.k, nil
case *Secp256k1PrivateKey:
return p, nil
default:
return nil, ErrBadKeyType
}
}
// PubKeyToStdKey converts libp2p/go-libp2p/core/crypto private keys to standard library (and secp256k1) public keys
func PubKeyToStdKey(pub PubKey) (crypto.PublicKey, error) {
if pub == nil {
return nil, ErrNilPublicKey
}
switch p := pub.(type) {
case *RsaPublicKey:
return &p.k, nil
case *ECDSAPublicKey:
return p.pub, nil
case *Ed25519PublicKey:
return p.k, nil
case *Secp256k1PublicKey:
return p, nil
default:
return nil, ErrBadKeyType
}
}

View File

@@ -0,0 +1,297 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.30.0
// protoc v3.21.12
// source: pb/crypto.proto
package pb
import (
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
type KeyType int32
const (
KeyType_RSA KeyType = 0
KeyType_Ed25519 KeyType = 1
KeyType_Secp256k1 KeyType = 2
KeyType_ECDSA KeyType = 3
)
// Enum value maps for KeyType.
var (
KeyType_name = map[int32]string{
0: "RSA",
1: "Ed25519",
2: "Secp256k1",
3: "ECDSA",
}
KeyType_value = map[string]int32{
"RSA": 0,
"Ed25519": 1,
"Secp256k1": 2,
"ECDSA": 3,
}
)
func (x KeyType) Enum() *KeyType {
p := new(KeyType)
*p = x
return p
}
func (x KeyType) String() string {
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
}
func (KeyType) Descriptor() protoreflect.EnumDescriptor {
return file_pb_crypto_proto_enumTypes[0].Descriptor()
}
func (KeyType) Type() protoreflect.EnumType {
return &file_pb_crypto_proto_enumTypes[0]
}
func (x KeyType) Number() protoreflect.EnumNumber {
return protoreflect.EnumNumber(x)
}
// Deprecated: Do not use.
func (x *KeyType) UnmarshalJSON(b []byte) error {
num, err := protoimpl.X.UnmarshalJSONEnum(x.Descriptor(), b)
if err != nil {
return err
}
*x = KeyType(num)
return nil
}
// Deprecated: Use KeyType.Descriptor instead.
func (KeyType) EnumDescriptor() ([]byte, []int) {
return file_pb_crypto_proto_rawDescGZIP(), []int{0}
}
type PublicKey struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Type *KeyType `protobuf:"varint,1,req,name=Type,enum=crypto.pb.KeyType" json:"Type,omitempty"`
Data []byte `protobuf:"bytes,2,req,name=Data" json:"Data,omitempty"`
}
func (x *PublicKey) Reset() {
*x = PublicKey{}
if protoimpl.UnsafeEnabled {
mi := &file_pb_crypto_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *PublicKey) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*PublicKey) ProtoMessage() {}
func (x *PublicKey) ProtoReflect() protoreflect.Message {
mi := &file_pb_crypto_proto_msgTypes[0]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use PublicKey.ProtoReflect.Descriptor instead.
func (*PublicKey) Descriptor() ([]byte, []int) {
return file_pb_crypto_proto_rawDescGZIP(), []int{0}
}
func (x *PublicKey) GetType() KeyType {
if x != nil && x.Type != nil {
return *x.Type
}
return KeyType_RSA
}
func (x *PublicKey) GetData() []byte {
if x != nil {
return x.Data
}
return nil
}
type PrivateKey struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Type *KeyType `protobuf:"varint,1,req,name=Type,enum=crypto.pb.KeyType" json:"Type,omitempty"`
Data []byte `protobuf:"bytes,2,req,name=Data" json:"Data,omitempty"`
}
func (x *PrivateKey) Reset() {
*x = PrivateKey{}
if protoimpl.UnsafeEnabled {
mi := &file_pb_crypto_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *PrivateKey) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*PrivateKey) ProtoMessage() {}
func (x *PrivateKey) ProtoReflect() protoreflect.Message {
mi := &file_pb_crypto_proto_msgTypes[1]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use PrivateKey.ProtoReflect.Descriptor instead.
func (*PrivateKey) Descriptor() ([]byte, []int) {
return file_pb_crypto_proto_rawDescGZIP(), []int{1}
}
func (x *PrivateKey) GetType() KeyType {
if x != nil && x.Type != nil {
return *x.Type
}
return KeyType_RSA
}
func (x *PrivateKey) GetData() []byte {
if x != nil {
return x.Data
}
return nil
}
var File_pb_crypto_proto protoreflect.FileDescriptor
var file_pb_crypto_proto_rawDesc = []byte{
0x0a, 0x0f, 0x70, 0x62, 0x2f, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74,
0x6f, 0x12, 0x09, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x2e, 0x70, 0x62, 0x22, 0x47, 0x0a, 0x09,
0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x12, 0x26, 0x0a, 0x04, 0x54, 0x79, 0x70,
0x65, 0x18, 0x01, 0x20, 0x02, 0x28, 0x0e, 0x32, 0x12, 0x2e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f,
0x2e, 0x70, 0x62, 0x2e, 0x4b, 0x65, 0x79, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x54, 0x79, 0x70,
0x65, 0x12, 0x12, 0x0a, 0x04, 0x44, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x02, 0x28, 0x0c, 0x52,
0x04, 0x44, 0x61, 0x74, 0x61, 0x22, 0x48, 0x0a, 0x0a, 0x50, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65,
0x4b, 0x65, 0x79, 0x12, 0x26, 0x0a, 0x04, 0x54, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x02, 0x28,
0x0e, 0x32, 0x12, 0x2e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x2e, 0x70, 0x62, 0x2e, 0x4b, 0x65,
0x79, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x54, 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x44,
0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x02, 0x28, 0x0c, 0x52, 0x04, 0x44, 0x61, 0x74, 0x61, 0x2a,
0x39, 0x0a, 0x07, 0x4b, 0x65, 0x79, 0x54, 0x79, 0x70, 0x65, 0x12, 0x07, 0x0a, 0x03, 0x52, 0x53,
0x41, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x45, 0x64, 0x32, 0x35, 0x35, 0x31, 0x39, 0x10, 0x01,
0x12, 0x0d, 0x0a, 0x09, 0x53, 0x65, 0x63, 0x70, 0x32, 0x35, 0x36, 0x6b, 0x31, 0x10, 0x02, 0x12,
0x09, 0x0a, 0x05, 0x45, 0x43, 0x44, 0x53, 0x41, 0x10, 0x03, 0x42, 0x2c, 0x5a, 0x2a, 0x67, 0x69,
0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6c, 0x69, 0x62, 0x70, 0x32, 0x70, 0x2f,
0x67, 0x6f, 0x2d, 0x6c, 0x69, 0x62, 0x70, 0x32, 0x70, 0x2f, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x63,
0x72, 0x79, 0x70, 0x74, 0x6f, 0x2f, 0x70, 0x62,
}
var (
file_pb_crypto_proto_rawDescOnce sync.Once
file_pb_crypto_proto_rawDescData = file_pb_crypto_proto_rawDesc
)
func file_pb_crypto_proto_rawDescGZIP() []byte {
file_pb_crypto_proto_rawDescOnce.Do(func() {
file_pb_crypto_proto_rawDescData = protoimpl.X.CompressGZIP(file_pb_crypto_proto_rawDescData)
})
return file_pb_crypto_proto_rawDescData
}
var file_pb_crypto_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
var file_pb_crypto_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
var file_pb_crypto_proto_goTypes = []interface{}{
(KeyType)(0), // 0: crypto.pb.KeyType
(*PublicKey)(nil), // 1: crypto.pb.PublicKey
(*PrivateKey)(nil), // 2: crypto.pb.PrivateKey
}
var file_pb_crypto_proto_depIdxs = []int32{
0, // 0: crypto.pb.PublicKey.Type:type_name -> crypto.pb.KeyType
0, // 1: crypto.pb.PrivateKey.Type:type_name -> crypto.pb.KeyType
2, // [2:2] is the sub-list for method output_type
2, // [2:2] is the sub-list for method input_type
2, // [2:2] is the sub-list for extension type_name
2, // [2:2] is the sub-list for extension extendee
0, // [0:2] is the sub-list for field type_name
}
func init() { file_pb_crypto_proto_init() }
func file_pb_crypto_proto_init() {
if File_pb_crypto_proto != nil {
return
}
if !protoimpl.UnsafeEnabled {
file_pb_crypto_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*PublicKey); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_pb_crypto_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*PrivateKey); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_pb_crypto_proto_rawDesc,
NumEnums: 1,
NumMessages: 2,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_pb_crypto_proto_goTypes,
DependencyIndexes: file_pb_crypto_proto_depIdxs,
EnumInfos: file_pb_crypto_proto_enumTypes,
MessageInfos: file_pb_crypto_proto_msgTypes,
}.Build()
File_pb_crypto_proto = out.File
file_pb_crypto_proto_rawDesc = nil
file_pb_crypto_proto_goTypes = nil
file_pb_crypto_proto_depIdxs = nil
}

View File

@@ -0,0 +1,22 @@
syntax = "proto2";
package crypto.pb;
option go_package = "github.com/libp2p/go-libp2p/core/crypto/pb";
enum KeyType {
RSA = 0;
Ed25519 = 1;
Secp256k1 = 2;
ECDSA = 3;
}
message PublicKey {
required KeyType Type = 1;
required bytes Data = 2;
}
message PrivateKey {
required KeyType Type = 1;
required bytes Data = 2;
}

View File

@@ -0,0 +1,28 @@
package crypto
import (
"fmt"
"os"
)
// WeakRsaKeyEnv is an environment variable which, when set, lowers the
// minimum required bits of RSA keys to 512. This should be used exclusively in
// test situations.
const WeakRsaKeyEnv = "LIBP2P_ALLOW_WEAK_RSA_KEYS"
var MinRsaKeyBits = 2048
var maxRsaKeyBits = 8192
// ErrRsaKeyTooSmall is returned when trying to generate or parse an RSA key
// that's smaller than MinRsaKeyBits bits. In test
var ErrRsaKeyTooSmall error
var ErrRsaKeyTooBig error = fmt.Errorf("rsa keys must be <= %d bits", maxRsaKeyBits)
func init() {
if _, ok := os.LookupEnv(WeakRsaKeyEnv); ok {
MinRsaKeyBits = 512
}
ErrRsaKeyTooSmall = fmt.Errorf("rsa keys must be >= %d bits to be useful", MinRsaKeyBits)
}

View File

@@ -0,0 +1,155 @@
package crypto
import (
"crypto"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"errors"
"io"
pb "github.com/libp2p/go-libp2p/core/crypto/pb"
"github.com/libp2p/go-libp2p/core/internal/catch"
"github.com/minio/sha256-simd"
)
// RsaPrivateKey is a rsa private key
type RsaPrivateKey struct {
sk rsa.PrivateKey
}
// RsaPublicKey is a rsa public key
type RsaPublicKey struct {
k rsa.PublicKey
cached []byte
}
// GenerateRSAKeyPair generates a new rsa private and public key
func GenerateRSAKeyPair(bits int, src io.Reader) (PrivKey, PubKey, error) {
if bits < MinRsaKeyBits {
return nil, nil, ErrRsaKeyTooSmall
}
if bits > maxRsaKeyBits {
return nil, nil, ErrRsaKeyTooBig
}
priv, err := rsa.GenerateKey(src, bits)
if err != nil {
return nil, nil, err
}
pk := priv.PublicKey
return &RsaPrivateKey{sk: *priv}, &RsaPublicKey{k: pk}, nil
}
// Verify compares a signature against input data
func (pk *RsaPublicKey) Verify(data, sig []byte) (success bool, err error) {
defer func() {
catch.HandlePanic(recover(), &err, "RSA signature verification")
// To be safe
if err != nil {
success = false
}
}()
hashed := sha256.Sum256(data)
err = rsa.VerifyPKCS1v15(&pk.k, crypto.SHA256, hashed[:], sig)
if err != nil {
return false, err
}
return true, nil
}
func (pk *RsaPublicKey) Type() pb.KeyType {
return pb.KeyType_RSA
}
func (pk *RsaPublicKey) Raw() (res []byte, err error) {
defer func() { catch.HandlePanic(recover(), &err, "RSA public-key marshaling") }()
return x509.MarshalPKIXPublicKey(&pk.k)
}
// Equals checks whether this key is equal to another
func (pk *RsaPublicKey) Equals(k Key) bool {
// make sure this is a rsa public key
other, ok := (k).(*RsaPublicKey)
if !ok {
return basicEquals(pk, k)
}
return pk.k.N.Cmp(other.k.N) == 0 && pk.k.E == other.k.E
}
// Sign returns a signature of the input data
func (sk *RsaPrivateKey) Sign(message []byte) (sig []byte, err error) {
defer func() { catch.HandlePanic(recover(), &err, "RSA signing") }()
hashed := sha256.Sum256(message)
return rsa.SignPKCS1v15(rand.Reader, &sk.sk, crypto.SHA256, hashed[:])
}
// GetPublic returns a public key
func (sk *RsaPrivateKey) GetPublic() PubKey {
return &RsaPublicKey{k: sk.sk.PublicKey}
}
func (sk *RsaPrivateKey) Type() pb.KeyType {
return pb.KeyType_RSA
}
func (sk *RsaPrivateKey) Raw() (res []byte, err error) {
defer func() { catch.HandlePanic(recover(), &err, "RSA private-key marshaling") }()
b := x509.MarshalPKCS1PrivateKey(&sk.sk)
return b, nil
}
// Equals checks whether this key is equal to another
func (sk *RsaPrivateKey) Equals(k Key) bool {
// make sure this is a rsa public key
other, ok := (k).(*RsaPrivateKey)
if !ok {
return basicEquals(sk, k)
}
a := sk.sk
b := other.sk
// Don't care about constant time. We're only comparing the public half.
return a.PublicKey.N.Cmp(b.PublicKey.N) == 0 && a.PublicKey.E == b.PublicKey.E
}
// UnmarshalRsaPrivateKey returns a private key from the input x509 bytes
func UnmarshalRsaPrivateKey(b []byte) (key PrivKey, err error) {
defer func() { catch.HandlePanic(recover(), &err, "RSA private-key unmarshaling") }()
sk, err := x509.ParsePKCS1PrivateKey(b)
if err != nil {
return nil, err
}
if sk.N.BitLen() < MinRsaKeyBits {
return nil, ErrRsaKeyTooSmall
}
if sk.N.BitLen() > maxRsaKeyBits {
return nil, ErrRsaKeyTooBig
}
return &RsaPrivateKey{sk: *sk}, nil
}
// UnmarshalRsaPublicKey returns a public key from the input x509 bytes
func UnmarshalRsaPublicKey(b []byte) (key PubKey, err error) {
defer func() { catch.HandlePanic(recover(), &err, "RSA public-key unmarshaling") }()
pub, err := x509.ParsePKIXPublicKey(b)
if err != nil {
return nil, err
}
pk, ok := pub.(*rsa.PublicKey)
if !ok {
return nil, errors.New("not actually an rsa public key")
}
if pk.N.BitLen() < MinRsaKeyBits {
return nil, ErrRsaKeyTooSmall
}
if pk.N.BitLen() > maxRsaKeyBits {
return nil, ErrRsaKeyTooBig
}
return &RsaPublicKey{k: *pk}, nil
}

View File

@@ -0,0 +1,127 @@
package crypto
import (
"fmt"
"io"
pb "github.com/libp2p/go-libp2p/core/crypto/pb"
"github.com/libp2p/go-libp2p/core/internal/catch"
"github.com/decred/dcrd/dcrec/secp256k1/v4"
"github.com/decred/dcrd/dcrec/secp256k1/v4/ecdsa"
"github.com/minio/sha256-simd"
)
// Secp256k1PrivateKey is a Secp256k1 private key
type Secp256k1PrivateKey secp256k1.PrivateKey
// Secp256k1PublicKey is a Secp256k1 public key
type Secp256k1PublicKey secp256k1.PublicKey
// GenerateSecp256k1Key generates a new Secp256k1 private and public key pair
func GenerateSecp256k1Key(src io.Reader) (PrivKey, PubKey, error) {
privk, err := secp256k1.GeneratePrivateKey()
if err != nil {
return nil, nil, err
}
k := (*Secp256k1PrivateKey)(privk)
return k, k.GetPublic(), nil
}
// UnmarshalSecp256k1PrivateKey returns a private key from bytes
func UnmarshalSecp256k1PrivateKey(data []byte) (k PrivKey, err error) {
if len(data) != secp256k1.PrivKeyBytesLen {
return nil, fmt.Errorf("expected secp256k1 data size to be %d", secp256k1.PrivKeyBytesLen)
}
defer func() { catch.HandlePanic(recover(), &err, "secp256k1 private-key unmarshal") }()
privk := secp256k1.PrivKeyFromBytes(data)
return (*Secp256k1PrivateKey)(privk), nil
}
// UnmarshalSecp256k1PublicKey returns a public key from bytes
func UnmarshalSecp256k1PublicKey(data []byte) (_k PubKey, err error) {
defer func() { catch.HandlePanic(recover(), &err, "secp256k1 public-key unmarshal") }()
k, err := secp256k1.ParsePubKey(data)
if err != nil {
return nil, err
}
return (*Secp256k1PublicKey)(k), nil
}
// Type returns the private key type
func (k *Secp256k1PrivateKey) Type() pb.KeyType {
return pb.KeyType_Secp256k1
}
// Raw returns the bytes of the key
func (k *Secp256k1PrivateKey) Raw() ([]byte, error) {
return (*secp256k1.PrivateKey)(k).Serialize(), nil
}
// Equals compares two private keys
func (k *Secp256k1PrivateKey) Equals(o Key) bool {
sk, ok := o.(*Secp256k1PrivateKey)
if !ok {
return basicEquals(k, o)
}
return k.GetPublic().Equals(sk.GetPublic())
}
// Sign returns a signature from input data
func (k *Secp256k1PrivateKey) Sign(data []byte) (_sig []byte, err error) {
defer func() { catch.HandlePanic(recover(), &err, "secp256k1 signing") }()
key := (*secp256k1.PrivateKey)(k)
hash := sha256.Sum256(data)
sig := ecdsa.Sign(key, hash[:])
return sig.Serialize(), nil
}
// GetPublic returns a public key
func (k *Secp256k1PrivateKey) GetPublic() PubKey {
return (*Secp256k1PublicKey)((*secp256k1.PrivateKey)(k).PubKey())
}
// Type returns the public key type
func (k *Secp256k1PublicKey) Type() pb.KeyType {
return pb.KeyType_Secp256k1
}
// Raw returns the bytes of the key
func (k *Secp256k1PublicKey) Raw() (res []byte, err error) {
defer func() { catch.HandlePanic(recover(), &err, "secp256k1 public key marshaling") }()
return (*secp256k1.PublicKey)(k).SerializeCompressed(), nil
}
// Equals compares two public keys
func (k *Secp256k1PublicKey) Equals(o Key) bool {
sk, ok := o.(*Secp256k1PublicKey)
if !ok {
return basicEquals(k, o)
}
return (*secp256k1.PublicKey)(k).IsEqual((*secp256k1.PublicKey)(sk))
}
// Verify compares a signature against the input data
func (k *Secp256k1PublicKey) Verify(data []byte, sigStr []byte) (success bool, err error) {
defer func() {
catch.HandlePanic(recover(), &err, "secp256k1 signature verification")
// To be extra safe.
if err != nil {
success = false
}
}()
sig, err := ecdsa.ParseDERSignature(sigStr)
if err != nil {
return false, err
}
hash := sha256.Sum256(data)
return sig.Verify(hash[:], (*secp256k1.PublicKey)(k)), nil
}

View File

@@ -0,0 +1,27 @@
// Package discovery provides service advertisement and peer discovery interfaces for libp2p.
package discovery
import (
"context"
"time"
"github.com/libp2p/go-libp2p/core/peer"
)
// Advertiser is an interface for advertising services
type Advertiser interface {
// Advertise advertises a service
Advertise(ctx context.Context, ns string, opts ...Option) (time.Duration, error)
}
// Discoverer is an interface for peer discovery
type Discoverer interface {
// FindPeers discovers peers providing a service
FindPeers(ctx context.Context, ns string, opts ...Option) (<-chan peer.AddrInfo, error)
}
// Discovery is an interface that combines service advertisement and peer discovery
type Discovery interface {
Advertiser
Discoverer
}

View File

@@ -0,0 +1,41 @@
package discovery
import "time"
// DiscoveryOpt is a single discovery option.
type Option func(opts *Options) error
// DiscoveryOpts is a set of discovery options.
type Options struct {
Ttl time.Duration
Limit int
// Other (implementation-specific) options
Other map[interface{}]interface{}
}
// Apply applies the given options to this DiscoveryOpts
func (opts *Options) Apply(options ...Option) error {
for _, o := range options {
if err := o(opts); err != nil {
return err
}
}
return nil
}
// TTL is an option that provides a hint for the duration of an advertisement
func TTL(ttl time.Duration) Option {
return func(opts *Options) error {
opts.Ttl = ttl
return nil
}
}
// Limit is an option that provides an upper bound on the peer count for discovery
func Limit(limit int) Option {
return func(opts *Options) error {
opts.Limit = limit
return nil
}
}

83
vendor/github.com/libp2p/go-libp2p/core/event/addrs.go generated vendored Normal file
View File

@@ -0,0 +1,83 @@
package event
import (
"github.com/libp2p/go-libp2p/core/record"
ma "github.com/multiformats/go-multiaddr"
)
// AddrAction represents an action taken on one of a Host's listen addresses.
// It is used to add context to address change events in EvtLocalAddressesUpdated.
type AddrAction int
const (
// Unknown means that the event producer was unable to determine why the address
// is in the current state.
Unknown AddrAction = iota
// Added means that the address is new and was not present prior to the event.
Added
// Maintained means that the address was not altered between the current and
// previous states.
Maintained
// Removed means that the address was removed from the Host.
Removed
)
// UpdatedAddress is used in the EvtLocalAddressesUpdated event to convey
// address change information.
type UpdatedAddress struct {
// Address contains the address that was updated.
Address ma.Multiaddr
// Action indicates what action was taken on the address during the
// event. May be Unknown if the event producer cannot produce diffs.
Action AddrAction
}
// EvtLocalAddressesUpdated should be emitted when the set of listen addresses for
// the local host changes. This may happen for a number of reasons. For example,
// we may have opened a new relay connection, established a new NAT mapping via
// UPnP, or been informed of our observed address by another peer.
//
// EvtLocalAddressesUpdated contains a snapshot of the current listen addresses,
// and may also contain a diff between the current state and the previous state.
// If the event producer is capable of creating a diff, the Diffs field will be
// true, and event consumers can inspect the Action field of each UpdatedAddress
// to see how each address was modified.
//
// For example, the Action will tell you whether an address in
// the Current list was Added by the event producer, or was Maintained without
// changes. Addresses that were removed from the Host will have the AddrAction
// of Removed, and will be in the Removed list.
//
// If the event producer is not capable or producing diffs, the Diffs field will
// be false, the Removed list will always be empty, and the Action for each
// UpdatedAddress in the Current list will be Unknown.
//
// In addition to the above, EvtLocalAddressesUpdated also contains the updated peer.PeerRecord
// for the Current set of listen addresses, wrapped in a record.Envelope and signed by the Host's private key.
// This record can be shared with other peers to inform them of what we believe are our diallable addresses
// a secure and authenticated way.
type EvtLocalAddressesUpdated struct {
// Diffs indicates whether this event contains a diff of the Host's previous
// address set.
Diffs bool
// Current contains all current listen addresses for the Host.
// If Diffs == true, the Action field of each UpdatedAddress will tell
// you whether an address was Added, or was Maintained from the previous
// state.
Current []UpdatedAddress
// Removed contains addresses that were removed from the Host.
// This field is only set when Diffs == true.
Removed []UpdatedAddress
// SignedPeerRecord contains our own updated peer.PeerRecord, listing the addresses enumerated in Current.
// wrapped in a record.Envelope and signed by the Host's private key.
SignedPeerRecord *record.Envelope
}

100
vendor/github.com/libp2p/go-libp2p/core/event/bus.go generated vendored Normal file
View File

@@ -0,0 +1,100 @@
package event
import (
"io"
"reflect"
)
// SubscriptionOpt represents a subscriber option. Use the options exposed by the implementation of choice.
type SubscriptionOpt = func(interface{}) error
// EmitterOpt represents an emitter option. Use the options exposed by the implementation of choice.
type EmitterOpt = func(interface{}) error
// CancelFunc closes a subscriber.
type CancelFunc = func()
// wildcardSubscriptionType is a virtual type to represent wildcard
// subscriptions.
type wildcardSubscriptionType interface{}
// WildcardSubscription is the type to subscribe to receive all events
// emitted in the eventbus.
var WildcardSubscription = new(wildcardSubscriptionType)
// Emitter represents an actor that emits events onto the eventbus.
type Emitter interface {
io.Closer
// Emit emits an event onto the eventbus. If any channel subscribed to the topic is blocked,
// calls to Emit will block.
//
// Calling this function with wrong event type will cause a panic.
Emit(evt interface{}) error
}
// Subscription represents a subscription to one or multiple event types.
type Subscription interface {
io.Closer
// Out returns the channel from which to consume events.
Out() <-chan interface{}
// Name returns the name for the subscription
Name() string
}
// Bus is an interface for a type-based event delivery system.
type Bus interface {
// Subscribe creates a new Subscription.
//
// eventType can be either a pointer to a single event type, or a slice of pointers to
// subscribe to multiple event types at once, under a single subscription (and channel).
//
// Failing to drain the channel may cause publishers to block.
//
// If you want to subscribe to ALL events emitted in the bus, use
// `WildcardSubscription` as the `eventType`:
//
// eventbus.Subscribe(WildcardSubscription)
//
// Simple example
//
// sub, err := eventbus.Subscribe(new(EventType))
// defer sub.Close()
// for e := range sub.Out() {
// event := e.(EventType) // guaranteed safe
// [...]
// }
//
// Multi-type example
//
// sub, err := eventbus.Subscribe([]interface{}{new(EventA), new(EventB)})
// defer sub.Close()
// for e := range sub.Out() {
// select e.(type):
// case EventA:
// [...]
// case EventB:
// [...]
// }
// }
Subscribe(eventType interface{}, opts ...SubscriptionOpt) (Subscription, error)
// Emitter creates a new event emitter.
//
// eventType accepts typed nil pointers, and uses the type information for wiring purposes.
//
// Example:
// em, err := eventbus.Emitter(new(EventT))
// defer em.Close() // MUST call this after being done with the emitter
// em.Emit(EventT{})
Emitter(eventType interface{}, opts ...EmitterOpt) (Emitter, error)
// GetAllEventTypes returns all the event types that this bus knows about
// (having emitters and subscribers). It omits the WildcardSubscription.
//
// The caller is guaranteed that this function will only return value types;
// no pointer types will be returned.
GetAllEventTypes() []reflect.Type
}

21
vendor/github.com/libp2p/go-libp2p/core/event/dht.go generated vendored Normal file
View File

@@ -0,0 +1,21 @@
package event
// RawJSON is a type that contains a raw JSON string.
type RawJSON string
// GenericDHTEvent is a type that encapsulates an actual DHT event by carrying
// its raw JSON.
//
// Context: the DHT event system is rather bespoke and a bit messy at the time,
// so until we unify/clean that up, this event bridges the gap. It should only
// be consumed for informational purposes.
//
// EXPERIMENTAL: this will likely be removed if/when the DHT event types are
// hoisted to core, and the DHT event system is reconciled with the eventbus.
type GenericDHTEvent struct {
// Type is the type of the DHT event that occurred.
Type string
// Raw is the raw JSON representation of the event payload.
Raw RawJSON
}

11
vendor/github.com/libp2p/go-libp2p/core/event/doc.go generated vendored Normal file
View File

@@ -0,0 +1,11 @@
// Package event contains the abstractions for a local event bus, along with the standard events
// that libp2p subsystems may emit.
//
// Source code is arranged as follows:
// - doc.go: this file.
// - bus.go: abstractions for the event bus.
// - rest: event structs, sensibly categorised in files by entity, and following this naming convention:
// Evt[Entity (noun)][Event (verb past tense / gerund)]
// The past tense is used to convey that something happened, whereas the gerund form of the verb (-ing)
// expresses that a process is in progress. Examples: EvtConnEstablishing, EvtConnEstablished.
package event

View File

@@ -0,0 +1,17 @@
package event
import "github.com/libp2p/go-libp2p/core/peer"
// EvtPeerIdentificationCompleted is emitted when the initial identification round for a peer is completed.
type EvtPeerIdentificationCompleted struct {
// Peer is the ID of the peer whose identification succeeded.
Peer peer.ID
}
// EvtPeerIdentificationFailed is emitted when the initial identification round for a peer failed.
type EvtPeerIdentificationFailed struct {
// Peer is the ID of the peer whose identification failed.
Peer peer.ID
// Reason is the reason why identification failed.
Reason error
}

View File

@@ -0,0 +1,18 @@
package event
import "github.com/libp2p/go-libp2p/core/network"
// EvtNATDeviceTypeChanged is an event struct to be emitted when the type of the NAT device changes for a Transport Protocol.
//
// Note: This event is meaningful ONLY if the AutoNAT Reachability is Private.
// Consumers of this event should ALSO consume the `EvtLocalReachabilityChanged` event and interpret
// this event ONLY if the Reachability on the `EvtLocalReachabilityChanged` is Private.
type EvtNATDeviceTypeChanged struct {
// TransportProtocol is the Transport Protocol for which the NAT Device Type has been determined.
TransportProtocol network.NATTransportProtocol
// NatDeviceType indicates the type of the NAT Device for the Transport Protocol.
// Currently, it can be either a `Cone NAT` or a `Symmetric NAT`. Please see the detailed documentation
// on `network.NATDeviceType` enumerations for a better understanding of what these types mean and
// how they impact Connectivity and Hole Punching.
NatDeviceType network.NATDeviceType
}

View File

@@ -0,0 +1,55 @@
package event
import (
"github.com/libp2p/go-libp2p/core/network"
"github.com/libp2p/go-libp2p/core/peer"
)
// EvtPeerConnectednessChanged should be emitted every time the "connectedness" to a
// given peer changes. Specifically, this event is emitted in the following
// cases:
//
// - Connectedness = Connected: Every time we transition from having no
// connections to a peer to having at least one connection to the peer.
// - Connectedness = NotConnected: Every time we transition from having at least
// one connection to a peer to having no connections to the peer.
//
// Additional connectedness states may be added in the future. This list should
// not be considered exhaustive.
//
// Take note:
//
// - It's possible to have _multiple_ connections to a given peer.
// - Both libp2p and networks are asynchronous.
//
// This means that all the following situations are possible:
//
// A connection is cut and is re-established:
//
// - Peer A observes a transition from Connected -> NotConnected -> Connected
// - Peer B observes a transition from Connected -> NotConnected -> Connected
//
// Explanation: Both peers observe the connection die. This is the "nice" case.
//
// A connection is cut and is re-established.
//
// - Peer A observes a transition from Connected -> NotConnected -> Connected.
// - Peer B observes no transition.
//
// Explanation: Peer A re-establishes the dead connection. Peer B observes the
// new connection form before it observes the old connection die.
//
// A connection is cut:
//
// - Peer A observes no transition.
// - Peer B observes no transition.
//
// Explanation: There were two connections and one was cut. This connection
// might have been in active use but neither peer will observe a change in
// "connectedness". Peers should always make sure to retry network requests.
type EvtPeerConnectednessChanged struct {
// Peer is the remote peer whose connectedness has changed.
Peer peer.ID
// Connectedness is the new connectedness state.
Connectedness network.Connectedness
}

View File

@@ -0,0 +1,26 @@
package event
import (
peer "github.com/libp2p/go-libp2p/core/peer"
protocol "github.com/libp2p/go-libp2p/core/protocol"
)
// EvtPeerProtocolsUpdated should be emitted when a peer we're connected to adds or removes protocols from their stack.
type EvtPeerProtocolsUpdated struct {
// Peer is the peer whose protocols were updated.
Peer peer.ID
// Added enumerates the protocols that were added by this peer.
Added []protocol.ID
// Removed enumerates the protocols that were removed by this peer.
Removed []protocol.ID
}
// EvtLocalProtocolsUpdated should be emitted when stream handlers are attached or detached from the local host.
// For handlers attached with a matcher predicate (host.SetStreamHandlerMatch()), only the protocol ID will be
// included in this event.
type EvtLocalProtocolsUpdated struct {
// Added enumerates the protocols that were added locally.
Added []protocol.ID
// Removed enumerates the protocols that were removed locally.
Removed []protocol.ID
}

View File

@@ -0,0 +1,13 @@
package event
import (
"github.com/libp2p/go-libp2p/core/network"
)
// EvtLocalReachabilityChanged is an event struct to be emitted when the local's
// node reachability changes state.
//
// This event is usually emitted by the AutoNAT subsystem.
type EvtLocalReachabilityChanged struct {
Reachability network.Reachability
}

View File

@@ -0,0 +1,11 @@
package host
import "github.com/libp2p/go-libp2p/core/peer"
// InfoFromHost returns a peer.AddrInfo struct with the Host's ID and all of its Addrs.
func InfoFromHost(h Host) *peer.AddrInfo {
return &peer.AddrInfo{
ID: h.ID(),
Addrs: h.Addrs(),
}
}

75
vendor/github.com/libp2p/go-libp2p/core/host/host.go generated vendored Normal file
View File

@@ -0,0 +1,75 @@
// Package host provides the core Host interface for libp2p.
//
// Host represents a single libp2p node in a peer-to-peer network.
package host
import (
"context"
"github.com/libp2p/go-libp2p/core/connmgr"
"github.com/libp2p/go-libp2p/core/event"
"github.com/libp2p/go-libp2p/core/network"
"github.com/libp2p/go-libp2p/core/peer"
"github.com/libp2p/go-libp2p/core/peerstore"
"github.com/libp2p/go-libp2p/core/protocol"
ma "github.com/multiformats/go-multiaddr"
)
// Host is an object participating in a p2p network, which
// implements protocols or provides services. It handles
// requests like a Server, and issues requests like a Client.
// It is called Host because it is both Server and Client (and Peer
// may be confusing).
type Host interface {
// ID returns the (local) peer.ID associated with this Host
ID() peer.ID
// Peerstore returns the Host's repository of Peer Addresses and Keys.
Peerstore() peerstore.Peerstore
// Returns the listen addresses of the Host
Addrs() []ma.Multiaddr
// Networks returns the Network interface of the Host
Network() network.Network
// Mux returns the Mux multiplexing incoming streams to protocol handlers
Mux() protocol.Switch
// Connect ensures there is a connection between this host and the peer with
// given peer.ID. Connect will absorb the addresses in pi into its internal
// peerstore. If there is not an active connection, Connect will issue a
// h.Network.Dial, and block until a connection is open, or an error is
// returned. // TODO: Relay + NAT.
Connect(ctx context.Context, pi peer.AddrInfo) error
// SetStreamHandler sets the protocol handler on the Host's Mux.
// This is equivalent to:
// host.Mux().SetHandler(proto, handler)
// (Thread-safe)
SetStreamHandler(pid protocol.ID, handler network.StreamHandler)
// SetStreamHandlerMatch sets the protocol handler on the Host's Mux
// using a matching function for protocol selection.
SetStreamHandlerMatch(protocol.ID, func(protocol.ID) bool, network.StreamHandler)
// RemoveStreamHandler removes a handler on the mux that was set by
// SetStreamHandler
RemoveStreamHandler(pid protocol.ID)
// NewStream opens a new stream to given peer p, and writes a p2p/protocol
// header with given ProtocolID. If there is no connection to p, attempts
// to create one. If ProtocolID is "", writes no header.
// (Thread-safe)
NewStream(ctx context.Context, p peer.ID, pids ...protocol.ID) (network.Stream, error)
// Close shuts down the host, its Network, and services.
Close() error
// ConnManager returns this hosts connection manager
ConnManager() connmgr.ConnManager
// EventBus returns the hosts eventbus
EventBus() event.Bus
}

View File

@@ -0,0 +1,18 @@
package catch
import (
"fmt"
"io"
"os"
"runtime/debug"
)
var panicWriter io.Writer = os.Stderr
// HandlePanic handles and logs panics.
func HandlePanic(rerr interface{}, err *error, where string) {
if rerr != nil {
fmt.Fprintf(panicWriter, "caught panic: %s\n%s\n", rerr, debug.Stack())
*err = fmt.Errorf("panic in %s: %s", where, rerr)
}
}

View File

@@ -0,0 +1,176 @@
// Package metrics provides metrics collection and reporting interfaces for libp2p.
package metrics
import (
"time"
"github.com/libp2p/go-flow-metrics"
"github.com/libp2p/go-libp2p/core/peer"
"github.com/libp2p/go-libp2p/core/protocol"
)
// BandwidthCounter tracks incoming and outgoing data transferred by the local peer.
// Metrics are available for total bandwidth across all peers / protocols, as well
// as segmented by remote peer ID and protocol ID.
type BandwidthCounter struct {
totalIn flow.Meter
totalOut flow.Meter
protocolIn flow.MeterRegistry
protocolOut flow.MeterRegistry
peerIn flow.MeterRegistry
peerOut flow.MeterRegistry
}
// NewBandwidthCounter creates a new BandwidthCounter.
func NewBandwidthCounter() *BandwidthCounter {
return new(BandwidthCounter)
}
// LogSentMessage records the size of an outgoing message
// without associating the bandwidth to a specific peer or protocol.
func (bwc *BandwidthCounter) LogSentMessage(size int64) {
bwc.totalOut.Mark(uint64(size))
}
// LogRecvMessage records the size of an incoming message
// without associating the bandwidth to a specific peer or protocol.
func (bwc *BandwidthCounter) LogRecvMessage(size int64) {
bwc.totalIn.Mark(uint64(size))
}
// LogSentMessageStream records the size of an outgoing message over a single logical stream.
// Bandwidth is associated with the given protocol.ID and peer.ID.
func (bwc *BandwidthCounter) LogSentMessageStream(size int64, proto protocol.ID, p peer.ID) {
bwc.protocolOut.Get(string(proto)).Mark(uint64(size))
bwc.peerOut.Get(string(p)).Mark(uint64(size))
}
// LogRecvMessageStream records the size of an incoming message over a single logical stream.
// Bandwidth is associated with the given protocol.ID and peer.ID.
func (bwc *BandwidthCounter) LogRecvMessageStream(size int64, proto protocol.ID, p peer.ID) {
bwc.protocolIn.Get(string(proto)).Mark(uint64(size))
bwc.peerIn.Get(string(p)).Mark(uint64(size))
}
// GetBandwidthForPeer returns a Stats struct with bandwidth metrics associated with the given peer.ID.
// The metrics returned include all traffic sent / received for the peer, regardless of protocol.
func (bwc *BandwidthCounter) GetBandwidthForPeer(p peer.ID) (out Stats) {
inSnap := bwc.peerIn.Get(string(p)).Snapshot()
outSnap := bwc.peerOut.Get(string(p)).Snapshot()
return Stats{
TotalIn: int64(inSnap.Total),
TotalOut: int64(outSnap.Total),
RateIn: inSnap.Rate,
RateOut: outSnap.Rate,
}
}
// GetBandwidthForProtocol returns a Stats struct with bandwidth metrics associated with the given protocol.ID.
// The metrics returned include all traffic sent / received for the protocol, regardless of which peers were
// involved.
func (bwc *BandwidthCounter) GetBandwidthForProtocol(proto protocol.ID) (out Stats) {
inSnap := bwc.protocolIn.Get(string(proto)).Snapshot()
outSnap := bwc.protocolOut.Get(string(proto)).Snapshot()
return Stats{
TotalIn: int64(inSnap.Total),
TotalOut: int64(outSnap.Total),
RateIn: inSnap.Rate,
RateOut: outSnap.Rate,
}
}
// GetBandwidthTotals returns a Stats struct with bandwidth metrics for all data sent / received by the
// local peer, regardless of protocol or remote peer IDs.
func (bwc *BandwidthCounter) GetBandwidthTotals() (out Stats) {
inSnap := bwc.totalIn.Snapshot()
outSnap := bwc.totalOut.Snapshot()
return Stats{
TotalIn: int64(inSnap.Total),
TotalOut: int64(outSnap.Total),
RateIn: inSnap.Rate,
RateOut: outSnap.Rate,
}
}
// GetBandwidthByPeer returns a map of all remembered peers and the bandwidth
// metrics with respect to each. This method may be very expensive.
func (bwc *BandwidthCounter) GetBandwidthByPeer() map[peer.ID]Stats {
peers := make(map[peer.ID]Stats)
bwc.peerIn.ForEach(func(p string, meter *flow.Meter) {
id := peer.ID(p)
snap := meter.Snapshot()
stat := peers[id]
stat.TotalIn = int64(snap.Total)
stat.RateIn = snap.Rate
peers[id] = stat
})
bwc.peerOut.ForEach(func(p string, meter *flow.Meter) {
id := peer.ID(p)
snap := meter.Snapshot()
stat := peers[id]
stat.TotalOut = int64(snap.Total)
stat.RateOut = snap.Rate
peers[id] = stat
})
return peers
}
// GetBandwidthByProtocol returns a map of all remembered protocols and
// the bandwidth metrics with respect to each. This method may be moderately
// expensive.
func (bwc *BandwidthCounter) GetBandwidthByProtocol() map[protocol.ID]Stats {
protocols := make(map[protocol.ID]Stats)
bwc.protocolIn.ForEach(func(p string, meter *flow.Meter) {
id := protocol.ID(p)
snap := meter.Snapshot()
stat := protocols[id]
stat.TotalIn = int64(snap.Total)
stat.RateIn = snap.Rate
protocols[id] = stat
})
bwc.protocolOut.ForEach(func(p string, meter *flow.Meter) {
id := protocol.ID(p)
snap := meter.Snapshot()
stat := protocols[id]
stat.TotalOut = int64(snap.Total)
stat.RateOut = snap.Rate
protocols[id] = stat
})
return protocols
}
// Reset clears all stats.
func (bwc *BandwidthCounter) Reset() {
bwc.totalIn.Reset()
bwc.totalOut.Reset()
bwc.protocolIn.Clear()
bwc.protocolOut.Clear()
bwc.peerIn.Clear()
bwc.peerOut.Clear()
}
// TrimIdle trims all timers idle since the given time.
func (bwc *BandwidthCounter) TrimIdle(since time.Time) {
bwc.peerIn.TrimIdle(since)
bwc.peerOut.TrimIdle(since)
bwc.protocolIn.TrimIdle(since)
bwc.protocolOut.TrimIdle(since)
}

View File

@@ -0,0 +1,31 @@
// Package metrics provides metrics collection and reporting interfaces for libp2p.
package metrics
import (
"github.com/libp2p/go-libp2p/core/peer"
"github.com/libp2p/go-libp2p/core/protocol"
)
// Stats represents a point-in-time snapshot of bandwidth metrics.
//
// The TotalIn and TotalOut fields record cumulative bytes sent / received.
// The RateIn and RateOut fields record bytes sent / received per second.
type Stats struct {
TotalIn int64
TotalOut int64
RateIn float64
RateOut float64
}
// Reporter provides methods for logging and retrieving metrics.
type Reporter interface {
LogSentMessage(int64)
LogRecvMessage(int64)
LogSentMessageStream(int64, protocol.ID, peer.ID)
LogRecvMessageStream(int64, protocol.ID, peer.ID)
GetBandwidthForPeer(peer.ID) Stats
GetBandwidthForProtocol(protocol.ID) Stats
GetBandwidthTotals() Stats
GetBandwidthByPeer() map[peer.ID]Stats
GetBandwidthByProtocol() map[protocol.ID]Stats
}

View File

@@ -0,0 +1,93 @@
package network
import (
"context"
"io"
ic "github.com/libp2p/go-libp2p/core/crypto"
"github.com/libp2p/go-libp2p/core/peer"
"github.com/libp2p/go-libp2p/core/protocol"
ma "github.com/multiformats/go-multiaddr"
)
// Conn is a connection to a remote peer. It multiplexes streams.
// Usually there is no need to use a Conn directly, but it may
// be useful to get information about the peer on the other side:
//
// stream.Conn().RemotePeer()
type Conn interface {
io.Closer
ConnSecurity
ConnMultiaddrs
ConnStat
ConnScoper
// ID returns an identifier that uniquely identifies this Conn within this
// host, during this run. Connection IDs may repeat across restarts.
ID() string
// NewStream constructs a new Stream over this conn.
NewStream(context.Context) (Stream, error)
// GetStreams returns all open streams over this conn.
GetStreams() []Stream
// IsClosed returns whether a connection is fully closed, so it can
// be garbage collected.
IsClosed() bool
}
// ConnectionState holds information about the connection.
type ConnectionState struct {
// The stream multiplexer used on this connection (if any). For example: /yamux/1.0.0
StreamMultiplexer protocol.ID
// The security protocol used on this connection (if any). For example: /tls/1.0.0
Security protocol.ID
// the transport used on this connection. For example: tcp
Transport string
// indicates whether StreamMultiplexer was selected using inlined muxer negotiation
UsedEarlyMuxerNegotiation bool
}
// ConnSecurity is the interface that one can mix into a connection interface to
// give it the security methods.
type ConnSecurity interface {
// LocalPeer returns our peer ID
LocalPeer() peer.ID
// RemotePeer returns the peer ID of the remote peer.
RemotePeer() peer.ID
// RemotePublicKey returns the public key of the remote peer.
RemotePublicKey() ic.PubKey
// ConnState returns information about the connection state.
ConnState() ConnectionState
}
// ConnMultiaddrs is an interface mixin for connection types that provide multiaddr
// addresses for the endpoints.
type ConnMultiaddrs interface {
// LocalMultiaddr returns the local Multiaddr associated
// with this connection
LocalMultiaddr() ma.Multiaddr
// RemoteMultiaddr returns the remote Multiaddr associated
// with this connection
RemoteMultiaddr() ma.Multiaddr
}
// ConnStat is an interface mixin for connection types that provide connection statistics.
type ConnStat interface {
// Stat stores metadata pertaining to this conn.
Stat() ConnStats
}
// ConnScoper is the interface that one can mix into a connection interface to give it a resource
// management scope
type ConnScoper interface {
// Scope returns the user view of this connection's resource scope
Scope() ConnScope
}

View File

@@ -0,0 +1,110 @@
package network
import (
"context"
"time"
)
// DialPeerTimeout is the default timeout for a single call to `DialPeer`. When
// there are multiple concurrent calls to `DialPeer`, this timeout will apply to
// each independently.
var DialPeerTimeout = 60 * time.Second
type noDialCtxKey struct{}
type dialPeerTimeoutCtxKey struct{}
type forceDirectDialCtxKey struct{}
type useTransientCtxKey struct{}
type simConnectCtxKey struct{ isClient bool }
var noDial = noDialCtxKey{}
var forceDirectDial = forceDirectDialCtxKey{}
var useTransient = useTransientCtxKey{}
var simConnectIsServer = simConnectCtxKey{}
var simConnectIsClient = simConnectCtxKey{isClient: true}
// EXPERIMENTAL
// WithForceDirectDial constructs a new context with an option that instructs the network
// to attempt to force a direct connection to a peer via a dial even if a proxied connection to it already exists.
func WithForceDirectDial(ctx context.Context, reason string) context.Context {
return context.WithValue(ctx, forceDirectDial, reason)
}
// EXPERIMENTAL
// GetForceDirectDial returns true if the force direct dial option is set in the context.
func GetForceDirectDial(ctx context.Context) (forceDirect bool, reason string) {
v := ctx.Value(forceDirectDial)
if v != nil {
return true, v.(string)
}
return false, ""
}
// WithSimultaneousConnect constructs a new context with an option that instructs the transport
// to apply hole punching logic where applicable.
// EXPERIMENTAL
func WithSimultaneousConnect(ctx context.Context, isClient bool, reason string) context.Context {
if isClient {
return context.WithValue(ctx, simConnectIsClient, reason)
}
return context.WithValue(ctx, simConnectIsServer, reason)
}
// GetSimultaneousConnect returns true if the simultaneous connect option is set in the context.
// EXPERIMENTAL
func GetSimultaneousConnect(ctx context.Context) (simconnect bool, isClient bool, reason string) {
if v := ctx.Value(simConnectIsClient); v != nil {
return true, true, v.(string)
}
if v := ctx.Value(simConnectIsServer); v != nil {
return true, false, v.(string)
}
return false, false, ""
}
// WithNoDial constructs a new context with an option that instructs the network
// to not attempt a new dial when opening a stream.
func WithNoDial(ctx context.Context, reason string) context.Context {
return context.WithValue(ctx, noDial, reason)
}
// GetNoDial returns true if the no dial option is set in the context.
func GetNoDial(ctx context.Context) (nodial bool, reason string) {
v := ctx.Value(noDial)
if v != nil {
return true, v.(string)
}
return false, ""
}
// GetDialPeerTimeout returns the current DialPeer timeout (or the default).
func GetDialPeerTimeout(ctx context.Context) time.Duration {
if to, ok := ctx.Value(dialPeerTimeoutCtxKey{}).(time.Duration); ok {
return to
}
return DialPeerTimeout
}
// WithDialPeerTimeout returns a new context with the DialPeer timeout applied.
//
// This timeout overrides the default DialPeerTimeout and applies per-dial
// independently.
func WithDialPeerTimeout(ctx context.Context, timeout time.Duration) context.Context {
return context.WithValue(ctx, dialPeerTimeoutCtxKey{}, timeout)
}
// WithUseTransient constructs a new context with an option that instructs the network
// that it is acceptable to use a transient connection when opening a new stream.
func WithUseTransient(ctx context.Context, reason string) context.Context {
return context.WithValue(ctx, useTransient, reason)
}
// GetUseTransient returns true if the use transient option is set in the context.
func GetUseTransient(ctx context.Context) (usetransient bool, reason string) {
v := ctx.Value(useTransient)
if v != nil {
return true, v.(string)
}
return false, ""
}

View File

@@ -0,0 +1,33 @@
package network
import (
"errors"
"net"
)
type temporaryError string
func (e temporaryError) Error() string { return string(e) }
func (e temporaryError) Temporary() bool { return true }
func (e temporaryError) Timeout() bool { return false }
var _ net.Error = temporaryError("")
// ErrNoRemoteAddrs is returned when there are no addresses associated with a peer during a dial.
var ErrNoRemoteAddrs = errors.New("no remote addresses")
// ErrNoConn is returned when attempting to open a stream to a peer with the NoDial
// option and no usable connection is available.
var ErrNoConn = errors.New("no usable connection to peer")
// ErrTransientConn is returned when attempting to open a stream to a peer with only a transient
// connection, without specifying the UseTransient option.
var ErrTransientConn = errors.New("transient connection to peer")
// ErrResourceLimitExceeded is returned when attempting to perform an operation that would
// exceed system resource limits.
var ErrResourceLimitExceeded = temporaryError("resource limit exceeded")
// ErrResourceScopeClosed is returned when attempting to reserve resources in a closed resource
// scope.
var ErrResourceScopeClosed = errors.New("resource scope closed")

95
vendor/github.com/libp2p/go-libp2p/core/network/mux.go generated vendored Normal file
View File

@@ -0,0 +1,95 @@
package network
import (
"context"
"errors"
"io"
"net"
"time"
)
// ErrReset is returned when reading or writing on a reset stream.
var ErrReset = errors.New("stream reset")
// MuxedStream is a bidirectional io pipe within a connection.
type MuxedStream interface {
io.Reader
io.Writer
// Close closes the stream.
//
// * Any buffered data for writing will be flushed.
// * Future reads will fail.
// * Any in-progress reads/writes will be interrupted.
//
// Close may be asynchronous and _does not_ guarantee receipt of the
// data.
//
// Close closes the stream for both reading and writing.
// Close is equivalent to calling `CloseRead` and `CloseWrite`. Importantly, Close will not wait for any form of acknowledgment.
// If acknowledgment is required, the caller must call `CloseWrite`, then wait on the stream for a response (or an EOF),
// then call Close() to free the stream object.
//
// When done with a stream, the user must call either Close() or `Reset()` to discard the stream, even after calling `CloseRead` and/or `CloseWrite`.
io.Closer
// CloseWrite closes the stream for writing but leaves it open for
// reading.
//
// CloseWrite does not free the stream, users must still call Close or
// Reset.
CloseWrite() error
// CloseRead closes the stream for reading but leaves it open for
// writing.
//
// When CloseRead is called, all in-progress Read calls are interrupted with a non-EOF error and
// no further calls to Read will succeed.
//
// The handling of new incoming data on the stream after calling this function is implementation defined.
//
// CloseRead does not free the stream, users must still call Close or
// Reset.
CloseRead() error
// Reset closes both ends of the stream. Use this to tell the remote
// side to hang up and go away.
Reset() error
SetDeadline(time.Time) error
SetReadDeadline(time.Time) error
SetWriteDeadline(time.Time) error
}
// MuxedConn represents a connection to a remote peer that has been
// extended to support stream multiplexing.
//
// A MuxedConn allows a single net.Conn connection to carry many logically
// independent bidirectional streams of binary data.
//
// Together with network.ConnSecurity, MuxedConn is a component of the
// transport.CapableConn interface, which represents a "raw" network
// connection that has been "upgraded" to support the libp2p capabilities
// of secure communication and stream multiplexing.
type MuxedConn interface {
// Close closes the stream muxer and the the underlying net.Conn.
io.Closer
// IsClosed returns whether a connection is fully closed, so it can
// be garbage collected.
IsClosed() bool
// OpenStream creates a new stream.
OpenStream(context.Context) (MuxedStream, error)
// AcceptStream accepts a stream opened by the other side.
AcceptStream() (MuxedStream, error)
}
// Multiplexer wraps a net.Conn with a stream multiplexing
// implementation and returns a MuxedConn that supports opening
// multiple streams over the underlying net.Conn
type Multiplexer interface {
// NewConn constructs a new connection
NewConn(c net.Conn, isServer bool, scope PeerScope) (MuxedConn, error)
}

View File

@@ -0,0 +1,58 @@
package network
// NATDeviceType indicates the type of the NAT device.
type NATDeviceType int
const (
// NATDeviceTypeUnknown indicates that the type of the NAT device is unknown.
NATDeviceTypeUnknown NATDeviceType = iota
// NATDeviceTypeCone indicates that the NAT device is a Cone NAT.
// A Cone NAT is a NAT where all outgoing connections from the same source IP address and port are mapped by the NAT device
// to the same IP address and port irrespective of the destination address.
// With regards to RFC 3489, this could be either a Full Cone NAT, a Restricted Cone NAT or a
// Port Restricted Cone NAT. However, we do NOT differentiate between them here and simply classify all such NATs as a Cone NAT.
// NAT traversal with hole punching is possible with a Cone NAT ONLY if the remote peer is ALSO behind a Cone NAT.
// If the remote peer is behind a Symmetric NAT, hole punching will fail.
NATDeviceTypeCone
// NATDeviceTypeSymmetric indicates that the NAT device is a Symmetric NAT.
// A Symmetric NAT maps outgoing connections with different destination addresses to different IP addresses and ports,
// even if they originate from the same source IP address and port.
// NAT traversal with hole-punching is currently NOT possible in libp2p with Symmetric NATs irrespective of the remote peer's NAT type.
NATDeviceTypeSymmetric
)
func (r NATDeviceType) String() string {
switch r {
case 0:
return "Unknown"
case 1:
return "Cone"
case 2:
return "Symmetric"
default:
return "unrecognized"
}
}
// NATTransportProtocol is the transport protocol for which the NAT Device Type has been determined.
type NATTransportProtocol int
const (
// NATTransportUDP means that the NAT Device Type has been determined for the UDP Protocol.
NATTransportUDP NATTransportProtocol = iota
// NATTransportTCP means that the NAT Device Type has been determined for the TCP Protocol.
NATTransportTCP
)
func (n NATTransportProtocol) String() string {
switch n {
case 0:
return "UDP"
case 1:
return "TCP"
default:
return "unrecognized"
}
}

View File

@@ -0,0 +1,198 @@
// Package network provides core networking abstractions for libp2p.
//
// The network package provides the high-level Network interface for interacting
// with other libp2p peers, which is the primary public API for initiating and
// accepting connections to remote peers.
package network
import (
"context"
"io"
"time"
"github.com/libp2p/go-libp2p/core/peer"
"github.com/libp2p/go-libp2p/core/peerstore"
ma "github.com/multiformats/go-multiaddr"
)
// MessageSizeMax is a soft (recommended) maximum for network messages.
// One can write more, as the interface is a stream. But it is useful
// to bunch it up into multiple read/writes when the whole message is
// a single, large serialized object.
const MessageSizeMax = 1 << 22 // 4 MB
// Direction represents which peer in a stream initiated a connection.
type Direction int
const (
// DirUnknown is the default direction.
DirUnknown Direction = iota
// DirInbound is for when the remote peer initiated a connection.
DirInbound
// DirOutbound is for when the local peer initiated a connection.
DirOutbound
)
const unrecognized = "(unrecognized)"
func (d Direction) String() string {
str := [...]string{"Unknown", "Inbound", "Outbound"}
if d < 0 || int(d) >= len(str) {
return unrecognized
}
return str[d]
}
// Connectedness signals the capacity for a connection with a given node.
// It is used to signal to services and other peers whether a node is reachable.
type Connectedness int
const (
// NotConnected means no connection to peer, and no extra information (default)
NotConnected Connectedness = iota
// Connected means has an open, live connection to peer
Connected
// CanConnect means recently connected to peer, terminated gracefully
CanConnect
// CannotConnect means recently attempted connecting but failed to connect.
// (should signal "made effort, failed")
CannotConnect
)
func (c Connectedness) String() string {
str := [...]string{"NotConnected", "Connected", "CanConnect", "CannotConnect"}
if c < 0 || int(c) >= len(str) {
return unrecognized
}
return str[c]
}
// Reachability indicates how reachable a node is.
type Reachability int
const (
// ReachabilityUnknown indicates that the reachability status of the
// node is unknown.
ReachabilityUnknown Reachability = iota
// ReachabilityPublic indicates that the node is reachable from the
// public internet.
ReachabilityPublic
// ReachabilityPrivate indicates that the node is not reachable from the
// public internet.
//
// NOTE: This node may _still_ be reachable via relays.
ReachabilityPrivate
)
func (r Reachability) String() string {
str := [...]string{"Unknown", "Public", "Private"}
if r < 0 || int(r) >= len(str) {
return unrecognized
}
return str[r]
}
// ConnStats stores metadata pertaining to a given Conn.
type ConnStats struct {
Stats
// NumStreams is the number of streams on the connection.
NumStreams int
}
// Stats stores metadata pertaining to a given Stream / Conn.
type Stats struct {
// Direction specifies whether this is an inbound or an outbound connection.
Direction Direction
// Opened is the timestamp when this connection was opened.
Opened time.Time
// Transient indicates that this connection is transient and may be closed soon.
Transient bool
// Extra stores additional metadata about this connection.
Extra map[interface{}]interface{}
}
// StreamHandler is the type of function used to listen for
// streams opened by the remote side.
type StreamHandler func(Stream)
// Network is the interface used to connect to the outside world.
// It dials and listens for connections. it uses a Swarm to pool
// connections (see swarm pkg, and peerstream.Swarm). Connections
// are encrypted with a TLS-like protocol.
type Network interface {
Dialer
io.Closer
// SetStreamHandler sets the handler for new streams opened by the
// remote side. This operation is thread-safe.
SetStreamHandler(StreamHandler)
// NewStream returns a new stream to given peer p.
// If there is no connection to p, attempts to create one.
NewStream(context.Context, peer.ID) (Stream, error)
// Listen tells the network to start listening on given multiaddrs.
Listen(...ma.Multiaddr) error
// ListenAddresses returns a list of addresses at which this network listens.
ListenAddresses() []ma.Multiaddr
// InterfaceListenAddresses returns a list of addresses at which this network
// listens. It expands "any interface" addresses (/ip4/0.0.0.0, /ip6/::) to
// use the known local interfaces.
InterfaceListenAddresses() ([]ma.Multiaddr, error)
// ResourceManager returns the ResourceManager associated with this network
ResourceManager() ResourceManager
}
// Dialer represents a service that can dial out to peers
// (this is usually just a Network, but other services may not need the whole
// stack, and thus it becomes easier to mock)
type Dialer interface {
// Peerstore returns the internal peerstore
// This is useful to tell the dialer about a new address for a peer.
// Or use one of the public keys found out over the network.
Peerstore() peerstore.Peerstore
// LocalPeer returns the local peer associated with this network
LocalPeer() peer.ID
// DialPeer establishes a connection to a given peer
DialPeer(context.Context, peer.ID) (Conn, error)
// ClosePeer closes the connection to a given peer
ClosePeer(peer.ID) error
// Connectedness returns a state signaling connection capabilities
Connectedness(peer.ID) Connectedness
// Peers returns the peers connected
Peers() []peer.ID
// Conns returns the connections in this Network
Conns() []Conn
// ConnsToPeer returns the connections in this Network for given peer.
ConnsToPeer(p peer.ID) []Conn
// Notify/StopNotify register and unregister a notifiee for signals
Notify(Notifiee)
StopNotify(Notifiee)
}
// AddrDelay provides an address along with the delay after which the address
// should be dialed
type AddrDelay struct {
Addr ma.Multiaddr
Delay time.Duration
}
// DialRanker provides a schedule of dialing the provided addresses
type DialRanker func([]ma.Multiaddr) []AddrDelay

View File

@@ -0,0 +1,67 @@
package network
import (
ma "github.com/multiformats/go-multiaddr"
)
// Notifiee is an interface for an object wishing to receive
// notifications from a Network.
type Notifiee interface {
Listen(Network, ma.Multiaddr) // called when network starts listening on an addr
ListenClose(Network, ma.Multiaddr) // called when network stops listening on an addr
Connected(Network, Conn) // called when a connection opened
Disconnected(Network, Conn) // called when a connection closed
}
// NotifyBundle implements Notifiee by calling any of the functions set on it,
// and nop'ing if they are unset. This is the easy way to register for
// notifications.
type NotifyBundle struct {
ListenF func(Network, ma.Multiaddr)
ListenCloseF func(Network, ma.Multiaddr)
ConnectedF func(Network, Conn)
DisconnectedF func(Network, Conn)
}
var _ Notifiee = (*NotifyBundle)(nil)
// Listen calls ListenF if it is not null.
func (nb *NotifyBundle) Listen(n Network, a ma.Multiaddr) {
if nb.ListenF != nil {
nb.ListenF(n, a)
}
}
// ListenClose calls ListenCloseF if it is not null.
func (nb *NotifyBundle) ListenClose(n Network, a ma.Multiaddr) {
if nb.ListenCloseF != nil {
nb.ListenCloseF(n, a)
}
}
// Connected calls ConnectedF if it is not null.
func (nb *NotifyBundle) Connected(n Network, c Conn) {
if nb.ConnectedF != nil {
nb.ConnectedF(n, c)
}
}
// Disconnected calls DisconnectedF if it is not null.
func (nb *NotifyBundle) Disconnected(n Network, c Conn) {
if nb.DisconnectedF != nil {
nb.DisconnectedF(n, c)
}
}
// Global noop notifiee. Do not change.
var GlobalNoopNotifiee = &NoopNotifiee{}
type NoopNotifiee struct{}
var _ Notifiee = (*NoopNotifiee)(nil)
func (nn *NoopNotifiee) Connected(n Network, c Conn) {}
func (nn *NoopNotifiee) Disconnected(n Network, c Conn) {}
func (nn *NoopNotifiee) Listen(n Network, addr ma.Multiaddr) {}
func (nn *NoopNotifiee) ListenClose(n Network, addr ma.Multiaddr) {}

View File

@@ -0,0 +1,326 @@
package network
import (
"github.com/libp2p/go-libp2p/core/peer"
"github.com/libp2p/go-libp2p/core/protocol"
"github.com/multiformats/go-multiaddr"
)
// ResourceManager is the interface to the network resource management subsystem.
// The ResourceManager tracks and accounts for resource usage in the stack, from the internals
// to the application, and provides a mechanism to limit resource usage according to a user
// configurable policy.
//
// Resource Management through the ResourceManager is based on the concept of Resource
// Management Scopes, whereby resource usage is constrained by a DAG of scopes,
// The following diagram illustrates the structure of the resource constraint DAG:
// System
//
// +------------> Transient.............+................+
// | . .
// +------------> Service------------- . ----------+ .
// | . | .
// +-------------> Protocol----------- . ----------+ .
// | . | .
// +--------------> Peer \ | .
// +------------> Connection | .
// | \ \
// +---------------------------> Stream
//
// The basic resources accounted by the ResourceManager include memory, streams, connections,
// and file descriptors. These account for both space and time used by
// the stack, as each resource has a direct effect on the system
// availability and performance.
//
// The modus operandi of the resource manager is to restrict resource usage at the time of
// reservation. When a component of the stack needs to use a resource, it reserves it in the
// appropriate scope. The resource manager gates the reservation against the scope applicable
// limits; if the limit is exceeded, then an error (wrapping ErrResourceLimitExceeded) and it
// is up the component to act accordingly. At the lower levels of the stack, this will normally
// signal a failure of some sorts, like failing to opening a stream or a connection, which will
// propagate to the programmer. Some components may be able to handle resource reservation failure
// more gracefully; for instance a muxer trying to grow a buffer for a window change, will simply
// retain the existing window size and continue to operate normally albeit with some degraded
// throughput.
// All resources reserved in some scope are released when the scope is closed. For low level
// scopes, mainly Connection and Stream scopes, this happens when the connection or stream is
// closed.
//
// Service programmers will typically use the resource manager to reserve memory
// for their subsystem.
// This happens with two avenues: the programmer can attach a stream to a service, whereby
// resources reserved by the stream are automatically accounted in the service budget; or the
// programmer may directly interact with the service scope, by using ViewService through the
// resource manager interface.
//
// Application programmers can also directly reserve memory in some applicable scope. In order
// to facilitate control flow delimited resource accounting, all scopes defined in the system
// allow for the user to create spans. Spans are temporary scopes rooted at some
// other scope and release their resources when the programmer is done with them. Span
// scopes can form trees, with nested spans.
//
// Typical Usage:
// - Low level components of the system (transports, muxers) all have access to the resource
// manager and create connection and stream scopes through it. These scopes are accessible
// to the user, albeit with a narrower interface, through Conn and Stream objects who have
// a Scope method.
// - Services typically center around streams, where the programmer can attach streams to a
// particular service. They can also directly reserve memory for a service by accessing the
// service scope using the ResourceManager interface.
// - Applications that want to account for their network resource usage can reserve memory,
// typically using a span, directly in the System or a Service scope; they can also
// opt to use appropriate stream scopes for streams that they create or own.
//
// User Serviceable Parts: the user has the option to specify their own implementation of the
// interface. We provide a canonical implementation in the go-libp2p-resource-manager package.
// The user of that package can specify limits for the various scopes, which can be static
// or dynamic.
//
// WARNING The ResourceManager interface is considered experimental and subject to change
// in subsequent releases.
type ResourceManager interface {
ResourceScopeViewer
// OpenConnection creates a new connection scope not yet associated with any peer; the connection
// is scoped at the transient scope.
// The caller owns the returned scope and is responsible for calling Done in order to signify
// the end of the scope's span.
OpenConnection(dir Direction, usefd bool, endpoint multiaddr.Multiaddr) (ConnManagementScope, error)
// OpenStream creates a new stream scope, initially unnegotiated.
// An unnegotiated stream will be initially unattached to any protocol scope
// and constrained by the transient scope.
// The caller owns the returned scope and is responsible for calling Done in order to signify
// the end of th scope's span.
OpenStream(p peer.ID, dir Direction) (StreamManagementScope, error)
// Close closes the resource manager
Close() error
}
// ResourceScopeViewer is a mixin interface providing view methods for accessing top level
// scopes.
type ResourceScopeViewer interface {
// ViewSystem views the system-wide resource scope.
// The system scope is the top level scope that accounts for global
// resource usage at all levels of the system. This scope constrains all
// other scopes and institutes global hard limits.
ViewSystem(func(ResourceScope) error) error
// ViewTransient views the transient (DMZ) resource scope.
// The transient scope accounts for resources that are in the process of
// full establishment. For instance, a new connection prior to the
// handshake does not belong to any peer, but it still needs to be
// constrained as this opens an avenue for attacks in transient resource
// usage. Similarly, a stream that has not negotiated a protocol yet is
// constrained by the transient scope.
ViewTransient(func(ResourceScope) error) error
// ViewService retrieves a service-specific scope.
ViewService(string, func(ServiceScope) error) error
// ViewProtocol views the resource management scope for a specific protocol.
ViewProtocol(protocol.ID, func(ProtocolScope) error) error
// ViewPeer views the resource management scope for a specific peer.
ViewPeer(peer.ID, func(PeerScope) error) error
}
const (
// ReservationPriorityLow is a reservation priority that indicates a reservation if the scope
// memory utilization is at 40% or less.
ReservationPriorityLow uint8 = 101
// Reservation PriorityMedium is a reservation priority that indicates a reservation if the scope
// memory utilization is at 60% or less.
ReservationPriorityMedium uint8 = 152
// ReservationPriorityHigh is a reservation priority that indicates a reservation if the scope
// memory utilization is at 80% or less.
ReservationPriorityHigh uint8 = 203
// ReservationPriorityAlways is a reservation priority that indicates a reservation if there is
// enough memory, regardless of scope utilization.
ReservationPriorityAlways uint8 = 255
)
// ResourceScope is the interface for all scopes.
type ResourceScope interface {
// ReserveMemory reserves memory/buffer space in the scope; the unit is bytes.
//
// If ReserveMemory returns an error, then no memory was reserved and the caller should handle
// the failure condition.
//
// The priority argument indicates the priority of the memory reservation. A reservation
// will fail if the available memory is less than (1+prio)/256 of the scope limit, providing
// a mechanism to gracefully handle optional reservations that might overload the system.
// For instance, a muxer growing a window buffer will use a low priority and only grow the buffer
// if there is no memory pressure in the system.
//
// There are 4 predefined priority levels, Low, Medium, High and Always,
// capturing common patterns, but the user is free to use any granularity applicable to his case.
ReserveMemory(size int, prio uint8) error
// ReleaseMemory explicitly releases memory previously reserved with ReserveMemory
ReleaseMemory(size int)
// Stat retrieves current resource usage for the scope.
Stat() ScopeStat
// BeginSpan creates a new span scope rooted at this scope
BeginSpan() (ResourceScopeSpan, error)
}
// ResourceScopeSpan is a ResourceScope with a delimited span.
// Span scopes are control flow delimited and release all their associated resources
// when the programmer calls Done.
//
// Example:
//
// s, err := someScope.BeginSpan()
// if err != nil { ... }
// defer s.Done()
//
// if err := s.ReserveMemory(...); err != nil { ... }
// // ... use memory
type ResourceScopeSpan interface {
ResourceScope
// Done ends the span and releases associated resources.
Done()
}
// ServiceScope is the interface for service resource scopes
type ServiceScope interface {
ResourceScope
// Name returns the name of this service
Name() string
}
// ProtocolScope is the interface for protocol resource scopes.
type ProtocolScope interface {
ResourceScope
// Protocol returns the protocol for this scope
Protocol() protocol.ID
}
// PeerScope is the interface for peer resource scopes.
type PeerScope interface {
ResourceScope
// Peer returns the peer ID for this scope
Peer() peer.ID
}
// ConnManagementScope is the low level interface for connection resource scopes.
// This interface is used by the low level components of the system who create and own
// the span of a connection scope.
type ConnManagementScope interface {
ResourceScopeSpan
// PeerScope returns the peer scope associated with this connection.
// It returns nil if the connection is not yet associated with any peer.
PeerScope() PeerScope
// SetPeer sets the peer for a previously unassociated connection
SetPeer(peer.ID) error
}
// ConnScope is the user view of a connection scope
type ConnScope interface {
ResourceScope
}
// StreamManagementScope is the interface for stream resource scopes.
// This interface is used by the low level components of the system who create and own
// the span of a stream scope.
type StreamManagementScope interface {
ResourceScopeSpan
// ProtocolScope returns the protocol resource scope associated with this stream.
// It returns nil if the stream is not associated with any protocol scope.
ProtocolScope() ProtocolScope
// SetProtocol sets the protocol for a previously unnegotiated stream
SetProtocol(proto protocol.ID) error
// ServiceScope returns the service owning the stream, if any.
ServiceScope() ServiceScope
// SetService sets the service owning this stream.
SetService(srv string) error
// PeerScope returns the peer resource scope associated with this stream.
PeerScope() PeerScope
}
// StreamScope is the user view of a StreamScope.
type StreamScope interface {
ResourceScope
// SetService sets the service owning this stream.
SetService(srv string) error
}
// ScopeStat is a struct containing resource accounting information.
type ScopeStat struct {
NumStreamsInbound int
NumStreamsOutbound int
NumConnsInbound int
NumConnsOutbound int
NumFD int
Memory int64
}
// NullResourceManager is a stub for tests and initialization of default values
type NullResourceManager struct{}
var _ ResourceScope = (*NullScope)(nil)
var _ ResourceScopeSpan = (*NullScope)(nil)
var _ ServiceScope = (*NullScope)(nil)
var _ ProtocolScope = (*NullScope)(nil)
var _ PeerScope = (*NullScope)(nil)
var _ ConnManagementScope = (*NullScope)(nil)
var _ ConnScope = (*NullScope)(nil)
var _ StreamManagementScope = (*NullScope)(nil)
var _ StreamScope = (*NullScope)(nil)
// NullScope is a stub for tests and initialization of default values
type NullScope struct{}
func (n *NullResourceManager) ViewSystem(f func(ResourceScope) error) error {
return f(&NullScope{})
}
func (n *NullResourceManager) ViewTransient(f func(ResourceScope) error) error {
return f(&NullScope{})
}
func (n *NullResourceManager) ViewService(svc string, f func(ServiceScope) error) error {
return f(&NullScope{})
}
func (n *NullResourceManager) ViewProtocol(p protocol.ID, f func(ProtocolScope) error) error {
return f(&NullScope{})
}
func (n *NullResourceManager) ViewPeer(p peer.ID, f func(PeerScope) error) error {
return f(&NullScope{})
}
func (n *NullResourceManager) OpenConnection(dir Direction, usefd bool, endpoint multiaddr.Multiaddr) (ConnManagementScope, error) {
return &NullScope{}, nil
}
func (n *NullResourceManager) OpenStream(p peer.ID, dir Direction) (StreamManagementScope, error) {
return &NullScope{}, nil
}
func (n *NullResourceManager) Close() error {
return nil
}
func (n *NullScope) ReserveMemory(size int, prio uint8) error { return nil }
func (n *NullScope) ReleaseMemory(size int) {}
func (n *NullScope) Stat() ScopeStat { return ScopeStat{} }
func (n *NullScope) BeginSpan() (ResourceScopeSpan, error) { return &NullScope{}, nil }
func (n *NullScope) Done() {}
func (n *NullScope) Name() string { return "" }
func (n *NullScope) Protocol() protocol.ID { return "" }
func (n *NullScope) Peer() peer.ID { return "" }
func (n *NullScope) PeerScope() PeerScope { return &NullScope{} }
func (n *NullScope) SetPeer(peer.ID) error { return nil }
func (n *NullScope) ProtocolScope() ProtocolScope { return &NullScope{} }
func (n *NullScope) SetProtocol(proto protocol.ID) error { return nil }
func (n *NullScope) ServiceScope() ServiceScope { return &NullScope{} }
func (n *NullScope) SetService(srv string) error { return nil }

View File

@@ -0,0 +1,30 @@
package network
import (
"github.com/libp2p/go-libp2p/core/protocol"
)
// Stream represents a bidirectional channel between two agents in
// a libp2p network. "agent" is as granular as desired, potentially
// being a "request -> reply" pair, or whole protocols.
//
// Streams are backed by a multiplexer underneath the hood.
type Stream interface {
MuxedStream
// ID returns an identifier that uniquely identifies this Stream within this
// host, during this run. Stream IDs may repeat across restarts.
ID() string
Protocol() protocol.ID
SetProtocol(id protocol.ID) error
// Stat returns metadata pertaining to this stream.
Stat() Stats
// Conn returns the connection this stream is part of.
Conn() Conn
// Scope returns the user's view of this stream's resource scope
Scope() StreamScope
}

View File

@@ -0,0 +1,117 @@
package peer
import (
"fmt"
ma "github.com/multiformats/go-multiaddr"
)
// AddrInfo is a small struct used to pass around a peer with
// a set of addresses (and later, keys?).
type AddrInfo struct {
ID ID
Addrs []ma.Multiaddr
}
var _ fmt.Stringer = AddrInfo{}
func (pi AddrInfo) String() string {
return fmt.Sprintf("{%v: %v}", pi.ID, pi.Addrs)
}
var ErrInvalidAddr = fmt.Errorf("invalid p2p multiaddr")
// AddrInfosFromP2pAddrs converts a set of Multiaddrs to a set of AddrInfos.
func AddrInfosFromP2pAddrs(maddrs ...ma.Multiaddr) ([]AddrInfo, error) {
m := make(map[ID][]ma.Multiaddr)
for _, maddr := range maddrs {
transport, id := SplitAddr(maddr)
if id == "" {
return nil, ErrInvalidAddr
}
if transport == nil {
if _, ok := m[id]; !ok {
m[id] = nil
}
} else {
m[id] = append(m[id], transport)
}
}
ais := make([]AddrInfo, 0, len(m))
for id, maddrs := range m {
ais = append(ais, AddrInfo{ID: id, Addrs: maddrs})
}
return ais, nil
}
// SplitAddr splits a p2p Multiaddr into a transport multiaddr and a peer ID.
//
// * Returns a nil transport if the address only contains a /p2p part.
// * Returns an empty peer ID if the address doesn't contain a /p2p part.
func SplitAddr(m ma.Multiaddr) (transport ma.Multiaddr, id ID) {
if m == nil {
return nil, ""
}
transport, p2ppart := ma.SplitLast(m)
if p2ppart == nil || p2ppart.Protocol().Code != ma.P_P2P {
return m, ""
}
id = ID(p2ppart.RawValue()) // already validated by the multiaddr library.
return transport, id
}
// AddrInfoFromString builds an AddrInfo from the string representation of a Multiaddr
func AddrInfoFromString(s string) (*AddrInfo, error) {
a, err := ma.NewMultiaddr(s)
if err != nil {
return nil, err
}
return AddrInfoFromP2pAddr(a)
}
// AddrInfoFromP2pAddr converts a Multiaddr to an AddrInfo.
func AddrInfoFromP2pAddr(m ma.Multiaddr) (*AddrInfo, error) {
transport, id := SplitAddr(m)
if id == "" {
return nil, ErrInvalidAddr
}
info := &AddrInfo{ID: id}
if transport != nil {
info.Addrs = []ma.Multiaddr{transport}
}
return info, nil
}
// AddrInfoToP2pAddrs converts an AddrInfo to a list of Multiaddrs.
func AddrInfoToP2pAddrs(pi *AddrInfo) ([]ma.Multiaddr, error) {
p2ppart, err := ma.NewComponent("p2p", Encode(pi.ID))
if err != nil {
return nil, err
}
if len(pi.Addrs) == 0 {
return []ma.Multiaddr{p2ppart}, nil
}
addrs := make([]ma.Multiaddr, 0, len(pi.Addrs))
for _, addr := range pi.Addrs {
addrs = append(addrs, addr.Encapsulate(p2ppart))
}
return addrs, nil
}
func (pi *AddrInfo) Loggable() map[string]interface{} {
return map[string]interface{}{
"peerID": pi.ID.Pretty(),
"addrs": pi.Addrs,
}
}
// AddrInfosToIDs extracts the peer IDs from the passed AddrInfos and returns them in-order.
func AddrInfosToIDs(pis []AddrInfo) []ID {
ps := make([]ID, len(pis))
for i, pi := range pis {
ps[i] = pi.ID
}
return ps
}

View File

@@ -0,0 +1,48 @@
package peer
import (
"encoding/json"
"github.com/libp2p/go-libp2p/core/internal/catch"
ma "github.com/multiformats/go-multiaddr"
)
// Helper struct for decoding as we can't unmarshal into an interface (Multiaddr).
type addrInfoJson struct {
ID ID
Addrs []string
}
func (pi AddrInfo) MarshalJSON() (res []byte, err error) {
defer func() { catch.HandlePanic(recover(), &err, "libp2p addr info marshal") }()
addrs := make([]string, len(pi.Addrs))
for i, addr := range pi.Addrs {
addrs[i] = addr.String()
}
return json.Marshal(&addrInfoJson{
ID: pi.ID,
Addrs: addrs,
})
}
func (pi *AddrInfo) UnmarshalJSON(b []byte) (err error) {
defer func() { catch.HandlePanic(recover(), &err, "libp2p addr info unmarshal") }()
var data addrInfoJson
if err := json.Unmarshal(b, &data); err != nil {
return err
}
addrs := make([]ma.Multiaddr, len(data.Addrs))
for i, addr := range data.Addrs {
maddr, err := ma.NewMultiaddr(addr)
if err != nil {
return err
}
addrs[i] = maddr
}
pi.ID = data.ID
pi.Addrs = addrs
return nil
}

View File

@@ -0,0 +1,239 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.30.0
// protoc v3.21.12
// source: pb/peer_record.proto
package pb
import (
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
// PeerRecord messages contain information that is useful to share with other peers.
// 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 designed to be serialized to bytes and placed inside of
// SignedEnvelopes before sharing with other peers.
// See https://github.com/libp2p/go-libp2p/core/record/pb/envelope.proto for
// the SignedEnvelope definition.
type PeerRecord struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
// peer_id contains a libp2p peer id in its binary representation.
PeerId []byte `protobuf:"bytes,1,opt,name=peer_id,json=peerId,proto3" json:"peer_id,omitempty"`
// seq contains a monotonically-increasing sequence counter to order PeerRecords in time.
Seq uint64 `protobuf:"varint,2,opt,name=seq,proto3" json:"seq,omitempty"`
// addresses is a list of public listen addresses for the peer.
Addresses []*PeerRecord_AddressInfo `protobuf:"bytes,3,rep,name=addresses,proto3" json:"addresses,omitempty"`
}
func (x *PeerRecord) Reset() {
*x = PeerRecord{}
if protoimpl.UnsafeEnabled {
mi := &file_pb_peer_record_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *PeerRecord) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*PeerRecord) ProtoMessage() {}
func (x *PeerRecord) ProtoReflect() protoreflect.Message {
mi := &file_pb_peer_record_proto_msgTypes[0]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use PeerRecord.ProtoReflect.Descriptor instead.
func (*PeerRecord) Descriptor() ([]byte, []int) {
return file_pb_peer_record_proto_rawDescGZIP(), []int{0}
}
func (x *PeerRecord) GetPeerId() []byte {
if x != nil {
return x.PeerId
}
return nil
}
func (x *PeerRecord) GetSeq() uint64 {
if x != nil {
return x.Seq
}
return 0
}
func (x *PeerRecord) GetAddresses() []*PeerRecord_AddressInfo {
if x != nil {
return x.Addresses
}
return nil
}
// AddressInfo is a wrapper around a binary multiaddr. It is defined as a
// separate message to allow us to add per-address metadata in the future.
type PeerRecord_AddressInfo struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Multiaddr []byte `protobuf:"bytes,1,opt,name=multiaddr,proto3" json:"multiaddr,omitempty"`
}
func (x *PeerRecord_AddressInfo) Reset() {
*x = PeerRecord_AddressInfo{}
if protoimpl.UnsafeEnabled {
mi := &file_pb_peer_record_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *PeerRecord_AddressInfo) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*PeerRecord_AddressInfo) ProtoMessage() {}
func (x *PeerRecord_AddressInfo) ProtoReflect() protoreflect.Message {
mi := &file_pb_peer_record_proto_msgTypes[1]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use PeerRecord_AddressInfo.ProtoReflect.Descriptor instead.
func (*PeerRecord_AddressInfo) Descriptor() ([]byte, []int) {
return file_pb_peer_record_proto_rawDescGZIP(), []int{0, 0}
}
func (x *PeerRecord_AddressInfo) GetMultiaddr() []byte {
if x != nil {
return x.Multiaddr
}
return nil
}
var File_pb_peer_record_proto protoreflect.FileDescriptor
var file_pb_peer_record_proto_rawDesc = []byte{
0x0a, 0x14, 0x70, 0x62, 0x2f, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64,
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x07, 0x70, 0x65, 0x65, 0x72, 0x2e, 0x70, 0x62, 0x22,
0xa3, 0x01, 0x0a, 0x0a, 0x50, 0x65, 0x65, 0x72, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x12, 0x17,
0x0a, 0x07, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52,
0x06, 0x70, 0x65, 0x65, 0x72, 0x49, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x73, 0x65, 0x71, 0x18, 0x02,
0x20, 0x01, 0x28, 0x04, 0x52, 0x03, 0x73, 0x65, 0x71, 0x12, 0x3d, 0x0a, 0x09, 0x61, 0x64, 0x64,
0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x70,
0x65, 0x65, 0x72, 0x2e, 0x70, 0x62, 0x2e, 0x50, 0x65, 0x65, 0x72, 0x52, 0x65, 0x63, 0x6f, 0x72,
0x64, 0x2e, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x09, 0x61,
0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x1a, 0x2b, 0x0a, 0x0b, 0x41, 0x64, 0x64, 0x72,
0x65, 0x73, 0x73, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1c, 0x0a, 0x09, 0x6d, 0x75, 0x6c, 0x74, 0x69,
0x61, 0x64, 0x64, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x6d, 0x75, 0x6c, 0x74,
0x69, 0x61, 0x64, 0x64, 0x72, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
file_pb_peer_record_proto_rawDescOnce sync.Once
file_pb_peer_record_proto_rawDescData = file_pb_peer_record_proto_rawDesc
)
func file_pb_peer_record_proto_rawDescGZIP() []byte {
file_pb_peer_record_proto_rawDescOnce.Do(func() {
file_pb_peer_record_proto_rawDescData = protoimpl.X.CompressGZIP(file_pb_peer_record_proto_rawDescData)
})
return file_pb_peer_record_proto_rawDescData
}
var file_pb_peer_record_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
var file_pb_peer_record_proto_goTypes = []interface{}{
(*PeerRecord)(nil), // 0: peer.pb.PeerRecord
(*PeerRecord_AddressInfo)(nil), // 1: peer.pb.PeerRecord.AddressInfo
}
var file_pb_peer_record_proto_depIdxs = []int32{
1, // 0: peer.pb.PeerRecord.addresses:type_name -> peer.pb.PeerRecord.AddressInfo
1, // [1:1] is the sub-list for method output_type
1, // [1:1] is the sub-list for method input_type
1, // [1:1] is the sub-list for extension type_name
1, // [1:1] is the sub-list for extension extendee
0, // [0:1] is the sub-list for field type_name
}
func init() { file_pb_peer_record_proto_init() }
func file_pb_peer_record_proto_init() {
if File_pb_peer_record_proto != nil {
return
}
if !protoimpl.UnsafeEnabled {
file_pb_peer_record_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*PeerRecord); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_pb_peer_record_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*PeerRecord_AddressInfo); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_pb_peer_record_proto_rawDesc,
NumEnums: 0,
NumMessages: 2,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_pb_peer_record_proto_goTypes,
DependencyIndexes: file_pb_peer_record_proto_depIdxs,
MessageInfos: file_pb_peer_record_proto_msgTypes,
}.Build()
File_pb_peer_record_proto = out.File
file_pb_peer_record_proto_rawDesc = nil
file_pb_peer_record_proto_goTypes = nil
file_pb_peer_record_proto_depIdxs = nil
}

View File

@@ -0,0 +1,29 @@
syntax = "proto3";
package peer.pb;
// PeerRecord messages contain information that is useful to share with other peers.
// 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 designed to be serialized to bytes and placed inside of
// SignedEnvelopes before sharing with other peers.
// See https://github.com/libp2p/go-libp2p/core/record/pb/envelope.proto for
// the SignedEnvelope definition.
message PeerRecord {
// AddressInfo is a wrapper around a binary multiaddr. It is defined as a
// separate message to allow us to add per-address metadata in the future.
message AddressInfo {
bytes multiaddr = 1;
}
// peer_id contains a libp2p peer id in its binary representation.
bytes peer_id = 1;
// seq contains a monotonically-increasing sequence counter to order PeerRecords in time.
uint64 seq = 2;
// addresses is a list of public listen addresses for the peer.
repeated AddressInfo addresses = 3;
}

210
vendor/github.com/libp2p/go-libp2p/core/peer/peer.go generated vendored Normal file
View File

@@ -0,0 +1,210 @@
// Package peer implements an object used to represent peers in the libp2p network.
package peer
import (
"errors"
"fmt"
"strings"
"github.com/ipfs/go-cid"
ic "github.com/libp2p/go-libp2p/core/crypto"
b58 "github.com/mr-tron/base58/base58"
mc "github.com/multiformats/go-multicodec"
mh "github.com/multiformats/go-multihash"
)
var (
// ErrEmptyPeerID is an error for empty peer ID.
ErrEmptyPeerID = errors.New("empty peer ID")
// ErrNoPublicKey is an error for peer IDs that don't embed public keys
ErrNoPublicKey = errors.New("public key is not embedded in peer ID")
)
// AdvancedEnableInlining enables automatically inlining keys shorter than
// 42 bytes into the peer ID (using the "identity" multihash function).
//
// WARNING: This flag will likely be set to false in the future and eventually
// be removed in favor of using a hash function specified by the key itself.
// See: https://github.com/libp2p/specs/issues/138
//
// DO NOT change this flag unless you know what you're doing.
//
// This currently defaults to true for backwards compatibility but will likely
// be set to false by default when an upgrade path is determined.
var AdvancedEnableInlining = true
const maxInlineKeyLength = 42
// ID is a libp2p peer identity.
//
// Peer IDs are derived by hashing a peer's public key and encoding the
// hash output as a multihash. See IDFromPublicKey for details.
type ID string
// Pretty returns a base58-encoded string representation of the ID.
// Deprecated: use String() instead.
func (id ID) Pretty() string {
return id.String()
}
// Loggable returns a pretty peer ID string in loggable JSON format.
func (id ID) Loggable() map[string]interface{} {
return map[string]interface{}{
"peerID": id.String(),
}
}
func (id ID) String() string {
return b58.Encode([]byte(id))
}
// ShortString prints out the peer ID.
//
// TODO(brian): ensure correctness at ID generation and
// enforce this by only exposing functions that generate
// IDs safely. Then any peer.ID type found in the
// codebase is known to be correct.
func (id ID) ShortString() string {
pid := id.String()
if len(pid) <= 10 {
return fmt.Sprintf("<peer.ID %s>", pid)
}
return fmt.Sprintf("<peer.ID %s*%s>", pid[:2], pid[len(pid)-6:])
}
// MatchesPrivateKey tests whether this ID was derived from the secret key sk.
func (id ID) MatchesPrivateKey(sk ic.PrivKey) bool {
return id.MatchesPublicKey(sk.GetPublic())
}
// MatchesPublicKey tests whether this ID was derived from the public key pk.
func (id ID) MatchesPublicKey(pk ic.PubKey) bool {
oid, err := IDFromPublicKey(pk)
if err != nil {
return false
}
return oid == id
}
// ExtractPublicKey attempts to extract the public key from an ID.
//
// This method returns ErrNoPublicKey if the peer ID looks valid, but it can't extract
// the public key.
func (id ID) ExtractPublicKey() (ic.PubKey, error) {
decoded, err := mh.Decode([]byte(id))
if err != nil {
return nil, err
}
if decoded.Code != mh.IDENTITY {
return nil, ErrNoPublicKey
}
pk, err := ic.UnmarshalPublicKey(decoded.Digest)
if err != nil {
return nil, err
}
return pk, nil
}
// Validate checks if ID is empty or not.
func (id ID) Validate() error {
if id == ID("") {
return ErrEmptyPeerID
}
return nil
}
// IDFromBytes casts a byte slice to the ID type, and validates
// the value to make sure it is a multihash.
func IDFromBytes(b []byte) (ID, error) {
if _, err := mh.Cast(b); err != nil {
return ID(""), err
}
return ID(b), nil
}
// Decode accepts an encoded peer ID and returns the decoded ID if the input is
// valid.
//
// The encoded peer ID can either be a CID of a key or a raw multihash (identity
// or sha256-256).
func Decode(s string) (ID, error) {
if strings.HasPrefix(s, "Qm") || strings.HasPrefix(s, "1") {
// base58 encoded sha256 or identity multihash
m, err := mh.FromB58String(s)
if err != nil {
return "", fmt.Errorf("failed to parse peer ID: %s", err)
}
return ID(m), nil
}
c, err := cid.Decode(s)
if err != nil {
return "", fmt.Errorf("failed to parse peer ID: %s", err)
}
return FromCid(c)
}
// Encode encodes a peer ID as a string.
//
// At the moment, it base58 encodes the peer ID but, in the future, it will
// switch to encoding it as a CID by default.
//
// Deprecated: use id.String instead.
func Encode(id ID) string {
return id.String()
}
// FromCid converts a CID to a peer ID, if possible.
func FromCid(c cid.Cid) (ID, error) {
code := mc.Code(c.Type())
if code != mc.Libp2pKey {
return "", fmt.Errorf("can't convert CID of type %q to a peer ID", code)
}
return ID(c.Hash()), nil
}
// ToCid encodes a peer ID as a CID of the public key.
//
// If the peer ID is invalid (e.g., empty), this will return the empty CID.
func ToCid(id ID) cid.Cid {
m, err := mh.Cast([]byte(id))
if err != nil {
return cid.Cid{}
}
return cid.NewCidV1(cid.Libp2pKey, m)
}
// IDFromPublicKey returns the Peer ID corresponding to the public key pk.
func IDFromPublicKey(pk ic.PubKey) (ID, error) {
b, err := ic.MarshalPublicKey(pk)
if err != nil {
return "", err
}
var alg uint64 = mh.SHA2_256
if AdvancedEnableInlining && len(b) <= maxInlineKeyLength {
alg = mh.IDENTITY
}
hash, _ := mh.Sum(b, alg, -1)
return ID(hash), nil
}
// IDFromPrivateKey returns the Peer ID corresponding to the secret key sk.
func IDFromPrivateKey(sk ic.PrivKey) (ID, error) {
return IDFromPublicKey(sk.GetPublic())
}
// IDSlice for sorting peers.
type IDSlice []ID
func (es IDSlice) Len() int { return len(es) }
func (es IDSlice) Swap(i, j int) { es[i], es[j] = es[j], es[i] }
func (es IDSlice) Less(i, j int) bool { return string(es[i]) < string(es[j]) }
func (es IDSlice) String() string {
peersStrings := make([]string, len(es))
for i, id := range es {
peersStrings[i] = id.String()
}
return strings.Join(peersStrings, ", ")
}

View File

@@ -0,0 +1,73 @@
// Package peer contains Protobuf and JSON serialization/deserialization methods for peer IDs.
package peer
import (
"encoding"
"encoding/json"
)
// Interface assertions commented out to avoid introducing hard dependencies to protobuf.
// var _ proto.Marshaler = (*ID)(nil)
// var _ proto.Unmarshaler = (*ID)(nil)
var _ json.Marshaler = (*ID)(nil)
var _ json.Unmarshaler = (*ID)(nil)
var _ encoding.BinaryMarshaler = (*ID)(nil)
var _ encoding.BinaryUnmarshaler = (*ID)(nil)
var _ encoding.TextMarshaler = (*ID)(nil)
var _ encoding.TextUnmarshaler = (*ID)(nil)
func (id ID) Marshal() ([]byte, error) {
return []byte(id), nil
}
// MarshalBinary returns the byte representation of the peer ID.
func (id ID) MarshalBinary() ([]byte, error) {
return id.Marshal()
}
func (id ID) MarshalTo(data []byte) (n int, err error) {
return copy(data, []byte(id)), nil
}
func (id *ID) Unmarshal(data []byte) (err error) {
*id, err = IDFromBytes(data)
return err
}
// UnmarshalBinary sets the ID from its binary representation.
func (id *ID) UnmarshalBinary(data []byte) error {
return id.Unmarshal(data)
}
func (id ID) Size() int {
return len([]byte(id))
}
func (id ID) MarshalJSON() ([]byte, error) {
return json.Marshal(Encode(id))
}
func (id *ID) UnmarshalJSON(data []byte) (err error) {
var v string
if err = json.Unmarshal(data, &v); err != nil {
return err
}
*id, err = Decode(v)
return err
}
// MarshalText returns the text encoding of the ID.
func (id ID) MarshalText() ([]byte, error) {
return []byte(Encode(id)), nil
}
// UnmarshalText restores the ID from its text encoding.
func (id *ID) UnmarshalText(data []byte) error {
pid, err := Decode(string(data))
if err != nil {
return err
}
*id = pid
return nil
}

253
vendor/github.com/libp2p/go-libp2p/core/peer/record.go generated vendored Normal file
View File

@@ -0,0 +1,253 @@
package peer
import (
"fmt"
"sync"
"time"
"github.com/libp2p/go-libp2p/core/internal/catch"
"github.com/libp2p/go-libp2p/core/peer/pb"
"github.com/libp2p/go-libp2p/core/record"
ma "github.com/multiformats/go-multiaddr"
"google.golang.org/protobuf/proto"
)
//go:generate protoc --proto_path=$PWD:$PWD/../.. --go_out=. --go_opt=Mpb/peer_record.proto=./pb pb/peer_record.proto
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 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
}
// PeerRecordFromProtobuf creates a PeerRecord from a protobuf PeerRecord
// struct.
func PeerRecordFromProtobuf(msg *pb.PeerRecord) (*PeerRecord, error) {
record := &PeerRecord{}
var id ID
if err := id.UnmarshalBinary(msg.PeerId); err != nil {
return nil, err
}
record.PeerID = id
record.Addrs = addrsFromProtobuf(msg.Addresses)
record.Seq = msg.Seq
return record, nil
}
var (
lastTimestampMu sync.Mutex
lastTimestamp uint64
)
// TimestampSeq is a helper to generate a timestamp-based sequence number for a PeerRecord.
func TimestampSeq() uint64 {
now := 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 now <= lastTimestamp {
now = lastTimestamp + 1
}
lastTimestamp = now
return now
}
// 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) (err error) {
if r == nil {
return fmt.Errorf("cannot unmarshal PeerRecord to nil receiver")
}
defer func() { catch.HandlePanic(recover(), &err, "libp2p peer record unmarshal") }()
var msg pb.PeerRecord
err = proto.Unmarshal(bytes, &msg)
if err != nil {
return err
}
rPtr, err := PeerRecordFromProtobuf(&msg)
if err != nil {
return err
}
*r = *rPtr
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() (res []byte, err error) {
defer func() { catch.HandlePanic(recover(), &err, "libp2p peer record marshal") }()
msg, err := r.ToProtobuf()
if err != nil {
return nil, err
}
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
}
// ToProtobuf returns the equivalent Protocol Buffer struct object of a PeerRecord.
func (r *PeerRecord) ToProtobuf() (*pb.PeerRecord, error) {
idBytes, err := r.PeerID.MarshalBinary()
if err != nil {
return nil, err
}
return &pb.PeerRecord{
PeerId: idBytes,
Addresses: addrsToProtobuf(r.Addrs),
Seq: r.Seq,
}, nil
}
func addrsFromProtobuf(addrs []*pb.PeerRecord_AddressInfo) []ma.Multiaddr {
out := make([]ma.Multiaddr, 0, len(addrs))
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 {
out := make([]*pb.PeerRecord_AddressInfo, 0, len(addrs))
for _, addr := range addrs {
out = append(out, &pb.PeerRecord_AddressInfo{Multiaddr: addr.Bytes()})
}
return out
}

View File

@@ -0,0 +1,14 @@
package peerstore
import (
"github.com/libp2p/go-libp2p/core/peer"
)
// AddrInfos returns an AddrInfo for each specified peer ID, in-order.
func AddrInfos(ps Peerstore, peers []peer.ID) []peer.AddrInfo {
pi := make([]peer.AddrInfo, len(peers))
for i, p := range peers {
pi[i] = ps.PeerInfo(p)
}
return pi
}

View File

@@ -0,0 +1,250 @@
// Package peerstore provides types and interfaces for local storage of address information,
// metadata, and public key material about libp2p peers.
package peerstore
import (
"context"
"errors"
"io"
"math"
"time"
ic "github.com/libp2p/go-libp2p/core/crypto"
"github.com/libp2p/go-libp2p/core/peer"
"github.com/libp2p/go-libp2p/core/protocol"
"github.com/libp2p/go-libp2p/core/record"
ma "github.com/multiformats/go-multiaddr"
)
var ErrNotFound = errors.New("item not found")
var (
// AddressTTL is the expiration time of addresses.
AddressTTL = time.Hour
// TempAddrTTL is the ttl used for a short-lived address.
TempAddrTTL = time.Minute * 2
// RecentlyConnectedAddrTTL is used when we recently connected to a peer.
// It means that we are reasonably certain of the peer's address.
RecentlyConnectedAddrTTL = time.Minute * 30
// OwnObservedAddrTTL is used for our own external addresses observed by peers.
OwnObservedAddrTTL = time.Minute * 30
)
// Permanent TTLs (distinct so we can distinguish between them, constant as they
// are, in fact, permanent)
const (
// PermanentAddrTTL is the ttl for a "permanent address" (e.g. bootstrap nodes).
PermanentAddrTTL = math.MaxInt64 - iota
// ConnectedAddrTTL is the ttl used for the addresses of a peer to whom
// we're connected directly. This is basically permanent, as we will
// clear them + re-add under a TempAddrTTL after disconnecting.
ConnectedAddrTTL
)
// Peerstore provides a thread-safe store of Peer related
// information.
type Peerstore interface {
io.Closer
AddrBook
KeyBook
PeerMetadata
Metrics
ProtoBook
// PeerInfo returns a peer.PeerInfo struct for given peer.ID.
// This is a small slice of the information Peerstore has on
// that peer, useful to other services.
PeerInfo(peer.ID) peer.AddrInfo
// Peers returns all the peer IDs stored across all inner stores.
Peers() peer.IDSlice
}
// PeerMetadata can handle values of any type. Serializing values is
// up to the implementation. Dynamic type introspection may not be
// supported, in which case explicitly enlisting types in the
// serializer may be required.
//
// Refer to the docs of the underlying implementation for more
// information.
type PeerMetadata interface {
// Get / Put is a simple registry for other peer-related key/value pairs.
// If we find something we use often, it should become its own set of
// methods. This is a last resort.
Get(p peer.ID, key string) (interface{}, error)
Put(p peer.ID, key string, val interface{}) error
// RemovePeer removes all values stored for a peer.
RemovePeer(peer.ID)
}
// AddrBook holds the multiaddrs of peers.
type AddrBook interface {
// AddAddr calls AddAddrs(p, []ma.Multiaddr{addr}, ttl)
AddAddr(p peer.ID, addr ma.Multiaddr, ttl time.Duration)
// AddAddrs gives this AddrBook addresses to use, with a given ttl
// (time-to-live), after which the address is no longer valid.
// If the manager has a longer TTL, the operation is a no-op for that address
AddAddrs(p peer.ID, addrs []ma.Multiaddr, ttl time.Duration)
// SetAddr calls mgr.SetAddrs(p, addr, ttl)
SetAddr(p peer.ID, addr ma.Multiaddr, ttl time.Duration)
// SetAddrs sets the ttl on addresses. This clears any TTL there previously.
// This is used when we receive the best estimate of the validity of an address.
SetAddrs(p peer.ID, addrs []ma.Multiaddr, ttl time.Duration)
// UpdateAddrs updates the addresses associated with the given peer that have
// the given oldTTL to have the given newTTL.
UpdateAddrs(p peer.ID, oldTTL time.Duration, newTTL time.Duration)
// Addrs returns all known (and valid) addresses for a given peer.
Addrs(p peer.ID) []ma.Multiaddr
// AddrStream returns a channel that gets all addresses for a given
// peer sent on it. If new addresses are added after the call is made
// they will be sent along through the channel as well.
AddrStream(context.Context, peer.ID) <-chan ma.Multiaddr
// ClearAddresses removes all previously stored addresses.
ClearAddrs(p peer.ID)
// PeersWithAddrs returns all the peer IDs stored in the AddrBook.
PeersWithAddrs() peer.IDSlice
}
// CertifiedAddrBook manages "self-certified" addresses for remote peers.
// Self-certified addresses are contained in peer.PeerRecords
// which are wrapped in a record.Envelope and signed by the peer
// to whom they belong.
//
// Certified addresses (CA) are generally more secure than uncertified
// addresses (UA). Consequently, CAs beat and displace UAs. When the
// peerstore learns CAs for a peer, it will reject UAs for the same peer
// (as long as the former haven't expired).
// Furthermore, peer records act like sequenced snapshots of CAs. Therefore,
// processing a peer record that's newer than the last one seen overwrites
// all addresses with the incoming ones.
//
// This interface is most useful when combined with AddrBook.
// To test whether a given AddrBook / Peerstore implementation supports
// certified addresses, callers should use the GetCertifiedAddrBook helper or
// type-assert on the CertifiedAddrBook interface:
//
// if cab, ok := aPeerstore.(CertifiedAddrBook); ok {
// cab.ConsumePeerRecord(signedPeerRecord, aTTL)
// }
type CertifiedAddrBook interface {
// ConsumePeerRecord adds addresses from a signed peer.PeerRecord (contained in
// a record.Envelope), which will expire after the given TTL.
//
// The 'accepted' return value indicates that the record was successfully processed
// and integrated into the CertifiedAddrBook state. If 'accepted' is false but no
// error is returned, it means that the record was ignored, most likely because
// a newer record exists for the same peer.
//
// Signed records added via this method will be stored without
// alteration as long as the address TTLs remain valid. The Envelopes
// containing the PeerRecords can be retrieved by calling GetPeerRecord(peerID).
//
// If the signed PeerRecord belongs to a peer that already has certified
// addresses in the CertifiedAddrBook, the new addresses will replace the
// older ones, if the new record has a higher sequence number than the
// existing record. Attempting to add a peer record with a
// sequence number that's <= an existing record for the same peer will not
// result in an error, but the record will be ignored, and the 'accepted'
// bool return value will be false.
//
// If the CertifiedAddrBook is also an AddrBook (which is most likely the case),
// adding certified addresses for a peer will *replace* any
// existing non-certified addresses for that peer, and only the certified
// addresses will be returned from AddrBook.Addrs thereafter.
//
// Likewise, once certified addresses have been added for a given peer,
// any non-certified addresses added via AddrBook.AddAddrs or
// AddrBook.SetAddrs will be ignored. AddrBook.SetAddrs may still be used
// to update the TTL of certified addresses that have previously been
// added via ConsumePeerRecord.
ConsumePeerRecord(s *record.Envelope, ttl time.Duration) (accepted bool, err error)
// GetPeerRecord returns an Envelope containing a PeerRecord for the
// given peer id, if one exists.
// Returns nil if no signed PeerRecord exists for the peer.
GetPeerRecord(p peer.ID) *record.Envelope
}
// GetCertifiedAddrBook is a helper to "upcast" an AddrBook to a
// CertifiedAddrBook by using type assertion. If the given AddrBook
// is also a CertifiedAddrBook, it will be returned, and the ok return
// value will be true. Returns (nil, false) if the AddrBook is not a
// CertifiedAddrBook.
//
// Note that since Peerstore embeds the AddrBook interface, you can also
// call GetCertifiedAddrBook(myPeerstore).
func GetCertifiedAddrBook(ab AddrBook) (cab CertifiedAddrBook, ok bool) {
cab, ok = ab.(CertifiedAddrBook)
return cab, ok
}
// KeyBook tracks the keys of Peers.
type KeyBook interface {
// PubKey stores the public key of a peer.
PubKey(peer.ID) ic.PubKey
// AddPubKey stores the public key of a peer.
AddPubKey(peer.ID, ic.PubKey) error
// PrivKey returns the private key of a peer, if known. Generally this might only be our own
// private key, see
// https://discuss.libp2p.io/t/what-is-the-purpose-of-having-map-peer-id-privatekey-in-peerstore/74.
PrivKey(peer.ID) ic.PrivKey
// AddPrivKey stores the private key of a peer.
AddPrivKey(peer.ID, ic.PrivKey) error
// PeersWithKeys returns all the peer IDs stored in the KeyBook.
PeersWithKeys() peer.IDSlice
// RemovePeer removes all keys associated with a peer.
RemovePeer(peer.ID)
}
// Metrics tracks metrics across a set of peers.
type Metrics interface {
// RecordLatency records a new latency measurement
RecordLatency(peer.ID, time.Duration)
// LatencyEWMA returns an exponentially-weighted moving avg.
// of all measurements of a peer's latency.
LatencyEWMA(peer.ID) time.Duration
// RemovePeer removes all metrics stored for a peer.
RemovePeer(peer.ID)
}
// ProtoBook tracks the protocols supported by peers.
type ProtoBook interface {
GetProtocols(peer.ID) ([]protocol.ID, error)
AddProtocols(peer.ID, ...protocol.ID) error
SetProtocols(peer.ID, ...protocol.ID) error
RemoveProtocols(peer.ID, ...protocol.ID) error
// SupportsProtocols returns the set of protocols the peer supports from among the given protocols.
// If the returned error is not nil, the result is indeterminate.
SupportsProtocols(peer.ID, ...protocol.ID) ([]protocol.ID, error)
// FirstSupportedProtocol returns the first protocol that the peer supports among the given protocols.
// If the peer does not support any of the given protocols, this function will return an empty protocol.ID and a nil error.
// If the returned error is not nil, the result is indeterminate.
FirstSupportedProtocol(peer.ID, ...protocol.ID) (protocol.ID, error)
// RemovePeer removes all protocols associated with a peer.
RemovePeer(peer.ID)
}

66
vendor/github.com/libp2p/go-libp2p/core/pnet/codec.go generated vendored Normal file
View File

@@ -0,0 +1,66 @@
package pnet
import (
"bufio"
"bytes"
"encoding/base64"
"encoding/hex"
"fmt"
"io"
)
var (
pathPSKv1 = []byte("/key/swarm/psk/1.0.0/")
pathBin = "/bin/"
pathBase16 = "/base16/"
pathBase64 = "/base64/"
)
func readHeader(r *bufio.Reader) ([]byte, error) {
header, err := r.ReadBytes('\n')
if err != nil {
return nil, err
}
return bytes.TrimRight(header, "\r\n"), nil
}
func expectHeader(r *bufio.Reader, expected []byte) error {
header, err := readHeader(r)
if err != nil {
return err
}
if !bytes.Equal(header, expected) {
return fmt.Errorf("expected file header %s, got: %s", expected, header)
}
return nil
}
// DecodeV1PSK reads a Multicodec encoded V1 PSK.
func DecodeV1PSK(in io.Reader) (PSK, error) {
reader := bufio.NewReader(in)
if err := expectHeader(reader, pathPSKv1); err != nil {
return nil, err
}
header, err := readHeader(reader)
if err != nil {
return nil, err
}
var decoder io.Reader
switch string(header) {
case pathBase16:
decoder = hex.NewDecoder(reader)
case pathBase64:
decoder = base64.NewDecoder(base64.StdEncoding, reader)
case pathBin:
decoder = reader
default:
return nil, fmt.Errorf("unknown encoding: %s", header)
}
out := make([]byte, 32)
if _, err = io.ReadFull(decoder, out[:]); err != nil {
return nil, err
}
return out, nil
}

19
vendor/github.com/libp2p/go-libp2p/core/pnet/env.go generated vendored Normal file
View File

@@ -0,0 +1,19 @@
package pnet
import "os"
// EnvKey defines environment variable name for forcing usage of PNet in libp2p
// When environment variable of this name is set to "1" the ForcePrivateNetwork
// variable will be set to true.
const EnvKey = "LIBP2P_FORCE_PNET"
// ForcePrivateNetwork is boolean variable that forces usage of PNet in libp2p
// Setting this variable to true or setting LIBP2P_FORCE_PNET environment variable
// to true will make libp2p to require private network protector.
// If no network protector is provided and this variable is set to true libp2p will
// refuse to connect.
var ForcePrivateNetwork = false
func init() {
ForcePrivateNetwork = os.Getenv(EnvKey) == "1"
}

34
vendor/github.com/libp2p/go-libp2p/core/pnet/error.go generated vendored Normal file
View File

@@ -0,0 +1,34 @@
package pnet
// ErrNotInPrivateNetwork is an error that should be returned by libp2p when it
// tries to dial with ForcePrivateNetwork set and no PNet Protector
var ErrNotInPrivateNetwork = NewError("private network was not configured but" +
" is enforced by the environment")
// Error is error type for ease of detecting PNet errors
type Error interface {
IsPNetError() bool
}
// NewError creates new Error
func NewError(err string) error {
return pnetErr("privnet: " + err)
}
// IsPNetError checks if given error is PNet Error
func IsPNetError(err error) bool {
v, ok := err.(Error)
return ok && v.IsPNetError()
}
type pnetErr string
var _ Error = (*pnetErr)(nil)
func (p pnetErr) Error() string {
return string(p)
}
func (pnetErr) IsPNetError() bool {
return true
}

View File

@@ -0,0 +1,7 @@
// Package pnet provides interfaces for private networking in libp2p.
package pnet
// A PSK enables private network implementation to be transparent in libp2p.
// It is used to ensure that peers can only establish connections to other peers
// that are using the same PSK.
type PSK []byte

29
vendor/github.com/libp2p/go-libp2p/core/protocol/id.go generated vendored Normal file
View File

@@ -0,0 +1,29 @@
package protocol
// ID is an identifier used to write protocol headers in streams.
type ID string
// These are reserved protocol.IDs.
const (
TestingID ID = "/p2p/_testing"
)
// ConvertFromStrings is a convenience function that takes a slice of strings and
// converts it to a slice of protocol.ID.
func ConvertFromStrings(ids []string) (res []ID) {
res = make([]ID, 0, len(ids))
for _, id := range ids {
res = append(res, ID(id))
}
return res
}
// ConvertToStrings is a convenience function that takes a slice of protocol.ID and
// converts it to a slice of strings.
func ConvertToStrings(ids []ID) (res []string) {
res = make([]string, 0, len(ids))
for _, id := range ids {
res = append(res, string(id))
}
return res
}

View File

@@ -0,0 +1,73 @@
// Package protocol provides core interfaces for protocol routing and negotiation in libp2p.
package protocol
import (
"io"
"github.com/multiformats/go-multistream"
)
// HandlerFunc is a user-provided function used by the Router to
// handle a protocol/stream.
//
// Will be invoked with the protocol ID string as the first argument,
// which may differ from the ID used for registration if the handler
// was registered using a match function.
type HandlerFunc = multistream.HandlerFunc[ID]
// Router is an interface that allows users to add and remove protocol handlers,
// which will be invoked when incoming stream requests for registered protocols
// are accepted.
//
// Upon receiving an incoming stream request, the Router will check all registered
// protocol handlers to determine which (if any) is capable of handling the stream.
// The handlers are checked in order of registration; if multiple handlers are
// eligible, only the first to be registered will be invoked.
type Router interface {
// AddHandler registers the given handler to be invoked for
// an exact literal match of the given protocol ID string.
AddHandler(protocol ID, handler HandlerFunc)
// AddHandlerWithFunc registers the given handler to be invoked
// when the provided match function returns true.
//
// The match function will be invoked with an incoming protocol
// ID string, and should return true if the handler supports
// the protocol. Note that the protocol ID argument is not
// used for matching; if you want to match the protocol ID
// string exactly, you must check for it in your match function.
AddHandlerWithFunc(protocol ID, match func(ID) bool, handler HandlerFunc)
// RemoveHandler removes the registered handler (if any) for the
// given protocol ID string.
RemoveHandler(protocol ID)
// Protocols returns a list of all registered protocol ID strings.
// Note that the Router may be able to handle protocol IDs not
// included in this list if handlers were added with match functions
// using AddHandlerWithFunc.
Protocols() []ID
}
// Negotiator is a component capable of reaching agreement over what protocols
// to use for inbound streams of communication.
type Negotiator interface {
// Negotiate will return the registered protocol handler to use for a given
// inbound stream, returning after the protocol has been determined and the
// Negotiator has finished using the stream for negotiation. Returns an
// error if negotiation fails.
Negotiate(rwc io.ReadWriteCloser) (ID, HandlerFunc, error)
// Handle calls Negotiate to determine which protocol handler to use for an
// inbound stream, then invokes the protocol handler function, passing it
// the protocol ID and the stream. Returns an error if negotiation fails.
Handle(rwc io.ReadWriteCloser) error
}
// Switch is the component responsible for "dispatching" incoming stream requests to
// their corresponding stream handlers. It is both a Negotiator and a Router.
type Switch interface {
Router
Negotiator
}

View File

@@ -0,0 +1,296 @@
package record
import (
"bytes"
"errors"
"fmt"
"sync"
"github.com/libp2p/go-libp2p/core/crypto"
"github.com/libp2p/go-libp2p/core/internal/catch"
"github.com/libp2p/go-libp2p/core/record/pb"
pool "github.com/libp2p/go-buffer-pool"
"github.com/multiformats/go-varint"
"google.golang.org/protobuf/proto"
)
//go:generate protoc --proto_path=$PWD:$PWD/../.. --go_out=. --go_opt=Mpb/envelope.proto=./pb pb/envelope.proto
// Envelope contains an arbitrary []byte payload, signed by a libp2p peer.
//
// Envelopes are signed in the context of a particular "domain", which is a
// string specified when creating and verifying the envelope. You must know the
// domain string used to produce the envelope in order to verify the signature
// and access the payload.
type Envelope struct {
// The public key that can be used to verify the signature and derive the peer id of the signer.
PublicKey crypto.PubKey
// A binary identifier that indicates what kind of data is contained in the payload.
// TODO(yusef): enforce multicodec prefix
PayloadType []byte
// The envelope payload.
RawPayload []byte
// The signature of the domain string :: type hint :: payload.
signature []byte
// the unmarshalled payload as a Record, cached on first access via the Record accessor method
cached Record
unmarshalError error
unmarshalOnce sync.Once
}
var ErrEmptyDomain = errors.New("envelope domain must not be empty")
var ErrEmptyPayloadType = errors.New("payloadType must not be empty")
var ErrInvalidSignature = errors.New("invalid signature or incorrect domain")
// Seal marshals the given Record, places the marshaled bytes inside an Envelope,
// and signs with the given private key.
func Seal(rec Record, privateKey crypto.PrivKey) (*Envelope, error) {
payload, err := rec.MarshalRecord()
if err != nil {
return nil, fmt.Errorf("error marshaling record: %v", err)
}
domain := rec.Domain()
payloadType := rec.Codec()
if domain == "" {
return nil, ErrEmptyDomain
}
if len(payloadType) == 0 {
return nil, ErrEmptyPayloadType
}
unsigned, err := makeUnsigned(domain, payloadType, payload)
if err != nil {
return nil, err
}
defer pool.Put(unsigned)
sig, err := privateKey.Sign(unsigned)
if err != nil {
return nil, err
}
return &Envelope{
PublicKey: privateKey.GetPublic(),
PayloadType: payloadType,
RawPayload: payload,
signature: sig,
}, nil
}
// ConsumeEnvelope unmarshals a serialized Envelope and validates its
// signature using the provided 'domain' string. If validation fails, an error
// is returned, along with the unmarshalled envelope, so it can be inspected.
//
// On success, ConsumeEnvelope returns the Envelope itself, as well as the inner payload,
// unmarshalled into a concrete Record type. The actual type of the returned Record depends
// on what has been registered for the Envelope's PayloadType (see RegisterType for details).
//
// You can type assert on the returned Record to convert it to an instance of the concrete
// Record type:
//
// envelope, rec, err := ConsumeEnvelope(envelopeBytes, peer.PeerRecordEnvelopeDomain)
// if err != nil {
// handleError(envelope, err) // envelope may be non-nil, even if errors occur!
// return
// }
// peerRec, ok := rec.(*peer.PeerRecord)
// if ok {
// doSomethingWithPeerRecord(peerRec)
// }
//
// If the Envelope signature is valid, but no Record type is registered for the Envelope's
// PayloadType, ErrPayloadTypeNotRegistered will be returned, along with the Envelope and
// a nil Record.
func ConsumeEnvelope(data []byte, domain string) (envelope *Envelope, rec Record, err error) {
e, err := UnmarshalEnvelope(data)
if err != nil {
return nil, nil, fmt.Errorf("failed when unmarshalling the envelope: %w", err)
}
err = e.validate(domain)
if err != nil {
return nil, nil, fmt.Errorf("failed to validate envelope: %w", err)
}
rec, err = e.Record()
if err != nil {
return nil, nil, fmt.Errorf("failed to unmarshal envelope payload: %w", err)
}
return e, rec, nil
}
// ConsumeTypedEnvelope unmarshals a serialized Envelope and validates its
// signature. If validation fails, an error is returned, along with the unmarshalled
// envelope, so it can be inspected.
//
// Unlike ConsumeEnvelope, ConsumeTypedEnvelope does not try to automatically determine
// the type of Record to unmarshal the Envelope's payload into. Instead, the caller provides
// a destination Record instance, which will unmarshal the Envelope payload. It is the caller's
// responsibility to determine whether the given Record type is able to unmarshal the payload
// correctly.
//
// rec := &MyRecordType{}
// envelope, err := ConsumeTypedEnvelope(envelopeBytes, rec)
// if err != nil {
// handleError(envelope, err)
// }
// doSomethingWithRecord(rec)
//
// Important: you MUST check the error value before using the returned Envelope. In some error
// cases, including when the envelope signature is invalid, both the Envelope and an error will
// be returned. This allows you to inspect the unmarshalled but invalid Envelope. As a result,
// you must not assume that any non-nil Envelope returned from this function is valid.
func ConsumeTypedEnvelope(data []byte, destRecord Record) (envelope *Envelope, err error) {
e, err := UnmarshalEnvelope(data)
if err != nil {
return nil, fmt.Errorf("failed when unmarshalling the envelope: %w", err)
}
err = e.validate(destRecord.Domain())
if err != nil {
return e, fmt.Errorf("failed to validate envelope: %w", err)
}
err = destRecord.UnmarshalRecord(e.RawPayload)
if err != nil {
return e, fmt.Errorf("failed to unmarshal envelope payload: %w", err)
}
e.cached = destRecord
return e, nil
}
// UnmarshalEnvelope unmarshals a serialized Envelope protobuf message,
// without validating its contents. Most users should use ConsumeEnvelope.
func UnmarshalEnvelope(data []byte) (*Envelope, error) {
var e pb.Envelope
if err := proto.Unmarshal(data, &e); err != nil {
return nil, err
}
key, err := crypto.PublicKeyFromProto(e.PublicKey)
if err != nil {
return nil, err
}
return &Envelope{
PublicKey: key,
PayloadType: e.PayloadType,
RawPayload: e.Payload,
signature: e.Signature,
}, nil
}
// Marshal returns a byte slice containing a serialized protobuf representation
// of an Envelope.
func (e *Envelope) Marshal() (res []byte, err error) {
defer func() { catch.HandlePanic(recover(), &err, "libp2p envelope marshal") }()
key, err := crypto.PublicKeyToProto(e.PublicKey)
if err != nil {
return nil, err
}
msg := pb.Envelope{
PublicKey: key,
PayloadType: e.PayloadType,
Payload: e.RawPayload,
Signature: e.signature,
}
return proto.Marshal(&msg)
}
// Equal returns true if the other Envelope has the same public key,
// payload, payload type, and signature. This implies that they were also
// created with the same domain string.
func (e *Envelope) Equal(other *Envelope) bool {
if other == nil {
return e == nil
}
return e.PublicKey.Equals(other.PublicKey) &&
bytes.Equal(e.PayloadType, other.PayloadType) &&
bytes.Equal(e.signature, other.signature) &&
bytes.Equal(e.RawPayload, other.RawPayload)
}
// Record returns the Envelope's payload unmarshalled as a Record.
// The concrete type of the returned Record depends on which Record
// type was registered for the Envelope's PayloadType - see record.RegisterType.
//
// Once unmarshalled, the Record is cached for future access.
func (e *Envelope) Record() (Record, error) {
e.unmarshalOnce.Do(func() {
if e.cached != nil {
return
}
e.cached, e.unmarshalError = unmarshalRecordPayload(e.PayloadType, e.RawPayload)
})
return e.cached, e.unmarshalError
}
// TypedRecord unmarshals the Envelope's payload to the given Record instance.
// It is the caller's responsibility to ensure that the Record type is capable
// of unmarshalling the Envelope payload. Callers can inspect the Envelope's
// PayloadType field to determine the correct type of Record to use.
//
// This method will always unmarshal the Envelope payload even if a cached record
// exists.
func (e *Envelope) TypedRecord(dest Record) error {
return dest.UnmarshalRecord(e.RawPayload)
}
// validate returns nil if the envelope signature is valid for the given 'domain',
// or an error if signature validation fails.
func (e *Envelope) validate(domain string) error {
unsigned, err := makeUnsigned(domain, e.PayloadType, e.RawPayload)
if err != nil {
return err
}
defer pool.Put(unsigned)
valid, err := e.PublicKey.Verify(unsigned, e.signature)
if err != nil {
return fmt.Errorf("failed while verifying signature: %w", err)
}
if !valid {
return ErrInvalidSignature
}
return nil
}
// makeUnsigned is a helper function that prepares a buffer to sign or verify.
// It returns a byte slice from a pool. The caller MUST return this slice to the
// pool.
func makeUnsigned(domain string, payloadType []byte, payload []byte) ([]byte, error) {
var (
fields = [][]byte{[]byte(domain), payloadType, payload}
// fields are prefixed with their length as an unsigned varint. we
// compute the lengths before allocating the sig buffer, so we know how
// much space to add for the lengths
flen = make([][]byte, len(fields))
size = 0
)
for i, f := range fields {
l := len(f)
flen[i] = varint.ToUvarint(uint64(l))
size += l + len(flen[i])
}
b := pool.Get(size)
var s int
for i, f := range fields {
s += copy(b[s:], flen[i])
s += copy(b[s:], f)
}
return b[:s], nil
}

View File

@@ -0,0 +1,192 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.30.0
// protoc v3.21.12
// source: pb/envelope.proto
package pb
import (
pb "github.com/libp2p/go-libp2p/core/crypto/pb"
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
// Envelope encloses a signed payload produced by a peer, along with the public
// key of the keypair it was signed with so that it can be statelessly validated
// by the receiver.
//
// The payload is prefixed with a byte string that determines the type, so it
// can be deserialized deterministically. Often, this byte string is a
// multicodec.
type Envelope struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
// public_key is the public key of the keypair the enclosed payload was
// signed with.
PublicKey *pb.PublicKey `protobuf:"bytes,1,opt,name=public_key,json=publicKey,proto3" json:"public_key,omitempty"`
// payload_type encodes the type of payload, so that it can be deserialized
// deterministically.
PayloadType []byte `protobuf:"bytes,2,opt,name=payload_type,json=payloadType,proto3" json:"payload_type,omitempty"`
// payload is the actual payload carried inside this envelope.
Payload []byte `protobuf:"bytes,3,opt,name=payload,proto3" json:"payload,omitempty"`
// signature is the signature produced by the private key corresponding to
// the enclosed public key, over the payload, prefixing a domain string for
// additional security.
Signature []byte `protobuf:"bytes,5,opt,name=signature,proto3" json:"signature,omitempty"`
}
func (x *Envelope) Reset() {
*x = Envelope{}
if protoimpl.UnsafeEnabled {
mi := &file_pb_envelope_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *Envelope) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*Envelope) ProtoMessage() {}
func (x *Envelope) ProtoReflect() protoreflect.Message {
mi := &file_pb_envelope_proto_msgTypes[0]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use Envelope.ProtoReflect.Descriptor instead.
func (*Envelope) Descriptor() ([]byte, []int) {
return file_pb_envelope_proto_rawDescGZIP(), []int{0}
}
func (x *Envelope) GetPublicKey() *pb.PublicKey {
if x != nil {
return x.PublicKey
}
return nil
}
func (x *Envelope) GetPayloadType() []byte {
if x != nil {
return x.PayloadType
}
return nil
}
func (x *Envelope) GetPayload() []byte {
if x != nil {
return x.Payload
}
return nil
}
func (x *Envelope) GetSignature() []byte {
if x != nil {
return x.Signature
}
return nil
}
var File_pb_envelope_proto protoreflect.FileDescriptor
var file_pb_envelope_proto_rawDesc = []byte{
0x0a, 0x11, 0x70, 0x62, 0x2f, 0x65, 0x6e, 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x2e, 0x70, 0x72,
0x6f, 0x74, 0x6f, 0x12, 0x09, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x2e, 0x70, 0x62, 0x1a, 0x1b,
0x63, 0x6f, 0x72, 0x65, 0x2f, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x2f, 0x70, 0x62, 0x2f, 0x63,
0x72, 0x79, 0x70, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x9a, 0x01, 0x0a, 0x08,
0x45, 0x6e, 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x12, 0x33, 0x0a, 0x0a, 0x70, 0x75, 0x62, 0x6c,
0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x63,
0x72, 0x79, 0x70, 0x74, 0x6f, 0x2e, 0x70, 0x62, 0x2e, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b,
0x65, 0x79, 0x52, 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x12, 0x21, 0x0a,
0x0c, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20,
0x01, 0x28, 0x0c, 0x52, 0x0b, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x54, 0x79, 0x70, 0x65,
0x12, 0x18, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28,
0x0c, 0x52, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x69,
0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x73,
0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
file_pb_envelope_proto_rawDescOnce sync.Once
file_pb_envelope_proto_rawDescData = file_pb_envelope_proto_rawDesc
)
func file_pb_envelope_proto_rawDescGZIP() []byte {
file_pb_envelope_proto_rawDescOnce.Do(func() {
file_pb_envelope_proto_rawDescData = protoimpl.X.CompressGZIP(file_pb_envelope_proto_rawDescData)
})
return file_pb_envelope_proto_rawDescData
}
var file_pb_envelope_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
var file_pb_envelope_proto_goTypes = []interface{}{
(*Envelope)(nil), // 0: record.pb.Envelope
(*pb.PublicKey)(nil), // 1: crypto.pb.PublicKey
}
var file_pb_envelope_proto_depIdxs = []int32{
1, // 0: record.pb.Envelope.public_key:type_name -> crypto.pb.PublicKey
1, // [1:1] is the sub-list for method output_type
1, // [1:1] is the sub-list for method input_type
1, // [1:1] is the sub-list for extension type_name
1, // [1:1] is the sub-list for extension extendee
0, // [0:1] is the sub-list for field type_name
}
func init() { file_pb_envelope_proto_init() }
func file_pb_envelope_proto_init() {
if File_pb_envelope_proto != nil {
return
}
if !protoimpl.UnsafeEnabled {
file_pb_envelope_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*Envelope); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_pb_envelope_proto_rawDesc,
NumEnums: 0,
NumMessages: 1,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_pb_envelope_proto_goTypes,
DependencyIndexes: file_pb_envelope_proto_depIdxs,
MessageInfos: file_pb_envelope_proto_msgTypes,
}.Build()
File_pb_envelope_proto = out.File
file_pb_envelope_proto_rawDesc = nil
file_pb_envelope_proto_goTypes = nil
file_pb_envelope_proto_depIdxs = nil
}

View File

@@ -0,0 +1,30 @@
syntax = "proto3";
package record.pb;
import "core/crypto/pb/crypto.proto";
// Envelope encloses a signed payload produced by a peer, along with the public
// key of the keypair it was signed with so that it can be statelessly validated
// by the receiver.
//
// The payload is prefixed with a byte string that determines the type, so it
// can be deserialized deterministically. Often, this byte string is a
// multicodec.
message Envelope {
// public_key is the public key of the keypair the enclosed payload was
// signed with.
crypto.pb.PublicKey public_key = 1;
// payload_type encodes the type of payload, so that it can be deserialized
// deterministically.
bytes payload_type = 2;
// payload is the actual payload carried inside this envelope.
bytes payload = 3;
// signature is the signature produced by the private key corresponding to
// the enclosed public key, over the payload, prefixing a domain string for
// additional security.
bytes signature = 5;
}

View File

@@ -0,0 +1,105 @@
package record
import (
"errors"
"reflect"
"github.com/libp2p/go-libp2p/core/internal/catch"
)
var (
// ErrPayloadTypeNotRegistered is returned from ConsumeEnvelope when the Envelope's
// PayloadType does not match any registered Record types.
ErrPayloadTypeNotRegistered = errors.New("payload type is not registered")
payloadTypeRegistry = make(map[string]reflect.Type)
)
// Record represents a data type that can be used as the payload of an Envelope.
// The Record interface defines the methods used to marshal and unmarshal a Record
// type to a byte slice.
//
// Record types may be "registered" as the default for a given Envelope.PayloadType
// using the RegisterType function. Once a Record type has been registered,
// an instance of that type will be created and used to unmarshal the payload of
// any Envelope with the registered PayloadType when the Envelope is opened using
// the ConsumeEnvelope function.
//
// To use an unregistered Record type instead, use ConsumeTypedEnvelope and pass in
// an instance of the Record type that you'd like the Envelope's payload to be
// unmarshaled into.
type Record interface {
// Domain is the "signature domain" used when signing and verifying a particular
// Record type. The Domain string should be unique to your Record type, and all
// instances of the Record type must have the same Domain string.
Domain() string
// Codec is a binary identifier for this type of record, ideally a registered multicodec
// (see https://github.com/multiformats/multicodec).
// When a Record is put into an Envelope (see record.Seal), the Codec value will be used
// as the Envelope's PayloadType. When the Envelope is later unsealed, the PayloadType
// will be used to look up the correct Record type to unmarshal the Envelope payload into.
Codec() []byte
// MarshalRecord converts a Record instance to a []byte, so that it can be used as an
// Envelope payload.
MarshalRecord() ([]byte, error)
// UnmarshalRecord unmarshals a []byte payload into an instance of a particular Record type.
UnmarshalRecord([]byte) error
}
// RegisterType associates a binary payload type identifier with a concrete
// Record type. This is used to automatically unmarshal Record payloads from Envelopes
// when using ConsumeEnvelope, and to automatically marshal Records and determine the
// correct PayloadType when calling Seal.
//
// Callers must provide an instance of the record type to be registered, which must be
// a pointer type. Registration should be done in the init function of the package
// where the Record type is defined:
//
// package hello_record
// import record "github.com/libp2p/go-libp2p/core/record"
//
// func init() {
// record.RegisterType(&HelloRecord{})
// }
//
// type HelloRecord struct { } // etc..
func RegisterType(prototype Record) {
payloadTypeRegistry[string(prototype.Codec())] = getValueType(prototype)
}
func unmarshalRecordPayload(payloadType []byte, payloadBytes []byte) (_rec Record, err error) {
defer func() { catch.HandlePanic(recover(), &err, "libp2p envelope record unmarshal") }()
rec, err := blankRecordForPayloadType(payloadType)
if err != nil {
return nil, err
}
err = rec.UnmarshalRecord(payloadBytes)
if err != nil {
return nil, err
}
return rec, nil
}
func blankRecordForPayloadType(payloadType []byte) (Record, error) {
valueType, ok := payloadTypeRegistry[string(payloadType)]
if !ok {
return nil, ErrPayloadTypeNotRegistered
}
val := reflect.New(valueType)
asRecord := val.Interface().(Record)
return asRecord, nil
}
func getValueType(i interface{}) reflect.Type {
valueType := reflect.TypeOf(i)
if valueType.Kind() == reflect.Ptr {
valueType = valueType.Elem()
}
return valueType
}

View File

@@ -0,0 +1,50 @@
package routing
// Option is a single routing option.
type Option func(opts *Options) error
// Options is a set of routing options
type Options struct {
// Allow expired values.
Expired bool
Offline bool
// Other (ValueStore implementation specific) options.
Other map[interface{}]interface{}
}
// Apply applies the given options to this Options
func (opts *Options) Apply(options ...Option) error {
for _, o := range options {
if err := o(opts); err != nil {
return err
}
}
return nil
}
// ToOption converts this Options to a single Option.
func (opts *Options) ToOption() Option {
return func(nopts *Options) error {
*nopts = *opts
if opts.Other != nil {
nopts.Other = make(map[interface{}]interface{}, len(opts.Other))
for k, v := range opts.Other {
nopts.Other[k] = v
}
}
return nil
}
}
// Expired is an option that tells the routing system to return expired records
// when no newer records are known.
var Expired Option = func(opts *Options) error {
opts.Expired = true
return nil
}
// Offline is an option that tells the routing system to operate offline (i.e., rely on cached/local data only).
var Offline Option = func(opts *Options) error {
opts.Offline = true
return nil
}

View File

@@ -0,0 +1,111 @@
package routing
import (
"context"
"sync"
"github.com/libp2p/go-libp2p/core/peer"
)
// QueryEventType indicates the query event's type.
type QueryEventType int
// Number of events to buffer.
var QueryEventBufferSize = 16
const (
// Sending a query to a peer.
SendingQuery QueryEventType = iota
// Got a response from a peer.
PeerResponse
// Found a "closest" peer (not currently used).
FinalPeer
// Got an error when querying.
QueryError
// Found a provider.
Provider
// Found a value.
Value
// Adding a peer to the query.
AddingPeer
// Dialing a peer.
DialingPeer
)
// QueryEvent is emitted for every notable event that happens during a DHT query.
type QueryEvent struct {
ID peer.ID
Type QueryEventType
Responses []*peer.AddrInfo
Extra string
}
type routingQueryKey struct{}
type eventChannel struct {
mu sync.Mutex
ctx context.Context
ch chan<- *QueryEvent
}
// waitThenClose is spawned in a goroutine when the channel is registered. This
// safely cleans up the channel when the context has been canceled.
func (e *eventChannel) waitThenClose() {
<-e.ctx.Done()
e.mu.Lock()
close(e.ch)
// 1. Signals that we're done.
// 2. Frees memory (in case we end up hanging on to this for a while).
e.ch = nil
e.mu.Unlock()
}
// send sends an event on the event channel, aborting if either the passed or
// the internal context expire.
func (e *eventChannel) send(ctx context.Context, ev *QueryEvent) {
e.mu.Lock()
// Closed.
if e.ch == nil {
e.mu.Unlock()
return
}
// in case the passed context is unrelated, wait on both.
select {
case e.ch <- ev:
case <-e.ctx.Done():
case <-ctx.Done():
}
e.mu.Unlock()
}
// RegisterForQueryEvents registers a query event channel with the given
// context. The returned context can be passed to DHT queries to receive query
// events on the returned channels.
//
// The passed context MUST be canceled when the caller is no longer interested
// in query events.
func RegisterForQueryEvents(ctx context.Context) (context.Context, <-chan *QueryEvent) {
ch := make(chan *QueryEvent, QueryEventBufferSize)
ech := &eventChannel{ch: ch, ctx: ctx}
go ech.waitThenClose()
return context.WithValue(ctx, routingQueryKey{}, ech), ch
}
// PublishQueryEvent publishes a query event to the query event channel
// associated with the given context, if any.
func PublishQueryEvent(ctx context.Context, ev *QueryEvent) {
ich := ctx.Value(routingQueryKey{})
if ich == nil {
return
}
// We *want* to panic here.
ech := ich.(*eventChannel)
ech.send(ctx, ev)
}
// SubscribesToQueryEvents returns true if the context subscribes to query
// events. If this function returns false, calling `PublishQueryEvent` on the
// context will be a no-op.
func SubscribesToQueryEvents(ctx context.Context) bool {
return ctx.Value(routingQueryKey{}) != nil
}

View File

@@ -0,0 +1,40 @@
package routing
import (
"encoding/json"
"github.com/libp2p/go-libp2p/core/peer"
)
func (qe *QueryEvent) MarshalJSON() ([]byte, error) {
return json.Marshal(map[string]interface{}{
"ID": qe.ID.String(),
"Type": int(qe.Type),
"Responses": qe.Responses,
"Extra": qe.Extra,
})
}
func (qe *QueryEvent) UnmarshalJSON(b []byte) error {
temp := struct {
ID string
Type int
Responses []*peer.AddrInfo
Extra string
}{}
err := json.Unmarshal(b, &temp)
if err != nil {
return err
}
if len(temp.ID) > 0 {
pid, err := peer.Decode(temp.ID)
if err != nil {
return err
}
qe.ID = pid
}
qe.Type = QueryEventType(temp.Type)
qe.Responses = temp.Responses
qe.Extra = temp.Extra
return nil
}

View File

@@ -0,0 +1,127 @@
// Package routing provides interfaces for peer routing and content routing in libp2p.
package routing
import (
"context"
"errors"
ci "github.com/libp2p/go-libp2p/core/crypto"
"github.com/libp2p/go-libp2p/core/peer"
cid "github.com/ipfs/go-cid"
)
// ErrNotFound is returned when the router fails to find the requested record.
var ErrNotFound = errors.New("routing: not found")
// ErrNotSupported is returned when the router doesn't support the given record
// type/operation.
var ErrNotSupported = errors.New("routing: operation or key not supported")
// ContentRouting is a value provider layer of indirection. It is used to find
// information about who has what content.
//
// Content is identified by CID (content identifier), which encodes a hash
// of the identified content in a future-proof manner.
type ContentRouting interface {
// Provide adds the given cid to the content routing system. If 'true' is
// passed, it also announces it, otherwise it is just kept in the local
// accounting of which objects are being provided.
Provide(context.Context, cid.Cid, bool) error
// Search for peers who are able to provide a given key
//
// When count is 0, this method will return an unbounded number of
// results.
FindProvidersAsync(context.Context, cid.Cid, int) <-chan peer.AddrInfo
}
// PeerRouting is a way to find address information about certain peers.
// This can be implemented by a simple lookup table, a tracking server,
// or even a DHT.
type PeerRouting interface {
// FindPeer searches for a peer with given ID, returns a peer.AddrInfo
// with relevant addresses.
FindPeer(context.Context, peer.ID) (peer.AddrInfo, error)
}
// ValueStore is a basic Put/Get interface.
type ValueStore interface {
// PutValue adds value corresponding to given Key.
PutValue(context.Context, string, []byte, ...Option) error
// GetValue searches for the value corresponding to given Key.
GetValue(context.Context, string, ...Option) ([]byte, error)
// SearchValue searches for better and better values from this value
// store corresponding to the given Key. By default, implementations must
// stop the search after a good value is found. A 'good' value is a value
// that would be returned from GetValue.
//
// Useful when you want a result *now* but still want to hear about
// better/newer results.
//
// Implementations of this methods won't return ErrNotFound. When a value
// couldn't be found, the channel will get closed without passing any results
SearchValue(context.Context, string, ...Option) (<-chan []byte, error)
}
// Routing is the combination of different routing types supported by libp2p.
// It can be satisfied by a single item (such as a DHT) or multiple different
// pieces that are more optimized to each task.
type Routing interface {
ContentRouting
PeerRouting
ValueStore
// Bootstrap allows callers to hint to the routing system to get into a
// Boostrapped state and remain there. It is not a synchronous call.
Bootstrap(context.Context) error
// TODO expose io.Closer or plain-old Close error
}
// PubKeyFetcher is an interfaces that should be implemented by value stores
// that can optimize retrieval of public keys.
//
// TODO(steb): Consider removing, see https://github.com/libp2p/go-libp2p-routing/issues/22.
type PubKeyFetcher interface {
// GetPublicKey returns the public key for the given peer.
GetPublicKey(context.Context, peer.ID) (ci.PubKey, error)
}
// KeyForPublicKey returns the key used to retrieve public keys
// from a value store.
func KeyForPublicKey(id peer.ID) string {
return "/pk/" + string(id)
}
// GetPublicKey retrieves the public key associated with the given peer ID from
// the value store.
//
// If the ValueStore is also a PubKeyFetcher, this method will call GetPublicKey
// (which may be better optimized) instead of GetValue.
func GetPublicKey(r ValueStore, ctx context.Context, p peer.ID) (ci.PubKey, error) {
switch k, err := p.ExtractPublicKey(); err {
case peer.ErrNoPublicKey:
// check the datastore
case nil:
return k, nil
default:
return nil, err
}
if dht, ok := r.(PubKeyFetcher); ok {
// If we have a DHT as our routing system, use optimized fetcher
return dht.GetPublicKey(ctx, p)
}
key := KeyForPublicKey(p)
pkval, err := r.GetValue(ctx, key)
if err != nil {
return nil, err
}
// get PublicKey from node.Data
return ci.UnmarshalPublicKey(pkval)
}

View File

@@ -0,0 +1,233 @@
// Package insecure provides an insecure, unencrypted implementation of the SecureConn and SecureTransport interfaces.
//
// Recommended only for testing and other non-production usage.
package insecure
import (
"context"
"fmt"
"io"
"net"
ci "github.com/libp2p/go-libp2p/core/crypto"
"github.com/libp2p/go-libp2p/core/network"
"github.com/libp2p/go-libp2p/core/peer"
"github.com/libp2p/go-libp2p/core/protocol"
"github.com/libp2p/go-libp2p/core/sec"
"github.com/libp2p/go-libp2p/core/sec/insecure/pb"
"github.com/libp2p/go-msgio"
"google.golang.org/protobuf/proto"
)
//go:generate protoc --proto_path=$PWD:$PWD/../../.. --go_out=. --go_opt=Mpb/plaintext.proto=./pb pb/plaintext.proto
// ID is the multistream-select protocol ID that should be used when identifying
// this security transport.
const ID = "/plaintext/2.0.0"
// Transport is a no-op stream security transport. It provides no
// security and simply mocks the security methods. Identity methods
// return the local peer's ID and private key, and whatever the remote
// peer presents as their ID and public key.
// No authentication of the remote identity is performed.
type Transport struct {
id peer.ID
key ci.PrivKey
protocolID protocol.ID
}
var _ sec.SecureTransport = &Transport{}
// NewWithIdentity constructs a new insecure transport. The public key is sent to
// remote peers. No security is provided.
func NewWithIdentity(protocolID protocol.ID, id peer.ID, key ci.PrivKey) *Transport {
return &Transport{
protocolID: protocolID,
id: id,
key: key,
}
}
// LocalPeer returns the transport's local peer ID.
func (t *Transport) LocalPeer() peer.ID {
return t.id
}
// SecureInbound *pretends to secure* an inbound connection to the given peer.
// It sends the local peer's ID and public key, and receives the same from the remote peer.
// No validation is performed as to the authenticity or ownership of the provided public key,
// and the key exchange provides no security.
//
// SecureInbound may fail if the remote peer sends an ID and public key that are inconsistent
// with each other, or if a network error occurs during the ID exchange.
func (t *Transport) SecureInbound(_ context.Context, insecure net.Conn, p peer.ID) (sec.SecureConn, error) {
conn := &Conn{
Conn: insecure,
local: t.id,
localPubKey: t.key.GetPublic(),
}
if err := conn.runHandshakeSync(); err != nil {
return nil, err
}
if p != "" && p != conn.remote {
return nil, fmt.Errorf("remote peer sent unexpected peer ID. expected=%s received=%s", p, conn.remote)
}
return conn, nil
}
// SecureOutbound *pretends to secure* an outbound connection to the given peer.
// It sends the local peer's ID and public key, and receives the same from the remote peer.
// No validation is performed as to the authenticity or ownership of the provided public key,
// and the key exchange provides no security.
//
// SecureOutbound may fail if the remote peer sends an ID and public key that are inconsistent
// with each other, or if the ID sent by the remote peer does not match the one dialed. It may
// also fail if a network error occurs during the ID exchange.
func (t *Transport) SecureOutbound(_ context.Context, insecure net.Conn, p peer.ID) (sec.SecureConn, error) {
conn := &Conn{
Conn: insecure,
local: t.id,
localPubKey: t.key.GetPublic(),
}
if err := conn.runHandshakeSync(); err != nil {
return nil, err
}
if p != conn.remote {
return nil, fmt.Errorf("remote peer sent unexpected peer ID. expected=%s received=%s",
p, conn.remote)
}
return conn, nil
}
func (t *Transport) ID() protocol.ID { return t.protocolID }
// Conn is the connection type returned by the insecure transport.
type Conn struct {
net.Conn
local, remote peer.ID
localPubKey, remotePubKey ci.PubKey
}
func makeExchangeMessage(pubkey ci.PubKey) (*pb.Exchange, error) {
keyMsg, err := ci.PublicKeyToProto(pubkey)
if err != nil {
return nil, err
}
id, err := peer.IDFromPublicKey(pubkey)
if err != nil {
return nil, err
}
return &pb.Exchange{
Id: []byte(id),
Pubkey: keyMsg,
}, nil
}
func (ic *Conn) runHandshakeSync() error {
// If we were initialized without keys, behave as in plaintext/1.0.0 (do nothing)
if ic.localPubKey == nil {
return nil
}
// Generate an Exchange message
msg, err := makeExchangeMessage(ic.localPubKey)
if err != nil {
return err
}
// Send our Exchange and read theirs
remoteMsg, err := readWriteMsg(ic.Conn, msg)
if err != nil {
return err
}
// Pull remote ID and public key from message
remotePubkey, err := ci.PublicKeyFromProto(remoteMsg.Pubkey)
if err != nil {
return err
}
remoteID, err := peer.IDFromBytes(remoteMsg.Id)
if err != nil {
return err
}
// Validate that ID matches public key
if !remoteID.MatchesPublicKey(remotePubkey) {
calculatedID, _ := peer.IDFromPublicKey(remotePubkey)
return fmt.Errorf("remote peer id does not match public key. id=%s calculated_id=%s",
remoteID, calculatedID)
}
// Add remote ID and key to conn state
ic.remotePubKey = remotePubkey
ic.remote = remoteID
return nil
}
// read and write a message at the same time.
func readWriteMsg(rw io.ReadWriter, out *pb.Exchange) (*pb.Exchange, error) {
const maxMessageSize = 1 << 16
outBytes, err := proto.Marshal(out)
if err != nil {
return nil, err
}
wresult := make(chan error)
go func() {
w := msgio.NewVarintWriter(rw)
wresult <- w.WriteMsg(outBytes)
}()
r := msgio.NewVarintReaderSize(rw, maxMessageSize)
b, err1 := r.ReadMsg()
// Always wait for the read to finish.
err2 := <-wresult
if err1 != nil {
return nil, err1
}
if err2 != nil {
r.ReleaseMsg(b)
return nil, err2
}
inMsg := new(pb.Exchange)
err = proto.Unmarshal(b, inMsg)
return inMsg, err
}
// LocalPeer returns the local peer ID.
func (ic *Conn) LocalPeer() peer.ID {
return ic.local
}
// RemotePeer returns the remote peer ID if we initiated the dial. Otherwise, it
// returns "" (because this connection isn't actually secure).
func (ic *Conn) RemotePeer() peer.ID {
return ic.remote
}
// RemotePublicKey returns whatever public key was given by the remote peer.
// Note that no verification of ownership is done, as this connection is not secure.
func (ic *Conn) RemotePublicKey() ci.PubKey {
return ic.remotePubKey
}
// ConnState returns the security connection's state information.
func (ic *Conn) ConnState() network.ConnectionState {
return network.ConnectionState{}
}
var _ sec.SecureTransport = (*Transport)(nil)
var _ sec.SecureConn = (*Conn)(nil)

View File

@@ -0,0 +1,156 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.30.0
// protoc v3.21.12
// source: pb/plaintext.proto
package pb
import (
pb "github.com/libp2p/go-libp2p/core/crypto/pb"
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
type Exchange struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Id []byte `protobuf:"bytes,1,opt,name=id" json:"id,omitempty"`
Pubkey *pb.PublicKey `protobuf:"bytes,2,opt,name=pubkey" json:"pubkey,omitempty"`
}
func (x *Exchange) Reset() {
*x = Exchange{}
if protoimpl.UnsafeEnabled {
mi := &file_pb_plaintext_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *Exchange) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*Exchange) ProtoMessage() {}
func (x *Exchange) ProtoReflect() protoreflect.Message {
mi := &file_pb_plaintext_proto_msgTypes[0]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use Exchange.ProtoReflect.Descriptor instead.
func (*Exchange) Descriptor() ([]byte, []int) {
return file_pb_plaintext_proto_rawDescGZIP(), []int{0}
}
func (x *Exchange) GetId() []byte {
if x != nil {
return x.Id
}
return nil
}
func (x *Exchange) GetPubkey() *pb.PublicKey {
if x != nil {
return x.Pubkey
}
return nil
}
var File_pb_plaintext_proto protoreflect.FileDescriptor
var file_pb_plaintext_proto_rawDesc = []byte{
0x0a, 0x12, 0x70, 0x62, 0x2f, 0x70, 0x6c, 0x61, 0x69, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x2e, 0x70,
0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0c, 0x70, 0x6c, 0x61, 0x69, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x2e,
0x70, 0x62, 0x1a, 0x1b, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x2f,
0x70, 0x62, 0x2f, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22,
0x48, 0x0a, 0x08, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69,
0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x02, 0x69, 0x64, 0x12, 0x2c, 0x0a, 0x06, 0x70,
0x75, 0x62, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x63, 0x72,
0x79, 0x70, 0x74, 0x6f, 0x2e, 0x70, 0x62, 0x2e, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65,
0x79, 0x52, 0x06, 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79,
}
var (
file_pb_plaintext_proto_rawDescOnce sync.Once
file_pb_plaintext_proto_rawDescData = file_pb_plaintext_proto_rawDesc
)
func file_pb_plaintext_proto_rawDescGZIP() []byte {
file_pb_plaintext_proto_rawDescOnce.Do(func() {
file_pb_plaintext_proto_rawDescData = protoimpl.X.CompressGZIP(file_pb_plaintext_proto_rawDescData)
})
return file_pb_plaintext_proto_rawDescData
}
var file_pb_plaintext_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
var file_pb_plaintext_proto_goTypes = []interface{}{
(*Exchange)(nil), // 0: plaintext.pb.Exchange
(*pb.PublicKey)(nil), // 1: crypto.pb.PublicKey
}
var file_pb_plaintext_proto_depIdxs = []int32{
1, // 0: plaintext.pb.Exchange.pubkey:type_name -> crypto.pb.PublicKey
1, // [1:1] is the sub-list for method output_type
1, // [1:1] is the sub-list for method input_type
1, // [1:1] is the sub-list for extension type_name
1, // [1:1] is the sub-list for extension extendee
0, // [0:1] is the sub-list for field type_name
}
func init() { file_pb_plaintext_proto_init() }
func file_pb_plaintext_proto_init() {
if File_pb_plaintext_proto != nil {
return
}
if !protoimpl.UnsafeEnabled {
file_pb_plaintext_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*Exchange); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_pb_plaintext_proto_rawDesc,
NumEnums: 0,
NumMessages: 1,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_pb_plaintext_proto_goTypes,
DependencyIndexes: file_pb_plaintext_proto_depIdxs,
MessageInfos: file_pb_plaintext_proto_msgTypes,
}.Build()
File_pb_plaintext_proto = out.File
file_pb_plaintext_proto_rawDesc = nil
file_pb_plaintext_proto_goTypes = nil
file_pb_plaintext_proto_depIdxs = nil
}

View File

@@ -0,0 +1,10 @@
syntax = "proto2";
package plaintext.pb;
import "core/crypto/pb/crypto.proto";
message Exchange {
optional bytes id = 1;
optional crypto.pb.PublicKey pubkey = 2;
}

View File

@@ -0,0 +1,31 @@
// Package sec provides secure connection and transport interfaces for libp2p.
package sec
import (
"context"
"net"
"github.com/libp2p/go-libp2p/core/network"
"github.com/libp2p/go-libp2p/core/peer"
"github.com/libp2p/go-libp2p/core/protocol"
)
// SecureConn is an authenticated, encrypted connection.
type SecureConn interface {
net.Conn
network.ConnSecurity
}
// A SecureTransport turns inbound and outbound unauthenticated,
// plain-text, native connections into authenticated, encrypted connections.
type SecureTransport interface {
// SecureInbound secures an inbound connection.
// If p is empty, connections from any peer are accepted.
SecureInbound(ctx context.Context, insecure net.Conn, p peer.ID) (SecureConn, error)
// SecureOutbound secures an outbound connection.
SecureOutbound(ctx context.Context, insecure net.Conn, p peer.ID) (SecureConn, error)
// ID is the protocol ID of the security protocol.
ID() protocol.ID
}

View File

@@ -0,0 +1,126 @@
// Package transport provides the Transport interface, which represents
// the devices and network protocols used to send and receive data.
package transport
import (
"context"
"errors"
"net"
"github.com/libp2p/go-libp2p/core/network"
"github.com/libp2p/go-libp2p/core/peer"
ma "github.com/multiformats/go-multiaddr"
manet "github.com/multiformats/go-multiaddr/net"
)
// A CapableConn represents a connection that has offers the basic
// capabilities required by libp2p: stream multiplexing, encryption and
// peer authentication.
//
// These capabilities may be natively provided by the transport, or they
// may be shimmed via the "connection upgrade" process, which converts a
// "raw" network connection into one that supports such capabilities by
// layering an encryption channel and a stream multiplexer.
//
// CapableConn provides accessors for the local and remote multiaddrs used to
// establish the connection and an accessor for the underlying Transport.
type CapableConn interface {
network.MuxedConn
network.ConnSecurity
network.ConnMultiaddrs
network.ConnScoper
// Transport returns the transport to which this connection belongs.
Transport() Transport
}
// Transport represents any device by which you can connect to and accept
// connections from other peers.
//
// The Transport interface allows you to open connections to other peers
// by dialing them, and also lets you listen for incoming connections.
//
// Connections returned by Dial and passed into Listeners are of type
// CapableConn, which means that they have been upgraded to support
// stream multiplexing and connection security (encryption and authentication).
//
// If a transport implements `io.Closer` (optional), libp2p will call `Close` on
// shutdown. NOTE: `Dial` and `Listen` may be called after or concurrently with
// `Close`.
//
// For a conceptual overview, see https://docs.libp2p.io/concepts/transport/
type Transport interface {
// Dial dials a remote peer. It should try to reuse local listener
// addresses if possible, but it may choose not to.
Dial(ctx context.Context, raddr ma.Multiaddr, p peer.ID) (CapableConn, error)
// CanDial returns true if this transport knows how to dial the given
// multiaddr.
//
// Returning true does not guarantee that dialing this multiaddr will
// succeed. This function should *only* be used to preemptively filter
// out addresses that we can't dial.
CanDial(addr ma.Multiaddr) bool
// Listen listens on the passed multiaddr.
Listen(laddr ma.Multiaddr) (Listener, error)
// Protocol returns the set of protocols handled by this transport.
//
// See the Network interface for an explanation of how this is used.
Protocols() []int
// Proxy returns true if this is a proxy transport.
//
// See the Network interface for an explanation of how this is used.
// TODO: Make this a part of the go-multiaddr protocol instead?
Proxy() bool
}
// Resolver can be optionally implemented by transports that want to resolve or transform the
// multiaddr.
type Resolver interface {
Resolve(ctx context.Context, maddr ma.Multiaddr) ([]ma.Multiaddr, error)
}
// Listener is an interface closely resembling the net.Listener interface. The
// only real difference is that Accept() returns Conn's of the type in this
// package, and also exposes a Multiaddr method as opposed to a regular Addr
// method
type Listener interface {
Accept() (CapableConn, error)
Close() error
Addr() net.Addr
Multiaddr() ma.Multiaddr
}
// ErrListenerClosed is returned by Listener.Accept when the listener is gracefully closed.
var ErrListenerClosed = errors.New("listener closed")
// TransportNetwork is an inet.Network with methods for managing transports.
type TransportNetwork interface {
network.Network
// AddTransport adds a transport to this Network.
//
// When dialing, this Network will iterate over the protocols in the
// remote multiaddr and pick the first protocol registered with a proxy
// transport, if any. Otherwise, it'll pick the transport registered to
// handle the last protocol in the multiaddr.
//
// When listening, this Network will iterate over the protocols in the
// local multiaddr and pick the *last* protocol registered with a proxy
// transport, if any. Otherwise, it'll pick the transport registered to
// handle the last protocol in the multiaddr.
AddTransport(t Transport) error
}
// Upgrader is a multistream upgrader that can upgrade an underlying connection
// to a full transport connection (secure and multiplexed).
type Upgrader interface {
// UpgradeListener upgrades the passed multiaddr-net listener into a full libp2p-transport listener.
UpgradeListener(Transport, manet.Listener) Listener
// Upgrade upgrades the multiaddr/net connection into a full libp2p-transport connection.
Upgrade(ctx context.Context, t Transport, maconn manet.Conn, dir network.Direction, p peer.ID, scope network.ConnManagementScope) (CapableConn, error)
}