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}