From b3f953b1c44c6408a3253fcf77ce9060aa09a5d1 Mon Sep 17 00:00:00 2001 From: Jonathan Harvey-Buschel Date: Thu, 17 Oct 2024 13:38:27 +0200 Subject: [PATCH 1/9] multi: allow mock aux signer to customize sig jobs --- funding/manager_test.go | 2 +- lnwallet/channel_test.go | 9 +++++---- lnwallet/mock.go | 23 +++++++++++++++++++---- lnwallet/test_utils.go | 2 +- peer/test_utils.go | 8 ++++++-- 5 files changed, 32 insertions(+), 12 deletions(-) diff --git a/funding/manager_test.go b/funding/manager_test.go index dbe635377..18dd8cf38 100644 --- a/funding/manager_test.go +++ b/funding/manager_test.go @@ -568,7 +568,7 @@ func createTestFundingManager(t *testing.T, privKey *btcec.PrivateKey, &lnwallet.MockAuxLeafStore{}, ), AuxSigner: fn.Some[lnwallet.AuxSigner]( - &lnwallet.MockAuxSigner{}, + lnwallet.NewAuxSignerMock(lnwallet.EmptyMockJobHandler), ), } diff --git a/lnwallet/channel_test.go b/lnwallet/channel_test.go index 49e4321be..bc3564d03 100644 --- a/lnwallet/channel_test.go +++ b/lnwallet/channel_test.go @@ -3479,8 +3479,9 @@ func TestChanSyncOweCommitmentAuxSigner(t *testing.T) { aliceChannel, bobChannel, err := CreateTestChannels(t, chanType) require.NoError(t, err, "unable to create test channels") - // We'll now manually attach an aux signer to Alice's channel. - auxSigner := &MockAuxSigner{} + // We'll now manually attach an aux signer to Alice's channel. We'll + // set each aux sig job to receive an instant response. + auxSigner := NewAuxSignerMock(EmptyMockJobHandler) aliceChannel.auxSigner = fn.Some[AuxSigner](auxSigner) var fakeOnionBlob [lnwire.OnionPacketSize]byte @@ -3504,8 +3505,8 @@ func TestChanSyncOweCommitmentAuxSigner(t *testing.T) { _, err = aliceChannel.AddHTLC(h, nil) require.NoError(t, err, "unable to recv bob's htlc: %v", err) - // We'll set up the mock to expect calls to PackSigs and also - // SubmitSubmitSecondLevelSigBatch. + // We'll set up the mock aux signer to expect calls to PackSigs and also + // SubmitSecondLevelSigBatch. var sigBlobBuf bytes.Buffer sigBlob := testSigBlob{ BlobInt: tlv.NewPrimitiveRecord[tlv.TlvType65634, uint16](5), diff --git a/lnwallet/mock.go b/lnwallet/mock.go index f99f5c0b9..d71b292a7 100644 --- a/lnwallet/mock.go +++ b/lnwallet/mock.go @@ -443,9 +443,26 @@ func (*MockAuxLeafStore) ApplyHtlcView( return fn.Ok(fn.None[tlv.Blob]()) } +// EmptyMockJobHandler is a mock job handler that just sends an empty response +// to all jobs. +func EmptyMockJobHandler(jobs []AuxSigJob) { + for _, sigJob := range jobs { + sigJob.Resp <- AuxSigJobResp{} + } +} + // MockAuxSigner is a mock implementation of the AuxSigner interface. type MockAuxSigner struct { mock.Mock + + jobHandlerFunc func([]AuxSigJob) +} + +// NewAuxSignerMock creates a new mock aux signer with the given job handler. +func NewAuxSignerMock(jobHandler func([]AuxSigJob)) *MockAuxSigner { + return &MockAuxSigner{ + jobHandlerFunc: jobHandler, + } } // SubmitSecondLevelSigBatch takes a batch of aux sign jobs and @@ -455,10 +472,8 @@ func (a *MockAuxSigner) SubmitSecondLevelSigBatch(chanState AuxChanState, args := a.Called(chanState, tx, jobs) - // While we return, we'll also send back an instant response for the - // set of jobs. - for _, sigJob := range jobs { - sigJob.Resp <- AuxSigJobResp{} + if a.jobHandlerFunc != nil { + a.jobHandlerFunc(jobs) } return args.Error(0) diff --git a/lnwallet/test_utils.go b/lnwallet/test_utils.go index d4f0d05ae..b450b3b30 100644 --- a/lnwallet/test_utils.go +++ b/lnwallet/test_utils.go @@ -597,7 +597,7 @@ func ForceStateTransition(chanA, chanB *LightningChannel) error { } func NewDefaultAuxSignerMock(t *testing.T) *MockAuxSigner { - auxSigner := &MockAuxSigner{} + auxSigner := NewAuxSignerMock(EmptyMockJobHandler) type testSigBlob struct { BlobInt tlv.RecordT[tlv.TlvType65634, uint16] diff --git a/peer/test_utils.go b/peer/test_utils.go index 231a476ce..1a6235590 100644 --- a/peer/test_utils.go +++ b/peer/test_utils.go @@ -305,7 +305,9 @@ func createTestPeerWithChannel(t *testing.T, updateChan func(a, channelAlice, err := lnwallet.NewLightningChannel( aliceSigner, aliceChannelState, alicePool, lnwallet.WithLeafStore(&lnwallet.MockAuxLeafStore{}), - lnwallet.WithAuxSigner(&lnwallet.MockAuxSigner{}), + lnwallet.WithAuxSigner(lnwallet.NewAuxSignerMock( + lnwallet.EmptyMockJobHandler, + )), ) if err != nil { return nil, err @@ -319,7 +321,9 @@ func createTestPeerWithChannel(t *testing.T, updateChan func(a, channelBob, err := lnwallet.NewLightningChannel( bobSigner, bobChannelState, bobPool, lnwallet.WithLeafStore(&lnwallet.MockAuxLeafStore{}), - lnwallet.WithAuxSigner(&lnwallet.MockAuxSigner{}), + lnwallet.WithAuxSigner(lnwallet.NewAuxSignerMock( + lnwallet.EmptyMockJobHandler, + )), ) if err != nil { return nil, err From 22deba3d260b2812a0a5404d1ae29166a5870aa3 Mon Sep 17 00:00:00 2001 From: Jonathan Harvey-Buschel Date: Thu, 17 Oct 2024 13:38:28 +0200 Subject: [PATCH 2/9] lnwallet: refactor test code for HTLC add and recv --- lnwallet/channel_test.go | 402 ++++++++------------------------------- 1 file changed, 75 insertions(+), 327 deletions(-) diff --git a/lnwallet/channel_test.go b/lnwallet/channel_test.go index bc3564d03..3686f7195 100644 --- a/lnwallet/channel_test.go +++ b/lnwallet/channel_test.go @@ -22,6 +22,7 @@ import ( "github.com/davecgh/go-spew/spew" "github.com/lightningnetwork/lnd/chainntnfs" "github.com/lightningnetwork/lnd/channeldb" + "github.com/lightningnetwork/lnd/channeldb/models" "github.com/lightningnetwork/lnd/fn" "github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/lntypes" @@ -49,6 +50,18 @@ func createHTLC(id int, amount lnwire.MilliSatoshi) (*lnwire.UpdateAddHTLC, [32] }, returnPreimage } +// addAndReceiveHTLC adds an HTLC as local to the first channel, and as remote +// to a second channel. The HTLC ID is not modified. +func addAndReceiveHTLC(t *testing.T, channel1, channel2 *LightningChannel, + htlc *lnwire.UpdateAddHTLC, openKey *models.CircuitKey) { + + _, err := channel1.AddHTLC(htlc, openKey) + require.NoErrorf(t, err, "channel 1 unable to add htlc: %v", err) + + _, err = channel2.ReceiveHTLC(htlc) + require.NoErrorf(t, err, "channel 2 unable to recv htlc: %v", err) +} + func assertOutputExistsByValue(t *testing.T, commitTx *wire.MsgTx, value btcutil.Amount) { @@ -437,10 +450,7 @@ func TestChannelZeroAddLocalHeight(t *testing.T) { htlc, _ := createHTLC(0, lnwire.MilliSatoshi(500000)) // -----add-----> - _, err = aliceChannel.AddHTLC(htlc, nil) - require.NoError(t, err) - _, err = bobChannel.ReceiveHTLC(htlc) - require.NoError(t, err) + addAndReceiveHTLC(t, aliceChannel, bobChannel, htlc, nil) // Force a state transition to lock in this add on both commitments. // -----sig-----> @@ -486,10 +496,7 @@ func TestChannelZeroAddLocalHeight(t *testing.T) { htlc2, _ := createHTLC(0, lnwire.MilliSatoshi(500000)) // <----add----- - _, err = bobChannel.AddHTLC(htlc2, nil) - require.NoError(t, err) - _, err = newAliceChannel.ReceiveHTLC(htlc2) - require.NoError(t, err) + addAndReceiveHTLC(t, bobChannel, newAliceChannel, htlc2, nil) // Bob should now send a commitment signature to Alice. // <----sig----- @@ -546,12 +553,7 @@ func TestCheckCommitTxSize(t *testing.T) { for i := 0; i <= 10; i++ { htlc, _ := createHTLC(i, lnwire.MilliSatoshi(1e7)) - if _, err := aliceChannel.AddHTLC(htlc, nil); err != nil { - t.Fatalf("alice unable to add htlc: %v", err) - } - if _, err := bobChannel.ReceiveHTLC(htlc); err != nil { - t.Fatalf("bob unable to receive htlc: %v", err) - } + addAndReceiveHTLC(t, aliceChannel, bobChannel, htlc, nil) if err := ForceStateTransition(aliceChannel, bobChannel); err != nil { t.Fatalf("unable to complete state update: %v", err) @@ -626,12 +628,7 @@ func testCommitHTLCSigTieBreak(t *testing.T, restart bool) { Expiry: uint32(numHtlcs - i), } - if _, err := aliceChannel.AddHTLC(htlc, nil); err != nil { - t.Fatalf("alice unable to add htlc: %v", err) - } - if _, err := bobChannel.ReceiveHTLC(htlc); err != nil { - t.Fatalf("bob unable to receive htlc: %v", err) - } + addAndReceiveHTLC(t, aliceChannel, bobChannel, htlc, nil) } // Have Alice initiate the first half of the commitment dance. The @@ -937,23 +934,13 @@ func testForceClose(t *testing.T, testCase *forceCloseTestCase) { // We'll ensure that the HTLC amount is above Alice's dust limit. htlcAmount := lnwire.NewMSatFromSatoshis(20000) htlcAlice, _ := createHTLC(0, htlcAmount) - if _, err := aliceChannel.AddHTLC(htlcAlice, nil); err != nil { - t.Fatalf("alice unable to add htlc: %v", err) - } - if _, err := bobChannel.ReceiveHTLC(htlcAlice); err != nil { - t.Fatalf("bob unable to recv add htlc: %v", err) - } + addAndReceiveHTLC(t, aliceChannel, bobChannel, htlcAlice, nil) // We'll also a distinct HTLC from Bob -> Alice. This way, Alice will // have both an incoming and outgoing HTLC on her commitment // transaction. htlcBob, preimageBob := createHTLC(0, htlcAmount) - if _, err := bobChannel.AddHTLC(htlcBob, nil); err != nil { - t.Fatalf("alice unable to add htlc: %v", err) - } - if _, err := aliceChannel.ReceiveHTLC(htlcBob); err != nil { - t.Fatalf("bob unable to recv add htlc: %v", err) - } + addAndReceiveHTLC(t, bobChannel, aliceChannel, htlcBob, nil) // Next, we'll perform two state transitions to ensure that both HTLC's // get fully locked-in. @@ -1407,12 +1394,7 @@ func TestDustHTLCFees(t *testing.T) { // This HTLC amount should be lower than the dust limits of both nodes. htlcAmount := lnwire.NewMSatFromSatoshis(100) htlc, _ := createHTLC(0, htlcAmount) - if _, err := aliceChannel.AddHTLC(htlc, nil); err != nil { - t.Fatalf("alice unable to add htlc: %v", err) - } - if _, err := bobChannel.ReceiveHTLC(htlc); err != nil { - t.Fatalf("bob unable to receive htlc: %v", err) - } + addAndReceiveHTLC(t, aliceChannel, bobChannel, htlc, nil) if err := ForceStateTransition(aliceChannel, bobChannel); err != nil { t.Fatalf("Can't update the channel state: %v", err) } @@ -1565,14 +1547,9 @@ func TestHTLCSigNumber(t *testing.T) { for i, htlcSat := range htlcValues { htlcMsat := lnwire.NewMSatFromSatoshis(htlcSat) htlc, _ := createHTLC(i, htlcMsat) - _, err := aliceChannel.AddHTLC(htlc, nil) - if err != nil { - t.Fatalf("alice unable to add htlc: %v", err) - } - _, err = bobChannel.ReceiveHTLC(htlc) - if err != nil { - t.Fatalf("bob unable to receive htlc: %v", err) - } + addAndReceiveHTLC( + t, aliceChannel, bobChannel, htlc, nil, + ) } return aliceChannel, bobChannel @@ -1806,12 +1783,7 @@ func TestStateUpdatePersistence(t *testing.T) { OnionBlob: fakeOnionBlob, } - if _, err := aliceChannel.AddHTLC(h, nil); err != nil { - t.Fatalf("unable to add alice's htlc: %v", err) - } - if _, err := bobChannel.ReceiveHTLC(h); err != nil { - t.Fatalf("unable to recv alice's htlc: %v", err) - } + addAndReceiveHTLC(t, aliceChannel, bobChannel, h, nil) } rHash := sha256.Sum256(bobPreimage[:]) bobh := &lnwire.UpdateAddHTLC{ @@ -1820,12 +1792,7 @@ func TestStateUpdatePersistence(t *testing.T) { Expiry: uint32(10), OnionBlob: fakeOnionBlob, } - if _, err := bobChannel.AddHTLC(bobh, nil); err != nil { - t.Fatalf("unable to add bob's htlc: %v", err) - } - if _, err := aliceChannel.ReceiveHTLC(bobh); err != nil { - t.Fatalf("unable to recv bob's htlc: %v", err) - } + addAndReceiveHTLC(t, bobChannel, aliceChannel, bobh, nil) // Also add a fee update to the update logs. fee := chainfee.SatPerKWeight(333) @@ -2494,12 +2461,7 @@ func TestUpdateFeeConcurrentSig(t *testing.T) { // First Alice adds the outgoing HTLC to her local channel's state // update log. Then Alice sends this wire message over to Bob who // adds this htlc to his remote state update log. - if _, err := aliceChannel.AddHTLC(htlc, nil); err != nil { - t.Fatalf("unable to add htlc: %v", err) - } - if _, err := bobChannel.ReceiveHTLC(htlc); err != nil { - t.Fatalf("unable to recv htlc: %v", err) - } + addAndReceiveHTLC(t, aliceChannel, bobChannel, htlc, nil) // Simulate Alice sending update fee message to bob. fee := chainfee.SatPerKWeight(333) @@ -2569,12 +2531,7 @@ func TestUpdateFeeSenderCommits(t *testing.T) { // First Alice adds the outgoing HTLC to her local channel's state // update log. Then Alice sends this wire message over to Bob who // adds this htlc to his remote state update log. - if _, err := aliceChannel.AddHTLC(htlc, nil); err != nil { - t.Fatalf("unable to add htlc: %v", err) - } - if _, err := bobChannel.ReceiveHTLC(htlc); err != nil { - t.Fatalf("unable to recv htlc: %v", err) - } + addAndReceiveHTLC(t, aliceChannel, bobChannel, htlc, nil) // Simulate Alice sending update fee message to bob. fee := chainfee.SatPerKWeight(333) @@ -2678,12 +2635,7 @@ func TestUpdateFeeReceiverCommits(t *testing.T) { // First Alice adds the outgoing HTLC to her local channel's state // update log. Then Alice sends this wire message over to Bob who // adds this htlc to his remote state update log. - if _, err := aliceChannel.AddHTLC(htlc, nil); err != nil { - t.Fatalf("unable to add htlc: %v", err) - } - if _, err := bobChannel.ReceiveHTLC(htlc); err != nil { - t.Fatalf("unable to recv htlc: %v", err) - } + addAndReceiveHTLC(t, aliceChannel, bobChannel, htlc, nil) // Simulate Alice sending update fee message to bob fee := chainfee.SatPerKWeight(333) @@ -3882,12 +3834,7 @@ func testChanSyncOweRevocation(t *testing.T, chanType channeldb.ChannelType) { Amount: htlcAmt, Expiry: uint32(10), } - if _, err := aliceChannel.AddHTLC(aliceHtlc, nil); err != nil { - t.Fatalf("unable to add alice's htlc: %v", err) - } - if _, err := bobChannel.ReceiveHTLC(aliceHtlc); err != nil { - t.Fatalf("unable to recv alice's htlc: %v", err) - } + addAndReceiveHTLC(t, aliceChannel, bobChannel, aliceHtlc, nil) if err := ForceStateTransition(aliceChannel, bobChannel); err != nil { t.Fatalf("unable to complete alice's state transition: %v", err) } @@ -4168,10 +4115,7 @@ func testChanSyncOweRevocationAndCommitForceTransition(t *testing.T, Expiry: uint32(10), ID: 1, } - _, err = bobChannel.AddHTLC(bobHtlc[1], nil) - require.NoError(t, err, "unable to add bob's htlc") - _, err = aliceChannel.ReceiveHTLC(bobHtlc[1]) - require.NoError(t, err, "unable to recv bob's htlc") + addAndReceiveHTLC(t, bobChannel, aliceChannel, bobHtlc[1], nil) // Bob signs the new state update, and sends the signature to Alice. bobNewCommit, err := bobChannel.SignNextCommitment() @@ -4404,14 +4348,7 @@ func TestChanSyncFailure(t *testing.T) { } index++ - _, err := bobChannel.AddHTLC(bobHtlc, nil) - if err != nil { - t.Fatalf("unable to add bob's htlc: %v", err) - } - _, err = aliceChannel.ReceiveHTLC(bobHtlc) - if err != nil { - t.Fatalf("unable to recv bob's htlc: %v", err) - } + addAndReceiveHTLC(t, bobChannel, aliceChannel, bobHtlc, nil) err = ForceStateTransition(bobChannel, aliceChannel) if err != nil { t.Fatalf("unable to complete bob's state "+ @@ -4437,14 +4374,7 @@ func TestChanSyncFailure(t *testing.T) { } index++ - _, err := bobChannel.AddHTLC(bobHtlc, nil) - if err != nil { - t.Fatalf("unable to add bob's htlc: %v", err) - } - _, err = aliceChannel.ReceiveHTLC(bobHtlc) - if err != nil { - t.Fatalf("unable to recv bob's htlc: %v", err) - } + addAndReceiveHTLC(t, bobChannel, aliceChannel, bobHtlc, nil) aliceNewCommit, err := aliceChannel.SignNextCommitment() if err != nil { @@ -4787,12 +4717,7 @@ func TestChannelRetransmissionFeeUpdate(t *testing.T) { Amount: lnwire.NewMSatFromSatoshis(20000), Expiry: uint32(10), } - if _, err := bobChannel.AddHTLC(bobHtlc, nil); err != nil { - t.Fatalf("unable to add bob's htlc: %v", err) - } - if _, err := aliceChannel.ReceiveHTLC(bobHtlc); err != nil { - t.Fatalf("unable to recv bob's htlc: %v", err) - } + addAndReceiveHTLC(t, bobChannel, aliceChannel, bobHtlc, nil) if err := ForceStateTransition(bobChannel, aliceChannel); err != nil { t.Fatalf("unable to complete bob's state transition: %v", err) } @@ -4864,12 +4789,7 @@ func TestFeeUpdateOldDiskFormat(t *testing.T) { var htlcs []*lnwire.UpdateAddHTLC for i := 0; i < numHTLCs; i++ { htlc, _ := createHTLC(i, htlcAmt) - if _, err := aliceChannel.AddHTLC(htlc, nil); err != nil { - t.Fatalf("unable to add htlc: %v", err) - } - if _, err := bobChannel.ReceiveHTLC(htlc); err != nil { - t.Fatalf("unable to recv htlc: %v", err) - } + addAndReceiveHTLC(t, aliceChannel, bobChannel, htlc, nil) htlcs = append(htlcs, htlc) if i%5 != 0 { @@ -4978,12 +4898,7 @@ func TestFeeUpdateOldDiskFormat(t *testing.T) { // Finally, to trigger a compactLogs execution, we'll add a new HTLC, // then force a state transition. htlc, _ := createHTLC(numHTLCs, htlcAmt) - if _, err := aliceChannel.AddHTLC(htlc, nil); err != nil { - t.Fatalf("unable to add htlc: %v", err) - } - if _, err := bobChannel.ReceiveHTLC(htlc); err != nil { - t.Fatalf("unable to recv htlc: %v", err) - } + addAndReceiveHTLC(t, aliceChannel, bobChannel, htlc, nil) if err := ForceStateTransition(aliceChannel, bobChannel); err != nil { t.Fatalf("unable to complete bob's state transition: %v", err) } @@ -5077,12 +4992,7 @@ func TestChanSyncInvalidLastSecret(t *testing.T) { Amount: htlcAmt, Expiry: uint32(5), } - if _, err := aliceChannel.AddHTLC(htlc, nil); err != nil { - t.Fatalf("unable to add htlc: %v", err) - } - if _, err := bobChannel.ReceiveHTLC(htlc); err != nil { - t.Fatalf("unable to recv htlc: %v", err) - } + addAndReceiveHTLC(t, aliceChannel, bobChannel, htlc, nil) // Then we'll initiate a state transition to lock in this new HTLC. if err := ForceStateTransition(aliceChannel, bobChannel); err != nil { @@ -5204,12 +5114,7 @@ func TestChanAvailableBandwidth(t *testing.T) { alicePreimages := make([][32]byte, numHtlcs) for i := 0; i < numHtlcs; i++ { htlc, preImage := createHTLC(i, dustAmt) - if _, err := aliceChannel.AddHTLC(htlc, nil); err != nil { - t.Fatalf("unable to add htlc: %v", err) - } - if _, err := bobChannel.ReceiveHTLC(htlc); err != nil { - t.Fatalf("unable to recv htlc: %v", err) - } + addAndReceiveHTLC(t, aliceChannel, bobChannel, htlc, nil) alicePreimages[i] = preImage } @@ -5222,12 +5127,7 @@ func TestChanAvailableBandwidth(t *testing.T) { htlcAmt := lnwire.NewMSatFromSatoshis(30000) for i := 0; i < numHtlcs; i++ { htlc, preImage := createHTLC(numHtlcs+i, htlcAmt) - if _, err := aliceChannel.AddHTLC(htlc, nil); err != nil { - t.Fatalf("unable to add htlc: %v", err) - } - if _, err := bobChannel.ReceiveHTLC(htlc); err != nil { - t.Fatalf("unable to recv htlc: %v", err) - } + addAndReceiveHTLC(t, aliceChannel, bobChannel, htlc, nil) alicePreimages = append(alicePreimages, preImage) } @@ -5496,13 +5396,7 @@ func TestChanCommitWeightDustHtlcs(t *testing.T) { t.Helper() htlc, preImage := createHTLC(int(htlcIndex), htlcAmt) - if _, err := aliceChannel.AddHTLC(htlc, nil); err != nil { - t.Fatalf("unable to add htlc: %v", err) - } - if _, err := bobChannel.ReceiveHTLC(htlc); err != nil { - t.Fatalf("unable to recv htlc: %v", err) - } - + addAndReceiveHTLC(t, aliceChannel, bobChannel, htlc, nil) if err := ForceStateTransition(aliceChannel, bobChannel); err != nil { t.Fatalf("unable to complete alice's state "+ "transition: %v", err) @@ -5637,19 +5531,10 @@ func TestLockedInHtlcForwardingSkipAfterRestart(t *testing.T) { // a state transition. var htlcAmt lnwire.MilliSatoshi = 100000 htlc, _ := createHTLC(0, htlcAmt) - if _, err := aliceChannel.AddHTLC(htlc, nil); err != nil { - t.Fatalf("unable to add htlc: %v", err) - } - if _, err := bobChannel.ReceiveHTLC(htlc); err != nil { - t.Fatalf("unable to recv htlc: %v", err) - } + addAndReceiveHTLC(t, aliceChannel, bobChannel, htlc, nil) + htlc2, _ := createHTLC(1, htlcAmt) - if _, err := aliceChannel.AddHTLC(htlc2, nil); err != nil { - t.Fatalf("unable to add htlc2: %v", err) - } - if _, err := bobChannel.ReceiveHTLC(htlc2); err != nil { - t.Fatalf("unable to recv htlc2: %v", err) - } + addAndReceiveHTLC(t, aliceChannel, bobChannel, htlc2, nil) // We'll now manually initiate a state transition between Alice and // bob. @@ -5955,12 +5840,7 @@ func TestInvalidCommitSigError(t *testing.T) { // Alice to Bob. var htlcAmt lnwire.MilliSatoshi = 100000 htlc, _ := createHTLC(0, htlcAmt) - if _, err := aliceChannel.AddHTLC(htlc, nil); err != nil { - t.Fatalf("unable to add htlc: %v", err) - } - if _, err := bobChannel.ReceiveHTLC(htlc); err != nil { - t.Fatalf("unable to recv htlc: %v", err) - } + addAndReceiveHTLC(t, aliceChannel, bobChannel, htlc, nil) // Alice will now attempt to initiate a state transition. aliceNewCommit, err := aliceChannel.SignNextCommitment() @@ -6005,19 +5885,9 @@ func TestChannelUnilateralCloseHtlcResolution(t *testing.T) { // initiating enough state transitions to lock both of them in. htlcAmount := lnwire.NewMSatFromSatoshis(20000) htlcAlice, _ := createHTLC(0, htlcAmount) - if _, err := aliceChannel.AddHTLC(htlcAlice, nil); err != nil { - t.Fatalf("alice unable to add htlc: %v", err) - } - if _, err := bobChannel.ReceiveHTLC(htlcAlice); err != nil { - t.Fatalf("bob unable to recv add htlc: %v", err) - } + addAndReceiveHTLC(t, aliceChannel, bobChannel, htlcAlice, nil) htlcBob, preimageBob := createHTLC(0, htlcAmount) - if _, err := bobChannel.AddHTLC(htlcBob, nil); err != nil { - t.Fatalf("bob unable to add htlc: %v", err) - } - if _, err := aliceChannel.ReceiveHTLC(htlcBob); err != nil { - t.Fatalf("alice unable to recv add htlc: %v", err) - } + addAndReceiveHTLC(t, bobChannel, aliceChannel, htlcBob, nil) if err := ForceStateTransition(aliceChannel, bobChannel); err != nil { t.Fatalf("Can't update the channel state: %v", err) } @@ -6162,12 +6032,7 @@ func TestChannelUnilateralClosePendingCommit(t *testing.T) { // create a new state transition. htlcAmount := lnwire.NewMSatFromSatoshis(20000) htlcAlice, _ := createHTLC(0, htlcAmount) - if _, err := aliceChannel.AddHTLC(htlcAlice, nil); err != nil { - t.Fatalf("alice unable to add htlc: %v", err) - } - if _, err := bobChannel.ReceiveHTLC(htlcAlice); err != nil { - t.Fatalf("bob unable to recv add htlc: %v", err) - } + addAndReceiveHTLC(t, aliceChannel, bobChannel, htlcAlice, nil) // With the HTLC added, we'll now manually initiate a state transition // from Alice to Bob. @@ -6363,12 +6228,7 @@ func TestMaxAcceptedHTLCs(t *testing.T) { // Send the maximum allowed number of HTLCs. for i := 0; i < numHTLCs; i++ { htlc, _ := createHTLC(i, htlcAmt) - if _, err := aliceChannel.AddHTLC(htlc, nil); err != nil { - t.Fatalf("unable to add htlc: %v", err) - } - if _, err := bobChannel.ReceiveHTLC(htlc); err != nil { - t.Fatalf("unable to recv htlc: %v", err) - } + addAndReceiveHTLC(t, aliceChannel, bobChannel, htlc, nil) // Just assign htlcID to the last received HTLC. htlcID = htlc.ID @@ -6407,12 +6267,7 @@ func TestMaxAcceptedHTLCs(t *testing.T) { // failed. We use numHTLCs here since the previous AddHTLC with this index // failed. htlc, _ = createHTLC(numHTLCs, htlcAmt) - if _, err := aliceChannel.AddHTLC(htlc, nil); err != nil { - t.Fatalf("unable to add htlc: %v", err) - } - if _, err := bobChannel.ReceiveHTLC(htlc); err != nil { - t.Fatalf("unable to recv htlc: %v", err) - } + addAndReceiveHTLC(t, aliceChannel, bobChannel, htlc, nil) // Add a commitment to Bob's commitment chain. aliceNewCommit, err := aliceChannel.SignNextCommitment() @@ -6488,12 +6343,7 @@ func TestMaxAsynchronousHtlcs(t *testing.T) { // Send the maximum allowed number of HTLCs minus one. for i := 0; i < numHTLCs-1; i++ { htlc, _ := createHTLC(i, htlcAmt) - if _, err := aliceChannel.AddHTLC(htlc, nil); err != nil { - t.Fatalf("unable to add htlc: %v", err) - } - if _, err := bobChannel.ReceiveHTLC(htlc); err != nil { - t.Fatalf("unable to recv htlc: %v", err) - } + addAndReceiveHTLC(t, aliceChannel, bobChannel, htlc, nil) // Just assign htlcID to the last received HTLC. htlcID = htlc.ID @@ -6505,12 +6355,7 @@ func TestMaxAsynchronousHtlcs(t *testing.T) { // Send an HTLC to Bob so that Bob's commitment transaction is full. htlc, _ := createHTLC(numHTLCs-1, htlcAmt) - if _, err := aliceChannel.AddHTLC(htlc, nil); err != nil { - t.Fatalf("unable to add htlc: %v", err) - } - if _, err := bobChannel.ReceiveHTLC(htlc); err != nil { - t.Fatalf("unable to recv htlc: %v", err) - } + addAndReceiveHTLC(t, aliceChannel, bobChannel, htlc, nil) // Fail back an HTLC and sign a commitment as in steps 1 & 2. err = bobChannel.FailHTLC(htlcID, []byte{}, nil, nil, nil) @@ -6549,12 +6394,7 @@ func TestMaxAsynchronousHtlcs(t *testing.T) { // Send the final Add which should succeed as in step 6. htlc, _ = createHTLC(numHTLCs, htlcAmt) - if _, err := aliceChannel.AddHTLC(htlc, nil); err != nil { - t.Fatalf("unable to add htlc: %v", err) - } - if _, err := bobChannel.ReceiveHTLC(htlc); err != nil { - t.Fatalf("unable to recv htlc: %v", err) - } + addAndReceiveHTLC(t, aliceChannel, bobChannel, htlc, nil) // Receiving the commitment should succeed as in step 7 since space was // made. @@ -6595,12 +6435,7 @@ func TestMaxPendingAmount(t *testing.T) { htlcAmt := lnwire.NewMSatFromSatoshis(1.5 * btcutil.SatoshiPerBitcoin) for i := 0; i < numHTLCs; i++ { htlc, _ := createHTLC(i, htlcAmt) - if _, err := aliceChannel.AddHTLC(htlc, nil); err != nil { - t.Fatalf("unable to add htlc: %v", err) - } - if _, err := bobChannel.ReceiveHTLC(htlc); err != nil { - t.Fatalf("unable to recv htlc: %v", err) - } + addAndReceiveHTLC(t, aliceChannel, bobChannel, htlc, nil) } // We finally add one more HTLC of 0.1 BTC to Alice's commitment. This @@ -6702,12 +6537,7 @@ func TestChanReserve(t *testing.T) { htlcAmt := lnwire.NewMSatFromSatoshis(0.5 * btcutil.SatoshiPerBitcoin) htlc, _ := createHTLC(aliceIndex, htlcAmt) aliceIndex++ - if _, err := aliceChannel.AddHTLC(htlc, nil); err != nil { - t.Fatalf("unable to add htlc: %v", err) - } - if _, err := bobChannel.ReceiveHTLC(htlc); err != nil { - t.Fatalf("unable to recv htlc: %v", err) - } + addAndReceiveHTLC(t, aliceChannel, bobChannel, htlc, nil) // Force a state transition, making sure this HTLC is considered valid // even though the channel reserves are not met. @@ -6754,12 +6584,7 @@ func TestChanReserve(t *testing.T) { // The first HTLC should successfully be sent. htlc, _ = createHTLC(aliceIndex, htlcAmt) aliceIndex++ - if _, err := aliceChannel.AddHTLC(htlc, nil); err != nil { - t.Fatalf("unable to add htlc: %v", err) - } - if _, err := bobChannel.ReceiveHTLC(htlc); err != nil { - t.Fatalf("unable to recv htlc: %v", err) - } + addAndReceiveHTLC(t, aliceChannel, bobChannel, htlc, nil) // Add a second HTLC of 1 BTC. This should fail because it will take // Alice's balance all the way down to her channel reserve, but since @@ -6825,12 +6650,7 @@ func TestChanReserve(t *testing.T) { htlcAmt = lnwire.NewMSatFromSatoshis(1 * btcutil.SatoshiPerBitcoin) htlc, _ = createHTLC(bobIndex, htlcAmt) bobIndex++ - if _, err := bobChannel.AddHTLC(htlc, nil); err != nil { - t.Fatalf("unable to add htlc: %v", err) - } - if _, err := aliceChannel.ReceiveHTLC(htlc); err != nil { - t.Fatalf("unable to recv htlc: %v", err) - } + addAndReceiveHTLC(t, bobChannel, aliceChannel, htlc, nil) // Do a last state transition, which should succeed. if err := ForceStateTransition(bobChannel, aliceChannel); err != nil { @@ -6958,12 +6778,7 @@ func TestMinHTLC(t *testing.T) { // ErrBelowMinHTLC. htlcAmt := lnwire.NewMSatFromSatoshis(0.5 * btcutil.SatoshiPerBitcoin) htlc, _ := createHTLC(0, htlcAmt) - if _, err := aliceChannel.AddHTLC(htlc, nil); err != nil { - t.Fatalf("unable to add htlc: %v", err) - } - if _, err := bobChannel.ReceiveHTLC(htlc); err != nil { - t.Fatalf("unable to recv htlc: %v", err) - } + addAndReceiveHTLC(t, aliceChannel, bobChannel, htlc, nil) // We add an HTLC below the min value, this should result in // an ErrBelowMinHTLC error. @@ -7205,12 +7020,7 @@ func TestChannelRestoreUpdateLogs(t *testing.T) { // Bob's commit, but not on Alice's. htlcAmount := lnwire.NewMSatFromSatoshis(20000) htlcAlice, _ := createHTLC(0, htlcAmount) - if _, err := aliceChannel.AddHTLC(htlcAlice, nil); err != nil { - t.Fatalf("alice unable to add htlc: %v", err) - } - if _, err := bobChannel.ReceiveHTLC(htlcAlice); err != nil { - t.Fatalf("bob unable to recv add htlc: %v", err) - } + addAndReceiveHTLC(t, aliceChannel, bobChannel, htlcAlice, nil) // Let Alice sign a new state, which will include the HTLC just sent. aliceNewCommit, err := aliceChannel.SignNextCommitment() @@ -7454,11 +7264,7 @@ func TestDuplicateFailRejection(t *testing.T) { // parties. htlcAmount := lnwire.NewMSatFromSatoshis(20000) htlcAlice, _ := createHTLC(0, htlcAmount) - if _, err := aliceChannel.AddHTLC(htlcAlice, nil); err != nil { - t.Fatalf("alice unable to add htlc: %v", err) - } - _, err = bobChannel.ReceiveHTLC(htlcAlice) - require.NoError(t, err, "unable to recv htlc") + addAndReceiveHTLC(t, aliceChannel, bobChannel, htlcAlice, nil) if err := ForceStateTransition(aliceChannel, bobChannel); err != nil { t.Fatalf("unable to complete state update: %v", err) @@ -7521,11 +7327,7 @@ func TestDuplicateSettleRejection(t *testing.T) { // parties. htlcAmount := lnwire.NewMSatFromSatoshis(20000) htlcAlice, alicePreimage := createHTLC(0, htlcAmount) - if _, err := aliceChannel.AddHTLC(htlcAlice, nil); err != nil { - t.Fatalf("alice unable to add htlc: %v", err) - } - _, err = bobChannel.ReceiveHTLC(htlcAlice) - require.NoError(t, err, "unable to recv htlc") + addAndReceiveHTLC(t, aliceChannel, bobChannel, htlcAlice, nil) if err := ForceStateTransition(aliceChannel, bobChannel); err != nil { t.Fatalf("unable to complete state update: %v", err) @@ -7629,12 +7431,7 @@ func TestChannelRestoreCommitHeight(t *testing.T) { // We'll send an HtLC from Alice to Bob. htlcAmount := lnwire.NewMSatFromSatoshis(100000000) htlcAlice, _ := createHTLC(0, htlcAmount) - if _, err := aliceChannel.AddHTLC(htlcAlice, nil); err != nil { - t.Fatalf("alice unable to add htlc: %v", err) - } - if _, err := bobChannel.ReceiveHTLC(htlcAlice); err != nil { - t.Fatalf("bob unable to recv add htlc: %v", err) - } + addAndReceiveHTLC(t, aliceChannel, bobChannel, htlcAlice, nil) // Let Alice sign a new state, which will include the HTLC just sent. aliceNewCommit, err := aliceChannel.SignNextCommitment() @@ -7698,12 +7495,7 @@ func TestChannelRestoreCommitHeight(t *testing.T) { // existing HTLCs (the HTLC with index 0) keep getting the add heights // restored properly. htlcAlice, _ = createHTLC(1, htlcAmount) - if _, err := aliceChannel.AddHTLC(htlcAlice, nil); err != nil { - t.Fatalf("alice unable to add htlc: %v", err) - } - if _, err := bobChannel.ReceiveHTLC(htlcAlice); err != nil { - t.Fatalf("bob unable to recv add htlc: %v", err) - } + addAndReceiveHTLC(t, aliceChannel, bobChannel, htlcAlice, nil) // Send a new signature from Alice to Bob, making Alice have a pending // remote commitment. @@ -9088,10 +8880,7 @@ func TestChannelUnsignedAckedFailure(t *testing.T) { htlc, _ := createHTLC(0, lnwire.MilliSatoshi(500000)) // -----add-----> - _, err = aliceChannel.AddHTLC(htlc, nil) - require.NoError(t, err) - _, err = bobChannel.ReceiveHTLC(htlc) - require.NoError(t, err) + addAndReceiveHTLC(t, aliceChannel, bobChannel, htlc, nil) // Force a state transition to lock in this add on both commitments. // -----sig-----> @@ -9151,10 +8940,7 @@ func TestChannelUnsignedAckedFailure(t *testing.T) { htlc2, _ := createHTLC(0, lnwire.MilliSatoshi(500000)) // <----add------ - _, err = bobChannel.AddHTLC(htlc2, nil) - require.NoError(t, err) - _, err = newAliceChannel.ReceiveHTLC(htlc2) - require.NoError(t, err) + addAndReceiveHTLC(t, bobChannel, newAliceChannel, htlc2, nil) // Bob sends the final signature to Alice and Alice should not // reject it, given that we properly restore the unsigned acked @@ -9198,10 +8984,7 @@ func TestChannelLocalUnsignedUpdatesFailure(t *testing.T) { htlc, _ := createHTLC(0, lnwire.MilliSatoshi(500000)) // <----add----- - _, err = bobChannel.AddHTLC(htlc, nil) - require.NoError(t, err) - _, err = aliceChannel.ReceiveHTLC(htlc) - require.NoError(t, err) + addAndReceiveHTLC(t, bobChannel, aliceChannel, htlc, nil) // Force a state transition to lock in this add on both commitments. // <----sig----- @@ -9285,10 +9068,7 @@ func TestChannelSignedAckRegression(t *testing.T) { htlc, preimage := createHTLC(0, lnwire.MilliSatoshi(5000000)) // <----add------ - _, err = bobChannel.AddHTLC(htlc, nil) - require.NoError(t, err) - _, err = aliceChannel.ReceiveHTLC(htlc) - require.NoError(t, err) + addAndReceiveHTLC(t, bobChannel, aliceChannel, htlc, nil) // Force a state transition to lock in the HTLC. // <----sig------ @@ -9327,10 +9107,7 @@ func TestChannelSignedAckRegression(t *testing.T) { htlc2, _ := createHTLC(0, lnwire.MilliSatoshi(5000000)) // -----add----> - _, err = aliceChannel.AddHTLC(htlc2, nil) - require.NoError(t, err) - _, err = bobChannel.ReceiveHTLC(htlc2) - require.NoError(t, err) + addAndReceiveHTLC(t, aliceChannel, bobChannel, htlc2, nil) // -----sig----> aliceNewCommit, err = aliceChannel.SignNextCommitment() @@ -9427,10 +9204,7 @@ func TestIsChannelClean(t *testing.T) { // sends an htlc. // ---add---> htlc, preimage := createHTLC(0, lnwire.MilliSatoshi(5000000)) - _, err = aliceChannel.AddHTLC(htlc, nil) - require.NoError(t, err) - _, err = bobChannel.ReceiveHTLC(htlc) - require.NoError(t, err) + addAndReceiveHTLC(t, aliceChannel, bobChannel, htlc, nil) assertCleanOrDirty(false, aliceChannel, bobChannel, t) // Assert that the channel remains dirty until the HTLC is completely @@ -9608,10 +9382,7 @@ func testGetDustSum(t *testing.T, chantype channeldb.ChannelType) { htlc1Amt := lnwire.MilliSatoshi(700_000) htlc1, preimage1 := createHTLC(0, htlc1Amt) - _, err = bobChannel.AddHTLC(htlc1, nil) - require.NoError(t, err) - _, err = aliceChannel.ReceiveHTLC(htlc1) - require.NoError(t, err) + addAndReceiveHTLC(t, bobChannel, aliceChannel, htlc1, nil) // Assert that GetDustSum from Alice's perspective does not consider // the HTLC dust on her commitment, but does on Bob's commitment. @@ -9651,10 +9422,7 @@ func testGetDustSum(t *testing.T, chantype channeldb.ChannelType) { htlc2Amt := lnwire.MilliSatoshi(100_000) htlc2, _ := createHTLC(0, htlc2Amt) - _, err = aliceChannel.AddHTLC(htlc2, nil) - require.NoError(t, err) - _, err = bobChannel.ReceiveHTLC(htlc2) - require.NoError(t, err) + addAndReceiveHTLC(t, aliceChannel, bobChannel, htlc2, nil) // Assert that GetDustSum from Alice's perspective includes the new // HTLC as dust on both commitments. @@ -9702,10 +9470,7 @@ func testGetDustSum(t *testing.T, chantype channeldb.ChannelType) { htlc3Amt := lnwire.MilliSatoshi(400_000) htlc3, _ := createHTLC(1, htlc3Amt) - _, err = aliceChannel.AddHTLC(htlc3, nil) - require.NoError(t, err) - _, err = bobChannel.ReceiveHTLC(htlc3) - require.NoError(t, err) + addAndReceiveHTLC(t, aliceChannel, bobChannel, htlc3, nil) // Assert that this new HTLC is not counted on Alice's local commitment // in the dust sum. Bob's commitment should count it. @@ -10707,11 +10472,7 @@ func TestAsynchronousSendingWithFeeBuffer(t *testing.T) { aliceChannel.channelState.LocalChanCfg.DustLimit + htlcFee, ) htlc3, _ := createHTLC(1, htlcAmt3) - _, err = bobChannel.AddHTLC(htlc3, nil) - require.NoError(t, err) - - _, err = aliceChannel.ReceiveHTLC(htlc3) - require.NoError(t, err) + addAndReceiveHTLC(t, bobChannel, aliceChannel, htlc3, nil) err = ForceStateTransition(bobChannel, aliceChannel) require.NoError(t, err) @@ -10817,10 +10578,7 @@ func TestEnforceFeeBuffer(t *testing.T) { // --------------- |-----rev------> htlc1, _ := createHTLC(0, lnwire.NewMSatFromSatoshis(htlcAmt1)) - _, err = aliceChannel.AddHTLC(htlc1, nil) - require.NoError(t, err) - _, err = bobChannel.ReceiveHTLC(htlc1) - require.NoError(t, err) + addAndReceiveHTLC(t, aliceChannel, bobChannel, htlc1, nil) err = ForceStateTransition(aliceChannel, bobChannel) require.NoError(t, err) @@ -10834,10 +10592,7 @@ func TestEnforceFeeBuffer(t *testing.T) { htlcAmt2 := lnwire.NewMSatFromSatoshis(btcutil.SatoshiPerBitcent) htlc2, _ := createHTLC(0, htlcAmt2) - _, err = bobChannel.AddHTLC(htlc2, nil) - require.NoError(t, err) - _, err = aliceChannel.ReceiveHTLC(htlc2) - require.NoError(t, err) + addAndReceiveHTLC(t, bobChannel, aliceChannel, htlc2, nil) err = ForceStateTransition(bobChannel, aliceChannel) require.NoError(t, err) @@ -10857,10 +10612,7 @@ func TestEnforceFeeBuffer(t *testing.T) { // <----rev------- |--------------- htlc4, _ := createHTLC(1, htlcAmt2) - _, err = bobChannel.AddHTLC(htlc4, nil) - require.NoError(t, err) - _, err = aliceChannel.ReceiveHTLC(htlc4) - require.NoError(t, err) + addAndReceiveHTLC(t, bobChannel, aliceChannel, htlc4, nil) err = ForceStateTransition(bobChannel, aliceChannel) require.NoError(t, err) @@ -10900,11 +10652,7 @@ func TestBlindingPointPersistence(t *testing.T) { tlv.NewPrimitiveRecord[lnwire.BlindingPointTlvType](blinding), ) - _, err = aliceChannel.AddHTLC(htlc, nil) - - require.NoError(t, err) - _, err = bobChannel.ReceiveHTLC(htlc) - require.NoError(t, err) + addAndReceiveHTLC(t, aliceChannel, bobChannel, htlc, nil) // Now, Alice will send a new commitment to Bob, which will persist our // pending HTLC to disk. From a0baa8fb41168c60b50371a0701e369f3942b074 Mon Sep 17 00:00:00 2001 From: Jonathan Harvey-Buschel Date: Thu, 17 Oct 2024 13:38:29 +0200 Subject: [PATCH 3/9] lnwallet: test aux signer shutdown handling --- lnwallet/channel_test.go | 80 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) diff --git a/lnwallet/channel_test.go b/lnwallet/channel_test.go index 3686f7195..f3fa052fe 100644 --- a/lnwallet/channel_test.go +++ b/lnwallet/channel_test.go @@ -4,6 +4,7 @@ import ( "bytes" crand "crypto/rand" "crypto/sha256" + "errors" "fmt" "math/rand" "reflect" @@ -3506,6 +3507,85 @@ func TestChanSyncOweCommitmentAuxSigner(t *testing.T) { require.NotEmpty(t, sigMsg.CustomRecords) } +// TestAuxSignerShutdown tests that the channel state machine gracefully handles +// a failure of the aux signer when signing a new commitment. +func TestAuxSignerShutdown(t *testing.T) { + t.Parallel() + + // We'll kick off the test by creating our channels which both are + // loaded with 5 BTC each. + aliceChannel, bobChannel, err := CreateTestChannels( + t, channeldb.SingleFunderTweaklessBit, + ) + require.NoError(t, err, "unable to create test channels") + + auxSignerShutdownErr := errors.New("aux signer shutdown") + + // We know that aux sig jobs will be checked in SignNextCommitment() in + // ascending output index order. So we'll fail on the first job that is + // out of order, i.e. with an output index greater than its position in + // the submitted jobs slice. If the jobs are ordered, we'll fail on the + // job that is at the middle of the submitted job slice. + failAuxSigJob := func(jobs []AuxSigJob) { + for idx, sigJob := range jobs { + // Simulate a clean shutdown of the aux signer and send + // an error. Skip all remaining jobs. + isMiddleJob := idx == len(jobs)/2 + if int(sigJob.OutputIndex) > idx || isMiddleJob { + sigJob.Resp <- AuxSigJobResp{ + Err: auxSignerShutdownErr, + } + + return + } + + // If the job is 'in order', send a response with no + // error. + sigJob.Resp <- AuxSigJobResp{} + } + } + + auxSigner := NewAuxSignerMock(failAuxSigJob) + aliceChannel.auxSigner = fn.Some[AuxSigner](auxSigner) + + // Each HTLC amount is 0.01 BTC. + htlcAmt := lnwire.NewMSatFromSatoshis(0.01 * btcutil.SatoshiPerBitcoin) + + // Create enough HTLCs to create multiple sig jobs (one job per HTLC). + const numHTLCs = 24 + + // Send the specified number of HTLCs. + for i := 0; i < numHTLCs; i++ { + htlc, _ := createHTLC(i, htlcAmt) + addAndReceiveHTLC(t, aliceChannel, bobChannel, htlc, nil) + } + + // We'll set up the mock aux signer to expect calls to PackSigs and also + // SubmitSecondLevelSigBatch. The direct return values for this mock aux + // signer are nil. The expected error comes from the sig jobs being + // passed to failAuxSigJob above, which mimics a faulty aux signer. + var sigBlobBuf bytes.Buffer + sigBlob := testSigBlob{ + BlobInt: tlv.NewPrimitiveRecord[tlv.TlvType65634, uint16](5), + } + tlvStream, err := tlv.NewStream(sigBlob.BlobInt.Record()) + require.NoError(t, err, "unable to create tlv stream") + require.NoError(t, tlvStream.Encode(&sigBlobBuf)) + + auxSigner.On( + "SubmitSecondLevelSigBatch", mock.Anything, mock.Anything, + mock.Anything, + ).Return(nil).Twice() + auxSigner.On( + "PackSigs", mock.Anything, + ).Return( + fn.Some(sigBlobBuf.Bytes()), nil, + ) + + _, err = aliceChannel.SignNextCommitment() + require.ErrorIs(t, err, auxSignerShutdownErr) +} + func testChanSyncOweCommitmentPendingRemote(t *testing.T, chanType channeldb.ChannelType) { From 48567e8944818a9d1b9a9ab71d4c2a5c8a3024ac Mon Sep 17 00:00:00 2001 From: Jonathan Harvey-Buschel Date: Thu, 17 Oct 2024 13:38:30 +0200 Subject: [PATCH 4/9] lnwallet: sort sig jobs before submission --- lnwallet/channel.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lnwallet/channel.go b/lnwallet/channel.go index 9417c8e60..fedc7e4d2 100644 --- a/lnwallet/channel.go +++ b/lnwallet/channel.go @@ -2,6 +2,7 @@ package lnwallet import ( "bytes" + "cmp" "crypto/sha256" "errors" "fmt" @@ -3996,10 +3997,10 @@ func (lc *LightningChannel) SignNextCommitment() (*NewCommitState, error) { // order as they appear on the commitment transaction after BIP 69 // sorting. slices.SortFunc(sigBatch, func(i, j SignJob) int { - return int(i.OutputIndex - j.OutputIndex) + return cmp.Compare(i.OutputIndex, j.OutputIndex) }) slices.SortFunc(auxSigBatch, func(i, j AuxSigJob) int { - return int(i.OutputIndex - j.OutputIndex) + return cmp.Compare(i.OutputIndex, j.OutputIndex) }) lc.sigPool.SubmitSignBatch(sigBatch) From cd14c52ecd066ac3c434fc5b38915b06867a1449 Mon Sep 17 00:00:00 2001 From: Jonathan Harvey-Buschel Date: Thu, 17 Oct 2024 13:38:31 +0200 Subject: [PATCH 5/9] htlcswitch: pass quit chans as unidirectional This is a requirement for replacing the quit channel with a Context. The Done() channel of a Context is always recv-only, so all users of that channel must not expect a bidirectional channel. --- htlcswitch/interceptable_switch.go | 6 ++--- htlcswitch/link.go | 2 +- htlcswitch/link_test.go | 40 ++++++++++++++++++------------ htlcswitch/mailbox.go | 4 +-- htlcswitch/mailbox_test.go | 4 +-- htlcswitch/switch.go | 4 +-- htlcswitch/test_utils.go | 18 ++++++++------ 7 files changed, 45 insertions(+), 33 deletions(-) diff --git a/htlcswitch/interceptable_switch.go b/htlcswitch/interceptable_switch.go index e06163d48..71302bf07 100644 --- a/htlcswitch/interceptable_switch.go +++ b/htlcswitch/interceptable_switch.go @@ -95,7 +95,7 @@ type InterceptableSwitch struct { type interceptedPackets struct { packets []*htlcPacket - linkQuit chan struct{} + linkQuit <-chan struct{} isReplay bool } @@ -465,8 +465,8 @@ func (s *InterceptableSwitch) Resolve(res *FwdResolution) error { // interceptor. If the interceptor signals the resume action, the htlcs are // forwarded to the switch. The link's quit signal should be provided to allow // cancellation of forwarding during link shutdown. -func (s *InterceptableSwitch) ForwardPackets(linkQuit chan struct{}, isReplay bool, - packets ...*htlcPacket) error { +func (s *InterceptableSwitch) ForwardPackets(linkQuit <-chan struct{}, + isReplay bool, packets ...*htlcPacket) error { // Synchronize with the main event loop. This should be light in the // case where there is no interceptor. diff --git a/htlcswitch/link.go b/htlcswitch/link.go index e7766bc81..d395a94b4 100644 --- a/htlcswitch/link.go +++ b/htlcswitch/link.go @@ -101,7 +101,7 @@ type ChannelLinkConfig struct { // switch. The function returns and error in case it fails to send one or // more packets. The link's quit signal should be provided to allow // cancellation of forwarding during link shutdown. - ForwardPackets func(chan struct{}, bool, ...*htlcPacket) error + ForwardPackets func(<-chan struct{}, bool, ...*htlcPacket) error // DecodeHopIterators facilitates batched decoding of HTLC Sphinx onion // blobs, which are then used to inform how to forward an HTLC. diff --git a/htlcswitch/link_test.go b/htlcswitch/link_test.go index 018601a04..e473f9111 100644 --- a/htlcswitch/link_test.go +++ b/htlcswitch/link_test.go @@ -2197,17 +2197,21 @@ func newSingleLinkTestHarness(t *testing.T, chanAmt, return nil } + forwardPackets := func(linkQuit <-chan struct{}, _ bool, + packets ...*htlcPacket) error { + + return aliceSwitch.ForwardPackets(linkQuit, packets...) + } + // Instantiate with a long interval, so that we can precisely control // the firing via force feeding. bticker := ticker.NewForce(time.Hour) aliceCfg := ChannelLinkConfig{ - FwrdingPolicy: globalPolicy, - Peer: alicePeer, - BestHeight: aliceSwitch.BestHeight, - Circuits: aliceSwitch.CircuitModifier(), - ForwardPackets: func(linkQuit chan struct{}, _ bool, packets ...*htlcPacket) error { - return aliceSwitch.ForwardPackets(linkQuit, packets...) - }, + FwrdingPolicy: globalPolicy, + Peer: alicePeer, + BestHeight: aliceSwitch.BestHeight, + Circuits: aliceSwitch.CircuitModifier(), + ForwardPackets: forwardPackets, DecodeHopIterators: decoder.DecodeHopIterators, ExtractErrorEncrypter: func(*btcec.PublicKey) ( hop.ErrorEncrypter, lnwire.FailCode) { @@ -4867,17 +4871,21 @@ func (h *persistentLinkHarness) restartLink( return nil } + forwardPackets := func(linkQuit <-chan struct{}, _ bool, + packets ...*htlcPacket) error { + + return h.hSwitch.ForwardPackets(linkQuit, packets...) + } + // Instantiate with a long interval, so that we can precisely control // the firing via force feeding. bticker := ticker.NewForce(time.Hour) aliceCfg := ChannelLinkConfig{ - FwrdingPolicy: globalPolicy, - Peer: alicePeer, - BestHeight: h.hSwitch.BestHeight, - Circuits: h.hSwitch.CircuitModifier(), - ForwardPackets: func(linkQuit chan struct{}, _ bool, packets ...*htlcPacket) error { - return h.hSwitch.ForwardPackets(linkQuit, packets...) - }, + FwrdingPolicy: globalPolicy, + Peer: alicePeer, + BestHeight: h.hSwitch.BestHeight, + Circuits: h.hSwitch.CircuitModifier(), + ForwardPackets: forwardPackets, DecodeHopIterators: decoder.DecodeHopIterators, ExtractErrorEncrypter: func(*btcec.PublicKey) ( hop.ErrorEncrypter, lnwire.FailCode) { @@ -7037,7 +7045,7 @@ func TestPipelineSettle(t *testing.T) { // erroneously forwarded. If the forwardChan is closed before the last // step, then the test will fail. forwardChan := make(chan struct{}) - fwdPkts := func(c chan struct{}, _ bool, hp ...*htlcPacket) error { + fwdPkts := func(c <-chan struct{}, _ bool, hp ...*htlcPacket) error { close(forwardChan) return nil } @@ -7223,7 +7231,7 @@ func TestChannelLinkShortFailureRelay(t *testing.T) { aliceMsgs := mockPeer.sentMsgs switchChan := make(chan *htlcPacket) - coreLink.cfg.ForwardPackets = func(linkQuit chan struct{}, _ bool, + coreLink.cfg.ForwardPackets = func(linkQuit <-chan struct{}, _ bool, packets ...*htlcPacket) error { for _, p := range packets { diff --git a/htlcswitch/mailbox.go b/htlcswitch/mailbox.go index 9b82f8912..b283825dd 100644 --- a/htlcswitch/mailbox.go +++ b/htlcswitch/mailbox.go @@ -95,7 +95,7 @@ type mailBoxConfig struct { // forwardPackets send a varidic number of htlcPackets to the switch to // be routed. A quit channel should be provided so that the call can // properly exit during shutdown. - forwardPackets func(chan struct{}, ...*htlcPacket) error + forwardPackets func(<-chan struct{}, ...*htlcPacket) error // clock is a time source for the mailbox. clock clock.Clock @@ -804,7 +804,7 @@ type mailOrchConfig struct { // forwardPackets send a varidic number of htlcPackets to the switch to // be routed. A quit channel should be provided so that the call can // properly exit during shutdown. - forwardPackets func(chan struct{}, ...*htlcPacket) error + forwardPackets func(<-chan struct{}, ...*htlcPacket) error // clock is a time source for the generated mailboxes. clock clock.Clock diff --git a/htlcswitch/mailbox_test.go b/htlcswitch/mailbox_test.go index dfb0fb616..57a581c4b 100644 --- a/htlcswitch/mailbox_test.go +++ b/htlcswitch/mailbox_test.go @@ -250,7 +250,7 @@ func newMailboxContext(t *testing.T, startTime time.Time, return ctx } -func (c *mailboxContext) forward(_ chan struct{}, +func (c *mailboxContext) forward(_ <-chan struct{}, pkts ...*htlcPacket) error { for _, pkt := range pkts { @@ -706,7 +706,7 @@ func TestMailOrchestrator(t *testing.T) { // First, we'll create a new instance of our orchestrator. mo := newMailOrchestrator(&mailOrchConfig{ failMailboxUpdate: failMailboxUpdate, - forwardPackets: func(_ chan struct{}, + forwardPackets: func(_ <-chan struct{}, pkts ...*htlcPacket) error { return nil diff --git a/htlcswitch/switch.go b/htlcswitch/switch.go index 4ff2746a7..10e4d37bf 100644 --- a/htlcswitch/switch.go +++ b/htlcswitch/switch.go @@ -672,7 +672,7 @@ func (s *Switch) IsForwardedHTLC(chanID lnwire.ShortChannelID, // given to forward them through the router. The sending link's quit channel is // used to prevent deadlocks when the switch stops a link in the midst of // forwarding. -func (s *Switch) ForwardPackets(linkQuit chan struct{}, +func (s *Switch) ForwardPackets(linkQuit <-chan struct{}, packets ...*htlcPacket) error { var ( @@ -850,7 +850,7 @@ func (s *Switch) logFwdErrs(num *int, wg *sync.WaitGroup, fwdChan chan error) { // receive a shutdown requuest. This method does not wait for a response from // the htlcForwarder before returning. func (s *Switch) routeAsync(packet *htlcPacket, errChan chan error, - linkQuit chan struct{}) error { + linkQuit <-chan struct{}) error { command := &plexPacket{ pkt: packet, diff --git a/htlcswitch/test_utils.go b/htlcswitch/test_utils.go index 450d5a19d..5ef83c8f1 100644 --- a/htlcswitch/test_utils.go +++ b/htlcswitch/test_utils.go @@ -1144,15 +1144,19 @@ func (h *hopNetwork) createChannelLink(server, peer *mockServer, return nil } + forwardPackets := func(linkQuit <-chan struct{}, _ bool, + packets ...*htlcPacket) error { + + return server.htlcSwitch.ForwardPackets(linkQuit, packets...) + } + link := NewChannelLink( ChannelLinkConfig{ - BestHeight: server.htlcSwitch.BestHeight, - FwrdingPolicy: h.globalPolicy, - Peer: peer, - Circuits: server.htlcSwitch.CircuitModifier(), - ForwardPackets: func(linkQuit chan struct{}, _ bool, packets ...*htlcPacket) error { - return server.htlcSwitch.ForwardPackets(linkQuit, packets...) - }, + BestHeight: server.htlcSwitch.BestHeight, + FwrdingPolicy: h.globalPolicy, + Peer: peer, + Circuits: server.htlcSwitch.CircuitModifier(), + ForwardPackets: forwardPackets, DecodeHopIterators: decoder.DecodeHopIterators, ExtractErrorEncrypter: func(*btcec.PublicKey) ( hop.ErrorEncrypter, lnwire.FailCode) { From 6edd942c7079dec772422bb086be626b2647525a Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Thu, 17 Oct 2024 13:38:33 +0200 Subject: [PATCH 6/9] mod: bump fn to v1.2.3 --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 290a13faf..b1e63f8cf 100644 --- a/go.mod +++ b/go.mod @@ -35,7 +35,7 @@ require ( github.com/lightningnetwork/lightning-onion v1.2.1-0.20240712235311-98bd56499dfb github.com/lightningnetwork/lnd/cert v1.2.2 github.com/lightningnetwork/lnd/clock v1.1.1 - github.com/lightningnetwork/lnd/fn v1.2.2 + github.com/lightningnetwork/lnd/fn v1.2.3 github.com/lightningnetwork/lnd/healthcheck v1.2.5 github.com/lightningnetwork/lnd/kvdb v1.4.10 github.com/lightningnetwork/lnd/queue v1.1.1 diff --git a/go.sum b/go.sum index 4f0d975d8..5c3b97e88 100644 --- a/go.sum +++ b/go.sum @@ -453,8 +453,8 @@ github.com/lightningnetwork/lnd/cert v1.2.2 h1:71YK6hogeJtxSxw2teq3eGeuy4rHGKcFf github.com/lightningnetwork/lnd/cert v1.2.2/go.mod h1:jQmFn/Ez4zhDgq2hnYSw8r35bqGVxViXhX6Cd7HXM6U= github.com/lightningnetwork/lnd/clock v1.1.1 h1:OfR3/zcJd2RhH0RU+zX/77c0ZiOnIMsDIBjgjWdZgA0= github.com/lightningnetwork/lnd/clock v1.1.1/go.mod h1:mGnAhPyjYZQJmebS7aevElXKTFDuO+uNFFfMXK1W8xQ= -github.com/lightningnetwork/lnd/fn v1.2.2 h1:rVtmGW1cQTmYce2XdUbRcc5qLDxqu+aQ6IGRpyspakk= -github.com/lightningnetwork/lnd/fn v1.2.2/go.mod h1:SyFohpVrARPKH3XVAJZlXdVe+IwMYc4OMAvrDY32kw0= +github.com/lightningnetwork/lnd/fn v1.2.3 h1:Q1OrgNSgQynVheBNa16CsKVov1JI5N2AR6G07x9Mles= +github.com/lightningnetwork/lnd/fn v1.2.3/go.mod h1:SyFohpVrARPKH3XVAJZlXdVe+IwMYc4OMAvrDY32kw0= github.com/lightningnetwork/lnd/healthcheck v1.2.5 h1:aTJy5xeBpcWgRtW/PGBDe+LMQEmNm/HQewlQx2jt7OA= github.com/lightningnetwork/lnd/healthcheck v1.2.5/go.mod h1:G7Tst2tVvWo7cx6mSBEToQC5L1XOGxzZTPB29g9Rv2I= github.com/lightningnetwork/lnd/kvdb v1.4.10 h1:vK89IVv1oVH9ubQWU+EmoCQFeVRaC8kfmOrqHbY5zoY= From 77ae7afe7811e71ba86bcfe7357b938015e2209e Mon Sep 17 00:00:00 2001 From: Jonathan Harvey-Buschel Date: Thu, 17 Oct 2024 13:38:34 +0200 Subject: [PATCH 7/9] multi: link quit can interrupt commitment signing In this commit, we make sig job handling when singing a next commitment non-blocking by allowing the shutdown of a channel link to prevent further waiting on sig jobs by the channel state machine. This addresses possible cases where the aux signer may be shut down via a separate quit signal, so the state machine could block indefinitely on receiving an update on a sig job. --- contractcourt/chain_watcher_test.go | 13 +- htlcswitch/link.go | 51 +++--- htlcswitch/link_isolated_test.go | 5 +- htlcswitch/link_test.go | 54 ++++-- htlcswitch/test_utils.go | 14 +- lnwallet/channel.go | 31 +++- lnwallet/channel_test.go | 252 +++++++++++++++------------- lnwallet/test_utils.go | 9 +- lnwallet/transactions_test.go | 4 +- 9 files changed, 256 insertions(+), 177 deletions(-) diff --git a/contractcourt/chain_watcher_test.go b/contractcourt/chain_watcher_test.go index 489a60518..2781170f0 100644 --- a/contractcourt/chain_watcher_test.go +++ b/contractcourt/chain_watcher_test.go @@ -2,6 +2,7 @@ package contractcourt import ( "bytes" + "context" "crypto/sha256" "fmt" "testing" @@ -145,17 +146,15 @@ func TestChainWatcherRemoteUnilateralClosePendingCommit(t *testing.T) { // With the HTLC added, we'll now manually initiate a state transition // from Alice to Bob. - _, err = aliceChannel.SignNextCommitment() - if err != nil { - t.Fatal(err) - } + testQuit, testQuitFunc := context.WithCancel(context.Background()) + t.Cleanup(testQuitFunc) + _, err = aliceChannel.SignNextCommitment(testQuit) + require.NoError(t, err) // At this point, we'll now Bob broadcasting this new pending unrevoked // commitment. bobPendingCommit, err := aliceChannel.State().RemoteCommitChainTip() - if err != nil { - t.Fatal(err) - } + require.NoError(t, err) // We'll craft a fake spend notification with Bob's actual commitment. // The chain watcher should be able to detect that this is a pending diff --git a/htlcswitch/link.go b/htlcswitch/link.go index d395a94b4..7ce618574 100644 --- a/htlcswitch/link.go +++ b/htlcswitch/link.go @@ -387,8 +387,10 @@ type channelLink struct { // our next CommitSig. incomingCommitHooks hookMap - wg sync.WaitGroup - quit chan struct{} + // ContextGuard is a helper that encapsulates a wait group and quit + // channel and allows contexts that either block or cancel on those + // depending on the use case. + *fn.ContextGuard } // hookMap is a data structure that is used to track the hooks that need to be @@ -469,7 +471,7 @@ func NewChannelLink(cfg ChannelLinkConfig, flushHooks: newHookMap(), outgoingCommitHooks: newHookMap(), incomingCommitHooks: newHookMap(), - quit: make(chan struct{}), + ContextGuard: fn.NewContextGuard(), } } @@ -548,7 +550,7 @@ func (l *channelLink) Start() error { l.updateFeeTimer = time.NewTimer(l.randomFeeUpdateTimeout()) - l.wg.Add(1) + l.Wg.Add(1) go l.htlcManager() return nil @@ -588,8 +590,8 @@ func (l *channelLink) Stop() { l.hodlQueue.Stop() } - close(l.quit) - l.wg.Wait() + close(l.Quit) + l.Wg.Wait() // Now that the htlcManager has completely exited, reset the packet // courier. This allows the mailbox to revaluate any lingering Adds that @@ -614,7 +616,7 @@ func (l *channelLink) Stop() { // WaitForShutdown blocks until the link finishes shutting down, which includes // termination of all dependent goroutines. func (l *channelLink) WaitForShutdown() { - l.wg.Wait() + l.Wg.Wait() } // EligibleToForward returns a bool indicating if the channel is able to @@ -675,7 +677,7 @@ func (l *channelLink) IsFlushing(linkDirection LinkDirection) bool { func (l *channelLink) OnFlushedOnce(hook func()) { select { case l.flushHooks.newTransients <- hook: - case <-l.quit: + case <-l.Quit: } } @@ -694,7 +696,7 @@ func (l *channelLink) OnCommitOnce(direction LinkDirection, hook func()) { select { case queue <- hook: - case <-l.quit: + case <-l.Quit: } } @@ -903,8 +905,10 @@ func (l *channelLink) syncChanStates() error { // We've just received a ChanSync message from the remote // party, so we'll process the message in order to determine // if we need to re-transmit any messages to the remote party. + ctx, cancel := l.WithCtxQuitNoTimeout() + defer cancel() msgsToReSend, openedCircuits, closedCircuits, err = - l.channel.ProcessChanSyncMsg(remoteChanSyncMsg) + l.channel.ProcessChanSyncMsg(ctx, remoteChanSyncMsg) if err != nil { return err } @@ -933,7 +937,7 @@ func (l *channelLink) syncChanStates() error { l.cfg.Peer.SendMessage(false, msg) } - case <-l.quit: + case <-l.Quit: return ErrLinkShuttingDown } @@ -1023,7 +1027,7 @@ func (l *channelLink) resolveFwdPkg(fwdPkg *channeldb.FwdPkg) error { // // NOTE: This MUST be run as a goroutine. func (l *channelLink) fwdPkgGarbager() { - defer l.wg.Done() + defer l.Wg.Done() l.cfg.FwdPkgGCTicker.Resume() defer l.cfg.FwdPkgGCTicker.Stop() @@ -1040,7 +1044,7 @@ func (l *channelLink) fwdPkgGarbager() { err) continue } - case <-l.quit: + case <-l.Quit: return } } @@ -1163,7 +1167,7 @@ func (l *channelLink) handleChanSyncErr(err error) { func (l *channelLink) htlcManager() { defer func() { l.cfg.BatchTicker.Stop() - l.wg.Done() + l.Wg.Done() l.log.Infof("exited") }() @@ -1257,7 +1261,7 @@ func (l *channelLink) htlcManager() { // With our link's in-memory state fully reconstructed, spawn a // goroutine to manage the reclamation of disk space occupied by // completed forwarding packages. - l.wg.Add(1) + l.Wg.Add(1) go l.fwdPkgGarbager() } @@ -1441,7 +1445,7 @@ func (l *channelLink) htlcManager() { ) } - case <-l.quit: + case <-l.Quit: return } } @@ -2299,7 +2303,7 @@ func (l *channelLink) handleUpstreamMsg(msg lnwire.Message) { } select { - case <-l.quit: + case <-l.Quit: return default: } @@ -2360,7 +2364,7 @@ func (l *channelLink) handleUpstreamMsg(msg lnwire.Message) { } select { - case <-l.quit: + case <-l.Quit: return default: } @@ -2590,7 +2594,10 @@ func (l *channelLink) updateCommitTx() error { return nil } - newCommit, err := l.channel.SignNextCommitment() + ctx, done := l.WithCtxQuitNoTimeout() + defer done() + + newCommit, err := l.channel.SignNextCommitment(ctx) if err == lnwallet.ErrNoWindow { l.cfg.PendingCommitTicker.Resume() l.log.Trace("PendingCommitTicker resumed") @@ -2627,7 +2634,7 @@ func (l *channelLink) updateCommitTx() error { } select { - case <-l.quit: + case <-l.Quit: return ErrLinkShuttingDown default: } @@ -3233,7 +3240,7 @@ func (l *channelLink) handleSwitchPacket(pkt *htlcPacket) error { // NOTE: Part of the ChannelLink interface. func (l *channelLink) HandleChannelUpdate(message lnwire.Message) { select { - case <-l.quit: + case <-l.Quit: // Return early if the link is already in the process of // quitting. It doesn't make sense to hand the message to the // mailbox here. @@ -3932,7 +3939,7 @@ func (l *channelLink) forwardBatch(replay bool, packets ...*htlcPacket) { filteredPkts = append(filteredPkts, pkt) } - err := l.cfg.ForwardPackets(l.quit, replay, filteredPkts...) + err := l.cfg.ForwardPackets(l.Quit, replay, filteredPkts...) if err != nil { log.Errorf("Unhandled error while reforwarding htlc "+ "settle/fail over htlcswitch: %v", err) diff --git a/htlcswitch/link_isolated_test.go b/htlcswitch/link_isolated_test.go index 5d769d920..5281cbed7 100644 --- a/htlcswitch/link_isolated_test.go +++ b/htlcswitch/link_isolated_test.go @@ -1,6 +1,7 @@ package htlcswitch import ( + "context" "crypto/sha256" "testing" "time" @@ -94,7 +95,9 @@ func (l *linkTestContext) receiveHtlcAliceToBob() { func (l *linkTestContext) sendCommitSigBobToAlice(expHtlcs int) { l.t.Helper() - sigs, err := l.bobChannel.SignNextCommitment() + testQuit, testQuitFunc := context.WithCancel(context.Background()) + defer testQuitFunc() + sigs, err := l.bobChannel.SignNextCommitment(testQuit) if err != nil { l.t.Fatalf("error signing commitment: %v", err) } diff --git a/htlcswitch/link_test.go b/htlcswitch/link_test.go index e473f9111..5bfb79ee0 100644 --- a/htlcswitch/link_test.go +++ b/htlcswitch/link_test.go @@ -2252,12 +2252,14 @@ func newSingleLinkTestHarness(t *testing.T, chanAmt, return aliceSwitch.AddLink(aliceLink) } go func() { - for { - select { - case <-notifyUpdateChan: - case <-aliceLink.(*channelLink).quit: - close(doneChan) - return + if chanLink, ok := aliceLink.(*channelLink); ok { + for { + select { + case <-notifyUpdateChan: + case <-chanLink.Quit: + close(doneChan) + return + } } } }() @@ -2324,7 +2326,10 @@ func handleStateUpdate(link *channelLink, } link.HandleChannelUpdate(remoteRev) - remoteSigs, err := remoteChannel.SignNextCommitment() + ctx, done := link.WithCtxQuitNoTimeout() + defer done() + + remoteSigs, err := remoteChannel.SignNextCommitment(ctx) if err != nil { return err } @@ -2367,7 +2372,7 @@ func updateState(batchTick chan time.Time, link *channelLink, // Trigger update by ticking the batchTicker. select { case batchTick <- time.Now(): - case <-link.quit: + case <-link.Quit: return fmt.Errorf("link shutting down") } return handleStateUpdate(link, remoteChannel) @@ -2375,7 +2380,10 @@ func updateState(batchTick chan time.Time, link *channelLink, // The remote is triggering the state update, emulate this by // signing and sending CommitSig to the link. - remoteSigs, err := remoteChannel.SignNextCommitment() + ctx, done := link.WithCtxQuitNoTimeout() + defer done() + + remoteSigs, err := remoteChannel.SignNextCommitment(ctx) if err != nil { return err } @@ -4931,12 +4939,14 @@ func (h *persistentLinkHarness) restartLink( return nil, nil, err } go func() { - for { - select { - case <-notifyUpdateChan: - case <-aliceLink.(*channelLink).quit: - close(doneChan) - return + if chanLink, ok := aliceLink.(*channelLink); ok { + for { + select { + case <-notifyUpdateChan: + case <-chanLink.Quit: + close(doneChan) + return + } } } }() @@ -5919,7 +5929,12 @@ func TestChannelLinkFail(t *testing.T) { // Sign a commitment that will include // signature for the HTLC just sent. - sigs, err := remoteChannel.SignNextCommitment() + quitCtx, done := c.WithCtxQuitNoTimeout() + defer done() + + sigs, err := remoteChannel.SignNextCommitment( + quitCtx, + ) if err != nil { t.Fatalf("error signing commitment: %v", err) @@ -5961,7 +5976,12 @@ func TestChannelLinkFail(t *testing.T) { // Sign a commitment that will include // signature for the HTLC just sent. - sigs, err := remoteChannel.SignNextCommitment() + quitCtx, done := c.WithCtxQuitNoTimeout() + defer done() + + sigs, err := remoteChannel.SignNextCommitment( + quitCtx, + ) if err != nil { t.Fatalf("error signing commitment: %v", err) diff --git a/htlcswitch/test_utils.go b/htlcswitch/test_utils.go index 5ef83c8f1..427dc8cb5 100644 --- a/htlcswitch/test_utils.go +++ b/htlcswitch/test_utils.go @@ -1197,12 +1197,14 @@ func (h *hopNetwork) createChannelLink(server, peer *mockServer, } go func() { - for { - select { - case <-notifyUpdateChan: - case <-link.(*channelLink).quit: - close(doneChan) - return + if chanLink, ok := link.(*channelLink); ok { + for { + select { + case <-notifyUpdateChan: + case <-chanLink.Quit: + close(doneChan) + return + } } } }() diff --git a/lnwallet/channel.go b/lnwallet/channel.go index fedc7e4d2..3a980df1a 100644 --- a/lnwallet/channel.go +++ b/lnwallet/channel.go @@ -3,6 +3,7 @@ package lnwallet import ( "bytes" "cmp" + "context" "crypto/sha256" "errors" "fmt" @@ -138,6 +139,10 @@ var ( // errNoPartialSig is returned when a partial signature is required, // but none is found. errNoPartialSig = errors.New("no partial signature found") + + // errQuit is returned when a quit signal was received, interrupting the + // current operation. + errQuit = errors.New("received quit signal") ) // ErrCommitSyncLocalDataLoss is returned in the case that we receive a valid @@ -3893,7 +3898,9 @@ type NewCommitState struct { // for the remote party's commitment are also returned. // //nolint:funlen -func (lc *LightningChannel) SignNextCommitment() (*NewCommitState, error) { +func (lc *LightningChannel) SignNextCommitment( + ctx context.Context) (*NewCommitState, error) { + lc.Lock() defer lc.Unlock() @@ -4059,7 +4066,13 @@ func (lc *LightningChannel) SignNextCommitment() (*NewCommitState, error) { auxSigs := make([]fn.Option[tlv.Blob], 0, len(auxSigBatch)) for i := range sigBatch { htlcSigJob := sigBatch[i] - jobResp := <-htlcSigJob.Resp + var jobResp SignJobResp + + select { + case jobResp = <-htlcSigJob.Resp: + case <-ctx.Done(): + return nil, errQuit + } // If an error occurred, then we'll cancel any other active // jobs. @@ -4075,7 +4088,13 @@ func (lc *LightningChannel) SignNextCommitment() (*NewCommitState, error) { } auxHtlcSigJob := auxSigBatch[i] - auxJobResp := <-auxHtlcSigJob.Resp + var auxJobResp AuxSigJobResp + + select { + case auxJobResp = <-auxHtlcSigJob.Resp: + case <-ctx.Done(): + return nil, errQuit + } // If an error occurred, then we'll cancel any other active // jobs. @@ -4169,7 +4188,9 @@ func (lc *LightningChannel) resignMusigCommit( // previous commitment txn. This allows the link to clear its mailbox of those // circuits in case they are still in memory, and ensure the switch's circuit // map has been updated by deleting the closed circuits. -func (lc *LightningChannel) ProcessChanSyncMsg( +// +//nolint:funlen +func (lc *LightningChannel) ProcessChanSyncMsg(ctx context.Context, msg *lnwire.ChannelReestablish) ([]lnwire.Message, []models.CircuitKey, []models.CircuitKey, error) { @@ -4333,7 +4354,7 @@ func (lc *LightningChannel) ProcessChanSyncMsg( // revocation, but also initiate a state transition to re-sync // them. if lc.OweCommitment() { - newCommit, err := lc.SignNextCommitment() + newCommit, err := lc.SignNextCommitment(ctx) switch { // If we signed this state, then we'll accumulate diff --git a/lnwallet/channel_test.go b/lnwallet/channel_test.go index f3fa052fe..ae2f474f2 100644 --- a/lnwallet/channel_test.go +++ b/lnwallet/channel_test.go @@ -124,7 +124,7 @@ func testAddSettleWorkflow(t *testing.T, tweakless bool, // we expect the messages to be ordered, Bob will receive the HTLC we // just sent before he receives this signature, so the signature will // cover the HTLC. - aliceNewCommit, err := aliceChannel.SignNextCommitment() + aliceNewCommit, err := aliceChannel.SignNextCommitment(ctxb) require.NoError(t, err, "alice unable to sign commitment") // Bob receives this signature message, and checks that this covers the @@ -142,7 +142,7 @@ func testAddSettleWorkflow(t *testing.T, tweakless bool, // This signature will cover the HTLC, since Bob will first send the // revocation just created. The revocation also acks every received // HTLC up to the point where Alice sent here signature. - bobNewCommit, err := bobChannel.SignNextCommitment() + bobNewCommit, err := bobChannel.SignNextCommitment(ctxb) require.NoError(t, err, "bob unable to sign alice's commitment") // Alice then processes this revocation, sending her own revocation for @@ -255,14 +255,14 @@ func testAddSettleWorkflow(t *testing.T, tweakless bool, t.Fatalf("alice unable to accept settle of outbound htlc: %v", err) } - bobNewCommit, err = bobChannel.SignNextCommitment() + bobNewCommit, err = bobChannel.SignNextCommitment(ctxb) require.NoError(t, err, "bob unable to sign settle commitment") err = aliceChannel.ReceiveNewCommitment(bobNewCommit.CommitSigs) require.NoError(t, err, "alice unable to process bob's new commitment") aliceRevocation2, _, _, err := aliceChannel.RevokeCurrentCommitment() require.NoError(t, err, "alice unable to generate revocation") - aliceNewCommit, err = aliceChannel.SignNextCommitment() + aliceNewCommit, err = aliceChannel.SignNextCommitment(ctxb) require.NoError(t, err, "alice unable to sign new commitment") fwdPkg, _, err = bobChannel.ReceiveRevocation(aliceRevocation2) @@ -470,7 +470,7 @@ func TestChannelZeroAddLocalHeight(t *testing.T) { // Bob should send a commitment signature to Alice. // <----sig------ - bobNewCommit, err := bobChannel.SignNextCommitment() + bobNewCommit, err := bobChannel.SignNextCommitment(ctxb) require.NoError(t, err) err = aliceChannel.ReceiveNewCommitment(bobNewCommit.CommitSigs) @@ -501,7 +501,7 @@ func TestChannelZeroAddLocalHeight(t *testing.T) { // Bob should now send a commitment signature to Alice. // <----sig----- - bobNewCommit, err = bobChannel.SignNextCommitment() + bobNewCommit, err = bobChannel.SignNextCommitment(ctxb) require.NoError(t, err) // Alice should accept the commitment. Previously she would @@ -636,7 +636,7 @@ func testCommitHTLCSigTieBreak(t *testing.T, restart bool) { // tie-breaking for commitment sorting won't affect the commitment // signed by Alice because received HTLC scripts commit to the CLTV // directly, so the outputs will have different scriptPubkeys. - aliceNewCommit, err := aliceChannel.SignNextCommitment() + aliceNewCommit, err := aliceChannel.SignNextCommitment(ctxb) require.NoError(t, err, "unable to sign alice's commitment") err = bobChannel.ReceiveNewCommitment(aliceNewCommit.CommitSigs) @@ -651,7 +651,7 @@ func testCommitHTLCSigTieBreak(t *testing.T, restart bool) { // the offered HTLC scripts he adds for Alice will need to have the // tie-breaking applied because the CLTV is not committed, but instead // implicit via the construction of the second-level transactions. - bobNewCommit, err := bobChannel.SignNextCommitment() + bobNewCommit, err := bobChannel.SignNextCommitment(ctxb) require.NoError(t, err, "unable to sign bob's commitment") if len(bobNewCommit.PendingHTLCs) != numHtlcs { @@ -760,7 +760,7 @@ func TestCommitHTLCSigCustomRecordSize(t *testing.T) { } // We expect an error because of the large custom records blob. - _, err = aliceChannel.SignNextCommitment() + _, err = aliceChannel.SignNextCommitment(ctxb) require.ErrorContains(t, err, "exceeds max allowed size") } @@ -1574,7 +1574,7 @@ func TestHTLCSigNumber(t *testing.T) { // =================================================================== aliceChannel, bobChannel := createChanWithHTLC(aboveDust, aboveDust) - aliceNewCommit, err := aliceChannel.SignNextCommitment() + aliceNewCommit, err := aliceChannel.SignNextCommitment(ctxb) require.NoError(t, err, "Error signing next commitment") if len(aliceNewCommit.HtlcSigs) != 2 { @@ -1597,7 +1597,7 @@ func TestHTLCSigNumber(t *testing.T) { // =================================================================== aliceChannel, bobChannel = createChanWithHTLC(aboveDust) - aliceNewCommit, err = aliceChannel.SignNextCommitment() + aliceNewCommit, err = aliceChannel.SignNextCommitment(ctxb) require.NoError(t, err, "Error signing next commitment") if len(aliceNewCommit.HtlcSigs) != 1 { @@ -1619,7 +1619,7 @@ func TestHTLCSigNumber(t *testing.T) { // ============================================================== aliceChannel, bobChannel = createChanWithHTLC(belowDust) - aliceNewCommit, err = aliceChannel.SignNextCommitment() + aliceNewCommit, err = aliceChannel.SignNextCommitment(ctxb) require.NoError(t, err, "Error signing next commitment") // Since the HTLC is below Bob's dust limit, Alice won't need to send @@ -1637,7 +1637,7 @@ func TestHTLCSigNumber(t *testing.T) { // ================================================================ aliceChannel, bobChannel = createChanWithHTLC(aboveDust) - aliceNewCommit, err = aliceChannel.SignNextCommitment() + aliceNewCommit, err = aliceChannel.SignNextCommitment(ctxb) require.NoError(t, err, "Error signing next commitment") // Since the HTLC is above Bob's dust limit, Alice should send a @@ -1658,7 +1658,7 @@ func TestHTLCSigNumber(t *testing.T) { // Alice should produce only one signature, since one HTLC is below // dust. - aliceNewCommit, err = aliceChannel.SignNextCommitment() + aliceNewCommit, err = aliceChannel.SignNextCommitment(ctxb) require.NoError(t, err, "Error signing next commitment") if len(aliceNewCommit.HtlcSigs) != 1 { @@ -2428,7 +2428,7 @@ func TestUpdateFeeFail(t *testing.T) { // Alice sends signature for commitment that does not cover any fee // update. - aliceNewCommit, err := aliceChannel.SignNextCommitment() + aliceNewCommit, err := aliceChannel.SignNextCommitment(ctxb) require.NoError(t, err, "alice unable to sign commitment") // Bob verifies this commit, meaning that he checks that it is @@ -2471,11 +2471,11 @@ func TestUpdateFeeConcurrentSig(t *testing.T) { } // Alice signs a commitment, and sends this to bob. - aliceNewCommits, err := aliceChannel.SignNextCommitment() + aliceNewCommits, err := aliceChannel.SignNextCommitment(ctxb) require.NoError(t, err, "alice unable to sign commitment") // At the same time, Bob signs a commitment. - bobNewCommits, err := bobChannel.SignNextCommitment() + bobNewCommits, err := bobChannel.SignNextCommitment(ctxb) require.NoError(t, err, "bob unable to sign alice's commitment") // ...that Alice receives. @@ -2542,7 +2542,7 @@ func TestUpdateFeeSenderCommits(t *testing.T) { // Alice signs a commitment, which will cover everything sent to Bob // (the HTLC and the fee update), and everything acked by Bob (nothing // so far). - aliceNewCommits, err := aliceChannel.SignNextCommitment() + aliceNewCommits, err := aliceChannel.SignNextCommitment(ctxb) require.NoError(t, err, "alice unable to sign commitment") // Bob receives this signature message, and verifies that it is @@ -2572,7 +2572,7 @@ func TestUpdateFeeSenderCommits(t *testing.T) { // Bob commits to all updates he has received from Alice. This includes // the HTLC he received, and the fee update. - bobNewCommit, err := bobChannel.SignNextCommitment() + bobNewCommit, err := bobChannel.SignNextCommitment(ctxb) require.NoError(t, err, "bob unable to sign alice's commitment") // Alice receives the revocation of the old one, and can now assume @@ -2646,7 +2646,7 @@ func TestUpdateFeeReceiverCommits(t *testing.T) { // Bob commits to every change he has sent since last time (none). He // does not commit to the received HTLC and fee update, since Alice // cannot know if he has received them. - bobNewCommit, err := bobChannel.SignNextCommitment() + bobNewCommit, err := bobChannel.SignNextCommitment(ctxb) require.NoError(t, err, "alice unable to sign commitment") // Alice receives this signature message, and verifies that it is @@ -2667,7 +2667,7 @@ func TestUpdateFeeReceiverCommits(t *testing.T) { // Alice will sign next commitment. Since she sent the revocation, she // also ack'ed everything received, but in this case this is nothing. // Since she sent the two updates, this signature will cover those two. - aliceNewCommit, err := aliceChannel.SignNextCommitment() + aliceNewCommit, err := aliceChannel.SignNextCommitment(ctxb) require.NoError(t, err, "bob unable to sign alice's commitment") // Bob gets the signature for the new commitment from Alice. He assumes @@ -2697,7 +2697,7 @@ func TestUpdateFeeReceiverCommits(t *testing.T) { // Bob will send a new signature, which will cover what he just acked: // the HTLC and fee update. - bobNewCommit, err = bobChannel.SignNextCommitment() + bobNewCommit, err = bobChannel.SignNextCommitment(ctxb) require.NoError(t, err, "alice unable to sign commitment") // Alice receives revocation from Bob, and can now be sure that Bob @@ -2787,7 +2787,7 @@ func TestUpdateFeeMultipleUpdates(t *testing.T) { // Alice signs a commitment, which will cover everything sent to Bob // (the HTLC and the fee update), and everything acked by Bob (nothing // so far). - aliceNewCommit, err := aliceChannel.SignNextCommitment() + aliceNewCommit, err := aliceChannel.SignNextCommitment(ctxb) require.NoError(t, err, "alice unable to sign commitment") bobChannel.ReceiveUpdateFee(fee1) @@ -2833,7 +2833,7 @@ func TestUpdateFeeMultipleUpdates(t *testing.T) { // Bob commits to all updates he has received from Alice. This includes // the HTLC he received, and the fee update. - bobNewCommit, err := bobChannel.SignNextCommitment() + bobNewCommit, err := bobChannel.SignNextCommitment(ctxb) require.NoError(t, err, "bob unable to sign alice's commitment") // Alice receives the revocation of the old one, and can now assume that @@ -2939,7 +2939,9 @@ func assertNoChanSyncNeeded(t *testing.T, aliceChannel *LightningChannel, } } - bobMsgsToSend, _, _, err := bobChannel.ProcessChanSyncMsg(aliceChanSyncMsg) + bobMsgsToSend, _, _, err := bobChannel.ProcessChanSyncMsg( + ctxb, aliceChanSyncMsg, + ) if err != nil { t.Fatalf("line #%v: unable to process ChannelReestablish "+ "msg: %v", line, err) @@ -2949,7 +2951,9 @@ func assertNoChanSyncNeeded(t *testing.T, aliceChannel *LightningChannel, "instead wants to send: %v", line, spew.Sdump(bobMsgsToSend)) } - aliceMsgsToSend, _, _, err := aliceChannel.ProcessChanSyncMsg(bobChanSyncMsg) + aliceMsgsToSend, _, _, err := aliceChannel.ProcessChanSyncMsg( + ctxb, bobChanSyncMsg, + ) if err != nil { t.Fatalf("line #%v: unable to process ChannelReestablish "+ "msg: %v", line, err) @@ -3143,7 +3147,7 @@ func testChanSyncOweCommitment(t *testing.T, chanType channeldb.ChannelType) { // Now we'll begin the core of the test itself. Alice will extend a new // commitment to Bob, but the connection drops before Bob can process // it. - aliceNewCommit, err := aliceChannel.SignNextCommitment() + aliceNewCommit, err := aliceChannel.SignNextCommitment(ctxb) require.NoError(t, err, "unable to sign commitment") // If this is a taproot channel, then we'll generate fresh verification @@ -3168,7 +3172,7 @@ func testChanSyncOweCommitment(t *testing.T, chanType channeldb.ChannelType) { // above. assertAliceCommitRetransmit := func() *lnwire.CommitSig { aliceMsgsToSend, _, _, err := aliceChannel.ProcessChanSyncMsg( - bobSyncMsg, + ctxb, bobSyncMsg, ) if err != nil { t.Fatalf("unable to process chan sync msg: %v", err) @@ -3260,7 +3264,9 @@ func testChanSyncOweCommitment(t *testing.T, chanType channeldb.ChannelType) { // From Bob's Pov he has nothing else to send, so he should conclude he // has no further action remaining. - bobMsgsToSend, _, _, err := bobChannel.ProcessChanSyncMsg(aliceSyncMsg) + bobMsgsToSend, _, _, err := bobChannel.ProcessChanSyncMsg( + ctxb, aliceSyncMsg, + ) require.NoError(t, err, "unable to process chan sync msg") if len(bobMsgsToSend) != 0 { t.Fatalf("expected bob to send %v messages instead will "+ @@ -3288,7 +3294,7 @@ func testChanSyncOweCommitment(t *testing.T, chanType channeldb.ChannelType) { require.NoError(t, err, "bob unable to process alice's commitment") bobRevocation, _, _, err := bobChannel.RevokeCurrentCommitment() require.NoError(t, err, "unable to revoke bob commitment") - bobNewCommit, err := bobChannel.SignNextCommitment() + bobNewCommit, err := bobChannel.SignNextCommitment(ctxb) require.NoError(t, err, "bob unable to sign commitment") _, _, err = aliceChannel.ReceiveRevocation(bobRevocation) require.NoError(t, err, "alice unable to recv revocation") @@ -3478,7 +3484,7 @@ func TestChanSyncOweCommitmentAuxSigner(t *testing.T) { fn.Ok(fn.Some(sigBlobBuf.Bytes())), nil, ) - _, err = aliceChannel.SignNextCommitment() + _, err = aliceChannel.SignNextCommitment(ctxb) require.NoError(t, err, "unable to sign commitment") _, err = aliceChannel.GenMusigNonces() @@ -3490,7 +3496,7 @@ func TestChanSyncOweCommitmentAuxSigner(t *testing.T) { require.NoError(t, err, "unable to produce chan sync msg") aliceMsgsToSend, _, _, err := aliceChannel.ProcessChanSyncMsg( - bobSyncMsg, + ctxb, bobSyncMsg, ) require.NoError(t, err) require.Len(t, aliceMsgsToSend, 2) @@ -3582,7 +3588,7 @@ func TestAuxSignerShutdown(t *testing.T) { fn.Some(sigBlobBuf.Bytes()), nil, ) - _, err = aliceChannel.SignNextCommitment() + _, err = aliceChannel.SignNextCommitment(ctxb) require.ErrorIs(t, err, auxSignerShutdownErr) } @@ -3648,7 +3654,9 @@ func testChanSyncOweCommitmentPendingRemote(t *testing.T, t.Fatalf("unable to settle htlc: %v", err) } - aliceNewCommit, err := aliceChannel.SignNextCommitment() + aliceNewCommit, err := aliceChannel.SignNextCommitment( + ctxb, + ) if err != nil { t.Fatalf("unable to sign commitment: %v", err) } @@ -3686,7 +3694,7 @@ func testChanSyncOweCommitmentPendingRemote(t *testing.T, } // Bob signs the commitment he owes. - bobNewCommit, err := bobChannel.SignNextCommitment() + bobNewCommit, err := bobChannel.SignNextCommitment(ctxb) require.NoError(t, err, "unable to sign commitment") // This commitment is expected to contain no htlcs anymore. @@ -3795,14 +3803,14 @@ func testChanSyncOweRevocation(t *testing.T, chanType channeldb.ChannelType) { // // Alice signs the next state, then Bob receives and sends his // revocation message. - aliceNewCommit, err := aliceChannel.SignNextCommitment() + aliceNewCommit, err := aliceChannel.SignNextCommitment(ctxb) require.NoError(t, err, "unable to sign commitment") err = bobChannel.ReceiveNewCommitment(aliceNewCommit.CommitSigs) require.NoError(t, err, "bob unable to process alice's commitment") bobRevocation, _, _, err := bobChannel.RevokeCurrentCommitment() require.NoError(t, err, "unable to revoke bob commitment") - bobNewCommit, err := bobChannel.SignNextCommitment() + bobNewCommit, err := bobChannel.SignNextCommitment(ctxb) require.NoError(t, err, "bob unable to sign commitment") _, _, err = aliceChannel.ReceiveRevocation(bobRevocation) @@ -3838,7 +3846,7 @@ func testChanSyncOweRevocation(t *testing.T, chanType channeldb.ChannelType) { t.Helper() aliceMsgsToSend, _, _, err := aliceChannel.ProcessChanSyncMsg( - bobSyncMsg, + ctxb, bobSyncMsg, ) if err != nil { t.Fatalf("unable to process chan sync msg: %v", err) @@ -3869,7 +3877,9 @@ func testChanSyncOweRevocation(t *testing.T, chanType channeldb.ChannelType) { } // From Bob's PoV he shouldn't think that he owes Alice any messages. - bobMsgsToSend, _, _, err := bobChannel.ProcessChanSyncMsg(aliceSyncMsg) + bobMsgsToSend, _, _, err := bobChannel.ProcessChanSyncMsg( + ctxb, aliceSyncMsg, + ) require.NoError(t, err, "unable to process chan sync msg") if len(bobMsgsToSend) != 0 { t.Fatalf("expected bob to not retransmit, instead has: %v", @@ -3990,7 +4000,7 @@ func testChanSyncOweRevocationAndCommit(t *testing.T, // Progressing the exchange: Alice will send her signature, Bob will // receive, send a revocation and also a signature for Alice's state. - aliceNewCommits, err := aliceChannel.SignNextCommitment() + aliceNewCommits, err := aliceChannel.SignNextCommitment(ctxb) require.NoError(t, err, "unable to sign commitment") err = bobChannel.ReceiveNewCommitment(aliceNewCommits.CommitSigs) require.NoError(t, err, "bob unable to process alice's commitment") @@ -3999,7 +4009,7 @@ func testChanSyncOweRevocationAndCommit(t *testing.T, // reach Alice before the connection dies. bobRevocation, _, _, err := bobChannel.RevokeCurrentCommitment() require.NoError(t, err, "unable to revoke bob commitment") - bobNewCommit, err := bobChannel.SignNextCommitment() + bobNewCommit, err := bobChannel.SignNextCommitment(ctxb) require.NoError(t, err, "bob unable to sign commitment") // If we now attempt to resync, then Alice should conclude that she @@ -4021,7 +4031,9 @@ func testChanSyncOweRevocationAndCommit(t *testing.T, } } - aliceMsgsToSend, _, _, err := aliceChannel.ProcessChanSyncMsg(bobSyncMsg) + aliceMsgsToSend, _, _, err := aliceChannel.ProcessChanSyncMsg( + ctxb, bobSyncMsg, + ) require.NoError(t, err, "unable to process chan sync msg") if len(aliceMsgsToSend) != 0 { t.Fatalf("expected alice to not retransmit, instead she's "+ @@ -4032,7 +4044,7 @@ func testChanSyncOweRevocationAndCommit(t *testing.T, t.Helper() bobMsgsToSend, _, _, err := bobChannel.ProcessChanSyncMsg( - aliceSyncMsg, + ctxb, aliceSyncMsg, ) if err != nil { t.Fatalf("unable to process chan sync msg: %v", err) @@ -4198,7 +4210,7 @@ func testChanSyncOweRevocationAndCommitForceTransition(t *testing.T, addAndReceiveHTLC(t, bobChannel, aliceChannel, bobHtlc[1], nil) // Bob signs the new state update, and sends the signature to Alice. - bobNewCommit, err := bobChannel.SignNextCommitment() + bobNewCommit, err := bobChannel.SignNextCommitment(ctxb) require.NoError(t, err, "bob unable to sign commitment") err = aliceChannel.ReceiveNewCommitment(bobNewCommit.CommitSigs) @@ -4222,7 +4234,7 @@ func testChanSyncOweRevocationAndCommitForceTransition(t *testing.T, // Progressing the exchange: Alice will send her signature, with Bob // processing the new state locally. - aliceNewCommits, err := aliceChannel.SignNextCommitment() + aliceNewCommits, err := aliceChannel.SignNextCommitment(ctxb) require.NoError(t, err, "unable to sign commitment") err = bobChannel.ReceiveNewCommitment(aliceNewCommits.CommitSigs) require.NoError(t, err, "bob unable to process alice's commitment") @@ -4252,7 +4264,9 @@ func testChanSyncOweRevocationAndCommitForceTransition(t *testing.T, } } - aliceMsgsToSend, _, _, err := aliceChannel.ProcessChanSyncMsg(bobSyncMsg) + aliceMsgsToSend, _, _, err := aliceChannel.ProcessChanSyncMsg( + ctxb, bobSyncMsg, + ) require.NoError(t, err, "unable to process chan sync msg") if len(aliceMsgsToSend) != 0 { t.Fatalf("expected alice to not retransmit, instead she's "+ @@ -4263,7 +4277,9 @@ func testChanSyncOweRevocationAndCommitForceTransition(t *testing.T, // send his RevokeAndAck message again. Additionally, the CommitSig // message that he sends should be sufficient to finalize the state // transition. - bobMsgsToSend, _, _, err := bobChannel.ProcessChanSyncMsg(aliceSyncMsg) + bobMsgsToSend, _, _, err := bobChannel.ProcessChanSyncMsg( + ctxb, aliceSyncMsg, + ) require.NoError(t, err, "unable to process chan sync msg") if len(bobMsgsToSend) != 2 { t.Fatalf("expected bob to send %v messages, instead "+ @@ -4456,7 +4472,9 @@ func TestChanSyncFailure(t *testing.T) { addAndReceiveHTLC(t, bobChannel, aliceChannel, bobHtlc, nil) - aliceNewCommit, err := aliceChannel.SignNextCommitment() + aliceNewCommit, err := aliceChannel.SignNextCommitment( + ctxb, + ) if err != nil { t.Fatalf("unable to sign next commit: %v", err) } @@ -4481,7 +4499,7 @@ func TestChanSyncFailure(t *testing.T) { } // Alice should detect from Bob's message that she lost state. - _, _, _, err = aliceOld.ProcessChanSyncMsg(bobSyncMsg) + _, _, _, err = aliceOld.ProcessChanSyncMsg(ctxb, bobSyncMsg) if _, ok := err.(*ErrCommitSyncLocalDataLoss); !ok { t.Fatalf("wrong error, expected "+ "ErrCommitSyncLocalDataLoss instead got: %v", @@ -4489,7 +4507,9 @@ func TestChanSyncFailure(t *testing.T) { } // Bob should detect that Alice probably lost state. - _, _, _, err = bobChannel.ProcessChanSyncMsg(aliceSyncMsg) + _, _, _, err = bobChannel.ProcessChanSyncMsg( + ctxb, aliceSyncMsg, + ) if err != ErrCommitSyncRemoteDataLoss { t.Fatalf("wrong error, expected "+ "ErrCommitSyncRemoteDataLoss instead got: %v", @@ -4550,7 +4570,7 @@ func TestChanSyncFailure(t *testing.T) { bobSyncMsg, err := bobChannel.channelState.ChanSyncMsg() require.NoError(t, err, "unable to produce chan sync msg") bobSyncMsg.LocalUnrevokedCommitPoint = nil - _, _, _, err = aliceOld.ProcessChanSyncMsg(bobSyncMsg) + _, _, _, err = aliceOld.ProcessChanSyncMsg(ctxb, bobSyncMsg) if err != ErrCannotSyncCommitChains { t.Fatalf("wrong error, expected ErrCannotSyncCommitChains "+ "instead got: %v", err) @@ -4562,7 +4582,7 @@ func TestChanSyncFailure(t *testing.T) { bobSyncMsg, err = bobChannel.channelState.ChanSyncMsg() require.NoError(t, err, "unable to produce chan sync msg") bobSyncMsg.NextLocalCommitHeight++ - _, _, _, err = aliceChannel.ProcessChanSyncMsg(bobSyncMsg) + _, _, _, err = aliceChannel.ProcessChanSyncMsg(ctxb, bobSyncMsg) if err != ErrCannotSyncCommitChains { t.Fatalf("wrong error, expected ErrCannotSyncCommitChains "+ "instead got: %v", err) @@ -4573,7 +4593,7 @@ func TestChanSyncFailure(t *testing.T) { bobSyncMsg, err = bobChannel.channelState.ChanSyncMsg() require.NoError(t, err, "unable to produce chan sync msg") bobSyncMsg.NextLocalCommitHeight-- - _, _, _, err = aliceChannel.ProcessChanSyncMsg(bobSyncMsg) + _, _, _, err = aliceChannel.ProcessChanSyncMsg(ctxb, bobSyncMsg) if err != ErrCommitSyncRemoteDataLoss { t.Fatalf("wrong error, expected ErrCommitSyncRemoteDataLoss "+ "instead got: %v", err) @@ -4589,7 +4609,7 @@ func TestChanSyncFailure(t *testing.T) { require.NoError(t, err, "unable to parse pubkey") bobSyncMsg.LocalUnrevokedCommitPoint = modCommitPoint - _, _, _, err = aliceChannel.ProcessChanSyncMsg(bobSyncMsg) + _, _, _, err = aliceChannel.ProcessChanSyncMsg(ctxb, bobSyncMsg) if err != ErrInvalidLocalUnrevokedCommitPoint { t.Fatalf("wrong error, expected "+ "ErrInvalidLocalUnrevokedCommitPoint instead got: %v", @@ -4609,7 +4629,7 @@ func TestChanSyncFailure(t *testing.T) { bobSyncMsg, err = bobChannel.channelState.ChanSyncMsg() require.NoError(t, err, "unable to produce chan sync msg") bobSyncMsg.LocalUnrevokedCommitPoint = modCommitPoint - _, _, _, err = aliceChannel.ProcessChanSyncMsg(bobSyncMsg) + _, _, _, err = aliceChannel.ProcessChanSyncMsg(ctxb, bobSyncMsg) if err != ErrInvalidLocalUnrevokedCommitPoint { t.Fatalf("wrong error, expected "+ "ErrInvalidLocalUnrevokedCommitPoint instead got: %v", @@ -4676,7 +4696,7 @@ func TestChannelRetransmissionFeeUpdate(t *testing.T) { // Now, Alice will send a new commitment to Bob, but we'll simulate a // connection failure, so Bob doesn't get her signature. - aliceNewCommit, err := aliceChannel.SignNextCommitment() + aliceNewCommit, err := aliceChannel.SignNextCommitment(ctxb) require.NoError(t, err, "unable to sign commitment") // Restart both channels to simulate a connection restart. @@ -4694,7 +4714,9 @@ func TestChannelRetransmissionFeeUpdate(t *testing.T) { require.NoError(t, err, "unable to produce chan sync msg") // Bob should detect that he doesn't need to send anything to Alice. - bobMsgsToSend, _, _, err := bobChannel.ProcessChanSyncMsg(aliceSyncMsg) + bobMsgsToSend, _, _, err := bobChannel.ProcessChanSyncMsg( + ctxb, aliceSyncMsg, + ) require.NoError(t, err, "unable to process chan sync msg") if len(bobMsgsToSend) != 0 { t.Fatalf("expected bob to send %v messages instead "+ @@ -4706,7 +4728,7 @@ func TestChannelRetransmissionFeeUpdate(t *testing.T) { // that she needs to first send a new UpdateFee message, and also a // CommitSig. aliceMsgsToSend, _, _, err := aliceChannel.ProcessChanSyncMsg( - bobSyncMsg, + ctxb, bobSyncMsg, ) require.NoError(t, err, "unable to process chan sync msg") if len(aliceMsgsToSend) != 2 { @@ -4762,7 +4784,7 @@ func TestChannelRetransmissionFeeUpdate(t *testing.T) { require.NoError(t, err, "bob unable to process alice's commitment") bobRevocation, _, _, err := bobChannel.RevokeCurrentCommitment() require.NoError(t, err, "unable to revoke bob commitment") - bobNewCommit, err := bobChannel.SignNextCommitment() + bobNewCommit, err := bobChannel.SignNextCommitment(ctxb) require.NoError(t, err, "bob unable to sign commitment") _, _, err = aliceChannel.ReceiveRevocation(bobRevocation) require.NoError(t, err, "alice unable to recv revocation") @@ -4893,7 +4915,7 @@ func TestFeeUpdateOldDiskFormat(t *testing.T) { // Now, Alice will send a new commitment to Bob, but we'll simulate a // connection failure, so Bob doesn't get the signature. - aliceNewCommitSig, err := aliceChannel.SignNextCommitment() + aliceNewCommitSig, err := aliceChannel.SignNextCommitment(ctxb) require.NoError(t, err, "unable to sign commitment") // Before restarting Alice, to mimic the old format, we fetch the @@ -4950,7 +4972,7 @@ func TestFeeUpdateOldDiskFormat(t *testing.T) { require.NoError(t, err, "bob unable to process alice's commitment") bobRevocation, _, _, err := bobChannel.RevokeCurrentCommitment() require.NoError(t, err, "unable to revoke bob commitment") - bobNewCommitSigs, err := bobChannel.SignNextCommitment() + bobNewCommitSigs, err := bobChannel.SignNextCommitment(ctxb) require.NoError(t, err, "bob unable to sign commitment") _, _, err = aliceChannel.ReceiveRevocation(bobRevocation) require.NoError(t, err, "alice unable to recv revocation") @@ -5026,11 +5048,11 @@ func TestChanSyncUnableToSync(t *testing.T) { NextLocalCommitHeight: 1000, RemoteCommitTailHeight: 9000, } - _, _, _, err = bobChannel.ProcessChanSyncMsg(badChanSync) + _, _, _, err = bobChannel.ProcessChanSyncMsg(ctxb, badChanSync) if err != ErrCannotSyncCommitChains { t.Fatalf("expected error instead have: %v", err) } - _, _, _, err = aliceChannel.ProcessChanSyncMsg(badChanSync) + _, _, _, err = aliceChannel.ProcessChanSyncMsg(ctxb, badChanSync) if err != ErrCannotSyncCommitChains { t.Fatalf("expected error instead have: %v", err) } @@ -5098,7 +5120,7 @@ func TestChanSyncInvalidLastSecret(t *testing.T) { // Alice's former self should conclude that she possibly lost data as // Bob is sending a valid commit secret for the latest state. - _, _, _, err = aliceOld.ProcessChanSyncMsg(bobChanSync) + _, _, _, err = aliceOld.ProcessChanSyncMsg(ctxb, bobChanSync) if _, ok := err.(*ErrCommitSyncLocalDataLoss); !ok { t.Fatalf("wrong error, expected ErrCommitSyncLocalDataLoss "+ "instead got: %v", err) @@ -5106,7 +5128,7 @@ func TestChanSyncInvalidLastSecret(t *testing.T) { // Bob should conclude that he should force close the channel, as Alice // cannot continue operation. - _, _, _, err = bobChannel.ProcessChanSyncMsg(aliceChanSync) + _, _, _, err = bobChannel.ProcessChanSyncMsg(ctxb, aliceChanSync) if err != ErrInvalidLastCommitSecret { t.Fatalf("wrong error, expected ErrInvalidLastCommitSecret, "+ "instead got: %v", err) @@ -5589,7 +5611,7 @@ func TestSignCommitmentFailNotLockedIn(t *testing.T) { // If we now try to initiate a state update, then it should fail as // Alice is unable to actually create a new state. - _, err = aliceChannel.SignNextCommitment() + _, err = aliceChannel.SignNextCommitment(ctxb) if err != ErrNoWindow { t.Fatalf("expected ErrNoWindow, instead have: %v", err) } @@ -5618,7 +5640,7 @@ func TestLockedInHtlcForwardingSkipAfterRestart(t *testing.T) { // We'll now manually initiate a state transition between Alice and // bob. - aliceNewCommit, err := aliceChannel.SignNextCommitment() + aliceNewCommit, err := aliceChannel.SignNextCommitment(ctxb) if err != nil { t.Fatal(err) } @@ -5647,7 +5669,7 @@ func TestLockedInHtlcForwardingSkipAfterRestart(t *testing.T) { // Now, have Bob initiate a transition to lock in the Adds sent by // Alice. - bobNewCommit, err := bobChannel.SignNextCommitment() + bobNewCommit, err := bobChannel.SignNextCommitment(ctxb) if err != nil { t.Fatal(err) } @@ -5692,7 +5714,7 @@ func TestLockedInHtlcForwardingSkipAfterRestart(t *testing.T) { // We'll now initiate another state transition, but this time Bob will // lead. - bobNewCommit, err = bobChannel.SignNextCommitment() + bobNewCommit, err = bobChannel.SignNextCommitment(ctxb) if err != nil { t.Fatal(err) } @@ -5727,7 +5749,7 @@ func TestLockedInHtlcForwardingSkipAfterRestart(t *testing.T) { // Now, begin another state transition led by Alice, and fail the second // HTLC part-way through the dance. - aliceNewCommit, err = aliceChannel.SignNextCommitment() + aliceNewCommit, err = aliceChannel.SignNextCommitment(ctxb) if err != nil { t.Fatal(err) } @@ -5800,7 +5822,7 @@ func TestLockedInHtlcForwardingSkipAfterRestart(t *testing.T) { // Have Alice initiate a state transition, which does not include the // HTLCs just re-added to the channel state. - aliceNewCommit, err = aliceChannel.SignNextCommitment() + aliceNewCommit, err = aliceChannel.SignNextCommitment(ctxb) if err != nil { t.Fatal(err) } @@ -5829,7 +5851,7 @@ func TestLockedInHtlcForwardingSkipAfterRestart(t *testing.T) { } // Now initiate a final update from Bob to lock in the final Fail. - bobNewCommit, err = bobChannel.SignNextCommitment() + bobNewCommit, err = bobChannel.SignNextCommitment(ctxb) if err != nil { t.Fatal(err) } @@ -5861,7 +5883,7 @@ func TestLockedInHtlcForwardingSkipAfterRestart(t *testing.T) { // Finally, have Bob initiate a state transition that locks in the Fail // added after the restart. - aliceNewCommit, err = aliceChannel.SignNextCommitment() + aliceNewCommit, err = aliceChannel.SignNextCommitment(ctxb) if err != nil { t.Fatal(err) } @@ -5923,7 +5945,7 @@ func TestInvalidCommitSigError(t *testing.T) { addAndReceiveHTLC(t, aliceChannel, bobChannel, htlc, nil) // Alice will now attempt to initiate a state transition. - aliceNewCommit, err := aliceChannel.SignNextCommitment() + aliceNewCommit, err := aliceChannel.SignNextCommitment(ctxb) require.NoError(t, err, "unable to sign new commit") // Before the signature gets to Bob, we'll mutate it, such that the @@ -6116,7 +6138,7 @@ func TestChannelUnilateralClosePendingCommit(t *testing.T) { // With the HTLC added, we'll now manually initiate a state transition // from Alice to Bob. - _, err = aliceChannel.SignNextCommitment() + _, err = aliceChannel.SignNextCommitment(ctxb) if err != nil { t.Fatal(err) } @@ -6350,7 +6372,7 @@ func TestMaxAcceptedHTLCs(t *testing.T) { addAndReceiveHTLC(t, aliceChannel, bobChannel, htlc, nil) // Add a commitment to Bob's commitment chain. - aliceNewCommit, err := aliceChannel.SignNextCommitment() + aliceNewCommit, err := aliceChannel.SignNextCommitment(ctxb) require.NoError(t, err, "unable to sign next commitment") err = bobChannel.ReceiveNewCommitment(aliceNewCommit.CommitSigs) require.NoError(t, err, "unable to recv new commitment") @@ -6445,7 +6467,7 @@ func TestMaxAsynchronousHtlcs(t *testing.T) { t.Fatalf("unable to receive fail htlc: %v", err) } - bobNewCommit, err := bobChannel.SignNextCommitment() + bobNewCommit, err := bobChannel.SignNextCommitment(ctxb) require.NoError(t, err, "unable to sign next commitment") err = aliceChannel.ReceiveNewCommitment(bobNewCommit.CommitSigs) @@ -6453,7 +6475,7 @@ func TestMaxAsynchronousHtlcs(t *testing.T) { // Cover the HTLC referenced with id equal to numHTLCs-1 with a new // signature (step 3). - aliceNewCommit, err := aliceChannel.SignNextCommitment() + aliceNewCommit, err := aliceChannel.SignNextCommitment(ctxb) require.NoError(t, err, "unable to sign next commitment") err = bobChannel.ReceiveNewCommitment(aliceNewCommit.CommitSigs) @@ -6478,7 +6500,7 @@ func TestMaxAsynchronousHtlcs(t *testing.T) { // Receiving the commitment should succeed as in step 7 since space was // made. - aliceNewCommit, err = aliceChannel.SignNextCommitment() + aliceNewCommit, err = aliceChannel.SignNextCommitment(ctxb) require.NoError(t, err, "unable to sign next commitment") err = bobChannel.ReceiveNewCommitment(aliceNewCommit.CommitSigs) @@ -7103,7 +7125,7 @@ func TestChannelRestoreUpdateLogs(t *testing.T) { addAndReceiveHTLC(t, aliceChannel, bobChannel, htlcAlice, nil) // Let Alice sign a new state, which will include the HTLC just sent. - aliceNewCommit, err := aliceChannel.SignNextCommitment() + aliceNewCommit, err := aliceChannel.SignNextCommitment(ctxb) require.NoError(t, err, "unable to sign commitment") // Bob receives this commitment signature, and revokes his old state. @@ -7133,7 +7155,7 @@ func TestChannelRestoreUpdateLogs(t *testing.T) { // and remote commit chains are updated in an async fashion. Since the // remote chain was updated with the latest state (since Bob sent the // revocation earlier) we can keep advancing the remote commit chain. - aliceNewCommit, err = aliceChannel.SignNextCommitment() + aliceNewCommit, err = aliceChannel.SignNextCommitment(ctxb) require.NoError(t, err, "unable to sign commitment") // After Alice has signed this commitment, her local commitment will @@ -7280,7 +7302,7 @@ func TestChannelRestoreUpdateLogsFailedHTLC(t *testing.T) { restoreAndAssert(t, aliceChannel, 1, 0, 0, 0) // Bob sends a signature. - bobNewCommit, err := bobChannel.SignNextCommitment() + bobNewCommit, err := bobChannel.SignNextCommitment(ctxb) require.NoError(t, err, "unable to sign commitment") err = aliceChannel.ReceiveNewCommitment(bobNewCommit.CommitSigs) require.NoError(t, err, "unable to receive commitment") @@ -7307,7 +7329,7 @@ func TestChannelRestoreUpdateLogsFailedHTLC(t *testing.T) { // Now send a signature from Alice. This will give Bob a new commitment // where the HTLC is removed. - aliceNewCommit, err := aliceChannel.SignNextCommitment() + aliceNewCommit, err := aliceChannel.SignNextCommitment(ctxb) require.NoError(t, err, "unable to sign commitment") err = bobChannel.ReceiveNewCommitment(aliceNewCommit.CommitSigs) require.NoError(t, err, "unable to receive commitment") @@ -7370,7 +7392,7 @@ func TestDuplicateFailRejection(t *testing.T) { // We'll now have Bob sign a new commitment to lock in the HTLC fail // for Alice. - _, err = bobChannel.SignNextCommitment() + _, err = bobChannel.SignNextCommitment(ctxb) require.NoError(t, err, "unable to sign commit") // We'll now force a restart for Bob and Alice, so we can test the @@ -7433,7 +7455,7 @@ func TestDuplicateSettleRejection(t *testing.T) { // We'll now have Bob sign a new commitment to lock in the HTLC fail // for Alice. - _, err = bobChannel.SignNextCommitment() + _, err = bobChannel.SignNextCommitment(ctxb) require.NoError(t, err, "unable to sign commit") // We'll now force a restart for Bob and Alice, so we can test the @@ -7514,7 +7536,7 @@ func TestChannelRestoreCommitHeight(t *testing.T) { addAndReceiveHTLC(t, aliceChannel, bobChannel, htlcAlice, nil) // Let Alice sign a new state, which will include the HTLC just sent. - aliceNewCommit, err := aliceChannel.SignNextCommitment() + aliceNewCommit, err := aliceChannel.SignNextCommitment(ctxb) require.NoError(t, err, "unable to sign commitment") // The HTLC should only be on the pending remote commitment, so the @@ -7546,7 +7568,7 @@ func TestChannelRestoreCommitHeight(t *testing.T) { // Now let Bob send the commitment signature making the HTLC lock in on // Alice's commitment. - bobNewCommit, err := bobChannel.SignNextCommitment() + bobNewCommit, err := bobChannel.SignNextCommitment(ctxb) require.NoError(t, err, "unable to sign commitment") // At this stage Bob has a pending remote commitment. Make sure @@ -7579,7 +7601,7 @@ func TestChannelRestoreCommitHeight(t *testing.T) { // Send a new signature from Alice to Bob, making Alice have a pending // remote commitment. - aliceNewCommit, err = aliceChannel.SignNextCommitment() + aliceNewCommit, err = aliceChannel.SignNextCommitment(ctxb) require.NoError(t, err, "unable to sign commitment") // A restoration should keep the add heights iof the first HTLC, and @@ -7618,7 +7640,7 @@ func TestChannelRestoreCommitHeight(t *testing.T) { // Sign a new state for Alice, making Bob have a pending remote // commitment. - bobNewCommit, err = bobChannel.SignNextCommitment() + bobNewCommit, err = bobChannel.SignNextCommitment(ctxb) require.NoError(t, err, "unable to sign commitment") // The signing of a new commitment for Alice should have given the new @@ -7655,7 +7677,7 @@ func TestChannelRestoreCommitHeight(t *testing.T) { require.NoError(t, err, "unable to recv htlc cancel") // Now Bob signs for the fail update. - bobNewCommit, err = bobChannel.SignNextCommitment() + bobNewCommit, err = bobChannel.SignNextCommitment(ctxb) require.NoError(t, err, "unable to sign commitment") // Bob has a pending commitment for Alice, it shouldn't affect the add @@ -7713,13 +7735,13 @@ func TestForceCloseBorkedState(t *testing.T) { // Do the commitment dance until Bob sends a revocation so Alice is // able to receive the revocation, and then also make a new state // herself. - aliceNewCommit, err := aliceChannel.SignNextCommitment() + aliceNewCommit, err := aliceChannel.SignNextCommitment(ctxb) require.NoError(t, err, "unable to sign commit") err = bobChannel.ReceiveNewCommitment(aliceNewCommit.CommitSigs) require.NoError(t, err, "unable to receive commitment") revokeMsg, _, _, err := bobChannel.RevokeCurrentCommitment() require.NoError(t, err, "unable to revoke bob commitment") - bobNewCommit, err := bobChannel.SignNextCommitment() + bobNewCommit, err := bobChannel.SignNextCommitment(ctxb) require.NoError(t, err, "unable to sign commit") err = aliceChannel.ReceiveNewCommitment(bobNewCommit.CommitSigs) require.NoError(t, err, "unable to receive commitment") @@ -7753,7 +7775,7 @@ func TestForceCloseBorkedState(t *testing.T) { // We manually advance the commitment tail here since the above // ReceiveRevocation call will fail before it's actually advanced. aliceChannel.commitChains.Remote.advanceTail() - _, err = aliceChannel.SignNextCommitment() + _, err = aliceChannel.SignNextCommitment(ctxb) if err != channeldb.ErrChanBorked { t.Fatalf("sign commitment should have failed: %v", err) } @@ -8220,7 +8242,7 @@ func TestChannelFeeRateFloor(t *testing.T) { } // Check that alice can still sign commitments. - aliceNewCommit, err := alice.SignNextCommitment() + aliceNewCommit, err := alice.SignNextCommitment(ctxb) require.NoError(t, err, "alice unable to sign commitment") // Check that bob can still receive commitments. @@ -8979,7 +9001,7 @@ func TestChannelUnsignedAckedFailure(t *testing.T) { // Bob should send a commitment signature to Alice. // <----sig------ - bobNewCommit, err := bobChannel.SignNextCommitment() + bobNewCommit, err := bobChannel.SignNextCommitment(ctxb) require.NoError(t, err) err = aliceChannel.ReceiveNewCommitment(bobNewCommit.CommitSigs) require.NoError(t, err) @@ -8994,7 +9016,7 @@ func TestChannelUnsignedAckedFailure(t *testing.T) { // Alice should sign the next commitment and go down before // sending it. // -----sig-----X - aliceNewCommit, err := aliceChannel.SignNextCommitment() + aliceNewCommit, err := aliceChannel.SignNextCommitment(ctxb) require.NoError(t, err) newAliceChannel, err := NewLightningChannel( @@ -9025,7 +9047,7 @@ func TestChannelUnsignedAckedFailure(t *testing.T) { // 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. - bobNewCommit, err = bobChannel.SignNextCommitment() + bobNewCommit, err = bobChannel.SignNextCommitment(ctxb) require.NoError(t, err) err = newAliceChannel.ReceiveNewCommitment(bobNewCommit.CommitSigs) require.NoError(t, err) @@ -9083,7 +9105,7 @@ func TestChannelLocalUnsignedUpdatesFailure(t *testing.T) { // Alice should send a commitment signature to Bob. // -----sig----> - aliceNewCommit, err := aliceChannel.SignNextCommitment() + aliceNewCommit, err := aliceChannel.SignNextCommitment(ctxb) require.NoError(t, err) err = bobChannel.ReceiveNewCommitment(aliceNewCommit.CommitSigs) require.NoError(t, err) @@ -9107,7 +9129,7 @@ func TestChannelLocalUnsignedUpdatesFailure(t *testing.T) { // Bob sends the final signature and Alice should not reject it. // <----sig----- - bobNewCommit, err := bobChannel.SignNextCommitment() + bobNewCommit, err := bobChannel.SignNextCommitment(ctxb) require.NoError(t, err) err = newAliceChannel.ReceiveNewCommitment(bobNewCommit.CommitSigs) require.NoError(t, err) @@ -9166,7 +9188,7 @@ func TestChannelSignedAckRegression(t *testing.T) { require.NoError(t, err) // -----sig----> - aliceNewCommit, err := aliceChannel.SignNextCommitment() + aliceNewCommit, err := aliceChannel.SignNextCommitment(ctxb) require.NoError(t, err) err = bobChannel.ReceiveNewCommitment(aliceNewCommit.CommitSigs) require.NoError(t, err) @@ -9178,7 +9200,7 @@ func TestChannelSignedAckRegression(t *testing.T) { require.NoError(t, err) // <----sig----- - bobNewCommit, err := bobChannel.SignNextCommitment() + bobNewCommit, err := bobChannel.SignNextCommitment(ctxb) require.NoError(t, err) err = aliceChannel.ReceiveNewCommitment(bobNewCommit.CommitSigs) require.NoError(t, err) @@ -9190,7 +9212,7 @@ func TestChannelSignedAckRegression(t *testing.T) { addAndReceiveHTLC(t, aliceChannel, bobChannel, htlc2, nil) // -----sig----> - aliceNewCommit, err = aliceChannel.SignNextCommitment() + aliceNewCommit, err = aliceChannel.SignNextCommitment(ctxb) require.NoError(t, err) err = bobChannel.ReceiveNewCommitment(aliceNewCommit.CommitSigs) require.NoError(t, err) @@ -9220,7 +9242,7 @@ func TestChannelSignedAckRegression(t *testing.T) { // Bob should no longer fail to sign this commitment due to faulty // update logs. // <----sig----- - bobNewCommit, err = newBobChannel.SignNextCommitment() + bobNewCommit, err = newBobChannel.SignNextCommitment(ctxb) require.NoError(t, err) // Alice should receive the new commitment without hiccups. @@ -9291,7 +9313,7 @@ func TestIsChannelClean(t *testing.T) { // removed from both commitments. // ---sig---> - aliceNewCommit, err := aliceChannel.SignNextCommitment() + aliceNewCommit, err := aliceChannel.SignNextCommitment(ctxb) require.NoError(t, err) err = bobChannel.ReceiveNewCommitment(aliceNewCommit.CommitSigs) require.NoError(t, err) @@ -9305,7 +9327,7 @@ func TestIsChannelClean(t *testing.T) { assertCleanOrDirty(false, aliceChannel, bobChannel, t) // <---sig--- - bobNewCommit, err := bobChannel.SignNextCommitment() + bobNewCommit, err := bobChannel.SignNextCommitment(ctxb) require.NoError(t, err) err = aliceChannel.ReceiveNewCommitment(bobNewCommit.CommitSigs) require.NoError(t, err) @@ -9326,7 +9348,7 @@ func TestIsChannelClean(t *testing.T) { assertCleanOrDirty(false, aliceChannel, bobChannel, t) // <---sig--- - bobNewCommit, err = bobChannel.SignNextCommitment() + bobNewCommit, err = bobChannel.SignNextCommitment(ctxb) require.NoError(t, err) err = aliceChannel.ReceiveNewCommitment(bobNewCommit.CommitSigs) require.NoError(t, err) @@ -9340,7 +9362,7 @@ func TestIsChannelClean(t *testing.T) { assertCleanOrDirty(false, aliceChannel, bobChannel, t) // ---sig---> - aliceNewCommit, err = aliceChannel.SignNextCommitment() + aliceNewCommit, err = aliceChannel.SignNextCommitment(ctxb) require.NoError(t, err) err = bobChannel.ReceiveNewCommitment(aliceNewCommit.CommitSigs) require.NoError(t, err) @@ -9364,7 +9386,7 @@ func TestIsChannelClean(t *testing.T) { assertCleanOrDirty(false, aliceChannel, bobChannel, t) // ---sig---> - aliceNewCommit, err = aliceChannel.SignNextCommitment() + aliceNewCommit, err = aliceChannel.SignNextCommitment(ctxb) require.NoError(t, err) err = bobChannel.ReceiveNewCommitment(aliceNewCommit.CommitSigs) require.NoError(t, err) @@ -9378,7 +9400,7 @@ func TestIsChannelClean(t *testing.T) { assertCleanOrDirty(false, aliceChannel, bobChannel, t) // <---sig--- - bobNewCommit, err = bobChannel.SignNextCommitment() + bobNewCommit, err = bobChannel.SignNextCommitment(ctxb) require.NoError(t, err) err = aliceChannel.ReceiveNewCommitment(bobNewCommit.CommitSigs) require.NoError(t, err) @@ -9513,7 +9535,7 @@ func testGetDustSum(t *testing.T, chantype channeldb.ChannelType) { checkDust(bobChannel, htlc2Amt, htlc2Amt) // Alice signs for this HTLC and neither perspective should change. - aliceNewCommit, err := aliceChannel.SignNextCommitment() + aliceNewCommit, err := aliceChannel.SignNextCommitment(ctxb) require.NoError(t, err) err = bobChannel.ReceiveNewCommitment(aliceNewCommit.CommitSigs) require.NoError(t, err) @@ -9532,7 +9554,7 @@ func testGetDustSum(t *testing.T, chantype channeldb.ChannelType) { // The rest of the dance is completed and neither perspective should // change. - bobNewCommit, err := bobChannel.SignNextCommitment() + bobNewCommit, err := bobChannel.SignNextCommitment(ctxb) require.NoError(t, err) err = aliceChannel.ReceiveNewCommitment(bobNewCommit.CommitSigs) require.NoError(t, err) @@ -10353,7 +10375,7 @@ func TestAsynchronousSendingContraint(t *testing.T) { // Bob signs the new state for alice, which ONLY has his htlc on it // because he only includes acked updates of alice. // <----sig-------|--------------- - bobNewCommit, err := bobChannel.SignNextCommitment() + bobNewCommit, err := bobChannel.SignNextCommitment(ctxb) require.NoError(t, err) err = aliceChannel.ReceiveNewCommitment(bobNewCommit.CommitSigs) @@ -10369,7 +10391,7 @@ func TestAsynchronousSendingContraint(t *testing.T) { // incoming htlc in her commitment sig to bob, but this will dip her // local balance below her reserve because she already used everything // up when adding her htlc. - _, err = aliceChannel.SignNextCommitment() + _, err = aliceChannel.SignNextCommitment(ctxb) require.ErrorIs(t, err, ErrBelowChanReserve) } @@ -10494,7 +10516,7 @@ func TestAsynchronousSendingWithFeeBuffer(t *testing.T) { // Force a state transition, this will lock-in the htlc of bob. // ------sig-----> (includes bob's htlc) // <----rev------ (locks in bob's htlc for alice) - aliceNewCommit, err := aliceChannel.SignNextCommitment() + aliceNewCommit, err := aliceChannel.SignNextCommitment(ctxb) require.NoError(t, err) err = bobChannel.ReceiveNewCommitment(aliceNewCommit.CommitSigs) require.NoError(t, err) @@ -10736,7 +10758,7 @@ func TestBlindingPointPersistence(t *testing.T) { // Now, Alice will send a new commitment to Bob, which will persist our // pending HTLC to disk. - aliceCommit, err := aliceChannel.SignNextCommitment() + aliceCommit, err := aliceChannel.SignNextCommitment(ctxb) require.NoError(t, err, "unable to sign commitment") // Restart alice to force fetching state from disk. diff --git a/lnwallet/test_utils.go b/lnwallet/test_utils.go index b450b3b30..225323296 100644 --- a/lnwallet/test_utils.go +++ b/lnwallet/test_utils.go @@ -2,6 +2,7 @@ package lnwallet import ( "bytes" + "context" "crypto/rand" "encoding/binary" "encoding/hex" @@ -103,6 +104,10 @@ var ( bobDustLimit = btcutil.Amount(1300) testChannelCapacity float64 = 10 + + // ctxb is a context that will never be cancelled, that is used in + // place of a real quit context. + ctxb = context.Background() ) // CreateTestChannels creates to fully populated channels to be used within @@ -557,7 +562,7 @@ func calcStaticFee(chanType channeldb.ChannelType, numHTLCs int) btcutil.Amount // pending updates. This method is useful when testing interactions between two // live state machines. func ForceStateTransition(chanA, chanB *LightningChannel) error { - aliceNewCommit, err := chanA.SignNextCommitment() + aliceNewCommit, err := chanA.SignNextCommitment(ctxb) if err != nil { return err } @@ -570,7 +575,7 @@ func ForceStateTransition(chanA, chanB *LightningChannel) error { if err != nil { return err } - bobNewCommit, err := chanB.SignNextCommitment() + bobNewCommit, err := chanB.SignNextCommitment(ctxb) if err != nil { return err } diff --git a/lnwallet/transactions_test.go b/lnwallet/transactions_test.go index 8786c2d5d..0f7988a32 100644 --- a/lnwallet/transactions_test.go +++ b/lnwallet/transactions_test.go @@ -357,7 +357,7 @@ func testVectors(t *testing.T, chanType channeldb.ChannelType, test testCase) { // Execute commit dance to arrive at the point where the local node has // received the test commitment and the remote signature. - localNewCommit, err := localChannel.SignNextCommitment() + localNewCommit, err := localChannel.SignNextCommitment(ctxb) require.NoError(t, err, "local unable to sign commitment") err = remoteChannel.ReceiveNewCommitment(localNewCommit.CommitSigs) @@ -369,7 +369,7 @@ func testVectors(t *testing.T, chanType channeldb.ChannelType, test testCase) { _, _, err = localChannel.ReceiveRevocation(revMsg) require.NoError(t, err) - remoteNewCommit, err := remoteChannel.SignNextCommitment() + remoteNewCommit, err := remoteChannel.SignNextCommitment(ctxb) require.NoError(t, err) require.Equal( From dce80ac9a13b00a87baaceb1bf0ec5603a9a5293 Mon Sep 17 00:00:00 2001 From: Jonathan Harvey-Buschel Date: Thu, 17 Oct 2024 13:38:35 +0200 Subject: [PATCH 8/9] lnwallet: test link quit signal handling --- lnwallet/channel_test.go | 71 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/lnwallet/channel_test.go b/lnwallet/channel_test.go index ae2f474f2..b900e30dc 100644 --- a/lnwallet/channel_test.go +++ b/lnwallet/channel_test.go @@ -2,6 +2,7 @@ package lnwallet import ( "bytes" + "context" crand "crypto/rand" "crypto/sha256" "errors" @@ -11,6 +12,7 @@ import ( "runtime" "testing" "testing/quick" + "time" "github.com/btcsuite/btcd/blockchain" "github.com/btcsuite/btcd/btcec/v2" @@ -3592,6 +3594,75 @@ func TestAuxSignerShutdown(t *testing.T) { require.ErrorIs(t, err, auxSignerShutdownErr) } +// TestQuitDuringSignNextCommitment tests that the channel state machine can +// successfully exit on receiving a quit signal when signing a new commitment. +func TestQuitDuringSignNextCommitment(t *testing.T) { + t.Parallel() + + // We'll kick off the test by creating our channels which both are + // loaded with 5 BTC each. + aliceChannel, bobChannel, err := CreateTestChannels( + t, channeldb.SingleFunderTweaklessBit, + ) + require.NoError(t, err, "unable to create test channels") + + // We'll simulate an aux signer that was started successfully, but is + // now frozen / inactive. This could happen if the aux signer shut down + // without sending an error on any aux sig job error channel. + noopAuxSigJob := func(jobs []AuxSigJob) {} + + auxSigner := NewAuxSignerMock(noopAuxSigJob) + aliceChannel.auxSigner = fn.Some[AuxSigner](auxSigner) + + // Each HTLC amount is 0.01 BTC. + htlcAmt := lnwire.NewMSatFromSatoshis(0.01 * btcutil.SatoshiPerBitcoin) + + // Create enough HTLCs to create multiple sig jobs (one job per HTLC). + const numHTLCs = 24 + + // Send the specified number of HTLCs. + for i := 0; i < numHTLCs; i++ { + htlc, _ := createHTLC(i, htlcAmt) + addAndReceiveHTLC(t, aliceChannel, bobChannel, htlc, nil) + } + + // We'll set up the mock aux signer to expect calls to PackSigs and also + // SubmitSecondLevelSigBatch. The direct return values for this mock aux + // signer are nil. The expected error comes from the behavior of + // noopAuxSigJob above, which mimics a faulty aux signer. + var sigBlobBuf bytes.Buffer + sigBlob := testSigBlob{ + BlobInt: tlv.NewPrimitiveRecord[tlv.TlvType65634, uint16](5), + } + tlvStream, err := tlv.NewStream(sigBlob.BlobInt.Record()) + require.NoError(t, err, "unable to create tlv stream") + require.NoError(t, tlvStream.Encode(&sigBlobBuf)) + + auxSigner.On( + "SubmitSecondLevelSigBatch", mock.Anything, mock.Anything, + mock.Anything, + ).Return(nil).Twice() + auxSigner.On( + "PackSigs", mock.Anything, + ).Return( + fn.Some(sigBlobBuf.Bytes()), nil, + ) + + quitDelay := time.Millisecond * 20 + quit, quitFunc := context.WithCancel(context.Background()) + + // Alice's channel will be stuck waiting for aux sig job responses until + // we send the quit signal. We add an explicit sleep here so that we can + // cause a failure if we run the test with a very short timeout. + go func() { + time.Sleep(quitDelay) + quitFunc() + }() + + _, err = aliceChannel.SignNextCommitment(quit) + require.ErrorIs(t, err, errQuit) +} + func testChanSyncOweCommitmentPendingRemote(t *testing.T, chanType channeldb.ChannelType) { From ac719dc0161d6995a95c85b7ee5cb4f227b37b99 Mon Sep 17 00:00:00 2001 From: Jonathan Harvey-Buschel Date: Thu, 17 Oct 2024 13:38:36 +0200 Subject: [PATCH 9/9] gitignore: ignore vscode workspace files --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index a44a7b8f5..6be439ed7 100644 --- a/.gitignore +++ b/.gitignore @@ -66,6 +66,7 @@ profile.tmp .DS_Store .vscode +*.code-workspace # Coverage test coverage.txt