Skip to content

Commit 57109ce

Browse files
authored
dgb(#82): faithful as_block FRAMING half — share->block reassembly + 5 KATs (#168)
* dgb(#82): faithful as_block FRAMING half — share->block reassembly Port the FRAMING half of p2pool data.py Share.as_block into src/impl/dgb/coin/block_assembly.hpp: reconstruct the full block header merkle_root from the gentx hash walked up the share merkle_link (the SmallBlockHeader stored on a share omits merkle_root), then frame the block txs as [gentx] ++ other_txs (coinbase at index 0) and serialize through the live BlockType codec NodeRPC::submit_block already uses, so the reconstructed block round-trips and a daemon accepts it byte-for-byte. gentx bytes + known_txs lookup stay INJECTED (the gentx-byte build that hits coinbase-byte adjudication is the next reconstructor slice); this pins the framing + merkle_root math now. dgb_block_assembly_test: 5 KATs (empty-branch root==gentx_hash, index-bit side select, coinbase-first round-trip, coinbase-only) registered in BOTH test/CMakeLists.txt and the build.yml --target allowlist. Per-coin isolation: src/impl/dgb/ only; p2pool-merged-v36 surface: none. * dgb(#82): assert won-block witness predicate (is_segwit_activated gate) Add two KATs to block_assembly_test proving the won-block witness shape is governed by whether the gentx carries a witness (i.e. is_segwit_activated at gentx-build time), not by an unconditional witness branch in the framer: * SegwitGentxEmitsWitnessBlock: a gentx carrying the BIP141 coinbase witness reserved value => TX_WITH_WITNESS block (strictly larger than legacy, marker emitted, reserved value round-trips). DGB SEGWIT_ACTIVATION_VERSION=35. * LegacyGentxEmitsLegacyBlock: a no-witness gentx => legacy block, no marker/flag, byte-stable re-serialization (the BCH is_segwit_activated()==0 shape the companion bch test asserts). block_assembly.hpp: correct the wire-encoding comment from unconditional (TX_WITH_WITNESS) to the conditional TX_WITH_WITNESS(m_txs) Bitcoin-Core codec. src/impl/dgb/ only; no p2pool-merged-v36 surface; same dgb_block_assembly_test target already in test/CMakeLists.txt + both build.yml allowlists. --------- Co-authored-by: frstrtr <frstrtr@users.noreply.github.com>
1 parent d0a23d5 commit 57109ce

4 files changed

Lines changed: 433 additions & 2 deletions

File tree

.github/workflows/build.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ jobs:
6767
test_phase4_embedded \
6868
test_mweb_builder \
6969
test_address_resolution test_compute_share_target \
70-
test_utxo test_dgb_subsidy dgb_share_test \
70+
test_utxo test_dgb_subsidy dgb_share_test dgb_block_assembly_test \
7171
rpc_request_test softfork_check_test genesis_check_test algo_select_test digishield_walk_test header_chain_test \
7272
v37_test \
7373
-j$(nproc)
@@ -197,7 +197,7 @@ jobs:
197197
test_phase4_embedded \
198198
test_mweb_builder \
199199
test_address_resolution test_compute_share_target \
200-
test_utxo test_dgb_subsidy dgb_share_test \
200+
test_utxo test_dgb_subsidy dgb_share_test dgb_block_assembly_test \
201201
rpc_request_test softfork_check_test genesis_check_test algo_select_test digishield_walk_test header_chain_test \
202202
test_coin_broadcaster test_multiaddress_pplns test_pplns_stress \
203203
v37_test \
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
#pragma once
2+
// ---------------------------------------------------------------------------
3+
// dgb::coin::assemble_won_block -- the share->block "as_block" reassembly that
4+
// the won-block reconstructor (#82) feeds to broadcast_won_block.
5+
//
6+
// This is the faithful C++ port of the FRAMING half of p2pool data.py
7+
// Share.as_block(tracker, known_txs):
8+
//
9+
// gentx = self.check(tracker, known_txs) # the coinbase tx
10+
// other_txs = [known_txs[h] for h in transaction_hashes]
11+
// return dict(header=self.header, txs=[gentx]+other_txs)
12+
//
13+
// Two consensus-relevant facts this captures:
14+
// 1. p2pool stores only a SmallBlockHeader on the share (version|prev|time|
15+
// bits|nonce -- NO merkle_root). The full block header's merkle_root is
16+
// RECONSTRUCTED as check_merkle_link(gentx_hash, share.m_merkle_link).
17+
// as_block/get_pow both recompute it this way; we must too, or the block
18+
// hashes wrong and the daemon rejects it.
19+
// 2. Block tx order is [gentx] ++ other_txs, with other_txs in the share's
20+
// transaction_hashes order. gentx is always index 0 (the coinbase).
21+
//
22+
// The gentx bytes and the other_txs are INJECTED (already deserialized
23+
// MutableTransaction objects): the gentx-byte build (mirroring
24+
// generate_share_transaction's coinbase assembly -- the part that will hit the
25+
// coinbase-byte adjudication, cf. the BCH lane) and the known_txs lookup are
26+
// the explicitly-next reconstructor slice. Keeping them as inputs makes this
27+
// framing build-verifiable and KAT-testable NOW (same seam-first decomposition
28+
// as won_block_dispatch.hpp), and lets the proven BlockType serializer (the
29+
// live submitblock path, rpc.cpp NodeRPC::submit_block) do the wire encoding so
30+
// the reconstructed block is byte-identical to a daemon-built one.
31+
//
32+
// Per-coin isolation: src/impl/dgb/ only. p2pool-merged-v36 surface: NONE --
33+
// block framing reuses BlockType + check_merkle_link verbatim; no share format,
34+
// PoW hash, coinbase commitment, or PPLNS math is touched. DGB-Scrypt is a
35+
// STANDALONE parent in the V36 default build (no merged-coinbase leg).
36+
// ---------------------------------------------------------------------------
37+
38+
#include <string>
39+
#include <utility>
40+
#include <vector>
41+
42+
#include <core/pack.hpp>
43+
#include <util/strencodings.h>
44+
45+
#include "block.hpp"
46+
#include "../share_check.hpp" // dgb::check_merkle_link (SSOT merkle-branch walk)
47+
#include "../share_types.hpp" // dgb::MerkleLink
48+
49+
namespace dgb
50+
{
51+
namespace coin
52+
{
53+
54+
// Reconstruct the full block header from the share's stored SmallBlockHeader
55+
// plus the gentx hash + merkle link. Mirrors the merkle_root recomputation in
56+
// p2pool data.py get_pow_hash / as_block (SmallBlockHeader omits merkle_root).
57+
inline BlockHeaderType
58+
reconstruct_block_header(const SmallBlockHeaderType& small_header,
59+
const uint256& gentx_hash,
60+
const ::dgb::MerkleLink& merkle_link)
61+
{
62+
BlockHeaderType header;
63+
header.m_version = small_header.m_version;
64+
header.m_previous_block = small_header.m_previous_block;
65+
header.m_timestamp = small_header.m_timestamp;
66+
header.m_bits = small_header.m_bits;
67+
header.m_nonce = small_header.m_nonce;
68+
// SmallBlockHeader has no merkle_root -- recompute it from the coinbase
69+
// (gentx) hash walked up the share's merkle branch, exactly as the share's
70+
// PoW hash was computed at verification time.
71+
header.m_merkle_root = ::dgb::check_merkle_link(gentx_hash, merkle_link);
72+
return header;
73+
}
74+
75+
// Assemble the full serialized parent block for a won share.
76+
// small_header : share.m_min_header (version|prev|time|bits|nonce)
77+
// gentx : the reconstructed coinbase transaction (block tx 0)
78+
// gentx_hash : its txid (double-SHA256), for the merkle_root walk
79+
// merkle_link : share.m_merkle_link (gentx -> merkle root branch)
80+
// other_txs : the share's transaction_hashes resolved to txs, in order
81+
// Returns {block_bytes, block_hex}: block_bytes is the blob the embedded P2P
82+
// relay sends; block_hex is the same block for the external submitblock
83+
// fallback. Wire encoding is BlockType::Serialize -> TX_WITH_WITNESS(m_txs):
84+
// the standard Bitcoin-Core CONDITIONAL serializer -- it emits the per-tx
85+
// witness marker/flag iff some tx HasWitness(), legacy otherwise. The block's
86+
// witness shape is thus governed by whether the gentx carries a witness (i.e.
87+
// is_segwit_activated(share_version) at gentx-build time: DGB v36 segwit-active
88+
// => witness block; a BCH-style is_segwit_activated()==false coin => legacy),
89+
// NOT an unconditional witness branch here. This is the identical path
90+
// NodeRPC::submit_block uses, so the result round-trips and the daemon accepts.
91+
inline std::pair<std::vector<unsigned char>, std::string>
92+
assemble_won_block(const SmallBlockHeaderType& small_header,
93+
const MutableTransaction& gentx,
94+
const uint256& gentx_hash,
95+
const ::dgb::MerkleLink& merkle_link,
96+
const std::vector<MutableTransaction>& other_txs)
97+
{
98+
BlockType block;
99+
static_cast<BlockHeaderType&>(block) =
100+
reconstruct_block_header(small_header, gentx_hash, merkle_link);
101+
102+
// txs = [gentx] ++ other_txs (coinbase first; data.py as_block ordering).
103+
block.m_txs.reserve(1 + other_txs.size());
104+
block.m_txs.push_back(gentx);
105+
for (const auto& tx : other_txs)
106+
block.m_txs.push_back(tx);
107+
108+
PackStream packed = pack<BlockType>(block);
109+
auto sp = packed.get_span();
110+
std::vector<unsigned char> bytes(
111+
reinterpret_cast<const unsigned char*>(sp.data()),
112+
reinterpret_cast<const unsigned char*>(sp.data()) + sp.size());
113+
std::string hex = HexStr(sp);
114+
return {std::move(bytes), std::move(hex)};
115+
}
116+
117+
} // namespace coin
118+
} // namespace dgb

src/impl/dgb/test/CMakeLists.txt

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,21 @@ if (BUILD_TESTING AND GTest_FOUND)
1717
include_directories(${gtest_SOURCE_DIR}/include ${gtest_SOURCE_DIR})
1818
gtest_add_tests(dgb_share_test "" AUTO)
1919

20+
# --- #82 broadcaster-gate: faithful as_block FRAMING -----------------------
21+
# Pins coin/block_assembly.hpp (share->block reassembly: merkle_root
22+
# reconstruction from the gentx hash + the share`s merkle_link, then
23+
# [gentx]++other_txs framing through the live BlockType submitblock codec).
24+
# Links the dgb OBJECT lib like dgb_share_test because block_assembly.hpp
25+
# reuses dgb::check_merkle_link (share_check.hpp) as the merkle SSOT. MUST
26+
# appear in BOTH this registration AND the build.yml --target allowlist.
27+
add_executable(dgb_block_assembly_test block_assembly_test.cpp)
28+
target_link_libraries(dgb_block_assembly_test PRIVATE
29+
GTest::gtest_main GTest::gtest
30+
core dgb
31+
c2pool_payout c2pool_merged_mining c2pool_hashrate c2pool_storage
32+
dgb_coin pool sharechain)
33+
gtest_add_tests(dgb_block_assembly_test "" AUTO)
34+
2035
# --- M3 RPC-transport standalone regression guards ---------------------
2136
# Header-only guards over the external-daemon RPC coin-layer SSOTs
2237
# (rpc_request.hpp request-shape + version floor + genesis identity, and

0 commit comments

Comments
 (0)