clementine_core/tx_sender/cpfp.rs
1//! # Child Pays For Parent (CPFP) Support For Transaction Sender
2//!
3//! This module implements the Child Pays For Parent (CPFP) strategy for sending
4//! Bitcoin transactions with transaction sender.
5//!
6//! ## Child Transaction Details
7//!
8//! A child transaction is created to pay for the fees of a parent transaction.
9//! They must be submitted together as a package for Bitcoin nodes to accept
10//! them.
11//!
12//! ### Fee Payer Transactions/UTXOs
13//!
14//! Child transaction needs to spend an UTXO for the fees. But because of the
15//! TRUC rules (https://github.com/bitcoin/bips/blob/master/bip-0431.mediawiki#specification),
16//! a third transaction can't be put into the package. So, a so called "fee
17//! payer" transaction must be send and confirmed before the CPFP package is
18//! send.
19
20use super::{Result, SendTxError, TxMetadata, TxSender};
21use crate::constants::NON_STANDARD_V3;
22use crate::errors::{ErrorExt, ResultExt};
23use crate::extended_bitcoin_rpc::BitcoinRPCError;
24use crate::utils::FeePayingType;
25use crate::{
26 builder::{
27 self,
28 script::SpendPath,
29 transaction::{
30 input::SpendableTxIn, output::UnspentTxOut, TransactionType, TxHandlerBuilder,
31 DEFAULT_SEQUENCE,
32 },
33 },
34 constants::MIN_TAPROOT_AMOUNT,
35 rpc::clementine::NormalSignatureKind,
36};
37use bitcoin::{Amount, FeeRate, OutPoint, Transaction, TxOut, Weight};
38use bitcoincore_rpc::PackageSubmissionResult;
39use bitcoincore_rpc::{PackageTransactionResult, RpcApi};
40use eyre::eyre;
41use eyre::Context;
42use std::collections::HashSet;
43use std::env;
44
45impl TxSender {
46 /// Creates and broadcasts a new "fee payer" UTXO to be used for CPFP
47 /// transactions.
48 ///
49 /// This function is called when a CPFP attempt fails due to insufficient funds
50 /// in the existing confirmed fee payer UTXOs associated with a transaction (`bumped_id`).
51 /// It calculates the required fee based on the parent transaction (`tx`) and the current
52 /// `fee_rate`, adding a buffer (2x required fee + dust limit) to handle potential fee spikes.
53 /// It then sends funds to the `TxSender`'s own signer address using the RPC's
54 /// `send_to_address` and saves the resulting UTXO information (`outpoint`, `amount`)
55 /// to the database, linking it to the `bumped_id`.
56 ///
57 /// # Arguments
58 /// * `bumped_id` - The database ID of the parent transaction requiring the fee bump.
59 /// * `tx` - The parent transaction itself.
60 /// * `fee_rate` - The target fee rate for the CPFP package.
61 /// * `total_fee_payer_amount` - The sum of amounts in currently available confirmed fee payer UTXOs.
62 /// * `fee_payer_utxos_len` - The number of currently available confirmed fee payer UTXOs.
63 async fn create_fee_payer_utxo(
64 &self,
65 bumped_id: u32,
66 tx: &Transaction,
67 fee_rate: FeeRate,
68 total_fee_payer_amount: Amount,
69 fee_payer_utxos_len: usize,
70 ) -> Result<()> {
71 tracing::debug!(
72 "Creating fee payer UTXO for txid {} with bump id {}",
73 &tx.compute_txid().to_string(),
74 bumped_id
75 );
76 let required_fee = Self::calculate_required_fee(
77 tx.weight(),
78 fee_payer_utxos_len + 1,
79 fee_rate,
80 FeePayingType::CPFP,
81 )?;
82
83 // Aggressively add 2x required fee to the total amount to account for sudden spikes
84 // We won't actually use 2x fees, but the fee payer utxo will hold that much amount so that while fee payer utxo gets mined
85 // if fees increase the utxo should still be sufficient to fund the tx with high probability
86 // leftover fees will get sent back to wallet with a change output in fn create_child_tx
87 let new_total_fee_needed = required_fee
88 .checked_mul(2)
89 .and_then(|fee| fee.checked_add(MIN_TAPROOT_AMOUNT));
90 if new_total_fee_needed.is_none() {
91 return Err(eyre!("Total fee needed is too large, required fee: {}, total fee payer amount: {}, fee rate: {}", required_fee, total_fee_payer_amount, fee_rate).into());
92 }
93 let new_fee_payer_amount =
94 new_total_fee_needed.and_then(|fee| fee.checked_sub(total_fee_payer_amount));
95
96 let new_fee_payer_amount = match new_fee_payer_amount {
97 Some(fee) => fee,
98 // if underflow, no new fee payer utxo is needed, log it anyway in case its a bug
99 None => {
100 tracing::debug!("create_fee_payer_utxo was called but no new fee payer utxo is needed for tx: {:?}, required fee: {}, total fee payer amount: {}, current fee rate: {}", tx, required_fee, total_fee_payer_amount, fee_rate);
101 return Ok(());
102 }
103 };
104
105 tracing::debug!(
106 "Creating fee payer UTXO with amount {} ({} sat/vb)",
107 new_fee_payer_amount,
108 fee_rate
109 );
110
111 let fee_payer_tx = Transaction {
112 version: bitcoin::transaction::Version::TWO,
113 lock_time: bitcoin::absolute::LockTime::ZERO,
114 input: vec![],
115 output: vec![TxOut {
116 value: new_fee_payer_amount,
117 script_pubkey: self.signer.address.script_pubkey(),
118 }],
119 };
120
121 // Manually serialize in legacy format for 0-input transactions
122 // Because fund_raw_transaction RPC gives deserialization error for 0-input transactions with segwit flag
123 // but in the end fund_raw_transaction returns a segwit transaction after adding inputs
124 let fee_payer_bytes = if fee_payer_tx.input.is_empty() {
125 use bitcoin::consensus::Encodable;
126 let mut buf = Vec::new();
127 // Serialize version
128 fee_payer_tx
129 .version
130 .consensus_encode(&mut buf)
131 .expect("Failed to serialize version");
132 fee_payer_tx
133 .input
134 .consensus_encode(&mut buf)
135 .expect("Failed to serialize inputs");
136 fee_payer_tx
137 .output
138 .consensus_encode(&mut buf)
139 .expect("Failed to serialize outputs");
140 // Serialize locktime
141 fee_payer_tx
142 .lock_time
143 .consensus_encode(&mut buf)
144 .expect("Failed to serialize locktime");
145
146 buf
147 } else {
148 bitcoin::consensus::encode::serialize(&fee_payer_tx)
149 };
150
151 let funded_fee_payer_tx = self
152 .rpc
153 .fund_raw_transaction(
154 &fee_payer_bytes,
155 Some(&bitcoincore_rpc::json::FundRawTransactionOptions {
156 add_inputs: Some(true),
157 change_address: None,
158 change_position: None,
159 change_type: None,
160 include_watching: None,
161 lock_unspents: None,
162 fee_rate: Some(Amount::from_sat(fee_rate.to_sat_per_vb_ceil() * 1000)),
163 subtract_fee_from_outputs: None,
164 replaceable: Some(true),
165 conf_target: None,
166 estimate_mode: None,
167 }),
168 None,
169 )
170 .await
171 .wrap_err("Failed to fund cpfp fee payer tx")?
172 .hex;
173
174 let signed_fee_payer_tx: Transaction = bitcoin::consensus::deserialize(
175 &self
176 .rpc
177 .sign_raw_transaction_with_wallet(&funded_fee_payer_tx, None, None)
178 .await
179 .wrap_err("Failed to sign funded tx through bitcoin RPC")?
180 .hex,
181 )
182 .wrap_err("Failed to deserialize signed tx")?;
183
184 let outpoint_vout = signed_fee_payer_tx
185 .output
186 .iter()
187 .position(|o| {
188 o.value == new_fee_payer_amount
189 && o.script_pubkey == self.signer.address.script_pubkey()
190 })
191 .ok_or(eyre!("Failed to find outpoint vout"))?;
192
193 self.rpc
194 .send_raw_transaction(&signed_fee_payer_tx)
195 .await
196 .wrap_err("Failed to send signed fee payer tx")?;
197
198 self.db
199 .save_fee_payer_tx(
200 None,
201 bumped_id,
202 signed_fee_payer_tx.compute_txid(),
203 outpoint_vout as u32,
204 new_fee_payer_amount,
205 None,
206 )
207 .await
208 .map_to_eyre()?;
209
210 Ok(())
211 }
212
213 /// Creates a Child-Pays-For-Parent (CPFP) child transaction.
214 ///
215 /// This transaction spends:
216 /// 1. The designated "P2A anchor" output of the parent transaction (`p2a_anchor`).
217 /// 2. One or more confirmed "fee payer" UTXOs (`fee_payer_utxos`) controlled by the `signer`.
218 ///
219 /// It calculates the total fee required (`required_fee`) to make the combined parent + child
220 /// package attractive to miners at the target `fee_rate`. The `required_fee` is paid entirely
221 /// by this child transaction.
222 ///
223 /// The remaining value (total input value - `required_fee`) is sent to the `change_address`.
224 ///
225 /// # Signing
226 /// We sign the input spending the P2A anchor and all fee payer UTXOs.
227 ///
228 /// # Returns
229 /// The constructed and partially signed child transaction.
230 async fn create_child_tx(
231 &self,
232 p2a_anchor: OutPoint,
233 anchor_sat: Amount,
234 fee_payer_utxos: Vec<SpendableTxIn>,
235 parent_tx_size: Weight,
236 fee_rate: FeeRate,
237 ) -> Result<Transaction> {
238 tracing::debug!(
239 "Creating child tx with {} fee payer utxos",
240 fee_payer_utxos.len()
241 );
242 let required_fee = Self::calculate_required_fee(
243 parent_tx_size,
244 fee_payer_utxos.len(),
245 fee_rate,
246 FeePayingType::CPFP,
247 )
248 .map_err(|e| eyre!(e))?;
249
250 let change_address = self
251 .rpc
252 .get_new_wallet_address()
253 .await
254 .wrap_err("Failed to get new wallet address")?;
255
256 let total_fee_payer_amount = fee_payer_utxos
257 .iter()
258 .map(|utxo| utxo.get_prevout().value)
259 .sum::<Amount>()
260 + anchor_sat; // We add the anchor output value to the total amount.
261 if change_address.script_pubkey().minimal_non_dust() + required_fee > total_fee_payer_amount
262 {
263 return Err(SendTxError::InsufficientFeePayerAmount);
264 }
265
266 let mut builder = TxHandlerBuilder::new(TransactionType::Dummy)
267 .with_version(NON_STANDARD_V3)
268 .add_input(
269 NormalSignatureKind::OperatorSighashDefault,
270 SpendableTxIn::new_partial(
271 p2a_anchor,
272 builder::transaction::anchor_output(anchor_sat),
273 ),
274 SpendPath::Unknown,
275 DEFAULT_SEQUENCE,
276 );
277
278 for fee_payer_utxo in fee_payer_utxos {
279 builder = builder.add_input(
280 NormalSignatureKind::OperatorSighashDefault,
281 fee_payer_utxo,
282 SpendPath::KeySpend,
283 DEFAULT_SEQUENCE,
284 );
285 }
286
287 builder = builder.add_output(UnspentTxOut::from_partial(TxOut {
288 value: total_fee_payer_amount - required_fee,
289 script_pubkey: change_address.script_pubkey(),
290 }));
291
292 let mut tx_handler = builder.finalize();
293
294 for fee_payer_input in 1..tx_handler.get_cached_tx().input.len() {
295 let sighash = tx_handler
296 .calculate_pubkey_spend_sighash(fee_payer_input, bitcoin::TapSighashType::Default)
297 .map_err(|e| eyre!(e))?;
298 let signature = self
299 .signer
300 .sign_with_tweak_data(sighash, builder::sighash::TapTweakData::KeyPath(None), None)
301 .map_err(|e| eyre!(e))?;
302 tx_handler
303 .set_p2tr_key_spend_witness(
304 &bitcoin::taproot::Signature {
305 signature,
306 sighash_type: bitcoin::TapSighashType::Default,
307 },
308 fee_payer_input,
309 )
310 .map_err(|e| eyre!(e))?;
311 }
312 let child_tx = tx_handler.get_cached_tx().clone();
313
314 Ok(child_tx)
315 }
316
317 /// Creates a transaction package for CPFP submission.
318 ///
319 /// Finds the P2A anchor output in the parent transaction (`tx`), then constructs
320 /// the child transaction using `create_child_tx`.
321 ///
322 /// # Returns
323 ///
324 /// - [`Vec<Transaction>`]: Parent transaction followed by the child
325 /// transaction ready for submission via the `submitpackage` RPC.
326 async fn create_package(
327 &self,
328 tx: Transaction,
329 fee_rate: FeeRate,
330 fee_payer_utxos: Vec<SpendableTxIn>,
331 ) -> Result<Vec<Transaction>> {
332 tracing::debug!(
333 "Creating package with {} fee payer utxos",
334 fee_payer_utxos.len()
335 );
336 let txid = tx.compute_txid();
337
338 let p2a_vout = self
339 .find_p2a_vout(&tx)
340 .wrap_err("Failed to find p2a vout")?;
341
342 // get sat amount of anchor output in the tx
343 let anchor_sat = tx.output[p2a_vout].value;
344
345 let child_tx = self
346 .create_child_tx(
347 OutPoint {
348 txid,
349 vout: p2a_vout as u32,
350 },
351 anchor_sat,
352 fee_payer_utxos,
353 tx.weight(),
354 fee_rate,
355 )
356 .await
357 .wrap_err("Failed to create child tx")?;
358
359 Ok(vec![tx, child_tx])
360 }
361
362 /// Retrieves confirmed fee payer UTXOs associated with a specific send attempt.
363 ///
364 /// Queries the database for UTXOs linked to `try_to_send_id` that are marked as confirmed.
365 /// These UTXOs are controlled by the `TxSender`'s `signer` and are intended to be
366 /// spent by a CPFP child transaction.
367 ///
368 /// # Returns
369 ///
370 /// - [`Vec<SpendableTxIn>`]: [`SpendableTxIn`]s of the confirmed fee payer
371 /// UTXOs that are ready to be included as inputs in the CPFP child tx.
372 async fn get_confirmed_fee_payer_utxos(
373 &self,
374 try_to_send_id: u32,
375 ) -> Result<Vec<SpendableTxIn>> {
376 Ok(self
377 .db
378 .get_confirmed_fee_payer_utxos(None, try_to_send_id)
379 .await
380 .map_to_eyre()?
381 .iter()
382 .map(|(txid, vout, amount)| {
383 SpendableTxIn::new(
384 OutPoint {
385 txid: *txid,
386 vout: *vout,
387 },
388 TxOut {
389 value: *amount,
390 script_pubkey: self.signer.address.script_pubkey(),
391 },
392 vec![],
393 Some(self.cached_spendinfo.clone()),
394 )
395 })
396 .collect())
397 }
398
399 /// Attempts to bump the fees of unconfirmed "fee payer" UTXOs using RBF.
400 ///
401 /// Fee payer UTXOs are created to fund CPFP child transactions. However, these
402 /// fee payer creation transactions might themselves get stuck due to low fees.
403 /// This function identifies such unconfirmed fee payer transactions associated with
404 /// a parent transaction (`bumped_id`) and attempts to RBF them using the provided `fee_rate`.
405 ///
406 /// This ensures the fee payer UTXOs confirm quickly, making them available to be spent
407 /// by the actual CPFP child transaction.
408 ///
409 /// # Arguments
410 /// * `bumped_id` - The database ID of the parent transaction whose fee payer UTXOs need bumping.
411 /// * `fee_rate` - The target fee rate for bumping the fee payer transactions.
412 #[tracing::instrument(skip_all, fields(sender = self.btc_syncer_consumer_id, fee_rate))]
413 pub(crate) async fn bump_fees_of_unconfirmed_fee_payer_txs(
414 &self,
415 fee_rate: FeeRate,
416 ) -> Result<()> {
417 let bumpable_fee_payer_txs = self
418 .db
419 .get_all_unconfirmed_fee_payer_txs(None)
420 .await
421 .map_to_eyre()?;
422
423 let mut not_evicted_ids = HashSet::new();
424 let mut all_parent_ids = HashSet::new();
425
426 for (id, try_to_send_id, fee_payer_txid, vout, amount, replacement_of_id) in
427 bumpable_fee_payer_txs
428 {
429 tracing::debug!(
430 "Bumping fee for fee payer tx {} for try to send id {} for fee rate {}",
431 fee_payer_txid,
432 try_to_send_id,
433 fee_rate
434 );
435 // parent id is the id of the first created tx for all replacements
436 let parent_id = match replacement_of_id {
437 Some(replacement_of_id) => replacement_of_id,
438 None => id,
439 };
440 all_parent_ids.insert(parent_id);
441 let mempool_info = self.rpc.get_mempool_entry(&fee_payer_txid).await;
442 let mempool_info = match mempool_info {
443 Ok(mempool_info) => {
444 not_evicted_ids.insert(parent_id);
445 mempool_info
446 }
447 Err(e) => {
448 // If not in mempool we should ignore
449 // give an error if the error is not "Transaction not in mempool"
450 if !e.to_string().contains("Transaction not in mempool") {
451 return Err(eyre::eyre!(
452 "Failed to get mempool entry for fee payer tx {}: {}",
453 fee_payer_txid,
454 e
455 )
456 .into());
457 }
458 // check here if the tx is already in block, if so do not mark it as evicted
459 let tx_info = self.rpc.get_transaction(&fee_payer_txid, None).await;
460 if let Ok(tx_info) = tx_info {
461 if tx_info.info.blockhash.is_some() && tx_info.info.confirmations > 0 {
462 not_evicted_ids.insert(parent_id);
463 }
464 }
465 continue;
466 }
467 };
468 // only try to bump if tx has no descendants and some time has passed since tx was created
469 if mempool_info.descendant_count > 1
470 || std::time::SystemTime::now()
471 .duration_since(std::time::UNIX_EPOCH)
472 .wrap_err("Failed to get unix timestamp")?
473 .as_secs()
474 .saturating_sub(mempool_info.time)
475 < self
476 .config
477 .tx_sender_limits
478 .cpfp_fee_payer_bump_wait_time_seconds
479 {
480 continue;
481 }
482 let new_txid_result = self
483 .rpc
484 .bump_fee_with_fee_rate(fee_payer_txid, fee_rate)
485 .await;
486
487 match new_txid_result {
488 Ok(new_txid) => {
489 if new_txid != fee_payer_txid {
490 self.db
491 .save_fee_payer_tx(
492 None,
493 try_to_send_id,
494 new_txid,
495 vout,
496 amount,
497 match replacement_of_id {
498 Some(replacement_of_id) => Some(replacement_of_id),
499 None => Some(id),
500 },
501 )
502 .await
503 .map_to_eyre()?;
504 } else {
505 tracing::trace!(
506 "Fee payer tx {} has enough fee, no need to bump",
507 fee_payer_txid
508 );
509 }
510 }
511 Err(e) => {
512 let e = e.into_eyre();
513 match e.root_cause().downcast_ref::<BitcoinRPCError>() {
514 Some(BitcoinRPCError::TransactionAlreadyInBlock(block_hash)) => {
515 tracing::debug!(
516 "Fee payer tx {} is already in block {}, skipping",
517 fee_payer_txid,
518 block_hash
519 );
520 continue;
521 }
522 Some(BitcoinRPCError::BumpFeeUTXOSpent(outpoint)) => {
523 tracing::debug!(
524 "Fee payer tx {} is already onchain, skipping: {:?}",
525 fee_payer_txid,
526 outpoint
527 );
528 continue;
529 }
530 _ => {
531 tracing::warn!(
532 "Failed to bump fee the fee payer tx {} with error {e}, skipping",
533 fee_payer_txid
534 );
535 continue;
536 }
537 }
538 }
539 }
540 }
541
542 // if all fee payer utxos are not in mempool
543 // in very rare cases, (if tx was mined, but before we called gettransaction it was reorged)
544 // it can be marked as evicted accidentally, but this is very rare and if it was mined once but reorged,
545 // it will likely enter the chain without any bumping anyway, but an extra fee payer utxo can be created by txsender
546 // because it is considered to be evicted
547 for parent_id in all_parent_ids {
548 if !not_evicted_ids.contains(&parent_id) {
549 self.db
550 .mark_fee_payer_utxo_as_evicted(None, parent_id)
551 .await
552 .map_to_eyre()?;
553 }
554 }
555
556 Ok(())
557 }
558
559 /// Sends a transaction using the Child-Pays-For-Parent (CPFP) strategy.
560 ///
561 /// # Logic:
562 /// 1. **Check Unconfirmed Fee Payers:** Ensures no unconfirmed fee payer UTXOs exist
563 /// for this `try_to_send_id`. If they do, returns [`SendTxError::UnconfirmedFeePayerUTXOsLeft`]
564 /// as they need to confirm before being spendable by the child.
565 /// 2. **Get Confirmed Fee Payers:** Retrieves the available confirmed fee payer UTXOs.
566 /// 3. **Create Package:** Calls `create_package` to build the `vec![parent_tx, child_tx]`.
567 /// The `child_tx` spends the parent's anchor output and the fee payer UTXOs, paying
568 /// a fee calculated for the whole package.
569 /// 4. **Test Mempool Accept (Debug step):** Uses `testmempoolaccept` RPC
570 /// to check if the package is likely to be accepted by the network before submitting.
571 /// 5. **Submit Package:** Uses the `submitpackage` RPC to atomically submit the parent
572 /// and child transactions. Bitcoin Core evaluates the fee rate of the package together.
573 /// 6. **Handle Results:** Checks the `submitpackage` result. If successful or already in
574 /// mempool, updates the effective fee rate in the database. If failed, logs an error.
575 ///
576 /// # Arguments
577 /// * `try_to_send_id` - The database ID tracking this send attempt.
578 /// * `tx` - The parent transaction requiring the fee bump.
579 /// * `tx_metadata` - Optional metadata associated with the transaction.
580 /// * `fee_rate` - The target fee rate for the CPFP package.
581 #[tracing::instrument(skip_all, fields(sender = self.btc_syncer_consumer_id, try_to_send_id, tx_meta=?tx_metadata))]
582 pub(super) async fn send_cpfp_tx(
583 &self,
584 try_to_send_id: u32,
585 tx: Transaction,
586 tx_metadata: Option<TxMetadata>,
587 fee_rate: FeeRate,
588 ) -> Result<()> {
589 let unconfirmed_fee_payer_utxos = self
590 .db
591 .get_unconfirmed_fee_payer_txs(None, try_to_send_id)
592 .await
593 .map_to_eyre()?;
594
595 if !unconfirmed_fee_payer_utxos.is_empty() {
596 // Log that we're waiting for unconfirmed UTXOs
597 tracing::debug!(
598 try_to_send_id,
599 "Waiting for {} UTXOs to confirm",
600 unconfirmed_fee_payer_utxos.len()
601 );
602
603 // Update the sending state
604 let _ = self
605 .db
606 .update_tx_debug_sending_state(
607 try_to_send_id,
608 "waiting_for_utxo_confirmation",
609 true,
610 )
611 .await;
612
613 return Ok(());
614 }
615
616 tracing::debug!(try_to_send_id, "Attempting to send CPFP tx");
617
618 let confirmed_fee_payers = self.get_confirmed_fee_payer_utxos(try_to_send_id).await?;
619 let confirmed_fee_payer_len = confirmed_fee_payers.len();
620
621 let _ = self
622 .db
623 .update_tx_debug_sending_state(try_to_send_id, "creating_package", true)
624 .await;
625
626 // to be used below
627 let total_fee_payer_amount = confirmed_fee_payers
628 .iter()
629 .map(|txi| txi.get_prevout().value)
630 .sum::<Amount>();
631
632 let package = self
633 .create_package(tx.clone(), fee_rate, confirmed_fee_payers)
634 .await
635 .wrap_err("Failed to create CPFP package");
636
637 let package = match package {
638 Ok(package) => package,
639 Err(e) => match e.root_cause().downcast_ref::<SendTxError>() {
640 Some(SendTxError::InsufficientFeePayerAmount) => {
641 tracing::debug!(
642 try_to_send_id,
643 "Insufficient fee payer amount, creating new fee payer utxo."
644 );
645
646 self.create_fee_payer_utxo(
647 try_to_send_id,
648 &tx,
649 fee_rate,
650 total_fee_payer_amount,
651 confirmed_fee_payer_len,
652 )
653 .await?;
654
655 let _ = self
656 .db
657 .update_tx_debug_sending_state(
658 try_to_send_id,
659 "waiting_for_fee_payer_utxos",
660 true,
661 )
662 .await;
663
664 return Ok(());
665 }
666 _ => {
667 tracing::error!(try_to_send_id, "Failed to create CPFP package: {:?}", e);
668 return Err(e.into());
669 }
670 },
671 };
672
673 let package_refs: Vec<&Transaction> = package.iter().collect();
674
675 tracing::debug!(
676 try_to_send_id,
677 "Submitting package\n Pkg tx hexs: {:?}",
678 if env::var("DBG_PACKAGE_HEX").is_ok() {
679 package
680 .iter()
681 .map(|tx| hex::encode(bitcoin::consensus::serialize(tx)))
682 .collect::<Vec<_>>()
683 } else {
684 vec!["use DBG_PACKAGE_HEX=1 to print the package as hex".into()]
685 }
686 );
687
688 // Update sending state to submitting_package
689 let _ = self
690 .db
691 .update_tx_debug_sending_state(try_to_send_id, "submitting_package", true)
692 .await;
693
694 tracing::debug!(try_to_send_id, "Submitting package, size {}", package.len());
695
696 // let test_mempool_result = self
697 // .rpc
698 // .test_mempool_accept(&package_refs)
699 // .await
700 // .wrap_err("Failed to test mempool accept")?;
701
702 let submit_package_result: PackageSubmissionResult = self
703 .rpc
704 .submit_package(&package_refs, Some(Amount::from_sat(0)), None)
705 .await
706 .wrap_err("Failed to submit package")?;
707
708 tracing::debug!(
709 try_to_send_id,
710 "Submit package result: {submit_package_result:?}"
711 );
712
713 // If tx_results is empty, it means the txs were already accepted by the network.
714 if submit_package_result.tx_results.is_empty() {
715 return Ok(());
716 }
717
718 let mut early_exit = false;
719 for (_txid, result) in submit_package_result.tx_results {
720 if let PackageTransactionResult::Failure { error, .. } = result {
721 tracing::error!(
722 try_to_send_id,
723 "Error submitting package: {:?}, package: {:?}",
724 error,
725 package_refs
726 .iter()
727 .map(|tx| hex::encode(bitcoin::consensus::serialize(tx)))
728 .collect::<Vec<_>>()
729 );
730
731 early_exit = true;
732 }
733 }
734 if early_exit {
735 return Ok(());
736 }
737
738 tracing::info!("Package submitted successfully.");
739
740 // // Get the effective fee rate from the first transaction result
741 // let effective_fee_rate_btc_per_kvb = submit_package_result
742 // .tx_results
743 // .iter()
744 // .next()
745 // .and_then(|(_, result)| match result {
746 // PackageTransactionResult::Success { fees, .. } => Some(fees.effective_feerate),
747 // PackageTransactionResult::SuccessAlreadyInMempool { txid, .. } => {
748 // tracing::warn!(
749 // "{}: transaction {txid} is already in mempool, skipping",
750 // self.consumer_handle
751 // );
752 // None
753 // }
754 // PackageTransactionResult::Failure { txid, error } => {
755 // tracing::warn!(
756 // "{}: failed to send the transaction {txid} with error {error}, skipping",
757 // self.consumer_handle
758 // );
759 // None
760 // }
761 // })
762 // .expect("Effective fee rate should be present")
763 // .expect("Effective fee rate should be present");
764
765 // let effective_fee_rate = Self::btc_per_kvb_to_fee_rate(effective_fee_rate_btc_per_kvb);
766 // Save the effective fee rate to the db
767 self.db
768 .update_effective_fee_rate(None, try_to_send_id, fee_rate)
769 .await
770 .wrap_err("Failed to update effective fee rate")?;
771
772 // Sanity check to make sure the fee rate is equal to the required fee rate
773 // assert_eq!(
774 // effective_fee_rate, fee_rate,
775 // "Effective fee rate is not equal to the required fee rate: {:?} to {:?} != {:?}",
776 // effective_fee_rate_btc_per_kvb, effective_fee_rate, fee_rate
777 // );
778
779 Ok(())
780 }
781}