1#[cfg(test)]
8mod tests;
9
10mod decode;
11mod encode;
12mod parser;
13
14use crate::{Error, Limits, contract};
15use alloc::vec::Vec;
16use core::cell::Cell;
17use serde::{Deserialize, Serialize};
18
19pub const fn method_id(name: &str) -> u64 {
32 let mut hash = 14_695_981_039_346_656_037u64;
33 let domain = b"ic-wire:method:";
34 let mut index = 0;
35 while index < domain.len() {
36 hash = (hash ^ domain[index] as u64).wrapping_mul(1_099_511_628_211);
37 index += 1;
38 }
39 let bytes = name.as_bytes();
40 index = 0;
41 while index < bytes.len() {
42 hash = (hash ^ bytes[index] as u64).wrapping_mul(1_099_511_628_211);
43 index += 1;
44 }
45 hash
46}
47
48struct Budget {
49 nodes: Cell<usize>,
50 heap_units: Cell<usize>,
51 depth: usize,
52 refused: Cell<bool>,
53}
54impl Budget {
55 fn new(limits: Limits) -> Self {
56 Self {
57 nodes: Cell::new(limits.nodes),
58 heap_units: Cell::new(limits.allocation.saturating_sub(limits.bytes) / 8),
63 depth: limits.depth,
64 refused: Cell::new(limits.allocation < limits.bytes),
65 }
66 }
67 #[cfg_attr(target_arch = "wasm32", inline(always))]
68 fn enter(&self, depth: usize, size: usize) -> bool {
69 self.enter_many(depth, size, 1)
70 }
71 #[cfg_attr(target_arch = "wasm32", inline(always))]
72 fn enter_many(&self, depth: usize, size: usize, count: usize) -> bool {
77 if self.refused.get() {
78 return false;
79 }
80 let nodes = self.nodes.get();
81 let heap = self.heap_units.get();
82 if nodes < count || depth > self.depth || heap < size {
83 self.refused.set(true);
84 return false;
85 }
86 self.heap_units.set(heap - size);
87 self.nodes.set(nodes - count);
88 true
89 }
90 #[cfg_attr(target_arch = "wasm32", inline(always))]
91 fn bytes(&self, bytes: usize) -> bool {
92 let Some(heap) = self.heap_units.get().checked_sub(bytes) else {
93 self.refused.set(true);
94 return false;
95 };
96 self.heap_units.set(heap);
97 true
98 }
99 fn error(&self) -> Error {
100 if self.refused.get() {
101 Error::Limit
102 } else {
103 Error::Malformed
104 }
105 }
106}
107
108pub fn encoded_len<T: Serialize + ?Sized>(value: &T, limits: Limits) -> Result<usize, Error> {
110 let budget = Budget::new(limits);
111 let size = postcard::experimental::serialized_size(&encode::Bounded {
112 value,
113 budget: &budget,
114 depth: 1,
115 })
116 .map_err(|_| budget.error())?;
117 if size > limits.bytes {
118 return Err(Error::Limit);
119 }
120 Ok(size)
121}
122
123pub fn encode<T: Serialize + ?Sized>(value: &T, limits: Limits) -> Result<Vec<u8>, Error> {
125 encode_prefixed(value, limits, &[])
126}
127fn encode_prefixed<T: Serialize + ?Sized>(
128 value: &T,
129 limits: Limits,
130 prefix: &[u8],
131) -> Result<Vec<u8>, Error> {
132 let mut bytes = Vec::new();
133 encode_prefixed_into(value, limits, prefix, &mut bytes)?;
134 Ok(bytes)
135}
136
137pub fn encode_into<T: Serialize + ?Sized>(
141 value: &T,
142 limits: Limits,
143 bytes: &mut Vec<u8>,
144) -> Result<(), Error> {
145 encode_prefixed_into(value, limits, &[], bytes)
146}
147
148pub fn encode_to_slice<'a, T: Serialize + ?Sized>(
156 value: &T,
157 limits: Limits,
158 storage: &'a mut [u8],
159) -> Result<&'a mut [u8], Error> {
160 encode_prefixed_to_slice(value, limits, &[], storage)
161}
162
163pub fn with_encoded<T: Serialize, R>(
199 value: T,
200 limits: Limits,
201 scratch: &mut [u8],
202 consume: impl FnOnce(&[u8]) -> R,
203) -> Result<R, Error> {
204 let size = encoded_len(&value, limits)?;
205 let budget = Budget::new(limits);
206 let bounded = encode::Bounded {
207 value: &value,
208 budget: &budget,
209 depth: 1,
210 };
211 if let Some(storage) = scratch.get_mut(..size) {
215 let bytes = postcard::to_slice(&bounded, storage).map_err(|_| budget.error())?;
216 drop(value);
217 Ok(consume(bytes))
218 } else {
219 let mut bytes = Vec::new();
220 bytes
221 .try_reserve_exact(size)
222 .map_err(|_| Error::Allocation)?;
223 let bytes =
224 postcard::serialize_with_flavor(&bounded, contract::Output { bytes, limit: size })
225 .map_err(|_| budget.error())?;
226 drop(value);
227 Ok(consume(&bytes))
228 }
229}
230
231pub fn encode_request_to_slice<'a, T: Serialize + ?Sized>(
233 id: u64,
234 value: &T,
235 limits: Limits,
236 storage: &'a mut [u8],
237) -> Result<&'a mut [u8], Error> {
238 encode_prefixed_to_slice(value, limits, &contract::header(id, 0), storage)
239}
240
241pub fn encode_response_to_slice<'a, T: Serialize + ?Sized>(
243 id: u64,
244 value: &T,
245 limits: Limits,
246 storage: &'a mut [u8],
247) -> Result<&'a mut [u8], Error> {
248 encode_prefixed_to_slice(value, limits, &contract::header(id, 1), storage)
249}
250
251fn encode_prefixed_to_slice<'a, T: Serialize + ?Sized>(
252 value: &T,
253 limits: Limits,
254 prefix: &[u8],
255 storage: &'a mut [u8],
256) -> Result<&'a mut [u8], Error> {
257 let size = encoded_len(value, payload_limits(limits, prefix.len())?)?;
258 let output = storage.get_mut(..size + prefix.len()).ok_or(Error::Limit)?;
259 output[..prefix.len()].copy_from_slice(prefix);
260 let budget = Budget::new(limits);
261 let payload = postcard::to_slice(
266 &encode::Bounded {
267 value,
268 budget: &budget,
269 depth: 1,
270 },
271 &mut output[prefix.len()..],
272 )
273 .map_err(|_| budget.error())?;
274 let written = prefix.len() + payload.len();
275 Ok(&mut output[..written])
276}
277
278fn encode_prefixed_into<T: Serialize + ?Sized>(
279 value: &T,
280 limits: Limits,
281 prefix: &[u8],
282 bytes: &mut Vec<u8>,
283) -> Result<(), Error> {
284 bytes.clear();
285 let size = encoded_len(value, payload_limits(limits, prefix.len())?)?;
286 bytes
287 .try_reserve_exact(size + prefix.len())
288 .map_err(|_| Error::Allocation)?;
289 bytes.extend_from_slice(prefix);
290 let budget = Budget::new(limits);
291 *bytes = postcard::serialize_with_flavor(
292 &encode::Bounded {
293 value,
294 budget: &budget,
295 depth: 1,
296 },
297 contract::Output {
298 bytes: core::mem::take(bytes),
299 limit: size + prefix.len(),
300 },
301 )
302 .map_err(|_| budget.error())?;
303 Ok(())
304}
305
306pub fn encode_request_into<T: Serialize + ?Sized>(
308 id: u64,
309 value: &T,
310 limits: Limits,
311 bytes: &mut Vec<u8>,
312) -> Result<(), Error> {
313 encode_prefixed_into(value, limits, &contract::header(id, 0), bytes)
314}
315
316pub fn encode_response_into<T: Serialize + ?Sized>(
318 id: u64,
319 value: &T,
320 limits: Limits,
321 bytes: &mut Vec<u8>,
322) -> Result<(), Error> {
323 encode_prefixed_into(value, limits, &contract::header(id, 1), bytes)
324}
325
326pub fn decode<'de, T: Deserialize<'de>>(bytes: &'de [u8], limits: Limits) -> Result<T, Error> {
330 if bytes.len() > limits.bytes {
331 return Err(Error::Limit);
332 }
333 let budget = Budget::new(limits);
334 let mut input = parser::Deserializer::from_bytes(bytes);
335 let value = T::deserialize(decode::Bounded {
336 inner: &mut input,
337 budget: &budget,
338 depth: 1,
339 })
340 .map_err(|_| budget.error())?;
341 if !input.finalize().map_err(|_| Error::Malformed)?.is_empty() {
342 return Err(Error::Trailing);
343 }
344 Ok(value)
349}
350
351pub fn encode_request<T: Serialize + ?Sized>(
353 id: u64,
354 value: &T,
355 limits: Limits,
356) -> Result<Vec<u8>, Error> {
357 encode_prefixed(value, limits, &contract::header(id, 0))
358}
359pub fn encode_response<T: Serialize + ?Sized>(
361 id: u64,
362 value: &T,
363 limits: Limits,
364) -> Result<Vec<u8>, Error> {
365 encode_prefixed(value, limits, &contract::header(id, 1))
366}
367pub fn encoded_frame_len<T: Serialize + ?Sized>(value: &T, limits: Limits) -> Result<usize, Error> {
369 encoded_len(value, payload_limits(limits, contract::HEADER)?)
370 .map(|size| size + contract::HEADER)
371}
372pub fn decode_request<'de, T: Deserialize<'de>>(
374 id: u64,
375 bytes: &'de [u8],
376 limits: Limits,
377) -> Result<T, Error> {
378 decode_frame(id, 0, bytes, limits)
379}
380pub fn decode_response<'de, T: Deserialize<'de>>(
382 id: u64,
383 bytes: &'de [u8],
384 limits: Limits,
385) -> Result<T, Error> {
386 decode_frame(id, 1, bytes, limits)
387}
388fn decode_frame<'de, T: Deserialize<'de>>(
389 id: u64,
390 direction: u8,
391 bytes: &'de [u8],
392 limits: Limits,
393) -> Result<T, Error> {
394 if bytes.len() > limits.bytes {
395 return Err(Error::Limit);
396 }
397 let payload = contract::payload(id, direction, bytes)?;
398 decode(payload, payload_limits(limits, contract::HEADER)?)
399}
400
401fn payload_limits(limits: Limits, prefix: usize) -> Result<Limits, Error> {
402 Ok(Limits {
403 bytes: limits.bytes.checked_sub(prefix).ok_or(Error::Limit)?,
404 allocation: limits.allocation.checked_sub(prefix).ok_or(Error::Limit)?,
405 ..limits
406 })
407}
408
409pub const REQUEST_HEADER_BYTES: usize = crate::contract::HEADER;
411
412pub fn validate_request_header(
417 id: u64,
418 header: &[u8; REQUEST_HEADER_BYTES],
419) -> Result<(), crate::Error> {
420 crate::contract::payload(id, 0, header).map(|_| ())
421}
422
423#[cfg(test)]
424mod header_admission_tests {
425 #[test]
426 fn pre_copy_header_agrees_with_complete_frame_admission() {
427 let id = 7;
428 let frame = super::encode_request(
429 id,
430 &(),
431 crate::Limits {
432 bytes: 64,
433 allocation: 1024,
434 nodes: 8,
435 depth: 8,
436 },
437 )
438 .unwrap();
439 let header: [u8; super::REQUEST_HEADER_BYTES] =
440 frame[..super::REQUEST_HEADER_BYTES].try_into().unwrap();
441 assert_eq!(super::validate_request_header(id, &header), Ok(()));
442 for index in [0, 2, 10] {
443 let mut damaged = header;
444 damaged[index] ^= 1;
445 assert_eq!(
446 super::validate_request_header(id, &damaged),
447 Err(crate::Error::Identity)
448 );
449 }
450 }
451}