diff --git a/fundingmanager.go b/fundingmanager.go index e3cd3d94..29f12c00 100644 --- a/fundingmanager.go +++ b/fundingmanager.go @@ -994,10 +994,10 @@ func (f *fundingManager) handleFundingOpen(fmsg *fundingOpenMsg) { // party is attempting to dictate for our commitment transaction. err = reservation.CommitConstraints( msg.CsvDelay, msg.MaxAcceptedHTLCs, msg.MaxValueInFlight, - msg.HtlcMinimum, msg.ChannelReserve, + msg.HtlcMinimum, msg.ChannelReserve, msg.DustLimit, ) if err != nil { - fndgLog.Errorf("Unaccaptable channel constraints: %v", err) + fndgLog.Errorf("Unacceptable channel constraints: %v", err) f.failFundingFlow(fmsg.peerAddress.IdentityKey, fmsg.msg.PendingChannelID, err, ) @@ -1146,7 +1146,7 @@ func (f *fundingManager) handleFundingAccept(fmsg *fundingAcceptMsg) { resCtx.reservation.SetNumConfsRequired(uint16(msg.MinAcceptDepth)) err = resCtx.reservation.CommitConstraints( msg.CsvDelay, msg.MaxAcceptedHTLCs, msg.MaxValueInFlight, - msg.HtlcMinimum, msg.ChannelReserve, + msg.HtlcMinimum, msg.ChannelReserve, msg.DustLimit, ) if err != nil { fndgLog.Warnf("Unacceptable channel constraints: %v", err) diff --git a/lnwallet/errors.go b/lnwallet/errors.go index 1e113473..403876c3 100644 --- a/lnwallet/errors.go +++ b/lnwallet/errors.go @@ -59,6 +59,15 @@ func ErrCsvDelayTooLarge(remoteDelay, maxDelay uint16) ReservationError { } } +// ErrChanReserveTooSmall returns an error indicating that the channel reserve +// the remote is requiring is too small to be accepted. +func ErrChanReserveTooSmall(reserve, dustLimit btcutil.Amount) ReservationError { + return ReservationError{ + fmt.Errorf("channel reserve of %v sat is too small, min is %v "+ + "sat", int64(reserve), int64(dustLimit)), + } +} + // ErrChanReserveTooLarge returns an error indicating that the chan reserve the // remote is requiring, is too large to be accepted. func ErrChanReserveTooLarge(reserve, diff --git a/lnwallet/interface_test.go b/lnwallet/interface_test.go index 3bd5f8e3..a549fe86 100644 --- a/lnwallet/interface_test.go +++ b/lnwallet/interface_test.go @@ -305,8 +305,14 @@ func testDualFundingReservationWorkflow(miner *rpctest.Harness, t.Fatalf("unable to initialize funding reservation: %v", err) } aliceChanReservation.SetNumConfsRequired(numReqConfs) - aliceChanReservation.CommitConstraints(csvDelay, lnwallet.MaxHTLCNumber/2, - lnwire.NewMSatFromSatoshis(fundingAmount), 1, 10) + err = aliceChanReservation.CommitConstraints( + csvDelay, lnwallet.MaxHTLCNumber/2, + lnwire.NewMSatFromSatoshis(fundingAmount), 1, fundingAmount/100, + lnwallet.DefaultDustLimit(), + ) + if err != nil { + t.Fatalf("unable to verify constraints: %v", err) + } // The channel reservation should now be populated with a multi-sig key // from our HD chain, a change output with 3 BTC, and 2 outputs @@ -328,8 +334,14 @@ func testDualFundingReservationWorkflow(miner *rpctest.Harness, if err != nil { t.Fatalf("bob unable to init channel reservation: %v", err) } - bobChanReservation.CommitConstraints(csvDelay, lnwallet.MaxHTLCNumber/2, - lnwire.NewMSatFromSatoshis(fundingAmount), 1, 10) + err = bobChanReservation.CommitConstraints( + csvDelay, lnwallet.MaxHTLCNumber/2, + lnwire.NewMSatFromSatoshis(fundingAmount), 1, fundingAmount/100, + lnwallet.DefaultDustLimit(), + ) + if err != nil { + t.Fatalf("unable to verify constraints: %v", err) + } bobChanReservation.SetNumConfsRequired(numReqConfs) assertContributionInitPopulated(t, bobChanReservation.OurContribution()) @@ -675,8 +687,14 @@ func testSingleFunderReservationWorkflow(miner *rpctest.Harness, t.Fatalf("unable to init channel reservation: %v", err) } aliceChanReservation.SetNumConfsRequired(numReqConfs) - aliceChanReservation.CommitConstraints(csvDelay, lnwallet.MaxHTLCNumber/2, - lnwire.NewMSatFromSatoshis(fundingAmt), 1, 10) + err = aliceChanReservation.CommitConstraints( + csvDelay, lnwallet.MaxHTLCNumber/2, + lnwire.NewMSatFromSatoshis(fundingAmt), 1, fundingAmt/100, + lnwallet.DefaultDustLimit(), + ) + if err != nil { + t.Fatalf("unable to verify constraints: %v", err) + } // Verify all contribution fields have been set properly. aliceContribution := aliceChanReservation.OurContribution() @@ -698,8 +716,14 @@ func testSingleFunderReservationWorkflow(miner *rpctest.Harness, if err != nil { t.Fatalf("unable to create bob reservation: %v", err) } - bobChanReservation.CommitConstraints(csvDelay, lnwallet.MaxHTLCNumber/2, - lnwire.NewMSatFromSatoshis(fundingAmt), 1, 10) + err = bobChanReservation.CommitConstraints( + csvDelay, lnwallet.MaxHTLCNumber/2, + lnwire.NewMSatFromSatoshis(fundingAmt), 1, fundingAmt/100, + lnwallet.DefaultDustLimit(), + ) + if err != nil { + t.Fatalf("unable to verify constraints: %v", err) + } bobChanReservation.SetNumConfsRequired(numReqConfs) // We'll ensure that Bob's contribution also gets generated properly. diff --git a/lnwallet/reservation.go b/lnwallet/reservation.go index 554b691a..e8a22f47 100644 --- a/lnwallet/reservation.go +++ b/lnwallet/reservation.go @@ -284,7 +284,7 @@ func (r *ChannelReservation) SetNumConfsRequired(numConfs uint16) { // if the parameters are seemed unsound. func (r *ChannelReservation) CommitConstraints(csvDelay, maxHtlcs uint16, maxValueInFlight, minHtlc lnwire.MilliSatoshi, - chanReserve btcutil.Amount) error { + chanReserve, dustLimit btcutil.Amount) error { r.Lock() defer r.Unlock() @@ -296,6 +296,12 @@ func (r *ChannelReservation) CommitConstraints(csvDelay, maxHtlcs uint16, return ErrCsvDelayTooLarge(csvDelay, maxDelay) } + // The dust limit should always be greater or equal to the channel + // reserve. The reservation request should be denied if otherwise. + if dustLimit > chanReserve { + return ErrChanReserveTooSmall(chanReserve, dustLimit) + } + // Fail if we consider the channel reserve to be too large. We // currently fail if it is greater than 20% of the channel capacity. maxChanReserve := r.partialState.Capacity / 5 @@ -331,6 +337,12 @@ func (r *ChannelReservation) CommitConstraints(csvDelay, maxHtlcs uint16, minNumHtlc*minHtlc) } + // Our dust limit should always be less than or equal our proposed + // channel reserve. + if r.ourContribution.DustLimit > chanReserve { + r.ourContribution.DustLimit = chanReserve + } + r.ourContribution.ChannelConfig.CsvDelay = csvDelay r.ourContribution.ChannelConfig.ChanReserve = chanReserve r.ourContribution.ChannelConfig.MaxAcceptedHtlcs = maxHtlcs