1use super::*;
2use alloc::{collections::btree_map::BTreeMap, format};
3use keccak_const::Keccak256;
4
5type Error = String;
6
7impl ServiceUnit {
8 pub fn interface_id(&self) -> Result<InterfaceId, Error> {
18 let type_map: BTreeMap<_, _> = self.types.iter().map(|ty| (ty.name.as_str(), ty)).collect();
19
20 let mut hash = Keccak256::new();
21 for func in &self.funcs {
22 hash = hash.update(&hash_func(func, &type_map)?);
23 }
24
25 if !self.events.is_empty() {
26 let mut ev_hash = Keccak256::new();
27 for var in &self.events {
28 ev_hash = ev_hash.update(&hash_struct(&var.name, &var.def.fields, &type_map, None)?)
29 }
30 hash = hash.update(&ev_hash.finalize());
31 }
32
33 for s in &self.extends {
34 let interface_id = s
35 .interface_id
36 .ok_or_else(|| format!("service `{}` does not have an `interface_id`", s.name))?;
37 hash = hash.update(interface_id.as_bytes());
38 }
39
40 Ok(InterfaceId::from_bytes_32(hash.finalize()))
41 }
42}
43
44fn hash_func(func: &ServiceFunc, type_map: &BTreeMap<&str, &Type>) -> Result<[u8; 32], Error> {
45 let mut hash = Keccak256::new();
46 hash = match func.kind {
47 FunctionKind::Command => hash.update(b"command"),
48 FunctionKind::Query => hash.update(b"query"),
49 };
50 hash = hash.update(func.name.as_bytes());
51 for p in &func.params {
52 hash = hash.update(&hash_type_decl(&p.type_decl, type_map, None)?);
53 }
54 hash = hash.update(b"res");
55 hash = hash.update(&hash_type_decl(&func.output, type_map, None)?);
56 if let Some(th) = &func.throws {
57 hash = hash.update(b"throws");
58 hash = hash.update(&hash_type_decl(th, type_map, None)?);
59 }
60 Ok(hash.finalize())
61}
62
63fn hash_type(
64 ty: &Type,
65 type_map: &BTreeMap<&str, &Type>,
66 type_params: Option<&BTreeMap<String, TypeDecl>>,
67) -> Result<[u8; 32], Error> {
68 let bytes = match &ty.def {
69 TypeDef::Struct(StructDef { fields }) => {
70 hash_struct(&ty.name, fields, type_map, type_params)?
71 }
72 TypeDef::Enum(enum_def) => {
73 let mut hash = Keccak256::new();
74 for var in &enum_def.variants {
75 hash = hash.update(&hash_struct(
76 &var.name,
77 &var.def.fields,
78 type_map,
79 type_params,
80 )?)
81 }
82 hash.finalize()
83 }
84 TypeDef::Alias(alias_def) => hash_type_decl(&alias_def.target, type_map, type_params)?,
85 };
86 Ok(bytes)
87}
88
89fn hash_struct(
90 name: &str,
91 fields: &[StructField],
92 type_map: &BTreeMap<&str, &Type>,
93 type_params: Option<&BTreeMap<String, TypeDecl>>,
94) -> Result<[u8; 32], Error> {
95 let mut hash = Keccak256::new().update(name.as_bytes());
96 for f in fields {
97 hash = hash.update(hash_type_decl(&f.type_decl, type_map, type_params)?.as_slice())
98 }
99 Ok(hash.finalize())
100}
101
102fn hash_type_decl(
103 type_decl: &TypeDecl,
104 type_map: &BTreeMap<&str, &Type>,
105 type_params: Option<&BTreeMap<String, TypeDecl>>,
106) -> Result<[u8; 32], Error> {
107 let bytes = match type_decl {
108 TypeDecl::Slice { item } => Keccak256::new()
110 .update(b"[")
111 .update(hash_type_decl(item, type_map, type_params)?.as_slice())
112 .update(b"]")
113 .finalize(),
114 TypeDecl::Array { item, len } => Keccak256::new()
116 .update(hash_type_decl(item, type_map, type_params)?.as_slice())
117 .update(format!("{len}").as_bytes())
118 .finalize(),
119 TypeDecl::Tuple { types } => {
121 let mut hash = Keccak256::new();
122 for ty in types {
123 hash = hash.update(&hash_type_decl(ty, type_map, type_params)?);
124 }
125 hash.finalize()
126 }
127 TypeDecl::Generic { name } => {
128 let Some(param_ty) = type_params.and_then(|map| map.get(name)) else {
129 return Err(format!("generic type parameter `{name}` must be resolved"));
130 };
131 return hash_type_decl(param_ty, type_map, type_params);
132 }
133 TypeDecl::Named { name, generics } => {
134 if let Some(ty) = TypeDecl::option_type_decl(type_decl) {
136 Keccak256::new()
137 .update(b"Option")
138 .update(&hash_type_decl(&ty, type_map, type_params)?)
139 .finalize()
140 } else if let Some((ok, err)) = TypeDecl::result_type_decl(type_decl) {
141 Keccak256::new()
142 .update(b"Result")
143 .update(&hash_type_decl(&ok, type_map, type_params)?)
144 .update(&hash_type_decl(&err, type_map, type_params)?)
145 .finalize()
146 } else if let Some(ty) = type_map.get(name.as_str()) {
148 if generics.is_empty() {
149 hash_type(ty, type_map, None)?
150 } else if ty.type_params.len() == generics.len() {
151 let mut params = BTreeMap::new();
152 for (param, arg) in ty.type_params.iter().zip(generics.iter()) {
153 params.insert(param.name.clone(), arg.clone());
154 }
155 hash_type(ty, type_map, Some(¶ms))?
156 } else {
157 return Err(format!("generic params type `{name}` must be resolved"));
158 }
159 } else {
160 return Err(format!("type `{name}` not supported"));
161 }
162 }
163 TypeDecl::Primitive(primitive_type) => Keccak256::new()
165 .update(primitive_type.as_str().as_bytes())
166 .finalize(),
167 };
168 Ok(bytes)
169}
170
171#[cfg(test)]
172mod tests {
173 use super::*;
174 use PrimitiveType::*;
175 use TypeDecl::*;
176 use alloc::{boxed::Box, vec};
177 use sails_reflect_hash::ReflectHash;
178
179 macro_rules! assert_type_decl {
180 ($ty: ty, $p: expr) => {
181 assert_eq!(
182 <$ty as ReflectHash>::HASH,
183 hash_type_decl(&$p, &BTreeMap::new(), None).unwrap()
184 );
185 };
186
187 ($ty: ty, $p: expr, $map: expr) => {
188 assert_eq!(
189 <$ty as ReflectHash>::HASH,
190 hash_type_decl(&$p, &$map, None).unwrap()
191 );
192 };
193 }
194
195 #[test]
196 fn hash_primitive() {
197 assert_type_decl!((), Primitive(Void));
198 assert_type_decl!(bool, Primitive(Bool));
199 assert_type_decl!(char, Primitive(Char));
200 assert_type_decl!(str, Primitive(String));
201
202 assert_type_decl!(u8, Primitive(U8));
203 assert_type_decl!(u16, Primitive(U16));
204 assert_type_decl!(u32, Primitive(U32));
205 assert_type_decl!(u64, Primitive(U64));
206 assert_type_decl!(u128, Primitive(U128));
207
208 assert_type_decl!(i8, Primitive(I8));
209 assert_type_decl!(i16, Primitive(I16));
210 assert_type_decl!(i32, Primitive(I32));
211 assert_type_decl!(i64, Primitive(I64));
212 assert_type_decl!(i128, Primitive(I128));
213
214 assert_type_decl!(gprimitives::ActorId, Primitive(ActorId));
215 assert_type_decl!(gprimitives::CodeId, Primitive(CodeId));
216 assert_type_decl!(gprimitives::MessageId, Primitive(MessageId));
217
218 assert_type_decl!(gprimitives::H160, Primitive(H160));
219 assert_type_decl!(gprimitives::H256, Primitive(H256));
220 assert_type_decl!(gprimitives::U256, Primitive(U256));
221 }
222
223 #[test]
224 fn hash_slice() {
225 assert_type_decl!(
226 [u8],
227 Slice {
228 item: Box::new(Primitive(U8))
229 }
230 );
231 assert_type_decl!(
232 Vec<u8>,
233 Slice {
234 item: Box::new(Primitive(U8))
235 }
236 );
237 assert_type_decl!(
238 Vec<(u8, &str)>,
239 Slice {
240 item: Box::new(Tuple {
241 types: vec![Primitive(U8), Primitive(String)]
242 })
243 }
244 );
245 }
246
247 #[test]
248 fn hash_array() {
249 assert_type_decl!(
250 [u8; 32],
251 Array {
252 item: Box::new(Primitive(U8)),
253 len: 32
254 }
255 );
256 assert_type_decl!(
257 [(u8, &str); 4],
258 Array {
259 item: Box::new(Tuple {
260 types: vec![Primitive(U8), Primitive(String)]
261 }),
262 len: 4
263 }
264 );
265 }
266
267 #[test]
268 fn hash_tuple() {
269 assert_type_decl!(
270 (u8, &str),
271 Tuple {
272 types: vec![Primitive(U8), Primitive(String)]
273 }
274 );
275 assert_type_decl!(
276 (u8, &str, [u8; 32]),
277 Tuple {
278 types: vec![
279 Primitive(U8),
280 Primitive(String),
281 Array {
282 item: Box::new(Primitive(U8)),
283 len: 32
284 }
285 ]
286 }
287 );
288 }
289
290 #[test]
291 fn hash_option() {
292 assert_type_decl!(
293 Option<u8>,
294 Named {
295 name: "Option".to_string(),
296 generics: vec![Primitive(U8)]
297 }
298 );
299 assert_type_decl!(
300 Option<(u8, &str, [u8; 32])>,
301 Named {
302 name: "Option".to_string(),
303 generics: vec![Tuple {
304 types: vec![
305 Primitive(U8),
306 Primitive(String),
307 Array {
308 item: Box::new(Primitive(U8)),
309 len: 32
310 }
311 ]
312 }]
313 }
314 );
315 }
316
317 #[test]
318 fn hash_result() {
319 assert_type_decl!(
320 Result<u8, &str>,
321 Named {
322 name: "Result".to_string(),
323 generics: vec![Primitive(U8), Primitive(String)]
324 }
325 );
326 assert_type_decl!(
327 Result<(u8, &str, [u8; 32]), ()>,
328 Named {
329 name: "Result".to_string(),
330 generics: vec![Tuple {
331 types: vec![
332 Primitive(U8),
333 Primitive(String),
334 Array {
335 item: Box::new(Primitive(U8)),
336 len: 32
337 }
338 ]
339 }, Primitive(Void)]
340 }
341 );
342 }
343
344 #[test]
345 fn hash_struct_unit() {
346 #[derive(ReflectHash)]
347 struct UnitStruct;
348
349 let mut map = BTreeMap::new();
350 let ty = Type {
351 name: "UnitStruct".to_string(),
352 type_params: vec![],
353 def: TypeDef::Struct(StructDef { fields: vec![] }),
354 docs: vec![],
355 annotations: vec![],
356 };
357 map.insert("UnitStruct", &ty);
358
359 assert_type_decl!(
360 UnitStruct,
361 Named {
362 name: "UnitStruct".to_string(),
363 generics: vec![]
364 },
365 map
366 );
367 }
368
369 #[test]
370 fn hash_struct_tuple() {
371 #[derive(ReflectHash)]
372 #[allow(unused)]
373 struct TupleStruct(u32);
374
375 let mut map = BTreeMap::new();
376 let ty = Type {
377 name: "TupleStruct".to_string(),
378 type_params: vec![],
379 def: TypeDef::Struct(StructDef {
380 fields: vec![StructField {
381 name: None,
382 type_decl: Primitive(U32),
383 docs: vec![],
384 annotations: vec![],
385 }],
386 }),
387 docs: vec![],
388 annotations: vec![],
389 };
390 map.insert("TupleStruct", &ty);
391
392 assert_type_decl!(
393 TupleStruct,
394 Named {
395 name: "TupleStruct".to_string(),
396 generics: vec![]
397 },
398 map
399 );
400 }
401
402 #[test]
403 fn hash_struct_named() {
404 #[derive(ReflectHash)]
405 #[allow(unused)]
406 struct NamedStruct {
407 f1: u32,
408 f2: Option<&'static str>,
409 }
410
411 let mut map = BTreeMap::new();
412 let ty = Type {
413 name: "NamedStruct".to_string(),
414 type_params: vec![],
415 def: TypeDef::Struct(StructDef {
416 fields: vec![
417 StructField {
418 name: Some("f1".to_string()),
419 type_decl: Primitive(U32),
420 docs: vec![],
421 annotations: vec![],
422 },
423 StructField {
424 name: Some("f2".to_string()),
425 type_decl: Named {
426 name: "Option".to_string(),
427 generics: vec![Primitive(String)],
428 },
429 docs: vec![],
430 annotations: vec![],
431 },
432 ],
433 }),
434 docs: vec![],
435 annotations: vec![],
436 };
437 map.insert("NamedStruct", &ty);
438
439 assert_type_decl!(
440 NamedStruct,
441 Named {
442 name: "NamedStruct".to_string(),
443 generics: vec![]
444 },
445 map
446 );
447 }
448
449 #[test]
450 fn hash_struct_generics() {
451 #[derive(ReflectHash)]
452 #[allow(unused)]
453 struct GenericStruct<T1: ReflectHash, T2: ReflectHash> {
454 f1: T1,
455 f2: Option<T2>,
456 }
457
458 let mut map = BTreeMap::new();
459 let ty = Type {
460 name: "GenericStruct".to_string(),
461 type_params: vec![
462 TypeParameter {
463 name: "T1".to_string(),
464 ty: None,
465 },
466 TypeParameter {
467 name: "T2".to_string(),
468 ty: None,
469 },
470 ],
471 def: TypeDef::Struct(StructDef {
472 fields: vec![
473 StructField {
474 name: Some("f1".to_string()),
475 type_decl: Generic {
476 name: "T1".to_string(),
477 },
478 docs: vec![],
479 annotations: vec![],
480 },
481 StructField {
482 name: Some("f2".to_string()),
483 type_decl: Named {
484 name: "Option".to_string(),
485 generics: vec![Generic {
486 name: "T2".to_string(),
487 }],
488 },
489 docs: vec![],
490 annotations: vec![],
491 },
492 ],
493 }),
494 docs: vec![],
495 annotations: vec![],
496 };
497 map.insert("GenericStruct", &ty);
498
499 let ty_u8_str = Named {
500 name: "GenericStruct".to_string(),
501 generics: vec![Primitive(U8), Primitive(String)],
502 };
503 let ty_str_u8 = Named {
504 name: "GenericStruct".to_string(),
505 generics: vec![Primitive(String), Primitive(U8)],
506 };
507
508 assert_ne!(
509 hash_type_decl(&ty_u8_str, &map, None),
510 hash_type_decl(&ty_str_u8, &map, None)
511 );
512
513 assert_type_decl!(
514 GenericStruct<u8, &str>,
515 Named {
516 name: "GenericStruct".to_string(),
517 generics: vec![Primitive(U8), Primitive(String)],
518 },
519 map
520 );
521
522 assert_type_decl!(
523 GenericStruct<&str, u8>,
524 Named {
525 name: "GenericStruct".to_string(),
526 generics: vec![Primitive(String), Primitive(U8)],
527 },
528 map
529 );
530 }
531
532 #[test]
533 fn hash_unresolved_generic_errors() {
534 let err = hash_type_decl(
535 &Generic {
536 name: "T".to_string(),
537 },
538 &BTreeMap::new(),
539 None,
540 )
541 .expect_err("unresolved Generic must not hash as a named type");
542
543 assert_eq!(err, "generic type parameter `T` must be resolved");
544 }
545
546 #[test]
547 fn hash_alias_is_same_as_target() {
548 let mut map = BTreeMap::new();
549 let target = TypeDecl::Primitive(PrimitiveType::U32);
550 let alias = Type {
551 name: "MyAlias".to_string(),
552 type_params: vec![],
553 def: TypeDef::Alias(AliasDef {
554 target: target.clone(),
555 }),
556 docs: vec![],
557 annotations: vec![],
558 };
559 map.insert("MyAlias", &alias);
560
561 let struct_hash = hash_type_decl(&target, &map, None).unwrap();
562 let alias_decl = TypeDecl::Named {
563 name: "MyAlias".to_string(),
564 generics: vec![],
565 };
566 let alias_hash = hash_type_decl(&alias_decl, &map, None).unwrap();
567
568 assert_eq!(struct_hash, alias_hash);
569 }
570}