Skip to content

Commit 7ed76ce

Browse files
committed
staticaddr: lock deposit reads while withdrawing
1 parent f3854c7 commit 7ed76ce

File tree

1 file changed

+41
-16
lines changed

1 file changed

+41
-16
lines changed

staticaddr/withdraw/manager.go

Lines changed: 41 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,11 @@ var (
4646
ErrMissingPreviousWithdrawn = errors.New("can't bump fee for subset " +
4747
"of clustered deposits")
4848

49+
// ErrMissingFinalizedTx is returned if previously withdrawn deposits
50+
// don't have a finalized withdrawal tx attached.
51+
ErrMissingFinalizedTx = errors.New("deposit does not have a " +
52+
"finalized withdrawal tx, can't bump fee")
53+
4954
// MinConfs is the minimum number of confirmations we require for a
5055
// deposit to be considered withdrawn.
5156
MinConfs int32 = 3
@@ -89,7 +94,7 @@ type ManagerConfig struct {
8994
Store *SqlStore
9095
}
9196

92-
// newWithdrawalRequest is used to send withdrawal request to the manager main
97+
// newWithdrawalRequest is used to send a withdrawal request to the manager main
9398
// loop.
9499
type newWithdrawalRequest struct {
95100
outpoints []wire.OutPoint
@@ -115,7 +120,7 @@ type Manager struct {
115120
mu sync.Mutex
116121

117122
// newWithdrawalRequestChan receives a list of outpoints that should be
118-
// withdrawn. The request is forwarded to the managers main loop.
123+
// withdrawn. The request is forwarded to the managers' main loop.
119124
newWithdrawalRequestChan chan newWithdrawalRequest
120125

121126
// exitChan signals subroutines that the withdrawal manager is exiting.
@@ -219,9 +224,9 @@ func (m *Manager) Run(ctx context.Context, initChan chan struct{}) error {
219224
}
220225

221226
func (m *Manager) recoverWithdrawals(ctx context.Context) error {
222-
// To recover withdrawals we cluster those with equal withdrawal
223-
// addresses and publish their withdrawal tx. Each cluster represents a
224-
// separate withdrawal intent by the user.
227+
// To recover withdrawals, we cluster those with equal withdrawal
228+
// addresses and publish their withdrawal tx. Each cluster represents
229+
// one separate withdrawal intent by the user.
225230
withdrawingDeposits, err := m.cfg.DepositManager.GetActiveDepositsInState(
226231
deposit.Withdrawing,
227232
)
@@ -294,7 +299,7 @@ func (m *Manager) recoverWithdrawals(ctx context.Context) error {
294299
return nil
295300
}
296301

297-
// WithdrawDeposits starts a deposits withdrawal flow. If the amount is set to 0
302+
// WithdrawDeposits starts a deposit withdrawal flow. If the amount is set to 0,
298303
// the full amount of the selected deposits will be withdrawn.
299304
func (m *Manager) WithdrawDeposits(ctx context.Context,
300305
outpoints []wire.OutPoint, destAddr string, satPerVbyte int64,
@@ -309,6 +314,16 @@ func (m *Manager) WithdrawDeposits(ctx context.Context,
309314
deposits []*deposit.Deposit
310315
allDeposited bool
311316
allWithdrawing bool
317+
lockDeposits = func(deposits []*deposit.Deposit) {
318+
for _, d := range deposits {
319+
d.Lock()
320+
}
321+
}
322+
unlockDeposits = func(deposits []*deposit.Deposit) {
323+
for _, d := range deposits {
324+
d.Unlock()
325+
}
326+
}
312327
)
313328

314329
// Ensure that the deposits are in a state in which they can be
@@ -325,20 +340,26 @@ func (m *Manager) WithdrawDeposits(ctx context.Context,
325340
deposits, allWithdrawing = m.cfg.DepositManager.AllOutpointsActiveDeposits(
326341
outpoints, deposit.Withdrawing,
327342
)
328-
329343
if !allWithdrawing {
330344
return "", "", ErrWithdrawingMixedDeposits
331345
}
346+
}
347+
348+
// Lock the deposits to prevent concurrent deposit access.
349+
lockDeposits(deposits)
332350

351+
// If the user requested a fee bump, we'll sanity check the selected
352+
// deposits to ensure that they are all part of the same withdrawal.
353+
if !allDeposited {
333354
// Ensure that all previously withdrawn deposits reference their
334355
// finalized withdrawal tx.
335356
for _, d := range deposits {
336357
if d.FinalizedWithdrawalTx == nil {
337-
return "", "", ErrMissingPreviousWithdrawn
358+
return "", "", ErrMissingFinalizedTx
338359
}
339360
}
340361

341-
// If republishing of an existing withdrawal is requested we
362+
// If republishing of an existing withdrawal is requested, we
342363
// ensure that all deposits remain clustered in the context of
343364
// the same withdrawal tx. We do this by checking that they have
344365
// the same previous withdrawal tx hash. This ensures that the
@@ -351,7 +372,7 @@ func (m *Manager) WithdrawDeposits(ctx context.Context,
351372
}
352373
}
353374

354-
// We also avoid that the user selects a subset of previously
375+
// We also avoid it that the user selects a subset of previously
355376
// clustered deposits for a fee bump. This would result in a
356377
// different transaction shape.
357378
outpointMap := make(map[wire.OutPoint]struct{})
@@ -443,9 +464,7 @@ func (m *Manager) WithdrawDeposits(ctx context.Context,
443464
// If a previous withdrawal existed across the selected deposits, and
444465
// it isn't the same as the new withdrawal, we remove it from the
445466
// finalized withdrawals to stop republishing it on block arrivals.
446-
deposits[0].Lock()
447467
prevTx := deposits[0].FinalizedWithdrawalTx
448-
deposits[0].Unlock()
449468

450469
if prevTx != nil && prevTx.TxHash() != finalizedTx.TxHash() {
451470
m.mu.Lock()
@@ -454,13 +473,11 @@ func (m *Manager) WithdrawDeposits(ctx context.Context,
454473
}
455474

456475
// Attach the finalized withdrawal tx to the deposits. After a client
457-
// restart we can use this address as an indicator to republish the
476+
// restarts, we can use this address as an indicator to republish the
458477
// withdrawal tx and continue the withdrawal.
459478
// Deposits with the same withdrawal tx are part of the same withdrawal.
460479
for _, d := range deposits {
461-
d.Lock()
462480
d.FinalizedWithdrawalTx = finalizedTx
463-
d.Unlock()
464481
}
465482

466483
// Add the new withdrawal tx to the finalized withdrawals to republish
@@ -469,8 +486,12 @@ func (m *Manager) WithdrawDeposits(ctx context.Context,
469486
m.finalizedWithdrawalTxns[finalizedTx.TxHash()] = finalizedTx
470487
m.mu.Unlock()
471488

489+
// Unlock the deposits before calling TransitionDeposits, because this
490+
// method locks the deposits itself.
491+
unlockDeposits(deposits)
492+
472493
// Transition the deposits to the withdrawing state. If the user fee
473-
// bumped a withdrawal this results in a NOOP transition.
494+
// bumped a withdrawal, this results in a NOOP transition.
474495
err = m.cfg.DepositManager.TransitionDeposits(
475496
ctx, deposits, deposit.OnWithdrawInitiated, deposit.Withdrawing,
476497
)
@@ -479,6 +500,10 @@ func (m *Manager) WithdrawDeposits(ctx context.Context,
479500
err)
480501
}
481502

503+
// Re-lock the deposits.
504+
lockDeposits(deposits)
505+
defer unlockDeposits(deposits)
506+
482507
// Update the deposits in the database.
483508
for _, d := range deposits {
484509
err = m.cfg.DepositManager.UpdateDeposit(ctx, d)

0 commit comments

Comments
 (0)