avalanche_types/key/secp256k1/
mod.rs1pub mod address;
3pub mod keychain;
4pub mod kms;
5pub mod private_key;
6pub mod public_key;
7pub mod signature;
8pub mod txs;
9
10#[cfg(feature = "libsecp256k1")]
11#[cfg_attr(docsrs, doc(cfg(feature = "libsecp256k1")))]
12pub mod libsecp256k1;
13
14#[cfg(feature = "mnemonic")]
15#[cfg_attr(docsrs, doc(cfg(feature = "mnemonic")))]
16pub mod mnemonic;
17
18use std::{
19 collections::HashMap,
20 fmt,
21 fs::{self, File},
22 io::Write,
23 path::Path,
24};
25
26use crate::{
27 codec::serde::hex_0x_primitive_types_h160::Hex0xH160,
28 errors::{Error, Result},
29 ids::short,
30};
31use async_trait::async_trait;
32use lazy_static::lazy_static;
33use rust_embed::RustEmbed;
34use serde::{Deserialize, Serialize};
35use serde_with::{serde_as, DisplayFromStr};
36
37#[async_trait]
41pub trait SignOnly {
42 fn signing_key(&self) -> Result<k256::ecdsa::SigningKey>;
43
44 async fn sign_digest(&self, digest: &[u8]) -> Result<[u8; 65]>;
52}
53
54pub trait ReadOnly {
56 fn key_type(&self) -> KeyType;
57 fn hrp_address(&self, network_id: u32, chain_id_alias: &str) -> Result<String>;
61 fn short_address(&self) -> Result<short::Id>;
62 fn short_address_bytes(&self) -> Result<Vec<u8>>;
63 fn eth_address(&self) -> String;
64 fn h160_address(&self) -> primitive_types::H160;
65}
66
67lazy_static! {
68 pub static ref TEST_KEYS: Vec<crate::key::secp256k1::private_key::Key> = {
70 #[derive(RustEmbed)]
71 #[folder = "artifacts/"]
72 #[prefix = "artifacts/"]
73 struct Asset;
74
75 let key_file = Asset::get("artifacts/test.insecure.secp256k1.key.infos.json").unwrap();
76
77 let key_infos: Vec<Info> = serde_json::from_slice(&key_file.data).unwrap();
78 let mut keys: Vec<crate::key::secp256k1::private_key::Key> = Vec::new();
79 for ki in key_infos.iter() {
80 keys.push(ki.to_private_key());
81 }
82 keys
83 };
84
85 pub static ref TEST_INFOS: Vec<Info> = {
87 #[derive(RustEmbed)]
88 #[folder = "artifacts/"]
89 #[prefix = "artifacts/"]
90 struct Asset;
91
92 let key_file = Asset::get("artifacts/test.insecure.secp256k1.key.infos.json").unwrap();
93 serde_json::from_slice(&key_file.data).unwrap()
94 };
95}
96
97#[test]
99fn test_keys() {
100 let _ = env_logger::builder()
101 .filter_level(log::LevelFilter::Info)
102 .is_test(true)
103 .try_init();
104
105 for k in TEST_KEYS.iter() {
106 log::info!(
107 "[KEY] test key eth address {:?}",
108 k.to_public_key().to_eth_address()
109 );
110 }
111 for ki in TEST_INFOS.iter() {
112 log::info!("[INFO] test key eth address {:?}", ki.eth_address);
113 }
114 assert_eq!(TEST_KEYS.len(), TEST_INFOS.len());
115
116 log::info!("total {} test keys are found", TEST_KEYS.len());
117}
118
119#[serde_as]
124#[derive(Debug, Serialize, Deserialize, Eq, PartialEq, Clone)]
125#[serde(rename_all = "snake_case")]
126pub struct Info {
127 #[serde(skip_serializing_if = "Option::is_none")]
129 pub id: Option<String>,
130 #[serde_as(as = "DisplayFromStr")]
131 pub key_type: KeyType,
132
133 #[serde(skip_serializing_if = "Option::is_none")]
134 pub mnemonic_phrase: Option<String>,
135 #[serde(skip_serializing_if = "Option::is_none")]
137 pub private_key_cb58: Option<String>,
138 #[serde(skip_serializing_if = "Option::is_none")]
140 pub private_key_hex: Option<String>,
141
142 #[serde(default)]
143 pub addresses: HashMap<u32, ChainAddresses>,
144 #[serde(default)]
145 pub short_address: short::Id,
146 #[serde(default)]
147 pub eth_address: String,
148 #[serde_as(as = "Hex0xH160")]
149 pub h160_address: primitive_types::H160,
150}
151
152impl Default for Info {
153 fn default() -> Self {
154 Info {
155 id: None,
156 key_type: KeyType::Unknown(String::new()),
157 mnemonic_phrase: None,
158 private_key_cb58: None,
159 private_key_hex: None,
160 addresses: HashMap::new(),
161 short_address: short::Id::empty(),
162 eth_address: String::new(),
163 h160_address: primitive_types::H160::zero(),
164 }
165 }
166}
167
168impl From<&crate::key::secp256k1::private_key::Key> for Info {
169 fn from(sk: &crate::key::secp256k1::private_key::Key) -> Self {
170 sk.to_info(1).unwrap()
171 }
172}
173
174impl Info {
175 pub fn load(file_path: &str) -> Result<Self> {
176 log::info!("loading Info from {}", file_path);
177
178 if !Path::new(file_path).exists() {
179 return Err(Error::Other {
180 message: format!("file {} does not exists", file_path),
181 retryable: false,
182 });
183 }
184
185 let f = File::open(file_path).map_err(|e| Error::Other {
186 message: format!("failed to open {} ({})", file_path, e),
187 retryable: false,
188 })?;
189 serde_yaml::from_reader(f).map_err(|e| Error::Other {
190 message: format!("failed serde_yaml::from_reader {}", e),
191 retryable: false,
192 })
193 }
194
195 pub fn sync(&self, file_path: String) -> std::io::Result<()> {
196 log::info!("syncing key info to '{}'", file_path);
197 let path = Path::new(&file_path);
198 let parent_dir = path.parent().unwrap();
199 fs::create_dir_all(parent_dir)?;
200
201 let d = serde_json::to_vec(self).map_err(|e| {
202 std::io::Error::new(
203 std::io::ErrorKind::Other,
204 format!("failed to serialize JSON {}", e),
205 )
206 })?;
207
208 let mut f = File::create(&file_path)?;
209 f.write_all(&d)?;
210
211 Ok(())
212 }
213
214 pub fn to_private_key(&self) -> crate::key::secp256k1::private_key::Key {
215 crate::key::secp256k1::private_key::Key::from_cb58(self.private_key_cb58.clone().unwrap())
216 .unwrap()
217 }
218}
219
220impl fmt::Display for Info {
224 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
225 let s = serde_yaml::to_string(&self).unwrap();
226 write!(f, "{}", s)
227 }
228}
229
230#[derive(
232 Deserialize,
233 Serialize,
234 std::clone::Clone,
235 std::cmp::Eq,
236 std::cmp::Ord,
237 std::cmp::PartialEq,
238 std::cmp::PartialOrd,
239 std::fmt::Debug,
240 std::hash::Hash,
241)]
242pub enum KeyType {
243 #[serde(rename = "hot")]
244 Hot,
245 #[serde(rename = "aws-kms")]
246 AwsKms,
247 Unknown(String),
248}
249
250impl std::convert::From<&str> for KeyType {
251 fn from(s: &str) -> Self {
252 match s {
253 "hot" => KeyType::Hot,
254 "aws-kms" => KeyType::AwsKms,
255 "aws_kms" => KeyType::AwsKms,
256
257 other => KeyType::Unknown(other.to_owned()),
258 }
259 }
260}
261
262impl std::str::FromStr for KeyType {
263 type Err = std::convert::Infallible;
264
265 fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
266 Ok(KeyType::from(s))
267 }
268}
269
270impl std::fmt::Display for KeyType {
274 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
275 write!(f, "{}", self.as_str())
276 }
277}
278
279impl KeyType {
280 pub fn as_str(&self) -> &str {
282 match self {
283 KeyType::Hot => "hot",
284 KeyType::AwsKms => "aws-kms",
285
286 KeyType::Unknown(s) => s.as_ref(),
287 }
288 }
289
290 pub fn values() -> &'static [&'static str] {
292 &[
293 "hot", "aws-kms", ]
296 }
297}
298
299impl AsRef<str> for KeyType {
300 fn as_ref(&self) -> &str {
301 self.as_str()
302 }
303}
304
305#[derive(Debug, Serialize, Deserialize, Eq, PartialEq, Clone)]
306#[serde(rename_all = "snake_case")]
307pub struct ChainAddresses {
308 pub x: String,
309 pub p: String,
310}
311
312#[test]
314fn test_keys_address() {
315 let _ = env_logger::builder()
316 .filter_level(log::LevelFilter::Info)
317 .is_test(true)
318 .try_init();
319
320 #[derive(RustEmbed)]
321 #[folder = "artifacts/"]
322 #[prefix = "artifacts/"]
323 struct Asset;
324
325 for asset in ["artifacts/test.insecure.secp256k1.key.infos.json"] {
326 let key_file = Asset::get(asset).unwrap();
327 let key_contents = std::str::from_utf8(key_file.data.as_ref()).unwrap();
328 let key_infos: Vec<Info> = serde_json::from_slice(key_contents.as_bytes()).unwrap();
329 log::info!("loaded {}", asset);
330
331 for (pos, ki) in key_infos.iter().enumerate() {
332 log::info!("checking the key info at {}", pos);
333
334 let sk = crate::key::secp256k1::private_key::Key::from_cb58(
335 &ki.private_key_cb58.clone().unwrap(),
336 )
337 .unwrap();
338 assert_eq!(
339 sk,
340 crate::key::secp256k1::private_key::Key::from_hex(
341 ki.private_key_hex.clone().unwrap()
342 )
343 .unwrap(),
344 );
345 let pubkey = sk.to_public_key();
346
347 assert_eq!(
348 pubkey.to_hrp_address(1, "X").unwrap(),
349 ki.addresses.get(&1).unwrap().x
350 );
351 assert_eq!(
352 pubkey.to_hrp_address(1, "P").unwrap(),
353 ki.addresses.get(&1).unwrap().p
354 );
355
356 assert_eq!(
357 pubkey.to_hrp_address(9999, "X").unwrap(),
358 ki.addresses.get(&9999).unwrap().x
359 );
360 assert_eq!(
361 pubkey.to_hrp_address(9999, "P").unwrap(),
362 ki.addresses.get(&9999).unwrap().p
363 );
364
365 assert_eq!(pubkey.to_short_id().unwrap(), ki.short_address);
366 assert_eq!(pubkey.to_eth_address(), ki.eth_address);
367 }
368 }
369}