mirror of
https://github.com/lightningnetwork/lnd.git
synced 2024-11-19 09:53:54 +01:00
test: add async payments stress test
In this commit asynchronous stress payment test have been added which tests the ability of lnd to be sustainable.
This commit is contained in:
parent
6861df0e23
commit
f48f653a7c
188
lnd_test.go
188
lnd_test.go
@ -17,6 +17,8 @@ import (
|
|||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
|
||||||
|
"math/rand"
|
||||||
|
|
||||||
"github.com/davecgh/go-spew/spew"
|
"github.com/davecgh/go-spew/spew"
|
||||||
"github.com/go-errors/errors"
|
"github.com/go-errors/errors"
|
||||||
"github.com/lightningnetwork/lnd/lnrpc"
|
"github.com/lightningnetwork/lnd/lnrpc"
|
||||||
@ -2454,6 +2456,188 @@ func testNodeSignVerify(net *networkHarness, t *harnessTest) {
|
|||||||
closeChannelAndAssert(ctxt, t, net, net.Alice, aliceBobCh, false)
|
closeChannelAndAssert(ctxt, t, net, net.Alice, aliceBobCh, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// testAsyncPayments tests the performance of the async payments, and also
|
||||||
|
// checks that balances of both sides can't be become negative under stress
|
||||||
|
// payment strikes.
|
||||||
|
func testAsyncPayments(net *networkHarness, t *harnessTest) {
|
||||||
|
ctxb := context.Background()
|
||||||
|
|
||||||
|
// As we'll be querying the channels state frequently we'll
|
||||||
|
// create a closure helper function for the purpose.
|
||||||
|
getChanInfo := func(node *lightningNode) (*lnrpc.ActiveChannel, error) {
|
||||||
|
req := &lnrpc.ListChannelsRequest{}
|
||||||
|
channelInfo, err := node.ListChannels(ctxb, req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if len(channelInfo.Channels) != 1 {
|
||||||
|
t.Fatalf("node should only have a single channel, "+
|
||||||
|
"instead he has %v",
|
||||||
|
len(channelInfo.Channels))
|
||||||
|
}
|
||||||
|
|
||||||
|
return channelInfo.Channels[0], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
timeout = time.Duration(time.Second * 5)
|
||||||
|
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, timeout)
|
||||||
|
chanPoint := openChannelAndAssert(ctxt, t, net, net.Alice, net.Bob,
|
||||||
|
paymentAmt*2000, 0)
|
||||||
|
|
||||||
|
info, err := getChanInfo(net.Alice)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unable to get alice channel info: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate the number of invoices.
|
||||||
|
numInvoices := int(info.LocalBalance / paymentAmt)
|
||||||
|
bobAmt := int64(numInvoices * paymentAmt)
|
||||||
|
aliceAmt := info.LocalBalance - bobAmt
|
||||||
|
|
||||||
|
// Send one more payment in order to cause insufficient capacity error.
|
||||||
|
numInvoices++
|
||||||
|
|
||||||
|
// Initialize seed random in order to generate invoices.
|
||||||
|
rand.Seed(time.Now().UnixNano())
|
||||||
|
|
||||||
|
// With the channel open, we'll create a invoices for Bob that
|
||||||
|
// Alice will pay to in order to advance the state of the channel.
|
||||||
|
bobPaymentHashes := make([][]byte, numInvoices)
|
||||||
|
for i := 0; i < numInvoices; i++ {
|
||||||
|
preimage := make([]byte, 32)
|
||||||
|
_, err := rand.Read(preimage)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unable to generate preimage: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
invoice := &lnrpc.Invoice{
|
||||||
|
Memo: "testing",
|
||||||
|
RPreimage: preimage,
|
||||||
|
Value: paymentAmt,
|
||||||
|
}
|
||||||
|
resp, err := net.Bob.AddInvoice(ctxb, invoice)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unable to add invoice: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
bobPaymentHashes[i] = resp.RHash
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for Alice to receive the channel edge from the funding manager.
|
||||||
|
ctxt, _ = context.WithTimeout(ctxb, timeout)
|
||||||
|
err = net.Alice.WaitForNetworkChannelOpen(ctxt, chanPoint)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("alice didn't see the alice->bob channel before "+
|
||||||
|
"timeout: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Open up a payment stream to Alice that we'll use to send payment to
|
||||||
|
// Bob. We also create a small helper function to send payments to Bob,
|
||||||
|
// consuming the payment hashes we generated above.
|
||||||
|
alicePayStream, err := net.Alice.SendPayment(ctxb)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unable to create payment stream for alice: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send payments from Alice to Bob using of Bob's payment hashes
|
||||||
|
// generated above.
|
||||||
|
now := time.Now()
|
||||||
|
for i := 0; i < numInvoices; i++ {
|
||||||
|
sendReq := &lnrpc.SendRequest{
|
||||||
|
PaymentHash: bobPaymentHashes[i],
|
||||||
|
Dest: net.Bob.PubKey[:],
|
||||||
|
Amt: paymentAmt,
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := alicePayStream.Send(sendReq); err != nil {
|
||||||
|
t.Fatalf("unable to send payment: "+
|
||||||
|
"%v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
errorReceived := false
|
||||||
|
for i := 0; i < numInvoices; i++ {
|
||||||
|
if resp, err := alicePayStream.Recv(); err != nil {
|
||||||
|
t.Fatalf("payment stream have been closed: %v", err)
|
||||||
|
} else if resp.PaymentError != "" {
|
||||||
|
if strings.Contains(resp.PaymentError,
|
||||||
|
"Insufficient") {
|
||||||
|
if errorReceived {
|
||||||
|
t.Fatalf("redundant payment "+
|
||||||
|
"error: %v", resp.PaymentError)
|
||||||
|
}
|
||||||
|
|
||||||
|
errorReceived = true
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Fatalf("unable to send payment: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !errorReceived {
|
||||||
|
t.Fatalf("insufficient capacity error haven't been received")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
aliceChan, err := getChanInfo(net.Alice)
|
||||||
|
if len(aliceChan.PendingHtlcs) != 0 {
|
||||||
|
t.Fatalf("alice's pending htlcs is incorrect, got %v, "+
|
||||||
|
"expected %v", len(aliceChan.PendingHtlcs), 0)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unable to get bob's channel info: %v", err)
|
||||||
|
}
|
||||||
|
if aliceChan.RemoteBalance != bobAmt {
|
||||||
|
t.Fatalf("alice's remote balance is incorrect, got %v, "+
|
||||||
|
"expected %v", aliceChan.RemoteBalance, bobAmt)
|
||||||
|
}
|
||||||
|
if aliceChan.LocalBalance != aliceAmt {
|
||||||
|
t.Fatalf("alice's local balance is incorrect, got %v, "+
|
||||||
|
"expected %v", aliceChan.LocalBalance, aliceAmt)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for Bob to receive revocation from Alice.
|
||||||
|
time.Sleep(2 * time.Second)
|
||||||
|
|
||||||
|
bobChan, err := getChanInfo(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)/float64(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, timeout)
|
||||||
|
closeChannelAndAssert(ctxt, t, net, net.Alice, chanPoint, false)
|
||||||
|
}
|
||||||
|
|
||||||
type testCase struct {
|
type testCase struct {
|
||||||
name string
|
name string
|
||||||
test func(net *networkHarness, t *harnessTest)
|
test func(net *networkHarness, t *harnessTest)
|
||||||
@ -2521,6 +2705,10 @@ var testsCases = []*testCase{
|
|||||||
name: "node sign verify",
|
name: "node sign verify",
|
||||||
test: testNodeSignVerify,
|
test: testNodeSignVerify,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "async payments benchmark",
|
||||||
|
test: testAsyncPayments,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
// TODO(roasbeef): test always needs to be last as Bob's state
|
// TODO(roasbeef): test always needs to be last as Bob's state
|
||||||
// is borked since we trick him into attempting to cheat Alice?
|
// is borked since we trick him into attempting to cheat Alice?
|
||||||
|
Loading…
Reference in New Issue
Block a user