Browse Source

autopilot/interface+choice: add NodeScore type

We create a new type NodeScore which is a tuple (NodeID, score). The
weightedChoice and chooseN algorithms are altered to expect this type.

This is done in order to simplify the types we are using, since we were
only using a subset of the fields in AttachmentDirective.
master
Johan T. Halseth 5 years ago
parent
commit
25de66d27b
No known key found for this signature in database
GPG Key ID: 15BAADA29DA20D26
  1. 39
      autopilot/agent.go
  2. 10
      autopilot/choice.go
  3. 14
      autopilot/choice_test.go
  4. 12
      autopilot/interface.go

39
autopilot/agent.go

@ -571,22 +571,43 @@ func (a *Agent) openChans(availableFunds btcutil.Amount, numChans uint32,
return fmt.Errorf("unable to calculate node scores : %v", err)
}
// Add addresses to the candidates.
for nID, c := range scores {
addrs := addresses[nID]
c.Addrs = addrs
}
log.Debugf("Got scores for %d nodes", len(scores))
// Now use the score to make a weighted choice which
// nodes to attempt to open channels to.
chanCandidates, err := chooseN(numChans, scores)
// Temporary convert to NodeScore.
nodeScores := make(map[NodeID]*NodeScore)
for k, v := range scores {
nodeScores[k] = &NodeScore{
NodeID: v.NodeID,
Score: v.Score,
}
}
// Now use the score to make a weighted choice which nodes to attempt
// to open channels to.
nodeScores, err = chooseN(numChans, nodeScores)
if err != nil {
return fmt.Errorf("Unable to make weighted choice: %v",
err)
}
chanCandidates := make(map[NodeID]*AttachmentDirective)
for nID := range nodeScores {
// Add addresses to the candidates.
addrs := addresses[nID]
// If the node has no known addresses, we cannot connect to it,
// so we'll skip it.
if len(addrs) == 0 {
continue
}
chanCandidates[nID] = &AttachmentDirective{
NodeID: nID,
ChanAmt: chanSize,
Addrs: addrs,
}
}
if len(chanCandidates) == 0 {
log.Infof("No eligible candidates to connect to")
return nil

10
autopilot/choice.go

@ -46,10 +46,10 @@ func weightedChoice(w []float64) (int, error) {
return 0, fmt.Errorf("unable to make choice")
}
// chooseN picks at random min[n, len(s)] nodes if from the
// AttachmentDirectives map, with a probability weighted by their score.
func chooseN(n uint32, s map[NodeID]*AttachmentDirective) (
map[NodeID]*AttachmentDirective, error) {
// chooseN picks at random min[n, len(s)] nodes if from the NodeScore map, with
// a probability weighted by their score.
func chooseN(n uint32, s map[NodeID]*NodeScore) (
map[NodeID]*NodeScore, error) {
// Keep track of the number of nodes not yet chosen, in addition to
// their scores and NodeIDs.
@ -65,7 +65,7 @@ func chooseN(n uint32, s map[NodeID]*AttachmentDirective) (
// Pick a weighted choice from the remaining nodes as long as there are
// nodes left, and we haven't already picked n.
chosen := make(map[NodeID]*AttachmentDirective)
chosen := make(map[NodeID]*NodeScore)
for len(chosen) < int(n) && rem > 0 {
choice, err := weightedChoice(scores)
if err == ErrNoPositive {

14
autopilot/choice_test.go

@ -173,7 +173,7 @@ func TestWeightedChoiceDistribution(t *testing.T) {
func TestChooseNEmptyMap(t *testing.T) {
t.Parallel()
nodes := map[NodeID]*AttachmentDirective{}
nodes := map[NodeID]*NodeScore{}
property := func(n uint32) bool {
res, err := chooseN(n, nodes)
if err != nil {
@ -191,12 +191,12 @@ func TestChooseNEmptyMap(t *testing.T) {
// candidateMapVarLen is a type we'll use to generate maps of various lengths
// up to 255 to be used during QuickTests.
type candidateMapVarLen map[NodeID]*AttachmentDirective
type candidateMapVarLen map[NodeID]*NodeScore
// Generate generates a value of type candidateMapVarLen to be used during
// QuickTests.
func (candidateMapVarLen) Generate(rand *rand.Rand, size int) reflect.Value {
nodes := make(map[NodeID]*AttachmentDirective)
nodes := make(map[NodeID]*NodeScore)
// To avoid creating huge maps, we restrict them to max uint8 len.
n := uint8(rand.Uint32())
@ -212,7 +212,7 @@ func (candidateMapVarLen) Generate(rand *rand.Rand, size int) reflect.Value {
var nID [33]byte
binary.BigEndian.PutUint32(nID[:], uint32(i))
nodes[nID] = &AttachmentDirective{
nodes[nID] = &NodeScore{
Score: s,
}
}
@ -226,7 +226,7 @@ func TestChooseNMinimum(t *testing.T) {
t.Parallel()
// Helper to count the number of positive scores in the given map.
numPositive := func(nodes map[NodeID]*AttachmentDirective) int {
numPositive := func(nodes map[NodeID]*NodeScore) int {
cnt := 0
for _, v := range nodes {
if v.Score > 0 {
@ -274,7 +274,7 @@ func TestChooseNSample(t *testing.T) {
const maxIterations = 100000
fifth := uint32(numNodes / 5)
nodes := make(map[NodeID]*AttachmentDirective)
nodes := make(map[NodeID]*NodeScore)
// we make 5 buckets of nodes: 0, 0.1, 0.2, 0.4 and 0.8 score. We want
// to check that zero scores never gets chosen, while a doubling the
@ -299,7 +299,7 @@ func TestChooseNSample(t *testing.T) {
var nID [33]byte
binary.BigEndian.PutUint32(nID[:], i)
nodes[nID] = &AttachmentDirective{
nodes[nID] = &NodeScore{
Score: s,
}
}

12
autopilot/interface.go

@ -81,6 +81,18 @@ type ChannelGraph interface {
ForEachNode(func(Node) error) error
}
// NodeScore is a tuple mapping a NodeID to a score indicating the preference
// of opening a channel with it.
type NodeScore struct {
// NodeID is the serialized compressed pubkey of the node that is being
// scored.
NodeID NodeID
// Score is the score given by the heuristic for opening a channel of
// the given size to this node.
Score float64
}
// AttachmentDirective describes a channel attachment proscribed by an
// AttachmentHeuristic. It details to which node a channel should be created
// to, and also the parameters which should be used in the channel creation.

Loading…
Cancel
Save