diff --git a/fundingmanager.go b/fundingmanager.go index 5c5f9687..a5914f29 100644 --- a/fundingmanager.go +++ b/fundingmanager.go @@ -1230,6 +1230,18 @@ func (f *fundingManager) handleFundingAccept(fmsg *fundingAcceptMsg) { fndgLog.Infof("Recv'd fundingResponse for pendingID(%x)", pendingChanID[:]) + // The required number of confirmations should not be greater than the + // maximum number of confirmations required by the ChainNotifier to + // properly dispatch confirmations. + if msg.MinAcceptDepth > chainntnfs.MaxNumConfs { + err := lnwallet.ErrNumConfsTooLarge( + msg.MinAcceptDepth, chainntnfs.MaxNumConfs, + ) + fndgLog.Warnf("Unacceptable channel constraints: %v", err) + f.failFundingFlow(fmsg.peer, fmsg.msg.PendingChannelID, err) + return + } + // We'll also specify the responder's preference for the number of // required confirmations, and also the set of channel constraints // they've specified for commitment states we can create. diff --git a/fundingmanager_test.go b/fundingmanager_test.go index 84886ea9..21a522e1 100644 --- a/fundingmanager_test.go +++ b/fundingmanager_test.go @@ -11,6 +11,7 @@ import ( "os" "path/filepath" "runtime" + "strings" "testing" "time" @@ -2539,3 +2540,72 @@ func TestFundingManagerRejectPush(t *testing.T) { string(err.Data)) } } + +// TestFundingManagerMaxConfs ensures that we don't accept a funding proposal +// that proposes a MinAcceptDepth greater than the maximum number of +// confirmations we're willing to accept. +func TestFundingManagerMaxConfs(t *testing.T) { + t.Parallel() + + alice, bob := setupFundingManagers(t, defaultMaxPendingChannels) + defer tearDownFundingManagers(t, alice, bob) + + // Create a funding request and start the workflow. + updateChan := make(chan *lnrpc.OpenStatusUpdate) + errChan := make(chan error, 1) + initReq := &openChanReq{ + targetPubkey: bob.privKey.PubKey(), + chainHash: *activeNetParams.GenesisHash, + localFundingAmt: 500000, + pushAmt: lnwire.NewMSatFromSatoshis(10), + private: false, + updates: updateChan, + err: errChan, + } + + 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. + acceptChannelResponse := assertFundingMsgSent( + t, bob.msgChan, "AcceptChannel", + ).(*lnwire.AcceptChannel) + + // Modify the AcceptChannel message Bob is proposing to including a + // MinAcceptDepth Alice won't be willing to accept. + acceptChannelResponse.MinAcceptDepth = chainntnfs.MaxNumConfs + 1 + + alice.fundingMgr.processFundingAccept(acceptChannelResponse, bob) + + // Alice should respond back with an error indicating MinAcceptDepth is + // too large. + err := assertFundingMsgSent(t, alice.msgChan, "Error").(*lnwire.Error) + if !strings.Contains(string(err.Data), "minimum depth") { + t.Fatalf("expected ErrNumConfsTooLarge, got \"%v\"", + string(err.Data)) + } +} diff --git a/lnwallet/errors.go b/lnwallet/errors.go index 0d208623..79ab10f7 100644 --- a/lnwallet/errors.go +++ b/lnwallet/errors.go @@ -123,6 +123,15 @@ func ErrMaxValueInFlightTooSmall(maxValInFlight, } } +// ErrNumConfsTooLarge returns an error indicating that the number of +// confirmations required for a channel is too large. +func ErrNumConfsTooLarge(numConfs, maxNumConfs uint32) error { + return ReservationError{ + fmt.Errorf("minimum depth of %d is too large, max is %d", + numConfs, maxNumConfs), + } +} + // ErrChanTooSmall returns an error indicating that an incoming channel request // was too small. We'll reject any incoming channels if they're below our // configured value for the min channel size we'll accept.