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}