]> Untitled Git - bdk/commitdiff
feat(chain): `KeychainTxOutIndex`: Make spk cache optional
author志宇 <hello@evanlinjin.me>
Fri, 23 May 2025 05:44:35 +0000 (15:44 +1000)
committer志宇 <hello@evanlinjin.me>
Fri, 23 May 2025 08:36:23 +0000 (18:36 +1000)
Also added staging changes to `ChangeSet::spk_cache`. This way, we can
avoid returning `ChangeSet`s for `apply_changeset` and
`insert_descriptor`.

* `KeychainTxOutIndex::new` now takes in an additional `use_spk_cache`
  parameter.
* Fixed `reveal_to_target` method to actually return `None` if the
  keychain does not exist.

crates/bitcoind_rpc/examples/filter_iter.rs
crates/chain/benches/canonicalization.rs
crates/chain/src/indexer/keychain_txout.rs
crates/chain/src/spk_iter.rs
crates/chain/tests/test_indexed_tx_graph.rs
crates/chain/tests/test_keychain_txout_index.rs
examples/example_cli/src/lib.rs

index b80542bc8e3451cb62a1553ebdb5e59ec61e159b..21300e700e13b9de6768a1317581b1548e819cad 100644 (file)
@@ -33,9 +33,8 @@ fn main() -> anyhow::Result<()> {
     let (mut chain, _) = LocalChain::from_genesis_hash(genesis_block(NETWORK).block_hash());
     let mut graph = IndexedTxGraph::<ConfirmationBlockTime, KeychainTxOutIndex<&str>>::new({
         let mut index = KeychainTxOutIndex::default();
-        // TODO: Properly deal with these changesets.
-        let _ = index.insert_descriptor("external", descriptor.clone())?;
-        let _ = index.insert_descriptor("internal", change_descriptor.clone())?;
+        index.insert_descriptor("external", descriptor.clone())?;
+        index.insert_descriptor("internal", change_descriptor.clone())?;
         index
     });
 
index 4cb26c6b8096aa5895fe3a2f012130aab44e90d9..df9c08b01e2e3518707da522e00df891d225a6d9 100644 (file)
@@ -82,8 +82,8 @@ fn setup<F: Fn(&mut KeychainTxGraph, &LocalChain)>(f: F) -> (KeychainTxGraph, Lo
 
     let (desc, _) =
         <Descriptor<DescriptorPublicKey>>::parse_descriptor(&Secp256k1::new(), DESC).unwrap();
-    let mut index = KeychainTxOutIndex::new(10);
-    let _ = index.insert_descriptor((), desc).unwrap();
+    let mut index = KeychainTxOutIndex::new(10, true);
+    index.insert_descriptor((), desc).unwrap();
     let mut tx_graph = KeychainTxGraph::new(index);
 
     f(&mut tx_graph, &chain);
index 55486e9e38729de78c5d507785931401aabd0a8d..6332faaef48417346797f7bae5a1bfb5925c5020 100644 (file)
@@ -132,12 +132,14 @@ pub struct KeychainTxOutIndex<K> {
     last_revealed: HashMap<DescriptorId, u32>,
     lookahead: u32,
 
+    use_spk_cache: bool,
     spk_cache: BTreeMap<DescriptorId, HashMap<u32, ScriptBuf>>,
+    spk_cache_stage: BTreeMap<DescriptorId, Vec<(u32, ScriptBuf)>>,
 }
 
 impl<K> Default for KeychainTxOutIndex<K> {
     fn default() -> Self {
-        Self::new(DEFAULT_LOOKAHEAD)
+        Self::new(DEFAULT_LOOKAHEAD, false)
     }
 }
 
@@ -152,31 +154,18 @@ impl<K: Clone + Ord + Debug> Indexer for KeychainTxOutIndex<K> {
 
     fn index_txout(&mut self, outpoint: OutPoint, txout: &TxOut) -> Self::ChangeSet {
         let mut changeset = ChangeSet::default();
-        if let Some((keychain, index)) = self.inner.scan_txout(outpoint, txout).cloned() {
-            let did = self
-                .keychain_to_descriptor_id
-                .get(&keychain)
-                .expect("invariant");
-            if self.last_revealed.get(did) < Some(&index) {
-                self.last_revealed.insert(*did, index);
-                changeset.last_revealed.insert(*did, index);
-                self.replenish_inner_index(
-                    *did,
-                    &keychain,
-                    self.lookahead,
-                    changeset.spk_cache.entry(*did).or_default(),
-                );
-            }
-        }
+        self._index_txout(&mut changeset, outpoint, txout);
+        self._empty_stage_into_changeset(&mut changeset);
         changeset
     }
 
     fn index_tx(&mut self, tx: &bitcoin::Transaction) -> Self::ChangeSet {
         let mut changeset = ChangeSet::default();
         let txid = tx.compute_txid();
-        for (op, txout) in tx.output.iter().enumerate() {
-            changeset.merge(self.index_txout(OutPoint::new(txid, op as u32), txout));
+        for (vout, txout) in tx.output.iter().enumerate() {
+            self._index_txout(&mut changeset, OutPoint::new(txid, vout as u32), txout);
         }
+        self._empty_stage_into_changeset(&mut changeset);
         changeset
     }
 
@@ -206,7 +195,9 @@ impl<K: Clone + Ord + Debug> Indexer for KeychainTxOutIndex<K> {
 }
 
 impl<K> KeychainTxOutIndex<K> {
-    /// Construct a [`KeychainTxOutIndex`] with the given `lookahead`.
+    /// Construct a [`KeychainTxOutIndex`] with the given `lookahead` and `use_spk_cache` boolean.
+    ///
+    /// # Lookahead
     ///
     /// The `lookahead` is the number of script pubkeys to derive and cache from the internal
     /// descriptors over and above the last revealed script index. Without a lookahead the index
@@ -216,7 +207,11 @@ impl<K> KeychainTxOutIndex<K> {
     /// of the last revealed script pubkey actually is.
     ///
     /// Refer to [struct-level docs](KeychainTxOutIndex) for more about `lookahead`.
-    pub fn new(lookahead: u32) -> Self {
+    ///
+    /// # Script pubkey cache
+    ///
+    /// The `use_spk_cache` parameter enables the internal script pubkey cache.
+    pub fn new(lookahead: u32, use_spk_cache: bool) -> Self {
         Self {
             inner: SpkTxOutIndex::default(),
             keychain_to_descriptor_id: Default::default(),
@@ -224,7 +219,9 @@ impl<K> KeychainTxOutIndex<K> {
             descriptor_id_to_keychain: Default::default(),
             last_revealed: Default::default(),
             lookahead,
+            use_spk_cache,
             spk_cache: Default::default(),
+            spk_cache_stage: Default::default(),
         }
     }
 
@@ -237,12 +234,63 @@ impl<K> KeychainTxOutIndex<K> {
 /// Methods that are *re-exposed* from the internal [`SpkTxOutIndex`].
 impl<K: Clone + Ord + Debug> KeychainTxOutIndex<K> {
     /// Construct `KeychainTxOutIndex<K>` from the given `changeset`.
-    pub fn from_changeset(changeset: ChangeSet) -> Self {
-        let mut out = Self::default();
+    ///
+    /// Shorthand for called [`new`] and then [`apply_changeset`].
+    ///
+    /// [`new`]: Self::new
+    /// [`apply_changeset`]: Self::apply_changeset
+    pub fn from_changeset(lookahead: u32, use_spk_cache: bool, changeset: ChangeSet) -> Self {
+        let mut out = Self::new(lookahead, use_spk_cache);
         out.apply_changeset(changeset);
         out
     }
 
+    fn _index_txout(&mut self, changeset: &mut ChangeSet, outpoint: OutPoint, txout: &TxOut) {
+        if let Some((keychain, index)) = self.inner.scan_txout(outpoint, txout).cloned() {
+            let did = self
+                .keychain_to_descriptor_id
+                .get(&keychain)
+                .expect("invariant");
+            let index_updated = match self.last_revealed.entry(*did) {
+                hash_map::Entry::Occupied(mut e) if e.get() < &index => {
+                    e.insert(index);
+                    true
+                }
+                hash_map::Entry::Vacant(e) => {
+                    e.insert(index);
+                    true
+                }
+                _ => false,
+            };
+            if index_updated {
+                changeset.last_revealed.insert(*did, index);
+                self.replenish_inner_index(*did, &keychain, self.lookahead);
+            }
+        }
+    }
+
+    fn _empty_stage_into_changeset(&mut self, changeset: &mut ChangeSet) {
+        if !self.use_spk_cache {
+            return;
+        }
+        for (did, spks) in core::mem::take(&mut self.spk_cache_stage) {
+            debug_assert!(
+                {
+                    let desc = self.descriptors.get(&did).expect("invariant");
+                    spks.iter().all(|(i, spk)| {
+                        let exp_spk = desc
+                            .at_derivation_index(*i)
+                            .expect("must derive")
+                            .script_pubkey();
+                        &exp_spk == spk
+                    })
+                },
+                "all staged spks must be correct"
+            );
+            changeset.spk_cache.entry(did).or_default().extend(spks);
+        }
+    }
+
     /// Get the set of indexed outpoints, corresponding to tracked keychains.
     pub fn outpoints(&self) -> &BTreeSet<KeychainIndexed<K, OutPoint>> {
         self.inner.outpoints()
@@ -393,9 +441,7 @@ impl<K: Clone + Ord + Debug> KeychainTxOutIndex<K> {
         &mut self,
         keychain: K,
         descriptor: Descriptor<DescriptorPublicKey>,
-    ) -> Result<(bool, ChangeSet), InsertDescriptorError<K>> {
-        let mut changeset = ChangeSet::default();
-
+    ) -> Result<bool, InsertDescriptorError<K>> {
         let did = descriptor.descriptor_id();
         if !self.keychain_to_descriptor_id.contains_key(&keychain)
             && !self.descriptor_id_to_keychain.contains_key(&did)
@@ -403,13 +449,8 @@ impl<K: Clone + Ord + Debug> KeychainTxOutIndex<K> {
             self.descriptors.insert(did, descriptor.clone());
             self.keychain_to_descriptor_id.insert(keychain.clone(), did);
             self.descriptor_id_to_keychain.insert(did, keychain.clone());
-            self.replenish_inner_index(
-                did,
-                &keychain,
-                self.lookahead,
-                changeset.spk_cache.entry(did).or_default(),
-            );
-            return Ok((true, changeset));
+            self.replenish_inner_index(did, &keychain, self.lookahead);
+            return Ok(true);
         }
 
         if let Some(existing_desc_id) = self.keychain_to_descriptor_id.get(&keychain) {
@@ -433,7 +474,7 @@ impl<K: Clone + Ord + Debug> KeychainTxOutIndex<K> {
             }
         }
 
-        Ok((false, changeset))
+        Ok(false)
     }
 
     /// Gets the descriptor associated with the keychain. Returns `None` if the keychain doesn't
@@ -463,50 +504,27 @@ impl<K: Clone + Ord + Debug> KeychainTxOutIndex<K> {
                 .filter(|&index| index > 0);
 
             if let Some(temp_lookahead) = temp_lookahead {
-                let did = self
-                    .keychain_to_descriptor_id
-                    .get(&keychain)
-                    .expect("invariant");
-                self.replenish_inner_index_keychain(
-                    keychain,
-                    temp_lookahead,
-                    changeset.spk_cache.entry(*did).or_default(),
-                );
+                self.replenish_inner_index_keychain(keychain, temp_lookahead);
             }
         }
+        self._empty_stage_into_changeset(&mut changeset);
         changeset
     }
 
-    fn replenish_inner_index_did(
-        &mut self,
-        did: DescriptorId,
-        lookahead: u32,
-        derived_spks: &mut impl Extend<Indexed<ScriptBuf>>,
-    ) {
+    fn replenish_inner_index_did(&mut self, did: DescriptorId, lookahead: u32) {
         if let Some(keychain) = self.descriptor_id_to_keychain.get(&did).cloned() {
-            self.replenish_inner_index(did, &keychain, lookahead, derived_spks);
+            self.replenish_inner_index(did, &keychain, lookahead);
         }
     }
 
-    fn replenish_inner_index_keychain(
-        &mut self,
-        keychain: K,
-        lookahead: u32,
-        derived_spks: &mut impl Extend<Indexed<ScriptBuf>>,
-    ) {
+    fn replenish_inner_index_keychain(&mut self, keychain: K, lookahead: u32) {
         if let Some(did) = self.keychain_to_descriptor_id.get(&keychain) {
-            self.replenish_inner_index(*did, &keychain, lookahead, derived_spks);
+            self.replenish_inner_index(*did, &keychain, lookahead);
         }
     }
 
     /// Syncs the state of the inner spk index after changes to a keychain
-    fn replenish_inner_index(
-        &mut self,
-        did: DescriptorId,
-        keychain: &K,
-        lookahead: u32,
-        derived_spks: &mut impl Extend<Indexed<ScriptBuf>>,
-    ) {
+    fn replenish_inner_index(&mut self, did: DescriptorId, keychain: &K, lookahead: u32) {
         let descriptor = self.descriptors.get(&did).expect("invariant");
 
         let mut next_index = self
@@ -524,43 +542,52 @@ impl<K: Clone + Ord + Debug> KeychainTxOutIndex<K> {
             1
         };
 
-        let derive_spk = {
-            let secp = Secp256k1::verification_only();
-            let _desc = &descriptor;
-            move |spk_i: u32| -> ScriptBuf {
-                _desc
-                    .derived_descriptor(&secp, spk_i)
-                    .expect("The descriptor cannot have hardened derivation")
-                    .script_pubkey()
-            }
-        };
-
-        let cached_spk_iter = core::iter::from_fn({
-            let spk_cache = self.spk_cache.entry(did).or_default();
-            let _i = &mut next_index;
-            move || -> Option<Indexed<ScriptBuf>> {
-                if *_i >= stop_index {
-                    return None;
+        if self.use_spk_cache {
+            let derive_spk = {
+                let secp = Secp256k1::verification_only();
+                let _desc = &descriptor;
+                move |spk_i: u32| -> ScriptBuf {
+                    _desc
+                        .derived_descriptor(&secp, spk_i)
+                        .expect("The descriptor cannot have hardened derivation")
+                        .script_pubkey()
                 }
-                let spk_i = *_i;
-                *_i = spk_i.saturating_add(1);
+            };
+            let cached_spk_iter = core::iter::from_fn({
+                let spk_cache = self.spk_cache.entry(did).or_default();
+                let spk_stage = self.spk_cache_stage.entry(did).or_default();
+                let _i = &mut next_index;
+                move || -> Option<Indexed<ScriptBuf>> {
+                    if *_i >= stop_index {
+                        return None;
+                    }
+                    let spk_i = *_i;
+                    *_i = spk_i.saturating_add(1);
 
-                if let Some(spk) = spk_cache.get(&spk_i) {
-                    debug_assert_eq!(spk, &derive_spk(spk_i), "cached spk must equal derived");
-                    return Some((spk_i, spk.clone()));
+                    if let Some(spk) = spk_cache.get(&spk_i) {
+                        debug_assert_eq!(spk, &derive_spk(spk_i), "cached spk must equal derived");
+                        return Some((spk_i, spk.clone()));
+                    }
+                    let spk = derive_spk(spk_i);
+                    spk_stage.push((spk_i, spk.clone()));
+                    spk_cache.insert(spk_i, spk.clone());
+                    Some((spk_i, spk))
                 }
-                let spk = derive_spk(spk_i);
-                derived_spks.extend(core::iter::once((spk_i, spk.clone())));
-                spk_cache.insert(spk_i, spk.clone());
-                Some((spk_i, spk))
+            });
+            for (new_index, new_spk) in cached_spk_iter {
+                let _inserted = self
+                    .inner
+                    .insert_spk((keychain.clone(), new_index), new_spk);
+                debug_assert!(_inserted, "replenish lookahead: must not have existing spk: keychain={:?}, lookahead={}, next_index={}", keychain, lookahead, next_index);
+            }
+        } else {
+            let spk_iter = SpkIterator::new_with_range(descriptor, next_index..stop_index);
+            for (new_index, new_spk) in spk_iter {
+                let _inserted = self
+                    .inner
+                    .insert_spk((keychain.clone(), new_index), new_spk);
+                debug_assert!(_inserted, "replenish lookahead: must not have existing spk: keychain={:?}, lookahead={}, next_index={}", keychain, lookahead, next_index);
             }
-        });
-
-        for (new_index, new_spk) in cached_spk_iter {
-            let _inserted = self
-                .inner
-                .insert_spk((keychain.clone(), new_index), new_spk);
-            debug_assert!(_inserted, "replenish lookahead: must not have existing spk: keychain={:?}, lookahead={}, next_index={}", keychain, lookahead, next_index);
         }
     }
 
@@ -729,11 +756,10 @@ impl<K: Clone + Ord + Debug> KeychainTxOutIndex<K> {
         let mut changeset = ChangeSet::default();
 
         for (keychain, &index) in keychains {
-            if let Some((_, new_changeset)) = self.reveal_to_target(keychain.clone(), index) {
-                changeset.merge(new_changeset);
-            }
+            self._reveal_to_target(&mut changeset, keychain.clone(), index);
         }
 
+        self._empty_stage_into_changeset(&mut changeset);
         changeset
     }
 
@@ -756,21 +782,28 @@ impl<K: Clone + Ord + Debug> KeychainTxOutIndex<K> {
         target_index: u32,
     ) -> Option<(Vec<Indexed<ScriptBuf>>, ChangeSet)> {
         let mut changeset = ChangeSet::default();
+        let revealed_spks = self._reveal_to_target(&mut changeset, keychain, target_index)?;
+        self._empty_stage_into_changeset(&mut changeset);
+        Some((revealed_spks, changeset))
+    }
+    fn _reveal_to_target(
+        &mut self,
+        changeset: &mut ChangeSet,
+        keychain: K,
+        target_index: u32,
+    ) -> Option<Vec<Indexed<ScriptBuf>>> {
         let mut spks: Vec<Indexed<ScriptBuf>> = vec![];
-        while let Some((i, new)) = self.next_index(keychain.clone()) {
+        loop {
+            let (i, new) = self.next_index(keychain.clone())?;
             if !new || i > target_index {
                 break;
             }
-            match self.reveal_next_spk(keychain.clone()) {
-                Some(((i, spk), change)) => {
-                    spks.push((i, spk));
-                    changeset.merge(change);
-                }
+            match self._reveal_next_spk(changeset, keychain.clone()) {
+                Some(indexed_spk) => spks.push(indexed_spk),
                 None => break,
             }
         }
-
-        Some((spks, changeset))
+        Some(spks)
     }
 
     /// Attempts to reveal the next script pubkey for `keychain`.
@@ -786,25 +819,28 @@ impl<K: Clone + Ord + Debug> KeychainTxOutIndex<K> {
     ///  2. The descriptor has already revealed scripts up to the numeric bound.
     ///  3. There is no descriptor associated with the given keychain.
     pub fn reveal_next_spk(&mut self, keychain: K) -> Option<(Indexed<ScriptBuf>, ChangeSet)> {
-        let (next_index, new) = self.next_index(keychain.clone())?;
         let mut changeset = ChangeSet::default();
-
+        let indexed_spk = self._reveal_next_spk(&mut changeset, keychain)?;
+        self._empty_stage_into_changeset(&mut changeset);
+        Some((indexed_spk, changeset))
+    }
+    fn _reveal_next_spk(
+        &mut self,
+        changeset: &mut ChangeSet,
+        keychain: K,
+    ) -> Option<Indexed<ScriptBuf>> {
+        let (next_index, new) = self.next_index(keychain.clone())?;
         if new {
             let did = self.keychain_to_descriptor_id.get(&keychain)?;
             self.last_revealed.insert(*did, next_index);
             changeset.last_revealed.insert(*did, next_index);
-            self.replenish_inner_index(
-                *did,
-                &keychain,
-                self.lookahead,
-                changeset.spk_cache.entry(*did).or_default(),
-            );
+            self.replenish_inner_index(*did, &keychain, self.lookahead);
         }
         let script = self
             .inner
             .spk_at_index(&(keychain.clone(), next_index))
             .expect("we just inserted it");
-        Some(((next_index, script), changeset))
+        Some((next_index, script))
     }
 
     /// Gets the next unused script pubkey in the keychain. I.e., the script pubkey with the lowest
@@ -821,12 +857,14 @@ impl<K: Clone + Ord + Debug> KeychainTxOutIndex<K> {
     ///
     /// [`reveal_next_spk`]: Self::reveal_next_spk
     pub fn next_unused_spk(&mut self, keychain: K) -> Option<(Indexed<ScriptBuf>, ChangeSet)> {
+        let mut changeset = ChangeSet::default();
         let next_unused = self
             .unused_keychain_spks(keychain.clone())
             .next()
-            .map(|(i, spk)| ((i, spk.to_owned()), ChangeSet::default()));
-
-        next_unused.or_else(|| self.reveal_next_spk(keychain))
+            .map(|(i, spk)| (i, spk.to_owned()));
+        let spk = next_unused.or_else(|| self._reveal_next_spk(&mut changeset, keychain))?;
+        self._empty_stage_into_changeset(&mut changeset);
+        Some((spk, changeset))
     }
 
     /// Iterate over all [`OutPoint`]s that have `TxOut`s with script pubkeys derived from
@@ -887,10 +925,12 @@ impl<K: Clone + Ord + Debug> KeychainTxOutIndex<K> {
         for (did, index) in changeset.last_revealed {
             let v = self.last_revealed.entry(did).or_default();
             *v = index.max(*v);
-            self.replenish_inner_index_did(did, self.lookahead, &mut Vec::new());
+            self.replenish_inner_index_did(did, self.lookahead);
         }
-        for (did, spks) in changeset.spk_cache {
-            self.spk_cache.entry(did).or_default().extend(spks);
+        if self.use_spk_cache {
+            for (did, spks) in changeset.spk_cache {
+                self.spk_cache.entry(did).or_default().extend(spks);
+            }
         }
     }
 }
index 33d78c33115f3735e54c69e6a7c8b17429acbf58..64f38c6dc0c49631ff18b8640278605f5218f69c 100644 (file)
@@ -153,16 +153,16 @@ mod test {
         Descriptor<DescriptorPublicKey>,
         Descriptor<DescriptorPublicKey>,
     ) {
-        let mut txout_index = KeychainTxOutIndex::<TestKeychain>::new(0);
+        let mut txout_index = KeychainTxOutIndex::<TestKeychain>::new(0, true);
 
         let secp = Secp256k1::signing_only();
         let (external_descriptor,_) = Descriptor::<DescriptorPublicKey>::parse_descriptor(&secp, "tr([73c5da0a/86'/0'/0']xprv9xgqHN7yz9MwCkxsBPN5qetuNdQSUttZNKw1dcYTV4mkaAFiBVGQziHs3NRSWMkCzvgjEe3n9xV8oYywvM8at9yRqyaZVz6TYYhX98VjsUk/0/*)").unwrap();
         let (internal_descriptor,_) = Descriptor::<DescriptorPublicKey>::parse_descriptor(&secp, "tr([73c5da0a/86'/0'/0']xprv9xgqHN7yz9MwCkxsBPN5qetuNdQSUttZNKw1dcYTV4mkaAFiBVGQziHs3NRSWMkCzvgjEe3n9xV8oYywvM8at9yRqyaZVz6TYYhX98VjsUk/1/*)").unwrap();
 
-        let _ = txout_index
+        txout_index
             .insert_descriptor(TestKeychain::External, external_descriptor.clone())
             .unwrap();
-        let _ = txout_index
+        txout_index
             .insert_descriptor(TestKeychain::Internal, internal_descriptor.clone())
             .unwrap();
 
index abc2c8237770c52785c77bdd3e03f08106615f1d..5f23af5598e35299cfd0f475cb693b196960a217 100644 (file)
@@ -36,24 +36,13 @@ fn insert_relevant_txs() {
     let lookahead = 10;
 
     let mut graph = IndexedTxGraph::<ConfirmationBlockTime, KeychainTxOutIndex<()>>::new(
-        KeychainTxOutIndex::new(lookahead),
+        KeychainTxOutIndex::new(lookahead, true),
     );
-    let (is_inserted, changeset) = graph
+    let is_inserted = graph
         .index
         .insert_descriptor((), descriptor.clone())
         .unwrap();
     assert!(is_inserted);
-    assert_eq!(
-        changeset,
-        keychain_txout::ChangeSet {
-            spk_cache: [(
-                descriptor.descriptor_id(),
-                SpkIterator::new_with_range(&descriptor, 0..lookahead).collect()
-            )]
-            .into(),
-            ..Default::default()
-        }
-    );
 
     let tx_a = Transaction {
         output: vec![
@@ -98,7 +87,9 @@ fn insert_relevant_txs() {
                 let index_after_spk_1 = 9 /* index of spk_1 */ + 1;
                 SpkIterator::new_with_range(
                     &descriptor,
-                    index_after_spk_1..index_after_spk_1 + lookahead,
+                    // This will also persist the staged spk cache inclusions from prev call to
+                    // `.insert_descriptor`.
+                    0..index_after_spk_1 + lookahead,
                 )
                 .collect()
             })]
@@ -170,23 +161,17 @@ fn test_list_owned_txouts() {
         Descriptor::parse_descriptor(&Secp256k1::signing_only(), DESCRIPTORS[3]).unwrap();
 
     let mut graph = IndexedTxGraph::<ConfirmationBlockTime, KeychainTxOutIndex<String>>::new(
-        KeychainTxOutIndex::new(10),
+        KeychainTxOutIndex::new(10, true),
     );
 
-    assert!(
-        graph
-            .index
-            .insert_descriptor("keychain_1".into(), desc_1)
-            .unwrap()
-            .0
-    );
-    assert!(
-        graph
-            .index
-            .insert_descriptor("keychain_2".into(), desc_2)
-            .unwrap()
-            .0
-    );
+    assert!(graph
+        .index
+        .insert_descriptor("keychain_1".into(), desc_1)
+        .unwrap());
+    assert!(graph
+        .index
+        .insert_descriptor("keychain_2".into(), desc_2)
+        .unwrap());
 
     // Get trusted and untrusted addresses
 
index 6619d5c22bed47daf60cd5ccc11141d03895c35f..0cce8476dc55fa8e9d7ea6c11e0c95dc940b73f2 100644 (file)
@@ -29,8 +29,9 @@ fn init_txout_index(
     external_descriptor: Descriptor<DescriptorPublicKey>,
     internal_descriptor: Descriptor<DescriptorPublicKey>,
     lookahead: u32,
+    use_spk_cache: bool,
 ) -> KeychainTxOutIndex<TestKeychain> {
-    let mut txout_index = KeychainTxOutIndex::<TestKeychain>::new(lookahead);
+    let mut txout_index = KeychainTxOutIndex::<TestKeychain>::new(lookahead, use_spk_cache);
 
     let _ = txout_index
         .insert_descriptor(TestKeychain::External, external_descriptor)
@@ -108,6 +109,7 @@ fn test_set_all_derivation_indices() {
         external_descriptor.clone(),
         internal_descriptor.clone(),
         lookahead,
+        true,
     );
     let derive_to: BTreeMap<_, _> =
         [(TestKeychain::External, 12), (TestKeychain::Internal, 24)].into();
@@ -152,6 +154,7 @@ fn test_lookahead() {
         external_descriptor.clone(),
         internal_descriptor.clone(),
         lookahead,
+        true,
     );
 
     // given:
@@ -333,8 +336,12 @@ fn test_lookahead() {
 fn test_scan_with_lookahead() {
     let external_descriptor = parse_descriptor(DESCRIPTORS[0]);
     let internal_descriptor = parse_descriptor(DESCRIPTORS[1]);
-    let mut txout_index =
-        init_txout_index(external_descriptor.clone(), internal_descriptor.clone(), 10);
+    let mut txout_index = init_txout_index(
+        external_descriptor.clone(),
+        internal_descriptor.clone(),
+        10,
+        true,
+    );
 
     let spks: BTreeMap<u32, ScriptBuf> = [0, 10, 20, 30]
         .into_iter()
@@ -390,7 +397,7 @@ fn test_scan_with_lookahead() {
 fn test_wildcard_derivations() {
     let external_descriptor = parse_descriptor(DESCRIPTORS[0]);
     let internal_descriptor = parse_descriptor(DESCRIPTORS[1]);
-    let mut txout_index = init_txout_index(external_descriptor.clone(), internal_descriptor.clone(), 0);
+    let mut txout_index = init_txout_index(external_descriptor.clone(), internal_descriptor.clone(), 0, true);
     let external_spk_0 = external_descriptor.at_derivation_index(0).unwrap().script_pubkey();
     let external_spk_16 = external_descriptor.at_derivation_index(16).unwrap().script_pubkey();
     let external_spk_26 = external_descriptor.at_derivation_index(26).unwrap().script_pubkey();
@@ -448,7 +455,7 @@ fn test_wildcard_derivations() {
 
 #[test]
 fn test_non_wildcard_derivations() {
-    let mut txout_index = KeychainTxOutIndex::<TestKeychain>::new(0);
+    let mut txout_index = KeychainTxOutIndex::<TestKeychain>::new(0, true);
 
     let secp = bitcoin::secp256k1::Secp256k1::signing_only();
     let (no_wildcard_descriptor, _) =
@@ -574,6 +581,7 @@ fn lookahead_to_target() {
             external_descriptor.clone(),
             internal_descriptor.clone(),
             t.lookahead,
+            true,
         );
 
         if let Some(last_revealed) = t.external_last_revealed {
@@ -632,7 +640,7 @@ fn applying_changesets_one_by_one_vs_aggregate_must_have_same_result() {
         },
     ];
 
-    let mut indexer_a = KeychainTxOutIndex::<TestKeychain>::new(0);
+    let mut indexer_a = KeychainTxOutIndex::<TestKeychain>::new(0, true);
     let _ = indexer_a
         .insert_descriptor(TestKeychain::External, desc.clone())
         .expect("must insert keychain");
@@ -640,7 +648,7 @@ fn applying_changesets_one_by_one_vs_aggregate_must_have_same_result() {
         indexer_a.apply_changeset(changeset.clone());
     }
 
-    let mut indexer_b = KeychainTxOutIndex::<TestKeychain>::new(0);
+    let mut indexer_b = KeychainTxOutIndex::<TestKeychain>::new(0, true);
     let _ = indexer_b
         .insert_descriptor(TestKeychain::External, desc.clone())
         .expect("must insert keychain");
@@ -675,7 +683,7 @@ fn applying_changesets_one_by_one_vs_aggregate_must_have_same_result() {
 #[test]
 fn assigning_same_descriptor_to_multiple_keychains_should_error() {
     let desc = parse_descriptor(DESCRIPTORS[0]);
-    let mut indexer = KeychainTxOutIndex::<TestKeychain>::new(0);
+    let mut indexer = KeychainTxOutIndex::<TestKeychain>::new(0, true);
     let _ = indexer
         .insert_descriptor(TestKeychain::Internal, desc.clone())
         .unwrap();
@@ -688,7 +696,7 @@ fn assigning_same_descriptor_to_multiple_keychains_should_error() {
 fn reassigning_keychain_to_a_new_descriptor_should_error() {
     let desc1 = parse_descriptor(DESCRIPTORS[0]);
     let desc2 = parse_descriptor(DESCRIPTORS[1]);
-    let mut indexer = KeychainTxOutIndex::<TestKeychain>::new(0);
+    let mut indexer = KeychainTxOutIndex::<TestKeychain>::new(0, true);
     let _ = indexer.insert_descriptor(TestKeychain::Internal, desc1);
     assert!(indexer
         .insert_descriptor(TestKeychain::Internal, desc2)
@@ -697,7 +705,7 @@ fn reassigning_keychain_to_a_new_descriptor_should_error() {
 
 #[test]
 fn when_querying_over_a_range_of_keychains_the_utxos_should_show_up() {
-    let mut indexer = KeychainTxOutIndex::<usize>::new(0);
+    let mut indexer = KeychainTxOutIndex::<usize>::new(0, true);
     let mut tx = new_tx(0);
 
     for (i, descriptor) in DESCRIPTORS.iter().enumerate() {
index 1fb1abc36583deffa3b4f4a70adeea4302662bf9..d432d12b66aa5f9a3a8b20fc8afb5f31d05eb3e9 100644 (file)
@@ -836,13 +836,10 @@ pub fn init_or_load<CS: clap::Subcommand, S: clap::Args>(
                 // insert descriptors and apply loaded changeset
                 let mut index = KeychainTxOutIndex::default();
                 if let Some(desc) = changeset.descriptor {
-                    // TODO: We should apply changeset to the graph before inserting descriptors.
-                    // TODO: Then persist the new _changesets returned to db.
-                    let (_, _changeset) = index.insert_descriptor(Keychain::External, desc)?;
+                    index.insert_descriptor(Keychain::External, desc)?;
                 }
                 if let Some(change_desc) = changeset.change_descriptor {
-                    let (_, _changeset) =
-                        index.insert_descriptor(Keychain::Internal, change_desc)?;
+                    index.insert_descriptor(Keychain::Internal, change_desc)?;
                 }
                 let mut graph = KeychainTxGraph::new(index);
                 graph.apply_changeset(indexed_tx_graph::ChangeSet {