mirror of
https://github.com/logos-messaging/logos-messaging-rust-bindings.git
synced 2026-01-10 18:03:06 +00:00
301 lines
14 KiB
Rust
301 lines
14 KiB
Rust
//! Waku node [configuration](https://rfc.vac.dev/spec/36/#jsonconfig-type) related items
|
|
|
|
use std::fmt::{Display, Formatter};
|
|
use std::str::FromStr;
|
|
// std
|
|
// crates
|
|
use crate::WakuPubSubTopic;
|
|
use multiaddr::Multiaddr;
|
|
use secp256k1::SecretKey;
|
|
use serde::{Deserialize, Serialize};
|
|
use smart_default::SmartDefault;
|
|
// internal
|
|
|
|
/// Waku node configuration
|
|
#[derive(Clone, SmartDefault, Serialize, Deserialize, Debug)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct WakuNodeConfig {
|
|
/// Listening IP address. Default `0.0.0.0`
|
|
#[default(Some(std::net::IpAddr::V4(std::net::Ipv4Addr::new(0, 0, 0, 0))))]
|
|
pub host: Option<std::net::IpAddr>,
|
|
/// Libp2p TCP listening port. Default `60000`. Use `0` for **random**
|
|
#[default(Some(60000))]
|
|
pub port: Option<usize>,
|
|
/// External address to advertise to other nodes. Can be ip4, ip6 or dns4, dns6.
|
|
/// If null, the multiaddress(es) generated from the ip and port specified in the config (or default ones) will be used.
|
|
/// Default: null
|
|
pub advertise_addr: Option<Multiaddr>,
|
|
/// Secp256k1 private key in Hex format (`0x123...abc`). Default random
|
|
#[serde(with = "secret_key_serde")]
|
|
pub node_key: Option<SecretKey>,
|
|
/// Interval in seconds for pinging peers to keep the connection alive. Default `20`
|
|
#[default(Some(20))]
|
|
pub keep_alive_interval: Option<usize>,
|
|
/// Enable relay protocol. Default `true`
|
|
#[default(Some(true))]
|
|
pub relay: Option<bool>,
|
|
/// Enable store protocol to persist message history
|
|
#[default(Some(false))]
|
|
pub store: Option<bool>,
|
|
/// Url connection string. Accepts SQLite and PostgreSQL connection strings
|
|
#[default(Some("sqlite3://store.db".to_string()))]
|
|
pub database_url: Option<String>,
|
|
/// Max number of messages to store in the databas
|
|
#[default(Some(1000))]
|
|
pub store_retention_max_messages: Option<usize>,
|
|
/// Max number of seconds that a message will be persisted in the database, default 1 day
|
|
#[default(Some(86400))]
|
|
pub store_retention_max_seconds: Option<usize>,
|
|
pub relay_topics: Vec<WakuPubSubTopic>,
|
|
/// The minimum number of peers required on a topic to allow broadcasting a message. Default `0`
|
|
#[default(Some(0))]
|
|
pub min_peers_to_publish: Option<usize>,
|
|
/// Enable filter protocol. Default `false`
|
|
#[default(Some(false))]
|
|
pub filter: Option<bool>,
|
|
/// Set the log level. Default `INFO`. Allowed values "DEBUG", "INFO", "WARN", "ERROR", "DPANIC", "PANIC", "FATAL"
|
|
#[default(Some(WakuLogLevel::Info))]
|
|
pub log_level: Option<WakuLogLevel>,
|
|
/// Enable DiscoveryV5. Default `false`
|
|
#[default(Some(false))]
|
|
#[serde(rename = "discV5")]
|
|
pub discv5: Option<bool>,
|
|
/// Array of bootstrap nodes ENR.
|
|
#[serde(rename = "discV5BootstrapNodes", default)]
|
|
pub discv5_bootstrap_nodes: Vec<String>,
|
|
/// UDP port for DiscoveryV5. Default `9000`.
|
|
#[default(Some(9000))]
|
|
#[serde(rename = "discV5UDPPort")]
|
|
pub discv5_udp_port: Option<u16>,
|
|
/// Gossipsub custom configuration.
|
|
pub gossipsub_params: Option<GossipSubParams>,
|
|
/// The domain name resolving to the node's public IPv4 address.
|
|
#[serde(rename = "dns4DomainName")]
|
|
pub dns4_domain_name: Option<String>,
|
|
/// Custom websocket support parameters
|
|
#[serde(rename = "websockets")]
|
|
pub websocket_params: Option<WebsocketParams>,
|
|
}
|
|
|
|
#[derive(Clone, SmartDefault, Serialize, Deserialize, Debug)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct GossipSubParams {
|
|
/// Sets the optimal degree for a GossipSub topic mesh. For example, if D == 6,
|
|
/// each peer will want to have about six peers in their mesh for each topic they're subscribed to.
|
|
/// `d` should be set somewhere between `dlo` and `dhi`.
|
|
#[serde(rename = "d")]
|
|
pub d: Option<i32>,
|
|
/// Sets the lower bound on the number of peers we keep in a GossipSub topic mesh.
|
|
/// If we have fewer than dlo peers, we will attempt to graft some more into the mesh at
|
|
/// the next heartbeat.
|
|
#[serde(rename = "d_low")]
|
|
pub dlo: Option<i32>,
|
|
/// Sets the upper bound on the number of peers we keep in a GossipSub topic mesh.
|
|
/// If we have more than dhi peers, we will select some to prune from the mesh at the next heartbeat.
|
|
#[serde(rename = "d_high")]
|
|
pub dhi: Option<i32>,
|
|
/// `dscore` affects how peers are selected when pruning a mesh due to over subscription.
|
|
/// At least dscore of the retained peers will be high-scoring, while the remainder are
|
|
/// chosen randomly.
|
|
#[serde(rename = "d_score")]
|
|
pub dscore: Option<i32>,
|
|
/// Sets the quota for the number of outbound connections to maintain in a topic mesh.
|
|
/// When the mesh is pruned due to over subscription, we make sure that we have outbound connections
|
|
/// to at least dout of the survivor peers. This prevents sybil attackers from overwhelming
|
|
/// our mesh with incoming connections.
|
|
///
|
|
/// dout must be set below Dlo, and must not exceed D / 2.
|
|
#[serde(rename = "d_out")]
|
|
pub dout: Option<i32>,
|
|
/// Controls the size of the message cache used for gossip.
|
|
/// The message cache will remember messages for history_length heartbeats.
|
|
pub history_length: Option<i32>,
|
|
/// Controls how many cached message ids we will advertise in
|
|
/// IHAVE gossip messages. When asked for our seen message IDs, we will return
|
|
/// only those from the most recent history_gossip heartbeats. The slack between
|
|
/// history_gossip and history_length allows us to avoid advertising messages
|
|
/// that will be expired by the time they're requested.
|
|
///
|
|
/// history_gossip must be less than or equal to history_length to
|
|
/// avoid a runtime panic.
|
|
pub history_gossip: Option<i32>,
|
|
/// dlazy affects how many peers we will emit gossip to at each heartbeat.
|
|
/// We will send gossip to at least dlazy peers outside our mesh. The actual
|
|
/// number may be more, depending on gossip_factor and how many peers we're
|
|
/// connected to.
|
|
pub dlazy: Option<i32>,
|
|
/// `gossip_factor` affects how many peers we will emit gossip to at each heartbeat.
|
|
/// We will send gossip to gossip_factor * (total number of non-mesh peers), or
|
|
/// Dlazy, whichever is greater.
|
|
pub gossip_factor: Option<f64>,
|
|
/// Controls how many times we will allow a peer to request
|
|
/// the same message id through IWANT gossip before we start ignoring them. This is designed
|
|
/// to prevent peers from spamming us with requests and wasting our resources.
|
|
pub gossip_retransmission: Option<i32>,
|
|
/// Short delay before the heartbeat timer begins
|
|
/// after the router is initialized.
|
|
pub heartbeat_initial_delay_ms: Option<i32>,
|
|
/// Controls the time between heartbeats.
|
|
pub heartbeat_interval_seconds: Option<i32>,
|
|
/// Duration threshold for heartbeat processing before emitting
|
|
/// a warning; this would be indicative of an overloaded peer.
|
|
pub slow_heartbeat_warning: Option<f64>,
|
|
/// Controls how long we keep track of the fanout state. If it's been
|
|
/// fanout_ttl_seconds since we've published to a topic that we're not subscribed to,
|
|
/// we'll delete the fanout map for that topic.
|
|
pub fanout_ttl_seconds: Option<i32>,
|
|
/// Controls the number of peers to include in prune Peer eXchange.
|
|
/// When we prune a peer that's eligible for PX (has a good score, etc), we will try to
|
|
/// send them signed peer records for up to prune_peers other peers that we
|
|
/// know of.
|
|
pub prune_peers: Option<i32>,
|
|
/// Controls the backoff time for pruned peers. This is how long
|
|
/// a peer must wait before attempting to graft into our mesh again after being pruned.
|
|
/// When pruning a peer, we send them our value of PruneBackoff so they know
|
|
/// the minimum time to wait. Peers running older versions may not send a backoff time,
|
|
/// so if we receive a prune message without one, we will wait at least PruneBackoff
|
|
/// before attempting to re-graft.
|
|
pub prune_backoff_seconds: Option<i32>,
|
|
/// Controls the backoff time to use when unsuscribing
|
|
/// from a topic. A peer should not resubscribe to this topic before this
|
|
/// duration.
|
|
pub unsubscribe_backoff_seconds: Option<i32>,
|
|
/// Controls the number of active connection attempts for peers obtained through PX.
|
|
pub connectors: Option<i32>,
|
|
/// Sets the maximum number of pending connections for peers attempted through px.
|
|
pub max_pending_connections: Option<i32>,
|
|
/// Controls the timeout for connection attempts.
|
|
pub connection_timeout_seconds: Option<i32>,
|
|
/// Number of heartbeat ticks for attempting to reconnect direct peers
|
|
/// that are not currently connected.
|
|
pub direct_connect_ticks: Option<u64>,
|
|
/// Initial delay before opening connections to direct peers
|
|
pub direct_connect_initial_delay_seconds: Option<i32>,
|
|
/// Number of heartbeat ticks for attempting to improve the mesh
|
|
/// with opportunistic grafting. Every opportunistic_graft_ticks we will attempt to select some
|
|
/// high-scoring mesh peers to replace lower-scoring ones, if the median score of our mesh peers falls
|
|
/// below a threshold (see https://godoc.org/github.com/libp2p/go-libp2p-pubsub#PeerScoreThresholds).
|
|
pub opportunistic_graft_ticks: Option<u64>,
|
|
/// Number of peers to opportunistically graft.
|
|
pub opportunistic_graft_peers: Option<i32>,
|
|
/// If a GRAFT comes before graft_flood_threshold_seconds has elapsed since the last PRUNE,
|
|
/// then there is an extra score penalty applied to the peer through P7.
|
|
pub graft_flood_threshold_seconds: Option<i32>,
|
|
/// Maximum number of messages to include in an IHAVE message.
|
|
/// Also controls the maximum number of IHAVE ids we will accept and request with IWANT from a
|
|
/// peer within a heartbeat, to protect from IHAVE floods. You should adjust this value from the
|
|
/// default if your system is pushing more than 5000 messages in history_gossip heartbeats;
|
|
/// with the defaults this is 1666 messages/s.
|
|
#[serde(rename = "maxIHaveLength")]
|
|
pub max_ihave_length: Option<i32>,
|
|
/// Maximum number of IHAVE messages to accept from a peer within a heartbeat.
|
|
#[serde(rename = "maxIHaveMessages")]
|
|
pub max_ihave_messages: Option<i32>,
|
|
/// Time to wait for a message requested through IWANT following an IHAVE advertisement.
|
|
/// If the message is not received within this window, a broken promise is declared and
|
|
/// the router may apply bahavioural penalties.
|
|
#[serde(rename = "iwantFollowupTimeSeconds")]
|
|
pub iwant_followup_time_seconds: Option<i32>,
|
|
// Time until a previously seen message ID can be forgotten about.
|
|
#[serde(rename = "seenMessagesTTLSeconds")]
|
|
pub seen_messages_ttl_seconds: Option<i32>,
|
|
}
|
|
|
|
#[derive(Clone, SmartDefault, Serialize, Deserialize, Debug)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct WebsocketParams {
|
|
/// Indicates if websockets support will be enabled
|
|
#[default(Some(false))]
|
|
pub enabled: Option<bool>,
|
|
/// Listening address for websocket connections. Default `0.0.0.0`
|
|
#[default(Some(std::net::IpAddr::V4(std::net::Ipv4Addr::new(0, 0, 0, 0))))]
|
|
pub host: Option<std::net::IpAddr>,
|
|
/// TCP listening port for websocket connection. Use `0` for **random**. Default `60001`, if secure websockets support is enabled, the default is `6443“`
|
|
pub port: Option<usize>,
|
|
/// Enable secure websockets support
|
|
#[default(Some(false))]
|
|
pub secure: Option<bool>,
|
|
/// Secure websocket certificate path. Mandatory if secure websockets support is enabled.
|
|
pub cert_path: Option<String>,
|
|
/// Secure websocket key path. Mandatory if secure websockets support is enabled.
|
|
pub key_path: Option<String>,
|
|
}
|
|
|
|
#[derive(Clone, Default, Serialize, Deserialize, Debug)]
|
|
pub enum WakuLogLevel {
|
|
#[default]
|
|
Info,
|
|
Debug,
|
|
Warn,
|
|
Error,
|
|
DPanic,
|
|
Panic,
|
|
Fatal,
|
|
}
|
|
|
|
impl FromStr for WakuLogLevel {
|
|
type Err = std::io::Error;
|
|
|
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
match s.to_lowercase().as_str() {
|
|
"info" => Ok(Self::Info),
|
|
"debug" => Ok(Self::Debug),
|
|
"warn" => Ok(Self::Warn),
|
|
"error" => Ok(Self::Error),
|
|
"dpanic" => Ok(Self::DPanic),
|
|
"panic" => Ok(Self::Panic),
|
|
"fatal" => Ok(Self::Fatal),
|
|
_ => Err(std::io::Error::new(
|
|
std::io::ErrorKind::InvalidData,
|
|
format!("Unrecognized waku log level: {s}. Allowed values \"DEBUG\", \"INFO\", \"WARN\", \"ERROR\", \"DPANIC\", \"PANIC\", \"FATAL\""),
|
|
)),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Display for WakuLogLevel {
|
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
|
let tag = match self {
|
|
WakuLogLevel::Info => "INFO",
|
|
WakuLogLevel::Debug => "DEBUG",
|
|
WakuLogLevel::Warn => "WARN",
|
|
WakuLogLevel::Error => "ERROR",
|
|
WakuLogLevel::DPanic => "DPANIC",
|
|
WakuLogLevel::Panic => "PANIC",
|
|
WakuLogLevel::Fatal => "FATAL",
|
|
};
|
|
write!(f, "{tag}")
|
|
}
|
|
}
|
|
|
|
mod secret_key_serde {
|
|
use secp256k1::SecretKey;
|
|
use serde::de::Error;
|
|
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
|
|
|
pub fn serialize<S>(key: &Option<SecretKey>, serializer: S) -> Result<S::Ok, S::Error>
|
|
where
|
|
S: Serializer,
|
|
{
|
|
let as_string: Option<String> = key.as_ref().map(|key| hex::encode(key.secret_bytes()));
|
|
as_string.serialize(serializer)
|
|
}
|
|
|
|
pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<SecretKey>, D::Error>
|
|
where
|
|
D: Deserializer<'de>,
|
|
{
|
|
let as_string: Option<String> = Option::<String>::deserialize(deserializer)?;
|
|
match as_string {
|
|
None => Ok(None),
|
|
Some(s) => {
|
|
let key_bytes = hex::decode(s).map_err(|e| D::Error::custom(format!("{e}")))?;
|
|
Ok(Some(
|
|
SecretKey::from_slice(&key_bytes)
|
|
.map_err(|e| D::Error::custom(format!("{e}")))?,
|
|
))
|
|
}
|
|
}
|
|
}
|
|
}
|