Add ControlBlock constructor that takes a hex string

This commit is contained in:
Shing Him Ng 2025-04-20 22:34:03 -05:00
parent 8ca3a430c7
commit 3319e6ef6d
1 changed files with 15 additions and 7 deletions

View File

@ -13,6 +13,7 @@ use core::fmt;
use core::iter::FusedIterator; use core::iter::FusedIterator;
use hashes::{hash_newtype, sha256t, sha256t_tag, HashEngine}; use hashes::{hash_newtype, sha256t, sha256t_tag, HashEngine};
use hex::{FromHex, HexToBytesError};
use internals::array::ArrayExt; use internals::array::ArrayExt;
#[allow(unused)] // MSRV polyfill #[allow(unused)] // MSRV polyfill
use internals::slice::SliceExt; use internals::slice::SliceExt;
@ -1180,6 +1181,12 @@ impl ControlBlock {
Ok(ControlBlock { leaf_version, output_key_parity, internal_key, merkle_branch }) Ok(ControlBlock { leaf_version, output_key_parity, internal_key, merkle_branch })
} }
/// Constructs a new [`ControlBlock`] from a hex string.
pub fn from_hex(hex: &str) -> Result<Self, TaprootError> {
let vec = Vec::from_hex(hex).map_err(TaprootError::InvalidControlBlockHex)?;
ControlBlock::decode(vec.as_slice())
}
} }
impl<B, K> ControlBlock<B, K> { impl<B, K> ControlBlock<B, K> {
@ -1496,6 +1503,8 @@ pub enum TaprootError {
InvalidControlBlockSize(InvalidControlBlockSizeError), InvalidControlBlockSize(InvalidControlBlockSizeError),
/// Invalid Taproot internal key. /// Invalid Taproot internal key.
InvalidInternalKey(secp256k1::Error), InvalidInternalKey(secp256k1::Error),
/// Invalid control block hex
InvalidControlBlockHex(HexToBytesError),
/// Empty Taproot tree. /// Empty Taproot tree.
EmptyTree, EmptyTree,
} }
@ -1513,6 +1522,7 @@ impl fmt::Display for TaprootError {
InvalidMerkleTreeDepth(ref e) => write_err!(f, "invalid Merkle tree depth"; e), InvalidMerkleTreeDepth(ref e) => write_err!(f, "invalid Merkle tree depth"; e),
InvalidTaprootLeafVersion(ref e) => write_err!(f, "invalid Taproot leaf version"; e), InvalidTaprootLeafVersion(ref e) => write_err!(f, "invalid Taproot leaf version"; e),
InvalidControlBlockSize(ref e) => write_err!(f, "invalid control block size"; e), InvalidControlBlockSize(ref e) => write_err!(f, "invalid control block size"; e),
InvalidControlBlockHex(ref e) => write_err!(f, "invalid control block hex"; e),
InvalidInternalKey(ref e) => write_err!(f, "invalid internal x-only key"; e), InvalidInternalKey(ref e) => write_err!(f, "invalid internal x-only key"; e),
EmptyTree => write!(f, "Taproot tree must contain at least one script"), EmptyTree => write!(f, "Taproot tree must contain at least one script"),
} }
@ -1528,6 +1538,7 @@ impl std::error::Error for TaprootError {
InvalidInternalKey(e) => Some(e), InvalidInternalKey(e) => Some(e),
InvalidTaprootLeafVersion(ref e) => Some(e), InvalidTaprootLeafVersion(ref e) => Some(e),
InvalidMerkleTreeDepth(ref e) => Some(e), InvalidMerkleTreeDepth(ref e) => Some(e),
InvalidControlBlockHex(ref e) => Some(e),
InvalidMerkleBranchSize(_) | InvalidControlBlockSize(_) | EmptyTree => None, InvalidMerkleBranchSize(_) | InvalidControlBlockSize(_) | EmptyTree => None,
} }
} }
@ -1652,7 +1663,7 @@ impl std::error::Error for InvalidControlBlockSizeError {}
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use hashes::sha256; use hashes::sha256;
use hex::{DisplayHex, FromHex}; use hex::DisplayHex;
use secp256k1::VerifyOnly; use secp256k1::VerifyOnly;
use super::*; use super::*;
@ -1753,8 +1764,7 @@ mod test {
let out_pk = out_spk_hex[4..].parse::<XOnlyPublicKey>().unwrap(); let out_pk = out_spk_hex[4..].parse::<XOnlyPublicKey>().unwrap();
let out_pk = TweakedPublicKey::dangerous_assume_tweaked(out_pk); let out_pk = TweakedPublicKey::dangerous_assume_tweaked(out_pk);
let script = ScriptBuf::from_hex(script_hex).unwrap(); let script = ScriptBuf::from_hex(script_hex).unwrap();
let control_block = let control_block = ControlBlock::from_hex(control_block_hex).unwrap();
ControlBlock::decode(&Vec::<u8>::from_hex(control_block_hex).unwrap()).unwrap();
assert_eq!(control_block_hex, control_block.serialize().to_lower_hex_string()); assert_eq!(control_block_hex, control_block.serialize().to_lower_hex_string());
assert!(control_block.verify_taproot_commitment(secp, out_pk.to_inner(), &script)); assert!(control_block.verify_taproot_commitment(secp, out_pk.to_inner(), &script));
} }
@ -2035,10 +2045,8 @@ mod test {
let spend_info = builder.finalize(secp, internal_key).unwrap(); let spend_info = builder.finalize(secp, internal_key).unwrap();
for (i, script_ver) in leaves.iter().enumerate() { for (i, script_ver) in leaves.iter().enumerate() {
let expected_leaf_hash = leaf_hashes[i].as_str().unwrap(); let expected_leaf_hash = leaf_hashes[i].as_str().unwrap();
let expected_ctrl_blk = ControlBlock::decode( let expected_ctrl_blk =
&Vec::<u8>::from_hex(ctrl_blks[i].as_str().unwrap()).unwrap(), ControlBlock::from_hex(ctrl_blks[i].as_str().unwrap()).unwrap();
)
.unwrap();
let leaf_hash = TapLeafHash::from_script(&script_ver.0, script_ver.1); let leaf_hash = TapLeafHash::from_script(&script_ver.0, script_ver.1);
let ctrl_blk = spend_info.control_block(script_ver).unwrap(); let ctrl_blk = spend_info.control_block(script_ver).unwrap();