Skip to content

Commit

Permalink
Add psbt finalizer
Browse files Browse the repository at this point in the history
  • Loading branch information
sanket1729 committed Nov 1, 2021
1 parent afed38f commit cbe744e
Show file tree
Hide file tree
Showing 5 changed files with 127 additions and 33 deletions.
3 changes: 3 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,9 @@ pub use miniscript::satisfy::{
};
pub use miniscript::Miniscript;

// Use schnorr pubkey as Xonlykey
pub(crate) use bitcoin::schnorr::PublicKey as XOnlyPubKey;

///Public key trait which can be converted to Hash type
pub trait MiniscriptKey: Clone + Eq + Ord + fmt::Debug + fmt::Display + hash::Hash {
/// Check if the publicKey is uncompressed. The default
Expand Down
32 changes: 12 additions & 20 deletions src/miniscript/satisfy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
//! scriptpubkeys.
//!

use std::collections::HashMap;
use std::collections::{BTreeMap, HashMap};
use std::sync::Arc;
use std::{cmp, i64, mem};

Expand Down Expand Up @@ -147,11 +147,9 @@ pub trait Satisfier<Pk: MiniscriptKey + ToPublicKey> {
}

/// Obtain a reference to the control block for a ver and script
fn lookup_tap_control_block(
fn lookup_tap_control_block_map(
&self,
_ver: LeafVersion,
_script: &bitcoin::Script,
) -> Option<&ControlBlock> {
) -> Option<&BTreeMap<ControlBlock, (bitcoin::Script, LeafVersion)>> {
None
}

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

fn lookup_tap_control_block(
fn lookup_tap_control_block_map(
&self,
ver: LeafVersion,
script: &bitcoin::Script,
) -> Option<&ControlBlock> {
(**self).lookup_tap_control_block(ver, script)
) -> Option<&BTreeMap<ControlBlock, (bitcoin::Script, LeafVersion)>> {
(**self).lookup_tap_control_block_map()
}

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

fn lookup_tap_control_block(
fn lookup_tap_control_block_map(
&self,
ver: LeafVersion,
script: &bitcoin::Script,
) -> Option<&ControlBlock> {
(**self).lookup_tap_control_block(ver, script)
) -> Option<&BTreeMap<ControlBlock, (bitcoin::Script, LeafVersion)>> {
(**self).lookup_tap_control_block_map()
}

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

fn lookup_tap_control_block(
fn lookup_tap_control_block_map(
&self,
ver: LeafVersion,
script: &bitcoin::Script,
) -> Option<&ControlBlock> {
) -> Option<&BTreeMap<ControlBlock, (bitcoin::Script, LeafVersion)>> {
let &($(ref $ty,)*) = self;
$(
if let Some(result) = $ty.lookup_tap_control_block(ver, script) {
if let Some(result) = $ty.lookup_tap_control_block_map() {
return Some(result);
}
)*
Expand Down
103 changes: 90 additions & 13 deletions src/psbt/finalizer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,72 @@
//! `https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki`
//!

use util::{script_is_v1_tr, witness_size};

use super::{sanity_check, Psbt};
use super::{Error, InputError, PsbtInputSatisfier};
use bitcoin::secp256k1::{self, Secp256k1};
use bitcoin::util::taproot::LeafVersion;
use bitcoin::{self, PublicKey, Script};
use descriptor::DescriptorTrait;
use interpreter;
use Descriptor;
use Miniscript;
use {BareCtx, Legacy, Segwitv0};
use Satisfier;
use XOnlyPubKey;
use {BareCtx, Legacy, Segwitv0, Tap};

// Satisfy the taproot descriptor. It is not possible to infer the complete
// descriptor from psbt because the information about all the scripts might not
// be present. Also, currently the spec does not support hidden branches, so
// inferring a descriptor is not possible
fn construct_tap_witness(
spk: &Script,
sat: &PsbtInputSatisfier,
) -> Result<Vec<Vec<u8>>, InputError> {
assert!(script_is_v1_tr(&spk));

// try the script spend path first
if let Some(sig) = <PsbtInputSatisfier as Satisfier<XOnlyPubKey>>::lookup_tap_key_spend_sig(sat)
{
return Ok(vec![sig.serialize()]);
}
// Next script spends
let (mut min_wit, mut min_wit_len) = (None, None);
if let Some(block_map) =
<PsbtInputSatisfier as Satisfier<XOnlyPubKey>>::lookup_tap_control_block_map(sat)
{
for (control_block, (script, ver)) in block_map {
if *ver != LeafVersion::default() {
// We don't know how to satisfy non default version scripts yet
continue;
}
let ms = match Miniscript::<XOnlyPubKey, Tap>::parse_insane(script) {
Ok(ms) => ms,
Err(..) => continue, // try another script
};
let mut wit = match ms.satisfy(sat) {
Ok(ms) => ms,
Err(..) => continue,
};
wit.push(ms.encode().into_bytes());
wit.push(control_block.serialize());
let wit_len = Some(witness_size(&wit));
if min_wit_len.is_some() && wit_len > min_wit_len {
continue;
} else {
// store the minimum
min_wit = Some(wit);
min_wit_len = wit_len;
}
}
min_wit.ok_or(InputError::CouldNotSatisfyTr)
} else {
// No control blocks found
Err(InputError::CouldNotSatisfyTr)
}
}

// Get the scriptpubkey for the psbt input
fn get_scriptpubkey(psbt: &Psbt, index: usize) -> Result<&Script, InputError> {
let script_pubkey;
Expand Down Expand Up @@ -301,13 +358,21 @@ pub fn finalize<C: secp256k1::Verification>(

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

//generate the satisfaction witness and scriptsig
let (witness, script_sig) = desc
.get_satisfaction(PsbtInputSatisfier::new(&psbt, index))
.map_err(|e| Error::InputError(InputError::MiniscriptError(e), index))?;
let (witness, script_sig) = if script_is_v1_tr(spk) {
// Deal with tr case separately, unfortunately we cannot infer the full descriptor for Tr
let wit = construct_tap_witness(spk, &sat).map_err(|e| Error::InputError(e, index))?;
(wit, Script::new())
} else {
// Get a descriptor for this input.
let desc = get_descriptor(&psbt, index).map_err(|e| Error::InputError(e, index))?;

//generate the satisfaction witness and scriptsig
desc.get_satisfaction(&sat)
.map_err(|e| Error::InputError(InputError::MiniscriptError(e), index))?
};

let input = &mut psbt.inputs[index];
//Fill in the satisfactions
Expand All @@ -322,12 +387,24 @@ pub fn finalize<C: secp256k1::Verification>(
Some(witness)
};
//reset everything
input.redeem_script = None;
input.partial_sigs.clear();
input.sighash_type = None;
input.redeem_script = None;
input.bip32_derivation.clear();
input.witness_script = None;
input.partial_sigs.clear(); // 0x02
input.sighash_type = None; // 0x03
input.redeem_script = None; // 0x04
input.witness_script = None; // 0x05
input.bip32_derivation.clear(); // 0x05
// finalized witness 0x06 and 0x07 are not clear
// 0x09 Proof of reserves not yet supported
input.ripemd160_preimages.clear(); // 0x0a
input.sha256_preimages.clear(); // 0x0b
input.hash160_preimages.clear(); // 0x0c
input.hash256_preimages.clear(); // 0x0d
// psbt v2 fields till 0x012 not supported
input.tap_key_sig = None; // 0x013
input.tap_script_sigs.clear(); // 0x014
input.tap_scripts.clear(); // 0x015
input.tap_key_origins.clear(); // 0x16
input.tap_internal_key = None; // x017
input.tap_merkle_root = None; // 0x018
}
// Double check everything with the interpreter
// This only checks whether the script will be executed
Expand Down
8 changes: 8 additions & 0 deletions src/psbt/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,11 @@ pub enum InputError {
SecpErr(bitcoin::secp256k1::Error),
/// Key errors
KeyErr(bitcoin::util::key::Error),
/// Could not satisfy taproot descriptor
/// This error is returned when both script path and key paths could not be
/// satisfied. We cannot return a detailed error because we try all miniscripts
/// in script spend path, we cannot know which miniscript failed.
CouldNotSatisfyTr,
/// Error doing an interpreter-check on a finalized psbt
Interpreter(interpreter::Error),
/// Redeem script does not match the p2sh hash
Expand Down Expand Up @@ -159,6 +164,9 @@ impl fmt::Display for InputError {
sighashflag {:?} rather than required {:?}",
pubkey.key, got, required
),
InputError::CouldNotSatisfyTr => {
write!(f, "Could not satisfy Tr descriptor")
}
}
}
}
Expand Down
14 changes: 14 additions & 0 deletions src/util.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
use bitcoin;
use bitcoin::blockdata::opcodes;
use bitcoin::blockdata::script;
use bitcoin::blockdata::script::Instruction;
use bitcoin::Script;

use {ScriptContext, ToPublicKey};
Expand Down Expand Up @@ -46,3 +48,15 @@ impl MsKeyBuilder for script::Builder {
}
}
}

// This belongs in rust-bitcoin, make this upstream later
pub(crate) fn script_is_v1_tr(s: &Script) -> bool {
let mut iter = s.instructions_minimal();
if s.len() != 32
|| iter.next() != Some(Ok(Instruction::Op(opcodes::all::OP_PUSHNUM_1)))
|| iter.next() != Some(Ok(Instruction::Op(opcodes::all::OP_PUSHBYTES_32)))
{
return false;
}
return true;
}

0 comments on commit cbe744e

Please sign in to comment.