Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

sudo/sessionKeys: Implement sudo_sessionKeys_unstable_generate #1682

Open
wants to merge 11 commits into
base: master
Choose a base branch
from
3 changes: 3 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 12 additions & 0 deletions prdoc/pr_1682.prdoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
title: Implement sudo_sessionKeys_unstable_generate

doc:
- audience: Node Operator
description: |
This PR implements the `sudo_sessionKeys_unstable_generate` method.
The specification for the method can be found at: https://github.com/paritytech/json-rpc-interface-spec/blob/main/src/api/sudo_sessionKeys_unstable_generate.md.
This is an alternative to generate (rotate) session keys to `author_rotateKeys`.

crates:
- name: sc-rpc-spec-v2
bump: minor
3 changes: 3 additions & 0 deletions substrate/client/rpc-spec-v2/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ sp-core = { workspace = true, default-features = true }
sp-runtime = { workspace = true, default-features = true }
sp-api = { workspace = true, default-features = true }
sp-rpc = { workspace = true, default-features = true }
sc-rpc-api = { workspace = true, default-features = true }
sp-blockchain = { workspace = true, default-features = true }
sp-version = { workspace = true, default-features = true }
sc-client-api = { workspace = true, default-features = true }
Expand All @@ -43,6 +44,8 @@ log = { workspace = true, default-features = true }
futures-util = { workspace = true }
rand = { workspace = true, default-features = true }
schnellru = { workspace = true }
sp-keystore = { workspace = true, default-features = true }
sp-session = { workspace = true, default-features = true }

[dev-dependencies]
jsonrpsee = { features = ["server", "ws-client"], workspace = true }
Expand Down
1 change: 1 addition & 0 deletions substrate/client/rpc-spec-v2/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ mod common;
pub mod archive;
pub mod chain_head;
pub mod chain_spec;
pub mod sudo_session_keys;
pub mod transaction;

/// Task executor that is being used by RPC subscriptions.
Expand Down
37 changes: 37 additions & 0 deletions substrate/client/rpc-spec-v2/src/sudo_session_keys/api.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// This file is part of Substrate.

// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0

// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.

//! API trait of the `sudo_sessionKeys` method.

use jsonrpsee::{core::RpcResult, proc_macros::rpc};

use crate::MethodResult;

#[rpc(client, server)]
pub trait SudoSessionKeys {
/// This RPC method calls into `SessionKeys_generate_session_keys` runtime function.
///
/// The `SessionKeys_generate_session_keys` runtime function generates a series of keys,
/// inserts those keys into the keystore, and returns all the public keys concatenated.
///
/// # Unstable
///
/// This method is unstable and subject to change in the future.
#[method(name = "sudo_sessionKeys_unstable_generate")]
fn sudo_session_keys_unstable_generate(&self, seed: Option<String>) -> RpcResult<MethodResult>;
}
28 changes: 28 additions & 0 deletions substrate/client/rpc-spec-v2/src/sudo_session_keys/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// This file is part of Substrate.

// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0

// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.

//! Substrate sudo sessions key API.

#[cfg(test)]
mod tests;

pub mod api;
pub mod sudo_session_keys;

pub use api::SudoSessionKeysServer;
pub use sudo_session_keys::SudoSessionKeys;
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
// This file is part of Substrate.

// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0

// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.

//! API implementation for `sudo_session_keys`.

use jsonrpsee::core::{async_trait, RpcResult};
use sc_rpc_api::DenyUnsafe;
use sp_blockchain::HeaderBackend;
use sp_keystore::{KeystoreExt, KeystorePtr};
use sp_runtime::traits::Block as BlockT;
use std::{marker::PhantomData, sync::Arc};

use crate::{hex_string, sudo_session_keys::api::SudoSessionKeysServer, MethodResult};

use sp_api::{ApiExt, ProvideRuntimeApi};
use sp_session::SessionKeys;

/// An API for `SudoSessionKeys` RPC calls.
pub struct SudoSessionKeys<Client, Block: BlockT> {
/// Substrate client.
client: Arc<Client>,
/// The key store.
keystore: KeystorePtr,
/// Whether to deny unsafe calls
deny_unsafe: DenyUnsafe,
/// Phantom data to hold the block type.
_phantom: PhantomData<Block>,
}

impl<Client, Block: BlockT> SudoSessionKeys<Client, Block> {
/// Create a new [`SudoSessionKeys`].
pub fn new(client: Arc<Client>, keystore: KeystorePtr, deny_unsafe: DenyUnsafe) -> Self {
Self { client, keystore, deny_unsafe, _phantom: PhantomData }
}
}

#[async_trait]
impl<Client, Block> SudoSessionKeysServer for SudoSessionKeys<Client, Block>
where
Block: BlockT + 'static,
Client: HeaderBackend<Block> + ProvideRuntimeApi<Block> + Send + Sync + 'static,
Client::Api: SessionKeys<Block>,
{
fn sudo_session_keys_unstable_generate(&self, seed: Option<String>) -> RpcResult<MethodResult> {
// Deny potentially unsafe calls.
if let Err(err) = self.deny_unsafe.check_if_safe() {
return Ok(MethodResult::err(err.to_string()));
}

// Call into the runtime of the best block hash.
let best_block_hash = self.client.info().best_hash;
let mut runtime_api = self.client.runtime_api();

runtime_api.register_extension(KeystoreExt::from(self.keystore.clone()));

let response = runtime_api
.generate_session_keys(best_block_hash, seed.map(|seed| seed.into_bytes()))
.map(|bytes| MethodResult::ok(hex_string(&bytes.as_slice())))
.unwrap_or_else(|api_err| MethodResult::err(api_err.to_string()));

Ok(response)
}
}
66 changes: 66 additions & 0 deletions substrate/client/rpc-spec-v2/src/sudo_session_keys/tests.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
// This file is part of Substrate.

// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0

// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.

use super::sudo_session_keys::SudoSessionKeys;
use crate::{sudo_session_keys::api::SudoSessionKeysServer, MethodResult};
use codec::Decode;
use jsonrpsee::{rpc_params, RpcModule};
use sc_rpc_api::DenyUnsafe;
use sp_core::{
crypto::ByteArray,
testing::{ED25519, SR25519},
};
use sp_keystore::{testing::MemoryKeystore, Keystore};
use std::sync::Arc;
use substrate_test_runtime_client::{
self,
runtime::{Block, SessionKeys},
Backend, Client, DefaultTestClientBuilderExt, TestClientBuilderExt,
};

fn setup_api(
deny_unsafe: DenyUnsafe,
) -> (Arc<MemoryKeystore>, RpcModule<SudoSessionKeys<Client<Backend>, Block>>) {
let keystore = Arc::new(MemoryKeystore::new());
let client = Arc::new(substrate_test_runtime_client::TestClientBuilder::new().build());
let api = SudoSessionKeys::new(client, keystore.clone(), deny_unsafe).into_rpc();

(keystore, api)
}

#[tokio::test]
async fn sudo_session_keys_unstable_generate() {
lexnv marked this conversation as resolved.
Show resolved Hide resolved
let (keystore, api) = setup_api(DenyUnsafe::No);

let response: MethodResult =
api.call("sudo_sessionKeys_unstable_generate", rpc_params![]).await.unwrap();

let bytes = match response {
MethodResult::Ok(ok) => hex::decode(ok.result.strip_prefix("0x").unwrap()).unwrap(),
_ => panic!("Unexpected response"),
};

let session_keys =
SessionKeys::decode(&mut &bytes[..]).expect("SessionKeys decode successfully");

let ed25519_pubkeys = keystore.keys(ED25519).unwrap();
let sr25519_pubkeys = keystore.keys(SR25519).unwrap();

assert!(ed25519_pubkeys.contains(&session_keys.ed25519.to_raw_vec()));
assert!(sr25519_pubkeys.contains(&session_keys.sr25519.to_raw_vec()));
}
10 changes: 10 additions & 0 deletions substrate/client/service/src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ use sc_rpc_spec_v2::{
archive::ArchiveApiServer,
chain_head::ChainHeadApiServer,
chain_spec::ChainSpecApiServer,
sudo_session_keys::SudoSessionKeysServer,
transaction::{TransactionApiServer, TransactionBroadcastApiServer},
};
use sc_telemetry::{telemetry, ConnectionMessage, Telemetry, TelemetryHandle, SUBSTRATE_INFO};
Expand Down Expand Up @@ -689,6 +690,14 @@ where
rpc_api.merge(archive_v2).map_err(|e| Error::Application(e.into()))?;
}

// RPC v2 spec to handle session keys.
let sudo_session_keys = sc_rpc_spec_v2::sudo_session_keys::SudoSessionKeys::new(
client.clone(),
keystore.clone(),
deny_unsafe,
)
.into_rpc();

// ChainSpec RPC-v2.
let chain_spec_v2 = sc_rpc_spec_v2::chain_spec::ChainSpec::new(
config.chain_spec.name().into(),
Expand Down Expand Up @@ -720,6 +729,7 @@ where
.merge(transaction_broadcast_rpc_v2)
.map_err(|e| Error::Application(e.into()))?;
rpc_api.merge(chain_head_v2).map_err(|e| Error::Application(e.into()))?;
rpc_api.merge(sudo_session_keys).map_err(|e| Error::Application(e.into()))?;
rpc_api.merge(chain_spec_v2).map_err(|e| Error::Application(e.into()))?;

// Part of the old RPC spec.
Expand Down
Loading