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

lightningnetwork / lnd / 12033440129

26 Nov 2024 03:03PM UTC coverage: 48.738% (-10.3%) from 58.999%
12033440129

Pull #9309

github

yyforyongyu
gomod: update `btcd` for shutdown fix
Pull Request #9309: chainntnfs: fix `TestHistoricalConfDetailsTxIndex`

97664 of 200385 relevant lines covered (48.74%)

0.52 hits per line

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

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

×
159
        }
×
160

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

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

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

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

1✔
222
        return nil
1✔
223
}
224

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

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

1✔
254
        return m.submitRequest(m.enableRequests, outpoint, manual)
1✔
255
}
1✔
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 {
1✔
279

1✔
280
        return m.submitRequest(m.disableRequests, outpoint, manual)
1✔
281
}
1✔
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 {
1✔
287
        return m.submitRequest(m.autoRequests, outpoint, true)
1✔
288
}
1✔
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 {
1✔
305

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

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

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

1✔
337
        for {
2✔
338
                select {
1✔
339

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

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

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

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

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

366
                case <-m.quit:
1✔
367
                        return
1✔
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 {
1✔
386

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

399
        switch curState.Status {
1✔
400

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

1✔
406
                return nil
1✔
407

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

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

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

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

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

1✔
432
        return nil
1✔
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 {
1✔
444

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

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

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

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

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

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

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

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

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

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

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

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

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

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

619
        return channels, nil
1✔
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 {
1✔
629

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

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

645
        return m.cfg.ApplyChannelUpdate(chanUpdate, &outpoint, private)
1✔
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) {
1✔
654

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

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

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

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

689
        return ChannelState{
1✔
690
                Status: initialStatus,
1✔
691
        }, nil
1✔
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) {
1✔
700

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

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

1✔
719
        return initialState, nil
1✔
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