Skip to content

Commit e468a1b

Browse files
committed
feat!: modify AddressInfo, add reveal_next_address
Also added default version of `reveal_next_address`.
1 parent 551a7e9 commit e468a1b

File tree

1 file changed

+151
-4
lines changed

1 file changed

+151
-4
lines changed

wallet/src/wallet/mod.rs

Lines changed: 151 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -149,24 +149,24 @@ impl From<SyncResponse> for Update {
149149
/// A derived address and the index it was found at.
150150
/// For convenience this automatically derefs to `Address`
151151
#[derive(Debug, Clone, PartialEq, Eq)]
152-
pub struct AddressInfo {
152+
pub struct AddressInfo<K> {
153153
/// Child index of this address
154154
pub index: u32,
155155
/// Address
156156
pub address: Address,
157157
/// Type of keychain
158-
pub keychain: KeychainKind,
158+
pub keychain: K,
159159
}
160160

161-
impl Deref for AddressInfo {
161+
impl<K> Deref for AddressInfo<K> {
162162
type Target = Address;
163163

164164
fn deref(&self) -> &Self::Target {
165165
&self.address
166166
}
167167
}
168168

169-
impl fmt::Display for AddressInfo {
169+
impl<K> fmt::Display for AddressInfo<K> {
170170
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
171171
write!(f, "{}", self.address)
172172
}
@@ -353,6 +353,77 @@ where
353353
stage,
354354
}
355355
}
356+
357+
/// Reveal the next address of the default `keychain`.
358+
///
359+
/// This is equivalent to calling [`Self::reveal_next_address`] with the default `keychain` as
360+
/// argument. Note: This is not fallible as the default keychain must always exist!
361+
///
362+
/// **WARNING**: To avoid address reuse you must persist the changes resulting from one or more
363+
/// calls to this method before closing the wallet. For example:
364+
// TODO: Fix the following example:
365+
// ///
366+
// /// ```rust,no_run
367+
// /// # use bdk_wallet::{LoadParams, ChangeSet, KeychainKind};
368+
// /// use bdk_chain::rusqlite::Connection;
369+
// /// let mut conn = Connection::open_in_memory().expect("must open connection");
370+
// /// let mut wallet = LoadParams::new()
371+
// /// .load_wallet(&mut conn)
372+
// /// .expect("database is okay")
373+
// /// .expect("database has data");
374+
// /// let next_address = wallet.reveal_next_address(KeychainKind::External);
375+
// /// wallet.persist(&mut conn).expect("write is okay");
376+
// ///
377+
// /// // Now it's safe to show the user their next address!
378+
// /// println!("Next address: {}", next_address.address);
379+
// /// # Ok::<(), anyhow::Error>(())
380+
// /// ```
381+
pub fn reveal_next_default_address(&mut self) -> AddressInfo<K> {
382+
self.reveal_next_address(self.keyring.default_keychain())
383+
.expect("default keychain must always exist!")
384+
}
385+
386+
/// Attempt to reveal the next address of the given `keychain`.
387+
///
388+
/// This will increment the keychain's derivation index. If the keychain's descriptor doesn't
389+
/// contain a wildcard or every address is already revealed up to the maximum derivation
390+
/// index defined in [BIP32](https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki),
391+
/// then the last revealed address will be returned.
392+
///
393+
/// **WARNING**: To avoid address reuse you must persist the changes resulting from one or more
394+
/// calls to this method before closing the wallet. For example:
395+
// TODO: Fix the following example:
396+
// ///
397+
// /// ```rust,no_run
398+
// /// # use bdk_wallet::{LoadParams, ChangeSet, KeychainKind};
399+
// /// use bdk_chain::rusqlite::Connection;
400+
// /// let mut conn = Connection::open_in_memory().expect("must open connection");
401+
// /// let mut wallet = LoadParams::new()
402+
// /// .load_wallet(&mut conn)
403+
// /// .expect("database is okay")
404+
// /// .expect("database has data");
405+
// /// let next_address = wallet.reveal_next_address(KeychainKind::External);
406+
// /// wallet.persist(&mut conn).expect("write is okay");
407+
// ///
408+
// /// // Now it's safe to show the user their next address!
409+
// /// println!("Next address: {}", next_address.address);
410+
// /// # Ok::<(), anyhow::Error>(())
411+
// /// ```
412+
pub fn reveal_next_address(&mut self, keychain: K) -> Option<AddressInfo<K>> {
413+
let index = &mut self.tx_graph.index;
414+
let stage = &mut self.stage;
415+
416+
let ((index, spk), index_changeset) = index.reveal_next_spk(keychain.clone())?;
417+
418+
stage.merge(index_changeset.into());
419+
420+
Some(AddressInfo {
421+
index,
422+
address: Address::from_script(spk.as_script(), self.keyring.network)
423+
.expect("must have address form"),
424+
keychain,
425+
})
426+
}
356427
}
357428

358429
// impl Wallet {
@@ -2861,6 +2932,82 @@ mod test {
28612932
// use crate::miniscript::Error::Unexpected;
28622933
// use crate::test_utils::get_test_tr_single_sig_xprv_and_change_desc;
28632934
// use crate::test_utils::insert_tx;
2935+
use bdk_chain::DescriptorId;
2936+
use core::str::FromStr;
2937+
use miniscript::{Descriptor, DescriptorPublicKey};
2938+
2939+
const DESCRIPTORS: [&str; 6] = [
2940+
"wpkh(tpubDCzuCBKnZA5TNKhiJnASku7kq8Q4iqcVF82JV7mHo2NxWpXkLRbrJaGA5ToE7LCuWpcPErBbpDzbdWKN8aTdJzmRy1jQPmZvnqpwwDwCdy7/1/*)",
2941+
"wpkh(tpubDCzuCBKnZA5TNKhiJnASku7kq8Q4iqcVF82JV7mHo2NxWpXkLRbrJaGA5ToE7LCuWpcPErBbpDzbdWKN8aTdJzmRy1jQPmZvnqpwwDwCdy7/2/*)",
2942+
"tr(tpubDCzuCBKnZA5TNKhiJnASku7kq8Q4iqcVF82JV7mHo2NxWpXkLRbrJaGA5ToE7LCuWpcPErBbpDzbdWKN8aTdJzmRy1jQPmZvnqpwwDwCdy7/3/*)",
2943+
"tr(tpubDCzuCBKnZA5TNKhiJnASku7kq8Q4iqcVF82JV7mHo2NxWpXkLRbrJaGA5ToE7LCuWpcPErBbpDzbdWKN8aTdJzmRy1jQPmZvnqpwwDwCdy7/4/*)",
2944+
"pkh(tpubDCzuCBKnZA5TNKhiJnASku7kq8Q4iqcVF82JV7mHo2NxWpXkLRbrJaGA5ToE7LCuWpcPErBbpDzbdWKN8aTdJzmRy1jQPmZvnqpwwDwCdy7/5/*)",
2945+
"pkh(tpubDCzuCBKnZA5TNKhiJnASku7kq8Q4iqcVF82JV7mHo2NxWpXkLRbrJaGA5ToE7LCuWpcPErBbpDzbdWKN8aTdJzmRy1jQPmZvnqpwwDwCdy7/6/*)"];
2946+
2947+
/// Parse a descriptor string
2948+
fn parse_descriptor(s: &str) -> Descriptor<DescriptorPublicKey> {
2949+
Descriptor::parse_descriptor(&Secp256k1::new(), s)
2950+
.expect("failed to parse descriptor")
2951+
.0
2952+
}
2953+
2954+
fn test_keyring(desc_strs: impl IntoIterator<Item = &'static str>) -> KeyRing<DescriptorId> {
2955+
let mut desc_strs = desc_strs.into_iter();
2956+
let desc = parse_descriptor(desc_strs.next().unwrap());
2957+
let mut keyring = KeyRing::new(Network::Testnet4, desc.descriptor_id(), desc);
2958+
for desc_str in desc_strs {
2959+
let desc = parse_descriptor(desc_str);
2960+
keyring.add_descriptor(desc.descriptor_id(), desc, false);
2961+
}
2962+
keyring
2963+
}
2964+
2965+
#[test]
2966+
fn correct_address_is_revealed() {
2967+
let mut wallet = Wallet::new(test_keyring(DESCRIPTORS));
2968+
let addrinfo = wallet.reveal_next_default_address();
2969+
assert_eq!(
2970+
addrinfo.address.into_unchecked(),
2971+
Address::from_str("tb1qat8h88r778d8e0x38t2ljtzmgkjusgn2u38avs").unwrap()
2972+
);
2973+
let addrinfo = wallet
2974+
.reveal_next_address(parse_descriptor(DESCRIPTORS[1]).descriptor_id())
2975+
.unwrap();
2976+
assert_eq!(
2977+
addrinfo.address.into_unchecked(),
2978+
Address::from_str("tb1qun8txyd3p4xgts6y6lj8h2dcxk20s487ll7ss3").unwrap()
2979+
);
2980+
let addrinfo = wallet
2981+
.reveal_next_address(parse_descriptor(DESCRIPTORS[2]).descriptor_id())
2982+
.unwrap();
2983+
assert_eq!(
2984+
addrinfo.address.into_unchecked(),
2985+
Address::from_str("tb1pnz3jex4wnz88e46rfzckpd9xyvdde8h2hnes4wrllkhygump8c2se9rusg")
2986+
.unwrap()
2987+
);
2988+
let addrinfo = wallet
2989+
.reveal_next_address(parse_descriptor(DESCRIPTORS[3]).descriptor_id())
2990+
.unwrap();
2991+
assert_eq!(
2992+
addrinfo.address.into_unchecked(),
2993+
Address::from_str("tb1pv6hmnghp0wtxzeqsvshdq4ennvmqg3eq78vluvzvfkqtmtd5e49q8zht5v")
2994+
.unwrap()
2995+
);
2996+
let addrinfo = wallet
2997+
.reveal_next_address(parse_descriptor(DESCRIPTORS[4]).descriptor_id())
2998+
.unwrap();
2999+
assert_eq!(
3000+
addrinfo.address.into_unchecked(),
3001+
Address::from_str("n3TJoFpLPBMGisVYHUGcEBwd9d1FVBwbJQ").unwrap()
3002+
);
3003+
let addrinfo = wallet
3004+
.reveal_next_address(parse_descriptor(DESCRIPTORS[5]).descriptor_id())
3005+
.unwrap();
3006+
assert_eq!(
3007+
addrinfo.address.into_unchecked(),
3008+
Address::from_str("mq2r39CD8ZnMqyuytQq2zPa1sfHTT6Rjo8").unwrap()
3009+
);
3010+
}
28643011

28653012
// #[test]
28663013
// fn not_duplicated_utxos_across_optional_and_required() {

0 commit comments

Comments
 (0)