1
+ use std:: collections:: BTreeMap ;
1
2
use std:: path:: Path ;
2
3
use std:: str:: FromStr ;
3
4
use std:: sync:: Arc ;
4
5
5
6
use anyhow:: Context ;
6
7
use 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
+ } ;
8
12
use bdk_wallet:: coin_selection:: { self , LargestFirstCoinSelection } ;
9
13
use bdk_wallet:: descriptor:: { calc_checksum, DescriptorError , IntoWalletDescriptor } ;
10
14
use bdk_wallet:: error:: CreateTxError ;
@@ -70,6 +74,7 @@ fn wallet_is_persisted() -> anyhow::Result<()> {
70
74
let mut db = create_db ( & file_path) ?;
71
75
let mut wallet = Wallet :: create ( external_desc, internal_desc)
72
76
. network ( Network :: Testnet )
77
+ . use_spk_cache ( true )
73
78
. create_wallet ( & mut db) ?;
74
79
wallet. reveal_next_address ( KeychainKind :: External ) ;
75
80
@@ -79,13 +84,6 @@ fn wallet_is_persisted() -> anyhow::Result<()> {
79
84
} ;
80
85
81
86
// 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
- }
89
87
{
90
88
let mut db = open_db ( & file_path) . context ( "failed to recover db" ) ?;
91
89
let wallet = Wallet :: load ( )
@@ -113,6 +111,67 @@ fn wallet_is_persisted() -> anyhow::Result<()> {
113
111
. 0
114
112
) ;
115
113
}
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
+ }
116
175
117
176
Ok ( ( ) )
118
177
}
@@ -4406,7 +4465,7 @@ fn test_taproot_load_descriptor_duplicated_keys() {
4406
4465
#[ test]
4407
4466
#[ cfg( debug_assertions) ]
4408
4467
#[ 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"
4410
4469
) ]
4411
4470
fn test_keychains_with_overlapping_spks ( ) {
4412
4471
// this can happen if a non-wildcard descriptor keychain derives an spk that a
0 commit comments