Skip to content

Commit d299dae

Browse files
committed
feat(chain)!: Persist spks derived from KeychainTxOutIndex
1 parent 4a7c0ed commit d299dae

File tree

7 files changed

+261
-51
lines changed

7 files changed

+261
-51
lines changed

crates/bitcoind_rpc/examples/filter_iter.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,9 @@ fn main() -> anyhow::Result<()> {
3333
let (mut chain, _) = LocalChain::from_genesis_hash(genesis_block(NETWORK).block_hash());
3434
let mut graph = IndexedTxGraph::<ConfirmationBlockTime, KeychainTxOutIndex<&str>>::new({
3535
let mut index = KeychainTxOutIndex::default();
36-
index.insert_descriptor("external", descriptor.clone())?;
37-
index.insert_descriptor("internal", change_descriptor.clone())?;
36+
// TODO: Properly deal with these changesets.
37+
let _ = index.insert_descriptor("external", descriptor.clone())?;
38+
let _ = index.insert_descriptor("internal", change_descriptor.clone())?;
3839
index
3940
});
4041

crates/chain/benches/canonicalization.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ fn setup<F: Fn(&mut KeychainTxGraph, &LocalChain)>(f: F) -> (KeychainTxGraph, Lo
8383
let (desc, _) =
8484
<Descriptor<DescriptorPublicKey>>::parse_descriptor(&Secp256k1::new(), DESC).unwrap();
8585
let mut index = KeychainTxOutIndex::new(10);
86-
index.insert_descriptor((), desc).unwrap();
86+
let _ = index.insert_descriptor((), desc).unwrap();
8787
let mut tx_graph = KeychainTxGraph::new(index);
8888

8989
f(&mut tx_graph, &chain);

crates/chain/src/indexer/keychain_txout.rs

Lines changed: 125 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
//! indexes [`TxOut`]s with them.
33
44
use crate::{
5+
alloc::boxed::Box,
56
collections::*,
67
miniscript::{Descriptor, DescriptorPublicKey},
78
spk_client::{FullScanRequestBuilder, SyncRequestBuilder},
@@ -10,7 +11,9 @@ use crate::{
1011
DescriptorExt, DescriptorId, Indexed, Indexer, KeychainIndexed, SpkIterator,
1112
};
1213
use alloc::{borrow::ToOwned, vec::Vec};
13-
use bitcoin::{Amount, OutPoint, ScriptBuf, SignedAmount, Transaction, TxOut, Txid};
14+
use bitcoin::{
15+
key::Secp256k1, Amount, OutPoint, ScriptBuf, SignedAmount, Transaction, TxOut, Txid,
16+
};
1417
use core::{
1518
fmt::Debug,
1619
ops::{Bound, RangeBounds},
@@ -128,6 +131,8 @@ pub struct KeychainTxOutIndex<K> {
128131
descriptors: HashMap<DescriptorId, Descriptor<DescriptorPublicKey>>,
129132
last_revealed: HashMap<DescriptorId, u32>,
130133
lookahead: u32,
134+
135+
spk_cache: BTreeMap<DescriptorId, HashMap<u32, ScriptBuf>>,
131136
}
132137

133138
impl<K> Default for KeychainTxOutIndex<K> {
@@ -155,7 +160,12 @@ impl<K: Clone + Ord + Debug> Indexer for KeychainTxOutIndex<K> {
155160
if self.last_revealed.get(did) < Some(&index) {
156161
self.last_revealed.insert(*did, index);
157162
changeset.last_revealed.insert(*did, index);
158-
self.replenish_inner_index(*did, &keychain, self.lookahead);
163+
self.replenish_inner_index(
164+
*did,
165+
&keychain,
166+
self.lookahead,
167+
changeset.spk_cache.entry(*did).or_default(),
168+
);
159169
}
160170
}
161171
changeset
@@ -173,6 +183,16 @@ impl<K: Clone + Ord + Debug> Indexer for KeychainTxOutIndex<K> {
173183
fn initial_changeset(&self) -> Self::ChangeSet {
174184
ChangeSet {
175185
last_revealed: self.last_revealed.clone().into_iter().collect(),
186+
spk_cache: self
187+
.spk_cache
188+
.iter()
189+
.map(|(desc, spks)| {
190+
(
191+
*desc,
192+
spks.iter().map(|(i, spk)| (*i, spk.clone())).collect(),
193+
)
194+
})
195+
.collect(),
176196
}
177197
}
178198

@@ -204,6 +224,7 @@ impl<K> KeychainTxOutIndex<K> {
204224
descriptor_id_to_keychain: Default::default(),
205225
last_revealed: Default::default(),
206226
lookahead,
227+
spk_cache: Default::default(),
207228
}
208229
}
209230

@@ -365,23 +386,30 @@ impl<K: Clone + Ord + Debug> KeychainTxOutIndex<K> {
365386
&mut self,
366387
keychain: K,
367388
descriptor: Descriptor<DescriptorPublicKey>,
368-
) -> Result<bool, InsertDescriptorError<K>> {
389+
) -> Result<(bool, ChangeSet), InsertDescriptorError<K>> {
390+
let mut changeset = ChangeSet::default();
391+
369392
let did = descriptor.descriptor_id();
370393
if !self.keychain_to_descriptor_id.contains_key(&keychain)
371394
&& !self.descriptor_id_to_keychain.contains_key(&did)
372395
{
373396
self.descriptors.insert(did, descriptor.clone());
374397
self.keychain_to_descriptor_id.insert(keychain.clone(), did);
375398
self.descriptor_id_to_keychain.insert(did, keychain.clone());
376-
self.replenish_inner_index(did, &keychain, self.lookahead);
377-
return Ok(true);
399+
self.replenish_inner_index(
400+
did,
401+
&keychain,
402+
self.lookahead,
403+
changeset.spk_cache.entry(did).or_default(),
404+
);
405+
return Ok((true, changeset));
378406
}
379407

380408
if let Some(existing_desc_id) = self.keychain_to_descriptor_id.get(&keychain) {
381409
let descriptor = self.descriptors.get(existing_desc_id).expect("invariant");
382410
if *existing_desc_id != did {
383411
return Err(InsertDescriptorError::KeychainAlreadyAssigned {
384-
existing_assignment: descriptor.clone(),
412+
existing_assignment: Box::new(descriptor.clone()),
385413
keychain,
386414
});
387415
}
@@ -393,12 +421,12 @@ impl<K: Clone + Ord + Debug> KeychainTxOutIndex<K> {
393421
if *existing_keychain != keychain {
394422
return Err(InsertDescriptorError::DescriptorAlreadyAssigned {
395423
existing_assignment: existing_keychain.clone(),
396-
descriptor,
424+
descriptor: Box::new(descriptor),
397425
});
398426
}
399427
}
400428

401-
Ok(false)
429+
Ok((false, changeset))
402430
}
403431

404432
/// Gets the descriptor associated with the keychain. Returns `None` if the keychain doesn't
@@ -420,47 +448,100 @@ impl<K: Clone + Ord + Debug> KeychainTxOutIndex<K> {
420448
/// Store lookahead scripts until `target_index` (inclusive).
421449
///
422450
/// This does not change the global `lookahead` setting.
423-
pub fn lookahead_to_target(&mut self, keychain: K, target_index: u32) {
451+
pub fn lookahead_to_target(
452+
&mut self,
453+
keychain: K,
454+
target_index: u32,
455+
derived_spks: &mut impl Extend<Indexed<ScriptBuf>>,
456+
) {
424457
if let Some((next_index, _)) = self.next_index(keychain.clone()) {
425458
let temp_lookahead = (target_index + 1)
426459
.checked_sub(next_index)
427460
.filter(|&index| index > 0);
428461

429462
if let Some(temp_lookahead) = temp_lookahead {
430-
self.replenish_inner_index_keychain(keychain, temp_lookahead);
463+
self.replenish_inner_index_keychain(keychain, temp_lookahead, derived_spks);
431464
}
432465
}
433466
}
434467

435-
fn replenish_inner_index_did(&mut self, did: DescriptorId, lookahead: u32) {
468+
fn replenish_inner_index_did(
469+
&mut self,
470+
did: DescriptorId,
471+
lookahead: u32,
472+
derived_spks: &mut impl Extend<Indexed<ScriptBuf>>,
473+
) {
436474
if let Some(keychain) = self.descriptor_id_to_keychain.get(&did).cloned() {
437-
self.replenish_inner_index(did, &keychain, lookahead);
475+
self.replenish_inner_index(did, &keychain, lookahead, derived_spks);
438476
}
439477
}
440478

441-
fn replenish_inner_index_keychain(&mut self, keychain: K, lookahead: u32) {
479+
fn replenish_inner_index_keychain(
480+
&mut self,
481+
keychain: K,
482+
lookahead: u32,
483+
derived_spks: &mut impl Extend<Indexed<ScriptBuf>>,
484+
) {
442485
if let Some(did) = self.keychain_to_descriptor_id.get(&keychain) {
443-
self.replenish_inner_index(*did, &keychain, lookahead);
486+
self.replenish_inner_index(*did, &keychain, lookahead, derived_spks);
444487
}
445488
}
446489

447490
/// Syncs the state of the inner spk index after changes to a keychain
448-
fn replenish_inner_index(&mut self, did: DescriptorId, keychain: &K, lookahead: u32) {
491+
fn replenish_inner_index(
492+
&mut self,
493+
did: DescriptorId,
494+
keychain: &K,
495+
lookahead: u32,
496+
derived_spks: &mut impl Extend<Indexed<ScriptBuf>>,
497+
) {
449498
let descriptor = self.descriptors.get(&did).expect("invariant");
450-
let next_store_index = self
499+
500+
let mut next_index = self
451501
.inner
452502
.all_spks()
453503
.range(&(keychain.clone(), u32::MIN)..=&(keychain.clone(), u32::MAX))
454504
.last()
455505
.map_or(0, |((_, index), _)| *index + 1);
456-
let next_reveal_index = self.last_revealed.get(&did).map_or(0, |v| *v + 1);
457-
for (new_index, new_spk) in
458-
SpkIterator::new_with_range(descriptor, next_store_index..next_reveal_index + lookahead)
459-
{
506+
507+
// Exclusive: index to stop at.
508+
let stop_index = if descriptor.has_wildcard() {
509+
let next_reveal_index = self.last_revealed.get(&did).map_or(0, |v| *v + 1);
510+
(next_reveal_index + lookahead).min(BIP32_MAX_INDEX)
511+
} else {
512+
1
513+
};
514+
515+
let cached_spk_iter = core::iter::from_fn({
516+
let secp = Secp256k1::verification_only();
517+
let _desc = &descriptor;
518+
let spk_cache = self.spk_cache.entry(did).or_default();
519+
let _i = &mut next_index;
520+
move || -> Option<Indexed<ScriptBuf>> {
521+
if *_i >= stop_index {
522+
return None;
523+
}
524+
let spk_i = *_i;
525+
*_i = spk_i.saturating_add(1);
526+
527+
if let Some(spk) = spk_cache.get(_i) {
528+
return Some((spk_i, spk.clone()));
529+
}
530+
let spk = _desc
531+
.derived_descriptor(&secp, spk_i)
532+
.expect("The descriptor cannot have hardened derivation")
533+
.script_pubkey();
534+
derived_spks.extend(core::iter::once((spk_i, spk.clone())));
535+
spk_cache.insert(spk_i, spk.clone());
536+
Some((spk_i, spk.clone()))
537+
}
538+
});
539+
540+
for (new_index, new_spk) in cached_spk_iter {
460541
let _inserted = self
461542
.inner
462543
.insert_spk((keychain.clone(), new_index), new_spk);
463-
debug_assert!(_inserted, "replenish lookahead: must not have existing spk: keychain={:?}, lookahead={}, next_store_index={}, next_reveal_index={}", keychain, lookahead, next_store_index, next_reveal_index);
544+
debug_assert!(_inserted, "replenish lookahead: must not have existing spk: keychain={:?}, lookahead={}, next_index={}", keychain, lookahead, next_index);
464545
}
465546
}
466547

@@ -693,7 +774,12 @@ impl<K: Clone + Ord + Debug> KeychainTxOutIndex<K> {
693774
let did = self.keychain_to_descriptor_id.get(&keychain)?;
694775
self.last_revealed.insert(*did, next_index);
695776
changeset.last_revealed.insert(*did, next_index);
696-
self.replenish_inner_index(*did, &keychain, self.lookahead);
777+
self.replenish_inner_index(
778+
*did,
779+
&keychain,
780+
self.lookahead,
781+
changeset.spk_cache.entry(*did).or_default(),
782+
);
697783
}
698784
let script = self
699785
.inner
@@ -779,10 +865,13 @@ impl<K: Clone + Ord + Debug> KeychainTxOutIndex<K> {
779865

780866
/// Applies the `ChangeSet<K>` to the [`KeychainTxOutIndex<K>`]
781867
pub fn apply_changeset(&mut self, changeset: ChangeSet) {
782-
for (&desc_id, &index) in &changeset.last_revealed {
783-
let v = self.last_revealed.entry(desc_id).or_default();
868+
for (did, index) in changeset.last_revealed {
869+
let v = self.last_revealed.entry(did).or_default();
784870
*v = index.max(*v);
785-
self.replenish_inner_index_did(desc_id, self.lookahead);
871+
self.replenish_inner_index_did(did, self.lookahead, &mut Vec::new());
872+
}
873+
for (did, spks) in changeset.spk_cache {
874+
self.spk_cache.entry(did).or_default().extend(spks);
786875
}
787876
}
788877
}
@@ -793,7 +882,7 @@ pub enum InsertDescriptorError<K> {
793882
/// The descriptor has already been assigned to a keychain so you can't assign it to another
794883
DescriptorAlreadyAssigned {
795884
/// The descriptor you have attempted to reassign
796-
descriptor: Descriptor<DescriptorPublicKey>,
885+
descriptor: Box<Descriptor<DescriptorPublicKey>>,
797886
/// The keychain that the descriptor is already assigned to
798887
existing_assignment: K,
799888
},
@@ -802,7 +891,7 @@ pub enum InsertDescriptorError<K> {
802891
/// The keychain that you have attempted to reassign
803892
keychain: K,
804893
/// The descriptor that the keychain is already assigned to
805-
existing_assignment: Descriptor<DescriptorPublicKey>,
894+
existing_assignment: Box<Descriptor<DescriptorPublicKey>>,
806895
},
807896
}
808897

@@ -852,6 +941,10 @@ impl<K: core::fmt::Debug> std::error::Error for InsertDescriptorError<K> {}
852941
pub struct ChangeSet {
853942
/// Contains for each descriptor_id the last revealed index of derivation
854943
pub last_revealed: BTreeMap<DescriptorId, u32>,
944+
945+
/// Spk cache.
946+
#[cfg_attr(feature = "serde", serde(default))]
947+
pub spk_cache: BTreeMap<DescriptorId, BTreeMap<u32, ScriptBuf>>,
855948
}
856949

857950
impl Merge for ChangeSet {
@@ -872,11 +965,15 @@ impl Merge for ChangeSet {
872965
}
873966
}
874967
}
968+
969+
for (did, spks) in other.spk_cache {
970+
self.spk_cache.entry(did).or_default().extend(spks);
971+
}
875972
}
876973

877974
/// Returns whether the changeset are empty.
878975
fn is_empty(&self) -> bool {
879-
self.last_revealed.is_empty()
976+
self.last_revealed.is_empty() && self.spk_cache.is_empty()
880977
}
881978
}
882979

0 commit comments

Comments
 (0)