@@ -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 } ;
@@ -259,16 +262,23 @@ impl Wallet {
259262 confirmation_status,
260263 ) ;
261264
262- let pending_payment =
263- self . create_pending_payment_from_tx ( payment. clone ( ) , Vec :: new ( ) ) ;
265+ self . payment_store . insert_or_update ( payment. clone ( ) ) ?;
264266
265- self . payment_store . insert_or_update ( payment) ?;
266- self . pending_payment_store . insert_or_update ( pending_payment) ?;
267+ if payment_status == PaymentStatus :: Pending {
268+ let pending_payment =
269+ self . create_pending_payment_from_tx ( payment, Vec :: new ( ) ) ;
270+
271+ self . pending_payment_store . insert_or_update ( pending_payment) ?;
272+ }
267273 } ,
268274 WalletEvent :: ChainTipChanged { new_tip, .. } => {
269- // Get all on-chain payments that are Pending
270275 let pending_payments: Vec < PendingPaymentDetails > =
271276 self . pending_payment_store . list_filter ( |p| {
277+ debug_assert ! (
278+ p. details. status == PaymentStatus :: Pending ,
279+ "Non-pending payment {:?} found in pending store" ,
280+ p. details. id,
281+ ) ;
272282 p. details . status == PaymentStatus :: Pending
273283 && matches ! ( p. details. kind, PaymentKind :: Onchain { .. } )
274284 } ) ;
@@ -286,6 +296,11 @@ impl Wallet {
286296 payment. details . status = PaymentStatus :: Succeeded ;
287297 self . payment_store . insert_or_update ( payment. details ) ?;
288298 self . pending_payment_store . remove ( & payment_id) ?;
299+ debug_assert ! (
300+ !self . pending_payment_store. contains_key( & payment_id) ,
301+ "Payment {:?} still in pending store after removal" ,
302+ payment_id,
303+ ) ;
289304 }
290305 } ,
291306 PaymentKind :: Onchain {
@@ -307,7 +322,16 @@ impl Wallet {
307322 . collect ( ) ;
308323
309324 if !txs_to_broadcast. is_empty ( ) {
310- let tx_refs: Vec < & Transaction > = txs_to_broadcast. iter ( ) . collect ( ) ;
325+ let tx_refs: Vec < (
326+ & Transaction ,
327+ lightning:: chain:: chaininterface:: TransactionType ,
328+ ) > =
329+ txs_to_broadcast
330+ . iter ( )
331+ . map ( |tx| {
332+ ( tx, lightning:: chain:: chaininterface:: TransactionType :: Sweep { channels : vec ! [ ] } )
333+ } )
334+ . collect ( ) ;
311335 self . broadcaster . broadcast_transactions ( & tx_refs) ;
312336 log_info ! (
313337 self . logger,
@@ -335,7 +359,7 @@ impl Wallet {
335359 self . payment_store . insert_or_update ( payment) ?;
336360 self . pending_payment_store . insert_or_update ( pending_payment) ?;
337361 } ,
338- WalletEvent :: TxReplaced { txid, conflicts, tx , .. } => {
362+ WalletEvent :: TxReplaced { txid, conflicts, .. } => {
339363 let payment_id = self
340364 . find_payment_by_txid ( txid)
341365 . unwrap_or_else ( || PaymentId ( txid. to_byte_array ( ) ) ) ;
@@ -344,14 +368,14 @@ impl Wallet {
344368 let conflict_txids: Vec < Txid > =
345369 conflicts. iter ( ) . map ( |( _, conflict_txid) | * conflict_txid) . collect ( ) ;
346370
347- let payment = self . create_payment_from_tx (
348- locked_wallet,
349- txid,
371+ // We fetch payment details here since the replacement has updated the stored state
372+ debug_assert ! (
373+ self . payment_store. get( & payment_id) . is_some( ) ,
374+ "Payment {:?} expected in store during WalletEvent::TxReplaced but not found" ,
350375 payment_id,
351- & tx,
352- PaymentStatus :: Pending ,
353- ConfirmationStatus :: Unconfirmed ,
354376 ) ;
377+ let payment =
378+ self . payment_store . get ( & payment_id) . ok_or ( Error :: InvalidPaymentId ) ?;
355379 let pending_payment_details = self
356380 . create_pending_payment_from_tx ( payment. clone ( ) , conflict_txids. clone ( ) ) ;
357381
@@ -1026,6 +1050,163 @@ impl Wallet {
10261050
10271051 None
10281052 }
1053+
1054+ #[ allow( deprecated) ]
1055+ pub ( crate ) fn bump_fee_rbf (
1056+ & self , payment_id : PaymentId , fee_rate : Option < FeeRate > ,
1057+ ) -> Result < Txid , Error > {
1058+ let payment = self . payment_store . get ( & payment_id) . ok_or ( Error :: InvalidPaymentId ) ?;
1059+
1060+ let mut locked_wallet = self . inner . lock ( ) . unwrap ( ) ;
1061+
1062+ if let PaymentKind :: Onchain { status, .. } = & payment. kind {
1063+ match status {
1064+ ConfirmationStatus :: Confirmed { .. } => {
1065+ log_error ! (
1066+ self . logger,
1067+ "Transaction {} is already confirmed and cannot be fee bumped" ,
1068+ payment_id
1069+ ) ;
1070+ return Err ( Error :: InvalidPaymentId ) ;
1071+ } ,
1072+ ConfirmationStatus :: Unconfirmed => { } ,
1073+ }
1074+ }
1075+
1076+ if payment. direction != PaymentDirection :: Outbound {
1077+ log_error ! ( self . logger, "Transaction {} is not an outbound payment" , payment_id) ;
1078+ return Err ( Error :: InvalidPaymentId ) ;
1079+ }
1080+
1081+ let txid = match & payment. kind {
1082+ PaymentKind :: Onchain { txid, .. } => * txid,
1083+ _ => return Err ( Error :: InvalidPaymentId ) ,
1084+ } ;
1085+
1086+ debug_assert ! (
1087+ locked_wallet. tx_details( txid) . is_some( ) ,
1088+ "Transaction {} expected in wallet but not found" ,
1089+ txid,
1090+ ) ;
1091+ let old_tx =
1092+ locked_wallet. tx_details ( txid) . ok_or ( Error :: InvalidPaymentId ) ?. tx . deref ( ) . clone ( ) ;
1093+
1094+ let old_fee_rate = locked_wallet. calculate_fee_rate ( & old_tx) . map_err ( |e| {
1095+ log_error ! ( self . logger, "Failed to calculate fee rate of transaction {}: {}" , txid, e) ;
1096+ Error :: InvalidPaymentId
1097+ } ) ?;
1098+ let old_fee_rate_sat_per_kwu = old_fee_rate. to_sat_per_kwu ( ) ;
1099+
1100+ // BIP 125 requires the replacement to pay a higher fee rate than the original.
1101+ // The minimum increase is the incremental relay fee.
1102+ let min_required_fee_rate_sat_per_kwu =
1103+ old_fee_rate_sat_per_kwu + INCREMENTAL_RELAY_FEE_SAT_PER_1000_WEIGHT as u64 ;
1104+
1105+ let confirmation_target = ConfirmationTarget :: OnchainPayment ;
1106+ let estimated_fee_rate =
1107+ fee_rate. unwrap_or_else ( || self . fee_estimator . estimate_fee_rate ( confirmation_target) ) ;
1108+
1109+ // Use the higher of minimum RBF requirement or current network estimate
1110+ let final_fee_rate_sat_per_kwu =
1111+ min_required_fee_rate_sat_per_kwu. max ( estimated_fee_rate. to_sat_per_kwu ( ) ) ;
1112+ let final_fee_rate = FeeRate :: from_sat_per_kwu ( final_fee_rate_sat_per_kwu) ;
1113+
1114+ let mut psbt = {
1115+ let mut builder = locked_wallet. build_fee_bump ( txid) . map_err ( |e| {
1116+ log_error ! ( self . logger, "BDK fee bump failed for {}: {:?}" , txid, e) ;
1117+ match e {
1118+ BuildFeeBumpError :: TransactionNotFound ( _) => Error :: InvalidPaymentId ,
1119+ BuildFeeBumpError :: TransactionConfirmed ( _) => {
1120+ log_error ! ( self . logger, "Payment {} is already confirmed" , payment_id) ;
1121+ Error :: InvalidPaymentId
1122+ } ,
1123+ BuildFeeBumpError :: IrreplaceableTransaction ( _) => {
1124+ Error :: OnchainTxCreationFailed
1125+ } ,
1126+ BuildFeeBumpError :: FeeRateUnavailable => Error :: FeerateEstimationUpdateFailed ,
1127+ BuildFeeBumpError :: UnknownUtxo ( _) => Error :: OnchainTxCreationFailed ,
1128+ BuildFeeBumpError :: InvalidOutputIndex ( _) => Error :: OnchainTxCreationFailed ,
1129+ }
1130+ } ) ?;
1131+
1132+ builder. fee_rate ( final_fee_rate) ;
1133+
1134+ match builder. finish ( ) {
1135+ Ok ( psbt) => Ok ( psbt) ,
1136+ Err ( CreateTxError :: FeeRateTooLow { required : required_fee_rate } ) => {
1137+ log_info ! ( self . logger, "BDK requires higher fee rate: {}" , required_fee_rate) ;
1138+
1139+ let mut builder = locked_wallet. build_fee_bump ( txid) . map_err ( |e| {
1140+ log_error ! ( self . logger, "BDK fee bump retry failed for {}: {:?}" , txid, e) ;
1141+ Error :: InvalidFeeRate
1142+ } ) ?;
1143+
1144+ builder. fee_rate ( required_fee_rate) ;
1145+ builder. finish ( ) . map_err ( |e| {
1146+ log_error ! (
1147+ self . logger,
1148+ "Failed to finish PSBT with required fee rate: {:?}" ,
1149+ e
1150+ ) ;
1151+ Error :: InvalidFeeRate
1152+ } )
1153+ } ,
1154+ Err ( e) => {
1155+ log_error ! ( self . logger, "Failed to create fee bump PSBT: {:?}" , e) ;
1156+ Err ( Error :: InvalidFeeRate )
1157+ } ,
1158+ } ?
1159+ } ;
1160+
1161+ match locked_wallet. sign ( & mut psbt, SignOptions :: default ( ) ) {
1162+ Ok ( finalized) => {
1163+ if !finalized {
1164+ return Err ( Error :: OnchainTxCreationFailed ) ;
1165+ }
1166+ } ,
1167+ Err ( err) => {
1168+ log_error ! ( self . logger, "Failed to create transaction: {}" , err) ;
1169+ return Err ( err. into ( ) ) ;
1170+ } ,
1171+ }
1172+
1173+ let mut locked_persister = self . persister . lock ( ) . unwrap ( ) ;
1174+ locked_wallet. persist ( & mut locked_persister) . map_err ( |e| {
1175+ log_error ! ( self . logger, "Failed to persist wallet: {}" , e) ;
1176+ Error :: PersistenceFailed
1177+ } ) ?;
1178+
1179+ let fee_bumped_tx = psbt. extract_tx ( ) . map_err ( |e| {
1180+ log_error ! ( self . logger, "Failed to extract transaction: {}" , e) ;
1181+ e
1182+ } ) ?;
1183+
1184+ let new_txid = fee_bumped_tx. compute_txid ( ) ;
1185+
1186+ self . broadcaster . broadcast_transactions ( & [ (
1187+ & fee_bumped_tx,
1188+ lightning:: chain:: chaininterface:: TransactionType :: Sweep { channels : vec ! [ ] } ,
1189+ ) ] ) ;
1190+
1191+ let new_payment = self . create_payment_from_tx (
1192+ & locked_wallet,
1193+ new_txid,
1194+ payment. id ,
1195+ & fee_bumped_tx,
1196+ PaymentStatus :: Pending ,
1197+ ConfirmationStatus :: Unconfirmed ,
1198+ ) ;
1199+
1200+ let pending_payment_store =
1201+ self . create_pending_payment_from_tx ( new_payment. clone ( ) , Vec :: new ( ) ) ;
1202+
1203+ self . pending_payment_store . insert_or_update ( pending_payment_store) ?;
1204+ self . payment_store . insert_or_update ( new_payment) ?;
1205+
1206+ log_info ! ( self . logger, "RBF successful: replaced {} with {}" , txid, new_txid) ;
1207+
1208+ Ok ( new_txid)
1209+ }
10291210}
10301211
10311212impl Listen for Wallet {
0 commit comments