|
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,
|
@@ -762,6 +763,30 @@ impl Descriptor<DescriptorPublicKey> {
|
762 | 763 |
|
763 | 764 | descriptor.to_string()
|
764 | 765 | }
|
| 766 | + |
| 767 | + /// Utility method for deriving the descriptor at each index in a range to find one matching |
| 768 | + /// `script_pubkey`. |
| 769 | + /// |
| 770 | + /// If it finds a match then it returns the index it was derived it and the concrete descriptor |
| 771 | + /// at that index. If the descriptor is non-derivable then it will simply check the script |
| 772 | + /// pubkey against the descriptor (and in that case the index returned will be meaningless). |
| 773 | + pub fn find_derivation_index_for_spk<C: secp256k1::Verification>( |
| 774 | + &self, |
| 775 | + secp: &secp256k1::Secp256k1<C>, |
| 776 | + script_pubkey: &Script, |
| 777 | + range: Range<u32>, |
| 778 | + ) -> Result<Option<(u32, Descriptor<bitcoin::PublicKey>)>, ConversionError> { |
| 779 | + let range = if self.is_deriveable() { range } else { 0..1 }; |
| 780 | + |
| 781 | + for i in range { |
| 782 | + let concrete = self.derived_descriptor(&secp, i)?; |
| 783 | + if &concrete.script_pubkey() == script_pubkey { |
| 784 | + return Ok(Some((i, concrete))); |
| 785 | + } |
| 786 | + } |
| 787 | + |
| 788 | + Ok(None) |
| 789 | + } |
765 | 790 | }
|
766 | 791 |
|
767 | 792 | impl<Pk> expression::FromTree for Descriptor<Pk>
|
@@ -1734,4 +1759,31 @@ pk(03f28773c2d975288bc7d1d205c3748651b075fbc6610e58cddeeddf8f19405aa8))";
|
1734 | 1759 | let descriptor: Descriptor<DescriptorPublicKey> = descriptor_str.parse().unwrap();
|
1735 | 1760 | assert_eq!(descriptor.to_string(), "sh(wsh(pk(xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL)))#6c6hwr22");
|
1736 | 1761 | }
|
| 1762 | + |
| 1763 | + #[test] |
| 1764 | + fn test_find_derivation_index_for_spk() { |
| 1765 | + let secp = secp256k1::Secp256k1::verification_only(); |
| 1766 | + let descriptor = Descriptor::from_str("tr([73c5da0a/86'/0'/0']xpub6BgBgsespWvERF3LHQu6CnqdvfEvtMcQjYrcRzx53QJjSxarj2afYWcLteoGVky7D3UKDP9QyrLprQ3VCECoY49yfdDEHGCtMMj92pReUsQ/0/*)").unwrap(); |
| 1767 | + let script_at_0_1 = Script::from_str( |
| 1768 | + "5120a82f29944d65b86ae6b5e5cc75e294ead6c59391a1edc5e016e3498c67fc7bbb", |
| 1769 | + ) |
| 1770 | + .unwrap(); |
| 1771 | + let expected_concrete = Descriptor::from_str( |
| 1772 | + "tr(0283dfe85a3151d2517290da461fe2815591ef69f2b18a2ce63f01697a8b313145)", |
| 1773 | + ) |
| 1774 | + .unwrap(); |
| 1775 | + |
| 1776 | + assert_eq!( |
| 1777 | + descriptor.find_derivation_index_for_spk(&secp, &script_at_0_1, 0..1), |
| 1778 | + Ok(None) |
| 1779 | + ); |
| 1780 | + assert_eq!( |
| 1781 | + descriptor.find_derivation_index_for_spk(&secp, &script_at_0_1, 0..2), |
| 1782 | + Ok(Some((1, expected_concrete.clone()))) |
| 1783 | + ); |
| 1784 | + assert_eq!( |
| 1785 | + descriptor.find_derivation_index_for_spk(&secp, &script_at_0_1, 0..10), |
| 1786 | + Ok(Some((1, expected_concrete))) |
| 1787 | + ); |
| 1788 | + } |
1737 | 1789 | }
|
0 commit comments