use alloc::{
format,
string::{String, ToString},
sync::Arc,
vec::Vec,
};
use miden_assembly_syntax::ast::{self, AttributeSet, PathBuf};
use miden_core::{
Word,
mast::{MastForest, MastNodeExt, MastNodeId, UntrustedMastForest},
serde::{
BudgetedReader, ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable,
SliceReader,
},
};
use super::{
ConstantExport, PackageId, PackageModule, PackageSubmodule, ProcedureExport, TargetType,
TypeExport,
};
use crate::{
Dependency, ManifestValidationError, Package, PackageExport, PackageManifest, Section,
debug_info::DebugSourceNodeId,
};
#[cfg(test)]
mod tests;
const MAGIC_PACKAGE: &[u8; 5] = b"MASP\0";
const VERSION: [u8; 3] = [7, 0, 0];
const PACKAGE_BYTE_READ_BUDGET_MULTIPLIER: usize = 64;
impl Package {
#[doc(hidden)]
pub fn write_header_into<W: ByteWriter>(&self, target: &mut W) {
target.write_bytes(MAGIC_PACKAGE);
target.write_bytes(&VERSION);
self.name.write_into(target);
self.version.to_string().write_into(target);
self.description.write_into(target);
target.write_u8(self.kind.into());
}
#[doc(hidden)]
pub fn write_trailer_into<W: ByteWriter>(&self, target: &mut W) {
self.manifest.write_into(target);
target.write_usize(self.sections.len());
for section in self.sections.iter() {
section.write_into(target);
}
}
#[track_caller]
pub fn read_from_trusted<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
let header = Self::read_header_from(source)?;
let mast_forest = Self::read_mast_forest(source, false)?;
Self::read_from_with_header_and_mast(source, header, mast_forest, false, false)
}
#[track_caller]
pub fn read_from_bytes_trusted(bytes: &[u8]) -> Result<Self, DeserializationError> {
let budget = bytes.len().saturating_mul(PACKAGE_BYTE_READ_BUDGET_MULTIPLIER);
let mut reader = BudgetedReader::new(SliceReader::new(bytes), budget);
Self::read_from_trusted(&mut reader)
}
#[track_caller]
fn read_mast_forest<R: ByteReader>(
source: &mut R,
validate_mast_forest: bool,
) -> Result<Arc<MastForest>, DeserializationError> {
if validate_mast_forest {
UntrustedMastForest::read_from(source)?.validate().map_err(|err| {
DeserializationError::InvalidValue(format!(
"library contains an invalid untrusted MAST forest: {err}"
))
})
} else {
MastForest::read_from(source)
}
.map(Arc::new)
}
}
impl Serializable for Package {
fn write_into<W: ByteWriter>(&self, target: &mut W) {
self.write_header_into(target);
self.mast.write_into(target);
self.write_trailer_into(target);
}
}
struct PackageHeader {
name: PackageId,
version: crate::Version,
description: Option<String>,
kind: TargetType,
}
impl Package {
fn read_header_from<R: ByteReader>(
source: &mut R,
) -> Result<PackageHeader, DeserializationError> {
let magic: [u8; 5] = source.read_array()?;
if magic != *MAGIC_PACKAGE {
return Err(DeserializationError::InvalidValue(format!(
"invalid magic bytes. Expected '{MAGIC_PACKAGE:?}', got '{magic:?}'"
)));
}
let version: [u8; 3] = source.read_array()?;
if version != VERSION {
return Err(DeserializationError::InvalidValue(format!(
"unsupported version. Got '{version:?}', but only '{VERSION:?}' is supported"
)));
}
let name = PackageId::read_from(source)?;
let version = String::read_from(source)?
.parse::<crate::Version>()
.map_err(|err| DeserializationError::InvalidValue(err.to_string()))?;
let description = Option::<String>::read_from(source)?;
let kind_tag = source.read_u8()?;
let kind = TargetType::try_from(kind_tag)
.map_err(|e| DeserializationError::InvalidValue(e.to_string()))?;
Ok(PackageHeader { name, version, description, kind })
}
fn read_from_with_header_and_mast<R: ByteReader>(
source: &mut R,
header: PackageHeader,
mast: Arc<MastForest>,
validate_manifest: bool,
validate_debug_sections: bool,
) -> Result<Self, DeserializationError> {
let PackageHeader { name, version, description, kind } = header;
let manifest = if validate_manifest {
PackageManifest::read_from_safe(source, &mast)?
} else {
PackageManifest::read_from_trusted(source, &mast)?
};
let sections = Vec::<Section>::read_from(source)?;
let mut package = Self {
name,
version,
mast_forest_commitment: Default::default(),
description,
kind,
mast,
manifest,
sections,
debug_sections_trusted: true,
};
if validate_debug_sections {
package.debug_info().map_err(|err| {
DeserializationError::InvalidValue(format!(
"package contains invalid debug information: {err}"
))
})?;
}
if validate_manifest {
package
.compute_interface_commitment()
.map_err(|err| DeserializationError::InvalidValue(err.to_string()))?;
}
package.recompute_mast_commitment();
Ok(package)
}
}
impl Deserializable for Package {
#[track_caller]
fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
let header = Self::read_header_from(source)?;
let mast = Self::read_mast_forest(source, true)?;
Self::read_from_with_header_and_mast(source, header, mast, true, true)
}
#[track_caller]
fn read_from_bytes(bytes: &[u8]) -> Result<Self, DeserializationError> {
let budget = bytes.len().saturating_mul(PACKAGE_BYTE_READ_BUDGET_MULTIPLIER);
let mut reader = BudgetedReader::new(SliceReader::new(bytes), budget);
Self::read_from(&mut reader)
}
}
impl Serializable for PackageManifest {
fn write_into<W: ByteWriter>(&self, target: &mut W) {
target.write_usize(self.num_exports());
for export in self.exports() {
export.write_into(target);
}
target.write_usize(self.num_modules());
for module in self.modules() {
module.write_into(target);
}
target.write_usize(self.num_dependencies());
for dep in self.dependencies() {
dep.write_into(target);
}
if let Some(entrypoint) = self.entrypoint.as_ref() {
target.write_bool(true);
entrypoint.write_into(target);
} else {
target.write_bool(false);
}
}
}
impl PackageManifest {
pub fn read_from_trusted<R: ByteReader>(
source: &mut R,
mast: &MastForest,
) -> Result<Self, DeserializationError> {
let exports_len = source.read_usize()?;
let max_exports = source.max_alloc(PackageExport::min_serialized_size());
if exports_len > max_exports {
return Err(DeserializationError::InvalidValue(format!(
"requested {exports_len} elements but reader can provide at most {max_exports}"
)));
}
let mut exports = Vec::with_capacity(exports_len);
for _ in 0..exports_len {
exports.push(PackageExport::read_from_trusted(source, mast)?);
}
let modules_len = source.read_usize()?;
let max_modules = source.max_alloc(PackageModule::min_serialized_size());
if modules_len > max_modules {
return Err(DeserializationError::InvalidValue(format!(
"requested {modules_len} elements but reader can provide at most {max_modules}"
)));
}
let modules = source.read_many_iter(modules_len)?.collect::<Result<Vec<_>, _>>()?;
let dependencies = Vec::<Dependency>::read_from(source)?;
let entrypoint = if source.read_bool()? {
Some(PathBuf::read_from(source).map(Arc::<ast::Path>::from)?)
} else {
None
};
PackageManifest::new(exports)
.and_then(|manifest| manifest.with_modules(modules))
.and_then(|manifest| manifest.with_dependencies(dependencies))
.and_then(|manifest| {
if let Some(entrypoint) = entrypoint {
manifest.with_entrypoint(entrypoint)
} else {
Ok(manifest)
}
})
.map_err(|error| DeserializationError::InvalidValue(error.to_string()))
}
pub fn read_from_safe<R: ByteReader>(
source: &mut R,
mast: &MastForest,
) -> Result<Self, DeserializationError> {
let exports_len = source.read_usize()?;
let max_exports = source.max_alloc(PackageExport::min_serialized_size());
if exports_len > max_exports {
return Err(DeserializationError::InvalidValue(format!(
"requested {exports_len} elements but reader can provide at most {max_exports}"
)));
}
let mut exports = Vec::with_capacity(exports_len);
for _ in 0..exports_len {
exports.push(PackageExport::read_from_safe(source, mast)?);
}
let modules_len = source.read_usize()?;
let max_modules = source.max_alloc(PackageModule::min_serialized_size());
if modules_len > max_modules {
return Err(DeserializationError::InvalidValue(format!(
"requested {modules_len} elements but reader can provide at most {max_modules}"
)));
}
let modules = source.read_many_iter(modules_len)?.collect::<Result<Vec<_>, _>>()?;
let dependencies = Vec::<Dependency>::read_from(source)?;
let entrypoint = if source.read_bool()? {
Some(PathBuf::read_from(source).map(Arc::<ast::Path>::from)?)
} else {
None
};
PackageManifest::new(exports)
.and_then(|manifest| manifest.with_modules(modules))
.and_then(|manifest| manifest.with_dependencies(dependencies))
.and_then(|manifest| {
if let Some(entrypoint) = entrypoint {
manifest.with_entrypoint(entrypoint)
} else {
Ok(manifest)
}
})
.map_err(|error| DeserializationError::InvalidValue(error.to_string()))
}
}
impl Deserializable for PackageManifest {
fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
let exports_len = source.read_usize()?;
let exports = source.read_many_iter(exports_len)?.collect::<Result<Vec<_>, _>>()?;
let modules_len = source.read_usize()?;
let modules = source.read_many_iter(modules_len)?.collect::<Result<Vec<_>, _>>()?;
let dependencies = Vec::<Dependency>::read_from(source)?;
let entrypoint = if source.read_bool()? {
Some(PathBuf::read_from(source).map(Arc::<ast::Path>::from)?)
} else {
None
};
PackageManifest::new(exports)
.and_then(|manifest| manifest.with_modules(modules))
.and_then(|manifest| manifest.with_dependencies(dependencies))
.and_then(|manifest| {
if let Some(entrypoint) = entrypoint {
manifest.with_entrypoint(entrypoint)
} else {
Ok(manifest)
}
})
.map_err(|error| DeserializationError::InvalidValue(error.to_string()))
}
}
impl Serializable for PackageModule {
fn write_into<W: ByteWriter>(&self, target: &mut W) {
self.path.write_into(target);
target.write_usize(self.submodules.len());
for submodule in self.submodules.iter() {
submodule.write_into(target);
}
}
}
impl Deserializable for PackageModule {
fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
let path = PathBuf::read_from(source)?.into_boxed_path().into();
let submodules = Vec::<PackageSubmodule>::read_from(source)?;
Ok(Self { path, submodules })
}
}
impl Serializable for PackageSubmodule {
fn write_into<W: ByteWriter>(&self, target: &mut W) {
self.name.write_into(target);
}
}
impl Deserializable for PackageSubmodule {
fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
let name = ast::Ident::read_from(source)?;
Ok(Self { name })
}
}
impl Serializable for PackageExport {
fn write_into<W: ByteWriter>(&self, target: &mut W) {
target.write_u8(self.tag());
match self {
Self::Procedure(export) => export.write_into(target),
Self::Constant(export) => export.write_into(target),
Self::Type(export) => export.write_into(target),
}
}
}
impl PackageExport {
pub fn read_from_trusted<R: ByteReader>(
source: &mut R,
mast: &MastForest,
) -> Result<Self, DeserializationError> {
match source.read_u8()? {
1 => ProcedureExport::read_from_trusted(source, mast).map(Self::Procedure),
2 => ConstantExport::read_from(source).map(Self::Constant),
3 => TypeExport::read_from(source).map(Self::Type),
invalid => Err(DeserializationError::InvalidValue(format!(
"unexpected PackageExport tag: '{invalid}'"
))),
}
}
pub fn read_from_safe<R: ByteReader>(
source: &mut R,
mast: &MastForest,
) -> Result<Self, DeserializationError> {
match source.read_u8()? {
1 => ProcedureExport::read_from_safe(source, mast).map(Self::Procedure),
2 => ConstantExport::read_from(source).map(Self::Constant),
3 => TypeExport::read_from(source).map(Self::Type),
invalid => Err(DeserializationError::InvalidValue(format!(
"unexpected PackageExport tag: '{invalid}'"
))),
}
}
}
impl Deserializable for PackageExport {
fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
match source.read_u8()? {
1 => ProcedureExport::read_from(source).map(Self::Procedure),
2 => ConstantExport::read_from(source).map(Self::Constant),
3 => TypeExport::read_from(source).map(Self::Type),
invalid => Err(DeserializationError::InvalidValue(format!(
"unexpected PackageExport tag: '{invalid}'"
))),
}
}
}
impl Serializable for ProcedureExport {
fn write_into<W: ByteWriter>(&self, target: &mut W) {
self.path.write_into(target);
if let Some(node_id) = self.node {
target.write_bool(true);
target.write_u32(node_id.into());
} else {
target.write_bool(false);
}
if let Some(source_node) = self.source_node {
target.write_bool(true);
source_node.write_into(target);
} else {
target.write_bool(false);
}
self.digest.write_into(target);
match self.signature.as_ref() {
Some(sig) => {
target.write_bool(true);
sig.write_into(target);
},
None => {
target.write_bool(false);
},
}
self.attributes.write_into(target);
}
}
impl ProcedureExport {
pub fn read_from_trusted<R: ByteReader>(
source: &mut R,
mast: &MastForest,
) -> Result<Self, DeserializationError> {
use miden_assembly_syntax::ast::types::FunctionType;
let path = PathBuf::read_from(source)?.into_boxed_path().into();
let node = if source.read_bool()? {
Some(MastNodeId::from_u32_safe(source.read_u32()?, mast)?)
} else {
None
};
let source_node = if source.read_bool()? {
Some(DebugSourceNodeId::read_from(source)?)
} else {
None
};
let digest = Word::read_from(source)?;
let signature = if source.read_bool()? {
Some(FunctionType::read_from(source)?)
} else {
None
};
let attributes = AttributeSet::read_from(source)?;
Ok(Self {
path,
node,
source_node,
digest,
signature,
attributes,
})
}
pub fn read_from_safe<R: ByteReader>(
source: &mut R,
mast: &MastForest,
) -> Result<Self, DeserializationError> {
use miden_assembly_syntax::ast::types::FunctionType;
let path = PathBuf::read_from(source)?.into_boxed_path().into();
let node = if source.read_bool()? {
let node_id = MastNodeId::from_u32_safe(source.read_u32()?, mast)?;
if !mast.is_procedure_root(node_id) {
return Err(DeserializationError::InvalidValue(
ManifestValidationError::InvalidProcedureExport { path }.to_string(),
));
}
Some(node_id)
} else {
None
};
let source_node = if source.read_bool()? {
Some(DebugSourceNodeId::read_from(source)?)
} else {
None
};
let digest = Word::read_from(source)?;
if let Some(node) = node
&& digest != mast[node].digest()
{
return Err(DeserializationError::InvalidValue(
ManifestValidationError::InvalidProcedureExport { path }.to_string(),
));
}
let signature = if source.read_bool()? {
Some(FunctionType::read_from(source)?)
} else {
None
};
let attributes = AttributeSet::read_from(source)?;
Ok(Self {
path,
node,
source_node,
digest,
signature,
attributes,
})
}
}
impl Deserializable for ProcedureExport {
fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
use miden_assembly_syntax::ast::types::FunctionType;
let path = PathBuf::read_from(source)?.into_boxed_path().into();
let node = if source.read_bool()? {
Some(MastNodeId::new_unchecked(source.read_u32()?))
} else {
None
};
let source_node = if source.read_bool()? {
Some(DebugSourceNodeId::read_from(source)?)
} else {
None
};
let digest = Word::read_from(source)?;
let signature = if source.read_bool()? {
Some(FunctionType::read_from(source)?)
} else {
None
};
let attributes = AttributeSet::read_from(source)?;
Ok(Self {
path,
node,
source_node,
digest,
signature,
attributes,
})
}
}
impl Serializable for ConstantExport {
fn write_into<W: ByteWriter>(&self, target: &mut W) {
self.path.write_into(target);
self.value.write_into(target);
}
}
impl Deserializable for ConstantExport {
fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
let path = PathBuf::read_from(source)?.into_boxed_path().into();
let value = ast::ConstantValue::read_from(source)?;
Ok(Self { path, value })
}
}
impl Serializable for TypeExport {
fn write_into<W: ByteWriter>(&self, target: &mut W) {
self.path.write_into(target);
self.ty.write_into(target);
}
}
impl Deserializable for TypeExport {
fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
use miden_assembly_syntax::ast::types::Type;
let path = PathBuf::read_from(source)?.into_boxed_path().into();
let ty = Type::read_from(source)?;
Ok(Self { path, ty })
}
}