kythera_common/abi/
mod.rs

1// Copyright 2023 Polyphene.
2// SPDX-License-Identifier: Apache-2.0, MIT
3
4use std::borrow::Borrow;
5use std::fmt;
6
7use anyhow::Result;
8use frc42_dispatch::hash::MethodResolver;
9use serde::de::SeqAccess;
10
11use crate::abi::blake2b::Blake2bHasher;
12use crate::error::{self, Error};
13
14mod blake2b;
15
16/// Split a PascalCase string into a vector of its components.
17/// If the string is not PascalCase function returns an empty [`Vec`].
18pub fn pascal_case_split(s: &str) -> Vec<&str> {
19    let mut split = vec![];
20    // Work with indices to avoid allocations.
21    let mut chars = s.char_indices();
22
23    // Check if first character is capitalized.
24    let mut beg = match chars.next() {
25        Some((i, c)) if c.is_uppercase() => i,
26        _ => return split,
27    };
28
29    // Iterate the rest of the characters.
30    for (i, c) in chars {
31        if c.is_uppercase() || c.is_numeric() {
32            split.push(&s[beg..i]);
33            beg = i;
34        }
35    }
36
37    // Push the last word, this word is not covered by the iterator
38    // as it doesn't know when it's last element.
39    split.push(&s[beg..s.len()]);
40    split
41}
42
43/// `Abi` is the structure we use internally to deal with Actor Binary Interface. It contains all
44/// exposed [`Method`] from a given Actor.
45#[derive(Clone, Default, Debug, PartialEq, Eq, PartialOrd, Ord)]
46pub struct Abi {
47    pub constructor: Option<Method>,
48    pub set_up: Option<Method>,
49    pub methods: Vec<Method>,
50}
51
52impl Abi {
53    /// Get the `Constructor` method on the Abi if it exists.
54    pub fn constructor(&self) -> Option<&Method> {
55        self.constructor.as_ref()
56    }
57
58    /// Get the `Setup` method on the Abi if it exists.
59    pub fn set_up(&self) -> Option<&Method> {
60        self.set_up.as_ref()
61    }
62
63    pub fn methods(&self) -> &[Method] {
64        &self.methods
65    }
66}
67
68/// Custom implementation of [`Serialize`] so that we join `Constructor` and `Setup`
69/// into the rest of the methods.
70impl serde::Serialize for Abi {
71    fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
72    where
73        S: serde::Serializer,
74    {
75        let mut s = vec![];
76        if let Some(constructor) = &self.constructor {
77            s.push(vec![constructor.name()]);
78        }
79        if let Some(constructor) = &self.set_up {
80            s.push(vec![constructor.name()]);
81        }
82        let methods = self.methods.iter().map(|m| vec![m.name()]);
83        s.extend(methods);
84
85        serde::Serialize::serialize(&vec![s], serializer)
86    }
87}
88
89/// Custom implementation of [`Deserialize`] so that we check for `Constructor` and `Setup`
90/// existence and assert there's only one of each.
91impl<'de> serde::Deserialize<'de> for Abi {
92    fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
93    where
94        D: serde::Deserializer<'de>,
95    {
96        struct AbiVisitor;
97
98        impl<'de> serde::de::Visitor<'de> for AbiVisitor {
99            type Value = Abi;
100
101            fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
102                formatter.write_str("Abi")
103            }
104
105            fn visit_seq<A>(self, mut seq: A) -> std::result::Result<Self::Value, A::Error>
106            where
107                A: SeqAccess<'de>,
108            {
109                let mut constructor = None;
110                let mut set_up = None;
111
112                let mut methods = vec![];
113                let seq_methods = seq
114                    .next_element::<Vec<Method>>()?
115                    .ok_or_else(|| serde::de::Error::invalid_length(0, &self))?;
116
117                // TODO: Can't we parse each method sequentially instead? and not have to
118                // iterate again all over here?
119                for method in seq_methods {
120                    match (constructor.is_some(), set_up.is_some(), &method.r#type()) {
121                        (false, _, MethodType::Constructor) => constructor = Some(method),
122                        (_, false, MethodType::Setup) => set_up = Some(method),
123                        (true, _, MethodType::Constructor) | (_, true, MethodType::Setup) => {
124                            return Err(serde::de::Error::custom(
125                                "Abi can only have one Constructor and one SetUp function",
126                            ))
127                        }
128                        (_, _, _) => methods.push(method),
129                    }
130                }
131
132                Ok(Abi {
133                    constructor,
134                    set_up,
135                    methods,
136                })
137            }
138        }
139        deserializer.deserialize_seq(AbiVisitor)
140    }
141}
142
143/// Method number indicator for calling Actor methods.
144pub type MethodNum = u64;
145
146/// [`Method`] describes an exposed method from an actor entrypoint.
147#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
148pub struct Method {
149    number: MethodNum,
150    name: String,
151    r#type: MethodType,
152}
153
154impl fmt::Display for Method {
155    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
156        write!(f, "{}", self.name())
157    }
158}
159
160impl Borrow<u64> for Method {
161    fn borrow(&self) -> &u64 {
162        &self.number
163    }
164}
165
166/// Type of the [`Abi`] [`Method`] of the test Actor.
167#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
168pub enum MethodType {
169    Constructor,
170    Entrypoint,
171    Setup,
172    Test,
173    TestFail,
174}
175
176impl Method {
177    /// Get the [`Method`] number.
178    pub fn number(&self) -> MethodNum {
179        self.number
180    }
181
182    /// Get the [`Method`] number.
183    pub fn name(&self) -> &str {
184        &self.name
185    }
186
187    /// Get the [`Method`] type.
188    pub fn r#type(&self) -> MethodType {
189        self.r#type
190    }
191}
192
193impl Method {
194    pub fn new_from_name(name: &str) -> Result<Self, Error> {
195        let number = derive_method_num(name)?;
196        let name = name.to_string();
197
198        let split = pascal_case_split(&name);
199        let r#type = match &split[..] {
200            ["Constructor", ..] => MethodType::Constructor,
201            ["Setup", ..] => MethodType::Setup,
202            ["Test", "Fail", ..] => MethodType::TestFail,
203            ["Test", ..] => MethodType::Test,
204            _ => MethodType::Entrypoint,
205        };
206
207        Ok(Method {
208            number,
209            name,
210            r#type,
211        })
212    }
213}
214
215/// Implement custom deserialization method for [`Method`] as we expect the bytes to be deserialized to only contain
216/// the `name` and not the `number` property that is generated at deserialization time.
217impl<'de> serde::de::Deserialize<'de> for Method {
218    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
219    where
220        D: serde::de::Deserializer<'de>,
221    {
222        struct MethodVisitor;
223
224        impl<'de> serde::de::Visitor<'de> for MethodVisitor {
225            type Value = Method;
226
227            fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
228                formatter.write_str("Method")
229            }
230
231            fn visit_seq<A>(self, mut seq: A) -> std::result::Result<Self::Value, A::Error>
232            where
233                A: SeqAccess<'de>,
234            {
235                let name = seq
236                    .next_element::<String>()?
237                    .ok_or_else(|| serde::de::Error::invalid_length(0, &self))?;
238
239                Self::Value::new_from_name(&name).map_err(|_| {
240                    serde::de::Error::custom(format!("Couldn't deserialize method: {}", &name))
241                })
242            }
243        }
244
245        deserializer.deserialize_seq(MethodVisitor)
246    }
247}
248
249/// `derive_method_num` will return the method number for a given method name based on the FRC-042:
250/// https://github.com/filecoin-project/FIPs/blob/master/FRCs/frc-0042.md .
251pub fn derive_method_num(name: &str) -> Result<MethodNum, error::Error> {
252    let resolver = MethodResolver::new(Blake2bHasher {});
253
254    match resolver.method_number(name) {
255        Ok(method_number) => Ok(method_number),
256        Err(err) => Err(Error::MethodNumberGeneration {
257            name: name.into(),
258            source: err.into(),
259        }),
260    }
261}
262
263#[cfg(test)]
264mod test {
265    use super::{derive_method_num, pascal_case_split};
266    use crate::abi::{Abi, Method, MethodType};
267
268    #[test]
269    fn test_method_derivation() {
270        let method_name = String::from("TestTransfer");
271
272        match derive_method_num(&method_name) {
273            Ok(method_num) => {
274                assert_eq!(method_num, 3760293944);
275            }
276            Err(_) => {
277                panic!("derive_method_num failed for {}", method_name);
278            }
279        }
280    }
281
282    #[test]
283    fn test_fail_method_derivation() {
284        // Using function with lower case as first character in method name to fail the test.
285        let method_name = String::from("test_transfer");
286
287        match derive_method_num(&method_name) {
288            Ok(_) => {
289                panic!("derive_method_num success for {}", method_name);
290            }
291            Err(err) => {
292                assert_eq!(
293                    format!("Could not generate method number for `{}`", method_name),
294                    err.to_string()
295                )
296            }
297        }
298    }
299
300    #[test]
301    fn test_pascal_case() {
302        assert_eq!(pascal_case_split("TestOne"), vec!["Test", "One"]);
303        assert_eq!(
304            pascal_case_split("TestFailWithMultipleWords"),
305            vec!["Test", "Fail", "With", "Multiple", "Words"]
306        );
307        assert_eq!(pascal_case_split("Test1"), vec!["Test", "1"]);
308        assert_eq!(pascal_case_split("testOne"), Vec::<&str>::new());
309    }
310
311    #[test]
312    fn test_tuple_serde() {
313        // Test assets.
314        let test_transfer_name = String::from("TestTransfer");
315        let test_transfer_fail_name = String::from("TestFailTransfer");
316
317        let abi = Abi {
318            constructor: None,
319            set_up: None,
320            methods: vec![
321                Method {
322                    number: derive_method_num(&test_transfer_name).unwrap(),
323                    name: test_transfer_name,
324                    r#type: MethodType::Test,
325                },
326                Method {
327                    number: derive_method_num(&test_transfer_fail_name).unwrap(),
328                    name: test_transfer_fail_name,
329                    r#type: MethodType::TestFail,
330                },
331            ],
332        };
333
334        let serialized_abi: Vec<u8> = vec![
335            129, 130, 129, 108, 84, 101, 115, 116, 84, 114, 97, 110, 115, 102, 101, 114, 129, 112,
336            84, 101, 115, 116, 70, 97, 105, 108, 84, 114, 97, 110, 115, 102, 101, 114,
337        ];
338
339        // Serialize.
340        let abi_vec = crate::to_vec(&abi).unwrap();
341        assert_eq!(abi_vec, serialized_abi);
342
343        // Deserialize.
344        let deserialized_abi: Abi = crate::from_slice(&serialized_abi).unwrap();
345        assert_eq!(deserialized_abi, abi);
346    }
347
348    #[test]
349    fn test_fail_tuple_serde() {
350        // Test assets.
351        let test_transfer_name = String::from("TestTransfer");
352        let test_transfer_fail_name = String::from("testFailTransfer");
353
354        let abi = Abi {
355            constructor: None,
356            set_up: None,
357            methods: vec![
358                Method {
359                    number: derive_method_num(&test_transfer_name).unwrap(),
360                    name: test_transfer_name,
361                    r#type: MethodType::Test,
362                },
363                Method {
364                    number: 3280706483,
365                    name: test_transfer_fail_name,
366                    r#type: MethodType::TestFail,
367                },
368            ],
369        };
370
371        let serialized_abi: Vec<u8> = vec![
372            129, 130, 129, 108, 84, 101, 115, 116, 84, 114, 97, 110, 115, 102, 101, 114, 129, 112,
373            116, 101, 115, 116, 70, 97, 105, 108, 84, 114, 97, 110, 115, 102, 101, 114,
374        ];
375
376        // Serialize.
377        let abi_vec = crate::to_vec(&abi).unwrap();
378        assert_eq!(abi_vec, serialized_abi);
379
380        // Deserialize.
381        match crate::from_slice::<Abi>(&serialized_abi) {
382            Ok(_) => panic!("Deserialization should fail"),
383            Err(err) => {
384                assert!(err
385                    .to_string()
386                    .contains("Couldn't deserialize method: testFailTransfer"));
387            }
388        };
389    }
390
391    #[test]
392    fn test_method_constructor() {
393        assert_eq!(
394            Method::new_from_name("TestOne").unwrap().r#type,
395            MethodType::Test
396        );
397        assert_eq!(
398            Method::new_from_name("TestFailOne").unwrap().r#type,
399            MethodType::TestFail
400        );
401        assert_eq!(
402            Method::new_from_name("Constructor").unwrap().r#type,
403            MethodType::Constructor
404        );
405        assert_eq!(
406            Method::new_from_name("Setup").unwrap().r#type,
407            MethodType::Setup
408        );
409
410        assert!(Method::new_from_name("testOne").is_err());
411        assert!(Method::new_from_name("").is_err());
412    }
413}