itest: move payment related tests into one file
This commit creates a new file lnd_payment_test.go to hold all payment related tests, further breaking down the old large lnd_test.go.
This commit is contained in:
parent
b657a977e3
commit
f62cdf7bfc
821
lntest/itest/lnd_payment_test.go
Normal file
821
lntest/itest/lnd_payment_test.go
Normal file
@ -0,0 +1,821 @@
|
||||
package itest
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"reflect"
|
||||
"time"
|
||||
|
||||
"github.com/btcsuite/btcutil"
|
||||
"github.com/lightningnetwork/lnd/input"
|
||||
"github.com/lightningnetwork/lnd/lnrpc"
|
||||
"github.com/lightningnetwork/lnd/lnrpc/routerrpc"
|
||||
"github.com/lightningnetwork/lnd/lntest"
|
||||
"github.com/lightningnetwork/lnd/lntest/wait"
|
||||
)
|
||||
|
||||
func testListPayments(net *lntest.NetworkHarness, t *harnessTest) {
|
||||
ctxb := context.Background()
|
||||
|
||||
// First start by deleting all payments that Alice knows of. This will
|
||||
// allow us to execute the test with a clean state for Alice.
|
||||
delPaymentsReq := &lnrpc.DeleteAllPaymentsRequest{}
|
||||
ctxt, _ := context.WithTimeout(ctxb, defaultTimeout)
|
||||
if _, err := net.Alice.DeleteAllPayments(ctxt, delPaymentsReq); err != nil {
|
||||
t.Fatalf("unable to delete payments: %v", err)
|
||||
}
|
||||
|
||||
// Check that there are no payments before test.
|
||||
reqInit := &lnrpc.ListPaymentsRequest{}
|
||||
ctxt, _ = context.WithTimeout(ctxt, defaultTimeout)
|
||||
paymentsRespInit, err := net.Alice.ListPayments(ctxt, reqInit)
|
||||
if err != nil {
|
||||
t.Fatalf("error when obtaining Alice payments: %v", err)
|
||||
}
|
||||
if len(paymentsRespInit.Payments) != 0 {
|
||||
t.Fatalf("incorrect number of payments, got %v, want %v",
|
||||
len(paymentsRespInit.Payments), 0)
|
||||
}
|
||||
|
||||
// Open a channel with 100k satoshis between Alice and Bob with Alice
|
||||
// being the sole funder of the channel.
|
||||
chanAmt := btcutil.Amount(100000)
|
||||
ctxt, _ = context.WithTimeout(ctxb, channelOpenTimeout)
|
||||
chanPoint := openChannelAndAssert(
|
||||
ctxt, t, net, net.Alice, net.Bob,
|
||||
lntest.OpenChannelParams{
|
||||
Amt: chanAmt,
|
||||
},
|
||||
)
|
||||
|
||||
// Now that the channel is open, create an invoice for Bob which
|
||||
// expects a payment of 1000 satoshis from Alice paid via a particular
|
||||
// preimage.
|
||||
const paymentAmt = 1000
|
||||
preimage := bytes.Repeat([]byte("B"), 32)
|
||||
invoice := &lnrpc.Invoice{
|
||||
Memo: "testing",
|
||||
RPreimage: preimage,
|
||||
Value: paymentAmt,
|
||||
}
|
||||
addInvoiceCtxt, _ := context.WithTimeout(ctxb, defaultTimeout)
|
||||
invoiceResp, err := net.Bob.AddInvoice(addInvoiceCtxt, invoice)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to add invoice: %v", err)
|
||||
}
|
||||
|
||||
// Wait for Alice to recognize and advertise the new channel generated
|
||||
// above.
|
||||
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout)
|
||||
if err = net.Alice.WaitForNetworkChannelOpen(ctxt, chanPoint); err != nil {
|
||||
t.Fatalf("alice didn't advertise channel before "+
|
||||
"timeout: %v", err)
|
||||
}
|
||||
if err = net.Bob.WaitForNetworkChannelOpen(ctxt, chanPoint); err != nil {
|
||||
t.Fatalf("bob didn't advertise channel before "+
|
||||
"timeout: %v", err)
|
||||
}
|
||||
|
||||
// With the invoice for Bob added, send a payment towards Alice paying
|
||||
// to the above generated invoice.
|
||||
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout)
|
||||
sendAndAssertSuccess(
|
||||
ctxt, t, net.Alice,
|
||||
&routerrpc.SendPaymentRequest{
|
||||
PaymentRequest: invoiceResp.PaymentRequest,
|
||||
TimeoutSeconds: 60,
|
||||
FeeLimitSat: 1000000,
|
||||
},
|
||||
)
|
||||
|
||||
// Grab Alice's list of payments, she should show the existence of
|
||||
// exactly one payment.
|
||||
req := &lnrpc.ListPaymentsRequest{}
|
||||
ctxt, _ = context.WithTimeout(ctxt, defaultTimeout)
|
||||
paymentsResp, err := net.Alice.ListPayments(ctxt, req)
|
||||
if err != nil {
|
||||
t.Fatalf("error when obtaining Alice payments: %v", err)
|
||||
}
|
||||
if len(paymentsResp.Payments) != 1 {
|
||||
t.Fatalf("incorrect number of payments, got %v, want %v",
|
||||
len(paymentsResp.Payments), 1)
|
||||
}
|
||||
p := paymentsResp.Payments[0]
|
||||
path := p.Htlcs[len(p.Htlcs)-1].Route.Hops
|
||||
|
||||
// Ensure that the stored path shows a direct payment to Bob with no
|
||||
// other nodes in-between.
|
||||
if len(path) != 1 || path[0].PubKey != net.Bob.PubKeyStr {
|
||||
t.Fatalf("incorrect path")
|
||||
}
|
||||
|
||||
// The payment amount should also match our previous payment directly.
|
||||
if p.Value != paymentAmt {
|
||||
t.Fatalf("incorrect amount, got %v, want %v",
|
||||
p.Value, paymentAmt)
|
||||
}
|
||||
|
||||
// The payment hash (or r-hash) should have been stored correctly.
|
||||
correctRHash := hex.EncodeToString(invoiceResp.RHash)
|
||||
if !reflect.DeepEqual(p.PaymentHash, correctRHash) {
|
||||
t.Fatalf("incorrect RHash, got %v, want %v",
|
||||
p.PaymentHash, correctRHash)
|
||||
}
|
||||
|
||||
// As we made a single-hop direct payment, there should have been no fee
|
||||
// applied.
|
||||
if p.Fee != 0 {
|
||||
t.Fatalf("incorrect Fee, got %v, want %v", p.Fee, 0)
|
||||
}
|
||||
|
||||
// Finally, verify that the payment request returned by the rpc matches
|
||||
// the invoice that we paid.
|
||||
if p.PaymentRequest != invoiceResp.PaymentRequest {
|
||||
t.Fatalf("incorrect payreq, got: %v, want: %v",
|
||||
p.PaymentRequest, invoiceResp.PaymentRequest)
|
||||
}
|
||||
|
||||
// Delete all payments from Alice. DB should have no payments.
|
||||
delReq := &lnrpc.DeleteAllPaymentsRequest{}
|
||||
ctxt, _ = context.WithTimeout(ctxt, defaultTimeout)
|
||||
_, err = net.Alice.DeleteAllPayments(ctxt, delReq)
|
||||
if err != nil {
|
||||
t.Fatalf("Can't delete payments at the end: %v", err)
|
||||
}
|
||||
|
||||
// Check that there are no payments after test.
|
||||
listReq := &lnrpc.ListPaymentsRequest{}
|
||||
ctxt, _ = context.WithTimeout(ctxt, defaultTimeout)
|
||||
paymentsResp, err = net.Alice.ListPayments(ctxt, listReq)
|
||||
if err != nil {
|
||||
t.Fatalf("error when obtaining Alice payments: %v", err)
|
||||
}
|
||||
if len(paymentsResp.Payments) != 0 {
|
||||
t.Fatalf("incorrect number of payments, got %v, want %v",
|
||||
len(paymentsRespInit.Payments), 0)
|
||||
}
|
||||
|
||||
ctxt, _ = context.WithTimeout(ctxb, channelCloseTimeout)
|
||||
closeChannelAndAssert(ctxt, t, net, net.Alice, chanPoint, false)
|
||||
}
|
||||
|
||||
// testPaymentFollowingChannelOpen tests that the channel transition from
|
||||
// 'pending' to 'open' state does not cause any inconsistencies within other
|
||||
// subsystems trying to update the channel state in the db. We follow this
|
||||
// transition with a payment that updates the commitment state and verify that
|
||||
// the pending state is up to date.
|
||||
func testPaymentFollowingChannelOpen(net *lntest.NetworkHarness, t *harnessTest) {
|
||||
ctxb := context.Background()
|
||||
|
||||
const paymentAmt = btcutil.Amount(100)
|
||||
channelCapacity := paymentAmt * 1000
|
||||
|
||||
// We first establish a channel between Alice and Bob.
|
||||
ctxt, cancel := context.WithTimeout(ctxb, channelOpenTimeout)
|
||||
defer cancel()
|
||||
pendingUpdate, err := net.OpenPendingChannel(
|
||||
ctxt, net.Alice, net.Bob, channelCapacity, 0,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to open channel: %v", err)
|
||||
}
|
||||
|
||||
// At this point, the channel's funding transaction will have been
|
||||
// broadcast, but not confirmed. Alice and Bob's nodes
|
||||
// should reflect this when queried via RPC.
|
||||
ctxt, cancel = context.WithTimeout(ctxb, defaultTimeout)
|
||||
defer cancel()
|
||||
assertNumOpenChannelsPending(ctxt, t, net.Alice, net.Bob, 1)
|
||||
|
||||
// We are restarting Bob's node to let the link be created for the
|
||||
// pending channel.
|
||||
if err := net.RestartNode(net.Bob, nil); err != nil {
|
||||
t.Fatalf("Bob restart failed: %v", err)
|
||||
}
|
||||
|
||||
// We ensure that Bob reconnects to Alice.
|
||||
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout)
|
||||
net.EnsureConnected(ctxt, t.t, net.Bob, net.Alice)
|
||||
|
||||
// We mine one block for the channel to be confirmed.
|
||||
_ = mineBlocks(t, net, 6, 1)[0]
|
||||
|
||||
// We verify that the channel is open from both nodes point of view.
|
||||
ctxt, cancel = context.WithTimeout(ctxb, defaultTimeout)
|
||||
defer cancel()
|
||||
assertNumOpenChannelsPending(ctxt, t, net.Alice, net.Bob, 0)
|
||||
|
||||
// With the channel open, we'll create invoices for Bob that Alice will
|
||||
// pay to in order to advance the state of the channel.
|
||||
bobPayReqs, _, _, err := createPayReqs(
|
||||
net.Bob, paymentAmt, 1,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to create pay reqs: %v", err)
|
||||
}
|
||||
|
||||
// Send payment to Bob so that a channel update to disk will be
|
||||
// executed.
|
||||
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout)
|
||||
sendAndAssertSuccess(
|
||||
ctxt, t, net.Alice, &routerrpc.SendPaymentRequest{
|
||||
PaymentRequest: bobPayReqs[0],
|
||||
TimeoutSeconds: 60,
|
||||
FeeLimitSat: 1000000,
|
||||
},
|
||||
)
|
||||
|
||||
// At this point we want to make sure the channel is opened and not
|
||||
// pending.
|
||||
ctxt, cancel = context.WithTimeout(ctxb, defaultTimeout)
|
||||
defer cancel()
|
||||
res, err := net.Bob.ListChannels(ctxt, &lnrpc.ListChannelsRequest{})
|
||||
if err != nil {
|
||||
t.Fatalf("unable to list bob channels: %v", err)
|
||||
}
|
||||
if len(res.Channels) == 0 {
|
||||
t.Fatalf("bob list of channels is empty")
|
||||
}
|
||||
|
||||
// Finally, immediately close the channel. This function will also
|
||||
// block until the channel is closed and will additionally assert the
|
||||
// relevant channel closing post conditions.
|
||||
chanPoint := &lnrpc.ChannelPoint{
|
||||
FundingTxid: &lnrpc.ChannelPoint_FundingTxidBytes{
|
||||
FundingTxidBytes: pendingUpdate.Txid,
|
||||
},
|
||||
OutputIndex: pendingUpdate.OutputIndex,
|
||||
}
|
||||
ctxt, cancel = context.WithTimeout(ctxb, channelCloseTimeout)
|
||||
defer cancel()
|
||||
closeChannelAndAssert(ctxt, t, net, net.Alice, chanPoint, false)
|
||||
}
|
||||
|
||||
// testAsyncPayments tests the performance of the async payments.
|
||||
func testAsyncPayments(net *lntest.NetworkHarness, t *harnessTest) {
|
||||
ctxb := context.Background()
|
||||
|
||||
const (
|
||||
paymentAmt = 100
|
||||
)
|
||||
|
||||
// First establish a channel with a capacity equals to the overall
|
||||
// amount of payments, between Alice and Bob, at the end of the test
|
||||
// Alice should send all money from her side to Bob.
|
||||
ctxt, _ := context.WithTimeout(ctxb, channelOpenTimeout)
|
||||
channelCapacity := btcutil.Amount(paymentAmt * 2000)
|
||||
chanPoint := openChannelAndAssert(
|
||||
ctxt, t, net, net.Alice, net.Bob,
|
||||
lntest.OpenChannelParams{
|
||||
Amt: channelCapacity,
|
||||
},
|
||||
)
|
||||
|
||||
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout)
|
||||
info, err := getChanInfo(ctxt, net.Alice)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to get alice channel info: %v", err)
|
||||
}
|
||||
|
||||
// We'll create a number of invoices equal the max number of HTLCs that
|
||||
// can be carried in one direction. The number on the commitment will
|
||||
// likely be lower, but we can't guarantee that any more HTLCs will
|
||||
// succeed due to the limited path diversity and inability of the router
|
||||
// to retry via another path.
|
||||
numInvoices := int(input.MaxHTLCNumber / 2)
|
||||
|
||||
bobAmt := int64(numInvoices * paymentAmt)
|
||||
aliceAmt := info.LocalBalance - bobAmt
|
||||
|
||||
// With the channel open, we'll create invoices for Bob that Alice
|
||||
// will pay to in order to advance the state of the channel.
|
||||
bobPayReqs, _, _, err := createPayReqs(
|
||||
net.Bob, paymentAmt, numInvoices,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to create pay reqs: %v", err)
|
||||
}
|
||||
|
||||
// Wait for Alice to receive the channel edge from the funding manager.
|
||||
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout)
|
||||
err = net.Alice.WaitForNetworkChannelOpen(ctxt, chanPoint)
|
||||
if err != nil {
|
||||
t.Fatalf("alice didn't see the alice->bob channel before "+
|
||||
"timeout: %v", err)
|
||||
}
|
||||
|
||||
// Simultaneously send payments from Alice to Bob using of Bob's payment
|
||||
// hashes generated above.
|
||||
now := time.Now()
|
||||
errChan := make(chan error)
|
||||
statusChan := make(chan *lnrpc.Payment)
|
||||
for i := 0; i < numInvoices; i++ {
|
||||
payReq := bobPayReqs[i]
|
||||
go func() {
|
||||
ctxt, _ = context.WithTimeout(ctxb, lntest.AsyncBenchmarkTimeout)
|
||||
stream, err := net.Alice.RouterClient.SendPaymentV2(
|
||||
ctxt,
|
||||
&routerrpc.SendPaymentRequest{
|
||||
PaymentRequest: payReq,
|
||||
TimeoutSeconds: 60,
|
||||
FeeLimitMsat: noFeeLimitMsat,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
errChan <- err
|
||||
}
|
||||
result, err := getPaymentResult(stream)
|
||||
if err != nil {
|
||||
errChan <- err
|
||||
}
|
||||
|
||||
statusChan <- result
|
||||
}()
|
||||
}
|
||||
|
||||
// Wait until all the payments have settled.
|
||||
for i := 0; i < numInvoices; i++ {
|
||||
select {
|
||||
case result := <-statusChan:
|
||||
if result.Status == lnrpc.Payment_SUCCEEDED {
|
||||
continue
|
||||
}
|
||||
|
||||
case err := <-errChan:
|
||||
t.Fatalf("payment error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// All payments have been sent, mark the finish time.
|
||||
timeTaken := time.Since(now)
|
||||
|
||||
// Next query for Bob's and Alice's channel states, in order to confirm
|
||||
// that all payment have been successful transmitted.
|
||||
|
||||
// Wait for the revocation to be received so alice no longer has pending
|
||||
// htlcs listed and has correct balances. This is needed due to the fact
|
||||
// that we now pipeline the settles.
|
||||
err = wait.Predicate(func() bool {
|
||||
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout)
|
||||
aliceChan, err := getChanInfo(ctxt, net.Alice)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
if len(aliceChan.PendingHtlcs) != 0 {
|
||||
return false
|
||||
}
|
||||
if aliceChan.RemoteBalance != bobAmt {
|
||||
return false
|
||||
}
|
||||
if aliceChan.LocalBalance != aliceAmt {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}, time.Second*5)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to assert alice's pending htlcs and/or remote/local balance")
|
||||
}
|
||||
|
||||
// Wait for Bob to receive revocation from Alice.
|
||||
time.Sleep(2 * time.Second)
|
||||
|
||||
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout)
|
||||
bobChan, err := getChanInfo(ctxt, net.Bob)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to get bob's channel info: %v", err)
|
||||
}
|
||||
if len(bobChan.PendingHtlcs) != 0 {
|
||||
t.Fatalf("bob's pending htlcs is incorrect, got %v, "+
|
||||
"expected %v", len(bobChan.PendingHtlcs), 0)
|
||||
}
|
||||
if bobChan.LocalBalance != bobAmt {
|
||||
t.Fatalf("bob's local balance is incorrect, got %v, expected"+
|
||||
" %v", bobChan.LocalBalance, bobAmt)
|
||||
}
|
||||
if bobChan.RemoteBalance != aliceAmt {
|
||||
t.Fatalf("bob's remote balance is incorrect, got %v, "+
|
||||
"expected %v", bobChan.RemoteBalance, aliceAmt)
|
||||
}
|
||||
|
||||
t.Log("\tBenchmark info: Elapsed time: ", timeTaken)
|
||||
t.Log("\tBenchmark info: TPS: ", float64(numInvoices)/timeTaken.Seconds())
|
||||
|
||||
// Finally, immediately close the channel. This function will also
|
||||
// block until the channel is closed and will additionally assert the
|
||||
// relevant channel closing post conditions.
|
||||
ctxt, _ = context.WithTimeout(ctxb, channelCloseTimeout)
|
||||
closeChannelAndAssert(ctxt, t, net, net.Alice, chanPoint, false)
|
||||
}
|
||||
|
||||
// testBidirectionalAsyncPayments tests that nodes are able to send the
|
||||
// payments to each other in async manner without blocking.
|
||||
func testBidirectionalAsyncPayments(net *lntest.NetworkHarness, t *harnessTest) {
|
||||
ctxb := context.Background()
|
||||
|
||||
const (
|
||||
paymentAmt = 1000
|
||||
)
|
||||
|
||||
// First establish a channel with a capacity equals to the overall
|
||||
// amount of payments, between Alice and Bob, at the end of the test
|
||||
// Alice should send all money from her side to Bob.
|
||||
ctxt, _ := context.WithTimeout(ctxb, channelOpenTimeout)
|
||||
chanPoint := openChannelAndAssert(
|
||||
ctxt, t, net, net.Alice, net.Bob,
|
||||
lntest.OpenChannelParams{
|
||||
Amt: paymentAmt * 2000,
|
||||
PushAmt: paymentAmt * 1000,
|
||||
},
|
||||
)
|
||||
|
||||
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout)
|
||||
info, err := getChanInfo(ctxt, net.Alice)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to get alice channel info: %v", err)
|
||||
}
|
||||
|
||||
// We'll create a number of invoices equal the max number of HTLCs that
|
||||
// can be carried in one direction. The number on the commitment will
|
||||
// likely be lower, but we can't guarantee that any more HTLCs will
|
||||
// succeed due to the limited path diversity and inability of the router
|
||||
// to retry via another path.
|
||||
numInvoices := int(input.MaxHTLCNumber / 2)
|
||||
|
||||
// Nodes should exchange the same amount of money and because of this
|
||||
// at the end balances should remain the same.
|
||||
aliceAmt := info.LocalBalance
|
||||
bobAmt := info.RemoteBalance
|
||||
|
||||
// With the channel open, we'll create invoices for Bob that Alice
|
||||
// will pay to in order to advance the state of the channel.
|
||||
bobPayReqs, _, _, err := createPayReqs(
|
||||
net.Bob, paymentAmt, numInvoices,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to create pay reqs: %v", err)
|
||||
}
|
||||
|
||||
// With the channel open, we'll create invoices for Alice that Bob
|
||||
// will pay to in order to advance the state of the channel.
|
||||
alicePayReqs, _, _, err := createPayReqs(
|
||||
net.Alice, paymentAmt, numInvoices,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to create pay reqs: %v", err)
|
||||
}
|
||||
|
||||
// Wait for Alice to receive the channel edge from the funding manager.
|
||||
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout)
|
||||
if err = net.Alice.WaitForNetworkChannelOpen(ctxt, chanPoint); err != nil {
|
||||
t.Fatalf("alice didn't see the alice->bob channel before "+
|
||||
"timeout: %v", err)
|
||||
}
|
||||
if err = net.Bob.WaitForNetworkChannelOpen(ctxt, chanPoint); err != nil {
|
||||
t.Fatalf("bob didn't see the bob->alice channel before "+
|
||||
"timeout: %v", err)
|
||||
}
|
||||
|
||||
// Reset mission control to prevent previous payment results from
|
||||
// interfering with this test. A new channel has been opened, but
|
||||
// mission control operates on node pairs.
|
||||
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout)
|
||||
_, err = net.Alice.RouterClient.ResetMissionControl(
|
||||
ctxt, &routerrpc.ResetMissionControlRequest{},
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to reset mc for alice: %v", err)
|
||||
}
|
||||
|
||||
// Send payments from Alice to Bob and from Bob to Alice in async
|
||||
// manner.
|
||||
errChan := make(chan error)
|
||||
statusChan := make(chan *lnrpc.Payment)
|
||||
|
||||
send := func(node *lntest.HarnessNode, payReq string) {
|
||||
go func() {
|
||||
ctxt, _ = context.WithTimeout(
|
||||
ctxb, lntest.AsyncBenchmarkTimeout,
|
||||
)
|
||||
stream, err := node.RouterClient.SendPaymentV2(
|
||||
ctxt,
|
||||
&routerrpc.SendPaymentRequest{
|
||||
PaymentRequest: payReq,
|
||||
TimeoutSeconds: 60,
|
||||
FeeLimitMsat: noFeeLimitMsat,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
errChan <- err
|
||||
}
|
||||
result, err := getPaymentResult(stream)
|
||||
if err != nil {
|
||||
errChan <- err
|
||||
}
|
||||
|
||||
statusChan <- result
|
||||
}()
|
||||
}
|
||||
|
||||
for i := 0; i < numInvoices; i++ {
|
||||
send(net.Bob, alicePayReqs[i])
|
||||
send(net.Alice, bobPayReqs[i])
|
||||
}
|
||||
|
||||
// Expect all payments to succeed.
|
||||
for i := 0; i < 2*numInvoices; i++ {
|
||||
select {
|
||||
case result := <-statusChan:
|
||||
if result.Status != lnrpc.Payment_SUCCEEDED {
|
||||
t.Fatalf("payment error: %v", result.Status)
|
||||
}
|
||||
|
||||
case err := <-errChan:
|
||||
t.Fatalf("payment error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Wait for Alice and Bob to receive revocations messages, and update
|
||||
// states, i.e. balance info.
|
||||
time.Sleep(1 * time.Second)
|
||||
|
||||
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout)
|
||||
aliceInfo, err := getChanInfo(ctxt, net.Alice)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to get bob's channel info: %v", err)
|
||||
}
|
||||
if aliceInfo.RemoteBalance != bobAmt {
|
||||
t.Fatalf("alice's remote balance is incorrect, got %v, "+
|
||||
"expected %v", aliceInfo.RemoteBalance, bobAmt)
|
||||
}
|
||||
if aliceInfo.LocalBalance != aliceAmt {
|
||||
t.Fatalf("alice's local balance is incorrect, got %v, "+
|
||||
"expected %v", aliceInfo.LocalBalance, aliceAmt)
|
||||
}
|
||||
if len(aliceInfo.PendingHtlcs) != 0 {
|
||||
t.Fatalf("alice's pending htlcs is incorrect, got %v, "+
|
||||
"expected %v", len(aliceInfo.PendingHtlcs), 0)
|
||||
}
|
||||
|
||||
// Next query for Bob's and Alice's channel states, in order to confirm
|
||||
// that all payment have been successful transmitted.
|
||||
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout)
|
||||
bobInfo, err := getChanInfo(ctxt, net.Bob)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to get bob's channel info: %v", err)
|
||||
}
|
||||
|
||||
if bobInfo.LocalBalance != bobAmt {
|
||||
t.Fatalf("bob's local balance is incorrect, got %v, expected"+
|
||||
" %v", bobInfo.LocalBalance, bobAmt)
|
||||
}
|
||||
if bobInfo.RemoteBalance != aliceAmt {
|
||||
t.Fatalf("bob's remote balance is incorrect, got %v, "+
|
||||
"expected %v", bobInfo.RemoteBalance, aliceAmt)
|
||||
}
|
||||
if len(bobInfo.PendingHtlcs) != 0 {
|
||||
t.Fatalf("bob's pending htlcs is incorrect, got %v, "+
|
||||
"expected %v", len(bobInfo.PendingHtlcs), 0)
|
||||
}
|
||||
|
||||
// Finally, immediately close the channel. This function will also
|
||||
// block until the channel is closed and will additionally assert the
|
||||
// relevant channel closing post conditions.
|
||||
ctxt, _ = context.WithTimeout(ctxb, channelCloseTimeout)
|
||||
closeChannelAndAssert(ctxt, t, net, net.Alice, chanPoint, false)
|
||||
}
|
||||
|
||||
func testInvoiceSubscriptions(net *lntest.NetworkHarness, t *harnessTest) {
|
||||
ctxb := context.Background()
|
||||
|
||||
const chanAmt = btcutil.Amount(500000)
|
||||
|
||||
// Open a channel with 500k satoshis between Alice and Bob with Alice
|
||||
// being the sole funder of the channel.
|
||||
ctxt, _ := context.WithTimeout(ctxb, channelOpenTimeout)
|
||||
chanPoint := openChannelAndAssert(
|
||||
ctxt, t, net, net.Alice, net.Bob,
|
||||
lntest.OpenChannelParams{
|
||||
Amt: chanAmt,
|
||||
},
|
||||
)
|
||||
|
||||
// Next create a new invoice for Bob requesting 1k satoshis.
|
||||
// TODO(roasbeef): make global list of invoices for each node to re-use
|
||||
// and avoid collisions
|
||||
const paymentAmt = 1000
|
||||
invoice := &lnrpc.Invoice{
|
||||
Memo: "testing",
|
||||
RPreimage: makeFakePayHash(t),
|
||||
Value: paymentAmt,
|
||||
}
|
||||
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout)
|
||||
invoiceResp, err := net.Bob.AddInvoice(ctxt, invoice)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to add invoice: %v", err)
|
||||
}
|
||||
lastAddIndex := invoiceResp.AddIndex
|
||||
|
||||
// Create a new invoice subscription client for Bob, the notification
|
||||
// should be dispatched shortly below.
|
||||
req := &lnrpc.InvoiceSubscription{}
|
||||
ctx, cancelInvoiceSubscription := context.WithCancel(ctxb)
|
||||
bobInvoiceSubscription, err := net.Bob.SubscribeInvoices(ctx, req)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to subscribe to bob's invoice updates: %v", err)
|
||||
}
|
||||
|
||||
var settleIndex uint64
|
||||
quit := make(chan struct{})
|
||||
updateSent := make(chan struct{})
|
||||
go func() {
|
||||
invoiceUpdate, err := bobInvoiceSubscription.Recv()
|
||||
select {
|
||||
case <-quit:
|
||||
// Received cancellation
|
||||
return
|
||||
default:
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("unable to recv invoice update: %v", err)
|
||||
}
|
||||
|
||||
// The invoice update should exactly match the invoice created
|
||||
// above, but should now be settled and have SettleDate
|
||||
if !invoiceUpdate.Settled { // nolint:staticcheck
|
||||
t.Fatalf("invoice not settled but should be")
|
||||
}
|
||||
if invoiceUpdate.SettleDate == 0 {
|
||||
t.Fatalf("invoice should have non zero settle date, but doesn't")
|
||||
}
|
||||
|
||||
if !bytes.Equal(invoiceUpdate.RPreimage, invoice.RPreimage) {
|
||||
t.Fatalf("payment preimages don't match: expected %v, got %v",
|
||||
invoice.RPreimage, invoiceUpdate.RPreimage)
|
||||
}
|
||||
|
||||
if invoiceUpdate.SettleIndex == 0 {
|
||||
t.Fatalf("invoice should have settle index")
|
||||
}
|
||||
|
||||
settleIndex = invoiceUpdate.SettleIndex
|
||||
|
||||
close(updateSent)
|
||||
}()
|
||||
|
||||
// Wait for the channel to be recognized by both Alice and Bob before
|
||||
// continuing the rest of the test.
|
||||
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout)
|
||||
err = net.Alice.WaitForNetworkChannelOpen(ctxt, chanPoint)
|
||||
if err != nil {
|
||||
// TODO(roasbeef): will need to make num blocks to advertise a
|
||||
// node param
|
||||
close(quit)
|
||||
t.Fatalf("channel not seen by alice before timeout: %v", err)
|
||||
}
|
||||
|
||||
// With the assertion above set up, send a payment from Alice to Bob
|
||||
// which should finalize and settle the invoice.
|
||||
sendReq := &routerrpc.SendPaymentRequest{
|
||||
PaymentRequest: invoiceResp.PaymentRequest,
|
||||
TimeoutSeconds: 60,
|
||||
FeeLimitMsat: noFeeLimitMsat,
|
||||
}
|
||||
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout)
|
||||
stream, err := net.Alice.RouterClient.SendPaymentV2(ctxt, sendReq)
|
||||
if err != nil {
|
||||
close(quit)
|
||||
t.Fatalf("unable to send payment: %v", err)
|
||||
}
|
||||
result, err := getPaymentResult(stream)
|
||||
if err != nil {
|
||||
close(quit)
|
||||
t.Fatalf("cannot get payment result: %v", err)
|
||||
}
|
||||
if result.Status != lnrpc.Payment_SUCCEEDED {
|
||||
close(quit)
|
||||
t.Fatalf("error when attempting recv: %v", result.Status)
|
||||
}
|
||||
|
||||
select {
|
||||
case <-time.After(time.Second * 10):
|
||||
close(quit)
|
||||
t.Fatalf("update not sent after 10 seconds")
|
||||
case <-updateSent: // Fall through on success
|
||||
}
|
||||
|
||||
// With the base case working, we'll now cancel Bob's current
|
||||
// subscription in order to exercise the backlog fill behavior.
|
||||
cancelInvoiceSubscription()
|
||||
|
||||
// We'll now add 3 more invoices to Bob's invoice registry.
|
||||
const numInvoices = 3
|
||||
payReqs, _, newInvoices, err := createPayReqs(
|
||||
net.Bob, paymentAmt, numInvoices,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to create pay reqs: %v", err)
|
||||
}
|
||||
|
||||
// Now that the set of invoices has been added, we'll re-register for
|
||||
// streaming invoice notifications for Bob, this time specifying the
|
||||
// add invoice of the last prior invoice.
|
||||
req = &lnrpc.InvoiceSubscription{
|
||||
AddIndex: lastAddIndex,
|
||||
}
|
||||
ctx, cancelInvoiceSubscription = context.WithCancel(ctxb)
|
||||
bobInvoiceSubscription, err = net.Bob.SubscribeInvoices(ctx, req)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to subscribe to bob's invoice updates: %v", err)
|
||||
}
|
||||
|
||||
// Since we specified a value of the prior add index above, we should
|
||||
// now immediately get the invoices we just added as we should get the
|
||||
// backlog of notifications.
|
||||
for i := 0; i < numInvoices; i++ {
|
||||
invoiceUpdate, err := bobInvoiceSubscription.Recv()
|
||||
if err != nil {
|
||||
t.Fatalf("unable to receive subscription")
|
||||
}
|
||||
|
||||
// We should now get the ith invoice we added, as they should
|
||||
// be returned in order.
|
||||
if invoiceUpdate.Settled { // nolint:staticcheck
|
||||
t.Fatalf("should have only received add events")
|
||||
}
|
||||
originalInvoice := newInvoices[i]
|
||||
rHash := sha256.Sum256(originalInvoice.RPreimage)
|
||||
if !bytes.Equal(invoiceUpdate.RHash, rHash[:]) {
|
||||
t.Fatalf("invoices have mismatched payment hashes: "+
|
||||
"expected %x, got %x", rHash[:],
|
||||
invoiceUpdate.RHash)
|
||||
}
|
||||
}
|
||||
|
||||
cancelInvoiceSubscription()
|
||||
|
||||
// We'll now have Bob settle out the remainder of these invoices so we
|
||||
// can test that all settled invoices are properly notified.
|
||||
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout)
|
||||
err = completePaymentRequests(
|
||||
ctxt, net.Alice, net.Alice.RouterClient, payReqs, true,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to send payment: %v", err)
|
||||
}
|
||||
|
||||
// With the set of invoices paid, we'll now cancel the old
|
||||
// subscription, and create a new one for Bob, this time using the
|
||||
// settle index to obtain the backlog of settled invoices.
|
||||
req = &lnrpc.InvoiceSubscription{
|
||||
SettleIndex: settleIndex,
|
||||
}
|
||||
ctx, cancelInvoiceSubscription = context.WithCancel(ctxb)
|
||||
bobInvoiceSubscription, err = net.Bob.SubscribeInvoices(ctx, req)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to subscribe to bob's invoice updates: %v", err)
|
||||
}
|
||||
|
||||
defer cancelInvoiceSubscription()
|
||||
|
||||
// As we specified the index of the past settle index, we should now
|
||||
// receive notifications for the three HTLCs that we just settled. As
|
||||
// the order that the HTLCs will be settled in is partially randomized,
|
||||
// we'll use a map to assert that the proper set has been settled.
|
||||
settledInvoices := make(map[[32]byte]struct{})
|
||||
for _, invoice := range newInvoices {
|
||||
rHash := sha256.Sum256(invoice.RPreimage)
|
||||
settledInvoices[rHash] = struct{}{}
|
||||
}
|
||||
for i := 0; i < numInvoices; i++ {
|
||||
invoiceUpdate, err := bobInvoiceSubscription.Recv()
|
||||
if err != nil {
|
||||
t.Fatalf("unable to receive subscription")
|
||||
}
|
||||
|
||||
// We should now get the ith invoice we added, as they should
|
||||
// be returned in order.
|
||||
if !invoiceUpdate.Settled { // nolint:staticcheck
|
||||
t.Fatalf("should have only received settle events")
|
||||
}
|
||||
|
||||
var rHash [32]byte
|
||||
copy(rHash[:], invoiceUpdate.RHash)
|
||||
if _, ok := settledInvoices[rHash]; !ok {
|
||||
t.Fatalf("unknown invoice settled: %x", rHash)
|
||||
}
|
||||
|
||||
delete(settledInvoices, rHash)
|
||||
}
|
||||
|
||||
// At this point, all the invoices should be fully settled.
|
||||
if len(settledInvoices) != 0 {
|
||||
t.Fatalf("not all invoices settled")
|
||||
}
|
||||
|
||||
ctxt, _ = context.WithTimeout(ctxb, channelCloseTimeout)
|
||||
closeChannelAndAssert(ctxt, t, net, net.Alice, chanPoint, false)
|
||||
}
|
@ -4,15 +4,12 @@ import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"math"
|
||||
"os"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
@ -736,98 +733,6 @@ func channelCommitType(node *lntest.HarnessNode,
|
||||
return 0, fmt.Errorf("channel point %v not found", chanPoint)
|
||||
}
|
||||
|
||||
// testPaymentFollowingChannelOpen tests that the channel transition from
|
||||
// 'pending' to 'open' state does not cause any inconsistencies within other
|
||||
// subsystems trying to update the channel state in the db. We follow this
|
||||
// transition with a payment that updates the commitment state and verify that
|
||||
// the pending state is up to date.
|
||||
func testPaymentFollowingChannelOpen(net *lntest.NetworkHarness, t *harnessTest) {
|
||||
ctxb := context.Background()
|
||||
|
||||
const paymentAmt = btcutil.Amount(100)
|
||||
channelCapacity := paymentAmt * 1000
|
||||
|
||||
// We first establish a channel between Alice and Bob.
|
||||
ctxt, cancel := context.WithTimeout(ctxb, channelOpenTimeout)
|
||||
defer cancel()
|
||||
pendingUpdate, err := net.OpenPendingChannel(
|
||||
ctxt, net.Alice, net.Bob, channelCapacity, 0,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to open channel: %v", err)
|
||||
}
|
||||
|
||||
// At this point, the channel's funding transaction will have been
|
||||
// broadcast, but not confirmed. Alice and Bob's nodes
|
||||
// should reflect this when queried via RPC.
|
||||
ctxt, cancel = context.WithTimeout(ctxb, defaultTimeout)
|
||||
defer cancel()
|
||||
assertNumOpenChannelsPending(ctxt, t, net.Alice, net.Bob, 1)
|
||||
|
||||
// We are restarting Bob's node to let the link be created for the
|
||||
// pending channel.
|
||||
if err := net.RestartNode(net.Bob, nil); err != nil {
|
||||
t.Fatalf("Bob restart failed: %v", err)
|
||||
}
|
||||
|
||||
// We ensure that Bob reconnects to Alice.
|
||||
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout)
|
||||
net.EnsureConnected(ctxt, t.t, net.Bob, net.Alice)
|
||||
|
||||
// We mine one block for the channel to be confirmed.
|
||||
_ = mineBlocks(t, net, 6, 1)[0]
|
||||
|
||||
// We verify that the channel is open from both nodes point of view.
|
||||
ctxt, cancel = context.WithTimeout(ctxb, defaultTimeout)
|
||||
defer cancel()
|
||||
assertNumOpenChannelsPending(ctxt, t, net.Alice, net.Bob, 0)
|
||||
|
||||
// With the channel open, we'll create invoices for Bob that Alice will
|
||||
// pay to in order to advance the state of the channel.
|
||||
bobPayReqs, _, _, err := createPayReqs(
|
||||
net.Bob, paymentAmt, 1,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to create pay reqs: %v", err)
|
||||
}
|
||||
|
||||
// Send payment to Bob so that a channel update to disk will be
|
||||
// executed.
|
||||
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout)
|
||||
sendAndAssertSuccess(
|
||||
ctxt, t, net.Alice, &routerrpc.SendPaymentRequest{
|
||||
PaymentRequest: bobPayReqs[0],
|
||||
TimeoutSeconds: 60,
|
||||
FeeLimitSat: 1000000,
|
||||
},
|
||||
)
|
||||
|
||||
// At this point we want to make sure the channel is opened and not
|
||||
// pending.
|
||||
ctxt, cancel = context.WithTimeout(ctxb, defaultTimeout)
|
||||
defer cancel()
|
||||
res, err := net.Bob.ListChannels(ctxt, &lnrpc.ListChannelsRequest{})
|
||||
if err != nil {
|
||||
t.Fatalf("unable to list bob channels: %v", err)
|
||||
}
|
||||
if len(res.Channels) == 0 {
|
||||
t.Fatalf("bob list of channels is empty")
|
||||
}
|
||||
|
||||
// Finally, immediately close the channel. This function will also
|
||||
// block until the channel is closed and will additionally assert the
|
||||
// relevant channel closing post conditions.
|
||||
chanPoint := &lnrpc.ChannelPoint{
|
||||
FundingTxid: &lnrpc.ChannelPoint_FundingTxidBytes{
|
||||
FundingTxidBytes: pendingUpdate.Txid,
|
||||
},
|
||||
OutputIndex: pendingUpdate.OutputIndex,
|
||||
}
|
||||
ctxt, cancel = context.WithTimeout(ctxb, channelCloseTimeout)
|
||||
defer cancel()
|
||||
closeChannelAndAssert(ctxt, t, net, net.Alice, chanPoint, false)
|
||||
}
|
||||
|
||||
// calculateMaxHtlc re-implements the RequiredRemoteChannelReserve of the
|
||||
// funding manager's config, which corresponds to the maximum MaxHTLC value we
|
||||
// allow users to set when updating a channel policy.
|
||||
@ -3985,151 +3890,6 @@ func testUpdateChanStatus(net *lntest.NetworkHarness, t *harnessTest) {
|
||||
assertEdgeDisabled(alice, chanPoint, false)
|
||||
}
|
||||
|
||||
func testListPayments(net *lntest.NetworkHarness, t *harnessTest) {
|
||||
ctxb := context.Background()
|
||||
|
||||
// First start by deleting all payments that Alice knows of. This will
|
||||
// allow us to execute the test with a clean state for Alice.
|
||||
delPaymentsReq := &lnrpc.DeleteAllPaymentsRequest{}
|
||||
ctxt, _ := context.WithTimeout(ctxb, defaultTimeout)
|
||||
if _, err := net.Alice.DeleteAllPayments(ctxt, delPaymentsReq); err != nil {
|
||||
t.Fatalf("unable to delete payments: %v", err)
|
||||
}
|
||||
|
||||
// Check that there are no payments before test.
|
||||
reqInit := &lnrpc.ListPaymentsRequest{}
|
||||
ctxt, _ = context.WithTimeout(ctxt, defaultTimeout)
|
||||
paymentsRespInit, err := net.Alice.ListPayments(ctxt, reqInit)
|
||||
if err != nil {
|
||||
t.Fatalf("error when obtaining Alice payments: %v", err)
|
||||
}
|
||||
if len(paymentsRespInit.Payments) != 0 {
|
||||
t.Fatalf("incorrect number of payments, got %v, want %v",
|
||||
len(paymentsRespInit.Payments), 0)
|
||||
}
|
||||
|
||||
// Open a channel with 100k satoshis between Alice and Bob with Alice
|
||||
// being the sole funder of the channel.
|
||||
chanAmt := btcutil.Amount(100000)
|
||||
ctxt, _ = context.WithTimeout(ctxb, channelOpenTimeout)
|
||||
chanPoint := openChannelAndAssert(
|
||||
ctxt, t, net, net.Alice, net.Bob,
|
||||
lntest.OpenChannelParams{
|
||||
Amt: chanAmt,
|
||||
},
|
||||
)
|
||||
|
||||
// Now that the channel is open, create an invoice for Bob which
|
||||
// expects a payment of 1000 satoshis from Alice paid via a particular
|
||||
// preimage.
|
||||
const paymentAmt = 1000
|
||||
preimage := bytes.Repeat([]byte("B"), 32)
|
||||
invoice := &lnrpc.Invoice{
|
||||
Memo: "testing",
|
||||
RPreimage: preimage,
|
||||
Value: paymentAmt,
|
||||
}
|
||||
addInvoiceCtxt, _ := context.WithTimeout(ctxb, defaultTimeout)
|
||||
invoiceResp, err := net.Bob.AddInvoice(addInvoiceCtxt, invoice)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to add invoice: %v", err)
|
||||
}
|
||||
|
||||
// Wait for Alice to recognize and advertise the new channel generated
|
||||
// above.
|
||||
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout)
|
||||
if err = net.Alice.WaitForNetworkChannelOpen(ctxt, chanPoint); err != nil {
|
||||
t.Fatalf("alice didn't advertise channel before "+
|
||||
"timeout: %v", err)
|
||||
}
|
||||
if err = net.Bob.WaitForNetworkChannelOpen(ctxt, chanPoint); err != nil {
|
||||
t.Fatalf("bob didn't advertise channel before "+
|
||||
"timeout: %v", err)
|
||||
}
|
||||
|
||||
// With the invoice for Bob added, send a payment towards Alice paying
|
||||
// to the above generated invoice.
|
||||
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout)
|
||||
sendAndAssertSuccess(
|
||||
ctxt, t, net.Alice,
|
||||
&routerrpc.SendPaymentRequest{
|
||||
PaymentRequest: invoiceResp.PaymentRequest,
|
||||
TimeoutSeconds: 60,
|
||||
FeeLimitSat: 1000000,
|
||||
},
|
||||
)
|
||||
|
||||
// Grab Alice's list of payments, she should show the existence of
|
||||
// exactly one payment.
|
||||
req := &lnrpc.ListPaymentsRequest{}
|
||||
ctxt, _ = context.WithTimeout(ctxt, defaultTimeout)
|
||||
paymentsResp, err := net.Alice.ListPayments(ctxt, req)
|
||||
if err != nil {
|
||||
t.Fatalf("error when obtaining Alice payments: %v", err)
|
||||
}
|
||||
if len(paymentsResp.Payments) != 1 {
|
||||
t.Fatalf("incorrect number of payments, got %v, want %v",
|
||||
len(paymentsResp.Payments), 1)
|
||||
}
|
||||
p := paymentsResp.Payments[0]
|
||||
path := p.Htlcs[len(p.Htlcs)-1].Route.Hops
|
||||
|
||||
// Ensure that the stored path shows a direct payment to Bob with no
|
||||
// other nodes in-between.
|
||||
if len(path) != 1 || path[0].PubKey != net.Bob.PubKeyStr {
|
||||
t.Fatalf("incorrect path")
|
||||
}
|
||||
|
||||
// The payment amount should also match our previous payment directly.
|
||||
if p.Value != paymentAmt {
|
||||
t.Fatalf("incorrect amount, got %v, want %v",
|
||||
p.Value, paymentAmt)
|
||||
}
|
||||
|
||||
// The payment hash (or r-hash) should have been stored correctly.
|
||||
correctRHash := hex.EncodeToString(invoiceResp.RHash)
|
||||
if !reflect.DeepEqual(p.PaymentHash, correctRHash) {
|
||||
t.Fatalf("incorrect RHash, got %v, want %v",
|
||||
p.PaymentHash, correctRHash)
|
||||
}
|
||||
|
||||
// As we made a single-hop direct payment, there should have been no fee
|
||||
// applied.
|
||||
if p.Fee != 0 {
|
||||
t.Fatalf("incorrect Fee, got %v, want %v", p.Fee, 0)
|
||||
}
|
||||
|
||||
// Finally, verify that the payment request returned by the rpc matches
|
||||
// the invoice that we paid.
|
||||
if p.PaymentRequest != invoiceResp.PaymentRequest {
|
||||
t.Fatalf("incorrect payreq, got: %v, want: %v",
|
||||
p.PaymentRequest, invoiceResp.PaymentRequest)
|
||||
}
|
||||
|
||||
// Delete all payments from Alice. DB should have no payments.
|
||||
delReq := &lnrpc.DeleteAllPaymentsRequest{}
|
||||
ctxt, _ = context.WithTimeout(ctxt, defaultTimeout)
|
||||
_, err = net.Alice.DeleteAllPayments(ctxt, delReq)
|
||||
if err != nil {
|
||||
t.Fatalf("Can't delete payments at the end: %v", err)
|
||||
}
|
||||
|
||||
// Check that there are no payments after test.
|
||||
listReq := &lnrpc.ListPaymentsRequest{}
|
||||
ctxt, _ = context.WithTimeout(ctxt, defaultTimeout)
|
||||
paymentsResp, err = net.Alice.ListPayments(ctxt, listReq)
|
||||
if err != nil {
|
||||
t.Fatalf("error when obtaining Alice payments: %v", err)
|
||||
}
|
||||
if len(paymentsResp.Payments) != 0 {
|
||||
t.Fatalf("incorrect number of payments, got %v, want %v",
|
||||
len(paymentsRespInit.Payments), 0)
|
||||
}
|
||||
|
||||
ctxt, _ = context.WithTimeout(ctxb, channelCloseTimeout)
|
||||
closeChannelAndAssert(ctxt, t, net, net.Alice, chanPoint, false)
|
||||
}
|
||||
|
||||
// updateChannelPolicy updates the channel policy of node to the
|
||||
// given fees and timelock delta. This function blocks until
|
||||
// listenerNode has received the policy update.
|
||||
@ -4282,239 +4042,6 @@ func testUnannouncedChannels(net *lntest.NetworkHarness, t *harnessTest) {
|
||||
closeChannelAndAssert(ctxt, t, net, net.Alice, fundingChanPoint, false)
|
||||
}
|
||||
|
||||
func testInvoiceSubscriptions(net *lntest.NetworkHarness, t *harnessTest) {
|
||||
ctxb := context.Background()
|
||||
|
||||
const chanAmt = btcutil.Amount(500000)
|
||||
|
||||
// Open a channel with 500k satoshis between Alice and Bob with Alice
|
||||
// being the sole funder of the channel.
|
||||
ctxt, _ := context.WithTimeout(ctxb, channelOpenTimeout)
|
||||
chanPoint := openChannelAndAssert(
|
||||
ctxt, t, net, net.Alice, net.Bob,
|
||||
lntest.OpenChannelParams{
|
||||
Amt: chanAmt,
|
||||
},
|
||||
)
|
||||
|
||||
// Next create a new invoice for Bob requesting 1k satoshis.
|
||||
// TODO(roasbeef): make global list of invoices for each node to re-use
|
||||
// and avoid collisions
|
||||
const paymentAmt = 1000
|
||||
invoice := &lnrpc.Invoice{
|
||||
Memo: "testing",
|
||||
RPreimage: makeFakePayHash(t),
|
||||
Value: paymentAmt,
|
||||
}
|
||||
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout)
|
||||
invoiceResp, err := net.Bob.AddInvoice(ctxt, invoice)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to add invoice: %v", err)
|
||||
}
|
||||
lastAddIndex := invoiceResp.AddIndex
|
||||
|
||||
// Create a new invoice subscription client for Bob, the notification
|
||||
// should be dispatched shortly below.
|
||||
req := &lnrpc.InvoiceSubscription{}
|
||||
ctx, cancelInvoiceSubscription := context.WithCancel(ctxb)
|
||||
bobInvoiceSubscription, err := net.Bob.SubscribeInvoices(ctx, req)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to subscribe to bob's invoice updates: %v", err)
|
||||
}
|
||||
|
||||
var settleIndex uint64
|
||||
quit := make(chan struct{})
|
||||
updateSent := make(chan struct{})
|
||||
go func() {
|
||||
invoiceUpdate, err := bobInvoiceSubscription.Recv()
|
||||
select {
|
||||
case <-quit:
|
||||
// Received cancellation
|
||||
return
|
||||
default:
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("unable to recv invoice update: %v", err)
|
||||
}
|
||||
|
||||
// The invoice update should exactly match the invoice created
|
||||
// above, but should now be settled and have SettleDate
|
||||
if !invoiceUpdate.Settled { // nolint:staticcheck
|
||||
t.Fatalf("invoice not settled but should be")
|
||||
}
|
||||
if invoiceUpdate.SettleDate == 0 {
|
||||
t.Fatalf("invoice should have non zero settle date, but doesn't")
|
||||
}
|
||||
|
||||
if !bytes.Equal(invoiceUpdate.RPreimage, invoice.RPreimage) {
|
||||
t.Fatalf("payment preimages don't match: expected %v, got %v",
|
||||
invoice.RPreimage, invoiceUpdate.RPreimage)
|
||||
}
|
||||
|
||||
if invoiceUpdate.SettleIndex == 0 {
|
||||
t.Fatalf("invoice should have settle index")
|
||||
}
|
||||
|
||||
settleIndex = invoiceUpdate.SettleIndex
|
||||
|
||||
close(updateSent)
|
||||
}()
|
||||
|
||||
// Wait for the channel to be recognized by both Alice and Bob before
|
||||
// continuing the rest of the test.
|
||||
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout)
|
||||
err = net.Alice.WaitForNetworkChannelOpen(ctxt, chanPoint)
|
||||
if err != nil {
|
||||
// TODO(roasbeef): will need to make num blocks to advertise a
|
||||
// node param
|
||||
close(quit)
|
||||
t.Fatalf("channel not seen by alice before timeout: %v", err)
|
||||
}
|
||||
|
||||
// With the assertion above set up, send a payment from Alice to Bob
|
||||
// which should finalize and settle the invoice.
|
||||
sendReq := &routerrpc.SendPaymentRequest{
|
||||
PaymentRequest: invoiceResp.PaymentRequest,
|
||||
TimeoutSeconds: 60,
|
||||
FeeLimitMsat: noFeeLimitMsat,
|
||||
}
|
||||
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout)
|
||||
stream, err := net.Alice.RouterClient.SendPaymentV2(ctxt, sendReq)
|
||||
if err != nil {
|
||||
close(quit)
|
||||
t.Fatalf("unable to send payment: %v", err)
|
||||
}
|
||||
result, err := getPaymentResult(stream)
|
||||
if err != nil {
|
||||
close(quit)
|
||||
t.Fatalf("cannot get payment result: %v", err)
|
||||
}
|
||||
if result.Status != lnrpc.Payment_SUCCEEDED {
|
||||
close(quit)
|
||||
t.Fatalf("error when attempting recv: %v", result.Status)
|
||||
}
|
||||
|
||||
select {
|
||||
case <-time.After(time.Second * 10):
|
||||
close(quit)
|
||||
t.Fatalf("update not sent after 10 seconds")
|
||||
case <-updateSent: // Fall through on success
|
||||
}
|
||||
|
||||
// With the base case working, we'll now cancel Bob's current
|
||||
// subscription in order to exercise the backlog fill behavior.
|
||||
cancelInvoiceSubscription()
|
||||
|
||||
// We'll now add 3 more invoices to Bob's invoice registry.
|
||||
const numInvoices = 3
|
||||
payReqs, _, newInvoices, err := createPayReqs(
|
||||
net.Bob, paymentAmt, numInvoices,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to create pay reqs: %v", err)
|
||||
}
|
||||
|
||||
// Now that the set of invoices has been added, we'll re-register for
|
||||
// streaming invoice notifications for Bob, this time specifying the
|
||||
// add invoice of the last prior invoice.
|
||||
req = &lnrpc.InvoiceSubscription{
|
||||
AddIndex: lastAddIndex,
|
||||
}
|
||||
ctx, cancelInvoiceSubscription = context.WithCancel(ctxb)
|
||||
bobInvoiceSubscription, err = net.Bob.SubscribeInvoices(ctx, req)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to subscribe to bob's invoice updates: %v", err)
|
||||
}
|
||||
|
||||
// Since we specified a value of the prior add index above, we should
|
||||
// now immediately get the invoices we just added as we should get the
|
||||
// backlog of notifications.
|
||||
for i := 0; i < numInvoices; i++ {
|
||||
invoiceUpdate, err := bobInvoiceSubscription.Recv()
|
||||
if err != nil {
|
||||
t.Fatalf("unable to receive subscription")
|
||||
}
|
||||
|
||||
// We should now get the ith invoice we added, as they should
|
||||
// be returned in order.
|
||||
if invoiceUpdate.Settled { // nolint:staticcheck
|
||||
t.Fatalf("should have only received add events")
|
||||
}
|
||||
originalInvoice := newInvoices[i]
|
||||
rHash := sha256.Sum256(originalInvoice.RPreimage)
|
||||
if !bytes.Equal(invoiceUpdate.RHash, rHash[:]) {
|
||||
t.Fatalf("invoices have mismatched payment hashes: "+
|
||||
"expected %x, got %x", rHash[:],
|
||||
invoiceUpdate.RHash)
|
||||
}
|
||||
}
|
||||
|
||||
cancelInvoiceSubscription()
|
||||
|
||||
// We'll now have Bob settle out the remainder of these invoices so we
|
||||
// can test that all settled invoices are properly notified.
|
||||
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout)
|
||||
err = completePaymentRequests(
|
||||
ctxt, net.Alice, net.Alice.RouterClient, payReqs, true,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to send payment: %v", err)
|
||||
}
|
||||
|
||||
// With the set of invoices paid, we'll now cancel the old
|
||||
// subscription, and create a new one for Bob, this time using the
|
||||
// settle index to obtain the backlog of settled invoices.
|
||||
req = &lnrpc.InvoiceSubscription{
|
||||
SettleIndex: settleIndex,
|
||||
}
|
||||
ctx, cancelInvoiceSubscription = context.WithCancel(ctxb)
|
||||
bobInvoiceSubscription, err = net.Bob.SubscribeInvoices(ctx, req)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to subscribe to bob's invoice updates: %v", err)
|
||||
}
|
||||
|
||||
defer cancelInvoiceSubscription()
|
||||
|
||||
// As we specified the index of the past settle index, we should now
|
||||
// receive notifications for the three HTLCs that we just settled. As
|
||||
// the order that the HTLCs will be settled in is partially randomized,
|
||||
// we'll use a map to assert that the proper set has been settled.
|
||||
settledInvoices := make(map[[32]byte]struct{})
|
||||
for _, invoice := range newInvoices {
|
||||
rHash := sha256.Sum256(invoice.RPreimage)
|
||||
settledInvoices[rHash] = struct{}{}
|
||||
}
|
||||
for i := 0; i < numInvoices; i++ {
|
||||
invoiceUpdate, err := bobInvoiceSubscription.Recv()
|
||||
if err != nil {
|
||||
t.Fatalf("unable to receive subscription")
|
||||
}
|
||||
|
||||
// We should now get the ith invoice we added, as they should
|
||||
// be returned in order.
|
||||
if !invoiceUpdate.Settled { // nolint:staticcheck
|
||||
t.Fatalf("should have only received settle events")
|
||||
}
|
||||
|
||||
var rHash [32]byte
|
||||
copy(rHash[:], invoiceUpdate.RHash)
|
||||
if _, ok := settledInvoices[rHash]; !ok {
|
||||
t.Fatalf("unknown invoice settled: %x", rHash)
|
||||
}
|
||||
|
||||
delete(settledInvoices, rHash)
|
||||
}
|
||||
|
||||
// At this point, all the invoices should be fully settled.
|
||||
if len(settledInvoices) != 0 {
|
||||
t.Fatalf("not all invoices settled")
|
||||
}
|
||||
|
||||
ctxt, _ = context.WithTimeout(ctxb, channelCloseTimeout)
|
||||
closeChannelAndAssert(ctxt, t, net, net.Alice, chanPoint, false)
|
||||
}
|
||||
|
||||
// channelSubscription houses the proxied update and error chans for a node's
|
||||
// channel subscriptions.
|
||||
type channelSubscription struct {
|
||||
@ -7551,340 +7078,6 @@ func testNodeSignVerify(net *lntest.NetworkHarness, t *harnessTest) {
|
||||
closeChannelAndAssert(ctxt, t, net, net.Alice, aliceBobCh, false)
|
||||
}
|
||||
|
||||
// testAsyncPayments tests the performance of the async payments.
|
||||
func testAsyncPayments(net *lntest.NetworkHarness, t *harnessTest) {
|
||||
ctxb := context.Background()
|
||||
|
||||
const (
|
||||
paymentAmt = 100
|
||||
)
|
||||
|
||||
// First establish a channel with a capacity equals to the overall
|
||||
// amount of payments, between Alice and Bob, at the end of the test
|
||||
// Alice should send all money from her side to Bob.
|
||||
ctxt, _ := context.WithTimeout(ctxb, channelOpenTimeout)
|
||||
channelCapacity := btcutil.Amount(paymentAmt * 2000)
|
||||
chanPoint := openChannelAndAssert(
|
||||
ctxt, t, net, net.Alice, net.Bob,
|
||||
lntest.OpenChannelParams{
|
||||
Amt: channelCapacity,
|
||||
},
|
||||
)
|
||||
|
||||
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout)
|
||||
info, err := getChanInfo(ctxt, net.Alice)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to get alice channel info: %v", err)
|
||||
}
|
||||
|
||||
// We'll create a number of invoices equal the max number of HTLCs that
|
||||
// can be carried in one direction. The number on the commitment will
|
||||
// likely be lower, but we can't guarantee that any more HTLCs will
|
||||
// succeed due to the limited path diversity and inability of the router
|
||||
// to retry via another path.
|
||||
numInvoices := int(input.MaxHTLCNumber / 2)
|
||||
|
||||
bobAmt := int64(numInvoices * paymentAmt)
|
||||
aliceAmt := info.LocalBalance - bobAmt
|
||||
|
||||
// With the channel open, we'll create invoices for Bob that Alice
|
||||
// will pay to in order to advance the state of the channel.
|
||||
bobPayReqs, _, _, err := createPayReqs(
|
||||
net.Bob, paymentAmt, numInvoices,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to create pay reqs: %v", err)
|
||||
}
|
||||
|
||||
// Wait for Alice to receive the channel edge from the funding manager.
|
||||
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout)
|
||||
err = net.Alice.WaitForNetworkChannelOpen(ctxt, chanPoint)
|
||||
if err != nil {
|
||||
t.Fatalf("alice didn't see the alice->bob channel before "+
|
||||
"timeout: %v", err)
|
||||
}
|
||||
|
||||
// Simultaneously send payments from Alice to Bob using of Bob's payment
|
||||
// hashes generated above.
|
||||
now := time.Now()
|
||||
errChan := make(chan error)
|
||||
statusChan := make(chan *lnrpc.Payment)
|
||||
for i := 0; i < numInvoices; i++ {
|
||||
payReq := bobPayReqs[i]
|
||||
go func() {
|
||||
ctxt, _ = context.WithTimeout(ctxb, lntest.AsyncBenchmarkTimeout)
|
||||
stream, err := net.Alice.RouterClient.SendPaymentV2(
|
||||
ctxt,
|
||||
&routerrpc.SendPaymentRequest{
|
||||
PaymentRequest: payReq,
|
||||
TimeoutSeconds: 60,
|
||||
FeeLimitMsat: noFeeLimitMsat,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
errChan <- err
|
||||
}
|
||||
result, err := getPaymentResult(stream)
|
||||
if err != nil {
|
||||
errChan <- err
|
||||
}
|
||||
|
||||
statusChan <- result
|
||||
}()
|
||||
}
|
||||
|
||||
// Wait until all the payments have settled.
|
||||
for i := 0; i < numInvoices; i++ {
|
||||
select {
|
||||
case result := <-statusChan:
|
||||
if result.Status == lnrpc.Payment_SUCCEEDED {
|
||||
continue
|
||||
}
|
||||
|
||||
case err := <-errChan:
|
||||
t.Fatalf("payment error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// All payments have been sent, mark the finish time.
|
||||
timeTaken := time.Since(now)
|
||||
|
||||
// Next query for Bob's and Alice's channel states, in order to confirm
|
||||
// that all payment have been successful transmitted.
|
||||
|
||||
// Wait for the revocation to be received so alice no longer has pending
|
||||
// htlcs listed and has correct balances. This is needed due to the fact
|
||||
// that we now pipeline the settles.
|
||||
err = wait.Predicate(func() bool {
|
||||
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout)
|
||||
aliceChan, err := getChanInfo(ctxt, net.Alice)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
if len(aliceChan.PendingHtlcs) != 0 {
|
||||
return false
|
||||
}
|
||||
if aliceChan.RemoteBalance != bobAmt {
|
||||
return false
|
||||
}
|
||||
if aliceChan.LocalBalance != aliceAmt {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}, time.Second*5)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to assert alice's pending htlcs and/or remote/local balance")
|
||||
}
|
||||
|
||||
// Wait for Bob to receive revocation from Alice.
|
||||
time.Sleep(2 * time.Second)
|
||||
|
||||
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout)
|
||||
bobChan, err := getChanInfo(ctxt, net.Bob)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to get bob's channel info: %v", err)
|
||||
}
|
||||
if len(bobChan.PendingHtlcs) != 0 {
|
||||
t.Fatalf("bob's pending htlcs is incorrect, got %v, "+
|
||||
"expected %v", len(bobChan.PendingHtlcs), 0)
|
||||
}
|
||||
if bobChan.LocalBalance != bobAmt {
|
||||
t.Fatalf("bob's local balance is incorrect, got %v, expected"+
|
||||
" %v", bobChan.LocalBalance, bobAmt)
|
||||
}
|
||||
if bobChan.RemoteBalance != aliceAmt {
|
||||
t.Fatalf("bob's remote balance is incorrect, got %v, "+
|
||||
"expected %v", bobChan.RemoteBalance, aliceAmt)
|
||||
}
|
||||
|
||||
t.Log("\tBenchmark info: Elapsed time: ", timeTaken)
|
||||
t.Log("\tBenchmark info: TPS: ", float64(numInvoices)/timeTaken.Seconds())
|
||||
|
||||
// Finally, immediately close the channel. This function will also
|
||||
// block until the channel is closed and will additionally assert the
|
||||
// relevant channel closing post conditions.
|
||||
ctxt, _ = context.WithTimeout(ctxb, channelCloseTimeout)
|
||||
closeChannelAndAssert(ctxt, t, net, net.Alice, chanPoint, false)
|
||||
}
|
||||
|
||||
// testBidirectionalAsyncPayments tests that nodes are able to send the
|
||||
// payments to each other in async manner without blocking.
|
||||
func testBidirectionalAsyncPayments(net *lntest.NetworkHarness, t *harnessTest) {
|
||||
ctxb := context.Background()
|
||||
|
||||
const (
|
||||
paymentAmt = 1000
|
||||
)
|
||||
|
||||
// First establish a channel with a capacity equals to the overall
|
||||
// amount of payments, between Alice and Bob, at the end of the test
|
||||
// Alice should send all money from her side to Bob.
|
||||
ctxt, _ := context.WithTimeout(ctxb, channelOpenTimeout)
|
||||
chanPoint := openChannelAndAssert(
|
||||
ctxt, t, net, net.Alice, net.Bob,
|
||||
lntest.OpenChannelParams{
|
||||
Amt: paymentAmt * 2000,
|
||||
PushAmt: paymentAmt * 1000,
|
||||
},
|
||||
)
|
||||
|
||||
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout)
|
||||
info, err := getChanInfo(ctxt, net.Alice)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to get alice channel info: %v", err)
|
||||
}
|
||||
|
||||
// We'll create a number of invoices equal the max number of HTLCs that
|
||||
// can be carried in one direction. The number on the commitment will
|
||||
// likely be lower, but we can't guarantee that any more HTLCs will
|
||||
// succeed due to the limited path diversity and inability of the router
|
||||
// to retry via another path.
|
||||
numInvoices := int(input.MaxHTLCNumber / 2)
|
||||
|
||||
// Nodes should exchange the same amount of money and because of this
|
||||
// at the end balances should remain the same.
|
||||
aliceAmt := info.LocalBalance
|
||||
bobAmt := info.RemoteBalance
|
||||
|
||||
// With the channel open, we'll create invoices for Bob that Alice
|
||||
// will pay to in order to advance the state of the channel.
|
||||
bobPayReqs, _, _, err := createPayReqs(
|
||||
net.Bob, paymentAmt, numInvoices,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to create pay reqs: %v", err)
|
||||
}
|
||||
|
||||
// With the channel open, we'll create invoices for Alice that Bob
|
||||
// will pay to in order to advance the state of the channel.
|
||||
alicePayReqs, _, _, err := createPayReqs(
|
||||
net.Alice, paymentAmt, numInvoices,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to create pay reqs: %v", err)
|
||||
}
|
||||
|
||||
// Wait for Alice to receive the channel edge from the funding manager.
|
||||
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout)
|
||||
if err = net.Alice.WaitForNetworkChannelOpen(ctxt, chanPoint); err != nil {
|
||||
t.Fatalf("alice didn't see the alice->bob channel before "+
|
||||
"timeout: %v", err)
|
||||
}
|
||||
if err = net.Bob.WaitForNetworkChannelOpen(ctxt, chanPoint); err != nil {
|
||||
t.Fatalf("bob didn't see the bob->alice channel before "+
|
||||
"timeout: %v", err)
|
||||
}
|
||||
|
||||
// Reset mission control to prevent previous payment results from
|
||||
// interfering with this test. A new channel has been opened, but
|
||||
// mission control operates on node pairs.
|
||||
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout)
|
||||
_, err = net.Alice.RouterClient.ResetMissionControl(
|
||||
ctxt, &routerrpc.ResetMissionControlRequest{},
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to reset mc for alice: %v", err)
|
||||
}
|
||||
|
||||
// Send payments from Alice to Bob and from Bob to Alice in async
|
||||
// manner.
|
||||
errChan := make(chan error)
|
||||
statusChan := make(chan *lnrpc.Payment)
|
||||
|
||||
send := func(node *lntest.HarnessNode, payReq string) {
|
||||
go func() {
|
||||
ctxt, _ = context.WithTimeout(
|
||||
ctxb, lntest.AsyncBenchmarkTimeout,
|
||||
)
|
||||
stream, err := node.RouterClient.SendPaymentV2(
|
||||
ctxt,
|
||||
&routerrpc.SendPaymentRequest{
|
||||
PaymentRequest: payReq,
|
||||
TimeoutSeconds: 60,
|
||||
FeeLimitMsat: noFeeLimitMsat,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
errChan <- err
|
||||
}
|
||||
result, err := getPaymentResult(stream)
|
||||
if err != nil {
|
||||
errChan <- err
|
||||
}
|
||||
|
||||
statusChan <- result
|
||||
}()
|
||||
}
|
||||
|
||||
for i := 0; i < numInvoices; i++ {
|
||||
send(net.Bob, alicePayReqs[i])
|
||||
send(net.Alice, bobPayReqs[i])
|
||||
}
|
||||
|
||||
// Expect all payments to succeed.
|
||||
for i := 0; i < 2*numInvoices; i++ {
|
||||
select {
|
||||
case result := <-statusChan:
|
||||
if result.Status != lnrpc.Payment_SUCCEEDED {
|
||||
t.Fatalf("payment error: %v", result.Status)
|
||||
}
|
||||
|
||||
case err := <-errChan:
|
||||
t.Fatalf("payment error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Wait for Alice and Bob to receive revocations messages, and update
|
||||
// states, i.e. balance info.
|
||||
time.Sleep(1 * time.Second)
|
||||
|
||||
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout)
|
||||
aliceInfo, err := getChanInfo(ctxt, net.Alice)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to get bob's channel info: %v", err)
|
||||
}
|
||||
if aliceInfo.RemoteBalance != bobAmt {
|
||||
t.Fatalf("alice's remote balance is incorrect, got %v, "+
|
||||
"expected %v", aliceInfo.RemoteBalance, bobAmt)
|
||||
}
|
||||
if aliceInfo.LocalBalance != aliceAmt {
|
||||
t.Fatalf("alice's local balance is incorrect, got %v, "+
|
||||
"expected %v", aliceInfo.LocalBalance, aliceAmt)
|
||||
}
|
||||
if len(aliceInfo.PendingHtlcs) != 0 {
|
||||
t.Fatalf("alice's pending htlcs is incorrect, got %v, "+
|
||||
"expected %v", len(aliceInfo.PendingHtlcs), 0)
|
||||
}
|
||||
|
||||
// Next query for Bob's and Alice's channel states, in order to confirm
|
||||
// that all payment have been successful transmitted.
|
||||
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout)
|
||||
bobInfo, err := getChanInfo(ctxt, net.Bob)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to get bob's channel info: %v", err)
|
||||
}
|
||||
|
||||
if bobInfo.LocalBalance != bobAmt {
|
||||
t.Fatalf("bob's local balance is incorrect, got %v, expected"+
|
||||
" %v", bobInfo.LocalBalance, bobAmt)
|
||||
}
|
||||
if bobInfo.RemoteBalance != aliceAmt {
|
||||
t.Fatalf("bob's remote balance is incorrect, got %v, "+
|
||||
"expected %v", bobInfo.RemoteBalance, aliceAmt)
|
||||
}
|
||||
if len(bobInfo.PendingHtlcs) != 0 {
|
||||
t.Fatalf("bob's pending htlcs is incorrect, got %v, "+
|
||||
"expected %v", len(bobInfo.PendingHtlcs), 0)
|
||||
}
|
||||
|
||||
// Finally, immediately close the channel. This function will also
|
||||
// block until the channel is closed and will additionally assert the
|
||||
// relevant channel closing post conditions.
|
||||
ctxt, _ = context.WithTimeout(ctxb, channelCloseTimeout)
|
||||
closeChannelAndAssert(ctxt, t, net, net.Alice, chanPoint, false)
|
||||
}
|
||||
|
||||
// testSwitchCircuitPersistence creates a multihop network to ensure the sender
|
||||
// and intermediaries are persisting their open payment circuits. After
|
||||
// forwarding a packet via an outgoing link, all are restarted, and expected to
|
||||
|
Loading…
Reference in New Issue
Block a user