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 {}