diff --git a/docs/release-notes/release-notes-0.19.1.md b/docs/release-notes/release-notes-0.19.1.md index 72ea90d41..c49ccfd34 100644 --- a/docs/release-notes/release-notes-0.19.1.md +++ b/docs/release-notes/release-notes-0.19.1.md @@ -27,6 +27,10 @@ - Fixed [a case](https://github.com/lightningnetwork/lnd/pull/9854) where the `BumpFee` doesn't give an error response. +- Fixed [a case](https://github.com/lightningnetwork/lnd/pull/9872) where a + peer would not disconnect properly when both peers supported the new + "rbf-coop-close" feature leaving the peer connection in a borked state. + # New Features ## Functional Enhancements @@ -83,3 +87,4 @@ * Elle Mouton * Yong Yu +* Ziggie diff --git a/itest/list_on_test.go b/itest/list_on_test.go index 7263b2451..5dacc0b1c 100644 --- a/itest/list_on_test.go +++ b/itest/list_on_test.go @@ -699,6 +699,10 @@ var allTestCases = []*lntest.TestCase{ Name: "rbf coop close", TestFunc: testCoopCloseRbf, }, + { + Name: "rbf coop close disconnect", + TestFunc: testRBFCoopCloseDisconnect, + }, { Name: "bump fee low budget", TestFunc: testBumpFeeLowBudget, diff --git a/itest/lnd_coop_close_rbf_test.go b/itest/lnd_coop_close_rbf_test.go index 870334648..5f8b15d40 100644 --- a/itest/lnd_coop_close_rbf_test.go +++ b/itest/lnd_coop_close_rbf_test.go @@ -131,3 +131,25 @@ func testCoopCloseRbf(ht *lntest.HarnessTest) { aliceClosingTxid := ht.WaitForChannelCloseEvent(aliceCloseStream) ht.AssertTxInBlock(block, aliceClosingTxid) } + +// testRBFCoopCloseDisconnect tests that when a node disconnects that the node +// is properly disconnected. +func testRBFCoopCloseDisconnect(ht *lntest.HarnessTest) { + rbfCoopFlags := []string{"--protocol.rbf-coop-close"} + + // To kick things off, we'll create two new nodes, then fund them with + // enough coins to make a 50/50 channel. + cfgs := [][]string{rbfCoopFlags, rbfCoopFlags} + params := lntest.OpenChannelParams{ + Amt: btcutil.Amount(1000000), + PushAmt: btcutil.Amount(1000000 / 2), + } + _, nodes := ht.CreateSimpleNetwork(cfgs, params) + alice, bob := nodes[0], nodes[1] + + // Make sure the nodes are connected. + ht.AssertConnected(alice, bob) + + // Disconnect Bob from Alice. + ht.DisconnectNodes(alice, bob) +} diff --git a/peer/brontide.go b/peer/brontide.go index 17e9bc409..43dbcb419 100644 --- a/peer/brontide.go +++ b/peer/brontide.go @@ -3804,18 +3804,28 @@ func (p *Brontide) chanFlushEventSentinel(chanCloser *chancloser.RbfChanCloser, // We'll wait until the channel enters the ChannelFlushing state. We // exit after a success loop. As after the first RBF iteration, the // channel will always be flushed. - for newState := range newStateChan { - if _, ok := newState.(*chancloser.ChannelFlushing); ok { - peerLog.Infof("ChannelPoint(%v): rbf coop "+ - "close is awaiting a flushed state, "+ - "registering with link..., ", - channel.ChannelPoint()) + for { + select { + case newState, ok := <-newStateChan: + if !ok { + return + } - // Request the link to send the event once the channel - // is flushed. We only need this event sent once, so we - // can exit now. - link.OnFlushedOnce(sendChanFlushed) + if _, ok := newState.(*chancloser.ChannelFlushing); ok { + peerLog.Infof("ChannelPoint(%v): rbf coop "+ + "close is awaiting a flushed state, "+ + "registering with link..., ", + channel.ChannelPoint()) + // Request the link to send the event once the + // channel is flushed. We only need this event + // sent once, so we can exit now. + link.OnFlushedOnce(sendChanFlushed) + + return + } + + case <-p.cg.Done(): return } }