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 /// `true` when this tx spends a P2A fee anchor — i.e. it is a CPFP child
63 /// bumping the parent that created the anchor.
64 pub is_cpfp: bool,
65}
66
67/// Represents an onchain UTXO known to the wallet.
68///
69/// This can be either:
70/// - `Local`: a standard wallet UTXO
71/// - `Exit`: a spendable exit output produced by the Ark exit mechanism
72#[derive(Debug, Clone)]
73pub enum Utxo {
74 Local(LocalUtxo),
75 Exit(SpendableExit),
76}
77
78/// A standard wallet [Utxo] owned by the local wallet implementation.
79#[derive(Debug, Clone)]
80pub struct LocalUtxo {
81 /// The outpoint referencing the UTXO.
82 pub outpoint: OutPoint,
83 /// The amount contained in the UTXO.
84 pub amount: Amount,
85 /// Optional confirmation height; `None` if unconfirmed.
86 pub confirmation_height: Option<BlockHeight>,
87}
88
89/// A spendable unilateral exit of a [Vtxo] which can be claimed onchain.
90///
91/// When exiting unilaterally, the wallet will end up with onchain outputs that correspond to
92/// previously-held VTXOs. These can be claimed and used for further spending.
93#[derive(Debug, Clone)]
94pub struct SpendableExit {
95 /// The VTXO being exited.
96 pub vtxo: Vtxo<Full>,
97 /// The block height associated with the exits' validity window.
98 pub height: BlockHeight,
99}
100
101/// Ability to finalize a [Psbt] into a fully signed [Transaction].
102///
103/// Wallets should apply all necessary signatures and finalize inputs according
104/// to their internal key management and policies.
105#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
106#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
107pub trait SignPsbt {
108 /// Consume a [Psbt] and return a fully signed [Psbt] with all witnesses filled in.
109 ///
110 /// Useful when the signed [Psbt] is needed after signing, e.g. to compute fees
111 /// via [Psbt::fee] before extracting the final [Transaction].
112 async fn finish_psbt(&mut self, psbt: Psbt) -> anyhow::Result<Psbt>;
113}
114
115/// Ability to get the next onchain address from the wallet.
116#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
117#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
118pub trait GetAddress {
119 async fn address(&mut self) -> anyhow::Result<Address>;
120}
121
122/// Ability to query the wallets' total balance.
123///
124/// This is used by higher-level flows to decide when onchain funds are available for boarding or
125/// fee bumping, and to present balance information to users.
126pub trait GetBalance {
127 /// Get the total balance of the wallet.
128 fn get_balance(&self) -> Amount;
129}
130
131/// Ability to look up transactions known to the wallet.
132///
133/// Implementations should return wallet-related transactions and, when possible,
134/// the block information those transactions confirmed in.
135pub trait GetWalletTx {
136 /// Retrieve the wallet [Transaction] for the given [Txid] if any.
137 fn get_wallet_tx(&self, txid: Txid) -> Option<Arc<Transaction>>;
138
139 /// Retrieve information about the block, if any, a given wallet transaction was confirmed in.
140 fn get_wallet_tx_confirmed_block(&self, txid: Txid) -> anyhow::Result<Option<BlockRef>>;
141}
142
143/// Ability to construct funded PSBTs for specific destinations or to drain the wallet.
144///
145/// These methods are used to build transactions for boarding, exits, and fee bumping.
146pub trait PreparePsbt {
147 /// Prepare a [Transaction] which will send to the given destinations.
148 fn prepare_tx(
149 &mut self,
150 destinations: &[(Address, Amount)],
151 fee_rate: FeeRate,
152 ) -> anyhow::Result<Psbt>;
153
154 /// Prepare a [Transaction] for sending all wallet funds to the given destination.
155 fn prepare_drain_tx(
156 &mut self,
157 destination: Address,
158 fee_rate: FeeRate,
159 ) -> anyhow::Result<Psbt>;
160}
161
162/// Ability to find wallet-local spends of a specific [OutPoint].
163///
164/// This helps identify if the wallet has already spent an exit or parent [Transaction].
165pub trait GetSpendingTx {
166 /// This should search the wallet and look for any [Transaction] that spends the given
167 /// [OutPoint]. The intent of the function is to only look at spends which happen in the wallet
168 /// itself.
169 fn get_spending_tx(&self, outpoint: OutPoint) -> Option<Arc<Transaction>>;
170}
171
172/// Ability to create and persist CPFP transactions for spending P2A outputs.
173#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
174#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
175pub trait MakeCpfp {
176 /// Creates a signed Child Pays for Parent (CPFP) transaction using a Pay-to-Anchor (P2A) output
177 /// to broadcast unilateral exits and other TRUC transactions.
178 ///
179 /// For more information please see [BIP431](https://github.com/bitcoin/bips/blob/master/bip-0431.mediawiki#topologically-restricted-until-confirmation).
180 ///
181 /// # Arguments
182 ///
183 /// * `tx` - A parent `Transaction` that is guaranteed to have one P2A output which
184 /// implementations must spend so that both the parent and child transactions can be
185 /// broadcast to the network as a v3 transaction package.
186 /// * `fees` - Informs the implementation how fees should be paid by the child transaction. Note
187 /// that an effective fee rate should be calculated using the weight of both the
188 /// parent and child transactions.
189 ///
190 /// # Returns
191 ///
192 /// Returns a `Result` containing:
193 /// * `Transaction` - The signed CPFP transaction ready to be broadcasted to the network with
194 /// the given parent transaction if construction and signing were successful.
195 /// * `CpfpError` - An error indicating the reason for failure in constructing the CPFP
196 /// transaction (e.g., insufficient funds, invalid parent transaction, or
197 /// signing failure).
198 fn make_signed_p2a_cpfp(
199 &mut self,
200 tx: &Transaction,
201 fees: MakeCpfpFees,
202 ) -> Result<Transaction, CpfpError>;
203
204 /// Persist the signed CPFP transaction so it can be rebroadcast or retrieved as needed.
205 async fn store_signed_p2a_cpfp(&mut self, tx: &Transaction) -> anyhow::Result<(), CpfpError>;
206}
207
208/// Trait alias for wallets that support boarding.
209///
210/// Any wallet type implementing these component traits automatically implements
211/// `Board`. The trait requires Send + Sync because boarding flows may be
212/// executed from async tasks and across threads.
213///
214/// Required capabilities:
215/// - [SignPsbt]: to finalize transactions
216/// - [GetWalletTx]: to query related transactions and their confirmations
217/// - [PreparePsbt]: to prepare transactions for boarding
218pub trait Board: PreparePsbt + SignPsbt + GetWalletTx + Send + Sync {}
219
220impl <W: PreparePsbt + SignPsbt + GetWalletTx + Send + Sync> Board for W {}
221
222/// Trait alias for wallets that support unilateral exit end-to-end.
223///
224/// Any wallet type implementing these component traits automatically implements
225/// `ExitUnilaterally`. The trait requires Send + Sync because exit flows may be
226/// executed from async tasks and across threads.
227///
228/// Required capabilities:
229/// - [GetBalance]: to evaluate available funds
230/// - [GetWalletTx]: to query related transactions and their confirmations
231/// - [MakeCpfp]: to accelerate slow/pinned exits
232/// - [SignPsbt]: to finalize transactions
233/// - [GetSpendingTx]: to detect local spends relevant to exit coordination
234pub trait ExitUnilaterally:
235 GetBalance +
236 GetWalletTx +
237 MakeCpfp +
238 SignPsbt +
239 GetSpendingTx +
240 Send + Sync + {}
241
242impl <W: GetBalance +
243 GetWalletTx +
244 MakeCpfp +
245 SignPsbt +
246 GetSpendingTx +
247 Send + Sync> ExitUnilaterally for W {}
248
249/// Ability to sync the wallet with the onchain network.
250#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
251#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
252pub trait ChainSync {
253 /// Sync the wallet with the onchain network.
254 async fn sync(&mut self, chain: &ChainSource) -> anyhow::Result<()>;
255}
256
257/// Trait that covers the requirements to use an onchain wallet with
258/// [Wallet::run_daemon](crate::Wallet::run_daemon).
259pub trait DaemonizableOnchainWallet: ExitUnilaterally + ChainSync {}
260impl <W: ExitUnilaterally + ChainSync> DaemonizableOnchainWallet for W {}