1use sha2::{Digest, Sha256};
2use sim_kernel::{ContentId, Symbol};
3
4use crate::{InstructionPolicy, LocatedCode};
5
6#[derive(Clone, Copy, Debug, PartialEq, Eq)]
8pub struct AdmissionLimits {
9 pub instructions: usize,
11 pub operand_units: usize,
13 pub slots: usize,
15 pub frames: usize,
17 pub work: usize,
19}
20
21impl AdmissionLimits {
22 fn encode(self, digest: &mut Sha256) {
23 for value in [
24 self.instructions,
25 self.operand_units,
26 self.slots,
27 self.frames,
28 self.work,
29 ] {
30 digest.update(value.to_le_bytes());
31 }
32 }
33}
34
35pub struct MachineDescription<'a, P: InstructionPolicy, M> {
37 code: &'a LocatedCode<P>,
38 limits: AdmissionLimits,
39 metadata: &'a M,
40}
41
42impl<'a, P: InstructionPolicy, M> MachineDescription<'a, P, M> {
43 pub fn new(code: &'a LocatedCode<P>, limits: AdmissionLimits, metadata: &'a M) -> Self {
45 Self {
46 code,
47 limits,
48 metadata,
49 }
50 }
51
52 pub fn code(&self) -> &LocatedCode<P> {
54 self.code
55 }
56
57 pub fn limits(&self) -> AdmissionLimits {
59 self.limits
60 }
61
62 pub fn metadata(&self) -> &M {
64 self.metadata
65 }
66}
67
68pub trait AdmissionPolicy<P: InstructionPolicy, M> {
74 type Refusal;
76
77 fn validate_description(
79 description: &MachineDescription<'_, P, M>,
80 ) -> Result<(), Self::Refusal>;
81
82 fn validate_instruction(
84 instruction: &P::Instruction,
85 metadata: &M,
86 ) -> Result<(), Self::Refusal>;
87
88 fn encode_metadata(metadata: &M, output: &mut Vec<u8>);
90
91 fn encode_instruction(instruction: &P::Instruction, output: &mut Vec<u8>);
93}
94
95#[derive(Clone, Debug, PartialEq, Eq)]
97pub enum AdmissionError<R> {
98 ZeroLimit {
100 limit: &'static str,
102 },
103 InstructionLimit {
105 actual: usize,
107 limit: usize,
109 },
110 Policy(R),
112}
113
114#[derive(Clone, Debug, PartialEq, Eq)]
116pub struct MachinePermit {
117 content_id: ContentId,
118}
119
120impl MachinePermit {
121 pub fn admit<P, M, A>(
123 description: &MachineDescription<'_, P, M>,
124 ) -> Result<Self, AdmissionError<A::Refusal>>
125 where
126 P: InstructionPolicy,
127 P::InstructionId: Copy + Eq + Ord,
128 A: AdmissionPolicy<P, M>,
129 {
130 validate_limits(description)?;
131 A::validate_description(description).map_err(AdmissionError::Policy)?;
132 for located in description.code.instructions() {
133 A::validate_instruction(located.instruction(), description.metadata)
134 .map_err(AdmissionError::Policy)?;
135 }
136 Ok(Self {
137 content_id: content_id::<P, M, A>(description),
138 })
139 }
140
141 pub fn content_id(&self) -> &ContentId {
143 &self.content_id
144 }
145
146 pub fn accepts<P, M, A>(&self, description: &MachineDescription<'_, P, M>) -> bool
148 where
149 P: InstructionPolicy,
150 P::InstructionId: Copy + Eq + Ord,
151 A: AdmissionPolicy<P, M>,
152 {
153 self.content_id == content_id::<P, M, A>(description)
154 }
155}
156
157fn validate_limits<P: InstructionPolicy, M, R>(
158 description: &MachineDescription<'_, P, M>,
159) -> Result<(), AdmissionError<R>> {
160 for (limit, value) in [
161 ("instructions", description.limits.instructions),
162 ("operand_units", description.limits.operand_units),
163 ("slots", description.limits.slots),
164 ("frames", description.limits.frames),
165 ("work", description.limits.work),
166 ] {
167 if value == 0 {
168 return Err(AdmissionError::ZeroLimit { limit });
169 }
170 }
171 if description.code.len() > description.limits.instructions {
172 return Err(AdmissionError::InstructionLimit {
173 actual: description.code.len(),
174 limit: description.limits.instructions,
175 });
176 }
177 Ok(())
178}
179
180fn content_id<P, M, A>(description: &MachineDescription<'_, P, M>) -> ContentId
181where
182 P: InstructionPolicy,
183 P::InstructionId: Copy + Eq + Ord,
184 A: AdmissionPolicy<P, M>,
185{
186 let mut digest = Sha256::new();
187 digest.update(b"sim-lib-machine/admission/v1\0");
188 description.limits.encode(&mut digest);
189 let mut encoded = Vec::new();
190 A::encode_metadata(description.metadata, &mut encoded);
191 hash_field(&mut digest, &encoded);
192 description
193 .code
194 .hash_structure(&mut digest, |instruction, output| {
195 A::encode_instruction(instruction, output);
196 });
197 ContentId::from_bytes(
198 Symbol::qualified("core", "sha256"),
199 digest.finalize().into(),
200 )
201}
202
203fn hash_field(digest: &mut Sha256, bytes: &[u8]) {
204 digest.update(bytes.len().to_le_bytes());
205 digest.update(bytes);
206}