1+ use std:: collections:: BTreeMap ;
12use std:: path:: Path ;
23use std:: str:: FromStr ;
34use std:: sync:: Arc ;
45
56use anyhow:: Context ;
67use assert_matches:: assert_matches;
7- use bdk_chain:: { BlockId , CanonicalizationParams , ChainPosition , ConfirmationBlockTime } ;
8+ use bdk_chain:: {
9+ keychain_txout:: DEFAULT_LOOKAHEAD , BlockId , CanonicalizationParams , ChainPosition ,
10+ ConfirmationBlockTime , DescriptorExt ,
11+ } ;
812use bdk_wallet:: coin_selection:: { self , LargestFirstCoinSelection } ;
913use bdk_wallet:: descriptor:: { calc_checksum, DescriptorError , IntoWalletDescriptor } ;
1014use bdk_wallet:: error:: CreateTxError ;
@@ -70,6 +74,7 @@ fn wallet_is_persisted() -> anyhow::Result<()> {
7074 let mut db = create_db ( & file_path) ?;
7175 let mut wallet = Wallet :: create ( external_desc, internal_desc)
7276 . network ( Network :: Testnet )
77+ . use_spk_cache ( true )
7378 . create_wallet ( & mut db) ?;
7479 wallet. reveal_next_address ( KeychainKind :: External ) ;
7580
@@ -79,13 +84,6 @@ fn wallet_is_persisted() -> anyhow::Result<()> {
7984 } ;
8085
8186 // recover wallet
82- {
83- let mut db = open_db ( & file_path) . context ( "failed to recover db" ) ?;
84- let _ = Wallet :: load ( )
85- . check_network ( Network :: Testnet )
86- . load_wallet ( & mut db) ?
87- . expect ( "wallet must exist" ) ;
88- }
8987 {
9088 let mut db = open_db ( & file_path) . context ( "failed to recover db" ) ?;
9189 let wallet = Wallet :: load ( )
@@ -113,6 +111,67 @@ fn wallet_is_persisted() -> anyhow::Result<()> {
113111 . 0
114112 ) ;
115113 }
114+ // Test SPK cache
115+ {
116+ let mut db = open_db ( & file_path) . context ( "failed to recover db" ) ?;
117+ let mut wallet = Wallet :: load ( )
118+ . check_network ( Network :: Testnet )
119+ . use_spk_cache ( true )
120+ . load_wallet ( & mut db) ?
121+ . expect ( "wallet must exist" ) ;
122+
123+ let external_did = wallet
124+ . public_descriptor ( KeychainKind :: External )
125+ . descriptor_id ( ) ;
126+ let internal_did = wallet
127+ . public_descriptor ( KeychainKind :: Internal )
128+ . descriptor_id ( ) ;
129+
130+ assert ! ( wallet. staged( ) . is_none( ) ) ;
131+
132+ let _addr = wallet. reveal_next_address ( KeychainKind :: External ) ;
133+ let cs = wallet. staged ( ) . expect ( "we should have staged a changeset" ) ;
134+ assert ! ( !cs. indexer. spk_cache. is_empty( ) , "failed to cache spks" ) ;
135+ assert_eq ! ( cs. indexer. spk_cache. len( ) , 2 , "we persisted two keychains" ) ;
136+ let spk_cache: & BTreeMap < u32 , ScriptBuf > =
137+ cs. indexer . spk_cache . get ( & external_did) . unwrap ( ) ;
138+ assert_eq ! ( spk_cache. len( ) as u32 , 1 + 1 + DEFAULT_LOOKAHEAD ) ;
139+ assert_eq ! ( spk_cache. keys( ) . last( ) , Some ( & 26 ) ) ;
140+ let spk_cache = cs. indexer . spk_cache . get ( & internal_did) . unwrap ( ) ;
141+ assert_eq ! ( spk_cache. len( ) as u32 , DEFAULT_LOOKAHEAD ) ;
142+ assert_eq ! ( spk_cache. keys( ) . last( ) , Some ( & 24 ) ) ;
143+ // Clear the stage
144+ let _ = wallet. take_staged ( ) ;
145+ let _addr = wallet. reveal_next_address ( KeychainKind :: Internal ) ;
146+ let cs = wallet. staged ( ) . unwrap ( ) ;
147+ assert_eq ! ( cs. indexer. spk_cache. len( ) , 1 ) ;
148+ let spk_cache = cs. indexer . spk_cache . get ( & internal_did) . unwrap ( ) ;
149+ assert_eq ! ( spk_cache. len( ) , 1 ) ;
150+ assert_eq ! ( spk_cache. keys( ) . next( ) , Some ( & 25 ) ) ;
151+ }
152+ // SPK cache requires load params
153+ {
154+ let mut db = open_db ( & file_path) . context ( "failed to recover db" ) ?;
155+ let mut wallet = Wallet :: load ( )
156+ . check_network ( Network :: Testnet )
157+ // .use_spk_cache(false)
158+ . load_wallet ( & mut db) ?
159+ . expect ( "wallet must exist" ) ;
160+
161+ let internal_did = wallet
162+ . public_descriptor ( KeychainKind :: Internal )
163+ . descriptor_id ( ) ;
164+
165+ assert ! ( wallet. staged( ) . is_none( ) ) ;
166+
167+ let _addr = wallet. reveal_next_address ( KeychainKind :: Internal ) ;
168+ let cs = wallet. staged ( ) . expect ( "we should have staged a changeset" ) ;
169+ assert_eq ! ( cs. indexer. last_revealed. get( & internal_did) , Some ( & 0 ) ) ;
170+ assert ! (
171+ cs. indexer. spk_cache. is_empty( ) ,
172+ "we didn't set `use_spk_cache`"
173+ ) ;
174+ }
116175
117176 Ok ( ( ) )
118177 }
@@ -4406,7 +4465,7 @@ fn test_taproot_load_descriptor_duplicated_keys() {
44064465#[ test]
44074466#[ cfg( debug_assertions) ]
44084467#[ should_panic(
4409- expected = "replenish lookahead: must not have existing spk: keychain=Internal, lookahead=25, next_store_index=0, next_reveal_index =0"
4468+ expected = "replenish lookahead: must not have existing spk: keychain=Internal, lookahead=25, next_index =0"
44104469) ]
44114470fn test_keychains_with_overlapping_spks ( ) {
44124471 // this can happen if a non-wildcard descriptor keychain derives an spk that a
0 commit comments