provekit_whir/hash/
mod.rs1mod blake3_engine;
2mod copy_engine;
3mod digest_engine;
4mod hash_counter;
5
6use core::fmt;
7use std::{
8 borrow::Cow,
9 sync::{Arc, LazyLock},
10};
11
12use const_oid::ObjectIdentifier;
13use serde::{Deserialize, Serialize};
14use static_assertions::{assert_impl_all, assert_obj_safe};
15use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout, Unaligned};
16
17pub use self::{
18 blake3_engine::{Blake3, BLAKE3},
19 copy_engine::{Copy, COPY},
20 digest_engine::{DigestEngine, Keccak, Sha2, Sha3, KECCAK, SHA2, SHA3},
21 hash_counter::HASH_COUNTER,
22};
23use crate::{
24 engines::{self, EngineId, Engines},
25 transcript::{Encoding, NargDeserialize, ProverMessage, VerificationError, VerificationResult},
26};
27
28pub static ENGINES: LazyLock<Engines<dyn HashEngine>> = LazyLock::new(|| {
29 let engines = Engines::<dyn HashEngine>::new();
30 engines.register(Arc::new(Copy::new()));
31 engines.register(Arc::new(Sha2::new()));
32 engines.register(Arc::new(Keccak::new()));
33 engines.register(Arc::new(Sha3::new()));
34 engines.register(Arc::new(Blake3::detect()));
35 engines
36});
37
38#[derive(
39 Clone,
40 Copy,
41 PartialEq,
42 Eq,
43 PartialOrd,
44 Ord,
45 Hash,
46 Default,
47 Serialize,
48 Deserialize,
49 KnownLayout,
50 Immutable,
51 Unaligned,
52 FromBytes,
53 IntoBytes,
54)]
55#[repr(transparent)]
56pub struct Hash(pub [u8; 32]);
57
58pub trait HashEngine: Send + Sync {
59 fn name(&self) -> Cow<'_, str>;
60
61 fn oid(&self) -> Option<ObjectIdentifier> {
62 None
63 }
64
65 fn supports_size(&self, size: usize) -> bool;
67
68 fn preferred_batch_size(&self) -> usize {
76 1
77 }
78
79 fn hash_many(&self, size: usize, input: &[u8], output: &mut [Hash]);
86}
87
88impl<E: HashEngine + ?Sized> engines::Engine for E {
89 fn engine_id(&self) -> EngineId {
90 use digest::Digest;
91 use sha3::Sha3_256;
92
93 let mut hasher = Sha3_256::new();
94 hasher.update(b"whir::hash");
95 if let Some(oid) = self.oid() {
96 hasher.update(oid.as_bytes());
97 } else {
98 hasher.update(self.name().as_bytes());
99 }
100 let hash: [u8; 32] = hasher.finalize().into();
101 hash.into()
102 }
103}
104
105assert_obj_safe!(HashEngine);
106
107impl fmt::Debug for Hash {
108 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
109 for byte in self.0 {
110 write!(f, "{byte:02x}")?;
111 }
112 Ok(())
113 }
114}
115
116impl Encoding<[u8]> for Hash {
117 fn encode(&self) -> impl AsRef<[u8]> {
118 self.as_bytes()
119 }
120}
121
122impl NargDeserialize for Hash {
123 fn deserialize_from_narg(buf: &mut &[u8]) -> VerificationResult<Self> {
124 let (hash, tail) = Self::read_from_prefix(buf).map_err(|_| VerificationError)?;
125 *buf = tail;
126 Ok(hash)
127 }
128}
129
130assert_impl_all!(Hash: ProverMessage);
131
132#[cfg(test)]
133pub(crate) mod tests {
134 use proptest::{sample::select, strategy::Strategy};
135
136 use super::*;
137 use crate::{
138 engines::EngineId,
139 hash::{BLAKE3, KECCAK},
140 };
141
142 const HASHES: [EngineId; 5] = [COPY, SHA2, SHA3, KECCAK, BLAKE3];
143
144 pub fn hash_for_size(size: usize) -> impl Strategy<Value = EngineId> {
145 let suitable = HASHES
146 .iter()
147 .copied()
148 .filter(|h| ENGINES.retrieve(*h).is_some_and(|h| h.supports_size(size)))
149 .collect::<Vec<_>>();
150 select(suitable)
151 }
152}