mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-05-06 16:08:08 +00:00
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:
Generated
+1
@@ -6642,6 +6642,7 @@ name = "sp-session"
|
||||
version = "2.0.0"
|
||||
dependencies = [
|
||||
"sp-api 2.0.0",
|
||||
"sp-core 2.0.0",
|
||||
"sp-runtime 2.0.0",
|
||||
"sp-std 2.0.0",
|
||||
]
|
||||
|
||||
@@ -360,6 +360,12 @@ impl_runtime_apis! {
|
||||
fn generate_session_keys(seed: Option<Vec<u8>>) -> Vec<u8> {
|
||||
opaque::SessionKeys::generate(seed)
|
||||
}
|
||||
|
||||
fn decode_session_keys(
|
||||
encoded: Vec<u8>,
|
||||
) -> Option<Vec<(Vec<u8>, sp_core::crypto::KeyTypeId)>> {
|
||||
opaque::SessionKeys::decode_into_raw_public_keys(&encoded)
|
||||
}
|
||||
}
|
||||
|
||||
impl fg_primitives::GrandpaApi<Block> for Runtime {
|
||||
|
||||
@@ -798,6 +798,12 @@ impl_runtime_apis! {
|
||||
fn generate_session_keys(seed: Option<Vec<u8>>) -> Vec<u8> {
|
||||
SessionKeys::generate(seed)
|
||||
}
|
||||
|
||||
fn decode_session_keys(
|
||||
encoded: Vec<u8>,
|
||||
) -> Option<Vec<(Vec<u8>, sp_core::crypto::KeyTypeId)>> {
|
||||
SessionKeys::decode_into_raw_public_keys(&encoded)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -72,7 +72,8 @@ impl std::error::Error for Error {
|
||||
/// Every pair that is being generated by a `seed`, will be placed in memory.
|
||||
pub struct Store {
|
||||
path: Option<PathBuf>,
|
||||
additional: HashMap<(KeyTypeId, Vec<u8>), Vec<u8>>,
|
||||
/// Map over `(KeyTypeId, Raw public key)` -> `Key phrase/seed`
|
||||
additional: HashMap<(KeyTypeId, Vec<u8>), String>,
|
||||
password: Option<Protected<String>>,
|
||||
}
|
||||
|
||||
@@ -97,25 +98,22 @@ impl Store {
|
||||
}))
|
||||
}
|
||||
|
||||
/// Get the public/private key pair for the given public key and key type.
|
||||
fn get_additional_pair<Pair: PairT>(
|
||||
/// Get the key phrase for the given public key and key type from the in-memory store.
|
||||
fn get_additional_pair(
|
||||
&self,
|
||||
public: &Pair::Public,
|
||||
public: &[u8],
|
||||
key_type: KeyTypeId,
|
||||
) -> Result<Option<Pair>> {
|
||||
let key = (key_type, public.to_raw_vec());
|
||||
self.additional
|
||||
.get(&key)
|
||||
.map(|bytes| Pair::from_seed_slice(bytes).map_err(|_| Error::InvalidSeed))
|
||||
.transpose()
|
||||
) -> Option<&String> {
|
||||
let key = (key_type, public.to_vec());
|
||||
self.additional.get(&key)
|
||||
}
|
||||
|
||||
/// Insert the given public/private key pair with the given key type.
|
||||
///
|
||||
/// Does not place it into the file system store.
|
||||
fn insert_ephemeral_pair<Pair: PairT>(&mut self, pair: &Pair, key_type: KeyTypeId) {
|
||||
fn insert_ephemeral_pair<Pair: PairT>(&mut self, pair: &Pair, seed: &str, key_type: KeyTypeId) {
|
||||
let key = (key_type, pair.public().to_raw_vec());
|
||||
self.additional.insert(key, pair.to_raw_vec());
|
||||
self.additional.insert(key, seed.into());
|
||||
}
|
||||
|
||||
/// Insert a new key with anonymous crypto.
|
||||
@@ -179,7 +177,7 @@ impl Store {
|
||||
key_type: KeyTypeId,
|
||||
) -> Result<Pair> {
|
||||
let pair = Pair::from_string(seed, None).map_err(|_| Error::InvalidSeed)?;
|
||||
self.insert_ephemeral_pair(&pair, key_type);
|
||||
self.insert_ephemeral_pair(&pair, seed, key_type);
|
||||
Ok(pair)
|
||||
}
|
||||
|
||||
@@ -190,20 +188,24 @@ impl Store {
|
||||
self.insert_ephemeral_from_seed_by_type::<Pair::Generic>(seed, Pair::ID).map(Into::into)
|
||||
}
|
||||
|
||||
/// Get the key phrase for a given public key and key type.
|
||||
fn key_phrase_by_type(&self, public: &[u8], key_type: KeyTypeId) -> Result<String> {
|
||||
if let Some(phrase) = self.get_additional_pair(public, key_type) {
|
||||
return Ok(phrase.clone())
|
||||
}
|
||||
|
||||
let path = self.key_file_path(public, key_type).ok_or_else(|| Error::Unavailable)?;
|
||||
let file = File::open(path)?;
|
||||
|
||||
serde_json::from_reader(&file).map_err(Into::into)
|
||||
}
|
||||
|
||||
/// Get a key pair for the given public key and key type.
|
||||
pub fn key_pair_by_type<Pair: PairT>(&self,
|
||||
public: &Pair::Public,
|
||||
key_type: KeyTypeId,
|
||||
) -> Result<Pair> {
|
||||
if let Some(pair) = self.get_additional_pair(public, key_type)? {
|
||||
return Ok(pair)
|
||||
}
|
||||
|
||||
let path = self.key_file_path(public.as_slice(), key_type)
|
||||
.ok_or_else(|| Error::Unavailable)?;
|
||||
let file = File::open(path)?;
|
||||
|
||||
let phrase: String = serde_json::from_reader(&file)?;
|
||||
let phrase = self.key_phrase_by_type(public.as_slice(), key_type)?;
|
||||
let pair = Pair::from_string(
|
||||
&phrase,
|
||||
self.password.as_ref().map(|p| &***p),
|
||||
@@ -328,6 +330,10 @@ impl BareCryptoStore for Store {
|
||||
fn password(&self) -> Option<&str> {
|
||||
self.password.as_ref().map(|x| x.as_str())
|
||||
}
|
||||
|
||||
fn has_keys(&self, public_keys: &[(Vec<u8>, KeyTypeId)]) -> bool {
|
||||
public_keys.iter().all(|(p, t)| self.key_phrase_by_type(&p, *t).is_ok())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -54,6 +54,9 @@ pub enum Error {
|
||||
/// Some random issue with the key store. Shouldn't happen.
|
||||
#[display(fmt="The key store is unavailable")]
|
||||
KeyStoreUnavailable,
|
||||
/// Invalid session keys encoding.
|
||||
#[display(fmt="Session keys are not encoded correctly")]
|
||||
InvalidSessionKeys,
|
||||
}
|
||||
|
||||
impl std::error::Error for Error {
|
||||
|
||||
@@ -39,7 +39,8 @@ pub trait AuthorApi<Hash, BlockHash> {
|
||||
|
||||
/// Insert a key into the keystore.
|
||||
#[rpc(name = "author_insertKey")]
|
||||
fn insert_key(&self,
|
||||
fn insert_key(
|
||||
&self,
|
||||
key_type: String,
|
||||
suri: String,
|
||||
public: Bytes,
|
||||
@@ -49,6 +50,20 @@ pub trait AuthorApi<Hash, BlockHash> {
|
||||
#[rpc(name = "author_rotateKeys")]
|
||||
fn rotate_keys(&self) -> Result<Bytes>;
|
||||
|
||||
/// Checks if the keystore has private keys for the given session public keys.
|
||||
///
|
||||
/// `session_keys` is the SCALE encoded session keys object from the runtime.
|
||||
///
|
||||
/// Returns `true` iff all private keys could be found.
|
||||
#[rpc(name = "author_hasSessionKeys")]
|
||||
fn has_session_keys(&self, session_keys: Bytes) -> Result<bool>;
|
||||
|
||||
/// Checks if the keystore has private keys for the given public key and key type.
|
||||
///
|
||||
/// Returns `true` if a private key could be found.
|
||||
#[rpc(name = "author_hasKey")]
|
||||
fn has_key(&self, public_key: Bytes, key_type: String) -> Result<bool>;
|
||||
|
||||
/// Returns all pending extrinsics, potentially grouped by sender.
|
||||
#[rpc(name = "author_pendingExtrinsics")]
|
||||
fn pending_extrinsics(&self) -> Result<Vec<Bytes>>;
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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)),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,4 +53,8 @@ impl RuntimePublic for Public {
|
||||
fn verify<M: AsRef<[u8]>>(&self, msg: &M, signature: &Self::Signature) -> bool {
|
||||
sp_io::crypto::ed25519_verify(&signature, msg.as_ref(), self)
|
||||
}
|
||||
|
||||
fn to_raw_vec(&self) -> Vec<u8> {
|
||||
sp_core::crypto::Public::to_raw_vec(self)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -301,6 +301,10 @@ macro_rules! app_crypto_public_common {
|
||||
fn verify<M: AsRef<[u8]>>(&self, msg: &M, signature: &Self::Signature) -> bool {
|
||||
<$public as $crate::RuntimePublic>::verify(self.as_ref(), msg, &signature.as_ref())
|
||||
}
|
||||
|
||||
fn to_raw_vec(&self) -> $crate::Vec<u8> {
|
||||
<$public as $crate::RuntimePublic>::to_raw_vec(&self.0)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,4 +53,8 @@ impl RuntimePublic for Public {
|
||||
fn verify<M: AsRef<[u8]>>(&self, msg: &M, signature: &Self::Signature) -> bool {
|
||||
sp_io::crypto::sr25519_verify(&signature, msg.as_ref(), self)
|
||||
}
|
||||
|
||||
fn to_raw_vec(&self) -> Vec<u8> {
|
||||
sp_core::crypto::Public::to_raw_vec(self)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,10 +106,13 @@ pub trait RuntimePublic: Sized {
|
||||
|
||||
/// Verify that the given signature matches the given message using this public key.
|
||||
fn verify<M: AsRef<[u8]>>(&self, msg: &M, signature: &Self::Signature) -> bool;
|
||||
|
||||
/// Returns `Self` as raw vec.
|
||||
fn to_raw_vec(&self) -> Vec<u8>;
|
||||
}
|
||||
|
||||
/// A runtime interface for an application's public key.
|
||||
pub trait RuntimeAppPublic: Sized {
|
||||
pub trait RuntimeAppPublic: Sized {
|
||||
/// An identifier for this application-specific key type.
|
||||
const ID: KeyTypeId;
|
||||
|
||||
@@ -136,6 +139,9 @@ pub trait RuntimeAppPublic: Sized {
|
||||
|
||||
/// Verify that the given signature matches the given message using this public key.
|
||||
fn verify<M: AsRef<[u8]>>(&self, msg: &M, signature: &Self::Signature) -> bool;
|
||||
|
||||
/// Returns `Self` as raw vec.
|
||||
fn to_raw_vec(&self) -> Vec<u8>;
|
||||
}
|
||||
|
||||
/// Something that bound to a fixed `RuntimeAppPublic`.
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
// end::description[]
|
||||
|
||||
use sp_std::hash::Hash;
|
||||
#[cfg(feature = "full_crypto")]
|
||||
use sp_std::vec::Vec;
|
||||
#[cfg(feature = "std")]
|
||||
use sp_std::convert::TryInto;
|
||||
@@ -520,8 +519,7 @@ pub trait Public: AsRef<[u8]> + AsMut<[u8]> + Default + Derive + CryptoType + Pa
|
||||
fn from_slice(data: &[u8]) -> Self;
|
||||
|
||||
/// Return a `Vec<u8>` filled with raw data.
|
||||
#[cfg(feature = "std")]
|
||||
fn to_raw_vec(&self) -> Vec<u8> { self.as_slice().to_owned() }
|
||||
fn to_raw_vec(&self) -> Vec<u8> { self.as_slice().to_vec() }
|
||||
|
||||
/// Return a slice filled with raw data.
|
||||
fn as_slice(&self) -> &[u8] { self.as_ref() }
|
||||
|
||||
@@ -127,6 +127,10 @@ impl crate::traits::BareCryptoStore for KeyStore {
|
||||
fn password(&self) -> Option<&str> {
|
||||
None
|
||||
}
|
||||
|
||||
fn has_keys(&self, public_keys: &[(Vec<u8>, KeyTypeId)]) -> bool {
|
||||
public_keys.iter().all(|(k, t)| self.keys.get(&t).and_then(|s| s.get(k)).is_some())
|
||||
}
|
||||
}
|
||||
|
||||
/// Macro for exporting functions from wasm in with the expected signature for using it with the
|
||||
|
||||
@@ -69,6 +69,11 @@ pub trait BareCryptoStore: Send + Sync {
|
||||
|
||||
/// Get the password for this store.
|
||||
fn password(&self) -> Option<&str>;
|
||||
|
||||
/// Checks if the private keys for the given public key and key type combinations exist.
|
||||
///
|
||||
/// Returns `true` iff all private keys could be found.
|
||||
fn has_keys(&self, public_keys: &[(Vec<u8>, KeyTypeId)]) -> bool;
|
||||
}
|
||||
|
||||
/// A pointer to the key store.
|
||||
|
||||
@@ -19,8 +19,6 @@
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
#![warn(missing_docs)]
|
||||
|
||||
use sp_runtime::traits::NumberFor;
|
||||
|
||||
/// Local Storage Prefix used by the Offchain Worker API to
|
||||
pub const STORAGE_PREFIX: &[u8] = b"storage";
|
||||
|
||||
@@ -31,7 +29,7 @@ sp_api::decl_runtime_apis! {
|
||||
/// Starts the off-chain task for given block number.
|
||||
#[skip_initialize_block]
|
||||
#[changed_in(2)]
|
||||
fn offchain_worker(number: NumberFor<Block>);
|
||||
fn offchain_worker(number: sp_runtime::traits::NumberFor<Block>);
|
||||
|
||||
/// Starts the off-chain task for given block header.
|
||||
#[skip_initialize_block]
|
||||
|
||||
@@ -115,6 +115,10 @@ impl sp_application_crypto::RuntimeAppPublic for UintAuthorityId {
|
||||
|
||||
u64::from_le_bytes(msg_signature) == *signature
|
||||
}
|
||||
|
||||
fn to_raw_vec(&self) -> Vec<u8> {
|
||||
AsRef::<[u8]>::as_ref(self).to_vec()
|
||||
}
|
||||
}
|
||||
|
||||
impl OpaqueKeys for UintAuthorityId {
|
||||
|
||||
@@ -1106,12 +1106,14 @@ macro_rules! count {
|
||||
#[macro_export]
|
||||
macro_rules! impl_opaque_keys {
|
||||
(
|
||||
$( #[ $attr:meta ] )*
|
||||
pub struct $name:ident {
|
||||
$(
|
||||
pub $field:ident: $type:ty,
|
||||
)*
|
||||
}
|
||||
) => {
|
||||
$( #[ $attr ] )*
|
||||
#[derive(
|
||||
Default, Clone, PartialEq, Eq,
|
||||
$crate::codec::Encode,
|
||||
@@ -1143,6 +1145,37 @@ macro_rules! impl_opaque_keys {
|
||||
};
|
||||
$crate::codec::Encode::encode(&keys)
|
||||
}
|
||||
|
||||
/// Converts `Self` into a `Vec` of `(raw public key, KeyTypeId)`.
|
||||
pub fn into_raw_public_keys(
|
||||
self,
|
||||
) -> $crate::sp_std::vec::Vec<($crate::sp_std::vec::Vec<u8>, $crate::KeyTypeId)> {
|
||||
let mut keys = Vec::new();
|
||||
$(
|
||||
keys.push((
|
||||
$crate::RuntimeAppPublic::to_raw_vec(&self.$field),
|
||||
<
|
||||
<
|
||||
$type as $crate::BoundToRuntimeAppPublic
|
||||
>::Public as $crate::RuntimeAppPublic
|
||||
>::ID,
|
||||
));
|
||||
)*
|
||||
|
||||
keys
|
||||
}
|
||||
|
||||
/// Decode `Self` from the given `encoded` slice and convert `Self` into the raw public
|
||||
/// keys (see [`Self::into_raw_public_keys`]).
|
||||
///
|
||||
/// Returns `None` when the decoding failed, otherwise `Some(_)`.
|
||||
pub fn decode_into_raw_public_keys(
|
||||
encoded: &[u8],
|
||||
) -> Option<$crate::sp_std::vec::Vec<($crate::sp_std::vec::Vec<u8>, $crate::KeyTypeId)>> {
|
||||
<Self as $crate::codec::Decode>::decode(&mut &encoded[..])
|
||||
.ok()
|
||||
.map(|s| s.into_raw_public_keys())
|
||||
}
|
||||
}
|
||||
|
||||
impl $crate::traits::OpaqueKeys for $name {
|
||||
|
||||
@@ -7,8 +7,9 @@ edition = "2018"
|
||||
[dependencies]
|
||||
sp-api = { version = "2.0.0", default-features = false, path = "../api" }
|
||||
sp-std = { version = "2.0.0", default-features = false, path = "../std" }
|
||||
sp-core = { version = "2.0.0", default-features = false, path = "../core" }
|
||||
sp-runtime = { version = "2.0.0", optional = true, path = "../runtime" }
|
||||
|
||||
[features]
|
||||
default = [ "std" ]
|
||||
std = [ "sp-api/std", "sp-std/std", "sp-runtime" ]
|
||||
std = [ "sp-api/std", "sp-std/std", "sp-runtime", "sp-core/std" ]
|
||||
|
||||
@@ -25,6 +25,8 @@ use sp_runtime::{generic::BlockId, traits::Block as BlockT};
|
||||
#[cfg(feature = "std")]
|
||||
use sp_api::ProvideRuntimeApi;
|
||||
|
||||
use sp_core::crypto::KeyTypeId;
|
||||
|
||||
sp_api::decl_runtime_apis! {
|
||||
/// Session keys runtime api.
|
||||
pub trait SessionKeys {
|
||||
@@ -36,6 +38,11 @@ sp_api::decl_runtime_apis! {
|
||||
///
|
||||
/// Returns the concatenated SCALE encoded public keys.
|
||||
fn generate_session_keys(seed: Option<Vec<u8>>) -> Vec<u8>;
|
||||
|
||||
/// Decode the given public session keys.
|
||||
///
|
||||
/// Returns the list of public raw public keys + key type.
|
||||
fn decode_session_keys(encoded: Vec<u8>) -> Option<Vec<(Vec<u8>, KeyTypeId)>>;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -631,6 +631,12 @@ cfg_if! {
|
||||
fn generate_session_keys(_: Option<Vec<u8>>) -> Vec<u8> {
|
||||
SessionKeys::generate(None)
|
||||
}
|
||||
|
||||
fn decode_session_keys(
|
||||
encoded: Vec<u8>,
|
||||
) -> Option<Vec<(Vec<u8>, sp_core::crypto::KeyTypeId)>> {
|
||||
SessionKeys::decode_into_raw_public_keys(&encoded)
|
||||
}
|
||||
}
|
||||
|
||||
impl frame_system_rpc_runtime_api::AccountNonceApi<Block, AccountId, Index> for Runtime {
|
||||
@@ -847,6 +853,12 @@ cfg_if! {
|
||||
fn generate_session_keys(_: Option<Vec<u8>>) -> Vec<u8> {
|
||||
SessionKeys::generate(None)
|
||||
}
|
||||
|
||||
fn decode_session_keys(
|
||||
encoded: Vec<u8>,
|
||||
) -> Option<Vec<(Vec<u8>, sp_core::crypto::KeyTypeId)>> {
|
||||
SessionKeys::decode_into_raw_public_keys(&encoded)
|
||||
}
|
||||
}
|
||||
|
||||
impl frame_system_rpc_runtime_api::AccountNonceApi<Block, AccountId, Index> for Runtime {
|
||||
|
||||
Reference in New Issue
Block a user