use std::cmp::Reverse;
use std::{error, io, fmt};
use crate::hashes::{sha256, sha256t_hash_newtype, Hash, HashEngine};
use crate::schnorr::{UntweakedPublicKey, TweakedPublicKey, TapTweak};
use crate::Script;
use std::collections::{BTreeMap, BTreeSet, BinaryHeap};
use secp256k1_zkp::{self, Secp256k1, Scalar};
use crate::encode::Encodable;
sha256t_hash_newtype! {
pub struct TapLeafTag = hash_str("TapLeaf/elements");
#[hash_newtype(forward)]
pub struct TapLeafHash(_);
pub struct TapBranchTag = hash_str("TapBranch/elements");
#[hash_newtype(forward)]
pub struct TapNodeHash(_);
pub struct TapTweakTag = hash_str("TapTweak/elements");
#[hash_newtype(forward)]
pub struct TapTweakHash(_);
pub struct TapSighashTag = hash_str("TapSighash/elements");
#[hash_newtype(forward)]
pub struct TapSighashHash(_);
}
impl TapTweakHash {
pub fn from_key_and_tweak(
internal_key: UntweakedPublicKey,
merkle_root: Option<TapNodeHash>,
) -> TapTweakHash {
let mut eng = TapTweakHash::engine();
eng.input(&internal_key.serialize());
if let Some(h) = merkle_root {
eng.input(h.as_ref());
} else {
}
TapTweakHash::from_engine(eng)
}
pub fn to_scalar(self) -> Scalar {
Scalar::from_be_bytes(self.to_byte_array()).expect("hash value greater than curve order")
}
}
impl TapLeafHash {
pub fn from_script(script: &Script, ver: LeafVersion) -> TapLeafHash {
let mut eng = TapLeafHash::engine();
ver.as_u8()
.consensus_encode(&mut eng)
.expect("engines don't error");
script
.consensus_encode(&mut eng)
.expect("engines don't error");
TapLeafHash::from_engine(eng)
}
}
pub const TAPROOT_CONTROL_MAX_NODE_COUNT: usize = 128;
pub const TAPROOT_CONTROL_NODE_SIZE: usize = 32;
pub const TAPROOT_LEAF_MASK: u8 = 0xfe;
pub const TAPROOT_LEAF_TAPSCRIPT: u8 = 0xc4;
pub const TAPROOT_CONTROL_BASE_SIZE: usize = 33;
pub const TAPROOT_CONTROL_MAX_SIZE: usize =
TAPROOT_CONTROL_BASE_SIZE + TAPROOT_CONTROL_NODE_SIZE * TAPROOT_CONTROL_MAX_NODE_COUNT;
type ScriptMerkleProofMap = BTreeMap<(Script, LeafVersion), BTreeSet<TaprootMerkleBranch>>;
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct TaprootSpendInfo {
internal_key: UntweakedPublicKey,
merkle_root: Option<TapNodeHash>,
output_key_parity: secp256k1_zkp::Parity,
output_key: TweakedPublicKey,
script_map: ScriptMerkleProofMap,
}
impl TaprootSpendInfo {
pub fn with_huffman_tree<C, I>(
secp: &Secp256k1<C>,
internal_key: UntweakedPublicKey,
script_weights: I,
) -> Result<Self, TaprootBuilderError>
where
I: IntoIterator<Item = (u32, Script)>,
C: secp256k1_zkp::Verification,
{
let mut node_weights = BinaryHeap::<(Reverse<u64>, NodeInfo)>::new();
for (p, leaf) in script_weights {
node_weights.push((Reverse(u64::from(p)), NodeInfo::new_leaf_with_ver(leaf, LeafVersion::default())));
}
if node_weights.is_empty() {
return Err(TaprootBuilderError::IncompleteTree);
}
while node_weights.len() > 1 {
let (p1, s1) = node_weights.pop().expect("len must be at least two");
let (p2, s2) = node_weights.pop().expect("len must be at least two");
let p = Reverse(p1.0.saturating_add(p2.0));
node_weights.push((p, NodeInfo::combine(s1, s2)?));
}
debug_assert!(node_weights.len() == 1);
let node = node_weights.pop().expect("huffman tree algorithm is broken").1;
Ok(Self::from_node_info(secp, internal_key, node))
}
pub fn new_key_spend<C: secp256k1_zkp::Verification>(
secp: &Secp256k1<C>,
internal_key: UntweakedPublicKey,
merkle_root: Option<TapNodeHash>,
) -> Self {
let (output_key, parity) = internal_key.tap_tweak(secp, merkle_root);
Self {
internal_key,
merkle_root,
output_key_parity: parity,
output_key,
script_map: BTreeMap::new(),
}
}
pub fn tap_tweak(&self) -> TapTweakHash {
TapTweakHash::from_key_and_tweak(self.internal_key, self.merkle_root)
}
pub fn internal_key(&self) -> UntweakedPublicKey {
self.internal_key
}
pub fn merkle_root(&self) -> Option<TapNodeHash> {
self.merkle_root
}
pub fn output_key(&self) -> TweakedPublicKey {
self.output_key
}
pub fn output_key_parity(&self) -> secp256k1_zkp::Parity {
self.output_key_parity
}
fn from_node_info<C: secp256k1_zkp::Verification>(
secp: &Secp256k1<C>,
internal_key: UntweakedPublicKey,
node: NodeInfo,
) -> TaprootSpendInfo {
let root_hash = Some(TapNodeHash::from_byte_array(node.hash.to_byte_array()));
let mut info = TaprootSpendInfo::new_key_spend(secp, internal_key, root_hash);
for leaves in node.leaves {
let key = (leaves.script, leaves.ver);
let value = leaves.merkle_branch;
if let Some(set) = info.script_map.get_mut(&key) {
set.insert(value);
} else {
let mut set = BTreeSet::new();
set.insert(value);
info.script_map.insert(key, set);
}
}
info
}
pub fn as_script_map(&self) -> &ScriptMerkleProofMap {
&self.script_map
}
pub fn control_block(&self, script_ver: &(Script, LeafVersion)) -> Option<ControlBlock> {
let merkle_branch_set = self.script_map.get(script_ver)?;
let smallest = merkle_branch_set
.iter()
.min_by(|x, y| x.0.len().cmp(&y.0.len()))
.expect("Invariant: Script map key must contain non-empty set value");
Some(ControlBlock {
internal_key: self.internal_key,
output_key_parity: self.output_key_parity,
leaf_version: script_ver.1,
merkle_branch: smallest.clone(),
})
}
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(crate = "actual_serde"))]
pub struct TaprootBuilder {
branch: Vec<Option<NodeInfo>>,
}
impl TaprootBuilder {
pub fn new() -> Self {
TaprootBuilder { branch: vec![] }
}
pub fn is_complete(&self) -> bool {
self.branch.len() == 1 && self.branch[0].is_some()
}
pub(crate) fn branch(&self) -> &[Option<NodeInfo>]{
&self.branch
}
pub fn add_leaf_with_ver(
self,
depth: usize,
script: Script,
ver: LeafVersion,
) -> Result<Self, TaprootBuilderError> {
let leaf = NodeInfo::new_leaf_with_ver(script, ver);
self.insert(leaf, depth)
}
pub fn add_leaf(self, depth: usize, script: Script) -> Result<Self, TaprootBuilderError> {
self.add_leaf_with_ver(depth, script, LeafVersion::default())
}
pub fn add_hidden(self, depth: usize, hash: sha256::Hash) -> Result<Self, TaprootBuilderError> {
let node = NodeInfo::new_hidden(hash);
self.insert(node, depth)
}
pub fn finalize<C: secp256k1_zkp::Verification>(
mut self,
secp: &Secp256k1<C>,
internal_key: UntweakedPublicKey,
) -> Result<TaprootSpendInfo, TaprootBuilderError> {
if self.branch.len() > 1 {
return Err(TaprootBuilderError::IncompleteTree);
}
let node = self
.branch
.pop()
.ok_or(TaprootBuilderError::EmptyTree)?
.expect("Builder invariant: last element of the branch must be some");
Ok(TaprootSpendInfo::from_node_info(secp, internal_key, node))
}
fn insert(mut self, mut node: NodeInfo, mut depth: usize) -> Result<Self, TaprootBuilderError> {
if depth > TAPROOT_CONTROL_MAX_NODE_COUNT {
return Err(TaprootBuilderError::InvalidMerkleTreeDepth(depth));
}
if depth + 1 < self.branch.len() {
return Err(TaprootBuilderError::NodeNotInDfsOrder);
}
while self.branch.len() == depth + 1 {
let child = match self.branch.pop() {
None => unreachable!("Len of branch checked to be >= 1"),
Some(Some(child)) => child,
Some(None) => {
self.branch.push(None);
break;
} };
if depth == 0 {
return Err(TaprootBuilderError::OverCompleteTree);
}
node = NodeInfo::combine(node, child)?;
depth -= 1;
}
if self.branch.len() < depth + 1 {
let num_extra_nodes = depth + 1 - self.branch.len();
self.branch
.extend((0..num_extra_nodes).map(|_| None));
}
self.branch[depth] = Some(node);
Ok(self)
}
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(crate = "actual_serde"))]
pub struct NodeInfo {
pub(crate) hash: sha256::Hash,
pub(crate) leaves: Vec<LeafInfo>,
}
impl NodeInfo {
pub fn new_hidden(hash: sha256::Hash) -> Self {
Self {
hash,
leaves: vec![],
}
}
pub fn new_leaf_with_ver(script: Script, ver: LeafVersion) -> Self {
let leaf = LeafInfo::new(script, ver);
Self {
hash: leaf.hash(),
leaves: vec![leaf],
}
}
pub fn combine(a: Self, b: Self) -> Result<Self, TaprootBuilderError> {
let mut all_leaves = Vec::with_capacity(a.leaves.len() + b.leaves.len());
for mut a_leaf in a.leaves {
a_leaf.merkle_branch.push(b.hash)?; all_leaves.push(a_leaf);
}
for mut b_leaf in b.leaves {
b_leaf.merkle_branch.push(a.hash)?; all_leaves.push(b_leaf);
}
let mut eng = TapNodeHash::engine();
if a.hash < b.hash {
eng.input(a.hash.as_ref());
eng.input(b.hash.as_ref());
} else {
eng.input(b.hash.as_ref());
eng.input(a.hash.as_ref());
};
Ok(Self {
hash: sha256::Hash::from_engine(eng),
leaves: all_leaves,
})
}
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(crate = "actual_serde"))]
pub struct LeafInfo {
pub(crate) script: Script,
pub(crate) ver: LeafVersion,
pub(crate) merkle_branch: TaprootMerkleBranch,
}
impl LeafInfo {
pub fn new(script: Script, ver: LeafVersion) -> Self {
Self {
script,
ver,
merkle_branch: TaprootMerkleBranch(vec![]),
}
}
fn hash(&self) -> sha256::Hash {
let leaf_hash = TapLeafHash::from_script(&self.script, self.ver);
sha256::Hash::from_byte_array(leaf_hash.to_byte_array())
}
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(crate = "actual_serde"))]
pub struct TaprootMerkleBranch(Vec<sha256::Hash>);
impl TaprootMerkleBranch {
pub fn as_inner(&self) -> &[sha256::Hash] {
&self.0
}
pub fn from_slice(sl: &[u8]) -> Result<Self, TaprootError> {
if sl.len() % TAPROOT_CONTROL_NODE_SIZE != 0 {
Err(TaprootError::InvalidMerkleBranchSize(sl.len()))
} else if sl.len() > TAPROOT_CONTROL_NODE_SIZE * TAPROOT_CONTROL_MAX_NODE_COUNT {
Err(TaprootError::InvalidMerkleTreeDepth(
sl.len() / TAPROOT_CONTROL_NODE_SIZE,
))
} else {
let inner = sl
.chunks(TAPROOT_CONTROL_NODE_SIZE)
.map(|chunk| {
sha256::Hash::from_slice(chunk)
.expect("chunk exact always returns the correct size")
})
.collect();
Ok(TaprootMerkleBranch(inner))
}
}
pub fn encode<Write: io::Write>(&self, mut writer: Write) -> io::Result<usize> {
let mut written = 0;
for hash in &self.0 {
written += writer.write(hash.as_ref())?;
}
Ok(written)
}
pub fn serialize(&self) -> Vec<u8> {
self.0.iter().flat_map(sha256::Hash::as_byte_array).copied().collect::<Vec<u8>>()
}
fn push(&mut self, h: sha256::Hash) -> Result<(), TaprootBuilderError> {
if self.0.len() >= TAPROOT_CONTROL_MAX_NODE_COUNT {
Err(TaprootBuilderError::InvalidMerkleTreeDepth(self.0.len()))
} else {
self.0.push(h);
Ok(())
}
}
pub fn from_inner(inner: Vec<sha256::Hash>) -> Result<Self, TaprootError> {
if inner.len() > TAPROOT_CONTROL_MAX_NODE_COUNT {
Err(TaprootError::InvalidMerkleTreeDepth(inner.len()))
} else {
Ok(TaprootMerkleBranch(inner))
}
}
pub fn into_inner(self) -> Vec<sha256::Hash> {
self.0
}
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(crate = "actual_serde"))]
pub struct ControlBlock {
pub leaf_version: LeafVersion,
pub output_key_parity: secp256k1_zkp::Parity,
pub internal_key: UntweakedPublicKey,
pub merkle_branch: TaprootMerkleBranch,
}
impl ControlBlock {
pub fn from_slice(sl: &[u8]) -> Result<ControlBlock, TaprootError> {
if sl.len() < TAPROOT_CONTROL_BASE_SIZE
|| (sl.len() - TAPROOT_CONTROL_BASE_SIZE) % TAPROOT_CONTROL_NODE_SIZE != 0
{
return Err(TaprootError::InvalidControlBlockSize(sl.len()));
}
let output_key_parity = secp256k1_zkp::Parity::from_u8(sl[0] & 1)
.expect("Parity is a single bit because it is masked by 0x01");
let leaf_version = LeafVersion::from_u8(sl[0] & TAPROOT_LEAF_MASK)?;
let internal_key = UntweakedPublicKey::from_slice(&sl[1..TAPROOT_CONTROL_BASE_SIZE])
.map_err(TaprootError::InvalidInternalKey)?;
let merkle_branch = TaprootMerkleBranch::from_slice(&sl[TAPROOT_CONTROL_BASE_SIZE..])?;
Ok(ControlBlock {
leaf_version,
output_key_parity,
internal_key,
merkle_branch,
})
}
pub fn size(&self) -> usize {
TAPROOT_CONTROL_BASE_SIZE + TAPROOT_CONTROL_NODE_SIZE * self.merkle_branch.as_inner().len()
}
pub fn encode<Write: io::Write>(&self, mut writer: Write) -> io::Result<usize> {
let first_byte: u8 = self.output_key_parity.to_u8() | self.leaf_version.as_u8();
let mut bytes_written = 0;
bytes_written += writer.write(&[first_byte])?;
bytes_written += writer.write(&self.internal_key.serialize())?;
bytes_written += self.merkle_branch.encode(&mut writer)?;
Ok(bytes_written)
}
pub fn serialize(&self) -> Vec<u8> {
let mut buf = Vec::with_capacity(self.size());
self.encode(&mut buf)
.expect("writers don't error");
buf
}
pub fn verify_taproot_commitment<C: secp256k1_zkp::Verification>(
&self,
secp: &Secp256k1<C>,
output_key: &TweakedPublicKey,
script: &Script,
) -> bool {
let leaf_hash = TapLeafHash::from_script(script, self.leaf_version);
let mut curr_hash = TapNodeHash::from_byte_array(leaf_hash.to_byte_array());
for elem in self.merkle_branch.as_inner() {
let mut eng = TapNodeHash::engine();
if curr_hash.as_byte_array() < elem.as_byte_array() {
eng.input(curr_hash.as_ref());
eng.input(elem.as_ref());
} else {
eng.input(elem.as_ref());
eng.input(curr_hash.as_ref());
}
curr_hash = TapNodeHash::from_engine(eng);
}
let tweak = TapTweakHash::from_key_and_tweak(self.internal_key, Some(curr_hash));
let tweak = Scalar::from_be_bytes(tweak.to_byte_array()).expect("hash value greater than curve order");
self.internal_key.tweak_add_check(
secp,
output_key.as_inner(),
self.output_key_parity,
tweak,
)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(crate = "actual_serde"))]
pub struct LeafVersion(u8);
impl Default for LeafVersion {
fn default() -> Self {
LeafVersion(TAPROOT_LEAF_TAPSCRIPT)
}
}
impl LeafVersion {
pub fn from_u8(ver: u8) -> Result<Self, TaprootError> {
if ver & TAPROOT_LEAF_MASK == ver && ver != 0x50 {
Ok(LeafVersion(ver))
} else {
Err(TaprootError::InvalidTaprootLeafVersion(ver))
}
}
pub fn as_u8(&self) -> u8 {
self.0
}
}
impl From<LeafVersion> for u8 {
fn from(lv: LeafVersion) -> u8 {
lv.0
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum TaprootBuilderError {
InvalidMerkleTreeDepth(usize),
NodeNotInDfsOrder,
OverCompleteTree,
InvalidInternalKey(secp256k1_zkp::UpstreamError),
IncompleteTree,
EmptyTree,
}
impl fmt::Display for TaprootBuilderError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
TaprootBuilderError::NodeNotInDfsOrder => {
write!(f, "add_leaf/add_hidden must be called in DFS walk order",)
}
TaprootBuilderError::OverCompleteTree => write!(
f,
"Attempted to create a tree with two nodes at depth 0. There must\
only be a exactly one node at depth 0",
),
TaprootBuilderError::InvalidMerkleTreeDepth(d) => write!(
f,
"Merkle Tree depth({}) must be less than {}",
d, TAPROOT_CONTROL_MAX_NODE_COUNT
),
TaprootBuilderError::InvalidInternalKey(e) => {
write!(f, "Invalid Internal XOnly key : {}", e)
}
TaprootBuilderError::IncompleteTree => {
write!(f, "Called finalize on an incomplete tree")
}
TaprootBuilderError::EmptyTree => {
write!(f, "Called finalize on an empty tree")
}
}
}
}
impl error::Error for TaprootBuilderError {}
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum TaprootError {
InvalidMerkleBranchSize(usize),
InvalidMerkleTreeDepth(usize),
InvalidTaprootLeafVersion(u8),
InvalidControlBlockSize(usize),
InvalidInternalKey(secp256k1_zkp::UpstreamError),
EmptyTree,
}
impl fmt::Display for TaprootError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
TaprootError::InvalidMerkleBranchSize(sz) => write!(
f,
"Merkle branch size({}) must be a multiple of {}",
sz, TAPROOT_CONTROL_NODE_SIZE
),
TaprootError::InvalidMerkleTreeDepth(d) => write!(
f,
"Merkle Tree depth({}) must be less than {}",
d, TAPROOT_CONTROL_MAX_NODE_COUNT
),
TaprootError::InvalidTaprootLeafVersion(v) => write!(
f,
"Leaf version({}) must have the least significant bit 0",
v
),
TaprootError::InvalidControlBlockSize(sz) => write!(
f,
"Control Block size({}) must be of the form 33 + 32*m where 0 <= m <= {} ",
sz, TAPROOT_CONTROL_MAX_NODE_COUNT
),
TaprootError::InvalidInternalKey(e) => write!(f, "Invalid Internal XOnly key : {}", e),
TaprootError::EmptyTree => write!(f, "Taproot Tree must contain at least one script"),
}
}
}
impl error::Error for TaprootError {}
#[cfg(test)]
mod tests{
use super::*;
use crate::hashes::HashEngine;
use crate::hashes::sha256t::Tag;
use crate::hex::FromHex;
use std::str::FromStr;
fn tag_engine(tag_name: &str) -> sha256::HashEngine {
let mut engine = sha256::Hash::engine();
let tag_hash = sha256::Hash::hash(tag_name.as_bytes());
engine.input(&tag_hash[..]);
engine.input(&tag_hash[..]);
engine
}
#[test]
fn test_midstates() {
fn empty_hash(tag_name: &str) -> [u8; 32] {
let mut e = tag_engine(tag_name);
e.input(&[]);
sha256::Hash::from_engine(e).to_byte_array()
}
assert_eq!(tag_engine("TapLeaf/elements").midstate(), TapLeafTag::engine().midstate());
assert_eq!(tag_engine("TapBranch/elements").midstate(), TapBranchTag::engine().midstate());
assert_eq!(tag_engine("TapTweak/elements").midstate(), TapTweakTag::engine().midstate());
assert_eq!(tag_engine("TapSighash/elements").midstate(), TapSighashTag::engine().midstate());
assert_eq!(empty_hash("TapLeaf/elements"), TapLeafHash::hash(&[]).to_byte_array());
assert_eq!(empty_hash("TapBranch/elements"), TapNodeHash::hash(&[]).to_byte_array());
assert_eq!(empty_hash("TapTweak/elements"), TapTweakHash::hash(&[]).to_byte_array());
assert_eq!(empty_hash("TapSighash/elements"), TapSighashHash::hash(&[]).to_byte_array());
}
#[test]
fn build_huffman_tree() {
let secp = Secp256k1::verification_only();
let internal_key = UntweakedPublicKey::from_str("93c7378d96518a75448821c4f7c8f4bae7ce60f804d03d1f0628dd5dd0f5de51").unwrap();
let script_weights = vec![
(10, Script::from_hex("51").unwrap()), (20, Script::from_hex("52").unwrap()),
(20, Script::from_hex("53").unwrap()),
(30, Script::from_hex("54").unwrap()),
(19, Script::from_hex("55").unwrap()),
];
let tree_info = TaprootSpendInfo::with_huffman_tree(&secp, internal_key, script_weights.clone()).unwrap();
for &(script, length) in &[("51", 3), ("52", 2), ("53", 2), ("54", 2), ("55", 3)] {
assert_eq!(
length,
tree_info
.script_map
.get(&(Script::from_hex(script).unwrap(), LeafVersion::default()))
.expect("Present Key")
.iter()
.next()
.expect("Present Path")
.0
.len()
);
}
let output_key = tree_info.output_key();
for (_weights, script) in script_weights {
let ver_script = (script, LeafVersion::default());
let ctrl_block = tree_info.control_block(&ver_script).unwrap();
assert!(ctrl_block.verify_taproot_commitment(&secp, &output_key, &ver_script.0));
}
}
#[test]
fn taptree_builder() {
let secp = Secp256k1::verification_only();
let internal_key = UntweakedPublicKey::from_str("93c7378d96518a75448821c4f7c8f4bae7ce60f804d03d1f0628dd5dd0f5de51").unwrap();
let builder = TaprootBuilder::new();
let scripts = [
Script::from_hex("51").unwrap(),
Script::from_hex("52").unwrap(),
Script::from_hex("53").unwrap(),
Script::from_hex("54").unwrap(),
Script::from_hex("55").unwrap(),
];
let builder = builder
.add_leaf(2, scripts[0].clone()).unwrap()
.add_leaf(2, scripts[1].clone()).unwrap()
.add_leaf(2, scripts[2].clone()).unwrap()
.add_leaf(3, scripts[3].clone()).unwrap()
.add_leaf(3, scripts[4].clone()).unwrap();
let tree_info = builder.finalize(&secp, internal_key).unwrap();
let output_key = tree_info.output_key();
for script in scripts {
let ver_script = (script, LeafVersion::default());
let ctrl_block = tree_info.control_block(&ver_script).unwrap();
assert!(ctrl_block.verify_taproot_commitment(&secp, &output_key, &ver_script.0));
}
}
}