|
12 | 12 | // reaches avg_target nor widens actual_timespan. A naive all-headers |
13 | 13 | // window would corrupt both; this proves it can't. |
14 | 14 | // |
15 | | -// Header-only guard: links the header-only chain helpers + gtest, no dgb |
16 | | -// OBJECT lib / transport. MUST appear in BOTH the test/CMakeLists foreach AND |
17 | | -// both build.yml --target allowlists, or it becomes a #143 NOT_BUILT sentinel. |
| 15 | +// Links the header-only chain helpers + gtest, plus the btclibs Scrypt PoW TU |
| 16 | +// set (scrypt.cpp + the SHA256 family) so the M3 7b digest conversion runs |
| 17 | +// against the REAL scrypt_1024_1_1_256 hash -- NOT the full btclibs archive, |
| 18 | +// NOT the dgb OBJECT lib / transport. MUST appear in BOTH the test/CMakeLists |
| 19 | +// dedicated target block AND both build.yml --target allowlists, or it becomes |
| 20 | +// a #143 NOT_BUILT sentinel. |
18 | 21 | // --------------------------------------------------------------------------- |
19 | 22 |
|
20 | 23 | #include <cstdint> |
| 24 | +#include <cstring> |
21 | 25 |
|
22 | 26 | #include <gtest/gtest.h> |
23 | 27 |
|
24 | 28 | #include <impl/dgb/coin/header_chain.hpp> |
| 29 | +#include <btclibs/crypto/scrypt.h> |
25 | 30 |
|
26 | 31 | using namespace c2pool::dgb; |
27 | 32 | using dgb::coin::DGB_BLOCK_VERSION_SCRYPT; |
@@ -486,3 +491,58 @@ TEST(HeaderChainValidate, ScryptDigestLittleEndianConversion) |
486 | 491 | EXPECT_EQ(hc.validate_and_append(s), IngestResult::VALIDATED_SCRYPT); |
487 | 492 | EXPECT_EQ(hc.size(), 1u); |
488 | 493 | } |
| 494 | + |
| 495 | + |
| 496 | +// --------------------------------------------------------------------------- |
| 497 | +// REAL SCRYPT DIGEST -> pow_hash, END-TO-END (M3 7b, slice (a)). The prior test |
| 498 | +// pins the from_le_bytes limb mapping against SYNTHETIC bytes; this one closes |
| 499 | +// the loop by running the ACTUAL btclibs scrypt_1024_1_1_256 PoW hash over an |
| 500 | +// 80-byte header, converting the 32-byte little-endian digest via from_le_bytes, |
| 501 | +// and feeding it through the live full-width satisfaction gate. Proves the |
| 502 | +// conversion primitive is correct against a GENUINE Scrypt digest. It does NOT |
| 503 | +// prove scrypt is wired into the production node.cpp ingest path -- that |
| 504 | +// (b)-shaped call lands in embedded-daemon Phase A, scoped with an exe-smoke. |
| 505 | +// --------------------------------------------------------------------------- |
| 506 | +TEST(HeaderChainValidate, RealScryptDigestFeedsSatisfactionGate) |
| 507 | +{ |
| 508 | + // Deterministic 80-byte header (content arbitrary for the conversion proof; |
| 509 | + // fixed so the digest is a stable self-derived KAT). |
| 510 | + char header[80]; |
| 511 | + for (int i = 0; i < 80; ++i) header[i] = (char)(i + 1); |
| 512 | + |
| 513 | + unsigned char digest[32]; |
| 514 | + scrypt_1024_1_1_256(header, reinterpret_cast<char*>(digest)); |
| 515 | + |
| 516 | + // Deterministic: a second run yields the identical digest. |
| 517 | + unsigned char digest2[32]; |
| 518 | + scrypt_1024_1_1_256(header, reinterpret_cast<char*>(digest2)); |
| 519 | + EXPECT_EQ(0, std::memcmp(digest, digest2, 32)); |
| 520 | + |
| 521 | + // Self-derived KAT: pin the actual scrypt PoW hash for this fixed header so |
| 522 | + // any drift in scrypt OR the LE conversion is caught. limb[0] is the least- |
| 523 | + // significant 8 bytes (from_le_bytes / UintToArith256 ordering). |
| 524 | + u256 h = u256::from_le_bytes(digest); |
| 525 | + EXPECT_EQ(h.limb[0], 0x3863b84a2bd8d4c7ULL); |
| 526 | + EXPECT_EQ(h.limb[1], 0xdbc48fcba50b456bULL); |
| 527 | + EXPECT_EQ(h.limb[2], 0x22185f7f7b983293ULL); |
| 528 | + EXPECT_EQ(h.limb[3], 0x64d9ed52d29c3fbdULL); |
| 529 | + |
| 530 | + // The real digest, converted, satisfies a max (pow_limit) target -> VALID. |
| 531 | + HeaderChain hc; |
| 532 | + u256 max_target; for (int i = 0; i < 4; ++i) max_target.limb[i] = 0xffffffffffffffffULL; |
| 533 | + HeaderSample ok{SCRYPT, 1000}; |
| 534 | + ok.target = max_target; |
| 535 | + ok.pow_hash = h; |
| 536 | + EXPECT_EQ(hc.validate_and_append(ok), IngestResult::VALIDATED_SCRYPT); |
| 537 | + EXPECT_EQ(hc.size(), 1u); |
| 538 | + |
| 539 | + // Same real digest vs a tiny target (value 1): the high limbs are nonzero |
| 540 | + // (0x64d9...) so hash > 1 at full width -> REJECT. A low64-only proxy could |
| 541 | + // not see the high limbs -- this proves the converted digest flows the |
| 542 | + // full-width satisfaction path, not a truncated comparison. No mutation. |
| 543 | + HeaderSample weak{SCRYPT, 1075}; |
| 544 | + weak.target.limb[0] = 1; // value == 1, all high limbs zero |
| 545 | + weak.pow_hash = h; |
| 546 | + EXPECT_EQ(hc.validate_and_append(weak), IngestResult::REJECTED); |
| 547 | + EXPECT_EQ(hc.size(), 1u); // unchanged |
| 548 | +} |
0 commit comments