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.
This commit is contained in:
Tobin C. Harding 2024-10-22 13:39:44 +11:00
parent c30ef617fb
commit 24e944ed82
No known key found for this signature in database
GPG Key ID: 40BF9E4C269D6607
4 changed files with 55 additions and 45 deletions

View File

@ -14,7 +14,7 @@ use crate::opcodes::all::*;
use crate::opcodes::{self, Opcode}; use crate::opcodes::{self, Opcode};
use crate::policy::DUST_RELAY_TX_FEE; use crate::policy::DUST_RELAY_TX_FEE;
use crate::prelude::{sink, DisplayHex, String, ToString}; use crate::prelude::{sink, DisplayHex, String, ToString};
use crate::taproot::{LeafVersion, TapLeafHash}; use crate::taproot::{LeafVersion, TapLeafHash, TapLeafHashExt as _};
use crate::FeeRate; use crate::FeeRate;
#[rustfmt::skip] // Keep public re-exports separate. #[rustfmt::skip] // Keep public re-exports separate.

View File

@ -20,7 +20,7 @@ use crate::internal_macros::impl_asref_push_bytes;
use crate::network::NetworkKind; use crate::network::NetworkKind;
use crate::prelude::{DisplayHex, String, Vec}; use crate::prelude::{DisplayHex, String, Vec};
use crate::script::{self, ScriptBuf}; use crate::script::{self, ScriptBuf};
use crate::taproot::{TapNodeHash, TapTweakHash}; use crate::taproot::{TapNodeHash, TapTweakHash, TapTweakHashExt as _};
#[rustfmt::skip] // Keep public re-exports separate. #[rustfmt::skip] // Keep public re-exports separate.
pub use secp256k1::{constants, Keypair, Parity, Secp256k1, Verification, XOnlyPublicKey}; pub use secp256k1::{constants, Keypair, Parity, Secp256k1, Verification, XOnlyPublicKey};

View File

@ -1509,6 +1509,7 @@ mod tests {
use crate::consensus::deserialize; use crate::consensus::deserialize;
use crate::locktime::absolute; use crate::locktime::absolute;
use crate::script::ScriptBufExt as _; use crate::script::ScriptBufExt as _;
use crate::taproot::TapTweakHashExt as _;
extern crate serde_json; extern crate serde_json;

View File

@ -62,40 +62,46 @@ hash_newtype! {
pub struct TapTweakHash(sha256t::Hash<TapTweakTag>); pub struct TapTweakHash(sha256t::Hash<TapTweakTag>);
} }
impl TapTweakHash { crate::internal_macros::define_extension_trait! {
/// Creates a new BIP341 [`TapTweakHash`] from key and tweak. Produces `H_taptweak(P||R)` where /// Extension functionality for the [`TapTweakHash`] type.
/// `P` is the internal key and `R` is the Merkle root. pub trait TapTweakHashExt impl for TapTweakHash {
pub fn from_key_and_tweak( /// Creates a new BIP341 [`TapTweakHash`] from key and tweak. Produces `H_taptweak(P||R)` where
internal_key: UntweakedPublicKey, /// `P` is the internal key and `R` is the Merkle root.
merkle_root: Option<TapNodeHash>, fn from_key_and_tweak(
) -> TapTweakHash { internal_key: UntweakedPublicKey,
let mut eng = sha256t::Hash::<TapTweakTag>::engine(); merkle_root: Option<TapNodeHash>,
// always hash the key ) -> TapTweakHash {
eng.input(&internal_key.serialize()); let mut eng = sha256t::Hash::<TapTweakTag>::engine();
if let Some(h) = merkle_root { // always hash the key
eng.input(h.as_ref()); eng.input(&internal_key.serialize());
} else { if let Some(h) = merkle_root {
// nothing to hash eng.input(h.as_ref());
} else {
// nothing to hash
}
let inner = sha256t::Hash::<TapTweakTag>::from_engine(eng);
TapTweakHash::from_byte_array(inner.to_byte_array())
} }
let inner = sha256t::Hash::<TapTweakTag>::from_engine(eng);
TapTweakHash::from_byte_array(inner.to_byte_array())
}
/// Converts a `TapTweakHash` into a `Scalar` ready for use with key tweaking API. /// Converts a `TapTweakHash` into a `Scalar` ready for use with key tweaking API.
pub fn to_scalar(self) -> Scalar { fn to_scalar(self) -> Scalar {
// This is statistically extremely unlikely to panic. // This is statistically extremely unlikely to panic.
Scalar::from_be_bytes(self.to_byte_array()).expect("hash value greater than curve order") Scalar::from_be_bytes(self.to_byte_array()).expect("hash value greater than curve order")
}
} }
} }
impl TapLeafHash { crate::internal_macros::define_extension_trait! {
/// Computes the leaf hash from components. /// Extension functionality for the [`TapLeafHash`] type.
pub fn from_script(script: &Script, ver: LeafVersion) -> TapLeafHash { pub trait TapLeafHashExt impl for TapLeafHash {
let mut eng = sha256t::Hash::<TapLeafTag>::engine(); /// Computes the leaf hash from components.
ver.to_consensus().consensus_encode(&mut eng).expect("engines don't error"); fn from_script(script: &Script, ver: LeafVersion) -> TapLeafHash {
script.consensus_encode(&mut eng).expect("engines don't error"); let mut eng = sha256t::Hash::<TapLeafTag>::engine();
let inner = sha256t::Hash::<TapTweakTag>::from_engine(eng); ver.to_consensus().consensus_encode(&mut eng).expect("engines don't error");
TapLeafHash::from_byte_array(inner.to_byte_array()) script.consensus_encode(&mut eng).expect("engines don't error");
let inner = sha256t::Hash::<TapTweakTag>::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() } fn from(leaf: &LeafNode) -> TapNodeHash { leaf.node_hash() }
} }
impl TapNodeHash { crate::internal_macros::define_extension_trait! {
/// Computes branch hash given two hashes of the nodes underneath it. /// Extension functionality for the [`TapNodeHash`] type.
pub fn from_node_hashes(a: TapNodeHash, b: TapNodeHash) -> TapNodeHash { pub trait TapNodeHashExt impl for TapNodeHash {
combine_node_hashes(a, b).0 /// 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`]. /// Assumes the given 32 byte array as hidden [`TapNodeHash`].
/// ///
/// Similar to [`TapLeafHash::from_byte_array`], but explicitly conveys that the /// Similar to [`TapLeafHash::from_byte_array`], but explicitly conveys that the
/// hash is constructed from a hidden node. This also has better ergonomics /// hash is constructed from a hidden node. This also has better ergonomics
/// because it does not require the caller to import the Hash trait. /// 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) } fn assume_hidden(hash: [u8; 32]) -> TapNodeHash { TapNodeHash::from_byte_array(hash) }
/// Computes the [`TapNodeHash`] from a script and a leaf version. /// Computes the [`TapNodeHash`] from a script and a leaf version.
pub fn from_script(script: &Script, ver: LeafVersion) -> TapNodeHash { fn from_script(script: &Script, ver: LeafVersion) -> TapNodeHash {
TapNodeHash::from(TapLeafHash::from_script(script, ver)) TapNodeHash::from(TapLeafHash::from_script(script, ver))
}
} }
} }