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  }