Skip to content

Commit 831f1c0

Browse files
committed
staticaddr: listen for spents on withdrawals
1 parent 94d8285 commit 831f1c0

File tree

2 files changed

+65
-57
lines changed

2 files changed

+65
-57
lines changed

staticaddr/deposit/fsm.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -361,7 +361,8 @@ func (f *FSM) DepositStatesV0() fsm.States {
361361
},
362362
Withdrawn: fsm.State{
363363
Transitions: fsm.Transitions{
364-
OnExpiry: Expired,
364+
OnExpiry: Expired,
365+
OnWithdrawn: Withdrawn,
365366
},
366367
Action: f.FinalizeDepositAction,
367368
},

staticaddr/withdraw/manager.go

Lines changed: 63 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"fmt"
77
"reflect"
88
"strings"
9+
"sync"
910
"sync/atomic"
1011

1112
"github.com/btcsuite/btcd/btcec/v2/schnorr"
@@ -93,6 +94,9 @@ type newWithdrawalResponse struct {
9394
type Manager struct {
9495
cfg *ManagerConfig
9596

97+
// mu protects access to finalizedWithdrawalTxns.
98+
mu sync.Mutex
99+
96100
// initChan signals the daemon that the withdrawal manager has completed
97101
// its initialization.
98102
initChan chan struct{}
@@ -113,25 +117,17 @@ type Manager struct {
113117
// finalizedWithdrawalTx are the finalized withdrawal transactions that
114118
// are published to the network and re-published on block arrivals.
115119
finalizedWithdrawalTxns map[chainhash.Hash]*wire.MsgTx
116-
117-
// withdrawalHandlerQuitChans is a map of quit channels for each
118-
// withdrawal transaction. The quit channels are used to stop the
119-
// withdrawal handler for a specific withdrawal transaction, e.g. if
120-
// a new rbf'd transaction has to be monitored for confirmation in
121-
// favor of the previous one.
122-
withdrawalHandlerQuitChans map[chainhash.Hash]chan struct{}
123120
}
124121

125122
// NewManager creates a new deposit withdrawal manager.
126123
func NewManager(cfg *ManagerConfig, currentHeight uint32) *Manager {
127124
m := &Manager{
128-
cfg: cfg,
129-
initChan: make(chan struct{}),
130-
finalizedWithdrawalTxns: make(map[chainhash.Hash]*wire.MsgTx),
131-
exitChan: make(chan struct{}),
132-
newWithdrawalRequestChan: make(chan newWithdrawalRequest),
133-
errChan: make(chan error),
134-
withdrawalHandlerQuitChans: make(map[chainhash.Hash]chan struct{}),
125+
cfg: cfg,
126+
initChan: make(chan struct{}),
127+
finalizedWithdrawalTxns: make(map[chainhash.Hash]*wire.MsgTx),
128+
exitChan: make(chan struct{}),
129+
newWithdrawalRequestChan: make(chan newWithdrawalRequest),
130+
errChan: make(chan error),
135131
}
136132
m.initiationHeight.Store(currentHeight)
137133

@@ -251,14 +247,14 @@ func (m *Manager) recoverWithdrawals(ctx context.Context) error {
251247
return err
252248
}
253249

254-
err = m.handleWithdrawal(
255-
ctx, deposits, tx.TxHash(), tx.TxOut[0].PkScript,
256-
)
250+
err = m.handleWithdrawal(ctx, deposits, tx.TxHash())
257251
if err != nil {
258252
return err
259253
}
260254

255+
m.mu.Lock()
261256
m.finalizedWithdrawalTxns[tx.TxHash()] = tx
257+
m.mu.Unlock()
262258
}
263259

264260
return nil
@@ -316,6 +312,30 @@ func (m *Manager) WithdrawDeposits(ctx context.Context,
316312
"previous withdrawal tx hash")
317313
}
318314
}
315+
316+
// We also avoid that the user selects a subset of previously
317+
// clustered deposits for a fee bump. This would result in a
318+
// different transaction shape.
319+
allDeposits, err := m.cfg.DepositManager.GetActiveDepositsInState(
320+
deposit.Withdrawing,
321+
)
322+
if err != nil {
323+
return "", "", err
324+
}
325+
326+
allDepositsWithHash := make(map[chainhash.Hash][]*deposit.Deposit)
327+
for _, d := range allDeposits {
328+
if d.FinalizedWithdrawalTx.TxHash() == hash {
329+
allDepositsWithHash[hash] = append(
330+
allDepositsWithHash[hash], d,
331+
)
332+
}
333+
}
334+
335+
if len(allDepositsWithHash[hash]) != len(deposits) {
336+
return "", "", fmt.Errorf("can't bump fee for subset " +
337+
"of clustered deposits")
338+
}
319339
}
320340

321341
var (
@@ -358,30 +378,24 @@ func (m *Manager) WithdrawDeposits(ctx context.Context,
358378
return "", "", nil
359379
}
360380

361-
withdrawalPkScript, err := txscript.PayToAddrScript(withdrawalAddress)
362-
if err != nil {
363-
return "", "", err
364-
}
365-
366-
err = m.handleWithdrawal(
367-
ctx, deposits, finalizedTx.TxHash(), withdrawalPkScript,
368-
)
381+
err = m.handleWithdrawal(ctx, deposits, finalizedTx.TxHash())
369382
if err != nil {
370383
return "", "", err
371384
}
372385

373386
// If a previous withdrawal existed across the selected deposits, and
374-
// it isn't the same as the new withdrawal, we'll stop monitoring the
375-
// previous withdrawal and remove it from the finalized withdrawals.
387+
// it isn't the same as the new withdrawal, we remove it from the
388+
// finalized withdrawals to stop republishing it, but we keep the
389+
// goroutine in handleWithdrawal running to monitor the potential
390+
// confirmation of the previous withdrawal.
376391
deposits[0].Lock()
377392
prevTx := deposits[0].FinalizedWithdrawalTx
378393
deposits[0].Unlock()
379394

380395
if prevTx != nil && prevTx.TxHash() != finalizedTx.TxHash() {
381-
quitChan := m.withdrawalHandlerQuitChans[prevTx.TxHash()]
382-
close(quitChan)
383-
delete(m.withdrawalHandlerQuitChans, prevTx.TxHash())
396+
m.mu.Lock()
384397
delete(m.finalizedWithdrawalTxns, prevTx.TxHash())
398+
m.mu.Unlock()
385399
}
386400

387401
// Attach the finalized withdrawal tx to the deposits. After a client
@@ -394,7 +408,9 @@ func (m *Manager) WithdrawDeposits(ctx context.Context,
394408
d.Unlock()
395409
}
396410

411+
m.mu.Lock()
397412
m.finalizedWithdrawalTxns[finalizedTx.TxHash()] = finalizedTx
413+
m.mu.Unlock()
398414

399415
// Transition the deposits to the withdrawing state. This updates each
400416
// deposits withdrawal address. If a transition fails, we'll return an
@@ -540,27 +556,16 @@ func (m *Manager) publishFinalizedWithdrawalTx(ctx context.Context,
540556
}
541557

542558
func (m *Manager) handleWithdrawal(ctx context.Context,
543-
deposits []*deposit.Deposit, txHash chainhash.Hash,
544-
withdrawalPkScript []byte) error {
559+
deposits []*deposit.Deposit, txHash chainhash.Hash) error {
545560

546-
confChan, errChan, err := m.cfg.ChainNotifier.RegisterConfirmationsNtfn(
547-
ctx, &txHash, withdrawalPkScript, MinConfs,
548-
int32(m.initiationHeight.Load()),
561+
d := deposits[0]
562+
spentChan, errChan, err := m.cfg.ChainNotifier.RegisterSpendNtfn(
563+
ctx, &d.OutPoint, nil, int32(d.ConfirmationHeight),
549564
)
550-
if err != nil {
551-
return err
552-
}
553-
554-
// Create a new quit chan for this set of deposits under the same
555-
// withdrawal tx hash. If a new withdrawal is requested the quit chan
556-
// is closed in favor of a new one, to start monitoring the new
557-
// withdrawal transaction.
558-
m.withdrawalHandlerQuitChans[txHash] = make(chan struct{})
559-
quitChan := m.withdrawalHandlerQuitChans[txHash]
560565

561566
go func() {
562567
select {
563-
case <-confChan:
568+
case <-spentChan:
564569
err = m.cfg.DepositManager.TransitionDeposits(
565570
ctx, deposits, deposit.OnWithdrawn,
566571
deposit.Withdrawn,
@@ -570,16 +575,11 @@ func (m *Manager) handleWithdrawal(ctx context.Context,
570575
err)
571576
}
572577

573-
// Remove the withdrawal from the active withdrawals and
574-
// remove its finalized to stop republishing it on block
575-
// arrivals.
578+
// Remove the withdrawal tx from the active withdrawals
579+
// to stop republishing it on block arrivals.
580+
m.mu.Lock()
576581
delete(m.finalizedWithdrawalTxns, txHash)
577-
578-
case <-quitChan:
579-
log.Debugf("Exiting withdrawal handler for tx %v",
580-
txHash)
581-
582-
return
582+
m.mu.Unlock()
583583

584584
case err := <-errChan:
585585
log.Errorf("Error waiting for confirmation: %v", err)
@@ -987,7 +987,14 @@ func (m *Manager) toPrevOuts(deposits []*deposit.Deposit,
987987
}
988988

989989
func (m *Manager) republishWithdrawals(ctx context.Context) error {
990-
for _, finalizedTx := range m.finalizedWithdrawalTxns {
990+
m.mu.Lock()
991+
txns := make([]*wire.MsgTx, 0, len(m.finalizedWithdrawalTxns))
992+
for _, tx := range m.finalizedWithdrawalTxns {
993+
txns = append(txns, tx)
994+
}
995+
m.mu.Unlock()
996+
997+
for _, finalizedTx := range txns {
991998
if finalizedTx == nil {
992999
log.Warnf("Finalized withdrawal tx is nil")
9931000
continue

0 commit comments

Comments
 (0)