autopilot/prefattach+interface: add API NodeScores
This commit adds a new method NodeScores to the AttachementHeuristic interface. Its intended use is to score a set of nodes according to their preference as channel counterparties. The PrefAttach heuristic gets a NodeScores method that will score the ndoes according to their number of already existing channels, similar to what is done already in Select.
This commit is contained in:
parent
5ecc209c41
commit
5e8e54083f
@ -93,6 +93,13 @@ func (m *mockHeuristic) Select(self *btcec.PublicKey, graph ChannelGraph,
|
||||
}
|
||||
}
|
||||
|
||||
func (m *mockHeuristic) NodeScores(g ChannelGraph, chans []Channel,
|
||||
fundsAvailable btcutil.Amount, nodes map[NodeID]struct{}) (
|
||||
map[NodeID]*AttachmentDirective, error) {
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
var _ AttachmentHeuristic = (*mockHeuristic)(nil)
|
||||
|
||||
type openChanIntent struct {
|
||||
|
@ -98,6 +98,10 @@ type AttachmentDirective struct {
|
||||
// Addrs is a list of addresses that the target peer may be reachable
|
||||
// at.
|
||||
Addrs []net.Addr
|
||||
|
||||
// Score is the score given by the heuristic for opening a channel of
|
||||
// the given size to this node.
|
||||
Score float64
|
||||
}
|
||||
|
||||
// AttachmentHeuristic is one of the primary interfaces within this package.
|
||||
@ -127,6 +131,24 @@ type AttachmentHeuristic interface {
|
||||
Select(self *btcec.PublicKey, graph ChannelGraph,
|
||||
amtToUse btcutil.Amount, numNewChans uint32,
|
||||
skipNodes map[NodeID]struct{}) ([]AttachmentDirective, error)
|
||||
|
||||
// NodeScores is a method that given the current channel graph, current
|
||||
// set of local channels and funds available, scores the given nodes
|
||||
// according to the preference of opening a channel with them. The
|
||||
// returned channel candidates maps the NodeID to an attachemnt
|
||||
// directive containing a score and a channel size.
|
||||
//
|
||||
// The scores will be in the range [0, M], where 0 indicates no
|
||||
// improvement in connectivity if a channel is opened to this node,
|
||||
// while M is the maximum possible improvement in connectivity. The
|
||||
// size of M is up to the implementation of this interface, so scores
|
||||
// must be normalized if compared against other implementations.
|
||||
//
|
||||
// NOTE: A NodeID not found in the returned map is implicitly given a
|
||||
// score of 0.
|
||||
NodeScores(g ChannelGraph, chans []Channel,
|
||||
fundsAvailable btcutil.Amount, nodes map[NodeID]struct{}) (
|
||||
map[NodeID]*AttachmentDirective, error)
|
||||
}
|
||||
|
||||
// ChannelController is a simple interface that allows an auto-pilot agent to
|
||||
|
@ -4,6 +4,7 @@ import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
prand "math/rand"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/btcsuite/btcd/btcec"
|
||||
@ -249,3 +250,108 @@ func (p *ConstrainedPrefAttachment) Select(self *btcec.PublicKey, g ChannelGraph
|
||||
return nil, fmt.Errorf("err")
|
||||
}
|
||||
}
|
||||
|
||||
// NodeScores is a method that given the current channel graph, current set of
|
||||
// local channels and funds available, scores the given nodes according the the
|
||||
// preference of opening a channel with them.
|
||||
//
|
||||
// The heuristic employed by this method is one that attempts to promote a
|
||||
// scale-free network globally, via local attachment preferences for new nodes
|
||||
// joining the network with an amount of available funds to be allocated to
|
||||
// channels. Specifically, we consider the degree of each node (and the flow
|
||||
// in/out of the node available via its open channels) and utilize the
|
||||
// Barabási–Albert model to drive our recommended attachment heuristics. If
|
||||
// implemented globally for each new participant, this results in a channel
|
||||
// graph that is scale-free and follows a power law distribution with k=-3.
|
||||
//
|
||||
// The returned scores will be in the range [0.0, 1.0], where higher scores are
|
||||
// given to nodes already having high connectivity in the graph.
|
||||
//
|
||||
// NOTE: This is a part of the AttachmentHeuristic interface.
|
||||
func (p *ConstrainedPrefAttachment) NodeScores(g ChannelGraph, chans []Channel,
|
||||
fundsAvailable btcutil.Amount, nodes map[NodeID]struct{}) (
|
||||
map[NodeID]*AttachmentDirective, error) {
|
||||
|
||||
// Count the number of channels in the graph. We'll also count the
|
||||
// number of channels as we go for the nodes we are interested in, and
|
||||
// record their addresses found in the db.
|
||||
var graphChans int
|
||||
nodeChanNum := make(map[NodeID]int)
|
||||
addresses := make(map[NodeID][]net.Addr)
|
||||
if err := g.ForEachNode(func(n Node) error {
|
||||
var nodeChans int
|
||||
err := n.ForEachChannel(func(_ ChannelEdge) error {
|
||||
nodeChans++
|
||||
graphChans++
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// If this node is not among our nodes to score, we can return
|
||||
// early.
|
||||
nID := NodeID(n.PubKey())
|
||||
if _, ok := nodes[nID]; !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Otherwise we'll record the number of channels, and also
|
||||
// populate the address in our channel candidates map.
|
||||
nodeChanNum[nID] = nodeChans
|
||||
addresses[nID] = n.Addrs()
|
||||
|
||||
return nil
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// If there are no channels in the graph we cannot determine any
|
||||
// preferences, so we return, indicating all candidates get a score of
|
||||
// zero.
|
||||
if graphChans == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
existingPeers := make(map[NodeID]struct{})
|
||||
for _, c := range chans {
|
||||
existingPeers[c.Node] = struct{}{}
|
||||
}
|
||||
|
||||
// For each node in the set of nodes, count their fraction of channels
|
||||
// in the graph, and use that as the score.
|
||||
candidates := make(map[NodeID]*AttachmentDirective)
|
||||
for nID, nodeChans := range nodeChanNum {
|
||||
// As channel size we'll use the maximum channel size available.
|
||||
chanSize := p.constraints.MaxChanSize
|
||||
if fundsAvailable-chanSize < 0 {
|
||||
chanSize = fundsAvailable
|
||||
}
|
||||
|
||||
_, ok := existingPeers[nID]
|
||||
|
||||
switch {
|
||||
|
||||
// If the node is among or existing channel peers, we don't
|
||||
// need another channel.
|
||||
case ok:
|
||||
continue
|
||||
|
||||
// If the amount is too small, we don't want to attempt opening
|
||||
// another channel.
|
||||
case chanSize == 0 || chanSize < p.constraints.MinChanSize:
|
||||
continue
|
||||
}
|
||||
|
||||
// Otherwise we score the node according to its fraction of
|
||||
// channels in the graph.
|
||||
score := float64(nodeChans) / float64(graphChans)
|
||||
candidates[nID] = &AttachmentDirective{
|
||||
NodeID: nID,
|
||||
ChanAmt: chanSize,
|
||||
Score: score,
|
||||
}
|
||||
}
|
||||
|
||||
return candidates, nil
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user