clementine_tx_sender/
rbf.rs

1use crate::{log_error_for_tx, TxSender, TxSenderDatabase, TxSenderSigner, TxSenderTxBuilder};
2use bitcoin::script::Instruction;
3use bitcoin::sighash::{Prevouts, SighashCache};
4use bitcoin::taproot::{self};
5use bitcoin::{consensus, Address, Amount, FeeRate, Transaction};
6use bitcoin::{Psbt, TapSighashType, TxOut, Txid, Witness};
7use bitcoincore_rpc::json::{
8    BumpFeeOptions, BumpFeeResult, CreateRawTransactionInput, FinalizePsbtResult,
9    WalletCreateFundedPsbtOutput, WalletCreateFundedPsbtOutputs, WalletCreateFundedPsbtResult,
10};
11use bitcoincore_rpc::RpcApi;
12use clementine_errors::SendTxError;
13use clementine_utils::sign::TapTweakData;
14use clementine_utils::{RbfSigningInfo, TxMetadata};
15use eyre::Context;
16use eyre::{eyre, OptionExt};
17use std::str::FromStr;
18
19use super::Result;
20
21impl<S, D, B> TxSender<S, D, B>
22where
23    S: TxSenderSigner,
24    D: TxSenderDatabase,
25    B: TxSenderTxBuilder,
26{
27    /// Calculates the appropriate fee rate for a Replace-By-Fee (RBF) transaction.
28    ///
29    /// This method determines the effective fee rate needed to successfully replace
30    /// an existing transaction in the mempool. It follows Bitcoin's RBF rules by:
31    ///
32    /// 1. Retrieving the original transaction and calculating its current fee rate
33    /// 2. Ensuring the new fee rate is higher than the original by at least the minimum
34    ///    required incremental relay fee
35    /// 3. Comparing the calculated minimum bump fee rate with the requested target fee rate
36    ///    and selecting the higher of the two
37    ///
38    /// # Arguments
39    /// * `txid` - The transaction ID of the original transaction to be replaced
40    /// * `new_feerate` - The target fee rate requested for the replacement transaction
41    ///
42    /// # Returns
43    /// * `Ok(Some(FeeRate))` - The effective fee rate (in satoshis per kilo-wu) to use for the replacement
44    /// * `Ok(None)` - If the original transaction already has a higher fee rate than requested
45    /// * `Err(...)` - If there was an error retrieving or analyzing the original transaction
46    pub async fn calculate_bump_feerate_if_needed(
47        &self,
48        txid: &Txid,
49        new_feerate: FeeRate,
50    ) -> Result<Option<FeeRate>> {
51        let original_tx = self.rpc.get_tx_of_txid(txid).await.map_err(|e| eyre!(e))?;
52
53        // Calculate original tx fee
54        let original_tx_fee = self.get_tx_fee(&original_tx).await.map_err(|e| eyre!(e))?;
55
56        let original_tx_weight = original_tx.weight();
57
58        // Original fee rate calculation according to Bitcoin Core
59        // In Rust Bitcoin, the calculations are done in sat/kwu
60        // so some precision is lost. https://github.com/bitcoin/bitcoin/blob/a33bd767a37dccf39a094d03c2f62ea81633410f/src/policy/feerate.cpp#L11
61        let original_feerate_sat_per_kwu = FeeRate::from_sat_per_kwu(
62            (original_tx_fee.to_sat() * 1000) / (original_tx_weight.to_vbytes_ceil() * 4),
63        );
64
65        // If original feerate is already higher than target, avoid bumping
66        if original_feerate_sat_per_kwu >= new_feerate {
67            return Ok(None);
68        }
69
70        // Get minimum fee increment rate from node for BIP125 compliance. Returned value is in BTC/kvB
71        let incremental_fee_rate = self
72            .rpc
73            .get_network_info()
74            .await
75            .map_err(|e| eyre!(e))?
76            .incremental_fee;
77        let incremental_fee_rate_sat_per_kvb = incremental_fee_rate.to_sat();
78        let incremental_fee_rate = FeeRate::from_sat_per_kwu(incremental_fee_rate_sat_per_kvb / 4);
79
80        // Use max of target fee rate and original + minimum fee increment rate.
81        let min_bump_feerate =
82            original_feerate_sat_per_kwu.to_sat_per_kwu() + incremental_fee_rate.to_sat_per_kwu();
83
84        let effective_feerate_sat_per_kwu =
85            std::cmp::max(new_feerate.to_sat_per_kwu(), min_bump_feerate);
86
87        Ok(Some(FeeRate::from_sat_per_kwu(
88            effective_feerate_sat_per_kwu,
89        )))
90    }
91
92    pub async fn fill_in_utxo_info(&self, psbt: &mut String) -> Result<()> {
93        let mut decoded_psbt = Psbt::from_str(psbt).map_err(|e| eyre!(e))?;
94        let tx = decoded_psbt.unsigned_tx.clone();
95
96        for (idx, input) in tx.input.iter().enumerate() {
97            let utxo = self
98                .rpc
99                .get_tx_out(
100                    &input.previous_output.txid,
101                    input.previous_output.vout,
102                    Some(false),
103                )
104                .await
105                .wrap_err("Failed to get UTXO info")?;
106
107            if let Some(utxo) = utxo {
108                decoded_psbt.inputs[idx].witness_utxo = Some(TxOut {
109                    value: utxo.value,
110                    script_pubkey: utxo
111                        .script_pub_key
112                        .script()
113                        .wrap_err("Failed to get script pubkey")?,
114                });
115            }
116        }
117
118        *psbt = decoded_psbt.to_string();
119
120        Ok(())
121    }
122
123    /// Given a PSBT with inputs, fill in the existing witnesses from the original tx
124    /// This allows us to create a finalized PSBT if
125    /// the original tx had SinglePlusAnyoneCanPay signatures.  If the original
126    /// tx did not have S+AP, these signatures will be added. The expected behavior is for them to be replaced using RbfSigningInfo.
127    ///
128    /// # Returns
129    /// The PSBT as a base64-encoded string.
130    pub async fn copy_witnesses(&self, psbt: String, initial_tx: &Transaction) -> Result<String> {
131        let mut decoded_psbt = Psbt::from_str(&psbt).map_err(|e| eyre!(e))?;
132
133        for (idx, input) in initial_tx.input.iter().enumerate() {
134            if let Some(sig) = input.witness.nth(0) {
135                if sig.len() == 65 && sig[64] == 0x83 {
136                    // This is a S+AP signature, copy it over
137                    decoded_psbt.inputs[idx].final_script_witness = Some(input.witness.clone());
138                }
139            }
140        }
141
142        Ok(decoded_psbt.to_string())
143    }
144
145    pub async fn create_funded_psbt(
146        &self,
147        tx: &Transaction,
148        fee_rate: FeeRate,
149    ) -> Result<WalletCreateFundedPsbtResult> {
150        // We need to carefully calculate the witness weight and factor that
151        // into the fee rate because wallet_create_funded_psbt does not factor
152        // in witnesses.
153
154        // The scaleup factor is the ratio of the total weight to the base weight
155        // The walletcreatefundedpsbt will use the base weight to calculate the fee
156        // and we'll scale up the fee rate by the scaleup factor to achieve our desired fee
157        let witness_scaleup = tx.weight().to_wu() as f64 / (tx.base_size() * 4) as f64;
158
159        let adjusted_fee_rate = FeeRate::from_sat_per_kwu(
160            (fee_rate.to_sat_per_kwu() as f64 * witness_scaleup).ceil() as u64,
161        );
162
163        // 1. Create a funded PSBT using the wallet
164        let create_psbt_opts = bitcoincore_rpc::json::WalletCreateFundedPsbtOptions {
165            add_inputs: Some(true), // Let the wallet add its inputs
166            change_address: None,
167            change_position: Some(tx.output.len() as u16), // Add change output at last index (so that SinglePlusAnyoneCanPay signatures stay valid)
168            change_type: None,
169            include_watching: None,
170            lock_unspent: None,
171            // Bitcoincore expects BTC/kvbyte for fee_rate
172            fee_rate: Some(
173                adjusted_fee_rate
174                    .fee_vb(1000)
175                    .ok_or_eyre("Failed to convert fee rate to BTC/kvbyte")?,
176            ),
177            subtract_fee_from_outputs: vec![],
178            replaceable: Some(true), // Mark as RBF enabled
179            conf_target: None,
180            estimate_mode: None,
181        };
182
183        let mut omitted = 0usize;
184        let filtered_outputs: Vec<WalletCreateFundedPsbtOutput> = tx
185            .output
186            .iter()
187            .filter_map(|out| {
188                if out.script_pubkey.is_op_return() {
189                    if let Some(Ok(Instruction::PushBytes(data))) =
190                        out.script_pubkey.instructions().last()
191                    {
192                        return Some(WalletCreateFundedPsbtOutput::OpReturn(
193                            data.as_bytes().to_vec(),
194                        ));
195                    }
196                }
197                let address = Address::from_script(
198                    &out.script_pubkey,
199                    self.protocol_paramset.network,
200                )
201                .map_err(|e| eyre!(e));
202                match address {
203                    Ok(address) => Some(WalletCreateFundedPsbtOutput::Spendable(
204                        address.to_string(),
205                        out.value,
206                    )),
207                    Err(err) => {
208                        tracing::error!(
209                            "Failed to get address from script for output of tx with txid {} for script: {}",
210                            tx.compute_txid(),
211                            err
212                        );
213                        omitted += 1;
214                        None
215                    }
216                }
217            })
218            .collect::<Vec<_>>();
219
220        if omitted > 0 {
221            return Err(eyre::eyre!("Failed to get address for outputs of tx with txid {} for {} outputs in create_funded_psbt", tx.compute_txid(), omitted).into());
222        }
223
224        let outputs = WalletCreateFundedPsbtOutputs(filtered_outputs);
225
226        self.rpc
227            .wallet_create_funded_psbt(
228                &tx.input
229                    .iter()
230                    .map(|inp| CreateRawTransactionInput {
231                        txid: inp.previous_output.txid,
232                        vout: inp.previous_output.vout,
233                        sequence: Some(inp.sequence.to_consensus_u32()),
234                    })
235                    .collect::<Vec<_>>(),
236                outputs,
237                None,
238                Some(create_psbt_opts),
239                None,
240            )
241            .await
242            .map_err(|e| eyre!(e).into())
243    }
244    /// Given a PSBT with inputs that've been signed by the wallet except for our new input,
245    /// we have to sign the first input with our self.signer actor.
246    ///
247    /// Assumes that the first input is the input with our key.
248    ///
249    /// # Returns
250    /// The signed PSBT as a base64-encoded string.
251    pub async fn attempt_sign_psbt(
252        &self,
253        psbt: String,
254        rbf_signing_info: RbfSigningInfo,
255    ) -> Result<String> {
256        // Parse the PSBT from string
257        let mut decoded_psbt = Psbt::from_str(&psbt).map_err(|e| eyre!(e))?;
258
259        // Ensure we have inputs to sign
260        if decoded_psbt.inputs.is_empty() {
261            return Err(eyre!("PSBT has no inputs to sign").into());
262        }
263
264        let input_index = rbf_signing_info.vout as usize;
265
266        // Get the transaction to calculate the sighash
267        let tx = decoded_psbt.unsigned_tx.clone();
268        let mut sighash_cache = SighashCache::new(&tx);
269
270        // Determine the sighash type (default to ALL if not specified)
271        let sighash_type = decoded_psbt.inputs[input_index]
272            .sighash_type
273            .unwrap_or((TapSighashType::Default).into());
274
275        // For Taproot key path spending
276        if let Ok(tap_sighash_type) = sighash_type.taproot_hash_ty() {
277            // Calculate the sighash for this input
278            // Extract previous outputs from the PSBT
279            let prevouts: Vec<bitcoin::TxOut> = decoded_psbt
280                .inputs
281                .iter()
282                .zip(tx.input.iter())
283                .map(|(psbt_input, tx_input)| {
284                    // Try witness_utxo first (for segwit inputs)
285                    if let Some(witness_utxo) = psbt_input.witness_utxo.clone() {
286                        Ok(witness_utxo)
287                    } else if let Some(ref non_witness_tx) = psbt_input.non_witness_utxo {
288                        // For non-segwit inputs, extract the output from the previous transaction
289                        let vout = tx_input.previous_output.vout as usize;
290                        non_witness_tx
291                            .output
292                            .get(vout)
293                            .cloned()
294                            .ok_or_eyre(format!(
295                                "Output index {vout} out of bounds in previous transaction",
296                            ))
297                            .map_err(SendTxError::Other)
298                    } else {
299                        Err(eyre!(
300                            "Neither witness_utxo nor non_witness_utxo found for input"
301                        ))
302                        .map_err(SendTxError::Other)
303                    }
304                })
305                .collect::<Result<Vec<_>>>()?;
306
307            let sighash = sighash_cache
308                .taproot_key_spend_signature_hash(
309                    input_index,
310                    &Prevouts::All(&prevouts),
311                    tap_sighash_type,
312                )
313                .map_err(|e| eyre!("Failed to calculate sighash: {}", e))?;
314
315            // Sign the sighash with our signer
316            let signature = self
317                .signer
318                .sign_with_tweak_data(
319                    sighash,
320                    TapTweakData::KeyPath(rbf_signing_info.tweak_merkle_root),
321                )
322                .map_err(|e| eyre!("Failed to sign input: {}", e))?;
323
324            // Add the signature to the PSBT
325            decoded_psbt.inputs[input_index].tap_key_sig = Some(taproot::Signature {
326                signature,
327                sighash_type: tap_sighash_type,
328            });
329
330            decoded_psbt.inputs[input_index].final_script_witness =
331                Some(Witness::from_slice(&[signature.serialize()]));
332
333            // Serialize the signed PSBT back to base64
334            Ok(decoded_psbt.to_string())
335        } else {
336            Err(eyre!("Only Taproot key path signing is currently supported").into())
337        }
338    }
339
340    #[track_caller]
341    pub fn handle_err(
342        &self,
343        err_msg: impl AsRef<str>,
344        err_state: impl Into<String>,
345        try_to_send_id: u32,
346    ) {
347        log_error_for_tx!(self.db, try_to_send_id, err_msg.as_ref());
348
349        let err_state = err_state.into();
350        let db = self.db.clone();
351
352        tokio::spawn(async move {
353            let _ = db
354                .update_tx_debug_sending_state(try_to_send_id, &err_state, true)
355                .await;
356        });
357    }
358
359    /// This function verifies that the wallet has added a funding input to the
360    /// PSBT.
361    ///
362    /// This is required for a transaction to be added to the wallet.
363    pub fn verify_new_inputs(&self, psbt: &str, original_tx: &Transaction) -> bool {
364        let Ok(psbt) = Psbt::from_str(psbt) else {
365            tracing::error!("Failed to parse PSBT");
366            return false;
367        };
368
369        psbt.inputs.len() > original_tx.input.len()
370    }
371
372    pub async fn get_tx_fee(&self, tx: &Transaction) -> Result<Amount> {
373        let inputs = {
374            let mut inputs = Amount::ZERO;
375            for inp in &tx.input {
376                inputs += self
377                    .rpc
378                    .get_txout_from_outpoint(&inp.previous_output)
379                    .await
380                    .map_err(|e| eyre!(e))?
381                    .value;
382            }
383            inputs
384        };
385        let outputs = tx.output.iter().map(|o| o.value).sum::<Amount>();
386
387        let tx_fee = inputs - outputs;
388
389        Ok(tx_fee)
390    }
391
392    /// Sends or bumps a transaction using the Replace-By-Fee (RBF) strategy.
393    ///
394    /// It interacts with the database to track the latest RBF attempt (`last_rbf_txid`).
395    ///
396    /// # Logic:
397    /// 1.  **Check for Existing RBF Tx:** Retrieves `last_rbf_txid` for the `try_to_send_id`.
398    /// 2.  **Bump Existing Tx:** If `psbt_bump_fee` exists, it calls `rpc.psbt_bump_fee`.
399    ///     - This internally uses the Bitcoin Core `psbtbumpfee` RPC.
400    ///     - We then sign the inputs that we can using our Actor and have the wallet sign the rest.
401    ///
402    /// 3.  **Send Initial RBF Tx:** If no `last_rbf_txid` exists (first attempt):
403    ///     - It uses `fund_raw_transaction` RPC to let the wallet add (potentially) inputs,
404    ///       outputs, set the fee according to `fee_rate`, and mark the transaction as replaceable.
405    ///     - Uses `sign_raw_transaction_with_wallet` RPC to sign the funded transaction.
406    ///     - Uses `send_raw_transaction` RPC to broadcast the initial RBF transaction.
407    ///     - Saves the resulting `txid` to the database as the `last_rbf_txid`.
408    ///
409    /// # Arguments
410    /// * `try_to_send_id` - The database ID tracking this send attempt.
411    /// * `tx` - The original transaction intended for RBF (used only on the first attempt).
412    /// * `tx_metadata` - Optional metadata associated with the transaction.
413    /// * `fee_rate` - The target fee rate for the RBF replacement.
414    #[tracing::instrument(skip_all, fields(try_to_send_id, tx_meta=?tx_metadata))]
415    pub async fn send_rbf_tx(
416        &self,
417        try_to_send_id: u32,
418        tx: Transaction,
419        tx_metadata: Option<TxMetadata>,
420        fee_rate: FeeRate,
421        rbf_signing_info: Option<RbfSigningInfo>,
422        current_tip_height: u32,
423    ) -> Result<()> {
424        tracing::debug!(?tx_metadata, "Sending RBF tx",);
425
426        tracing::debug!(?try_to_send_id, "Attempting to send.");
427
428        let _ = self
429            .db
430            .update_tx_debug_sending_state(try_to_send_id, "preparing_rbf", true)
431            .await;
432
433        let mut dbtx = self
434            .db
435            .begin_transaction()
436            .await
437            .wrap_err("Failed to begin database transaction")?;
438
439        let last_rbf_txid = self
440            .db
441            .get_last_rbf_txid(Some(&mut dbtx), try_to_send_id)
442            .await
443            .wrap_err("Failed to get last RBF txid")?;
444
445        let effective_feerate = if let Some(last_rbf_txid) = last_rbf_txid {
446            tracing::debug!(
447                ?try_to_send_id,
448                "Attempting to bump fee for txid {last_rbf_txid} using psbt_bump_fee"
449            );
450
451            let effective_feerate = self
452                .calculate_bump_feerate_if_needed(&last_rbf_txid, fee_rate)
453                .await?;
454
455            let Some(effective_feerate) = effective_feerate else {
456                tracing::debug!(
457                    ?try_to_send_id,
458                    "Original tx feerate already higher than target ({} sat/vB), skipping bump",
459                    fee_rate.to_sat_per_vb_ceil()
460                );
461                return Ok(());
462            };
463
464            let psbt_bump_opts = BumpFeeOptions {
465                conf_target: None, // Use fee_rate instead
466                fee_rate: Some(bitcoincore_rpc::json::FeeRate::per_vbyte(Amount::from_sat(
467                    effective_feerate.to_sat_per_vb_ceil(),
468                ))),
469                replaceable: Some(true), // Ensure the bumped tx is also replaceable
470                estimate_mode: None,
471            };
472
473            let bump_result = self
474                .rpc
475                .psbt_bump_fee(&last_rbf_txid, Some(&psbt_bump_opts))
476                .await;
477
478            let mut bumped_psbt = match bump_result {
479                Err(e) => {
480                    // Check for common errors indicating the tx is already confirmed or spent
481                    let rpc_error_str = e.to_string();
482                    if rpc_error_str.contains("Transaction already in block chain") {
483                        tracing::debug!(
484                            ?try_to_send_id,
485                            "RBF bump failed for {last_rbf_txid}, likely confirmed or spent: {e}"
486                        );
487                        // No need to return error, just log and proceed
488                        self.db.commit_transaction(dbtx).await.wrap_err(
489                            "Failed to commit database transaction after failed bump check",
490                        )?;
491                        return Ok(());
492                    } else {
493                        // Other potentially transient errors
494                        let error_message = format!("psbt_bump_fee failed: {e}");
495                        log_error_for_tx!(self.db, try_to_send_id, error_message);
496                        let _ = self
497                            .db
498                            .update_tx_debug_sending_state(
499                                try_to_send_id,
500                                "rbf_psbt_bump_failed",
501                                true,
502                            )
503                            .await;
504                        tracing::warn!(?try_to_send_id, "psbt_bump_fee failed: {e:?}");
505                        return Err(SendTxError::Other(eyre!(e)));
506                    }
507                }
508                Ok(BumpFeeResult {
509                    psbt: Some(psbt), ..
510                }) => psbt,
511                Ok(BumpFeeResult { errors, .. }) if !errors.is_empty() => {
512                    self.handle_err(
513                        format!("psbt_bump_fee failed: {errors:?}"),
514                        "rbf_psbt_bump_failed",
515                        try_to_send_id,
516                    );
517                    return Err(SendTxError::Other(eyre!(errors.join(", "))));
518                }
519                Ok(BumpFeeResult { psbt: None, .. }) => {
520                    self.handle_err(
521                        "psbt_bump_fee returned no psbt",
522                        "rbf_psbt_bump_failed",
523                        try_to_send_id,
524                    );
525                    return Err(SendTxError::Other(eyre!("psbt_bump_fee returned no psbt")));
526                }
527            };
528
529            self.fill_in_utxo_info(&mut bumped_psbt)
530                .await
531                .map_err(|err| {
532                    let err = eyre!(err).wrap_err("Failed to fill in utxo info");
533                    self.handle_err(
534                        format!("{err:?}"),
535                        "rbf_fill_in_utxo_info_failed",
536                        try_to_send_id,
537                    );
538
539                    err
540                })?;
541
542            let bumped_psbt = self
543                .copy_witnesses(bumped_psbt, &tx)
544                .await
545                .wrap_err("Failed to fill SAP signatures")?;
546
547            // Wallet first pass
548            // We rely on the node's wallet here because psbt_bump_fee might add inputs from it.
549            let process_result = self
550                .rpc
551                .wallet_process_psbt(&bumped_psbt, Some(true), None, None) // sign=true
552                .await;
553
554            let processed_psbt = match process_result {
555                Ok(res) if res.complete => res.psbt,
556                // attempt to sign
557                Ok(res) => {
558                    let Some(rbf_signing_info) = rbf_signing_info else {
559                        return Err(eyre!(
560                            "RBF signing info is required for non SighashSingle RBF txs"
561                        )
562                        .into());
563                    };
564                    self.attempt_sign_psbt(res.psbt, rbf_signing_info).await?
565                }
566                Err(e) => {
567                    let err_msg = format!("wallet_process_psbt error: {e}");
568                    tracing::warn!(?try_to_send_id, "{}", err_msg);
569                    log_error_for_tx!(self.db, try_to_send_id, err_msg);
570                    let _ = self
571                        .db
572                        .update_tx_debug_sending_state(try_to_send_id, "rbf_psbt_sign_failed", true)
573                        .await;
574                    return Err(SendTxError::Other(eyre!(e)));
575                }
576            };
577
578            // Finalize the PSBT
579            let finalize_result = self
580                .rpc
581                .finalize_psbt(&processed_psbt, None) // extract=true by default
582                .await;
583
584            let final_tx_hex = match finalize_result {
585                Ok(FinalizePsbtResult {
586                    hex: Some(hex),
587                    complete: true,
588                    ..
589                }) => hex,
590                Ok(res) => {
591                    let err_msg = format!("Could not finalize PSBT: {res:?}");
592                    log_error_for_tx!(self.db, try_to_send_id, err_msg);
593
594                    let _ = self
595                        .db
596                        .update_tx_debug_sending_state(
597                            try_to_send_id,
598                            "rbf_psbt_finalize_incomplete",
599                            true,
600                        )
601                        .await;
602                    return Err(SendTxError::PsbtError(err_msg));
603                }
604                Err(e) => {
605                    log_error_for_tx!(
606                        self.db,
607                        try_to_send_id,
608                        format!("finalize_psbt error: {}", e)
609                    );
610                    let _ = self
611                        .db
612                        .update_tx_debug_sending_state(
613                            try_to_send_id,
614                            "rbf_psbt_finalize_failed",
615                            true,
616                        )
617                        .await;
618                    return Err(SendTxError::Other(eyre!(e)));
619                }
620            };
621
622            // Deserialize final tx to get txid
623            let final_tx: Transaction = match consensus::deserialize(&final_tx_hex) {
624                Ok(tx) => tx,
625                Err(e) => {
626                    log_error_for_tx!(
627                        self.db,
628                        try_to_send_id,
629                        format!("Failed to deserialize final RBF tx hex: {}", e)
630                    );
631                    return Err(SendTxError::Other(eyre!(e)));
632                }
633            };
634            let bumped_txid = final_tx.compute_txid();
635
636            // Broadcast the finalized transaction
637            let sent_txid = match self.rpc.send_raw_transaction(&final_tx).await {
638                Ok(sent_txid) if sent_txid == bumped_txid => sent_txid,
639                Ok(other_txid) => {
640                    log_error_for_tx!(
641                        self.db,
642                        try_to_send_id,
643                        format!(
644                            "send_raw_transaction returned unexpected txid {} (expected {})",
645                            other_txid, bumped_txid
646                        )
647                    );
648                    let _ = self
649                        .db
650                        .update_tx_debug_sending_state(
651                            try_to_send_id,
652                            "rbf_send_txid_mismatch",
653                            true,
654                        )
655                        .await;
656                    return Err(SendTxError::Other(eyre!(
657                        "send_raw_transaction returned unexpected txid"
658                    )));
659                }
660                Err(e) => {
661                    log_error_for_tx!(
662                        self.db,
663                        try_to_send_id,
664                        format!("send_raw_transaction error for bumped RBF tx: {}", e)
665                    );
666                    let _ = self
667                        .db
668                        .update_tx_debug_sending_state(try_to_send_id, "rbf_bump_send_failed", true)
669                        .await;
670                    return Err(SendTxError::Other(eyre!(e)));
671                }
672            };
673
674            tracing::debug!(
675                ?try_to_send_id,
676                "RBF tx {last_rbf_txid} successfully bumped and sent as {sent_txid}"
677            );
678
679            let _ = self
680                .db
681                .update_tx_debug_sending_state(try_to_send_id, "rbf_bumped_sent", true)
682                .await;
683
684            self.db
685                .save_rbf_txid(Some(&mut dbtx), try_to_send_id, sent_txid)
686                .await
687                .wrap_err("Failed to save new RBF txid after bump")?;
688
689            effective_feerate
690        } else {
691            tracing::debug!(
692                ?try_to_send_id,
693                "Funding initial RBF tx using PSBT workflow"
694            );
695
696            let _ = self
697                .db
698                .update_tx_debug_sending_state(try_to_send_id, "creating_initial_rbf_psbt", true)
699                .await;
700
701            let create_result = self
702                .create_funded_psbt(&tx, fee_rate)
703                .await
704                .map_err(|err| {
705                    let err = eyre!(err).wrap_err("Failed to create funded PSBT");
706                    self.handle_err(format!("{err:?}"), "rbf_psbt_create_failed", try_to_send_id);
707
708                    err
709                })?;
710
711            if !self.verify_new_inputs(&create_result.psbt, &tx) {
712                tracing::warn!(
713                    ?try_to_send_id,
714                    "Transaction has not been funded and is being sent as is. This transaction will have to be manually bumped as the wallet will not add it to itself."
715                );
716            }
717
718            // replace locktime and version
719            let mut psbt = Psbt::from_str(&create_result.psbt).map_err(|e| eyre!(e))?;
720            psbt.unsigned_tx.lock_time = tx.lock_time;
721            psbt.unsigned_tx.version = tx.version;
722
723            tracing::debug!(
724                try_to_send_id,
725                "Successfully created initial RBF PSBT with fee {}",
726                create_result.fee
727            );
728
729            let mut psbt = psbt.to_string();
730
731            self.fill_in_utxo_info(&mut psbt).await.map_err(|err| {
732                let err = eyre!(err).wrap_err("Failed to fill in utxo info");
733                self.handle_err(
734                    format!("{err:?}"),
735                    "rbf_fill_in_utxo_info_failed",
736                    try_to_send_id,
737                );
738
739                err
740            })?;
741
742            psbt = self.copy_witnesses(psbt, &tx).await.map_err(|err| {
743                let err = eyre!(err).wrap_err("Failed to copy witnesses");
744                self.handle_err(
745                    format!("{err:?}"),
746                    "rbf_copy_witnesses_failed",
747                    try_to_send_id,
748                );
749
750                err
751            })?;
752
753            // 2. Process the PSBT (let the wallet sign its inputs)
754            let process_result = self
755                .rpc
756                .wallet_process_psbt(&psbt, Some(true), None, None)
757                .await
758                .map_err(|err| {
759                    let err = eyre!(err).wrap_err("Failed to process initial RBF PSBT");
760                    self.handle_err(
761                        format!("{err:?}"),
762                        "rbf_psbt_process_failed",
763                        try_to_send_id,
764                    );
765
766                    err
767                })?;
768
769            if let Some(rbf_signing_info) = rbf_signing_info {
770                psbt = self
771                    .attempt_sign_psbt(process_result.psbt, rbf_signing_info)
772                    .await
773                    .map_err(|err| {
774                        let err = eyre!(err).wrap_err("Failed to sign initial RBF PSBT");
775                        self.handle_err(format!("{err:?}"), "rbf_psbt_sign_failed", try_to_send_id);
776
777                        err
778                    })?;
779            } else {
780                psbt = process_result.psbt;
781            }
782
783            tracing::debug!(try_to_send_id, "Successfully processed initial RBF PSBT");
784
785            let final_tx = {
786                // Extract tx
787                let psbt = Psbt::from_str(&psbt).map_err(|e| eyre!(e)).map_err(|err| {
788                    let err = eyre!(err).wrap_err("Failed to deserialize initial RBF PSBT");
789                    self.handle_err(
790                        format!("{err:?}"),
791                        "rbf_psbt_deserialize_failed",
792                        try_to_send_id,
793                    );
794                    err
795                })?;
796
797                let mut tx = psbt.unsigned_tx.clone();
798
799                for (idx, input) in tx.input.iter_mut().enumerate() {
800                    if let Some(witness) = psbt.inputs[idx].final_script_witness.clone() {
801                        input.witness = witness;
802                    }
803                    if let Some(sig) = psbt.inputs[idx].final_script_sig.clone() {
804                        input.script_sig = sig;
805                    }
806                }
807
808                tx
809            };
810
811            let initial_txid = final_tx.compute_txid();
812
813            // 4. Broadcast the finalized transaction
814            let sent_txid = match self.rpc.send_raw_transaction(&final_tx).await {
815                Ok(sent_txid) => {
816                    if sent_txid != initial_txid {
817                        let err_msg = format!(
818                            "send_raw_transaction returned unexpected txid {sent_txid} (expected {initial_txid}) for initial RBF",
819                        );
820                        log_error_for_tx!(self.db, try_to_send_id, err_msg);
821                        let _ = self
822                            .db
823                            .update_tx_debug_sending_state(
824                                try_to_send_id,
825                                "rbf_initial_send_txid_mismatch",
826                                true,
827                            )
828                            .await;
829                        return Err(SendTxError::Other(eyre!(err_msg)));
830                    }
831                    tracing::debug!(
832                        try_to_send_id,
833                        "Successfully sent initial RBF tx with txid {sent_txid}"
834                    );
835                    sent_txid
836                }
837                Err(e) => {
838                    tracing::error!("RBF failed for: {:?}", final_tx);
839                    let err_msg = format!("send_raw_transaction error for initial RBF tx: {e}");
840                    log_error_for_tx!(self.db, try_to_send_id, err_msg);
841                    let _ = self
842                        .db
843                        .update_tx_debug_sending_state(
844                            try_to_send_id,
845                            "rbf_initial_send_failed",
846                            true,
847                        )
848                        .await;
849                    return Err(SendTxError::Other(eyre!(e)));
850                }
851            };
852
853            // Update debug sending state
854            let _ = self
855                .db
856                .update_tx_debug_sending_state(try_to_send_id, "rbf_initial_sent", true)
857                .await;
858
859            self.db
860                .save_rbf_txid(Some(&mut dbtx), try_to_send_id, sent_txid)
861                .await
862                .wrap_err("Failed to save initial RBF txid")?;
863
864            fee_rate
865        };
866
867        self.db
868            .update_effective_fee_rate(
869                Some(&mut dbtx),
870                try_to_send_id,
871                effective_feerate,
872                current_tip_height,
873            )
874            .await
875            .wrap_err("Failed to update effective fee rate")?;
876
877        self.db
878            .commit_transaction(dbtx)
879            .await
880            .wrap_err("Failed to commit database transaction")?;
881
882        Ok(())
883    }
884}