diff --git a/Cargo.lock b/Cargo.lock index 15e89cff44f9f0e0503bc38f7b1e4bdad4dd02e7..03e3e81864fb6d7b9a8d6baf4838ed1897a214ef 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1235,6 +1235,7 @@ dependencies = [ "g1-runtime", "gdev-runtime", "gtest-runtime", + "hex", "jsonrpc-core", "log", "maplit", @@ -1268,6 +1269,7 @@ dependencies = [ "sp-core", "sp-finality-grandpa", "sp-keyring", + "sp-keystore", "sp-membership", "sp-offchain", "sp-runtime", diff --git a/Cargo.toml b/Cargo.toml index caeb104c209013002d0f791d0dde0fba2b0e5d20..6d9f96550140d542435b5c88b5b541c22c3c77fa 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -47,6 +47,7 @@ sp-membership = { path = 'primitives/membership' } # crates.io dependencies async-io = "1.3" futures = { version = "0.3.1", features = ["compat"] } +hex = "0.4.3" jsonrpc-core = '18.0.0' log = "0.4" maplit = '1.0.2' @@ -85,6 +86,7 @@ sp-core = { git = "https://github.com/librelois/substrate.git", branch = "dunite sp-finality-grandpa = { git = "https://github.com/librelois/substrate.git", branch = "duniter-monthly-2022-01" } sp-offchain = { git = "https://github.com/librelois/substrate.git", branch = "duniter-monthly-2022-01" } sp-keyring = { git = "https://github.com/librelois/substrate.git", branch = "duniter-monthly-2022-01" } +sp-keystore = { git = "https://github.com/librelois/substrate.git", branch = "duniter-monthly-2022-01" } sp-runtime = { git = "https://github.com/librelois/substrate.git", branch = "duniter-monthly-2022-01" } sp-session = { git = "https://github.com/librelois/substrate.git", branch = "duniter-monthly-2022-01" } sp-storage = { git = "https://github.com/librelois/substrate.git", branch = "duniter-monthly-2022-01" } diff --git a/node/src/cli.rs b/node/src/cli.rs index 534ad8b38f1446ef49873c5aadade50c8135882e..ed59dbc65c907ee79d6ad400f467e8fb0031ed74 100644 --- a/node/src/cli.rs +++ b/node/src/cli.rs @@ -14,6 +14,8 @@ // You should have received a copy of the GNU Affero General Public License // along with Substrate-Libre-Currency. If not, see <https://www.gnu.org/licenses/>. +pub mod key; + use sc_cli::RunCmd; use std::str::FromStr; use structopt::StructOpt; @@ -26,7 +28,7 @@ pub struct Cli { #[structopt(flatten)] pub run: RunCmd, - /// When blocks should be sealed in the dev service. + /// How blocks should be sealed /// /// Options are "production", "instant", "manual", or timer interval in milliseconds #[structopt(long, default_value = "production")] @@ -36,7 +38,7 @@ pub struct Cli { #[derive(Debug, StructOpt)] pub enum Subcommand { /// Key management cli utilities - Key(sc_cli::KeySubcommand), + Key(key::KeySubcommand), /// Build a chain specification. BuildSpec(sc_cli::BuildSpecCmd), diff --git a/node/src/cli/key.rs b/node/src/cli/key.rs new file mode 100644 index 0000000000000000000000000000000000000000..0ed9d0a6ee0341996dcd6d82a04b72f4161741c3 --- /dev/null +++ b/node/src/cli/key.rs @@ -0,0 +1,111 @@ +// Copyright 2021 Axiom-Team +// +// This file is part of Substrate-Libre-Currency. +// +// Substrate-Libre-Currency is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, version 3 of the License. +// +// Substrate-Libre-Currency 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 Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with Substrate-Libre-Currency. If not, see <https://www.gnu.org/licenses/>. + +use sc_cli::{ + utils, with_crypto_scheme, CryptoScheme, Error, KeystoreParams, SharedParams, SubstrateCli, +}; +use sc_keystore::LocalKeystore; +use sc_service::config::{BasePath, KeystoreConfig}; +use sp_core::crypto::{KeyTypeId, SecretString}; +use sp_keystore::{SyncCryptoStore, SyncCryptoStorePtr}; +use std::sync::Arc; +use structopt::StructOpt; + +#[derive(Debug, StructOpt)] +pub enum KeySubcommand { + /// Generate session keys and store them in the keystore + GenerateSessionKeys(GenSessionKeysCmd), + + #[allow(missing_docs)] + #[structopt(flatten)] + Key(sc_cli::KeySubcommand), +} + +impl KeySubcommand { + /// Run the command + pub fn run<C: SubstrateCli>(&self, cli: &C) -> Result<(), Error> { + match self { + Self::GenerateSessionKeys(cmd) => cmd.run(cli), + Self::Key(cmd) => cmd.run(cli), + } + } +} + +#[derive(Debug, StructOpt)] +pub struct GenSessionKeysCmd { + /// The secret key URI. + /// If the value is a file, the file content is used as URI. + /// If not given, you will be prompted for the URI. + #[structopt(long)] + suri: Option<String>, + + #[allow(missing_docs)] + #[structopt(flatten)] + pub shared_params: SharedParams, + + #[allow(missing_docs)] + #[structopt(flatten)] + pub keystore_params: KeystoreParams, +} + +const KEY_TYPES: [(KeyTypeId, CryptoScheme); 4] = [ + (KeyTypeId(*b"gran"), CryptoScheme::Ed25519), + (KeyTypeId(*b"babe"), CryptoScheme::Sr25519), + (KeyTypeId(*b"imon"), CryptoScheme::Sr25519), + (KeyTypeId(*b"audi"), CryptoScheme::Sr25519), +]; + +impl GenSessionKeysCmd { + /// Run the command + pub fn run<C: SubstrateCli>(&self, cli: &C) -> Result<(), Error> { + let suri = utils::read_uri(self.suri.as_ref())?; + let base_path = self + .shared_params + .base_path() + .unwrap_or_else(|| BasePath::from_project("", "", &C::executable_name())); + let chain_id = self.shared_params.chain_id(self.shared_params.is_dev()); + let chain_spec = cli.load_spec(&chain_id)?; + let config_dir = base_path.config_dir(chain_spec.id()); + + let mut public_keys = Vec::with_capacity(128); + for (key_type_id, crypto_scheme) in KEY_TYPES { + let (keystore, public) = match self.keystore_params.keystore_config(&config_dir)? { + (_, KeystoreConfig::Path { path, password }) => { + let public = + with_crypto_scheme!(crypto_scheme, to_vec(&suri, password.clone()))?; + let keystore: SyncCryptoStorePtr = + Arc::new(LocalKeystore::open(path, password)?); + (keystore, public) + } + _ => unreachable!("keystore_config always returns path and password; qed"), + }; + + SyncCryptoStore::insert_unknown(&*keystore, key_type_id, &suri, &public[..]) + .map_err(|_| Error::KeyStoreOperation)?; + + public_keys.extend_from_slice(&public[..]); + } + + println!("Session Keys: 0x{}", hex::encode(public_keys)); + + Ok(()) + } +} + +fn to_vec<P: sp_core::Pair>(uri: &str, pass: Option<SecretString>) -> Result<Vec<u8>, Error> { + let p = utils::pair_from_suri::<P>(uri, pass)?; + Ok(p.public().as_ref().to_vec()) +}