@@ -93,7 +93,8 @@ pub const DEFAULT_LOOKAHEAD: u32 = 25;
9393/// }
9494/// }
9595///
96- /// let mut txout_index = KeychainTxOutIndex::<MyKeychain>::default();
96+ /// // Construct index with lookahead of 21 and enable spk caching.
97+ /// let mut txout_index = KeychainTxOutIndex::<MyKeychain>::new(21, true);
9798///
9899/// # let secp = bdk_chain::bitcoin::secp256k1::Secp256k1::signing_only();
99100/// # let (external_descriptor,_) = Descriptor::<DescriptorPublicKey>::parse_descriptor(&secp, "tr([73c5da0a/86'/0'/0']xprv9xgqHN7yz9MwCkxsBPN5qetuNdQSUttZNKw1dcYTV4mkaAFiBVGQziHs3NRSWMkCzvgjEe3n9xV8oYywvM8at9yRqyaZVz6TYYhX98VjsUk/0/*)").unwrap();
@@ -132,8 +133,12 @@ pub struct KeychainTxOutIndex<K> {
132133 last_revealed : HashMap < DescriptorId , u32 > ,
133134 lookahead : u32 ,
134135
135- use_spk_cache : bool ,
136+ /// If `true`, the script pubkeys are persisted across restarts to avoid re-derivation.
137+ /// If `false`, `spk_cache` and `spk_cache_stage` will remain empty.
138+ persist_spks : bool ,
139+ /// Cache of derived spks.
136140 spk_cache : BTreeMap < DescriptorId , HashMap < u32 , ScriptBuf > > ,
141+ /// Staged script pubkeys waiting to be written out in the next ChangeSet.
137142 spk_cache_stage : BTreeMap < DescriptorId , Vec < ( u32 , ScriptBuf ) > > ,
138143}
139144
@@ -199,27 +204,38 @@ impl<K> KeychainTxOutIndex<K> {
199204 ///
200205 /// # Lookahead
201206 ///
202- /// The `lookahead` is the number of script pubkeys to derive and cache from the internal
203- /// descriptors over and above the last revealed script index. Without a lookahead the index
204- /// will miss outputs you own when processing transactions whose output script pubkeys lie
205- /// beyond the last revealed index. In certain situations, such as when performing an initial
206- /// scan of the blockchain during wallet import, it may be uncertain or unknown what the index
207- /// of the last revealed script pubkey actually is.
207+ /// The `lookahead` parameter controls how many script pubkeys to derive *beyond* the highest
208+ /// revealed index for each keychain (external/internal). Without any lookahead, the index will
209+ /// miss outputs sent to addresses you haven’t explicitly revealed yet. A nonzero `lookahead`
210+ /// lets you catch outputs on those “future” addresses automatically.
208211 ///
209212 /// Refer to [struct-level docs](KeychainTxOutIndex) for more about `lookahead`.
210213 ///
211- /// # Script pubkey cache
214+ /// # Script pubkey persistence
212215 ///
213- /// The `use_spk_cache` parameter enables the internal script pubkey cache.
214- pub fn new ( lookahead : u32 , use_spk_cache : bool ) -> Self {
216+ /// Derived script pubkeys remain in memory. If `persist_spks` is `true`, they're saved and
217+ /// reloaded via the `ChangeSet` on startup, avoiding re-derivation. Otherwise, they must be
218+ /// re-derived on init, affecting startup only for very large or complex wallets.
219+ ///
220+ /// # Examples
221+ ///
222+ /// ```rust
223+ /// # use bdk_chain::keychain_txout::KeychainTxOutIndex;
224+ /// // Derive 20 future addresses per chain and persist + reload script pubkeys via ChangeSets:
225+ /// let idx = KeychainTxOutIndex::<&'static str>::new(20, true);
226+ ///
227+ /// // Derive 10 future addresses per chain without persistence:
228+ /// let idx = KeychainTxOutIndex::<&'static str>::new(10, false);
229+ /// ```
230+ pub fn new ( lookahead : u32 , persist_spks : bool ) -> Self {
215231 Self {
216232 inner : SpkTxOutIndex :: default ( ) ,
217233 keychain_to_descriptor_id : Default :: default ( ) ,
218234 descriptors : Default :: default ( ) ,
219235 descriptor_id_to_keychain : Default :: default ( ) ,
220236 last_revealed : Default :: default ( ) ,
221237 lookahead,
222- use_spk_cache ,
238+ persist_spks ,
223239 spk_cache : Default :: default ( ) ,
224240 spk_cache_stage : Default :: default ( ) ,
225241 }
@@ -270,7 +286,7 @@ impl<K: Clone + Ord + Debug> KeychainTxOutIndex<K> {
270286 }
271287
272288 fn _empty_stage_into_changeset ( & mut self , changeset : & mut ChangeSet ) {
273- if !self . use_spk_cache {
289+ if !self . persist_spks {
274290 return ;
275291 }
276292 for ( did, spks) in core:: mem:: take ( & mut self . spk_cache_stage ) {
@@ -542,7 +558,7 @@ impl<K: Clone + Ord + Debug> KeychainTxOutIndex<K> {
542558 1
543559 } ;
544560
545- if self . use_spk_cache {
561+ if self . persist_spks {
546562 let derive_spk = {
547563 let secp = Secp256k1 :: verification_only ( ) ;
548564 let _desc = & descriptor;
@@ -927,7 +943,7 @@ impl<K: Clone + Ord + Debug> KeychainTxOutIndex<K> {
927943 * v = index. max ( * v) ;
928944 self . replenish_inner_index_did ( did, self . lookahead ) ;
929945 }
930- if self . use_spk_cache {
946+ if self . persist_spks {
931947 for ( did, spks) in changeset. spk_cache {
932948 self . spk_cache . entry ( did) . or_default ( ) . extend ( spks) ;
933949 }
@@ -982,14 +998,22 @@ impl<K: core::fmt::Debug> core::fmt::Display for InsertDescriptorError<K> {
982998#[ cfg( feature = "std" ) ]
983999impl < K : core:: fmt:: Debug > std:: error:: Error for InsertDescriptorError < K > { }
9841000
985- /// Represents updates to the derivation index of a [`KeychainTxOutIndex`].
986- /// It maps each keychain `K` to a descriptor and its last revealed index.
1001+ /// `ChangeSet` represents persistent updates to a [`KeychainTxOutIndex`].
1002+ ///
1003+ /// It tracks:
1004+ /// 1. `last_revealed`: the highest derivation index revealed per descriptor.
1005+ /// 2. `spks`: the cache of derived script pubkeys to persist across runs.
1006+ ///
1007+ /// You can apply a `ChangeSet` to a `KeychainTxOutIndex` via
1008+ /// [`KeychainTxOutIndex::apply_changeset`], or merge two change sets with [`ChangeSet::merge`].
9871009///
988- /// It can be applied to [`KeychainTxOutIndex`] with [`apply_changeset`].
1010+ /// # Monotonicity
9891011///
990- /// The `last_revealed` field is monotone in that [`merge`] will never decrease it.
991- /// `keychains_added` is *not* monotone, once it is set any attempt to change it is subject to the
992- /// same *one-to-one* keychain <-> descriptor mapping invariant as [`KeychainTxOutIndex`] itself.
1012+ /// - `last_revealed` is monotonic: merging retains the maximum index for each descriptor and never
1013+ /// decreases.
1014+ /// - `spks` accumulates entries: once a script pubkey is persisted, it remains available for
1015+ /// reload. If the same descriptor and index appear again with a new script pubkey, the latter
1016+ /// value overrides the former.
9931017///
9941018/// [`KeychainTxOutIndex`]: crate::keychain_txout::KeychainTxOutIndex
9951019/// [`apply_changeset`]: crate::keychain_txout::KeychainTxOutIndex::apply_changeset
@@ -998,10 +1022,11 @@ impl<K: core::fmt::Debug> std::error::Error for InsertDescriptorError<K> {}
9981022#[ cfg_attr( feature = "serde" , derive( serde:: Deserialize , serde:: Serialize ) ) ]
9991023#[ must_use]
10001024pub struct ChangeSet {
1001- /// Contains for each descriptor_id the last revealed index of derivation
1025+ /// Maps each `DescriptorId` to its last revealed derivation index.
10021026 pub last_revealed : BTreeMap < DescriptorId , u32 > ,
10031027
1004- /// Cache of previously derived script pubkeys.
1028+ /// Cache of derived script pubkeys to persist, keyed by descriptor ID and derivation index
1029+ /// (`u32`).
10051030 #[ cfg_attr( feature = "serde" , serde( default ) ) ]
10061031 pub spk_cache : BTreeMap < DescriptorId , BTreeMap < u32 , ScriptBuf > > ,
10071032}
0 commit comments