autopilot/interface+agent: remove Select
This commit is contained in:
parent
b3d315298c
commit
6130189d95
@ -66,12 +66,6 @@ type directiveArg struct {
|
||||
nodes map[NodeID]struct{}
|
||||
}
|
||||
|
||||
func (m *mockHeuristic) Select(self *btcec.PublicKey, graph ChannelGraph,
|
||||
amtToUse btcutil.Amount, numChans uint32,
|
||||
skipChans map[NodeID]struct{}) ([]AttachmentDirective, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (m *mockHeuristic) NodeScores(g ChannelGraph, chans []Channel,
|
||||
fundsAvailable btcutil.Amount, nodes map[NodeID]struct{}) (
|
||||
map[NodeID]*AttachmentDirective, error) {
|
||||
|
@ -121,17 +121,6 @@ type AttachmentHeuristic interface {
|
||||
// ideal state.
|
||||
NeedMoreChans(chans []Channel, balance btcutil.Amount) (btcutil.Amount, uint32, bool)
|
||||
|
||||
// Select is a method that given the current state of the channel
|
||||
// graph, a set of nodes to ignore, and an amount of available funds,
|
||||
// should return a set of attachment directives which describe which
|
||||
// additional channels should be opened within the graph to push the
|
||||
// heuristic back towards its equilibrium state. The numNewChans
|
||||
// argument represents the additional number of channels that should be
|
||||
// open.
|
||||
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
|
||||
|
@ -1,8 +1,6 @@
|
||||
package autopilot
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
prand "math/rand"
|
||||
"net"
|
||||
"time"
|
||||
@ -72,185 +70,6 @@ func NewNodeID(pub *btcec.PublicKey) NodeID {
|
||||
return n
|
||||
}
|
||||
|
||||
// shuffleCandidates shuffles the set of candidate nodes for preferential
|
||||
// attachment in order to break any ordering already enforced by the sorted
|
||||
// order of the public key for each node. To shuffle the set of candidates, we
|
||||
// use a version of the Fisher–Yates shuffle algorithm.
|
||||
func shuffleCandidates(candidates []Node) []Node {
|
||||
shuffledNodes := make([]Node, len(candidates))
|
||||
perm := prand.Perm(len(candidates))
|
||||
|
||||
for i, v := range perm {
|
||||
shuffledNodes[v] = candidates[i]
|
||||
}
|
||||
|
||||
return shuffledNodes
|
||||
}
|
||||
|
||||
// Select returns a candidate set of attachment directives that should be
|
||||
// executed based on the current internal state, the state of the channel
|
||||
// graph, the set of nodes we should exclude, and the amount of funds
|
||||
// available. 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.
|
||||
//
|
||||
// NOTE: This is a part of the AttachmentHeuristic interface.
|
||||
func (p *ConstrainedPrefAttachment) Select(self *btcec.PublicKey, g ChannelGraph,
|
||||
fundsAvailable btcutil.Amount, numNewChans uint32,
|
||||
skipNodes map[NodeID]struct{}) ([]AttachmentDirective, error) {
|
||||
|
||||
// TODO(roasbeef): rename?
|
||||
|
||||
var directives []AttachmentDirective
|
||||
|
||||
if fundsAvailable < p.constraints.MinChanSize {
|
||||
return directives, nil
|
||||
}
|
||||
|
||||
selfPubBytes := self.SerializeCompressed()
|
||||
|
||||
// We'll continue our attachment loop until we've exhausted the current
|
||||
// amount of available funds.
|
||||
visited := make(map[NodeID]struct{})
|
||||
for i := uint32(0); i < numNewChans; i++ {
|
||||
// selectionSlice will be used to randomly select a node
|
||||
// according to a power law distribution. For each connected
|
||||
// edge, we'll add an instance of the node to this slice. Thus,
|
||||
// for a given node, the probability that we'll attach to it
|
||||
// is: k_i / sum(k_j), where k_i is the degree of the target
|
||||
// node, and k_j is the degree of all other nodes i != j. This
|
||||
// implements the classic Barabási–Albert model for
|
||||
// preferential attachment.
|
||||
var selectionSlice []Node
|
||||
|
||||
// For each node, and each channel that the node has, we'll add
|
||||
// an instance of that node to the selection slice above.
|
||||
// This'll slice where the frequency of each node is equivalent
|
||||
// to the number of channels that connect to it.
|
||||
//
|
||||
// TODO(roasbeef): add noise to make adversarially resistant?
|
||||
if err := g.ForEachNode(func(node Node) error {
|
||||
nID := NodeID(node.PubKey())
|
||||
|
||||
// Once a node has already been attached to, we'll
|
||||
// ensure that it isn't factored into any further
|
||||
// decisions within this round.
|
||||
if _, ok := visited[nID]; ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
// If we come across ourselves, them we'll continue in
|
||||
// order to avoid attempting to make a channel with
|
||||
// ourselves.
|
||||
if bytes.Equal(nID[:], selfPubBytes) {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Additionally, if this node is in the blacklist, then
|
||||
// we'll skip it.
|
||||
if _, ok := skipNodes[nID]; ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
// For initial bootstrap purposes, if a node doesn't
|
||||
// have any channels, then we'll ensure that it has at
|
||||
// least one item in the selection slice.
|
||||
//
|
||||
// TODO(roasbeef): make conditional?
|
||||
selectionSlice = append(selectionSlice, node)
|
||||
|
||||
// For each active channel the node has, we'll add an
|
||||
// additional channel to the selection slice to
|
||||
// increase their weight.
|
||||
if err := node.ForEachChannel(func(channel ChannelEdge) error {
|
||||
selectionSlice = append(selectionSlice, node)
|
||||
return nil
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// If no nodes at all were accumulated, then we'll exit early
|
||||
// as there are no eligible candidates.
|
||||
if len(selectionSlice) == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
// Given our selection slice, we'll now generate a random index
|
||||
// into this slice. The node we select will be recommended by
|
||||
// us to create a channel to.
|
||||
candidates := shuffleCandidates(selectionSlice)
|
||||
selectedIndex := prand.Int31n(int32(len(candidates)))
|
||||
selectedNode := candidates[selectedIndex]
|
||||
|
||||
// TODO(roasbeef): cap on num channels to same participant?
|
||||
|
||||
// With the node selected, we'll add this (node, amount) tuple
|
||||
// to out set of recommended directives.
|
||||
pubBytes := selectedNode.PubKey()
|
||||
nID := NodeID(pubBytes)
|
||||
directives = append(directives, AttachmentDirective{
|
||||
NodeID: nID,
|
||||
Addrs: selectedNode.Addrs(),
|
||||
})
|
||||
|
||||
// With the node selected, we'll add it to the set of visited
|
||||
// nodes to avoid attaching to it again.
|
||||
visited[nID] = struct{}{}
|
||||
}
|
||||
|
||||
numSelectedNodes := int64(len(directives))
|
||||
switch {
|
||||
// If we have enough available funds to distribute the maximum channel
|
||||
// size for each of the selected peers to attach to, then we'll
|
||||
// allocate the maximum amount to each peer.
|
||||
case int64(fundsAvailable) >= numSelectedNodes*int64(p.constraints.MaxChanSize):
|
||||
for i := 0; i < int(numSelectedNodes); i++ {
|
||||
directives[i].ChanAmt = p.constraints.MaxChanSize
|
||||
}
|
||||
|
||||
return directives, nil
|
||||
|
||||
// Otherwise, we'll greedily allocate our funds to the channels
|
||||
// successively until we run out of available funds, or can't create a
|
||||
// channel above the min channel size.
|
||||
case int64(fundsAvailable) < numSelectedNodes*int64(p.constraints.MaxChanSize):
|
||||
i := 0
|
||||
for fundsAvailable > p.constraints.MinChanSize {
|
||||
// We'll attempt to allocate the max channel size
|
||||
// initially. If we don't have enough funds to do this,
|
||||
// then we'll allocate the remainder of the funds
|
||||
// available to the channel.
|
||||
delta := p.constraints.MaxChanSize
|
||||
if fundsAvailable-delta < 0 {
|
||||
delta = fundsAvailable
|
||||
}
|
||||
|
||||
directives[i].ChanAmt = delta
|
||||
|
||||
fundsAvailable -= delta
|
||||
i++
|
||||
}
|
||||
|
||||
// We'll slice the initial set of directives to properly
|
||||
// reflect the amount of funds we were able to allocate.
|
||||
return directives[:i:i], nil
|
||||
|
||||
default:
|
||||
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.
|
||||
|
Loading…
Reference in New Issue
Block a user