@@ -12,6 +12,7 @@ use std::sync::{Arc, Mutex};
1212
1313use bdk_chain:: spk_client:: { FullScanRequest , SyncRequest } ;
1414use bdk_wallet:: descriptor:: ExtendedDescriptor ;
15+ use bdk_wallet:: error:: { BuildFeeBumpError , CreateTxError } ;
1516use bdk_wallet:: event:: WalletEvent ;
1617#[ allow( deprecated) ]
1718use bdk_wallet:: SignOptions ;
@@ -30,7 +31,9 @@ use bitcoin::{
3031 WitnessProgram , WitnessVersion ,
3132} ;
3233
33- use lightning:: chain:: chaininterface:: BroadcasterInterface ;
34+ use lightning:: chain:: chaininterface:: {
35+ BroadcasterInterface , INCREMENTAL_RELAY_FEE_SAT_PER_1000_WEIGHT ,
36+ } ;
3437use lightning:: chain:: channelmonitor:: ANTI_REORG_DELAY ;
3538use lightning:: chain:: { BestBlock , Listen } ;
3639use lightning:: events:: bump_transaction:: { Input , Utxo , WalletSource } ;
@@ -238,16 +241,23 @@ impl Wallet {
238241 confirmation_status,
239242 ) ;
240243
241- let pending_payment =
242- self . create_pending_payment_from_tx ( payment. clone ( ) , Vec :: new ( ) ) ;
244+ self . payment_store . insert_or_update ( payment. clone ( ) ) ?;
243245
244- self . payment_store . insert_or_update ( payment) ?;
245- self . pending_payment_store . insert_or_update ( pending_payment) ?;
246+ if payment_status == PaymentStatus :: Pending {
247+ let pending_payment =
248+ self . create_pending_payment_from_tx ( payment, Vec :: new ( ) ) ;
249+
250+ self . pending_payment_store . insert_or_update ( pending_payment) ?;
251+ }
246252 } ,
247253 WalletEvent :: ChainTipChanged { new_tip, .. } => {
248- // Get all on-chain payments that are Pending
249254 let pending_payments: Vec < PendingPaymentDetails > =
250255 self . pending_payment_store . list_filter ( |p| {
256+ debug_assert ! (
257+ p. details. status == PaymentStatus :: Pending ,
258+ "Non-pending payment {:?} found in pending store" ,
259+ p. details. id,
260+ ) ;
251261 p. details . status == PaymentStatus :: Pending
252262 && matches ! ( p. details. kind, PaymentKind :: Onchain { .. } )
253263 } ) ;
@@ -265,6 +275,11 @@ impl Wallet {
265275 payment. details . status = PaymentStatus :: Succeeded ;
266276 self . payment_store . insert_or_update ( payment. details ) ?;
267277 self . pending_payment_store . remove ( & payment_id) ?;
278+ debug_assert ! (
279+ !self . pending_payment_store. contains_key( & payment_id) ,
280+ "Payment {:?} still in pending store after removal" ,
281+ payment_id,
282+ ) ;
268283 }
269284 } ,
270285 PaymentKind :: Onchain {
@@ -286,7 +301,16 @@ impl Wallet {
286301 . collect ( ) ;
287302
288303 if !txs_to_broadcast. is_empty ( ) {
289- let tx_refs: Vec < & Transaction > = txs_to_broadcast. iter ( ) . collect ( ) ;
304+ let tx_refs: Vec < (
305+ & Transaction ,
306+ lightning:: chain:: chaininterface:: TransactionType ,
307+ ) > =
308+ txs_to_broadcast
309+ . iter ( )
310+ . map ( |tx| {
311+ ( tx, lightning:: chain:: chaininterface:: TransactionType :: Sweep { channels : vec ! [ ] } )
312+ } )
313+ . collect ( ) ;
290314 self . broadcaster . broadcast_transactions ( & tx_refs) ;
291315 log_info ! (
292316 self . logger,
@@ -314,7 +338,7 @@ impl Wallet {
314338 self . payment_store . insert_or_update ( payment) ?;
315339 self . pending_payment_store . insert_or_update ( pending_payment) ?;
316340 } ,
317- WalletEvent :: TxReplaced { txid, conflicts, tx , .. } => {
341+ WalletEvent :: TxReplaced { txid, conflicts, .. } => {
318342 let payment_id = self
319343 . find_payment_by_txid ( txid)
320344 . unwrap_or_else ( || PaymentId ( txid. to_byte_array ( ) ) ) ;
@@ -323,14 +347,14 @@ impl Wallet {
323347 let conflict_txids: Vec < Txid > =
324348 conflicts. iter ( ) . map ( |( _, conflict_txid) | * conflict_txid) . collect ( ) ;
325349
326- let payment = self . create_payment_from_tx (
327- locked_wallet,
328- txid,
350+ // We fetch payment details here since the replacement has updated the stored state
351+ debug_assert ! (
352+ self . payment_store. get( & payment_id) . is_some( ) ,
353+ "Payment {:?} expected in store during WalletEvent::TxReplaced but not found" ,
329354 payment_id,
330- & tx,
331- PaymentStatus :: Pending ,
332- ConfirmationStatus :: Unconfirmed ,
333355 ) ;
356+ let payment =
357+ self . payment_store . get ( & payment_id) . ok_or ( Error :: InvalidPaymentId ) ?;
334358 let pending_payment_details = self
335359 . create_pending_payment_from_tx ( payment. clone ( ) , conflict_txids. clone ( ) ) ;
336360
@@ -1005,6 +1029,163 @@ impl Wallet {
10051029
10061030 None
10071031 }
1032+
1033+ #[ allow( deprecated) ]
1034+ pub ( crate ) fn bump_fee_rbf (
1035+ & self , payment_id : PaymentId , fee_rate : Option < FeeRate > ,
1036+ ) -> Result < Txid , Error > {
1037+ let payment = self . payment_store . get ( & payment_id) . ok_or ( Error :: InvalidPaymentId ) ?;
1038+
1039+ let mut locked_wallet = self . inner . lock ( ) . unwrap ( ) ;
1040+
1041+ if let PaymentKind :: Onchain { status, .. } = & payment. kind {
1042+ match status {
1043+ ConfirmationStatus :: Confirmed { .. } => {
1044+ log_error ! (
1045+ self . logger,
1046+ "Transaction {} is already confirmed and cannot be fee bumped" ,
1047+ payment_id
1048+ ) ;
1049+ return Err ( Error :: InvalidPaymentId ) ;
1050+ } ,
1051+ ConfirmationStatus :: Unconfirmed => { } ,
1052+ }
1053+ }
1054+
1055+ if payment. direction != PaymentDirection :: Outbound {
1056+ log_error ! ( self . logger, "Transaction {} is not an outbound payment" , payment_id) ;
1057+ return Err ( Error :: InvalidPaymentId ) ;
1058+ }
1059+
1060+ let txid = match & payment. kind {
1061+ PaymentKind :: Onchain { txid, .. } => * txid,
1062+ _ => return Err ( Error :: InvalidPaymentId ) ,
1063+ } ;
1064+
1065+ debug_assert ! (
1066+ locked_wallet. tx_details( txid) . is_some( ) ,
1067+ "Transaction {} expected in wallet but not found" ,
1068+ txid,
1069+ ) ;
1070+ let old_tx =
1071+ locked_wallet. tx_details ( txid) . ok_or ( Error :: InvalidPaymentId ) ?. tx . deref ( ) . clone ( ) ;
1072+
1073+ let old_fee_rate = locked_wallet. calculate_fee_rate ( & old_tx) . map_err ( |e| {
1074+ log_error ! ( self . logger, "Failed to calculate fee rate of transaction {}: {}" , txid, e) ;
1075+ Error :: InvalidPaymentId
1076+ } ) ?;
1077+ let old_fee_rate_sat_per_kwu = old_fee_rate. to_sat_per_kwu ( ) ;
1078+
1079+ // BIP 125 requires the replacement to pay a higher fee rate than the original.
1080+ // The minimum increase is the incremental relay fee.
1081+ let min_required_fee_rate_sat_per_kwu =
1082+ old_fee_rate_sat_per_kwu + INCREMENTAL_RELAY_FEE_SAT_PER_1000_WEIGHT as u64 ;
1083+
1084+ let confirmation_target = ConfirmationTarget :: OnchainPayment ;
1085+ let estimated_fee_rate =
1086+ fee_rate. unwrap_or_else ( || self . fee_estimator . estimate_fee_rate ( confirmation_target) ) ;
1087+
1088+ // Use the higher of minimum RBF requirement or current network estimate
1089+ let final_fee_rate_sat_per_kwu =
1090+ min_required_fee_rate_sat_per_kwu. max ( estimated_fee_rate. to_sat_per_kwu ( ) ) ;
1091+ let final_fee_rate = FeeRate :: from_sat_per_kwu ( final_fee_rate_sat_per_kwu) ;
1092+
1093+ let mut psbt = {
1094+ let mut builder = locked_wallet. build_fee_bump ( txid) . map_err ( |e| {
1095+ log_error ! ( self . logger, "BDK fee bump failed for {}: {:?}" , txid, e) ;
1096+ match e {
1097+ BuildFeeBumpError :: TransactionNotFound ( _) => Error :: InvalidPaymentId ,
1098+ BuildFeeBumpError :: TransactionConfirmed ( _) => {
1099+ log_error ! ( self . logger, "Payment {} is already confirmed" , payment_id) ;
1100+ Error :: InvalidPaymentId
1101+ } ,
1102+ BuildFeeBumpError :: IrreplaceableTransaction ( _) => {
1103+ Error :: OnchainTxCreationFailed
1104+ } ,
1105+ BuildFeeBumpError :: FeeRateUnavailable => Error :: FeerateEstimationUpdateFailed ,
1106+ BuildFeeBumpError :: UnknownUtxo ( _) => Error :: OnchainTxCreationFailed ,
1107+ BuildFeeBumpError :: InvalidOutputIndex ( _) => Error :: OnchainTxCreationFailed ,
1108+ }
1109+ } ) ?;
1110+
1111+ builder. fee_rate ( final_fee_rate) ;
1112+
1113+ match builder. finish ( ) {
1114+ Ok ( psbt) => Ok ( psbt) ,
1115+ Err ( CreateTxError :: FeeRateTooLow { required : required_fee_rate } ) => {
1116+ log_info ! ( self . logger, "BDK requires higher fee rate: {}" , required_fee_rate) ;
1117+
1118+ let mut builder = locked_wallet. build_fee_bump ( txid) . map_err ( |e| {
1119+ log_error ! ( self . logger, "BDK fee bump retry failed for {}: {:?}" , txid, e) ;
1120+ Error :: InvalidFeeRate
1121+ } ) ?;
1122+
1123+ builder. fee_rate ( required_fee_rate) ;
1124+ builder. finish ( ) . map_err ( |e| {
1125+ log_error ! (
1126+ self . logger,
1127+ "Failed to finish PSBT with required fee rate: {:?}" ,
1128+ e
1129+ ) ;
1130+ Error :: InvalidFeeRate
1131+ } )
1132+ } ,
1133+ Err ( e) => {
1134+ log_error ! ( self . logger, "Failed to create fee bump PSBT: {:?}" , e) ;
1135+ Err ( Error :: InvalidFeeRate )
1136+ } ,
1137+ } ?
1138+ } ;
1139+
1140+ match locked_wallet. sign ( & mut psbt, SignOptions :: default ( ) ) {
1141+ Ok ( finalized) => {
1142+ if !finalized {
1143+ return Err ( Error :: OnchainTxCreationFailed ) ;
1144+ }
1145+ } ,
1146+ Err ( err) => {
1147+ log_error ! ( self . logger, "Failed to create transaction: {}" , err) ;
1148+ return Err ( err. into ( ) ) ;
1149+ } ,
1150+ }
1151+
1152+ let mut locked_persister = self . persister . lock ( ) . unwrap ( ) ;
1153+ locked_wallet. persist ( & mut locked_persister) . map_err ( |e| {
1154+ log_error ! ( self . logger, "Failed to persist wallet: {}" , e) ;
1155+ Error :: PersistenceFailed
1156+ } ) ?;
1157+
1158+ let fee_bumped_tx = psbt. extract_tx ( ) . map_err ( |e| {
1159+ log_error ! ( self . logger, "Failed to extract transaction: {}" , e) ;
1160+ e
1161+ } ) ?;
1162+
1163+ let new_txid = fee_bumped_tx. compute_txid ( ) ;
1164+
1165+ self . broadcaster . broadcast_transactions ( & [ (
1166+ & fee_bumped_tx,
1167+ lightning:: chain:: chaininterface:: TransactionType :: Sweep { channels : vec ! [ ] } ,
1168+ ) ] ) ;
1169+
1170+ let new_payment = self . create_payment_from_tx (
1171+ & locked_wallet,
1172+ new_txid,
1173+ payment. id ,
1174+ & fee_bumped_tx,
1175+ PaymentStatus :: Pending ,
1176+ ConfirmationStatus :: Unconfirmed ,
1177+ ) ;
1178+
1179+ let pending_payment_store =
1180+ self . create_pending_payment_from_tx ( new_payment. clone ( ) , Vec :: new ( ) ) ;
1181+
1182+ self . pending_payment_store . insert_or_update ( pending_payment_store) ?;
1183+ self . payment_store . insert_or_update ( new_payment) ?;
1184+
1185+ log_info ! ( self . logger, "RBF successful: replaced {} with {}" , txid, new_txid) ;
1186+
1187+ Ok ( new_txid)
1188+ }
10081189}
10091190
10101191impl Listen for Wallet {
0 commit comments