Skip to content

Commit 1d98954

Browse files
committed
fix(keychain_txout): Remove spk_cache_stage
`spk_cache_stage` was being used to hold newly derived SPKs before being transferred to the `ChangeSet`. As a result it became necessary to call `_empty_stage_into_changeset` whenever the internal state changed, requiring the developer to know which methods depended on the functionality, e.g. `scan_txout` and `reveal_next_spk`. Further, this altered the behavior of the `ChangeSet` in surprising ways. For example revealing 1 SPK might return additional scripts in the `ChangeSet` if they had previously been staged. The rationale in bitcoindevkit#1963 was to avoid returning a `ChangeSet` from `insert_descriptor`. We can do this by providing another method that returns a tuple of `(bool, ChangeSet)` so that descriptor insertion results in updating the `ChangeSet` directly. Any code using the original `insert_descriptor` can still access the `ChangeSet` using `initial_changeset` (which may include SPKs from other descriptors), or we can expose an accessor to get a reference to the `spk_cache`.
1 parent b45ecb9 commit 1d98954

File tree

2 files changed

+36
-49
lines changed

2 files changed

+36
-49
lines changed

crates/chain/src/indexer/keychain_txout.rs

Lines changed: 31 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -138,8 +138,6 @@ pub struct KeychainTxOutIndex<K> {
138138
persist_spks: bool,
139139
/// Cache of derived spks.
140140
spk_cache: BTreeMap<DescriptorId, HashMap<u32, ScriptBuf>>,
141-
/// Staged script pubkeys waiting to be written out in the next ChangeSet.
142-
spk_cache_stage: BTreeMap<DescriptorId, Vec<(u32, ScriptBuf)>>,
143141
}
144142

145143
impl<K> Default for KeychainTxOutIndex<K> {
@@ -160,7 +158,6 @@ impl<K: Clone + Ord + Debug> Indexer for KeychainTxOutIndex<K> {
160158
fn index_txout(&mut self, outpoint: OutPoint, txout: &TxOut) -> Self::ChangeSet {
161159
let mut changeset = ChangeSet::default();
162160
self._index_txout(&mut changeset, outpoint, txout);
163-
self._empty_stage_into_changeset(&mut changeset);
164161
changeset
165162
}
166163

@@ -170,7 +167,6 @@ impl<K: Clone + Ord + Debug> Indexer for KeychainTxOutIndex<K> {
170167
for (vout, txout) in tx.output.iter().enumerate() {
171168
self._index_txout(&mut changeset, OutPoint::new(txid, vout as u32), txout);
172169
}
173-
self._empty_stage_into_changeset(&mut changeset);
174170
changeset
175171
}
176172

@@ -237,7 +233,6 @@ impl<K> KeychainTxOutIndex<K> {
237233
lookahead,
238234
persist_spks,
239235
spk_cache: Default::default(),
240-
spk_cache_stage: Default::default(),
241236
}
242237
}
243238

@@ -280,33 +275,11 @@ impl<K: Clone + Ord + Debug> KeychainTxOutIndex<K> {
280275
};
281276
if index_updated {
282277
changeset.last_revealed.insert(*did, index);
283-
self.replenish_inner_index(*did, &keychain, self.lookahead);
278+
self.replenish_inner_index(*did, &keychain, self.lookahead, changeset);
284279
}
285280
}
286281
}
287282

288-
fn _empty_stage_into_changeset(&mut self, changeset: &mut ChangeSet) {
289-
if !self.persist_spks {
290-
return;
291-
}
292-
for (did, spks) in core::mem::take(&mut self.spk_cache_stage) {
293-
debug_assert!(
294-
{
295-
let desc = self.descriptors.get(&did).expect("invariant");
296-
spks.iter().all(|(i, spk)| {
297-
let exp_spk = desc
298-
.at_derivation_index(*i)
299-
.expect("must derive")
300-
.script_pubkey();
301-
&exp_spk == spk
302-
})
303-
},
304-
"all staged spks must be correct"
305-
);
306-
changeset.spk_cache.entry(did).or_default().extend(spks);
307-
}
308-
}
309-
310283
/// Get the set of indexed outpoints, corresponding to tracked keychains.
311284
pub fn outpoints(&self) -> &BTreeSet<KeychainIndexed<K, OutPoint>> {
312285
self.inner.outpoints()
@@ -468,7 +441,7 @@ impl<K: Clone + Ord + Debug> KeychainTxOutIndex<K> {
468441
self.descriptors.insert(did, descriptor.clone());
469442
self.keychain_to_descriptor_id.insert(keychain.clone(), did);
470443
self.descriptor_id_to_keychain.insert(did, keychain.clone());
471-
self.replenish_inner_index(did, &keychain, self.lookahead);
444+
self.replenish_inner_index(did, &keychain, self.lookahead, &mut ChangeSet::default());
472445
return Ok(true);
473446
}
474447

@@ -523,27 +496,42 @@ impl<K: Clone + Ord + Debug> KeychainTxOutIndex<K> {
523496
.filter(|&index| index > 0);
524497

525498
if let Some(temp_lookahead) = temp_lookahead {
526-
self.replenish_inner_index_keychain(keychain, temp_lookahead);
499+
self.replenish_inner_index_keychain(keychain, temp_lookahead, &mut changeset);
527500
}
528501
}
529-
self._empty_stage_into_changeset(&mut changeset);
530502
changeset
531503
}
532504

533-
fn replenish_inner_index_did(&mut self, did: DescriptorId, lookahead: u32) {
505+
fn replenish_inner_index_did(
506+
&mut self,
507+
did: DescriptorId,
508+
lookahead: u32,
509+
changeset: &mut ChangeSet,
510+
) {
534511
if let Some(keychain) = self.descriptor_id_to_keychain.get(&did).cloned() {
535-
self.replenish_inner_index(did, &keychain, lookahead);
512+
self.replenish_inner_index(did, &keychain, lookahead, changeset);
536513
}
537514
}
538515

539-
fn replenish_inner_index_keychain(&mut self, keychain: K, lookahead: u32) {
516+
fn replenish_inner_index_keychain(
517+
&mut self,
518+
keychain: K,
519+
lookahead: u32,
520+
changeset: &mut ChangeSet,
521+
) {
540522
if let Some(did) = self.keychain_to_descriptor_id.get(&keychain) {
541-
self.replenish_inner_index(*did, &keychain, lookahead);
523+
self.replenish_inner_index(*did, &keychain, lookahead, changeset);
542524
}
543525
}
544526

545527
/// Syncs the state of the inner spk index after changes to a keychain
546-
fn replenish_inner_index(&mut self, did: DescriptorId, keychain: &K, lookahead: u32) {
528+
fn replenish_inner_index(
529+
&mut self,
530+
did: DescriptorId,
531+
keychain: &K,
532+
lookahead: u32,
533+
changeset: &mut ChangeSet,
534+
) {
547535
let descriptor = self.descriptors.get(&did).expect("invariant");
548536

549537
let mut next_index = self
@@ -574,7 +562,6 @@ impl<K: Clone + Ord + Debug> KeychainTxOutIndex<K> {
574562
};
575563
let cached_spk_iter = core::iter::from_fn({
576564
let spk_cache = self.spk_cache.entry(did).or_default();
577-
let spk_stage = self.spk_cache_stage.entry(did).or_default();
578565
let _i = &mut next_index;
579566
move || -> Option<Indexed<ScriptBuf>> {
580567
if *_i >= stop_index {
@@ -588,8 +575,12 @@ impl<K: Clone + Ord + Debug> KeychainTxOutIndex<K> {
588575
return Some((spk_i, spk.clone()));
589576
}
590577
let spk = derive_spk(spk_i);
591-
spk_stage.push((spk_i, spk.clone()));
592578
spk_cache.insert(spk_i, spk.clone());
579+
changeset
580+
.spk_cache
581+
.entry(did)
582+
.or_default()
583+
.insert(spk_i, spk.clone());
593584
Some((spk_i, spk))
594585
}
595586
});
@@ -778,7 +769,6 @@ impl<K: Clone + Ord + Debug> KeychainTxOutIndex<K> {
778769
self._reveal_to_target(&mut changeset, keychain.clone(), index);
779770
}
780771

781-
self._empty_stage_into_changeset(&mut changeset);
782772
changeset
783773
}
784774

@@ -802,7 +792,6 @@ impl<K: Clone + Ord + Debug> KeychainTxOutIndex<K> {
802792
) -> Option<(Vec<Indexed<ScriptBuf>>, ChangeSet)> {
803793
let mut changeset = ChangeSet::default();
804794
let revealed_spks = self._reveal_to_target(&mut changeset, keychain, target_index)?;
805-
self._empty_stage_into_changeset(&mut changeset);
806795
Some((revealed_spks, changeset))
807796
}
808797
fn _reveal_to_target(
@@ -840,7 +829,6 @@ impl<K: Clone + Ord + Debug> KeychainTxOutIndex<K> {
840829
pub fn reveal_next_spk(&mut self, keychain: K) -> Option<(Indexed<ScriptBuf>, ChangeSet)> {
841830
let mut changeset = ChangeSet::default();
842831
let indexed_spk = self._reveal_next_spk(&mut changeset, keychain)?;
843-
self._empty_stage_into_changeset(&mut changeset);
844832
Some((indexed_spk, changeset))
845833
}
846834
fn _reveal_next_spk(
@@ -853,7 +841,7 @@ impl<K: Clone + Ord + Debug> KeychainTxOutIndex<K> {
853841
let did = self.keychain_to_descriptor_id.get(&keychain)?;
854842
self.last_revealed.insert(*did, next_index);
855843
changeset.last_revealed.insert(*did, next_index);
856-
self.replenish_inner_index(*did, &keychain, self.lookahead);
844+
self.replenish_inner_index(*did, &keychain, self.lookahead, changeset);
857845
}
858846
let script = self
859847
.inner
@@ -882,7 +870,6 @@ impl<K: Clone + Ord + Debug> KeychainTxOutIndex<K> {
882870
.next()
883871
.map(|(i, spk)| (i, spk.to_owned()));
884872
let spk = next_unused.or_else(|| self._reveal_next_spk(&mut changeset, keychain))?;
885-
self._empty_stage_into_changeset(&mut changeset);
886873
Some((spk, changeset))
887874
}
888875

@@ -949,7 +936,7 @@ impl<K: Clone + Ord + Debug> KeychainTxOutIndex<K> {
949936
for (did, index) in changeset.last_revealed {
950937
let v = self.last_revealed.entry(did).or_default();
951938
*v = index.max(*v);
952-
self.replenish_inner_index_did(did, self.lookahead);
939+
self.replenish_inner_index_did(did, self.lookahead, &mut ChangeSet::default());
953940
}
954941
}
955942
}

crates/chain/tests/test_indexed_tx_graph.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,7 @@ fn insert_relevant_txs() {
219219
let (descriptor, _) = Descriptor::parse_descriptor(&Secp256k1::signing_only(), DESCRIPTORS[0])
220220
.expect("must be valid");
221221
let spk_0 = descriptor.at_derivation_index(0).unwrap().script_pubkey();
222-
let spk_1 = descriptor.at_derivation_index(9).unwrap().script_pubkey();
222+
let spk_9 = descriptor.at_derivation_index(9).unwrap().script_pubkey();
223223
let lookahead = 10;
224224

225225
let mut graph = IndexedTxGraph::<ConfirmationBlockTime, KeychainTxOutIndex<()>>::new({
@@ -237,7 +237,7 @@ fn insert_relevant_txs() {
237237
},
238238
TxOut {
239239
value: Amount::from_sat(20_000),
240-
script_pubkey: spk_1,
240+
script_pubkey: spk_9,
241241
},
242242
],
243243
..new_tx(0)
@@ -269,12 +269,12 @@ fn insert_relevant_txs() {
269269
indexer: keychain_txout::ChangeSet {
270270
last_revealed: [(descriptor.descriptor_id(), 9_u32)].into(),
271271
spk_cache: [(descriptor.descriptor_id(), {
272-
let index_after_spk_1 = 9 /* index of spk_1 */ + 1;
272+
let index_after_spk_9 = 9 /* index of spk_9 */ + 1;
273273
SpkIterator::new_with_range(
274274
&descriptor,
275275
// This will also persist the staged spk cache inclusions from prev call to
276276
// `.insert_descriptor`.
277-
0..index_after_spk_1 + lookahead,
277+
index_after_spk_9..index_after_spk_9 + lookahead,
278278
)
279279
.collect()
280280
})]
@@ -294,7 +294,7 @@ fn insert_relevant_txs() {
294294
last_revealed: changeset.indexer.last_revealed,
295295
spk_cache: [(
296296
descriptor.descriptor_id(),
297-
SpkIterator::new_with_range(&descriptor, 0..=9 /* index of spk_1*/ + lookahead)
297+
SpkIterator::new_with_range(&descriptor, 0..=9 /* index of spk_9*/ + lookahead)
298298
.collect(),
299299
)]
300300
.into(),

0 commit comments

Comments
 (0)