mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-04-28 01:38:04 +00:00
2bc4ed1153
As discovered during investigation of https://github.com/paritytech/polkadot-sdk/issues/3314 and https://github.com/paritytech/polkadot-sdk/issues/3673 there are active validators which accidentally might change their network key during restart, that's not a safe operation when you are in the active set because of distributed nature of DHT, so the old records would still exist in the network until they expire 36h, so unless they have a good reason validators should avoid changing their key when they restart their nodes. There is an effort in parallel to improve this situation https://github.com/paritytech/polkadot-sdk/pull/3786, but those changes are way more intrusive and will need more rigorous testing, additionally they will reduce the time to less than 36h, but the propagation won't be instant anyway, so not changing your network during restart should be the safest way to run your node, unless you have a really good reason to change it. ## Proposal 1. Do not auto-generate the network if the network file does not exist in the provided path. Nodes where the key file does not exist will get the following error: ``` Error: 0: Starting an authorithy without network key in /home/alexggh/.local/share/polkadot/chains/ksmcc3/network/secret_ed25519. This is not a safe operation because the old identity still lives in the dht for 36 hours. Because of it your node might suffer from not being properly connected to other nodes for validation purposes. If it is the first time running your node you could use one of the following methods. 1. Pass --unsafe-force-node-key-generation and make sure you remove it for subsequent node restarts 2. Separetly generate the key with: polkadot key generate-node-key --file <YOUR_PATH_TO_NODE_KEY> ``` 2. Add an explicit parameters for nodes that do want to change their network despite the warnings or if they run the node for the first time. `--unsafe-force-node-key-generation` 3. For `polkadot key generate-node-key` add two new mutually exclusive parameters `base_path` and `default_base_path` to help with the key generation in the same path the polkadot main command would expect it. 4. Modify the installation scripts to auto-generate a key in default path if one was not present already there, this should help with making the executable work out of the box after an instalation. ## Notes Nodes that do not have already the key persisted will fail to start after this change, however I do consider that better than the current situation where they start but they silently hide that they might not be properly connected to their peers. ## TODO - [x] Make sure only nodes that are authorities on producation chains will be affected by this restrictions. - [x] Proper PRDOC, to make sure node operators are aware this is coming. --------- Signed-off-by: Alexandru Gheorghe <alexandru.gheorghe@parity.io> Co-authored-by: Dmitry Markin <dmitry@markin.tech> Co-authored-by: s0me0ne-unkn0wn <48632512+s0me0ne-unkn0wn@users.noreply.github.com> Co-authored-by: Bastian Köcher <git@kchr.de>
187 lines
5.7 KiB
Rust
187 lines
5.7 KiB
Rust
// This file is part of Substrate.
|
|
|
|
// Copyright (C) Parity Technologies (UK) Ltd.
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
//! Implementation of the `generate-node-key` subcommand
|
|
|
|
use crate::{build_network_key_dir_or_default, Error, NODE_KEY_ED25519_FILE};
|
|
use clap::{Args, Parser};
|
|
use libp2p_identity::{ed25519, Keypair};
|
|
use sc_service::BasePath;
|
|
use std::{
|
|
fs,
|
|
io::{self, Write},
|
|
path::PathBuf,
|
|
};
|
|
|
|
/// Common arguments accross all generate key commands, subkey and node.
|
|
#[derive(Debug, Args, Clone)]
|
|
pub struct GenerateKeyCmdCommon {
|
|
/// Name of file to save secret key to.
|
|
/// If not given, the secret key is printed to stdout.
|
|
#[arg(long)]
|
|
file: Option<PathBuf>,
|
|
|
|
/// The output is in raw binary format.
|
|
/// If not given, the output is written as an hex encoded string.
|
|
#[arg(long)]
|
|
bin: bool,
|
|
}
|
|
|
|
/// The `generate-node-key` command
|
|
#[derive(Debug, Clone, Parser)]
|
|
#[command(
|
|
name = "generate-node-key",
|
|
about = "Generate a random node key, write it to a file or stdout \
|
|
and write the corresponding peer-id to stderr"
|
|
)]
|
|
pub struct GenerateNodeKeyCmd {
|
|
#[clap(flatten)]
|
|
pub common: GenerateKeyCmdCommon,
|
|
/// Specify the chain specification.
|
|
///
|
|
/// It can be any of the predefined chains like dev, local, staging, polkadot, kusama.
|
|
#[arg(long, value_name = "CHAIN_SPEC")]
|
|
pub chain: Option<String>,
|
|
/// A directory where the key should be saved. If a key already
|
|
/// exists in the directory, it won't be overwritten.
|
|
#[arg(long, conflicts_with_all = ["file", "default_base_path"])]
|
|
base_path: Option<PathBuf>,
|
|
|
|
/// Save the key in the default directory. If a key already
|
|
/// exists in the directory, it won't be overwritten.
|
|
#[arg(long, conflicts_with_all = ["base_path", "file"])]
|
|
default_base_path: bool,
|
|
}
|
|
|
|
impl GenerateKeyCmdCommon {
|
|
/// Run the command
|
|
pub fn run(&self) -> Result<(), Error> {
|
|
generate_key(&self.file, self.bin, None, &None, false, None)
|
|
}
|
|
}
|
|
|
|
impl GenerateNodeKeyCmd {
|
|
/// Run the command
|
|
pub fn run(&self, chain_spec_id: &str, executable_name: &String) -> Result<(), Error> {
|
|
generate_key(
|
|
&self.common.file,
|
|
self.common.bin,
|
|
Some(chain_spec_id),
|
|
&self.base_path,
|
|
self.default_base_path,
|
|
Some(executable_name),
|
|
)
|
|
}
|
|
}
|
|
|
|
// Utility function for generating a key based on the provided CLI arguments
|
|
//
|
|
// `file` - Name of file to save secret key to
|
|
// `bin`
|
|
fn generate_key(
|
|
file: &Option<PathBuf>,
|
|
bin: bool,
|
|
chain_spec_id: Option<&str>,
|
|
base_path: &Option<PathBuf>,
|
|
default_base_path: bool,
|
|
executable_name: Option<&String>,
|
|
) -> Result<(), Error> {
|
|
let keypair = ed25519::Keypair::generate();
|
|
|
|
let secret = keypair.secret();
|
|
|
|
let file_data = if bin {
|
|
secret.as_ref().to_owned()
|
|
} else {
|
|
array_bytes::bytes2hex("", secret).into_bytes()
|
|
};
|
|
|
|
match (file, base_path, default_base_path) {
|
|
(Some(file), None, false) => fs::write(file, file_data)?,
|
|
(None, Some(_), false) | (None, None, true) => {
|
|
let network_path = build_network_key_dir_or_default(
|
|
base_path.clone().map(BasePath::new),
|
|
chain_spec_id.unwrap_or_default(),
|
|
executable_name.ok_or(Error::Input("Executable name not provided".into()))?,
|
|
);
|
|
|
|
fs::create_dir_all(network_path.as_path())?;
|
|
|
|
let key_path = network_path.join(NODE_KEY_ED25519_FILE);
|
|
if key_path.exists() {
|
|
eprintln!("Skip generation, a key already exists in {:?}", key_path);
|
|
return Err(Error::KeyAlreadyExistsInPath(key_path));
|
|
} else {
|
|
eprintln!("Generating key in {:?}", key_path);
|
|
fs::write(key_path, file_data)?
|
|
}
|
|
},
|
|
(None, None, false) => io::stdout().lock().write_all(&file_data)?,
|
|
(_, _, _) => {
|
|
// This should not happen, arguments are marked as mutually exclusive.
|
|
return Err(Error::Input("Mutually exclusive arguments provided".into()));
|
|
},
|
|
}
|
|
|
|
eprintln!("{}", Keypair::from(keypair).public().to_peer_id());
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[cfg(test)]
|
|
pub mod tests {
|
|
use crate::DEFAULT_NETWORK_CONFIG_PATH;
|
|
|
|
use super::*;
|
|
use std::io::Read;
|
|
use tempfile::Builder;
|
|
|
|
#[test]
|
|
fn generate_node_key() {
|
|
let mut file = Builder::new().prefix("keyfile").tempfile().unwrap();
|
|
let file_path = file.path().display().to_string();
|
|
let generate = GenerateNodeKeyCmd::parse_from(&["generate-node-key", "--file", &file_path]);
|
|
assert!(generate.run("test", &String::from("test")).is_ok());
|
|
let mut buf = String::new();
|
|
assert!(file.read_to_string(&mut buf).is_ok());
|
|
assert!(array_bytes::hex2bytes(&buf).is_ok());
|
|
}
|
|
|
|
#[test]
|
|
fn generate_node_key_base_path() {
|
|
let base_dir = Builder::new().prefix("keyfile").tempdir().unwrap();
|
|
let key_path = base_dir
|
|
.path()
|
|
.join("chains/test_id/")
|
|
.join(DEFAULT_NETWORK_CONFIG_PATH)
|
|
.join(NODE_KEY_ED25519_FILE);
|
|
let base_path = base_dir.path().display().to_string();
|
|
let generate =
|
|
GenerateNodeKeyCmd::parse_from(&["generate-node-key", "--base-path", &base_path]);
|
|
assert!(generate.run("test_id", &String::from("test")).is_ok());
|
|
let buf = fs::read_to_string(key_path.as_path()).unwrap();
|
|
assert!(array_bytes::hex2bytes(&buf).is_ok());
|
|
|
|
assert!(generate.run("test_id", &String::from("test")).is_err());
|
|
let new_buf = fs::read_to_string(key_path).unwrap();
|
|
assert_eq!(
|
|
array_bytes::hex2bytes(&new_buf).unwrap(),
|
|
array_bytes::hex2bytes(&buf).unwrap()
|
|
);
|
|
}
|
|
}
|