github.com/decred/dcrlnd@v0.7.6/netann/chan_status_manager.go (about)

     1  package netann
     2  
     3  import (
     4  	"errors"
     5  	"sync"
     6  	"time"
     7  
     8  	"github.com/decred/dcrd/dcrec/secp256k1/v4"
     9  	"github.com/decred/dcrd/wire"
    10  	"github.com/decred/dcrlnd/channeldb"
    11  	"github.com/decred/dcrlnd/keychain"
    12  	"github.com/decred/dcrlnd/lnwallet"
    13  	"github.com/decred/dcrlnd/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 *secp256k1.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  	// FundingLocked, 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.ChannelUpdate) error
    64  
    65  	// DB stores the set of channels that are to be monitored.
    66  	DB DB
    67  
    68  	// Graph stores the channel info and policies for channels in DB.
    69  	Graph ChannelGraph
    70  
    71  	// ChanEnableTimeout is the duration a peer's connect must remain stable
    72  	// before attempting to reenable the channel.
    73  	//
    74  	// NOTE: This value is only used to verify that the relation between
    75  	// itself, ChanDisableTimeout, and ChanStatusSampleInterval is correct.
    76  	// The user is still responsible for ensuring that the same duration
    77  	// elapses before attempting to reenable a channel.
    78  	ChanEnableTimeout time.Duration
    79  
    80  	// ChanDisableTimeout is the duration the manager will wait after
    81  	// detecting that a channel has become inactive before broadcasting an
    82  	// update to disable the channel.
    83  	ChanDisableTimeout time.Duration
    84  
    85  	// ChanStatusSampleInterval is the long-polling interval used by the
    86  	// manager to check if the channels being monitored have become
    87  	// inactive.
    88  	ChanStatusSampleInterval time.Duration
    89  }
    90  
    91  // ChanStatusManager facilitates requests to enable or disable a channel via a
    92  // network announcement that sets the disable bit on the ChannelUpdate
    93  // accordingly. The manager will periodically sample to detect cases where a
    94  // link has become inactive, and facilitate the process of disabling the channel
    95  // passively. The ChanStatusManager state machine is designed to reduce the
    96  // likelihood of spamming the network with updates for flapping peers.
    97  type ChanStatusManager struct {
    98  	started sync.Once
    99  	stopped sync.Once
   100  
   101  	cfg *ChanStatusConfig
   102  
   103  	// ourPubKeyBytes is the serialized compressed pubkey of our node.
   104  	ourPubKeyBytes []byte
   105  
   106  	// chanStates contains the set of channels being monitored for status
   107  	// updates. Access to the map is serialized by the statusManager's event
   108  	// loop.
   109  	chanStates channelStates
   110  
   111  	// enableRequests pipes external requests to enable a channel into the
   112  	// primary event loop.
   113  	enableRequests chan statusRequest
   114  
   115  	// disableRequests pipes external requests to disable a channel into the
   116  	// primary event loop.
   117  	disableRequests chan statusRequest
   118  
   119  	// autoRequests pipes external requests to restore automatic channel
   120  	// state management into the primary event loop.
   121  	autoRequests chan statusRequest
   122  
   123  	// statusSampleTicker fires at the interval prescribed by
   124  	// ChanStatusSampleInterval to check if channels in chanStates have
   125  	// become inactive.
   126  	statusSampleTicker *time.Ticker
   127  
   128  	wg   sync.WaitGroup
   129  	quit chan struct{}
   130  }
   131  
   132  // NewChanStatusManager initializes a new ChanStatusManager using the given
   133  // configuration. An error is returned if the timeouts and sample interval fail
   134  // to meet do not satisfy the equation:
   135  //
   136  //	ChanEnableTimeout + ChanStatusSampleInterval > ChanDisableTimeout.
   137  func NewChanStatusManager(cfg *ChanStatusConfig) (*ChanStatusManager, error) {
   138  	// Assert that the config timeouts are properly formed. We require the
   139  	// enable_timeout + sample_interval to be less than or equal to the
   140  	// disable_timeout and that all are positive values. A peer that
   141  	// disconnects and reconnects quickly may cause a disable update to be
   142  	// sent, shortly followed by a reenable. Ensuring a healthy separation
   143  	// helps dampen the possibility of spamming updates that toggle the
   144  	// disable bit for such events.
   145  	if cfg.ChanStatusSampleInterval <= 0 {
   146  		return nil, ErrInvalidTimeoutConstraints
   147  	}
   148  	if cfg.ChanEnableTimeout <= 0 {
   149  		return nil, ErrInvalidTimeoutConstraints
   150  	}
   151  	if cfg.ChanDisableTimeout <= 0 {
   152  		return nil, ErrInvalidTimeoutConstraints
   153  	}
   154  	if cfg.ChanEnableTimeout+cfg.ChanStatusSampleInterval >
   155  		cfg.ChanDisableTimeout {
   156  		return nil, ErrInvalidTimeoutConstraints
   157  
   158  	}
   159  
   160  	return &ChanStatusManager{
   161  		cfg:                cfg,
   162  		ourPubKeyBytes:     cfg.OurPubKey.SerializeCompressed(),
   163  		chanStates:         make(channelStates),
   164  		statusSampleTicker: time.NewTicker(cfg.ChanStatusSampleInterval),
   165  		enableRequests:     make(chan statusRequest),
   166  		disableRequests:    make(chan statusRequest),
   167  		autoRequests:       make(chan statusRequest),
   168  		quit:               make(chan struct{}),
   169  	}, nil
   170  }
   171  
   172  // Start safely starts the ChanStatusManager.
   173  func (m *ChanStatusManager) Start() error {
   174  	var err error
   175  	m.started.Do(func() {
   176  		err = m.start()
   177  	})
   178  	return err
   179  }
   180  
   181  func (m *ChanStatusManager) start() error {
   182  	channels, err := m.fetchChannels()
   183  	if err != nil {
   184  		return err
   185  	}
   186  
   187  	// Populate the initial states of all confirmed, public channels.
   188  	for _, c := range channels {
   189  		_, err := m.getOrInitChanStatus(c.FundingOutpoint)
   190  		switch {
   191  
   192  		// If we can't retrieve the edge info for this channel, it may
   193  		// have been pruned from the channel graph but not yet from our
   194  		// set of channels. We'll skip it as we can't determine its
   195  		// initial state.
   196  		case err == channeldb.ErrEdgeNotFound:
   197  			log.Warnf("Unable to find channel policies for %v, "+
   198  				"skipping. This is typical if the channel is "+
   199  				"in the process of closing.", c.FundingOutpoint)
   200  			continue
   201  
   202  		// If we are in the process of opening a channel, the funding
   203  		// manager might not have added the ChannelUpdate to the graph
   204  		// yet. We'll ignore the channel for now.
   205  		case err == ErrUnableToExtractChanUpdate:
   206  			log.Warnf("Unable to find channel policies for %v, "+
   207  				"skipping. This is typical if the channel is "+
   208  				"in the process of being opened.",
   209  				c.FundingOutpoint)
   210  			continue
   211  
   212  		case err != nil:
   213  			return err
   214  		}
   215  	}
   216  
   217  	m.wg.Add(1)
   218  	go m.statusManager()
   219  
   220  	return nil
   221  }
   222  
   223  // Stop safely shuts down the ChanStatusManager.
   224  func (m *ChanStatusManager) Stop() error {
   225  	m.stopped.Do(func() {
   226  		log.Info("Channel Status Manager shutting down")
   227  		close(m.quit)
   228  		m.wg.Wait()
   229  	})
   230  	return nil
   231  }
   232  
   233  // RequestEnable submits a request to immediately enable a channel identified by
   234  // the provided outpoint. If the channel is already enabled, no action will be
   235  // taken. If the channel is marked pending-disable the channel will be returned
   236  // to an active status as the scheduled disable was never sent. Otherwise if the
   237  // channel is found to be disabled, a new announcement will be signed with the
   238  // disabled bit cleared and broadcast to the network.
   239  //
   240  // If the channel was manually disabled and RequestEnable is called with
   241  // manual = false, then the request will be ignored.
   242  //
   243  // NOTE: RequestEnable should only be called after a stable connection with the
   244  // channel's peer has lasted at least the ChanEnableTimeout. Failure to do so
   245  // may result in behavior that deviates from the expected behavior of the state
   246  // machine.
   247  func (m *ChanStatusManager) RequestEnable(outpoint wire.OutPoint,
   248  	manual bool) error {
   249  
   250  	return m.submitRequest(m.enableRequests, outpoint, manual)
   251  }
   252  
   253  // RequestDisable submits a request to immediately disable a channel identified
   254  // by the provided outpoint. If the channel is already disabled, no action will
   255  // be taken. Otherwise, a new announcement will be signed with the disabled bit
   256  // set and broadcast to the network.
   257  //
   258  // The channel state will be changed to either ChanStatusDisabled or
   259  // ChanStatusManuallyDisabled, depending on the passed-in value of manual. In
   260  // particular, note the following state transitions:
   261  //
   262  //	current state    | manual | new state
   263  //	---------------------------------------------------
   264  //	Disabled         | false  | Disabled
   265  //	ManuallyDisabled | false  | ManuallyDisabled (*)
   266  //	Disabled         | true   | ManuallyDisabled
   267  //	ManuallyDisabled | true   | ManuallyDisabled
   268  //
   269  // (*) If a channel was manually disabled, subsequent automatic / background
   270  //
   271  //	requests to disable the channel do not change the fact that the channel
   272  //	was manually disabled.
   273  func (m *ChanStatusManager) RequestDisable(outpoint wire.OutPoint,
   274  	manual bool) error {
   275  
   276  	return m.submitRequest(m.disableRequests, outpoint, manual)
   277  }
   278  
   279  // RequestAuto submits a request to restore automatic channel state management.
   280  // If the channel is in the state ChanStatusManuallyDisabled, it will be moved
   281  // back to the state ChanStatusDisabled. Otherwise, no action will be taken.
   282  func (m *ChanStatusManager) RequestAuto(outpoint wire.OutPoint) error {
   283  	return m.submitRequest(m.autoRequests, outpoint, true)
   284  }
   285  
   286  // statusRequest is passed to the statusManager to request a change in status
   287  // for a particular channel point.  The exact action is governed by passing the
   288  // request through one of the enableRequests or disableRequests channels.
   289  type statusRequest struct {
   290  	outpoint wire.OutPoint
   291  	manual   bool
   292  	errChan  chan error
   293  }
   294  
   295  // submitRequest sends a request for either enabling or disabling a particular
   296  // outpoint and awaits an error response. The request type is dictated by the
   297  // reqChan passed in, which can be either of the enableRequests or
   298  // disableRequests channels.
   299  func (m *ChanStatusManager) submitRequest(reqChan chan statusRequest,
   300  	outpoint wire.OutPoint, manual bool) error {
   301  
   302  	req := statusRequest{
   303  		outpoint: outpoint,
   304  		manual:   manual,
   305  		errChan:  make(chan error, 1),
   306  	}
   307  
   308  	select {
   309  	case reqChan <- req:
   310  	case <-m.quit:
   311  		return ErrChanStatusManagerExiting
   312  	}
   313  
   314  	select {
   315  	case err := <-req.errChan:
   316  		return err
   317  	case <-m.quit:
   318  		return ErrChanStatusManagerExiting
   319  	}
   320  }
   321  
   322  // statusManager is the primary event loop for the ChanStatusManager, providing
   323  // the necessary synchronization primitive to protect access to the chanStates
   324  // map. All requests to explicitly enable or disable a channel are processed
   325  // within this method. The statusManager will also periodically poll the active
   326  // status of channels within the htlcswitch to see if a disable announcement
   327  // should be scheduled or broadcast.
   328  //
   329  // NOTE: This method MUST be run as a goroutine.
   330  func (m *ChanStatusManager) statusManager() {
   331  	defer m.wg.Done()
   332  
   333  	for {
   334  		select {
   335  
   336  		// Process any requests to mark channel as enabled.
   337  		case req := <-m.enableRequests:
   338  			req.errChan <- m.processEnableRequest(req.outpoint, req.manual)
   339  
   340  		// Process any requests to mark channel as disabled.
   341  		case req := <-m.disableRequests:
   342  			req.errChan <- m.processDisableRequest(req.outpoint, req.manual)
   343  
   344  		// Process any requests to restore automatic channel state management.
   345  		case req := <-m.autoRequests:
   346  			req.errChan <- m.processAutoRequest(req.outpoint)
   347  
   348  		// Use long-polling to detect when channels become inactive.
   349  		case <-m.statusSampleTicker.C:
   350  			// First, do a sweep and mark any ChanStatusEnabled
   351  			// channels that are not active within the htlcswitch as
   352  			// ChanStatusPendingDisabled. The channel will then be
   353  			// disabled if no request to enable is received before
   354  			// the ChanDisableTimeout expires.
   355  			m.markPendingInactiveChannels()
   356  
   357  			// Now, do another sweep to disable any channels that
   358  			// were marked in a prior iteration as pending inactive
   359  			// if the inactive chan timeout has elapsed.
   360  			m.disableInactiveChannels()
   361  
   362  		case <-m.quit:
   363  			return
   364  		}
   365  	}
   366  }
   367  
   368  // processEnableRequest attempts to enable the given outpoint.
   369  //
   370  //   - If the channel is not active at the time of the request,
   371  //     ErrEnableInactiveChan will be returned.
   372  //   - If the channel was in the ManuallyDisabled state and manual = false,
   373  //     the request will be ignored and ErrEnableManuallyDisabledChan will be
   374  //     returned.
   375  //   - Otherwise, the status of the channel in chanStates will be
   376  //     ChanStatusEnabled and the method will return nil.
   377  //
   378  // An update will be broadcast only if the channel is currently disabled,
   379  // otherwise no update will be sent on the network.
   380  func (m *ChanStatusManager) processEnableRequest(outpoint wire.OutPoint,
   381  	manual bool) error {
   382  
   383  	curState, err := m.getOrInitChanStatus(outpoint)
   384  	if err != nil {
   385  		return err
   386  	}
   387  
   388  	// Quickly check to see if the requested channel is active within the
   389  	// htlcswitch and return an error if it isn't.
   390  	chanID := lnwire.NewChanIDFromOutPoint(&outpoint)
   391  	if !m.cfg.IsChannelActive(chanID) {
   392  		return ErrEnableInactiveChan
   393  	}
   394  
   395  	switch curState.Status {
   396  
   397  	// Channel is already enabled, nothing to do.
   398  	case ChanStatusEnabled:
   399  		return nil
   400  
   401  	// The channel is enabled, though we are now canceling the scheduled
   402  	// disable.
   403  	case ChanStatusPendingDisabled:
   404  		log.Debugf("Channel(%v) already enabled, canceling scheduled "+
   405  			"disable", outpoint)
   406  
   407  	// We'll sign a new update if the channel is still disabled.
   408  	case ChanStatusManuallyDisabled:
   409  		if !manual {
   410  			return ErrEnableManuallyDisabledChan
   411  		}
   412  		fallthrough
   413  
   414  	case ChanStatusDisabled:
   415  		log.Infof("Announcing channel(%v) enabled", outpoint)
   416  
   417  		err := m.signAndSendNextUpdate(outpoint, false)
   418  		if err != nil {
   419  			return err
   420  		}
   421  	}
   422  
   423  	m.chanStates.markEnabled(outpoint)
   424  
   425  	return nil
   426  }
   427  
   428  // processDisableRequest attempts to disable the given outpoint. If the method
   429  // returns nil, the status of the channel in chanStates will be either
   430  // ChanStatusDisabled or ChanStatusManuallyDisabled, depending on the
   431  // passed-in value of manual.
   432  //
   433  // An update will only be sent if the channel has a status other than
   434  // ChanStatusEnabled, otherwise no update will be sent on the network.
   435  func (m *ChanStatusManager) processDisableRequest(outpoint wire.OutPoint,
   436  	manual bool) error {
   437  
   438  	curState, err := m.getOrInitChanStatus(outpoint)
   439  	if err != nil {
   440  		return err
   441  	}
   442  
   443  	status := curState.Status
   444  	if status == ChanStatusEnabled || status == ChanStatusPendingDisabled {
   445  		log.Infof("Announcing channel(%v) disabled [requested]",
   446  			outpoint)
   447  
   448  		err := m.signAndSendNextUpdate(outpoint, true)
   449  		if err != nil {
   450  			return err
   451  		}
   452  	}
   453  
   454  	// Typically, a request to disable a channel via the manager's public
   455  	// interface signals that the channel is being closed.
   456  	//
   457  	// If we don't need to keep track of a manual request to disable the
   458  	// channel, then we can remove the outpoint to free up space in the map
   459  	// of channel states. If for some reason the channel isn't closed, the
   460  	// state will be repopulated on subsequent calls to the manager's public
   461  	// interface via a db lookup, or on startup.
   462  	if manual {
   463  		m.chanStates.markManuallyDisabled(outpoint)
   464  	} else if status != ChanStatusManuallyDisabled {
   465  		delete(m.chanStates, outpoint)
   466  	}
   467  
   468  	return nil
   469  }
   470  
   471  // processAutoRequest attempts to restore automatic channel state management
   472  // for the given outpoint. If the method returns nil, the state of the channel
   473  // will no longer be ChanStatusManuallyDisabled (currently the only state in
   474  // which automatic / background requests are ignored).
   475  //
   476  // No update will be sent on the network.
   477  func (m *ChanStatusManager) processAutoRequest(outpoint wire.OutPoint) error {
   478  	curState, err := m.getOrInitChanStatus(outpoint)
   479  	if err != nil {
   480  		return err
   481  	}
   482  
   483  	if curState.Status == ChanStatusManuallyDisabled {
   484  		log.Debugf("Restoring automatic control for manually disabled "+
   485  			"channel(%v)", outpoint)
   486  
   487  		m.chanStates.markDisabled(outpoint)
   488  	}
   489  	return nil
   490  }
   491  
   492  // markPendingInactiveChannels performs a sweep of the database's active
   493  // channels and determines which, if any, should have a disable announcement
   494  // scheduled. Once an active channel is determined to be pending-inactive, one
   495  // of two transitions can follow. Either the channel is disabled because no
   496  // request to enable is received before the scheduled disable is broadcast, or
   497  // the channel is successfully reenabled and channel is returned to an active
   498  // state from the POV of the ChanStatusManager.
   499  func (m *ChanStatusManager) markPendingInactiveChannels() {
   500  	channels, err := m.fetchChannels()
   501  	if err != nil {
   502  		log.Errorf("Unable to load active channels: %v", err)
   503  		return
   504  	}
   505  
   506  	for _, c := range channels {
   507  		// Determine the initial status of the active channel, and
   508  		// populate the entry in the chanStates map.
   509  		curState, err := m.getOrInitChanStatus(c.FundingOutpoint)
   510  		if err != nil {
   511  			log.Errorf("Unable to retrieve chan status for "+
   512  				"Channel(%v): %v", c.FundingOutpoint, err)
   513  			continue
   514  		}
   515  
   516  		// If the channel's status is not ChanStatusEnabled, we are
   517  		// done.  Either it is already disabled, or it has been marked
   518  		// ChanStatusPendingDisable meaning that we have already
   519  		// scheduled the time at which it will be disabled.
   520  		if curState.Status != ChanStatusEnabled {
   521  			continue
   522  		}
   523  
   524  		// If our bookkeeping shows the channel as active, sample the
   525  		// htlcswitch to see if it believes the link is also active. If
   526  		// so, we will skip marking it as ChanStatusPendingDisabled.
   527  		chanID := lnwire.NewChanIDFromOutPoint(&c.FundingOutpoint)
   528  		if m.cfg.IsChannelActive(chanID) {
   529  			continue
   530  		}
   531  
   532  		// Otherwise, we discovered that this link was inactive within
   533  		// the switch. Compute the time at which we will send out a
   534  		// disable if the peer is unable to reestablish a stable
   535  		// connection.
   536  		disableTime := time.Now().Add(m.cfg.ChanDisableTimeout)
   537  
   538  		log.Debugf("Marking channel(%v) pending-inactive",
   539  			c.FundingOutpoint)
   540  
   541  		m.chanStates.markPendingDisabled(c.FundingOutpoint, disableTime)
   542  	}
   543  }
   544  
   545  // disableInactiveChannels scans through the set of monitored channels, and
   546  // broadcast a disable update for any pending inactive channels whose
   547  // SendDisableTime has been superseded by the current time.
   548  func (m *ChanStatusManager) disableInactiveChannels() {
   549  	// Now, disable any channels whose inactive chan timeout has elapsed.
   550  	now := time.Now()
   551  	for outpoint, state := range m.chanStates {
   552  		// Ignore statuses that are not in the pending-inactive state.
   553  		if state.Status != ChanStatusPendingDisabled {
   554  			continue
   555  		}
   556  
   557  		// Ignore statuses for which the disable timeout has not
   558  		// expired.
   559  		if state.SendDisableTime.After(now) {
   560  			continue
   561  		}
   562  
   563  		log.Infof("Announcing channel(%v) disabled "+
   564  			"[detected]", outpoint)
   565  
   566  		// Sign an update disabling the channel.
   567  		err := m.signAndSendNextUpdate(outpoint, true)
   568  		if err != nil {
   569  			log.Errorf("Unable to sign update disabling "+
   570  				"channel(%v): %v", outpoint, err)
   571  
   572  			// If the edge was not found, this is a likely indicator
   573  			// that the channel has been closed. Thus we remove the
   574  			// outpoint from the set of tracked outpoints to prevent
   575  			// further attempts.
   576  			if err == channeldb.ErrEdgeNotFound {
   577  				log.Debugf("Removing channel(%v) from "+
   578  					"consideration for passive disabling",
   579  					outpoint)
   580  				delete(m.chanStates, outpoint)
   581  			}
   582  
   583  			continue
   584  		}
   585  
   586  		// Record that the channel has now been disabled.
   587  		m.chanStates.markDisabled(outpoint)
   588  	}
   589  }
   590  
   591  // fetchChannels returns the working set of channels managed by the
   592  // ChanStatusManager. The returned channels are filtered to only contain public
   593  // channels.
   594  func (m *ChanStatusManager) fetchChannels() ([]*channeldb.OpenChannel, error) {
   595  	allChannels, err := m.cfg.DB.FetchAllOpenChannels()
   596  	if err != nil {
   597  		return nil, err
   598  	}
   599  
   600  	// Filter out private channels.
   601  	var channels []*channeldb.OpenChannel
   602  	for _, c := range allChannels {
   603  		// We'll skip any private channels, as they aren't used for
   604  		// routing within the network by other nodes.
   605  		if c.ChannelFlags&lnwire.FFAnnounceChannel == 0 {
   606  			continue
   607  		}
   608  
   609  		channels = append(channels, c)
   610  	}
   611  
   612  	return channels, nil
   613  }
   614  
   615  // signAndSendNextUpdate computes and signs a valid update for the passed
   616  // outpoint, with the ability to toggle the disabled bit. The new update will
   617  // use the current time as the update's timestamp, or increment the old
   618  // timestamp by 1 to ensure the update can propagate. If signing is successful,
   619  // the new update will be sent out on the network.
   620  func (m *ChanStatusManager) signAndSendNextUpdate(outpoint wire.OutPoint,
   621  	disabled bool) error {
   622  
   623  	// Retrieve the latest update for this channel. We'll use this
   624  	// as our starting point to send the new update.
   625  	chanUpdate, err := m.fetchLastChanUpdateByOutPoint(outpoint)
   626  	if err != nil {
   627  		return err
   628  	}
   629  
   630  	err = SignChannelUpdate(
   631  		m.cfg.MessageSigner, m.cfg.OurKeyLoc, chanUpdate,
   632  		ChanUpdSetDisable(disabled), ChanUpdSetTimestamp,
   633  	)
   634  	if err != nil {
   635  		return err
   636  	}
   637  
   638  	return m.cfg.ApplyChannelUpdate(chanUpdate)
   639  }
   640  
   641  // fetchLastChanUpdateByOutPoint fetches the latest policy for our direction of
   642  // a channel, and crafts a new ChannelUpdate with this policy. Returns an error
   643  // in case our ChannelEdgePolicy is not found in the database.
   644  func (m *ChanStatusManager) fetchLastChanUpdateByOutPoint(op wire.OutPoint) (
   645  	*lnwire.ChannelUpdate, error) {
   646  
   647  	// Get the edge info and policies for this channel from the graph.
   648  	info, edge1, edge2, err := m.cfg.Graph.FetchChannelEdgesByOutpoint(&op)
   649  	if err != nil {
   650  		return nil, err
   651  	}
   652  
   653  	return ExtractChannelUpdate(m.ourPubKeyBytes, info, edge1, edge2)
   654  }
   655  
   656  // loadInitialChanState determines the initial ChannelState for a particular
   657  // outpoint. The initial ChanStatus for a given outpoint will either be
   658  // ChanStatusEnabled or ChanStatusDisabled, determined by inspecting the bits on
   659  // the most recent announcement. An error is returned if the latest update could
   660  // not be retrieved.
   661  func (m *ChanStatusManager) loadInitialChanState(
   662  	outpoint *wire.OutPoint) (ChannelState, error) {
   663  
   664  	lastUpdate, err := m.fetchLastChanUpdateByOutPoint(*outpoint)
   665  	if err != nil {
   666  		return ChannelState{}, err
   667  	}
   668  
   669  	// Determine the channel's starting status by inspecting the disable bit
   670  	// on last announcement we sent out.
   671  	var initialStatus ChanStatus
   672  	if lastUpdate.ChannelFlags&lnwire.ChanUpdateDisabled == 0 {
   673  		initialStatus = ChanStatusEnabled
   674  	} else {
   675  		initialStatus = ChanStatusDisabled
   676  	}
   677  
   678  	return ChannelState{
   679  		Status: initialStatus,
   680  	}, nil
   681  }
   682  
   683  // getOrInitChanStatus retrieves the current ChannelState for a particular
   684  // outpoint. If the chanStates map already contains an entry for the outpoint,
   685  // the value in the map is returned. Otherwise, the outpoint's initial status is
   686  // computed and updated in the chanStates map before being returned.
   687  func (m *ChanStatusManager) getOrInitChanStatus(
   688  	outpoint wire.OutPoint) (ChannelState, error) {
   689  
   690  	// Return the current ChannelState from the chanStates map if it is
   691  	// already known to the ChanStatusManager.
   692  	if curState, ok := m.chanStates[outpoint]; ok {
   693  		return curState, nil
   694  	}
   695  
   696  	// Otherwise, determine the initial state based on the last update we
   697  	// sent for the outpoint.
   698  	initialState, err := m.loadInitialChanState(&outpoint)
   699  	if err != nil {
   700  		return ChannelState{}, err
   701  	}
   702  
   703  	// Finally, store the initial state in the chanStates map. This will
   704  	// serve as are up-to-date view of the outpoint's current status, in
   705  	// addition to making the channel eligible for detecting inactivity.
   706  	m.chanStates[outpoint] = initialState
   707  
   708  	return initialState, nil
   709  }