Introduces author_hasKey and author_hasSessionKeys rpc endpoints (#4720)

* Introduces `author_hasKey` and `author_hasSessionKeys` rpc endpoints

Both endpoints can be used to check if a key is present in the keystore.

- `hasKey` works on with an individual public key and key type. It
checks if a private key for the given combination exists in the
keystore.
- `hasSessionKeys` works with the full encoded session key blob stored
on-chain in `nextKeys`. This requires that the given blob can be decoded
by the runtime. It will return `true`, iff all public keys of the
session key exist in the storage.

Fixes: https://github.com/paritytech/substrate/issues/4696

* Update client/rpc-api/src/author/error.rs

Co-Authored-By: Nikolay Volf <nikvolf@gmail.com>

* Indentation

Co-authored-by: Nikolay Volf <nikvolf@gmail.com>
This commit is contained in:
Bastian Köcher
2020-01-24 15:20:45 +01:00
committed by GitHub
parent 1614ce0996
commit fc99887de0
21 changed files with 223 additions and 34 deletions
+16
View File
@@ -112,6 +112,22 @@ where
).map(Into::into).map_err(|e| Error::Client(Box::new(e)))
}
fn has_session_keys(&self, session_keys: Bytes) -> Result<bool> {
let best_block_hash = self.client.chain_info().best_hash;
let keys = self.client.runtime_api().decode_session_keys(
&generic::BlockId::Hash(best_block_hash),
session_keys.to_vec(),
).map_err(|e| Error::Client(Box::new(e)))?
.ok_or_else(|| Error::InvalidSessionKeys)?;
Ok(self.keystore.read().has_keys(&keys))
}
fn has_key(&self, public_key: Bytes, key_type: String) -> Result<bool> {
let key_type = key_type.as_str().try_into().map_err(|_| Error::BadKeyType)?;
Ok(self.keystore.read().has_keys(&[(public_key.to_vec(), key_type)]))
}
fn submit_extrinsic(&self, ext: Bytes) -> FutureResult<TxHash<P>> {
let xt = match Decode::decode(&mut &ext[..]) {
Ok(xt) => xt,
+59 -3
View File
@@ -16,12 +16,12 @@
use super::*;
use std::sync::Arc;
use std::{mem, sync::Arc};
use assert_matches::assert_matches;
use codec::Encode;
use sp_core::{
H256, blake2_256, hexdisplay::HexDisplay, testing::{ED25519, SR25519, KeyStore}, traits::BareCryptoStorePtr, ed25519,
crypto::Pair,
H256, blake2_256, hexdisplay::HexDisplay, testing::{ED25519, SR25519, KeyStore},
traits::BareCryptoStorePtr, ed25519, crypto::{Pair, Public},
};
use rpc::futures::Stream as _;
use substrate_test_runtime_client::{
@@ -237,3 +237,59 @@ fn should_rotate_keys() {
assert_eq!(session_keys.ed25519, ed25519_key_pair.public().into());
assert_eq!(session_keys.sr25519, sr25519_key_pair.public().into());
}
#[test]
fn test_has_session_keys() {
let setup = TestSetup::default();
let p = setup.author();
let non_existent_public_keys = TestSetup::default()
.author()
.rotate_keys()
.expect("Rotates the keys");
let public_keys = p.rotate_keys().expect("Rotates the keys");
let test_vectors = vec![
(public_keys, Ok(true)),
(vec![1, 2, 3].into(), Err(Error::InvalidSessionKeys)),
(non_existent_public_keys, Ok(false)),
];
for (keys, result) in test_vectors {
assert_eq!(
result.map_err(|e| mem::discriminant(&e)),
p.has_session_keys(keys).map_err(|e| mem::discriminant(&e)),
);
}
}
#[test]
fn test_has_key() {
let setup = TestSetup::default();
let p = setup.author();
let suri = "//Alice";
let alice_key_pair = ed25519::Pair::from_string(suri, None).expect("Generates keypair");
p.insert_key(
String::from_utf8(ED25519.0.to_vec()).expect("Keytype is a valid string"),
suri.to_string(),
alice_key_pair.public().0.to_vec().into(),
).expect("Insert key");
let bob_key_pair = ed25519::Pair::from_string("//Bob", None).expect("Generates keypair");
let test_vectors = vec![
(alice_key_pair.public().to_raw_vec().into(), ED25519, Ok(true)),
(alice_key_pair.public().to_raw_vec().into(), SR25519, Ok(false)),
(bob_key_pair.public().to_raw_vec().into(), ED25519, Ok(false)),
];
for (key, key_type, result) in test_vectors {
assert_eq!(
result.map_err(|e| mem::discriminant(&e)),
p.has_key(
key,
String::from_utf8(key_type.0.to_vec()).expect("Keytype is a valid string"),
).map_err(|e| mem::discriminant(&e)),
);
}
}