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

lightningnetwork / lnd / 18016273007

25 Sep 2025 05:55PM UTC coverage: 54.653% (-12.0%) from 66.622%
18016273007

Pull #10248

github

web-flow
Merge 128443298 into b09b20c69
Pull Request #10248: Enforce TLV when creating a Route

25 of 30 new or added lines in 4 files covered. (83.33%)

23906 existing lines in 281 files now uncovered.

109536 of 200421 relevant lines covered (54.65%)

21816.97 hits per line

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

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

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

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

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

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

35
        graphCache *GraphCache
36

37
        V1Store
38
        *topologyManager
39

40
        quit chan struct{}
41
        wg   sync.WaitGroup
42
}
43

44
// NewChannelGraph creates a new ChannelGraph instance with the given backend.
45
func NewChannelGraph(v1Store V1Store,
46
        options ...ChanGraphOption) (*ChannelGraph, error) {
171✔
47

171✔
48
        opts := defaultChanGraphOptions()
171✔
49
        for _, o := range options {
262✔
50
                o(opts)
91✔
51
        }
91✔
52

53
        g := &ChannelGraph{
171✔
54
                V1Store:         v1Store,
171✔
55
                topologyManager: newTopologyManager(),
171✔
56
                quit:            make(chan struct{}),
171✔
57
        }
171✔
58

171✔
59
        // The graph cache can be turned off (e.g. for mobile users) for a
171✔
60
        // speed/memory usage tradeoff.
171✔
61
        if opts.useGraphCache {
309✔
62
                g.graphCache = NewGraphCache(opts.preAllocCacheNumNodes)
138✔
63
        }
138✔
64

65
        return g, nil
171✔
66
}
67

68
// Start kicks off any goroutines required for the ChannelGraph to function.
69
// If the graph cache is enabled, then it will be populated with the contents of
70
// the database.
71
func (c *ChannelGraph) Start() error {
284✔
72
        if !c.started.CompareAndSwap(false, true) {
397✔
73
                return nil
113✔
74
        }
113✔
75
        log.Debugf("ChannelGraph starting")
171✔
76
        defer log.Debug("ChannelGraph started")
171✔
77

171✔
78
        if c.graphCache != nil {
309✔
79
                if err := c.populateCache(context.TODO()); err != nil {
138✔
80
                        return fmt.Errorf("could not populate the graph "+
×
81
                                "cache: %w", err)
×
82
                }
×
83
        }
84

85
        c.wg.Add(1)
171✔
86
        go c.handleTopologySubscriptions()
171✔
87

171✔
88
        return nil
171✔
89
}
90

91
// Stop signals any active goroutines for a graceful closure.
92
func (c *ChannelGraph) Stop() error {
284✔
93
        if !c.stopped.CompareAndSwap(false, true) {
397✔
94
                return nil
113✔
95
        }
113✔
96

97
        log.Debugf("ChannelGraph shutting down...")
171✔
98
        defer log.Debug("ChannelGraph shutdown complete")
171✔
99

171✔
100
        close(c.quit)
171✔
101
        c.wg.Wait()
171✔
102

171✔
103
        return nil
171✔
104
}
105

106
// handleTopologySubscriptions ensures that topology client subscriptions,
107
// subscription cancellations and topology notifications are handled
108
// synchronously.
109
//
110
// NOTE: this MUST be run in a goroutine.
111
func (c *ChannelGraph) handleTopologySubscriptions() {
171✔
112
        defer c.wg.Done()
171✔
113

171✔
114
        for {
5,235✔
115
                select {
5,064✔
116
                // A new fully validated topology update has just arrived.
117
                // We'll notify any registered clients.
118
                case update := <-c.topologyUpdate:
4,888✔
119
                        // TODO(elle): change topology handling to be handled
4,888✔
120
                        // synchronously so that we can guarantee the order of
4,888✔
121
                        // notification delivery.
4,888✔
122
                        c.wg.Add(1)
4,888✔
123
                        go c.handleTopologyUpdate(update)
4,888✔
124

125
                        // TODO(roasbeef): remove all unconnected vertexes
126
                        // after N blocks pass with no corresponding
127
                        // announcements.
128

129
                // A new notification client update has arrived. We're either
130
                // gaining a new client, or cancelling notifications for an
131
                // existing client.
132
                case ntfnUpdate := <-c.ntfnClientUpdates:
5✔
133
                        clientID := ntfnUpdate.clientID
5✔
134

5✔
135
                        if ntfnUpdate.cancel {
6✔
136
                                client, ok := c.topologyClients.LoadAndDelete(
1✔
137
                                        clientID,
1✔
138
                                )
1✔
139
                                if ok {
2✔
140
                                        close(client.exit)
1✔
141
                                        client.wg.Wait()
1✔
142

1✔
143
                                        close(client.ntfnChan)
1✔
144
                                }
1✔
145

146
                                continue
1✔
147
                        }
148

149
                        c.topologyClients.Store(clientID, &topologyClient{
4✔
150
                                ntfnChan: ntfnUpdate.ntfnChan,
4✔
151
                                exit:     make(chan struct{}),
4✔
152
                        })
4✔
153

154
                case <-c.quit:
171✔
155
                        return
171✔
156
                }
157
        }
158
}
159

160
// populateCache loads the entire channel graph into the in-memory graph cache.
161
//
162
// NOTE: This should only be called if the graphCache has been constructed.
163
func (c *ChannelGraph) populateCache(ctx context.Context) error {
138✔
164
        startTime := time.Now()
138✔
165
        log.Info("Populating in-memory channel graph, this might take a " +
138✔
166
                "while...")
138✔
167

138✔
168
        err := c.V1Store.ForEachNodeCacheable(ctx, func(node route.Vertex,
138✔
169
                features *lnwire.FeatureVector) error {
238✔
170

100✔
171
                c.graphCache.AddNodeFeatures(node, features)
100✔
172

100✔
173
                return nil
100✔
174
        }, func() {})
238✔
175
        if err != nil {
138✔
176
                return err
×
177
        }
×
178

179
        err = c.V1Store.ForEachChannelCacheable(
138✔
180
                func(info *models.CachedEdgeInfo,
138✔
181
                        policy1, policy2 *models.CachedEdgePolicy) error {
534✔
182

396✔
183
                        c.graphCache.AddChannel(info, policy1, policy2)
396✔
184

396✔
185
                        return nil
396✔
186
                }, func() {},
534✔
187
        )
188
        if err != nil {
138✔
189
                return err
×
190
        }
×
191

192
        log.Infof("Finished populating in-memory channel graph (took %v, %s)",
138✔
193
                time.Since(startTime), c.graphCache.Stats())
138✔
194

138✔
195
        return nil
138✔
196
}
197

198
// ForEachNodeDirectedChannel iterates through all channels of a given node,
199
// executing the passed callback on the directed edge representing the channel
200
// and its incoming policy. If the callback returns an error, then the iteration
201
// is halted with the error propagated back up to the caller. If the graphCache
202
// is available, then it will be used to retrieve the node's channels instead
203
// of the database.
204
//
205
// Unknown policies are passed into the callback as nil values.
206
//
207
// NOTE: this is part of the graphdb.NodeTraverser interface.
208
func (c *ChannelGraph) ForEachNodeDirectedChannel(node route.Vertex,
209
        cb func(channel *DirectedChannel) error, reset func()) error {
508✔
210

508✔
211
        if c.graphCache != nil {
1,013✔
212
                return c.graphCache.ForEachChannel(node, cb)
505✔
213
        }
505✔
214

215
        return c.V1Store.ForEachNodeDirectedChannel(node, cb, reset)
3✔
216
}
217

218
// FetchNodeFeatures returns the features of the given node. If no features are
219
// known for the node, an empty feature vector is returned.
220
// If the graphCache is available, then it will be used to retrieve the node's
221
// features instead of the database.
222
//
223
// NOTE: this is part of the graphdb.NodeTraverser interface.
224
func (c *ChannelGraph) FetchNodeFeatures(node route.Vertex) (
225
        *lnwire.FeatureVector, error) {
462✔
226

462✔
227
        if c.graphCache != nil {
924✔
228
                return c.graphCache.GetFeatures(node), nil
462✔
229
        }
462✔
230

UNCOV
231
        return c.V1Store.FetchNodeFeatures(node)
×
232
}
233

234
// GraphSession will provide the call-back with access to a NodeTraverser
235
// instance which can be used to perform queries against the channel graph. If
236
// the graph cache is not enabled, then the call-back will be provided with
237
// access to the graph via a consistent read-only transaction.
238
func (c *ChannelGraph) GraphSession(cb func(graph NodeTraverser) error,
239
        reset func()) error {
133✔
240

133✔
241
        if c.graphCache != nil {
212✔
242
                return cb(c)
79✔
243
        }
79✔
244

245
        return c.V1Store.GraphSession(cb, reset)
54✔
246
}
247

248
// ForEachNodeCached iterates through all the stored vertices/nodes in the
249
// graph, executing the passed callback with each node encountered.
250
//
251
// NOTE: The callback contents MUST not be modified.
252
func (c *ChannelGraph) ForEachNodeCached(ctx context.Context, withAddrs bool,
253
        cb func(ctx context.Context, node route.Vertex, addrs []net.Addr,
254
                chans map[uint64]*DirectedChannel) error, reset func()) error {
120✔
255

120✔
256
        if !withAddrs && c.graphCache != nil {
120✔
257
                return c.graphCache.ForEachNode(
×
258
                        func(node route.Vertex,
×
259
                                channels map[uint64]*DirectedChannel) error {
×
260

×
261
                                return cb(ctx, node, nil, channels)
×
262
                        },
×
263
                )
264
        }
265

266
        return c.V1Store.ForEachNodeCached(ctx, withAddrs, cb, reset)
120✔
267
}
268

269
// AddNode adds a vertex/node to the graph database. If the node is not
270
// in the database from before, this will add a new, unconnected one to the
271
// graph. If it is present from before, this will update that node's
272
// information. Note that this method is expected to only be called to update an
273
// already present node from a node announcement, or to insert a node found in a
274
// channel update.
275
func (c *ChannelGraph) AddNode(ctx context.Context,
276
        node *models.Node, op ...batch.SchedulerOption) error {
714✔
277

714✔
278
        err := c.V1Store.AddNode(ctx, node, op...)
714✔
279
        if err != nil {
714✔
280
                return err
×
281
        }
×
282

283
        if c.graphCache != nil {
1,274✔
284
                c.graphCache.AddNodeFeatures(
560✔
285
                        node.PubKeyBytes, node.Features,
560✔
286
                )
560✔
287
        }
560✔
288

289
        select {
714✔
290
        case c.topologyUpdate <- node:
714✔
291
        case <-c.quit:
×
292
                return ErrChanGraphShuttingDown
×
293
        }
294

295
        return nil
714✔
296
}
297

298
// SetSourceNode sets the source node in the graph database and also adds it
299
// to the graph cache.
300
func (c *ChannelGraph) SetSourceNode(ctx context.Context,
301
        node *models.Node) error {
114✔
302

114✔
303
        err := c.V1Store.SetSourceNode(ctx, node)
114✔
304
        if err != nil {
114✔
NEW
305
                return err
×
NEW
306
        }
×
307

308
        if c.graphCache != nil {
195✔
309
                c.graphCache.AddNodeFeatures(
81✔
310
                        node.PubKeyBytes, node.Features,
81✔
311
                )
81✔
312
        }
81✔
313

314
        return nil
114✔
315
}
316

317
// DeleteNode starts a new database transaction to remove a vertex/node
318
// from the database according to the node's public key.
319
func (c *ChannelGraph) DeleteNode(ctx context.Context,
320
        nodePub route.Vertex) error {
4✔
321

4✔
322
        err := c.V1Store.DeleteNode(ctx, nodePub)
4✔
323
        if err != nil {
5✔
324
                return err
1✔
325
        }
1✔
326

327
        if c.graphCache != nil {
6✔
328
                c.graphCache.RemoveNode(nodePub)
3✔
329
        }
3✔
330

331
        return nil
3✔
332
}
333

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

1,727✔
343
        err := c.V1Store.AddChannelEdge(ctx, edge, op...)
1,727✔
344
        if err != nil {
1,964✔
345
                return err
237✔
346
        }
237✔
347

348
        if c.graphCache != nil {
2,790✔
349
                c.graphCache.AddChannel(models.NewCachedEdge(edge), nil, nil)
1,300✔
350
        }
1,300✔
351

352
        select {
1,490✔
353
        case c.topologyUpdate <- edge:
1,490✔
354
        case <-c.quit:
×
355
                return ErrChanGraphShuttingDown
×
356
        }
357

358
        return nil
1,490✔
359
}
360

361
// MarkEdgeLive clears an edge from our zombie index, deeming it as live.
362
// If the cache is enabled, the edge will be added back to the graph cache if
363
// we still have a record of this channel in the DB.
364
func (c *ChannelGraph) MarkEdgeLive(chanID uint64) error {
2✔
365
        err := c.V1Store.MarkEdgeLive(chanID)
2✔
366
        if err != nil {
3✔
367
                return err
1✔
368
        }
1✔
369

370
        if c.graphCache != nil {
2✔
371
                // We need to add the channel back into our graph cache,
1✔
372
                // otherwise we won't use it for path finding.
1✔
373
                infos, err := c.V1Store.FetchChanInfos([]uint64{chanID})
1✔
374
                if err != nil {
1✔
375
                        return err
×
376
                }
×
377

378
                if len(infos) == 0 {
2✔
379
                        return nil
1✔
380
                }
1✔
381

382
                info := infos[0]
×
383

×
384
                var policy1, policy2 *models.CachedEdgePolicy
×
385
                if info.Policy1 != nil {
×
386
                        policy1 = models.NewCachedPolicy(info.Policy1)
×
387
                }
×
388
                if info.Policy2 != nil {
×
389
                        policy2 = models.NewCachedPolicy(info.Policy2)
×
390
                }
×
391

392
                c.graphCache.AddChannel(
×
393
                        models.NewCachedEdge(info.Info), policy1, policy2,
×
394
                )
×
395
        }
396

397
        return nil
×
398
}
399

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

141✔
411
        infos, err := c.V1Store.DeleteChannelEdges(
141✔
412
                strictZombiePruning, markZombie, chanIDs...,
141✔
413
        )
141✔
414
        if err != nil {
203✔
415
                return err
62✔
416
        }
62✔
417

418
        if c.graphCache != nil {
158✔
419
                for _, info := range infos {
103✔
420
                        c.graphCache.RemoveChannel(
24✔
421
                                info.NodeKey1Bytes, info.NodeKey2Bytes,
24✔
422
                                info.ChannelID,
24✔
423
                        )
24✔
424
                }
24✔
425
        }
426

427
        return err
79✔
428
}
429

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

147✔
440
        edges, err := c.V1Store.DisconnectBlockAtHeight(height)
147✔
441
        if err != nil {
147✔
442
                return nil, err
×
443
        }
×
444

445
        if c.graphCache != nil {
294✔
446
                for _, edge := range edges {
244✔
447
                        c.graphCache.RemoveChannel(
97✔
448
                                edge.NodeKey1Bytes, edge.NodeKey2Bytes,
97✔
449
                                edge.ChannelID,
97✔
450
                        )
97✔
451
                }
97✔
452
        }
453

454
        return edges, nil
147✔
455
}
456

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

243✔
468
        edges, nodes, err := c.V1Store.PruneGraph(
243✔
469
                spentOutputs, blockHash, blockHeight,
243✔
470
        )
243✔
471
        if err != nil {
243✔
472
                return nil, err
×
473
        }
×
474

475
        if c.graphCache != nil {
486✔
476
                for _, edge := range edges {
263✔
477
                        c.graphCache.RemoveChannel(
20✔
478
                                edge.NodeKey1Bytes, edge.NodeKey2Bytes,
20✔
479
                                edge.ChannelID,
20✔
480
                        )
20✔
481
                }
20✔
482

483
                for _, node := range nodes {
301✔
484
                        c.graphCache.RemoveNode(node)
58✔
485
                }
58✔
486

487
                log.Debugf("Pruned graph, cache now has %s",
243✔
488
                        c.graphCache.Stats())
243✔
489
        }
490

491
        if len(edges) != 0 {
260✔
492
                // Notify all currently registered clients of the newly closed
17✔
493
                // channels.
17✔
494
                closeSummaries := createCloseSummaries(
17✔
495
                        blockHeight, edges...,
17✔
496
                )
17✔
497

17✔
498
                select {
17✔
499
                case c.topologyUpdate <- closeSummaries:
17✔
500
                case <-c.quit:
×
501
                        return nil, ErrChanGraphShuttingDown
×
502
                }
503
        }
504

505
        return edges, nil
243✔
506
}
507

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

518
        if c.graphCache != nil {
46✔
519
                for _, node := range nodes {
30✔
520
                        c.graphCache.RemoveNode(node)
7✔
521
                }
7✔
522
        }
523

524
        return nil
23✔
525
}
526

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

125✔
535
        unknown, knownZombies, err := c.V1Store.FilterKnownChanIDs(chansInfo)
125✔
536
        if err != nil {
125✔
537
                return nil, err
×
538
        }
×
539

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

49✔
556
                if isStillZombie {
75✔
557
                        continue
26✔
558
                }
559

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

576
        return unknown, nil
125✔
577
}
578

579
// MarkEdgeZombie attempts to mark a channel identified by its channel ID as a
580
// zombie. This method is used on an ad-hoc basis, when channels need to be
581
// marked as zombies outside the normal pruning cycle.
582
func (c *ChannelGraph) MarkEdgeZombie(chanID uint64,
583
        pubKey1, pubKey2 [33]byte) error {
127✔
584

127✔
585
        err := c.V1Store.MarkEdgeZombie(chanID, pubKey1, pubKey2)
127✔
586
        if err != nil {
127✔
587
                return err
×
588
        }
×
589

590
        if c.graphCache != nil {
254✔
591
                c.graphCache.RemoveChannel(pubKey1, pubKey2, chanID)
127✔
592
        }
127✔
593

594
        return nil
127✔
595
}
596

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

2,672✔
607
        from, to, err := c.V1Store.UpdateEdgePolicy(ctx, edge, op...)
2,672✔
608
        if err != nil {
2,677✔
609
                return err
5✔
610
        }
5✔
611

612
        if c.graphCache != nil {
4,949✔
613
                c.graphCache.UpdatePolicy(
2,282✔
614
                        models.NewCachedPolicy(edge), from, to,
2,282✔
615
                )
2,282✔
616
        }
2,282✔
617

618
        select {
2,667✔
619
        case c.topologyUpdate <- edge:
2,667✔
620
        case <-c.quit:
×
621
                return ErrChanGraphShuttingDown
×
622
        }
623

624
        return nil
2,667✔
625
}
626

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

169✔
639
        t.Helper()
169✔
640

169✔
641
        store := NewTestDB(t)
169✔
642

169✔
643
        graph, err := NewChannelGraph(store, opts...)
169✔
644
        require.NoError(t, err)
169✔
645
        require.NoError(t, graph.Start())
169✔
646

169✔
647
        t.Cleanup(func() {
338✔
648
                require.NoError(t, graph.Stop())
169✔
649
        })
169✔
650

651
        return graph
169✔
652
}
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