anychain_ethereum/
address.rs

1use crate::format::EthereumFormat;
2use crate::public_key::EthereumPublicKey;
3use anychain_core::{to_hex_string, Address, AddressError, Error, PublicKey};
4
5use anychain_core::hex;
6use anychain_core::utilities::crypto::keccak256;
7use core::{convert::TryFrom, fmt, str::FromStr};
8use libsecp256k1::SecretKey;
9use regex::Regex;
10use serde::{Deserialize, Serialize};
11
12/// Represents an Ethereum address
13#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, Hash, Default)]
14pub struct EthereumAddress(String);
15
16impl Address for EthereumAddress {
17    type SecretKey = SecretKey;
18    type Format = EthereumFormat;
19    type PublicKey = EthereumPublicKey;
20
21    /// Returns the address corresponding to the given private key.
22    fn from_secret_key(
23        secret_key: &Self::SecretKey,
24        _format: &Self::Format,
25    ) -> Result<Self, AddressError> {
26        Self::from_public_key(&EthereumPublicKey::from_secret_key(secret_key), _format)
27    }
28
29    /// Returns the address corresponding to the given public key.
30    fn from_public_key(
31        public_key: &Self::PublicKey,
32        _: &Self::Format,
33    ) -> Result<Self, AddressError> {
34        // public_key.from_private_key();
35        Ok(Self::checksum_address(public_key))
36    }
37}
38
39impl EthereumAddress {
40    /// Returns the checksum address given a public key.
41    /// Adheres to EIP-55 <https://eips.ethereum.org/EIPS/eip-55>.
42    pub fn checksum_address(public_key: &EthereumPublicKey) -> Self {
43        let hash = keccak256(&public_key.to_secp256k1_public_key().serialize()[1..]);
44        let address = to_hex_string(&hash[12..]).to_lowercase();
45
46        let hash = to_hex_string(&keccak256(address.as_bytes()));
47        let mut checksum_address = "0x".to_string();
48        for c in 0..40 {
49            let ch = match &hash[c..=c] {
50                "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" => address[c..=c].to_lowercase(),
51                _ => address[c..=c].to_uppercase(),
52            };
53            checksum_address.push_str(&ch);
54        }
55
56        EthereumAddress(checksum_address)
57    }
58
59    pub fn to_bytes(&self) -> Result<Vec<u8>, Error> {
60        let regex = Regex::new(r"^0x").unwrap();
61        let address = self.0.clone();
62        let address = address.to_lowercase();
63        let address = regex.replace_all(&address, "").to_string();
64        Ok(hex::decode(address)?)
65    }
66
67    pub fn len(&self) -> usize {
68        self.0.len()
69    }
70
71    pub fn is_empty(&self) -> bool {
72        self.len() == 0
73    }
74}
75
76impl<'a> TryFrom<&'a str> for EthereumAddress {
77    type Error = AddressError;
78
79    fn try_from(address: &'a str) -> Result<Self, Self::Error> {
80        Self::from_str(address)
81    }
82}
83
84impl FromStr for EthereumAddress {
85    type Err = AddressError;
86
87    fn from_str(address: &str) -> Result<Self, Self::Err> {
88        let addr = match address.starts_with("0x") {
89            true => &address[2..],
90            false => address,
91        };
92        if addr.len() != 40 {
93            return Err(AddressError::InvalidCharacterLength(addr.len()));
94        }
95        let addr = addr.to_lowercase();
96        let _ = hex::decode(addr)?;
97        match address.starts_with("0x") {
98            true => Ok(EthereumAddress(address.to_string())),
99            false => Ok(EthereumAddress(format!("0x{}", address))),
100        }
101    }
102}
103
104impl fmt::Display for EthereumAddress {
105    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
106        write!(f, "{}", self.0)
107    }
108}
109
110#[cfg(test)]
111mod tests {
112    use super::*;
113    use anychain_core::public_key::PublicKey;
114
115    fn test_from_str(expected_address: &str) {
116        let address = EthereumAddress::from_str(expected_address).unwrap();
117        assert_eq!(expected_address, address.to_string());
118    }
119
120    fn test_to_str(expected_address: &str, address: &EthereumAddress) {
121        assert_eq!(expected_address, address.to_string());
122    }
123
124    #[test]
125    fn test_public_key_bytes_to_address() {
126        let public_key = &[
127            48, 197, 53, 33, 226, 92, 169, 86, 37, 63, 188, 254, 37, 235, 20, 135, 106, 56, 177,
128            59, 236, 29, 192, 201, 164, 68, 243, 209, 167, 158, 75, 249, 32, 161, 71, 27, 58, 76,
129            240, 10, 117, 87, 201, 40, 236, 137, 172, 167, 140, 5, 65, 94, 239, 146, 230, 155, 0,
130            250, 200, 93, 219, 69, 123, 168,
131        ];
132
133        let public_key = libsecp256k1::PublicKey::parse_slice(public_key, None).unwrap();
134        let public_key = EthereumPublicKey::from_secp256k1_public_key(public_key);
135        let address = public_key.to_address(&EthereumFormat::Standard).unwrap();
136
137        assert_eq!(
138            "0x0Df2f15895AB69A7eF06519F6c4732e648719f04",
139            address.to_string()
140        );
141    }
142
143    mod checksum_address {
144        use super::*;
145
146        const KEYPAIRS: [(&str, &str); 5] = [
147            (
148                "f89f23eaeac18252fedf81bb8318d3c111d48c19b0680dcf6e0a8d5136caf287",
149                "0x9141B7539E7902872095C408BfA294435e2b8c8a",
150            ),
151            (
152                "a93701ea343247db13466f6448ffbca658726e2b4a77530db3eca3c9250b4f0d",
153                "0xa0967B1F698DC497A694FE955666D1dDd398145C",
154            ),
155            (
156                "de61e35e2e5eb9504d52f5042126591d80144d49f74b8ced68f4959a3e8edffd",
157                "0xD5d13d1dD277BB9041e560A63ee29c086D370b0A",
158            ),
159            (
160                "56f01d5e01b6fd1cc123d8d1eae0d148e00c025b5be2ef624775f7a1b802e9c1",
161                "0xc4488ebbE882fa2aF1D466CB2C8ecafE316c067a",
162            ),
163            (
164                "363af8b4d3ff22bb0e4ffc2ff198b4b5be0316f8a507ad5fe32f021c3d1ae8ad",
165                "0xF9001e6AEE6EA439D713fBbF960EbA76f4770E2B",
166            ),
167        ];
168
169        #[test]
170        fn from_str() {
171            KEYPAIRS.iter().for_each(|(_, address)| {
172                test_from_str(address);
173            });
174        }
175
176        #[test]
177        fn to_str() {
178            KEYPAIRS.iter().for_each(|(_, expected_address)| {
179                let address = EthereumAddress::from_str(expected_address).unwrap();
180                test_to_str(expected_address, &address);
181            });
182        }
183    }
184
185    #[test]
186    fn test_address() {
187        let pubkey = EthereumPublicKey::from_str(
188            "040b4fed878e6b0ff6847e2ac9c13b556d161e1344cd270ed6cafac21f0144399d9ef31f2\
189             67722fdeccba59ffd57ff84a020a2d3b416344c68e840bc7d97e77570",
190        )
191        .unwrap();
192        let address = EthereumAddress::from_public_key(&pubkey, &EthereumFormat::Standard).unwrap();
193        assert_eq!(
194            "0x5a2a8410875E882aEe87bF8e5F2e1eDE8810617b",
195            address.to_string()
196        )
197    }
198}