use super::{PrivateKey, PublicKey};
use crate::{
address::SubstrateAddress,
keyring::{KeyCodec, Keyring as GenericKeyring, SubstrateKeystore},
};
use anyhow::{Result, anyhow};
use schnorrkel::KEYPAIR_LENGTH;
use serde::{Deserialize, Serialize};
#[derive(Clone, Copy, Debug, Default, Serialize, Deserialize)]
pub struct Sr25519Codec;
impl KeyCodec for Sr25519Codec {
type Pair = sp_core::sr25519::Pair;
type PrivateKey = PrivateKey;
type PublicKey = PublicKey;
type Address = SubstrateAddress;
const KEY_TYPE: &'static str = "sr25519";
fn derive_public(private_key: &Self::PrivateKey) -> Self::PublicKey {
crate::keyring::codec_defaults::derive_public(private_key)
}
fn derive_address(public_key: &Self::PublicKey) -> Result<Self::Address> {
public_key
.to_address()
.map_err(|err| anyhow!("Failed to derive address: {err}"))
}
fn encode_private(private_key: &Self::PrivateKey) -> Result<String> {
Ok(hex::encode(private_key.to_bytes()))
}
fn decode_private(encoded: &str) -> Result<Self::PrivateKey> {
let bytes = hex::decode(encoded)?;
if bytes.len() == 32 {
let mut seed = [0u8; 32];
seed.copy_from_slice(&bytes);
PrivateKey::from_seed(seed).map_err(|err| anyhow!("Invalid sr25519 seed: {err}"))
} else if bytes.len() == KEYPAIR_LENGTH {
let keypair = schnorrkel::Keypair::from_half_ed25519_bytes(&bytes)
.map_err(|e| anyhow!("Invalid sr25519 keypair: {:?}", e))?;
Ok(PrivateKey::from_keypair(keypair))
} else {
Err(anyhow!(
"Invalid sr25519 private key length: expected 32 or 96, got {}",
bytes.len()
))
}
}
fn encode_public(public_key: &Self::PublicKey) -> Result<String> {
crate::keyring::codec_defaults::encode_public(public_key)
}
fn decode_public(encoded: &str) -> Result<Self::PublicKey> {
crate::keyring::codec_defaults::decode_public(encoded)
}
fn encode_address(address: &Self::Address) -> Result<String> {
crate::keyring::codec_defaults::encode_ss58_address(address)
}
fn decode_address(encoded: &str) -> Result<Self::Address> {
crate::keyring::codec_defaults::decode_ss58_address(encoded)
}
fn random_private() -> Result<Self::PrivateKey> {
crate::keyring::codec_defaults::random_private()
}
fn import_suri(suri: &str, password: Option<&str>) -> Result<Self::PrivateKey> {
PrivateKey::from_suri(suri, password).map_err(|err| anyhow!("Invalid SURI: {err}"))
}
}
pub type Keystore = SubstrateKeystore<Sr25519Codec>;
pub type Keyring = GenericKeyring<Keystore>;
#[cfg(test)]
mod tests {
use super::*;
use crate::keyring::KeystoreEntry;
#[test]
fn create_and_restore_private_key() {
let temp_dir = tempfile::tempdir().unwrap();
let mut keyring = Keyring::load(temp_dir.path().to_path_buf()).unwrap();
let (keystore, private_key) = keyring.create("alice", None).unwrap();
assert_eq!(keyring.list().len(), 1);
assert_eq!(keyring.list()[0].name(), "alice");
assert_eq!(keystore.private_key().unwrap(), private_key);
}
}