From 24e944ed82d28393233271ca2d52414b96faeb53 Mon Sep 17 00:00:00 2001 From: "Tobin C. Harding" Date: Tue, 22 Oct 2024 13:39:44 +1100 Subject: [PATCH] Introduce taproot hash type extension traits Introduce three extension traits for the taproot hash types. All logic for the hash types is now within the extension traits. --- bitcoin/src/blockdata/script/borrowed.rs | 2 +- bitcoin/src/crypto/key.rs | 2 +- bitcoin/src/crypto/sighash.rs | 1 + bitcoin/src/taproot/mod.rs | 95 +++++++++++++----------- 4 files changed, 55 insertions(+), 45 deletions(-) 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 6fb892825..a1236d014 100644 --- a/bitcoin/src/taproot/mod.rs +++ b/bitcoin/src/taproot/mod.rs @@ -62,40 +62,46 @@ hash_newtype! { 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,22 +113,25 @@ 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 { - 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 + } - /// 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) } + /// 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) } - /// 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)) + } } }