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

lightningnetwork / lnd / 11216766535

07 Oct 2024 01:37PM UTC coverage: 57.817% (-1.0%) from 58.817%
11216766535

Pull #9148

github

ProofOfKeags
lnwire: remove kickoff feerate from propose/commit
Pull Request #9148: DynComms [2/n]: lnwire: add authenticated wire messages for Dyn*

571 of 879 new or added lines in 16 files covered. (64.96%)

23253 existing lines in 251 files now uncovered.

99022 of 171268 relevant lines covered (57.82%)

38420.67 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
        "github.com/lightningnetwork/lnd/keychain"
12
        "github.com/lightningnetwork/lnd/lnwallet"
13
        "github.com/lightningnetwork/lnd/lnwire"
14
)
15

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

102
        cfg *ChanStatusConfig
103

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

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

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

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

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

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

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

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

×
159
        }
×
160

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

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

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

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

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

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

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

219
        m.wg.Add(1)
32✔
220
        go m.statusManager()
32✔
221

32✔
222
        return nil
32✔
223
}
224

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

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

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

99✔
254
        return m.submitRequest(m.enableRequests, outpoint, manual)
99✔
255
}
99✔
256

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

72✔
280
        return m.submitRequest(m.disableRequests, outpoint, manual)
72✔
281
}
72✔
282

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

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

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

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

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

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

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

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

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

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

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

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

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

366
                case <-m.quit:
32✔
367
                        return
32✔
368
                }
369
        }
370
}
371

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

99✔
387
        curState, err := m.getOrInitChanStatus(outpoint)
99✔
388
        if err != nil {
105✔
389
                return err
6✔
390
        }
6✔
391

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

399
        switch curState.Status {
83✔
400

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

13✔
406
                return nil
13✔
407

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

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

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

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

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

50✔
432
        return nil
50✔
433
}
434

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

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

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

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

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

475
        return nil
55✔
476
}
477

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

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

10✔
494
                m.chanStates.markDisabled(outpoint)
10✔
495
        }
10✔
496
        return nil
10✔
497
}
498

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

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

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

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

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

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

20✔
548
                m.chanStates.markPendingDisabled(c.FundingOutpoint, disableTime)
20✔
549
        }
550
}
551

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

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

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

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

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

590
                        continue
×
591
                }
592

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

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

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

616
                channels = append(channels, c)
7,875✔
617
        }
618

619
        return channels, nil
814✔
620
}
621

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

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

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

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

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

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

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

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

326✔
675
        lastUpdate, _, err := m.fetchLastChanUpdateByOutPoint(*outpoint)
326✔
676
        if err != nil {
434✔
677
                return ChannelState{}, err
108✔
678
        }
108✔
679

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

689
        return ChannelState{
218✔
690
                Status: initialStatus,
218✔
691
        }, nil
218✔
692
}
693

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

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

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

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

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