use std::path::{Path, PathBuf};
use serde::Deserialize;
use std::net::SocketAddr;
use stratum_apps::{
config_helpers::opt_path_from_toml,
key_utils::Secp256k1PublicKey,
utils::types::{Hashrate, SharesPerMinute},
};
#[derive(Debug, Deserialize, Clone)]
pub struct TranslatorConfig {
pub upstreams: Vec<Upstream>,
pub downstream_address: String,
pub downstream_port: u16,
pub max_supported_version: u16,
pub min_supported_version: u16,
pub downstream_extranonce2_size: u16,
pub user_identity: String,
pub downstream_difficulty_config: DownstreamDifficultyConfig,
pub aggregate_channels: bool,
#[serde(default)]
pub supported_extensions: Vec<u16>,
#[serde(default)]
pub required_extensions: Vec<u16>,
#[serde(default, deserialize_with = "opt_path_from_toml")]
log_file: Option<PathBuf>,
#[serde(default)]
monitoring_address: Option<SocketAddr>,
#[serde(default)]
monitoring_cache_refresh_secs: Option<u64>,
}
#[derive(Debug, Deserialize, Clone)]
pub struct Upstream {
pub address: String,
pub port: u16,
pub authority_pubkey: Secp256k1PublicKey,
}
impl Upstream {
pub fn new(address: String, port: u16, authority_pubkey: Secp256k1PublicKey) -> Self {
Self {
address,
port,
authority_pubkey,
}
}
}
impl TranslatorConfig {
#[allow(clippy::too_many_arguments)]
pub fn new(
upstreams: Vec<Upstream>,
downstream_address: String,
downstream_port: u16,
downstream_difficulty_config: DownstreamDifficultyConfig,
max_supported_version: u16,
min_supported_version: u16,
downstream_extranonce2_size: u16,
user_identity: String,
aggregate_channels: bool,
supported_extensions: Vec<u16>,
required_extensions: Vec<u16>,
monitoring_address: Option<SocketAddr>,
monitoring_cache_refresh_secs: Option<u64>,
) -> Self {
Self {
upstreams,
downstream_address,
downstream_port,
max_supported_version,
min_supported_version,
downstream_extranonce2_size,
user_identity,
downstream_difficulty_config,
aggregate_channels,
supported_extensions,
required_extensions,
log_file: None,
monitoring_address,
monitoring_cache_refresh_secs,
}
}
pub fn monitoring_address(&self) -> Option<SocketAddr> {
self.monitoring_address
}
pub fn monitoring_cache_refresh_secs(&self) -> Option<u64> {
self.monitoring_cache_refresh_secs
}
pub fn set_log_dir(&mut self, log_dir: Option<PathBuf>) {
if let Some(dir) = log_dir {
self.log_file = Some(dir);
}
}
pub fn log_dir(&self) -> Option<&Path> {
self.log_file.as_deref()
}
}
#[derive(Debug, Deserialize, Clone)]
pub struct DownstreamDifficultyConfig {
pub min_individual_miner_hashrate: Hashrate,
pub shares_per_minute: SharesPerMinute,
pub enable_vardiff: bool,
pub job_keepalive_interval_secs: u16,
}
impl DownstreamDifficultyConfig {
pub fn new(
min_individual_miner_hashrate: Hashrate,
shares_per_minute: SharesPerMinute,
enable_vardiff: bool,
job_keepalive_interval_secs: u16,
) -> Self {
Self {
min_individual_miner_hashrate,
shares_per_minute,
enable_vardiff,
job_keepalive_interval_secs,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::str::FromStr;
fn create_test_upstream() -> Upstream {
let pubkey_str = "9bDuixKmZqAJnrmP746n8zU1wyAQRrus7th9dxnkPg6RzQvCnan";
let pubkey = Secp256k1PublicKey::from_str(pubkey_str).unwrap();
Upstream::new("127.0.0.1".to_string(), 4444, pubkey)
}
fn create_test_difficulty_config() -> DownstreamDifficultyConfig {
DownstreamDifficultyConfig::new(100.0, 5.0, true, 60)
}
#[test]
fn test_upstream_creation() {
let upstream = create_test_upstream();
assert_eq!(upstream.address, "127.0.0.1");
assert_eq!(upstream.port, 4444);
}
#[test]
fn test_downstream_difficulty_config_creation() {
let config = create_test_difficulty_config();
assert_eq!(config.min_individual_miner_hashrate, 100.0);
assert_eq!(config.shares_per_minute, 5.0);
assert!(config.enable_vardiff);
}
#[test]
fn test_translator_config_creation() {
let upstreams = vec![create_test_upstream()];
let difficulty_config = create_test_difficulty_config();
let config = TranslatorConfig::new(
upstreams,
"0.0.0.0".to_string(),
3333,
difficulty_config,
2,
1,
4,
"test_user".to_string(),
true,
vec![],
vec![],
None,
None,
);
assert_eq!(config.upstreams.len(), 1);
assert_eq!(config.downstream_address, "0.0.0.0");
assert_eq!(config.downstream_port, 3333);
assert_eq!(config.max_supported_version, 2);
assert_eq!(config.min_supported_version, 1);
assert_eq!(config.downstream_extranonce2_size, 4);
assert_eq!(config.user_identity, "test_user");
assert!(config.aggregate_channels);
assert!(config.supported_extensions.is_empty());
assert!(config.required_extensions.is_empty());
assert!(config.log_file.is_none());
}
#[test]
fn test_translator_config_log_dir() {
let upstreams = vec![create_test_upstream()];
let difficulty_config = create_test_difficulty_config();
let mut config = TranslatorConfig::new(
upstreams,
"0.0.0.0".to_string(),
3333,
difficulty_config,
2,
1,
4,
"test_user".to_string(),
false,
vec![],
vec![],
None,
None,
);
assert!(config.log_dir().is_none());
let log_path = PathBuf::from("/tmp/logs");
config.set_log_dir(Some(log_path.clone()));
assert_eq!(config.log_dir(), Some(log_path.as_path()));
config.set_log_dir(None);
assert_eq!(config.log_dir(), Some(log_path.as_path())); }
#[test]
fn test_multiple_upstreams() {
let upstream1 = create_test_upstream();
let mut upstream2 = create_test_upstream();
upstream2.address = "192.168.1.1".to_string();
upstream2.port = 5555;
let upstreams = vec![upstream1, upstream2];
let difficulty_config = create_test_difficulty_config();
let config = TranslatorConfig::new(
upstreams,
"0.0.0.0".to_string(),
3333,
difficulty_config,
2,
1,
4,
"test_user".to_string(),
true,
vec![],
vec![],
None,
None,
);
assert_eq!(config.upstreams.len(), 2);
assert_eq!(config.upstreams[0].address, "127.0.0.1");
assert_eq!(config.upstreams[0].port, 4444);
assert_eq!(config.upstreams[1].address, "192.168.1.1");
assert_eq!(config.upstreams[1].port, 5555);
}
#[test]
fn test_vardiff_disabled_config() {
let mut difficulty_config = create_test_difficulty_config();
difficulty_config.enable_vardiff = false;
let upstreams = vec![create_test_upstream()];
let config = TranslatorConfig::new(
upstreams,
"0.0.0.0".to_string(),
3333,
difficulty_config,
2,
1,
4,
"test_user".to_string(),
false,
vec![],
vec![],
None,
None,
);
assert!(!config.downstream_difficulty_config.enable_vardiff);
assert!(!config.aggregate_channels);
}
}