@@ -26,10 +26,11 @@ import (
2626)
2727
2828var (
29- // ErrWithdrawingInactiveDeposits is returned when the user tries to
30- // withdraw inactive deposits.
31- ErrWithdrawingInactiveDeposits = errors .New ("deposits to be " +
32- "withdrawn are unknown or inactive" )
29+ // ErrWithdrawingMixedDeposits is returned when a withdrawal is
30+ // requested for deposits in different states.
31+ ErrWithdrawingMixedDeposits = errors .New ("need to withdraw deposits " +
32+ "having the same state, either all deposited or all " +
33+ "withdrawing" )
3334
3435 // MinConfs is the minimum number of confirmations we require for a
3536 // deposit to be considered withdrawn.
@@ -237,7 +238,7 @@ func (m *Manager) recoverWithdrawals(ctx context.Context) error {
237238 return err
238239 }
239240
240- err = m .publishFinalizedWithdrawalTx (ctx , tx )
241+ _ , err : = m .publishFinalizedWithdrawalTx (ctx , tx )
241242 if err != nil {
242243 return err
243244 }
@@ -276,12 +277,37 @@ func (m *Manager) WithdrawDeposits(ctx context.Context,
276277
277278 // Ensure that the deposits are in a state in which they can be
278279 // withdrawn.
279- deposits , allActive := m .cfg .DepositManager .AllOutpointsActiveDeposits (
280+ deposits , allDeposited := m .cfg .DepositManager .AllOutpointsActiveDeposits (
280281 outpoints , deposit .Deposited ,
281282 )
282283
283- if ! allActive {
284- return "" , "" , ErrWithdrawingInactiveDeposits
284+ // If not all passed outpoints are in state Deposited, we'll check if
285+ // they are all in state Withdrawing. If they are, then the user is
286+ // requesting a fee bump, if not we'll return an error as we only allow
287+ // fee bumping deposits in state Withdrawing.
288+ if ! allDeposited {
289+ deposits , allWithdrawing := m .cfg .DepositManager .AllOutpointsActiveDeposits (
290+ outpoints , deposit .Withdrawing ,
291+ )
292+
293+ if ! allWithdrawing {
294+ return "" , "" , ErrWithdrawingMixedDeposits
295+ }
296+
297+ // If a republishing of an existing withdrawal is requested we
298+ // ensure that all deposits remain clustered in the context of
299+ // the same withdrawal by checking if they have the same
300+ // previous withdrawal tx hash.
301+ // This ensures that the shape of the transaction stays the
302+ // same.
303+ hash := deposits [0 ].FinalizedWithdrawalTx .TxHash ()
304+ for i := 1 ; i < len (deposits ); i ++ {
305+ if deposits [i ].FinalizedWithdrawalTx .TxHash () != hash {
306+ return "" , "" , fmt .Errorf ("can't bump fee " +
307+ "for deposits with different " +
308+ "previous withdrawal tx hash" )
309+ }
310+ }
285311 }
286312
287313 var (
@@ -315,6 +341,38 @@ func (m *Manager) WithdrawDeposits(ctx context.Context,
315341 return "" , "" , err
316342 }
317343
344+ published , err := m .publishFinalizedWithdrawalTx (ctx , finalizedTx )
345+ if err != nil {
346+ return "" , "" , err
347+ }
348+
349+ if ! published {
350+ return "" , "" , nil
351+ }
352+
353+ withdrawalPkScript , err := txscript .PayToAddrScript (withdrawalAddress )
354+ if err != nil {
355+ return "" , "" , err
356+ }
357+
358+ err = m .handleWithdrawal (
359+ ctx , deposits , finalizedTx .TxHash (), withdrawalPkScript ,
360+ )
361+ if err != nil {
362+ return "" , "" , err
363+ }
364+
365+ // If a previous withdrawal existed across the selected deposits, and
366+ // it isn't the same as the new withdrawal, we'll stop monitoring the
367+ // previous withdrawal and remove it from the finalized withdrawals.
368+ deposits [0 ].Lock ()
369+ prevTx := deposits [0 ].FinalizedWithdrawalTx
370+ deposits [0 ].Unlock ()
371+
372+ if prevTx != nil && prevTx .TxHash () != finalizedTx .TxHash () {
373+ delete (m .finalizedWithdrawalTxns , prevTx .TxHash ())
374+ }
375+
318376 // Attach the finalized withdrawal tx to the deposits. After a client
319377 // restart we can use this address as an indicator to republish the
320378 // withdrawal tx and continue the withdrawal.
@@ -325,6 +383,8 @@ func (m *Manager) WithdrawDeposits(ctx context.Context,
325383 d .Unlock ()
326384 }
327385
386+ m .finalizedWithdrawalTxns [finalizedTx .TxHash ()] = finalizedTx
387+
328388 // Transition the deposits to the withdrawing state. This updates each
329389 // deposits withdrawal address. If a transition fails, we'll return an
330390 // error and abort the withdrawal. An error in transition is likely due
@@ -337,25 +397,14 @@ func (m *Manager) WithdrawDeposits(ctx context.Context,
337397 return "" , "" , err
338398 }
339399
340- err = m .publishFinalizedWithdrawalTx (ctx , finalizedTx )
341- if err != nil {
342- return "" , "" , err
343- }
344-
345- withdrawalPkScript , err := txscript .PayToAddrScript (withdrawalAddress )
346- if err != nil {
347- return "" , "" , err
348- }
349-
350- err = m .handleWithdrawal (
351- ctx , deposits , finalizedTx .TxHash (), withdrawalPkScript ,
352- )
353- if err != nil {
354- return "" , "" , err
400+ // Update the deposits in the database.
401+ for _ , d := range deposits {
402+ err = m .cfg .DepositManager .UpdateDeposit (ctx , d )
403+ if err != nil {
404+ return "" , "" , err
405+ }
355406 }
356407
357- m .finalizedWithdrawalTxns [finalizedTx .TxHash ()] = finalizedTx
358-
359408 return finalizedTx .TxID (), withdrawalAddress .String (), nil
360409}
361410
@@ -452,27 +501,31 @@ func (m *Manager) createFinalizedWithdrawalTx(ctx context.Context,
452501}
453502
454503func (m * Manager ) publishFinalizedWithdrawalTx (ctx context.Context ,
455- tx * wire.MsgTx ) error {
504+ tx * wire.MsgTx ) ( bool , error ) {
456505
457506 if tx == nil {
458- return errors .New ("can't publish, finalized withdrawal tx is " +
459- "nil" )
507+ return false , errors .New ("can't publish, finalized " +
508+ "withdrawal tx is nil" )
460509 }
461510
462511 txLabel := fmt .Sprintf ("deposit-withdrawal-%v" , tx .TxHash ())
463512
464513 // Publish the withdrawal sweep transaction.
465514 err := m .cfg .WalletKit .PublishTransaction (ctx , tx , txLabel )
466-
467515 if err != nil {
468- if ! strings .Contains (err .Error (), "output already spent" ) {
469- log .Errorf ("%v: %v" , txLabel , err )
516+ if ! strings .Contains (err .Error (), "output already spent" ) &&
517+ ! strings .Contains (err .Error (), "insufficient fee" ) {
518+
519+ return false , err
520+ } else {
521+ return false , nil
470522 }
523+ } else {
524+ log .Debugf ("published deposit withdrawal with txid: %v" ,
525+ tx .TxHash ())
471526 }
472527
473- log .Debugf ("published deposit withdrawal with txid: %v" , tx .TxHash ())
474-
475- return nil
528+ return true , nil
476529}
477530
478531func (m * Manager ) handleWithdrawal (ctx context.Context ,
@@ -916,7 +969,7 @@ func (m *Manager) republishWithdrawals(ctx context.Context) error {
916969 continue
917970 }
918971
919- err := m .publishFinalizedWithdrawalTx (ctx , finalizedTx )
972+ _ , err := m .publishFinalizedWithdrawalTx (ctx , finalizedTx )
920973 if err != nil {
921974 log .Errorf ("Error republishing withdrawal: %v" , err )
922975
0 commit comments