@@ -11,6 +11,7 @@ import (
1111 "github.com/btcsuite/btcd/btcec/v2/schnorr/musig2"
1212 "github.com/btcsuite/btcd/btcutil"
1313 "github.com/btcsuite/btcd/chaincfg"
14+ "github.com/btcsuite/btcd/chaincfg/chainhash"
1415 "github.com/btcsuite/btcd/txscript"
1516 "github.com/btcsuite/btcd/wire"
1617 "github.com/lightninglabs/lndclient"
@@ -89,14 +90,19 @@ type Manager struct {
8990 // activeWithdrawals stores pending withdrawals by their withdrawal
9091 // address.
9192 activeWithdrawals map [string ][]* deposit.Deposit
93+
94+ // finalizedWithdrawalTxns are the finalized withdrawal transactions
95+ // that are published to the network and re-published on block arrivals.
96+ finalizedWithdrawalTxns map [chainhash.Hash ]* wire.MsgTx
9297}
9398
9499// NewManager creates a new deposit withdrawal manager.
95100func NewManager (cfg * ManagerConfig ) * Manager {
96101 return & Manager {
97- cfg : cfg ,
98- initChan : make (chan struct {}),
99- activeWithdrawals : make (map [string ][]* deposit.Deposit ),
102+ cfg : cfg ,
103+ initChan : make (chan struct {}),
104+ activeWithdrawals : make (map [string ][]* deposit.Deposit ),
105+ finalizedWithdrawalTxns : make (map [chainhash.Hash ]* wire.MsgTx ),
100106 }
101107}
102108
@@ -187,12 +193,21 @@ func (m *Manager) recover() error {
187193 return err
188194 }
189195
190- err = m .publishWithdrawal (m .runCtx , deposits , withdrawalAddress )
196+ finalizedTx , err := m .createFinalizedWithdrawalTx (
197+ m .runCtx , deposits , withdrawalAddress ,
198+ )
199+ if err != nil {
200+ return err
201+ }
202+
203+ err = m .publishFinalizedWithdrawalTx (finalizedTx )
191204 if err != nil {
192205 return err
193206 }
194207
195- err = m .handleWithdrawal (deposits , withdrawalAddress )
208+ err = m .handleWithdrawal (
209+ deposits , finalizedTx .TxHash (), withdrawalAddress ,
210+ )
196211 if err != nil {
197212 return err
198213 }
@@ -261,12 +276,21 @@ func (m *Manager) WithdrawDeposits(ctx context.Context,
261276 return err
262277 }
263278
264- err = m .publishWithdrawal (ctx , deposits , withdrawalAddress )
279+ finalizedTx , err := m .createFinalizedWithdrawalTx (
280+ ctx , deposits , withdrawalAddress ,
281+ )
282+ if err != nil {
283+ return err
284+ }
285+
286+ err = m .publishFinalizedWithdrawalTx (finalizedTx )
265287 if err != nil {
266288 return err
267289 }
268290
269- err = m .handleWithdrawal (deposits , withdrawalAddress )
291+ err = m .handleWithdrawal (
292+ deposits , finalizedTx .TxHash (), withdrawalAddress ,
293+ )
270294 if err != nil {
271295 return err
272296 }
@@ -278,23 +302,24 @@ func (m *Manager) WithdrawDeposits(ctx context.Context,
278302 return nil
279303}
280304
281- func (m * Manager ) publishWithdrawal (ctx context.Context ,
282- deposits []* deposit.Deposit , withdrawalAddress btcutil.Address ) error {
305+ func (m * Manager ) createFinalizedWithdrawalTx (ctx context.Context ,
306+ deposits []* deposit.Deposit , withdrawalAddress btcutil.Address ) (
307+ * wire.MsgTx , error ) {
283308
284309 // Create a musig2 session for each deposit.
285310 withdrawalSessions , clientNonces , err := m .createMusig2Sessions (
286311 ctx , deposits ,
287312 )
288313 if err != nil {
289- return err
314+ return nil , err
290315 }
291316
292317 // Get the fee rate for the withdrawal sweep.
293318 withdrawalSweepFeeRate , err := m .cfg .WalletKit .EstimateFeeRate (
294319 m .runCtx , defaultConfTarget ,
295320 )
296321 if err != nil {
297- return err
322+ return nil , err
298323 }
299324
300325 outpoints := toOutpoints (deposits )
@@ -307,14 +332,14 @@ func (m *Manager) publishWithdrawal(ctx context.Context,
307332 },
308333 )
309334 if err != nil {
310- return err
335+ return nil , err
311336 }
312337
313338 addressParams , err := m .cfg .AddressManager .GetStaticAddressParameters (
314339 ctx ,
315340 )
316341 if err != nil {
317- return fmt .Errorf ("couldn't get confirmation height for " +
342+ return nil , fmt .Errorf ("couldn't get confirmation height for " +
318343 "deposit, %v" , err )
319344 }
320345
@@ -325,12 +350,12 @@ func (m *Manager) publishWithdrawal(ctx context.Context,
325350 withdrawalSweepFeeRate ,
326351 )
327352 if err != nil {
328- return err
353+ return nil , err
329354 }
330355
331356 coopServerNonces , err := toNonces (resp .ServerNonces )
332357 if err != nil {
333- return err
358+ return nil , err
334359 }
335360
336361 // Next we'll get our sweep tx signatures.
@@ -340,40 +365,47 @@ func (m *Manager) publishWithdrawal(ctx context.Context,
340365 withdrawalSessions , coopServerNonces ,
341366 )
342367 if err != nil {
343- return err
368+ return nil , err
344369 }
345370
346371 // Now we'll finalize the sweepless sweep transaction.
347- finalizedWithdrawalTx , err := m .finalizeMusig2Transaction (
372+ finalizedTx , err := m .finalizeMusig2Transaction (
348373 m .runCtx , outpoints , m .cfg .Signer , withdrawalSessions ,
349374 withdrawalTx , resp .Musig2SweepSigs ,
350375 )
351376 if err != nil {
352- return err
377+ return nil , err
353378 }
354379
355- txLabel := fmt .Sprintf ("deposit-withdrawal-%v" ,
356- finalizedWithdrawalTx .TxHash ())
380+ m .finalizedWithdrawalTxns [finalizedTx .TxHash ()] = finalizedTx
381+
382+ return finalizedTx , nil
383+ }
384+
385+ func (m * Manager ) publishFinalizedWithdrawalTx (tx * wire.MsgTx ) error {
386+ if tx == nil {
387+ return errors .New ("can't publish, finalized withdrawal tx is " +
388+ "nil" )
389+ }
390+
391+ txLabel := fmt .Sprintf ("deposit-withdrawal-%v" , tx .TxHash ())
357392
358393 // Publish the withdrawal sweep transaction.
359- err = m .cfg .WalletKit .PublishTransaction (
360- m .runCtx , finalizedWithdrawalTx , txLabel ,
361- )
394+ err := m .cfg .WalletKit .PublishTransaction (m .runCtx , tx , txLabel )
362395
363396 if err != nil {
364397 if ! strings .Contains (err .Error (), "output already spent" ) {
365398 log .Errorf ("%v: %v" , txLabel , err )
366399 }
367400 }
368401
369- log .Debugf ("published deposit withdrawal with txid: %v" ,
370- finalizedWithdrawalTx .TxHash ())
402+ log .Debugf ("published deposit withdrawal with txid: %v" , tx .TxHash ())
371403
372404 return nil
373405}
374406
375407func (m * Manager ) handleWithdrawal (deposits []* deposit.Deposit ,
376- withdrawalAddress btcutil.Address ) error {
408+ txHash chainhash. Hash , withdrawalAddress btcutil.Address ) error {
377409
378410 withdrawalPkScript , err := txscript .PayToAddrScript (withdrawalAddress )
379411 if err != nil {
@@ -382,7 +414,7 @@ func (m *Manager) handleWithdrawal(deposits []*deposit.Deposit,
382414
383415 m .Lock ()
384416 confChan , errChan , err := m .cfg .ChainNotifier .RegisterConfirmationsNtfn (
385- m .runCtx , nil , withdrawalPkScript , MinConfs ,
417+ m .runCtx , & txHash , withdrawalPkScript , MinConfs ,
386418 int32 (m .initiationHeight ),
387419 )
388420 m .Unlock ()
@@ -402,9 +434,12 @@ func (m *Manager) handleWithdrawal(deposits []*deposit.Deposit,
402434 err )
403435 }
404436
405- // Remove the withdrawal from the active withdrawals.
437+ // Remove the withdrawal from the active withdrawals and
438+ // remove its finalized to stop republishing it on block
439+ // arrivals.
406440 m .Lock ()
407441 delete (m .activeWithdrawals , withdrawalAddress .String ())
442+ delete (m .finalizedWithdrawalTxns , txHash )
408443 m .Unlock ()
409444
410445 case err := <- errChan :
@@ -710,17 +745,13 @@ func (m *Manager) toPrevOuts(deposits []*deposit.Deposit,
710745}
711746
712747func (m * Manager ) republishWithdrawals () error {
713- for withdrawalAddress , deposits := range m .activeWithdrawals {
714- address , err := btcutil .DecodeAddress (
715- withdrawalAddress , m .cfg .ChainParams ,
716- )
717- if err != nil {
718- log .Errorf ("Error decoding withdrawal address: %v" , err )
719-
720- return err
748+ for _ , finalizedTx := range m .finalizedWithdrawalTxns {
749+ if finalizedTx == nil {
750+ log .Warnf ("Finalized withdrawal tx is nil" )
751+ continue
721752 }
722753
723- err = m .publishWithdrawal ( m . runCtx , deposits , address )
754+ err : = m .publishFinalizedWithdrawalTx ( finalizedTx )
724755 if err != nil {
725756 log .Errorf ("Error republishing withdrawal: %v" , err )
726757
0 commit comments