use crate::error::{GadgetIoError, Result};
pub use crate::shared::keystore::SubstrateKeystore;
use sp_core::{ecdsa, sr25519, ByteArray};
use std::path::{Path, PathBuf};
use tracing;
use sc_keystore::{Keystore, LocalKeystore};
use sp_keystore::KeystorePtr;
use std::sync::Arc;
pub struct KeystoreContainer(Arc<LocalKeystore>);
impl KeystoreContainer {
pub fn new(config: &KeystoreConfig) -> Result<Self> {
let keystore = Arc::new(match config {
KeystoreConfig::Path { path, password } => {
LocalKeystore::open(path.clone(), password.clone())
.map_err(|e| GadgetIoError::Other(e.to_string()))?
}
KeystoreConfig::InMemory => LocalKeystore::in_memory(),
});
Ok(Self(keystore))
}
#[allow(dead_code)]
pub fn keystore(&self) -> KeystorePtr {
self.0.clone()
}
pub fn local_keystore(&self) -> Arc<LocalKeystore> {
self.0.clone()
}
}
#[derive(Debug, Clone)]
#[allow(dead_code)]
pub enum KeystoreConfig {
Path {
path: PathBuf,
password: Option<sp_core::crypto::SecretString>,
},
InMemory,
}
impl KeystoreConfig {
#[allow(dead_code)]
pub fn path(&self) -> Option<&Path> {
match self {
Self::Path { path, .. } => Some(path),
Self::InMemory => None,
}
}
}
pub mod crypto {
use sp_application_crypto::{app_crypto, ecdsa, sr25519};
pub mod acco {
use super::*;
pub use sp_core::crypto::key_types::ACCOUNT as KEY_TYPE;
app_crypto!(sr25519, KEY_TYPE);
}
pub mod role {
use super::*;
pub const KEY_TYPE: sp_application_crypto::KeyTypeId =
sp_application_crypto::KeyTypeId(*b"role");
app_crypto!(ecdsa, KEY_TYPE);
}
}
impl SubstrateKeystore for KeystoreContainer {
fn ecdsa_key(&self) -> Result<ecdsa::Pair> {
let keystore_container = self;
let keystore = keystore_container.local_keystore();
tracing::debug!("Loaded keystore from path");
let ecdsa_keys = keystore.ecdsa_public_keys(crypto::acco::KEY_TYPE);
if ecdsa_keys.len() != 1 {
return Err(GadgetIoError::Other(format!(
"`acco`: Expected exactly one key in ECDSA keystore, found {}",
ecdsa_keys.len()
)));
}
let role_public_key = crypto::role::Public::from_slice(&ecdsa_keys[0].0).map_err(|_| {
GadgetIoError::Other(String::from("Failed to parse public key from keystore"))
})?;
let role_key = keystore
.key_pair::<crypto::role::Pair>(&role_public_key)
.map_err(|e| GadgetIoError::Other(e.to_string()))?
.ok_or(GadgetIoError::Other(String::from(
"Failed to load key `role` from keystore",
)))?
.into_inner();
tracing::debug!(%role_public_key, "Loaded key from keystore");
println!("ECDSA KEY: {:?}", role_public_key.to_string());
println!("ECDSA SEED: {:?}", role_key.seed());
println!("ECDSA RAW: {:?}", ecdsa_keys);
Ok(role_key)
}
fn sr25519_key(&self) -> Result<sr25519::Pair> {
let keystore_container = self;
let keystore = keystore_container.local_keystore();
tracing::debug!("Loaded keystore from path");
let sr25519_keys = keystore.sr25519_public_keys(crypto::acco::KEY_TYPE);
if sr25519_keys.len() != 1 {
return Err(GadgetIoError::Other(format!(
"`acco`: Expected exactly one key in SR25519 keystore, found {}",
sr25519_keys.len()
)));
}
let account_public_key =
crypto::acco::Public::from_slice(&sr25519_keys[0].0).map_err(|_| {
GadgetIoError::Other(String::from("Failed to parse public key from keystore"))
})?;
let acco_key = keystore
.key_pair::<crypto::acco::Pair>(&account_public_key)
.map_err(|e| GadgetIoError::Other(e.to_string()))?
.ok_or(GadgetIoError::Other(String::from(
"Failed to load key `acco` from keystore",
)))?
.into_inner();
tracing::debug!(%account_public_key, "Loaded key from keystore");
println!("SR25519 KEY: {:?}", account_public_key.to_string());
println!("SR25519 SEED: {:?}", sr25519_keys[0].0);
println!("SR25519 RAW: {:?}", sr25519_keys);
Ok(acco_key)
}
}