diff --git a/src/config/settings.cpp b/src/config/settings.cpp index fd58bc2..e6c05ad 100644 --- a/src/config/settings.cpp +++ b/src/config/settings.cpp @@ -252,6 +252,8 @@ bool Settings::load(const std::string& path) loadScalar(j, "keep_daemon_running", keep_daemon_running_); loadScalar(j, "stop_external_daemon", stop_external_daemon_); loadScalar(j, "max_connections", max_connections_); + loadScalar(j, "stratum_host_enabled", stratum_host_enabled_); + loadScalar(j, "stratum_allowip", stratum_allowip_); if (j.contains("lite_wallet") && j["lite_wallet"].is_object()) { const auto& lite = j["lite_wallet"]; if (lite.contains("server_selection_mode")) { @@ -525,6 +527,8 @@ bool Settings::save(const std::string& path) j["keep_daemon_running"] = keep_daemon_running_; j["stop_external_daemon"] = stop_external_daemon_; j["max_connections"] = max_connections_; + j["stratum_host_enabled"] = stratum_host_enabled_; + j["stratum_allowip"] = stratum_allowip_; { json lite = json::object(); lite["server_selection_mode"] = liteServerSelectionPreferenceModeName(lite_server_selection_mode_); diff --git a/src/config/settings.h b/src/config/settings.h index 5d12aff..4017575 100644 --- a/src/config/settings.h +++ b/src/config/settings.h @@ -400,6 +400,11 @@ public: // Daemon — maximum peer connections (0 = daemon default) int getMaxConnections() const { return max_connections_; } void setMaxConnections(int v) { max_connections_ = std::max(0, v); } + // Host a RandomX stratum pool from the node (v1.3.0+ daemons). Empty allow-IP = loopback only (safe). + bool getStratumHost() const { return stratum_host_enabled_; } + void setStratumHost(bool v) { stratum_host_enabled_ = v; } + const std::string& getStratumAllowIp() const { return stratum_allowip_; } + void setStratumAllowIp(const std::string& v) { stratum_allowip_ = v; } // Lite wallet server selection LiteServerSelectionPreferenceMode getLiteServerSelectionMode() const { return lite_server_selection_mode_; } @@ -617,6 +622,8 @@ private: bool keep_daemon_running_ = false; bool stop_external_daemon_ = false; int max_connections_ = 0; // 0 = daemon default + bool stratum_host_enabled_ = false; // host a RandomX stratum pool from the node (v1.3.0+ daemons) + std::string stratum_allowip_; // -stratumallowip filter (empty = daemon default: loopback only) // Lite wallet server preferences. These are user/server settings only; // wallet secrets, wallet files, and lifecycle state are never stored here. diff --git a/src/daemon/daemon_controller.cpp b/src/daemon/daemon_controller.cpp index d960975..305656d 100644 --- a/src/daemon/daemon_controller.cpp +++ b/src/daemon/daemon_controller.cpp @@ -26,6 +26,7 @@ void DaemonController::syncSettings(const config::Settings* settings) if (!settings) return; daemon_->setDebugCategories(settings->getDebugCategories()); daemon_->setMaxConnections(settings->getMaxConnections()); + daemon_->setStratumHosting(settings->getStratumHost(), settings->getStratumAllowIp()); std::string walletFile = settings->getActiveWalletFile(); // The Wallets dialog opens an out-of-datadir wallet by linking it into the datadir under a diff --git a/src/daemon/embedded_daemon.cpp b/src/daemon/embedded_daemon.cpp index 2fae346..0ca0e1a 100644 --- a/src/daemon/embedded_daemon.cpp +++ b/src/daemon/embedded_daemon.cpp @@ -545,6 +545,15 @@ bool EmbeddedDaemon::start(const std::string& binary_path) args.push_back("-maxconnections=" + std::to_string(max_connections_)); } + // Host a RandomX stratum pool from this node (-stratum). Only v1.3.0+ daemons implement it; older + // ones ignore the unknown flag (no fatal arg check), and the Settings toggle is gated on daemon + // version, so this is only enabled against a daemon that supports it. Without -stratumallowip the + // daemon serves loopback only (safe default); a subnet opens it to that LAN. + if (stratum_enabled_) { + args.push_back("-stratum"); + if (!stratum_allowip_.empty()) args.push_back("-stratumallowip=" + stratum_allowip_); + } + // Active wallet file (multi-wallet). The daemon loads /. Only pass it for a // non-default name so the common case's command line is unchanged; skip during an isolated // start (seed migration manages its own throwaway wallet). diff --git a/src/daemon/embedded_daemon.h b/src/daemon/embedded_daemon.h index 86d9abc..b2b8576 100644 --- a/src/daemon/embedded_daemon.h +++ b/src/daemon/embedded_daemon.h @@ -182,6 +182,7 @@ public: * @brief Set maximum peer connections (0 = use daemon default) */ void setMaxConnections(int v) { max_connections_ = v; } + void setStratumHosting(bool enabled, const std::string& allowIp) { stratum_enabled_ = enabled; stratum_allowip_ = allowIp; } /** * @brief Request a blockchain rescan on the next daemon start @@ -311,6 +312,8 @@ private: std::atomic should_stop_{false}; std::set debug_categories_; int max_connections_ = 0; // 0 = daemon default + bool stratum_enabled_ = false; // -stratum: host a RandomX pool (v1.3.0+; older daemons ignore it) + std::string stratum_allowip_; // -stratumallowip subnet (empty = daemon default: loopback only) std::string wallet_file_; // -wallet= for the active wallet; empty/"wallet.dat" = default std::atomic crash_count_{0}; // consecutive crash counter std::atomic rescan_on_next_start_{false}; // -rescan flag for next start diff --git a/src/ui/pages/settings_page.cpp b/src/ui/pages/settings_page.cpp index b1f36e4..25f2539 100644 --- a/src/ui/pages/settings_page.cpp +++ b/src/ui/pages/settings_page.cpp @@ -116,6 +116,8 @@ struct SettingsPageState { LowSpecSnapshot low_spec_snapshot; bool keep_daemon_running = false; bool stop_external_daemon = false; + bool stratum_host = false; // O2: host a RandomX stratum pool from the node (v1.3.0+) + char stratum_allowip[64] = ""; // -stratumallowip subnet (blank = loopback only) bool lite_lifecycle_expanded = false; int lite_lifecycle_operation = 0; char lite_wallet_path[256] = ""; @@ -420,6 +422,9 @@ static void loadSettingsPageState(config::Settings* settings) { Layout::setUserFontScale(s_settingsState.font_scale); // sync with Layout on load s_settingsState.keep_daemon_running = settings->getKeepDaemonRunning(); s_settingsState.stop_external_daemon = settings->getStopExternalDaemon(); + s_settingsState.stratum_host = settings->getStratumHost(); + std::snprintf(s_settingsState.stratum_allowip, sizeof(s_settingsState.stratum_allowip), "%s", + settings->getStratumAllowIp().c_str()); // Lite-server selection is managed entirely by the Network tab (not the Settings page). s_settingsState.mine_when_idle = settings->getMineWhenIdle(); s_settingsState.mine_idle_delay = settings->getMineIdleDelay(); @@ -483,6 +488,8 @@ static void saveSettingsPageState(config::Settings* settings) { settings->setFontScale(s_settingsState.font_scale); settings->setKeepDaemonRunning(s_settingsState.keep_daemon_running); settings->setStopExternalDaemon(s_settingsState.stop_external_daemon); + settings->setStratumHost(s_settingsState.stratum_host); + settings->setStratumAllowIp(s_settingsState.stratum_allowip); // Lite-server selection is owned by the Network tab; the Settings page no longer writes it. settings->setMineWhenIdle(s_settingsState.mine_when_idle); settings->setMineIdleDelay(s_settingsState.mine_idle_delay); @@ -1207,6 +1214,26 @@ void RenderSettingsPage(App* app) { if (CB(TrId("stop_external", "stop_ext"), &s_settingsState.stop_external_daemon)) saveSettingsPageState(app->settings()); if (ImGui::IsItemHovered()) material::Tooltip("%s", TR("tt_stop_external")); + + // O2: host a RandomX stratum pool from this node. Only offered on daemons that implement + // it (v1.3.0+, version encoded major*1e6+minor*1e4+rev*100+build), so we never show a + // toggle that does nothing. Takes effect on the next daemon start/restart. Blank allow-IP + // = loopback only (safe); a subnet opens it to that LAN. + if (app->state().daemon_version >= 1030000) { + if (CB(TrId("stratum_host", "strat_host"), &s_settingsState.stratum_host)) + saveSettingsPageState(app->settings()); + if (ImGui::IsItemHovered()) material::Tooltip("%s", TR("tt_stratum_host")); + if (s_settingsState.stratum_host) { + ImGui::TextDisabled(" %s", TR("stratum_host_hint")); + ImGui::SetNextItemWidth(220.0f * Layout::dpiScale()); + if (ImGui::InputTextWithHint("##stratumallowip", TR("stratum_allowip_hint"), + s_settingsState.stratum_allowip, sizeof(s_settingsState.stratum_allowip))) + saveSettingsPageState(app->settings()); + if (s_settingsState.stratum_allowip[0] != '\0') + ImGui::TextColored(ImVec4(0.95f, 0.75f, 0.25f, 1.0f), " %s", + TR("stratum_expose_warn")); + } + } } if (CB(TrId("verbose_logging", "verbose"), &s_settingsState.verbose_logging)) { dragonx::util::Logger::instance().setVerbose(s_settingsState.verbose_logging); diff --git a/src/util/i18n.cpp b/src/util/i18n.cpp index 00b7e6f..44c520b 100644 --- a/src/util/i18n.cpp +++ b/src/util/i18n.cpp @@ -730,6 +730,11 @@ void I18n::loadBuiltinEnglish() strings_["tt_tor"] = "Route daemon connections through the Tor network for anonymity"; strings_["tt_keep_daemon"] = "Daemon will still stop when running the setup wizard"; strings_["tt_stop_external"] = "Applies when connecting to a daemon\nyou started outside this wallet"; + strings_["stratum_host"] = "Host a mining pool (stratum)"; + strings_["tt_stratum_host"] = "Run a RandomX stratum pool server on this node so other RandomX miners can point at this computer. Requires a v1.3.0+ node and a daemon restart to apply."; + strings_["stratum_host_hint"] = "Miners connect to this computer on port 22769 (RPC port + 1000) with a RandomX stratum miner. Restart the daemon to apply."; + strings_["stratum_allowip_hint"] = "Allow miners from IP or CIDR (blank = this computer only)"; + strings_["stratum_expose_warn"] = "Opens a mining port to the network you allow — only use on a trusted LAN."; strings_["tt_verbose"] = "Log detailed connection diagnostics,\ndaemon state, and port owner info\nto the Console tab"; strings_["tt_mine_idle"] = "Automatically start mining when the\nsystem is idle (no keyboard/mouse input)"; strings_["tt_idle_delay"] = "How long to wait before starting mining";