contractcourt/chain_watcher: handleUnknownLocalState
Similar to what we did for other states, we extract handling of acting on a local future state into its own method.
This commit is contained in:
parent
93d917d82a
commit
5bb8996162
@ -17,7 +17,6 @@ import (
|
|||||||
"github.com/lightningnetwork/lnd/channeldb"
|
"github.com/lightningnetwork/lnd/channeldb"
|
||||||
"github.com/lightningnetwork/lnd/input"
|
"github.com/lightningnetwork/lnd/input"
|
||||||
"github.com/lightningnetwork/lnd/lnwallet"
|
"github.com/lightningnetwork/lnd/lnwallet"
|
||||||
"github.com/lightningnetwork/lnd/shachain"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -324,18 +323,27 @@ func (c *chainWatcher) SubscribeChannelEvents() *ChainEventSubscription {
|
|||||||
return sub
|
return sub
|
||||||
}
|
}
|
||||||
|
|
||||||
// isOurCommitment returns true if the passed commitSpend is a spend of the
|
// handleUnknownLocalState checks whether the passed spend _could_ be a local
|
||||||
// funding transaction using our commitment transaction (a local force close).
|
// state that for some reason is unknown to us. This could be a state published
|
||||||
// In order to do this in a state agnostic manner, we'll make our decisions
|
// by us before we lost state, which we will try to sweep. Or it could be one
|
||||||
// based off of only the set of outputs included.
|
// of our revoked states that somehow made it to the chain. If that's the case
|
||||||
func isOurCommitment(localChanCfg, remoteChanCfg channeldb.ChannelConfig,
|
// we cannot really hope that we'll be able to get our money back, but we'll
|
||||||
|
// try to sweep it anyway. If this is not an unknown local state, false is
|
||||||
|
// returned.
|
||||||
|
func (c *chainWatcher) handleUnknownLocalState(
|
||||||
commitSpend *chainntnfs.SpendDetail, broadcastStateNum uint64,
|
commitSpend *chainntnfs.SpendDetail, broadcastStateNum uint64,
|
||||||
revocationProducer shachain.Producer,
|
chainSet *chainSet) (bool, error) {
|
||||||
chanType channeldb.ChannelType) (bool, error) {
|
|
||||||
|
// If the spend was a local commitment, at this point it must either be
|
||||||
|
// a past state (we breached!) or a future state (we lost state!). In
|
||||||
|
// either case, the only thing we can do is to attempt to sweep what is
|
||||||
|
// there.
|
||||||
|
|
||||||
// First, we'll re-derive our commitment point for this state since
|
// First, we'll re-derive our commitment point for this state since
|
||||||
// this is what we use to randomize each of the keys for this state.
|
// this is what we use to randomize each of the keys for this state.
|
||||||
commitSecret, err := revocationProducer.AtIndex(broadcastStateNum)
|
commitSecret, err := c.cfg.chanState.RevocationProducer.AtIndex(
|
||||||
|
broadcastStateNum,
|
||||||
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
@ -345,13 +353,14 @@ func isOurCommitment(localChanCfg, remoteChanCfg channeldb.ChannelConfig,
|
|||||||
// and remote keys for this state. We use our point as only we can
|
// and remote keys for this state. We use our point as only we can
|
||||||
// revoke our own commitment.
|
// revoke our own commitment.
|
||||||
commitKeyRing := lnwallet.DeriveCommitmentKeys(
|
commitKeyRing := lnwallet.DeriveCommitmentKeys(
|
||||||
commitPoint, true, chanType, &localChanCfg, &remoteChanCfg,
|
commitPoint, true, c.cfg.chanState.ChanType,
|
||||||
|
&c.cfg.chanState.LocalChanCfg, &c.cfg.chanState.RemoteChanCfg,
|
||||||
)
|
)
|
||||||
|
|
||||||
// With the keys derived, we'll construct the remote script that'll be
|
// With the keys derived, we'll construct the remote script that'll be
|
||||||
// present if they have a non-dust balance on the commitment.
|
// present if they have a non-dust balance on the commitment.
|
||||||
remoteScript, _, err := lnwallet.CommitScriptToRemote(
|
remoteScript, _, err := lnwallet.CommitScriptToRemote(
|
||||||
chanType, commitKeyRing.ToRemoteKey,
|
c.cfg.chanState.ChanType, commitKeyRing.ToRemoteKey,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
@ -361,12 +370,13 @@ func isOurCommitment(localChanCfg, remoteChanCfg channeldb.ChannelConfig,
|
|||||||
// the remote party allowing them to claim this output before the CSV
|
// the remote party allowing them to claim this output before the CSV
|
||||||
// delay if we breach.
|
// delay if we breach.
|
||||||
localScript, err := input.CommitScriptToSelf(
|
localScript, err := input.CommitScriptToSelf(
|
||||||
uint32(localChanCfg.CsvDelay), commitKeyRing.ToLocalKey,
|
uint32(c.cfg.chanState.LocalChanCfg.CsvDelay),
|
||||||
commitKeyRing.RevocationKey,
|
commitKeyRing.ToLocalKey, commitKeyRing.RevocationKey,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
localPkScript, err := input.WitnessScriptHash(localScript)
|
localPkScript, err := input.WitnessScriptHash(localScript)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
@ -375,21 +385,40 @@ func isOurCommitment(localChanCfg, remoteChanCfg channeldb.ChannelConfig,
|
|||||||
// With all our scripts assembled, we'll examine the outputs of the
|
// With all our scripts assembled, we'll examine the outputs of the
|
||||||
// commitment transaction to determine if this is a local force close
|
// commitment transaction to determine if this is a local force close
|
||||||
// or not.
|
// or not.
|
||||||
|
ourCommit := false
|
||||||
for _, output := range commitSpend.SpendingTx.TxOut {
|
for _, output := range commitSpend.SpendingTx.TxOut {
|
||||||
pkScript := output.PkScript
|
pkScript := output.PkScript
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
case bytes.Equal(localPkScript, pkScript):
|
case bytes.Equal(localPkScript, pkScript):
|
||||||
return true, nil
|
ourCommit = true
|
||||||
|
|
||||||
case bytes.Equal(remoteScript.PkScript, pkScript):
|
case bytes.Equal(remoteScript.PkScript, pkScript):
|
||||||
return true, nil
|
ourCommit = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If neither of these scripts are present, then it isn't a local force
|
// If the script is not present, this cannot be our commit.
|
||||||
// close.
|
if !ourCommit {
|
||||||
return false, nil
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Warnf("Detected local unilateral close of unknown state %v "+
|
||||||
|
"(our state=%v)", broadcastStateNum,
|
||||||
|
chainSet.localCommit.CommitHeight)
|
||||||
|
|
||||||
|
// If this is our commitment transaction, then we try to act even
|
||||||
|
// though we won't be able to sweep HTLCs.
|
||||||
|
chainSet.commitSet.ConfCommitKey = &LocalHtlcSet
|
||||||
|
if err := c.dispatchLocalForceClose(
|
||||||
|
commitSpend, chainSet.localCommit, chainSet.commitSet,
|
||||||
|
); err != nil {
|
||||||
|
return false, fmt.Errorf("unable to handle local"+
|
||||||
|
"close for chan_point=%v: %v",
|
||||||
|
c.cfg.chanState.FundingOutpoint, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// chainSet includes all the information we need to dispatch a channel close
|
// chainSet includes all the information we need to dispatch a channel close
|
||||||
@ -562,39 +591,6 @@ func (c *chainWatcher) closeObserver(spendNtfn *chainntnfs.SpendEvent) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Based on the output scripts within this commitment, we'll
|
|
||||||
// determine if this is our commitment transaction or not (a
|
|
||||||
// self force close).
|
|
||||||
isOurCommit, err := isOurCommitment(
|
|
||||||
c.cfg.chanState.LocalChanCfg,
|
|
||||||
c.cfg.chanState.RemoteChanCfg, commitSpend,
|
|
||||||
broadcastStateNum, c.cfg.chanState.RevocationProducer,
|
|
||||||
c.cfg.chanState.ChanType,
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
log.Errorf("unable to determine self commit for "+
|
|
||||||
"chan_point=%v: %v",
|
|
||||||
c.cfg.chanState.FundingOutpoint, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// If this is our commitment transaction, then we can exit here
|
|
||||||
// as we don't have any further processing we need to do (we
|
|
||||||
// can't cheat ourselves :p).
|
|
||||||
if isOurCommit {
|
|
||||||
chainSet.commitSet.ConfCommitKey = &LocalHtlcSet
|
|
||||||
|
|
||||||
if err := c.dispatchLocalForceClose(
|
|
||||||
commitSpend, chainSet.localCommit,
|
|
||||||
chainSet.commitSet,
|
|
||||||
); err != nil {
|
|
||||||
log.Errorf("unable to handle local"+
|
|
||||||
"close for chan_point=%v: %v",
|
|
||||||
c.cfg.chanState.FundingOutpoint, err)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Next, we'll check to see if this is a cooperative channel
|
// Next, we'll check to see if this is a cooperative channel
|
||||||
// closure or not. This is characterized by having an input
|
// closure or not. This is characterized by having an input
|
||||||
// sequence number that's finalized. This won't happen with
|
// sequence number that's finalized. This won't happen with
|
||||||
@ -610,9 +606,26 @@ func (c *chainWatcher) closeObserver(spendNtfn *chainntnfs.SpendEvent) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Warnf("Unprompted commitment broadcast for "+
|
log.Warnf("Unknown commitment broadcast for "+
|
||||||
"ChannelPoint(%v) ", c.cfg.chanState.FundingOutpoint)
|
"ChannelPoint(%v) ", c.cfg.chanState.FundingOutpoint)
|
||||||
|
|
||||||
|
// We'll try to recover as best as possible from losing state.
|
||||||
|
// We first check if this was a local unknown state. This could
|
||||||
|
// happen if we force close, then lose state or attempt
|
||||||
|
// recovery before the commitment confirms.
|
||||||
|
ok, err = c.handleUnknownLocalState(
|
||||||
|
commitSpend, broadcastStateNum, chainSet,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("Unable to handle known local state: %v",
|
||||||
|
err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// Since it was neither a known remote state, nor a local state
|
// Since it was neither a known remote state, nor a local state
|
||||||
// that was published, it most likely mean we lost state and
|
// that was published, it most likely mean we lost state and
|
||||||
// the remote node closed. In this case we must start the DLP
|
// the remote node closed. In this case we must start the DLP
|
||||||
|
Loading…
Reference in New Issue
Block a user