From 280a2b456abb2e5d0dab95ae8c31b99d5d5582db Mon Sep 17 00:00:00 2001 From: Alec Muffett Date: Sat, 30 May 2026 22:15:00 +0400 Subject: [PATCH] bucket-A2: extract enhanced_node into coin-agnostic template (SAFE-ADDITIVE) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Lifts the EnhancedC2PoolNode implementation out of the compiled src/c2pool/node/enhanced_node.cpp into a header-only, coin-agnostic template EnhancedC2PoolNodeT in the new enhanced_node_impl.hpp. enhanced_node.hpp becomes a thin backward-compat header that pins the template to LTC types (EnhancedC2PoolNode = EnhancedC2PoolNodeT), so existing callers compile unchanged. c2pool_node_enhanced becomes an INTERFACE (header-only) library; the two executables that consume it (c2pool, c2pool_enhanced) already link `ltc` directly, so dropping `ltc` from the library's transitive deps is a no-op for the LTC build. Pure refactor — LTC behavior identical. The Dash specialization (src/impl/dash/enhanced_node.hpp) is deferred to Bucket B with the rest of src/impl/dash/, since master has no dash impl directory yet and that shim references not-yet-landed dash headers. Cherry-picked from dash-spv-embedded HEAD 8249dc28. 4 files, +251 / -368: - new src/c2pool/node/enhanced_node_impl.hpp (template) - modified src/c2pool/node/enhanced_node.hpp (LTC alias shim) - deleted src/c2pool/node/enhanced_node.cpp (impl moved to template) - modified src/c2pool/CMakeLists.txt (library -> INTERFACE) --- src/c2pool/CMakeLists.txt | 13 +- src/c2pool/node/enhanced_node.cpp | 222 ----------------------- src/c2pool/node/enhanced_node.hpp | 146 +-------------- src/c2pool/node/enhanced_node_impl.hpp | 238 +++++++++++++++++++++++++ 4 files changed, 251 insertions(+), 368 deletions(-) delete mode 100644 src/c2pool/node/enhanced_node.cpp create mode 100644 src/c2pool/node/enhanced_node_impl.hpp diff --git a/src/c2pool/CMakeLists.txt b/src/c2pool/CMakeLists.txt index a734d1140..e02387379 100644 --- a/src/c2pool/CMakeLists.txt +++ b/src/c2pool/CMakeLists.txt @@ -44,13 +44,12 @@ target_link_libraries(c2pool_merged_mining ${Boost_LIBRARIES} ) -# Enhanced Node Library -add_library(c2pool_node_enhanced - node/enhanced_node.cpp -) -target_include_directories(c2pool_node_enhanced PUBLIC .) -target_link_libraries(c2pool_node_enhanced - ltc +# Enhanced Node Library (header-only template after M6 decoupling). +# Coin-agnostic; callers that use the LTC alias (enhanced_node.hpp) must link +# `ltc` themselves. Dash callers include impl/dash/enhanced_node.hpp. +add_library(c2pool_node_enhanced INTERFACE) +target_include_directories(c2pool_node_enhanced INTERFACE .) +target_link_libraries(c2pool_node_enhanced INTERFACE core c2pool_hashrate c2pool_difficulty diff --git a/src/c2pool/node/enhanced_node.cpp b/src/c2pool/node/enhanced_node.cpp deleted file mode 100644 index b3d763c1b..000000000 --- a/src/c2pool/node/enhanced_node.cpp +++ /dev/null @@ -1,222 +0,0 @@ -#include "enhanced_node.hpp" -#include - -namespace c2pool { -namespace node { - -EnhancedC2PoolNode::EnhancedC2PoolNode(bool testnet) { - m_hashrate_tracker = std::make_unique(); - m_hashrate_tracker->set_difficulty_bounds(0.001, 65536.0); - // NOTE: In integrated mode, NodeImpl opens LevelDB for sharechain persist. - // We do NOT open a second LevelDB here — it would fail with "LOCK already - // held by process". EnhancedNode's m_storage is left null; NodeImpl - // handles all persist. - - LOG_INFO << "Enhanced C2Pool node initialized with default configuration"; - LOG_INFO << " - Automatic difficulty adjustment"; - LOG_INFO << " - Real-time hashrate tracking"; - LOG_INFO << " - Persistent storage (managed by NodeImpl)"; -} - -EnhancedC2PoolNode::EnhancedC2PoolNode(boost::asio::io_context* ctx, ltc::Config* config) - : m_context(ctx), m_config(config) -{ - // Initialize components - m_hashrate_tracker = std::make_unique(); - m_hashrate_tracker->set_difficulty_bounds(0.001, 65536.0); - - // NOTE: In integrated mode, NodeImpl opens LevelDB for sharechain persist - // (path: litecoin_testnet/sharechain_leveldb). We do NOT open a second - // LevelDB here — it would fail with "LOCK already held by process". - // EnhancedNode's m_storage is left null; NodeImpl handles all persist. - - LOG_INFO << "Enhanced C2Pool node initialized with:"; - LOG_INFO << " - Automatic difficulty adjustment"; - LOG_INFO << " - Real-time hashrate tracking"; - LOG_INFO << " - Persistent storage (managed by NodeImpl)"; -} - -void EnhancedC2PoolNode::handle(std::unique_ptr rmsg, const NetService& service) { - LOG_INFO << "Enhanced C2Pool node received message: " << rmsg->m_command - << " from " << service.to_string(); - - // Delegate to LTC node implementation for protocol handling - // Additional enhanced processing could be added here -} - -void EnhancedC2PoolNode::add_shares_to_chain(const std::vector& shares) { - for (const auto& share : shares) { - // Add to LTC chain using proper methods - // Note: This would need proper integration with LTC sharechain - - // Process through bridge for enhanced tracking - // if (m_bridge) { - // Convert LTC share to C2PoolShare format for processing - // This is a simplified conversion - // c2pool::C2PoolShare c2pool_share; - // share.USE([&](auto* typed_share) { - // c2pool_share.m_hash = typed_share->m_hash; - // c2pool_share.m_submit_time = static_cast(std::time(nullptr)); - // // Additional field mapping as needed - // }); - - // m_bridge->process_share(c2pool_share); - // } - } - - // Increment shares counter and save if threshold reached - m_shares_since_save += shares.size(); - if (m_shares_since_save >= 100) { // Save every 100 new shares - LOG_INFO << "Saving sharechain due to threshold (" << m_shares_since_save << " new shares)"; - m_storage->save_sharechain(*m_chain); - m_shares_since_save = 0; - } -} - -void node::EnhancedC2PoolNode::listen(int port) { - LOG_INFO << "Enhanced C2Pool node starting to listen on port " << port; - // Simplified listen implementation - would need proper socket setup -} - -void node::EnhancedC2PoolNode::shutdown() -{ - LOG_INFO << "Shutting down Enhanced C2Pool node, saving sharechain..."; - - if (m_storage && m_chain) { - m_storage->save_sharechain(*m_chain); - m_storage->log_storage_stats(); - } -} - -void node::EnhancedC2PoolNode::log_sharechain_stats() -{ - if (!m_chain) return; - - // Get stats using available LTC sharechain methods - uint64_t total_mining_shares = get_total_mining_shares(); - - LOG_INFO << "Enhanced C2Pool Sharechain stats:"; - LOG_INFO << " Total mining_shares: " << total_mining_shares; - LOG_INFO << " Connected peers: " << get_connected_peers_count(); - LOG_INFO << " Storage: " << (m_storage && m_storage->is_available() ? "enabled" : "disabled"); - - // Log hashrate and difficulty statistics - if (m_hashrate_tracker) { - auto stats = m_hashrate_tracker->get_statistics(); - LOG_INFO << " Current difficulty: " << stats["current_difficulty"]; - LOG_INFO << " Current hashrate: " << stats["current_hashrate"] << " H/s"; - LOG_INFO << " Total mining_shares submitted: " << stats["total_shares_submitted"]; - LOG_INFO << " Total mining_shares accepted: " << stats["total_shares_accepted"]; - LOG_INFO << " Acceptance rate (last hour): " << stats["acceptance_rate_1h"] << "%"; - } - - // Log difficulty adjustment stats - LOG_INFO << " Difficulty Adjustment Stats:"; - auto diff_stats = m_difficulty_adjustment_engine.get_difficulty_stats(); - LOG_INFO << " Pool difficulty: " << diff_stats["pool_difficulty"]; - LOG_INFO << " Network difficulty: " << diff_stats["network_difficulty"]; - LOG_INFO << " Mining_shares since adjustment: " << diff_stats["shares_since_adjustment"]; - LOG_INFO << " Target mining_shares per adjustment: " << diff_stats["target_shares_per_adjustment"]; -} - -void node::EnhancedC2PoolNode::track_mining_share_submission(const std::string& session_id, double difficulty) { - if (m_hashrate_tracker) { - m_hashrate_tracker->record_share_submission(difficulty, true); // Assume accepted for now - } - - LOG_DEBUG_OTHER << "Tracked mining_share submission for session " << session_id - << " with difficulty " << difficulty; -} - -nlohmann::json node::EnhancedC2PoolNode::get_difficulty_stats() const { - return m_difficulty_adjustment_engine.get_difficulty_stats(); -} - -nlohmann::json node::EnhancedC2PoolNode::get_hashrate_stats() const { - if (m_hashrate_tracker) { - return m_hashrate_tracker->get_statistics(); - } - return nlohmann::json{{"global_hashrate", 0.0}}; -} - -uint64_t node::EnhancedC2PoolNode::get_total_mining_shares() const { - return m_chain ? static_cast(m_chain->size()) : 0; -} - -void node::EnhancedC2PoolNode::add_local_mining_share(const uint256& /*hash*/, const uint256& /*prev_hash*/, const uint256& /*target*/) { - // Handled by the real share creation pipeline (create_local_share + broadcast). - // This stub satisfies the IMiningNode interface. -} - -size_t node::EnhancedC2PoolNode::get_connected_peers_count() const { - return m_connections.size(); -} - -nlohmann::json node::EnhancedC2PoolNode::get_stale_stats() const { - nlohmann::json result; - result["orphan_count"] = 0; - result["doa_count"] = 0; - result["stale_count"] = 0; - result["stale_prop"] = 0.0; - // When ShareTracker is wired, call get_stale_counts / get_average_stale_prop - // on the active best share with a configurable lookbehind window. - return result; -} - -// Mining interface implementations -std::string node::EnhancedC2PoolNode::get_mining_work(const std::string& address) { - // Generate work based on current difficulty - auto current_difficulty = m_hashrate_tracker->get_current_difficulty(); - auto pool_target = m_difficulty_adjustment_engine.get_pool_target(); - - // Create work template (simplified) - nlohmann::json work = { - {"target", pool_target.ToString()}, - {"difficulty", current_difficulty}, - {"address", address}, - {"timestamp", static_cast(std::time(nullptr))} - }; - - return work.dump(); -} - -bool node::EnhancedC2PoolNode::submit_mining_work(const std::string& work_data) { - try { - auto work = nlohmann::json::parse(work_data); - double difficulty = work.value("difficulty", 1.0); - - // Track the submission - m_hashrate_tracker->record_share_submission(difficulty, true); - - LOG_INFO << "Mining work submitted with difficulty " << difficulty; - return true; - - } catch (const std::exception& e) { - LOG_ERROR << "Failed to submit mining work: " << e.what(); - return false; - } -} - -nlohmann::json node::EnhancedC2PoolNode::get_mining_stats() { - nlohmann::json stats; - - // Combine all statistics - stats["hashrate"] = get_hashrate_stats(); - stats["difficulty"] = get_difficulty_stats(); - stats["mining_shares"] = { - {"total", get_total_mining_shares()}, - {"since_save", m_shares_since_save} - }; - stats["network"] = { - {"connected_peers", get_connected_peers_count()} - }; - - return stats; -} - -double node::EnhancedC2PoolNode::get_current_difficulty() { - return m_hashrate_tracker->get_current_difficulty(); -} - -} // namespace node -} // namespace c2pool diff --git a/src/c2pool/node/enhanced_node.hpp b/src/c2pool/node/enhanced_node.hpp index e8f7a4b18..40d3e0b93 100644 --- a/src/c2pool/node/enhanced_node.hpp +++ b/src/c2pool/node/enhanced_node.hpp @@ -1,150 +1,18 @@ #pragma once -#include -#include +// Backward-compat header: resolves c2pool::node::EnhancedC2PoolNode to the +// LTC-specialized instance of the template in enhanced_node_impl.hpp. +// For Dash (or other coins), include impl//enhanced_node.hpp instead. + +#include #include #include -#include -#include -#include -#include -#include -#include -#include -#include +#include namespace c2pool { namespace node { -/** - * @brief Enhanced C2Pool node with automatic difficulty adjustment and hashrate tracking - * - * Integrates all the enhanced features while maintaining compatibility with - * the existing LTC sharechain infrastructure and c2pool network protocol. - */ -class EnhancedC2PoolNode : public core::IMiningNode { -private: - std::unique_ptr m_storage; - uint64_t m_shares_since_save = 0; - difficulty::DifficultyAdjustmentEngine m_difficulty_adjustment_engine; - std::unique_ptr m_hashrate_tracker; - - // Basic node infrastructure - boost::asio::io_context* m_context = nullptr; - ltc::Config* m_config = nullptr; - ltc::ShareChain* m_chain = nullptr; // Placeholder for sharechain - std::map> m_connections; - -public: - /** - * @brief Default constructor - * @param testnet Whether to use testnet storage path - */ - EnhancedC2PoolNode(bool testnet = false); - - /** - * @brief Construct with IO context and configuration - * @param ctx Boost ASIO IO context - * @param config LTC configuration - */ - EnhancedC2PoolNode(boost::asio::io_context* ctx, ltc::Config* config); - - /** - * @brief Destructor with cleanup - */ - ~EnhancedC2PoolNode() = default; - - // ICommunicator implementation - void handle(std::unique_ptr rmsg, const NetService& service); - - // IMiningNode implementation - std::string get_mining_work(const std::string& address); - bool submit_mining_work(const std::string& work_data); - nlohmann::json get_mining_stats(); - double get_current_difficulty(); - - /** - * @brief Add shares to the chain with persistence - * @param shares Vector of LTC shares to add - */ - void add_shares_to_chain(const std::vector& shares); - - /** - * @brief Start listening on the specified port - * @param port Port to listen on - */ - void listen(int port); - - /** - * @brief Shutdown node gracefully - */ - void shutdown(); - - /** - * @brief Log comprehensive sharechain statistics - */ - void log_sharechain_stats() override; - - /** - * @brief Track mining_share submission for mining interface - * @param session_id Session identifier - * @param difficulty MiningShare difficulty - */ - void track_mining_share_submission(const std::string& session_id, double difficulty) override; - - /** - * @brief Get difficulty statistics - * @return JSON object with difficulty stats - */ - nlohmann::json get_difficulty_stats() const override; - - /** - * @brief Get hashrate statistics - * @return JSON object with hashrate stats - */ - nlohmann::json get_hashrate_stats() const override; - - /** - * @brief Add a local mining share (IMiningNode) - */ - void add_local_mining_share(const uint256& hash, const uint256& prev_hash, const uint256& target) override; - - /** - * @brief Get total number of mining_shares in chain - * @return MiningShare count - */ - uint64_t get_total_mining_shares() const override; - - /** - * @brief Get number of connected peers - * @return Peer count - */ - size_t get_connected_peers_count() const override; - - /** - * @brief Get stale/DOA share statistics - * @return JSON with orphan_count, doa_count, stale_prop - */ - nlohmann::json get_stale_stats() const override; - - /** - * @brief Get storage manager - * @return Pointer to storage manager - */ - storage::SharechainStorage* get_storage() const { return m_storage.get(); } - - /** - * @brief Get hashrate tracker - * @return Pointer to hashrate tracker - */ - hashrate::HashrateTracker* get_hashrate_tracker() const { return m_hashrate_tracker.get(); } - - /** - * @brief Get difficulty engine - * @return Reference to difficulty adjustment engine - */ - difficulty::DifficultyAdjustmentEngine& get_difficulty_engine() { return m_difficulty_adjustment_engine; } -}; +using EnhancedC2PoolNode = EnhancedC2PoolNodeT; } // namespace node } // namespace c2pool diff --git a/src/c2pool/node/enhanced_node_impl.hpp b/src/c2pool/node/enhanced_node_impl.hpp new file mode 100644 index 000000000..b85ddd5a1 --- /dev/null +++ b/src/c2pool/node/enhanced_node_impl.hpp @@ -0,0 +1,238 @@ +#pragma once + +// Coin-agnostic template for the enhanced c2pool node. +// Parameterized over Config, ShareChain, Peer, ShareType so it works for any +// Bitcoin-family coin (LTC, Dash, DOGE, BTC...). The concrete aliases live in +// impl//enhanced_node.hpp (and in this directory's enhanced_node.hpp for +// LTC's backward-compat alias). + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace c2pool { +namespace node { + +template +class EnhancedC2PoolNodeT : public core::IMiningNode +{ +protected: + std::unique_ptr m_storage; + uint64_t m_shares_since_save = 0; + difficulty::DifficultyAdjustmentEngine m_difficulty_adjustment_engine; + std::unique_ptr m_hashrate_tracker; + + boost::asio::io_context* m_context = nullptr; + Config* m_config = nullptr; + ShareChain* m_chain = nullptr; + std::map> m_connections; + +public: + explicit EnhancedC2PoolNodeT(bool /*testnet*/ = false) + { + m_hashrate_tracker = std::make_unique(); + m_hashrate_tracker->set_difficulty_bounds(0.001, 65536.0); + // NodeImpl opens LevelDB for sharechain persist; we don't open a second + // store here (would fail with LOCK already held). + LOG_INFO << "Enhanced C2Pool node initialized with default configuration"; + LOG_INFO << " - Automatic difficulty adjustment"; + LOG_INFO << " - Real-time hashrate tracking"; + LOG_INFO << " - Persistent storage (managed by NodeImpl)"; + } + + EnhancedC2PoolNodeT(boost::asio::io_context* ctx, Config* config) + : m_context(ctx), m_config(config) + { + m_hashrate_tracker = std::make_unique(); + m_hashrate_tracker->set_difficulty_bounds(0.001, 65536.0); + LOG_INFO << "Enhanced C2Pool node initialized with:"; + LOG_INFO << " - Automatic difficulty adjustment"; + LOG_INFO << " - Real-time hashrate tracking"; + LOG_INFO << " - Persistent storage (managed by NodeImpl)"; + } + + ~EnhancedC2PoolNodeT() override = default; + + void handle(std::unique_ptr rmsg, const NetService& service) + { + LOG_INFO << "Enhanced C2Pool node received message: " << rmsg->m_command + << " from " << service.to_string(); + } + + std::string get_mining_work(const std::string& address) + { + auto current_difficulty = m_hashrate_tracker->get_current_difficulty(); + auto pool_target = m_difficulty_adjustment_engine.get_pool_target(); + nlohmann::json work = { + {"target", pool_target.ToString()}, + {"difficulty", current_difficulty}, + {"address", address}, + {"timestamp", static_cast(std::time(nullptr))} + }; + return work.dump(); + } + + bool submit_mining_work(const std::string& work_data) + { + try { + auto work = nlohmann::json::parse(work_data); + double difficulty = work.value("difficulty", 1.0); + m_hashrate_tracker->record_share_submission(difficulty, true); + LOG_INFO << "Mining work submitted with difficulty " << difficulty; + return true; + } catch (const std::exception& e) { + LOG_ERROR << "Failed to submit mining work: " << e.what(); + return false; + } + } + + nlohmann::json get_mining_stats() + { + nlohmann::json stats; + stats["hashrate"] = get_hashrate_stats(); + stats["difficulty"] = get_difficulty_stats(); + stats["mining_shares"] = { + {"total", get_total_mining_shares()}, + {"since_save", m_shares_since_save} + }; + stats["network"] = { + {"connected_peers", get_connected_peers_count()} + }; + return stats; + } + + double get_current_difficulty() + { + return m_hashrate_tracker->get_current_difficulty(); + } + + void add_shares_to_chain(const std::vector& shares) + { + m_shares_since_save += shares.size(); + if (m_shares_since_save >= 100 && m_storage && m_chain) { + LOG_INFO << "Saving sharechain due to threshold (" << m_shares_since_save << " new shares)"; + m_storage->save_sharechain(*m_chain); + m_shares_since_save = 0; + } + } + + void listen(int port) + { + LOG_INFO << "Enhanced C2Pool node starting to listen on port " << port; + } + + void shutdown() + { + LOG_INFO << "Shutting down Enhanced C2Pool node, saving sharechain..."; + if (m_storage && m_chain) { + m_storage->save_sharechain(*m_chain); + m_storage->log_storage_stats(); + } + } + + void log_sharechain_stats() override + { + uint64_t total = get_total_mining_shares(); + LOG_INFO << "Enhanced C2Pool Sharechain stats:"; + LOG_INFO << " Total mining_shares: " << total; + LOG_INFO << " Connected peers: " << get_connected_peers_count(); + LOG_INFO << " Storage: " << (m_storage && m_storage->is_available() ? "enabled" : "disabled"); + + if (m_hashrate_tracker) { + auto stats = m_hashrate_tracker->get_statistics(); + LOG_INFO << " Current difficulty: " << stats["current_difficulty"]; + LOG_INFO << " Current hashrate: " << stats["current_hashrate"] << " H/s"; + LOG_INFO << " Total mining_shares submitted: " << stats["total_shares_submitted"]; + LOG_INFO << " Total mining_shares accepted: " << stats["total_shares_accepted"]; + LOG_INFO << " Acceptance rate (last hour): " << stats["acceptance_rate_1h"] << "%"; + } + + LOG_INFO << " Difficulty Adjustment Stats:"; + auto diff_stats = m_difficulty_adjustment_engine.get_difficulty_stats(); + LOG_INFO << " Pool difficulty: " << diff_stats["pool_difficulty"]; + LOG_INFO << " Network difficulty: " << diff_stats["network_difficulty"]; + LOG_INFO << " Mining_shares since adjustment: " << diff_stats["shares_since_adjustment"]; + LOG_INFO << " Target mining_shares per adjustment: " << diff_stats["target_shares_per_adjustment"]; + } + + void track_mining_share_submission(const std::string& session_id, double difficulty) override + { + if (m_hashrate_tracker) + m_hashrate_tracker->record_share_submission(difficulty, true); + LOG_DEBUG_OTHER << "Tracked mining_share submission for session " << session_id + << " with difficulty " << difficulty; + } + + nlohmann::json get_difficulty_stats() const override + { + return m_difficulty_adjustment_engine.get_difficulty_stats(); + } + + nlohmann::json get_hashrate_stats() const override + { + if (m_hashrate_tracker) + return m_hashrate_tracker->get_statistics(); + return nlohmann::json{{"global_hashrate", 0.0}}; + } + + void add_local_mining_share(const uint256& /*hash*/, const uint256& /*prev_hash*/, const uint256& /*target*/) override + { + // Handled by the real share creation pipeline (create_local_share + broadcast). + // This stub satisfies the IMiningNode interface. + } + + // Optional overrides — used by coin targets that own their own tracker + // (c2pool-dash passes DashNodeImpl accessors). Leave unset and the + // defaults below fall back to m_chain/m_connections. + void set_total_shares_fn(std::function fn) { m_total_shares_fn = std::move(fn); } + void set_connected_peers_fn(std::function fn) { m_connected_peers_fn = std::move(fn); } + + uint64_t get_total_mining_shares() const override + { + if (m_total_shares_fn) return m_total_shares_fn(); + return m_chain ? static_cast(m_chain->size()) : 0; + } + + size_t get_connected_peers_count() const override + { + if (m_connected_peers_fn) return m_connected_peers_fn(); + return m_connections.size(); + } + + nlohmann::json get_stale_stats() const override + { + nlohmann::json result; + result["orphan_count"] = 0; + result["doa_count"] = 0; + result["stale_count"] = 0; + result["stale_prop"] = 0.0; + return result; + } + + storage::SharechainStorage* get_storage() const { return m_storage.get(); } + hashrate::HashrateTracker* get_hashrate_tracker() const { return m_hashrate_tracker.get(); } + difficulty::DifficultyAdjustmentEngine& get_difficulty_engine() { return m_difficulty_adjustment_engine; } + +private: + std::function m_total_shares_fn; + std::function m_connected_peers_fn; +}; + +} // namespace node +} // namespace c2pool