From b437d031748e48427899971a969016ee0ad75556 Mon Sep 17 00:00:00 2001 From: "Johan T. Halseth" Date: Thu, 26 Jul 2018 19:23:18 +0200 Subject: [PATCH] fundingmanager test: add TestFundingManagerMaxPendingChannels --- fundingmanager_test.go | 202 +++++++++++++++++++++++++++++++++++++---- 1 file changed, 184 insertions(+), 18 deletions(-) diff --git a/fundingmanager_test.go b/fundingmanager_test.go index 38b15dbd..d78c851e 100644 --- a/fundingmanager_test.go +++ b/fundingmanager_test.go @@ -10,6 +10,7 @@ import ( "net" "os" "path/filepath" + "runtime" "testing" "time" @@ -447,11 +448,11 @@ func recreateAliceFundingManager(t *testing.T, alice *testNode) { } } -func setupFundingManagers(t *testing.T) (*testNode, *testNode) { +func setupFundingManagers(t *testing.T, maxPendingChannels int) (*testNode, *testNode) { // We need to set the global config, as fundingManager uses // MaxPendingChannels, and it is usually set in lndMain(). cfg = &config{ - MaxPendingChannels: defaultMaxPendingChannels, + MaxPendingChannels: maxPendingChannels, } aliceTestDir, err := ioutil.TempDir("", "alicelnwallet") @@ -673,6 +674,8 @@ func assertFundingMsgSent(t *testing.T, msgChan chan lnwire.Message, sentMsg, ok = msg.(*lnwire.FundingSigned) case "FundingLocked": sentMsg, ok = msg.(*lnwire.FundingLocked) + case "Error": + sentMsg, ok = msg.(*lnwire.Error) default: t.Fatalf("unknown message type: %s", msgType) } @@ -683,8 +686,10 @@ func assertFundingMsgSent(t *testing.T, msgChan chan lnwire.Message, t.Fatalf("expected %s to be sent, instead got error: %v", msgType, lnwire.ErrorCode(errorMsg.Data[0])) } - t.Fatalf("expected %s to be sent, instead got %T", - msgType, msg) + + _, _, line, _ := runtime.Caller(1) + t.Fatalf("expected %s to be sent, instead got %T at %v", + msgType, msg, line) } return sentMsg @@ -950,7 +955,7 @@ func assertHandleFundingLocked(t *testing.T, alice, bob *testNode) { } func TestFundingManagerNormalWorkflow(t *testing.T) { - alice, bob := setupFundingManagers(t) + alice, bob := setupFundingManagers(t, defaultMaxPendingChannels) defer tearDownFundingManagers(t, alice, bob) // We will consume the channel updates as we go, so no buffering is needed. @@ -1020,7 +1025,7 @@ func TestFundingManagerNormalWorkflow(t *testing.T) { } func TestFundingManagerRestartBehavior(t *testing.T) { - alice, bob := setupFundingManagers(t) + alice, bob := setupFundingManagers(t, defaultMaxPendingChannels) defer tearDownFundingManagers(t, alice, bob) // Run through the process of opening the channel, up until the funding @@ -1151,7 +1156,7 @@ func TestFundingManagerRestartBehavior(t *testing.T) { // server to notify when the peer comes online, in case sending the // fundingLocked message fails the first time. func TestFundingManagerOfflinePeer(t *testing.T) { - alice, bob := setupFundingManagers(t) + alice, bob := setupFundingManagers(t, defaultMaxPendingChannels) defer tearDownFundingManagers(t, alice, bob) // Run through the process of opening the channel, up until the funding @@ -1282,7 +1287,7 @@ func TestFundingManagerOfflinePeer(t *testing.T) { // will properly clean up a zombie reservation that times out after the // initFundingMsg has been handled. func TestFundingManagerPeerTimeoutAfterInitFunding(t *testing.T) { - alice, bob := setupFundingManagers(t) + alice, bob := setupFundingManagers(t, defaultMaxPendingChannels) defer tearDownFundingManagers(t, alice, bob) // We will consume the channel updates as we go, so no buffering is needed. @@ -1342,7 +1347,7 @@ func TestFundingManagerPeerTimeoutAfterInitFunding(t *testing.T) { // will properly clean up a zombie reservation that times out after the // fundingOpenMsg has been handled. func TestFundingManagerPeerTimeoutAfterFundingOpen(t *testing.T) { - alice, bob := setupFundingManagers(t) + alice, bob := setupFundingManagers(t, defaultMaxPendingChannels) defer tearDownFundingManagers(t, alice, bob) // We will consume the channel updates as we go, so no buffering is needed. @@ -1411,7 +1416,7 @@ func TestFundingManagerPeerTimeoutAfterFundingOpen(t *testing.T) { // will properly clean up a zombie reservation that times out after the // fundingAcceptMsg has been handled. func TestFundingManagerPeerTimeoutAfterFundingAccept(t *testing.T) { - alice, bob := setupFundingManagers(t) + alice, bob := setupFundingManagers(t, defaultMaxPendingChannels) defer tearDownFundingManagers(t, alice, bob) // We will consume the channel updates as we go, so no buffering is needed. @@ -1485,7 +1490,7 @@ func TestFundingManagerPeerTimeoutAfterFundingAccept(t *testing.T) { } func TestFundingManagerFundingTimeout(t *testing.T) { - alice, bob := setupFundingManagers(t) + alice, bob := setupFundingManagers(t, defaultMaxPendingChannels) defer tearDownFundingManagers(t, alice, bob) // We will consume the channel updates as we go, so no buffering is needed. @@ -1530,7 +1535,7 @@ func TestFundingManagerFundingTimeout(t *testing.T) { // the channel initiator, that it does not timeout when the lnd restarts. func TestFundingManagerFundingNotTimeoutInitiator(t *testing.T) { - alice, bob := setupFundingManagers(t) + alice, bob := setupFundingManagers(t, defaultMaxPendingChannels) defer tearDownFundingManagers(t, alice, bob) // We will consume the channel updates as we go, so no buffering is needed. @@ -1597,7 +1602,7 @@ func TestFundingManagerFundingNotTimeoutInitiator(t *testing.T) { // continues to operate as expected in case we receive a duplicate fundingLocked // message. func TestFundingManagerReceiveFundingLockedTwice(t *testing.T) { - alice, bob := setupFundingManagers(t) + alice, bob := setupFundingManagers(t, defaultMaxPendingChannels) defer tearDownFundingManagers(t, alice, bob) // We will consume the channel updates as we go, so no buffering is needed. @@ -1686,7 +1691,7 @@ func TestFundingManagerReceiveFundingLockedTwice(t *testing.T) { // handles receiving a fundingLocked after the its own fundingLocked and channel // announcement is sent and gets restarted. func TestFundingManagerRestartAfterChanAnn(t *testing.T) { - alice, bob := setupFundingManagers(t) + alice, bob := setupFundingManagers(t, defaultMaxPendingChannels) defer tearDownFundingManagers(t, alice, bob) // We will consume the channel updates as we go, so no buffering is needed. @@ -1760,7 +1765,7 @@ func TestFundingManagerRestartAfterChanAnn(t *testing.T) { // fundingManager continues to operate as expected after it has received // fundingLocked and then gets restarted. func TestFundingManagerRestartAfterReceivingFundingLocked(t *testing.T) { - alice, bob := setupFundingManagers(t) + alice, bob := setupFundingManagers(t, defaultMaxPendingChannels) defer tearDownFundingManagers(t, alice, bob) // We will consume the channel updates as we go, so no buffering is needed. @@ -1830,7 +1835,7 @@ func TestFundingManagerRestartAfterReceivingFundingLocked(t *testing.T) { // (a channel not supposed to be announced to the rest of the network), // the announcementSignatures nor the nodeAnnouncement messages are sent. func TestFundingManagerPrivateChannel(t *testing.T) { - alice, bob := setupFundingManagers(t) + alice, bob := setupFundingManagers(t, defaultMaxPendingChannels) defer tearDownFundingManagers(t, alice, bob) // We will consume the channel updates as we go, so no buffering is needed. @@ -1910,7 +1915,7 @@ func TestFundingManagerPrivateChannel(t *testing.T) { // announcement signatures nor the node announcement messages are sent upon // restart. func TestFundingManagerPrivateRestart(t *testing.T) { - alice, bob := setupFundingManagers(t) + alice, bob := setupFundingManagers(t, defaultMaxPendingChannels) defer tearDownFundingManagers(t, alice, bob) // We will consume the channel updates as we go, so no buffering is needed. @@ -2000,7 +2005,7 @@ func TestFundingManagerPrivateRestart(t *testing.T) { // TestFundingManagerCustomChannelParameters checks that custom requirements we // specify during the channel funding flow is preserved correcly on both sides. func TestFundingManagerCustomChannelParameters(t *testing.T) { - alice, bob := setupFundingManagers(t) + alice, bob := setupFundingManagers(t, defaultMaxPendingChannels) defer tearDownFundingManagers(t, alice, bob) // This is the custom parameters we'll use. @@ -2195,3 +2200,164 @@ func TestFundingManagerCustomChannelParameters(t *testing.T) { t.Fatalf("alice did not publish funding tx") } } + +// TestFundingManagerMaxPendingChannels checks that trying to open another +// channel with the same peer when MaxPending channels are pending fails. +func TestFundingManagerMaxPendingChannels(t *testing.T) { + const maxPending = 4 + + alice, bob := setupFundingManagers(t, maxPending) + defer tearDownFundingManagers(t, alice, bob) + + // Create openChanReqs for maxPending+1 channels. + var initReqs []*openChanReq + for i := 0; i < maxPending+1; i++ { + updateChan := make(chan *lnrpc.OpenStatusUpdate) + errChan := make(chan error, 1) + initReq := &openChanReq{ + targetPubkey: bob.privKey.PubKey(), + chainHash: *activeNetParams.GenesisHash, + localFundingAmt: 5000000, + pushAmt: lnwire.NewMSatFromSatoshis(0), + private: false, + updates: updateChan, + err: errChan, + } + initReqs = append(initReqs, initReq) + } + + // Kick of maxPending+1 funding workflows. + var accepts []*lnwire.AcceptChannel + var lastOpen *lnwire.OpenChannel + for i, initReq := range initReqs { + alice.fundingMgr.initFundingWorkflow(bob, initReq) + + // Alice should have sent the OpenChannel message to Bob. + var aliceMsg lnwire.Message + select { + case aliceMsg = <-alice.msgChan: + case err := <-initReq.err: + t.Fatalf("error init funding workflow: %v", err) + case <-time.After(time.Second * 5): + t.Fatalf("alice did not send OpenChannel message") + } + + openChannelReq, ok := aliceMsg.(*lnwire.OpenChannel) + if !ok { + errorMsg, gotError := aliceMsg.(*lnwire.Error) + if gotError { + t.Fatalf("expected OpenChannel to be sent "+ + "from bob, instead got error: %v", + lnwire.ErrorCode(errorMsg.Data[0])) + } + t.Fatalf("expected OpenChannel to be sent from "+ + "alice, instead got %T", aliceMsg) + } + + // Let Bob handle the init message. + bob.fundingMgr.processFundingOpen(openChannelReq, alice) + + // Bob should answer with an AcceptChannel message for the + // first maxPending channels. + if i < maxPending { + acceptChannelResponse := assertFundingMsgSent( + t, bob.msgChan, "AcceptChannel", + ).(*lnwire.AcceptChannel) + accepts = append(accepts, acceptChannelResponse) + continue + } + + // For the last channel, Bob should answer with an error. + lastOpen = openChannelReq + _ = assertFundingMsgSent( + t, bob.msgChan, "Error", + ).(*lnwire.Error) + + } + + // Forward the responses to Alice. + var signs []*lnwire.FundingSigned + for _, accept := range accepts { + alice.fundingMgr.processFundingAccept(accept, bob) + + // Alice responds with a FundingCreated message. + fundingCreated := assertFundingMsgSent( + t, alice.msgChan, "FundingCreated", + ).(*lnwire.FundingCreated) + + // Give the message to Bob. + bob.fundingMgr.processFundingCreated(fundingCreated, alice) + + // Finally, Bob should send the FundingSigned message. + fundingSigned := assertFundingMsgSent( + t, bob.msgChan, "FundingSigned", + ).(*lnwire.FundingSigned) + + signs = append(signs, fundingSigned) + } + + // Sending another init request from Alice should still make Bob + // respond with an error. + bob.fundingMgr.processFundingOpen(lastOpen, alice) + _ = assertFundingMsgSent( + t, bob.msgChan, "Error", + ).(*lnwire.Error) + + // Give the FundingSigned messages to Alice. + for i, sign := range signs { + alice.fundingMgr.processFundingSigned(sign, bob) + + // Alice should send a status update for each channel, and + // publish a funding tx to the network. + var pendingUpdate *lnrpc.OpenStatusUpdate + select { + case pendingUpdate = <-initReqs[i].updates: + case <-time.After(time.Second * 5): + t.Fatalf("alice did not send OpenStatusUpdate_ChanPending") + } + + _, ok := pendingUpdate.Update.(*lnrpc.OpenStatusUpdate_ChanPending) + if !ok { + t.Fatal("OpenStatusUpdate was not OpenStatusUpdate_ChanPending") + } + + select { + case <-alice.publTxChan: + case <-time.After(time.Second * 5): + t.Fatalf("alice did not publish funding tx") + } + + } + + // Sending another init request from Alice should still make Bob + // respond with an error, since the funding transactions are not + // confirmed yet, + bob.fundingMgr.processFundingOpen(lastOpen, alice) + _ = assertFundingMsgSent( + t, bob.msgChan, "Error", + ).(*lnwire.Error) + + // Notify that the transactions were mined. + for i := 0; i < maxPending; i++ { + alice.mockNotifier.oneConfChannel <- &chainntnfs.TxConfirmation{} + bob.mockNotifier.oneConfChannel <- &chainntnfs.TxConfirmation{} + + // Expect both to be sending FundingLocked. + _ = assertFundingMsgSent( + t, alice.msgChan, "FundingLocked", + ).(*lnwire.FundingLocked) + + _ = assertFundingMsgSent( + t, bob.msgChan, "FundingLocked", + ).(*lnwire.FundingLocked) + + } + + // Now opening another channel should work. + bob.fundingMgr.processFundingOpen(lastOpen, alice) + + // Bob should answer with an AcceptChannel message. + _ = assertFundingMsgSent( + t, bob.msgChan, "AcceptChannel", + ).(*lnwire.AcceptChannel) +}