lntest: add AMP itest
This commit is contained in:
parent
888af51ab4
commit
730b718208
@ -2,7 +2,6 @@ package invoicesrpc
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/btcec"
|
"github.com/btcsuite/btcd/btcec"
|
||||||
@ -24,7 +23,7 @@ func decodePayReq(invoice *channeldb.Invoice,
|
|||||||
if paymentRequest == "" {
|
if paymentRequest == "" {
|
||||||
preimage := invoice.Terms.PaymentPreimage
|
preimage := invoice.Terms.PaymentPreimage
|
||||||
if preimage == nil {
|
if preimage == nil {
|
||||||
return nil, errors.New("cannot reconstruct pay req")
|
return &zpay32.Invoice{}, nil
|
||||||
}
|
}
|
||||||
hash := [32]byte(preimage.Hash())
|
hash := [32]byte(preimage.Hash())
|
||||||
return &zpay32.Invoice{
|
return &zpay32.Invoice{
|
||||||
@ -51,6 +50,11 @@ func CreateRPCInvoice(invoice *channeldb.Invoice,
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var rHash []byte
|
||||||
|
if decoded.PaymentHash != nil {
|
||||||
|
rHash = decoded.PaymentHash[:]
|
||||||
|
}
|
||||||
|
|
||||||
var descHash []byte
|
var descHash []byte
|
||||||
if decoded.DescriptionHash != nil {
|
if decoded.DescriptionHash != nil {
|
||||||
descHash = decoded.DescriptionHash[:]
|
descHash = decoded.DescriptionHash[:]
|
||||||
@ -144,8 +148,8 @@ func CreateRPCInvoice(invoice *channeldb.Invoice,
|
|||||||
}
|
}
|
||||||
|
|
||||||
rpcInvoice := &lnrpc.Invoice{
|
rpcInvoice := &lnrpc.Invoice{
|
||||||
Memo: string(invoice.Memo[:]),
|
Memo: string(invoice.Memo),
|
||||||
RHash: decoded.PaymentHash[:],
|
RHash: rHash,
|
||||||
Value: int64(satAmt),
|
Value: int64(satAmt),
|
||||||
ValueMsat: int64(invoice.Terms.Value),
|
ValueMsat: int64(invoice.Terms.Value),
|
||||||
CreationDate: invoice.CreationDate.Unix(),
|
CreationDate: invoice.CreationDate.Unix(),
|
||||||
|
208
lntest/itest/lnd_amp_test.go
Normal file
208
lntest/itest/lnd_amp_test.go
Normal file
@ -0,0 +1,208 @@
|
|||||||
|
package itest
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"crypto/rand"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/btcsuite/btcutil"
|
||||||
|
"github.com/lightningnetwork/lnd/amp"
|
||||||
|
"github.com/lightningnetwork/lnd/lnrpc"
|
||||||
|
"github.com/lightningnetwork/lnd/lnrpc/routerrpc"
|
||||||
|
"github.com/lightningnetwork/lnd/lntest"
|
||||||
|
"github.com/lightningnetwork/lnd/lntypes"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func testSendToRouteAMP(net *lntest.NetworkHarness, t *harnessTest) {
|
||||||
|
ctxb := context.Background()
|
||||||
|
|
||||||
|
ctx := newMppTestContext(t, net)
|
||||||
|
defer ctx.shutdownNodes()
|
||||||
|
|
||||||
|
const (
|
||||||
|
paymentAmt = btcutil.Amount(300000)
|
||||||
|
numShards = 3
|
||||||
|
shardAmt = paymentAmt / numShards
|
||||||
|
chanAmt = shardAmt * 3 / 2
|
||||||
|
)
|
||||||
|
|
||||||
|
// Set up a network with three different paths Alice <-> Bob.
|
||||||
|
// _ Eve _
|
||||||
|
// / \
|
||||||
|
// Alice -- Carol ---- Bob
|
||||||
|
// \ /
|
||||||
|
// \__ Dave ____/
|
||||||
|
//
|
||||||
|
ctx.openChannel(ctx.carol, ctx.bob, chanAmt)
|
||||||
|
ctx.openChannel(ctx.dave, ctx.bob, chanAmt)
|
||||||
|
ctx.openChannel(ctx.alice, ctx.dave, chanAmt)
|
||||||
|
ctx.openChannel(ctx.eve, ctx.bob, chanAmt)
|
||||||
|
ctx.openChannel(ctx.carol, ctx.eve, chanAmt)
|
||||||
|
|
||||||
|
// Since the channel Alice-> Carol will have to carry two
|
||||||
|
// shards, we make it larger.
|
||||||
|
ctx.openChannel(ctx.alice, ctx.carol, chanAmt+shardAmt)
|
||||||
|
|
||||||
|
defer ctx.closeChannels()
|
||||||
|
|
||||||
|
ctx.waitForChannels()
|
||||||
|
|
||||||
|
// We'll send shards along three routes from Alice.
|
||||||
|
sendRoutes := [numShards][]*lntest.HarnessNode{
|
||||||
|
{ctx.carol, ctx.bob},
|
||||||
|
{ctx.dave, ctx.bob},
|
||||||
|
{ctx.carol, ctx.eve, ctx.bob},
|
||||||
|
}
|
||||||
|
|
||||||
|
payAddr := make([]byte, 32)
|
||||||
|
_, err := rand.Read(payAddr)
|
||||||
|
require.NoError(t.t, err)
|
||||||
|
|
||||||
|
setID := make([]byte, 32)
|
||||||
|
_, err = rand.Read(setID)
|
||||||
|
require.NoError(t.t, err)
|
||||||
|
|
||||||
|
var sharer amp.Sharer
|
||||||
|
sharer, err = amp.NewSeedSharer()
|
||||||
|
require.NoError(t.t, err)
|
||||||
|
|
||||||
|
childPreimages := make(map[lntypes.Preimage]uint32)
|
||||||
|
responses := make(chan *lnrpc.HTLCAttempt, len(sendRoutes))
|
||||||
|
for i, hops := range sendRoutes {
|
||||||
|
// Build a route for the specified hops.
|
||||||
|
r, err := ctx.buildRoute(ctxb, shardAmt, net.Alice, hops)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unable to build route: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the MPP records to indicate this is a payment shard.
|
||||||
|
hop := r.Hops[len(r.Hops)-1]
|
||||||
|
hop.TlvPayload = true
|
||||||
|
hop.MppRecord = &lnrpc.MPPRecord{
|
||||||
|
PaymentAddr: payAddr,
|
||||||
|
TotalAmtMsat: int64(paymentAmt * 1000),
|
||||||
|
}
|
||||||
|
|
||||||
|
var child *amp.Child
|
||||||
|
if i < len(sendRoutes)-1 {
|
||||||
|
var left amp.Sharer
|
||||||
|
left, sharer, err = sharer.Split()
|
||||||
|
require.NoError(t.t, err)
|
||||||
|
|
||||||
|
child = left.Child(uint32(i))
|
||||||
|
} else {
|
||||||
|
child = sharer.Child(uint32(i))
|
||||||
|
}
|
||||||
|
childPreimages[child.Preimage] = child.Index
|
||||||
|
|
||||||
|
hop.AmpRecord = &lnrpc.AMPRecord{
|
||||||
|
RootShare: child.Share[:],
|
||||||
|
SetId: setID,
|
||||||
|
ChildIndex: child.Index,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send the shard.
|
||||||
|
sendReq := &routerrpc.SendToRouteRequest{
|
||||||
|
PaymentHash: child.Hash[:],
|
||||||
|
Route: r,
|
||||||
|
}
|
||||||
|
|
||||||
|
// We'll send all shards in their own goroutine, since SendToRoute will
|
||||||
|
// block as long as the payment is in flight.
|
||||||
|
go func() {
|
||||||
|
ctxt, _ := context.WithTimeout(ctxb, defaultTimeout)
|
||||||
|
resp, err := net.Alice.RouterClient.SendToRouteV2(ctxt, sendReq)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unable to send payment: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
responses <- resp
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assert that all of the child preimages are unique.
|
||||||
|
require.Equal(t.t, len(sendRoutes), len(childPreimages))
|
||||||
|
|
||||||
|
// Make a copy of the childPreimages map for validating the resulting
|
||||||
|
// invoice.
|
||||||
|
childPreimagesCopy := make(map[lntypes.Preimage]uint32)
|
||||||
|
for preimage, childIndex := range childPreimages {
|
||||||
|
childPreimagesCopy[preimage] = childIndex
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for all responses to be back, and check that they all
|
||||||
|
// succeeded.
|
||||||
|
for range sendRoutes {
|
||||||
|
var resp *lnrpc.HTLCAttempt
|
||||||
|
select {
|
||||||
|
case resp = <-responses:
|
||||||
|
case <-time.After(defaultTimeout):
|
||||||
|
t.Fatalf("response not received")
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp.Failure != nil {
|
||||||
|
t.Fatalf("received payment failure : %v", resp.Failure)
|
||||||
|
}
|
||||||
|
|
||||||
|
preimage, err := lntypes.MakePreimage(resp.Preimage)
|
||||||
|
require.NoError(t.t, err)
|
||||||
|
|
||||||
|
// Assert that the response includes one of our child preimages.
|
||||||
|
_, ok := childPreimages[preimage]
|
||||||
|
require.True(t.t, ok)
|
||||||
|
|
||||||
|
// Remove this preimage from out set so that we ensure all
|
||||||
|
// responses have a unique child preimage.
|
||||||
|
delete(childPreimages, preimage)
|
||||||
|
}
|
||||||
|
childPreimages = childPreimagesCopy
|
||||||
|
|
||||||
|
// Fetch Bob's invoices.
|
||||||
|
invoiceResp, err := net.Bob.ListInvoices(
|
||||||
|
ctxb, &lnrpc.ListInvoiceRequest{},
|
||||||
|
)
|
||||||
|
require.NoError(t.t, err)
|
||||||
|
|
||||||
|
// There should only be one invoice.
|
||||||
|
require.Equal(t.t, 1, len(invoiceResp.Invoices))
|
||||||
|
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)
|
||||||
|
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)
|
||||||
|
require.Equal(t.t, payAddr, rpcInvoice.PaymentAddr)
|
||||||
|
|
||||||
|
// Finally, assert that the proper set id is recorded for each htlc, and
|
||||||
|
// that the preimage hash pair is valid.
|
||||||
|
require.Equal(t.t, numShards, len(rpcInvoice.Htlcs))
|
||||||
|
for _, htlc := range rpcInvoice.Htlcs {
|
||||||
|
require.NotNil(t.t, htlc.Amp)
|
||||||
|
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)
|
||||||
|
|
||||||
|
// Assert that the HTLC includes one of our child preimages.
|
||||||
|
childIndex, ok := childPreimages[childPreimage]
|
||||||
|
require.True(t.t, ok)
|
||||||
|
|
||||||
|
// Assert that the correct child index is reflected.
|
||||||
|
require.Equal(t.t, childIndex, htlc.Amp.ChildIndex)
|
||||||
|
|
||||||
|
// Remove this preimage from our set so that we ensure all HTLCs
|
||||||
|
// have a unique child preimage.
|
||||||
|
delete(childPreimages, childPreimage)
|
||||||
|
}
|
||||||
|
}
|
@ -266,6 +266,10 @@ var allTestCases = []*testCase{
|
|||||||
name: "sendtoroute multi path payment",
|
name: "sendtoroute multi path payment",
|
||||||
test: testSendToRouteMultiPath,
|
test: testSendToRouteMultiPath,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "sendtoroute amp",
|
||||||
|
test: testSendToRouteAMP,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "send multi path payment",
|
name: "send multi path payment",
|
||||||
test: testSendMultiPathPayment,
|
test: testSendMultiPathPayment,
|
||||||
|
Loading…
Reference in New Issue
Block a user