@@ -31,10 +31,13 @@ type sweeperTask struct {
3131// it is responsible for sweeping batch outputs that reached the expiration date.
3232// it also handles delaying the sweep events in case some parts of the tree are broadcasted
3333type sweeper struct {
34- wallet ports.WalletService
35- repoManager ports.RepoManager
36- builder ports.TxBuilder
37- scheduler ports.SchedulerService
34+ wallet ports.WalletService
35+ // walletFallbacks are additional arkd-wallets whose batches this arkd may also
36+ // sweep; signing is attempted with the primary wallet first, then each fallback.
37+ walletFallbacks []ports.WalletService
38+ repoManager ports.RepoManager
39+ builder ports.TxBuilder
40+ scheduler ports.SchedulerService
3841
3942 // cache of scheduled tasks, avoid scheduling the same sweep event multiple times
4043 locker * sync.Mutex
@@ -44,14 +47,54 @@ type sweeper struct {
4447}
4548
4649func newSweeper (
47- wallet ports.WalletService , repoManager ports.RepoManager , builder ports.TxBuilder ,
50+ wallet ports.WalletService , walletFallbacks []ports.WalletService ,
51+ repoManager ports.RepoManager , builder ports.TxBuilder ,
4852 scheduler ports.SchedulerService ,
4953) * sweeper {
5054 return & sweeper {
51- wallet , repoManager , builder , scheduler , & sync.Mutex {}, make (map [string ]struct {}), nil ,
55+ wallet , walletFallbacks , repoManager , builder , scheduler ,
56+ & sync.Mutex {}, make (map [string ]struct {}), nil ,
5257 }
5358}
5459
60+ // signingWallets returns the wallets to try when signing a sweep, in order: the
61+ // primary wallet first, then any configured fallbacks.
62+ func (s * sweeper ) signingWallets () []ports.WalletService {
63+ return append ([]ports.WalletService {s .wallet }, s .walletFallbacks ... )
64+ }
65+
66+ // buildAndSignSweepTx builds the sweep transaction once (its destination and fees
67+ // come from the primary wallet) and then attempts to sign it with each wallet in
68+ // order, returning as soon as one succeeds. This lets a single arkd sweep batches
69+ // signed by any of its primary/fallback arkd-wallets. Broadcasting is left to the
70+ // caller.
71+ func buildAndSignSweepTx (
72+ builder ports.TxBuilder , wallets []ports.WalletService , inputs []ports.TxInput ,
73+ ) (string , string , error ) {
74+ unsignedTx , txid , err := builder .BuildSweepTx (inputs )
75+ if err != nil {
76+ return "" , "" , err
77+ }
78+
79+ if len (wallets ) == 0 {
80+ return "" , "" , fmt .Errorf ("no signing wallets configured for sweep tx %s" , txid )
81+ }
82+
83+ signErrs := make ([]error , 0 , len (wallets ))
84+ for i , wallet := range wallets {
85+ signed , signErr := builder .SignSweepTx (wallet , unsignedTx )
86+ if signErr == nil {
87+ return txid , signed , nil
88+ }
89+ // name the failing wallet so a multi-wallet operator can tell which rejected it
90+ signErrs = append (signErrs , fmt .Errorf ("wallet[%d]: %w" , i , signErr ))
91+ }
92+
93+ return "" , "" , fmt .Errorf (
94+ "no wallet could sign sweep tx %s: %w" , txid , errors .Join (signErrs ... ),
95+ )
96+ }
97+
5598func (s * sweeper ) start (ctx context.Context ) error {
5699 s .scheduledTasks = make (map [string ]struct {})
57100 s .scheduler .Start ()
@@ -627,7 +670,9 @@ func (s *sweeper) createBatchSweepTask(commitmentTxid, vtxoTreeRootTxid string)
627670 )
628671
629672 // build the sweep transaction with all the expired non-swept batch outputs
630- sweepTxId , sweepTx , err = s .builder .BuildSweepTx (unspentOutputsToSweep )
673+ sweepTxId , sweepTx , err = buildAndSignSweepTx (
674+ s .builder , s .signingWallets (), unspentOutputsToSweep ,
675+ )
631676 if err != nil {
632677 return err
633678 }
@@ -663,7 +708,9 @@ func (s *sweeper) createBatchSweepTask(commitmentTxid, vtxoTreeRootTxid string)
663708 } else {
664709 // if all outputs are spent, it means we missed to mark the batch as swept,
665710 // build a sweep transaction without broadcasting it. we'll use it rebuild sweepEvent.
666- sweepTxId , sweepTx , err = s .builder .BuildSweepTx (outputsToSweep )
711+ sweepTxId , sweepTx , err = buildAndSignSweepTx (
712+ s .builder , s .signingWallets (), outputsToSweep ,
713+ )
667714 if err != nil {
668715 return err
669716 }
@@ -749,7 +796,9 @@ func (s *sweeper) createCheckpointSweepTask(
749796 checkpointTxid := toSweep .Txid
750797 log .Debugf ("sweeper: start sweeping checkpoint %s" , checkpointTxid )
751798
752- _ , sweepTx , err := s .builder .BuildSweepTx ([]ports.TxInput {toSweep })
799+ _ , sweepTx , err := buildAndSignSweepTx (
800+ s .builder , s .signingWallets (), []ports.TxInput {toSweep },
801+ )
753802 if err != nil {
754803 return err
755804 }
0 commit comments