From 9d8e67d81ebcb8be9f31c101649ab7f380f2a8f7 Mon Sep 17 00:00:00 2001 From: "Johan T. Halseth" Date: Wed, 27 Mar 2019 15:04:15 +0100 Subject: [PATCH] autopilot/prefattach: count small channels negatively Decrease scores of nodes having a large number of small channels. --- autopilot/prefattach.go | 58 +++++++++++++++++++++++++++++++++++++---- 1 file changed, 53 insertions(+), 5 deletions(-) diff --git a/autopilot/prefattach.go b/autopilot/prefattach.go index de27fc56..5d94e14d 100644 --- a/autopilot/prefattach.go +++ b/autopilot/prefattach.go @@ -8,6 +8,12 @@ import ( "github.com/btcsuite/btcutil" ) +// minMedianChanSizeFraction determines the minimum size a channel must have to +// count positively when calculating the scores using preferential attachment. +// The minimum channel size is calculated as median/minMedianChanSizeFraction, +// where median is the median channel size of the entire graph. +const minMedianChanSizeFraction = 4 + // PrefAttachment is an implementation of the AttachmentHeuristic interface // that implement a non-linear preferential attachment heuristic. This means // that given a threshold to allocate to automatic channel establishment, the @@ -64,6 +70,10 @@ func (p *PrefAttachment) Name() string { // 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. // +// To avoid assigning a high score to nodes with a large number of small +// channels, we only count channels at least as large as a given fraction of +// the graph's median channel size. +// // 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. // @@ -72,12 +82,50 @@ func (p *PrefAttachment) NodeScores(g ChannelGraph, chans []Channel, chanSize btcutil.Amount, nodes map[NodeID]struct{}) ( map[NodeID]*NodeScore, error) { - // Count the number of channels for each particular node in the graph. + // We first run though the graph once in order to find the median + // channel size. + var ( + allChans []btcutil.Amount + seenChans = make(map[uint64]struct{}) + ) + if err := g.ForEachNode(func(n Node) error { + err := n.ForEachChannel(func(e ChannelEdge) error { + if _, ok := seenChans[e.ChanID.ToUint64()]; ok { + return nil + } + seenChans[e.ChanID.ToUint64()] = struct{}{} + allChans = append(allChans, e.Capacity) + return nil + }) + if err != nil { + return err + } + + return nil + }); err != nil { + return nil, err + } + + medianChanSize := Median(allChans) + + // Count the number of large-ish channels for each particular node in + // the graph. var maxChans int nodeChanNum := make(map[NodeID]int) if err := g.ForEachNode(func(n Node) error { var nodeChans int - err := n.ForEachChannel(func(_ ChannelEdge) error { + err := n.ForEachChannel(func(e ChannelEdge) error { + // Since connecting to nodes with a lot of small + // channels actually worsens our connectivity in the + // graph (we will potentially waste time trying to use + // these useless channels in path finding), we decrease + // the counter for such channels. + if e.Capacity < medianChanSize/minMedianChanSizeFraction { + nodeChans-- + return nil + } + + // Larger channels we count. nodeChans++ return nil }) @@ -132,9 +180,9 @@ func (p *PrefAttachment) NodeScores(g ChannelGraph, chans []Channel, case ok: continue - // If the node had no channels, we skip it, since it would have - // gotten a zero score anyway. - case nodeChans == 0: + // If the node had no large channels, we skip it, since it + // would have gotten a zero score anyway. + case nodeChans <= 0: continue }