Skip to content

dgb(#82): gentx-bytes -> MutableTransaction unpack (inverse of #173) - #179

Merged
frstrtr merged 7 commits into
masterfrom
dgb/gentx-unpack
Jun 19, 2026
Merged

dgb(#82): gentx-bytes -> MutableTransaction unpack (inverse of #173)#179
frstrtr merged 7 commits into
masterfrom
dgb/gentx-unpack

Conversation

@frstrtr

@frstrtr frstrtr commented Jun 19, 2026

Copy link
Copy Markdown
Owner

Inverse of #173 GentxCoinbase exposure: coin/gentx_unpack.hpp unpack_gentx_coinbase() turns the share SSOT non-witness gentx bytes back into the MutableTransaction reconstruct_won_block injects at block tx index 0.

  • Parses with TX_NO_WITNESS: coinbase vin count is 0x01 (never the segwit 0x00 dummy), so the non-witness bytes round-trip EXACTLY and the txid (== gentx_hash, consumed by the merkle_root walk) stays stable. No witness/serialization drift -> assembled block merkle root cannot be corrupted.
  • Throws std::out_of_range on trailing bytes (malformed gentx).

KAT (dgb_gentx_unpack_test, 6/6 PASS, build EXIT=0): oracle round-trip for both no-segwit + witness-commitment-first layouts (byte-exact re-serialize + txid stable + HasWitness false), coinbase-shape recovery, inverse-pairing vs assemble_gentx_coinbase (txid == GentxCoinbase.txid), trailing-byte throw. Vectors reused from gentx_coinbase_test (oracle frstrtr/p2pool-dgb-scrypt).

Wired into test CMake + both build.yml --target allowlists (#143 NOT_BUILT trap avoided). src/impl/dgb/ only; p2pool-merged-v36 surface: NONE. Stacked on #177 (c48328d). HOLD merge — lands bottom-up on operator-approved card.

frstrtr added 7 commits June 19, 2026 01:42
…coinbase

generate_share_transaction (verification path) and create_local_share
(emission path) each open-coded the coinbase wire layout
(version|vin|vouts|locktime) and the txid double-SHA256. Two byte paths
pinned to the same oracle vector = proven-equivalent, not unified: a
third edit could still diverge them.

Collapse both onto dgb::coin::assemble_gentx_coinbase() (coin/gentx_coinbase.hpp,
the #171 SSOT) so emission == verification == one source. Each call site
now only builds its per-share inputs (coinbase script, optional segwit
witness-commitment script, PPLNS payout outputs, donation, OP_RETURN ref
commitment) and hands them to the assembler; the assembler owns wire
framing + txid. PackStream tx is reconstructed from the SSOT bytes so the
downstream V36 hash_link diagnostics are byte-for-byte unchanged.

No oracle vector regeneration. Single-coin scope: src/impl/dgb/ only.
dgb build EXIT=0; dgb_share_test 5/5; dgb_gentx_coinbase_test 3/3.
…its SSOT framing

Round-trip equivalence KAT (op_return ref_hash is share-derived, cannot pin a
fixed oracle vector): generate_share_transaction(share) txid == verbatim
re-derivation through the SAME assemble_gentx_coinbase SSOT. Proves the #172
collapse behaviorally, not structurally. 3/3 PASS, txid 9e2b78d5. Wired into
ctest + BOTH build.yml allowlists in this commit (#143 NOT_BUILT trap).
generate_share_transaction computes the coinbase via assemble_gentx_coinbase
(the SSOT proven by #172) but returned only the gentx_hash, discarding the
bytes. reconstruct_won_block needs the exact non-witness coinbase bytes -- it
is block tx[0] and its txid is the merkle leaf -- not just the hash.

Add an optional out_gentx (GentxCoinbase*) param, default nullptr: zero change
for the verification callers, surfaces {bytes,txid} for the reconstructor. KAT
proves the exposed bytes hash to the returned gentx_hash and deserialize into
the coinbase MutableTransaction that assemble_won_block frames, round-tripping
byte-identically (non-witness). Single-coin (src/impl/dgb/ only); reuses the
existing #172 dgb_gentx_share_path_test target (no new allowlist entry).
Won-block reconstructor connecting tissue (sub-slice 1): faithful port of
p2pool get_other_tx_hashes ancestry resolution. coin/other_tx_resolver.hpp
walks each (share_count, tx_count) ref back share_count generations from the
won share (0 == the share itself) and indexes tx_count into that ancestors
new_transaction_hashes, preserving ref order = block other_txs order.

The two tracker ops (nth_parent / new_transaction_hashes lookup) are injected
as callables, mirroring the seam-first decomposition of won_block_dispatch.hpp,
so the walk is unit-testable against a synthetic ancestry without a live
ShareTracker. Reuses the tracker ancestry walk rather than a flat known_txs
map: a flat lookup cannot reproduce how the sharechain addresses a tx and
diverges on duplicate txids or refs past the locally-known set.

Malformed refs (tx_count OOB, share_count past chain end) throw rather than
emit a wrong hash. 6 gtests pin self-share/walk-back/order/empty/both throws.
Single-coin (src/impl/dgb/ only); no shared base / p2pool-merged-v36 surface.
Test wired into ctest + both build.yml --target allowlists (#143).
…b-slice 2)

Won-block reconstructor connecting tissue (sub-slice 2): coin/other_tx_assembler.hpp
bridges the ordered other_tx hash list from resolve_other_tx_hashes (sub-slice 1)
to the deserialized MutableTransaction vector that assemble_won_block frames as
txs = [gentx] ++ other_txs. Faithful port of the known_txs lookup in p2pool
data.py Share.as_block: other_txs = [known_txs[h] for h in transaction_hashes].

The known-tx lookup is INJECTED as a (hash) -> const MutableTransaction* callable
(nullptr == unknown), mirroring the seam-first decomposition of sub-slice 1 and
won_block_dispatch.hpp, so the bridge is unit-testable against a synthetic
known-tx set without a live mempool. A missing hash THROWS std::out_of_range
rather than dropping/placeholder-filling the tx: a block with a missing other_tx
has the wrong merkle root and is daemon-rejected, so loud failure is strictly
safer than emitting a malformed block.

Witness framing is not decided here -- the txs are returned verbatim and the
block witness shape stays governed by assemble_won_block`s TX_WITH_WITNESS
conditional codec. DGB is segwit-active (SEGWIT_ACTIVATION_VERSION, v36 >= it),
so the path is live; the KAT pins that a witness-bearing other_tx flips the
assembled block to the witness codec via the other_tx leg, not gentx-only.

dgb_other_tx_assembler_test: 5/5 PASS -- order-preserve, missing-throws, empty,
end-to-end resolve->assemble->frame (coinbase-first, ref order), witness
reachability. Wired into test/CMakeLists.txt + both build.yml --target
allowlists (#143 NOT_BUILT trap). Per-coin isolation: src/impl/dgb/ only;
p2pool-merged-v36 surface: NONE.
…me as_block

Tie the three landed sub-slices into the single composition the dispatch
handler injects as its WonBlockReconstructor:
  resolve_other_tx_hashes (#174) -> assemble_other_txs (#176)
  -> assemble_won_block (#168 framing)

Faithful port of p2pool data.py Share.as_block: produces {bytes, hex} for
broadcast_won_block dual path. Gentx enters as a deserialized
MutableTransaction + txid (the SSOT gentx-bytes -> MutableTransaction unpack,
inverse of #173 exposure, is the next slice). Injected tracker/known_txs
seams keep it unit-testable; out_of_range propagation from each step
preserved (never emit a partial/wrong block).

dgb_reconstruct_won_block_test 5/5 PASS: end-to-end == manual
resolve+assemble+frame (byte-identical), [gentx]++others in ref order,
empty-refs gentx-only, ref-past-chain + unknown-known-tx throw. Wired into
test/CMakeLists.txt + both build.yml --target allowlists (#143 trap).

Per-coin isolation: src/impl/dgb/ only. p2pool-merged-v36 surface: none.
coin/gentx_unpack.hpp: unpack_gentx_coinbase() turns the share SSOT
non-witness gentx bytes (generate_share_transaction out_gentx, #173)
back into the MutableTransaction reconstruct_won_block injects at block
tx index 0. Parses with TX_NO_WITNESS so the coinbase round-trips its
non-witness bytes EXACTLY and the txid (== gentx_hash, consumed by the
merkle_root walk) stays stable -- no witness/serialization drift; throws
std::out_of_range on trailing bytes (malformed gentx).

gentx_unpack_test: 6/6 oracle round-trip (no-segwit + witness-commitment
layouts, byte-exact + txid stable + HasWitness false), coinbase-shape
recovery, inverse-pairing vs assemble_gentx_coinbase, and trailing-byte
throw. Wired into test CMake + both build.yml --target allowlists.

src/impl/dgb/ only; p2pool-merged-v36 surface NONE.
@frstrtr
frstrtr force-pushed the dgb/gentx-unpack branch from 209435c to 02049a9 Compare June 19, 2026 01:44
@frstrtr
frstrtr merged commit fa93959 into master Jun 19, 2026
17 checks passed
frstrtr added a commit that referenced this pull request Jun 19, 2026
…(--run)

Stand up the run-loop spine in main_dgb.cpp behind a new --run flag: a
boost::asio::io_context plus an explicit graceful shutdown driven from
signal_set(SIGINT, SIGTERM), mirroring main_btc.cpp s teardown contract.

This is the lifecycle every node subsystem (sharechain peer dgb::Node,
embedded digibyted P2P, Stratum acceptor) and the #82 won-block dispatch
handler hangs off. Standing the spine up first keeps the next increments
(node/Config construction over LevelDB, P2P listen, Stratum, and the
m_on_block_found -> reconstruct_won_block -> broadcast_won_block binding
landed across #163/#166/#167/#173/#174/#176/#177/#179) build-verifiable
rather than landing a large unverified port in one step.

--selftest / bare invocation unchanged (live ShareTracker::score() path).
External digibyted RPC fallback posture preserved.

Build: c2pool-dgb links EXIT=0 (-DCOIN_DGB=ON). Smoke: --selftest OK
(block_period=75s SSOT); --run spine up, SIGINT -> clean shutdown exit 0.
frstrtr added a commit that referenced this pull request Jun 19, 2026
…(--run)

Stand up the run-loop spine in main_dgb.cpp behind a new --run flag: a
boost::asio::io_context plus an explicit graceful shutdown driven from
signal_set(SIGINT, SIGTERM), mirroring main_btc.cpp s teardown contract.

This is the lifecycle every node subsystem (sharechain peer dgb::Node,
embedded digibyted P2P, Stratum acceptor) and the #82 won-block dispatch
handler hangs off. Standing the spine up first keeps the next increments
(node/Config construction over LevelDB, P2P listen, Stratum, and the
m_on_block_found -> reconstruct_won_block -> broadcast_won_block binding
landed across #163/#166/#167/#173/#174/#176/#177/#179) build-verifiable
rather than landing a large unverified port in one step.

--selftest / bare invocation unchanged (live ShareTracker::score() path).
External digibyted RPC fallback posture preserved.

Build: c2pool-dgb links EXIT=0 (-DCOIN_DGB=ON). Smoke: --selftest OK
(block_period=75s SSOT); --run spine up, SIGINT -> clean shutdown exit 0.
frstrtr added a commit that referenced this pull request Jun 19, 2026
…(--run)

Stand up the run-loop spine in main_dgb.cpp behind a new --run flag: a
boost::asio::io_context plus an explicit graceful shutdown driven from
signal_set(SIGINT, SIGTERM), mirroring main_btc.cpp s teardown contract.

This is the lifecycle every node subsystem (sharechain peer dgb::Node,
embedded digibyted P2P, Stratum acceptor) and the #82 won-block dispatch
handler hangs off. Standing the spine up first keeps the next increments
(node/Config construction over LevelDB, P2P listen, Stratum, and the
m_on_block_found -> reconstruct_won_block -> broadcast_won_block binding
landed across #163/#166/#167/#173/#174/#176/#177/#179) build-verifiable
rather than landing a large unverified port in one step.

--selftest / bare invocation unchanged (live ShareTracker::score() path).
External digibyted RPC fallback posture preserved.

Build: c2pool-dgb links EXIT=0 (-DCOIN_DGB=ON). Smoke: --selftest OK
(block_period=75s SSOT); --run spine up, SIGINT -> clean shutdown exit 0.
frstrtr added a commit that referenced this pull request Jun 19, 2026
…(--run)

Stand up the run-loop spine in main_dgb.cpp behind a new --run flag: a
boost::asio::io_context plus an explicit graceful shutdown driven from
signal_set(SIGINT, SIGTERM), mirroring main_btc.cpp s teardown contract.

This is the lifecycle every node subsystem (sharechain peer dgb::Node,
embedded digibyted P2P, Stratum acceptor) and the #82 won-block dispatch
handler hangs off. Standing the spine up first keeps the next increments
(node/Config construction over LevelDB, P2P listen, Stratum, and the
m_on_block_found -> reconstruct_won_block -> broadcast_won_block binding
landed across #163/#166/#167/#173/#174/#176/#177/#179) build-verifiable
rather than landing a large unverified port in one step.

--selftest / bare invocation unchanged (live ShareTracker::score() path).
External digibyted RPC fallback posture preserved.

Build: c2pool-dgb links EXIT=0 (-DCOIN_DGB=ON). Smoke: --selftest OK
(block_period=75s SSOT); --run spine up, SIGINT -> clean shutdown exit 0.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant