clementine_tx_sender/
nonstandard.rs1use crate::{log_error_for_tx, TxSender};
2use bitcoin::consensus::serialize;
3use bitcoin::Transaction;
4use clementine_errors::SendTxError;
5use std::collections::HashMap;
6
7impl TxSender {
8 pub fn is_bridge_tx_nonstandard(&self, tx: &Transaction) -> bool {
21 tx.output.iter().any(|output| {
22 output.value.to_sat() == 0
23 && !self.is_p2a_anchor(output)
24 && !output.script_pubkey.is_op_return()
25 }) || tx.weight().to_wu() > 400_000
26 }
27
28 pub async fn send_testnet4_nonstandard_tx(
43 &self,
44 tx: &Transaction,
45 try_to_send_id: u32,
46 ) -> Result<(), SendTxError> {
47 let api_key = std::env::var("MEMPOOL_SPACE_API_KEY").map_err(|_| {
49 SendTxError::Other(eyre::eyre!(
50 "MEMPOOL_SPACE_API_KEY environment variable not set, cannot send nonstandard transactions to testnet4"
51 ))
52 })?;
53
54 let txid = tx.compute_txid();
56 let response = self
57 .http_client
58 .get("https://mempool.space/api/v1/services/accelerator/testnet4/accelerations")
59 .header("X-Mempool-Auth", api_key.clone())
60 .send()
61 .await
62 .map_err(|e| {
63 SendTxError::NetworkError(format!(
64 "Failed to get transaction history from mempool.space accelerator: {e}"
65 ))
66 })?;
67 if response.status().is_success() {
68 let text = response.text().await.unwrap_or_default();
71 let previously_sent_txs: serde_json::Value =
72 serde_json::from_str(&text).unwrap_or_else(|_| serde_json::json!([]));
73
74 for tx in previously_sent_txs.as_array().unwrap_or(&vec![]) {
76 let Some(response_txid) = tx.get("txid").and_then(|v| v.as_str()) else {
77 continue;
78 };
79
80 if response_txid == txid.to_string() && tx["status"] != "failed" {
81 tracing::debug!(
82 "Found {:?} with status {:?} in accelerator transaction history",
83 txid,
84 tx["status"]
85 );
86 let _ = self
87 .db
88 .update_tx_debug_sending_state(
89 try_to_send_id,
90 "nonstandard_testnet4_send_submitted",
91 false,
92 )
93 .await;
94 return Ok(()); }
96 }
97 } else {
98 let status = response.status();
99 let error_text = response.text().await.unwrap_or_default();
100 return Err(SendTxError::NetworkError(format!(
101 "Accelerator returned HTTP {status}: {error_text}"
102 )));
103 }
104
105 let tx_hex = hex::encode(serialize(tx));
107
108 let mut form_data = HashMap::new();
110 form_data.insert("txInput", tx_hex);
111 form_data.insert("label", format!("clementine-{}", tx.compute_txid()));
112
113 let response = self
115 .http_client
116 .post("https://mempool.space/api/v1/services/accelerator/testnet4/accelerate/hex")
117 .header("X-Mempool-Auth", api_key)
118 .form(&form_data)
119 .send()
120 .await
121 .map_err(|e| {
122 SendTxError::NetworkError(format!(
123 "Failed to submit transaction to mempool.space accelerator: {e}"
124 ))
125 })?;
126
127 if response.status().is_success() {
129 let response_text = response
130 .text()
131 .await
132 .map_err(|e| SendTxError::NetworkError(format!("Failed to read response: {e}")))?;
133
134 tracing::info!(
135 "Successfully submitted nonstandard transaction {:?} to mempool.space testnet4 accelerator: {}",
136 txid,
137 response_text
138 );
139
140 let _ = self
141 .db
142 .update_tx_debug_sending_state(
143 try_to_send_id,
144 "nonstandard_testnet4_send_success",
145 true,
146 )
147 .await;
148
149 Ok(())
150 } else {
151 let status = response.status();
152 let error_text = response
153 .text()
154 .await
155 .unwrap_or_else(|_| "Unknown error".to_string());
156
157 log_error_for_tx!(
158 self.db,
159 try_to_send_id,
160 format!(
161 "Failed to submit transaction to mempool.space. Status: {}, Error: {}",
162 status, error_text
163 )
164 );
165 let _ = self
166 .db
167 .update_tx_debug_sending_state(
168 try_to_send_id,
169 "nonstandard_testnet4_send_failed",
170 true,
171 )
172 .await;
173
174 Err(SendTxError::NetworkError(format!(
175 "Failed to submit transaction to mempool.space. Status: {status}, Error: {error_text}"
176 )))
177 }
178 }
179}