contractcourt+lnwallet: use state num instead of commit height when
outdated local state This commit fixes a bug that would cause us to not sweep our local output in case we force closed, then lost state or attempted recovery. The reason being that we would use or local commit height when deriving our scripts, which would be incorrect. Instead we use the extracted state number to derive the correct scripts, allowing us to sweep the output. Allthough being an unlikely scenario, we would leave money on chain in this case without any warning (since we would just end up with an empty delay script) and forget about the spend.
This commit is contained in:
parent
5bb8996162
commit
2a7a34ae10
@ -411,7 +411,7 @@ func (c *chainWatcher) handleUnknownLocalState(
|
|||||||
// though we won't be able to sweep HTLCs.
|
// though we won't be able to sweep HTLCs.
|
||||||
chainSet.commitSet.ConfCommitKey = &LocalHtlcSet
|
chainSet.commitSet.ConfCommitKey = &LocalHtlcSet
|
||||||
if err := c.dispatchLocalForceClose(
|
if err := c.dispatchLocalForceClose(
|
||||||
commitSpend, chainSet.localCommit, chainSet.commitSet,
|
commitSpend, broadcastStateNum, chainSet.commitSet,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
return false, fmt.Errorf("unable to handle local"+
|
return false, fmt.Errorf("unable to handle local"+
|
||||||
"close for chan_point=%v: %v",
|
"close for chan_point=%v: %v",
|
||||||
@ -564,7 +564,9 @@ func (c *chainWatcher) closeObserver(spendNtfn *chainntnfs.SpendEvent) {
|
|||||||
|
|
||||||
// We'll go on to check whether it could be our own commitment
|
// We'll go on to check whether it could be our own commitment
|
||||||
// that was published and know is confirmed.
|
// that was published and know is confirmed.
|
||||||
ok, err = c.handleKnownLocalState(commitSpend, chainSet)
|
ok, err = c.handleKnownLocalState(
|
||||||
|
commitSpend, broadcastStateNum, chainSet,
|
||||||
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("Unable to handle known local state: %v",
|
log.Errorf("Unable to handle known local state: %v",
|
||||||
err)
|
err)
|
||||||
@ -657,7 +659,8 @@ func (c *chainWatcher) closeObserver(spendNtfn *chainntnfs.SpendEvent) {
|
|||||||
// is known to us (the current state). If so we will act on this state using
|
// is known to us (the current state). If so we will act on this state using
|
||||||
// the passed chainSet. If this is not a known local state, false is returned.
|
// the passed chainSet. If this is not a known local state, false is returned.
|
||||||
func (c *chainWatcher) handleKnownLocalState(
|
func (c *chainWatcher) handleKnownLocalState(
|
||||||
commitSpend *chainntnfs.SpendDetail, chainSet *chainSet) (bool, error) {
|
commitSpend *chainntnfs.SpendDetail, broadcastStateNum uint64,
|
||||||
|
chainSet *chainSet) (bool, error) {
|
||||||
|
|
||||||
// If the channel is recovered, we won't have a local commit to check
|
// If the channel is recovered, we won't have a local commit to check
|
||||||
// against, so immediately return.
|
// against, so immediately return.
|
||||||
@ -675,7 +678,7 @@ func (c *chainWatcher) handleKnownLocalState(
|
|||||||
|
|
||||||
chainSet.commitSet.ConfCommitKey = &LocalHtlcSet
|
chainSet.commitSet.ConfCommitKey = &LocalHtlcSet
|
||||||
if err := c.dispatchLocalForceClose(
|
if err := c.dispatchLocalForceClose(
|
||||||
commitSpend, chainSet.localCommit, chainSet.commitSet,
|
commitSpend, broadcastStateNum, chainSet.commitSet,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
return false, fmt.Errorf("unable to handle local"+
|
return false, fmt.Errorf("unable to handle local"+
|
||||||
"close for chan_point=%v: %v",
|
"close for chan_point=%v: %v",
|
||||||
@ -945,14 +948,14 @@ func (c *chainWatcher) dispatchCooperativeClose(commitSpend *chainntnfs.SpendDet
|
|||||||
// dispatchLocalForceClose processes a unilateral close by us being confirmed.
|
// dispatchLocalForceClose processes a unilateral close by us being confirmed.
|
||||||
func (c *chainWatcher) dispatchLocalForceClose(
|
func (c *chainWatcher) dispatchLocalForceClose(
|
||||||
commitSpend *chainntnfs.SpendDetail,
|
commitSpend *chainntnfs.SpendDetail,
|
||||||
localCommit channeldb.ChannelCommitment, commitSet CommitSet) error {
|
stateNum uint64, commitSet CommitSet) error {
|
||||||
|
|
||||||
log.Infof("Local unilateral close of ChannelPoint(%v) "+
|
log.Infof("Local unilateral close of ChannelPoint(%v) "+
|
||||||
"detected", c.cfg.chanState.FundingOutpoint)
|
"detected", c.cfg.chanState.FundingOutpoint)
|
||||||
|
|
||||||
forceClose, err := lnwallet.NewLocalForceCloseSummary(
|
forceClose, err := lnwallet.NewLocalForceCloseSummary(
|
||||||
c.cfg.chanState, c.cfg.signer,
|
c.cfg.chanState, c.cfg.signer,
|
||||||
commitSpend.SpendingTx, localCommit,
|
commitSpend.SpendingTx, stateNum,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -6038,7 +6038,7 @@ func (lc *LightningChannel) ForceClose() (*LocalForceCloseSummary, error) {
|
|||||||
localCommitment := lc.channelState.LocalCommitment
|
localCommitment := lc.channelState.LocalCommitment
|
||||||
summary, err := NewLocalForceCloseSummary(
|
summary, err := NewLocalForceCloseSummary(
|
||||||
lc.channelState, lc.Signer, commitTx,
|
lc.channelState, lc.Signer, commitTx,
|
||||||
localCommitment,
|
localCommitment.CommitHeight,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -6054,8 +6054,8 @@ func (lc *LightningChannel) ForceClose() (*LocalForceCloseSummary, error) {
|
|||||||
// NewLocalForceCloseSummary generates a LocalForceCloseSummary from the given
|
// NewLocalForceCloseSummary generates a LocalForceCloseSummary from the given
|
||||||
// channel state. The passed commitTx must be a fully signed commitment
|
// channel state. The passed commitTx must be a fully signed commitment
|
||||||
// transaction corresponding to localCommit.
|
// transaction corresponding to localCommit.
|
||||||
func NewLocalForceCloseSummary(chanState *channeldb.OpenChannel, signer input.Signer,
|
func NewLocalForceCloseSummary(chanState *channeldb.OpenChannel,
|
||||||
commitTx *wire.MsgTx, localCommit channeldb.ChannelCommitment) (
|
signer input.Signer, commitTx *wire.MsgTx, stateNum uint64) (
|
||||||
*LocalForceCloseSummary, error) {
|
*LocalForceCloseSummary, error) {
|
||||||
|
|
||||||
// Re-derive the original pkScript for to-self output within the
|
// Re-derive the original pkScript for to-self output within the
|
||||||
@ -6063,9 +6063,11 @@ func NewLocalForceCloseSummary(chanState *channeldb.OpenChannel, signer input.Si
|
|||||||
// output in the commitment transaction and potentially for creating
|
// output in the commitment transaction and potentially for creating
|
||||||
// the sign descriptor.
|
// the sign descriptor.
|
||||||
csvTimeout := uint32(chanState.LocalChanCfg.CsvDelay)
|
csvTimeout := uint32(chanState.LocalChanCfg.CsvDelay)
|
||||||
revocation, err := chanState.RevocationProducer.AtIndex(
|
|
||||||
localCommit.CommitHeight,
|
// We use the passed state num to derive our scripts, since in case
|
||||||
)
|
// this is after recovery, our latest channels state might not be up to
|
||||||
|
// date.
|
||||||
|
revocation, err := chanState.RevocationProducer.AtIndex(stateNum)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -6090,8 +6092,8 @@ func NewLocalForceCloseSummary(chanState *channeldb.OpenChannel, signer input.Si
|
|||||||
// We'll return the details of this output to the caller so they can
|
// We'll return the details of this output to the caller so they can
|
||||||
// sweep it once it's mature.
|
// sweep it once it's mature.
|
||||||
var (
|
var (
|
||||||
delayIndex uint32
|
delayIndex uint32
|
||||||
delayScript []byte
|
delayOut *wire.TxOut
|
||||||
)
|
)
|
||||||
for i, txOut := range commitTx.TxOut {
|
for i, txOut := range commitTx.TxOut {
|
||||||
if !bytes.Equal(payToUsScriptHash, txOut.PkScript) {
|
if !bytes.Equal(payToUsScriptHash, txOut.PkScript) {
|
||||||
@ -6099,7 +6101,7 @@ func NewLocalForceCloseSummary(chanState *channeldb.OpenChannel, signer input.Si
|
|||||||
}
|
}
|
||||||
|
|
||||||
delayIndex = uint32(i)
|
delayIndex = uint32(i)
|
||||||
delayScript = txOut.PkScript
|
delayOut = txOut
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -6110,8 +6112,8 @@ func NewLocalForceCloseSummary(chanState *channeldb.OpenChannel, signer input.Si
|
|||||||
// If the output is non-existent (dust), have the sign descriptor be
|
// If the output is non-existent (dust), have the sign descriptor be
|
||||||
// nil.
|
// nil.
|
||||||
var commitResolution *CommitOutputResolution
|
var commitResolution *CommitOutputResolution
|
||||||
if len(delayScript) != 0 {
|
if delayOut != nil {
|
||||||
localBalance := localCommit.LocalBalance
|
localBalance := delayOut.Value
|
||||||
commitResolution = &CommitOutputResolution{
|
commitResolution = &CommitOutputResolution{
|
||||||
SelfOutPoint: wire.OutPoint{
|
SelfOutPoint: wire.OutPoint{
|
||||||
Hash: commitTx.TxHash(),
|
Hash: commitTx.TxHash(),
|
||||||
@ -6122,8 +6124,8 @@ func NewLocalForceCloseSummary(chanState *channeldb.OpenChannel, signer input.Si
|
|||||||
SingleTweak: keyRing.LocalCommitKeyTweak,
|
SingleTweak: keyRing.LocalCommitKeyTweak,
|
||||||
WitnessScript: selfScript,
|
WitnessScript: selfScript,
|
||||||
Output: &wire.TxOut{
|
Output: &wire.TxOut{
|
||||||
PkScript: delayScript,
|
PkScript: delayOut.PkScript,
|
||||||
Value: int64(localBalance.ToSatoshis()),
|
Value: localBalance,
|
||||||
},
|
},
|
||||||
HashType: txscript.SigHashAll,
|
HashType: txscript.SigHashAll,
|
||||||
},
|
},
|
||||||
@ -6133,8 +6135,11 @@ func NewLocalForceCloseSummary(chanState *channeldb.OpenChannel, signer input.Si
|
|||||||
|
|
||||||
// Once the delay output has been found (if it exists), then we'll also
|
// Once the delay output has been found (if it exists), then we'll also
|
||||||
// need to create a series of sign descriptors for any lingering
|
// need to create a series of sign descriptors for any lingering
|
||||||
// outgoing HTLC's that we'll need to claim as well.
|
// outgoing HTLC's that we'll need to claim as well. If this is after
|
||||||
|
// recovery there is not much we can do with HTLCs, so we'll always
|
||||||
|
// use what we have in our latest state when extracting resolutions.
|
||||||
txHash := commitTx.TxHash()
|
txHash := commitTx.TxHash()
|
||||||
|
localCommit := chanState.LocalCommitment
|
||||||
htlcResolutions, err := extractHtlcResolutions(
|
htlcResolutions, err := extractHtlcResolutions(
|
||||||
chainfee.SatPerKWeight(localCommit.FeePerKw), true, signer,
|
chainfee.SatPerKWeight(localCommit.FeePerKw), true, signer,
|
||||||
localCommit.Htlcs, keyRing, &chanState.LocalChanCfg,
|
localCommit.Htlcs, keyRing, &chanState.LocalChanCfg,
|
||||||
|
Loading…
Reference in New Issue
Block a user