-
Notifications
You must be signed in to change notification settings - Fork 14
Expand file tree
/
Copy pathwork_source.cpp
More file actions
302 lines (269 loc) · 15.6 KB
/
Copy pathwork_source.cpp
File metadata and controls
302 lines (269 loc) · 15.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
// dgb::stratum::DGBWorkSource — Stage 4a skeleton.
//
// All IWorkSource methods are stubbed to safe defaults. Subsequent
// sub-stages flesh them out (mirroring btc::stratum::BTCWorkSource's own
// 4b/4c/4d progression):
// Stage 4b: read-only getters (config, prevhash, generation, workers)
// Stage 4c: Scrypt work generation (template, merkle branches, coinbase)
// Stage 4d: mining_submit hot path (Scrypt PoW classify + won-block dispatch)
//
// The skeleton is intentionally non-functional but compiles, instantiates,
// and lets us validate the StratumServer wiring in main_dgb.cpp end-to-end
// before implementing the substantive logic. DGB validates Scrypt blocks
// only (V36 / project_v36_dgb_scrypt_only); the other four DGB algos are
// accept-by-continuity / V37 and never reach this work source.
#include <impl/dgb/stratum/work_source.hpp>
#include <impl/dgb/coin/header_chain.hpp>
#include <impl/dgb/coin/mempool.hpp>
#include <impl/dgb/coin/embedded_coinbase_value.hpp>
#include <impl/dgb/coin/dgb_block_algo.hpp>
#include <core/log.hpp>
#include <ctime>
#include <limits>
namespace dgb::stratum {
DGBWorkSource::DGBWorkSource(c2pool::dgb::HeaderChain& chain,
dgb::coin::Mempool& mempool,
bool is_testnet,
SubmitBlockFn submit_fn,
core::SubsidyFunc subsidy_func,
core::stratum::StratumConfig config)
: chain_(chain)
, mempool_(mempool)
, is_testnet_(is_testnet)
, submit_block_fn_(std::move(submit_fn))
, subsidy_func_(std::move(subsidy_func))
, config_(std::move(config))
{
LOG_INFO << "[DGB-STRATUM] DGBWorkSource constructed"
<< " (testnet=" << is_testnet_
<< " min_diff=" << config_.min_difficulty
<< " max_diff=" << config_.max_difficulty
<< " target_time=" << config_.target_time
<< "s vardiff=" << (config_.vardiff_enabled ? "on" : "off")
<< " subsidy_func=" << (subsidy_func_ ? "wired" : "UNSET") << ")";
}
DGBWorkSource::~DGBWorkSource() = default;
// ──────────────────────────────────────────────────────────────────
// Embedded coinbasevalue — first PRODUCTION caller of CoinParams::subsidy_func.
// Delegates to the dgb::coin SSOT (embedded_coinbase_value.hpp) so the embedded
// TemplateBuilder coinbasevalue and the external-daemon GBT coinbasevalue are
// computed from ONE definition and can never silently diverge. The GBT value,
// when present, is authoritative and returned verbatim (external-daemon
// fallback MUST PERSIST); otherwise subsidy_func(height)+fees is derived from
// the DGB oracle decay schedule.
// ──────────────────────────────────────────────────────────────────
uint64_t DGBWorkSource::coinbase_value(uint32_t height, uint64_t total_fees,
std::optional<uint64_t> gbt_coinbasevalue) const
{
return dgb::coin::resolve_coinbase_value(subsidy_func_, height, total_fees,
gbt_coinbasevalue);
}
// ─────────────────────────────────────────────────────────────────────────────
// IWorkSource: config + read-only state — Stage 4b will fill these in.
// ─────────────────────────────────────────────────────────────────────────────
const core::stratum::StratumConfig& DGBWorkSource::get_stratum_config() const
{
return config_;
}
std::function<uint256()> DGBWorkSource::get_best_share_hash_fn() const
{
std::lock_guard<std::mutex> lk(best_share_mutex_);
return best_share_hash_fn_; // empty function until set_best_share_hash_fn() called
}
std::string DGBWorkSource::get_current_gbt_prevhash() const
{
// Stage 4b: read chain_.tip() and return BE-display-hex form.
return {};
}
uint64_t DGBWorkSource::get_work_generation() const
{
return work_generation_.load(std::memory_order_relaxed);
}
bool DGBWorkSource::has_merged_chain(uint32_t /*chain_id*/) const
{
// DGB V36 default build: standalone Scrypt parent, no merged mining.
// The DGB-as-DOGE-parent dual-parent path (-DAUX_DOGE=ON) is a V36
// STRETCH, parked behind the shared DOGE-aux settle — not wired here.
return false;
}
// ─────────────────────────────────────────────────────────────────────────────
// IWorkSource: per-connection bookkeeping — minimal but real now.
// ─────────────────────────────────────────────────────────────────────────────
void DGBWorkSource::register_stratum_worker(const std::string& session_id,
const core::stratum::WorkerInfo& info)
{
std::lock_guard<std::mutex> lk(workers_mutex_);
workers_[session_id] = info;
LOG_INFO << "[DGB-STRATUM] worker registered: session=" << session_id
<< " user=" << info.username
<< " worker=" << info.worker_name
<< " endpoint=" << info.remote_endpoint;
}
void DGBWorkSource::unregister_stratum_worker(const std::string& session_id)
{
std::lock_guard<std::mutex> lk(workers_mutex_);
auto it = workers_.find(session_id);
if (it != workers_.end()) {
LOG_INFO << "[DGB-STRATUM] worker unregistered: session=" << session_id
<< " user=" << it->second.username
<< " accepted=" << it->second.accepted
<< " rejected=" << it->second.rejected
<< " stale=" << it->second.stale;
workers_.erase(it);
}
}
void DGBWorkSource::update_stratum_worker(const std::string& session_id,
double hashrate, double dead_hashrate,
double difficulty,
uint64_t accepted, uint64_t rejected, uint64_t stale)
{
std::lock_guard<std::mutex> lk(workers_mutex_);
auto it = workers_.find(session_id);
if (it == workers_.end()) return;
it->second.hashrate = hashrate;
it->second.dead_hashrate = dead_hashrate;
it->second.difficulty = difficulty;
it->second.accepted = accepted;
it->second.rejected = rejected;
it->second.stale = stale;
}
// ─────────────────────────────────────────────────────────────────────────────
// IWorkSource: work generation — Stage 4c will fill these in.
// ─────────────────────────────────────────────────────────────────────────────
nlohmann::json DGBWorkSource::get_current_work_template() const
{
// Stage 4c (coinbasevalue wire): the full GBT-shaped template
// (previousblockhash, bits, version, curtime, mintime, transactions[])
// is assembled by the embedded dgb::coin::TemplateBuilder in a following
// slice. What lands here is the consensus-bearing reward field: the
// coinbasevalue for the NEXT block, derived through the #207 SSOT
// (coinbase_value -> resolve_coinbase_value -> subsidy_func) keyed on the
// absolute next-block height from the #209 HeaderChain accessor
// (next_block_height() == tip_height()+1, or base_height for an empty
// chain). Embedded path: no external-daemon GBT value is plumbed in yet
// and mempool-fee aggregation is not wired, so total_fees = 0 here. Both
// compose in later slices WITHOUT changing this SSOT call (a present GBT
// coinbasevalue stays authoritative; fees add on top of subsidy).
const uint32_t next_h = chain_.next_block_height();
const uint64_t coinbasevalue =
coinbase_value(next_h, /*total_fees=*/0, /*gbt_coinbasevalue=*/std::nullopt);
// GBT-scaffold fields the embedded path can derive TRUTHFULLY from current
// chain state, ahead of a full dgb::coin::TemplateBuilder port (M3 TODO):
// version — BIP9 base | the DGB Scrypt algo nibble. A DGB block
// template MUST pin the Scrypt lane: the mining algo lives
// in 4 nVersion bits (coin/dgb_block_algo.hpp SSOT) and
// Scrypt is the all-zero codepoint (DGB_BLOCK_VERSION_SCRYPT
// == 0x0000); any other nibble is a non-Scrypt algo that is
// accept-by-continuity / V37 here, never a template we emit.
// curtime — current wall-clock; GBT's suggested header nTime.
// mintime — median_time_past()+1 (#209 accessor): DGB Core's
// ContextualCheckBlockHeader lower bound (nTime > MTP). An
// empty chain returns INT64_MIN (unconstrained) -> emit 0.
// transactions — empty array: embedded mempool tx SELECTION is not wired,
// so no transactions are fabricated and fees stay 0
// (consistent with the total_fees=0 coinbasevalue above).
//
// Deliberately NOT emitted yet — they need accessors the Scrypt-only
// HeaderSample does not carry, and land in the following Stage 4c/4d slices:
// previousblockhash — the tip block hash (HeaderSample stores no hash yet)
// bits — the next-block compact target off the DigiShield
// retarget window
// and the per-connection coinbase (gentx + ShareTracker ref_hash + PPLNS
// payout map) assembles in build_connection_coinbase() — that output is
// consensus-bearing and surfaces for an operator tap, not in this field wire.
static constexpr uint32_t BIP9_BASE_VERSION = 0x20000000u;
const uint32_t version =
BIP9_BASE_VERSION |
static_cast<uint32_t>(dgb::coin::DGB_BLOCK_VERSION_SCRYPT);
const int64_t mtp = chain_.median_time_past();
const int64_t mintime = (mtp == std::numeric_limits<int64_t>::min())
? 0 : (mtp + 1);
const int64_t curtime = static_cast<int64_t>(std::time(nullptr));
nlohmann::json tmpl = nlohmann::json::object();
tmpl["height"] = next_h;
tmpl["coinbasevalue"] = coinbasevalue;
tmpl["version"] = version;
tmpl["curtime"] = curtime;
tmpl["mintime"] = mintime;
tmpl["transactions"] = nlohmann::json::array();
return tmpl;
}
std::vector<std::string> DGBWorkSource::get_stratum_merkle_branches() const
{
// Stage 4c: return cached branches from last template build.
return {};
}
std::pair<std::string, std::string> DGBWorkSource::get_coinbase_parts() const
{
// Stage 4c: return cached coinb1/coinb2 (extranonce slot between them).
return { {}, {} };
}
core::stratum::CoinbaseResult DGBWorkSource::build_connection_coinbase(
const uint256& /*prev_share_hash*/,
const std::string& /*extranonce1_hex*/,
const std::vector<unsigned char>& /*payout_script*/,
const std::vector<std::pair<uint32_t, std::vector<unsigned char>>>& /*merged_addrs*/) const
{
// Stage 4c: build per-connection coinbase using the SSOT gentx assembler
// (coin/gentx_coinbase.hpp) + ShareTracker ref_hash + PPLNS payout map.
// For now return an empty result; sessions calling this will get an empty
// job and skip pushing work, which is safe but non-functional.
return {};
}
// ─────────────────────────────────────────────────────────────────────────────
// IWorkSource: share submission — Stage 4d (the hot path).
// ─────────────────────────────────────────────────────────────────────────────
nlohmann::json DGBWorkSource::mining_submit(
const std::string& username, const std::string& job_id,
const std::string& /*extranonce1*/, const std::string& /*extranonce2*/,
const std::string& /*ntime*/, const std::string& /*nonce*/,
const std::string& /*request_id*/,
const std::map<uint32_t, std::vector<unsigned char>>& /*merged_addresses*/,
const core::stratum::JobSnapshot* /*job*/)
{
// Stage 4d will:
// 1. Reconstruct the 80-byte block header from JobSnapshot + miner inputs
// 2. Scrypt(header) → pow_hash (scrypt_1024_1_1_256, the DGB-Scrypt algo)
// 3. Decode share target from job->share_bits (compact)
// 4. Decode block target from job->block_nbits (compact)
// 5. Classify:
// pow_hash <= block target → submit_block_fn_(full_block, height)
// pow_hash <= share target → record share in tracker (sharechain accept)
// otherwise → reject as low-difficulty
// 6. Update worker stats accordingly
//
// For now: log + reject everything as low-difficulty. Miners will see
// stratum errors but the binary won't crash.
LOG_WARNING << "[DGB-STRATUM] mining_submit not implemented (stage 4d): "
<< "user=" << username << " job=" << job_id
<< " — submission rejected as low-difficulty";
return nlohmann::json::array({
false,
nlohmann::json::array({23, "Low difficulty share (stratum stub: stage 4d not implemented)", nullptr})
});
}
double DGBWorkSource::compute_share_difficulty(
const std::string& /*coinb1*/, const std::string& /*coinb2*/,
const std::string& /*extranonce1*/, const std::string& /*extranonce2*/,
const std::string& /*ntime*/, const std::string& /*nonce*/,
uint32_t /*version*/, const std::string& /*prevhash_hex*/,
const std::string& /*nbits_hex*/,
const std::vector<std::string>& /*merkle_branches*/) const
{
// Stage 4b/4c: reconstruct the 80-byte header from (coinb1+en1+en2+coinb2)
// merkle-rooted with merkle_branches, then scrypt_1024_1_1_256(header) and
// return diff1 / pow_hash. Until then the coin-agnostic StratumServer must
// not credit pseudoshares it cannot score, so we return 0.0 — the documented
// parse-error / not-yet sentinel that the vardiff gate already treats as a
// hard reject (no garbage difficulty leaks into the rate monitor).
return 0.0;
}
// ─────────────────────────────────────────────────────────────────────────────
// DGB-specific control surface
// ─────────────────────────────────────────────────────────────────────────────
void DGBWorkSource::set_best_share_hash_fn(std::function<uint256()> fn)
{
std::lock_guard<std::mutex> lk(best_share_mutex_);
best_share_hash_fn_ = std::move(fn);
}
} // namespace dgb::stratum