diff --git a/crates/wallet/src/descriptor/error.rs b/crates/wallet/src/descriptor/error.rs index e018b5352..b157c33ec 100644 --- a/crates/wallet/src/descriptor/error.rs +++ b/crates/wallet/src/descriptor/error.rs @@ -43,6 +43,8 @@ pub enum Error { Hex(bitcoin::hex::HexToBytesError), /// The provided wallet descriptors are identical ExternalAndInternalAreTheSame, + /// The provided parameters mismatch + Mismatch(MismatchError), } impl From for Error { @@ -83,6 +85,7 @@ impl fmt::Display for Error { Self::ExternalAndInternalAreTheSame => { write!(f, "External and internal descriptors are the same") } + Self::Mismatch(mismatch) => write!(f, "The provided parameters mismatch: {mismatch:?}"), } } } @@ -125,3 +128,15 @@ impl From for Error { Error::Policy(err) } } + +#[derive(Debug, PartialEq)] +/// Represents a mismatch within the parameters that passed as [`crate::wallet::CreateParams`]. +pub enum MismatchError { + /// Genesis hash does not match. + Genesis { + /// The genesis hash for the given network parameter. + network: bitcoin::BlockHash, + /// The genesis hash given as parameter. + parameter: bitcoin::BlockHash, + }, +} diff --git a/crates/wallet/src/wallet/mod.rs b/crates/wallet/src/wallet/mod.rs index 3af424d37..cf97c6200 100644 --- a/crates/wallet/src/wallet/mod.rs +++ b/crates/wallet/src/wallet/mod.rs @@ -59,7 +59,6 @@ pub mod signer; pub mod tx_builder; pub(crate) mod utils; -use crate::collections::{BTreeMap, HashMap, HashSet}; use crate::descriptor::{ check_wallet_descriptor, error::Error as DescriptorError, policy::BuildSatisfaction, DerivedDescriptor, DescriptorMeta, ExtendedDescriptor, ExtractPolicy, IntoWalletDescriptor, @@ -74,6 +73,10 @@ use crate::wallet::{ tx_builder::{FeePolicy, TxBuilder, TxParams}, utils::{check_nsequence_rbf, After, Older, SecpCtx}, }; +use crate::{ + collections::{BTreeMap, HashMap, HashSet}, + descriptor::error::MismatchError, +}; // re-exports pub use bdk_chain::Balance; @@ -372,6 +375,14 @@ impl Wallet { let genesis_hash = params .genesis_hash .unwrap_or(genesis_block(network).block_hash()); + + if genesis_hash.ne(&genesis_block(network).block_hash()) { + return Err(DescriptorError::Mismatch(MismatchError::Genesis { + network: genesis_block(network).block_hash(), + parameter: genesis_hash, + })); + } + let (chain, chain_changeset) = LocalChain::from_genesis_hash(genesis_hash); let (descriptor, mut descriptor_keymap) = (params.descriptor)(&secp, network)?; @@ -506,6 +517,14 @@ impl Wallet { expected: exp_genesis_hash, })); } + + let network_genesis_hash = genesis_block(network).block_hash(); + if network_genesis_hash != exp_genesis_hash { + return Err(LoadError::Mismatch(LoadMismatch::Genesis { + loaded: network_genesis_hash, + expected: exp_genesis_hash, + })); + } } let descriptor = changeset diff --git a/crates/wallet/tests/wallet.rs b/crates/wallet/tests/wallet.rs index afb346905..fe09a4049 100644 --- a/crates/wallet/tests/wallet.rs +++ b/crates/wallet/tests/wallet.rs @@ -6,6 +6,7 @@ use anyhow::Context; use assert_matches::assert_matches; use bdk_chain::{BlockId, ChainPosition, ConfirmationBlockTime}; use bdk_wallet::coin_selection::{self, LargestFirstCoinSelection}; +use bdk_wallet::descriptor::error::MismatchError; use bdk_wallet::descriptor::{calc_checksum, DescriptorError, IntoWalletDescriptor}; use bdk_wallet::error::CreateTxError; use bdk_wallet::psbt::PsbtUtils; @@ -355,6 +356,24 @@ fn test_error_external_and_internal_are_the_same() { ); } +#[test] +fn test_create_genesis_hash_and_network_consistency() { + let (external_desc, internal_desc) = get_test_tr_single_sig_xprv_and_change_desc(); + let mainnet_genesis_hash = BlockHash::from_byte_array(ChainHash::BITCOIN.to_bytes()); + let err = Wallet::create(external_desc, internal_desc) + .genesis_hash(mainnet_genesis_hash) + .network(Network::Regtest) + .create_wallet_no_persist(); + + assert!( + matches!( + &err, + Err(DescriptorError::Mismatch(MismatchError::Genesis { .. })) + ), + "unexpected network and genesis_hash parameter check result: `genesis_hash` network (Mainnet) does not match network parameter (Regtest)", + ); +} + #[test] fn test_descriptor_checksum() { let (wallet, _) = get_funded_wallet_wpkh();