233 lines
5.0 KiB
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
|
|
}
|