@@ -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
@@ -450,27 +509,31 @@ func (m *Manager) createFinalizedWithdrawalTx(ctx context.Context,
450509}
451510
452511func (m * Manager ) publishFinalizedWithdrawalTx (ctx context.Context ,
453- tx * wire.MsgTx ) error {
512+ tx * wire.MsgTx ) ( bool , error ) {
454513
455514 if tx == nil {
456- return errors .New ("can't publish, finalized withdrawal tx is " +
457- "nil" )
515+ return false , errors .New ("can't publish, finalized " +
516+ "withdrawal tx is nil" )
458517 }
459518
460519 txLabel := fmt .Sprintf ("deposit-withdrawal-%v" , tx .TxHash ())
461520
462521 // Publish the withdrawal sweep transaction.
463522 err := m .cfg .WalletKit .PublishTransaction (ctx , tx , txLabel )
464-
465523 if err != nil {
466- if ! strings .Contains (err .Error (), "output already spent" ) {
467- log .Errorf ("%v: %v" , txLabel , err )
524+ if ! strings .Contains (err .Error (), "output already spent" ) &&
525+ ! strings .Contains (err .Error (), "insufficient fee" ) {
526+
527+ return false , err
528+ } else {
529+ return false , nil
468530 }
531+ } else {
532+ log .Debugf ("published deposit withdrawal with txid: %v" ,
533+ tx .TxHash ())
469534 }
470535
471- log .Debugf ("published deposit withdrawal with txid: %v" , tx .TxHash ())
472-
473- return nil
536+ return true , nil
474537}
475538
476539func (m * Manager ) handleWithdrawal (ctx context.Context ,
@@ -485,6 +548,13 @@ func (m *Manager) handleWithdrawal(ctx context.Context,
485548 return err
486549 }
487550
551+ // Create a new quit chan for this set of deposits under the same
552+ // withdrawal tx hash. If a new withdrawal is requested the quit chan
553+ // is closed in favor of a new one, to start monitoring the new
554+ // withdrawal transaction.
555+ m .withdrawalHandlerQuitChans [txHash ] = make (chan struct {})
556+ quitChan := m .withdrawalHandlerQuitChans [txHash ]
557+
488558 go func () {
489559 select {
490560 case <- confChan :
@@ -502,6 +572,12 @@ func (m *Manager) handleWithdrawal(ctx context.Context,
502572 // arrivals.
503573 delete (m .finalizedWithdrawalTxns , txHash )
504574
575+ case <- quitChan :
576+ log .Debugf ("Exiting withdrawal handler for tx %v" ,
577+ txHash )
578+
579+ return
580+
505581 case err := <- errChan :
506582 log .Errorf ("Error waiting for confirmation: %v" , err )
507583
@@ -915,7 +991,7 @@ func (m *Manager) republishWithdrawals(ctx context.Context) error {
915991 continue
916992 }
917993
918- err := m .publishFinalizedWithdrawalTx (ctx , finalizedTx )
994+ _ , err := m .publishFinalizedWithdrawalTx (ctx , finalizedTx )
919995 if err != nil {
920996 log .Errorf ("Error republishing withdrawal: %v" , err )
921997
0 commit comments