|
25 | 25 | #include <impl/dgb/coin/embedded_tx_select.hpp> |
26 | 26 | #include <impl/dgb/coin/mempool.hpp> |
27 | 27 | #include <impl/dgb/coin/transaction.hpp> |
| 28 | +#include <impl/dgb/coin/template_builder.hpp> |
| 29 | +#include <impl/dgb/coin/embedded_coinbase_value.hpp> |
28 | 30 |
|
29 | 31 | #include <core/pack.hpp> |
30 | 32 | #include <core/hash.hpp> |
| 33 | +#include <core/pow.hpp> |
31 | 34 | #include <btclibs/util/strencodings.h> |
32 | 35 |
|
33 | 36 | using dgb::coin::Mempool; |
@@ -219,3 +222,70 @@ TEST(DgbEmbeddedTxSelect, WeightCapSkipsHeavyButPacksLighter) |
219 | 222 | EXPECT_EQ(sel.transactions[0]["txid"], compute_txid(light).GetHex()); |
220 | 223 | EXPECT_EQ(sel.total_fees, 100u); |
221 | 224 | } |
| 225 | + |
| 226 | +// --------------------------------------------------------------------------- |
| 227 | +// END-TO-END ASSEMBLY CONFORMANCE: the full embedded path the production |
| 228 | +// EmbeddedCoinNode wires -- Mempool -> make_mempool_tx_source() selection -> |
| 229 | +// WorkTemplateInputs.transactions + (subsidy + total_fees) coinbasevalue -> |
| 230 | +// build_work_template(). The isolated shaper tests above and the isolated |
| 231 | +// build_work_template pass-through test (dgb_template_builder_test) each pin |
| 232 | +// HALF; nothing pins that the two seams COMPOSE faithfully -- that the shaper's |
| 233 | +// fee-sorted entries survive into tmpl["transactions"] byte-for-byte and IN |
| 234 | +// ORDER, and that the SAME selection's total_fees folds into |
| 235 | +// tmpl["coinbasevalue"] through the #188/#207 embedded_coinbase_value SSOT. |
| 236 | +// This is the seam where a reorder/drop/double-count would silently desync the |
| 237 | +// embedded template from the frstrtr/p2pool-dgb-scrypt GBT consumer. |
| 238 | +// --------------------------------------------------------------------------- |
| 239 | +TEST(DgbEmbeddedTxSelect, EndToEndTemplateAssemblyConformance) |
| 240 | +{ |
| 241 | + Mempool pool; |
| 242 | + MutableTransaction lo = tagged_tx(11, 0); // lowest feerate |
| 243 | + MutableTransaction mid = tagged_tx(22, 1); |
| 244 | + MutableTransaction hi = tagged_tx(33, 2); // highest feerate |
| 245 | + ASSERT_TRUE(pool.add_tx(lo)); |
| 246 | + ASSERT_TRUE(pool.add_tx(mid)); |
| 247 | + ASSERT_TRUE(pool.add_tx(hi)); |
| 248 | + pool.set_tx_fee(compute_txid(lo), 250); |
| 249 | + pool.set_tx_fee(compute_txid(mid), 500); |
| 250 | + pool.set_tx_fee(compute_txid(hi), 900); |
| 251 | + |
| 252 | + // 1) production shaper selects + shapes the fee-sorted transactions[]. |
| 253 | + auto source = make_mempool_tx_source(pool, /*max_weight=*/4'000'000); |
| 254 | + const auto sel = source(); |
| 255 | + ASSERT_EQ(sel.transactions.size(), 3u); |
| 256 | + EXPECT_EQ(sel.total_fees, 1650u); // 250 + 500 + 900 |
| 257 | + |
| 258 | + // 2) coinbasevalue folds total_fees through the embedded SSOT (#188) -- the |
| 259 | + // SAME definition the external-daemon GBT path resolves against. |
| 260 | + const uint32_t height = 1'000'000; |
| 261 | + const uint64_t subsidy = 64'200'000'000ull; // fixed subsidy for the KAT |
| 262 | + core::SubsidyFunc subsidy_func = [&](uint32_t) -> uint64_t { return subsidy; }; |
| 263 | + const uint64_t coinbasevalue = |
| 264 | + dgb::coin::embedded_coinbase_value(subsidy_func, height, sel.total_fees); |
| 265 | + EXPECT_EQ(coinbasevalue, subsidy + 1650ull); |
| 266 | + |
| 267 | + // 3) assemble the work template from that one selection. |
| 268 | + dgb::coin::WorkTemplateInputs in; |
| 269 | + in.next_height = height; |
| 270 | + in.coinbasevalue = coinbasevalue; |
| 271 | + in.curtime = 1'700'000'000; |
| 272 | + in.transactions = sel.transactions; |
| 273 | + const nlohmann::json tmpl = dgb::coin::build_work_template(in); |
| 274 | + |
| 275 | + // transactions[] survive the seam byte-for-byte AND in feerate-desc order. |
| 276 | + ASSERT_TRUE(tmpl["transactions"].is_array()); |
| 277 | + EXPECT_EQ(tmpl["transactions"], sel.transactions); |
| 278 | + ASSERT_EQ(tmpl["transactions"].size(), 3u); |
| 279 | + EXPECT_EQ(tmpl["transactions"][0]["txid"], compute_txid(hi).GetHex()); |
| 280 | + EXPECT_EQ(tmpl["transactions"][1]["txid"], compute_txid(mid).GetHex()); |
| 281 | + EXPECT_EQ(tmpl["transactions"][2]["txid"], compute_txid(lo).GetHex()); |
| 282 | + // every entry keeps the full p2pool GBT {data,txid,hash,fee} shape. |
| 283 | + for (const auto& e : tmpl["transactions"]) { |
| 284 | + EXPECT_TRUE(e.contains("data")); |
| 285 | + EXPECT_TRUE(e.contains("txid")); |
| 286 | + EXPECT_TRUE(e.contains("hash")); |
| 287 | + EXPECT_TRUE(e.contains("fee")); |
| 288 | + } |
| 289 | + // coinbasevalue carries the folded fee total verbatim (no double count). |
| 290 | + EXPECT_EQ(tmpl["coinbasevalue"].get<uint64_t>(), subsidy + 1650ull); |
| 291 | +} |
0 commit comments