github.com/decred/dcrlnd@v0.7.6/lntest/itest/lnd_send_multi_path_payment_test.go (about)

     1  package itest
     2  
     3  import (
     4  	"context"
     5  	"encoding/hex"
     6  
     7  	"github.com/decred/dcrd/dcrutil/v4"
     8  	"github.com/decred/dcrlnd/lnrpc"
     9  	"github.com/decred/dcrlnd/lnrpc/routerrpc"
    10  	"github.com/decred/dcrlnd/lntest"
    11  )
    12  
    13  // testSendMultiPathPayment tests that we are able to successfully route a
    14  // payment using multiple shards across different paths.
    15  func testSendMultiPathPayment(net *lntest.NetworkHarness, t *harnessTest) {
    16  	ctxb := context.Background()
    17  
    18  	ctx := newMppTestContext(t, net)
    19  	defer ctx.shutdownNodes()
    20  
    21  	const paymentAmt = dcrutil.Amount(300000)
    22  
    23  	// Set up a network with three different paths Alice <-> Bob. Channel
    24  	// capacities are set such that the payment can only succeed if (at
    25  	// least) three paths are used.
    26  	//
    27  	//              _ Eve _
    28  	//             /       \
    29  	// Alice -- Carol ---- Bob
    30  	//      \              /
    31  	//       \__ Dave ____/
    32  	//
    33  	ctx.openChannel(ctx.carol, ctx.bob, 135000)
    34  	ctx.openChannel(ctx.alice, ctx.carol, 235000)
    35  	ctx.openChannel(ctx.dave, ctx.bob, 135000)
    36  	ctx.openChannel(ctx.alice, ctx.dave, 135000)
    37  	ctx.openChannel(ctx.eve, ctx.bob, 135000)
    38  	ctx.openChannel(ctx.carol, ctx.eve, 135000)
    39  
    40  	defer ctx.closeChannels()
    41  
    42  	ctx.waitForChannels()
    43  
    44  	// Increase Dave's fee to make the test deterministic. Otherwise it
    45  	// would be unpredictable whether pathfinding would go through Charlie
    46  	// or Dave for the first shard.
    47  	_, err := ctx.dave.UpdateChannelPolicy(
    48  		context.Background(),
    49  		&lnrpc.PolicyUpdateRequest{
    50  			Scope:         &lnrpc.PolicyUpdateRequest_Global{Global: true},
    51  			BaseFeeMAtoms: 500000,
    52  			FeeRate:       0.001,
    53  			TimeLockDelta: 40,
    54  		},
    55  	)
    56  	if err != nil {
    57  		t.Fatalf("dave policy update: %v", err)
    58  	}
    59  	// Our first test will be Alice paying Bob using a SendPayment call.
    60  	// Let Bob create an invoice for Alice to pay.
    61  	payReqs, rHashes, invoices, err := createPayReqs(
    62  		ctx.bob, paymentAmt, 1,
    63  	)
    64  	if err != nil {
    65  		t.Fatalf("unable to create pay reqs: %v", err)
    66  	}
    67  
    68  	rHash := rHashes[0]
    69  	payReq := payReqs[0]
    70  
    71  	payment := sendAndAssertSuccess(
    72  		t, ctx.alice, &routerrpc.SendPaymentRequest{
    73  			PaymentRequest: payReq,
    74  			MaxParts:       10,
    75  			TimeoutSeconds: 60,
    76  			FeeLimitMAtoms: noFeeLimitMAtoms,
    77  		},
    78  	)
    79  
    80  	// Make sure we got the preimage.
    81  	if payment.PaymentPreimage != hex.EncodeToString(invoices[0].RPreimage) {
    82  		t.Fatalf("preimage doesn't match")
    83  	}
    84  
    85  	// Check that Alice split the payment in at least three shards. Because
    86  	// the hand-off of the htlc to the link is asynchronous (via a mailbox),
    87  	// there is some non-determinism in the process. Depending on whether
    88  	// the new pathfinding round is started before or after the htlc is
    89  	// locked into the channel, different sharding may occur. Therefore we
    90  	// can only check if the number of shards isn't below the theoretical
    91  	// minimum.
    92  	succeeded := 0
    93  	for _, htlc := range payment.Htlcs {
    94  		if htlc.Status == lnrpc.HTLCAttempt_SUCCEEDED {
    95  			succeeded++
    96  		}
    97  	}
    98  
    99  	const minExpectedShards = 3
   100  	if succeeded < minExpectedShards {
   101  		t.Fatalf("expected at least %v shards, but got %v",
   102  			minExpectedShards, succeeded)
   103  	}
   104  
   105  	// Make sure Bob show the invoice as settled for the full
   106  	// amount.
   107  	ctxt, _ := context.WithTimeout(ctxb, defaultTimeout)
   108  	inv, err := ctx.bob.LookupInvoice(
   109  		ctxt, &lnrpc.PaymentHash{
   110  			RHash: rHash,
   111  		},
   112  	)
   113  	if err != nil {
   114  		t.Fatalf("error when obtaining invoice: %v", err)
   115  	}
   116  
   117  	if inv.AmtPaidAtoms != int64(paymentAmt) {
   118  		t.Fatalf("incorrect payment amt for invoice"+
   119  			"want: %d, got %d",
   120  			paymentAmt, inv.AmtPaidAtoms)
   121  	}
   122  
   123  	if inv.State != lnrpc.Invoice_SETTLED {
   124  		t.Fatalf("Invoice not settled: %v", inv.State)
   125  	}
   126  
   127  	settled := 0
   128  	for _, htlc := range inv.Htlcs {
   129  		if htlc.State == lnrpc.InvoiceHTLCState_SETTLED {
   130  			settled++
   131  		}
   132  
   133  	}
   134  	if settled != succeeded {
   135  		t.Fatalf("expected invoice to be settled "+
   136  			"with %v HTLCs, had %v", succeeded, settled)
   137  	}
   138  }