summaryrefslogtreecommitdiffstats
path: root/src/config/mod.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/config/mod.rs')
-rw-r--r--src/config/mod.rs104
1 files changed, 98 insertions, 6 deletions
diff --git a/src/config/mod.rs b/src/config/mod.rs
index 0890d3b..9210922 100644
--- a/src/config/mod.rs
+++ b/src/config/mod.rs
@@ -100,7 +100,7 @@ pub(crate) const OPTIONS: &[OptionSpec] = &[
Kind::Text,
"PATH",
None,
- "Users of the HTTP API (htpasswd file with Argon2 hashes). Required.",
+ "Users of the HTTP API (htpasswd file with Argon2 hashes). Required, but with relay-only (then without it, no HTTP API).",
),
option(
"protocol-writers",
@@ -191,7 +191,7 @@ pub(crate) const OPTIONS: &[OptionSpec] = &[
Kind::Text,
"PATH",
None,
- "This node's key (Ed25519, 32 raw bytes): who the node is, whatever transport reaches it. Keep it with the node: a new key is a new node. Required.",
+ "This node's key (Ed25519, 32 raw bytes): who the node is, whatever transport reaches it. Keep it with the node: a new key is a new node. Required, but with relay-only (then not allowed).",
),
option(
"create-node-key",
@@ -306,6 +306,13 @@ pub(crate) const OPTIONS: &[OptionSpec] = &[
"With the tor transport: the port of this node's onion service (published with its onion address).",
),
option(
+ "relay-only",
+ Kind::Flag,
+ "BOOL",
+ Some("false"),
+ "Only relay for nodes of swarms (libp2p circuit relay), without being a node: no document, no swarm, no node key. Needs transports = libp2p, libp2p-listen and libp2p-key-file; options about a swarm or a document are errors with it. List it for a swarm in the document (_continuum/relays), or configure it (libp2p-relays). The HTTP API (with htpasswd) has /health, /admin/stats and /admin/log only.",
+ ),
+ option(
"libp2p-key-file",
Kind::Text,
"PATH",
@@ -377,7 +384,8 @@ pub(crate) fn spec(name: &str) -> Option<&'static OptionSpec> {
pub(crate) struct Settings {
#[serde(default = "default_bind_addr")]
pub(crate) bind_addr: SocketAddr,
- pub(crate) htpasswd: String,
+ /// Required (`check`), but with `relay_only`.
+ pub(crate) htpasswd: Option<String>,
#[serde(default)]
pub(crate) protocol_writers: Vec<String>,
#[serde(default)]
@@ -404,7 +412,8 @@ pub(crate) struct Settings {
pub(crate) remake: Option<String>,
#[serde(default)]
pub(crate) force: Vec<String>,
- pub(crate) node_key_file: PathBuf,
+ /// Required (`check`), but not allowed with `relay_only`.
+ pub(crate) node_key_file: Option<PathBuf>,
#[serde(default)]
pub(crate) create_node_key: bool,
pub(crate) swarm_key_file: Option<PathBuf>,
@@ -448,6 +457,8 @@ pub(crate) struct Settings {
#[serde(default = "default_tor_port")]
#[cfg_attr(not(feature = "tor"), allow(dead_code))]
pub(crate) tor_port: u16,
+ #[serde(default)]
+ pub(crate) relay_only: bool,
pub(crate) mycelium_rest_url: Option<String>,
pub(crate) mycelium_key_file: Option<PathBuf>,
pub(crate) mycelium_network_key_file: Option<PathBuf>,
@@ -552,6 +563,18 @@ impl Settings {
if self.genesis && self.recover_from.is_some() {
bail!("recover-from starts the swarm from a backup; it can't be combined with genesis");
}
+ if self.relay_only {
+ self.check_relay_only()?;
+ } else {
+ if self.htpasswd.is_none() {
+ bail!("htpasswd is required: the users of the HTTP API");
+ }
+ if self.node_key_file.is_none() {
+ bail!(
+ "node-key-file is required: who this node is (create one with create-node-key)"
+ );
+ }
+ }
if self.transports.is_empty() {
bail!("transports: a node needs at least one");
}
@@ -604,6 +627,53 @@ impl Settings {
}
}
+impl Settings {
+ /// A relay that is no node: libp2p alone, dialable, with a key kept, and nothing
+ /// about a swarm or a document.
+ fn check_relay_only(&self) -> Result<()> {
+ if !cfg!(feature = "libp2p") {
+ bail!("relay-only relays over libp2p; this build has no libp2p");
+ }
+ if self.transports != ["libp2p"] {
+ bail!("relay-only relays over libp2p alone: set transports to libp2p");
+ }
+ if self.libp2p_listen.is_empty() {
+ bail!("relay-only needs libp2p-listen: where nodes reach the relay");
+ }
+ if self.libp2p_key_file.is_none() {
+ bail!(
+ "relay-only needs libp2p-key-file: without it, the relay's peer ID changes with every start and nodes lose it"
+ );
+ }
+ let swarm_options = [
+ ("node-key-file", self.node_key_file.is_some()),
+ ("create-node-key", self.create_node_key),
+ ("seeds", !self.seeds.is_empty()),
+ ("genesis", self.genesis),
+ ("state-file", self.state_file.is_some()),
+ ("document-file", self.document_file.is_some()),
+ ("recover-from", self.recover_from.is_some()),
+ ("remake", self.remake.is_some()),
+ ("swarm-key-file", self.swarm_key_file.is_some()),
+ ("protocol-writers", !self.protocol_writers.is_empty()),
+ ("alias", self.alias.is_some()),
+ ("debug-api", self.debug_api),
+ ];
+ let set: Vec<&str> = swarm_options
+ .iter()
+ .filter(|(_, set)| *set)
+ .map(|(name, _)| *name)
+ .collect();
+ if !set.is_empty() {
+ bail!(
+ "relay-only is no node of a swarm, so these don't apply: {}",
+ set.join(", ")
+ );
+ }
+ Ok(())
+ }
+}
+
/// Up to 64 characters, no control characters, not blank.
fn check_alias(alias: &str) -> Result<()> {
if alias.trim().is_empty() {
@@ -949,8 +1019,6 @@ fn culprit(merged: &Layer) -> Option<(&String, &Entry)> {
return false;
}
let mut alone = serde_json::Map::new();
- alone.insert("htpasswd".to_owned(), JsonValue::String(String::new()));
- alone.insert("node-key-file".to_owned(), JsonValue::String(String::new()));
alone.insert((*name).clone(), entry.value.clone());
serde_json::from_value::<Settings>(JsonValue::Object(alone)).is_err()
})
@@ -1506,6 +1574,30 @@ mod tests {
)
};
assert!(transports(json!([])).is_err());
+ // Without the node key: an error, but for a relay.
+ assert!(resolve(layer(json!({ "htpasswd": "u" })), Vec::new()).is_err());
+ let relay = |extra: JsonValue| {
+ let mut settings = json!({
+ "relay-only": true,
+ "transports": ["libp2p"],
+ "libp2p-listen": ["/ip6/::/tcp/9661"],
+ "libp2p-key-file": "libp2p.key",
+ });
+ settings
+ .as_object_mut()
+ .unwrap()
+ .extend(extra.as_object().unwrap().clone());
+ resolve(layer(settings), Vec::new())
+ };
+ assert_eq!(relay(json!({})).is_ok(), cfg!(feature = "libp2p"));
+ assert_eq!(
+ relay(json!({ "htpasswd": "u" })).is_ok(),
+ cfg!(feature = "libp2p")
+ );
+ assert!(relay(json!({ "node-key-file": "node.key" })).is_err());
+ assert!(relay(json!({ "seeds": ["a"] })).is_err());
+ assert!(relay(json!({ "transports": ["mycelium"] })).is_err());
+ assert!(relay(json!({ "libp2p-listen": [] })).is_err());
assert!(transports(json!(["pigeon"])).is_err());
assert!(transports(json!(["mycelium", "mycelium"])).is_err());
assert!(transports(json!(["mycelium"])).is_ok());