cat_gateway/service/common/objects/cardano/
hash.rs

1//! Defines API schema of Cardano hash type.
2
3use core::fmt;
4use std::str::FromStr;
5
6use poem_openapi::{
7    registry::{MetaSchema, MetaSchemaRef},
8    types::{Example, ParseError, ParseFromJSON, ParseFromParameter, ParseResult, ToJSON, Type},
9};
10
11use crate::service::utilities::as_hex_string;
12
13/// Cardano Blake2b256 hash encoded in hex.
14#[derive(Debug, Clone)]
15pub(crate) struct Hash256([u8; Hash256::BYTE_LEN]);
16
17impl Hash256 {
18    /// The byte size for this hash.
19    const BYTE_LEN: usize = 32;
20    /// The hex-encoded hash length of this hash type.
21    const HASH_LEN: usize = Self::BYTE_LEN * 2;
22
23    /// Creates a `Hash256` schema definition.
24    fn schema() -> MetaSchema {
25        let mut schema = MetaSchema::new("string");
26        schema.title = Some(format!("Cardano {}-bit Hash", Self::BYTE_LEN * 8));
27        schema.description = Some("Cardano Blake2b256 hash encoded in hex.");
28        schema.example = Some(Self::example().to_string().into());
29        schema.min_length = Some(Self::HASH_LEN + 2);
30        schema.max_length = Some(Self::HASH_LEN + 2);
31        schema.pattern = Some("^0x[0-9a-f]{64}$".to_string());
32        schema
33    }
34}
35
36impl Type for Hash256 {
37    type RawElementValueType = Self;
38    type RawValueType = Self;
39
40    const IS_REQUIRED: bool = true;
41
42    fn name() -> std::borrow::Cow<'static, str> {
43        "CardanoHash256".into()
44    }
45
46    fn schema_ref() -> MetaSchemaRef {
47        MetaSchemaRef::Inline(Box::new(Self::schema()))
48    }
49
50    fn as_raw_value(&self) -> Option<&Self::RawValueType> {
51        Some(self)
52    }
53
54    fn raw_element_iter<'a>(
55        &'a self
56    ) -> Box<dyn Iterator<Item = &'a Self::RawElementValueType> + 'a> {
57        Box::new(self.as_raw_value().into_iter())
58    }
59}
60
61impl Example for Hash256 {
62    fn example() -> Self {
63        // 0xff561c1ce6becf136a5d3063f50d78b8db50b8a1d4c03b18d41a8e98a6a18aed
64        Self([
65            255, 86, 28, 28, 230, 190, 207, 19, 106, 93, 48, 99, 245, 13, 120, 184, 219, 80, 184,
66            161, 212, 192, 59, 24, 212, 26, 142, 152, 166, 161, 138, 237,
67        ])
68    }
69}
70
71impl TryFrom<Vec<u8>> for Hash256 {
72    type Error = anyhow::Error;
73
74    fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
75        value
76            .try_into()
77            .map_err(|_| anyhow::anyhow!("Invalid {}-bit Cardano hash length.", Self::BYTE_LEN * 8))
78            .map(Self)
79    }
80}
81
82impl FromStr for Hash256 {
83    type Err = anyhow::Error;
84
85    fn from_str(s: &str) -> Result<Self, Self::Err> {
86        let hash = s.strip_prefix("0x").ok_or(anyhow::anyhow!(
87            "Invalid Cardano hash. Hex string must start with `0x`.",
88        ))?;
89
90        hex::decode(hash)
91            .map_err(|_| anyhow::anyhow!("Invalid Cardano hash. Must be hex string."))?
92            .try_into()
93    }
94}
95
96impl AsRef<[u8]> for Hash256 {
97    fn as_ref(&self) -> &[u8] {
98        &self.0
99    }
100}
101
102impl ParseFromParameter for Hash256 {
103    fn parse_from_parameter(param: &str) -> ParseResult<Self> {
104        Self::from_str(param).map_err(ParseError::custom)
105    }
106}
107
108impl ParseFromJSON for Hash256 {
109    fn parse_from_json(value: Option<serde_json::Value>) -> ParseResult<Self> {
110        String::parse_from_json(value)
111            .map_err(ParseError::propagate)
112            .map(|v| Self::from_str(&v))?
113            .map_err(ParseError::custom)
114    }
115}
116
117impl ToJSON for Hash256 {
118    fn to_json(&self) -> Option<serde_json::Value> {
119        Some(self.to_string().into())
120    }
121}
122
123impl fmt::Display for Hash256 {
124    fn fmt(
125        &self,
126        f: &mut fmt::Formatter<'_>,
127    ) -> fmt::Result {
128        as_hex_string(&self.0).fmt(f)
129    }
130}
131
132/// Cardano Blake2b128 hash encoded in hex.
133#[derive(Debug, Clone)]
134pub(crate) struct Hash128([u8; Hash128::BYTE_LEN]);
135
136impl Hash128 {
137    /// The byte size for this hash.
138    const BYTE_LEN: usize = 16;
139    /// The hex-encoded hash length of this hash type.
140    const HASH_LEN: usize = Self::BYTE_LEN * 2;
141
142    /// Creates a `Hash128` schema definition.
143    fn schema() -> MetaSchema {
144        let mut schema = MetaSchema::new("string");
145        schema.title = Some(format!("Cardano {}-bit Hash", Self::BYTE_LEN * 8));
146        schema.description = Some("Cardano Blake2b128 hash encoded in hex.");
147        schema.example = Some(Self::example().to_string().into());
148        schema.min_length = Some(Self::HASH_LEN + 2);
149        schema.max_length = Some(Self::HASH_LEN + 2);
150        schema.pattern = Some("^0x[0-9a-f]{32}$".to_string());
151        schema
152    }
153}
154
155impl Type for Hash128 {
156    type RawElementValueType = Self;
157    type RawValueType = Self;
158
159    const IS_REQUIRED: bool = true;
160
161    fn name() -> std::borrow::Cow<'static, str> {
162        "CardanoHash128".into()
163    }
164
165    fn schema_ref() -> MetaSchemaRef {
166        MetaSchemaRef::Inline(Box::new(Self::schema()))
167    }
168
169    fn as_raw_value(&self) -> Option<&Self::RawValueType> {
170        Some(self)
171    }
172
173    fn raw_element_iter<'a>(
174        &'a self
175    ) -> Box<dyn Iterator<Item = &'a Self::RawElementValueType> + 'a> {
176        Box::new(self.as_raw_value().into_iter())
177    }
178}
179
180impl Example for Hash128 {
181    fn example() -> Self {
182        // 0xdb50b8a1d4c03b18d41a8e98a6a18aed
183        Self([
184            219, 80, 184, 161, 212, 192, 59, 24, 212, 26, 142, 152, 166, 161, 138, 237,
185        ])
186    }
187}
188
189impl TryFrom<Vec<u8>> for Hash128 {
190    type Error = anyhow::Error;
191
192    fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
193        value
194            .try_into()
195            .map_err(|_| anyhow::anyhow!("Invalid {}-bit Cardano hash length.", Self::BYTE_LEN * 8))
196            .map(Self)
197    }
198}
199
200impl FromStr for Hash128 {
201    type Err = anyhow::Error;
202
203    fn from_str(s: &str) -> Result<Self, Self::Err> {
204        let hash = s.strip_prefix("0x").ok_or(anyhow::anyhow!(
205            "Invalid Cardano hash. Hex string must start with `0x`.",
206        ))?;
207
208        hex::decode(hash)
209            .map_err(|_| anyhow::anyhow!("Invalid Cardano hash. Must be hex string."))?
210            .try_into()
211    }
212}
213
214impl AsRef<[u8]> for Hash128 {
215    fn as_ref(&self) -> &[u8] {
216        &self.0
217    }
218}
219
220impl ParseFromParameter for Hash128 {
221    fn parse_from_parameter(param: &str) -> ParseResult<Self> {
222        Self::from_str(param).map_err(ParseError::custom)
223    }
224}
225
226impl ParseFromJSON for Hash128 {
227    fn parse_from_json(value: Option<serde_json::Value>) -> ParseResult<Self> {
228        String::parse_from_json(value)
229            .map_err(ParseError::propagate)
230            .map(|v| Self::from_str(&v))?
231            .map_err(ParseError::custom)
232    }
233}
234
235impl ToJSON for Hash128 {
236    fn to_json(&self) -> Option<serde_json::Value> {
237        Some(self.to_string().into())
238    }
239}
240
241impl fmt::Display for Hash128 {
242    fn fmt(
243        &self,
244        f: &mut fmt::Formatter<'_>,
245    ) -> fmt::Result {
246        as_hex_string(&self.0).fmt(f)
247    }
248}