Skip to content

Commit 67e1cec

Browse files
authored
dash(g0/g1): real testnet3 DIP4 special-tx byte-parity KATs for hash_link VarStr payload (#419)
Extends the #412 VarStr(coinbase_payload) conformance (proven there with a 4-byte synthetic payload) with two REAL on-chain extraPayloads captured from dashd testnet3, covering both CompactSize length-prefix branches: A: blk 1502030 txid 7d292a63... type-5 CbTx, 175B -> single-byte 0xaf B: blk 1501948 txid b4d1a2a5... type-6 qcTx, 419B -> 0xfd a3 01 (u16-LE) Expected gentx digests are computed OUT-OF-BAND with CPython sha256d, so the vectors are non-circular with the C++ check_hash_link path; each vector also pins the buggy raw-append (no-prefix) digest and asserts it differs, keeping the test discriminating. hash_link is agnostic to special-tx type, so the qcTx is the honest vector for the multi-byte path (no fabricated coinbase). Per-coin G0/G1 byte-parity gate before the G2 crossing. Co-authored-by: frstrtr <frstrtr@users.noreply.github.com>
1 parent 7082ea2 commit 67e1cec

1 file changed

Lines changed: 67 additions & 0 deletions

File tree

test/test_dash_share_hash_link.cpp

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,23 @@ void put_le(std::vector<unsigned char>& v, uint64_t x, int n) {
7070
for (int i = 0; i < n; ++i) v.push_back(static_cast<unsigned char>((x >> (8 * i)) & 0xff));
7171
}
7272

73+
// Parse a hex string to bytes (test fixtures embed real on-chain payloads).
74+
std::vector<unsigned char> bytes_from_hex(const std::string& h) {
75+
std::vector<unsigned char> v; v.reserve(h.size() / 2);
76+
auto nib = [](char c) -> int { return c <= '9' ? c - '0' : (c | 0x20) - 'a' + 10; };
77+
for (size_t i = 0; i + 1 < h.size(); i += 2)
78+
v.push_back(static_cast<unsigned char>((nib(h[i]) << 4) | nib(h[i + 1])));
79+
return v;
80+
}
81+
82+
// Append Bitcoin CompactSize(n) == oracle pack.VarStrType length prefix.
83+
void append_compactsize(std::vector<unsigned char>& v, uint64_t n) {
84+
if (n < 0xfd) { v.push_back(static_cast<unsigned char>(n)); }
85+
else if (n <= 0xffff) { v.push_back(0xfd); put_le(v, n, 2); }
86+
else if (n <= 0xffffffffULL) { v.push_back(0xfe); put_le(v, n, 4); }
87+
else { v.push_back(0xff); put_le(v, n, 8); }
88+
}
89+
7390
const std::vector<unsigned char> kPayload = {0xde, 0xad, 0xbe, 0xef};
7491

7592
} // namespace
@@ -101,6 +118,56 @@ TEST(DashShareHashLink, OracleCheckHashLinkVarStrPayload) {
101118
EXPECT_NE(hex_of(g), hex_of(gr)) << "compactsize prefix must change gentx_hash";
102119
}
103120

121+
// (1b) Non-circular oracle anchor over REAL testnet3 DIP4 special-tx payloads.
122+
// #412 proved the VarStr(coinbase_payload) assembly with a 4-byte synthetic
123+
// payload; G0/G1 (per-coin byte-parity gate before the G2 crossing) pins the
124+
// SAME assembly against ACTUAL on-chain extraPayloads captured from dashd
125+
// (testnet3), covering BOTH compactsize branches: single-byte (<0xfd) and
126+
// 0xfd+u16-LE. Expected digests are computed OUT-OF-BAND with CPython
127+
// (sha256d), so they are non-circular with the C++ check_hash_link path.
128+
// A: blk 1502030 txid 7d292a63... type-5 CbTx, 175B (compactsize 0xaf)
129+
// B: blk 1501948 txid b4d1a2a5... type-6 qcTx, 419B (compactsize 0xfd a3 01)
130+
TEST(DashShareHashLink, RealTestnet3SpecialTxVarStrParity) {
131+
auto hl = iv_hash_link();
132+
const auto ce = dash::compute_gentx_before_refhash(); // unused at length%64==0
133+
134+
struct CbTxKat { const char* name; const char* payload_hex;
135+
const char* varstr; const char* raw_append; };
136+
const CbTxKat kats[] = {
137+
{"testnet3-1502030-cbtx-type5",
138+
"03004eeb1600286ca33b2ec502759646df1259df9ee5b599722122f5bb4ba6a01062782e489ff09af4994ea6306dbe1aa636373c0d9c0fabc6c61d4402754b9878f63f4c277e008b96e1d2f74c1717728f47d319e151460adc51ffd76686c10ede2e3a31d5faaeb724a65736167a7d8f17b1f880585a0c1164f6882d5e5d6c8a5ff3b75d47ee6359d0662964109e7fa17658d04843e4c6eca499a55ed01bd4efd49b10848aa04a5a36b0fcdb1d0000",
139+
"6e985201f8a1d0cf46c97a1379c140c9564ee56c1649af5cbce8b7d9cc986665",
140+
"69ebfd8ee54d039ffece6bb1c8cab97ec3c6b24715e4ae57c30428a9dbc4ac1a"},
141+
{"testnet3-1501948-qctx-type6",
142+
"0100fcea160003000270282f59392f2433c05a9f4c948c9f31dab53cf42e112ca68b9854fdc5000000fd90010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000fd900100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
143+
"e60ea7ea8c11c17af6a7573b0399c87e4672175986cf54d407875bf33c05c84f",
144+
"4fa8a56bb753593362540b9d8006c2b80a191d2d194eba2714162a9cd41bbe56"},
145+
};
146+
for (const auto& k : kats) {
147+
const auto payload = bytes_from_hex(k.payload_hex);
148+
149+
// VarStr(payload) = CompactSize(len) || payload (oracle data.py:278)
150+
std::vector<unsigned char> data;
151+
for (int i = 0; i < 32; ++i) data.push_back(static_cast<unsigned char>(i));
152+
put_le(data, 0, 8); // last_txout_nonce
153+
put_le(data, 0, 4); // IntType(32).pack(0)
154+
append_compactsize(data, payload.size());
155+
data.insert(data.end(), payload.begin(), payload.end());
156+
uint256 g = dash::check_hash_link(hl, data, ce);
157+
EXPECT_EQ(hex_of(g), k.varstr) << k.name;
158+
159+
// Buggy raw-append (no compactsize) -> separately-pinned, must differ.
160+
std::vector<unsigned char> raw;
161+
for (int i = 0; i < 32; ++i) raw.push_back(static_cast<unsigned char>(i));
162+
put_le(raw, 0, 8);
163+
put_le(raw, 0, 4);
164+
raw.insert(raw.end(), payload.begin(), payload.end());
165+
uint256 gr = dash::check_hash_link(hl, raw, ce);
166+
EXPECT_EQ(hex_of(gr), k.raw_append) << k.name;
167+
EXPECT_NE(hex_of(g), hex_of(gr)) << k.name << ": compactsize prefix must matter";
168+
}
169+
}
170+
104171
// (2) End-to-end through the production share_init_verify().
105172
TEST(DashShareHashLink, ShareInitVerifyAppendsVarStrPayload) {
106173
const core::CoinParams params = dash::make_coin_params(/*testnet=*/false);

0 commit comments

Comments
 (0)