From 06fa17513ca6460ea4401700cea5050133738013 Mon Sep 17 00:00:00 2001 From: yyforyongyu Date: Sun, 8 Aug 2021 04:50:45 +0800 Subject: [PATCH] itest: move tests by their category --- lntest/itest/assertions.go | 67 ---- lntest/itest/lnd_channel_graph_test.go | 67 ++++ lntest/itest/lnd_channel_policy_test.go | 377 ++++++++++++++++++-- lntest/itest/lnd_misc_test.go | 203 ----------- lntest/itest/lnd_multi-hop-payments_test.go | 45 +++ lntest/itest/lnd_routing_test.go | 139 -------- 6 files changed, 451 insertions(+), 447 deletions(-) diff --git a/lntest/itest/assertions.go b/lntest/itest/assertions.go index fe881f1d1..4b540a09c 100644 --- a/lntest/itest/assertions.go +++ b/lntest/itest/assertions.go @@ -4,7 +4,6 @@ import ( "context" "encoding/hex" "fmt" - "io" "math" "sync/atomic" "testing" @@ -94,72 +93,6 @@ func openChannelAndAssert(t *harnessTest, net *lntest.NetworkHarness, return fundingChanPoint } -// graphSubscription houses the proxied update and error chans for a node's -// graph subscriptions. -type graphSubscription struct { - updateChan chan *lnrpc.GraphTopologyUpdate - errChan chan error - quit chan struct{} -} - -// subscribeGraphNotifications subscribes to channel graph updates and launches -// a goroutine that forwards these to the returned channel. -func subscribeGraphNotifications(ctxb context.Context, t *harnessTest, - node *lntest.HarnessNode) graphSubscription { - - // We'll first start by establishing a notification client which will - // send us notifications upon detected changes in the channel graph. - req := &lnrpc.GraphTopologySubscription{} - ctx, cancelFunc := context.WithCancel(ctxb) - topologyClient, err := node.SubscribeChannelGraph(ctx, req) - require.NoError(t.t, err, "unable to create topology client") - - // We'll launch a goroutine that will be responsible for proxying all - // notifications recv'd from the client into the channel below. - errChan := make(chan error, 1) - quit := make(chan struct{}) - graphUpdates := make(chan *lnrpc.GraphTopologyUpdate, 20) - go func() { - for { - defer cancelFunc() - - select { - case <-quit: - return - default: - graphUpdate, err := topologyClient.Recv() - select { - case <-quit: - return - default: - } - - if err == io.EOF { - return - } else if err != nil { - select { - case errChan <- err: - case <-quit: - } - return - } - - select { - case graphUpdates <- graphUpdate: - case <-quit: - return - } - } - } - }() - - return graphSubscription{ - updateChan: graphUpdates, - errChan: errChan, - quit: quit, - } -} - func waitForGraphSync(t *harnessTest, node *lntest.HarnessNode) { t.t.Helper() diff --git a/lntest/itest/lnd_channel_graph_test.go b/lntest/itest/lnd_channel_graph_test.go index f357696ba..774f8e2b9 100644 --- a/lntest/itest/lnd_channel_graph_test.go +++ b/lntest/itest/lnd_channel_graph_test.go @@ -4,6 +4,7 @@ import ( "bytes" "context" "fmt" + "io" "testing" "time" @@ -749,3 +750,69 @@ func testNodeAnnouncement(net *lntest.NetworkHarness, t *harnessTest) { // Close the channel between Bob and Dave. closeChannelAndAssert(t, net, net.Bob, chanPoint, false) } + +// graphSubscription houses the proxied update and error chans for a node's +// graph subscriptions. +type graphSubscription struct { + updateChan chan *lnrpc.GraphTopologyUpdate + errChan chan error + quit chan struct{} +} + +// subscribeGraphNotifications subscribes to channel graph updates and launches +// a goroutine that forwards these to the returned channel. +func subscribeGraphNotifications(ctxb context.Context, t *harnessTest, + node *lntest.HarnessNode) graphSubscription { + + // We'll first start by establishing a notification client which will + // send us notifications upon detected changes in the channel graph. + req := &lnrpc.GraphTopologySubscription{} + ctx, cancelFunc := context.WithCancel(ctxb) + topologyClient, err := node.SubscribeChannelGraph(ctx, req) + require.NoError(t.t, err, "unable to create topology client") + + // We'll launch a goroutine that will be responsible for proxying all + // notifications recv'd from the client into the channel below. + errChan := make(chan error, 1) + quit := make(chan struct{}) + graphUpdates := make(chan *lnrpc.GraphTopologyUpdate, 20) + go func() { + for { + defer cancelFunc() + + select { + case <-quit: + return + default: + graphUpdate, err := topologyClient.Recv() + select { + case <-quit: + return + default: + } + + if err == io.EOF { + return + } else if err != nil { + select { + case errChan <- err: + case <-quit: + } + return + } + + select { + case graphUpdates <- graphUpdate: + case <-quit: + return + } + } + } + }() + + return graphSubscription{ + updateChan: graphUpdates, + errChan: errChan, + quit: quit, + } +} diff --git a/lntest/itest/lnd_channel_policy_test.go b/lntest/itest/lnd_channel_policy_test.go index 6906d54a6..67c76f342 100644 --- a/lntest/itest/lnd_channel_policy_test.go +++ b/lntest/itest/lnd_channel_policy_test.go @@ -3,13 +3,16 @@ package itest import ( "context" "strings" + "time" + "github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcutil" "github.com/lightningnetwork/lnd/chainreg" "github.com/lightningnetwork/lnd/funding" "github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lntest" "github.com/lightningnetwork/lnd/lnwire" + "github.com/stretchr/testify/require" ) // testUpdateChannelPolicy tests that policy updates made to a channel @@ -509,47 +512,345 @@ func testUpdateChannelPolicy(net *lntest.NetworkHarness, t *harnessTest) { closeChannelAndAssert(t, net, net.Alice, chanPoint3, false) } -// updateChannelPolicy updates the channel policy of node to the -// given fees and timelock delta. This function blocks until -// listenerNode has received the policy update. -func updateChannelPolicy(t *harnessTest, node *lntest.HarnessNode, - chanPoint *lnrpc.ChannelPoint, baseFee int64, feeRate int64, - timeLockDelta uint32, maxHtlc uint64, listenerNode *lntest.HarnessNode) { - +// testSendUpdateDisableChannel ensures that a channel update with the disable +// flag set is sent once a channel has been either unilaterally or cooperatively +// closed. +func testSendUpdateDisableChannel(net *lntest.NetworkHarness, t *harnessTest) { ctxb := context.Background() - expectedPolicy := &lnrpc.RoutingPolicy{ - FeeBaseMsat: baseFee, - FeeRateMilliMsat: feeRate, - TimeLockDelta: timeLockDelta, - MinHtlc: 1000, // default value - MaxHtlcMsat: maxHtlc, - } + const ( + chanAmt = 100000 + ) - updateFeeReq := &lnrpc.PolicyUpdateRequest{ - BaseFeeMsat: baseFee, - FeeRate: float64(feeRate) / testFeeBase, - TimeLockDelta: timeLockDelta, - Scope: &lnrpc.PolicyUpdateRequest_ChanPoint{ - ChanPoint: chanPoint, - }, - MaxHtlcMsat: maxHtlc, - } - - ctxt, _ := context.WithTimeout(ctxb, defaultTimeout) - if _, err := node.UpdateChannelPolicy(ctxt, updateFeeReq); err != nil { - t.Fatalf("unable to update chan policy: %v", err) - } - - // Wait for listener node to receive the channel update from node. - ctxt, _ = context.WithTimeout(ctxb, defaultTimeout) - graphSub := subscribeGraphNotifications(ctxt, t, listenerNode) - defer close(graphSub.quit) - - waitForChannelUpdate( - t, graphSub, - []expectedChanUpdate{ - {node.PubKeyStr, expectedPolicy, chanPoint}, + // Open a channel between Alice and Bob and Alice and Carol. These will + // be closed later on in order to trigger channel update messages + // marking the channels as disabled. + chanPointAliceBob := openChannelAndAssert( + t, net, net.Alice, net.Bob, + lntest.OpenChannelParams{ + Amt: chanAmt, }, ) + + carol := net.NewNode( + t.t, "Carol", []string{ + "--minbackoff=10s", + "--chan-enable-timeout=1.5s", + "--chan-disable-timeout=3s", + "--chan-status-sample-interval=.5s", + }) + defer shutdownAndAssert(net, t, carol) + + net.ConnectNodes(t.t, net.Alice, carol) + chanPointAliceCarol := openChannelAndAssert( + t, net, net.Alice, carol, + lntest.OpenChannelParams{ + Amt: chanAmt, + }, + ) + + // We create a new node Eve that has an inactive channel timeout of + // just 2 seconds (down from the default 20m). It will be used to test + // channel updates for channels going inactive. + eve := net.NewNode( + t.t, "Eve", []string{ + "--minbackoff=10s", + "--chan-enable-timeout=1.5s", + "--chan-disable-timeout=3s", + "--chan-status-sample-interval=.5s", + }) + defer shutdownAndAssert(net, t, eve) + + // Give Eve some coins. + net.SendCoins(t.t, btcutil.SatoshiPerBitcoin, eve) + + // Connect Eve to Carol and Bob, and open a channel to carol. + net.ConnectNodes(t.t, eve, carol) + net.ConnectNodes(t.t, eve, net.Bob) + + chanPointEveCarol := openChannelAndAssert( + t, net, eve, carol, + lntest.OpenChannelParams{ + Amt: chanAmt, + }, + ) + + // Launch a node for Dave which will connect to Bob in order to receive + // graph updates from. This will ensure that the channel updates are + // propagated throughout the network. + dave := net.NewNode(t.t, "Dave", nil) + defer shutdownAndAssert(net, t, dave) + + net.ConnectNodes(t.t, net.Bob, dave) + + daveSub := subscribeGraphNotifications(ctxb, t, dave) + defer close(daveSub.quit) + + // We should expect to see a channel update with the default routing + // policy, except that it should indicate the channel is disabled. + expectedPolicy := &lnrpc.RoutingPolicy{ + FeeBaseMsat: int64(chainreg.DefaultBitcoinBaseFeeMSat), + FeeRateMilliMsat: int64(chainreg.DefaultBitcoinFeeRate), + TimeLockDelta: chainreg.DefaultBitcoinTimeLockDelta, + MinHtlc: 1000, // default value + MaxHtlcMsat: calculateMaxHtlc(chanAmt), + Disabled: true, + } + + // Let Carol go offline. Since Eve has an inactive timeout of 2s, we + // expect her to send an update disabling the channel. + restartCarol, err := net.SuspendNode(carol) + if err != nil { + t.Fatalf("unable to suspend carol: %v", err) + } + waitForChannelUpdate( + t, daveSub, + []expectedChanUpdate{ + {eve.PubKeyStr, expectedPolicy, chanPointEveCarol}, + }, + ) + + // We restart Carol. Since the channel now becomes active again, Eve + // should send a ChannelUpdate setting the channel no longer disabled. + if err := restartCarol(); err != nil { + t.Fatalf("unable to restart carol: %v", err) + } + + expectedPolicy.Disabled = false + waitForChannelUpdate( + t, daveSub, + []expectedChanUpdate{ + {eve.PubKeyStr, expectedPolicy, chanPointEveCarol}, + }, + ) + + // Now we'll test a long disconnection. Disconnect Carol and Eve and + // ensure they both detect each other as disabled. Their min backoffs + // are high enough to not interfere with disabling logic. + if err := net.DisconnectNodes(carol, eve); err != nil { + t.Fatalf("unable to disconnect Carol from Eve: %v", err) + } + + // Wait for a disable from both Carol and Eve to come through. + expectedPolicy.Disabled = true + waitForChannelUpdate( + t, daveSub, + []expectedChanUpdate{ + {eve.PubKeyStr, expectedPolicy, chanPointEveCarol}, + {carol.PubKeyStr, expectedPolicy, chanPointEveCarol}, + }, + ) + + // Reconnect Carol and Eve, this should cause them to reenable the + // channel from both ends after a short delay. + net.EnsureConnected(t.t, carol, eve) + + expectedPolicy.Disabled = false + waitForChannelUpdate( + t, daveSub, + []expectedChanUpdate{ + {eve.PubKeyStr, expectedPolicy, chanPointEveCarol}, + {carol.PubKeyStr, expectedPolicy, chanPointEveCarol}, + }, + ) + + // Now we'll test a short disconnection. Disconnect Carol and Eve, then + // reconnect them after one second so that their scheduled disables are + // aborted. One second is twice the status sample interval, so this + // should allow for the disconnect to be detected, but still leave time + // to cancel the announcement before the 3 second inactive timeout is + // hit. + if err := net.DisconnectNodes(carol, eve); err != nil { + t.Fatalf("unable to disconnect Carol from Eve: %v", err) + } + time.Sleep(time.Second) + net.EnsureConnected(t.t, eve, carol) + + // Since the disable should have been canceled by both Carol and Eve, we + // expect no channel updates to appear on the network. + assertNoChannelUpdates(t, daveSub, 4*time.Second) + + // Close Alice's channels with Bob and Carol cooperatively and + // unilaterally respectively. + _, _, err = net.CloseChannel(net.Alice, chanPointAliceBob, false) + if err != nil { + t.Fatalf("unable to close channel: %v", err) + } + + _, _, err = net.CloseChannel(net.Alice, chanPointAliceCarol, true) + if err != nil { + t.Fatalf("unable to close channel: %v", err) + } + + // Now that the channel close processes have been started, we should + // receive an update marking each as disabled. + expectedPolicy.Disabled = true + waitForChannelUpdate( + t, daveSub, + []expectedChanUpdate{ + {net.Alice.PubKeyStr, expectedPolicy, chanPointAliceBob}, + {net.Alice.PubKeyStr, expectedPolicy, chanPointAliceCarol}, + }, + ) + + // Finally, close the channels by mining the closing transactions. + mineBlocks(t, net, 1, 2) + + // Also do this check for Eve's channel with Carol. + _, _, err = net.CloseChannel(eve, chanPointEveCarol, false) + if err != nil { + t.Fatalf("unable to close channel: %v", err) + } + + waitForChannelUpdate( + t, daveSub, + []expectedChanUpdate{ + {eve.PubKeyStr, expectedPolicy, chanPointEveCarol}, + }, + ) + mineBlocks(t, net, 1, 1) + + // And finally, clean up the force closed channel by mining the + // sweeping transaction. + cleanupForceClose(t, net, net.Alice, chanPointAliceCarol) +} + +// testUpdateChannelPolicyForPrivateChannel tests when a private channel +// updates its channel edge policy, we will use the updated policy to send our +// payment. +// The topology is created as: Alice -> Bob -> Carol, where Alice -> Bob is +// public and Bob -> Carol is private. After an invoice is created by Carol, +// Bob will update the base fee via UpdateChannelPolicy, we will test that +// Alice will not fail the payment and send it using the updated channel +// policy. +func testUpdateChannelPolicyForPrivateChannel(net *lntest.NetworkHarness, + t *harnessTest) { + + ctxb := context.Background() + defer ctxb.Done() + + // We'll create the following topology first, + // Alice <--public:100k--> Bob <--private:100k--> Carol + const chanAmt = btcutil.Amount(100000) + + // Open a channel with 100k satoshis between Alice and Bob. + chanPointAliceBob := openChannelAndAssert( + t, net, net.Alice, net.Bob, + lntest.OpenChannelParams{ + Amt: chanAmt, + }, + ) + defer closeChannelAndAssert(t, net, net.Alice, chanPointAliceBob, false) + + // Get Alice's funding point. + aliceChanTXID, err := lnrpc.GetChanPointFundingTxid(chanPointAliceBob) + require.NoError(t.t, err, "unable to get txid") + aliceFundPoint := wire.OutPoint{ + Hash: *aliceChanTXID, + Index: chanPointAliceBob.OutputIndex, + } + + // Create a new node Carol. + carol := net.NewNode(t.t, "Carol", nil) + defer shutdownAndAssert(net, t, carol) + + // Connect Carol to Bob. + net.ConnectNodes(t.t, carol, net.Bob) + + // Open a channel with 100k satoshis between Bob and Carol. + chanPointBobCarol := openChannelAndAssert( + t, net, net.Bob, carol, + lntest.OpenChannelParams{ + Amt: chanAmt, + Private: true, + }, + ) + defer closeChannelAndAssert(t, net, net.Bob, chanPointBobCarol, false) + + // Get Bob's funding point. + bobChanTXID, err := lnrpc.GetChanPointFundingTxid(chanPointBobCarol) + require.NoError(t.t, err, "unable to get txid") + bobFundPoint := wire.OutPoint{ + Hash: *bobChanTXID, + Index: chanPointBobCarol.OutputIndex, + } + + // We should have the following topology now, + // Alice <--public:100k--> Bob <--private:100k--> Carol + // + // Now we will create an invoice for Carol. + const paymentAmt = 20000 + invoice := &lnrpc.Invoice{ + Memo: "routing hints", + Value: paymentAmt, + Private: true, + } + ctxt, _ := context.WithTimeout(ctxb, defaultTimeout) + resp, err := carol.AddInvoice(ctxt, invoice) + require.NoError(t.t, err, "unable to create invoice for carol") + + // Bob now updates the channel edge policy for the private channel. + const ( + baseFeeMSat = 33000 + ) + timeLockDelta := uint32(chainreg.DefaultBitcoinTimeLockDelta) + updateFeeReq := &lnrpc.PolicyUpdateRequest{ + BaseFeeMsat: baseFeeMSat, + TimeLockDelta: timeLockDelta, + Scope: &lnrpc.PolicyUpdateRequest_ChanPoint{ + ChanPoint: chanPointBobCarol, + }, + } + ctxt, _ = context.WithTimeout(ctxb, defaultTimeout) + _, err = net.Bob.UpdateChannelPolicy(ctxt, updateFeeReq) + require.NoError(t.t, err, "unable to update chan policy") + + // Alice pays the invoices. She will use the updated baseFeeMSat in the + // payment + ctxt, _ = context.WithTimeout(ctxb, defaultTimeout) + payReqs := []string{resp.PaymentRequest} + require.NoError(t.t, + completePaymentRequests( + net.Alice, net.Alice.RouterClient, payReqs, true, + ), "unable to send payment", + ) + + // Check that Alice did make the payment with two HTLCs, one failed and + // one succeeded. + ctxt, _ = context.WithTimeout(ctxt, defaultTimeout) + paymentsResp, err := net.Alice.ListPayments( + ctxt, &lnrpc.ListPaymentsRequest{}, + ) + require.NoError(t.t, err, "failed to obtain payments for Alice") + require.Equal(t.t, 1, len(paymentsResp.Payments), "expected 1 payment") + + htlcs := paymentsResp.Payments[0].Htlcs + require.Equal(t.t, 2, len(htlcs), "expected to have 2 HTLCs") + require.Equal( + t.t, lnrpc.HTLCAttempt_FAILED, htlcs[0].Status, + "the first HTLC attempt should fail", + ) + require.Equal( + t.t, lnrpc.HTLCAttempt_SUCCEEDED, htlcs[1].Status, + "the second HTLC attempt should succeed", + ) + + // Carol should have received 20k satoshis from Bob. + assertAmountPaid(t, "Carol(remote) [<=private] Bob(local)", + carol, bobFundPoint, 0, paymentAmt) + + // Bob should have sent 20k satoshis to Carol. + assertAmountPaid(t, "Bob(local) [private=>] Carol(remote)", + net.Bob, bobFundPoint, paymentAmt, 0) + + // Calcuate the amount in satoshis. + amtExpected := int64(paymentAmt + baseFeeMSat/1000) + + // Bob should have received 20k satoshis + fee from Alice. + assertAmountPaid(t, "Bob(remote) <= Alice(local)", + net.Bob, aliceFundPoint, 0, amtExpected) + + // Alice should have sent 20k satoshis + fee to Bob. + assertAmountPaid(t, "Alice(local) => Bob(remote)", + net.Alice, aliceFundPoint, amtExpected, 0) } diff --git a/lntest/itest/lnd_misc_test.go b/lntest/itest/lnd_misc_test.go index 9b9af7a4d..3d2c877e0 100644 --- a/lntest/itest/lnd_misc_test.go +++ b/lntest/itest/lnd_misc_test.go @@ -1323,209 +1323,6 @@ func testNodeSignVerify(net *lntest.NetworkHarness, t *harnessTest) { closeChannelAndAssert(t, net, net.Alice, aliceBobCh, false) } -// testSendUpdateDisableChannel ensures that a channel update with the disable -// flag set is sent once a channel has been either unilaterally or cooperatively -// closed. -func testSendUpdateDisableChannel(net *lntest.NetworkHarness, t *harnessTest) { - ctxb := context.Background() - - const ( - chanAmt = 100000 - ) - - // Open a channel between Alice and Bob and Alice and Carol. These will - // be closed later on in order to trigger channel update messages - // marking the channels as disabled. - chanPointAliceBob := openChannelAndAssert( - t, net, net.Alice, net.Bob, - lntest.OpenChannelParams{ - Amt: chanAmt, - }, - ) - - carol := net.NewNode( - t.t, "Carol", []string{ - "--minbackoff=10s", - "--chan-enable-timeout=1.5s", - "--chan-disable-timeout=3s", - "--chan-status-sample-interval=.5s", - }) - defer shutdownAndAssert(net, t, carol) - - net.ConnectNodes(t.t, net.Alice, carol) - chanPointAliceCarol := openChannelAndAssert( - t, net, net.Alice, carol, - lntest.OpenChannelParams{ - Amt: chanAmt, - }, - ) - - // We create a new node Eve that has an inactive channel timeout of - // just 2 seconds (down from the default 20m). It will be used to test - // channel updates for channels going inactive. - eve := net.NewNode( - t.t, "Eve", []string{ - "--minbackoff=10s", - "--chan-enable-timeout=1.5s", - "--chan-disable-timeout=3s", - "--chan-status-sample-interval=.5s", - }) - defer shutdownAndAssert(net, t, eve) - - // Give Eve some coins. - net.SendCoins(t.t, btcutil.SatoshiPerBitcoin, eve) - - // Connect Eve to Carol and Bob, and open a channel to carol. - net.ConnectNodes(t.t, eve, carol) - net.ConnectNodes(t.t, eve, net.Bob) - - chanPointEveCarol := openChannelAndAssert( - t, net, eve, carol, - lntest.OpenChannelParams{ - Amt: chanAmt, - }, - ) - - // Launch a node for Dave which will connect to Bob in order to receive - // graph updates from. This will ensure that the channel updates are - // propagated throughout the network. - dave := net.NewNode(t.t, "Dave", nil) - defer shutdownAndAssert(net, t, dave) - - net.ConnectNodes(t.t, net.Bob, dave) - - daveSub := subscribeGraphNotifications(ctxb, t, dave) - defer close(daveSub.quit) - - // We should expect to see a channel update with the default routing - // policy, except that it should indicate the channel is disabled. - expectedPolicy := &lnrpc.RoutingPolicy{ - FeeBaseMsat: int64(chainreg.DefaultBitcoinBaseFeeMSat), - FeeRateMilliMsat: int64(chainreg.DefaultBitcoinFeeRate), - TimeLockDelta: chainreg.DefaultBitcoinTimeLockDelta, - MinHtlc: 1000, // default value - MaxHtlcMsat: calculateMaxHtlc(chanAmt), - Disabled: true, - } - - // Let Carol go offline. Since Eve has an inactive timeout of 2s, we - // expect her to send an update disabling the channel. - restartCarol, err := net.SuspendNode(carol) - if err != nil { - t.Fatalf("unable to suspend carol: %v", err) - } - waitForChannelUpdate( - t, daveSub, - []expectedChanUpdate{ - {eve.PubKeyStr, expectedPolicy, chanPointEveCarol}, - }, - ) - - // We restart Carol. Since the channel now becomes active again, Eve - // should send a ChannelUpdate setting the channel no longer disabled. - if err := restartCarol(); err != nil { - t.Fatalf("unable to restart carol: %v", err) - } - - expectedPolicy.Disabled = false - waitForChannelUpdate( - t, daveSub, - []expectedChanUpdate{ - {eve.PubKeyStr, expectedPolicy, chanPointEveCarol}, - }, - ) - - // Now we'll test a long disconnection. Disconnect Carol and Eve and - // ensure they both detect each other as disabled. Their min backoffs - // are high enough to not interfere with disabling logic. - if err := net.DisconnectNodes(carol, eve); err != nil { - t.Fatalf("unable to disconnect Carol from Eve: %v", err) - } - - // Wait for a disable from both Carol and Eve to come through. - expectedPolicy.Disabled = true - waitForChannelUpdate( - t, daveSub, - []expectedChanUpdate{ - {eve.PubKeyStr, expectedPolicy, chanPointEveCarol}, - {carol.PubKeyStr, expectedPolicy, chanPointEveCarol}, - }, - ) - - // Reconnect Carol and Eve, this should cause them to reenable the - // channel from both ends after a short delay. - net.EnsureConnected(t.t, carol, eve) - - expectedPolicy.Disabled = false - waitForChannelUpdate( - t, daveSub, - []expectedChanUpdate{ - {eve.PubKeyStr, expectedPolicy, chanPointEveCarol}, - {carol.PubKeyStr, expectedPolicy, chanPointEveCarol}, - }, - ) - - // Now we'll test a short disconnection. Disconnect Carol and Eve, then - // reconnect them after one second so that their scheduled disables are - // aborted. One second is twice the status sample interval, so this - // should allow for the disconnect to be detected, but still leave time - // to cancel the announcement before the 3 second inactive timeout is - // hit. - if err := net.DisconnectNodes(carol, eve); err != nil { - t.Fatalf("unable to disconnect Carol from Eve: %v", err) - } - time.Sleep(time.Second) - net.EnsureConnected(t.t, eve, carol) - - // Since the disable should have been canceled by both Carol and Eve, we - // expect no channel updates to appear on the network. - assertNoChannelUpdates(t, daveSub, 4*time.Second) - - // Close Alice's channels with Bob and Carol cooperatively and - // unilaterally respectively. - _, _, err = net.CloseChannel(net.Alice, chanPointAliceBob, false) - if err != nil { - t.Fatalf("unable to close channel: %v", err) - } - - _, _, err = net.CloseChannel(net.Alice, chanPointAliceCarol, true) - if err != nil { - t.Fatalf("unable to close channel: %v", err) - } - - // Now that the channel close processes have been started, we should - // receive an update marking each as disabled. - expectedPolicy.Disabled = true - waitForChannelUpdate( - t, daveSub, - []expectedChanUpdate{ - {net.Alice.PubKeyStr, expectedPolicy, chanPointAliceBob}, - {net.Alice.PubKeyStr, expectedPolicy, chanPointAliceCarol}, - }, - ) - - // Finally, close the channels by mining the closing transactions. - mineBlocks(t, net, 1, 2) - - // Also do this check for Eve's channel with Carol. - _, _, err = net.CloseChannel(eve, chanPointEveCarol, false) - if err != nil { - t.Fatalf("unable to close channel: %v", err) - } - - waitForChannelUpdate( - t, daveSub, - []expectedChanUpdate{ - {eve.PubKeyStr, expectedPolicy, chanPointEveCarol}, - }, - ) - mineBlocks(t, net, 1, 1) - - // And finally, clean up the force closed channel by mining the - // sweeping transaction. - cleanupForceClose(t, net, net.Alice, chanPointAliceCarol) -} - // testAbandonChannel abandones a channel and asserts that it is no // longer open and not in one of the pending closure states. It also // verifies that the abandoned channel is reported as closed with close diff --git a/lntest/itest/lnd_multi-hop-payments_test.go b/lntest/itest/lnd_multi-hop-payments_test.go index 792d4fb17..734d5378f 100644 --- a/lntest/itest/lnd_multi-hop-payments_test.go +++ b/lntest/itest/lnd_multi-hop-payments_test.go @@ -384,3 +384,48 @@ func assertEventAndType(t *harnessTest, eventType routerrpc.HtlcEvent_EventType, return event } + +// updateChannelPolicy updates the channel policy of node to the +// given fees and timelock delta. This function blocks until +// listenerNode has received the policy update. +func updateChannelPolicy(t *harnessTest, node *lntest.HarnessNode, + chanPoint *lnrpc.ChannelPoint, baseFee int64, feeRate int64, + timeLockDelta uint32, maxHtlc uint64, listenerNode *lntest.HarnessNode) { + + ctxb := context.Background() + + expectedPolicy := &lnrpc.RoutingPolicy{ + FeeBaseMsat: baseFee, + FeeRateMilliMsat: feeRate, + TimeLockDelta: timeLockDelta, + MinHtlc: 1000, // default value + MaxHtlcMsat: maxHtlc, + } + + updateFeeReq := &lnrpc.PolicyUpdateRequest{ + BaseFeeMsat: baseFee, + FeeRate: float64(feeRate) / testFeeBase, + TimeLockDelta: timeLockDelta, + Scope: &lnrpc.PolicyUpdateRequest_ChanPoint{ + ChanPoint: chanPoint, + }, + MaxHtlcMsat: maxHtlc, + } + + ctxt, _ := context.WithTimeout(ctxb, defaultTimeout) + if _, err := node.UpdateChannelPolicy(ctxt, updateFeeReq); err != nil { + t.Fatalf("unable to update chan policy: %v", err) + } + + // Wait for listener node to receive the channel update from node. + ctxt, _ = context.WithTimeout(ctxb, defaultTimeout) + graphSub := subscribeGraphNotifications(ctxt, t, listenerNode) + defer close(graphSub.quit) + + waitForChannelUpdate( + t, graphSub, + []expectedChanUpdate{ + {node.PubKeyStr, expectedPolicy, chanPoint}, + }, + ) +} diff --git a/lntest/itest/lnd_routing_test.go b/lntest/itest/lnd_routing_test.go index e69354151..6f33d3def 100644 --- a/lntest/itest/lnd_routing_test.go +++ b/lntest/itest/lnd_routing_test.go @@ -1017,145 +1017,6 @@ func testPrivateChannels(net *lntest.NetworkHarness, t *harnessTest) { closeChannelAndAssert(t, net, carol, chanPointPrivate, false) } -// testUpdateChannelPolicyForPrivateChannel tests when a private channel -// updates its channel edge policy, we will use the updated policy to send our -// payment. -// The topology is created as: Alice -> Bob -> Carol, where Alice -> Bob is -// public and Bob -> Carol is private. After an invoice is created by Carol, -// Bob will update the base fee via UpdateChannelPolicy, we will test that -// Alice will not fail the payment and send it using the updated channel -// policy. -func testUpdateChannelPolicyForPrivateChannel(net *lntest.NetworkHarness, - t *harnessTest) { - - ctxb := context.Background() - defer ctxb.Done() - - // We'll create the following topology first, - // Alice <--public:100k--> Bob <--private:100k--> Carol - const chanAmt = btcutil.Amount(100000) - - // Open a channel with 100k satoshis between Alice and Bob. - chanPointAliceBob := openChannelAndAssert( - t, net, net.Alice, net.Bob, - lntest.OpenChannelParams{ - Amt: chanAmt, - }, - ) - defer closeChannelAndAssert(t, net, net.Alice, chanPointAliceBob, false) - - // Get Alice's funding point. - aliceChanTXID, err := lnrpc.GetChanPointFundingTxid(chanPointAliceBob) - require.NoError(t.t, err, "unable to get txid") - aliceFundPoint := wire.OutPoint{ - Hash: *aliceChanTXID, - Index: chanPointAliceBob.OutputIndex, - } - - // Create a new node Carol. - carol := net.NewNode(t.t, "Carol", nil) - defer shutdownAndAssert(net, t, carol) - - // Connect Carol to Bob. - net.ConnectNodes(t.t, carol, net.Bob) - - // Open a channel with 100k satoshis between Bob and Carol. - chanPointBobCarol := openChannelAndAssert( - t, net, net.Bob, carol, - lntest.OpenChannelParams{ - Amt: chanAmt, - Private: true, - }, - ) - defer closeChannelAndAssert(t, net, net.Bob, chanPointBobCarol, false) - - // Get Bob's funding point. - bobChanTXID, err := lnrpc.GetChanPointFundingTxid(chanPointBobCarol) - require.NoError(t.t, err, "unable to get txid") - bobFundPoint := wire.OutPoint{ - Hash: *bobChanTXID, - Index: chanPointBobCarol.OutputIndex, - } - - // We should have the following topology now, - // Alice <--public:100k--> Bob <--private:100k--> Carol - // - // Now we will create an invoice for Carol. - const paymentAmt = 20000 - invoice := &lnrpc.Invoice{ - Memo: "routing hints", - Value: paymentAmt, - Private: true, - } - ctxt, _ := context.WithTimeout(ctxb, defaultTimeout) - resp, err := carol.AddInvoice(ctxt, invoice) - require.NoError(t.t, err, "unable to create invoice for carol") - - // Bob now updates the channel edge policy for the private channel. - const ( - baseFeeMSat = 33000 - ) - timeLockDelta := uint32(chainreg.DefaultBitcoinTimeLockDelta) - updateFeeReq := &lnrpc.PolicyUpdateRequest{ - BaseFeeMsat: baseFeeMSat, - TimeLockDelta: timeLockDelta, - Scope: &lnrpc.PolicyUpdateRequest_ChanPoint{ - ChanPoint: chanPointBobCarol, - }, - } - ctxt, _ = context.WithTimeout(ctxb, defaultTimeout) - _, err = net.Bob.UpdateChannelPolicy(ctxt, updateFeeReq) - require.NoError(t.t, err, "unable to update chan policy") - - // Alice pays the invoices. She will use the updated baseFeeMSat in the - // payment - payReqs := []string{resp.PaymentRequest} - require.NoError(t.t, - completePaymentRequests( - net.Alice, net.Alice.RouterClient, payReqs, true, - ), "unable to send payment", - ) - - // Check that Alice did make the payment with two HTLCs, one failed and - // one succeeded. - ctxt, _ = context.WithTimeout(ctxb, defaultTimeout) - paymentsResp, err := net.Alice.ListPayments( - ctxt, &lnrpc.ListPaymentsRequest{}, - ) - require.NoError(t.t, err, "failed to obtain payments for Alice") - require.Equal(t.t, 1, len(paymentsResp.Payments), "expected 1 payment") - - htlcs := paymentsResp.Payments[0].Htlcs - require.Equal(t.t, 2, len(htlcs), "expected to have 2 HTLCs") - require.Equal( - t.t, lnrpc.HTLCAttempt_FAILED, htlcs[0].Status, - "the first HTLC attempt should fail", - ) - require.Equal( - t.t, lnrpc.HTLCAttempt_SUCCEEDED, htlcs[1].Status, - "the second HTLC attempt should succeed", - ) - - // Carol should have received 20k satoshis from Bob. - assertAmountPaid(t, "Carol(remote) [<=private] Bob(local)", - carol, bobFundPoint, 0, paymentAmt) - - // Bob should have sent 20k satoshis to Carol. - assertAmountPaid(t, "Bob(local) [private=>] Carol(remote)", - net.Bob, bobFundPoint, paymentAmt, 0) - - // Calcuate the amount in satoshis. - amtExpected := int64(paymentAmt + baseFeeMSat/1000) - - // Bob should have received 20k satoshis + fee from Alice. - assertAmountPaid(t, "Bob(remote) <= Alice(local)", - net.Bob, aliceFundPoint, 0, amtExpected) - - // Alice should have sent 20k satoshis + fee to Bob. - assertAmountPaid(t, "Alice(local) => Bob(remote)", - net.Alice, aliceFundPoint, amtExpected, 0) -} - // testInvoiceRoutingHints tests that the routing hints for an invoice are // created properly. func testInvoiceRoutingHints(net *lntest.NetworkHarness, t *harnessTest) {