mirror of
https://github.com/status-im/status-go.git
synced 2025-02-09 13:24:49 +00:00
Update vendor Integrate rendezvous into status node Add a test with failover using rendezvous Use multiple servers in client Use discovery V5 by default and test that node can be started with rendezvous discovet Fix linter Update rendezvous client to one with instrumented stream Address feedback Fix test with updated topic limits Apply several suggestions Change log to debug for request errors because we continue execution Remove web3js after rebase Update rendezvous package
345 lines
7.7 KiB
Go
345 lines
7.7 KiB
Go
package peerstore
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"sync"
|
|
"time"
|
|
|
|
ic "github.com/libp2p/go-libp2p-crypto"
|
|
|
|
//ds "github.com/jbenet/go-datastore"
|
|
//dssync "github.com/jbenet/go-datastore/sync"
|
|
logging "github.com/ipfs/go-log"
|
|
"github.com/libp2p/go-libp2p-peer"
|
|
ma "github.com/multiformats/go-multiaddr"
|
|
)
|
|
|
|
var log = logging.Logger("peerstore")
|
|
|
|
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() []peer.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(peer.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 peer.ID, key string) (interface{}, error)
|
|
Put(id peer.ID, key string, val interface{}) error
|
|
|
|
GetProtocols(peer.ID) ([]string, error)
|
|
AddProtocols(peer.ID, ...string) error
|
|
SetProtocols(peer.ID, ...string) error
|
|
SupportsProtocols(peer.ID, ...string) ([]string, 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 peer.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 peer.ID, addrs []ma.Multiaddr, ttl time.Duration)
|
|
|
|
// SetAddr calls mgr.SetAddrs(p, addr, ttl)
|
|
SetAddr(p peer.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 peer.ID, addrs []ma.Multiaddr, ttl time.Duration)
|
|
|
|
// UpdateAddrs updates the addresses associated with the given peer that have
|
|
// the given oldTTL to have the given newTTL.
|
|
UpdateAddrs(p peer.ID, oldTTL time.Duration, newTTL time.Duration)
|
|
|
|
// Addresses returns all known (and valid) addresses for a given peer
|
|
Addrs(p peer.ID) []ma.Multiaddr
|
|
|
|
// AddrStream returns a channel that gets all addresses for a given
|
|
// peer sent on it. If new addresses are added after the call is made
|
|
// they will be sent along through the channel as well.
|
|
AddrStream(context.Context, peer.ID) <-chan ma.Multiaddr
|
|
|
|
// ClearAddresses removes all previously stored addresses
|
|
ClearAddrs(p peer.ID)
|
|
}
|
|
|
|
// KeyBook tracks the Public keys of Peers.
|
|
type KeyBook interface {
|
|
PubKey(peer.ID) ic.PubKey
|
|
AddPubKey(peer.ID, ic.PubKey) error
|
|
|
|
PrivKey(peer.ID) ic.PrivKey
|
|
AddPrivKey(peer.ID, ic.PrivKey) error
|
|
}
|
|
|
|
type keybook struct {
|
|
pks map[peer.ID]ic.PubKey
|
|
sks map[peer.ID]ic.PrivKey
|
|
|
|
sync.RWMutex // same lock. wont happen a ton.
|
|
}
|
|
|
|
func newKeybook() *keybook {
|
|
return &keybook{
|
|
pks: map[peer.ID]ic.PubKey{},
|
|
sks: map[peer.ID]ic.PrivKey{},
|
|
}
|
|
}
|
|
|
|
func (kb *keybook) Peers() []peer.ID {
|
|
kb.RLock()
|
|
ps := make([]peer.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 peer.ID) ic.PubKey {
|
|
kb.RLock()
|
|
pk := kb.pks[p]
|
|
kb.RUnlock()
|
|
if pk != nil {
|
|
return pk
|
|
}
|
|
pk, err := p.ExtractPublicKey()
|
|
if err == nil && pk != nil {
|
|
kb.Lock()
|
|
kb.pks[p] = pk
|
|
kb.Unlock()
|
|
}
|
|
return pk
|
|
}
|
|
|
|
func (kb *keybook) AddPubKey(p peer.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 peer.ID) ic.PrivKey {
|
|
kb.RLock()
|
|
sk := kb.sks[p]
|
|
kb.RUnlock()
|
|
return sk
|
|
}
|
|
|
|
func (kb *keybook) AddPrivKey(p peer.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
|
|
|
|
// lock for protocol information, separate from datastore lock
|
|
protolock sync.Mutex
|
|
}
|
|
|
|
// NewPeerstore creates a threadsafe collection of peers.
|
|
func NewPeerstore() Peerstore {
|
|
return &peerstore{
|
|
keybook: newKeybook(),
|
|
metrics: NewMetrics(),
|
|
AddrManager: AddrManager{},
|
|
//ds: dssync.MutexWrap(ds.NewMapDatastore()),
|
|
ds: make(map[string]interface{}),
|
|
}
|
|
}
|
|
|
|
func (ps *peerstore) Put(p peer.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
|
|
}
|
|
|
|
var ErrNotFound = errors.New("item not found")
|
|
|
|
func (ps *peerstore) Get(p peer.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, ErrNotFound
|
|
}
|
|
return i, nil
|
|
}
|
|
|
|
func (ps *peerstore) Peers() []peer.ID {
|
|
set := map[peer.ID]struct{}{}
|
|
for _, p := range ps.keybook.Peers() {
|
|
set[p] = struct{}{}
|
|
}
|
|
for _, p := range ps.AddrManager.Peers() {
|
|
set[p] = struct{}{}
|
|
}
|
|
|
|
pps := make([]peer.ID, 0, len(set))
|
|
for p := range set {
|
|
pps = append(pps, p)
|
|
}
|
|
return pps
|
|
}
|
|
|
|
func (ps *peerstore) PeerInfo(p peer.ID) PeerInfo {
|
|
return PeerInfo{
|
|
ID: p,
|
|
Addrs: ps.AddrManager.Addrs(p),
|
|
}
|
|
}
|
|
|
|
func (ps *peerstore) SetProtocols(p peer.ID, protos ...string) error {
|
|
ps.protolock.Lock()
|
|
defer ps.protolock.Unlock()
|
|
|
|
protomap := make(map[string]struct{})
|
|
for _, proto := range protos {
|
|
protomap[proto] = struct{}{}
|
|
}
|
|
|
|
return ps.Put(p, "protocols", protomap)
|
|
}
|
|
|
|
func (ps *peerstore) AddProtocols(p peer.ID, protos ...string) error {
|
|
ps.protolock.Lock()
|
|
defer ps.protolock.Unlock()
|
|
protomap, err := ps.getProtocolMap(p)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
for _, proto := range protos {
|
|
protomap[proto] = struct{}{}
|
|
}
|
|
|
|
return ps.Put(p, "protocols", protomap)
|
|
}
|
|
|
|
func (ps *peerstore) getProtocolMap(p peer.ID) (map[string]struct{}, error) {
|
|
iprotomap, err := ps.Get(p, "protocols")
|
|
switch err {
|
|
default:
|
|
return nil, err
|
|
case ErrNotFound:
|
|
return make(map[string]struct{}), nil
|
|
case nil:
|
|
cast, ok := iprotomap.(map[string]struct{})
|
|
if !ok {
|
|
return nil, fmt.Errorf("stored protocol set was not a map")
|
|
}
|
|
|
|
return cast, nil
|
|
}
|
|
}
|
|
|
|
func (ps *peerstore) GetProtocols(p peer.ID) ([]string, error) {
|
|
ps.protolock.Lock()
|
|
defer ps.protolock.Unlock()
|
|
pmap, err := ps.getProtocolMap(p)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var out []string
|
|
for k, _ := range pmap {
|
|
out = append(out, k)
|
|
}
|
|
|
|
return out, nil
|
|
}
|
|
|
|
func (ps *peerstore) SupportsProtocols(p peer.ID, protos ...string) ([]string, error) {
|
|
ps.protolock.Lock()
|
|
defer ps.protolock.Unlock()
|
|
pmap, err := ps.getProtocolMap(p)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var out []string
|
|
for _, proto := range protos {
|
|
if _, ok := pmap[proto]; ok {
|
|
out = append(out, proto)
|
|
}
|
|
}
|
|
|
|
return out, nil
|
|
}
|
|
|
|
func PeerInfos(ps Peerstore, peers []peer.ID) []PeerInfo {
|
|
pi := make([]PeerInfo, len(peers))
|
|
for i, p := range peers {
|
|
pi[i] = ps.PeerInfo(p)
|
|
}
|
|
return pi
|
|
}
|
|
|
|
func PeerInfoIDs(pis []PeerInfo) []peer.ID {
|
|
ps := make([]peer.ID, len(pis))
|
|
for i, pi := range pis {
|
|
ps[i] = pi.ID
|
|
}
|
|
return ps
|
|
}
|