ex3_ic_agent/request_id/mod.rs
1//! This module deals with computing Request IDs based on the content of a
2//! message.
3//!
4//! We compute the `RequestId` according to the public spec, which
5//! specifies it as a "sha256" digest.
6//!
7//! A single method is exported, to_request_id, which returns a RequestId
8//! (a 256 bits slice) or an error.
9use error::RequestIdFromStringError;
10use serde::{ser, Deserialize, Serialize};
11use sha2::{Digest, Sha256};
12use std::{collections::BTreeMap, iter::Extend, str::FromStr};
13
14pub mod error;
15#[doc(inline)]
16pub use error::RequestIdError;
17
18/// Type alias for a sha256 result (ie. a u256).
19type Sha256Hash = [u8; 32];
20
21/// A Request ID.
22#[derive(Clone, Copy, Debug, PartialOrd, Ord, PartialEq, Eq, Deserialize, Serialize)]
23pub struct RequestId(Sha256Hash);
24
25impl RequestId {
26 /// Creates a new `RequestId` from a SHA-256 hash.
27 pub fn new(from: &[u8; 32]) -> RequestId {
28 RequestId(*from)
29 }
30
31 /// Returns the SHA-256 hash this ID is based on.
32 pub fn as_slice(&self) -> &[u8] {
33 &self.0
34 }
35
36 pub(crate) fn to_vec(self) -> Vec<u8> {
37 self.0.to_vec()
38 }
39}
40
41impl FromStr for RequestId {
42 type Err = RequestIdFromStringError;
43
44 fn from_str(from: &str) -> Result<Self, Self::Err> {
45 let mut blob: [u8; 32] = [0; 32];
46 let vec = hex::decode(from).map_err(RequestIdFromStringError::FromHexError)?;
47 if vec.len() != 32 {
48 return Err(RequestIdFromStringError::InvalidSize(vec.len()));
49 }
50
51 blob.copy_from_slice(vec.as_slice());
52 Ok(RequestId::new(&blob))
53 }
54}
55
56impl From<RequestId> for String {
57 fn from(id: RequestId) -> String {
58 hex::encode(id.0)
59 }
60}
61
62enum Hasher {
63 /// The hasher for the overall request id. This is the only part
64 /// that may directly contain a Struct.
65 RequestId(Sha256),
66
67 /// A structure to be included in the hash. May not contain other structures.
68 Struct {
69 // We use a BTreeMap here as there is no indication that keys might not be duplicated,
70 // and we want to make sure they're overwritten in that case.
71 fields: BTreeMap<Sha256Hash, Sha256Hash>,
72 parent: Box<Hasher>,
73 },
74
75 /// The hasher for a value. Array elements will append the hash of their
76 /// contents into the hasher of the array.
77 Value(Sha256),
78}
79
80impl Hasher {
81 fn request_id() -> Hasher {
82 Hasher::RequestId(Sha256::new())
83 }
84
85 fn fields(parent: Box<Hasher>) -> Hasher {
86 Hasher::Struct {
87 fields: BTreeMap::new(),
88 parent,
89 }
90 }
91
92 fn value() -> Hasher {
93 Hasher::Value(Sha256::new())
94 }
95}
96
97/// A Serde Serializer that collects fields and values in order to hash them later.
98/// We serialize the type to this structure, then use the trait to hash its content.
99/// It is a simple state machine that contains 3 states:
100/// 1. The root value, which is a structure. If a value other than a structure is
101/// serialized, this errors. This is determined by whether `fields` is Some(_).
102/// 2. The structure is being processed, and the value of a field is being
103/// serialized. The field_value_hash will be set to Some(_).
104/// 3. The finish() function has been called and the hasher cannot be reused. The
105/// hash should have been gotten at this point.
106///
107/// Inconsistent state are when a field is being serialized and `fields` is None, or
108/// when a value (not struct) is being serialized and field_value_hash is None.
109///
110/// This will always fail on types that are unknown to the Request format (e.g. i8).
111/// An UnsupportedTypeXXX error will be returned.
112///
113/// The only types that are supported right now are:
114/// . Strings and string slices.
115/// . Vector of u8 (byte strings).
116/// . A structure as the base level. Its typename and fields are not validated.
117///
118/// Additionally, this will fail if there are unsupported data structure, for example
119/// if a UnitVariant of another type than Blob is used, or a structure inside a
120/// structure.
121///
122/// This does not validate whether a message is valid. This is very important as
123/// the message format might change faster than the ID calculation.
124struct RequestIdSerializer {
125 element_encoder: Option<Hasher>,
126}
127
128impl RequestIdSerializer {
129 pub fn new() -> RequestIdSerializer {
130 Default::default()
131 }
132
133 /// Finish the hashing and returns the RequestId for the structure that was
134 /// serialized.
135 ///
136 /// This can only be called once (it borrows self). Since this whole class is not public,
137 /// it should not be a problem.
138 pub fn finish(self) -> Result<RequestId, RequestIdError> {
139 match self.element_encoder {
140 Some(Hasher::RequestId(hasher)) => Ok(RequestId(hasher.finalize().into())),
141 _ => Err(RequestIdError::EmptySerializer),
142 }
143 }
144
145 /// Hash a single value, returning its sha256_hash. If there is already a value
146 /// being hashed it will return an InvalidState. This cannot happen currently
147 /// as we don't allow embedded structures, but is left as a safeguard when
148 /// making changes.
149 fn hash_value<T>(&mut self, value: &T) -> Result<Sha256Hash, RequestIdError>
150 where
151 T: ?Sized + Serialize,
152 {
153 let prev_encoder = self.element_encoder.take();
154
155 self.element_encoder = Some(Hasher::value());
156
157 value.serialize(&mut *self)?;
158 let result = match self.element_encoder.take() {
159 Some(Hasher::Value(hasher)) => Ok(hasher.finalize().into()),
160 _ => Err(RequestIdError::InvalidState),
161 };
162 self.element_encoder = prev_encoder;
163 result
164 }
165
166 fn hash_fields(&mut self) -> Result<(), RequestIdError> {
167 match self.element_encoder.take() {
168 Some(Hasher::Struct { fields, parent }) => {
169 // Sort the fields.
170 let mut keyvalues: Vec<Vec<u8>> = fields
171 .keys()
172 .zip(fields.values())
173 .map(|(k, v)| {
174 let mut x = k.to_vec();
175 x.extend(v);
176 x
177 })
178 .collect();
179 keyvalues.sort();
180
181 let mut parent = *parent;
182
183 match &mut parent {
184 Hasher::RequestId(hasher) => {
185 for kv in keyvalues {
186 hasher.update(&kv);
187 }
188 Ok(())
189 }
190 _ => Err(RequestIdError::InvalidState),
191 }?;
192
193 self.element_encoder = Some(parent);
194 Ok(())
195 }
196 _ => Err(RequestIdError::InvalidState),
197 }
198 }
199}
200
201impl Default for RequestIdSerializer {
202 fn default() -> RequestIdSerializer {
203 RequestIdSerializer {
204 element_encoder: Some(Hasher::request_id()),
205 }
206 }
207}
208
209/// See https://serde.rs/data-format.html for more information on how to implement a
210/// custom data format.
211impl<'a> ser::Serializer for &'a mut RequestIdSerializer {
212 /// The output type produced by this `Serializer` during successful
213 /// serialization. Most serializers that produce text or binary output
214 /// should set `Ok = ()` and serialize into an [`io::Write`] or buffer
215 /// contained within the `Serializer` instance. Serializers that build
216 /// in-memory data structures may be simplified by using `Ok` to propagate
217 /// the data structure around.
218 ///
219 /// [`io::Write`]: https://doc.rust-lang.org/std/io/trait.Write.html
220 type Ok = ();
221
222 /// The error type when some error occurs during serialization.
223 type Error = RequestIdError;
224
225 // Associated types for keeping track of additional state while serializing
226 // compound data structures like sequences and maps. In this case no
227 // additional state is required beyond what is already stored in the
228 // Serializer struct.
229 type SerializeSeq = Self;
230 type SerializeTuple = Self;
231 type SerializeTupleStruct = Self;
232 type SerializeTupleVariant = Self;
233 type SerializeMap = Self;
234 type SerializeStruct = Self;
235 type SerializeStructVariant = Self;
236
237 /// Serialize a `bool` value.
238 fn serialize_bool(self, _v: bool) -> Result<Self::Ok, Self::Error> {
239 Err(RequestIdError::UnsupportedTypeBool)
240 }
241
242 /// Serialize an `i8` value.
243 fn serialize_i8(self, _v: i8) -> Result<Self::Ok, Self::Error> {
244 Err(RequestIdError::UnsupportedTypeI8)
245 }
246
247 /// Serialize an `i16` value.
248 fn serialize_i16(self, _v: i16) -> Result<Self::Ok, Self::Error> {
249 Err(RequestIdError::UnsupportedTypeI16)
250 }
251
252 /// Serialize an `i32` value.
253 fn serialize_i32(self, _v: i32) -> Result<Self::Ok, Self::Error> {
254 Err(RequestIdError::UnsupportedTypeI32)
255 }
256
257 /// Serialize an `i64` value.
258 fn serialize_i64(self, _v: i64) -> Result<Self::Ok, Self::Error> {
259 Err(RequestIdError::UnsupportedTypeI64)
260 }
261
262 /// Serialize a `u8` value.
263 fn serialize_u8(self, v: u8) -> Result<Self::Ok, Self::Error> {
264 self.serialize_u64(v as u64)
265 }
266
267 /// Serialize a `u16` value.
268 fn serialize_u16(self, v: u16) -> Result<Self::Ok, Self::Error> {
269 self.serialize_u64(v as u64)
270 }
271
272 /// Serialize a `u32` value.
273 fn serialize_u32(self, v: u32) -> Result<Self::Ok, Self::Error> {
274 self.serialize_u64(v as u64)
275 }
276
277 /// Serialize a `u64` value.
278 fn serialize_u64(self, v: u64) -> Result<Self::Ok, Self::Error> {
279 // 10 bytes is enough for a 64-bit number in leb128.
280 let mut buffer = [0; 10];
281 let mut writable = &mut buffer[..];
282 let n_bytes =
283 leb128::write::unsigned(&mut writable, v).expect("Could not serialize number.");
284 self.serialize_bytes(&buffer[..n_bytes])
285 }
286
287 /// Serialize an `f32` value.
288 fn serialize_f32(self, _v: f32) -> Result<Self::Ok, Self::Error> {
289 Err(RequestIdError::UnsupportedTypeF32)
290 }
291
292 /// Serialize an `f64` value.
293 fn serialize_f64(self, _v: f64) -> Result<Self::Ok, Self::Error> {
294 Err(RequestIdError::UnsupportedTypeF64)
295 }
296
297 /// Serialize a character.
298 fn serialize_char(self, _v: char) -> Result<Self::Ok, Self::Error> {
299 Err(RequestIdError::UnsupportedTypeChar)
300 }
301
302 /// Serialize a `&str`.
303 fn serialize_str(self, v: &str) -> Result<Self::Ok, Self::Error> {
304 self.serialize_bytes(v.as_bytes())
305 }
306
307 /// Serialize a chunk of raw byte data.
308 fn serialize_bytes(self, v: &[u8]) -> Result<Self::Ok, Self::Error> {
309 match &mut self.element_encoder {
310 Some(Hasher::RequestId(hasher)) => {
311 hasher.update(v);
312 Ok(())
313 }
314 Some(Hasher::Value(hasher)) => {
315 hasher.update(v);
316 Ok(())
317 }
318 _ => Err(RequestIdError::InvalidState),
319 }
320 }
321
322 /// Serialize a [`None`] value.
323 fn serialize_none(self) -> Result<Self::Ok, Self::Error> {
324 // Compute the hash as if it was empty string or blob.
325 Ok(())
326 }
327
328 /// Serialize a [`Some(T)`] value.
329 fn serialize_some<T: ?Sized>(self, value: &T) -> Result<Self::Ok, Self::Error>
330 where
331 T: Serialize,
332 {
333 // Compute the hash as if it was the value itself.
334 value.serialize(self)
335 }
336
337 /// Serialize a `()` value.
338 fn serialize_unit(self) -> Result<Self::Ok, Self::Error> {
339 Err(RequestIdError::UnsupportedTypeUnit)
340 }
341
342 /// Serialize a unit struct like `struct Unit` or `PhantomData<T>`.
343 fn serialize_unit_struct(self, _name: &'static str) -> Result<Self::Ok, Self::Error> {
344 Err(RequestIdError::UnsupportedTypePhantomData)
345 }
346
347 /// Serialize a unit variant like `E::A` in `enum E { A, B }`.
348 fn serialize_unit_variant(
349 self,
350 _name: &'static str,
351 _variant_index: u32,
352 _variant: &'static str,
353 ) -> Result<Self::Ok, Self::Error> {
354 Err(RequestIdError::UnsupportedTypeUnitVariant)
355 }
356
357 /// Serialize a newtype struct like `struct Millimeters(u8)`.
358 fn serialize_newtype_struct<T: ?Sized>(
359 self,
360 name: &'static str,
361 _value: &T,
362 ) -> Result<Self::Ok, Self::Error>
363 where
364 T: Serialize,
365 {
366 Err(RequestIdError::UnsupportedTypeNewtypeStruct(
367 name.to_owned(),
368 ))
369 }
370
371 /// Serialize a newtype variant like `E::N` in `enum E { N(u8) }`.
372 fn serialize_newtype_variant<T: ?Sized>(
373 self,
374 _name: &'static str,
375 _variant_index: u32,
376 _variant: &'static str,
377 _value: &T,
378 ) -> Result<Self::Ok, Self::Error>
379 where
380 T: Serialize,
381 {
382 Err(RequestIdError::UnsupportedTypeNewTypeVariant)
383 }
384
385 /// Begin to serialize a variably sized sequence. This call must be
386 /// followed by zero or more calls to `serialize_element`, then a call to
387 /// `end`.
388 fn serialize_seq(self, _len: Option<usize>) -> Result<Self::SerializeSeq, Self::Error> {
389 Ok(self)
390 }
391
392 /// Begin to serialize a statically sized sequence whose length will be
393 /// known at deserialization time without looking at the serialized data.
394 /// This call must be followed by zero or more calls to `serialize_element`,
395 /// then a call to `end`.
396 fn serialize_tuple(self, _len: usize) -> Result<Self::SerializeTuple, Self::Error> {
397 Err(RequestIdError::UnsupportedTypeTuple)
398 }
399
400 /// Begin to serialize a tuple struct like `struct Rgb(u8, u8, u8)`. This
401 /// call must be followed by zero or more calls to `serialize_field`, then a
402 /// call to `end`.
403 fn serialize_tuple_struct(
404 self,
405 _name: &'static str,
406 _len: usize,
407 ) -> Result<Self::SerializeTupleStruct, Self::Error> {
408 Err(RequestIdError::UnsupportedTypeTupleStruct)
409 }
410
411 /// Begin to serialize a tuple variant like `E::T` in `enum E { T(u8, u8)
412 /// }`. This call must be followed by zero or more calls to
413 /// `serialize_field`, then a call to `end`.
414 fn serialize_tuple_variant(
415 self,
416 _name: &'static str,
417 _variant_index: u32,
418 _variant: &'static str,
419 _len: usize,
420 ) -> Result<Self::SerializeTupleVariant, Self::Error> {
421 Err(RequestIdError::UnsupportedTypeTupleVariant)
422 }
423
424 /// Begin to serialize a map. This call must be followed by zero or more
425 /// calls to `serialize_key` and `serialize_value`, then a call to `end`.
426 fn serialize_map(self, _len: Option<usize>) -> Result<Self::SerializeMap, Self::Error> {
427 Err(RequestIdError::UnsupportedTypeMap)
428 }
429
430 /// Begin to serialize a struct like `struct Rgb { r: u8, g: u8, b: u8 }`.
431 /// This call must be followed by zero or more calls to `serialize_field`,
432 /// then a call to `end`.
433 fn serialize_struct(
434 self,
435 _name: &'static str,
436 _len: usize,
437 ) -> Result<Self::SerializeStruct, Self::Error> {
438 let parent_encoder = self.element_encoder.take();
439 match &parent_encoder {
440 Some(Hasher::RequestId(_)) => {
441 self.element_encoder = Some(Hasher::fields(Box::new(parent_encoder.unwrap())));
442 Ok(self)
443 }
444 _ => Err(RequestIdError::UnsupportedStructInsideStruct),
445 }
446 }
447
448 /// Begin to serialize a struct variant like `E::S` in `enum E { S { r: u8,
449 /// g: u8, b: u8 } }`. This call must be followed by zero or more calls to
450 /// `serialize_field`, then a call to `end`.
451 fn serialize_struct_variant(
452 self,
453 _name: &'static str,
454 _variant_index: u32,
455 _variant: &'static str,
456 _len: usize,
457 ) -> Result<Self::SerializeStructVariant, Self::Error> {
458 Err(RequestIdError::UnsupportedTypeStructVariant)
459 }
460
461 fn is_human_readable(&self) -> bool {
462 false
463 }
464}
465
466// The following 7 impls deal with the serialization of compound types like
467// sequences and maps. Serialization of such types is begun by a Serializer
468// method and followed by zero or more calls to serialize individual elements of
469// the compound type and one call to end the compound type.
470//
471// This impl is SerializeSeq so these methods are called after `serialize_seq`
472// is called on the Serializer.
473impl<'a> ser::SerializeSeq for &'a mut RequestIdSerializer {
474 // Must match the `Ok` type of the serializer.
475 type Ok = ();
476 // Must match the `Error` type of the serializer.
477 type Error = RequestIdError;
478
479 // Serialize a single element of the sequence.
480 fn serialize_element<T>(&mut self, value: &T) -> Result<Self::Ok, Self::Error>
481 where
482 T: ?Sized + Serialize,
483 {
484 let mut prev_encoder = self.element_encoder.take();
485
486 self.element_encoder = Some(Hasher::value());
487
488 value.serialize(&mut **self)?;
489
490 let value_encoder = self.element_encoder.take();
491 let hash = match value_encoder {
492 Some(Hasher::Value(hasher)) => Ok(hasher.finalize()),
493 _ => Err(RequestIdError::InvalidState),
494 }?;
495
496 self.element_encoder = prev_encoder.take();
497 match &mut self.element_encoder {
498 Some(Hasher::Value(hasher)) => {
499 hasher.update(hash);
500 Ok(())
501 }
502 _ => Err(RequestIdError::InvalidState),
503 }
504 }
505
506 // Close the sequence.
507 fn end(self) -> Result<Self::Ok, Self::Error> {
508 Ok(())
509 }
510}
511
512// Same thing but for tuples.
513impl<'a> ser::SerializeTuple for &'a mut RequestIdSerializer {
514 type Ok = ();
515 type Error = RequestIdError;
516
517 fn serialize_element<T>(&mut self, _value: &T) -> Result<Self::Ok, Self::Error>
518 where
519 T: ?Sized + Serialize,
520 {
521 Err(RequestIdError::UnsupportedTypeTuple)
522 }
523
524 fn end(self) -> Result<Self::Ok, Self::Error> {
525 Ok(())
526 }
527}
528
529// Same thing but for tuple structs.
530impl<'a> ser::SerializeTupleStruct for &'a mut RequestIdSerializer {
531 type Ok = ();
532 type Error = RequestIdError;
533
534 fn serialize_field<T>(&mut self, _value: &T) -> Result<Self::Ok, Self::Error>
535 where
536 T: ?Sized + Serialize,
537 {
538 Err(RequestIdError::UnsupportedTypeTupleStruct)
539 }
540
541 fn end(self) -> Result<Self::Ok, Self::Error> {
542 Ok(())
543 }
544}
545
546// Tuple variants are a little different. Refer back to the
547// `serialize_tuple_variant` method above:
548//
549// self.output += "{";
550// variant.serialize(&mut *self)?;
551// self.output += ":[";
552//
553// So the `end` method in this impl is responsible for closing both the `]` and
554// the `}`.
555impl<'a> ser::SerializeTupleVariant for &'a mut RequestIdSerializer {
556 type Ok = ();
557 type Error = RequestIdError;
558
559 fn serialize_field<T>(&mut self, _value: &T) -> Result<Self::Ok, Self::Error>
560 where
561 T: ?Sized + Serialize,
562 {
563 Err(RequestIdError::UnsupportedTypeTupleVariant)
564 }
565
566 fn end(self) -> Result<Self::Ok, Self::Error> {
567 Ok(())
568 }
569}
570
571// Some `Serialize` types are not able to hold a key and value in memory at the
572// same time so `SerializeMap` implementations are required to support
573// `serialize_key` and `serialize_value` individually.
574//
575// There is a third optional method on the `SerializeMap` trait. The
576// `serialize_entry` method allows serializers to optimize for the case where
577// key and value are both available simultaneously. In JSON it doesn't make a
578// difference so the default behavior for `serialize_entry` is fine.
579impl<'a> ser::SerializeMap for &'a mut RequestIdSerializer {
580 type Ok = ();
581 type Error = RequestIdError;
582
583 // The Serde data model allows map keys to be any serializable type. JSON
584 // only allows string keys so the implementation below will produce invalid
585 // JSON if the key serializes as something other than a string.
586 //
587 // A real JSON serializer would need to validate that map keys are strings.
588 // This can be done by using a different Serializer to serialize the key
589 // (instead of `&mut **self`) and having that other serializer only
590 // implement `serialize_str` and return an error on any other data type.
591 fn serialize_key<T>(&mut self, _key: &T) -> Result<Self::Ok, Self::Error>
592 where
593 T: ?Sized + Serialize,
594 {
595 Err(RequestIdError::UnsupportedTypeMap)
596 }
597
598 // It doesn't make a difference whether the colon is printed at the end of
599 // `serialize_key` or at the beginning of `serialize_value`. In this case
600 // the code is a bit simpler having it here.
601 fn serialize_value<T>(&mut self, _value: &T) -> Result<Self::Ok, Self::Error>
602 where
603 T: ?Sized + Serialize,
604 {
605 Err(RequestIdError::UnsupportedTypeMap)
606 }
607
608 fn end(self) -> Result<Self::Ok, Self::Error> {
609 self.hash_fields()
610 }
611}
612
613// Structs are like maps in which the keys are constrained to be compile-time
614// constant strings.
615impl<'a> ser::SerializeStruct for &'a mut RequestIdSerializer {
616 type Ok = ();
617 type Error = RequestIdError;
618
619 fn serialize_field<T>(&mut self, key: &'static str, value: &T) -> Result<Self::Ok, Self::Error>
620 where
621 T: ?Sized + Serialize,
622 {
623 let key_hash = self.hash_value(key)?;
624 let value_hash = self.hash_value(value)?;
625 match &mut self.element_encoder {
626 Some(Hasher::Struct { fields, .. }) => {
627 fields.insert(key_hash, value_hash);
628 Ok(())
629 }
630 _ => Err(RequestIdError::InvalidState),
631 }
632 }
633
634 fn end(self) -> Result<Self::Ok, Self::Error> {
635 self.hash_fields()
636 }
637}
638
639// Similar to `SerializeTupleVariant`, here the `end` method is responsible for
640// closing both of the curly braces opened by `serialize_struct_variant`.
641impl<'a> ser::SerializeStructVariant for &'a mut RequestIdSerializer {
642 type Ok = ();
643 type Error = RequestIdError;
644
645 fn serialize_field<T>(
646 &mut self,
647 _key: &'static str,
648 _value: &T,
649 ) -> Result<Self::Ok, Self::Error>
650 where
651 T: ?Sized + Serialize,
652 {
653 Err(RequestIdError::UnsupportedTypeStructVariant)
654 }
655
656 fn end(self) -> Result<Self::Ok, Self::Error> {
657 Ok(())
658 }
659}
660
661/// Derive the request ID from a serializable data structure.
662///
663/// See <https://hydra.dfinity.systems//build/268411/download/1/dfinity/spec/public/index.html#api-request-id>
664///
665/// # Warnings
666///
667/// The argument type simply needs to be serializable; the function
668/// does NOT sift between fields to include them or not and assumes
669/// the passed value only includes fields that are not part of the
670/// envelope and should be included in the calculation of the request
671/// id.
672///
673/// # Panics
674///
675/// This function panics if the value provided is not a struct or a map.
676pub fn to_request_id<'a, V>(value: &V) -> Result<RequestId, RequestIdError>
677where
678 V: 'a + Serialize,
679{
680 let mut serializer = RequestIdSerializer::new();
681 value.serialize(&mut serializer)?;
682 serializer.finish()
683}
684
685#[cfg(test)]
686mod tests {
687 use super::*;
688 use crate::export::Principal;
689 use std::convert::TryFrom;
690
691 /// The actual example used in the public spec in the Request ID section.
692 #[test]
693 fn public_spec_example() {
694 #[derive(Serialize)]
695 struct PublicSpecExampleStruct {
696 request_type: &'static str,
697 canister_id: Principal,
698 method_name: &'static str,
699 #[serde(with = "serde_bytes")]
700 arg: Vec<u8>,
701 }
702 let data = PublicSpecExampleStruct {
703 request_type: "call",
704 canister_id: Principal::try_from(&vec![0, 0, 0, 0, 0, 0, 0x04, 0xD2]).unwrap(), // 1234 in u64
705 method_name: "hello",
706 arg: b"DIDL\x00\xFD*".to_vec(),
707 };
708
709 // Hash taken from the example on the public spec.
710 let request_id = to_request_id(&data).unwrap();
711 assert_eq!(
712 hex::encode(request_id.0),
713 "8781291c347db32a9d8c10eb62b710fce5a93be676474c42babc74c51858f94b"
714 );
715 }
716
717 /// The same example as above, except we use the ApiClient enum newtypes.
718 #[test]
719 fn public_spec_example_api_client() {
720 #[derive(Serialize)]
721 #[serde(rename_all = "snake_case")]
722 #[serde(tag = "request_type")]
723 enum PublicSpec {
724 Call {
725 canister_id: Principal,
726 method_name: String,
727 #[serde(with = "serde_bytes")]
728 arg: Option<Vec<u8>>,
729 },
730 }
731 let data = PublicSpec::Call {
732 canister_id: Principal::try_from(&vec![0, 0, 0, 0, 0, 0, 0x04, 0xD2]).unwrap(), // 1234 in u64
733 method_name: "hello".to_owned(),
734 arg: Some(b"DIDL\x00\xFD*".to_vec()),
735 };
736
737 // Hash taken from the example on the public spec.
738 let request_id = to_request_id(&data).unwrap();
739 assert_eq!(
740 hex::encode(request_id.0),
741 "8781291c347db32a9d8c10eb62b710fce5a93be676474c42babc74c51858f94b"
742 );
743 }
744
745 /// A simple example with nested arrays and blobs
746 #[test]
747 #[allow(clippy::string_lit_as_bytes)]
748 fn array_example() {
749 #[derive(Serialize)]
750 struct NestedArraysExample {
751 sender: Principal,
752 paths: Vec<Vec<serde_bytes::ByteBuf>>,
753 }
754 let data = NestedArraysExample {
755 sender: Principal::try_from(&vec![0, 0, 0, 0, 0, 0, 0x04, 0xD2]).unwrap(), // 1234 in u64
756 paths: vec![
757 vec![],
758 vec![serde_bytes::ByteBuf::from("".as_bytes())],
759 vec![
760 serde_bytes::ByteBuf::from("hello".as_bytes()),
761 serde_bytes::ByteBuf::from("world".as_bytes()),
762 ],
763 ],
764 };
765
766 let request_id = to_request_id(&data).unwrap();
767 assert_eq!(
768 hex::encode(request_id.0),
769 "97d6f297aea699aec85d3377c7643ea66db810aba5c4372fbc2082c999f452dc"
770 );
771
772 /* The above was generated using ic-ref as follows:
773
774 ~/dfinity/ic-ref/impl $ cabal repl ic-ref
775 Build profile: -w ghc-8.8.4 -O1
776 …
777 *Main> :set -XOverloadedStrings
778 *Main> :m + IC.HTTP.RequestId IC.HTTP.GenR
779 *Main IC.HTTP.RequestId IC.HTTP.GenR> import qualified Data.HashMap.Lazy as HM
780 *Main IC.HTTP.RequestId IC.HTTP.GenR HM> let input = GRec (HM.fromList [("sender", GBlob "\0\0\0\0\0\0\x04\xD2"), ("paths", GList [ GList [], GList [GBlob ""], GList [GBlob "hello", GBlob "world"]])])
781 *Main IC.HTTP.RequestId IC.HTTP.GenR HM> putStrLn $ IC.Types.prettyBlob (requestId input )
782 0x97d6f297aea699aec85d3377c7643ea66db810aba5c4372fbc2082c999f452dc
783 */
784 }
785
786 /// A simple example with just an empty array
787 #[test]
788 fn array_example_empty_array() {
789 #[derive(Serialize)]
790 struct NestedArraysExample {
791 paths: Vec<Vec<serde_bytes::ByteBuf>>,
792 }
793 let data = NestedArraysExample { paths: vec![] };
794
795 let request_id = to_request_id(&data).unwrap();
796 assert_eq!(
797 hex::encode(request_id.0),
798 "99daa8c80a61e87ac1fdf9dd49e39963bfe4dafb2a45095ebf4cad72d916d5be"
799 );
800
801 /* The above was generated using ic-ref as follows:
802
803 ~/dfinity/ic-ref/impl $ cabal repl ic-ref
804 Build profile: -w ghc-8.8.4 -O1
805 …
806 *Main> :set -XOverloadedStrings
807 *Main> :m + IC.HTTP.RequestId IC.HTTP.GenR
808 *Main IC.HTTP.RequestId IC.HTTP.GenR> import qualified Data.HashMap as HM
809 *Main IC.HTTP.RequestId IC.HTTP.GenR HM> let input = GRec (HM.fromList [("paths", GList [])])
810 *Main IC.HTTP.RequestId IC.HTTP.GenR HM> putStrLn $ IC.Types.prettyBlob (requestId input )
811 0x99daa8c80a61e87ac1fdf9dd49e39963bfe4dafb2a45095ebf4cad72d916d5be
812 */
813 }
814
815 /// A simple example with an array that holds an empty array
816 #[test]
817 fn array_example_array_with_empty_array() {
818 #[derive(Serialize)]
819 struct NestedArraysExample {
820 paths: Vec<Vec<serde_bytes::ByteBuf>>,
821 }
822 let data = NestedArraysExample {
823 paths: vec![vec![]],
824 };
825
826 let request_id = to_request_id(&data).unwrap();
827 assert_eq!(
828 hex::encode(request_id.0),
829 "ea01a9c3d3830db108e0a87995ea0d4183dc9c6e51324e9818fced5c57aa64f5"
830 );
831
832 /* The above was generated using ic-ref as follows:
833
834 ~/dfinity/ic-ref/impl $ cabal repl ic-ref
835 Build profile: -w ghc-8.8.4 -O1
836 …
837 *Main> :set -XOverloadedStrings
838 *Main> :m + IC.HTTP.RequestId IC.HTTP.GenR
839 *Main IC.HTTP.RequestId IC.HTTP.GenR> import qualified Data.HashMap.Lazy as HM
840 *Main IC.HTTP.RequestId IC.HTTP.GenR HM> let input = GRec (HM.fromList [("paths", GList [ GList [] ])])
841 *Main IC.HTTP.RequestId IC.HTTP.GenR HM> putStrLn $ IC.Types.prettyBlob (requestId input )
842 0xea01a9c3d3830db108e0a87995ea0d4183dc9c6e51324e9818fced5c57aa64f5
843 */
844 }
845
846 /// We do not support creating a request id from a map.
847 /// It adds complexity, and isn't that useful anyway because a real request would
848 /// have to have different kinds of values (strings, principals, arrays) and
849 /// we don't support the wrappers that would be required to make that work
850 /// with rust maps.
851 #[test]
852 fn maps_are_not_supported() {
853 let mut data = BTreeMap::new();
854 data.insert("request_type", "call");
855 data.insert("canister_id", "a principal / the canister id");
856 data.insert("method_name", "hello");
857 data.insert("arg", "some argument value");
858
859 let error = to_request_id(&data).unwrap_err();
860 assert_eq!(error, RequestIdError::UnsupportedTypeMap);
861 }
862}