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