use crate::imp::core::bytes::Bytes32;
use crate::imp::core::capsule::Capsule;
use crate::imp::core::datasection::{decode_merkle_leaves, DataView, SectionId, DIGS_DATA_OFFSET};
use crate::imp::core::merkle::MerkleTree;
use crate::imp::extract::{extract_digs_segment, ExtractError};
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ModuleReadError {
BadWasm,
NoDataSection,
BadBlob,
MissingSection(SectionId),
BadSectionLen,
RootMismatch,
}
impl core::fmt::Display for ModuleReadError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
ModuleReadError::BadWasm => f.write_str("input is not a parseable wasm module"),
ModuleReadError::NoDataSection => {
f.write_str("no DIGS data segment at the canonical offset")
}
ModuleReadError::BadBlob => f.write_str("malformed DIGS data-section blob"),
ModuleReadError::MissingSection(id) => {
write!(f, "missing required data section: {id:?}")
}
ModuleReadError::BadSectionLen => {
f.write_str("fixed-width section has an unexpected length")
}
ModuleReadError::RootMismatch => {
f.write_str("recomputed merkle root does not match embedded CurrentRoot")
}
}
}
}
#[cfg(feature = "std")]
impl std::error::Error for ModuleReadError {}
impl Capsule {
pub fn from_module_bytes(module: &[u8]) -> Result<Capsule, ModuleReadError> {
let raw = extract_digs_segment(module, DIGS_DATA_OFFSET).map_err(|e| match e {
ExtractError::BadWasm => ModuleReadError::BadWasm,
ExtractError::NoDataSection => ModuleReadError::NoDataSection,
})?;
let view = DataView::parse(&raw).map_err(|_| ModuleReadError::BadBlob)?;
let store_id = read_bytes32(&view, SectionId::StoreId)?;
let current_root = read_bytes32(&view, SectionId::CurrentRoot)?;
let merkle_body = view
.section(SectionId::MerkleNodes)
.ok_or(ModuleReadError::MissingSection(SectionId::MerkleNodes))?;
let leaves = decode_merkle_leaves(merkle_body).map_err(|_| ModuleReadError::BadBlob)?;
let recomputed = MerkleTree::from_leaves(leaves).root();
if recomputed != current_root {
return Err(ModuleReadError::RootMismatch);
}
Ok(Capsule {
store_id,
root_hash: current_root,
})
}
}
fn read_bytes32(view: &DataView<'_>, id: SectionId) -> Result<Bytes32, ModuleReadError> {
let body = view
.section(id)
.ok_or(ModuleReadError::MissingSection(id))?;
let arr: [u8; 32] = body
.try_into()
.map_err(|_| ModuleReadError::BadSectionLen)?;
Ok(Bytes32(arr))
}