From 22d02ed3d19e763d1ff54e3ec3657ed39c4ad66c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BF=97=E5=AE=87?= Date: Thu, 18 Jul 2024 03:04:00 +0000 Subject: [PATCH] feat!: improve wallet building methods Remove returning `Result` for builder methods on `CreateParams` and `LoadParams`. --- crates/hwi/src/lib.rs | 5 +- crates/wallet/README.md | 18 ++- crates/wallet/examples/compiler.rs | 5 +- crates/wallet/src/descriptor/mod.rs | 10 ++ crates/wallet/src/descriptor/template.rs | 111 +++++++------ crates/wallet/src/wallet/export.rs | 22 +-- crates/wallet/src/wallet/mod.rs | 147 +++++++----------- crates/wallet/src/wallet/params.rs | 112 +++++++------ crates/wallet/src/wallet/signer.rs | 3 +- crates/wallet/tests/common.rs | 4 +- crates/wallet/tests/wallet.rs | 87 +++++------ example-crates/wallet_electrum/src/main.rs | 15 +- .../wallet_esplora_async/src/main.rs | 14 +- .../wallet_esplora_blocking/src/main.rs | 15 +- example-crates/wallet_rpc/src/main.rs | 16 +- 15 files changed, 287 insertions(+), 297 deletions(-) diff --git a/crates/hwi/src/lib.rs b/crates/hwi/src/lib.rs index 80649112..7bb7e4ca 100644 --- a/crates/hwi/src/lib.rs +++ b/crates/hwi/src/lib.rs @@ -7,7 +7,7 @@ //! # use bdk_wallet::descriptor::Descriptor; //! # use bdk_wallet::signer::SignerOrdering; //! # use bdk_hwi::HWISigner; -//! # use bdk_wallet::{KeychainKind, SignOptions}; +//! # use bdk_wallet::{KeychainKind, SignOptions, Wallet}; //! # use hwi::HWIClient; //! # use std::sync::Arc; //! # use std::str::FromStr; @@ -20,8 +20,7 @@ //! let first_device = devices.remove(0)?; //! let custom_signer = HWISigner::from_device(&first_device, Network::Testnet.into())?; //! -//! # let mut wallet = bdk_wallet::CreateParams::new("", "", Network::Testnet)? -//! # .create_wallet_no_persist()?; +//! # let mut wallet = Wallet::create("", "").network(Network::Testnet).create_wallet_no_persist()?; //! # //! // Adding the hardware signer to the BDK wallet //! wallet.add_signer( diff --git a/crates/wallet/README.md b/crates/wallet/README.md index 423cf2e7..9dee46aa 100644 --- a/crates/wallet/README.md +++ b/crates/wallet/README.md @@ -67,7 +67,7 @@ To persist `Wallet` state data use a data store crate that reads and writes [`Ch ```rust,no_run -use bdk_wallet::{bitcoin::Network, CreateParams, LoadParams, KeychainKind, ChangeSet}; +use bdk_wallet::{bitcoin::Network, KeychainKind, ChangeSet, Wallet}; // Open or create a new file store for wallet data. let mut db = @@ -78,13 +78,17 @@ let mut db = let network = Network::Testnet; let descriptor = "wpkh(tprv8ZgxMBicQKsPdcAqYBpzAFwU5yxBUo88ggoBqu1qPcHUfSbKK1sKMLmC7EAk438btHQrSdu3jGGQa6PA71nvH5nkDexhLteJqkM4dQmWF9g/84'/1'/0'/0/*)"; let change_descriptor = "wpkh(tprv8ZgxMBicQKsPdcAqYBpzAFwU5yxBUo88ggoBqu1qPcHUfSbKK1sKMLmC7EAk438btHQrSdu3jGGQa6PA71nvH5nkDexhLteJqkM4dQmWF9g/84'/1'/0'/1/*)"; -let load_params = LoadParams::with_descriptors(descriptor, change_descriptor, network) - .expect("must parse descriptors"); -let create_params = CreateParams::new(descriptor, change_descriptor, network) - .expect("must parse descriptors"); -let mut wallet = match load_params.load_wallet(&mut db).expect("wallet") { +let wallet_opt = Wallet::load() + .descriptors(descriptor, change_descriptor) + .network(network) + .load_wallet(&mut db) + .expect("wallet"); +let mut wallet = match wallet_opt { Some(wallet) => wallet, - None => create_params.create_wallet(&mut db).expect("wallet"), + None => Wallet::create(descriptor, change_descriptor) + .network(network) + .create_wallet(&mut db) + .expect("wallet"), }; // Get a new address to receive bitcoin. diff --git a/crates/wallet/examples/compiler.rs b/crates/wallet/examples/compiler.rs index 23102d8b..d3f1391e 100644 --- a/crates/wallet/examples/compiler.rs +++ b/crates/wallet/examples/compiler.rs @@ -21,7 +21,7 @@ use bitcoin::Network; use miniscript::policy::Concrete; use miniscript::Descriptor; -use bdk_wallet::{CreateParams, KeychainKind}; +use bdk_wallet::{KeychainKind, Wallet}; /// Miniscript policy is a high level abstraction of spending conditions. Defined in the /// rust-miniscript library here https://docs.rs/miniscript/7.0.0/miniscript/policy/index.html @@ -77,7 +77,8 @@ fn main() -> Result<(), Box> { ); // Create a new wallet from descriptors - let mut wallet = CreateParams::new(&descriptor, &internal_descriptor, Network::Regtest)? + let mut wallet = Wallet::create(descriptor, internal_descriptor) + .network(Network::Regtest) .create_wallet_no_persist()?; println!( diff --git a/crates/wallet/src/descriptor/mod.rs b/crates/wallet/src/descriptor/mod.rs index e196c2f8..477b265e 100644 --- a/crates/wallet/src/descriptor/mod.rs +++ b/crates/wallet/src/descriptor/mod.rs @@ -112,6 +112,16 @@ impl IntoWalletDescriptor for &String { } } +impl IntoWalletDescriptor for String { + fn into_wallet_descriptor( + self, + secp: &SecpCtx, + network: Network, + ) -> Result<(ExtendedDescriptor, KeyMap), DescriptorError> { + self.as_str().into_wallet_descriptor(secp, network) + } +} + impl IntoWalletDescriptor for ExtendedDescriptor { fn into_wallet_descriptor( self, diff --git a/crates/wallet/src/descriptor/template.rs b/crates/wallet/src/descriptor/template.rs index a7f72668..ee9ec9ae 100644 --- a/crates/wallet/src/descriptor/template.rs +++ b/crates/wallet/src/descriptor/template.rs @@ -73,7 +73,7 @@ impl IntoWalletDescriptor for T { /// /// ``` /// # use bdk_wallet::bitcoin::{PrivateKey, Network}; -/// # use bdk_wallet::CreateParams; +/// # use bdk_wallet::Wallet; /// # use bdk_wallet::KeychainKind; /// use bdk_wallet::template::P2Pkh; /// @@ -81,7 +81,8 @@ impl IntoWalletDescriptor for T { /// bitcoin::PrivateKey::from_wif("cTc4vURSzdx6QE6KVynWGomDbLaA75dNALMNyfjh3p8DRRar84Um")?; /// let key_internal = /// bitcoin::PrivateKey::from_wif("cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW")?; -/// let mut wallet = CreateParams::new(P2Pkh(key_external), P2Pkh(key_internal), Network::Testnet)? +/// let mut wallet = Wallet::create(P2Pkh(key_external), P2Pkh(key_internal)) +/// .network(Network::Testnet) /// .create_wallet_no_persist()?; /// /// assert_eq!( @@ -92,6 +93,7 @@ impl IntoWalletDescriptor for T { /// ); /// # Ok::<_, Box>(()) /// ``` +#[derive(Debug, Clone)] pub struct P2Pkh>(pub K); impl> DescriptorTemplate for P2Pkh { @@ -106,7 +108,7 @@ impl> DescriptorTemplate for P2Pkh { /// /// ``` /// # use bdk_wallet::bitcoin::{PrivateKey, Network}; -/// # use bdk_wallet::CreateParams; +/// # use bdk_wallet::Wallet; /// # use bdk_wallet::KeychainKind; /// use bdk_wallet::template::P2Wpkh_P2Sh; /// @@ -114,12 +116,9 @@ impl> DescriptorTemplate for P2Pkh { /// bitcoin::PrivateKey::from_wif("cTc4vURSzdx6QE6KVynWGomDbLaA75dNALMNyfjh3p8DRRar84Um")?; /// let key_internal = /// bitcoin::PrivateKey::from_wif("cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW")?; -/// let mut wallet = CreateParams::new( -/// P2Wpkh_P2Sh(key_external), -/// P2Wpkh_P2Sh(key_internal), -/// Network::Testnet, -/// )? -/// .create_wallet_no_persist()?; +/// let mut wallet = Wallet::create(P2Wpkh_P2Sh(key_external), P2Wpkh_P2Sh(key_internal)) +/// .network(Network::Testnet) +/// .create_wallet_no_persist()?; /// /// assert_eq!( /// wallet @@ -130,6 +129,7 @@ impl> DescriptorTemplate for P2Pkh { /// # Ok::<_, Box>(()) /// ``` #[allow(non_camel_case_types)] +#[derive(Debug, Clone)] pub struct P2Wpkh_P2Sh>(pub K); impl> DescriptorTemplate for P2Wpkh_P2Sh { @@ -144,7 +144,7 @@ impl> DescriptorTemplate for P2Wpkh_P2Sh { /// /// ``` /// # use bdk_wallet::bitcoin::{PrivateKey, Network}; -/// # use bdk_wallet::CreateParams; +/// # use bdk_wallet::Wallet; /// # use bdk_wallet::KeychainKind; /// use bdk_wallet::template::P2Wpkh; /// @@ -152,9 +152,9 @@ impl> DescriptorTemplate for P2Wpkh_P2Sh { /// bitcoin::PrivateKey::from_wif("cTc4vURSzdx6QE6KVynWGomDbLaA75dNALMNyfjh3p8DRRar84Um")?; /// let key_internal = /// bitcoin::PrivateKey::from_wif("cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW")?; -/// let mut wallet = -/// CreateParams::new(P2Wpkh(key_external), P2Wpkh(key_internal), Network::Testnet)? -/// .create_wallet_no_persist()?; +/// let mut wallet = Wallet::create(P2Wpkh(key_external), P2Wpkh(key_internal)) +/// .network(Network::Testnet) +/// .create_wallet_no_persist()?; /// /// assert_eq!( /// wallet @@ -164,6 +164,7 @@ impl> DescriptorTemplate for P2Wpkh_P2Sh { /// ); /// # Ok::<_, Box>(()) /// ``` +#[derive(Debug, Clone)] pub struct P2Wpkh>(pub K); impl> DescriptorTemplate for P2Wpkh { @@ -178,7 +179,7 @@ impl> DescriptorTemplate for P2Wpkh { /// /// ``` /// # use bdk_wallet::bitcoin::{PrivateKey, Network}; -/// # use bdk_wallet::CreateParams; +/// # use bdk_wallet::Wallet; /// # use bdk_wallet::KeychainKind; /// use bdk_wallet::template::P2TR; /// @@ -186,7 +187,8 @@ impl> DescriptorTemplate for P2Wpkh { /// bitcoin::PrivateKey::from_wif("cTc4vURSzdx6QE6KVynWGomDbLaA75dNALMNyfjh3p8DRRar84Um")?; /// let key_internal = /// bitcoin::PrivateKey::from_wif("cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW")?; -/// let mut wallet = CreateParams::new(P2TR(key_external), P2TR(key_internal), Network::Testnet)? +/// let mut wallet = Wallet::create(P2TR(key_external), P2TR(key_internal)) +/// .network(Network::Testnet) /// .create_wallet_no_persist()?; /// /// assert_eq!( @@ -197,6 +199,7 @@ impl> DescriptorTemplate for P2Wpkh { /// ); /// # Ok::<_, Box>(()) /// ``` +#[derive(Debug, Clone)] pub struct P2TR>(pub K); impl> DescriptorTemplate for P2TR { @@ -213,24 +216,22 @@ impl> DescriptorTemplate for P2TR { /// /// ## Example /// -/// ``` +/// ```rust /// # use std::str::FromStr; /// # use bdk_wallet::bitcoin::{PrivateKey, Network}; -/// # use bdk_wallet::{CreateParams, KeychainKind}; +/// # use bdk_wallet::{Wallet, KeychainKind}; /// use bdk_wallet::template::Bip44; /// /// let key = bitcoin::bip32::Xpriv::from_str("tprv8ZgxMBicQKsPeZRHk4rTG6orPS2CRNFX3njhUXx5vj9qGog5ZMH4uGReDWN5kCkY3jmWEtWause41CDvBRXD1shKknAMKxT99o9qUTRVC6m")?; -/// let mut wallet = CreateParams::new( -/// Bip44(key.clone(), KeychainKind::External), -/// Bip44(key, KeychainKind::Internal), -/// Network::Testnet, -/// )? -/// .create_wallet_no_persist()?; +/// let mut wallet = Wallet::create(Bip44(key.clone(), KeychainKind::External), Bip44(key, KeychainKind::Internal)) +/// .network(Network::Testnet) +/// .create_wallet_no_persist()?; /// /// 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>(()) /// ``` +#[derive(Debug, Clone)] pub struct Bip44>(pub K, pub KeychainKind); impl> DescriptorTemplate for Bip44 { @@ -253,22 +254,23 @@ impl> DescriptorTemplate for Bip44 { /// ``` /// # use std::str::FromStr; /// # use bdk_wallet::bitcoin::{PrivateKey, Network}; -/// # use bdk_wallet::{CreateParams, KeychainKind}; +/// # use bdk_wallet::{KeychainKind, Wallet}; /// use bdk_wallet::template::Bip44Public; /// /// let key = bitcoin::bip32::Xpub::from_str("tpubDDDzQ31JkZB7VxUr9bjvBivDdqoFLrDPyLWtLapArAi51ftfmCb2DPxwLQzX65iNcXz1DGaVvyvo6JQ6rTU73r2gqdEo8uov9QKRb7nKCSU")?; /// let fingerprint = bitcoin::bip32::Fingerprint::from_str("c55b303f")?; -/// let mut wallet = CreateParams::new( +/// let mut wallet = Wallet::create( /// Bip44Public(key.clone(), fingerprint, KeychainKind::External), /// Bip44Public(key, fingerprint, KeychainKind::Internal), -/// Network::Testnet, -/// )? +/// ) +/// .network(Network::Testnet) /// .create_wallet_no_persist()?; /// /// 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>(()) /// ``` +#[derive(Debug, Clone)] pub struct Bip44Public>(pub K, pub bip32::Fingerprint, pub KeychainKind); impl> DescriptorTemplate for Bip44Public { @@ -291,21 +293,22 @@ impl> DescriptorTemplate for Bip44Public { /// ``` /// # use std::str::FromStr; /// # use bdk_wallet::bitcoin::{PrivateKey, Network}; -/// # use bdk_wallet::{CreateParams, KeychainKind}; +/// # use bdk_wallet::{Wallet, KeychainKind}; /// use bdk_wallet::template::Bip49; /// /// let key = bitcoin::bip32::Xpriv::from_str("tprv8ZgxMBicQKsPeZRHk4rTG6orPS2CRNFX3njhUXx5vj9qGog5ZMH4uGReDWN5kCkY3jmWEtWause41CDvBRXD1shKknAMKxT99o9qUTRVC6m")?; -/// let mut wallet = CreateParams::new( +/// let mut wallet = Wallet::create( /// Bip49(key.clone(), KeychainKind::External), /// Bip49(key, KeychainKind::Internal), -/// Network::Testnet, -/// )? +/// ) +/// .network(Network::Testnet) /// .create_wallet_no_persist()?; /// /// 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>(()) /// ``` +#[derive(Debug, Clone)] pub struct Bip49>(pub K, pub KeychainKind); impl> DescriptorTemplate for Bip49 { @@ -328,22 +331,23 @@ impl> DescriptorTemplate for Bip49 { /// ``` /// # use std::str::FromStr; /// # use bdk_wallet::bitcoin::{PrivateKey, Network}; -/// # use bdk_wallet::{CreateParams, KeychainKind}; +/// # use bdk_wallet::{Wallet, KeychainKind}; /// use bdk_wallet::template::Bip49Public; /// /// let key = bitcoin::bip32::Xpub::from_str("tpubDC49r947KGK52X5rBWS4BLs5m9SRY3pYHnvRrm7HcybZ3BfdEsGFyzCMzayi1u58eT82ZeyFZwH7DD6Q83E3fM9CpfMtmnTygnLfP59jL9L")?; /// let fingerprint = bitcoin::bip32::Fingerprint::from_str("c55b303f")?; -/// let mut wallet = CreateParams::new( +/// let mut wallet = Wallet::create( /// Bip49Public(key.clone(), fingerprint, KeychainKind::External), /// Bip49Public(key, fingerprint, KeychainKind::Internal), -/// Network::Testnet, -/// )? +/// ) +/// .network(Network::Testnet) /// .create_wallet_no_persist()?; /// /// 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>(()) /// ``` +#[derive(Debug, Clone)] pub struct Bip49Public>(pub K, pub bip32::Fingerprint, pub KeychainKind); impl> DescriptorTemplate for Bip49Public { @@ -366,21 +370,22 @@ impl> DescriptorTemplate for Bip49Public { /// ``` /// # use std::str::FromStr; /// # use bdk_wallet::bitcoin::{PrivateKey, Network}; -/// # use bdk_wallet::{CreateParams, KeychainKind}; +/// # use bdk_wallet::{Wallet, KeychainKind}; /// use bdk_wallet::template::Bip84; /// /// let key = bitcoin::bip32::Xpriv::from_str("tprv8ZgxMBicQKsPeZRHk4rTG6orPS2CRNFX3njhUXx5vj9qGog5ZMH4uGReDWN5kCkY3jmWEtWause41CDvBRXD1shKknAMKxT99o9qUTRVC6m")?; -/// let mut wallet = CreateParams::new( +/// let mut wallet = Wallet::create( /// Bip84(key.clone(), KeychainKind::External), /// Bip84(key, KeychainKind::Internal), -/// Network::Testnet, -/// )? +/// ) +/// .network(Network::Testnet) /// .create_wallet_no_persist()?; /// /// 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>(()) /// ``` +#[derive(Debug, Clone)] pub struct Bip84>(pub K, pub KeychainKind); impl> DescriptorTemplate for Bip84 { @@ -403,21 +408,23 @@ impl> DescriptorTemplate for Bip84 { /// ``` /// # use std::str::FromStr; /// # use bdk_wallet::bitcoin::{PrivateKey, Network}; -/// # use bdk_wallet::{CreateParams, KeychainKind}; +/// # use bdk_wallet::{Wallet, KeychainKind}; /// use bdk_wallet::template::Bip84Public; /// /// let key = bitcoin::bip32::Xpub::from_str("tpubDC2Qwo2TFsaNC4ju8nrUJ9mqVT3eSgdmy1yPqhgkjwmke3PRXutNGRYAUo6RCHTcVQaDR3ohNU9we59brGHuEKPvH1ags2nevW5opEE9Z5Q")?; /// let fingerprint = bitcoin::bip32::Fingerprint::from_str("c55b303f")?; -/// let mut wallet = CreateParams::new( +/// let mut wallet = Wallet::create( /// Bip84Public(key.clone(), fingerprint, KeychainKind::External), /// Bip84Public(key, fingerprint, KeychainKind::Internal), -/// Network::Testnet, -/// )?.create_wallet_no_persist()?; +/// ) +/// .network(Network::Testnet) +/// .create_wallet_no_persist()?; /// /// 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>(()) /// ``` +#[derive(Debug, Clone)] pub struct Bip84Public>(pub K, pub bip32::Fingerprint, pub KeychainKind); impl> DescriptorTemplate for Bip84Public { @@ -440,21 +447,22 @@ impl> DescriptorTemplate for Bip84Public { /// ``` /// # use std::str::FromStr; /// # use bdk_wallet::bitcoin::{PrivateKey, Network}; -/// # use bdk_wallet::{CreateParams, KeychainKind}; +/// # use bdk_wallet::{Wallet, KeychainKind}; /// use bdk_wallet::template::Bip86; /// /// let key = bitcoin::bip32::Xpriv::from_str("tprv8ZgxMBicQKsPeZRHk4rTG6orPS2CRNFX3njhUXx5vj9qGog5ZMH4uGReDWN5kCkY3jmWEtWause41CDvBRXD1shKknAMKxT99o9qUTRVC6m")?; -/// let mut wallet = CreateParams::new( +/// let mut wallet = Wallet::create( /// Bip86(key.clone(), KeychainKind::External), /// Bip86(key, KeychainKind::Internal), -/// Network::Testnet, -/// )? +/// ) +/// .network(Network::Testnet) /// .create_wallet_no_persist()?; /// /// 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>(()) /// ``` +#[derive(Debug, Clone)] pub struct Bip86>(pub K, pub KeychainKind); impl> DescriptorTemplate for Bip86 { @@ -477,22 +485,23 @@ impl> DescriptorTemplate for Bip86 { /// ``` /// # use std::str::FromStr; /// # use bdk_wallet::bitcoin::{PrivateKey, Network}; -/// # use bdk_wallet::{CreateParams, KeychainKind}; +/// # use bdk_wallet::{Wallet, KeychainKind}; /// use bdk_wallet::template::Bip86Public; /// /// let key = bitcoin::bip32::Xpub::from_str("tpubDC2Qwo2TFsaNC4ju8nrUJ9mqVT3eSgdmy1yPqhgkjwmke3PRXutNGRYAUo6RCHTcVQaDR3ohNU9we59brGHuEKPvH1ags2nevW5opEE9Z5Q")?; /// let fingerprint = bitcoin::bip32::Fingerprint::from_str("c55b303f")?; -/// let mut wallet = CreateParams::new( +/// let mut wallet = Wallet::create( /// Bip86Public(key.clone(), fingerprint, KeychainKind::External), /// Bip86Public(key, fingerprint, KeychainKind::Internal), -/// Network::Testnet, -/// )? +/// ) +/// .network(Network::Testnet) /// .create_wallet_no_persist()?; /// /// 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>(()) /// ``` +#[derive(Debug, Clone)] pub struct Bip86Public>(pub K, pub bip32::Fingerprint, pub KeychainKind); impl> DescriptorTemplate for Bip86Public { diff --git a/crates/wallet/src/wallet/export.rs b/crates/wallet/src/wallet/export.rs index 8e4d8a4e..6dce7503 100644 --- a/crates/wallet/src/wallet/export.rs +++ b/crates/wallet/src/wallet/export.rs @@ -29,11 +29,11 @@ //! }"#; //! //! let import = FullyNodedExport::from_str(import)?; -//! let wallet = CreateParams::new( -//! &import.descriptor(), -//! &import.change_descriptor().expect("change descriptor"), -//! Network::Testnet, -//! )? +//! let wallet = Wallet::create( +//! import.descriptor(), +//! import.change_descriptor().expect("change descriptor"), +//! ) +//! .network(Network::Testnet) //! .create_wallet_no_persist()?; //! # Ok::<_, Box>(()) //! ``` @@ -43,11 +43,11 @@ //! # use bitcoin::*; //! # use bdk_wallet::export::*; //! # use bdk_wallet::*; -//! let wallet = CreateParams::new( +//! let wallet = Wallet::create( //! "wpkh([c258d2e4/84h/1h/0h]tpubDD3ynpHgJQW8VvWRzQ5WFDCrs4jqVFGHB3vLC3r49XHJSqP8bHKdK4AriuUKLccK68zfzowx7YhmDN8SiSkgCDENUFx9qVw65YyqM78vyVe/0/*)", //! "wpkh([c258d2e4/84h/1h/0h]tpubDD3ynpHgJQW8VvWRzQ5WFDCrs4jqVFGHB3vLC3r49XHJSqP8bHKdK4AriuUKLccK68zfzowx7YhmDN8SiSkgCDENUFx9qVw65YyqM78vyVe/1/*)", -//! Network::Testnet, -//! )? +//! ) +//! .network(Network::Testnet) //! .create_wallet_no_persist()?; //! let export = FullyNodedExport::export_wallet(&wallet, "exported wallet", true).unwrap(); //! @@ -221,13 +221,13 @@ mod test { use bitcoin::{transaction, BlockHash, Network, Transaction}; use super::*; - use crate::wallet::{CreateParams, Wallet}; + use crate::Wallet; fn get_test_wallet(descriptor: &str, change_descriptor: &str, network: Network) -> Wallet { use crate::wallet::Update; use bdk_chain::TxGraph; - let mut wallet = CreateParams::new(descriptor, change_descriptor, network) - .expect("must parse descriptors") + let mut wallet = Wallet::create(descriptor.to_string(), change_descriptor.to_string()) + .network(network) .create_wallet_no_persist() .expect("must create wallet"); let transaction = Transaction { diff --git a/crates/wallet/src/wallet/mod.rs b/crates/wallet/src/wallet/mod.rs index 30fcb3c6..71468e4e 100644 --- a/crates/wallet/src/wallet/mod.rs +++ b/crates/wallet/src/wallet/mod.rs @@ -290,7 +290,8 @@ impl Wallet { /// # const EXTERNAL_DESC: &str = "wpkh(tprv8ZgxMBicQKsPdy6LMhUtFHAgpocR8GC6QmwMSFpZs7h6Eziw3SpThFfczTDh5rW2krkqffa11UpX3XkeTTB2FvzZKWXqPY54Y6Rq4AQ5R8L/84'/1'/0'/0/*)"; /// # const INTERNAL_DESC: &str = "wpkh(tprv8ZgxMBicQKsPdy6LMhUtFHAgpocR8GC6QmwMSFpZs7h6Eziw3SpThFfczTDh5rW2krkqffa11UpX3XkeTTB2FvzZKWXqPY54Y6Rq4AQ5R8L/84'/1'/0'/1/*)"; /// // Create a non-persisted wallet. - /// let wallet = Wallet::create(EXTERNAL_DESC, INTERNAL_DESC, Network::Testnet)? + /// let wallet = Wallet::create(EXTERNAL_DESC, INTERNAL_DESC) + /// .network(Network::Testnet) /// .create_wallet_no_persist()?; /// /// // Create a wallet that is persisted to SQLite database. @@ -298,48 +299,48 @@ impl Wallet { /// # let file_path = temp_dir.path().join("store.db"); /// use bdk_wallet::rusqlite::Connection; /// let mut conn = Connection::open(file_path)?; - /// let wallet = Wallet::create(EXTERNAL_DESC, INTERNAL_DESC, Network::Testnet)? + /// let wallet = Wallet::create(EXTERNAL_DESC, INTERNAL_DESC) + /// .network(Network::Testnet) /// .create_wallet(&mut conn)?; /// # Ok(()) /// # } /// ``` - pub fn create( - descriptor: E, - change_descriptor: E, - network: Network, - ) -> Result { - CreateParams::new(descriptor, change_descriptor, network) + pub fn create(descriptor: D, change_descriptor: D) -> CreateParams + where + D: IntoWalletDescriptor + Clone + 'static, + { + CreateParams::new(descriptor, change_descriptor) } /// Create a new [`Wallet`] with given `params`. /// - /// If you have previously created a wallet, use [`load`](Self::load) instead. + /// Refer to [`Wallet::create`] for more. pub fn create_with_params(params: CreateParams) -> Result { - let secp = params.secp; + let secp = SecpCtx::new(); let network = params.network; let genesis_hash = params .genesis_hash .unwrap_or(genesis_block(network).block_hash()); - let (chain, chain_changeset) = LocalChain::from_genesis_hash(genesis_hash); - check_wallet_descriptor(¶ms.descriptor)?; - check_wallet_descriptor(¶ms.change_descriptor)?; + let (descriptor, mut descriptor_keymap) = (params.descriptor)(&secp, network)?; + descriptor_keymap.extend(params.descriptor_keymap); + + let (change_descriptor, mut change_descriptor_keymap) = + (params.change_descriptor)(&secp, network)?; + change_descriptor_keymap.extend(params.change_descriptor_keymap); + let signers = Arc::new(SignersContainer::build( - params.descriptor_keymap, - ¶ms.descriptor, + descriptor_keymap, + &descriptor, &secp, )); let change_signers = Arc::new(SignersContainer::build( - params.change_descriptor_keymap, - ¶ms.change_descriptor, + change_descriptor_keymap, + &change_descriptor, &secp, )); - let index = create_indexer( - params.descriptor, - params.change_descriptor, - params.lookahead, - )?; + let index = create_indexer(descriptor, change_descriptor, params.lookahead)?; let descriptor = index.get_descriptor(&KeychainKind::External).cloned(); let change_descriptor = index.get_descriptor(&KeychainKind::Internal).cloned(); @@ -370,7 +371,8 @@ impl Wallet { /// /// Note that the descriptor secret keys are not persisted to the db. You can either add /// signers after-the-fact with [`Wallet::add_signer`] or [`Wallet::set_keymap`]. Or you can - /// construct wallet using [`Wallet::load_with_descriptors`]. + /// add keys when building the wallet using [`LoadParams::keymap`] and/or + /// [`LoadParams::descriptors`]. /// /// # Synopsis /// @@ -394,14 +396,15 @@ impl Wallet { /// # let genesis_hash = BlockHash::all_zeros(); /// let mut conn = bdk_wallet::rusqlite::Connection::open(file_path)?; /// let mut wallet = Wallet::load() - /// // manually include private keys - /// // the alternative is to use `Wallet::load_with_descriptors` + /// // check loaded descriptors matches these values and extract private keys + /// .descriptors(EXTERNAL_DESC, INTERNAL_DESC) + /// // you can also manually add private keys /// .keymap(KeychainKind::External, external_keymap) /// .keymap(KeychainKind::Internal, internal_keymap) - /// // set a lookahead for our indexer - /// .lookahead(101) /// // ensure loaded wallet's genesis hash matches this value /// .genesis_hash(genesis_hash) + /// // set a lookahead for our indexer + /// .lookahead(101) /// .load_wallet(&mut conn)? /// .expect("must have data to load wallet"); /// # Ok(()) @@ -411,59 +414,9 @@ impl Wallet { LoadParams::new() } - /// Build [`Wallet`] by loading from persistence or [`ChangeSet`]. This fails if the loaded - /// wallet has a different `network`. + /// Load [`Wallet`] from the given previously persisted [`ChangeSet`] and `params`. /// - /// Note that the descriptor secret keys are not persisted to the db. You can either add - /// signers after-the-fact with [`Wallet::add_signer`] or [`Wallet::set_keymap`]. Or you can - /// construct wallet using [`Wallet::load_with_descriptors`]. - pub fn load_with_network(network: Network) -> LoadParams { - LoadParams::with_network(network) - } - - /// Build [`Wallet`] by loading from persistence or [`ChangeSet`]. This fails if the loaded - /// wallet has a different `network`, `descriptor` or `change_descriptor`. - /// - /// If the passed-in descriptors contains secret keys, the keys will be included in the - /// constructed wallet (which means you can sign transactions). - pub fn load_with_descriptors( - descriptor: E, - change_descriptor: E, - network: Network, - ) -> Result { - LoadParams::with_descriptors(descriptor, change_descriptor, network) - } - - /// 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 - /// able to sign transactions. - /// - /// If you wish to use the wallet to sign transactions, you need to add the secret keys - /// manually to the [`Wallet`]: - /// - /// ```rust,no_run - /// # use bdk_wallet::Wallet; - /// # use bitcoin::Network; - /// # use bdk_wallet::{LoadParams, KeychainKind, PersistedWallet}; - /// use bdk_chain::sqlite::Connection; - /// # - /// # fn main() -> anyhow::Result<()> { - /// # let temp_dir = tempfile::tempdir().expect("must create tempdir"); - /// # let file_path = temp_dir.path().join("store.db"); - /// const EXTERNAL_DESC: &str = "wpkh(tprv8ZgxMBicQKsPdy6LMhUtFHAgpocR8GC6QmwMSFpZs7h6Eziw3SpThFfczTDh5rW2krkqffa11UpX3XkeTTB2FvzZKWXqPY54Y6Rq4AQ5R8L/84'/1'/0'/0/*)"; - /// const INTERNAL_DESC: &str = "wpkh(tprv8ZgxMBicQKsPdy6LMhUtFHAgpocR8GC6QmwMSFpZs7h6Eziw3SpThFfczTDh5rW2krkqffa11UpX3XkeTTB2FvzZKWXqPY54Y6Rq4AQ5R8L/84'/1'/0'/1/*)"; - /// - /// let mut conn = Connection::open(file_path)?; - /// let mut wallet: PersistedWallet = - /// LoadParams::with_descriptors(EXTERNAL_DESC, INTERNAL_DESC, Network::Testnet)? - /// .load_wallet(&mut conn)? - /// .expect("db should have data to load wallet"); - /// - /// # Ok(()) - /// # } - /// ``` + /// Refer to [`Wallet::load`] for more. pub fn load_with_params( changeset: ChangeSet, params: LoadParams, @@ -476,13 +429,16 @@ impl Wallet { let chain = LocalChain::from_changeset(changeset.local_chain) .map_err(|_| LoadError::MissingGenesis)?; + let mut descriptor_keymap = params.descriptor_keymap; let descriptor = changeset .descriptor .ok_or(LoadError::MissingDescriptor(KeychainKind::External))?; + check_wallet_descriptor(&descriptor).map_err(LoadError::Descriptor)?; + + let mut change_descriptor_keymap = params.change_descriptor_keymap; let change_descriptor = changeset .change_descriptor .ok_or(LoadError::MissingDescriptor(KeychainKind::Internal))?; - check_wallet_descriptor(&descriptor).map_err(LoadError::Descriptor)?; check_wallet_descriptor(&change_descriptor).map_err(LoadError::Descriptor)?; // checks @@ -503,6 +459,10 @@ impl Wallet { } } if let Some(exp_descriptor) = params.check_descriptor { + let (exp_descriptor, keymap) = + (exp_descriptor)(&secp, network).map_err(LoadError::Descriptor)?; + descriptor_keymap.extend(keymap); + if descriptor.descriptor_id() != exp_descriptor.descriptor_id() { return Err(LoadError::Mismatch(LoadMismatch::Descriptor { keychain: KeychainKind::External, @@ -512,6 +472,10 @@ impl Wallet { } } if let Some(exp_change_descriptor) = params.check_change_descriptor { + let (exp_change_descriptor, keymap) = + (exp_change_descriptor)(&secp, network).map_err(LoadError::Descriptor)?; + change_descriptor_keymap.extend(keymap); + if change_descriptor.descriptor_id() != exp_change_descriptor.descriptor_id() { return Err(LoadError::Mismatch(LoadMismatch::Descriptor { keychain: KeychainKind::External, @@ -522,12 +486,12 @@ impl Wallet { } let signers = Arc::new(SignersContainer::build( - params.descriptor_keymap, + descriptor_keymap, &descriptor, &secp, )); let change_signers = Arc::new(SignersContainer::build( - params.change_descriptor_keymap, + change_descriptor_keymap, &change_descriptor, &secp, )); @@ -1092,11 +1056,12 @@ impl Wallet { /// ## Example /// /// ``` - /// # use bdk_wallet::{CreateParams, KeychainKind}; + /// # use bdk_wallet::{Wallet, KeychainKind}; /// # use bdk_wallet::bitcoin::Network; /// let descriptor = "wpkh(tprv8ZgxMBicQKsPe73PBRSmNbTfbcsZnwWhz5eVmhHpi31HW29Z7mc9B4cWGRQzopNUzZUT391DeDJxL2PefNunWyLgqCKRMDkU1s2s8bAfoSk/84'/1'/0'/0/*)"; /// let change_descriptor = "wpkh(tprv8ZgxMBicQKsPe73PBRSmNbTfbcsZnwWhz5eVmhHpi31HW29Z7mc9B4cWGRQzopNUzZUT391DeDJxL2PefNunWyLgqCKRMDkU1s2s8bAfoSk/84'/1'/0'/1/*)"; - /// let wallet = CreateParams::new(descriptor, change_descriptor, Network::Testnet)? + /// let wallet = Wallet::create(descriptor, change_descriptor) + /// .network(Network::Testnet) /// .create_wallet_no_persist()?; /// 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/* @@ -2452,18 +2417,14 @@ macro_rules! doctest_wallet { () => {{ use $crate::bitcoin::{BlockHash, Transaction, absolute, TxOut, Network, hashes::Hash}; use $crate::chain::{ConfirmationBlockTime, BlockId, TxGraph}; - use $crate::{Update, CreateParams, KeychainKind}; + use $crate::{Update, KeychainKind, Wallet}; let descriptor = "tr([73c5da0a/86'/0'/0']tprv8fMn4hSKPRC1oaCPqxDb1JWtgkpeiQvZhsr8W2xuy3GEMkzoArcAWTfJxYb6Wj8XNNDWEjfYKK4wGQXh3ZUXhDF2NcnsALpWTeSwarJt7Vc/0/*)"; let change_descriptor = "tr([73c5da0a/86'/0'/0']tprv8fMn4hSKPRC1oaCPqxDb1JWtgkpeiQvZhsr8W2xuy3GEMkzoArcAWTfJxYb6Wj8XNNDWEjfYKK4wGQXh3ZUXhDF2NcnsALpWTeSwarJt7Vc/1/*)"; - let mut wallet = CreateParams::new( - descriptor, - change_descriptor, - Network::Regtest, - ) - .unwrap() - .create_wallet_no_persist() - .unwrap(); + let mut wallet = Wallet::create(descriptor, change_descriptor) + .network(Network::Regtest) + .create_wallet_no_persist() + .unwrap(); let address = wallet.peek_address(KeychainKind::External, 0).address; let tx = Transaction { version: transaction::Version::ONE, diff --git a/crates/wallet/src/wallet/params.rs b/crates/wallet/src/wallet/params.rs index f6fce556..563082a1 100644 --- a/crates/wallet/src/wallet/params.rs +++ b/crates/wallet/src/wallet/params.rs @@ -1,53 +1,58 @@ +use alloc::boxed::Box; use bdk_chain::{keychain_txout::DEFAULT_LOOKAHEAD, PersistAsyncWith, PersistWith}; use bitcoin::{BlockHash, Network}; use miniscript::descriptor::KeyMap; use crate::{ descriptor::{DescriptorError, ExtendedDescriptor, IntoWalletDescriptor}, + utils::SecpCtx, KeychainKind, Wallet, }; -use super::{utils::SecpCtx, ChangeSet, LoadError, PersistedWallet}; +use super::{ChangeSet, LoadError, PersistedWallet}; + +/// This atrocity is to avoid having type parameters on [`CreateParams`] and [`LoadParams`]. +/// +/// The better option would be to do `Box`, but we cannot due to Rust's +/// [object safety rules](https://doc.rust-lang.org/reference/items/traits.html#object-safety). +type DescriptorToExtract = Box< + dyn FnOnce(&SecpCtx, Network) -> Result<(ExtendedDescriptor, KeyMap), DescriptorError> + + 'static, +>; + +fn make_descriptor_to_extract(descriptor: D) -> DescriptorToExtract +where + D: IntoWalletDescriptor + 'static, +{ + Box::new(|secp, network| descriptor.into_wallet_descriptor(secp, network)) +} /// Parameters for [`Wallet::create`] or [`PersistedWallet::create`]. -#[derive(Debug, Clone)] #[must_use] pub struct CreateParams { - pub(crate) descriptor: ExtendedDescriptor, + pub(crate) descriptor: DescriptorToExtract, pub(crate) descriptor_keymap: KeyMap, - pub(crate) change_descriptor: ExtendedDescriptor, + pub(crate) change_descriptor: DescriptorToExtract, pub(crate) change_descriptor_keymap: KeyMap, pub(crate) network: Network, pub(crate) genesis_hash: Option, pub(crate) lookahead: u32, - pub(crate) secp: SecpCtx, } impl CreateParams { /// Construct parameters with provided `descriptor`, `change_descriptor` and `network`. /// /// Default values: `genesis_hash` = `None`, `lookahead` = [`DEFAULT_LOOKAHEAD`] - pub fn new( - descriptor: E, - change_descriptor: E, - network: Network, - ) -> Result { - let secp = SecpCtx::default(); - - let (descriptor, descriptor_keymap) = descriptor.into_wallet_descriptor(&secp, network)?; - let (change_descriptor, change_descriptor_keymap) = - change_descriptor.into_wallet_descriptor(&secp, network)?; - - Ok(Self { - descriptor, - descriptor_keymap, - change_descriptor, - change_descriptor_keymap, - network, + pub fn new(descriptor: D, change_descriptor: D) -> Self { + Self { + descriptor: make_descriptor_to_extract(descriptor), + descriptor_keymap: KeyMap::default(), + change_descriptor: make_descriptor_to_extract(change_descriptor), + change_descriptor_keymap: KeyMap::default(), + network: Network::Bitcoin, genesis_hash: None, lookahead: DEFAULT_LOOKAHEAD, - secp, - }) + } } /// Extend the given `keychain`'s `keymap`. @@ -60,6 +65,12 @@ impl CreateParams { self } + /// Set `network`. + pub fn network(mut self, network: Network) -> Self { + self.network = network; + self + } + /// Use a custom `genesis_hash`. pub fn genesis_hash(mut self, genesis_hash: BlockHash) -> Self { self.genesis_hash = Some(genesis_hash); @@ -102,16 +113,14 @@ impl CreateParams { /// Parameters for [`Wallet::load`] or [`PersistedWallet::load`]. #[must_use] -#[derive(Debug, Clone)] pub struct LoadParams { pub(crate) descriptor_keymap: KeyMap, pub(crate) change_descriptor_keymap: KeyMap, pub(crate) lookahead: u32, pub(crate) check_network: Option, pub(crate) check_genesis_hash: Option, - pub(crate) check_descriptor: Option, - pub(crate) check_change_descriptor: Option, - pub(crate) secp: SecpCtx, + pub(crate) check_descriptor: Option, + pub(crate) check_change_descriptor: Option, } impl LoadParams { @@ -127,39 +136,9 @@ impl LoadParams { check_genesis_hash: None, check_descriptor: None, check_change_descriptor: None, - secp: SecpCtx::new(), } } - /// Construct parameters with `network` check. - pub fn with_network(network: Network) -> Self { - Self { - check_network: Some(network), - ..Default::default() - } - } - - /// Construct parameters with descriptor checks. - pub fn with_descriptors( - descriptor: E, - change_descriptor: E, - network: Network, - ) -> Result { - let mut params = Self::with_network(network); - let secp = ¶ms.secp; - - let (descriptor, descriptor_keymap) = descriptor.into_wallet_descriptor(secp, network)?; - params.check_descriptor = Some(descriptor); - params.descriptor_keymap = descriptor_keymap; - - let (change_descriptor, change_descriptor_keymap) = - change_descriptor.into_wallet_descriptor(secp, network)?; - params.check_change_descriptor = Some(change_descriptor); - params.change_descriptor_keymap = change_descriptor_keymap; - - Ok(params) - } - /// Extend the given `keychain`'s `keymap`. pub fn keymap(mut self, keychain: KeychainKind, keymap: KeyMap) -> Self { match keychain { @@ -170,6 +149,23 @@ impl LoadParams { self } + /// Checks that `descriptor` of `keychain` matches this, and extracts private keys (if + /// avaliable). + pub fn descriptors(mut self, descriptor: D, change_descriptor: D) -> Self + where + D: IntoWalletDescriptor + 'static, + { + self.check_descriptor = Some(make_descriptor_to_extract(descriptor)); + self.check_change_descriptor = Some(make_descriptor_to_extract(change_descriptor)); + self + } + + /// Check for `network`. + pub fn network(mut self, network: Network) -> Self { + self.check_network = Some(network); + self + } + /// Check for a `genesis_hash`. pub fn genesis_hash(mut self, genesis_hash: BlockHash) -> Self { self.check_genesis_hash = Some(genesis_hash); diff --git a/crates/wallet/src/wallet/signer.rs b/crates/wallet/src/wallet/signer.rs index c53eb6cd..946ac20d 100644 --- a/crates/wallet/src/wallet/signer.rs +++ b/crates/wallet/src/wallet/signer.rs @@ -69,7 +69,8 @@ //! //! let descriptor = "wpkh(tpubD6NzVbkrYhZ4Xferm7Pz4VnjdcDPFyjVu5K4iZXQ4pVN8Cks4pHVowTBXBKRhX64pkRyJZJN5xAKj4UDNnLPb5p2sSKXhewoYx5GbTdUFWq/0/*)"; //! let change_descriptor = "wpkh(tpubD6NzVbkrYhZ4Xferm7Pz4VnjdcDPFyjVu5K4iZXQ4pVN8Cks4pHVowTBXBKRhX64pkRyJZJN5xAKj4UDNnLPb5p2sSKXhewoYx5GbTdUFWq/1/*)"; -//! let mut wallet = CreateParams::new(descriptor, change_descriptor, Network::Testnet)? +//! let mut wallet = Wallet::create(descriptor, change_descriptor) +//! .network(Network::Testnet) //! .create_wallet_no_persist()?; //! wallet.add_signer( //! KeychainKind::External, diff --git a/crates/wallet/tests/common.rs b/crates/wallet/tests/common.rs index 0cdbda5a..288560b0 100644 --- a/crates/wallet/tests/common.rs +++ b/crates/wallet/tests/common.rs @@ -13,8 +13,8 @@ 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 = CreateParams::new(descriptor, change, Network::Regtest) - .expect("must parse descriptors") + let mut wallet = Wallet::create(descriptor.to_string(), change.to_string()) + .network(Network::Regtest) .create_wallet_no_persist() .expect("descriptors must be valid"); diff --git a/crates/wallet/tests/wallet.rs b/crates/wallet/tests/wallet.rs index edd2827b..94d4b07e 100644 --- a/crates/wallet/tests/wallet.rs +++ b/crates/wallet/tests/wallet.rs @@ -122,7 +122,8 @@ fn wallet_is_persisted() -> anyhow::Result<()> { // create new wallet let wallet_spk_index = { let mut db = create_db(&file_path)?; - let mut wallet = CreateParams::new(external_desc, internal_desc, Network::Testnet)? + let mut wallet = Wallet::create(external_desc, internal_desc) + .network(Network::Testnet) .create_wallet(&mut db)?; wallet.reveal_next_address(KeychainKind::External); @@ -134,10 +135,11 @@ fn wallet_is_persisted() -> anyhow::Result<()> { // recover wallet { let mut db = open_db(&file_path).context("failed to recover db")?; - let wallet = - LoadParams::with_descriptors(external_desc, internal_desc, Network::Testnet)? - .load_wallet(&mut db)? - .expect("wallet must exist"); + let wallet = Wallet::load() + .descriptors(external_desc, internal_desc) + .network(Network::Testnet) + .load_wallet(&mut db)? + .expect("wallet must exist"); assert_eq!(wallet.network(), Network::Testnet); assert_eq!( @@ -179,8 +181,8 @@ fn wallet_is_persisted() -> 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 = CreateParams::new(desc, desc, Network::Testnet) - .unwrap() + let err = Wallet::create(desc, desc) + .network(Network::Testnet) .create_wallet_no_persist(); assert!( matches!(&err, Err(DescriptorError::ExternalAndInternalAreTheSame)), @@ -191,8 +193,8 @@ 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 = CreateParams::new(desc, change_desc, Network::Testnet) - .unwrap() + let err = Wallet::create(desc, change_desc) + .network(Network::Testnet) .create_wallet_no_persist(); assert!( matches!(err, Err(DescriptorError::ExternalAndInternalAreTheSame)), @@ -1154,8 +1156,8 @@ fn test_create_tx_policy_path_required() { #[test] fn test_create_tx_policy_path_no_csv() { let (descriptor, change_descriptor) = get_test_wpkh_with_change_desc(); - let mut wallet = CreateParams::new(descriptor, change_descriptor, Network::Regtest) - .expect("must parse") + let mut wallet = Wallet::create(descriptor, change_descriptor) + .network(Network::Regtest) .create_wallet_no_persist() .expect("wallet"); @@ -2769,8 +2771,8 @@ fn test_sign_nonstandard_sighash() { fn test_unused_address() { let descriptor = "wpkh(tpubEBr4i6yk5nf5DAaJpsi9N2pPYBeJ7fZ5Z9rmN4977iYLCGco1VyjB9tvvuvYtfZzjD5A8igzgw3HeWeeKFmanHYqksqZXYXGsw5zjnj7KM9/*)"; let change_descriptor = get_test_wpkh(); - let mut wallet = CreateParams::new(descriptor, change_descriptor, Network::Testnet) - .expect("must parse descriptors") + let mut wallet = Wallet::create(descriptor, change_descriptor) + .network(Network::Testnet) .create_wallet_no_persist() .expect("wallet"); @@ -2800,8 +2802,8 @@ fn test_unused_address() { fn test_next_unused_address() { let descriptor = "wpkh(tpubEBr4i6yk5nf5DAaJpsi9N2pPYBeJ7fZ5Z9rmN4977iYLCGco1VyjB9tvvuvYtfZzjD5A8igzgw3HeWeeKFmanHYqksqZXYXGsw5zjnj7KM9/*)"; let change_descriptor = get_test_wpkh(); - let mut wallet = CreateParams::new(descriptor, change_descriptor, Network::Testnet) - .expect("must parse descriptors") + let mut wallet = Wallet::create(descriptor, change_descriptor) + .network(Network::Testnet) .create_wallet_no_persist() .expect("wallet"); assert_eq!(wallet.derivation_index(KeychainKind::External), None); @@ -2850,8 +2852,8 @@ fn test_next_unused_address() { fn test_peek_address_at_index() { let descriptor = "wpkh(tpubEBr4i6yk5nf5DAaJpsi9N2pPYBeJ7fZ5Z9rmN4977iYLCGco1VyjB9tvvuvYtfZzjD5A8igzgw3HeWeeKFmanHYqksqZXYXGsw5zjnj7KM9/*)"; let change_descriptor = get_test_wpkh(); - let mut wallet = CreateParams::new(descriptor, change_descriptor, Network::Testnet) - .expect("must parse descriptors") + let mut wallet = Wallet::create(descriptor, change_descriptor) + .network(Network::Testnet) .create_wallet_no_persist() .expect("wallet"); @@ -2888,11 +2890,11 @@ fn test_peek_address_at_index() { #[test] fn test_peek_address_at_index_not_derivable() { - let wallet = CreateParams::new( - "wpkh(tpubEBr4i6yk5nf5DAaJpsi9N2pPYBeJ7fZ5Z9rmN4977iYLCGco1VyjB9tvvuvYtfZzjD5A8igzgw3HeWeeKFmanHYqksqZXYXGsw5zjnj7KM9/1)", - get_test_wpkh(), - Network::Testnet, - ).unwrap().create_wallet_no_persist().unwrap(); + let descriptor = "wpkh(tpubEBr4i6yk5nf5DAaJpsi9N2pPYBeJ7fZ5Z9rmN4977iYLCGco1VyjB9tvvuvYtfZzjD5A8igzgw3HeWeeKFmanHYqksqZXYXGsw5zjnj7KM9/1)"; + let wallet = Wallet::create(descriptor, get_test_wpkh()) + .network(Network::Testnet) + .create_wallet_no_persist() + .unwrap(); assert_eq!( wallet.peek_address(KeychainKind::External, 1).to_string(), @@ -2912,11 +2914,12 @@ fn test_peek_address_at_index_not_derivable() { #[test] fn test_returns_index_and_address() { - let mut wallet = CreateParams::new( - "wpkh(tpubEBr4i6yk5nf5DAaJpsi9N2pPYBeJ7fZ5Z9rmN4977iYLCGco1VyjB9tvvuvYtfZzjD5A8igzgw3HeWeeKFmanHYqksqZXYXGsw5zjnj7KM9/*)", - get_test_wpkh(), - Network::Testnet, - ).unwrap().create_wallet_no_persist().unwrap(); + let descriptor = + "wpkh(tpubEBr4i6yk5nf5DAaJpsi9N2pPYBeJ7fZ5Z9rmN4977iYLCGco1VyjB9tvvuvYtfZzjD5A8igzgw3HeWeeKFmanHYqksqZXYXGsw5zjnj7KM9/*)"; + let mut wallet = Wallet::create(descriptor, get_test_wpkh()) + .network(Network::Testnet) + .create_wallet_no_persist() + .unwrap(); // new index 0 assert_eq!( @@ -2982,12 +2985,11 @@ 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 = CreateParams::new( + let wallet = Wallet::create( Bip84(key, KeychainKind::External), Bip84(key, KeychainKind::Internal), - Network::Regtest, ) - .unwrap() + .network(Network::Regtest) .create_wallet_no_persist() .unwrap(); @@ -3017,8 +3019,8 @@ 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 = CreateParams::new(desc, change_desc, Network::Signet) - .expect("must parse") + let mut wallet = Wallet::create(desc, change_desc) + .network(Network::Signet) .create_wallet_no_persist() .unwrap(); let keychain = KeychainKind::External; @@ -3041,12 +3043,11 @@ fn test_get_address_no_reuse() { use std::collections::HashSet; let key = bitcoin::bip32::Xpriv::from_str("tprv8ZgxMBicQKsPcx5nBGsR63Pe8KnRUqmbJNENAfGftF3yuXoMMoVJJcYeUw5eVkm9WBPjWYt6HMWYJNesB5HaNVBaFc1M6dRjWSYnmewUMYy").unwrap(); - let mut wallet = CreateParams::new( + let mut wallet = Wallet::create( Bip84(key, KeychainKind::External), Bip84(key, KeychainKind::Internal), - Network::Regtest, ) - .unwrap() + .network(Network::Regtest) .create_wallet_no_persist() .unwrap(); @@ -3517,14 +3518,10 @@ fn test_taproot_sign_derive_index_from_psbt() { let mut psbt = builder.finish().unwrap(); // re-create the wallet with an empty db - let wallet_empty = CreateParams::new( - get_test_tr_single_sig_xprv(), - get_test_tr_single_sig(), - Network::Regtest, - ) - .unwrap() - .create_wallet_no_persist() - .unwrap(); + let wallet_empty = Wallet::create(get_test_tr_single_sig_xprv(), get_test_tr_single_sig()) + .network(Network::Regtest) + .create_wallet_no_persist() + .unwrap(); // signing with an empty db means that we will only look at the psbt to infer the // derivation index @@ -3624,8 +3621,8 @@ 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 = CreateParams::new(desc, change_desc, Network::Regtest) - .unwrap() + let mut wallet = Wallet::create(desc, change_desc) + .network(Network::Regtest) .create_wallet_no_persist() .unwrap(); diff --git a/example-crates/wallet_electrum/src/main.rs b/example-crates/wallet_electrum/src/main.rs index 4913db9a..35413a96 100644 --- a/example-crates/wallet_electrum/src/main.rs +++ b/example-crates/wallet_electrum/src/main.rs @@ -1,6 +1,5 @@ use bdk_wallet::file_store::Store; -use bdk_wallet::CreateParams; -use bdk_wallet::LoadParams; +use bdk_wallet::Wallet; use std::io::Write; use std::str::FromStr; @@ -26,11 +25,15 @@ fn main() -> Result<(), anyhow::Error> { let mut db = Store::::open_or_create_new(DB_MAGIC.as_bytes(), db_path)?; - let load_params = LoadParams::with_descriptors(EXTERNAL_DESC, INTERNAL_DESC, NETWORK)?; - let create_params = CreateParams::new(EXTERNAL_DESC, INTERNAL_DESC, NETWORK)?; - let mut wallet = match load_params.load_wallet(&mut db)? { + let wallet_opt = Wallet::load() + .descriptors(EXTERNAL_DESC, INTERNAL_DESC) + .network(NETWORK) + .load_wallet(&mut db)?; + let mut wallet = match wallet_opt { Some(wallet) => wallet, - None => create_params.create_wallet(&mut db)?, + None => Wallet::create(EXTERNAL_DESC, INTERNAL_DESC) + .network(NETWORK) + .create_wallet(&mut db)?, }; let address = wallet.next_unused_address(KeychainKind::External); diff --git a/example-crates/wallet_esplora_async/src/main.rs b/example-crates/wallet_esplora_async/src/main.rs index efdf2f66..cccd8339 100644 --- a/example-crates/wallet_esplora_async/src/main.rs +++ b/example-crates/wallet_esplora_async/src/main.rs @@ -5,7 +5,7 @@ use bdk_esplora::{esplora_client, EsploraAsyncExt}; use bdk_wallet::{ bitcoin::{Amount, Network}, rusqlite::Connection, - CreateParams, KeychainKind, LoadParams, SignOptions, + KeychainKind, SignOptions, Wallet, }; const SEND_AMOUNT: Amount = Amount::from_sat(5000); @@ -22,11 +22,15 @@ const ESPLORA_URL: &str = "http://signet.bitcoindevkit.net"; async fn main() -> Result<(), anyhow::Error> { let mut conn = Connection::open(DB_PATH)?; - let load_params = LoadParams::with_descriptors(EXTERNAL_DESC, INTERNAL_DESC, NETWORK)?; - let create_params = CreateParams::new(EXTERNAL_DESC, INTERNAL_DESC, NETWORK)?; - let mut wallet = match load_params.load_wallet(&mut conn)? { + let wallet_opt = Wallet::load() + .descriptors(EXTERNAL_DESC, INTERNAL_DESC) + .network(NETWORK) + .load_wallet(&mut conn)?; + let mut wallet = match wallet_opt { Some(wallet) => wallet, - None => create_params.create_wallet(&mut conn)?, + None => Wallet::create(EXTERNAL_DESC, INTERNAL_DESC) + .network(NETWORK) + .create_wallet(&mut conn)?, }; let address = wallet.next_unused_address(KeychainKind::External); diff --git a/example-crates/wallet_esplora_blocking/src/main.rs b/example-crates/wallet_esplora_blocking/src/main.rs index 8075561b..4c4fe99e 100644 --- a/example-crates/wallet_esplora_blocking/src/main.rs +++ b/example-crates/wallet_esplora_blocking/src/main.rs @@ -4,7 +4,7 @@ use bdk_esplora::{esplora_client, EsploraExt}; use bdk_wallet::{ bitcoin::{Amount, Network}, file_store::Store, - CreateParams, KeychainKind, LoadParams, SignOptions, + KeychainKind, SignOptions, Wallet, }; const DB_MAGIC: &str = "bdk_wallet_esplora_example"; @@ -21,12 +21,15 @@ const ESPLORA_URL: &str = "http://signet.bitcoindevkit.net"; fn main() -> Result<(), anyhow::Error> { let mut db = Store::::open_or_create_new(DB_MAGIC.as_bytes(), DB_PATH)?; - let load_params = LoadParams::with_descriptors(EXTERNAL_DESC, INTERNAL_DESC, NETWORK)?; - let create_params = CreateParams::new(EXTERNAL_DESC, INTERNAL_DESC, NETWORK)?; - - let mut wallet = match load_params.load_wallet(&mut db)? { + let wallet_opt = Wallet::load() + .descriptors(EXTERNAL_DESC, INTERNAL_DESC) + .network(NETWORK) + .load_wallet(&mut db)?; + let mut wallet = match wallet_opt { Some(wallet) => wallet, - None => create_params.create_wallet(&mut db)?, + None => Wallet::create(EXTERNAL_DESC, INTERNAL_DESC) + .network(NETWORK) + .create_wallet(&mut db)?, }; let address = wallet.next_unused_address(KeychainKind::External); diff --git a/example-crates/wallet_rpc/src/main.rs b/example-crates/wallet_rpc/src/main.rs index fea8947a..2533de64 100644 --- a/example-crates/wallet_rpc/src/main.rs +++ b/example-crates/wallet_rpc/src/main.rs @@ -5,7 +5,7 @@ use bdk_bitcoind_rpc::{ use bdk_wallet::{ bitcoin::{Block, Network, Transaction}, file_store::Store, - CreateParams, LoadParams, + Wallet, }; use clap::{self, Parser}; use std::{path::PathBuf, sync::mpsc::sync_channel, thread::spawn, time::Instant}; @@ -88,13 +88,15 @@ fn main() -> anyhow::Result<()> { let start_load_wallet = Instant::now(); let mut db = Store::::open_or_create_new(DB_MAGIC.as_bytes(), args.db_path)?; - - let load_params = - LoadParams::with_descriptors(&args.descriptor, &args.change_descriptor, args.network)?; - let create_params = CreateParams::new(&args.descriptor, &args.change_descriptor, args.network)?; - let mut wallet = match load_params.load_wallet(&mut db)? { + let wallet_opt = Wallet::load() + .descriptors(args.descriptor.clone(), args.change_descriptor.clone()) + .network(args.network) + .load_wallet(&mut db)?; + let mut wallet = match wallet_opt { Some(wallet) => wallet, - None => create_params.create_wallet(&mut db)?, + None => Wallet::create(args.descriptor, args.change_descriptor) + .network(args.network) + .create_wallet(&mut db)?, }; println!( "Loaded wallet in {}s",