From d7cbdea9db462c07fc1715e2b29c03ebf9236c29 Mon Sep 17 00:00:00 2001 From: "Johan T. Halseth" Date: Tue, 6 Nov 2018 11:03:01 +0100 Subject: [PATCH] lnd_test: add waitForChannelPendingForceClose This commit introduces a new utility method waitForChannelPendingForceClose, that is used to ensure a force closed channel has been recognized by the UTXO nursery, and is ready to be swept as soon as it matures. The commit also utilizes this method to properly wait before mining blocks in certain tests, as it makes sure that the UTXO nursery will react properly to the new blocks. --- lnd_test.go | 141 ++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 126 insertions(+), 15 deletions(-) diff --git a/lnd_test.go b/lnd_test.go index a23f111f..b0af5066 100644 --- a/lnd_test.go +++ b/lnd_test.go @@ -280,6 +280,89 @@ func closeChannelAndAssert(ctx context.Context, t *harnessTest, return closingTxid } +// waitForChannelPendingForceClose waits for the node to report that the +// channel is pending force close, and that the UTXO nursery is aware of it. +func waitForChannelPendingForceClose(ctx context.Context, + node *lntest.HarnessNode, fundingChanPoint *lnrpc.ChannelPoint) error { + + txidHash, err := getChanPointFundingTxid(fundingChanPoint) + if err != nil { + return err + } + + txid, err := chainhash.NewHash(txidHash) + if err != nil { + return err + } + + op := wire.OutPoint{ + Hash: *txid, + Index: fundingChanPoint.OutputIndex, + } + + var predErr error + err = lntest.WaitPredicate(func() bool { + pendingChansRequest := &lnrpc.PendingChannelsRequest{} + pendingChanResp, err := node.PendingChannels( + ctx, pendingChansRequest, + ) + if err != nil { + predErr = fmt.Errorf("unable to get pending "+ + "channels: %v", err) + return false + } + + forceClose, err := findForceClosedChannel(pendingChanResp, &op) + if err != nil { + predErr = err + return false + } + + // We must wait until the UTXO nursery has received the channel + // and is aware of its maturity height. + if forceClose.MaturityHeight == 0 { + predErr = fmt.Errorf("channel had maturity height of 0") + return false + } + return true + }, time.Second*15) + if err != nil { + return predErr + } + + return nil +} + +// cleanupForceClose mines a force close commitment found in the mempool and +// the following sweep transaction from the force closing node. +func cleanupForceClose(t *harnessTest, net *lntest.NetworkHarness, + node *lntest.HarnessNode, chanPoint *lnrpc.ChannelPoint) { + ctxb := context.Background() + + // Wait for the channel to be marked pending force close. + ctxt, _ := context.WithTimeout(ctxb, defaultTimeout) + err := waitForChannelPendingForceClose(ctxt, node, chanPoint) + if err != nil { + t.Fatalf("channel not pending force close: %v", err) + } + + // Mine enough blocks for the node to sweep its funds from the force + // closed channel. + _, err = net.Miner.Node.Generate(defaultCSV) + if err != nil { + t.Fatalf("unable to generate blocks: %v", err) + } + + // THe node should now sweep the funds, clean up by mining the sweeping + // tx. + txid, err := waitForTxInMempool(net.Miner.Node, minerMempoolTimeout) + if err != nil { + t.Fatalf("unable to find sweeping tx in mempool: %v", err) + } + block := mineBlocks(t, net, 1)[0] + assertTxInBlock(t, block, txid) +} + // numOpenChannelsPending sends an RPC request to a node to get a count of the // node's channels that are currently in a pending state (with a broadcast, but // not confirmed funding transaction). @@ -1701,12 +1784,8 @@ func testDisconnectingTargetPeer(net *lntest.NetworkHarness, t *harnessTest) { // Check existing connection. assertNumConnections(ctxb, t, net.Alice, net.Bob, 1) - // Mine enough blocks to clear the force closed outputs from the UTXO - // nursery. - if _, err := net.Miner.Node.Generate(4); err != nil { - t.Fatalf("unable to mine blocks: %v", err) - } - time.Sleep(300 * time.Millisecond) + // Cleanup by mining the force close and sweep transaction. + cleanupForceClose(t, net, net.Alice, chanPoint) } // testFundingPersistence is intended to ensure that the Funding Manager @@ -2989,6 +3068,9 @@ func testSphinxReplayPersistence(net *lntest.NetworkHarness, t *harnessTest) { ctxt, _ = context.WithTimeout(ctxb, timeout) closeChannelAndAssert(ctxt, t, net, carol, chanPoint, true) + + // Cleanup by mining the force close and sweep transaction. + cleanupForceClose(t, net, carol, chanPoint) } func testSingleHopInvoice(net *lntest.NetworkHarness, t *harnessTest) { @@ -4838,6 +4920,9 @@ func testInvoiceRoutingHints(net *lntest.NetworkHarness, t *harnessTest) { // is offline. ctxt, _ = context.WithTimeout(ctxb, timeout) closeChannelAndAssert(ctxt, t, net, net.Alice, chanPointEve, true) + + // Cleanup by mining the force close and sweep transaction. + cleanupForceClose(t, net, net.Alice, chanPointEve) } // testMultiHopOverPrivateChannels tests that private channels can be used as @@ -5892,9 +5977,12 @@ func testGarbageCollectLinkNodes(net *lntest.NetworkHarness, t *harnessTest) { ctxt, _ = context.WithTimeout(ctxb, timeout) closeChannelAndAssert(ctxt, t, net, net.Alice, forceCloseChanPoint, true) + // Cleanup by mining the force close and sweep transaction. + cleanupForceClose(t, net, net.Alice, forceCloseChanPoint) + // We'll need to mine some blocks in order to mark the channel fully // closed. - _, err = net.Miner.Node.Generate(defaultBitcoinTimeLockDelta) + _, err = net.Miner.Node.Generate(defaultBitcoinTimeLockDelta - defaultCSV) if err != nil { t.Fatalf("unable to generate blocks: %v", err) } @@ -7589,14 +7677,12 @@ out: ctxt, _ = context.WithTimeout(ctxb, timeout) closeChannelAndAssert(ctxt, t, net, net.Alice, chanPointAlice, false) - // Force close Bob's final channel, also mining enough blocks to - // trigger a sweep of the funds by the utxoNursery. - // TODO(roasbeef): use config value for default CSV here. - ctxt, _ = context.WithTimeout(ctxb, timeout) + // Force close Bob's final channel. + ctxt, _ = context.WithTimeout(ctxb, channelCloseTimeout) closeChannelAndAssert(ctxt, t, net, net.Bob, chanPointBob, true) - if _, err := net.Miner.Node.Generate(5); err != nil { - t.Fatalf("unable to generate blocks: %v", err) - } + + // Cleanup by mining the force close and sweep transaction. + cleanupForceClose(t, net, net.Bob, chanPointBob) } // graphSubscription houses the proxied update and error chans for a node's @@ -9552,6 +9638,8 @@ func testMultiHopRemoteForceCloseOnChainHtlcTimeout(net *lntest.NetworkHarness, // registration. As a result, we'll mine another block and // repeat the check. If it doesn't go through this time, then // we'll fail. + // TODO(halseth): can we use waitForChannelPendingForceClose to + // avoid this hack? if _, err := net.Miner.Node.Generate(1); err != nil { t.Fatalf("unable to generate block: %v", err) } @@ -10003,10 +10091,30 @@ func testMultiHopHtlcRemoteChainClaim(net *lntest.NetworkHarness, t *harnessTest aliceForceClose := closeChannelAndAssert(ctxt, t, net, net.Alice, aliceChanPoint, true) + // Wait for the channel to be marked pending force close. + ctxt, _ = context.WithTimeout(ctxb, defaultTimeout) + err = waitForChannelPendingForceClose(ctxt, net.Alice, aliceChanPoint) + if err != nil { + t.Fatalf("channel not pending force close: %v", err) + } + + // Mine enough blocks for Alice to sweep her funds from the force + // closed channel. + _, err = net.Miner.Node.Generate(defaultCSV) + if err != nil { + t.Fatalf("unable to generate blocks: %v", err) + } + + // Alice should now sweep her funds. + _, err = waitForTxInMempool(net.Miner.Node, minerMempoolTimeout) + if err != nil { + t.Fatalf("unable to find sweeping tx in mempool: %v", err) + } + // We'll now mine enough blocks so Carol decides that she needs to go // on-chain to claim the HTLC as Bob has been inactive. claimDelta := uint32(2 * defaultBroadcastDelta) - numBlocks := uint32(defaultBitcoinTimeLockDelta - claimDelta) + numBlocks := uint32(defaultBitcoinTimeLockDelta-claimDelta) - defaultCSV if _, err := net.Miner.Node.Generate(numBlocks); err != nil { t.Fatalf("unable to generate blocks") } @@ -12281,6 +12389,9 @@ func testAbandonChannel(net *lntest.NetworkHarness, t *harnessTest) { // lnd instance. ctxt, _ = context.WithTimeout(ctxb, timeout) closeChannelAndAssert(ctxt, t, net, net.Bob, chanPoint, true) + + // Cleanup by mining the force close and sweep transaction. + cleanupForceClose(t, net, net.Bob, chanPoint) } type testCase struct {