libp2p-0.5.0 (#1971)

* Update libp2p. Add support for ed25519 node (network) keys.

  * Update networking to the changes from https://github.com/libp2p/rust-libp2p/pull/972.
  * Add support for using ed25519 keys for libp2p networking.
  * Add support for reading libp2p secret keys from (external) files.

* Adapt to changes from https://github.com/libp2p/rust-libp2p/pull/992

* More tests.

* Cosmetics

* Deduplicate tests.

* Remove quickcheck from tests that don't use extra random inputs.

* Remove quickcheck.

* Swap new/default impls for NetworkConfiguration.

* Use libp2p-0.5.0 from crates.io.

* Post-rebase update.

* Remove unnecessary wildcard pattern.

* Combine two overlapping tests.
This commit is contained in:
Roman Borschel
2019-03-19 11:16:25 +01:00
committed by GitHub
parent 648b11e118
commit 57387ef585
19 changed files with 757 additions and 528 deletions
+194 -29
View File
@@ -32,7 +32,8 @@ use service::{
FactoryGenesis, PruningMode, ChainSpec,
};
use network::{
Protocol, config::{NetworkConfiguration, NonReservedPeerMode, Secret},
self, multiaddr::Protocol,
config::{NetworkConfiguration, NonReservedPeerMode, NodeKeyConfig},
build_multiaddr,
};
use primitives::H256;
@@ -50,6 +51,7 @@ pub use structopt::clap::App;
use params::{
RunCmd, PurgeChainCmd, RevertCmd, ImportBlocksCmd, ExportBlocksCmd, BuildSpecCmd,
NetworkConfigurationParams, SharedParams, MergeParameters, TransactionPoolParams,
NodeKeyParams, NodeKeyType
};
pub use params::{NoCustom, CoreParams};
pub use traits::{GetLogFilter, AugmentClap};
@@ -61,7 +63,18 @@ use lazy_static::lazy_static;
use futures::Future;
use substrate_telemetry::TelemetryEndpoints;
const MAX_NODE_NAME_LENGTH: usize = 32;
/// The maximum number of characters for a node name.
const NODE_NAME_MAX_LENGTH: usize = 32;
/// The file name of the node's Secp256k1 secret key inside the chain-specific
/// network config directory, if neither `--node-key` nor `--node-key-file`
/// is specified in combination with `--node-key-type=secp256k1`.
const NODE_KEY_SECP256K1_FILE: &str = "secret";
/// The file name of the node's Ed25519 secret key inside the chain-specific
/// network config directory, if neither `--node-key` nor `--node-key-file`
/// is specified in combination with `--node-key-type=ed25519`.
const NODE_KEY_ED25519_FILE: &str = "secret_ed25519";
/// Executable version. Used to pass version information from the root crate.
pub struct VersionInfo {
@@ -101,7 +114,7 @@ fn generate_node_name() -> String {
let node_name = Generator::with_naming(Name::Numbered).next().unwrap();
let count = node_name.chars().count();
if count < MAX_NODE_NAME_LENGTH {
if count < NODE_NAME_MAX_LENGTH {
break node_name
}
};
@@ -133,14 +146,14 @@ fn base_path(cli: &SharedParams, version: &VersionInfo) -> PathBuf {
)
}
fn create_input_err<T: Into<String>>(msg: T) -> error::Error {
fn input_err<T: Into<String>>(msg: T) -> error::Error {
error::ErrorKind::Input(msg.into()).into()
}
/// Check whether a node name is considered as valid
fn is_node_name_valid(_name: &str) -> Result<(), &str> {
let name = _name.to_string();
if name.chars().count() >= MAX_NODE_NAME_LENGTH {
if name.chars().count() >= NODE_NAME_MAX_LENGTH {
return Err("Node name too long");
}
@@ -231,14 +244,60 @@ where
}
}
fn parse_node_key(key: Option<String>) -> error::Result<Option<Secret>> {
match key.map(|k| H256::from_str(&k)) {
Some(Ok(secret)) => Ok(Some(secret.into())),
Some(Err(err)) => Err(create_input_err(format!("Error parsing node key: {}", err))),
None => Ok(None),
/// Create a `NodeKeyConfig` from the given `NodeKeyParams` in the context
/// of an optional network config storage directory.
fn node_key_config<P>(params: NodeKeyParams, net_config_dir: &Option<P>)
-> error::Result<NodeKeyConfig>
where
P: AsRef<Path>
{
match params.node_key_type {
NodeKeyType::Secp256k1 =>
params.node_key.as_ref().map(parse_secp256k1_secret).unwrap_or_else(||
Ok(params.node_key_file
.or_else(|| net_config_file(net_config_dir, NODE_KEY_SECP256K1_FILE))
.map(network::Secret::File)
.unwrap_or(network::Secret::New)))
.map(NodeKeyConfig::Secp256k1),
NodeKeyType::Ed25519 =>
params.node_key.as_ref().map(parse_ed25519_secret).unwrap_or_else(||
Ok(params.node_key_file
.or_else(|| net_config_file(net_config_dir, NODE_KEY_ED25519_FILE))
.map(network::Secret::File)
.unwrap_or(network::Secret::New)))
.map(NodeKeyConfig::Ed25519)
}
}
fn net_config_file<P>(net_config_dir: &Option<P>, name: &str) -> Option<PathBuf>
where
P: AsRef<Path>
{
net_config_dir.as_ref().map(|d| d.as_ref().join(name))
}
/// Create an error caused by an invalid node key argument.
fn invalid_node_key(e: impl std::fmt::Display) -> error::Error {
input_err(format!("Invalid node key: {}", e))
}
/// Parse a Secp256k1 secret key from a hex string into a `network::Secret`.
fn parse_secp256k1_secret(hex: &String) -> error::Result<network::Secp256k1Secret> {
H256::from_str(hex).map_err(invalid_node_key).and_then(|bytes|
network::identity::secp256k1::SecretKey::from_bytes(bytes)
.map(network::Secret::Input)
.map_err(invalid_node_key))
}
/// Parse a Ed25519 secret key from a hex string into a `network::Secret`.
fn parse_ed25519_secret(hex: &String) -> error::Result<network::Ed25519Secret> {
H256::from_str(&hex).map_err(invalid_node_key).and_then(|bytes|
network::identity::ed25519::SecretKey::from_bytes(bytes)
.map(network::Secret::Input)
.map_err(invalid_node_key))
}
/// Fill the given `PoolConfiguration` by looking at the cli parameters.
fn fill_transaction_pool_configuration<F: ServiceFactory>(
options: &mut FactoryFullConfiguration<F>,
@@ -295,7 +354,7 @@ fn fill_network_configuration(
config.public_addresses = Vec::new();
config.client_version = client_id;
config.use_secret = parse_node_key(cli.node_key)?;
config.node_key = node_key_config(cli.node_key_params, &config.net_config_path)?;
config.in_peers = cli.in_peers;
config.out_peers = cli.out_peers;
@@ -324,7 +383,7 @@ where
match is_node_name_valid(&config.name) {
Ok(_) => (),
Err(msg) => bail!(
create_input_err(
input_err(
format!("Invalid node name '{}'. Reason: {}. If unsure, use none.",
config.name,
msg
@@ -347,7 +406,7 @@ where
Some(ref s) if s == "archive" => PruningMode::ArchiveAll,
None => PruningMode::default(),
Some(s) => PruningMode::keep_blocks(
s.parse().map_err(|_| create_input_err("Invalid pruning mode specified"))?
s.parse().map_err(|_| input_err("Invalid pruning mode specified"))?
),
};
@@ -443,23 +502,19 @@ where
// 9926-9949 Unassigned
fn with_default_boot_node<F>(
mut spec: ChainSpec<FactoryGenesis<F>>,
cli: &BuildSpecCmd,
spec: &mut ChainSpec<FactoryGenesis<F>>,
cli: BuildSpecCmd,
version: &VersionInfo,
) -> error::Result<ChainSpec<FactoryGenesis<F>>>
) -> error::Result<()>
where
F: ServiceFactory
{
if spec.boot_nodes().is_empty() {
let network_path =
Some(network_path(&base_path(&cli.shared_params, version), spec.id()).to_string_lossy().into());
let network_key = parse_node_key(cli.node_key.clone())?;
let network_keys =
network::obtain_private_key(&network_key, &network_path)
.map_err(|err| format!("Error obtaining network key: {}", err))?;
let peer_id = network_keys.to_peer_id();
let base_path = base_path(&cli.shared_params, version);
let storage_path = network_path(&base_path, spec.id());
let node_key = node_key_config(cli.node_key_params, &Some(storage_path))?;
let keys = node_key.into_keypair()?;
let peer_id = keys.public().into_peer_id();
let addr = build_multiaddr![
Ip4([127, 0, 0, 1]),
Tcp(30333u16),
@@ -467,7 +522,7 @@ where
];
spec.add_boot_node(addr)
}
Ok(spec)
Ok(())
}
fn build_spec<F, S>(
@@ -480,9 +535,10 @@ where
S: FnOnce(&str) -> Result<Option<ChainSpec<FactoryGenesis<F>>>, String>,
{
info!("Building chain spec");
let spec = load_spec(&cli.shared_params, spec_factory)?;
let spec = with_default_boot_node::<F>(spec, &cli, version)?;
let json = service::chain_ops::build_spec::<FactoryGenesis<F>>(spec, cli.raw)?;
let raw_output = cli.raw;
let mut spec = load_spec(&cli.shared_params, spec_factory)?;
with_default_boot_node::<F>(&mut spec, cli, version)?;
let json = service::chain_ops::build_spec::<FactoryGenesis<F>>(spec, raw_output)?;
print!("{}", json);
@@ -707,6 +763,8 @@ fn kill_color(s: &str) -> String {
#[cfg(test)]
mod tests {
use super::*;
use tempdir::TempDir;
use network::identity::{secp256k1, ed25519};
#[test]
fn tests_node_name_good() {
@@ -722,4 +780,111 @@ mod tests {
assert!(is_node_name_valid("www.visit.me").is_err());
assert!(is_node_name_valid("email@domain").is_err());
}
#[test]
fn test_node_key_config_input() {
fn secret_input(net_config_dir: Option<String>) -> error::Result<()> {
NodeKeyType::variants().into_iter().try_for_each(|t| {
let node_key_type = NodeKeyType::from_str(t).unwrap();
let sk = match node_key_type {
NodeKeyType::Secp256k1 => secp256k1::SecretKey::generate().as_ref().to_vec(),
NodeKeyType::Ed25519 => ed25519::SecretKey::generate().as_ref().to_vec()
};
let params = NodeKeyParams {
node_key_type,
node_key: Some(format!("{:x}", H256::from_slice(sk.as_ref()))),
node_key_file: None
};
node_key_config(params, &net_config_dir).and_then(|c| match c {
NodeKeyConfig::Secp256k1(network::Secret::Input(ref ski))
if node_key_type == NodeKeyType::Secp256k1 &&
&sk[..] == ski.as_ref() => Ok(()),
NodeKeyConfig::Ed25519(network::Secret::Input(ref ski))
if node_key_type == NodeKeyType::Ed25519 &&
&sk[..] == ski.as_ref() => Ok(()),
_ => Err(input_err("Unexpected node key config"))
})
})
}
assert!(secret_input(None).is_ok());
assert!(secret_input(Some("x".to_string())).is_ok());
}
#[test]
fn test_node_key_config_file() {
fn secret_file(net_config_dir: Option<String>) -> error::Result<()> {
NodeKeyType::variants().into_iter().try_for_each(|t| {
let node_key_type = NodeKeyType::from_str(t).unwrap();
let tmp = TempDir::new("alice")?;
let file = tmp.path().join(format!("{}_mysecret", t)).to_path_buf();
let params = NodeKeyParams {
node_key_type,
node_key: None,
node_key_file: Some(file.clone())
};
node_key_config(params, &net_config_dir).and_then(|c| match c {
NodeKeyConfig::Secp256k1(network::Secret::File(ref f))
if node_key_type == NodeKeyType::Secp256k1 && f == &file => Ok(()),
NodeKeyConfig::Ed25519(network::Secret::File(ref f))
if node_key_type == NodeKeyType::Ed25519 && f == &file => Ok(()),
_ => Err(input_err("Unexpected node key config"))
})
})
}
assert!(secret_file(None).is_ok());
assert!(secret_file(Some("x".to_string())).is_ok());
}
#[test]
fn test_node_key_config_default() {
fn with_def_params<F>(f: F) -> error::Result<()>
where
F: Fn(NodeKeyParams) -> error::Result<()>
{
NodeKeyType::variants().into_iter().try_for_each(|t| {
let node_key_type = NodeKeyType::from_str(t).unwrap();
f(NodeKeyParams {
node_key_type,
node_key: None,
node_key_file: None
})
})
}
fn no_config_dir() -> error::Result<()> {
with_def_params(|params| {
let typ = params.node_key_type;
node_key_config::<String>(params, &None)
.and_then(|c| match c {
NodeKeyConfig::Secp256k1(network::Secret::New)
if typ == NodeKeyType::Secp256k1 => Ok(()),
NodeKeyConfig::Ed25519(network::Secret::New)
if typ == NodeKeyType::Ed25519 => Ok(()),
_ => Err(input_err("Unexpected node key config"))
})
})
}
fn some_config_dir(net_config_dir: String) -> error::Result<()> {
with_def_params(|params| {
let dir = PathBuf::from(net_config_dir.clone());
let typ = params.node_key_type;
node_key_config(params, &Some(net_config_dir.clone()))
.and_then(move |c| match c {
NodeKeyConfig::Secp256k1(network::Secret::File(ref f))
if typ == NodeKeyType::Secp256k1 &&
f == &dir.join(NODE_KEY_SECP256K1_FILE) => Ok(()),
NodeKeyConfig::Ed25519(network::Secret::File(ref f))
if typ == NodeKeyType::Ed25519 &&
f == &dir.join(NODE_KEY_ED25519_FILE) => Ok(()),
_ => Err(input_err("Unexpected node key config"))
})
})
}
assert!(no_config_dir().is_ok());
assert!(some_config_dir("x".to_string()).is_ok());
}
}
+92 -9
View File
@@ -68,7 +68,7 @@ pub struct SharedParams {
#[structopt(long = "base-path", short = "d", value_name = "PATH", parse(from_os_str))]
pub base_path: Option<PathBuf>,
///Sets a custom logging filter
/// Sets a custom logging filter
#[structopt(short = "l", long = "log", value_name = "LOG_PATTERN")]
pub log: Option<String>,
}
@@ -98,10 +98,6 @@ pub struct NetworkConfigurationParams {
#[structopt(long = "port", value_name = "PORT")]
pub port: Option<u16>,
/// Specify node secret key (64-character hex string)
#[structopt(long = "node-key", value_name = "KEY")]
pub node_key: Option<String>,
/// Specify the number of outgoing connections we're trying to maintain
#[structopt(long = "out-peers", value_name = "OUT_PEERS", default_value = "25")]
pub out_peers: u32,
@@ -109,6 +105,93 @@ pub struct NetworkConfigurationParams {
/// Specify the maximum number of incoming connections we're accepting
#[structopt(long = "in-peers", value_name = "IN_PEERS", default_value = "25")]
pub in_peers: u32,
#[allow(missing_docs)]
#[structopt(flatten)]
pub node_key_params: NodeKeyParams
}
arg_enum! {
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum NodeKeyType {
Secp256k1,
Ed25519
}
}
/// Parameters used to create the `NodeKeyConfig`, which determines the keypair
/// used for libp2p networking.
#[derive(Debug, StructOpt, Clone)]
pub struct NodeKeyParams {
/// The secret key to use for libp2p networking.
///
/// The value is a string that is parsed according to the choice of
/// `--node-key-type` as follows:
///
/// `secp256k1`:
/// The value is parsed as a hex-encoded Secp256k1 32 bytes secret key,
/// i.e. 64 hex characters.
///
/// `ed25519`:
/// The value is parsed as a hex-encoded Ed25519 32 bytes secret key,
/// i.e. 64 hex characters.
///
/// The value of this option takes precedence over `--node-key-file`.
///
/// WARNING: Secrets provided as command-line arguments are easily exposed.
/// Use of this option should be limited to development and testing. To use
/// an externally managed secret key, use `--node-key-file` instead.
#[structopt(long = "node-key", value_name = "KEY")]
pub node_key: Option<String>,
/// The type of secret key to use for libp2p networking.
///
/// The secret key of the node is obtained as follows:
///
/// * If the `--node-key` option is given, the value is parsed as a secret key
/// according to the type. See the documentation for `--node-key`.
///
/// * If the `--node-key-file` option is given, the secret key is read from the
/// specified file. See the documentation for `--node-key-file`.
///
/// * Otherwise, the secret key is read from a file with a predetermined,
/// type-specific name from the chain-specific network config directory
/// inside the base directory specified by `--base-dir`. If this file does
/// not exist, it is created with a newly generated secret key of the
/// chosen type.
///
/// The node's secret key determines the corresponding public key and hence the
/// node's peer ID in the context of libp2p.
///
/// NOTE: The current default key type is `secp256k1` for a transition period only
/// but will eventually change to `ed25519` in a future release. To continue using
/// `secp256k1` keys, use `--node-key-type=secp256k1`.
#[structopt(
long = "node-key-type",
value_name = "TYPE",
raw(
possible_values = "&NodeKeyType::variants()",
case_insensitive = "true",
default_value = r#""Secp256k1""#
)
)]
pub node_key_type: NodeKeyType,
/// The file from which to read the node's secret key to use for libp2p networking.
///
/// The contents of the file are parsed according to the choice of `--node-key-type`
/// as follows:
///
/// `secp256k1`:
/// The file must contain an unencoded 32 bytes Secp256k1 secret key.
///
/// `ed25519`:
/// The file must contain an unencoded 32 bytes Ed25519 secret key.
///
/// If the file does not exist, it is created with a newly generated secret key of
/// the chosen type.
#[structopt(long = "node-key-file", value_name = "FILE")]
pub node_key_file: Option<PathBuf>
}
/// Parameters used to create the pool configuration.
@@ -169,7 +252,7 @@ pub struct RunCmd {
#[structopt(long = "name", value_name = "NAME")]
pub name: Option<String>,
/// Should not connect to the Substrate telemetry server (telemetry is on by default on global chains)
/// Disable connecting to the Substrate telemetry server (telemetry is on by default on global chains).
#[structopt(long = "no-telemetry")]
pub no_telemetry: bool,
@@ -343,9 +426,9 @@ pub struct BuildSpecCmd {
#[structopt(flatten)]
pub shared_params: SharedParams,
/// Specify node secret key (64-character hex string)
#[structopt(long = "node-key", value_name = "KEY")]
pub node_key: Option<String>,
#[allow(missing_docs)]
#[structopt(flatten)]
pub node_key_params: NodeKeyParams,
}
impl_get_log_filter!(BuildSpecCmd);