From 0c4948b40bd09758bcafa0ee9e86fdb34c6d84bb Mon Sep 17 00:00:00 2001 From: "Johan T. Halseth" Date: Tue, 20 Nov 2018 15:09:46 +0100 Subject: [PATCH] lnd test: add offline scenario to testDataLossProtection This adds the scenario where a channel is closed while the node is offline, the node loses state and comes back online. In this case the node should attempt to resync the channel, and the peer should resend a channel sync message for the closed channel, such that the node can retrieve its funds. --- lnd_test.go | 85 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) diff --git a/lnd_test.go b/lnd_test.go index a3a093e5..71938818 100644 --- a/lnd_test.go +++ b/lnd_test.go @@ -7272,6 +7272,91 @@ func testDataLossProtection(net *lntest.NetworkHarness, t *harnessTest) { assertNodeNumChannels(t, ctxb, dave, 0) assertNodeNumChannels(t, ctxb, carol, 0) + + // As a second part of this test, we will test the the scenario where a + // channel is closed while Dave is offline, loses his state and comes + // back online. In this case the node should attempt to resync the + // channel, and the peer should resend a channel sync message for the + // closed channel, such that Dave can retrieve his funds. + // + // We start by letting Dave time travel back to an outdated state. + restartDave, chanPoint2, daveStartingBalance, err := timeTravel(dave) + if err != nil { + t.Fatalf("unable to time travel eve: %v", err) + } + + carolBalResp, err = carol.WalletBalance(ctxb, balReq) + if err != nil { + t.Fatalf("unable to get carol's balance: %v", err) + } + carolStartingBalance = carolBalResp.ConfirmedBalance + + // Now let Carol force close the channel while Dave is offline. + ctxt, _ := context.WithTimeout(ctxb, timeout) + closeChannelAndAssert(ctxt, t, net, carol, chanPoint2, true) + + // Wait for the channel to be marked pending force close. + ctxt, _ = context.WithTimeout(ctxb, timeout) + err = waitForChannelPendingForceClose(ctxt, carol, chanPoint2) + if err != nil { + t.Fatalf("channel not pending force close: %v", err) + } + + // Mine enough blocks for Carol to sweep her funds. + mineBlocks(t, net, defaultCSV) + + carolSweep, err = waitForTxInMempool(net.Miner.Node, 15*time.Second) + if err != nil { + t.Fatalf("unable to find Carol's sweep tx in mempool: %v", err) + } + block = mineBlocks(t, net, 1)[0] + assertTxInBlock(t, block, carolSweep) + + // Now the channel should be fully closed also from Carol's POV. + assertNumPendingChannels(t, carol, 0, 0) + + // Make sure Carol got her balance back. + carolBalResp, err = carol.WalletBalance(ctxb, balReq) + if err != nil { + t.Fatalf("unable to get carol's balance: %v", err) + } + carolBalance = carolBalResp.ConfirmedBalance + if carolBalance <= carolStartingBalance { + t.Fatalf("expected carol to have balance above %d, "+ + "instead had %v", carolStartingBalance, + carolBalance) + } + + assertNodeNumChannels(t, ctxb, carol, 0) + + // When Dave comes online, he will reconnect to Carol, try to resync + // the channel, but it will already be closed. Carol should resend the + // information Dave needs to sweep his funds. + if err := restartDave(); err != nil { + t.Fatalf("unabel to restart Eve: %v", err) + } + + // Dave should sweep his funds. + _, err = waitForTxInMempool(net.Miner.Node, 15*time.Second) + if err != nil { + t.Fatalf("unable to find Dave's sweep tx in mempool: %v", err) + } + + // Mine a block to confirm the sweep, and make sure Dave got his + // balance back. + mineBlocks(t, net, 1) + assertNodeNumChannels(t, ctxb, dave, 0) + + daveBalResp, err = dave.WalletBalance(ctxb, balReq) + if err != nil { + t.Fatalf("unable to get dave's balance: %v", err) + } + + daveBalance = daveBalResp.ConfirmedBalance + if daveBalance <= daveStartingBalance { + t.Fatalf("expected dave to have balance above %d, intead had %v", + daveStartingBalance, daveBalance) + } } // assertNodeNumChannels polls the provided node's list channels rpc until it