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
22 changes: 22 additions & 0 deletions src/impl/dgb/stratum/work_source.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -333,4 +333,26 @@ void DGBWorkSource::set_best_share_hash_fn(std::function<uint256()> fn)
best_share_hash_fn_ = std::move(fn);
}

void DGBWorkSource::set_mint_share_fn(MintShareFn fn)
{
std::lock_guard<std::mutex> lk(mint_share_mutex_);
mint_share_fn_ = std::move(fn);
}

uint256 DGBWorkSource::try_mint_share(const MintShareInputs& in) const
{
MintShareFn fn;
{
std::lock_guard<std::mutex> lk(mint_share_mutex_);
fn = mint_share_fn_; // copy so a concurrent set cannot tear it out mid-call
}
if (!fn) {
LOG_WARNING << "[DGB-STRATUM] share met share-target but no mint callback "
"wired -- share NOT recorded (set_mint_share_fn unbound). "
"No silent drop: logged.";
return uint256{};
}
return fn(in);
}

} // namespace dgb::stratum
51 changes: 51 additions & 0 deletions src/impl/dgb/stratum/work_source.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,35 @@ class DGBWorkSource : public core::stratum::IWorkSource
using SubmitBlockFn = std::function<bool(const std::vector<unsigned char>& block_bytes,
uint32_t height)>;

/// Inputs the worker->mint sharechain-accept path hands to the run-loop:
/// the assembled fields of a submission whose **Scrypt** PoW met the SHARE
/// target (but NOT the full block target). Raw-bytes form (header +
/// coinbase) keeps DGBWorkSource decoupled from the share/BlockType
/// serialization details, exactly like SubmitBlockFn -- main_dgb.cpp parses
/// these into create_local_share()'s arguments.
struct MintShareInputs {
std::vector<unsigned char> header_bytes; ///< 80-byte reconstructed block header
std::vector<unsigned char> coinbase_bytes; ///< p2pool coinbase scriptSig (BIP34 height + marker)
uint64_t subsidy = 0; ///< coinbasevalue (block reward)
uint256 prev_share; ///< current sharechain tip = the new share's parent
std::vector<uint256> merkle_branches; ///< coinbase txid -> merkle root branches
std::vector<unsigned char> payout_script; ///< finder's scriptPubKey
bool segwit_active = false;
};

/// Callback invoked when `mining_submit` validates a submission whose
/// **Scrypt** PoW meets the SHARE target but NOT the full block target --
/// the worker->mint sharechain-accept branch (the mining_submit classify
/// step "pow_hash <= share target -> record share in tracker"). main_dgb.cpp
/// binds this to mint_local_share_with_ratchet (run_loop_mint.hpp, #294)
/// -> create_local_share: the WorkSource hands out the found-share fields,
/// the run-loop asks the AutoRatchet for the {mint, vote} version pair and
/// inserts the share. Returns the minted share hash (NULL uint256 on
/// failure). Parallel to SubmitBlockFn but for the share-difficulty (not
/// block-difficulty) outcome -- the two non-reject classes of mining_submit.
using MintShareFn =
std::function<uint256(const MintShareInputs&)>;

DGBWorkSource(c2pool::dgb::HeaderChain& chain,
dgb::coin::Mempool& mempool,
bool is_testnet,
Expand Down Expand Up @@ -163,6 +192,22 @@ class DGBWorkSource : public core::stratum::IWorkSource
/// is constructed.
void set_best_share_hash_fn(std::function<uint256()> fn);

/// Wire the worker->mint sharechain-accept dispatch. Called once at startup
/// from main_dgb.cpp, bound to mint_local_share_with_ratchet (#294) ->
/// create_local_share. Empty until wired (mirrors set_best_share_hash_fn);
/// while empty, try_mint_share() no-ops with a loud log rather than
/// silently dropping an accepted share.
void set_mint_share_fn(MintShareFn fn);

/// Dispatch one share-difficulty submission to the bound mint callback.
/// The stage-4d mining_submit classify branch calls this on the
/// "pow_hash <= share target" outcome. Returns the minted share hash, or a
/// NULL uint256 when no mint callback is wired (logged, never crashes) or
/// when the callback itself returns null. Thread-safe: copies the callback
/// under lock before invoking so a concurrent set_mint_share_fn() cannot
/// tear it out mid-call.
uint256 try_mint_share(const MintShareInputs& in) const;

/// Embedded-path coinbasevalue for the template builder (Stage 4c).
/// Routes through dgb::coin::resolve_coinbase_value: when the external
/// digibyted GBT supplied a coinbasevalue it is returned verbatim (the
Expand Down Expand Up @@ -202,6 +247,12 @@ class DGBWorkSource : public core::stratum::IWorkSource
mutable std::mutex best_share_mutex_;
std::function<uint256()> best_share_hash_fn_;

// Worker->mint sharechain-accept callback (to mint_local_share_with_ratchet
// -> create_local_share, wired in main_dgb.cpp). Empty until set; the
// mining_submit classify branch no-ops the mint when unbound.
mutable std::mutex mint_share_mutex_;
MintShareFn mint_share_fn_;

// Template cache (filled lazily; invalidated when work_generation_ bumps)
// Stage 4c populates these.
mutable std::mutex template_mutex_;
Expand Down
81 changes: 81 additions & 0 deletions src/impl/dgb/test/work_source_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,87 @@ TEST(DgbWorkSource, BestShareHashFnEmptyUntilWired)
EXPECT_EQ(fn(), uint256::ZERO);
}

// ── Worker->mint sharechain-accept seam (set_mint_share_fn / try_mint_share) ──
// The producer half of the worker->mint run-loop standup: DGBWorkSource hands a
// share-difficulty submission's found-share fields to a callback main_dgb.cpp
// binds to mint_local_share_with_ratchet (#294) -> create_local_share. These
// pin the seam contract before the stage-4d classify branch reaches it.

TEST(DgbWorkSource, MintShareFnEmptyUntilWiredReturnsNullNoSilentDrop)
{
Fixture fx;
auto ws = fx.make();
// Unbound: try_mint_share must NOT crash and must return a NULL hash
// (the accepted share is logged, never silently dispatched into a null fn).
dgb::stratum::DGBWorkSource::MintShareInputs in;
in.subsidy = 500000000;
EXPECT_EQ(ws->try_mint_share(in), uint256::ZERO);
}

TEST(DgbWorkSource, MintShareFnForwardsInputsAndReturnsHash)
{
Fixture fx;
auto ws = fx.make();

// Spy mint callback: capture the inputs (forward) and return a sentinel
// hash (pass-through back to the classify branch).
dgb::stratum::DGBWorkSource::MintShareInputs seen;
bool called = false;
uint256 sentinel; sentinel.SetHex(
"00000000000000000000000000000000000000000000000000000000cafe5a7e");

ws->set_mint_share_fn(
[&](const dgb::stratum::DGBWorkSource::MintShareInputs& got) -> uint256 {
called = true;
seen = got;
return sentinel;
});

dgb::stratum::DGBWorkSource::MintShareInputs in;
in.header_bytes = std::vector<unsigned char>(80, 0xab);
in.coinbase_bytes = {0x03, 0x01, 0x02, 0x03};
in.subsidy = 0x1234567890ULL;
in.prev_share.SetHex(
"00000000000000000000000000000000000000000000000000000000000000aa");
in.merkle_branches.push_back(in.prev_share);
in.payout_script = {0x76, 0xa9};
in.segwit_active = true;

uint256 minted = ws->try_mint_share(in);

EXPECT_TRUE(called);
EXPECT_EQ(minted, sentinel); // minted hash flows back verbatim
EXPECT_EQ(seen.header_bytes.size(), 80u); // inputs forwarded, not dropped
EXPECT_EQ(seen.coinbase_bytes.size(), 4u);
EXPECT_EQ(seen.subsidy, 0x1234567890ULL);
EXPECT_EQ(seen.prev_share, in.prev_share);
ASSERT_EQ(seen.merkle_branches.size(), 1u);
EXPECT_EQ(seen.merkle_branches[0], in.prev_share);
EXPECT_EQ(seen.payout_script.size(), 2u);
EXPECT_TRUE(seen.segwit_active);
}

// No behavior change this slice: the seam is stood up but the 4a mining_submit
// stub still rejects every submission and must NOT reach the mint callback
// (the classify branch that calls try_mint_share lands in stage 4d).
TEST(DgbWorkSource, MiningSubmitStubDoesNotInvokeMintFnYet)
{
Fixture fx;
auto ws = fx.make();
bool mint_called = false;
ws->set_mint_share_fn(
[&](const dgb::stratum::DGBWorkSource::MintShareInputs&) -> uint256 {
mint_called = true;
return uint256{};
});
auto result = ws->mining_submit(
"DGBaddr.worker1", "job-0", "en1", "en2", "ntime", "nonce", "rid-0",
/*merged_addresses=*/{}, /*job=*/nullptr);
ASSERT_TRUE(result.is_array());
EXPECT_FALSE(result[0].get<bool>()); // 4a stub still rejects
EXPECT_FALSE(mint_called); // seam wired but NOT yet reached
}

TEST(DgbWorkSource, WorkerRegistryRoundTrip)
{
Fixture fx;
Expand Down
Loading