@@ -425,9 +425,12 @@ pub trait CoinSelectionSource {
425425 /// which UTXOs to double spend is left to the implementation, but it must strive to keep the
426426 /// set of other claims being double spent to a minimum.
427427 ///
428+ /// If `claim_id` is not set, then the selection should be treated as if it were for a unique
429+ /// claim and must NOT be double-spent rather than being kept to a minimum.
430+ ///
428431 /// [`ChannelMonitor::rebroadcast_pending_claims`]: crate::chain::channelmonitor::ChannelMonitor::rebroadcast_pending_claims
429432 fn select_confirmed_utxos < ' a > (
430- & ' a self , claim_id : ClaimId , must_spend : Vec < Input > , must_pay_to : & ' a [ TxOut ] ,
433+ & ' a self , claim_id : Option < ClaimId > , must_spend : Vec < Input > , must_pay_to : & ' a [ TxOut ] ,
431434 target_feerate_sat_per_1000_weight : u32 , max_tx_weight : u64 ,
432435 ) -> impl Future < Output = Result < CoinSelection , ( ) > > + MaybeSend + ' a ;
433436 /// Signs and provides the full witness for all inputs within the transaction known to the
@@ -492,7 +495,7 @@ where
492495 // TODO: Do we care about cleaning this up once the UTXOs have a confirmed spend? We can do so
493496 // by checking whether any UTXOs that exist in the map are no longer returned in
494497 // `list_confirmed_utxos`.
495- locked_utxos : Mutex < HashMap < OutPoint , ClaimId > > ,
498+ locked_utxos : Mutex < HashMap < OutPoint , Option < ClaimId > > > ,
496499}
497500
498501impl < W : Deref + MaybeSync + MaybeSend , L : Logger + MaybeSync + MaybeSend > Wallet < W , L >
@@ -514,11 +517,13 @@ where
514517 /// least 1 satoshi at the current feerate, otherwise, we'll only attempt to spend those which
515518 /// contribute at least twice their fee.
516519 async fn select_confirmed_utxos_internal (
517- & self , utxos : & [ Utxo ] , claim_id : ClaimId , force_conflicting_utxo_spend : bool ,
520+ & self , utxos : & [ Utxo ] , claim_id : Option < ClaimId > , force_conflicting_utxo_spend : bool ,
518521 tolerate_high_network_feerates : bool , target_feerate_sat_per_1000_weight : u32 ,
519522 preexisting_tx_weight : u64 , input_amount_sat : Amount , target_amount_sat : Amount ,
520523 max_tx_weight : u64 ,
521524 ) -> Result < CoinSelection , ( ) > {
525+ debug_assert ! ( !( claim_id. is_none( ) && force_conflicting_utxo_spend) ) ;
526+
522527 // P2WSH and P2TR outputs are both the heaviest-weight standard outputs at 34 bytes
523528 let max_coin_selection_weight = max_tx_weight
524529 . checked_sub ( preexisting_tx_weight + P2WSH_TXOUT_WEIGHT )
@@ -538,7 +543,12 @@ where
538543 . iter ( )
539544 . filter_map ( |utxo| {
540545 if let Some ( utxo_claim_id) = locked_utxos. get ( & utxo. outpoint ) {
541- if * utxo_claim_id != claim_id && !force_conflicting_utxo_spend {
546+ // TODO(splicing): For splicing (i.e., claim_id.is_none()), ideally we'd
547+ // allow force_conflicting_utxo_spend for an RBF attempt. However, we'd need
548+ // something similar to a ClaimId to identify a splice.
549+ if ( utxo_claim_id. is_none ( ) || * utxo_claim_id != claim_id)
550+ && !force_conflicting_utxo_spend
551+ {
542552 log_trace ! (
543553 self . logger,
544554 "Skipping UTXO {} to prevent conflicting spend" ,
@@ -678,7 +688,7 @@ where
678688 W :: Target : WalletSource + MaybeSend + MaybeSync ,
679689{
680690 fn select_confirmed_utxos < ' a > (
681- & ' a self , claim_id : ClaimId , must_spend : Vec < Input > , must_pay_to : & ' a [ TxOut ] ,
691+ & ' a self , claim_id : Option < ClaimId > , must_spend : Vec < Input > , must_pay_to : & ' a [ TxOut ] ,
682692 target_feerate_sat_per_1000_weight : u32 , max_tx_weight : u64 ,
683693 ) -> impl Future < Output = Result < CoinSelection , ( ) > > + MaybeSend + ' a {
684694 async move {
@@ -703,6 +713,9 @@ where
703713
704714 let configs = [ ( false , false ) , ( false , true ) , ( true , false ) , ( true , true ) ] ;
705715 for ( force_conflicting_utxo_spend, tolerate_high_network_feerates) in configs {
716+ if claim_id. is_none ( ) && force_conflicting_utxo_spend {
717+ continue ;
718+ }
706719 log_debug ! (
707720 self . logger,
708721 "Attempting coin selection targeting {} sat/kW (force_conflicting_utxo_spend = {}, tolerate_high_network_feerates = {})" ,
@@ -874,7 +887,7 @@ where
874887 let coin_selection: CoinSelection = self
875888 . utxo_source
876889 . select_confirmed_utxos (
877- claim_id,
890+ Some ( claim_id) ,
878891 must_spend,
879892 & [ ] ,
880893 package_target_feerate_sat_per_1000_weight,
@@ -1137,7 +1150,7 @@ where
11371150 let coin_selection: CoinSelection = match self
11381151 . utxo_source
11391152 . select_confirmed_utxos (
1140- utxo_id,
1153+ Some ( utxo_id) ,
11411154 must_spend,
11421155 & htlc_tx. output ,
11431156 target_feerate_sat_per_1000_weight,
@@ -1358,7 +1371,7 @@ mod tests {
13581371 }
13591372 impl CoinSelectionSourceSync for TestCoinSelectionSource {
13601373 fn select_confirmed_utxos (
1361- & self , _claim_id : ClaimId , must_spend : Vec < Input > , _must_pay_to : & [ TxOut ] ,
1374+ & self , _claim_id : Option < ClaimId > , must_spend : Vec < Input > , _must_pay_to : & [ TxOut ] ,
13621375 target_feerate_sat_per_1000_weight : u32 , _max_tx_weight : u64 ,
13631376 ) -> Result < CoinSelection , ( ) > {
13641377 let mut expected_selects = self . expected_selects . lock ( ) . unwrap ( ) ;
0 commit comments