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

lightningnetwork / lnd / 13566028875

27 Feb 2025 12:09PM UTC coverage: 49.396% (-9.4%) from 58.748%
13566028875

Pull #9555

github

ellemouton
graph/db: populate the graph cache in Start instead of during construction

In this commit, we move the graph cache population logic out of the
ChannelGraph constructor and into its Start method instead.
Pull Request #9555: graph: extract cache from CRUD [6]

34 of 54 new or added lines in 4 files covered. (62.96%)

27464 existing lines in 436 files now uncovered.

101095 of 204664 relevant lines covered (49.4%)

1.54 hits per line

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

4.35
/autopilot/graph.go
1
package autopilot
2

3
import (
4
        "encoding/hex"
5
        "net"
6
        "sort"
7

8
        "github.com/btcsuite/btcd/btcec/v2"
9
        "github.com/btcsuite/btcd/btcec/v2/ecdsa"
10
        "github.com/btcsuite/btcd/btcutil"
11
        graphdb "github.com/lightningnetwork/lnd/graph/db"
12
        "github.com/lightningnetwork/lnd/graph/db/models"
13
        "github.com/lightningnetwork/lnd/lnwire"
14
        "github.com/lightningnetwork/lnd/routing/route"
15
)
16

17
var (
18
        testRBytes, _ = hex.DecodeString("8ce2bc69281ce27da07e6683571319d18e949ddfa2965fb6caa1bf0314f882d7")
19
        testSBytes, _ = hex.DecodeString("299105481d63e0f4bc2a88121167221b6700d72a0ead154c03be696a292d24ae")
20
        testRScalar   = new(btcec.ModNScalar)
21
        testSScalar   = new(btcec.ModNScalar)
22
        _             = testRScalar.SetByteSlice(testRBytes)
23
        _             = testSScalar.SetByteSlice(testSBytes)
24
        testSig       = ecdsa.NewSignature(testRScalar, testSScalar)
25

26
        chanIDCounter uint64 // To be used atomically.
27
)
28

29
// databaseChannelGraph wraps a channeldb.ChannelGraph instance with the
30
// necessary API to properly implement the autopilot.ChannelGraph interface.
31
//
32
// TODO(roasbeef): move inmpl to main package?
33
type databaseChannelGraph struct {
34
        db GraphSource
35
}
36

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

41
// ChannelGraphFromDatabase returns an instance of the autopilot.ChannelGraph
42
// backed by a GraphSource.
43
func ChannelGraphFromDatabase(db GraphSource) ChannelGraph {
3✔
44
        return &databaseChannelGraph{
3✔
45
                db: db,
3✔
46
        }
3✔
47
}
3✔
48

49
// type dbNode is a wrapper struct around a database transaction an
50
// channeldb.LightningNode. The wrapper method implement the autopilot.Node
51
// interface.
52
type dbNode struct {
53
        tx graphdb.NodeRTx
54
}
55

56
// A compile time assertion to ensure dbNode meets the autopilot.Node
57
// interface.
58
var _ Node = (*dbNode)(nil)
59

60
// PubKey is the identity public key of the node. This will be used to attempt
61
// to target a node for channel opening by the main autopilot agent. The key
62
// will be returned in serialized compressed format.
63
//
64
// NOTE: Part of the autopilot.Node interface.
UNCOV
65
func (d *dbNode) PubKey() [33]byte {
×
UNCOV
66
        return d.tx.Node().PubKeyBytes
×
UNCOV
67
}
×
68

69
// Addrs returns a slice of publicly reachable public TCP addresses that the
70
// peer is known to be listening on.
71
//
72
// NOTE: Part of the autopilot.Node interface.
73
func (d *dbNode) Addrs() []net.Addr {
×
74
        return d.tx.Node().Addresses
×
75
}
×
76

77
// ForEachChannel is a higher-order function that will be used to iterate
78
// through all edges emanating from/to the target node. For each active
79
// channel, this function should be called with the populated ChannelEdge that
80
// describes the active channel.
81
//
82
// NOTE: Part of the autopilot.Node interface.
UNCOV
83
func (d *dbNode) ForEachChannel(cb func(ChannelEdge) error) error {
×
UNCOV
84
        return d.tx.ForEachChannel(func(ei *models.ChannelEdgeInfo, ep,
×
UNCOV
85
                _ *models.ChannelEdgePolicy) error {
×
UNCOV
86

×
UNCOV
87
                // Skip channels for which no outgoing edge policy is available.
×
UNCOV
88
                //
×
UNCOV
89
                // TODO(joostjager): Ideally the case where channels have a nil
×
UNCOV
90
                // policy should be supported, as autopilot is not looking at
×
UNCOV
91
                // the policies. For now, it is not easily possible to get a
×
UNCOV
92
                // reference to the other end LightningNode object without
×
UNCOV
93
                // retrieving the policy.
×
UNCOV
94
                if ep == nil {
×
95
                        return nil
×
96
                }
×
97

UNCOV
98
                node, err := d.tx.FetchNode(ep.ToNode)
×
UNCOV
99
                if err != nil {
×
100
                        return err
×
101
                }
×
102

UNCOV
103
                edge := ChannelEdge{
×
UNCOV
104
                        ChanID:   lnwire.NewShortChanIDFromInt(ep.ChannelID),
×
UNCOV
105
                        Capacity: ei.Capacity,
×
UNCOV
106
                        Peer: &dbNode{
×
UNCOV
107
                                tx: node,
×
UNCOV
108
                        },
×
UNCOV
109
                }
×
UNCOV
110

×
UNCOV
111
                return cb(edge)
×
112
        })
113
}
114

115
// ForEachNode is a higher-order function that should be called once for each
116
// connected node within the channel graph. If the passed callback returns an
117
// error, then execution should be terminated.
118
//
119
// NOTE: Part of the autopilot.ChannelGraph interface.
UNCOV
120
func (d *databaseChannelGraph) ForEachNode(cb func(Node) error) error {
×
UNCOV
121
        return d.db.ForEachNode(func(nodeTx graphdb.NodeRTx) error {
×
UNCOV
122
                // We'll skip over any node that doesn't have any advertised
×
UNCOV
123
                // addresses. As we won't be able to reach them to actually
×
UNCOV
124
                // open any channels.
×
UNCOV
125
                if len(nodeTx.Node().Addresses) == 0 {
×
126
                        return nil
×
127
                }
×
128

UNCOV
129
                node := &dbNode{
×
UNCOV
130
                        tx: nodeTx,
×
UNCOV
131
                }
×
UNCOV
132
                return cb(node)
×
133
        })
134
}
135

136
// databaseChannelGraphCached wraps a channeldb.ChannelGraph instance with the
137
// necessary API to properly implement the autopilot.ChannelGraph interface.
138
type databaseChannelGraphCached struct {
139
        db GraphSource
140
}
141

142
// A compile time assertion to ensure databaseChannelGraphCached meets the
143
// autopilot.ChannelGraph interface.
144
var _ ChannelGraph = (*databaseChannelGraphCached)(nil)
145

146
// ChannelGraphFromCachedDatabase returns an instance of the
147
// autopilot.ChannelGraph backed by a live, open channeldb instance.
148
func ChannelGraphFromCachedDatabase(db GraphSource) ChannelGraph {
×
149
        return &databaseChannelGraphCached{
×
150
                db: db,
×
151
        }
×
152
}
×
153

154
// dbNodeCached is a wrapper struct around a database transaction for a
155
// channeldb.LightningNode. The wrapper methods implement the autopilot.Node
156
// interface.
157
type dbNodeCached struct {
158
        node     route.Vertex
159
        channels map[uint64]*graphdb.DirectedChannel
160
}
161

162
// A compile time assertion to ensure dbNodeCached meets the autopilot.Node
163
// interface.
164
var _ Node = (*dbNodeCached)(nil)
165

166
// PubKey is the identity public key of the node.
167
//
168
// NOTE: Part of the autopilot.Node interface.
169
func (nc dbNodeCached) PubKey() [33]byte {
×
170
        return nc.node
×
171
}
×
172

173
// Addrs returns a slice of publicly reachable public TCP addresses that the
174
// peer is known to be listening on.
175
//
176
// NOTE: Part of the autopilot.Node interface.
177
func (nc dbNodeCached) Addrs() []net.Addr {
×
178
        // TODO: Add addresses to be usable by autopilot.
×
179
        return []net.Addr{}
×
180
}
×
181

182
// ForEachChannel is a higher-order function that will be used to iterate
183
// through all edges emanating from/to the target node. For each active
184
// channel, this function should be called with the populated ChannelEdge that
185
// describes the active channel.
186
//
187
// NOTE: Part of the autopilot.Node interface.
188
func (nc dbNodeCached) ForEachChannel(cb func(ChannelEdge) error) error {
×
189
        for cid, channel := range nc.channels {
×
190
                edge := ChannelEdge{
×
191
                        ChanID:   lnwire.NewShortChanIDFromInt(cid),
×
192
                        Capacity: channel.Capacity,
×
193
                        Peer: dbNodeCached{
×
194
                                node: channel.OtherNode,
×
195
                        },
×
196
                }
×
197

×
198
                if err := cb(edge); err != nil {
×
199
                        return err
×
200
                }
×
201
        }
202

203
        return nil
×
204
}
205

206
// ForEachNode is a higher-order function that should be called once for each
207
// connected node within the channel graph. If the passed callback returns an
208
// error, then execution should be terminated.
209
//
210
// NOTE: Part of the autopilot.ChannelGraph interface.
211
func (dc *databaseChannelGraphCached) ForEachNode(cb func(Node) error) error {
×
212
        return dc.db.ForEachNodeCached(func(n route.Vertex,
×
213
                channels map[uint64]*graphdb.DirectedChannel) error {
×
214

×
215
                if len(channels) > 0 {
×
216
                        node := dbNodeCached{
×
217
                                node:     n,
×
218
                                channels: channels,
×
219
                        }
×
220
                        return cb(node)
×
221
                }
×
222
                return nil
×
223
        })
224
}
225

226
// memNode is a purely in-memory implementation of the autopilot.Node
227
// interface.
228
type memNode struct {
229
        pub *btcec.PublicKey
230

231
        chans []ChannelEdge
232

233
        addrs []net.Addr
234
}
235

236
// A compile time assertion to ensure memNode meets the autopilot.Node
237
// interface.
238
var _ Node = (*memNode)(nil)
239

240
// PubKey is the identity public key of the node. This will be used to attempt
241
// to target a node for channel opening by the main autopilot agent.
242
//
243
// NOTE: Part of the autopilot.Node interface.
UNCOV
244
func (m memNode) PubKey() [33]byte {
×
UNCOV
245
        var n [33]byte
×
UNCOV
246
        copy(n[:], m.pub.SerializeCompressed())
×
UNCOV
247

×
UNCOV
248
        return n
×
UNCOV
249
}
×
250

251
// Addrs returns a slice of publicly reachable public TCP addresses that the
252
// peer is known to be listening on.
253
//
254
// NOTE: Part of the autopilot.Node interface.
UNCOV
255
func (m memNode) Addrs() []net.Addr {
×
UNCOV
256
        return m.addrs
×
UNCOV
257
}
×
258

259
// ForEachChannel is a higher-order function that will be used to iterate
260
// through all edges emanating from/to the target node. For each active
261
// channel, this function should be called with the populated ChannelEdge that
262
// describes the active channel.
263
//
264
// NOTE: Part of the autopilot.Node interface.
UNCOV
265
func (m memNode) ForEachChannel(cb func(ChannelEdge) error) error {
×
UNCOV
266
        for _, channel := range m.chans {
×
UNCOV
267
                if err := cb(channel); err != nil {
×
268
                        return err
×
269
                }
×
270
        }
271

UNCOV
272
        return nil
×
273
}
274

275
// Median returns the median value in the slice of Amounts.
UNCOV
276
func Median(vals []btcutil.Amount) btcutil.Amount {
×
UNCOV
277
        sort.Slice(vals, func(i, j int) bool {
×
UNCOV
278
                return vals[i] < vals[j]
×
UNCOV
279
        })
×
280

UNCOV
281
        num := len(vals)
×
UNCOV
282
        switch {
×
UNCOV
283
        case num == 0:
×
UNCOV
284
                return 0
×
285

UNCOV
286
        case num%2 == 0:
×
UNCOV
287
                return (vals[num/2-1] + vals[num/2]) / 2
×
288

UNCOV
289
        default:
×
UNCOV
290
                return vals[num/2]
×
291
        }
292
}
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