Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 59 additions & 0 deletions config/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -590,3 +590,62 @@ fn test_api_secret_paths_config_file() {
foreign_api_secret_exists,
);
}

/// Missing logging keys must fall back to defaults (issue #3002).
#[test]
fn test_logging_config_missing_keys_use_defaults() {
// Omitting log_to_file previously failed to parse; it must default to true.
let toml = r#"
[server]
db_root = "chain_data"
api_http_addr = "127.0.0.1:3413"

[logging]
log_to_stdout = false
"#;
let members: ConfigMembers = toml::from_str(toml).expect("minimal config should parse");
let logging = members.logging.expect("logging section present");
assert_eq!(logging.log_to_stdout, false);
assert_eq!(logging.log_to_file, true); // default
assert_eq!(logging.log_file_append, true); // default
assert_eq!(format!("{:?}", logging.stdout_log_level), "Warn");
assert_eq!(format!("{:?}", logging.file_log_level), "Info");
}

/// Entire [logging] section can be omitted.
#[test]
fn test_logging_section_optional() {
let toml = r#"
[server]
db_root = "chain_data"
api_http_addr = "127.0.0.1:3413"
"#;
let members: ConfigMembers = toml::from_str(toml).expect("config without logging should parse");
assert!(members.logging.is_none());
assert_eq!(members.server.db_root, "chain_data");
assert_eq!(members.server.api_http_addr, "127.0.0.1:3413");
// Other server fields use defaults
assert_eq!(members.server.run_tui, Some(true));
}

/// Partial [server.stratum_mining_config] uses stratum defaults.
#[test]
fn test_stratum_partial_config_defaults() {
let toml = r#"
[server]
db_root = "chain_data"

[server.stratum_mining_config]
enable_stratum_server = true
"#;
let members: ConfigMembers = toml::from_str(toml).expect("partial stratum config should parse");
let stratum = members
.server
.stratum_mining_config
.expect("stratum section");
assert_eq!(stratum.enable_stratum_server, Some(true));
assert_eq!(stratum.attempt_time_per_block, 15);
assert_eq!(stratum.minimum_share_difficulty, 1);
assert_eq!(stratum.wallet_listener_url, "http://127.0.0.1:3415");
assert!(!stratum.burn_reward);
}
5 changes: 4 additions & 1 deletion config/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,12 +87,15 @@ pub struct GlobalConfig {
/// internal state that we don't necessarily
/// want serialised or deserialised
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
#[serde(default)]
pub struct ConfigMembers {
/// Config file version (None == version 1)
#[serde(default)]
pub config_file_version: Option<u32>,
/// Server config
#[serde(default)]
pub server: ServerConfig,
/// Logging config
/// Logging config (optional section; missing keys use LoggingConfig defaults)
#[serde(default)]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A missing logging section now becomes None, but node startup still calls .logging.clone().unwrap(). The minimal config therefore parses and then panics. Should this default to Some(LoggingConfig::default()) instead?

pub logging: Option<LoggingConfig>,
}
69 changes: 66 additions & 3 deletions servers/src/common/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -138,23 +138,32 @@ impl Default for ChainValidationMode {

/// Full server configuration, aggregating configurations required for the
/// different components.
///
/// Missing optional keys fall back to [`Default`] (see #3002).
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(default)]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These are always the mainnet defaults. A trimmed testnet or usernet config therefore misses the network-specific API, P2P, and stratum values from GlobalConfig::for_chain(). Could we apply defaults for the selected chain instead?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With these defaults, a misspelled key can be silently ignored and replaced by its default, which is risky for settings such as archive_mode. Could we reject or at least report unknown fields?

pub struct ServerConfig {
/// Directory under which the rocksdb stores will be created
#[serde(default = "default_db_root")]
pub db_root: String,

/// Network address for the Rest API HTTP server.
#[serde(default = "default_api_http_addr")]
pub api_http_addr: String,

/// Location of secret for basic auth on Rest API HTTP and V2 Owner API server.
#[serde(default = "default_api_secret_path")]
pub api_secret_path: Option<String>,

/// Location of secret for basic auth on v2 Foreign API server.
#[serde(default = "default_foreign_api_secret_path")]
pub foreign_api_secret_path: Option<String>,

/// TLS certificate file
#[serde(default)]
pub tls_certificate_file: Option<String>,
/// TLS certificate private key file
#[serde(default)]
pub tls_certificate_key: Option<String>,

/// Setup the server for tests, testnet or mainnet
Expand All @@ -170,23 +179,29 @@ pub struct ServerConfig {
pub chain_validation_mode: ChainValidationMode,

/// Whether this node is a full archival node or a fast-sync, pruned node
#[serde(default = "default_archive_mode")]
pub archive_mode: Option<bool>,

/// Whether to skip the sync timeout on startup
/// (To assist testing on solo chains)
#[serde(default = "default_skip_sync_wait")]
pub skip_sync_wait: Option<bool>,

/// Whether to run the TUI
/// if enabled, this will disable logging to stdout
#[serde(default = "default_run_tui")]
pub run_tui: Option<bool>,

/// Whether to run the test miner (internal, cuckoo 16)
#[serde(default = "default_run_test_miner")]
pub run_test_miner: Option<bool>,

/// Test miner wallet URL
#[serde(default)]
pub test_miner_wallet_url: Option<String>,

/// Configuration for the peer-to-peer server
#[serde(default)]
pub p2p_config: p2p::P2PConfig,

/// Transaction pool configuration
Expand All @@ -206,6 +221,31 @@ pub struct ServerConfig {
pub webhook_config: WebHooksConfig,
}

fn default_db_root() -> String {
"grin_chain".to_string()
}
fn default_api_http_addr() -> String {
"127.0.0.1:3413".to_string()
}
fn default_api_secret_path() -> Option<String> {
Some(".api_secret".to_string())
}
fn default_foreign_api_secret_path() -> Option<String> {
Some(".foreign_api_secret".to_string())
}
fn default_archive_mode() -> Option<bool> {
Some(false)
}
fn default_skip_sync_wait() -> Option<bool> {
Some(false)
}
fn default_run_tui() -> Option<bool> {
Some(true)
}
fn default_run_test_miner() -> Option<bool> {
Some(false)
}

fn default_future_time_limit() -> u64 {
DEFAULT_FUTURE_TIME_LIMIT
}
Expand Down Expand Up @@ -238,36 +278,59 @@ impl Default for ServerConfig {

/// Stratum (Mining server) configuration
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(default)]
pub struct StratumServerConfig {
/// Run a stratum mining server (the only way to communicate to mine this
/// node via grin-miner
#[serde(default = "default_enable_stratum_server")]
pub enable_stratum_server: Option<bool>,

/// If enabled, the address and port to listen on
#[serde(default = "default_stratum_server_addr")]
pub stratum_server_addr: Option<String>,

/// How long to wait before stopping the miner, recollecting transactions
/// and starting again
#[serde(default = "default_attempt_time_per_block")]
pub attempt_time_per_block: u32,

/// Minimum difficulty for worker shares
#[serde(default = "default_minimum_share_difficulty")]
pub minimum_share_difficulty: u64,

/// Base address to the HTTP wallet receiver
#[serde(default = "default_wallet_listener_url")]
pub wallet_listener_url: String,

/// Attributes the reward to a random private key instead of contacting the
/// wallet receiver. Mostly used for tests.
#[serde(default)]
pub burn_reward: bool,
}

fn default_attempt_time_per_block() -> u32 {
15
}
fn default_minimum_share_difficulty() -> u64 {
1
}
fn default_wallet_listener_url() -> String {
"http://127.0.0.1:3415".to_string()
}
fn default_enable_stratum_server() -> Option<bool> {
Some(false)
}
fn default_stratum_server_addr() -> Option<String> {
Some("127.0.0.1:3416".to_string())
}

impl Default for StratumServerConfig {
fn default() -> StratumServerConfig {
StratumServerConfig {
wallet_listener_url: "http://127.0.0.1:3415".to_string(),
wallet_listener_url: default_wallet_listener_url(),
burn_reward: false,
attempt_time_per_block: 15,
minimum_share_difficulty: 1,
attempt_time_per_block: default_attempt_time_per_block(),
minimum_share_difficulty: default_minimum_share_difficulty(),
enable_stratum_server: Some(false),
stratum_server_addr: Some("127.0.0.1:3416".to_string()),
}
Expand Down
47 changes: 39 additions & 8 deletions util/src/logger.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,39 +60,70 @@ pub struct LogEntry {
}

/// Logging config
///
/// Missing keys in `grin-server.toml` fall back to these defaults (see #3002).
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(default)]
pub struct LoggingConfig {
/// whether to log to stdout
#[serde(default = "default_true")]
pub log_to_stdout: bool,
/// logging level for stdout
#[serde(default = "default_stdout_log_level")]
pub stdout_log_level: Level,
/// whether to log to file
#[serde(default = "default_true")]
pub log_to_file: bool,
/// log file level
#[serde(default = "default_file_log_level")]
pub file_log_level: Level,
/// Log file path
#[serde(default = "default_log_file_path")]
pub log_file_path: String,
/// Whether to append to log or replace
#[serde(default = "default_true")]
pub log_file_append: bool,
/// Size of the log in bytes to rotate over (optional)
#[serde(default = "default_log_max_size")]
pub log_max_size: Option<u64>,
/// Number of the log files to rotate over (optional)
#[serde(default = "default_log_max_files")]
pub log_max_files: Option<u32>,
/// Whether the tui is running (optional)
#[serde(default)]
pub tui_running: Option<bool>,
}

fn default_true() -> bool {
true
}
fn default_stdout_log_level() -> Level {
Level::Warn
}
fn default_file_log_level() -> Level {
Level::Info
}
fn default_log_file_path() -> String {
String::from("grin.log")
}
fn default_log_max_size() -> Option<u64> {
Some(1024 * 1024 * 16) // 16 megabytes default
}
fn default_log_max_files() -> Option<u32> {
Some(DEFAULT_ROTATE_LOG_FILES)
}

impl Default for LoggingConfig {
fn default() -> LoggingConfig {
LoggingConfig {
log_to_stdout: true,
stdout_log_level: Level::Warn,
log_to_file: true,
file_log_level: Level::Info,
log_file_path: String::from("grin.log"),
log_file_append: true,
log_max_size: Some(1024 * 1024 * 16), // 16 megabytes default
log_max_files: Some(DEFAULT_ROTATE_LOG_FILES),
log_to_stdout: default_true(),
stdout_log_level: default_stdout_log_level(),
log_to_file: default_true(),
file_log_level: default_file_log_level(),
log_file_path: default_log_file_path(),
log_file_append: default_true(),
log_max_size: default_log_max_size(),
log_max_files: default_log_max_files(),
tui_running: None,
}
}
Expand Down
Loading