use crate::{ bitcoin::{secp256k1::Secp256k1, ScriptBuf}, miniscript::{Descriptor, DescriptorPublicKey}, }; use core::{borrow::Borrow, ops::Bound, ops::RangeBounds}; /// Maximum [BIP32](https://bips.xyz/32) derivation index. pub const BIP32_MAX_INDEX: u32 = (1 << 31) - 1; /// An iterator for derived script pubkeys. /// /// [`SpkIterator`] is an implementation of the [`Iterator`] trait which possesses its own `next()` /// and `nth()` functions, both of which circumvent the unnecessary intermediate derivations required /// when using their default implementations. /// /// ## Examples /// /// ``` /// use bdk_chain::SpkIterator; /// # use miniscript::{Descriptor, DescriptorPublicKey}; /// # use bitcoin::{secp256k1::Secp256k1}; /// # use std::str::FromStr; /// # let secp = bitcoin::secp256k1::Secp256k1::signing_only(); /// # let (descriptor, _) = Descriptor::::parse_descriptor(&secp, "wpkh([73c5da0a/86'/0'/0']xprv9xgqHN7yz9MwCkxsBPN5qetuNdQSUttZNKw1dcYTV4mkaAFiBVGQziHs3NRSWMkCzvgjEe3n9xV8oYywvM8at9yRqyaZVz6TYYhX98VjsUk/1/0)").unwrap(); /// # let external_spk_0 = descriptor.at_derivation_index(0).unwrap().script_pubkey(); /// # let external_spk_3 = descriptor.at_derivation_index(3).unwrap().script_pubkey(); /// # let external_spk_4 = descriptor.at_derivation_index(4).unwrap().script_pubkey(); /// /// // Creates a new script pubkey iterator starting at 0 from a descriptor. /// let mut spk_iter = SpkIterator::new(&descriptor); /// assert_eq!(spk_iter.next(), Some((0, external_spk_0))); /// assert_eq!(spk_iter.next(), None); /// ``` #[derive(Clone)] pub struct SpkIterator { next_index: u32, end: u32, descriptor: D, secp: Secp256k1, } impl SpkIterator where D: Borrow>, { /// Create a new script pubkey iterator from `descriptor`. /// /// This iterates from derivation index 0 and stops at index 0x7FFFFFFF (as specified in /// BIP-32). Non-wildcard descriptors will only return one script pubkey at derivation index 0. /// /// Use [`new_with_range`](SpkIterator::new_with_range) to create an iterator with a specified /// derivation index range. pub fn new(descriptor: D) -> Self { SpkIterator::new_with_range(descriptor, 0..=BIP32_MAX_INDEX) } /// Create a new script pubkey iterator from `descriptor` and a given `range`. /// /// Non-wildcard descriptors will only emit a single script pubkey (at derivation index 0). /// Wildcard descriptors have an end-bound of 0x7FFFFFFF (inclusive). /// /// Refer to [`new`](SpkIterator::new) for more. pub fn new_with_range(descriptor: D, range: R) -> Self where R: RangeBounds, { let start = match range.start_bound() { Bound::Included(start) => *start, Bound::Excluded(start) => *start + 1, Bound::Unbounded => u32::MIN, }; let mut end = match range.end_bound() { Bound::Included(end) => *end + 1, Bound::Excluded(end) => *end, Bound::Unbounded => u32::MAX, }; // Because `end` is exclusive, we want the maximum value to be BIP32_MAX_INDEX + 1. end = end.min(BIP32_MAX_INDEX + 1); Self { next_index: start, end, descriptor, secp: Secp256k1::verification_only(), } } /// Get a reference to the internal descriptor. pub fn descriptor(&self) -> &D { &self.descriptor } } impl Iterator for SpkIterator where D: Borrow>, { type Item = (u32, ScriptBuf); fn next(&mut self) -> Option { // For non-wildcard descriptors, we expect the first element to be Some((0, spk)), then None after. // For wildcard descriptors, we expect it to keep iterating until exhausted. if self.next_index >= self.end { return None; } // If the descriptor is non-wildcard, only index 0 will return an spk. if !self.descriptor.borrow().has_wildcard() && self.next_index != 0 { return None; } let script = self .descriptor .borrow() .derived_descriptor(&self.secp, self.next_index) .expect("the descriptor cannot need hardened derivation") .script_pubkey(); let output = (self.next_index, script); self.next_index += 1; Some(output) } fn nth(&mut self, n: usize) -> Option { self.next_index = self .next_index .saturating_add(u32::try_from(n).unwrap_or(u32::MAX)); self.next() } } #[cfg(test)] mod test { use crate::{ bitcoin::secp256k1::Secp256k1, keychain::KeychainTxOutIndex, miniscript::{Descriptor, DescriptorPublicKey}, spk_iter::{SpkIterator, BIP32_MAX_INDEX}, }; #[derive(Clone, Debug, PartialEq, Eq, Ord, PartialOrd)] enum TestKeychain { External, Internal, } fn init_txout_index() -> ( KeychainTxOutIndex, Descriptor, Descriptor, ) { let mut txout_index = KeychainTxOutIndex::::new(0); let secp = Secp256k1::signing_only(); let (external_descriptor,_) = Descriptor::::parse_descriptor(&secp, "tr([73c5da0a/86'/0'/0']xprv9xgqHN7yz9MwCkxsBPN5qetuNdQSUttZNKw1dcYTV4mkaAFiBVGQziHs3NRSWMkCzvgjEe3n9xV8oYywvM8at9yRqyaZVz6TYYhX98VjsUk/0/*)").unwrap(); let (internal_descriptor,_) = Descriptor::::parse_descriptor(&secp, "tr([73c5da0a/86'/0'/0']xprv9xgqHN7yz9MwCkxsBPN5qetuNdQSUttZNKw1dcYTV4mkaAFiBVGQziHs3NRSWMkCzvgjEe3n9xV8oYywvM8at9yRqyaZVz6TYYhX98VjsUk/1/*)").unwrap(); txout_index.add_keychain(TestKeychain::External, external_descriptor.clone()); txout_index.add_keychain(TestKeychain::Internal, internal_descriptor.clone()); (txout_index, external_descriptor, internal_descriptor) } #[test] #[allow(clippy::iter_nth_zero)] #[rustfmt::skip] fn test_spkiterator_wildcard() { let (_, external_desc, _) = init_txout_index(); let external_spk_0 = external_desc.at_derivation_index(0).unwrap().script_pubkey(); let external_spk_16 = external_desc.at_derivation_index(16).unwrap().script_pubkey(); let external_spk_20 = external_desc.at_derivation_index(20).unwrap().script_pubkey(); let external_spk_21 = external_desc.at_derivation_index(21).unwrap().script_pubkey(); let external_spk_max = external_desc.at_derivation_index(BIP32_MAX_INDEX).unwrap().script_pubkey(); let mut external_spk = SpkIterator::new(&external_desc); let max_index = BIP32_MAX_INDEX - 22; assert_eq!(external_spk.next(), Some((0, external_spk_0))); assert_eq!(external_spk.nth(15), Some((16, external_spk_16))); assert_eq!(external_spk.nth(3), Some((20, external_spk_20.clone()))); assert_eq!(external_spk.next(), Some((21, external_spk_21))); assert_eq!( external_spk.nth(max_index as usize), Some((BIP32_MAX_INDEX, external_spk_max)) ); assert_eq!(external_spk.nth(0), None); let mut external_spk = SpkIterator::new_with_range(&external_desc, 0..21); assert_eq!(external_spk.nth(20), Some((20, external_spk_20))); assert_eq!(external_spk.next(), None); let mut external_spk = SpkIterator::new_with_range(&external_desc, 0..21); assert_eq!(external_spk.nth(21), None); } #[test] #[allow(clippy::iter_nth_zero)] fn test_spkiterator_non_wildcard() { let secp = bitcoin::secp256k1::Secp256k1::signing_only(); let (no_wildcard_descriptor, _) = Descriptor::::parse_descriptor(&secp, "wpkh([73c5da0a/86'/0'/0']xprv9xgqHN7yz9MwCkxsBPN5qetuNdQSUttZNKw1dcYTV4mkaAFiBVGQziHs3NRSWMkCzvgjEe3n9xV8oYywvM8at9yRqyaZVz6TYYhX98VjsUk/1/0)").unwrap(); let external_spk_0 = no_wildcard_descriptor .at_derivation_index(0) .unwrap() .script_pubkey(); let mut external_spk = SpkIterator::new(&no_wildcard_descriptor); assert_eq!(external_spk.next(), Some((0, external_spk_0.clone()))); assert_eq!(external_spk.next(), None); let mut external_spk = SpkIterator::new(&no_wildcard_descriptor); assert_eq!(external_spk.nth(0), Some((0, external_spk_0.clone()))); assert_eq!(external_spk.nth(0), None); let mut external_spk = SpkIterator::new_with_range(&no_wildcard_descriptor, 0..0); assert_eq!(external_spk.next(), None); let mut external_spk = SpkIterator::new_with_range(&no_wildcard_descriptor, 0..1); assert_eq!(external_spk.nth(0), Some((0, external_spk_0.clone()))); assert_eq!(external_spk.next(), None); // We test that using new_with_range with range_len > 1 gives back an iterator with // range_len = 1 let mut external_spk = SpkIterator::new_with_range(&no_wildcard_descriptor, 0..10); assert_eq!(external_spk.nth(0), Some((0, external_spk_0))); assert_eq!(external_spk.nth(0), None); // non index-0 should NOT return an spk assert_eq!( SpkIterator::new_with_range(&no_wildcard_descriptor, 1..1).next(), None ); assert_eq!( SpkIterator::new_with_range(&no_wildcard_descriptor, 1..=1).next(), None ); assert_eq!( SpkIterator::new_with_range(&no_wildcard_descriptor, 1..2).next(), None ); assert_eq!( SpkIterator::new_with_range(&no_wildcard_descriptor, 1..=2).next(), None ); assert_eq!( SpkIterator::new_with_range(&no_wildcard_descriptor, 10..11).next(), None ); assert_eq!( SpkIterator::new_with_range(&no_wildcard_descriptor, 10..=10).next(), None ); } // The following dummy traits were created to test if SpkIterator is working properly. trait TestSendStatic: Send + 'static { fn test(&self) -> u32 { 20 } } impl TestSendStatic for SpkIterator> { fn test(&self) -> u32 { 20 } } }