Skip to content

Instantly share code, notes, and snippets.

@bshramin
Created March 4, 2023 21:59
Show Gist options
  • Save bshramin/9a26a9dfefc23f8c22bf898fa623f247 to your computer and use it in GitHub Desktop.
Save bshramin/9a26a9dfefc23f8c22bf898fa623f247 to your computer and use it in GitHub Desktop.
LND multi-hop payment test
func testSingleHopInvoice(ht *lntemp.HarnessTest) {
// Open a channel with 100k satoshis between Alice and Bob with Alice
// being the sole funder of the channel.
chanAmt := btcutil.Amount(100000)
alice, bob := ht.Alice, ht.Bob
carol := ht.NewNode("Carol", nil)
cp := ht.OpenChannel(
alice, bob, lntemp.OpenChannelParams{Amt: chanAmt},
)
cp2 := ht.OpenChannel(
bob, carol, lntemp.OpenChannelParams{Amt: chanAmt},
)
// assertAmountPaid is a helper closure that asserts the amount paid by
// Alice and received by Bob are expected.
assertAmountPaid := func(expected int64) {
ht.AssertAmountPaid("alice -> bob", alice, cp, expected, 0)
ht.AssertAmountPaid("bob <- alice", bob, cp, 0, expected)
ht.AssertAmountPaid("carol -> bob", carol, cp, expected, 0)
ht.AssertAmountPaid("bob <- carol", bob, cp, 0, expected)
}
// Now that the channel is open, create an invoice for Carol which
// expects a payment of 1000 satoshis from Alice paid via a particular
// preimage.
const paymentAmt = 1000
preimage := bytes.Repeat([]byte("A"), 32)
invoice := &lnrpc.Invoice{
Memo: "testing",
RPreimage: preimage,
Value: paymentAmt,
}
invoiceResp := carol.RPC.AddInvoice(invoice)
// With the invoice for Carol added, send a payment towards Alice paying
// to the above generated invoice.
ht.CompletePaymentRequests(alice, []string{invoiceResp.PaymentRequest})
// Carol's invoice should now be found and marked as settled.
dbInvoice := bob.RPC.LookupInvoice(invoiceResp.RHash)
require.Equal(ht, lnrpc.Invoice_SETTLED, dbInvoice.State,
"carol's invoice should be marked as settled")
// With the payment completed all balance related stats should be
// properly updated.
assertAmountPaid(paymentAmt)
// Create another invoice for Carol, this time leaving off the preimage
// to one will be randomly generated. We'll test the proper
// encoding/decoding of the zpay32 payment requests.
invoice = &lnrpc.Invoice{
Memo: "test3",
Value: paymentAmt,
}
invoiceResp = carol.RPC.AddInvoice(invoice)
// Next send another payment, but this time using a zpay32 encoded
// invoice rather than manually specifying the payment details.
ht.CompletePaymentRequests(alice, []string{invoiceResp.PaymentRequest})
// The second payment should also have succeeded, with the balances
// being update accordingly.
assertAmountPaid(paymentAmt * 2)
// Next send a keysend payment.
keySendPreimage := lntypes.Preimage{3, 4, 5, 11}
keySendHash := keySendPreimage.Hash()
req := &routerrpc.SendPaymentRequest{
Dest: carol.PubKey[:],
Amt: paymentAmt,
FinalCltvDelta: 40,
PaymentHash: keySendHash[:],
DestCustomRecords: map[uint64][]byte{
record.KeySendType: keySendPreimage[:],
},
TimeoutSeconds: 60,
FeeLimitMsat: noFeeLimitMsat,
}
ht.SendPaymentAssertSettled(alice, req)
// The keysend payment should also have succeeded, with the balances
// being update accordingly.
assertAmountPaid(paymentAmt * 3)
// Assert that the invoice has the proper AMP fields set, since the
// legacy keysend payment should have been promoted into an AMP payment
// internally.
keysendInvoice := carol.RPC.LookupInvoice(keySendHash[:])
require.Len(ht, keysendInvoice.Htlcs, 1)
htlc := keysendInvoice.Htlcs[0]
require.Zero(ht, htlc.MppTotalAmtMsat)
require.Nil(ht, htlc.Amp)
// Now create an invoice and specify routing hints.
// We will test that the routing hints are encoded properly.
hintChannel := lnwire.ShortChannelID{BlockHeight: 10}
carolPubKey := hex.EncodeToString(carol.PubKey[:])
hint := &lnrpc.HopHint{
NodeId: carolPubKey,
ChanId: hintChannel.ToUint64(),
FeeBaseMsat: 1,
FeeProportionalMillionths: 1000000,
CltvExpiryDelta: 20,
}
hints := []*lnrpc.RouteHint{{HopHints: []*lnrpc.HopHint{hint}}}
invoice = &lnrpc.Invoice{
Memo: "hints",
Value: paymentAmt,
RouteHints: hints,
}
invoiceResp = carol.RPC.AddInvoice(invoice)
payreq := carol.RPC.DecodePayReq(invoiceResp.PaymentRequest)
require.Len(ht, payreq.RouteHints, 1, "expected one routing hint")
routingHint := payreq.RouteHints[0]
require.Len(ht, routingHint.HopHints, 1, "expected one hop hint")
hopHint := routingHint.HopHints[0]
require.EqualValues(ht, 1000000, hopHint.FeeProportionalMillionths,
"wrong FeeProportionalMillionths")
require.Equal(ht, carolPubKey, hopHint.NodeId, "wrong NodeId")
require.Equal(ht, hintChannel.ToUint64(), hopHint.ChanId,
"wrong ChanId")
require.EqualValues(ht, 1, hopHint.FeeBaseMsat, "wrong FeeBaseMsat")
require.EqualValues(ht, 20, hopHint.CltvExpiryDelta,
"wrong CltvExpiryDelta")
ht.CloseChannel(alice, cp)
ht.CloseChannel(carol, cp2)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment