Adds author_rotateKeys RPC (#3337)

* Adds `author_rotateKeys` RPC

`author_rotateKeys` will rotate all session keys, store them into the
keystore and returns their encoded public keys as result. These encoded
public keys can be used directly to send the transaction to the chain.

* Review comments
This commit is contained in:
Bastian Köcher
2019-08-08 11:00:04 +02:00
committed by Tomasz Drwięga
parent 6565e1f8aa
commit 0de7d9bb64
11 changed files with 100 additions and 26 deletions
+3 -11
View File
@@ -2339,7 +2339,7 @@ name = "node-primitives"
version = "2.0.0"
dependencies = [
"parity-scale-codec 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
"pretty_assertions 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
"pretty_assertions 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.97 (registry+https://github.com/rust-lang/crates.io-index)",
"sr-primitives 2.0.0",
"sr-std 2.0.0",
@@ -2858,15 +2858,6 @@ name = "ppv-lite86"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "pretty_assertions"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
"difference 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "pretty_assertions"
version = "0.6.1"
@@ -4837,6 +4828,7 @@ dependencies = [
"substrate-keystore 2.0.0",
"substrate-network 2.0.0",
"substrate-primitives 2.0.0",
"substrate-session 2.0.0",
"substrate-state-machine 2.0.0",
"substrate-test-runtime-client 2.0.0",
"substrate-transaction-pool 2.0.0",
@@ -5040,6 +5032,7 @@ dependencies = [
"substrate-keyring 2.0.0",
"substrate-offchain-primitives 2.0.0",
"substrate-primitives 2.0.0",
"substrate-session 2.0.0",
"substrate-state-machine 2.0.0",
"substrate-test-runtime-client 2.0.0",
"substrate-trie 2.0.0",
@@ -6364,7 +6357,6 @@ dependencies = [
"checksum pin-utils 0.1.0-alpha.4 (registry+https://github.com/rust-lang/crates.io-index)" = "5894c618ce612a3fa23881b152b608bafb8c56cfc22f434a3ba3120b40f7b587"
"checksum pkg-config 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "a7c1d2cfa5a714db3b5f24f0915e74fcdf91d09d496ba61329705dda7774d2af"
"checksum ppv-lite86 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e3cbf9f658cdb5000fcf6f362b8ea2ba154b9f146a61c7a20d647034c6b6561b"
"checksum pretty_assertions 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3a029430f0d744bc3d15dd474d591bed2402b645d024583082b9f63bb936dac6"
"checksum pretty_assertions 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3f81e1644e1b54f5a68959a29aa86cde704219254669da328ecfdf6a1f09d427"
"checksum primitive-types 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e44400d651ca5276415dc8e00541c5c9d03844f1f0a87ad28f0a8fadcb2300bc"
"checksum proc-macro-crate 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "e10d4b51f154c8a7fb96fd6dad097cb74b863943ec010ac94b9fd1be8861fe1e"
+3 -1
View File
@@ -18,7 +18,9 @@
//! Cryptographic utilities.
// end::description[]
use rstd::convert::{TryFrom, TryInto};
#[cfg(feature = "std")]
use rstd::convert::TryInto;
use rstd::convert::TryFrom;
#[cfg(feature = "std")]
use parking_lot::Mutex;
#[cfg(feature = "std")]
+2 -1
View File
@@ -21,6 +21,7 @@ client = { package = "substrate-client", path = "../client" }
substrate-executor = { path = "../executor" }
network = { package = "substrate-network", path = "../network" }
primitives = { package = "substrate-primitives", path = "../primitives" }
session = { package = "substrate-session", path = "../session" }
state_machine = { package = "substrate-state-machine", path = "../state-machine" }
transaction_pool = { package = "substrate-transaction-pool", path = "../transaction-pool" }
sr-primitives = { path = "../sr-primitives" }
@@ -33,4 +34,4 @@ futures = "0.1.17"
sr-io = { path = "../sr-io" }
test-client = { package = "substrate-test-runtime-client", path = "../test-runtime/client" }
rustc-hex = "2.0"
tokio = "0.1.17"
tokio = "0.1.17"
+17 -2
View File
@@ -33,9 +33,9 @@ use log::warn;
use codec::{Encode, Decode};
use primitives::{
Bytes, Blake2Hasher, H256, ed25519, sr25519, crypto::{Pair, Public, key_types},
traits::BareCryptoStorePtr
traits::BareCryptoStorePtr,
};
use sr_primitives::{generic, traits};
use sr_primitives::{generic, traits::{self, ProvideRuntimeApi}};
use self::error::{Error, Result};
use transaction_pool::{
txpool::{
@@ -47,6 +47,7 @@ use transaction_pool::{
watcher::Status,
},
};
use session::SessionKeys;
pub use self::gen_client::Client as AuthorClient;
@@ -68,6 +69,10 @@ pub trait AuthorApi<Hash, BlockHash> {
maybe_public: Option<Bytes>
) -> Result<Bytes>;
/// Generate new session keys and returns the corresponding public keys.
#[rpc(name = "author_rotateKeys")]
fn rotate_keys(&self) -> Result<Bytes>;
/// Returns all pending extrinsics, potentially grouped by sender.
#[rpc(name = "author_pendingExtrinsics")]
fn pending_extrinsics(&self) -> Result<Vec<Bytes>>;
@@ -138,6 +143,8 @@ impl<B, E, P, RA> AuthorApi<ExHash<P>, BlockHash<P>> for Author<B, E, P, RA> whe
P::Block: traits::Block<Hash=H256>,
P::Error: 'static,
RA: Send + Sync + 'static,
Client<B, E, P::Block, RA>: ProvideRuntimeApi,
<Client<B, E, P::Block, RA> as ProvideRuntimeApi>::Api: SessionKeys<P::Block>,
{
type Metadata = crate::metadata::Metadata;
@@ -170,6 +177,14 @@ impl<B, E, P, RA> AuthorApi<ExHash<P>, BlockHash<P>> for Author<B, E, P, RA> whe
Ok(public.into())
}
fn rotate_keys(&self) -> Result<Bytes> {
let best_block_hash = self.client.info().chain.best_hash;
self.client.runtime_api().generate_session_keys(
&generic::BlockId::Hash(best_block_hash),
None,
).map(Into::into).map_err(Into::into)
}
fn submit_extrinsic(&self, ext: Bytes) -> Result<ExHash<P>> {
let xt = Decode::decode(&mut &ext[..])?;
let best_block_hash = self.client.info().chain.best_hash;
+35 -1
View File
@@ -27,7 +27,10 @@ use primitives::{
H256, blake2_256, hexdisplay::HexDisplay, traits::BareCryptoStore, testing::KeyStore,
ed25519, crypto::key_types,
};
use test_client::{self, AccountKeyring, runtime::{Extrinsic, Transfer}};
use test_client::{
self, AccountKeyring, runtime::{Extrinsic, Transfer, SessionKeys}, DefaultTestClientBuilderExt,
TestClientBuilderExt,
};
use tokio::runtime;
fn uxt(sender: AccountKeyring, nonce: u64) -> Extrinsic {
@@ -203,4 +206,35 @@ fn should_insert_key() {
.ed25519_key_pair(key_types::ED25519, &key_pair.public()).expect("Key exists in store");
assert_eq!(key_pair.public(), store_key_pair.public());
}
#[test]
fn should_rotate_keys() {
let runtime = runtime::Runtime::new().unwrap();
let keystore = KeyStore::new();
let client = Arc::new(test_client::TestClientBuilder::new().set_keystore(keystore.clone()).build());
let p = Author {
client: client.clone(),
pool: Arc::new(Pool::new(Default::default(), ChainApi::new(client))),
subscriptions: Subscriptions::new(Arc::new(runtime.executor())),
keystore: keystore.clone(),
};
let new_public_keys = p.rotate_keys().expect("Rotates the keys");
let session_keys = SessionKeys::decode(&mut &new_public_keys[..])
.expect("SessionKeys decode successfully");
let ed25519_key_pair = keystore.read().ed25519_key_pair(
key_types::ED25519,
&session_keys.ed25519.clone().into(),
).expect("ed25519 key exists in store");
let sr25519_key_pair = keystore.read().sr25519_key_pair(
key_types::SR25519,
&session_keys.sr25519.clone().into(),
).expect("sr25519 key exists in store");
assert_eq!(session_keys.ed25519, ed25519_key_pair.public().into());
assert_eq!(session_keys.sr25519, sr25519_key_pair.public().into());
}
+2 -1
View File
@@ -260,7 +260,8 @@ fn should_return_runtime_version() {
\"specVersion\":1,\"implVersion\":1,\"apis\":[[\"0xdf6acb689907609b\",2],\
[\"0x37e397fc7c91f5e4\",1],[\"0xd2bc9897eed08f15\",1],[\"0x40fe3ad401f8959a\",3],\
[\"0xc6e9a76309f39b09\",1],[\"0xdd718d5cc53262d4\",1],[\"0xcbca25e39f142387\",1],\
[\"0xf78b278be53f454c\",1]]}";
[\"0xf78b278be53f454c\",1],[\"0xab3c0572291feb8b\",1]]}";
assert_eq!(
serde_json::to_string(&api.runtime_version(None.into()).unwrap()).unwrap(),
result,
+1 -1
View File
@@ -23,8 +23,8 @@ keystore = { package = "substrate-keystore", path = "../../core/keystore" }
sr-io = { path = "../../core/sr-io" }
sr-primitives = { path = "../../core/sr-primitives" }
primitives = { package = "substrate-primitives", path = "../primitives" }
session = { package = "substrate-session", path = "../session" }
app-crypto = { package = "substrate-application-crypto", path = "../application-crypto" }
substrate-session = { path = "../session" }
consensus_common = { package = "substrate-consensus-common", path = "../../core/consensus/common" }
network = { package = "substrate-network", path = "../../core/network" }
client = { package = "substrate-client", path = "../../core/client" }
+10 -4
View File
@@ -156,13 +156,13 @@ pub trait InitialSessionKeys<C: Components> {
impl<C: Components> InitialSessionKeys<Self> for C where
ComponentClient<C>: ProvideRuntimeApi,
<ComponentClient<C> as ProvideRuntimeApi>::Api: substrate_session::SessionKeys<ComponentBlock<C>>,
<ComponentClient<C> as ProvideRuntimeApi>::Api: session::SessionKeys<ComponentBlock<C>>,
{
fn generate_intial_session_keys(
client: Arc<ComponentClient<C>>,
seeds: Vec<String>,
) -> error::Result<()> {
substrate_session::generate_initial_session_keys(client, seeds).map_err(Into::into)
session::generate_initial_session_keys(client, seeds).map_err(Into::into)
}
}
@@ -180,7 +180,8 @@ pub trait StartRPC<C: Components> {
impl<C: Components> StartRPC<Self> for C where
ComponentClient<C>: ProvideRuntimeApi,
<ComponentClient<C> as ProvideRuntimeApi>::Api: runtime_api::Metadata<ComponentBlock<C>>,
<ComponentClient<C> as ProvideRuntimeApi>::Api:
runtime_api::Metadata<ComponentBlock<C>> + session::SessionKeys<ComponentBlock<C>>,
{
fn start_rpc(
client: Arc<ComponentClient<C>>,
@@ -193,7 +194,12 @@ impl<C: Components> StartRPC<Self> for C where
let subscriptions = rpc::apis::Subscriptions::new(task_executor.clone());
let chain = rpc::apis::chain::Chain::new(client.clone(), subscriptions.clone());
let state = rpc::apis::state::State::new(client.clone(), subscriptions.clone());
let author = rpc::apis::author::Author::new(client, transaction_pool, subscriptions, keystore);
let author = rpc::apis::author::Author::new(
client,
transaction_pool,
subscriptions,
keystore,
);
let system = rpc::apis::system::System::new(rpc_system_info, system_send_back);
rpc::rpc_handler::<ComponentBlock<C>, ComponentExHash<C>, _, _, _, _>(
state,
+2
View File
@@ -19,6 +19,7 @@ babe-primitives = { package = "substrate-consensus-babe-primitives", path = "../
rstd = { package = "sr-std", path = "../sr-std", default-features = false }
runtime_io = { package = "sr-io", path = "../sr-io", default-features = false }
sr-primitives = { path = "../sr-primitives", default-features = false }
session = { package = "substrate-session", path = "../session", default-features = false }
runtime_version = { package = "sr-version", path = "../sr-version", default-features = false }
runtime_support = { package = "srml-support", path = "../../srml/support", default-features = false }
substrate-trie = { path = "../trie", default-features = false }
@@ -69,4 +70,5 @@ std = [
"srml-timestamp/std",
"srml-system/std",
"app-crypto/std",
"session/std",
]
+24 -3
View File
@@ -37,15 +37,15 @@ use substrate_client::{
impl_runtime_apis,
};
use sr_primitives::{
ApplyResult, create_runtime_str, Perbill,
ApplyResult, create_runtime_str, Perbill, impl_opaque_keys,
transaction_validity::{TransactionValidity, ValidTransaction},
traits::{
BlindCheckable, BlakeTwo256, Block as BlockT, Extrinsic as ExtrinsicT,
GetNodeBlockType, GetRuntimeBlockType, Verify, IdentityLookup
GetNodeBlockType, GetRuntimeBlockType, Verify, IdentityLookup,
},
};
use runtime_version::RuntimeVersion;
pub use primitives::hash::H256;
pub use primitives::{hash::H256, crypto::key_types};
#[cfg(any(feature = "std", test))]
use runtime_version::NativeVersion;
use runtime_support::{impl_outer_origin, parameter_types};
@@ -434,6 +434,15 @@ fn code_using_trie() -> u64 {
} else { 103 }
}
impl_opaque_keys! {
pub struct SessionKeys {
#[id(key_types::ED25519)]
pub ed25519: ed25519::AppPublic,
#[id(key_types::SR25519)]
pub sr25519: sr25519::AppPublic,
}
}
#[cfg(not(feature = "std"))]
/// Mutable static variables should be always observed to have
/// the initialized value at the start of a runtime call.
@@ -612,6 +621,12 @@ cfg_if! {
runtime_io::submit_transaction(&ex).unwrap();
}
}
impl session::SessionKeys<Block> for Runtime {
fn generate_session_keys(_: Option<Vec<u8>>) -> Vec<u8> {
SessionKeys::generate(None)
}
}
}
} else {
impl_runtime_apis! {
@@ -816,6 +831,12 @@ cfg_if! {
runtime_io::submit_transaction(&ex).unwrap()
}
}
impl session::SessionKeys<Block> for Runtime {
fn generate_session_keys(_: Option<Vec<u8>>) -> Vec<u8> {
SessionKeys::generate(None)
}
}
}
}
}
+1 -1
View File
@@ -13,7 +13,7 @@ sr-primitives = { path = "../../core/sr-primitives", default-features = false }
[dev-dependencies]
substrate-serializer = { path = "../../core/serializer" }
pretty_assertions = "0.5"
pretty_assertions = "0.6.1"
[features]
default = ["std"]