• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

lightningnetwork / lnd / 14193549836

01 Apr 2025 10:40AM UTC coverage: 69.046% (+0.007%) from 69.039%
14193549836

Pull #9665

github

web-flow
Merge e8825f209 into b01f4e514
Pull Request #9665: kvdb: bump etcd libs to v3.5.12

133439 of 193262 relevant lines covered (69.05%)

22119.45 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

83.96
/autopilot/prefattach.go
1
package autopilot
2

3
import (
4
        prand "math/rand"
5
        "time"
6

7
        "github.com/btcsuite/btcd/btcec/v2"
8
        "github.com/btcsuite/btcd/btcutil"
9
)
10

11
// minMedianChanSizeFraction determines the minimum size a channel must have to
12
// count positively when calculating the scores using preferential attachment.
13
// The minimum channel size is calculated as median/minMedianChanSizeFraction,
14
// where median is the median channel size of the entire graph.
15
const minMedianChanSizeFraction = 4
16

17
// PrefAttachment is an implementation of the AttachmentHeuristic interface
18
// that implement a non-linear preferential attachment heuristic. This means
19
// that given a threshold to allocate to automatic channel establishment, the
20
// heuristic will attempt to favor connecting to nodes which already have a set
21
// amount of links, selected by sampling from a power law distribution. The
22
// attachment is non-linear in that it favors nodes with a higher in-degree but
23
// less so than regular linear preferential attachment. As a result, this
24
// creates smaller and less clusters than regular linear preferential
25
// attachment.
26
//
27
// TODO(roasbeef): BA, with k=-3
28
type PrefAttachment struct {
29
}
30

31
// NewPrefAttachment creates a new instance of a PrefAttachment heuristic.
32
func NewPrefAttachment() *PrefAttachment {
21✔
33
        prand.Seed(time.Now().Unix())
21✔
34
        return &PrefAttachment{}
21✔
35
}
21✔
36

37
// A compile time assertion to ensure PrefAttachment meets the
38
// AttachmentHeuristic interface.
39
var _ AttachmentHeuristic = (*PrefAttachment)(nil)
40

41
// NodeID is a simple type that holds an EC public key serialized in compressed
42
// format.
43
type NodeID [33]byte
44

45
// NewNodeID creates a new nodeID from a passed public key.
46
func NewNodeID(pub *btcec.PublicKey) NodeID {
1,584✔
47
        var n NodeID
1,584✔
48
        copy(n[:], pub.SerializeCompressed())
1,584✔
49
        return n
1,584✔
50
}
1,584✔
51

52
// Name returns the name of this heuristic.
53
//
54
// NOTE: This is a part of the AttachmentHeuristic interface.
55
func (p *PrefAttachment) Name() string {
14✔
56
        return "preferential"
14✔
57
}
14✔
58

59
// NodeScores is a method that given the current channel graph and current set
60
// of local channels, scores the given nodes according to the preference of
61
// opening a channel of the given size with them. The returned channel
62
// candidates maps the NodeID to a NodeScore for the node.
63
//
64
// The heuristic employed by this method is one that attempts to promote a
65
// scale-free network globally, via local attachment preferences for new nodes
66
// joining the network with an amount of available funds to be allocated to
67
// channels. Specifically, we consider the degree of each node (and the flow
68
// in/out of the node available via its open channels) and utilize the
69
// Barabási–Albert model to drive our recommended attachment heuristics. If
70
// implemented globally for each new participant, this results in a channel
71
// graph that is scale-free and follows a power law distribution with k=-3.
72
//
73
// To avoid assigning a high score to nodes with a large number of small
74
// channels, we only count channels at least as large as a given fraction of
75
// the graph's median channel size.
76
//
77
// The returned scores will be in the range [0.0, 1.0], where higher scores are
78
// given to nodes already having high connectivity in the graph.
79
//
80
// NOTE: This is a part of the AttachmentHeuristic interface.
81
func (p *PrefAttachment) NodeScores(g ChannelGraph, chans []LocalChannel,
82
        chanSize btcutil.Amount, nodes map[NodeID]struct{}) (
83
        map[NodeID]*NodeScore, error) {
12✔
84

12✔
85
        // We first run though the graph once in order to find the median
12✔
86
        // channel size.
12✔
87
        var (
12✔
88
                allChans  []btcutil.Amount
12✔
89
                seenChans = make(map[uint64]struct{})
12✔
90
        )
12✔
91
        if err := g.ForEachNode(func(n Node) error {
38✔
92
                err := n.ForEachChannel(func(e ChannelEdge) error {
54✔
93
                        if _, ok := seenChans[e.ChanID.ToUint64()]; ok {
35✔
94
                                return nil
7✔
95
                        }
7✔
96
                        seenChans[e.ChanID.ToUint64()] = struct{}{}
21✔
97
                        allChans = append(allChans, e.Capacity)
21✔
98
                        return nil
21✔
99
                })
100
                if err != nil {
26✔
101
                        return err
×
102
                }
×
103

104
                return nil
26✔
105
        }); err != nil {
×
106
                return nil, err
×
107
        }
×
108

109
        medianChanSize := Median(allChans)
12✔
110
        log.Tracef("Found channel median %v for preferential score heuristic",
12✔
111
                medianChanSize)
12✔
112

12✔
113
        // Count the number of large-ish channels for each particular node in
12✔
114
        // the graph.
12✔
115
        var maxChans int
12✔
116
        nodeChanNum := make(map[NodeID]int)
12✔
117
        if err := g.ForEachNode(func(n Node) error {
38✔
118
                var nodeChans int
26✔
119
                err := n.ForEachChannel(func(e ChannelEdge) error {
54✔
120
                        // Since connecting to nodes with a lot of small
28✔
121
                        // channels actually worsens our connectivity in the
28✔
122
                        // graph (we will potentially waste time trying to use
28✔
123
                        // these useless channels in path finding), we decrease
28✔
124
                        // the counter for such channels.
28✔
125
                        if e.Capacity < medianChanSize/minMedianChanSizeFraction {
28✔
126
                                nodeChans--
×
127
                                return nil
×
128
                        }
×
129

130
                        // Larger channels we count.
131
                        nodeChans++
28✔
132
                        return nil
28✔
133
                })
134
                if err != nil {
26✔
135
                        return err
×
136
                }
×
137

138
                // We keep track of the highest-degree node we've seen, as this
139
                // will be given the max score.
140
                if nodeChans > maxChans {
39✔
141
                        maxChans = nodeChans
13✔
142
                }
13✔
143

144
                // If this node is not among our nodes to score, we can return
145
                // early.
146
                nID := NodeID(n.PubKey())
26✔
147
                if _, ok := nodes[nID]; !ok {
26✔
148
                        log.Tracef("Node %x not among nodes to score, "+
×
149
                                "ignoring", nID[:])
×
150
                        return nil
×
151
                }
×
152

153
                // Otherwise we'll record the number of channels.
154
                nodeChanNum[nID] = nodeChans
26✔
155
                log.Tracef("Counted %v channels for node %x", nodeChans, nID[:])
26✔
156

26✔
157
                return nil
26✔
158
        }); err != nil {
×
159
                return nil, err
×
160
        }
×
161

162
        // If there are no channels in the graph we cannot determine any
163
        // preferences, so we return, indicating all candidates get a score of
164
        // zero.
165
        if maxChans == 0 {
14✔
166
                log.Tracef("No channels in the graph")
2✔
167
                return nil, nil
2✔
168
        }
2✔
169

170
        existingPeers := make(map[NodeID]struct{})
10✔
171
        for _, c := range chans {
14✔
172
                existingPeers[c.Node] = struct{}{}
4✔
173
        }
4✔
174

175
        // For each node in the set of nodes, count their fraction of channels
176
        // in the graph, and use that as the score.
177
        candidates := make(map[NodeID]*NodeScore)
10✔
178
        for nID, nodeChans := range nodeChanNum {
36✔
179

26✔
180
                // If the node is among or existing channel peers, we don't
26✔
181
                // need another channel.
26✔
182
                if _, ok := existingPeers[nID]; ok {
30✔
183
                        log.Tracef("Node %x among existing peers for pref "+
4✔
184
                                "attach heuristic, giving zero score", nID[:])
4✔
185
                        continue
4✔
186
                }
187

188
                // If the node had no large channels, we skip it, since it
189
                // would have gotten a zero score anyway.
190
                if nodeChans <= 0 {
24✔
191
                        log.Tracef("Skipping node %x with channel count %v",
2✔
192
                                nID[:], nodeChans)
2✔
193
                        continue
2✔
194
                }
195

196
                // Otherwise we score the node according to its fraction of
197
                // channels in the graph, scaled such that the highest-degree
198
                // node will be given a score of 1.0.
199
                score := float64(nodeChans) / float64(maxChans)
20✔
200
                log.Tracef("Giving node %x a pref attach score of %v",
20✔
201
                        nID[:], score)
20✔
202

20✔
203
                candidates[nID] = &NodeScore{
20✔
204
                        NodeID: nID,
20✔
205
                        Score:  score,
20✔
206
                }
20✔
207
        }
208

209
        return candidates, nil
10✔
210
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc