|
| 1 | +// Forward-only TODO (integrator 2026-06-18): wire CoinParams::subsidy_func into |
| 2 | +// the embedded coinbase builder + a 4-era boundary conformance test, with the |
| 3 | +// external-daemon GBT fallback preserved. |
| 4 | +// |
| 5 | +// This guards src/impl/dgb/coin/embedded_coinbase_value.hpp — the SSOT that |
| 6 | +// feeds subsidy_func(height)+fees to the embedded TemplateBuilder coinbasevalue |
| 7 | +// (Stage 4c, work_source.cpp) and that takes a live digibyted GBT coinbasevalue |
| 8 | +// verbatim whenever present (the external-daemon fallback that MUST PERSIST). |
| 9 | +// |
| 10 | +// The subsidy values themselves are byte-exact vs the p2pool-dgb-scrypt oracle |
| 11 | +// (see test_dgb_subsidy.cpp). This test proves the *wiring*: that the embedded |
| 12 | +// coinbasevalue is derived THROUGH the CoinParams::subsidy_func indirection |
| 13 | +// (which params.hpp populates and which previously had zero invocation sites) |
| 14 | +// and that subsidy + fees compose correctly at all four reward-era boundaries. |
| 15 | + |
| 16 | +#include <gtest/gtest.h> |
| 17 | +#include <cstdint> |
| 18 | +#include <optional> |
| 19 | +#include <stdexcept> |
| 20 | + |
| 21 | +#include <core/pow.hpp> // core::SubsidyFunc |
| 22 | +#include <impl/dgb/config_coin.hpp> // dgb::CoinParams::subsidy (oracle SSOT) |
| 23 | +#include <impl/dgb/coin/embedded_coinbase_value.hpp> |
| 24 | + |
| 25 | +namespace { |
| 26 | + |
| 27 | +// IDENTICAL lambda to params.hpp `p.subsidy_func` — exercises the same |
| 28 | +// std::function indirection the live CoinParams carries. |
| 29 | +const core::SubsidyFunc kSubsidyFunc = |
| 30 | + [](uint32_t height) -> uint64_t { return dgb::CoinParams::subsidy(height); }; |
| 31 | + |
| 32 | +struct EraVec { uint32_t height; uint64_t subsidy; const char* era; }; |
| 33 | + |
| 34 | +// One pin on each side of every reward-era boundary. Expected subsidy values |
| 35 | +// are the p2pool-dgb-scrypt oracle vectors (test_dgb_subsidy.cpp). |
| 36 | +constexpr EraVec kEraBoundaries[] = { |
| 37 | + {67199, 8000000000ULL, "phase1-fixed last"}, |
| 38 | + {67200, 7960000000ULL, "phase2 -0.5%/wk first"}, |
| 39 | + {399999, 6746441103ULL, "phase2 last"}, |
| 40 | + {400000, 2434410000ULL, "phase3 -1%/wk first"}, |
| 41 | + {1429999, 2157824200ULL, "phase3 last"}, |
| 42 | + {1430000, 1078500000ULL, "phase4 monthly-decay first"}, |
| 43 | +}; |
| 44 | + |
| 45 | +} // namespace |
| 46 | + |
| 47 | +// Zero fees: embedded coinbasevalue == the oracle subsidy, derived through the |
| 48 | +// subsidy_func callback (not the static call) at every era boundary. |
| 49 | +TEST(DgbCoinbaseValue, EmbeddedEqualsSubsidyAtEveryEraBoundary) { |
| 50 | + for (const auto& v : kEraBoundaries) { |
| 51 | + EXPECT_EQ(dgb::coin::embedded_coinbase_value(kSubsidyFunc, v.height, /*fees=*/0), |
| 52 | + v.subsidy) |
| 53 | + << "embedded coinbasevalue diverged from oracle subsidy at " << v.era; |
| 54 | + } |
| 55 | +} |
| 56 | + |
| 57 | +// Non-zero fees compose additively: coinbasevalue = subsidy + total_fees. |
| 58 | +TEST(DgbCoinbaseValue, AddsTransactionFees) { |
| 59 | + constexpr uint64_t kFees = 1234567ULL; |
| 60 | + for (const auto& v : kEraBoundaries) { |
| 61 | + EXPECT_EQ(dgb::coin::embedded_coinbase_value(kSubsidyFunc, v.height, kFees), |
| 62 | + v.subsidy + kFees) |
| 63 | + << "fee addition wrong at " << v.era; |
| 64 | + } |
| 65 | +} |
| 66 | + |
| 67 | +// External-daemon fallback PERSISTS: when a GBT coinbasevalue is present it is |
| 68 | +// authoritative and returned verbatim — the embedded derivation is bypassed, |
| 69 | +// even when it would differ. |
| 70 | +TEST(DgbCoinbaseValue, GbtValueTakesPrecedence) { |
| 71 | + constexpr uint32_t kHeight = 400000; // phase3 boundary |
| 72 | + constexpr uint64_t kGbt = 99999999999ULL; // deliberately != subsidy+fees |
| 73 | + EXPECT_EQ(dgb::coin::resolve_coinbase_value(kSubsidyFunc, kHeight, /*fees=*/500, |
| 74 | + std::optional<uint64_t>{kGbt}), |
| 75 | + kGbt); |
| 76 | +} |
| 77 | + |
| 78 | +// No external daemon (nullopt): resolve derives locally via subsidy_func+fees. |
| 79 | +TEST(DgbCoinbaseValue, NoGbtDerivesEmbedded) { |
| 80 | + constexpr uint32_t kHeight = 400000; |
| 81 | + constexpr uint64_t kFees = 777ULL; |
| 82 | + EXPECT_EQ(dgb::coin::resolve_coinbase_value(kSubsidyFunc, kHeight, kFees, std::nullopt), |
| 83 | + dgb::CoinParams::subsidy(kHeight) + kFees); |
| 84 | +} |
| 85 | + |
| 86 | +// An unset subsidy_func must fail LOUDLY (no silent zero-subsidy coinbase). |
| 87 | +TEST(DgbCoinbaseValue, UnsetSubsidyFuncThrows) { |
| 88 | + core::SubsidyFunc empty; |
| 89 | + EXPECT_THROW(dgb::coin::embedded_coinbase_value(empty, 400000, 0), std::logic_error); |
| 90 | +} |
0 commit comments