2019-02-14 13:37:47 +03:00
|
|
|
package autopilot
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"sync"
|
|
|
|
|
|
|
|
"github.com/btcsuite/btcutil"
|
|
|
|
)
|
|
|
|
|
|
|
|
// ExternalScoreAttachment is an implementation of the AttachmentHeuristic
|
|
|
|
// interface that allows an external source to provide it with node scores.
|
|
|
|
type ExternalScoreAttachment struct {
|
|
|
|
// TODO(halseth): persist across restarts.
|
|
|
|
nodeScores map[NodeID]float64
|
|
|
|
|
|
|
|
sync.Mutex
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewExternalScoreAttachment creates a new instance of an
|
|
|
|
// ExternalScoreAttachment.
|
|
|
|
func NewExternalScoreAttachment() *ExternalScoreAttachment {
|
|
|
|
return &ExternalScoreAttachment{}
|
|
|
|
}
|
|
|
|
|
|
|
|
// A compile time assertion to ensure ExternalScoreAttachment meets the
|
2019-02-14 13:37:47 +03:00
|
|
|
// AttachmentHeuristic and ScoreSettable interfaces.
|
2019-02-14 13:37:47 +03:00
|
|
|
var _ AttachmentHeuristic = (*ExternalScoreAttachment)(nil)
|
2019-02-14 13:37:47 +03:00
|
|
|
var _ ScoreSettable = (*ExternalScoreAttachment)(nil)
|
2019-02-14 13:37:47 +03:00
|
|
|
|
|
|
|
// Name returns the name of this heuristic.
|
|
|
|
//
|
|
|
|
// NOTE: This is a part of the AttachmentHeuristic interface.
|
|
|
|
func (s *ExternalScoreAttachment) Name() string {
|
|
|
|
return "externalscore"
|
|
|
|
}
|
|
|
|
|
|
|
|
// SetNodeScores is used to set the internal map from NodeIDs to scores. The
|
|
|
|
// passed scores must be in the range [0, 1.0]. The fist parameter is the name
|
|
|
|
// of the targeted heuristic, to allow recursively target specific
|
|
|
|
// sub-heuristics. The returned boolean indicates whether the targeted
|
|
|
|
// heuristic was found.
|
2019-02-14 13:37:47 +03:00
|
|
|
//
|
|
|
|
// NOTE: This is a part of the ScoreSettable interface.
|
2019-02-14 13:37:47 +03:00
|
|
|
func (s *ExternalScoreAttachment) SetNodeScores(targetHeuristic string,
|
|
|
|
newScores map[NodeID]float64) (bool, error) {
|
|
|
|
|
|
|
|
// Return if this heuristic wasn't targeted.
|
|
|
|
if targetHeuristic != s.Name() {
|
|
|
|
return false, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Since there's a requirement that all score are in the range [0,
|
|
|
|
// 1.0], we validate them before setting the internal list.
|
|
|
|
for nID, s := range newScores {
|
|
|
|
if s < 0 || s > 1.0 {
|
|
|
|
return false, fmt.Errorf("invalid score %v for "+
|
|
|
|
"nodeID %v", s, nID)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
s.Lock()
|
|
|
|
defer s.Unlock()
|
|
|
|
|
|
|
|
s.nodeScores = newScores
|
2019-08-27 10:56:09 +03:00
|
|
|
log.Tracef("Setting %v external scores", len(s.nodeScores))
|
|
|
|
|
2019-02-14 13:37:47 +03:00
|
|
|
return true, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// NodeScores is a method that given the current channel graph and current set
|
|
|
|
// of local channels, scores the given nodes according to the preference of
|
|
|
|
// opening a channel of the given size with them. The returned channel
|
|
|
|
// candidates maps the NodeID to a NodeScore for the node.
|
|
|
|
//
|
|
|
|
// The returned scores will be in the range [0, 1.0], where 0 indicates no
|
|
|
|
// improvement in connectivity if a channel is opened to this node, while 1.0
|
|
|
|
// is the maximum possible improvement in connectivity.
|
|
|
|
//
|
|
|
|
// The scores are determined by checking the internal node scores list. Nodes
|
|
|
|
// not known will get a score of 0.
|
|
|
|
//
|
|
|
|
// NOTE: This is a part of the AttachmentHeuristic interface.
|
|
|
|
func (s *ExternalScoreAttachment) NodeScores(g ChannelGraph, chans []Channel,
|
|
|
|
chanSize btcutil.Amount, nodes map[NodeID]struct{}) (
|
|
|
|
map[NodeID]*NodeScore, error) {
|
|
|
|
|
|
|
|
existingPeers := make(map[NodeID]struct{})
|
|
|
|
for _, c := range chans {
|
|
|
|
existingPeers[c.Node] = struct{}{}
|
|
|
|
}
|
|
|
|
|
|
|
|
s.Lock()
|
|
|
|
defer s.Unlock()
|
|
|
|
|
2019-08-27 10:56:09 +03:00
|
|
|
log.Tracef("External scoring %v nodes, from %v set scores",
|
|
|
|
len(nodes), len(s.nodeScores))
|
|
|
|
|
2019-02-14 13:37:47 +03:00
|
|
|
// Fill the map of candidates to return.
|
|
|
|
candidates := make(map[NodeID]*NodeScore)
|
|
|
|
for nID := range nodes {
|
|
|
|
var score float64
|
|
|
|
if nodeScore, ok := s.nodeScores[nID]; ok {
|
2019-08-27 10:56:09 +03:00
|
|
|
score = nodeScore
|
2019-02-14 13:37:47 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// If the node is among or existing channel peers, we don't
|
|
|
|
// need another channel.
|
2019-08-27 10:56:09 +03:00
|
|
|
if _, ok := existingPeers[nID]; ok {
|
|
|
|
log.Tracef("Skipping existing peer %x from external "+
|
|
|
|
"score results", nID[:])
|
2019-02-14 13:37:47 +03:00
|
|
|
continue
|
2019-08-27 10:56:09 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
log.Tracef("External score %v given to node %x", score, nID[:])
|
2019-02-14 13:37:47 +03:00
|
|
|
|
|
|
|
// Instead of adding a node with score 0 to the returned set,
|
|
|
|
// we just skip it.
|
2019-08-27 10:56:09 +03:00
|
|
|
if score == 0 {
|
2019-02-14 13:37:47 +03:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
candidates[nID] = &NodeScore{
|
|
|
|
NodeID: nID,
|
|
|
|
Score: score,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return candidates, nil
|
|
|
|
}
|