refactor(wallet)!: Make Wallet require a change descriptor
All `Wallet` constructors are modified to require a change descriptor, where previously it was optional. Additionally we enforce uniqueness of the change descriptor to avoid ambiguity when deriving scripts and ensure the wallet will always have two distinct keystores. Notable changes * Add error DescriptorError::ExternalAndInternalAreTheSame * Remove error CreateTxError::ChangePolicyDescriptor * No longer rely on `map_keychain`
This commit is contained in:
parent
8eef350bd0
commit
9d954cf7d2
@ -20,7 +20,7 @@
|
||||
//!
|
||||
//! # let mut wallet = Wallet::new_no_persist(
|
||||
//! # "",
|
||||
//! # None,
|
||||
//! # "",
|
||||
//! # Network::Testnet,
|
||||
//! # )?;
|
||||
//! #
|
||||
|
@ -74,7 +74,8 @@ fn main() {
|
||||
let db = bdk_file_store::Store::<ChangeSet>::open_or_create_new(b"magic_bytes", "path/to/my_wallet.db").expect("create store");
|
||||
|
||||
let descriptor = "wpkh(tprv8ZgxMBicQKsPdcAqYBpzAFwU5yxBUo88ggoBqu1qPcHUfSbKK1sKMLmC7EAk438btHQrSdu3jGGQa6PA71nvH5nkDexhLteJqkM4dQmWF9g/84'/1'/0'/0/*)";
|
||||
let mut wallet = Wallet::new_or_load(descriptor, None, db, Network::Testnet).expect("create or load wallet");
|
||||
let change_descriptor = "wpkh(tprv8ZgxMBicQKsPdcAqYBpzAFwU5yxBUo88ggoBqu1qPcHUfSbKK1sKMLmC7EAk438btHQrSdu3jGGQa6PA71nvH5nkDexhLteJqkM4dQmWF9g/84'/1'/0'/1/*)";
|
||||
let mut wallet = Wallet::new_or_load(descriptor, change_descriptor, db, Network::Testnet).expect("create or load wallet");
|
||||
|
||||
// Insert a single `TxOut` at `OutPoint` into the wallet.
|
||||
let _ = wallet.insert_txout(outpoint, txout);
|
||||
|
@ -32,21 +32,52 @@ use bdk_wallet::{KeychainKind, Wallet};
|
||||
/// This example demonstrates the interaction between a bdk wallet and miniscript policy.
|
||||
|
||||
fn main() -> Result<(), Box<dyn Error>> {
|
||||
// We start with a generic miniscript policy string
|
||||
let policy_str = "or(10@thresh(4,pk(029ffbe722b147f3035c87cb1c60b9a5947dd49c774cc31e94773478711a929ac0),pk(025f05815e3a1a8a83bfbb03ce016c9a2ee31066b98f567f6227df1d76ec4bd143),pk(025625f41e4a065efc06d5019cbbd56fe8c07595af1231e7cbc03fafb87ebb71ec),pk(02a27c8b850a00f67da3499b60562673dcf5fdfb82b7e17652a7ac54416812aefd),pk(03e618ec5f384d6e19ca9ebdb8e2119e5bef978285076828ce054e55c4daf473e2)),1@and(older(4209713),thresh(2,pk(03deae92101c790b12653231439f27b8897264125ecb2f46f48278603102573165),pk(033841045a531e1adf9910a6ec279589a90b3b8a904ee64ffd692bd08a8996c1aa),pk(02aebf2d10b040eb936a6f02f44ee82f8b34f5c1ccb20ff3949c2b28206b7c1068))))";
|
||||
// We start with a miniscript policy string
|
||||
let policy_str = "or(
|
||||
10@thresh(4,
|
||||
pk(029ffbe722b147f3035c87cb1c60b9a5947dd49c774cc31e94773478711a929ac0),pk(025f05815e3a1a8a83bfbb03ce016c9a2ee31066b98f567f6227df1d76ec4bd143),pk(025625f41e4a065efc06d5019cbbd56fe8c07595af1231e7cbc03fafb87ebb71ec),pk(02a27c8b850a00f67da3499b60562673dcf5fdfb82b7e17652a7ac54416812aefd),pk(03e618ec5f384d6e19ca9ebdb8e2119e5bef978285076828ce054e55c4daf473e2)
|
||||
),1@and(
|
||||
older(4209713),
|
||||
thresh(2,
|
||||
pk(03deae92101c790b12653231439f27b8897264125ecb2f46f48278603102573165),pk(033841045a531e1adf9910a6ec279589a90b3b8a904ee64ffd692bd08a8996c1aa),pk(02aebf2d10b040eb936a6f02f44ee82f8b34f5c1ccb20ff3949c2b28206b7c1068)
|
||||
)
|
||||
)
|
||||
)"
|
||||
.replace(&[' ', '\n', '\t'][..], "");
|
||||
|
||||
println!("Compiling policy: \n{}", policy_str);
|
||||
|
||||
// Parse the string as a [`Concrete`] type miniscript policy.
|
||||
let policy = Concrete::<String>::from_str(policy_str)?;
|
||||
let policy = Concrete::<String>::from_str(&policy_str)?;
|
||||
|
||||
// Create a `wsh` type descriptor from the policy.
|
||||
// `policy.compile()` returns the resulting miniscript from the policy.
|
||||
let descriptor = Descriptor::new_wsh(policy.compile()?)?;
|
||||
let descriptor = Descriptor::new_wsh(policy.compile()?)?.to_string();
|
||||
|
||||
println!("Compiled into following Descriptor: \n{}", descriptor);
|
||||
println!("Compiled into Descriptor: \n{}", descriptor);
|
||||
|
||||
// Create a new wallet from this descriptor
|
||||
let mut wallet = Wallet::new_no_persist(&format!("{}", descriptor), None, Network::Regtest)?;
|
||||
// Do the same for another (internal) keychain
|
||||
let policy_str = "or(
|
||||
10@thresh(2,
|
||||
pk(029ffbe722b147f3035c87cb1c60b9a5947dd49c774cc31e94773478711a929ac0),pk(025f05815e3a1a8a83bfbb03ce016c9a2ee31066b98f567f6227df1d76ec4bd143),pk(025625f41e4a065efc06d5019cbbd56fe8c07595af1231e7cbc03fafb87ebb71ec)
|
||||
),1@and(
|
||||
pk(03deae92101c790b12653231439f27b8897264125ecb2f46f48278603102573165),
|
||||
older(12960)
|
||||
)
|
||||
)"
|
||||
.replace(&[' ', '\n', '\t'][..], "");
|
||||
|
||||
println!("Compiling internal policy: \n{}", policy_str);
|
||||
|
||||
let policy = Concrete::<String>::from_str(&policy_str)?;
|
||||
let internal_descriptor = Descriptor::new_wsh(policy.compile()?)?.to_string();
|
||||
println!(
|
||||
"Compiled into internal Descriptor: \n{}",
|
||||
internal_descriptor
|
||||
);
|
||||
|
||||
// Create a new wallet from descriptors
|
||||
let mut wallet = Wallet::new_no_persist(&descriptor, &internal_descriptor, Network::Regtest)?;
|
||||
|
||||
println!(
|
||||
"First derived address from the descriptor: \n{}",
|
||||
|
@ -42,6 +42,8 @@ pub enum Error {
|
||||
Miniscript(miniscript::Error),
|
||||
/// Hex decoding error
|
||||
Hex(bitcoin::hex::HexToBytesError),
|
||||
/// The provided wallet descriptors are identical
|
||||
ExternalAndInternalAreTheSame,
|
||||
}
|
||||
|
||||
impl From<crate::keys::KeyError> for Error {
|
||||
@ -79,6 +81,9 @@ impl fmt::Display for Error {
|
||||
Self::Pk(err) => write!(f, "Key-related error: {}", err),
|
||||
Self::Miniscript(err) => write!(f, "Miniscript error: {}", err),
|
||||
Self::Hex(err) => write!(f, "Hex decoding error: {}", err),
|
||||
Self::ExternalAndInternalAreTheSame => {
|
||||
write!(f, "External and internal descriptors are the same")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -77,9 +77,12 @@ impl<T: DescriptorTemplate> IntoWalletDescriptor for T {
|
||||
/// # use bdk_wallet::KeychainKind;
|
||||
/// use bdk_wallet::template::P2Pkh;
|
||||
///
|
||||
/// let key =
|
||||
/// let key_external =
|
||||
/// bitcoin::PrivateKey::from_wif("cTc4vURSzdx6QE6KVynWGomDbLaA75dNALMNyfjh3p8DRRar84Um")?;
|
||||
/// let mut wallet = Wallet::new_no_persist(P2Pkh(key), None, Network::Testnet)?;
|
||||
/// let key_internal =
|
||||
/// bitcoin::PrivateKey::from_wif("cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW")?;
|
||||
/// let mut wallet =
|
||||
/// Wallet::new_no_persist(P2Pkh(key_external), P2Pkh(key_internal), Network::Testnet)?;
|
||||
///
|
||||
/// assert_eq!(
|
||||
/// wallet
|
||||
@ -107,9 +110,15 @@ impl<K: IntoDescriptorKey<Legacy>> DescriptorTemplate for P2Pkh<K> {
|
||||
/// # use bdk_wallet::KeychainKind;
|
||||
/// use bdk_wallet::template::P2Wpkh_P2Sh;
|
||||
///
|
||||
/// let key =
|
||||
/// let key_external =
|
||||
/// bitcoin::PrivateKey::from_wif("cTc4vURSzdx6QE6KVynWGomDbLaA75dNALMNyfjh3p8DRRar84Um")?;
|
||||
/// let mut wallet = Wallet::new_no_persist(P2Wpkh_P2Sh(key), None, Network::Testnet)?;
|
||||
/// let key_internal =
|
||||
/// bitcoin::PrivateKey::from_wif("cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW")?;
|
||||
/// let mut wallet = Wallet::new_no_persist(
|
||||
/// P2Wpkh_P2Sh(key_external),
|
||||
/// P2Wpkh_P2Sh(key_internal),
|
||||
/// Network::Testnet,
|
||||
/// )?;
|
||||
///
|
||||
/// assert_eq!(
|
||||
/// wallet
|
||||
@ -138,9 +147,12 @@ impl<K: IntoDescriptorKey<Segwitv0>> DescriptorTemplate for P2Wpkh_P2Sh<K> {
|
||||
/// # use bdk_wallet::KeychainKind;
|
||||
/// use bdk_wallet::template::P2Wpkh;
|
||||
///
|
||||
/// let key =
|
||||
/// let key_external =
|
||||
/// bitcoin::PrivateKey::from_wif("cTc4vURSzdx6QE6KVynWGomDbLaA75dNALMNyfjh3p8DRRar84Um")?;
|
||||
/// let mut wallet = Wallet::new_no_persist(P2Wpkh(key), None, Network::Testnet)?;
|
||||
/// let key_internal =
|
||||
/// bitcoin::PrivateKey::from_wif("cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW")?;
|
||||
/// let mut wallet =
|
||||
/// Wallet::new_no_persist(P2Wpkh(key_external), P2Wpkh(key_internal), Network::Testnet)?;
|
||||
///
|
||||
/// assert_eq!(
|
||||
/// wallet
|
||||
@ -168,9 +180,12 @@ impl<K: IntoDescriptorKey<Segwitv0>> DescriptorTemplate for P2Wpkh<K> {
|
||||
/// # use bdk_wallet::KeychainKind;
|
||||
/// use bdk_wallet::template::P2TR;
|
||||
///
|
||||
/// let key =
|
||||
/// let key_external =
|
||||
/// bitcoin::PrivateKey::from_wif("cTc4vURSzdx6QE6KVynWGomDbLaA75dNALMNyfjh3p8DRRar84Um")?;
|
||||
/// let mut wallet = Wallet::new_no_persist(P2TR(key), None, Network::Testnet)?;
|
||||
/// let key_internal =
|
||||
/// bitcoin::PrivateKey::from_wif("cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW")?;
|
||||
/// let mut wallet =
|
||||
/// Wallet::new_no_persist(P2TR(key_external), P2TR(key_internal), Network::Testnet)?;
|
||||
///
|
||||
/// assert_eq!(
|
||||
/// wallet
|
||||
@ -205,7 +220,7 @@ impl<K: IntoDescriptorKey<Tap>> DescriptorTemplate for P2TR<K> {
|
||||
/// let key = bitcoin::bip32::Xpriv::from_str("tprv8ZgxMBicQKsPeZRHk4rTG6orPS2CRNFX3njhUXx5vj9qGog5ZMH4uGReDWN5kCkY3jmWEtWause41CDvBRXD1shKknAMKxT99o9qUTRVC6m")?;
|
||||
/// let mut wallet = Wallet::new_no_persist(
|
||||
/// Bip44(key.clone(), KeychainKind::External),
|
||||
/// Some(Bip44(key, KeychainKind::Internal)),
|
||||
/// Bip44(key, KeychainKind::Internal),
|
||||
/// Network::Testnet,
|
||||
/// )?;
|
||||
///
|
||||
@ -242,7 +257,7 @@ impl<K: DerivableKey<Legacy>> DescriptorTemplate for Bip44<K> {
|
||||
/// let fingerprint = bitcoin::bip32::Fingerprint::from_str("c55b303f")?;
|
||||
/// let mut wallet = Wallet::new_no_persist(
|
||||
/// Bip44Public(key.clone(), fingerprint, KeychainKind::External),
|
||||
/// Some(Bip44Public(key, fingerprint, KeychainKind::Internal)),
|
||||
/// Bip44Public(key, fingerprint, KeychainKind::Internal),
|
||||
/// Network::Testnet,
|
||||
/// )?;
|
||||
///
|
||||
@ -278,7 +293,7 @@ impl<K: DerivableKey<Legacy>> DescriptorTemplate for Bip44Public<K> {
|
||||
/// let key = bitcoin::bip32::Xpriv::from_str("tprv8ZgxMBicQKsPeZRHk4rTG6orPS2CRNFX3njhUXx5vj9qGog5ZMH4uGReDWN5kCkY3jmWEtWause41CDvBRXD1shKknAMKxT99o9qUTRVC6m")?;
|
||||
/// let mut wallet = Wallet::new_no_persist(
|
||||
/// Bip49(key.clone(), KeychainKind::External),
|
||||
/// Some(Bip49(key, KeychainKind::Internal)),
|
||||
/// Bip49(key, KeychainKind::Internal),
|
||||
/// Network::Testnet,
|
||||
/// )?;
|
||||
///
|
||||
@ -315,7 +330,7 @@ impl<K: DerivableKey<Segwitv0>> DescriptorTemplate for Bip49<K> {
|
||||
/// let fingerprint = bitcoin::bip32::Fingerprint::from_str("c55b303f")?;
|
||||
/// let mut wallet = Wallet::new_no_persist(
|
||||
/// Bip49Public(key.clone(), fingerprint, KeychainKind::External),
|
||||
/// Some(Bip49Public(key, fingerprint, KeychainKind::Internal)),
|
||||
/// Bip49Public(key, fingerprint, KeychainKind::Internal),
|
||||
/// Network::Testnet,
|
||||
/// )?;
|
||||
///
|
||||
@ -351,7 +366,7 @@ impl<K: DerivableKey<Segwitv0>> DescriptorTemplate for Bip49Public<K> {
|
||||
/// let key = bitcoin::bip32::Xpriv::from_str("tprv8ZgxMBicQKsPeZRHk4rTG6orPS2CRNFX3njhUXx5vj9qGog5ZMH4uGReDWN5kCkY3jmWEtWause41CDvBRXD1shKknAMKxT99o9qUTRVC6m")?;
|
||||
/// let mut wallet = Wallet::new_no_persist(
|
||||
/// Bip84(key.clone(), KeychainKind::External),
|
||||
/// Some(Bip84(key, KeychainKind::Internal)),
|
||||
/// Bip84(key, KeychainKind::Internal),
|
||||
/// Network::Testnet,
|
||||
/// )?;
|
||||
///
|
||||
@ -388,7 +403,7 @@ impl<K: DerivableKey<Segwitv0>> DescriptorTemplate for Bip84<K> {
|
||||
/// let fingerprint = bitcoin::bip32::Fingerprint::from_str("c55b303f")?;
|
||||
/// let mut wallet = Wallet::new_no_persist(
|
||||
/// Bip84Public(key.clone(), fingerprint, KeychainKind::External),
|
||||
/// Some(Bip84Public(key, fingerprint, KeychainKind::Internal)),
|
||||
/// Bip84Public(key, fingerprint, KeychainKind::Internal),
|
||||
/// Network::Testnet,
|
||||
/// )?;
|
||||
///
|
||||
@ -424,7 +439,7 @@ impl<K: DerivableKey<Segwitv0>> DescriptorTemplate for Bip84Public<K> {
|
||||
/// let key = bitcoin::bip32::Xpriv::from_str("tprv8ZgxMBicQKsPeZRHk4rTG6orPS2CRNFX3njhUXx5vj9qGog5ZMH4uGReDWN5kCkY3jmWEtWause41CDvBRXD1shKknAMKxT99o9qUTRVC6m")?;
|
||||
/// let mut wallet = Wallet::new_no_persist(
|
||||
/// Bip86(key.clone(), KeychainKind::External),
|
||||
/// Some(Bip86(key, KeychainKind::Internal)),
|
||||
/// Bip86(key, KeychainKind::Internal),
|
||||
/// Network::Testnet,
|
||||
/// )?;
|
||||
///
|
||||
@ -461,7 +476,7 @@ impl<K: DerivableKey<Tap>> DescriptorTemplate for Bip86<K> {
|
||||
/// let fingerprint = bitcoin::bip32::Fingerprint::from_str("c55b303f")?;
|
||||
/// let mut wallet = Wallet::new_no_persist(
|
||||
/// Bip86Public(key.clone(), fingerprint, KeychainKind::External),
|
||||
/// Some(Bip86Public(key, fingerprint, KeychainKind::Internal)),
|
||||
/// Bip86Public(key, fingerprint, KeychainKind::Internal),
|
||||
/// Network::Testnet,
|
||||
/// )?;
|
||||
///
|
||||
|
@ -90,8 +90,6 @@ pub enum CreateTxError {
|
||||
NoUtxosSelected,
|
||||
/// Output created is under the dust limit, 546 satoshis
|
||||
OutputBelowDustLimit(usize),
|
||||
/// The `change_policy` was set but the wallet does not have a change_descriptor
|
||||
ChangePolicyDescriptor,
|
||||
/// There was an error with coin selection
|
||||
CoinSelection(coin_selection::Error),
|
||||
/// Wallet's UTXO set is not enough to cover recipient's requested plus fee
|
||||
@ -177,12 +175,6 @@ impl fmt::Display for CreateTxError {
|
||||
CreateTxError::OutputBelowDustLimit(limit) => {
|
||||
write!(f, "Output below the dust limit: {}", limit)
|
||||
}
|
||||
CreateTxError::ChangePolicyDescriptor => {
|
||||
write!(
|
||||
f,
|
||||
"The `change_policy` can be set only if the wallet has a change_descriptor"
|
||||
)
|
||||
}
|
||||
CreateTxError::CoinSelection(e) => e.fmt(f),
|
||||
CreateTxError::InsufficientFunds { needed, available } => {
|
||||
write!(
|
||||
|
@ -31,7 +31,7 @@
|
||||
//! let import = FullyNodedExport::from_str(import)?;
|
||||
//! let wallet = Wallet::new_no_persist(
|
||||
//! &import.descriptor(),
|
||||
//! import.change_descriptor().as_ref(),
|
||||
//! &import.change_descriptor().expect("change descriptor"),
|
||||
//! Network::Testnet,
|
||||
//! )?;
|
||||
//! # Ok::<_, Box<dyn std::error::Error>>(())
|
||||
@ -44,7 +44,7 @@
|
||||
//! # use bdk_wallet::*;
|
||||
//! let wallet = Wallet::new_no_persist(
|
||||
//! "wpkh([c258d2e4/84h/1h/0h]tpubDD3ynpHgJQW8VvWRzQ5WFDCrs4jqVFGHB3vLC3r49XHJSqP8bHKdK4AriuUKLccK68zfzowx7YhmDN8SiSkgCDENUFx9qVw65YyqM78vyVe/0/*)",
|
||||
//! Some("wpkh([c258d2e4/84h/1h/0h]tpubDD3ynpHgJQW8VvWRzQ5WFDCrs4jqVFGHB3vLC3r49XHJSqP8bHKdK4AriuUKLccK68zfzowx7YhmDN8SiSkgCDENUFx9qVw65YyqM78vyVe/1/*)"),
|
||||
//! "wpkh([c258d2e4/84h/1h/0h]tpubDD3ynpHgJQW8VvWRzQ5WFDCrs4jqVFGHB3vLC3r49XHJSqP8bHKdK4AriuUKLccK68zfzowx7YhmDN8SiSkgCDENUFx9qVw65YyqM78vyVe/1/*)",
|
||||
//! Network::Testnet,
|
||||
//! )?;
|
||||
//! let export = FullyNodedExport::export_wallet(&wallet, "exported wallet", true).unwrap();
|
||||
@ -142,19 +142,17 @@ impl FullyNodedExport {
|
||||
blockheight,
|
||||
};
|
||||
|
||||
let change_descriptor = match wallet.public_descriptor(KeychainKind::Internal).is_some() {
|
||||
false => None,
|
||||
true => {
|
||||
let descriptor = wallet
|
||||
.get_descriptor_for_keychain(KeychainKind::Internal)
|
||||
.to_string_with_secret(
|
||||
&wallet
|
||||
.get_signers(KeychainKind::Internal)
|
||||
.as_key_map(wallet.secp_ctx()),
|
||||
);
|
||||
Some(remove_checksum(descriptor))
|
||||
}
|
||||
let change_descriptor = {
|
||||
let descriptor = wallet
|
||||
.get_descriptor_for_keychain(KeychainKind::Internal)
|
||||
.to_string_with_secret(
|
||||
&wallet
|
||||
.get_signers(KeychainKind::Internal)
|
||||
.as_key_map(wallet.secp_ctx()),
|
||||
);
|
||||
Some(remove_checksum(descriptor))
|
||||
};
|
||||
|
||||
if export.change_descriptor() != change_descriptor {
|
||||
return Err("Incompatible change descriptor");
|
||||
}
|
||||
@ -223,11 +221,7 @@ mod test {
|
||||
use super::*;
|
||||
use crate::wallet::Wallet;
|
||||
|
||||
fn get_test_wallet(
|
||||
descriptor: &str,
|
||||
change_descriptor: Option<&str>,
|
||||
network: Network,
|
||||
) -> Wallet {
|
||||
fn get_test_wallet(descriptor: &str, change_descriptor: &str, network: Network) -> Wallet {
|
||||
let mut wallet = Wallet::new_no_persist(descriptor, change_descriptor, network).unwrap();
|
||||
let transaction = Transaction {
|
||||
input: vec![],
|
||||
@ -258,7 +252,7 @@ mod test {
|
||||
let descriptor = "wpkh(xprv9s21ZrQH143K4CTb63EaMxja1YiTnSEWKMbn23uoEnAzxjdUJRQkazCAtzxGm4LSoTSVTptoV9RbchnKPW9HxKtZumdyxyikZFDLhogJ5Uj/44'/0'/0'/0/*)";
|
||||
let change_descriptor = "wpkh(xprv9s21ZrQH143K4CTb63EaMxja1YiTnSEWKMbn23uoEnAzxjdUJRQkazCAtzxGm4LSoTSVTptoV9RbchnKPW9HxKtZumdyxyikZFDLhogJ5Uj/44'/0'/0'/1/*)";
|
||||
|
||||
let wallet = get_test_wallet(descriptor, Some(change_descriptor), Network::Bitcoin);
|
||||
let wallet = get_test_wallet(descriptor, change_descriptor, Network::Bitcoin);
|
||||
let export = FullyNodedExport::export_wallet(&wallet, "Test Label", true).unwrap();
|
||||
|
||||
assert_eq!(export.descriptor(), descriptor);
|
||||
@ -270,13 +264,14 @@ mod test {
|
||||
#[test]
|
||||
#[should_panic(expected = "Incompatible change descriptor")]
|
||||
fn test_export_no_change() {
|
||||
// This wallet explicitly doesn't have a change descriptor. It should be impossible to
|
||||
// The wallet's change descriptor has no wildcard. It should be impossible to
|
||||
// export, because exporting this kind of external descriptor normally implies the
|
||||
// existence of an internal descriptor
|
||||
// existence of a compatible internal descriptor
|
||||
|
||||
let descriptor = "wpkh(xprv9s21ZrQH143K4CTb63EaMxja1YiTnSEWKMbn23uoEnAzxjdUJRQkazCAtzxGm4LSoTSVTptoV9RbchnKPW9HxKtZumdyxyikZFDLhogJ5Uj/44'/0'/0'/0/*)";
|
||||
let change_descriptor = "wpkh(xprv9s21ZrQH143K4CTb63EaMxja1YiTnSEWKMbn23uoEnAzxjdUJRQkazCAtzxGm4LSoTSVTptoV9RbchnKPW9HxKtZumdyxyikZFDLhogJ5Uj/44'/0'/0'/1/0)";
|
||||
|
||||
let wallet = get_test_wallet(descriptor, None, Network::Bitcoin);
|
||||
let wallet = get_test_wallet(descriptor, change_descriptor, Network::Bitcoin);
|
||||
FullyNodedExport::export_wallet(&wallet, "Test Label", true).unwrap();
|
||||
}
|
||||
|
||||
@ -289,7 +284,7 @@ mod test {
|
||||
let descriptor = "wpkh(xprv9s21ZrQH143K4CTb63EaMxja1YiTnSEWKMbn23uoEnAzxjdUJRQkazCAtzxGm4LSoTSVTptoV9RbchnKPW9HxKtZumdyxyikZFDLhogJ5Uj/44'/0'/0'/0/*)";
|
||||
let change_descriptor = "wpkh(xprv9s21ZrQH143K4CTb63EaMxja1YiTnSEWKMbn23uoEnAzxjdUJRQkazCAtzxGm4LSoTSVTptoV9RbchnKPW9HxKtZumdyxyikZFDLhogJ5Uj/50'/0'/1/*)";
|
||||
|
||||
let wallet = get_test_wallet(descriptor, Some(change_descriptor), Network::Bitcoin);
|
||||
let wallet = get_test_wallet(descriptor, change_descriptor, Network::Bitcoin);
|
||||
FullyNodedExport::export_wallet(&wallet, "Test Label", true).unwrap();
|
||||
}
|
||||
|
||||
@ -306,7 +301,7 @@ mod test {
|
||||
[c98b1535/48'/0'/0'/2']tpubDCDi5W4sP6zSnzJeowy8rQDVhBdRARaPhK1axABi8V1661wEPeanpEXj4ZLAUEoikVtoWcyK26TKKJSecSfeKxwHCcRrge9k1ybuiL71z4a/1/*\
|
||||
))";
|
||||
|
||||
let wallet = get_test_wallet(descriptor, Some(change_descriptor), Network::Testnet);
|
||||
let wallet = get_test_wallet(descriptor, change_descriptor, Network::Testnet);
|
||||
let export = FullyNodedExport::export_wallet(&wallet, "Test Label", true).unwrap();
|
||||
|
||||
assert_eq!(export.descriptor(), descriptor);
|
||||
@ -319,7 +314,7 @@ mod test {
|
||||
fn test_export_tr() {
|
||||
let descriptor = "tr([73c5da0a/86'/0'/0']tprv8fMn4hSKPRC1oaCPqxDb1JWtgkpeiQvZhsr8W2xuy3GEMkzoArcAWTfJxYb6Wj8XNNDWEjfYKK4wGQXh3ZUXhDF2NcnsALpWTeSwarJt7Vc/0/*)";
|
||||
let change_descriptor = "tr([73c5da0a/86'/0'/0']tprv8fMn4hSKPRC1oaCPqxDb1JWtgkpeiQvZhsr8W2xuy3GEMkzoArcAWTfJxYb6Wj8XNNDWEjfYKK4wGQXh3ZUXhDF2NcnsALpWTeSwarJt7Vc/1/*)";
|
||||
let wallet = get_test_wallet(descriptor, Some(change_descriptor), Network::Testnet);
|
||||
let wallet = get_test_wallet(descriptor, change_descriptor, Network::Testnet);
|
||||
let export = FullyNodedExport::export_wallet(&wallet, "Test Label", true).unwrap();
|
||||
assert_eq!(export.descriptor(), descriptor);
|
||||
assert_eq!(export.change_descriptor(), Some(change_descriptor.into()));
|
||||
@ -332,7 +327,7 @@ mod test {
|
||||
let descriptor = "wpkh(xprv9s21ZrQH143K4CTb63EaMxja1YiTnSEWKMbn23uoEnAzxjdUJRQkazCAtzxGm4LSoTSVTptoV9RbchnKPW9HxKtZumdyxyikZFDLhogJ5Uj/44'/0'/0'/0/*)";
|
||||
let change_descriptor = "wpkh(xprv9s21ZrQH143K4CTb63EaMxja1YiTnSEWKMbn23uoEnAzxjdUJRQkazCAtzxGm4LSoTSVTptoV9RbchnKPW9HxKtZumdyxyikZFDLhogJ5Uj/44'/0'/0'/1/*)";
|
||||
|
||||
let wallet = get_test_wallet(descriptor, Some(change_descriptor), Network::Bitcoin);
|
||||
let wallet = get_test_wallet(descriptor, change_descriptor, Network::Bitcoin);
|
||||
let export = FullyNodedExport::export_wallet(&wallet, "Test Label", true).unwrap();
|
||||
|
||||
assert_eq!(export.to_string(), "{\"descriptor\":\"wpkh(xprv9s21ZrQH143K4CTb63EaMxja1YiTnSEWKMbn23uoEnAzxjdUJRQkazCAtzxGm4LSoTSVTptoV9RbchnKPW9HxKtZumdyxyikZFDLhogJ5Uj/44\'/0\'/0\'/0/*)\",\"blockheight\":5000,\"label\":\"Test Label\"}");
|
||||
|
@ -166,7 +166,7 @@ impl Wallet {
|
||||
/// Creates a wallet that does not persist data.
|
||||
pub fn new_no_persist<E: IntoWalletDescriptor>(
|
||||
descriptor: E,
|
||||
change_descriptor: Option<E>,
|
||||
change_descriptor: E,
|
||||
network: Network,
|
||||
) -> Result<Self, DescriptorError> {
|
||||
Self::new(descriptor, change_descriptor, (), network).map_err(|e| match e {
|
||||
@ -179,7 +179,7 @@ impl Wallet {
|
||||
/// Creates a wallet that does not persist data, with a custom genesis hash.
|
||||
pub fn new_no_persist_with_genesis_hash<E: IntoWalletDescriptor>(
|
||||
descriptor: E,
|
||||
change_descriptor: Option<E>,
|
||||
change_descriptor: E,
|
||||
network: Network,
|
||||
genesis_hash: BlockHash,
|
||||
) -> Result<Self, crate::descriptor::DescriptorError> {
|
||||
@ -398,7 +398,7 @@ impl Wallet {
|
||||
/// Initialize an empty [`Wallet`].
|
||||
pub fn new<E: IntoWalletDescriptor>(
|
||||
descriptor: E,
|
||||
change_descriptor: Option<E>,
|
||||
change_descriptor: E,
|
||||
db: impl PersistBackend<ChangeSet> + Send + Sync + 'static,
|
||||
network: Network,
|
||||
) -> Result<Self, NewError> {
|
||||
@ -412,7 +412,7 @@ impl Wallet {
|
||||
/// for syncing from alternative networks.
|
||||
pub fn new_with_genesis_hash<E: IntoWalletDescriptor>(
|
||||
descriptor: E,
|
||||
change_descriptor: Option<E>,
|
||||
change_descriptor: E,
|
||||
mut db: impl PersistBackend<ChangeSet> + Send + Sync + 'static,
|
||||
network: Network,
|
||||
genesis_hash: BlockHash,
|
||||
@ -524,7 +524,8 @@ impl Wallet {
|
||||
.indexer
|
||||
.keychains_added
|
||||
.get(&KeychainKind::Internal)
|
||||
.cloned();
|
||||
.ok_or(LoadError::MissingDescriptor)?
|
||||
.clone();
|
||||
|
||||
let (signers, change_signers) =
|
||||
create_signers(&mut index, &secp, descriptor, change_descriptor, network)
|
||||
@ -551,7 +552,7 @@ impl Wallet {
|
||||
/// This method will fail if the loaded [`Wallet`] has different parameters to those provided.
|
||||
pub fn new_or_load<E: IntoWalletDescriptor>(
|
||||
descriptor: E,
|
||||
change_descriptor: Option<E>,
|
||||
change_descriptor: E,
|
||||
db: impl PersistBackend<ChangeSet> + Send + Sync + 'static,
|
||||
network: Network,
|
||||
) -> Result<Self, NewOrLoadError> {
|
||||
@ -573,7 +574,7 @@ impl Wallet {
|
||||
/// useful for syncing from alternative networks.
|
||||
pub fn new_or_load_with_genesis_hash<E: IntoWalletDescriptor>(
|
||||
descriptor: E,
|
||||
change_descriptor: Option<E>,
|
||||
change_descriptor: E,
|
||||
mut db: impl PersistBackend<ChangeSet> + Send + Sync + 'static,
|
||||
network: Network,
|
||||
genesis_hash: BlockHash,
|
||||
@ -639,44 +640,32 @@ impl Wallet {
|
||||
});
|
||||
}
|
||||
|
||||
let expected_change_descriptor = if let Some(c) = change_descriptor {
|
||||
Some(
|
||||
c.into_wallet_descriptor(&wallet.secp, network)
|
||||
.map_err(NewOrLoadError::Descriptor)?,
|
||||
)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let (expected_change_descriptor, expected_change_descriptor_keymap) =
|
||||
change_descriptor
|
||||
.into_wallet_descriptor(&wallet.secp, network)
|
||||
.map_err(NewOrLoadError::Descriptor)?;
|
||||
let wallet_change_descriptor =
|
||||
wallet.public_descriptor(KeychainKind::Internal).cloned();
|
||||
|
||||
match (expected_change_descriptor, wallet_change_descriptor) {
|
||||
(Some((expected_descriptor, expected_keymap)), Some(wallet_descriptor))
|
||||
if wallet_descriptor == expected_descriptor =>
|
||||
{
|
||||
// if expected change descriptor has private keys add them as new signers
|
||||
if !expected_keymap.is_empty() {
|
||||
let signer_container = SignersContainer::build(
|
||||
expected_keymap,
|
||||
&expected_descriptor,
|
||||
&wallet.secp,
|
||||
);
|
||||
signer_container.signers().into_iter().for_each(|signer| {
|
||||
wallet.add_signer(
|
||||
KeychainKind::Internal,
|
||||
SignerOrdering::default(),
|
||||
signer.clone(),
|
||||
)
|
||||
});
|
||||
}
|
||||
}
|
||||
(None, None) => (),
|
||||
(_, wallet_descriptor) => {
|
||||
return Err(NewOrLoadError::LoadedDescriptorDoesNotMatch {
|
||||
got: wallet_descriptor,
|
||||
keychain: KeychainKind::Internal,
|
||||
});
|
||||
}
|
||||
if wallet_change_descriptor != Some(expected_change_descriptor.clone()) {
|
||||
return Err(NewOrLoadError::LoadedDescriptorDoesNotMatch {
|
||||
got: wallet_change_descriptor,
|
||||
keychain: KeychainKind::Internal,
|
||||
});
|
||||
}
|
||||
// if expected change descriptor has private keys add them as new signers
|
||||
if !expected_change_descriptor_keymap.is_empty() {
|
||||
let signer_container = SignersContainer::build(
|
||||
expected_change_descriptor_keymap,
|
||||
&expected_change_descriptor,
|
||||
&wallet.secp,
|
||||
);
|
||||
signer_container.signers().into_iter().for_each(|signer| {
|
||||
wallet.add_signer(
|
||||
KeychainKind::Internal,
|
||||
SignerOrdering::default(),
|
||||
signer.clone(),
|
||||
)
|
||||
});
|
||||
}
|
||||
|
||||
Ok(wallet)
|
||||
@ -717,12 +706,11 @@ impl Wallet {
|
||||
/// This panics when the caller requests for an address of derivation index greater than the
|
||||
/// [BIP32](https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki) max index.
|
||||
pub fn peek_address(&self, keychain: KeychainKind, mut index: u32) -> AddressInfo {
|
||||
let keychain = self.map_keychain(keychain);
|
||||
let mut spk_iter = self
|
||||
.indexed_graph
|
||||
.index
|
||||
.unbounded_spk_iter(&keychain)
|
||||
.expect("Must exist (we called map_keychain)");
|
||||
.expect("keychain must exist");
|
||||
if !spk_iter.descriptor().has_wildcard() {
|
||||
index = 0;
|
||||
}
|
||||
@ -748,12 +736,11 @@ impl Wallet {
|
||||
///
|
||||
/// If writing to persistent storage fails.
|
||||
pub fn reveal_next_address(&mut self, keychain: KeychainKind) -> anyhow::Result<AddressInfo> {
|
||||
let keychain = self.map_keychain(keychain);
|
||||
let ((index, spk), index_changeset) = self
|
||||
.indexed_graph
|
||||
.index
|
||||
.reveal_next_spk(&keychain)
|
||||
.expect("Must exist (we called map_keychain)");
|
||||
.expect("keychain must exist");
|
||||
|
||||
self.persist
|
||||
.stage_and_commit(indexed_tx_graph::ChangeSet::from(index_changeset).into())?;
|
||||
@ -780,12 +767,11 @@ impl Wallet {
|
||||
keychain: KeychainKind,
|
||||
index: u32,
|
||||
) -> anyhow::Result<impl Iterator<Item = AddressInfo> + '_> {
|
||||
let keychain = self.map_keychain(keychain);
|
||||
let (spk_iter, index_changeset) = self
|
||||
.indexed_graph
|
||||
.index
|
||||
.reveal_to_target(&keychain, index)
|
||||
.expect("must exist (we called map_keychain)");
|
||||
.expect("keychain must exist");
|
||||
|
||||
self.persist
|
||||
.stage_and_commit(indexed_tx_graph::ChangeSet::from(index_changeset).into())?;
|
||||
@ -807,12 +793,11 @@ impl Wallet {
|
||||
///
|
||||
/// If writing to persistent storage fails.
|
||||
pub fn next_unused_address(&mut self, keychain: KeychainKind) -> anyhow::Result<AddressInfo> {
|
||||
let keychain = self.map_keychain(keychain);
|
||||
let ((index, spk), index_changeset) = self
|
||||
.indexed_graph
|
||||
.index
|
||||
.next_unused_spk(&keychain)
|
||||
.expect("must exist (we called map_keychain)");
|
||||
.expect("keychain must exist");
|
||||
|
||||
self.persist
|
||||
.stage_and_commit(indexed_tx_graph::ChangeSet::from(index_changeset).into())?;
|
||||
@ -852,7 +837,6 @@ impl Wallet {
|
||||
&self,
|
||||
keychain: KeychainKind,
|
||||
) -> impl DoubleEndedIterator<Item = AddressInfo> + '_ {
|
||||
let keychain = self.map_keychain(keychain);
|
||||
self.indexed_graph
|
||||
.index
|
||||
.unused_keychain_spks(&keychain)
|
||||
@ -934,11 +918,10 @@ impl Wallet {
|
||||
&self,
|
||||
keychain: KeychainKind,
|
||||
) -> impl Iterator<Item = (u32, ScriptBuf)> + Clone {
|
||||
let keychain = self.map_keychain(keychain);
|
||||
self.indexed_graph
|
||||
.index
|
||||
.unbounded_spk_iter(&keychain)
|
||||
.expect("Must exist (we called map_keychain)")
|
||||
.expect("keychain must exist")
|
||||
}
|
||||
|
||||
/// Returns the utxo owned by this wallet corresponding to `outpoint` if it exists in the
|
||||
@ -1248,7 +1231,9 @@ impl Wallet {
|
||||
/// ```
|
||||
/// # use bdk_wallet::{Wallet, KeychainKind};
|
||||
/// # use bdk_wallet::bitcoin::Network;
|
||||
/// let wallet = Wallet::new_no_persist("wpkh(tprv8ZgxMBicQKsPe73PBRSmNbTfbcsZnwWhz5eVmhHpi31HW29Z7mc9B4cWGRQzopNUzZUT391DeDJxL2PefNunWyLgqCKRMDkU1s2s8bAfoSk/84'/0'/0'/0/*)", None, Network::Testnet)?;
|
||||
/// let descriptor = "wpkh(tprv8ZgxMBicQKsPe73PBRSmNbTfbcsZnwWhz5eVmhHpi31HW29Z7mc9B4cWGRQzopNUzZUT391DeDJxL2PefNunWyLgqCKRMDkU1s2s8bAfoSk/84'/1'/0'/0/*)";
|
||||
/// let change_descriptor = "wpkh(tprv8ZgxMBicQKsPe73PBRSmNbTfbcsZnwWhz5eVmhHpi31HW29Z7mc9B4cWGRQzopNUzZUT391DeDJxL2PefNunWyLgqCKRMDkU1s2s8bAfoSk/84'/1'/0'/1/*)";
|
||||
/// let wallet = Wallet::new_no_persist(descriptor, change_descriptor, Network::Testnet)?;
|
||||
/// 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/*
|
||||
/// println!("secret_key: {}", secret_key);
|
||||
@ -1307,20 +1292,14 @@ impl Wallet {
|
||||
) -> Result<Psbt, CreateTxError> {
|
||||
let keychains: BTreeMap<_, _> = self.indexed_graph.index.keychains().collect();
|
||||
let external_descriptor = keychains.get(&KeychainKind::External).expect("must exist");
|
||||
let internal_descriptor = keychains.get(&KeychainKind::Internal);
|
||||
let internal_descriptor = keychains.get(&KeychainKind::Internal).expect("must exist");
|
||||
|
||||
let external_policy = external_descriptor
|
||||
.extract_policy(&self.signers, BuildSatisfaction::None, &self.secp)?
|
||||
.unwrap();
|
||||
let internal_policy = internal_descriptor
|
||||
.as_ref()
|
||||
.map(|desc| {
|
||||
Ok::<_, CreateTxError>(
|
||||
desc.extract_policy(&self.change_signers, BuildSatisfaction::None, &self.secp)?
|
||||
.unwrap(),
|
||||
)
|
||||
})
|
||||
.transpose()?;
|
||||
.extract_policy(&self.change_signers, BuildSatisfaction::None, &self.secp)?
|
||||
.unwrap();
|
||||
|
||||
// The policy allows spending external outputs, but it requires a policy path that hasn't been
|
||||
// provided
|
||||
@ -1332,17 +1311,15 @@ impl Wallet {
|
||||
KeychainKind::External,
|
||||
));
|
||||
};
|
||||
// Same for the internal_policy path, if present
|
||||
if let Some(internal_policy) = &internal_policy {
|
||||
if params.change_policy != tx_builder::ChangeSpendPolicy::ChangeForbidden
|
||||
&& internal_policy.requires_path()
|
||||
&& params.internal_policy_path.is_none()
|
||||
{
|
||||
return Err(CreateTxError::SpendingPolicyRequired(
|
||||
KeychainKind::Internal,
|
||||
));
|
||||
};
|
||||
}
|
||||
// Same for the internal_policy path
|
||||
if params.change_policy != tx_builder::ChangeSpendPolicy::ChangeForbidden
|
||||
&& internal_policy.requires_path()
|
||||
&& params.internal_policy_path.is_none()
|
||||
{
|
||||
return Err(CreateTxError::SpendingPolicyRequired(
|
||||
KeychainKind::Internal,
|
||||
));
|
||||
};
|
||||
|
||||
let external_requirements = external_policy.get_condition(
|
||||
params
|
||||
@ -1350,21 +1327,14 @@ impl Wallet {
|
||||
.as_ref()
|
||||
.unwrap_or(&BTreeMap::new()),
|
||||
)?;
|
||||
let internal_requirements = internal_policy
|
||||
.map(|policy| {
|
||||
Ok::<_, CreateTxError>(
|
||||
policy.get_condition(
|
||||
params
|
||||
.internal_policy_path
|
||||
.as_ref()
|
||||
.unwrap_or(&BTreeMap::new()),
|
||||
)?,
|
||||
)
|
||||
})
|
||||
.transpose()?;
|
||||
let internal_requirements = internal_policy.get_condition(
|
||||
params
|
||||
.internal_policy_path
|
||||
.as_ref()
|
||||
.unwrap_or(&BTreeMap::new()),
|
||||
)?;
|
||||
|
||||
let requirements =
|
||||
external_requirements.merge(&internal_requirements.unwrap_or_default())?;
|
||||
let requirements = external_requirements.merge(&internal_requirements)?;
|
||||
|
||||
let version = match params.version {
|
||||
Some(tx_builder::Version(0)) => return Err(CreateTxError::Version0),
|
||||
@ -1526,12 +1496,6 @@ impl Wallet {
|
||||
|
||||
fee_amount += (fee_rate * tx.weight()).to_sat();
|
||||
|
||||
if params.change_policy != tx_builder::ChangeSpendPolicy::ChangeAllowed
|
||||
&& internal_descriptor.is_none()
|
||||
{
|
||||
return Err(CreateTxError::ChangePolicyDescriptor);
|
||||
}
|
||||
|
||||
let (required_utxos, optional_utxos) =
|
||||
self.preselect_utxos(¶ms, Some(current_height.to_consensus_u32()));
|
||||
|
||||
@ -1539,12 +1503,12 @@ impl Wallet {
|
||||
let drain_script = match params.drain_to {
|
||||
Some(ref drain_recipient) => drain_recipient.clone(),
|
||||
None => {
|
||||
let change_keychain = self.map_keychain(KeychainKind::Internal);
|
||||
let change_keychain = KeychainKind::Internal;
|
||||
let ((index, spk), index_changeset) = self
|
||||
.indexed_graph
|
||||
.index
|
||||
.next_unused_spk(&change_keychain)
|
||||
.expect("Keychain exists (we called map_keychain)");
|
||||
.expect("keychain must exist");
|
||||
let spk = spk.into();
|
||||
self.indexed_graph.index.mark_used(change_keychain, index);
|
||||
self.persist
|
||||
@ -1773,9 +1737,11 @@ impl Wallet {
|
||||
if tx.output.len() > 1 {
|
||||
let mut change_index = None;
|
||||
for (index, txout) in tx.output.iter().enumerate() {
|
||||
let change_type = self.map_keychain(KeychainKind::Internal);
|
||||
let change_keychain = KeychainKind::Internal;
|
||||
match txout_index.index_of_spk(&txout.script_pubkey) {
|
||||
Some((keychain, _)) if keychain == change_type => change_index = Some(index),
|
||||
Some((keychain, _)) if keychain == change_keychain => {
|
||||
change_index = Some(index)
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
@ -2014,8 +1980,7 @@ impl Wallet {
|
||||
|
||||
/// Returns the descriptor used to create addresses for a particular `keychain`.
|
||||
pub fn get_descriptor_for_keychain(&self, keychain: KeychainKind) -> &ExtendedDescriptor {
|
||||
self.public_descriptor(self.map_keychain(keychain))
|
||||
.expect("we mapped it to external if it doesn't exist")
|
||||
self.public_descriptor(keychain).expect("keychain exists")
|
||||
}
|
||||
|
||||
/// The derivation index of this wallet. It will return `None` if it has not derived any addresses.
|
||||
@ -2026,11 +1991,10 @@ impl Wallet {
|
||||
|
||||
/// The index of the next address that you would get if you were to ask the wallet for a new address
|
||||
pub fn next_derivation_index(&self, keychain: KeychainKind) -> u32 {
|
||||
let keychain = self.map_keychain(keychain);
|
||||
self.indexed_graph
|
||||
.index
|
||||
.next_index(&keychain)
|
||||
.expect("Keychain must exist (we called map_keychain)")
|
||||
.expect("keychain must exist")
|
||||
.0
|
||||
}
|
||||
|
||||
@ -2049,16 +2013,6 @@ impl Wallet {
|
||||
}
|
||||
}
|
||||
|
||||
fn map_keychain(&self, keychain: KeychainKind) -> KeychainKind {
|
||||
if keychain == KeychainKind::Internal
|
||||
&& self.public_descriptor(KeychainKind::Internal).is_none()
|
||||
{
|
||||
KeychainKind::External
|
||||
} else {
|
||||
keychain
|
||||
}
|
||||
}
|
||||
|
||||
fn get_descriptor_for_txout(&self, txout: &TxOut) -> Option<DerivedDescriptor> {
|
||||
let (keychain, child) = self
|
||||
.indexed_graph
|
||||
@ -2569,22 +2523,22 @@ fn create_signers<E: IntoWalletDescriptor>(
|
||||
index: &mut KeychainTxOutIndex<KeychainKind>,
|
||||
secp: &Secp256k1<All>,
|
||||
descriptor: E,
|
||||
change_descriptor: Option<E>,
|
||||
change_descriptor: E,
|
||||
network: Network,
|
||||
) -> Result<(Arc<SignersContainer>, Arc<SignersContainer>), crate::descriptor::error::Error> {
|
||||
let (descriptor, keymap) = into_wallet_descriptor_checked(descriptor, secp, network)?;
|
||||
) -> Result<(Arc<SignersContainer>, Arc<SignersContainer>), DescriptorError> {
|
||||
let descriptor = into_wallet_descriptor_checked(descriptor, secp, network)?;
|
||||
let change_descriptor = into_wallet_descriptor_checked(change_descriptor, secp, network)?;
|
||||
if descriptor.0 == change_descriptor.0 {
|
||||
return Err(DescriptorError::ExternalAndInternalAreTheSame);
|
||||
}
|
||||
|
||||
let (descriptor, keymap) = descriptor;
|
||||
let signers = Arc::new(SignersContainer::build(keymap, &descriptor, secp));
|
||||
let _ = index.insert_descriptor(KeychainKind::External, descriptor);
|
||||
|
||||
let change_signers = match change_descriptor {
|
||||
Some(descriptor) => {
|
||||
let (descriptor, keymap) = into_wallet_descriptor_checked(descriptor, secp, network)?;
|
||||
let signers = Arc::new(SignersContainer::build(keymap, &descriptor, secp));
|
||||
let _ = index.insert_descriptor(KeychainKind::Internal, descriptor);
|
||||
signers
|
||||
}
|
||||
None => Arc::new(SignersContainer::new()),
|
||||
};
|
||||
let (descriptor, keymap) = change_descriptor;
|
||||
let change_signers = Arc::new(SignersContainer::build(keymap, &descriptor, secp));
|
||||
let _ = index.insert_descriptor(KeychainKind::Internal, descriptor);
|
||||
|
||||
Ok((signers, change_signers))
|
||||
}
|
||||
@ -2613,7 +2567,7 @@ macro_rules! doctest_wallet {
|
||||
|
||||
let mut wallet = Wallet::new_no_persist(
|
||||
descriptor,
|
||||
Some(change_descriptor),
|
||||
change_descriptor,
|
||||
Network::Regtest,
|
||||
)
|
||||
.unwrap();
|
||||
|
@ -67,8 +67,9 @@
|
||||
//!
|
||||
//! let custom_signer = CustomSigner::connect();
|
||||
//!
|
||||
//! let descriptor = "wpkh(tpubD6NzVbkrYhZ4Xferm7Pz4VnjdcDPFyjVu5K4iZXQ4pVN8Cks4pHVowTBXBKRhX64pkRyJZJN5xAKj4UDNnLPb5p2sSKXhewoYx5GbTdUFWq/*)";
|
||||
//! let mut wallet = Wallet::new_no_persist(descriptor, None, Network::Testnet)?;
|
||||
//! let descriptor = "wpkh(tpubD6NzVbkrYhZ4Xferm7Pz4VnjdcDPFyjVu5K4iZXQ4pVN8Cks4pHVowTBXBKRhX64pkRyJZJN5xAKj4UDNnLPb5p2sSKXhewoYx5GbTdUFWq/0/*)";
|
||||
//! let change_descriptor = "wpkh(tpubD6NzVbkrYhZ4Xferm7Pz4VnjdcDPFyjVu5K4iZXQ4pVN8Cks4pHVowTBXBKRhX64pkRyJZJN5xAKj4UDNnLPb5p2sSKXhewoYx5GbTdUFWq/1/*)";
|
||||
//! let mut wallet = Wallet::new_no_persist(descriptor, change_descriptor, Network::Testnet)?;
|
||||
//! wallet.add_signer(
|
||||
//! KeychainKind::External,
|
||||
//! SignerOrdering(200),
|
||||
|
@ -15,12 +15,9 @@ use std::str::FromStr;
|
||||
// The funded wallet containing a tx with a 76_000 sats input and two outputs, one spending 25_000
|
||||
// to a foreign address and one returning 50_000 back to the wallet as change. The remaining 1000
|
||||
// sats are the transaction fee.
|
||||
pub fn get_funded_wallet_with_change(
|
||||
descriptor: &str,
|
||||
change: Option<&str>,
|
||||
) -> (Wallet, bitcoin::Txid) {
|
||||
pub fn get_funded_wallet_with_change(descriptor: &str, change: &str) -> (Wallet, bitcoin::Txid) {
|
||||
let mut wallet = Wallet::new_no_persist(descriptor, change, Network::Regtest).unwrap();
|
||||
let change_address = wallet.peek_address(KeychainKind::External, 0).address;
|
||||
let receive_address = wallet.peek_address(KeychainKind::External, 0).address;
|
||||
let sendto_address = Address::from_str("bcrt1q3qtze4ys45tgdvguj66zrk4fu6hq3a3v9pfly5")
|
||||
.expect("address")
|
||||
.require_network(Network::Regtest)
|
||||
@ -40,7 +37,7 @@ pub fn get_funded_wallet_with_change(
|
||||
}],
|
||||
output: vec![TxOut {
|
||||
value: Amount::from_sat(76_000),
|
||||
script_pubkey: change_address.script_pubkey(),
|
||||
script_pubkey: receive_address.script_pubkey(),
|
||||
}],
|
||||
};
|
||||
|
||||
@ -59,7 +56,7 @@ pub fn get_funded_wallet_with_change(
|
||||
output: vec![
|
||||
TxOut {
|
||||
value: Amount::from_sat(50_000),
|
||||
script_pubkey: change_address.script_pubkey(),
|
||||
script_pubkey: receive_address.script_pubkey(),
|
||||
},
|
||||
TxOut {
|
||||
value: Amount::from_sat(25_000),
|
||||
@ -108,13 +105,29 @@ pub fn get_funded_wallet_with_change(
|
||||
// to a foreign address and one returning 50_000 back to the wallet as change. The remaining 1000
|
||||
// sats are the transaction fee.
|
||||
pub fn get_funded_wallet(descriptor: &str) -> (Wallet, bitcoin::Txid) {
|
||||
get_funded_wallet_with_change(descriptor, None)
|
||||
let change = get_test_wpkh_change();
|
||||
get_funded_wallet_with_change(descriptor, change)
|
||||
}
|
||||
|
||||
pub fn get_funded_wallet_wpkh() -> (Wallet, bitcoin::Txid) {
|
||||
get_funded_wallet_with_change(get_test_wpkh(), get_test_wpkh_change())
|
||||
}
|
||||
|
||||
pub fn get_test_wpkh() -> &'static str {
|
||||
"wpkh(cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW)"
|
||||
}
|
||||
|
||||
pub fn get_test_wpkh_with_change_desc() -> (&'static str, &'static str) {
|
||||
(
|
||||
"wpkh(cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW)",
|
||||
get_test_wpkh_change(),
|
||||
)
|
||||
}
|
||||
|
||||
fn get_test_wpkh_change() -> &'static str {
|
||||
"wpkh(tprv8ZgxMBicQKsPdy6LMhUtFHAgpocR8GC6QmwMSFpZs7h6Eziw3SpThFfczTDh5rW2krkqffa11UpX3XkeTTB2FvzZKWXqPY54Y6Rq4AQ5R8L/84'/1'/0'/1/0)"
|
||||
}
|
||||
|
||||
pub fn get_test_single_sig_csv() -> &'static str {
|
||||
// and(pk(Alice),older(6))
|
||||
"wsh(and_v(v:pk(cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW),older(6)))"
|
||||
@ -150,6 +163,11 @@ pub fn get_test_tr_single_sig_xprv() -> &'static str {
|
||||
"tr(tprv8ZgxMBicQKsPdDArR4xSAECuVxeX1jwwSXR4ApKbkYgZiziDc4LdBy2WvJeGDfUSE4UT4hHhbgEwbdq8ajjUHiKDegkwrNU6V55CxcxonVN/*)"
|
||||
}
|
||||
|
||||
pub fn get_test_tr_single_sig_xprv_with_change_desc() -> (&'static str, &'static str) {
|
||||
("tr(tprv8ZgxMBicQKsPdDArR4xSAECuVxeX1jwwSXR4ApKbkYgZiziDc4LdBy2WvJeGDfUSE4UT4hHhbgEwbdq8ajjUHiKDegkwrNU6V55CxcxonVN/0/*)",
|
||||
"tr(tprv8ZgxMBicQKsPdDArR4xSAECuVxeX1jwwSXR4ApKbkYgZiziDc4LdBy2WvJeGDfUSE4UT4hHhbgEwbdq8ajjUHiKDegkwrNU6V55CxcxonVN/1/*)")
|
||||
}
|
||||
|
||||
pub fn get_test_tr_with_taptree_xprv() -> &'static str {
|
||||
"tr(cNJmN3fH9DDbDt131fQNkVakkpzawJBSeybCUNmP1BovpmGQ45xG,{pk(tprv8ZgxMBicQKsPdDArR4xSAECuVxeX1jwwSXR4ApKbkYgZiziDc4LdBy2WvJeGDfUSE4UT4hHhbgEwbdq8ajjUHiKDegkwrNU6V55CxcxonVN/*),pk(8aee2b8120a5f157f1223f72b5e62b825831a27a9fdf427db7cc697494d4a642)})"
|
||||
}
|
||||
|
@ -142,7 +142,9 @@ fn test_psbt_fee_rate_with_missing_txout() {
|
||||
assert!(wpkh_psbt.fee_amount().is_none());
|
||||
assert!(wpkh_psbt.fee_rate().is_none());
|
||||
|
||||
let (mut pkh_wallet, _) = get_funded_wallet("pkh(tprv8ZgxMBicQKsPd3EupYiPRhaMooHKUHJxNsTfYuScep13go8QFfHdtkG9nRkFGb7busX4isf6X9dURGCoKgitaApQ6MupRhZMcELAxTBRJgS/*)");
|
||||
let desc = "pkh(tprv8ZgxMBicQKsPd3EupYiPRhaMooHKUHJxNsTfYuScep13go8QFfHdtkG9nRkFGb7busX4isf6X9dURGCoKgitaApQ6MupRhZMcELAxTBRJgS/0)";
|
||||
let change_desc = "pkh(tprv8ZgxMBicQKsPd3EupYiPRhaMooHKUHJxNsTfYuScep13go8QFfHdtkG9nRkFGb7busX4isf6X9dURGCoKgitaApQ6MupRhZMcELAxTBRJgS/1)";
|
||||
let (mut pkh_wallet, _) = get_funded_wallet_with_change(desc, change_desc);
|
||||
let addr = pkh_wallet.peek_address(KeychainKind::External, 0);
|
||||
let mut builder = pkh_wallet.build_tx();
|
||||
builder.drain_to(addr.script_pubkey()).drain_wallet();
|
||||
@ -170,7 +172,8 @@ fn test_psbt_multiple_internalkey_signers() {
|
||||
let prv = PrivateKey::from_wif(wif).unwrap();
|
||||
let keypair = Keypair::from_secret_key(&secp, &prv.inner);
|
||||
|
||||
let (mut wallet, _) = get_funded_wallet(&desc);
|
||||
let change_desc = "tr(cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW)";
|
||||
let (mut wallet, _) = get_funded_wallet_with_change(&desc, change_desc);
|
||||
let to_spend = wallet.balance().total();
|
||||
let send_to = wallet.peek_address(KeychainKind::External, 0);
|
||||
let mut builder = wallet.build_tx();
|
||||
|
@ -7,7 +7,7 @@ use bdk_chain::COINBASE_MATURITY;
|
||||
use bdk_chain::{BlockId, ConfirmationTime};
|
||||
use bdk_persist::PersistBackend;
|
||||
use bdk_sqlite::rusqlite::Connection;
|
||||
use bdk_wallet::descriptor::{calc_checksum, IntoWalletDescriptor};
|
||||
use bdk_wallet::descriptor::{calc_checksum, DescriptorError, IntoWalletDescriptor};
|
||||
use bdk_wallet::psbt::PsbtUtils;
|
||||
use bdk_wallet::signer::{SignOptions, SignerError};
|
||||
use bdk_wallet::wallet::coin_selection::{self, LargestFirstCoinSelection};
|
||||
@ -81,12 +81,13 @@ fn load_recovers_wallet() -> anyhow::Result<()> {
|
||||
{
|
||||
let temp_dir = tempfile::tempdir().expect("must create tempdir");
|
||||
let file_path = temp_dir.path().join(filename);
|
||||
let (desc, change_desc) = get_test_tr_single_sig_xprv_with_change_desc();
|
||||
|
||||
// create new wallet
|
||||
let wallet_spk_index = {
|
||||
let db = create_new(&file_path).expect("must create db");
|
||||
let mut wallet = Wallet::new(get_test_tr_single_sig_xprv(), None, db, Network::Testnet)
|
||||
.expect("must init wallet");
|
||||
let mut wallet =
|
||||
Wallet::new(desc, change_desc, db, Network::Testnet).expect("must init wallet");
|
||||
|
||||
wallet.reveal_next_address(KeychainKind::External).unwrap();
|
||||
wallet.spk_index().clone()
|
||||
@ -108,8 +109,7 @@ fn load_recovers_wallet() -> anyhow::Result<()> {
|
||||
let secp = Secp256k1::new();
|
||||
assert_eq!(
|
||||
*wallet.get_descriptor_for_keychain(KeychainKind::External),
|
||||
get_test_tr_single_sig_xprv()
|
||||
.into_wallet_descriptor(&secp, wallet.network())
|
||||
desc.into_wallet_descriptor(&secp, wallet.network())
|
||||
.unwrap()
|
||||
.0
|
||||
);
|
||||
@ -118,7 +118,7 @@ fn load_recovers_wallet() -> anyhow::Result<()> {
|
||||
// `new` can only be called on empty db
|
||||
{
|
||||
let db = recover(&file_path).expect("must recover db");
|
||||
let result = Wallet::new(get_test_tr_single_sig_xprv(), None, db, Network::Testnet);
|
||||
let result = Wallet::new(desc, change_desc, db, Network::Testnet);
|
||||
assert!(matches!(result, Err(NewError::NonEmptyDatabase)));
|
||||
}
|
||||
|
||||
@ -148,11 +148,12 @@ fn new_or_load() -> anyhow::Result<()> {
|
||||
{
|
||||
let temp_dir = tempfile::tempdir().expect("must create tempdir");
|
||||
let file_path = temp_dir.path().join(filename);
|
||||
let (desc, change_desc) = get_test_wpkh_with_change_desc();
|
||||
|
||||
// init wallet when non-existent
|
||||
let wallet_keychains: BTreeMap<_, _> = {
|
||||
let db = new_or_load(&file_path).expect("must create db");
|
||||
let wallet = Wallet::new_or_load(get_test_wpkh(), None, db, Network::Testnet)
|
||||
let wallet = Wallet::new_or_load(desc, change_desc, db, Network::Testnet)
|
||||
.expect("must init wallet");
|
||||
wallet.keychains().map(|(k, v)| (*k, v.clone())).collect()
|
||||
};
|
||||
@ -160,7 +161,7 @@ fn new_or_load() -> anyhow::Result<()> {
|
||||
// wrong network
|
||||
{
|
||||
let db = new_or_load(&file_path).expect("must create db");
|
||||
let err = Wallet::new_or_load(get_test_wpkh(), None, db, Network::Bitcoin)
|
||||
let err = Wallet::new_or_load(desc, change_desc, db, Network::Bitcoin)
|
||||
.expect_err("wrong network");
|
||||
assert!(
|
||||
matches!(
|
||||
@ -183,8 +184,8 @@ fn new_or_load() -> anyhow::Result<()> {
|
||||
|
||||
let db = new_or_load(&file_path).expect("must open db");
|
||||
let err = Wallet::new_or_load_with_genesis_hash(
|
||||
get_test_wpkh(),
|
||||
None,
|
||||
desc,
|
||||
change_desc,
|
||||
db,
|
||||
Network::Testnet,
|
||||
exp_blockhash,
|
||||
@ -204,13 +205,13 @@ fn new_or_load() -> anyhow::Result<()> {
|
||||
// wrong external descriptor
|
||||
{
|
||||
let exp_descriptor = get_test_tr_single_sig();
|
||||
let got_descriptor = get_test_wpkh()
|
||||
let got_descriptor = desc
|
||||
.into_wallet_descriptor(&Secp256k1::new(), Network::Testnet)
|
||||
.unwrap()
|
||||
.0;
|
||||
|
||||
let db = new_or_load(&file_path).expect("must open db");
|
||||
let err = Wallet::new_or_load(exp_descriptor, None, db, Network::Testnet)
|
||||
let err = Wallet::new_or_load(exp_descriptor, change_desc, db, Network::Testnet)
|
||||
.expect_err("wrong external descriptor");
|
||||
assert!(
|
||||
matches!(
|
||||
@ -225,17 +226,20 @@ fn new_or_load() -> anyhow::Result<()> {
|
||||
|
||||
// wrong internal descriptor
|
||||
{
|
||||
let exp_descriptor = Some(get_test_tr_single_sig());
|
||||
let got_descriptor = None;
|
||||
let exp_descriptor = get_test_tr_single_sig();
|
||||
let got_descriptor = change_desc
|
||||
.into_wallet_descriptor(&Secp256k1::new(), Network::Testnet)
|
||||
.unwrap()
|
||||
.0;
|
||||
|
||||
let db = new_or_load(&file_path).expect("must open db");
|
||||
let err = Wallet::new_or_load(get_test_wpkh(), exp_descriptor, db, Network::Testnet)
|
||||
let err = Wallet::new_or_load(desc, exp_descriptor, db, Network::Testnet)
|
||||
.expect_err("wrong internal descriptor");
|
||||
assert!(
|
||||
matches!(
|
||||
err,
|
||||
bdk_wallet::wallet::NewOrLoadError::LoadedDescriptorDoesNotMatch { ref got, keychain }
|
||||
if got == &got_descriptor && keychain == KeychainKind::Internal
|
||||
if got == &Some(got_descriptor) && keychain == KeychainKind::Internal
|
||||
),
|
||||
"err: {}",
|
||||
err,
|
||||
@ -245,7 +249,7 @@ fn new_or_load() -> anyhow::Result<()> {
|
||||
// all parameters match
|
||||
{
|
||||
let db = new_or_load(&file_path).expect("must open db");
|
||||
let wallet = Wallet::new_or_load(get_test_wpkh(), None, db, Network::Testnet)
|
||||
let wallet = Wallet::new_or_load(desc, change_desc, db, Network::Testnet)
|
||||
.expect("must recover wallet");
|
||||
assert_eq!(wallet.network(), Network::Testnet);
|
||||
assert!(wallet
|
||||
@ -266,9 +270,31 @@ fn new_or_load() -> anyhow::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_error_external_and_internal_are_the_same() {
|
||||
// identical descriptors should fail to create wallet
|
||||
let desc = get_test_wpkh();
|
||||
let err = Wallet::new_no_persist(desc, desc, Network::Testnet);
|
||||
assert!(
|
||||
matches!(&err, Err(DescriptorError::ExternalAndInternalAreTheSame)),
|
||||
"expected same descriptors error, got {:?}",
|
||||
err,
|
||||
);
|
||||
|
||||
// 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 = Wallet::new_no_persist(desc, change_desc, Network::Testnet);
|
||||
assert!(
|
||||
matches!(err, Err(DescriptorError::ExternalAndInternalAreTheSame)),
|
||||
"expected same descriptors error, got {:?}",
|
||||
err,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_descriptor_checksum() {
|
||||
let (wallet, _) = get_funded_wallet(get_test_wpkh());
|
||||
let (wallet, _) = get_funded_wallet_wpkh();
|
||||
let checksum = wallet.descriptor_checksum(KeychainKind::External);
|
||||
assert_eq!(checksum.len(), 8);
|
||||
|
||||
@ -287,7 +313,7 @@ fn test_descriptor_checksum() {
|
||||
|
||||
#[test]
|
||||
fn test_get_funded_wallet_balance() {
|
||||
let (wallet, _) = get_funded_wallet(get_test_wpkh());
|
||||
let (wallet, _) = get_funded_wallet_wpkh();
|
||||
|
||||
// The funded wallet contains a tx with a 76_000 sats input and two outputs, one spending 25_000
|
||||
// to a foreign address and one returning 50_000 back to the wallet as change. The remaining 1000
|
||||
@ -297,7 +323,7 @@ fn test_get_funded_wallet_balance() {
|
||||
|
||||
#[test]
|
||||
fn test_get_funded_wallet_sent_and_received() {
|
||||
let (wallet, txid) = get_funded_wallet(get_test_wpkh());
|
||||
let (wallet, txid) = get_funded_wallet_wpkh();
|
||||
|
||||
let mut tx_amounts: Vec<(Txid, (Amount, Amount))> = wallet
|
||||
.transactions()
|
||||
@ -317,7 +343,7 @@ fn test_get_funded_wallet_sent_and_received() {
|
||||
|
||||
#[test]
|
||||
fn test_get_funded_wallet_tx_fees() {
|
||||
let (wallet, txid) = get_funded_wallet(get_test_wpkh());
|
||||
let (wallet, txid) = get_funded_wallet_wpkh();
|
||||
|
||||
let tx = wallet.get_tx(txid).expect("transaction").tx_node.tx;
|
||||
let tx_fee = wallet.calculate_fee(&tx).expect("transaction fee");
|
||||
@ -330,7 +356,7 @@ fn test_get_funded_wallet_tx_fees() {
|
||||
|
||||
#[test]
|
||||
fn test_get_funded_wallet_tx_fee_rate() {
|
||||
let (wallet, txid) = get_funded_wallet(get_test_wpkh());
|
||||
let (wallet, txid) = get_funded_wallet_wpkh();
|
||||
|
||||
let tx = wallet.get_tx(txid).expect("transaction").tx_node.tx;
|
||||
let tx_fee_rate = wallet
|
||||
@ -350,7 +376,7 @@ fn test_get_funded_wallet_tx_fee_rate() {
|
||||
|
||||
#[test]
|
||||
fn test_list_output() {
|
||||
let (wallet, txid) = get_funded_wallet(get_test_wpkh());
|
||||
let (wallet, txid) = get_funded_wallet_wpkh();
|
||||
let txos = wallet
|
||||
.list_output()
|
||||
.map(|op| (op.outpoint, op))
|
||||
@ -428,14 +454,14 @@ macro_rules! from_str {
|
||||
#[test]
|
||||
#[should_panic(expected = "NoRecipients")]
|
||||
fn test_create_tx_empty_recipients() {
|
||||
let (mut wallet, _) = get_funded_wallet(get_test_wpkh());
|
||||
let (mut wallet, _) = get_funded_wallet_wpkh();
|
||||
wallet.build_tx().finish().unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "NoUtxosSelected")]
|
||||
fn test_create_tx_manually_selected_empty_utxos() {
|
||||
let (mut wallet, _) = get_funded_wallet(get_test_wpkh());
|
||||
let (mut wallet, _) = get_funded_wallet_wpkh();
|
||||
let addr = wallet.next_unused_address(KeychainKind::External).unwrap();
|
||||
let mut builder = wallet.build_tx();
|
||||
builder
|
||||
@ -446,7 +472,7 @@ fn test_create_tx_manually_selected_empty_utxos() {
|
||||
|
||||
#[test]
|
||||
fn test_create_tx_version_0() {
|
||||
let (mut wallet, _) = get_funded_wallet(get_test_wpkh());
|
||||
let (mut wallet, _) = get_funded_wallet_wpkh();
|
||||
let addr = wallet.next_unused_address(KeychainKind::External).unwrap();
|
||||
let mut builder = wallet.build_tx();
|
||||
builder
|
||||
@ -468,7 +494,7 @@ fn test_create_tx_version_1_csv() {
|
||||
|
||||
#[test]
|
||||
fn test_create_tx_custom_version() {
|
||||
let (mut wallet, _) = get_funded_wallet(get_test_wpkh());
|
||||
let (mut wallet, _) = get_funded_wallet_wpkh();
|
||||
let addr = wallet.next_unused_address(KeychainKind::External).unwrap();
|
||||
let mut builder = wallet.build_tx();
|
||||
builder
|
||||
@ -481,7 +507,7 @@ fn test_create_tx_custom_version() {
|
||||
|
||||
#[test]
|
||||
fn test_create_tx_default_locktime_is_last_sync_height() {
|
||||
let (mut wallet, _) = get_funded_wallet(get_test_wpkh());
|
||||
let (mut wallet, _) = get_funded_wallet_wpkh();
|
||||
|
||||
let addr = wallet.next_unused_address(KeychainKind::External).unwrap();
|
||||
let mut builder = wallet.build_tx();
|
||||
@ -495,7 +521,7 @@ fn test_create_tx_default_locktime_is_last_sync_height() {
|
||||
|
||||
#[test]
|
||||
fn test_create_tx_fee_sniping_locktime_last_sync() {
|
||||
let (mut wallet, _) = get_funded_wallet(get_test_wpkh());
|
||||
let (mut wallet, _) = get_funded_wallet_wpkh();
|
||||
let addr = wallet.next_unused_address(KeychainKind::External).unwrap();
|
||||
let mut builder = wallet.build_tx();
|
||||
builder.add_recipient(addr.script_pubkey(), Amount::from_sat(25_000));
|
||||
@ -522,7 +548,7 @@ fn test_create_tx_default_locktime_cltv() {
|
||||
|
||||
#[test]
|
||||
fn test_create_tx_custom_locktime() {
|
||||
let (mut wallet, _) = get_funded_wallet(get_test_wpkh());
|
||||
let (mut wallet, _) = get_funded_wallet_wpkh();
|
||||
let addr = wallet.next_unused_address(KeychainKind::External).unwrap();
|
||||
let mut builder = wallet.build_tx();
|
||||
builder
|
||||
@ -614,7 +640,7 @@ fn test_create_tx_no_rbf_cltv() {
|
||||
|
||||
#[test]
|
||||
fn test_create_tx_invalid_rbf_sequence() {
|
||||
let (mut wallet, _) = get_funded_wallet(get_test_wpkh());
|
||||
let (mut wallet, _) = get_funded_wallet_wpkh();
|
||||
let addr = wallet.next_unused_address(KeychainKind::External).unwrap();
|
||||
let mut builder = wallet.build_tx();
|
||||
builder
|
||||
@ -625,7 +651,7 @@ fn test_create_tx_invalid_rbf_sequence() {
|
||||
|
||||
#[test]
|
||||
fn test_create_tx_custom_rbf_sequence() {
|
||||
let (mut wallet, _) = get_funded_wallet(get_test_wpkh());
|
||||
let (mut wallet, _) = get_funded_wallet_wpkh();
|
||||
let addr = wallet.next_unused_address(KeychainKind::External).unwrap();
|
||||
let mut builder = wallet.build_tx();
|
||||
builder
|
||||
@ -636,9 +662,33 @@ fn test_create_tx_custom_rbf_sequence() {
|
||||
assert_eq!(psbt.unsigned_tx.input[0].sequence, Sequence(0xDEADBEEF));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_create_tx_change_policy() {
|
||||
let (mut wallet, _) = get_funded_wallet_wpkh();
|
||||
let addr = wallet.next_unused_address(KeychainKind::External).unwrap();
|
||||
let mut builder = wallet.build_tx();
|
||||
builder
|
||||
.add_recipient(addr.script_pubkey(), Amount::from_sat(25_000))
|
||||
.do_not_spend_change();
|
||||
assert!(builder.finish().is_ok());
|
||||
|
||||
// wallet has no change, so setting `only_spend_change`
|
||||
// should cause tx building to fail
|
||||
let mut builder = wallet.build_tx();
|
||||
builder
|
||||
.add_recipient(addr.script_pubkey(), Amount::from_sat(25_000))
|
||||
.only_spend_change();
|
||||
assert!(matches!(
|
||||
builder.finish(),
|
||||
Err(CreateTxError::CoinSelection(
|
||||
coin_selection::Error::InsufficientFunds { .. }
|
||||
)),
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_create_tx_default_sequence() {
|
||||
let (mut wallet, _) = get_funded_wallet(get_test_wpkh());
|
||||
let (mut wallet, _) = get_funded_wallet_wpkh();
|
||||
let addr = wallet.next_unused_address(KeychainKind::External).unwrap();
|
||||
let mut builder = wallet.build_tx();
|
||||
builder.add_recipient(addr.script_pubkey(), Amount::from_sat(25_000));
|
||||
@ -647,20 +697,6 @@ fn test_create_tx_default_sequence() {
|
||||
assert_eq!(psbt.unsigned_tx.input[0].sequence, Sequence(0xFFFFFFFE));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_create_tx_change_policy_no_internal() {
|
||||
let (mut wallet, _) = get_funded_wallet(get_test_wpkh());
|
||||
let addr = wallet.next_unused_address(KeychainKind::External).unwrap();
|
||||
let mut builder = wallet.build_tx();
|
||||
builder
|
||||
.add_recipient(addr.script_pubkey(), Amount::from_sat(25_000))
|
||||
.do_not_spend_change();
|
||||
assert!(matches!(
|
||||
builder.finish(),
|
||||
Err(CreateTxError::ChangePolicyDescriptor)
|
||||
));
|
||||
}
|
||||
|
||||
macro_rules! check_fee {
|
||||
($wallet:expr, $psbt: expr) => {{
|
||||
let tx = $psbt.clone().extract_tx().expect("failed to extract tx");
|
||||
@ -672,7 +708,7 @@ macro_rules! check_fee {
|
||||
|
||||
#[test]
|
||||
fn test_create_tx_drain_wallet_and_drain_to() {
|
||||
let (mut wallet, _) = get_funded_wallet(get_test_wpkh());
|
||||
let (mut wallet, _) = get_funded_wallet_wpkh();
|
||||
let addr = wallet.next_unused_address(KeychainKind::External).unwrap();
|
||||
let mut builder = wallet.build_tx();
|
||||
builder.drain_to(addr.script_pubkey()).drain_wallet();
|
||||
@ -688,7 +724,7 @@ fn test_create_tx_drain_wallet_and_drain_to() {
|
||||
|
||||
#[test]
|
||||
fn test_create_tx_drain_wallet_and_drain_to_and_with_recipient() {
|
||||
let (mut wallet, _) = get_funded_wallet(get_test_wpkh());
|
||||
let (mut wallet, _) = get_funded_wallet_wpkh();
|
||||
let addr = Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt")
|
||||
.unwrap()
|
||||
.assume_checked();
|
||||
@ -720,7 +756,7 @@ fn test_create_tx_drain_wallet_and_drain_to_and_with_recipient() {
|
||||
|
||||
#[test]
|
||||
fn test_create_tx_drain_to_and_utxos() {
|
||||
let (mut wallet, _) = get_funded_wallet(get_test_wpkh());
|
||||
let (mut wallet, _) = get_funded_wallet_wpkh();
|
||||
let addr = wallet.next_unused_address(KeychainKind::External).unwrap();
|
||||
let utxos: Vec<_> = wallet.list_unspent().map(|u| u.outpoint).collect();
|
||||
let mut builder = wallet.build_tx();
|
||||
@ -741,7 +777,7 @@ fn test_create_tx_drain_to_and_utxos() {
|
||||
#[test]
|
||||
#[should_panic(expected = "NoRecipients")]
|
||||
fn test_create_tx_drain_to_no_drain_wallet_no_utxos() {
|
||||
let (mut wallet, _) = get_funded_wallet(get_test_wpkh());
|
||||
let (mut wallet, _) = get_funded_wallet_wpkh();
|
||||
let drain_addr = wallet.next_unused_address(KeychainKind::External).unwrap();
|
||||
let mut builder = wallet.build_tx();
|
||||
builder.drain_to(drain_addr.script_pubkey());
|
||||
@ -750,7 +786,7 @@ fn test_create_tx_drain_to_no_drain_wallet_no_utxos() {
|
||||
|
||||
#[test]
|
||||
fn test_create_tx_default_fee_rate() {
|
||||
let (mut wallet, _) = get_funded_wallet(get_test_wpkh());
|
||||
let (mut wallet, _) = get_funded_wallet_wpkh();
|
||||
let addr = wallet.next_unused_address(KeychainKind::External).unwrap();
|
||||
let mut builder = wallet.build_tx();
|
||||
builder.add_recipient(addr.script_pubkey(), Amount::from_sat(25_000));
|
||||
@ -762,7 +798,7 @@ fn test_create_tx_default_fee_rate() {
|
||||
|
||||
#[test]
|
||||
fn test_create_tx_custom_fee_rate() {
|
||||
let (mut wallet, _) = get_funded_wallet(get_test_wpkh());
|
||||
let (mut wallet, _) = get_funded_wallet_wpkh();
|
||||
let addr = wallet.next_unused_address(KeychainKind::External).unwrap();
|
||||
let mut builder = wallet.build_tx();
|
||||
builder
|
||||
@ -776,7 +812,7 @@ fn test_create_tx_custom_fee_rate() {
|
||||
|
||||
#[test]
|
||||
fn test_create_tx_absolute_fee() {
|
||||
let (mut wallet, _) = get_funded_wallet(get_test_wpkh());
|
||||
let (mut wallet, _) = get_funded_wallet_wpkh();
|
||||
let addr = wallet.next_unused_address(KeychainKind::External).unwrap();
|
||||
let mut builder = wallet.build_tx();
|
||||
builder
|
||||
@ -796,7 +832,7 @@ fn test_create_tx_absolute_fee() {
|
||||
|
||||
#[test]
|
||||
fn test_create_tx_absolute_zero_fee() {
|
||||
let (mut wallet, _) = get_funded_wallet(get_test_wpkh());
|
||||
let (mut wallet, _) = get_funded_wallet_wpkh();
|
||||
let addr = wallet.next_unused_address(KeychainKind::External).unwrap();
|
||||
let mut builder = wallet.build_tx();
|
||||
builder
|
||||
@ -817,7 +853,7 @@ fn test_create_tx_absolute_zero_fee() {
|
||||
#[test]
|
||||
#[should_panic(expected = "InsufficientFunds")]
|
||||
fn test_create_tx_absolute_high_fee() {
|
||||
let (mut wallet, _) = get_funded_wallet(get_test_wpkh());
|
||||
let (mut wallet, _) = get_funded_wallet_wpkh();
|
||||
let addr = wallet.next_unused_address(KeychainKind::External).unwrap();
|
||||
let mut builder = wallet.build_tx();
|
||||
builder
|
||||
@ -831,7 +867,7 @@ fn test_create_tx_absolute_high_fee() {
|
||||
fn test_create_tx_add_change() {
|
||||
use bdk_wallet::wallet::tx_builder::TxOrdering;
|
||||
|
||||
let (mut wallet, _) = get_funded_wallet(get_test_wpkh());
|
||||
let (mut wallet, _) = get_funded_wallet_wpkh();
|
||||
let addr = wallet.next_unused_address(KeychainKind::External).unwrap();
|
||||
let mut builder = wallet.build_tx();
|
||||
builder
|
||||
@ -850,7 +886,7 @@ fn test_create_tx_add_change() {
|
||||
|
||||
#[test]
|
||||
fn test_create_tx_skip_change_dust() {
|
||||
let (mut wallet, _) = get_funded_wallet(get_test_wpkh());
|
||||
let (mut wallet, _) = get_funded_wallet_wpkh();
|
||||
let addr = wallet.next_unused_address(KeychainKind::External).unwrap();
|
||||
let mut builder = wallet.build_tx();
|
||||
builder.add_recipient(addr.script_pubkey(), Amount::from_sat(49_800));
|
||||
@ -865,7 +901,7 @@ fn test_create_tx_skip_change_dust() {
|
||||
#[test]
|
||||
#[should_panic(expected = "InsufficientFunds")]
|
||||
fn test_create_tx_drain_to_dust_amount() {
|
||||
let (mut wallet, _) = get_funded_wallet(get_test_wpkh());
|
||||
let (mut wallet, _) = get_funded_wallet_wpkh();
|
||||
let addr = wallet.next_unused_address(KeychainKind::External).unwrap();
|
||||
// very high fee rate, so that the only output would be below dust
|
||||
let mut builder = wallet.build_tx();
|
||||
@ -878,7 +914,7 @@ fn test_create_tx_drain_to_dust_amount() {
|
||||
|
||||
#[test]
|
||||
fn test_create_tx_ordering_respected() {
|
||||
let (mut wallet, _) = get_funded_wallet(get_test_wpkh());
|
||||
let (mut wallet, _) = get_funded_wallet_wpkh();
|
||||
let addr = wallet.next_unused_address(KeychainKind::External).unwrap();
|
||||
let mut builder = wallet.build_tx();
|
||||
builder
|
||||
@ -899,7 +935,7 @@ fn test_create_tx_ordering_respected() {
|
||||
|
||||
#[test]
|
||||
fn test_create_tx_default_sighash() {
|
||||
let (mut wallet, _) = get_funded_wallet(get_test_wpkh());
|
||||
let (mut wallet, _) = get_funded_wallet_wpkh();
|
||||
let addr = wallet.next_unused_address(KeychainKind::External).unwrap();
|
||||
let mut builder = wallet.build_tx();
|
||||
builder.add_recipient(addr.script_pubkey(), Amount::from_sat(30_000));
|
||||
@ -910,7 +946,7 @@ fn test_create_tx_default_sighash() {
|
||||
|
||||
#[test]
|
||||
fn test_create_tx_custom_sighash() {
|
||||
let (mut wallet, _) = get_funded_wallet(get_test_wpkh());
|
||||
let (mut wallet, _) = get_funded_wallet_wpkh();
|
||||
let addr = wallet.next_unused_address(KeychainKind::External).unwrap();
|
||||
let mut builder = wallet.build_tx();
|
||||
builder
|
||||
@ -1088,7 +1124,7 @@ fn test_create_tx_both_non_witness_utxo_and_witness_utxo_default() {
|
||||
|
||||
#[test]
|
||||
fn test_create_tx_add_utxo() {
|
||||
let (mut wallet, _) = get_funded_wallet(get_test_wpkh());
|
||||
let (mut wallet, _) = get_funded_wallet_wpkh();
|
||||
let small_output_tx = Transaction {
|
||||
input: vec![],
|
||||
output: vec![TxOut {
|
||||
@ -1138,7 +1174,7 @@ fn test_create_tx_add_utxo() {
|
||||
#[test]
|
||||
#[should_panic(expected = "InsufficientFunds")]
|
||||
fn test_create_tx_manually_selected_insufficient() {
|
||||
let (mut wallet, _) = get_funded_wallet(get_test_wpkh());
|
||||
let (mut wallet, _) = get_funded_wallet_wpkh();
|
||||
let small_output_tx = Transaction {
|
||||
input: vec![],
|
||||
output: vec![TxOut {
|
||||
@ -1189,8 +1225,8 @@ fn test_create_tx_policy_path_required() {
|
||||
|
||||
#[test]
|
||||
fn test_create_tx_policy_path_no_csv() {
|
||||
let descriptors = get_test_wpkh();
|
||||
let mut wallet = Wallet::new_no_persist(descriptors, None, Network::Regtest).unwrap();
|
||||
let (desc, change_desc) = get_test_wpkh_with_change_desc();
|
||||
let mut wallet = Wallet::new_no_persist(desc, change_desc, Network::Regtest).unwrap();
|
||||
|
||||
let tx = Transaction {
|
||||
version: transaction::Version::non_standard(0),
|
||||
@ -1284,13 +1320,13 @@ fn test_create_tx_global_xpubs_with_origin() {
|
||||
let fingerprint = bip32::Fingerprint::from_hex("73756c7f").unwrap();
|
||||
let path = bip32::DerivationPath::from_str("m/48'/0'/0'/2'").unwrap();
|
||||
|
||||
assert_eq!(psbt.xpub.len(), 1);
|
||||
assert_eq!(psbt.xpub.len(), 2);
|
||||
assert_eq!(psbt.xpub.get(&key), Some(&(fingerprint, path)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_add_foreign_utxo() {
|
||||
let (mut wallet1, _) = get_funded_wallet(get_test_wpkh());
|
||||
let (mut wallet1, _) = get_funded_wallet_wpkh();
|
||||
let (wallet2, _) =
|
||||
get_funded_wallet("wpkh(cVbZ8ovhye9AoAHFsqobCf7LxbXDAECy9Kb8TZdfsDYMZGBUyCnm)");
|
||||
|
||||
@ -1366,7 +1402,7 @@ fn test_add_foreign_utxo() {
|
||||
expected = "MissingTxOut([OutPoint { txid: 0x21d7fb1bceda00ab4069fc52d06baa13470803e9050edd16f5736e5d8c4925fd, vout: 0 }])"
|
||||
)]
|
||||
fn test_calculate_fee_with_missing_foreign_utxo() {
|
||||
let (mut wallet1, _) = get_funded_wallet(get_test_wpkh());
|
||||
let (mut wallet1, _) = get_funded_wallet_wpkh();
|
||||
let (wallet2, _) =
|
||||
get_funded_wallet("wpkh(cVbZ8ovhye9AoAHFsqobCf7LxbXDAECy9Kb8TZdfsDYMZGBUyCnm)");
|
||||
|
||||
@ -1397,7 +1433,7 @@ fn test_calculate_fee_with_missing_foreign_utxo() {
|
||||
|
||||
#[test]
|
||||
fn test_add_foreign_utxo_invalid_psbt_input() {
|
||||
let (mut wallet, _) = get_funded_wallet(get_test_wpkh());
|
||||
let (mut wallet, _) = get_funded_wallet_wpkh();
|
||||
let outpoint = wallet.list_unspent().next().expect("must exist").outpoint;
|
||||
let foreign_utxo_satisfaction = wallet
|
||||
.get_descriptor_for_keychain(KeychainKind::External)
|
||||
@ -1412,7 +1448,7 @@ fn test_add_foreign_utxo_invalid_psbt_input() {
|
||||
|
||||
#[test]
|
||||
fn test_add_foreign_utxo_where_outpoint_doesnt_match_psbt_input() {
|
||||
let (mut wallet1, txid1) = get_funded_wallet(get_test_wpkh());
|
||||
let (mut wallet1, txid1) = get_funded_wallet_wpkh();
|
||||
let (wallet2, txid2) =
|
||||
get_funded_wallet("wpkh(cVbZ8ovhye9AoAHFsqobCf7LxbXDAECy9Kb8TZdfsDYMZGBUyCnm)");
|
||||
|
||||
@ -1456,7 +1492,7 @@ fn test_add_foreign_utxo_where_outpoint_doesnt_match_psbt_input() {
|
||||
|
||||
#[test]
|
||||
fn test_add_foreign_utxo_only_witness_utxo() {
|
||||
let (mut wallet1, _) = get_funded_wallet(get_test_wpkh());
|
||||
let (mut wallet1, _) = get_funded_wallet_wpkh();
|
||||
let (wallet2, txid2) =
|
||||
get_funded_wallet("wpkh(cVbZ8ovhye9AoAHFsqobCf7LxbXDAECy9Kb8TZdfsDYMZGBUyCnm)");
|
||||
let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX")
|
||||
@ -1523,7 +1559,7 @@ fn test_add_foreign_utxo_only_witness_utxo() {
|
||||
#[test]
|
||||
fn test_get_psbt_input() {
|
||||
// this should grab a known good utxo and set the input
|
||||
let (wallet, _) = get_funded_wallet(get_test_wpkh());
|
||||
let (wallet, _) = get_funded_wallet_wpkh();
|
||||
for utxo in wallet.list_unspent() {
|
||||
let psbt_input = wallet.get_psbt_input(utxo, None, false).unwrap();
|
||||
assert!(psbt_input.witness_utxo.is_some() || psbt_input.non_witness_utxo.is_some());
|
||||
@ -1560,7 +1596,7 @@ fn test_create_tx_global_xpubs_master_without_origin() {
|
||||
let key = bip32::Xpub::from_str("tpubD6NzVbkrYhZ4Y55A58Gv9RSNF5hy84b5AJqYy7sCcjFrkcLpPre8kmgfit6kY1Zs3BLgeypTDBZJM222guPpdz7Cup5yzaMu62u7mYGbwFL").unwrap();
|
||||
let fingerprint = bip32::Fingerprint::from_hex("997a323b").unwrap();
|
||||
|
||||
assert_eq!(psbt.xpub.len(), 1);
|
||||
assert_eq!(psbt.xpub.len(), 2);
|
||||
assert_eq!(
|
||||
psbt.xpub.get(&key),
|
||||
Some(&(fingerprint, bip32::DerivationPath::default()))
|
||||
@ -1570,7 +1606,7 @@ fn test_create_tx_global_xpubs_master_without_origin() {
|
||||
#[test]
|
||||
#[should_panic(expected = "IrreplaceableTransaction")]
|
||||
fn test_bump_fee_irreplaceable_tx() {
|
||||
let (mut wallet, _) = get_funded_wallet(get_test_wpkh());
|
||||
let (mut wallet, _) = get_funded_wallet_wpkh();
|
||||
let addr = wallet.next_unused_address(KeychainKind::External).unwrap();
|
||||
let mut builder = wallet.build_tx();
|
||||
builder.add_recipient(addr.script_pubkey(), Amount::from_sat(25_000));
|
||||
@ -1587,7 +1623,7 @@ fn test_bump_fee_irreplaceable_tx() {
|
||||
#[test]
|
||||
#[should_panic(expected = "TransactionConfirmed")]
|
||||
fn test_bump_fee_confirmed_tx() {
|
||||
let (mut wallet, _) = get_funded_wallet(get_test_wpkh());
|
||||
let (mut wallet, _) = get_funded_wallet_wpkh();
|
||||
let addr = wallet.next_unused_address(KeychainKind::External).unwrap();
|
||||
let mut builder = wallet.build_tx();
|
||||
builder.add_recipient(addr.script_pubkey(), Amount::from_sat(25_000));
|
||||
@ -1611,7 +1647,7 @@ fn test_bump_fee_confirmed_tx() {
|
||||
|
||||
#[test]
|
||||
fn test_bump_fee_low_fee_rate() {
|
||||
let (mut wallet, _) = get_funded_wallet(get_test_wpkh());
|
||||
let (mut wallet, _) = get_funded_wallet_wpkh();
|
||||
let addr = wallet.next_unused_address(KeychainKind::External).unwrap();
|
||||
let mut builder = wallet.build_tx();
|
||||
builder
|
||||
@ -1645,7 +1681,7 @@ fn test_bump_fee_low_fee_rate() {
|
||||
#[test]
|
||||
#[should_panic(expected = "FeeTooLow")]
|
||||
fn test_bump_fee_low_abs() {
|
||||
let (mut wallet, _) = get_funded_wallet(get_test_wpkh());
|
||||
let (mut wallet, _) = get_funded_wallet_wpkh();
|
||||
let addr = wallet.next_unused_address(KeychainKind::External).unwrap();
|
||||
let mut builder = wallet.build_tx();
|
||||
builder
|
||||
@ -1668,7 +1704,7 @@ fn test_bump_fee_low_abs() {
|
||||
#[test]
|
||||
#[should_panic(expected = "FeeTooLow")]
|
||||
fn test_bump_fee_zero_abs() {
|
||||
let (mut wallet, _) = get_funded_wallet(get_test_wpkh());
|
||||
let (mut wallet, _) = get_funded_wallet_wpkh();
|
||||
let addr = wallet.next_unused_address(KeychainKind::External).unwrap();
|
||||
let mut builder = wallet.build_tx();
|
||||
builder
|
||||
@ -1689,7 +1725,7 @@ fn test_bump_fee_zero_abs() {
|
||||
|
||||
#[test]
|
||||
fn test_bump_fee_reduce_change() {
|
||||
let (mut wallet, _) = get_funded_wallet(get_test_wpkh());
|
||||
let (mut wallet, _) = get_funded_wallet_wpkh();
|
||||
let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX")
|
||||
.unwrap()
|
||||
.assume_checked();
|
||||
@ -1788,7 +1824,7 @@ fn test_bump_fee_reduce_change() {
|
||||
|
||||
#[test]
|
||||
fn test_bump_fee_reduce_single_recipient() {
|
||||
let (mut wallet, _) = get_funded_wallet(get_test_wpkh());
|
||||
let (mut wallet, _) = get_funded_wallet_wpkh();
|
||||
let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX")
|
||||
.unwrap()
|
||||
.assume_checked();
|
||||
@ -1836,7 +1872,7 @@ fn test_bump_fee_reduce_single_recipient() {
|
||||
|
||||
#[test]
|
||||
fn test_bump_fee_absolute_reduce_single_recipient() {
|
||||
let (mut wallet, _) = get_funded_wallet(get_test_wpkh());
|
||||
let (mut wallet, _) = get_funded_wallet_wpkh();
|
||||
let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX")
|
||||
.unwrap()
|
||||
.assume_checked();
|
||||
@ -1882,7 +1918,7 @@ fn test_bump_fee_absolute_reduce_single_recipient() {
|
||||
|
||||
#[test]
|
||||
fn test_bump_fee_drain_wallet() {
|
||||
let (mut wallet, _) = get_funded_wallet(get_test_wpkh());
|
||||
let (mut wallet, _) = get_funded_wallet_wpkh();
|
||||
// receive an extra tx so that our wallet has two utxos.
|
||||
let tx = Transaction {
|
||||
version: transaction::Version::ONE,
|
||||
@ -1944,7 +1980,7 @@ fn test_bump_fee_drain_wallet() {
|
||||
#[test]
|
||||
#[should_panic(expected = "InsufficientFunds")]
|
||||
fn test_bump_fee_remove_output_manually_selected_only() {
|
||||
let (mut wallet, _) = get_funded_wallet(get_test_wpkh());
|
||||
let (mut wallet, _) = get_funded_wallet_wpkh();
|
||||
// receive an extra tx so that our wallet has two utxos. then we manually pick only one of
|
||||
// them, and make sure that `bump_fee` doesn't try to add more. This fails because we've
|
||||
// told the wallet it's not allowed to add more inputs AND it can't reduce the value of the
|
||||
@ -2006,7 +2042,7 @@ fn test_bump_fee_remove_output_manually_selected_only() {
|
||||
|
||||
#[test]
|
||||
fn test_bump_fee_add_input() {
|
||||
let (mut wallet, _) = get_funded_wallet(get_test_wpkh());
|
||||
let (mut wallet, _) = get_funded_wallet_wpkh();
|
||||
let init_tx = Transaction {
|
||||
version: transaction::Version::ONE,
|
||||
lock_time: absolute::LockTime::ZERO,
|
||||
@ -2083,7 +2119,7 @@ fn test_bump_fee_add_input() {
|
||||
|
||||
#[test]
|
||||
fn test_bump_fee_absolute_add_input() {
|
||||
let (mut wallet, _) = get_funded_wallet(get_test_wpkh());
|
||||
let (mut wallet, _) = get_funded_wallet_wpkh();
|
||||
receive_output_in_latest_block(&mut wallet, 25_000);
|
||||
let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX")
|
||||
.unwrap()
|
||||
@ -2141,7 +2177,7 @@ fn test_bump_fee_absolute_add_input() {
|
||||
|
||||
#[test]
|
||||
fn test_bump_fee_no_change_add_input_and_change() {
|
||||
let (mut wallet, _) = get_funded_wallet(get_test_wpkh());
|
||||
let (mut wallet, _) = get_funded_wallet_wpkh();
|
||||
let op = receive_output_in_latest_block(&mut wallet, 25_000);
|
||||
|
||||
// initially make a tx without change by using `drain_to`
|
||||
@ -2210,7 +2246,7 @@ fn test_bump_fee_no_change_add_input_and_change() {
|
||||
|
||||
#[test]
|
||||
fn test_bump_fee_add_input_change_dust() {
|
||||
let (mut wallet, _) = get_funded_wallet(get_test_wpkh());
|
||||
let (mut wallet, _) = get_funded_wallet_wpkh();
|
||||
receive_output_in_latest_block(&mut wallet, 25_000);
|
||||
let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX")
|
||||
.unwrap()
|
||||
@ -2286,7 +2322,7 @@ fn test_bump_fee_add_input_change_dust() {
|
||||
|
||||
#[test]
|
||||
fn test_bump_fee_force_add_input() {
|
||||
let (mut wallet, _) = get_funded_wallet(get_test_wpkh());
|
||||
let (mut wallet, _) = get_funded_wallet_wpkh();
|
||||
let incoming_op = receive_output_in_latest_block(&mut wallet, 25_000);
|
||||
|
||||
let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX")
|
||||
@ -2352,7 +2388,7 @@ fn test_bump_fee_force_add_input() {
|
||||
|
||||
#[test]
|
||||
fn test_bump_fee_absolute_force_add_input() {
|
||||
let (mut wallet, _) = get_funded_wallet(get_test_wpkh());
|
||||
let (mut wallet, _) = get_funded_wallet_wpkh();
|
||||
let incoming_op = receive_output_in_latest_block(&mut wallet, 25_000);
|
||||
|
||||
let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX")
|
||||
@ -2427,7 +2463,7 @@ fn test_bump_fee_unconfirmed_inputs_only() {
|
||||
// So, we fail with "InsufficientFunds", as per RBF rule 2:
|
||||
// The replacement transaction may only include an unconfirmed input
|
||||
// if that input was included in one of the original transactions.
|
||||
let (mut wallet, _) = get_funded_wallet(get_test_wpkh());
|
||||
let (mut wallet, _) = get_funded_wallet_wpkh();
|
||||
let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX")
|
||||
.unwrap()
|
||||
.assume_checked();
|
||||
@ -2464,7 +2500,7 @@ fn test_bump_fee_unconfirmed_input() {
|
||||
// (BIP125 rule 2 only apply to newly added unconfirmed input, you can
|
||||
// always fee bump with an unconfirmed input if it was included in the
|
||||
// original transaction)
|
||||
let (mut wallet, _) = get_funded_wallet(get_test_wpkh());
|
||||
let (mut wallet, _) = get_funded_wallet_wpkh();
|
||||
let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX")
|
||||
.unwrap()
|
||||
.assume_checked();
|
||||
@ -2508,7 +2544,7 @@ fn test_fee_amount_negative_drain_val() {
|
||||
// This caused a bug in master where we would calculate the wrong fee
|
||||
// for a transaction.
|
||||
// See https://github.com/bitcoindevkit/bdk/issues/660
|
||||
let (mut wallet, _) = get_funded_wallet(get_test_wpkh());
|
||||
let (mut wallet, _) = get_funded_wallet_wpkh();
|
||||
let send_to = Address::from_str("tb1ql7w62elx9ucw4pj5lgw4l028hmuw80sndtntxt")
|
||||
.unwrap()
|
||||
.assume_checked();
|
||||
@ -2625,7 +2661,9 @@ fn test_sign_single_xprv_no_hd_keypaths() {
|
||||
|
||||
#[test]
|
||||
fn test_include_output_redeem_witness_script() {
|
||||
let (mut wallet, _) = get_funded_wallet("sh(wsh(multi(1,cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW,cRjo6jqfVNP33HhSS76UhXETZsGTZYx8FMFvR9kpbtCSV1PmdZdu)))");
|
||||
let desc = get_test_wpkh();
|
||||
let change_desc = "sh(wsh(multi(1,cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW,cRjo6jqfVNP33HhSS76UhXETZsGTZYx8FMFvR9kpbtCSV1PmdZdu)))";
|
||||
let (mut wallet, _) = get_funded_wallet_with_change(desc, change_desc);
|
||||
let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX")
|
||||
.unwrap()
|
||||
.assume_checked();
|
||||
@ -2644,7 +2682,7 @@ fn test_include_output_redeem_witness_script() {
|
||||
|
||||
#[test]
|
||||
fn test_signing_only_one_of_multiple_inputs() {
|
||||
let (mut wallet, _) = get_funded_wallet(get_test_wpkh());
|
||||
let (mut wallet, _) = get_funded_wallet_wpkh();
|
||||
let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX")
|
||||
.unwrap()
|
||||
.assume_checked();
|
||||
@ -2800,8 +2838,9 @@ fn test_sign_nonstandard_sighash() {
|
||||
|
||||
#[test]
|
||||
fn test_unused_address() {
|
||||
let mut wallet = Wallet::new_no_persist("wpkh(tpubEBr4i6yk5nf5DAaJpsi9N2pPYBeJ7fZ5Z9rmN4977iYLCGco1VyjB9tvvuvYtfZzjD5A8igzgw3HeWeeKFmanHYqksqZXYXGsw5zjnj7KM9/*)",
|
||||
None, Network::Testnet).unwrap();
|
||||
let desc = "wpkh(tpubEBr4i6yk5nf5DAaJpsi9N2pPYBeJ7fZ5Z9rmN4977iYLCGco1VyjB9tvvuvYtfZzjD5A8igzgw3HeWeeKFmanHYqksqZXYXGsw5zjnj7KM9/*)";
|
||||
let change_desc = get_test_wpkh();
|
||||
let mut wallet = Wallet::new_no_persist(desc, change_desc, Network::Testnet).unwrap();
|
||||
|
||||
// `list_unused_addresses` should be empty if we haven't revealed any
|
||||
assert!(wallet
|
||||
@ -2829,7 +2868,8 @@ fn test_unused_address() {
|
||||
#[test]
|
||||
fn test_next_unused_address() {
|
||||
let descriptor = "wpkh(tpubEBr4i6yk5nf5DAaJpsi9N2pPYBeJ7fZ5Z9rmN4977iYLCGco1VyjB9tvvuvYtfZzjD5A8igzgw3HeWeeKFmanHYqksqZXYXGsw5zjnj7KM9/*)";
|
||||
let mut wallet = Wallet::new_no_persist(descriptor, None, Network::Testnet).unwrap();
|
||||
let change = get_test_wpkh();
|
||||
let mut wallet = Wallet::new_no_persist(descriptor, change, Network::Testnet).unwrap();
|
||||
assert_eq!(wallet.derivation_index(KeychainKind::External), None);
|
||||
|
||||
assert_eq!(
|
||||
@ -2877,8 +2917,9 @@ fn test_next_unused_address() {
|
||||
|
||||
#[test]
|
||||
fn test_peek_address_at_index() {
|
||||
let mut wallet = Wallet::new_no_persist("wpkh(tpubEBr4i6yk5nf5DAaJpsi9N2pPYBeJ7fZ5Z9rmN4977iYLCGco1VyjB9tvvuvYtfZzjD5A8igzgw3HeWeeKFmanHYqksqZXYXGsw5zjnj7KM9/*)",
|
||||
None, Network::Testnet).unwrap();
|
||||
let desc = "wpkh(tpubEBr4i6yk5nf5DAaJpsi9N2pPYBeJ7fZ5Z9rmN4977iYLCGco1VyjB9tvvuvYtfZzjD5A8igzgw3HeWeeKFmanHYqksqZXYXGsw5zjnj7KM9/*)";
|
||||
let change_desc = get_test_wpkh();
|
||||
let mut wallet = Wallet::new_no_persist(desc, change_desc, Network::Testnet).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
wallet.peek_address(KeychainKind::External, 1).to_string(),
|
||||
@ -2916,7 +2957,7 @@ fn test_peek_address_at_index() {
|
||||
#[test]
|
||||
fn test_peek_address_at_index_not_derivable() {
|
||||
let wallet = Wallet::new_no_persist("wpkh(tpubEBr4i6yk5nf5DAaJpsi9N2pPYBeJ7fZ5Z9rmN4977iYLCGco1VyjB9tvvuvYtfZzjD5A8igzgw3HeWeeKFmanHYqksqZXYXGsw5zjnj7KM9/1)",
|
||||
None, Network::Testnet).unwrap();
|
||||
get_test_wpkh(), Network::Testnet).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
wallet.peek_address(KeychainKind::External, 1).to_string(),
|
||||
@ -2937,7 +2978,7 @@ fn test_peek_address_at_index_not_derivable() {
|
||||
#[test]
|
||||
fn test_returns_index_and_address() {
|
||||
let mut wallet = Wallet::new_no_persist("wpkh(tpubEBr4i6yk5nf5DAaJpsi9N2pPYBeJ7fZ5Z9rmN4977iYLCGco1VyjB9tvvuvYtfZzjD5A8igzgw3HeWeeKFmanHYqksqZXYXGsw5zjnj7KM9/*)",
|
||||
None, Network::Testnet).unwrap();
|
||||
get_test_wpkh(), Network::Testnet).unwrap();
|
||||
|
||||
// new index 0
|
||||
assert_eq!(
|
||||
@ -2990,7 +3031,7 @@ fn test_returns_index_and_address() {
|
||||
|
||||
#[test]
|
||||
fn test_sending_to_bip350_bech32m_address() {
|
||||
let (mut wallet, _) = get_funded_wallet(get_test_wpkh());
|
||||
let (mut wallet, _) = get_funded_wallet_wpkh();
|
||||
let addr = Address::from_str("tb1pqqqqp399et2xygdj5xreqhjjvcmzhxw4aywxecjdzew6hylgvsesf3hn0c")
|
||||
.unwrap()
|
||||
.assume_checked();
|
||||
@ -3005,7 +3046,7 @@ fn test_get_address() {
|
||||
let key = bitcoin::bip32::Xpriv::from_str("tprv8ZgxMBicQKsPcx5nBGsR63Pe8KnRUqmbJNENAfGftF3yuXoMMoVJJcYeUw5eVkm9WBPjWYt6HMWYJNesB5HaNVBaFc1M6dRjWSYnmewUMYy").unwrap();
|
||||
let wallet = Wallet::new_no_persist(
|
||||
Bip84(key, KeychainKind::External),
|
||||
Some(Bip84(key, KeychainKind::Internal)),
|
||||
Bip84(key, KeychainKind::Internal),
|
||||
Network::Regtest,
|
||||
)
|
||||
.unwrap();
|
||||
@ -3031,27 +3072,12 @@ fn test_get_address() {
|
||||
keychain: KeychainKind::Internal,
|
||||
}
|
||||
);
|
||||
|
||||
let wallet =
|
||||
Wallet::new_no_persist(Bip84(key, KeychainKind::External), None, Network::Regtest).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
wallet.peek_address(KeychainKind::Internal, 0),
|
||||
AddressInfo {
|
||||
index: 0,
|
||||
address: Address::from_str("bcrt1qrhgaqu0zvf5q2d0gwwz04w0dh0cuehhqvzpp4w")
|
||||
.unwrap()
|
||||
.assume_checked(),
|
||||
keychain: KeychainKind::External,
|
||||
},
|
||||
"when there's no internal descriptor it should just use external"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_reveal_addresses() {
|
||||
let desc = get_test_tr_single_sig_xprv();
|
||||
let mut wallet = Wallet::new_no_persist(desc, None, Network::Signet).unwrap();
|
||||
let (desc, change_desc) = get_test_tr_single_sig_xprv_with_change_desc();
|
||||
let mut wallet = Wallet::new_no_persist(desc, change_desc, Network::Signet).unwrap();
|
||||
let keychain = KeychainKind::External;
|
||||
|
||||
let last_revealed_addr = wallet
|
||||
@ -3071,13 +3097,17 @@ fn test_reveal_addresses() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_address_no_reuse_single_descriptor() {
|
||||
fn test_get_address_no_reuse() {
|
||||
use bdk_wallet::descriptor::template::Bip84;
|
||||
use std::collections::HashSet;
|
||||
|
||||
let key = bitcoin::bip32::Xpriv::from_str("tprv8ZgxMBicQKsPcx5nBGsR63Pe8KnRUqmbJNENAfGftF3yuXoMMoVJJcYeUw5eVkm9WBPjWYt6HMWYJNesB5HaNVBaFc1M6dRjWSYnmewUMYy").unwrap();
|
||||
let mut wallet =
|
||||
Wallet::new_no_persist(Bip84(key, KeychainKind::External), None, Network::Regtest).unwrap();
|
||||
let mut wallet = Wallet::new_no_persist(
|
||||
Bip84(key, KeychainKind::External),
|
||||
Bip84(key, KeychainKind::Internal),
|
||||
Network::Regtest,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let mut used_set = HashSet::new();
|
||||
|
||||
@ -3124,11 +3154,12 @@ fn test_taproot_remove_tapfields_after_finalize_sign_option() {
|
||||
|
||||
#[test]
|
||||
fn test_taproot_psbt_populate_tap_key_origins() {
|
||||
let (mut wallet, _) = get_funded_wallet(get_test_tr_single_sig_xprv());
|
||||
let (desc, change_desc) = get_test_tr_single_sig_xprv_with_change_desc();
|
||||
let (mut wallet, _) = get_funded_wallet_with_change(desc, change_desc);
|
||||
let addr = wallet.reveal_next_address(KeychainKind::External).unwrap();
|
||||
|
||||
let mut builder = wallet.build_tx();
|
||||
builder.add_recipient(addr.script_pubkey(), Amount::from_sat(25_000));
|
||||
builder.drain_to(addr.script_pubkey()).drain_wallet();
|
||||
let psbt = builder.finish().unwrap();
|
||||
|
||||
assert_eq!(
|
||||
@ -3138,8 +3169,8 @@ fn test_taproot_psbt_populate_tap_key_origins() {
|
||||
.into_iter()
|
||||
.collect::<Vec<_>>(),
|
||||
vec![(
|
||||
from_str!("b96d3a3dc76a4fc74e976511b23aecb78e0754c23c0ed7a6513e18cbbc7178e9"),
|
||||
(vec![], (from_str!("f6a5cb8b"), from_str!("m/0")))
|
||||
from_str!("0841db1dbaf949dbbda893e01a18f2cca9179cf8ea2d8e667857690502b06483"),
|
||||
(vec![], (from_str!("f6a5cb8b"), from_str!("m/0/0")))
|
||||
)],
|
||||
"Wrong input tap_key_origins"
|
||||
);
|
||||
@ -3150,8 +3181,8 @@ fn test_taproot_psbt_populate_tap_key_origins() {
|
||||
.into_iter()
|
||||
.collect::<Vec<_>>(),
|
||||
vec![(
|
||||
from_str!("e9b03068cf4a2621d4f81e68f6c4216e6bd260fe6edf6acc55c8d8ae5aeff0a8"),
|
||||
(vec![], (from_str!("f6a5cb8b"), from_str!("m/1")))
|
||||
from_str!("9187c1e80002d19ddde9c5c7f5394e9a063cee8695867b58815af0562695ca21"),
|
||||
(vec![], (from_str!("f6a5cb8b"), from_str!("m/0/1")))
|
||||
)],
|
||||
"Wrong output tap_key_origins"
|
||||
);
|
||||
@ -3159,7 +3190,8 @@ fn test_taproot_psbt_populate_tap_key_origins() {
|
||||
|
||||
#[test]
|
||||
fn test_taproot_psbt_populate_tap_key_origins_repeated_key() {
|
||||
let (mut wallet, _) = get_funded_wallet(get_test_tr_repeated_key());
|
||||
let (mut wallet, _) =
|
||||
get_funded_wallet_with_change(get_test_tr_repeated_key(), get_test_tr_single_sig());
|
||||
let addr = wallet.reveal_next_address(KeychainKind::External).unwrap();
|
||||
|
||||
let path = vec![("rn4nre9c".to_string(), vec![0])]
|
||||
@ -3168,7 +3200,8 @@ fn test_taproot_psbt_populate_tap_key_origins_repeated_key() {
|
||||
|
||||
let mut builder = wallet.build_tx();
|
||||
builder
|
||||
.add_recipient(addr.script_pubkey(), Amount::from_sat(25_000))
|
||||
.drain_to(addr.script_pubkey())
|
||||
.drain_wallet()
|
||||
.policy_path(path, KeychainKind::External);
|
||||
let psbt = builder.finish().unwrap();
|
||||
|
||||
@ -3332,7 +3365,7 @@ fn test_taproot_sign_using_non_witness_utxo() {
|
||||
|
||||
#[test]
|
||||
fn test_taproot_foreign_utxo() {
|
||||
let (mut wallet1, _) = get_funded_wallet(get_test_wpkh());
|
||||
let (mut wallet1, _) = get_funded_wallet_wpkh();
|
||||
let (wallet2, _) = get_funded_wallet(get_test_tr_single_sig());
|
||||
|
||||
let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX")
|
||||
@ -3575,8 +3608,12 @@ 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 =
|
||||
Wallet::new_no_persist(get_test_tr_single_sig_xprv(), None, Network::Regtest).unwrap();
|
||||
let wallet_empty = Wallet::new_no_persist(
|
||||
get_test_tr_single_sig_xprv(),
|
||||
get_test_tr_single_sig(),
|
||||
Network::Regtest,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// signing with an empty db means that we will only look at the psbt to infer the
|
||||
// derivation index
|
||||
@ -3675,8 +3712,8 @@ fn test_taproot_sign_non_default_sighash() {
|
||||
|
||||
#[test]
|
||||
fn test_spend_coinbase() {
|
||||
let descriptor = get_test_wpkh();
|
||||
let mut wallet = Wallet::new_no_persist(descriptor, None, Network::Regtest).unwrap();
|
||||
let (desc, change_desc) = get_test_wpkh_with_change_desc();
|
||||
let mut wallet = Wallet::new_no_persist(desc, change_desc, Network::Regtest).unwrap();
|
||||
|
||||
let confirmation_height = 5;
|
||||
wallet
|
||||
@ -3937,7 +3974,7 @@ fn test_tx_cancellation() {
|
||||
}
|
||||
|
||||
let (mut wallet, _) =
|
||||
get_funded_wallet_with_change(get_test_wpkh(), Some(get_test_tr_single_sig_xprv()));
|
||||
get_funded_wallet_with_change(get_test_wpkh(), get_test_tr_single_sig_xprv());
|
||||
|
||||
let psbt1 = new_tx!(wallet);
|
||||
let change_derivation_1 = psbt1
|
||||
|
@ -23,7 +23,7 @@ fn main() -> Result<(), anyhow::Error> {
|
||||
|
||||
let mut wallet = Wallet::new_or_load(
|
||||
external_descriptor,
|
||||
Some(internal_descriptor),
|
||||
internal_descriptor,
|
||||
db,
|
||||
Network::Testnet,
|
||||
)?;
|
||||
|
@ -22,7 +22,7 @@ async fn main() -> Result<(), anyhow::Error> {
|
||||
|
||||
let mut wallet = Wallet::new_or_load(
|
||||
external_descriptor,
|
||||
Some(internal_descriptor),
|
||||
internal_descriptor,
|
||||
db,
|
||||
Network::Signet,
|
||||
)?;
|
||||
|
@ -21,7 +21,7 @@ fn main() -> Result<(), anyhow::Error> {
|
||||
|
||||
let mut wallet = Wallet::new_or_load(
|
||||
external_descriptor,
|
||||
Some(internal_descriptor),
|
||||
internal_descriptor,
|
||||
db,
|
||||
Network::Testnet,
|
||||
)?;
|
||||
|
@ -25,7 +25,7 @@ pub struct Args {
|
||||
pub descriptor: String,
|
||||
/// Wallet change descriptor
|
||||
#[clap(env = "CHANGE_DESCRIPTOR")]
|
||||
pub change_descriptor: Option<String>,
|
||||
pub change_descriptor: String,
|
||||
/// Earliest block height to start sync from
|
||||
#[clap(env = "START_HEIGHT", long, default_value = "481824")]
|
||||
pub start_height: u32,
|
||||
@ -88,7 +88,7 @@ fn main() -> anyhow::Result<()> {
|
||||
let start_load_wallet = Instant::now();
|
||||
let mut wallet = Wallet::new_or_load(
|
||||
&args.descriptor,
|
||||
args.change_descriptor.as_ref(),
|
||||
&args.change_descriptor,
|
||||
Store::<bdk_wallet::wallet::ChangeSet>::open_or_create_new(
|
||||
DB_MAGIC.as_bytes(),
|
||||
args.db_path,
|
||||
|
Loading…
x
Reference in New Issue
Block a user