feat!: improve wallet building methods

Remove returning `Result` for builder methods on `CreateParams` and
`LoadParams`.
This commit is contained in:
志宇 2024-07-18 03:04:00 +00:00
parent eb73f0659e
commit 22d02ed3d1
No known key found for this signature in database
GPG Key ID: F6345C9837C2BDE8
15 changed files with 287 additions and 297 deletions

View File

@ -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(

View File

@ -67,7 +67,7 @@ To persist `Wallet` state data use a data store crate that reads and writes [`Ch
<!-- compile_fail because outpoint and txout are fake variables -->
```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.

View File

@ -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<dyn Error>> {
);
// 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!(

View File

@ -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,

View File

@ -73,7 +73,7 @@ impl<T: DescriptorTemplate> 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<T: DescriptorTemplate> 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<T: DescriptorTemplate> IntoWalletDescriptor for T {
/// );
/// # Ok::<_, Box<dyn std::error::Error>>(())
/// ```
#[derive(Debug, Clone)]
pub struct P2Pkh<K: IntoDescriptorKey<Legacy>>(pub K);
impl<K: IntoDescriptorKey<Legacy>> DescriptorTemplate for P2Pkh<K> {
@ -106,7 +108,7 @@ impl<K: IntoDescriptorKey<Legacy>> DescriptorTemplate for P2Pkh<K> {
///
/// ```
/// # 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<K: IntoDescriptorKey<Legacy>> DescriptorTemplate for P2Pkh<K> {
/// 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<K: IntoDescriptorKey<Legacy>> DescriptorTemplate for P2Pkh<K> {
/// # Ok::<_, Box<dyn std::error::Error>>(())
/// ```
#[allow(non_camel_case_types)]
#[derive(Debug, Clone)]
pub struct P2Wpkh_P2Sh<K: IntoDescriptorKey<Segwitv0>>(pub K);
impl<K: IntoDescriptorKey<Segwitv0>> DescriptorTemplate for P2Wpkh_P2Sh<K> {
@ -144,7 +144,7 @@ impl<K: IntoDescriptorKey<Segwitv0>> DescriptorTemplate for P2Wpkh_P2Sh<K> {
///
/// ```
/// # 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<K: IntoDescriptorKey<Segwitv0>> DescriptorTemplate for P2Wpkh_P2Sh<K> {
/// 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<K: IntoDescriptorKey<Segwitv0>> DescriptorTemplate for P2Wpkh_P2Sh<K> {
/// );
/// # Ok::<_, Box<dyn std::error::Error>>(())
/// ```
#[derive(Debug, Clone)]
pub struct P2Wpkh<K: IntoDescriptorKey<Segwitv0>>(pub K);
impl<K: IntoDescriptorKey<Segwitv0>> DescriptorTemplate for P2Wpkh<K> {
@ -178,7 +179,7 @@ impl<K: IntoDescriptorKey<Segwitv0>> DescriptorTemplate for P2Wpkh<K> {
///
/// ```
/// # 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<K: IntoDescriptorKey<Segwitv0>> DescriptorTemplate for P2Wpkh<K> {
/// 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<K: IntoDescriptorKey<Segwitv0>> DescriptorTemplate for P2Wpkh<K> {
/// );
/// # Ok::<_, Box<dyn std::error::Error>>(())
/// ```
#[derive(Debug, Clone)]
pub struct P2TR<K: IntoDescriptorKey<Tap>>(pub K);
impl<K: IntoDescriptorKey<Tap>> DescriptorTemplate for P2TR<K> {
@ -213,24 +216,22 @@ impl<K: IntoDescriptorKey<Tap>> DescriptorTemplate for P2TR<K> {
///
/// ## 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<dyn std::error::Error>>(())
/// ```
#[derive(Debug, Clone)]
pub struct Bip44<K: DerivableKey<Legacy>>(pub K, pub KeychainKind);
impl<K: DerivableKey<Legacy>> DescriptorTemplate for Bip44<K> {
@ -253,22 +254,23 @@ impl<K: DerivableKey<Legacy>> DescriptorTemplate for Bip44<K> {
/// ```
/// # 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<dyn std::error::Error>>(())
/// ```
#[derive(Debug, Clone)]
pub struct Bip44Public<K: DerivableKey<Legacy>>(pub K, pub bip32::Fingerprint, pub KeychainKind);
impl<K: DerivableKey<Legacy>> DescriptorTemplate for Bip44Public<K> {
@ -291,21 +293,22 @@ impl<K: DerivableKey<Legacy>> DescriptorTemplate for Bip44Public<K> {
/// ```
/// # 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<dyn std::error::Error>>(())
/// ```
#[derive(Debug, Clone)]
pub struct Bip49<K: DerivableKey<Segwitv0>>(pub K, pub KeychainKind);
impl<K: DerivableKey<Segwitv0>> DescriptorTemplate for Bip49<K> {
@ -328,22 +331,23 @@ impl<K: DerivableKey<Segwitv0>> DescriptorTemplate for Bip49<K> {
/// ```
/// # 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<dyn std::error::Error>>(())
/// ```
#[derive(Debug, Clone)]
pub struct Bip49Public<K: DerivableKey<Segwitv0>>(pub K, pub bip32::Fingerprint, pub KeychainKind);
impl<K: DerivableKey<Segwitv0>> DescriptorTemplate for Bip49Public<K> {
@ -366,21 +370,22 @@ impl<K: DerivableKey<Segwitv0>> DescriptorTemplate for Bip49Public<K> {
/// ```
/// # 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<dyn std::error::Error>>(())
/// ```
#[derive(Debug, Clone)]
pub struct Bip84<K: DerivableKey<Segwitv0>>(pub K, pub KeychainKind);
impl<K: DerivableKey<Segwitv0>> DescriptorTemplate for Bip84<K> {
@ -403,21 +408,23 @@ impl<K: DerivableKey<Segwitv0>> DescriptorTemplate for Bip84<K> {
/// ```
/// # 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<dyn std::error::Error>>(())
/// ```
#[derive(Debug, Clone)]
pub struct Bip84Public<K: DerivableKey<Segwitv0>>(pub K, pub bip32::Fingerprint, pub KeychainKind);
impl<K: DerivableKey<Segwitv0>> DescriptorTemplate for Bip84Public<K> {
@ -440,21 +447,22 @@ impl<K: DerivableKey<Segwitv0>> DescriptorTemplate for Bip84Public<K> {
/// ```
/// # 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<dyn std::error::Error>>(())
/// ```
#[derive(Debug, Clone)]
pub struct Bip86<K: DerivableKey<Tap>>(pub K, pub KeychainKind);
impl<K: DerivableKey<Tap>> DescriptorTemplate for Bip86<K> {
@ -477,22 +485,23 @@ impl<K: DerivableKey<Tap>> DescriptorTemplate for Bip86<K> {
/// ```
/// # 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<dyn std::error::Error>>(())
/// ```
#[derive(Debug, Clone)]
pub struct Bip86Public<K: DerivableKey<Tap>>(pub K, pub bip32::Fingerprint, pub KeychainKind);
impl<K: DerivableKey<Tap>> DescriptorTemplate for Bip86Public<K> {

View File

@ -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<dyn std::error::Error>>(())
//! ```
@ -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 {

View File

@ -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<E: IntoWalletDescriptor>(
descriptor: E,
change_descriptor: E,
network: Network,
) -> Result<CreateParams, DescriptorError> {
CreateParams::new(descriptor, change_descriptor, network)
pub fn create<D>(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<Self, DescriptorError> {
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(&params.descriptor)?;
check_wallet_descriptor(&params.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,
&params.descriptor,
descriptor_keymap,
&descriptor,
&secp,
));
let change_signers = Arc::new(SignersContainer::build(
params.change_descriptor_keymap,
&params.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<E: IntoWalletDescriptor>(
descriptor: E,
change_descriptor: E,
network: Network,
) -> Result<LoadParams, DescriptorError> {
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,

View File

@ -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<dyn IntoWalletDescriptor>`, 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<D>(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<BlockHash>,
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<E: IntoWalletDescriptor>(
descriptor: E,
change_descriptor: E,
network: Network,
) -> Result<Self, DescriptorError> {
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<D: IntoWalletDescriptor + 'static>(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<Network>,
pub(crate) check_genesis_hash: Option<BlockHash>,
pub(crate) check_descriptor: Option<ExtendedDescriptor>,
pub(crate) check_change_descriptor: Option<ExtendedDescriptor>,
pub(crate) secp: SecpCtx,
pub(crate) check_descriptor: Option<DescriptorToExtract>,
pub(crate) check_change_descriptor: Option<DescriptorToExtract>,
}
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<E: IntoWalletDescriptor>(
descriptor: E,
change_descriptor: E,
network: Network,
) -> Result<Self, DescriptorError> {
let mut params = Self::with_network(network);
let secp = &params.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<D>(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);

View File

@ -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,

View File

@ -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");

View File

@ -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();

View File

@ -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::<bdk_wallet::ChangeSet>::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);

View File

@ -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);

View File

@ -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::<bdk_wallet::ChangeSet>::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);

View File

@ -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::<bdk_wallet::ChangeSet>::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",