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
776777        *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
805806        *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
836837        *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}