Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ jobs:
test_address_resolution test_compute_share_target \
test_utxo test_dgb_subsidy test_dgb_coinbase_value dgb_share_test dgb_block_assembly_test dgb_header_sample_build_test dgb_header_ingest_test dgb_mempool_ingest_test \
dgb_gentx_coinbase_test nmc_auxpow_merkle_test nmc_template_builder_test nmc_auxpow_wire_test dgb_gentx_share_path_test dgb_other_tx_resolver_test \
dgb_other_tx_assembler_test dgb_reconstruct_won_block_test dgb_gentx_unpack_test dgb_work_source_test dgb_template_builder_test dgb_embedded_coin_node_test dgb_embedded_tx_select_test \
dgb_other_tx_assembler_test dgb_reconstruct_won_block_test dgb_reconstruct_closure_test dgb_gentx_unpack_test dgb_work_source_test dgb_template_builder_test dgb_embedded_coin_node_test dgb_embedded_tx_select_test \
rpc_request_test softfork_check_test genesis_check_test algo_select_test digishield_walk_test header_chain_test \
dgb_coin_node_seam_test dgb_block_broadcast_test dgb_won_block_dispatch_test \
v37_test \
Expand Down Expand Up @@ -202,7 +202,7 @@ jobs:
test_address_resolution test_compute_share_target \
test_utxo test_dgb_subsidy test_dgb_coinbase_value dgb_share_test dgb_block_assembly_test dgb_header_sample_build_test dgb_header_ingest_test dgb_mempool_ingest_test \
dgb_gentx_coinbase_test nmc_auxpow_merkle_test nmc_template_builder_test nmc_auxpow_wire_test dgb_gentx_share_path_test dgb_other_tx_resolver_test \
dgb_other_tx_assembler_test dgb_reconstruct_won_block_test dgb_gentx_unpack_test dgb_work_source_test dgb_template_builder_test dgb_embedded_coin_node_test dgb_embedded_tx_select_test \
dgb_other_tx_assembler_test dgb_reconstruct_won_block_test dgb_reconstruct_closure_test dgb_gentx_unpack_test dgb_work_source_test dgb_template_builder_test dgb_embedded_coin_node_test dgb_embedded_tx_select_test \
rpc_request_test softfork_check_test genesis_check_test algo_select_test digishield_walk_test header_chain_test \
dgb_coin_node_seam_test dgb_block_broadcast_test dgb_won_block_dispatch_test \
test_coin_broadcaster test_multiaddress_pplns test_pplns_stress \
Expand Down
126 changes: 126 additions & 0 deletions src/impl/dgb/coin/reconstruct_closure.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
#pragma once
// ---------------------------------------------------------------------------
// dgb::coin::make_reconstruct_closure -- the run-loop's faithful
// WonBlockReconstructor (#82): the closure main_dgb.cpp binds into
// make_on_block_found, replacing the interim `return nullopt` stub. It is the
// open half of DGB's #82 broadcaster dual-path -- until this is non-stub a
// pool-won DGB block is announced + audited but never reconstructed, so neither
// the P2P-relay arm (#260) nor the submitblock RPC fallback ever fires on a
// real won share.
//
// It composes the already-landed, individually-KAT'd slices into one faithful
// port of p2pool data.py Share.as_block(tracker, known_txs):
// gentx = unpack_gentx_coinbase(generate_share_transaction(share).bytes)
// other_txs = [known_txs[h] for h in get_other_tx_hashes(tracker)]
// block = reconstruct_won_block(header, link, gentx, ..., refs, ...)
//
// SEAM-FIRST (mirrors every #82 sub-slice): the share lookup, the gentx
// regeneration, and the three tracker/mempool walks are INJECTED as callables,
// so the whole closure -- including its fail-closed posture -- is unit-testable
// against a synthetic share set with NO live ShareTracker / mempool. In the
// run-loop they bind to:
// share_fields_fn = chain.get_share(h) -> {m_min_header, m_merkle_link,
// m_tx_info.m_transaction_hash_refs}
// gentx_bytes_fn = generate_share_transaction(share, tracker, params,
// false, v36_active=false, &gc) -> gc.bytes (#173 SSOT)
// nth_parent_fn = chain.get_nth_parent_via_skip(h, n)
// new_tx_hashes_fn = chain.get_share(h)->m_tx_info.m_new_transaction_hashes
// known_txs_fn = mempool/known-tx find_tx(hash) -> *MutableTransaction
//
// FAIL-CLOSED (integrator 2026-06-19/20, REWARD-PATH CRITICAL): this callback
// fires from the compute thread on a won share. It NEVER throws out of the
// callback and NEVER emits a partial/wrong block: ANY missing share, missing
// known-tx, out-of-range ref-walk, or gentx-regen failure is caught, logged
// LOUDLY, and yields std::nullopt -- the share is announced + audited, the RPC
// submitblock fallback still attempts independently, and no malformed block
// reaches the network. A wrong reconstruction would hash to the wrong merkle
// root and be daemon-rejected anyway, so failing closed here is strictly safer
// than emitting it.
//
// Per-coin isolation: src/impl/dgb/ only. p2pool-merged-v36 surface: NONE --
// pure composition of already-validated share_info + already-relayed txs + the
// proven BlockType serializer. DGB-Scrypt is a STANDALONE parent in the V36
// default build (no merged-coinbase leg).
// ---------------------------------------------------------------------------

#include <exception>
#include <functional>
#include <iostream>
#include <optional>
#include <string>
#include <utility>
#include <vector>

#include <core/uint256.hpp>

#include "reconstruct_won_block.hpp" // reconstruct_won_block, ReconstructedWonBlock, SmallBlockHeaderType, MutableTransaction
#include "gentx_unpack.hpp" // unpack_gentx_coinbase, UnpackedGentx
#include "won_block_dispatch.hpp" // WonBlockReconstructor

namespace dgb
{
namespace coin
{

// The per-share inputs the closure pulls from the sharechain to drive
// reconstruct_won_block: everything that is NOT the gentx or the ancestry walk.
// small_header : share.m_min_header (version|prev|time|bits|nonce)
// merkle_link : share.m_merkle_link (gentx -> merkle root branch)
// refs : share.m_tx_info.m_transaction_hash_refs, in order
struct ShareReconstructFields
{
SmallBlockHeaderType small_header;
::dgb::MerkleLink merkle_link;
std::vector<TxHashRefs> refs;
};

// Build the run-loop WonBlockReconstructor. See header note for the run-loop
// bindings of each injected seam. The returned callable is fail-closed: it
// returns std::nullopt (never throws, never a partial block) on ANY error.
inline WonBlockReconstructor
make_reconstruct_closure(
std::function<ShareReconstructFields(const uint256&)> share_fields_fn,
std::function<std::vector<unsigned char>(const uint256&)> gentx_bytes_fn,
std::function<uint256(const uint256&, uint64_t)> nth_parent_fn,
std::function<const std::vector<uint256>&(const uint256&)> new_tx_hashes_fn,
std::function<const MutableTransaction*(const uint256&)> known_txs_fn)
{
return [share_fields_fn = std::move(share_fields_fn),
gentx_bytes_fn = std::move(gentx_bytes_fn),
nth_parent_fn = std::move(nth_parent_fn),
new_tx_hashes_fn = std::move(new_tx_hashes_fn),
known_txs_fn = std::move(known_txs_fn)](const uint256& share_hash)
-> std::optional<std::pair<std::vector<unsigned char>, std::string>>
{
try
{
// 1. share-level fields (header / merkle_link / tx refs).
const ShareReconstructFields f = share_fields_fn(share_hash);

// 2. regenerate the share's SSOT gentx bytes, then unpack to the
// MutableTransaction (+ canonical txid) reconstruct injects at
// block tx index 0. unpack throws on a malformed serialization.
const UnpackedGentx ug = unpack_gentx_coinbase(gentx_bytes_fn(share_hash));

// 3. compose: resolve refs -> assemble other_txs -> frame the block.
ReconstructedWonBlock r = reconstruct_won_block(
f.small_header, f.merkle_link, ug.tx, ug.txid, share_hash, f.refs,
nth_parent_fn, new_tx_hashes_fn, known_txs_fn);

return std::make_pair(std::move(r.bytes), std::move(r.hex));
}
catch (const std::exception& e)
{
// Fail closed: announce + audit, never broadcast a partial/wrong
// block. The RPC submitblock fallback still attempts independently.
std::cout << "[DGB-POOL-BLOCK] won share " << share_hash.GetHex().substr(0, 16)
<< " -- reconstruct FAILED CLOSED (" << e.what()
<< "); NOT broadcast on P2P arm (RPC fallback still attempts)"
<< std::endl;
return std::nullopt;
}
};
}

} // namespace coin
} // namespace dgb
14 changes: 14 additions & 0 deletions src/impl/dgb/test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,20 @@ if (BUILD_TESTING AND GTest_FOUND)
dgb_coin pool sharechain)
gtest_add_tests(dgb_reconstruct_won_block_test "" AUTO)

# --- #82: won-block reconstruct CLOSURE (run-loop WonBlockReconstructor) -
# Pins coin/reconstruct_closure.hpp: make_reconstruct_closure composes the
# share-field + gentx-regen seams + reconstruct_won_block under a fail-closed
# catch (the closure main_dgb.cpp binds in place of the nullopt stub). Links
# the dgb OBJECT lib like dgb_reconstruct_won_block_test (pulls block_assembly
# via the closure). MUST also appear in the build.yml --target allowlist.
add_executable(dgb_reconstruct_closure_test reconstruct_closure_test.cpp)
target_link_libraries(dgb_reconstruct_closure_test PRIVATE
GTest::gtest_main GTest::gtest
core dgb
c2pool_payout c2pool_merged_mining c2pool_hashrate c2pool_storage
dgb_coin pool sharechain)
gtest_add_tests(dgb_reconstruct_closure_test "" AUTO)

# --- M3 RPC-transport standalone regression guards ---------------------
# Header-only guards over the external-daemon RPC coin-layer SSOTs
# (rpc_request.hpp request-shape + version floor + genesis identity, and
Expand Down
Loading
Loading