|
|
@ -1,7 +1,6 @@
|
|
|
|
package itest
|
|
|
|
package itest
|
|
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
|
|
|
|
"context"
|
|
|
|
"context"
|
|
|
|
"encoding/hex"
|
|
|
|
"encoding/hex"
|
|
|
|
"fmt"
|
|
|
|
"fmt"
|
|
|
@ -14,6 +13,7 @@ import (
|
|
|
|
"github.com/lightningnetwork/lnd/chainreg"
|
|
|
|
"github.com/lightningnetwork/lnd/chainreg"
|
|
|
|
"github.com/lightningnetwork/lnd/lnrpc"
|
|
|
|
"github.com/lightningnetwork/lnd/lnrpc"
|
|
|
|
"github.com/lightningnetwork/lnd/lnrpc/routerrpc"
|
|
|
|
"github.com/lightningnetwork/lnd/lnrpc/routerrpc"
|
|
|
|
|
|
|
|
"github.com/lightningnetwork/lnd/lntemp"
|
|
|
|
"github.com/lightningnetwork/lnd/lntest"
|
|
|
|
"github.com/lightningnetwork/lnd/lntest"
|
|
|
|
"github.com/lightningnetwork/lnd/lntest/wait"
|
|
|
|
"github.com/lightningnetwork/lnd/lntest/wait"
|
|
|
|
"github.com/lightningnetwork/lnd/lnwire"
|
|
|
|
"github.com/lightningnetwork/lnd/lnwire"
|
|
|
@ -68,23 +68,18 @@ var singleHopSendToRouteCases = []singleHopSendToRouteCase{
|
|
|
|
// by feeding the route back into the various SendToRoute RPC methods. Here we
|
|
|
|
// by feeding the route back into the various SendToRoute RPC methods. Here we
|
|
|
|
// test all three SendToRoute endpoints, forcing each to perform both a regular
|
|
|
|
// test all three SendToRoute endpoints, forcing each to perform both a regular
|
|
|
|
// payment and an MPP payment.
|
|
|
|
// payment and an MPP payment.
|
|
|
|
func testSingleHopSendToRoute(net *lntest.NetworkHarness, t *harnessTest) {
|
|
|
|
func testSingleHopSendToRoute(ht *lntemp.HarnessTest) {
|
|
|
|
for _, test := range singleHopSendToRouteCases {
|
|
|
|
for _, test := range singleHopSendToRouteCases {
|
|
|
|
test := test
|
|
|
|
test := test
|
|
|
|
|
|
|
|
|
|
|
|
t.t.Run(test.name, func(t1 *testing.T) {
|
|
|
|
ht.Run(test.name, func(t1 *testing.T) {
|
|
|
|
ht := newHarnessTest(t1, t.lndHarness)
|
|
|
|
st := ht.Subtest(t1)
|
|
|
|
ht.RunTestCase(&testCase{
|
|
|
|
testSingleHopSendToRouteCase(st, test)
|
|
|
|
name: test.name,
|
|
|
|
|
|
|
|
test: func(_ *lntest.NetworkHarness, tt *harnessTest) {
|
|
|
|
|
|
|
|
testSingleHopSendToRouteCase(net, tt, test)
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
})
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func testSingleHopSendToRouteCase(net *lntest.NetworkHarness, t *harnessTest,
|
|
|
|
func testSingleHopSendToRouteCase(ht *lntemp.HarnessTest,
|
|
|
|
test singleHopSendToRouteCase) {
|
|
|
|
test singleHopSendToRouteCase) {
|
|
|
|
|
|
|
|
|
|
|
|
const chanAmt = btcutil.Amount(100000)
|
|
|
|
const chanAmt = btcutil.Amount(100000)
|
|
|
@ -92,96 +87,41 @@ func testSingleHopSendToRouteCase(net *lntest.NetworkHarness, t *harnessTest,
|
|
|
|
const numPayments = 5
|
|
|
|
const numPayments = 5
|
|
|
|
const amountPaid = int64(numPayments * paymentAmtSat)
|
|
|
|
const amountPaid = int64(numPayments * paymentAmtSat)
|
|
|
|
|
|
|
|
|
|
|
|
ctxb := context.Background()
|
|
|
|
|
|
|
|
var networkChans []*lnrpc.ChannelPoint
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Create Carol and Dave, then establish a channel between them. Carol
|
|
|
|
// Create Carol and Dave, then establish a channel between them. Carol
|
|
|
|
// is the sole funder of the channel with 100k satoshis. The network
|
|
|
|
// is the sole funder of the channel with 100k satoshis. The network
|
|
|
|
// topology should look like:
|
|
|
|
// topology should look like:
|
|
|
|
// Carol -> 100k -> Dave
|
|
|
|
// Carol -> 100k -> Dave
|
|
|
|
carol := net.NewNode(t.t, "Carol", nil)
|
|
|
|
carol := ht.NewNode("Carol", nil)
|
|
|
|
defer shutdownAndAssert(net, t, carol)
|
|
|
|
dave := ht.NewNode("Dave", nil)
|
|
|
|
|
|
|
|
|
|
|
|
dave := net.NewNode(t.t, "Dave", nil)
|
|
|
|
ht.ConnectNodes(carol, dave)
|
|
|
|
defer shutdownAndAssert(net, t, dave)
|
|
|
|
ht.FundCoins(btcutil.SatoshiPerBitcoin, carol)
|
|
|
|
|
|
|
|
|
|
|
|
net.ConnectNodes(t.t, carol, dave)
|
|
|
|
|
|
|
|
net.SendCoins(t.t, btcutil.SatoshiPerBitcoin, carol)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Open a channel with 100k satoshis between Carol and Dave with Carol
|
|
|
|
// Open a channel with 100k satoshis between Carol and Dave with Carol
|
|
|
|
// being the sole funder of the channel.
|
|
|
|
// being the sole funder of the channel.
|
|
|
|
chanPointCarol := openChannelAndAssert(
|
|
|
|
chanPointCarol := ht.OpenChannel(
|
|
|
|
t, net, carol, dave,
|
|
|
|
carol, dave, lntemp.OpenChannelParams{Amt: chanAmt},
|
|
|
|
lntest.OpenChannelParams{
|
|
|
|
|
|
|
|
Amt: chanAmt,
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
)
|
|
|
|
)
|
|
|
|
networkChans = append(networkChans, chanPointCarol)
|
|
|
|
defer ht.CloseChannel(carol, chanPointCarol)
|
|
|
|
|
|
|
|
|
|
|
|
carolChanTXID, err := lnrpc.GetChanPointFundingTxid(chanPointCarol)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
|
|
t.Fatalf("unable to get txid: %v", err)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
carolFundPoint := wire.OutPoint{
|
|
|
|
|
|
|
|
Hash: *carolChanTXID,
|
|
|
|
|
|
|
|
Index: chanPointCarol.OutputIndex,
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Wait for all nodes to have seen all channels.
|
|
|
|
|
|
|
|
nodes := []*lntest.HarnessNode{carol, dave}
|
|
|
|
|
|
|
|
for _, chanPoint := range networkChans {
|
|
|
|
|
|
|
|
for _, node := range nodes {
|
|
|
|
|
|
|
|
txid, err := lnrpc.GetChanPointFundingTxid(chanPoint)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
|
|
t.Fatalf("unable to get txid: %v", err)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
point := wire.OutPoint{
|
|
|
|
|
|
|
|
Hash: *txid,
|
|
|
|
|
|
|
|
Index: chanPoint.OutputIndex,
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
err = node.WaitForNetworkChannelOpen(chanPoint)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
|
|
t.Fatalf("%s(%d): timeout waiting for "+
|
|
|
|
|
|
|
|
"channel(%s) open: %v", node.Name(),
|
|
|
|
|
|
|
|
node.NodeID, point, err)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Create invoices for Dave, which expect a payment from Carol.
|
|
|
|
// Create invoices for Dave, which expect a payment from Carol.
|
|
|
|
payReqs, rHashes, _, err := createPayReqs(
|
|
|
|
payReqs, rHashes, _ := ht.CreatePayReqs(
|
|
|
|
dave, paymentAmtSat, numPayments,
|
|
|
|
dave, paymentAmtSat, numPayments,
|
|
|
|
)
|
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
|
|
|
|
t.Fatalf("unable to create pay reqs: %v", err)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Reconstruct payment addresses.
|
|
|
|
// Reconstruct payment addresses.
|
|
|
|
var payAddrs [][]byte
|
|
|
|
var payAddrs [][]byte
|
|
|
|
for _, payReq := range payReqs {
|
|
|
|
for _, payReq := range payReqs {
|
|
|
|
ctx, _ := context.WithTimeout(
|
|
|
|
resp := dave.RPC.DecodePayReq(payReq)
|
|
|
|
context.Background(), defaultTimeout,
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
resp, err := dave.DecodePayReq(
|
|
|
|
|
|
|
|
ctx,
|
|
|
|
|
|
|
|
&lnrpc.PayReqString{PayReq: payReq},
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
|
|
t.Fatalf("decode pay req: %v", err)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
payAddrs = append(payAddrs, resp.PaymentAddr)
|
|
|
|
payAddrs = append(payAddrs, resp.PaymentAddr)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Assert Carol and Dave are synced to the chain before proceeding, to
|
|
|
|
// Assert Carol and Dave are synced to the chain before proceeding, to
|
|
|
|
// ensure the queried route will have a valid final CLTV once the HTLC
|
|
|
|
// ensure the queried route will have a valid final CLTV once the HTLC
|
|
|
|
// reaches Dave.
|
|
|
|
// reaches Dave.
|
|
|
|
_, minerHeight, err := net.Miner.Client.GetBestBlock()
|
|
|
|
_, minerHeight := ht.Miner.GetBestBlock()
|
|
|
|
if err != nil {
|
|
|
|
ht.WaitForNodeBlockHeight(carol, minerHeight)
|
|
|
|
t.Fatalf("unable to get best height: %v", err)
|
|
|
|
ht.WaitForNodeBlockHeight(dave, minerHeight)
|
|
|
|
}
|
|
|
|
|
|
|
|
require.NoError(t.t, waitForNodeBlockHeight(carol, minerHeight))
|
|
|
|
|
|
|
|
require.NoError(t.t, waitForNodeBlockHeight(dave, minerHeight))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Query for routes to pay from Carol to Dave using the default CLTV
|
|
|
|
// Query for routes to pay from Carol to Dave using the default CLTV
|
|
|
|
// config.
|
|
|
|
// config.
|
|
|
@ -189,12 +129,7 @@ func testSingleHopSendToRouteCase(net *lntest.NetworkHarness, t *harnessTest,
|
|
|
|
PubKey: dave.PubKeyStr,
|
|
|
|
PubKey: dave.PubKeyStr,
|
|
|
|
Amt: paymentAmtSat,
|
|
|
|
Amt: paymentAmtSat,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ctxt, _ := context.WithTimeout(ctxb, defaultTimeout)
|
|
|
|
routes := carol.RPC.QueryRoutes(routesReq)
|
|
|
|
routes, err := carol.QueryRoutes(ctxt, routesReq)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
|
|
t.Fatalf("unable to get route from %s: %v",
|
|
|
|
|
|
|
|
carol.Name(), err)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// There should only be one route to try, so take the first item.
|
|
|
|
// There should only be one route to try, so take the first item.
|
|
|
|
r := routes.Routes[0]
|
|
|
|
r := routes.Routes[0]
|
|
|
@ -222,27 +157,14 @@ func testSingleHopSendToRouteCase(net *lntest.NetworkHarness, t *harnessTest,
|
|
|
|
PaymentHash: rHash,
|
|
|
|
PaymentHash: rHash,
|
|
|
|
Route: r,
|
|
|
|
Route: r,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout)
|
|
|
|
resp := carol.RPC.SendToRouteSync(sendReq)
|
|
|
|
resp, err := carol.SendToRouteSync(
|
|
|
|
require.Emptyf(ht, resp.PaymentError,
|
|
|
|
ctxt, sendReq,
|
|
|
|
"received payment error from %s: %v",
|
|
|
|
)
|
|
|
|
carol.Name(), resp.PaymentError)
|
|
|
|
if err != nil {
|
|
|
|
|
|
|
|
t.Fatalf("unable to send to route for "+
|
|
|
|
|
|
|
|
"%s: %v", carol.Name(), err)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if resp.PaymentError != "" {
|
|
|
|
|
|
|
|
t.Fatalf("received payment error from %s: %v",
|
|
|
|
|
|
|
|
carol.Name(), resp.PaymentError)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
sendToRouteStream := func() {
|
|
|
|
sendToRouteStream := func() {
|
|
|
|
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout)
|
|
|
|
alicePayStream := carol.RPC.SendToRoute()
|
|
|
|
alicePayStream, err := carol.SendToRoute(ctxt) // nolint:staticcheck
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
|
|
t.Fatalf("unable to create payment stream for "+
|
|
|
|
|
|
|
|
"carol: %v", err)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for i, rHash := range rHashes {
|
|
|
|
for i, rHash := range rHashes {
|
|
|
|
setMPPFields(i)
|
|
|
|
setMPPFields(i)
|
|
|
@ -252,19 +174,13 @@ func testSingleHopSendToRouteCase(net *lntest.NetworkHarness, t *harnessTest,
|
|
|
|
Route: routes.Routes[0],
|
|
|
|
Route: routes.Routes[0],
|
|
|
|
}
|
|
|
|
}
|
|
|
|
err := alicePayStream.Send(sendReq)
|
|
|
|
err := alicePayStream.Send(sendReq)
|
|
|
|
|
|
|
|
require.NoError(ht, err, "unable to send payment")
|
|
|
|
if err != nil {
|
|
|
|
|
|
|
|
t.Fatalf("unable to send payment: %v", err)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
resp, err := alicePayStream.Recv()
|
|
|
|
resp, err := alicePayStream.Recv()
|
|
|
|
if err != nil {
|
|
|
|
require.NoError(ht, err, "unable to receive stream")
|
|
|
|
t.Fatalf("unable to send payment: %v", err)
|
|
|
|
require.Emptyf(ht, resp.PaymentError,
|
|
|
|
}
|
|
|
|
"received payment error from %s: %v",
|
|
|
|
if resp.PaymentError != "" {
|
|
|
|
carol.Name(), resp.PaymentError)
|
|
|
|
t.Fatalf("received payment error: %v",
|
|
|
|
|
|
|
|
resp.PaymentError)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
sendToRouteRouterRPC := func() {
|
|
|
|
sendToRouteRouterRPC := func() {
|
|
|
@ -275,23 +191,14 @@ func testSingleHopSendToRouteCase(net *lntest.NetworkHarness, t *harnessTest,
|
|
|
|
PaymentHash: rHash,
|
|
|
|
PaymentHash: rHash,
|
|
|
|
Route: r,
|
|
|
|
Route: r,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout)
|
|
|
|
resp := carol.RPC.SendToRouteV2(sendReq)
|
|
|
|
resp, err := carol.RouterClient.SendToRouteV2(
|
|
|
|
require.Nilf(ht, resp.Failure, "received payment "+
|
|
|
|
ctxt, sendReq,
|
|
|
|
"error from %s", carol.Name())
|
|
|
|
)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
|
|
t.Fatalf("unable to send to route for "+
|
|
|
|
|
|
|
|
"%s: %v", carol.Name(), err)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if resp.Failure != nil {
|
|
|
|
|
|
|
|
t.Fatalf("received payment error from %s: %v",
|
|
|
|
|
|
|
|
carol.Name(), resp.Failure)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Using Carol as the node as the source, send the payments
|
|
|
|
// Using Carol as the node as the source, send the payments
|
|
|
|
// synchronously via the the routerrpc's SendToRoute, or via the main RPC
|
|
|
|
// synchronously via the routerrpc's SendToRoute, or via the main RPC
|
|
|
|
// server's SendToRoute streaming or sync calls.
|
|
|
|
// server's SendToRoute streaming or sync calls.
|
|
|
|
switch {
|
|
|
|
switch {
|
|
|
|
case !test.routerrpc && test.streaming:
|
|
|
|
case !test.routerrpc && test.streaming:
|
|
|
@ -301,118 +208,70 @@ func testSingleHopSendToRouteCase(net *lntest.NetworkHarness, t *harnessTest,
|
|
|
|
case test.routerrpc && !test.streaming:
|
|
|
|
case test.routerrpc && !test.streaming:
|
|
|
|
sendToRouteRouterRPC()
|
|
|
|
sendToRouteRouterRPC()
|
|
|
|
default:
|
|
|
|
default:
|
|
|
|
t.Fatalf("routerrpc does not support streaming send_to_route")
|
|
|
|
require.Fail(ht, "routerrpc does not support "+
|
|
|
|
|
|
|
|
"streaming send_to_route")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Verify that the payment's from Carol's PoV have the correct payment
|
|
|
|
// Verify that the payment's from Carol's PoV have the correct payment
|
|
|
|
// hash and amount.
|
|
|
|
// hash and amount.
|
|
|
|
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout)
|
|
|
|
payments := ht.AssertNumPayments(carol, numPayments)
|
|
|
|
paymentsResp, err := carol.ListPayments(
|
|
|
|
|
|
|
|
ctxt, &lnrpc.ListPaymentsRequest{},
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
|
|
t.Fatalf("error when obtaining %s payments: %v",
|
|
|
|
|
|
|
|
carol.Name(), err)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(paymentsResp.Payments) != numPayments {
|
|
|
|
|
|
|
|
t.Fatalf("incorrect number of payments, got %v, want %v",
|
|
|
|
|
|
|
|
len(paymentsResp.Payments), numPayments)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for i, p := range paymentsResp.Payments {
|
|
|
|
for i, p := range payments {
|
|
|
|
// Assert that the payment hashes for each payment match up.
|
|
|
|
// Assert that the payment hashes for each payment match up.
|
|
|
|
rHashHex := hex.EncodeToString(rHashes[i])
|
|
|
|
rHashHex := hex.EncodeToString(rHashes[i])
|
|
|
|
if p.PaymentHash != rHashHex {
|
|
|
|
require.Equalf(ht, rHashHex, p.PaymentHash,
|
|
|
|
t.Fatalf("incorrect payment hash for payment %d, "+
|
|
|
|
"incorrect payment hash for payment %d", i)
|
|
|
|
"want: %s got: %s",
|
|
|
|
|
|
|
|
i, rHashHex, p.PaymentHash)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Assert that each payment has no invoice since the payment was
|
|
|
|
// Assert that each payment has no invoice since the payment
|
|
|
|
// completed using SendToRoute.
|
|
|
|
// was completed using SendToRoute.
|
|
|
|
if p.PaymentRequest != "" {
|
|
|
|
require.Emptyf(ht, p.PaymentRequest,
|
|
|
|
t.Fatalf("incorrect payment request for payment: %d, "+
|
|
|
|
"incorrect payment request for payment: %d", i)
|
|
|
|
"want: \"\", got: %s",
|
|
|
|
|
|
|
|
i, p.PaymentRequest)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Assert the payment amount is correct.
|
|
|
|
// Assert the payment amount is correct.
|
|
|
|
if p.ValueSat != paymentAmtSat {
|
|
|
|
require.EqualValues(ht, paymentAmtSat, p.ValueSat,
|
|
|
|
t.Fatalf("incorrect payment amt for payment %d, "+
|
|
|
|
"incorrect payment amt for payment %d, ", i)
|
|
|
|
"want: %d, got: %d",
|
|
|
|
|
|
|
|
i, paymentAmtSat, p.ValueSat)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Assert exactly one htlc was made.
|
|
|
|
// Assert exactly one htlc was made.
|
|
|
|
if len(p.Htlcs) != 1 {
|
|
|
|
require.Lenf(ht, p.Htlcs, 1,
|
|
|
|
t.Fatalf("expected 1 htlc for payment %d, got: %d",
|
|
|
|
"expected 1 htlc for payment %d", i)
|
|
|
|
i, len(p.Htlcs))
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Assert the htlc's route is populated.
|
|
|
|
// Assert the htlc's route is populated.
|
|
|
|
htlc := p.Htlcs[0]
|
|
|
|
htlc := p.Htlcs[0]
|
|
|
|
if htlc.Route == nil {
|
|
|
|
require.NotNilf(ht, htlc.Route,
|
|
|
|
t.Fatalf("expected route for payment %d", i)
|
|
|
|
"expected route for payment %d", i)
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Assert the hop has exactly one hop.
|
|
|
|
// Assert the hop has exactly one hop.
|
|
|
|
if len(htlc.Route.Hops) != 1 {
|
|
|
|
require.Lenf(ht, htlc.Route.Hops, 1,
|
|
|
|
t.Fatalf("expected 1 hop for payment %d, got: %d",
|
|
|
|
"expected 1 hop for payment %d", i)
|
|
|
|
i, len(htlc.Route.Hops))
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// If this is an MPP test, assert the MPP record's fields are
|
|
|
|
// If this is an MPP test, assert the MPP record's fields are
|
|
|
|
// properly populated. Otherwise the hop should not have an MPP
|
|
|
|
// properly populated. Otherwise the hop should not have an MPP
|
|
|
|
// record.
|
|
|
|
// record.
|
|
|
|
hop := htlc.Route.Hops[0]
|
|
|
|
hop := htlc.Route.Hops[0]
|
|
|
|
if hop.MppRecord == nil {
|
|
|
|
require.NotNilf(ht, hop.MppRecord,
|
|
|
|
t.Fatalf("expected mpp record for mpp payment")
|
|
|
|
"expected mpp record for mpp payment %d", i)
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if hop.MppRecord.TotalAmtMsat != paymentAmtSat*1000 {
|
|
|
|
require.EqualValues(ht, paymentAmtSat*1000,
|
|
|
|
t.Fatalf("incorrect mpp total msat for payment %d "+
|
|
|
|
hop.MppRecord.TotalAmtMsat,
|
|
|
|
"want: %d, got: %d",
|
|
|
|
"incorrect mpp total msat for payment %d", i)
|
|
|
|
i, paymentAmtSat*1000,
|
|
|
|
|
|
|
|
hop.MppRecord.TotalAmtMsat)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
expAddr := payAddrs[i]
|
|
|
|
expAddr := payAddrs[i]
|
|
|
|
if !bytes.Equal(hop.MppRecord.PaymentAddr, expAddr) {
|
|
|
|
require.Equal(ht, expAddr, hop.MppRecord.PaymentAddr,
|
|
|
|
t.Fatalf("incorrect mpp payment addr for payment %d "+
|
|
|
|
"incorrect mpp payment addr for payment %d ", i)
|
|
|
|
"want: %x, got: %x",
|
|
|
|
|
|
|
|
i, expAddr, hop.MppRecord.PaymentAddr)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Verify that the invoices's from Dave's PoV have the correct payment
|
|
|
|
// Verify that the invoices's from Dave's PoV have the correct payment
|
|
|
|
// hash and amount.
|
|
|
|
// hash and amount.
|
|
|
|
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout)
|
|
|
|
invoices := ht.AssertNumInvoices(dave, numPayments)
|
|
|
|
invoicesResp, err := dave.ListInvoices(
|
|
|
|
|
|
|
|
ctxt, &lnrpc.ListInvoiceRequest{},
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
|
|
t.Fatalf("error when obtaining %s payments: %v",
|
|
|
|
|
|
|
|
dave.Name(), err)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(invoicesResp.Invoices) != numPayments {
|
|
|
|
|
|
|
|
t.Fatalf("incorrect number of invoices, got %v, want %v",
|
|
|
|
|
|
|
|
len(invoicesResp.Invoices), numPayments)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for i, inv := range invoicesResp.Invoices {
|
|
|
|
for i, inv := range invoices {
|
|
|
|
// Assert that the payment hashes match up.
|
|
|
|
// Assert that the payment hashes match up.
|
|
|
|
if !bytes.Equal(inv.RHash, rHashes[i]) {
|
|
|
|
require.Equal(ht, rHashes[i], inv.RHash,
|
|
|
|
t.Fatalf("incorrect payment hash for invoice %d, "+
|
|
|
|
"incorrect payment hash for invoice %d", i)
|
|
|
|
"want: %x got: %x",
|
|
|
|
|
|
|
|
i, rHashes[i], inv.RHash)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Assert that the amount paid to the invoice is correct.
|
|
|
|
// Assert that the amount paid to the invoice is correct.
|
|
|
|
if inv.AmtPaidSat != paymentAmtSat {
|
|
|
|
require.EqualValues(ht, paymentAmtSat, inv.AmtPaidSat,
|
|
|
|
t.Fatalf("incorrect payment amt for invoice %d, "+
|
|
|
|
"incorrect payment amt for invoice %d, ", i)
|
|
|
|
"want: %d, got %d",
|
|
|
|
|
|
|
|
i, paymentAmtSat, inv.AmtPaidSat)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// At this point all the channels within our proto network should be
|
|
|
|
// At this point all the channels within our proto network should be
|
|
|
@ -420,12 +279,10 @@ func testSingleHopSendToRouteCase(net *lntest.NetworkHarness, t *harnessTest,
|
|
|
|
// payment flow generated above. The order of asserts corresponds to
|
|
|
|
// payment flow generated above. The order of asserts corresponds to
|
|
|
|
// increasing of time is needed to embed the HTLC in commitment
|
|
|
|
// increasing of time is needed to embed the HTLC in commitment
|
|
|
|
// transaction, in channel Carol->Dave, order is Dave and then Carol.
|
|
|
|
// transaction, in channel Carol->Dave, order is Dave and then Carol.
|
|
|
|
assertAmountPaid(t, "Carol(local) => Dave(remote)", dave,
|
|
|
|
ht.AssertAmountPaid("Carol(local) => Dave(remote)", dave,
|
|
|
|
carolFundPoint, int64(0), amountPaid)
|
|
|
|
chanPointCarol, int64(0), amountPaid)
|
|
|
|
assertAmountPaid(t, "Carol(local) => Dave(remote)", carol,
|
|
|
|
ht.AssertAmountPaid("Carol(local) => Dave(remote)", carol,
|
|
|
|
carolFundPoint, amountPaid, int64(0))
|
|
|
|
chanPointCarol, amountPaid, int64(0))
|
|
|
|
|
|
|
|
|
|
|
|
closeChannelAndAssert(t, net, carol, chanPointCarol, false)
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// testMultiHopSendToRoute tests that payments are properly processed
|
|
|
|
// testMultiHopSendToRoute tests that payments are properly processed
|
|
|
|