64e43e555e
the net package does not make it easy to know if DialContext failed because the context was done. it's important for some of our tests that canceled contexts are detected as such, so we accept the small race that's arguably correct (the context must be canceled asynchronously) to ensure we always return the context error if available. Change-Id: I058064d5c666e5353b74fb5bd300bf7abe537ff5
133 lines
3.4 KiB
Go
133 lines
3.4 KiB
Go
// Copyright (C) 2019 Storj Labs, Inc.
|
|
// See LICENSE for copying information.
|
|
|
|
package trust
|
|
|
|
import (
|
|
"context"
|
|
"sync"
|
|
|
|
"github.com/zeebo/errs"
|
|
monkit "gopkg.in/spacemonkeygo/monkit.v2"
|
|
|
|
"storj.io/storj/pkg/identity"
|
|
"storj.io/storj/pkg/rpc"
|
|
"storj.io/storj/pkg/signing"
|
|
"storj.io/storj/pkg/storj"
|
|
)
|
|
|
|
// Error is the default error class
|
|
var Error = errs.Class("trust")
|
|
|
|
var mon = monkit.Package()
|
|
|
|
// Pool implements different peer verifications.
|
|
//
|
|
// architecture: Service
|
|
type Pool struct {
|
|
mu sync.RWMutex
|
|
dialer rpc.Dialer
|
|
|
|
trustedSatellites map[storj.NodeID]*satelliteInfoCache
|
|
}
|
|
|
|
// satelliteInfoCache caches identity information about a satellite
|
|
type satelliteInfoCache struct {
|
|
mu sync.Mutex
|
|
url storj.NodeURL
|
|
identity *identity.PeerIdentity
|
|
}
|
|
|
|
// NewPool creates a new trust pool of the specified list of trusted satellites.
|
|
func NewPool(dialer rpc.Dialer, trustedSatellites storj.NodeURLs) (*Pool, error) {
|
|
// TODO: preload all satellite peer identities
|
|
|
|
// parse the comma separated list of approved satellite IDs into an array of storj.NodeIDs
|
|
trusted := make(map[storj.NodeID]*satelliteInfoCache)
|
|
|
|
for _, node := range trustedSatellites {
|
|
trusted[node.ID] = &satelliteInfoCache{url: node}
|
|
}
|
|
|
|
return &Pool{
|
|
dialer: dialer,
|
|
trustedSatellites: trusted,
|
|
}, nil
|
|
}
|
|
|
|
// VerifySatelliteID checks whether id corresponds to a trusted satellite.
|
|
func (pool *Pool) VerifySatelliteID(ctx context.Context, id storj.NodeID) (err error) {
|
|
defer mon.Task()(&ctx)(&err)
|
|
|
|
pool.mu.RLock()
|
|
defer pool.mu.RUnlock()
|
|
|
|
_, ok := pool.trustedSatellites[id]
|
|
if !ok {
|
|
return Error.New("satellite %q is untrusted", id)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// GetSignee gets the corresponding signee for verifying signatures.
|
|
// It ignores passed in ctx cancellation to avoid miscaching between concurrent requests.
|
|
func (pool *Pool) GetSignee(ctx context.Context, id storj.NodeID) (_ signing.Signee, err error) {
|
|
defer mon.Task()(&ctx)(&err)
|
|
|
|
// lookup peer identity with id
|
|
pool.mu.RLock()
|
|
info, ok := pool.trustedSatellites[id]
|
|
pool.mu.RUnlock()
|
|
|
|
if !ok {
|
|
return nil, Error.New("signee %q is untrusted", id)
|
|
}
|
|
|
|
info.mu.Lock()
|
|
defer info.mu.Unlock()
|
|
|
|
if info.identity == nil {
|
|
identity, err := pool.FetchPeerIdentity(ctx, info.url)
|
|
if err != nil {
|
|
return nil, Error.Wrap(err)
|
|
}
|
|
info.identity = identity
|
|
}
|
|
|
|
return signing.SigneeFromPeerIdentity(info.identity), nil
|
|
}
|
|
|
|
// FetchPeerIdentity dials the url and fetches the identity.
|
|
func (pool *Pool) FetchPeerIdentity(ctx context.Context, url storj.NodeURL) (_ *identity.PeerIdentity, err error) {
|
|
conn, err := pool.dialer.DialAddressID(ctx, url.Address, url.ID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer func() { err = errs.Combine(err, conn.Close()) }()
|
|
|
|
return conn.PeerIdentity()
|
|
}
|
|
|
|
// GetSatellites returns a slice containing all trusted satellites
|
|
func (pool *Pool) GetSatellites(ctx context.Context) (satellites []storj.NodeID) {
|
|
defer mon.Task()(&ctx)(nil)
|
|
for sat := range pool.trustedSatellites {
|
|
satellites = append(satellites, sat)
|
|
}
|
|
return satellites
|
|
}
|
|
|
|
// GetAddress returns the address of a satellite in the trusted list
|
|
func (pool *Pool) GetAddress(ctx context.Context, id storj.NodeID) (_ string, err error) {
|
|
defer mon.Task()(&ctx)(&err)
|
|
|
|
pool.mu.RLock()
|
|
defer pool.mu.RUnlock()
|
|
|
|
info, ok := pool.trustedSatellites[id]
|
|
if !ok {
|
|
return "", Error.New("ID %v not found in trusted list", id)
|
|
}
|
|
return info.url.Address, nil
|
|
}
|