contractcourt: create breachResolver if BreachResolution present
Also transitions to the proper state based on if this is a legacy breach in the channel arbitrator or a modern breach with a resolver.
This commit is contained in:
1 parent
af03c8cb46
commit
b7407882ac
2 files changed
+122
-28
No files matched your search
@@ -147,7 +147,7 @@ const (
|
|||||||
// | | |
|
// | | |
|
||||||
// | | |-> StateCommitmentBroadcasted: chain/user trigger
|
// | | |-> StateCommitmentBroadcasted: chain/user trigger
|
||||||
// | | |
|
// | | |
|
||||||
// | | |-> StateContractClosed: local/remote close trigger
|
// | | |-> StateContractClosed: local/remote/breach close trigger
|
||||||
// | | | |
|
// | | | |
|
||||||
// | | | |-> StateWaitingFullResolution: contract resolutions not empty
|
// | | | |-> StateWaitingFullResolution: contract resolutions not empty
|
||||||
// | | | | |
|
// | | | | |
|
||||||
@@ -157,9 +157,9 @@ const (
|
|||||||
// | | | |
|
// | | | |
|
||||||
// | | | |-> StateFullyResolved: contract resolutions empty
|
// | | | |-> StateFullyResolved: contract resolutions empty
|
||||||
// | | |
|
// | | |
|
||||||
// | | |-> StateFullyResolved: coop/breach close trigger
|
// | | |-> StateFullyResolved: coop/breach(legacy) close trigger
|
||||||
// | |
|
// | |
|
||||||
// | |-> StateContractClosed: local/remote close trigger
|
// | |-> StateContractClosed: local/remote/breach close trigger
|
||||||
// | | |
|
// | | |
|
||||||
// | | |-> StateWaitingFullResolution: contract resolutions not empty
|
// | | |-> StateWaitingFullResolution: contract resolutions not empty
|
||||||
// | | | |
|
// | | | |
|
||||||
@@ -169,11 +169,11 @@ const (
|
|||||||
// | | |
|
// | | |
|
||||||
// | | |-> StateFullyResolved: contract resolutions empty
|
// | | |-> StateFullyResolved: contract resolutions empty
|
||||||
// | |
|
// | |
|
||||||
// | |-> StateFullyResolved: coop/breach close trigger
|
// | |-> StateFullyResolved: coop/breach(legacy) close trigger
|
||||||
// |
|
// |
|
||||||
// |-> StateContractClosed: local/remote close trigger
|
// |-> StateContractClosed: local/remote/breach close trigger
|
||||||
// | |
|
// | |
|
||||||
// | |-> StateWaitingFullResolution: contract resolutions empty
|
// | |-> StateWaitingFullResolution: contract resolutions not empty
|
||||||
// | | |
|
// | | |
|
||||||
// | | |-> StateWaitingFullResolution: contract resolutions not empty
|
// | | |-> StateWaitingFullResolution: contract resolutions not empty
|
||||||
// | | |
|
// | | |
|
||||||
@@ -181,7 +181,7 @@ const (
|
|||||||
// | |
|
// | |
|
||||||
// | |-> StateFullyResolved: contract resolutions empty
|
// | |-> StateFullyResolved: contract resolutions empty
|
||||||
// |
|
// |
|
||||||
// |-> StateFullyResolved: coop/breach close trigger
|
// |-> StateFullyResolved: coop/breach(legacy) close trigger
|
||||||
|
|
||||||
// StateDefault is the default state. In this state, no major actions
|
// StateDefault is the default state. In this state, no major actions
|
||||||
// need to be executed.
|
// need to be executed.
|
||||||
|
|||||||
@@ -747,8 +747,8 @@ const (
|
|||||||
coopCloseTrigger
|
coopCloseTrigger
|
||||||
|
|
||||||
// breachCloseTrigger is a transition trigger driven by a remote breach
|
// breachCloseTrigger is a transition trigger driven by a remote breach
|
||||||
// being confirmed. In this case the channel arbitrator won't have to
|
// being confirmed. In this case the channel arbitrator will wait for
|
||||||
// do anything, so we'll just clean up and exit gracefully.
|
// the breacharbiter to finish and then clean up gracefully.
|
||||||
breachCloseTrigger
|
breachCloseTrigger
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -852,9 +852,8 @@ func (c *ChannelArbitrator) stateStep(
|
|||||||
|
|
||||||
// If the trigger is a cooperative close being confirmed, then
|
// If the trigger is a cooperative close being confirmed, then
|
||||||
// we can go straight to StateFullyResolved, as there won't be
|
// we can go straight to StateFullyResolved, as there won't be
|
||||||
// any contracts to resolve. The same is true in the case of a
|
// any contracts to resolve.
|
||||||
// breach.
|
case coopCloseTrigger:
|
||||||
case coopCloseTrigger, breachCloseTrigger:
|
|
||||||
nextState = StateFullyResolved
|
nextState = StateFullyResolved
|
||||||
|
|
||||||
// Otherwise, if this state advance was triggered by a
|
// Otherwise, if this state advance was triggered by a
|
||||||
@@ -868,6 +867,14 @@ func (c *ChannelArbitrator) stateStep(
|
|||||||
fallthrough
|
fallthrough
|
||||||
case remoteCloseTrigger:
|
case remoteCloseTrigger:
|
||||||
nextState = StateContractClosed
|
nextState = StateContractClosed
|
||||||
|
|
||||||
|
case breachCloseTrigger:
|
||||||
|
nextContractState, err := c.checkLegacyBreach()
|
||||||
|
if nextContractState == StateError {
|
||||||
|
return nextContractState, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
nextState = nextContractState
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we're in this state, then we've decided to broadcast the
|
// If we're in this state, then we've decided to broadcast the
|
||||||
@@ -890,7 +897,23 @@ func (c *ChannelArbitrator) stateStep(
|
|||||||
c.cfg.ChanPoint, trigger, StateContractClosed)
|
c.cfg.ChanPoint, trigger, StateContractClosed)
|
||||||
return StateContractClosed, closeTx, nil
|
return StateContractClosed, closeTx, nil
|
||||||
|
|
||||||
case coopCloseTrigger, breachCloseTrigger:
|
case breachCloseTrigger:
|
||||||
|
nextContractState, err := c.checkLegacyBreach()
|
||||||
|
if nextContractState == StateError {
|
||||||
|
log.Infof("ChannelArbitrator(%v): unable to "+
|
||||||
|
"advance breach close resolution: %v",
|
||||||
|
c.cfg.ChanPoint, nextContractState)
|
||||||
|
return StateError, closeTx, err
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Infof("ChannelArbitrator(%v): detected %s close "+
|
||||||
|
"after closing channel, fast-forwarding to %s"+
|
||||||
|
" to resolve contract", c.cfg.ChanPoint,
|
||||||
|
trigger, nextContractState)
|
||||||
|
|
||||||
|
return nextContractState, closeTx, nil
|
||||||
|
|
||||||
|
case coopCloseTrigger:
|
||||||
log.Infof("ChannelArbitrator(%v): detected %s "+
|
log.Infof("ChannelArbitrator(%v): detected %s "+
|
||||||
"close after closing channel, fast-forwarding "+
|
"close after closing channel, fast-forwarding "+
|
||||||
"to %s to resolve contract",
|
"to %s to resolve contract",
|
||||||
@@ -994,10 +1017,18 @@ func (c *ChannelArbitrator) stateStep(
|
|||||||
case localCloseTrigger, remoteCloseTrigger:
|
case localCloseTrigger, remoteCloseTrigger:
|
||||||
nextState = StateContractClosed
|
nextState = StateContractClosed
|
||||||
|
|
||||||
// If a coop close or breach was confirmed, jump straight to
|
// If a coop close was confirmed, jump straight to the fully
|
||||||
// the fully resolved state.
|
// resolved state.
|
||||||
case coopCloseTrigger, breachCloseTrigger:
|
case coopCloseTrigger:
|
||||||
nextState = StateFullyResolved
|
nextState = StateFullyResolved
|
||||||
|
|
||||||
|
case breachCloseTrigger:
|
||||||
|
nextContractState, err := c.checkLegacyBreach()
|
||||||
|
if nextContractState == StateError {
|
||||||
|
return nextContractState, closeTx, err
|
||||||
|
}
|
||||||
|
|
||||||
|
nextState = nextContractState
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Infof("ChannelArbitrator(%v): trigger %v moving from "+
|
log.Infof("ChannelArbitrator(%v): trigger %v moving from "+
|
||||||
@@ -1996,10 +2027,62 @@ func (c *ChannelArbitrator) prepContractResolutions(
|
|||||||
commitHash := contractResolutions.CommitHash
|
commitHash := contractResolutions.CommitHash
|
||||||
failureMsg := &lnwire.FailPermanentChannelFailure{}
|
failureMsg := &lnwire.FailPermanentChannelFailure{}
|
||||||
|
|
||||||
|
var htlcResolvers []ContractResolver
|
||||||
|
|
||||||
|
// We instantiate an anchor resolver if the commitment tx has an
|
||||||
|
// anchor.
|
||||||
|
if contractResolutions.AnchorResolution != nil {
|
||||||
|
anchorResolver := newAnchorResolver(
|
||||||
|
contractResolutions.AnchorResolution.AnchorSignDescriptor,
|
||||||
|
contractResolutions.AnchorResolution.CommitAnchor,
|
||||||
|
height, c.cfg.ChanPoint, resolverCfg,
|
||||||
|
)
|
||||||
|
htlcResolvers = append(htlcResolvers, anchorResolver)
|
||||||
|
}
|
||||||
|
|
||||||
|
// If this is a breach close, we'll create a breach resolver, determine
|
||||||
|
// the htlc's to fail back, and exit. This is done because the other
|
||||||
|
// steps taken for non-breach-closes do not matter for breach-closes.
|
||||||
|
if contractResolutions.BreachResolution != nil {
|
||||||
|
breachResolver := newBreachResolver(resolverCfg)
|
||||||
|
htlcResolvers = append(htlcResolvers, breachResolver)
|
||||||
|
|
||||||
|
// We'll use the CommitSet, we'll fail back all outgoing HTLC's
|
||||||
|
// that exist on either of the remote commitments. The map is
|
||||||
|
// used to deduplicate any shared htlc's.
|
||||||
|
remoteOutgoing := make(map[uint64]channeldb.HTLC)
|
||||||
|
for htlcSetKey, htlcs := range confCommitSet.HtlcSets {
|
||||||
|
if !htlcSetKey.IsRemote {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, htlc := range htlcs {
|
||||||
|
if htlc.Incoming {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
remoteOutgoing[htlc.HtlcIndex] = htlc
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now we'll loop over the map and create ResolutionMsgs for
|
||||||
|
// each of them.
|
||||||
|
for _, htlc := range remoteOutgoing {
|
||||||
|
failMsg := ResolutionMsg{
|
||||||
|
SourceChan: c.cfg.ShortChanID,
|
||||||
|
HtlcIndex: htlc.HtlcIndex,
|
||||||
|
Failure: failureMsg,
|
||||||
|
}
|
||||||
|
|
||||||
|
msgsToSend = append(msgsToSend, failMsg)
|
||||||
|
}
|
||||||
|
|
||||||
|
return htlcResolvers, msgsToSend, nil
|
||||||
|
}
|
||||||
|
|
||||||
// For each HTLC, we'll either act immediately, meaning we'll instantly
|
// For each HTLC, we'll either act immediately, meaning we'll instantly
|
||||||
// fail the HTLC, or we'll act only once the transaction has been
|
// fail the HTLC, or we'll act only once the transaction has been
|
||||||
// confirmed, in which case we'll need an HTLC resolver.
|
// confirmed, in which case we'll need an HTLC resolver.
|
||||||
var htlcResolvers []ContractResolver
|
|
||||||
for htlcAction, htlcs := range htlcActions {
|
for htlcAction, htlcs := range htlcActions {
|
||||||
switch htlcAction {
|
switch htlcAction {
|
||||||
|
|
||||||
@@ -2145,17 +2228,6 @@ func (c *ChannelArbitrator) prepContractResolutions(
|
|||||||
htlcResolvers = append(htlcResolvers, resolver)
|
htlcResolvers = append(htlcResolvers, resolver)
|
||||||
}
|
}
|
||||||
|
|
||||||
// We instantiate an anchor resolver if the commitmentment tx has an
|
|
||||||
// anchor.
|
|
||||||
if contractResolutions.AnchorResolution != nil {
|
|
||||||
anchorResolver := newAnchorResolver(
|
|
||||||
contractResolutions.AnchorResolution.AnchorSignDescriptor,
|
|
||||||
contractResolutions.AnchorResolution.CommitAnchor,
|
|
||||||
height, c.cfg.ChanPoint, resolverCfg,
|
|
||||||
)
|
|
||||||
htlcResolvers = append(htlcResolvers, anchorResolver)
|
|
||||||
}
|
|
||||||
|
|
||||||
return htlcResolvers, msgsToSend, nil
|
return htlcResolvers, msgsToSend, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2661,3 +2733,25 @@ func (c *ChannelArbitrator) channelAttendant(bestHeight int32) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// checkLegacyBreach returns StateFullyResolved if the channel was closed with
|
||||||
|
// a breach transaction before the channel arbitrator launched its own breach
|
||||||
|
// resolver. StateContractClosed is returned if this is a modern breach close
|
||||||
|
// with a breach resolver. StateError is returned if the log lookup failed.
|
||||||
|
func (c *ChannelArbitrator) checkLegacyBreach() (ArbitratorState, error) {
|
||||||
|
// A previous version of the channel arbitrator would make the breach
|
||||||
|
// close skip to StateFullyResolved. If there are no contract
|
||||||
|
// resolutions in the bolt arbitrator log, then this is an older breach
|
||||||
|
// close. Otherwise, if there are resolutions, the state should advance
|
||||||
|
// to StateContractClosed.
|
||||||
|
_, err := c.log.FetchContractResolutions()
|
||||||
|
if err == errNoResolutions {
|
||||||
|
// This is an older breach close still in the database.
|
||||||
|
return StateFullyResolved, nil
|
||||||
|
} else if err != nil {
|
||||||
|
return StateError, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is a modern breach close with resolvers.
|
||||||
|
return StateContractClosed, nil
|
||||||
|
}
|
||||||
Reference in new issue
Block a user