diff --git a/lntemp/harness.go b/lntemp/harness.go index 485c076ce..1d0001d02 100644 --- a/lntemp/harness.go +++ b/lntemp/harness.go @@ -480,7 +480,12 @@ func (h *HarnessTest) SuspendNode(node *node.HarnessNode) func() error { return func() error { h.manager.registerNode(node) - return node.Start(h.runCtx) + if err := node.Start(h.runCtx); err != nil { + return err + } + h.WaitForBlockchainSync(node) + + return nil } } diff --git a/lntemp/harness_assertion.go b/lntemp/harness_assertion.go index 1378bb9a7..7c5879d92 100644 --- a/lntemp/harness_assertion.go +++ b/lntemp/harness_assertion.go @@ -1992,3 +1992,25 @@ func (h *HarnessTest) WaitForNodeBlockHeight(hn *node.HarnessNode, require.NoErrorf(h, err, "%s: timeout while waiting for height", hn.Name()) } + +// AssertChannelCommitHeight asserts the given channel for the node has the +// expected commit height(`NumUpdates`). +func (h *HarnessTest) AssertChannelCommitHeight(hn *node.HarnessNode, + cp *lnrpc.ChannelPoint, height int) { + + err := wait.NoError(func() error { + c, err := h.findChannel(hn, cp) + if err != nil { + return err + } + + if int(c.NumUpdates) == height { + return nil + } + + return fmt.Errorf("expected commit height to be %v, was %v", + height, c.NumUpdates) + }, DefaultTimeout) + + require.NoError(h, err, "timeout while waiting for commit height") +} diff --git a/lntest/itest/list_on_test.go b/lntest/itest/list_on_test.go index f5a411490..1d8d48e54 100644 --- a/lntest/itest/list_on_test.go +++ b/lntest/itest/list_on_test.go @@ -283,4 +283,8 @@ var allTestCasesTemp = []*lntemp.TestCase{ Name: "psbt channel funding single step", TestFunc: testPsbtChanFundingSingleStep, }, + { + Name: "resolution handoff", + TestFunc: testResHandoff, + }, } diff --git a/lntest/itest/lnd_res_handoff_test.go b/lntest/itest/lnd_res_handoff_test.go index 36d74527c..a18d2fce0 100644 --- a/lntest/itest/lnd_res_handoff_test.go +++ b/lntest/itest/lnd_res_handoff_test.go @@ -5,131 +5,69 @@ import ( "github.com/btcsuite/btcd/btcutil" "github.com/lightningnetwork/lnd/lnrpc" - "github.com/lightningnetwork/lnd/lntest" - "github.com/lightningnetwork/lnd/lntest/wait" + "github.com/lightningnetwork/lnd/lntemp" "github.com/stretchr/testify/require" - "golang.org/x/net/context" ) // testResHandoff tests that the contractcourt is able to properly hand-off // resolution messages to the switch. -func testResHandoff(net *lntest.NetworkHarness, t *harnessTest) { +func testResHandoff(ht *lntemp.HarnessTest) { const ( chanAmt = btcutil.Amount(1000000) paymentAmt = 50000 ) - ctxb := context.Background() + alice, bob := ht.Alice, ht.Bob // First we'll create a channel between Alice and Bob. - net.EnsureConnected(t.t, net.Alice, net.Bob) + ht.EnsureConnected(alice, bob) - chanPointAlice := openChannelAndAssert( - t, net, net.Alice, net.Bob, - lntest.OpenChannelParams{ - Amt: chanAmt, - }, - ) - defer closeChannelAndAssert(t, net, net.Alice, chanPointAlice, false) - - // Wait for Alice and Bob to receive the channel edge from the funding - // manager. - err := net.Alice.WaitForNetworkChannelOpen(chanPointAlice) - require.NoError(t.t, err) - - err = net.Bob.WaitForNetworkChannelOpen(chanPointAlice) - require.NoError(t.t, err) + params := lntemp.OpenChannelParams{Amt: chanAmt} + chanPointAlice := ht.OpenChannel(alice, bob, params) // Create a new node Carol that will be in hodl mode. This is used to // trigger the behavior of checkRemoteDanglingActions in the // contractcourt. This will cause Bob to fail the HTLC back to Alice. - carol := net.NewNode(t.t, "Carol", []string{"--hodl.commit"}) - defer shutdownAndAssert(net, t, carol) + carol := ht.NewNode("Carol", []string{"--hodl.commit"}) + defer ht.Shutdown(carol) // Connect Bob to Carol. - net.ConnectNodes(t.t, net.Bob, carol) + ht.ConnectNodes(bob, carol) // Open a channel between Bob and Carol. - chanPointCarol := openChannelAndAssert( - t, net, net.Bob, carol, - lntest.OpenChannelParams{ - Amt: chanAmt, - }, - ) - - // Wait for Bob and Carol to receive the channel edge from the funding - // manager. - err = net.Bob.WaitForNetworkChannelOpen(chanPointCarol) - require.NoError(t.t, err) - - err = carol.WaitForNetworkChannelOpen(chanPointCarol) - require.NoError(t.t, err) + chanPointCarol := ht.OpenChannel(bob, carol, params) // Wait for Alice to see the channel edge in the graph. - err = net.Alice.WaitForNetworkChannelOpen(chanPointCarol) - require.NoError(t.t, err) + ht.AssertTopologyChannelOpen(alice, chanPointCarol) // We'll create an invoice for Carol that Alice will attempt to pay. // Since Carol is in hodl.commit mode, she won't send back any commit // sigs. - carolPayReqs, _, _, err := createPayReqs( - carol, paymentAmt, 1, - ) - require.NoError(t.t, err) + carolPayReqs, _, _ := ht.CreatePayReqs(carol, paymentAmt, 1) // Alice will now attempt to fulfill the invoice. - err = completePaymentRequests( - net.Alice, net.Alice.RouterClient, carolPayReqs, false, - ) - require.NoError(t.t, err) + ht.CompletePaymentRequestsNoWait(alice, carolPayReqs, chanPointAlice) // Wait until Carol has received the Add, CommitSig from Bob, and has // responded with a RevokeAndAck. We expect NumUpdates to be 1 meaning // Carol's CommitHeight is 1. - err = wait.Predicate(func() bool { - carolInfo, err := getChanInfo(carol) - if err != nil { - return false - } - - return carolInfo.NumUpdates == 1 - }, defaultTimeout) - require.NoError(t.t, err) + ht.AssertChannelCommitHeight(carol, chanPointCarol, 1) // Before we shutdown Alice, we'll assert that she only has 1 update. - err = wait.Predicate(func() bool { - aliceInfo, err := getChanInfo(net.Alice) - if err != nil { - return false - } - - return aliceInfo.NumUpdates == 1 - }, defaultTimeout) - require.NoError(t.t, err) + ht.AssertChannelCommitHeight(alice, chanPointAlice, 1) // We'll shutdown Alice so that Bob can't connect to her. - restartAlice, err := net.SuspendNode(net.Alice) - require.NoError(t.t, err) + restartAlice := ht.SuspendNode(alice) // Bob will now force close his channel with Carol such that resolution // messages are created and forwarded backwards to Alice. - _, _, err = net.CloseChannel(net.Bob, chanPointCarol, true) - require.NoError(t.t, err) + ht.CloseChannelAssertPending(bob, chanPointCarol, true) // The channel should be listed in the PendingChannels result. - ctxt, cancel := context.WithTimeout(ctxb, defaultTimeout) - defer cancel() + ht.AssertNumWaitingClose(bob, 1) - pendingChansRequest := &lnrpc.PendingChannelsRequest{} - pendingChanResp, err := net.Bob.PendingChannels( - ctxt, pendingChansRequest, - ) - require.NoError(t.t, err) - require.NoError(t.t, checkNumWaitingCloseChannels(pendingChanResp, 1)) - - // We'll mine a block to confirm the force close transaction and to - // advance Bob's contract state with Carol to StateContractClosed. - mineBlocks(t, net, 1, 1) + // Mine a block to confirm the closing tx. + ht.MineBlocks(1) // We sleep here so we can be sure that the hand-off has occurred from // Bob's contractcourt to Bob's htlcswitch. This sleep could be removed @@ -137,64 +75,25 @@ func testResHandoff(net *lntest.NetworkHarness, t *harnessTest) { // querying the state of resolution messages. time.Sleep(10 * time.Second) - // Mine blocks until Bob has no waiting close channels. This tests - // that the circuit-deletion logic is skipped if a resolution message + // Mine blocks until Bob has no waiting close channels. This tests that + // the circuit-deletion logic is skipped if a resolution message // exists. - for { - _, err = net.Miner.Client.Generate(1) - require.NoError(t.t, err) - - pendingChanResp, err = net.Bob.PendingChannels( - ctxt, pendingChansRequest, - ) - require.NoError(t.t, err) - - isErr := checkNumForceClosedChannels(pendingChanResp, 0) - if isErr == nil { - break - } - - time.Sleep(150 * time.Millisecond) - } + ht.CleanupForceClose(bob, chanPointCarol) // We will now restart Bob so that we can test whether the resolution // messages are re-forwarded on start-up. - restartBob, err := net.SuspendNode(net.Bob) - require.NoError(t.t, err) - - err = restartBob() - require.NoError(t.t, err) + ht.RestartNode(bob) // We'll now also restart Alice and connect her with Bob. - err = restartAlice() - require.NoError(t.t, err) + require.NoError(ht, restartAlice()) - net.EnsureConnected(t.t, net.Alice, net.Bob) + ht.EnsureConnected(alice, bob) - // We'll assert that Alice has received the failure resolution - // message. - err = wait.Predicate(func() bool { - aliceInfo, err := getChanInfo(net.Alice) - if err != nil { - return false - } - - return aliceInfo.NumUpdates == 2 - }, defaultTimeout) - require.NoError(t.t, err) + // We'll assert that Alice has received the failure resolution message. + ht.AssertChannelCommitHeight(alice, chanPointAlice, 2) // Assert that Alice's payment failed. - ctxt, _ = context.WithTimeout(ctxb, defaultTimeout) - paymentsResp, err := net.Alice.ListPayments( - ctxt, &lnrpc.ListPaymentsRequest{ - IncludeIncomplete: true, - }, - ) - require.NoError(t.t, err) - require.Equal(t.t, 1, len(paymentsResp.Payments)) + ht.AssertFirstHTLCError(alice, lnrpc.Failure_PERMANENT_CHANNEL_FAILURE) - htlcs := paymentsResp.Payments[0].Htlcs - - require.Equal(t.t, 1, len(htlcs)) - require.Equal(t.t, lnrpc.HTLCAttempt_FAILED, htlcs[0].Status) + ht.CloseChannel(alice, chanPointAlice) } diff --git a/lntest/itest/lnd_test_list_on_test.go b/lntest/itest/lnd_test_list_on_test.go index ebc4867e3..2f0d0cf42 100644 --- a/lntest/itest/lnd_test_list_on_test.go +++ b/lntest/itest/lnd_test_list_on_test.go @@ -186,10 +186,6 @@ var allTestCases = []*testCase{ name: "taproot", test: testTaproot, }, - { - name: "resolution handoff", - test: testResHandoff, - }, { name: "zero conf channel open", test: testZeroConfChannelOpen,