peer.go 6.55 KB
Newer Older
tavit ohanian's avatar
tavit ohanian committed
1
// Package peer implements an object used to represent peers in the p2p network.
2 3 4 5 6 7
package peer

import (
	"encoding/hex"
	"errors"
	"fmt"
8
	"strings"
9 10 11

	b58 "github.com/mr-tron/base58/base58"
	mh "github.com/multiformats/go-multihash"
12
	cid "gitlab.dms3.io/dms3/go-cid"
tavit ohanian's avatar
tavit ohanian committed
13
	ic "gitlab.dms3.io/p2p/go-p2p-core/crypto"
14 15 16 17 18 19 20 21 22 23 24 25 26 27
)

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.
tavit ohanian's avatar
tavit ohanian committed
28
// See: https://gitlab.dms3.io/p2p/specs/issues/138
29 30 31 32 33 34 35 36 37
//
// 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

tavit ohanian's avatar
tavit ohanian committed
38
// ID is a p2p peer identity.
39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
//
// 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.
func (id ID) Pretty() string {
	return IDB58Encode(id)
}

// Loggable returns a pretty peer ID string in loggable JSON format.
func (id ID) Loggable() map[string]interface{} {
	return map[string]interface{}{
		"peerID": id.Pretty(),
	}
}

func (id ID) String() string {
	return id.Pretty()
}

Vasco Santos's avatar
Vasco Santos committed
60
// ShortString prints out the peer ID.
61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87
//
// 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.Pretty()
	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
}

Vasco Santos's avatar
Vasco Santos committed
88
// ExtractPublicKey attempts to extract the public key from an ID.
89 90 91 92 93 94 95 96
//
// 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
	}
97
	if decoded.Code != mh.IDENTITY {
98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133
		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
}

// IDFromString casts a string to the ID type, and validates
// the value to make sure it is a multihash.
func IDFromString(s string) (ID, error) {
	if _, err := mh.Cast([]byte(s)); err != nil {
		return ID(""), err
	}
	return ID(s), 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
}

134 135 136
// IDB58Decode decodes a peer ID.
//
// Deprecated: Use Decode.
137
func IDB58Decode(s string) (ID, error) {
138
	return Decode(s)
139 140 141
}

// IDB58Encode returns the base58-encoded multihash representation of the ID.
142 143
//
// Deprecated: Use Encode.
144 145 146 147 148 149
func IDB58Encode(id ID) string {
	return b58.Encode([]byte(id))
}

// IDHexDecode accepts a hex-encoded multihash representing a peer ID
// and returns the decoded ID if the input is valid.
150 151
//
// Deprecated: Don't raw-hex encode peer IDs, use base16 CIDs.
152 153 154 155 156 157 158 159 160
func IDHexDecode(s string) (ID, error) {
	m, err := mh.FromHexString(s)
	if err != nil {
		return "", err
	}
	return ID(m), err
}

// IDHexEncode returns the hex-encoded multihash representation of the ID.
161 162
//
// Deprecated: Don't raw-hex encode peer IDs, use base16 CIDs.
163 164 165 166
func IDHexEncode(id ID) string {
	return hex.EncodeToString([]byte(id))
}

167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199
// 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.
func Encode(id ID) string {
	return IDB58Encode(id)
}

// FromCid converts a CID to a peer ID, if possible.
func FromCid(c cid.Cid) (ID, error) {
	ty := c.Type()
tavit ohanian's avatar
tavit ohanian committed
200
	if ty != cid.P2pKey {
201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217
		s := cid.CodecToStr[ty]
		if s == "" {
			s = fmt.Sprintf("[unknown multicodec %d]", ty)
		}
		return "", fmt.Errorf("can't convert CID of type %s to a peer ID", s)
	}
	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{}
	}
tavit ohanian's avatar
tavit ohanian committed
218
	return cid.NewCidV1(cid.P2pKey, m)
219 220
}

221 222 223 224 225 226 227 228
// IDFromPublicKey returns the Peer ID corresponding to the public key pk.
func IDFromPublicKey(pk ic.PubKey) (ID, error) {
	b, err := pk.Bytes()
	if err != nil {
		return "", err
	}
	var alg uint64 = mh.SHA2_256
	if AdvancedEnableInlining && len(b) <= maxInlineKeyLength {
Marten Seemann's avatar
Marten Seemann committed
229
		alg = mh.IDENTITY
230 231 232 233 234 235 236 237 238 239
	}
	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())
}

Vasco Santos's avatar
Vasco Santos committed
240
// IDSlice for sorting peers.
241 242 243 244 245
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]) }