Merge rust-bitcoin/rust-bitcoin#3500: Move `taproot` hash types to `primitives`

1b521dce99 Move taproot hash types to primitives (Tobin C. Harding)
24e944ed82 Introduce taproot hash type extension traits (Tobin C. Harding)
c30ef617fb Move combine_node_hashes out of TapNodeHash (Tobin C. Harding)

Pull request description:

  Move the three `taproot` hash types and their associated tags over to the `primitives` crate.

  First two patches are preparation, third on is the move - this one is a no-brainer.

ACKs for top commit:
  apoelstra:
    ACK 1b521dce99db1900326eec29c47445f522f94ee6; successfully ran local tests; nice! will one-ACK merge

Tree-SHA512: 076f017dad6263144ac1a7e0e0251cfb5f1790898ec1d344107471875fc73157116c04f698ff8a02f1b17dee5306b5054d10c990abc84d8098de9ff74f8fc302
This commit is contained in:
merge-script 2024-10-23 02:58:26 +00:00
commit 9f598063c3
No known key found for this signature in database
GPG Key ID: C588D63CE41B97C1
6 changed files with 130 additions and 102 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

@ -11,7 +11,7 @@ use core::cmp::Reverse;
use core::fmt; use core::fmt;
use core::iter::FusedIterator; 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 internals::{impl_to_hex_from_lower_hex, write_err};
use io::Write; use io::Write;
use secp256k1::{Scalar, Secp256k1}; use secp256k1::{Scalar, Secp256k1};
@ -27,45 +27,14 @@ use crate::{Script, ScriptBuf};
pub use crate::crypto::taproot::{SigFromSliceError, Signature}; pub use crate::crypto::taproot::{SigFromSliceError, Signature};
#[doc(inline)] #[doc(inline)]
pub use merkle_branch::TaprootMerkleBranch; 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 crate::internal_macros::define_extension_trait! {
sha256t_tag! { /// Extension functionality for the [`TapTweakHash`] type.
pub struct TapLeafTag = hash_str("TapLeaf"); pub trait TapTweakHashExt impl for TapTweakHash {
}
hash_newtype! {
/// Taproot-tagged hash with tag \"TapLeaf\".
///
/// This is used for computing tapscript script spend hash.
pub struct TapLeafHash(sha256t::Hash<TapLeafTag>);
}
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<TapBranchTag>);
}
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<TapTweakTag>);
}
impl TapTweakHash {
/// Creates a new BIP341 [`TapTweakHash`] from key and tweak. Produces `H_taptweak(P||R)` where /// 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. /// `P` is the internal key and `R` is the Merkle root.
pub fn from_key_and_tweak( fn from_key_and_tweak(
internal_key: UntweakedPublicKey, internal_key: UntweakedPublicKey,
merkle_root: Option<TapNodeHash>, merkle_root: Option<TapNodeHash>,
) -> TapTweakHash { ) -> TapTweakHash {
@ -82,15 +51,18 @@ impl TapTweakHash {
} }
/// 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! {
/// Extension functionality for the [`TapLeafHash`] type.
pub trait TapLeafHashExt impl for TapLeafHash {
/// Computes the leaf hash from components. /// Computes the leaf hash from components.
pub fn from_script(script: &Script, ver: LeafVersion) -> TapLeafHash { fn from_script(script: &Script, ver: LeafVersion) -> TapLeafHash {
let mut eng = sha256t::Hash::<TapLeafTag>::engine(); let mut eng = sha256t::Hash::<TapLeafTag>::engine();
ver.to_consensus().consensus_encode(&mut eng).expect("engines don't error"); ver.to_consensus().consensus_encode(&mut eng).expect("engines don't error");
script.consensus_encode(&mut eng).expect("engines don't error"); script.consensus_encode(&mut eng).expect("engines don't error");
@ -98,6 +70,7 @@ impl TapLeafHash {
TapLeafHash::from_byte_array(inner.to_byte_array()) TapLeafHash::from_byte_array(inner.to_byte_array())
} }
} }
}
impl From<LeafNode> for TapNodeHash { impl From<LeafNode> for TapNodeHash {
fn from(leaf: LeafNode) -> TapNodeHash { leaf.node_hash() } fn from(leaf: LeafNode) -> TapNodeHash { leaf.node_hash() }
@ -107,10 +80,26 @@ 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! {
/// Extension functionality for the [`TapNodeHash`] type.
pub trait TapNodeHashExt impl for TapNodeHash {
/// Computes branch hash given two hashes of the nodes underneath it. /// Computes branch hash given two hashes of the nodes underneath it.
pub fn from_node_hashes(a: TapNodeHash, b: TapNodeHash) -> TapNodeHash { fn from_node_hashes(a: TapNodeHash, b: TapNodeHash) -> TapNodeHash {
Self::combine_node_hashes(a, b).0 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.
fn assume_hidden(hash: [u8; 32]) -> TapNodeHash { TapNodeHash::from_byte_array(hash) }
/// 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))
}
}
} }
/// Computes branch hash given two hashes of the nodes underneath it and returns /// Computes branch hash given two hashes of the nodes underneath it and returns
@ -128,23 +117,6 @@ impl TapNodeHash {
(TapNodeHash::from_byte_array(inner.to_byte_array()), a < b) (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.
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))
}
}
impl From<TapLeafHash> for TapNodeHash {
fn from(leaf: TapLeafHash) -> TapNodeHash { TapNodeHash::from_byte_array(leaf.to_byte_array()) }
}
/// Maximum depth of a Taproot tree script spend path. /// Maximum depth of a Taproot tree script spend path.
// https://github.com/bitcoin/bitcoin/blob/e826b22da252e0599c61d21c98ff89f366b3120f/src/script/interpreter.h#L229 // https://github.com/bitcoin/bitcoin/blob/e826b22da252e0599c61d21c98ff89f366b3120f/src/script/interpreter.h#L229
pub const TAPROOT_CONTROL_MAX_NODE_COUNT: usize = 128; pub const TAPROOT_CONTROL_MAX_NODE_COUNT: usize = 128;
@ -853,7 +825,7 @@ impl NodeInfo {
/// Combines two [`NodeInfo`] to create a new parent. /// Combines two [`NodeInfo`] to create a new parent.
pub fn combine(a: Self, b: Self) -> Result<Self, TaprootBuilderError> { pub fn combine(a: Self, b: Self) -> Result<Self, TaprootBuilderError> {
let mut all_leaves = Vec::with_capacity(a.leaves.len() + b.leaves.len()); 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) }; let (a, b) = if left_first { (a, b) } else { (b, a) };
for mut a_leaf in a.leaves { for mut a_leaf in a.leaves {
a_leaf.merkle_branch.push(b.hash)?; // add hashing partner a_leaf.merkle_branch.push(b.hash)?; // add hashing partner
@ -1545,6 +1517,13 @@ impl fmt::Display for InvalidControlBlockSizeError {
#[cfg(feature = "std")] #[cfg(feature = "std")]
impl std::error::Error for InvalidControlBlockSizeError {} 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)] #[cfg(test)]
mod test { mod test {
use hashes::sha256; use hashes::sha256;
@ -1854,15 +1833,17 @@ mod test {
#[test] #[test]
#[cfg(feature = "serde")] #[cfg(feature = "serde")]
fn test_merkle_branch_serde() { fn test_merkle_branch_serde() {
let hash1 = TapNodeHash( let hash1 = TapNodeHash::from_byte_array(
"03ba2a4dcd914fed29a1c630c7e811271b081a0e2f2f52cf1c197583dfd46c1b" "03ba2a4dcd914fed29a1c630c7e811271b081a0e2f2f52cf1c197583dfd46c1b"
.parse::<sha256t::Hash<TapBranchTag>>() .parse::<sha256t::Hash<TapBranchTag>>()
.unwrap(), .unwrap()
.to_byte_array(),
); );
let hash2 = TapNodeHash( let hash2 = TapNodeHash::from_byte_array(
"8d79dedc2fa0b55167b5d28c61dbad9ce1191a433f3a1a6c8ee291631b2c94c9" "8d79dedc2fa0b55167b5d28c61dbad9ce1191a433f3a1a6c8ee291631b2c94c9"
.parse::<sha256t::Hash<TapBranchTag>>() .parse::<sha256t::Hash<TapBranchTag>>()
.unwrap(), .unwrap()
.to_byte_array(),
); );
let merkle_branch = TaprootMerkleBranch::from([hash1, hash2]); let merkle_branch = TaprootMerkleBranch::from([hash1, hash2]);
// use serde_test to test serialization and deserialization // use serde_test to test serialization and deserialization

View File

@ -39,6 +39,7 @@ pub mod pow;
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
pub mod script; pub mod script;
pub mod sequence; pub mod sequence;
pub mod taproot;
pub mod transaction; pub mod transaction;
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
pub mod witness; pub mod witness;

45
primitives/src/taproot.rs Normal file
View File

@ -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<TapLeafTag>);
}
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<TapBranchTag>);
}
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<TapTweakTag>);
}
impl From<TapLeafHash> for TapNodeHash {
fn from(leaf: TapLeafHash) -> TapNodeHash { TapNodeHash::from_byte_array(leaf.to_byte_array()) }
}