From f379b71103cbc23347dd806f110358430e6cf2e5 Mon Sep 17 00:00:00 2001 From: frstrtr Date: Fri, 19 Jun 2026 12:58:11 +0000 Subject: [PATCH] dgb: wire subsidy_func into embedded work source coinbasevalue (Phase B) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit DGBWorkSource now carries CoinParams::subsidy_func and derives the embedded template coinbasevalue through dgb::coin::resolve_coinbase_value (the #188 SSOT) — the first PRODUCTION invocation site of subsidy_func, which until now had only test callers. main_dgb.cpp passes params.subsidy_func at construction. External-daemon GBT coinbasevalue is returned verbatim when present (the fallback that MUST PERSIST); the embedded path derives subsidy_func(height) + fees from the DGB oracle decay schedule. Three tests added to the existing dgb_work_source_test (no new target, #143 allowlist trap avoided): embedded derivation at all four reward-era boundaries vs the p2pool-dgb-scrypt oracle, additive fees, and GBT-verbatim precedence. 13/13 PASS. --- src/c2pool/main_dgb.cpp | 3 +- src/impl/dgb/stratum/work_source.cpp | 22 +++++++++- src/impl/dgb/stratum/work_source.hpp | 17 +++++++ src/impl/dgb/test/work_source_test.cpp | 61 +++++++++++++++++++++++++- 4 files changed, 100 insertions(+), 3 deletions(-) diff --git a/src/c2pool/main_dgb.cpp b/src/c2pool/main_dgb.cpp index be95f8045..991930cb8 100644 --- a/src/c2pool/main_dgb.cpp +++ b/src/c2pool/main_dgb.cpp @@ -277,7 +277,8 @@ int run_node(const core::CoinParams& params, bool testnet, // (declared just above, same scope). The StratumServer co-owns the work // source via shared_ptr. auto work_source = std::make_shared( - header_chain, mempool, testnet, std::move(stratum_submit_fn)); + header_chain, mempool, testnet, std::move(stratum_submit_fn), + params.subsidy_func); if (stratum_port != 0) { stratum_server = std::make_unique( diff --git a/src/impl/dgb/stratum/work_source.cpp b/src/impl/dgb/stratum/work_source.cpp index 4405c4d77..c07029160 100644 --- a/src/impl/dgb/stratum/work_source.cpp +++ b/src/impl/dgb/stratum/work_source.cpp @@ -17,6 +17,7 @@ #include #include +#include #include @@ -26,11 +27,13 @@ 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" @@ -38,11 +41,28 @@ DGBWorkSource::DGBWorkSource(c2pool::dgb::HeaderChain& chain, << " min_diff=" << config_.min_difficulty << " max_diff=" << config_.max_difficulty << " target_time=" << config_.target_time - << "s vardiff=" << (config_.vardiff_enabled ? "on" : "off") << ")"; + << "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 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. // ───────────────────────────────────────────────────────────────────────────── diff --git a/src/impl/dgb/stratum/work_source.hpp b/src/impl/dgb/stratum/work_source.hpp index 48276ce3a..a4eff3588 100644 --- a/src/impl/dgb/stratum/work_source.hpp +++ b/src/impl/dgb/stratum/work_source.hpp @@ -41,6 +41,7 @@ #include #include +#include // core::SubsidyFunc — embedded coinbasevalue SSOT feed #include #include @@ -48,6 +49,7 @@ #include #include #include +#include #include #include #include @@ -83,6 +85,7 @@ class DGBWorkSource : public core::stratum::IWorkSource dgb::coin::Mempool& mempool, bool is_testnet, SubmitBlockFn submit_fn, + core::SubsidyFunc subsidy_func, core::stratum::StratumConfig config = {}); ~DGBWorkSource() override; @@ -160,6 +163,16 @@ class DGBWorkSource : public core::stratum::IWorkSource /// is constructed. void set_best_share_hash_fn(std::function fn); + /// Embedded-path coinbasevalue for the template builder (Stage 4c). + /// Routes through dgb::coin::resolve_coinbase_value: when the external + /// digibyted GBT supplied a coinbasevalue it is returned verbatim (the + /// external-daemon fallback that MUST PERSIST); otherwise the value is + /// derived locally as subsidy_func(height) + total_fees from the DGB + /// oracle decay schedule. First PRODUCTION invocation site of + /// CoinParams::subsidy_func (SSOT guarded by test_dgb_coinbase_value). + uint64_t coinbase_value(uint32_t height, uint64_t total_fees, + std::optional gbt_coinbasevalue) const; + private: // External dependencies (non-owning references) c2pool::dgb::HeaderChain& chain_; @@ -169,6 +182,10 @@ class DGBWorkSource : public core::stratum::IWorkSource // Submission dispatch SubmitBlockFn submit_block_fn_; + // Embedded coinbasevalue SSOT feed (CoinParams::subsidy_func). Drives the + // template builder's coinbasevalue when no external-daemon GBT value. + core::SubsidyFunc subsidy_func_; + // Config (held by value; const after construction in MVP) core::stratum::StratumConfig config_; diff --git a/src/impl/dgb/test/work_source_test.cpp b/src/impl/dgb/test/work_source_test.cpp index 5f61bb9ab..6d0dc6f82 100644 --- a/src/impl/dgb/test/work_source_test.cpp +++ b/src/impl/dgb/test/work_source_test.cpp @@ -16,15 +16,23 @@ #include #include #include +#include // dgb::CoinParams::subsidy (oracle SSOT) + +#include // core::SubsidyFunc #include #include #include +#include namespace { +// IDENTICAL to params.hpp `p.subsidy_func` — the live CoinParams indirection. +const core::SubsidyFunc kSubsidyFunc = + [](uint32_t height) -> uint64_t { return dgb::CoinParams::subsidy(height); }; + // Construct a DGBWorkSource over default-constructed coin deps. The submit // callback records whether it was invoked (it must NOT be in the 4a skeleton). struct Fixture { @@ -39,7 +47,7 @@ struct Fixture { return false; }; return std::make_unique( - chain, mempool, /*is_testnet=*/false, fn); + chain, mempool, /*is_testnet=*/false, fn, kSubsidyFunc); } }; @@ -166,4 +174,55 @@ TEST(DgbWorkSource, ComputeShareDifficultyReturnsNotYetSentinel) EXPECT_DOUBLE_EQ(diff, 0.0); } +// ── Embedded coinbasevalue: first production caller of subsidy_func ────────── +// One pin on each side of every DGB reward-era boundary (p2pool-dgb-scrypt +// oracle vectors, test_dgb_subsidy.cpp). +namespace { +struct EraVec { uint32_t height; uint64_t subsidy; const char* era; }; +constexpr EraVec kEraBoundaries[] = { + {67199, 8000000000ULL, "phase1-fixed last"}, + {67200, 7960000000ULL, "phase2 -0.5%/wk first"}, + {399999, 6746441103ULL, "phase2 last"}, + {400000, 2434410000ULL, "phase3 -1%/wk first"}, + {1429999, 2157824200ULL, "phase3 last"}, + {1430000, 1078500000ULL, "phase4 monthly-decay first"}, +}; +} // namespace + +// No external GBT (embedded path): coinbasevalue is derived THROUGH the work +// source's subsidy_func at every era boundary, zero fees -> oracle subsidy. +TEST(DgbWorkSource, CoinbaseValueDerivesViaSubsidyFuncWhenNoGbt) +{ + Fixture fx; + auto ws = fx.make(); + for (const auto& v : kEraBoundaries) { + EXPECT_EQ(ws->coinbase_value(v.height, /*fees=*/0, std::nullopt), v.subsidy) + << "embedded coinbasevalue diverged from oracle subsidy at " << v.era; + } +} + +// Fees compose additively on the embedded path: subsidy + total_fees. +TEST(DgbWorkSource, CoinbaseValueAddsFeesOnEmbeddedPath) +{ + Fixture fx; + auto ws = fx.make(); + constexpr uint64_t kFees = 1234567ULL; + for (const auto& v : kEraBoundaries) { + EXPECT_EQ(ws->coinbase_value(v.height, kFees, std::nullopt), v.subsidy + kFees) + << "fee addition wrong at " << v.era; + } +} + +// External-daemon fallback PERSISTS: a present GBT coinbasevalue is authoritative +// and returned verbatim through the work source, bypassing local derivation. +TEST(DgbWorkSource, CoinbaseValueHonorsGbtVerbatim) +{ + Fixture fx; + auto ws = fx.make(); + constexpr uint64_t kGbt = 99999999999ULL; // deliberately != subsidy+fees + EXPECT_EQ(ws->coinbase_value(/*height=*/400000, /*fees=*/500, + std::optional{kGbt}), + kGbt); +} + } // namespace