peer.go 8.44 KB
Newer Older
1
// package peer implements an object used to represent peers in the ipfs network.
2 3 4
package peer

import (
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
5
	"bytes"
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
6
	"errors"
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
7
	"fmt"
Jeromy's avatar
Jeromy committed
8
	"sync"
Jeromy's avatar
Jeromy committed
9
	"time"
10

11 12 13
	b58 "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-base58"
	ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr"
	mh "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash"
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
14

15
	ic "github.com/jbenet/go-ipfs/crypto"
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
16
	u "github.com/jbenet/go-ipfs/util"
17 18
)

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
19 20
var log = u.Logger("peer")

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
21 22
// ID is a byte slice representing the identity of a peer.
type ID mh.Multihash
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
23

24 25 26 27 28
// String is utililty function for printing out peer ID strings.
func (id ID) String() string {
	return id.Pretty()
}

Siraj Ravel's avatar
Siraj Ravel committed
29
// Equal is utililty function for comparing two peer ID's
30 31
func (id ID) Equal(other ID) bool {
	return bytes.Equal(id, other)
32 33
}

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
34
// Pretty returns a b58-encoded string of the ID
35
func (id ID) Pretty() string {
36
	return b58.Encode(id)
37 38
}

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
39 40 41 42 43
// DecodePrettyID returns a b58-encoded string of the ID
func DecodePrettyID(s string) ID {
	return b58.Decode(s)
}

44 45 46 47 48 49 50 51 52 53
// IDFromPubKey retrieves a Public Key from the peer given by pk
func IDFromPubKey(pk ic.PubKey) (ID, error) {
	b, err := pk.Bytes()
	if err != nil {
		return nil, err
	}
	hash := u.Hash(b)
	return ID(hash), nil
}

54 55
// Map maps Key (string) : *peer (slices are not comparable).
type Map map[u.Key]Peer
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
56

Juan Batiz-Benet's avatar
go lint  
Juan Batiz-Benet committed
57
// Peer represents the identity information of an IPFS Node, including
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
58
// ID, and relevant Addresses.
59
type Peer interface {
Brian Tiger Chow's avatar
Brian Tiger Chow committed
60 61 62 63

	// TODO reduce the peer interface to be read-only. Force mutations to occur
	// on the peer store eg. peerstore.SetLatency(peerId, value).

64 65
	// ID returns the peer's ID
	ID() ID
Jeromy's avatar
Jeromy committed
66

67 68 69 70 71 72 73
	// Key returns the ID as a Key (string) for maps.
	Key() u.Key

	// Addresses returns the peer's multiaddrs
	Addresses() []ma.Multiaddr

	// AddAddress adds the given Multiaddr address to Peer's addresses.
74 75
	// returns whether this was a newly added address.
	AddAddress(a ma.Multiaddr) bool
76 77 78 79

	// NetAddress returns the first Multiaddr found for a given network.
	NetAddress(n string) ma.Multiaddr

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
80 81 82 83
	// Services returns the peer's services
	// Services() []mux.ProtocolID
	// SetServices([]mux.ProtocolID)

84 85 86 87 88 89 90 91 92 93 94 95 96
	// Priv/PubKey returns the peer's Private Key
	PrivKey() ic.PrivKey
	PubKey() ic.PubKey

	// LoadAndVerifyKeyPair unmarshalls, loads a private/public key pair.
	// Error if (a) unmarshalling fails, or (b) pubkey does not match id.
	LoadAndVerifyKeyPair(marshalled []byte) error
	VerifyAndSetPrivKey(sk ic.PrivKey) error
	VerifyAndSetPubKey(pk ic.PubKey) error

	// Get/SetLatency manipulate the current latency measurement.
	GetLatency() (out time.Duration)
	SetLatency(laten time.Duration)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
97 98 99

	// Update with the data of another peer instance
	Update(Peer) error
100 101 102 103 104
}

type peer struct {
	id        ID
	addresses []ma.Multiaddr
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
105
	// services  []mux.ProtocolID
106 107 108

	privKey ic.PrivKey
	pubKey  ic.PubKey
109

Brian Tiger Chow's avatar
Brian Tiger Chow committed
110 111
	// TODO move latency away from peer into the package that uses it. Instead,
	// within that package, map from ID to latency value.
112 113 114
	latency time.Duration

	sync.RWMutex
115 116
}

117
// String prints out the peer.
Brian Tiger Chow's avatar
Brian Tiger Chow committed
118 119 120 121 122
//
// 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.
123
func (p *peer) String() string {
124 125 126 127 128 129
	pid := p.id.String()
	maxRunes := 12
	if len(pid) < maxRunes {
		maxRunes = len(pid)
	}
	return "[Peer " + pid[:maxRunes] + "]"
130 131
}

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
132
// Key returns the ID as a Key (string) for maps.
133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154
func (p *peer) Key() u.Key {
	return u.Key(p.id)
}

// ID returns the peer's ID
func (p *peer) ID() ID {
	return p.id
}

// PrivKey returns the peer's Private Key
func (p *peer) PrivKey() ic.PrivKey {
	return p.privKey
}

// PubKey returns the peer's Private Key
func (p *peer) PubKey() ic.PubKey {
	return p.pubKey
}

// Addresses returns the peer's multiaddrs
func (p *peer) Addresses() []ma.Multiaddr {
	cp := make([]ma.Multiaddr, len(p.addresses))
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
155
	p.RLock()
156
	copy(cp, p.addresses)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
157
	defer p.RUnlock()
158
	return cp
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
159 160
}

Juan Batiz-Benet's avatar
go lint  
Juan Batiz-Benet committed
161
// AddAddress adds the given Multiaddr address to Peer's addresses.
162 163
// Returns whether this address was a newly added address
func (p *peer) AddAddress(a ma.Multiaddr) bool {
164 165 166
	p.Lock()
	defer p.Unlock()

167
	for _, addr := range p.addresses {
168
		if addr.Equal(a) {
169
			return false
170 171
		}
	}
172
	p.addresses = append(p.addresses, a)
173
	return true
174 175
}

Juan Batiz-Benet's avatar
go lint  
Juan Batiz-Benet committed
176
// NetAddress returns the first Multiaddr found for a given network.
177
func (p *peer) NetAddress(n string) ma.Multiaddr {
178 179 180
	p.RLock()
	defer p.RUnlock()

181
	for _, a := range p.addresses {
182
		for _, p := range a.Protocols() {
183 184 185 186 187 188 189
			if p.Name == n {
				return a
			}
		}
	}
	return nil
}
Jeromy's avatar
Jeromy committed
190

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
191 192 193 194 195 196 197 198 199 200 201 202
// func (p *peer) Services() []mux.ProtocolID {
// 	p.RLock()
// 	defer p.RUnlock()
// 	return p.services
// }
//
// func (p *peer) SetServices(s []mux.ProtocolID) {
// 	p.Lock()
// 	defer p.Unlock()
// 	p.services = s
// }

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
203
// GetLatency retrieves the current latency measurement.
204
func (p *peer) GetLatency() (out time.Duration) {
205
	p.RLock()
206
	out = p.latency
207
	p.RUnlock()
208
	return
Jeromy's avatar
Jeromy committed
209 210
}

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
211
// SetLatency sets the latency measurement.
212 213
// TODO: Instead of just keeping a single number,
//		 keep a running average over the last hour or so
214
// Yep, should be EWMA or something. (-jbenet)
215
func (p *peer) SetLatency(laten time.Duration) {
216
	p.Lock()
217 218 219 220 221
	if p.latency == 0 {
		p.latency = laten
	} else {
		p.latency = ((p.latency * 9) + laten) / 10
	}
222
	p.Unlock()
Jeromy's avatar
Jeromy committed
223
}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
224 225 226

// LoadAndVerifyKeyPair unmarshalls, loads a private/public key pair.
// Error if (a) unmarshalling fails, or (b) pubkey does not match id.
227
func (p *peer) LoadAndVerifyKeyPair(marshalled []byte) error {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
228 229 230 231 232
	sk, err := ic.UnmarshalPrivateKey(marshalled)
	if err != nil {
		return fmt.Errorf("Failed to unmarshal private key: %v", err)
	}

233 234 235 236 237 238
	return p.VerifyAndSetPrivKey(sk)
}

// VerifyAndSetPrivKey sets private key, given its pubkey matches the peer.ID
func (p *peer) VerifyAndSetPrivKey(sk ic.PrivKey) error {

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
239 240 241 242 243
	// construct and assign pubkey. ensure it matches this peer
	if err := p.VerifyAndSetPubKey(sk.GetPublic()); err != nil {
		return err
	}

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
244 245 246
	p.Lock()
	defer p.Unlock()

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
247
	// if we didn't have the priavte key, assign it
248 249
	if p.privKey == nil {
		p.privKey = sk
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
250 251 252 253
		return nil
	}

	// if we already had the keys, check they're equal.
254
	if p.privKey.Equals(sk) {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
255 256 257 258 259 260
		return nil // as expected. keep the old objects.
	}

	// keys not equal. invariant violated. this warrants a panic.
	// these keys should be _the same_ because peer.ID = H(pk)
	// this mismatch should never happen.
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
261
	log.Errorf("%s had PrivKey: %v -- got %v", p, p.privKey, sk)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
262 263 264 265
	panic("invariant violated: unexpected key mismatch")
}

// VerifyAndSetPubKey sets public key, given it matches the peer.ID
266
func (p *peer) VerifyAndSetPubKey(pk ic.PubKey) error {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
267 268 269 270 271
	pkid, err := IDFromPubKey(pk)
	if err != nil {
		return fmt.Errorf("Failed to hash public key: %v", err)
	}

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
272 273 274
	p.Lock()
	defer p.Unlock()

275
	if !p.id.Equal(pkid) {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
276 277 278 279
		return fmt.Errorf("Public key does not match peer.ID.")
	}

	// if we didn't have the keys, assign them.
280 281
	if p.pubKey == nil {
		p.pubKey = pk
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
282 283 284 285
		return nil
	}

	// if we already had the pubkey, check they're equal.
286
	if p.pubKey.Equals(pk) {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
287 288 289 290 291 292
		return nil // as expected. keep the old objects.
	}

	// keys not equal. invariant violated. this warrants a panic.
	// these keys should be _the same_ because peer.ID = H(pk)
	// this mismatch should never happen.
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
293
	log.Errorf("%s had PubKey: %v -- got %v", p, p.pubKey, pk)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
294 295
	panic("invariant violated: unexpected key mismatch")
}
296

Jeromy's avatar
Jeromy committed
297
// Updates this peer with information from another peer instance
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
298 299 300 301 302 303 304 305 306
func (p *peer) Update(other Peer) error {
	if !p.ID().Equal(other.ID()) {
		return errors.New("peer ids do not match")
	}

	for _, a := range other.Addresses() {
		p.AddAddress(a)
	}

Brian Tiger Chow's avatar
Brian Tiger Chow committed
307 308
	p.SetLatency(other.GetLatency())

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
309 310 311 312 313 314 315 316 317 318 319 320 321 322
	sk := other.PrivKey()
	pk := other.PubKey()
	p.Lock()
	if p.privKey == nil {
		p.privKey = sk
	}

	if p.pubKey == nil {
		p.pubKey = pk
	}
	defer p.Unlock()
	return nil
}

323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352
// WithKeyPair returns a Peer object with given keys.
func WithKeyPair(sk ic.PrivKey, pk ic.PubKey) (Peer, error) {
	if sk == nil && pk == nil {
		return nil, fmt.Errorf("PeerWithKeyPair nil keys")
	}

	pk2 := sk.GetPublic()
	if pk == nil {
		pk = pk2
	} else if !pk.Equals(pk2) {
		return nil, fmt.Errorf("key mismatch. pubkey is not privkey's pubkey")
	}

	pkid, err := IDFromPubKey(pk)
	if err != nil {
		return nil, fmt.Errorf("Failed to hash public key: %v", err)
	}

	return &peer{id: pkid, pubKey: pk, privKey: sk}, nil
}

// WithID constructs a peer with given ID.
func WithID(id ID) Peer {
	return &peer{id: id}
}

// WithIDString constructs a peer with given ID (string).
func WithIDString(id string) Peer {
	return WithID(ID(id))
}