clementine_core/
tx_sender_ext.rs

1use crate::rpc;
2use crate::rpc::clementine::TxDebugInfo;
3use bitcoin::hashes::Hash;
4use clementine_errors::{BridgeError, ResultExt as _, RoundIndex};
5use clementine_tx_sender::client::TxSenderClient;
6use clementine_tx_sender::TxSender;
7use clementine_utils::FeePayingType;
8use tonic::async_trait;
9
10#[async_trait]
11pub trait TxSenderClientExt {
12    /// Returns debugging information for a transaction
13    ///
14    /// This function gathers all debugging information about a transaction from the database,
15    /// including its state history, fee payer UTXOs, submission errors, and current state.
16    ///
17    /// # Arguments
18    /// * `id` - The ID of the transaction to debug
19    ///
20    /// # Returns
21    /// A comprehensive debug info structure with all available information about the transaction
22    async fn debug_tx(&self, id: u32) -> Result<TxDebugInfo, BridgeError>;
23}
24
25#[async_trait]
26impl TxSenderClientExt for TxSenderClient {
27    async fn debug_tx(&self, id: u32) -> Result<TxDebugInfo, BridgeError> {
28        use crate::rpc::clementine::{TxDebugFeePayerUtxo, TxDebugInfo, TxDebugSubmissionError};
29
30        let (tx_metadata, tx, fee_paying_type, seen_at_height, _) =
31            self.db.get_try_to_send_tx(None, id).await.map_to_eyre()?;
32
33        let submission_errors = self
34            .db
35            .get_tx_debug_submission_errors(None, id)
36            .await
37            .map_to_eyre()?;
38
39        let submission_errors = submission_errors
40            .into_iter()
41            .map(|(error_message, timestamp)| TxDebugSubmissionError {
42                error_message,
43                timestamp,
44            })
45            .collect();
46
47        let current_state = self.db.get_tx_debug_info(None, id).await.map_to_eyre()?;
48
49        let fee_payer_utxos = self
50            .db
51            .get_tx_debug_fee_payer_utxos(None, id)
52            .await
53            .map_to_eyre()?;
54
55        let fee_payer_utxos = fee_payer_utxos
56            .into_iter()
57            .map(|(txid, vout, amount, confirmed)| TxDebugFeePayerUtxo {
58                txid: Some(txid.into()),
59                vout,
60                amount: amount.to_sat(),
61                confirmed,
62            })
63            .collect::<Vec<_>>();
64
65        let txid = match fee_paying_type {
66            FeePayingType::CPFP | FeePayingType::NoFunding => tx.compute_txid(),
67            FeePayingType::RBF | FeePayingType::RbfWtxidGrind => self
68                .db
69                .get_last_rbf_txid(None, id)
70                .await
71                .map_to_eyre()?
72                .unwrap_or(bitcoin::Txid::all_zeros()),
73        };
74        let debug_info = TxDebugInfo {
75            id,
76            is_active: seen_at_height.is_none(),
77            current_state: current_state.unwrap_or_else(|| "unknown".to_string()),
78            submission_errors,
79            created_at: "".to_string(),
80            txid: Some(txid.into()),
81            fee_paying_type: format!("{fee_paying_type:?}"),
82            fee_payer_utxos_count: fee_payer_utxos.len() as u32,
83            fee_payer_utxos_confirmed_count: fee_payer_utxos
84                .iter()
85                .filter(|utxo| utxo.confirmed)
86                .count() as u32,
87            fee_payer_utxos,
88            raw_tx: bitcoin::consensus::serialize(&tx),
89            metadata: tx_metadata.map(|metadata| rpc::clementine::TxMetadata {
90                deposit_outpoint: metadata.deposit_outpoint.map(Into::into),
91                operator_xonly_pk: metadata.operator_xonly_pk.map(Into::into),
92
93                round_idx: metadata
94                    .round_idx
95                    .unwrap_or(RoundIndex::Round(0))
96                    .to_index() as u32,
97                kickoff_idx: metadata.kickoff_idx.unwrap_or(0),
98                tx_type: Some(metadata.tx_type.into()),
99            }),
100        };
101
102        Ok(debug_info)
103    }
104}
105
106#[async_trait]
107pub trait TxSenderExt {
108    async fn debug_tx(&self, id: u32) -> Result<TxDebugInfo, BridgeError>;
109}
110
111#[async_trait]
112impl TxSenderExt for TxSender {
113    async fn debug_tx(&self, id: u32) -> Result<TxDebugInfo, BridgeError> {
114        let client = self.client();
115        client.debug_tx(id).await
116    }
117}