]> Untitled Git - bdk/commitdiff
test(wallet): ensure checks work when loading wallet
author志宇 <hello@evanlinjin.me>
Thu, 18 Jul 2024 06:47:34 +0000 (06:47 +0000)
committer志宇 <hello@evanlinjin.me>
Thu, 18 Jul 2024 06:47:34 +0000 (06:47 +0000)
crates/chain/src/persist.rs
crates/wallet/src/descriptor/error.rs
crates/wallet/src/keys/mod.rs
crates/wallet/src/wallet/mod.rs
crates/wallet/src/wallet/persisted.rs
crates/wallet/tests/wallet.rs

index cdaf6d5e6bd3ac61c5163fad6b8798c09e92371c..5f7b37e4c8a34d04a5d829dd34f1ff6abdca2fce 100644 (file)
@@ -57,6 +57,7 @@ pub trait PersistAsyncWith<Db>: Sized {
 }
 
 /// Represents a persisted `T`.
+#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
 pub struct Persisted<T> {
     inner: T,
 }
index f83fb24d23a44a742998eba5bab85e23fabf6594..e018b5352d63f9d1ba0e40330ca24862983294de 100644 (file)
@@ -13,7 +13,7 @@
 use core::fmt;
 
 /// Errors related to the parsing and usage of descriptors
-#[derive(Debug)]
+#[derive(Debug, PartialEq)]
 pub enum Error {
     /// Invalid HD Key path, such as having a wildcard but a length != 1
     InvalidHdKeyPath,
index 907deb7ba360a66b21ef238d3f5212f5f8b3027c..d9c241ff0ec0dc3db82f89b466ae68b561c4321d 100644 (file)
@@ -935,7 +935,7 @@ impl<Ctx: ScriptContext> IntoDescriptorKey<Ctx> for PrivateKey {
 }
 
 /// Errors thrown while working with [`keys`](crate::keys)
-#[derive(Debug)]
+#[derive(Debug, PartialEq)]
 pub enum KeyError {
     /// The key cannot exist in the given script context
     InvalidScriptContext,
index 71468e4e5cd1e1eb840feeeac2d2c89b12d39dc7..d2d7c0ad04746a759aaa0778e642dc6791c668b0 100644 (file)
@@ -184,7 +184,7 @@ impl fmt::Display for AddressInfo {
 }
 
 /// The error type when loading a [`Wallet`] from a [`ChangeSet`].
-#[derive(Debug)]
+#[derive(Debug, PartialEq)]
 pub enum LoadError {
     /// There was a problem with the passed-in descriptor(s).
     Descriptor(crate::descriptor::DescriptorError),
@@ -216,7 +216,7 @@ impl fmt::Display for LoadError {
 impl std::error::Error for LoadError {}
 
 /// Represents a mismatch with what is loaded and what is expected from [`LoadParams`].
-#[derive(Debug)]
+#[derive(Debug, PartialEq)]
 pub enum LoadMismatch {
     /// Network does not match.
     Network {
@@ -243,6 +243,18 @@ pub enum LoadMismatch {
     },
 }
 
+impl From<LoadMismatch> for LoadError {
+    fn from(mismatch: LoadMismatch) -> Self {
+        Self::Mismatch(mismatch)
+    }
+}
+
+impl<E> From<LoadMismatch> for LoadWithPersistError<E> {
+    fn from(mismatch: LoadMismatch) -> Self {
+        Self::InvalidChangeSet(LoadError::Mismatch(mismatch))
+    }
+}
+
 /// An error that may occur when applying a block to [`Wallet`].
 #[derive(Debug)]
 pub enum ApplyBlockError {
index 1a0450e26d2eab5d8e82b0b5a137f04775a5fe81..70d9f27cceae49f250c1ad1a31a592e8032f3124 100644 (file)
@@ -138,7 +138,7 @@ impl chain::PersistWith<bdk_file_store::Store<crate::ChangeSet>> for Wallet {
 }
 
 /// Error type for [`PersistedWallet::load`].
-#[derive(Debug)]
+#[derive(Debug, PartialEq)]
 pub enum LoadWithPersistError<E> {
     /// Error from persistence.
     Persist(E),
index 94d4b07e1e16645ee4151c67614bcfc048184285..5a709f1c6303110fae054e49a6634778ad8aa0e5 100644 (file)
@@ -13,8 +13,9 @@ use bdk_wallet::error::CreateTxError;
 use bdk_wallet::psbt::PsbtUtils;
 use bdk_wallet::signer::{SignOptions, SignerError};
 use bdk_wallet::tx_builder::AddForeignUtxoError;
-use bdk_wallet::KeychainKind;
 use bdk_wallet::{AddressInfo, Balance, CreateParams, LoadParams, Wallet};
+use bdk_wallet::{KeychainKind, LoadError, LoadMismatch, LoadWithPersistError};
+use bitcoin::constants::ChainHash;
 use bitcoin::hashes::Hash;
 use bitcoin::key::Secp256k1;
 use bitcoin::psbt;
@@ -177,6 +178,93 @@ fn wallet_is_persisted() -> anyhow::Result<()> {
     Ok(())
 }
 
+#[test]
+fn wallet_load_checks() -> anyhow::Result<()> {
+    fn run<Db, CreateDb, OpenDb, LoadDbError>(
+        filename: &str,
+        create_db: CreateDb,
+        open_db: OpenDb,
+    ) -> anyhow::Result<()>
+    where
+        CreateDb: Fn(&Path) -> anyhow::Result<Db>,
+        OpenDb: Fn(&Path) -> anyhow::Result<Db>,
+        Wallet: PersistWith<
+            Db,
+            CreateParams = CreateParams,
+            LoadParams = LoadParams,
+            LoadError = LoadWithPersistError<LoadDbError>,
+        >,
+        <Wallet as PersistWith<Db>>::CreateError: std::error::Error + Send + Sync + 'static,
+        <Wallet as PersistWith<Db>>::LoadError: std::error::Error + Send + Sync + 'static,
+        <Wallet as PersistWith<Db>>::PersistError: std::error::Error + Send + Sync + 'static,
+    {
+        let temp_dir = tempfile::tempdir().expect("must create tempdir");
+        let file_path = temp_dir.path().join(filename);
+        let network = Network::Testnet;
+        let (external_desc, internal_desc) = get_test_tr_single_sig_xprv_with_change_desc();
+
+        // create new wallet
+        let _ = Wallet::create(external_desc, internal_desc)
+            .network(network)
+            .create_wallet(&mut create_db(&file_path)?)?;
+
+        assert_matches!(
+            Wallet::load()
+                .network(Network::Regtest)
+                .load_wallet(&mut open_db(&file_path)?),
+            Err(LoadWithPersistError::InvalidChangeSet(LoadError::Mismatch(
+                LoadMismatch::Network {
+                    loaded: Network::Testnet,
+                    expected: Network::Regtest,
+                }
+            ))),
+            "unexpected network check result: Regtest (check) is not Testnet (loaded)",
+        );
+        assert_matches!(
+            Wallet::load()
+                .network(Network::Bitcoin)
+                .load_wallet(&mut open_db(&file_path)?),
+            Err(LoadWithPersistError::InvalidChangeSet(LoadError::Mismatch(
+                LoadMismatch::Network {
+                    loaded: Network::Testnet,
+                    expected: Network::Bitcoin,
+                }
+            ))),
+            "unexpected network check result: Bitcoin (check) is not Testnet (loaded)",
+        );
+        let mainnet_hash = BlockHash::from_byte_array(ChainHash::BITCOIN.to_bytes());
+        assert_matches!(
+            Wallet::load().genesis_hash(mainnet_hash).load_wallet(&mut open_db(&file_path)?),
+            Err(LoadWithPersistError::InvalidChangeSet(LoadError::Mismatch(LoadMismatch::Genesis { .. }))),
+            "unexpected genesis hash check result: mainnet hash (check) is not testnet hash (loaded)",
+        );
+        assert_matches!(
+            Wallet::load()
+                .descriptors(internal_desc, external_desc)
+                .load_wallet(&mut open_db(&file_path)?),
+            Err(LoadWithPersistError::InvalidChangeSet(LoadError::Mismatch(
+                LoadMismatch::Descriptor { .. }
+            ))),
+            "unexpected descriptors check result",
+        );
+
+        Ok(())
+    }
+
+    run(
+        "store.db",
+        |path| Ok(bdk_file_store::Store::create_new(DB_MAGIC, path)?),
+        |path| Ok(bdk_file_store::Store::open(DB_MAGIC, path)?),
+    )?;
+    run(
+        "store.sqlite",
+        |path| Ok(bdk_chain::sqlite::Connection::open(path)?),
+        |path| Ok(bdk_chain::sqlite::Connection::open(path)?),
+    )?;
+
+    Ok(())
+}
+
 #[test]
 fn test_error_external_and_internal_are_the_same() {
     // identical descriptors should fail to create wallet