Skip to content

Commit d4d45ac

Browse files
authored
Merge pull request #775 from frstrtr/dash/v36-share-type-phaseA
dash: v36 share type (Phase A) — define + parse, dormant, byte-standardized
2 parents 9ac8b3d + 2acbcc6 commit d4d45ac

5 files changed

Lines changed: 753 additions & 1 deletion

File tree

src/impl/dash/share.hpp

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,4 +70,95 @@ struct DashShare : chain::BaseShare<uint256, 16>
7070
NetService peer_addr;
7171
};
7272

73+
// ═══════════════════════════════════════════════════════════════════════════
74+
// DASH v36 share (wire-type 36) — PHASE A: DEFINED + PARSEABLE, DORMANT.
75+
//
76+
// Byte-standardized to the cross-coin v36 share shape (the v36-standardize-for-
77+
// v37 goal: uniform structs). The STANDARDIZED PREFIX (min_header .. message_data)
78+
// is byte-for-byte the NON-SEGWIT v36 layout — i.e. the BCH v36 MergedMiningShare
79+
// shape (src/impl/bch/share.hpp), which is the LTC/DGB v36 layout MINUS the
80+
// Optional(segwit_data) slot. BCH is the reference here (not LTC/DGB directly)
81+
// because DASH, like BCH, is non-segwit: BCH's SEGWIT_ACTIVATION_VERSION==0 makes
82+
// is_segwit_activated(36) false, so a non-segwit v36 coin OMITS segwit_data. This
83+
// is the ESTABLISHED cross-coin convention, NOT a DASH divergence.
84+
//
85+
// Field categories (operator 3-bucket / v36-standardize model):
86+
// * SHARED / STANDARDIZED (Bucket-2, byte-identical cross-coin): min_header,
87+
// prev_hash, coinbase, nonce, pubkey_hash + pubkey_type, subsidy(VarInt),
88+
// donation, stale_info, desired_version(VarInt) [THE version-vote], the merged
89+
// fields (merged_addresses / merged_coinbase_info / merged_payout_hash — held
90+
// EMPTY/INERT since DASH is standalone X11 with no AuxPoW child, exactly as a
91+
// non-merged ltc/dgb/bch coin populates them), far_share_hash, max_bits, bits,
92+
// timestamp, absheight, abswork(AbsworkV36Format), ref_merkle_link,
93+
// last_txout_nonce, hash_link(V36HashLinkType), merkle_link, message_data.
94+
// * DASH-SPECIFIC SUFFIX (Bucket-3 / coin-specific, the FLAGGED divergence):
95+
// coinbase_payload, payment_amount, packed_payments, coinbase_payload_outer.
96+
// DASH masternode / DIP4-CbTx consensus data with NO cross-coin equivalent;
97+
// required so Phase C can reconstruct+verify a real DASH coinbase from the
98+
// share (a non-merged coin has no analogue). Appended AFTER the standardized
99+
// message_data so the ENTIRE standardized prefix stays byte-identical to
100+
// bch/ltc/dgb. See the design note + PR body for the review decision.
101+
//
102+
// The version-vote (m_desired_version) is what #774's AutoRatchet reads to
103+
// activate; Phase A only DEFINES this type (dormant, unminted). current_share_version
104+
// stays 16, so nothing mints a v36 share and the live 1700 accept path is
105+
// byte-unchanged (Phase C flips activation via the ratchet, NOT here).
106+
struct DashV36Share : chain::BaseShare<uint256, 36>
107+
{
108+
// ── min_header (SmallBlockHeaderType — coin block header, standardized) ──
109+
bitcoin_family::coin::SmallBlockHeaderType m_min_header;
110+
111+
// ── share_data (STANDARDIZED v36) ──
112+
// NOTE: m_prev_hash is inherited from chain::BaseShare<uint256,36>.
113+
BaseScript m_coinbase; // coinbase scriptSig (VarStr)
114+
uint32_t m_nonce{0}; // share nonce
115+
uint160 m_pubkey_hash; // v36 address (IntType(160))
116+
uint8_t m_pubkey_type{0}; // v36: 0=P2PKH, 1=P2WPKH, 2=P2SH (DASH always 0/P2PKH)
117+
uint64_t m_subsidy{0}; // v36: VarInt-encoded
118+
uint16_t m_donation{0}; // donation (bps)
119+
StaleInfo m_stale_info{StaleInfo::none};
120+
uint64_t m_desired_version{36}; // THE version-vote (VarInt)
121+
122+
// v36 merged-mining address entries — EMPTY/INERT on DASH (no AuxPoW child).
123+
std::vector<v36::MergedAddressEntry> m_merged_addresses;
124+
125+
// ── share_info (non-share_data, STANDARDIZED v36) ──
126+
uint256 m_far_share_hash;
127+
uint32_t m_max_bits{0};
128+
uint32_t m_bits{0};
129+
uint32_t m_timestamp{0};
130+
uint32_t m_absheight{0};
131+
uint128 m_abswork; // v36: AbsworkV36Format (VarInt low64) on the wire
132+
133+
// v36 merged-mining coinbase verification — EMPTY/INERT on DASH.
134+
std::vector<v36::MergedCoinbaseEntry> m_merged_coinbase_info;
135+
uint256 m_merged_payout_hash; // zero = none (INERT on DASH)
136+
137+
// ── remaining STANDARDIZED v36 share fields ──
138+
v36::MerkleLink m_ref_merkle_link;
139+
uint64_t m_last_txout_nonce{0};
140+
v36::V36HashLinkType m_hash_link; // v36: state + extra_data(VarStr) + length(VarInt)
141+
v36::MerkleLink m_merkle_link;
142+
BaseScript m_message_data; // v36 messaging hook — EMPTY on DASH in Phase A (Phase B)
143+
144+
// ── DASH-SPECIFIC SUFFIX (FLAGGED divergence — see struct header) ──
145+
BaseScript m_coinbase_payload; // DIP3/DIP4 CBTX inner (PossiblyNone '', VarStr)
146+
uint64_t m_payment_amount{0}; // total masternode payment amount
147+
std::vector<PackedPayment> m_packed_payments; // masternode/superblock/platform
148+
BaseScript m_coinbase_payload_outer; // outer coinbase_payload_data (appended to hash_link)
149+
150+
// ── carried-but-UNSERIALIZED members (v36 wire omits tx_info; kept so a
151+
// future promotion into the live variant matches DashShare's field
152+
// surface for the shared generic-invoke call sites in node.cpp) ──
153+
std::vector<uint256> m_new_transaction_hashes;
154+
std::vector<uint64_t> m_transaction_hash_refs;
155+
156+
// Ingestion metadata — not on the wire (parity with DashShare::peer_addr).
157+
NetService peer_addr;
158+
159+
DashV36Share() {}
160+
DashV36Share(const uint256& hash, const uint256& prev_hash)
161+
: chain::BaseShare<uint256, 36>(hash, prev_hash) {}
162+
};
163+
73164
} // namespace dash

src/impl/dash/share_chain.hpp

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include <sharechain/share.hpp>
1212
#include <sharechain/sharechain.hpp>
1313
#include <core/target_utils.hpp>
14+
#include <core/version_gate.hpp> // SSOT: core::version_gate::is_v36_active
1415

1516
#include <chrono>
1617

@@ -57,6 +58,17 @@ struct DashFormatter
5758
template <typename StreamType, typename ShareT>
5859
static void Read(StreamType& is, ShareT* share)
5960
{
61+
// V36 shares (wire-type 36, dash::DashV36Share) use the standardized
62+
// cross-coin v36 layout. Compile-time dispatch: the v16 body below is
63+
// instantiated ONLY for DashShare (version 16), so it stays byte-
64+
// unchanged; the v36 body is instantiated ONLY for DashV36Share.
65+
if constexpr (core::version_gate::is_v36_active(ShareT::version))
66+
{
67+
ReadV36(is, share);
68+
return;
69+
}
70+
else
71+
{
6072
// ── min_header ──
6173
is >> share->m_min_header;
6274

@@ -136,11 +148,19 @@ struct DashFormatter
136148

137149
// ── coinbase_payload (outer) ──
138150
is >> share->m_coinbase_payload_outer; // PossiblyNone('', VarStr)
151+
} // else (v16)
139152
}
140153

141154
template <typename StreamType, typename ShareT>
142155
static void Write(StreamType& os, const ShareT* share)
143156
{
157+
if constexpr (core::version_gate::is_v36_active(ShareT::version))
158+
{
159+
WriteV36(os, share);
160+
return;
161+
}
162+
else
163+
{
144164
os << share->m_min_header;
145165
os << share->m_prev_hash;
146166
os << share->m_coinbase;
@@ -180,6 +200,124 @@ struct DashFormatter
180200
os << share->m_hash_link;
181201
os << share->m_merkle_link.m_branch;
182202
os << share->m_coinbase_payload_outer;
203+
} // else (v16)
204+
}
205+
206+
// ═══════════════════════════════════════════════════════════════════════
207+
// V36 (wire-type 36) read/write — STANDARDIZED cross-coin non-segwit v36
208+
// layout (the bch/ltc/dgb MergedMiningShare shape MINUS segwit_data) plus
209+
// the DASH-specific suffix. Field ORDER and ENCODINGS mirror the BCH v36
210+
// Formatter (src/impl/bch/share.hpp) byte-for-byte for the standardized
211+
// prefix; the suffix (coinbase_payload / payment_amount / packed_payments /
212+
// coinbase_payload_outer) rides AFTER message_data. Pinned by the byte-
213+
// parity + round-trip KATs in test/test_dash_v36_share.cpp.
214+
// ═══════════════════════════════════════════════════════════════════════
215+
template <typename StreamType, typename ShareT>
216+
static void ReadV36(StreamType& is, ShareT* share)
217+
{
218+
// ── min_header ──
219+
is >> share->m_min_header;
220+
221+
// ── share_data (standardized v36) ──
222+
is >> share->m_prev_hash; // PossiblyNone(0, IntType(256))
223+
is >> share->m_coinbase; // VarStr
224+
is >> share->m_nonce; // uint32
225+
is >> share->m_pubkey_hash; // v36 address: IntType(160)
226+
is >> share->m_pubkey_type; // v36: IntType(8)
227+
{ uint64_t sub; ::Unserialize(is, VarInt(sub)); share->m_subsidy = sub; } // v36: VarInt
228+
is >> share->m_donation; // uint16
229+
{ uint8_t si; is >> si; share->m_stale_info = static_cast<dash::StaleInfo>(si); }
230+
{ uint64_t dv; ::Unserialize(is, VarInt(dv)); share->m_desired_version = dv; } // version-vote
231+
232+
// NO segwit_data (non-segwit coin — BCH convention).
233+
234+
// v36 merged_addresses (empty/inert on DASH).
235+
is >> share->m_merged_addresses;
236+
237+
// NO tx_info (only serialized for version < 34).
238+
239+
is >> share->m_far_share_hash; // PossiblyNone(0, IntType(256))
240+
is >> share->m_max_bits; // uint32
241+
is >> share->m_bits; // uint32
242+
is >> share->m_timestamp; // uint32
243+
is >> share->m_absheight; // uint32
244+
::Unserialize(is, Using<v36::AbsworkV36Format>(share->m_abswork)); // v36: VarInt low64
245+
246+
// v36 merged_coinbase_info + merged_payout_hash (empty/zero-inert on DASH).
247+
is >> share->m_merged_coinbase_info;
248+
is >> share->m_merged_payout_hash;
249+
250+
// ref_merkle_link (MERKLE_LINK_SMALL — index omitted).
251+
{ ParamPackStream ps{v36::MERKLE_LINK_SMALL, is}; ::Unserialize(ps, share->m_ref_merkle_link); }
252+
253+
is >> share->m_last_txout_nonce; // uint64
254+
is >> share->m_hash_link; // V36HashLinkType
255+
256+
{ ParamPackStream ps{v36::MERKLE_LINK_SMALL, is}; ::Unserialize(ps, share->m_merkle_link); }
257+
258+
is >> share->m_message_data; // v36 messaging hook (empty on DASH Phase A)
259+
260+
// ── DASH-specific suffix (FLAGGED divergence) ──
261+
is >> share->m_coinbase_payload; // PossiblyNone('', VarStr)
262+
is >> share->m_payment_amount; // uint64
263+
{
264+
uint64_t count;
265+
::Unserialize(is, VarInt(count));
266+
if (count > MAX_PAYMENTS_PER_SHARE)
267+
throw std::ios_base::failure("packed_payments count exceeds cap");
268+
share->m_packed_payments.resize(count);
269+
for (uint64_t i = 0; i < count; ++i) {
270+
BaseScript payee_bs;
271+
is >> payee_bs;
272+
share->m_packed_payments[i].m_payee.assign(
273+
payee_bs.m_data.begin(), payee_bs.m_data.end());
274+
is >> share->m_packed_payments[i].m_amount;
275+
}
276+
}
277+
is >> share->m_coinbase_payload_outer; // PossiblyNone('', VarStr)
278+
}
279+
280+
template <typename StreamType, typename ShareT>
281+
static void WriteV36(StreamType& os, const ShareT* share)
282+
{
283+
os << share->m_min_header;
284+
os << share->m_prev_hash;
285+
os << share->m_coinbase;
286+
os << share->m_nonce;
287+
os << share->m_pubkey_hash;
288+
os << share->m_pubkey_type;
289+
::Serialize(os, VarInt(share->m_subsidy));
290+
os << share->m_donation;
291+
{ uint8_t si = static_cast<uint8_t>(share->m_stale_info); os << si; }
292+
::Serialize(os, VarInt(share->m_desired_version));
293+
os << share->m_merged_addresses;
294+
os << share->m_far_share_hash;
295+
os << share->m_max_bits;
296+
os << share->m_bits;
297+
os << share->m_timestamp;
298+
os << share->m_absheight;
299+
::Serialize(os, Using<v36::AbsworkV36Format>(share->m_abswork));
300+
os << share->m_merged_coinbase_info;
301+
os << share->m_merged_payout_hash;
302+
{ ParamPackStream ps{v36::MERKLE_LINK_SMALL, os}; ::Serialize(ps, share->m_ref_merkle_link); }
303+
os << share->m_last_txout_nonce;
304+
os << share->m_hash_link;
305+
{ ParamPackStream ps{v36::MERKLE_LINK_SMALL, os}; ::Serialize(ps, share->m_merkle_link); }
306+
os << share->m_message_data;
307+
// ── DASH-specific suffix (FLAGGED divergence) ──
308+
os << share->m_coinbase_payload;
309+
os << share->m_payment_amount;
310+
{
311+
uint64_t count = share->m_packed_payments.size();
312+
::Serialize(os, VarInt(count));
313+
for (auto& pay : share->m_packed_payments) {
314+
BaseScript bs;
315+
bs.m_data.assign(pay.m_payee.begin(), pay.m_payee.end());
316+
os << bs;
317+
os << pay.m_amount;
318+
}
319+
}
320+
os << share->m_coinbase_payload_outer;
183321
}
184322
};
185323

@@ -188,6 +326,22 @@ struct DashFormatter
188326

189327
using ShareType = chain::ShareVariants<DashFormatter, DashShare>;
190328

329+
// ── PHASE A: v36 share-type dispatch (DORMANT) ───────────────────────────────
330+
// The live `ShareType` above is DELIBERATELY left as {DashShare} only, so the
331+
// live accept / mint / store / send paths (node.cpp) stay byte-unchanged and
332+
// keep compiling — several consume the share via generic-invoke lambdas that
333+
// call DashShare-concrete helpers (e.g. share_init_verify(const DashShare&) at
334+
// node.cpp:64). Promoting DashV36Share into the live variant requires guarding
335+
// those call sites with `if constexpr (std::is_same_v<share_t, DashShare>)`, and
336+
// is the FIRST wiring step of Phase B/C — NOT Phase A.
337+
//
338+
// This SEPARATE variant proves the v36 wire type round-trips through the REAL
339+
// chain::ShareVariants dispatch machinery (load map keyed by version 36 ->
340+
// DashV36Share, DashFormatter::ReadV36/WriteV36). It is what the round-trip KAT
341+
// exercises. Nothing mints a v36 share (current_share_version stays 16), so the
342+
// type is parseable/mintable-in-principle yet dormant until Phase-C activation.
343+
using V36ShareType = chain::ShareVariants<DashFormatter, DashV36Share>;
344+
191345
// ── Load share from wire format ──────────────────────────────────────────────
192346

193347
inline ShareType load_share(chain::RawShare& rshare, NetService peer_addr)
@@ -200,6 +354,16 @@ inline ShareType load_share(chain::RawShare& rshare, NetService peer_addr)
200354
return share;
201355
}
202356

357+
// PHASE A v36 load helper (dispatch-by-version-36). Mirrors load_share but over
358+
// the dormant V36ShareType variant; used by the round-trip KAT.
359+
inline V36ShareType load_v36_share(chain::RawShare& rshare, NetService peer_addr)
360+
{
361+
auto stream = rshare.contents.as_stream();
362+
auto share = V36ShareType::load(rshare.type, stream);
363+
share.ACTION({ obj->peer_addr = peer_addr; });
364+
return share;
365+
}
366+
203367
// ── ShareHasher ──────────────────────────────────────────────────────────────
204368

205369
struct ShareHasher

0 commit comments

Comments
 (0)