ethereum_crypto/
lib.rs

1pub mod utils;
2pub mod error;
3
4use error::Error;
5use secp256k1::{PublicKey, SecretKey};
6use secp256k1::rand::{thread_rng};
7
8pub struct EthereumKeypair {
9    pub secret_key: SecretKey,
10    pub public_key: PublicKey,
11    pub address: EthereumAddress,
12}
13
14pub struct EthereumAddress([u8;20]);
15
16impl ToString for EthereumAddress {
17    fn to_string(&self) -> String {
18        let mut base = String::from("0x");
19        base.push_str(&hex::encode(self.0));
20        base
21    }
22}
23
24#[cfg(feature = "ethereum-types")]
25impl EthereumAddress {
26    pub fn as_h160(self) -> ethereum_types::H160 {
27        ethereum_types::H160(self.0)
28    }
29}
30
31impl EthereumKeypair {
32    pub fn from_secret_key_str(private_key: &str) -> Result<Self, Error> {
33        let trimmed = match private_key.strip_prefix("0x") {
34            Some(t) => t,
35            None => private_key
36        };
37        if trimmed.len() != 64 {
38            return Err(Error::SecretKeyLenMismatch)
39        }
40        let decoded = hex::decode(trimmed)?;
41        if decoded.len() != 32 {
42            return Err(Error::SecretKeyLenMismatch);
43        }
44        Ok(Self::from_secret_key(&decoded[0..32])?)
45    }
46
47    pub fn from_secret_key(x: &[u8]) -> Result<Self, Error> {
48        let secret_key = SecretKey::from_slice(x)?;
49        let public_key = PublicKey::from_secret_key(secp256k1::SECP256K1, &secret_key);
50        let address = utils::get_address_from_public_key(&public_key);
51        Ok(Self {
52            secret_key, public_key, address: EthereumAddress(address.try_into().unwrap())
53        })
54    }
55
56    pub fn generate_new<T : secp256k1::rand::Rng + ?Sized>(rng_core: &mut T) -> Self {
57        let (secret_key, public_key) = secp256k1::generate_keypair(rng_core);
58        let address = utils::get_address_from_public_key(&public_key);
59        Self {
60            secret_key, public_key, address: EthereumAddress(address.try_into().expect("Wrong address length"))
61        }
62    }
63
64    pub fn generate_new_with_thread_rng() -> Self {
65        Self::generate_new(&mut thread_rng())
66    }
67
68    pub fn get_address(&self) -> &EthereumAddress {
69        &self.address
70    }
71
72    pub fn get_secret_key(&self) -> &SecretKey {
73        &self.secret_key
74    }
75
76    pub fn get_public_key(&self) -> &PublicKey {
77        &self.public_key
78    }
79
80    pub fn export_secret_key_as_hex_string(&self) -> String {
81        self.secret_key.display_secret().to_string()
82    }
83
84    pub fn export_public_key_as_hex_string(&self) -> String {
85        self.public_key.to_string()
86    }
87}
88
89#[cfg(test)]
90mod tests {
91    use crate::EthereumKeypair;
92
93    #[test]
94    #[should_panic]
95    fn from_secret_key_string_0() {
96        EthereumKeypair::from_secret_key_str("this is not a private key, probably").unwrap();
97    }
98
99    #[test]
100    fn from_secret_key_string_1() {
101        let key = EthereumKeypair::from_secret_key_str(
102            "742a504d9674cf3c3a6f2ade3b3780660559209ee45279c230a534ca35187b9e"
103        ).unwrap();
104        assert_eq!(
105            key.export_secret_key_as_hex_string(),
106            "742a504d9674cf3c3a6f2ade3b3780660559209ee45279c230a534ca35187b9e".to_string()
107        );
108        assert_eq!(
109            key.get_address().to_string(),
110            "0x55555556e84ad25e7d3288da2122f0784e27213d".to_string()
111        );
112    }
113
114    #[test]
115    fn generate_new() {
116        let key = EthereumKeypair::generate_new_with_thread_rng();
117        assert!(key.address.to_string().starts_with("0x"));
118    }
119}