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

lightningnetwork / lnd / 16200178152

10 Jul 2025 04:04PM UTC coverage: 57.65% (-9.8%) from 67.417%
16200178152

Pull #10065

github

web-flow
Merge 944c68c38 into 04a2be29d
Pull Request #10065: graph/db: async graph cache population

28 of 38 new or added lines in 3 files covered. (73.68%)

28436 existing lines in 456 files now uncovered.

98585 of 171006 relevant lines covered (57.65%)

1.79 hits per line

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

61.88
/graph/db/graph.go
1
package graphdb
2

3
import (
4
        "context"
5
        "errors"
6
        "fmt"
7
        "sync"
8
        "sync/atomic"
9
        "testing"
10
        "time"
11

12
        "github.com/btcsuite/btcd/chaincfg/chainhash"
13
        "github.com/btcsuite/btcd/wire"
14
        "github.com/lightningnetwork/lnd/batch"
15
        "github.com/lightningnetwork/lnd/graph/db/models"
16
        "github.com/lightningnetwork/lnd/lnwire"
17
        "github.com/lightningnetwork/lnd/routing/route"
18
        "github.com/stretchr/testify/require"
19
)
20

21
// ErrChanGraphShuttingDown indicates that the ChannelGraph has shutdown or is
22
// busy shutting down.
23
var ErrChanGraphShuttingDown = fmt.Errorf("ChannelGraph shutting down")
24

25
// ChannelGraph is a layer above the graph's CRUD layer.
26
//
27
// NOTE: currently, this is purely a pass-through layer directly to the backing
28
// KVStore. Upcoming commits will move the graph cache out of the KVStore and
29
// into this layer so that the KVStore is only responsible for CRUD operations.
30
type ChannelGraph struct {
31
        started atomic.Bool
32
        stopped atomic.Bool
33

34
        opts *chanGraphOptions
35

36
        // cacheLoaded is true if the initial graphCache population has
37
        // finished. We use this to ensure that when performing any reads,
38
        // we only read from the graphCache if it has been fully populated.
39
        cacheLoaded atomic.Bool
40
        graphCache  *GraphCache
41

42
        V1Store
43
        *topologyManager
44

45
        quit chan struct{}
46
        wg   sync.WaitGroup
47
}
48

49
// NewChannelGraph creates a new ChannelGraph instance with the given backend.
50
func NewChannelGraph(v1Store V1Store,
51
        options ...ChanGraphOption) (*ChannelGraph, error) {
3✔
52

3✔
53
        opts := defaultChanGraphOptions()
3✔
54
        for _, o := range options {
6✔
55
                o(opts)
3✔
56
        }
3✔
57

58
        g := &ChannelGraph{
3✔
59
                opts:            opts,
3✔
60
                V1Store:         v1Store,
3✔
61
                topologyManager: newTopologyManager(),
3✔
62
                quit:            make(chan struct{}),
3✔
63
        }
3✔
64

3✔
65
        // The graph cache can be turned off (e.g. for mobile users) for a
3✔
66
        // speed/memory usage tradeoff.
3✔
67
        if opts.useGraphCache {
6✔
68
                g.graphCache = NewGraphCache(opts.preAllocCacheNumNodes)
3✔
69
        }
3✔
70

71
        return g, nil
3✔
72
}
73

74
// Start kicks off any goroutines required for the ChannelGraph to function.
75
// If the graph cache is enabled, then it will be populated with the contents of
76
// the database.
77
func (c *ChannelGraph) Start() error {
3✔
78
        if !c.started.CompareAndSwap(false, true) {
3✔
UNCOV
79
                return nil
×
UNCOV
80
        }
×
81
        log.Debugf("ChannelGraph starting")
3✔
82
        defer log.Debug("ChannelGraph started")
3✔
83

3✔
84
        ctx := context.TODO()
3✔
85
        if c.opts.asyncGraphCachePopulation {
6✔
86
                c.wg.Add(1)
3✔
87
                go func() {
6✔
88
                        defer c.wg.Done()
3✔
89

3✔
90
                        if err := c.populateCache(ctx); err != nil {
3✔
NEW
91
                                log.Errorf("Could not populate the graph "+
×
NEW
92
                                        "cache: %v", err)
×
NEW
93
                        }
×
94
                }()
NEW
95
        } else {
×
NEW
96
                if err := c.populateCache(ctx); err != nil {
×
97
                        return fmt.Errorf("could not populate the graph "+
×
98
                                "cache: %w", err)
×
99
                }
×
100
        }
101

102
        c.wg.Add(1)
3✔
103
        go c.handleTopologySubscriptions()
3✔
104

3✔
105
        return nil
3✔
106
}
107

108
// Stop signals any active goroutines for a graceful closure.
109
func (c *ChannelGraph) Stop() error {
3✔
110
        if !c.stopped.CompareAndSwap(false, true) {
3✔
UNCOV
111
                return nil
×
UNCOV
112
        }
×
113

114
        log.Debugf("ChannelGraph shutting down...")
3✔
115
        defer log.Debug("ChannelGraph shutdown complete")
3✔
116

3✔
117
        close(c.quit)
3✔
118
        c.wg.Wait()
3✔
119

3✔
120
        return nil
3✔
121
}
122

123
// handleTopologySubscriptions ensures that topology client subscriptions,
124
// subscription cancellations and topology notifications are handled
125
// synchronously.
126
//
127
// NOTE: this MUST be run in a goroutine.
128
func (c *ChannelGraph) handleTopologySubscriptions() {
3✔
129
        defer c.wg.Done()
3✔
130

3✔
131
        for {
6✔
132
                select {
3✔
133
                // A new fully validated topology update has just arrived.
134
                // We'll notify any registered clients.
135
                case update := <-c.topologyUpdate:
3✔
136
                        // TODO(elle): change topology handling to be handled
3✔
137
                        // synchronously so that we can guarantee the order of
3✔
138
                        // notification delivery.
3✔
139
                        c.wg.Add(1)
3✔
140
                        go c.handleTopologyUpdate(update)
3✔
141

142
                        // TODO(roasbeef): remove all unconnected vertexes
143
                        // after N blocks pass with no corresponding
144
                        // announcements.
145

146
                // A new notification client update has arrived. We're either
147
                // gaining a new client, or cancelling notifications for an
148
                // existing client.
149
                case ntfnUpdate := <-c.ntfnClientUpdates:
3✔
150
                        clientID := ntfnUpdate.clientID
3✔
151

3✔
152
                        if ntfnUpdate.cancel {
6✔
153
                                client, ok := c.topologyClients.LoadAndDelete(
3✔
154
                                        clientID,
3✔
155
                                )
3✔
156
                                if ok {
6✔
157
                                        close(client.exit)
3✔
158
                                        client.wg.Wait()
3✔
159

3✔
160
                                        close(client.ntfnChan)
3✔
161
                                }
3✔
162

163
                                continue
3✔
164
                        }
165

166
                        c.topologyClients.Store(clientID, &topologyClient{
3✔
167
                                ntfnChan: ntfnUpdate.ntfnChan,
3✔
168
                                exit:     make(chan struct{}),
3✔
169
                        })
3✔
170

171
                case <-c.quit:
3✔
172
                        return
3✔
173
                }
174
        }
175
}
176

177
// populateCache loads the entire channel graph into the in-memory graph cache.
178
func (c *ChannelGraph) populateCache(ctx context.Context) error {
3✔
179
        if c.graphCache == nil {
6✔
180
                log.Info("In-memory channel graph cache disabled")
3✔
181

3✔
182
                return nil
3✔
183
        }
3✔
184

185
        startTime := time.Now()
3✔
186
        log.Info("Populating in-memory channel graph, this might take a " +
3✔
187
                "while...")
3✔
188

3✔
189
        err := c.V1Store.ForEachNodeCacheable(ctx, func(node route.Vertex,
3✔
190
                features *lnwire.FeatureVector) error {
6✔
191

3✔
192
                c.graphCache.AddNodeFeatures(node, features)
3✔
193

3✔
194
                return nil
3✔
195
        })
3✔
196
        if err != nil {
3✔
197
                return err
×
198
        }
×
199

200
        err = c.V1Store.ForEachChannelCacheable(
3✔
201
                func(info *models.CachedEdgeInfo,
3✔
202
                        policy1, policy2 *models.CachedEdgePolicy) error {
6✔
203

3✔
204
                        c.graphCache.AddChannel(info, policy1, policy2)
3✔
205

3✔
206
                        return nil
3✔
207
                })
3✔
208
        if err != nil {
3✔
209
                return err
×
210
        }
×
211

212
        c.cacheLoaded.Store(true)
3✔
213

3✔
214
        log.Infof("Finished populating in-memory channel graph (took %v, %s)",
3✔
215
                time.Since(startTime), c.graphCache.Stats())
3✔
216

3✔
217
        return nil
3✔
218
}
219

220
// ForEachNodeDirectedChannel iterates through all channels of a given node,
221
// executing the passed callback on the directed edge representing the channel
222
// and its incoming policy. If the callback returns an error, then the iteration
223
// is halted with the error propagated back up to the caller. If the graphCache
224
// is available, then it will be used to retrieve the node's channels instead
225
// of the database.
226
//
227
// Unknown policies are passed into the callback as nil values.
228
//
229
// NOTE: this is part of the graphdb.NodeTraverser interface.
230
func (c *ChannelGraph) ForEachNodeDirectedChannel(node route.Vertex,
231
        cb func(channel *DirectedChannel) error) error {
3✔
232

3✔
233
        if c.graphCache != nil && c.cacheLoaded.Load() {
6✔
234
                return c.graphCache.ForEachChannel(node, cb)
3✔
235
        }
3✔
236

237
        return c.V1Store.ForEachNodeDirectedChannel(node, cb)
3✔
238
}
239

240
// FetchNodeFeatures returns the features of the given node. If no features are
241
// known for the node, an empty feature vector is returned.
242
// If the graphCache is available, then it will be used to retrieve the node's
243
// features instead of the database.
244
//
245
// NOTE: this is part of the graphdb.NodeTraverser interface.
246
func (c *ChannelGraph) FetchNodeFeatures(node route.Vertex) (
247
        *lnwire.FeatureVector, error) {
3✔
248

3✔
249
        if c.graphCache != nil && c.cacheLoaded.Load() {
6✔
250
                return c.graphCache.GetFeatures(node), nil
3✔
251
        }
3✔
252

253
        return c.V1Store.FetchNodeFeatures(node)
3✔
254
}
255

256
// GraphSession will provide the call-back with access to a NodeTraverser
257
// instance which can be used to perform queries against the channel graph. If
258
// the graph cache is not enabled, then the call-back will be provided with
259
// access to the graph via a consistent read-only transaction.
260
func (c *ChannelGraph) GraphSession(cb func(graph NodeTraverser) error) error {
3✔
261
        if c.graphCache != nil && c.cacheLoaded.Load() {
6✔
262
                return cb(c)
3✔
263
        }
3✔
264

UNCOV
265
        return c.V1Store.GraphSession(cb)
×
266
}
267

268
// ForEachNodeCached iterates through all the stored vertices/nodes in the
269
// graph, executing the passed callback with each node encountered.
270
//
271
// NOTE: The callback contents MUST not be modified.
272
func (c *ChannelGraph) ForEachNodeCached(ctx context.Context,
273
        cb func(node route.Vertex,
UNCOV
274
                chans map[uint64]*DirectedChannel) error) error {
×
UNCOV
275

×
NEW
276
        if c.graphCache != nil && c.cacheLoaded.Load() {
×
277
                return c.graphCache.ForEachNode(cb)
×
278
        }
×
279

UNCOV
280
        return c.V1Store.ForEachNodeCached(ctx, cb)
×
281
}
282

283
// AddLightningNode adds a vertex/node to the graph database. If the node is not
284
// in the database from before, this will add a new, unconnected one to the
285
// graph. If it is present from before, this will update that node's
286
// information. Note that this method is expected to only be called to update an
287
// already present node from a node announcement, or to insert a node found in a
288
// channel update.
289
func (c *ChannelGraph) AddLightningNode(ctx context.Context,
290
        node *models.LightningNode, op ...batch.SchedulerOption) error {
3✔
291

3✔
292
        err := c.V1Store.AddLightningNode(ctx, node, op...)
3✔
293
        if err != nil {
3✔
294
                return err
×
295
        }
×
296

297
        if c.graphCache != nil {
6✔
298
                c.graphCache.AddNodeFeatures(
3✔
299
                        node.PubKeyBytes, node.Features,
3✔
300
                )
3✔
301
        }
3✔
302

303
        select {
3✔
304
        case c.topologyUpdate <- node:
3✔
305
        case <-c.quit:
×
306
                return ErrChanGraphShuttingDown
×
307
        }
308

309
        return nil
3✔
310
}
311

312
// DeleteLightningNode starts a new database transaction to remove a vertex/node
313
// from the database according to the node's public key.
314
func (c *ChannelGraph) DeleteLightningNode(ctx context.Context,
UNCOV
315
        nodePub route.Vertex) error {
×
UNCOV
316

×
UNCOV
317
        err := c.V1Store.DeleteLightningNode(ctx, nodePub)
×
UNCOV
318
        if err != nil {
×
UNCOV
319
                return err
×
UNCOV
320
        }
×
321

UNCOV
322
        if c.graphCache != nil {
×
UNCOV
323
                c.graphCache.RemoveNode(nodePub)
×
UNCOV
324
        }
×
325

UNCOV
326
        return nil
×
327
}
328

329
// AddChannelEdge adds a new (undirected, blank) edge to the graph database. An
330
// undirected edge from the two target nodes are created. The information stored
331
// denotes the static attributes of the channel, such as the channelID, the keys
332
// involved in creation of the channel, and the set of features that the channel
333
// supports. The chanPoint and chanID are used to uniquely identify the edge
334
// globally within the database.
335
func (c *ChannelGraph) AddChannelEdge(ctx context.Context,
336
        edge *models.ChannelEdgeInfo, op ...batch.SchedulerOption) error {
3✔
337

3✔
338
        err := c.V1Store.AddChannelEdge(ctx, edge, op...)
3✔
339
        if err != nil {
3✔
UNCOV
340
                return err
×
UNCOV
341
        }
×
342

343
        if c.graphCache != nil {
6✔
344
                c.graphCache.AddChannel(models.NewCachedEdge(edge), nil, nil)
3✔
345
        }
3✔
346

347
        select {
3✔
348
        case c.topologyUpdate <- edge:
3✔
349
        case <-c.quit:
×
350
                return ErrChanGraphShuttingDown
×
351
        }
352

353
        return nil
3✔
354
}
355

356
// MarkEdgeLive clears an edge from our zombie index, deeming it as live.
357
// If the cache is enabled, the edge will be added back to the graph cache if
358
// we still have a record of this channel in the DB.
UNCOV
359
func (c *ChannelGraph) MarkEdgeLive(chanID uint64) error {
×
UNCOV
360
        err := c.V1Store.MarkEdgeLive(chanID)
×
UNCOV
361
        if err != nil {
×
UNCOV
362
                return err
×
UNCOV
363
        }
×
364

UNCOV
365
        if c.graphCache != nil {
×
UNCOV
366
                // We need to add the channel back into our graph cache,
×
UNCOV
367
                // otherwise we won't use it for path finding.
×
UNCOV
368
                infos, err := c.V1Store.FetchChanInfos([]uint64{chanID})
×
UNCOV
369
                if err != nil {
×
370
                        return err
×
371
                }
×
372

UNCOV
373
                if len(infos) == 0 {
×
UNCOV
374
                        return nil
×
UNCOV
375
                }
×
376

377
                info := infos[0]
×
378

×
379
                var policy1, policy2 *models.CachedEdgePolicy
×
380
                if info.Policy1 != nil {
×
381
                        policy1 = models.NewCachedPolicy(info.Policy1)
×
382
                }
×
383
                if info.Policy2 != nil {
×
384
                        policy2 = models.NewCachedPolicy(info.Policy2)
×
385
                }
×
386

387
                c.graphCache.AddChannel(
×
388
                        models.NewCachedEdge(info.Info), policy1, policy2,
×
389
                )
×
390
        }
391

392
        return nil
×
393
}
394

395
// DeleteChannelEdges removes edges with the given channel IDs from the
396
// database and marks them as zombies. This ensures that we're unable to re-add
397
// it to our database once again. If an edge does not exist within the
398
// database, then ErrEdgeNotFound will be returned. If strictZombiePruning is
399
// true, then when we mark these edges as zombies, we'll set up the keys such
400
// that we require the node that failed to send the fresh update to be the one
401
// that resurrects the channel from its zombie state. The markZombie bool
402
// denotes whether to mark the channel as a zombie.
403
func (c *ChannelGraph) DeleteChannelEdges(strictZombiePruning, markZombie bool,
404
        chanIDs ...uint64) error {
3✔
405

3✔
406
        infos, err := c.V1Store.DeleteChannelEdges(
3✔
407
                strictZombiePruning, markZombie, chanIDs...,
3✔
408
        )
3✔
409
        if err != nil {
3✔
UNCOV
410
                return err
×
UNCOV
411
        }
×
412

413
        if c.graphCache != nil {
6✔
414
                for _, info := range infos {
6✔
415
                        c.graphCache.RemoveChannel(
3✔
416
                                info.NodeKey1Bytes, info.NodeKey2Bytes,
3✔
417
                                info.ChannelID,
3✔
418
                        )
3✔
419
                }
3✔
420
        }
421

422
        return err
3✔
423
}
424

425
// DisconnectBlockAtHeight is used to indicate that the block specified
426
// by the passed height has been disconnected from the main chain. This
427
// will "rewind" the graph back to the height below, deleting channels
428
// that are no longer confirmed from the graph. The prune log will be
429
// set to the last prune height valid for the remaining chain.
430
// Channels that were removed from the graph resulting from the
431
// disconnected block are returned.
432
func (c *ChannelGraph) DisconnectBlockAtHeight(height uint32) (
433
        []*models.ChannelEdgeInfo, error) {
2✔
434

2✔
435
        edges, err := c.V1Store.DisconnectBlockAtHeight(height)
2✔
436
        if err != nil {
2✔
437
                return nil, err
×
438
        }
×
439

440
        if c.graphCache != nil {
4✔
441
                for _, edge := range edges {
4✔
442
                        c.graphCache.RemoveChannel(
2✔
443
                                edge.NodeKey1Bytes, edge.NodeKey2Bytes,
2✔
444
                                edge.ChannelID,
2✔
445
                        )
2✔
446
                }
2✔
447
        }
448

449
        return edges, nil
2✔
450
}
451

452
// PruneGraph prunes newly closed channels from the channel graph in response
453
// to a new block being solved on the network. Any transactions which spend the
454
// funding output of any known channels within he graph will be deleted.
455
// Additionally, the "prune tip", or the last block which has been used to
456
// prune the graph is stored so callers can ensure the graph is fully in sync
457
// with the current UTXO state. A slice of channels that have been closed by
458
// the target block are returned if the function succeeds without error.
459
func (c *ChannelGraph) PruneGraph(spentOutputs []*wire.OutPoint,
460
        blockHash *chainhash.Hash, blockHeight uint32) (
461
        []*models.ChannelEdgeInfo, error) {
3✔
462

3✔
463
        edges, nodes, err := c.V1Store.PruneGraph(
3✔
464
                spentOutputs, blockHash, blockHeight,
3✔
465
        )
3✔
466
        if err != nil {
3✔
467
                return nil, err
×
468
        }
×
469

470
        if c.graphCache != nil {
6✔
471
                for _, edge := range edges {
6✔
472
                        c.graphCache.RemoveChannel(
3✔
473
                                edge.NodeKey1Bytes, edge.NodeKey2Bytes,
3✔
474
                                edge.ChannelID,
3✔
475
                        )
3✔
476
                }
3✔
477

478
                for _, node := range nodes {
6✔
479
                        c.graphCache.RemoveNode(node)
3✔
480
                }
3✔
481

482
                log.Debugf("Pruned graph, cache now has %s",
3✔
483
                        c.graphCache.Stats())
3✔
484
        }
485

486
        if len(edges) != 0 {
6✔
487
                // Notify all currently registered clients of the newly closed
3✔
488
                // channels.
3✔
489
                closeSummaries := createCloseSummaries(
3✔
490
                        blockHeight, edges...,
3✔
491
                )
3✔
492

3✔
493
                select {
3✔
494
                case c.topologyUpdate <- closeSummaries:
3✔
495
                case <-c.quit:
×
496
                        return nil, ErrChanGraphShuttingDown
×
497
                }
498
        }
499

500
        return edges, nil
3✔
501
}
502

503
// PruneGraphNodes is a garbage collection method which attempts to prune out
504
// any nodes from the channel graph that are currently unconnected. This ensure
505
// that we only maintain a graph of reachable nodes. In the event that a pruned
506
// node gains more channels, it will be re-added back to the graph.
507
func (c *ChannelGraph) PruneGraphNodes() error {
3✔
508
        nodes, err := c.V1Store.PruneGraphNodes()
3✔
509
        if err != nil {
3✔
510
                return err
×
511
        }
×
512

513
        if c.graphCache != nil {
6✔
514
                for _, node := range nodes {
3✔
UNCOV
515
                        c.graphCache.RemoveNode(node)
×
UNCOV
516
                }
×
517
        }
518

519
        return nil
3✔
520
}
521

522
// FilterKnownChanIDs takes a set of channel IDs and return the subset of chan
523
// ID's that we don't know and are not known zombies of the passed set. In other
524
// words, we perform a set difference of our set of chan ID's and the ones
525
// passed in. This method can be used by callers to determine the set of
526
// channels another peer knows of that we don't.
527
func (c *ChannelGraph) FilterKnownChanIDs(chansInfo []ChannelUpdateInfo,
528
        isZombieChan func(time.Time, time.Time) bool) ([]uint64, error) {
3✔
529

3✔
530
        unknown, knownZombies, err := c.V1Store.FilterKnownChanIDs(chansInfo)
3✔
531
        if err != nil {
3✔
532
                return nil, err
×
533
        }
×
534

535
        for _, info := range knownZombies {
3✔
UNCOV
536
                // TODO(ziggie): Make sure that for the strict pruning case we
×
UNCOV
537
                // compare the pubkeys and whether the right timestamp is not
×
UNCOV
538
                // older than the `ChannelPruneExpiry`.
×
UNCOV
539
                //
×
UNCOV
540
                // NOTE: The timestamp data has no verification attached to it
×
UNCOV
541
                // in the `ReplyChannelRange` msg so we are trusting this data
×
UNCOV
542
                // at this point. However it is not critical because we are just
×
UNCOV
543
                // removing the channel from the db when the timestamps are more
×
UNCOV
544
                // recent. During the querying of the gossip msg verification
×
UNCOV
545
                // happens as usual. However we should start punishing peers
×
UNCOV
546
                // when they don't provide us honest data ?
×
UNCOV
547
                isStillZombie := isZombieChan(
×
UNCOV
548
                        info.Node1UpdateTimestamp, info.Node2UpdateTimestamp,
×
UNCOV
549
                )
×
UNCOV
550

×
UNCOV
551
                if isStillZombie {
×
UNCOV
552
                        continue
×
553
                }
554

555
                // If we have marked it as a zombie but the latest update
556
                // timestamps could bring it back from the dead, then we mark it
557
                // alive, and we let it be added to the set of IDs to query our
558
                // peer for.
UNCOV
559
                err := c.V1Store.MarkEdgeLive(
×
UNCOV
560
                        info.ShortChannelID.ToUint64(),
×
UNCOV
561
                )
×
UNCOV
562
                // Since there is a chance that the edge could have been marked
×
UNCOV
563
                // as "live" between the FilterKnownChanIDs call and the
×
UNCOV
564
                // MarkEdgeLive call, we ignore the error if the edge is already
×
UNCOV
565
                // marked as live.
×
UNCOV
566
                if err != nil && !errors.Is(err, ErrZombieEdgeNotFound) {
×
567
                        return nil, err
×
568
                }
×
569
        }
570

571
        return unknown, nil
3✔
572
}
573

574
// MarkEdgeZombie attempts to mark a channel identified by its channel ID as a
575
// zombie. This method is used on an ad-hoc basis, when channels need to be
576
// marked as zombies outside the normal pruning cycle.
577
func (c *ChannelGraph) MarkEdgeZombie(chanID uint64,
UNCOV
578
        pubKey1, pubKey2 [33]byte) error {
×
UNCOV
579

×
UNCOV
580
        err := c.V1Store.MarkEdgeZombie(chanID, pubKey1, pubKey2)
×
UNCOV
581
        if err != nil {
×
582
                return err
×
583
        }
×
584

UNCOV
585
        if c.graphCache != nil {
×
UNCOV
586
                c.graphCache.RemoveChannel(pubKey1, pubKey2, chanID)
×
UNCOV
587
        }
×
588

UNCOV
589
        return nil
×
590
}
591

592
// UpdateEdgePolicy updates the edge routing policy for a single directed edge
593
// within the database for the referenced channel. The `flags` attribute within
594
// the ChannelEdgePolicy determines which of the directed edges are being
595
// updated. If the flag is 1, then the first node's information is being
596
// updated, otherwise it's the second node's information. The node ordering is
597
// determined by the lexicographical ordering of the identity public keys of the
598
// nodes on either side of the channel.
599
func (c *ChannelGraph) UpdateEdgePolicy(ctx context.Context,
600
        edge *models.ChannelEdgePolicy, op ...batch.SchedulerOption) error {
3✔
601

3✔
602
        from, to, err := c.V1Store.UpdateEdgePolicy(ctx, edge, op...)
3✔
603
        if err != nil {
3✔
UNCOV
604
                return err
×
UNCOV
605
        }
×
606

607
        if c.graphCache != nil {
6✔
608
                c.graphCache.UpdatePolicy(
3✔
609
                        models.NewCachedPolicy(edge), from, to,
3✔
610
                )
3✔
611
        }
3✔
612

613
        select {
3✔
614
        case c.topologyUpdate <- edge:
3✔
615
        case <-c.quit:
×
616
                return ErrChanGraphShuttingDown
×
617
        }
618

619
        return nil
3✔
620
}
621

622
// MakeTestGraph creates a new instance of the ChannelGraph for testing
623
// purposes. The backing V1Store implementation depends on the version of
624
// NewTestDB included in the current build.
625
//
626
// NOTE: this is currently unused, but is left here for future use to show how
627
// NewTestDB can be used. As the SQL implementation of the V1Store is
628
// implemented, unit tests will be switched to use this function instead of
629
// the existing MakeTestGraph helper. Once only this function is used, the
630
// existing MakeTestGraph function will be removed and this one will be renamed.
631
func MakeTestGraph(t testing.TB,
UNCOV
632
        opts ...ChanGraphOption) *ChannelGraph {
×
UNCOV
633

×
UNCOV
634
        t.Helper()
×
UNCOV
635

×
UNCOV
636
        store := NewTestDB(t)
×
UNCOV
637

×
UNCOV
638
        graph, err := NewChannelGraph(store, opts...)
×
UNCOV
639
        require.NoError(t, err)
×
UNCOV
640
        require.NoError(t, graph.Start())
×
UNCOV
641

×
UNCOV
642
        t.Cleanup(func() {
×
UNCOV
643
                require.NoError(t, graph.Stop())
×
UNCOV
644
        })
×
645

UNCOV
646
        return graph
×
647
}
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