From b3f376fac98499187dfa740a087084964d711542 Mon Sep 17 00:00:00 2001 From: Andrey Samokhvalov Date: Thu, 18 May 2017 17:36:55 +0300 Subject: [PATCH] test: add async bydirectional test In this commit additional test have been added which tests the ability of Alice and Bob asynchroniously exchange the payment between each other. This scenario will be higly frequent in the payment between payment providers. --- lnd_test.go | 237 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 237 insertions(+) diff --git a/lnd_test.go b/lnd_test.go index 03dd24ab..7f8fcc54 100644 --- a/lnd_test.go +++ b/lnd_test.go @@ -2638,6 +2638,239 @@ func testAsyncPayments(net *networkHarness, t *harnessTest) { 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 *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, paymentAmt*1000) + + 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) + + // 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 + + // 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 + } + + // With the channel open, we'll create a invoices for Alice that + // Bob will pay to in order to advance the state of the channel. + alicePaymentHashes := 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.Alice.AddInvoice(ctxb, invoice) + if err != nil { + t.Fatalf("unable to add invoice: %v", err) + } + + alicePaymentHashes[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 streams to Alice and to Bob, that we'll use to + // send payment between nodes. + alicePayStream, err := net.Alice.SendPayment(ctxb) + if err != nil { + t.Fatalf("unable to create payment stream for alice: %v", err) + } + + bobPayStream, err := net.Bob.SendPayment(ctxb) + if err != nil { + t.Fatalf("unable to create payment stream for bob: %v", err) + } + + // Send payments from Alice to Bob and from Bob to Alice in async + // manner. + for i := 0; i < numInvoices; i++ { + aliceSendReq := &lnrpc.SendRequest{ + PaymentHash: bobPaymentHashes[i], + Dest: net.Bob.PubKey[:], + Amt: paymentAmt, + } + + bobSendReq := &lnrpc.SendRequest{ + PaymentHash: alicePaymentHashes[i], + Dest: net.Alice.PubKey[:], + Amt: paymentAmt, + } + + if err := alicePayStream.Send(aliceSendReq); err != nil { + t.Fatalf("unable to send payment: "+ + "%v", err) + } + + if err := bobPayStream.Send(bobSendReq); err != nil { + t.Fatalf("unable to send payment: "+ + "%v", err) + } + } + + errChan := make(chan error) + go func() { + for i := 0; i < numInvoices; i++ { + if resp, err := alicePayStream.Recv(); err != nil { + errChan <- errors.Errorf("payment stream has"+ + " been closed: %v", err) + } else if resp.PaymentError != "" { + errChan <- errors.Errorf("unable to send "+ + "payment from alice to bob: %v", + resp.PaymentError) + } + } + errChan <- nil + }() + + go func() { + for i := 0; i < numInvoices; i++ { + if resp, err := bobPayStream.Recv(); err != nil { + errChan <- errors.Errorf("payment stream has"+ + " been closed: %v", err) + } else if resp.PaymentError != "" { + errChan <- errors.Errorf("unable to send "+ + "payment from bob to alice: %v", + resp.PaymentError) + } + } + errChan <- nil + }() + + // Wait for Alice and Bob receive their payments, and throw and error + // if something goes wrong. + for i := 0; i < 2; i++ { + select { + case err := <-errChan: + if err != nil { + t.Fatalf(err.Error()) + } + case <-time.After(time.Second * 7): + t.Fatalf("waiting for payments to finish too long " + + "(7s)") + } + } + + // Wait for Alice and Bob to receive revocations messages, and update + // states, i.e. balance info. + time.Sleep(1 * time.Second) + + aliceInfo, err := getChanInfo(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. + bobInfo, err := getChanInfo(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, timeout) + closeChannelAndAssert(ctxt, t, net, net.Alice, chanPoint, false) +} + type testCase struct { name string test func(net *networkHarness, t *harnessTest) @@ -2709,6 +2942,10 @@ var testsCases = []*testCase{ name: "async payments benchmark", test: testAsyncPayments, }, + { + name: "async bidirectional payments", + test: testBidirectionalAsyncPayments, + }, { // TODO(roasbeef): test always needs to be last as Bob's state // is borked since we trick him into attempting to cheat Alice?