Skip to content

Commit 43ed99e

Browse files
committed
client: add WebRTC transport
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.
1 parent 672cac2 commit 43ed99e

File tree

15 files changed

+2146
-931
lines changed

15 files changed

+2146
-931
lines changed

Cargo.lock

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

bin/node/cli/benches/block_production.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ use sc_consensus::{
2929
use sc_service::{
3030
config::{
3131
BlocksPruning, DatabaseSource, KeystoreConfig, NetworkConfiguration, OffchainWorkerConfig,
32-
PruningMode, WasmExecutionMethod, WasmtimeInstantiationStrategy,
32+
PruningMode, WasmExecutionMethod, WasmtimeInstantiationStrategy, WebRTCConfig,
3333
},
3434
BasePath, Configuration, Role,
3535
};
@@ -52,6 +52,7 @@ fn new_node(tokio_handle: Handle) -> node_cli::service::NewFullBase {
5252
Sr25519Keyring::Alice.to_seed(),
5353
"network/test/0.1",
5454
Default::default(),
55+
WebRTCConfig::Ephemeral,
5556
None,
5657
);
5758

bin/node/cli/benches/transaction_pool.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ use sc_client_api::execution_extensions::ExecutionStrategies;
2727
use sc_service::{
2828
config::{
2929
BlocksPruning, DatabaseSource, KeystoreConfig, NetworkConfiguration, OffchainWorkerConfig,
30-
PruningMode, TransactionPoolOptions, WasmExecutionMethod,
30+
PruningMode, TransactionPoolOptions, WasmExecutionMethod, WebRTCConfig,
3131
},
3232
BasePath, Configuration, Role,
3333
};
@@ -46,6 +46,7 @@ fn new_node(tokio_handle: Handle) -> node_cli::service::NewFullBase {
4646
Sr25519Keyring::Alice.to_seed(),
4747
"network/test/0.1",
4848
Default::default(),
49+
WebRTCConfig::Ephemeral,
4950
None,
5051
);
5152

client/cli/src/config.rs

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
use crate::{
2222
arg_enums::Database, error::Result, DatabaseParams, ImportParams, KeystoreParams,
2323
NetworkParams, NodeKeyParams, OffchainWorkerParams, PruningParams, SharedParams, SubstrateCli,
24+
WebRTCCertificateParams,
2425
};
2526
use log::warn;
2627
use names::{Generator, Name};
@@ -29,7 +30,7 @@ use sc_service::{
2930
config::{
3031
BasePath, Configuration, DatabaseSource, KeystoreConfig, NetworkConfiguration,
3132
NodeKeyConfig, OffchainWorkerConfig, PrometheusConfig, PruningMode, Role, RpcMethods,
32-
TelemetryEndpoints, TransactionPoolOptions, WasmExecutionMethod,
33+
TelemetryEndpoints, TransactionPoolOptions, WasmExecutionMethod, WebRTCConfig,
3334
},
3435
BlocksPruning, ChainSpec, TracingReceiver,
3536
};
@@ -116,6 +117,11 @@ pub trait CliConfiguration<DCV: DefaultConfigurationValues = ()>: Sized {
116117
self.network_params().map(|x| &x.node_key_params)
117118
}
118119

120+
/// Get the [`WebRTCCertificateParams`] for this object.
121+
fn webrtc_certificate_params(&self) -> Option<&WebRTCCertificateParams> {
122+
self.network_params().map(|x| &x.webrtc_certificate_params)
123+
}
124+
119125
/// Get the DatabaseParams for this object
120126
fn database_params(&self) -> Option<&DatabaseParams> {
121127
self.import_params().map(|x| &x.database_params)
@@ -163,6 +169,7 @@ pub trait CliConfiguration<DCV: DefaultConfigurationValues = ()>: Sized {
163169
client_id: &str,
164170
node_name: &str,
165171
node_key: NodeKeyConfig,
172+
webrtc: WebRTCConfig,
166173
default_listen_port: u16,
167174
) -> Result<NetworkConfiguration> {
168175
Ok(if let Some(network_params) = self.network_params() {
@@ -174,10 +181,11 @@ pub trait CliConfiguration<DCV: DefaultConfigurationValues = ()>: Sized {
174181
client_id,
175182
node_name,
176183
node_key,
184+
webrtc,
177185
default_listen_port,
178186
)
179187
} else {
180-
NetworkConfiguration::new(node_name, client_id, node_key, Some(net_config_dir))
188+
NetworkConfiguration::new(node_name, client_id, node_key, webrtc, Some(net_config_dir))
181189
})
182190
}
183191

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

465+
/// Get the WebRTC config from the current object.
466+
///
467+
/// By default this is retrieved from [`WebRTCCertificateParams`] if it is available. Otherwise
468+
/// its [`WebRTCConfig::Ephemeral`].
469+
fn webrtc(&self, net_config_dir: &PathBuf) -> Result<WebRTCConfig> {
470+
self.webrtc_certificate_params()
471+
.map(|x| x.webrtc_certificate(net_config_dir))
472+
.unwrap_or_else(|| Ok(WebRTCConfig::Ephemeral))
473+
}
474+
457475
/// Get maximum runtime instances
458476
///
459477
/// By default this is `None`.
@@ -502,6 +520,7 @@ pub trait CliConfiguration<DCV: DefaultConfigurationValues = ()>: Sized {
502520
},
503521
);
504522
let node_key = self.node_key(&net_config_dir)?;
523+
let webrtc = self.webrtc(&net_config_dir)?;
505524
let role = self.role(is_dev)?;
506525
let max_runtime_instances = self.max_runtime_instances()?.unwrap_or(8);
507526
let is_validator = role.is_authority();
@@ -522,6 +541,7 @@ pub trait CliConfiguration<DCV: DefaultConfigurationValues = ()>: Sized {
522541
client_id.as_str(),
523542
self.node_name()?.as_str(),
524543
node_key,
544+
webrtc,
525545
DCV::p2p_listen_port(),
526546
)?,
527547
keystore_remote,

client/cli/src/params/mod.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ mod offchain_worker_params;
2424
mod pruning_params;
2525
mod shared_params;
2626
mod transaction_pool_params;
27+
mod webrtc_certificate_params;
2728

2829
use crate::arg_enums::{CryptoScheme, OutputType};
2930
use clap::Args;
@@ -37,7 +38,7 @@ use std::{fmt::Debug, str::FromStr};
3738
pub use crate::params::{
3839
database_params::*, import_params::*, keystore_params::*, network_params::*,
3940
node_key_params::*, offchain_worker_params::*, pruning_params::*, shared_params::*,
40-
transaction_pool_params::*,
41+
transaction_pool_params::*, webrtc_certificate_params::*,
4142
};
4243

4344
/// Parse Ss58AddressFormat

client/cli/src/params/network_params.rs

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,13 @@
1616
// You should have received a copy of the GNU General Public License
1717
// along with this program. If not, see <https://www.gnu.org/licenses/>.
1818

19-
use crate::{arg_enums::SyncMode, params::node_key_params::NodeKeyParams};
19+
use crate::{
20+
arg_enums::SyncMode,
21+
params::{node_key_params::NodeKeyParams, webrtc_certificate_params::WebRTCCertificateParams},
22+
};
2023
use clap::Args;
2124
use sc_network::{
22-
config::{NetworkConfiguration, NodeKeyConfig},
25+
config::{NetworkConfiguration, NodeKeyConfig, WebRTCConfig},
2326
multiaddr::Protocol,
2427
};
2528
use sc_network_common::config::{NonReservedPeerMode, SetConfig, TransportConfig};
@@ -111,6 +114,10 @@ pub struct NetworkParams {
111114
#[clap(flatten)]
112115
pub node_key_params: NodeKeyParams,
113116

117+
#[allow(missing_docs)]
118+
#[clap(flatten)]
119+
pub webrtc_certificate_params: WebRTCCertificateParams,
120+
114121
/// Enable peer discovery on local networks.
115122
///
116123
/// By default this option is `true` for `--dev` or when the chain type is
@@ -158,12 +165,13 @@ impl NetworkParams {
158165
client_id: &str,
159166
node_name: &str,
160167
node_key: NodeKeyConfig,
168+
webrtc: WebRTCConfig,
161169
default_listen_port: u16,
162170
) -> NetworkConfiguration {
163171
let port = self.port.unwrap_or(default_listen_port);
164172

165173
let listen_addresses = if self.listen_addr.is_empty() {
166-
if is_validator || is_dev {
174+
let mut addrs = if is_validator || is_dev {
167175
vec![
168176
Multiaddr::empty()
169177
.with(Protocol::Ip6([0, 0, 0, 0, 0, 0, 0, 0].into()))
@@ -183,7 +191,25 @@ impl NetworkParams {
183191
.with(Protocol::Tcp(port))
184192
.with(Protocol::Ws(Cow::Borrowed("/"))),
185193
]
194+
};
195+
196+
// Enable WebRTC for non-validators and developers by default.
197+
if !is_validator || is_dev {
198+
addrs.push(
199+
Multiaddr::empty()
200+
.with(Protocol::Ip6([0, 0, 0, 0, 0, 0, 0, 0].into()))
201+
.with(Protocol::Udp(port))
202+
.with(Protocol::WebRTC),
203+
);
204+
addrs.push(
205+
Multiaddr::empty()
206+
.with(Protocol::Ip4([0, 0, 0, 0].into()))
207+
.with(Protocol::Tcp(port))
208+
.with(Protocol::WebRTC),
209+
);
186210
}
211+
212+
addrs
187213
} else {
188214
self.listen_addr.clone()
189215
};
@@ -227,6 +253,7 @@ impl NetworkParams {
227253
extra_sets: Vec::new(),
228254
request_response_protocols: Vec::new(),
229255
node_key,
256+
webrtc,
230257
node_name: node_name.to_string(),
231258
client_version: client_id.to_string(),
232259
transport: TransportConfig::Normal {
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
// This file is part of Substrate.
2+
3+
// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd.
4+
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
5+
6+
// This program is free software: you can redistribute it and/or modify
7+
// it under the terms of the GNU General Public License as published by
8+
// the Free Software Foundation, either version 3 of the License, or
9+
// (at your option) any later version.
10+
11+
// This program is distributed in the hope that it will be useful,
12+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
// GNU General Public License for more details.
15+
16+
// You should have received a copy of the GNU General Public License
17+
// along with this program. If not, see <https://www.gnu.org/licenses/>.
18+
19+
use clap::Args;
20+
use sc_network::config::WebRTCConfig;
21+
use std::path::PathBuf;
22+
23+
use crate::error;
24+
25+
/// The file name of the WebRTC's certificate inside the chain-specific
26+
/// network config directory.
27+
const WEBRTC_CERTIFICATE_FILENAME: &str = "webrtc_certificate";
28+
29+
/// Parameters used to create the `WebRTCConfig`, which determines the certificate used
30+
/// for libp2p WebRTC transport.
31+
#[derive(Debug, Clone, Args)]
32+
pub struct WebRTCCertificateParams {
33+
/// The file from which to read the node's WebRTC certificate to use for libp2p networking.
34+
///
35+
/// The contents of the file are parsed as follows:
36+
///
37+
/// The file must contain an ASCII PEM encoded
38+
/// [`webrtc::peer_connection::certificate::RTCCertificate`].
39+
///
40+
/// If the file does not exist, it is created with a newly generated certificate.
41+
#[clap(long, value_name = "FILE")]
42+
pub webrtc_certificate_file: Option<PathBuf>,
43+
44+
/// When set to `true`, a new WebRTC certificate will be created each time you start a node.
45+
///
46+
/// The certificate won't be stored on disk. Use this option only if you DON'T want to preserve
47+
/// node's WebRTC identity between (re)starts.
48+
///
49+
/// This option takes precedence over `--webrtc-certificate-file` option.
50+
#[arg(long, value_name = "EPHEMERAL")]
51+
pub webrtc_certificate_ephemeral: Option<bool>,
52+
}
53+
54+
impl WebRTCCertificateParams {
55+
/// Create a `WebRTCConfig` from the given `WebRTCCertificateParams` in the context
56+
/// of an optional network config storage directory.
57+
pub fn webrtc_certificate(&self, net_config_dir: &PathBuf) -> error::Result<WebRTCConfig> {
58+
if let Some(true) = self.webrtc_certificate_ephemeral {
59+
return Ok(WebRTCConfig::Ephemeral)
60+
}
61+
62+
let filename = self
63+
.webrtc_certificate_file
64+
.clone()
65+
.unwrap_or_else(|| net_config_dir.join(WEBRTC_CERTIFICATE_FILENAME));
66+
67+
Ok(WebRTCConfig::File(filename))
68+
}
69+
}
70+
71+
#[cfg(test)]
72+
mod tests {
73+
use super::*;
74+
use libp2p::webrtc::tokio::Certificate as WebRTCCertificate;
75+
use rand::thread_rng;
76+
use std::fs;
77+
78+
#[test]
79+
fn test_webrtc_certificate_file() {
80+
fn load_cert_and_assert_eq(file: PathBuf, cert: &WebRTCCertificate) {
81+
let params = WebRTCCertificateParams { webrtc_certificate_file: Some(file) };
82+
83+
let loaded_cert = params
84+
.webrtc_certificate(&PathBuf::from("not-used"))
85+
.expect("Creates certificate config")
86+
.into_certificate()
87+
.expect("Creates certificate");
88+
89+
assert_eq!(cert, loaded_cert, "expected the same certificate");
90+
}
91+
92+
let tmp = tempfile::Builder::new().prefix("alice").tempdir().expect("Creates tempfile");
93+
let file = tmp.path().join("mycertificate").to_path_buf();
94+
95+
let cert = WebRTCCertificate::generate(&mut thread_rng()).expect("Generates certificate");
96+
97+
fs::write(&file, cert.serialize_pem().as_bytes()).expect("Writes certificate");
98+
load_cert_and_assert_eq(file.clone(), &cert);
99+
}
100+
101+
#[test]
102+
fn test_webrtc_certificate_ephemeral() {
103+
let filepath = PathBuf::from("not-used");
104+
let params = WebRTCCertificateParams {
105+
webrtc_certificate_ephemeral: Some(true),
106+
webrtc_certificate_file: Some(&filepath),
107+
};
108+
109+
let _loaded_cert = params
110+
.webrtc_certificate(&filepath)
111+
.expect("Creates certificate config")
112+
.into_certificate()
113+
.expect("Creates certificate");
114+
115+
assert!(!filepath.exists(), "Does not create a file");
116+
assert!(!filepath.join(WEBRTC_CERTIFICATE_FILENAME).exists(), "Does not create a file");
117+
}
118+
}

client/network/Cargo.toml

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -16,49 +16,48 @@ targets = ["x86_64-unknown-linux-gnu"]
1616
[dependencies]
1717
array-bytes = "4.1"
1818
async-trait = "0.1"
19-
asynchronous-codec = "0.6"
19+
asynchronous-codec = "0.6.1"
2020
bitflags = "1.3.2"
2121
bytes = "1"
2222
cid = "0.8.4"
2323
codec = { package = "parity-scale-codec", version = "3.0.0", features = ["derive"] }
2424
either = "1.5.3"
2525
fnv = "1.0.6"
26+
fork-tree = { version = "3.0.0", path = "../../utils/fork-tree" }
2627
futures = "0.3.21"
2728
futures-timer = "3.0.2"
2829
ip_network = "0.4.1"
29-
libp2p = { version = "0.50.0", features = ["async-std", "dns", "identify", "kad", "macros", "mdns", "mplex", "noise", "ping", "tcp", "yamux", "websocket"] }
30-
linked_hash_set = "0.1.3"
30+
libp2p = { version = "0.50.0", features = ["async-std", "dns", "identify", "kad", "macros", "mdns", "mplex", "noise", "ping", "tcp", "tokio", "yamux", "webrtc", "websocket"] }
3131
linked-hash-map = "0.5.4"
32+
linked_hash_set = "0.1.3"
3233
log = "0.4.17"
3334
lru = "0.8.1"
3435
parking_lot = "0.12.1"
3536
pin-project = "1.0.12"
36-
prost = "0.11"
37-
rand = "0.7.2"
38-
serde = { version = "1.0.136", features = ["derive"] }
39-
serde_json = "1.0.85"
40-
smallvec = "1.8.0"
41-
thiserror = "1.0"
42-
unsigned-varint = { version = "0.7.1", features = ["futures", "asynchronous_codec"] }
43-
zeroize = "1.4.3"
44-
fork-tree = { version = "3.0.0", path = "../../utils/fork-tree" }
4537
prometheus-endpoint = { package = "substrate-prometheus-endpoint", version = "0.10.0-dev", path = "../../utils/prometheus" }
38+
prost = "0.11"
39+
rand = "0.8"
4640
sc-block-builder = { version = "0.10.0-dev", path = "../block-builder" }
4741
sc-client-api = { version = "4.0.0-dev", path = "../api" }
4842
sc-consensus = { version = "0.10.0-dev", path = "../consensus/common" }
4943
sc-network-common = { version = "0.10.0-dev", path = "./common" }
5044
sc-peerset = { version = "4.0.0-dev", path = "../peerset" }
5145
sc-utils = { version = "4.0.0-dev", path = "../utils" }
46+
serde = { version = "1.0.136", features = ["derive"] }
47+
serde_json = "1.0.85"
48+
smallvec = "1.8.0"
5249
sp-arithmetic = { version = "6.0.0", path = "../../primitives/arithmetic" }
5350
sp-blockchain = { version = "4.0.0-dev", path = "../../primitives/blockchain" }
5451
sp-consensus = { version = "0.10.0-dev", path = "../../primitives/consensus/common" }
5552
sp-core = { version = "7.0.0", path = "../../primitives/core" }
5653
sp-runtime = { version = "7.0.0", path = "../../primitives/runtime" }
54+
thiserror = "1.0"
55+
unsigned-varint = { version = "0.7.1", features = ["futures", "asynchronous_codec"] }
56+
zeroize = "1.4.3"
5757

5858
[dev-dependencies]
5959
assert_matches = "1.3"
6060
async-std = { version = "1.11.0", features = ["attributes"] }
61-
rand = "0.7.2"
6261
tempfile = "3.1.0"
6362
sc-network-light = { version = "0.10.0-dev", path = "./light" }
6463
sc-network-sync = { version = "0.10.0-dev", path = "./sync" }

0 commit comments

Comments
 (0)