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