tofuri_address/
lib.rs

1use sha2::Digest;
2use sha2::Sha256;
3use std::fmt;
4use std::fmt::Display;
5use std::fmt::Formatter;
6use tofuri_core::*;
7#[derive(Debug)]
8pub enum Error {
9    Hex(hex::FromHexError),
10    InvalidAddress,
11    InvalidAddressChecksum,
12    AddressChecksumMismatch,
13    InvalidSecretKey,
14    InvalidSecretKeyChecksum,
15    SecretKeyChecksumMismatch,
16}
17impl Display for Error {
18    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
19        write!(f, "{:?}", self)
20    }
21}
22impl std::error::Error for Error {
23    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
24        match self {
25            Error::Hex(err) => Some(err),
26            _ => None,
27        }
28    }
29}
30pub fn checksum(bytes: &[u8]) -> [u8; 4] {
31    let mut hasher = Sha256::new();
32    hasher.update(bytes);
33    let hash = hasher.finalize();
34    let mut checksum = [0; 4];
35    checksum.copy_from_slice(&hash[..4]);
36    checksum
37}
38pub mod address {
39    use super::*;
40    pub fn encode(address: &AddressBytes) -> String {
41        [
42            PREFIX_ADDRESS,
43            &hex::encode(address),
44            &hex::encode(checksum(address)),
45        ]
46        .concat()
47    }
48    pub fn decode(str: &str) -> Result<AddressBytes, Error> {
49        let decoded = hex::decode(str.replacen(PREFIX_ADDRESS, "", 1)).map_err(Error::Hex)?;
50        let address_bytes: AddressBytes = decoded
51            .get(0..20)
52            .ok_or(Error::InvalidAddress)?
53            .try_into()
54            .unwrap();
55        if checksum(&address_bytes) == decoded.get(20..).ok_or(Error::InvalidAddressChecksum)? {
56            Ok(address_bytes)
57        } else {
58            Err(Error::AddressChecksumMismatch)
59        }
60    }
61    #[cfg(test)]
62    mod tests {
63        use super::*;
64        #[test]
65        fn test_encode() {
66            assert_eq!(
67                "0x0000000000000000000000000000000000000000de47c9b2",
68                encode(&[0; 20])
69            );
70        }
71        #[test]
72        fn test_decode() {
73            assert_eq!(
74                [0; 20],
75                decode("0x0000000000000000000000000000000000000000de47c9b2").unwrap()
76            );
77        }
78    }
79}
80pub mod secret {
81    use super::*;
82    pub fn encode(secret_key: &SecretKeyBytes) -> String {
83        [
84            PREFIX_SECRET_KEY,
85            &hex::encode(secret_key),
86            &hex::encode(checksum(secret_key)),
87        ]
88        .concat()
89    }
90    pub fn decode(str: &str) -> Result<SecretKeyBytes, Error> {
91        let decoded = hex::decode(str.replacen(PREFIX_SECRET_KEY, "", 1)).map_err(Error::Hex)?;
92        let secret_key_bytes: SecretKeyBytes = decoded
93            .get(0..32)
94            .ok_or(Error::InvalidSecretKey)?
95            .try_into()
96            .unwrap();
97        if checksum(&secret_key_bytes)
98            == decoded.get(32..).ok_or(Error::InvalidSecretKeyChecksum)?
99        {
100            Ok(secret_key_bytes)
101        } else {
102            Err(Error::SecretKeyChecksumMismatch)
103        }
104    }
105    #[cfg(test)]
106    mod tests {
107        use super::*;
108        #[test]
109        fn test_encode() {
110            assert_eq!(
111                encode(&[0; 32]),
112                "SECRETx000000000000000000000000000000000000000000000000000000000000000066687aad"
113            );
114        }
115        #[test]
116        fn test_decode() {
117            assert_eq!(
118                decode("SECRETx000000000000000000000000000000000000000000000000000000000000000066687aad").unwrap(),
119                [0; 32]
120            );
121        }
122    }
123}
124#[cfg(test)]
125mod tests {
126    use super::*;
127    #[test]
128    fn test_cecksum() {
129        assert_eq!(checksum(&[0; 32]), [102, 104, 122, 173]);
130        assert_eq!(checksum(&[0; 33]), [127, 156, 158, 49]);
131    }
132}