]> Untitled Git - bdk/commitdiff
fix(wallet): remove generic from wallet
authorRob N <rob.netzke@gmail.com>
Sat, 13 Apr 2024 20:21:24 +0000 (10:21 -1000)
committerRob N <rob.netzke@gmail.com>
Mon, 15 Apr 2024 20:33:34 +0000 (10:33 -1000)
crates/bdk/Cargo.toml
crates/bdk/src/wallet/error.rs
crates/bdk/src/wallet/export.rs
crates/bdk/src/wallet/mod.rs
crates/bdk/src/wallet/tx_builder.rs
crates/chain/Cargo.toml
crates/chain/src/persist.rs
crates/file_store/Cargo.toml
crates/file_store/src/store.rs
example-crates/example_cli/src/lib.rs

index 900300445c7780d8aeaf471b7cf1a19d27b73e12..425199e101c26cdc39fbafe46c11ba35d16f17aa 100644 (file)
@@ -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 }
index 46cf8ef3c706ef47df3ab509c7ee44f9a64fc484..eaf811d6fc06bdba89519499634554659ee10b9e 100644 (file)
@@ -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<P> {
+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<P> {
     MiniscriptPsbt(MiniscriptPsbtError),
 }
 
-impl<P> fmt::Display for CreateTxError<P>
-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<P> From<descriptor::error::Error> for CreateTxError<P> {
+impl From<descriptor::error::Error> for CreateTxError {
     fn from(err: descriptor::error::Error) -> Self {
         CreateTxError::Descriptor(err)
     }
 }
 
-impl<P> From<PolicyError> for CreateTxError<P> {
+impl From<PolicyError> for CreateTxError {
     fn from(err: PolicyError) -> Self {
         CreateTxError::Policy(err)
     }
 }
 
-impl<P> From<MiniscriptPsbtError> for CreateTxError<P> {
+impl From<MiniscriptPsbtError> for CreateTxError {
     fn from(err: MiniscriptPsbtError) -> Self {
         CreateTxError::MiniscriptPsbt(err)
     }
 }
 
-impl<P> From<psbt::Error> for CreateTxError<P> {
+impl From<psbt::Error> for CreateTxError {
     fn from(err: psbt::Error) -> Self {
         CreateTxError::Psbt(err)
     }
 }
 
-impl<P> From<coin_selection::Error> for CreateTxError<P> {
+impl From<coin_selection::Error> for CreateTxError {
     fn from(err: coin_selection::Error) -> Self {
         CreateTxError::CoinSelection(err)
     }
 }
 
 #[cfg(feature = "std")]
-impl<P: core::fmt::Display + core::fmt::Debug> std::error::Error for CreateTxError<P> {}
+impl std::error::Error for CreateTxError {}
 
 #[derive(Debug)]
 /// Error returned from [`Wallet::build_fee_bump`]
index 50c91eb66f3b5f1acfe3df41eb564f275539f884..95252a01dda32e4fc7e70ea02ab2fd43de91e4e9 100644 (file)
@@ -53,9 +53,8 @@
 //! # Ok::<_, Box<dyn std::error::Error>>(())
 //! ```
 
-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<D>(
-        wallet: &Wallet<D>,
+    pub fn export_wallet(
+        wallet: &Wallet,
         label: &str,
         include_blockheight: bool,
     ) -> Result<Self, &'static str> {
@@ -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![],
index 846878823c4895d8ff44cad5d4e90975aa68c8af..b4eabab7690a293bf4c265404c133d7ba6232baa 100644 (file)
@@ -82,12 +82,12 @@ const COINBASE_MATURITY: u32 = 100;
 ///
 /// [`signer`]: crate::signer
 #[derive(Debug)]
-pub struct Wallet<D = ()> {
+pub struct Wallet {
     signers: Arc<SignersContainer>,
     change_signers: Arc<SignersContainer>,
     chain: LocalChain,
     indexed_graph: IndexedTxGraph<ConfirmationTimeHeightAnchor, KeychainTxOutIndex<KeychainKind>>,
-    persist: Persist<D, ChangeSet>,
+    persist: Persist<ChangeSet>,
     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<D> Wallet<D>
-where
-    D: PersistBackend<ChangeSet, WriteError = core::convert::Infallible>,
-{
+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<W> {
+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<W> fmt::Display for NewError<W>
-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<W> std::error::Error for NewError<W> 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<W> std::error::Error for NewError<W> where W: core::fmt::Display + core::fm
 ///
 /// [`load`]: Wallet::load
 #[derive(Debug)]
-pub enum LoadError<L> {
+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<L> {
     MissingGenesis,
 }
 
-impl<L> fmt::Display for LoadError<L>
-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<L> std::error::Error for LoadError<L> 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<L> std::error::Error for LoadError<L> 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<W, L> {
+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<W, L> {
     },
 }
 
-impl<W, L> fmt::Display for NewOrLoadError<W, L>
-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<W, L> std::error::Error for NewOrLoadError<W, L>
-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<D> Wallet<D> {
+impl Wallet {
     /// Initialize an empty [`Wallet`].
     pub fn new<E: IntoWalletDescriptor>(
         descriptor: E,
         change_descriptor: Option<E>,
-        db: D,
+        db: impl PersistBackend<ChangeSet> + Send + Sync + 'static,
         network: Network,
-    ) -> Result<Self, NewError<D::WriteError>>
-    where
-        D: PersistBackend<ChangeSet>,
-    {
+    ) -> Result<Self, NewError> {
         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<D> Wallet<D> {
     pub fn new_with_genesis_hash<E: IntoWalletDescriptor>(
         descriptor: E,
         change_descriptor: Option<E>,
-        mut db: D,
+        mut db: impl PersistBackend<ChangeSet> + Send + Sync + 'static,
         network: Network,
         genesis_hash: BlockHash,
-    ) -> Result<Self, NewError<D::WriteError>>
-    where
-        D: PersistBackend<ChangeSet>,
-    {
+    ) -> Result<Self, NewError> {
         if let Ok(changeset) = db.load_from_persistence() {
             if changeset.is_some() {
                 return Err(NewError::NonEmptyDatabase);
@@ -538,7 +515,7 @@ impl<D> Wallet<D> {
             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<D> Wallet<D> {
     pub fn load<E: IntoWalletDescriptor>(
         descriptor: E,
         change_descriptor: Option<E>,
-        mut db: D,
-    ) -> Result<Self, LoadError<D::LoadError>>
-    where
-        D: PersistBackend<ChangeSet>,
-    {
+        mut db: impl PersistBackend<ChangeSet> + Send + Sync + 'static,
+    ) -> Result<Self, LoadError> {
         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<D> Wallet<D> {
     fn load_from_changeset<E: IntoWalletDescriptor>(
         descriptor: E,
         change_descriptor: Option<E>,
-        db: D,
+        db: impl PersistBackend<ChangeSet> + Send + Sync + 'static,
         changeset: ChangeSet,
-    ) -> Result<Self, LoadError<D::LoadError>>
-    where
-        D: PersistBackend<ChangeSet>,
-    {
+    ) -> Result<Self, LoadError> {
         let secp = Secp256k1::new();
         let network = changeset.network.ok_or(LoadError::MissingNetwork)?;
         let chain =
@@ -608,12 +579,9 @@ impl<D> Wallet<D> {
     pub fn new_or_load<E: IntoWalletDescriptor>(
         descriptor: E,
         change_descriptor: Option<E>,
-        db: D,
+        db: impl PersistBackend<ChangeSet> + Send + Sync + 'static,
         network: Network,
-    ) -> Result<Self, NewOrLoadError<D::WriteError, D::LoadError>>
-    where
-        D: PersistBackend<ChangeSet>,
-    {
+    ) -> Result<Self, NewOrLoadError> {
         let genesis_hash = genesis_block(network).block_hash();
         Self::new_or_load_with_genesis_hash(
             descriptor,
@@ -633,21 +601,20 @@ impl<D> Wallet<D> {
     pub fn new_or_load_with_genesis_hash<E: IntoWalletDescriptor>(
         descriptor: E,
         change_descriptor: Option<E>,
-        mut db: D,
+        mut db: impl PersistBackend<ChangeSet> + Send + Sync + 'static,
         network: Network,
         genesis_hash: BlockHash,
-    ) -> Result<Self, NewOrLoadError<D::WriteError, D::LoadError>>
-    where
-        D: PersistBackend<ChangeSet>,
-    {
-        let changeset = db.load_from_persistence().map_err(NewOrLoadError::Load)?;
+    ) -> Result<Self, NewOrLoadError> {
+        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<D> Wallet<D> {
                     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<D> Wallet<D> {
     ///
     /// 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<AddressInfo, D::WriteError>
-    where
-        D: PersistBackend<ChangeSet>,
-    {
+    pub fn try_get_address(&mut self, address_index: AddressIndex) -> anyhow::Result<AddressInfo> {
         self._get_address(KeychainKind::External, address_index)
     }
 
@@ -742,10 +703,7 @@ impl<D> Wallet<D> {
     pub fn try_get_internal_address(
         &mut self,
         address_index: AddressIndex,
-    ) -> Result<AddressInfo, D::WriteError>
-    where
-        D: PersistBackend<ChangeSet>,
-    {
+    ) -> anyhow::Result<AddressInfo> {
         self._get_address(KeychainKind::Internal, address_index)
     }
 
@@ -770,10 +728,7 @@ impl<D> Wallet<D> {
         &mut self,
         keychain: KeychainKind,
         address_index: AddressIndex,
-    ) -> Result<AddressInfo, D::WriteError>
-    where
-        D: PersistBackend<ChangeSet>,
-    {
+    ) -> anyhow::Result<AddressInfo> {
         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<D> Wallet<D> {
     /// [`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<ChangeSet>,
-    {
+    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<D> Wallet<D> {
     /// ```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<D> Wallet<D> {
     /// ```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<D> Wallet<D> {
     /// ```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<D> Wallet<D> {
     /// ```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<D> Wallet<D> {
     /// ```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<D> Wallet<D> {
     /// ```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<D> Wallet<D> {
     /// ```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<D> Wallet<D> {
     pub fn insert_checkpoint(
         &mut self,
         block_id: BlockId,
-    ) -> Result<bool, local_chain::AlterCheckPointError>
-    where
-        D: PersistBackend<ChangeSet>,
-    {
+    ) -> Result<bool, local_chain::AlterCheckPointError> {
         let changeset = self.chain.insert_block(block_id)?;
         let changed = !changeset.is_empty();
         self.persist.stage(changeset.into());
@@ -1118,10 +1067,7 @@ impl<D> Wallet<D> {
         &mut self,
         tx: Transaction,
         position: ConfirmationTime,
-    ) -> Result<bool, InsertTxError>
-    where
-        D: PersistBackend<ChangeSet>,
-    {
+    ) -> Result<bool, InsertTxError> {
         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<D> Wallet<D> {
     /// ```
     ///
     /// [`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<D> Wallet<D> {
         &mut self,
         coin_selection: Cs,
         params: TxParams,
-    ) -> Result<Psbt, CreateTxError<D::WriteError>>
-    where
-        D: PersistBackend<ChangeSet>,
-    {
+    ) -> Result<Psbt, CreateTxError> {
         let external_descriptor = self
             .indexed_graph
             .index
@@ -1283,7 +1226,7 @@ impl<D> Wallet<D> {
         let internal_policy = internal_descriptor
             .as_ref()
             .map(|desc| {
-                Ok::<_, CreateTxError<D::WriteError>>(
+                Ok::<_, CreateTxError>(
                     desc.extract_policy(&self.change_signers, BuildSatisfaction::None, &self.secp)?
                         .unwrap(),
                 )
@@ -1320,7 +1263,7 @@ impl<D> Wallet<D> {
         )?;
         let internal_requirements = internal_policy
             .map(|policy| {
-                Ok::<_, CreateTxError<D::WriteError>>(
+                Ok::<_, CreateTxError>(
                     policy.get_condition(
                         params
                             .internal_policy_path
@@ -1647,7 +1590,7 @@ impl<D> Wallet<D> {
     pub fn build_fee_bump(
         &mut self,
         txid: Txid,
-    ) -> Result<TxBuilder<'_, D, DefaultCoinSelectionAlgorithm, BumpFee>, BuildFeeBumpError> {
+    ) -> Result<TxBuilder<'_, DefaultCoinSelectionAlgorithm, BumpFee>, 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<D> Wallet<D> {
         tx: Transaction,
         selected: Vec<Utxo>,
         params: TxParams,
-    ) -> Result<Psbt, CreateTxError<D::WriteError>>
-    where
-        D: PersistBackend<ChangeSet>,
-    {
+    ) -> Result<Psbt, CreateTxError> {
         let mut psbt = Psbt::from_unsigned_tx(tx)?;
 
         if params.add_global_xpubs {
@@ -2242,10 +2182,7 @@ impl<D> Wallet<D> {
         utxo: LocalOutput,
         sighash_type: Option<psbt::PsbtSighashType>,
         only_witness_utxo: bool,
-    ) -> Result<psbt::Input, CreateTxError<D::WriteError>>
-    where
-        D: PersistBackend<ChangeSet>,
-    {
+    ) -> Result<psbt::Input, CreateTxError> {
         // 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<D> Wallet<D> {
     /// transactions related to your wallet into it.
     ///
     /// [`commit`]: Self::commit
-    pub fn apply_update(&mut self, update: Update) -> Result<(), CannotConnectError>
-    where
-        D: PersistBackend<ChangeSet>,
-    {
+    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<D> Wallet<D> {
         changeset.append(ChangeSet::from(
             self.indexed_graph.apply_update(update.graph),
         ));
-
         self.persist.stage(changeset);
         Ok(())
     }
@@ -2365,20 +2298,14 @@ impl<D> Wallet<D> {
     /// This returns whether the `update` resulted in any changes.
     ///
     /// [`staged`]: Self::staged
-    pub fn commit(&mut self) -> Result<bool, D::WriteError>
-    where
-        D: PersistBackend<ChangeSet>,
-    {
+    pub fn commit(&mut self) -> anyhow::Result<bool> {
         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<ChangeSet>,
-    {
+    pub fn staged(&self) -> &ChangeSet {
         self.persist.staged()
     }
 
@@ -2404,10 +2331,7 @@ impl<D> Wallet<D> {
     /// 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<ChangeSet>,
-    {
+    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<D> Wallet<D> {
         block: &Block,
         height: u32,
         connected_to: BlockId,
-    ) -> Result<(), ApplyHeaderError>
-    where
-        D: PersistBackend<ChangeSet>,
-    {
+    ) -> Result<(), ApplyHeaderError> {
         let mut changeset = ChangeSet::default();
         changeset.append(
             self.chain
@@ -2468,9 +2389,7 @@ impl<D> Wallet<D> {
     pub fn apply_unconfirmed_txs<'t>(
         &mut self,
         unconfirmed_txs: impl IntoIterator<Item = (&'t Transaction, u64)>,
-    ) where
-        D: PersistBackend<ChangeSet>,
-    {
+    ) {
         let indexed_graph_changeset = self
             .indexed_graph
             .batch_insert_relevant_unconfirmed(unconfirmed_txs);
@@ -2478,7 +2397,7 @@ impl<D> Wallet<D> {
     }
 }
 
-impl<D> AsRef<bdk_chain::tx_graph::TxGraph<ConfirmationTimeHeightAnchor>> for Wallet<D> {
+impl AsRef<bdk_chain::tx_graph::TxGraph<ConfirmationTimeHeightAnchor>> for Wallet {
     fn as_ref(&self) -> &bdk_chain::tx_graph::TxGraph<ConfirmationTimeHeightAnchor> {
         self.indexed_graph.graph()
     }
index 4009dab1aa0918ea05d2bbd44e6b8ae3c17389c7..e7df86678d34ec9f840c61a70f3a757327287a0c 100644 (file)
@@ -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<RefCell<&'a mut Wallet<D>>>,
+pub struct TxBuilder<'a, Cs, Ctx> {
+    pub(crate) wallet: Rc<RefCell<&'a mut Wallet>>,
     pub(crate) params: TxParams,
     pub(crate) coin_selection: Cs,
     pub(crate) phantom: PhantomData<Ctx>,
@@ -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<P: CoinSelectionAlgorithm>(
         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<Psbt, CreateTxError<D::WriteError>>
-    where
-        D: PersistBackend<ChangeSet>,
-    {
+    pub fn finish(self) -> Result<Psbt, CreateTxError> {
         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.
index fda5924d2f02f9306b06286fd8dfabfd16860eeb..56cadd85a170c545f5836770d2b92e62bfdf2de1 100644 (file)
@@ -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"] }
 
index 5ddd622a3dd00a0bc3e30ca01bcf493e7fe4ae95..64efa55b4d306b816328cc7c7e5c0d93db07561b 100644 (file)
@@ -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<B, C> {
-    backend: B,
+pub struct Persist<C> {
+    backend: Box<dyn PersistBackend<C> + Send + Sync>,
     stage: C,
 }
 
-impl<B, C> Persist<B, C>
+impl<C: fmt::Debug> fmt::Debug for Persist<C> {
+    fn fmt(&self, fmt: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> {
+        write!(fmt, "{:?}", self.stage)?;
+        Ok(())
+    }
+}
+
+impl<C> Persist<C>
 where
-    B: PersistBackend<C>,
     C: Default + Append,
 {
     /// Create a new [`Persist`] from [`PersistBackend`].
-    pub fn new(backend: B) -> Self {
+    pub fn new(backend: impl PersistBackend<C> + 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<Option<C>, B::WriteError> {
+    pub fn commit(&mut self) -> anyhow::Result<Option<C>> {
         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<Option<C>, B::WriteError> {
+    pub fn stage_and_commit(&mut self, changeset: C) -> anyhow::Result<Option<C>> {
         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<C> {
-    /// 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<C> {
     /// 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<Option<C>, Self::LoadError>;
+    fn load_from_persistence(&mut self) -> anyhow::Result<Option<C>>;
 }
 
 impl<C> PersistBackend<C> 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<Option<C>, Self::LoadError> {
+    fn load_from_persistence(&mut self) -> anyhow::Result<Option<C>> {
         Ok(None)
     }
 }
index 599bbd32c23f63f3be4b36d35d7d41873860b8e5..0d382b4bf7664a637f97d9d62fa13c89c2980b06 100644 (file)
@@ -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"] }
index 3682f0033d563cbabd2bb5734f02d1837e773df3..16d8f7b72adb28ad321a6507cb63c1b07cd9968a 100644 (file)
@@ -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<C> {
+pub struct Store<C>
+where
+    C: Sync + Send,
+{
     magic_len: usize,
     db_file: File,
     marker: PhantomData<C>,
@@ -23,24 +25,30 @@ pub struct Store<C> {
 
 impl<C> PersistBackend<C> for Store<C>
 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<Option<C>, Self::LoadError> {
-        self.aggregate_changesets().map_err(|e| e.iter_error)
+    fn load_from_persistence(&mut self) -> anyhow::Result<Option<C>> {
+        self.aggregate_changesets()
+            .map_err(|e| anyhow!(e.iter_error).context("error loading from persistence backend"))
     }
 }
 
 impl<C> Store<C>
 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<C> std::fmt::Display for AggregateChangesetsError<C> {
     }
 }
 
-impl<C: std::fmt::Debug> std::error::Error for AggregateChangesetsError<C> {}
+impl<C: fmt::Debug> std::error::Error for AggregateChangesetsError<C> {}
 
 #[cfg(test)]
 mod test {
index cac9f8667b0e4ebb618b01624b6e2bf23c02b984..e7c4efece201383879e82a3dab1e55ee677f6b71 100644 (file)
@@ -31,7 +31,6 @@ pub type KeychainChangeSet<A> = (
     local_chain::ChangeSet,
     indexed_tx_graph::ChangeSet<A, keychain::ChangeSet<Keychain>>,
 );
-pub type Database<C> = Persist<Store<C>, C>;
 
 #[derive(Parser)]
 #[clap(author, version, about, long_about = None)]
@@ -440,7 +439,7 @@ pub fn planned_utxos<A: Anchor, O: ChainOracle, K: Clone + bdk_tmp_plan::CanDeri
 
 pub fn handle_commands<CS: clap::Subcommand, S: clap::Args, A: Anchor, O: ChainOracle, C>(
     graph: &Mutex<KeychainTxGraph<A>>,
-    db: &Mutex<Database<C>>,
+    db: &Mutex<Persist<C>>,
     chain: &Mutex<O>,
     keymap: &BTreeMap<DescriptorPublicKey, DescriptorSecretKey>,
     network: Network,
@@ -667,7 +666,7 @@ pub struct Init<CS: clap::Subcommand, S: clap::Args, C> {
     /// Keychain-txout index.
     pub index: KeychainTxOutIndex<Keychain>,
     /// Persistence backend.
-    pub db: Mutex<Database<C>>,
+    pub db: Mutex<Persist<C>>,
     /// Initial changeset.
     pub init_changeset: C,
 }
@@ -679,7 +678,13 @@ pub fn init<CS: clap::Subcommand, S: clap::Args, C>(
     db_default_path: &str,
 ) -> anyhow::Result<Init<CS, S, C>>
 where
-    C: Default + Append + Serialize + DeserializeOwned,
+    C: Default
+        + Append
+        + Serialize
+        + DeserializeOwned
+        + core::marker::Send
+        + core::marker::Sync
+        + 'static,
 {
     if std::env::var("BDK_DB_PATH").is_err() {
         std::env::set_var("BDK_DB_PATH", db_default_path);
@@ -715,7 +720,7 @@ where
         args,
         keymap,
         index,
-        db: Mutex::new(Database::new(db_backend)),
+        db: Mutex::new(Persist::new(db_backend)),
         init_changeset,
     })
 }