|
| 1 | +#pragma once |
| 2 | +// --------------------------------------------------------------------------- |
| 3 | +// dgb::coin::grind_won_nonce -- the embedded real work-gen nonce grinder |
| 4 | +// (#82 Stage 4b/4c). Given a reconstructed 80-byte DigiByte block header and |
| 5 | +// the parent target, it increments the header nonce until the DGB-Scrypt PoW |
| 6 | +// digest satisfies the target, returning the winning nonce. This is the |
| 7 | +// missing primitive between the faithful won-block RECONSTRUCTION (#82 leg-2, |
| 8 | +// reconstruct_won_block.hpp) and a node-B ProcessNewBlock ACCEPT: the A/B |
| 9 | +// delivery proof reached node B but was consensus-rejected "high-hash, proof |
| 10 | +// of work failed" precisely because the forced-won seam carried no real PoW. |
| 11 | +// The grinder closes that gap. |
| 12 | +// |
| 13 | +// SSOT call-through: the hash is computed ONLY via scrypt_pow_hash (the #286 |
| 14 | +// digest CALL SSOT), never a private/bypass routine -- so a nonce this grinder |
| 15 | +// accepts is, by construction, a nonce node B's own Scrypt validation accepts. |
| 16 | +// The comparison is the EXACT satisfaction gate coin/header_chain.hpp runs: |
| 17 | +// pow_hash <= target (inclusive), MSB-first via u256::operator>. |
| 18 | +// |
| 19 | +// Nonce placement: the canonical bitcoin/DigiByte 80-byte header is |
| 20 | +// version(4)|prev(32)|merkle(32)|time(4)|bits(4)|nonce(4) -- the nonce is the |
| 21 | +// LAST field, bytes [76..79], little-endian. This is the identical layout |
| 22 | +// pack(BlockHeaderType) emits (header_sample_build.hpp serializes the header |
| 23 | +// once and feeds the SAME bytes to sha256d AND scrypt), so a header serialized |
| 24 | +// at the ingest/reconstruct boundary and ground here agree byte-for-byte. |
| 25 | +// |
| 26 | +// Standalone-guard discipline: header-only, depends ONLY on scrypt_pow.hpp |
| 27 | +// (which pulls real btclibs scrypt + the header-only u256). No core, no dgb |
| 28 | +// OBJECT lib -- the same no-link constraint dgb_arith256.hpp / scrypt_pow.hpp |
| 29 | +// keep, so its guard TU links the _dgb_scrypt_tus real-scrypt set, not a |
| 30 | +// synthetic hash. Wiring grind -> reconstruct -> submit onto the live |
| 31 | +// forced-won path (serialize BlockHeaderType, grind, write the winning nonce |
| 32 | +// back) is the explicitly-next integration slice; this is the pure primitive. |
| 33 | +// |
| 34 | +// Per-coin isolation: src/impl/dgb/ only. p2pool-merged-v36 surface: NONE -- |
| 35 | +// DGB-Scrypt is a STANDALONE parent; no share format, coinbase commitment, or |
| 36 | +// PPLNS math is touched. |
| 37 | +// --------------------------------------------------------------------------- |
| 38 | + |
| 39 | +#include <array> |
| 40 | +#include <cstddef> |
| 41 | +#include <cstdint> |
| 42 | +#include <optional> |
| 43 | + |
| 44 | +#include <impl/dgb/coin/scrypt_pow.hpp> // scrypt_pow_hash (PoW digest SSOT, #286) |
| 45 | +#include <impl/dgb/coin/dgb_arith256.hpp> // dgb::coin::u256 (also via scrypt_pow.hpp) |
| 46 | + |
| 47 | +namespace dgb::coin { |
| 48 | + |
| 49 | +// Byte offset of the 4-byte little-endian nonce in the canonical 80-byte header. |
| 50 | +inline constexpr std::size_t kHeaderNonceOffset = 76; |
| 51 | + |
| 52 | +// Write a 32-bit nonce little-endian into header[76..79] (consensus byte order). |
| 53 | +inline void put_header_nonce_le(std::array<unsigned char, 80>& header, uint32_t nonce) { |
| 54 | + header[kHeaderNonceOffset + 0] = static_cast<unsigned char>(nonce & 0xffu); |
| 55 | + header[kHeaderNonceOffset + 1] = static_cast<unsigned char>((nonce >> 8) & 0xffu); |
| 56 | + header[kHeaderNonceOffset + 2] = static_cast<unsigned char>((nonce >> 16) & 0xffu); |
| 57 | + header[kHeaderNonceOffset + 3] = static_cast<unsigned char>((nonce >> 24) & 0xffu); |
| 58 | +} |
| 59 | + |
| 60 | +struct GrindOutcome { |
| 61 | + uint32_t nonce; // winning nonce -- also left written into header[76..79] |
| 62 | + u256 pow_hash; // scrypt_pow_hash of the winning header (<= target) |
| 63 | + uint64_t iters; // nonces tried, 1-based -- the search budget consumed |
| 64 | +}; |
| 65 | + |
| 66 | +// Grind header[76..79] from start_nonce until scrypt_pow_hash(header) <= target. |
| 67 | +// |
| 68 | +// header : the reconstructed 80-byte header; on success its nonce field |
| 69 | +// is left set to the winning value (caller broadcasts THIS blob) |
| 70 | +// target : compact_to_target(nBits) for the parent -- the SAME u256 the |
| 71 | +// satisfaction gate compares pow_hash against |
| 72 | +// start_nonce : where to begin the search (default 0) |
| 73 | +// max_iters : search budget. Regtest powLimit is trivially easy, so the live |
| 74 | +// forced-won path satisfies in very few iters; this is the guard |
| 75 | +// that the grinder TERMINATES (no infinite loop), not an expected |
| 76 | +// ceiling. Defaults to the full 2^32 nonce space. |
| 77 | +// |
| 78 | +// Returns the winning {nonce, pow_hash, iters} on success, or std::nullopt if |
| 79 | +// the budget (or the full 2^32 space) is exhausted with no satisfying nonce. |
| 80 | +// "No nonce found in budget" is a hard failure the caller MUST NOT broadcast a |
| 81 | +// header for -- failing closed mirrors the reconstructor's loud-throw posture: |
| 82 | +// a header that does not satisfy its target is rejected by the daemon anyway. |
| 83 | +inline std::optional<GrindOutcome> |
| 84 | +grind_won_nonce(std::array<unsigned char, 80>& header, const u256& target, |
| 85 | + uint32_t start_nonce = 0, |
| 86 | + uint64_t max_iters = (uint64_t{1} << 32)) { |
| 87 | + uint32_t nonce = start_nonce; |
| 88 | + for (uint64_t tried = 1; tried <= max_iters; ++tried) { |
| 89 | + put_header_nonce_le(header, nonce); |
| 90 | + u256 pow = scrypt_pow_hash(header); |
| 91 | + if (!(pow > target)) // pow <= target -> valid PoW (inclusive) |
| 92 | + return GrindOutcome{nonce, pow, tried}; |
| 93 | + ++nonce; |
| 94 | + if (nonce == start_nonce) break; // wrapped full 2^32 space, none satisfied |
| 95 | + } |
| 96 | + return std::nullopt; |
| 97 | +} |
| 98 | + |
| 99 | +} // namespace dgb::coin |
0 commit comments