|
| 1 | +// dgb::stratum::DGBWorkSource — Stage 4a skeleton. |
| 2 | +// |
| 3 | +// All IWorkSource methods are stubbed to safe defaults. Subsequent |
| 4 | +// sub-stages flesh them out (mirroring btc::stratum::BTCWorkSource's own |
| 5 | +// 4b/4c/4d progression): |
| 6 | +// Stage 4b: read-only getters (config, prevhash, generation, workers) |
| 7 | +// Stage 4c: Scrypt work generation (template, merkle branches, coinbase) |
| 8 | +// Stage 4d: mining_submit hot path (Scrypt PoW classify + won-block dispatch) |
| 9 | +// |
| 10 | +// The skeleton is intentionally non-functional but compiles, instantiates, |
| 11 | +// and lets us validate the StratumServer wiring in main_dgb.cpp end-to-end |
| 12 | +// before implementing the substantive logic. DGB validates Scrypt blocks |
| 13 | +// only (V36 / project_v36_dgb_scrypt_only); the other four DGB algos are |
| 14 | +// accept-by-continuity / V37 and never reach this work source. |
| 15 | + |
| 16 | +#include <impl/dgb/stratum/work_source.hpp> |
| 17 | + |
| 18 | +#include <impl/dgb/coin/header_chain.hpp> |
| 19 | +#include <impl/dgb/coin/mempool.hpp> |
| 20 | + |
| 21 | +#include <core/log.hpp> |
| 22 | + |
| 23 | +namespace dgb::stratum { |
| 24 | + |
| 25 | +DGBWorkSource::DGBWorkSource(c2pool::dgb::HeaderChain& chain, |
| 26 | + dgb::coin::Mempool& mempool, |
| 27 | + bool is_testnet, |
| 28 | + SubmitBlockFn submit_fn, |
| 29 | + core::stratum::StratumConfig config) |
| 30 | + : chain_(chain) |
| 31 | + , mempool_(mempool) |
| 32 | + , is_testnet_(is_testnet) |
| 33 | + , submit_block_fn_(std::move(submit_fn)) |
| 34 | + , config_(std::move(config)) |
| 35 | +{ |
| 36 | + LOG_INFO << "[DGB-STRATUM] DGBWorkSource constructed" |
| 37 | + << " (testnet=" << is_testnet_ |
| 38 | + << " min_diff=" << config_.min_difficulty |
| 39 | + << " max_diff=" << config_.max_difficulty |
| 40 | + << " target_time=" << config_.target_time |
| 41 | + << "s vardiff=" << (config_.vardiff_enabled ? "on" : "off") << ")"; |
| 42 | +} |
| 43 | + |
| 44 | +DGBWorkSource::~DGBWorkSource() = default; |
| 45 | + |
| 46 | +// ───────────────────────────────────────────────────────────────────────────── |
| 47 | +// IWorkSource: config + read-only state — Stage 4b will fill these in. |
| 48 | +// ───────────────────────────────────────────────────────────────────────────── |
| 49 | + |
| 50 | +const core::stratum::StratumConfig& DGBWorkSource::get_stratum_config() const |
| 51 | +{ |
| 52 | + return config_; |
| 53 | +} |
| 54 | + |
| 55 | +std::function<uint256()> DGBWorkSource::get_best_share_hash_fn() const |
| 56 | +{ |
| 57 | + std::lock_guard<std::mutex> lk(best_share_mutex_); |
| 58 | + return best_share_hash_fn_; // empty function until set_best_share_hash_fn() called |
| 59 | +} |
| 60 | + |
| 61 | +std::string DGBWorkSource::get_current_gbt_prevhash() const |
| 62 | +{ |
| 63 | + // Stage 4b: read chain_.tip() and return BE-display-hex form. |
| 64 | + return {}; |
| 65 | +} |
| 66 | + |
| 67 | +uint64_t DGBWorkSource::get_work_generation() const |
| 68 | +{ |
| 69 | + return work_generation_.load(std::memory_order_relaxed); |
| 70 | +} |
| 71 | + |
| 72 | +bool DGBWorkSource::has_merged_chain(uint32_t /*chain_id*/) const |
| 73 | +{ |
| 74 | + // DGB V36 default build: standalone Scrypt parent, no merged mining. |
| 75 | + // The DGB-as-DOGE-parent dual-parent path (-DAUX_DOGE=ON) is a V36 |
| 76 | + // STRETCH, parked behind the shared DOGE-aux settle — not wired here. |
| 77 | + return false; |
| 78 | +} |
| 79 | + |
| 80 | +// ───────────────────────────────────────────────────────────────────────────── |
| 81 | +// IWorkSource: per-connection bookkeeping — minimal but real now. |
| 82 | +// ───────────────────────────────────────────────────────────────────────────── |
| 83 | + |
| 84 | +void DGBWorkSource::register_stratum_worker(const std::string& session_id, |
| 85 | + const core::stratum::WorkerInfo& info) |
| 86 | +{ |
| 87 | + std::lock_guard<std::mutex> lk(workers_mutex_); |
| 88 | + workers_[session_id] = info; |
| 89 | + LOG_INFO << "[DGB-STRATUM] worker registered: session=" << session_id |
| 90 | + << " user=" << info.username |
| 91 | + << " worker=" << info.worker_name |
| 92 | + << " endpoint=" << info.remote_endpoint; |
| 93 | +} |
| 94 | + |
| 95 | +void DGBWorkSource::unregister_stratum_worker(const std::string& session_id) |
| 96 | +{ |
| 97 | + std::lock_guard<std::mutex> lk(workers_mutex_); |
| 98 | + auto it = workers_.find(session_id); |
| 99 | + if (it != workers_.end()) { |
| 100 | + LOG_INFO << "[DGB-STRATUM] worker unregistered: session=" << session_id |
| 101 | + << " user=" << it->second.username |
| 102 | + << " accepted=" << it->second.accepted |
| 103 | + << " rejected=" << it->second.rejected |
| 104 | + << " stale=" << it->second.stale; |
| 105 | + workers_.erase(it); |
| 106 | + } |
| 107 | +} |
| 108 | + |
| 109 | +void DGBWorkSource::update_stratum_worker(const std::string& session_id, |
| 110 | + double hashrate, double dead_hashrate, |
| 111 | + double difficulty, |
| 112 | + uint64_t accepted, uint64_t rejected, uint64_t stale) |
| 113 | +{ |
| 114 | + std::lock_guard<std::mutex> lk(workers_mutex_); |
| 115 | + auto it = workers_.find(session_id); |
| 116 | + if (it == workers_.end()) return; |
| 117 | + it->second.hashrate = hashrate; |
| 118 | + it->second.dead_hashrate = dead_hashrate; |
| 119 | + it->second.difficulty = difficulty; |
| 120 | + it->second.accepted = accepted; |
| 121 | + it->second.rejected = rejected; |
| 122 | + it->second.stale = stale; |
| 123 | +} |
| 124 | + |
| 125 | +// ───────────────────────────────────────────────────────────────────────────── |
| 126 | +// IWorkSource: work generation — Stage 4c will fill these in. |
| 127 | +// ───────────────────────────────────────────────────────────────────────────── |
| 128 | + |
| 129 | +nlohmann::json DGBWorkSource::get_current_work_template() const |
| 130 | +{ |
| 131 | + // Stage 4c: drive dgb::coin::TemplateBuilder over chain_ + mempool_ and |
| 132 | + // shape its WorkData into the GBT-style JSON the stratum session expects |
| 133 | + // (previousblockhash, bits, version, curtime, mintime, height, |
| 134 | + // coinbasevalue, transactions[]). Scrypt nBits, not SHA256d. |
| 135 | + return nlohmann::json::object(); |
| 136 | +} |
| 137 | + |
| 138 | +std::vector<std::string> DGBWorkSource::get_stratum_merkle_branches() const |
| 139 | +{ |
| 140 | + // Stage 4c: return cached branches from last template build. |
| 141 | + return {}; |
| 142 | +} |
| 143 | + |
| 144 | +std::pair<std::string, std::string> DGBWorkSource::get_coinbase_parts() const |
| 145 | +{ |
| 146 | + // Stage 4c: return cached coinb1/coinb2 (extranonce slot between them). |
| 147 | + return { {}, {} }; |
| 148 | +} |
| 149 | + |
| 150 | +core::stratum::CoinbaseResult DGBWorkSource::build_connection_coinbase( |
| 151 | + const uint256& /*prev_share_hash*/, |
| 152 | + const std::string& /*extranonce1_hex*/, |
| 153 | + const std::vector<unsigned char>& /*payout_script*/, |
| 154 | + const std::vector<std::pair<uint32_t, std::vector<unsigned char>>>& /*merged_addrs*/) const |
| 155 | +{ |
| 156 | + // Stage 4c: build per-connection coinbase using the SSOT gentx assembler |
| 157 | + // (coin/gentx_coinbase.hpp) + ShareTracker ref_hash + PPLNS payout map. |
| 158 | + // For now return an empty result; sessions calling this will get an empty |
| 159 | + // job and skip pushing work, which is safe but non-functional. |
| 160 | + return {}; |
| 161 | +} |
| 162 | + |
| 163 | +// ───────────────────────────────────────────────────────────────────────────── |
| 164 | +// IWorkSource: share submission — Stage 4d (the hot path). |
| 165 | +// ───────────────────────────────────────────────────────────────────────────── |
| 166 | + |
| 167 | +nlohmann::json DGBWorkSource::mining_submit( |
| 168 | + const std::string& username, const std::string& job_id, |
| 169 | + const std::string& /*extranonce1*/, const std::string& /*extranonce2*/, |
| 170 | + const std::string& /*ntime*/, const std::string& /*nonce*/, |
| 171 | + const std::string& /*request_id*/, |
| 172 | + const std::map<uint32_t, std::vector<unsigned char>>& /*merged_addresses*/, |
| 173 | + const core::stratum::JobSnapshot* /*job*/) |
| 174 | +{ |
| 175 | + // Stage 4d will: |
| 176 | + // 1. Reconstruct the 80-byte block header from JobSnapshot + miner inputs |
| 177 | + // 2. Scrypt(header) → pow_hash (scrypt_1024_1_1_256, the DGB-Scrypt algo) |
| 178 | + // 3. Decode share target from job->share_bits (compact) |
| 179 | + // 4. Decode block target from job->block_nbits (compact) |
| 180 | + // 5. Classify: |
| 181 | + // pow_hash <= block target → submit_block_fn_(full_block, height) |
| 182 | + // pow_hash <= share target → record share in tracker (sharechain accept) |
| 183 | + // otherwise → reject as low-difficulty |
| 184 | + // 6. Update worker stats accordingly |
| 185 | + // |
| 186 | + // For now: log + reject everything as low-difficulty. Miners will see |
| 187 | + // stratum errors but the binary won't crash. |
| 188 | + LOG_WARNING << "[DGB-STRATUM] mining_submit not implemented (stage 4d): " |
| 189 | + << "user=" << username << " job=" << job_id |
| 190 | + << " — submission rejected as low-difficulty"; |
| 191 | + |
| 192 | + return nlohmann::json::array({ |
| 193 | + false, |
| 194 | + nlohmann::json::array({23, "Low difficulty share (stratum stub: stage 4d not implemented)", nullptr}) |
| 195 | + }); |
| 196 | +} |
| 197 | + |
| 198 | +double DGBWorkSource::compute_share_difficulty( |
| 199 | + const std::string& /*coinb1*/, const std::string& /*coinb2*/, |
| 200 | + const std::string& /*extranonce1*/, const std::string& /*extranonce2*/, |
| 201 | + const std::string& /*ntime*/, const std::string& /*nonce*/, |
| 202 | + uint32_t /*version*/, const std::string& /*prevhash_hex*/, |
| 203 | + const std::string& /*nbits_hex*/, |
| 204 | + const std::vector<std::string>& /*merkle_branches*/) const |
| 205 | +{ |
| 206 | + // Stage 4b/4c: reconstruct the 80-byte header from (coinb1+en1+en2+coinb2) |
| 207 | + // merkle-rooted with merkle_branches, then scrypt_1024_1_1_256(header) and |
| 208 | + // return diff1 / pow_hash. Until then the coin-agnostic StratumServer must |
| 209 | + // not credit pseudoshares it cannot score, so we return 0.0 — the documented |
| 210 | + // parse-error / not-yet sentinel that the vardiff gate already treats as a |
| 211 | + // hard reject (no garbage difficulty leaks into the rate monitor). |
| 212 | + return 0.0; |
| 213 | +} |
| 214 | + |
| 215 | +// ───────────────────────────────────────────────────────────────────────────── |
| 216 | +// DGB-specific control surface |
| 217 | +// ───────────────────────────────────────────────────────────────────────────── |
| 218 | + |
| 219 | +void DGBWorkSource::set_best_share_hash_fn(std::function<uint256()> fn) |
| 220 | +{ |
| 221 | + std::lock_guard<std::mutex> lk(best_share_mutex_); |
| 222 | + best_share_hash_fn_ = std::move(fn); |
| 223 | +} |
| 224 | + |
| 225 | +} // namespace dgb::stratum |
0 commit comments