/// 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 {
/// 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> {
/// 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> {
/// 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,
.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)
/// 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> {
/// 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,
});
}
- 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)
/// 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;
}
///
/// 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())?;
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())?;
///
/// 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())?;
&self,
keychain: KeychainKind,
) -> impl DoubleEndedIterator<Item = AddressInfo> + '_ {
- let keychain = self.map_keychain(keychain);
self.indexed_graph
.index
.unused_keychain_spks(&keychain)
&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
/// ```
/// # 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);
) -> 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
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
.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),
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()));
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
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)
+ }
_ => {}
}
}
/// 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.
/// 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
}
}
}
- 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
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))
}
let mut wallet = Wallet::new_no_persist(
descriptor,
- Some(change_descriptor),
+ change_descriptor,
Network::Regtest,
)
.unwrap();
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};
{
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()
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
);
// `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)));
}
{
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()
};
// 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!(
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,
// 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!(
// 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,
// 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
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);
#[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
#[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()
#[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");
#[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
#[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))
#[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
#[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
#[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
#[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();
#[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));
#[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
#[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
#[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
}
#[test]
-fn test_create_tx_default_sequence() {
- let (mut wallet, _) = get_funded_wallet(get_test_wpkh());
+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));
- let psbt = builder.finish().unwrap();
-
- assert_eq!(psbt.unsigned_tx.input[0].sequence, Sequence(0xFFFFFFFE));
-}
+ builder
+ .add_recipient(addr.script_pubkey(), Amount::from_sat(25_000))
+ .do_not_spend_change();
+ assert!(builder.finish().is_ok());
-#[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();
+ // 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))
- .do_not_spend_change();
+ .only_spend_change();
assert!(matches!(
builder.finish(),
- Err(CreateTxError::ChangePolicyDescriptor)
+ Err(CreateTxError::CoinSelection(
+ coin_selection::Error::InsufficientFunds { .. }
+ )),
));
}
+#[test]
+fn test_create_tx_default_sequence() {
+ 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));
+ let psbt = builder.finish().unwrap();
+
+ assert_eq!(psbt.unsigned_tx.input[0].sequence, Sequence(0xFFFFFFFE));
+}
+
macro_rules! check_fee {
($wallet:expr, $psbt: expr) => {{
let tx = $psbt.clone().extract_tx().expect("failed to extract tx");
#[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();
#[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();
#[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();
#[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());
#[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));
#[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
#[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
#[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
#[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
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
#[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));
#[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();
#[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
#[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));
#[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
#[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 {
#[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 {
#[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),
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)");
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)");
#[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)
#[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)");
#[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")
#[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());
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()))
#[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));
#[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));
#[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
#[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
#[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
#[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();
#[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();
#[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();
#[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,
#[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
#[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,
#[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()
#[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`
#[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()
#[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")
#[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")
// 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();
// (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();
// 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();
#[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();
#[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();
#[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
#[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!(
#[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(),
#[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(),
#[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!(
#[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();
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();
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
}
#[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();
#[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!(
.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"
);
.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"
);
#[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])]
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();
#[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")
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
#[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
}
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