use crate::types::{Pubkey, Signature, SdkError, Result};
pub struct Keypair {
secret: ed25519_compact::SecretKey,
public: ed25519_compact::PublicKey,
}
impl Keypair {
pub fn from_seed(seed: &[u8; 32]) -> Result<Self> {
let kp = ed25519_compact::KeyPair::from_seed(
ed25519_compact::Seed::new(*seed),
);
Ok(Self {
secret: kp.sk,
public: kp.pk,
})
}
pub fn from_bytes(bytes: &[u8; 64]) -> Result<Self> {
let mut seed = [0u8; 32];
seed.copy_from_slice(&bytes[..32]);
let kp = ed25519_compact::KeyPair::from_seed(
ed25519_compact::Seed::new(seed),
);
let expected_pk: &[u8] = &bytes[32..64];
if kp.pk.as_ref() != expected_pk {
return Err(SdkError::Crypto);
}
Ok(Self {
secret: kp.sk,
public: kp.pk,
})
}
pub fn pubkey(&self) -> Pubkey {
let mut bytes = [0u8; 32];
bytes.copy_from_slice(self.public.as_ref());
Pubkey(bytes)
}
pub fn sign(&self, message: &[u8]) -> Signature {
let sig = self.secret.sign(message, None);
let mut out = [0u8; 64];
out.copy_from_slice(sig.as_ref());
Signature(out)
}
}
pub fn verify(pubkey: &Pubkey, message: &[u8], signature: &Signature) -> bool {
let pk = match ed25519_compact::PublicKey::from_slice(&pubkey.0) {
Ok(pk) => pk,
Err(_) => return false,
};
let sig = match ed25519_compact::Signature::from_slice(&signature.0) {
Ok(s) => s,
Err(_) => return false,
};
pk.verify(message, &sig).is_ok()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn keypair_from_seed_deterministic() {
let seed = [42u8; 32];
let kp1 = Keypair::from_seed(&seed).unwrap();
let kp2 = Keypair::from_seed(&seed).unwrap();
assert_eq!(kp1.pubkey(), kp2.pubkey());
}
#[test]
fn sign_and_verify() {
let seed = [7u8; 32];
let kp = Keypair::from_seed(&seed).unwrap();
let msg = b"hello solana";
let sig = kp.sign(msg);
assert!(verify(&kp.pubkey(), msg, &sig));
}
#[test]
fn verify_wrong_message() {
let seed = [7u8; 32];
let kp = Keypair::from_seed(&seed).unwrap();
let sig = kp.sign(b"hello");
assert!(!verify(&kp.pubkey(), b"world", &sig));
}
#[test]
fn from_bytes_roundtrip() {
let seed = [99u8; 32];
let kp = Keypair::from_seed(&seed).unwrap();
let mut bytes = [0u8; 64];
bytes[..32].copy_from_slice(&seed);
bytes[32..].copy_from_slice(kp.pubkey().as_bytes());
let kp2 = Keypair::from_bytes(&bytes).unwrap();
assert_eq!(kp.pubkey(), kp2.pubkey());
}
#[test]
fn from_bytes_mismatch() {
let mut bytes = [0u8; 64];
bytes[..32].copy_from_slice(&[1u8; 32]);
bytes[32..].copy_from_slice(&[2u8; 32]); assert!(Keypair::from_bytes(&bytes).is_err());
}
}