Skip to content

Commit d4b7379

Browse files
authored
Merge pull request #305 from frstrtr/dgb/phase-b-mint-adapter
dgb: re-land worker->mint adapter (run_loop_mint.hpp) — Phase B consumer seam
2 parents 5eaed7b + 9d0b131 commit d4b7379

2 files changed

Lines changed: 185 additions & 0 deletions

File tree

src/impl/dgb/run_loop_mint.hpp

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
#pragma once
2+
// run_loop_mint.hpp — DGB Phase B: worker->mint adapter SSOT.
3+
//
4+
// This is the consumer adapter that DGBWorkSource::MintShareFn binds to in
5+
// main_dgb.cpp (work_source.hpp:197 "binds this to mint_local_share_with_ratchet
6+
// (run_loop_mint.hpp) -> create_local_share"). When mining_submit classifies a
7+
// submission as ShareAccept (Scrypt PoW meets the SHARE target but NOT the full
8+
// block target), try_mint_share() invokes this with the assembled MintShareInputs.
9+
// It:
10+
// 1. parses the 80-byte reconstructed block header into a SmallBlockHeaderType,
11+
// 2. asks the live AutoRatchet for the {mint, vote} version pair via
12+
// dgb_select_mint_versions (anchored at the current sharechain tip = the
13+
// new share's parent), and
14+
// 3. inserts the share into the tracker via create_local_share, returning the
15+
// minted share hash (NULL uint256 on a malformed header / fail-closed).
16+
//
17+
// History: this helper was authored under #294 and lost from master in the
18+
// #292/#293/#294 stacked squash-merge (no #294 merge commit reaches master; the
19+
// underlying primitives dgb_select_mint_versions + create_local_share survive).
20+
// Re-landed here to its documented contract against master's create_local_share
21+
// signature, which is authoritative.
22+
//
23+
// PURE adapter: the helper takes references and performs ONE tracker insertion.
24+
// Thread-safety of that insertion is the CALLER's concern — main_dgb binds this
25+
// on the Stratum-submission thread and is responsible for the tracker lock.
26+
//
27+
// Duck-typed on the inputs (InputsT) so this header does NOT pull the stratum
28+
// work_source TU in: any struct exposing { header_bytes, coinbase_bytes, subsidy,
29+
// prev_share, merkle_branches, payout_script, segwit_active } binds — in
30+
// production that is DGBWorkSource::MintShareInputs.
31+
32+
#include <optional>
33+
#include <vector>
34+
35+
#include <core/pack.hpp> // PackStream, operator>>
36+
#include <core/pack_types.hpp> // BaseScript
37+
#include <core/uint256.hpp>
38+
39+
#include <impl/dgb/coin/block.hpp> // SmallBlockHeaderType, BlockHeaderType
40+
#include <impl/dgb/share_check.hpp> // create_local_share, MergedAddressEntry, StaleInfo, uint128
41+
#include <impl/dgb/auto_ratchet_wire.hpp> // dgb_select_mint_versions, AutoRatchet
42+
43+
namespace dgb {
44+
45+
// Parse a canonical 80-byte block header (fixed 4-byte version + merkle_root)
46+
// into the SmallBlockHeaderType (version|prev|time|bits|nonce) create_local_share
47+
// consumes. Returns nullopt on a short/malformed buffer (fail-closed). Pure.
48+
inline std::optional<coin::SmallBlockHeaderType>
49+
parse_min_header_80(const std::vector<unsigned char>& header_bytes)
50+
{
51+
if (header_bytes.size() < 80)
52+
return std::nullopt;
53+
try {
54+
PackStream ps(header_bytes);
55+
coin::BlockHeaderType full;
56+
ps >> full;
57+
// SmallBlockHeaderType is the base subobject of BlockHeaderType; slicing
58+
// off m_merkle_root yields exactly the small-header fields.
59+
return static_cast<coin::SmallBlockHeaderType&>(full);
60+
} catch (const std::exception&) {
61+
return std::nullopt; // truncated / unserialize failure -> fail-closed
62+
}
63+
}
64+
65+
// The worker->mint adapter. Returns the minted share hash, or NULL uint256 if
66+
// the header is malformed (fail-closed; the caller logs a no-record outcome).
67+
template <typename TrackerT, typename InputsT>
68+
inline uint256 mint_local_share_with_ratchet(
69+
const InputsT& in,
70+
TrackerT& tracker,
71+
const core::CoinParams& params,
72+
AutoRatchet& ratchet,
73+
uint16_t donation = 50)
74+
{
75+
auto min_header = parse_min_header_80(in.header_bytes);
76+
if (!min_header)
77+
return uint256(); // fail-closed: malformed 80-byte header
78+
79+
BaseScript coinbase(in.coinbase_bytes);
80+
81+
// The new share's parent is the current sharechain tip carried by the
82+
// submission; the ratchet decides which {mint, vote} version pair to stamp.
83+
const uint256 best_share = in.prev_share;
84+
auto [mint_version, vote_version] =
85+
dgb_select_mint_versions(ratchet, tracker, best_share);
86+
87+
return create_local_share(
88+
tracker, params, *min_header, coinbase, in.subsidy, in.prev_share,
89+
in.merkle_branches, in.payout_script,
90+
donation,
91+
std::vector<MergedAddressEntry>{}, // merged_addrs (DOGE aux: none in Scrypt-only)
92+
StaleInfo::none, // stale_info
93+
in.segwit_active, // segwit_active
94+
std::string{}, // witness_commitment_hex
95+
std::vector<unsigned char>{}, // message_data
96+
std::vector<unsigned char>{}, // actual_coinbase_bytes
97+
uint256(), // witness_root
98+
0u, // override_max_bits
99+
0u, // override_bits
100+
0u, // frozen_absheight
101+
uint128(), // frozen_abswork
102+
uint256(), // frozen_far_share_hash
103+
0u, // frozen_timestamp
104+
uint256(), // frozen_merged_payout_hash
105+
false, // has_frozen
106+
std::vector<uint256>{}, // frozen_merkle_branches
107+
uint256(), // frozen_witness_root
108+
std::vector<unsigned char>{}, // frozen_merged_coinbase_info
109+
static_cast<int64_t>(mint_version), // share_version (ratchet-selected)
110+
static_cast<uint64_t>(vote_version)); // desired_version (always target)
111+
}
112+
113+
} // namespace dgb

src/impl/dgb/test/share_test.cpp

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include <impl/dgb/coin/rpc_conf.hpp> // #82 external-daemon RPC creds (digibyte.conf)
2323
#include <impl/dgb/auto_ratchet.hpp> // Phase B: mint-side share-version ratchet
2424
#include <impl/dgb/auto_ratchet_wire.hpp> // Phase B: production wire-in (base_version=35)
25+
#include <impl/dgb/run_loop_mint.hpp> // Phase B: worker->mint adapter (re-landed #294)
2526
#include <unistd.h> // getpid (AutoRatchet KAT temp state file)
2627

2728
#include <cstdio>
@@ -494,3 +495,74 @@ TEST(DGB_share_test, AutoRatchetWireBootstrapMints35Votes36)
494495
EXPECT_EQ(vote, 36); // always vote target
495496
EXPECT_EQ(ar.state(), dgb::RatchetState::VOTING);
496497
}
498+
499+
// ----------------------------------------------------------------------------
500+
// run_loop_mint.hpp — Phase B worker->mint adapter (re-landed #294 helper).
501+
// The adapter maps a ShareAccept submission's raw header/coinbase bytes into
502+
// create_local_share()'s arguments, stamping the ratchet-selected {mint, vote}
503+
// version pair. These KATs pin the novel logic: the 80-byte header parse and the
504+
// fail-closed guard. The version-selection delegation is pinned by the
505+
// AutoRatchetWire* tests above; create_local_share's own insertion path is
506+
// covered by its existing fixtures.
507+
// ----------------------------------------------------------------------------
508+
509+
// Local stand-in for DGBWorkSource::MintShareInputs — the adapter is duck-typed
510+
// on the inputs so this TU need not pull the stratum work_source in.
511+
struct MintInputsKAT {
512+
std::vector<unsigned char> header_bytes;
513+
std::vector<unsigned char> coinbase_bytes;
514+
uint64_t subsidy = 0;
515+
uint256 prev_share;
516+
std::vector<uint256> merkle_branches;
517+
std::vector<unsigned char> payout_script;
518+
bool segwit_active = false;
519+
};
520+
521+
TEST(DGB_run_loop_mint, ParseMinHeader80RoundTrip)
522+
{
523+
dgb::coin::BlockHeaderType hdr;
524+
hdr.m_version = 0x20000000u; // version bits | SCRYPT marker shape
525+
hdr.m_previous_block = uint256S(
526+
"00000000000000000000000000000000000000000000000000000000deadbeef");
527+
hdr.m_merkle_root = uint256S(
528+
"00000000000000000000000000000000000000000000000000000000cafef00d");
529+
hdr.m_timestamp = 0x5f5e1000u;
530+
hdr.m_bits = 0x1e0ffff0u;
531+
hdr.m_nonce = 0x12345678u;
532+
533+
PackStream packed = pack<dgb::coin::BlockHeaderType>(hdr);
534+
std::vector<unsigned char> bytes(
535+
reinterpret_cast<unsigned char*>(packed.data()),
536+
reinterpret_cast<unsigned char*>(packed.data()) + packed.size());
537+
ASSERT_EQ(bytes.size(), 80u); // 4+32+32+4+4+4
538+
539+
auto small = dgb::parse_min_header_80(bytes);
540+
ASSERT_TRUE(small.has_value());
541+
EXPECT_EQ(small->m_version, 0x20000000u);
542+
EXPECT_EQ(small->m_previous_block, hdr.m_previous_block);
543+
EXPECT_EQ(small->m_timestamp, 0x5f5e1000u);
544+
EXPECT_EQ(small->m_bits, 0x1e0ffff0u);
545+
EXPECT_EQ(small->m_nonce, 0x12345678u);
546+
}
547+
548+
TEST(DGB_run_loop_mint, ParseMinHeaderShortFailsClosed)
549+
{
550+
EXPECT_FALSE(dgb::parse_min_header_80({}).has_value());
551+
EXPECT_FALSE(dgb::parse_min_header_80(std::vector<unsigned char>(79, 0)).has_value());
552+
}
553+
554+
// A malformed (short) header returns NULL uint256 WITHOUT touching the tracker —
555+
// and exercises the template instantiation of mint_local_share_with_ratchet
556+
// against master's create_local_share signature (compile+link drift guard).
557+
TEST(DGB_run_loop_mint, AdapterFailsClosedOnShortHeader)
558+
{
559+
dgb::ShareTracker tracker;
560+
auto params = dgb::make_coin_params(/*testnet=*/false);
561+
auto ratchet = dgb::make_dgb_ratchet();
562+
563+
MintInputsKAT in;
564+
in.header_bytes = std::vector<unsigned char>(40, 0); // too short -> fail-closed
565+
566+
uint256 h = dgb::mint_local_share_with_ratchet(in, tracker, params, ratchet);
567+
EXPECT_TRUE(h.IsNull());
568+
}

0 commit comments

Comments
 (0)