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

lightningnetwork / lnd / 13180337775

06 Feb 2025 01:54PM UTC coverage: 57.708% (+8.4%) from 49.279%
13180337775

Pull #9470

github

ziggie1984
walletrpc+lncli: add new param. to bumpfee rpc.

Add new parameter deadline-delta to the bumpfee request and only
allow it to be used when the budget value is used as well.
Pull Request #9470: Make sure we fail the bump fee request as long as no budget is specified

103606 of 179536 relevant lines covered (57.71%)

24866.63 hits per line

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

89.59
/graph/db/graph_cache.go
1
package graphdb
2

3
import (
4
        "fmt"
5
        "sync"
6

7
        "github.com/btcsuite/btcd/btcutil"
8
        "github.com/lightningnetwork/lnd/graph/db/models"
9
        "github.com/lightningnetwork/lnd/kvdb"
10
        "github.com/lightningnetwork/lnd/lnwire"
11
        "github.com/lightningnetwork/lnd/routing/route"
12
)
13

14
// GraphCacheNode is an interface for all the information the cache needs to know
15
// about a lightning node.
16
type GraphCacheNode interface {
17
        // PubKey is the node's public identity key.
18
        PubKey() route.Vertex
19

20
        // Features returns the node's p2p features.
21
        Features() *lnwire.FeatureVector
22

23
        // ForEachChannel iterates through all channels of a given node,
24
        // executing the passed callback with an edge info structure and the
25
        // policies of each end of the channel. The first edge policy is the
26
        // outgoing edge *to* the connecting node, while the second is the
27
        // incoming edge *from* the connecting node. If the callback returns an
28
        // error, then the iteration is halted with the error propagated back up
29
        // to the caller.
30
        ForEachChannel(kvdb.RTx,
31
                func(kvdb.RTx, *models.ChannelEdgeInfo,
32
                        *models.ChannelEdgePolicy,
33
                        *models.ChannelEdgePolicy) error) error
34
}
35

36
// DirectedChannel is a type that stores the channel information as seen from
37
// one side of the channel.
38
type DirectedChannel struct {
39
        // ChannelID is the unique identifier of this channel.
40
        ChannelID uint64
41

42
        // IsNode1 indicates if this is the node with the smaller public key.
43
        IsNode1 bool
44

45
        // OtherNode is the public key of the node on the other end of this
46
        // channel.
47
        OtherNode route.Vertex
48

49
        // Capacity is the announced capacity of this channel in satoshis.
50
        Capacity btcutil.Amount
51

52
        // OutPolicySet is a boolean that indicates whether the node has an
53
        // outgoing policy set. For pathfinding only the existence of the policy
54
        // is important to know, not the actual content.
55
        OutPolicySet bool
56

57
        // InPolicy is the incoming policy *from* the other node to this node.
58
        // In path finding, we're walking backward from the destination to the
59
        // source, so we're always interested in the edge that arrives to us
60
        // from the other node.
61
        InPolicy *models.CachedEdgePolicy
62

63
        // Inbound fees of this node.
64
        InboundFee lnwire.Fee
65
}
66

67
// DeepCopy creates a deep copy of the channel, including the incoming policy.
68
func (c *DirectedChannel) DeepCopy() *DirectedChannel {
1,336✔
69
        channelCopy := *c
1,336✔
70

1,336✔
71
        if channelCopy.InPolicy != nil {
2,646✔
72
                inPolicyCopy := *channelCopy.InPolicy
1,310✔
73
                channelCopy.InPolicy = &inPolicyCopy
1,310✔
74

1,310✔
75
                // The fields for the ToNode can be overwritten by the path
1,310✔
76
                // finding algorithm, which is why we need a deep copy in the
1,310✔
77
                // first place. So we always start out with nil values, just to
1,310✔
78
                // be sure they don't contain any old data.
1,310✔
79
                channelCopy.InPolicy.ToNodePubKey = nil
1,310✔
80
                channelCopy.InPolicy.ToNodeFeatures = nil
1,310✔
81
        }
1,310✔
82

83
        return &channelCopy
1,336✔
84
}
85

86
// GraphCache is a type that holds a minimal set of information of the public
87
// channel graph that can be used for pathfinding.
88
type GraphCache struct {
89
        nodeChannels map[route.Vertex]map[uint64]*DirectedChannel
90
        nodeFeatures map[route.Vertex]*lnwire.FeatureVector
91

92
        mtx sync.RWMutex
93
}
94

95
// NewGraphCache creates a new graphCache.
96
func NewGraphCache(preAllocNumNodes int) *GraphCache {
142✔
97
        return &GraphCache{
142✔
98
                nodeChannels: make(
142✔
99
                        map[route.Vertex]map[uint64]*DirectedChannel,
142✔
100
                        // A channel connects two nodes, so we can look it up
142✔
101
                        // from both sides, meaning we get double the number of
142✔
102
                        // entries.
142✔
103
                        preAllocNumNodes*2,
142✔
104
                ),
142✔
105
                nodeFeatures: make(
142✔
106
                        map[route.Vertex]*lnwire.FeatureVector,
142✔
107
                        preAllocNumNodes,
142✔
108
                ),
142✔
109
        }
142✔
110
}
142✔
111

112
// Stats returns statistics about the current cache size.
113
func (c *GraphCache) Stats() string {
379✔
114
        c.mtx.RLock()
379✔
115
        defer c.mtx.RUnlock()
379✔
116

379✔
117
        numChannels := 0
379✔
118
        for node := range c.nodeChannels {
766✔
119
                numChannels += len(c.nodeChannels[node])
387✔
120
        }
387✔
121
        return fmt.Sprintf("num_node_features=%d, num_nodes=%d, "+
379✔
122
                "num_channels=%d", len(c.nodeFeatures), len(c.nodeChannels),
379✔
123
                numChannels)
379✔
124
}
125

126
// AddNodeFeatures adds a graph node and its features to the cache.
127
func (c *GraphCache) AddNodeFeatures(node GraphCacheNode) {
714✔
128
        nodePubKey := node.PubKey()
714✔
129

714✔
130
        // Only hold the lock for a short time. The `ForEachChannel()` below is
714✔
131
        // possibly slow as it has to go to the backend, so we can unlock
714✔
132
        // between the calls. And the AddChannel() method will acquire its own
714✔
133
        // lock anyway.
714✔
134
        c.mtx.Lock()
714✔
135
        c.nodeFeatures[nodePubKey] = node.Features()
714✔
136
        c.mtx.Unlock()
714✔
137
}
714✔
138

139
// AddNode adds a graph node, including all the (directed) channels of that
140
// node.
141
func (c *GraphCache) AddNode(tx kvdb.RTx, node GraphCacheNode) error {
614✔
142
        c.AddNodeFeatures(node)
614✔
143

614✔
144
        return node.ForEachChannel(
614✔
145
                tx, func(tx kvdb.RTx, info *models.ChannelEdgeInfo,
614✔
146
                        outPolicy *models.ChannelEdgePolicy,
614✔
147
                        inPolicy *models.ChannelEdgePolicy) error {
625✔
148

11✔
149
                        c.AddChannel(info, outPolicy, inPolicy)
11✔
150

11✔
151
                        return nil
11✔
152
                },
11✔
153
        )
154
}
155

156
// AddChannel adds a non-directed channel, meaning that the order of policy 1
157
// and policy 2 does not matter, the directionality is extracted from the info
158
// and policy flags automatically. The policy will be set as the outgoing policy
159
// on one node and the incoming policy on the peer's side.
160
func (c *GraphCache) AddChannel(info *models.ChannelEdgeInfo,
161
        policy1 *models.ChannelEdgePolicy, policy2 *models.ChannelEdgePolicy) {
1,695✔
162

1,695✔
163
        if info == nil {
1,695✔
164
                return
×
165
        }
×
166

167
        if policy1 != nil && policy1.IsDisabled() &&
1,695✔
168
                policy2 != nil && policy2.IsDisabled() {
1,695✔
169

×
170
                return
×
171
        }
×
172

173
        // Create the edge entry for both nodes.
174
        c.mtx.Lock()
1,695✔
175
        c.updateOrAddEdge(info.NodeKey1Bytes, &DirectedChannel{
1,695✔
176
                ChannelID: info.ChannelID,
1,695✔
177
                IsNode1:   true,
1,695✔
178
                OtherNode: info.NodeKey2Bytes,
1,695✔
179
                Capacity:  info.Capacity,
1,695✔
180
        })
1,695✔
181
        c.updateOrAddEdge(info.NodeKey2Bytes, &DirectedChannel{
1,695✔
182
                ChannelID: info.ChannelID,
1,695✔
183
                IsNode1:   false,
1,695✔
184
                OtherNode: info.NodeKey1Bytes,
1,695✔
185
                Capacity:  info.Capacity,
1,695✔
186
        })
1,695✔
187
        c.mtx.Unlock()
1,695✔
188

1,695✔
189
        // The policy's node is always the to_node. So if policy 1 has to_node
1,695✔
190
        // of node 2 then we have the policy 1 as seen from node 1.
1,695✔
191
        if policy1 != nil {
2,096✔
192
                fromNode, toNode := info.NodeKey1Bytes, info.NodeKey2Bytes
401✔
193
                if policy1.ToNode != info.NodeKey2Bytes {
403✔
194
                        fromNode, toNode = toNode, fromNode
2✔
195
                }
2✔
196
                isEdge1 := policy1.ChannelFlags&lnwire.ChanUpdateDirection == 0
401✔
197
                c.UpdatePolicy(policy1, fromNode, toNode, isEdge1)
401✔
198
        }
199
        if policy2 != nil {
2,096✔
200
                fromNode, toNode := info.NodeKey2Bytes, info.NodeKey1Bytes
401✔
201
                if policy2.ToNode != info.NodeKey1Bytes {
403✔
202
                        fromNode, toNode = toNode, fromNode
2✔
203
                }
2✔
204
                isEdge1 := policy2.ChannelFlags&lnwire.ChanUpdateDirection == 0
401✔
205
                c.UpdatePolicy(policy2, fromNode, toNode, isEdge1)
401✔
206
        }
207
}
208

209
// updateOrAddEdge makes sure the edge information for a node is either updated
210
// if it already exists or is added to that node's list of channels.
211
func (c *GraphCache) updateOrAddEdge(node route.Vertex, edge *DirectedChannel) {
3,390✔
212
        if len(c.nodeChannels[node]) == 0 {
4,251✔
213
                c.nodeChannels[node] = make(map[uint64]*DirectedChannel)
861✔
214
        }
861✔
215

216
        c.nodeChannels[node][edge.ChannelID] = edge
3,390✔
217
}
218

219
// UpdatePolicy updates a single policy on both the from and to node. The order
220
// of the from and to node is not strictly important. But we assume that a
221
// channel edge was added beforehand so that the directed channel struct already
222
// exists in the cache.
223
func (c *GraphCache) UpdatePolicy(policy *models.ChannelEdgePolicy, fromNode,
224
        toNode route.Vertex, edge1 bool) {
3,075✔
225

3,075✔
226
        // Extract inbound fee if possible and available. If there is a decoding
3,075✔
227
        // error, ignore this policy.
3,075✔
228
        var inboundFee lnwire.Fee
3,075✔
229
        _, err := policy.ExtraOpaqueData.ExtractRecords(&inboundFee)
3,075✔
230
        if err != nil {
3,075✔
231
                log.Errorf("Failed to extract records from edge policy %v: %v",
×
232
                        policy.ChannelID, err)
×
233

×
234
                return
×
235
        }
×
236

237
        c.mtx.Lock()
3,075✔
238
        defer c.mtx.Unlock()
3,075✔
239

3,075✔
240
        updatePolicy := func(nodeKey route.Vertex) {
9,225✔
241
                if len(c.nodeChannels[nodeKey]) == 0 {
6,150✔
242
                        log.Warnf("Node=%v not found in graph cache", nodeKey)
×
243

×
244
                        return
×
245
                }
×
246

247
                channel, ok := c.nodeChannels[nodeKey][policy.ChannelID]
6,150✔
248
                if !ok {
6,150✔
249
                        log.Warnf("Channel=%v not found in graph cache",
×
250
                                policy.ChannelID)
×
251

×
252
                        return
×
253
                }
×
254

255
                // Edge 1 is defined as the policy for the direction of node1 to
256
                // node2.
257
                switch {
6,150✔
258
                // This is node 1, and it is edge 1, so this is the outgoing
259
                // policy for node 1.
260
                case channel.IsNode1 && edge1:
1,541✔
261
                        channel.OutPolicySet = true
1,541✔
262
                        channel.InboundFee = inboundFee
1,541✔
263

264
                // This is node 2, and it is edge 2, so this is the outgoing
265
                // policy for node 2.
266
                case !channel.IsNode1 && !edge1:
1,534✔
267
                        channel.OutPolicySet = true
1,534✔
268
                        channel.InboundFee = inboundFee
1,534✔
269

270
                // The other two cases left mean it's the inbound policy for the
271
                // node.
272
                default:
3,075✔
273
                        channel.InPolicy = models.NewCachedPolicy(policy)
3,075✔
274
                }
275
        }
276

277
        updatePolicy(fromNode)
3,075✔
278
        updatePolicy(toNode)
3,075✔
279
}
280

281
// RemoveNode completely removes a node and all its channels (including the
282
// peer's side).
283
func (c *GraphCache) RemoveNode(node route.Vertex) {
69✔
284
        c.mtx.Lock()
69✔
285
        defer c.mtx.Unlock()
69✔
286

69✔
287
        delete(c.nodeFeatures, node)
69✔
288

69✔
289
        // First remove all channels from the other nodes' lists.
69✔
290
        for _, channel := range c.nodeChannels[node] {
69✔
291
                c.removeChannelIfFound(channel.OtherNode, channel.ChannelID)
×
292
        }
×
293

294
        // Then remove our whole node completely.
295
        delete(c.nodeChannels, node)
69✔
296
}
297

298
// RemoveChannel removes a single channel between two nodes.
299
func (c *GraphCache) RemoveChannel(node1, node2 route.Vertex, chanID uint64) {
265✔
300
        c.mtx.Lock()
265✔
301
        defer c.mtx.Unlock()
265✔
302

265✔
303
        // Remove that one channel from both sides.
265✔
304
        c.removeChannelIfFound(node1, chanID)
265✔
305
        c.removeChannelIfFound(node2, chanID)
265✔
306
}
265✔
307

308
// removeChannelIfFound removes a single channel from one side.
309
func (c *GraphCache) removeChannelIfFound(node route.Vertex, chanID uint64) {
530✔
310
        if len(c.nodeChannels[node]) == 0 {
700✔
311
                return
170✔
312
        }
170✔
313

314
        delete(c.nodeChannels[node], chanID)
360✔
315
}
316

317
// UpdateChannel updates the channel edge information for a specific edge. We
318
// expect the edge to already exist and be known. If it does not yet exist, this
319
// call is a no-op.
320
func (c *GraphCache) UpdateChannel(info *models.ChannelEdgeInfo) {
1✔
321
        c.mtx.Lock()
1✔
322
        defer c.mtx.Unlock()
1✔
323

1✔
324
        if len(c.nodeChannels[info.NodeKey1Bytes]) == 0 ||
1✔
325
                len(c.nodeChannels[info.NodeKey2Bytes]) == 0 {
1✔
326

×
327
                return
×
328
        }
×
329

330
        channel, ok := c.nodeChannels[info.NodeKey1Bytes][info.ChannelID]
1✔
331
        if ok {
2✔
332
                // We only expect to be called when the channel is already
1✔
333
                // known.
1✔
334
                channel.Capacity = info.Capacity
1✔
335
                channel.OtherNode = info.NodeKey2Bytes
1✔
336
        }
1✔
337

338
        channel, ok = c.nodeChannels[info.NodeKey2Bytes][info.ChannelID]
1✔
339
        if ok {
2✔
340
                channel.Capacity = info.Capacity
1✔
341
                channel.OtherNode = info.NodeKey1Bytes
1✔
342
        }
1✔
343
}
344

345
// getChannels returns a copy of the passed node's channels or nil if there
346
// isn't any.
347
func (c *GraphCache) getChannels(node route.Vertex) []*DirectedChannel {
501✔
348
        c.mtx.RLock()
501✔
349
        defer c.mtx.RUnlock()
501✔
350

501✔
351
        channels, ok := c.nodeChannels[node]
501✔
352
        if !ok {
508✔
353
                return nil
7✔
354
        }
7✔
355

356
        features, ok := c.nodeFeatures[node]
494✔
357
        if !ok {
508✔
358
                // If the features were set to nil explicitly, that's fine here.
14✔
359
                // The router will overwrite the features of the destination
14✔
360
                // node with those found in the invoice if necessary. But if we
14✔
361
                // didn't yet get a node announcement we want to mimic the
14✔
362
                // behavior of the old DB based code that would always set an
14✔
363
                // empty feature vector instead of leaving it nil.
14✔
364
                features = lnwire.EmptyFeatureVector()
14✔
365
        }
14✔
366

367
        toNodeCallback := func() route.Vertex {
942✔
368
                return node
448✔
369
        }
448✔
370

371
        i := 0
494✔
372
        channelsCopy := make([]*DirectedChannel, len(channels))
494✔
373
        for _, channel := range channels {
1,826✔
374
                // We need to copy the channel and policy to avoid it being
1,332✔
375
                // updated in the cache if the path finding algorithm sets
1,332✔
376
                // fields on it (currently only the ToNodeFeatures of the
1,332✔
377
                // policy).
1,332✔
378
                channelCopy := channel.DeepCopy()
1,332✔
379
                if channelCopy.InPolicy != nil {
2,639✔
380
                        channelCopy.InPolicy.ToNodePubKey = toNodeCallback
1,307✔
381
                        channelCopy.InPolicy.ToNodeFeatures = features
1,307✔
382
                }
1,307✔
383

384
                channelsCopy[i] = channelCopy
1,332✔
385
                i++
1,332✔
386
        }
387

388
        return channelsCopy
494✔
389
}
390

391
// ForEachChannel invokes the given callback for each channel of the given node.
392
func (c *GraphCache) ForEachChannel(node route.Vertex,
393
        cb func(channel *DirectedChannel) error) error {
501✔
394

501✔
395
        // Obtain a copy of the node's channels. We need do this in order to
501✔
396
        // avoid deadlocks caused by interaction with the graph cache, channel
501✔
397
        // state and the graph database from multiple goroutines. This snapshot
501✔
398
        // is only used for path finding where being stale is acceptable since
501✔
399
        // the real world graph and our representation may always become
501✔
400
        // slightly out of sync for a short time and the actual channel state
501✔
401
        // is stored separately.
501✔
402
        channels := c.getChannels(node)
501✔
403
        for _, channel := range channels {
1,833✔
404
                if err := cb(channel); err != nil {
1,332✔
405
                        return err
×
406
                }
×
407
        }
408

409
        return nil
501✔
410
}
411

412
// ForEachNode iterates over the adjacency list of the graph, executing the
413
// call back for each node and the set of channels that emanate from the given
414
// node.
415
//
416
// NOTE: This method should be considered _read only_, the channels or nodes
417
// passed in MUST NOT be modified.
418
func (c *GraphCache) ForEachNode(cb func(node route.Vertex,
419
        channels map[uint64]*DirectedChannel) error) error {
2✔
420

2✔
421
        c.mtx.RLock()
2✔
422
        defer c.mtx.RUnlock()
2✔
423

2✔
424
        for node, channels := range c.nodeChannels {
6✔
425
                // We don't make a copy here since this is a read-only RPC
4✔
426
                // call. We also don't need the node features either for this
4✔
427
                // call.
4✔
428
                if err := cb(node, channels); err != nil {
4✔
429
                        return err
×
430
                }
×
431
        }
432

433
        return nil
2✔
434
}
435

436
// GetFeatures returns the features of the node with the given ID. If no
437
// features are known for the node, an empty feature vector is returned.
438
func (c *GraphCache) GetFeatures(node route.Vertex) *lnwire.FeatureVector {
459✔
439
        c.mtx.RLock()
459✔
440
        defer c.mtx.RUnlock()
459✔
441

459✔
442
        features, ok := c.nodeFeatures[node]
459✔
443
        if !ok || features == nil {
469✔
444
                // The router expects the features to never be nil, so we return
10✔
445
                // an empty feature set instead.
10✔
446
                return lnwire.EmptyFeatureVector()
10✔
447
        }
10✔
448

449
        return features
449✔
450
}
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