This repository has been archived by the owner on Sep 6, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 75
/
peer.go
245 lines (215 loc) · 6.56 KB
/
peer.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
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
88
89
90
91
92
93
94
95
96
97
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
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
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
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
// Package peer implements an object used to represent peers in the libp2p network.
package peer
import (
"encoding/hex"
"errors"
"fmt"
"strings"
cid "github.com/ipfs/go-cid"
ic "github.com/libp2p/go-libp2p-core/crypto"
b58 "github.com/mr-tron/base58/base58"
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.
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()
}
// 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.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
}
// 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
}
// 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
}
// IDB58Decode decodes a peer ID.
//
// Deprecated: Use Decode.
func IDB58Decode(s string) (ID, error) {
return Decode(s)
}
// IDB58Encode returns the base58-encoded multihash representation of the ID.
//
// Deprecated: Use Encode.
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.
//
// Deprecated: Don't raw-hex encode peer IDs, use base16 CIDs.
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.
//
// Deprecated: Don't raw-hex encode peer IDs, use base16 CIDs.
func IDHexEncode(id ID) string {
return hex.EncodeToString([]byte(id))
}
// 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()
if ty != cid.Libp2pKey {
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{}
}
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 := pk.Bytes()
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]) }