Skip to main content

miden_protocol/protocol_config/
mod.rs

1use alloc::string::ToString;
2use alloc::vec::Vec;
3
4use crate::asset::AssetId;
5use crate::batch::BatchKernel;
6use crate::constants::MIN_PROOF_SECURITY_LEVEL;
7use crate::crypto::SequentialCommit;
8use crate::errors::ProtocolConfigError;
9use crate::transaction::TransactionKernel;
10use crate::utils::serde::{
11    ByteReader,
12    ByteWriter,
13    Deserializable,
14    DeserializationError,
15    Serializable,
16};
17use crate::{Felt, Word};
18
19mod kernel_config;
20pub use kernel_config::KernelConfig;
21
22mod next_protocol_config;
23pub use next_protocol_config::NextProtocolConfig;
24
25mod proof_verification;
26pub use proof_verification::{ProofSecurityPolicy, ProofVerificationConfig};
27
28// PROTOCOL CONFIG
29// ================================================================================================
30
31/// The configuration parameters of the protocol that are expected to change rarely over the
32/// lifetime of a chain.
33///
34/// A [`BlockHeader`](crate::block::BlockHeader) holds only the commitment to this config, so that
35/// rarely changing data does not take up space in every block.
36#[derive(Debug, Clone, PartialEq, Eq)]
37pub struct ProtocolConfig {
38    /// The ID of the asset that fees are paid in.
39    fee_asset_id: AssetId,
40
41    /// The configuration of the transaction kernel.
42    tx_kernel: KernelConfig,
43
44    /// The configuration of the batch kernel.
45    batch_kernel: KernelConfig,
46
47    /// The configuration of the block kernel.
48    block_kernel: KernelConfig,
49
50    /// The parameters defining which proofs the protocol accepts.
51    proof_verification: ProofVerificationConfig,
52}
53
54impl ProtocolConfig {
55    // CONSTANTS
56    // --------------------------------------------------------------------------------------------
57
58    /// The minimum proof security in bits, as a `u8`.
59    const MINIMUM_SECURITY_BITS: u8 = {
60        assert!(MIN_PROOF_SECURITY_LEVEL <= u8::MAX as u32);
61        MIN_PROOF_SECURITY_LEVEL as u8
62    };
63
64    // CONSTRUCTORS
65    // --------------------------------------------------------------------------------------------
66
67    /// Creates a new [`ProtocolConfig`] from the provided inputs.
68    ///
69    /// # Errors
70    ///
71    /// Returns an error if `fee_asset_id` is not a fungible asset ID.
72    pub fn new(
73        fee_asset_id: AssetId,
74        tx_kernel: KernelConfig,
75        batch_kernel: KernelConfig,
76        block_kernel: KernelConfig,
77        proof_verification: ProofVerificationConfig,
78    ) -> Result<Self, ProtocolConfigError> {
79        if !fee_asset_id.composition().is_fungible() {
80            return Err(ProtocolConfigError::FeeAssetMustBeFungible(fee_asset_id.composition()));
81        }
82
83        Ok(Self {
84            fee_asset_id,
85            tx_kernel,
86            batch_kernel,
87            block_kernel,
88            proof_verification,
89        })
90    }
91
92    /// Creates the [`ProtocolConfig`] described by the currently linked kernels.
93    ///
94    /// TODO(#3644): The batch kernel, the block kernel and the proof verification roots are
95    /// placeholders until those parts of the protocol exist.
96    ///
97    /// # Errors
98    ///
99    /// Returns an error if `fee_asset_id` is not a fungible asset ID.
100    pub fn current(fee_asset_id: AssetId) -> Result<Self, ProtocolConfigError> {
101        let tx_kernel = KernelConfig::new(
102            TransactionKernel::main().hash(),
103            TransactionKernel::PROCEDURES.to_vec(),
104        )?;
105        let batch_kernel = KernelConfig::new(BatchKernel::main().hash(), Vec::new())?;
106        let block_kernel = KernelConfig::new(Word::empty(), Vec::new())?;
107
108        // Placeholders.
109        let security_policy = ProofSecurityPolicy::new(Word::empty(), Self::MINIMUM_SECURITY_BITS)?;
110        let proof_verification =
111            ProofVerificationConfig::new(Word::empty(), Word::empty(), security_policy);
112
113        Self::new(fee_asset_id, tx_kernel, batch_kernel, block_kernel, proof_verification)
114    }
115
116    // PUBLIC ACCESSORS
117    // --------------------------------------------------------------------------------------------
118
119    /// Returns the ID of the asset that fees are paid in.
120    pub fn fee_asset_id(&self) -> AssetId {
121        self.fee_asset_id
122    }
123
124    /// Returns the configuration of the transaction kernel.
125    pub fn tx_kernel(&self) -> &KernelConfig {
126        &self.tx_kernel
127    }
128
129    /// Returns the configuration of the batch kernel.
130    pub fn batch_kernel(&self) -> &KernelConfig {
131        &self.batch_kernel
132    }
133
134    /// Returns the configuration of the block kernel.
135    pub fn block_kernel(&self) -> &KernelConfig {
136        &self.block_kernel
137    }
138
139    /// Returns the parameters defining which proofs the protocol accepts.
140    pub fn proof_verification(&self) -> &ProofVerificationConfig {
141        &self.proof_verification
142    }
143
144    /// Returns the commitment to this configuration, which is what a block header commits to.
145    pub fn to_commitment(&self) -> Word {
146        <Self as SequentialCommit>::to_commitment(self)
147    }
148
149    /// Returns the preimage of [`ProtocolConfig::to_commitment`] as a sequence of field elements.
150    ///
151    /// The element layout is:
152    ///
153    /// ```text
154    /// [
155    ///     FEE_ASSET_ID,
156    ///     TX_KERNEL_CONFIG_COMMITMENT,
157    ///     BATCH_KERNEL_CONFIG_COMMITMENT,
158    ///     BLOCK_KERNEL_CONFIG_COMMITMENT,
159    ///     PROOF_VERIFICATION_CONFIG_COMMITMENT,
160    ///     EMPTY_WORD,
161    /// ]
162    /// ```
163    pub fn to_elements(&self) -> Vec<Felt> {
164        <Self as SequentialCommit>::to_elements(self)
165    }
166}
167
168impl SequentialCommit for ProtocolConfig {
169    type Commitment = Word;
170
171    fn to_elements(&self) -> Vec<Felt> {
172        let fee_asset_id = self.fee_asset_id.to_word();
173        let tx_kernel = self.tx_kernel.to_commitment();
174        let batch_kernel = self.batch_kernel.to_commitment();
175        let block_kernel = self.block_kernel.to_commitment();
176        let proof_verification = self.proof_verification.to_commitment();
177
178        [
179            fee_asset_id.as_elements(),
180            tx_kernel.as_elements(),
181            batch_kernel.as_elements(),
182            block_kernel.as_elements(),
183            proof_verification.as_elements(),
184            Word::empty().as_elements(),
185        ]
186        .concat()
187    }
188}
189
190// SERIALIZATION
191// ================================================================================================
192
193impl Serializable for ProtocolConfig {
194    fn write_into<W: ByteWriter>(&self, target: &mut W) {
195        let Self {
196            fee_asset_id,
197            tx_kernel,
198            batch_kernel,
199            block_kernel,
200            proof_verification,
201        } = self;
202
203        fee_asset_id.write_into(target);
204        tx_kernel.write_into(target);
205        batch_kernel.write_into(target);
206        block_kernel.write_into(target);
207        proof_verification.write_into(target);
208    }
209}
210
211impl Deserializable for ProtocolConfig {
212    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
213        let fee_asset_id = source.read()?;
214        let tx_kernel = source.read()?;
215        let batch_kernel = source.read()?;
216        let block_kernel = source.read()?;
217        let proof_verification = source.read()?;
218
219        Self::new(fee_asset_id, tx_kernel, batch_kernel, block_kernel, proof_verification)
220            .map_err(|err| DeserializationError::InvalidValue(err.to_string()))
221    }
222}
223
224// TESTS
225// ================================================================================================
226
227#[cfg(test)]
228mod tests {
229    use assert_matches::assert_matches;
230
231    use super::*;
232    use crate::account::AccountId;
233    use crate::asset::{AssetClass, AssetComposition};
234    use crate::testing::account_id::{
235        ACCOUNT_ID_PRIVATE_NON_FUNGIBLE_FAUCET,
236        ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET,
237    };
238
239    fn fee_asset_id() -> AssetId {
240        let faucet_id = AccountId::try_from(ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET)
241            .expect("test faucet ID should be valid");
242        AssetId::new_fungible(faucet_id)
243    }
244
245    #[test]
246    fn to_elements_is_pipeable() {
247        // The kernel pipes the preimage into memory, which requires the element count to be a
248        // multiple of the hasher's rate width.
249        let config = ProtocolConfig::current(fee_asset_id()).unwrap();
250
251        assert_eq!(config.to_elements().len(), 24);
252    }
253
254    #[test]
255    fn current_commits_to_the_linked_tx_kernel() {
256        let config = ProtocolConfig::current(fee_asset_id()).unwrap();
257
258        assert_eq!(config.tx_kernel().main_proc(), TransactionKernel::main().hash());
259        assert_eq!(config.tx_kernel().kernel_procs(), TransactionKernel::PROCEDURES);
260    }
261
262    #[test]
263    fn new_rejects_non_fungible_fee_asset() {
264        let faucet_id = AccountId::try_from(ACCOUNT_ID_PRIVATE_NON_FUNGIBLE_FAUCET)
265            .expect("test faucet ID should be valid");
266        let fee_asset_id =
267            AssetId::new(AssetClass::default(), faucet_id, AssetComposition::None).unwrap();
268
269        let error = ProtocolConfig::new(
270            fee_asset_id,
271            KernelConfig::dummy(),
272            KernelConfig::dummy(),
273            KernelConfig::dummy(),
274            ProofVerificationConfig::new(
275                Word::empty(),
276                Word::empty(),
277                ProofSecurityPolicy::new(Word::empty(), 96).unwrap(),
278            ),
279        )
280        .unwrap_err();
281
282        assert_matches!(error, ProtocolConfigError::FeeAssetMustBeFungible(AssetComposition::None));
283    }
284
285    #[test]
286    fn serde_round_trip() -> anyhow::Result<()> {
287        let config = ProtocolConfig::current(fee_asset_id())?;
288
289        let deserialized = ProtocolConfig::read_from_bytes(&config.to_bytes())
290            .map_err(|err| anyhow::anyhow!("{err}"))?;
291
292        assert_eq!(config, deserialized);
293        Ok(())
294    }
295}