From e891dd0fd388a1f2a62f3afdc42e21e73606e134 Mon Sep 17 00:00:00 2001 From: Wilmer Paulino Date: Mon, 26 Jul 2021 13:03:41 -0700 Subject: [PATCH] itest: test backup restore of script enforced lease channel type --- lntest/itest/assertions.go | 117 ++++++++++++++++++------ lntest/itest/lnd_channel_backup_test.go | 81 +++++++++++----- lntest/itest/lnd_misc_test.go | 2 +- 3 files changed, 148 insertions(+), 52 deletions(-) diff --git a/lntest/itest/assertions.go b/lntest/itest/assertions.go index 5f0f79a38..071b07c48 100644 --- a/lntest/itest/assertions.go +++ b/lntest/itest/assertions.go @@ -1109,7 +1109,7 @@ func assertNumPendingChannels(t *harnessTest, node *lntest.HarnessNode, func assertDLPExecuted(net *lntest.NetworkHarness, t *harnessTest, carol *lntest.HarnessNode, carolStartingBalance int64, dave *lntest.HarnessNode, daveStartingBalance int64, - anchors bool) { + commitType lnrpc.CommitmentType) { // Increase the fee estimate so that the following force close tx will // be cpfp'ed. @@ -1124,7 +1124,7 @@ func assertDLPExecuted(net *lntest.NetworkHarness, t *harnessTest, // Upon reconnection, the nodes should detect that Dave is out of sync. // Carol should force close the channel using her latest commitment. expectedTxes := 1 - if anchors { + if commitTypeHasAnchors(commitType) { expectedTxes = 2 } _, err := waitForNTxsInMempool( @@ -1151,13 +1151,6 @@ func assertDLPExecuted(net *lntest.NetworkHarness, t *harnessTest, // Generate a single block, which should confirm the closing tx. _ = mineBlocks(t, net, 1, expectedTxes)[0] - // Dave should sweep his funds immediately, as they are not timelocked. - // We also expect Dave to sweep his anchor, if present. - _, err = waitForNTxsInMempool( - net.Miner.Client, expectedTxes, minerMempoolTimeout, - ) - require.NoError(t.t, err, "unable to find Dave's sweep tx in mempool") - // Dave should consider the channel pending force close (since he is // waiting for his sweep to confirm). assertNumPendingChannels(t, dave, 0, 1) @@ -1166,11 +1159,93 @@ func assertDLPExecuted(net *lntest.NetworkHarness, t *harnessTest, // before she can sweep her outputs. assertNumPendingChannels(t, carol, 0, 1) - // Mine the sweep tx. - _ = mineBlocks(t, net, 1, expectedTxes)[0] + if commitType == lnrpc.CommitmentType_SCRIPT_ENFORCED_LEASE { + // Dave should sweep his anchor only, since he still has the + // lease CLTV constraint on his commitment output. + _, err = waitForNTxsInMempool( + net.Miner.Client, 1, minerMempoolTimeout, + ) + require.NoError(t.t, err, "unable to find Dave's anchor sweep "+ + "tx in mempool") - // Now Dave should consider the channel fully closed. - assertNumPendingChannels(t, dave, 0, 0) + // Mine Dave's anchor sweep tx. + _ = mineBlocks(t, net, 1, 1)[0] + + // After Carol's output matures, she should also reclaim her + // funds. + // + // The commit sweep resolver publishes the sweep tx at + // defaultCSV-1 and we already mined one block after the + // commitmment was published, so take that into account. + mineBlocks(t, net, defaultCSV-1-1, 0) + carolSweep, err := waitForTxInMempool( + net.Miner.Client, minerMempoolTimeout, + ) + require.NoError(t.t, err, "unable to find Carol's sweep tx in "+ + "mempool") + block := mineBlocks(t, net, 1, 1)[0] + assertTxInBlock(t, block, carolSweep) + + // Now the channel should be fully closed also from Carol's POV. + assertNumPendingChannels(t, carol, 0, 0) + + // We'll now mine the remaining blocks to prompt Dave to sweep + // his CLTV-constrained output. + ctxt, cancel := context.WithTimeout(ctxb, defaultTimeout) + defer cancel() + resp, err := dave.PendingChannels( + ctxt, &lnrpc.PendingChannelsRequest{}, + ) + require.NoError(t.t, err) + blocksTilMaturity := + resp.PendingForceClosingChannels[0].BlocksTilMaturity + require.Positive(t.t, blocksTilMaturity) + + mineBlocks(t, net, uint32(blocksTilMaturity), 0) + daveSweep, err := waitForTxInMempool( + net.Miner.Client, minerMempoolTimeout, + ) + require.NoError(t.t, err, "unable to find Dave's sweep tx in "+ + "mempool") + block = mineBlocks(t, net, 1, 1)[0] + assertTxInBlock(t, block, daveSweep) + + // Now Dave should consider the channel fully closed. + assertNumPendingChannels(t, dave, 0, 0) + } else { + // Dave should sweep his funds immediately, as they are not + // timelocked. We also expect Dave to sweep his anchor, if + // present. + _, err = waitForNTxsInMempool( + net.Miner.Client, expectedTxes, minerMempoolTimeout, + ) + require.NoError(t.t, err, "unable to find Dave's sweep tx in "+ + "mempool") + + // Mine the sweep tx. + _ = mineBlocks(t, net, 1, expectedTxes)[0] + + // Now Dave should consider the channel fully closed. + assertNumPendingChannels(t, dave, 0, 0) + + // After Carol's output matures, she should also reclaim her + // funds. + // + // The commit sweep resolver publishes the sweep tx at + // defaultCSV-1 and we already mined one block after the + // commitmment was published, so take that into account. + mineBlocks(t, net, defaultCSV-1-1, 0) + carolSweep, err := waitForTxInMempool( + net.Miner.Client, minerMempoolTimeout, + ) + require.NoError(t.t, err, "unable to find Carol's sweep tx in "+ + "mempool") + block := mineBlocks(t, net, 1, 1)[0] + assertTxInBlock(t, block, carolSweep) + + // Now the channel should be fully closed also from Carol's POV. + assertNumPendingChannels(t, carol, 0, 0) + } // We query Dave's balance to make sure it increased after the channel // closed. This checks that he was able to sweep the funds he had in @@ -1185,22 +1260,6 @@ func assertDLPExecuted(net *lntest.NetworkHarness, t *harnessTest, t.t, daveBalance, daveStartingBalance, "balance not increased", ) - // After the Carol's output matures, she should also reclaim her funds. - // - // The commit sweep resolver publishes the sweep tx at defaultCSV-1 and - // we already mined one block after the commitment was published, so - // take that into account. - mineBlocks(t, net, defaultCSV-1-1, 0) - carolSweep, err := waitForTxInMempool( - net.Miner.Client, minerMempoolTimeout, - ) - require.NoError(t.t, err, "unable to find Carol's sweep tx in mempool") - block := mineBlocks(t, net, 1, 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. err = wait.NoError(func() error { ctxt, _ = context.WithTimeout(ctxb, defaultTimeout) diff --git a/lntest/itest/lnd_channel_backup_test.go b/lntest/itest/lnd_channel_backup_test.go index a2acf74fd..3aa355b2c 100644 --- a/lntest/itest/lnd_channel_backup_test.go +++ b/lntest/itest/lnd_channel_backup_test.go @@ -337,10 +337,37 @@ func testChannelBackupRestore(net *lntest.NetworkHarness, t *harnessTest) { // Restore the backup from the on-disk file, using the RPC // interface, for anchor commitment channels. { - name: "restore from backup file anchors", - initiator: true, - private: false, - anchorCommit: true, + name: "restore from backup file anchors", + initiator: true, + private: false, + commitmentType: lnrpc.CommitmentType_ANCHORS, + restoreMethod: func(oldNode *lntest.HarnessNode, + backupFilePath string, + mnemonic []string) (nodeRestorer, error) { + + // Read the entire Multi backup stored within + // this node's channels.backup file. + multi, err := ioutil.ReadFile(backupFilePath) + if err != nil { + return nil, err + } + + // Now that we have Dave's backup file, we'll + // create a new nodeRestorer that will restore + // using the on-disk channels.backup. + return chanRestoreViaRPC( + net, password, mnemonic, multi, oldNode, + ) + }, + }, + + // Restore the backup from the on-disk file, using the RPC + // interface, for script-enforced leased channels. + { + name: "restore from backup file script enforced lease", + initiator: true, + private: false, + commitmentType: lnrpc.CommitmentType_SCRIPT_ENFORCED_LEASE, restoreMethod: func(oldNode *lntest.HarnessNode, backupFilePath string, mnemonic []string) (nodeRestorer, error) { @@ -399,7 +426,7 @@ func testChannelBackupRestore(net *lntest.NetworkHarness, t *harnessTest) { "anchors", initiator: true, private: false, - anchorCommit: true, + commitmentType: lnrpc.CommitmentType_ANCHORS, localForceClose: true, restoreMethod: func(oldNode *lntest.HarnessNode, backupFilePath string, @@ -814,9 +841,9 @@ type chanRestoreTestCase struct { // confirmed or not. unconfirmed bool - // anchorCommit is true, then the new anchor commitment type will be - // used for the channels created in the test. - anchorCommit bool + // commitmentType specifies the commitment type that should be used for + // the channel from Dave to Carol. + commitmentType lnrpc.CommitmentType // legacyRevocation signals if a channel with the legacy revocation // producer format should also be created before restoring. @@ -854,11 +881,9 @@ func testChanRestoreScenario(t *harnessTest, net *lntest.NetworkHarness, "--minbackoff=50ms", "--maxbackoff=1s", } - if testCase.anchorCommit { - anchorNodeArgs := nodeArgsForCommitType( - lnrpc.CommitmentType_ANCHORS, - ) - nodeArgs = append(nodeArgs, anchorNodeArgs...) + if testCase.commitmentType != lnrpc.CommitmentType_UNKNOWN_COMMITMENT_TYPE { + args := nodeArgsForCommitType(testCase.commitmentType) + nodeArgs = append(nodeArgs, args...) } // First, we'll create a brand new node we'll use within the test. If @@ -884,7 +909,7 @@ func testChanRestoreScenario(t *harnessTest, net *lntest.NetworkHarness, // For the anchor output case we need two UTXOs for Carol so she can // sweep both the local and remote anchor. - if testCase.anchorCommit { + if commitTypeHasAnchors(testCase.commitmentType) { net.SendCoins(t.t, btcutil.SatoshiPerBitcoin, carol) } @@ -936,12 +961,23 @@ func testChanRestoreScenario(t *harnessTest, net *lntest.NetworkHarness, ) default: + var fundingShim *lnrpc.FundingShim + if testCase.commitmentType == lnrpc.CommitmentType_SCRIPT_ENFORCED_LEASE { + _, minerHeight, err := net.Miner.Client.GetBestBlock() + require.NoError(t.t, err) + thawHeight := uint32(minerHeight + 144) + + fundingShim, _, _ = deriveFundingShim( + net, t, from, to, chanAmt, thawHeight, true, + ) + } chanPoint = openChannelAndAssert( - t, net, from, to, - lntest.OpenChannelParams{ - Amt: chanAmt, - PushAmt: pushAmt, - Private: testCase.private, + t, net, from, to, lntest.OpenChannelParams{ + Amt: chanAmt, + PushAmt: pushAmt, + Private: testCase.private, + FundingShim: fundingShim, + CommitmentType: testCase.commitmentType, }, ) @@ -1086,7 +1122,8 @@ func testChanRestoreScenario(t *harnessTest, net *lntest.NetworkHarness, assertTimeLockSwept( net, t, carol, carolStartingBalance, dave, - daveStartingBalance, testCase.anchorCommit, + daveStartingBalance, + commitTypeHasAnchors(testCase.commitmentType), ) return @@ -1167,7 +1204,7 @@ func testChanRestoreScenario(t *harnessTest, net *lntest.NetworkHarness, require.NoError(t.t, err) numUTXOs := 1 - if testCase.anchorCommit { + if commitTypeHasAnchors(testCase.commitmentType) { numUTXOs = 2 } assertNumUTXOs(t.t, carol, numUTXOs) @@ -1183,7 +1220,7 @@ func testChanRestoreScenario(t *harnessTest, net *lntest.NetworkHarness, // end of the protocol. assertDLPExecuted( net, t, carol, carolStartingBalance, dave, daveStartingBalance, - testCase.anchorCommit, + testCase.commitmentType, ) } diff --git a/lntest/itest/lnd_misc_test.go b/lntest/itest/lnd_misc_test.go index 4a08de9c7..d90e64b40 100644 --- a/lntest/itest/lnd_misc_test.go +++ b/lntest/itest/lnd_misc_test.go @@ -1014,7 +1014,7 @@ func testDataLossProtection(net *lntest.NetworkHarness, t *harnessTest) { // on chain, and both of them properly carry out the DLP protocol. assertDLPExecuted( net, t, carol, carolStartingBalance, dave, daveStartingBalance, - false, + lnrpc.CommitmentType_STATIC_REMOTE_KEY, ) // As a second part of this test, we will test the scenario where a