diff --git a/src/jet/type_name.rs b/src/jet/type_name.rs index 5ef9f32e..a2601b0e 100644 --- a/src/jet/type_name.rs +++ b/src/jet/type_name.rs @@ -5,6 +5,7 @@ //! Source and target types of jet nodes need to be specified manually. use crate::types::{self, Final, Type}; +use crate::Tmr; use std::cmp; use std::sync::Arc; @@ -30,6 +31,18 @@ use std::sync::Arc; #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] pub struct TypeName(pub &'static [u8]); +impl PartialEq for TypeName { + fn eq(&self, other: &Final) -> bool { + self.tmr() == other.tmr() + } +} + +impl PartialEq for Final { + fn eq(&self, other: &TypeName) -> bool { + self.tmr() == other.tmr() + } +} + impl TypeName { /// Convert the type name into a type. pub fn to_type<'brand>(&self, ctx: &types::Context<'brand>) -> Type<'brand> { @@ -70,6 +83,40 @@ impl TypeName { } } + /// Compute the TMR of the type name. + pub fn tmr(&self) -> Tmr { + let mut stack = Vec::with_capacity(16); + + for c in self.0.iter().rev() { + match c { + b'1' => stack.push(Tmr::unit()), + b'2' => stack.push(Tmr::TWO_TWO_N[0]), + b'c' => stack.push(Tmr::TWO_TWO_N[3]), + b's' => stack.push(Tmr::TWO_TWO_N[4]), + b'i' => stack.push(Tmr::TWO_TWO_N[5]), + b'l' => stack.push(Tmr::TWO_TWO_N[6]), + b'h' => stack.push(Tmr::TWO_TWO_N[8]), + b'+' | b'*' => { + let left = stack.pop().expect("Illegal type name syntax!"); + let right = stack.pop().expect("Illegal type name syntax!"); + + match c { + b'+' => stack.push(Tmr::sum(left, right)), + b'*' => stack.push(Tmr::product(left, right)), + _ => unreachable!(), + } + } + _ => panic!("Illegal type name syntax!"), + } + } + + if stack.len() == 1 { + stack.pop().unwrap() + } else { + panic!("Illegal type name syntax!") + } + } + /// Convert the type name into a type's bitwidth. /// /// This is more efficient than creating the type and computing its bit-width @@ -106,3 +153,26 @@ impl TypeName { } } } + +#[cfg(test)] +mod tests { + use crate::jet::{Core, Jet}; + + #[test] + fn all_jet_tmrs() { + for jet in &Core::ALL { + let source_final = jet.source_ty().to_final(); + let target_final = jet.target_ty().to_final(); + + assert_eq!(*source_final, jet.source_ty()); + assert_eq!(jet.source_ty(), *source_final); + assert_eq!(source_final.tmr(), jet.source_ty().tmr()); + assert_eq!(source_final.bit_width(), jet.source_ty().to_bit_width()); + + assert_eq!(*target_final, jet.target_ty()); + assert_eq!(jet.target_ty(), *target_final); + assert_eq!(target_final.tmr(), jet.target_ty().tmr()); + assert_eq!(target_final.bit_width(), jet.target_ty().to_bit_width()); + } + } +} diff --git a/src/merkle/cmr.rs b/src/merkle/cmr.rs index 8aebca84..181bc704 100644 --- a/src/merkle/cmr.rs +++ b/src/merkle/cmr.rs @@ -32,13 +32,13 @@ impl From for Cmr { impl Cmr { /// Produce a CMR for an iden combinator - pub fn iden() -> Self { - Self::IDEN_IV.into_merkle_root() + pub const fn iden() -> Self { + Self::from_byte_array(Self::IDEN_IV.to_parts().0) } /// Produce a CMR for a unit combinator - pub fn unit() -> Self { - Self::UNIT_IV.into_merkle_root() + pub const fn unit() -> Self { + Self::from_byte_array(Self::UNIT_IV.to_parts().0) } /// Produce a CMR for an injl combinator @@ -84,8 +84,8 @@ impl Cmr { } /// Produce a CMR for a witness combinator - pub fn witness() -> Self { - Self::WITNESS_IV.into_merkle_root() + pub const fn witness() -> Self { + Self::from_byte_array(Self::WITNESS_IV.to_parts().0) } /// Produce a CMR for a fail combinator diff --git a/src/merkle/ihr.rs b/src/merkle/ihr.rs index bd5e8d04..14c1d4fe 100644 --- a/src/merkle/ihr.rs +++ b/src/merkle/ihr.rs @@ -49,13 +49,13 @@ impl From for Ihr { impl Imr { /// Produce a CMR for an iden combinator - pub fn iden() -> Self { - Self::IDEN_IV.into_merkle_root() + pub const fn iden() -> Self { + Self::from_byte_array(Self::IDEN_IV.to_parts().0) } /// Produce a CMR for a unit combinator - pub fn unit() -> Self { - Self::UNIT_IV.into_merkle_root() + pub const fn unit() -> Self { + Self::from_byte_array(Self::UNIT_IV.to_parts().0) } /// Produce a CMR for an injl combinator diff --git a/src/merkle/mod.rs b/src/merkle/mod.rs index 10353022..5481fded 100644 --- a/src/merkle/mod.rs +++ b/src/merkle/mod.rs @@ -33,12 +33,12 @@ impl FailEntropy { pub const ZERO: Self = FailEntropy([0; 64]); /// Construct a [`FailEntropy`] from raw data - pub fn from_byte_array(data: [u8; 64]) -> Self { + pub const fn from_byte_array(data: [u8; 64]) -> Self { FailEntropy(data) } /// Extract the raw bytes from a [`FailEntropy`]. - pub fn to_byte_array(self) -> [u8; 64] { + pub const fn to_byte_array(self) -> [u8; 64] { self.0 } } @@ -101,7 +101,7 @@ fn compact_value(value: &Value) -> [u8; 32] { .0 } -fn bip340_iv(tag: &[u8]) -> sha256::Midstate { +const fn bip340_iv(tag: &[u8]) -> sha256::Midstate { sha256::Midstate::hash_tag(tag) } @@ -146,14 +146,19 @@ macro_rules! impl_mr_type { impl $wrapper { /// Converts the given tagged hash into a byte array - pub fn from_byte_array(data: [u8; 32]) -> Self { + pub const fn from_byte_array(data: [u8; 32]) -> Self { $wrapper(data) } /// Converts the given tagged hash into a byte array - pub fn to_byte_array(self) -> [u8; 32] { + pub const fn to_byte_array(self) -> [u8; 32] { self.0 } + + /// Converts the given tagged hash into a byte array + pub const fn as_byte_array(&self) -> &[u8; 32] { + &self.0 + } } impl_serde_string!($wrapper); diff --git a/src/merkle/tmr.rs b/src/merkle/tmr.rs index f049beb7..28e40a76 100644 --- a/src/merkle/tmr.rs +++ b/src/merkle/tmr.rs @@ -347,8 +347,8 @@ impl Tmr { ]); /// The TMR for the unit type - pub fn unit() -> Tmr { - Self::UNIT_IV.into_merkle_root() + pub const fn unit() -> Tmr { + Self::from_byte_array(Self::UNIT_IV.to_parts().0) } /// The TMR for the successor of a type. diff --git a/src/types/arrow.rs b/src/types/arrow.rs index cfac4aea..09005e22 100644 --- a/src/types/arrow.rs +++ b/src/types/arrow.rs @@ -314,7 +314,7 @@ impl<'brand> CoreConstructible<'brand> for Arrow<'brand> { fn const_word(inference_context: &Context<'brand>, word: Word) -> Self { Arrow { source: Type::unit(inference_context), - target: Type::two_two_n(inference_context, word.n() as usize), // cast safety: 32-bit machine or higher + target: Type::two_two_n(inference_context, word.n()), inference_context: inference_context.shallow_clone(), } } diff --git a/src/types/final_data.rs b/src/types/final_data.rs index bec8240b..cde475c5 100644 --- a/src/types/final_data.rs +++ b/src/types/final_data.rs @@ -342,10 +342,8 @@ impl Final { /// ## Post condition /// /// 0 ≤ n < 32. - pub fn as_word(&self) -> Option { - (0..32u32).find(|&n| { - self.tmr == Tmr::TWO_TWO_N[n as usize] // cast safety: 32-bit machine or higher - }) + pub fn as_word(&self) -> Option { + (0u8..32).find(|&n| self.tmr == Tmr::TWO_TWO_N[usize::from(n)]) } /// Compute the padding of left values of the sum type `Self + Other`. diff --git a/src/value.rs b/src/value.rs index 649c3efd..e430c374 100644 --- a/src/value.rs +++ b/src/value.rs @@ -7,9 +7,11 @@ use crate::dag::{Dag, DagLike}; use crate::types::{CompleteBound, Final}; +use crate::{BitCollector, EarlyEndOfStreamError}; use crate::{BitIter, Tmr}; -use crate::{BitCollector, EarlyEndOfStreamError}; +use hashes::sha256; + use core::{cmp, fmt, iter}; use std::collections::VecDeque; use std::hash::Hash; @@ -497,6 +499,48 @@ impl Value { }) } + /// Constructs a `Ctx8` from raw parts. + /// + /// Bear in mind that the count is a count of **bytes**, not a count of **bits**. For the + /// context to be valid (i.e. something that could be obtained by a sequence of hashing + /// operations), `bytes_hashed % 64` must equal `buffer.len()`. + /// + /// You may want to obtain a hash engine and call [`Self::ctx8_from_hash_engine`] instead. + /// + /// # Errors + /// + /// Errors if the `buffer` exceeds the maximum length of a sha256 buffer (63 bytes). + pub fn ctx8( + midstate: [u8; 32], + bytes_hashed: u64, + buffer: &[u8], + ) -> Result { + Ok(Self::product( + Self::buffer8_two_n_plus_one(5, buffer)?, + Self::product(Self::u64(bytes_hashed), Self::u256(midstate)), + )) + } + + /// Converts a SHA256 midstate into a Simplicity value. + pub fn ctx8_from_midstate(midstate: sha256::Midstate) -> Self { + let (hash, n) = midstate.to_parts(); + Self::ctx8(hash, n, &[]).expect("0 < 64") + } + + /// Converts a SHA256 engine into a Simplicity value. + pub fn ctx8_from_hash_engine(engine: &sha256::HashEngine) -> Self { + match engine.midstate() { + Ok(midstate) => { + let (hash, n) = midstate.to_parts(); + Self::ctx8(hash, n, &[]).expect("0 < 64") + } + Err(midstate_err) => { + let (hash, n) = midstate_err.midstate().to_parts(); + Self::ctx8(hash, n, midstate_err.unprocessed_bytes()).expect("< 64") + } + } + } + /// Return the bit length of the value in compact encoding. pub fn compact_len(&self) -> usize { self.iter_compact().count() @@ -1012,7 +1056,7 @@ pub struct Word { /// Value of type `TWO^(2^n)`. value: Value, /// 0 ≤ n < 32. - n: u32, + n: u8, } macro_rules! construct_word_fallible { @@ -1091,8 +1135,8 @@ impl Word { } /// The word is of type `TWO^(2^n)`. Return `n`. - pub fn n(&self) -> u32 { - self.n + pub fn n(&self) -> usize { + usize::from(self.n) } /// Return the bit length of the word. @@ -1100,7 +1144,7 @@ impl Word { /// The word is of type `TWO^(2^n)`. Return `2^n`. #[allow(clippy::len_without_is_empty)] pub fn len(&self) -> usize { - 2usize.pow(self.n) + 2usize.pow(u32::from(self.n)) } /// Return an iterator over the bit encoding of the word. @@ -1120,12 +1164,12 @@ impl Word { bits: &mut BitIter, n: u32, ) -> Result { - let nsize = usize::try_from(n).unwrap_or(usize::MAX); // usize::MAX will error on next line - let Ok(ty) = Final::two_two_n(nsize) else { + let n8 = u8::try_from(n).unwrap_or(u8::MAX); // u8::MAX will error on next line + let Ok(ty) = Final::two_two_n(usize::from(n8)) else { panic!("TWO^(2^{n}) is not supported as a word type"); }; let value = Value::from_compact_bits(bits, &ty)?; - Ok(Self { value, n }) + Ok(Self { value, n: n8 }) } }