Skip to content

Commit 965a136

Browse files
committed
Merge #339: Add PsbtInputExt::update_with_descriptor
f40dc83 fixup! Add find_derivation_index_for_spk (LLFourn) 3374e11 fixup! Add segwit_version to DescriptorType (LLFourn) e85b99c Add segwit_version to DescriptorType (LLFourn) cb0dd48 Add PsbtInputExt::update_with_descriptor (LLFourn) f3198dc Add find_derivation_index_for_spk (LLFourn) Pull request description: Fixes #335 This populates all relevant fields in a PSBT input according to a descriptor. Includes previously not yet implemented logic for populating `bip32_derivation` and `tap_key_origins`. Also optionally checks the `witness_utxo` and `non_witness_utxo`. I renamed `PsbtExt::update_desc` to `PsbtExt::update_inp_with_descriptor` which calls `update_with_descriptor` internally but retrieves the vout for the input for the `non_witness_utxo` check automatically. I removed the derivation range functionality because it didn't feel useful (and if it is then it probably belongs in a separate function). Let me know if you want me to add it back where it was or to provide an additional utility function to find which index a script pubkey was derived from. Top commit has no ACKs. Tree-SHA512: 03a89a0aaaa3e3ef906537101e364fbbc8fc925872dbd8345e405383d307abbed5d6155967e34bae1b10451a170a6b1339ad507abf4943f80a2f5a6513b87783
2 parents dcc9d0f + f40dc83 commit 965a136

File tree

3 files changed

+585
-107
lines changed

3 files changed

+585
-107
lines changed

integration_test/src/test_desc.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use bitcoin::util::{psbt, sighash};
1313
use bitcoin::{self, Amount, OutPoint, SchnorrSig, Script, Transaction, TxIn, TxOut, Txid};
1414
use bitcoincore_rpc::{json, Client, RpcApi};
1515
use miniscript::miniscript::iter;
16-
use miniscript::psbt::PsbtExt;
16+
use miniscript::psbt::{PsbtInputExt, PsbtExt};
1717
use miniscript::{Descriptor, DescriptorTrait, Miniscript, ToPublicKey};
1818
use miniscript::{MiniscriptKey, ScriptContext};
1919
use std::collections::BTreeMap;
@@ -109,10 +109,10 @@ pub fn test_desc_satisfy(cl: &Client, testdata: &TestData, desc: &str) -> Witnes
109109
script_pubkey: addr.script_pubkey(),
110110
});
111111
let mut input = psbt::Input::default();
112+
input.update_with_descriptor_unchecked(&desc).unwrap();
112113
input.witness_utxo = Some(witness_utxo.clone());
113114
psbt.inputs.push(input);
114115
psbt.outputs.push(psbt::Output::default());
115-
psbt.update_desc(0, &desc, None).unwrap();
116116

117117
// --------------------------------------------
118118
// Sign the transactions with all keys

src/descriptor/mod.rs

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,15 @@
2323
//! these with BIP32 paths, pay-to-contract instructions, etc.
2424
//!
2525
26+
use std::ops::Range;
2627
use std::{collections::HashMap, sync::Arc};
2728
use std::{
2829
fmt,
2930
str::{self, FromStr},
3031
};
3132

3233
use bitcoin::blockdata::witness::Witness;
34+
use bitcoin::util::address::WitnessVersion;
3335
use bitcoin::{self, secp256k1, Script};
3436

3537
use self::checksum::verify_checksum;
@@ -259,6 +261,22 @@ pub enum DescriptorType {
259261
Tr,
260262
}
261263

264+
impl DescriptorType {
265+
/// Returns the segwit version implied by the descriptor type.
266+
///
267+
/// This will return `Some(WitnessVersion::V0)` whether it is "native" segwitv0 or "wrapped" p2sh segwit.
268+
pub fn segwit_version(&self) -> Option<WitnessVersion> {
269+
use self::DescriptorType::*;
270+
match self {
271+
Tr => Some(WitnessVersion::V1),
272+
Wpkh | ShWpkh | Wsh | ShWsh | ShWshSortedMulti | WshSortedMulti => {
273+
Some(WitnessVersion::V0)
274+
}
275+
Bare | Sh | Pkh | ShSortedMulti => None,
276+
}
277+
}
278+
}
279+
262280
impl<Pk: MiniscriptKey> Descriptor<Pk> {
263281
// Keys
264282

@@ -749,6 +767,31 @@ impl Descriptor<DescriptorPublicKey> {
749767

750768
descriptor.to_string()
751769
}
770+
771+
/// Utility method for deriving the descriptor at each index in a range to find one matching
772+
/// `script_pubkey`.
773+
///
774+
/// If it finds a match then it returns the index it was derived at and the concrete
775+
/// descriptor at that index. If the descriptor is non-derivable then it will simply check the
776+
/// script pubkey against the descriptor and return it if it matches (in this case the index
777+
/// returned will be meaningless).
778+
pub fn find_derivation_index_for_spk<C: secp256k1::Verification>(
779+
&self,
780+
secp: &secp256k1::Secp256k1<C>,
781+
script_pubkey: &Script,
782+
range: Range<u32>,
783+
) -> Result<Option<(u32, Descriptor<bitcoin::PublicKey>)>, ConversionError> {
784+
let range = if self.is_deriveable() { range } else { 0..1 };
785+
786+
for i in range {
787+
let concrete = self.derived_descriptor(&secp, i)?;
788+
if &concrete.script_pubkey() == script_pubkey {
789+
return Ok(Some((i, concrete)));
790+
}
791+
}
792+
793+
Ok(None)
794+
}
752795
}
753796

754797
impl<Pk> expression::FromTree for Descriptor<Pk>
@@ -1736,4 +1779,31 @@ pk(03f28773c2d975288bc7d1d205c3748651b075fbc6610e58cddeeddf8f19405aa8))";
17361779
Descriptor::<DescriptorPublicKey>::from_str(&format!("wsh(pk({}))", x_only_key))
17371780
.unwrap_err();
17381781
}
1782+
1783+
#[test]
1784+
fn test_find_derivation_index_for_spk() {
1785+
let secp = secp256k1::Secp256k1::verification_only();
1786+
let descriptor = Descriptor::from_str("tr([73c5da0a/86'/0'/0']xpub6BgBgsespWvERF3LHQu6CnqdvfEvtMcQjYrcRzx53QJjSxarj2afYWcLteoGVky7D3UKDP9QyrLprQ3VCECoY49yfdDEHGCtMMj92pReUsQ/0/*)").unwrap();
1787+
let script_at_0_1 = Script::from_str(
1788+
"5120a82f29944d65b86ae6b5e5cc75e294ead6c59391a1edc5e016e3498c67fc7bbb",
1789+
)
1790+
.unwrap();
1791+
let expected_concrete = Descriptor::from_str(
1792+
"tr(0283dfe85a3151d2517290da461fe2815591ef69f2b18a2ce63f01697a8b313145)",
1793+
)
1794+
.unwrap();
1795+
1796+
assert_eq!(
1797+
descriptor.find_derivation_index_for_spk(&secp, &script_at_0_1, 0..1),
1798+
Ok(None)
1799+
);
1800+
assert_eq!(
1801+
descriptor.find_derivation_index_for_spk(&secp, &script_at_0_1, 0..2),
1802+
Ok(Some((1, expected_concrete.clone())))
1803+
);
1804+
assert_eq!(
1805+
descriptor.find_derivation_index_for_spk(&secp, &script_at_0_1, 0..10),
1806+
Ok(Some((1, expected_concrete)))
1807+
);
1808+
}
17391809
}

0 commit comments

Comments
 (0)