@@ -255,7 +255,7 @@ type ChanCloser struct {
255255// calcCoopCloseFee computes an "ideal" absolute co-op close fee given the
256256// delivery scripts of both parties and our ideal fee rate.
257257func calcCoopCloseFee (chanType channeldb.ChannelType ,
258- localOutput , remoteOutput * wire.TxOut ,
258+ localOutput , remoteOutput * wire.TxOut , extraOutputs [] * wire. TxOut ,
259259 idealFeeRate chainfee.SatPerKWeight ) btcutil.Amount {
260260
261261 var weightEstimator input.TxWeightEstimator
@@ -276,6 +276,9 @@ func calcCoopCloseFee(chanType channeldb.ChannelType,
276276 if remoteOutput != nil {
277277 weightEstimator .AddTxOutput (remoteOutput )
278278 }
279+ for _ , extraOutput := range extraOutputs {
280+ weightEstimator .AddTxOutput (extraOutput )
281+ }
279282
280283 totalWeight := weightEstimator .Weight ()
281284
@@ -295,7 +298,65 @@ func (d *SimpleCoopFeeEstimator) EstimateFee(chanType channeldb.ChannelType,
295298 localTxOut , remoteTxOut * wire.TxOut ,
296299 idealFeeRate chainfee.SatPerKWeight ) btcutil.Amount {
297300
298- return calcCoopCloseFee (chanType , localTxOut , remoteTxOut , idealFeeRate )
301+ return calcCoopCloseFee (
302+ chanType , localTxOut , remoteTxOut , nil , idealFeeRate ,
303+ )
304+ }
305+
306+ // estimateCloseFee computes a close fee for the given fee rate while taking
307+ // into account any optional auxiliary close outputs.
308+ func (c * ChanCloser ) estimateCloseFee (localTxOut , remoteTxOut * wire.TxOut ,
309+ feeRate chainfee.SatPerKWeight ) (btcutil.Amount , error ) {
310+
311+ // Historical behavior uses the default channel type here and doesn't
312+ // differentiate between channel feature variants.
313+ var defaultChanType channeldb.ChannelType
314+ fee := c .cfg .FeeEstimator .EstimateFee (
315+ defaultChanType , localTxOut , remoteTxOut , feeRate ,
316+ )
317+
318+ if c .cfg .AuxCloser .IsNone () {
319+ return fee , nil
320+ }
321+
322+ // Aux output selection can depend on CloseFee. After we bump the fee,
323+ // the aux closer can return a different
324+ // output set (for example around dust thresholds). Run a second pass so
325+ // the fee and output set are self-consistent, while keeping this
326+ // bounded.
327+ for range 2 {
328+ auxOutputs , err := c .auxCloseOutputs (fee )
329+ if err != nil {
330+ return 0 , err
331+ }
332+
333+ var extraOutputs []* wire.TxOut
334+ auxOutputs .WhenSome (func (outs AuxCloseOutputs ) {
335+ extraOutputs = make (
336+ []* wire.TxOut , 0 , len (outs .ExtraCloseOutputs ),
337+ )
338+ for _ , closeOutput := range outs .ExtraCloseOutputs {
339+ txOut := closeOutput .TxOut
340+ extraOutputs = append (extraOutputs , & txOut )
341+ }
342+ })
343+
344+ if len (extraOutputs ) == 0 {
345+ return fee , nil
346+ }
347+
348+ feeWithAuxOutputs := calcCoopCloseFee (
349+ defaultChanType , localTxOut , remoteTxOut ,
350+ extraOutputs , feeRate ,
351+ )
352+ if feeWithAuxOutputs <= fee {
353+ return fee , nil
354+ }
355+
356+ fee = feeWithAuxOutputs
357+ }
358+
359+ return fee , nil
299360}
300361
301362// NewChanCloser creates a new instance of the channel closure given the passed
@@ -327,7 +388,7 @@ func NewChanCloser(cfg ChanCloseCfg, deliveryScript DeliveryAddrWithKey,
327388
328389// initFeeBaseline computes our ideal fee rate, and also the largest fee we'll
329390// accept given information about the delivery script of the remote party.
330- func (c * ChanCloser ) initFeeBaseline () {
391+ func (c * ChanCloser ) initFeeBaseline () error {
331392 // Depending on if a balance ends up being dust or not, we'll pass a
332393 // nil TxOut into the EstimateFee call which can handle it.
333394 var localTxOut , remoteTxOut * wire.TxOut
@@ -346,18 +407,25 @@ func (c *ChanCloser) initFeeBaseline() {
346407
347408 // Given the target fee-per-kw, we'll compute what our ideal _total_
348409 // fee will be starting at for this fee negotiation.
349- c .idealFeeSat = c .cfg .FeeEstimator .EstimateFee (
350- 0 , localTxOut , remoteTxOut , c .idealFeeRate ,
410+ var err error
411+ c .idealFeeSat , err = c .estimateCloseFee (
412+ localTxOut , remoteTxOut , c .idealFeeRate ,
351413 )
414+ if err != nil {
415+ return err
416+ }
352417
353418 // When we're the initiator, we'll want to also factor in the highest
354419 // fee we want to pay. This'll either be 3x the ideal fee, or the
355420 // specified explicit max fee.
356421 c .maxFee = c .idealFeeSat * defaultMaxFeeMultiplier
357422 if c .cfg .MaxFee > 0 {
358- c .maxFee = c .cfg . FeeEstimator . EstimateFee (
359- 0 , localTxOut , remoteTxOut , c .cfg .MaxFee ,
423+ c .maxFee , err = c .estimateCloseFee (
424+ localTxOut , remoteTxOut , c .cfg .MaxFee ,
360425 )
426+ if err != nil {
427+ return err
428+ }
361429 }
362430
363431 // TODO(ziggie): Make sure the ideal fee is not higher than the max fee.
@@ -366,6 +434,8 @@ func (c *ChanCloser) initFeeBaseline() {
366434 chancloserLog .Infof ("Ideal fee for closure of ChannelPoint(%v) " +
367435 "is: %v sat (max_fee=%v sat)" , c .cfg .Channel .ChannelPoint (),
368436 int64 (c .idealFeeSat ), int64 (c .maxFee ))
437+
438+ return nil
369439}
370440
371441// initChanShutdown begins the shutdown process by un-registering the channel,
@@ -740,14 +810,17 @@ func (c *ChanCloser) BeginNegotiation() (fn.Option[lnwire.ClosingSigned],
740810 case closeAwaitingFlush :
741811 // Now that we know their desired delivery script, we can
742812 // compute what our max/ideal fee will be.
743- c .initFeeBaseline ()
813+ err := c .initFeeBaseline ()
814+ if err != nil {
815+ return noClosingSigned , err
816+ }
744817
745818 // Before continuing, mark the channel as cooperatively closed
746819 // with a nil txn. Even though we haven't negotiated the final
747820 // txn, this guarantees that our listchannels rpc will be
748821 // externally consistent, and reflect that the channel is being
749822 // shutdown by the time the closing request returns.
750- err : = c .cfg .Channel .MarkCoopBroadcasted (
823+ err = c .cfg .Channel .MarkCoopBroadcasted (
751824 nil , c .closer ,
752825 )
753826 if err != nil {
0 commit comments