From c525c3d34e353936d929f086eacbb59a49b796a2 Mon Sep 17 00:00:00 2001 From: yyforyongyu Date: Fri, 27 Aug 2021 21:00:52 +0800 Subject: [PATCH] itest: test wipe forwarind packages --- lntest/itest/lnd_test_list_on_test.go | 4 + lntest/itest/lnd_wipe_fwdpkgs_test.go | 227 ++++++++++++++++++++++++++ 2 files changed, 231 insertions(+) create mode 100644 lntest/itest/lnd_wipe_fwdpkgs_test.go diff --git a/lntest/itest/lnd_test_list_on_test.go b/lntest/itest/lnd_test_list_on_test.go index 079786635..a3acbcaf8 100644 --- a/lntest/itest/lnd_test_list_on_test.go +++ b/lntest/itest/lnd_test_list_on_test.go @@ -342,4 +342,8 @@ var allTestCases = []*testCase{ name: "rpc middleware interceptor", test: testRPCMiddlewareInterceptor, }, + { + name: "wipe forwarding packages", + test: testWipeForwardingPackages, + }, } diff --git a/lntest/itest/lnd_wipe_fwdpkgs_test.go b/lntest/itest/lnd_wipe_fwdpkgs_test.go new file mode 100644 index 000000000..8ff6da685 --- /dev/null +++ b/lntest/itest/lnd_wipe_fwdpkgs_test.go @@ -0,0 +1,227 @@ +package itest + +import ( + "context" + "testing" + "time" + + "github.com/lightningnetwork/lnd/chainreg" + "github.com/lightningnetwork/lnd/lnrpc" + "github.com/lightningnetwork/lnd/lnrpc/routerrpc" + "github.com/lightningnetwork/lnd/lntest" + "github.com/stretchr/testify/require" +) + +type pendingChan *lnrpc.PendingChannelsResponse_PendingChannel + +// testWipeForwardingPackagesLocal tests that when a channel is closed, either +// through local force close, remote close, or coop close, all the forwarding +// packages of that particular channel are deleted. The test creates a +// topology as Alice -> Bob -> Carol, and sends payments from Alice to Carol +// through Bob. It then performs the test in two steps, +// - Bob force closes the channel Alice->Bob, and checks from both Bob's +// PoV(local force close) and Alice's Pov(remote close) that the forwarding +// packages are wiped. +// - Bob coop closes the channel Bob->Carol, and checks from both Bob PoVs that +// the forwarding packages are wiped. +func testWipeForwardingPackages(net *lntest.NetworkHarness, + t *harnessTest) { + + // Setup the test and get the channel points. + pointAB, pointBC, carol, cleanUp := setupFwdPkgTest(net, t) + defer cleanUp() + + // Firstly, Bob force closes the channel. + _, _, err := net.CloseChannel(net.Bob, pointAB, true) + require.NoError(t.t, err, "unable to force close channel") + + // Now that the channel has been force closed, it should show up in + // bob's PendingChannels RPC under the waiting close section. + pendingChan := assertWaitingCloseChannel(t.t, net.Bob) + + // Check that Bob has created forwarding packages. We don't care the + // exact number here as long as these packages are deleted when the + // channel is closed. + require.NotZero(t.t, pendingChan.NumForwardingPackages) + + // Mine 1 block to get the closing transaction confirmed. + _, err = net.Miner.Client.Generate(1) + require.NoError(t.t, err, "unable to mine blocks") + + // Now that the closing transaction is confirmed, the above waiting + // close channel should now become pending force closed channel. + pendingChan = assertPendingForceClosedChannel(t.t, net.Bob) + + // Check the forwarding pacakges are deleted. + require.Zero(t.t, pendingChan.NumForwardingPackages) + + // For Alice, the forwarding packages should have been wiped too. + pendingChanAlice := assertPendingForceClosedChannel(t.t, net.Alice) + require.Zero(t.t, pendingChanAlice.NumForwardingPackages) + + // Secondly, Bob coop closes the channel. + _, _, err = net.CloseChannel(net.Bob, pointBC, false) + require.NoError(t.t, err, "unable to coop close channel") + + // Now that the channel has been coop closed, it should show up in + // bob's PendingChannels RPC under the waiting close section. + pendingChan = assertWaitingCloseChannel(t.t, net.Bob) + + // Check that Bob has created forwarding packages. We don't care the + // exact number here as long as these packages are deleted when the + // channel is closed. + require.NotZero(t.t, pendingChan.NumForwardingPackages) + + // Since it's a coop close, Carol should see the waiting close channel + // too. + pendingChanCarol := assertWaitingCloseChannel(t.t, carol) + require.NotZero(t.t, pendingChanCarol.NumForwardingPackages) + + // Mine 1 block to get the closing transaction confirmed. + _, err = net.Miner.Client.Generate(1) + require.NoError(t.t, err, "unable to mine blocks") + + // Now that the closing transaction is confirmed, the above waiting + // close channel should now become pending closed channel. Note that + // the name PendingForceClosingChannels is a bit confusing, what it + // really contains is channels whose closing tx has been broadcast. + pendingChan = assertPendingForceClosedChannel(t.t, net.Bob) + + // Check the forwarding pacakges are deleted. + require.Zero(t.t, pendingChan.NumForwardingPackages) + + // Mine a block to confirm sweep transactions such that they + // don't remain in the mempool for any subsequent tests. + _, err = net.Miner.Client.Generate(1) + require.NoError(t.t, err, "unable to mine blocks") +} + +// setupFwdPkgTest prepares the wipe forwarding packages tests. It creates a +// network topology that has a channel direction: Alice -> Bob -> Carol, sends +// several payments from Alice to Carol, and returns the two channel points(one +// for Alice and Bob, the other for Bob and Carol), the node Carol, and a +// cleanup function to be used when the test finishes. +func setupFwdPkgTest(net *lntest.NetworkHarness, + t *harnessTest) (*lnrpc.ChannelPoint, *lnrpc.ChannelPoint, + *lntest.HarnessNode, func()) { + + ctxb := context.Background() + + const ( + chanAmt = 10e6 + paymentAmt = 10e4 + finalCTLVDelta = chainreg.DefaultBitcoinTimeLockDelta + numInvoices = 3 + ) + + // Grab Alice and Bob from harness net. + alice, bob := net.Alice, net.Bob + + // Create a new node Carol, which will create invoices that require + // Alice to pay. + carol := net.NewNode(t.t, "Carol", nil) + + // Connect Bob to Carol. + net.ConnectNodes(t.t, bob, carol) + + // Open a channel between Alice and Bob. + chanPointAB := openChannelAndAssert( + t, net, alice, bob, lntest.OpenChannelParams{ + Amt: chanAmt, + }, + ) + + // Open a channel between Bob and Carol. + chanPointBC := openChannelAndAssert( + t, net, bob, carol, lntest.OpenChannelParams{ + Amt: chanAmt, + }, + ) + + ctxt, cancel := context.WithTimeout(ctxb, defaultTimeout) + defer cancel() + + // Alice sends several payments to Carol through Bob, which triggers + // Bob to create forwarding packages. + for i := 0; i < numInvoices; i++ { + // Add an invoice for Carol. + invoice := &lnrpc.Invoice{Memo: "testing", Value: paymentAmt} + invoiceResp, err := carol.AddInvoice(ctxt, invoice) + require.NoError(t.t, err, "unable to add invoice") + + // Alice sends a payment to Carol through Bob. + sendAndAssertSuccess( + t, net.Alice, &routerrpc.SendPaymentRequest{ + PaymentRequest: invoiceResp.PaymentRequest, + TimeoutSeconds: 60, + FeeLimitSat: noFeeLimitMsat, + }, + ) + } + + return chanPointAB, chanPointBC, carol, func() { + shutdownAndAssert(net, t, alice) + shutdownAndAssert(net, t, bob) + shutdownAndAssert(net, t, carol) + } +} + +// assertWaitingCloseChannel checks there is a single channel that is waiting +// for close and returns the channel found. +func assertWaitingCloseChannel(t *testing.T, + node *lntest.HarnessNode) pendingChan { + + ctxb := context.Background() + + var channel pendingChan + require.Eventually(t, func() bool { + ctxt, cancel := context.WithTimeout(ctxb, defaultTimeout) + defer cancel() + + req := &lnrpc.PendingChannelsRequest{} + resp, err := node.PendingChannels(ctxt, req) + + // We require the RPC call to be succeeded and won't retry upon + // an error. + require.NoError(t, err, "unable to query for pending channels") + + if err := checkNumWaitingCloseChannels(resp, 1); err != nil { + return false + } + + channel = resp.WaitingCloseChannels[0].Channel + return true + }, defaultTimeout, 200*time.Millisecond) + + return channel +} + +// assertForceClosedChannel checks there is a single channel that is pending +// force closed and returns the channel found. +func assertPendingForceClosedChannel(t *testing.T, + node *lntest.HarnessNode) pendingChan { + + ctxb := context.Background() + + var channel pendingChan + require.Eventually(t, func() bool { + ctxt, cancel := context.WithTimeout(ctxb, defaultTimeout) + defer cancel() + + req := &lnrpc.PendingChannelsRequest{} + resp, err := node.PendingChannels(ctxt, req) + + // We require the RPC call to be succeeded and won't retry upon + // an error. + require.NoError(t, err, "unable to query for pending channels") + + if err := checkNumForceClosedChannels(resp, 1); err != nil { + return false + } + + channel = resp.PendingForceClosingChannels[0].Channel + return true + }, defaultTimeout, 200*time.Millisecond) + + return channel +}