From a65539661670faf3a037c2f59e1f5c22c12d70ef Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Mon, 13 Nov 2017 22:40:45 -0800 Subject: [PATCH] lnwallet: add optional data loss support to ChanSyncMsg() In this commit we extend the set of fields populated within the returned lnwire.ChannelReestablish to populate the optional data loss fields. This entails included the commitment secret of the most recently revoked remote commitment transaction and also our current unrevoked commitment point. --- lnwallet/channel.go | 35 +++++++++++++++++-- lnwallet/channel_test.go | 75 ++++++++++++++++++++++++++++++---------- 2 files changed, 89 insertions(+), 21 deletions(-) diff --git a/lnwallet/channel.go b/lnwallet/channel.go index 88173f69..e52efc76 100644 --- a/lnwallet/channel.go +++ b/lnwallet/channel.go @@ -3139,26 +3139,55 @@ func (lc *LightningChannel) ProcessChanSyncMsg(msg *lnwire.ChannelReestablish) ( // it. // 3. We didn't get the last RevokeAndAck message they sent, so they'll // re-send it. -func (lc *LightningChannel) ChanSyncMsg() *lnwire.ChannelReestablish { +func (lc *LightningChannel) ChanSyncMsg() (*lnwire.ChannelReestablish, error) { // The remote commitment height that we we'll send in the // ChannelReestablish message is our current commitment height plus // one. If the receiver thinks that our commitment height is actually // *equal* to this value, then they'll re-send the last commitment that // they sent but we never fully processed. - nextLocalCommitHeight := lc.localCommitChain.tip().height + 1 + localHeight := lc.localCommitChain.tip().height + nextLocalCommitHeight := localHeight + 1 // The second value we'll send is the height of the remote commitment // from our PoV. If the receiver thinks that their height is actually // *one plus* this value, then they'll re-send their last revocation. remoteChainTipHeight := lc.remoteCommitChain.tail().height + // If this channel has undergone a commitment update, then in order to + // prove to the remote party our knwoeldge of their prior commitment + // state, we'll also send over the last commitment secret that the + // remote party sent. + var lastCommitSecret [32]byte + if remoteChainTipHeight != 0 { + remoteSecret, err := lc.channelState.RevocationStore.LookUp( + remoteChainTipHeight - 1, + ) + if err != nil { + return nil, err + } + lastCommitSecret = [32]byte(*remoteSecret) + } + + // Additionally, we'll send over the current unrevoked commitment on + // our local commitment transaction. + currentCommitSecret, err := lc.channelState.RevocationProducer.AtIndex( + localHeight, + ) + if err != nil { + return nil, err + } + return &lnwire.ChannelReestablish{ ChanID: lnwire.NewChanIDFromOutPoint( &lc.channelState.FundingOutpoint, ), NextLocalCommitHeight: nextLocalCommitHeight, RemoteCommitTailHeight: remoteChainTipHeight, - } + LastRemoteCommitSecret: lastCommitSecret, + LocalUnrevokedCommitPoint: ComputeCommitmentPoint( + currentCommitSecret[:], + ), + }, nil } // validateCommitmentSanity is used to validate that on current state the commitment diff --git a/lnwallet/channel_test.go b/lnwallet/channel_test.go index f48a0a0a..15692609 100644 --- a/lnwallet/channel_test.go +++ b/lnwallet/channel_test.go @@ -2378,24 +2378,37 @@ func TestAddHTLCNegativeBalance(t *testing.T) { func assertNoChanSyncNeeded(t *testing.T, aliceChannel *LightningChannel, bobChannel *LightningChannel) { - aliceChanSyncMsg := aliceChannel.ChanSyncMsg() + _, _, line, _ := runtime.Caller(1) + + aliceChanSyncMsg, err := aliceChannel.ChanSyncMsg() + if err != nil { + t.Fatalf("line #%v: unable to produce chan sync msg: %v", + line, err) + } bobMsgsToSend, err := bobChannel.ProcessChanSyncMsg(aliceChanSyncMsg) if err != nil { - t.Fatalf("unable to process ChannelReestablish msg: %v", err) + t.Fatalf("line #%v: unable to process ChannelReestablish "+ + "msg: %v", line, err) } if len(bobMsgsToSend) != 0 { - t.Fatalf("bob shouldn't have to send any messages, instead wants "+ - "to send: %v", spew.Sdump(bobMsgsToSend)) + t.Fatalf("line #%v: bob shouldn't have to send any messages, "+ + "instead wants to send: %v", line, spew.Sdump(bobMsgsToSend)) } - bobChanSyncMsg := bobChannel.ChanSyncMsg() + bobChanSyncMsg, err := bobChannel.ChanSyncMsg() + if err != nil { + t.Fatalf("line #%v: unable to produce chan sync msg: %v", + line, err) + } aliceMsgsToSend, err := aliceChannel.ProcessChanSyncMsg(bobChanSyncMsg) if err != nil { - t.Fatalf("unable to process ChannelReestablish msg: %v", err) + t.Fatalf("line #%v: unable to process ChannelReestablish "+ + "msg: %v", line, err) } if len(bobMsgsToSend) != 0 { - t.Fatalf("alice shouldn't have to send any messages, instead wants "+ - "to send: %v", spew.Sdump(aliceMsgsToSend)) + t.Fatalf("line #%v: alice shouldn't have to send any "+ + "messages, instead wants to send: %v", line, + spew.Sdump(aliceMsgsToSend)) } } @@ -2489,7 +2502,9 @@ func TestChanSyncFullySynced(t *testing.T) { assertNoChanSyncNeeded(t, aliceChannelNew, bobChannelNew) } -// restartChannel... +// restartChannel reads the passe channel from disk, and returns a newly +// initialized instance. This simulates one party restarting and losing their +// in memory state. func restartChannel(channelOld *LightningChannel) (*LightningChannel, error) { nodePub := channelOld.channelState.IdentityPub nodeChannels, err := channelOld.channelState.Db.FetchOpenChannels( @@ -2596,8 +2611,14 @@ 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 := aliceChannel.ChanSyncMsg() - bobSyncMsg := bobChannel.ChanSyncMsg() + aliceSyncMsg, err := aliceChannel.ChanSyncMsg() + if err != nil { + t.Fatalf("unable to produce chan sync msg: %v", err) + } + bobSyncMsg, err := bobChannel.ChanSyncMsg() + if err != nil { + t.Fatalf("unable to produce chan sync msg: %v", err) + } // This is a helper function that asserts Alice concludes that she // needs to retransmit the exact commitment that we failed to send @@ -2787,7 +2808,7 @@ func TestChanSyncOweCommitment(t *testing.T) { } // At this point, the final balances of both parties should properly - // reflect + // reflect the amount of HTLC's sent. bobMsatSent := numBobHtlcs * htlcAmt if aliceChannel.channelState.TotalMSatSent != htlcAmt { t.Fatalf("wrong value for msat sent: expected %v, got %v", @@ -2902,8 +2923,14 @@ 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 := aliceChannel.ChanSyncMsg() - bobSyncMsg := bobChannel.ChanSyncMsg() + aliceSyncMsg, err := aliceChannel.ChanSyncMsg() + if err != nil { + t.Fatalf("unable to produce chan sync msg: %v", err) + } + bobSyncMsg, err := bobChannel.ChanSyncMsg() + if err != nil { + t.Fatalf("unable to produce chan sync msg: %v", err) + } assertAliceOwesRevoke := func() { aliceMsgsToSend, err := aliceChannel.ProcessChanSyncMsg(bobSyncMsg) @@ -3063,8 +3090,14 @@ 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 := aliceChannel.ChanSyncMsg() - bobSyncMsg := bobChannel.ChanSyncMsg() + aliceSyncMsg, err := aliceChannel.ChanSyncMsg() + if err != nil { + t.Fatalf("unable to produce chan sync msg: %v", err) + } + bobSyncMsg, err := bobChannel.ChanSyncMsg() + if err != nil { + t.Fatalf("unable to produce chan sync msg: %v", err) + } aliceMsgsToSend, err := aliceChannel.ProcessChanSyncMsg(bobSyncMsg) if err != nil { @@ -3221,8 +3254,14 @@ 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 := aliceChannel.ChanSyncMsg() - bobSyncMsg := bobChannel.ChanSyncMsg() + aliceSyncMsg, err := aliceChannel.ChanSyncMsg() + if err != nil { + t.Fatalf("unable to produce chan sync msg: %v", err) + } + bobSyncMsg, err := bobChannel.ChanSyncMsg() + if err != nil { + t.Fatalf("unable to produce chan sync msg: %v", err) + } aliceMsgsToSend, err := aliceChannel.ProcessChanSyncMsg(bobSyncMsg) if err != nil {