Skip to content

Commit

Permalink
test(wallet): ensure checks work when loading wallet
Browse files Browse the repository at this point in the history
  • Loading branch information
evanlinjin committed Jul 18, 2024
1 parent af4ee0f commit 3aed4cf
Show file tree
Hide file tree
Showing 6 changed files with 107 additions and 6 deletions.
1 change: 1 addition & 0 deletions crates/chain/src/persist.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
}
Expand Down
2 changes: 1 addition & 1 deletion crates/wallet/src/descriptor/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
2 changes: 1 addition & 1 deletion crates/wallet/src/keys/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
16 changes: 14 additions & 2 deletions crates/wallet/src/wallet/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down Expand Up @@ -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 {
Expand All @@ -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 {
Expand Down
2 changes: 1 addition & 1 deletion crates/wallet/src/wallet/persisted.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down
90 changes: 89 additions & 1 deletion crates/wallet/tests/wallet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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
Expand Down

0 comments on commit 3aed4cf

Please sign in to comment.