Skip to main content

miden_mast_package/package/serialization/
mod.rs

1//! The serialization format of `Package` is as follows:
2//!
3//! #### Header
4//! - `MAGIC_PACKAGE`, a 4-byte tag, followed by a NUL-byte, i.e. `b"\0"`
5//! - `VERSION`, a 3-byte semantic version number, 1 byte for each component, i.e. MAJ.MIN.PATCH
6//!
7//! #### Metadata
8//! - `name` (`String`)
9//! - `version` ([`miden_assembly_syntax::Version`] serialized as a `String`)
10//! - `description` (optional, `String`)
11//! - `kind` (`u8`, see [`crate::TargetType`])
12//!
13//! #### Code
14//! - `mast` (see [`miden_assembly_syntax::Library`])
15//!
16//! #### Manifest
17//! - `manifest` (see [`crate::PackageManifest`])
18//!
19//! #### Custom Sections
20//! - `sections` (a vector of zero or more [`crate::Section`])
21//!
22//! #### Reader trust policy
23//!
24//! Package deserialization has two independently important trust decisions:
25//!
26//! - whether the embedded [`MastForest`] must be recomputed and validated;
27//! - whether package-owned debug sections may be exposed to callers.
28//!
29//! [`Package::read_from`] and [`Package::read_from_bytes`] are the normal untrusted readers. They
30//! validate the embedded MAST forest and package-owned debug information before returning the
31//! package. Use them for bytes received across a trust boundary.
32//!
33//! [`Package::read_from_trusted`] and [`Package::read_from_bytes_trusted`] are for local
34//! files/cache entries controlled by the same trusted build or execution system. They preserve
35//! package-owned debug sections and skip embedded MAST and manifest cross-check validation.
36//!
37//! Embedded kernel package bytes are stored in the opaque `kernel` custom section. Decoding an
38//! embedded kernel through the package API uses the untrusted reader, so nested package-owned debug
39//! information is validated and retained under the same policy.
40
41use alloc::{
42    format,
43    string::{String, ToString},
44    sync::Arc,
45    vec::Vec,
46};
47
48use miden_assembly_syntax::ast::{self, AttributeSet, PathBuf};
49use miden_core::{
50    Word,
51    mast::{MastForest, MastNodeExt, MastNodeId, UntrustedMastForest},
52    serde::{
53        BudgetedReader, ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable,
54        SliceReader,
55    },
56};
57
58use super::{
59    ConstantExport, PackageId, PackageModule, PackageSubmodule, ProcedureExport, TargetType,
60    TypeExport,
61};
62use crate::{
63    Dependency, ManifestValidationError, Package, PackageExport, PackageManifest, Section,
64    debug_info::DebugSourceNodeId,
65};
66
67#[cfg(test)]
68mod tests;
69
70// CONSTANTS
71// ================================================================================================
72
73/// Magic string for detecting that a file is serialized [`Package`]
74const MAGIC_PACKAGE: &[u8; 5] = b"MASP\0";
75
76/// The format version.
77///
78/// If future modifications are made to this format, the version should be incremented by 1.
79const VERSION: [u8; 3] = [7, 0, 0];
80
81/// Byte-read budget multiplier for package deserialization from a byte slice.
82///
83/// The budget is intentionally finite to reject malicious length prefixes, but larger than the
84/// source length because collection deserialization uses conservative per-element size estimates.
85const PACKAGE_BYTE_READ_BUDGET_MULTIPLIER: usize = 64;
86
87// PACKAGE SERIALIZATION/DESERIALIZATION
88// ================================================================================================
89
90impl Package {
91    #[doc(hidden)]
92    pub fn write_header_into<W: ByteWriter>(&self, target: &mut W) {
93        // Write magic & version
94        target.write_bytes(MAGIC_PACKAGE);
95        target.write_bytes(&VERSION);
96
97        // Write package name
98        self.name.write_into(target);
99
100        // Write package version
101        self.version.to_string().write_into(target);
102
103        // Write package description
104        self.description.write_into(target);
105
106        // Write package kind
107        target.write_u8(self.kind.into());
108    }
109
110    #[doc(hidden)]
111    pub fn write_trailer_into<W: ByteWriter>(&self, target: &mut W) {
112        // Write manifest
113        self.manifest.write_into(target);
114
115        // Write custom sections
116        target.write_usize(self.sections.len());
117        for section in self.sections.iter() {
118            section.write_into(target);
119        }
120    }
121
122    /// Reads a package from trusted storage without validating the embedded MAST forest.
123    ///
124    /// # Trust boundary
125    ///
126    /// This skips embedded MAST and manifest cross-check validation and trusts serialized node
127    /// digests. Use it for a package written and retained by the same trusted system, such as a
128    /// local build cache.
129    ///
130    /// Do not use this for user-controlled packages, network input, or any other package that
131    /// crosses a trust boundary. Use [`Package::read_from`] for those inputs.
132    #[track_caller]
133    pub fn read_from_trusted<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
134        let header = Self::read_header_from(source)?;
135        let mast_forest = Self::read_mast_forest(source, false)?;
136        Self::read_from_with_header_and_mast(source, header, mast_forest, false, false)
137    }
138
139    /// Reads package bytes from trusted storage without validating the embedded MAST forest.
140    ///
141    /// # Trust boundary
142    ///
143    /// This skips embedded MAST and manifest cross-check validation and trusts serialized node
144    /// digests. Use it for a package written and retained by the same trusted system, such as a
145    /// local build cache. This method still applies the finite byte-read budget used by
146    /// [`Package::read_from_bytes`].
147    ///
148    /// Do not use this for user-controlled packages, network input, or any other package that
149    /// crosses a trust boundary. Use [`Package::read_from_bytes`] for those inputs.
150    #[track_caller]
151    pub fn read_from_bytes_trusted(bytes: &[u8]) -> Result<Self, DeserializationError> {
152        let budget = bytes.len().saturating_mul(PACKAGE_BYTE_READ_BUDGET_MULTIPLIER);
153        let mut reader = BudgetedReader::new(SliceReader::new(bytes), budget);
154        Self::read_from_trusted(&mut reader)
155    }
156
157    #[track_caller]
158    fn read_mast_forest<R: ByteReader>(
159        source: &mut R,
160        validate_mast_forest: bool,
161    ) -> Result<Arc<MastForest>, DeserializationError> {
162        if validate_mast_forest {
163            UntrustedMastForest::read_from(source)?.validate().map_err(|err| {
164                DeserializationError::InvalidValue(format!(
165                    "library contains an invalid untrusted MAST forest: {err}"
166                ))
167            })
168        } else {
169            MastForest::read_from(source)
170        }
171        .map(Arc::new)
172    }
173}
174
175impl Serializable for Package {
176    fn write_into<W: ByteWriter>(&self, target: &mut W) {
177        self.write_header_into(target);
178
179        // Write MAST artifact
180        self.mast.write_into(target);
181
182        self.write_trailer_into(target);
183    }
184}
185
186struct PackageHeader {
187    name: PackageId,
188    version: crate::Version,
189    description: Option<String>,
190    kind: TargetType,
191}
192
193impl Package {
194    fn read_header_from<R: ByteReader>(
195        source: &mut R,
196    ) -> Result<PackageHeader, DeserializationError> {
197        // Read and validate magic & version
198        let magic: [u8; 5] = source.read_array()?;
199        if magic != *MAGIC_PACKAGE {
200            return Err(DeserializationError::InvalidValue(format!(
201                "invalid magic bytes. Expected '{MAGIC_PACKAGE:?}', got '{magic:?}'"
202            )));
203        }
204
205        let version: [u8; 3] = source.read_array()?;
206        if version != VERSION {
207            return Err(DeserializationError::InvalidValue(format!(
208                "unsupported version. Got '{version:?}', but only '{VERSION:?}' is supported"
209            )));
210        }
211
212        // Read package name
213        let name = PackageId::read_from(source)?;
214
215        // Read package version
216        let version = String::read_from(source)?
217            .parse::<crate::Version>()
218            .map_err(|err| DeserializationError::InvalidValue(err.to_string()))?;
219
220        // Read package description
221        let description = Option::<String>::read_from(source)?;
222
223        // Read package kind
224        let kind_tag = source.read_u8()?;
225        let kind = TargetType::try_from(kind_tag)
226            .map_err(|e| DeserializationError::InvalidValue(e.to_string()))?;
227
228        Ok(PackageHeader { name, version, description, kind })
229    }
230
231    fn read_from_with_header_and_mast<R: ByteReader>(
232        source: &mut R,
233        header: PackageHeader,
234        mast: Arc<MastForest>,
235        validate_manifest: bool,
236        validate_debug_sections: bool,
237    ) -> Result<Self, DeserializationError> {
238        let PackageHeader { name, version, description, kind } = header;
239
240        // Read manifest
241        let manifest = if validate_manifest {
242            PackageManifest::read_from_safe(source, &mast)?
243        } else {
244            PackageManifest::read_from_trusted(source, &mast)?
245        };
246
247        // Read custom sections
248        let sections = Vec::<Section>::read_from(source)?;
249
250        let mut package = Self {
251            name,
252            version,
253            mast_forest_commitment: Default::default(),
254            description,
255            kind,
256            mast,
257            manifest,
258            sections,
259            debug_sections_trusted: true,
260        };
261
262        if validate_debug_sections {
263            package.debug_info().map_err(|err| {
264                DeserializationError::InvalidValue(format!(
265                    "package contains invalid debug information: {err}"
266                ))
267            })?;
268        }
269
270        if validate_manifest {
271            package
272                .compute_interface_commitment()
273                .map_err(|err| DeserializationError::InvalidValue(err.to_string()))?;
274        }
275        package.recompute_mast_commitment();
276
277        Ok(package)
278    }
279}
280
281impl Deserializable for Package {
282    /// Reads and validates a package from potentially adversarial input.
283    ///
284    /// This validates the embedded MAST forest, manifest references, and package-owned debug
285    /// information before returning. The caller's [`ByteReader`] controls the resource budget. For
286    /// a byte slice, prefer [`Package::read_from_bytes`], which applies a finite byte-read budget.
287    /// Use [`Package::read_from_trusted`] for packages written and retained by the same trusted
288    /// system when repeating these checks is unnecessary.
289    #[track_caller]
290    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
291        let header = Self::read_header_from(source)?;
292
293        // Read MAST artifact
294        let mast = Self::read_mast_forest(source, true)?;
295
296        Self::read_from_with_header_and_mast(source, header, mast, true, true)
297    }
298
299    /// Reads and validates a package from a potentially adversarial byte slice.
300    ///
301    /// This is the recommended reader for untrusted package bytes. It applies a finite byte-read
302    /// budget and validates the embedded MAST forest, manifest references, and package-owned debug
303    /// information. Use [`Package::read_from_bytes_trusted`] for packages written and retained by
304    /// the same trusted system when repeating these checks is unnecessary.
305    #[track_caller]
306    fn read_from_bytes(bytes: &[u8]) -> Result<Self, DeserializationError> {
307        let budget = bytes.len().saturating_mul(PACKAGE_BYTE_READ_BUDGET_MULTIPLIER);
308        let mut reader = BudgetedReader::new(SliceReader::new(bytes), budget);
309        Self::read_from(&mut reader)
310    }
311}
312
313// PACKAGE MANIFEST SERIALIZATION/DESERIALIZATION
314// ================================================================================================
315
316impl Serializable for PackageManifest {
317    fn write_into<W: ByteWriter>(&self, target: &mut W) {
318        // Write exports
319        target.write_usize(self.num_exports());
320        for export in self.exports() {
321            export.write_into(target);
322        }
323
324        // Write module surfaces
325        target.write_usize(self.num_modules());
326        for module in self.modules() {
327            module.write_into(target);
328        }
329
330        // Write dependencies
331        target.write_usize(self.num_dependencies());
332        for dep in self.dependencies() {
333            dep.write_into(target);
334        }
335
336        // Write entrypoint
337        if let Some(entrypoint) = self.entrypoint.as_ref() {
338            target.write_bool(true);
339            entrypoint.write_into(target);
340        } else {
341            target.write_bool(false);
342        }
343    }
344}
345
346impl PackageManifest {
347    pub fn read_from_trusted<R: ByteReader>(
348        source: &mut R,
349        mast: &MastForest,
350    ) -> Result<Self, DeserializationError> {
351        // Read exports
352        let exports_len = source.read_usize()?;
353        let max_exports = source.max_alloc(PackageExport::min_serialized_size());
354        if exports_len > max_exports {
355            return Err(DeserializationError::InvalidValue(format!(
356                "requested {exports_len} elements but reader can provide at most {max_exports}"
357            )));
358        }
359        let mut exports = Vec::with_capacity(exports_len);
360        for _ in 0..exports_len {
361            exports.push(PackageExport::read_from_trusted(source, mast)?);
362        }
363
364        // Read module surfaces
365        let modules_len = source.read_usize()?;
366        let max_modules = source.max_alloc(PackageModule::min_serialized_size());
367        if modules_len > max_modules {
368            return Err(DeserializationError::InvalidValue(format!(
369                "requested {modules_len} elements but reader can provide at most {max_modules}"
370            )));
371        }
372        let modules = source.read_many_iter(modules_len)?.collect::<Result<Vec<_>, _>>()?;
373
374        // Read dependencies
375        let dependencies = Vec::<Dependency>::read_from(source)?;
376
377        // Read entrypoint
378        let entrypoint = if source.read_bool()? {
379            Some(PathBuf::read_from(source).map(Arc::<ast::Path>::from)?)
380        } else {
381            None
382        };
383
384        PackageManifest::new(exports)
385            .and_then(|manifest| manifest.with_modules(modules))
386            .and_then(|manifest| manifest.with_dependencies(dependencies))
387            .and_then(|manifest| {
388                if let Some(entrypoint) = entrypoint {
389                    manifest.with_entrypoint(entrypoint)
390                } else {
391                    Ok(manifest)
392                }
393            })
394            .map_err(|error| DeserializationError::InvalidValue(error.to_string()))
395    }
396
397    pub fn read_from_safe<R: ByteReader>(
398        source: &mut R,
399        mast: &MastForest,
400    ) -> Result<Self, DeserializationError> {
401        // Read exports
402        let exports_len = source.read_usize()?;
403        let max_exports = source.max_alloc(PackageExport::min_serialized_size());
404        if exports_len > max_exports {
405            return Err(DeserializationError::InvalidValue(format!(
406                "requested {exports_len} elements but reader can provide at most {max_exports}"
407            )));
408        }
409        let mut exports = Vec::with_capacity(exports_len);
410        for _ in 0..exports_len {
411            exports.push(PackageExport::read_from_safe(source, mast)?);
412        }
413
414        // Read module surfaces
415        let modules_len = source.read_usize()?;
416        let max_modules = source.max_alloc(PackageModule::min_serialized_size());
417        if modules_len > max_modules {
418            return Err(DeserializationError::InvalidValue(format!(
419                "requested {modules_len} elements but reader can provide at most {max_modules}"
420            )));
421        }
422        let modules = source.read_many_iter(modules_len)?.collect::<Result<Vec<_>, _>>()?;
423
424        // Read dependencies
425        let dependencies = Vec::<Dependency>::read_from(source)?;
426
427        // Read entrypoint
428        let entrypoint = if source.read_bool()? {
429            Some(PathBuf::read_from(source).map(Arc::<ast::Path>::from)?)
430        } else {
431            None
432        };
433
434        PackageManifest::new(exports)
435            .and_then(|manifest| manifest.with_modules(modules))
436            .and_then(|manifest| manifest.with_dependencies(dependencies))
437            .and_then(|manifest| {
438                if let Some(entrypoint) = entrypoint {
439                    manifest.with_entrypoint(entrypoint)
440                } else {
441                    Ok(manifest)
442                }
443            })
444            .map_err(|error| DeserializationError::InvalidValue(error.to_string()))
445    }
446}
447
448impl Deserializable for PackageManifest {
449    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
450        // Read exports
451        let exports_len = source.read_usize()?;
452        let exports = source.read_many_iter(exports_len)?.collect::<Result<Vec<_>, _>>()?;
453
454        // Read module surfaces
455        let modules_len = source.read_usize()?;
456        let modules = source.read_many_iter(modules_len)?.collect::<Result<Vec<_>, _>>()?;
457
458        // Read dependencies
459        let dependencies = Vec::<Dependency>::read_from(source)?;
460
461        // Read entrypoint
462        let entrypoint = if source.read_bool()? {
463            Some(PathBuf::read_from(source).map(Arc::<ast::Path>::from)?)
464        } else {
465            None
466        };
467
468        PackageManifest::new(exports)
469            .and_then(|manifest| manifest.with_modules(modules))
470            .and_then(|manifest| manifest.with_dependencies(dependencies))
471            .and_then(|manifest| {
472                if let Some(entrypoint) = entrypoint {
473                    manifest.with_entrypoint(entrypoint)
474                } else {
475                    Ok(manifest)
476                }
477            })
478            .map_err(|error| DeserializationError::InvalidValue(error.to_string()))
479    }
480}
481
482// PACKAGE MODULE SURFACE SERIALIZATION/DESERIALIZATION
483// ================================================================================================
484
485impl Serializable for PackageModule {
486    fn write_into<W: ByteWriter>(&self, target: &mut W) {
487        self.path.write_into(target);
488        target.write_usize(self.submodules.len());
489        for submodule in self.submodules.iter() {
490            submodule.write_into(target);
491        }
492    }
493}
494
495impl Deserializable for PackageModule {
496    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
497        let path = PathBuf::read_from(source)?.into_boxed_path().into();
498        let submodules = Vec::<PackageSubmodule>::read_from(source)?;
499        Ok(Self { path, submodules })
500    }
501}
502
503impl Serializable for PackageSubmodule {
504    fn write_into<W: ByteWriter>(&self, target: &mut W) {
505        self.name.write_into(target);
506    }
507}
508
509impl Deserializable for PackageSubmodule {
510    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
511        let name = ast::Ident::read_from(source)?;
512        Ok(Self { name })
513    }
514}
515
516// PACKAGE EXPORT SERIALIZATION/DESERIALIZATION
517// ================================================================================================
518
519impl Serializable for PackageExport {
520    fn write_into<W: ByteWriter>(&self, target: &mut W) {
521        target.write_u8(self.tag());
522        match self {
523            Self::Procedure(export) => export.write_into(target),
524            Self::Constant(export) => export.write_into(target),
525            Self::Type(export) => export.write_into(target),
526        }
527    }
528}
529
530impl PackageExport {
531    pub fn read_from_trusted<R: ByteReader>(
532        source: &mut R,
533        mast: &MastForest,
534    ) -> Result<Self, DeserializationError> {
535        match source.read_u8()? {
536            1 => ProcedureExport::read_from_trusted(source, mast).map(Self::Procedure),
537            2 => ConstantExport::read_from(source).map(Self::Constant),
538            3 => TypeExport::read_from(source).map(Self::Type),
539            invalid => Err(DeserializationError::InvalidValue(format!(
540                "unexpected PackageExport tag: '{invalid}'"
541            ))),
542        }
543    }
544
545    pub fn read_from_safe<R: ByteReader>(
546        source: &mut R,
547        mast: &MastForest,
548    ) -> Result<Self, DeserializationError> {
549        match source.read_u8()? {
550            1 => ProcedureExport::read_from_safe(source, mast).map(Self::Procedure),
551            2 => ConstantExport::read_from(source).map(Self::Constant),
552            3 => TypeExport::read_from(source).map(Self::Type),
553            invalid => Err(DeserializationError::InvalidValue(format!(
554                "unexpected PackageExport tag: '{invalid}'"
555            ))),
556        }
557    }
558}
559
560impl Deserializable for PackageExport {
561    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
562        match source.read_u8()? {
563            1 => ProcedureExport::read_from(source).map(Self::Procedure),
564            2 => ConstantExport::read_from(source).map(Self::Constant),
565            3 => TypeExport::read_from(source).map(Self::Type),
566            invalid => Err(DeserializationError::InvalidValue(format!(
567                "unexpected PackageExport tag: '{invalid}'"
568            ))),
569        }
570    }
571}
572
573impl Serializable for ProcedureExport {
574    fn write_into<W: ByteWriter>(&self, target: &mut W) {
575        self.path.write_into(target);
576        if let Some(node_id) = self.node {
577            target.write_bool(true);
578            target.write_u32(node_id.into());
579        } else {
580            target.write_bool(false);
581        }
582        if let Some(source_node) = self.source_node {
583            target.write_bool(true);
584            source_node.write_into(target);
585        } else {
586            target.write_bool(false);
587        }
588        self.digest.write_into(target);
589        match self.signature.as_ref() {
590            Some(sig) => {
591                target.write_bool(true);
592                sig.write_into(target);
593            },
594            None => {
595                target.write_bool(false);
596            },
597        }
598        self.attributes.write_into(target);
599    }
600}
601
602impl ProcedureExport {
603    pub fn read_from_trusted<R: ByteReader>(
604        source: &mut R,
605        mast: &MastForest,
606    ) -> Result<Self, DeserializationError> {
607        use miden_assembly_syntax::ast::types::FunctionType;
608        let path = PathBuf::read_from(source)?.into_boxed_path().into();
609        let node = if source.read_bool()? {
610            Some(MastNodeId::from_u32_safe(source.read_u32()?, mast)?)
611        } else {
612            None
613        };
614        let source_node = if source.read_bool()? {
615            Some(DebugSourceNodeId::read_from(source)?)
616        } else {
617            None
618        };
619        let digest = Word::read_from(source)?;
620        let signature = if source.read_bool()? {
621            Some(FunctionType::read_from(source)?)
622        } else {
623            None
624        };
625        let attributes = AttributeSet::read_from(source)?;
626        Ok(Self {
627            path,
628            node,
629            source_node,
630            digest,
631            signature,
632            attributes,
633        })
634    }
635
636    pub fn read_from_safe<R: ByteReader>(
637        source: &mut R,
638        mast: &MastForest,
639    ) -> Result<Self, DeserializationError> {
640        use miden_assembly_syntax::ast::types::FunctionType;
641        let path = PathBuf::read_from(source)?.into_boxed_path().into();
642        let node = if source.read_bool()? {
643            let node_id = MastNodeId::from_u32_safe(source.read_u32()?, mast)?;
644            if !mast.is_procedure_root(node_id) {
645                return Err(DeserializationError::InvalidValue(
646                    ManifestValidationError::InvalidProcedureExport { path }.to_string(),
647                ));
648            }
649            Some(node_id)
650        } else {
651            None
652        };
653        let source_node = if source.read_bool()? {
654            Some(DebugSourceNodeId::read_from(source)?)
655        } else {
656            None
657        };
658        let digest = Word::read_from(source)?;
659        // Ensure that the digest associated with `node` matches the provided digest
660        if let Some(node) = node
661            && digest != mast[node].digest()
662        {
663            return Err(DeserializationError::InvalidValue(
664                ManifestValidationError::InvalidProcedureExport { path }.to_string(),
665            ));
666        }
667        let signature = if source.read_bool()? {
668            Some(FunctionType::read_from(source)?)
669        } else {
670            None
671        };
672        let attributes = AttributeSet::read_from(source)?;
673        Ok(Self {
674            path,
675            node,
676            source_node,
677            digest,
678            signature,
679            attributes,
680        })
681    }
682}
683
684impl Deserializable for ProcedureExport {
685    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
686        use miden_assembly_syntax::ast::types::FunctionType;
687        let path = PathBuf::read_from(source)?.into_boxed_path().into();
688        let node = if source.read_bool()? {
689            Some(MastNodeId::new_unchecked(source.read_u32()?))
690        } else {
691            None
692        };
693        let source_node = if source.read_bool()? {
694            Some(DebugSourceNodeId::read_from(source)?)
695        } else {
696            None
697        };
698        let digest = Word::read_from(source)?;
699        let signature = if source.read_bool()? {
700            Some(FunctionType::read_from(source)?)
701        } else {
702            None
703        };
704        let attributes = AttributeSet::read_from(source)?;
705        Ok(Self {
706            path,
707            node,
708            source_node,
709            digest,
710            signature,
711            attributes,
712        })
713    }
714}
715
716impl Serializable for ConstantExport {
717    fn write_into<W: ByteWriter>(&self, target: &mut W) {
718        self.path.write_into(target);
719        self.value.write_into(target);
720    }
721}
722
723impl Deserializable for ConstantExport {
724    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
725        let path = PathBuf::read_from(source)?.into_boxed_path().into();
726        let value = ast::ConstantValue::read_from(source)?;
727        Ok(Self { path, value })
728    }
729}
730
731impl Serializable for TypeExport {
732    fn write_into<W: ByteWriter>(&self, target: &mut W) {
733        self.path.write_into(target);
734        self.ty.write_into(target);
735    }
736}
737
738impl Deserializable for TypeExport {
739    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
740        use miden_assembly_syntax::ast::types::Type;
741        let path = PathBuf::read_from(source)?.into_boxed_path().into();
742        let ty = Type::read_from(source)?;
743        Ok(Self { path, ty })
744    }
745}