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

lightningnetwork / lnd / 14779943269

01 May 2025 05:22PM UTC coverage: 69.041% (+0.009%) from 69.032%
14779943269

Pull #9752

github

web-flow
Merge 51b1059cc into b068d79df
Pull Request #9752: routerrpc: reject payment to invoice that don't have payment secret or blinded paths

5 of 6 new or added lines in 1 file covered. (83.33%)

241 existing lines in 16 files now uncovered.

133931 of 193989 relevant lines covered (69.04%)

22108.46 hits per line

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

84.86
/netann/chan_status_manager.go
1
package netann
2

3
import (
4
        "errors"
5
        "sync"
6
        "time"
7

8
        "github.com/btcsuite/btcd/btcec/v2"
9
        "github.com/btcsuite/btcd/wire"
10
        "github.com/lightningnetwork/lnd/channeldb"
11
        graphdb "github.com/lightningnetwork/lnd/graph/db"
12
        "github.com/lightningnetwork/lnd/keychain"
13
        "github.com/lightningnetwork/lnd/lnwallet"
14
        "github.com/lightningnetwork/lnd/lnwire"
15
)
16

17
var (
18
        // ErrChanStatusManagerExiting signals that a shutdown of the
19
        // ChanStatusManager has already been requested.
20
        ErrChanStatusManagerExiting = errors.New("chan status manager exiting")
21

22
        // ErrInvalidTimeoutConstraints signals that the ChanStatusManager could
23
        // not be initialized because the timeouts and sample intervals were
24
        // malformed.
25
        ErrInvalidTimeoutConstraints = errors.New("chan-enable-timeout + " +
26
                "chan-status-sample-interval must <= chan-disable-timeout " +
27
                "and all three chan configs must be positive integers")
28

29
        // ErrEnableInactiveChan signals that a request to enable a channel
30
        // could not be completed because the channel isn't actually active at
31
        // the time of the request.
32
        ErrEnableInactiveChan = errors.New("unable to enable channel which " +
33
                "is not currently active")
34

35
        // ErrEnableManuallyDisabledChan signals that an automatic / background
36
        // request to enable a channel could not be completed because the channel
37
        // was manually disabled.
38
        ErrEnableManuallyDisabledChan = errors.New("unable to enable channel " +
39
                "which was manually disabled")
40
)
41

42
// ChanStatusConfig holds parameters and resources required by the
43
// ChanStatusManager to perform its duty.
44
type ChanStatusConfig struct {
45
        // OurPubKey is the public key identifying this node on the network.
46
        OurPubKey *btcec.PublicKey
47

48
        // OurKeyLoc is the locator for the public key identifying this node on
49
        // the network.
50
        OurKeyLoc keychain.KeyLocator
51

52
        // MessageSigner signs messages that validate under OurPubKey.
53
        MessageSigner lnwallet.MessageSigner
54

55
        // IsChannelActive checks whether the channel identified by the provided
56
        // ChannelID is considered active. This should only return true if the
57
        // channel has been sufficiently confirmed, the channel has received
58
        // ChannelReady, and the remote peer is online.
59
        IsChannelActive func(lnwire.ChannelID) bool
60

61
        // ApplyChannelUpdate processes new ChannelUpdates signed by our node by
62
        // updating our local routing table and broadcasting the update to our
63
        // peers.
64
        ApplyChannelUpdate func(*lnwire.ChannelUpdate1, *wire.OutPoint,
65
                bool) error
66

67
        // DB stores the set of channels that are to be monitored.
68
        DB DB
69

70
        // Graph stores the channel info and policies for channels in DB.
71
        Graph ChannelGraph
72

73
        // ChanEnableTimeout is the duration a peer's connect must remain stable
74
        // before attempting to re-enable the channel.
75
        //
76
        // NOTE: This value is only used to verify that the relation between
77
        // itself, ChanDisableTimeout, and ChanStatusSampleInterval is correct.
78
        // The user is still responsible for ensuring that the same duration
79
        // elapses before attempting to re-enable a channel.
80
        ChanEnableTimeout time.Duration
81

82
        // ChanDisableTimeout is the duration the manager will wait after
83
        // detecting that a channel has become inactive before broadcasting an
84
        // update to disable the channel.
85
        ChanDisableTimeout time.Duration
86

87
        // ChanStatusSampleInterval is the long-polling interval used by the
88
        // manager to check if the channels being monitored have become
89
        // inactive.
90
        ChanStatusSampleInterval time.Duration
91
}
92

93
// ChanStatusManager facilitates requests to enable or disable a channel via a
94
// network announcement that sets the disable bit on the ChannelUpdate
95
// accordingly. The manager will periodically sample to detect cases where a
96
// link has become inactive, and facilitate the process of disabling the channel
97
// passively. The ChanStatusManager state machine is designed to reduce the
98
// likelihood of spamming the network with updates for flapping peers.
99
type ChanStatusManager struct {
100
        started sync.Once
101
        stopped sync.Once
102

103
        cfg *ChanStatusConfig
104

105
        // ourPubKeyBytes is the serialized compressed pubkey of our node.
106
        ourPubKeyBytes []byte
107

108
        // chanStates contains the set of channels being monitored for status
109
        // updates. Access to the map is serialized by the statusManager's event
110
        // loop.
111
        chanStates channelStates
112

113
        // enableRequests pipes external requests to enable a channel into the
114
        // primary event loop.
115
        enableRequests chan statusRequest
116

117
        // disableRequests pipes external requests to disable a channel into the
118
        // primary event loop.
119
        disableRequests chan statusRequest
120

121
        // autoRequests pipes external requests to restore automatic channel
122
        // state management into the primary event loop.
123
        autoRequests chan statusRequest
124

125
        // statusSampleTicker fires at the interval prescribed by
126
        // ChanStatusSampleInterval to check if channels in chanStates have
127
        // become inactive.
128
        statusSampleTicker *time.Ticker
129

130
        wg   sync.WaitGroup
131
        quit chan struct{}
132
}
133

134
// NewChanStatusManager initializes a new ChanStatusManager using the given
135
// configuration. An error is returned if the timeouts and sample interval fail
136
// to meet do not satisfy the equation:
137
//
138
//        ChanEnableTimeout + ChanStatusSampleInterval > ChanDisableTimeout.
139
func NewChanStatusManager(cfg *ChanStatusConfig) (*ChanStatusManager, error) {
40✔
140
        // Assert that the config timeouts are properly formed. We require the
40✔
141
        // enable_timeout + sample_interval to be less than or equal to the
40✔
142
        // disable_timeout and that all are positive values. A peer that
40✔
143
        // disconnects and reconnects quickly may cause a disable update to be
40✔
144
        // sent, shortly followed by a re-enable. Ensuring a healthy separation
40✔
145
        // helps dampen the possibility of spamming updates that toggle the
40✔
146
        // disable bit for such events.
40✔
147
        if cfg.ChanStatusSampleInterval <= 0 {
40✔
148
                return nil, ErrInvalidTimeoutConstraints
×
149
        }
×
150
        if cfg.ChanEnableTimeout <= 0 {
40✔
151
                return nil, ErrInvalidTimeoutConstraints
×
152
        }
×
153
        if cfg.ChanDisableTimeout <= 0 {
40✔
154
                return nil, ErrInvalidTimeoutConstraints
×
155
        }
×
156
        if cfg.ChanEnableTimeout+cfg.ChanStatusSampleInterval >
40✔
157
                cfg.ChanDisableTimeout {
40✔
158
                return nil, ErrInvalidTimeoutConstraints
×
159

×
160
        }
×
161

162
        return &ChanStatusManager{
40✔
163
                cfg:                cfg,
40✔
164
                ourPubKeyBytes:     cfg.OurPubKey.SerializeCompressed(),
40✔
165
                chanStates:         make(channelStates),
40✔
166
                statusSampleTicker: time.NewTicker(cfg.ChanStatusSampleInterval),
40✔
167
                enableRequests:     make(chan statusRequest),
40✔
168
                disableRequests:    make(chan statusRequest),
40✔
169
                autoRequests:       make(chan statusRequest),
40✔
170
                quit:               make(chan struct{}),
40✔
171
        }, nil
40✔
172
}
173

174
// Start safely starts the ChanStatusManager.
175
func (m *ChanStatusManager) Start() error {
35✔
176
        var err error
35✔
177
        m.started.Do(func() {
70✔
178
                log.Info("Channel Status Manager starting")
35✔
179
                err = m.start()
35✔
180
        })
35✔
181
        return err
35✔
182
}
183

184
func (m *ChanStatusManager) start() error {
35✔
185
        channels, err := m.fetchChannels()
35✔
186
        if err != nil {
35✔
187
                return err
×
188
        }
×
189

190
        // Populate the initial states of all confirmed, public channels.
191
        for _, c := range channels {
218✔
192
                _, err := m.getOrInitChanStatus(c.FundingOutpoint)
183✔
193
                switch {
183✔
194

195
                // If we can't retrieve the edge info for this channel, it may
196
                // have been pruned from the channel graph but not yet from our
197
                // set of channels. We'll skip it as we can't determine its
198
                // initial state.
199
                case errors.Is(err, graphdb.ErrEdgeNotFound):
3✔
200
                        log.Warnf("Unable to find channel policies for %v, "+
3✔
201
                                "skipping. This is typical if the channel is "+
3✔
202
                                "in the process of closing.", c.FundingOutpoint)
3✔
203
                        continue
3✔
204

205
                // If we are in the process of opening a channel, the funding
206
                // manager might not have added the ChannelUpdate to the graph
207
                // yet. We'll ignore the channel for now.
208
                case err == ErrUnableToExtractChanUpdate:
×
209
                        log.Warnf("Unable to find channel policies for %v, "+
×
210
                                "skipping. This is typical if the channel is "+
×
211
                                "in the process of being opened.",
×
212
                                c.FundingOutpoint)
×
213
                        continue
×
214

215
                case err != nil:
×
216
                        return err
×
217
                }
218
        }
219

220
        m.wg.Add(1)
35✔
221
        go m.statusManager()
35✔
222

35✔
223
        return nil
35✔
224
}
225

226
// Stop safely shuts down the ChanStatusManager.
227
func (m *ChanStatusManager) Stop() error {
35✔
228
        m.stopped.Do(func() {
70✔
229
                log.Info("Channel Status Manager shutting down...")
35✔
230
                defer log.Debug("Channel Status Manager shutdown complete")
35✔
231

35✔
232
                close(m.quit)
35✔
233
                m.wg.Wait()
35✔
234
        })
35✔
235
        return nil
35✔
236
}
237

238
// RequestEnable submits a request to immediately enable a channel identified by
239
// the provided outpoint. If the channel is already enabled, no action will be
240
// taken. If the channel is marked pending-disable the channel will be returned
241
// to an active status as the scheduled disable was never sent. Otherwise if the
242
// channel is found to be disabled, a new announcement will be signed with the
243
// disabled bit cleared and broadcast to the network.
244
//
245
// If the channel was manually disabled and RequestEnable is called with
246
// manual = false, then the request will be ignored.
247
//
248
// NOTE: RequestEnable should only be called after a stable connection with the
249
// channel's peer has lasted at least the ChanEnableTimeout. Failure to do so
250
// may result in behavior that deviates from the expected behavior of the state
251
// machine.
252
func (m *ChanStatusManager) RequestEnable(outpoint wire.OutPoint,
253
        manual bool) error {
102✔
254

102✔
255
        return m.submitRequest(m.enableRequests, outpoint, manual)
102✔
256
}
102✔
257

258
// RequestDisable submits a request to immediately disable a channel identified
259
// by the provided outpoint. If the channel is already disabled, no action will
260
// be taken. Otherwise, a new announcement will be signed with the disabled bit
261
// set and broadcast to the network.
262
//
263
// The channel state will be changed to either ChanStatusDisabled or
264
// ChanStatusManuallyDisabled, depending on the passed-in value of manual. In
265
// particular, note the following state transitions:
266
//
267
//        current state    | manual | new state
268
//        ---------------------------------------------------
269
//        Disabled         | false  | Disabled
270
//        ManuallyDisabled | false  | ManuallyDisabled (*)
271
//        Disabled         | true   | ManuallyDisabled
272
//        ManuallyDisabled | true   | ManuallyDisabled
273
//
274
// (*) If a channel was manually disabled, subsequent automatic / background
275
//
276
//        requests to disable the channel do not change the fact that the channel
277
//        was manually disabled.
278
func (m *ChanStatusManager) RequestDisable(outpoint wire.OutPoint,
279
        manual bool) error {
75✔
280

75✔
281
        return m.submitRequest(m.disableRequests, outpoint, manual)
75✔
282
}
75✔
283

284
// RequestAuto submits a request to restore automatic channel state management.
285
// If the channel is in the state ChanStatusManuallyDisabled, it will be moved
286
// back to the state ChanStatusDisabled. Otherwise, no action will be taken.
287
func (m *ChanStatusManager) RequestAuto(outpoint wire.OutPoint) error {
13✔
288
        return m.submitRequest(m.autoRequests, outpoint, true)
13✔
289
}
13✔
290

291
// statusRequest is passed to the statusManager to request a change in status
292
// for a particular channel point.  The exact action is governed by passing the
293
// request through one of the enableRequests or disableRequests channels.
294
type statusRequest struct {
295
        outpoint wire.OutPoint
296
        manual   bool
297
        errChan  chan error
298
}
299

300
// submitRequest sends a request for either enabling or disabling a particular
301
// outpoint and awaits an error response. The request type is dictated by the
302
// reqChan passed in, which can be either of the enableRequests or
303
// disableRequests channels.
304
func (m *ChanStatusManager) submitRequest(reqChan chan statusRequest,
305
        outpoint wire.OutPoint, manual bool) error {
184✔
306

184✔
307
        req := statusRequest{
184✔
308
                outpoint: outpoint,
184✔
309
                manual:   manual,
184✔
310
                errChan:  make(chan error, 1),
184✔
311
        }
184✔
312

184✔
313
        select {
184✔
314
        case reqChan <- req:
184✔
315
        case <-m.quit:
×
316
                return ErrChanStatusManagerExiting
×
317
        }
318

319
        select {
184✔
320
        case err := <-req.errChan:
184✔
321
                return err
184✔
322
        case <-m.quit:
×
323
                return ErrChanStatusManagerExiting
×
324
        }
325
}
326

327
// statusManager is the primary event loop for the ChanStatusManager, providing
328
// the necessary synchronization primitive to protect access to the chanStates
329
// map. All requests to explicitly enable or disable a channel are processed
330
// within this method. The statusManager will also periodically poll the active
331
// status of channels within the htlcswitch to see if a disable announcement
332
// should be scheduled or broadcast.
333
//
334
// NOTE: This method MUST be run as a goroutine.
335
func (m *ChanStatusManager) statusManager() {
35✔
336
        defer m.wg.Done()
35✔
337

35✔
338
        for {
1,031✔
339
                select {
996✔
340

341
                // Process any requests to mark channel as enabled.
342
                case req := <-m.enableRequests:
102✔
343
                        req.errChan <- m.processEnableRequest(req.outpoint, req.manual)
102✔
344

345
                // Process any requests to mark channel as disabled.
346
                case req := <-m.disableRequests:
75✔
347
                        req.errChan <- m.processDisableRequest(req.outpoint, req.manual)
75✔
348

349
                // Process any requests to restore automatic channel state management.
350
                case req := <-m.autoRequests:
13✔
351
                        req.errChan <- m.processAutoRequest(req.outpoint)
13✔
352

353
                // Use long-polling to detect when channels become inactive.
354
                case <-m.statusSampleTicker.C:
783✔
355
                        // First, do a sweep and mark any ChanStatusEnabled
783✔
356
                        // channels that are not active within the htlcswitch as
783✔
357
                        // ChanStatusPendingDisabled. The channel will then be
783✔
358
                        // disabled if no request to enable is received before
783✔
359
                        // the ChanDisableTimeout expires.
783✔
360
                        m.markPendingInactiveChannels()
783✔
361

783✔
362
                        // Now, do another sweep to disable any channels that
783✔
363
                        // were marked in a prior iteration as pending inactive
783✔
364
                        // if the inactive chan timeout has elapsed.
783✔
365
                        m.disableInactiveChannels()
783✔
366

367
                case <-m.quit:
35✔
368
                        return
35✔
369
                }
370
        }
371
}
372

373
// processEnableRequest attempts to enable the given outpoint.
374
//
375
//   - If the channel is not active at the time of the request,
376
//     ErrEnableInactiveChan will be returned.
377
//   - If the channel was in the ManuallyDisabled state and manual = false,
378
//     the request will be ignored and ErrEnableManuallyDisabledChan will be
379
//     returned.
380
//   - Otherwise, the status of the channel in chanStates will be
381
//     ChanStatusEnabled and the method will return nil.
382
//
383
// An update will be broadcast only if the channel is currently disabled,
384
// otherwise no update will be sent on the network.
385
func (m *ChanStatusManager) processEnableRequest(outpoint wire.OutPoint,
386
        manual bool) error {
102✔
387

102✔
388
        curState, err := m.getOrInitChanStatus(outpoint)
102✔
389
        if err != nil {
108✔
390
                return err
6✔
391
        }
6✔
392

393
        // Quickly check to see if the requested channel is active within the
394
        // htlcswitch and return an error if it isn't.
395
        chanID := lnwire.NewChanIDFromOutPoint(outpoint)
96✔
396
        if !m.cfg.IsChannelActive(chanID) {
106✔
397
                return ErrEnableInactiveChan
10✔
398
        }
10✔
399

400
        switch curState.Status {
86✔
401

402
        // Channel is already enabled, nothing to do.
403
        case ChanStatusEnabled:
16✔
404
                log.Debugf("Channel(%v) already enabled, skipped "+
16✔
405
                        "announcement", outpoint)
16✔
406

16✔
407
                return nil
16✔
408

409
        // The channel is enabled, though we are now canceling the scheduled
410
        // disable.
411
        case ChanStatusPendingDisabled:
13✔
412
                log.Debugf("Channel(%v) already enabled, canceling scheduled "+
13✔
413
                        "disable", outpoint)
13✔
414

415
        // We'll sign a new update if the channel is still disabled.
416
        case ChanStatusManuallyDisabled:
33✔
417
                if !manual {
56✔
418
                        return ErrEnableManuallyDisabledChan
23✔
419
                }
23✔
420
                fallthrough
13✔
421

422
        case ChanStatusDisabled:
43✔
423
                log.Infof("Announcing channel(%v) enabled", outpoint)
43✔
424

43✔
425
                err := m.signAndSendNextUpdate(outpoint, false)
43✔
426
                if err != nil {
43✔
427
                        return err
×
428
                }
×
429
        }
430

431
        m.chanStates.markEnabled(outpoint)
53✔
432

53✔
433
        return nil
53✔
434
}
435

436
// processDisableRequest attempts to disable the given outpoint. If the method
437
// returns nil, the status of the channel in chanStates will be either
438
// ChanStatusDisabled or ChanStatusManuallyDisabled, depending on the
439
// passed-in value of manual.
440
//
441
// An update will only be sent if the channel has a status other than
442
// ChanStatusEnabled, otherwise no update will be sent on the network.
443
func (m *ChanStatusManager) processDisableRequest(outpoint wire.OutPoint,
444
        manual bool) error {
75✔
445

75✔
446
        curState, err := m.getOrInitChanStatus(outpoint)
75✔
447
        if err != nil {
87✔
448
                return err
12✔
449
        }
12✔
450

451
        status := curState.Status
63✔
452
        if status == ChanStatusEnabled || status == ChanStatusPendingDisabled {
116✔
453
                log.Infof("Announcing channel(%v) disabled [requested]",
53✔
454
                        outpoint)
53✔
455

53✔
456
                err := m.signAndSendNextUpdate(outpoint, true)
53✔
457
                if err != nil {
58✔
458
                        return err
5✔
459
                }
5✔
460
        }
461

462
        // Typically, a request to disable a channel via the manager's public
463
        // interface signals that the channel is being closed.
464
        //
465
        // If we don't need to keep track of a manual request to disable the
466
        // channel, then we can remove the outpoint to free up space in the map
467
        // of channel states. If for some reason the channel isn't closed, the
468
        // state will be repopulated on subsequent calls to the manager's public
469
        // interface via a db lookup, or on startup.
470
        if manual {
81✔
471
                m.chanStates.markManuallyDisabled(outpoint)
23✔
472
        } else if status != ChanStatusManuallyDisabled {
99✔
473
                delete(m.chanStates, outpoint)
38✔
474
        }
38✔
475

476
        return nil
58✔
477
}
478

479
// processAutoRequest attempts to restore automatic channel state management
480
// for the given outpoint. If the method returns nil, the state of the channel
481
// will no longer be ChanStatusManuallyDisabled (currently the only state in
482
// which automatic / background requests are ignored).
483
//
484
// No update will be sent on the network.
485
func (m *ChanStatusManager) processAutoRequest(outpoint wire.OutPoint) error {
13✔
486
        curState, err := m.getOrInitChanStatus(outpoint)
13✔
487
        if err != nil {
13✔
488
                return err
×
489
        }
×
490

491
        if curState.Status == ChanStatusManuallyDisabled {
26✔
492
                log.Debugf("Restoring automatic control for manually disabled "+
13✔
493
                        "channel(%v)", outpoint)
13✔
494

13✔
495
                m.chanStates.markDisabled(outpoint)
13✔
496
        }
13✔
497
        return nil
13✔
498
}
499

500
// markPendingInactiveChannels performs a sweep of the database's active
501
// channels and determines which, if any, should have a disable announcement
502
// scheduled. Once an active channel is determined to be pending-inactive, one
503
// of two transitions can follow. Either the channel is disabled because no
504
// request to enable is received before the scheduled disable is broadcast, or
505
// the channel is successfully re-enabled and channel is returned to an active
506
// state from the POV of the ChanStatusManager.
507
func (m *ChanStatusManager) markPendingInactiveChannels() {
783✔
508
        channels, err := m.fetchChannels()
783✔
509
        if err != nil {
783✔
510
                log.Errorf("Unable to load active channels: %v", err)
×
511
                return
×
512
        }
×
513

514
        for _, c := range channels {
8,466✔
515
                // Determine the initial status of the active channel, and
7,683✔
516
                // populate the entry in the chanStates map.
7,683✔
517
                curState, err := m.getOrInitChanStatus(c.FundingOutpoint)
7,683✔
518
                if err != nil {
7,773✔
519
                        log.Errorf("Unable to retrieve chan status for "+
90✔
520
                                "Channel(%v): %v", c.FundingOutpoint, err)
90✔
521
                        continue
90✔
522
                }
523

524
                // If the channel's status is not ChanStatusEnabled, we are
525
                // done.  Either it is already disabled, or it has been marked
526
                // ChanStatusPendingDisable meaning that we have already
527
                // scheduled the time at which it will be disabled.
528
                if curState.Status != ChanStatusEnabled {
11,936✔
529
                        continue
4,343✔
530
                }
531

532
                // If our bookkeeping shows the channel as active, sample the
533
                // htlcswitch to see if it believes the link is also active. If
534
                // so, we will skip marking it as ChanStatusPendingDisabled.
535
                chanID := lnwire.NewChanIDFromOutPoint(c.FundingOutpoint)
3,253✔
536
                if m.cfg.IsChannelActive(chanID) {
6,486✔
537
                        continue
3,233✔
538
                }
539

540
                // Otherwise, we discovered that this link was inactive within
541
                // the switch. Compute the time at which we will send out a
542
                // disable if the peer is unable to reestablish a stable
543
                // connection.
544
                disableTime := time.Now().Add(m.cfg.ChanDisableTimeout)
23✔
545

23✔
546
                log.Debugf("Marking channel(%v) pending-inactive",
23✔
547
                        c.FundingOutpoint)
23✔
548

23✔
549
                m.chanStates.markPendingDisabled(c.FundingOutpoint, disableTime)
23✔
550
        }
551
}
552

553
// disableInactiveChannels scans through the set of monitored channels, and
554
// broadcast a disable update for any pending inactive channels whose
555
// SendDisableTime has been superseded by the current time.
556
func (m *ChanStatusManager) disableInactiveChannels() {
783✔
557
        // Now, disable any channels whose inactive chan timeout has elapsed.
783✔
558
        now := time.Now()
783✔
559
        for outpoint, state := range m.chanStates {
8,676✔
560
                // Ignore statuses that are not in the pending-inactive state.
7,893✔
561
                if state.Status != ChanStatusPendingDisabled {
15,546✔
562
                        continue
7,653✔
563
                }
564

565
                // Ignore statuses for which the disable timeout has not
566
                // expired.
567
                if state.SendDisableTime.After(now) {
476✔
568
                        continue
233✔
569
                }
570

571
                log.Infof("Announcing channel(%v) disabled "+
13✔
572
                        "[detected]", outpoint)
13✔
573

13✔
574
                // Sign an update disabling the channel.
13✔
575
                err := m.signAndSendNextUpdate(outpoint, true)
13✔
576
                if err != nil {
13✔
UNCOV
577
                        log.Errorf("Unable to sign update disabling "+
×
UNCOV
578
                                "channel(%v): %v", outpoint, err)
×
UNCOV
579

×
UNCOV
580
                        // If the edge was not found, this is a likely indicator
×
UNCOV
581
                        // that the channel has been closed. Thus we remove the
×
UNCOV
582
                        // outpoint from the set of tracked outpoints to prevent
×
UNCOV
583
                        // further attempts.
×
UNCOV
584
                        if errors.Is(err, graphdb.ErrEdgeNotFound) {
×
UNCOV
585
                                log.Debugf("Removing channel(%v) from "+
×
UNCOV
586
                                        "consideration for passive disabling",
×
UNCOV
587
                                        outpoint)
×
UNCOV
588
                                delete(m.chanStates, outpoint)
×
UNCOV
589
                        }
×
590

UNCOV
591
                        continue
×
592
                }
593

594
                // Record that the channel has now been disabled.
595
                m.chanStates.markDisabled(outpoint)
13✔
596
        }
597
}
598

599
// fetchChannels returns the working set of channels managed by the
600
// ChanStatusManager. The returned channels are filtered to only contain public
601
// channels.
602
func (m *ChanStatusManager) fetchChannels() ([]*channeldb.OpenChannel, error) {
815✔
603
        allChannels, err := m.cfg.DB.FetchAllOpenChannels()
815✔
604
        if err != nil {
815✔
605
                return nil, err
×
606
        }
×
607

608
        // Filter out private channels.
609
        var channels []*channeldb.OpenChannel
815✔
610
        for _, c := range allChannels {
8,678✔
611
                // We'll skip any private channels, as they aren't used for
7,863✔
612
                // routing within the network by other nodes.
7,863✔
613
                if c.ChannelFlags&lnwire.FFAnnounceChannel == 0 {
7,866✔
614
                        continue
3✔
615
                }
616

617
                channels = append(channels, c)
7,863✔
618
        }
619

620
        return channels, nil
815✔
621
}
622

623
// signAndSendNextUpdate computes and signs a valid update for the passed
624
// outpoint, with the ability to toggle the disabled bit. The new update will
625
// use the current time as the update's timestamp, or increment the old
626
// timestamp by 1 to ensure the update can propagate. If signing is successful,
627
// the new update will be sent out on the network.
628
func (m *ChanStatusManager) signAndSendNextUpdate(outpoint wire.OutPoint,
629
        disabled bool) error {
103✔
630

103✔
631
        // Retrieve the latest update for this channel. We'll use this
103✔
632
        // as our starting point to send the new update.
103✔
633
        chanUpdate, private, err := m.fetchLastChanUpdateByOutPoint(outpoint)
103✔
634
        if err != nil {
108✔
635
                return err
5✔
636
        }
5✔
637

638
        err = SignChannelUpdate(
98✔
639
                m.cfg.MessageSigner, m.cfg.OurKeyLoc, chanUpdate,
98✔
640
                ChanUpdSetDisable(disabled), ChanUpdSetTimestamp,
98✔
641
        )
98✔
642
        if err != nil {
98✔
643
                return err
×
644
        }
×
645

646
        return m.cfg.ApplyChannelUpdate(chanUpdate, &outpoint, private)
98✔
647
}
648

649
// fetchLastChanUpdateByOutPoint fetches the latest policy for our direction of
650
// a channel, and crafts a new ChannelUpdate with this policy. Returns an error
651
// in case our ChannelEdgePolicy is not found in the database. Also returns if
652
// the channel is private by checking AuthProof for nil.
653
func (m *ChanStatusManager) fetchLastChanUpdateByOutPoint(op wire.OutPoint) (
654
        *lnwire.ChannelUpdate1, bool, error) {
429✔
655

429✔
656
        // Get the edge info and policies for this channel from the graph.
429✔
657
        info, edge1, edge2, err := m.cfg.Graph.FetchChannelEdgesByOutpoint(&op)
429✔
658
        if err != nil {
545✔
659
                return nil, false, err
116✔
660
        }
116✔
661

662
        update, err := ExtractChannelUpdate(
316✔
663
                m.ourPubKeyBytes, info, edge1, edge2,
316✔
664
        )
316✔
665
        return update, info.AuthProof == nil, err
316✔
666
}
667

668
// loadInitialChanState determines the initial ChannelState for a particular
669
// outpoint. The initial ChanStatus for a given outpoint will either be
670
// ChanStatusEnabled or ChanStatusDisabled, determined by inspecting the bits on
671
// the most recent announcement. An error is returned if the latest update could
672
// not be retrieved.
673
func (m *ChanStatusManager) loadInitialChanState(
674
        outpoint *wire.OutPoint) (ChannelState, error) {
329✔
675

329✔
676
        lastUpdate, _, err := m.fetchLastChanUpdateByOutPoint(*outpoint)
329✔
677
        if err != nil {
440✔
678
                return ChannelState{}, err
111✔
679
        }
111✔
680

681
        // Determine the channel's starting status by inspecting the disable bit
682
        // on last announcement we sent out.
683
        var initialStatus ChanStatus
221✔
684
        if lastUpdate.ChannelFlags&lnwire.ChanUpdateDisabled == 0 {
317✔
685
                initialStatus = ChanStatusEnabled
96✔
686
        } else {
224✔
687
                initialStatus = ChanStatusDisabled
128✔
688
        }
128✔
689

690
        return ChannelState{
221✔
691
                Status: initialStatus,
221✔
692
        }, nil
221✔
693
}
694

695
// getOrInitChanStatus retrieves the current ChannelState for a particular
696
// outpoint. If the chanStates map already contains an entry for the outpoint,
697
// the value in the map is returned. Otherwise, the outpoint's initial status is
698
// computed and updated in the chanStates map before being returned.
699
func (m *ChanStatusManager) getOrInitChanStatus(
700
        outpoint wire.OutPoint) (ChannelState, error) {
8,044✔
701

8,044✔
702
        // Return the current ChannelState from the chanStates map if it is
8,044✔
703
        // already known to the ChanStatusManager.
8,044✔
704
        if curState, ok := m.chanStates[outpoint]; ok {
15,762✔
705
                return curState, nil
7,718✔
706
        }
7,718✔
707

708
        // Otherwise, determine the initial state based on the last update we
709
        // sent for the outpoint.
710
        initialState, err := m.loadInitialChanState(&outpoint)
329✔
711
        if err != nil {
440✔
712
                return ChannelState{}, err
111✔
713
        }
111✔
714

715
        // Finally, store the initial state in the chanStates map. This will
716
        // serve as are up-to-date view of the outpoint's current status, in
717
        // addition to making the channel eligible for detecting inactivity.
718
        m.chanStates[outpoint] = initialState
221✔
719

221✔
720
        return initialState, nil
221✔
721
}
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