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, units: usize) -> bool {
69 self.enter_many(depth, units, 1)
70 }
71 #[cfg_attr(target_arch = "wasm32", inline(always))]
72 fn enter_many(&self, depth: usize, units: 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 < units {
83 self.refused.set(true);
84 return false;
85 }
86 self.heap_units.set(heap - units);
87 self.nodes.set(nodes - count);
88 true
89 }
90 #[cfg_attr(target_arch = "wasm32", inline(always))]
91 fn charge_buffer(&self, units: usize) -> bool {
94 let Some(heap) = self.heap_units.get().checked_sub(units) else {
95 self.refused.set(true);
96 return false;
97 };
98 self.heap_units.set(heap);
99 true
100 }
101 fn error(&self) -> Error {
102 if self.refused.get() {
103 Error::Limit
104 } else {
105 Error::Malformed
106 }
107 }
108}
109
110pub fn encoded_len<T: Serialize + ?Sized>(value: &T, limits: Limits) -> Result<usize, Error> {
112 let budget = Budget::new(limits);
113 let size = postcard::experimental::serialized_size(&encode::Bounded {
114 value,
115 budget: &budget,
116 depth: 1,
117 })
118 .map_err(|_| budget.error())?;
119 if size > limits.bytes {
120 return Err(Error::Limit);
121 }
122 Ok(size)
123}
124
125pub fn encode<T: Serialize + ?Sized>(value: &T, limits: Limits) -> Result<Vec<u8>, Error> {
127 encode_prefixed(value, limits, &[])
128}
129fn encode_prefixed<T: Serialize + ?Sized>(
130 value: &T,
131 limits: Limits,
132 prefix: &[u8],
133) -> Result<Vec<u8>, Error> {
134 let mut bytes = Vec::new();
135 encode_prefixed_into(value, limits, prefix, &mut bytes)?;
136 Ok(bytes)
137}
138
139pub fn encode_into<T: Serialize + ?Sized>(
143 value: &T,
144 limits: Limits,
145 bytes: &mut Vec<u8>,
146) -> Result<(), Error> {
147 encode_prefixed_into(value, limits, &[], bytes)
148}
149
150pub fn encode_to_slice<'a, T: Serialize + ?Sized>(
158 value: &T,
159 limits: Limits,
160 storage: &'a mut [u8],
161) -> Result<&'a mut [u8], Error> {
162 encode_prefixed_to_slice(value, limits, &[], storage)
163}
164
165pub fn with_encoded<T: Serialize, R>(
201 value: T,
202 limits: Limits,
203 scratch: &mut [u8],
204 consume: impl FnOnce(&[u8]) -> R,
205) -> Result<R, Error> {
206 let size = encoded_len(&value, limits)?;
207 let budget = Budget::new(limits);
208 let bounded = encode::Bounded {
209 value: &value,
210 budget: &budget,
211 depth: 1,
212 };
213 if let Some(storage) = scratch.get_mut(..size) {
217 let bytes = postcard::to_slice(&bounded, storage).map_err(|_| budget.error())?;
218 drop(value);
219 Ok(consume(bytes))
220 } else {
221 let mut bytes = Vec::new();
222 bytes
223 .try_reserve_exact(size)
224 .map_err(|_| Error::Allocation)?;
225 let bytes =
226 postcard::serialize_with_flavor(&bounded, contract::Output { bytes, limit: size })
227 .map_err(|_| budget.error())?;
228 drop(value);
229 Ok(consume(&bytes))
230 }
231}
232
233pub fn encode_request_to_slice<'a, T: Serialize + ?Sized>(
235 id: u64,
236 value: &T,
237 limits: Limits,
238 storage: &'a mut [u8],
239) -> Result<&'a mut [u8], Error> {
240 encode_prefixed_to_slice(value, limits, &contract::header(id, 0), storage)
241}
242
243pub fn encode_response_to_slice<'a, T: Serialize + ?Sized>(
245 id: u64,
246 value: &T,
247 limits: Limits,
248 storage: &'a mut [u8],
249) -> Result<&'a mut [u8], Error> {
250 encode_prefixed_to_slice(value, limits, &contract::header(id, 1), storage)
251}
252
253fn encode_prefixed_to_slice<'a, T: Serialize + ?Sized>(
254 value: &T,
255 limits: Limits,
256 prefix: &[u8],
257 storage: &'a mut [u8],
258) -> Result<&'a mut [u8], Error> {
259 let size = encoded_len(value, payload_limits(limits, prefix.len())?)?;
260 let output = storage.get_mut(..size + prefix.len()).ok_or(Error::Limit)?;
261 output[..prefix.len()].copy_from_slice(prefix);
262 let budget = Budget::new(limits);
263 let payload = postcard::to_slice(
268 &encode::Bounded {
269 value,
270 budget: &budget,
271 depth: 1,
272 },
273 &mut output[prefix.len()..],
274 )
275 .map_err(|_| budget.error())?;
276 let written = prefix.len() + payload.len();
277 Ok(&mut output[..written])
278}
279
280fn encode_prefixed_into<T: Serialize + ?Sized>(
281 value: &T,
282 limits: Limits,
283 prefix: &[u8],
284 bytes: &mut Vec<u8>,
285) -> Result<(), Error> {
286 bytes.clear();
287 let size = encoded_len(value, payload_limits(limits, prefix.len())?)?;
288 bytes
289 .try_reserve_exact(size + prefix.len())
290 .map_err(|_| Error::Allocation)?;
291 bytes.extend_from_slice(prefix);
292 let budget = Budget::new(limits);
293 *bytes = postcard::serialize_with_flavor(
294 &encode::Bounded {
295 value,
296 budget: &budget,
297 depth: 1,
298 },
299 contract::Output {
300 bytes: core::mem::take(bytes),
301 limit: size + prefix.len(),
302 },
303 )
304 .map_err(|_| budget.error())?;
305 Ok(())
306}
307
308pub fn encode_request_into<T: Serialize + ?Sized>(
310 id: u64,
311 value: &T,
312 limits: Limits,
313 bytes: &mut Vec<u8>,
314) -> Result<(), Error> {
315 encode_prefixed_into(value, limits, &contract::header(id, 0), bytes)
316}
317
318pub fn encode_response_into<T: Serialize + ?Sized>(
320 id: u64,
321 value: &T,
322 limits: Limits,
323 bytes: &mut Vec<u8>,
324) -> Result<(), Error> {
325 encode_prefixed_into(value, limits, &contract::header(id, 1), bytes)
326}
327
328pub fn decode<'de, T: Deserialize<'de>>(bytes: &'de [u8], limits: Limits) -> Result<T, Error> {
332 if bytes.len() > limits.bytes {
333 return Err(Error::Limit);
334 }
335 let budget = Budget::new(limits);
336 let mut input = parser::Deserializer::from_bytes(bytes);
337 let value = T::deserialize(decode::Bounded {
338 inner: &mut input,
339 budget: &budget,
340 depth: 1,
341 })
342 .map_err(|_| budget.error())?;
343 if !input.finalize().map_err(|_| Error::Malformed)?.is_empty() {
344 return Err(Error::Trailing);
345 }
346 Ok(value)
351}
352
353pub fn encode_request<T: Serialize + ?Sized>(
355 id: u64,
356 value: &T,
357 limits: Limits,
358) -> Result<Vec<u8>, Error> {
359 encode_prefixed(value, limits, &contract::header(id, 0))
360}
361pub fn encode_response<T: Serialize + ?Sized>(
363 id: u64,
364 value: &T,
365 limits: Limits,
366) -> Result<Vec<u8>, Error> {
367 encode_prefixed(value, limits, &contract::header(id, 1))
368}
369pub fn encoded_frame_len<T: Serialize + ?Sized>(value: &T, limits: Limits) -> Result<usize, Error> {
371 encoded_len(value, payload_limits(limits, contract::HEADER)?)
372 .map(|size| size + contract::HEADER)
373}
374pub fn decode_request<'de, T: Deserialize<'de>>(
376 id: u64,
377 bytes: &'de [u8],
378 limits: Limits,
379) -> Result<T, Error> {
380 decode_frame(id, 0, bytes, limits)
381}
382pub fn decode_response<'de, T: Deserialize<'de>>(
384 id: u64,
385 bytes: &'de [u8],
386 limits: Limits,
387) -> Result<T, Error> {
388 decode_frame(id, 1, bytes, limits)
389}
390fn decode_frame<'de, T: Deserialize<'de>>(
391 id: u64,
392 direction: u8,
393 bytes: &'de [u8],
394 limits: Limits,
395) -> Result<T, Error> {
396 if bytes.len() > limits.bytes {
397 return Err(Error::Limit);
398 }
399 let payload = contract::payload(id, direction, bytes)?;
400 decode(payload, payload_limits(limits, contract::HEADER)?)
401}
402
403fn payload_limits(limits: Limits, prefix: usize) -> Result<Limits, Error> {
404 Ok(Limits {
405 bytes: limits.bytes.checked_sub(prefix).ok_or(Error::Limit)?,
406 allocation: limits.allocation.checked_sub(prefix).ok_or(Error::Limit)?,
407 ..limits
408 })
409}
410
411pub const REQUEST_HEADER_BYTES: usize = crate::contract::HEADER;
413
414pub fn validate_request_header(
419 id: u64,
420 header: &[u8; REQUEST_HEADER_BYTES],
421) -> Result<(), crate::Error> {
422 crate::contract::payload(id, 0, header).map(|_| ())
423}
424
425#[cfg(test)]
426mod header_admission_tests {
427 #[test]
428 fn pre_copy_header_agrees_with_complete_frame_admission() {
429 let id = 7;
430 let frame = super::encode_request(
431 id,
432 &(),
433 crate::Limits {
434 bytes: 64,
435 allocation: 1024,
436 nodes: 8,
437 depth: 8,
438 },
439 )
440 .unwrap();
441 let header: [u8; super::REQUEST_HEADER_BYTES] =
442 frame[..super::REQUEST_HEADER_BYTES].try_into().unwrap();
443 assert_eq!(super::validate_request_header(id, &header), Ok(()));
444 for index in [0, 2, 10] {
445 let mut damaged = header;
446 damaged[index] ^= 1;
447 assert_eq!(
448 super::validate_request_header(id, &damaged),
449 Err(crate::Error::Identity)
450 );
451 }
452 }
453}