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{}
|
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,
|
func (m *mockHeuristic) NodeScores(g ChannelGraph, chans []Channel,
|
||||||
fundsAvailable btcutil.Amount, nodes map[NodeID]struct{}) (
|
fundsAvailable btcutil.Amount, nodes map[NodeID]struct{}) (
|
||||||
map[NodeID]*AttachmentDirective, error) {
|
map[NodeID]*AttachmentDirective, error) {
|
||||||
|
@ -121,17 +121,6 @@ type AttachmentHeuristic interface {
|
|||||||
// ideal state.
|
// ideal state.
|
||||||
NeedMoreChans(chans []Channel, balance btcutil.Amount) (btcutil.Amount, uint32, bool)
|
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
|
// NodeScores is a method that given the current channel graph, current
|
||||||
// set of local channels and funds available, scores the given nodes
|
// set of local channels and funds available, scores the given nodes
|
||||||
// according to the preference of opening a channel with them. The
|
// according to the preference of opening a channel with them. The
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
package autopilot
|
package autopilot
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
prand "math/rand"
|
prand "math/rand"
|
||||||
"net"
|
"net"
|
||||||
"time"
|
"time"
|
||||||
@ -72,185 +70,6 @@ func NewNodeID(pub *btcec.PublicKey) NodeID {
|
|||||||
return n
|
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
|
// 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
|
// local channels and funds available, scores the given nodes according the the
|
||||||
// preference of opening a channel with them.
|
// preference of opening a channel with them.
|
||||||
|
Loading…
Reference in New Issue
Block a user