Skip to content
This repository was archived by the owner on Nov 15, 2023. It is now read-only.

Commit 34a1362

Browse files
committed
Merge branch 'master' of github.com:paritytech/substrate
2 parents bb77d05 + eaa3e6d commit 34a1362

File tree

43 files changed

+1555
-1494
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+1555
-1494
lines changed

Cargo.lock

Lines changed: 111 additions & 83 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

core/cli/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,6 @@ substrate-telemetry = { path = "../../core/telemetry" }
3333
keyring = { package = "substrate-keyring", path = "../keyring" }
3434
names = "0.11.0"
3535
structopt = "0.2"
36+
37+
[dev-dependencies]
38+
tempdir = "0.3"

core/cli/src/lib.rs

Lines changed: 194 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,8 @@ use service::{
3232
FactoryGenesis, PruningMode, ChainSpec,
3333
};
3434
use network::{
35-
Protocol, config::{NetworkConfiguration, NonReservedPeerMode, Secret},
35+
self, multiaddr::Protocol,
36+
config::{NetworkConfiguration, NonReservedPeerMode, NodeKeyConfig},
3637
build_multiaddr,
3738
};
3839
use primitives::H256;
@@ -50,6 +51,7 @@ pub use structopt::clap::App;
5051
use params::{
5152
RunCmd, PurgeChainCmd, RevertCmd, ImportBlocksCmd, ExportBlocksCmd, BuildSpecCmd,
5253
NetworkConfigurationParams, SharedParams, MergeParameters, TransactionPoolParams,
54+
NodeKeyParams, NodeKeyType
5355
};
5456
pub use params::{NoCustom, CoreParams};
5557
pub use traits::{GetLogFilter, AugmentClap};
@@ -61,7 +63,18 @@ use lazy_static::lazy_static;
6163
use futures::Future;
6264
use substrate_telemetry::TelemetryEndpoints;
6365

64-
const MAX_NODE_NAME_LENGTH: usize = 32;
66+
/// The maximum number of characters for a node name.
67+
const NODE_NAME_MAX_LENGTH: usize = 32;
68+
69+
/// The file name of the node's Secp256k1 secret key inside the chain-specific
70+
/// network config directory, if neither `--node-key` nor `--node-key-file`
71+
/// is specified in combination with `--node-key-type=secp256k1`.
72+
const NODE_KEY_SECP256K1_FILE: &str = "secret";
73+
74+
/// The file name of the node's Ed25519 secret key inside the chain-specific
75+
/// network config directory, if neither `--node-key` nor `--node-key-file`
76+
/// is specified in combination with `--node-key-type=ed25519`.
77+
const NODE_KEY_ED25519_FILE: &str = "secret_ed25519";
6578

6679
/// Executable version. Used to pass version information from the root crate.
6780
pub struct VersionInfo {
@@ -101,7 +114,7 @@ fn generate_node_name() -> String {
101114
let node_name = Generator::with_naming(Name::Numbered).next().unwrap();
102115
let count = node_name.chars().count();
103116

104-
if count < MAX_NODE_NAME_LENGTH {
117+
if count < NODE_NAME_MAX_LENGTH {
105118
break node_name
106119
}
107120
};
@@ -133,14 +146,14 @@ fn base_path(cli: &SharedParams, version: &VersionInfo) -> PathBuf {
133146
)
134147
}
135148

136-
fn create_input_err<T: Into<String>>(msg: T) -> error::Error {
149+
fn input_err<T: Into<String>>(msg: T) -> error::Error {
137150
error::ErrorKind::Input(msg.into()).into()
138151
}
139152

140153
/// Check whether a node name is considered as valid
141154
fn is_node_name_valid(_name: &str) -> Result<(), &str> {
142155
let name = _name.to_string();
143-
if name.chars().count() >= MAX_NODE_NAME_LENGTH {
156+
if name.chars().count() >= NODE_NAME_MAX_LENGTH {
144157
return Err("Node name too long");
145158
}
146159

@@ -231,14 +244,60 @@ where
231244
}
232245
}
233246

234-
fn parse_node_key(key: Option<String>) -> error::Result<Option<Secret>> {
235-
match key.map(|k| H256::from_str(&k)) {
236-
Some(Ok(secret)) => Ok(Some(secret.into())),
237-
Some(Err(err)) => Err(create_input_err(format!("Error parsing node key: {}", err))),
238-
None => Ok(None),
247+
/// Create a `NodeKeyConfig` from the given `NodeKeyParams` in the context
248+
/// of an optional network config storage directory.
249+
fn node_key_config<P>(params: NodeKeyParams, net_config_dir: &Option<P>)
250+
-> error::Result<NodeKeyConfig>
251+
where
252+
P: AsRef<Path>
253+
{
254+
match params.node_key_type {
255+
NodeKeyType::Secp256k1 =>
256+
params.node_key.as_ref().map(parse_secp256k1_secret).unwrap_or_else(||
257+
Ok(params.node_key_file
258+
.or_else(|| net_config_file(net_config_dir, NODE_KEY_SECP256K1_FILE))
259+
.map(network::Secret::File)
260+
.unwrap_or(network::Secret::New)))
261+
.map(NodeKeyConfig::Secp256k1),
262+
263+
NodeKeyType::Ed25519 =>
264+
params.node_key.as_ref().map(parse_ed25519_secret).unwrap_or_else(||
265+
Ok(params.node_key_file
266+
.or_else(|| net_config_file(net_config_dir, NODE_KEY_ED25519_FILE))
267+
.map(network::Secret::File)
268+
.unwrap_or(network::Secret::New)))
269+
.map(NodeKeyConfig::Ed25519)
239270
}
240271
}
241272

273+
fn net_config_file<P>(net_config_dir: &Option<P>, name: &str) -> Option<PathBuf>
274+
where
275+
P: AsRef<Path>
276+
{
277+
net_config_dir.as_ref().map(|d| d.as_ref().join(name))
278+
}
279+
280+
/// Create an error caused by an invalid node key argument.
281+
fn invalid_node_key(e: impl std::fmt::Display) -> error::Error {
282+
input_err(format!("Invalid node key: {}", e))
283+
}
284+
285+
/// Parse a Secp256k1 secret key from a hex string into a `network::Secret`.
286+
fn parse_secp256k1_secret(hex: &String) -> error::Result<network::Secp256k1Secret> {
287+
H256::from_str(hex).map_err(invalid_node_key).and_then(|bytes|
288+
network::identity::secp256k1::SecretKey::from_bytes(bytes)
289+
.map(network::Secret::Input)
290+
.map_err(invalid_node_key))
291+
}
292+
293+
/// Parse a Ed25519 secret key from a hex string into a `network::Secret`.
294+
fn parse_ed25519_secret(hex: &String) -> error::Result<network::Ed25519Secret> {
295+
H256::from_str(&hex).map_err(invalid_node_key).and_then(|bytes|
296+
network::identity::ed25519::SecretKey::from_bytes(bytes)
297+
.map(network::Secret::Input)
298+
.map_err(invalid_node_key))
299+
}
300+
242301
/// Fill the given `PoolConfiguration` by looking at the cli parameters.
243302
fn fill_transaction_pool_configuration<F: ServiceFactory>(
244303
options: &mut FactoryFullConfiguration<F>,
@@ -295,7 +354,7 @@ fn fill_network_configuration(
295354
config.public_addresses = Vec::new();
296355

297356
config.client_version = client_id;
298-
config.use_secret = parse_node_key(cli.node_key)?;
357+
config.node_key = node_key_config(cli.node_key_params, &config.net_config_path)?;
299358

300359
config.in_peers = cli.in_peers;
301360
config.out_peers = cli.out_peers;
@@ -324,7 +383,7 @@ where
324383
match is_node_name_valid(&config.name) {
325384
Ok(_) => (),
326385
Err(msg) => bail!(
327-
create_input_err(
386+
input_err(
328387
format!("Invalid node name '{}'. Reason: {}. If unsure, use none.",
329388
config.name,
330389
msg
@@ -347,7 +406,7 @@ where
347406
Some(ref s) if s == "archive" => PruningMode::ArchiveAll,
348407
None => PruningMode::default(),
349408
Some(s) => PruningMode::keep_blocks(
350-
s.parse().map_err(|_| create_input_err("Invalid pruning mode specified"))?
409+
s.parse().map_err(|_| input_err("Invalid pruning mode specified"))?
351410
),
352411
};
353412

@@ -443,31 +502,27 @@ where
443502
// 9926-9949 Unassigned
444503

445504
fn with_default_boot_node<F>(
446-
mut spec: ChainSpec<FactoryGenesis<F>>,
447-
cli: &BuildSpecCmd,
505+
spec: &mut ChainSpec<FactoryGenesis<F>>,
506+
cli: BuildSpecCmd,
448507
version: &VersionInfo,
449-
) -> error::Result<ChainSpec<FactoryGenesis<F>>>
508+
) -> error::Result<()>
450509
where
451510
F: ServiceFactory
452511
{
453512
if spec.boot_nodes().is_empty() {
454-
let network_path =
455-
Some(network_path(&base_path(&cli.shared_params, version), spec.id()).to_string_lossy().into());
456-
let network_key = parse_node_key(cli.node_key.clone())?;
457-
458-
let network_keys =
459-
network::obtain_private_key(&network_key, &network_path)
460-
.map_err(|err| format!("Error obtaining network key: {}", err))?;
461-
462-
let peer_id = network_keys.to_peer_id();
513+
let base_path = base_path(&cli.shared_params, version);
514+
let storage_path = network_path(&base_path, spec.id());
515+
let node_key = node_key_config(cli.node_key_params, &Some(storage_path))?;
516+
let keys = node_key.into_keypair()?;
517+
let peer_id = keys.public().into_peer_id();
463518
let addr = build_multiaddr![
464519
Ip4([127, 0, 0, 1]),
465520
Tcp(30333u16),
466521
P2p(peer_id)
467522
];
468523
spec.add_boot_node(addr)
469524
}
470-
Ok(spec)
525+
Ok(())
471526
}
472527

473528
fn build_spec<F, S>(
@@ -480,9 +535,10 @@ where
480535
S: FnOnce(&str) -> Result<Option<ChainSpec<FactoryGenesis<F>>>, String>,
481536
{
482537
info!("Building chain spec");
483-
let spec = load_spec(&cli.shared_params, spec_factory)?;
484-
let spec = with_default_boot_node::<F>(spec, &cli, version)?;
485-
let json = service::chain_ops::build_spec::<FactoryGenesis<F>>(spec, cli.raw)?;
538+
let raw_output = cli.raw;
539+
let mut spec = load_spec(&cli.shared_params, spec_factory)?;
540+
with_default_boot_node::<F>(&mut spec, cli, version)?;
541+
let json = service::chain_ops::build_spec::<FactoryGenesis<F>>(spec, raw_output)?;
486542

487543
print!("{}", json);
488544

@@ -707,6 +763,8 @@ fn kill_color(s: &str) -> String {
707763
#[cfg(test)]
708764
mod tests {
709765
use super::*;
766+
use tempdir::TempDir;
767+
use network::identity::{secp256k1, ed25519};
710768

711769
#[test]
712770
fn tests_node_name_good() {
@@ -722,4 +780,111 @@ mod tests {
722780
assert!(is_node_name_valid("www.visit.me").is_err());
723781
assert!(is_node_name_valid("email@domain").is_err());
724782
}
783+
784+
#[test]
785+
fn test_node_key_config_input() {
786+
fn secret_input(net_config_dir: Option<String>) -> error::Result<()> {
787+
NodeKeyType::variants().into_iter().try_for_each(|t| {
788+
let node_key_type = NodeKeyType::from_str(t).unwrap();
789+
let sk = match node_key_type {
790+
NodeKeyType::Secp256k1 => secp256k1::SecretKey::generate().as_ref().to_vec(),
791+
NodeKeyType::Ed25519 => ed25519::SecretKey::generate().as_ref().to_vec()
792+
};
793+
let params = NodeKeyParams {
794+
node_key_type,
795+
node_key: Some(format!("{:x}", H256::from_slice(sk.as_ref()))),
796+
node_key_file: None
797+
};
798+
node_key_config(params, &net_config_dir).and_then(|c| match c {
799+
NodeKeyConfig::Secp256k1(network::Secret::Input(ref ski))
800+
if node_key_type == NodeKeyType::Secp256k1 &&
801+
&sk[..] == ski.as_ref() => Ok(()),
802+
NodeKeyConfig::Ed25519(network::Secret::Input(ref ski))
803+
if node_key_type == NodeKeyType::Ed25519 &&
804+
&sk[..] == ski.as_ref() => Ok(()),
805+
_ => Err(input_err("Unexpected node key config"))
806+
})
807+
})
808+
}
809+
810+
assert!(secret_input(None).is_ok());
811+
assert!(secret_input(Some("x".to_string())).is_ok());
812+
}
813+
814+
#[test]
815+
fn test_node_key_config_file() {
816+
fn secret_file(net_config_dir: Option<String>) -> error::Result<()> {
817+
NodeKeyType::variants().into_iter().try_for_each(|t| {
818+
let node_key_type = NodeKeyType::from_str(t).unwrap();
819+
let tmp = TempDir::new("alice")?;
820+
let file = tmp.path().join(format!("{}_mysecret", t)).to_path_buf();
821+
let params = NodeKeyParams {
822+
node_key_type,
823+
node_key: None,
824+
node_key_file: Some(file.clone())
825+
};
826+
node_key_config(params, &net_config_dir).and_then(|c| match c {
827+
NodeKeyConfig::Secp256k1(network::Secret::File(ref f))
828+
if node_key_type == NodeKeyType::Secp256k1 && f == &file => Ok(()),
829+
NodeKeyConfig::Ed25519(network::Secret::File(ref f))
830+
if node_key_type == NodeKeyType::Ed25519 && f == &file => Ok(()),
831+
_ => Err(input_err("Unexpected node key config"))
832+
})
833+
})
834+
}
835+
836+
assert!(secret_file(None).is_ok());
837+
assert!(secret_file(Some("x".to_string())).is_ok());
838+
}
839+
840+
#[test]
841+
fn test_node_key_config_default() {
842+
fn with_def_params<F>(f: F) -> error::Result<()>
843+
where
844+
F: Fn(NodeKeyParams) -> error::Result<()>
845+
{
846+
NodeKeyType::variants().into_iter().try_for_each(|t| {
847+
let node_key_type = NodeKeyType::from_str(t).unwrap();
848+
f(NodeKeyParams {
849+
node_key_type,
850+
node_key: None,
851+
node_key_file: None
852+
})
853+
})
854+
}
855+
856+
fn no_config_dir() -> error::Result<()> {
857+
with_def_params(|params| {
858+
let typ = params.node_key_type;
859+
node_key_config::<String>(params, &None)
860+
.and_then(|c| match c {
861+
NodeKeyConfig::Secp256k1(network::Secret::New)
862+
if typ == NodeKeyType::Secp256k1 => Ok(()),
863+
NodeKeyConfig::Ed25519(network::Secret::New)
864+
if typ == NodeKeyType::Ed25519 => Ok(()),
865+
_ => Err(input_err("Unexpected node key config"))
866+
})
867+
})
868+
}
869+
870+
fn some_config_dir(net_config_dir: String) -> error::Result<()> {
871+
with_def_params(|params| {
872+
let dir = PathBuf::from(net_config_dir.clone());
873+
let typ = params.node_key_type;
874+
node_key_config(params, &Some(net_config_dir.clone()))
875+
.and_then(move |c| match c {
876+
NodeKeyConfig::Secp256k1(network::Secret::File(ref f))
877+
if typ == NodeKeyType::Secp256k1 &&
878+
f == &dir.join(NODE_KEY_SECP256K1_FILE) => Ok(()),
879+
NodeKeyConfig::Ed25519(network::Secret::File(ref f))
880+
if typ == NodeKeyType::Ed25519 &&
881+
f == &dir.join(NODE_KEY_ED25519_FILE) => Ok(()),
882+
_ => Err(input_err("Unexpected node key config"))
883+
})
884+
})
885+
}
886+
887+
assert!(no_config_dir().is_ok());
888+
assert!(some_config_dir("x".to_string()).is_ok());
889+
}
725890
}

0 commit comments

Comments
 (0)