Skip to content
This repository has been archived by the owner on Nov 15, 2023. It is now read-only.

Export app-crypto specific keystore functions #7489

Merged
merged 12 commits into from
Nov 11, 2020
94 changes: 45 additions & 49 deletions client/keystore/src/local.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ use sp_keystore::{
SyncCryptoStore,
vrf::{VRFTranscriptData, VRFSignature, make_transcript},
};
use sp_application_crypto::{ed25519, sr25519, ecdsa};
use sp_application_crypto::{ed25519, sr25519, ecdsa, AppPair, AppKey, IsWrappedBy};

use crate::{Result, Error};

Expand All @@ -57,6 +57,14 @@ impl LocalKeystore {
let inner = KeystoreInner::new_in_memory();
Self(RwLock::new(inner))
}

/// Get a key pair for the given public key.
///
/// This function is only available for a local keystore. If your application plans to work with
/// remote keystores, you do not want to depend on it.
pub fn key_pair<Pair: AppPair>(&self, public: &<Pair as AppKey>::Public) -> Result<Pair> {
self.0.read().key_pair::<Pair>(public)
}
}

#[async_trait]
Expand Down Expand Up @@ -470,6 +478,11 @@ impl KeystoreInner {

Ok(public_keys)
}

/// Get a key pair for the given public key.
pub fn key_pair<Pair: AppPair>(&self, public: &<Pair as AppKey>::Public) -> Result<Pair> {
self.key_pair_by_type::<Pair::Generic>(IsWrappedBy::from_ref(public), Pair::ID).map(Into::into)
}
}


Expand All @@ -479,71 +492,55 @@ mod tests {
use tempfile::TempDir;
use sp_core::{
Pair,
crypto::{IsWrappedBy, Ss58Codec},
crypto::Ss58Codec,
testing::SR25519,
};
use sp_application_crypto::{ed25519, sr25519, AppPublic, AppKey, AppPair};
use sp_application_crypto::{ed25519, sr25519, AppPublic};
use std::{
fs,
str::FromStr,
};

/// Generate a new key.
///
/// Places it into the file system store.
fn generate<Pair: AppPair>(store: &KeystoreInner) -> Result<Pair> {
store.generate_by_type::<Pair::Generic>(Pair::ID).map(Into::into)
}

/// Create a new key from seed.
///
/// Does not place it into the file system store.
fn insert_ephemeral_from_seed<Pair: AppPair>(store: &mut KeystoreInner, seed: &str) -> Result<Pair> {
store.insert_ephemeral_from_seed_by_type::<Pair::Generic>(seed, Pair::ID).map(Into::into)
}
impl KeystoreInner {
fn insert_ephemeral_from_seed<Pair: AppPair>(&mut self, seed: &str) -> Result<Pair> {
self.insert_ephemeral_from_seed_by_type::<Pair::Generic>(seed, Pair::ID).map(Into::into)
}

/// Get public keys of all stored keys that match the key type.
///
/// This will just use the type of the public key (a list of which to be returned) in order
/// to determine the key type. Unless you use a specialized application-type public key, then
/// this only give you keys registered under generic cryptography, and will not return keys
/// registered under the application type.
fn public_keys<Public: AppPublic>(store: &KeystoreInner) -> Result<Vec<Public>> {
store.raw_public_keys(Public::ID)
.map(|v| {
v.into_iter()
.map(|k| Public::from_slice(k.as_slice()))
.collect()
})
}
fn public_keys<Public: AppPublic>(&self) -> Result<Vec<Public>> {
self.raw_public_keys(Public::ID)
.map(|v| {
v.into_iter()
.map(|k| Public::from_slice(k.as_slice()))
.collect()
})
}

/// Get a key pair for the given public key.
fn key_pair<Pair: AppPair>(store: &KeystoreInner, public: &<Pair as AppKey>::Public) -> Result<Pair> {
store.key_pair_by_type::<Pair::Generic>(IsWrappedBy::from_ref(public), Pair::ID).map(Into::into)
fn generate<Pair: AppPair>(&self) -> Result<Pair> {
self.generate_by_type::<Pair::Generic>(Pair::ID).map(Into::into)
}
}

#[test]
fn basic_store() {
let temp_dir = TempDir::new().unwrap();
let store = KeystoreInner::open(temp_dir.path(), None).unwrap();

assert!(public_keys::<ed25519::AppPublic>(&store).unwrap().is_empty());
assert!(store.public_keys::<ed25519::AppPublic>().unwrap().is_empty());

let key: ed25519::AppPair = generate(&store).unwrap();
let key2: ed25519::AppPair = key_pair(&store, &key.public()).unwrap();
let key: ed25519::AppPair = store.generate().unwrap();
let key2: ed25519::AppPair = store.key_pair(&key.public()).unwrap();

assert_eq!(key.public(), key2.public());

assert_eq!(public_keys::<ed25519::AppPublic>(&store).unwrap()[0], key.public());
assert_eq!(store.public_keys::<ed25519::AppPublic>().unwrap()[0], key.public());
}

#[test]
fn test_insert_ephemeral_from_seed() {
let temp_dir = TempDir::new().unwrap();
let mut store = KeystoreInner::open(temp_dir.path(), None).unwrap();

let pair: ed25519::AppPair = insert_ephemeral_from_seed(
&mut store,
let pair: ed25519::AppPair = store.insert_ephemeral_from_seed(
"0x3d97c819d68f9bafa7d6e79cb991eebcd77d966c5334c0b94d9e1fa7ad0869dc"
).unwrap();
assert_eq!(
Expand All @@ -554,7 +551,7 @@ mod tests {
drop(store);
let store = KeystoreInner::open(temp_dir.path(), None).unwrap();
// Keys generated from seed should not be persisted!
assert!(key_pair::<ed25519::AppPair>(&store, &pair.public()).is_err());
assert!(store.key_pair::<ed25519::AppPair>(&pair.public()).is_err());
}

#[test]
Expand All @@ -566,23 +563,23 @@ mod tests {
Some(FromStr::from_str(password.as_str()).unwrap()),
).unwrap();

let pair: ed25519::AppPair = generate(&store).unwrap();
let pair: ed25519::AppPair = store.generate().unwrap();
assert_eq!(
pair.public(),
key_pair::<ed25519::AppPair>(&store, &pair.public()).unwrap().public(),
store.key_pair::<ed25519::AppPair>(&pair.public()).unwrap().public(),
);

// Without the password the key should not be retrievable
let store = KeystoreInner::open(temp_dir.path(), None).unwrap();
assert!(key_pair::<ed25519::AppPair>(&store, &pair.public()).is_err());
assert!(store.key_pair::<ed25519::AppPair>(&pair.public()).is_err());

let store = KeystoreInner::open(
temp_dir.path(),
Some(FromStr::from_str(password.as_str()).unwrap()),
).unwrap();
assert_eq!(
pair.public(),
key_pair::<ed25519::AppPair>(&store, &pair.public()).unwrap().public(),
store.key_pair::<ed25519::AppPair>(&pair.public()).unwrap().public(),
);
}

Expand All @@ -593,18 +590,17 @@ mod tests {

let mut keys = Vec::new();
for i in 0..10 {
keys.push(generate::<ed25519::AppPair>(&store).unwrap().public());
keys.push(insert_ephemeral_from_seed::<ed25519::AppPair>(
&mut store,
keys.push(store.generate::<ed25519::AppPair>().unwrap().public());
keys.push(store.insert_ephemeral_from_seed::<ed25519::AppPair>(
&format!("0x3d97c819d68f9bafa7d6e79cb991eebcd7{}d966c5334c0b94d9e1fa7ad0869dc", i),
).unwrap().public());
}

// Generate a key of a different type
generate::<sr25519::AppPair>(&store).unwrap();
store.generate::<sr25519::AppPair>().unwrap();

keys.sort();
let mut store_pubs = public_keys::<ed25519::AppPublic>(&store).unwrap();
let mut store_pubs = store.public_keys::<ed25519::AppPublic>().unwrap();
store_pubs.sort();

assert_eq!(keys, store_pubs);
Expand Down
37 changes: 26 additions & 11 deletions client/service/src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -205,12 +205,13 @@ pub type TLightClientWithBackend<TBl, TRtApi, TExecDisp, TBackend> = Client<
TRtApi,
>;

/// Construct and hold different layers of Keystore wrappers
pub struct KeystoreContainer {
keystore: Arc<dyn CryptoStore>,
sync_keystore: SyncCryptoStorePtr,
enum KeystoreContainerInner {
bkchr marked this conversation as resolved.
Show resolved Hide resolved
Local(Arc<LocalKeystore>)
}

/// Construct and hold different layers of Keystore wrappers
pub struct KeystoreContainer(KeystoreContainerInner);

impl KeystoreContainer {
/// Construct KeystoreContainer
pub fn new(config: &KeystoreConfig) -> Result<Self, Error> {
Expand All @@ -221,22 +222,36 @@ impl KeystoreContainer {
)?,
KeystoreConfig::InMemory => LocalKeystore::in_memory(),
});
let sync_keystore = keystore.clone() as SyncCryptoStorePtr;

Ok(Self {
keystore,
sync_keystore,
})
Ok(Self(KeystoreContainerInner::Local(keystore)))
}

/// Returns an adapter to the asynchronous keystore that implements `CryptoStore`
pub fn keystore(&self) -> Arc<dyn CryptoStore> {
self.keystore.clone()
match self.0 {
KeystoreContainerInner::Local(ref keystore) => keystore.clone(),
}
}

/// Returns the synchrnous keystore wrapper
pub fn sync_keystore(&self) -> SyncCryptoStorePtr {
self.sync_keystore.clone()
match self.0 {
KeystoreContainerInner::Local(ref keystore) => keystore.clone() as SyncCryptoStorePtr,
}
}

/// Returns the local keystore if available
///
/// The function will return None if the available keystore is not a local keystore.
sorpaas marked this conversation as resolved.
Show resolved Hide resolved
///
/// # Note
///
/// Using the [`LocalKeystore`] will result in loosing the ability to use any other keystore implementation, like
/// a remote keystore for example. Only use this if you a certain that you require it!
pub fn local_keystore(&self) -> Option<Arc<LocalKeystore>> {
match self.0 {
KeystoreContainerInner::Local(ref keystore) => Some(keystore.clone()),
}
}
}

Expand Down