Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
186 changes: 186 additions & 0 deletions test/test_doge_chain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -474,6 +474,192 @@ TEST(AuxPowStructuredTest, BatchStopsAtTruncatedHeader) {
EXPECT_EQ(result.size(), 1u) << "Truncated tail header must be dropped, first kept";
}

// ─── Known-Answer Vector: real Dogecoin mainnet AuxPoW block #371337 ─────────
//
// Provenance: Dogecoin block 371,337 — the FIRST AuxPoW block on mainnet
// (mainnet AuxPoW activation height). DOGE_371337_EXTHEADER below is that
// block's extended header exactly as it travels on the P2P 'headers' wire:
// CPureBlockHeader(80) + CAuxPow(proof) + tx_count(CompactSize = 0)
//
// It is assembled from Dogecoin Core's OWN decode of the on-chain block, NOT
// from c2pool's Serialize — so unlike the round-trip tests above it can catch
// a field-order or witness-mode error in OUR parser. Every component is
// double-SHA256 hash-verified against an externally published value, so the
// bytes cannot be silently wrong:
// * sha256d(first 80 bytes) == canonical block hash
// 60323982f9c5ff1b5a954eac9dc1269352835f47c2c5222691d80f0d50dcf053
// * sha256d(parent coinbase) == its known txid
// e5422732b20e9e7ecc243427abbe296e9528d308bb111aae8d30c3465e442de8
// * sha256d(parent 80B hdr) == merkle_tx.block_hash
// 45df41e40aba5b2a03d08bd1202a1c02ef3954d8aa22ea6c5ae62fd00f290ea9
// All asserted field values are read off the block explorer, independent of
// the code under test.
namespace {
std::vector<uint8_t> hex_to_bytes(const std::string& h) {
auto nib = [](char c) -> int {
if (c >= '0' && c <= '9') return c - '0';
if (c >= 'a' && c <= 'f') return c - 'a' + 10;
if (c >= 'A' && c <= 'F') return c - 'A' + 10;
return -1;
};
std::vector<uint8_t> out;
out.reserve(h.size() / 2);
for (size_t i = 0; i + 1 < h.size(); i += 2)
out.push_back(static_cast<uint8_t>((nib(h[i]) << 4) | nib(h[i + 1])));
return out;
}

// Dogecoin mainnet block #371337 extended header (headers-message form,
// tx_count = 0). See provenance note above. 536 bytes.
const char* const DOGE_371337_EXTHEADER =
"020162000d6f03470d329026cd1fc720c0609cd378ca8691a117bd1aa46f01fb"
"09b1a8468a15bf6f0b0e83f2e5036684169eafb9406468d4f075c999fb5b2a78"
"fbb827ee41fb11548441361b0000000001000000010000000000000000000000"
"000000000000000000000000000000000000000000ffffffff380345bf09fabe"
"6d6d980ba42120410de0554d42a5b5ee58167bcd86bf7591f429005f24da45fb"
"51cf0800000000000000cdb1f1ff0e000000ffffffff01800c0c2a0100000019"
"76a914aa3750aa18b8a0f3f0590731e1fab934856680cf88ac00000000a90e29"
"0fd02fe65a6cea22aad85439ef021c2a20d18bd0032a5bba0ae441df4503a979"
"a636db2450363972d211aee67b71387a3daaa3051be0fd260c5acd4739cd52a4"
"18d29d8a0e56c8714c95a0dc24e1c9624480ec497fe2441941f3fee8f9481a33"
"70c334178415c83d1d0c2deeec727c2330617a47691fc5e79203669312d10000"
"0000036fa40307b3a439538195245b0de56a2c1db6ba3a64f8bdd2071d00bc48"
"c841b5e77b98e5c7d6f06f92dec5cf6d61277ecb9a0342406f49f34c51ee8ce4"
"abd678038129485de14238bd1ca12cd2de12ff0e383aee542d90437cd664ce13"
"9446a00000000002000000d2ec7dfeb7e8f43fe77aba3368df95ac2088034420"
"402730ee0492a2084217083411b3fc91033bfdeea339bc11b9efc986e161c703"
"e07a9045338c165673f09940fb11548b54021b58cc9ae500";
} // namespace

TEST(AuxPowKnownAnswerTest, Mainnet371337SkipParserRecoversChildHeader) {
auto blob = hex_to_bytes(DOGE_371337_EXTHEADER);
ASSERT_EQ(blob.size(), 536u);

const uint8_t* pos = blob.data();
const uint8_t* end = blob.data() + blob.size();
auto hdr = doge::coin::parse_doge_header(pos, end);

EXPECT_EQ(hdr.m_version, 0x00620102u);
EXPECT_TRUE(doge::coin::is_auxpow_version(static_cast<int32_t>(hdr.m_version)));
EXPECT_EQ(hdr.m_timestamp, 1410464577u);
EXPECT_EQ(hdr.m_bits, 0x1b364184u);
EXPECT_EQ(hdr.m_nonce, 0u);
uint256 prev; prev.SetHex("46a8b109fb016fa41abd17a19186ca78d39c60c020c71fcd2690320d47036f0d");
uint256 root; root.SetHex("ee27b8fb782a5bfb99c975f0d4686440b9af9e16846603e5f2830e0b6fbf158a");
EXPECT_EQ(hdr.m_previous_block, prev);
EXPECT_EQ(hdr.m_merkle_root, root);
EXPECT_EQ(pos, end) << "skip parser must consume header + auxpow + tx_count exactly";
}

TEST(AuxPowKnownAnswerTest, Mainnet371337StructuredDecodesRealProof) {
auto blob = hex_to_bytes(DOGE_371337_EXTHEADER);
PackStream ps(std::span<const std::byte>(
reinterpret_cast<const std::byte*>(blob.data()), blob.size()));
doge::coin::CAuxPow out;
bool has_aux = false;
auto hdr = doge::coin::parse_aux_header(ps, out, has_aux);

ASSERT_TRUE(has_aux);
EXPECT_EQ(hdr.m_version, 0x00620102u);

// Parent coinbase tx (witness-stripped tx_id_type), decoded field-for-field.
ASSERT_EQ(out.m_merkle_tx.m_tx.vin.size(), 1u);
EXPECT_TRUE(out.m_merkle_tx.m_tx.vin[0].prevout.hash.IsNull());
EXPECT_EQ(out.m_merkle_tx.m_tx.vin[0].prevout.index, 0xffffffffu);
EXPECT_EQ(out.m_merkle_tx.m_tx.vin[0].sequence, 0xffffffffu);
EXPECT_EQ(out.m_merkle_tx.m_tx.vin[0].scriptSig.m_data,
hex_to_bytes("0345bf09fabe6d6d980ba42120410de0554d42a5b5ee58167bcd86bf7591f429005f24da45fb51cf0800000000000000cdb1f1ff0e000000"));
ASSERT_EQ(out.m_merkle_tx.m_tx.vout.size(), 1u);
EXPECT_EQ(out.m_merkle_tx.m_tx.vout[0].value, 5000400000LL);
EXPECT_EQ(out.m_merkle_tx.m_tx.vout[0].scriptPubKey.m_data,
hex_to_bytes("76a914aa3750aa18b8a0f3f0590731e1fab934856680cf88ac"));

// CMerkleTx.block_hash == the parent block the coinbase was included in.
uint256 pblk; pblk.SetHex("45df41e40aba5b2a03d08bd1202a1c02ef3954d8aa22ea6c5ae62fd00f290ea9");
EXPECT_EQ(out.m_merkle_tx.m_block_hash, pblk);

// Coinbase merkle branch: 3 levels, leaf index 0.
ASSERT_EQ(out.m_merkle_tx.m_merkle_link.m_branch.size(), 3u);
uint256 mb0; mb0.SetHex("cd3947cd5a0c26fde01b05a3aa3d7a38717be6ae11d27239365024db36a679a9");
EXPECT_EQ(out.m_merkle_tx.m_merkle_link.m_branch[0], mb0);
EXPECT_EQ(out.m_merkle_tx.m_merkle_link.m_index, 0u);

// Chain (aux) merkle branch: 3 levels, chain slot index 0.
ASSERT_EQ(out.m_chain_merkle_link.m_branch.size(), 3u);
uint256 cmb0; cmb0.SetHex("b541c848bc001d07d2bdf8643abab61d2c6ae50d5b2495815339a4b30703a46f");
EXPECT_EQ(out.m_chain_merkle_link.m_branch[0], cmb0);
EXPECT_EQ(out.m_chain_merkle_link.m_index, 0u);

// Parent (LTC) 80-byte block header.
EXPECT_EQ(out.m_parent_block_header.m_version, 2u);
EXPECT_EQ(out.m_parent_block_header.m_timestamp, 1410464576u);
EXPECT_EQ(out.m_parent_block_header.m_bits, 0x1b02548bu);
EXPECT_EQ(out.m_parent_block_header.m_nonce, 3852127320u);

// The whole proof is consumed; only the tx_count CompactSize byte remains.
EXPECT_EQ(ps.cursor_size(), 1u) << "structured parse must stop exactly at tx_count";
}

// ─── Known-Answer Vector: self-hosted regtest/testnet4alpha AuxPoW (h=20) ──
//
// Provenance: a real AuxPoW block mined at height 20 on a self-hosted Dogecoin
// regtest/testnet4alpha seed (VM 215) and ACCEPTED by dogecoind — the
// operator-ratified fixture path (public testnet4 was rejected as contaminated
// with ultrafast blocks). DOGE_REGTEST_H20_CAUXPOW is the 217-byte CAuxPow
// proof exactly as dogecoind's OWN serializer emitted it: parent coinbase
// (witness-stripped) + CMerkleTx tail + chain merkle link + parent 80B header.
// It is a bare CAuxPow — NOT an extended header (no 80B child prefix, no
// tx_count) — so it is fed straight to the c2pool CAuxPow Unserialize path,
// the same one parse_aux_header() drives. Independent serializer => a real
// cross-implementation check, not a c2pool self-round-trip.
namespace {
const char* const DOGE_REGTEST_H20_CAUXPOW =
"01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff2cfabe6d6df091be03e588d8f33a0bc14145cb034dbf68e0a8ca774f0f842027a1366275900100000000000000ffffffff0000000000106303778e4f91a2984bb151de0bf0126c6cde013e9e11f86f7a942818f635f00000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000013175e533f07d6a5e7ab3df1fb0e67283b2b52daf0aaba1efbb2a22c77e96695000000000000000000000001";
} // namespace

TEST(AuxPowKnownAnswerTest, RegtestH20CAuxPowDecodesRealProof) {
auto blob = hex_to_bytes(DOGE_REGTEST_H20_CAUXPOW);
ASSERT_EQ(blob.size(), 217u);

PackStream ps(std::span<const std::byte>(
reinterpret_cast<const std::byte*>(blob.data()), blob.size()));
doge::coin::CAuxPow out;
::Unserialize(ps, out); // c2pool CAuxPow parser, on dogecoind's own bytes

// Parent coinbase: single null-prevout input carrying the merged-mining
// (fabe6d6d) commitment in its scriptSig; no outputs; locktime 0.
ASSERT_EQ(out.m_merkle_tx.m_tx.vin.size(), 1u);
EXPECT_TRUE(out.m_merkle_tx.m_tx.vin[0].prevout.hash.IsNull());
EXPECT_EQ(out.m_merkle_tx.m_tx.vin[0].prevout.index, 0xffffffffu);
EXPECT_EQ(out.m_merkle_tx.m_tx.vin[0].sequence, 0xffffffffu);
EXPECT_EQ(out.m_merkle_tx.m_tx.vin[0].scriptSig.m_data,
hex_to_bytes("fabe6d6df091be03e588d8f33a0bc14145cb034dbf68e0a8ca774f0f842027a1366275900100000000000000"));
EXPECT_EQ(out.m_merkle_tx.m_tx.vout.size(), 0u);
EXPECT_EQ(out.m_merkle_tx.m_tx.locktime, 0u);

// CMerkleTx tail: parent block hash; empty coinbase merkle branch (leaf 0).
uint256 pblk; pblk.SetHex("f035f61828947a6ff8119e3e01de6c6c12f00bde51b14b98a2914f8e77036310");
EXPECT_EQ(out.m_merkle_tx.m_block_hash, pblk);
EXPECT_EQ(out.m_merkle_tx.m_merkle_link.m_branch.size(), 0u);
EXPECT_EQ(out.m_merkle_tx.m_merkle_link.m_index, 0u);

// Chain (aux) merkle link: single aux chain => empty branch, slot 0.
EXPECT_EQ(out.m_chain_merkle_link.m_branch.size(), 0u);
EXPECT_EQ(out.m_chain_merkle_link.m_index, 0u);

// Parent (regtest) 80-byte header.
EXPECT_EQ(out.m_parent_block_header.m_version, 1u);
EXPECT_TRUE(out.m_parent_block_header.m_previous_block.IsNull());
uint256 proot; proot.SetHex("9566e9772ca2b2fb1ebaaaf0da522b3b28670efbf13dabe7a5d6073f535e1713");
EXPECT_EQ(out.m_parent_block_header.m_merkle_root, proot);
EXPECT_EQ(out.m_parent_block_header.m_timestamp, 0u);
EXPECT_EQ(out.m_parent_block_header.m_bits, 0u);
EXPECT_EQ(out.m_parent_block_header.m_nonce, 0x01000000u); // wire 00 00 00 01 LE

// The 217-byte CAuxPow is consumed exactly — no trailing bytes, no overrun.
EXPECT_EQ(ps.cursor_size(), 0u) << "CAuxPow parser must consume exactly 217 bytes";
}

// ─── Mersenne Twister Uniform Int Tests ─────────────────────────────────────

TEST(MersenneTwisterTest, BoostCompatibleOutput) {
Expand Down
Loading