Skip to content

Commit

Permalink
client: add WebRTC transport
Browse files Browse the repository at this point in the history
Refs paritytech/smoldot#1712
Impl libp2p/rust-libp2p#2622

- WebRTC transport is enabled for non-validators and developers by default.
- The transport will generate and store the certificate, which is required for WebRTC identity, in
base dir. In the future, when a new version of `ring` library is released, the certificate will be
deterministically derived from the node's peer ID.
  • Loading branch information
melekes committed Nov 22, 2022
1 parent 0dc5358 commit c4b3dcc
Show file tree
Hide file tree
Showing 34 changed files with 2,516 additions and 1,059 deletions.
3,125 changes: 2,140 additions & 985 deletions Cargo.lock

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions Cargo.toml
Expand Up @@ -310,3 +310,6 @@ inherits = "release"
lto = "fat"
# https://doc.rust-lang.org/rustc/codegen-options/index.html#codegen-units
codegen-units = 1

[patch.crates-io]
libp2p = { version = "0.50.0", git = "https://github.com/libp2p/rust-libp2p.git", branch = "master" }
3 changes: 2 additions & 1 deletion bin/node/cli/benches/block_production.rs
Expand Up @@ -29,7 +29,7 @@ use sc_consensus::{
use sc_service::{
config::{
BlocksPruning, DatabaseSource, KeystoreConfig, NetworkConfiguration, OffchainWorkerConfig,
PruningMode, WasmExecutionMethod, WasmtimeInstantiationStrategy,
PruningMode, WasmExecutionMethod, WasmtimeInstantiationStrategy, WebRTCConfig,
},
BasePath, Configuration, Role,
};
Expand All @@ -52,6 +52,7 @@ fn new_node(tokio_handle: Handle) -> node_cli::service::NewFullBase {
Sr25519Keyring::Alice.to_seed(),
"network/test/0.1",
Default::default(),
WebRTCConfig::Ephemeral,
None,
);

Expand Down
3 changes: 2 additions & 1 deletion bin/node/cli/benches/transaction_pool.rs
Expand Up @@ -27,7 +27,7 @@ use sc_client_api::execution_extensions::ExecutionStrategies;
use sc_service::{
config::{
BlocksPruning, DatabaseSource, KeystoreConfig, NetworkConfiguration, OffchainWorkerConfig,
PruningMode, TransactionPoolOptions, WasmExecutionMethod,
PruningMode, TransactionPoolOptions, WasmExecutionMethod, WebRTCConfig,
},
BasePath, Configuration, Role,
};
Expand All @@ -46,6 +46,7 @@ fn new_node(tokio_handle: Handle) -> node_cli::service::NewFullBase {
Sr25519Keyring::Alice.to_seed(),
"network/test/0.1",
Default::default(),
WebRTCConfig::Ephemeral,
None,
);

Expand Down
2 changes: 1 addition & 1 deletion client/authority-discovery/Cargo.toml
Expand Up @@ -21,7 +21,7 @@ codec = { package = "parity-scale-codec", version = "3.0.0", default-features =
futures = "0.3.21"
futures-timer = "3.0.1"
ip_network = "0.4.1"
libp2p = { version = "0.49.0", default-features = false, features = ["kad"] }
libp2p = { version = "0.50.0", features = ["kad"] }
log = "0.4.17"
prost = "0.11"
rand = "0.7.2"
Expand Down
2 changes: 1 addition & 1 deletion client/cli/Cargo.toml
Expand Up @@ -18,7 +18,7 @@ chrono = "0.4.10"
clap = { version = "4.0.9", features = ["derive", "string"] }
fdlimit = "0.2.1"
futures = "0.3.21"
libp2p = "0.49.0"
libp2p = "0.50.0"
log = "0.4.17"
names = { version = "0.13.0", default-features = false }
parity-scale-codec = "3.0.0"
Expand Down
24 changes: 22 additions & 2 deletions client/cli/src/config.rs
Expand Up @@ -21,6 +21,7 @@
use crate::{
arg_enums::Database, error::Result, DatabaseParams, ImportParams, KeystoreParams,
NetworkParams, NodeKeyParams, OffchainWorkerParams, PruningParams, SharedParams, SubstrateCli,
WebRTCCertificateParams,
};
use log::warn;
use names::{Generator, Name};
Expand All @@ -29,7 +30,7 @@ use sc_service::{
config::{
BasePath, Configuration, DatabaseSource, KeystoreConfig, NetworkConfiguration,
NodeKeyConfig, OffchainWorkerConfig, PrometheusConfig, PruningMode, Role, RpcMethods,
TelemetryEndpoints, TransactionPoolOptions, WasmExecutionMethod,
TelemetryEndpoints, TransactionPoolOptions, WasmExecutionMethod, WebRTCConfig,
},
BlocksPruning, ChainSpec, TracingReceiver,
};
Expand Down Expand Up @@ -116,6 +117,11 @@ pub trait CliConfiguration<DCV: DefaultConfigurationValues = ()>: Sized {
self.network_params().map(|x| &x.node_key_params)
}

/// Get the [`WebRTCCertificateParams`] for this object.
fn webrtc_certificate_params(&self) -> Option<&WebRTCCertificateParams> {
self.network_params().map(|x| &x.webrtc_certificate_params)
}

/// Get the DatabaseParams for this object
fn database_params(&self) -> Option<&DatabaseParams> {
self.import_params().map(|x| &x.database_params)
Expand Down Expand Up @@ -163,6 +169,7 @@ pub trait CliConfiguration<DCV: DefaultConfigurationValues = ()>: Sized {
client_id: &str,
node_name: &str,
node_key: NodeKeyConfig,
webrtc: WebRTCConfig,
default_listen_port: u16,
) -> Result<NetworkConfiguration> {
Ok(if let Some(network_params) = self.network_params() {
Expand All @@ -174,10 +181,11 @@ pub trait CliConfiguration<DCV: DefaultConfigurationValues = ()>: Sized {
client_id,
node_name,
node_key,
webrtc,
default_listen_port,
)
} else {
NetworkConfiguration::new(node_name, client_id, node_key, Some(net_config_dir))
NetworkConfiguration::new(node_name, client_id, node_key, webrtc, Some(net_config_dir))
})
}

Expand Down Expand Up @@ -454,6 +462,16 @@ pub trait CliConfiguration<DCV: DefaultConfigurationValues = ()>: Sized {
.unwrap_or_else(|| Ok(Default::default()))
}

/// Get the WebRTC config from the current object.
///
/// By default this is retrieved from [`WebRTCCertificateParams`] if it is available. Otherwise
/// its [`WebRTCConfig::Ephemeral`].
fn webrtc(&self, net_config_dir: &PathBuf) -> Result<WebRTCConfig> {
self.webrtc_certificate_params()
.map(|x| x.webrtc_certificate(net_config_dir))
.unwrap_or_else(|| Ok(WebRTCConfig::Ephemeral))
}

/// Get maximum runtime instances
///
/// By default this is `None`.
Expand Down Expand Up @@ -502,6 +520,7 @@ pub trait CliConfiguration<DCV: DefaultConfigurationValues = ()>: Sized {
},
);
let node_key = self.node_key(&net_config_dir)?;
let webrtc = self.webrtc(&net_config_dir)?;
let role = self.role(is_dev)?;
let max_runtime_instances = self.max_runtime_instances()?.unwrap_or(8);
let is_validator = role.is_authority();
Expand All @@ -522,6 +541,7 @@ pub trait CliConfiguration<DCV: DefaultConfigurationValues = ()>: Sized {
client_id.as_str(),
self.node_name()?.as_str(),
node_key,
webrtc,
DCV::p2p_listen_port(),
)?,
keystore_remote,
Expand Down
3 changes: 2 additions & 1 deletion client/cli/src/params/mod.rs
Expand Up @@ -24,6 +24,7 @@ mod offchain_worker_params;
mod pruning_params;
mod shared_params;
mod transaction_pool_params;
mod webrtc_certificate_params;

use crate::arg_enums::{CryptoScheme, OutputType};
use clap::Args;
Expand All @@ -37,7 +38,7 @@ use std::{fmt::Debug, str::FromStr};
pub use crate::params::{
database_params::*, import_params::*, keystore_params::*, network_params::*,
node_key_params::*, offchain_worker_params::*, pruning_params::*, shared_params::*,
transaction_pool_params::*,
transaction_pool_params::*, webrtc_certificate_params::*,
};

/// Parse Ss58AddressFormat
Expand Down
33 changes: 30 additions & 3 deletions client/cli/src/params/network_params.rs
Expand Up @@ -16,10 +16,13 @@
// 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 crate::{arg_enums::SyncMode, params::node_key_params::NodeKeyParams};
use crate::{
arg_enums::SyncMode,
params::{node_key_params::NodeKeyParams, webrtc_certificate_params::WebRTCCertificateParams},
};
use clap::Args;
use sc_network::{
config::{NetworkConfiguration, NodeKeyConfig},
config::{NetworkConfiguration, NodeKeyConfig, WebRTCConfig},
multiaddr::Protocol,
};
use sc_network_common::config::{NonReservedPeerMode, SetConfig, TransportConfig};
Expand Down Expand Up @@ -111,6 +114,10 @@ pub struct NetworkParams {
#[clap(flatten)]
pub node_key_params: NodeKeyParams,

#[allow(missing_docs)]
#[clap(flatten)]
pub webrtc_certificate_params: WebRTCCertificateParams,

/// Enable peer discovery on local networks.
///
/// By default this option is `true` for `--dev` or when the chain type is
Expand Down Expand Up @@ -158,12 +165,13 @@ impl NetworkParams {
client_id: &str,
node_name: &str,
node_key: NodeKeyConfig,
webrtc: WebRTCConfig,
default_listen_port: u16,
) -> NetworkConfiguration {
let port = self.port.unwrap_or(default_listen_port);

let listen_addresses = if self.listen_addr.is_empty() {
if is_validator || is_dev {
let mut addrs = if is_validator || is_dev {
vec![
Multiaddr::empty()
.with(Protocol::Ip6([0, 0, 0, 0, 0, 0, 0, 0].into()))
Expand All @@ -183,7 +191,25 @@ impl NetworkParams {
.with(Protocol::Tcp(port))
.with(Protocol::Ws(Cow::Borrowed("/"))),
]
};

// Enable WebRTC for non-validators and developers by default.
if !is_validator || is_dev {
addrs.push(
Multiaddr::empty()
.with(Protocol::Ip6([0, 0, 0, 0, 0, 0, 0, 0].into()))
.with(Protocol::Udp(port))
.with(Protocol::WebRTC),
);
addrs.push(
Multiaddr::empty()
.with(Protocol::Ip4([0, 0, 0, 0].into()))
.with(Protocol::Tcp(port))
.with(Protocol::WebRTC),
);
}

addrs
} else {
self.listen_addr.clone()
};
Expand Down Expand Up @@ -227,6 +253,7 @@ impl NetworkParams {
extra_sets: Vec::new(),
request_response_protocols: Vec::new(),
node_key,
webrtc,
node_name: node_name.to_string(),
client_version: client_id.to_string(),
transport: TransportConfig::Normal {
Expand Down
118 changes: 118 additions & 0 deletions client/cli/src/params/webrtc_certificate_params.rs
@@ -0,0 +1,118 @@
// This file is part of Substrate.

// Copyright (C) 2017-2022 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 clap::Args;
use sc_network::config::WebRTCConfig;
use std::path::PathBuf;

use crate::error;

/// The file name of the WebRTC's certificate inside the chain-specific
/// network config directory.
const WEBRTC_CERTIFICATE_FILENAME: &str = "webrtc_certificate";

/// Parameters used to create the `WebRTCConfig`, which determines the certificate used
/// for libp2p WebRTC transport.
#[derive(Debug, Clone, Args)]
pub struct WebRTCCertificateParams {
/// The file from which to read the node's WebRTC certificate to use for libp2p networking.
///
/// The contents of the file are parsed as follows:
///
/// The file must contain an ASCII PEM encoded
/// [`webrtc::peer_connection::certificate::RTCCertificate`].
///
/// If the file does not exist, it is created with a newly generated certificate.
#[clap(long, value_name = "FILE")]
pub webrtc_certificate_file: Option<PathBuf>,

/// When set to `true`, a new WebRTC certificate will be created each time you start a node.
///
/// The certificate won't be stored on disk. Use this option only if you DON'T want to preserve
/// node's WebRTC identity between (re)starts.
///
/// This option takes precedence over `--webrtc-certificate-file` option.
#[arg(long, value_name = "EPHEMERAL")]
pub webrtc_certificate_ephemeral: Option<bool>,
}

impl WebRTCCertificateParams {
/// Create a `WebRTCConfig` from the given `WebRTCCertificateParams` in the context
/// of an optional network config storage directory.
pub fn webrtc_certificate(&self, net_config_dir: &PathBuf) -> error::Result<WebRTCConfig> {
if let Some(true) = self.webrtc_certificate_ephemeral {
return Ok(WebRTCConfig::Ephemeral)
}

let filename = self
.webrtc_certificate_file
.clone()
.unwrap_or_else(|| net_config_dir.join(WEBRTC_CERTIFICATE_FILENAME));

Ok(WebRTCConfig::File(filename))
}
}

#[cfg(test)]
mod tests {
use super::*;
use libp2p::webrtc::tokio::Certificate as WebRTCCertificate;
use rand::thread_rng;
use std::fs;

#[test]
fn test_webrtc_certificate_file() {
fn load_cert_and_assert_eq(file: PathBuf, cert: &WebRTCCertificate) {
let params = WebRTCCertificateParams { webrtc_certificate_file: Some(file) };

let loaded_cert = params
.webrtc_certificate(&PathBuf::from("not-used"))
.expect("Creates certificate config")
.into_certificate()
.expect("Creates certificate");

assert_eq!(cert, loaded_cert, "expected the same certificate");
}

let tmp = tempfile::Builder::new().prefix("alice").tempdir().expect("Creates tempfile");
let file = tmp.path().join("mycertificate").to_path_buf();

let cert = WebRTCCertificate::generate(&mut thread_rng()).expect("Generates certificate");

fs::write(&file, cert.serialize_pem().as_bytes()).expect("Writes certificate");
load_cert_and_assert_eq(file.clone(), &cert);
}

#[test]
fn test_webrtc_certificate_ephemeral() {
let filepath = PathBuf::from("not-used");
let params = WebRTCCertificateParams {
webrtc_certificate_ephemeral: Some(true),
webrtc_certificate_file: Some(&filepath),
};

let _loaded_cert = params
.webrtc_certificate(&filepath)
.expect("Creates certificate config")
.into_certificate()
.expect("Creates certificate");

assert!(!filepath.exists(), "Does not create a file");
assert!(!filepath.join(WEBRTC_CERTIFICATE_FILENAME).exists(), "Does not create a file");
}
}
2 changes: 1 addition & 1 deletion client/consensus/common/Cargo.toml
Expand Up @@ -16,7 +16,7 @@ targets = ["x86_64-unknown-linux-gnu"]
async-trait = "0.1.57"
futures = { version = "0.3.21", features = ["thread-pool"] }
futures-timer = "3.0.1"
libp2p = { version = "0.49.0", default-features = false }
libp2p = "0.50.0"
log = "0.4.17"
parking_lot = "0.12.1"
serde = { version = "1.0", features = ["derive"] }
Expand Down
2 changes: 1 addition & 1 deletion client/network-gossip/Cargo.toml
Expand Up @@ -17,7 +17,7 @@ targets = ["x86_64-unknown-linux-gnu"]
ahash = "0.7.6"
futures = "0.3.21"
futures-timer = "3.0.1"
libp2p = { version = "0.49.0", default-features = false }
libp2p = "0.50.0"
log = "0.4.17"
lru = "0.8.1"
tracing = "0.1.29"
Expand Down

0 comments on commit c4b3dcc

Please sign in to comment.