Skip to content

Commit d0ef185

Browse files
authored
dgb: stand up worker->mint share-accept seam on DGBWorkSource (Phase B) (#295)
mining_submit (work_source.cpp:292) classifies a Scrypt submission three ways; the won-block outcome already dispatches via SubmitBlockFn, but the share-difficulty outcome -- "pow_hash <= share target -> record share in tracker" -- had no seam. This adds the producer half of the worker->mint run-loop standup, parallel to SubmitBlockFn: - MintShareInputs: raw-bytes found-share fields (header, coinbase, subsidy, prev_share/tip, merkle_branches, payout_script, segwit), decoupled from share/BlockType serialization like SubmitBlockFn. - MintShareFn + set_mint_share_fn(): the callback main_dgb.cpp binds to mint_local_share_with_ratchet (run_loop_mint.hpp) -> create_local_share, where the AutoRatchet chooses the {mint,vote} version pair. Mirrors the set_best_share_hash_fn idiom (empty until wired). - try_mint_share(): thread-safe dispatch (copies fn under lock); when unbound, logs loudly and returns a NULL hash -- no silent drop. No behavior change: the 4a mining_submit stub still rejects every submission and never reaches the seam (the classify branch lands in 4d). +3 KATs (forward+pass-through, empty-until-wired no-silent-drop, stub does-not-reach), dgb_work_source_test 20/20. Co-authored-by: frstrtr <frstrtr@users.noreply.github.com>
1 parent e45baef commit d0ef185

3 files changed

Lines changed: 154 additions & 0 deletions

File tree

src/impl/dgb/stratum/work_source.cpp

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -333,4 +333,26 @@ void DGBWorkSource::set_best_share_hash_fn(std::function<uint256()> fn)
333333
best_share_hash_fn_ = std::move(fn);
334334
}
335335

336+
void DGBWorkSource::set_mint_share_fn(MintShareFn fn)
337+
{
338+
std::lock_guard<std::mutex> lk(mint_share_mutex_);
339+
mint_share_fn_ = std::move(fn);
340+
}
341+
342+
uint256 DGBWorkSource::try_mint_share(const MintShareInputs& in) const
343+
{
344+
MintShareFn fn;
345+
{
346+
std::lock_guard<std::mutex> lk(mint_share_mutex_);
347+
fn = mint_share_fn_; // copy so a concurrent set cannot tear it out mid-call
348+
}
349+
if (!fn) {
350+
LOG_WARNING << "[DGB-STRATUM] share met share-target but no mint callback "
351+
"wired -- share NOT recorded (set_mint_share_fn unbound). "
352+
"No silent drop: logged.";
353+
return uint256{};
354+
}
355+
return fn(in);
356+
}
357+
336358
} // namespace dgb::stratum

src/impl/dgb/stratum/work_source.hpp

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,35 @@ class DGBWorkSource : public core::stratum::IWorkSource
8181
using SubmitBlockFn = std::function<bool(const std::vector<unsigned char>& block_bytes,
8282
uint32_t height)>;
8383

84+
/// Inputs the worker->mint sharechain-accept path hands to the run-loop:
85+
/// the assembled fields of a submission whose **Scrypt** PoW met the SHARE
86+
/// target (but NOT the full block target). Raw-bytes form (header +
87+
/// coinbase) keeps DGBWorkSource decoupled from the share/BlockType
88+
/// serialization details, exactly like SubmitBlockFn -- main_dgb.cpp parses
89+
/// these into create_local_share()'s arguments.
90+
struct MintShareInputs {
91+
std::vector<unsigned char> header_bytes; ///< 80-byte reconstructed block header
92+
std::vector<unsigned char> coinbase_bytes; ///< p2pool coinbase scriptSig (BIP34 height + marker)
93+
uint64_t subsidy = 0; ///< coinbasevalue (block reward)
94+
uint256 prev_share; ///< current sharechain tip = the new share's parent
95+
std::vector<uint256> merkle_branches; ///< coinbase txid -> merkle root branches
96+
std::vector<unsigned char> payout_script; ///< finder's scriptPubKey
97+
bool segwit_active = false;
98+
};
99+
100+
/// Callback invoked when `mining_submit` validates a submission whose
101+
/// **Scrypt** PoW meets the SHARE target but NOT the full block target --
102+
/// the worker->mint sharechain-accept branch (the mining_submit classify
103+
/// step "pow_hash <= share target -> record share in tracker"). main_dgb.cpp
104+
/// binds this to mint_local_share_with_ratchet (run_loop_mint.hpp, #294)
105+
/// -> create_local_share: the WorkSource hands out the found-share fields,
106+
/// the run-loop asks the AutoRatchet for the {mint, vote} version pair and
107+
/// inserts the share. Returns the minted share hash (NULL uint256 on
108+
/// failure). Parallel to SubmitBlockFn but for the share-difficulty (not
109+
/// block-difficulty) outcome -- the two non-reject classes of mining_submit.
110+
using MintShareFn =
111+
std::function<uint256(const MintShareInputs&)>;
112+
84113
DGBWorkSource(c2pool::dgb::HeaderChain& chain,
85114
dgb::coin::Mempool& mempool,
86115
bool is_testnet,
@@ -163,6 +192,22 @@ class DGBWorkSource : public core::stratum::IWorkSource
163192
/// is constructed.
164193
void set_best_share_hash_fn(std::function<uint256()> fn);
165194

195+
/// Wire the worker->mint sharechain-accept dispatch. Called once at startup
196+
/// from main_dgb.cpp, bound to mint_local_share_with_ratchet (#294) ->
197+
/// create_local_share. Empty until wired (mirrors set_best_share_hash_fn);
198+
/// while empty, try_mint_share() no-ops with a loud log rather than
199+
/// silently dropping an accepted share.
200+
void set_mint_share_fn(MintShareFn fn);
201+
202+
/// Dispatch one share-difficulty submission to the bound mint callback.
203+
/// The stage-4d mining_submit classify branch calls this on the
204+
/// "pow_hash <= share target" outcome. Returns the minted share hash, or a
205+
/// NULL uint256 when no mint callback is wired (logged, never crashes) or
206+
/// when the callback itself returns null. Thread-safe: copies the callback
207+
/// under lock before invoking so a concurrent set_mint_share_fn() cannot
208+
/// tear it out mid-call.
209+
uint256 try_mint_share(const MintShareInputs& in) const;
210+
166211
/// Embedded-path coinbasevalue for the template builder (Stage 4c).
167212
/// Routes through dgb::coin::resolve_coinbase_value: when the external
168213
/// digibyted GBT supplied a coinbasevalue it is returned verbatim (the
@@ -202,6 +247,12 @@ class DGBWorkSource : public core::stratum::IWorkSource
202247
mutable std::mutex best_share_mutex_;
203248
std::function<uint256()> best_share_hash_fn_;
204249

250+
// Worker->mint sharechain-accept callback (to mint_local_share_with_ratchet
251+
// -> create_local_share, wired in main_dgb.cpp). Empty until set; the
252+
// mining_submit classify branch no-ops the mint when unbound.
253+
mutable std::mutex mint_share_mutex_;
254+
MintShareFn mint_share_fn_;
255+
205256
// Template cache (filled lazily; invalidated when work_generation_ bumps)
206257
// Stage 4c populates these.
207258
mutable std::mutex template_mutex_;

src/impl/dgb/test/work_source_test.cpp

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,87 @@ TEST(DgbWorkSource, BestShareHashFnEmptyUntilWired)
112112
EXPECT_EQ(fn(), uint256::ZERO);
113113
}
114114

115+
// ── Worker->mint sharechain-accept seam (set_mint_share_fn / try_mint_share) ──
116+
// The producer half of the worker->mint run-loop standup: DGBWorkSource hands a
117+
// share-difficulty submission's found-share fields to a callback main_dgb.cpp
118+
// binds to mint_local_share_with_ratchet (#294) -> create_local_share. These
119+
// pin the seam contract before the stage-4d classify branch reaches it.
120+
121+
TEST(DgbWorkSource, MintShareFnEmptyUntilWiredReturnsNullNoSilentDrop)
122+
{
123+
Fixture fx;
124+
auto ws = fx.make();
125+
// Unbound: try_mint_share must NOT crash and must return a NULL hash
126+
// (the accepted share is logged, never silently dispatched into a null fn).
127+
dgb::stratum::DGBWorkSource::MintShareInputs in;
128+
in.subsidy = 500000000;
129+
EXPECT_EQ(ws->try_mint_share(in), uint256::ZERO);
130+
}
131+
132+
TEST(DgbWorkSource, MintShareFnForwardsInputsAndReturnsHash)
133+
{
134+
Fixture fx;
135+
auto ws = fx.make();
136+
137+
// Spy mint callback: capture the inputs (forward) and return a sentinel
138+
// hash (pass-through back to the classify branch).
139+
dgb::stratum::DGBWorkSource::MintShareInputs seen;
140+
bool called = false;
141+
uint256 sentinel; sentinel.SetHex(
142+
"00000000000000000000000000000000000000000000000000000000cafe5a7e");
143+
144+
ws->set_mint_share_fn(
145+
[&](const dgb::stratum::DGBWorkSource::MintShareInputs& got) -> uint256 {
146+
called = true;
147+
seen = got;
148+
return sentinel;
149+
});
150+
151+
dgb::stratum::DGBWorkSource::MintShareInputs in;
152+
in.header_bytes = std::vector<unsigned char>(80, 0xab);
153+
in.coinbase_bytes = {0x03, 0x01, 0x02, 0x03};
154+
in.subsidy = 0x1234567890ULL;
155+
in.prev_share.SetHex(
156+
"00000000000000000000000000000000000000000000000000000000000000aa");
157+
in.merkle_branches.push_back(in.prev_share);
158+
in.payout_script = {0x76, 0xa9};
159+
in.segwit_active = true;
160+
161+
uint256 minted = ws->try_mint_share(in);
162+
163+
EXPECT_TRUE(called);
164+
EXPECT_EQ(minted, sentinel); // minted hash flows back verbatim
165+
EXPECT_EQ(seen.header_bytes.size(), 80u); // inputs forwarded, not dropped
166+
EXPECT_EQ(seen.coinbase_bytes.size(), 4u);
167+
EXPECT_EQ(seen.subsidy, 0x1234567890ULL);
168+
EXPECT_EQ(seen.prev_share, in.prev_share);
169+
ASSERT_EQ(seen.merkle_branches.size(), 1u);
170+
EXPECT_EQ(seen.merkle_branches[0], in.prev_share);
171+
EXPECT_EQ(seen.payout_script.size(), 2u);
172+
EXPECT_TRUE(seen.segwit_active);
173+
}
174+
175+
// No behavior change this slice: the seam is stood up but the 4a mining_submit
176+
// stub still rejects every submission and must NOT reach the mint callback
177+
// (the classify branch that calls try_mint_share lands in stage 4d).
178+
TEST(DgbWorkSource, MiningSubmitStubDoesNotInvokeMintFnYet)
179+
{
180+
Fixture fx;
181+
auto ws = fx.make();
182+
bool mint_called = false;
183+
ws->set_mint_share_fn(
184+
[&](const dgb::stratum::DGBWorkSource::MintShareInputs&) -> uint256 {
185+
mint_called = true;
186+
return uint256{};
187+
});
188+
auto result = ws->mining_submit(
189+
"DGBaddr.worker1", "job-0", "en1", "en2", "ntime", "nonce", "rid-0",
190+
/*merged_addresses=*/{}, /*job=*/nullptr);
191+
ASSERT_TRUE(result.is_array());
192+
EXPECT_FALSE(result[0].get<bool>()); // 4a stub still rejects
193+
EXPECT_FALSE(mint_called); // seam wired but NOT yet reached
194+
}
195+
115196
TEST(DgbWorkSource, WorkerRegistryRoundTrip)
116197
{
117198
Fixture fx;

0 commit comments

Comments
 (0)