@@ -111,17 +111,25 @@ type Manager struct {
111
111
// finalizedWithdrawalTx are the finalized withdrawal transactions that
112
112
// are published to the network and re-published on block arrivals.
113
113
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 {}
114
121
}
115
122
116
123
// NewManager creates a new deposit withdrawal manager.
117
124
func NewManager (cfg * ManagerConfig ) * Manager {
118
125
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 {}),
125
133
}
126
134
}
127
135
@@ -235,7 +243,7 @@ func (m *Manager) recoverWithdrawals(ctx context.Context) error {
235
243
return err
236
244
}
237
245
238
- err = m .publishFinalizedWithdrawalTx (ctx , tx )
246
+ _ , err : = m .publishFinalizedWithdrawalTx (ctx , tx )
239
247
if err != nil {
240
248
return err
241
249
}
@@ -278,8 +286,34 @@ func (m *Manager) WithdrawDeposits(ctx context.Context,
278
286
outpoints , deposit .Deposited ,
279
287
)
280
288
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.
281
293
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
+
283
317
}
284
318
285
319
var (
@@ -313,6 +347,40 @@ func (m *Manager) WithdrawDeposits(ctx context.Context,
313
347
return "" , "" , err
314
348
}
315
349
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
+
316
384
// Attach the finalized withdrawal tx to the deposits. After a client
317
385
// restart we can use this address as an indicator to republish the
318
386
// withdrawal tx and continue the withdrawal.
@@ -323,6 +391,8 @@ func (m *Manager) WithdrawDeposits(ctx context.Context,
323
391
d .Unlock ()
324
392
}
325
393
394
+ m .finalizedWithdrawalTxns [finalizedTx .TxHash ()] = finalizedTx
395
+
326
396
// Transition the deposits to the withdrawing state. This updates each
327
397
// deposits withdrawal address. If a transition fails, we'll return an
328
398
// error and abort the withdrawal. An error in transition is likely due
@@ -335,25 +405,14 @@ func (m *Manager) WithdrawDeposits(ctx context.Context,
335
405
return "" , "" , err
336
406
}
337
407
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
+ }
353
414
}
354
415
355
- m .finalizedWithdrawalTxns [finalizedTx .TxHash ()] = finalizedTx
356
-
357
416
return finalizedTx .TxID (), withdrawalAddress .String (), nil
358
417
}
359
418
@@ -449,27 +508,31 @@ func (m *Manager) createFinalizedWithdrawalTx(ctx context.Context,
449
508
}
450
509
451
510
func (m * Manager ) publishFinalizedWithdrawalTx (ctx context.Context ,
452
- tx * wire.MsgTx ) error {
511
+ tx * wire.MsgTx ) ( bool , error ) {
453
512
454
513
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" )
457
516
}
458
517
459
518
txLabel := fmt .Sprintf ("deposit-withdrawal-%v" , tx .TxHash ())
460
519
461
520
// Publish the withdrawal sweep transaction.
462
521
err := m .cfg .WalletKit .PublishTransaction (ctx , tx , txLabel )
463
-
464
522
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
467
529
}
530
+ } else {
531
+ log .Debugf ("published deposit withdrawal with txid: %v" ,
532
+ tx .TxHash ())
468
533
}
469
534
470
- log .Debugf ("published deposit withdrawal with txid: %v" , tx .TxHash ())
471
-
472
- return nil
535
+ return true , nil
473
536
}
474
537
475
538
func (m * Manager ) handleWithdrawal (ctx context.Context ,
@@ -484,6 +547,13 @@ func (m *Manager) handleWithdrawal(ctx context.Context,
484
547
return err
485
548
}
486
549
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
+
487
557
go func () {
488
558
select {
489
559
case <- confChan :
@@ -501,6 +571,12 @@ func (m *Manager) handleWithdrawal(ctx context.Context,
501
571
// arrivals.
502
572
delete (m .finalizedWithdrawalTxns , txHash )
503
573
574
+ case <- quitChan :
575
+ log .Debugf ("Exiting withdrawal handler for tx %v" ,
576
+ txHash )
577
+
578
+ return
579
+
504
580
case err := <- errChan :
505
581
log .Errorf ("Error waiting for confirmation: %v" , err )
506
582
@@ -914,7 +990,7 @@ func (m *Manager) republishWithdrawals(ctx context.Context) error {
914
990
continue
915
991
}
916
992
917
- err := m .publishFinalizedWithdrawalTx (ctx , finalizedTx )
993
+ _ , err := m .publishFinalizedWithdrawalTx (ctx , finalizedTx )
918
994
if err != nil {
919
995
log .Errorf ("Error republishing withdrawal: %v" , err )
920
996
0 commit comments