Skip to main content

bark/exit/
bdk.rs

1use bitcoin::FeeRate;
2use log::warn;
3
4use crate::Wallet;
5use crate::exit::{Exit, ExitProgressStatus};
6use crate::onchain::{CpfpError, ExitUnilaterally, MakeCpfpFees};
7
8impl Exit {
9	/// Advance ongoing exits by one step, handling CPFP fee-bumping via an onchain wallet.
10	///
11	/// This makes progress on each exit but does not run an exit to completion — exits
12	/// span many blocks (broadcasting, confirmations, CSV timelocks, claim spends), so
13	/// this must be called repeatedly (e.g. once per block) until all exits reach a
14	/// terminal state.
15	///
16	/// It calls [Exit::progress_exits], creates CPFP transactions via `onchain` for any
17	/// exits in [ExitTxStatus::AwaitingCpfpBroadcast], then calls [Exit::progress_exits]
18	/// again so those exits advance to [ExitTxStatus::AwaitingConfirmation].
19	///
20	/// Callers with external or hardware wallets should use [Exit::exits_needing_cpfp]
21	/// and [Exit::provide_cpfp_tx] directly instead.
22	pub async fn progress_exits_with_bdk(
23		&self,
24		wallet: &Wallet,
25		onchain: &mut dyn ExitUnilaterally,
26		fee_rate_override: Option<FeeRate>,
27	) -> anyhow::Result<Option<Vec<ExitProgressStatus>>> {
28		self.progress_exits(wallet).await?;
29
30		let fee_rate = fee_rate_override.unwrap_or(wallet.chain().fee_rates().await.fast);
31		for req in self.exits_needing_cpfp().await {
32			let fees = match req.rbf_requirement {
33				None => MakeCpfpFees::Effective(fee_rate),
34				Some(rbf) => {
35					// Only RBF if we can improve the fee rate; equal or lower rates are rejected
36					// by Bitcoin Core's RBF policy ("new feerate must be strictly greater").
37					if fee_rate <= rbf.min_fee_rate {
38						warn!(
39							"Skipping exit CPFP RBF: requested fee rate {} is not above current package rate {}",
40							fee_rate, rbf.min_fee_rate,
41						);
42						continue;
43					}
44					MakeCpfpFees::Rbf {
45						min_effective_fee_rate: fee_rate,
46						current_package_fee: rbf.current_package_fee,
47					}
48				},
49			};
50			let child_tx = match onchain.make_signed_p2a_cpfp(&req.exit_tx, fees) {
51				Ok(tx) => tx,
52				Err(CpfpError::InsufficientConfirmedFunds { needed, available }) => {
53					warn!("Insufficient funds for exit CPFP: needed {} available {}", needed, available);
54					continue;
55				},
56				Err(e) => return Err(e.into()),
57			};
58			onchain.store_signed_p2a_cpfp(&child_tx).await?;
59			let exit_txid = req.exit_tx.compute_txid();
60			self.provide_cpfp_tx(wallet, exit_txid, child_tx).await?;
61		}
62
63		self.progress_exits(wallet).await
64	}
65}