kubo/peer/peerstore.go
Brian Tiger Chow c69f6895db feat(peerstore) add peer to peerstore upon instantiation
License: MIT
Signed-off-by: Brian Tiger Chow <brian@perfmode.com>
2014-12-08 02:10:42 -08:00

133 lines
2.9 KiB
Go

package peer
import (
"sync"
ic "github.com/jbenet/go-ipfs/crypto"
u "github.com/jbenet/go-ipfs/util"
errors "github.com/jbenet/go-ipfs/util/debugerror"
)
// Peerstore provides a threadsafe collection for peers.
type Peerstore interface {
FindOrCreate(ID) (Peer, error)
Add(Peer) (Peer, error)
Delete(ID) error
All() (*Map, error)
WithKeyPair(sk ic.PrivKey, pk ic.PubKey) (Peer, error)
WithID(id ID) Peer
WithIDString(id string) Peer
}
type peerstore struct {
sync.RWMutex
data map[string]Peer // key is string(ID)
}
// NewPeerstore creates a threadsafe collection of peers.
func NewPeerstore() Peerstore {
return &peerstore{
data: make(map[string]Peer),
}
}
func (ps *peerstore) FindOrCreate(i ID) (Peer, error) {
ps.Lock()
defer ps.Unlock()
if i == nil {
panic("wat")
}
p, ok := ps.data[i.String()]
if !ok { // not found, construct it ourselves, add it to datastore, and return.
// TODO(brian) kinda dangerous, no? If ID is invalid and doesn't
// correspond to an actual valid peer ID, this peerstore will return an
// instantiated peer value, allowing the error to propagate. It might
// be better to nip this at the bud by returning nil and making the
// client manually add a Peer. To keep the peerstore in control, this
// can even be a peerstore method that performs cursory validation.
//
// Potential bad case: Suppose values arrive from untrusted providers
// in the DHT.
p = &peer{id: i}
ps.data[i.String()] = p
}
// no error, got it back fine
return p, nil
}
func (p *peerstore) Add(peer Peer) (Peer, error) {
p.Lock()
defer p.Unlock()
existing, ok := p.data[peer.Key().String()]
if !ok { // not found? just add and return.
p.data[peer.Key().String()] = peer
return peer, nil
}
// already here.
if peer == existing {
return peer, nil
}
existing.Update(peer) // must do some merging.
return existing, nil
}
func (p *peerstore) Delete(i ID) error {
p.Lock()
defer p.Unlock()
delete(p.data, i.String())
return nil
}
func (p *peerstore) All() (*Map, error) {
p.Lock()
defer p.Unlock()
ps := Map{}
for k, v := range p.data {
ps[u.Key(k)] = v
}
return &ps, nil
}
// WithKeyPair returns a Peer object with given keys.
func (ps *peerstore) WithKeyPair(sk ic.PrivKey, pk ic.PubKey) (Peer, error) {
if sk == nil && pk == nil {
return nil, errors.Errorf("PeerWithKeyPair nil keys")
}
pk2 := sk.GetPublic()
if pk == nil {
pk = pk2
} else if !pk.Equals(pk2) {
return nil, errors.Errorf("key mismatch. pubkey is not privkey's pubkey")
}
pkid, err := IDFromPubKey(pk)
if err != nil {
return nil, errors.Errorf("Failed to hash public key: %v", err)
}
p := &peer{id: pkid, pubKey: pk, privKey: sk}
ps.Add(p)
return p, nil
}
// WithID constructs a peer with given ID.
func (ps *peerstore) WithID(id ID) Peer {
p := &peer{id: id}
ps.Add(p)
return p
}
// WithIDString constructs a peer with given ID (string).
func (ps *peerstore) WithIDString(id string) Peer {
return ps.WithID(ID(id))
}