diff --git a/bitcoin/src/blockdata/script/borrowed.rs b/bitcoin/src/blockdata/script/borrowed.rs index 55181d9c4..cf2e62513 100644 --- a/bitcoin/src/blockdata/script/borrowed.rs +++ b/bitcoin/src/blockdata/script/borrowed.rs @@ -14,7 +14,7 @@ use crate::opcodes::all::*; use crate::opcodes::{self, Opcode}; use crate::policy::DUST_RELAY_TX_FEE; use crate::prelude::{sink, DisplayHex, String, ToString}; -use crate::taproot::{LeafVersion, TapLeafHash}; +use crate::taproot::{LeafVersion, TapLeafHash, TapLeafHashExt as _}; use crate::FeeRate; #[rustfmt::skip] // Keep public re-exports separate. diff --git a/bitcoin/src/crypto/key.rs b/bitcoin/src/crypto/key.rs index a0eb870dd..facf399b1 100644 --- a/bitcoin/src/crypto/key.rs +++ b/bitcoin/src/crypto/key.rs @@ -20,7 +20,7 @@ use crate::internal_macros::impl_asref_push_bytes; use crate::network::NetworkKind; use crate::prelude::{DisplayHex, String, Vec}; use crate::script::{self, ScriptBuf}; -use crate::taproot::{TapNodeHash, TapTweakHash}; +use crate::taproot::{TapNodeHash, TapTweakHash, TapTweakHashExt as _}; #[rustfmt::skip] // Keep public re-exports separate. pub use secp256k1::{constants, Keypair, Parity, Secp256k1, Verification, XOnlyPublicKey}; diff --git a/bitcoin/src/crypto/sighash.rs b/bitcoin/src/crypto/sighash.rs index 45a9b4d93..046eb491e 100644 --- a/bitcoin/src/crypto/sighash.rs +++ b/bitcoin/src/crypto/sighash.rs @@ -1509,6 +1509,7 @@ mod tests { use crate::consensus::deserialize; use crate::locktime::absolute; use crate::script::ScriptBufExt as _; + use crate::taproot::TapTweakHashExt as _; extern crate serde_json; diff --git a/bitcoin/src/taproot/mod.rs b/bitcoin/src/taproot/mod.rs index b18d87a7e..350da2cfb 100644 --- a/bitcoin/src/taproot/mod.rs +++ b/bitcoin/src/taproot/mod.rs @@ -11,7 +11,7 @@ use core::cmp::Reverse; use core::fmt; use core::iter::FusedIterator; -use hashes::{hash_newtype, sha256t, sha256t_tag, HashEngine}; +use hashes::{sha256t, HashEngine}; use internals::{impl_to_hex_from_lower_hex, write_err}; use io::Write; use secp256k1::{Scalar, Secp256k1}; @@ -27,75 +27,48 @@ use crate::{Script, ScriptBuf}; pub use crate::crypto::taproot::{SigFromSliceError, Signature}; #[doc(inline)] pub use merkle_branch::TaprootMerkleBranch; +pub use primitives::taproot::{TapLeafTag, TapLeafHash, TapBranchTag, TapNodeHash, TapTweakTag, TapTweakHash}; -// Taproot test vectors from BIP-341 state the hashes without any reversing -sha256t_tag! { - pub struct TapLeafTag = hash_str("TapLeaf"); -} - -hash_newtype! { - /// Taproot-tagged hash with tag \"TapLeaf\". - /// - /// This is used for computing tapscript script spend hash. - pub struct TapLeafHash(sha256t::Hash); -} - -sha256t_tag! { - pub struct TapBranchTag = hash_str("TapBranch"); -} - -hash_newtype! { - /// Tagged hash used in Taproot trees. - /// - /// See BIP-340 for tagging rules. - pub struct TapNodeHash(sha256t::Hash); -} - -sha256t_tag! { - pub struct TapTweakTag = hash_str("TapTweak"); -} - -hash_newtype! { - /// Taproot-tagged hash with tag \"TapTweak\". - /// - /// This hash type is used while computing the tweaked public key. - pub struct TapTweakHash(sha256t::Hash); -} - -impl TapTweakHash { - /// Creates a new BIP341 [`TapTweakHash`] from key and tweak. Produces `H_taptweak(P||R)` where - /// `P` is the internal key and `R` is the Merkle root. - pub fn from_key_and_tweak( - internal_key: UntweakedPublicKey, - merkle_root: Option, - ) -> TapTweakHash { - let mut eng = sha256t::Hash::::engine(); - // always hash the key - eng.input(&internal_key.serialize()); - if let Some(h) = merkle_root { - eng.input(h.as_ref()); - } else { - // nothing to hash +crate::internal_macros::define_extension_trait! { + /// Extension functionality for the [`TapTweakHash`] type. + pub trait TapTweakHashExt impl for TapTweakHash { + /// Creates a new BIP341 [`TapTweakHash`] from key and tweak. Produces `H_taptweak(P||R)` where + /// `P` is the internal key and `R` is the Merkle root. + fn from_key_and_tweak( + internal_key: UntweakedPublicKey, + merkle_root: Option, + ) -> TapTweakHash { + let mut eng = sha256t::Hash::::engine(); + // always hash the key + eng.input(&internal_key.serialize()); + if let Some(h) = merkle_root { + eng.input(h.as_ref()); + } else { + // nothing to hash + } + let inner = sha256t::Hash::::from_engine(eng); + TapTweakHash::from_byte_array(inner.to_byte_array()) } - let inner = sha256t::Hash::::from_engine(eng); - TapTweakHash::from_byte_array(inner.to_byte_array()) - } - /// Converts a `TapTweakHash` into a `Scalar` ready for use with key tweaking API. - pub fn to_scalar(self) -> Scalar { - // This is statistically extremely unlikely to panic. - Scalar::from_be_bytes(self.to_byte_array()).expect("hash value greater than curve order") + /// Converts a `TapTweakHash` into a `Scalar` ready for use with key tweaking API. + fn to_scalar(self) -> Scalar { + // This is statistically extremely unlikely to panic. + Scalar::from_be_bytes(self.to_byte_array()).expect("hash value greater than curve order") + } } } -impl TapLeafHash { - /// Computes the leaf hash from components. - pub fn from_script(script: &Script, ver: LeafVersion) -> TapLeafHash { - let mut eng = sha256t::Hash::::engine(); - ver.to_consensus().consensus_encode(&mut eng).expect("engines don't error"); - script.consensus_encode(&mut eng).expect("engines don't error"); - let inner = sha256t::Hash::::from_engine(eng); - TapLeafHash::from_byte_array(inner.to_byte_array()) +crate::internal_macros::define_extension_trait! { + /// Extension functionality for the [`TapLeafHash`] type. + pub trait TapLeafHashExt impl for TapLeafHash { + /// Computes the leaf hash from components. + fn from_script(script: &Script, ver: LeafVersion) -> TapLeafHash { + let mut eng = sha256t::Hash::::engine(); + ver.to_consensus().consensus_encode(&mut eng).expect("engines don't error"); + script.consensus_encode(&mut eng).expect("engines don't error"); + let inner = sha256t::Hash::::from_engine(eng); + TapLeafHash::from_byte_array(inner.to_byte_array()) + } } } @@ -107,42 +80,41 @@ impl From<&LeafNode> for TapNodeHash { fn from(leaf: &LeafNode) -> TapNodeHash { leaf.node_hash() } } -impl TapNodeHash { - /// Computes branch hash given two hashes of the nodes underneath it. - pub fn from_node_hashes(a: TapNodeHash, b: TapNodeHash) -> TapNodeHash { - Self::combine_node_hashes(a, b).0 - } +crate::internal_macros::define_extension_trait! { + /// Extension functionality for the [`TapNodeHash`] type. + pub trait TapNodeHashExt impl for TapNodeHash { + /// Computes branch hash given two hashes of the nodes underneath it. + fn from_node_hashes(a: TapNodeHash, b: TapNodeHash) -> TapNodeHash { + combine_node_hashes(a, b).0 + } - /// Computes branch hash given two hashes of the nodes underneath it and returns - /// whether the left node was the one hashed first. - fn combine_node_hashes(a: TapNodeHash, b: TapNodeHash) -> (TapNodeHash, bool) { - let mut eng = sha256t::Hash::::engine(); - if a < b { - eng.input(a.as_ref()); - eng.input(b.as_ref()); - } else { - eng.input(b.as_ref()); - eng.input(a.as_ref()); - }; - let inner = sha256t::Hash::::from_engine(eng); - (TapNodeHash::from_byte_array(inner.to_byte_array()), a < b) - } + /// Assumes the given 32 byte array as hidden [`TapNodeHash`]. + /// + /// Similar to [`TapLeafHash::from_byte_array`], but explicitly conveys that the + /// hash is constructed from a hidden node. This also has better ergonomics + /// because it does not require the caller to import the Hash trait. + fn assume_hidden(hash: [u8; 32]) -> TapNodeHash { TapNodeHash::from_byte_array(hash) } - /// Assumes the given 32 byte array as hidden [`TapNodeHash`]. - /// - /// Similar to [`TapLeafHash::from_byte_array`], but explicitly conveys that the - /// hash is constructed from a hidden node. This also has better ergonomics - /// because it does not require the caller to import the Hash trait. - pub fn assume_hidden(hash: [u8; 32]) -> TapNodeHash { TapNodeHash::from_byte_array(hash) } - - /// Computes the [`TapNodeHash`] from a script and a leaf version. - pub fn from_script(script: &Script, ver: LeafVersion) -> TapNodeHash { - TapNodeHash::from(TapLeafHash::from_script(script, ver)) + /// Computes the [`TapNodeHash`] from a script and a leaf version. + fn from_script(script: &Script, ver: LeafVersion) -> TapNodeHash { + TapNodeHash::from(TapLeafHash::from_script(script, ver)) + } } } -impl From for TapNodeHash { - fn from(leaf: TapLeafHash) -> TapNodeHash { TapNodeHash::from_byte_array(leaf.to_byte_array()) } +/// Computes branch hash given two hashes of the nodes underneath it and returns +/// whether the left node was the one hashed first. +fn combine_node_hashes(a: TapNodeHash, b: TapNodeHash) -> (TapNodeHash, bool) { + let mut eng = sha256t::Hash::::engine(); + if a < b { + eng.input(a.as_ref()); + eng.input(b.as_ref()); + } else { + eng.input(b.as_ref()); + eng.input(a.as_ref()); + }; + let inner = sha256t::Hash::::from_engine(eng); + (TapNodeHash::from_byte_array(inner.to_byte_array()), a < b) } /// Maximum depth of a Taproot tree script spend path. @@ -853,7 +825,7 @@ impl NodeInfo { /// Combines two [`NodeInfo`] to create a new parent. pub fn combine(a: Self, b: Self) -> Result { let mut all_leaves = Vec::with_capacity(a.leaves.len() + b.leaves.len()); - let (hash, left_first) = TapNodeHash::combine_node_hashes(a.hash, b.hash); + let (hash, left_first) = combine_node_hashes(a.hash, b.hash); let (a, b) = if left_first { (a, b) } else { (b, a) }; for mut a_leaf in a.leaves { a_leaf.merkle_branch.push(b.hash)?; // add hashing partner @@ -1545,6 +1517,13 @@ impl fmt::Display for InvalidControlBlockSizeError { #[cfg(feature = "std")] impl std::error::Error for InvalidControlBlockSizeError {} +mod sealed { + pub trait Sealed {} + impl Sealed for super::TapTweakHash {} + impl Sealed for super::TapLeafHash {} + impl Sealed for super::TapNodeHash {} +} + #[cfg(test)] mod test { use hashes::sha256; @@ -1854,15 +1833,17 @@ mod test { #[test] #[cfg(feature = "serde")] fn test_merkle_branch_serde() { - let hash1 = TapNodeHash( + let hash1 = TapNodeHash::from_byte_array( "03ba2a4dcd914fed29a1c630c7e811271b081a0e2f2f52cf1c197583dfd46c1b" .parse::>() - .unwrap(), + .unwrap() + .to_byte_array(), ); - let hash2 = TapNodeHash( + let hash2 = TapNodeHash::from_byte_array( "8d79dedc2fa0b55167b5d28c61dbad9ce1191a433f3a1a6c8ee291631b2c94c9" .parse::>() - .unwrap(), + .unwrap() + .to_byte_array(), ); let merkle_branch = TaprootMerkleBranch::from([hash1, hash2]); // use serde_test to test serialization and deserialization diff --git a/primitives/src/lib.rs b/primitives/src/lib.rs index 209fd3db9..bd924e0e2 100644 --- a/primitives/src/lib.rs +++ b/primitives/src/lib.rs @@ -39,6 +39,7 @@ pub mod pow; #[cfg(feature = "alloc")] pub mod script; pub mod sequence; +pub mod taproot; pub mod transaction; #[cfg(feature = "alloc")] pub mod witness; diff --git a/primitives/src/taproot.rs b/primitives/src/taproot.rs new file mode 100644 index 000000000..452b10af5 --- /dev/null +++ b/primitives/src/taproot.rs @@ -0,0 +1,45 @@ +// SPDX-License-Identifier: CC0-1.0 + +//! Bitcoin Taproot. +//! +//! This module provides support for Taproot tagged hashes. + +use hashes::{hash_newtype, sha256t, sha256t_tag}; + +// Taproot test vectors from BIP-341 state the hashes without any reversing +sha256t_tag! { + pub struct TapLeafTag = hash_str("TapLeaf"); +} + +hash_newtype! { + /// Taproot-tagged hash with tag \"TapLeaf\". + /// + /// This is used for computing tapscript script spend hash. + pub struct TapLeafHash(sha256t::Hash); +} + +sha256t_tag! { + pub struct TapBranchTag = hash_str("TapBranch"); +} + +hash_newtype! { + /// Tagged hash used in Taproot trees. + /// + /// See BIP-340 for tagging rules. + pub struct TapNodeHash(sha256t::Hash); +} + +sha256t_tag! { + pub struct TapTweakTag = hash_str("TapTweak"); +} + +hash_newtype! { + /// Taproot-tagged hash with tag \"TapTweak\". + /// + /// This hash type is used while computing the tweaked public key. + pub struct TapTweakHash(sha256t::Hash); +} + +impl From for TapNodeHash { + fn from(leaf: TapLeafHash) -> TapNodeHash { TapNodeHash::from_byte_array(leaf.to_byte_array()) } +}