Skip to main content

runmat_execution/executable/
component.rs

1use serde::{Deserialize, Serialize};
2
3use super::ExecutableComponentRevisions;
4use crate::{ContractError, Digest};
5
6pub const EXECUTABLE_COMPONENT_MAX_BYTES: u64 = 64 * 1024 * 1024;
7
8#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd, Serialize, Deserialize)]
9#[serde(rename_all = "snake_case")]
10pub enum ExecutableComponentKind {
11    Mir,
12    Analysis,
13    Bytecode,
14    VmLayout,
15    FunctionRegistry,
16    SourceMap,
17}
18
19impl ExecutableComponentKind {
20    pub const REQUIRED: [Self; 6] = [
21        Self::Mir,
22        Self::Analysis,
23        Self::Bytecode,
24        Self::VmLayout,
25        Self::FunctionRegistry,
26        Self::SourceMap,
27    ];
28
29    pub(crate) const fn schema(self, revisions: &ExecutableComponentRevisions) -> u16 {
30        match self {
31            Self::Mir => revisions.mir_schema,
32            Self::Analysis => revisions.analysis_schema,
33            Self::Bytecode => revisions.bytecode_schema,
34            Self::VmLayout => revisions.vm_layout_schema,
35            Self::FunctionRegistry => revisions.function_registry_schema,
36            Self::SourceMap => revisions.source_map_schema,
37        }
38    }
39}
40
41#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
42#[serde(deny_unknown_fields)]
43pub struct ExecutableComponentDescriptor {
44    pub kind: ExecutableComponentKind,
45    pub schema_version: u16,
46    pub digest: Digest,
47    pub encoded_length: u64,
48}
49
50impl ExecutableComponentDescriptor {
51    pub fn from_payload(
52        kind: ExecutableComponentKind,
53        schema_version: u16,
54        bytes: &[u8],
55    ) -> Result<Self, ContractError> {
56        let descriptor = Self {
57            kind,
58            schema_version,
59            digest: Digest::sha256(bytes),
60            encoded_length: bytes.len() as u64,
61        };
62        descriptor.validate()?;
63        Ok(descriptor)
64    }
65
66    fn validate(&self) -> Result<(), ContractError> {
67        if self.schema_version == 0 {
68            return Err(ContractError::invalid(
69                "executable.components.schema_version",
70                "version must be non-zero",
71            ));
72        }
73        if self.encoded_length == 0 {
74            return Err(ContractError::invalid(
75                "executable.components.encoded_length",
76                "required component payloads must not be empty",
77            ));
78        }
79        if self.encoded_length > EXECUTABLE_COMPONENT_MAX_BYTES {
80            return Err(ContractError::Limit {
81                field: "executable.components.encoded_length",
82                limit: EXECUTABLE_COMPONENT_MAX_BYTES,
83            });
84        }
85        Ok(())
86    }
87}
88
89#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
90#[serde(deny_unknown_fields)]
91pub struct ExecutableComponentPayload {
92    pub kind: ExecutableComponentKind,
93    pub bytes: Vec<u8>,
94}
95
96impl ExecutableComponentPayload {
97    pub fn new(kind: ExecutableComponentKind, bytes: Vec<u8>) -> Result<Self, ContractError> {
98        if bytes.is_empty() {
99            return Err(ContractError::invalid(
100                "executable.payloads.bytes",
101                "required component payloads must not be empty",
102            ));
103        }
104        if bytes.len() as u64 > EXECUTABLE_COMPONENT_MAX_BYTES {
105            return Err(ContractError::Limit {
106                field: "executable.payloads.bytes",
107                limit: EXECUTABLE_COMPONENT_MAX_BYTES,
108            });
109        }
110        Ok(Self { kind, bytes })
111    }
112}
113
114pub(crate) fn validate_component_descriptors(
115    components: &[ExecutableComponentDescriptor],
116    revisions: &ExecutableComponentRevisions,
117) -> Result<(), ContractError> {
118    if components.len() != ExecutableComponentKind::REQUIRED.len() {
119        return Err(ContractError::invalid(
120            "executable.components",
121            "all required executable components must be present exactly once",
122        ));
123    }
124    for (descriptor, required) in components.iter().zip(ExecutableComponentKind::REQUIRED) {
125        descriptor.validate()?;
126        if descriptor.kind != required {
127            return Err(ContractError::invalid(
128                "executable.components",
129                "components must be complete, unique, and sorted by kind",
130            ));
131        }
132        if descriptor.schema_version != descriptor.kind.schema(revisions) {
133            return Err(ContractError::invalid(
134                "executable.components.schema_version",
135                "component schema does not match executable revisions",
136            ));
137        }
138    }
139    Ok(())
140}