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