use serde::{Deserialize, Serialize};
fn default_max_request_size_bytes() -> usize {
1_048_576
}
fn default_rate_limit_when_auth_disabled() -> bool {
true
}
fn default_ip_rate_limit_burst() -> u32 {
50
}
fn default_ip_rate_limit_rate() -> u32 {
5
}
fn default_max_connections_per_ip_per_minute() -> u32 {
10
}
fn default_batch_rate_multiplier_cap() -> u32 {
10
}
fn default_connection_rate_limit_window_seconds() -> u64 {
60
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RpcConfig {
#[serde(default = "default_max_request_size_bytes")]
pub max_request_size_bytes: usize,
#[serde(default = "default_rate_limit_when_auth_disabled")]
pub rate_limit_when_auth_disabled: bool,
#[serde(default = "default_ip_rate_limit_burst")]
pub ip_rate_limit_burst: u32,
#[serde(default = "default_ip_rate_limit_rate")]
pub ip_rate_limit_rate: u32,
#[serde(default = "default_max_connections_per_ip_per_minute")]
pub max_connections_per_ip_per_minute: u32,
#[serde(default = "default_batch_rate_multiplier_cap")]
pub batch_rate_multiplier_cap: u32,
#[serde(default = "default_connection_rate_limit_window_seconds")]
pub connection_rate_limit_window_seconds: u64,
}
impl Default for RpcConfig {
fn default() -> Self {
Self {
max_request_size_bytes: 1_048_576,
rate_limit_when_auth_disabled: true,
ip_rate_limit_burst: 50,
ip_rate_limit_rate: 5,
max_connections_per_ip_per_minute: 10,
batch_rate_multiplier_cap: 10,
connection_rate_limit_window_seconds: 60,
}
}
}
fn default_rate_limit_burst() -> u32 {
100
}
fn default_rate_limit_rate() -> u32 {
10
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RpcAuthConfig {
#[serde(default)]
pub required: bool,
#[serde(default)]
pub tokens: Vec<String>,
#[serde(default)]
pub token_file: Option<String>,
#[serde(default)]
pub admin_tokens: Vec<String>,
#[serde(default)]
pub certificates: Vec<String>,
#[serde(default)]
pub username: Option<String>,
#[serde(default)]
pub password: Option<String>,
#[serde(default = "default_rate_limit_burst")]
pub rate_limit_burst: u32,
#[serde(default = "default_rate_limit_rate")]
pub rate_limit_rate: u32,
#[serde(default = "default_rate_limit_when_auth_disabled")]
pub rate_limit_when_auth_disabled: bool,
#[serde(default = "default_ip_rate_limit_burst")]
pub ip_rate_limit_burst: u32,
#[serde(default = "default_ip_rate_limit_rate")]
pub ip_rate_limit_rate: u32,
}
impl Default for RpcAuthConfig {
fn default() -> Self {
Self {
required: true,
tokens: Vec::new(),
token_file: None,
admin_tokens: Vec::new(),
certificates: Vec::new(),
username: None,
password: None,
rate_limit_burst: 100,
rate_limit_rate: 10,
rate_limit_when_auth_disabled: true,
ip_rate_limit_burst: 50,
ip_rate_limit_rate: 5,
}
}
}
impl RpcAuthConfig {
pub fn rate_limit_only(burst: u32, rate: u32) -> Self {
Self {
required: false,
tokens: Vec::new(),
token_file: None,
admin_tokens: Vec::new(),
certificates: Vec::new(),
username: None,
password: None,
rate_limit_burst: burst,
rate_limit_rate: rate,
rate_limit_when_auth_disabled: true,
ip_rate_limit_burst: burst,
ip_rate_limit_rate: rate,
}
}
pub fn load_tokens(&self) -> anyhow::Result<Vec<String>> {
use std::env;
if let Ok(env_tokens) = env::var("RPC_AUTH_TOKENS") {
let tokens: Vec<String> = env_tokens
.split(',')
.map(|s| s.trim().to_string())
.filter(|s| !s.is_empty())
.collect();
if !tokens.is_empty() {
return Ok(tokens);
}
}
if let Some(ref token_file) = self.token_file {
let content = std::fs::read_to_string(token_file).map_err(|e| {
anyhow::anyhow!("Failed to read token file {:?}: {}", token_file, e)
})?;
let tokens: Vec<String> = content
.lines()
.map(|s| s.trim().to_string())
.filter(|s| !s.is_empty() && !s.starts_with('#'))
.collect();
if !tokens.is_empty() {
return Ok(tokens);
}
}
Ok(self.tokens.clone())
}
}
#[cfg(feature = "stratum-v2")]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct StratumV2Config {
#[serde(default = "crate::config::default_true")]
pub p2p_stratum_demux: bool,
}
#[cfg(feature = "stratum-v2")]
impl Default for StratumV2Config {
fn default() -> Self {
Self {
p2p_stratum_demux: true,
}
}
}
#[cfg(feature = "stratum-v2")]
pub fn p2p_stratum_demux_enabled(stratum: Option<&StratumV2Config>) -> bool {
stratum
.map(|section| section.p2p_stratum_demux)
.unwrap_or(true)
}
#[cfg(all(test, feature = "stratum-v2"))]
mod stratum_v2_config_tests {
use super::{StratumV2Config, p2p_stratum_demux_enabled};
use crate::config::NodeConfig;
#[test]
fn stratum_v2_config_demux_defaults_on() {
let c = StratumV2Config::default();
assert!(c.p2p_stratum_demux);
assert!(p2p_stratum_demux_enabled(None));
}
#[test]
fn old_stratum_keys_parse_and_demux_false_disables_the_tlv_path() {
let config: NodeConfig = toml::from_str(
r#"
[stratum_v2]
listen_addr = "0.0.0.0:3333"
pool_url = "tcp://pool.example.com:3333"
enabled = true
p2p_stratum_demux = false
"#,
)
.expect("old stratum keys are ignored");
let section = config.stratum_v2.as_ref().expect("section present");
assert!(!section.p2p_stratum_demux);
assert!(!p2p_stratum_demux_enabled(Some(section)));
}
}