github.com/decred/dcrlnd@v0.7.6/lntest/itest/lnd_single_hop_invoice_test.go (about) 1 package itest 2 3 import ( 4 "bytes" 5 "context" 6 "encoding/hex" 7 "time" 8 9 "github.com/davecgh/go-spew/spew" 10 "github.com/decred/dcrd/dcrutil/v4" 11 "github.com/decred/dcrlnd/lnrpc" 12 "github.com/decred/dcrlnd/lnrpc/routerrpc" 13 "github.com/decred/dcrlnd/lntest" 14 "github.com/decred/dcrlnd/lntest/wait" 15 "github.com/decred/dcrlnd/lntypes" 16 "github.com/decred/dcrlnd/lnwire" 17 "github.com/decred/dcrlnd/record" 18 "github.com/stretchr/testify/require" 19 ) 20 21 func testSingleHopInvoice(net *lntest.NetworkHarness, t *harnessTest) { 22 ctxb := context.Background() 23 24 // Open a channel with 100k atoms between Alice and Bob with Alice being 25 // the sole funder of the channel. 26 chanAmt := dcrutil.Amount(300000) 27 chanPoint := openChannelAndAssert( 28 t, net, net.Alice, net.Bob, 29 lntest.OpenChannelParams{ 30 Amt: chanAmt, 31 }, 32 ) 33 34 // Now that the channel is open, create an invoice for Bob which 35 // expects a payment of 20000 atoms from Alice paid via a particular 36 // preimage. 37 const paymentAmt = 20000 38 preimage := bytes.Repeat([]byte("A"), 32) 39 invoice := &lnrpc.Invoice{ 40 Memo: "testing", 41 RPreimage: preimage, 42 Value: paymentAmt, 43 } 44 invoiceResp, err := net.Bob.AddInvoice(ctxb, invoice) 45 if err != nil { 46 t.Fatalf("unable to add invoice: %v", err) 47 } 48 49 // Wait for Alice to recognize and advertise the new channel generated 50 // above. 51 err = net.Alice.WaitForNetworkChannelOpen(chanPoint) 52 if err != nil { 53 t.Fatalf("alice didn't advertise channel before "+ 54 "timeout: %v", err) 55 } 56 err = net.Bob.WaitForNetworkChannelOpen(chanPoint) 57 if err != nil { 58 t.Fatalf("bob didn't advertise channel before "+ 59 "timeout: %v", err) 60 } 61 62 // With the invoice for Bob added, send a payment towards Alice paying 63 // to the above generated invoice. 64 resp := sendAndAssertSuccess( 65 t, net.Alice, &routerrpc.SendPaymentRequest{ 66 PaymentRequest: invoiceResp.PaymentRequest, 67 TimeoutSeconds: 60, 68 FeeLimitMAtoms: noFeeLimitMAtoms, 69 }, 70 ) 71 if hex.EncodeToString(preimage) != resp.PaymentPreimage { 72 t.Fatalf("preimage mismatch: expected %v, got %v", preimage, 73 resp.PaymentPreimage) 74 } 75 76 // Bob's invoice should now be found and marked as settled. 77 payHash := &lnrpc.PaymentHash{ 78 RHash: invoiceResp.RHash, 79 } 80 ctxt, _ := context.WithTimeout(ctxb, defaultTimeout) 81 dbInvoice, err := net.Bob.LookupInvoice(ctxt, payHash) 82 if err != nil { 83 t.Fatalf("unable to lookup invoice: %v", err) 84 } 85 if !dbInvoice.Settled { 86 t.Fatalf("bob's invoice should be marked as settled: %v", 87 spew.Sdump(dbInvoice)) 88 } 89 90 // With the payment completed all balance related stats should be 91 // properly updated. 92 err = wait.NoError( 93 assertAmountSent(paymentAmt, net.Alice, net.Bob), 94 3*time.Second, 95 ) 96 if err != nil { 97 t.Fatalf(err.Error()) 98 } 99 100 // Create another invoice for Bob, this time leaving off the preimage 101 // to one will be randomly generated. We'll test the proper 102 // encoding/decoding of the zpay32 payment requests. 103 invoice = &lnrpc.Invoice{ 104 Memo: "test3", 105 Value: paymentAmt, 106 } 107 ctxt, _ = context.WithTimeout(ctxb, defaultTimeout) 108 invoiceResp, err = net.Bob.AddInvoice(ctxt, invoice) 109 if err != nil { 110 t.Fatalf("unable to add invoice: %v", err) 111 } 112 113 // Next send another payment, but this time using a zpay32 encoded 114 // invoice rather than manually specifying the payment details. 115 sendAndAssertSuccess( 116 t, net.Alice, &routerrpc.SendPaymentRequest{ 117 PaymentRequest: invoiceResp.PaymentRequest, 118 TimeoutSeconds: 60, 119 FeeLimitMAtoms: noFeeLimitMAtoms, 120 }, 121 ) 122 123 // The second payment should also have succeeded, with the balances 124 // being update accordingly. 125 err = wait.NoError( 126 assertAmountSent(2*paymentAmt, net.Alice, net.Bob), 127 3*time.Second, 128 ) 129 if err != nil { 130 t.Fatalf(err.Error()) 131 } 132 133 // Next send a keysend payment. 134 keySendPreimage := lntypes.Preimage{3, 4, 5, 11} 135 keySendHash := keySendPreimage.Hash() 136 137 sendAndAssertSuccess( 138 t, net.Alice, &routerrpc.SendPaymentRequest{ 139 Dest: net.Bob.PubKey[:], 140 Amt: paymentAmt, 141 FinalCltvDelta: 40, 142 PaymentHash: keySendHash[:], 143 DestCustomRecords: map[uint64][]byte{ 144 record.KeySendType: keySendPreimage[:], 145 }, 146 TimeoutSeconds: 60, 147 FeeLimitMAtoms: noFeeLimitMAtoms, 148 }, 149 ) 150 151 // The keysend payment should also have succeeded, with the balances 152 // being update accordingly. 153 err = wait.NoError( 154 assertAmountSent(3*paymentAmt, net.Alice, net.Bob), 155 3*time.Second, 156 ) 157 if err != nil { 158 t.Fatalf(err.Error()) 159 } 160 161 // Assert that the invoice has the proper AMP fields set, since the 162 // legacy keysend payment should have been promoted into an AMP payment 163 // internally. 164 ctxt, _ = context.WithTimeout(ctxb, defaultTimeout) 165 keysendInvoice, err := net.Bob.LookupInvoice( 166 ctxt, &lnrpc.PaymentHash{ 167 RHash: keySendHash[:], 168 }, 169 ) 170 require.NoError(t.t, err) 171 require.Equal(t.t, 1, len(keysendInvoice.Htlcs)) 172 htlc := keysendInvoice.Htlcs[0] 173 require.Equal(t.t, uint64(0), htlc.MppTotalAmtMAtoms) 174 require.Nil(t.t, htlc.Amp) 175 176 // Now create an invoice and specify routing hints. 177 // We will test that the routing hints are encoded properly. 178 hintChannel := lnwire.ShortChannelID{BlockHeight: 10} 179 bobPubKey := hex.EncodeToString(net.Bob.PubKey[:]) 180 hints := []*lnrpc.RouteHint{ 181 { 182 HopHints: []*lnrpc.HopHint{ 183 { 184 NodeId: bobPubKey, 185 ChanId: hintChannel.ToUint64(), 186 FeeBaseMAtoms: 1, 187 FeeProportionalMillionths: 1000000, 188 CltvExpiryDelta: 20, 189 }, 190 }, 191 }, 192 } 193 194 invoice = &lnrpc.Invoice{ 195 Memo: "hints", 196 Value: paymentAmt, 197 RouteHints: hints, 198 } 199 200 ctxt, _ = context.WithTimeout(ctxb, defaultTimeout) 201 invoiceResp, err = net.Bob.AddInvoice(ctxt, invoice) 202 if err != nil { 203 t.Fatalf("unable to add invoice: %v", err) 204 } 205 payreq, err := net.Bob.DecodePayReq(ctxt, &lnrpc.PayReqString{PayReq: invoiceResp.PaymentRequest}) 206 if err != nil { 207 t.Fatalf("failed to decode payment request %v", err) 208 } 209 if len(payreq.RouteHints) != 1 { 210 t.Fatalf("expected one routing hint") 211 } 212 routingHint := payreq.RouteHints[0] 213 if len(routingHint.HopHints) != 1 { 214 t.Fatalf("expected one hop hint") 215 } 216 hopHint := routingHint.HopHints[0] 217 if hopHint.FeeProportionalMillionths != 1000000 { 218 t.Fatalf("wrong FeeProportionalMillionths %v", 219 hopHint.FeeProportionalMillionths) 220 } 221 if hopHint.NodeId != bobPubKey { 222 t.Fatalf("wrong NodeId %v", 223 hopHint.NodeId) 224 } 225 if hopHint.ChanId != hintChannel.ToUint64() { 226 t.Fatalf("wrong ChanId %v", 227 hopHint.ChanId) 228 } 229 if hopHint.FeeBaseMAtoms != 1 { 230 t.Fatalf("wrong FeeBaseMAtoms %v", 231 hopHint.FeeBaseMAtoms) 232 } 233 if hopHint.CltvExpiryDelta != 20 { 234 t.Fatalf("wrong CltvExpiryDelta %v", 235 hopHint.CltvExpiryDelta) 236 } 237 238 closeChannelAndAssert(t, net, net.Alice, chanPoint, false) 239 }