diff --git a/lntest/itest/lnd_routing_test.go b/lntest/itest/lnd_routing_test.go index 1b69ffc3..3ef7639f 100644 --- a/lntest/itest/lnd_routing_test.go +++ b/lntest/itest/lnd_routing_test.go @@ -860,7 +860,7 @@ func testPrivateChannels(net *lntest.NetworkHarness, t *harnessTest) { // Alice of 100k. ctxt, _ = context.WithTimeout(ctxb, defaultTimeout) if err := net.ConnectNodes(ctxt, carol, net.Alice); err != nil { - t.Fatalf("unable to connect dave to alice: %v", err) + t.Fatalf("unable to connect carol to alice: %v", err) } ctxt, _ = context.WithTimeout(ctxb, channelOpenTimeout) chanOpenUpdate := openChannelStream( @@ -1068,6 +1068,156 @@ func testPrivateChannels(net *lntest.NetworkHarness, t *harnessTest) { closeChannelAndAssert(ctxt, 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. + ctxt, _ := context.WithTimeout(ctxb, channelOpenTimeout) + chanPointAliceBob := openChannelAndAssert( + ctxt, t, net, net.Alice, net.Bob, + lntest.OpenChannelParams{ + Amt: chanAmt, + }, + ) + defer closeChannelAndAssert( + ctxt, 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. + ctxt, _ = context.WithTimeout(ctxb, defaultTimeout) + require.NoError(t.t, + net.ConnectNodes(ctxt, carol, net.Bob), + "unable to connect carol to bob", + ) + + // Open a channel with 100k satoshis between Bob and Carol. + ctxt, _ = context.WithTimeout(ctxb, channelOpenTimeout) + chanPointBobCarol := openChannelAndAssert( + ctxt, t, net, net.Bob, carol, + lntest.OpenChannelParams{ + Amt: chanAmt, + Private: true, + }, + ) + defer closeChannelAndAssert( + ctxt, 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( + ctxt, 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) +} + // testInvoiceRoutingHints tests that the routing hints for an invoice are // created properly. func testInvoiceRoutingHints(net *lntest.NetworkHarness, t *harnessTest) { diff --git a/lntest/itest/lnd_test_list_on_test.go b/lntest/itest/lnd_test_list_on_test.go index d5ecc34a..d3430f4f 100644 --- a/lntest/itest/lnd_test_list_on_test.go +++ b/lntest/itest/lnd_test_list_on_test.go @@ -107,6 +107,11 @@ var allTestCases = []*testCase{ name: "private channels", test: testPrivateChannels, }, + { + name: "private channel update policy", + test: testUpdateChannelPolicyForPrivateChannel, + }, + { name: "invoice routing hints", test: testInvoiceRoutingHints, diff --git a/lntest/itest/log_error_whitelist.txt b/lntest/itest/log_error_whitelist.txt index a51376df..0276c942 100644 --- a/lntest/itest/log_error_whitelist.txt +++ b/lntest/itest/log_error_whitelist.txt @@ -283,3 +283,4 @@