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 discard package-owned debug sections 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 validate the
35//! embedded MAST forest, but preserve package-owned debug sections so [`Package::debug_info`] can
36//! decode them.
37//!
38//! [`Package::read_from_unchecked`] and [`Package::read_from_bytes_unchecked`] are also trusted
39//! same-domain readers, but skip MAST validation. Use them only for bytes that were already
40//! validated before being persisted by the same trusted system.
41//!
42//! Embedded kernel package bytes are stored in the opaque `kernel` custom section. Untrusted
43//! package reads may carry those bytes, but decoding the embedded kernel through the package API
44//! uses the untrusted reader and therefore strips any nested package-owned debug sections.
45
46use alloc::{
47    format,
48    string::{String, ToString},
49    sync::Arc,
50    vec::Vec,
51};
52
53use miden_assembly_syntax::ast::{self, AttributeSet, PathBuf};
54use miden_core::{
55    Word,
56    mast::{MastForest, MastNodeExt, MastNodeId, UntrustedMastForest},
57    serde::{
58        BudgetedReader, ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable,
59        SliceReader,
60    },
61};
62
63use super::{
64    ConstantExport, PackageId, PackageModule, PackageSubmodule, ProcedureExport, TargetType,
65    TypeExport,
66};
67use crate::{
68    Dependency, ManifestValidationError, Package, PackageExport, PackageManifest, Section,
69    debug_info::DebugSourceNodeId,
70};
71
72#[cfg(test)]
73mod tests;
74
75// CONSTANTS
76// ================================================================================================
77
78/// Magic string for detecting that a file is serialized [`Package`]
79const MAGIC_PACKAGE: &[u8; 5] = b"MASP\0";
80
81/// The format version.
82///
83/// If future modifications are made to this format, the version should be incremented by 1.
84const VERSION: [u8; 3] = [6, 0, 0];
85
86/// Byte-read budget multiplier for package deserialization from a byte slice.
87///
88/// The budget is intentionally finite to reject malicious length prefixes, but larger than the
89/// source length because collection deserialization uses conservative per-element size estimates.
90const PACKAGE_BYTE_READ_BUDGET_MULTIPLIER: usize = 64;
91
92// PACKAGE SERIALIZATION/DESERIALIZATION
93// ================================================================================================
94
95impl Package {
96    #[doc(hidden)]
97    pub fn write_header_into<W: ByteWriter>(&self, target: &mut W) {
98        // Write magic & version
99        target.write_bytes(MAGIC_PACKAGE);
100        target.write_bytes(&VERSION);
101
102        // Write package name
103        self.name.write_into(target);
104
105        // Write package version
106        self.version.to_string().write_into(target);
107
108        // Write package description
109        self.description.write_into(target);
110
111        // Write package kind
112        target.write_u8(self.kind.into());
113    }
114
115    #[doc(hidden)]
116    pub fn write_trailer_into<W: ByteWriter>(&self, target: &mut W) {
117        // Write manifest
118        self.manifest.write_into(target);
119
120        // Write custom sections
121        target.write_usize(self.sections.len());
122        for section in self.sections.iter() {
123            section.write_into(target);
124        }
125    }
126
127    /// Reads a trusted package from `source` without validating the embedded MAST forest.
128    ///
129    /// # Trust boundary
130    ///
131    /// This skips embedded MAST validation and trusts serialized node digests. Use it only for
132    /// bytes that were already validated before being persisted by the same trusted system.
133    ///
134    /// Do not use this for user-controlled packages, network input, registry artifacts, or any
135    /// other package that crosses a trust boundary. Use [`Package::read_from`] for those
136    /// inputs.
137    pub fn read_from_unchecked<R: ByteReader>(
138        source: &mut R,
139    ) -> Result<Self, DeserializationError> {
140        let header = Self::read_header_from(source)?;
141        let mast_forest = Self::read_mast_forest(source, false)?;
142        Self::read_from_with_header_and_mast(source, header, mast_forest, true)
143    }
144
145    /// Reads trusted package bytes without validating the embedded MAST forest.
146    ///
147    /// # Trust boundary
148    ///
149    /// This skips embedded MAST validation and trusts serialized node digests. Use it only for
150    /// bytes that were already validated before being persisted by the same trusted system.
151    ///
152    /// Do not use this for user-controlled packages, network input, registry artifacts, or any
153    /// other package that crosses a trust boundary. Use [`Package::read_from_bytes`] for those
154    /// inputs.
155    pub fn read_from_bytes_unchecked(bytes: &[u8]) -> Result<Self, DeserializationError> {
156        let mut source = SliceReader::new(bytes);
157        Self::read_from_unchecked(&mut source)
158    }
159
160    /// Reads a trusted local package while validating the embedded MAST forest.
161    ///
162    /// This keeps the same structural validation as [`Package::read_from`], but allows
163    /// package-owned debug sections to be decoded as trusted metadata. Use this only for local
164    /// files or cache artifacts controlled by this process or build system. Do not use this for
165    /// inbound artifacts from an untrusted channel; use [`Package::read_from`] instead so debug
166    /// sections are discarded before the package is exposed to callers.
167    pub fn read_from_trusted<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
168        let header = Self::read_header_from(source)?;
169        let mast_forest = Self::read_mast_forest(source, true)?;
170        Self::read_from_with_header_and_mast(source, header, mast_forest, true)
171    }
172
173    /// Reads trusted local package bytes while validating the embedded MAST forest.
174    ///
175    /// See [`Package::read_from_trusted`].
176    pub fn read_from_bytes_trusted(bytes: &[u8]) -> Result<Self, DeserializationError> {
177        let budget = bytes.len().saturating_mul(PACKAGE_BYTE_READ_BUDGET_MULTIPLIER);
178        let mut reader = BudgetedReader::new(SliceReader::new(bytes), budget);
179        Self::read_from_trusted(&mut reader)
180    }
181
182    fn read_mast_forest<R: ByteReader>(
183        source: &mut R,
184        validate_mast_forest: bool,
185    ) -> Result<Arc<MastForest>, DeserializationError> {
186        if validate_mast_forest {
187            UntrustedMastForest::read_from(source)?.validate().map_err(|err| {
188                DeserializationError::InvalidValue(format!(
189                    "library contains an invalid untrusted MAST forest: {err}"
190                ))
191            })
192        } else {
193            MastForest::read_from(source)
194        }
195        .map(Arc::new)
196    }
197}
198
199impl Serializable for Package {
200    fn write_into<W: ByteWriter>(&self, target: &mut W) {
201        self.write_header_into(target);
202
203        // Write MAST artifact
204        self.mast.write_into(target);
205
206        self.write_trailer_into(target);
207    }
208}
209
210struct PackageHeader {
211    name: PackageId,
212    version: crate::Version,
213    description: Option<String>,
214    kind: TargetType,
215}
216
217impl Package {
218    fn read_header_from<R: ByteReader>(
219        source: &mut R,
220    ) -> Result<PackageHeader, DeserializationError> {
221        // Read and validate magic & version
222        let magic: [u8; 5] = source.read_array()?;
223        if magic != *MAGIC_PACKAGE {
224            return Err(DeserializationError::InvalidValue(format!(
225                "invalid magic bytes. Expected '{MAGIC_PACKAGE:?}', got '{magic:?}'"
226            )));
227        }
228
229        let version: [u8; 3] = source.read_array()?;
230        if version != VERSION {
231            return Err(DeserializationError::InvalidValue(format!(
232                "unsupported version. Got '{version:?}', but only '{VERSION:?}' is supported"
233            )));
234        }
235
236        // Read package name
237        let name = PackageId::read_from(source)?;
238
239        // Read package version
240        let version = String::read_from(source)?
241            .parse::<crate::Version>()
242            .map_err(|err| DeserializationError::InvalidValue(err.to_string()))?;
243
244        // Read package description
245        let description = Option::<String>::read_from(source)?;
246
247        // Read package kind
248        let kind_tag = source.read_u8()?;
249        let kind = TargetType::try_from(kind_tag)
250            .map_err(|e| DeserializationError::InvalidValue(e.to_string()))?;
251
252        Ok(PackageHeader { name, version, description, kind })
253    }
254
255    fn read_from_with_header_and_mast<R: ByteReader>(
256        source: &mut R,
257        header: PackageHeader,
258        mast: Arc<MastForest>,
259        debug_sections_trusted: bool,
260    ) -> Result<Self, DeserializationError> {
261        let PackageHeader { name, version, description, kind } = header;
262
263        // Read manifest
264        let manifest = PackageManifest::read_from_safe(source, &mast)?;
265
266        // Read custom sections
267        let mut sections = Vec::<Section>::read_from(source)?;
268        if !debug_sections_trusted && sections.iter().any(|section| section.id.is_debug()) {
269            log::warn!(
270                "Package read ignored debug sections from an untrusted artifact; use Package::read_from_trusted for local cache/debug reads"
271            );
272            sections.retain(|section| !section.id.is_debug());
273        }
274
275        let mut package = Self {
276            name,
277            version,
278            digest: Default::default(),
279            description,
280            kind,
281            mast,
282            manifest,
283            sections,
284            debug_sections_trusted,
285        };
286
287        package
288            .compute_interface_digest()
289            .map_err(|err| DeserializationError::InvalidValue(err.to_string()))?;
290        package.recompute_mast_commitment();
291
292        Ok(package)
293    }
294}
295
296impl Deserializable for Package {
297    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
298        let header = Self::read_header_from(source)?;
299
300        // Read MAST artifact
301        let mast = Self::read_mast_forest(source, true)?;
302
303        Self::read_from_with_header_and_mast(source, header, mast, false)
304    }
305
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
316#[cfg(feature = "serde")]
317impl serde::Serialize for PackageManifest {
318    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
319    where
320        S: serde::Serializer,
321    {
322        use alloc::collections::BTreeMap;
323
324        use miden_assembly_syntax::Path;
325        use serde::ser::SerializeStruct;
326
327        struct PackageExports<'a>(&'a BTreeMap<Arc<Path>, PackageExport>);
328
329        impl serde::Serialize for PackageExports<'_> {
330            fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
331            where
332                S: serde::Serializer,
333            {
334                use serde::ser::SerializeSeq;
335
336                let mut serializer = serializer.serialize_seq(Some(self.0.len()))?;
337                for value in self.0.values() {
338                    serializer.serialize_element(value)?;
339                }
340                serializer.end()
341            }
342        }
343
344        struct PackageModules<'a>(&'a BTreeMap<Arc<Path>, PackageModule>);
345
346        impl serde::Serialize for PackageModules<'_> {
347            fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
348            where
349                S: serde::Serializer,
350            {
351                use serde::ser::SerializeSeq;
352
353                let mut serializer = serializer.serialize_seq(Some(self.0.len()))?;
354                for value in self.0.values() {
355                    serializer.serialize_element(value)?;
356                }
357                serializer.end()
358            }
359        }
360
361        let mut serializer = serializer.serialize_struct("PackageManifest", 4)?;
362        serializer.serialize_field("exports", &PackageExports(&self.exports))?;
363        serializer.serialize_field("modules", &PackageModules(&self.modules))?;
364        serializer.serialize_field("dependencies", &self.dependencies)?;
365        serializer.serialize_field("entrypoint", &self.entrypoint)?;
366        serializer.end()
367    }
368}
369
370#[cfg(feature = "serde")]
371impl<'de> serde::Deserialize<'de> for PackageManifest {
372    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
373    where
374        D: serde::Deserializer<'de>,
375    {
376        #[derive(serde::Deserialize)]
377        #[serde(field_identifier, rename_all = "lowercase")]
378        enum Field {
379            Exports,
380            Modules,
381            Dependencies,
382            Entrypoint,
383        }
384
385        struct PackageManifestVisitor;
386
387        impl<'de> serde::de::Visitor<'de> for PackageManifestVisitor {
388            type Value = PackageManifest;
389
390            fn expecting(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result {
391                formatter.write_str("struct PackageManifest")
392            }
393
394            fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
395            where
396                A: serde::de::SeqAccess<'de>,
397            {
398                let exports = seq
399                    .next_element::<Vec<PackageExport>>()?
400                    .ok_or_else(|| serde::de::Error::invalid_length(0, &self))?;
401                let modules = seq
402                    .next_element::<Vec<PackageModule>>()?
403                    .ok_or_else(|| serde::de::Error::invalid_length(1, &self))?;
404                let dependencies = seq
405                    .next_element::<Vec<Dependency>>()?
406                    .ok_or_else(|| serde::de::Error::invalid_length(2, &self))?;
407                let entrypoint = seq
408                    .next_element::<Option<PathBuf>>()
409                    .map(|p| p.map(|p| p.map(Arc::<ast::Path>::from)))?;
410                PackageManifest::new(exports)
411                    .and_then(|manifest| manifest.with_modules(modules))
412                    .and_then(|manifest| manifest.with_dependencies(dependencies))
413                    .and_then(|manifest| {
414                        if let Some(Some(entrypoint)) = entrypoint {
415                            manifest.with_entrypoint(entrypoint)
416                        } else {
417                            Ok(manifest)
418                        }
419                    })
420                    .map_err(serde::de::Error::custom)
421            }
422
423            fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
424            where
425                A: serde::de::MapAccess<'de>,
426            {
427                let mut exports = None;
428                let mut modules = None;
429                let mut dependencies = None;
430                let mut entrypoint = None;
431                while let Some(key) = map.next_key()? {
432                    match key {
433                        Field::Exports => {
434                            if exports.is_some() {
435                                return Err(serde::de::Error::duplicate_field("exports"));
436                            }
437                            exports = Some(map.next_value::<Vec<PackageExport>>()?);
438                        },
439                        Field::Modules => {
440                            if modules.is_some() {
441                                return Err(serde::de::Error::duplicate_field("modules"));
442                            }
443                            modules = Some(map.next_value::<Vec<PackageModule>>()?);
444                        },
445                        Field::Dependencies => {
446                            if dependencies.is_some() {
447                                return Err(serde::de::Error::duplicate_field("dependencies"));
448                            }
449                            dependencies = Some(map.next_value::<Vec<Dependency>>()?);
450                        },
451                        Field::Entrypoint => {
452                            if entrypoint.is_some() {
453                                return Err(serde::de::Error::duplicate_field("entrypoint"));
454                            }
455                            entrypoint = Some(
456                                map.next_value::<Option<PathBuf>>()
457                                    .map(|p| p.map(Arc::<ast::Path>::from))?,
458                            );
459                        },
460                    }
461                }
462                let exports = exports.ok_or_else(|| serde::de::Error::missing_field("exports"))?;
463                let modules = modules.ok_or_else(|| serde::de::Error::missing_field("modules"))?;
464                let dependencies =
465                    dependencies.ok_or_else(|| serde::de::Error::missing_field("dependencies"))?;
466                PackageManifest::new(exports)
467                    .and_then(|manifest| manifest.with_modules(modules))
468                    .and_then(|manifest| manifest.with_dependencies(dependencies))
469                    .and_then(|manifest| {
470                        if let Some(Some(entrypoint)) = entrypoint {
471                            manifest.with_entrypoint(entrypoint)
472                        } else {
473                            Ok(manifest)
474                        }
475                    })
476                    .map_err(serde::de::Error::custom)
477            }
478        }
479
480        deserializer.deserialize_struct(
481            "PackageManifest",
482            &["exports", "modules", "dependencies", "entrypoint"],
483            PackageManifestVisitor,
484        )
485    }
486}
487
488impl Serializable for PackageManifest {
489    fn write_into<W: ByteWriter>(&self, target: &mut W) {
490        // Write exports
491        target.write_usize(self.num_exports());
492        for export in self.exports() {
493            export.write_into(target);
494        }
495
496        // Write module surfaces
497        target.write_usize(self.num_modules());
498        for module in self.modules() {
499            module.write_into(target);
500        }
501
502        // Write dependencies
503        target.write_usize(self.num_dependencies());
504        for dep in self.dependencies() {
505            dep.write_into(target);
506        }
507
508        // Write entrypoint
509        if let Some(entrypoint) = self.entrypoint.as_ref() {
510            target.write_bool(true);
511            entrypoint.write_into(target);
512        } else {
513            target.write_bool(false);
514        }
515    }
516}
517
518impl PackageManifest {
519    pub fn read_from_safe<R: ByteReader>(
520        source: &mut R,
521        mast: &MastForest,
522    ) -> Result<Self, DeserializationError> {
523        // Read exports
524        let exports_len = source.read_usize()?;
525        let max_exports = source.max_alloc(PackageExport::min_serialized_size());
526        if exports_len > max_exports {
527            return Err(DeserializationError::InvalidValue(format!(
528                "requested {exports_len} elements but reader can provide at most {max_exports}"
529            )));
530        }
531        let mut exports = Vec::with_capacity(exports_len);
532        for _ in 0..exports_len {
533            exports.push(PackageExport::read_from_safe(source, mast)?);
534        }
535
536        // Read module surfaces
537        let modules_len = source.read_usize()?;
538        let max_modules = source.max_alloc(PackageModule::min_serialized_size());
539        if modules_len > max_modules {
540            return Err(DeserializationError::InvalidValue(format!(
541                "requested {modules_len} elements but reader can provide at most {max_modules}"
542            )));
543        }
544        let modules = source.read_many_iter(modules_len)?.collect::<Result<Vec<_>, _>>()?;
545
546        // Read dependencies
547        let dependencies = Vec::<Dependency>::read_from(source)?;
548
549        // Read entrypoint
550        let entrypoint = if source.read_bool()? {
551            Some(PathBuf::read_from(source).map(Arc::<ast::Path>::from)?)
552        } else {
553            None
554        };
555
556        PackageManifest::new(exports)
557            .and_then(|manifest| manifest.with_modules(modules))
558            .and_then(|manifest| manifest.with_dependencies(dependencies))
559            .and_then(|manifest| {
560                if let Some(entrypoint) = entrypoint {
561                    manifest.with_entrypoint(entrypoint)
562                } else {
563                    Ok(manifest)
564                }
565            })
566            .map_err(|error| DeserializationError::InvalidValue(error.to_string()))
567    }
568}
569
570impl Deserializable for PackageManifest {
571    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
572        // Read exports
573        let exports_len = source.read_usize()?;
574        let exports = source.read_many_iter(exports_len)?.collect::<Result<Vec<_>, _>>()?;
575
576        // Read module surfaces
577        let modules_len = source.read_usize()?;
578        let modules = source.read_many_iter(modules_len)?.collect::<Result<Vec<_>, _>>()?;
579
580        // Read dependencies
581        let dependencies = Vec::<Dependency>::read_from(source)?;
582
583        // Read entrypoint
584        let entrypoint = if source.read_bool()? {
585            Some(PathBuf::read_from(source).map(Arc::<ast::Path>::from)?)
586        } else {
587            None
588        };
589
590        PackageManifest::new(exports)
591            .and_then(|manifest| manifest.with_modules(modules))
592            .and_then(|manifest| manifest.with_dependencies(dependencies))
593            .and_then(|manifest| {
594                if let Some(entrypoint) = entrypoint {
595                    manifest.with_entrypoint(entrypoint)
596                } else {
597                    Ok(manifest)
598                }
599            })
600            .map_err(|error| DeserializationError::InvalidValue(error.to_string()))
601    }
602}
603
604// PACKAGE MODULE SURFACE SERIALIZATION/DESERIALIZATION
605// ================================================================================================
606
607impl Serializable for PackageModule {
608    fn write_into<W: ByteWriter>(&self, target: &mut W) {
609        self.path.write_into(target);
610        target.write_usize(self.submodules.len());
611        for submodule in self.submodules.iter() {
612            submodule.write_into(target);
613        }
614    }
615}
616
617impl Deserializable for PackageModule {
618    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
619        let path = PathBuf::read_from(source)?.into_boxed_path().into();
620        let submodules = Vec::<PackageSubmodule>::read_from(source)?;
621        Ok(Self { path, submodules })
622    }
623}
624
625impl Serializable for PackageSubmodule {
626    fn write_into<W: ByteWriter>(&self, target: &mut W) {
627        self.name.write_into(target);
628    }
629}
630
631impl Deserializable for PackageSubmodule {
632    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
633        let name = ast::Ident::read_from(source)?;
634        Ok(Self { name })
635    }
636}
637
638// PACKAGE EXPORT SERIALIZATION/DESERIALIZATION
639// ================================================================================================
640
641impl Serializable for PackageExport {
642    fn write_into<W: ByteWriter>(&self, target: &mut W) {
643        target.write_u8(self.tag());
644        match self {
645            Self::Procedure(export) => export.write_into(target),
646            Self::Constant(export) => export.write_into(target),
647            Self::Type(export) => export.write_into(target),
648        }
649    }
650}
651
652impl PackageExport {
653    pub fn read_from_safe<R: ByteReader>(
654        source: &mut R,
655        mast: &MastForest,
656    ) -> Result<Self, DeserializationError> {
657        match source.read_u8()? {
658            1 => ProcedureExport::read_from_safe(source, mast).map(Self::Procedure),
659            2 => ConstantExport::read_from(source).map(Self::Constant),
660            3 => TypeExport::read_from(source).map(Self::Type),
661            invalid => Err(DeserializationError::InvalidValue(format!(
662                "unexpected PackageExport tag: '{invalid}'"
663            ))),
664        }
665    }
666}
667
668impl Deserializable for PackageExport {
669    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
670        match source.read_u8()? {
671            1 => ProcedureExport::read_from(source).map(Self::Procedure),
672            2 => ConstantExport::read_from(source).map(Self::Constant),
673            3 => TypeExport::read_from(source).map(Self::Type),
674            invalid => Err(DeserializationError::InvalidValue(format!(
675                "unexpected PackageExport tag: '{invalid}'"
676            ))),
677        }
678    }
679}
680
681impl Serializable for ProcedureExport {
682    fn write_into<W: ByteWriter>(&self, target: &mut W) {
683        self.path.write_into(target);
684        if let Some(node_id) = self.node {
685            target.write_bool(true);
686            target.write_u32(node_id.into());
687        } else {
688            target.write_bool(false);
689        }
690        if let Some(source_node) = self.source_node {
691            target.write_bool(true);
692            source_node.write_into(target);
693        } else {
694            target.write_bool(false);
695        }
696        self.digest.write_into(target);
697        match self.signature.as_ref() {
698            Some(sig) => {
699                target.write_bool(true);
700                sig.write_into(target);
701            },
702            None => {
703                target.write_bool(false);
704            },
705        }
706        self.attributes.write_into(target);
707    }
708}
709
710impl ProcedureExport {
711    pub fn read_from_safe<R: ByteReader>(
712        source: &mut R,
713        mast: &MastForest,
714    ) -> Result<Self, DeserializationError> {
715        use miden_assembly_syntax::ast::types::FunctionType;
716        let path = PathBuf::read_from(source)?.into_boxed_path().into();
717        let node = if source.read_bool()? {
718            let node_id = MastNodeId::from_u32_safe(source.read_u32()?, mast)?;
719            if !mast.is_procedure_root(node_id) {
720                return Err(DeserializationError::InvalidValue(
721                    ManifestValidationError::InvalidProcedureExport { path }.to_string(),
722                ));
723            }
724            Some(node_id)
725        } else {
726            None
727        };
728        let source_node = if source.read_bool()? {
729            Some(DebugSourceNodeId::read_from(source)?)
730        } else {
731            None
732        };
733        let digest = Word::read_from(source)?;
734        // Ensure that the digest associated with `node` matches the provided digest
735        if let Some(node) = node
736            && digest != mast[node].digest()
737        {
738            return Err(DeserializationError::InvalidValue(
739                ManifestValidationError::InvalidProcedureExport { path }.to_string(),
740            ));
741        }
742        let signature = if source.read_bool()? {
743            Some(FunctionType::read_from(source)?)
744        } else {
745            None
746        };
747        let attributes = AttributeSet::read_from(source)?;
748        Ok(Self {
749            path,
750            node,
751            source_node,
752            digest,
753            signature,
754            attributes,
755        })
756    }
757}
758
759impl Deserializable for ProcedureExport {
760    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
761        use miden_assembly_syntax::ast::types::FunctionType;
762        let path = PathBuf::read_from(source)?.into_boxed_path().into();
763        let node = if source.read_bool()? {
764            Some(MastNodeId::new_unchecked(source.read_u32()?))
765        } else {
766            None
767        };
768        let source_node = if source.read_bool()? {
769            Some(DebugSourceNodeId::read_from(source)?)
770        } else {
771            None
772        };
773        let digest = Word::read_from(source)?;
774        let signature = if source.read_bool()? {
775            Some(FunctionType::read_from(source)?)
776        } else {
777            None
778        };
779        let attributes = AttributeSet::read_from(source)?;
780        Ok(Self {
781            path,
782            node,
783            source_node,
784            digest,
785            signature,
786            attributes,
787        })
788    }
789}
790
791impl Serializable for ConstantExport {
792    fn write_into<W: ByteWriter>(&self, target: &mut W) {
793        self.path.write_into(target);
794        self.value.write_into(target);
795    }
796}
797
798impl Deserializable for ConstantExport {
799    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
800        let path = PathBuf::read_from(source)?.into_boxed_path().into();
801        let value = ast::ConstantValue::read_from(source)?;
802        Ok(Self { path, value })
803    }
804}
805
806impl Serializable for TypeExport {
807    fn write_into<W: ByteWriter>(&self, target: &mut W) {
808        self.path.write_into(target);
809        self.ty.write_into(target);
810    }
811}
812
813impl Deserializable for TypeExport {
814    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
815        use miden_assembly_syntax::ast::types::Type;
816        let path = PathBuf::read_from(source)?.into_boxed_path().into();
817        let ty = Type::read_from(source)?;
818        Ok(Self { path, ty })
819    }
820}