netann: update channel status transitions to handle manual requests

This commit is contained in:
Elliott Jin 2021-02-16 00:29:29 -08:00
parent c40d291488
commit 542c89ad5d

@ -29,6 +29,12 @@ var (
// the time of the request. // the time of the request.
ErrEnableInactiveChan = errors.New("unable to enable channel which " + ErrEnableInactiveChan = errors.New("unable to enable channel which " +
"is not currently active") "is not currently active")
// ErrEnableManuallyDisabledChan signals that an automatic / background
// request to enable a channel could not be completed because the channel
// was manually disabled.
ErrEnableManuallyDisabledChan = errors.New("unable to enable channel " +
"which was manually disabled")
) )
// ChanStatusConfig holds parameters and resources required by the // ChanStatusConfig holds parameters and resources required by the
@ -219,6 +225,9 @@ func (m *ChanStatusManager) Stop() error {
// channel is found to be disabled, a new announcement will be signed with the // channel is found to be disabled, a new announcement will be signed with the
// disabled bit cleared and broadcast to the network. // disabled bit cleared and broadcast to the network.
// //
// If the channel was manually disabled and RequestEnable is called with
// manual = false, then the request will be ignored.
//
// NOTE: RequestEnable should only be called after a stable connection with the // NOTE: RequestEnable should only be called after a stable connection with the
// channel's peer has lasted at least the ChanEnableTimeout. Failure to do so // channel's peer has lasted at least the ChanEnableTimeout. Failure to do so
// may result in behavior that deviates from the expected behavior of the state // may result in behavior that deviates from the expected behavior of the state
@ -233,6 +242,21 @@ func (m *ChanStatusManager) RequestEnable(outpoint wire.OutPoint,
// by the provided outpoint. If the channel is already disabled, no action will // by the provided outpoint. If the channel is already disabled, no action will
// be taken. Otherwise, a new announcement will be signed with the disabled bit // be taken. Otherwise, a new announcement will be signed with the disabled bit
// set and broadcast to the network. // set and broadcast to the network.
//
// The channel state will be changed to either ChanStatusDisabled or
// ChanStatusManuallyDisabled, depending on the passed-in value of manual. In
// particular, note the following state transitions:
//
// current state | manual | new state
// ---------------------------------------------------
// Disabled | false | Disabled
// ManuallyDisabled | false | ManuallyDisabled (*)
// Disabled | true | ManuallyDisabled
// ManuallyDisabled | true | ManuallyDisabled
//
// (*) If a channel was manually disabled, subsequent automatic / background
// requests to disable the channel do not change the fact that the channel
// was manually disabled.
func (m *ChanStatusManager) RequestDisable(outpoint wire.OutPoint, func (m *ChanStatusManager) RequestDisable(outpoint wire.OutPoint,
manual bool) error { manual bool) error {
@ -317,12 +341,18 @@ func (m *ChanStatusManager) statusManager() {
} }
} }
// processEnableRequest attempts to enable the given outpoint. If the method // processEnableRequest attempts to enable the given outpoint.
// returns nil, the status of the channel in chanStates will be //
// ChanStatusEnabled. If the channel is not active at the time of the request, // * If the channel is not active at the time of the request,
// ErrEnableInactiveChan will be returned. An update will be broadcast only if // ErrEnableInactiveChan will be returned.
// the channel is currently disabled, otherwise no update will be sent on the // * If the channel was in the ManuallyDisabled state and manual = false,
// network. // the request will be ignored and ErrEnableManuallyDisabledChan will be
// returned.
// * Otherwise, the status of the channel in chanStates will be
// ChanStatusEnabled and the method will return nil.
//
// An update will be broadcast only if the channel is currently disabled,
// otherwise no update will be sent on the network.
func (m *ChanStatusManager) processEnableRequest(outpoint wire.OutPoint, func (m *ChanStatusManager) processEnableRequest(outpoint wire.OutPoint,
manual bool) error { manual bool) error {
@ -351,6 +381,12 @@ func (m *ChanStatusManager) processEnableRequest(outpoint wire.OutPoint,
"disable", outpoint) "disable", outpoint)
// We'll sign a new update if the channel is still disabled. // We'll sign a new update if the channel is still disabled.
case ChanStatusManuallyDisabled:
if !manual {
return ErrEnableManuallyDisabledChan
}
fallthrough
case ChanStatusDisabled: case ChanStatusDisabled:
log.Infof("Announcing channel(%v) enabled", outpoint) log.Infof("Announcing channel(%v) enabled", outpoint)
@ -366,10 +402,12 @@ func (m *ChanStatusManager) processEnableRequest(outpoint wire.OutPoint,
} }
// processDisableRequest attempts to disable the given outpoint. If the method // processDisableRequest attempts to disable the given outpoint. If the method
// returns nil, the status of the channel in chanStates will be // returns nil, the status of the channel in chanStates will be either
// ChanStatusDisabled. An update will only be sent if the channel has a status // ChanStatusDisabled or ChanStatusManuallyDisabled, depending on the
// other than ChanStatusEnabled, otherwise no update will be sent on the // passed-in value of manual.
// network. //
// An update will only be sent if the channel has a status other than
// ChanStatusEnabled, otherwise no update will be sent on the network.
func (m *ChanStatusManager) processDisableRequest(outpoint wire.OutPoint, func (m *ChanStatusManager) processDisableRequest(outpoint wire.OutPoint,
manual bool) error { manual bool) error {
@ -378,15 +416,8 @@ func (m *ChanStatusManager) processDisableRequest(outpoint wire.OutPoint,
return err return err
} }
switch curState.Status { status := curState.Status
if status == ChanStatusEnabled || status == ChanStatusPendingDisabled {
// Channel is already disabled, nothing to do.
case ChanStatusDisabled:
return nil
// We'll sign a new update disabling the channel if the current status
// is enabled or pending-inactive.
case ChanStatusEnabled, ChanStatusPendingDisabled:
log.Infof("Announcing channel(%v) disabled [requested]", log.Infof("Announcing channel(%v) disabled [requested]",
outpoint) outpoint)
@ -396,13 +427,19 @@ func (m *ChanStatusManager) processDisableRequest(outpoint wire.OutPoint,
} }
} }
// If the disable was requested via the manager's public interface, we // Typically, a request to disable a channel via the manager's public
// will remove the output from our map of channel states. Typically this // interface signals that the channel is being closed.
// signals that the channel is being closed, so this frees up the space //
// in the map. If for some reason the channel isn't closed, the state // If we don't need to keep track of a manual request to disable the
// will be repopulated on subsequent calls to RequestEnable or // channel, then we can remove the outpoint to free up space in the map
// RequestDisable via a db lookup, or on startup. // of channel states. If for some reason the channel isn't closed, the
delete(m.chanStates, outpoint) // state will be repopulated on subsequent calls to the manager's public
// interface via a db lookup, or on startup.
if manual {
m.chanStates.markManuallyDisabled(outpoint)
} else if status != ChanStatusManuallyDisabled {
delete(m.chanStates, outpoint)
}
return nil return nil
} }