diff --git a/crates/bdk/Cargo.toml b/crates/bdk/Cargo.toml
index 90030044..425199e1 100644
--- a/crates/bdk/Cargo.toml
+++ b/crates/bdk/Cargo.toml
@@ -13,6 +13,7 @@ edition = "2021"
rust-version = "1.63"
[dependencies]
+anyhow = { version = "1", default-features = false }
rand = "^0.8"
miniscript = { version = "11.0.0", features = ["serde"], default-features = false }
bitcoin = { version = "0.31.0", features = ["serde", "base64", "rand-std"], default-features = false }
diff --git a/crates/bdk/src/wallet/error.rs b/crates/bdk/src/wallet/error.rs
index 46cf8ef3..eaf811d6 100644
--- a/crates/bdk/src/wallet/error.rs
+++ b/crates/bdk/src/wallet/error.rs
@@ -47,11 +47,11 @@ impl std::error::Error for MiniscriptPsbtError {}
/// Error returned from [`TxBuilder::finish`]
///
/// [`TxBuilder::finish`]: crate::wallet::tx_builder::TxBuilder::finish
-pub enum CreateTxError
{
+pub enum CreateTxError {
/// There was a problem with the descriptors passed in
Descriptor(DescriptorError),
- /// We were unable to write wallet data to the persistence backend
- Persist(P),
+ /// We were unable to load wallet data from or write wallet data to the persistence backend
+ Persist(anyhow::Error),
/// There was a problem while extracting and manipulating policies
Policy(PolicyError),
/// Spending policy is not compatible with this [`KeychainKind`]
@@ -119,17 +119,14 @@ pub enum CreateTxError
{
MiniscriptPsbt(MiniscriptPsbtError),
}
-impl
fmt::Display for CreateTxError
-where
- P: fmt::Display,
-{
+impl fmt::Display for CreateTxError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Descriptor(e) => e.fmt(f),
Self::Persist(e) => {
write!(
f,
- "failed to write wallet data to persistence backend: {}",
+ "failed to load wallet data from or write wallet data to persistence backend: {}",
e
)
}
@@ -214,38 +211,38 @@ where
}
}
-impl
From for CreateTxError {
+impl From for CreateTxError {
fn from(err: descriptor::error::Error) -> Self {
CreateTxError::Descriptor(err)
}
}
-impl From for CreateTxError {
+impl From for CreateTxError {
fn from(err: PolicyError) -> Self {
CreateTxError::Policy(err)
}
}
-impl From for CreateTxError {
+impl From for CreateTxError {
fn from(err: MiniscriptPsbtError) -> Self {
CreateTxError::MiniscriptPsbt(err)
}
}
-impl From for CreateTxError {
+impl From for CreateTxError {
fn from(err: psbt::Error) -> Self {
CreateTxError::Psbt(err)
}
}
-impl From for CreateTxError {
+impl From for CreateTxError {
fn from(err: coin_selection::Error) -> Self {
CreateTxError::CoinSelection(err)
}
}
#[cfg(feature = "std")]
-impl std::error::Error for CreateTxError {}
+impl std::error::Error for CreateTxError {}
#[derive(Debug)]
/// Error returned from [`Wallet::build_fee_bump`]
diff --git a/crates/bdk/src/wallet/export.rs b/crates/bdk/src/wallet/export.rs
index 50c91eb6..95252a01 100644
--- a/crates/bdk/src/wallet/export.rs
+++ b/crates/bdk/src/wallet/export.rs
@@ -53,9 +53,8 @@
//! # Ok::<_, Box>(())
//! ```
-use core::str::FromStr;
-
use alloc::string::{String, ToString};
+use core::str::FromStr;
use serde::{Deserialize, Serialize};
use miniscript::descriptor::{ShInner, WshInner};
@@ -110,8 +109,8 @@ impl FullyNodedExport {
///
/// If the database is empty or `include_blockheight` is false, the `blockheight` field
/// returned will be `0`.
- pub fn export_wallet(
- wallet: &Wallet,
+ pub fn export_wallet(
+ wallet: &Wallet,
label: &str,
include_blockheight: bool,
) -> Result {
@@ -225,7 +224,7 @@ mod test {
descriptor: &str,
change_descriptor: Option<&str>,
network: Network,
- ) -> Wallet<()> {
+ ) -> Wallet {
let mut wallet = Wallet::new_no_persist(descriptor, change_descriptor, network).unwrap();
let transaction = Transaction {
input: vec![],
diff --git a/crates/bdk/src/wallet/mod.rs b/crates/bdk/src/wallet/mod.rs
index 84687882..b4eabab7 100644
--- a/crates/bdk/src/wallet/mod.rs
+++ b/crates/bdk/src/wallet/mod.rs
@@ -82,12 +82,12 @@ const COINBASE_MATURITY: u32 = 100;
///
/// [`signer`]: crate::signer
#[derive(Debug)]
-pub struct Wallet {
+pub struct Wallet {
signers: Arc,
change_signers: Arc,
chain: LocalChain,
indexed_graph: IndexedTxGraph>,
- persist: Persist,
+ persist: Persist,
network: Network,
secp: SecpCtx,
}
@@ -236,7 +236,7 @@ impl Wallet {
Self::new(descriptor, change_descriptor, (), network).map_err(|e| match e {
NewError::NonEmptyDatabase => unreachable!("mock-database cannot have data"),
NewError::Descriptor(e) => e,
- NewError::Write(_) => unreachable!("mock-write must always succeed"),
+ NewError::Persist(_) => unreachable!("mock-write must always succeed"),
})
}
@@ -251,15 +251,12 @@ impl Wallet {
.map_err(|e| match e {
NewError::NonEmptyDatabase => unreachable!("mock-database cannot have data"),
NewError::Descriptor(e) => e,
- NewError::Write(_) => unreachable!("mock-write must always succeed"),
+ NewError::Persist(_) => unreachable!("mock-write must always succeed"),
})
}
}
-impl Wallet
-where
- D: PersistBackend,
-{
+impl Wallet {
/// Infallibly return a derived address using the external descriptor, see [`AddressIndex`] for
/// available address index selection strategies. If none of the keys in the descriptor are derivable
/// (i.e. does not end with /*) then the same address will always be returned for any [`AddressIndex`].
@@ -296,19 +293,16 @@ where
/// [`new`]: Wallet::new
/// [`new_with_genesis_hash`]: Wallet::new_with_genesis_hash
#[derive(Debug)]
-pub enum NewError {
+pub enum NewError {
/// Database already has data.
NonEmptyDatabase,
/// There was problem with the passed-in descriptor(s).
Descriptor(crate::descriptor::DescriptorError),
/// We were unable to write the wallet's data to the persistence backend.
- Write(W),
+ Persist(anyhow::Error),
}
-impl fmt::Display for NewError
-where
- W: fmt::Display,
-{
+impl fmt::Display for NewError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
NewError::NonEmptyDatabase => write!(
@@ -316,13 +310,13 @@ where
"database already has data - use `load` or `new_or_load` methods instead"
),
NewError::Descriptor(e) => e.fmt(f),
- NewError::Write(e) => e.fmt(f),
+ NewError::Persist(e) => e.fmt(f),
}
}
}
#[cfg(feature = "std")]
-impl std::error::Error for NewError where W: core::fmt::Display + core::fmt::Debug {}
+impl std::error::Error for NewError {}
/// The error type when loading a [`Wallet`] from persistence.
///
@@ -330,11 +324,11 @@ impl std::error::Error for NewError where W: core::fmt::Display + core::fm
///
/// [`load`]: Wallet::load
#[derive(Debug)]
-pub enum LoadError {
+pub enum LoadError {
/// There was a problem with the passed-in descriptor(s).
Descriptor(crate::descriptor::DescriptorError),
/// Loading data from the persistence backend failed.
- Load(L),
+ Persist(anyhow::Error),
/// Wallet not initialized, persistence backend is empty.
NotInitialized,
/// Data loaded from persistence is missing network type.
@@ -343,14 +337,11 @@ pub enum LoadError {
MissingGenesis,
}
-impl fmt::Display for LoadError
-where
- L: fmt::Display,
-{
+impl fmt::Display for LoadError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
LoadError::Descriptor(e) => e.fmt(f),
- LoadError::Load(e) => e.fmt(f),
+ LoadError::Persist(e) => e.fmt(f),
LoadError::NotInitialized => {
write!(f, "wallet is not initialized, persistence backend is empty")
}
@@ -361,7 +352,7 @@ where
}
#[cfg(feature = "std")]
-impl std::error::Error for LoadError where L: core::fmt::Display + core::fmt::Debug {}
+impl std::error::Error for LoadError {}
/// Error type for when we try load a [`Wallet`] from persistence and creating it if non-existent.
///
@@ -370,13 +361,11 @@ impl std::error::Error for LoadError where L: core::fmt::Display + core::f
/// [`new_or_load`]: Wallet::new_or_load
/// [`new_or_load_with_genesis_hash`]: Wallet::new_or_load_with_genesis_hash
#[derive(Debug)]
-pub enum NewOrLoadError {
+pub enum NewOrLoadError {
/// There is a problem with the passed-in descriptor.
Descriptor(crate::descriptor::DescriptorError),
- /// Writing to the persistence backend failed.
- Write(W),
- /// Loading from the persistence backend failed.
- Load(L),
+ /// Either writing to or loading from the persistence backend failed.
+ Persist(anyhow::Error),
/// Wallet is not initialized, persistence backend is empty.
NotInitialized,
/// The loaded genesis hash does not match what was provided.
@@ -395,16 +384,15 @@ pub enum NewOrLoadError {
},
}
-impl fmt::Display for NewOrLoadError
-where
- W: fmt::Display,
- L: fmt::Display,
-{
+impl fmt::Display for NewOrLoadError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
NewOrLoadError::Descriptor(e) => e.fmt(f),
- NewOrLoadError::Write(e) => write!(f, "failed to write to persistence: {}", e),
- NewOrLoadError::Load(e) => write!(f, "failed to load from persistence: {}", e),
+ NewOrLoadError::Persist(e) => write!(
+ f,
+ "failed to either write to or load from persistence, {}",
+ e
+ ),
NewOrLoadError::NotInitialized => {
write!(f, "wallet is not initialized, persistence backend is empty")
}
@@ -419,12 +407,7 @@ where
}
#[cfg(feature = "std")]
-impl std::error::Error for NewOrLoadError
-where
- W: core::fmt::Display + core::fmt::Debug,
- L: core::fmt::Display + core::fmt::Debug,
-{
-}
+impl std::error::Error for NewOrLoadError {}
/// An error that may occur when inserting a transaction into [`Wallet`].
#[derive(Debug)]
@@ -488,17 +471,14 @@ impl fmt::Display for ApplyBlockError {
#[cfg(feature = "std")]
impl std::error::Error for ApplyBlockError {}
-impl Wallet {
+impl Wallet {
/// Initialize an empty [`Wallet`].
pub fn new(
descriptor: E,
change_descriptor: Option,
- db: D,
+ db: impl PersistBackend + Send + Sync + 'static,
network: Network,
- ) -> Result>
- where
- D: PersistBackend,
- {
+ ) -> Result {
let genesis_hash = genesis_block(network).block_hash();
Self::new_with_genesis_hash(descriptor, change_descriptor, db, network, genesis_hash)
}
@@ -510,13 +490,10 @@ impl Wallet {
pub fn new_with_genesis_hash(
descriptor: E,
change_descriptor: Option,
- mut db: D,
+ mut db: impl PersistBackend + Send + Sync + 'static,
network: Network,
genesis_hash: BlockHash,
- ) -> Result>
- where
- D: PersistBackend,
- {
+ ) -> Result {
if let Ok(changeset) = db.load_from_persistence() {
if changeset.is_some() {
return Err(NewError::NonEmptyDatabase);
@@ -538,7 +515,7 @@ impl Wallet {
indexed_tx_graph: indexed_graph.initial_changeset(),
network: Some(network),
});
- persist.commit().map_err(NewError::Write)?;
+ persist.commit().map_err(NewError::Persist)?;
Ok(Wallet {
signers,
@@ -555,14 +532,11 @@ impl Wallet {
pub fn load(
descriptor: E,
change_descriptor: Option,
- mut db: D,
- ) -> Result>
- where
- D: PersistBackend,
- {
+ mut db: impl PersistBackend + Send + Sync + 'static,
+ ) -> Result {
let changeset = db
.load_from_persistence()
- .map_err(LoadError::Load)?
+ .map_err(LoadError::Persist)?
.ok_or(LoadError::NotInitialized)?;
Self::load_from_changeset(descriptor, change_descriptor, db, changeset)
}
@@ -570,12 +544,9 @@ impl Wallet {
fn load_from_changeset(
descriptor: E,
change_descriptor: Option,
- db: D,
+ db: impl PersistBackend + Send + Sync + 'static,
changeset: ChangeSet,
- ) -> Result>
- where
- D: PersistBackend,
- {
+ ) -> Result {
let secp = Secp256k1::new();
let network = changeset.network.ok_or(LoadError::MissingNetwork)?;
let chain =
@@ -608,12 +579,9 @@ impl Wallet {
pub fn new_or_load(
descriptor: E,
change_descriptor: Option,
- db: D,
+ db: impl PersistBackend + Send + Sync + 'static,
network: Network,
- ) -> Result>
- where
- D: PersistBackend,
- {
+ ) -> Result {
let genesis_hash = genesis_block(network).block_hash();
Self::new_or_load_with_genesis_hash(
descriptor,
@@ -633,21 +601,20 @@ impl Wallet {
pub fn new_or_load_with_genesis_hash(
descriptor: E,
change_descriptor: Option,
- mut db: D,
+ mut db: impl PersistBackend + Send + Sync + 'static,
network: Network,
genesis_hash: BlockHash,
- ) -> Result>
- where
- D: PersistBackend,
- {
- let changeset = db.load_from_persistence().map_err(NewOrLoadError::Load)?;
+ ) -> Result {
+ let changeset = db
+ .load_from_persistence()
+ .map_err(NewOrLoadError::Persist)?;
match changeset {
Some(changeset) => {
let wallet =
Self::load_from_changeset(descriptor, change_descriptor, db, changeset)
.map_err(|e| match e {
LoadError::Descriptor(e) => NewOrLoadError::Descriptor(e),
- LoadError::Load(e) => NewOrLoadError::Load(e),
+ LoadError::Persist(e) => NewOrLoadError::Persist(e),
LoadError::NotInitialized => NewOrLoadError::NotInitialized,
LoadError::MissingNetwork => {
NewOrLoadError::LoadedNetworkDoesNotMatch {
@@ -688,7 +655,7 @@ impl Wallet {
unreachable!("database is already checked to have no data")
}
NewError::Descriptor(e) => NewOrLoadError::Descriptor(e),
- NewError::Write(e) => NewOrLoadError::Write(e),
+ NewError::Persist(e) => NewOrLoadError::Persist(e),
}),
}
}
@@ -714,13 +681,7 @@ impl Wallet {
///
/// This panics when the caller requests for an address of derivation index greater than the
/// BIP32 max index.
- pub fn try_get_address(
- &mut self,
- address_index: AddressIndex,
- ) -> Result
- where
- D: PersistBackend,
- {
+ pub fn try_get_address(&mut self, address_index: AddressIndex) -> anyhow::Result {
self._get_address(KeychainKind::External, address_index)
}
@@ -742,10 +703,7 @@ impl Wallet {
pub fn try_get_internal_address(
&mut self,
address_index: AddressIndex,
- ) -> Result
- where
- D: PersistBackend,
- {
+ ) -> anyhow::Result {
self._get_address(KeychainKind::Internal, address_index)
}
@@ -770,10 +728,7 @@ impl Wallet {
&mut self,
keychain: KeychainKind,
address_index: AddressIndex,
- ) -> Result
- where
- D: PersistBackend,
- {
+ ) -> anyhow::Result {
let keychain = self.map_keychain(keychain);
let txout_index = &mut self.indexed_graph.index;
let (index, spk, changeset) = match address_index {
@@ -918,10 +873,7 @@ impl Wallet {
/// [`list_unspent`]: Self::list_unspent
/// [`list_output`]: Self::list_output
/// [`commit`]: Self::commit
- pub fn insert_txout(&mut self, outpoint: OutPoint, txout: TxOut)
- where
- D: PersistBackend,
- {
+ pub fn insert_txout(&mut self, outpoint: OutPoint, txout: TxOut) {
let additions = self.indexed_graph.insert_txout(outpoint, txout);
self.persist.stage(ChangeSet::from(additions));
}
@@ -938,7 +890,7 @@ impl Wallet {
/// ```rust, no_run
/// # use bitcoin::Txid;
/// # use bdk::Wallet;
- /// # let mut wallet: Wallet<()> = todo!();
+ /// # let mut wallet: Wallet = todo!();
/// # let txid:Txid = todo!();
/// let tx = wallet.get_tx(txid).expect("transaction").tx_node.tx;
/// let fee = wallet.calculate_fee(&tx).expect("fee");
@@ -947,7 +899,7 @@ impl Wallet {
/// ```rust, no_run
/// # use bitcoin::Psbt;
/// # use bdk::Wallet;
- /// # let mut wallet: Wallet<()> = todo!();
+ /// # let mut wallet: Wallet = todo!();
/// # let mut psbt: Psbt = todo!();
/// let tx = &psbt.clone().extract_tx().expect("tx");
/// let fee = wallet.calculate_fee(tx).expect("fee");
@@ -969,7 +921,7 @@ impl Wallet {
/// ```rust, no_run
/// # use bitcoin::Txid;
/// # use bdk::Wallet;
- /// # let mut wallet: Wallet<()> = todo!();
+ /// # let mut wallet: Wallet = todo!();
/// # let txid:Txid = todo!();
/// let tx = wallet.get_tx(txid).expect("transaction").tx_node.tx;
/// let fee_rate = wallet.calculate_fee_rate(&tx).expect("fee rate");
@@ -978,7 +930,7 @@ impl Wallet {
/// ```rust, no_run
/// # use bitcoin::Psbt;
/// # use bdk::Wallet;
- /// # let mut wallet: Wallet<()> = todo!();
+ /// # let mut wallet: Wallet = todo!();
/// # let mut psbt: Psbt = todo!();
/// let tx = &psbt.clone().extract_tx().expect("tx");
/// let fee_rate = wallet.calculate_fee_rate(tx).expect("fee rate");
@@ -1000,7 +952,7 @@ impl Wallet {
/// ```rust, no_run
/// # use bitcoin::Txid;
/// # use bdk::Wallet;
- /// # let mut wallet: Wallet<()> = todo!();
+ /// # let mut wallet: Wallet = todo!();
/// # let txid:Txid = todo!();
/// let tx = wallet.get_tx(txid).expect("tx exists").tx_node.tx;
/// let (sent, received) = wallet.sent_and_received(&tx);
@@ -1009,7 +961,7 @@ impl Wallet {
/// ```rust, no_run
/// # use bitcoin::Psbt;
/// # use bdk::Wallet;
- /// # let mut wallet: Wallet<()> = todo!();
+ /// # let mut wallet: Wallet = todo!();
/// # let mut psbt: Psbt = todo!();
/// let tx = &psbt.clone().extract_tx().expect("tx");
/// let (sent, received) = wallet.sent_and_received(tx);
@@ -1031,7 +983,7 @@ impl Wallet {
/// ```rust, no_run
/// use bdk::{chain::ChainPosition, Wallet};
/// use bdk_chain::Anchor;
- /// # let wallet: Wallet<()> = todo!();
+ /// # let wallet: Wallet = todo!();
/// # let my_txid: bitcoin::Txid = todo!();
///
/// let canonical_tx = wallet.get_tx(my_txid).expect("panic if tx does not exist");
@@ -1087,10 +1039,7 @@ impl Wallet {
pub fn insert_checkpoint(
&mut self,
block_id: BlockId,
- ) -> Result
- where
- D: PersistBackend,
- {
+ ) -> Result {
let changeset = self.chain.insert_block(block_id)?;
let changed = !changeset.is_empty();
self.persist.stage(changeset.into());
@@ -1118,10 +1067,7 @@ impl Wallet {
&mut self,
tx: Transaction,
position: ConfirmationTime,
- ) -> Result
- where
- D: PersistBackend,
- {
+ ) -> Result {
let (anchor, last_seen) = match position {
ConfirmationTime::Confirmed { height, time } => {
// anchor tx to checkpoint with lowest height that is >= position's height
@@ -1248,7 +1194,7 @@ impl Wallet {
/// ```
///
/// [`TxBuilder`]: crate::TxBuilder
- pub fn build_tx(&mut self) -> TxBuilder<'_, D, DefaultCoinSelectionAlgorithm, CreateTx> {
+ pub fn build_tx(&mut self) -> TxBuilder<'_, DefaultCoinSelectionAlgorithm, CreateTx> {
TxBuilder {
wallet: alloc::rc::Rc::new(core::cell::RefCell::new(self)),
params: TxParams::default(),
@@ -1261,10 +1207,7 @@ impl Wallet {
&mut self,
coin_selection: Cs,
params: TxParams,
- ) -> Result>
- where
- D: PersistBackend,
- {
+ ) -> Result {
let external_descriptor = self
.indexed_graph
.index
@@ -1283,7 +1226,7 @@ impl Wallet {
let internal_policy = internal_descriptor
.as_ref()
.map(|desc| {
- Ok::<_, CreateTxError>(
+ Ok::<_, CreateTxError>(
desc.extract_policy(&self.change_signers, BuildSatisfaction::None, &self.secp)?
.unwrap(),
)
@@ -1320,7 +1263,7 @@ impl Wallet {
)?;
let internal_requirements = internal_policy
.map(|policy| {
- Ok::<_, CreateTxError>(
+ Ok::<_, CreateTxError>(
policy.get_condition(
params
.internal_policy_path
@@ -1647,7 +1590,7 @@ impl Wallet {
pub fn build_fee_bump(
&mut self,
txid: Txid,
- ) -> Result, BuildFeeBumpError> {
+ ) -> Result, BuildFeeBumpError> {
let graph = self.indexed_graph.graph();
let txout_index = &self.indexed_graph.index;
let chain_tip = self.chain.tip().block_id();
@@ -2158,10 +2101,7 @@ impl Wallet {
tx: Transaction,
selected: Vec,
params: TxParams,
- ) -> Result>
- where
- D: PersistBackend,
- {
+ ) -> Result {
let mut psbt = Psbt::from_unsigned_tx(tx)?;
if params.add_global_xpubs {
@@ -2242,10 +2182,7 @@ impl Wallet {
utxo: LocalOutput,
sighash_type: Option,
only_witness_utxo: bool,
- ) -> Result>
- where
- D: PersistBackend,
- {
+ ) -> Result {
// Try to find the prev_script in our db to figure out if this is internal or external,
// and the derivation index
let (keychain, child) = self
@@ -2335,10 +2272,7 @@ impl Wallet {
/// transactions related to your wallet into it.
///
/// [`commit`]: Self::commit
- pub fn apply_update(&mut self, update: Update) -> Result<(), CannotConnectError>
- where
- D: PersistBackend,
- {
+ pub fn apply_update(&mut self, update: Update) -> Result<(), CannotConnectError> {
let mut changeset = match update.chain {
Some(chain_update) => ChangeSet::from(self.chain.apply_update(chain_update)?),
None => ChangeSet::default(),
@@ -2354,7 +2288,6 @@ impl Wallet {
changeset.append(ChangeSet::from(
self.indexed_graph.apply_update(update.graph),
));
-
self.persist.stage(changeset);
Ok(())
}
@@ -2365,20 +2298,14 @@ impl Wallet {
/// This returns whether the `update` resulted in any changes.
///
/// [`staged`]: Self::staged
- pub fn commit(&mut self) -> Result
- where
- D: PersistBackend,
- {
+ pub fn commit(&mut self) -> anyhow::Result {
self.persist.commit().map(|c| c.is_some())
}
/// Returns the changes that will be committed with the next call to [`commit`].
///
/// [`commit`]: Self::commit
- pub fn staged(&self) -> &ChangeSet
- where
- D: PersistBackend,
- {
+ pub fn staged(&self) -> &ChangeSet {
self.persist.staged()
}
@@ -2404,10 +2331,7 @@ impl Wallet {
/// with `prev_blockhash` and `height-1` as the `connected_to` parameter.
///
/// [`apply_block_connected_to`]: Self::apply_block_connected_to
- pub fn apply_block(&mut self, block: &Block, height: u32) -> Result<(), CannotConnectError>
- where
- D: PersistBackend,
- {
+ pub fn apply_block(&mut self, block: &Block, height: u32) -> Result<(), CannotConnectError> {
let connected_to = match height.checked_sub(1) {
Some(prev_height) => BlockId {
height: prev_height,
@@ -2438,10 +2362,7 @@ impl Wallet {
block: &Block,
height: u32,
connected_to: BlockId,
- ) -> Result<(), ApplyHeaderError>
- where
- D: PersistBackend,
- {
+ ) -> Result<(), ApplyHeaderError> {
let mut changeset = ChangeSet::default();
changeset.append(
self.chain
@@ -2468,9 +2389,7 @@ impl Wallet {
pub fn apply_unconfirmed_txs<'t>(
&mut self,
unconfirmed_txs: impl IntoIterator- ,
- ) where
- D: PersistBackend
,
- {
+ ) {
let indexed_graph_changeset = self
.indexed_graph
.batch_insert_relevant_unconfirmed(unconfirmed_txs);
@@ -2478,7 +2397,7 @@ impl Wallet {
}
}
-impl AsRef> for Wallet {
+impl AsRef> for Wallet {
fn as_ref(&self) -> &bdk_chain::tx_graph::TxGraph {
self.indexed_graph.graph()
}
diff --git a/crates/bdk/src/wallet/tx_builder.rs b/crates/bdk/src/wallet/tx_builder.rs
index 4009dab1..e7df8667 100644
--- a/crates/bdk/src/wallet/tx_builder.rs
+++ b/crates/bdk/src/wallet/tx_builder.rs
@@ -45,13 +45,12 @@ use core::cell::RefCell;
use core::fmt;
use core::marker::PhantomData;
-use bdk_chain::PersistBackend;
use bitcoin::psbt::{self, Psbt};
use bitcoin::script::PushBytes;
use bitcoin::{absolute, FeeRate, OutPoint, ScriptBuf, Sequence, Transaction, Txid};
use super::coin_selection::{CoinSelectionAlgorithm, DefaultCoinSelectionAlgorithm};
-use super::{ChangeSet, CreateTxError, Wallet};
+use super::{CreateTxError, Wallet};
use crate::collections::{BTreeMap, HashSet};
use crate::{KeychainKind, LocalOutput, Utxo, WeightedUtxo};
@@ -124,8 +123,8 @@ impl TxBuilderContext for BumpFee {}
/// [`finish`]: Self::finish
/// [`coin_selection`]: Self::coin_selection
#[derive(Debug)]
-pub struct TxBuilder<'a, D, Cs, Ctx> {
- pub(crate) wallet: Rc>>,
+pub struct TxBuilder<'a, Cs, Ctx> {
+ pub(crate) wallet: Rc>,
pub(crate) params: TxParams,
pub(crate) coin_selection: Cs,
pub(crate) phantom: PhantomData,
@@ -176,7 +175,7 @@ impl Default for FeePolicy {
}
}
-impl<'a, D, Cs: Clone, Ctx> Clone for TxBuilder<'a, D, Cs, Ctx> {
+impl<'a, Cs: Clone, Ctx> Clone for TxBuilder<'a, Cs, Ctx> {
fn clone(&self) -> Self {
TxBuilder {
wallet: self.wallet.clone(),
@@ -188,7 +187,7 @@ impl<'a, D, Cs: Clone, Ctx> Clone for TxBuilder<'a, D, Cs, Ctx> {
}
// methods supported by both contexts, for any CoinSelectionAlgorithm
-impl<'a, D, Cs, Ctx> TxBuilder<'a, D, Cs, Ctx> {
+impl<'a, Cs, Ctx> TxBuilder<'a, Cs, Ctx> {
/// Set a custom fee rate.
///
/// This method sets the mining fee paid by the transaction as a rate on its size.
@@ -560,7 +559,7 @@ impl<'a, D, Cs, Ctx> TxBuilder<'a, D, Cs, Ctx> {
pub fn coin_selection(
self,
coin_selection: P,
- ) -> TxBuilder<'a, D, P, Ctx> {
+ ) -> TxBuilder<'a, P, Ctx> {
TxBuilder {
wallet: self.wallet,
params: self.params,
@@ -615,16 +614,13 @@ impl<'a, D, Cs, Ctx> TxBuilder<'a, D, Cs, Ctx> {
}
}
-impl<'a, D, Cs: CoinSelectionAlgorithm, Ctx> TxBuilder<'a, D, Cs, Ctx> {
+impl<'a, Cs: CoinSelectionAlgorithm, Ctx> TxBuilder<'a, Cs, Ctx> {
/// Finish building the transaction.
///
/// Returns a new [`Psbt`] per [`BIP174`].
///
/// [`BIP174`]: https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki
- pub fn finish(self) -> Result>
- where
- D: PersistBackend,
- {
+ pub fn finish(self) -> Result {
self.wallet
.borrow_mut()
.create_tx(self.coin_selection, self.params)
@@ -715,7 +711,7 @@ impl fmt::Display for AllowShrinkingError {
#[cfg(feature = "std")]
impl std::error::Error for AllowShrinkingError {}
-impl<'a, D, Cs: CoinSelectionAlgorithm> TxBuilder<'a, D, Cs, CreateTx> {
+impl<'a, Cs: CoinSelectionAlgorithm> TxBuilder<'a, Cs, CreateTx> {
/// Replace the recipients already added with a new list
pub fn set_recipients(&mut self, recipients: Vec<(ScriptBuf, u64)>) -> &mut Self {
self.params.recipients = recipients;
@@ -793,7 +789,7 @@ impl<'a, D, Cs: CoinSelectionAlgorithm> TxBuilder<'a, D, Cs, CreateTx> {
}
// methods supported only by bump_fee
-impl<'a, D> TxBuilder<'a, D, DefaultCoinSelectionAlgorithm, BumpFee> {
+impl<'a> TxBuilder<'a, DefaultCoinSelectionAlgorithm, BumpFee> {
/// Explicitly tells the wallet that it is allowed to reduce the amount of the output matching this
/// `script_pubkey` in order to bump the transaction fee. Without specifying this the wallet
/// will attempt to find a change output to shrink instead.
diff --git a/crates/chain/Cargo.toml b/crates/chain/Cargo.toml
index fda5924d..56cadd85 100644
--- a/crates/chain/Cargo.toml
+++ b/crates/chain/Cargo.toml
@@ -14,6 +14,7 @@ readme = "README.md"
[dependencies]
# For no-std, remember to enable the bitcoin/no-std feature
+anyhow = { version = "1", default-features = false }
bitcoin = { version = "0.31.0", default-features = false }
serde_crate = { package = "serde", version = "1", optional = true, features = ["derive", "rc"] }
diff --git a/crates/chain/src/persist.rs b/crates/chain/src/persist.rs
index 5ddd622a..64efa55b 100644
--- a/crates/chain/src/persist.rs
+++ b/crates/chain/src/persist.rs
@@ -1,26 +1,32 @@
-use core::convert::Infallible;
-
use crate::Append;
+use alloc::boxed::Box;
+use core::fmt;
-/// `Persist` wraps a [`PersistBackend`] (`B`) to create a convenient staging area for changes (`C`)
+/// `Persist` wraps a [`PersistBackend`] to create a convenient staging area for changes (`C`)
/// before they are persisted.
///
/// Not all changes to the in-memory representation needs to be written to disk right away, so
/// [`Persist::stage`] can be used to *stage* changes first and then [`Persist::commit`] can be used
/// to write changes to disk.
-#[derive(Debug)]
-pub struct Persist {
- backend: B,
+pub struct Persist {
+ backend: Box + Send + Sync>,
stage: C,
}
-impl Persist
+impl fmt::Debug for Persist {
+ fn fmt(&self, fmt: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> {
+ write!(fmt, "{:?}", self.stage)?;
+ Ok(())
+ }
+}
+
+impl Persist
where
- B: PersistBackend,
C: Default + Append,
{
/// Create a new [`Persist`] from [`PersistBackend`].
- pub fn new(backend: B) -> Self {
+ pub fn new(backend: impl PersistBackend + Send + Sync + 'static) -> Self {
+ let backend = Box::new(backend);
Self {
backend,
stage: Default::default(),
@@ -46,7 +52,7 @@ where
/// # Error
///
/// Returns a backend-defined error if this fails.
- pub fn commit(&mut self) -> Result, B::WriteError> {
+ pub fn commit(&mut self) -> anyhow::Result > {
if self.stage.is_empty() {
return Ok(None);
}
@@ -63,7 +69,7 @@ where
///
/// [`stage`]: Self::stage
/// [`commit`]: Self::commit
- pub fn stage_and_commit(&mut self, changeset: C) -> Result , B::WriteError> {
+ pub fn stage_and_commit(&mut self, changeset: C) -> anyhow::Result > {
self.stage(changeset);
self.commit()
}
@@ -74,12 +80,6 @@ where
/// `C` represents the changeset; a datatype that records changes made to in-memory data structures
/// that are to be persisted, or retrieved from persistence.
pub trait PersistBackend {
- /// The error the backend returns when it fails to write.
- type WriteError: core::fmt::Debug;
-
- /// The error the backend returns when it fails to load changesets `C`.
- type LoadError: core::fmt::Debug;
-
/// Writes a changeset to the persistence backend.
///
/// It is up to the backend what it does with this. It could store every changeset in a list or
@@ -88,22 +88,18 @@ pub trait PersistBackend {
/// changesets had been applied sequentially.
///
/// [`load_from_persistence`]: Self::load_from_persistence
- fn write_changes(&mut self, changeset: &C) -> Result<(), Self::WriteError>;
+ fn write_changes(&mut self, changeset: &C) -> anyhow::Result<()>;
/// Return the aggregate changeset `C` from persistence.
- fn load_from_persistence(&mut self) -> Result, Self::LoadError>;
+ fn load_from_persistence(&mut self) -> anyhow::Result >;
}
impl PersistBackend for () {
- type WriteError = Infallible;
-
- type LoadError = Infallible;
-
- fn write_changes(&mut self, _changeset: &C) -> Result<(), Self::WriteError> {
+ fn write_changes(&mut self, _changeset: &C) -> anyhow::Result<()> {
Ok(())
}
- fn load_from_persistence(&mut self) -> Result, Self::LoadError> {
+ fn load_from_persistence(&mut self) -> anyhow::Result > {
Ok(None)
}
}
diff --git a/crates/file_store/Cargo.toml b/crates/file_store/Cargo.toml
index 599bbd32..0d382b4b 100644
--- a/crates/file_store/Cargo.toml
+++ b/crates/file_store/Cargo.toml
@@ -11,6 +11,7 @@ authors = ["Bitcoin Dev Kit Developers"]
readme = "README.md"
[dependencies]
+anyhow = { version = "1", default-features = false }
bdk_chain = { path = "../chain", version = "0.12.0", features = [ "serde", "miniscript" ] }
bincode = { version = "1" }
serde = { version = "1", features = ["derive"] }
diff --git a/crates/file_store/src/store.rs b/crates/file_store/src/store.rs
index 3682f003..16d8f7b7 100644
--- a/crates/file_store/src/store.rs
+++ b/crates/file_store/src/store.rs
@@ -1,21 +1,23 @@
+use crate::{bincode_options, EntryIter, FileError, IterError};
+use anyhow::anyhow;
+use bdk_chain::{Append, PersistBackend};
+use bincode::Options;
use std::{
- fmt::Debug,
+ fmt::{self, Debug},
fs::{File, OpenOptions},
io::{self, Read, Seek, Write},
marker::PhantomData,
path::Path,
};
-use bdk_chain::{Append, PersistBackend};
-use bincode::Options;
-
-use crate::{bincode_options, EntryIter, FileError, IterError};
-
/// Persists an append-only list of changesets (`C`) to a single file.
///
/// The changesets are the results of altering a tracker implementation (`T`).
#[derive(Debug)]
-pub struct Store {
+pub struct Store
+where
+ C: Sync + Send,
+{
magic_len: usize,
db_file: File,
marker: PhantomData,
@@ -23,24 +25,30 @@ pub struct Store {
impl PersistBackend for Store
where
- C: Append + serde::Serialize + serde::de::DeserializeOwned,
+ C: Append
+ + serde::Serialize
+ + serde::de::DeserializeOwned
+ + core::marker::Send
+ + core::marker::Sync,
{
- type WriteError = std::io::Error;
-
- type LoadError = IterError;
-
- fn write_changes(&mut self, changeset: &C) -> Result<(), Self::WriteError> {
+ fn write_changes(&mut self, changeset: &C) -> anyhow::Result<()> {
self.append_changeset(changeset)
+ .map_err(|e| anyhow!(e).context("failed to write changes to persistence backend"))
}
- fn load_from_persistence(&mut self) -> Result, Self::LoadError> {
- self.aggregate_changesets().map_err(|e| e.iter_error)
+ fn load_from_persistence(&mut self) -> anyhow::Result > {
+ self.aggregate_changesets()
+ .map_err(|e| anyhow!(e.iter_error).context("error loading from persistence backend"))
}
}
impl Store
where
- C: Append + serde::Serialize + serde::de::DeserializeOwned,
+ C: Append
+ + serde::Serialize
+ + serde::de::DeserializeOwned
+ + core::marker::Send
+ + core::marker::Sync,
{
/// Create a new [`Store`] file in write-only mode; error if the file exists.
///
@@ -182,7 +190,7 @@ where
bincode_options()
.serialize_into(&mut self.db_file, changeset)
.map_err(|e| match *e {
- bincode::ErrorKind::Io(inner) => inner,
+ bincode::ErrorKind::Io(error) => error,
unexpected_err => panic!("unexpected bincode error: {}", unexpected_err),
})?;
@@ -212,7 +220,7 @@ impl std::fmt::Display for AggregateChangesetsError {
}
}
-impl std::error::Error for AggregateChangesetsError {}
+impl std::error::Error for AggregateChangesetsError {}
#[cfg(test)]
mod test {
diff --git a/example-crates/example_cli/src/lib.rs b/example-crates/example_cli/src/lib.rs
index cac9f866..e7c4efec 100644
--- a/example-crates/example_cli/src/lib.rs
+++ b/example-crates/example_cli/src/lib.rs
@@ -31,7 +31,6 @@ pub type KeychainChangeSet = (
local_chain::ChangeSet,
indexed_tx_graph::ChangeSet >,
);
-pub type Database = Persist, C>;
#[derive(Parser)]
#[clap(author, version, about, long_about = None)]
@@ -440,7 +439,7 @@ pub fn planned_utxos(
graph: &Mutex>,
- db: &Mutex