forked from TrueCloudLab/frostfs-node
239 lines
6 KiB
Go
239 lines
6 KiB
Go
|
package peers
|
||
|
|
||
|
import (
|
||
|
"crypto/ecdsa"
|
||
|
"crypto/elliptic"
|
||
|
|
||
|
"github.com/multiformats/go-multiaddr"
|
||
|
crypto "github.com/nspcc-dev/neofs-crypto"
|
||
|
"github.com/nspcc-dev/neofs-node/lib/netmap"
|
||
|
"github.com/pkg/errors"
|
||
|
"go.uber.org/zap"
|
||
|
)
|
||
|
|
||
|
type (
|
||
|
// Store is an interface to storage of all p2p connections
|
||
|
Store interface {
|
||
|
SelfIDReceiver
|
||
|
PublicKeyStore
|
||
|
AddressIDReceiver
|
||
|
AddPeer(multiaddr.Multiaddr, *ecdsa.PublicKey, *ecdsa.PrivateKey) (ID, error)
|
||
|
DeletePeer(ID)
|
||
|
PeerNetAddressStore
|
||
|
GetPrivateKey(ID) (*ecdsa.PrivateKey, error)
|
||
|
Update(*netmap.NetMap) error
|
||
|
Sign([]byte) ([]byte, error)
|
||
|
Verify(id ID, data, sign []byte) error
|
||
|
Check(min int) error
|
||
|
}
|
||
|
|
||
|
// PublicKeyStore is an interface of the storage of peer's public keys.
|
||
|
PublicKeyStore interface {
|
||
|
GetPublicKey(ID) (*ecdsa.PublicKey, error)
|
||
|
}
|
||
|
|
||
|
// SelfIDReceiver is an interface of local peer ID value with read access.
|
||
|
SelfIDReceiver interface {
|
||
|
SelfID() ID
|
||
|
}
|
||
|
|
||
|
// AddressIDReceiver is an interface of Multiaddr to ID converter.
|
||
|
AddressIDReceiver interface {
|
||
|
AddressID(multiaddr.Multiaddr) (ID, error)
|
||
|
}
|
||
|
|
||
|
// PeerNetAddressStore is an interface of ID to Multiaddr converter.
|
||
|
PeerNetAddressStore interface {
|
||
|
GetAddr(ID) (multiaddr.Multiaddr, error)
|
||
|
}
|
||
|
|
||
|
// StoreParams for creating new Store.
|
||
|
StoreParams struct {
|
||
|
Addr multiaddr.Multiaddr
|
||
|
Key *ecdsa.PrivateKey
|
||
|
Storage Storage
|
||
|
StoreCap int
|
||
|
Logger *zap.Logger
|
||
|
}
|
||
|
|
||
|
store struct {
|
||
|
self ID
|
||
|
addr multiaddr.Multiaddr
|
||
|
storage Storage
|
||
|
log *zap.Logger
|
||
|
key *ecdsa.PrivateKey
|
||
|
}
|
||
|
)
|
||
|
|
||
|
const defaultMinimalSignaturesCount = 3
|
||
|
|
||
|
var errPeerNotFound = errors.New("peer not found")
|
||
|
|
||
|
func (p *store) AddressID(addr multiaddr.Multiaddr) (ID, error) {
|
||
|
if p.addr.Equal(addr) {
|
||
|
return p.self, nil
|
||
|
}
|
||
|
|
||
|
res := p.storage.Filter(maddrFilter(addr))
|
||
|
if len(res) == 0 {
|
||
|
return "", errPeerNotFound
|
||
|
}
|
||
|
|
||
|
return res[0], nil
|
||
|
}
|
||
|
|
||
|
func maddrFilter(addr multiaddr.Multiaddr) PeerFilter {
|
||
|
return func(p Peer) bool { return addr.Equal(p.Address()) }
|
||
|
}
|
||
|
|
||
|
// SelfID return ID of current Node.
|
||
|
func (p *store) SelfID() ID {
|
||
|
return p.self
|
||
|
}
|
||
|
|
||
|
// AddPeer to store..
|
||
|
// Try to get PeerID from PublicKey, or return error
|
||
|
// Store Address and PublicKey for that PeerID.
|
||
|
func (p *store) AddPeer(addr multiaddr.Multiaddr, pub *ecdsa.PublicKey, key *ecdsa.PrivateKey) (ID, error) {
|
||
|
item := NewPeer(addr, pub, key)
|
||
|
if err := p.storage.Set(item.ID(), item); err != nil {
|
||
|
return "", err
|
||
|
}
|
||
|
|
||
|
return item.ID(), nil
|
||
|
}
|
||
|
|
||
|
// DeletePeer from store.
|
||
|
func (p *store) DeletePeer(id ID) {
|
||
|
if err := p.storage.Rem(id); err != nil {
|
||
|
p.log.Error("could not delete peer",
|
||
|
zap.Stringer("id", id),
|
||
|
zap.Error(err))
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Update update Store by new network map.
|
||
|
func (p *store) Update(nm *netmap.NetMap) error {
|
||
|
if err := p.storage.Update(nm); err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
// we must provide our PrivateKey, after updating
|
||
|
if peer, err := p.storage.Get(p.self); err != nil {
|
||
|
peer = NewPeer(p.addr, &p.key.PublicKey, p.key)
|
||
|
return p.storage.Set(p.self, peer)
|
||
|
} else if err := peer.SetPrivateKey(p.key); err != nil {
|
||
|
return errors.Wrapf(err, "could not update private key (%s)", p.self.String())
|
||
|
} else if err := p.storage.Set(p.self, peer); err != nil {
|
||
|
return errors.Wrapf(err, "could not save peer(%s)", p.self.String())
|
||
|
}
|
||
|
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// GetAddr by PeerID.
|
||
|
func (p *store) GetAddr(id ID) (multiaddr.Multiaddr, error) {
|
||
|
n, err := p.storage.Get(id)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
return n.Address(), nil
|
||
|
}
|
||
|
|
||
|
// GetPublicKey by PeerID.
|
||
|
func (p *store) GetPublicKey(id ID) (*ecdsa.PublicKey, error) {
|
||
|
n, err := p.storage.Get(id)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
return n.PublicKey(), nil
|
||
|
}
|
||
|
|
||
|
// GetPrivateKey by PeerID.
|
||
|
func (p *store) GetPrivateKey(id ID) (*ecdsa.PrivateKey, error) {
|
||
|
n, err := p.storage.Get(id)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
return n.PrivateKey()
|
||
|
}
|
||
|
|
||
|
// Sign signs a data using the private key. If the data is longer than
|
||
|
// the bit-length of the private key's curve order, the hash will be
|
||
|
// truncated to that length. It returns the signature as slice bytes.
|
||
|
// The security of the private key depends on the entropy of rand.
|
||
|
func (p *store) Sign(data []byte) ([]byte, error) {
|
||
|
return crypto.Sign(p.key, data)
|
||
|
}
|
||
|
|
||
|
// Verify verifies the signature in r, s of hash using the public key, pub. Its
|
||
|
// return value records whether the signature is valid.
|
||
|
// If store doesn't contains public key for ID,
|
||
|
// returns error about that
|
||
|
// TODO we must provide same method, but for IR list, to check,
|
||
|
// that we have valid signatures of needed IR members
|
||
|
func (p *store) Verify(id ID, data, sign []byte) error {
|
||
|
if pub, err := p.GetPublicKey(id); err != nil {
|
||
|
return errors.Wrap(err, "could not get PublicKey")
|
||
|
} else if err := crypto.Verify(pub, data, sign); err != nil {
|
||
|
return errors.Wrapf(err, "could not verify signature: sign(`%x`) & data(`%x`)", sign, data)
|
||
|
}
|
||
|
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// Neighbours peers that which are distributed by hrw(id).
|
||
|
func (p *store) Neighbours(seed int64, count int) ([]ID, error) {
|
||
|
return p.storage.List(p.self, seed, count)
|
||
|
}
|
||
|
|
||
|
// Check validate signatures count
|
||
|
// TODO replace with settings or something else.
|
||
|
// We can fetch min-count from settings, or
|
||
|
// use another method for validate this..
|
||
|
func (p *store) Check(min int) error {
|
||
|
if min <= defaultMinimalSignaturesCount {
|
||
|
return errors.Errorf("invalid count of valid signatures: minimum %d, actual %d",
|
||
|
defaultMinimalSignaturesCount,
|
||
|
min,
|
||
|
)
|
||
|
}
|
||
|
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// NewStore creates new store by params.
|
||
|
func NewStore(p StoreParams) (Store, error) {
|
||
|
var storage Storage
|
||
|
|
||
|
if p.Key == nil || p.Key.Curve != elliptic.P256() {
|
||
|
return nil, crypto.ErrEmptyPrivateKey
|
||
|
}
|
||
|
|
||
|
if p.Addr == nil {
|
||
|
return nil, errNilMultiaddr
|
||
|
}
|
||
|
|
||
|
if storage = p.Storage; storage == nil {
|
||
|
storage = NewSimpleStorage(p.StoreCap, p.Logger)
|
||
|
}
|
||
|
|
||
|
id := IDFromPublicKey(&p.Key.PublicKey)
|
||
|
peer := NewPeer(p.Addr, &p.Key.PublicKey, p.Key)
|
||
|
|
||
|
if err := storage.Set(id, peer); err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
return &store{
|
||
|
self: id,
|
||
|
storage: storage,
|
||
|
key: p.Key,
|
||
|
addr: p.Addr,
|
||
|
log: p.Logger,
|
||
|
}, nil
|
||
|
}
|