2018-04-18 17:55:28 +01:00
|
|
|
// Copyright (C) 2018 Storj Labs, Inc.
|
|
|
|
// See LICENSE for copying information.
|
|
|
|
|
2018-06-13 19:22:32 +01:00
|
|
|
package overlay
|
2018-04-18 16:34:15 +01:00
|
|
|
|
|
|
|
import (
|
2018-05-16 19:47:59 +01:00
|
|
|
"context"
|
2018-04-18 16:34:15 +01:00
|
|
|
|
|
|
|
"github.com/gogo/protobuf/proto"
|
2018-06-13 19:22:32 +01:00
|
|
|
"github.com/zeebo/errs"
|
2018-07-09 23:43:32 +01:00
|
|
|
"go.uber.org/zap"
|
2018-04-18 16:34:15 +01:00
|
|
|
|
2018-06-22 14:33:57 +01:00
|
|
|
"storj.io/storj/pkg/dht"
|
2018-05-16 19:47:59 +01:00
|
|
|
"storj.io/storj/pkg/kademlia"
|
2018-04-18 16:34:15 +01:00
|
|
|
"storj.io/storj/protos/overlay"
|
2018-06-13 19:22:32 +01:00
|
|
|
"storj.io/storj/storage"
|
|
|
|
"storj.io/storj/storage/boltdb"
|
|
|
|
"storj.io/storj/storage/redis"
|
2018-04-18 16:34:15 +01:00
|
|
|
)
|
|
|
|
|
2018-07-09 23:43:32 +01:00
|
|
|
// ErrNodeNotFound error standardization
|
|
|
|
var ErrNodeNotFound = errs.New("Node not found")
|
|
|
|
|
|
|
|
// OverlayError creates class of errors for stack traces
|
|
|
|
var OverlayError = errs.Class("Overlay Error")
|
2018-06-05 22:06:37 +01:00
|
|
|
|
2018-06-13 19:22:32 +01:00
|
|
|
// Cache is used to store overlay data in Redis
|
|
|
|
type Cache struct {
|
|
|
|
DB storage.KeyValueStore
|
2018-06-22 14:33:57 +01:00
|
|
|
DHT dht.DHT
|
2018-04-18 16:34:15 +01:00
|
|
|
}
|
|
|
|
|
2018-06-13 19:22:32 +01:00
|
|
|
// NewRedisOverlayCache returns a pointer to a new Cache instance with an initalized connection to Redis.
|
2018-06-22 14:33:57 +01:00
|
|
|
func NewRedisOverlayCache(address, password string, db int, DHT dht.DHT) (*Cache, error) {
|
2018-06-13 19:22:32 +01:00
|
|
|
rc, err := redis.NewClient(address, password, db)
|
2018-04-18 16:34:15 +01:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2018-06-13 19:22:32 +01:00
|
|
|
return &Cache{
|
2018-06-05 22:06:37 +01:00
|
|
|
DB: rc,
|
|
|
|
DHT: DHT,
|
2018-04-18 17:55:28 +01:00
|
|
|
}, nil
|
2018-04-18 16:34:15 +01:00
|
|
|
}
|
|
|
|
|
2018-06-13 19:22:32 +01:00
|
|
|
// NewBoltOverlayCache returns a pointer to a new Cache instance with an initalized connection to a Bolt db.
|
2018-06-22 14:33:57 +01:00
|
|
|
func NewBoltOverlayCache(dbPath string, DHT dht.DHT) (*Cache, error) {
|
2018-06-13 19:22:32 +01:00
|
|
|
bc, err := boltdb.NewClient(nil, dbPath, boltdb.OverlayBucket)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return &Cache{
|
|
|
|
DB: bc,
|
|
|
|
DHT: DHT,
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
2018-04-18 16:34:15 +01:00
|
|
|
// Get looks up the provided nodeID from the redis cache
|
2018-06-13 19:22:32 +01:00
|
|
|
func (o *Cache) Get(ctx context.Context, key string) (*overlay.NodeAddress, error) {
|
|
|
|
b, err := o.DB.Get([]byte(key))
|
2018-04-18 16:34:15 +01:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2018-06-29 21:06:25 +01:00
|
|
|
if b.IsZero() {
|
|
|
|
// TODO: log? return an error?
|
|
|
|
return nil, nil
|
|
|
|
}
|
2018-04-18 16:34:15 +01:00
|
|
|
|
|
|
|
na := &overlay.NodeAddress{}
|
2018-06-05 22:06:37 +01:00
|
|
|
if err := proto.Unmarshal(b, na); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2018-04-18 16:34:15 +01:00
|
|
|
|
2018-06-05 22:06:37 +01:00
|
|
|
return na, nil
|
2018-04-18 16:34:15 +01:00
|
|
|
}
|
|
|
|
|
2018-06-13 19:22:32 +01:00
|
|
|
// Put adds a nodeID to the redis cache with a binary representation of proto defined NodeAddress
|
|
|
|
func (o *Cache) Put(nodeID string, value overlay.NodeAddress) error {
|
2018-04-18 16:34:15 +01:00
|
|
|
data, err := proto.Marshal(&value)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2018-06-13 19:22:32 +01:00
|
|
|
return o.DB.Put([]byte(nodeID), []byte(data))
|
2018-04-18 16:34:15 +01:00
|
|
|
}
|
2018-05-16 19:47:59 +01:00
|
|
|
|
|
|
|
// Bootstrap walks the initialized network and populates the cache
|
2018-06-13 19:22:32 +01:00
|
|
|
func (o *Cache) Bootstrap(ctx context.Context) error {
|
2018-06-05 22:06:37 +01:00
|
|
|
nodes, err := o.DHT.GetNodes(ctx, "0", 1280)
|
|
|
|
|
2018-07-09 23:43:32 +01:00
|
|
|
if err != nil {
|
|
|
|
zap.Error(OverlayError.New("Error getting nodes from DHT", err))
|
|
|
|
}
|
|
|
|
|
2018-06-05 22:06:37 +01:00
|
|
|
for _, v := range nodes {
|
2018-06-22 14:33:57 +01:00
|
|
|
found, err := o.DHT.FindNode(ctx, kademlia.StringToNodeID(v.Id))
|
2018-06-05 22:06:37 +01:00
|
|
|
if err != nil {
|
2018-07-09 23:43:32 +01:00
|
|
|
zap.Error(ErrNodeNotFound)
|
2018-06-05 22:06:37 +01:00
|
|
|
}
|
|
|
|
addr, err := proto.Marshal(found.Address)
|
2018-07-16 20:22:34 +01:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := o.DB.Put([]byte(found.Id), addr); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2018-06-05 22:06:37 +01:00
|
|
|
}
|
|
|
|
// called after kademlia is bootstrapped
|
|
|
|
// needs to take RoutingTable and start to persist it into the cache
|
|
|
|
// take bootstrap node
|
|
|
|
// get their route table
|
|
|
|
// loop through nodes in RT and get THEIR route table
|
|
|
|
// keep going forever and ever
|
|
|
|
|
|
|
|
// Other Possibilities: Randomly generate node ID's to ask for?
|
|
|
|
|
|
|
|
_, err = o.DHT.GetRoutingTable(ctx)
|
|
|
|
|
2018-07-16 20:22:34 +01:00
|
|
|
return err
|
2018-05-16 19:47:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Refresh walks the network looking for new nodes and pings existing nodes to eliminate stale addresses
|
2018-06-13 19:22:32 +01:00
|
|
|
func (o *Cache) Refresh(ctx context.Context) error {
|
2018-06-05 22:06:37 +01:00
|
|
|
// iterate over all nodes
|
|
|
|
// compare responses to find new nodes
|
|
|
|
// listen for responses from existing nodes
|
|
|
|
// if no response from existing, then mark it as offline for time period
|
|
|
|
// if responds, it refreshes in DHT
|
|
|
|
_, rtErr := o.DHT.GetRoutingTable(ctx)
|
|
|
|
|
|
|
|
if rtErr != nil {
|
|
|
|
return rtErr
|
|
|
|
}
|
|
|
|
|
|
|
|
_, err := o.DHT.GetNodes(ctx, "0", 128)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Walk iterates over buckets to traverse the network
|
2018-06-13 19:22:32 +01:00
|
|
|
func (o *Cache) Walk(ctx context.Context) error {
|
2018-06-05 22:06:37 +01:00
|
|
|
nodes, err := o.DHT.GetNodes(ctx, "0", 128)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, v := range nodes {
|
2018-07-09 23:43:32 +01:00
|
|
|
_, err := o.DHT.FindNode(ctx, kademlia.StringToNodeID(v.Id))
|
|
|
|
if err != nil {
|
|
|
|
zap.Error(ErrNodeNotFound)
|
|
|
|
return err
|
2018-06-05 22:06:37 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
2018-05-16 19:47:59 +01:00
|
|
|
}
|