use alloc::string::{String, ToString};
use alloc::sync::Arc;
use alloc::vec::Vec;
use core::str::FromStr;
use keetanetwork_account::account::AccountSigner;
use keetanetwork_account::{Account as CoreAccount, GenericAccount, KeyED25519, Keyable};
use keetanetwork_block::{AccountRef, BlockHash};
use keetanetwork_crypto::prelude::{ExposeSecret, IntoSecret};
use wasm_bindgen::prelude::wasm_bindgen;
use crate::convert::{coded, coded_error, parse_identifier_type, JsResult};
#[wasm_bindgen]
#[derive(Clone)]
pub struct Account {
inner: AccountRef,
}
#[wasm_bindgen]
impl Account {
#[wasm_bindgen(js_name = generateSeed)]
pub fn generate_seed() -> JsResult<String> {
let seed =
CoreAccount::<KeyED25519>::generate_random_seed().map_err(|error| coded_error("RNG", error.as_ref()))?;
Ok(hex::encode(seed.expose_secret()))
}
#[wasm_bindgen(js_name = generatePassphrase)]
pub fn generate_passphrase() -> JsResult<Vec<String>> {
let passphrase =
CoreAccount::<KeyED25519>::generate_passphrase().map_err(|error| coded_error("RNG", error.as_ref()))?;
Ok(passphrase.expose_secret().clone())
}
#[wasm_bindgen(js_name = fromSeed)]
pub fn from_seed(seed: String, index: u32, algorithm: Option<String>) -> JsResult<Account> {
let mut bytes = [0u8; 32];
hex::decode_to_slice(&seed, &mut bytes).map_err(|_| coded_error("INVALID_SEED", "seed must be 32-byte hex"))?;
let algorithm = algorithm.as_deref().unwrap_or("ecdsa_secp256k1");
Self::from_keyable(Keyable::Seed((bytes.into_secret(), index)), algorithm)
}
#[wasm_bindgen(js_name = fromPrivateKey)]
pub fn from_private_key(key: String, algorithm: String) -> JsResult<Account> {
let bytes = hex::decode(&key).map_err(|_| coded_error("INVALID_PRIVATE_KEY", "private key must be hex"))?;
Self::from_keyable(Keyable::PrivateKey(bytes), &algorithm)
}
#[wasm_bindgen(js_name = fromPassphrase)]
pub fn from_passphrase(words: Vec<String>, index: u32, algorithm: String) -> JsResult<Account> {
Self::from_keyable(Keyable::from((words, index)), &algorithm)
}
#[wasm_bindgen(js_name = fromPublicKey)]
pub fn from_public_key(key: String, algorithm: String) -> JsResult<Account> {
let bytes = hex::decode(&key).map_err(|_| coded_error("INVALID_PUBLIC_KEY", "public key must be hex"))?;
Self::from_keyable(Keyable::PublicKey(bytes), &algorithm)
}
#[wasm_bindgen(js_name = fromAddress)]
pub fn from_address(address: String) -> JsResult<Account> {
let account = GenericAccount::from_str(&address)
.map_err(|_| coded_error("INVALID_ADDRESS", "invalid account address"))?;
Ok(Self { inner: Arc::new(account) })
}
#[wasm_bindgen(getter)]
pub fn address(&self) -> String {
self.inner.to_string()
}
#[wasm_bindgen(getter)]
pub fn algorithm(&self) -> String {
String::from(keetanetwork_bindings::account::algorithm_name(self.inner.to_keypair_type()))
}
#[wasm_bindgen(getter, js_name = publicKey)]
pub fn public_key(&self) -> String {
hex::encode(self.inner.to_public_key_with_type())
}
#[wasm_bindgen(js_name = generateIdentifier)]
pub fn generate_identifier(
&self,
kind: String,
previous: Option<String>,
op_index: Option<u32>,
) -> JsResult<Account> {
let kind = parse_identifier_type(&kind)?;
let previous = previous
.map(|hash| {
BlockHash::from_str(&hash).map_err(|_| coded_error("INVALID_BLOCK_HASH", "block hash must be hex"))
})
.transpose()?;
let identifier = self
.inner
.generate_identifier(kind, previous.as_ref(), op_index.unwrap_or(0))
.map_err(|error| coded_error("IDENTIFIER", error.as_ref()))?;
Ok(Self { inner: Arc::new(identifier) })
}
pub fn sign(&self, message: Vec<u8>) -> JsResult<Vec<u8>> {
AccountSigner::sign(self.inner.as_ref(), message, None).map_err(|error| coded_error("SIGN", error.as_ref()))
}
pub fn verify(&self, message: Vec<u8>, signature: Vec<u8>) -> bool {
self.inner.verify(message, signature, None).is_ok()
}
pub fn encrypt(&self, plaintext: Vec<u8>) -> JsResult<Vec<u8>> {
self.inner
.encrypt(plaintext)
.map_err(|error| coded_error("ENCRYPT", error.as_ref()))
}
pub fn decrypt(&self, ciphertext: Vec<u8>) -> JsResult<Vec<u8>> {
self.inner
.decrypt(ciphertext)
.map_err(|error| coded_error("DECRYPT", error.as_ref()))
}
}
impl Account {
pub(crate) fn inner(&self) -> AccountRef {
Arc::clone(&self.inner)
}
fn from_keyable(keyable: Keyable, algorithm: &str) -> JsResult<Account> {
let account = keetanetwork_bindings::account::from_keyable(keyable, algorithm).map_err(coded)?;
Ok(Self { inner: Arc::new(account) })
}
}
impl From<AccountRef> for Account {
fn from(inner: AccountRef) -> Self {
Self { inner }
}
}