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.
This commit is contained in:
Olaoluwa Osuntokun 2017-11-13 22:40:45 -08:00
parent f7964e7474
commit a655396616
No known key found for this signature in database
GPG Key ID: 964EA263DD637C21
2 changed files with 89 additions and 21 deletions

@ -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

@ -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 {