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#[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 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 fn from_public_key(
31 public_key: &Self::PublicKey,
32 _: &Self::Format,
33 ) -> Result<Self, AddressError> {
34 Ok(Self::checksum_address(public_key))
36 }
37}
38
39impl EthereumAddress {
40 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}