From 0de7d9bb646313eb88ff8794a55388bd6c2e25b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20K=C3=B6cher?= Date: Thu, 8 Aug 2019 11:00:04 +0200 Subject: [PATCH] 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 --- substrate/Cargo.lock | 14 ++------- substrate/core/primitives/src/crypto.rs | 4 ++- substrate/core/rpc/Cargo.toml | 3 +- substrate/core/rpc/src/author/mod.rs | 19 +++++++++++-- substrate/core/rpc/src/author/tests.rs | 36 +++++++++++++++++++++++- substrate/core/rpc/src/state/tests.rs | 3 +- substrate/core/service/Cargo.toml | 2 +- substrate/core/service/src/components.rs | 14 ++++++--- substrate/core/test-runtime/Cargo.toml | 2 ++ substrate/core/test-runtime/src/lib.rs | 27 ++++++++++++++++-- substrate/node/primitives/Cargo.toml | 2 +- 11 files changed, 100 insertions(+), 26 deletions(-) diff --git a/substrate/Cargo.lock b/substrate/Cargo.lock index 5e763195a4..bcadbe2e74 100644 --- a/substrate/Cargo.lock +++ b/substrate/Cargo.lock @@ -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" diff --git a/substrate/core/primitives/src/crypto.rs b/substrate/core/primitives/src/crypto.rs index 56a8528369..0adf50e160 100644 --- a/substrate/core/primitives/src/crypto.rs +++ b/substrate/core/primitives/src/crypto.rs @@ -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")] diff --git a/substrate/core/rpc/Cargo.toml b/substrate/core/rpc/Cargo.toml index 3072ba4783..ab05eb4e24 100644 --- a/substrate/core/rpc/Cargo.toml +++ b/substrate/core/rpc/Cargo.toml @@ -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" \ No newline at end of file diff --git a/substrate/core/rpc/src/author/mod.rs b/substrate/core/rpc/src/author/mod.rs index 47f481f00f..d797e87da5 100644 --- a/substrate/core/rpc/src/author/mod.rs +++ b/substrate/core/rpc/src/author/mod.rs @@ -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 { maybe_public: Option ) -> Result; + /// Generate new session keys and returns the corresponding public keys. + #[rpc(name = "author_rotateKeys")] + fn rotate_keys(&self) -> Result; + /// Returns all pending extrinsics, potentially grouped by sender. #[rpc(name = "author_pendingExtrinsics")] fn pending_extrinsics(&self) -> Result>; @@ -138,6 +143,8 @@ impl AuthorApi, BlockHash

> for Author whe P::Block: traits::Block, P::Error: 'static, RA: Send + Sync + 'static, + Client: ProvideRuntimeApi, + as ProvideRuntimeApi>::Api: SessionKeys, { type Metadata = crate::metadata::Metadata; @@ -170,6 +177,14 @@ impl AuthorApi, BlockHash

> for Author whe Ok(public.into()) } + fn rotate_keys(&self) -> Result { + 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> { let xt = Decode::decode(&mut &ext[..])?; let best_block_hash = self.client.info().chain.best_hash; diff --git a/substrate/core/rpc/src/author/tests.rs b/substrate/core/rpc/src/author/tests.rs index 18a2e41739..0fdff9989b 100644 --- a/substrate/core/rpc/src/author/tests.rs +++ b/substrate/core/rpc/src/author/tests.rs @@ -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()); } \ No newline at end of file diff --git a/substrate/core/rpc/src/state/tests.rs b/substrate/core/rpc/src/state/tests.rs index 6a8eefa10b..6b4ddc9b92 100644 --- a/substrate/core/rpc/src/state/tests.rs +++ b/substrate/core/rpc/src/state/tests.rs @@ -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, diff --git a/substrate/core/service/Cargo.toml b/substrate/core/service/Cargo.toml index 2bcb180570..c2a17371da 100644 --- a/substrate/core/service/Cargo.toml +++ b/substrate/core/service/Cargo.toml @@ -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" } diff --git a/substrate/core/service/src/components.rs b/substrate/core/service/src/components.rs index d2b6131ed0..2ca99e0b31 100644 --- a/substrate/core/service/src/components.rs +++ b/substrate/core/service/src/components.rs @@ -156,13 +156,13 @@ pub trait InitialSessionKeys { impl InitialSessionKeys for C where ComponentClient: ProvideRuntimeApi, - as ProvideRuntimeApi>::Api: substrate_session::SessionKeys>, + as ProvideRuntimeApi>::Api: session::SessionKeys>, { fn generate_intial_session_keys( client: Arc>, seeds: Vec, ) -> 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 { impl StartRPC for C where ComponentClient: ProvideRuntimeApi, - as ProvideRuntimeApi>::Api: runtime_api::Metadata>, + as ProvideRuntimeApi>::Api: + runtime_api::Metadata> + session::SessionKeys>, { fn start_rpc( client: Arc>, @@ -193,7 +194,12 @@ impl StartRPC 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::, ComponentExHash, _, _, _, _>( state, diff --git a/substrate/core/test-runtime/Cargo.toml b/substrate/core/test-runtime/Cargo.toml index 98d110a574..13186056ce 100644 --- a/substrate/core/test-runtime/Cargo.toml +++ b/substrate/core/test-runtime/Cargo.toml @@ -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", ] diff --git a/substrate/core/test-runtime/src/lib.rs b/substrate/core/test-runtime/src/lib.rs index e2c7c27876..54c922c216 100644 --- a/substrate/core/test-runtime/src/lib.rs +++ b/substrate/core/test-runtime/src/lib.rs @@ -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 for Runtime { + fn generate_session_keys(_: Option>) -> Vec { + SessionKeys::generate(None) + } + } } } else { impl_runtime_apis! { @@ -816,6 +831,12 @@ cfg_if! { runtime_io::submit_transaction(&ex).unwrap() } } + + impl session::SessionKeys for Runtime { + fn generate_session_keys(_: Option>) -> Vec { + SessionKeys::generate(None) + } + } } } } diff --git a/substrate/node/primitives/Cargo.toml b/substrate/node/primitives/Cargo.toml index 05fb231205..654347273f 100644 --- a/substrate/node/primitives/Cargo.toml +++ b/substrate/node/primitives/Cargo.toml @@ -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"]