Skip to content

Commit f49a89e

Browse files
committed
dgb: M3 §7b prove scrypt digest->pow_hash conversion vs REAL scrypt_1024_1_1_256
Links the btclibs Scrypt PoW TU set (scrypt.cpp + SHA256 family) into header_chain_test and adds an end-to-end test that runs the real scrypt_1024_1_1_256 over an 80-byte header, converts the 32-byte LE digest via u256::from_le_bytes, pins it as a self-derived KAT, and feeds it through the live full-width satisfaction gate (accept vs pow_limit, reject vs a tiny target -> proves full-width ordering, not a low64 proxy). Compiles only the scrypt + SHA256 crypto TUs directly (not the full btclibs archive, whose siphash TU needs the removed base_uint<256> symbol, and not core/the dgb OBJECT lib) -- linking an existing scrypt.cpp, no shared source modified. Same target name, still in both build.yml --target allowlists. Proves the conversion primitive end-to-end against a genuine Scrypt digest; does NOT wire scrypt into the production node.cpp ingest path (that lands in embedded-daemon Phase A, scoped with an exe-smoke).
1 parent b1e7bc7 commit f49a89e

2 files changed

Lines changed: 86 additions & 4 deletions

File tree

src/impl/dgb/test/CMakeLists.txt

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,32 @@ if (BUILD_TESTING AND GTest_FOUND)
2525
# transport rpc.cpp wiring is deferred (Option-B scope). Each MUST appear
2626
# in BOTH this ctest registration AND the build.yml --target allowlist,
2727
# or it becomes a #143-style NOT_BUILT sentinel that reds master.
28-
foreach(dgb_guard rpc_request_test softfork_check_test genesis_check_test algo_select_test digishield_walk_test header_chain_test)
28+
foreach(dgb_guard rpc_request_test softfork_check_test genesis_check_test algo_select_test digishield_walk_test)
2929
add_executable(${dgb_guard} ${dgb_guard}.cpp)
3030
target_link_libraries(${dgb_guard} PRIVATE
3131
GTest::gtest_main GTest::gtest nlohmann_json::nlohmann_json)
3232
gtest_add_tests(${dgb_guard} "" AUTO)
3333
endforeach()
34+
35+
# header_chain_test compiles the btclibs Scrypt PoW TU set DIRECTLY (the
36+
# same crypto/*.cpp sources btclibs builds, with the same default flags) so
37+
# the M3 7b digest conversion is exercised against the REAL
38+
# scrypt_1024_1_1_256 hash, not a synthetic digest. Only the scrypt + SHA256
39+
# family is pulled in -- NOT the full btclibs archive (its siphash.o needs
40+
# the removed base_uint<256> symbol) and NOT core/the dgb OBJECT lib. This
41+
# is linking against an existing scrypt.cpp, no shared source modified: the
42+
# single DGB smoke gate holds, no shared-base flag owed. Same target name,
43+
# so it stays in BOTH build.yml --target allowlists unchanged.
44+
set(_dgb_scrypt_tus
45+
${CMAKE_SOURCE_DIR}/src/btclibs/crypto/scrypt.cpp
46+
${CMAKE_SOURCE_DIR}/src/btclibs/crypto/sha256.cpp
47+
${CMAKE_SOURCE_DIR}/src/btclibs/crypto/sha256_sse4.cpp
48+
${CMAKE_SOURCE_DIR}/src/btclibs/crypto/sha256_sse41.cpp
49+
${CMAKE_SOURCE_DIR}/src/btclibs/crypto/sha256_avx2.cpp
50+
${CMAKE_SOURCE_DIR}/src/btclibs/crypto/sha256_shani.cpp)
51+
add_executable(header_chain_test header_chain_test.cpp ${_dgb_scrypt_tus})
52+
target_include_directories(header_chain_test PRIVATE ${CMAKE_SOURCE_DIR}/src/btclibs)
53+
target_link_libraries(header_chain_test PRIVATE
54+
GTest::gtest_main GTest::gtest nlohmann_json::nlohmann_json)
55+
gtest_add_tests(header_chain_test "" AUTO)
3456
endif()

src/impl/dgb/test/header_chain_test.cpp

Lines changed: 63 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,16 +12,21 @@
1212
// reaches avg_target nor widens actual_timespan. A naive all-headers
1313
// window would corrupt both; this proves it can't.
1414
//
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.
1821
// ---------------------------------------------------------------------------
1922

2023
#include <cstdint>
24+
#include <cstring>
2125

2226
#include <gtest/gtest.h>
2327

2428
#include <impl/dgb/coin/header_chain.hpp>
29+
#include <btclibs/crypto/scrypt.h>
2530

2631
using namespace c2pool::dgb;
2732
using dgb::coin::DGB_BLOCK_VERSION_SCRYPT;
@@ -486,3 +491,58 @@ TEST(HeaderChainValidate, ScryptDigestLittleEndianConversion)
486491
EXPECT_EQ(hc.validate_and_append(s), IngestResult::VALIDATED_SCRYPT);
487492
EXPECT_EQ(hc.size(), 1u);
488493
}
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

Comments
 (0)