1use crate::address::EthereumAddress;
2use crate::format::EthereumFormat;
3use anychain_core::{hex, Address, AddressError, PublicKey, PublicKeyError};
4use core::{fmt, fmt::Display, str::FromStr};
5
6#[derive(Debug, Clone, PartialEq, Eq)]
8pub struct EthereumPublicKey(libsecp256k1::PublicKey);
9
10impl PublicKey for EthereumPublicKey {
11 type SecretKey = libsecp256k1::SecretKey;
12 type Address = EthereumAddress;
13 type Format = EthereumFormat;
14
15 fn from_secret_key(secret_key: &Self::SecretKey) -> Self {
17 Self(libsecp256k1::PublicKey::from_secret_key(secret_key))
18 }
19
20 fn to_address(&self, _format: &Self::Format) -> Result<Self::Address, AddressError> {
22 Self::Address::from_public_key(self, _format)
23 }
24}
25
26impl EthereumPublicKey {
27 pub fn from_secp256k1_public_key(public_key: libsecp256k1::PublicKey) -> Self {
29 Self(public_key)
30 }
31
32 pub fn from_slice(sl: &[u8]) -> Result<Self, PublicKeyError> {
33 libsecp256k1::PublicKey::parse_slice(sl, None)
34 .map(Self)
35 .map_err(|e| PublicKeyError::Crate("from splice", format!("{:?}", e)))
36 }
37
38 pub fn to_secp256k1_public_key(&self) -> libsecp256k1::PublicKey {
40 self.0
41 }
42}
43
44impl FromStr for EthereumPublicKey {
45 type Err = PublicKeyError;
46
47 fn from_str(public_key: &str) -> Result<Self, Self::Err> {
48 let p = hex::decode(public_key)
49 .map_err(|error| PublicKeyError::Crate("hex", format!("{:?}", error)))?;
50 let public_key = libsecp256k1::PublicKey::parse_slice(p.as_slice(), None)
51 .map_err(|error| PublicKeyError::Crate("libsecp256k1", format!("{:?}", error)))?;
52
53 Ok(Self(public_key))
54 }
55}
56
57impl Display for EthereumPublicKey {
58 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
59 for s in &self.0.serialize()[1..] {
60 write!(f, "{:02x}", s)?;
61 }
62 Ok(())
63 }
64}
65
66#[cfg(test)]
67mod tests {
68 use super::*;
69 use libsecp256k1::SecretKey;
70
71 fn test_from_secret_key(expected_public_key: &EthereumPublicKey, secret_key: &SecretKey) {
72 let public_key = EthereumPublicKey::from_secret_key(secret_key);
73 assert_eq!(*expected_public_key, public_key);
74 }
75
76 fn test_to_address(expected_address: &EthereumAddress, public_key: &EthereumPublicKey) {
77 let address = public_key.to_address(&EthereumFormat::Standard).unwrap();
78 assert_eq!(*expected_address, address);
79 }
80
81 fn test_from_str(expected_public_key: &str, expected_address: &str) {
82 let public_key = EthereumPublicKey::from_str(expected_public_key).unwrap();
83 let address = public_key.to_address(&EthereumFormat::Standard).unwrap();
84 assert_eq!(expected_public_key, public_key.to_string());
85 assert_eq!(expected_address, address.to_string());
86 }
87
88 fn test_to_str(expected_public_key: &str, public_key: &EthereumPublicKey) {
89 assert_eq!(expected_public_key, public_key.to_string());
90 }
91
92 mod checksum_address {
93 use super::*;
94
95 const KEYPAIRS: [(&str, &str, &str); 5] = [
96 (
97 "2f46188bd601ece2a4446fa31de9419ee9baabf5305d65a5a7aea8badee27a5a",
98 "06d68e391c6961fceb5d8c5ad8ee5c6346db24df9dae61c9c0b0142409760451d982c0f35931f33e57adfc4f11bdf1946be2d75d6ecc925e8d22f319c71a721c",
99 "0x9Ed0C5817aE96Cb886BF74EB02B238De682e9B07"
100 ),
101 (
102 "d96c4c30bbabde58653e4fb4f4d97d064c70e300a37ab8780a8ecc15220423fb",
103 "bfe0746c85802c3ca1c2d5e4f4d23fb8321b8b1009af67855cc9a4aed8285567d7045bb700e27d5e33572ae5d84a8d1e11bb134f6f14f37ffcb2fa73f7c6b0ac",
104 "0xBc90633A78dA594ace8e25AAA3517F924C76099d"
105 ),
106 (
107 "c677a1215eebd35d20337d8896ee6579c78f41f93946b17c8d4ccb772c25cde4",
108 "ff3e50efb509efd0d18ff9074bc8b253419d2437e0c1e81661c1ba419f877162eed685d80bdd3b33adde4ff2a0946dd97460f126992064059a129e2a7172d566",
109 "0xA99E404A60ab8561F7c844529F735A88D7A61C5A"
110 ),
111 (
112 "b681e5bd4ddffefe1a691fe7c6375775c11992b9a25e4f9e3f235eb054d49343",
113 "d9ed72afa68a9732df005df2dbbfb2abcad050579bd8dfeb32389d0f1e492d130ca33f9e71345d558da5859026fee86c03be685f95a4c8ddc55e048c5ff8b398",
114 "0x28826C9f713c96ee63e59Ed9220c77b021FAfC3e"
115 ),
116 (
117 "da5d359af6827e76e0a1b71c75c375f0d33f63bae4fd551d81ee10faa34e33e9",
118 "0b752d5e89126b62a99edfe40a4cbd9122cfb04257a28d225858d38bc92a0e1517e797e9029e810b329afa32a1d46268e84eb10c700314b0059f506130d1e9e6",
119 "0x9eC59170674DbEfeF40efE2ED03175b39fCA921a"
120 )
121 ];
122
123 #[test]
124 fn from_private_key() {
125 KEYPAIRS.iter().for_each(|(secret_key, public_key, _)| {
126 let public_key = EthereumPublicKey::from_str(public_key).unwrap();
127 let secret_key = hex::decode(*secret_key).unwrap();
128 let secret_key = SecretKey::parse_slice(&secret_key).unwrap();
129 test_from_secret_key(&public_key, &secret_key);
130 });
131 }
132
133 #[test]
134 fn to_address() {
135 KEYPAIRS.iter().for_each(|(_, public_key, address)| {
136 let address = EthereumAddress::from_str(address).unwrap();
137 let public_key = EthereumPublicKey::from_str(public_key).unwrap();
138 test_to_address(&address, &public_key);
139 });
140 }
141
142 #[test]
143 fn from_str() {
144 KEYPAIRS
145 .iter()
146 .for_each(|(_, expected_public_key, expected_address)| {
147 test_from_str(expected_public_key, expected_address);
148 });
149 }
150
151 #[test]
152 fn to_str() {
153 KEYPAIRS.iter().for_each(|(_, expected_public_key, _)| {
154 let public_key = EthereumPublicKey::from_str(expected_public_key).unwrap();
155 test_to_str(expected_public_key, &public_key);
156 });
157 }
158
159 #[test]
160 fn test_pubkey() {
161 let str = "b9b77d6ac1380a581d3efc136a21a939f5a6ce59afeb3eddf6a52b342b33f5be455b3610100ee1129d1638e99272879be60519835e2b3b7703eb4791af3daa7f";
162 let public_key = EthereumPublicKey::from_str(str).unwrap();
163 let address = EthereumAddress::checksum_address(&public_key);
164 assert_eq!(
165 "0xDF3e1897f4b01f6b17870b98B4548BaBE14A007C",
166 address.to_string()
167 );
168 }
169 }
170
171 #[test]
172 fn test_checksum_address_invalid() {
173 let public_key = "0";
176 assert!(EthereumPublicKey::from_str(public_key).is_err());
177
178 let public_key = "06d68e391c6961fceb5d8c5ad8ee5c6346db24df9dae61c9c0b014";
179 assert!(EthereumPublicKey::from_str(public_key).is_err());
180
181 let public_key = "06d68e391c6961fceb5d8c5ad8ee5c6346db24df9dae61c9c0b0142409760451d982c0f35931f33e57adfc4f11bdf1946be2d75d6ecc925e8d22f319c71a721";
182 assert!(EthereumPublicKey::from_str(public_key).is_err());
183
184 let public_key = "06d68e391c6961fceb5d8c5ad8ee5c6346db24df9dae61c9c0b0142409760451d982c0f35931f33e57adfc4f11bdf1946be2d75d6ecc925e8d22f319c71a721c06d68e391c6961fceb5d8c5ad8ee5c6346db24df9dae61c9c0b0142409760451d982c0f3593";
185 assert!(EthereumPublicKey::from_str(public_key).is_err());
186
187 let public_key = "06d68e391c6961fceb5d8c5ad8ee5c6346db24df9dae61c9c0b0142409760451d982c0f35931f33e57adfc4f11bdf1946be2d75d6ecc925e8d22f319c71a721c06d68e391c6961fceb5d8c5ad8ee5c6346db24df9dae61c9c0b0142409760451d982c0f35931f33e57adfc4f11bdf1946be2d75d6ecc925e8d22f319c71a721c";
188 assert!(EthereumPublicKey::from_str(public_key).is_err());
189 }
190
191 #[test]
192 fn address_gen() {
193 let raw_pk = [
194 68, 157, 12, 4, 213, 228, 35, 105, 155, 249, 86, 130, 216, 186, 113, 85, 31, 137, 113,
195 153, 70, 239, 218, 142, 132, 65, 222, 134, 52, 145, 148, 88, 63, 245, 105, 222, 219,
196 39, 56, 192, 195, 4, 38, 29, 9, 78, 172, 238, 179, 168, 66, 80, 132, 123, 45, 104, 145,
197 132, 159, 243, 144, 62, 194, 164,
198 ];
199 let raw_pk1 = [
200 117, 243, 73, 0, 152, 143, 226, 83, 116, 252, 10, 247, 191, 14, 206, 13, 110, 192, 140,
201 32, 250, 238, 177, 101, 109, 113, 26, 254, 67, 191, 47, 11, 155, 57, 117, 158, 227,
202 111, 235, 20, 65, 167, 102, 64, 98, 103, 106, 226, 241, 213, 193, 36, 72, 57, 163, 202,
203 72, 21, 35, 233, 194, 163, 225, 28,
204 ];
205
206 let pk = EthereumPublicKey::from_slice(&raw_pk);
207 assert!(pk.is_ok());
208 let pk1 = EthereumPublicKey::from_slice(&raw_pk1);
209 assert!(pk1.is_ok());
210
211 let addr = pk.unwrap().to_address(&EthereumFormat::Standard).unwrap();
212 let addr1 = pk1.unwrap().to_address(&EthereumFormat::Standard).unwrap();
213
214 assert_eq!(
215 "0xE28D6881aC932066611A259a8C343E545b0b55B7",
216 addr.to_string()
217 );
218 assert_eq!(
219 "0xCd28AF3e09527D2a756F1e7c7aD7A8A9BdEB080d",
220 addr1.to_string()
221 );
222 }
223
224 #[test]
225 fn test_public_key_from_invalid_slice() {
226 let invalid_slice = [1u8; 31]; let public_key = EthereumPublicKey::from_slice(&invalid_slice);
228 assert!(public_key.is_err());
229
230 let invalid_slice = [0u8; 65]; let public_key = EthereumPublicKey::from_slice(&invalid_slice);
232 assert!(public_key.is_err());
233 }
234}