Skip to main content

miden_mast_package/package/
serialization.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
22use alloc::{
23    format,
24    string::{String, ToString},
25    sync::Arc,
26    vec::Vec,
27};
28
29use miden_assembly_syntax::{
30    Library,
31    ast::{AttributeSet, PathBuf},
32};
33use miden_core::{
34    Word,
35    serde::{ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable},
36};
37
38use super::{ConstantExport, PackageId, ProcedureExport, TargetType, TypeExport};
39use crate::{Dependency, Package, PackageExport, PackageManifest, Section};
40
41// CONSTANTS
42// ================================================================================================
43
44/// Magic string for detecting that a file is serialized [`Package`]
45const MAGIC_PACKAGE: &[u8; 5] = b"MASP\0";
46
47/// The format version.
48///
49/// If future modifications are made to this format, the version should be incremented by 1.
50const VERSION: [u8; 3] = [4, 0, 0];
51
52// PACKAGE SERIALIZATION/DESERIALIZATION
53// ================================================================================================
54
55impl Serializable for Package {
56    fn write_into<W: ByteWriter>(&self, target: &mut W) {
57        // Write magic & version
58        target.write_bytes(MAGIC_PACKAGE);
59        target.write_bytes(&VERSION);
60
61        // Write package name
62        self.name.write_into(target);
63
64        // Write package version
65        self.version.to_string().write_into(target);
66
67        // Write package description
68        self.description.write_into(target);
69
70        // Write package kind
71        target.write_u8(self.kind.into());
72
73        // Write MAST artifact
74        self.mast.write_into(target);
75
76        // Write manifest
77        self.manifest.write_into(target);
78
79        // Write custom sections
80        target.write_usize(self.sections.len());
81        for section in self.sections.iter() {
82            section.write_into(target);
83        }
84    }
85}
86
87impl Deserializable for Package {
88    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
89        // Read and validate magic & version
90        let magic: [u8; 5] = source.read_array()?;
91        if magic != *MAGIC_PACKAGE {
92            return Err(DeserializationError::InvalidValue(format!(
93                "invalid magic bytes. Expected '{MAGIC_PACKAGE:?}', got '{magic:?}'"
94            )));
95        }
96
97        let version: [u8; 3] = source.read_array()?;
98        if version != VERSION {
99            return Err(DeserializationError::InvalidValue(format!(
100                "unsupported version. Got '{version:?}', but only '{VERSION:?}' is supported"
101            )));
102        }
103
104        // Read package name
105        let name = PackageId::read_from(source)?;
106
107        // Read package version
108        let version = String::read_from(source)?
109            .parse::<crate::Version>()
110            .map_err(|err| DeserializationError::InvalidValue(err.to_string()))?;
111
112        // Read package description
113        let description = Option::<String>::read_from(source)?;
114
115        // Read package kind
116        let kind_tag = source.read_u8()?;
117        let kind = TargetType::try_from(kind_tag)
118            .map_err(|e| DeserializationError::InvalidValue(e.to_string()))?;
119
120        // Read MAST artifact
121        let mast = Arc::new(Library::read_from(source)?);
122
123        // Read manifest
124        let manifest = PackageManifest::read_from(source)?;
125
126        // Read custom sections
127        let sections = Vec::<Section>::read_from(source)?;
128
129        Ok(Self {
130            name,
131            version,
132            description,
133            kind,
134            mast,
135            manifest,
136            sections,
137        })
138    }
139}
140
141// PACKAGE MANIFEST SERIALIZATION/DESERIALIZATION
142// ================================================================================================
143
144#[cfg(feature = "serde")]
145impl serde::Serialize for PackageManifest {
146    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
147    where
148        S: serde::Serializer,
149    {
150        use alloc::collections::BTreeMap;
151
152        use miden_assembly_syntax::Path;
153        use serde::ser::SerializeStruct;
154
155        struct PackageExports<'a>(&'a BTreeMap<Arc<Path>, PackageExport>);
156
157        impl serde::Serialize for PackageExports<'_> {
158            fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
159            where
160                S: serde::Serializer,
161            {
162                use serde::ser::SerializeSeq;
163
164                let mut serializer = serializer.serialize_seq(Some(self.0.len()))?;
165                for value in self.0.values() {
166                    serializer.serialize_element(value)?;
167                }
168                serializer.end()
169            }
170        }
171
172        let mut serializer = serializer.serialize_struct("PackageManifest", 2)?;
173        serializer.serialize_field("exports", &PackageExports(&self.exports))?;
174        serializer.serialize_field("dependencies", &self.dependencies)?;
175        serializer.end()
176    }
177}
178
179#[cfg(feature = "serde")]
180impl<'de> serde::Deserialize<'de> for PackageManifest {
181    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
182    where
183        D: serde::Deserializer<'de>,
184    {
185        #[derive(serde::Deserialize)]
186        #[serde(field_identifier, rename_all = "lowercase")]
187        enum Field {
188            Exports,
189            Dependencies,
190        }
191
192        struct PackageManifestVisitor;
193
194        impl<'de> serde::de::Visitor<'de> for PackageManifestVisitor {
195            type Value = PackageManifest;
196
197            fn expecting(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result {
198                formatter.write_str("struct PackageManifest")
199            }
200
201            fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
202            where
203                A: serde::de::SeqAccess<'de>,
204            {
205                let exports = seq
206                    .next_element::<Vec<PackageExport>>()?
207                    .ok_or_else(|| serde::de::Error::invalid_length(0, &self))?;
208                let dependencies = seq
209                    .next_element::<Vec<Dependency>>()?
210                    .ok_or_else(|| serde::de::Error::invalid_length(1, &self))?;
211                PackageManifest::new(exports)
212                    .and_then(|manifest| manifest.with_dependencies(dependencies))
213                    .map_err(serde::de::Error::custom)
214            }
215
216            fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
217            where
218                A: serde::de::MapAccess<'de>,
219            {
220                let mut exports = None;
221                let mut dependencies = None;
222                while let Some(key) = map.next_key()? {
223                    match key {
224                        Field::Exports => {
225                            if exports.is_some() {
226                                return Err(serde::de::Error::duplicate_field("exports"));
227                            }
228                            exports = Some(map.next_value::<Vec<PackageExport>>()?);
229                        },
230                        Field::Dependencies => {
231                            if dependencies.is_some() {
232                                return Err(serde::de::Error::duplicate_field("dependencies"));
233                            }
234                            dependencies = Some(map.next_value::<Vec<Dependency>>()?);
235                        },
236                    }
237                }
238                let exports = exports.ok_or_else(|| serde::de::Error::missing_field("exports"))?;
239                let dependencies =
240                    dependencies.ok_or_else(|| serde::de::Error::missing_field("dependencies"))?;
241                PackageManifest::new(exports)
242                    .and_then(|manifest| manifest.with_dependencies(dependencies))
243                    .map_err(serde::de::Error::custom)
244            }
245        }
246
247        deserializer.deserialize_struct(
248            "PackageManifest",
249            &["exports", "dependencies"],
250            PackageManifestVisitor,
251        )
252    }
253}
254
255impl Serializable for PackageManifest {
256    fn write_into<W: ByteWriter>(&self, target: &mut W) {
257        // Write exports
258        target.write_usize(self.num_exports());
259        for export in self.exports() {
260            export.write_into(target);
261        }
262
263        // Write dependencies
264        target.write_usize(self.num_dependencies());
265        for dep in self.dependencies() {
266            dep.write_into(target);
267        }
268    }
269}
270
271impl Deserializable for PackageManifest {
272    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
273        // Read exports
274        let exports_len = source.read_usize()?;
275        let mut exports = Vec::with_capacity(exports_len);
276        for _ in 0..exports_len {
277            exports.push(PackageExport::read_from(source)?);
278        }
279
280        // Read dependencies
281        let dependencies = Vec::<Dependency>::read_from(source)?;
282
283        PackageManifest::new(exports)
284            .and_then(|manifest| manifest.with_dependencies(dependencies))
285            .map_err(|error| DeserializationError::InvalidValue(error.to_string()))
286    }
287}
288
289// PACKAGE EXPORT SERIALIZATION/DESERIALIZATION
290// ================================================================================================
291impl Serializable for PackageExport {
292    fn write_into<W: ByteWriter>(&self, target: &mut W) {
293        target.write_u8(self.tag());
294        match self {
295            Self::Procedure(export) => export.write_into(target),
296            Self::Constant(export) => export.write_into(target),
297            Self::Type(export) => export.write_into(target),
298        }
299    }
300}
301
302impl Deserializable for PackageExport {
303    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
304        match source.read_u8()? {
305            1 => ProcedureExport::read_from(source).map(Self::Procedure),
306            2 => ConstantExport::read_from(source).map(Self::Constant),
307            3 => TypeExport::read_from(source).map(Self::Type),
308            invalid => Err(DeserializationError::InvalidValue(format!(
309                "unexpected PackageExport tag: '{invalid}'"
310            ))),
311        }
312    }
313}
314
315impl Serializable for ProcedureExport {
316    fn write_into<W: ByteWriter>(&self, target: &mut W) {
317        self.path.write_into(target);
318        self.digest.write_into(target);
319        match self.signature.as_ref() {
320            Some(sig) => {
321                target.write_bool(true);
322                sig.write_into(target);
323            },
324            None => {
325                target.write_bool(false);
326            },
327        }
328        self.attributes.write_into(target);
329    }
330}
331
332impl Deserializable for ProcedureExport {
333    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
334        use miden_assembly_syntax::ast::types::FunctionType;
335        let path = PathBuf::read_from(source)?.into_boxed_path().into();
336        let digest = Word::read_from(source)?;
337        let signature = if source.read_bool()? {
338            Some(FunctionType::read_from(source)?)
339        } else {
340            None
341        };
342        let attributes = AttributeSet::read_from(source)?;
343        Ok(Self { path, digest, signature, attributes })
344    }
345}
346
347impl Serializable for ConstantExport {
348    fn write_into<W: ByteWriter>(&self, target: &mut W) {
349        self.path.write_into(target);
350        self.value.write_into(target);
351    }
352}
353
354impl Deserializable for ConstantExport {
355    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
356        let path = PathBuf::read_from(source)?.into_boxed_path().into();
357        let value = miden_assembly_syntax::ast::ConstantValue::read_from(source)?;
358        Ok(Self { path, value })
359    }
360}
361
362impl Serializable for TypeExport {
363    fn write_into<W: ByteWriter>(&self, target: &mut W) {
364        self.path.write_into(target);
365        self.ty.write_into(target);
366    }
367}
368
369impl Deserializable for TypeExport {
370    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
371        use miden_assembly_syntax::ast::types::Type;
372        let path = PathBuf::read_from(source)?.into_boxed_path().into();
373        let ty = Type::read_from(source)?;
374        Ok(Self { path, ty })
375    }
376}
377
378#[cfg(test)]
379mod tests {
380    use alloc::{collections::BTreeMap, string::ToString, sync::Arc, vec, vec::Vec};
381
382    use miden_assembly_syntax::{
383        Library,
384        ast::{AttributeSet, Path as AstPath, PathBuf},
385        library::{LibraryExport, ProcedureExport as LibraryProcedureExport},
386    };
387    use miden_core::{
388        mast::{BasicBlockNodeBuilder, MastForest, MastForestContributor, MastNodeExt, MastNodeId},
389        operations::Operation,
390        serde::{
391            BudgetedReader, ByteWriter, Deserializable, DeserializationError, Serializable,
392            SliceReader,
393        },
394    };
395    #[cfg(feature = "serde")]
396    use serde_json::{json, to_value};
397
398    use super::{MAGIC_PACKAGE, Package, PackageExport, PackageManifest, VERSION};
399    use crate::{
400        Dependency, ManifestValidationError, PackageId, TargetType,
401        package::manifest::ProcedureExport as PackageProcedureExport,
402    };
403
404    fn build_forest() -> (MastForest, MastNodeId) {
405        let mut forest = MastForest::new();
406        let node_id = BasicBlockNodeBuilder::new(vec![Operation::Add], Vec::new())
407            .add_to_forest(&mut forest)
408            .expect("failed to build basic block");
409        forest.make_root(node_id);
410        (forest, node_id)
411    }
412
413    fn absolute_path(name: &str) -> Arc<AstPath> {
414        let path = PathBuf::new(name).expect("invalid path");
415        let path = path.as_path().to_absolute().into_owned();
416        Arc::from(path.into_boxed_path())
417    }
418
419    fn build_library() -> Arc<Library> {
420        let (forest, node_id) = build_forest();
421        let path = absolute_path("test::proc");
422        let export = LibraryProcedureExport::new(node_id, Arc::clone(&path));
423
424        let mut exports = BTreeMap::new();
425        exports.insert(path, LibraryExport::Procedure(export));
426
427        Arc::new(Library::new(Arc::new(forest), exports).expect("failed to build library"))
428    }
429
430    fn build_package() -> Package {
431        let library = build_library();
432        let path = absolute_path("test::proc");
433        let node_id = library.get_export_node_id(path.as_ref());
434        let digest = library.mast_forest()[node_id].digest();
435
436        let export = PackageExport::Procedure(PackageProcedureExport {
437            path: Arc::clone(&path),
438            digest,
439            signature: None,
440            attributes: AttributeSet::default(),
441        });
442
443        let manifest =
444            PackageManifest::new([export]).expect("test package manifest should be valid");
445
446        Package {
447            name: PackageId::from("test_pkg"),
448            version: crate::Version::new(0, 0, 0),
449            description: None,
450            kind: TargetType::Library,
451            mast: library,
452            manifest,
453            sections: Vec::new(),
454        }
455    }
456
457    fn build_dependency() -> Dependency {
458        Dependency {
459            name: PackageId::from("dep"),
460            kind: TargetType::Library,
461            version: crate::Version::new(1, 0, 0),
462            digest: Default::default(),
463        }
464    }
465
466    fn package_bytes_with_sections_count(count: usize) -> Vec<u8> {
467        let package = build_package();
468        let mut bytes = Vec::new();
469
470        bytes.write_bytes(MAGIC_PACKAGE);
471        bytes.write_bytes(&VERSION);
472        package.name.write_into(&mut bytes);
473        package.version.to_string().write_into(&mut bytes);
474        package.description.write_into(&mut bytes);
475        bytes.write_u8(package.kind.into());
476        package.mast.write_into(&mut bytes);
477        package.manifest.write_into(&mut bytes);
478        bytes.write_usize(count);
479
480        bytes
481    }
482
483    #[test]
484    fn package_manifest_rejects_over_budget_dependencies() {
485        let mut bytes = Vec::new();
486        bytes.write_usize(0);
487        bytes.write_usize(2);
488
489        let mut reader = BudgetedReader::new(SliceReader::new(&bytes), 2);
490        let err = PackageManifest::read_from(&mut reader).unwrap_err();
491        assert!(matches!(err, DeserializationError::InvalidValue(_)));
492    }
493
494    #[test]
495    fn package_rejects_over_budget_sections() {
496        let bytes = package_bytes_with_sections_count(2);
497        let mut reader = BudgetedReader::new(SliceReader::new(&bytes), bytes.len());
498        let err = Package::read_from(&mut reader).unwrap_err();
499        assert!(matches!(err, DeserializationError::InvalidValue(_)));
500    }
501
502    #[test]
503    fn package_manifest_new_rejects_duplicate_export_paths() {
504        let library = build_library();
505        let path = absolute_path("test::proc");
506        let node_id = library.get_export_node_id(path.as_ref());
507        let digest = library.mast_forest()[node_id].digest();
508        let export = PackageExport::Procedure(PackageProcedureExport {
509            path: path.clone(),
510            digest,
511            signature: None,
512            attributes: AttributeSet::default(),
513        });
514
515        let err = PackageManifest::new([export.clone(), export])
516            .expect_err("duplicate export paths should be rejected by constructors");
517        assert_eq!(err, ManifestValidationError::DuplicateExport(path));
518    }
519
520    #[test]
521    fn package_manifest_add_dependency_rejects_duplicate_dependencies() {
522        let mut manifest =
523            PackageManifest::new([]).expect("empty package manifest should be valid");
524        let dependency = build_dependency();
525
526        manifest
527            .add_dependency(dependency.clone())
528            .expect("first dependency should be accepted");
529        let err = manifest
530            .add_dependency(dependency)
531            .expect_err("duplicate dependencies should be rejected by helpers");
532        assert_eq!(err, ManifestValidationError::DuplicateDependency(PackageId::from("dep")));
533    }
534
535    #[test]
536    fn package_manifest_rejects_duplicate_export_paths() {
537        let library = build_library();
538        let path = absolute_path("test::proc");
539        let node_id = library.get_export_node_id(path.as_ref());
540        let digest = library.mast_forest()[node_id].digest();
541        let export = PackageExport::Procedure(PackageProcedureExport {
542            path,
543            digest,
544            signature: None,
545            attributes: AttributeSet::default(),
546        });
547
548        let mut bytes = Vec::new();
549        bytes.write_usize(2);
550        export.write_into(&mut bytes);
551        export.write_into(&mut bytes);
552        bytes.write_usize(0);
553
554        let mut reader = SliceReader::new(&bytes);
555        let err = PackageManifest::read_from(&mut reader)
556            .expect_err("duplicate export paths should be rejected during deserialization");
557        assert!(matches!(err, DeserializationError::InvalidValue(_)));
558    }
559
560    #[test]
561    fn package_manifest_rejects_duplicate_dependencies() {
562        let dependency = build_dependency();
563
564        let mut bytes = Vec::new();
565        bytes.write_usize(0);
566        bytes.write_usize(2);
567        dependency.write_into(&mut bytes);
568        dependency.write_into(&mut bytes);
569
570        let mut reader = SliceReader::new(&bytes);
571        let err = PackageManifest::read_from(&mut reader)
572            .expect_err("duplicate dependencies should be rejected during deserialization");
573        assert!(matches!(err, DeserializationError::InvalidValue(_)));
574    }
575
576    #[cfg(feature = "serde")]
577    #[test]
578    fn serde_package_manifest_rejects_duplicate_export_paths() {
579        let library = build_library();
580        let path = absolute_path("test::proc");
581        let node_id = library.get_export_node_id(path.as_ref());
582        let digest = library.mast_forest()[node_id].digest();
583        let export = PackageExport::Procedure(PackageProcedureExport {
584            path,
585            digest,
586            signature: None,
587            attributes: AttributeSet::default(),
588        });
589        let export = to_value(&export).expect("export should serialize");
590
591        let manifest = serde_json::to_string(&json!({
592            "exports": [export.clone(), export],
593            "dependencies": [],
594        }))
595        .expect("manifest should serialize to JSON");
596        let err = serde_json::from_str::<PackageManifest>(&manifest)
597            .expect_err("serde deserialization should reject duplicate export paths");
598        let message = err.to_string();
599        assert!(message.contains("duplicate export path"));
600    }
601
602    #[cfg(feature = "serde")]
603    #[test]
604    fn serde_package_manifest_rejects_duplicate_dependencies() {
605        let dependency = to_value(build_dependency()).expect("dependency should serialize");
606
607        let manifest = serde_json::to_string(&json!({
608            "exports": [],
609            "dependencies": [dependency.clone(), dependency],
610        }))
611        .expect("manifest should serialize to JSON");
612        let err = serde_json::from_str::<PackageManifest>(&manifest)
613            .expect_err("serde deserialization should reject duplicate dependencies");
614        let message = err.to_string();
615        assert!(message.contains("duplicate dependency"));
616    }
617}