Skip to content

Commit 3319e6e

Browse files
committed
Add ControlBlock constructor that takes a hex string
1 parent 8ca3a43 commit 3319e6e

File tree

1 file changed

+15
-7
lines changed

1 file changed

+15
-7
lines changed

bitcoin/src/taproot/mod.rs

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ use core::fmt;
1313
use core::iter::FusedIterator;
1414

1515
use hashes::{hash_newtype, sha256t, sha256t_tag, HashEngine};
16+
use hex::{FromHex, HexToBytesError};
1617
use internals::array::ArrayExt;
1718
#[allow(unused)] // MSRV polyfill
1819
use internals::slice::SliceExt;
@@ -1180,6 +1181,12 @@ impl ControlBlock {
11801181

11811182
Ok(ControlBlock { leaf_version, output_key_parity, internal_key, merkle_branch })
11821183
}
1184+
1185+
/// Constructs a new [`ControlBlock`] from a hex string.
1186+
pub fn from_hex(hex: &str) -> Result<Self, TaprootError> {
1187+
let vec = Vec::from_hex(hex).map_err(TaprootError::InvalidControlBlockHex)?;
1188+
ControlBlock::decode(vec.as_slice())
1189+
}
11831190
}
11841191

11851192
impl<B, K> ControlBlock<B, K> {
@@ -1496,6 +1503,8 @@ pub enum TaprootError {
14961503
InvalidControlBlockSize(InvalidControlBlockSizeError),
14971504
/// Invalid Taproot internal key.
14981505
InvalidInternalKey(secp256k1::Error),
1506+
/// Invalid control block hex
1507+
InvalidControlBlockHex(HexToBytesError),
14991508
/// Empty Taproot tree.
15001509
EmptyTree,
15011510
}
@@ -1513,6 +1522,7 @@ impl fmt::Display for TaprootError {
15131522
InvalidMerkleTreeDepth(ref e) => write_err!(f, "invalid Merkle tree depth"; e),
15141523
InvalidTaprootLeafVersion(ref e) => write_err!(f, "invalid Taproot leaf version"; e),
15151524
InvalidControlBlockSize(ref e) => write_err!(f, "invalid control block size"; e),
1525+
InvalidControlBlockHex(ref e) => write_err!(f, "invalid control block hex"; e),
15161526
InvalidInternalKey(ref e) => write_err!(f, "invalid internal x-only key"; e),
15171527
EmptyTree => write!(f, "Taproot tree must contain at least one script"),
15181528
}
@@ -1528,6 +1538,7 @@ impl std::error::Error for TaprootError {
15281538
InvalidInternalKey(e) => Some(e),
15291539
InvalidTaprootLeafVersion(ref e) => Some(e),
15301540
InvalidMerkleTreeDepth(ref e) => Some(e),
1541+
InvalidControlBlockHex(ref e) => Some(e),
15311542
InvalidMerkleBranchSize(_) | InvalidControlBlockSize(_) | EmptyTree => None,
15321543
}
15331544
}
@@ -1652,7 +1663,7 @@ impl std::error::Error for InvalidControlBlockSizeError {}
16521663
#[cfg(test)]
16531664
mod test {
16541665
use hashes::sha256;
1655-
use hex::{DisplayHex, FromHex};
1666+
use hex::DisplayHex;
16561667
use secp256k1::VerifyOnly;
16571668

16581669
use super::*;
@@ -1753,8 +1764,7 @@ mod test {
17531764
let out_pk = out_spk_hex[4..].parse::<XOnlyPublicKey>().unwrap();
17541765
let out_pk = TweakedPublicKey::dangerous_assume_tweaked(out_pk);
17551766
let script = ScriptBuf::from_hex(script_hex).unwrap();
1756-
let control_block =
1757-
ControlBlock::decode(&Vec::<u8>::from_hex(control_block_hex).unwrap()).unwrap();
1767+
let control_block = ControlBlock::from_hex(control_block_hex).unwrap();
17581768
assert_eq!(control_block_hex, control_block.serialize().to_lower_hex_string());
17591769
assert!(control_block.verify_taproot_commitment(secp, out_pk.to_inner(), &script));
17601770
}
@@ -2035,10 +2045,8 @@ mod test {
20352045
let spend_info = builder.finalize(secp, internal_key).unwrap();
20362046
for (i, script_ver) in leaves.iter().enumerate() {
20372047
let expected_leaf_hash = leaf_hashes[i].as_str().unwrap();
2038-
let expected_ctrl_blk = ControlBlock::decode(
2039-
&Vec::<u8>::from_hex(ctrl_blks[i].as_str().unwrap()).unwrap(),
2040-
)
2041-
.unwrap();
2048+
let expected_ctrl_blk =
2049+
ControlBlock::from_hex(ctrl_blks[i].as_str().unwrap()).unwrap();
20422050

20432051
let leaf_hash = TapLeafHash::from_script(&script_ver.0, script_ver.1);
20442052
let ctrl_blk = spend_info.control_block(script_ver).unwrap();

0 commit comments

Comments
 (0)