Skip to content

Commit 9eb9538

Browse files
committed
dgb: embed-at-mint MM commitment into coinbase scriptSig (phase DB)
Wire the AuxPoW merged-mining commitment into the per-connection coinbase assembler so a won DGB block, built as the DOGE merged-mining parent (-DAUX_DOGE=ON), carries the 44-byte MM tag (magic fabe6d6d || aux_root32 BE || size4 LE || nonce4 LE) the aux verifier decodes. ConnCoinbasePplnsInputs gains an optional aux_mm_commitment carrying the pre-built tag; build_connection_coinbase_from_pplns appends it to the coinbase scriptSig when present. DEFAULT nullopt leaves the scriptSig -- and therefore the whole gentx -- BYTE-IDENTICAL to the standalone-parent build: the no-op is structural, so the default V36 DGB build cannot regress. The producer (main_dgb seam) only ever populates the field under #ifdef AUX_DOGE; sourcing a LIVE aux merkle root is the gated DC routing slice and is not wired here. Per the integrator core=SSOT adjudication, the commitment blob is consumed as a read-only product of the canonical builder c2pool::merged::build_auxpow_commitment (shared A-level merged_mining.cpp / coinbase_builder.hpp). This slice carries no DGB-local layout logic and makes no edit to the 44-byte layout or the shared DOGE-aux seam; it is independent of #475 (the SSOT KAT-pin) -- both pin to the same core builder. Fenced to src/impl/dgb/; zero ltc/doge tree edits. KAT (connection_coinbase_test, runs in both default and AUX_DOGE arms): E1 NulloptIsByteIdentical -- nullopt == no-aux gentx, txid-identical E2 AppendsMmTagToScriptSig -- gentx grows by EXACTLY 44 bytes, tag bytes match the core SSOT output, scriptSig == baseline || tag 10/10 green both arms (default + -DAUX_DOGE=ON).
1 parent d4b57d8 commit 9eb9538

2 files changed

Lines changed: 88 additions & 0 deletions

File tree

src/impl/dgb/coin/connection_coinbase.hpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,17 @@ struct ConnCoinbasePplnsInputs
133133
std::vector<unsigned char> donation_script;
134134
uint256 ref_hash; // p2pool ref_hash (32B)
135135
uint64_t last_txout_nonce{0}; // OP_RETURN nonce (extranonce slot)
136+
137+
// DGB-as-DOGE-parent merged-mining commitment (-DAUX_DOGE=ON path only).
138+
// The pre-built 44-byte AuxPoW MM tag (magic fabe6d6d || aux_merkle_root32 BE
139+
// || size4 LE || nonce4 LE) produced by dgb::coin::build_aux_mm_commitment
140+
// (the #475 SSOT). When set, it is appended to the coinbase scriptSig at
141+
// mint so a won DGB block carries the DOGE merged-mining commitment the aux
142+
// verifier decodes. DEFAULT nullopt -> coinbase_script is emitted unchanged,
143+
// so the assembled coinbase is BYTE-IDENTICAL to the standalone-parent build.
144+
// The producer (main_dgb seam) only ever populates this under #ifdef AUX_DOGE;
145+
// sourcing a LIVE aux merkle root is the gated DC routing slice, not this one.
146+
std::optional<std::vector<unsigned char>> aux_mm_commitment;
136147
};
137148

138149
inline ConnCoinbaseParts build_connection_coinbase_from_pplns(const ConnCoinbasePplnsInputs& in)
@@ -142,6 +153,13 @@ inline ConnCoinbaseParts build_connection_coinbase_from_pplns(const ConnCoinbase
142153

143154
ConnCoinbaseInputs ci;
144155
ci.coinbase_script = in.coinbase_script;
156+
// Embed-at-mint: append the DGB-as-DOGE-parent MM commitment to the
157+
// coinbase scriptSig when present. nullopt (default / standalone parent)
158+
// leaves coinbase_script byte-identical -- the no-op is structural.
159+
if (in.aux_mm_commitment)
160+
ci.coinbase_script.insert(ci.coinbase_script.end(),
161+
in.aux_mm_commitment->begin(),
162+
in.aux_mm_commitment->end());
145163
ci.segwit_commitment_script = in.segwit_commitment_script;
146164
ci.payout_outputs = std::move(split.payout_outputs);
147165
ci.donation_amount = split.donation_amount;

src/impl/dgb/test/connection_coinbase_test.cpp

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include <gtest/gtest.h>
1919
#include <impl/dgb/coin/connection_coinbase.hpp>
2020
#include <impl/dgb/coin/last_txout_nonce.hpp>
21+
#include <c2pool/merged/merged_mining.hpp> // core SSOT: build_auxpow_commitment
2122

2223
#include <map>
2324
#include <set>
@@ -207,6 +208,75 @@ TEST(ConnCoinbasePplns, ValueAnchorAscendingPayoutOrder) {
207208
}
208209

209210

211+
// ============================================================================
212+
// DGB+DOGE phase DB -- embed-at-mint: the optional aux_mm_commitment field on
213+
// ConnCoinbasePplnsInputs appends the core c2pool::merged::build_auxpow_commitment blob to the
214+
// coinbase scriptSig at mint (the -DAUX_DOGE=ON DGB-as-DOGE-parent path).
215+
// Two invariants, both proven non-circularly off the same fixed inputs:
216+
// (E1) DEFAULT (nullopt) is BYTE-IDENTICAL to the standalone-parent coinbase
217+
// -- the no-op is structural, so the standalone build cannot regress.
218+
// (E2) When set, the assembled scriptSig is the baseline scriptSig with the
219+
// 44-byte MM tag appended verbatim (magic..root..size..nonce), and the
220+
// gentx grows by EXACTLY 44 bytes -- nothing else in the coinbase moves.
221+
// ============================================================================
222+
223+
// (E1) nullopt leaves the gentx byte-identical to the no-aux build.
224+
TEST(ConnCoinbaseEmbedAtMint, NulloptIsByteIdentical) {
225+
std::map<Script, uint288> w{{P1, uint288(3)}, {P2, uint288(1)}};
226+
uint256 ref(std::vector<unsigned char>(32, 0xab));
227+
// baseline: aux_mm_commitment defaults to nullopt inside wired_from_pplns.
228+
auto baseline = wired_from_pplns(w, uint288(4), 10000, true, {}, std::nullopt, ref, NONCE);
229+
230+
dgb::coin::ConnCoinbasePplnsInputs in;
231+
in.coinbase_script = CB;
232+
in.segwit_commitment_script = std::nullopt;
233+
in.weights = w; in.total_weight = uint288(4); in.subsidy = 10000;
234+
in.use_v36_pplns = true; in.donation_script = DON;
235+
in.ref_hash = ref; in.last_txout_nonce = NONCE;
236+
in.aux_mm_commitment = std::nullopt; // explicit default
237+
auto with_field = dgb::coin::build_connection_coinbase_from_pplns(in);
238+
239+
EXPECT_EQ(tohex(baseline.gentx.bytes), tohex(with_field.gentx.bytes));
240+
EXPECT_EQ(baseline.gentx.txid, with_field.gentx.txid);
241+
}
242+
243+
// (E2) a populated commitment appends the 44-byte MM tag to the scriptSig and
244+
// grows the gentx by exactly 44 bytes; the tag bytes match the core SSOT.
245+
TEST(ConnCoinbaseEmbedAtMint, AppendsMmTagToScriptSig) {
246+
std::map<Script, uint288> w{{P1, uint288(3)}, {P2, uint288(1)}};
247+
uint256 ref(std::vector<unsigned char>(32, 0xab));
248+
auto baseline = wired_from_pplns(w, uint288(4), 10000, true, {}, std::nullopt, ref, NONCE);
249+
250+
// aux merkle root = 0x11..0x11 (internal LE); build the canonical 44-byte tag.
251+
uint256 aux_root(std::vector<unsigned char>(32, 0x11));
252+
auto tag = c2pool::merged::build_auxpow_commitment(aux_root, /*size=*/1, /*nonce=*/0);
253+
ASSERT_EQ(tag.size(), size_t{44}); // magic4+root32+size4+nonce4
254+
255+
dgb::coin::ConnCoinbasePplnsInputs in;
256+
in.coinbase_script = CB;
257+
in.segwit_commitment_script = std::nullopt;
258+
in.weights = w; in.total_weight = uint288(4); in.subsidy = 10000;
259+
in.use_v36_pplns = true; in.donation_script = DON;
260+
in.ref_hash = ref; in.last_txout_nonce = NONCE;
261+
in.aux_mm_commitment = tag;
262+
auto embedded = dgb::coin::build_connection_coinbase_from_pplns(in);
263+
264+
// gentx grows by EXACTLY the 44-byte tag -- nothing else shifts.
265+
EXPECT_EQ(embedded.gentx.bytes.size(), baseline.gentx.bytes.size() + tag.size());
266+
267+
// the embedded scriptSig is the baseline scriptSig (CB) followed by the tag.
268+
std::vector<unsigned char> expect_script = CB;
269+
expect_script.insert(expect_script.end(), tag.begin(), tag.end());
270+
// the scriptSig sits right after the 42-byte coinbase txin prologue
271+
// (version4 + vin_count1 + prevout36 + script_len1); locate it by content.
272+
const std::string g = tohex(embedded.gentx.bytes);
273+
EXPECT_NE(g.find(tohex(expect_script)), std::string::npos);
274+
// and the bare tag is present where the baseline had none.
275+
EXPECT_NE(g.find(tohex(tag)), std::string::npos);
276+
EXPECT_EQ(tohex(baseline.gentx.bytes).find(tohex(tag)), std::string::npos);
277+
}
278+
279+
210280
// ============================================================================
211281
// last_txout_nonce SSOT (coin/last_txout_nonce.hpp) -- oracle-faithful uniform
212282
// 64-bit draw. Replaced three drifted std::time()-XOR formulas in

0 commit comments

Comments
 (0)