diff --git a/lnwallet/channel.go b/lnwallet/channel.go index e53ef9c5..2bb049f0 100644 --- a/lnwallet/channel.go +++ b/lnwallet/channel.go @@ -3531,7 +3531,14 @@ func (lc *LightningChannel) ProcessChanSyncMsg( // it. // 3. We didn't get the last RevokeAndAck message they sent, so they'll // re-send it. -func ChanSyncMsg(c *channeldb.OpenChannel) (*lnwire.ChannelReestablish, error) { +// +// The isRestoredChan bool indicates if we need to craft a chan sync message +// for a channel that's been restored. If this is a restored channel, then +// we'll modify our typical chan sync message to ensure they force close even +// if we're on the very first state. +func ChanSyncMsg(c *channeldb.OpenChannel, + isRestoredChan bool) (*lnwire.ChannelReestablish, error) { + c.Lock() defer c.Unlock() @@ -3572,6 +3579,13 @@ func ChanSyncMsg(c *channeldb.OpenChannel) (*lnwire.ChannelReestablish, error) { return nil, err } + // If we've restored this channel, then we'll purposefully give them an + // invalid LocalUnrevokedCommitPoint so they'll force close the channel + // allowing us to sweep our funds. + if isRestoredChan { + currentCommitSecret[0] ^= 1 + } + return &lnwire.ChannelReestablish{ ChanID: lnwire.NewChanIDFromOutPoint( &c.FundingOutpoint, @@ -3632,7 +3646,7 @@ func (lc *LightningChannel) computeView(view *htlcView, remoteChain bool, // will remove their corresponding added HTLCs. The resulting filtered // view will only have Add entries left, making it easy to compare the // channel constraints to the final commitment state. If any fee - // updates are found in the logs, the comitment fee rate should be + // updates are found in the logs, the commitment fee rate should be // changed, so we'll also set the feePerKw to this new value. filteredHTLCView := lc.evaluateHTLCView(view, &ourBalance, &theirBalance, nextHeight, remoteChain, updateState) @@ -5086,12 +5100,14 @@ func NewUnilateralCloseSummary(chanState *channeldb.OpenChannel, signer input.Si // Next, we'll obtain HTLC resolutions for all the outgoing HTLC's we // had on their commitment transaction. htlcResolutions, err := extractHtlcResolutions( - SatPerKWeight(remoteCommit.FeePerKw), false, signer, remoteCommit.Htlcs, - keyRing, &chanState.LocalChanCfg, &chanState.RemoteChanCfg, - *commitSpend.SpenderTxHash, pCache, + SatPerKWeight(remoteCommit.FeePerKw), false, signer, + remoteCommit.Htlcs, keyRing, &chanState.LocalChanCfg, + &chanState.RemoteChanCfg, *commitSpend.SpenderTxHash, + pCache, ) if err != nil { - return nil, fmt.Errorf("unable to create htlc resolutions: %v", err) + return nil, fmt.Errorf("unable to create htlc "+ + "resolutions: %v", err) } commitTxBroadcast := commitSpend.SpendingTx @@ -5101,7 +5117,8 @@ func NewUnilateralCloseSummary(chanState *channeldb.OpenChannel, signer input.Si // transaction. selfP2WKH, err := input.CommitScriptUnencumbered(keyRing.NoDelayKey) if err != nil { - return nil, fmt.Errorf("unable to create self commit script: %v", err) + return nil, fmt.Errorf("unable to create self commit "+ + "script: %v", err) } var ( @@ -5159,7 +5176,10 @@ func NewUnilateralCloseSummary(chanState *channeldb.OpenChannel, signer input.Si } // Attempt to add a channel sync message to the close summary. - chanSync, err := ChanSyncMsg(chanState) + chanSync, err := ChanSyncMsg( + chanState, + chanState.HasChanStatus(channeldb.ChanStatusRestored), + ) if err != nil { walletLog.Errorf("ChannelPoint(%v): unable to create channel sync "+ "message: %v", chanState.FundingOutpoint, err) diff --git a/lnwallet/channel_test.go b/lnwallet/channel_test.go index 1093ff04..067e6258 100644 --- a/lnwallet/channel_test.go +++ b/lnwallet/channel_test.go @@ -2532,7 +2532,7 @@ func assertNoChanSyncNeeded(t *testing.T, aliceChannel *LightningChannel, _, _, line, _ := runtime.Caller(1) - aliceChanSyncMsg, err := ChanSyncMsg(aliceChannel.channelState) + aliceChanSyncMsg, err := ChanSyncMsg(aliceChannel.channelState, false) if err != nil { t.Fatalf("line #%v: unable to produce chan sync msg: %v", line, err) @@ -2547,7 +2547,7 @@ func assertNoChanSyncNeeded(t *testing.T, aliceChannel *LightningChannel, "instead wants to send: %v", line, spew.Sdump(bobMsgsToSend)) } - bobChanSyncMsg, err := ChanSyncMsg(bobChannel.channelState) + bobChanSyncMsg, err := ChanSyncMsg(bobChannel.channelState, false) if err != nil { t.Fatalf("line #%v: unable to produce chan sync msg: %v", line, err) @@ -2780,11 +2780,11 @@ func TestChanSyncOweCommitment(t *testing.T) { // Bob doesn't get this message so upon reconnection, they need to // synchronize. Alice should conclude that she owes Bob a commitment, // while Bob should think he's properly synchronized. - aliceSyncMsg, err := ChanSyncMsg(aliceChannel.channelState) + aliceSyncMsg, err := ChanSyncMsg(aliceChannel.channelState, false) if err != nil { t.Fatalf("unable to produce chan sync msg: %v", err) } - bobSyncMsg, err := ChanSyncMsg(bobChannel.channelState) + bobSyncMsg, err := ChanSyncMsg(bobChannel.channelState, false) if err != nil { t.Fatalf("unable to produce chan sync msg: %v", err) } @@ -3093,11 +3093,11 @@ func TestChanSyncOweRevocation(t *testing.T) { // If we fetch the channel sync messages at this state, then Alice // should report that she owes Bob a revocation message, while Bob // thinks they're fully in sync. - aliceSyncMsg, err := ChanSyncMsg(aliceChannel.channelState) + aliceSyncMsg, err := ChanSyncMsg(aliceChannel.channelState, false) if err != nil { t.Fatalf("unable to produce chan sync msg: %v", err) } - bobSyncMsg, err := ChanSyncMsg(bobChannel.channelState) + bobSyncMsg, err := ChanSyncMsg(bobChannel.channelState, false) if err != nil { t.Fatalf("unable to produce chan sync msg: %v", err) } @@ -3261,11 +3261,11 @@ func TestChanSyncOweRevocationAndCommit(t *testing.T) { // If we now attempt to resync, then Alice should conclude that she // doesn't need any further updates, while Bob concludes that he needs // to re-send both his revocation and commit sig message. - aliceSyncMsg, err := ChanSyncMsg(aliceChannel.channelState) + aliceSyncMsg, err := ChanSyncMsg(aliceChannel.channelState, false) if err != nil { t.Fatalf("unable to produce chan sync msg: %v", err) } - bobSyncMsg, err := ChanSyncMsg(bobChannel.channelState) + bobSyncMsg, err := ChanSyncMsg(bobChannel.channelState, false) if err != nil { t.Fatalf("unable to produce chan sync msg: %v", err) } @@ -3470,11 +3470,11 @@ func TestChanSyncOweRevocationAndCommitForceTransition(t *testing.T) { // Now if we attempt to synchronize states at this point, Alice should // detect that she owes nothing, while Bob should re-send both his // RevokeAndAck as well as his commitment message. - aliceSyncMsg, err := ChanSyncMsg(aliceChannel.channelState) + aliceSyncMsg, err := ChanSyncMsg(aliceChannel.channelState, false) if err != nil { t.Fatalf("unable to produce chan sync msg: %v", err) } - bobSyncMsg, err := ChanSyncMsg(bobChannel.channelState) + bobSyncMsg, err := ChanSyncMsg(bobChannel.channelState, false) if err != nil { t.Fatalf("unable to produce chan sync msg: %v", err) } @@ -3674,11 +3674,11 @@ func TestChanSyncFailure(t *testing.T) { assertLocalDataLoss := func(aliceOld *LightningChannel) { t.Helper() - aliceSyncMsg, err := ChanSyncMsg(aliceOld.channelState) + aliceSyncMsg, err := ChanSyncMsg(aliceOld.channelState, false) if err != nil { t.Fatalf("unable to produce chan sync msg: %v", err) } - bobSyncMsg, err := ChanSyncMsg(bobChannel.channelState) + bobSyncMsg, err := ChanSyncMsg(bobChannel.channelState, false) if err != nil { t.Fatalf("unable to produce chan sync msg: %v", err) } @@ -3752,7 +3752,7 @@ func TestChanSyncFailure(t *testing.T) { // If we remove the recovery options from Bob's message, Alice cannot // tell if she lost state, since Bob might be lying. She still should // be able to detect that chains cannot be synced. - bobSyncMsg, err := ChanSyncMsg(bobChannel.channelState) + bobSyncMsg, err := ChanSyncMsg(bobChannel.channelState, false) if err != nil { t.Fatalf("unable to produce chan sync msg: %v", err) } @@ -3766,7 +3766,7 @@ func TestChanSyncFailure(t *testing.T) { // If Bob lies about the NextLocalCommitHeight, making it greater than // what Alice expect, she cannot tell for sure whether she lost state, // but should detect the desync. - bobSyncMsg, err = ChanSyncMsg(bobChannel.channelState) + bobSyncMsg, err = ChanSyncMsg(bobChannel.channelState, false) if err != nil { t.Fatalf("unable to produce chan sync msg: %v", err) } @@ -3779,7 +3779,7 @@ func TestChanSyncFailure(t *testing.T) { // If Bob's NextLocalCommitHeight is lower than what Alice expects, Bob // probably lost state. - bobSyncMsg, err = ChanSyncMsg(bobChannel.channelState) + bobSyncMsg, err = ChanSyncMsg(bobChannel.channelState, false) if err != nil { t.Fatalf("unable to produce chan sync msg: %v", err) } @@ -3792,7 +3792,7 @@ func TestChanSyncFailure(t *testing.T) { // If Alice and Bob's states are in sync, but Bob is sending the wrong // LocalUnrevokedCommitPoint, Alice should detect this. - bobSyncMsg, err = ChanSyncMsg(bobChannel.channelState) + bobSyncMsg, err = ChanSyncMsg(bobChannel.channelState, false) if err != nil { t.Fatalf("unable to produce chan sync msg: %v", err) } @@ -3821,7 +3821,7 @@ func TestChanSyncFailure(t *testing.T) { // when there's a pending remote commit. halfAdvance() - bobSyncMsg, err = ChanSyncMsg(bobChannel.channelState) + bobSyncMsg, err = ChanSyncMsg(bobChannel.channelState, false) if err != nil { t.Fatalf("unable to produce chan sync msg: %v", err) } @@ -3909,11 +3909,11 @@ func TestChannelRetransmissionFeeUpdate(t *testing.T) { // Bob doesn't get this message so upon reconnection, they need to // synchronize. Alice should conclude that she owes Bob a commitment, // while Bob should think he's properly synchronized. - aliceSyncMsg, err := ChanSyncMsg(aliceChannel.channelState) + aliceSyncMsg, err := ChanSyncMsg(aliceChannel.channelState, false) if err != nil { t.Fatalf("unable to produce chan sync msg: %v", err) } - bobSyncMsg, err := ChanSyncMsg(bobChannel.channelState) + bobSyncMsg, err := ChanSyncMsg(bobChannel.channelState, false) if err != nil { t.Fatalf("unable to produce chan sync msg: %v", err) } @@ -4357,11 +4357,11 @@ func TestChanSyncInvalidLastSecret(t *testing.T) { } // Next, we'll produce the ChanSync messages for both parties. - aliceChanSync, err := ChanSyncMsg(aliceChannel.channelState) + aliceChanSync, err := ChanSyncMsg(aliceChannel.channelState, false) if err != nil { t.Fatalf("unable to generate chan sync msg: %v", err) } - bobChanSync, err := ChanSyncMsg(bobChannel.channelState) + bobChanSync, err := ChanSyncMsg(bobChannel.channelState, false) if err != nil { t.Fatalf("unable to generate chan sync msg: %v", err) }