Skip to content

Commit cbe744e

Browse files
committed
Add psbt finalizer
1 parent afed38f commit cbe744e

File tree

5 files changed

+127
-33
lines changed

5 files changed

+127
-33
lines changed

src/lib.rs

+3
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,9 @@ pub use miniscript::satisfy::{
131131
};
132132
pub use miniscript::Miniscript;
133133

134+
// Use schnorr pubkey as Xonlykey
135+
pub(crate) use bitcoin::schnorr::PublicKey as XOnlyPubKey;
136+
134137
///Public key trait which can be converted to Hash type
135138
pub trait MiniscriptKey: Clone + Eq + Ord + fmt::Debug + fmt::Display + hash::Hash {
136139
/// Check if the publicKey is uncompressed. The default

src/miniscript/satisfy.rs

+12-20
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
//! scriptpubkeys.
1919
//!
2020
21-
use std::collections::HashMap;
21+
use std::collections::{BTreeMap, HashMap};
2222
use std::sync::Arc;
2323
use std::{cmp, i64, mem};
2424

@@ -147,11 +147,9 @@ pub trait Satisfier<Pk: MiniscriptKey + ToPublicKey> {
147147
}
148148

149149
/// Obtain a reference to the control block for a ver and script
150-
fn lookup_tap_control_block(
150+
fn lookup_tap_control_block_map(
151151
&self,
152-
_ver: LeafVersion,
153-
_script: &bitcoin::Script,
154-
) -> Option<&ControlBlock> {
152+
) -> Option<&BTreeMap<ControlBlock, (bitcoin::Script, LeafVersion)>> {
155153
None
156154
}
157155

@@ -335,12 +333,10 @@ impl<'a, Pk: MiniscriptKey + ToPublicKey, S: Satisfier<Pk>> Satisfier<Pk> for &'
335333
(**self).lookup_pkh_tap_leaf_script_sig(pkh)
336334
}
337335

338-
fn lookup_tap_control_block(
336+
fn lookup_tap_control_block_map(
339337
&self,
340-
ver: LeafVersion,
341-
script: &bitcoin::Script,
342-
) -> Option<&ControlBlock> {
343-
(**self).lookup_tap_control_block(ver, script)
338+
) -> Option<&BTreeMap<ControlBlock, (bitcoin::Script, LeafVersion)>> {
339+
(**self).lookup_tap_control_block_map()
344340
}
345341

346342
fn lookup_sha256(&self, h: sha256::Hash) -> Option<Preimage32> {
@@ -396,12 +392,10 @@ impl<'a, Pk: MiniscriptKey + ToPublicKey, S: Satisfier<Pk>> Satisfier<Pk> for &'
396392
(**self).lookup_pkh_tap_leaf_script_sig(pkh)
397393
}
398394

399-
fn lookup_tap_control_block(
395+
fn lookup_tap_control_block_map(
400396
&self,
401-
ver: LeafVersion,
402-
script: &bitcoin::Script,
403-
) -> Option<&ControlBlock> {
404-
(**self).lookup_tap_control_block(ver, script)
397+
) -> Option<&BTreeMap<ControlBlock, (bitcoin::Script, LeafVersion)>> {
398+
(**self).lookup_tap_control_block_map()
405399
}
406400

407401
fn lookup_sha256(&self, h: sha256::Hash) -> Option<Preimage32> {
@@ -506,14 +500,12 @@ macro_rules! impl_tuple_satisfier {
506500
None
507501
}
508502

509-
fn lookup_tap_control_block(
503+
fn lookup_tap_control_block_map(
510504
&self,
511-
ver: LeafVersion,
512-
script: &bitcoin::Script,
513-
) -> Option<&ControlBlock> {
505+
) -> Option<&BTreeMap<ControlBlock, (bitcoin::Script, LeafVersion)>> {
514506
let &($(ref $ty,)*) = self;
515507
$(
516-
if let Some(result) = $ty.lookup_tap_control_block(ver, script) {
508+
if let Some(result) = $ty.lookup_tap_control_block_map() {
517509
return Some(result);
518510
}
519511
)*

src/psbt/finalizer.rs

+90-13
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,72 @@
1919
//! `https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki`
2020
//!
2121
22+
use util::{script_is_v1_tr, witness_size};
23+
2224
use super::{sanity_check, Psbt};
2325
use super::{Error, InputError, PsbtInputSatisfier};
2426
use bitcoin::secp256k1::{self, Secp256k1};
27+
use bitcoin::util::taproot::LeafVersion;
2528
use bitcoin::{self, PublicKey, Script};
2629
use descriptor::DescriptorTrait;
2730
use interpreter;
2831
use Descriptor;
2932
use Miniscript;
30-
use {BareCtx, Legacy, Segwitv0};
33+
use Satisfier;
34+
use XOnlyPubKey;
35+
use {BareCtx, Legacy, Segwitv0, Tap};
36+
37+
// Satisfy the taproot descriptor. It is not possible to infer the complete
38+
// descriptor from psbt because the information about all the scripts might not
39+
// be present. Also, currently the spec does not support hidden branches, so
40+
// inferring a descriptor is not possible
41+
fn construct_tap_witness(
42+
spk: &Script,
43+
sat: &PsbtInputSatisfier,
44+
) -> Result<Vec<Vec<u8>>, InputError> {
45+
assert!(script_is_v1_tr(&spk));
46+
47+
// try the script spend path first
48+
if let Some(sig) = <PsbtInputSatisfier as Satisfier<XOnlyPubKey>>::lookup_tap_key_spend_sig(sat)
49+
{
50+
return Ok(vec![sig.serialize()]);
51+
}
52+
// Next script spends
53+
let (mut min_wit, mut min_wit_len) = (None, None);
54+
if let Some(block_map) =
55+
<PsbtInputSatisfier as Satisfier<XOnlyPubKey>>::lookup_tap_control_block_map(sat)
56+
{
57+
for (control_block, (script, ver)) in block_map {
58+
if *ver != LeafVersion::default() {
59+
// We don't know how to satisfy non default version scripts yet
60+
continue;
61+
}
62+
let ms = match Miniscript::<XOnlyPubKey, Tap>::parse_insane(script) {
63+
Ok(ms) => ms,
64+
Err(..) => continue, // try another script
65+
};
66+
let mut wit = match ms.satisfy(sat) {
67+
Ok(ms) => ms,
68+
Err(..) => continue,
69+
};
70+
wit.push(ms.encode().into_bytes());
71+
wit.push(control_block.serialize());
72+
let wit_len = Some(witness_size(&wit));
73+
if min_wit_len.is_some() && wit_len > min_wit_len {
74+
continue;
75+
} else {
76+
// store the minimum
77+
min_wit = Some(wit);
78+
min_wit_len = wit_len;
79+
}
80+
}
81+
min_wit.ok_or(InputError::CouldNotSatisfyTr)
82+
} else {
83+
// No control blocks found
84+
Err(InputError::CouldNotSatisfyTr)
85+
}
86+
}
87+
3188
// Get the scriptpubkey for the psbt input
3289
fn get_scriptpubkey(psbt: &Psbt, index: usize) -> Result<&Script, InputError> {
3390
let script_pubkey;
@@ -301,13 +358,21 @@ pub fn finalize<C: secp256k1::Verification>(
301358

302359
// Actually construct the witnesses
303360
for index in 0..psbt.inputs.len() {
304-
// Get a descriptor for this input
305-
let desc = get_descriptor(&psbt, index).map_err(|e| Error::InputError(e, index))?;
361+
let spk = get_scriptpubkey(psbt, index).map_err(|e| Error::InputError(e, index))?;
362+
let sat = PsbtInputSatisfier::new(&psbt, index);
306363

307-
//generate the satisfaction witness and scriptsig
308-
let (witness, script_sig) = desc
309-
.get_satisfaction(PsbtInputSatisfier::new(&psbt, index))
310-
.map_err(|e| Error::InputError(InputError::MiniscriptError(e), index))?;
364+
let (witness, script_sig) = if script_is_v1_tr(spk) {
365+
// Deal with tr case separately, unfortunately we cannot infer the full descriptor for Tr
366+
let wit = construct_tap_witness(spk, &sat).map_err(|e| Error::InputError(e, index))?;
367+
(wit, Script::new())
368+
} else {
369+
// Get a descriptor for this input.
370+
let desc = get_descriptor(&psbt, index).map_err(|e| Error::InputError(e, index))?;
371+
372+
//generate the satisfaction witness and scriptsig
373+
desc.get_satisfaction(&sat)
374+
.map_err(|e| Error::InputError(InputError::MiniscriptError(e), index))?
375+
};
311376

312377
let input = &mut psbt.inputs[index];
313378
//Fill in the satisfactions
@@ -322,12 +387,24 @@ pub fn finalize<C: secp256k1::Verification>(
322387
Some(witness)
323388
};
324389
//reset everything
325-
input.redeem_script = None;
326-
input.partial_sigs.clear();
327-
input.sighash_type = None;
328-
input.redeem_script = None;
329-
input.bip32_derivation.clear();
330-
input.witness_script = None;
390+
input.partial_sigs.clear(); // 0x02
391+
input.sighash_type = None; // 0x03
392+
input.redeem_script = None; // 0x04
393+
input.witness_script = None; // 0x05
394+
input.bip32_derivation.clear(); // 0x05
395+
// finalized witness 0x06 and 0x07 are not clear
396+
// 0x09 Proof of reserves not yet supported
397+
input.ripemd160_preimages.clear(); // 0x0a
398+
input.sha256_preimages.clear(); // 0x0b
399+
input.hash160_preimages.clear(); // 0x0c
400+
input.hash256_preimages.clear(); // 0x0d
401+
// psbt v2 fields till 0x012 not supported
402+
input.tap_key_sig = None; // 0x013
403+
input.tap_script_sigs.clear(); // 0x014
404+
input.tap_scripts.clear(); // 0x015
405+
input.tap_key_origins.clear(); // 0x16
406+
input.tap_internal_key = None; // x017
407+
input.tap_merkle_root = None; // 0x018
331408
}
332409
// Double check everything with the interpreter
333410
// This only checks whether the script will be executed

src/psbt/mod.rs

+8
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,11 @@ pub enum InputError {
4444
SecpErr(bitcoin::secp256k1::Error),
4545
/// Key errors
4646
KeyErr(bitcoin::util::key::Error),
47+
/// Could not satisfy taproot descriptor
48+
/// This error is returned when both script path and key paths could not be
49+
/// satisfied. We cannot return a detailed error because we try all miniscripts
50+
/// in script spend path, we cannot know which miniscript failed.
51+
CouldNotSatisfyTr,
4752
/// Error doing an interpreter-check on a finalized psbt
4853
Interpreter(interpreter::Error),
4954
/// Redeem script does not match the p2sh hash
@@ -159,6 +164,9 @@ impl fmt::Display for InputError {
159164
sighashflag {:?} rather than required {:?}",
160165
pubkey.key, got, required
161166
),
167+
InputError::CouldNotSatisfyTr => {
168+
write!(f, "Could not satisfy Tr descriptor")
169+
}
162170
}
163171
}
164172
}

src/util.rs

+14
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
use bitcoin;
2+
use bitcoin::blockdata::opcodes;
23
use bitcoin::blockdata::script;
4+
use bitcoin::blockdata::script::Instruction;
35
use bitcoin::Script;
46

57
use {ScriptContext, ToPublicKey};
@@ -46,3 +48,15 @@ impl MsKeyBuilder for script::Builder {
4648
}
4749
}
4850
}
51+
52+
// This belongs in rust-bitcoin, make this upstream later
53+
pub(crate) fn script_is_v1_tr(s: &Script) -> bool {
54+
let mut iter = s.instructions_minimal();
55+
if s.len() != 32
56+
|| iter.next() != Some(Ok(Instruction::Op(opcodes::all::OP_PUSHNUM_1)))
57+
|| iter.next() != Some(Ok(Instruction::Op(opcodes::all::OP_PUSHBYTES_32)))
58+
{
59+
return false;
60+
}
61+
return true;
62+
}

0 commit comments

Comments
 (0)