]> Untitled Git - bdk/commitdiff
refactor(persist): update file_store, sqlite, wallet to use bdk_chain::persist
authorSteve Myers <steve@notmandatory.org>
Sat, 1 Jun 2024 05:06:20 +0000 (00:06 -0500)
committerSteve Myers <steve@notmandatory.org>
Thu, 13 Jun 2024 17:56:25 +0000 (12:56 -0500)
Also update examples and remove bdk_persist crate.

39 files changed:
Cargo.toml
crates/chain/Cargo.toml
crates/chain/src/keychain/txout_index.rs
crates/chain/src/lib.rs
crates/chain/src/persist.rs
crates/file_store/Cargo.toml
crates/file_store/README.md
crates/file_store/src/store.rs
crates/hwi/src/lib.rs
crates/persist/Cargo.toml [deleted file]
crates/persist/README.md [deleted file]
crates/persist/src/changeset.rs [deleted file]
crates/persist/src/lib.rs [deleted file]
crates/persist/src/persist.rs [deleted file]
crates/sqlite/Cargo.toml
crates/sqlite/README.md
crates/sqlite/src/store.rs
crates/wallet/Cargo.toml
crates/wallet/README.md
crates/wallet/examples/compiler.rs
crates/wallet/src/descriptor/template.rs
crates/wallet/src/wallet/coin_selection.rs
crates/wallet/src/wallet/error.rs
crates/wallet/src/wallet/export.rs
crates/wallet/src/wallet/hardwaresigner.rs
crates/wallet/src/wallet/mod.rs
crates/wallet/src/wallet/signer.rs
crates/wallet/src/wallet/tx_builder.rs
crates/wallet/tests/common.rs
crates/wallet/tests/wallet.rs
example-crates/example_bitcoind_rpc_polling/src/main.rs
example-crates/example_cli/Cargo.toml
example-crates/example_cli/src/lib.rs
example-crates/example_electrum/src/main.rs
example-crates/example_esplora/src/main.rs
example-crates/wallet_electrum/src/main.rs
example-crates/wallet_esplora_async/src/main.rs
example-crates/wallet_esplora_blocking/src/main.rs
example-crates/wallet_rpc/src/main.rs

index ea1b8f5048c7113d42f46e364fdb9bbedc967bf6..201bb21f3bcabe707939a800cfb54eb1cd5bae5e 100644 (file)
@@ -9,7 +9,6 @@ members = [
     "crates/esplora",
     "crates/bitcoind_rpc",
     "crates/hwi",
-    "crates/persist",
     "crates/testenv",
     "example-crates/example_cli",
     "example-crates/example_electrum",
index e6adfe5e164091ec60fc1cd1abe95e3045de8c74..293a24458c2804657d6468ac88cde1d0a8d640ea 100644 (file)
@@ -26,8 +26,7 @@ rand = "0.8"
 proptest = "1.2.0"
 
 [features]
-default = ["std", "miniscript", "persist"]
+default = ["std", "miniscript"]
 std = ["bitcoin/std", "miniscript?/std"]
 serde = ["serde_crate", "bitcoin/serde", "miniscript?/serde"]
-persist = ["miniscript"]
 async = ["async-trait"]
index cf0f21f881709146237e54c74fab72671e6c8879..e4fad3d5ad1c2bf186e021f7a25b8094b8e512ba 100644 (file)
@@ -207,7 +207,7 @@ impl<K> KeychainTxOutIndex<K> {
 impl<K: Clone + Ord + Debug> KeychainTxOutIndex<K> {
     /// Return a reference to the internal [`SpkTxOutIndex`].
     ///
-    /// **WARNING:** The internal index will contain lookahead spks. Refer to
+    /// **WARNING**: The internal index will contain lookahead spks. Refer to
     /// [struct-level docs](KeychainTxOutIndex) for more about `lookahead`.
     pub fn inner(&self) -> &SpkTxOutIndex<(K, u32)> {
         &self.inner
index 976c3d0c93bccae094b3d70eec33d910e2ee57af..6ce94254b9ed24b388c72b57a23950c081bca738 100644 (file)
@@ -50,7 +50,7 @@ pub use descriptor_ext::{DescriptorExt, DescriptorId};
 mod spk_iter;
 #[cfg(feature = "miniscript")]
 pub use spk_iter::*;
-#[cfg(feature = "persist")]
+#[cfg(feature = "miniscript")]
 pub mod persist;
 pub mod spk_client;
 
index a953e724f5fac78521fab4cab61abadb29802ec0..49973c163d419f54cb9bd589ef4daa066fbb4395 100644 (file)
@@ -1,9 +1,6 @@
-//! This module is home to the [`Persist`] trait which defines the behavior of a data store
+//! This module is home to the [`PersistBackend`] trait which defines the behavior of a data store
 //! required to persist changes made to BDK data structures.
 //!
-//! The [`StagedPersist`] type provides a convenient wrapper around implementations of [`Persist`] that
-//! allows changes to be staged before committing them.
-//!
 //! The [`CombinedChangeSet`] type encapsulates a combination of [`crate`] structures that are
 //! typically persisted together.
 
@@ -16,7 +13,6 @@ use bitcoin::Network;
 use core::convert::Infallible;
 use core::default::Default;
 use core::fmt::{Debug, Display};
-use core::mem;
 
 /// A changeset containing [`crate`] structures typically persisted together.
 #[derive(Debug, Clone, PartialEq)]
@@ -92,7 +88,7 @@ impl<K, A> From<indexed_tx_graph::ChangeSet<A, keychain::ChangeSet<K>>>
 ///
 /// `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 Persist<C> {
+pub trait PersistBackend<C> {
     /// The error the backend returns when it fails to write.
     type WriteError: Debug + Display;
 
@@ -113,7 +109,7 @@ pub trait Persist<C> {
     fn load_changes(&mut self) -> Result<Option<C>, Self::LoadError>;
 }
 
-impl<C> Persist<C> for () {
+impl<C> PersistBackend<C> for () {
     type WriteError = Infallible;
     type LoadError = Infallible;
 
@@ -132,7 +128,7 @@ impl<C> Persist<C> for () {
 /// `C` represents the changeset; a datatype that records changes made to in-memory data structures
 /// that are to be persisted, or retrieved from persistence.
 #[async_trait]
-pub trait PersistAsync<C> {
+pub trait PersistBackendAsync<C> {
     /// The error the backend returns when it fails to write.
     type WriteError: Debug + Display;
 
@@ -155,7 +151,7 @@ pub trait PersistAsync<C> {
 
 #[cfg(feature = "async")]
 #[async_trait]
-impl<C> PersistAsync<C> for () {
+impl<C> PersistBackendAsync<C> for () {
     type WriteError = Infallible;
     type LoadError = Infallible;
 
@@ -167,288 +163,3 @@ impl<C> PersistAsync<C> for () {
         Ok(None)
     }
 }
-
-/// `StagedPersist` adds a convenient staging area for changesets before they are persisted.
-///
-/// Not all changes to the in-memory representation needs to be written to disk right away, so
-/// [`crate::persist::StagedPersist::stage`] can be used to *stage* changes first and then
-/// [`crate::persist::StagedPersist::commit`] can be used to write changes to disk.
-pub struct StagedPersist<C, P: Persist<C>> {
-    inner: P,
-    stage: C,
-}
-
-impl<C, P: Persist<C>> Persist<C> for StagedPersist<C, P> {
-    type WriteError = P::WriteError;
-    type LoadError = P::LoadError;
-
-    fn write_changes(&mut self, changeset: &C) -> Result<(), Self::WriteError> {
-        self.inner.write_changes(changeset)
-    }
-
-    fn load_changes(&mut self) -> Result<Option<C>, Self::LoadError> {
-        self.inner.load_changes()
-    }
-}
-
-impl<C, P> StagedPersist<C, P>
-where
-    C: Default + Append,
-    P: Persist<C>,
-{
-    /// Create a new [`StagedPersist`] adding staging to an inner data store that implements
-    /// [`Persist`].
-    pub fn new(persist: P) -> Self {
-        Self {
-            inner: persist,
-            stage: Default::default(),
-        }
-    }
-
-    /// Stage a `changeset` to be committed later with [`commit`].
-    ///
-    /// [`commit`]: Self::commit
-    pub fn stage(&mut self, changeset: C) {
-        self.stage.append(changeset)
-    }
-
-    /// Get the changes that have not been committed yet.
-    pub fn staged(&self) -> &C {
-        &self.stage
-    }
-
-    /// Take the changes that have not been committed yet.
-    ///
-    /// New staged is set to default;
-    pub fn take_staged(&mut self) -> C {
-        mem::take(&mut self.stage)
-    }
-
-    /// Commit the staged changes to the underlying persistence backend.
-    ///
-    /// Changes that are committed (if any) are returned.
-    ///
-    /// # Error
-    ///
-    /// Returns a backend-defined error if this fails.
-    pub fn commit(&mut self) -> Result<Option<C>, P::WriteError> {
-        if self.staged().is_empty() {
-            return Ok(None);
-        }
-        let staged = self.take_staged();
-        self.write_changes(&staged)
-            // if written successfully, take and return `self.stage`
-            .map(|_| Some(staged))
-    }
-
-    /// Stages a new changeset and commits it (along with any other previously staged changes) to
-    /// the persistence backend
-    ///
-    /// Convenience method for calling [`stage`] and then [`commit`].
-    ///
-    /// [`stage`]: Self::stage
-    /// [`commit`]: Self::commit
-    pub fn stage_and_commit(&mut self, changeset: C) -> Result<Option<C>, P::WriteError> {
-        self.stage(changeset);
-        self.commit()
-    }
-}
-
-#[cfg(feature = "async")]
-/// `StagedPersistAsync` adds a convenient async staging area for changesets before they are persisted.
-///
-/// Not all changes to the in-memory representation needs to be written to disk right away, so
-/// [`StagedPersistAsync::stage`] can be used to *stage* changes first and then
-/// [`StagedPersistAsync::commit`] can be used to write changes to disk.
-pub struct StagedPersistAsync<C, P: PersistAsync<C>> {
-    inner: P,
-    staged: C,
-}
-
-#[cfg(feature = "async")]
-#[async_trait]
-impl<C: Send + Sync, P: PersistAsync<C> + Send> PersistAsync<C> for StagedPersistAsync<C, P> {
-    type WriteError = P::WriteError;
-    type LoadError = P::LoadError;
-
-    async fn write_changes(&mut self, changeset: &C) -> Result<(), Self::WriteError> {
-        self.inner.write_changes(changeset).await
-    }
-
-    async fn load_changes(&mut self) -> Result<Option<C>, Self::LoadError> {
-        self.inner.load_changes().await
-    }
-}
-
-#[cfg(feature = "async")]
-impl<C, P> StagedPersistAsync<C, P>
-where
-    C: Default + Append + Send + Sync,
-    P: PersistAsync<C> + Send,
-{
-    /// Create a new [`StagedPersistAsync`] adding staging to an inner data store that implements
-    /// [`PersistAsync`].
-    pub fn new(persist: P) -> Self {
-        Self {
-            inner: persist,
-            staged: Default::default(),
-        }
-    }
-
-    /// Stage a `changeset` to be committed later with [`commit`].
-    ///
-    /// [`commit`]: Self::commit
-    pub fn stage(&mut self, changeset: C) {
-        self.staged.append(changeset)
-    }
-
-    /// Get the changes that have not been committed yet.
-    pub fn staged(&self) -> &C {
-        &self.staged
-    }
-
-    /// Take the changes that have not been committed yet.
-    ///
-    /// New staged is set to default;
-    pub fn take_staged(&mut self) -> C {
-        mem::take(&mut self.staged)
-    }
-
-    /// Commit the staged changes to the underlying persistence backend.
-    ///
-    /// Changes that are committed (if any) are returned.
-    ///
-    /// # Error
-    ///
-    /// Returns a backend-defined error if this fails.
-    pub async fn commit(&mut self) -> Result<Option<C>, P::WriteError> {
-        if self.staged().is_empty() {
-            return Ok(None);
-        }
-        let staged = self.take_staged();
-        self.write_changes(&staged)
-            .await
-            // if written successfully, take and return `self.stage`
-            .map(|_| Some(staged))
-    }
-
-    /// Stages a new changeset and commits it (along with any other previously staged changes) to
-    /// the persistence backend
-    ///
-    /// Convenience method for calling [`stage`] and then [`commit`].
-    ///
-    /// [`stage`]: Self::stage
-    /// [`commit`]: Self::commit
-    pub async fn stage_and_commit(&mut self, changeset: C) -> Result<Option<C>, P::WriteError> {
-        self.stage(changeset);
-        self.commit().await
-    }
-}
-
-#[cfg(test)]
-mod test {
-    extern crate core;
-
-    use crate::persist::{Persist, StagedPersist};
-    use crate::Append;
-    use std::error::Error;
-    use std::fmt::{self, Display, Formatter};
-    use std::prelude::rust_2015::{String, ToString};
-    use TestError::FailedWrite;
-
-    struct TestBackend<C: Default + Append + Clone + ToString> {
-        changeset: C,
-    }
-
-    #[derive(Debug, Eq, PartialEq)]
-    enum TestError {
-        FailedWrite,
-        FailedLoad,
-    }
-
-    impl Display for TestError {
-        fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
-            write!(f, "{:?}", self)
-        }
-    }
-
-    impl Error for TestError {}
-
-    #[derive(Clone, Default)]
-    struct TestChangeSet(Option<String>);
-
-    impl fmt::Display for TestChangeSet {
-        fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
-            write!(f, "{}", self.clone().0.unwrap_or_default())
-        }
-    }
-
-    impl Append for TestChangeSet {
-        fn append(&mut self, other: Self) {
-            if other.0.is_some() {
-                self.0 = other.0
-            }
-        }
-
-        fn is_empty(&self) -> bool {
-            self.0.is_none()
-        }
-    }
-
-    impl<C> Persist<C> for TestBackend<C>
-    where
-        C: Default + Append + Clone + ToString,
-    {
-        type WriteError = TestError;
-        type LoadError = TestError;
-
-        fn write_changes(&mut self, changeset: &C) -> Result<(), Self::WriteError> {
-            if changeset.to_string() == "ERROR" {
-                Err(FailedWrite)
-            } else {
-                self.changeset = changeset.clone();
-                Ok(())
-            }
-        }
-
-        fn load_changes(&mut self) -> Result<Option<C>, Self::LoadError> {
-            if self.changeset.to_string() == "ERROR" {
-                Err(Self::LoadError::FailedLoad)
-            } else {
-                Ok(Some(self.changeset.clone()))
-            }
-        }
-    }
-
-    #[test]
-    fn test_persist_stage_commit() {
-        let backend = TestBackend {
-            changeset: TestChangeSet(None),
-        };
-
-        let mut staged_backend = StagedPersist::new(backend);
-        staged_backend.stage(TestChangeSet(Some("ONE".to_string())));
-        staged_backend.stage(TestChangeSet(None));
-        staged_backend.stage(TestChangeSet(Some("TWO".to_string())));
-        let result = staged_backend.commit();
-        assert!(matches!(result, Ok(Some(TestChangeSet(Some(v)))) if v == *"TWO".to_string()));
-
-        let result = staged_backend.commit();
-        assert!(matches!(result, Ok(None)));
-
-        staged_backend.stage(TestChangeSet(Some("TWO".to_string())));
-        let result = staged_backend.stage_and_commit(TestChangeSet(Some("ONE".to_string())));
-        assert!(matches!(result, Ok(Some(TestChangeSet(Some(v)))) if v == *"ONE".to_string()));
-    }
-
-    #[test]
-    fn test_persist_commit_error() {
-        let backend = TestBackend {
-            changeset: TestChangeSet(None),
-        };
-        let mut staged_backend = StagedPersist::new(backend);
-        staged_backend.stage(TestChangeSet(Some("ERROR".to_string())));
-        let result = staged_backend.commit();
-        assert!(matches!(result, Err(e) if e == FailedWrite));
-    }
-}
index 09b16c140be619db5d3b6e740a18b79a19df9c04..392424cb38128b9fe93bbc246fa184bcb400b1da 100644 (file)
@@ -5,15 +5,13 @@ edition = "2021"
 license = "MIT OR Apache-2.0"
 repository = "https://github.com/bitcoindevkit/bdk"
 documentation = "https://docs.rs/bdk_file_store"
-description = "A simple append-only flat file implementation of Persist for Bitcoin Dev Kit."
+description = "A simple append-only flat file implementation of PersistBackend for Bitcoin Dev Kit."
 keywords = ["bitcoin", "persist", "persistence", "bdk", "file"]
 authors = ["Bitcoin Dev Kit Developers"]
 readme = "README.md"
 
 [dependencies]
-anyhow = { version = "1", default-features = false }
 bdk_chain = { path = "../chain", version = "0.15.0", features = [ "serde", "miniscript" ] }
-bdk_persist = { path = "../persist", version = "0.3.0"}
 bincode = { version = "1" }
 serde = { version = "1", features = ["derive"] }
 
index 58b572ce002638936cba2862bee64582ff24079a..cad196b2ddc3c14f54e99cc1b244ed0f3ec940ac 100644 (file)
@@ -1,6 +1,6 @@
 # BDK File Store
 
-This is a simple append-only flat file implementation of [`PersistBackend`](bdk_persist::PersistBackend).
+This is a simple append-only flat file implementation of [`PersistBackend`](bdk_chain::persist::PersistBackend).
 
 The main structure is [`Store`] which works with any [`bdk_chain`] based changesets to persist data into a flat file.
 
index 6cea927657492226907264deeaef1cc203f2a654..2f451db722016ae5299bdad158ed0b0f3a18cb07 100644 (file)
@@ -1,7 +1,6 @@
 use crate::{bincode_options, EntryIter, FileError, IterError};
-use anyhow::anyhow;
+use bdk_chain::persist::PersistBackend;
 use bdk_chain::Append;
-use bdk_persist::PersistBackend;
 use bincode::Options;
 use std::{
     fmt::{self, Debug},
@@ -25,19 +24,21 @@ where
 impl<C> PersistBackend<C> for Store<C>
 where
     C: Append
+        + Debug
         + serde::Serialize
         + serde::de::DeserializeOwned
         + core::marker::Send
         + core::marker::Sync,
 {
-    fn write_changes(&mut self, changeset: &C) -> anyhow::Result<()> {
+    type WriteError = io::Error;
+    type LoadError = AggregateChangesetsError<C>;
+
+    fn write_changes(&mut self, changeset: &C) -> Result<(), Self::WriteError> {
         self.append_changeset(changeset)
-            .map_err(|e| anyhow!(e).context("failed to write changes to persistence backend"))
     }
 
-    fn load_from_persistence(&mut self) -> anyhow::Result<Option<C>> {
+    fn load_changes(&mut self) -> Result<Option<C>, Self::LoadError> {
         self.aggregate_changesets()
-            .map_err(|e| anyhow!(e.iter_error).context("error loading from persistence backend"))
     }
 }
 
index 762af8ff11eb56ba6161fcf8a597ddbbf430baf5..73a8fc539698bfc1e67b5384e7d0d0791972a810 100644 (file)
@@ -18,7 +18,7 @@
 //! let first_device = devices.remove(0)?;
 //! let custom_signer = HWISigner::from_device(&first_device, Network::Testnet.into())?;
 //!
-//! # let mut wallet = Wallet::new_no_persist(
+//! # let mut wallet = Wallet::new(
 //! #     "",
 //! #     "",
 //! #     Network::Testnet,
diff --git a/crates/persist/Cargo.toml b/crates/persist/Cargo.toml
deleted file mode 100644 (file)
index 446eea4..0000000
+++ /dev/null
@@ -1,22 +0,0 @@
-[package]
-name = "bdk_persist"
-homepage = "https://bitcoindevkit.org"
-version = "0.3.0"
-repository = "https://github.com/bitcoindevkit/bdk"
-documentation = "https://docs.rs/bdk_persist"
-description = "Types that define data persistence of a BDK wallet"
-keywords = ["bitcoin", "wallet", "persistence", "database"]
-readme = "README.md"
-license = "MIT OR Apache-2.0"
-authors = ["Bitcoin Dev Kit Developers"]
-edition = "2021"
-rust-version = "1.63"
-
-[dependencies]
-anyhow = { version = "1", default-features = false }
-bdk_chain = { path = "../chain", version = "0.15.0", default-features = false }
-
-[features]
-default = ["bdk_chain/std", "miniscript"]
-serde = ["bdk_chain/serde"]
-miniscript = ["bdk_chain/miniscript"]
diff --git a/crates/persist/README.md b/crates/persist/README.md
deleted file mode 100644 (file)
index c823548..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-# BDK Persist
-
-This crate is home to the [`PersistBackend`] trait which defines the behavior of a database to perform the task of persisting changes made to BDK data structures. 
-
-The [`Persist`] type provides a convenient wrapper around a [`PersistBackend`] that allows staging changes before committing them.
diff --git a/crates/persist/src/changeset.rs b/crates/persist/src/changeset.rs
deleted file mode 100644 (file)
index b796b07..0000000
+++ /dev/null
@@ -1,73 +0,0 @@
-#![cfg(feature = "miniscript")]
-
-use bdk_chain::{bitcoin::Network, indexed_tx_graph, keychain, local_chain, Anchor, Append};
-
-/// Changes from a combination of [`bdk_chain`] structures.
-#[derive(Debug, Clone, PartialEq)]
-#[cfg_attr(
-    feature = "serde",
-    derive(bdk_chain::serde::Deserialize, bdk_chain::serde::Serialize),
-    serde(
-        crate = "bdk_chain::serde",
-        bound(
-            deserialize = "A: Ord + bdk_chain::serde::Deserialize<'de>, K: Ord + bdk_chain::serde::Deserialize<'de>",
-            serialize = "A: Ord + bdk_chain::serde::Serialize, K: Ord + bdk_chain::serde::Serialize",
-        ),
-    )
-)]
-pub struct CombinedChangeSet<K, A> {
-    /// Changes to the [`LocalChain`](local_chain::LocalChain).
-    pub chain: local_chain::ChangeSet,
-    /// Changes to [`IndexedTxGraph`](indexed_tx_graph::IndexedTxGraph).
-    pub indexed_tx_graph: indexed_tx_graph::ChangeSet<A, keychain::ChangeSet<K>>,
-    /// Stores the network type of the transaction data.
-    pub network: Option<Network>,
-}
-
-impl<K, A> Default for CombinedChangeSet<K, A> {
-    fn default() -> Self {
-        Self {
-            chain: Default::default(),
-            indexed_tx_graph: Default::default(),
-            network: None,
-        }
-    }
-}
-
-impl<K: Ord, A: Anchor> Append for CombinedChangeSet<K, A> {
-    fn append(&mut self, other: Self) {
-        Append::append(&mut self.chain, other.chain);
-        Append::append(&mut self.indexed_tx_graph, other.indexed_tx_graph);
-        if other.network.is_some() {
-            debug_assert!(
-                self.network.is_none() || self.network == other.network,
-                "network type must either be just introduced or remain the same"
-            );
-            self.network = other.network;
-        }
-    }
-
-    fn is_empty(&self) -> bool {
-        self.chain.is_empty() && self.indexed_tx_graph.is_empty() && self.network.is_none()
-    }
-}
-
-impl<K, A> From<local_chain::ChangeSet> for CombinedChangeSet<K, A> {
-    fn from(chain: local_chain::ChangeSet) -> Self {
-        Self {
-            chain,
-            ..Default::default()
-        }
-    }
-}
-
-impl<K, A> From<indexed_tx_graph::ChangeSet<A, keychain::ChangeSet<K>>>
-    for CombinedChangeSet<K, A>
-{
-    fn from(indexed_tx_graph: indexed_tx_graph::ChangeSet<A, keychain::ChangeSet<K>>) -> Self {
-        Self {
-            indexed_tx_graph,
-            ..Default::default()
-        }
-    }
-}
diff --git a/crates/persist/src/lib.rs b/crates/persist/src/lib.rs
deleted file mode 100644 (file)
index e3824e1..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-#![doc = include_str!("../README.md")]
-#![no_std]
-#![warn(missing_docs)]
-
-mod changeset;
-mod persist;
-pub use changeset::*;
-pub use persist::*;
diff --git a/crates/persist/src/persist.rs b/crates/persist/src/persist.rs
deleted file mode 100644 (file)
index 5d9df3b..0000000
+++ /dev/null
@@ -1,106 +0,0 @@
-extern crate alloc;
-use alloc::boxed::Box;
-use bdk_chain::Append;
-use core::fmt;
-
-/// `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.
-pub struct Persist<C> {
-    backend: Box<dyn PersistBackend<C> + Send + Sync>,
-    stage: 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
-    C: Default + Append,
-{
-    /// Create a new [`Persist`] from [`PersistBackend`].
-    pub fn new(backend: impl PersistBackend<C> + Send + Sync + 'static) -> Self {
-        let backend = Box::new(backend);
-        Self {
-            backend,
-            stage: Default::default(),
-        }
-    }
-
-    /// Stage a `changeset` to be committed later with [`commit`].
-    ///
-    /// [`commit`]: Self::commit
-    pub fn stage(&mut self, changeset: C) {
-        self.stage.append(changeset)
-    }
-
-    /// Get the changes that have not been committed yet.
-    pub fn staged(&self) -> &C {
-        &self.stage
-    }
-
-    /// Commit the staged changes to the underlying persistence backend.
-    ///
-    /// Changes that are committed (if any) are returned.
-    ///
-    /// # Error
-    ///
-    /// Returns a backend-defined error if this fails.
-    pub fn commit(&mut self) -> anyhow::Result<Option<C>> {
-        if self.stage.is_empty() {
-            return Ok(None);
-        }
-        self.backend
-            .write_changes(&self.stage)
-            // if written successfully, take and return `self.stage`
-            .map(|_| Some(core::mem::take(&mut self.stage)))
-    }
-
-    /// Stages a new changeset and commits it (along with any other previously staged changes) to
-    /// the persistence backend
-    ///
-    /// Convenience method for calling [`stage`] and then [`commit`].
-    ///
-    /// [`stage`]: Self::stage
-    /// [`commit`]: Self::commit
-    pub fn stage_and_commit(&mut self, changeset: C) -> anyhow::Result<Option<C>> {
-        self.stage(changeset);
-        self.commit()
-    }
-}
-
-/// A persistence backend for [`Persist`].
-///
-/// `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> {
-    /// 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
-    /// it inserts the actual changes into a more structured database. All it needs to guarantee is
-    /// that [`load_from_persistence`] restores a keychain tracker to what it should be if all
-    /// changesets had been applied sequentially.
-    ///
-    /// [`load_from_persistence`]: Self::load_from_persistence
-    fn write_changes(&mut self, changeset: &C) -> anyhow::Result<()>;
-
-    /// Return the aggregate changeset `C` from persistence.
-    fn load_from_persistence(&mut self) -> anyhow::Result<Option<C>>;
-}
-
-impl<C> PersistBackend<C> for () {
-    fn write_changes(&mut self, _changeset: &C) -> anyhow::Result<()> {
-        Ok(())
-    }
-
-    fn load_from_persistence(&mut self) -> anyhow::Result<Option<C>> {
-        Ok(None)
-    }
-}
index fbbfc374f6f7df04f22b90b042a27fa0de039f09..238c818f73ce7fb7067fdb1cd6316fd8c85bf938 100644 (file)
@@ -5,15 +5,13 @@ edition = "2021"
 license = "MIT OR Apache-2.0"
 repository = "https://github.com/bitcoindevkit/bdk"
 documentation = "https://docs.rs/bdk_sqlite"
-description = "A simple SQLite based implementation of Persist for Bitcoin Dev Kit."
+description = "A simple SQLite based implementation of PersistBackend for Bitcoin Dev Kit."
 keywords = ["bitcoin", "persist", "persistence", "bdk", "sqlite"]
 authors = ["Bitcoin Dev Kit Developers"]
 readme = "README.md"
 
 [dependencies]
-anyhow = { version = "1", default-features = false }
 bdk_chain = { path = "../chain", version = "0.15.0", features = ["serde", "miniscript"] }
-bdk_persist = { path = "../persist", version = "0.3.0", features = ["serde"] }
 rusqlite = { version = "0.31.0", features = ["bundled"] }
 serde = { version = "1", features = ["derive"] }
 serde_json = "1"
\ No newline at end of file
index 4136656f9ca970bac36e330a47dcce61654a2e08..26f4650bc32132ffa20e2cc42cf8ff9155dfe0c5 100644 (file)
@@ -1,8 +1,8 @@
 # BDK SQLite
 
-This is a simple [SQLite] relational database schema backed implementation of [`PersistBackend`](bdk_persist::PersistBackend).
+This is a simple [SQLite] relational database schema backed implementation of `PersistBackend`.
 
-The main structure is `Store` which persists [`bdk_persist`] `CombinedChangeSet` data into a SQLite database file.
+The main structure is `Store` which persists `CombinedChangeSet` data into a SQLite database file.
 
-[`bdk_persist`]:https://docs.rs/bdk_persist/latest/bdk_persist/
+<!-- [`PersistBackend`]: bdk_chain::persist::PersistBackend -->
 [SQLite]: https://www.sqlite.org/index.html
index fe4572dbd70d2824a6e872da77372cb87bf73daf..5bead7bc998472072b84c61aacdb1c4153c531f4 100644 (file)
@@ -12,10 +12,10 @@ use std::str::FromStr;
 use std::sync::{Arc, Mutex};
 
 use crate::Error;
+use bdk_chain::persist::{CombinedChangeSet, PersistBackend};
 use bdk_chain::{
     indexed_tx_graph, keychain, local_chain, tx_graph, Anchor, Append, DescriptorExt, DescriptorId,
 };
-use bdk_persist::CombinedChangeSet;
 
 /// Persists data in to a relational schema based [SQLite] database file.
 ///
@@ -57,21 +57,23 @@ where
     }
 }
 
-impl<K, A, C> bdk_persist::PersistBackend<C> for Store<K, A>
+impl<K, A> PersistBackend<CombinedChangeSet<K, A>> for Store<K, A>
 where
     K: Ord + for<'de> Deserialize<'de> + Serialize + Send,
     A: Anchor + for<'de> Deserialize<'de> + Serialize + Send,
-    C: Clone + From<CombinedChangeSet<K, A>> + Into<CombinedChangeSet<K, A>>,
 {
-    fn write_changes(&mut self, changeset: &C) -> anyhow::Result<()> {
-        self.write(&changeset.clone().into())
-            .map_err(|e| anyhow::anyhow!(e).context("unable to write changes to sqlite database"))
+    type WriteError = Error;
+    type LoadError = Error;
+
+    fn write_changes(
+        &mut self,
+        changeset: &CombinedChangeSet<K, A>,
+    ) -> Result<(), Self::WriteError> {
+        self.write(changeset)
     }
 
-    fn load_from_persistence(&mut self) -> anyhow::Result<Option<C>> {
+    fn load_changes(&mut self) -> Result<Option<CombinedChangeSet<K, A>>, Self::LoadError> {
         self.read()
-            .map(|c| c.map(Into::into))
-            .map_err(|e| anyhow::anyhow!(e).context("unable to read changes from sqlite database"))
     }
 }
 
@@ -561,11 +563,11 @@ mod test {
     use bdk_chain::bitcoin::Network::Testnet;
     use bdk_chain::bitcoin::{secp256k1, BlockHash, OutPoint};
     use bdk_chain::miniscript::Descriptor;
+    use bdk_chain::persist::{CombinedChangeSet, PersistBackend};
     use bdk_chain::{
         indexed_tx_graph, keychain, tx_graph, BlockId, ConfirmationHeightAnchor,
         ConfirmationTimeHeightAnchor, DescriptorExt,
     };
-    use bdk_persist::PersistBackend;
     use std::str::FromStr;
     use std::sync::Arc;
 
@@ -576,8 +578,7 @@ mod test {
     }
 
     #[test]
-    fn insert_and_load_aggregate_changesets_with_confirmation_time_height_anchor(
-    ) -> anyhow::Result<()> {
+    fn insert_and_load_aggregate_changesets_with_confirmation_time_height_anchor() {
         let (test_changesets, agg_test_changesets) =
             create_test_changesets(&|height, time, hash| ConfirmationTimeHeightAnchor {
                 confirmation_height: height,
@@ -593,15 +594,13 @@ mod test {
             store.write_changes(changeset).expect("write changeset");
         });
 
-        let agg_changeset = store.load_from_persistence().expect("aggregated changeset");
+        let agg_changeset = store.load_changes().expect("aggregated changeset");
 
         assert_eq!(agg_changeset, Some(agg_test_changesets));
-        Ok(())
     }
 
     #[test]
-    fn insert_and_load_aggregate_changesets_with_confirmation_height_anchor() -> anyhow::Result<()>
-    {
+    fn insert_and_load_aggregate_changesets_with_confirmation_height_anchor() {
         let (test_changesets, agg_test_changesets) =
             create_test_changesets(&|height, _time, hash| ConfirmationHeightAnchor {
                 confirmation_height: height,
@@ -616,14 +615,13 @@ mod test {
             store.write_changes(changeset).expect("write changeset");
         });
 
-        let agg_changeset = store.load_from_persistence().expect("aggregated changeset");
+        let agg_changeset = store.load_changes().expect("aggregated changeset");
 
         assert_eq!(agg_changeset, Some(agg_test_changesets));
-        Ok(())
     }
 
     #[test]
-    fn insert_and_load_aggregate_changesets_with_blockid_anchor() -> anyhow::Result<()> {
+    fn insert_and_load_aggregate_changesets_with_blockid_anchor() {
         let (test_changesets, agg_test_changesets) =
             create_test_changesets(&|height, _time, hash| BlockId { height, hash });
 
@@ -634,10 +632,9 @@ mod test {
             store.write_changes(changeset).expect("write changeset");
         });
 
-        let agg_changeset = store.load_from_persistence().expect("aggregated changeset");
+        let agg_changeset = store.load_changes().expect("aggregated changeset");
 
         assert_eq!(agg_changeset, Some(agg_test_changesets));
-        Ok(())
     }
 
     fn create_test_changesets<A: Anchor + Copy>(
index 8b72af47c336d768d6f35aac4dd9d521204d5ef3..d7f3290ace59b566263bbe0df12b3af434b1a942 100644 (file)
@@ -13,14 +13,12 @@ edition = "2021"
 rust-version = "1.63"
 
 [dependencies]
-anyhow = { version = "1", default-features = false }
 rand = "^0.8"
 miniscript = { version = "12.0.0", features = ["serde"], default-features = false }
 bitcoin = { version = "0.32.0", features = ["serde", "base64", "rand-std"], default-features = false }
 serde = { version = "^1.0", features = ["derive"] }
 serde_json = { version = "^1.0" }
 bdk_chain = { path = "../chain", version = "0.15.0", features = ["miniscript", "serde"], default-features = false }
-bdk_persist = { path = "../persist", version = "0.3.0", features = ["miniscript", "serde"], default-features = false }
 
 # Optional dependencies
 bip39 = { version = "2.0", optional = true }
index 73ff4b58dae8619f7e7117fc1ba7779fb0fe7fd6..9aba478214e715d329078ad29fe658b622526867 100644 (file)
@@ -57,11 +57,12 @@ that the `Wallet` can use to update its view of the chain.
 
 ## Persistence
 
-To persist the `Wallet` on disk, it must be constructed with a [`PersistBackend`] implementation.
+To persist `Wallet` state data on disk use an implementation of the [`PersistBackend`] trait.
 
 **Implementations**
 
 * [`bdk_file_store`]: A simple flat-file implementation of [`PersistBackend`].
+* [`bdk_sqlite`]: A simple sqlite implementation of [`PersistBackend`].
 
 **Example**
 
@@ -71,15 +72,16 @@ use bdk_wallet::{bitcoin::Network, wallet::{ChangeSet, Wallet}};
 
 fn main() {
     // Create a new file `Store`.
-    let db = bdk_file_store::Store::<ChangeSet>::open_or_create_new(b"magic_bytes", "path/to/my_wallet.db").expect("create store");
+    let mut db = bdk_file_store::Store::<ChangeSet>::open_or_create_new(b"magic_bytes", "path/to/my_wallet.db").expect("create store");
 
     let descriptor = "wpkh(tprv8ZgxMBicQKsPdcAqYBpzAFwU5yxBUo88ggoBqu1qPcHUfSbKK1sKMLmC7EAk438btHQrSdu3jGGQa6PA71nvH5nkDexhLteJqkM4dQmWF9g/84'/1'/0'/0/*)";
     let change_descriptor = "wpkh(tprv8ZgxMBicQKsPdcAqYBpzAFwU5yxBUo88ggoBqu1qPcHUfSbKK1sKMLmC7EAk438btHQrSdu3jGGQa6PA71nvH5nkDexhLteJqkM4dQmWF9g/84'/1'/0'/1/*)";
-    let mut wallet = Wallet::new_or_load(descriptor, change_descriptor, db, Network::Testnet).expect("create or load wallet");
+    let changeset = db.load_changes().expect("changeset loaded");
+    let mut wallet = Wallet::new_or_load(descriptor, change_descriptor, changeset, Network::Testnet).expect("create or load wallet");
 
     // Insert a single `TxOut` at `OutPoint` into the wallet.
     let _ = wallet.insert_txout(outpoint, txout);
-    wallet.commit().expect("must write to database");
+    wallet.commit_to(&mut db).expect("must commit changes to database");
 }
 ```
 
@@ -115,7 +117,7 @@ fn main() {
 <!-- use bdk_wallet::bitcoin::Network; -->
 
 <!-- fn main() -> Result<(), bdk_wallet::Error> { -->
-<!--     let wallet = Wallet::new_no_persist( -->
+<!--     let wallet = Wallet::new( -->
 <!--         "wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)", -->
 <!--         Some("wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/1/*)"), -->
 <!--         Network::Testnet, -->
@@ -144,7 +146,7 @@ fn main() {
 
 <!-- fn main() -> Result<(), bdk_wallet::Error> { -->
 <!--     let blockchain = ElectrumBlockchain::from(Client::new("ssl://electrum.blockstream.info:60002")?); -->
-<!--     let wallet = Wallet::new_no_persist( -->
+<!--     let wallet = Wallet::new( -->
 <!--         "wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)", -->
 <!--         Some("wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/1/*)"), -->
 <!--         Network::Testnet, -->
@@ -180,7 +182,7 @@ fn main() {
 <!-- use bdk_wallet::bitcoin::Network; -->
 
 <!-- fn main() -> Result<(), bdk_wallet::Error> { -->
-<!--     let wallet = Wallet::new_no_persist( -->
+<!--     let wallet = Wallet::new( -->
 <!--         "wpkh([c258d2e4/84h/1h/0h]tprv8griRPhA7342zfRyB6CqeKF8CJDXYu5pgnj1cjL1u2ngKcJha5jjTRimG82ABzJQ4MQe71CV54xfn25BbhCNfEGGJZnxvCDQCd6JkbvxW6h/0/*)", -->
 <!--         Some("wpkh([c258d2e4/84h/1h/0h]tprv8griRPhA7342zfRyB6CqeKF8CJDXYu5pgnj1cjL1u2ngKcJha5jjTRimG82ABzJQ4MQe71CV54xfn25BbhCNfEGGJZnxvCDQCd6JkbvxW6h/1/*)"), -->
 <!--         Network::Testnet, -->
@@ -220,9 +222,10 @@ license, shall be dual licensed as above, without any additional terms or
 conditions.
 
 [`Wallet`]: https://docs.rs/bdk_wallet/latest/bdk_wallet/wallet/struct.Wallet.html
-[`PersistBackend`]: https://docs.rs/bdk_chain/latest/bdk_chain/trait.PersistBackend.html
+[`PersistBackend`]: https://docs.rs/bdk_chain/latest/bdk_chain/persist/trait.PersistBackend.html
 [`bdk_chain`]: https://docs.rs/bdk_chain/latest
 [`bdk_file_store`]: https://docs.rs/bdk_file_store/latest
+[`bdk_sqlite`]: https://docs.rs/bdk_sqlite/latest
 [`bdk_electrum`]: https://docs.rs/bdk_electrum/latest
 [`bdk_esplora`]: https://docs.rs/bdk_esplora/latest
 [`bdk_bitcoind_rpc`]: https://docs.rs/bdk_bitcoind_rpc/latest
index a11899f4751d7fe769fb5b975a84c0d73f10c648..13b905ad9c388f66466e9d57eb8fc4e0eb96d797 100644 (file)
@@ -77,11 +77,11 @@ fn main() -> Result<(), Box<dyn Error>> {
     );
 
     // Create a new wallet from descriptors
-    let mut wallet = Wallet::new_no_persist(&descriptor, &internal_descriptor, Network::Regtest)?;
+    let mut wallet = Wallet::new(&descriptor, &internal_descriptor, Network::Regtest)?;
 
     println!(
         "First derived address from the descriptor: \n{}",
-        wallet.next_unused_address(KeychainKind::External)?,
+        wallet.next_unused_address(KeychainKind::External),
     );
 
     // BDK also has it's own `Policy` structure to represent the spending condition in a more
index 9eb375169b4c9ab3b7008106f78b6c9fac10e6c7..3ee346d31d0be00e9900e3d7809a5993008fc45a 100644 (file)
@@ -81,12 +81,11 @@ impl<T: DescriptorTemplate> IntoWalletDescriptor for T {
 ///     bitcoin::PrivateKey::from_wif("cTc4vURSzdx6QE6KVynWGomDbLaA75dNALMNyfjh3p8DRRar84Um")?;
 /// let key_internal =
 ///     bitcoin::PrivateKey::from_wif("cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW")?;
-/// let mut wallet =
-///     Wallet::new_no_persist(P2Pkh(key_external), P2Pkh(key_internal), Network::Testnet)?;
+/// let mut wallet = Wallet::new(P2Pkh(key_external), P2Pkh(key_internal), Network::Testnet)?;
 ///
 /// assert_eq!(
 ///     wallet
-///         .next_unused_address(KeychainKind::External)?
+///         .next_unused_address(KeychainKind::External)
 ///         .to_string(),
 ///     "mwJ8hxFYW19JLuc65RCTaP4v1rzVU8cVMT"
 /// );
@@ -114,7 +113,7 @@ impl<K: IntoDescriptorKey<Legacy>> DescriptorTemplate for P2Pkh<K> {
 ///     bitcoin::PrivateKey::from_wif("cTc4vURSzdx6QE6KVynWGomDbLaA75dNALMNyfjh3p8DRRar84Um")?;
 /// let key_internal =
 ///     bitcoin::PrivateKey::from_wif("cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW")?;
-/// let mut wallet = Wallet::new_no_persist(
+/// let mut wallet = Wallet::new(
 ///     P2Wpkh_P2Sh(key_external),
 ///     P2Wpkh_P2Sh(key_internal),
 ///     Network::Testnet,
@@ -122,7 +121,7 @@ impl<K: IntoDescriptorKey<Legacy>> DescriptorTemplate for P2Pkh<K> {
 ///
 /// assert_eq!(
 ///     wallet
-///         .next_unused_address(KeychainKind::External)?
+///         .next_unused_address(KeychainKind::External)
 ///         .to_string(),
 ///     "2NB4ox5VDRw1ecUv6SnT3VQHPXveYztRqk5"
 /// );
@@ -151,12 +150,11 @@ impl<K: IntoDescriptorKey<Segwitv0>> DescriptorTemplate for P2Wpkh_P2Sh<K> {
 ///     bitcoin::PrivateKey::from_wif("cTc4vURSzdx6QE6KVynWGomDbLaA75dNALMNyfjh3p8DRRar84Um")?;
 /// let key_internal =
 ///     bitcoin::PrivateKey::from_wif("cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW")?;
-/// let mut wallet =
-///     Wallet::new_no_persist(P2Wpkh(key_external), P2Wpkh(key_internal), Network::Testnet)?;
+/// let mut wallet = Wallet::new(P2Wpkh(key_external), P2Wpkh(key_internal), Network::Testnet)?;
 ///
 /// assert_eq!(
 ///     wallet
-///         .next_unused_address(KeychainKind::External)?
+///         .next_unused_address(KeychainKind::External)
 ///         .to_string(),
 ///     "tb1q4525hmgw265tl3drrl8jjta7ayffu6jf68ltjd"
 /// );
@@ -184,12 +182,11 @@ impl<K: IntoDescriptorKey<Segwitv0>> DescriptorTemplate for P2Wpkh<K> {
 ///     bitcoin::PrivateKey::from_wif("cTc4vURSzdx6QE6KVynWGomDbLaA75dNALMNyfjh3p8DRRar84Um")?;
 /// let key_internal =
 ///     bitcoin::PrivateKey::from_wif("cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW")?;
-/// let mut wallet =
-///     Wallet::new_no_persist(P2TR(key_external), P2TR(key_internal), Network::Testnet)?;
+/// let mut wallet = Wallet::new(P2TR(key_external), P2TR(key_internal), Network::Testnet)?;
 ///
 /// assert_eq!(
 ///     wallet
-///         .next_unused_address(KeychainKind::External)?
+///         .next_unused_address(KeychainKind::External)
 ///         .to_string(),
 ///     "tb1pvjf9t34fznr53u5tqhejz4nr69luzkhlvsdsdfq9pglutrpve2xq7hps46"
 /// );
@@ -218,13 +215,13 @@ impl<K: IntoDescriptorKey<Tap>> DescriptorTemplate for P2TR<K> {
 /// use bdk_wallet::template::Bip44;
 ///
 /// let key = bitcoin::bip32::Xpriv::from_str("tprv8ZgxMBicQKsPeZRHk4rTG6orPS2CRNFX3njhUXx5vj9qGog5ZMH4uGReDWN5kCkY3jmWEtWause41CDvBRXD1shKknAMKxT99o9qUTRVC6m")?;
-/// let mut wallet = Wallet::new_no_persist(
+/// let mut wallet = Wallet::new(
 ///     Bip44(key.clone(), KeychainKind::External),
 ///     Bip44(key, KeychainKind::Internal),
 ///     Network::Testnet,
 /// )?;
 ///
-/// assert_eq!(wallet.next_unused_address(KeychainKind::External)?.to_string(), "mmogjc7HJEZkrLqyQYqJmxUqFaC7i4uf89");
+/// assert_eq!(wallet.next_unused_address(KeychainKind::External).to_string(), "mmogjc7HJEZkrLqyQYqJmxUqFaC7i4uf89");
 /// assert_eq!(wallet.public_descriptor(KeychainKind::External).to_string(), "pkh([c55b303f/44'/1'/0']tpubDCuorCpzvYS2LCD75BR46KHE8GdDeg1wsAgNZeNr6DaB5gQK1o14uErKwKLuFmeemkQ6N2m3rNgvctdJLyr7nwu2yia7413Hhg8WWE44cgT/0/*)#5wrnv0xt");
 /// # Ok::<_, Box<dyn std::error::Error>>(())
 /// ```
@@ -255,13 +252,13 @@ impl<K: DerivableKey<Legacy>> DescriptorTemplate for Bip44<K> {
 ///
 /// let key = bitcoin::bip32::Xpub::from_str("tpubDDDzQ31JkZB7VxUr9bjvBivDdqoFLrDPyLWtLapArAi51ftfmCb2DPxwLQzX65iNcXz1DGaVvyvo6JQ6rTU73r2gqdEo8uov9QKRb7nKCSU")?;
 /// let fingerprint = bitcoin::bip32::Fingerprint::from_str("c55b303f")?;
-/// let mut wallet = Wallet::new_no_persist(
+/// let mut wallet = Wallet::new(
 ///     Bip44Public(key.clone(), fingerprint, KeychainKind::External),
 ///     Bip44Public(key, fingerprint, KeychainKind::Internal),
 ///     Network::Testnet,
 /// )?;
 ///
-/// assert_eq!(wallet.next_unused_address(KeychainKind::External)?.to_string(), "miNG7dJTzJqNbFS19svRdTCisC65dsubtR");
+/// assert_eq!(wallet.next_unused_address(KeychainKind::External).to_string(), "miNG7dJTzJqNbFS19svRdTCisC65dsubtR");
 /// assert_eq!(wallet.public_descriptor(KeychainKind::External).to_string(), "pkh([c55b303f/44'/1'/0']tpubDDDzQ31JkZB7VxUr9bjvBivDdqoFLrDPyLWtLapArAi51ftfmCb2DPxwLQzX65iNcXz1DGaVvyvo6JQ6rTU73r2gqdEo8uov9QKRb7nKCSU/0/*)#cfhumdqz");
 /// # Ok::<_, Box<dyn std::error::Error>>(())
 /// ```
@@ -291,13 +288,13 @@ impl<K: DerivableKey<Legacy>> DescriptorTemplate for Bip44Public<K> {
 /// use bdk_wallet::template::Bip49;
 ///
 /// let key = bitcoin::bip32::Xpriv::from_str("tprv8ZgxMBicQKsPeZRHk4rTG6orPS2CRNFX3njhUXx5vj9qGog5ZMH4uGReDWN5kCkY3jmWEtWause41CDvBRXD1shKknAMKxT99o9qUTRVC6m")?;
-/// let mut wallet = Wallet::new_no_persist(
+/// let mut wallet = Wallet::new(
 ///     Bip49(key.clone(), KeychainKind::External),
 ///     Bip49(key, KeychainKind::Internal),
 ///     Network::Testnet,
 /// )?;
 ///
-/// assert_eq!(wallet.next_unused_address(KeychainKind::External)?.to_string(), "2N4zkWAoGdUv4NXhSsU8DvS5MB36T8nKHEB");
+/// assert_eq!(wallet.next_unused_address(KeychainKind::External).to_string(), "2N4zkWAoGdUv4NXhSsU8DvS5MB36T8nKHEB");
 /// assert_eq!(wallet.public_descriptor(KeychainKind::External).to_string(), "sh(wpkh([c55b303f/49'/1'/0']tpubDDYr4kdnZgjjShzYNjZUZXUUtpXaofdkMaipyS8ThEh45qFmhT4hKYways7UXmg6V7het1QiFo9kf4kYUXyDvV4rHEyvSpys9pjCB3pukxi/0/*))#s9vxlc8e");
 /// # Ok::<_, Box<dyn std::error::Error>>(())
 /// ```
@@ -328,13 +325,13 @@ impl<K: DerivableKey<Segwitv0>> DescriptorTemplate for Bip49<K> {
 ///
 /// let key = bitcoin::bip32::Xpub::from_str("tpubDC49r947KGK52X5rBWS4BLs5m9SRY3pYHnvRrm7HcybZ3BfdEsGFyzCMzayi1u58eT82ZeyFZwH7DD6Q83E3fM9CpfMtmnTygnLfP59jL9L")?;
 /// let fingerprint = bitcoin::bip32::Fingerprint::from_str("c55b303f")?;
-/// let mut wallet = Wallet::new_no_persist(
+/// let mut wallet = Wallet::new(
 ///     Bip49Public(key.clone(), fingerprint, KeychainKind::External),
 ///     Bip49Public(key, fingerprint, KeychainKind::Internal),
 ///     Network::Testnet,
 /// )?;
 ///
-/// assert_eq!(wallet.next_unused_address(KeychainKind::External)?.to_string(), "2N3K4xbVAHoiTQSwxkZjWDfKoNC27pLkYnt");
+/// assert_eq!(wallet.next_unused_address(KeychainKind::External).to_string(), "2N3K4xbVAHoiTQSwxkZjWDfKoNC27pLkYnt");
 /// assert_eq!(wallet.public_descriptor(KeychainKind::External).to_string(), "sh(wpkh([c55b303f/49'/1'/0']tpubDC49r947KGK52X5rBWS4BLs5m9SRY3pYHnvRrm7HcybZ3BfdEsGFyzCMzayi1u58eT82ZeyFZwH7DD6Q83E3fM9CpfMtmnTygnLfP59jL9L/0/*))#3tka9g0q");
 /// # Ok::<_, Box<dyn std::error::Error>>(())
 /// ```
@@ -364,13 +361,13 @@ impl<K: DerivableKey<Segwitv0>> DescriptorTemplate for Bip49Public<K> {
 /// use bdk_wallet::template::Bip84;
 ///
 /// let key = bitcoin::bip32::Xpriv::from_str("tprv8ZgxMBicQKsPeZRHk4rTG6orPS2CRNFX3njhUXx5vj9qGog5ZMH4uGReDWN5kCkY3jmWEtWause41CDvBRXD1shKknAMKxT99o9qUTRVC6m")?;
-/// let mut wallet = Wallet::new_no_persist(
+/// let mut wallet = Wallet::new(
 ///     Bip84(key.clone(), KeychainKind::External),
 ///     Bip84(key, KeychainKind::Internal),
 ///     Network::Testnet,
 /// )?;
 ///
-/// assert_eq!(wallet.next_unused_address(KeychainKind::External)?.to_string(), "tb1qhl85z42h7r4su5u37rvvw0gk8j2t3n9y7zsg4n");
+/// assert_eq!(wallet.next_unused_address(KeychainKind::External).to_string(), "tb1qhl85z42h7r4su5u37rvvw0gk8j2t3n9y7zsg4n");
 /// assert_eq!(wallet.public_descriptor(KeychainKind::External).to_string(), "wpkh([c55b303f/84'/1'/0']tpubDDc5mum24DekpNw92t6fHGp8Gr2JjF9J7i4TZBtN6Vp8xpAULG5CFaKsfugWa5imhrQQUZKXe261asP5koDHo5bs3qNTmf3U3o4v9SaB8gg/0/*)#6kfecsmr");
 /// # Ok::<_, Box<dyn std::error::Error>>(())
 /// ```
@@ -401,13 +398,13 @@ impl<K: DerivableKey<Segwitv0>> DescriptorTemplate for Bip84<K> {
 ///
 /// let key = bitcoin::bip32::Xpub::from_str("tpubDC2Qwo2TFsaNC4ju8nrUJ9mqVT3eSgdmy1yPqhgkjwmke3PRXutNGRYAUo6RCHTcVQaDR3ohNU9we59brGHuEKPvH1ags2nevW5opEE9Z5Q")?;
 /// let fingerprint = bitcoin::bip32::Fingerprint::from_str("c55b303f")?;
-/// let mut wallet = Wallet::new_no_persist(
+/// let mut wallet = Wallet::new(
 ///     Bip84Public(key.clone(), fingerprint, KeychainKind::External),
 ///     Bip84Public(key, fingerprint, KeychainKind::Internal),
 ///     Network::Testnet,
 /// )?;
 ///
-/// assert_eq!(wallet.next_unused_address(KeychainKind::External)?.to_string(), "tb1qedg9fdlf8cnnqfd5mks6uz5w4kgpk2pr6y4qc7");
+/// assert_eq!(wallet.next_unused_address(KeychainKind::External).to_string(), "tb1qedg9fdlf8cnnqfd5mks6uz5w4kgpk2pr6y4qc7");
 /// assert_eq!(wallet.public_descriptor(KeychainKind::External).to_string(), "wpkh([c55b303f/84'/1'/0']tpubDC2Qwo2TFsaNC4ju8nrUJ9mqVT3eSgdmy1yPqhgkjwmke3PRXutNGRYAUo6RCHTcVQaDR3ohNU9we59brGHuEKPvH1ags2nevW5opEE9Z5Q/0/*)#dhu402yv");
 /// # Ok::<_, Box<dyn std::error::Error>>(())
 /// ```
@@ -437,13 +434,13 @@ impl<K: DerivableKey<Segwitv0>> DescriptorTemplate for Bip84Public<K> {
 /// use bdk_wallet::template::Bip86;
 ///
 /// let key = bitcoin::bip32::Xpriv::from_str("tprv8ZgxMBicQKsPeZRHk4rTG6orPS2CRNFX3njhUXx5vj9qGog5ZMH4uGReDWN5kCkY3jmWEtWause41CDvBRXD1shKknAMKxT99o9qUTRVC6m")?;
-/// let mut wallet = Wallet::new_no_persist(
+/// let mut wallet = Wallet::new(
 ///     Bip86(key.clone(), KeychainKind::External),
 ///     Bip86(key, KeychainKind::Internal),
 ///     Network::Testnet,
 /// )?;
 ///
-/// assert_eq!(wallet.next_unused_address(KeychainKind::External)?.to_string(), "tb1p5unlj09djx8xsjwe97269kqtxqpwpu2epeskgqjfk4lnf69v4tnqpp35qu");
+/// assert_eq!(wallet.next_unused_address(KeychainKind::External).to_string(), "tb1p5unlj09djx8xsjwe97269kqtxqpwpu2epeskgqjfk4lnf69v4tnqpp35qu");
 /// assert_eq!(wallet.public_descriptor(KeychainKind::External).to_string(), "tr([c55b303f/86'/1'/0']tpubDCiHofpEs47kx358bPdJmTZHmCDqQ8qw32upCSxHrSEdeeBs2T5Mq6QMB2ukeMqhNBiyhosBvJErteVhfURPGXPv3qLJPw5MVpHUewsbP2m/0/*)#dkgvr5hm");
 /// # Ok::<_, Box<dyn std::error::Error>>(())
 /// ```
@@ -474,13 +471,13 @@ impl<K: DerivableKey<Tap>> DescriptorTemplate for Bip86<K> {
 ///
 /// let key = bitcoin::bip32::Xpub::from_str("tpubDC2Qwo2TFsaNC4ju8nrUJ9mqVT3eSgdmy1yPqhgkjwmke3PRXutNGRYAUo6RCHTcVQaDR3ohNU9we59brGHuEKPvH1ags2nevW5opEE9Z5Q")?;
 /// let fingerprint = bitcoin::bip32::Fingerprint::from_str("c55b303f")?;
-/// let mut wallet = Wallet::new_no_persist(
+/// let mut wallet = Wallet::new(
 ///     Bip86Public(key.clone(), fingerprint, KeychainKind::External),
 ///     Bip86Public(key, fingerprint, KeychainKind::Internal),
 ///     Network::Testnet,
 /// )?;
 ///
-/// assert_eq!(wallet.next_unused_address(KeychainKind::External)?.to_string(), "tb1pwjp9f2k5n0xq73ecuu0c5njvgqr3vkh7yaylmpqvsuuaafymh0msvcmh37");
+/// assert_eq!(wallet.next_unused_address(KeychainKind::External).to_string(), "tb1pwjp9f2k5n0xq73ecuu0c5njvgqr3vkh7yaylmpqvsuuaafymh0msvcmh37");
 /// assert_eq!(wallet.public_descriptor(KeychainKind::External).to_string(), "tr([c55b303f/86'/1'/0']tpubDC2Qwo2TFsaNC4ju8nrUJ9mqVT3eSgdmy1yPqhgkjwmke3PRXutNGRYAUo6RCHTcVQaDR3ohNU9we59brGHuEKPvH1ags2nevW5opEE9Z5Q/0/*)#2p65srku");
 /// # Ok::<_, Box<dyn std::error::Error>>(())
 /// ```
index 7bffb91d959ece8165c82cedb8b3a7dfc56be48f..819a15edbaa52de5746b7b09739abdca8fe5ef2e 100644 (file)
@@ -28,7 +28,6 @@
 //! # use bitcoin::*;
 //! # use bdk_wallet::wallet::{self, ChangeSet, coin_selection::*, coin_selection};
 //! # use bdk_wallet::wallet::error::CreateTxError;
-//! # use bdk_persist::PersistBackend;
 //! # use bdk_wallet::*;
 //! # use bdk_wallet::wallet::coin_selection::decide_change;
 //! # use anyhow::Error;
index 7b19a2ec5b0a5d950183d37ac499351cd0866a7d..b6c9375a438f16f094f4fde5f1515ca1c9728f5a 100644 (file)
@@ -50,8 +50,6 @@ impl std::error::Error for MiniscriptPsbtError {}
 pub enum CreateTxError {
     /// There was a problem with the descriptors passed in
     Descriptor(DescriptorError),
-    /// 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`]
@@ -114,13 +112,6 @@ 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 load wallet data from or write wallet data to persistence backend: {}",
-                    e
-                )
-            }
             Self::Policy(e) => e.fmt(f),
             CreateTxError::SpendingPolicyRequired(keychain_kind) => {
                 write!(f, "Spending policy required: {:?}", keychain_kind)
index 41f463a461a5677b7ff3900f656d9fddea155742..0aa3ec887ce64bfc290b779d3406e8bbcc40bfd7 100644 (file)
@@ -29,7 +29,7 @@
 //! }"#;
 //!
 //! let import = FullyNodedExport::from_str(import)?;
-//! let wallet = Wallet::new_no_persist(
+//! let wallet = Wallet::new(
 //!     &import.descriptor(),
 //!     &import.change_descriptor().expect("change descriptor"),
 //!     Network::Testnet,
@@ -42,7 +42,7 @@
 //! # use bitcoin::*;
 //! # use bdk_wallet::wallet::export::*;
 //! # use bdk_wallet::*;
-//! let wallet = Wallet::new_no_persist(
+//! let wallet = Wallet::new(
 //!     "wpkh([c258d2e4/84h/1h/0h]tpubDD3ynpHgJQW8VvWRzQ5WFDCrs4jqVFGHB3vLC3r49XHJSqP8bHKdK4AriuUKLccK68zfzowx7YhmDN8SiSkgCDENUFx9qVw65YyqM78vyVe/0/*)",
 //!     "wpkh([c258d2e4/84h/1h/0h]tpubDD3ynpHgJQW8VvWRzQ5WFDCrs4jqVFGHB3vLC3r49XHJSqP8bHKdK4AriuUKLccK68zfzowx7YhmDN8SiSkgCDENUFx9qVw65YyqM78vyVe/1/*)",
 //!     Network::Testnet,
@@ -222,7 +222,7 @@ mod test {
     use crate::wallet::Wallet;
 
     fn get_test_wallet(descriptor: &str, change_descriptor: &str, network: Network) -> Wallet {
-        let mut wallet = Wallet::new_no_persist(descriptor, change_descriptor, network).unwrap();
+        let mut wallet = Wallet::new(descriptor, change_descriptor, network).unwrap();
         let transaction = Transaction {
             input: vec![],
             output: vec![],
index b9bff5ad50ed277e5e049a693d72a66f0e0a6497..b79cd5cf66e7aea1d8c3503245cc813543300251 100644 (file)
@@ -30,7 +30,7 @@
 //! let first_device = devices.remove(0)?;
 //! let custom_signer = HWISigner::from_device(&first_device, Network::Testnet.into())?;
 //!
-//! # let mut wallet = Wallet::new_no_persist(
+//! # let mut wallet = Wallet::new(
 //! #     "",
 //! #     None,
 //! #     Network::Testnet,
index 33244ca6134c57d4d91ad8fd63f5819c7ea77950..7917be156d934d65eb49fddd0a9ec322bdc2f3ef 100644 (file)
@@ -31,7 +31,6 @@ use bdk_chain::{
     Append, BlockId, ChainPosition, ConfirmationTime, ConfirmationTimeHeightAnchor, FullTxOut,
     Indexed, IndexedTxGraph,
 };
-use bdk_persist::{Persist, PersistBackend};
 use bitcoin::secp256k1::{All, Secp256k1};
 use bitcoin::sighash::{EcdsaSighashType, TapSighashType};
 use bitcoin::{
@@ -41,6 +40,7 @@ use bitcoin::{
 use bitcoin::{consensus::encode::serialize, transaction, BlockHash, Psbt};
 use bitcoin::{constants::genesis_block, Amount};
 use core::fmt;
+use core::mem;
 use core::ops::Deref;
 use descriptor::error::Error as DescriptorError;
 use miniscript::psbt::{PsbtExt, PsbtInputExt, PsbtInputSatisfier};
@@ -85,6 +85,11 @@ const COINBASE_MATURITY: u32 = 100;
 /// 1. output *descriptors* from which it can derive addresses.
 /// 2. [`signer`]s that can contribute signatures to addresses instantiated from the descriptors.
 ///
+/// The user is responsible for loading and writing wallet changes using an implementation of
+/// [`PersistBackend`]. See individual functions and example for instructions on when [`Wallet`]
+/// state needs to be persisted.
+///
+/// [`PersistBackend`]: bdk_chain::persist::PersistBackend
 /// [`signer`]: crate::signer
 #[derive(Debug)]
 pub struct Wallet {
@@ -92,7 +97,7 @@ pub struct Wallet {
     change_signers: Arc<SignersContainer>,
     chain: LocalChain,
     indexed_graph: IndexedTxGraph<ConfirmationTimeHeightAnchor, KeychainTxOutIndex<KeychainKind>>,
-    persist: Persist<ChangeSet>,
+    stage: ChangeSet,
     network: Network,
     secp: SecpCtx,
 }
@@ -136,7 +141,8 @@ impl From<SyncResult> for Update {
 }
 
 /// The changes made to a wallet by applying an [`Update`].
-pub type ChangeSet = bdk_persist::CombinedChangeSet<KeychainKind, ConfirmationTimeHeightAnchor>;
+pub type ChangeSet =
+    bdk_chain::persist::CombinedChangeSet<KeychainKind, ConfirmationTimeHeightAnchor>;
 
 /// A derived address and the index it was found at.
 /// For convenience this automatically derefs to `Address`
@@ -164,36 +170,6 @@ impl fmt::Display for AddressInfo {
     }
 }
 
-impl Wallet {
-    /// Creates a wallet that does not persist data.
-    pub fn new_no_persist<E: IntoWalletDescriptor>(
-        descriptor: E,
-        change_descriptor: E,
-        network: Network,
-    ) -> Result<Self, DescriptorError> {
-        Self::new(descriptor, change_descriptor, (), network).map_err(|e| match e {
-            NewError::NonEmptyDatabase => unreachable!("mock-database cannot have data"),
-            NewError::Descriptor(e) => e,
-            NewError::Persist(_) => unreachable!("mock-write must always succeed"),
-        })
-    }
-
-    /// Creates a wallet that does not persist data, with a custom genesis hash.
-    pub fn new_no_persist_with_genesis_hash<E: IntoWalletDescriptor>(
-        descriptor: E,
-        change_descriptor: E,
-        network: Network,
-        genesis_hash: BlockHash,
-    ) -> Result<Self, crate::descriptor::DescriptorError> {
-        Self::new_with_genesis_hash(descriptor, change_descriptor, (), network, genesis_hash)
-            .map_err(|e| match e {
-                NewError::NonEmptyDatabase => unreachable!("mock-database cannot have data"),
-                NewError::Descriptor(e) => e,
-                NewError::Persist(_) => unreachable!("mock-write must always succeed"),
-            })
-    }
-}
-
 /// The error type when constructing a fresh [`Wallet`].
 ///
 /// Methods [`new`] and [`new_with_genesis_hash`] may return this error.
@@ -202,23 +178,14 @@ impl Wallet {
 /// [`new_with_genesis_hash`]: Wallet::new_with_genesis_hash
 #[derive(Debug)]
 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.
-    Persist(anyhow::Error),
 }
 
 impl fmt::Display for NewError {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         match self {
-            NewError::NonEmptyDatabase => write!(
-                f,
-                "database already has data - use `load` or `new_or_load` methods instead"
-            ),
             NewError::Descriptor(e) => e.fmt(f),
-            NewError::Persist(e) => e.fmt(f),
         }
     }
 }
@@ -226,19 +193,15 @@ impl fmt::Display for NewError {
 #[cfg(feature = "std")]
 impl std::error::Error for NewError {}
 
-/// The error type when loading a [`Wallet`] from persistence.
+/// The error type when loading a [`Wallet`] from a [`ChangeSet`].
 ///
-/// Method [`load`] may return this error.
+/// Method [`load_from_changeset`] may return this error.
 ///
-/// [`load`]: Wallet::load
+/// [`load_from_changeset`]: Wallet::load_from_changeset
 #[derive(Debug)]
 pub enum LoadError {
     /// There was a problem with the passed-in descriptor(s).
     Descriptor(crate::descriptor::DescriptorError),
-    /// Loading data from the persistence backend failed.
-    Persist(anyhow::Error),
-    /// Wallet not initialized, persistence backend is empty.
-    NotInitialized,
     /// Data loaded from persistence is missing network type.
     MissingNetwork,
     /// Data loaded from persistence is missing genesis hash.
@@ -251,10 +214,6 @@ impl fmt::Display for LoadError {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         match self {
             LoadError::Descriptor(e) => e.fmt(f),
-            LoadError::Persist(e) => e.fmt(f),
-            LoadError::NotInitialized => {
-                write!(f, "wallet is not initialized, persistence backend is empty")
-            }
             LoadError::MissingNetwork => write!(f, "loaded data is missing network type"),
             LoadError::MissingGenesis => write!(f, "loaded data is missing genesis hash"),
             LoadError::MissingDescriptor(k) => {
@@ -277,10 +236,6 @@ impl std::error::Error for LoadError {}
 pub enum NewOrLoadError {
     /// There is a problem with the passed-in descriptor.
     Descriptor(crate::descriptor::DescriptorError),
-    /// 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.
     LoadedGenesisDoesNotMatch {
         /// The expected genesis block hash.
@@ -308,14 +263,6 @@ impl fmt::Display for NewOrLoadError {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         match self {
             NewOrLoadError::Descriptor(e) => e.fmt(f),
-            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")
-            }
             NewOrLoadError::LoadedGenesisDoesNotMatch { expected, got } => {
                 write!(f, "loaded genesis hash is not {}, got {:?}", expected, got)
             }
@@ -403,11 +350,10 @@ impl Wallet {
     pub fn new<E: IntoWalletDescriptor>(
         descriptor: E,
         change_descriptor: E,
-        db: impl PersistBackend<ChangeSet> + Send + Sync + 'static,
         network: Network,
     ) -> Result<Self, NewError> {
         let genesis_hash = genesis_block(network).block_hash();
-        Self::new_with_genesis_hash(descriptor, change_descriptor, db, network, genesis_hash)
+        Self::new_with_genesis_hash(descriptor, change_descriptor, network, genesis_hash)
     }
 
     /// Initialize an empty [`Wallet`] with a custom genesis hash.
@@ -417,15 +363,9 @@ impl Wallet {
     pub fn new_with_genesis_hash<E: IntoWalletDescriptor>(
         descriptor: E,
         change_descriptor: E,
-        mut db: impl PersistBackend<ChangeSet> + Send + Sync + 'static,
         network: Network,
         genesis_hash: BlockHash,
     ) -> Result<Self, NewError> {
-        if let Ok(changeset) = db.load_from_persistence() {
-            if changeset.is_some() {
-                return Err(NewError::NonEmptyDatabase);
-            }
-        }
         let secp = Secp256k1::new();
         let (chain, chain_changeset) = LocalChain::from_genesis_hash(genesis_hash);
         let mut index = KeychainTxOutIndex::<KeychainKind>::default();
@@ -436,13 +376,11 @@ impl Wallet {
 
         let indexed_graph = IndexedTxGraph::new(index);
 
-        let mut persist = Persist::new(db);
-        persist.stage(ChangeSet {
+        let staged = ChangeSet {
             chain: chain_changeset,
             indexed_tx_graph: indexed_graph.initial_changeset(),
             network: Some(network),
-        });
-        persist.commit().map_err(NewError::Persist)?;
+        };
 
         Ok(Wallet {
             signers,
@@ -450,12 +388,24 @@ impl Wallet {
             network,
             chain,
             indexed_graph,
-            persist,
+            stage: staged,
             secp,
         })
     }
 
-    /// Load [`Wallet`] from the given persistence backend.
+    /// Stage a ['ChangeSet'] to be persisted later.
+    ///
+    /// [`commit`]: Self::commit
+    fn stage(&mut self, changeset: ChangeSet) {
+        self.stage.append(changeset)
+    }
+
+    /// Take the staged [`ChangeSet`] to be persisted now.
+    pub fn take_staged(&mut self) -> ChangeSet {
+        mem::take(&mut self.stage)
+    }
+
+    /// Load [`Wallet`] from the given previously persisted [`ChangeSet`].
     ///
     /// Note that the descriptor secret keys are not persisted to the db; this means that after
     /// calling this method the [`Wallet`] **won't** know the secret keys, and as such, won't be
@@ -473,10 +423,11 @@ impl Wallet {
     /// # use bdk_sqlite::{Store, rusqlite::Connection};
     /// #
     /// # fn main() -> Result<(), anyhow::Error> {
+    /// # use bdk_chain::persist::PersistBackend;
     /// # let temp_dir = tempfile::tempdir().expect("must create tempdir");
     /// # let file_path = temp_dir.path().join("store.db");
     /// # let conn = Connection::open(file_path).expect("must open connection");
-    /// # let db = Store::new(conn).expect("must create db");
+    /// # let mut db = Store::new(conn).expect("must create db");
     /// let secp = Secp256k1::new();
     ///
     /// let (external_descriptor, external_keymap) = Descriptor::parse_descriptor(&secp, "wpkh(tprv8ZgxMBicQKsPdy6LMhUtFHAgpocR8GC6QmwMSFpZs7h6Eziw3SpThFfczTDh5rW2krkqffa11UpX3XkeTTB2FvzZKWXqPY54Y6Rq4AQ5R8L/84'/1'/0'/0/*)").unwrap();
@@ -484,8 +435,8 @@ impl Wallet {
     ///
     /// let external_signer_container = SignersContainer::build(external_keymap, &external_descriptor, &secp);
     /// let internal_signer_container = SignersContainer::build(internal_keymap, &internal_descriptor, &secp);
-    ///
-    /// let mut wallet = Wallet::load(db)?;
+    /// let changeset = db.load_changes()?.expect("there must be an existing changeset");
+    /// let mut wallet = Wallet::load_from_changeset(changeset)?;
     ///
     /// external_signer_container.signers().into_iter()
     ///     .for_each(|s| wallet.add_signer(KeychainKind::External, SignerOrdering::default(), s.clone()));
@@ -497,20 +448,7 @@ impl Wallet {
     ///
     /// Alternatively, you can call [`Wallet::new_or_load`], which will add the private keys of the
     /// passed-in descriptors to the [`Wallet`].
-    pub fn load(
-        mut db: impl PersistBackend<ChangeSet> + Send + Sync + 'static,
-    ) -> Result<Self, LoadError> {
-        let changeset = db
-            .load_from_persistence()
-            .map_err(LoadError::Persist)?
-            .ok_or(LoadError::NotInitialized)?;
-        Self::load_from_changeset(db, changeset)
-    }
-
-    fn load_from_changeset(
-        db: impl PersistBackend<ChangeSet> + Send + Sync + 'static,
-        changeset: ChangeSet,
-    ) -> Result<Self, LoadError> {
+    pub fn load_from_changeset(changeset: ChangeSet) -> Result<Self, LoadError> {
         let secp = Secp256k1::new();
         let network = changeset.network.ok_or(LoadError::MissingNetwork)?;
         let chain =
@@ -538,157 +476,156 @@ impl Wallet {
         let mut indexed_graph = IndexedTxGraph::new(index);
         indexed_graph.apply_changeset(changeset.indexed_tx_graph);
 
-        let persist = Persist::new(db);
+        let stage = ChangeSet::default();
 
         Ok(Wallet {
             signers,
             change_signers,
             chain,
             indexed_graph,
-            persist,
+            stage,
             network,
             secp,
         })
     }
 
-    /// Either loads [`Wallet`] from persistence, or initializes it if it does not exist.
+    /// Either loads [`Wallet`] from the given [`ChangeSet`] or initializes it if one does not exist.
+    ///
+    /// This method will fail if the loaded [`ChangeSet`] has different parameters to those provided.
+    ///
+    /// ```rust,no_run
+    /// # use bdk_chain::persist::PersistBackend;
+    /// # use bdk_wallet::Wallet;
+    /// # use bdk_sqlite::{Store, rusqlite::Connection};
+    /// # use bitcoin::Network::Testnet;
+    /// # let conn = Connection::open_in_memory().expect("must open connection");
+    /// let mut db = Store::new(conn).expect("must create db");
+    /// let changeset = db.load_changes()?;
+    ///
+    /// let external_descriptor = "wpkh(tprv8ZgxMBicQKsPdy6LMhUtFHAgpocR8GC6QmwMSFpZs7h6Eziw3SpThFfczTDh5rW2krkqffa11UpX3XkeTTB2FvzZKWXqPY54Y6Rq4AQ5R8L/84'/1'/0'/0/*)";
+    /// let internal_descriptor = "wpkh(tprv8ZgxMBicQKsPdy6LMhUtFHAgpocR8GC6QmwMSFpZs7h6Eziw3SpThFfczTDh5rW2krkqffa11UpX3XkeTTB2FvzZKWXqPY54Y6Rq4AQ5R8L/84'/1'/0'/1/*)";
     ///
-    /// This method will fail if the loaded [`Wallet`] has different parameters to those provided.
+    /// let mut wallet = Wallet::new_or_load(external_descriptor, internal_descriptor, changeset, Testnet)?;
+    /// # Ok::<(), anyhow::Error>(())
+    /// ```
     pub fn new_or_load<E: IntoWalletDescriptor>(
         descriptor: E,
         change_descriptor: E,
-        db: impl PersistBackend<ChangeSet> + Send + Sync + 'static,
+        changeset: Option<ChangeSet>,
         network: Network,
     ) -> Result<Self, NewOrLoadError> {
         let genesis_hash = genesis_block(network).block_hash();
         Self::new_or_load_with_genesis_hash(
             descriptor,
             change_descriptor,
-            db,
+            changeset,
             network,
             genesis_hash,
         )
     }
 
-    /// Either loads [`Wallet`] from persistence, or initializes it if it does not exist, using the
+    /// Either loads [`Wallet`] from a [`ChangeSet`] or initializes it if one does not exist, using the
     /// provided descriptor, change descriptor, network, and custom genesis hash.
     ///
-    /// This method will fail if the loaded [`Wallet`] has different parameters to those provided.
+    /// This method will fail if the loaded [`ChangeSet`] has different parameters to those provided.
     /// This is like [`Wallet::new_or_load`] with an additional `genesis_hash` parameter. This is
     /// useful for syncing from alternative networks.
     pub fn new_or_load_with_genesis_hash<E: IntoWalletDescriptor>(
         descriptor: E,
         change_descriptor: E,
-        mut db: impl PersistBackend<ChangeSet> + Send + Sync + 'static,
+        changeset: Option<ChangeSet>,
         network: Network,
         genesis_hash: BlockHash,
     ) -> Result<Self, NewOrLoadError> {
-        let changeset = db
-            .load_from_persistence()
-            .map_err(NewOrLoadError::Persist)?;
-        match changeset {
-            Some(changeset) => {
-                let mut wallet = Self::load_from_changeset(db, changeset).map_err(|e| match e {
-                    LoadError::Descriptor(e) => NewOrLoadError::Descriptor(e),
-                    LoadError::Persist(e) => NewOrLoadError::Persist(e),
-                    LoadError::NotInitialized => NewOrLoadError::NotInitialized,
-                    LoadError::MissingNetwork => NewOrLoadError::LoadedNetworkDoesNotMatch {
-                        expected: network,
+        if let Some(changeset) = changeset {
+            let mut wallet = Self::load_from_changeset(changeset).map_err(|e| match e {
+                LoadError::Descriptor(e) => NewOrLoadError::Descriptor(e),
+                LoadError::MissingNetwork => NewOrLoadError::LoadedNetworkDoesNotMatch {
+                    expected: network,
+                    got: None,
+                },
+                LoadError::MissingGenesis => NewOrLoadError::LoadedGenesisDoesNotMatch {
+                    expected: genesis_hash,
+                    got: None,
+                },
+                LoadError::MissingDescriptor(keychain) => {
+                    NewOrLoadError::LoadedDescriptorDoesNotMatch {
                         got: None,
-                    },
-                    LoadError::MissingGenesis => NewOrLoadError::LoadedGenesisDoesNotMatch {
-                        expected: genesis_hash,
-                        got: None,
-                    },
-                    LoadError::MissingDescriptor(keychain) => {
-                        NewOrLoadError::LoadedDescriptorDoesNotMatch {
-                            got: None,
-                            keychain,
-                        }
+                        keychain,
                     }
-                })?;
-                if wallet.network != network {
-                    return Err(NewOrLoadError::LoadedNetworkDoesNotMatch {
-                        expected: network,
-                        got: Some(wallet.network),
-                    });
-                }
-                if wallet.chain.genesis_hash() != genesis_hash {
-                    return Err(NewOrLoadError::LoadedGenesisDoesNotMatch {
-                        expected: genesis_hash,
-                        got: Some(wallet.chain.genesis_hash()),
-                    });
-                }
-
-                let (expected_descriptor, expected_descriptor_keymap) = descriptor
-                    .into_wallet_descriptor(&wallet.secp, network)
-                    .map_err(NewOrLoadError::Descriptor)?;
-                let wallet_descriptor = wallet.public_descriptor(KeychainKind::External);
-                if wallet_descriptor != &expected_descriptor {
-                    return Err(NewOrLoadError::LoadedDescriptorDoesNotMatch {
-                        got: Some(wallet_descriptor.clone()),
-                        keychain: KeychainKind::External,
-                    });
-                }
-                // if expected descriptor has private keys add them as new signers
-                if !expected_descriptor_keymap.is_empty() {
-                    let signer_container = SignersContainer::build(
-                        expected_descriptor_keymap,
-                        &expected_descriptor,
-                        &wallet.secp,
-                    );
-                    signer_container.signers().into_iter().for_each(|signer| {
-                        wallet.add_signer(
-                            KeychainKind::External,
-                            SignerOrdering::default(),
-                            signer.clone(),
-                        )
-                    });
                 }
+            })?;
+            if wallet.network != network {
+                return Err(NewOrLoadError::LoadedNetworkDoesNotMatch {
+                    expected: network,
+                    got: Some(wallet.network),
+                });
+            }
+            if wallet.chain.genesis_hash() != genesis_hash {
+                return Err(NewOrLoadError::LoadedGenesisDoesNotMatch {
+                    expected: genesis_hash,
+                    got: Some(wallet.chain.genesis_hash()),
+                });
+            }
 
-                let (expected_change_descriptor, expected_change_descriptor_keymap) =
-                    change_descriptor
-                        .into_wallet_descriptor(&wallet.secp, network)
-                        .map_err(NewOrLoadError::Descriptor)?;
-                let wallet_change_descriptor = wallet.public_descriptor(KeychainKind::Internal);
-                if wallet_change_descriptor != &expected_change_descriptor {
-                    return Err(NewOrLoadError::LoadedDescriptorDoesNotMatch {
-                        got: Some(wallet_change_descriptor.clone()),
-                        keychain: KeychainKind::Internal,
-                    });
-                }
-                // if expected change descriptor has private keys add them as new signers
-                if !expected_change_descriptor_keymap.is_empty() {
-                    let signer_container = SignersContainer::build(
-                        expected_change_descriptor_keymap,
-                        &expected_change_descriptor,
-                        &wallet.secp,
-                    );
-                    signer_container.signers().into_iter().for_each(|signer| {
-                        wallet.add_signer(
-                            KeychainKind::Internal,
-                            SignerOrdering::default(),
-                            signer.clone(),
-                        )
-                    });
-                }
+            let (expected_descriptor, expected_descriptor_keymap) = descriptor
+                .into_wallet_descriptor(&wallet.secp, network)
+                .map_err(NewOrLoadError::Descriptor)?;
+            let wallet_descriptor = wallet.public_descriptor(KeychainKind::External);
+            if wallet_descriptor != &expected_descriptor {
+                return Err(NewOrLoadError::LoadedDescriptorDoesNotMatch {
+                    got: Some(wallet_descriptor.clone()),
+                    keychain: KeychainKind::External,
+                });
+            }
+            // if expected descriptor has private keys add them as new signers
+            if !expected_descriptor_keymap.is_empty() {
+                let signer_container = SignersContainer::build(
+                    expected_descriptor_keymap,
+                    &expected_descriptor,
+                    &wallet.secp,
+                );
+                signer_container.signers().into_iter().for_each(|signer| {
+                    wallet.add_signer(
+                        KeychainKind::External,
+                        SignerOrdering::default(),
+                        signer.clone(),
+                    )
+                });
+            }
 
-                Ok(wallet)
+            let (expected_change_descriptor, expected_change_descriptor_keymap) = change_descriptor
+                .into_wallet_descriptor(&wallet.secp, network)
+                .map_err(NewOrLoadError::Descriptor)?;
+            let wallet_change_descriptor = wallet.public_descriptor(KeychainKind::Internal);
+            if wallet_change_descriptor != &expected_change_descriptor {
+                return Err(NewOrLoadError::LoadedDescriptorDoesNotMatch {
+                    got: Some(wallet_change_descriptor.clone()),
+                    keychain: KeychainKind::Internal,
+                });
             }
-            None => Self::new_with_genesis_hash(
-                descriptor,
-                change_descriptor,
-                db,
-                network,
-                genesis_hash,
-            )
-            .map_err(|e| match e {
-                NewError::NonEmptyDatabase => {
-                    unreachable!("database is already checked to have no data")
-                }
-                NewError::Descriptor(e) => NewOrLoadError::Descriptor(e),
-                NewError::Persist(e) => NewOrLoadError::Persist(e),
-            }),
+            // if expected change descriptor has private keys add them as new signers
+            if !expected_change_descriptor_keymap.is_empty() {
+                let signer_container = SignersContainer::build(
+                    expected_change_descriptor_keymap,
+                    &expected_change_descriptor,
+                    &wallet.secp,
+                );
+                signer_container.signers().into_iter().for_each(|signer| {
+                    wallet.add_signer(
+                        KeychainKind::Internal,
+                        SignerOrdering::default(),
+                        signer.clone(),
+                    )
+                });
+            }
+
+            Ok(wallet)
+        } else {
+            Self::new_with_genesis_hash(descriptor, change_descriptor, network, genesis_hash)
+                .map_err(|e| match e {
+                    NewError::Descriptor(e) => NewOrLoadError::Descriptor(e),
+                })
         }
     }
 
@@ -732,30 +669,46 @@ impl Wallet {
 
     /// Attempt to reveal the next address of the given `keychain`.
     ///
-    /// This will increment the internal derivation index. If the keychain's descriptor doesn't
+    /// This will increment the keychain's derivation index. If the keychain's descriptor doesn't
     /// contain a wildcard or every address is already revealed up to the maximum derivation
     /// index defined in [BIP32](https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki),
-    /// then returns the last revealed address.
+    /// then the last revealed address will be returned.
     ///
-    /// # Errors
+    /// **WARNING**: To avoid address reuse you must persist the changes resulting from one or more
+    /// calls to this method before closing the wallet. For example:
     ///
-    /// If writing to persistent storage fails.
-    pub fn reveal_next_address(&mut self, keychain: KeychainKind) -> anyhow::Result<AddressInfo> {
-        let ((index, spk), index_changeset) = self
-            .indexed_graph
-            .index
+    /// ```rust,no_run
+    /// # use bdk_chain::persist::PersistBackend;
+    /// # use bdk_wallet::wallet::{Wallet, ChangeSet};
+    /// # use bdk_wallet::KeychainKind;
+    /// # use bdk_sqlite::{Store, rusqlite::Connection};
+    /// # let conn = Connection::open_in_memory().expect("must open connection");
+    /// # let mut db = Store::new(conn).expect("must create store");
+    /// # let changeset = ChangeSet::default();
+    /// # let mut wallet = Wallet::load_from_changeset(changeset).expect("load wallet");
+    /// let next_address = wallet.reveal_next_address(KeychainKind::External);
+    /// db.write_changes(&wallet.take_staged())?;
+    ///
+    /// // Now it's safe to show the user their next address!
+    /// println!("Next address: {}", next_address.address);
+    /// # Ok::<(), anyhow::Error>(())
+    /// ```
+    pub fn reveal_next_address(&mut self, keychain: KeychainKind) -> AddressInfo {
+        let index = &mut self.indexed_graph.index;
+        let stage = &mut self.stage;
+
+        let ((index, spk), index_changeset) = index
             .reveal_next_spk(&keychain)
             .expect("keychain must exist");
 
-        self.persist
-            .stage_and_commit(indexed_tx_graph::ChangeSet::from(index_changeset).into())?;
+        stage.append(indexed_tx_graph::ChangeSet::from(index_changeset).into());
 
-        Ok(AddressInfo {
+        AddressInfo {
             index,
             address: Address::from_script(spk.as_script(), self.network)
                 .expect("must have address form"),
             keychain,
-        })
+        }
     }
 
     /// Reveal addresses up to and including the target `index` and return an iterator
@@ -765,28 +718,26 @@ impl Wallet {
     /// possible index. If all addresses up to the given `index` are already revealed, then
     /// no new addresses are returned.
     ///
-    /// # Errors
-    ///
-    /// If writing to persistent storage fails.
+    /// **WARNING**: To avoid address reuse you must persist the changes resulting from one or more
+    /// calls to this method before closing the wallet. See [`Wallet::reveal_next_address`].
     pub fn reveal_addresses_to(
         &mut self,
         keychain: KeychainKind,
         index: u32,
-    ) -> anyhow::Result<impl Iterator<Item = AddressInfo> + '_> {
+    ) -> impl Iterator<Item = AddressInfo> + '_ {
         let (spks, index_changeset) = self
             .indexed_graph
             .index
             .reveal_to_target(&keychain, index)
             .expect("keychain must exist");
 
-        self.persist
-            .stage_and_commit(indexed_tx_graph::ChangeSet::from(index_changeset).into())?;
+        self.stage(indexed_tx_graph::ChangeSet::from(index_changeset).into());
 
-        Ok(spks.into_iter().map(move |(index, spk)| AddressInfo {
+        spks.into_iter().map(move |(index, spk)| AddressInfo {
             index,
             address: Address::from_script(&spk, self.network).expect("must have address form"),
             keychain,
-        }))
+        })
     }
 
     /// Get the next unused address for the given `keychain`, i.e. the address with the lowest
@@ -795,25 +746,24 @@ impl Wallet {
     /// This will attempt to derive and reveal a new address if no newly revealed addresses
     /// are available. See also [`reveal_next_address`](Self::reveal_next_address).
     ///
-    /// # Errors
-    ///
-    /// If writing to persistent storage fails.
-    pub fn next_unused_address(&mut self, keychain: KeychainKind) -> anyhow::Result<AddressInfo> {
-        let ((index, spk), index_changeset) = self
-            .indexed_graph
-            .index
+    /// **WARNING**: To avoid address reuse you must persist the changes resulting from one or more
+    /// calls to this method before closing the wallet. See [`Wallet::reveal_next_address`].
+    pub fn next_unused_address(&mut self, keychain: KeychainKind) -> AddressInfo {
+        let index = &mut self.indexed_graph.index;
+
+        let ((index, spk), index_changeset) = index
             .next_unused_spk(&keychain)
             .expect("keychain must exist");
 
-        self.persist
-            .stage_and_commit(indexed_tx_graph::ChangeSet::from(index_changeset).into())?;
+        self.stage
+            .append(indexed_tx_graph::ChangeSet::from(index_changeset).into());
 
-        Ok(AddressInfo {
+        AddressInfo {
             index,
             address: Address::from_script(spk.as_script(), self.network)
                 .expect("must have address form"),
             keychain,
-        })
+        }
     }
 
     /// Marks an address used of the given `keychain` at `index`.
@@ -952,19 +902,20 @@ impl Wallet {
     /// or [`calculate_fee_rate`] on a given transaction. Outputs inserted with this method will
     /// not be returned in [`list_unspent`] or [`list_output`].
     ///
-    /// Any inserted `TxOut`s are not persisted until [`commit`] is called.
-    ///
-    /// **WARNING:** This should only be used to add `TxOut`s that the wallet does not own. Only
+    /// **WARNINGS:** This should only be used to add `TxOut`s that the wallet does not own. Only
     /// insert `TxOut`s that you trust the values for!
     ///
+    /// You must persist the changes resulting from one or more calls to this method if you need
+    /// the inserted `TxOut` data to be reloaded after closing the wallet.
+    /// See [`Wallet::reveal_next_address`].
+    ///
     /// [`calculate_fee`]: Self::calculate_fee
     /// [`calculate_fee_rate`]: Self::calculate_fee_rate
     /// [`list_unspent`]: Self::list_unspent
     /// [`list_output`]: Self::list_output
-    /// [`commit`]: Self::commit
     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));
+        self.stage(ChangeSet::from(additions));
     }
 
     /// Calculates the fee of a given transaction. Returns [`Amount::ZERO`] if `tx` is a coinbase transaction.
@@ -1118,11 +1069,14 @@ impl Wallet {
     }
 
     /// Add a new checkpoint to the wallet's internal view of the chain.
-    /// This stages but does not [`commit`] the change.
     ///
     /// Returns whether anything changed with the insertion (e.g. `false` if checkpoint was already
     /// there).
     ///
+    /// **WARNING**: You must persist the changes resulting from one or more calls to this method
+    /// if you need the inserted checkpoint data to be reloaded after closing the wallet.
+    /// See [`Wallet::reveal_next_address`].
+    ///
     /// [`commit`]: Self::commit
     pub fn insert_checkpoint(
         &mut self,
@@ -1130,12 +1084,12 @@ impl Wallet {
     ) -> Result<bool, local_chain::AlterCheckPointError> {
         let changeset = self.chain.insert_block(block_id)?;
         let changed = !changeset.is_empty();
-        self.persist.stage(changeset.into());
+        self.stage(changeset.into());
         Ok(changed)
     }
 
-    /// Add a transaction to the wallet's internal view of the chain. This stages but does not
-    /// [`commit`] the change.
+    /// Add a transaction to the wallet's internal view of the chain. This stages the change,
+    /// you must persist it later.
     ///
     /// Returns whether anything changed with the transaction insertion (e.g. `false` if the
     /// transaction was already inserted at the same position).
@@ -1144,10 +1098,13 @@ impl Wallet {
     /// Therefore you should use [`insert_checkpoint`] to insert new checkpoints before manually
     /// inserting new transactions.
     ///
-    /// **WARNING:** If `position` is confirmed, we anchor the `tx` to a the lowest checkpoint that
+    /// **WARNING**: If `position` is confirmed, we anchor the `tx` to the lowest checkpoint that
     /// is >= the `position`'s height. The caller is responsible for ensuring the `tx` exists in our
     /// local view of the best chain's history.
     ///
+    /// You must persist the changes resulting from one or more calls to this method if you need
+    /// the inserted tx to be reloaded after closing the wallet.
+    ///
     /// [`commit`]: Self::commit
     /// [`latest_checkpoint`]: Self::latest_checkpoint
     /// [`insert_checkpoint`]: Self::insert_checkpoint
@@ -1189,7 +1146,7 @@ impl Wallet {
         }
 
         let changed = !changeset.is_empty();
-        self.persist.stage(changeset);
+        self.stage(changeset);
         Ok(changed)
     }
 
@@ -1240,7 +1197,7 @@ impl Wallet {
     /// # use bdk_wallet::bitcoin::Network;
     /// let descriptor = "wpkh(tprv8ZgxMBicQKsPe73PBRSmNbTfbcsZnwWhz5eVmhHpi31HW29Z7mc9B4cWGRQzopNUzZUT391DeDJxL2PefNunWyLgqCKRMDkU1s2s8bAfoSk/84'/1'/0'/0/*)";
     /// let change_descriptor = "wpkh(tprv8ZgxMBicQKsPe73PBRSmNbTfbcsZnwWhz5eVmhHpi31HW29Z7mc9B4cWGRQzopNUzZUT391DeDJxL2PefNunWyLgqCKRMDkU1s2s8bAfoSk/84'/1'/0'/1/*)";
-    /// let wallet = Wallet::new_no_persist(descriptor, change_descriptor, Network::Testnet)?;
+    /// let wallet = Wallet::new(descriptor, change_descriptor, Network::Testnet)?;
     /// for secret_key in wallet.get_signers(KeychainKind::External).signers().iter().filter_map(|s| s.descriptor_secret_key()) {
     ///     // secret_key: tprv8ZgxMBicQKsPe73PBRSmNbTfbcsZnwWhz5eVmhHpi31HW29Z7mc9B4cWGRQzopNUzZUT391DeDJxL2PefNunWyLgqCKRMDkU1s2s8bAfoSk/84'/0'/0'/0/*
     ///     println!("secret_key: {}", secret_key);
@@ -1267,7 +1224,6 @@ impl Wallet {
     /// # use bdk_wallet::*;
     /// # use bdk_wallet::wallet::ChangeSet;
     /// # use bdk_wallet::wallet::error::CreateTxError;
-    /// # use bdk_persist::PersistBackend;
     /// # use anyhow::Error;
     /// # let descriptor = "wpkh(tpubD6NzVbkrYhZ4Xferm7Pz4VnjdcDPFyjVu5K4iZXQ4pVN8Cks4pHVowTBXBKRhX64pkRyJZJN5xAKj4UDNnLPb5p2sSKXhewoYx5GbTdUFWq/*)";
     /// # let mut wallet = doctest_wallet!();
@@ -1514,11 +1470,9 @@ impl Wallet {
                     .next_unused_spk(&change_keychain)
                     .expect("keychain must exist");
                 self.indexed_graph.index.mark_used(change_keychain, index);
-                self.persist
-                    .stage(ChangeSet::from(indexed_tx_graph::ChangeSet::from(
-                        index_changeset,
-                    )));
-                self.persist.commit().map_err(CreateTxError::Persist)?;
+                self.stage(ChangeSet::from(indexed_tx_graph::ChangeSet::from(
+                    index_changeset,
+                )));
                 spk
             }
         };
@@ -1617,7 +1571,6 @@ impl Wallet {
     /// # use bdk_wallet::*;
     /// # use bdk_wallet::wallet::ChangeSet;
     /// # use bdk_wallet::wallet::error::CreateTxError;
-    /// # use bdk_persist::PersistBackend;
     /// # use anyhow::Error;
     /// # let descriptor = "wpkh(tpubD6NzVbkrYhZ4Xferm7Pz4VnjdcDPFyjVu5K4iZXQ4pVN8Cks4pHVowTBXBKRhX64pkRyJZJN5xAKj4UDNnLPb5p2sSKXhewoYx5GbTdUFWq/*)";
     /// # let mut wallet = doctest_wallet!();
@@ -1796,7 +1749,6 @@ impl Wallet {
     /// # use bdk_wallet::*;
     /// # use bdk_wallet::wallet::ChangeSet;
     /// # use bdk_wallet::wallet::error::CreateTxError;
-    /// # use bdk_persist::PersistBackend;
     /// # let descriptor = "wpkh(tpubD6NzVbkrYhZ4Xferm7Pz4VnjdcDPFyjVu5K4iZXQ4pVN8Cks4pHVowTBXBKRhX64pkRyJZJN5xAKj4UDNnLPb5p2sSKXhewoYx5GbTdUFWq/*)";
     /// # let mut wallet = doctest_wallet!();
     /// # let to_address = Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt").unwrap().assume_checked();
@@ -2319,11 +2271,14 @@ impl Wallet {
             .to_string()
     }
 
-    /// Applies an update to the wallet and stages the changes (but does not [`commit`] them).
+    /// Applies an update to the wallet and stages the changes (but does not persist them).
     ///
     /// Usually you create an `update` by interacting with some blockchain data source and inserting
     /// transactions related to your wallet into it.
     ///
+    /// After applying updates you should persist the staged wallet changes. For an example of how
+    /// to persist staged wallet changes see [`Wallet::reveal_next_address`]. `
+    ///
     /// [`commit`]: Self::commit
     pub fn apply_update(&mut self, update: impl Into<Update>) -> Result<(), CannotConnectError> {
         let update = update.into();
@@ -2342,27 +2297,10 @@ impl Wallet {
         changeset.append(ChangeSet::from(
             self.indexed_graph.apply_update(update.graph),
         ));
-        self.persist.stage(changeset);
+        self.stage(changeset);
         Ok(())
     }
 
-    /// Commits all currently [`staged`] changed to the persistence backend returning and error when
-    /// this fails.
-    ///
-    /// This returns whether the `update` resulted in any changes.
-    ///
-    /// [`staged`]: Self::staged
-    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 {
-        self.persist.staged()
-    }
-
     /// Get a reference to the inner [`TxGraph`].
     pub fn tx_graph(&self) -> &TxGraph<ConfirmationTimeHeightAnchor> {
         self.indexed_graph.graph()
@@ -2411,6 +2349,10 @@ impl Wallet {
     /// The `connected_to` parameter informs the wallet how this block connects to the internal
     /// [`LocalChain`]. Relevant transactions are filtered from the `block` and inserted into the
     /// internal [`TxGraph`].
+    ///
+    /// **WARNING**: You must persist the changes resulting from one or more calls to this method
+    /// if you need the inserted block data to be reloaded after closing the wallet.
+    /// See [`Wallet::reveal_next_address`].
     pub fn apply_block_connected_to(
         &mut self,
         block: &Block,
@@ -2428,7 +2370,7 @@ impl Wallet {
                 .apply_block_relevant(block, height)
                 .into(),
         );
-        self.persist.stage(changeset);
+        self.stage(changeset);
         Ok(())
     }
 
@@ -2440,6 +2382,10 @@ impl Wallet {
     /// when the transaction was last seen in the mempool. This is used for conflict resolution
     /// when there is conflicting unconfirmed transactions. The transaction with the later
     /// `last_seen` is prioritized.
+    ///
+    /// **WARNING**: You must persist the changes resulting from one or more calls to this method
+    /// if you need the applied unconfirmed transactions to be reloaded after closing the wallet.
+    /// See [`Wallet::reveal_next_address`].
     pub fn apply_unconfirmed_txs<'t>(
         &mut self,
         unconfirmed_txs: impl IntoIterator<Item = (&'t Transaction, u64)>,
@@ -2447,7 +2393,7 @@ impl Wallet {
         let indexed_graph_changeset = self
             .indexed_graph
             .batch_insert_relevant_unconfirmed(unconfirmed_txs);
-        self.persist.stage(ChangeSet::from(indexed_graph_changeset));
+        self.stage(ChangeSet::from(indexed_graph_changeset));
     }
 }
 
@@ -2584,7 +2530,7 @@ macro_rules! doctest_wallet {
         let descriptor = "tr([73c5da0a/86'/0'/0']tprv8fMn4hSKPRC1oaCPqxDb1JWtgkpeiQvZhsr8W2xuy3GEMkzoArcAWTfJxYb6Wj8XNNDWEjfYKK4wGQXh3ZUXhDF2NcnsALpWTeSwarJt7Vc/0/*)";
         let change_descriptor = "tr([73c5da0a/86'/0'/0']tprv8fMn4hSKPRC1oaCPqxDb1JWtgkpeiQvZhsr8W2xuy3GEMkzoArcAWTfJxYb6Wj8XNNDWEjfYKK4wGQXh3ZUXhDF2NcnsALpWTeSwarJt7Vc/1/*)";
 
-        let mut wallet = Wallet::new_no_persist(
+        let mut wallet = Wallet::new(
             descriptor,
             change_descriptor,
             Network::Regtest,
index c8c523ab02639b43956f5f76054ad4009456eb63..16194961a32570854eaf1d7ab200403950a1f25b 100644 (file)
@@ -69,7 +69,7 @@
 //!
 //! let descriptor = "wpkh(tpubD6NzVbkrYhZ4Xferm7Pz4VnjdcDPFyjVu5K4iZXQ4pVN8Cks4pHVowTBXBKRhX64pkRyJZJN5xAKj4UDNnLPb5p2sSKXhewoYx5GbTdUFWq/0/*)";
 //! let change_descriptor = "wpkh(tpubD6NzVbkrYhZ4Xferm7Pz4VnjdcDPFyjVu5K4iZXQ4pVN8Cks4pHVowTBXBKRhX64pkRyJZJN5xAKj4UDNnLPb5p2sSKXhewoYx5GbTdUFWq/1/*)";
-//! let mut wallet = Wallet::new_no_persist(descriptor, change_descriptor, Network::Testnet)?;
+//! let mut wallet = Wallet::new(descriptor, change_descriptor, Network::Testnet)?;
 //! wallet.add_signer(
 //!     KeychainKind::External,
 //!     SignerOrdering(200),
index 7b02248d9470b64c901a62afc3342141f8f8281f..02d6df59d181f84e8f18b62030d841c44ee1297a 100644 (file)
@@ -19,7 +19,6 @@
 //! # use bdk_wallet::*;
 //! # use bdk_wallet::wallet::ChangeSet;
 //! # use bdk_wallet::wallet::error::CreateTxError;
-//! # use bdk_persist::PersistBackend;
 //! # use anyhow::Error;
 //! # let to_address = Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt").unwrap().assume_checked();
 //! # let mut wallet = doctest_wallet!();
@@ -68,7 +67,6 @@ use crate::{KeychainKind, LocalOutput, Utxo, WeightedUtxo};
 /// # use core::str::FromStr;
 /// # use bdk_wallet::wallet::ChangeSet;
 /// # use bdk_wallet::wallet::error::CreateTxError;
-/// # use bdk_persist::PersistBackend;
 /// # use anyhow::Error;
 /// # let mut wallet = doctest_wallet!();
 /// # let addr1 = Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt").unwrap().assume_checked();
@@ -640,7 +638,6 @@ impl<'a, Cs> TxBuilder<'a, Cs> {
     /// # use bdk_wallet::*;
     /// # use bdk_wallet::wallet::ChangeSet;
     /// # use bdk_wallet::wallet::error::CreateTxError;
-    /// # use bdk_persist::PersistBackend;
     /// # use anyhow::Error;
     /// # let to_address =
     /// Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt")
@@ -675,6 +672,9 @@ impl<'a, Cs: CoinSelectionAlgorithm> TxBuilder<'a, Cs> {
     /// Returns a new [`Psbt`] per [`BIP174`].
     ///
     /// [`BIP174`]: https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki
+    ///
+    /// **WARNING**: To avoid change address reuse you must persist the changes resulting from one
+    /// or more calls to this method before closing the wallet. See [`Wallet::reveal_next_address`].
     pub fn finish(self) -> Result<Psbt, CreateTxError> {
         self.wallet
             .borrow_mut()
index 0acdc5ea1aed285476ff49b426cdeae6f4516892..bab75e275cb8c087d0a2b02b008c00190dda7011 100644 (file)
@@ -16,7 +16,7 @@ use std::str::FromStr;
 /// to a foreign address and one returning 50_000 back to the wallet. The remaining 1000
 /// sats are the transaction fee.
 pub fn get_funded_wallet_with_change(descriptor: &str, change: &str) -> (Wallet, bitcoin::Txid) {
-    let mut wallet = Wallet::new_no_persist(descriptor, change, Network::Regtest).unwrap();
+    let mut wallet = Wallet::new(descriptor, change, Network::Regtest).unwrap();
     let receive_address = wallet.peek_address(KeychainKind::External, 0).address;
     let sendto_address = Address::from_str("bcrt1q3qtze4ys45tgdvguj66zrk4fu6hq3a3v9pfly5")
         .expect("address")
index 17dbabc856366e13b82d24fcb67e41445261a8ce..beddacd6e12cfca6ba7f29c95005a187f24394ce 100644 (file)
@@ -1,11 +1,11 @@
+use anyhow::anyhow;
 use std::path::Path;
 use std::str::FromStr;
 
 use assert_matches::assert_matches;
 use bdk_chain::collections::BTreeMap;
 use bdk_chain::COINBASE_MATURITY;
-use bdk_chain::{BlockId, ConfirmationTime};
-use bdk_persist::PersistBackend;
+use bdk_chain::{persist::PersistBackend, BlockId, ConfirmationTime};
 use bdk_sqlite::rusqlite::Connection;
 use bdk_wallet::descriptor::{calc_checksum, DescriptorError, IntoWalletDescriptor};
 use bdk_wallet::psbt::PsbtUtils;
@@ -13,8 +13,7 @@ use bdk_wallet::signer::{SignOptions, SignerError};
 use bdk_wallet::wallet::coin_selection::{self, LargestFirstCoinSelection};
 use bdk_wallet::wallet::error::CreateTxError;
 use bdk_wallet::wallet::tx_builder::AddForeignUtxoError;
-use bdk_wallet::wallet::NewError;
-use bdk_wallet::wallet::{AddressInfo, Balance, Wallet};
+use bdk_wallet::wallet::{AddressInfo, Balance, NewError, Wallet};
 use bdk_wallet::KeychainKind;
 use bitcoin::hashes::Hash;
 use bitcoin::key::Secp256k1;
@@ -31,7 +30,7 @@ mod common;
 use common::*;
 
 fn receive_output(wallet: &mut Wallet, value: u64, height: ConfirmationTime) -> OutPoint {
-    let addr = wallet.next_unused_address(KeychainKind::External).unwrap();
+    let addr = wallet.next_unused_address(KeychainKind::External);
     let tx = Transaction {
         version: transaction::Version::ONE,
         lock_time: absolute::LockTime::ZERO,
@@ -85,18 +84,30 @@ fn load_recovers_wallet() -> anyhow::Result<()> {
 
         // create new wallet
         let wallet_spk_index = {
-            let db = create_new(&file_path).expect("must create db");
             let mut wallet =
-                Wallet::new(desc, change_desc, db, Network::Testnet).expect("must init wallet");
+                Wallet::new(desc, change_desc, Network::Testnet).expect("must init wallet");
+
+            wallet.reveal_next_address(KeychainKind::External);
+
+            // persist new wallet changes
+            let staged_changeset = wallet.take_staged();
+            let db = &mut create_new(&file_path).expect("must create db");
+            db.write_changes(&staged_changeset)
+                .map_err(|e| anyhow!("write changes error: {}", e))?;
 
-            wallet.reveal_next_address(KeychainKind::External).unwrap();
             wallet.spk_index().clone()
         };
 
         // recover wallet
         {
-            let db = recover(&file_path).expect("must recover db");
-            let wallet = Wallet::load(db).expect("must recover wallet");
+            // load persisted wallet changes
+            let db = &mut recover(&file_path).expect("must recover db");
+            let changeset = db
+                .load_changes()
+                .expect("must recover wallet")
+                .expect("changeset");
+
+            let wallet = Wallet::load_from_changeset(changeset).expect("must recover wallet");
             assert_eq!(wallet.network(), Network::Testnet);
             assert_eq!(
                 wallet.spk_index().keychains().collect::<Vec<_>>(),
@@ -115,13 +126,6 @@ fn load_recovers_wallet() -> anyhow::Result<()> {
             );
         }
 
-        // `new` can only be called on empty db
-        {
-            let db = recover(&file_path).expect("must recover db");
-            let result = Wallet::new(desc, change_desc, db, Network::Testnet);
-            assert!(matches!(result, Err(NewError::NonEmptyDatabase)));
-        }
-
         Ok(())
     }
 
@@ -152,16 +156,22 @@ fn new_or_load() -> anyhow::Result<()> {
 
         // init wallet when non-existent
         let wallet_keychains: BTreeMap<_, _> = {
-            let db = new_or_load(&file_path).expect("must create db");
-            let wallet = Wallet::new_or_load(desc, change_desc, db, Network::Testnet)
+            let wallet = &mut Wallet::new_or_load(desc, change_desc, None, Network::Testnet)
                 .expect("must init wallet");
+            let staged_changeset = wallet.take_staged();
+            let mut db = new_or_load(&file_path).expect("must create db");
+            db.write_changes(&staged_changeset)
+                .map_err(|e| anyhow!("write changes error: {}", e))?;
             wallet.keychains().map(|(k, v)| (*k, v.clone())).collect()
         };
 
         // wrong network
         {
-            let db = new_or_load(&file_path).expect("must create db");
-            let err = Wallet::new_or_load(desc, change_desc, db, Network::Bitcoin)
+            let mut db = new_or_load(&file_path).expect("must create db");
+            let changeset = db
+                .load_changes()
+                .map_err(|e| anyhow!("load changes error: {}", e))?;
+            let err = Wallet::new_or_load(desc, change_desc, changeset, Network::Bitcoin)
                 .expect_err("wrong network");
             assert!(
                 matches!(
@@ -182,11 +192,14 @@ fn new_or_load() -> anyhow::Result<()> {
             let got_blockhash =
                 bitcoin::blockdata::constants::genesis_block(Network::Testnet).block_hash();
 
-            let db = new_or_load(&file_path).expect("must open db");
+            let db = &mut new_or_load(&file_path).expect("must open db");
+            let changeset = db
+                .load_changes()
+                .map_err(|e| anyhow!("load changes error: {}", e))?;
             let err = Wallet::new_or_load_with_genesis_hash(
                 desc,
                 change_desc,
-                db,
+                changeset,
                 Network::Testnet,
                 exp_blockhash,
             )
@@ -204,15 +217,19 @@ fn new_or_load() -> anyhow::Result<()> {
 
         // wrong external descriptor
         {
-            let exp_descriptor = get_test_tr_single_sig();
+            let (exp_descriptor, exp_change_desc) = get_test_tr_single_sig_xprv_with_change_desc();
             let got_descriptor = desc
                 .into_wallet_descriptor(&Secp256k1::new(), Network::Testnet)
                 .unwrap()
                 .0;
 
-            let db = new_or_load(&file_path).expect("must open db");
-            let err = Wallet::new_or_load(exp_descriptor, change_desc, db, Network::Testnet)
-                .expect_err("wrong external descriptor");
+            let db = &mut new_or_load(&file_path).expect("must open db");
+            let changeset = db
+                .load_changes()
+                .map_err(|e| anyhow!("load changes error: {}", e))?;
+            let err =
+                Wallet::new_or_load(exp_descriptor, exp_change_desc, changeset, Network::Testnet)
+                    .expect_err("wrong external descriptor");
             assert!(
                 matches!(
                     err,
@@ -232,8 +249,11 @@ fn new_or_load() -> anyhow::Result<()> {
                 .unwrap()
                 .0;
 
-            let db = new_or_load(&file_path).expect("must open db");
-            let err = Wallet::new_or_load(desc, exp_descriptor, db, Network::Testnet)
+            let db = &mut new_or_load(&file_path).expect("must open db");
+            let changeset = db
+                .load_changes()
+                .map_err(|e| anyhow!("load changes error: {}", e))?;
+            let err = Wallet::new_or_load(desc, exp_descriptor, changeset, Network::Testnet)
                 .expect_err("wrong internal descriptor");
             assert!(
                 matches!(
@@ -248,8 +268,11 @@ fn new_or_load() -> anyhow::Result<()> {
 
         // all parameters match
         {
-            let db = new_or_load(&file_path).expect("must open db");
-            let wallet = Wallet::new_or_load(desc, change_desc, db, Network::Testnet)
+            let db = &mut new_or_load(&file_path).expect("must open db");
+            let changeset = db
+                .load_changes()
+                .map_err(|e| anyhow!("load changes error: {}", e))?;
+            let wallet = Wallet::new_or_load(desc, change_desc, changeset, Network::Testnet)
                 .expect("must recover wallet");
             assert_eq!(wallet.network(), Network::Testnet);
             assert!(wallet
@@ -274,9 +297,14 @@ fn new_or_load() -> anyhow::Result<()> {
 fn test_error_external_and_internal_are_the_same() {
     // identical descriptors should fail to create wallet
     let desc = get_test_wpkh();
-    let err = Wallet::new_no_persist(desc, desc, Network::Testnet);
+    let err = Wallet::new(desc, desc, Network::Testnet);
     assert!(
-        matches!(&err, Err(DescriptorError::ExternalAndInternalAreTheSame)),
+        matches!(
+            &err,
+            Err(NewError::Descriptor(
+                DescriptorError::ExternalAndInternalAreTheSame
+            ))
+        ),
         "expected same descriptors error, got {:?}",
         err,
     );
@@ -284,9 +312,14 @@ fn test_error_external_and_internal_are_the_same() {
     // public + private of same descriptor should fail to create wallet
     let desc = "wpkh(tprv8ZgxMBicQKsPdcAqYBpzAFwU5yxBUo88ggoBqu1qPcHUfSbKK1sKMLmC7EAk438btHQrSdu3jGGQa6PA71nvH5nkDexhLteJqkM4dQmWF9g/84'/1'/0'/0/*)";
     let change_desc = "wpkh([3c31d632/84'/1'/0']tpubDCYwFkks2cg78N7eoYbBatsFEGje8vW8arSKW4rLwD1AU1s9KJMDRHE32JkvYERuiFjArrsH7qpWSpJATed5ShZbG9KsskA5Rmi6NSYgYN2/0/*)";
-    let err = Wallet::new_no_persist(desc, change_desc, Network::Testnet);
+    let err = Wallet::new(desc, change_desc, Network::Testnet);
     assert!(
-        matches!(err, Err(DescriptorError::ExternalAndInternalAreTheSame)),
+        matches!(
+            err,
+            Err(NewError::Descriptor(
+                DescriptorError::ExternalAndInternalAreTheSame
+            ))
+        ),
         "expected same descriptors error, got {:?}",
         err,
     );
@@ -462,7 +495,7 @@ fn test_create_tx_empty_recipients() {
 #[should_panic(expected = "NoUtxosSelected")]
 fn test_create_tx_manually_selected_empty_utxos() {
     let (mut wallet, _) = get_funded_wallet_wpkh();
-    let addr = wallet.next_unused_address(KeychainKind::External).unwrap();
+    let addr = wallet.next_unused_address(KeychainKind::External);
     let mut builder = wallet.build_tx();
     builder
         .add_recipient(addr.script_pubkey(), Amount::from_sat(25_000))
@@ -473,7 +506,7 @@ fn test_create_tx_manually_selected_empty_utxos() {
 #[test]
 fn test_create_tx_version_0() {
     let (mut wallet, _) = get_funded_wallet_wpkh();
-    let addr = wallet.next_unused_address(KeychainKind::External).unwrap();
+    let addr = wallet.next_unused_address(KeychainKind::External);
     let mut builder = wallet.build_tx();
     builder
         .add_recipient(addr.script_pubkey(), Amount::from_sat(25_000))
@@ -484,7 +517,7 @@ fn test_create_tx_version_0() {
 #[test]
 fn test_create_tx_version_1_csv() {
     let (mut wallet, _) = get_funded_wallet(get_test_single_sig_csv());
-    let addr = wallet.next_unused_address(KeychainKind::External).unwrap();
+    let addr = wallet.next_unused_address(KeychainKind::External);
     let mut builder = wallet.build_tx();
     builder
         .add_recipient(addr.script_pubkey(), Amount::from_sat(25_000))
@@ -495,7 +528,7 @@ fn test_create_tx_version_1_csv() {
 #[test]
 fn test_create_tx_custom_version() {
     let (mut wallet, _) = get_funded_wallet_wpkh();
-    let addr = wallet.next_unused_address(KeychainKind::External).unwrap();
+    let addr = wallet.next_unused_address(KeychainKind::External);
     let mut builder = wallet.build_tx();
     builder
         .add_recipient(addr.script_pubkey(), Amount::from_sat(25_000))
@@ -509,7 +542,7 @@ fn test_create_tx_custom_version() {
 fn test_create_tx_default_locktime_is_last_sync_height() {
     let (mut wallet, _) = get_funded_wallet_wpkh();
 
-    let addr = wallet.next_unused_address(KeychainKind::External).unwrap();
+    let addr = wallet.next_unused_address(KeychainKind::External);
     let mut builder = wallet.build_tx();
     builder.add_recipient(addr.script_pubkey(), Amount::from_sat(25_000));
     let psbt = builder.finish().unwrap();
@@ -522,7 +555,7 @@ fn test_create_tx_default_locktime_is_last_sync_height() {
 #[test]
 fn test_create_tx_fee_sniping_locktime_last_sync() {
     let (mut wallet, _) = get_funded_wallet_wpkh();
-    let addr = wallet.next_unused_address(KeychainKind::External).unwrap();
+    let addr = wallet.next_unused_address(KeychainKind::External);
     let mut builder = wallet.build_tx();
     builder.add_recipient(addr.script_pubkey(), Amount::from_sat(25_000));
 
@@ -538,7 +571,7 @@ fn test_create_tx_fee_sniping_locktime_last_sync() {
 #[test]
 fn test_create_tx_default_locktime_cltv() {
     let (mut wallet, _) = get_funded_wallet(get_test_single_sig_cltv());
-    let addr = wallet.next_unused_address(KeychainKind::External).unwrap();
+    let addr = wallet.next_unused_address(KeychainKind::External);
     let mut builder = wallet.build_tx();
     builder.add_recipient(addr.script_pubkey(), Amount::from_sat(25_000));
     let psbt = builder.finish().unwrap();
@@ -549,7 +582,7 @@ fn test_create_tx_default_locktime_cltv() {
 #[test]
 fn test_create_tx_custom_locktime() {
     let (mut wallet, _) = get_funded_wallet_wpkh();
-    let addr = wallet.next_unused_address(KeychainKind::External).unwrap();
+    let addr = wallet.next_unused_address(KeychainKind::External);
     let mut builder = wallet.build_tx();
     builder
         .add_recipient(addr.script_pubkey(), Amount::from_sat(25_000))
@@ -566,7 +599,7 @@ fn test_create_tx_custom_locktime() {
 #[test]
 fn test_create_tx_custom_locktime_compatible_with_cltv() {
     let (mut wallet, _) = get_funded_wallet(get_test_single_sig_cltv());
-    let addr = wallet.next_unused_address(KeychainKind::External).unwrap();
+    let addr = wallet.next_unused_address(KeychainKind::External);
     let mut builder = wallet.build_tx();
     builder
         .add_recipient(addr.script_pubkey(), Amount::from_sat(25_000))
@@ -579,7 +612,7 @@ fn test_create_tx_custom_locktime_compatible_with_cltv() {
 #[test]
 fn test_create_tx_custom_locktime_incompatible_with_cltv() {
     let (mut wallet, _) = get_funded_wallet(get_test_single_sig_cltv());
-    let addr = wallet.next_unused_address(KeychainKind::External).unwrap();
+    let addr = wallet.next_unused_address(KeychainKind::External);
     let mut builder = wallet.build_tx();
     builder
         .add_recipient(addr.script_pubkey(), Amount::from_sat(25_000))
@@ -592,7 +625,7 @@ fn test_create_tx_custom_locktime_incompatible_with_cltv() {
 #[test]
 fn test_create_tx_no_rbf_csv() {
     let (mut wallet, _) = get_funded_wallet(get_test_single_sig_csv());
-    let addr = wallet.next_unused_address(KeychainKind::External).unwrap();
+    let addr = wallet.next_unused_address(KeychainKind::External);
     let mut builder = wallet.build_tx();
     builder.add_recipient(addr.script_pubkey(), Amount::from_sat(25_000));
     let psbt = builder.finish().unwrap();
@@ -603,7 +636,7 @@ fn test_create_tx_no_rbf_csv() {
 #[test]
 fn test_create_tx_with_default_rbf_csv() {
     let (mut wallet, _) = get_funded_wallet(get_test_single_sig_csv());
-    let addr = wallet.next_unused_address(KeychainKind::External).unwrap();
+    let addr = wallet.next_unused_address(KeychainKind::External);
     let mut builder = wallet.build_tx();
     builder
         .add_recipient(addr.script_pubkey(), Amount::from_sat(25_000))
@@ -617,7 +650,7 @@ fn test_create_tx_with_default_rbf_csv() {
 #[test]
 fn test_create_tx_with_custom_rbf_csv() {
     let (mut wallet, _) = get_funded_wallet(get_test_single_sig_csv());
-    let addr = wallet.next_unused_address(KeychainKind::External).unwrap();
+    let addr = wallet.next_unused_address(KeychainKind::External);
     let mut builder = wallet.build_tx();
     builder
         .add_recipient(addr.script_pubkey(), Amount::from_sat(25_000))
@@ -630,7 +663,7 @@ fn test_create_tx_with_custom_rbf_csv() {
 #[test]
 fn test_create_tx_no_rbf_cltv() {
     let (mut wallet, _) = get_funded_wallet(get_test_single_sig_cltv());
-    let addr = wallet.next_unused_address(KeychainKind::External).unwrap();
+    let addr = wallet.next_unused_address(KeychainKind::External);
     let mut builder = wallet.build_tx();
     builder.add_recipient(addr.script_pubkey(), Amount::from_sat(25_000));
     let psbt = builder.finish().unwrap();
@@ -641,7 +674,7 @@ fn test_create_tx_no_rbf_cltv() {
 #[test]
 fn test_create_tx_invalid_rbf_sequence() {
     let (mut wallet, _) = get_funded_wallet_wpkh();
-    let addr = wallet.next_unused_address(KeychainKind::External).unwrap();
+    let addr = wallet.next_unused_address(KeychainKind::External);
     let mut builder = wallet.build_tx();
     builder
         .add_recipient(addr.script_pubkey(), Amount::from_sat(25_000))
@@ -652,7 +685,7 @@ fn test_create_tx_invalid_rbf_sequence() {
 #[test]
 fn test_create_tx_custom_rbf_sequence() {
     let (mut wallet, _) = get_funded_wallet_wpkh();
-    let addr = wallet.next_unused_address(KeychainKind::External).unwrap();
+    let addr = wallet.next_unused_address(KeychainKind::External);
     let mut builder = wallet.build_tx();
     builder
         .add_recipient(addr.script_pubkey(), Amount::from_sat(25_000))
@@ -665,7 +698,7 @@ fn test_create_tx_custom_rbf_sequence() {
 #[test]
 fn test_create_tx_change_policy() {
     let (mut wallet, _) = get_funded_wallet_wpkh();
-    let addr = wallet.next_unused_address(KeychainKind::External).unwrap();
+    let addr = wallet.next_unused_address(KeychainKind::External);
     let mut builder = wallet.build_tx();
     builder
         .add_recipient(addr.script_pubkey(), Amount::from_sat(25_000))
@@ -689,7 +722,7 @@ fn test_create_tx_change_policy() {
 #[test]
 fn test_create_tx_default_sequence() {
     let (mut wallet, _) = get_funded_wallet_wpkh();
-    let addr = wallet.next_unused_address(KeychainKind::External).unwrap();
+    let addr = wallet.next_unused_address(KeychainKind::External);
     let mut builder = wallet.build_tx();
     builder.add_recipient(addr.script_pubkey(), Amount::from_sat(25_000));
     let psbt = builder.finish().unwrap();
@@ -709,7 +742,7 @@ macro_rules! check_fee {
 #[test]
 fn test_create_tx_drain_wallet_and_drain_to() {
     let (mut wallet, _) = get_funded_wallet_wpkh();
-    let addr = wallet.next_unused_address(KeychainKind::External).unwrap();
+    let addr = wallet.next_unused_address(KeychainKind::External);
     let mut builder = wallet.build_tx();
     builder.drain_to(addr.script_pubkey()).drain_wallet();
     let psbt = builder.finish().unwrap();
@@ -728,7 +761,7 @@ fn test_create_tx_drain_wallet_and_drain_to_and_with_recipient() {
     let addr = Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt")
         .unwrap()
         .assume_checked();
-    let drain_addr = wallet.next_unused_address(KeychainKind::External).unwrap();
+    let drain_addr = wallet.next_unused_address(KeychainKind::External);
     let mut builder = wallet.build_tx();
     builder
         .add_recipient(addr.script_pubkey(), Amount::from_sat(20_000))
@@ -757,7 +790,7 @@ fn test_create_tx_drain_wallet_and_drain_to_and_with_recipient() {
 #[test]
 fn test_create_tx_drain_to_and_utxos() {
     let (mut wallet, _) = get_funded_wallet_wpkh();
-    let addr = wallet.next_unused_address(KeychainKind::External).unwrap();
+    let addr = wallet.next_unused_address(KeychainKind::External);
     let utxos: Vec<_> = wallet.list_unspent().map(|u| u.outpoint).collect();
     let mut builder = wallet.build_tx();
     builder
@@ -778,7 +811,7 @@ fn test_create_tx_drain_to_and_utxos() {
 #[should_panic(expected = "NoRecipients")]
 fn test_create_tx_drain_to_no_drain_wallet_no_utxos() {
     let (mut wallet, _) = get_funded_wallet_wpkh();
-    let drain_addr = wallet.next_unused_address(KeychainKind::External).unwrap();
+    let drain_addr = wallet.next_unused_address(KeychainKind::External);
     let mut builder = wallet.build_tx();
     builder.drain_to(drain_addr.script_pubkey());
     builder.finish().unwrap();
@@ -787,7 +820,7 @@ fn test_create_tx_drain_to_no_drain_wallet_no_utxos() {
 #[test]
 fn test_create_tx_default_fee_rate() {
     let (mut wallet, _) = get_funded_wallet_wpkh();
-    let addr = wallet.next_unused_address(KeychainKind::External).unwrap();
+    let addr = wallet.next_unused_address(KeychainKind::External);
     let mut builder = wallet.build_tx();
     builder.add_recipient(addr.script_pubkey(), Amount::from_sat(25_000));
     let psbt = builder.finish().unwrap();
@@ -799,7 +832,7 @@ fn test_create_tx_default_fee_rate() {
 #[test]
 fn test_create_tx_custom_fee_rate() {
     let (mut wallet, _) = get_funded_wallet_wpkh();
-    let addr = wallet.next_unused_address(KeychainKind::External).unwrap();
+    let addr = wallet.next_unused_address(KeychainKind::External);
     let mut builder = wallet.build_tx();
     builder
         .add_recipient(addr.script_pubkey(), Amount::from_sat(25_000))
@@ -813,7 +846,7 @@ fn test_create_tx_custom_fee_rate() {
 #[test]
 fn test_create_tx_absolute_fee() {
     let (mut wallet, _) = get_funded_wallet_wpkh();
-    let addr = wallet.next_unused_address(KeychainKind::External).unwrap();
+    let addr = wallet.next_unused_address(KeychainKind::External);
     let mut builder = wallet.build_tx();
     builder
         .drain_to(addr.script_pubkey())
@@ -833,7 +866,7 @@ fn test_create_tx_absolute_fee() {
 #[test]
 fn test_create_tx_absolute_zero_fee() {
     let (mut wallet, _) = get_funded_wallet_wpkh();
-    let addr = wallet.next_unused_address(KeychainKind::External).unwrap();
+    let addr = wallet.next_unused_address(KeychainKind::External);
     let mut builder = wallet.build_tx();
     builder
         .drain_to(addr.script_pubkey())
@@ -854,7 +887,7 @@ fn test_create_tx_absolute_zero_fee() {
 #[should_panic(expected = "InsufficientFunds")]
 fn test_create_tx_absolute_high_fee() {
     let (mut wallet, _) = get_funded_wallet_wpkh();
-    let addr = wallet.next_unused_address(KeychainKind::External).unwrap();
+    let addr = wallet.next_unused_address(KeychainKind::External);
     let mut builder = wallet.build_tx();
     builder
         .drain_to(addr.script_pubkey())
@@ -868,7 +901,7 @@ fn test_create_tx_add_change() {
     use bdk_wallet::wallet::tx_builder::TxOrdering;
 
     let (mut wallet, _) = get_funded_wallet_wpkh();
-    let addr = wallet.next_unused_address(KeychainKind::External).unwrap();
+    let addr = wallet.next_unused_address(KeychainKind::External);
     let mut builder = wallet.build_tx();
     builder
         .add_recipient(addr.script_pubkey(), Amount::from_sat(25_000))
@@ -887,7 +920,7 @@ fn test_create_tx_add_change() {
 #[test]
 fn test_create_tx_skip_change_dust() {
     let (mut wallet, _) = get_funded_wallet_wpkh();
-    let addr = wallet.next_unused_address(KeychainKind::External).unwrap();
+    let addr = wallet.next_unused_address(KeychainKind::External);
     let mut builder = wallet.build_tx();
     builder.add_recipient(addr.script_pubkey(), Amount::from_sat(49_800));
     let psbt = builder.finish().unwrap();
@@ -902,7 +935,7 @@ fn test_create_tx_skip_change_dust() {
 #[should_panic(expected = "InsufficientFunds")]
 fn test_create_tx_drain_to_dust_amount() {
     let (mut wallet, _) = get_funded_wallet_wpkh();
-    let addr = wallet.next_unused_address(KeychainKind::External).unwrap();
+    let addr = wallet.next_unused_address(KeychainKind::External);
     // very high fee rate, so that the only output would be below dust
     let mut builder = wallet.build_tx();
     builder
@@ -915,7 +948,7 @@ fn test_create_tx_drain_to_dust_amount() {
 #[test]
 fn test_create_tx_ordering_respected() {
     let (mut wallet, _) = get_funded_wallet_wpkh();
-    let addr = wallet.next_unused_address(KeychainKind::External).unwrap();
+    let addr = wallet.next_unused_address(KeychainKind::External);
     let mut builder = wallet.build_tx();
     builder
         .add_recipient(addr.script_pubkey(), Amount::from_sat(30_000))
@@ -936,7 +969,7 @@ fn test_create_tx_ordering_respected() {
 #[test]
 fn test_create_tx_default_sighash() {
     let (mut wallet, _) = get_funded_wallet_wpkh();
-    let addr = wallet.next_unused_address(KeychainKind::External).unwrap();
+    let addr = wallet.next_unused_address(KeychainKind::External);
     let mut builder = wallet.build_tx();
     builder.add_recipient(addr.script_pubkey(), Amount::from_sat(30_000));
     let psbt = builder.finish().unwrap();
@@ -947,7 +980,7 @@ fn test_create_tx_default_sighash() {
 #[test]
 fn test_create_tx_custom_sighash() {
     let (mut wallet, _) = get_funded_wallet_wpkh();
-    let addr = wallet.next_unused_address(KeychainKind::External).unwrap();
+    let addr = wallet.next_unused_address(KeychainKind::External);
     let mut builder = wallet.build_tx();
     builder
         .add_recipient(addr.script_pubkey(), Amount::from_sat(30_000))
@@ -966,7 +999,7 @@ fn test_create_tx_input_hd_keypaths() {
     use core::str::FromStr;
 
     let (mut wallet, _) = get_funded_wallet("wpkh([d34db33f/44'/0'/0']tpubDEnoLuPdBep9bzw5LoGYpsxUQYheRQ9gcgrJhJEcdKFB9cWQRyYmkCyRoTqeD4tJYiVVgt6A3rN6rWn9RYhR9sBsGxji29LYWHuKKbdb1ev/0/*)");
-    let addr = wallet.next_unused_address(KeychainKind::External).unwrap();
+    let addr = wallet.next_unused_address(KeychainKind::External);
     let mut builder = wallet.build_tx();
     builder.drain_to(addr.script_pubkey()).drain_wallet();
     let psbt = builder.finish().unwrap();
@@ -988,7 +1021,7 @@ fn test_create_tx_output_hd_keypaths() {
 
     let (mut wallet, _) = get_funded_wallet("wpkh([d34db33f/44'/0'/0']tpubDEnoLuPdBep9bzw5LoGYpsxUQYheRQ9gcgrJhJEcdKFB9cWQRyYmkCyRoTqeD4tJYiVVgt6A3rN6rWn9RYhR9sBsGxji29LYWHuKKbdb1ev/0/*)");
 
-    let addr = wallet.next_unused_address(KeychainKind::External).unwrap();
+    let addr = wallet.next_unused_address(KeychainKind::External);
     let mut builder = wallet.build_tx();
     builder.drain_to(addr.script_pubkey()).drain_wallet();
     let psbt = builder.finish().unwrap();
@@ -1010,7 +1043,7 @@ fn test_create_tx_set_redeem_script_p2sh() {
 
     let (mut wallet, _) =
         get_funded_wallet("sh(pk(cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW))");
-    let addr = wallet.next_unused_address(KeychainKind::External).unwrap();
+    let addr = wallet.next_unused_address(KeychainKind::External);
     let mut builder = wallet.build_tx();
     builder.drain_to(addr.script_pubkey()).drain_wallet();
     let psbt = builder.finish().unwrap();
@@ -1033,7 +1066,7 @@ fn test_create_tx_set_witness_script_p2wsh() {
 
     let (mut wallet, _) =
         get_funded_wallet("wsh(pk(cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW))");
-    let addr = wallet.next_unused_address(KeychainKind::External).unwrap();
+    let addr = wallet.next_unused_address(KeychainKind::External);
     let mut builder = wallet.build_tx();
     builder.drain_to(addr.script_pubkey()).drain_wallet();
     let psbt = builder.finish().unwrap();
@@ -1054,7 +1087,7 @@ fn test_create_tx_set_witness_script_p2wsh() {
 fn test_create_tx_set_redeem_witness_script_p2wsh_p2sh() {
     let (mut wallet, _) =
         get_funded_wallet("sh(wsh(pk(cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW)))");
-    let addr = wallet.next_unused_address(KeychainKind::External).unwrap();
+    let addr = wallet.next_unused_address(KeychainKind::External);
     let mut builder = wallet.build_tx();
     builder.drain_to(addr.script_pubkey()).drain_wallet();
     let psbt = builder.finish().unwrap();
@@ -1072,7 +1105,7 @@ fn test_create_tx_set_redeem_witness_script_p2wsh_p2sh() {
 fn test_create_tx_non_witness_utxo() {
     let (mut wallet, _) =
         get_funded_wallet("sh(pk(cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW))");
-    let addr = wallet.next_unused_address(KeychainKind::External).unwrap();
+    let addr = wallet.next_unused_address(KeychainKind::External);
     let mut builder = wallet.build_tx();
     builder.drain_to(addr.script_pubkey()).drain_wallet();
     let psbt = builder.finish().unwrap();
@@ -1085,7 +1118,7 @@ fn test_create_tx_non_witness_utxo() {
 fn test_create_tx_only_witness_utxo() {
     let (mut wallet, _) =
         get_funded_wallet("wsh(pk(cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW))");
-    let addr = wallet.next_unused_address(KeychainKind::External).unwrap();
+    let addr = wallet.next_unused_address(KeychainKind::External);
     let mut builder = wallet.build_tx();
     builder
         .drain_to(addr.script_pubkey())
@@ -1101,7 +1134,7 @@ fn test_create_tx_only_witness_utxo() {
 fn test_create_tx_shwpkh_has_witness_utxo() {
     let (mut wallet, _) =
         get_funded_wallet("sh(wpkh(cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW))");
-    let addr = wallet.next_unused_address(KeychainKind::External).unwrap();
+    let addr = wallet.next_unused_address(KeychainKind::External);
     let mut builder = wallet.build_tx();
     builder.drain_to(addr.script_pubkey()).drain_wallet();
     let psbt = builder.finish().unwrap();
@@ -1113,7 +1146,7 @@ fn test_create_tx_shwpkh_has_witness_utxo() {
 fn test_create_tx_both_non_witness_utxo_and_witness_utxo_default() {
     let (mut wallet, _) =
         get_funded_wallet("wsh(pk(cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW))");
-    let addr = wallet.next_unused_address(KeychainKind::External).unwrap();
+    let addr = wallet.next_unused_address(KeychainKind::External);
     let mut builder = wallet.build_tx();
     builder.drain_to(addr.script_pubkey()).drain_wallet();
     let psbt = builder.finish().unwrap();
@@ -1130,7 +1163,6 @@ fn test_create_tx_add_utxo() {
         output: vec![TxOut {
             script_pubkey: wallet
                 .next_unused_address(KeychainKind::External)
-                .unwrap()
                 .script_pubkey(),
             value: Amount::from_sat(25_000),
         }],
@@ -1180,7 +1212,6 @@ fn test_create_tx_manually_selected_insufficient() {
         output: vec![TxOut {
             script_pubkey: wallet
                 .next_unused_address(KeychainKind::External)
-                .unwrap()
                 .script_pubkey(),
             value: Amount::from_sat(25_000),
         }],
@@ -1226,7 +1257,7 @@ fn test_create_tx_policy_path_required() {
 #[test]
 fn test_create_tx_policy_path_no_csv() {
     let (desc, change_desc) = get_test_wpkh_with_change_desc();
-    let mut wallet = Wallet::new_no_persist(desc, change_desc, Network::Regtest).unwrap();
+    let mut wallet = Wallet::new(desc, change_desc, Network::Regtest).expect("wallet");
 
     let tx = Transaction {
         version: transaction::Version::non_standard(0),
@@ -1235,7 +1266,6 @@ fn test_create_tx_policy_path_no_csv() {
         output: vec![TxOut {
             script_pubkey: wallet
                 .next_unused_address(KeychainKind::External)
-                .unwrap()
                 .script_pubkey(),
             value: Amount::from_sat(50_000),
         }],
@@ -1307,7 +1337,7 @@ fn test_create_tx_policy_path_ignored_subtree_with_csv() {
 fn test_create_tx_global_xpubs_with_origin() {
     use bitcoin::bip32;
     let (mut wallet, _) = get_funded_wallet("wpkh([73756c7f/48'/0'/0'/2']tpubDCKxNyM3bLgbEX13Mcd8mYxbVg9ajDkWXMh29hMWBurKfVmBfWAM96QVP3zaUcN51HvkZ3ar4VwP82kC8JZhhux8vFQoJintSpVBwpFvyU3/0/*)");
-    let addr = wallet.next_unused_address(KeychainKind::External).unwrap();
+    let addr = wallet.next_unused_address(KeychainKind::External);
     let mut builder = wallet.build_tx();
     builder
         .add_recipient(addr.script_pubkey(), Amount::from_sat(25_000))
@@ -1593,7 +1623,7 @@ fn test_get_psbt_input() {
 )]
 fn test_create_tx_global_xpubs_origin_missing() {
     let (mut wallet, _) = get_funded_wallet("wpkh(tpubDCKxNyM3bLgbEX13Mcd8mYxbVg9ajDkWXMh29hMWBurKfVmBfWAM96QVP3zaUcN51HvkZ3ar4VwP82kC8JZhhux8vFQoJintSpVBwpFvyU3/0/*)");
-    let addr = wallet.next_unused_address(KeychainKind::External).unwrap();
+    let addr = wallet.next_unused_address(KeychainKind::External);
     let mut builder = wallet.build_tx();
     builder
         .add_recipient(addr.script_pubkey(), Amount::from_sat(25_000))
@@ -1605,7 +1635,7 @@ fn test_create_tx_global_xpubs_origin_missing() {
 fn test_create_tx_global_xpubs_master_without_origin() {
     use bitcoin::bip32;
     let (mut wallet, _) = get_funded_wallet("wpkh(tpubD6NzVbkrYhZ4Y55A58Gv9RSNF5hy84b5AJqYy7sCcjFrkcLpPre8kmgfit6kY1Zs3BLgeypTDBZJM222guPpdz7Cup5yzaMu62u7mYGbwFL/0/*)");
-    let addr = wallet.next_unused_address(KeychainKind::External).unwrap();
+    let addr = wallet.next_unused_address(KeychainKind::External);
     let mut builder = wallet.build_tx();
     builder
         .add_recipient(addr.script_pubkey(), Amount::from_sat(25_000))
@@ -1626,7 +1656,7 @@ fn test_create_tx_global_xpubs_master_without_origin() {
 #[should_panic(expected = "IrreplaceableTransaction")]
 fn test_bump_fee_irreplaceable_tx() {
     let (mut wallet, _) = get_funded_wallet_wpkh();
-    let addr = wallet.next_unused_address(KeychainKind::External).unwrap();
+    let addr = wallet.next_unused_address(KeychainKind::External);
     let mut builder = wallet.build_tx();
     builder.add_recipient(addr.script_pubkey(), Amount::from_sat(25_000));
     let psbt = builder.finish().unwrap();
@@ -1643,7 +1673,7 @@ fn test_bump_fee_irreplaceable_tx() {
 #[should_panic(expected = "TransactionConfirmed")]
 fn test_bump_fee_confirmed_tx() {
     let (mut wallet, _) = get_funded_wallet_wpkh();
-    let addr = wallet.next_unused_address(KeychainKind::External).unwrap();
+    let addr = wallet.next_unused_address(KeychainKind::External);
     let mut builder = wallet.build_tx();
     builder.add_recipient(addr.script_pubkey(), Amount::from_sat(25_000));
     let psbt = builder.finish().unwrap();
@@ -1667,7 +1697,7 @@ fn test_bump_fee_confirmed_tx() {
 #[test]
 fn test_bump_fee_low_fee_rate() {
     let (mut wallet, _) = get_funded_wallet_wpkh();
-    let addr = wallet.next_unused_address(KeychainKind::External).unwrap();
+    let addr = wallet.next_unused_address(KeychainKind::External);
     let mut builder = wallet.build_tx();
     builder
         .add_recipient(addr.script_pubkey(), Amount::from_sat(25_000))
@@ -1701,7 +1731,7 @@ fn test_bump_fee_low_fee_rate() {
 #[should_panic(expected = "FeeTooLow")]
 fn test_bump_fee_low_abs() {
     let (mut wallet, _) = get_funded_wallet_wpkh();
-    let addr = wallet.next_unused_address(KeychainKind::External).unwrap();
+    let addr = wallet.next_unused_address(KeychainKind::External);
     let mut builder = wallet.build_tx();
     builder
         .add_recipient(addr.script_pubkey(), Amount::from_sat(25_000))
@@ -1724,7 +1754,7 @@ fn test_bump_fee_low_abs() {
 #[should_panic(expected = "FeeTooLow")]
 fn test_bump_fee_zero_abs() {
     let (mut wallet, _) = get_funded_wallet_wpkh();
-    let addr = wallet.next_unused_address(KeychainKind::External).unwrap();
+    let addr = wallet.next_unused_address(KeychainKind::External);
     let mut builder = wallet.build_tx();
     builder
         .add_recipient(addr.script_pubkey(), Amount::from_sat(25_000))
@@ -1946,7 +1976,6 @@ fn test_bump_fee_drain_wallet() {
         output: vec![TxOut {
             script_pubkey: wallet
                 .next_unused_address(KeychainKind::External)
-                .unwrap()
                 .script_pubkey(),
             value: Amount::from_sat(25_000),
         }],
@@ -2012,7 +2041,6 @@ fn test_bump_fee_remove_output_manually_selected_only() {
         output: vec![TxOut {
             script_pubkey: wallet
                 .next_unused_address(KeychainKind::External)
-                .unwrap()
                 .script_pubkey(),
             value: Amount::from_sat(25_000),
         }],
@@ -2069,7 +2097,6 @@ fn test_bump_fee_add_input() {
         output: vec![TxOut {
             script_pubkey: wallet
                 .next_unused_address(KeychainKind::External)
-                .unwrap()
                 .script_pubkey(),
             value: Amount::from_sat(25_000),
         }],
@@ -2587,7 +2614,7 @@ fn test_fee_amount_negative_drain_val() {
 #[test]
 fn test_sign_single_xprv() {
     let (mut wallet, _) = get_funded_wallet("wpkh(tprv8ZgxMBicQKsPd3EupYiPRhaMooHKUHJxNsTfYuScep13go8QFfHdtkG9nRkFGb7busX4isf6X9dURGCoKgitaApQ6MupRhZMcELAxTBRJgS/*)");
-    let addr = wallet.next_unused_address(KeychainKind::External).unwrap();
+    let addr = wallet.next_unused_address(KeychainKind::External);
     let mut builder = wallet.build_tx();
     builder.drain_to(addr.script_pubkey()).drain_wallet();
     let mut psbt = builder.finish().unwrap();
@@ -2602,7 +2629,7 @@ fn test_sign_single_xprv() {
 #[test]
 fn test_sign_single_xprv_with_master_fingerprint_and_path() {
     let (mut wallet, _) = get_funded_wallet("wpkh([d34db33f/84h/1h/0h]tprv8ZgxMBicQKsPd3EupYiPRhaMooHKUHJxNsTfYuScep13go8QFfHdtkG9nRkFGb7busX4isf6X9dURGCoKgitaApQ6MupRhZMcELAxTBRJgS/*)");
-    let addr = wallet.next_unused_address(KeychainKind::External).unwrap();
+    let addr = wallet.next_unused_address(KeychainKind::External);
     let mut builder = wallet.build_tx();
     builder.drain_to(addr.script_pubkey()).drain_wallet();
     let mut psbt = builder.finish().unwrap();
@@ -2617,7 +2644,7 @@ fn test_sign_single_xprv_with_master_fingerprint_and_path() {
 #[test]
 fn test_sign_single_xprv_bip44_path() {
     let (mut wallet, _) = get_funded_wallet("wpkh(tprv8ZgxMBicQKsPd3EupYiPRhaMooHKUHJxNsTfYuScep13go8QFfHdtkG9nRkFGb7busX4isf6X9dURGCoKgitaApQ6MupRhZMcELAxTBRJgS/44'/0'/0'/0/*)");
-    let addr = wallet.next_unused_address(KeychainKind::External).unwrap();
+    let addr = wallet.next_unused_address(KeychainKind::External);
     let mut builder = wallet.build_tx();
     builder.drain_to(addr.script_pubkey()).drain_wallet();
     let mut psbt = builder.finish().unwrap();
@@ -2632,7 +2659,7 @@ fn test_sign_single_xprv_bip44_path() {
 #[test]
 fn test_sign_single_xprv_sh_wpkh() {
     let (mut wallet, _) = get_funded_wallet("sh(wpkh(tprv8ZgxMBicQKsPd3EupYiPRhaMooHKUHJxNsTfYuScep13go8QFfHdtkG9nRkFGb7busX4isf6X9dURGCoKgitaApQ6MupRhZMcELAxTBRJgS/*))");
-    let addr = wallet.next_unused_address(KeychainKind::External).unwrap();
+    let addr = wallet.next_unused_address(KeychainKind::External);
     let mut builder = wallet.build_tx();
     builder.drain_to(addr.script_pubkey()).drain_wallet();
     let mut psbt = builder.finish().unwrap();
@@ -2648,7 +2675,7 @@ fn test_sign_single_xprv_sh_wpkh() {
 fn test_sign_single_wif() {
     let (mut wallet, _) =
         get_funded_wallet("wpkh(cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW)");
-    let addr = wallet.next_unused_address(KeychainKind::External).unwrap();
+    let addr = wallet.next_unused_address(KeychainKind::External);
     let mut builder = wallet.build_tx();
     builder.drain_to(addr.script_pubkey()).drain_wallet();
     let mut psbt = builder.finish().unwrap();
@@ -2663,7 +2690,7 @@ fn test_sign_single_wif() {
 #[test]
 fn test_sign_single_xprv_no_hd_keypaths() {
     let (mut wallet, _) = get_funded_wallet("wpkh(tprv8ZgxMBicQKsPd3EupYiPRhaMooHKUHJxNsTfYuScep13go8QFfHdtkG9nRkFGb7busX4isf6X9dURGCoKgitaApQ6MupRhZMcELAxTBRJgS/*)");
-    let addr = wallet.next_unused_address(KeychainKind::External).unwrap();
+    let addr = wallet.next_unused_address(KeychainKind::External);
     let mut builder = wallet.build_tx();
     builder.drain_to(addr.script_pubkey()).drain_wallet();
     let mut psbt = builder.finish().unwrap();
@@ -2750,7 +2777,7 @@ fn test_remove_partial_sigs_after_finalize_sign_option() {
     let (mut wallet, _) = get_funded_wallet("wpkh(tprv8ZgxMBicQKsPd3EupYiPRhaMooHKUHJxNsTfYuScep13go8QFfHdtkG9nRkFGb7busX4isf6X9dURGCoKgitaApQ6MupRhZMcELAxTBRJgS/*)");
 
     for remove_partial_sigs in &[true, false] {
-        let addr = wallet.next_unused_address(KeychainKind::External).unwrap();
+        let addr = wallet.next_unused_address(KeychainKind::External);
         let mut builder = wallet.build_tx();
         builder.drain_to(addr.script_pubkey()).drain_wallet();
         let mut psbt = builder.finish().unwrap();
@@ -2780,7 +2807,7 @@ fn test_try_finalize_sign_option() {
     let (mut wallet, _) = get_funded_wallet("wpkh(tprv8ZgxMBicQKsPd3EupYiPRhaMooHKUHJxNsTfYuScep13go8QFfHdtkG9nRkFGb7busX4isf6X9dURGCoKgitaApQ6MupRhZMcELAxTBRJgS/*)");
 
     for try_finalize in &[true, false] {
-        let addr = wallet.next_unused_address(KeychainKind::External).unwrap();
+        let addr = wallet.next_unused_address(KeychainKind::External);
         let mut builder = wallet.build_tx();
         builder.drain_to(addr.script_pubkey()).drain_wallet();
         let mut psbt = builder.finish().unwrap();
@@ -2814,7 +2841,7 @@ fn test_sign_nonstandard_sighash() {
     let sighash = EcdsaSighashType::NonePlusAnyoneCanPay;
 
     let (mut wallet, _) = get_funded_wallet("wpkh(tprv8ZgxMBicQKsPd3EupYiPRhaMooHKUHJxNsTfYuScep13go8QFfHdtkG9nRkFGb7busX4isf6X9dURGCoKgitaApQ6MupRhZMcELAxTBRJgS/*)");
-    let addr = wallet.next_unused_address(KeychainKind::External).unwrap();
+    let addr = wallet.next_unused_address(KeychainKind::External);
     let mut builder = wallet.build_tx();
     builder
         .drain_to(addr.script_pubkey())
@@ -2859,7 +2886,7 @@ fn test_sign_nonstandard_sighash() {
 fn test_unused_address() {
     let desc = "wpkh(tpubEBr4i6yk5nf5DAaJpsi9N2pPYBeJ7fZ5Z9rmN4977iYLCGco1VyjB9tvvuvYtfZzjD5A8igzgw3HeWeeKFmanHYqksqZXYXGsw5zjnj7KM9/*)";
     let change_desc = get_test_wpkh();
-    let mut wallet = Wallet::new_no_persist(desc, change_desc, Network::Testnet).unwrap();
+    let mut wallet = Wallet::new(desc, change_desc, Network::Testnet).expect("wallet");
 
     // `list_unused_addresses` should be empty if we haven't revealed any
     assert!(wallet
@@ -2870,7 +2897,6 @@ fn test_unused_address() {
     assert_eq!(
         wallet
             .next_unused_address(KeychainKind::External)
-            .unwrap()
             .to_string(),
         "tb1q6yn66vajcctph75pvylgkksgpp6nq04ppwct9a"
     );
@@ -2888,13 +2914,12 @@ fn test_unused_address() {
 fn test_next_unused_address() {
     let descriptor = "wpkh(tpubEBr4i6yk5nf5DAaJpsi9N2pPYBeJ7fZ5Z9rmN4977iYLCGco1VyjB9tvvuvYtfZzjD5A8igzgw3HeWeeKFmanHYqksqZXYXGsw5zjnj7KM9/*)";
     let change = get_test_wpkh();
-    let mut wallet = Wallet::new_no_persist(descriptor, change, Network::Testnet).unwrap();
+    let mut wallet = Wallet::new(descriptor, change, Network::Testnet).expect("wallet");
     assert_eq!(wallet.derivation_index(KeychainKind::External), None);
 
     assert_eq!(
         wallet
             .next_unused_address(KeychainKind::External)
-            .unwrap()
             .to_string(),
         "tb1q6yn66vajcctph75pvylgkksgpp6nq04ppwct9a"
     );
@@ -2903,7 +2928,6 @@ fn test_next_unused_address() {
     assert_eq!(
         wallet
             .next_unused_address(KeychainKind::External)
-            .unwrap()
             .to_string(),
         "tb1q6yn66vajcctph75pvylgkksgpp6nq04ppwct9a"
     );
@@ -2911,11 +2935,11 @@ fn test_next_unused_address() {
 
     // test mark used / unused
     assert!(wallet.mark_used(KeychainKind::External, 0));
-    let next_unused_addr = wallet.next_unused_address(KeychainKind::External).unwrap();
+    let next_unused_addr = wallet.next_unused_address(KeychainKind::External);
     assert_eq!(next_unused_addr.index, 1);
 
     assert!(wallet.unmark_used(KeychainKind::External, 0));
-    let next_unused_addr = wallet.next_unused_address(KeychainKind::External).unwrap();
+    let next_unused_addr = wallet.next_unused_address(KeychainKind::External);
     assert_eq!(next_unused_addr.index, 0);
 
     // use the above address
@@ -2924,7 +2948,6 @@ fn test_next_unused_address() {
     assert_eq!(
         wallet
             .next_unused_address(KeychainKind::External)
-            .unwrap()
             .to_string(),
         "tb1q4er7kxx6sssz3q7qp7zsqsdx4erceahhax77d7"
     );
@@ -2938,7 +2961,7 @@ fn test_next_unused_address() {
 fn test_peek_address_at_index() {
     let desc = "wpkh(tpubEBr4i6yk5nf5DAaJpsi9N2pPYBeJ7fZ5Z9rmN4977iYLCGco1VyjB9tvvuvYtfZzjD5A8igzgw3HeWeeKFmanHYqksqZXYXGsw5zjnj7KM9/*)";
     let change_desc = get_test_wpkh();
-    let mut wallet = Wallet::new_no_persist(desc, change_desc, Network::Testnet).unwrap();
+    let mut wallet = Wallet::new(desc, change_desc, Network::Testnet).unwrap();
 
     assert_eq!(
         wallet.peek_address(KeychainKind::External, 1).to_string(),
@@ -2959,7 +2982,6 @@ fn test_peek_address_at_index() {
     assert_eq!(
         wallet
             .reveal_next_address(KeychainKind::External)
-            .unwrap()
             .to_string(),
         "tb1q6yn66vajcctph75pvylgkksgpp6nq04ppwct9a"
     );
@@ -2967,7 +2989,6 @@ fn test_peek_address_at_index() {
     assert_eq!(
         wallet
             .reveal_next_address(KeychainKind::External)
-            .unwrap()
             .to_string(),
         "tb1q4er7kxx6sssz3q7qp7zsqsdx4erceahhax77d7"
     );
@@ -2975,7 +2996,7 @@ fn test_peek_address_at_index() {
 
 #[test]
 fn test_peek_address_at_index_not_derivable() {
-    let wallet = Wallet::new_no_persist("wpkh(tpubEBr4i6yk5nf5DAaJpsi9N2pPYBeJ7fZ5Z9rmN4977iYLCGco1VyjB9tvvuvYtfZzjD5A8igzgw3HeWeeKFmanHYqksqZXYXGsw5zjnj7KM9/1)",
+    let wallet = Wallet::new("wpkh(tpubEBr4i6yk5nf5DAaJpsi9N2pPYBeJ7fZ5Z9rmN4977iYLCGco1VyjB9tvvuvYtfZzjD5A8igzgw3HeWeeKFmanHYqksqZXYXGsw5zjnj7KM9/1)",
                                  get_test_wpkh(), Network::Testnet).unwrap();
 
     assert_eq!(
@@ -2996,12 +3017,12 @@ fn test_peek_address_at_index_not_derivable() {
 
 #[test]
 fn test_returns_index_and_address() {
-    let mut wallet = Wallet::new_no_persist("wpkh(tpubEBr4i6yk5nf5DAaJpsi9N2pPYBeJ7fZ5Z9rmN4977iYLCGco1VyjB9tvvuvYtfZzjD5A8igzgw3HeWeeKFmanHYqksqZXYXGsw5zjnj7KM9/*)",
+    let mut wallet = Wallet::new("wpkh(tpubEBr4i6yk5nf5DAaJpsi9N2pPYBeJ7fZ5Z9rmN4977iYLCGco1VyjB9tvvuvYtfZzjD5A8igzgw3HeWeeKFmanHYqksqZXYXGsw5zjnj7KM9/*)",
                                  get_test_wpkh(), Network::Testnet).unwrap();
 
     // new index 0
     assert_eq!(
-        wallet.reveal_next_address(KeychainKind::External).unwrap(),
+        wallet.reveal_next_address(KeychainKind::External),
         AddressInfo {
             index: 0,
             address: Address::from_str("tb1q6yn66vajcctph75pvylgkksgpp6nq04ppwct9a")
@@ -3013,7 +3034,7 @@ fn test_returns_index_and_address() {
 
     // new index 1
     assert_eq!(
-        wallet.reveal_next_address(KeychainKind::External).unwrap(),
+        wallet.reveal_next_address(KeychainKind::External),
         AddressInfo {
             index: 1,
             address: Address::from_str("tb1q4er7kxx6sssz3q7qp7zsqsdx4erceahhax77d7")
@@ -3037,7 +3058,7 @@ fn test_returns_index_and_address() {
 
     // new index 2
     assert_eq!(
-        wallet.reveal_next_address(KeychainKind::External).unwrap(),
+        wallet.reveal_next_address(KeychainKind::External),
         AddressInfo {
             index: 2,
             address: Address::from_str("tb1qzntf2mqex4ehwkjlfdyy3ewdlk08qkvkvrz7x2")
@@ -3063,7 +3084,7 @@ fn test_sending_to_bip350_bech32m_address() {
 fn test_get_address() {
     use bdk_wallet::descriptor::template::Bip84;
     let key = bitcoin::bip32::Xpriv::from_str("tprv8ZgxMBicQKsPcx5nBGsR63Pe8KnRUqmbJNENAfGftF3yuXoMMoVJJcYeUw5eVkm9WBPjWYt6HMWYJNesB5HaNVBaFc1M6dRjWSYnmewUMYy").unwrap();
-    let wallet = Wallet::new_no_persist(
+    let wallet = Wallet::new(
         Bip84(key, KeychainKind::External),
         Bip84(key, KeychainKind::Internal),
         Network::Regtest,
@@ -3096,14 +3117,10 @@ fn test_get_address() {
 #[test]
 fn test_reveal_addresses() {
     let (desc, change_desc) = get_test_tr_single_sig_xprv_with_change_desc();
-    let mut wallet = Wallet::new_no_persist(desc, change_desc, Network::Signet).unwrap();
+    let mut wallet = Wallet::new(desc, change_desc, Network::Signet).unwrap();
     let keychain = KeychainKind::External;
 
-    let last_revealed_addr = wallet
-        .reveal_addresses_to(keychain, 9)
-        .unwrap()
-        .last()
-        .unwrap();
+    let last_revealed_addr = wallet.reveal_addresses_to(keychain, 9).last().unwrap();
     assert_eq!(wallet.derivation_index(keychain), Some(9));
 
     let unused_addrs = wallet.list_unused_addresses(keychain).collect::<Vec<_>>();
@@ -3111,7 +3128,7 @@ fn test_reveal_addresses() {
     assert_eq!(unused_addrs.last().unwrap(), &last_revealed_addr);
 
     // revealing to an already revealed index returns nothing
-    let mut already_revealed = wallet.reveal_addresses_to(keychain, 9).unwrap();
+    let mut already_revealed = wallet.reveal_addresses_to(keychain, 9);
     assert!(already_revealed.next().is_none());
 }
 
@@ -3121,7 +3138,7 @@ fn test_get_address_no_reuse() {
     use std::collections::HashSet;
 
     let key = bitcoin::bip32::Xpriv::from_str("tprv8ZgxMBicQKsPcx5nBGsR63Pe8KnRUqmbJNENAfGftF3yuXoMMoVJJcYeUw5eVkm9WBPjWYt6HMWYJNesB5HaNVBaFc1M6dRjWSYnmewUMYy").unwrap();
-    let mut wallet = Wallet::new_no_persist(
+    let mut wallet = Wallet::new(
         Bip84(key, KeychainKind::External),
         Bip84(key, KeychainKind::Internal),
         Network::Regtest,
@@ -3131,16 +3148,10 @@ fn test_get_address_no_reuse() {
     let mut used_set = HashSet::new();
 
     (0..3).for_each(|_| {
-        let external_addr = wallet
-            .reveal_next_address(KeychainKind::External)
-            .unwrap()
-            .address;
+        let external_addr = wallet.reveal_next_address(KeychainKind::External).address;
         assert!(used_set.insert(external_addr));
 
-        let internal_addr = wallet
-            .reveal_next_address(KeychainKind::Internal)
-            .unwrap()
-            .address;
+        let internal_addr = wallet.reveal_next_address(KeychainKind::Internal).address;
         assert!(used_set.insert(internal_addr));
     });
 }
@@ -3149,7 +3160,7 @@ fn test_get_address_no_reuse() {
 fn test_taproot_remove_tapfields_after_finalize_sign_option() {
     let (mut wallet, _) = get_funded_wallet(get_test_tr_with_taptree());
 
-    let addr = wallet.next_unused_address(KeychainKind::External).unwrap();
+    let addr = wallet.next_unused_address(KeychainKind::External);
     let mut builder = wallet.build_tx();
     builder.drain_to(addr.script_pubkey()).drain_wallet();
     let mut psbt = builder.finish().unwrap();
@@ -3175,7 +3186,7 @@ fn test_taproot_remove_tapfields_after_finalize_sign_option() {
 fn test_taproot_psbt_populate_tap_key_origins() {
     let (desc, change_desc) = get_test_tr_single_sig_xprv_with_change_desc();
     let (mut wallet, _) = get_funded_wallet_with_change(desc, change_desc);
-    let addr = wallet.reveal_next_address(KeychainKind::External).unwrap();
+    let addr = wallet.reveal_next_address(KeychainKind::External);
 
     let mut builder = wallet.build_tx();
     builder.drain_to(addr.script_pubkey()).drain_wallet();
@@ -3211,7 +3222,7 @@ fn test_taproot_psbt_populate_tap_key_origins() {
 fn test_taproot_psbt_populate_tap_key_origins_repeated_key() {
     let (mut wallet, _) =
         get_funded_wallet_with_change(get_test_tr_repeated_key(), get_test_tr_single_sig());
-    let addr = wallet.reveal_next_address(KeychainKind::External).unwrap();
+    let addr = wallet.reveal_next_address(KeychainKind::External);
 
     let path = vec![("rn4nre9c".to_string(), vec![0])]
         .into_iter()
@@ -3278,7 +3289,7 @@ fn test_taproot_psbt_input_tap_tree() {
     use bitcoin::taproot;
 
     let (mut wallet, _) = get_funded_wallet(get_test_tr_with_taptree());
-    let addr = wallet.next_unused_address(KeychainKind::External).unwrap();
+    let addr = wallet.next_unused_address(KeychainKind::External);
 
     let mut builder = wallet.build_tx();
     builder.drain_to(addr.script_pubkey()).drain_wallet();
@@ -3321,7 +3332,7 @@ fn test_taproot_psbt_input_tap_tree() {
 #[test]
 fn test_taproot_sign_missing_witness_utxo() {
     let (mut wallet, _) = get_funded_wallet(get_test_tr_single_sig());
-    let addr = wallet.next_unused_address(KeychainKind::External).unwrap();
+    let addr = wallet.next_unused_address(KeychainKind::External);
     let mut builder = wallet.build_tx();
     builder.drain_to(addr.script_pubkey()).drain_wallet();
     let mut psbt = builder.finish().unwrap();
@@ -3361,7 +3372,7 @@ fn test_taproot_sign_missing_witness_utxo() {
 #[test]
 fn test_taproot_sign_using_non_witness_utxo() {
     let (mut wallet, prev_txid) = get_funded_wallet(get_test_tr_single_sig());
-    let addr = wallet.next_unused_address(KeychainKind::External).unwrap();
+    let addr = wallet.next_unused_address(KeychainKind::External);
     let mut builder = wallet.build_tx();
     builder.drain_to(addr.script_pubkey()).drain_wallet();
     let mut psbt = builder.finish().unwrap();
@@ -3433,7 +3444,7 @@ fn test_taproot_foreign_utxo() {
 }
 
 fn test_spend_from_wallet(mut wallet: Wallet) {
-    let addr = wallet.next_unused_address(KeychainKind::External).unwrap();
+    let addr = wallet.next_unused_address(KeychainKind::External);
 
     let mut builder = wallet.build_tx();
     builder.add_recipient(addr.script_pubkey(), Amount::from_sat(25_000));
@@ -3457,7 +3468,7 @@ fn test_spend_from_wallet(mut wallet: Wallet) {
 #[test]
 fn test_taproot_no_key_spend() {
     let (mut wallet, _) = get_funded_wallet(get_test_tr_with_taptree_both_priv());
-    let addr = wallet.next_unused_address(KeychainKind::External).unwrap();
+    let addr = wallet.next_unused_address(KeychainKind::External);
 
     let mut builder = wallet.build_tx();
     builder.add_recipient(addr.script_pubkey(), Amount::from_sat(25_000));
@@ -3492,7 +3503,7 @@ fn test_taproot_script_spend() {
 fn test_taproot_script_spend_sign_all_leaves() {
     use bdk_wallet::signer::TapLeavesOptions;
     let (mut wallet, _) = get_funded_wallet(get_test_tr_with_taptree_both_priv());
-    let addr = wallet.next_unused_address(KeychainKind::External).unwrap();
+    let addr = wallet.next_unused_address(KeychainKind::External);
 
     let mut builder = wallet.build_tx();
     builder.add_recipient(addr.script_pubkey(), Amount::from_sat(25_000));
@@ -3523,7 +3534,7 @@ fn test_taproot_script_spend_sign_include_some_leaves() {
     use bitcoin::taproot::TapLeafHash;
 
     let (mut wallet, _) = get_funded_wallet(get_test_tr_with_taptree_both_priv());
-    let addr = wallet.next_unused_address(KeychainKind::External).unwrap();
+    let addr = wallet.next_unused_address(KeychainKind::External);
 
     let mut builder = wallet.build_tx();
     builder.add_recipient(addr.script_pubkey(), Amount::from_sat(25_000));
@@ -3563,7 +3574,7 @@ fn test_taproot_script_spend_sign_exclude_some_leaves() {
     use bitcoin::taproot::TapLeafHash;
 
     let (mut wallet, _) = get_funded_wallet(get_test_tr_with_taptree_both_priv());
-    let addr = wallet.next_unused_address(KeychainKind::External).unwrap();
+    let addr = wallet.next_unused_address(KeychainKind::External);
 
     let mut builder = wallet.build_tx();
     builder.add_recipient(addr.script_pubkey(), Amount::from_sat(25_000));
@@ -3601,7 +3612,7 @@ fn test_taproot_script_spend_sign_exclude_some_leaves() {
 fn test_taproot_script_spend_sign_no_leaves() {
     use bdk_wallet::signer::TapLeavesOptions;
     let (mut wallet, _) = get_funded_wallet(get_test_tr_with_taptree_both_priv());
-    let addr = wallet.next_unused_address(KeychainKind::External).unwrap();
+    let addr = wallet.next_unused_address(KeychainKind::External);
 
     let mut builder = wallet.build_tx();
     builder.add_recipient(addr.script_pubkey(), Amount::from_sat(25_000));
@@ -3624,14 +3635,14 @@ fn test_taproot_script_spend_sign_no_leaves() {
 fn test_taproot_sign_derive_index_from_psbt() {
     let (mut wallet, _) = get_funded_wallet(get_test_tr_single_sig_xprv());
 
-    let addr = wallet.next_unused_address(KeychainKind::External).unwrap();
+    let addr = wallet.next_unused_address(KeychainKind::External);
 
     let mut builder = wallet.build_tx();
     builder.add_recipient(addr.script_pubkey(), Amount::from_sat(25_000));
     let mut psbt = builder.finish().unwrap();
 
     // re-create the wallet with an empty db
-    let wallet_empty = Wallet::new_no_persist(
+    let wallet_empty = Wallet::new(
         get_test_tr_single_sig_xprv(),
         get_test_tr_single_sig(),
         Network::Regtest,
@@ -3649,7 +3660,7 @@ fn test_taproot_sign_derive_index_from_psbt() {
 #[test]
 fn test_taproot_sign_explicit_sighash_all() {
     let (mut wallet, _) = get_funded_wallet(get_test_tr_single_sig());
-    let addr = wallet.next_unused_address(KeychainKind::External).unwrap();
+    let addr = wallet.next_unused_address(KeychainKind::External);
     let mut builder = wallet.build_tx();
     builder
         .drain_to(addr.script_pubkey())
@@ -3669,7 +3680,7 @@ fn test_taproot_sign_non_default_sighash() {
     let sighash = TapSighashType::NonePlusAnyoneCanPay;
 
     let (mut wallet, _) = get_funded_wallet(get_test_tr_single_sig());
-    let addr = wallet.next_unused_address(KeychainKind::External).unwrap();
+    let addr = wallet.next_unused_address(KeychainKind::External);
     let mut builder = wallet.build_tx();
     builder
         .drain_to(addr.script_pubkey())
@@ -3736,7 +3747,7 @@ fn test_taproot_sign_non_default_sighash() {
 #[test]
 fn test_spend_coinbase() {
     let (desc, change_desc) = get_test_wpkh_with_change_desc();
-    let mut wallet = Wallet::new_no_persist(desc, change_desc, Network::Regtest).unwrap();
+    let mut wallet = Wallet::new(desc, change_desc, Network::Regtest).unwrap();
 
     let confirmation_height = 5;
     wallet
@@ -3755,7 +3766,6 @@ fn test_spend_coinbase() {
         output: vec![TxOut {
             script_pubkey: wallet
                 .next_unused_address(KeychainKind::External)
-                .unwrap()
                 .script_pubkey(),
             value: Amount::from_sat(25_000),
         }],
@@ -3845,7 +3855,7 @@ fn test_spend_coinbase() {
 fn test_allow_dust_limit() {
     let (mut wallet, _) = get_funded_wallet(get_test_single_sig_cltv());
 
-    let addr = wallet.next_unused_address(KeychainKind::External).unwrap();
+    let addr = wallet.next_unused_address(KeychainKind::External);
 
     let mut builder = wallet.build_tx();
 
@@ -3871,7 +3881,7 @@ fn test_fee_rate_sign_no_grinding_high_r() {
     // instead of 70). We then check that our fee rate and fee calculation is
     // alright.
     let (mut wallet, _) = get_funded_wallet("wpkh(tprv8ZgxMBicQKsPd3EupYiPRhaMooHKUHJxNsTfYuScep13go8QFfHdtkG9nRkFGb7busX4isf6X9dURGCoKgitaApQ6MupRhZMcELAxTBRJgS/*)");
-    let addr = wallet.next_unused_address(KeychainKind::External).unwrap();
+    let addr = wallet.next_unused_address(KeychainKind::External);
     let fee_rate = FeeRate::from_sat_per_vb_unchecked(1);
     let mut builder = wallet.build_tx();
     let mut data = PushBytesBuf::try_from(vec![0]).unwrap();
@@ -3940,7 +3950,7 @@ fn test_fee_rate_sign_grinding_low_r() {
     // We then check that our fee rate and fee calculation is alright and that our
     // signature is 70 bytes.
     let (mut wallet, _) = get_funded_wallet("wpkh(tprv8ZgxMBicQKsPd3EupYiPRhaMooHKUHJxNsTfYuScep13go8QFfHdtkG9nRkFGb7busX4isf6X9dURGCoKgitaApQ6MupRhZMcELAxTBRJgS/*)");
-    let addr = wallet.next_unused_address(KeychainKind::External).unwrap();
+    let addr = wallet.next_unused_address(KeychainKind::External);
     let fee_rate = FeeRate::from_sat_per_vb_unchecked(1);
     let mut builder = wallet.build_tx();
     builder
index be9e1839f48654a10dd27e3f2604c7a026d32f22..55ff6f007e79409d5dbf334c16592530816dc8ea 100644 (file)
@@ -11,6 +11,7 @@ use bdk_bitcoind_rpc::{
     bitcoincore_rpc::{Auth, Client, RpcApi},
     Emitter,
 };
+use bdk_chain::persist::PersistBackend;
 use bdk_chain::{
     bitcoin::{constants::genesis_block, Block, Transaction},
     indexed_tx_graph, keychain,
@@ -137,8 +138,7 @@ fn main() -> anyhow::Result<()> {
         let genesis_hash = genesis_block(args.network).block_hash();
         let (chain, chain_changeset) = LocalChain::from_genesis_hash(genesis_hash);
         let mut db = db.lock().unwrap();
-        db.stage((chain_changeset, Default::default()));
-        db.commit()?;
+        db.write_changes(&(chain_changeset, Default::default()))?;
         chain
     } else {
         LocalChain::from_changeset(init_chain_changeset)?
@@ -191,12 +191,11 @@ fn main() -> anyhow::Result<()> {
                     .apply_update(emission.checkpoint)
                     .expect("must always apply as we receive blocks in order from emitter");
                 let graph_changeset = graph.apply_block_relevant(&emission.block, height);
-                db.stage((chain_changeset, graph_changeset));
+                db.write_changes(&(chain_changeset, graph_changeset))?;
 
                 // commit staged db changes in intervals
                 if last_db_commit.elapsed() >= DB_COMMIT_DELAY {
                     last_db_commit = Instant::now();
-                    db.commit()?;
                     println!(
                         "[{:>10}s] committed to db (took {}s)",
                         start.elapsed().as_secs_f32(),
@@ -232,8 +231,7 @@ fn main() -> anyhow::Result<()> {
             );
             {
                 let mut db = db.lock().unwrap();
-                db.stage((local_chain::ChangeSet::default(), graph_changeset));
-                db.commit()?; // commit one last time
+                db.write_changes(&(local_chain::ChangeSet::default(), graph_changeset))?;
             }
         }
         RpcCommands::Live { rpc_args } => {
@@ -317,11 +315,10 @@ fn main() -> anyhow::Result<()> {
                     }
                 };
 
-                db.stage(changeset);
+                db.write_changes(&changeset)?;
 
                 if last_db_commit.elapsed() >= DB_COMMIT_DELAY {
                     last_db_commit = Instant::now();
-                    db.commit()?;
                     println!(
                         "[{:>10}s] committed to db (took {}s)",
                         start.elapsed().as_secs_f32(),
index 42a0b51b0906d275bd4ddb8e2d5a396f29988ee9..c85d2e996310b0fc8e3fc28347e12cc867cce433 100644 (file)
@@ -7,7 +7,6 @@ edition = "2021"
 
 [dependencies]
 bdk_chain = { path = "../../crates/chain", features = ["serde", "miniscript"]}
-bdk_persist = { path = "../../crates/persist" }
 bdk_file_store = { path = "../../crates/file_store" }
 bdk_tmp_plan = { path = "../../nursery/tmp_plan" }
 bdk_coin_select = { path = "../../nursery/coin_select" }
index 3c69a506f11808e965476c657ae0b1b4b4ccddc9..6a3f432018eed09d318318e88e87561a23792ddc 100644 (file)
@@ -3,6 +3,7 @@ use anyhow::Context;
 use bdk_coin_select::{coin_select_bnb, CoinSelector, CoinSelectorOpt, WeightedValue};
 use bdk_file_store::Store;
 use serde::{de::DeserializeOwned, Serialize};
+use std::fmt::Debug;
 use std::{cmp::Reverse, collections::BTreeMap, path::PathBuf, sync::Mutex, time::Duration};
 
 use bdk_chain::{
@@ -22,9 +23,9 @@ use bdk_chain::{
     Anchor, Append, ChainOracle, DescriptorExt, FullTxOut,
 };
 pub use bdk_file_store;
-use bdk_persist::{Persist, PersistBackend};
 pub use clap;
 
+use bdk_chain::persist::PersistBackend;
 use clap::{Parser, Subcommand};
 
 pub type KeychainTxGraph<A> = IndexedTxGraph<A, KeychainTxOutIndex<Keychain>>;
@@ -446,7 +447,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<Persist<C>>,
+    db: &Mutex<Store<C>>,
     chain: &Mutex<O>,
     keymap: &BTreeMap<DescriptorPublicKey, DescriptorSecretKey>,
     network: Network,
@@ -455,7 +456,14 @@ pub fn handle_commands<CS: clap::Subcommand, S: clap::Args, A: Anchor, O: ChainO
 ) -> anyhow::Result<()>
 where
     O::Error: std::error::Error + Send + Sync + 'static,
-    C: Default + Append + DeserializeOwned + Serialize + From<KeychainChangeSet<A>>,
+    C: Default
+        + Append
+        + DeserializeOwned
+        + Serialize
+        + From<KeychainChangeSet<A>>
+        + Send
+        + Sync
+        + Debug,
 {
     match cmd {
         Commands::ChainSpecific(_) => unreachable!("example code should handle this!"),
@@ -474,7 +482,7 @@ where
                     let ((spk_i, spk), index_changeset) =
                         spk_chooser(index, &Keychain::External).expect("Must exist");
                     let db = &mut *db.lock().unwrap();
-                    db.stage_and_commit(C::from((
+                    db.write_changes(&C::from((
                         local_chain::ChangeSet::default(),
                         indexed_tx_graph::ChangeSet::from(index_changeset),
                     )))?;
@@ -622,7 +630,7 @@ where
                     // If we're unable to persist this, then we don't want to broadcast.
                     {
                         let db = &mut *db.lock().unwrap();
-                        db.stage_and_commit(C::from((
+                        db.write_changes(&C::from((
                             local_chain::ChangeSet::default(),
                             indexed_tx_graph::ChangeSet::from(index_changeset),
                         )))?;
@@ -647,7 +655,7 @@ where
                     // We know the tx is at least unconfirmed now. Note if persisting here fails,
                     // it's not a big deal since we can always find it again form
                     // blockchain.
-                    db.lock().unwrap().stage_and_commit(C::from((
+                    db.lock().unwrap().write_changes(&C::from((
                         local_chain::ChangeSet::default(),
                         keychain_changeset,
                     )))?;
@@ -666,7 +674,10 @@ where
 }
 
 /// The initial state returned by [`init`].
-pub struct Init<CS: clap::Subcommand, S: clap::Args, C> {
+pub struct Init<CS: clap::Subcommand, S: clap::Args, C>
+where
+    C: Default + Append + Serialize + DeserializeOwned + Debug + Send + Sync + 'static,
+{
     /// Arguments parsed by the cli.
     pub args: Args<CS, S>,
     /// Descriptor keymap.
@@ -674,7 +685,7 @@ pub struct Init<CS: clap::Subcommand, S: clap::Args, C> {
     /// Keychain-txout index.
     pub index: KeychainTxOutIndex<Keychain>,
     /// Persistence backend.
-    pub db: Mutex<Persist<C>>,
+    pub db: Mutex<Store<C>>,
     /// Initial changeset.
     pub init_changeset: C,
 }
@@ -690,6 +701,7 @@ where
         + Append
         + Serialize
         + DeserializeOwned
+        + Debug
         + core::marker::Send
         + core::marker::Sync
         + 'static,
@@ -724,13 +736,13 @@ where
         Err(err) => return Err(anyhow::anyhow!("failed to init db backend: {:?}", err)),
     };
 
-    let init_changeset = db_backend.load_from_persistence()?.unwrap_or_default();
+    let init_changeset = db_backend.load_changes()?.unwrap_or_default();
 
     Ok(Init {
         args,
         keymap,
         index,
-        db: Mutex::new(Persist::new(db_backend)),
+        db: Mutex::new(db_backend),
         init_changeset,
     })
 }
index 38443e14c433f500e70025a25bb8b0b64cb62b28..2b77e97c4927792c14610a0900794ba22c06ad3a 100644 (file)
@@ -3,6 +3,7 @@ use std::{
     sync::Mutex,
 };
 
+use bdk_chain::persist::PersistBackend;
 use bdk_chain::{
     bitcoin::{constants::genesis_block, Address, Network, Txid},
     collections::BTreeSet,
@@ -351,7 +352,6 @@ fn main() -> anyhow::Result<()> {
     };
 
     let mut db = db.lock().unwrap();
-    db.stage(db_changeset);
-    db.commit()?;
+    db.write_changes(&db_changeset)?;
     Ok(())
 }
index 3e605a037f48abd06fbf4068368777fe74d57435..13e6e6abb3bd4484655e4ab6c236fe9d082dfc7e 100644 (file)
@@ -4,6 +4,7 @@ use std::{
     sync::Mutex,
 };
 
+use bdk_chain::persist::PersistBackend;
 use bdk_chain::{
     bitcoin::{constants::genesis_block, Address, Network, Txid},
     indexed_tx_graph::{self, IndexedTxGraph},
@@ -361,7 +362,6 @@ fn main() -> anyhow::Result<()> {
 
     // We persist the changes
     let mut db = db.lock().unwrap();
-    db.stage((local_chain_changeset, indexed_tx_graph_changeset));
-    db.commit()?;
+    db.write_changes(&(local_chain_changeset, indexed_tx_graph_changeset))?;
     Ok(())
 }
index d8dbc458f7440c003e9f11e21f3c1db13db8e113..f634466fa1757e7588bd8da6363136346a84a5fc 100644 (file)
@@ -3,6 +3,7 @@ const SEND_AMOUNT: Amount = Amount::from_sat(5000);
 const STOP_GAP: usize = 50;
 const BATCH_SIZE: usize = 5;
 
+use anyhow::anyhow;
 use std::io::Write;
 use std::str::FromStr;
 
@@ -11,24 +12,28 @@ use bdk_electrum::BdkElectrumClient;
 use bdk_file_store::Store;
 use bdk_wallet::bitcoin::{Address, Amount};
 use bdk_wallet::chain::collections::HashSet;
+use bdk_wallet::chain::persist::PersistBackend;
 use bdk_wallet::{bitcoin::Network, Wallet};
 use bdk_wallet::{KeychainKind, SignOptions};
 
 fn main() -> Result<(), anyhow::Error> {
     let db_path = std::env::temp_dir().join("bdk-electrum-example");
-    let db =
+    let mut db =
         Store::<bdk_wallet::wallet::ChangeSet>::open_or_create_new(DB_MAGIC.as_bytes(), db_path)?;
     let external_descriptor = "wpkh(tprv8ZgxMBicQKsPdy6LMhUtFHAgpocR8GC6QmwMSFpZs7h6Eziw3SpThFfczTDh5rW2krkqffa11UpX3XkeTTB2FvzZKWXqPY54Y6Rq4AQ5R8L/84'/1'/0'/0/*)";
     let internal_descriptor = "wpkh(tprv8ZgxMBicQKsPdy6LMhUtFHAgpocR8GC6QmwMSFpZs7h6Eziw3SpThFfczTDh5rW2krkqffa11UpX3XkeTTB2FvzZKWXqPY54Y6Rq4AQ5R8L/84'/1'/0'/1/*)";
-
+    let changeset = db
+        .load_changes()
+        .map_err(|e| anyhow!("load changes error: {}", e))?;
     let mut wallet = Wallet::new_or_load(
         external_descriptor,
         internal_descriptor,
-        db,
+        changeset,
         Network::Testnet,
     )?;
 
-    let address = wallet.next_unused_address(KeychainKind::External)?;
+    let address = wallet.next_unused_address(KeychainKind::External);
+    db.write_changes(&wallet.take_staged())?;
     println!("Generated Address: {}", address);
 
     let balance = wallet.balance();
@@ -67,7 +72,7 @@ fn main() -> Result<(), anyhow::Error> {
     println!();
 
     wallet.apply_update(update)?;
-    wallet.commit()?;
+    db.write_changes(&wallet.take_staged())?;
 
     let balance = wallet.balance();
     println!("Wallet balance after syncing: {} sats", balance.total());
index 6510c5660e5551876d2b0bd5176553486332ccb8..c3e4244cdb74b8cd5fc1a00f897d79a265b61d62 100644 (file)
@@ -7,6 +7,7 @@ use bdk_wallet::{
 };
 
 use bdk_sqlite::{rusqlite::Connection, Store};
+use bdk_wallet::chain::persist::PersistBackend;
 
 const SEND_AMOUNT: Amount = Amount::from_sat(5000);
 const STOP_GAP: usize = 50;
@@ -16,18 +17,20 @@ const PARALLEL_REQUESTS: usize = 5;
 async fn main() -> Result<(), anyhow::Error> {
     let db_path = "bdk-esplora-async-example.sqlite";
     let conn = Connection::open(db_path)?;
-    let db = Store::new(conn)?;
+    let mut db = Store::new(conn)?;
     let external_descriptor = "wpkh(tprv8ZgxMBicQKsPdy6LMhUtFHAgpocR8GC6QmwMSFpZs7h6Eziw3SpThFfczTDh5rW2krkqffa11UpX3XkeTTB2FvzZKWXqPY54Y6Rq4AQ5R8L/84'/1'/0'/0/*)";
     let internal_descriptor = "wpkh(tprv8ZgxMBicQKsPdy6LMhUtFHAgpocR8GC6QmwMSFpZs7h6Eziw3SpThFfczTDh5rW2krkqffa11UpX3XkeTTB2FvzZKWXqPY54Y6Rq4AQ5R8L/84'/1'/0'/1/*)";
+    let changeset = db.load_changes()?;
 
     let mut wallet = Wallet::new_or_load(
         external_descriptor,
         internal_descriptor,
-        db,
+        changeset,
         Network::Signet,
     )?;
 
-    let address = wallet.next_unused_address(KeychainKind::External)?;
+    let address = wallet.next_unused_address(KeychainKind::External);
+    db.write_changes(&wallet.take_staged())?;
     println!("Generated Address: {}", address);
 
     let balance = wallet.balance();
@@ -75,7 +78,7 @@ async fn main() -> Result<(), anyhow::Error> {
     let _ = update.graph_update.update_last_seen_unconfirmed(now);
 
     wallet.apply_update(update)?;
-    wallet.commit()?;
+    db.write_changes(&wallet.take_staged())?;
     println!();
 
     let balance = wallet.balance();
index 5ade6804e0ffede6229a644a21286327ab861e5e..3b431465a091c000d8bbfb5652f33c17a7f79b52 100644 (file)
@@ -7,6 +7,7 @@ use std::{collections::BTreeSet, io::Write, str::FromStr};
 
 use bdk_esplora::{esplora_client, EsploraExt};
 use bdk_file_store::Store;
+use bdk_wallet::chain::persist::PersistBackend;
 use bdk_wallet::{
     bitcoin::{Address, Amount, Network},
     KeychainKind, SignOptions, Wallet,
@@ -14,19 +15,21 @@ use bdk_wallet::{
 
 fn main() -> Result<(), anyhow::Error> {
     let db_path = std::env::temp_dir().join("bdk-esplora-example");
-    let db =
+    let mut db =
         Store::<bdk_wallet::wallet::ChangeSet>::open_or_create_new(DB_MAGIC.as_bytes(), db_path)?;
     let external_descriptor = "wpkh(tprv8ZgxMBicQKsPdy6LMhUtFHAgpocR8GC6QmwMSFpZs7h6Eziw3SpThFfczTDh5rW2krkqffa11UpX3XkeTTB2FvzZKWXqPY54Y6Rq4AQ5R8L/84'/1'/0'/0/*)";
     let internal_descriptor = "wpkh(tprv8ZgxMBicQKsPdy6LMhUtFHAgpocR8GC6QmwMSFpZs7h6Eziw3SpThFfczTDh5rW2krkqffa11UpX3XkeTTB2FvzZKWXqPY54Y6Rq4AQ5R8L/84'/1'/0'/1/*)";
+    let changeset = db.load_changes()?;
 
     let mut wallet = Wallet::new_or_load(
         external_descriptor,
         internal_descriptor,
-        db,
+        changeset,
         Network::Testnet,
     )?;
 
-    let address = wallet.next_unused_address(KeychainKind::External)?;
+    let address = wallet.next_unused_address(KeychainKind::External);
+    db.write_changes(&wallet.take_staged())?;
     println!("Generated Address: {}", address);
 
     let balance = wallet.balance();
@@ -52,7 +55,7 @@ fn main() -> Result<(), anyhow::Error> {
     let _ = update.graph_update.update_last_seen_unconfirmed(now);
 
     wallet.apply_update(update)?;
-    wallet.commit()?;
+    db.write_changes(&wallet.take_staged())?;
     println!();
 
     let balance = wallet.balance();
index a64f0539e92a84749706a436dafa0f930ff6676b..f5d12f8a607ef5d0a127abc8e88123387af04560 100644 (file)
@@ -3,6 +3,7 @@ use bdk_bitcoind_rpc::{
     Emitter,
 };
 use bdk_file_store::Store;
+use bdk_wallet::chain::persist::PersistBackend;
 use bdk_wallet::{
     bitcoin::{Block, Network, Transaction},
     wallet::Wallet,
@@ -86,13 +87,16 @@ fn main() -> anyhow::Result<()> {
     );
 
     let start_load_wallet = Instant::now();
+    let mut db = Store::<bdk_wallet::wallet::ChangeSet>::open_or_create_new(
+        DB_MAGIC.as_bytes(),
+        args.db_path,
+    )?;
+    let changeset = db.load_changes()?;
+
     let mut wallet = Wallet::new_or_load(
         &args.descriptor,
         &args.change_descriptor,
-        Store::<bdk_wallet::wallet::ChangeSet>::open_or_create_new(
-            DB_MAGIC.as_bytes(),
-            args.db_path,
-        )?,
+        changeset,
         args.network,
     )?;
     println!(
@@ -143,7 +147,7 @@ fn main() -> anyhow::Result<()> {
                 let connected_to = block_emission.connected_to();
                 let start_apply_block = Instant::now();
                 wallet.apply_block_connected_to(&block_emission.block, height, connected_to)?;
-                wallet.commit()?;
+                db.write_changes(&wallet.take_staged())?;
                 let elapsed = start_apply_block.elapsed().as_secs_f32();
                 println!(
                     "Applied block {} at height {} in {}s",
@@ -153,7 +157,7 @@ fn main() -> anyhow::Result<()> {
             Emission::Mempool(mempool_emission) => {
                 let start_apply_mempool = Instant::now();
                 wallet.apply_unconfirmed_txs(mempool_emission.iter().map(|(tx, time)| (tx, *time)));
-                wallet.commit()?;
+                db.write_changes(&wallet.take_staged())?;
                 println!(
                     "Applied unconfirmed transactions in {}s",
                     start_apply_mempool.elapsed().as_secs_f32()