1use super::Result;
21use crate::{TxSender, TxSenderTransaction};
22use bitcoin::absolute::LockTime;
23use bitcoin::sighash::{Prevouts, SighashCache};
24use bitcoin::taproot;
25use bitcoin::transaction::Version;
26use bitcoin::{Amount, OutPoint, ScriptBuf, Transaction, TxIn, TxOut, Weight};
27use bitcoin::{TapSighashType, Witness};
28use bitcoincore_rpc::json::FundRawTransactionOptions;
29use bitcoincore_rpc::{PackageTransactionResult, RpcApi};
30use clementine_errors::{BitcoinRPCError, BridgeError, ResultExt as _, SendTxError};
31use clementine_primitives::FeeRateKvb;
32use clementine_primitives::{MIN_TAPROOT_AMOUNT, NON_STANDARD_V3};
33use clementine_utils::{FeePayingType, TxMetadata};
34use eyre::{eyre, Context};
35use std::collections::HashSet;
36use std::env;
37
38impl TxSender {
39 fn anchor_prevout(anchor_sat: Amount) -> TxOut {
40 TxOut {
42 value: anchor_sat,
43 script_pubkey: ScriptBuf::from_hex("51024e73").expect("statically valid anchor script"),
44 }
45 }
46
47 fn build_and_sign_child_tx(
48 &self,
49 p2a_anchor: OutPoint,
50 anchor_sat: Amount,
51 fee_payer_utxos: Vec<crate::SpendableUtxo>,
52 change_address: bitcoin::Address,
53 required_fee: Amount,
54 ) -> Result<Transaction> {
55 let total_in: Amount = fee_payer_utxos
56 .iter()
57 .map(|u| u.txout.value)
58 .sum::<Amount>()
59 + anchor_sat;
60
61 let change_amount = total_in
62 .checked_sub(required_fee)
63 .ok_or_else(|| SendTxError::Other(eyre!("required_fee > total_in")))?;
64
65 let mut inputs: Vec<TxIn> = Vec::with_capacity(1 + fee_payer_utxos.len());
66 inputs.push(TxIn {
67 previous_output: p2a_anchor,
68 script_sig: ScriptBuf::new(),
69 sequence: crate::DEFAULT_SEQUENCE,
70 witness: Witness::new(),
71 });
72
73 for utxo in &fee_payer_utxos {
74 inputs.push(TxIn {
75 previous_output: utxo.outpoint,
76 script_sig: ScriptBuf::new(),
77 sequence: crate::DEFAULT_SEQUENCE,
78 witness: Witness::new(),
79 });
80 }
81
82 let mut child_tx = Transaction {
83 version: NON_STANDARD_V3,
84 lock_time: LockTime::ZERO,
85 input: inputs,
86 output: vec![TxOut {
87 value: change_amount,
88 script_pubkey: change_address.script_pubkey(),
89 }],
90 };
91
92 let mut prevouts: Vec<TxOut> = Vec::with_capacity(child_tx.input.len());
94 prevouts.push(Self::anchor_prevout(anchor_sat));
95 prevouts.extend(fee_payer_utxos.into_iter().map(|u| u.txout));
96
97 let mut cache = SighashCache::new(&child_tx);
99 let mut signed_witnesses: Vec<(usize, Witness)> = Vec::new();
100
101 for input_index in 1..child_tx.input.len() {
102 let sighash = cache
103 .taproot_key_spend_signature_hash(
104 input_index,
105 &Prevouts::All(&prevouts),
106 TapSighashType::Default,
107 )
108 .map_err(|e| SendTxError::Other(eyre!("failed to compute sighash: {e}")))?;
109
110 let signature = self
111 .signer
112 .sign_with_tweak_data(sighash, clementine_utils::sign::TapTweakData::KeyPath(None))
113 .map_err(|e| SendTxError::Other(e.into()))?;
114
115 let tr_sig = taproot::Signature {
116 signature,
117 sighash_type: TapSighashType::Default,
118 };
119 signed_witnesses.push((input_index, Witness::p2tr_key_spend(&tr_sig)));
120 }
121
122 for (idx, witness) in signed_witnesses {
123 child_tx.input[idx].witness = witness;
124 }
125
126 Ok(child_tx)
127 }
128
129 async fn create_fee_payer_utxo(
147 &self,
148 bumped_id: u32,
149 dbtx: Option<&mut TxSenderTransaction>,
150 tx: &Transaction,
151 fee_rate: FeeRateKvb,
152 total_fee_payer_amount: Amount,
153 fee_payer_utxos_len: usize,
154 ) -> Result<()> {
155 tracing::debug!(
156 "Creating fee payer UTXO for txid {} with bump id {}",
157 &tx.compute_txid().to_string(),
158 bumped_id
159 );
160 let required_fee = Self::calculate_required_fee(
161 tx.weight(),
162 fee_payer_utxos_len + 1,
163 fee_rate,
164 FeePayingType::CPFP,
165 )?;
166
167 let new_total_fee_needed = required_fee
172 .checked_mul(2)
173 .and_then(|fee| fee.checked_add(MIN_TAPROOT_AMOUNT));
174 if new_total_fee_needed.is_none() {
175 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());
176 }
177 let new_fee_payer_amount =
178 new_total_fee_needed.and_then(|fee| fee.checked_sub(total_fee_payer_amount));
179
180 let new_fee_payer_amount = match new_fee_payer_amount {
181 Some(fee) => fee,
182 None => {
184 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);
185 return Ok(());
186 }
187 };
188
189 tracing::debug!(
190 "Creating fee payer UTXO with amount {} ({} sat/kvB)",
191 new_fee_payer_amount,
192 fee_rate
193 );
194
195 let fee_payer_tx = Transaction {
196 version: Version::TWO,
197 lock_time: LockTime::ZERO,
198 input: vec![],
199 output: vec![TxOut {
200 value: new_fee_payer_amount,
201 script_pubkey: self.signer.address().script_pubkey(),
202 }],
203 };
204
205 let fee_payer_bytes = crate::serialize_tx_for_fund_raw(&fee_payer_tx);
206
207 let funded_fee_payer_tx = self
208 .rpc
209 .fund_raw_transaction(
210 &fee_payer_bytes,
211 Some(&FundRawTransactionOptions {
212 add_inputs: Some(true),
213 include_unsafe: Some(self.include_unsafe),
215 change_address: None,
216 change_position: None,
217 change_type: None,
218 include_watching: None,
219 lock_unspents: None,
220 fee_rate: Some(Amount::from_sat(fee_rate.to_sat_per_kvb())),
221 subtract_fee_from_outputs: None,
222 replaceable: Some(true),
223 conf_target: None,
224 estimate_mode: None,
225 }),
226 None,
227 )
228 .await
229 .wrap_err("Failed to fund cpfp fee payer tx")?
230 .hex;
231
232 let signed_fee_payer_tx: Transaction = bitcoin::consensus::deserialize(
233 &self
234 .rpc
235 .sign_raw_transaction_with_wallet(&funded_fee_payer_tx, None, None)
236 .await
237 .wrap_err("Failed to sign funded tx through bitcoin RPC")?
238 .hex,
239 )
240 .wrap_err("Failed to deserialize signed tx")?;
241
242 let outpoint_vout = signed_fee_payer_tx
243 .output
244 .iter()
245 .position(|o| {
246 o.value == new_fee_payer_amount
247 && o.script_pubkey == self.signer.address().script_pubkey()
248 })
249 .ok_or(eyre!("Failed to find outpoint vout"))?;
250
251 self.rpc
252 .send_raw_transaction(&signed_fee_payer_tx)
253 .await
254 .wrap_err("Failed to send signed fee payer tx")?;
255
256 self.db
257 .save_fee_payer_tx(
258 dbtx,
259 bumped_id,
260 signed_fee_payer_tx.compute_txid(),
261 outpoint_vout as u32,
262 new_fee_payer_amount,
263 None,
264 )
265 .await
266 .map_to_eyre()?;
267
268 Ok(())
269 }
270
271 async fn create_child_tx(
289 &self,
290 p2a_anchor: OutPoint,
291 anchor_sat: Amount,
292 fee_payer_utxos: Vec<crate::SpendableUtxo>,
293 parent_tx_size: Weight,
294 fee_rate: FeeRateKvb,
295 ) -> Result<Transaction> {
296 let required_fee = Self::calculate_required_fee(
297 parent_tx_size,
298 fee_payer_utxos.len(),
299 fee_rate,
300 FeePayingType::CPFP,
301 )?;
302
303 let change_address = self
304 .rpc
305 .get_new_wallet_address()
306 .await
307 .wrap_err("Failed to get new wallet address")?;
308
309 let total_fee_payer_amount = fee_payer_utxos
310 .iter()
311 .map(|utxo| utxo.txout.value)
312 .sum::<Amount>()
313 + anchor_sat;
314
315 if change_address.script_pubkey().minimal_non_dust() + required_fee > total_fee_payer_amount
316 {
317 return Err(SendTxError::InsufficientFeePayerAmount);
318 }
319
320 self.build_and_sign_child_tx(
321 p2a_anchor,
322 anchor_sat,
323 fee_payer_utxos,
324 change_address,
325 required_fee,
326 )
327 }
328
329 async fn create_package(
339 &self,
340 tx: Transaction,
341 fee_rate: FeeRateKvb,
342 fee_payer_utxos: Vec<crate::SpendableUtxo>,
343 ) -> Result<Vec<Transaction>> {
344 let txid = tx.compute_txid();
345 let p2a_vout = self
346 .find_p2a_vout(&tx)
347 .map_err(|e: BridgeError| SendTxError::Other(e.into()))?;
348 let anchor_sat = tx.output[p2a_vout].value;
349
350 let child_tx = self
351 .create_child_tx(
352 OutPoint {
353 txid,
354 vout: p2a_vout as u32,
355 },
356 anchor_sat,
357 fee_payer_utxos,
358 tx.weight(),
359 fee_rate,
360 )
361 .await?;
362
363 Ok(vec![tx, child_tx])
364 }
365
366 async fn get_confirmed_fee_payer_utxos(
377 &self,
378 try_to_send_id: u32,
379 ) -> Result<Vec<crate::SpendableUtxo>> {
380 let utxos = self
381 .db
382 .get_confirmed_fee_payer_utxos(None, try_to_send_id)
383 .await
384 .map_err(|e: BridgeError| SendTxError::Other(e.into()))?;
385
386 let mut spendables = Vec::with_capacity(utxos.len());
387
388 for (txid, vout, _db_amount) in utxos {
389 let utxo = self
390 .rpc
391 .get_tx_out(&txid, vout, Some(false))
392 .await
393 .wrap_err("Failed to gettxout for fee payer utxo")?;
394
395 let Some(utxo) = utxo else {
396 return Err(SendTxError::Other(eyre!(
399 "Confirmed fee payer UTXO missing from gettxout: {txid}:{vout}"
400 )));
401 };
402
403 let script_pubkey = utxo
404 .script_pub_key
405 .script()
406 .wrap_err("Failed to parse script pubkey from gettxout")?;
407
408 spendables.push(crate::SpendableUtxo {
409 outpoint: OutPoint { txid, vout },
410 txout: TxOut {
411 value: utxo.value,
412 script_pubkey,
413 },
414 spend_info: None,
415 });
416 }
417
418 Ok(spendables)
419 }
420
421 #[tracing::instrument(skip_all, fields(fee_rate))]
434 pub async fn bump_fees_of_unconfirmed_fee_payer_txs(&self, fee_rate: FeeRateKvb) -> Result<()> {
435 let bumpable_txs = self
436 .db
437 .get_all_unconfirmed_fee_payer_txs(None)
438 .await
439 .map_err(|e: BridgeError| SendTxError::Other(e.into()))?;
440 let mut not_evicted_ids = HashSet::new();
441 let mut all_parent_ids = HashSet::new();
442
443 for (id, try_to_send_id, fee_payer_txid, vout, amount, replacement_of_id) in bumpable_txs {
444 tracing::debug!(
445 "Bumping fee for fee payer tx {} for try to send id {} for fee rate {}",
446 fee_payer_txid,
447 try_to_send_id,
448 fee_rate
449 );
450 let parent_id = replacement_of_id.unwrap_or(id);
451 all_parent_ids.insert(parent_id);
452
453 match self.rpc.get_mempool_entry(&fee_payer_txid).await {
454 Ok(info) => {
455 not_evicted_ids.insert(parent_id);
456 if info.descendant_count > 1
458 || std::time::SystemTime::now()
459 .duration_since(std::time::UNIX_EPOCH)
460 .unwrap()
461 .as_secs()
462 .saturating_sub(info.time)
463 < self.tx_sender_limits.cpfp_fee_payer_bump_wait_time_seconds
464 {
465 continue;
466 }
467 }
468 Err(e) => {
469 if !e.to_string().contains("Transaction not in mempool") {
472 return Err(
473 eyre!("Failed to get mempool entry for {fee_payer_txid}: {e}").into(),
474 );
475 }
476 if let Ok(tx_info) = self.rpc.get_transaction(&fee_payer_txid, None).await {
479 if tx_info.info.blockhash.is_some() && tx_info.info.confirmations > 0 {
480 not_evicted_ids.insert(parent_id);
481 }
482 }
483 continue;
484 }
485 }
486
487 match self
488 .rpc
489 .bump_fee_with_fee_rate(fee_payer_txid, fee_rate)
490 .await
491 {
492 Ok(new_txid) => {
493 if new_txid != fee_payer_txid {
494 self.db
495 .save_fee_payer_tx(
496 None,
497 try_to_send_id,
498 new_txid,
499 vout,
500 amount,
501 Some(parent_id),
502 )
503 .await
504 .map_err(|e: BridgeError| SendTxError::Other(e.into()))?;
505 } else {
506 tracing::trace!(
507 "Fee payer tx {} has enough fee, no need to bump",
508 fee_payer_txid
509 );
510 }
511 }
512 Err(e) => match e {
513 BitcoinRPCError::TransactionAlreadyInBlock(block_hash) => {
514 tracing::debug!(
515 "Fee payer tx {} is already in block {}, skipping",
516 fee_payer_txid,
517 block_hash
518 );
519 continue;
520 }
521 BitcoinRPCError::BumpFeeUTXOSpent(outpoint) => {
522 tracing::debug!(
523 "Fee payer tx {} is already onchain, skipping: {:?}",
524 fee_payer_txid,
525 outpoint
526 );
527 continue;
528 }
529 _ => {
530 tracing::warn!(
531 "Failed to bump fee the fee payer tx {} with error {e}, skipping",
532 fee_payer_txid
533 );
534 continue;
535 }
536 },
537 }
538 }
539
540 for parent_id in all_parent_ids {
541 if !not_evicted_ids.contains(&parent_id) {
542 self.db
543 .mark_fee_payer_utxo_as_evicted(None, parent_id)
544 .await
545 .map_err(|e: BridgeError| SendTxError::Other(e.into()))?;
546 }
547 }
548 Ok(())
549 }
550
551 #[tracing::instrument(skip_all, fields(try_to_send_id, tx_meta=?tx_metadata))]
575 pub async fn send_cpfp_tx(
576 &self,
577 try_to_send_id: u32,
578 tx: Transaction,
579 tx_metadata: Option<TxMetadata>,
580 fee_rate: FeeRateKvb,
581 current_tip_height: u32,
582 ) -> Result<()> {
583 let unconfirmed = self
584 .db
585 .get_unconfirmed_fee_payer_txs(None, try_to_send_id)
586 .await
587 .map_err(|e: BridgeError| SendTxError::Other(e.into()))?;
588 if !unconfirmed.is_empty() {
589 tracing::debug!(
591 try_to_send_id,
592 "Waiting for {} UTXOs to confirm",
593 unconfirmed.len()
594 );
595
596 let _ = self
597 .db
598 .update_tx_debug_sending_state(
599 try_to_send_id,
600 "waiting_for_utxo_confirmation",
601 true,
602 )
603 .await;
604 return Ok(());
605 }
606
607 let confirmed = self.get_confirmed_fee_payer_utxos(try_to_send_id).await?;
608 let total_amount: Amount = confirmed.iter().map(|u| u.txout.value).sum();
609
610 let _ = self
611 .db
612 .update_tx_debug_sending_state(try_to_send_id, "creating_package", true)
613 .await;
614
615 let package = match self
616 .create_package(tx.clone(), fee_rate, confirmed.clone())
617 .await
618 {
619 Ok(p) => p,
620 Err(SendTxError::InsufficientFeePayerAmount) => {
621 self.create_fee_payer_utxo(
622 try_to_send_id,
623 None,
624 &tx,
625 fee_rate,
626 total_amount,
627 confirmed.len(),
628 )
629 .await?;
630 let _ = self
631 .db
632 .update_tx_debug_sending_state(
633 try_to_send_id,
634 "waiting_for_fee_payer_utxos",
635 true,
636 )
637 .await;
638 return Ok(());
639 }
640 Err(e) => {
641 tracing::error!(try_to_send_id, "Failed to create CPFP package: {:?}", e);
642 return Err(e);
643 }
644 };
645
646 let package_refs: Vec<&Transaction> = package.iter().collect();
647
648 tracing::debug!(
649 try_to_send_id,
650 "Submitting package\n Pkg tx hexs: {:?}",
651 if env::var("DBG_PACKAGE_HEX").is_ok() {
652 package
653 .iter()
654 .map(|tx| hex::encode(bitcoin::consensus::serialize(tx)))
655 .collect::<Vec<_>>()
656 } else {
657 vec!["use DBG_PACKAGE_HEX=1 to print the package as hex".into()]
658 }
659 );
660
661 self.db
665 .update_effective_fee_rate(None, try_to_send_id, fee_rate, current_tip_height)
666 .await
667 .wrap_err("Failed to update effective fee rate")?;
668
669 let _ = self
671 .db
672 .update_tx_debug_sending_state(try_to_send_id, "submitting_package", true)
673 .await;
674
675 let submit_result = self
676 .rpc
677 .submit_package(&package_refs, Some(Amount::ZERO), None)
678 .await
679 .wrap_err("Failed to submit package")?;
680
681 if submit_result.tx_results.is_empty() {
683 return Ok(());
684 }
685
686 let mut package_errors = Vec::new();
687 let mut has_replacement_error = false;
688
689 for result in submit_result.tx_results.into_values() {
690 if let PackageTransactionResult::Failure { error, .. } = result {
691 if crate::rpc_errors::is_rejecting_replacement_error(&error) {
692 has_replacement_error = true;
693 }
694 package_errors.push(error);
695 }
696 }
697
698 if has_replacement_error {
699 tracing::debug!(
700 try_to_send_id,
701 "Package tx rejected (tx already in mempool): {:?}",
702 package_errors
703 );
704 return Ok(());
705 }
706
707 if !package_errors.is_empty() {
708 return Err(SendTxError::Other(eyre!(
709 "Failed to submit package: {:?}, package: {:?}",
710 package_errors,
711 package_refs
712 .iter()
713 .map(|tx| hex::encode(bitcoin::consensus::serialize(tx)))
714 .collect::<Vec<_>>()
715 )));
716 }
717
718 Ok(())
719 }
720}