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
18 changes: 15 additions & 3 deletions src/impl/dgb/coin/template_builder.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,10 @@ class CoinNodeInterface {
// NON-CONSENSUS: this function only SHAPES values; it never derives or alters
// the consensus-bearing coinbasevalue. That value is computed by the caller
// through the #207 resolve_coinbase_value -> subsidy_func SSOT and passed in
// verbatim. The builder fabricates nothing: transactions[] stays empty
// (embedded mempool tx selection is not wired, fees stay 0), previousblockhash
// verbatim. The builder fabricates nothing: transactions[] is a caller-supplied
// pass-through (empty by default until a mempool tx source is wired -- truthful
// absence, never a fabricated tx; the fee total is folded into coinbasevalue
// UPSTREAM via resolve_coinbase_value, not here), previousblockhash
// is emitted ONLY when the caller supplies a real tip hash (truthful absence,
// never a fabricated id), and `bits` is held back entirely -- DGB Core's live
// next-target is MultiShield V4 (a global 5-algo window == V37), so a
Expand All @@ -96,6 +98,16 @@ struct WorkTemplateInputs {
// caller (work source: u256_be_display_hex). nullopt -> previousblockhash
// omitted from the template.
std::optional<std::string> previousblockhash;
// GBT transactions[] array, already shaped by the caller (per-tx
// {data,txid,hash,fee} objects, the same shape btc's template_builder
// emits). The builder passes it through VERBATIM and shapes nothing -- the
// identical truthful-absence discipline as previousblockhash: it defaults
// to an empty array, so a caller that has wired no transaction source (the
// current embedded + stratum paths) emits an empty transactions[] and
// fabricates nothing. The fee total those txs carry is folded into
// coinbasevalue UPSTREAM via resolve_coinbase_value(total_fees) (#207 SSOT);
// the builder never derives the reward, so this field is display-only shape.
nlohmann::json transactions = nlohmann::json::array();
};

inline nlohmann::json build_work_template(const WorkTemplateInputs& in)
Expand All @@ -122,7 +134,7 @@ inline nlohmann::json build_work_template(const WorkTemplateInputs& in)
tmpl["version"] = version;
tmpl["curtime"] = in.curtime;
tmpl["mintime"] = mintime;
tmpl["transactions"] = nlohmann::json::array();
tmpl["transactions"] = in.transactions; // caller-supplied; empty by default

// previousblockhash: truthful conditional emit (see struct notes).
if (in.previousblockhash)
Expand Down
31 changes: 30 additions & 1 deletion src/impl/dgb/test/template_builder_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,8 @@ TEST(DgbTemplateBuilder, EmptyChainMintimeIsZero)
EXPECT_EQ(t.at("mintime").get<int64_t>(), 0);
}

// transactions[] is always an empty array (embedded tx selection unwired);
// transactions[] is an empty array BY DEFAULT (no tx source wired into the
// inputs); bits is NEVER emitted (MultiShield V4 next-target is V37 -- the
// bits is NEVER emitted (MultiShield V4 next-target is V37 — a Scrypt-only
// walk would be a known-wrong difficulty).
TEST(DgbTemplateBuilder, EmptyTransactionsAndNoBits)
Expand All @@ -112,6 +113,34 @@ TEST(DgbTemplateBuilder, PreviousblockhashConditionalEmit)
EXPECT_FALSE(without.contains("previousblockhash"));
}

// transactions[] is a caller-supplied pass-through: a shaped array round-trips
// into the template VERBATIM (the seam a wired mempool source emits through),
// while an unset input stays an empty array (truthful absence by default). This
// is the same conditional-shape contract previousblockhash holds.
TEST(DgbTemplateBuilder, TransactionsPassThroughVerbatim)
{
auto in = make_inputs();
nlohmann::json tx = nlohmann::json::object();
tx["data"] = "0100000001abcd";
tx["txid"] = std::string(64, '1');
tx["hash"] = std::string(64, '1');
tx["fee"] = 4200;
in.transactions = nlohmann::json::array();
in.transactions.push_back(tx);

auto t = build_work_template(in);
ASSERT_TRUE(t.at("transactions").is_array());
ASSERT_EQ(t.at("transactions").size(), 1u);
// byte-identical pass-through: the builder shapes nothing of its own.
EXPECT_EQ(t.at("transactions").dump(), in.transactions.dump());
EXPECT_EQ(t.at("transactions")[0].at("fee").get<int64_t>(), 4200);

// Unset -> empty array (default): never absent, never fabricated.
auto def = build_work_template(make_inputs());
ASSERT_TRUE(def.at("transactions").is_array());
EXPECT_TRUE(def.at("transactions").empty());
}

// Divergence guard: identical inputs -> byte-identical template. This is the
// whole point of the SSOT — the work source and embedded path cannot diverge.
TEST(DgbTemplateBuilder, DeterministicForIdenticalInputs)
Expand Down
Loading