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

lightningnetwork / lnd / 13982721865

21 Mar 2025 01:30AM UTC coverage: 58.587% (-0.5%) from 59.126%
13982721865

Pull #9623

github

web-flow
Merge 05a6b6838 into 5d921723b
Pull Request #9623: Size msg test msg

0 of 1572 new or added lines in 43 files covered. (0.0%)

17 existing lines in 5 files now uncovered.

96767 of 165169 relevant lines covered (58.59%)

1.82 hits per line

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

80.76
/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) {
3✔
140
        // Assert that the config timeouts are properly formed. We require the
3✔
141
        // enable_timeout + sample_interval to be less than or equal to the
3✔
142
        // disable_timeout and that all are positive values. A peer that
3✔
143
        // disconnects and reconnects quickly may cause a disable update to be
3✔
144
        // sent, shortly followed by a re-enable. Ensuring a healthy separation
3✔
145
        // helps dampen the possibility of spamming updates that toggle the
3✔
146
        // disable bit for such events.
3✔
147
        if cfg.ChanStatusSampleInterval <= 0 {
3✔
148
                return nil, ErrInvalidTimeoutConstraints
×
149
        }
×
150
        if cfg.ChanEnableTimeout <= 0 {
3✔
151
                return nil, ErrInvalidTimeoutConstraints
×
152
        }
×
153
        if cfg.ChanDisableTimeout <= 0 {
3✔
154
                return nil, ErrInvalidTimeoutConstraints
×
155
        }
×
156
        if cfg.ChanEnableTimeout+cfg.ChanStatusSampleInterval >
3✔
157
                cfg.ChanDisableTimeout {
3✔
158
                return nil, ErrInvalidTimeoutConstraints
×
159

×
160
        }
×
161

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

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

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

190
        // Populate the initial states of all confirmed, public channels.
191
        for _, c := range channels {
6✔
192
                _, err := m.getOrInitChanStatus(c.FundingOutpoint)
3✔
193
                switch {
3✔
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)
3✔
221
        go m.statusManager()
3✔
222

3✔
223
        return nil
3✔
224
}
225

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

3✔
232
                close(m.quit)
3✔
233
                m.wg.Wait()
3✔
234
        })
3✔
235
        return nil
3✔
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 {
3✔
254

3✔
255
        return m.submitRequest(m.enableRequests, outpoint, manual)
3✔
256
}
3✔
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 {
3✔
280

3✔
281
        return m.submitRequest(m.disableRequests, outpoint, manual)
3✔
282
}
3✔
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 {
3✔
288
        return m.submitRequest(m.autoRequests, outpoint, true)
3✔
289
}
3✔
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 {
3✔
306

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

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

319
        select {
3✔
320
        case err := <-req.errChan:
3✔
321
                return err
3✔
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() {
3✔
336
        defer m.wg.Done()
3✔
337

3✔
338
        for {
6✔
339
                select {
3✔
340

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

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

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

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

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

367
                case <-m.quit:
3✔
368
                        return
3✔
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 {
3✔
387

3✔
388
        curState, err := m.getOrInitChanStatus(outpoint)
3✔
389
        if err != nil {
3✔
390
                return err
×
391
        }
×
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)
3✔
396
        if !m.cfg.IsChannelActive(chanID) {
3✔
397
                return ErrEnableInactiveChan
×
398
        }
×
399

400
        switch curState.Status {
3✔
401

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

3✔
407
                return nil
3✔
408

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

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

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

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

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

3✔
433
        return nil
3✔
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 {
3✔
445

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

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

3✔
456
                err := m.signAndSendNextUpdate(outpoint, true)
3✔
457
                if err != nil {
3✔
458
                        return err
×
459
                }
×
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 {
6✔
471
                m.chanStates.markManuallyDisabled(outpoint)
3✔
472
        } else if status != ChanStatusManuallyDisabled {
9✔
473
                delete(m.chanStates, outpoint)
3✔
474
        }
3✔
475

476
        return nil
3✔
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 {
3✔
486
        curState, err := m.getOrInitChanStatus(outpoint)
3✔
487
        if err != nil {
3✔
488
                return err
×
489
        }
×
490

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

3✔
495
                m.chanStates.markDisabled(outpoint)
3✔
496
        }
3✔
497
        return nil
3✔
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() {
3✔
508
        channels, err := m.fetchChannels()
3✔
509
        if err != nil {
3✔
510
                log.Errorf("Unable to load active channels: %v", err)
×
511
                return
×
512
        }
×
513

514
        for _, c := range channels {
6✔
515
                // Determine the initial status of the active channel, and
3✔
516
                // populate the entry in the chanStates map.
3✔
517
                curState, err := m.getOrInitChanStatus(c.FundingOutpoint)
3✔
518
                if err != nil {
3✔
UNCOV
519
                        log.Errorf("Unable to retrieve chan status for "+
×
UNCOV
520
                                "Channel(%v): %v", c.FundingOutpoint, err)
×
UNCOV
521
                        continue
×
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 {
6✔
529
                        continue
3✔
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✔
536
                if m.cfg.IsChannelActive(chanID) {
6✔
537
                        continue
3✔
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)
3✔
545

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

3✔
549
                m.chanStates.markPendingDisabled(c.FundingOutpoint, disableTime)
3✔
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() {
3✔
557
        // Now, disable any channels whose inactive chan timeout has elapsed.
3✔
558
        now := time.Now()
3✔
559
        for outpoint, state := range m.chanStates {
6✔
560
                // Ignore statuses that are not in the pending-inactive state.
3✔
561
                if state.Status != ChanStatusPendingDisabled {
6✔
562
                        continue
3✔
563
                }
564

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

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

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

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

591
                        continue
×
592
                }
593

594
                // Record that the channel has now been disabled.
595
                m.chanStates.markDisabled(outpoint)
3✔
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) {
3✔
603
        allChannels, err := m.cfg.DB.FetchAllOpenChannels()
3✔
604
        if err != nil {
3✔
605
                return nil, err
×
606
        }
×
607

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

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

620
        return channels, nil
3✔
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 {
3✔
630

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

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

646
        return m.cfg.ApplyChannelUpdate(chanUpdate, &outpoint, private)
3✔
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) {
3✔
655

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

662
        update, err := ExtractChannelUpdate(
3✔
663
                m.ourPubKeyBytes, info, edge1, edge2,
3✔
664
        )
3✔
665
        return update, info.AuthProof == nil, err
3✔
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) {
3✔
675

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

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

690
        return ChannelState{
3✔
691
                Status: initialStatus,
3✔
692
        }, nil
3✔
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) {
3✔
701

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

708
        // Otherwise, determine the initial state based on the last update we
709
        // sent for the outpoint.
710
        initialState, err := m.loadInitialChanState(&outpoint)
3✔
711
        if err != nil {
6✔
712
                return ChannelState{}, err
3✔
713
        }
3✔
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
3✔
719

3✔
720
        return initialState, nil
3✔
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