zilliqa_rs/core/
mod.rs

1//! Shared data types and functionalities.
2
3#[doc(hidden)]
4pub mod proto;
5pub mod types;
6pub mod units;
7
8use bech32::{FromBase32, ToBase32, Variant};
9use primitive_types::H160;
10pub use types::*;
11pub use units::*;
12
13use std::{
14    fmt::Display,
15    ops::{BitAnd, Deref},
16    str::FromStr,
17};
18
19use serde::de::Error as SerdeError;
20use serde::{Deserialize, Deserializer, Serialize};
21use sha2::Digest;
22
23use crate::Error;
24
25/// Checks if a given string is a valid byte string of a specified length.
26fn is_byte_string(str: &str, len: usize) -> bool {
27    let regex = regex::Regex::new(&format!("^[0-9a-fA-F]{{{}}}$", len)).expect("Failed to create the regex for `is_byte_string`");
28    let str = str.replace("0x", "");
29    regex.is_match(&str)
30}
31
32/// A Type-safe Transaction hash.
33#[derive(Debug, Clone, Deserialize)]
34pub struct TxHash(String);
35
36impl TxHash {
37    /// Checks if a given transaction hash is valid.
38    ///
39    /// Example
40    /// ```
41    /// use zilliqa_rs::core::TxHash;
42    /// assert!(TxHash::is_valid("bdadfd994f452df803cc223d1f417b02830ac96dbe5edad1b9f8d58613f95206"));
43    /// ```
44    pub fn is_valid(tx_hash: &str) -> bool {
45        is_byte_string(tx_hash, 64)
46    }
47}
48
49impl FromStr for TxHash {
50    type Err = Error;
51    /// Parses a given string slice to Transaction hash.
52    ///
53    /// # Example
54    /// ```
55    /// use zilliqa_rs::core::TxHash;
56    /// let hash: TxHash = "bdadfd994f452df803cc223d1f417b02830ac96dbe5edad1b9f8d58613f95206".parse().unwrap();
57    /// ```
58    fn from_str(tx_hash: &str) -> Result<Self, Self::Err> {
59        if TxHash::is_valid(tx_hash) {
60            Ok(Self(tx_hash.to_string()))
61        } else {
62            Err(Error::InvalidTransactionHash(tx_hash.to_string()))
63        }
64    }
65}
66
67impl Display for TxHash {
68    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
69        write!(f, "{}", self.0)
70    }
71}
72
73/// Rust corresponding type for scilla BNum.
74#[derive(Serialize, Debug, Clone, Deserialize, PartialEq)]
75pub struct BNum(String);
76impl BNum {
77    pub fn new(bnum: &str) -> Self {
78        Self(bnum.to_string())
79    }
80}
81
82impl Deref for BNum {
83    type Target = String;
84
85    fn deref(&self) -> &Self::Target {
86        &self.0
87    }
88}
89
90impl FromStr for BNum {
91    type Err = Error;
92
93    // TODO: Make to more strict.
94    fn from_str(s: &str) -> Result<Self, Self::Err> {
95        Ok(Self::new(s))
96    }
97}
98
99#[derive(Debug, Clone)]
100/// secp256k1 (K-256) secret key.
101pub struct PrivateKey(k256::SecretKey);
102
103impl PrivateKey {
104    /// Generates a random private key.
105    ///
106    /// # Example
107    /// ```
108    /// use zilliqa_rs::core::PrivateKey;
109    /// let private_key = PrivateKey::create_random();
110    /// ```
111    pub fn create_random() -> Self {
112        Self(k256::SecretKey::random(&mut rand::thread_rng()))
113    }
114
115    /// Constructs a private key from a raw secret key.
116    pub fn from_slice(slice: &[u8]) -> Result<Self, Error> {
117        Ok(Self(k256::SecretKey::from_slice(slice)?))
118    }
119
120    /// Returns corresponding public key of the private key
121    pub fn public_key(&self) -> PublicKey {
122        PublicKey::new(self.0.public_key())
123    }
124}
125
126impl FromStr for PrivateKey {
127    type Err = Error;
128
129    /// Create a private key out of a sting slice.
130    ///
131    /// # Example
132    /// ```
133    /// use zilliqa_rs::core::PrivateKey;
134    ///let pv: PrivateKey = "D96e9eb5b782a80ea153c937fa83e5948485fbfc8b7e7c069d7b914dbc350aba"
135    ///    .parse()
136    ///    .unwrap();
137    ///assert_eq!(
138    ///    "d96e9eb5b782a80ea153c937fa83e5948485fbfc8b7e7c069d7b914dbc350aba",
139    ///    pv.to_string()
140    ///);
141    ///```
142    fn from_str(secret_key: &str) -> Result<Self, Self::Err> {
143        let secret_key = match secret_key.strip_prefix("0x") {
144            Some(secret_key) => secret_key,
145            None => secret_key,
146        };
147        Ok(Self(k256::SecretKey::from_slice(&hex::decode(secret_key)?)?))
148    }
149}
150
151impl Deref for PrivateKey {
152    type Target = k256::SecretKey;
153
154    fn deref(&self) -> &Self::Target {
155        &self.0
156    }
157}
158
159impl Display for PrivateKey {
160    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
161        write!(f, "{}", hex::encode(self.to_bytes()).to_lowercase())
162    }
163}
164
165impl PartialEq for PrivateKey {
166    // TODO: Make it efficient
167    fn eq(&self, other: &Self) -> bool {
168        self.to_string() == other.to_string()
169    }
170}
171
172/// secp256k1 (K-256) public key.
173#[derive(Debug, Clone)]
174pub struct PublicKey(k256::PublicKey);
175
176impl PublicKey {
177    /// Creates a new public key.
178    pub fn new(pk: k256::PublicKey) -> Self {
179        Self(pk)
180    }
181}
182
183impl FromStr for PublicKey {
184    type Err = Error;
185
186    /// Parse a string into a public key
187    ///
188    /// # Example
189    ///  ```
190    /// use zilliqa_rs::core::PublicKey;
191    /// let public_key: PublicKey = "03bfad0f0b53cff5213b5947f3ddd66acee8906aba3610c111915aecc84092e052"
192    ///     .parse()
193    ///     .unwrap();
194    /// assert_eq!(
195    ///     public_key.to_string(),
196    ///     "03bfad0f0b53cff5213b5947f3ddd66acee8906aba3610c111915aecc84092e052"
197    /// );
198    /// ```
199    fn from_str(public_key: &str) -> Result<Self, Self::Err> {
200        let public_key = match public_key.strip_prefix("0x") {
201            Some(public_key) => public_key,
202            None => public_key,
203        };
204
205        Ok(Self(k256::PublicKey::from_sec1_bytes(&hex::decode(public_key)?)?))
206    }
207}
208
209impl PartialEq for PublicKey {
210    // TODO: Make it efficient
211    fn eq(&self, other: &Self) -> bool {
212        self.to_string() == other.to_string()
213    }
214}
215
216impl Deref for PublicKey {
217    type Target = k256::PublicKey;
218
219    fn deref(&self) -> &Self::Target {
220        &self.0
221    }
222}
223
224impl Display for PublicKey {
225    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
226        write!(f, "{}", hex::encode(self.to_sec1_bytes()).to_lowercase())
227    }
228}
229
230/// Type-safe address fo zilliqa network.
231#[derive(Eq, Hash, Debug, PartialEq, Clone, serde::Serialize, Default)]
232pub struct ZilAddress(String);
233
234impl Deref for ZilAddress {
235    type Target = String;
236
237    fn deref(&self) -> &Self::Target {
238        &self.0
239    }
240}
241
242impl TryFrom<&PublicKey> for ZilAddress {
243    type Error = Error;
244
245    /// Convert a public key into a ZilAddress
246    fn try_from(value: &PublicKey) -> Result<Self, Self::Error> {
247        let mut hasher = sha2::Sha256::new();
248        hasher.update(value.to_sec1_bytes());
249        Ok(Self(ZilAddress::to_checksum_address(&hex::encode(hasher.finalize())[24..])?))
250    }
251}
252
253impl ZilAddress {
254    fn from_bech32(address: &str) -> Result<Self, Error> {
255        let (_hrp, data, _) = bech32::decode(address)?;
256
257        let address = hex::encode(Vec::<u8>::from_base32(&data)?);
258
259        Ok(Self(ZilAddress::to_checksum_address(&address)?))
260    }
261
262    fn to_checksum_address(address: &str) -> Result<String, Error> {
263        let address = address.replace("0x", "");
264        if !Self::is_address(&address) {
265            return Err(Error::InvalidAddress(address.to_string()));
266        }
267
268        let mut hasher = sha2::Sha256::new();
269        hasher.update(hex::decode(&address)?);
270        let v = primitive_types::U256::from_big_endian(&hasher.finalize());
271        let ret = address
272            .chars()
273            .enumerate()
274            .map(|(i, c)| {
275                if c.is_ascii_digit() {
276                    c
277                } else {
278                    let cond = v
279                        .bitand(primitive_types::U256::from(2).pow(primitive_types::U256::from(255 - 6 * i)))
280                        .ge(&primitive_types::U256::one());
281                    if cond {
282                        c.to_ascii_uppercase()
283                    } else {
284                        c.to_ascii_lowercase()
285                    }
286                }
287            })
288            .collect::<String>();
289
290        Ok(format!("0x{}", ret))
291    }
292
293    pub fn to_bech32(&self) -> Result<String, Error> {
294        let address = self.0.strip_prefix("0x").unwrap(); // Safe to call unwrap, we create addresses with 0x prefixed
295
296        Ok(bech32::encode("zil", hex::decode(address)?.to_base32(), Variant::Bech32)?)
297    }
298
299    /// Create an empty ZilAddress, mainly to deploy a new contract.
300    pub fn nil() -> Self {
301        Self("0x0000000000000000000000000000000000000000".to_string())
302    }
303
304    /// Checks if the given raw string slice is a valid bech32 address.
305    ///
306    /// # Example
307    /// ```
308    /// use zilliqa_rs::core::ZilAddress;
309    ///
310    /// assert!(ZilAddress::is_bech32("zil18q05qzzst62q44mgrmp5dzn3jpsv4aukxredu2"))
311    /// ```
312    pub fn is_bech32(raw: &str) -> bool {
313        let regex = regex::Regex::new("^zil1[qpzry9x8gf2tvdw0s3jn54khce6mua7l]{38}$")
314            .expect("Failed to create the regex for `is_bech32`");
315
316        regex.is_match(raw)
317    }
318
319    pub fn is_address(address: &str) -> bool {
320        is_byte_string(address, 40)
321    }
322}
323
324impl FromStr for ZilAddress {
325    type Err = Error;
326
327    /// Parse a string slice into a ZilAddress.
328    fn from_str(addr: &str) -> Result<Self, Self::Err> {
329        if ZilAddress::is_address(addr) {
330            Ok(Self(ZilAddress::to_checksum_address(addr)?))
331        } else if ZilAddress::is_bech32(addr) {
332            Self::from_bech32(addr)
333        } else {
334            Err(Error::InvalidAddress(addr.to_string()))
335        }
336    }
337}
338
339impl TryFrom<H160> for ZilAddress {
340    type Error = Error;
341
342    fn try_from(value: H160) -> Result<Self, Self::Error> {
343        Self::from_str(&hex::encode(value))
344    }
345}
346
347impl Display for ZilAddress {
348    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
349        write!(f, "{}", self.0)
350    }
351}
352
353impl<'de> Deserialize<'de> for ZilAddress {
354    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
355    where
356        D: Deserializer<'de>,
357    {
358        let s: &str = Deserialize::deserialize(deserializer)?;
359
360        s.parse::<Self>().map_err(D::Error::custom)
361    }
362}
363
364#[cfg(test)]
365mod tests {
366    use claim::assert_ok;
367
368    use super::{is_byte_string, PrivateKey, PublicKey, TxHash, ZilAddress};
369
370    #[test]
371    fn is_byte_string_should_return_true_for_a_valid_byte_string_with_correct_size() {
372        let str = "1234567890";
373        assert!(is_byte_string(str, str.len()))
374    }
375
376    #[test]
377    fn is_byte_string_should_return_true_for_a_valid_byte_string_with_correct_size_even_if_its_prepended_with_0x() {
378        let str = "0x1234567890";
379        assert!(is_byte_string(str, str.len() - 2)) // -2 for 0x
380    }
381
382    #[test]
383    fn is_byte_string_should_return_true_for_a_valid_byte_string_with_correct_size_when_it_contains_letters_a_f() {
384        let str = "1234567890aabbccddeeff";
385        assert!(is_byte_string(str, str.len()))
386    }
387
388    #[test]
389    fn is_byte_string_should_return_false_if_size_is_incorrect() {
390        let str = "1234567890aabbccddeeff";
391        assert_eq!(is_byte_string(str, str.len() - 2), false);
392    }
393
394    #[test]
395    fn is_byte_string_should_return_false_if_contains_out_of_a_f_characters() {
396        let str = "1234567890aabbccddeeffgg";
397        assert_eq!(is_byte_string(str, str.len()), false);
398    }
399
400    #[test]
401    fn is_tx_hash_should_return_true_for_a_valid_hash() {
402        let hash = "bdadfd994f452df803cc223d1f417b02830ac96dbe5edad1b9f8d58613f95206";
403        assert!(TxHash::is_valid(hash));
404
405        let hash = "bdadfd994f452df803cc223d1f417b02830ac96dbe5edad1b9f8d58613f95206".to_ascii_uppercase();
406        assert!(TxHash::is_valid(&hash));
407    }
408
409    #[test]
410    fn is_tx_hash_should_return_false_for_a_invalid_hash() {
411        let hash = "bdadfd994f452df803cc223d102830ac96dbe5edad1b9f8d58613f95206";
412        assert!(!TxHash::is_valid(hash));
413    }
414
415    #[test]
416    fn should_parse_hex_string_to_private_key() {
417        let pv: PrivateKey = "D96e9eb5b782a80ea153c937fa83e5948485fbfc8b7e7c069d7b914dbc350aba"
418            .parse()
419            .unwrap();
420        assert_eq!(
421            "d96e9eb5b782a80ea153c937fa83e5948485fbfc8b7e7c069d7b914dbc350aba",
422            pv.to_string()
423        );
424
425        assert_eq!(
426            pv.public_key().to_string(),
427            "03bfad0f0b53cff5213b5947f3ddd66acee8906aba3610c111915aecc84092e052"
428        );
429    }
430
431    #[test]
432    fn should_parse_hex_string_to_private_key_if_prefixed_with_0x() {
433        let pv: PrivateKey = "0xD96e9eb5b782a80ea153c937fa83e5948485fbfc8b7e7c069d7b914dbc350aba"
434            .parse()
435            .unwrap();
436        assert_eq!(
437            "d96e9eb5b782a80ea153c937fa83e5948485fbfc8b7e7c069d7b914dbc350aba",
438            pv.to_string()
439        );
440
441        assert_eq!(
442            pv.public_key().to_string(),
443            "03bfad0f0b53cff5213b5947f3ddd66acee8906aba3610c111915aecc84092e052"
444        );
445    }
446
447    #[test]
448    fn should_parse_public_key_from_hexstring() {
449        let public_key: PublicKey = "03bfad0f0b53cff5213b5947f3ddd66acee8906aba3610c111915aecc84092e052"
450            .parse()
451            .unwrap();
452
453        assert_eq!(
454            public_key.to_string(),
455            "03bfad0f0b53cff5213b5947f3ddd66acee8906aba3610c111915aecc84092e052"
456        );
457    }
458
459    #[test]
460    fn valid_address_should_parse_correctly() {
461        let address = "0x381f4008505e940AD7681EC3468a719060caF796";
462        assert_ok!(address.parse::<ZilAddress>());
463        assert_eq!(address.parse::<ZilAddress>().unwrap().to_string(), address);
464
465        assert_ok!(address.strip_prefix("0x").unwrap().parse::<ZilAddress>());
466        assert_eq!(
467            address.strip_prefix("0x").unwrap().parse::<ZilAddress>().unwrap().to_string(),
468            address
469        );
470    }
471
472    #[test]
473    fn valid_bech32_address_should_parse_correctly() {
474        let address = "0x381f4008505e940AD7681EC3468a719060caF796";
475        let bech32_address = "zil18q05qzzst62q44mgrmp5dzn3jpsv4aukxredu2";
476        let zil_addr: ZilAddress = bech32_address.parse().unwrap();
477
478        assert_eq!(zil_addr.to_string(), address);
479    }
480
481    #[test]
482    fn to_bech32_address_should_return_correct_address() {
483        let address = "0x381f4008505e940AD7681EC3468a719060caF796";
484        let bech32_address = "zil18q05qzzst62q44mgrmp5dzn3jpsv4aukxredu2";
485
486        let zil_addr: ZilAddress = address.parse().unwrap();
487        assert_eq!(zil_addr.to_bech32().unwrap(), bech32_address);
488    }
489
490    #[test]
491    fn is_bech32_should_return_true_for_valid_one() {
492        assert!(ZilAddress::is_bech32("zil18q05qzzst62q44mgrmp5dzn3jpsv4aukxredu2"))
493    }
494
495    #[test]
496    fn is_bech32_should_return_false_for_invalid_ones() {
497        assert!(!ZilAddress::is_bech32("liz18q05qzzst62q44mgrmp5dzn3jpsv4aukxredu2"));
498        assert!(!ZilAddress::is_bech32("zil18q05qzzst62q44mgrmp5dzn3jpsv4aukxredu2ssaas"));
499    }
500
501    #[test]
502    fn to_checksum_address_should_return_correct_value_for_valid_input() {
503        let address = "11223344556677889900aabbccddeeff11223344";
504        let checksum = "0x11223344556677889900AabbccdDeefF11223344";
505
506        assert_eq!(checksum, ZilAddress::to_checksum_address(address).unwrap())
507    }
508}