From b6eff5b0ecab2a26e44174a8cb7579634ceea14e Mon Sep 17 00:00:00 2001 From: nsa Date: Mon, 13 Jul 2020 15:37:14 -0400 Subject: [PATCH] lnwallet: add regression test TestChannelUnsignedAckedFailure This commit includes a regression test that checks that a force close won't occur and that unsigned acked updates are properly restored. --- lnwallet/channel_test.go | 116 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 116 insertions(+) diff --git a/lnwallet/channel_test.go b/lnwallet/channel_test.go index 5307d867..18b2d760 100644 --- a/lnwallet/channel_test.go +++ b/lnwallet/channel_test.go @@ -9096,3 +9096,119 @@ func TestProcessAddRemoveEntry(t *testing.T) { }) } } + +// TestChannelUnsignedAckedFailure tests that unsigned acked updates are +// properly restored after signing for them and disconnecting. +// +// The full state transition of this test is: +// +// Alice Bob +// -----add-----> +// -----sig-----> +// <----rev------ +// <----sig------ +// -----rev-----> +// <----fail----- +// <----sig------ +// -----rev-----> +// -----sig-----X (does not reach Bob! Alice dies!) +// +// -----sig-----> +// <----rev------ +// <----add------ +// <----sig------ +// +// The last sig was rejected with the old behavior of deleting unsigned +// acked updates from the database after signing for them. The current +// behavior of filtering them for deletion upon receiving a revocation +// causes Alice to accept the signature as valid. +func TestChannelUnsignedAckedFailure(t *testing.T) { + t.Parallel() + + // Create a test channel so that we can test the buggy behavior. + aliceChannel, bobChannel, cleanUp, err := CreateTestChannels( + channeldb.SingleFunderTweaklessBit, + ) + require.NoError(t, err) + defer cleanUp() + + // First we create an HTLC that Alice sends to Bob. + htlc, _ := createHTLC(0, lnwire.MilliSatoshi(500000)) + + // -----add-----> + _, err = aliceChannel.AddHTLC(htlc, nil) + require.NoError(t, err) + _, err = bobChannel.ReceiveHTLC(htlc) + require.NoError(t, err) + + // Force a state transition to lock in this add on both commitments. + // -----sig-----> + // <----rev------ + // <----sig------ + // -----rev-----> + err = ForceStateTransition(aliceChannel, bobChannel) + require.NoError(t, err) + + // Now Bob should fail the htlc back to Alice. + // <----fail----- + err = bobChannel.FailHTLC(0, []byte("failreason"), nil, nil, nil) + require.NoError(t, err) + err = aliceChannel.ReceiveFailHTLC(0, []byte("bad")) + require.NoError(t, err) + + // Bob should send a commitment signature to Alice. + // <----sig------ + bobSig, bobHtlcSigs, _, err := bobChannel.SignNextCommitment() + require.NoError(t, err) + err = aliceChannel.ReceiveNewCommitment(bobSig, bobHtlcSigs) + require.NoError(t, err) + + // Alice should reply with a revocation. + // -----rev-----> + aliceRevocation, _, err := aliceChannel.RevokeCurrentCommitment() + require.NoError(t, err) + _, _, _, _, err = bobChannel.ReceiveRevocation(aliceRevocation) + require.NoError(t, err) + + // Alice should sign the next commitment and go down before + // sending it. + // -----sig-----X + aliceSig, aliceHtlcSigs, _, err := aliceChannel.SignNextCommitment() + require.NoError(t, err) + + newAliceChannel, err := NewLightningChannel( + aliceChannel.Signer, aliceChannel.channelState, + aliceChannel.sigPool, + ) + require.NoError(t, err) + + // Bob receives Alice's signature. + // -----sig-----> + err = bobChannel.ReceiveNewCommitment(aliceSig, aliceHtlcSigs) + require.NoError(t, err) + + // Bob revokes his current commitment and sends a revocation + // to Alice. + // <----rev------ + bobRevocation, _, err := bobChannel.RevokeCurrentCommitment() + require.NoError(t, err) + _, _, _, _, err = newAliceChannel.ReceiveRevocation(bobRevocation) + require.NoError(t, err) + + // Now Bob sends an HTLC to Alice. + htlc2, _ := createHTLC(0, lnwire.MilliSatoshi(500000)) + + // <----add------ + _, err = bobChannel.AddHTLC(htlc2, nil) + require.NoError(t, err) + _, err = newAliceChannel.ReceiveHTLC(htlc2) + require.NoError(t, err) + + // Bob sends the final signature to Alice and Alice should not + // reject it, given that we properly restore the unsigned acked + // updates and therefore our update log is structured correctly. + bobSig, bobHtlcSigs, _, err = bobChannel.SignNextCommitment() + require.NoError(t, err) + err = newAliceChannel.ReceiveNewCommitment(bobSig, bobHtlcSigs) + require.NoError(t, err) +}