anychain_ethereum/
public_key.rs

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/// Represents an Ethereum public key
7#[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    /// Returns the public key corresponding to the given private key.
16    fn from_secret_key(secret_key: &Self::SecretKey) -> Self {
17        Self(libsecp256k1::PublicKey::from_secret_key(secret_key))
18    }
19
20    /// Returns the address of this public key.
21    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    /// Returns a public key given a secp256k1 public key.
28    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    /// Returns the secp256k1 public key of the public key
39    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        // Invalid public key length
174
175        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]; // A 31-byte slice, invalid as a public key
227        let public_key = EthereumPublicKey::from_slice(&invalid_slice);
228        assert!(public_key.is_err());
229
230        let invalid_slice = [0u8; 65]; // A 31-byte slice, invalid as a public key
231        let public_key = EthereumPublicKey::from_slice(&invalid_slice);
232        assert!(public_key.is_err());
233    }
234}