miden_core/program/
kernel.rs1use alloc::{string::ToString, vec::Vec};
2
3use miden_crypto::{Felt, Word};
4#[cfg(feature = "serde")]
5use serde::{Deserialize, Serialize};
6
7use crate::{
8 chiplets::hasher,
9 serde::{ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable},
10};
11
12#[derive(Debug, Clone, Default, PartialEq, Eq)]
20#[cfg_attr(feature = "serde", derive(Serialize))]
21#[cfg_attr(feature = "serde", serde(transparent))]
22#[cfg_attr(
23 all(feature = "arbitrary", test),
24 miden_test_serde_macros::serde_test(binary_serde(true))
25)]
26pub struct Kernel(Vec<Word>);
27
28impl Kernel {
29 pub const MAX_NUM_PROCEDURES: usize = u8::MAX as usize;
31
32 pub fn new(proc_hashes: &[Word]) -> Result<Self, KernelError> {
41 Self::from_hashes(proc_hashes.to_vec())
42 }
43
44 pub fn from_hashes(mut hashes: Vec<Word>) -> Result<Self, KernelError> {
53 if hashes.len() > Self::MAX_NUM_PROCEDURES {
54 return Err(KernelError::TooManyProcedures(Self::MAX_NUM_PROCEDURES, hashes.len()));
55 }
56
57 hashes.sort_by_key(Word::as_bytes); let duplicated = hashes.windows(2).any(|data| data[0] == data[1]);
61
62 if duplicated {
63 Err(KernelError::DuplicatedProcedures)
64 } else {
65 Ok(Self(hashes))
66 }
67 }
68
69 #[cfg(test)]
73 pub(crate) fn from_hashes_unchecked(hashes: Vec<Word>) -> Self {
74 Self(hashes)
75 }
76
77 pub fn is_empty(&self) -> bool {
79 self.0.is_empty()
80 }
81
82 pub fn contains_proc(&self, proc_hash: Word) -> bool {
86 self.0.contains(&proc_hash)
89 }
90
91 pub fn proc_hashes(&self) -> &[Word] {
93 &self.0
94 }
95
96 pub fn commitment(&self) -> Word {
102 let elements: Vec<Felt> = self.0.iter().flat_map(Word::as_elements).copied().collect();
103 hasher::hash_elements(&elements)
104 }
105}
106
107impl Serializable for Kernel {
109 fn write_into<W: ByteWriter>(&self, target: &mut W) {
110 target.write_u8(self.0.len().try_into().expect("too many kernel procedures"));
112 target.write_many(&self.0)
113 }
114}
115
116impl Deserializable for Kernel {
117 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
118 let len = source.read_u8()? as usize;
119 let kernel = source.read_many_iter::<Word>(len)?.collect::<Result<_, _>>()?;
120 Self::from_hashes(kernel).map_err(|err| DeserializationError::InvalidValue(err.to_string()))
121 }
122}
123
124#[cfg(feature = "serde")]
125impl<'de> Deserialize<'de> for Kernel {
126 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
127 where
128 D: serde::Deserializer<'de>,
129 {
130 let kernel = Vec::<Word>::deserialize(deserializer)?;
131 Self::from_hashes(kernel).map_err(serde::de::Error::custom)
132 }
133}
134
135#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
139pub enum KernelError {
140 #[error("kernel cannot have duplicated procedures")]
141 DuplicatedProcedures,
142 #[error("kernel can have at most {0} procedures, received {1}")]
143 TooManyProcedures(usize, usize),
144}
145
146#[cfg(test)]
147mod tests {
148 use alloc::vec::Vec;
149
150 use super::Kernel;
151 use crate::{
152 Felt, Word,
153 serde::{ByteWriter, Deserializable, Serializable, SliceReader},
154 };
155
156 #[test]
157 fn empty_kernel_commitment_matches_hash_of_no_elements() {
158 let empty = Kernel::default();
161 assert_eq!(empty.commitment(), crate::chiplets::hasher::hash_elements(&[]));
162 }
163
164 #[test]
165 fn kernel_commitment_is_independent_of_procedure_order() {
166 let a: Word = [
167 Felt::new_unchecked(1),
168 Felt::new_unchecked(2),
169 Felt::new_unchecked(3),
170 Felt::new_unchecked(4),
171 ]
172 .into();
173 let b: Word = [
174 Felt::new_unchecked(5),
175 Felt::new_unchecked(6),
176 Felt::new_unchecked(7),
177 Felt::new_unchecked(8),
178 ]
179 .into();
180
181 let in_order = Kernel::new(&[a, b]).unwrap();
184 let reversed = Kernel::new(&[b, a]).unwrap();
185 assert_eq!(in_order.commitment(), reversed.commitment());
186 }
187
188 #[test]
189 fn kernel_read_from_rejects_duplicate_procedure_hashes() {
190 let a: Word = [
191 Felt::new_unchecked(1),
192 Felt::new_unchecked(2),
193 Felt::new_unchecked(3),
194 Felt::new_unchecked(4),
195 ]
196 .into();
197 let b: Word = [
198 Felt::new_unchecked(5),
199 Felt::new_unchecked(6),
200 Felt::new_unchecked(7),
201 Felt::new_unchecked(8),
202 ]
203 .into();
204
205 assert!(
206 Kernel::new(&[a, a]).is_err(),
207 "test precondition: Kernel::new must reject duplicates"
208 );
209
210 let mut bytes = Vec::new();
213 bytes.write_u8(3);
214 b.write_into(&mut bytes);
215 a.write_into(&mut bytes);
216 a.write_into(&mut bytes);
217
218 let mut reader = SliceReader::new(&bytes);
219 let result = Kernel::read_from(&mut reader);
220
221 assert!(
222 result.is_err(),
223 "expected Kernel::read_from to reject duplicate procedure hashes"
224 );
225 }
226
227 #[cfg(feature = "serde")]
228 #[test]
229 fn kernel_serde_deserialisation_rejects_duplicate_procedure_hashes() {
230 let a: Word = [
231 Felt::new_unchecked(1),
232 Felt::new_unchecked(2),
233 Felt::new_unchecked(3),
234 Felt::new_unchecked(4),
235 ]
236 .into();
237
238 assert!(
239 Kernel::new(&[a, a]).is_err(),
240 "test precondition: Kernel::new must reject duplicates"
241 );
242
243 let json = serde_json::to_string(&vec![a, a]).unwrap();
245 let result: Result<Kernel, _> = serde_json::from_str(&json);
246 assert!(
247 result.is_err(),
248 "expected serde deserialization to reject duplicate procedure hashes"
249 );
250 }
251
252 #[cfg(feature = "serde")]
253 #[test]
254 fn kernel_serde_deserialisation_rejects_too_many_procedure_hashes() {
255 let proc_hashes: Vec<Word> = (0u64..=255)
256 .map(|n| {
257 [
258 Felt::new_unchecked(n),
259 Felt::new_unchecked(n + 1),
260 Felt::new_unchecked(n + 2),
261 Felt::new_unchecked(n + 3),
262 ]
263 .into()
264 })
265 .collect();
266
267 let json = serde_json::to_string(&proc_hashes).unwrap();
268 let result: Result<Kernel, _> = serde_json::from_str(&json);
269 assert!(
270 result.is_err(),
271 "expected serde deserialization to reject more than MAX_NUM_PROCEDURES hashes"
272 );
273 }
274}