From e51af49ffa951e14203ac2b465ade351dd90f6cd Mon Sep 17 00:00:00 2001 From: Rob N Date: Sat, 13 Apr 2024 10:21:24 -1000 Subject: [PATCH] fix(wallet): remove generic from wallet --- crates/bdk/Cargo.toml | 1 + crates/bdk/src/wallet/error.rs | 25 ++- crates/bdk/src/wallet/export.rs | 9 +- crates/bdk/src/wallet/mod.rs | 225 +++++++++----------------- crates/bdk/src/wallet/tx_builder.rs | 24 ++- crates/chain/Cargo.toml | 1 + crates/chain/src/persist.rs | 46 +++--- crates/file_store/Cargo.toml | 1 + crates/file_store/src/store.rs | 44 ++--- example-crates/example_cli/src/lib.rs | 15 +- 10 files changed, 157 insertions(+), 234 deletions(-) diff --git a/crates/bdk/Cargo.toml b/crates/bdk/Cargo.toml index 900300445..425199e10 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 46cf8ef3c..eaf811d6f 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 50c91eb66..95252a01d 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 846878823..b4eabab76 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 4009dab1a..e7df86678 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 fda5924d2..56cadd85a 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 5ddd622a3..64efa55b4 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 599bbd32c..0d382b4bf 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 3682f0033..16d8f7b72 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 cac9f8667..e7c4efece 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>, + db: &Mutex>, chain: &Mutex, keymap: &BTreeMap, network: Network, @@ -667,7 +666,7 @@ pub struct Init { /// Keychain-txout index. pub index: KeychainTxOutIndex, /// Persistence backend. - pub db: Mutex>, + pub db: Mutex>, /// Initial changeset. pub init_changeset: C, } @@ -679,7 +678,13 @@ pub fn init( db_default_path: &str, ) -> anyhow::Result> 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, }) }