use crate::account::{Address, Encryptor, PrivateKeyCiphertext, Signature, ViewKey};
use crate::types::native::{CurrentNetwork, Environment, FromBytes, PrimeField, PrivateKeyNative, ToBytes};
use core::{convert::TryInto, fmt, ops::Deref, str::FromStr};
use rand::{rngs::StdRng, SeedableRng};
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct PrivateKey(PrivateKeyNative);
#[wasm_bindgen]
impl PrivateKey {
#[wasm_bindgen(constructor)]
#[allow(clippy::new_without_default)]
pub fn new() -> Self {
Self(PrivateKeyNative::new(&mut StdRng::from_entropy()).unwrap())
}
pub fn from_seed_unchecked(seed: &[u8]) -> PrivateKey {
let seed: [u8; 32] = seed.try_into().unwrap();
let field = <CurrentNetwork as Environment>::Field::from_bytes_le_mod_order(&seed);
Self(PrivateKeyNative::try_from(FromBytes::read_le(&*field.to_bytes_le().unwrap()).unwrap()).unwrap())
}
pub fn from_string(private_key: &str) -> Result<PrivateKey, String> {
Self::from_str(private_key).map_err(|_| "Invalid private key".to_string())
}
#[allow(clippy::inherent_to_string_shadow_display)]
pub fn to_string(&self) -> String {
self.0.to_string()
}
pub fn to_view_key(&self) -> ViewKey {
ViewKey::from_private_key(self)
}
pub fn to_address(&self) -> Address {
Address::from_private_key(self)
}
pub fn sign(&self, message: &[u8]) -> Signature {
Signature::sign(self, message)
}
#[wasm_bindgen(js_name = newEncrypted)]
pub fn new_encrypted(secret: &str) -> Result<PrivateKeyCiphertext, String> {
let key = Self::new();
let ciphertext =
Encryptor::encrypt_private_key_with_secret(&key, secret).map_err(|_| "Encryption failed".to_string())?;
Ok(PrivateKeyCiphertext::from(ciphertext))
}
#[wasm_bindgen(js_name = toCiphertext)]
pub fn to_ciphertext(&self, secret: &str) -> Result<PrivateKeyCiphertext, String> {
let ciphertext =
Encryptor::encrypt_private_key_with_secret(self, secret).map_err(|_| "Encryption failed".to_string())?;
Ok(PrivateKeyCiphertext::from(ciphertext))
}
#[wasm_bindgen(js_name = fromPrivateKeyCiphertext)]
pub fn from_private_key_ciphertext(ciphertext: &PrivateKeyCiphertext, secret: &str) -> Result<PrivateKey, String> {
let private_key = Encryptor::decrypt_private_key_with_secret(ciphertext, secret)
.map_err(|_| "Decryption failed".to_string())?;
Ok(Self::from(private_key))
}
}
impl From<PrivateKeyNative> for PrivateKey {
fn from(private_key: PrivateKeyNative) -> Self {
Self(private_key)
}
}
impl From<PrivateKey> for PrivateKeyNative {
fn from(private_key: PrivateKey) -> Self {
private_key.0
}
}
impl From<&PrivateKey> for PrivateKeyNative {
fn from(private_key: &PrivateKey) -> Self {
private_key.0
}
}
impl FromStr for PrivateKey {
type Err = anyhow::Error;
fn from_str(private_key: &str) -> Result<Self, Self::Err> {
Ok(Self(PrivateKeyNative::from_str(private_key)?))
}
}
impl fmt::Display for PrivateKey {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.0)
}
}
impl Deref for PrivateKey {
type Target = PrivateKeyNative;
fn deref(&self) -> &Self::Target {
&self.0
}
}
#[cfg(test)]
mod tests {
use super::*;
use rand::Rng;
use wasm_bindgen_test::*;
const ITERATIONS: u64 = 1_000;
const ALEO_PRIVATE_KEY: &str = "APrivateKey1zkp3dQx4WASWYQVWKkq14v3RoQDfY2kbLssUj7iifi1VUQ6";
const ALEO_VIEW_KEY: &str = "AViewKey1cxguxtKkjYnT9XDza9yTvVMxt6Ckb1Pv4ck1hppMzmCB";
const ALEO_ADDRESS: &str = "aleo184vuwr5u7u0ha5f5k44067dd2uaqewxx6pe5ltha5pv99wvhfqxqv339h4";
#[wasm_bindgen_test]
pub fn test_sanity_check() {
let private_key = PrivateKey::from_string(ALEO_PRIVATE_KEY).unwrap();
println!("{} == {}", ALEO_PRIVATE_KEY, private_key.to_string());
assert_eq!(ALEO_PRIVATE_KEY, private_key.to_string());
println!("{} == {}", ALEO_VIEW_KEY, private_key.to_view_key());
assert_eq!(ALEO_VIEW_KEY, private_key.to_view_key().to_string());
println!("{} == {}", ALEO_ADDRESS, private_key.to_address());
assert_eq!(ALEO_ADDRESS, private_key.to_address().to_string());
}
#[wasm_bindgen_test]
pub fn test_new() {
for _ in 0..ITERATIONS {
let expected = PrivateKey::new();
assert_eq!(expected, PrivateKey::from_string(&expected.to_string()).unwrap());
}
}
#[wasm_bindgen_test]
pub fn test_from_seed_unchecked() {
for _ in 0..ITERATIONS {
let seed: [u8; 32] = StdRng::from_entropy().gen();
let expected = PrivateKey::from_seed_unchecked(&seed);
assert_eq!(expected, PrivateKey::from_seed_unchecked(&seed));
}
}
#[wasm_bindgen_test]
pub fn test_to_address() {
for _ in 0..ITERATIONS {
let private_key = PrivateKey::new();
let expected = private_key.to_address();
let view_key = private_key.to_view_key();
assert_eq!(expected, Address::from_view_key(&view_key));
}
}
#[wasm_bindgen_test]
pub fn test_signature() {
for _ in 0..ITERATIONS {
let private_key = PrivateKey::new();
let message: [u8; 32] = StdRng::from_entropy().gen();
let signature = private_key.sign(&message);
assert!(signature.verify(&private_key.to_address(), &message));
assert!(signature.verify_bytes(&private_key.to_address(), &message));
}
}
}