@@ -160,6 +160,20 @@ const DEFAULT_LOOKAHEAD: u32 = 25;
160160/// let new_spk_for_user = txout_index.reveal_next_spk(&MyKeychain::MyAppUser{ user_id: 42 });
161161/// ```
162162///
163+ /// # Re-assigning descriptors
164+ ///
165+ /// Under the hood, the [`KeychainTxOutIndex`] uses a [`SpkTxOutIndex`] that keeps track of spks,
166+ /// indexed by descriptors. Users then assign or unassign keychains to those descriptors. It's
167+ /// important to note that descriptors, once added, never get removed from the [`SpkTxOutIndex`];
168+ /// this is useful in case a user unassigns a keychain from a descriptor and after some time
169+ /// assigns it again.
170+ ///
171+ /// Additionally, although a keychain can only be assigned to one descriptor, different keychains
172+ /// can be assigned to the same descriptor. When a method returns spks/outpoints that is associated
173+ /// with a descriptor, it may be associated with multiple keychain variants. The keychain variant
174+ /// with the higher rank will be returned. Rank is determined by the [`Ord`] implementation of the
175+ /// keychain type. Earlier keychain variants have higher rank.
176+ ///
163177/// [`Ord`]: core::cmp::Ord
164178/// [`SpkTxOutIndex`]: crate::spk_txout_index::SpkTxOutIndex
165179/// [`Descriptor`]: crate::miniscript::Descriptor
@@ -172,17 +186,17 @@ const DEFAULT_LOOKAHEAD: u32 = 25;
172186/// [`new`]: KeychainTxOutIndex::new
173187/// [`unbounded_spk_iter`]: KeychainTxOutIndex::unbounded_spk_iter
174188/// [`all_unbounded_spk_iters`]: KeychainTxOutIndex::all_unbounded_spk_iters
175- // Under the hood, the KeychainTxOutIndex uses a SpkTxOutIndex that keeps track of spks, indexed by
176- // descriptors. Users then assign or unassign keychains to those descriptors. It's important to
177- // note that descriptors, once added, never get removed from the SpkTxOutIndex; this is useful in
178- // case a user unassigns a keychain from a descriptor and after some time assigns it again.
179189#[ derive( Clone , Debug ) ]
180190pub struct KeychainTxOutIndex < K > {
181191 inner : SpkTxOutIndex < ( DescriptorId , u32 ) > ,
182192 // keychain -> (descriptor, descriptor id) map
183193 keychains_to_descriptors : BTreeMap < K , ( DescriptorId , Descriptor < DescriptorPublicKey > ) > ,
184- // descriptor id -> keychain map
185- descriptor_ids_to_keychain : BTreeMap < DescriptorId , ( K , Descriptor < DescriptorPublicKey > ) > ,
194+ // descriptor id -> keychain set
195+ // Because different keychains can have the same descriptor, we rank keychains by `Ord` so that
196+ // that the first keychain variant (according to `Ord`) has the highest rank. When associated
197+ // data (such as spks, outpoints) are returned with a keychain, we return the highest-ranked
198+ // keychain with it.
199+ descriptor_ids_to_keychain_set : HashMap < DescriptorId , BTreeSet < K > > ,
186200 // descriptor_id -> descriptor map
187201 // This is a "monotone" map, meaning that its size keeps growing, i.e., we never delete
188202 // descriptors from it. This is useful for revealing spks for descriptors that don't have
@@ -208,11 +222,9 @@ impl<K: Clone + Ord + Debug> Indexer for KeychainTxOutIndex<K> {
208222 Some ( ( descriptor_id, index) ) => {
209223 // We want to reveal spks for descriptors that aren't tracked by any keychain, and
210224 // so we call reveal with descriptor_id
211- if let Some ( ( _, changeset) ) = self . reveal_to_target_with_id ( descriptor_id, index) {
212- changeset
213- } else {
214- super :: ChangeSet :: default ( )
215- }
225+ let ( _, changeset) = self . reveal_to_target_with_id ( descriptor_id, index)
226+ . expect ( "descriptors are added in a monotone manner, there cannot be a descriptor id with no corresponding descriptor" ) ;
227+ changeset
216228 }
217229 None => super :: ChangeSet :: default ( ) ,
218230 }
@@ -259,9 +271,9 @@ impl<K> KeychainTxOutIndex<K> {
259271 pub fn new ( lookahead : u32 ) -> Self {
260272 Self {
261273 inner : SpkTxOutIndex :: default ( ) ,
262- descriptor_ids_to_keychain : BTreeMap :: new ( ) ,
263- descriptor_ids_to_descriptors : BTreeMap :: new ( ) ,
264274 keychains_to_descriptors : BTreeMap :: new ( ) ,
275+ descriptor_ids_to_keychain_set : HashMap :: new ( ) ,
276+ descriptor_ids_to_descriptors : BTreeMap :: new ( ) ,
265277 last_revealed : BTreeMap :: new ( ) ,
266278 lookahead,
267279 }
@@ -270,6 +282,12 @@ impl<K> KeychainTxOutIndex<K> {
270282
271283/// Methods that are *re-exposed* from the internal [`SpkTxOutIndex`].
272284impl < K : Clone + Ord + Debug > KeychainTxOutIndex < K > {
285+ /// Get the highest-ranked keychain that is currently associated with the given `desc_id`.
286+ fn keychain_of_desc_id ( & self , desc_id : & DescriptorId ) -> Option < & K > {
287+ let keychains = self . descriptor_ids_to_keychain_set . get ( desc_id) ?;
288+ keychains. iter ( ) . next ( )
289+ }
290+
273291 /// Return a reference to the internal [`SpkTxOutIndex`].
274292 ///
275293 /// **WARNING:** The internal index will contain lookahead spks. Refer to
@@ -284,18 +302,16 @@ impl<K: Clone + Ord + Debug> KeychainTxOutIndex<K> {
284302 . outpoints ( )
285303 . iter ( )
286304 . filter_map ( |( ( desc_id, index) , op) | {
287- self . descriptor_ids_to_keychain
288- . get ( desc_id)
289- . map ( |( k, _) | ( ( k. clone ( ) , * index) , * op) )
305+ let keychain = self . keychain_of_desc_id ( desc_id) ?;
306+ Some ( ( ( keychain. clone ( ) , * index) , * op) )
290307 } )
291308 }
292309
293310 /// Iterate over known txouts that spend to tracked script pubkeys.
294311 pub fn txouts ( & self ) -> impl DoubleEndedIterator < Item = ( K , u32 , OutPoint , & TxOut ) > + ' _ {
295312 self . inner . txouts ( ) . filter_map ( |( ( desc_id, i) , op, txo) | {
296- self . descriptor_ids_to_keychain
297- . get ( desc_id)
298- . map ( |( k, _) | ( k. clone ( ) , * i, op, txo) )
313+ let keychain = self . keychain_of_desc_id ( desc_id) ?;
314+ Some ( ( keychain. clone ( ) , * i, op, txo) )
299315 } )
300316 }
301317
@@ -307,9 +323,8 @@ impl<K: Clone + Ord + Debug> KeychainTxOutIndex<K> {
307323 self . inner
308324 . txouts_in_tx ( txid)
309325 . filter_map ( |( ( desc_id, i) , op, txo) | {
310- self . descriptor_ids_to_keychain
311- . get ( desc_id)
312- . map ( |( k, _) | ( k. clone ( ) , * i, op, txo) )
326+ let keychain = self . keychain_of_desc_id ( desc_id) ?;
327+ Some ( ( keychain. clone ( ) , * i, op, txo) )
313328 } )
314329 }
315330
@@ -321,7 +336,7 @@ impl<K: Clone + Ord + Debug> KeychainTxOutIndex<K> {
321336 /// This calls [`SpkTxOutIndex::txout`] internally.
322337 pub fn txout ( & self , outpoint : OutPoint ) -> Option < ( K , u32 , & TxOut ) > {
323338 let ( ( descriptor_id, index) , txo) = self . inner . txout ( outpoint) ?;
324- let ( keychain, _ ) = self . descriptor_ids_to_keychain . get ( descriptor_id) ?;
339+ let keychain = self . keychain_of_desc_id ( descriptor_id) ?;
325340 Some ( ( keychain. clone ( ) , * index, txo) )
326341 }
327342
@@ -338,9 +353,8 @@ impl<K: Clone + Ord + Debug> KeychainTxOutIndex<K> {
338353 /// This calls [`SpkTxOutIndex::index_of_spk`] internally.
339354 pub fn index_of_spk ( & self , script : & Script ) -> Option < ( K , u32 ) > {
340355 let ( desc_id, last_index) = self . inner . index_of_spk ( script) ?;
341- self . descriptor_ids_to_keychain
342- . get ( desc_id)
343- . map ( |( k, _) | ( k. clone ( ) , * last_index) )
356+ let keychain = self . keychain_of_desc_id ( desc_id) ?;
357+ Some ( ( keychain. clone ( ) , * last_index) )
344358 }
345359
346360 /// Returns whether the spk under the `keychain`'s `index` has been used.
@@ -448,45 +462,42 @@ impl<K: Clone + Ord + Debug> KeychainTxOutIndex<K> {
448462 keychain : K ,
449463 descriptor : Descriptor < DescriptorPublicKey > ,
450464 ) -> super :: ChangeSet < K > {
451- let descriptor_id = descriptor. descriptor_id ( ) ;
452- // First, we fill the keychain -> (desc_id, descriptor) map
453- let old_descriptor_opt = self
465+ let mut changeset = super :: ChangeSet :: < K > :: default ( ) ;
466+ let desc_id = descriptor. descriptor_id ( ) ;
467+
468+ let old_desc = self
454469 . keychains_to_descriptors
455- . insert ( keychain. clone ( ) , ( descriptor_id, descriptor. clone ( ) ) ) ;
456-
457- // Then, we fill the descriptor_id -> (keychain, descriptor) map
458- let old_keychain_opt = self
459- . descriptor_ids_to_keychain
460- . insert ( descriptor_id, ( keychain. clone ( ) , descriptor. clone ( ) ) ) ;
461-
462- // If `keychain` already had a `descriptor` associated, different from the `descriptor`
463- // passed in, we remove it from the descriptor -> keychain map
464- if let Some ( ( old_desc_id, _) ) = old_descriptor_opt {
465- if old_desc_id != descriptor_id {
466- self . descriptor_ids_to_keychain . remove ( & old_desc_id) ;
470+ . insert ( keychain. clone ( ) , ( desc_id, descriptor. clone ( ) ) ) ;
471+
472+ if let Some ( ( old_desc_id, _) ) = old_desc {
473+ // nothing needs to be done if caller reinsterted the same descriptor under the same
474+ // keychain
475+ if old_desc_id == desc_id {
476+ return changeset;
467477 }
478+ // we should remove old descriptor that is associated with this keychain as the index
479+ // is designed to track one descriptor per keychain (however different keychains can
480+ // share the same descriptor)
481+ let _is_keychain_removed = self
482+ . descriptor_ids_to_keychain_set
483+ . get_mut ( & old_desc_id)
484+ . expect ( "we must have already inserted this descriptor" )
485+ . remove ( & keychain) ;
486+ debug_assert ! ( _is_keychain_removed) ;
468487 }
469488
470- // Lastly, we fill the desc_id -> desc map
489+ self . descriptor_ids_to_keychain_set
490+ . entry ( desc_id)
491+ . or_default ( )
492+ . insert ( keychain. clone ( ) ) ;
471493 self . descriptor_ids_to_descriptors
472- . insert ( descriptor_id, descriptor. clone ( ) ) ;
473-
494+ . insert ( desc_id, descriptor. clone ( ) ) ;
474495 self . replenish_lookahead ( & keychain, self . lookahead ) ;
475496
476- // If both the keychain and descriptor were already inserted and associated, the
477- // keychains_added changeset must be empty
478- let keychains_added = if old_keychain_opt. map ( |( k, _) | k) == Some ( keychain. clone ( ) )
479- && old_descriptor_opt. map ( |( _, d) | d) == Some ( descriptor. clone ( ) )
480- {
481- [ ] . into ( )
482- } else {
483- [ ( keychain, descriptor) ] . into ( )
484- } ;
485-
486- super :: ChangeSet {
487- keychains_added,
488- last_revealed : [ ] . into ( ) ,
489- }
497+ changeset
498+ . keychains_added
499+ . insert ( keychain. clone ( ) , descriptor. clone ( ) ) ;
500+ changeset
490501 }
491502
492503 /// Gets the descriptor associated with the keychain. Returns `None` if the keychain doesn't
@@ -584,11 +595,8 @@ impl<K: Clone + Ord + Debug> KeychainTxOutIndex<K> {
584595 . range ( ( start, end) )
585596 . map ( |( ( descriptor_id, i) , spk) | {
586597 (
587- & self
588- . descriptor_ids_to_keychain
589- . get ( descriptor_id)
590- . expect ( "Must be here" )
591- . 0 ,
598+ self . keychain_of_desc_id ( descriptor_id)
599+ . expect ( "must have keychain" ) ,
592600 * i,
593601 spk. as_script ( ) ,
594602 )
@@ -673,10 +681,9 @@ impl<K: Clone + Ord + Debug> KeychainTxOutIndex<K> {
673681 pub fn last_revealed_indices ( & self ) -> BTreeMap < K , u32 > {
674682 self . last_revealed
675683 . iter ( )
676- . filter_map ( |( descriptor_id, index) | {
677- self . descriptor_ids_to_keychain
678- . get ( descriptor_id)
679- . map ( |( k, _) | ( k. clone ( ) , * index) )
684+ . filter_map ( |( desc_id, index) | {
685+ let keychain = self . keychain_of_desc_id ( desc_id) ?;
686+ Some ( ( keychain. clone ( ) , * index) )
680687 } )
681688 . collect ( )
682689 }
@@ -870,16 +877,11 @@ impl<K: Clone + Ord + Debug> KeychainTxOutIndex<K> {
870877 let bounds = self . map_to_inner_bounds ( range) ;
871878 self . inner
872879 . outputs_in_range ( bounds)
873- . map ( move |( ( descriptor_id, i) , op) | {
874- (
875- & self
876- . descriptor_ids_to_keychain
877- . get ( descriptor_id)
878- . expect ( "must be here" )
879- . 0 ,
880- * i,
881- op,
882- )
880+ . map ( move |( ( desc_id, i) , op) | {
881+ let keychain = self
882+ . keychain_of_desc_id ( desc_id)
883+ . expect ( "keychain must exist" ) ;
884+ ( keychain, * i, op)
883885 } )
884886 }
885887
@@ -941,10 +943,9 @@ impl<K: Clone + Ord + Debug> KeychainTxOutIndex<K> {
941943 }
942944 let last_revealed = last_revealed
943945 . into_iter ( )
944- . filter_map ( |( descriptor_id, index) | {
945- self . descriptor_ids_to_keychain
946- . get ( & descriptor_id)
947- . map ( |( k, _) | ( k. clone ( ) , index) )
946+ . filter_map ( |( desc_id, index) | {
947+ let keychain = self . keychain_of_desc_id ( & desc_id) ?;
948+ Some ( ( keychain. clone ( ) , index) )
948949 } )
949950 . collect ( ) ;
950951 let _ = self . reveal_to_target_multi ( & last_revealed) ;
0 commit comments