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

lightningnetwork / lnd / 13035292482

29 Jan 2025 03:59PM UTC coverage: 49.3% (-9.5%) from 58.777%
13035292482

Pull #9456

github

mohamedawnallah
docs: update release-notes-0.19.0.md

In this commit, we warn users about the removal
of RPCs `SendToRoute`, `SendToRouteSync`, `SendPayment`,
and `SendPaymentSync` in the next release 0.20.
Pull Request #9456: lnrpc+docs: deprecate warning `SendToRoute`, `SendToRouteSync`, `SendPayment`, and `SendPaymentSync` in Release 0.19

100634 of 204126 relevant lines covered (49.3%)

1.54 hits per line

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

0.0
/autopilot/simple_graph.go
1
package autopilot
2

3
// diameterCutoff is used to discard nodes in the diameter calculation.
4
// It is the multiplier for the eccentricity of the highest-degree node,
5
// serving as a cutoff to discard all nodes with a smaller hop distance. This
6
// number should not be set close to 1 and is a tradeoff for computation cost,
7
// where 0 is maximally costly.
8
const diameterCutoff = 0.75
9

10
// SimpleGraph stores a simplified adj graph of a channel graph to speed
11
// up graph processing by eliminating all unnecessary hashing and map access.
12
type SimpleGraph struct {
13
        // Nodes is a map from node index to NodeID.
14
        Nodes []NodeID
15

16
        // Adj stores nodes and neighbors in an adjacency list.
17
        Adj [][]int
18
}
19

20
// NewSimpleGraph creates a simplified graph from the current channel graph.
21
// Returns an error if the channel graph iteration fails due to underlying
22
// failure.
23
func NewSimpleGraph(g ChannelGraph) (*SimpleGraph, error) {
×
24
        nodes := make(map[NodeID]int)
×
25
        adj := make(map[int][]int)
×
26
        nextIndex := 0
×
27

×
28
        // getNodeIndex returns the integer index of the passed node.
×
29
        // The returned index is then used to create a simplified adjacency list
×
30
        // where each node is identified by its index instead of its pubkey, and
×
31
        // also to create a mapping from node index to node pubkey.
×
32
        getNodeIndex := func(node Node) int {
×
33
                key := NodeID(node.PubKey())
×
34
                nodeIndex, ok := nodes[key]
×
35

×
36
                if !ok {
×
37
                        nodes[key] = nextIndex
×
38
                        nodeIndex = nextIndex
×
39
                        nextIndex++
×
40
                }
×
41

42
                return nodeIndex
×
43
        }
44

45
        // Iterate over each node and each channel and update the adj and the node
46
        // index.
47
        err := g.ForEachNode(func(node Node) error {
×
48
                u := getNodeIndex(node)
×
49

×
50
                return node.ForEachChannel(func(edge ChannelEdge) error {
×
51
                        v := getNodeIndex(edge.Peer)
×
52

×
53
                        adj[u] = append(adj[u], v)
×
54
                        return nil
×
55
                })
×
56
        })
57
        if err != nil {
×
58
                return nil, err
×
59
        }
×
60

61
        graph := &SimpleGraph{
×
62
                Nodes: make([]NodeID, len(nodes)),
×
63
                Adj:   make([][]int, len(nodes)),
×
64
        }
×
65

×
66
        // Fill the adj and the node index to node pubkey mapping.
×
67
        for nodeID, nodeIndex := range nodes {
×
68
                graph.Adj[nodeIndex] = adj[nodeIndex]
×
69
                graph.Nodes[nodeIndex] = nodeID
×
70
        }
×
71

72
        // We prepare to give some debug output about the size of the graph.
73
        totalChannels := 0
×
74
        for _, channels := range graph.Adj {
×
75
                totalChannels += len(channels)
×
76
        }
×
77

78
        // The number of channels is double counted, so divide by two.
79
        log.Debugf("Initialized simple graph with %d nodes and %d "+
×
80
                "channels", len(graph.Adj), totalChannels/2)
×
81
        return graph, nil
×
82
}
83

84
// maxVal is a helper function to get the maximal value of all values of a map.
85
func maxVal(mapping map[int]uint32) uint32 {
×
86
        maxValue := uint32(0)
×
87
        for _, value := range mapping {
×
88
                if maxValue < value {
×
89
                        maxValue = value
×
90
                }
×
91
        }
92
        return maxValue
×
93
}
94

95
// degree determines the number of edges for a node in the graph.
96
func (graph *SimpleGraph) degree(node int) int {
×
97
        return len(graph.Adj[node])
×
98
}
×
99

100
// nodeMaxDegree determines the node with the max degree and its degree.
101
func (graph *SimpleGraph) nodeMaxDegree() (int, int) {
×
102
        var maxNode, maxDegree int
×
103
        for node := range graph.Adj {
×
104
                degree := graph.degree(node)
×
105
                if degree > maxDegree {
×
106
                        maxNode = node
×
107
                        maxDegree = degree
×
108
                }
×
109
        }
110
        return maxNode, maxDegree
×
111
}
112

113
// shortestPathLengths performs a breadth-first-search from a node to all other
114
// nodes, returning the lengths of the paths.
115
func (graph *SimpleGraph) shortestPathLengths(node int) map[int]uint32 {
×
116
        // level indicates the shell of the search around the root node.
×
117
        var level uint32
×
118
        graphOrder := len(graph.Adj)
×
119

×
120
        // nextLevel tracks which nodes should be visited in the next round.
×
121
        nextLevel := make([]int, 0, graphOrder)
×
122

×
123
        // The root node is put as a starting point for the exploration.
×
124
        nextLevel = append(nextLevel, node)
×
125

×
126
        // Seen tracks already visited nodes and tracks how far away they are.
×
127
        seen := make(map[int]uint32, graphOrder)
×
128

×
129
        // Mark the root node as seen.
×
130
        seen[node] = level
×
131

×
132
        // thisLevel contains the nodes that are explored in the round.
×
133
        thisLevel := make([]int, 0, graphOrder)
×
134

×
135
        // Abort if we have an empty graph.
×
136
        if len(graph.Adj) == 0 {
×
137
                return seen
×
138
        }
×
139

140
        // We discover other nodes in a ring-like structure as long as we don't
141
        // have more nodes to explore.
142
        for len(nextLevel) > 0 {
×
143
                level++
×
144

×
145
                // We swap the queues for efficient memory management.
×
146
                thisLevel, nextLevel = nextLevel, thisLevel
×
147

×
148
                // Visit all neighboring nodes of the level and mark them as
×
149
                // seen if they were not discovered before.
×
150
                for _, thisNode := range thisLevel {
×
151
                        for _, neighbor := range graph.Adj[thisNode] {
×
152
                                _, ok := seen[neighbor]
×
153
                                if !ok {
×
154
                                        nextLevel = append(nextLevel, neighbor)
×
155
                                        seen[neighbor] = level
×
156
                                }
×
157

158
                                // If we have seen all nodes, we return early.
159
                                if len(seen) == graphOrder {
×
160
                                        return seen
×
161
                                }
×
162
                        }
163
                }
164

165
                // Empty the queue to be used in the next level.
166
                thisLevel = thisLevel[:0:cap(thisLevel)]
×
167
        }
168

169
        return seen
×
170
}
171

172
// nodeEccentricity calculates the eccentricity (longest shortest path to all
173
// other nodes) of a node.
174
func (graph *SimpleGraph) nodeEccentricity(node int) uint32 {
×
175
        pathLengths := graph.shortestPathLengths(node)
×
176
        return maxVal(pathLengths)
×
177
}
×
178

179
// nodeEccentricities calculates the eccentricities for the given nodes.
180
func (graph *SimpleGraph) nodeEccentricities(nodes []int) map[int]uint32 {
×
181
        eccentricities := make(map[int]uint32, len(graph.Adj))
×
182
        for _, node := range nodes {
×
183
                eccentricities[node] = graph.nodeEccentricity(node)
×
184
        }
×
185
        return eccentricities
×
186
}
187

188
// Diameter returns the maximal eccentricity (longest shortest path
189
// between any node pair) in the graph.
190
//
191
// Note: This method is exact but expensive, use DiameterRadialCutoff instead.
192
func (graph *SimpleGraph) Diameter() uint32 {
×
193
        nodes := make([]int, len(graph.Adj))
×
194
        for a := range nodes {
×
195
                nodes[a] = a
×
196
        }
×
197
        eccentricities := graph.nodeEccentricities(nodes)
×
198
        return maxVal(eccentricities)
×
199
}
200

201
// DiameterRadialCutoff is a method to efficiently evaluate the diameter of a
202
// graph. The highest-degree node is usually central in the graph. We can
203
// determine its eccentricity (shortest-longest path length to any other node)
204
// and use it as an approximation for the radius of the network. We then
205
// use this radius to compute a cutoff. All the nodes within a distance of the
206
// cutoff are discarded, as they represent the inside of the graph. We then
207
// loop over all outer nodes and determine their eccentricities, from which we
208
// get the diameter.
209
func (graph *SimpleGraph) DiameterRadialCutoff() uint32 {
×
210
        // Determine the reference node as the node with the highest degree.
×
211
        nodeMaxDegree, _ := graph.nodeMaxDegree()
×
212

×
213
        distances := graph.shortestPathLengths(nodeMaxDegree)
×
214
        eccentricityMaxDegreeNode := maxVal(distances)
×
215

×
216
        // We use the eccentricity to define a cutoff for the interior of the
×
217
        // graph from the reference node.
×
218
        cutoff := uint32(float32(eccentricityMaxDegreeNode) * diameterCutoff)
×
219
        log.Debugf("Cutoff radius is %d hops (max-degree node's "+
×
220
                "eccentricity is %d)", cutoff, eccentricityMaxDegreeNode)
×
221

×
222
        // Remove the nodes that are close to the reference node.
×
223
        var nodes []int
×
224
        for node, distance := range distances {
×
225
                if distance > cutoff {
×
226
                        nodes = append(nodes, node)
×
227
                }
×
228
        }
229
        log.Debugf("Evaluated nodes: %d, discarded nodes %d",
×
230
                len(nodes), len(graph.Adj)-len(nodes))
×
231

×
232
        // Compute the diameter of the remaining nodes.
×
233
        eccentricities := graph.nodeEccentricities(nodes)
×
234
        return maxVal(eccentricities)
×
235
}
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