clementine_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;
21use crate::{SpendableInputInfo, TxSender, TxSenderDatabase, TxSenderSigner, TxSenderTxBuilder};
22use bitcoin::absolute::LockTime;
23use bitcoin::transaction::Version;
24use bitcoin::{Amount, FeeRate, OutPoint, Transaction, TxOut, Weight};
25use bitcoincore_rpc::json::FundRawTransactionOptions;
26use bitcoincore_rpc::{PackageTransactionResult, RpcApi};
27use clementine_errors::{BitcoinRPCError, BridgeError, ResultExt as _, SendTxError};
28use clementine_primitives::MIN_TAPROOT_AMOUNT;
29use clementine_utils::{FeePayingType, TxMetadata};
30use eyre::{eyre, Context};
31use std::collections::HashSet;
32use std::env;
33
34impl<S, D, B> TxSender<S, D, B>
35where
36 S: TxSenderSigner,
37 D: TxSenderDatabase,
38 B: TxSenderTxBuilder,
39{
40 /// Creates and broadcasts a new "fee payer" UTXO to be used for CPFP
41 /// transactions.
42 ///
43 /// This function is called when a CPFP attempt fails due to insufficient funds
44 /// in the existing confirmed fee payer UTXOs associated with a transaction (`bumped_id`).
45 /// It calculates the required fee based on the parent transaction (`tx`) and the current
46 /// `fee_rate`, adding a buffer (2x required fee + dust limit) to handle potential fee spikes.
47 /// It then sends funds to the `TxSender`'s own signer address using the RPC's
48 /// `send_to_address` and saves the resulting UTXO information (`outpoint`, `amount`)
49 /// to the database, linking it to the `bumped_id`.
50 ///
51 /// # Arguments
52 /// * `bumped_id` - The database ID of the parent transaction requiring the fee bump.
53 /// * `tx` - The parent transaction itself.
54 /// * `fee_rate` - The target fee rate for the CPFP package.
55 /// * `total_fee_payer_amount` - The sum of amounts in currently available confirmed fee payer UTXOs.
56 /// * `fee_payer_utxos_len` - The number of currently available confirmed fee payer UTXOs.
57 async fn create_fee_payer_utxo(
58 &self,
59 bumped_id: u32,
60 dbtx: Option<&mut D::Transaction>,
61 tx: &Transaction,
62 fee_rate: FeeRate,
63 total_fee_payer_amount: Amount,
64 fee_payer_utxos_len: usize,
65 ) -> Result<()> {
66 tracing::debug!(
67 "Creating fee payer UTXO for txid {} with bump id {}",
68 &tx.compute_txid().to_string(),
69 bumped_id
70 );
71 let required_fee = Self::calculate_required_fee(
72 tx.weight(),
73 fee_payer_utxos_len + 1,
74 fee_rate,
75 FeePayingType::CPFP,
76 )?;
77
78 // Aggressively add 2x required fee to the total amount to account for sudden spikes
79 // 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
80 // if fees increase the utxo should still be sufficient to fund the tx with high probability
81 // leftover fees will get sent back to wallet with a change output in fn create_child_tx
82 let new_total_fee_needed = required_fee
83 .checked_mul(2)
84 .and_then(|fee| fee.checked_add(MIN_TAPROOT_AMOUNT));
85 if new_total_fee_needed.is_none() {
86 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());
87 }
88 let new_fee_payer_amount =
89 new_total_fee_needed.and_then(|fee| fee.checked_sub(total_fee_payer_amount));
90
91 let new_fee_payer_amount = match new_fee_payer_amount {
92 Some(fee) => fee,
93 // if underflow, no new fee payer utxo is needed, log it anyway in case its a bug
94 None => {
95 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);
96 return Ok(());
97 }
98 };
99
100 tracing::debug!(
101 "Creating fee payer UTXO with amount {} ({} sat/vb)",
102 new_fee_payer_amount,
103 fee_rate
104 );
105
106 let fee_payer_tx = Transaction {
107 version: Version::TWO,
108 lock_time: LockTime::ZERO,
109 input: vec![],
110 output: vec![TxOut {
111 value: new_fee_payer_amount,
112 script_pubkey: self.signer.address().script_pubkey(),
113 }],
114 };
115
116 // Manually serialize in legacy format for 0-input transactions
117 // Because fund_raw_transaction RPC gives deserialization error for 0-input transactions with segwit flag
118 // but in the end fund_raw_transaction returns a segwit transaction after adding inputs
119 let fee_payer_bytes = if fee_payer_tx.input.is_empty() {
120 use bitcoin::consensus::Encodable;
121 let mut buf = Vec::new();
122 // Serialize version
123 fee_payer_tx
124 .version
125 .consensus_encode(&mut buf)
126 .expect("Failed to serialize version");
127 fee_payer_tx
128 .input
129 .consensus_encode(&mut buf)
130 .expect("Failed to serialize inputs");
131 fee_payer_tx
132 .output
133 .consensus_encode(&mut buf)
134 .expect("Failed to serialize outputs");
135 // Serialize locktime
136 fee_payer_tx
137 .lock_time
138 .consensus_encode(&mut buf)
139 .expect("Failed to serialize locktime");
140
141 buf
142 } else {
143 bitcoin::consensus::encode::serialize(&fee_payer_tx)
144 };
145
146 let funded_fee_payer_tx = self
147 .rpc
148 .fund_raw_transaction(
149 &fee_payer_bytes,
150 Some(&FundRawTransactionOptions {
151 add_inputs: Some(true),
152 change_address: None,
153 change_position: None,
154 change_type: None,
155 include_watching: None,
156 lock_unspents: None,
157 fee_rate: Some(Amount::from_sat(fee_rate.to_sat_per_vb_ceil() * 1000)),
158 subtract_fee_from_outputs: None,
159 replaceable: Some(true),
160 conf_target: None,
161 estimate_mode: None,
162 }),
163 None,
164 )
165 .await
166 .wrap_err("Failed to fund cpfp fee payer tx")?
167 .hex;
168
169 let signed_fee_payer_tx: Transaction = bitcoin::consensus::deserialize(
170 &self
171 .rpc
172 .sign_raw_transaction_with_wallet(&funded_fee_payer_tx, None, None)
173 .await
174 .wrap_err("Failed to sign funded tx through bitcoin RPC")?
175 .hex,
176 )
177 .wrap_err("Failed to deserialize signed tx")?;
178
179 let outpoint_vout = signed_fee_payer_tx
180 .output
181 .iter()
182 .position(|o| {
183 o.value == new_fee_payer_amount
184 && o.script_pubkey == self.signer.address().script_pubkey()
185 })
186 .ok_or(eyre!("Failed to find outpoint vout"))?;
187
188 self.rpc
189 .send_raw_transaction(&signed_fee_payer_tx)
190 .await
191 .wrap_err("Failed to send signed fee payer tx")?;
192
193 self.db
194 .save_fee_payer_tx(
195 dbtx,
196 bumped_id,
197 signed_fee_payer_tx.compute_txid(),
198 outpoint_vout as u32,
199 new_fee_payer_amount,
200 None,
201 )
202 .await
203 .map_to_eyre()?;
204
205 Ok(())
206 }
207
208 /// Creates a Child-Pays-For-Parent (CPFP) child transaction.
209 ///
210 /// This transaction spends:
211 /// 1. The designated "P2A anchor" output of the parent transaction (`p2a_anchor`).
212 /// 2. One or more confirmed "fee payer" UTXOs (`fee_payer_utxos`) controlled by the `signer`.
213 ///
214 /// It calculates the total fee required (`required_fee`) to make the combined parent + child
215 /// package attractive to miners at the target `fee_rate`. The `required_fee` is paid entirely
216 /// by this child transaction.
217 ///
218 /// The remaining value (total input value - `required_fee`) is sent to the `change_address`.
219 ///
220 /// # Signing
221 /// We sign the input spending the P2A anchor and all fee payer UTXOs.
222 ///
223 /// # Returns
224 /// The constructed and partially signed child transaction.
225 async fn create_child_tx(
226 &self,
227 p2a_anchor: OutPoint,
228 anchor_sat: Amount,
229 fee_payer_utxos: Vec<B::SpendableInput>,
230 parent_tx_size: Weight,
231 fee_rate: FeeRate,
232 ) -> Result<Transaction> {
233 let required_fee = Self::calculate_required_fee(
234 parent_tx_size,
235 fee_payer_utxos.len(),
236 fee_rate,
237 FeePayingType::CPFP,
238 )?;
239
240 let change_address = self
241 .rpc
242 .get_new_wallet_address()
243 .await
244 .wrap_err("Failed to get new wallet address")?;
245
246 let total_fee_payer_amount = fee_payer_utxos
247 .iter()
248 .map(|utxo| utxo.get_prevout().value)
249 .sum::<Amount>()
250 + anchor_sat;
251
252 if change_address.script_pubkey().minimal_non_dust() + required_fee > total_fee_payer_amount
253 {
254 return Err(SendTxError::InsufficientFeePayerAmount);
255 }
256
257 // Delegate to the TxBuilder's static method
258 B::build_child_tx(
259 p2a_anchor,
260 anchor_sat,
261 fee_payer_utxos,
262 change_address,
263 required_fee,
264 &self.signer,
265 )
266 .map_err(|e| SendTxError::Other(e.into()))
267 }
268
269 /// Creates a transaction package for CPFP submission.
270 ///
271 /// Finds the P2A anchor output in the parent transaction (`tx`), then constructs
272 /// the child transaction using `create_child_tx`.
273 ///
274 /// # Returns
275 ///
276 /// - [`Vec<Transaction>`]: Parent transaction followed by the child
277 /// transaction ready for submission via the `submitpackage` RPC.
278 async fn create_package(
279 &self,
280 tx: Transaction,
281 fee_rate: FeeRate,
282 fee_payer_utxos: Vec<B::SpendableInput>,
283 ) -> Result<Vec<Transaction>> {
284 let txid = tx.compute_txid();
285 let p2a_vout = self
286 .find_p2a_vout(&tx)
287 .map_err(|e: BridgeError| SendTxError::Other(e.into()))?;
288 let anchor_sat = tx.output[p2a_vout].value;
289
290 let child_tx = self
291 .create_child_tx(
292 OutPoint {
293 txid,
294 vout: p2a_vout as u32,
295 },
296 anchor_sat,
297 fee_payer_utxos,
298 tx.weight(),
299 fee_rate,
300 )
301 .await?;
302
303 Ok(vec![tx, child_tx])
304 }
305
306 /// Retrieves confirmed fee payer UTXOs associated with a specific send attempt.
307 ///
308 /// Queries the database for UTXOs linked to `try_to_send_id` that are marked as confirmed.
309 /// These UTXOs are controlled by the `TxSender`'s `signer` and are intended to be
310 /// spent by a CPFP child transaction.
311 ///
312 /// # Returns
313 ///
314 /// - [`Vec<B::SpendableInput>`]: [`B::SpendableInput`]s of the confirmed fee payer
315 /// UTXOs that are ready to be included as inputs in the CPFP child tx.
316 async fn get_confirmed_fee_payer_utxos(
317 &self,
318 try_to_send_id: u32,
319 ) -> Result<Vec<B::SpendableInput>> {
320 let utxos = self
321 .db
322 .get_confirmed_fee_payer_utxos(None, try_to_send_id)
323 .await
324 .map_err(|e: BridgeError| SendTxError::Other(e.into()))?;
325 Ok(B::utxos_to_spendable_inputs(utxos, self.signer.address()))
326 }
327
328 /// Attempts to bump the fees of unconfirmed "fee payer" UTXOs using RBF.
329 ///
330 /// Fee payer UTXOs are created to fund CPFP child transactions. However, these
331 /// fee payer creation transactions might themselves get stuck due to low fees.
332 /// This function identifies such unconfirmed fee payer transactions associated with
333 /// a parent transaction (`bumped_id`) and attempts to RBF them using the provided `fee_rate`.
334 ///
335 /// This ensures the fee payer UTXOs confirm quickly, making them available to be spent
336 /// by the actual CPFP child transaction.
337 ///
338 /// # Arguments
339 /// * `fee_rate` - The target fee rate for bumping the fee payer transactions.
340 #[tracing::instrument(skip_all, fields(fee_rate))]
341 pub async fn bump_fees_of_unconfirmed_fee_payer_txs(&self, fee_rate: FeeRate) -> Result<()> {
342 let bumpable_txs = self
343 .db
344 .get_all_unconfirmed_fee_payer_txs(None)
345 .await
346 .map_err(|e: BridgeError| SendTxError::Other(e.into()))?;
347 let mut not_evicted_ids = HashSet::new();
348 let mut all_parent_ids = HashSet::new();
349
350 for (id, try_to_send_id, fee_payer_txid, vout, amount, replacement_of_id) in bumpable_txs {
351 tracing::debug!(
352 "Bumping fee for fee payer tx {} for try to send id {} for fee rate {}",
353 fee_payer_txid,
354 try_to_send_id,
355 fee_rate
356 );
357 let parent_id = replacement_of_id.unwrap_or(id);
358 all_parent_ids.insert(parent_id);
359
360 match self.rpc.get_mempool_entry(&fee_payer_txid).await {
361 Ok(info) => {
362 not_evicted_ids.insert(parent_id);
363 // if it has descendants, it cannot be bumped, or if it was bumped recently, we should not bump it again
364 if info.descendant_count > 1
365 || std::time::SystemTime::now()
366 .duration_since(std::time::UNIX_EPOCH)
367 .unwrap()
368 .as_secs()
369 .saturating_sub(info.time)
370 < self.tx_sender_limits.cpfp_fee_payer_bump_wait_time_seconds
371 {
372 continue;
373 }
374 }
375 Err(e) => {
376 // If not in mempool we should ignore, it was either evicted or replaced by a bumped feepayer tx
377 // give an error if the error is not "Transaction not in mempool"
378 if !e.to_string().contains("Transaction not in mempool") {
379 return Err(
380 eyre!("Failed to get mempool entry for {fee_payer_txid}: {e}").into(),
381 );
382 }
383 // get_transaction only returns if tx is wallet owned, it should not be an issue here as if it is not wallet owned,
384 // for example if wallet was changed and txsender restarted, it cannot be bumped anyway
385 if let Ok(tx_info) = self.rpc.get_transaction(&fee_payer_txid, None).await {
386 if tx_info.info.blockhash.is_some() && tx_info.info.confirmations > 0 {
387 not_evicted_ids.insert(parent_id);
388 }
389 }
390 continue;
391 }
392 }
393
394 match self
395 .rpc
396 .bump_fee_with_fee_rate(fee_payer_txid, fee_rate)
397 .await
398 {
399 Ok(new_txid) => {
400 if new_txid != fee_payer_txid {
401 self.db
402 .save_fee_payer_tx(
403 None,
404 try_to_send_id,
405 new_txid,
406 vout,
407 amount,
408 Some(parent_id),
409 )
410 .await
411 .map_err(|e: BridgeError| SendTxError::Other(e.into()))?;
412 } else {
413 tracing::trace!(
414 "Fee payer tx {} has enough fee, no need to bump",
415 fee_payer_txid
416 );
417 }
418 }
419 Err(e) => match e {
420 BitcoinRPCError::TransactionAlreadyInBlock(block_hash) => {
421 tracing::debug!(
422 "Fee payer tx {} is already in block {}, skipping",
423 fee_payer_txid,
424 block_hash
425 );
426 continue;
427 }
428 BitcoinRPCError::BumpFeeUTXOSpent(outpoint) => {
429 tracing::debug!(
430 "Fee payer tx {} is already onchain, skipping: {:?}",
431 fee_payer_txid,
432 outpoint
433 );
434 continue;
435 }
436 _ => {
437 tracing::warn!(
438 "Failed to bump fee the fee payer tx {} with error {e}, skipping",
439 fee_payer_txid
440 );
441 continue;
442 }
443 },
444 }
445 }
446
447 for parent_id in all_parent_ids {
448 if !not_evicted_ids.contains(&parent_id) {
449 self.db
450 .mark_fee_payer_utxo_as_evicted(None, parent_id)
451 .await
452 .map_err(|e: BridgeError| SendTxError::Other(e.into()))?;
453 }
454 }
455 Ok(())
456 }
457
458 /// Sends a transaction using the Child-Pays-For-Parent (CPFP) strategy.
459 ///
460 /// # Logic:
461 /// 1. **Check Unconfirmed Fee Payers:** Ensures no unconfirmed fee payer UTXOs exist
462 /// for this `try_to_send_id`. If they do, skips this transaction for now
463 /// as they need to confirm before being spendable by the child.
464 /// 2. **Get Confirmed Fee Payers:** Retrieves the available confirmed fee payer UTXOs.
465 /// 3. **Create Package:** Calls `create_package` to build the `vec![parent_tx, child_tx]`.
466 /// The `child_tx` spends the parent's anchor output and the fee payer UTXOs, paying
467 /// a fee calculated for the whole package.
468 /// 4. **Test Mempool Accept (Not implemented right now as testmempoolaccept didn't support TRUC package submission #1011):**
469 /// Uses `testmempoolaccept` RPC to check if the package is likely to be accepted by the network before submitting.
470 /// 5. **Submit Package:** Uses the `submitpackage` RPC to atomically submit the parent
471 /// and child transactions. Bitcoin Core evaluates the fee rate of the package together.
472 /// 6. **Handle Results:** Checks the `submitpackage` result. If successful or already in
473 /// mempool, updates the effective fee rate in the database. If failed, logs an error.
474 ///
475 /// # Arguments
476 /// * `try_to_send_id` - The database ID tracking this send attempt.
477 /// * `tx` - The parent transaction requiring the fee bump.
478 /// * `tx_metadata` - Optional metadata associated with the transaction.
479 /// * `fee_rate` - The target fee rate for the CPFP package.
480 /// * `current_tip_height` - The current height of the tip of the chain.
481 #[tracing::instrument(skip_all, fields(try_to_send_id, tx_meta=?tx_metadata))]
482 pub async fn send_cpfp_tx(
483 &self,
484 try_to_send_id: u32,
485 tx: Transaction,
486 tx_metadata: Option<TxMetadata>,
487 fee_rate: FeeRate,
488 current_tip_height: u32,
489 ) -> Result<()> {
490 let unconfirmed = self
491 .db
492 .get_unconfirmed_fee_payer_txs(None, try_to_send_id)
493 .await
494 .map_err(|e: BridgeError| SendTxError::Other(e.into()))?;
495 if !unconfirmed.is_empty() {
496 // Log that we're waiting for unconfirmed UTXOs
497 tracing::debug!(
498 try_to_send_id,
499 "Waiting for {} UTXOs to confirm",
500 unconfirmed.len()
501 );
502
503 let _ = self
504 .db
505 .update_tx_debug_sending_state(
506 try_to_send_id,
507 "waiting_for_utxo_confirmation",
508 true,
509 )
510 .await;
511 return Ok(());
512 }
513
514 let confirmed = self.get_confirmed_fee_payer_utxos(try_to_send_id).await?;
515 let total_amount: Amount = confirmed.iter().map(|u| u.get_prevout().value).sum();
516
517 let _ = self
518 .db
519 .update_tx_debug_sending_state(try_to_send_id, "creating_package", true)
520 .await;
521
522 let package = match self
523 .create_package(tx.clone(), fee_rate, confirmed.clone())
524 .await
525 {
526 Ok(p) => p,
527 Err(SendTxError::InsufficientFeePayerAmount) => {
528 self.create_fee_payer_utxo(
529 try_to_send_id,
530 None,
531 &tx,
532 fee_rate,
533 total_amount,
534 confirmed.len(),
535 )
536 .await?;
537 let _ = self
538 .db
539 .update_tx_debug_sending_state(
540 try_to_send_id,
541 "waiting_for_fee_payer_utxos",
542 true,
543 )
544 .await;
545 return Ok(());
546 }
547 Err(e) => {
548 tracing::error!(try_to_send_id, "Failed to create CPFP package: {:?}", e);
549 return Err(e);
550 }
551 };
552
553 let package_refs: Vec<&Transaction> = package.iter().collect();
554
555 tracing::debug!(
556 try_to_send_id,
557 "Submitting package\n Pkg tx hexs: {:?}",
558 if env::var("DBG_PACKAGE_HEX").is_ok() {
559 package
560 .iter()
561 .map(|tx| hex::encode(bitcoin::consensus::serialize(tx)))
562 .collect::<Vec<_>>()
563 } else {
564 vec!["use DBG_PACKAGE_HEX=1 to print the package as hex".into()]
565 }
566 );
567
568 // Save the effective fee rate before attempting to send
569 // This ensures that even if the send fails, we track the attempt
570 // so the 10-block stuck logic can trigger a bump
571 self.db
572 .update_effective_fee_rate(None, try_to_send_id, fee_rate, current_tip_height)
573 .await
574 .wrap_err("Failed to update effective fee rate")?;
575
576 // Update sending state to submitting_package
577 let _ = self
578 .db
579 .update_tx_debug_sending_state(try_to_send_id, "submitting_package", true)
580 .await;
581
582 let submit_result = self
583 .rpc
584 .submit_package(&package_refs, Some(Amount::ZERO), None)
585 .await
586 .wrap_err("Failed to submit package")?;
587
588 // If tx_results is empty, it means the txs were already accepted by the network.
589 if submit_result.tx_results.is_empty() {
590 return Ok(());
591 }
592
593 for (_txid, result) in submit_result.tx_results {
594 if let PackageTransactionResult::Failure { error, .. } = result {
595 tracing::error!(
596 try_to_send_id,
597 "Error submitting package: {:?}, package: {:?}",
598 error,
599 package_refs
600 .iter()
601 .map(|tx| hex::encode(bitcoin::consensus::serialize(tx)))
602 .collect::<Vec<_>>()
603 );
604 }
605 }
606
607 Ok(())
608 }
609}