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        //tracing::debug!("original_tx_fee: {}", original_tx_fee);
57
58        let original_tx_weight = original_tx.weight();
59
60        // Original fee rate calculation according to Bitcoin Core
61        // In Rust Bitcoin, the calculations are done in sat/kwu
62        // so some precision is lost. https://github.com/bitcoin/bitcoin/blob/a33bd767a37dccf39a094d03c2f62ea81633410f/src/policy/feerate.cpp#L11
63        let original_feerate_sat_per_kwu = FeeRate::from_sat_per_kwu(
64            (original_tx_fee.to_sat() * 1000) / (original_tx_weight.to_vbytes_ceil() * 4),
65        );
66
67        // If original feerate is already higher than target, avoid bumping
68        if original_feerate_sat_per_kwu >= new_feerate {
69            return Ok(None);
70        }
71
72        // Get minimum fee increment rate from node for BIP125 compliance. Returned value is in BTC/kvB
73        let incremental_fee_rate = self
74            .rpc
75            .get_network_info()
76            .await
77            .map_err(|e| eyre!(e))?
78            .incremental_fee;
79        let incremental_fee_rate_sat_per_kvb = incremental_fee_rate.to_sat();
80        let incremental_fee_rate = FeeRate::from_sat_per_kwu(incremental_fee_rate_sat_per_kvb / 4);
81
82        // Use max of target fee rate and original + minimum fee increment rate.
83        let min_bump_feerate =
84            original_feerate_sat_per_kwu.to_sat_per_kwu() + incremental_fee_rate.to_sat_per_kwu();
85
86        let effective_feerate_sat_per_kwu =
87            std::cmp::max(new_feerate.to_sat_per_kwu(), min_bump_feerate);
88
89        Ok(Some(FeeRate::from_sat_per_kwu(
90            effective_feerate_sat_per_kwu,
91        )))
92    }
93
94    pub async fn fill_in_utxo_info(&self, psbt: &mut String) -> Result<()> {
95        let mut decoded_psbt = Psbt::from_str(psbt).map_err(|e| eyre!(e))?;
96        let tx = decoded_psbt.unsigned_tx.clone();
97
98        for (idx, input) in tx.input.iter().enumerate() {
99            let utxo = self
100                .rpc
101                .get_tx_out(
102                    &input.previous_output.txid,
103                    input.previous_output.vout,
104                    Some(false),
105                )
106                .await
107                .wrap_err("Failed to get UTXO info")?;
108
109            if let Some(utxo) = utxo {
110                decoded_psbt.inputs[idx].witness_utxo = Some(TxOut {
111                    value: utxo.value,
112                    script_pubkey: utxo
113                        .script_pub_key
114                        .script()
115                        .wrap_err("Failed to get script pubkey")?,
116                });
117            }
118        }
119
120        *psbt = decoded_psbt.to_string();
121
122        Ok(())
123    }
124
125    /// Given a PSBT with inputs, fill in the existing witnesses from the original tx
126    /// This allows us to create a finalized PSBT if
127    /// the original tx had SinglePlusAnyoneCanPay signatures.  If the original
128    /// tx did not have S+AP, these signatures will be added. The expected behavior is for them to be replaced using RbfSigningInfo.
129    ///
130    /// # Returns
131    /// The PSBT as a base64-encoded string.
132    pub async fn copy_witnesses(&self, psbt: String, initial_tx: &Transaction) -> Result<String> {
133        let mut decoded_psbt = Psbt::from_str(&psbt).map_err(|e| eyre!(e))?;
134
135        for (idx, input) in initial_tx.input.iter().enumerate() {
136            if let Some(sig) = input.witness.nth(0) {
137                if sig.len() == 65 && sig[64] == 0x83 {
138                    // This is a S+AP signature, copy it over
139                    decoded_psbt.inputs[idx].final_script_witness = Some(input.witness.clone());
140                }
141            }
142        }
143
144        Ok(decoded_psbt.to_string())
145    }
146
147    pub async fn create_funded_psbt(
148        &self,
149        tx: &Transaction,
150        fee_rate: FeeRate,
151    ) -> Result<WalletCreateFundedPsbtResult> {
152        // We need to carefully calculate the witness weight and factor that
153        // into the fee rate because wallet_create_funded_psbt does not factor
154        // in witnesses.
155
156        // The scaleup factor is the ratio of the total weight to the base weight
157        // The walletcreatefundedpsbt will use the base weight to calculate the fee
158        // and we'll scale up the fee rate by the scaleup factor to achieve our desired fee
159        let witness_scaleup = tx.weight().to_wu() as f64 / (tx.base_size() * 4) as f64;
160
161        let adjusted_fee_rate = FeeRate::from_sat_per_kwu(
162            (fee_rate.to_sat_per_kwu() as f64 * witness_scaleup).ceil() as u64,
163        );
164
165        // 1. Create a funded PSBT using the wallet
166        let create_psbt_opts = bitcoincore_rpc::json::WalletCreateFundedPsbtOptions {
167            add_inputs: Some(true), // Let the wallet add its inputs
168            change_address: None,
169            change_position: Some(tx.output.len() as u16), // Add change output at last index (so that SinglePlusAnyoneCanPay signatures stay valid)
170            change_type: None,
171            include_watching: None,
172            lock_unspent: None,
173            // Bitcoincore expects BTC/kvbyte for fee_rate
174            fee_rate: Some(
175                adjusted_fee_rate
176                    .fee_vb(1000)
177                    .ok_or_eyre("Failed to convert fee rate to BTC/kvbyte")?,
178            ),
179            subtract_fee_from_outputs: vec![],
180            replaceable: Some(true), // Mark as RBF enabled
181            conf_target: None,
182            estimate_mode: None,
183        };
184
185        let mut omitted = 0usize;
186        let filtered_outputs: Vec<WalletCreateFundedPsbtOutput> = tx
187            .output
188            .iter()
189            .filter_map(|out| {
190                if out.script_pubkey.is_op_return() {
191                    if let Some(Ok(Instruction::PushBytes(data))) =
192                        out.script_pubkey.instructions().last()
193                    {
194                        return Some(WalletCreateFundedPsbtOutput::OpReturn(
195                            data.as_bytes().to_vec(),
196                        ));
197                    }
198                }
199                let address = Address::from_script(
200                    &out.script_pubkey,
201                    self.protocol_paramset.network,
202                )
203                .map_err(|e| eyre!(e));
204                match address {
205                    Ok(address) => Some(WalletCreateFundedPsbtOutput::Spendable(
206                        address.to_string(),
207                        out.value,
208                    )),
209                    Err(err) => {
210                        tracing::error!(
211                            "Failed to get address from script for output of tx with txid {} for script: {}",
212                            tx.compute_txid(),
213                            err
214                        );
215                        omitted += 1;
216                        None
217                    }
218                }
219            })
220            .collect::<Vec<_>>();
221
222        if omitted > 0 {
223            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());
224        }
225
226        let outputs = WalletCreateFundedPsbtOutputs(filtered_outputs);
227
228        self.rpc
229            .wallet_create_funded_psbt(
230                &tx.input
231                    .iter()
232                    .map(|inp| CreateRawTransactionInput {
233                        txid: inp.previous_output.txid,
234                        vout: inp.previous_output.vout,
235                        sequence: Some(inp.sequence.to_consensus_u32()),
236                    })
237                    .collect::<Vec<_>>(),
238                outputs,
239                None,
240                Some(create_psbt_opts),
241                None,
242            )
243            .await
244            .map_err(|e| eyre!(e).into())
245    }
246    /// Given a PSBT with inputs that've been signed by the wallet except for our new input,
247    /// we have to sign the first input with our self.signer actor.
248    ///
249    /// Assumes that the first input is the input with our key.
250    ///
251    /// # Returns
252    /// The signed PSBT as a base64-encoded string.
253    pub async fn attempt_sign_psbt(
254        &self,
255        psbt: String,
256        rbf_signing_info: RbfSigningInfo,
257    ) -> Result<String> {
258        // Parse the PSBT from string
259        let mut decoded_psbt = Psbt::from_str(&psbt).map_err(|e| eyre!(e))?;
260
261        // Ensure we have inputs to sign
262        if decoded_psbt.inputs.is_empty() {
263            return Err(eyre!("PSBT has no inputs to sign").into());
264        }
265
266        let input_index = rbf_signing_info.vout as usize;
267
268        // Get the transaction to calculate the sighash
269        let tx = decoded_psbt.unsigned_tx.clone();
270        let mut sighash_cache = SighashCache::new(&tx);
271
272        // Determine the sighash type (default to ALL if not specified)
273        let sighash_type = decoded_psbt.inputs[input_index]
274            .sighash_type
275            .unwrap_or((TapSighashType::Default).into());
276
277        // For Taproot key path spending
278        if let Ok(tap_sighash_type) = sighash_type.taproot_hash_ty() {
279            // Calculate the sighash for this input
280            // Extract previous outputs from the PSBT
281            let prevouts: Vec<bitcoin::TxOut> = decoded_psbt
282                .inputs
283                .iter()
284                .map(|input| {
285                    input
286                        .witness_utxo
287                        .clone()
288                        .ok_or_eyre("expected inputs to be segwit")
289                        .map_err(SendTxError::Other)
290                })
291                .collect::<Result<Vec<_>>>()?;
292
293            let sighash = sighash_cache
294                .taproot_key_spend_signature_hash(
295                    input_index,
296                    &Prevouts::All(&prevouts),
297                    tap_sighash_type,
298                )
299                .map_err(|e| eyre!("Failed to calculate sighash: {}", e))?;
300
301            #[cfg(test)]
302            let mut sighash = sighash;
303
304            #[cfg(test)]
305            {
306                use bitcoin::sighash::Annex;
307                // This should provide the Sighash for the key spend
308                if let Some(ref annex_bytes) = rbf_signing_info.annex {
309                    let annex = Annex::new(annex_bytes).unwrap();
310                    sighash = sighash_cache
311                        .taproot_signature_hash(
312                            input_index,
313                            &Prevouts::All(&prevouts),
314                            Some(annex),
315                            None,
316                            tap_sighash_type,
317                        )
318                        .map_err(|e| eyre!("Failed to calculate sighash with annex: {}", e))?;
319                }
320            }
321
322            // Sign the sighash with our signer
323            let signature = self
324                .signer
325                .sign_with_tweak_data(
326                    sighash,
327                    TapTweakData::KeyPath(rbf_signing_info.tweak_merkle_root),
328                )
329                .map_err(|e| eyre!("Failed to sign input: {}", e))?;
330
331            // Add the signature to the PSBT
332            decoded_psbt.inputs[input_index].tap_key_sig = Some(taproot::Signature {
333                signature,
334                sighash_type: tap_sighash_type,
335            });
336
337            decoded_psbt.inputs[input_index].final_script_witness =
338                Some(Witness::from_slice(&[signature.serialize()]));
339
340            #[cfg(test)]
341            {
342                if let Some(ref annex_bytes) = rbf_signing_info.annex {
343                    let mut witness = Witness::from_slice(&[signature.serialize()]);
344                    witness.push(annex_bytes);
345                    decoded_psbt.inputs[input_index].final_script_witness = Some(witness);
346                    tracing::info!("Decoded PSBT: {:?}", decoded_psbt);
347                }
348            }
349            // Serialize the signed PSBT back to base64
350            Ok(decoded_psbt.to_string())
351        } else {
352            Err(eyre!("Only Taproot key path signing is currently supported").into())
353        }
354    }
355
356    #[track_caller]
357    pub fn handle_err(
358        &self,
359        err_msg: impl AsRef<str>,
360        err_state: impl Into<String>,
361        try_to_send_id: u32,
362    ) {
363        log_error_for_tx!(self.db, try_to_send_id, err_msg.as_ref());
364
365        let err_state = err_state.into();
366        let db = self.db.clone();
367
368        tokio::spawn(async move {
369            let _ = db
370                .update_tx_debug_sending_state(try_to_send_id, &err_state, true)
371                .await;
372        });
373    }
374
375    /// This function verifies that the wallet has added a funding input to the
376    /// PSBT.
377    ///
378    /// This is required for a transaction to be added to the wallet.
379    pub fn verify_new_inputs(&self, psbt: &str, original_tx: &Transaction) -> bool {
380        let Ok(psbt) = Psbt::from_str(psbt) else {
381            tracing::error!("Failed to parse PSBT");
382            return false;
383        };
384
385        psbt.inputs.len() > original_tx.input.len()
386    }
387
388    pub async fn get_tx_fee(&self, tx: &Transaction) -> Result<Amount> {
389        let inputs = {
390            let mut inputs = Amount::ZERO;
391            for inp in &tx.input {
392                inputs += self
393                    .rpc
394                    .get_txout_from_outpoint(&inp.previous_output)
395                    .await
396                    .map_err(|e| eyre!(e))?
397                    .value;
398            }
399            inputs
400        };
401        let outputs = tx.output.iter().map(|o| o.value).sum::<Amount>();
402
403        let tx_fee = inputs - outputs;
404
405        Ok(tx_fee)
406    }
407
408    /// Sends or bumps a transaction using the Replace-By-Fee (RBF) strategy.
409    ///
410    /// It interacts with the database to track the latest RBF attempt (`last_rbf_txid`).
411    ///
412    /// # Logic:
413    /// 1.  **Check for Existing RBF Tx:** Retrieves `last_rbf_txid` for the `try_to_send_id`.
414    /// 2.  **Bump Existing Tx:** If `psbt_bump_fee` exists, it calls `rpc.psbt_bump_fee`.
415    ///     - This internally uses the Bitcoin Core `psbtbumpfee` RPC.
416    ///     - We then sign the inputs that we can using our Actor and have the wallet sign the rest.
417    ///
418    /// 3.  **Send Initial RBF Tx:** If no `last_rbf_txid` exists (first attempt):
419    ///     - It uses `fund_raw_transaction` RPC to let the wallet add (potentially) inputs,
420    ///       outputs, set the fee according to `fee_rate`, and mark the transaction as replaceable.
421    ///     - Uses `sign_raw_transaction_with_wallet` RPC to sign the funded transaction.
422    ///     - Uses `send_raw_transaction` RPC to broadcast the initial RBF transaction.
423    ///     - Saves the resulting `txid` to the database as the `last_rbf_txid`.
424    ///
425    /// # Arguments
426    /// * `try_to_send_id` - The database ID tracking this send attempt.
427    /// * `tx` - The original transaction intended for RBF (used only on the first attempt).
428    /// * `tx_metadata` - Optional metadata associated with the transaction.
429    /// * `fee_rate` - The target fee rate for the RBF replacement.
430    #[tracing::instrument(skip_all, fields(sender = self.btc_syncer_consumer_id, try_to_send_id, tx_meta=?tx_metadata))]
431    pub async fn send_rbf_tx(
432        &self,
433        try_to_send_id: u32,
434        tx: Transaction,
435        tx_metadata: Option<TxMetadata>,
436        fee_rate: FeeRate,
437        rbf_signing_info: Option<RbfSigningInfo>,
438    ) -> Result<()> {
439        tracing::debug!(?tx_metadata, "Sending RBF tx",);
440
441        tracing::debug!(?try_to_send_id, "Attempting to send.");
442
443        let _ = self
444            .db
445            .update_tx_debug_sending_state(try_to_send_id, "preparing_rbf", true)
446            .await;
447
448        let mut dbtx = self
449            .db
450            .begin_transaction()
451            .await
452            .wrap_err("Failed to begin database transaction")?;
453
454        let last_rbf_txid = self
455            .db
456            .get_last_rbf_txid(Some(&mut dbtx), try_to_send_id)
457            .await
458            .wrap_err("Failed to get last RBF txid")?;
459
460        if let Some(last_rbf_txid) = last_rbf_txid {
461            tracing::debug!(
462                ?try_to_send_id,
463                "Attempting to bump fee for txid {last_rbf_txid} using psbt_bump_fee"
464            );
465
466            let effective_feerate = self
467                .calculate_bump_feerate_if_needed(&last_rbf_txid, fee_rate)
468                .await?;
469
470            let Some(effective_feerate) = effective_feerate else {
471                tracing::debug!(
472                    ?try_to_send_id,
473                    "Original tx feerate already higher than target ({} sat/vB), skipping bump",
474                    fee_rate.to_sat_per_vb_ceil()
475                );
476                return Ok(());
477            };
478
479            let psbt_bump_opts = BumpFeeOptions {
480                conf_target: None, // Use fee_rate instead
481                fee_rate: Some(bitcoincore_rpc::json::FeeRate::per_vbyte(Amount::from_sat(
482                    effective_feerate.to_sat_per_vb_ceil(),
483                ))),
484                replaceable: Some(true), // Ensure the bumped tx is also replaceable
485                estimate_mode: None,
486            };
487
488            let bump_result = self
489                .rpc
490                .psbt_bump_fee(&last_rbf_txid, Some(&psbt_bump_opts))
491                .await;
492
493            let bumped_psbt = match bump_result {
494                Err(e) => {
495                    // Check for common errors indicating the tx is already confirmed or spent
496                    let rpc_error_str = e.to_string();
497                    if rpc_error_str.contains("Transaction already in block chain") {
498                        tracing::debug!(
499                            ?try_to_send_id,
500                            "RBF bump failed for {last_rbf_txid}, likely confirmed or spent: {e}"
501                        );
502                        // No need to return error, just log and proceed
503                        self.db.commit_transaction(dbtx).await.wrap_err(
504                            "Failed to commit database transaction after failed bump check",
505                        )?;
506                        return Ok(());
507                    } else {
508                        // Other potentially transient errors
509                        let error_message = format!("psbt_bump_fee failed: {e}");
510                        log_error_for_tx!(self.db, try_to_send_id, error_message);
511                        let _ = self
512                            .db
513                            .update_tx_debug_sending_state(
514                                try_to_send_id,
515                                "rbf_psbt_bump_failed",
516                                true,
517                            )
518                            .await;
519                        tracing::warn!(?try_to_send_id, "psbt_bump_fee failed: {e:?}");
520                        return Err(SendTxError::Other(eyre!(e)));
521                    }
522                }
523                Ok(BumpFeeResult {
524                    psbt: Some(psbt), ..
525                }) => psbt,
526                Ok(BumpFeeResult { errors, .. }) if !errors.is_empty() => {
527                    self.handle_err(
528                        format!("psbt_bump_fee failed: {errors:?}"),
529                        "rbf_psbt_bump_failed",
530                        try_to_send_id,
531                    );
532                    return Err(SendTxError::Other(eyre!(errors.join(", "))));
533                }
534                Ok(BumpFeeResult { psbt: None, .. }) => {
535                    self.handle_err(
536                        "psbt_bump_fee returned no psbt",
537                        "rbf_psbt_bump_failed",
538                        try_to_send_id,
539                    );
540                    return Err(SendTxError::Other(eyre!("psbt_bump_fee returned no psbt")));
541                }
542            };
543
544            let bumped_psbt = self
545                .copy_witnesses(bumped_psbt, &tx)
546                .await
547                .wrap_err("Failed to fill SAP signatures")?;
548
549            // Wallet first pass
550            // We rely on the node's wallet here because psbt_bump_fee might add inputs from it.
551            let process_result = self
552                .rpc
553                .wallet_process_psbt(&bumped_psbt, Some(true), None, None) // sign=true
554                .await;
555
556            let processed_psbt = match process_result {
557                Ok(res) if res.complete => res.psbt,
558                // attempt to sign
559                Ok(res) => {
560                    let Some(rbf_signing_info) = rbf_signing_info else {
561                        return Err(eyre!(
562                            "RBF signing info is required for non SighashSingle RBF txs"
563                        )
564                        .into());
565                    };
566                    self.attempt_sign_psbt(res.psbt, rbf_signing_info).await?
567                }
568                Err(e) => {
569                    let err_msg = format!("wallet_process_psbt error: {e}");
570                    tracing::warn!(?try_to_send_id, "{}", err_msg);
571                    log_error_for_tx!(self.db, try_to_send_id, err_msg);
572                    let _ = self
573                        .db
574                        .update_tx_debug_sending_state(try_to_send_id, "rbf_psbt_sign_failed", true)
575                        .await;
576                    return Err(SendTxError::Other(eyre!(e)));
577                }
578            };
579
580            // Finalize the PSBT
581            let finalize_result = self
582                .rpc
583                .finalize_psbt(&processed_psbt, None) // extract=true by default
584                .await;
585
586            let final_tx_hex = match finalize_result {
587                Ok(FinalizePsbtResult {
588                    hex: Some(hex),
589                    complete: true,
590                    ..
591                }) => hex,
592                Ok(res) => {
593                    let err_msg = format!("Could not finalize PSBT: {res:?}");
594                    log_error_for_tx!(self.db, try_to_send_id, err_msg);
595
596                    let _ = self
597                        .db
598                        .update_tx_debug_sending_state(
599                            try_to_send_id,
600                            "rbf_psbt_finalize_incomplete",
601                            true,
602                        )
603                        .await;
604                    return Err(SendTxError::PsbtError(err_msg));
605                }
606                Err(e) => {
607                    log_error_for_tx!(
608                        self.db,
609                        try_to_send_id,
610                        format!("finalize_psbt error: {}", e)
611                    );
612                    let _ = self
613                        .db
614                        .update_tx_debug_sending_state(
615                            try_to_send_id,
616                            "rbf_psbt_finalize_failed",
617                            true,
618                        )
619                        .await;
620                    return Err(SendTxError::Other(eyre!(e)));
621                }
622            };
623
624            // Deserialize final tx to get txid
625            let final_tx: Transaction = match consensus::deserialize(&final_tx_hex) {
626                Ok(tx) => tx,
627                Err(e) => {
628                    log_error_for_tx!(
629                        self.db,
630                        try_to_send_id,
631                        format!("Failed to deserialize final RBF tx hex: {}", e)
632                    );
633                    return Err(SendTxError::Other(eyre!(e)));
634                }
635            };
636            let bumped_txid = final_tx.compute_txid();
637
638            // Broadcast the finalized transaction
639            let sent_txid = match self.rpc.send_raw_transaction(&final_tx).await {
640                Ok(sent_txid) if sent_txid == bumped_txid => sent_txid,
641                Ok(other_txid) => {
642                    log_error_for_tx!(
643                        self.db,
644                        try_to_send_id,
645                        format!(
646                            "send_raw_transaction returned unexpected txid {} (expected {})",
647                            other_txid, bumped_txid
648                        )
649                    );
650                    let _ = self
651                        .db
652                        .update_tx_debug_sending_state(
653                            try_to_send_id,
654                            "rbf_send_txid_mismatch",
655                            true,
656                        )
657                        .await;
658                    return Err(SendTxError::Other(eyre!(
659                        "send_raw_transaction returned unexpected txid"
660                    )));
661                }
662                Err(e) => {
663                    log_error_for_tx!(
664                        self.db,
665                        try_to_send_id,
666                        format!("send_raw_transaction error for bumped RBF tx: {}", e)
667                    );
668                    let _ = self
669                        .db
670                        .update_tx_debug_sending_state(try_to_send_id, "rbf_bump_send_failed", true)
671                        .await;
672                    return Err(SendTxError::Other(eyre!(e)));
673                }
674            };
675
676            tracing::debug!(
677                ?try_to_send_id,
678                "RBF tx {last_rbf_txid} successfully bumped and sent as {sent_txid}"
679            );
680
681            let _ = self
682                .db
683                .update_tx_debug_sending_state(try_to_send_id, "rbf_bumped_sent", true)
684                .await;
685
686            self.db
687                .save_rbf_txid(Some(&mut dbtx), try_to_send_id, sent_txid)
688                .await
689                .wrap_err("Failed to save new RBF txid after bump")?;
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
865        self.db
866            .commit_transaction(dbtx)
867            .await
868            .wrap_err("Failed to commit database transaction")?;
869
870        Ok(())
871    }
872}