use crate::crypto::substrate::Address;
use crate::{crypto::RawKeyPair, errors::Result, KeyIdentifier, KeyType, Signature};
use bip39::{Mnemonic, MnemonicType};
use rand::{CryptoRng, Rng, RngCore};
use secrecy::{ExposeSecret, Secret};
use sp_core::{crypto::Ss58Codec, ed25519, DeriveJunction, Pair};
use std::fmt::{Debug, Formatter};
pub(crate) type Public = sp_core::ed25519::Public;
impl Signature for ed25519::Signature {}
#[derive(Clone, Constructor)]
pub struct SubstrateEd25519KeyPair {
pub entropy: Secret<[u8; 16]>,
pub derive_paths: Vec<Secret<String>>,
pub(crate) key: ed25519::Pair,
}
impl Debug for SubstrateEd25519KeyPair {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "Ed25519({})", &self.key.public())
}
}
fn make_ed25519_pair(
entropy: &Secret<[u8; 16]>,
derive_paths: &[Secret<String>],
) -> Result<ed25519::Pair> {
let seed = substrate_bip39::seed_from_entropy(&entropy.expose_secret()[..], "")?;
let derivations = derive_paths
.iter()
.map(|path| DeriveJunction::from(path.expose_secret()));
let pair = ed25519::Pair::from_seed_slice(&seed[0..32])?;
let (pair, _seed) = pair.derive(derivations, None)?;
Ok(pair)
}
impl SubstrateEd25519KeyPair {
fn get_ed25519_pair(&self) -> Result<ed25519::Pair> {
make_ed25519_pair(&self.entropy, &self.derive_paths)
}
pub fn to_address(&self) -> Address {
Address {
value: self.key.public().to_ss58check(),
key_type: KeyType::SubstrateEd25519,
}
}
}
impl RawKeyPair for SubstrateEd25519KeyPair {
type PublicKey = Public;
type Signature = ed25519::Signature;
const KEY_TYPE: KeyType = KeyType::SubstrateEd25519;
fn from_strings(secret: Secret<String>) -> Result<Self> {
let (entropy, derive_paths) = super::deconstruct_secret_string(secret)?;
let pair = make_ed25519_pair(&entropy, &derive_paths)?;
Ok(Self::new(entropy, derive_paths, pair))
}
fn from_secret(secret: Secret<String>) -> Result<Self> {
let (entropy, derive_paths) = super::deconstruct_secret_string(secret)?;
let pair = make_ed25519_pair(&entropy, &derive_paths)?;
Ok(Self::new(entropy, derive_paths, pair))
}
fn generate_with<R: RngCore + CryptoRng>(rng: &mut R) -> Result<Self> {
let mut entropy = vec![0; MnemonicType::Words12.entropy_bits() / 8];
rng.fill(&mut *entropy);
let mnemonic = Mnemonic::from_entropy(&entropy, bip39::Language::English)
.expect("Mnemonic length is valid by construction");
let phrase = mnemonic.phrase();
let (key, _) = ed25519::Pair::from_phrase(phrase, None)
.expect("All phrases generated by Mnemonic are valid");
let secret_phrase = phrase.to_owned();
let phrase = Secret::new(secret_phrase);
let (entropy, derive_paths) = super::deconstruct_secret_string(phrase)?;
Ok(Self::new(entropy, derive_paths, key))
}
fn sign(&self, message: &[u8]) -> Result<Self::Signature> {
let pair = self.get_ed25519_pair()?;
Ok(pair.sign(message))
}
fn identifier(&self) -> KeyIdentifier {
KeyIdentifier {
value: self.public().to_ss58check(),
key_type: Self::KEY_TYPE,
}
}
fn to_secret(&self) -> Result<Secret<String>> {
super::to_secret_string(&self.entropy, &self.derive_paths)
}
fn public(&self) -> Self::PublicKey {
self.key.public()
}
}