Skip to main content

bark/onchain/
mod.rs

1//! Onchain wallet integration interfaces.
2//!
3//! This module defines the traits and types that an external onchain wallet must
4//! implement to be used by the library. The goal is to let integrators plug in
5//! their own wallet implementation, so features like boarding (moving onchain funds
6//! into Ark) and unilateral exit (claiming VTXOs onchain without server cooperation)
7//! are supported.
8//!
9//! Key concepts exposed here:
10//! - [Utxo], [LocalUtxo] & [SpendableExit]: lightweight types representing wallet UTXOs and
11//!   spendable exit outputs.
12//! - [PreparePsbt] & [SignPsbt]: funding and signing interfaces for building transactions.
13//! - [GetBalance], [GetWalletTx], [GetSpendingTx]: read-access to wallet state for balance
14//!   and [Transaction] lookups required by various parts of the library.
15//! - [MakeCpfp]: CPFP construction interfaces used for create child transactions.
16//! - [ExitUnilaterally]: a convenience trait that aggregates the required capabilities a
17//!   wallet must provide to support unilateral exits.
18//!
19//! A reference implementation based on BDK is available behind the `onchain-bdk`
20//! cargo feature. Enable it to use the provided [OnchainWallet] implementation.
21//! You can use all features from BDK because [bdk_wallet] is re-exported.
22
23#[cfg(feature = "onchain-bdk")]
24mod bdk;
25
26#[cfg(feature = "onchain-bdk")]
27pub use bdk_wallet;
28
29pub use bitcoin_ext::cpfp::{CpfpError, MakeCpfpFees};
30
31/// BDK-backed onchain wallet implementation.
32///
33/// Available only when the `onchain-bdk` feature is enabled.
34#[cfg(feature = "onchain-bdk")]
35pub use crate::onchain::bdk::{OnchainWallet, TxBuilderExt};
36
37use std::sync::Arc;
38
39use bitcoin::{
40	Address, Amount, FeeRate, OutPoint, Psbt, SignedAmount, Transaction, Txid
41};
42
43use ark::Vtxo;
44use ark::vtxo::Full;
45use bitcoin_ext::{BlockHeight, BlockRef};
46
47use crate::chain::ChainSource;
48
49/// Summary of a wallet transaction produced by [OnchainWallet::list_transaction_infos].
50#[derive(Debug, Clone)]
51pub struct WalletTxInfo {
52	pub txid: Txid,
53	pub tx: Arc<Transaction>,
54	/// Total fee paid by the transaction, when computable. `None` for inbound or
55	/// collaboratively-funded txs whose foreign prevouts BDK has not indexed
56	/// (e.g. after a bitcoind-rpc sync — esplora syncs populate prevouts).
57	pub onchain_fees: Option<Amount>,
58	/// Net change to the wallet's balance: `received - sent` over wallet-owned outputs.
59	pub balance_change: SignedAmount,
60	/// `Some` if the transaction is confirmed in a block, `None` if still in the mempool.
61	pub confirmation: Option<BlockRef>,
62}
63
64/// Represents an onchain UTXO known to the wallet.
65///
66/// This can be either:
67/// - `Local`: a standard wallet UTXO
68/// - `Exit`: a spendable exit output produced by the Ark exit mechanism
69#[derive(Debug, Clone)]
70pub enum Utxo {
71	Local(LocalUtxo),
72	Exit(SpendableExit),
73}
74
75/// A standard wallet [Utxo] owned by the local wallet implementation.
76#[derive(Debug, Clone)]
77pub struct LocalUtxo {
78	/// The outpoint referencing the UTXO.
79	pub outpoint: OutPoint,
80	/// The amount contained in the UTXO.
81	pub amount: Amount,
82	/// Optional confirmation height; `None` if unconfirmed.
83	pub confirmation_height: Option<BlockHeight>,
84}
85
86/// A spendable unilateral exit of a [Vtxo] which can be claimed onchain.
87///
88/// When exiting unilaterally, the wallet will end up with onchain outputs that correspond to
89/// previously-held VTXOs. These can be claimed and used for further spending.
90#[derive(Debug, Clone)]
91pub struct SpendableExit {
92	/// The VTXO being exited.
93	pub vtxo: Vtxo<Full>,
94	/// The block height associated with the exits' validity window.
95	pub height: BlockHeight,
96}
97
98/// Ability to finalize a [Psbt] into a fully signed [Transaction].
99///
100/// Wallets should apply all necessary signatures and finalize inputs according
101/// to their internal key management and policies.
102#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
103#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
104pub trait SignPsbt {
105	/// Consume a [Psbt] and return a fully signed [Psbt] with all witnesses filled in.
106	///
107	/// Useful when the signed [Psbt] is needed after signing, e.g. to compute fees
108	/// via [Psbt::fee] before extracting the final [Transaction].
109	async fn finish_psbt(&mut self, psbt: Psbt) -> anyhow::Result<Psbt>;
110}
111
112/// Ability to query the wallets' total balance.
113///
114/// This is used by higher-level flows to decide when onchain funds are available for boarding or
115/// fee bumping, and to present balance information to users.
116pub trait GetBalance {
117	/// Get the total balance of the wallet.
118	fn get_balance(&self) -> Amount;
119}
120
121/// Ability to look up transactions known to the wallet.
122///
123/// Implementations should return wallet-related transactions and, when possible,
124/// the block information those transactions confirmed in.
125pub trait GetWalletTx {
126	/// Retrieve the wallet [Transaction] for the given [Txid] if any.
127	fn get_wallet_tx(&self, txid: Txid) -> Option<Arc<Transaction>>;
128
129	/// Retrieve information about the block, if any, a given wallet transaction was confirmed in.
130	fn get_wallet_tx_confirmed_block(&self, txid: Txid) -> anyhow::Result<Option<BlockRef>>;
131}
132
133/// Ability to construct funded PSBTs for specific destinations or to drain the wallet.
134///
135/// These methods are used to build transactions for boarding, exits, and fee bumping.
136pub trait PreparePsbt {
137	/// Prepare a [Transaction] which will send to the given destinations.
138	fn prepare_tx(
139		&mut self,
140		destinations: &[(Address, Amount)],
141		fee_rate: FeeRate,
142	) -> anyhow::Result<Psbt>;
143
144	/// Prepare a [Transaction] for sending all wallet funds to the given destination.
145	fn prepare_drain_tx(
146		&mut self,
147		destination: Address,
148		fee_rate: FeeRate,
149	) -> anyhow::Result<Psbt>;
150}
151
152/// Ability to find wallet-local spends of a specific [OutPoint].
153///
154/// This helps identify if the wallet has already spent an exit or parent [Transaction].
155pub trait GetSpendingTx {
156	/// This should search the wallet and look for any [Transaction] that spends the given
157	/// [OutPoint]. The intent of the function is to only look at spends which happen in the wallet
158	/// itself.
159	fn get_spending_tx(&self, outpoint: OutPoint) -> Option<Arc<Transaction>>;
160}
161
162/// Ability to create and persist CPFP transactions for spending P2A outputs.
163#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
164#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
165pub trait MakeCpfp {
166	/// Creates a signed Child Pays for Parent (CPFP) transaction using a Pay-to-Anchor (P2A) output
167	/// to broadcast unilateral exits and other TRUC transactions.
168	///
169	/// For more information please see [BIP431](https://github.com/bitcoin/bips/blob/master/bip-0431.mediawiki#topologically-restricted-until-confirmation).
170	///
171	/// # Arguments
172	///
173	/// * `tx` - A parent `Transaction` that is guaranteed to have one P2A output which
174	///          implementations must spend so that both the parent and child transactions can be
175	///          broadcast to the network as a v3 transaction package.
176	/// * `fees` - Informs the implementation how fees should be paid by the child transaction. Note
177	///            that an effective fee rate should be calculated using the weight of both the
178	///            parent and child transactions.
179	///
180	/// # Returns
181	///
182	/// Returns a `Result` containing:
183	/// * `Transaction` - The signed CPFP transaction ready to be broadcasted to the network with
184	///                   the given parent transaction if construction and signing were successful.
185	/// * `CpfpError` - An error indicating the reason for failure in constructing the CPFP
186	///                 transaction (e.g., insufficient funds, invalid parent transaction, or
187	///                 signing failure).
188	fn make_signed_p2a_cpfp(
189		&mut self,
190		tx: &Transaction,
191		fees: MakeCpfpFees,
192	) -> Result<Transaction, CpfpError>;
193
194	/// Persist the signed CPFP transaction so it can be rebroadcast or retrieved as needed.
195	async fn store_signed_p2a_cpfp(&mut self, tx: &Transaction) -> anyhow::Result<(), CpfpError>;
196}
197
198/// Trait alias for wallets that support boarding.
199///
200/// Any wallet type implementing these component traits automatically implements
201/// `Board`. The trait requires Send + Sync because boarding flows may be
202/// executed from async tasks and across threads.
203///
204/// Required capabilities:
205/// - [SignPsbt]: to finalize transactions
206/// - [GetWalletTx]: to query related transactions and their confirmations
207/// - [PreparePsbt]: to prepare transactions for boarding
208pub trait Board: PreparePsbt + SignPsbt + GetWalletTx + Send + Sync {}
209
210impl <W: PreparePsbt + SignPsbt + GetWalletTx + Send + Sync> Board for W {}
211
212/// Trait alias for wallets that support unilateral exit end-to-end.
213///
214/// Any wallet type implementing these component traits automatically implements
215/// `ExitUnilaterally`. The trait requires Send + Sync because exit flows may be
216/// executed from async tasks and across threads.
217///
218/// Required capabilities:
219/// - [GetBalance]: to evaluate available funds
220/// - [GetWalletTx]: to query related transactions and their confirmations
221/// - [MakeCpfp]: to accelerate slow/pinned exits
222/// - [SignPsbt]: to finalize transactions
223/// - [GetSpendingTx]: to detect local spends relevant to exit coordination
224pub trait ExitUnilaterally:
225	GetBalance +
226	GetWalletTx +
227	MakeCpfp +
228	SignPsbt +
229	GetSpendingTx +
230	Send + Sync + {}
231
232impl <W: GetBalance +
233	GetWalletTx +
234	MakeCpfp +
235	SignPsbt +
236	GetSpendingTx +
237	Send + Sync> ExitUnilaterally for W {}
238
239/// Ability to sync the wallet with the onchain network.
240#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
241#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
242pub trait ChainSync {
243	/// Sync the wallet with the onchain network.
244	async fn sync(&mut self, chain: &ChainSource) -> anyhow::Result<()>;
245}
246
247/// Trait that covers the requirements to use an onchain wallet with
248/// [Wallet::run_daemon](crate::Wallet::run_daemon).
249pub trait DaemonizableOnchainWallet: ExitUnilaterally + ChainSync {}
250impl <W: ExitUnilaterally + ChainSync> DaemonizableOnchainWallet for W {}