pub mod address;
pub mod keychain;
pub mod kms;
pub mod private_key;
pub mod public_key;
pub mod signature;
pub mod txs;
#[cfg(feature = "libsecp256k1")]
#[cfg_attr(docsrs, doc(cfg(feature = "libsecp256k1")))]
pub mod libsecp256k1;
#[cfg(feature = "mnemonic")]
#[cfg_attr(docsrs, doc(cfg(feature = "mnemonic")))]
pub mod mnemonic;
use std::{
collections::HashMap,
fmt,
fs::{self, File},
io::Write,
path::Path,
};
use crate::{
codec::serde::hex_0x_primitive_types_h160::Hex0xH160,
errors::{Error, Result},
ids::short,
};
use async_trait::async_trait;
use lazy_static::lazy_static;
use rust_embed::RustEmbed;
use serde::{Deserialize, Serialize};
use serde_with::{serde_as, DisplayFromStr};
#[async_trait]
pub trait SignOnly {
fn signing_key(&self) -> Result<k256::ecdsa::SigningKey>;
async fn sign_digest(&self, digest: &[u8]) -> Result<[u8; 65]>;
}
pub trait ReadOnly {
fn key_type(&self) -> KeyType;
fn hrp_address(&self, network_id: u32, chain_id_alias: &str) -> Result<String>;
fn short_address(&self) -> Result<short::Id>;
fn short_address_bytes(&self) -> Result<Vec<u8>>;
fn eth_address(&self) -> String;
fn h160_address(&self) -> primitive_types::H160;
}
lazy_static! {
pub static ref TEST_KEYS: Vec<crate::key::secp256k1::private_key::Key> = {
#[derive(RustEmbed)]
#[folder = "artifacts/"]
#[prefix = "artifacts/"]
struct Asset;
let key_file = Asset::get("artifacts/test.insecure.secp256k1.key.infos.json").unwrap();
let key_infos: Vec<Info> = serde_json::from_slice(&key_file.data).unwrap();
let mut keys: Vec<crate::key::secp256k1::private_key::Key> = Vec::new();
for ki in key_infos.iter() {
keys.push(ki.to_private_key());
}
keys
};
pub static ref TEST_INFOS: Vec<Info> = {
#[derive(RustEmbed)]
#[folder = "artifacts/"]
#[prefix = "artifacts/"]
struct Asset;
let key_file = Asset::get("artifacts/test.insecure.secp256k1.key.infos.json").unwrap();
serde_json::from_slice(&key_file.data).unwrap()
};
}
#[test]
fn test_keys() {
let _ = env_logger::builder()
.filter_level(log::LevelFilter::Info)
.is_test(true)
.try_init();
for k in TEST_KEYS.iter() {
log::info!(
"[KEY] test key eth address {:?}",
k.to_public_key().to_eth_address()
);
}
for ki in TEST_INFOS.iter() {
log::info!("[INFO] test key eth address {:?}", ki.eth_address);
}
assert_eq!(TEST_KEYS.len(), TEST_INFOS.len());
log::info!("total {} test keys are found", TEST_KEYS.len());
}
#[serde_as]
#[derive(Debug, Serialize, Deserialize, Eq, PartialEq, Clone)]
#[serde(rename_all = "snake_case")]
pub struct Info {
#[serde(skip_serializing_if = "Option::is_none")]
pub id: Option<String>,
#[serde_as(as = "DisplayFromStr")]
pub key_type: KeyType,
#[serde(skip_serializing_if = "Option::is_none")]
pub mnemonic_phrase: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub private_key_cb58: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub private_key_hex: Option<String>,
#[serde(default)]
pub addresses: HashMap<u32, ChainAddresses>,
#[serde(default)]
pub short_address: short::Id,
#[serde(default)]
pub eth_address: String,
#[serde_as(as = "Hex0xH160")]
pub h160_address: primitive_types::H160,
}
impl Default for Info {
fn default() -> Self {
Info {
id: None,
key_type: KeyType::Unknown(String::new()),
mnemonic_phrase: None,
private_key_cb58: None,
private_key_hex: None,
addresses: HashMap::new(),
short_address: short::Id::empty(),
eth_address: String::new(),
h160_address: primitive_types::H160::zero(),
}
}
}
impl From<&crate::key::secp256k1::private_key::Key> for Info {
fn from(sk: &crate::key::secp256k1::private_key::Key) -> Self {
sk.to_info(1).unwrap()
}
}
impl Info {
pub fn load(file_path: &str) -> Result<Self> {
log::info!("loading Info from {}", file_path);
if !Path::new(file_path).exists() {
return Err(Error::Other {
message: format!("file {} does not exists", file_path),
retryable: false,
});
}
let f = File::open(file_path).map_err(|e| Error::Other {
message: format!("failed to open {} ({})", file_path, e),
retryable: false,
})?;
serde_yaml::from_reader(f).map_err(|e| Error::Other {
message: format!("failed serde_yaml::from_reader {}", e),
retryable: false,
})
}
pub fn sync(&self, file_path: String) -> std::io::Result<()> {
log::info!("syncing key info to '{}'", file_path);
let path = Path::new(&file_path);
let parent_dir = path.parent().unwrap();
fs::create_dir_all(parent_dir)?;
let d = serde_json::to_vec(self).map_err(|e| {
std::io::Error::new(
std::io::ErrorKind::Other,
format!("failed to serialize JSON {}", e),
)
})?;
let mut f = File::create(&file_path)?;
f.write_all(&d)?;
Ok(())
}
pub fn to_private_key(&self) -> crate::key::secp256k1::private_key::Key {
crate::key::secp256k1::private_key::Key::from_cb58(self.private_key_cb58.clone().unwrap())
.unwrap()
}
}
impl fmt::Display for Info {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let s = serde_yaml::to_string(&self).unwrap();
write!(f, "{}", s)
}
}
#[derive(
Deserialize,
Serialize,
std::clone::Clone,
std::cmp::Eq,
std::cmp::Ord,
std::cmp::PartialEq,
std::cmp::PartialOrd,
std::fmt::Debug,
std::hash::Hash,
)]
pub enum KeyType {
#[serde(rename = "hot")]
Hot,
#[serde(rename = "aws-kms")]
AwsKms,
Unknown(String),
}
impl std::convert::From<&str> for KeyType {
fn from(s: &str) -> Self {
match s {
"hot" => KeyType::Hot,
"aws-kms" => KeyType::AwsKms,
"aws_kms" => KeyType::AwsKms,
other => KeyType::Unknown(other.to_owned()),
}
}
}
impl std::str::FromStr for KeyType {
type Err = std::convert::Infallible;
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
Ok(KeyType::from(s))
}
}
impl std::fmt::Display for KeyType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.as_str())
}
}
impl KeyType {
pub fn as_str(&self) -> &str {
match self {
KeyType::Hot => "hot",
KeyType::AwsKms => "aws-kms",
KeyType::Unknown(s) => s.as_ref(),
}
}
pub fn values() -> &'static [&'static str] {
&[
"hot", "aws-kms", ]
}
}
impl AsRef<str> for KeyType {
fn as_ref(&self) -> &str {
self.as_str()
}
}
#[derive(Debug, Serialize, Deserialize, Eq, PartialEq, Clone)]
#[serde(rename_all = "snake_case")]
pub struct ChainAddresses {
pub x: String,
pub p: String,
}
#[test]
fn test_keys_address() {
let _ = env_logger::builder()
.filter_level(log::LevelFilter::Info)
.is_test(true)
.try_init();
#[derive(RustEmbed)]
#[folder = "artifacts/"]
#[prefix = "artifacts/"]
struct Asset;
for asset in ["artifacts/test.insecure.secp256k1.key.infos.json"] {
let key_file = Asset::get(asset).unwrap();
let key_contents = std::str::from_utf8(key_file.data.as_ref()).unwrap();
let key_infos: Vec<Info> = serde_json::from_slice(key_contents.as_bytes()).unwrap();
log::info!("loaded {}", asset);
for (pos, ki) in key_infos.iter().enumerate() {
log::info!("checking the key info at {}", pos);
let sk = crate::key::secp256k1::private_key::Key::from_cb58(
&ki.private_key_cb58.clone().unwrap(),
)
.unwrap();
assert_eq!(
sk,
crate::key::secp256k1::private_key::Key::from_hex(
ki.private_key_hex.clone().unwrap()
)
.unwrap(),
);
let pubkey = sk.to_public_key();
assert_eq!(
pubkey.to_hrp_address(1, "X").unwrap(),
ki.addresses.get(&1).unwrap().x
);
assert_eq!(
pubkey.to_hrp_address(1, "P").unwrap(),
ki.addresses.get(&1).unwrap().p
);
assert_eq!(
pubkey.to_hrp_address(9999, "X").unwrap(),
ki.addresses.get(&9999).unwrap().x
);
assert_eq!(
pubkey.to_hrp_address(9999, "P").unwrap(),
ki.addresses.get(&9999).unwrap().p
);
assert_eq!(pubkey.to_short_id().unwrap(), ki.short_address);
assert_eq!(pubkey.to_eth_address(), ki.eth_address);
}
}
}