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

lightningnetwork / lnd / 13236757158

10 Feb 2025 08:39AM UTC coverage: 57.649% (-1.2%) from 58.815%
13236757158

Pull #9493

github

ziggie1984
lncli: for some cmds we don't replace the data of the response.

For some cmds it is not very practical to replace the json output
because we might pipe it into other commands. For example when
creating the route we want to pipe it into sendtoRoute.
Pull Request #9493: For some lncli cmds we should not replace the content with other data

0 of 9 new or added lines in 2 files covered. (0.0%)

19535 existing lines in 252 files now uncovered.

103517 of 179563 relevant lines covered (57.65%)

24878.49 hits per line

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

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

×
160
        }
×
161

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

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

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

190
        // Populate the initial states of all confirmed, public channels.
191
        for _, c := range channels {
212✔
192
                _, err := m.getOrInitChanStatus(c.FundingOutpoint)
180✔
193
                switch {
180✔
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.
UNCOV
199
                case errors.Is(err, graphdb.ErrEdgeNotFound):
×
UNCOV
200
                        log.Warnf("Unable to find channel policies for %v, "+
×
UNCOV
201
                                "skipping. This is typical if the channel is "+
×
UNCOV
202
                                "in the process of closing.", c.FundingOutpoint)
×
UNCOV
203
                        continue
×
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)
32✔
221
        go m.statusManager()
32✔
222

32✔
223
        return nil
32✔
224
}
225

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

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

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

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

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

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

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

32✔
338
        for {
1,027✔
339
                select {
995✔
340

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

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

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

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

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

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

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

400
        switch curState.Status {
83✔
401

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

13✔
407
                return nil
13✔
408

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

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

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

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

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

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

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

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

50✔
456
                err := m.signAndSendNextUpdate(outpoint, true)
50✔
457
                if err != nil {
55✔
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 {
75✔
471
                m.chanStates.markManuallyDisabled(outpoint)
20✔
472
        } else if status != ChanStatusManuallyDisabled {
90✔
473
                delete(m.chanStates, outpoint)
35✔
474
        }
35✔
475

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

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

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

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

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

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

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

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

10✔
574
                // Sign an update disabling the channel.
10✔
575
                err := m.signAndSendNextUpdate(outpoint, true)
10✔
576
                if err != nil {
10✔
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)
10✔
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) {
814✔
603
        allChannels, err := m.cfg.DB.FetchAllOpenChannels()
814✔
604
        if err != nil {
814✔
605
                return nil, err
×
606
        }
×
607

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

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

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

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

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

646
        return m.cfg.ApplyChannelUpdate(chanUpdate, &outpoint, private)
95✔
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(
313✔
663
                m.ourPubKeyBytes, info, edge1, edge2,
313✔
664
        )
313✔
665
        return update, info.AuthProof == nil, err
313✔
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
218✔
684
        if lastUpdate.ChannelFlags&lnwire.ChanUpdateDisabled == 0 {
311✔
685
                initialStatus = ChanStatusEnabled
93✔
686
        } else {
218✔
687
                initialStatus = ChanStatusDisabled
125✔
688
        }
125✔
689

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

8,059✔
702
        // Return the current ChannelState from the chanStates map if it is
8,059✔
703
        // already known to the ChanStatusManager.
8,059✔
704
        if curState, ok := m.chanStates[outpoint]; ok {
15,789✔
705
                return curState, nil
7,730✔
706
        }
7,730✔
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
218✔
719

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