Taproot-related methods for Script type

This commit is contained in:
Dr Maxim Orlovsky 2021-11-12 23:55:28 +01:00
parent 599c5f9488
commit 91b68a468d
1 changed files with 44 additions and 4 deletions

View File

@ -41,6 +41,9 @@ use policy::DUST_RELAY_TX_FEE;
use util::ecdsa::PublicKey; use util::ecdsa::PublicKey;
use util::address::WitnessVersion; use util::address::WitnessVersion;
use util::taproot::{LeafVersion, TapBranchHash, TapLeafHash};
use secp256k1::{Secp256k1, Verification};
use schnorr::{TapTweak, TweakedPublicKey, UntweakedPublicKey};
/// A Bitcoin script. /// A Bitcoin script.
#[derive(Clone, Default, PartialOrd, Ord, PartialEq, Eq, Hash)] #[derive(Clone, Default, PartialOrd, Ord, PartialEq, Eq, Hash)]
@ -313,12 +316,27 @@ impl Script {
/// Generates P2WPKH-type of scriptPubkey /// Generates P2WPKH-type of scriptPubkey
pub fn new_v0_wpkh(pubkey_hash: &WPubkeyHash) -> Script { pub fn new_v0_wpkh(pubkey_hash: &WPubkeyHash) -> Script {
Script::new_witness_program(WitnessVersion::V0, &pubkey_hash.to_vec()) Script::new_witness_program(WitnessVersion::V0, &pubkey_hash[..])
} }
/// Generates P2WSH-type of scriptPubkey with a given hash of the redeem script /// Generates P2WSH-type of scriptPubkey with a given hash of the redeem script
pub fn new_v0_wsh(script_hash: &WScriptHash) -> Script { pub fn new_v0_wsh(script_hash: &WScriptHash) -> Script {
Script::new_witness_program(WitnessVersion::V0, &script_hash.to_vec()) Script::new_witness_program(WitnessVersion::V0, &script_hash[..])
}
/// Generates P2TR for script spending path using an internal public key and some optional
/// script tree merkle root.
pub fn new_v1_p2tr<C: Verification>(secp: &Secp256k1<C>, internal_key: UntweakedPublicKey, merkle_root: Option<TapBranchHash>) -> Script {
let (output_key, _) = internal_key.tap_tweak(secp, merkle_root);
Script::new_witness_program(WitnessVersion::V1, &output_key.serialize())
}
/// Generates P2TR for key spending path for a known [`TweakedPublicKey`].
///
/// NB: Make sure that the used key is indeed tweaked (for instance, it comes from `rawtr`
/// descriptor content); otherwise please use [`Script::new_v1_p2tr`] method.
pub fn new_v1_p2tr_tweaked(output_key: TweakedPublicKey) -> Script {
Script::new_witness_program(WitnessVersion::V1, &output_key.serialize())
} }
/// Generates P2WSH-type of scriptPubkey with a given hash of the redeem script /// Generates P2WSH-type of scriptPubkey with a given hash of the redeem script
@ -373,6 +391,20 @@ impl Script {
Script::new_v0_wsh(&self.wscript_hash()) Script::new_v0_wsh(&self.wscript_hash())
} }
/// Compute P2TR output with a given internal key and a single script spending path equal to the
/// current script, assuming that the script is a Tapscript
#[inline]
pub fn to_v1_p2tr<C: Verification>(&self, secp: &Secp256k1<C>, internal_key: UntweakedPublicKey) -> Script {
let leaf_hash = TapLeafHash::from_script(&self, LeafVersion::TapScript);
let merkle_root = TapBranchHash::from_inner(leaf_hash.into_inner());
Script::new_v1_p2tr(&secp, internal_key, Some(merkle_root))
}
#[inline]
fn witness_version(&self) -> Option<WitnessVersion> {
WitnessVersion::from_opcode(self.0[0].into()).ok()
}
/// Checks whether a script pubkey is a p2sh output /// Checks whether a script pubkey is a p2sh output
#[inline] #[inline]
pub fn is_p2sh(&self) -> bool { pub fn is_p2sh(&self) -> bool {
@ -428,7 +460,7 @@ impl Script {
#[inline] #[inline]
pub fn is_v0_p2wsh(&self) -> bool { pub fn is_v0_p2wsh(&self) -> bool {
self.0.len() == 34 && self.0.len() == 34 &&
self.0[0] == opcodes::all::OP_PUSHBYTES_0.into_u8() && self.witness_version() == Some(WitnessVersion::V0) &&
self.0[1] == opcodes::all::OP_PUSHBYTES_32.into_u8() self.0[1] == opcodes::all::OP_PUSHBYTES_32.into_u8()
} }
@ -436,10 +468,18 @@ impl Script {
#[inline] #[inline]
pub fn is_v0_p2wpkh(&self) -> bool { pub fn is_v0_p2wpkh(&self) -> bool {
self.0.len() == 22 && self.0.len() == 22 &&
self.0[0] == opcodes::all::OP_PUSHBYTES_0.into_u8() && self.witness_version() == Some(WitnessVersion::V0) &&
self.0[1] == opcodes::all::OP_PUSHBYTES_20.into_u8() self.0[1] == opcodes::all::OP_PUSHBYTES_20.into_u8()
} }
/// Checks whether a script pubkey is a P2TR output
#[inline]
pub fn is_v1_p2tr(&self) -> bool {
self.0.len() == 34 &&
self.witness_version() == Some(WitnessVersion::V1) &&
self.0[1] == opcodes::all::OP_PUSHBYTES_32.into_u8()
}
/// Check if this is an OP_RETURN output /// Check if this is an OP_RETURN output
pub fn is_op_return (&self) -> bool { pub fn is_op_return (&self) -> bool {
!self.0.is_empty() && (opcodes::All::from(self.0[0]) == opcodes::all::OP_RETURN) !self.0.is_empty() && (opcodes::All::from(self.0[0]) == opcodes::all::OP_RETURN)