use super::{Digest, ElementHasher, Felt, FieldElement, Hasher, StarkField}; use crate::utils::{ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable}; use core::{ mem::{size_of, transmute, transmute_copy}, ops::Deref, slice::from_raw_parts, }; #[cfg(test)] mod tests; // CONSTANTS // ================================================================================================ const DIGEST32_BYTES: usize = 32; const DIGEST24_BYTES: usize = 24; const DIGEST20_BYTES: usize = 20; // BLAKE3 N-BIT OUTPUT // ================================================================================================ /// N-bytes output of a blake3 function. /// /// Note: `N` can't be greater than `32` because [`Digest::as_bytes`] currently supports only 32 /// bytes. #[derive(Debug, Copy, Clone, Eq, PartialEq)] pub struct Blake3Digest([u8; N]); impl Default for Blake3Digest { fn default() -> Self { Self([0; N]) } } impl Deref for Blake3Digest { type Target = [u8]; fn deref(&self) -> &Self::Target { &self.0 } } impl From> for [u8; N] { fn from(value: Blake3Digest) -> Self { value.0 } } impl From<[u8; N]> for Blake3Digest { fn from(value: [u8; N]) -> Self { Self(value) } } impl Serializable for Blake3Digest { fn write_into(&self, target: &mut W) { target.write_bytes(&self.0); } } impl Deserializable for Blake3Digest { fn read_from(source: &mut R) -> Result { source.read_array().map(Self) } } impl Digest for Blake3Digest { fn as_bytes(&self) -> [u8; 32] { // compile-time assertion assert!(N <= 32, "digest currently supports only 32 bytes!"); expand_bytes(&self.0) } } // BLAKE3 256-BIT OUTPUT // ================================================================================================ /// 256-bit output blake3 hasher. #[derive(Debug, Copy, Clone, Eq, PartialEq)] pub struct Blake3_256; impl Hasher for Blake3_256 { /// Blake3 collision resistance is 128-bits for 32-bytes output. const COLLISION_RESISTANCE: u32 = 128; type Digest = Blake3Digest<32>; fn hash(bytes: &[u8]) -> Self::Digest { Blake3Digest(blake3::hash(bytes).into()) } fn merge(values: &[Self::Digest; 2]) -> Self::Digest { Self::hash(prepare_merge(values)) } fn merge_with_int(seed: Self::Digest, value: u64) -> Self::Digest { let mut hasher = blake3::Hasher::new(); hasher.update(&seed.0); hasher.update(&value.to_le_bytes()); Blake3Digest(hasher.finalize().into()) } } impl ElementHasher for Blake3_256 { type BaseField = Felt; fn hash_elements(elements: &[E]) -> Self::Digest where E: FieldElement, { Blake3Digest(hash_elements(elements)) } } impl Blake3_256 { /// Returns a hash of the provided sequence of bytes. #[inline(always)] pub fn hash(bytes: &[u8]) -> Blake3Digest { ::hash(bytes) } /// Returns a hash of two digests. This method is intended for use in construction of /// Merkle trees and verification of Merkle paths. #[inline(always)] pub fn merge(values: &[Blake3Digest; 2]) -> Blake3Digest { ::merge(values) } /// Returns a hash of the provided field elements. #[inline(always)] pub fn hash_elements(elements: &[E]) -> Blake3Digest where E: FieldElement, { ::hash_elements(elements) } } // BLAKE3 192-BIT OUTPUT // ================================================================================================ /// 192-bit output blake3 hasher. #[derive(Debug, Copy, Clone, Eq, PartialEq)] pub struct Blake3_192; impl Hasher for Blake3_192 { /// Blake3 collision resistance is 96-bits for 24-bytes output. const COLLISION_RESISTANCE: u32 = 96; type Digest = Blake3Digest<24>; fn hash(bytes: &[u8]) -> Self::Digest { Blake3Digest(*shrink_bytes(&blake3::hash(bytes).into())) } fn merge(values: &[Self::Digest; 2]) -> Self::Digest { Self::hash(prepare_merge(values)) } fn merge_with_int(seed: Self::Digest, value: u64) -> Self::Digest { let mut hasher = blake3::Hasher::new(); hasher.update(&seed.0); hasher.update(&value.to_le_bytes()); Blake3Digest(*shrink_bytes(&hasher.finalize().into())) } } impl ElementHasher for Blake3_192 { type BaseField = Felt; fn hash_elements(elements: &[E]) -> Self::Digest where E: FieldElement, { Blake3Digest(hash_elements(elements)) } } impl Blake3_192 { /// Returns a hash of the provided sequence of bytes. #[inline(always)] pub fn hash(bytes: &[u8]) -> Blake3Digest { ::hash(bytes) } /// Returns a hash of two digests. This method is intended for use in construction of /// Merkle trees and verification of Merkle paths. #[inline(always)] pub fn merge(values: &[Blake3Digest; 2]) -> Blake3Digest { ::merge(values) } /// Returns a hash of the provided field elements. #[inline(always)] pub fn hash_elements(elements: &[E]) -> Blake3Digest where E: FieldElement, { ::hash_elements(elements) } } // BLAKE3 160-BIT OUTPUT // ================================================================================================ /// 160-bit output blake3 hasher. #[derive(Debug, Copy, Clone, Eq, PartialEq)] pub struct Blake3_160; impl Hasher for Blake3_160 { /// Blake3 collision resistance is 80-bits for 20-bytes output. const COLLISION_RESISTANCE: u32 = 80; type Digest = Blake3Digest<20>; fn hash(bytes: &[u8]) -> Self::Digest { Blake3Digest(*shrink_bytes(&blake3::hash(bytes).into())) } fn merge(values: &[Self::Digest; 2]) -> Self::Digest { Self::hash(prepare_merge(values)) } fn merge_with_int(seed: Self::Digest, value: u64) -> Self::Digest { let mut hasher = blake3::Hasher::new(); hasher.update(&seed.0); hasher.update(&value.to_le_bytes()); Blake3Digest(*shrink_bytes(&hasher.finalize().into())) } } impl ElementHasher for Blake3_160 { type BaseField = Felt; fn hash_elements(elements: &[E]) -> Self::Digest where E: FieldElement, { Blake3Digest(hash_elements(elements)) } } impl Blake3_160 { /// Returns a hash of the provided sequence of bytes. #[inline(always)] pub fn hash(bytes: &[u8]) -> Blake3Digest { ::hash(bytes) } /// Returns a hash of two digests. This method is intended for use in construction of /// Merkle trees and verification of Merkle paths. #[inline(always)] pub fn merge(values: &[Blake3Digest; 2]) -> Blake3Digest { ::merge(values) } /// Returns a hash of the provided field elements. #[inline(always)] pub fn hash_elements(elements: &[E]) -> Blake3Digest where E: FieldElement, { ::hash_elements(elements) } } // HELPER FUNCTIONS // ================================================================================================ /// Zero-copy ref shrink to array. fn shrink_bytes(bytes: &[u8; M]) -> &[u8; N] { // compile-time assertion assert!( M >= N, "N should fit in M so it can be safely transmuted into a smaller slice!" ); // safety: bytes len is asserted unsafe { transmute(bytes) } } /// Hash the elements into bytes and shrink the output. fn hash_elements(elements: &[E]) -> [u8; N] where E: FieldElement, { // don't leak assumptions from felt and check its actual implementation. // this is a compile-time branch so it is for free let digest = if Felt::IS_CANONICAL { blake3::hash(E::elements_as_bytes(elements)) } else { let mut hasher = blake3::Hasher::new(); // BLAKE3 state is 64 bytes - so, we can absorb 64 bytes into the state in a single // permutation. we move the elements into the hasher via the buffer to give the CPU // a chance to process multiple element-to-byte conversions in parallel let mut buf = [0_u8; 64]; let mut chunk_iter = E::slice_as_base_elements(elements).chunks_exact(8); for chunk in chunk_iter.by_ref() { for i in 0..8 { buf[i * 8..(i + 1) * 8].copy_from_slice(&chunk[i].as_int().to_le_bytes()); } hasher.update(&buf); } for element in chunk_iter.remainder() { hasher.update(&element.as_int().to_le_bytes()); } hasher.finalize() }; *shrink_bytes(&digest.into()) } /// Owned bytes expansion. fn expand_bytes(bytes: &[u8; M]) -> [u8; N] { // compile-time assertion assert!(M <= N, "M should fit in N so M can be expanded!"); // this branch is constant so it will be optimized to be either one of the variants in release // mode if M == N { // safety: the sizes are checked to be the same unsafe { transmute_copy(bytes) } } else { let mut expanded = [0u8; N]; expanded[..M].copy_from_slice(bytes); expanded } } // Cast the slice into contiguous bytes. fn prepare_merge(args: &[D; N]) -> &[u8] where D: Deref, { // compile-time assertion assert!(N > 0, "N shouldn't represent an empty slice!"); let values = args.as_ptr() as *const u8; let len = size_of::() * N; // safety: the values are tested to be contiguous let bytes = unsafe { from_raw_parts(values, len) }; debug_assert_eq!(args[0].deref(), &bytes[..len / N]); bytes }