go-libp2p/p2p/peer/peerstore.go

233 lines
5.0 KiB
Go

package peer
import (
"errors"
"sync"
"time"
ic "github.com/ipfs/go-libp2p/p2p/crypto"
//ds "github.com/jbenet/go-datastore"
//dssync "github.com/jbenet/go-datastore/sync"
ma "gx/ipfs/QmcobAGsCjYt5DXoq9et9L8yR8er7o7Cu3DTvpaq12jYSz/go-multiaddr"
)
const (
// AddressTTL is the expiration time of addresses.
AddressTTL = time.Hour
)
// Peerstore provides a threadsafe store of Peer related
// information.
type Peerstore interface {
AddrBook
KeyBook
Metrics
// Peers returns a list of all peer.IDs in this Peerstore
Peers() []ID
// 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(ID) PeerInfo
// 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(id ID, key string) (interface{}, error)
Put(id ID, key string, val interface{}) error
}
// AddrBook is an interface that fits the new AddrManager. I'm patching
// it up in here to avoid changing a ton of the codebase.
type AddrBook interface {
// AddAddr calls AddAddrs(p, []ma.Multiaddr{addr}, ttl)
AddAddr(p ID, addr ma.Multiaddr, ttl time.Duration)
// AddAddrs gives AddrManager 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 ID, addrs []ma.Multiaddr, ttl time.Duration)
// SetAddr calls mgr.SetAddrs(p, addr, ttl)
SetAddr(p 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 ID, addrs []ma.Multiaddr, ttl time.Duration)
// Addresses returns all known (and valid) addresses for a given
Addrs(p ID) []ma.Multiaddr
// ClearAddresses removes all previously stored addresses
ClearAddrs(p ID)
}
// KeyBook tracks the Public keys of Peers.
type KeyBook interface {
PubKey(ID) ic.PubKey
AddPubKey(ID, ic.PubKey) error
PrivKey(ID) ic.PrivKey
AddPrivKey(ID, ic.PrivKey) error
}
type keybook struct {
pks map[ID]ic.PubKey
sks map[ID]ic.PrivKey
sync.RWMutex // same lock. wont happen a ton.
}
func newKeybook() *keybook {
return &keybook{
pks: map[ID]ic.PubKey{},
sks: map[ID]ic.PrivKey{},
}
}
func (kb *keybook) Peers() []ID {
kb.RLock()
ps := make([]ID, 0, len(kb.pks)+len(kb.sks))
for p := range kb.pks {
ps = append(ps, p)
}
for p := range kb.sks {
if _, found := kb.pks[p]; !found {
ps = append(ps, p)
}
}
kb.RUnlock()
return ps
}
func (kb *keybook) PubKey(p ID) ic.PubKey {
kb.RLock()
pk := kb.pks[p]
kb.RUnlock()
return pk
}
func (kb *keybook) AddPubKey(p ID, pk ic.PubKey) error {
// check it's correct first
if !p.MatchesPublicKey(pk) {
return errors.New("ID does not match PublicKey")
}
kb.Lock()
kb.pks[p] = pk
kb.Unlock()
return nil
}
func (kb *keybook) PrivKey(p ID) ic.PrivKey {
kb.RLock()
sk := kb.sks[p]
kb.RUnlock()
return sk
}
func (kb *keybook) AddPrivKey(p ID, sk ic.PrivKey) error {
if sk == nil {
return errors.New("sk is nil (PrivKey)")
}
// check it's correct first
if !p.MatchesPrivateKey(sk) {
return errors.New("ID does not match PrivateKey")
}
kb.Lock()
kb.sks[p] = sk
kb.Unlock()
return nil
}
type peerstore struct {
keybook
metrics
AddrManager
// store other data, like versions
//ds ds.ThreadSafeDatastore
// TODO: use a datastore for this
ds map[string]interface{}
dslock sync.Mutex
}
// NewPeerstore creates a threadsafe collection of peers.
func NewPeerstore() Peerstore {
return &peerstore{
keybook: *newKeybook(),
metrics: *(NewMetrics()).(*metrics),
AddrManager: AddrManager{},
//ds: dssync.MutexWrap(ds.NewMapDatastore()),
ds: make(map[string]interface{}),
}
}
func (ps *peerstore) Put(p ID, key string, val interface{}) error {
//dsk := ds.NewKey(string(p) + "/" + key)
//return ps.ds.Put(dsk, val)
ps.dslock.Lock()
defer ps.dslock.Unlock()
ps.ds[string(p)+"/"+key] = val
return nil
}
func (ps *peerstore) Get(p ID, key string) (interface{}, error) {
//dsk := ds.NewKey(string(p) + "/" + key)
//return ps.ds.Get(dsk)
ps.dslock.Lock()
defer ps.dslock.Unlock()
i, ok := ps.ds[string(p)+"/"+key]
if !ok {
return nil, errors.New("item not found")
}
return i, nil
}
func (ps *peerstore) Peers() []ID {
set := map[ID]struct{}{}
for _, p := range ps.keybook.Peers() {
set[p] = struct{}{}
}
for _, p := range ps.AddrManager.Peers() {
set[p] = struct{}{}
}
pps := make([]ID, 0, len(set))
for p := range set {
pps = append(pps, p)
}
return pps
}
func (ps *peerstore) PeerInfo(p ID) PeerInfo {
return PeerInfo{
ID: p,
Addrs: ps.AddrManager.Addrs(p),
}
}
func PeerInfos(ps Peerstore, peers []ID) []PeerInfo {
pi := make([]PeerInfo, len(peers))
for i, p := range peers {
pi[i] = ps.PeerInfo(p)
}
return pi
}
func PeerInfoIDs(pis []PeerInfo) []ID {
ps := make([]ID, len(pis))
for i, pi := range pis {
ps[i] = pi.ID
}
return ps
}