use crate::errors::{SDKError, SDKResult};
#[derive(Clone)]
pub struct Keypair {
signing_key: ed25519_dalek::SigningKey,
}
impl Keypair {
pub fn from_bytes(secret_key: [u8; 32]) -> Self {
let signing_key = ed25519_dalek::SigningKey::from_bytes(&secret_key);
Self { signing_key }
}
pub fn from_hex(hex: &str) -> SDKResult<Self> {
let hex = hex.strip_prefix("0x").unwrap_or(hex);
let bytes: [u8; 32] = hex::decode(hex)
.map_err(|e| SDKError::InvalidPrivateKey(e.to_string()))?
.try_into()
.map_err(|_| SDKError::InvalidPrivateKey("Expected 32 bytes".into()))?;
Ok(Self::from_bytes(bytes))
}
pub fn generate() -> Self {
use ed25519_dalek::SigningKey;
use rand::rngs::OsRng;
let signing_key = SigningKey::generate(&mut OsRng);
Self { signing_key }
}
pub fn sign(&self, message: &[u8]) -> Vec<u8> {
use ed25519_dalek::Signer;
let signature = self.signing_key.sign(message);
signature.to_bytes().to_vec()
}
pub fn public_key(&self) -> Vec<u8> {
self.signing_key.verifying_key().as_bytes().to_vec()
}
pub fn address(&self) -> String {
let pk_bytes: [u8; 32] = self.signing_key.verifying_key().to_bytes();
bullet_exchange_interface::address::Address(pk_bytes).to_string()
}
pub fn address_hex(&self) -> String {
hex::encode(self.public_key())
}
pub fn write_to_file(&self, path: impl AsRef<std::path::Path>) -> std::io::Result<()> {
let secret = self.signing_key.to_bytes();
let public = self.signing_key.verifying_key().to_bytes();
let mut bytes = [0u8; 64];
bytes[..32].copy_from_slice(&secret);
bytes[32..].copy_from_slice(&public);
let json = serde_json::to_string(&bytes.as_slice())
.map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidData, e))?;
std::fs::write(path, json)
}
pub fn read_from_file(path: impl AsRef<std::path::Path>) -> std::io::Result<Self> {
let path = path.as_ref();
let data = std::fs::read_to_string(path)?;
let bytes: Vec<u8> = serde_json::from_str(&data)
.map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidData, e))?;
if bytes.len() < 32 {
return Err(std::io::Error::new(
std::io::ErrorKind::InvalidData,
format!("keystore too short: {} bytes (need ≥32)", bytes.len()),
));
}
let mut secret = [0u8; 32];
secret.copy_from_slice(&bytes[..32]);
Ok(Self::from_bytes(secret))
}
}
impl std::fmt::Debug for Keypair {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Keypair").field("address", &self.address()).finish_non_exhaustive()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_generate_and_sign() {
let keypair = Keypair::generate();
let message = b"test message";
let signature = keypair.sign(message);
assert_eq!(signature.len(), 64); }
#[test]
fn test_from_hex() {
let hex = "0000000000000000000000000000000000000000000000000000000000000001";
let keypair = Keypair::from_hex(hex).unwrap();
assert_eq!(keypair.public_key().len(), 32);
}
#[test]
fn test_from_hex_with_prefix() {
let hex = "0x0000000000000000000000000000000000000000000000000000000000000001";
let keypair = Keypair::from_hex(hex).unwrap();
assert_eq!(keypair.public_key().len(), 32);
}
}