diff --git a/crates/chain/src/indexer/keychain_txout.rs b/crates/chain/src/indexer/keychain_txout.rs index 4717cffdc..e6cb1aa78 100644 --- a/crates/chain/src/indexer/keychain_txout.rs +++ b/crates/chain/src/indexer/keychain_txout.rs @@ -390,6 +390,8 @@ impl KeychainTxOutIndex { /// Here, "unused" means that after the script pubkey was stored in the index, the index has /// never scanned a transaction output with it. /// + /// Returns `false` for an unrevealed index. + /// /// This calls [`SpkTxOutIndex::is_used`] internally. pub fn is_used(&self, keychain: K, index: u32) -> bool { self.inner.is_used(&(keychain, index)) diff --git a/crates/chain/src/indexer/spk_txout.rs b/crates/chain/src/indexer/spk_txout.rs index 1bd889cbb..0c1688ed1 100644 --- a/crates/chain/src/indexer/spk_txout.rs +++ b/crates/chain/src/indexer/spk_txout.rs @@ -246,8 +246,10 @@ impl SpkTxOutIndex { /// /// Here, "unused" means that after the script pubkey was stored in the index, the index has /// never scanned a transaction output with it. + /// + /// Returns `false` if the script pubkey at `index` is not present in the index. pub fn is_used(&self, index: &I) -> bool { - !self.unused.contains(index) + self.spks.contains_key(index) && !self.unused.contains(index) } /// Marks the script pubkey at `index` as used even though it hasn't seen an output spending to diff --git a/crates/chain/tests/test_keychain_txout_index.rs b/crates/chain/tests/test_keychain_txout_index.rs index 3dea4544b..8c736653b 100644 --- a/crates/chain/tests/test_keychain_txout_index.rs +++ b/crates/chain/tests/test_keychain_txout_index.rs @@ -761,3 +761,29 @@ fn overlapping_descriptor_does_not_panic_in_release() { let _ = index.insert_descriptor(1, single); assert!(index.reveal_next_spk(1).is_none()); } + +#[test] +fn is_used_returns_false_for_unrevealed_or_untracked_index() { + let mut index = KeychainTxOutIndex::::default(); + assert!(!index.is_used(TestKeychain::External, 0)); + assert!(!index.is_used(TestKeychain::External, 100)); + + let descriptor = parse_descriptor(DESCRIPTORS[0]); + let _ = index + .insert_descriptor(TestKeychain::External, descriptor) + .unwrap(); + + // Not revealed yet, so not added to the internal spk index + assert!(!index.is_used(TestKeychain::External, 0)); + assert!(!index.is_used(TestKeychain::External, 1)); + + // Reveal index 0 + let _ = index.reveal_next_spk(TestKeychain::External).unwrap(); + assert!(!index.is_used(TestKeychain::External, 0)); + assert!(!index.is_used(TestKeychain::External, 1)); + + // Mark index 0 as used + assert!(index.mark_used(TestKeychain::External, 0)); + assert!(index.is_used(TestKeychain::External, 0)); + assert!(!index.is_used(TestKeychain::External, 1)); +} diff --git a/crates/chain/tests/test_spk_txout_index.rs b/crates/chain/tests/test_spk_txout_index.rs index 9f527760a..71066f203 100644 --- a/crates/chain/tests/test_spk_txout_index.rs +++ b/crates/chain/tests/test_spk_txout_index.rs @@ -196,6 +196,22 @@ fn spk_txout_spent_created_txouts() { ); } +#[test] +fn is_used_returns_false_for_untracked_index() { + let mut index = SpkTxOutIndex::::default(); + assert!(!index.is_used(&0)); + assert!(!index.is_used(&1)); + + let spk = ScriptBuf::from_hex("001404f1e52ce2bab3423c6a8c63b7cd730d8f12542c").unwrap(); + index.insert_spk(0, spk); + assert!(!index.is_used(&0)); + assert!(!index.is_used(&1)); + + index.mark_used(&0); + assert!(index.is_used(&0)); + assert!(!index.is_used(&1)); +} + #[test] fn mark_used() { let spk1 = ScriptBuf::from_hex("001404f1e52ce2bab3423c6a8c63b7cd730d8f12542c").unwrap();