lntest: add amp invoice test
This commit is contained in:
parent
4e93c1303c
commit
ef392dcd0c
@ -17,6 +17,152 @@ import (
|
|||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// testSendPaymentAMPInvoice tests that we can send an AMP payment to a
|
||||||
|
// specified AMP invoice using SendPaymentV2.
|
||||||
|
func testSendPaymentAMPInvoice(net *lntest.NetworkHarness, t *harnessTest) {
|
||||||
|
ctxb := context.Background()
|
||||||
|
|
||||||
|
ctx := newMppTestContext(t, net)
|
||||||
|
defer ctx.shutdownNodes()
|
||||||
|
|
||||||
|
const paymentAmt = btcutil.Amount(300000)
|
||||||
|
|
||||||
|
// Set up a network with three different paths Alice <-> Bob. Channel
|
||||||
|
// capacities are set such that the payment can only succeed if (at
|
||||||
|
// least) three paths are used.
|
||||||
|
//
|
||||||
|
// _ Eve _
|
||||||
|
// / \
|
||||||
|
// Alice -- Carol ---- Bob
|
||||||
|
// \ /
|
||||||
|
// \__ Dave ____/
|
||||||
|
//
|
||||||
|
ctx.openChannel(ctx.carol, ctx.bob, 135000)
|
||||||
|
ctx.openChannel(ctx.alice, ctx.carol, 235000)
|
||||||
|
ctx.openChannel(ctx.dave, ctx.bob, 135000)
|
||||||
|
ctx.openChannel(ctx.alice, ctx.dave, 135000)
|
||||||
|
ctx.openChannel(ctx.eve, ctx.bob, 135000)
|
||||||
|
ctx.openChannel(ctx.carol, ctx.eve, 135000)
|
||||||
|
|
||||||
|
defer ctx.closeChannels()
|
||||||
|
|
||||||
|
ctx.waitForChannels()
|
||||||
|
|
||||||
|
// Subscribe to bob's invoices.
|
||||||
|
req := &lnrpc.InvoiceSubscription{}
|
||||||
|
ctxc, cancelSubscription := context.WithCancel(ctxb)
|
||||||
|
bobInvoiceSubscription, err := ctx.bob.SubscribeInvoices(ctxc, req)
|
||||||
|
require.NoError(t.t, err)
|
||||||
|
defer cancelSubscription()
|
||||||
|
|
||||||
|
addInvoiceResp, err := ctx.bob.AddInvoice(context.Background(), &lnrpc.Invoice{
|
||||||
|
Value: int64(paymentAmt),
|
||||||
|
IsAmp: true,
|
||||||
|
})
|
||||||
|
require.NoError(t.t, err)
|
||||||
|
|
||||||
|
// Ensure we get a notification of the invoice being added by Bob.
|
||||||
|
rpcInvoice, err := bobInvoiceSubscription.Recv()
|
||||||
|
require.NoError(t.t, err)
|
||||||
|
|
||||||
|
require.False(t.t, rpcInvoice.Settled) // nolint:staticcheck
|
||||||
|
require.Equal(t.t, lnrpc.Invoice_OPEN, rpcInvoice.State)
|
||||||
|
require.Equal(t.t, int64(0), rpcInvoice.AmtPaidSat)
|
||||||
|
require.Equal(t.t, int64(0), rpcInvoice.AmtPaidMsat)
|
||||||
|
|
||||||
|
require.Equal(t.t, 0, len(rpcInvoice.Htlcs))
|
||||||
|
|
||||||
|
// Increase Dave's fee to make the test deterministic. Otherwise it
|
||||||
|
// would be unpredictable whether pathfinding would go through Charlie
|
||||||
|
// or Dave for the first shard.
|
||||||
|
_, err = ctx.dave.UpdateChannelPolicy(
|
||||||
|
context.Background(),
|
||||||
|
&lnrpc.PolicyUpdateRequest{
|
||||||
|
Scope: &lnrpc.PolicyUpdateRequest_Global{Global: true},
|
||||||
|
BaseFeeMsat: 500000,
|
||||||
|
FeeRate: 0.001,
|
||||||
|
TimeLockDelta: 40,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("dave policy update: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ctxt, _ := context.WithTimeout(context.Background(), 4*defaultTimeout)
|
||||||
|
payment := sendAndAssertSuccess(
|
||||||
|
ctxt, t, ctx.alice,
|
||||||
|
&routerrpc.SendPaymentRequest{
|
||||||
|
PaymentRequest: addInvoiceResp.PaymentRequest,
|
||||||
|
TimeoutSeconds: 60,
|
||||||
|
FeeLimitMsat: noFeeLimitMsat,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
// Check that Alice split the payment in at least three shards. Because
|
||||||
|
// the hand-off of the htlc to the link is asynchronous (via a mailbox),
|
||||||
|
// there is some non-determinism in the process. Depending on whether
|
||||||
|
// the new pathfinding round is started before or after the htlc is
|
||||||
|
// locked into the channel, different sharding may occur. Therefore we
|
||||||
|
// can only check if the number of shards isn't below the theoretical
|
||||||
|
// minimum.
|
||||||
|
succeeded := 0
|
||||||
|
for _, htlc := range payment.Htlcs {
|
||||||
|
if htlc.Status == lnrpc.HTLCAttempt_SUCCEEDED {
|
||||||
|
succeeded++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const minExpectedShards = 3
|
||||||
|
if succeeded < minExpectedShards {
|
||||||
|
t.Fatalf("expected at least %v shards, but got %v",
|
||||||
|
minExpectedShards, succeeded)
|
||||||
|
}
|
||||||
|
|
||||||
|
// There should now be a settle event for the invoice.
|
||||||
|
rpcInvoice, err = bobInvoiceSubscription.Recv()
|
||||||
|
require.NoError(t.t, err)
|
||||||
|
|
||||||
|
// Also fetch Bob's invoice from ListInvoices and assert it is equal to
|
||||||
|
// the one recevied via the subscription.
|
||||||
|
invoiceResp, err := ctx.bob.ListInvoices(
|
||||||
|
ctxb, &lnrpc.ListInvoiceRequest{},
|
||||||
|
)
|
||||||
|
require.NoError(t.t, err)
|
||||||
|
require.Equal(t.t, 1, len(invoiceResp.Invoices))
|
||||||
|
assertInvoiceEqual(t.t, rpcInvoice, invoiceResp.Invoices[0])
|
||||||
|
|
||||||
|
// Assert that the invoice is settled for the total payment amount and
|
||||||
|
// has the correct payment address.
|
||||||
|
require.True(t.t, rpcInvoice.Settled) // nolint:staticcheck
|
||||||
|
require.Equal(t.t, lnrpc.Invoice_SETTLED, rpcInvoice.State)
|
||||||
|
require.Equal(t.t, int64(paymentAmt), rpcInvoice.AmtPaidSat)
|
||||||
|
require.Equal(t.t, int64(paymentAmt*1000), rpcInvoice.AmtPaidMsat)
|
||||||
|
|
||||||
|
// Finally, assert that the same set id is recorded for each htlc, and
|
||||||
|
// that the preimage hash pair is valid.
|
||||||
|
var setID []byte
|
||||||
|
require.Equal(t.t, succeeded, len(rpcInvoice.Htlcs))
|
||||||
|
for _, htlc := range rpcInvoice.Htlcs {
|
||||||
|
require.NotNil(t.t, htlc.Amp)
|
||||||
|
if setID == nil {
|
||||||
|
setID = make([]byte, 32)
|
||||||
|
copy(setID, htlc.Amp.SetId)
|
||||||
|
}
|
||||||
|
require.Equal(t.t, setID, htlc.Amp.SetId)
|
||||||
|
|
||||||
|
// Parse the child hash and child preimage, and assert they are
|
||||||
|
// well-formed.
|
||||||
|
childHash, err := lntypes.MakeHash(htlc.Amp.Hash)
|
||||||
|
require.NoError(t.t, err)
|
||||||
|
childPreimage, err := lntypes.MakePreimage(htlc.Amp.Preimage)
|
||||||
|
require.NoError(t.t, err)
|
||||||
|
|
||||||
|
// Assert that the preimage actually matches the hashes.
|
||||||
|
validPreimage := childPreimage.Matches(childHash)
|
||||||
|
require.True(t.t, validPreimage)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// testSendPaymentAMP tests that we can send an AMP payment to a specified
|
// testSendPaymentAMP tests that we can send an AMP payment to a specified
|
||||||
// destination using SendPaymentV2.
|
// destination using SendPaymentV2.
|
||||||
func testSendPaymentAMP(net *lntest.NetworkHarness, t *harnessTest) {
|
func testSendPaymentAMP(net *lntest.NetworkHarness, t *harnessTest) {
|
||||||
|
@ -278,7 +278,10 @@ var allTestCases = []*testCase{
|
|||||||
name: "sendpayment amp",
|
name: "sendpayment amp",
|
||||||
test: testSendPaymentAMP,
|
test: testSendPaymentAMP,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "sendpayment amp invoice",
|
||||||
|
test: testSendPaymentAMPInvoice,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "send multi path payment",
|
name: "send multi path payment",
|
||||||
test: testSendMultiPathPayment,
|
test: testSendMultiPathPayment,
|
||||||
|
Loading…
Reference in New Issue
Block a user