From e78f986ecd0a278f7617a69e1fc5c4d851bec931 Mon Sep 17 00:00:00 2001 From: "Johan T. Halseth" Date: Wed, 4 Mar 2020 13:21:27 +0100 Subject: [PATCH 01/11] itest: print wrong balance in correct format --- lntest/itest/lnd_test.go | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/lntest/itest/lnd_test.go b/lntest/itest/lnd_test.go index d84ed1148ba..568cdc15a1e 100644 --- a/lntest/itest/lnd_test.go +++ b/lntest/itest/lnd_test.go @@ -1053,14 +1053,18 @@ func basicChannelFundingTest(t *harnessTest, net *lntest.NetworkHarness, return nil, nil, nil, fmt.Errorf("unable to get bobs's "+ "balance: %v", err) } - if aliceBal.Balance != int64(chanAmt-pushAmt-calcStaticFee(0)) { + + aliceBalance := btcutil.Amount(aliceBal.Balance) + if aliceBalance != chanAmt-pushAmt-calcStaticFee(0) { return nil, nil, nil, fmt.Errorf("alice's balance is "+ "incorrect: expected %v got %v", - chanAmt-pushAmt-calcStaticFee(0), aliceBal) + chanAmt-pushAmt-calcStaticFee(0), aliceBalance) } - if bobBal.Balance != int64(pushAmt) { + + bobBalance := btcutil.Amount(bobBal.Balance) + if bobBalance != pushAmt { return nil, nil, nil, fmt.Errorf("bob's balance is incorrect: "+ - "expected %v got %v", pushAmt, bobBal.Balance) + "expected %v got %v", pushAmt, bobBalance) } req := &lnrpc.ListChannelsRequest{} From 99bbd45130f1febedc9a536681f18fc55b8091d9 Mon Sep 17 00:00:00 2001 From: "Johan T. Halseth" Date: Wed, 4 Mar 2020 13:21:27 +0100 Subject: [PATCH 02/11] itest: extract channel force closure test into subtest To make it possible to run the for close test for multiple commit types, we extract ut into a subtest, where the two nodes get passed in. --- lntest/itest/lnd_test.go | 78 +++++++++++++++++++++++----------------- 1 file changed, 46 insertions(+), 32 deletions(-) diff --git a/lntest/itest/lnd_test.go b/lntest/itest/lnd_test.go index 568cdc15a1e..0d8c5c8cad4 100644 --- a/lntest/itest/lnd_test.go +++ b/lntest/itest/lnd_test.go @@ -2982,6 +2982,28 @@ func padCLTV(cltv uint32) uint32 { // // TODO(roasbeef): also add an unsettled HTLC before force closing. func testChannelForceClosure(net *lntest.NetworkHarness, t *harnessTest) { + success := t.t.Run("channelForceClosure", func(t *testing.T) { + ht := newHarnessTest(t, net) + + // Since we'd like to test failure scenarios with outstanding + // htlcs, we'll introduce another node into our test network: + // Carol. + carol, err := net.NewNode("Carol", []string{"--hodl.exit-settle"}) + if err != nil { + t.Fatalf("unable to create new nodes: %v", err) + } + defer shutdownAndAssert(net, ht, carol) + + channelForceClosureTest(net, ht, net.Alice, carol) + }) + if !success { + return + } +} + +func channelForceClosureTest(net *lntest.NetworkHarness, t *harnessTest, + alice, carol *lntest.HarnessNode) { + ctxb := context.Background() const ( @@ -2995,18 +3017,10 @@ func testChannelForceClosure(net *lntest.NetworkHarness, t *harnessTest) { // instead, or make delay a param defaultCLTV := uint32(lnd.DefaultBitcoinTimeLockDelta) - // Since we'd like to test failure scenarios with outstanding htlcs, - // we'll introduce another node into our test network: Carol. - carol, err := net.NewNode("Carol", []string{"--hodl.exit-settle"}) - if err != nil { - t.Fatalf("unable to create new nodes: %v", err) - } - defer shutdownAndAssert(net, t, carol) - // We must let Alice have an open channel before she can send a node // announcement, so we open a channel with Carol, ctxt, _ := context.WithTimeout(ctxb, defaultTimeout) - if err := net.ConnectNodes(ctxt, net.Alice, carol); err != nil { + if err := net.ConnectNodes(ctxt, alice, carol); err != nil { t.Fatalf("unable to connect alice to carol: %v", err) } @@ -3024,7 +3038,7 @@ func testChannelForceClosure(net *lntest.NetworkHarness, t *harnessTest) { ctxt, _ = context.WithTimeout(ctxb, channelOpenTimeout) chanPoint := openChannelAndAssert( - ctxt, t, net, net.Alice, carol, + ctxt, t, net, alice, carol, lntest.OpenChannelParams{ Amt: chanAmt, PushAmt: pushAmt, @@ -3034,7 +3048,7 @@ func testChannelForceClosure(net *lntest.NetworkHarness, t *harnessTest) { // Wait for Alice and Carol to receive the channel edge from the // funding manager. ctxt, _ = context.WithTimeout(ctxb, defaultTimeout) - err = net.Alice.WaitForNetworkChannelOpen(ctxt, chanPoint) + err = alice.WaitForNetworkChannelOpen(ctxt, chanPoint) if err != nil { t.Fatalf("alice didn't see the alice->carol channel before "+ "timeout: %v", err) @@ -3051,7 +3065,7 @@ func testChannelForceClosure(net *lntest.NetworkHarness, t *harnessTest) { ctx, cancel := context.WithCancel(ctxb) defer cancel() - alicePayStream, err := net.Alice.SendPayment(ctx) + alicePayStream, err := alice.SendPayment(ctx) if err != nil { t.Fatalf("unable to create payment stream for alice: %v", err) } @@ -3071,7 +3085,7 @@ func testChannelForceClosure(net *lntest.NetworkHarness, t *harnessTest) { // Once the HTLC has cleared, all the nodes n our mini network should // show that the HTLC has been locked in. - nodes := []*lntest.HarnessNode{net.Alice, carol} + nodes := []*lntest.HarnessNode{alice, carol} var predErr error err = wait.Predicate(func() bool { predErr = assertNumActiveHtlcs(nodes, numInvoices) @@ -3101,7 +3115,7 @@ func testChannelForceClosure(net *lntest.NetworkHarness, t *harnessTest) { ) ctxt, _ = context.WithTimeout(ctxb, defaultTimeout) - aliceChan, err := getChanInfo(ctxt, net.Alice) + aliceChan, err := getChanInfo(ctxt, alice) if err != nil { t.Fatalf("unable to get alice's channel info: %v", err) } @@ -3114,7 +3128,7 @@ func testChannelForceClosure(net *lntest.NetworkHarness, t *harnessTest) { // the commitment transaction was immediately broadcast in order to // fulfill the force closure request. ctxt, _ = context.WithTimeout(ctxb, channelCloseTimeout) - _, closingTxID, err := net.CloseChannel(ctxt, net.Alice, chanPoint, true) + _, closingTxID, err := net.CloseChannel(ctxt, alice, chanPoint, true) if err != nil { t.Fatalf("unable to execute force channel closure: %v", err) } @@ -3123,7 +3137,7 @@ func testChannelForceClosure(net *lntest.NetworkHarness, t *harnessTest) { // PendingChannels RPC under the waiting close section. pendingChansRequest := &lnrpc.PendingChannelsRequest{} ctxt, _ = context.WithTimeout(ctxb, defaultTimeout) - pendingChanResp, err := net.Alice.PendingChannels(ctxt, pendingChansRequest) + pendingChanResp, err := alice.PendingChannels(ctxt, pendingChansRequest) if err != nil { t.Fatalf("unable to query for pending channels: %v", err) } @@ -3159,7 +3173,7 @@ func testChannelForceClosure(net *lntest.NetworkHarness, t *harnessTest) { // when the system comes back on line. This restart tests state // persistence at the beginning of the process, when the commitment // transaction has been broadcast but not yet confirmed in a block. - if err := net.RestartNode(net.Alice, nil); err != nil { + if err := net.RestartNode(alice, nil); err != nil { t.Fatalf("Node restart failed: %v", err) } @@ -3178,7 +3192,7 @@ func testChannelForceClosure(net *lntest.NetworkHarness, t *harnessTest) { // marked as force closed. err = wait.Predicate(func() bool { ctxt, _ := context.WithTimeout(ctxb, defaultTimeout) - pendingChanResp, err := net.Alice.PendingChannels( + pendingChanResp, err := alice.PendingChannels( ctxt, pendingChansRequest, ) if err != nil { @@ -3231,7 +3245,7 @@ func testChannelForceClosure(net *lntest.NetworkHarness, t *harnessTest) { // force close commitment transaction have been persisted once the // transaction has been confirmed, but before the outputs are spendable // (the "kindergarten" bucket.) - if err := net.RestartNode(net.Alice, nil); err != nil { + if err := net.RestartNode(alice, nil); err != nil { t.Fatalf("Node restart failed: %v", err) } @@ -3254,7 +3268,7 @@ func testChannelForceClosure(net *lntest.NetworkHarness, t *harnessTest) { // The following restart checks to ensure that outputs in the // kindergarten bucket are persisted while waiting for the required // number of confirmations to be reported. - if err := net.RestartNode(net.Alice, nil); err != nil { + if err := net.RestartNode(alice, nil); err != nil { t.Fatalf("Node restart failed: %v", err) } @@ -3262,7 +3276,7 @@ func testChannelForceClosure(net *lntest.NetworkHarness, t *harnessTest) { // channels with her funds still in limbo. err = wait.NoError(func() error { ctxt, _ := context.WithTimeout(ctxb, defaultTimeout) - pendingChanResp, err := net.Alice.PendingChannels( + pendingChanResp, err := alice.PendingChannels( ctxt, pendingChansRequest, ) if err != nil { @@ -3340,7 +3354,7 @@ func testChannelForceClosure(net *lntest.NetworkHarness, t *harnessTest) { // Restart Alice to ensure that she resumes watching the finalized // commitment sweep txid. - if err := net.RestartNode(net.Alice, nil); err != nil { + if err := net.RestartNode(alice, nil); err != nil { t.Fatalf("Node restart failed: %v", err) } @@ -3368,7 +3382,7 @@ func testChannelForceClosure(net *lntest.NetworkHarness, t *harnessTest) { // Now that the commit output has been fully swept, check to see // that the channel remains open for the pending htlc outputs. ctxt, _ := context.WithTimeout(ctxb, defaultTimeout) - pendingChanResp, err := net.Alice.PendingChannels( + pendingChanResp, err := alice.PendingChannels( ctxt, pendingChansRequest, ) if err != nil { @@ -3431,7 +3445,7 @@ func testChannelForceClosure(net *lntest.NetworkHarness, t *harnessTest) { // We now restart Alice, to ensure that she will broadcast the presigned // htlc timeout txns after the delay expires after experiencing a while // waiting for the htlc outputs to incubate. - if err := net.RestartNode(net.Alice, nil); err != nil { + if err := net.RestartNode(alice, nil); err != nil { t.Fatalf("Node restart failed: %v", err) } @@ -3439,7 +3453,7 @@ func testChannelForceClosure(net *lntest.NetworkHarness, t *harnessTest) { // channels with one pending HTLC. err = wait.NoError(func() error { ctxt, _ := context.WithTimeout(ctxb, defaultTimeout) - pendingChanResp, err := net.Alice.PendingChannels( + pendingChanResp, err := alice.PendingChannels( ctxt, pendingChansRequest, ) if err != nil { @@ -3532,7 +3546,7 @@ func testChannelForceClosure(net *lntest.NetworkHarness, t *harnessTest) { // With the htlc timeout txns still in the mempool, we restart Alice to // verify that she can resume watching the htlc txns she broadcasted // before crashing. - if err := net.RestartNode(net.Alice, nil); err != nil { + if err := net.RestartNode(alice, nil); err != nil { t.Fatalf("Node restart failed: %v", err) } @@ -3546,7 +3560,7 @@ func testChannelForceClosure(net *lntest.NetworkHarness, t *harnessTest) { // Alice is restarted here to ensure that she promptly moved the crib // outputs to the kindergarten bucket after the htlc timeout txns were // confirmed. - if err := net.RestartNode(net.Alice, nil); err != nil { + if err := net.RestartNode(alice, nil); err != nil { t.Fatalf("Node restart failed: %v", err) } @@ -3558,7 +3572,7 @@ func testChannelForceClosure(net *lntest.NetworkHarness, t *harnessTest) { // Restart Alice to ensure that she can recover from a failure before // having graduated the htlc outputs in the kindergarten bucket. - if err := net.RestartNode(net.Alice, nil); err != nil { + if err := net.RestartNode(alice, nil); err != nil { t.Fatalf("Node restart failed: %v", err) } @@ -3567,7 +3581,7 @@ func testChannelForceClosure(net *lntest.NetworkHarness, t *harnessTest) { // as pending force closed. err = wait.Predicate(func() bool { ctxt, _ := context.WithTimeout(ctxb, defaultTimeout) - pendingChanResp, err = net.Alice.PendingChannels( + pendingChanResp, err = alice.PendingChannels( ctxt, pendingChansRequest, ) if err != nil { @@ -3659,7 +3673,7 @@ func testChannelForceClosure(net *lntest.NetworkHarness, t *harnessTest) { // The following restart checks to ensure that the nursery store is // storing the txid of the previously broadcast htlc sweep txn, and that // it begins watching that txid after restarting. - if err := net.RestartNode(net.Alice, nil); err != nil { + if err := net.RestartNode(alice, nil); err != nil { t.Fatalf("Node restart failed: %v", err) } @@ -3668,7 +3682,7 @@ func testChannelForceClosure(net *lntest.NetworkHarness, t *harnessTest) { // as pending force closed. err = wait.Predicate(func() bool { ctxt, _ := context.WithTimeout(ctxb, defaultTimeout) - pendingChanResp, err := net.Alice.PendingChannels( + pendingChanResp, err := alice.PendingChannels( ctxt, pendingChansRequest, ) if err != nil { @@ -3717,7 +3731,7 @@ func testChannelForceClosure(net *lntest.NetworkHarness, t *harnessTest) { // up within the pending channels RPC. err = wait.Predicate(func() bool { ctxt, _ := context.WithTimeout(ctxb, defaultTimeout) - pendingChanResp, err := net.Alice.PendingChannels( + pendingChanResp, err := alice.PendingChannels( ctxt, pendingChansRequest, ) if err != nil { From b6e8ab09b2f24c9fc5a0a17422feeba3da47bf22 Mon Sep 17 00:00:00 2001 From: "Johan T. Halseth" Date: Wed, 4 Mar 2020 13:21:27 +0100 Subject: [PATCH 03/11] itest: run force closure test for all commit types Now that the force closure test has been extracted, spin up new nodes for each commit type, and ensure the test succeed for all types. --- lntest/itest/lnd_test.go | 60 ++++++++++++++++++++++++++++++---------- 1 file changed, 46 insertions(+), 14 deletions(-) diff --git a/lntest/itest/lnd_test.go b/lntest/itest/lnd_test.go index 0d8c5c8cad4..b0011d2ae39 100644 --- a/lntest/itest/lnd_test.go +++ b/lntest/itest/lnd_test.go @@ -2982,22 +2982,54 @@ func padCLTV(cltv uint32) uint32 { // // TODO(roasbeef): also add an unsettled HTLC before force closing. func testChannelForceClosure(net *lntest.NetworkHarness, t *harnessTest) { - success := t.t.Run("channelForceClosure", func(t *testing.T) { - ht := newHarnessTest(t, net) + // We'll test the scenario for all commitment types, to ensure outputs + // can be swept. + allTypes := []commitType{ + commitTypeLegacy, + commitTypeTweakless, + } - // Since we'd like to test failure scenarios with outstanding - // htlcs, we'll introduce another node into our test network: - // Carol. - carol, err := net.NewNode("Carol", []string{"--hodl.exit-settle"}) - if err != nil { - t.Fatalf("unable to create new nodes: %v", err) - } - defer shutdownAndAssert(net, ht, carol) + for _, channelType := range allTypes { + testName := fmt.Sprintf("committype=%v", channelType) - channelForceClosureTest(net, ht, net.Alice, carol) - }) - if !success { - return + success := t.t.Run(testName, func(t *testing.T) { + ht := newHarnessTest(t, net) + args := channelType.Args() + alice, err := net.NewNode("Alice", args) + if err != nil { + t.Fatalf("unable to create new node: %v", err) + } + defer shutdownAndAssert(net, ht, alice) + + // Since we'd like to test failure scenarios with + // outstanding htlcs, we'll introduce another node into + // our test network: Carol. + carolArgs := []string{"--hodl.exit-settle"} + carolArgs = append(carolArgs, args...) + carol, err := net.NewNode("Carol", carolArgs) + if err != nil { + t.Fatalf("unable to create new nodes: %v", err) + } + defer shutdownAndAssert(net, ht, carol) + + // Each time, we'll send Alice new set of coins in + // order to fund the channel. + ctxt, _ := context.WithTimeout( + context.Background(), defaultTimeout, + ) + err = net.SendCoins( + ctxt, btcutil.SatoshiPerBitcoin, alice, + ) + if err != nil { + t.Fatalf("unable to send coins to Alice: %v", + err) + } + + channelForceClosureTest(net, ht, alice, carol) + }) + if !success { + return + } } } From 592b2100fcc1a6532a750cec47be1e7a3f95541f Mon Sep 17 00:00:00 2001 From: "Johan T. Halseth" Date: Wed, 4 Mar 2020 13:21:27 +0100 Subject: [PATCH 04/11] itest: move multi hop tests to own files PURE CODE MOVE. --- ...d_multi-hop_htlc_local_chain_claim_test.go | 73 ++ .../lnd_multi-hop_htlc_local_timeout_test.go | 250 ++++++ ..._force_close_on_chain_htlc_timeout_test.go | 278 ++++++ ..._force_close_on_chain_htlc_timeout_test.go | 236 ++++++ lntest/itest/lnd_test.go | 790 ------------------ 5 files changed, 837 insertions(+), 790 deletions(-) create mode 100644 lntest/itest/lnd_multi-hop_htlc_local_timeout_test.go create mode 100644 lntest/itest/lnd_multi-hop_local_force_close_on_chain_htlc_timeout_test.go create mode 100644 lntest/itest/lnd_multi-hop_remote_force_close_on_chain_htlc_timeout_test.go diff --git a/lntest/itest/lnd_multi-hop_htlc_local_chain_claim_test.go b/lntest/itest/lnd_multi-hop_htlc_local_chain_claim_test.go index 123359dc49a..dd84105c76f 100644 --- a/lntest/itest/lnd_multi-hop_htlc_local_chain_claim_test.go +++ b/lntest/itest/lnd_multi-hop_htlc_local_chain_claim_test.go @@ -498,3 +498,76 @@ func checkPaymentStatus(ctxt context.Context, node *lntest.HarnessNode, return nil } + +func createThreeHopNetwork(t *harnessTest, net *lntest.NetworkHarness, + carolHodl bool) (*lnrpc.ChannelPoint, *lnrpc.ChannelPoint, + *lntest.HarnessNode) { + + ctxb := context.Background() + + // We'll start the test by creating a channel between Alice and Bob, + // which will act as the first leg for out multi-hop HTLC. + const chanAmt = 1000000 + ctxt, _ := context.WithTimeout(ctxb, channelOpenTimeout) + aliceChanPoint := openChannelAndAssert( + ctxt, t, net, net.Alice, net.Bob, + lntest.OpenChannelParams{ + Amt: chanAmt, + }, + ) + + ctxt, _ = context.WithTimeout(ctxb, defaultTimeout) + err := net.Alice.WaitForNetworkChannelOpen(ctxt, aliceChanPoint) + if err != nil { + t.Fatalf("alice didn't report channel: %v", err) + } + + ctxt, _ = context.WithTimeout(ctxb, defaultTimeout) + err = net.Bob.WaitForNetworkChannelOpen(ctxt, aliceChanPoint) + if err != nil { + t.Fatalf("bob didn't report channel: %v", err) + } + + // Next, we'll create a new node "carol" and have Bob connect to her. If + // the carolHodl flag is set, we'll make carol always hold onto the + // HTLC, this way it'll force Bob to go to chain to resolve the HTLC. + carolFlags := []string{} + if carolHodl { + carolFlags = append(carolFlags, "--hodl.exit-settle") + } + carol, err := net.NewNode("Carol", carolFlags) + if err != nil { + t.Fatalf("unable to create new node: %v", err) + } + ctxt, _ = context.WithTimeout(ctxb, defaultTimeout) + if err := net.ConnectNodes(ctxt, net.Bob, carol); err != nil { + t.Fatalf("unable to connect bob to carol: %v", err) + } + + // We'll then create a channel from Bob to Carol. After this channel is + // open, our topology looks like: A -> B -> C. + ctxt, _ = context.WithTimeout(ctxb, channelOpenTimeout) + bobChanPoint := openChannelAndAssert( + ctxt, t, net, net.Bob, carol, + lntest.OpenChannelParams{ + Amt: chanAmt, + }, + ) + ctxt, _ = context.WithTimeout(ctxb, defaultTimeout) + err = net.Bob.WaitForNetworkChannelOpen(ctxt, bobChanPoint) + if err != nil { + t.Fatalf("alice didn't report channel: %v", err) + } + ctxt, _ = context.WithTimeout(ctxb, defaultTimeout) + err = carol.WaitForNetworkChannelOpen(ctxt, bobChanPoint) + if err != nil { + t.Fatalf("bob didn't report channel: %v", err) + } + ctxt, _ = context.WithTimeout(ctxb, defaultTimeout) + err = net.Alice.WaitForNetworkChannelOpen(ctxt, bobChanPoint) + if err != nil { + t.Fatalf("bob didn't report channel: %v", err) + } + + return aliceChanPoint, bobChanPoint, carol +} diff --git a/lntest/itest/lnd_multi-hop_htlc_local_timeout_test.go b/lntest/itest/lnd_multi-hop_htlc_local_timeout_test.go new file mode 100644 index 00000000000..cb32a0f9d50 --- /dev/null +++ b/lntest/itest/lnd_multi-hop_htlc_local_timeout_test.go @@ -0,0 +1,250 @@ +// +build rpctest + +package itest + +import ( + "context" + "fmt" + "time" + + "github.com/btcsuite/btcd/wire" + "github.com/btcsuite/btcutil" + "github.com/davecgh/go-spew/spew" + "github.com/lightningnetwork/lnd" + "github.com/lightningnetwork/lnd/lnrpc" + "github.com/lightningnetwork/lnd/lntest" + "github.com/lightningnetwork/lnd/lntest/wait" +) + +// testMultiHopHtlcLocalTimeout tests that in a multi-hop HTLC scenario, if the +// outgoing HTLC is about to time out, then we'll go to chain in order to claim +// it. Any dust HTLC's should be immediately canceled backwards. Once the +// timeout has been reached, then we should sweep it on-chain, and cancel the +// HTLC backwards. +func testMultiHopHtlcLocalTimeout(net *lntest.NetworkHarness, t *harnessTest) { + ctxb := context.Background() + + // First, we'll create a three hop network: Alice -> Bob -> Carol, with + // Carol refusing to actually settle or directly cancel any HTLC's + // self. + aliceChanPoint, bobChanPoint, carol := + createThreeHopNetwork(t, net, true) + + // Clean up carol's node when the test finishes. + defer shutdownAndAssert(net, t, carol) + + time.Sleep(time.Second * 1) + + // Now that our channels are set up, we'll send two HTLC's from Alice + // to Carol. The first HTLC will be universally considered "dust", + // while the second will be a proper fully valued HTLC. + const ( + dustHtlcAmt = btcutil.Amount(100) + htlcAmt = btcutil.Amount(30000) + finalCltvDelta = 40 + ) + + ctx, cancel := context.WithCancel(ctxb) + defer cancel() + + alicePayStream, err := net.Alice.SendPayment(ctx) + if err != nil { + t.Fatalf("unable to create payment stream for alice: %v", err) + } + + // We'll create two random payment hashes unknown to carol, then send + // each of them by manually specifying the HTLC details. + carolPubKey := carol.PubKey[:] + dustPayHash := makeFakePayHash(t) + payHash := makeFakePayHash(t) + err = alicePayStream.Send(&lnrpc.SendRequest{ + Dest: carolPubKey, + Amt: int64(dustHtlcAmt), + PaymentHash: dustPayHash, + FinalCltvDelta: finalCltvDelta, + }) + if err != nil { + t.Fatalf("unable to send alice htlc: %v", err) + } + err = alicePayStream.Send(&lnrpc.SendRequest{ + Dest: carolPubKey, + Amt: int64(htlcAmt), + PaymentHash: payHash, + FinalCltvDelta: finalCltvDelta, + }) + if err != nil { + t.Fatalf("unable to send alice htlc: %v", err) + } + + // Verify that all nodes in the path now have two HTLC's with the + // proper parameters. + var predErr error + nodes := []*lntest.HarnessNode{net.Alice, net.Bob, carol} + err = wait.Predicate(func() bool { + predErr = assertActiveHtlcs(nodes, dustPayHash, payHash) + if predErr != nil { + return false + } + + return true + }, time.Second*15) + if err != nil { + t.Fatalf("htlc mismatch: %v", predErr) + } + + // We'll now mine enough blocks to trigger Bob's broadcast of his + // commitment transaction due to the fact that the HTLC is about to + // timeout. With the default outgoing broadcast delta of zero, this will + // be the same height as the htlc expiry height. + numBlocks := padCLTV( + uint32(finalCltvDelta - lnd.DefaultOutgoingBroadcastDelta), + ) + if _, err := net.Miner.Node.Generate(numBlocks); err != nil { + t.Fatalf("unable to generate blocks: %v", err) + } + + // Bob's force close transaction should now be found in the mempool. + bobFundingTxid, err := lnd.GetChanPointFundingTxid(bobChanPoint) + if err != nil { + t.Fatalf("unable to get txid: %v", err) + } + closeTxid, err := waitForTxInMempool(net.Miner.Node, minerMempoolTimeout) + if err != nil { + t.Fatalf("unable to find closing txid: %v", err) + } + assertSpendingTxInMempool( + t, net.Miner.Node, minerMempoolTimeout, wire.OutPoint{ + Hash: *bobFundingTxid, + Index: bobChanPoint.OutputIndex, + }, + ) + + // Mine a block to confirm the closing transaction. + mineBlocks(t, net, 1, 1) + + // At this point, Bob should have canceled backwards the dust HTLC + // that we sent earlier. This means Alice should now only have a single + // HTLC on her channel. + nodes = []*lntest.HarnessNode{net.Alice} + err = wait.Predicate(func() bool { + predErr = assertActiveHtlcs(nodes, payHash) + if predErr != nil { + return false + } + + return true + }, time.Second*15) + if err != nil { + t.Fatalf("htlc mismatch: %v", predErr) + } + + // With the closing transaction confirmed, we should expect Bob's HTLC + // timeout transaction to be broadcast due to the expiry being reached. + htlcTimeout, err := waitForTxInMempool(net.Miner.Node, minerMempoolTimeout) + if err != nil { + t.Fatalf("unable to find bob's htlc timeout tx: %v", err) + } + + // We'll mine the remaining blocks in order to generate the sweep + // transaction of Bob's commitment output. + mineBlocks(t, net, defaultCSV, 1) + assertSpendingTxInMempool( + t, net.Miner.Node, minerMempoolTimeout, wire.OutPoint{ + Hash: *closeTxid, + Index: 1, + }, + ) + + // Bob's pending channel report should show that he has a commitment + // output awaiting sweeping, and also that there's an outgoing HTLC + // output pending. + pendingChansRequest := &lnrpc.PendingChannelsRequest{} + ctxt, _ := context.WithTimeout(ctxb, defaultTimeout) + pendingChanResp, err := net.Bob.PendingChannels(ctxt, pendingChansRequest) + if err != nil { + t.Fatalf("unable to query for pending channels: %v", err) + } + + if len(pendingChanResp.PendingForceClosingChannels) == 0 { + t.Fatalf("bob should have pending for close chan but doesn't") + } + forceCloseChan := pendingChanResp.PendingForceClosingChannels[0] + if forceCloseChan.LimboBalance == 0 { + t.Fatalf("bob should have nonzero limbo balance instead "+ + "has: %v", forceCloseChan.LimboBalance) + } + if len(forceCloseChan.PendingHtlcs) == 0 { + t.Fatalf("bob should have pending htlc but doesn't") + } + + // Now we'll mine an additional block, which should confirm Bob's commit + // sweep. This block should also prompt Bob to broadcast their second + // layer sweep due to the CSV on the HTLC timeout output. + mineBlocks(t, net, 1, 1) + assertSpendingTxInMempool( + t, net.Miner.Node, minerMempoolTimeout, wire.OutPoint{ + Hash: *htlcTimeout, + Index: 0, + }, + ) + + // The block should have confirmed Bob's HTLC timeout transaction. + // Therefore, at this point, there should be no active HTLC's on the + // commitment transaction from Alice -> Bob. + nodes = []*lntest.HarnessNode{net.Alice} + err = wait.Predicate(func() bool { + predErr = assertNumActiveHtlcs(nodes, 0) + if predErr != nil { + return false + } + return true + }, time.Second*15) + if err != nil { + t.Fatalf("alice's channel still has active htlc's: %v", predErr) + } + + // At this point, Bob should show that the pending HTLC has advanced to + // the second stage and is to be swept. + ctxt, _ = context.WithTimeout(ctxb, defaultTimeout) + pendingChanResp, err = net.Bob.PendingChannels(ctxt, pendingChansRequest) + if err != nil { + t.Fatalf("unable to query for pending channels: %v", err) + } + forceCloseChan = pendingChanResp.PendingForceClosingChannels[0] + if forceCloseChan.PendingHtlcs[0].Stage != 2 { + t.Fatalf("bob's htlc should have advanced to the second stage: %v", err) + } + + // Next, we'll mine a final block that should confirm the second-layer + // sweeping transaction. + if _, err := net.Miner.Node.Generate(1); err != nil { + t.Fatalf("unable to generate blocks: %v", err) + } + + // Once this transaction has been confirmed, Bob should detect that he + // no longer has any pending channels. + err = wait.Predicate(func() bool { + ctxt, _ = context.WithTimeout(ctxb, defaultTimeout) + pendingChanResp, err = net.Bob.PendingChannels(ctxt, pendingChansRequest) + if err != nil { + predErr = fmt.Errorf("unable to query for pending "+ + "channels: %v", err) + return false + } + if len(pendingChanResp.PendingForceClosingChannels) != 0 { + predErr = fmt.Errorf("bob still has pending "+ + "channels but shouldn't: %v", + spew.Sdump(pendingChanResp)) + return false + } + + return true + + }, time.Second*15) + if err != nil { + t.Fatalf(predErr.Error()) + } + + ctxt, _ = context.WithTimeout(ctxb, channelCloseTimeout) + closeChannelAndAssert(ctxt, t, net, net.Alice, aliceChanPoint, false) +} diff --git a/lntest/itest/lnd_multi-hop_local_force_close_on_chain_htlc_timeout_test.go b/lntest/itest/lnd_multi-hop_local_force_close_on_chain_htlc_timeout_test.go new file mode 100644 index 00000000000..ea5bf3d7851 --- /dev/null +++ b/lntest/itest/lnd_multi-hop_local_force_close_on_chain_htlc_timeout_test.go @@ -0,0 +1,278 @@ +// +build rpctest + +package itest + +import ( + "context" + "fmt" + "time" + + "github.com/btcsuite/btcutil" + "github.com/davecgh/go-spew/spew" + "github.com/lightningnetwork/lnd/lnrpc" + "github.com/lightningnetwork/lnd/lntest" + "github.com/lightningnetwork/lnd/lntest/wait" +) + +// testMultiHopLocalForceCloseOnChainHtlcTimeout tests that in a multi-hop HTLC +// scenario, if the node that extended the HTLC to the final node closes their +// commitment on-chain early, then it eventually recognizes this HTLC as one +// that's timed out. At this point, the node should timeout the HTLC, then +// cancel it backwards as normal. +func testMultiHopLocalForceCloseOnChainHtlcTimeout(net *lntest.NetworkHarness, + t *harnessTest) { + ctxb := context.Background() + + // First, we'll create a three hop network: Alice -> Bob -> Carol, with + // Carol refusing to actually settle or directly cancel any HTLC's + // self. + aliceChanPoint, bobChanPoint, carol := + createThreeHopNetwork(t, net, true) + + // Clean up carol's node when the test finishes. + defer shutdownAndAssert(net, t, carol) + + // With our channels set up, we'll then send a single HTLC from Alice + // to Carol. As Carol is in hodl mode, she won't settle this HTLC which + // opens up the base for out tests. + const ( + finalCltvDelta = 40 + htlcAmt = btcutil.Amount(30000) + ) + ctx, cancel := context.WithCancel(ctxb) + defer cancel() + + alicePayStream, err := net.Alice.SendPayment(ctx) + if err != nil { + t.Fatalf("unable to create payment stream for alice: %v", err) + } + + // We'll now send a single HTLC across our multi-hop network. + carolPubKey := carol.PubKey[:] + payHash := makeFakePayHash(t) + err = alicePayStream.Send(&lnrpc.SendRequest{ + Dest: carolPubKey, + Amt: int64(htlcAmt), + PaymentHash: payHash, + FinalCltvDelta: finalCltvDelta, + }) + if err != nil { + t.Fatalf("unable to send alice htlc: %v", err) + } + + // Once the HTLC has cleared, all channels in our mini network should + // have the it locked in. + var predErr error + nodes := []*lntest.HarnessNode{net.Alice, net.Bob, carol} + err = wait.Predicate(func() bool { + predErr = assertActiveHtlcs(nodes, payHash) + if predErr != nil { + return false + } + + return true + }, time.Second*15) + if err != nil { + t.Fatalf("htlc mismatch: %v", err) + } + + // Now that all parties have the HTLC locked in, we'll immediately + // force close the Bob -> Carol channel. This should trigger contract + // resolution mode for both of them. + ctxt, _ := context.WithTimeout(ctxb, channelCloseTimeout) + closeChannelAndAssert(ctxt, t, net, net.Bob, bobChanPoint, true) + + // At this point, Bob should have a pending force close channel as he + // just went to chain. + pendingChansRequest := &lnrpc.PendingChannelsRequest{} + err = wait.Predicate(func() bool { + ctxt, _ = context.WithTimeout(ctxb, defaultTimeout) + pendingChanResp, err := net.Bob.PendingChannels(ctxt, + pendingChansRequest) + if err != nil { + predErr = fmt.Errorf("unable to query for pending "+ + "channels: %v", err) + return false + } + if len(pendingChanResp.PendingForceClosingChannels) == 0 { + predErr = fmt.Errorf("bob should have pending for " + + "close chan but doesn't") + return false + } + + forceCloseChan := pendingChanResp.PendingForceClosingChannels[0] + if forceCloseChan.LimboBalance == 0 { + predErr = fmt.Errorf("bob should have nonzero limbo "+ + "balance instead has: %v", + forceCloseChan.LimboBalance) + return false + } + + return true + }, time.Second*15) + if err != nil { + t.Fatalf(predErr.Error()) + } + + // We'll mine defaultCSV blocks in order to generate the sweep transaction + // of Bob's funding output. + if _, err := net.Miner.Node.Generate(defaultCSV); err != nil { + t.Fatalf("unable to generate blocks: %v", err) + } + + _, err = waitForTxInMempool(net.Miner.Node, minerMempoolTimeout) + if err != nil { + t.Fatalf("unable to find bob's funding output sweep tx: %v", err) + } + + // We'll now mine enough blocks for the HTLC to expire. After this, Bob + // should hand off the now expired HTLC output to the utxo nursery. + numBlocks := padCLTV(uint32(finalCltvDelta - defaultCSV - 1)) + if _, err := net.Miner.Node.Generate(numBlocks); err != nil { + t.Fatalf("unable to generate blocks: %v", err) + } + + // Bob's pending channel report should show that he has a single HTLC + // that's now in stage one. + err = wait.Predicate(func() bool { + ctxt, _ = context.WithTimeout(ctxb, defaultTimeout) + pendingChanResp, err := net.Bob.PendingChannels( + ctxt, pendingChansRequest, + ) + if err != nil { + predErr = fmt.Errorf("unable to query for pending "+ + "channels: %v", err) + return false + } + + if len(pendingChanResp.PendingForceClosingChannels) == 0 { + predErr = fmt.Errorf("bob should have pending force " + + "close chan but doesn't") + return false + } + + forceCloseChan := pendingChanResp.PendingForceClosingChannels[0] + if len(forceCloseChan.PendingHtlcs) != 1 { + predErr = fmt.Errorf("bob should have pending htlc " + + "but doesn't") + return false + } + if forceCloseChan.PendingHtlcs[0].Stage != 1 { + predErr = fmt.Errorf("bob's htlc should have "+ + "advanced to the first stage: %v", err) + return false + } + + return true + }, time.Second*15) + if err != nil { + t.Fatalf("bob didn't hand off time-locked HTLC: %v", predErr) + } + + // We should also now find a transaction in the mempool, as Bob should + // have broadcast his second layer timeout transaction. + timeoutTx, err := waitForTxInMempool(net.Miner.Node, minerMempoolTimeout) + if err != nil { + t.Fatalf("unable to find bob's htlc timeout tx: %v", err) + } + + // Next, we'll mine an additional block. This should serve to confirm + // the second layer timeout transaction. + block := mineBlocks(t, net, 1, 1)[0] + assertTxInBlock(t, block, timeoutTx) + + // With the second layer timeout transaction confirmed, Bob should have + // canceled backwards the HTLC that carol sent. + nodes = []*lntest.HarnessNode{net.Alice} + err = wait.Predicate(func() bool { + predErr = assertNumActiveHtlcs(nodes, 0) + if predErr != nil { + return false + } + return true + }, time.Second*15) + if err != nil { + t.Fatalf("alice's channel still has active htlc's: %v", predErr) + } + + // Additionally, Bob should now show that HTLC as being advanced to the + // second stage. + err = wait.Predicate(func() bool { + ctxt, _ = context.WithTimeout(ctxb, defaultTimeout) + pendingChanResp, err := net.Bob.PendingChannels( + ctxt, pendingChansRequest, + ) + if err != nil { + predErr = fmt.Errorf("unable to query for pending "+ + "channels: %v", err) + return false + } + + if len(pendingChanResp.PendingForceClosingChannels) == 0 { + predErr = fmt.Errorf("bob should have pending for " + + "close chan but doesn't") + return false + } + + forceCloseChan := pendingChanResp.PendingForceClosingChannels[0] + if len(forceCloseChan.PendingHtlcs) != 1 { + predErr = fmt.Errorf("bob should have pending htlc " + + "but doesn't") + return false + } + if forceCloseChan.PendingHtlcs[0].Stage != 2 { + predErr = fmt.Errorf("bob's htlc should have "+ + "advanced to the second stage: %v", err) + return false + } + + return true + }, time.Second*15) + if err != nil { + t.Fatalf("bob didn't hand off time-locked HTLC: %v", predErr) + } + + // We'll now mine 4 additional blocks. This should be enough for Bob's + // CSV timelock to expire and the sweeping transaction of the HTLC to be + // broadcast. + if _, err := net.Miner.Node.Generate(defaultCSV); err != nil { + t.Fatalf("unable to mine blocks: %v", err) + } + + sweepTx, err := waitForTxInMempool(net.Miner.Node, minerMempoolTimeout) + if err != nil { + t.Fatalf("unable to find bob's htlc sweep tx: %v", err) + } + + // We'll then mine a final block which should confirm this second layer + // sweep transaction. + block = mineBlocks(t, net, 1, 1)[0] + assertTxInBlock(t, block, sweepTx) + + // At this point, Bob should no longer show any channels as pending + // close. + err = wait.Predicate(func() bool { + ctxt, _ = context.WithTimeout(ctxb, defaultTimeout) + pendingChanResp, err := net.Bob.PendingChannels( + ctxt, pendingChansRequest, + ) + if err != nil { + predErr = fmt.Errorf("unable to query for pending "+ + "channels: %v", err) + return false + } + if len(pendingChanResp.PendingForceClosingChannels) != 0 { + predErr = fmt.Errorf("bob still has pending channels "+ + "but shouldn't: %v", spew.Sdump(pendingChanResp)) + return false + } + + return true + }, time.Second*15) + if err != nil { + t.Fatalf(predErr.Error()) + } + + ctxt, _ = context.WithTimeout(ctxb, channelCloseTimeout) + closeChannelAndAssert(ctxt, t, net, net.Alice, aliceChanPoint, false) +} diff --git a/lntest/itest/lnd_multi-hop_remote_force_close_on_chain_htlc_timeout_test.go b/lntest/itest/lnd_multi-hop_remote_force_close_on_chain_htlc_timeout_test.go new file mode 100644 index 00000000000..eea8e1f63c4 --- /dev/null +++ b/lntest/itest/lnd_multi-hop_remote_force_close_on_chain_htlc_timeout_test.go @@ -0,0 +1,236 @@ +// +build rpctest + +package itest + +import ( + "context" + "fmt" + "time" + + "github.com/btcsuite/btcutil" + "github.com/davecgh/go-spew/spew" + "github.com/lightningnetwork/lnd/lnrpc" + "github.com/lightningnetwork/lnd/lntest" + "github.com/lightningnetwork/lnd/lntest/wait" +) + +// testMultiHopRemoteForceCloseOnChainHtlcTimeout tests that if we extend a +// multi-hop HTLC, and the final destination of the HTLC force closes the +// channel, then we properly timeout the HTLC on *their* commitment transaction +// once the timeout has expired. Once we sweep the transaction, we should also +// cancel back the initial HTLC. +func testMultiHopRemoteForceCloseOnChainHtlcTimeout(net *lntest.NetworkHarness, + t *harnessTest) { + ctxb := context.Background() + + // First, we'll create a three hop network: Alice -> Bob -> Carol, with + // Carol refusing to actually settle or directly cancel any HTLC's + // self. + aliceChanPoint, bobChanPoint, carol := + createThreeHopNetwork(t, net, true) + + // Clean up carol's node when the test finishes. + defer shutdownAndAssert(net, t, carol) + + // With our channels set up, we'll then send a single HTLC from Alice + // to Carol. As Carol is in hodl mode, she won't settle this HTLC which + // opens up the base for out tests. + const ( + finalCltvDelta = 40 + htlcAmt = btcutil.Amount(30000) + ) + + ctx, cancel := context.WithCancel(ctxb) + defer cancel() + + alicePayStream, err := net.Alice.SendPayment(ctx) + if err != nil { + t.Fatalf("unable to create payment stream for alice: %v", err) + } + + // We'll now send a single HTLC across our multi-hop network. + carolPubKey := carol.PubKey[:] + payHash := makeFakePayHash(t) + err = alicePayStream.Send(&lnrpc.SendRequest{ + Dest: carolPubKey, + Amt: int64(htlcAmt), + PaymentHash: payHash, + FinalCltvDelta: finalCltvDelta, + }) + if err != nil { + t.Fatalf("unable to send alice htlc: %v", err) + } + + // Once the HTLC has cleared, all the nodes in our mini network should + // show that the HTLC has been locked in. + var predErr error + nodes := []*lntest.HarnessNode{net.Alice, net.Bob, carol} + err = wait.Predicate(func() bool { + predErr = assertActiveHtlcs(nodes, payHash) + if predErr != nil { + return false + } + + return true + }, time.Second*15) + if err != nil { + t.Fatalf("htlc mismatch: %v", predErr) + } + + // At this point, we'll now instruct Carol to force close the + // transaction. This will let us exercise that Bob is able to sweep the + // expired HTLC on Carol's version of the commitment transaction. + ctxt, _ := context.WithTimeout(ctxb, channelCloseTimeout) + closeChannelAndAssert(ctxt, t, net, carol, bobChanPoint, true) + + // At this point, Bob should have a pending force close channel as + // Carol has gone directly to chain. + pendingChansRequest := &lnrpc.PendingChannelsRequest{} + err = wait.Predicate(func() bool { + ctxt, _ = context.WithTimeout(ctxb, defaultTimeout) + pendingChanResp, err := net.Bob.PendingChannels( + ctxt, pendingChansRequest, + ) + if err != nil { + predErr = fmt.Errorf("unable to query for "+ + "pending channels: %v", err) + return false + } + if len(pendingChanResp.PendingForceClosingChannels) == 0 { + predErr = fmt.Errorf("bob should have pending " + + "force close channels but doesn't") + return false + } + + return true + }, time.Second*15) + if err != nil { + t.Fatalf(predErr.Error()) + } + + // Bob can sweep his output immediately. + _, err = waitForTxInMempool(net.Miner.Node, minerMempoolTimeout) + if err != nil { + t.Fatalf("unable to find bob's funding output sweep tx: %v", + err) + } + + // Next, we'll mine enough blocks for the HTLC to expire. At this + // point, Bob should hand off the output to his internal utxo nursery, + // which will broadcast a sweep transaction. + numBlocks := padCLTV(finalCltvDelta - 1) + if _, err := net.Miner.Node.Generate(numBlocks); err != nil { + t.Fatalf("unable to generate blocks: %v", err) + } + + // If we check Bob's pending channel report, it should show that he has + // a single HTLC that's now in the second stage, as skip the initial + // first stage since this is a direct HTLC. + err = wait.Predicate(func() bool { + ctxt, _ = context.WithTimeout(ctxb, defaultTimeout) + pendingChanResp, err := net.Bob.PendingChannels( + ctxt, pendingChansRequest, + ) + if err != nil { + predErr = fmt.Errorf("unable to query for pending "+ + "channels: %v", err) + return false + } + + if len(pendingChanResp.PendingForceClosingChannels) == 0 { + predErr = fmt.Errorf("bob should have pending for " + + "close chan but doesn't") + return false + } + + forceCloseChan := pendingChanResp.PendingForceClosingChannels[0] + if len(forceCloseChan.PendingHtlcs) != 1 { + predErr = fmt.Errorf("bob should have pending htlc " + + "but doesn't") + return false + } + if forceCloseChan.PendingHtlcs[0].Stage != 2 { + predErr = fmt.Errorf("bob's htlc should have "+ + "advanced to the second stage: %v", err) + return false + } + + return true + }, time.Second*15) + if err != nil { + t.Fatalf("bob didn't hand off time-locked HTLC: %v", predErr) + } + + // Bob's sweeping transaction should now be found in the mempool at + // this point. + sweepTx, err := waitForTxInMempool(net.Miner.Node, minerMempoolTimeout) + if err != nil { + // If Bob's transaction isn't yet in the mempool, then due to + // internal message passing and the low period between blocks + // being mined, it may have been detected as a late + // 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) + } + sweepTx, err = waitForTxInMempool(net.Miner.Node, minerMempoolTimeout) + if err != nil { + t.Fatalf("unable to find bob's sweeping transaction: "+ + "%v", err) + } + } + + // If we mine an additional block, then this should confirm Bob's + // transaction which sweeps the direct HTLC output. + block := mineBlocks(t, net, 1, 1)[0] + assertTxInBlock(t, block, sweepTx) + + // Now that the sweeping transaction has been confirmed, Bob should + // cancel back that HTLC. As a result, Alice should not know of any + // active HTLC's. + nodes = []*lntest.HarnessNode{net.Alice} + err = wait.Predicate(func() bool { + predErr = assertNumActiveHtlcs(nodes, 0) + if predErr != nil { + return false + } + return true + }, time.Second*15) + if err != nil { + t.Fatalf("alice's channel still has active htlc's: %v", predErr) + } + + // Now we'll check Bob's pending channel report. Since this was Carol's + // commitment, he doesn't have to wait for any CSV delays. As a result, + // he should show no additional pending transactions. + err = wait.Predicate(func() bool { + ctxt, _ = context.WithTimeout(ctxb, defaultTimeout) + pendingChanResp, err := net.Bob.PendingChannels( + ctxt, pendingChansRequest, + ) + if err != nil { + predErr = fmt.Errorf("unable to query for pending "+ + "channels: %v", err) + return false + } + if len(pendingChanResp.PendingForceClosingChannels) != 0 { + predErr = fmt.Errorf("bob still has pending channels "+ + "but shouldn't: %v", spew.Sdump(pendingChanResp)) + return false + } + + return true + }, time.Second*15) + if err != nil { + t.Fatalf(predErr.Error()) + } + + // We'll close out the test by closing the channel from Alice to Bob, + // and then shutting down the new node we created as its no longer + // needed. + ctxt, _ = context.WithTimeout(ctxb, channelCloseTimeout) + closeChannelAndAssert(ctxt, t, net, net.Alice, aliceChanPoint, false) +} diff --git a/lntest/itest/lnd_test.go b/lntest/itest/lnd_test.go index b0011d2ae39..09e567fca82 100644 --- a/lntest/itest/lnd_test.go +++ b/lntest/itest/lnd_test.go @@ -10592,796 +10592,6 @@ func assertSpendingTxInMempool(t *harnessTest, miner *rpcclient.Client, } } -func createThreeHopNetwork(t *harnessTest, net *lntest.NetworkHarness, - carolHodl bool) (*lnrpc.ChannelPoint, *lnrpc.ChannelPoint, - *lntest.HarnessNode) { - - ctxb := context.Background() - - // We'll start the test by creating a channel between Alice and Bob, - // which will act as the first leg for out multi-hop HTLC. - const chanAmt = 1000000 - ctxt, _ := context.WithTimeout(ctxb, channelOpenTimeout) - aliceChanPoint := openChannelAndAssert( - ctxt, t, net, net.Alice, net.Bob, - lntest.OpenChannelParams{ - Amt: chanAmt, - }, - ) - - ctxt, _ = context.WithTimeout(ctxb, defaultTimeout) - err := net.Alice.WaitForNetworkChannelOpen(ctxt, aliceChanPoint) - if err != nil { - t.Fatalf("alice didn't report channel: %v", err) - } - - ctxt, _ = context.WithTimeout(ctxb, defaultTimeout) - err = net.Bob.WaitForNetworkChannelOpen(ctxt, aliceChanPoint) - if err != nil { - t.Fatalf("bob didn't report channel: %v", err) - } - - // Next, we'll create a new node "carol" and have Bob connect to her. If - // the carolHodl flag is set, we'll make carol always hold onto the - // HTLC, this way it'll force Bob to go to chain to resolve the HTLC. - carolFlags := []string{} - if carolHodl { - carolFlags = append(carolFlags, "--hodl.exit-settle") - } - carol, err := net.NewNode("Carol", carolFlags) - if err != nil { - t.Fatalf("unable to create new node: %v", err) - } - ctxt, _ = context.WithTimeout(ctxb, defaultTimeout) - if err := net.ConnectNodes(ctxt, net.Bob, carol); err != nil { - t.Fatalf("unable to connect bob to carol: %v", err) - } - - // We'll then create a channel from Bob to Carol. After this channel is - // open, our topology looks like: A -> B -> C. - ctxt, _ = context.WithTimeout(ctxb, channelOpenTimeout) - bobChanPoint := openChannelAndAssert( - ctxt, t, net, net.Bob, carol, - lntest.OpenChannelParams{ - Amt: chanAmt, - }, - ) - ctxt, _ = context.WithTimeout(ctxb, defaultTimeout) - err = net.Bob.WaitForNetworkChannelOpen(ctxt, bobChanPoint) - if err != nil { - t.Fatalf("alice didn't report channel: %v", err) - } - ctxt, _ = context.WithTimeout(ctxb, defaultTimeout) - err = carol.WaitForNetworkChannelOpen(ctxt, bobChanPoint) - if err != nil { - t.Fatalf("bob didn't report channel: %v", err) - } - ctxt, _ = context.WithTimeout(ctxb, defaultTimeout) - err = net.Alice.WaitForNetworkChannelOpen(ctxt, bobChanPoint) - if err != nil { - t.Fatalf("bob didn't report channel: %v", err) - } - - return aliceChanPoint, bobChanPoint, carol -} - -// testMultiHopHtlcLocalTimeout tests that in a multi-hop HTLC scenario, if the -// outgoing HTLC is about to time out, then we'll go to chain in order to claim -// it. Any dust HTLC's should be immediately canceled backwards. Once the -// timeout has been reached, then we should sweep it on-chain, and cancel the -// HTLC backwards. -func testMultiHopHtlcLocalTimeout(net *lntest.NetworkHarness, t *harnessTest) { - ctxb := context.Background() - - // First, we'll create a three hop network: Alice -> Bob -> Carol, with - // Carol refusing to actually settle or directly cancel any HTLC's - // self. - aliceChanPoint, bobChanPoint, carol := - createThreeHopNetwork(t, net, true) - - // Clean up carol's node when the test finishes. - defer shutdownAndAssert(net, t, carol) - - time.Sleep(time.Second * 1) - - // Now that our channels are set up, we'll send two HTLC's from Alice - // to Carol. The first HTLC will be universally considered "dust", - // while the second will be a proper fully valued HTLC. - const ( - dustHtlcAmt = btcutil.Amount(100) - htlcAmt = btcutil.Amount(30000) - finalCltvDelta = 40 - ) - - ctx, cancel := context.WithCancel(ctxb) - defer cancel() - - alicePayStream, err := net.Alice.SendPayment(ctx) - if err != nil { - t.Fatalf("unable to create payment stream for alice: %v", err) - } - - // We'll create two random payment hashes unknown to carol, then send - // each of them by manually specifying the HTLC details. - carolPubKey := carol.PubKey[:] - dustPayHash := makeFakePayHash(t) - payHash := makeFakePayHash(t) - err = alicePayStream.Send(&lnrpc.SendRequest{ - Dest: carolPubKey, - Amt: int64(dustHtlcAmt), - PaymentHash: dustPayHash, - FinalCltvDelta: finalCltvDelta, - }) - if err != nil { - t.Fatalf("unable to send alice htlc: %v", err) - } - err = alicePayStream.Send(&lnrpc.SendRequest{ - Dest: carolPubKey, - Amt: int64(htlcAmt), - PaymentHash: payHash, - FinalCltvDelta: finalCltvDelta, - }) - if err != nil { - t.Fatalf("unable to send alice htlc: %v", err) - } - - // Verify that all nodes in the path now have two HTLC's with the - // proper parameters. - var predErr error - nodes := []*lntest.HarnessNode{net.Alice, net.Bob, carol} - err = wait.Predicate(func() bool { - predErr = assertActiveHtlcs(nodes, dustPayHash, payHash) - if predErr != nil { - return false - } - - return true - }, time.Second*15) - if err != nil { - t.Fatalf("htlc mismatch: %v", predErr) - } - - // We'll now mine enough blocks to trigger Bob's broadcast of his - // commitment transaction due to the fact that the HTLC is about to - // timeout. With the default outgoing broadcast delta of zero, this will - // be the same height as the htlc expiry height. - numBlocks := padCLTV( - uint32(finalCltvDelta - lnd.DefaultOutgoingBroadcastDelta), - ) - if _, err := net.Miner.Node.Generate(numBlocks); err != nil { - t.Fatalf("unable to generate blocks: %v", err) - } - - // Bob's force close transaction should now be found in the mempool. - bobFundingTxid, err := lnd.GetChanPointFundingTxid(bobChanPoint) - if err != nil { - t.Fatalf("unable to get txid: %v", err) - } - closeTxid, err := waitForTxInMempool(net.Miner.Node, minerMempoolTimeout) - if err != nil { - t.Fatalf("unable to find closing txid: %v", err) - } - assertSpendingTxInMempool( - t, net.Miner.Node, minerMempoolTimeout, wire.OutPoint{ - Hash: *bobFundingTxid, - Index: bobChanPoint.OutputIndex, - }, - ) - - // Mine a block to confirm the closing transaction. - mineBlocks(t, net, 1, 1) - - // At this point, Bob should have canceled backwards the dust HTLC - // that we sent earlier. This means Alice should now only have a single - // HTLC on her channel. - nodes = []*lntest.HarnessNode{net.Alice} - err = wait.Predicate(func() bool { - predErr = assertActiveHtlcs(nodes, payHash) - if predErr != nil { - return false - } - - return true - }, time.Second*15) - if err != nil { - t.Fatalf("htlc mismatch: %v", predErr) - } - - // With the closing transaction confirmed, we should expect Bob's HTLC - // timeout transaction to be broadcast due to the expiry being reached. - htlcTimeout, err := waitForTxInMempool(net.Miner.Node, minerMempoolTimeout) - if err != nil { - t.Fatalf("unable to find bob's htlc timeout tx: %v", err) - } - - // We'll mine the remaining blocks in order to generate the sweep - // transaction of Bob's commitment output. - mineBlocks(t, net, defaultCSV, 1) - assertSpendingTxInMempool( - t, net.Miner.Node, minerMempoolTimeout, wire.OutPoint{ - Hash: *closeTxid, - Index: 1, - }, - ) - - // Bob's pending channel report should show that he has a commitment - // output awaiting sweeping, and also that there's an outgoing HTLC - // output pending. - pendingChansRequest := &lnrpc.PendingChannelsRequest{} - ctxt, _ := context.WithTimeout(ctxb, defaultTimeout) - pendingChanResp, err := net.Bob.PendingChannels(ctxt, pendingChansRequest) - if err != nil { - t.Fatalf("unable to query for pending channels: %v", err) - } - - if len(pendingChanResp.PendingForceClosingChannels) == 0 { - t.Fatalf("bob should have pending for close chan but doesn't") - } - forceCloseChan := pendingChanResp.PendingForceClosingChannels[0] - if forceCloseChan.LimboBalance == 0 { - t.Fatalf("bob should have nonzero limbo balance instead "+ - "has: %v", forceCloseChan.LimboBalance) - } - if len(forceCloseChan.PendingHtlcs) == 0 { - t.Fatalf("bob should have pending htlc but doesn't") - } - - // Now we'll mine an additional block, which should confirm Bob's commit - // sweep. This block should also prompt Bob to broadcast their second - // layer sweep due to the CSV on the HTLC timeout output. - mineBlocks(t, net, 1, 1) - assertSpendingTxInMempool( - t, net.Miner.Node, minerMempoolTimeout, wire.OutPoint{ - Hash: *htlcTimeout, - Index: 0, - }, - ) - - // The block should have confirmed Bob's HTLC timeout transaction. - // Therefore, at this point, there should be no active HTLC's on the - // commitment transaction from Alice -> Bob. - nodes = []*lntest.HarnessNode{net.Alice} - err = wait.Predicate(func() bool { - predErr = assertNumActiveHtlcs(nodes, 0) - if predErr != nil { - return false - } - return true - }, time.Second*15) - if err != nil { - t.Fatalf("alice's channel still has active htlc's: %v", predErr) - } - - // At this point, Bob should show that the pending HTLC has advanced to - // the second stage and is to be swept. - ctxt, _ = context.WithTimeout(ctxb, defaultTimeout) - pendingChanResp, err = net.Bob.PendingChannels(ctxt, pendingChansRequest) - if err != nil { - t.Fatalf("unable to query for pending channels: %v", err) - } - forceCloseChan = pendingChanResp.PendingForceClosingChannels[0] - if forceCloseChan.PendingHtlcs[0].Stage != 2 { - t.Fatalf("bob's htlc should have advanced to the second stage: %v", err) - } - - // Next, we'll mine a final block that should confirm the second-layer - // sweeping transaction. - if _, err := net.Miner.Node.Generate(1); err != nil { - t.Fatalf("unable to generate blocks: %v", err) - } - - // Once this transaction has been confirmed, Bob should detect that he - // no longer has any pending channels. - err = wait.Predicate(func() bool { - ctxt, _ = context.WithTimeout(ctxb, defaultTimeout) - pendingChanResp, err = net.Bob.PendingChannels(ctxt, pendingChansRequest) - if err != nil { - predErr = fmt.Errorf("unable to query for pending "+ - "channels: %v", err) - return false - } - if len(pendingChanResp.PendingForceClosingChannels) != 0 { - predErr = fmt.Errorf("bob still has pending "+ - "channels but shouldn't: %v", - spew.Sdump(pendingChanResp)) - return false - } - - return true - - }, time.Second*15) - if err != nil { - t.Fatalf(predErr.Error()) - } - - ctxt, _ = context.WithTimeout(ctxb, channelCloseTimeout) - closeChannelAndAssert(ctxt, t, net, net.Alice, aliceChanPoint, false) -} - -// testMultiHopLocalForceCloseOnChainHtlcTimeout tests that in a multi-hop HTLC -// scenario, if the node that extended the HTLC to the final node closes their -// commitment on-chain early, then it eventually recognizes this HTLC as one -// that's timed out. At this point, the node should timeout the HTLC, then -// cancel it backwards as normal. -func testMultiHopLocalForceCloseOnChainHtlcTimeout(net *lntest.NetworkHarness, - t *harnessTest) { - ctxb := context.Background() - - // First, we'll create a three hop network: Alice -> Bob -> Carol, with - // Carol refusing to actually settle or directly cancel any HTLC's - // self. - aliceChanPoint, bobChanPoint, carol := - createThreeHopNetwork(t, net, true) - - // Clean up carol's node when the test finishes. - defer shutdownAndAssert(net, t, carol) - - // With our channels set up, we'll then send a single HTLC from Alice - // to Carol. As Carol is in hodl mode, she won't settle this HTLC which - // opens up the base for out tests. - const ( - finalCltvDelta = 40 - htlcAmt = btcutil.Amount(30000) - ) - ctx, cancel := context.WithCancel(ctxb) - defer cancel() - - alicePayStream, err := net.Alice.SendPayment(ctx) - if err != nil { - t.Fatalf("unable to create payment stream for alice: %v", err) - } - - // We'll now send a single HTLC across our multi-hop network. - carolPubKey := carol.PubKey[:] - payHash := makeFakePayHash(t) - err = alicePayStream.Send(&lnrpc.SendRequest{ - Dest: carolPubKey, - Amt: int64(htlcAmt), - PaymentHash: payHash, - FinalCltvDelta: finalCltvDelta, - }) - if err != nil { - t.Fatalf("unable to send alice htlc: %v", err) - } - - // Once the HTLC has cleared, all channels in our mini network should - // have the it locked in. - var predErr error - nodes := []*lntest.HarnessNode{net.Alice, net.Bob, carol} - err = wait.Predicate(func() bool { - predErr = assertActiveHtlcs(nodes, payHash) - if predErr != nil { - return false - } - - return true - }, time.Second*15) - if err != nil { - t.Fatalf("htlc mismatch: %v", err) - } - - // Now that all parties have the HTLC locked in, we'll immediately - // force close the Bob -> Carol channel. This should trigger contract - // resolution mode for both of them. - ctxt, _ := context.WithTimeout(ctxb, channelCloseTimeout) - closeChannelAndAssert(ctxt, t, net, net.Bob, bobChanPoint, true) - - // At this point, Bob should have a pending force close channel as he - // just went to chain. - pendingChansRequest := &lnrpc.PendingChannelsRequest{} - err = wait.Predicate(func() bool { - ctxt, _ = context.WithTimeout(ctxb, defaultTimeout) - pendingChanResp, err := net.Bob.PendingChannels(ctxt, - pendingChansRequest) - if err != nil { - predErr = fmt.Errorf("unable to query for pending "+ - "channels: %v", err) - return false - } - if len(pendingChanResp.PendingForceClosingChannels) == 0 { - predErr = fmt.Errorf("bob should have pending for " + - "close chan but doesn't") - return false - } - - forceCloseChan := pendingChanResp.PendingForceClosingChannels[0] - if forceCloseChan.LimboBalance == 0 { - predErr = fmt.Errorf("bob should have nonzero limbo "+ - "balance instead has: %v", - forceCloseChan.LimboBalance) - return false - } - - return true - }, time.Second*15) - if err != nil { - t.Fatalf(predErr.Error()) - } - - // We'll mine defaultCSV blocks in order to generate the sweep transaction - // of Bob's funding output. - if _, err := net.Miner.Node.Generate(defaultCSV); err != nil { - t.Fatalf("unable to generate blocks: %v", err) - } - - _, err = waitForTxInMempool(net.Miner.Node, minerMempoolTimeout) - if err != nil { - t.Fatalf("unable to find bob's funding output sweep tx: %v", err) - } - - // We'll now mine enough blocks for the HTLC to expire. After this, Bob - // should hand off the now expired HTLC output to the utxo nursery. - numBlocks := padCLTV(uint32(finalCltvDelta - defaultCSV - 1)) - if _, err := net.Miner.Node.Generate(numBlocks); err != nil { - t.Fatalf("unable to generate blocks: %v", err) - } - - // Bob's pending channel report should show that he has a single HTLC - // that's now in stage one. - err = wait.Predicate(func() bool { - ctxt, _ = context.WithTimeout(ctxb, defaultTimeout) - pendingChanResp, err := net.Bob.PendingChannels( - ctxt, pendingChansRequest, - ) - if err != nil { - predErr = fmt.Errorf("unable to query for pending "+ - "channels: %v", err) - return false - } - - if len(pendingChanResp.PendingForceClosingChannels) == 0 { - predErr = fmt.Errorf("bob should have pending force " + - "close chan but doesn't") - return false - } - - forceCloseChan := pendingChanResp.PendingForceClosingChannels[0] - if len(forceCloseChan.PendingHtlcs) != 1 { - predErr = fmt.Errorf("bob should have pending htlc " + - "but doesn't") - return false - } - if forceCloseChan.PendingHtlcs[0].Stage != 1 { - predErr = fmt.Errorf("bob's htlc should have "+ - "advanced to the first stage: %v", err) - return false - } - - return true - }, time.Second*15) - if err != nil { - t.Fatalf("bob didn't hand off time-locked HTLC: %v", predErr) - } - - // We should also now find a transaction in the mempool, as Bob should - // have broadcast his second layer timeout transaction. - timeoutTx, err := waitForTxInMempool(net.Miner.Node, minerMempoolTimeout) - if err != nil { - t.Fatalf("unable to find bob's htlc timeout tx: %v", err) - } - - // Next, we'll mine an additional block. This should serve to confirm - // the second layer timeout transaction. - block := mineBlocks(t, net, 1, 1)[0] - assertTxInBlock(t, block, timeoutTx) - - // With the second layer timeout transaction confirmed, Bob should have - // canceled backwards the HTLC that carol sent. - nodes = []*lntest.HarnessNode{net.Alice} - err = wait.Predicate(func() bool { - predErr = assertNumActiveHtlcs(nodes, 0) - if predErr != nil { - return false - } - return true - }, time.Second*15) - if err != nil { - t.Fatalf("alice's channel still has active htlc's: %v", predErr) - } - - // Additionally, Bob should now show that HTLC as being advanced to the - // second stage. - err = wait.Predicate(func() bool { - ctxt, _ = context.WithTimeout(ctxb, defaultTimeout) - pendingChanResp, err := net.Bob.PendingChannels( - ctxt, pendingChansRequest, - ) - if err != nil { - predErr = fmt.Errorf("unable to query for pending "+ - "channels: %v", err) - return false - } - - if len(pendingChanResp.PendingForceClosingChannels) == 0 { - predErr = fmt.Errorf("bob should have pending for " + - "close chan but doesn't") - return false - } - - forceCloseChan := pendingChanResp.PendingForceClosingChannels[0] - if len(forceCloseChan.PendingHtlcs) != 1 { - predErr = fmt.Errorf("bob should have pending htlc " + - "but doesn't") - return false - } - if forceCloseChan.PendingHtlcs[0].Stage != 2 { - predErr = fmt.Errorf("bob's htlc should have "+ - "advanced to the second stage: %v", err) - return false - } - - return true - }, time.Second*15) - if err != nil { - t.Fatalf("bob didn't hand off time-locked HTLC: %v", predErr) - } - - // We'll now mine 4 additional blocks. This should be enough for Bob's - // CSV timelock to expire and the sweeping transaction of the HTLC to be - // broadcast. - if _, err := net.Miner.Node.Generate(defaultCSV); err != nil { - t.Fatalf("unable to mine blocks: %v", err) - } - - sweepTx, err := waitForTxInMempool(net.Miner.Node, minerMempoolTimeout) - if err != nil { - t.Fatalf("unable to find bob's htlc sweep tx: %v", err) - } - - // We'll then mine a final block which should confirm this second layer - // sweep transaction. - block = mineBlocks(t, net, 1, 1)[0] - assertTxInBlock(t, block, sweepTx) - - // At this point, Bob should no longer show any channels as pending - // close. - err = wait.Predicate(func() bool { - ctxt, _ = context.WithTimeout(ctxb, defaultTimeout) - pendingChanResp, err := net.Bob.PendingChannels( - ctxt, pendingChansRequest, - ) - if err != nil { - predErr = fmt.Errorf("unable to query for pending "+ - "channels: %v", err) - return false - } - if len(pendingChanResp.PendingForceClosingChannels) != 0 { - predErr = fmt.Errorf("bob still has pending channels "+ - "but shouldn't: %v", spew.Sdump(pendingChanResp)) - return false - } - - return true - }, time.Second*15) - if err != nil { - t.Fatalf(predErr.Error()) - } - - ctxt, _ = context.WithTimeout(ctxb, channelCloseTimeout) - closeChannelAndAssert(ctxt, t, net, net.Alice, aliceChanPoint, false) -} - -// testMultiHopRemoteForceCloseOnChainHtlcTimeout tests that if we extend a -// multi-hop HTLC, and the final destination of the HTLC force closes the -// channel, then we properly timeout the HTLC on *their* commitment transaction -// once the timeout has expired. Once we sweep the transaction, we should also -// cancel back the initial HTLC. -func testMultiHopRemoteForceCloseOnChainHtlcTimeout(net *lntest.NetworkHarness, - t *harnessTest) { - ctxb := context.Background() - - // First, we'll create a three hop network: Alice -> Bob -> Carol, with - // Carol refusing to actually settle or directly cancel any HTLC's - // self. - aliceChanPoint, bobChanPoint, carol := - createThreeHopNetwork(t, net, true) - - // Clean up carol's node when the test finishes. - defer shutdownAndAssert(net, t, carol) - - // With our channels set up, we'll then send a single HTLC from Alice - // to Carol. As Carol is in hodl mode, she won't settle this HTLC which - // opens up the base for out tests. - const ( - finalCltvDelta = 40 - htlcAmt = btcutil.Amount(30000) - ) - - ctx, cancel := context.WithCancel(ctxb) - defer cancel() - - alicePayStream, err := net.Alice.SendPayment(ctx) - if err != nil { - t.Fatalf("unable to create payment stream for alice: %v", err) - } - - // We'll now send a single HTLC across our multi-hop network. - carolPubKey := carol.PubKey[:] - payHash := makeFakePayHash(t) - err = alicePayStream.Send(&lnrpc.SendRequest{ - Dest: carolPubKey, - Amt: int64(htlcAmt), - PaymentHash: payHash, - FinalCltvDelta: finalCltvDelta, - }) - if err != nil { - t.Fatalf("unable to send alice htlc: %v", err) - } - - // Once the HTLC has cleared, all the nodes in our mini network should - // show that the HTLC has been locked in. - var predErr error - nodes := []*lntest.HarnessNode{net.Alice, net.Bob, carol} - err = wait.Predicate(func() bool { - predErr = assertActiveHtlcs(nodes, payHash) - if predErr != nil { - return false - } - - return true - }, time.Second*15) - if err != nil { - t.Fatalf("htlc mismatch: %v", predErr) - } - - // At this point, we'll now instruct Carol to force close the - // transaction. This will let us exercise that Bob is able to sweep the - // expired HTLC on Carol's version of the commitment transaction. - ctxt, _ := context.WithTimeout(ctxb, channelCloseTimeout) - closeChannelAndAssert(ctxt, t, net, carol, bobChanPoint, true) - - // At this point, Bob should have a pending force close channel as - // Carol has gone directly to chain. - pendingChansRequest := &lnrpc.PendingChannelsRequest{} - err = wait.Predicate(func() bool { - ctxt, _ = context.WithTimeout(ctxb, defaultTimeout) - pendingChanResp, err := net.Bob.PendingChannels( - ctxt, pendingChansRequest, - ) - if err != nil { - predErr = fmt.Errorf("unable to query for "+ - "pending channels: %v", err) - return false - } - if len(pendingChanResp.PendingForceClosingChannels) == 0 { - predErr = fmt.Errorf("bob should have pending " + - "force close channels but doesn't") - return false - } - - return true - }, time.Second*15) - if err != nil { - t.Fatalf(predErr.Error()) - } - - // Bob can sweep his output immediately. - _, err = waitForTxInMempool(net.Miner.Node, minerMempoolTimeout) - if err != nil { - t.Fatalf("unable to find bob's funding output sweep tx: %v", - err) - } - - // Next, we'll mine enough blocks for the HTLC to expire. At this - // point, Bob should hand off the output to his internal utxo nursery, - // which will broadcast a sweep transaction. - numBlocks := padCLTV(finalCltvDelta - 1) - if _, err := net.Miner.Node.Generate(numBlocks); err != nil { - t.Fatalf("unable to generate blocks: %v", err) - } - - // If we check Bob's pending channel report, it should show that he has - // a single HTLC that's now in the second stage, as skip the initial - // first stage since this is a direct HTLC. - err = wait.Predicate(func() bool { - ctxt, _ = context.WithTimeout(ctxb, defaultTimeout) - pendingChanResp, err := net.Bob.PendingChannels( - ctxt, pendingChansRequest, - ) - if err != nil { - predErr = fmt.Errorf("unable to query for pending "+ - "channels: %v", err) - return false - } - - if len(pendingChanResp.PendingForceClosingChannels) == 0 { - predErr = fmt.Errorf("bob should have pending for " + - "close chan but doesn't") - return false - } - - forceCloseChan := pendingChanResp.PendingForceClosingChannels[0] - if len(forceCloseChan.PendingHtlcs) != 1 { - predErr = fmt.Errorf("bob should have pending htlc " + - "but doesn't") - return false - } - if forceCloseChan.PendingHtlcs[0].Stage != 2 { - predErr = fmt.Errorf("bob's htlc should have "+ - "advanced to the second stage: %v", err) - return false - } - - return true - }, time.Second*15) - if err != nil { - t.Fatalf("bob didn't hand off time-locked HTLC: %v", predErr) - } - - // Bob's sweeping transaction should now be found in the mempool at - // this point. - sweepTx, err := waitForTxInMempool(net.Miner.Node, minerMempoolTimeout) - if err != nil { - // If Bob's transaction isn't yet in the mempool, then due to - // internal message passing and the low period between blocks - // being mined, it may have been detected as a late - // 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) - } - sweepTx, err = waitForTxInMempool(net.Miner.Node, minerMempoolTimeout) - if err != nil { - t.Fatalf("unable to find bob's sweeping transaction: "+ - "%v", err) - } - } - - // If we mine an additional block, then this should confirm Bob's - // transaction which sweeps the direct HTLC output. - block := mineBlocks(t, net, 1, 1)[0] - assertTxInBlock(t, block, sweepTx) - - // Now that the sweeping transaction has been confirmed, Bob should - // cancel back that HTLC. As a result, Alice should not know of any - // active HTLC's. - nodes = []*lntest.HarnessNode{net.Alice} - err = wait.Predicate(func() bool { - predErr = assertNumActiveHtlcs(nodes, 0) - if predErr != nil { - return false - } - return true - }, time.Second*15) - if err != nil { - t.Fatalf("alice's channel still has active htlc's: %v", predErr) - } - - // Now we'll check Bob's pending channel report. Since this was Carol's - // commitment, he doesn't have to wait for any CSV delays. As a result, - // he should show no additional pending transactions. - err = wait.Predicate(func() bool { - ctxt, _ = context.WithTimeout(ctxb, defaultTimeout) - pendingChanResp, err := net.Bob.PendingChannels( - ctxt, pendingChansRequest, - ) - if err != nil { - predErr = fmt.Errorf("unable to query for pending "+ - "channels: %v", err) - return false - } - if len(pendingChanResp.PendingForceClosingChannels) != 0 { - predErr = fmt.Errorf("bob still has pending channels "+ - "but shouldn't: %v", spew.Sdump(pendingChanResp)) - return false - } - - return true - }, time.Second*15) - if err != nil { - t.Fatalf(predErr.Error()) - } - - // We'll close out the test by closing the channel from Alice to Bob, - // and then shutting down the new node we created as its no longer - // needed. - ctxt, _ = context.WithTimeout(ctxb, channelCloseTimeout) - closeChannelAndAssert(ctxt, t, net, net.Alice, aliceChanPoint, false) -} - // testSwitchCircuitPersistence creates a multihop network to ensure the sender // and intermediaries are persisting their open payment circuits. After // forwarding a packet via an outgoing link, all are restarted, and expected to From 23bf4dfec1a290d74f2f8bd43e1a94a8f0babaa2 Mon Sep 17 00:00:00 2001 From: "Johan T. Halseth" Date: Wed, 4 Mar 2020 13:21:28 +0100 Subject: [PATCH 05/11] itest: update multi hop test case docs To make clear whcih sweep scenarios are actually being tested --- lntest/itest/lnd_multi-hop_htlc_local_chain_claim_test.go | 6 +++--- lntest/itest/lnd_multi-hop_htlc_local_timeout_test.go | 6 +++--- .../itest/lnd_multi-hop_htlc_receiver_chain_claim_test.go | 7 ++++--- lntest/itest/lnd_multi-hop_htlc_remote_chain_claim_test.go | 3 ++- ...lti-hop_local_force_close_on_chain_htlc_timeout_test.go | 4 ++-- ...ti-hop_remote_force_close_on_chain_htlc_timeout_test.go | 6 +++--- lntest/itest/lnd_test.go | 2 +- 7 files changed, 18 insertions(+), 16 deletions(-) diff --git a/lntest/itest/lnd_multi-hop_htlc_local_chain_claim_test.go b/lntest/itest/lnd_multi-hop_htlc_local_chain_claim_test.go index dd84105c76f..1ae63b14339 100644 --- a/lntest/itest/lnd_multi-hop_htlc_local_chain_claim_test.go +++ b/lntest/itest/lnd_multi-hop_htlc_local_chain_claim_test.go @@ -18,9 +18,9 @@ import ( ) // testMultiHopHtlcLocalChainClaim tests that in a multi-hop HTLC scenario, if -// we're forced to go to chain with an incoming HTLC, then when we find out the -// preimage via the witness beacon, we properly settle the HTLC on-chain in -// order to ensure we don't lose any funds. +// we force close a channel with an incoming HTLC, and later find out the +// preimage via the witness beacon, we properly settle the HTLC on-chain using +// the HTLC success transaction in order to ensure we don't lose any funds. func testMultiHopHtlcLocalChainClaim(net *lntest.NetworkHarness, t *harnessTest) { ctxb := context.Background() diff --git a/lntest/itest/lnd_multi-hop_htlc_local_timeout_test.go b/lntest/itest/lnd_multi-hop_htlc_local_timeout_test.go index cb32a0f9d50..71c66716672 100644 --- a/lntest/itest/lnd_multi-hop_htlc_local_timeout_test.go +++ b/lntest/itest/lnd_multi-hop_htlc_local_timeout_test.go @@ -18,9 +18,9 @@ import ( // testMultiHopHtlcLocalTimeout tests that in a multi-hop HTLC scenario, if the // outgoing HTLC is about to time out, then we'll go to chain in order to claim -// it. Any dust HTLC's should be immediately canceled backwards. Once the -// timeout has been reached, then we should sweep it on-chain, and cancel the -// HTLC backwards. +// it using the HTLC timeout transaction. Any dust HTLC's should be immediately +// canceled backwards. Once the timeout has been reached, then we should sweep +// it on-chain, and cancel the HTLC backwards. func testMultiHopHtlcLocalTimeout(net *lntest.NetworkHarness, t *harnessTest) { ctxb := context.Background() diff --git a/lntest/itest/lnd_multi-hop_htlc_receiver_chain_claim_test.go b/lntest/itest/lnd_multi-hop_htlc_receiver_chain_claim_test.go index 8a017633935..0df39250461 100644 --- a/lntest/itest/lnd_multi-hop_htlc_receiver_chain_claim_test.go +++ b/lntest/itest/lnd_multi-hop_htlc_receiver_chain_claim_test.go @@ -20,9 +20,10 @@ import ( // testMultiHopReceiverChainClaim tests that in the multi-hop setting, if the // receiver of an HTLC knows the preimage, but wasn't able to settle the HTLC -// off-chain, then it goes on chain to claim the HTLC. In this scenario, the -// node that sent the outgoing HTLC should extract the preimage from the sweep -// transaction, and finish settling the HTLC backwards into the route. +// off-chain, then it goes on chain to claim the HTLC uing the HTLC success +// transaction. In this scenario, the node that sent the outgoing HTLC should +// extract the preimage from the sweep transaction, and finish settling the +// HTLC backwards into the route. func testMultiHopReceiverChainClaim(net *lntest.NetworkHarness, t *harnessTest) { ctxb := context.Background() diff --git a/lntest/itest/lnd_multi-hop_htlc_remote_chain_claim_test.go b/lntest/itest/lnd_multi-hop_htlc_remote_chain_claim_test.go index b96bc17f8ee..8c3a1f285f8 100644 --- a/lntest/itest/lnd_multi-hop_htlc_remote_chain_claim_test.go +++ b/lntest/itest/lnd_multi-hop_htlc_remote_chain_claim_test.go @@ -20,7 +20,8 @@ import ( // testMultiHopHtlcRemoteChainClaim tests that in the multi-hop HTLC scenario, // if the remote party goes to chain while we have an incoming HTLC, then when // we found out the preimage via the witness beacon, we properly settle the -// HTLC on-chain in order to ensure that we don't lose any funds. +// HTLC directly on-chain using the preimage in order to ensure that we don't +// lose any funds. func testMultiHopHtlcRemoteChainClaim(net *lntest.NetworkHarness, t *harnessTest) { ctxb := context.Background() diff --git a/lntest/itest/lnd_multi-hop_local_force_close_on_chain_htlc_timeout_test.go b/lntest/itest/lnd_multi-hop_local_force_close_on_chain_htlc_timeout_test.go index ea5bf3d7851..960ff17d4d1 100644 --- a/lntest/itest/lnd_multi-hop_local_force_close_on_chain_htlc_timeout_test.go +++ b/lntest/itest/lnd_multi-hop_local_force_close_on_chain_htlc_timeout_test.go @@ -17,8 +17,8 @@ import ( // testMultiHopLocalForceCloseOnChainHtlcTimeout tests that in a multi-hop HTLC // scenario, if the node that extended the HTLC to the final node closes their // commitment on-chain early, then it eventually recognizes this HTLC as one -// that's timed out. At this point, the node should timeout the HTLC, then -// cancel it backwards as normal. +// that's timed out. At this point, the node should timeout the HTLC using the +// HTLC timeout transaction, then cancel it backwards as normal. func testMultiHopLocalForceCloseOnChainHtlcTimeout(net *lntest.NetworkHarness, t *harnessTest) { ctxb := context.Background() diff --git a/lntest/itest/lnd_multi-hop_remote_force_close_on_chain_htlc_timeout_test.go b/lntest/itest/lnd_multi-hop_remote_force_close_on_chain_htlc_timeout_test.go index eea8e1f63c4..6c6d6eab65c 100644 --- a/lntest/itest/lnd_multi-hop_remote_force_close_on_chain_htlc_timeout_test.go +++ b/lntest/itest/lnd_multi-hop_remote_force_close_on_chain_htlc_timeout_test.go @@ -16,9 +16,9 @@ import ( // testMultiHopRemoteForceCloseOnChainHtlcTimeout tests that if we extend a // multi-hop HTLC, and the final destination of the HTLC force closes the -// channel, then we properly timeout the HTLC on *their* commitment transaction -// once the timeout has expired. Once we sweep the transaction, we should also -// cancel back the initial HTLC. +// channel, then we properly timeout the HTLC directly on *their* commitment +// transaction once the timeout has expired. Once we sweep the transaction, we +// should also cancel back the initial HTLC. func testMultiHopRemoteForceCloseOnChainHtlcTimeout(net *lntest.NetworkHarness, t *harnessTest) { ctxb := context.Background() diff --git a/lntest/itest/lnd_test.go b/lntest/itest/lnd_test.go index 09e567fca82..a54c3ca0e5a 100644 --- a/lntest/itest/lnd_test.go +++ b/lntest/itest/lnd_test.go @@ -2975,7 +2975,7 @@ func padCLTV(cltv uint32) uint32 { // total of 3 + n transactions will be broadcast, representing the commitment // transaction, a transaction sweeping the local CSV delayed output, a // transaction sweeping the CSV delayed 2nd-layer htlcs outputs, and n -// htlc success transactions, where n is the number of payments Alice attempted +// htlc timeout transactions, where n is the number of payments Alice attempted // to send to Carol. This test includes several restarts to ensure that the // transaction output states are persisted throughout the forced closure // process. From 03288b1572b2ab8377cb72816416e87d97d1d05d Mon Sep 17 00:00:00 2001 From: "Johan T. Halseth" Date: Wed, 4 Mar 2020 13:21:28 +0100 Subject: [PATCH 06/11] itest: extract multi-hop tests into sub tests --- ...d_multi-hop_htlc_local_chain_claim_test.go | 65 +++++++++++++++++++ lntest/itest/lnd_test.go | 38 +---------- 2 files changed, 67 insertions(+), 36 deletions(-) diff --git a/lntest/itest/lnd_multi-hop_htlc_local_chain_claim_test.go b/lntest/itest/lnd_multi-hop_htlc_local_chain_claim_test.go index 1ae63b14339..e07102b1529 100644 --- a/lntest/itest/lnd_multi-hop_htlc_local_chain_claim_test.go +++ b/lntest/itest/lnd_multi-hop_htlc_local_chain_claim_test.go @@ -5,9 +5,11 @@ package itest import ( "context" "fmt" + "testing" "time" "github.com/btcsuite/btcd/wire" + "github.com/btcsuite/btcutil" "github.com/davecgh/go-spew/spew" "github.com/lightningnetwork/lnd" "github.com/lightningnetwork/lnd/lnrpc" @@ -17,6 +19,69 @@ import ( "github.com/lightningnetwork/lnd/lntypes" ) +func testMultiHopHtlcClaims(net *lntest.NetworkHarness, t *harnessTest) { + + type testCase struct { + name string + test func(net *lntest.NetworkHarness, t *harnessTest) + } + + subTests := []testCase{ + { + // bob: outgoing our commit timeout + // carol: incoming their commit watch and see timeout + name: "local force close immediate expiry", + test: testMultiHopHtlcLocalTimeout, + }, + { + // bob: outgoing watch and see, they sweep on chain + // carol: incoming our commit, know preimage + name: "receiver chain claim", + test: testMultiHopReceiverChainClaim, + }, + { + // bob: outgoing our commit watch and see timeout + // carol: incoming their commit watch and see timeout + name: "local force close on-chain htlc timeout", + test: testMultiHopLocalForceCloseOnChainHtlcTimeout, + }, + { + // bob: outgoing their commit watch and see timeout + // carol: incoming our commit watch and see timeout + name: "remote force close on-chain htlc timeout", + test: testMultiHopRemoteForceCloseOnChainHtlcTimeout, + }, + { + // bob: outgoing our commit watch and see, they sweep on chain + // bob: incoming our commit watch and learn preimage + // carol: incoming their commit know preimage + name: "local chain claim", + test: testMultiHopHtlcLocalChainClaim, + }, + { + // bob: outgoing their commit watch and see, they sweep on chain + // bob: incoming their commit watch and learn preimage + // carol: incoming our commit know preimage + name: "remote chain claim", + test: testMultiHopHtlcRemoteChainClaim, + }, + } + + for _, subTest := range subTests { + + subTest := subTest + + success := t.t.Run(subTest.name, func(t *testing.T) { + ht := newHarnessTest(t, net) + + subTest.test(net, ht) + }) + if !success { + return + } + } +} + // testMultiHopHtlcLocalChainClaim tests that in a multi-hop HTLC scenario, if // we force close a channel with an incoming HTLC, and later find out the // preimage via the witness beacon, we properly settle the HTLC on-chain using diff --git a/lntest/itest/lnd_test.go b/lntest/itest/lnd_test.go index a54c3ca0e5a..e4d88a36c7f 100644 --- a/lntest/itest/lnd_test.go +++ b/lntest/itest/lnd_test.go @@ -14550,42 +14550,8 @@ var testsCases = []*testCase{ test: testBidirectionalAsyncPayments, }, { - // bob: outgoing our commit timeout - // carol: incoming their commit watch and see timeout - name: "test multi-hop htlc local force close immediate expiry", - test: testMultiHopHtlcLocalTimeout, - }, - { - // bob: outgoing watch and see, they sweep on chain - // carol: incoming our commit, know preimage - name: "test multi-hop htlc receiver chain claim", - test: testMultiHopReceiverChainClaim, - }, - { - // bob: outgoing our commit watch and see timeout - // carol: incoming their commit watch and see timeout - name: "test multi-hop local force close on-chain htlc timeout", - test: testMultiHopLocalForceCloseOnChainHtlcTimeout, - }, - { - // bob: outgoing their commit watch and see timeout - // carol: incoming our commit watch and see timeout - name: "test multi-hop remote force close on-chain htlc timeout", - test: testMultiHopRemoteForceCloseOnChainHtlcTimeout, - }, - { - // bob: outgoing our commit watch and see, they sweep on chain - // bob: incoming our commit watch and learn preimage - // carol: incoming their commit know preimage - name: "test multi-hop htlc local chain claim", - test: testMultiHopHtlcLocalChainClaim, - }, - { - // bob: outgoing their commit watch and see, they sweep on chain - // bob: incoming their commit watch and learn preimage - // carol: incoming our commit know preimage - name: "test multi-hop htlc remote chain claim", - test: testMultiHopHtlcRemoteChainClaim, + name: "test multi-hop htlc", + test: testMultiHopHtlcClaims, }, { name: "switch circuit persistence", From 393b4051dd2e82c7ba548e849098ae2f554f93f1 Mon Sep 17 00:00:00 2001 From: "Johan T. Halseth" Date: Wed, 4 Mar 2020 13:21:28 +0100 Subject: [PATCH 07/11] itest: spin up new nodes for multi-hop tests This will let us set their commitment type for the subtest. --- ...d_multi-hop_htlc_local_chain_claim_test.go | 86 ++++++++++++++----- .../lnd_multi-hop_htlc_local_timeout_test.go | 25 +++--- ...ulti-hop_htlc_receiver_chain_claim_test.go | 18 ++-- ..._multi-hop_htlc_remote_chain_claim_test.go | 20 +++-- ..._force_close_on_chain_htlc_timeout_test.go | 26 +++--- ..._force_close_on_chain_htlc_timeout_test.go | 22 ++--- 6 files changed, 124 insertions(+), 73 deletions(-) diff --git a/lntest/itest/lnd_multi-hop_htlc_local_chain_claim_test.go b/lntest/itest/lnd_multi-hop_htlc_local_chain_claim_test.go index e07102b1529..3f4ded6e96d 100644 --- a/lntest/itest/lnd_multi-hop_htlc_local_chain_claim_test.go +++ b/lntest/itest/lnd_multi-hop_htlc_local_chain_claim_test.go @@ -23,7 +23,8 @@ func testMultiHopHtlcClaims(net *lntest.NetworkHarness, t *harnessTest) { type testCase struct { name string - test func(net *lntest.NetworkHarness, t *harnessTest) + test func(net *lntest.NetworkHarness, t *harnessTest, alice, + bob *lntest.HarnessNode) } subTests := []testCase{ @@ -67,6 +68,25 @@ func testMultiHopHtlcClaims(net *lntest.NetworkHarness, t *harnessTest) { }, } + args := []string{} + alice, err := net.NewNode("Alice", args) + if err != nil { + t.Fatalf("unable to create new node: %v", err) + } + defer shutdownAndAssert(net, t, alice) + + bob, err := net.NewNode("Bob", args) + if err != nil { + t.Fatalf("unable to create new node: %v", err) + } + defer shutdownAndAssert(net, t, bob) + + ctxb := context.Background() + ctxt, _ := context.WithTimeout(ctxb, defaultTimeout) + if err := net.ConnectNodes(ctxt, alice, bob); err != nil { + t.Fatalf("unable to connect alice to bob: %v", err) + } + for _, subTest := range subTests { subTest := subTest @@ -74,7 +94,7 @@ func testMultiHopHtlcClaims(net *lntest.NetworkHarness, t *harnessTest) { success := t.t.Run(subTest.name, func(t *testing.T) { ht := newHarnessTest(t, net) - subTest.test(net, ht) + subTest.test(net, ht, alice, bob) }) if !success { return @@ -86,14 +106,16 @@ func testMultiHopHtlcClaims(net *lntest.NetworkHarness, t *harnessTest) { // we force close a channel with an incoming HTLC, and later find out the // preimage via the witness beacon, we properly settle the HTLC on-chain using // the HTLC success transaction in order to ensure we don't lose any funds. -func testMultiHopHtlcLocalChainClaim(net *lntest.NetworkHarness, t *harnessTest) { +func testMultiHopHtlcLocalChainClaim(net *lntest.NetworkHarness, t *harnessTest, + alice, bob *lntest.HarnessNode) { + ctxb := context.Background() // First, we'll create a three hop network: Alice -> Bob -> Carol, with // Carol refusing to actually settle or directly cancel any HTLC's // self. aliceChanPoint, bobChanPoint, carol := createThreeHopNetwork( - t, net, false, + t, net, alice, bob, false, ) // Clean up carol's node when the test finishes. @@ -124,7 +146,7 @@ func testMultiHopHtlcLocalChainClaim(net *lntest.NetworkHarness, t *harnessTest) ctx, cancel := context.WithCancel(ctxb) defer cancel() - alicePayStream, err := net.Alice.SendPayment(ctx) + alicePayStream, err := alice.SendPayment(ctx) if err != nil { t.Fatalf("unable to create payment stream for alice: %v", err) } @@ -138,7 +160,7 @@ func testMultiHopHtlcLocalChainClaim(net *lntest.NetworkHarness, t *harnessTest) // At this point, all 3 nodes should now have an active channel with // the created HTLC pending on all of them. var predErr error - nodes := []*lntest.HarnessNode{net.Alice, net.Bob, carol} + nodes := []*lntest.HarnessNode{alice, bob, carol} err = wait.Predicate(func() bool { predErr = assertActiveHtlcs(nodes, payHash[:]) if predErr != nil { @@ -159,7 +181,7 @@ func testMultiHopHtlcLocalChainClaim(net *lntest.NetworkHarness, t *harnessTest) // At this point, Bob decides that he wants to exit the channel // immediately, so he force closes his commitment transaction. ctxt, _ = context.WithTimeout(ctxb, channelCloseTimeout) - bobForceClose := closeChannelAndAssert(ctxt, t, net, net.Bob, + bobForceClose := closeChannelAndAssert(ctxt, t, net, bob, aliceChanPoint, true) // Alice will sweep her output immediately. @@ -170,7 +192,7 @@ func testMultiHopHtlcLocalChainClaim(net *lntest.NetworkHarness, t *harnessTest) } // Suspend Bob to force Carol to go to chain. - restartBob, err := net.SuspendNode(net.Bob) + restartBob, err := net.SuspendNode(bob) if err != nil { t.Fatalf("unable to suspend bob: %v", err) } @@ -262,7 +284,7 @@ func testMultiHopHtlcLocalChainClaim(net *lntest.NetworkHarness, t *harnessTest) // At this point we suspend Alice to make sure she'll handle the // on-chain settle after a restart. - restartAlice, err := net.SuspendNode(net.Alice) + restartAlice, err := net.SuspendNode(alice) if err != nil { t.Fatalf("unable to suspend alice: %v", err) } @@ -304,7 +326,7 @@ func testMultiHopHtlcLocalChainClaim(net *lntest.NetworkHarness, t *harnessTest) pendingChansRequest := &lnrpc.PendingChannelsRequest{} err = wait.Predicate(func() bool { ctxt, _ = context.WithTimeout(ctxb, defaultTimeout) - pendingChanResp, err := net.Bob.PendingChannels( + pendingChanResp, err := bob.PendingChannels( ctxt, pendingChansRequest, ) if err != nil { @@ -402,7 +424,7 @@ func testMultiHopHtlcLocalChainClaim(net *lntest.NetworkHarness, t *harnessTest) err = wait.Predicate(func() bool { ctxt, _ = context.WithTimeout(ctxb, defaultTimeout) - pendingChanResp, err := net.Bob.PendingChannels( + pendingChanResp, err := bob.PendingChannels( ctxt, pendingChansRequest, ) if err != nil { @@ -417,7 +439,7 @@ func testMultiHopHtlcLocalChainClaim(net *lntest.NetworkHarness, t *harnessTest) } req := &lnrpc.ListChannelsRequest{} ctxt, _ = context.WithTimeout(ctxb, defaultTimeout) - chanInfo, err := net.Bob.ListChannels(ctxt, req) + chanInfo, err := bob.ListChannels(ctxt, req) if err != nil { predErr = fmt.Errorf("unable to query for open "+ "channels: %v", err) @@ -476,7 +498,7 @@ func testMultiHopHtlcLocalChainClaim(net *lntest.NetworkHarness, t *harnessTest) // succeeded. ctxt, _ = context.WithTimeout(ctxt, defaultTimeout) err = checkPaymentStatus( - ctxt, net.Alice, preimage, lnrpc.Payment_SUCCEEDED, + ctxt, alice, preimage, lnrpc.Payment_SUCCEEDED, ) if err != nil { t.Fatalf(err.Error()) @@ -565,30 +587,48 @@ func checkPaymentStatus(ctxt context.Context, node *lntest.HarnessNode, } func createThreeHopNetwork(t *harnessTest, net *lntest.NetworkHarness, - carolHodl bool) (*lnrpc.ChannelPoint, *lnrpc.ChannelPoint, - *lntest.HarnessNode) { + alice, bob *lntest.HarnessNode, carolHodl bool) (*lnrpc.ChannelPoint, + *lnrpc.ChannelPoint, *lntest.HarnessNode) { ctxb := context.Background() + ctxt, _ := context.WithTimeout(ctxb, defaultTimeout) + err := net.EnsureConnected(ctxt, alice, bob) + if err != nil { + t.Fatalf("unable to connect peers: %v", err) + } + + ctxt, _ = context.WithTimeout(context.Background(), defaultTimeout) + err = net.SendCoins(ctxt, btcutil.SatoshiPerBitcoin, alice) + if err != nil { + t.Fatalf("unable to send coins to Alice: %v", err) + } + + ctxt, _ = context.WithTimeout(context.Background(), defaultTimeout) + err = net.SendCoins(ctxt, btcutil.SatoshiPerBitcoin, bob) + if err != nil { + t.Fatalf("unable to send coins to Bob: %v", err) + } + // We'll start the test by creating a channel between Alice and Bob, // which will act as the first leg for out multi-hop HTLC. const chanAmt = 1000000 - ctxt, _ := context.WithTimeout(ctxb, channelOpenTimeout) + ctxt, _ = context.WithTimeout(ctxb, channelOpenTimeout) aliceChanPoint := openChannelAndAssert( - ctxt, t, net, net.Alice, net.Bob, + ctxt, t, net, alice, bob, lntest.OpenChannelParams{ Amt: chanAmt, }, ) ctxt, _ = context.WithTimeout(ctxb, defaultTimeout) - err := net.Alice.WaitForNetworkChannelOpen(ctxt, aliceChanPoint) + err = alice.WaitForNetworkChannelOpen(ctxt, aliceChanPoint) if err != nil { t.Fatalf("alice didn't report channel: %v", err) } ctxt, _ = context.WithTimeout(ctxb, defaultTimeout) - err = net.Bob.WaitForNetworkChannelOpen(ctxt, aliceChanPoint) + err = bob.WaitForNetworkChannelOpen(ctxt, aliceChanPoint) if err != nil { t.Fatalf("bob didn't report channel: %v", err) } @@ -605,7 +645,7 @@ func createThreeHopNetwork(t *harnessTest, net *lntest.NetworkHarness, t.Fatalf("unable to create new node: %v", err) } ctxt, _ = context.WithTimeout(ctxb, defaultTimeout) - if err := net.ConnectNodes(ctxt, net.Bob, carol); err != nil { + if err := net.ConnectNodes(ctxt, bob, carol); err != nil { t.Fatalf("unable to connect bob to carol: %v", err) } @@ -613,13 +653,13 @@ func createThreeHopNetwork(t *harnessTest, net *lntest.NetworkHarness, // open, our topology looks like: A -> B -> C. ctxt, _ = context.WithTimeout(ctxb, channelOpenTimeout) bobChanPoint := openChannelAndAssert( - ctxt, t, net, net.Bob, carol, + ctxt, t, net, bob, carol, lntest.OpenChannelParams{ Amt: chanAmt, }, ) ctxt, _ = context.WithTimeout(ctxb, defaultTimeout) - err = net.Bob.WaitForNetworkChannelOpen(ctxt, bobChanPoint) + err = bob.WaitForNetworkChannelOpen(ctxt, bobChanPoint) if err != nil { t.Fatalf("alice didn't report channel: %v", err) } @@ -629,7 +669,7 @@ func createThreeHopNetwork(t *harnessTest, net *lntest.NetworkHarness, t.Fatalf("bob didn't report channel: %v", err) } ctxt, _ = context.WithTimeout(ctxb, defaultTimeout) - err = net.Alice.WaitForNetworkChannelOpen(ctxt, bobChanPoint) + err = alice.WaitForNetworkChannelOpen(ctxt, bobChanPoint) if err != nil { t.Fatalf("bob didn't report channel: %v", err) } diff --git a/lntest/itest/lnd_multi-hop_htlc_local_timeout_test.go b/lntest/itest/lnd_multi-hop_htlc_local_timeout_test.go index 71c66716672..8d6e8fa79c7 100644 --- a/lntest/itest/lnd_multi-hop_htlc_local_timeout_test.go +++ b/lntest/itest/lnd_multi-hop_htlc_local_timeout_test.go @@ -21,14 +21,17 @@ import ( // it using the HTLC timeout transaction. Any dust HTLC's should be immediately // canceled backwards. Once the timeout has been reached, then we should sweep // it on-chain, and cancel the HTLC backwards. -func testMultiHopHtlcLocalTimeout(net *lntest.NetworkHarness, t *harnessTest) { +func testMultiHopHtlcLocalTimeout(net *lntest.NetworkHarness, t *harnessTest, + alice, bob *lntest.HarnessNode) { + ctxb := context.Background() // First, we'll create a three hop network: Alice -> Bob -> Carol, with // Carol refusing to actually settle or directly cancel any HTLC's // self. - aliceChanPoint, bobChanPoint, carol := - createThreeHopNetwork(t, net, true) + aliceChanPoint, bobChanPoint, carol := createThreeHopNetwork( + t, net, alice, bob, true, + ) // Clean up carol's node when the test finishes. defer shutdownAndAssert(net, t, carol) @@ -47,7 +50,7 @@ func testMultiHopHtlcLocalTimeout(net *lntest.NetworkHarness, t *harnessTest) { ctx, cancel := context.WithCancel(ctxb) defer cancel() - alicePayStream, err := net.Alice.SendPayment(ctx) + alicePayStream, err := alice.SendPayment(ctx) if err != nil { t.Fatalf("unable to create payment stream for alice: %v", err) } @@ -79,7 +82,7 @@ func testMultiHopHtlcLocalTimeout(net *lntest.NetworkHarness, t *harnessTest) { // Verify that all nodes in the path now have two HTLC's with the // proper parameters. var predErr error - nodes := []*lntest.HarnessNode{net.Alice, net.Bob, carol} + nodes := []*lntest.HarnessNode{alice, bob, carol} err = wait.Predicate(func() bool { predErr = assertActiveHtlcs(nodes, dustPayHash, payHash) if predErr != nil { @@ -125,7 +128,7 @@ func testMultiHopHtlcLocalTimeout(net *lntest.NetworkHarness, t *harnessTest) { // At this point, Bob should have canceled backwards the dust HTLC // that we sent earlier. This means Alice should now only have a single // HTLC on her channel. - nodes = []*lntest.HarnessNode{net.Alice} + nodes = []*lntest.HarnessNode{alice} err = wait.Predicate(func() bool { predErr = assertActiveHtlcs(nodes, payHash) if predErr != nil { @@ -160,7 +163,7 @@ func testMultiHopHtlcLocalTimeout(net *lntest.NetworkHarness, t *harnessTest) { // output pending. pendingChansRequest := &lnrpc.PendingChannelsRequest{} ctxt, _ := context.WithTimeout(ctxb, defaultTimeout) - pendingChanResp, err := net.Bob.PendingChannels(ctxt, pendingChansRequest) + pendingChanResp, err := bob.PendingChannels(ctxt, pendingChansRequest) if err != nil { t.Fatalf("unable to query for pending channels: %v", err) } @@ -191,7 +194,7 @@ func testMultiHopHtlcLocalTimeout(net *lntest.NetworkHarness, t *harnessTest) { // The block should have confirmed Bob's HTLC timeout transaction. // Therefore, at this point, there should be no active HTLC's on the // commitment transaction from Alice -> Bob. - nodes = []*lntest.HarnessNode{net.Alice} + nodes = []*lntest.HarnessNode{alice} err = wait.Predicate(func() bool { predErr = assertNumActiveHtlcs(nodes, 0) if predErr != nil { @@ -206,7 +209,7 @@ func testMultiHopHtlcLocalTimeout(net *lntest.NetworkHarness, t *harnessTest) { // At this point, Bob should show that the pending HTLC has advanced to // the second stage and is to be swept. ctxt, _ = context.WithTimeout(ctxb, defaultTimeout) - pendingChanResp, err = net.Bob.PendingChannels(ctxt, pendingChansRequest) + pendingChanResp, err = bob.PendingChannels(ctxt, pendingChansRequest) if err != nil { t.Fatalf("unable to query for pending channels: %v", err) } @@ -225,7 +228,7 @@ func testMultiHopHtlcLocalTimeout(net *lntest.NetworkHarness, t *harnessTest) { // no longer has any pending channels. err = wait.Predicate(func() bool { ctxt, _ = context.WithTimeout(ctxb, defaultTimeout) - pendingChanResp, err = net.Bob.PendingChannels(ctxt, pendingChansRequest) + pendingChanResp, err = bob.PendingChannels(ctxt, pendingChansRequest) if err != nil { predErr = fmt.Errorf("unable to query for pending "+ "channels: %v", err) @@ -246,5 +249,5 @@ func testMultiHopHtlcLocalTimeout(net *lntest.NetworkHarness, t *harnessTest) { } ctxt, _ = context.WithTimeout(ctxb, channelCloseTimeout) - closeChannelAndAssert(ctxt, t, net, net.Alice, aliceChanPoint, false) + closeChannelAndAssert(ctxt, t, net, alice, aliceChanPoint, false) } diff --git a/lntest/itest/lnd_multi-hop_htlc_receiver_chain_claim_test.go b/lntest/itest/lnd_multi-hop_htlc_receiver_chain_claim_test.go index 0df39250461..845d7998752 100644 --- a/lntest/itest/lnd_multi-hop_htlc_receiver_chain_claim_test.go +++ b/lntest/itest/lnd_multi-hop_htlc_receiver_chain_claim_test.go @@ -24,14 +24,16 @@ import ( // transaction. In this scenario, the node that sent the outgoing HTLC should // extract the preimage from the sweep transaction, and finish settling the // HTLC backwards into the route. -func testMultiHopReceiverChainClaim(net *lntest.NetworkHarness, t *harnessTest) { +func testMultiHopReceiverChainClaim(net *lntest.NetworkHarness, t *harnessTest, + alice, bob *lntest.HarnessNode) { + ctxb := context.Background() // First, we'll create a three hop network: Alice -> Bob -> Carol, with // Carol refusing to actually settle or directly cancel any HTLC's // self. aliceChanPoint, bobChanPoint, carol := createThreeHopNetwork( - t, net, false, + t, net, alice, bob, false, ) // Clean up carol's node when the test finishes. @@ -62,7 +64,7 @@ func testMultiHopReceiverChainClaim(net *lntest.NetworkHarness, t *harnessTest) ctx, cancel := context.WithCancel(ctxb) defer cancel() - alicePayStream, err := net.Alice.SendPayment(ctx) + alicePayStream, err := alice.SendPayment(ctx) if err != nil { t.Fatalf("unable to create payment stream for alice: %v", err) } @@ -76,7 +78,7 @@ func testMultiHopReceiverChainClaim(net *lntest.NetworkHarness, t *harnessTest) // At this point, all 3 nodes should now have an active channel with // the created HTLC pending on all of them. var predErr error - nodes := []*lntest.HarnessNode{net.Alice, net.Bob, carol} + nodes := []*lntest.HarnessNode{alice, bob, carol} err = wait.Predicate(func() bool { predErr = assertActiveHtlcs(nodes, payHash[:]) if predErr != nil { @@ -94,7 +96,7 @@ func testMultiHopReceiverChainClaim(net *lntest.NetworkHarness, t *harnessTest) // hop logic. waitForInvoiceAccepted(t, carol, payHash) - restartBob, err := net.SuspendNode(net.Bob) + restartBob, err := net.SuspendNode(bob) if err != nil { t.Fatalf("unable to suspend bob: %v", err) } @@ -231,7 +233,7 @@ func testMultiHopReceiverChainClaim(net *lntest.NetworkHarness, t *harnessTest) // Once the second-level transaction confirmed, Bob should have // extracted the preimage from the chain, and sent it back to Alice, // clearing the HTLC off-chain. - nodes = []*lntest.HarnessNode{net.Alice} + nodes = []*lntest.HarnessNode{alice} err = wait.Predicate(func() bool { predErr = assertNumActiveHtlcs(nodes, 0) if predErr != nil { @@ -303,7 +305,7 @@ func testMultiHopReceiverChainClaim(net *lntest.NetworkHarness, t *harnessTest) // succeeded. ctxt, _ = context.WithTimeout(ctxt, defaultTimeout) err = checkPaymentStatus( - ctxt, net.Alice, preimage, lnrpc.Payment_SUCCEEDED, + ctxt, alice, preimage, lnrpc.Payment_SUCCEEDED, ) if err != nil { t.Fatalf(err.Error()) @@ -312,5 +314,5 @@ func testMultiHopReceiverChainClaim(net *lntest.NetworkHarness, t *harnessTest) // We'll close out the channel between Alice and Bob, then shutdown // carol to conclude the test. ctxt, _ = context.WithTimeout(ctxb, channelCloseTimeout) - closeChannelAndAssert(ctxt, t, net, net.Alice, aliceChanPoint, false) + closeChannelAndAssert(ctxt, t, net, alice, aliceChanPoint, false) } diff --git a/lntest/itest/lnd_multi-hop_htlc_remote_chain_claim_test.go b/lntest/itest/lnd_multi-hop_htlc_remote_chain_claim_test.go index 8c3a1f285f8..1a5db70b784 100644 --- a/lntest/itest/lnd_multi-hop_htlc_remote_chain_claim_test.go +++ b/lntest/itest/lnd_multi-hop_htlc_remote_chain_claim_test.go @@ -22,14 +22,16 @@ import ( // we found out the preimage via the witness beacon, we properly settle the // HTLC directly on-chain using the preimage in order to ensure that we don't // lose any funds. -func testMultiHopHtlcRemoteChainClaim(net *lntest.NetworkHarness, t *harnessTest) { +func testMultiHopHtlcRemoteChainClaim(net *lntest.NetworkHarness, t *harnessTest, + alice, bob *lntest.HarnessNode) { + ctxb := context.Background() // First, we'll create a three hop network: Alice -> Bob -> Carol, with // Carol refusing to actually settle or directly cancel any HTLC's // self. aliceChanPoint, bobChanPoint, carol := createThreeHopNetwork( - t, net, false, + t, net, alice, bob, false, ) // Clean up carol's node when the test finishes. @@ -59,7 +61,7 @@ func testMultiHopHtlcRemoteChainClaim(net *lntest.NetworkHarness, t *harnessTest ctx, cancel := context.WithCancel(ctxb) defer cancel() - alicePayStream, err := net.Alice.SendPayment(ctx) + alicePayStream, err := alice.SendPayment(ctx) if err != nil { t.Fatalf("unable to create payment stream for alice: %v", err) } @@ -73,7 +75,7 @@ func testMultiHopHtlcRemoteChainClaim(net *lntest.NetworkHarness, t *harnessTest // At this point, all 3 nodes should now have an active channel with // the created HTLC pending on all of them. var predErr error - nodes := []*lntest.HarnessNode{net.Alice, net.Bob, carol} + nodes := []*lntest.HarnessNode{alice, bob, carol} err = wait.Predicate(func() bool { predErr = assertActiveHtlcs(nodes, payHash[:]) if predErr != nil { @@ -95,12 +97,12 @@ func testMultiHopHtlcRemoteChainClaim(net *lntest.NetworkHarness, t *harnessTest // immediately force close the channel by broadcast her commitment // transaction. ctxt, _ = context.WithTimeout(ctxb, channelCloseTimeout) - aliceForceClose := closeChannelAndAssert(ctxt, t, net, net.Alice, + aliceForceClose := closeChannelAndAssert(ctxt, t, 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) + err = waitForChannelPendingForceClose(ctxt, alice, aliceChanPoint) if err != nil { t.Fatalf("channel not pending force close: %v", err) } @@ -119,7 +121,7 @@ func testMultiHopHtlcRemoteChainClaim(net *lntest.NetworkHarness, t *harnessTest } // Suspend bob, so Carol is forced to go on chain. - restartBob, err := net.SuspendNode(net.Bob) + restartBob, err := net.SuspendNode(bob) if err != nil { t.Fatalf("unable to suspend bob: %v", err) } @@ -256,7 +258,7 @@ func testMultiHopHtlcRemoteChainClaim(net *lntest.NetworkHarness, t *harnessTest pendingChansRequest := &lnrpc.PendingChannelsRequest{} err = wait.Predicate(func() bool { ctxt, _ = context.WithTimeout(ctxb, defaultTimeout) - pendingChanResp, err := net.Bob.PendingChannels( + pendingChanResp, err := bob.PendingChannels( ctxt, pendingChansRequest, ) if err != nil { @@ -338,7 +340,7 @@ func testMultiHopHtlcRemoteChainClaim(net *lntest.NetworkHarness, t *harnessTest // succeeded. ctxt, _ = context.WithTimeout(ctxt, defaultTimeout) err = checkPaymentStatus( - ctxt, net.Alice, preimage, lnrpc.Payment_SUCCEEDED, + ctxt, alice, preimage, lnrpc.Payment_SUCCEEDED, ) if err != nil { t.Fatalf(err.Error()) diff --git a/lntest/itest/lnd_multi-hop_local_force_close_on_chain_htlc_timeout_test.go b/lntest/itest/lnd_multi-hop_local_force_close_on_chain_htlc_timeout_test.go index 960ff17d4d1..b6a8bcd9763 100644 --- a/lntest/itest/lnd_multi-hop_local_force_close_on_chain_htlc_timeout_test.go +++ b/lntest/itest/lnd_multi-hop_local_force_close_on_chain_htlc_timeout_test.go @@ -20,14 +20,16 @@ import ( // that's timed out. At this point, the node should timeout the HTLC using the // HTLC timeout transaction, then cancel it backwards as normal. func testMultiHopLocalForceCloseOnChainHtlcTimeout(net *lntest.NetworkHarness, - t *harnessTest) { + t *harnessTest, alice, bob *lntest.HarnessNode) { + ctxb := context.Background() // First, we'll create a three hop network: Alice -> Bob -> Carol, with // Carol refusing to actually settle or directly cancel any HTLC's // self. - aliceChanPoint, bobChanPoint, carol := - createThreeHopNetwork(t, net, true) + aliceChanPoint, bobChanPoint, carol := createThreeHopNetwork( + t, net, alice, bob, true, + ) // Clean up carol's node when the test finishes. defer shutdownAndAssert(net, t, carol) @@ -42,7 +44,7 @@ func testMultiHopLocalForceCloseOnChainHtlcTimeout(net *lntest.NetworkHarness, ctx, cancel := context.WithCancel(ctxb) defer cancel() - alicePayStream, err := net.Alice.SendPayment(ctx) + alicePayStream, err := alice.SendPayment(ctx) if err != nil { t.Fatalf("unable to create payment stream for alice: %v", err) } @@ -63,7 +65,7 @@ func testMultiHopLocalForceCloseOnChainHtlcTimeout(net *lntest.NetworkHarness, // Once the HTLC has cleared, all channels in our mini network should // have the it locked in. var predErr error - nodes := []*lntest.HarnessNode{net.Alice, net.Bob, carol} + nodes := []*lntest.HarnessNode{alice, bob, carol} err = wait.Predicate(func() bool { predErr = assertActiveHtlcs(nodes, payHash) if predErr != nil { @@ -80,14 +82,14 @@ func testMultiHopLocalForceCloseOnChainHtlcTimeout(net *lntest.NetworkHarness, // force close the Bob -> Carol channel. This should trigger contract // resolution mode for both of them. ctxt, _ := context.WithTimeout(ctxb, channelCloseTimeout) - closeChannelAndAssert(ctxt, t, net, net.Bob, bobChanPoint, true) + closeChannelAndAssert(ctxt, t, net, bob, bobChanPoint, true) // At this point, Bob should have a pending force close channel as he // just went to chain. pendingChansRequest := &lnrpc.PendingChannelsRequest{} err = wait.Predicate(func() bool { ctxt, _ = context.WithTimeout(ctxb, defaultTimeout) - pendingChanResp, err := net.Bob.PendingChannels(ctxt, + pendingChanResp, err := bob.PendingChannels(ctxt, pendingChansRequest) if err != nil { predErr = fmt.Errorf("unable to query for pending "+ @@ -136,7 +138,7 @@ func testMultiHopLocalForceCloseOnChainHtlcTimeout(net *lntest.NetworkHarness, // that's now in stage one. err = wait.Predicate(func() bool { ctxt, _ = context.WithTimeout(ctxb, defaultTimeout) - pendingChanResp, err := net.Bob.PendingChannels( + pendingChanResp, err := bob.PendingChannels( ctxt, pendingChansRequest, ) if err != nil { @@ -183,7 +185,7 @@ func testMultiHopLocalForceCloseOnChainHtlcTimeout(net *lntest.NetworkHarness, // With the second layer timeout transaction confirmed, Bob should have // canceled backwards the HTLC that carol sent. - nodes = []*lntest.HarnessNode{net.Alice} + nodes = []*lntest.HarnessNode{alice} err = wait.Predicate(func() bool { predErr = assertNumActiveHtlcs(nodes, 0) if predErr != nil { @@ -199,7 +201,7 @@ func testMultiHopLocalForceCloseOnChainHtlcTimeout(net *lntest.NetworkHarness, // second stage. err = wait.Predicate(func() bool { ctxt, _ = context.WithTimeout(ctxb, defaultTimeout) - pendingChanResp, err := net.Bob.PendingChannels( + pendingChanResp, err := bob.PendingChannels( ctxt, pendingChansRequest, ) if err != nil { @@ -253,7 +255,7 @@ func testMultiHopLocalForceCloseOnChainHtlcTimeout(net *lntest.NetworkHarness, // close. err = wait.Predicate(func() bool { ctxt, _ = context.WithTimeout(ctxb, defaultTimeout) - pendingChanResp, err := net.Bob.PendingChannels( + pendingChanResp, err := bob.PendingChannels( ctxt, pendingChansRequest, ) if err != nil { @@ -274,5 +276,5 @@ func testMultiHopLocalForceCloseOnChainHtlcTimeout(net *lntest.NetworkHarness, } ctxt, _ = context.WithTimeout(ctxb, channelCloseTimeout) - closeChannelAndAssert(ctxt, t, net, net.Alice, aliceChanPoint, false) + closeChannelAndAssert(ctxt, t, net, alice, aliceChanPoint, false) } diff --git a/lntest/itest/lnd_multi-hop_remote_force_close_on_chain_htlc_timeout_test.go b/lntest/itest/lnd_multi-hop_remote_force_close_on_chain_htlc_timeout_test.go index 6c6d6eab65c..7d9cfa56bf0 100644 --- a/lntest/itest/lnd_multi-hop_remote_force_close_on_chain_htlc_timeout_test.go +++ b/lntest/itest/lnd_multi-hop_remote_force_close_on_chain_htlc_timeout_test.go @@ -20,14 +20,16 @@ import ( // transaction once the timeout has expired. Once we sweep the transaction, we // should also cancel back the initial HTLC. func testMultiHopRemoteForceCloseOnChainHtlcTimeout(net *lntest.NetworkHarness, - t *harnessTest) { + t *harnessTest, alice, bob *lntest.HarnessNode) { + ctxb := context.Background() // First, we'll create a three hop network: Alice -> Bob -> Carol, with // Carol refusing to actually settle or directly cancel any HTLC's // self. - aliceChanPoint, bobChanPoint, carol := - createThreeHopNetwork(t, net, true) + aliceChanPoint, bobChanPoint, carol := createThreeHopNetwork( + t, net, alice, bob, true, + ) // Clean up carol's node when the test finishes. defer shutdownAndAssert(net, t, carol) @@ -43,7 +45,7 @@ func testMultiHopRemoteForceCloseOnChainHtlcTimeout(net *lntest.NetworkHarness, ctx, cancel := context.WithCancel(ctxb) defer cancel() - alicePayStream, err := net.Alice.SendPayment(ctx) + alicePayStream, err := alice.SendPayment(ctx) if err != nil { t.Fatalf("unable to create payment stream for alice: %v", err) } @@ -64,7 +66,7 @@ func testMultiHopRemoteForceCloseOnChainHtlcTimeout(net *lntest.NetworkHarness, // Once the HTLC has cleared, all the nodes in our mini network should // show that the HTLC has been locked in. var predErr error - nodes := []*lntest.HarnessNode{net.Alice, net.Bob, carol} + nodes := []*lntest.HarnessNode{alice, bob, carol} err = wait.Predicate(func() bool { predErr = assertActiveHtlcs(nodes, payHash) if predErr != nil { @@ -88,7 +90,7 @@ func testMultiHopRemoteForceCloseOnChainHtlcTimeout(net *lntest.NetworkHarness, pendingChansRequest := &lnrpc.PendingChannelsRequest{} err = wait.Predicate(func() bool { ctxt, _ = context.WithTimeout(ctxb, defaultTimeout) - pendingChanResp, err := net.Bob.PendingChannels( + pendingChanResp, err := bob.PendingChannels( ctxt, pendingChansRequest, ) if err != nil { @@ -128,7 +130,7 @@ func testMultiHopRemoteForceCloseOnChainHtlcTimeout(net *lntest.NetworkHarness, // first stage since this is a direct HTLC. err = wait.Predicate(func() bool { ctxt, _ = context.WithTimeout(ctxb, defaultTimeout) - pendingChanResp, err := net.Bob.PendingChannels( + pendingChanResp, err := bob.PendingChannels( ctxt, pendingChansRequest, ) if err != nil { @@ -191,7 +193,7 @@ func testMultiHopRemoteForceCloseOnChainHtlcTimeout(net *lntest.NetworkHarness, // Now that the sweeping transaction has been confirmed, Bob should // cancel back that HTLC. As a result, Alice should not know of any // active HTLC's. - nodes = []*lntest.HarnessNode{net.Alice} + nodes = []*lntest.HarnessNode{alice} err = wait.Predicate(func() bool { predErr = assertNumActiveHtlcs(nodes, 0) if predErr != nil { @@ -208,7 +210,7 @@ func testMultiHopRemoteForceCloseOnChainHtlcTimeout(net *lntest.NetworkHarness, // he should show no additional pending transactions. err = wait.Predicate(func() bool { ctxt, _ = context.WithTimeout(ctxb, defaultTimeout) - pendingChanResp, err := net.Bob.PendingChannels( + pendingChanResp, err := bob.PendingChannels( ctxt, pendingChansRequest, ) if err != nil { @@ -232,5 +234,5 @@ func testMultiHopRemoteForceCloseOnChainHtlcTimeout(net *lntest.NetworkHarness, // and then shutting down the new node we created as its no longer // needed. ctxt, _ = context.WithTimeout(ctxb, channelCloseTimeout) - closeChannelAndAssert(ctxt, t, net, net.Alice, aliceChanPoint, false) + closeChannelAndAssert(ctxt, t, net, alice, aliceChanPoint, false) } From 8c84e502acd656311d88459f5fba37a3e666c6b0 Mon Sep 17 00:00:00 2001 From: "Johan T. Halseth" Date: Wed, 4 Mar 2020 13:21:28 +0100 Subject: [PATCH 08/11] itest: run multi-hop claim tests for all commit types --- ...d_multi-hop_htlc_local_chain_claim_test.go | 67 ++++++++++++------- .../lnd_multi-hop_htlc_local_timeout_test.go | 4 +- ...ulti-hop_htlc_receiver_chain_claim_test.go | 4 +- ..._multi-hop_htlc_remote_chain_claim_test.go | 4 +- ..._force_close_on_chain_htlc_timeout_test.go | 4 +- ..._force_close_on_chain_htlc_timeout_test.go | 4 +- 6 files changed, 51 insertions(+), 36 deletions(-) diff --git a/lntest/itest/lnd_multi-hop_htlc_local_chain_claim_test.go b/lntest/itest/lnd_multi-hop_htlc_local_chain_claim_test.go index 3f4ded6e96d..91fccbbc877 100644 --- a/lntest/itest/lnd_multi-hop_htlc_local_chain_claim_test.go +++ b/lntest/itest/lnd_multi-hop_htlc_local_chain_claim_test.go @@ -24,7 +24,7 @@ func testMultiHopHtlcClaims(net *lntest.NetworkHarness, t *harnessTest) { type testCase struct { name string test func(net *lntest.NetworkHarness, t *harnessTest, alice, - bob *lntest.HarnessNode) + bob *lntest.HarnessNode, c commitType) } subTests := []testCase{ @@ -68,33 +68,48 @@ func testMultiHopHtlcClaims(net *lntest.NetworkHarness, t *harnessTest) { }, } - args := []string{} - alice, err := net.NewNode("Alice", args) - if err != nil { - t.Fatalf("unable to create new node: %v", err) + allTypes := []commitType{ + commitTypeLegacy, + commitTypeTweakless, } - defer shutdownAndAssert(net, t, alice) - bob, err := net.NewNode("Bob", args) - if err != nil { - t.Fatalf("unable to create new node: %v", err) - } - defer shutdownAndAssert(net, t, bob) + for _, commitType := range allTypes { + testName := fmt.Sprintf("committype=%v", commitType.String()) - ctxb := context.Background() - ctxt, _ := context.WithTimeout(ctxb, defaultTimeout) - if err := net.ConnectNodes(ctxt, alice, bob); err != nil { - t.Fatalf("unable to connect alice to bob: %v", err) - } + success := t.t.Run(testName, func(t *testing.T) { + ht := newHarnessTest(t, net) - for _, subTest := range subTests { + args := commitType.Args() + alice, err := net.NewNode("Alice", args) + if err != nil { + t.Fatalf("unable to create new node: %v", err) + } + defer shutdownAndAssert(net, ht, alice) - subTest := subTest + bob, err := net.NewNode("Bob", args) + if err != nil { + t.Fatalf("unable to create new node: %v", err) + } + defer shutdownAndAssert(net, ht, bob) - success := t.t.Run(subTest.name, func(t *testing.T) { - ht := newHarnessTest(t, net) + ctxb := context.Background() + ctxt, _ := context.WithTimeout(ctxb, defaultTimeout) + if err := net.ConnectNodes(ctxt, alice, bob); err != nil { + t.Fatalf("unable to connect alice to bob: %v", err) + } + + for _, subTest := range subTests { + subTest := subTest - subTest.test(net, ht, alice, bob) + success := ht.t.Run(subTest.name, func(t *testing.T) { + ht := newHarnessTest(t, net) + + subTest.test(net, ht, alice, bob, commitType) + }) + if !success { + return + } + } }) if !success { return @@ -107,7 +122,7 @@ func testMultiHopHtlcClaims(net *lntest.NetworkHarness, t *harnessTest) { // preimage via the witness beacon, we properly settle the HTLC on-chain using // the HTLC success transaction in order to ensure we don't lose any funds. func testMultiHopHtlcLocalChainClaim(net *lntest.NetworkHarness, t *harnessTest, - alice, bob *lntest.HarnessNode) { + alice, bob *lntest.HarnessNode, c commitType) { ctxb := context.Background() @@ -115,7 +130,7 @@ func testMultiHopHtlcLocalChainClaim(net *lntest.NetworkHarness, t *harnessTest, // Carol refusing to actually settle or directly cancel any HTLC's // self. aliceChanPoint, bobChanPoint, carol := createThreeHopNetwork( - t, net, alice, bob, false, + t, net, alice, bob, false, c, ) // Clean up carol's node when the test finishes. @@ -587,8 +602,8 @@ func checkPaymentStatus(ctxt context.Context, node *lntest.HarnessNode, } func createThreeHopNetwork(t *harnessTest, net *lntest.NetworkHarness, - alice, bob *lntest.HarnessNode, carolHodl bool) (*lnrpc.ChannelPoint, - *lnrpc.ChannelPoint, *lntest.HarnessNode) { + alice, bob *lntest.HarnessNode, carolHodl bool, c commitType) ( + *lnrpc.ChannelPoint, *lnrpc.ChannelPoint, *lntest.HarnessNode) { ctxb := context.Background() @@ -636,7 +651,7 @@ func createThreeHopNetwork(t *harnessTest, net *lntest.NetworkHarness, // Next, we'll create a new node "carol" and have Bob connect to her. If // the carolHodl flag is set, we'll make carol always hold onto the // HTLC, this way it'll force Bob to go to chain to resolve the HTLC. - carolFlags := []string{} + carolFlags := c.Args() if carolHodl { carolFlags = append(carolFlags, "--hodl.exit-settle") } diff --git a/lntest/itest/lnd_multi-hop_htlc_local_timeout_test.go b/lntest/itest/lnd_multi-hop_htlc_local_timeout_test.go index 8d6e8fa79c7..98b66bda0a9 100644 --- a/lntest/itest/lnd_multi-hop_htlc_local_timeout_test.go +++ b/lntest/itest/lnd_multi-hop_htlc_local_timeout_test.go @@ -22,7 +22,7 @@ import ( // canceled backwards. Once the timeout has been reached, then we should sweep // it on-chain, and cancel the HTLC backwards. func testMultiHopHtlcLocalTimeout(net *lntest.NetworkHarness, t *harnessTest, - alice, bob *lntest.HarnessNode) { + alice, bob *lntest.HarnessNode, c commitType) { ctxb := context.Background() @@ -30,7 +30,7 @@ func testMultiHopHtlcLocalTimeout(net *lntest.NetworkHarness, t *harnessTest, // Carol refusing to actually settle or directly cancel any HTLC's // self. aliceChanPoint, bobChanPoint, carol := createThreeHopNetwork( - t, net, alice, bob, true, + t, net, alice, bob, true, c, ) // Clean up carol's node when the test finishes. diff --git a/lntest/itest/lnd_multi-hop_htlc_receiver_chain_claim_test.go b/lntest/itest/lnd_multi-hop_htlc_receiver_chain_claim_test.go index 845d7998752..e11b2ed7fa5 100644 --- a/lntest/itest/lnd_multi-hop_htlc_receiver_chain_claim_test.go +++ b/lntest/itest/lnd_multi-hop_htlc_receiver_chain_claim_test.go @@ -25,7 +25,7 @@ import ( // extract the preimage from the sweep transaction, and finish settling the // HTLC backwards into the route. func testMultiHopReceiverChainClaim(net *lntest.NetworkHarness, t *harnessTest, - alice, bob *lntest.HarnessNode) { + alice, bob *lntest.HarnessNode, c commitType) { ctxb := context.Background() @@ -33,7 +33,7 @@ func testMultiHopReceiverChainClaim(net *lntest.NetworkHarness, t *harnessTest, // Carol refusing to actually settle or directly cancel any HTLC's // self. aliceChanPoint, bobChanPoint, carol := createThreeHopNetwork( - t, net, alice, bob, false, + t, net, alice, bob, false, c, ) // Clean up carol's node when the test finishes. diff --git a/lntest/itest/lnd_multi-hop_htlc_remote_chain_claim_test.go b/lntest/itest/lnd_multi-hop_htlc_remote_chain_claim_test.go index 1a5db70b784..257b7b9703c 100644 --- a/lntest/itest/lnd_multi-hop_htlc_remote_chain_claim_test.go +++ b/lntest/itest/lnd_multi-hop_htlc_remote_chain_claim_test.go @@ -23,7 +23,7 @@ import ( // HTLC directly on-chain using the preimage in order to ensure that we don't // lose any funds. func testMultiHopHtlcRemoteChainClaim(net *lntest.NetworkHarness, t *harnessTest, - alice, bob *lntest.HarnessNode) { + alice, bob *lntest.HarnessNode, c commitType) { ctxb := context.Background() @@ -31,7 +31,7 @@ func testMultiHopHtlcRemoteChainClaim(net *lntest.NetworkHarness, t *harnessTest // Carol refusing to actually settle or directly cancel any HTLC's // self. aliceChanPoint, bobChanPoint, carol := createThreeHopNetwork( - t, net, alice, bob, false, + t, net, alice, bob, false, c, ) // Clean up carol's node when the test finishes. diff --git a/lntest/itest/lnd_multi-hop_local_force_close_on_chain_htlc_timeout_test.go b/lntest/itest/lnd_multi-hop_local_force_close_on_chain_htlc_timeout_test.go index b6a8bcd9763..b60cfda4bef 100644 --- a/lntest/itest/lnd_multi-hop_local_force_close_on_chain_htlc_timeout_test.go +++ b/lntest/itest/lnd_multi-hop_local_force_close_on_chain_htlc_timeout_test.go @@ -20,7 +20,7 @@ import ( // that's timed out. At this point, the node should timeout the HTLC using the // HTLC timeout transaction, then cancel it backwards as normal. func testMultiHopLocalForceCloseOnChainHtlcTimeout(net *lntest.NetworkHarness, - t *harnessTest, alice, bob *lntest.HarnessNode) { + t *harnessTest, alice, bob *lntest.HarnessNode, c commitType) { ctxb := context.Background() @@ -28,7 +28,7 @@ func testMultiHopLocalForceCloseOnChainHtlcTimeout(net *lntest.NetworkHarness, // Carol refusing to actually settle or directly cancel any HTLC's // self. aliceChanPoint, bobChanPoint, carol := createThreeHopNetwork( - t, net, alice, bob, true, + t, net, alice, bob, true, c, ) // Clean up carol's node when the test finishes. diff --git a/lntest/itest/lnd_multi-hop_remote_force_close_on_chain_htlc_timeout_test.go b/lntest/itest/lnd_multi-hop_remote_force_close_on_chain_htlc_timeout_test.go index 7d9cfa56bf0..2f70fe3cf4b 100644 --- a/lntest/itest/lnd_multi-hop_remote_force_close_on_chain_htlc_timeout_test.go +++ b/lntest/itest/lnd_multi-hop_remote_force_close_on_chain_htlc_timeout_test.go @@ -20,7 +20,7 @@ import ( // transaction once the timeout has expired. Once we sweep the transaction, we // should also cancel back the initial HTLC. func testMultiHopRemoteForceCloseOnChainHtlcTimeout(net *lntest.NetworkHarness, - t *harnessTest, alice, bob *lntest.HarnessNode) { + t *harnessTest, alice, bob *lntest.HarnessNode, c commitType) { ctxb := context.Background() @@ -28,7 +28,7 @@ func testMultiHopRemoteForceCloseOnChainHtlcTimeout(net *lntest.NetworkHarness, // Carol refusing to actually settle or directly cancel any HTLC's // self. aliceChanPoint, bobChanPoint, carol := createThreeHopNetwork( - t, net, alice, bob, true, + t, net, alice, bob, true, c, ) // Clean up carol's node when the test finishes. From 20c9ad93dcc73a222986412fa4bf182b0de64bdf Mon Sep 17 00:00:00 2001 From: "Johan T. Halseth" Date: Wed, 4 Mar 2020 13:21:28 +0100 Subject: [PATCH 09/11] itest: calcStaticFee based on commit type --- lntest/itest/lnd_test.go | 114 +++++++++++++++++++++++++++++++-------- 1 file changed, 92 insertions(+), 22 deletions(-) diff --git a/lntest/itest/lnd_test.go b/lntest/itest/lnd_test.go index e4d88a36c7f..4a3e7fd08c2 100644 --- a/lntest/itest/lnd_test.go +++ b/lntest/itest/lnd_test.go @@ -600,22 +600,6 @@ func shutdownAndAssert(net *lntest.NetworkHarness, t *harnessTest, } } -// calcStaticFee calculates appropriate fees for commitment transactions. This -// function provides a simple way to allow test balance assertions to take fee -// calculations into account. -// -// TODO(bvu): Refactor when dynamic fee estimation is added. -// TODO(conner) remove code duplication -func calcStaticFee(numHTLCs int) btcutil.Amount { - const ( - commitWeight = btcutil.Amount(724) - htlcWeight = 172 - feePerKw = btcutil.Amount(50 * 1000 / 4) - ) - return feePerKw * (commitWeight + - btcutil.Amount(htlcWeight*numHTLCs)) / 1000 -} - // completePaymentRequests sends payments from a lightning node to complete all // payment requests. If the awaitResponse parameter is true, this function // does not return until all payments successfully complete without errors. @@ -998,6 +982,71 @@ func (c commitType) Args() []string { return nil } +// calcStaticFee calculates appropriate fees for commitment transactions. This +// function provides a simple way to allow test balance assertions to take fee +// calculations into account. +// +// TODO(bvu): Refactor when dynamic fee estimation is added. +// TODO(conner) remove code duplication +func (c commitType) calcStaticFee(numHTLCs int) btcutil.Amount { + const ( + htlcWeight = input.HTLCWeight + feePerKw = btcutil.Amount(50 * 1000 / 4) + ) + + commitWeight := btcutil.Amount(input.CommitWeight) + anchors := btcutil.Amount(0) + + // The anchor commitment type is slightly heavier, and we must also add + // the value of the two anchors to the resulting fee the initiator + // pays. + if c == commitTypeAnchors { + commitWeight = btcutil.Amount(input.AnchorCommitWeight) + anchors = 2 * btcutil.Amount(330) + } + + return feePerKw*(commitWeight+ + btcutil.Amount(htlcWeight*numHTLCs))/1000 + anchors +} + +// channelCommitType retrieves the active channel commitment type for the given +// chan point. +func channelCommitType(node *lntest.HarnessNode, + chanPoint *lnrpc.ChannelPoint) (commitType, error) { + + ctxb := context.Background() + ctxt, _ := context.WithTimeout(ctxb, defaultTimeout) + + req := &lnrpc.ListChannelsRequest{} + channels, err := node.ListChannels(ctxt, req) + if err != nil { + return 0, fmt.Errorf("listchannels failed: %v", err) + } + + for _, c := range channels.Channels { + if c.ChannelPoint == txStr(chanPoint) { + + switch c.CommitmentType { + // If the anchor output size is non-zero, we are + // dealing with the anchor type. + case lnrpc.CommitmentType_ANCHORS: + return commitTypeAnchors, nil + + // StaticRemoteKey means it is tweakless, + case lnrpc.CommitmentType_STATIC_REMOTE_KEY: + return commitTypeTweakless, nil + + // Otherwise legacy. + default: + return commitTypeLegacy, nil + } + } + + } + + return 0, fmt.Errorf("channel point %v not found", chanPoint) +} + // basicChannelFundingTest is a sub-test of the main testBasicChannelFunding // test. Given two nodes: Alice and Bob, it'll assert proper channel creation, // then return a function closure that should be called to assert proper @@ -1038,6 +1087,12 @@ func basicChannelFundingTest(t *harnessTest, net *lntest.NetworkHarness, "channel: %v", err) } + cType, err := channelCommitType(alice, chanPoint) + if err != nil { + return nil, nil, nil, fmt.Errorf("unable to get channel "+ + "type: %v", err) + } + // With the channel open, ensure that the amount specified above has // properly been pushed to Bob. balReq := &lnrpc.ChannelBalanceRequest{} @@ -1055,10 +1110,10 @@ func basicChannelFundingTest(t *harnessTest, net *lntest.NetworkHarness, } aliceBalance := btcutil.Amount(aliceBal.Balance) - if aliceBalance != chanAmt-pushAmt-calcStaticFee(0) { + if aliceBalance != chanAmt-pushAmt-cType.calcStaticFee(0) { return nil, nil, nil, fmt.Errorf("alice's balance is "+ "incorrect: expected %v got %v", - chanAmt-pushAmt-calcStaticFee(0), aliceBalance) + chanAmt-pushAmt-cType.calcStaticFee(0), aliceBalance) } bobBalance := btcutil.Amount(bobBal.Balance) @@ -1274,6 +1329,11 @@ func testUnconfirmedChannelFunding(net *lntest.NetworkHarness, t *harnessTest) { t.Fatalf("error while waiting for channel open: %v", err) } + cType, err := channelCommitType(net.Alice, chanPoint) + if err != nil { + t.Fatalf("unable to get channel type: %v", err) + } + // With the channel open, we'll check the balances on each side of the // channel as a sanity check to ensure things worked out as intended. balReq := &lnrpc.ChannelBalanceRequest{} @@ -1287,9 +1347,9 @@ func testUnconfirmedChannelFunding(net *lntest.NetworkHarness, t *harnessTest) { if err != nil { t.Fatalf("unable to get alice's balance: %v", err) } - if carolBal.Balance != int64(chanAmt-pushAmt-calcStaticFee(0)) { + if carolBal.Balance != int64(chanAmt-pushAmt-cType.calcStaticFee(0)) { t.Fatalf("carol's balance is incorrect: expected %v got %v", - chanAmt-pushAmt-calcStaticFee(0), carolBal) + chanAmt-pushAmt-cType.calcStaticFee(0), carolBal) } if aliceBal.Balance != int64(pushAmt) { t.Fatalf("alice's balance is incorrect: expected %v got %v", @@ -2707,9 +2767,14 @@ func testChannelBalance(net *lntest.NetworkHarness, t *harnessTest) { "timeout: %v", err) } + cType, err := channelCommitType(net.Alice, chanPoint) + if err != nil { + t.Fatalf("unable to get channel type: %v", err) + } + // As this is a single funder channel, Alice's balance should be // exactly 0.5 BTC since now state transitions have taken place yet. - checkChannelBalance(net.Alice, amount-calcStaticFee(0)) + checkChannelBalance(net.Alice, amount-cType.calcStaticFee(0)) // Ensure Bob currently has no available balance within the channel. checkChannelBalance(net.Bob, 0) @@ -9139,7 +9204,12 @@ func testHtlcErrorPropagation(net *lntest.NetworkHarness, t *harnessTest) { t.Fatalf("channel not seen by alice before timeout: %v", err) } - commitFee := calcStaticFee(0) + cType, err := channelCommitType(net.Alice, chanPointAlice) + if err != nil { + t.Fatalf("unable to get channel type: %v", err) + } + + commitFee := cType.calcStaticFee(0) assertBaseBalance := func() { balReq := &lnrpc.ChannelBalanceRequest{} ctxt, _ = context.WithTimeout(ctxb, defaultTimeout) From d142a3d5861cc8a5316ce6a387981c5f0effe975 Mon Sep 17 00:00:00 2001 From: "Johan T. Halseth" Date: Wed, 4 Mar 2020 13:21:29 +0100 Subject: [PATCH 10/11] itest: enable anchor type for basic funding flow, force close test --- lntest/itest/lnd_test.go | 69 +++++++++++++++++++++++++++++----------- 1 file changed, 51 insertions(+), 18 deletions(-) diff --git a/lntest/itest/lnd_test.go b/lntest/itest/lnd_test.go index 4a3e7fd08c2..2fc86a812f0 100644 --- a/lntest/itest/lnd_test.go +++ b/lntest/itest/lnd_test.go @@ -1158,6 +1158,7 @@ func testBasicChannelFunding(net *lntest.NetworkHarness, t *harnessTest) { allTypes := []commitType{ commitTypeLegacy, commitTypeTweakless, + commitTypeAnchors, } test: @@ -1210,27 +1211,58 @@ test: t.Fatalf("failed funding flow: %v", err) } - carolTweakless := carolCommitType == commitTypeTweakless + // Both nodes should report the same commitment + // type. + chansCommitType := carolChannel.CommitmentType + if daveChannel.CommitmentType != chansCommitType { + t.Fatalf("commit types don't match, "+ + "carol got %v, dave got %v", + carolChannel.CommitmentType, + daveChannel.CommitmentType, + ) + } + + // Now check that the commitment type reported + // by both nodes is what we expect. It will be + // the minimum of the two nodes' preference, in + // the order Legacy, Tweakless, Anchors. + expType := carolCommitType + + switch daveCommitType { + + // Dave supports anchors, type will be what + // Carol supports. + case commitTypeAnchors: + + // Dave only supports tweakless, channel will + // be downgraded to this type if Carol supports + // anchors. + case commitTypeTweakless: + if expType == commitTypeAnchors { + expType = commitTypeTweakless + } + + // Dave only supoprts legacy type, channel will + // be downgraded to this type. + case commitTypeLegacy: + expType = commitTypeLegacy - daveTweakless := daveCommitType == commitTypeTweakless + default: + t.Fatalf("invalid commit type %v", + daveCommitType) + } - tweaklessSignalled := carolTweakless && daveTweakless - tweaklessChans := (carolChannel.StaticRemoteKey && - daveChannel.StaticRemoteKey) + // Check that the signalled type match what we + // expect. switch { - // If both sides signalled a tweakless channel, and the - // resulting channel doesn't reflect this, then this - // is a failed case. - case tweaklessSignalled && !tweaklessChans: - t.Fatalf("expected tweakless channnel, got " + - "non-tweaked channel") - - // If both sides didn't signal a tweakless - // channel, and the resulting channel is - // tweakless, and this is also a failed case. - case !tweaklessSignalled && tweaklessChans: - t.Fatalf("expected non-tweaked channel, got " + - "tweakless channel") + case expType == commitTypeAnchors && chansCommitType == lnrpc.CommitmentType_ANCHORS: + case expType == commitTypeTweakless && chansCommitType == lnrpc.CommitmentType_STATIC_REMOTE_KEY: + case expType == commitTypeLegacy && chansCommitType == lnrpc.CommitmentType_LEGACY: + + default: + t.Fatalf("expected nodes to signal "+ + "commit type %v, instead got "+ + "%v", expType, chansCommitType) } // As we've concluded this sub-test case we'll @@ -3052,6 +3084,7 @@ func testChannelForceClosure(net *lntest.NetworkHarness, t *harnessTest) { allTypes := []commitType{ commitTypeLegacy, commitTypeTweakless, + commitTypeAnchors, } for _, channelType := range allTypes { From d8e032475b4a77841def5a5f43665f597b9f0ea5 Mon Sep 17 00:00:00 2001 From: "Johan T. Halseth" Date: Wed, 4 Mar 2020 13:21:29 +0100 Subject: [PATCH 11/11] itest: enable anchor commitment for multi-hop test These tests exercise the different ways of sweeping a commitment, so we'll cover the modified scripts used for anchor commitments. The only change we need for the tests to pass is to not assert exactly which output is being swept, since this index depends on whether the anchors are there or not. --- ...d_multi-hop_htlc_local_chain_claim_test.go | 1 + .../lnd_multi-hop_htlc_local_timeout_test.go | 21 +++++++++++++------ 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/lntest/itest/lnd_multi-hop_htlc_local_chain_claim_test.go b/lntest/itest/lnd_multi-hop_htlc_local_chain_claim_test.go index 91fccbbc877..9fb9a9311b5 100644 --- a/lntest/itest/lnd_multi-hop_htlc_local_chain_claim_test.go +++ b/lntest/itest/lnd_multi-hop_htlc_local_chain_claim_test.go @@ -71,6 +71,7 @@ func testMultiHopHtlcClaims(net *lntest.NetworkHarness, t *harnessTest) { allTypes := []commitType{ commitTypeLegacy, commitTypeTweakless, + commitTypeAnchors, } for _, commitType := range allTypes { diff --git a/lntest/itest/lnd_multi-hop_htlc_local_timeout_test.go b/lntest/itest/lnd_multi-hop_htlc_local_timeout_test.go index 98b66bda0a9..a770a281e2a 100644 --- a/lntest/itest/lnd_multi-hop_htlc_local_timeout_test.go +++ b/lntest/itest/lnd_multi-hop_htlc_local_timeout_test.go @@ -151,12 +151,21 @@ func testMultiHopHtlcLocalTimeout(net *lntest.NetworkHarness, t *harnessTest, // We'll mine the remaining blocks in order to generate the sweep // transaction of Bob's commitment output. mineBlocks(t, net, defaultCSV, 1) - assertSpendingTxInMempool( - t, net.Miner.Node, minerMempoolTimeout, wire.OutPoint{ - Hash: *closeTxid, - Index: 1, - }, - ) + + // Check that the sweep spends from the mined commitment. + sweepHash, err := waitForTxInMempool(net.Miner.Node, minerMempoolTimeout) + if err != nil { + t.Fatalf("unable to find bob's sweep tx: %v", err) + } + sweepTx, err := net.Miner.Node.GetRawTransaction(sweepHash) + if err != nil { + t.Fatalf("unable to get txn: %v", err) + } + // Since we cannot know for sure which index is swept, check only txid. + if sweepTx.MsgTx().TxIn[0].PreviousOutPoint.Hash != *closeTxid { + t.Fatalf("sweep transaction not spending close tx: %v", + spew.Sdump(sweepTx)) + } // Bob's pending channel report should show that he has a commitment // output awaiting sweeping, and also that there's an outgoing HTLC