@@ -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 the user is requesting
291+ // a fee bump, if not we'll return an error as we only allow fee bumping
292+ // 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,40 @@ 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+ if prevTx != nil && prevTx .TxHash () != finalizedTx .TxHash () {
377+ quitChan := m .withdrawalHandlerQuitChans [prevTx .TxHash ()]
378+ close (quitChan )
379+ delete (m .withdrawalHandlerQuitChans , prevTx .TxHash ())
380+ delete (m .finalizedWithdrawalTxns , prevTx .TxHash ())
381+ }
382+ deposits [0 ].Unlock ()
383+
316384 // Attach the finalized withdrawal tx to the deposits. After a client
317385 // restart we can use this address as an indicator to republish the
318386 // withdrawal tx and continue the withdrawal.
@@ -323,6 +391,8 @@ func (m *Manager) WithdrawDeposits(ctx context.Context,
323391 d .Unlock ()
324392 }
325393
394+ m .finalizedWithdrawalTxns [finalizedTx .TxHash ()] = finalizedTx
395+
326396 // Transition the deposits to the withdrawing state. This updates each
327397 // deposits withdrawal address. If a transition fails, we'll return an
328398 // error and abort the withdrawal. An error in transition is likely due
@@ -335,25 +405,14 @@ func (m *Manager) WithdrawDeposits(ctx context.Context,
335405 return "" , "" , err
336406 }
337407
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
408+ // Update the deposits in the database.
409+ for _ , d := range deposits {
410+ err = m .cfg .DepositManager .UpdateDeposit (ctx , d )
411+ if err != nil {
412+ return "" , "" , err
413+ }
353414 }
354415
355- m .finalizedWithdrawalTxns [finalizedTx .TxHash ()] = finalizedTx
356-
357416 return finalizedTx .TxID (), withdrawalAddress .String (), nil
358417}
359418
@@ -449,27 +508,31 @@ func (m *Manager) createFinalizedWithdrawalTx(ctx context.Context,
449508}
450509
451510func (m * Manager ) publishFinalizedWithdrawalTx (ctx context.Context ,
452- tx * wire.MsgTx ) error {
511+ tx * wire.MsgTx ) ( bool , error ) {
453512
454513 if tx == nil {
455- return errors .New ("can't publish, finalized withdrawal tx is " +
456- "nil" )
514+ return false , errors .New ("can't publish, finalized " +
515+ "withdrawal tx is nil" )
457516 }
458517
459518 txLabel := fmt .Sprintf ("deposit-withdrawal-%v" , tx .TxHash ())
460519
461520 // Publish the withdrawal sweep transaction.
462521 err := m .cfg .WalletKit .PublishTransaction (ctx , tx , txLabel )
463-
464522 if err != nil {
465- if ! strings .Contains (err .Error (), "output already spent" ) {
466- log .Errorf ("%v: %v" , txLabel , err )
523+ if ! strings .Contains (err .Error (), "output already spent" ) &&
524+ ! strings .Contains (err .Error (), "insufficient fee" ) {
525+
526+ return false , err
527+ } else {
528+ return false , nil
467529 }
530+ } else {
531+ log .Debugf ("published deposit withdrawal with txid: %v" ,
532+ tx .TxHash ())
468533 }
469534
470- log .Debugf ("published deposit withdrawal with txid: %v" , tx .TxHash ())
471-
472- return nil
535+ return true , nil
473536}
474537
475538func (m * Manager ) handleWithdrawal (ctx context.Context ,
@@ -484,6 +547,13 @@ func (m *Manager) handleWithdrawal(ctx context.Context,
484547 return err
485548 }
486549
550+ // Create a new quit chan for this set of deposits under the same
551+ // withdrawal tx hash. If a new withdrawal is requested the quit chan
552+ // is closed in favor of a new one, to start monitoring the new
553+ // withdrawal transaction.
554+ m .withdrawalHandlerQuitChans [txHash ] = make (chan struct {})
555+ quitChan := m .withdrawalHandlerQuitChans [txHash ]
556+
487557 go func () {
488558 select {
489559 case <- confChan :
@@ -501,6 +571,12 @@ func (m *Manager) handleWithdrawal(ctx context.Context,
501571 // arrivals.
502572 delete (m .finalizedWithdrawalTxns , txHash )
503573
574+ case <- quitChan :
575+ log .Debugf ("Exiting withdrawal handler for tx %v" ,
576+ txHash )
577+
578+ return
579+
504580 case err := <- errChan :
505581 log .Errorf ("Error waiting for confirmation: %v" , err )
506582
@@ -914,7 +990,7 @@ func (m *Manager) republishWithdrawals(ctx context.Context) error {
914990 continue
915991 }
916992
917- err := m .publishFinalizedWithdrawalTx (ctx , finalizedTx )
993+ _ , err := m .publishFinalizedWithdrawalTx (ctx , finalizedTx )
918994 if err != nil {
919995 log .Errorf ("Error republishing withdrawal: %v" , err )
920996
0 commit comments