Skip to main content

miden_protocol/protocol_config/
kernel_config.rs

1use alloc::string::ToString;
2use alloc::vec::Vec;
3
4use miden_verifier::KernelDescriptor;
5
6use super::ProtocolConfigError;
7use crate::utils::serde::{
8    ByteReader,
9    ByteWriter,
10    Deserializable,
11    DeserializationError,
12    Serializable,
13};
14use crate::{Felt, Hasher, Word};
15
16// KERNEL CONFIG
17// ================================================================================================
18
19/// The configuration of one of the protocol's kernels.
20///
21/// A kernel is identified by the root of its executable procedure together with the set of
22/// procedures it exposes through its API. Both are needed by other kernels: the batch kernel
23/// verifies transaction proofs against the transaction kernel's `main_proc`, while the exposed
24/// procedure roots are what users may invoke.
25///
26/// `kernel_procs` is the set of kernel procedures exposed via index-based invocation, which is
27/// different from [`KernelDescriptor::proc_hashes`], which is the set of statically invocable
28/// kernel procedures (e.g. `exec_kernel_proc`).
29#[derive(Debug, Clone, PartialEq, Eq)]
30pub struct KernelConfig {
31    /// The root of the executable kernel procedure.
32    main_proc: Word,
33
34    /// The roots of the procedures exposed by the kernel API.
35    kernel_procs: Vec<Word>,
36}
37
38impl KernelConfig {
39    // CONSTANTS
40    // --------------------------------------------------------------------------------------------
41
42    /// The maximum number of procedures that can be exported from a kernel.
43    pub const MAX_NUM_KERNEL_PROCEDURES: usize = KernelDescriptor::MAX_NUM_PROCEDURES;
44
45    // CONSTRUCTORS
46    // --------------------------------------------------------------------------------------------
47
48    /// Creates a new [`KernelConfig`] from the provided inputs.
49    ///
50    /// # Errors
51    ///
52    /// Returns an error if `kernel_procs` contains more than
53    /// [`Self::MAX_NUM_KERNEL_PROCEDURES`] procedure roots.
54    pub fn new(main_proc: Word, kernel_procs: Vec<Word>) -> Result<Self, ProtocolConfigError> {
55        if kernel_procs.len() > Self::MAX_NUM_KERNEL_PROCEDURES {
56            return Err(ProtocolConfigError::TooManyKernelProcedures { count: kernel_procs.len() });
57        }
58
59        Ok(Self { main_proc, kernel_procs })
60    }
61
62    // PUBLIC ACCESSORS
63    // --------------------------------------------------------------------------------------------
64
65    /// Returns the root of the executable kernel procedure.
66    pub fn main_proc(&self) -> Word {
67        self.main_proc
68    }
69
70    /// Returns the roots of the procedures exposed by the kernel API.
71    pub fn kernel_procs(&self) -> &[Word] {
72        &self.kernel_procs
73    }
74
75    /// Returns the roots of the procedures exposed by the kernel API.
76    pub fn num_kernel_procs(&self) -> u8 {
77        u8::try_from(self.kernel_procs.len())
78            .expect("constructor should validate num procs fits in u8")
79    }
80
81    /// Returns the sequential hash of the exposed kernel procedure roots.
82    pub fn kernel_procs_elements(&self) -> &[Felt] {
83        Word::words_as_elements(&self.kernel_procs)
84    }
85
86    /// Returns the sequential hash of the exposed kernel procedure roots.
87    pub fn kernel_procs_commitment(&self) -> Word {
88        Hasher::hash_elements(self.kernel_procs_elements())
89    }
90
91    /// Returns a commitment to this kernel configuration.
92    pub fn to_commitment(&self) -> Word {
93        Hasher::merge(&[self.main_proc, self.kernel_procs_commitment()])
94    }
95
96    /// Returns the preimage of [`KernelConfig::to_commitment`] as a sequence of field elements.
97    pub fn to_elements(&self) -> Vec<Felt> {
98        let kernel_procs_commitment = self.kernel_procs_commitment();
99        [self.main_proc.as_elements(), kernel_procs_commitment.as_elements()].concat()
100    }
101}
102
103// SERIALIZATION
104// ================================================================================================
105
106impl Serializable for KernelConfig {
107    fn write_into<W: ByteWriter>(&self, target: &mut W) {
108        let num_kernel_procs = self.num_kernel_procs();
109        let Self { main_proc, kernel_procs } = self;
110
111        main_proc.write_into(target);
112        num_kernel_procs.write_into(target);
113        target.write_many(kernel_procs);
114    }
115}
116
117impl Deserializable for KernelConfig {
118    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
119        let main_proc = source.read()?;
120        let num_kernel_procs: u8 = source.read()?;
121        let kernel_procs = source
122            .read_many_iter(num_kernel_procs as usize)?
123            .collect::<Result<Vec<Word>, _>>()?;
124
125        Self::new(main_proc, kernel_procs)
126            .map_err(|err| DeserializationError::InvalidValue(err.to_string()))
127    }
128}
129
130// TESTS
131// ================================================================================================
132
133#[cfg(test)]
134mod tests {
135    use alloc::vec;
136
137    use assert_matches::assert_matches;
138    use miden_crypto::rand::test_utils::rand_value;
139
140    use super::*;
141
142    #[test]
143    fn new_rejects_too_many_procedures() {
144        let procs = vec![Word::empty(); KernelConfig::MAX_NUM_KERNEL_PROCEDURES + 1];
145
146        let error = KernelConfig::new(Word::empty(), procs).unwrap_err();
147        assert_matches!(error, ProtocolConfigError::TooManyKernelProcedures { count } => {
148            assert_eq!(count, KernelConfig::MAX_NUM_KERNEL_PROCEDURES + 1);
149        });
150    }
151
152    #[test]
153    fn serde_round_trip() -> anyhow::Result<()> {
154        let config = KernelConfig::new(rand_value::<Word>(), vec![rand_value::<Word>(); 3])?;
155
156        let deserialized = KernelConfig::read_from_bytes(&config.to_bytes())?;
157        assert_eq!(config, deserialized);
158
159        Ok(())
160    }
161}