From 2356967b73d3171b061e625c9fe2a686aebc62c0 Mon Sep 17 00:00:00 2001 From: frstrtr Date: Sun, 21 Jun 2026 16:29:11 +0000 Subject: [PATCH] dgb: aux-parent coinbase serialization parity baseline KAT (D0) Freezes the pre-templating wire image of the shared doge::coin::CMerkleTx (parent-coinbase carrier) so the v36-standardize Scrypt value-type extraction can prove its m_tx type swap is byte-neutral. Asserts the cross-type invariant directly: ltc::coin::MutableTransaction and dgb::coin::MutableTransaction serialize the parent coinbase byte-identically (both extend bitcoin_family::coin::base_transaction). Test-only, fenced to src/impl/dgb/; CONSUMES the shared aux module, modifies nothing. Golden anchored to master HEAD f732061bc; a later rebase that re-shapes the serializer trips the test rather than silently shifting the golden. 4/4 green. Registered in both test/CMakeLists.txt and the build.yml --target allowlist. --- .github/workflows/build.yml | 4 +- src/impl/dgb/test/CMakeLists.txt | 18 ++ .../test/aux_parent_coinbase_parity_test.cpp | 188 ++++++++++++++++++ 3 files changed, 208 insertions(+), 2 deletions(-) create mode 100644 src/impl/dgb/test/aux_parent_coinbase_parity_test.cpp diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 3cd78132e..13fce7edd 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -82,7 +82,7 @@ jobs: test_address_resolution test_compute_share_target \ test_utxo test_dgb_subsidy test_dgb_coinbase_value dgb_share_test dgb_block_assembly_test dgb_header_sample_build_test dgb_header_ingest_test dgb_mempool_ingest_test \ dgb_gentx_coinbase_test nmc_auxpow_merkle_test nmc_template_builder_test nmc_auxpow_wire_test nmc_reconstruct_won_block_test dgb_gentx_share_path_test dgb_other_tx_resolver_test \ - dgb_other_tx_assembler_test dgb_reconstruct_won_block_test dgb_reconstruct_closure_test dgb_gentx_unpack_test dgb_work_source_test dgb_template_builder_test dgb_embedded_coin_node_test dgb_embedded_tx_select_test dgb_template_other_txs_test dgb_coinbase_value_parity_test dgb_submit_classify_test \ + dgb_other_tx_assembler_test dgb_reconstruct_won_block_test dgb_reconstruct_closure_test dgb_gentx_unpack_test dgb_work_source_test dgb_template_builder_test dgb_embedded_coin_node_test dgb_embedded_tx_select_test dgb_template_other_txs_test dgb_coinbase_value_parity_test dgb_submit_classify_test dgb_aux_parent_coinbase_parity_test \ rpc_request_test softfork_check_test genesis_check_test algo_select_test digishield_walk_test header_chain_test \ dgb_coin_node_seam_test dgb_block_broadcast_test dgb_won_block_dispatch_test dgb_forced_won_share_dualpath_test dgb_scrypt_pow_test dgb_nonce_grinder_test dgb_regrind_block_test dgb_won_block_finalize_test v37_test \ -j$(nproc) @@ -214,7 +214,7 @@ jobs: test_address_resolution test_compute_share_target \ test_utxo test_dgb_subsidy test_dgb_coinbase_value dgb_share_test dgb_block_assembly_test dgb_header_sample_build_test dgb_header_ingest_test dgb_mempool_ingest_test \ dgb_gentx_coinbase_test nmc_auxpow_merkle_test nmc_template_builder_test nmc_auxpow_wire_test nmc_reconstruct_won_block_test dgb_gentx_share_path_test dgb_other_tx_resolver_test \ - dgb_other_tx_assembler_test dgb_reconstruct_won_block_test dgb_reconstruct_closure_test dgb_gentx_unpack_test dgb_work_source_test dgb_template_builder_test dgb_embedded_coin_node_test dgb_embedded_tx_select_test dgb_template_other_txs_test dgb_coinbase_value_parity_test dgb_submit_classify_test \ + dgb_other_tx_assembler_test dgb_reconstruct_won_block_test dgb_reconstruct_closure_test dgb_gentx_unpack_test dgb_work_source_test dgb_template_builder_test dgb_embedded_coin_node_test dgb_embedded_tx_select_test dgb_template_other_txs_test dgb_coinbase_value_parity_test dgb_submit_classify_test dgb_aux_parent_coinbase_parity_test \ rpc_request_test softfork_check_test genesis_check_test algo_select_test digishield_walk_test header_chain_test \ dgb_coin_node_seam_test dgb_block_broadcast_test dgb_won_block_dispatch_test dgb_forced_won_share_dualpath_test dgb_scrypt_pow_test dgb_nonce_grinder_test dgb_regrind_block_test dgb_won_block_finalize_test test_coin_broadcaster test_multiaddress_pplns test_pplns_stress \ v37_test \ diff --git a/src/impl/dgb/test/CMakeLists.txt b/src/impl/dgb/test/CMakeLists.txt index ada041fde..d26a490f3 100644 --- a/src/impl/dgb/test/CMakeLists.txt +++ b/src/impl/dgb/test/CMakeLists.txt @@ -434,4 +434,22 @@ if (BUILD_TESTING AND GTest_FOUND) GTest::gtest_main GTest::gtest) gtest_add_tests(dgb_submit_classify_test "" AUTO) + # --- DGB+DOGE (D0) aux-pow parent-coinbase serialization PARITY BASELINE ---- + # Pins the pre-templating wire image of the shared aux module struct + # doge::coin::CMerkleTx (src/impl/doge/coin/auxpow.hpp) anchored to master + # f732061bc, so the v36-standardize Scrypt value-type extraction can prove + # its parent-coinbase type swap is byte-neutral. Links the ltc OBJECT lib + # (member type ltc::coin::MutableTransaction) + dgb (neutral stand-in type + # for the cross-type byte-identity assert) — the -DAUX_DOGE consumption path + # legitimately couples to ltc. Test-only; consumes the shared module, does + # not modify it. MUST appear in BOTH this registration AND the build.yml + # --target allowlist. + add_executable(dgb_aux_parent_coinbase_parity_test aux_parent_coinbase_parity_test.cpp) + target_link_libraries(dgb_aux_parent_coinbase_parity_test PRIVATE + GTest::gtest_main GTest::gtest + core dgb ltc_coin + c2pool_payout c2pool_merged_mining c2pool_hashrate c2pool_storage + dgb_coin pool sharechain) + gtest_add_tests(dgb_aux_parent_coinbase_parity_test "" AUTO) + endif() diff --git a/src/impl/dgb/test/aux_parent_coinbase_parity_test.cpp b/src/impl/dgb/test/aux_parent_coinbase_parity_test.cpp new file mode 100644 index 000000000..da132eb73 --- /dev/null +++ b/src/impl/dgb/test/aux_parent_coinbase_parity_test.cpp @@ -0,0 +1,188 @@ +// --------------------------------------------------------------------------- +// DGB+DOGE merged-mining (D0) — aux-pow parent-coinbase serialization PARITY +// BASELINE KAT. Fenced / test-only — consumes the shared aux module, modifies +// nothing. +// +// PURPOSE. The v36-standardize Scrypt value-type extraction will lift the +// parent-coinbase member out of ltc::coin into a neutral Scrypt-family base so +// that a non-LTC parent (DGB-as-parent under -DAUX_DOGE=ON) can occupy it +// without the dgb -> ltc_coin build edge. Today that member is hard-typed: +// +// src/impl/doge/coin/auxpow.hpp:77 ltc::coin::MutableTransaction m_tx; +// src/impl/doge/coin/auxpow.hpp:84 ::Serialize(s, TX_NO_WITNESS(m_tx)); +// +// This KAT freezes the CURRENT, PRE-TEMPLATING wire image of the whole +// doge::coin::CMerkleTx (the struct that carries m_tx) so the extraction PR can +// prove its type swap is byte-neutral: re-run after templating must reproduce +// the golden BYTE-FOR-BYTE. A single moved byte fails here. +// +// ANCHOR. Golden captured against the shared aux module at master HEAD +// git rev-parse origin/master == f732061bc +// recorded below as AUX_MODULE_MASTER_SHA. A later rebase that silently +// re-shapes the serializer cannot move the golden without tripping this test; +// if the anchor SHA and the live module diverge in field order, that is a HARD +// STOP back to integrator, not a golden update. +// +// CROSS-TYPE INVARIANT. The extraction's whole safety claim is that the parent +// coinbase serializes identically whichever Scrypt-family type holds it. We +// assert that directly: the same canonical coinbase fields, serialized once +// through ltc::coin::MutableTransaction (the real member type) and once through +// dgb::coin::MutableTransaction (a stand-in for the future neutral / DGB-parent +// type), produce identical witness-stripped bytes. Both extend +// bitcoin_family::coin::base_transaction, so a divergence would mean the shared +// base is NOT actually shared — exactly the thing extraction must not break. +// +// Per-coin isolation note: this test legitimately links the ltc OBJECT lib +// because it exercises the -DAUX_DOGE dual-parent consumption path, whose +// shared module (ltc-doge's domain) already includes ltc/coin/transaction.hpp +// by design. We CONSUME the module; we do not modify it. MUST appear in BOTH +// test/CMakeLists.txt AND the build.yml --target allowlist or it becomes a +// NOT_BUILT sentinel that reds master (cf. DGB #137). +// --------------------------------------------------------------------------- + +#include + +#include // the REAL shared module (SSOT) +#include // ltc::coin::MutableTransaction (member type) +#include // dgb::coin::MutableTransaction + dgb::coin::TX_NO_WITNESS (neutral stand-in) + +#include +#include +#include + +#include +#include +#include + +namespace { + +// Anchor — see file header. Update ONLY together with a re-verified golden. +constexpr const char* AUX_MODULE_MASTER_SHA = "f732061bc"; + +std::vector unhex(const std::string& h) { + std::vector v; v.reserve(h.size() / 2); + auto nyb = [](char c) -> int { return (c <= '9') ? c - '0' : (c | 0x20) - 'a' + 10; }; + for (size_t i = 0; i + 1 < h.size(); i += 2) + v.push_back(static_cast((nyb(h[i]) << 4) | nyb(h[i + 1]))); + return v; +} +std::string tohex(const std::vector& v) { + static const char* H = "0123456789abcdef"; + std::string s; s.reserve(v.size() * 2); + for (unsigned char b : v) { s.push_back(H[b >> 4]); s.push_back(H[b & 0xf]); } + return s; +} +template +std::string pack_hex(const T& value) { + auto packed = pack(value); + auto sp = packed.get_span(); + std::vector v( + reinterpret_cast(sp.data()), + reinterpret_cast(sp.data()) + sp.size()); + return tohex(v); +} + +OPScript script_of(const std::vector& bytes) { + return OPScript(bytes.data(), bytes.data() + bytes.size()); +} + +// --- canonical, deterministic parent coinbase fields ----------------------- +// A minimal but representative coinbase: null prevout, a fixed coinbase +// scriptSig, one P2PKH-shaped payout, version 1, locktime 0. Shared verbatim +// between the ltc- and dgb-typed builders below so the only variable is the +// transaction TYPE, not the data. +const std::vector CB_SCRIPT = unhex("03a1b2c3041122334455667788"); +const std::vector PK_SCRIPT = + unhex(std::string("76a914") + std::string(40, '3') + "88ac"); +constexpr int32_t TX_VERSION = 1; +constexpr uint32_t TX_LOCKTIME = 0; +constexpr uint32_t CB_SEQUENCE = 0xffffffffu; +constexpr int64_t CB_VALUE = 5000000000ll; // 50.00000000 + +template +MTx build_parent_coinbase() { + MTx tx; + tx.version = TX_VERSION; + tx.locktime = TX_LOCKTIME; + + typename decltype(tx.vin)::value_type in; + in.prevout.hash = uint256(); // null + in.prevout.index = 0xffffffffu; // coinbase marker + in.scriptSig = script_of(CB_SCRIPT); + in.sequence = CB_SEQUENCE; + tx.vin.push_back(in); + + typename decltype(tx.vout)::value_type out; + out.value = CB_VALUE; + out.scriptPubKey = script_of(PK_SCRIPT); + tx.vout.push_back(out); + + return tx; +} + +// Fixed surrounding CMerkleTx fields. +const uint256 BLOCK_HASH = + uint256S("00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff"); +const uint256 MERKLE_BRANCH_0 = + uint256S("ffeeddccbbaa99887766554433221100ffeeddccbbaa99887766554433221100"); +constexpr uint32_t MERKLE_INDEX = 1; + +doge::coin::CMerkleTx build_merkle_tx() { + doge::coin::CMerkleTx mt; + mt.m_tx = build_parent_coinbase(); + mt.m_block_hash = BLOCK_HASH; + mt.m_merkle_link.m_branch = {MERKLE_BRANCH_0}; + mt.m_merkle_link.m_index = MERKLE_INDEX; + return mt; +} + +// --------------------------------------------------------------------------- +// GOLDENS — captured against master f732061bc (see header). DO NOT hand-edit; +// regenerate only with a documented module change and integrator sign-off. +// --------------------------------------------------------------------------- +// Witness-stripped parent coinbase (the m_tx member payload, == tx_id_type). +const std::string PARENT_COINBASE_NOSEG = + "01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0d03a1b2c3041122334455667788ffffffff0100f2052a010000001976a914333333333333333333333333333333333333333388ac00000000"; +// Full doge::coin::CMerkleTx wire image: +// TX_NO_WITNESS(m_tx) ++ m_block_hash(32, LE) ++ merkle_link(branch ++ index) +const std::string MERKLE_TX_WIRE = + "01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0d03a1b2c3041122334455667788ffffffff0100f2052a010000001976a914333333333333333333333333333333333333333388ac00000000ffeeddccbbaa99887766554433221100ffeeddccbbaa998877665544332211000100112233445566778899aabbccddeeff00112233445566778899aabbccddeeff01000000"; // index = 1 (uint32 LE) + +} // namespace + +// 1) The member payload: witness-stripped parent coinbase, ltc-typed (real type). +TEST(DGB_AuxParentCoinbaseParity, LtcTypedMemberPayloadGolden) { + auto tx = build_parent_coinbase(); + EXPECT_EQ(pack_hex(ltc::coin::TX_NO_WITNESS(tx)), PARENT_COINBASE_NOSEG); +} + +// 2) CROSS-TYPE INVARIANT: the dgb-typed (neutral stand-in) coinbase serializes +// BYTE-IDENTICALLY to the ltc-typed one. This is exactly what the extraction +// must preserve when m_tx's type is lifted to the Scrypt-family base. +TEST(DGB_AuxParentCoinbaseParity, LtcVsDgbTypeByteIdentical) { + auto ltc_tx = build_parent_coinbase(); + auto dgb_tx = build_parent_coinbase(); + const auto ltc_bytes = pack_hex(ltc::coin::TX_NO_WITNESS(ltc_tx)); + const auto dgb_bytes = pack_hex(dgb::coin::TX_NO_WITNESS(dgb_tx)); + EXPECT_EQ(ltc_bytes, dgb_bytes); + EXPECT_EQ(ltc_bytes, PARENT_COINBASE_NOSEG); +} + +// 3) Full shared-module CMerkleTx wire image, anchored to AUX_MODULE_MASTER_SHA. +// Pins the pre-templating layout the extraction must reproduce verbatim. +TEST(DGB_AuxParentCoinbaseParity, MerkleTxWireGoldenAnchored) { + ASSERT_STREQ(AUX_MODULE_MASTER_SHA, "f732061bc"); // golden provenance guard + auto mt = build_merkle_tx(); + EXPECT_EQ(pack_hex(mt), MERKLE_TX_WIRE); +} + +// 4) Round-trip: Unserialize(serialize(mt)) re-serializes to the same bytes. +// A field-order/width bug that happened to be self-consistent on write but +// not on read would diverge here. +TEST(DGB_AuxParentCoinbaseParity, MerkleTxRoundTripStable) { + auto mt = build_merkle_tx(); + auto packed = pack(mt); + doge::coin::CMerkleTx rt; + packed >> rt; + EXPECT_EQ(pack_hex(rt), MERKLE_TX_WIRE); +}