From 9943bb8539da6449785a6b9957e7f7e60320eff1 Mon Sep 17 00:00:00 2001 From: yyforyongyu Date: Tue, 9 Aug 2022 19:15:20 +0800 Subject: [PATCH] itest: refactor `testSwitchOfflineDeliveryOutgoingOffline` --- lntest/itest/list_on_test.go | 4 + lntest/itest/lnd_switch_test.go | 255 ++++++-------------------- lntest/itest/lnd_test_list_on_test.go | 4 - 3 files changed, 55 insertions(+), 208 deletions(-) diff --git a/lntest/itest/list_on_test.go b/lntest/itest/list_on_test.go index 00259372c..eb4474da7 100644 --- a/lntest/itest/list_on_test.go +++ b/lntest/itest/list_on_test.go @@ -381,4 +381,8 @@ var allTestCasesTemp = []*lntemp.TestCase{ Name: "switch offline delivery persistence", TestFunc: testSwitchOfflineDeliveryPersistence, }, + { + Name: "switch offline delivery outgoing offline", + TestFunc: testSwitchOfflineDeliveryOutgoingOffline, + }, } diff --git a/lntest/itest/lnd_switch_test.go b/lntest/itest/lnd_switch_test.go index 1c2a408dd..fd90be4a7 100644 --- a/lntest/itest/lnd_switch_test.go +++ b/lntest/itest/lnd_switch_test.go @@ -2,12 +2,9 @@ package itest import ( "github.com/btcsuite/btcd/btcutil" - "github.com/btcsuite/btcd/wire" "github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lntemp" "github.com/lightningnetwork/lnd/lntemp/node" - "github.com/lightningnetwork/lnd/lntest" - "github.com/lightningnetwork/lnd/lntest/wait" "github.com/stretchr/testify/require" ) @@ -250,11 +247,12 @@ func testSwitchOfflineDeliveryPersistence(ht *lntemp.HarnessTest) { s.assertAmoutPaid(ht, amountPaid, numPayments+1) } -// testSwitchOfflineDeliveryOutgoingOffline constructs a set of multihop payments, -// and tests that the returning payments are not lost if a peer on the backwards -// path is offline when the settle/fails are received AND the peer buffering the -// responses is completely restarts. We expect the payments to be reloaded from -// disk, and transmitted as soon as the intermediaries are reconnected. +// testSwitchOfflineDeliveryOutgoingOffline constructs a set of multihop +// payments, and tests that the returning payments are not lost if a peer on +// the backwards path is offline when the settle/fails are received AND the +// peer buffering the responses is completely restarts. We expect the payments +// to be reloaded from disk, and transmitted as soon as the intermediaries are +// reconnected. // // The general flow of this test: // 1. Carol --> Dave --> Alice --> Bob forward payment @@ -262,239 +260,88 @@ func testSwitchOfflineDeliveryPersistence(ht *lntemp.HarnessTest) { // 3. Carol --- Dave X Alice <-- Bob settle last hop // 4. Carol --- Dave X X shutdown Bob, restart Alice // 5. Carol <-- Dave <-- Alice X expect settle to propagate -func testSwitchOfflineDeliveryOutgoingOffline( - net *lntest.NetworkHarness, t *harnessTest) { - - const chanAmt = btcutil.Amount(1000000) - const pushAmt = btcutil.Amount(900000) - var networkChans []*lnrpc.ChannelPoint - - // Open a channel with 100k satoshis between Alice and Bob with Alice - // being the sole funder of the channel. - chanPointAlice := openChannelAndAssert( - t, net, net.Alice, net.Bob, - lntest.OpenChannelParams{ - Amt: chanAmt, - PushAmt: pushAmt, - }, - ) - networkChans = append(networkChans, chanPointAlice) - - aliceChanTXID, err := lnrpc.GetChanPointFundingTxid(chanPointAlice) - if err != nil { - t.Fatalf("unable to get txid: %v", err) - } - aliceFundPoint := wire.OutPoint{ - Hash: *aliceChanTXID, - Index: chanPointAlice.OutputIndex, - } - - // As preliminary setup, we'll create two new nodes: Carol and Dave, - // such that we now have a 4 ndoe, 3 channel topology. Dave will make - // a channel with Alice, and Carol with Dave. After this setup, the - // network topology should now look like: - // Carol -> Dave -> Alice -> Bob - // - // First, we'll create Dave and establish a channel to Alice. - dave := net.NewNode(t.t, "Dave", nil) - defer shutdownAndAssert(net, t, dave) - - net.ConnectNodes(t.t, dave, net.Alice) - net.SendCoins(t.t, btcutil.SatoshiPerBitcoin, dave) - - chanPointDave := openChannelAndAssert( - t, net, dave, net.Alice, - lntest.OpenChannelParams{ - Amt: chanAmt, - PushAmt: pushAmt, - }, - ) - networkChans = append(networkChans, chanPointDave) - daveChanTXID, err := lnrpc.GetChanPointFundingTxid(chanPointDave) - if err != nil { - t.Fatalf("unable to get txid: %v", err) - } - daveFundPoint := wire.OutPoint{ - Hash: *daveChanTXID, - Index: chanPointDave.OutputIndex, - } - - // Next, we'll create Carol and establish a channel to from her to - // Dave. Carol is started in htlchodl mode so that we can disconnect the - // intermediary hops before starting the settle. - carol := net.NewNode(t.t, "Carol", []string{"--hodl.exit-settle"}) - net.ConnectNodes(t.t, carol, dave) - net.SendCoins(t.t, btcutil.SatoshiPerBitcoin, carol) - - chanPointCarol := openChannelAndAssert( - t, net, carol, dave, - lntest.OpenChannelParams{ - Amt: chanAmt, - PushAmt: pushAmt, - }, - ) - networkChans = append(networkChans, chanPointCarol) - - carolChanTXID, err := lnrpc.GetChanPointFundingTxid(chanPointCarol) - if err != nil { - t.Fatalf("unable to get txid: %v", err) - } - carolFundPoint := wire.OutPoint{ - Hash: *carolChanTXID, - Index: chanPointCarol.OutputIndex, - } - - // Wait for all nodes to have seen all channels. - nodes := []*lntest.HarnessNode{net.Alice, net.Bob, carol, dave} - nodeNames := []string{"Alice", "Bob", "Carol", "Dave"} - for _, chanPoint := range networkChans { - for i, node := range nodes { - txid, err := lnrpc.GetChanPointFundingTxid(chanPoint) - if err != nil { - t.Fatalf("unable to get txid: %v", err) - } - point := wire.OutPoint{ - Hash: *txid, - Index: chanPoint.OutputIndex, - } - - err = node.WaitForNetworkChannelOpen(chanPoint) - if err != nil { - t.Fatalf("%s(%d): timeout waiting for "+ - "channel(%s) open: %v", nodeNames[i], - node.NodeID, point, err) - } - } - } - - // Create 5 invoices for Carol, which expect a payment from Bob for 1k - // satoshis with a different preimage each time. - const numPayments = 5 - const paymentAmt = 1000 - payReqs, _, _, err := createPayReqs( - carol, paymentAmt, numPayments, - ) - if err != nil { - t.Fatalf("unable to create pay reqs: %v", err) - } - - // We'll wait for all parties to recognize the new channels within the - // network. - err = dave.WaitForNetworkChannelOpen(chanPointDave) - if err != nil { - t.Fatalf("dave didn't advertise his channel: %v", err) - } - err = carol.WaitForNetworkChannelOpen(chanPointCarol) - if err != nil { - t.Fatalf("carol didn't advertise her channel in time: %v", - err) - } - - // Using Carol as the source, pay to the 5 invoices from Bob created - // above. - err = completePaymentRequests( - net.Bob, net.Bob.RouterClient, payReqs, false, - ) - if err != nil { - t.Fatalf("unable to send payments: %v", err) - } - - // Wait for all payments to reach Carol. - var predErr error - err = wait.Predicate(func() bool { - return assertNumActiveHtlcs(nodes, numPayments) == nil - }, defaultTimeout) - if err != nil { - t.Fatalf("htlc mismatch: %v", predErr) - } +func testSwitchOfflineDeliveryOutgoingOffline(ht *lntemp.HarnessTest) { + // Setup our test scenario. We should now have four nodes running with + // three channels. Note that we won't call the cleanUp function here as + // we will manually stop the node Carol and her channel. + s := setupScenarioFourNodes(ht) // Disconnect the two intermediaries, Alice and Dave, so that when carol // restarts, the response will be held by Dave. - if err := net.StopNode(net.Alice); err != nil { - t.Fatalf("unable to shutdown alice: %v", err) - } + restartAlice := ht.SuspendNode(s.alice) // Now restart carol without hodl mode, to settle back the outstanding // payments. - carol.SetExtraArgs(nil) - if err := net.RestartNode(carol, nil); err != nil { - t.Fatalf("Node restart failed: %v", err) - } + s.carol.SetExtraArgs(nil) + ht.RestartNode(s.carol) // Wait for Carol to report no outstanding htlcs. - carolNode := []*lntest.HarnessNode{carol} - err = wait.Predicate(func() bool { - predErr = assertNumActiveHtlcs(carolNode, 0) - if predErr != nil { - return false - } - - predErr = assertNumActiveHtlcsChanPoint(dave, carolFundPoint, 0) - return predErr == nil - }, defaultTimeout) - if err != nil { - t.Fatalf("htlc mismatch: %v", predErr) - } + ht.AssertNumActiveHtlcs(s.carol, 0) + // As an intermediate node, Dave should now have zero outgoing HTLCs + // and 5 incoming HTLCs from Alice. + ht.AssertNumActiveHtlcs(s.dave, numPayments) // Now check that the total amount was transferred from Dave to Carol. // The amount transferred should be exactly equal to the invoice total // payment amount, 5k satsohis. const amountPaid = int64(5000) - assertAmountPaid(t, "Dave(local) => Carol(remote)", carol, - carolFundPoint, int64(0), amountPaid) - assertAmountPaid(t, "Dave(local) => Carol(remote)", dave, - carolFundPoint, amountPaid, int64(0)) + ht.AssertAmountPaid( + "Dave(local) => Carol(remote)", s.carol, + s.chanPointCarolDave, int64(0), amountPaid, + ) + ht.AssertAmountPaid( + "Dave(local) => Carol(remote)", s.dave, + s.chanPointCarolDave, amountPaid, int64(0), + ) // Shutdown carol and leave her offline for the rest of the test. This // is critical, as we wish to see if Dave can propragate settles even if // the outgoing link is never revived. - shutdownAndAssert(net, t, carol) + ht.Shutdown(s.carol) // Now restart Dave, ensuring he is both persisting the settles, and is // able to reforward them to Alice after recovering from a restart. - if err := net.RestartNode(dave, nil); err != nil { - t.Fatalf("unable to restart dave: %v", err) - } - if err = net.RestartNode(net.Alice, nil); err != nil { - t.Fatalf("unable to restart alice: %v", err) - } + ht.RestartNode(s.dave) + require.NoErrorf(ht, restartAlice(), "restart alice failed") // Ensure that Dave is reconnected to Alice before waiting for the // htlcs to clear. - net.EnsureConnected(t.t, dave, net.Alice) + ht.EnsureConnected(s.dave, s.alice) // Since Carol has been shutdown permanently, we will wait until all // other nodes in the network report no active htlcs. - nodesMinusCarol := []*lntest.HarnessNode{net.Bob, net.Alice, dave} - err = wait.Predicate(func() bool { - predErr = assertNumActiveHtlcs(nodesMinusCarol, 0) - return predErr == nil - }, defaultTimeout) - if err != nil { - t.Fatalf("htlc mismatch: %v", predErr) - } + ht.AssertNumActiveHtlcs(s.alice, 0) + ht.AssertNumActiveHtlcs(s.bob, 0) + ht.AssertNumActiveHtlcs(s.dave, 0) // When asserting the amount of satoshis moved, we'll factor in the // default base fee, as we didn't modify the fee structure when // creating the seed nodes in the network. - const baseFee = 1 // At this point, all channels (minus Carol, who is shutdown) should // show a shift of 5k satoshis towards Carol. The order of asserts // corresponds to increasing of time is needed to embed the HTLC in // commitment transaction, in channel Bob->Alice->David, order is // David, Alice, Bob. - assertAmountPaid(t, "Alice(local) => Dave(remote)", dave, - daveFundPoint, int64(0), amountPaid+(baseFee*numPayments)) - assertAmountPaid(t, "Alice(local) => Dave(remote)", net.Alice, - daveFundPoint, amountPaid+(baseFee*numPayments), int64(0)) - assertAmountPaid(t, "Bob(local) => Alice(remote)", net.Alice, - aliceFundPoint, int64(0), amountPaid+((baseFee*numPayments)*2)) - assertAmountPaid(t, "Bob(local) => Alice(remote)", net.Bob, - aliceFundPoint, amountPaid+(baseFee*numPayments)*2, int64(0)) + ht.AssertAmountPaid( + "Alice(local) => Dave(remote)", s.dave, s.chanPointDaveAlice, + int64(0), amountPaid+(baseFee*numPayments), + ) + ht.AssertAmountPaid( + "Alice(local) => Dave(remote)", s.alice, s.chanPointDaveAlice, + amountPaid+(baseFee*numPayments), int64(0), + ) + ht.AssertAmountPaid( + "Bob(local) => Alice(remote)", s.alice, s.chanPointAliceBob, + int64(0), amountPaid+((baseFee*numPayments)*2), + ) + ht.AssertAmountPaid( + "Bob(local) => Alice(remote)", s.bob, s.chanPointAliceBob, + amountPaid+(baseFee*numPayments)*2, int64(0), + ) - closeChannelAndAssert(t, net, net.Alice, chanPointAlice, false) - closeChannelAndAssert(t, net, dave, chanPointDave, false) + ht.CloseChannel(s.alice, s.chanPointAliceBob) + ht.CloseChannel(s.dave, s.chanPointDaveAlice) } // scenarioFourNodes specifies a scenario which we have a topology that has diff --git a/lntest/itest/lnd_test_list_on_test.go b/lntest/itest/lnd_test_list_on_test.go index 8207da236..c10e02b44 100644 --- a/lntest/itest/lnd_test_list_on_test.go +++ b/lntest/itest/lnd_test_list_on_test.go @@ -28,10 +28,6 @@ var allTestCases = []*testCase{ name: "async bidirectional payments", test: testBidirectionalAsyncPayments, }, - { - name: "switch offline delivery outgoing offline", - test: testSwitchOfflineDeliveryOutgoingOffline, - }, { name: "cpfp", test: testCPFP,