miden_core/program/
kernel.rs1use alloc::{string::ToString, vec::Vec};
2
3use miden_crypto::{Word, hash::eidos::EidosDomain};
4
5use crate::{
6 chiplets::hasher,
7 serde::{ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable},
8};
9
10pub const KERNEL_DOMAIN_TAG: crate::Felt = super::domain::KernelCommitmentDomain::TAG.as_felt();
15
16#[derive(Debug, Clone, Default, PartialEq, Eq)]
24#[cfg_attr(
25 all(feature = "arbitrary", test),
26 miden_test_serialization_macros::serialization_test
27)]
28pub struct KernelDescriptor(Vec<Word>);
29
30impl KernelDescriptor {
31 pub const MAX_NUM_PROCEDURES: usize = u8::MAX as usize;
33
34 pub fn new(proc_hashes: &[Word]) -> Result<Self, KernelError> {
43 Self::from_hashes(proc_hashes.to_vec())
44 }
45
46 pub fn from_hashes(mut hashes: Vec<Word>) -> Result<Self, KernelError> {
55 if hashes.len() > Self::MAX_NUM_PROCEDURES {
56 return Err(KernelError::TooManyProcedures(Self::MAX_NUM_PROCEDURES, hashes.len()));
57 }
58
59 hashes.sort_by_key(Word::as_bytes); let duplicated = hashes.windows(2).any(|data| data[0] == data[1]);
63
64 if duplicated {
65 Err(KernelError::DuplicatedProcedures)
66 } else {
67 Ok(Self(hashes))
68 }
69 }
70
71 #[cfg(test)]
75 pub(crate) fn from_hashes_unchecked(hashes: Vec<Word>) -> Self {
76 Self(hashes)
77 }
78
79 pub fn is_empty(&self) -> bool {
81 self.0.is_empty()
82 }
83
84 pub fn contains_proc(&self, proc_hash: Word) -> bool {
88 self.0.contains(&proc_hash)
91 }
92
93 pub fn proc_hashes(&self) -> &[Word] {
95 &self.0
96 }
97
98 pub fn commitment(&self) -> Word {
107 hasher::hash_elements_in_domain(
108 Word::words_as_elements(&self.0),
109 super::domain::KERNEL_COMMITMENT,
110 )
111 }
112}
113
114impl Serializable for KernelDescriptor {
116 fn write_into<W: ByteWriter>(&self, target: &mut W) {
117 target.write_u8(self.0.len().try_into().expect("too many kernel procedures"));
119 target.write_many(&self.0)
120 }
121}
122
123impl Deserializable for KernelDescriptor {
124 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
125 let len = source.read_u8()? as usize;
126 let kernel = source.read_many_iter::<Word>(len)?.collect::<Result<_, _>>()?;
127 Self::from_hashes(kernel).map_err(|err| DeserializationError::InvalidValue(err.to_string()))
128 }
129}
130
131#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
135pub enum KernelError {
136 #[error("kernel cannot have duplicated procedures")]
137 DuplicatedProcedures,
138 #[error("kernel can have at most {0} procedures, received {1}")]
139 TooManyProcedures(usize, usize),
140}
141
142#[cfg(test)]
143mod tests {
144 use alloc::vec::Vec;
145
146 use super::KernelDescriptor;
147 use crate::{
148 Felt, Word,
149 program::domain,
150 serde::{ByteWriter, Deserializable, Serializable, SliceReader},
151 };
152
153 #[test]
154 fn empty_kernel_commitment_matches_known_vector() {
155 let expected = Word::from(
156 [
157 7_629_329_411_056_522_819,
158 2_158_594_640_249_218_307,
159 769_500_264_184_883_565,
160 419_898_756_627_082_940,
161 ]
162 .map(Felt::new_unchecked),
163 );
164 let empty = KernelDescriptor::default();
165
166 assert_eq!(empty.commitment(), expected);
167 assert_eq!(
168 empty.commitment(),
169 crate::chiplets::hasher::hash_elements_in_domain(&[], domain::KERNEL_COMMITMENT)
170 );
171 }
172
173 #[test]
174 fn kernel_commitment_is_independent_of_procedure_order() {
175 let a: Word = [
176 Felt::new_unchecked(1),
177 Felt::new_unchecked(2),
178 Felt::new_unchecked(3),
179 Felt::new_unchecked(4),
180 ]
181 .into();
182 let b: Word = [
183 Felt::new_unchecked(5),
184 Felt::new_unchecked(6),
185 Felt::new_unchecked(7),
186 Felt::new_unchecked(8),
187 ]
188 .into();
189
190 let in_order = KernelDescriptor::new(&[a, b]).unwrap();
193 let reversed = KernelDescriptor::new(&[b, a]).unwrap();
194 assert_eq!(in_order.commitment(), reversed.commitment());
195 }
196
197 #[test]
198 fn kernel_read_from_rejects_duplicate_procedure_hashes() {
199 let a: Word = [
200 Felt::new_unchecked(1),
201 Felt::new_unchecked(2),
202 Felt::new_unchecked(3),
203 Felt::new_unchecked(4),
204 ]
205 .into();
206 let b: Word = [
207 Felt::new_unchecked(5),
208 Felt::new_unchecked(6),
209 Felt::new_unchecked(7),
210 Felt::new_unchecked(8),
211 ]
212 .into();
213
214 assert!(
215 KernelDescriptor::new(&[a, a]).is_err(),
216 "test precondition: KernelDescriptor::new must reject duplicates"
217 );
218
219 let mut bytes = Vec::new();
223 bytes.write_u8(3);
224 b.write_into(&mut bytes);
225 a.write_into(&mut bytes);
226 a.write_into(&mut bytes);
227
228 let mut reader = SliceReader::new(&bytes);
229 let result = KernelDescriptor::read_from(&mut reader);
230
231 assert!(
232 result.is_err(),
233 "expected KernelDescriptor::read_from to reject duplicate procedure hashes"
234 );
235 }
236}