From d3763e5e37569cea0860217fba980ab0389e4e64 Mon Sep 17 00:00:00 2001 From: valued mammal Date: Wed, 10 Apr 2024 15:16:40 -0400 Subject: [PATCH 1/4] feat(wallet): Add new address methods Introduce a new API for getting addresses from the Wallet that reflects a similiar interface as the underlying indexer `KeychainTxOutIndex` in preparation for removing `AddressIndex` enum. Before this change, the only way to get an address was via the methods `try_get{_internal}_address` which required a `&mut` reference to the wallet, matching on the desired AddressIndex variant. This is too restrictive since for example peeking or listing unused addresses shouldn't change the state of the wallet. Hence we provide separate methods for each use case which makes for a more efficient API. --- crates/bdk/src/wallet/mod.rs | 155 +++++++++++++++++++++++++++++++++++ 1 file changed, 155 insertions(+) diff --git a/crates/bdk/src/wallet/mod.rs b/crates/bdk/src/wallet/mod.rs index 8f823652..c608bae7 100644 --- a/crates/bdk/src/wallet/mod.rs +++ b/crates/bdk/src/wallet/mod.rs @@ -768,6 +768,161 @@ impl Wallet { }) } + /// Peek an address of the given `keychain` at `index` without revealing it. + /// + /// For non-wildcard descriptors this returns the same address at every provided index. + /// + /// # Panics + /// + /// 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); + if !spk_iter.descriptor().has_wildcard() { + index = 0; + } + let (index, spk) = spk_iter + .nth(index as usize) + .expect("derivation index is out of bounds"); + + AddressInfo { + index, + address: Address::from_script(&spk, self.network).expect("must have address form"), + keychain, + } + } + + /// Attempt to reveal the next address of the given `keychain`. + /// + /// This will increment the internal derivation index. If the keychain's descriptor doesn't + /// contain a wildcard or every address is already revealed up to the maximum derivation + /// index defined in [BIP32](https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki), + /// then returns the last revealed address. + /// + /// # Errors + /// + /// If writing to persistent storage fails. + pub fn reveal_next_address( + &mut self, + keychain: KeychainKind, + ) -> Result + where + D: PersistBackend, + { + let keychain = self.map_keychain(keychain); + let ((index, spk), index_changeset) = self.indexed_graph.index.reveal_next_spk(&keychain); + + self.persist + .stage_and_commit(indexed_tx_graph::ChangeSet::from(index_changeset).into())?; + + Ok(AddressInfo { + index, + address: Address::from_script(spk, self.network).expect("must have address form"), + keychain, + }) + } + + /// Reveal addresses up to and including the target `index` and return an iterator + /// of newly revealed addresses. + /// + /// If the target `index` is unreachable, we make a best effort to reveal up to the last + /// possible index. If all addresses up to the given `index` are already revealed, then + /// no new addresses are returned. + /// + /// # Errors + /// + /// If writing to persistent storage fails. + pub fn reveal_addresses_to( + &mut self, + keychain: KeychainKind, + index: u32, + ) -> Result + '_, D::WriteError> + where + D: PersistBackend, + { + let keychain = self.map_keychain(keychain); + let (spk_iter, index_changeset) = + self.indexed_graph.index.reveal_to_target(&keychain, index); + + self.persist + .stage_and_commit(indexed_tx_graph::ChangeSet::from(index_changeset).into())?; + + Ok(spk_iter.map(move |(index, spk)| AddressInfo { + index, + address: Address::from_script(&spk, self.network).expect("must have address form"), + keychain, + })) + } + + /// Get the next unused address for the given `keychain`, i.e. the address with the lowest + /// derivation index that hasn't been used. + /// + /// This will attempt to derive and reveal a new address if no newly revealed addresses + /// are available. See also [`reveal_next_address`](Self::reveal_next_address). + /// + /// # Errors + /// + /// If writing to persistent storage fails. + pub fn next_unused_address( + &mut self, + keychain: KeychainKind, + ) -> Result + where + D: PersistBackend, + { + let keychain = self.map_keychain(keychain); + let ((index, spk), index_changeset) = self.indexed_graph.index.next_unused_spk(&keychain); + + self.persist + .stage_and_commit(indexed_tx_graph::ChangeSet::from(index_changeset).into())?; + + Ok(AddressInfo { + index, + address: Address::from_script(spk, self.network).expect("must have address form"), + keychain, + }) + } + + /// Marks an address used of the given `keychain` at `index`. + /// + /// Returns whether the given index was present and then removed from the unused set. + pub fn mark_used(&mut self, keychain: KeychainKind, index: u32) -> bool { + self.indexed_graph.index.mark_used(keychain, index) + } + + /// Undoes the effect of [`mark_used`] and returns whether the `index` was inserted + /// back into the unused set. + /// + /// Since this is only a superficial marker, it will have no effect if the address at the given + /// `index` was actually used, i.e. the wallet has previously indexed a tx output for the + /// derived spk. + /// + /// [`mark_used`]: Self::mark_used + pub fn unmark_used(&mut self, keychain: KeychainKind, index: u32) -> bool { + self.indexed_graph.index.unmark_used(keychain, index) + } + + /// List addresses that are revealed but unused. + /// + /// Note if the returned iterator is empty you can reveal more addresses + /// by using [`reveal_next_address`](Self::reveal_next_address) or + /// [`reveal_addresses_to`](Self::reveal_addresses_to). + pub fn list_unused_addresses( + &self, + keychain: KeychainKind, + ) -> impl DoubleEndedIterator + '_ { + let keychain = self.map_keychain(keychain); + self.indexed_graph + .index + .unused_keychain_spks(&keychain) + .map(move |(index, spk)| AddressInfo { + index, + address: Address::from_script(spk, self.network).expect("must have address form"), + keychain, + }) + } + /// Return whether or not a `script` is part of this wallet (either internal or external) pub fn is_mine(&self, script: &Script) -> bool { self.indexed_graph.index.index_of_spk(script).is_some() From d87874780b4a4c7be282381ae1102c028f3ae581 Mon Sep 17 00:00:00 2001 From: valued mammal Date: Sun, 14 Apr 2024 10:57:48 -0400 Subject: [PATCH 2/4] refactor(wallet)!: Remove method get_address As this is now made redundant by the newly added wallet address methods. --- crates/bdk/examples/compiler.rs | 3 +- crates/bdk/src/descriptor/template.rs | 48 ++--- crates/bdk/src/wallet/mod.rs | 57 +---- crates/bdk/tests/common.rs | 4 +- crates/bdk/tests/psbt.rs | 22 +- crates/bdk/tests/wallet.rs | 286 +++++++++++++++----------- 6 files changed, 214 insertions(+), 206 deletions(-) diff --git a/crates/bdk/examples/compiler.rs b/crates/bdk/examples/compiler.rs index e22ccadc..0dbe1dd2 100644 --- a/crates/bdk/examples/compiler.rs +++ b/crates/bdk/examples/compiler.rs @@ -21,7 +21,6 @@ use bitcoin::Network; use miniscript::policy::Concrete; use miniscript::Descriptor; -use bdk::wallet::AddressIndex::New; use bdk::{KeychainKind, Wallet}; /// Miniscript policy is a high level abstraction of spending conditions. Defined in the @@ -51,7 +50,7 @@ fn main() -> Result<(), Box> { println!( "First derived address from the descriptor: \n{}", - wallet.get_address(New) + wallet.next_unused_address(KeychainKind::External)?, ); // BDK also has it's own `Policy` structure to represent the spending condition in a more diff --git a/crates/bdk/src/descriptor/template.rs b/crates/bdk/src/descriptor/template.rs index 6f51139a..61f9c4f2 100644 --- a/crates/bdk/src/descriptor/template.rs +++ b/crates/bdk/src/descriptor/template.rs @@ -74,7 +74,7 @@ impl IntoWalletDescriptor for T { /// ``` /// # use bdk::bitcoin::{PrivateKey, Network}; /// # use bdk::Wallet; -/// # use bdk::wallet::AddressIndex::New; +/// # use bdk::KeychainKind; /// use bdk::template::P2Pkh; /// /// let key = @@ -82,7 +82,9 @@ impl IntoWalletDescriptor for T { /// let mut wallet = Wallet::new_no_persist(P2Pkh(key), None, Network::Testnet)?; /// /// assert_eq!( -/// wallet.get_address(New).to_string(), +/// wallet +/// .next_unused_address(KeychainKind::External)? +/// .to_string(), /// "mwJ8hxFYW19JLuc65RCTaP4v1rzVU8cVMT" /// ); /// # Ok::<_, Box>(()) @@ -102,15 +104,17 @@ impl> DescriptorTemplate for P2Pkh { /// ``` /// # use bdk::bitcoin::{PrivateKey, Network}; /// # use bdk::Wallet; +/// # use bdk::KeychainKind; /// use bdk::template::P2Wpkh_P2Sh; -/// use bdk::wallet::AddressIndex; /// /// let key = /// bitcoin::PrivateKey::from_wif("cTc4vURSzdx6QE6KVynWGomDbLaA75dNALMNyfjh3p8DRRar84Um")?; /// let mut wallet = Wallet::new_no_persist(P2Wpkh_P2Sh(key), None, Network::Testnet)?; /// /// assert_eq!( -/// wallet.get_address(AddressIndex::New).to_string(), +/// wallet +/// .next_unused_address(KeychainKind::External)? +/// .to_string(), /// "2NB4ox5VDRw1ecUv6SnT3VQHPXveYztRqk5" /// ); /// # Ok::<_, Box>(()) @@ -131,15 +135,17 @@ impl> DescriptorTemplate for P2Wpkh_P2Sh { /// ``` /// # use bdk::bitcoin::{PrivateKey, Network}; /// # use bdk::{Wallet}; +/// # use bdk::KeychainKind; /// use bdk::template::P2Wpkh; -/// use bdk::wallet::AddressIndex::New; /// /// let key = /// bitcoin::PrivateKey::from_wif("cTc4vURSzdx6QE6KVynWGomDbLaA75dNALMNyfjh3p8DRRar84Um")?; /// let mut wallet = Wallet::new_no_persist(P2Wpkh(key), None, Network::Testnet)?; /// /// assert_eq!( -/// wallet.get_address(New).to_string(), +/// wallet +/// .next_unused_address(KeychainKind::External)? +/// .to_string(), /// "tb1q4525hmgw265tl3drrl8jjta7ayffu6jf68ltjd" /// ); /// # Ok::<_, Box>(()) @@ -159,7 +165,7 @@ impl> DescriptorTemplate for P2Wpkh { /// ``` /// # use bdk::bitcoin::{PrivateKey, Network}; /// # use bdk::Wallet; -/// # use bdk::wallet::AddressIndex::New; +/// # use bdk::KeychainKind; /// use bdk::template::P2TR; /// /// let key = @@ -167,7 +173,9 @@ impl> DescriptorTemplate for P2Wpkh { /// let mut wallet = Wallet::new_no_persist(P2TR(key), None, Network::Testnet)?; /// /// assert_eq!( -/// wallet.get_address(New).to_string(), +/// wallet +/// .next_unused_address(KeychainKind::External)? +/// .to_string(), /// "tb1pvjf9t34fznr53u5tqhejz4nr69luzkhlvsdsdfq9pglutrpve2xq7hps46" /// ); /// # Ok::<_, Box>(()) @@ -192,7 +200,6 @@ impl> DescriptorTemplate for P2TR { /// # use std::str::FromStr; /// # use bdk::bitcoin::{PrivateKey, Network}; /// # use bdk::{Wallet, KeychainKind}; -/// # use bdk::wallet::AddressIndex::New; /// use bdk::template::Bip44; /// /// let key = bitcoin::bip32::Xpriv::from_str("tprv8ZgxMBicQKsPeZRHk4rTG6orPS2CRNFX3njhUXx5vj9qGog5ZMH4uGReDWN5kCkY3jmWEtWause41CDvBRXD1shKknAMKxT99o9qUTRVC6m")?; @@ -202,7 +209,7 @@ impl> DescriptorTemplate for P2TR { /// Network::Testnet, /// )?; /// -/// assert_eq!(wallet.get_address(New).to_string(), "mmogjc7HJEZkrLqyQYqJmxUqFaC7i4uf89"); +/// assert_eq!(wallet.next_unused_address(KeychainKind::External)?.to_string(), "mmogjc7HJEZkrLqyQYqJmxUqFaC7i4uf89"); /// assert_eq!(wallet.public_descriptor(KeychainKind::External).unwrap().to_string(), "pkh([c55b303f/44'/1'/0']tpubDCuorCpzvYS2LCD75BR46KHE8GdDeg1wsAgNZeNr6DaB5gQK1o14uErKwKLuFmeemkQ6N2m3rNgvctdJLyr7nwu2yia7413Hhg8WWE44cgT/0/*)#5wrnv0xt"); /// # Ok::<_, Box>(()) /// ``` @@ -229,7 +236,6 @@ impl> DescriptorTemplate for Bip44 { /// # use std::str::FromStr; /// # use bdk::bitcoin::{PrivateKey, Network}; /// # use bdk::{Wallet, KeychainKind}; -/// # use bdk::wallet::AddressIndex::New; /// use bdk::template::Bip44Public; /// /// let key = bitcoin::bip32::Xpub::from_str("tpubDDDzQ31JkZB7VxUr9bjvBivDdqoFLrDPyLWtLapArAi51ftfmCb2DPxwLQzX65iNcXz1DGaVvyvo6JQ6rTU73r2gqdEo8uov9QKRb7nKCSU")?; @@ -240,7 +246,7 @@ impl> DescriptorTemplate for Bip44 { /// Network::Testnet, /// )?; /// -/// assert_eq!(wallet.get_address(New).to_string(), "miNG7dJTzJqNbFS19svRdTCisC65dsubtR"); +/// assert_eq!(wallet.next_unused_address(KeychainKind::External)?.to_string(), "miNG7dJTzJqNbFS19svRdTCisC65dsubtR"); /// assert_eq!(wallet.public_descriptor(KeychainKind::External).unwrap().to_string(), "pkh([c55b303f/44'/1'/0']tpubDDDzQ31JkZB7VxUr9bjvBivDdqoFLrDPyLWtLapArAi51ftfmCb2DPxwLQzX65iNcXz1DGaVvyvo6JQ6rTU73r2gqdEo8uov9QKRb7nKCSU/0/*)#cfhumdqz"); /// # Ok::<_, Box>(()) /// ``` @@ -267,7 +273,6 @@ impl> DescriptorTemplate for Bip44Public { /// # use std::str::FromStr; /// # use bdk::bitcoin::{PrivateKey, Network}; /// # use bdk::{Wallet, KeychainKind}; -/// # use bdk::wallet::AddressIndex::New; /// use bdk::template::Bip49; /// /// let key = bitcoin::bip32::Xpriv::from_str("tprv8ZgxMBicQKsPeZRHk4rTG6orPS2CRNFX3njhUXx5vj9qGog5ZMH4uGReDWN5kCkY3jmWEtWause41CDvBRXD1shKknAMKxT99o9qUTRVC6m")?; @@ -277,7 +282,7 @@ impl> DescriptorTemplate for Bip44Public { /// Network::Testnet, /// )?; /// -/// assert_eq!(wallet.get_address(New).to_string(), "2N4zkWAoGdUv4NXhSsU8DvS5MB36T8nKHEB"); +/// assert_eq!(wallet.next_unused_address(KeychainKind::External)?.to_string(), "2N4zkWAoGdUv4NXhSsU8DvS5MB36T8nKHEB"); /// assert_eq!(wallet.public_descriptor(KeychainKind::External).unwrap().to_string(), "sh(wpkh([c55b303f/49'/1'/0']tpubDDYr4kdnZgjjShzYNjZUZXUUtpXaofdkMaipyS8ThEh45qFmhT4hKYways7UXmg6V7het1QiFo9kf4kYUXyDvV4rHEyvSpys9pjCB3pukxi/0/*))#s9vxlc8e"); /// # Ok::<_, Box>(()) /// ``` @@ -304,7 +309,6 @@ impl> DescriptorTemplate for Bip49 { /// # use std::str::FromStr; /// # use bdk::bitcoin::{PrivateKey, Network}; /// # use bdk::{Wallet, KeychainKind}; -/// # use bdk::wallet::AddressIndex::New; /// use bdk::template::Bip49Public; /// /// let key = bitcoin::bip32::Xpub::from_str("tpubDC49r947KGK52X5rBWS4BLs5m9SRY3pYHnvRrm7HcybZ3BfdEsGFyzCMzayi1u58eT82ZeyFZwH7DD6Q83E3fM9CpfMtmnTygnLfP59jL9L")?; @@ -315,7 +319,7 @@ impl> DescriptorTemplate for Bip49 { /// Network::Testnet, /// )?; /// -/// assert_eq!(wallet.get_address(New).to_string(), "2N3K4xbVAHoiTQSwxkZjWDfKoNC27pLkYnt"); +/// assert_eq!(wallet.next_unused_address(KeychainKind::External)?.to_string(), "2N3K4xbVAHoiTQSwxkZjWDfKoNC27pLkYnt"); /// assert_eq!(wallet.public_descriptor(KeychainKind::External).unwrap().to_string(), "sh(wpkh([c55b303f/49'/1'/0']tpubDC49r947KGK52X5rBWS4BLs5m9SRY3pYHnvRrm7HcybZ3BfdEsGFyzCMzayi1u58eT82ZeyFZwH7DD6Q83E3fM9CpfMtmnTygnLfP59jL9L/0/*))#3tka9g0q"); /// # Ok::<_, Box>(()) /// ``` @@ -342,7 +346,6 @@ impl> DescriptorTemplate for Bip49Public { /// # use std::str::FromStr; /// # use bdk::bitcoin::{PrivateKey, Network}; /// # use bdk::{Wallet, KeychainKind}; -/// # use bdk::wallet::AddressIndex::New; /// use bdk::template::Bip84; /// /// let key = bitcoin::bip32::Xpriv::from_str("tprv8ZgxMBicQKsPeZRHk4rTG6orPS2CRNFX3njhUXx5vj9qGog5ZMH4uGReDWN5kCkY3jmWEtWause41CDvBRXD1shKknAMKxT99o9qUTRVC6m")?; @@ -352,7 +355,7 @@ impl> DescriptorTemplate for Bip49Public { /// Network::Testnet, /// )?; /// -/// assert_eq!(wallet.get_address(New).to_string(), "tb1qhl85z42h7r4su5u37rvvw0gk8j2t3n9y7zsg4n"); +/// assert_eq!(wallet.next_unused_address(KeychainKind::External)?.to_string(), "tb1qhl85z42h7r4su5u37rvvw0gk8j2t3n9y7zsg4n"); /// assert_eq!(wallet.public_descriptor(KeychainKind::External).unwrap().to_string(), "wpkh([c55b303f/84'/1'/0']tpubDDc5mum24DekpNw92t6fHGp8Gr2JjF9J7i4TZBtN6Vp8xpAULG5CFaKsfugWa5imhrQQUZKXe261asP5koDHo5bs3qNTmf3U3o4v9SaB8gg/0/*)#6kfecsmr"); /// # Ok::<_, Box>(()) /// ``` @@ -379,7 +382,6 @@ impl> DescriptorTemplate for Bip84 { /// # use std::str::FromStr; /// # use bdk::bitcoin::{PrivateKey, Network}; /// # use bdk::{Wallet, KeychainKind}; -/// # use bdk::wallet::AddressIndex::New; /// use bdk::template::Bip84Public; /// /// let key = bitcoin::bip32::Xpub::from_str("tpubDC2Qwo2TFsaNC4ju8nrUJ9mqVT3eSgdmy1yPqhgkjwmke3PRXutNGRYAUo6RCHTcVQaDR3ohNU9we59brGHuEKPvH1ags2nevW5opEE9Z5Q")?; @@ -390,7 +392,7 @@ impl> DescriptorTemplate for Bip84 { /// Network::Testnet, /// )?; /// -/// assert_eq!(wallet.get_address(New).to_string(), "tb1qedg9fdlf8cnnqfd5mks6uz5w4kgpk2pr6y4qc7"); +/// assert_eq!(wallet.next_unused_address(KeychainKind::External)?.to_string(), "tb1qedg9fdlf8cnnqfd5mks6uz5w4kgpk2pr6y4qc7"); /// assert_eq!(wallet.public_descriptor(KeychainKind::External).unwrap().to_string(), "wpkh([c55b303f/84'/1'/0']tpubDC2Qwo2TFsaNC4ju8nrUJ9mqVT3eSgdmy1yPqhgkjwmke3PRXutNGRYAUo6RCHTcVQaDR3ohNU9we59brGHuEKPvH1ags2nevW5opEE9Z5Q/0/*)#dhu402yv"); /// # Ok::<_, Box>(()) /// ``` @@ -417,7 +419,6 @@ impl> DescriptorTemplate for Bip84Public { /// # use std::str::FromStr; /// # use bdk::bitcoin::{PrivateKey, Network}; /// # use bdk::{Wallet, KeychainKind}; -/// # use bdk::wallet::AddressIndex::New; /// use bdk::template::Bip86; /// /// let key = bitcoin::bip32::Xpriv::from_str("tprv8ZgxMBicQKsPeZRHk4rTG6orPS2CRNFX3njhUXx5vj9qGog5ZMH4uGReDWN5kCkY3jmWEtWause41CDvBRXD1shKknAMKxT99o9qUTRVC6m")?; @@ -427,7 +428,7 @@ impl> DescriptorTemplate for Bip84Public { /// Network::Testnet, /// )?; /// -/// assert_eq!(wallet.get_address(New).to_string(), "tb1p5unlj09djx8xsjwe97269kqtxqpwpu2epeskgqjfk4lnf69v4tnqpp35qu"); +/// assert_eq!(wallet.next_unused_address(KeychainKind::External)?.to_string(), "tb1p5unlj09djx8xsjwe97269kqtxqpwpu2epeskgqjfk4lnf69v4tnqpp35qu"); /// assert_eq!(wallet.public_descriptor(KeychainKind::External).unwrap().to_string(), "tr([c55b303f/86'/1'/0']tpubDCiHofpEs47kx358bPdJmTZHmCDqQ8qw32upCSxHrSEdeeBs2T5Mq6QMB2ukeMqhNBiyhosBvJErteVhfURPGXPv3qLJPw5MVpHUewsbP2m/0/*)#dkgvr5hm"); /// # Ok::<_, Box>(()) /// ``` @@ -454,7 +455,6 @@ impl> DescriptorTemplate for Bip86 { /// # use std::str::FromStr; /// # use bdk::bitcoin::{PrivateKey, Network}; /// # use bdk::{Wallet, KeychainKind}; -/// # use bdk::wallet::AddressIndex::New; /// use bdk::template::Bip86Public; /// /// let key = bitcoin::bip32::Xpub::from_str("tpubDC2Qwo2TFsaNC4ju8nrUJ9mqVT3eSgdmy1yPqhgkjwmke3PRXutNGRYAUo6RCHTcVQaDR3ohNU9we59brGHuEKPvH1ags2nevW5opEE9Z5Q")?; @@ -465,7 +465,7 @@ impl> DescriptorTemplate for Bip86 { /// Network::Testnet, /// )?; /// -/// assert_eq!(wallet.get_address(New).to_string(), "tb1pwjp9f2k5n0xq73ecuu0c5njvgqr3vkh7yaylmpqvsuuaafymh0msvcmh37"); +/// assert_eq!(wallet.next_unused_address(KeychainKind::External)?.to_string(), "tb1pwjp9f2k5n0xq73ecuu0c5njvgqr3vkh7yaylmpqvsuuaafymh0msvcmh37"); /// assert_eq!(wallet.public_descriptor(KeychainKind::External).unwrap().to_string(), "tr([c55b303f/86'/1'/0']tpubDC2Qwo2TFsaNC4ju8nrUJ9mqVT3eSgdmy1yPqhgkjwmke3PRXutNGRYAUo6RCHTcVQaDR3ohNU9we59brGHuEKPvH1ags2nevW5opEE9Z5Q/0/*)#2p65srku"); /// # Ok::<_, Box>(()) /// ``` diff --git a/crates/bdk/src/wallet/mod.rs b/crates/bdk/src/wallet/mod.rs index c608bae7..00f0c461 100644 --- a/crates/bdk/src/wallet/mod.rs +++ b/crates/bdk/src/wallet/mod.rs @@ -179,7 +179,7 @@ impl } /// The address index selection strategy to use to derived an address from the wallet's external -/// descriptor. See [`Wallet::get_address`]. If you're unsure which one to use use `WalletIndex::New`. +/// descriptor. #[derive(Debug)] pub enum AddressIndex { /// Return a new address after incrementing the current descriptor index. @@ -256,36 +256,6 @@ impl Wallet { } } -impl Wallet { - /// Infallibly return a derived address using the external descriptor, see [`AddressIndex`] for - /// available address index selection strategies. If none of the keys in the descriptor are derivable - /// (i.e. does not end with /*) then the same address will always be returned for any [`AddressIndex`]. - /// - /// # Panics - /// - /// This panics when the caller requests for an address of derivation index greater than the - /// BIP32 max index. - pub fn get_address(&mut self, address_index: AddressIndex) -> AddressInfo { - self.try_get_address(address_index).unwrap() - } - - /// Infallibly return a derived address using the internal (change) descriptor. - /// - /// If the wallet doesn't have an internal descriptor it will use the external descriptor. - /// - /// see [`AddressIndex`] for available address index selection strategies. If none of the keys - /// in the descriptor are derivable (i.e. does not end with /*) then the same address will always - /// be returned for any [`AddressIndex`]. - /// - /// # Panics - /// - /// This panics when the caller requests for an address of derivation index greater than the - /// BIP32 max index. - pub fn get_internal_address(&mut self, address_index: AddressIndex) -> AddressInfo { - self.try_get_internal_address(address_index).unwrap() - } -} - /// The error type when constructing a fresh [`Wallet`]. /// /// Methods [`new`] and [`new_with_genesis_hash`] may return this error. @@ -803,13 +773,7 @@ impl Wallet { /// # Errors /// /// If writing to persistent storage fails. - pub fn reveal_next_address( - &mut self, - keychain: KeychainKind, - ) -> Result - where - D: PersistBackend, - { + pub fn reveal_next_address(&mut self, keychain: KeychainKind) -> anyhow::Result { let keychain = self.map_keychain(keychain); let ((index, spk), index_changeset) = self.indexed_graph.index.reveal_next_spk(&keychain); @@ -837,10 +801,7 @@ impl Wallet { &mut self, keychain: KeychainKind, index: u32, - ) -> Result + '_, D::WriteError> - where - D: PersistBackend, - { + ) -> anyhow::Result + '_> { let keychain = self.map_keychain(keychain); let (spk_iter, index_changeset) = self.indexed_graph.index.reveal_to_target(&keychain, index); @@ -864,13 +825,7 @@ impl Wallet { /// # Errors /// /// If writing to persistent storage fails. - pub fn next_unused_address( - &mut self, - keychain: KeychainKind, - ) -> Result - where - D: PersistBackend, - { + pub fn next_unused_address(&mut self, keychain: KeychainKind) -> anyhow::Result { let keychain = self.map_keychain(keychain); let ((index, spk), index_changeset) = self.indexed_graph.index.next_unused_spk(&keychain); @@ -2646,7 +2601,7 @@ macro_rules! doctest_wallet { () => {{ use $crate::bitcoin::{BlockHash, Transaction, absolute, TxOut, Network, hashes::Hash}; use $crate::chain::{ConfirmationTime, BlockId}; - use $crate::wallet::{AddressIndex, Wallet}; + use $crate::{KeychainKind, wallet::Wallet}; let descriptor = "tr([73c5da0a/86'/0'/0']tprv8fMn4hSKPRC1oaCPqxDb1JWtgkpeiQvZhsr8W2xuy3GEMkzoArcAWTfJxYb6Wj8XNNDWEjfYKK4wGQXh3ZUXhDF2NcnsALpWTeSwarJt7Vc/0/*)"; let change_descriptor = "tr([73c5da0a/86'/0'/0']tprv8fMn4hSKPRC1oaCPqxDb1JWtgkpeiQvZhsr8W2xuy3GEMkzoArcAWTfJxYb6Wj8XNNDWEjfYKK4wGQXh3ZUXhDF2NcnsALpWTeSwarJt7Vc/1/*)"; @@ -2656,7 +2611,7 @@ macro_rules! doctest_wallet { Network::Regtest, ) .unwrap(); - let address = wallet.get_address(AddressIndex::New).address; + let address = wallet.peek_address(KeychainKind::External, 0).address; let tx = Transaction { version: transaction::Version::ONE, lock_time: absolute::LockTime::ZERO, diff --git a/crates/bdk/tests/common.rs b/crates/bdk/tests/common.rs index 0598e9f1..ec421551 100644 --- a/crates/bdk/tests/common.rs +++ b/crates/bdk/tests/common.rs @@ -1,6 +1,6 @@ #![allow(unused)] -use bdk::{wallet::AddressIndex, KeychainKind, LocalOutput, Wallet}; +use bdk::{KeychainKind, LocalOutput, Wallet}; use bdk_chain::indexed_tx_graph::Indexer; use bdk_chain::{BlockId, ConfirmationTime}; use bitcoin::hashes::Hash; @@ -20,7 +20,7 @@ pub fn get_funded_wallet_with_change( change: Option<&str>, ) -> (Wallet, bitcoin::Txid) { let mut wallet = Wallet::new_no_persist(descriptor, change, Network::Regtest).unwrap(); - let change_address = wallet.get_address(AddressIndex::New).address; + let change_address = wallet.peek_address(KeychainKind::External, 0).address; let sendto_address = Address::from_str("bcrt1q3qtze4ys45tgdvguj66zrk4fu6hq3a3v9pfly5") .expect("address") .require_network(Network::Regtest) diff --git a/crates/bdk/tests/psbt.rs b/crates/bdk/tests/psbt.rs index 24dc38c1..ed7a88ad 100644 --- a/crates/bdk/tests/psbt.rs +++ b/crates/bdk/tests/psbt.rs @@ -1,7 +1,5 @@ use bdk::bitcoin::{Amount, FeeRate, Psbt, TxIn}; -use bdk::wallet::AddressIndex; -use bdk::wallet::AddressIndex::New; -use bdk::{psbt, SignOptions}; +use bdk::{psbt, KeychainKind, SignOptions}; use core::str::FromStr; mod common; use common::*; @@ -14,7 +12,7 @@ const PSBT_STR: &str = "cHNidP8BAKACAAAAAqsJSaCMWvfEm4IS9Bfi8Vqz9cM9zxU4IagTn4d6 fn test_psbt_malformed_psbt_input_legacy() { let psbt_bip = Psbt::from_str(PSBT_STR).unwrap(); let (mut wallet, _) = get_funded_wallet(get_test_wpkh()); - let send_to = wallet.get_address(AddressIndex::New); + let send_to = wallet.peek_address(KeychainKind::External, 0); let mut builder = wallet.build_tx(); builder.add_recipient(send_to.script_pubkey(), 10_000); let mut psbt = builder.finish().unwrap(); @@ -31,7 +29,7 @@ fn test_psbt_malformed_psbt_input_legacy() { fn test_psbt_malformed_psbt_input_segwit() { let psbt_bip = Psbt::from_str(PSBT_STR).unwrap(); let (mut wallet, _) = get_funded_wallet(get_test_wpkh()); - let send_to = wallet.get_address(AddressIndex::New); + let send_to = wallet.peek_address(KeychainKind::External, 0); let mut builder = wallet.build_tx(); builder.add_recipient(send_to.script_pubkey(), 10_000); let mut psbt = builder.finish().unwrap(); @@ -47,7 +45,7 @@ fn test_psbt_malformed_psbt_input_segwit() { #[should_panic(expected = "InputIndexOutOfRange")] fn test_psbt_malformed_tx_input() { let (mut wallet, _) = get_funded_wallet(get_test_wpkh()); - let send_to = wallet.get_address(AddressIndex::New); + let send_to = wallet.peek_address(KeychainKind::External, 0); let mut builder = wallet.build_tx(); builder.add_recipient(send_to.script_pubkey(), 10_000); let mut psbt = builder.finish().unwrap(); @@ -63,7 +61,7 @@ fn test_psbt_malformed_tx_input() { fn test_psbt_sign_with_finalized() { let psbt_bip = Psbt::from_str(PSBT_STR).unwrap(); let (mut wallet, _) = get_funded_wallet(get_test_wpkh()); - let send_to = wallet.get_address(AddressIndex::New); + let send_to = wallet.peek_address(KeychainKind::External, 0); let mut builder = wallet.build_tx(); builder.add_recipient(send_to.script_pubkey(), 10_000); let mut psbt = builder.finish().unwrap(); @@ -84,7 +82,7 @@ fn test_psbt_fee_rate_with_witness_utxo() { let expected_fee_rate = FeeRate::from_sat_per_kwu(310); let (mut wallet, _) = get_funded_wallet("wpkh(tprv8ZgxMBicQKsPd3EupYiPRhaMooHKUHJxNsTfYuScep13go8QFfHdtkG9nRkFGb7busX4isf6X9dURGCoKgitaApQ6MupRhZMcELAxTBRJgS/*)"); - let addr = wallet.get_address(New); + let addr = wallet.peek_address(KeychainKind::External, 0); let mut builder = wallet.build_tx(); builder.drain_to(addr.script_pubkey()).drain_wallet(); builder.fee_rate(expected_fee_rate); @@ -109,7 +107,7 @@ fn test_psbt_fee_rate_with_nonwitness_utxo() { let expected_fee_rate = FeeRate::from_sat_per_kwu(310); let (mut wallet, _) = get_funded_wallet("pkh(tprv8ZgxMBicQKsPd3EupYiPRhaMooHKUHJxNsTfYuScep13go8QFfHdtkG9nRkFGb7busX4isf6X9dURGCoKgitaApQ6MupRhZMcELAxTBRJgS/*)"); - let addr = wallet.get_address(New); + let addr = wallet.peek_address(KeychainKind::External, 0); let mut builder = wallet.build_tx(); builder.drain_to(addr.script_pubkey()).drain_wallet(); builder.fee_rate(expected_fee_rate); @@ -133,7 +131,7 @@ fn test_psbt_fee_rate_with_missing_txout() { let expected_fee_rate = FeeRate::from_sat_per_kwu(310); let (mut wpkh_wallet, _) = get_funded_wallet("wpkh(tprv8ZgxMBicQKsPd3EupYiPRhaMooHKUHJxNsTfYuScep13go8QFfHdtkG9nRkFGb7busX4isf6X9dURGCoKgitaApQ6MupRhZMcELAxTBRJgS/*)"); - let addr = wpkh_wallet.get_address(New); + let addr = wpkh_wallet.peek_address(KeychainKind::External, 0); let mut builder = wpkh_wallet.build_tx(); builder.drain_to(addr.script_pubkey()).drain_wallet(); builder.fee_rate(expected_fee_rate); @@ -145,7 +143,7 @@ fn test_psbt_fee_rate_with_missing_txout() { assert!(wpkh_psbt.fee_rate().is_none()); let (mut pkh_wallet, _) = get_funded_wallet("pkh(tprv8ZgxMBicQKsPd3EupYiPRhaMooHKUHJxNsTfYuScep13go8QFfHdtkG9nRkFGb7busX4isf6X9dURGCoKgitaApQ6MupRhZMcELAxTBRJgS/*)"); - let addr = pkh_wallet.get_address(New); + let addr = pkh_wallet.peek_address(KeychainKind::External, 0); let mut builder = pkh_wallet.build_tx(); builder.drain_to(addr.script_pubkey()).drain_wallet(); builder.fee_rate(expected_fee_rate); @@ -174,7 +172,7 @@ fn test_psbt_multiple_internalkey_signers() { let (mut wallet, _) = get_funded_wallet(&desc); let to_spend = wallet.get_balance().total(); - let send_to = wallet.get_address(AddressIndex::New); + let send_to = wallet.peek_address(KeychainKind::External, 0); let mut builder = wallet.build_tx(); builder.drain_to(send_to.script_pubkey()).drain_wallet(); let mut psbt = builder.finish().unwrap(); diff --git a/crates/bdk/tests/wallet.rs b/crates/bdk/tests/wallet.rs index 68ef541b..b41c4b42 100644 --- a/crates/bdk/tests/wallet.rs +++ b/crates/bdk/tests/wallet.rs @@ -7,8 +7,8 @@ use bdk::signer::{SignOptions, SignerError}; use bdk::wallet::coin_selection::{self, LargestFirstCoinSelection}; use bdk::wallet::error::CreateTxError; use bdk::wallet::tx_builder::AddForeignUtxoError; -use bdk::wallet::{AddressIndex, AddressInfo, Balance, Wallet}; -use bdk::wallet::{AddressIndex::*, NewError}; +use bdk::wallet::NewError; +use bdk::wallet::{AddressInfo, Balance, Wallet}; use bdk::KeychainKind; use bdk_chain::COINBASE_MATURITY; use bdk_chain::{BlockId, ConfirmationTime}; @@ -26,12 +26,13 @@ mod common; use common::*; fn receive_output(wallet: &mut Wallet, value: u64, height: ConfirmationTime) -> OutPoint { + let addr = wallet.next_unused_address(KeychainKind::External).unwrap(); let tx = Transaction { version: transaction::Version::ONE, lock_time: absolute::LockTime::ZERO, input: vec![], output: vec![TxOut { - script_pubkey: wallet.get_address(LastUnused).script_pubkey(), + script_pubkey: addr.script_pubkey(), value: Amount::from_sat(value), }], }; @@ -76,7 +77,7 @@ fn load_recovers_wallet() { let mut wallet = Wallet::new(get_test_tr_single_sig_xprv(), None, db, Network::Testnet) .expect("must init wallet"); - wallet.try_get_address(New).unwrap(); + wallet.reveal_next_address(KeychainKind::External).unwrap(); wallet.spk_index().clone() }; @@ -343,7 +344,7 @@ fn test_create_tx_empty_recipients() { #[should_panic(expected = "NoUtxosSelected")] fn test_create_tx_manually_selected_empty_utxos() { let (mut wallet, _) = get_funded_wallet(get_test_wpkh()); - let addr = wallet.get_address(New); + let addr = wallet.next_unused_address(KeychainKind::External).unwrap(); let mut builder = wallet.build_tx(); builder .add_recipient(addr.script_pubkey(), 25_000) @@ -354,7 +355,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 addr = wallet.get_address(New); + let addr = wallet.next_unused_address(KeychainKind::External).unwrap(); let mut builder = wallet.build_tx(); builder .add_recipient(addr.script_pubkey(), 25_000) @@ -365,7 +366,7 @@ fn test_create_tx_version_0() { #[test] fn test_create_tx_version_1_csv() { let (mut wallet, _) = get_funded_wallet(get_test_single_sig_csv()); - let addr = wallet.get_address(New); + let addr = wallet.next_unused_address(KeychainKind::External).unwrap(); let mut builder = wallet.build_tx(); builder .add_recipient(addr.script_pubkey(), 25_000) @@ -376,7 +377,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 addr = wallet.get_address(New); + let addr = wallet.next_unused_address(KeychainKind::External).unwrap(); let mut builder = wallet.build_tx(); builder .add_recipient(addr.script_pubkey(), 25_000) @@ -390,7 +391,7 @@ fn test_create_tx_custom_version() { fn test_create_tx_default_locktime_is_last_sync_height() { let (mut wallet, _) = get_funded_wallet(get_test_wpkh()); - let addr = wallet.get_address(New); + let addr = wallet.next_unused_address(KeychainKind::External).unwrap(); let mut builder = wallet.build_tx(); builder.add_recipient(addr.script_pubkey(), 25_000); let psbt = builder.finish().unwrap(); @@ -403,7 +404,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 addr = wallet.get_address(New); + let addr = wallet.next_unused_address(KeychainKind::External).unwrap(); let mut builder = wallet.build_tx(); builder.add_recipient(addr.script_pubkey(), 25_000); @@ -419,7 +420,7 @@ fn test_create_tx_fee_sniping_locktime_last_sync() { #[test] fn test_create_tx_default_locktime_cltv() { let (mut wallet, _) = get_funded_wallet(get_test_single_sig_cltv()); - let addr = wallet.get_address(New); + let addr = wallet.next_unused_address(KeychainKind::External).unwrap(); let mut builder = wallet.build_tx(); builder.add_recipient(addr.script_pubkey(), 25_000); let psbt = builder.finish().unwrap(); @@ -430,7 +431,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 addr = wallet.get_address(New); + let addr = wallet.next_unused_address(KeychainKind::External).unwrap(); let mut builder = wallet.build_tx(); builder .add_recipient(addr.script_pubkey(), 25_000) @@ -447,7 +448,7 @@ fn test_create_tx_custom_locktime() { #[test] fn test_create_tx_custom_locktime_compatible_with_cltv() { let (mut wallet, _) = get_funded_wallet(get_test_single_sig_cltv()); - let addr = wallet.get_address(New); + let addr = wallet.next_unused_address(KeychainKind::External).unwrap(); let mut builder = wallet.build_tx(); builder .add_recipient(addr.script_pubkey(), 25_000) @@ -460,7 +461,7 @@ fn test_create_tx_custom_locktime_compatible_with_cltv() { #[test] fn test_create_tx_custom_locktime_incompatible_with_cltv() { let (mut wallet, _) = get_funded_wallet(get_test_single_sig_cltv()); - let addr = wallet.get_address(New); + let addr = wallet.next_unused_address(KeychainKind::External).unwrap(); let mut builder = wallet.build_tx(); builder .add_recipient(addr.script_pubkey(), 25_000) @@ -473,7 +474,7 @@ fn test_create_tx_custom_locktime_incompatible_with_cltv() { #[test] fn test_create_tx_no_rbf_csv() { let (mut wallet, _) = get_funded_wallet(get_test_single_sig_csv()); - let addr = wallet.get_address(New); + let addr = wallet.next_unused_address(KeychainKind::External).unwrap(); let mut builder = wallet.build_tx(); builder.add_recipient(addr.script_pubkey(), 25_000); let psbt = builder.finish().unwrap(); @@ -484,7 +485,7 @@ fn test_create_tx_no_rbf_csv() { #[test] fn test_create_tx_with_default_rbf_csv() { let (mut wallet, _) = get_funded_wallet(get_test_single_sig_csv()); - let addr = wallet.get_address(New); + let addr = wallet.next_unused_address(KeychainKind::External).unwrap(); let mut builder = wallet.build_tx(); builder .add_recipient(addr.script_pubkey(), 25_000) @@ -498,7 +499,7 @@ fn test_create_tx_with_default_rbf_csv() { #[test] fn test_create_tx_with_custom_rbf_csv() { let (mut wallet, _) = get_funded_wallet(get_test_single_sig_csv()); - let addr = wallet.get_address(New); + let addr = wallet.next_unused_address(KeychainKind::External).unwrap(); let mut builder = wallet.build_tx(); builder .add_recipient(addr.script_pubkey(), 25_000) @@ -511,7 +512,7 @@ fn test_create_tx_with_custom_rbf_csv() { #[test] fn test_create_tx_no_rbf_cltv() { let (mut wallet, _) = get_funded_wallet(get_test_single_sig_cltv()); - let addr = wallet.get_address(New); + let addr = wallet.next_unused_address(KeychainKind::External).unwrap(); let mut builder = wallet.build_tx(); builder.add_recipient(addr.script_pubkey(), 25_000); let psbt = builder.finish().unwrap(); @@ -522,7 +523,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 addr = wallet.get_address(New); + let addr = wallet.next_unused_address(KeychainKind::External).unwrap(); let mut builder = wallet.build_tx(); builder .add_recipient(addr.script_pubkey(), 25_000) @@ -533,7 +534,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 addr = wallet.get_address(New); + let addr = wallet.next_unused_address(KeychainKind::External).unwrap(); let mut builder = wallet.build_tx(); builder .add_recipient(addr.script_pubkey(), 25_000) @@ -546,7 +547,7 @@ fn test_create_tx_custom_rbf_sequence() { #[test] fn test_create_tx_default_sequence() { let (mut wallet, _) = get_funded_wallet(get_test_wpkh()); - let addr = wallet.get_address(New); + let addr = wallet.next_unused_address(KeychainKind::External).unwrap(); let mut builder = wallet.build_tx(); builder.add_recipient(addr.script_pubkey(), 25_000); let psbt = builder.finish().unwrap(); @@ -557,7 +558,7 @@ fn test_create_tx_default_sequence() { #[test] fn test_create_tx_change_policy_no_internal() { let (mut wallet, _) = get_funded_wallet(get_test_wpkh()); - let addr = wallet.get_address(New); + let addr = wallet.next_unused_address(KeychainKind::External).unwrap(); let mut builder = wallet.build_tx(); builder .add_recipient(addr.script_pubkey(), 25_000) @@ -580,7 +581,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 addr = wallet.get_address(New); + let addr = wallet.next_unused_address(KeychainKind::External).unwrap(); let mut builder = wallet.build_tx(); builder.drain_to(addr.script_pubkey()).drain_wallet(); let psbt = builder.finish().unwrap(); @@ -599,7 +600,7 @@ fn test_create_tx_drain_wallet_and_drain_to_and_with_recipient() { let addr = Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt") .unwrap() .assume_checked(); - let drain_addr = wallet.get_address(New); + let drain_addr = wallet.next_unused_address(KeychainKind::External).unwrap(); let mut builder = wallet.build_tx(); builder .add_recipient(addr.script_pubkey(), 20_000) @@ -625,7 +626,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 addr = wallet.get_address(New); + 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(); builder @@ -646,7 +647,7 @@ fn test_create_tx_drain_to_and_utxos() { #[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 drain_addr = wallet.get_address(New); + let drain_addr = wallet.next_unused_address(KeychainKind::External).unwrap(); let mut builder = wallet.build_tx(); builder.drain_to(drain_addr.script_pubkey()); builder.finish().unwrap(); @@ -655,7 +656,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 addr = wallet.get_address(New); + let addr = wallet.next_unused_address(KeychainKind::External).unwrap(); let mut builder = wallet.build_tx(); builder.add_recipient(addr.script_pubkey(), 25_000); let psbt = builder.finish().unwrap(); @@ -667,7 +668,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 addr = wallet.get_address(New); + let addr = wallet.next_unused_address(KeychainKind::External).unwrap(); let mut builder = wallet.build_tx(); builder .add_recipient(addr.script_pubkey(), 25_000) @@ -681,7 +682,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 addr = wallet.get_address(New); + let addr = wallet.next_unused_address(KeychainKind::External).unwrap(); let mut builder = wallet.build_tx(); builder .drain_to(addr.script_pubkey()) @@ -701,7 +702,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 addr = wallet.get_address(New); + let addr = wallet.next_unused_address(KeychainKind::External).unwrap(); let mut builder = wallet.build_tx(); builder .drain_to(addr.script_pubkey()) @@ -722,7 +723,7 @@ fn test_create_tx_absolute_zero_fee() { #[should_panic(expected = "InsufficientFunds")] fn test_create_tx_absolute_high_fee() { let (mut wallet, _) = get_funded_wallet(get_test_wpkh()); - let addr = wallet.get_address(New); + let addr = wallet.next_unused_address(KeychainKind::External).unwrap(); let mut builder = wallet.build_tx(); builder .drain_to(addr.script_pubkey()) @@ -736,7 +737,7 @@ fn test_create_tx_add_change() { use bdk::wallet::tx_builder::TxOrdering; let (mut wallet, _) = get_funded_wallet(get_test_wpkh()); - let addr = wallet.get_address(New); + let addr = wallet.next_unused_address(KeychainKind::External).unwrap(); let mut builder = wallet.build_tx(); builder .add_recipient(addr.script_pubkey(), 25_000) @@ -755,7 +756,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 addr = wallet.get_address(New); + let addr = wallet.next_unused_address(KeychainKind::External).unwrap(); let mut builder = wallet.build_tx(); builder.add_recipient(addr.script_pubkey(), 49_800); let psbt = builder.finish().unwrap(); @@ -770,7 +771,7 @@ fn test_create_tx_skip_change_dust() { #[should_panic(expected = "InsufficientFunds")] fn test_create_tx_drain_to_dust_amount() { let (mut wallet, _) = get_funded_wallet(get_test_wpkh()); - let addr = wallet.get_address(New); + 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(); builder @@ -783,7 +784,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 addr = wallet.get_address(New); + let addr = wallet.next_unused_address(KeychainKind::External).unwrap(); let mut builder = wallet.build_tx(); builder .add_recipient(addr.script_pubkey(), 30_000) @@ -804,7 +805,7 @@ fn test_create_tx_ordering_respected() { #[test] fn test_create_tx_default_sighash() { let (mut wallet, _) = get_funded_wallet(get_test_wpkh()); - let addr = wallet.get_address(New); + let addr = wallet.next_unused_address(KeychainKind::External).unwrap(); let mut builder = wallet.build_tx(); builder.add_recipient(addr.script_pubkey(), 30_000); let psbt = builder.finish().unwrap(); @@ -815,7 +816,7 @@ fn test_create_tx_default_sighash() { #[test] fn test_create_tx_custom_sighash() { let (mut wallet, _) = get_funded_wallet(get_test_wpkh()); - let addr = wallet.get_address(New); + let addr = wallet.next_unused_address(KeychainKind::External).unwrap(); let mut builder = wallet.build_tx(); builder .add_recipient(addr.script_pubkey(), 30_000) @@ -834,7 +835,7 @@ fn test_create_tx_input_hd_keypaths() { use core::str::FromStr; let (mut wallet, _) = get_funded_wallet("wpkh([d34db33f/44'/0'/0']tpubDEnoLuPdBep9bzw5LoGYpsxUQYheRQ9gcgrJhJEcdKFB9cWQRyYmkCyRoTqeD4tJYiVVgt6A3rN6rWn9RYhR9sBsGxji29LYWHuKKbdb1ev/0/*)"); - let addr = wallet.get_address(New); + let addr = wallet.next_unused_address(KeychainKind::External).unwrap(); let mut builder = wallet.build_tx(); builder.drain_to(addr.script_pubkey()).drain_wallet(); let psbt = builder.finish().unwrap(); @@ -856,7 +857,7 @@ fn test_create_tx_output_hd_keypaths() { let (mut wallet, _) = get_funded_wallet("wpkh([d34db33f/44'/0'/0']tpubDEnoLuPdBep9bzw5LoGYpsxUQYheRQ9gcgrJhJEcdKFB9cWQRyYmkCyRoTqeD4tJYiVVgt6A3rN6rWn9RYhR9sBsGxji29LYWHuKKbdb1ev/0/*)"); - let addr = wallet.get_address(New); + let addr = wallet.next_unused_address(KeychainKind::External).unwrap(); let mut builder = wallet.build_tx(); builder.drain_to(addr.script_pubkey()).drain_wallet(); let psbt = builder.finish().unwrap(); @@ -878,7 +879,7 @@ fn test_create_tx_set_redeem_script_p2sh() { let (mut wallet, _) = get_funded_wallet("sh(pk(cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW))"); - let addr = wallet.get_address(New); + let addr = wallet.next_unused_address(KeychainKind::External).unwrap(); let mut builder = wallet.build_tx(); builder.drain_to(addr.script_pubkey()).drain_wallet(); let psbt = builder.finish().unwrap(); @@ -901,7 +902,7 @@ fn test_create_tx_set_witness_script_p2wsh() { let (mut wallet, _) = get_funded_wallet("wsh(pk(cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW))"); - let addr = wallet.get_address(New); + let addr = wallet.next_unused_address(KeychainKind::External).unwrap(); let mut builder = wallet.build_tx(); builder.drain_to(addr.script_pubkey()).drain_wallet(); let psbt = builder.finish().unwrap(); @@ -922,7 +923,7 @@ fn test_create_tx_set_witness_script_p2wsh() { fn test_create_tx_set_redeem_witness_script_p2wsh_p2sh() { let (mut wallet, _) = get_funded_wallet("sh(wsh(pk(cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW)))"); - let addr = wallet.get_address(New); + let addr = wallet.next_unused_address(KeychainKind::External).unwrap(); let mut builder = wallet.build_tx(); builder.drain_to(addr.script_pubkey()).drain_wallet(); let psbt = builder.finish().unwrap(); @@ -940,7 +941,7 @@ fn test_create_tx_set_redeem_witness_script_p2wsh_p2sh() { fn test_create_tx_non_witness_utxo() { let (mut wallet, _) = get_funded_wallet("sh(pk(cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW))"); - let addr = wallet.get_address(New); + let addr = wallet.next_unused_address(KeychainKind::External).unwrap(); let mut builder = wallet.build_tx(); builder.drain_to(addr.script_pubkey()).drain_wallet(); let psbt = builder.finish().unwrap(); @@ -953,7 +954,7 @@ fn test_create_tx_non_witness_utxo() { fn test_create_tx_only_witness_utxo() { let (mut wallet, _) = get_funded_wallet("wsh(pk(cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW))"); - let addr = wallet.get_address(New); + let addr = wallet.next_unused_address(KeychainKind::External).unwrap(); let mut builder = wallet.build_tx(); builder .drain_to(addr.script_pubkey()) @@ -969,7 +970,7 @@ fn test_create_tx_only_witness_utxo() { fn test_create_tx_shwpkh_has_witness_utxo() { let (mut wallet, _) = get_funded_wallet("sh(wpkh(cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW))"); - let addr = wallet.get_address(New); + let addr = wallet.next_unused_address(KeychainKind::External).unwrap(); let mut builder = wallet.build_tx(); builder.drain_to(addr.script_pubkey()).drain_wallet(); let psbt = builder.finish().unwrap(); @@ -981,7 +982,7 @@ fn test_create_tx_shwpkh_has_witness_utxo() { fn test_create_tx_both_non_witness_utxo_and_witness_utxo_default() { let (mut wallet, _) = get_funded_wallet("wsh(pk(cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW))"); - let addr = wallet.get_address(New); + let addr = wallet.next_unused_address(KeychainKind::External).unwrap(); let mut builder = wallet.build_tx(); builder.drain_to(addr.script_pubkey()).drain_wallet(); let psbt = builder.finish().unwrap(); @@ -996,8 +997,11 @@ fn test_create_tx_add_utxo() { let small_output_tx = Transaction { input: vec![], output: vec![TxOut { + script_pubkey: wallet + .next_unused_address(KeychainKind::External) + .unwrap() + .script_pubkey(), value: Amount::from_sat(25_000), - script_pubkey: wallet.get_address(New).address.script_pubkey(), }], version: transaction::Version::non_standard(0), lock_time: absolute::LockTime::ZERO, @@ -1042,8 +1046,11 @@ fn test_create_tx_manually_selected_insufficient() { let small_output_tx = Transaction { input: vec![], output: vec![TxOut { + script_pubkey: wallet + .next_unused_address(KeychainKind::External) + .unwrap() + .script_pubkey(), value: Amount::from_sat(25_000), - script_pubkey: wallet.get_address(New).address.script_pubkey(), }], version: transaction::Version::non_standard(0), lock_time: absolute::LockTime::ZERO, @@ -1094,8 +1101,11 @@ fn test_create_tx_policy_path_no_csv() { lock_time: absolute::LockTime::ZERO, input: vec![], output: vec![TxOut { + script_pubkey: wallet + .next_unused_address(KeychainKind::External) + .unwrap() + .script_pubkey(), value: Amount::from_sat(50_000), - script_pubkey: wallet.get_address(New).script_pubkey(), }], }; wallet @@ -1167,7 +1177,7 @@ fn test_create_tx_global_xpubs_with_origin() { use bitcoin::hex::FromHex; let (mut wallet, _) = get_funded_wallet("wpkh([73756c7f/48'/0'/0'/2']tpubDCKxNyM3bLgbEX13Mcd8mYxbVg9ajDkWXMh29hMWBurKfVmBfWAM96QVP3zaUcN51HvkZ3ar4VwP82kC8JZhhux8vFQoJintSpVBwpFvyU3/0/*)"); - let addr = wallet.get_address(New); + let addr = wallet.next_unused_address(KeychainKind::External).unwrap(); let mut builder = wallet.build_tx(); builder .add_recipient(addr.script_pubkey(), 25_000) @@ -1430,7 +1440,7 @@ fn test_get_psbt_input() { )] fn test_create_tx_global_xpubs_origin_missing() { let (mut wallet, _) = get_funded_wallet("wpkh(tpubDCKxNyM3bLgbEX13Mcd8mYxbVg9ajDkWXMh29hMWBurKfVmBfWAM96QVP3zaUcN51HvkZ3ar4VwP82kC8JZhhux8vFQoJintSpVBwpFvyU3/0/*)"); - let addr = wallet.get_address(New); + let addr = wallet.next_unused_address(KeychainKind::External).unwrap(); let mut builder = wallet.build_tx(); builder .add_recipient(addr.script_pubkey(), 25_000) @@ -1444,7 +1454,7 @@ fn test_create_tx_global_xpubs_master_without_origin() { use bitcoin::hex::FromHex; let (mut wallet, _) = get_funded_wallet("wpkh(tpubD6NzVbkrYhZ4Y55A58Gv9RSNF5hy84b5AJqYy7sCcjFrkcLpPre8kmgfit6kY1Zs3BLgeypTDBZJM222guPpdz7Cup5yzaMu62u7mYGbwFL/0/*)"); - let addr = wallet.get_address(New); + let addr = wallet.next_unused_address(KeychainKind::External).unwrap(); let mut builder = wallet.build_tx(); builder .add_recipient(addr.script_pubkey(), 25_000) @@ -1465,7 +1475,7 @@ fn test_create_tx_global_xpubs_master_without_origin() { #[should_panic(expected = "IrreplaceableTransaction")] fn test_bump_fee_irreplaceable_tx() { let (mut wallet, _) = get_funded_wallet(get_test_wpkh()); - let addr = wallet.get_address(New); + let addr = wallet.next_unused_address(KeychainKind::External).unwrap(); let mut builder = wallet.build_tx(); builder.add_recipient(addr.script_pubkey(), 25_000); let psbt = builder.finish().unwrap(); @@ -1482,7 +1492,7 @@ fn test_bump_fee_irreplaceable_tx() { #[should_panic(expected = "TransactionConfirmed")] fn test_bump_fee_confirmed_tx() { let (mut wallet, _) = get_funded_wallet(get_test_wpkh()); - let addr = wallet.get_address(New); + let addr = wallet.next_unused_address(KeychainKind::External).unwrap(); let mut builder = wallet.build_tx(); builder.add_recipient(addr.script_pubkey(), 25_000); let psbt = builder.finish().unwrap(); @@ -1506,7 +1516,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 addr = wallet.get_address(New); + let addr = wallet.next_unused_address(KeychainKind::External).unwrap(); let mut builder = wallet.build_tx(); builder .add_recipient(addr.script_pubkey(), 25_000) @@ -1540,7 +1550,7 @@ fn test_bump_fee_low_fee_rate() { #[should_panic(expected = "FeeTooLow")] fn test_bump_fee_low_abs() { let (mut wallet, _) = get_funded_wallet(get_test_wpkh()); - let addr = wallet.get_address(New); + let addr = wallet.next_unused_address(KeychainKind::External).unwrap(); let mut builder = wallet.build_tx(); builder .add_recipient(addr.script_pubkey(), 25_000) @@ -1563,7 +1573,7 @@ fn test_bump_fee_low_abs() { #[should_panic(expected = "FeeTooLow")] fn test_bump_fee_zero_abs() { let (mut wallet, _) = get_funded_wallet(get_test_wpkh()); - let addr = wallet.get_address(New); + let addr = wallet.next_unused_address(KeychainKind::External).unwrap(); let mut builder = wallet.build_tx(); builder .add_recipient(addr.script_pubkey(), 25_000) @@ -1777,8 +1787,11 @@ fn test_bump_fee_drain_wallet() { lock_time: absolute::LockTime::ZERO, input: vec![], output: vec![TxOut { + script_pubkey: wallet + .next_unused_address(KeychainKind::External) + .unwrap() + .script_pubkey(), value: Amount::from_sat(25_000), - script_pubkey: wallet.get_address(New).script_pubkey(), }], }; wallet @@ -1842,7 +1855,10 @@ fn test_bump_fee_remove_output_manually_selected_only() { lock_time: absolute::LockTime::ZERO, input: vec![], output: vec![TxOut { - script_pubkey: wallet.get_address(New).script_pubkey(), + script_pubkey: wallet + .next_unused_address(KeychainKind::External) + .unwrap() + .script_pubkey(), value: Amount::from_sat(25_000), }], }; @@ -1896,7 +1912,10 @@ fn test_bump_fee_add_input() { lock_time: absolute::LockTime::ZERO, input: vec![], output: vec![TxOut { - script_pubkey: wallet.get_address(New).script_pubkey(), + script_pubkey: wallet + .next_unused_address(KeychainKind::External) + .unwrap() + .script_pubkey(), value: Amount::from_sat(25_000), }], }; @@ -2378,7 +2397,7 @@ fn test_fee_amount_negative_drain_val() { #[test] fn test_sign_single_xprv() { let (mut wallet, _) = get_funded_wallet("wpkh(tprv8ZgxMBicQKsPd3EupYiPRhaMooHKUHJxNsTfYuScep13go8QFfHdtkG9nRkFGb7busX4isf6X9dURGCoKgitaApQ6MupRhZMcELAxTBRJgS/*)"); - let addr = wallet.get_address(New); + let addr = wallet.next_unused_address(KeychainKind::External).unwrap(); let mut builder = wallet.build_tx(); builder.drain_to(addr.script_pubkey()).drain_wallet(); let mut psbt = builder.finish().unwrap(); @@ -2393,7 +2412,7 @@ fn test_sign_single_xprv() { #[test] fn test_sign_single_xprv_with_master_fingerprint_and_path() { let (mut wallet, _) = get_funded_wallet("wpkh([d34db33f/84h/1h/0h]tprv8ZgxMBicQKsPd3EupYiPRhaMooHKUHJxNsTfYuScep13go8QFfHdtkG9nRkFGb7busX4isf6X9dURGCoKgitaApQ6MupRhZMcELAxTBRJgS/*)"); - let addr = wallet.get_address(New); + let addr = wallet.next_unused_address(KeychainKind::External).unwrap(); let mut builder = wallet.build_tx(); builder.drain_to(addr.script_pubkey()).drain_wallet(); let mut psbt = builder.finish().unwrap(); @@ -2408,7 +2427,7 @@ fn test_sign_single_xprv_with_master_fingerprint_and_path() { #[test] fn test_sign_single_xprv_bip44_path() { let (mut wallet, _) = get_funded_wallet("wpkh(tprv8ZgxMBicQKsPd3EupYiPRhaMooHKUHJxNsTfYuScep13go8QFfHdtkG9nRkFGb7busX4isf6X9dURGCoKgitaApQ6MupRhZMcELAxTBRJgS/44'/0'/0'/0/*)"); - let addr = wallet.get_address(New); + let addr = wallet.next_unused_address(KeychainKind::External).unwrap(); let mut builder = wallet.build_tx(); builder.drain_to(addr.script_pubkey()).drain_wallet(); let mut psbt = builder.finish().unwrap(); @@ -2423,7 +2442,7 @@ fn test_sign_single_xprv_bip44_path() { #[test] fn test_sign_single_xprv_sh_wpkh() { let (mut wallet, _) = get_funded_wallet("sh(wpkh(tprv8ZgxMBicQKsPd3EupYiPRhaMooHKUHJxNsTfYuScep13go8QFfHdtkG9nRkFGb7busX4isf6X9dURGCoKgitaApQ6MupRhZMcELAxTBRJgS/*))"); - let addr = wallet.get_address(New); + let addr = wallet.next_unused_address(KeychainKind::External).unwrap(); let mut builder = wallet.build_tx(); builder.drain_to(addr.script_pubkey()).drain_wallet(); let mut psbt = builder.finish().unwrap(); @@ -2439,7 +2458,7 @@ fn test_sign_single_xprv_sh_wpkh() { fn test_sign_single_wif() { let (mut wallet, _) = get_funded_wallet("wpkh(cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW)"); - let addr = wallet.get_address(New); + let addr = wallet.next_unused_address(KeychainKind::External).unwrap(); let mut builder = wallet.build_tx(); builder.drain_to(addr.script_pubkey()).drain_wallet(); let mut psbt = builder.finish().unwrap(); @@ -2454,7 +2473,7 @@ fn test_sign_single_wif() { #[test] fn test_sign_single_xprv_no_hd_keypaths() { let (mut wallet, _) = get_funded_wallet("wpkh(tprv8ZgxMBicQKsPd3EupYiPRhaMooHKUHJxNsTfYuScep13go8QFfHdtkG9nRkFGb7busX4isf6X9dURGCoKgitaApQ6MupRhZMcELAxTBRJgS/*)"); - let addr = wallet.get_address(New); + let addr = wallet.next_unused_address(KeychainKind::External).unwrap(); let mut builder = wallet.build_tx(); builder.drain_to(addr.script_pubkey()).drain_wallet(); let mut psbt = builder.finish().unwrap(); @@ -2539,7 +2558,7 @@ fn test_remove_partial_sigs_after_finalize_sign_option() { let (mut wallet, _) = get_funded_wallet("wpkh(tprv8ZgxMBicQKsPd3EupYiPRhaMooHKUHJxNsTfYuScep13go8QFfHdtkG9nRkFGb7busX4isf6X9dURGCoKgitaApQ6MupRhZMcELAxTBRJgS/*)"); for remove_partial_sigs in &[true, false] { - let addr = wallet.get_address(New); + let addr = wallet.next_unused_address(KeychainKind::External).unwrap(); let mut builder = wallet.build_tx(); builder.drain_to(addr.script_pubkey()).drain_wallet(); let mut psbt = builder.finish().unwrap(); @@ -2569,7 +2588,7 @@ fn test_try_finalize_sign_option() { let (mut wallet, _) = get_funded_wallet("wpkh(tprv8ZgxMBicQKsPd3EupYiPRhaMooHKUHJxNsTfYuScep13go8QFfHdtkG9nRkFGb7busX4isf6X9dURGCoKgitaApQ6MupRhZMcELAxTBRJgS/*)"); for try_finalize in &[true, false] { - let addr = wallet.get_address(New); + let addr = wallet.next_unused_address(KeychainKind::External).unwrap(); let mut builder = wallet.build_tx(); builder.drain_to(addr.script_pubkey()).drain_wallet(); let mut psbt = builder.finish().unwrap(); @@ -2603,7 +2622,7 @@ fn test_sign_nonstandard_sighash() { let sighash = EcdsaSighashType::NonePlusAnyoneCanPay; let (mut wallet, _) = get_funded_wallet("wpkh(tprv8ZgxMBicQKsPd3EupYiPRhaMooHKUHJxNsTfYuScep13go8QFfHdtkG9nRkFGb7busX4isf6X9dURGCoKgitaApQ6MupRhZMcELAxTBRJgS/*)"); - let addr = wallet.get_address(New); + let addr = wallet.next_unused_address(KeychainKind::External).unwrap(); let mut builder = wallet.build_tx(); builder .drain_to(addr.script_pubkey()) @@ -2649,12 +2668,25 @@ fn test_unused_address() { let mut wallet = Wallet::new_no_persist("wpkh(tpubEBr4i6yk5nf5DAaJpsi9N2pPYBeJ7fZ5Z9rmN4977iYLCGco1VyjB9tvvuvYtfZzjD5A8igzgw3HeWeeKFmanHYqksqZXYXGsw5zjnj7KM9/*)", None, Network::Testnet).unwrap(); + // `list_unused_addresses` should be empty if we haven't revealed any + assert!(wallet + .list_unused_addresses(KeychainKind::External) + .next() + .is_none()); + assert_eq!( - wallet.get_address(LastUnused).to_string(), + wallet + .next_unused_address(KeychainKind::External) + .unwrap() + .to_string(), "tb1q6yn66vajcctph75pvylgkksgpp6nq04ppwct9a" ); assert_eq!( - wallet.get_address(LastUnused).to_string(), + wallet + .list_unused_addresses(KeychainKind::External) + .next() + .unwrap() + .to_string(), "tb1q6yn66vajcctph75pvylgkksgpp6nq04ppwct9a" ); } @@ -2666,12 +2698,18 @@ fn test_next_unused_address() { assert_eq!(wallet.derivation_index(KeychainKind::External), None); assert_eq!( - wallet.get_address(LastUnused).to_string(), + wallet + .next_unused_address(KeychainKind::External) + .unwrap() + .to_string(), "tb1q6yn66vajcctph75pvylgkksgpp6nq04ppwct9a" ); assert_eq!(wallet.derivation_index(KeychainKind::External), Some(0)); assert_eq!( - wallet.get_address(LastUnused).to_string(), + wallet + .next_unused_address(KeychainKind::External) + .unwrap() + .to_string(), "tb1q6yn66vajcctph75pvylgkksgpp6nq04ppwct9a" ); assert_eq!(wallet.derivation_index(KeychainKind::External), Some(0)); @@ -2680,7 +2718,10 @@ fn test_next_unused_address() { receive_output_in_latest_block(&mut wallet, 25_000); assert_eq!( - wallet.get_address(LastUnused).to_string(), + wallet + .next_unused_address(KeychainKind::External) + .unwrap() + .to_string(), "tb1q4er7kxx6sssz3q7qp7zsqsdx4erceahhax77d7" ); assert_eq!(wallet.derivation_index(KeychainKind::External), Some(1)); @@ -2692,49 +2733,55 @@ fn test_peek_address_at_index() { None, Network::Testnet).unwrap(); assert_eq!( - wallet.get_address(Peek(1)).to_string(), + wallet.peek_address(KeychainKind::External, 1).to_string(), "tb1q4er7kxx6sssz3q7qp7zsqsdx4erceahhax77d7" ); assert_eq!( - wallet.get_address(Peek(0)).to_string(), + wallet.peek_address(KeychainKind::External, 0).to_string(), "tb1q6yn66vajcctph75pvylgkksgpp6nq04ppwct9a" ); assert_eq!( - wallet.get_address(Peek(2)).to_string(), + wallet.peek_address(KeychainKind::External, 2).to_string(), "tb1qzntf2mqex4ehwkjlfdyy3ewdlk08qkvkvrz7x2" ); // current new address is not affected assert_eq!( - wallet.get_address(New).to_string(), + wallet + .reveal_next_address(KeychainKind::External) + .unwrap() + .to_string(), "tb1q6yn66vajcctph75pvylgkksgpp6nq04ppwct9a" ); assert_eq!( - wallet.get_address(New).to_string(), + wallet + .reveal_next_address(KeychainKind::External) + .unwrap() + .to_string(), "tb1q4er7kxx6sssz3q7qp7zsqsdx4erceahhax77d7" ); } #[test] fn test_peek_address_at_index_not_derivable() { - let mut wallet = Wallet::new_no_persist("wpkh(tpubEBr4i6yk5nf5DAaJpsi9N2pPYBeJ7fZ5Z9rmN4977iYLCGco1VyjB9tvvuvYtfZzjD5A8igzgw3HeWeeKFmanHYqksqZXYXGsw5zjnj7KM9/1)", + let wallet = Wallet::new_no_persist("wpkh(tpubEBr4i6yk5nf5DAaJpsi9N2pPYBeJ7fZ5Z9rmN4977iYLCGco1VyjB9tvvuvYtfZzjD5A8igzgw3HeWeeKFmanHYqksqZXYXGsw5zjnj7KM9/1)", None, Network::Testnet).unwrap(); assert_eq!( - wallet.get_address(Peek(1)).to_string(), + wallet.peek_address(KeychainKind::External, 1).to_string(), "tb1q4er7kxx6sssz3q7qp7zsqsdx4erceahhax77d7" ); assert_eq!( - wallet.get_address(Peek(0)).to_string(), + wallet.peek_address(KeychainKind::External, 0).to_string(), "tb1q4er7kxx6sssz3q7qp7zsqsdx4erceahhax77d7" ); assert_eq!( - wallet.get_address(Peek(2)).to_string(), + wallet.peek_address(KeychainKind::External, 2).to_string(), "tb1q4er7kxx6sssz3q7qp7zsqsdx4erceahhax77d7" ); } @@ -2746,7 +2793,7 @@ fn test_returns_index_and_address() { // new index 0 assert_eq!( - wallet.get_address(New), + wallet.reveal_next_address(KeychainKind::External).unwrap(), AddressInfo { index: 0, address: Address::from_str("tb1q6yn66vajcctph75pvylgkksgpp6nq04ppwct9a") @@ -2758,7 +2805,7 @@ fn test_returns_index_and_address() { // new index 1 assert_eq!( - wallet.get_address(New), + wallet.reveal_next_address(KeychainKind::External).unwrap(), AddressInfo { index: 1, address: Address::from_str("tb1q4er7kxx6sssz3q7qp7zsqsdx4erceahhax77d7") @@ -2770,7 +2817,7 @@ fn test_returns_index_and_address() { // peek index 25 assert_eq!( - wallet.get_address(Peek(25)), + wallet.peek_address(KeychainKind::External, 25), AddressInfo { index: 25, address: Address::from_str("tb1qsp7qu0knx3sl6536dzs0703u2w2ag6ppl9d0c2") @@ -2782,7 +2829,7 @@ fn test_returns_index_and_address() { // new index 2 assert_eq!( - wallet.get_address(New), + wallet.reveal_next_address(KeychainKind::External).unwrap(), AddressInfo { index: 2, address: Address::from_str("tb1qzntf2mqex4ehwkjlfdyy3ewdlk08qkvkvrz7x2") @@ -2808,7 +2855,7 @@ fn test_sending_to_bip350_bech32m_address() { fn test_get_address() { use bdk::descriptor::template::Bip84; let key = bitcoin::bip32::Xpriv::from_str("tprv8ZgxMBicQKsPcx5nBGsR63Pe8KnRUqmbJNENAfGftF3yuXoMMoVJJcYeUw5eVkm9WBPjWYt6HMWYJNesB5HaNVBaFc1M6dRjWSYnmewUMYy").unwrap(); - let mut wallet = Wallet::new_no_persist( + let wallet = Wallet::new_no_persist( Bip84(key, KeychainKind::External), Some(Bip84(key, KeychainKind::Internal)), Network::Regtest, @@ -2816,7 +2863,7 @@ fn test_get_address() { .unwrap(); assert_eq!( - wallet.get_address(AddressIndex::New), + wallet.peek_address(KeychainKind::External, 0), AddressInfo { index: 0, address: Address::from_str("bcrt1qrhgaqu0zvf5q2d0gwwz04w0dh0cuehhqvzpp4w") @@ -2827,7 +2874,7 @@ fn test_get_address() { ); assert_eq!( - wallet.get_internal_address(AddressIndex::New), + wallet.peek_address(KeychainKind::Internal, 0), AddressInfo { index: 0, address: Address::from_str("bcrt1q0ue3s5y935tw7v3gmnh36c5zzsaw4n9c9smq79") @@ -2837,11 +2884,11 @@ fn test_get_address() { } ); - let mut wallet = + let wallet = Wallet::new_no_persist(Bip84(key, KeychainKind::External), None, Network::Regtest).unwrap(); assert_eq!( - wallet.get_internal_address(AddressIndex::New), + wallet.peek_address(KeychainKind::Internal, 0), AddressInfo { index: 0, address: Address::from_str("bcrt1qrhgaqu0zvf5q2d0gwwz04w0dh0cuehhqvzpp4w") @@ -2865,10 +2912,16 @@ fn test_get_address_no_reuse_single_descriptor() { let mut used_set = HashSet::new(); (0..3).for_each(|_| { - let external_addr = wallet.get_address(AddressIndex::New).address; + let external_addr = wallet + .reveal_next_address(KeychainKind::External) + .unwrap() + .address; assert!(used_set.insert(external_addr)); - let internal_addr = wallet.get_internal_address(AddressIndex::New).address; + let internal_addr = wallet + .reveal_next_address(KeychainKind::Internal) + .unwrap() + .address; assert!(used_set.insert(internal_addr)); }); } @@ -2877,7 +2930,7 @@ fn test_get_address_no_reuse_single_descriptor() { fn test_taproot_remove_tapfields_after_finalize_sign_option() { let (mut wallet, _) = get_funded_wallet(get_test_tr_with_taptree()); - let addr = wallet.get_address(New); + let addr = wallet.next_unused_address(KeychainKind::External).unwrap(); let mut builder = wallet.build_tx(); builder.drain_to(addr.script_pubkey()).drain_wallet(); let mut psbt = builder.finish().unwrap(); @@ -2902,7 +2955,7 @@ 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 addr = wallet.get_address(AddressIndex::New); + let addr = wallet.reveal_next_address(KeychainKind::External).unwrap(); let mut builder = wallet.build_tx(); builder.add_recipient(addr.script_pubkey(), 25_000); @@ -2937,7 +2990,7 @@ 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 addr = wallet.get_address(AddressIndex::New); + let addr = wallet.reveal_next_address(KeychainKind::External).unwrap(); let path = vec![("rn4nre9c".to_string(), vec![0])] .into_iter() @@ -3003,7 +3056,7 @@ fn test_taproot_psbt_input_tap_tree() { use bitcoin::taproot; let (mut wallet, _) = get_funded_wallet(get_test_tr_with_taptree()); - let addr = wallet.get_address(AddressIndex::Peek(0)); + let addr = wallet.next_unused_address(KeychainKind::External).unwrap(); let mut builder = wallet.build_tx(); builder.drain_to(addr.script_pubkey()).drain_wallet(); @@ -3046,7 +3099,7 @@ fn test_taproot_psbt_input_tap_tree() { #[test] fn test_taproot_sign_missing_witness_utxo() { let (mut wallet, _) = get_funded_wallet(get_test_tr_single_sig()); - let addr = wallet.get_address(New); + let addr = wallet.next_unused_address(KeychainKind::External).unwrap(); let mut builder = wallet.build_tx(); builder.drain_to(addr.script_pubkey()).drain_wallet(); let mut psbt = builder.finish().unwrap(); @@ -3086,7 +3139,7 @@ fn test_taproot_sign_missing_witness_utxo() { #[test] fn test_taproot_sign_using_non_witness_utxo() { let (mut wallet, prev_txid) = get_funded_wallet(get_test_tr_single_sig()); - let addr = wallet.get_address(New); + let addr = wallet.next_unused_address(KeychainKind::External).unwrap(); let mut builder = wallet.build_tx(); builder.drain_to(addr.script_pubkey()).drain_wallet(); let mut psbt = builder.finish().unwrap(); @@ -3154,7 +3207,7 @@ fn test_taproot_foreign_utxo() { } fn test_spend_from_wallet(mut wallet: Wallet) { - let addr = wallet.get_address(AddressIndex::New); + let addr = wallet.next_unused_address(KeychainKind::External).unwrap(); let mut builder = wallet.build_tx(); builder.add_recipient(addr.script_pubkey(), 25_000); @@ -3178,7 +3231,7 @@ fn test_spend_from_wallet(mut wallet: Wallet) { #[test] fn test_taproot_no_key_spend() { let (mut wallet, _) = get_funded_wallet(get_test_tr_with_taptree_both_priv()); - let addr = wallet.get_address(AddressIndex::New); + let addr = wallet.next_unused_address(KeychainKind::External).unwrap(); let mut builder = wallet.build_tx(); builder.add_recipient(addr.script_pubkey(), 25_000); @@ -3213,7 +3266,7 @@ fn test_taproot_script_spend() { fn test_taproot_script_spend_sign_all_leaves() { use bdk::signer::TapLeavesOptions; let (mut wallet, _) = get_funded_wallet(get_test_tr_with_taptree_both_priv()); - let addr = wallet.get_address(AddressIndex::New); + let addr = wallet.next_unused_address(KeychainKind::External).unwrap(); let mut builder = wallet.build_tx(); builder.add_recipient(addr.script_pubkey(), 25_000); @@ -3244,7 +3297,7 @@ fn test_taproot_script_spend_sign_include_some_leaves() { use bitcoin::taproot::TapLeafHash; let (mut wallet, _) = get_funded_wallet(get_test_tr_with_taptree_both_priv()); - let addr = wallet.get_address(AddressIndex::New); + let addr = wallet.next_unused_address(KeychainKind::External).unwrap(); let mut builder = wallet.build_tx(); builder.add_recipient(addr.script_pubkey(), 25_000); @@ -3284,7 +3337,7 @@ fn test_taproot_script_spend_sign_exclude_some_leaves() { use bitcoin::taproot::TapLeafHash; let (mut wallet, _) = get_funded_wallet(get_test_tr_with_taptree_both_priv()); - let addr = wallet.get_address(AddressIndex::New); + let addr = wallet.next_unused_address(KeychainKind::External).unwrap(); let mut builder = wallet.build_tx(); builder.add_recipient(addr.script_pubkey(), 25_000); @@ -3322,7 +3375,7 @@ fn test_taproot_script_spend_sign_exclude_some_leaves() { fn test_taproot_script_spend_sign_no_leaves() { use bdk::signer::TapLeavesOptions; let (mut wallet, _) = get_funded_wallet(get_test_tr_with_taptree_both_priv()); - let addr = wallet.get_address(AddressIndex::New); + let addr = wallet.next_unused_address(KeychainKind::External).unwrap(); let mut builder = wallet.build_tx(); builder.add_recipient(addr.script_pubkey(), 25_000); @@ -3345,7 +3398,7 @@ fn test_taproot_script_spend_sign_no_leaves() { fn test_taproot_sign_derive_index_from_psbt() { let (mut wallet, _) = get_funded_wallet(get_test_tr_single_sig_xprv()); - let addr = wallet.get_address(AddressIndex::New); + let addr = wallet.next_unused_address(KeychainKind::External).unwrap(); let mut builder = wallet.build_tx(); builder.add_recipient(addr.script_pubkey(), 25_000); @@ -3366,7 +3419,7 @@ fn test_taproot_sign_derive_index_from_psbt() { #[test] fn test_taproot_sign_explicit_sighash_all() { let (mut wallet, _) = get_funded_wallet(get_test_tr_single_sig()); - let addr = wallet.get_address(New); + let addr = wallet.next_unused_address(KeychainKind::External).unwrap(); let mut builder = wallet.build_tx(); builder .drain_to(addr.script_pubkey()) @@ -3386,7 +3439,7 @@ fn test_taproot_sign_non_default_sighash() { let sighash = TapSighashType::NonePlusAnyoneCanPay; let (mut wallet, _) = get_funded_wallet(get_test_tr_single_sig()); - let addr = wallet.get_address(New); + let addr = wallet.next_unused_address(KeychainKind::External).unwrap(); let mut builder = wallet.build_tx(); builder .drain_to(addr.script_pubkey()) @@ -3470,8 +3523,11 @@ fn test_spend_coinbase() { ..Default::default() }], output: vec![TxOut { + script_pubkey: wallet + .next_unused_address(KeychainKind::External) + .unwrap() + .script_pubkey(), value: Amount::from_sat(25_000), - script_pubkey: wallet.get_address(New).address.script_pubkey(), }], }; wallet @@ -3559,7 +3615,7 @@ fn test_spend_coinbase() { fn test_allow_dust_limit() { let (mut wallet, _) = get_funded_wallet(get_test_single_sig_cltv()); - let addr = wallet.get_address(New); + let addr = wallet.next_unused_address(KeychainKind::External).unwrap(); let mut builder = wallet.build_tx(); @@ -3585,7 +3641,7 @@ fn test_fee_rate_sign_no_grinding_high_r() { // instead of 70). We then check that our fee rate and fee calculation is // alright. let (mut wallet, _) = get_funded_wallet("wpkh(tprv8ZgxMBicQKsPd3EupYiPRhaMooHKUHJxNsTfYuScep13go8QFfHdtkG9nRkFGb7busX4isf6X9dURGCoKgitaApQ6MupRhZMcELAxTBRJgS/*)"); - let addr = wallet.get_address(New); + let addr = wallet.next_unused_address(KeychainKind::External).unwrap(); let fee_rate = FeeRate::from_sat_per_vb_unchecked(1); let mut builder = wallet.build_tx(); let mut data = PushBytesBuf::try_from(vec![0]).unwrap(); @@ -3651,7 +3707,7 @@ fn test_fee_rate_sign_grinding_low_r() { // We then check that our fee rate and fee calculation is alright and that our // signature is 70 bytes. let (mut wallet, _) = get_funded_wallet("wpkh(tprv8ZgxMBicQKsPd3EupYiPRhaMooHKUHJxNsTfYuScep13go8QFfHdtkG9nRkFGb7busX4isf6X9dURGCoKgitaApQ6MupRhZMcELAxTBRJgS/*)"); - let addr = wallet.get_address(New); + let addr = wallet.next_unused_address(KeychainKind::External).unwrap(); let fee_rate = FeeRate::from_sat_per_vb_unchecked(1); let mut builder = wallet.build_tx(); builder @@ -3684,8 +3740,8 @@ fn test_taproot_load_descriptor_duplicated_keys() { // // Having the same key in multiple taproot leaves is safe and should be accepted by BDK - let (mut wallet, _) = get_funded_wallet(get_test_tr_dup_keys()); - let addr = wallet.get_address(New); + let (wallet, _) = get_funded_wallet(get_test_tr_dup_keys()); + let addr = wallet.peek_address(KeychainKind::External, 0); assert_eq!( addr.to_string(), From a266b4718f7839759a3e615805931041b67b8e91 Mon Sep 17 00:00:00 2001 From: valued mammal Date: Sun, 14 Apr 2024 10:58:59 -0400 Subject: [PATCH 3/4] chore(wallet)!: Remove enum AddressIndex --- crates/bdk/src/wallet/mod.rs | 120 ------------------ crates/hwi/src/lib.rs | 1 - example-crates/wallet_electrum/src/main.rs | 4 +- .../wallet_esplora_async/src/main.rs | 6 +- .../wallet_esplora_blocking/src/main.rs | 6 +- 5 files changed, 8 insertions(+), 129 deletions(-) diff --git a/crates/bdk/src/wallet/mod.rs b/crates/bdk/src/wallet/mod.rs index 00f0c461..a03d7ac7 100644 --- a/crates/bdk/src/wallet/mod.rs +++ b/crates/bdk/src/wallet/mod.rs @@ -178,28 +178,6 @@ impl } } -/// The address index selection strategy to use to derived an address from the wallet's external -/// descriptor. -#[derive(Debug)] -pub enum AddressIndex { - /// Return a new address after incrementing the current descriptor index. - New, - /// Return the address for the current descriptor index if it has not been used in a received - /// transaction. Otherwise return a new address as with [`AddressIndex::New`]. - /// - /// Use with caution, if the wallet has not yet detected an address has been used it could - /// return an already used address. This function is primarily meant for situations where the - /// caller is untrusted; for example when deriving donation addresses on-demand for a public - /// web page. - LastUnused, - /// Return the address for a specific descriptor index. Does not change the current descriptor - /// index used by `AddressIndex::New` and `AddressIndex::LastUsed`. - /// - /// Use with caution, if an index is given that is less than the current descriptor index - /// then the returned address may have already been used. - Peek(u32), -} - /// A derived address and the index it was found at. /// For convenience this automatically derefs to `Address` #[derive(Debug, PartialEq, Eq)] @@ -640,104 +618,6 @@ impl Wallet { self.indexed_graph.index.keychains() } - /// Return a derived address using the external descriptor, see [`AddressIndex`] for - /// available address index selection strategies. If none of the keys in the descriptor are derivable - /// (i.e. does not end with /*) then the same address will always be returned for any [`AddressIndex`]. - /// - /// A `PersistBackend::WriteError` will result if unable to persist the new address - /// to the `PersistBackend`. - /// - /// # Panics - /// - /// This panics when the caller requests for an address of derivation index greater than the - /// BIP32 max index. - pub fn try_get_address(&mut self, address_index: AddressIndex) -> anyhow::Result { - self._get_address(KeychainKind::External, address_index) - } - - /// Return a derived address using the internal (change) descriptor. - /// - /// If the wallet doesn't have an internal descriptor it will use the external descriptor. - /// - /// A `PersistBackend::WriteError` will result if unable to persist the new address - /// to the `PersistBackend`. - /// - /// see [`AddressIndex`] for available address index selection strategies. If none of the keys - /// in the descriptor are derivable (i.e. does not end with /*) then the same address will always - /// be returned for any [`AddressIndex`]. - /// - /// # Panics - /// - /// This panics when the caller requests for an address of derivation index greater than the - /// BIP32 max index. - pub fn try_get_internal_address( - &mut self, - address_index: AddressIndex, - ) -> anyhow::Result { - self._get_address(KeychainKind::Internal, address_index) - } - - /// Return a derived address using the specified `keychain` (external/internal). - /// - /// If `keychain` is [`KeychainKind::External`], external addresses will be derived (used for - /// receiving funds). - /// - /// If `keychain` is [`KeychainKind::Internal`], internal addresses will be derived (used for - /// creating change outputs). If the wallet does not have an internal keychain, it will use the - /// external keychain to derive change outputs. - /// - /// See [`AddressIndex`] for available address index selection strategies. If none of the keys - /// in the descriptor are derivable (i.e. does not end with /*) then the same address will - /// always be returned for any [`AddressIndex`]. - /// - /// # Panics - /// - /// This panics when the caller requests for an address of derivation index greater than the - /// BIP32 max index. - fn _get_address( - &mut self, - keychain: KeychainKind, - address_index: AddressIndex, - ) -> anyhow::Result { - let keychain = self.map_keychain(keychain); - let txout_index = &mut self.indexed_graph.index; - let (index, spk, changeset) = match address_index { - AddressIndex::New => { - let ((index, spk), index_changeset) = txout_index.reveal_next_spk(&keychain); - (index, spk.into(), Some(index_changeset)) - } - AddressIndex::LastUnused => { - let ((index, spk), index_changeset) = txout_index.next_unused_spk(&keychain); - (index, spk.into(), Some(index_changeset)) - } - AddressIndex::Peek(mut peek_index) => { - let mut spk_iter = txout_index.unbounded_spk_iter(&keychain); - if !spk_iter.descriptor().has_wildcard() { - peek_index = 0; - } - let (index, spk) = spk_iter - .nth(peek_index as usize) - .expect("derivation index is out of bounds"); - (index, spk, None) - } - }; - - if let Some(changeset) = changeset { - self.persist - .stage(ChangeSet::from(indexed_tx_graph::ChangeSet::from( - changeset, - ))); - self.persist.commit()?; - } - - Ok(AddressInfo { - index, - address: Address::from_script(&spk, self.network) - .expect("descriptor must have address form"), - keychain, - }) - } - /// Peek an address of the given `keychain` at `index` without revealing it. /// /// For non-wildcard descriptors this returns the same address at every provided index. diff --git a/crates/hwi/src/lib.rs b/crates/hwi/src/lib.rs index 4e2cd0c0..ab87e8a8 100644 --- a/crates/hwi/src/lib.rs +++ b/crates/hwi/src/lib.rs @@ -6,7 +6,6 @@ //! # use bdk::bitcoin::Network; //! # use bdk::signer::SignerOrdering; //! # use bdk_hwi::HWISigner; -//! # use bdk::wallet::AddressIndex::New; //! # use bdk::{KeychainKind, SignOptions, Wallet}; //! # use hwi::HWIClient; //! # use std::sync::Arc; diff --git a/example-crates/wallet_electrum/src/main.rs b/example-crates/wallet_electrum/src/main.rs index a1a628c7..4af9e71d 100644 --- a/example-crates/wallet_electrum/src/main.rs +++ b/example-crates/wallet_electrum/src/main.rs @@ -8,8 +8,8 @@ use std::str::FromStr; use bdk::bitcoin::Address; use bdk::wallet::Update; -use bdk::SignOptions; use bdk::{bitcoin::Network, Wallet}; +use bdk::{KeychainKind, SignOptions}; use bdk_electrum::{ electrum_client::{self, ElectrumApi}, ElectrumExt, ElectrumUpdate, @@ -29,7 +29,7 @@ fn main() -> Result<(), anyhow::Error> { Network::Testnet, )?; - let address = wallet.try_get_address(bdk::wallet::AddressIndex::New)?; + let address = wallet.next_unused_address(KeychainKind::External)?; println!("Generated Address: {}", address); let balance = wallet.get_balance(); diff --git a/example-crates/wallet_esplora_async/src/main.rs b/example-crates/wallet_esplora_async/src/main.rs index 14e9e38d..734b3491 100644 --- a/example-crates/wallet_esplora_async/src/main.rs +++ b/example-crates/wallet_esplora_async/src/main.rs @@ -2,8 +2,8 @@ use std::{io::Write, str::FromStr}; use bdk::{ bitcoin::{Address, Network}, - wallet::{AddressIndex, Update}, - SignOptions, Wallet, + wallet::Update, + KeychainKind, SignOptions, Wallet, }; use bdk_esplora::{esplora_client, EsploraAsyncExt}; use bdk_file_store::Store; @@ -27,7 +27,7 @@ async fn main() -> Result<(), anyhow::Error> { Network::Testnet, )?; - let address = wallet.try_get_address(AddressIndex::New)?; + let address = wallet.next_unused_address(KeychainKind::External)?; println!("Generated Address: {}", address); let balance = wallet.get_balance(); diff --git a/example-crates/wallet_esplora_blocking/src/main.rs b/example-crates/wallet_esplora_blocking/src/main.rs index 1815650d..cb8fa729 100644 --- a/example-crates/wallet_esplora_blocking/src/main.rs +++ b/example-crates/wallet_esplora_blocking/src/main.rs @@ -7,8 +7,8 @@ use std::{io::Write, str::FromStr}; use bdk::{ bitcoin::{Address, Network}, - wallet::{AddressIndex, Update}, - SignOptions, Wallet, + wallet::Update, + KeychainKind, SignOptions, Wallet, }; use bdk_esplora::{esplora_client, EsploraExt}; use bdk_file_store::Store; @@ -26,7 +26,7 @@ fn main() -> Result<(), anyhow::Error> { Network::Testnet, )?; - let address = wallet.try_get_address(AddressIndex::New)?; + let address = wallet.next_unused_address(KeychainKind::External)?; println!("Generated Address: {}", address); let balance = wallet.get_balance(); From d39b319ddf6bf3365d5d01841a8d366c6231db06 Mon Sep 17 00:00:00 2001 From: valued mammal Date: Sun, 14 Apr 2024 16:02:10 -0400 Subject: [PATCH 4/4] test(wallet): Test wallet addresses Adds test coverage for Wallet methods `reveal_addresses_to`, `mark_used`, and `unmark_used` --- crates/bdk/tests/wallet.rs | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/crates/bdk/tests/wallet.rs b/crates/bdk/tests/wallet.rs index b41c4b42..9a0dd3bf 100644 --- a/crates/bdk/tests/wallet.rs +++ b/crates/bdk/tests/wallet.rs @@ -2705,6 +2705,7 @@ fn test_next_unused_address() { "tb1q6yn66vajcctph75pvylgkksgpp6nq04ppwct9a" ); assert_eq!(wallet.derivation_index(KeychainKind::External), Some(0)); + // calling next_unused again gives same address assert_eq!( wallet .next_unused_address(KeychainKind::External) @@ -2714,6 +2715,15 @@ fn test_next_unused_address() { ); assert_eq!(wallet.derivation_index(KeychainKind::External), Some(0)); + // test mark used / unused + assert!(wallet.mark_used(KeychainKind::External, 0)); + let next_unused_addr = wallet.next_unused_address(KeychainKind::External).unwrap(); + assert_eq!(next_unused_addr.index, 1); + + assert!(wallet.unmark_used(KeychainKind::External, 0)); + let next_unused_addr = wallet.next_unused_address(KeychainKind::External).unwrap(); + assert_eq!(next_unused_addr.index, 0); + // use the above address receive_output_in_latest_block(&mut wallet, 25_000); @@ -2725,6 +2735,9 @@ fn test_next_unused_address() { "tb1q4er7kxx6sssz3q7qp7zsqsdx4erceahhax77d7" ); assert_eq!(wallet.derivation_index(KeychainKind::External), Some(1)); + + // trying to mark index 0 unused should return false + assert!(!wallet.unmark_used(KeychainKind::External, 0)); } #[test] @@ -2900,6 +2913,28 @@ fn test_get_address() { ); } +#[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 keychain = KeychainKind::External; + + let last_revealed_addr = wallet + .reveal_addresses_to(keychain, 9) + .unwrap() + .last() + .unwrap(); + assert_eq!(wallet.derivation_index(keychain), Some(9)); + + let unused_addrs = wallet.list_unused_addresses(keychain).collect::>(); + assert_eq!(unused_addrs.len(), 10); + assert_eq!(unused_addrs.last().unwrap(), &last_revealed_addr); + + // revealing to an already revealed index returns nothing + let mut already_revealed = wallet.reveal_addresses_to(keychain, 9).unwrap(); + assert!(already_revealed.next().is_none()); +} + #[test] fn test_get_address_no_reuse_single_descriptor() { use bdk::descriptor::template::Bip84;