|
23 | 23 | //! these with BIP32 paths, pay-to-contract instructions, etc.
|
24 | 24 | //!
|
25 | 25 |
|
| 26 | +use std::ops::Range; |
26 | 27 | use std::{collections::HashMap, sync::Arc};
|
27 | 28 | use std::{
|
28 | 29 | fmt,
|
@@ -749,6 +750,30 @@ impl Descriptor<DescriptorPublicKey> {
|
749 | 750 |
|
750 | 751 | descriptor.to_string()
|
751 | 752 | }
|
| 753 | + |
| 754 | + /// Utility method for deriving the descriptor at each index in a range to find one matching |
| 755 | + /// `script_pubkey`. |
| 756 | + /// |
| 757 | + /// If it finds a match then it returns the index it was derived it and the concrete descriptor |
| 758 | + /// at that index. If the descriptor is non-derivable then it will simply check the script |
| 759 | + /// pubkey against the descriptor (and in that case the index returned will be meaningless). |
| 760 | + pub fn find_derivation_index_for_spk<C: secp256k1::Verification>( |
| 761 | + &self, |
| 762 | + secp: &secp256k1::Secp256k1<C>, |
| 763 | + script_pubkey: &Script, |
| 764 | + range: Range<u32>, |
| 765 | + ) -> Result<Option<(u32, Descriptor<bitcoin::PublicKey>)>, ConversionError> { |
| 766 | + let range = if self.is_deriveable() { range } else { 0..1 }; |
| 767 | + |
| 768 | + for i in range { |
| 769 | + let concrete = self.derived_descriptor(&secp, i)?; |
| 770 | + if &concrete.script_pubkey() == script_pubkey { |
| 771 | + return Ok(Some((i, concrete))); |
| 772 | + } |
| 773 | + } |
| 774 | + |
| 775 | + Ok(None) |
| 776 | + } |
752 | 777 | }
|
753 | 778 |
|
754 | 779 | impl<Pk> expression::FromTree for Descriptor<Pk>
|
@@ -1736,4 +1761,31 @@ pk(03f28773c2d975288bc7d1d205c3748651b075fbc6610e58cddeeddf8f19405aa8))";
|
1736 | 1761 | Descriptor::<DescriptorPublicKey>::from_str(&format!("wsh(pk({}))", x_only_key))
|
1737 | 1762 | .unwrap_err();
|
1738 | 1763 | }
|
| 1764 | + |
| 1765 | + #[test] |
| 1766 | + fn test_find_derivation_index_for_spk() { |
| 1767 | + let secp = secp256k1::Secp256k1::verification_only(); |
| 1768 | + let descriptor = Descriptor::from_str("tr([73c5da0a/86'/0'/0']xpub6BgBgsespWvERF3LHQu6CnqdvfEvtMcQjYrcRzx53QJjSxarj2afYWcLteoGVky7D3UKDP9QyrLprQ3VCECoY49yfdDEHGCtMMj92pReUsQ/0/*)").unwrap(); |
| 1769 | + let script_at_0_1 = Script::from_str( |
| 1770 | + "5120a82f29944d65b86ae6b5e5cc75e294ead6c59391a1edc5e016e3498c67fc7bbb", |
| 1771 | + ) |
| 1772 | + .unwrap(); |
| 1773 | + let expected_concrete = Descriptor::from_str( |
| 1774 | + "tr(0283dfe85a3151d2517290da461fe2815591ef69f2b18a2ce63f01697a8b313145)", |
| 1775 | + ) |
| 1776 | + .unwrap(); |
| 1777 | + |
| 1778 | + assert_eq!( |
| 1779 | + descriptor.find_derivation_index_for_spk(&secp, &script_at_0_1, 0..1), |
| 1780 | + Ok(None) |
| 1781 | + ); |
| 1782 | + assert_eq!( |
| 1783 | + descriptor.find_derivation_index_for_spk(&secp, &script_at_0_1, 0..2), |
| 1784 | + Ok(Some((1, expected_concrete.clone()))) |
| 1785 | + ); |
| 1786 | + assert_eq!( |
| 1787 | + descriptor.find_derivation_index_for_spk(&secp, &script_at_0_1, 0..10), |
| 1788 | + Ok(Some((1, expected_concrete))) |
| 1789 | + ); |
| 1790 | + } |
1739 | 1791 | }
|
0 commit comments