|
| 1 | +// c2pool-dash — DASH (X11 standalone parent, older-than-v35 -> V36) p2pool node |
| 2 | +// entry point. |
| 3 | +// |
| 4 | +// EXE-WIRE slice 2 (integrator 2026-06-23, stacked on launcher slice 1 #387): |
| 5 | +// closes the "DASH is impl-files-only, not runnable" gap. Slice 1 registered |
| 6 | +// DASH in the unified launcher dispatch (parse_blockchain / port / net-magic); |
| 7 | +// this slice gives DASH its own runnable executable that drives the REAL dash |
| 8 | +// consensus primitives, so `dash` is no longer a dispatch label with no body. |
| 9 | +// |
| 10 | +// PER-COIN ISOLATION: src/impl/dash headers only (params/crypto/subsidy); no |
| 11 | +// src/impl/<other-coin> edit, no shared-base/core source edit, dashd RPC |
| 12 | +// fallback untouched. Mirrors the c2pool-bch / c2pool-dgb add_executable shape, |
| 13 | +// pruned to the header-only consensus path (DASH carries no node.cpp run-loop |
| 14 | +// TU on master yet — that is the S7/S8 block-submission lane). |
| 15 | +// |
| 16 | +// ONE MODE TODAY: |
| 17 | +// --selftest (default) : drive the LIVE dash consensus paths std-only, network |
| 18 | +// free, exercising the exact code the sharechain depends on, then exit: |
| 19 | +// (1) make_coin_params — the oracle-sourced CoinParams factory wired, |
| 20 | +// incl. the X11 pow_func reachable through the coin-params seam. |
| 21 | +// (2) X11 PoW — DASH mainnet genesis + a real-node testnet3 |
| 22 | +// block header reproduce their published hashes (CI-pinned KATs, |
| 23 | +// test_dash_x11_kat.cpp). |
| 24 | +// (3) subsidy — post-V20 block reward + 3/4 MN payment match |
| 25 | +// the live-validated mainnet value (test_dash_subsidy.cpp). |
| 26 | +// |
| 27 | +// BLOCK-SUBMISSION (--run) — EXPLICITLY DEFERRED, NOT a silent stub. A won DASH |
| 28 | +// block reaches the network by a dual-path broadcaster, BOTH arms of which live |
| 29 | +// in the unmerged dash-spv-embedded work and are NOT on master: |
| 30 | +// - dashd-RPC submitblock fallback: needs a DASH NodeRPC TU (rpc.cpp/rpc.hpp/ |
| 31 | +// rpc_conf.hpp) — DASH has only coin/rpc_data.hpp (a data placeholder), no |
| 32 | +// RPC client. Porting it (mirroring dgb/coin/rpc.cpp) is the NEXT slice. |
| 33 | +// - embedded P2P relay arm: the broadcaster_full / reconstruct stack (S8). |
| 34 | +// The CoinParams *path* the RPC fallback consumes IS wired here (make_coin_params, |
| 35 | +// oracle-sourced via dash::PoolConfig SSOT); the block-submission SINKS are the |
| 36 | +// deferred piece. --run prints this status and exits cleanly so a smoke gate that |
| 37 | +// invokes it is never misled into thinking block relay is live. |
| 38 | +// |
| 39 | +// Conformance oracle: frstrtr/p2pool-dash (older-than-v35; transition 16 -> v36). |
| 40 | +// External dashd RPC stays as a fallback alongside the (future) embedded path. |
| 41 | + |
| 42 | +#include <impl/dash/params.hpp> |
| 43 | +#include <impl/dash/crypto/hash_x11.hpp> |
| 44 | +#include <impl/dash/coin/utxo_adapter.hpp> // must precede subsidy.hpp (dash_txid in scope) |
| 45 | +#include <impl/dash/coin/subsidy.hpp> |
| 46 | + |
| 47 | +#include <core/coin_params.hpp> |
| 48 | +#include <core/uint256.hpp> |
| 49 | + |
| 50 | +#include <cstdint> |
| 51 | +#include <cstring> |
| 52 | +#include <iostream> |
| 53 | +#include <string> |
| 54 | + |
| 55 | +#ifndef C2POOL_VERSION |
| 56 | +#define C2POOL_VERSION "dev" |
| 57 | +#endif |
| 58 | + |
| 59 | +namespace { |
| 60 | + |
| 61 | +using dash::coin::compute_dash_block_reward_post_v20; |
| 62 | +using dash::coin::compute_dash_mn_payment_post_v20; |
| 63 | + |
| 64 | +void print_banner(const char* argv0) |
| 65 | +{ |
| 66 | + std::cout |
| 67 | + << "c2pool-dash " << C2POOL_VERSION << " — DASH (X11, older-than-v35 -> V36)\n\n" |
| 68 | + << "Usage: " << argv0 << " [--version] [--help] [--selftest]\n" |
| 69 | + << " " << argv0 << " --run (block-submission DEFERRED — see status)\n\n" |
| 70 | + << "Status: consensus layer live (X11 PoW, subsidy, oracle CoinParams).\n" |
| 71 | + << " Block submission (won-block dual-path broadcaster) is the\n" |
| 72 | + << " next stacked slice; external dashd RPC stays the fallback.\n" |
| 73 | + << "Consensus: X11 PoW + block identity; 2.5 min spacing; 5 DASH post-V20\n" |
| 74 | + << " base, -1/14 per 210240; masternode payment 3/4 of block value.\n"; |
| 75 | +} |
| 76 | + |
| 77 | +// Serialize an 80-byte DASH block header (LE; host is LE on the x86_64 target). |
| 78 | +void serialize_header(unsigned char out[80], uint32_t version, const char* prev_hex, |
| 79 | + const char* merkle_hex, uint32_t time, uint32_t bits, uint32_t nonce) |
| 80 | +{ |
| 81 | + uint256 prev_block; prev_block.SetHex(prev_hex); |
| 82 | + uint256 merkle_root; merkle_root.SetHex(merkle_hex); |
| 83 | + size_t off = 0; |
| 84 | + std::memcpy(out + off, &version, 4); off += 4; |
| 85 | + std::memcpy(out + off, prev_block.data(), 32); off += 32; |
| 86 | + std::memcpy(out + off, merkle_root.data(), 32); off += 32; |
| 87 | + std::memcpy(out + off, &time, 4); off += 4; |
| 88 | + std::memcpy(out + off, &bits, 4); off += 4; |
| 89 | + std::memcpy(out + off, &nonce, 4); off += 4; |
| 90 | +} |
| 91 | + |
| 92 | +// (1) The oracle CoinParams factory is wired and self-consistent, AND the X11 |
| 93 | +// pow_func is reachable through the coin-params seam (the path the work |
| 94 | +// source + block-identity checks consume). |
| 95 | +int check_coin_params() |
| 96 | +{ |
| 97 | + const core::CoinParams main = dash::make_coin_params(/*testnet=*/false); |
| 98 | + const core::CoinParams test = dash::make_coin_params(/*testnet=*/true); |
| 99 | + |
| 100 | + int fails = 0; |
| 101 | + auto want = [&](bool ok, const char* what) { |
| 102 | + std::cout << "[selftest] coin_params: " << what << (ok ? " ok\n" : " FAIL\n"); |
| 103 | + if (!ok) ++fails; |
| 104 | + }; |
| 105 | + want(main.symbol == "DASH", "symbol == DASH"); |
| 106 | + // CoinParams.p2p_port is the SHARECHAIN/pool peer port (dash::PoolConfig SSOT), |
| 107 | + // distinct from the DASH coin-daemon P2P port (9999/19999) wired in slice-1's |
| 108 | + // get_coin_p2p_port. Assert the sharechain SSOT here. |
| 109 | + want(main.p2p_port == 8999, "mainnet sharechain p2p_port == 8999 (SSOT)"); |
| 110 | + want(test.p2p_port == 18999, "testnet sharechain p2p_port == 18999 (SSOT)"); |
| 111 | + want(main.current_share_version == 16, "share_version == 16 (older-than-v35 baseline)"); |
| 112 | + want(main.address_version == 76, "mainnet pubkey addr version == 76 (X...)"); |
| 113 | + want(static_cast<bool>(main.pow_func), "pow_func wired"); |
| 114 | + |
| 115 | + // Drive X11 THROUGH the CoinParams pow_func seam against the genesis header. |
| 116 | + if (main.pow_func) { |
| 117 | + unsigned char hdr[80]; |
| 118 | + serialize_header(hdr, 1, "0000000000000000000000000000000000000000000000000000000000000000", |
| 119 | + "e0028eb9648db56b1ac77cf090b99048a8007e2bb64b68f092c03c7f56a662c7", |
| 120 | + 1390095618u, 0x1e0ffff0u, 28917698u); |
| 121 | + const uint256 pow = main.pow_func(std::span<const unsigned char>(hdr, 80)); |
| 122 | + const bool ok = pow.GetHex() == "00000ffd590b1485b3caadc19b22e6379c733355108f107a430458cdf3407ab6"; |
| 123 | + want(ok, "pow_func(genesis) reproduces genesis hash"); |
| 124 | + } |
| 125 | + return fails; |
| 126 | +} |
| 127 | + |
| 128 | +// (2) X11 PoW KATs: mainnet genesis + a real-node testnet3 block (CI-pinned, |
| 129 | +// test_dash_x11_kat.cpp). Pins BLAKE->...->ECHO end to end via the direct |
| 130 | +// dash::crypto::hash_x11 entry. |
| 131 | +int check_x11_kats() |
| 132 | +{ |
| 133 | + int fails = 0; |
| 134 | + struct Vec { const char* name; uint32_t v; const char* prev; const char* merkle; |
| 135 | + uint32_t t; uint32_t bits; uint32_t nonce; const char* expect; }; |
| 136 | + const Vec vecs[] = { |
| 137 | + { "mainnet-genesis", 1, |
| 138 | + "0000000000000000000000000000000000000000000000000000000000000000", |
| 139 | + "e0028eb9648db56b1ac77cf090b99048a8007e2bb64b68f092c03c7f56a662c7", |
| 140 | + 1390095618u, 0x1e0ffff0u, 28917698u, |
| 141 | + "00000ffd590b1485b3caadc19b22e6379c733355108f107a430458cdf3407ab6" }, |
| 142 | + { "testnet3-#1497944", 536870912u, |
| 143 | + "000000dbbc08ee519459b38b02bb7754b455dd00cd74069a1352f08f0dd986db", |
| 144 | + "0464a4ac5f058a742f6aa42b2b3c7489abde7609b529612bcfa5da34b10bdb1b", |
| 145 | + 1781737170u, 0x1e00f256u, 721236u, |
| 146 | + "000000b6a4e5ea1a0854ef83f0028dde5b96cdaacc604decd8b064d0cea38234" }, |
| 147 | + }; |
| 148 | + for (const auto& vc : vecs) { |
| 149 | + unsigned char hdr[80]; |
| 150 | + serialize_header(hdr, vc.v, vc.prev, vc.merkle, vc.t, vc.bits, vc.nonce); |
| 151 | + const uint256 pow = dash::crypto::hash_x11(hdr, sizeof(hdr)); |
| 152 | + const bool ok = pow.GetHex() == vc.expect; |
| 153 | + std::cout << "[selftest] x11 KAT " << vc.name << ": " << pow.GetHex() |
| 154 | + << (ok ? " ok\n" : " FAIL\n"); |
| 155 | + if (!ok) ++fails; |
| 156 | + } |
| 157 | + return fails; |
| 158 | +} |
| 159 | + |
| 160 | +// (3) Subsidy: post-V20 block reward + 3/4 MN payment (test_dash_subsidy.cpp, |
| 161 | +// live-validated against dashd getblocktemplate at h=2459985). |
| 162 | +int check_subsidy() |
| 163 | +{ |
| 164 | + int fails = 0; |
| 165 | + const int64_t reward = compute_dash_block_reward_post_v20(2459985); |
| 166 | + const bool r_ok = reward == 177'022'505LL; |
| 167 | + std::cout << "[selftest] subsidy h=2459985 reward = " << reward |
| 168 | + << (r_ok ? " ok\n" : " FAIL (want 177022505)\n"); |
| 169 | + if (!r_ok) ++fails; |
| 170 | + |
| 171 | + const int64_t mn = compute_dash_mn_payment_post_v20(200'000'000LL); |
| 172 | + const bool mn_ok = mn == 150'000'000LL; |
| 173 | + std::cout << "[selftest] masternode payment 3/4 of 2.0 DASH = " << mn |
| 174 | + << (mn_ok ? " ok\n" : " FAIL (want 150000000)\n"); |
| 175 | + if (!mn_ok) ++fails; |
| 176 | + return fails; |
| 177 | +} |
| 178 | + |
| 179 | +int run_selftest() |
| 180 | +{ |
| 181 | + std::cout << "[selftest] driving live DASH consensus (network-free)\n"; |
| 182 | + int fails = 0; |
| 183 | + fails += check_coin_params(); |
| 184 | + fails += check_x11_kats(); |
| 185 | + fails += check_subsidy(); |
| 186 | + if (fails == 0) { |
| 187 | + std::cout << "[selftest] OK — CoinParams + X11 PoW + subsidy all conform to oracle\n"; |
| 188 | + return 0; |
| 189 | + } |
| 190 | + std::cout << "[selftest] FAIL — " << fails << " consensus check(s) failed\n"; |
| 191 | + return 1; |
| 192 | +} |
| 193 | + |
| 194 | +// --run: block submission is DEFERRED. Print the exact deferral status so a smoke |
| 195 | +// gate is never misled, then exit cleanly (the consensus layer IS exercised by |
| 196 | +// --selftest; the run-loop standup lands with the broadcaster slice). |
| 197 | +int run_node_stub() |
| 198 | +{ |
| 199 | + std::cout |
| 200 | + << "[run] DASH block submission is DEFERRED to the next stacked slice.\n" |
| 201 | + << "[run] - dashd-RPC submitblock fallback: needs a DASH NodeRPC TU\n" |
| 202 | + << "[run] (rpc.cpp/rpc.hpp/rpc_conf.hpp); only coin/rpc_data.hpp exists.\n" |
| 203 | + << "[run] - embedded P2P relay arm: S8 broadcaster/reconstruct stack.\n" |
| 204 | + << "[run] The oracle CoinParams path the fallback consumes IS wired; run\n" |
| 205 | + << "[run] --selftest to exercise the live consensus layer (X11 + subsidy).\n"; |
| 206 | + return 0; |
| 207 | +} |
| 208 | + |
| 209 | +} // namespace |
| 210 | + |
| 211 | +int main(int argc, char** argv) |
| 212 | +{ |
| 213 | + bool want_help = false; |
| 214 | + bool want_run = false; |
| 215 | + for (int i = 1; i < argc; ++i) { |
| 216 | + if (std::strcmp(argv[i], "--version") == 0) { |
| 217 | + std::cout << "c2pool-dash " << C2POOL_VERSION << "\n"; |
| 218 | + return 0; |
| 219 | + } |
| 220 | + if (std::strcmp(argv[i], "--help") == 0) want_help = true; |
| 221 | + if (std::strcmp(argv[i], "--run") == 0) want_run = true; |
| 222 | + // --selftest is the default; accepted explicitly for symmetry. |
| 223 | + } |
| 224 | + |
| 225 | + print_banner(argv[0]); |
| 226 | + if (want_help) |
| 227 | + return 0; |
| 228 | + if (want_run) |
| 229 | + return run_node_stub(); |
| 230 | + return run_selftest(); |
| 231 | +} |
0 commit comments