@@ -111,17 +111,25 @@ type Manager struct {
111111 // finalizedWithdrawalTx are the finalized withdrawal transactions that
112112 // are published to the network and re-published on block arrivals.
113113 finalizedWithdrawalTxns map [chainhash.Hash ]* wire.MsgTx
114+
115+ // withdrawalHandlerQuitChans is a map of quit channels for each
116+ // withdrawal transaction. The quit channels are used to stop the
117+ // withdrawal handler for a specific withdrawal transaction, e.g. if
118+ // a new rbf'd transaction has to be monitored for confirmation in
119+ // favor of the previous one.
120+ withdrawalHandlerQuitChans map [chainhash.Hash ]chan struct {}
114121}
115122
116123// NewManager creates a new deposit withdrawal manager.
117124func NewManager (cfg * ManagerConfig ) * Manager {
118125 return & Manager {
119- cfg : cfg ,
120- initChan : make (chan struct {}),
121- finalizedWithdrawalTxns : make (map [chainhash.Hash ]* wire.MsgTx ),
122- exitChan : make (chan struct {}),
123- newWithdrawalRequestChan : make (chan newWithdrawalRequest ),
124- errChan : make (chan error ),
126+ cfg : cfg ,
127+ initChan : make (chan struct {}),
128+ finalizedWithdrawalTxns : make (map [chainhash.Hash ]* wire.MsgTx ),
129+ exitChan : make (chan struct {}),
130+ newWithdrawalRequestChan : make (chan newWithdrawalRequest ),
131+ errChan : make (chan error ),
132+ withdrawalHandlerQuitChans : make (map [chainhash.Hash ]chan struct {}),
125133 }
126134}
127135
@@ -235,7 +243,7 @@ func (m *Manager) recoverWithdrawals(ctx context.Context) error {
235243 return err
236244 }
237245
238- err = m .publishFinalizedWithdrawalTx (ctx , tx )
246+ _ , err : = m .publishFinalizedWithdrawalTx (ctx , tx )
239247 if err != nil {
240248 return err
241249 }
@@ -278,8 +286,34 @@ func (m *Manager) WithdrawDeposits(ctx context.Context,
278286 outpoints , deposit .Deposited ,
279287 )
280288
289+ // If not all passed outpoints are in state Deposited, we'll check if
290+ // they are all in state Withdrawing. If they are, then the user is
291+ // requesting a fee bump, if not we'll return an error as we only allow
292+ // fee bumping deposits in state Withdrawing.
281293 if ! allActive {
282- return "" , "" , ErrWithdrawingInactiveDeposits
294+ deposits , allActive = m .cfg .DepositManager .AllOutpointsActiveDeposits (
295+ outpoints , deposit .Withdrawing ,
296+ )
297+
298+ if ! allActive {
299+ return "" , "" , ErrWithdrawingInactiveDeposits
300+ }
301+
302+ // If a republishing of an existing withdrawal is requested we
303+ // ensure that all deposits remain clustered in the context of
304+ // the same withdrawal by checking if they have the same
305+ // previous withdrawal tx hash.
306+ // This ensures that the shape of the transaction stays the
307+ // same.
308+ hash := deposits [0 ].FinalizedWithdrawalTx .TxHash ()
309+ for i := 1 ; i < len (deposits ); i ++ {
310+ if deposits [i ].FinalizedWithdrawalTx .TxHash () != hash {
311+ return "" , "" , fmt .Errorf ("can't bump fee " +
312+ "for deposits with different " +
313+ "previous withdrawal tx hash" )
314+ }
315+ }
316+
283317 }
284318
285319 var (
@@ -313,6 +347,41 @@ func (m *Manager) WithdrawDeposits(ctx context.Context,
313347 return "" , "" , err
314348 }
315349
350+ published , err := m .publishFinalizedWithdrawalTx (ctx , finalizedTx )
351+ if err != nil {
352+ return "" , "" , err
353+ }
354+
355+ if ! published {
356+ return "" , "" , nil
357+ }
358+
359+ withdrawalPkScript , err := txscript .PayToAddrScript (withdrawalAddress )
360+ if err != nil {
361+ return "" , "" , err
362+ }
363+
364+ err = m .handleWithdrawal (
365+ ctx , deposits , finalizedTx .TxHash (), withdrawalPkScript ,
366+ )
367+ if err != nil {
368+ return "" , "" , err
369+ }
370+
371+ // If a previous withdrawal existed across the selected deposits, and
372+ // it isn't the same as the new withdrawal, we'll stop monitoring the
373+ // previous withdrawal and remove it from the finalized withdrawals.
374+ deposits [0 ].Lock ()
375+ prevTx := deposits [0 ].FinalizedWithdrawalTx
376+ deposits [0 ].Unlock ()
377+
378+ if prevTx != nil && prevTx .TxHash () != finalizedTx .TxHash () {
379+ quitChan := m .withdrawalHandlerQuitChans [prevTx .TxHash ()]
380+ close (quitChan )
381+ delete (m .withdrawalHandlerQuitChans , prevTx .TxHash ())
382+ delete (m .finalizedWithdrawalTxns , prevTx .TxHash ())
383+ }
384+
316385 // Attach the finalized withdrawal tx to the deposits. After a client
317386 // restart we can use this address as an indicator to republish the
318387 // withdrawal tx and continue the withdrawal.
@@ -323,6 +392,8 @@ func (m *Manager) WithdrawDeposits(ctx context.Context,
323392 d .Unlock ()
324393 }
325394
395+ m .finalizedWithdrawalTxns [finalizedTx .TxHash ()] = finalizedTx
396+
326397 // Transition the deposits to the withdrawing state. This updates each
327398 // deposits withdrawal address. If a transition fails, we'll return an
328399 // error and abort the withdrawal. An error in transition is likely due
@@ -335,25 +406,14 @@ func (m *Manager) WithdrawDeposits(ctx context.Context,
335406 return "" , "" , err
336407 }
337408
338- err = m .publishFinalizedWithdrawalTx (ctx , finalizedTx )
339- if err != nil {
340- return "" , "" , err
341- }
342-
343- withdrawalPkScript , err := txscript .PayToAddrScript (withdrawalAddress )
344- if err != nil {
345- return "" , "" , err
346- }
347-
348- err = m .handleWithdrawal (
349- ctx , deposits , finalizedTx .TxHash (), withdrawalPkScript ,
350- )
351- if err != nil {
352- return "" , "" , err
409+ // Update the deposits in the database.
410+ for _ , d := range deposits {
411+ err = m .cfg .DepositManager .UpdateDeposit (ctx , d )
412+ if err != nil {
413+ return "" , "" , err
414+ }
353415 }
354416
355- m .finalizedWithdrawalTxns [finalizedTx .TxHash ()] = finalizedTx
356-
357417 return finalizedTx .TxID (), withdrawalAddress .String (), nil
358418}
359419
@@ -450,27 +510,31 @@ func (m *Manager) createFinalizedWithdrawalTx(ctx context.Context,
450510}
451511
452512func (m * Manager ) publishFinalizedWithdrawalTx (ctx context.Context ,
453- tx * wire.MsgTx ) error {
513+ tx * wire.MsgTx ) ( bool , error ) {
454514
455515 if tx == nil {
456- return errors .New ("can't publish, finalized withdrawal tx is " +
457- "nil" )
516+ return false , errors .New ("can't publish, finalized " +
517+ "withdrawal tx is nil" )
458518 }
459519
460520 txLabel := fmt .Sprintf ("deposit-withdrawal-%v" , tx .TxHash ())
461521
462522 // Publish the withdrawal sweep transaction.
463523 err := m .cfg .WalletKit .PublishTransaction (ctx , tx , txLabel )
464-
465524 if err != nil {
466- if ! strings .Contains (err .Error (), "output already spent" ) {
467- log .Errorf ("%v: %v" , txLabel , err )
525+ if ! strings .Contains (err .Error (), "output already spent" ) &&
526+ ! strings .Contains (err .Error (), "insufficient fee" ) {
527+
528+ return false , err
529+ } else {
530+ return false , nil
468531 }
532+ } else {
533+ log .Debugf ("published deposit withdrawal with txid: %v" ,
534+ tx .TxHash ())
469535 }
470536
471- log .Debugf ("published deposit withdrawal with txid: %v" , tx .TxHash ())
472-
473- return nil
537+ return true , nil
474538}
475539
476540func (m * Manager ) handleWithdrawal (ctx context.Context ,
@@ -485,6 +549,13 @@ func (m *Manager) handleWithdrawal(ctx context.Context,
485549 return err
486550 }
487551
552+ // Create a new quit chan for this set of deposits under the same
553+ // withdrawal tx hash. If a new withdrawal is requested the quit chan
554+ // is closed in favor of a new one, to start monitoring the new
555+ // withdrawal transaction.
556+ m .withdrawalHandlerQuitChans [txHash ] = make (chan struct {})
557+ quitChan := m .withdrawalHandlerQuitChans [txHash ]
558+
488559 go func () {
489560 select {
490561 case <- confChan :
@@ -502,6 +573,12 @@ func (m *Manager) handleWithdrawal(ctx context.Context,
502573 // arrivals.
503574 delete (m .finalizedWithdrawalTxns , txHash )
504575
576+ case <- quitChan :
577+ log .Debugf ("Exiting withdrawal handler for tx %v" ,
578+ txHash )
579+
580+ return
581+
505582 case err := <- errChan :
506583 log .Errorf ("Error waiting for confirmation: %v" , err )
507584
@@ -915,7 +992,7 @@ func (m *Manager) republishWithdrawals(ctx context.Context) error {
915992 continue
916993 }
917994
918- err := m .publishFinalizedWithdrawalTx (ctx , finalizedTx )
995+ _ , err := m .publishFinalizedWithdrawalTx (ctx , finalizedTx )
919996 if err != nil {
920997 log .Errorf ("Error republishing withdrawal: %v" , err )
921998
0 commit comments