github.com/decred/dcrlnd@v0.7.6/lntest/itest/dcrlnd_offline_invoice_test.go (about) 1 package itest 2 3 import ( 4 "context" 5 "fmt" 6 "time" 7 8 "github.com/decred/dcrd/chaincfg/chainhash" 9 "github.com/decred/dcrd/dcrutil/v4" 10 "github.com/decred/dcrlnd/lnrpc" 11 "github.com/decred/dcrlnd/lnrpc/invoicesrpc" 12 "github.com/decred/dcrlnd/lnrpc/routerrpc" 13 "github.com/decred/dcrlnd/lntest" 14 "github.com/decred/dcrlnd/lntypes" 15 "github.com/stretchr/testify/require" 16 "matheusd.com/testctx" 17 ) 18 19 // testOfflineHopInvoice tests whether trying to pay an invoice to an offline 20 // node fails as expected. 21 // 22 // This test creates the following network of channels: 23 // 24 // Dave -> Carol Alice -> Bob 25 // 26 // Then tries to perform a payment from Dave -> to Bob. This should fail, since 27 // there is no route connecting them. Carol and Alice are then connected, 28 // payments are performed. And a final test disconnecting Alice and trying to 29 // perform a new payment should also fail. 30 func testOfflineHopInvoice(net *lntest.NetworkHarness, t *harnessTest) { 31 const chanAmt = dcrutil.Amount(100000) 32 ctxb := context.Background() 33 34 // Open a channel between Alice and Bob with Alice being the sole funder of 35 // the channel. 36 chanPointAlice := openChannelAndAssert( 37 t, net, net.Alice, net.Bob, 38 lntest.OpenChannelParams{ 39 Amt: chanAmt, 40 }, 41 ) 42 43 // Create Dave's Node. 44 dave := net.NewNode(t.t, "Dave", nil) 45 defer shutdownAndAssert(net, t, dave) 46 net.SendCoins(t.t, dcrutil.AtomsPerCoin, dave) 47 48 carol := net.NewNode(t.t, "Carol", []string{"--nolisten"}) 49 defer shutdownAndAssert(net, t, carol) 50 51 net.ConnectNodes(t.t, carol, dave) 52 net.SendCoins(t.t, dcrutil.AtomsPerCoin, carol) 53 54 chanPointDave := openChannelAndAssert( 55 t, net, dave, carol, 56 lntest.OpenChannelParams{ 57 Amt: chanAmt, 58 }, 59 ) 60 61 // Generate 5 payment requests in Bob. 62 const numPayments = 5 63 const paymentAmt = 1000 64 payReqs, _, _, err := createPayReqs( 65 net.Bob, paymentAmt, numPayments, 66 ) 67 if err != nil { 68 t.Fatalf("unable to create pay reqs: %v", err) 69 } 70 71 // tryPayment tries to pay the given invoice from srcNode. It will check 72 // if the returned error is the expected one. 73 tryPayment := func(payReq string, srcNode *lntest.HarnessNode, expectedErr string) { 74 sendReq := &lnrpc.SendRequest{ 75 PaymentRequest: payReq, 76 IgnoreMaxOutboundAmt: true, 77 } 78 ctxt, _ := context.WithTimeout(ctxb, defaultTimeout) 79 resp, err := srcNode.SendPaymentSync(ctxt, sendReq) 80 if err != nil { 81 t.Fatalf("unable to send payment: %v", err) 82 } 83 if resp.PaymentError != expectedErr { 84 t.Fatalf("payment error (%v) != expected (%v)", 85 resp.PaymentError, expectedErr) 86 } 87 } 88 89 // Constants to make our lives easier. 90 errNoPath := "unable to find a path to destination" 91 success := "" 92 93 // At this stage, our network looks like the following: 94 // Dave -> Carol Alice -> Bob 95 96 // Payment from Alice should work, given the Alice -> Bob link. 97 tryPayment(payReqs[0], net.Alice, success) 98 99 // Payments from Carol and Dave should _not_ work, given there is no route. 100 tryPayment(payReqs[1], carol, errNoPath) 101 tryPayment(payReqs[1], dave, errNoPath) 102 103 // Connect Carol to Alice (but don't create a channel yet). 104 net.EnsureConnected(t.t, carol, net.Alice) 105 106 // Try to perform the payments from Carol and Dave. They should still fail. 107 tryPayment(payReqs[1], carol, errNoPath) 108 tryPayment(payReqs[1], dave, errNoPath) 109 110 // Create a channel between Carol and Alice and wait for it to become valid. 111 chanPointCarol := openChannelAndAssert( 112 t, net, carol, net.Alice, 113 lntest.OpenChannelParams{ 114 Amt: chanAmt, 115 }, 116 ) 117 118 // Ensure Dave knows about the Carol -> Alice channel 119 if err = dave.WaitForNetworkChannelOpen(chanPointCarol); err != nil { 120 t.Fatalf("carol didn't advertise channel before "+ 121 "timeout: %v", err) 122 } 123 124 // TODO(decred): Fix this. 125 // 126 // This test fails after the upstream PR 2740 is merged due to network 127 // partitions now taking discovery.DefaultHistoricalSyncInterval (10 128 // minutes) to trigger a full graph re-discovery. 129 // 130 // This means dave doesn't get a gossip message describing the 131 // Alice->Bob channel for a long time and fails to find a route to 132 // perform the payments. 133 // 134 // We need some way of force triggering a historical graph sync in dave 135 // after connecting carol and alice (or better yet some way of reliably 136 // knowing that carol didn't previously relay that channel to him). 137 time.Sleep(time.Second * 10) 138 139 fundingTxId, _ := chainhash.NewHash(chanPointAlice.GetFundingTxidBytes()) 140 fmt.Printf("looking for %s\n", fundingTxId) 141 142 resp, err := dave.DescribeGraph(testctx.New(t), &lnrpc.ChannelGraphRequest{}) 143 if err != nil { 144 t.Fatalf("blergh: %v", err) 145 } 146 fmt.Println("Existing network graph") 147 for _, e := range resp.Edges { 148 fmt.Printf("edge %s\n n1=%s\n n2=%s\n", e.ChanPoint, 149 e.Node1Pub, e.Node2Pub) 150 } 151 152 if err = dave.WaitForNetworkChannelOpen(chanPointAlice); err != nil { 153 t.Fatalf("dave didn't receive the alice->bob channel before "+ 154 "timeout: %v", err) 155 } 156 // At this stage, our network looks like the following: 157 // Dave -> Carol -> Alice -> Bob 158 159 // Performing the payments should now work. 160 tryPayment(payReqs[1], carol, success) 161 tryPayment(payReqs[2], dave, success) 162 163 // Disconnect Carol from Alice & Dave (simulating a broken link, carol 164 // offline, etc) 165 if err := net.DisconnectNodes(carol, net.Alice); err != nil { 166 t.Fatalf("unable to disconnect carol from alice: %v", err) 167 } 168 if err := net.DisconnectNodes(carol, dave); err != nil { 169 t.Fatalf("unable to disconnect carol from dave: %v", err) 170 } 171 172 // Give some time for disconnection to finalize. 173 time.Sleep(time.Second) 174 175 // Starting payments from Carol and Dave should fail. 176 tryPayment(payReqs[3], carol, errNoPath) 177 tryPayment(payReqs[3], dave, errNoPath) 178 179 // Reconnect Carol to Alice & Dave 180 net.EnsureConnected(t.t, carol, net.Alice) 181 net.EnsureConnected(t.t, carol, dave) 182 183 // Give some time for reconnection to finalize. 184 time.Sleep(time.Second) 185 186 // Payments now succeed again. 187 tryPayment(payReqs[3], carol, success) 188 tryPayment(payReqs[4], dave, success) 189 190 // Close the channels. 191 closeChannelAndAssert(t, net, net.Alice, chanPointAlice, false) 192 closeChannelAndAssert(t, net, carol, chanPointCarol, false) 193 closeChannelAndAssert(t, net, dave, chanPointDave, false) 194 } 195 196 // testCalcPayStats asserts the correctness of the CalcPaymentStats RPC. 197 func testCalcPayStats(net *lntest.NetworkHarness, t *harnessTest) { 198 ctxb, cancel := context.WithCancel(context.Background()) 199 defer cancel() 200 201 alice := net.NewNode(t.t, "Alice", nil) 202 defer shutdownAndAssert(net, t, alice) 203 net.SendCoins(t.t, dcrutil.AtomsPerCoin, alice) 204 205 bob := net.NewNode(t.t, "Bob", nil) 206 defer shutdownAndAssert(net, t, bob) 207 net.ConnectNodes(t.t, alice, bob) 208 209 // Open a channel between alice and bob. 210 chanReq := lntest.OpenChannelParams{ 211 Amt: defaultChanAmt, 212 } 213 214 chanPoint := openChannelAndAssert(t, net, alice, bob, chanReq) 215 216 // Complete 5 payments. 217 const numPayments = 5 218 const paymentAmt = 1000 219 payReqs, _, _, err := createPayReqs( 220 bob, paymentAmt, numPayments, 221 ) 222 require.NoError(t.t, err) 223 err = completePaymentRequests( 224 alice, alice.RouterClient, 225 payReqs, true, 226 ) 227 require.NoError(t.t, err) 228 229 // Create and fail a payment (by using a hold invoice). 230 var ( 231 preimage = lntypes.Preimage{1, 2, 3, 4, 5} 232 payHash = preimage.Hash() 233 ) 234 invoiceReq := &invoicesrpc.AddHoldInvoiceRequest{ 235 Value: 30000, 236 CltvExpiry: 40, 237 Hash: payHash[:], 238 } 239 240 bobInvoice, err := bob.AddHoldInvoice(testctx.New(t), invoiceReq) 241 require.NoError(t.t, err) 242 _, err = alice.RouterClient.SendPaymentV2( 243 ctxb, &routerrpc.SendPaymentRequest{ 244 PaymentRequest: bobInvoice.PaymentRequest, 245 TimeoutSeconds: 60, 246 FeeLimitMAtoms: noFeeLimitMAtoms, 247 }, 248 ) 249 require.NoError(t.t, err) 250 waitForInvoiceAccepted(t, bob, payHash) 251 _, err = bob.CancelInvoice(testctx.New(t), &invoicesrpc.CancelInvoiceMsg{PaymentHash: payHash[:]}) 252 require.NoError(t.t, err) 253 254 // Create but do not settle a payment (by using a hold invoice). 255 var ( 256 preimage2 = lntypes.Preimage{1, 2, 3, 4, 5, 6} 257 payHash2 = preimage2.Hash() 258 ) 259 invoiceReq2 := &invoicesrpc.AddHoldInvoiceRequest{ 260 Value: 30000, 261 CltvExpiry: 40, 262 Hash: payHash2[:], 263 } 264 265 bobInvoice2, err := bob.AddHoldInvoice(testctx.New(t), invoiceReq2) 266 require.NoError(t.t, err) 267 _, err = alice.RouterClient.SendPaymentV2( 268 ctxb, &routerrpc.SendPaymentRequest{ 269 PaymentRequest: bobInvoice2.PaymentRequest, 270 TimeoutSeconds: 60, 271 FeeLimitMAtoms: noFeeLimitMAtoms, 272 }, 273 ) 274 require.NoError(t.t, err) 275 waitForInvoiceAccepted(t, bob, payHash2) 276 277 // Fetch the payment stats. 278 payStats, err := alice.CalcPaymentStats(testctx.WithTimeout(t, defaultTimeout), &lnrpc.CalcPaymentStatsRequest{}) 279 require.NoError(t.t, err) 280 require.Equal(t.t, uint64(7), payStats.Total) 281 require.Equal(t.t, uint64(5), payStats.Succeeded) 282 require.Equal(t.t, uint64(1), payStats.Failed) 283 require.Equal(t.t, uint64(7), payStats.HtlcAttempts) 284 require.Equal(t.t, uint64(1), payStats.HtlcFailed) 285 require.Equal(t.t, uint64(5), payStats.HtlcSettled) 286 require.Equal(t.t, uint64(0), payStats.OldDupePayments) 287 288 // Clean up the channel. 289 _, err = bob.CancelInvoice(testctx.New(t), &invoicesrpc.CancelInvoiceMsg{PaymentHash: payHash2[:]}) 290 require.NoError(t.t, err) 291 closeChannelAndAssert(t, net, alice, chanPoint, false) 292 }