|
| 1 | +// SPDX-License-Identifier: AGPL-3.0-or-later |
| 2 | +/// |
| 3 | +/// KAT for dash::coin::select_dash_work — the embedded-vs-dashd work-source |
| 4 | +/// selector (S8 embedded_gbt live-wire capstone). Proves the routing contract |
| 5 | +/// and the RETAINED dashd fallback, without a live daemon or a populated |
| 6 | +/// MN/mempool harness (the embedded builder is injected as a stub; its |
| 7 | +/// oracle-parity output is already pinned by test_dash_embedded_gbt). |
| 8 | +/// |
| 9 | +/// Contract under test: |
| 10 | +/// 1. viable() bundle -> WorkSource::Embedded, embedded builder run, |
| 11 | +/// fallback NEVER touched. |
| 12 | +/// 2. has_state=false -> WorkSource::DashdFallback, fallback run. |
| 13 | +/// 3. viable but null mnstates -> fallback (defensive null-guard). |
| 14 | +/// 4. viable but null mempool -> fallback (defensive null-guard). |
| 15 | + |
| 16 | +#include <impl/dash/coin/work_source.hpp> |
| 17 | + |
| 18 | +#include <gtest/gtest.h> |
| 19 | + |
| 20 | +using dash::coin::EmbeddedWorkInputs; |
| 21 | +using dash::coin::WorkSource; |
| 22 | +using dash::coin::WorkSelection; |
| 23 | +using dash::coin::select_dash_work; |
| 24 | +using dash::coin::DashWorkData; |
| 25 | +using dash::coin::MnStateMachine; |
| 26 | +using dash::coin::Mempool; |
| 27 | + |
| 28 | +namespace { |
| 29 | + |
| 30 | +// Distinguishable sentinels so we can prove WHICH closure produced the result. |
| 31 | +constexpr uint32_t EMB_SENTINEL_HEIGHT = 0xE3BEDDEDu & 0xffffffu; // "embedded" |
| 32 | +constexpr uint32_t DASHD_SENTINEL_HEIGHT = 999'999u; |
| 33 | + |
| 34 | +DashWorkData embedded_stub(bool& ran) { |
| 35 | + ran = true; |
| 36 | + DashWorkData w; |
| 37 | + w.m_height = EMB_SENTINEL_HEIGHT; |
| 38 | + return w; |
| 39 | +} |
| 40 | + |
| 41 | +DashWorkData dashd_stub(bool& ran) { |
| 42 | + ran = true; |
| 43 | + DashWorkData w; |
| 44 | + w.m_height = DASHD_SENTINEL_HEIGHT; |
| 45 | + return w; |
| 46 | +} |
| 47 | + |
| 48 | +} // namespace |
| 49 | + |
| 50 | +// 1) Viable bundle routes to the EMBEDDED builder; fallback is not invoked. |
| 51 | +TEST(DashWorkSource, ViableRoutesEmbedded) |
| 52 | +{ |
| 53 | + MnStateMachine mn; |
| 54 | + Mempool mp; |
| 55 | + EmbeddedWorkInputs emb; |
| 56 | + emb.has_state = true; |
| 57 | + emb.mnstates = &mn; |
| 58 | + emb.mempool = ∓ |
| 59 | + ASSERT_TRUE(emb.viable()); |
| 60 | + |
| 61 | + bool emb_ran = false, fb_ran = false; |
| 62 | + WorkSelection sel = select_dash_work( |
| 63 | + emb, |
| 64 | + [&] { return embedded_stub(emb_ran); }, |
| 65 | + [&] { return dashd_stub(fb_ran); }); |
| 66 | + |
| 67 | + EXPECT_EQ(sel.source, WorkSource::Embedded); |
| 68 | + EXPECT_TRUE(emb_ran); |
| 69 | + EXPECT_FALSE(fb_ran); |
| 70 | + EXPECT_EQ(sel.work.m_height, EMB_SENTINEL_HEIGHT); |
| 71 | +} |
| 72 | + |
| 73 | +// 2) No embedded state -> the RETAINED dashd getblocktemplate fallback runs. |
| 74 | +TEST(DashWorkSource, NoStateRoutesDashdFallback) |
| 75 | +{ |
| 76 | + EmbeddedWorkInputs emb; // has_state defaults false |
| 77 | + ASSERT_FALSE(emb.viable()); |
| 78 | + |
| 79 | + bool emb_ran = false, fb_ran = false; |
| 80 | + WorkSelection sel = select_dash_work( |
| 81 | + emb, |
| 82 | + [&] { return embedded_stub(emb_ran); }, |
| 83 | + [&] { return dashd_stub(fb_ran); }); |
| 84 | + |
| 85 | + EXPECT_EQ(sel.source, WorkSource::DashdFallback); |
| 86 | + EXPECT_FALSE(emb_ran); |
| 87 | + EXPECT_TRUE(fb_ran); |
| 88 | + EXPECT_EQ(sel.work.m_height, DASHD_SENTINEL_HEIGHT); |
| 89 | +} |
| 90 | + |
| 91 | +// 3) has_state true but mnstates null -> not viable -> fallback (null-guard). |
| 92 | +TEST(DashWorkSource, NullMnStatesRoutesFallback) |
| 93 | +{ |
| 94 | + Mempool mp; |
| 95 | + EmbeddedWorkInputs emb; |
| 96 | + emb.has_state = true; |
| 97 | + emb.mnstates = nullptr; |
| 98 | + emb.mempool = ∓ |
| 99 | + EXPECT_FALSE(emb.viable()); |
| 100 | + |
| 101 | + bool emb_ran = false, fb_ran = false; |
| 102 | + WorkSelection sel = select_dash_work( |
| 103 | + emb, |
| 104 | + [&] { return embedded_stub(emb_ran); }, |
| 105 | + [&] { return dashd_stub(fb_ran); }); |
| 106 | + |
| 107 | + EXPECT_EQ(sel.source, WorkSource::DashdFallback); |
| 108 | + EXPECT_FALSE(emb_ran); |
| 109 | + EXPECT_TRUE(fb_ran); |
| 110 | +} |
| 111 | + |
| 112 | +// 4) has_state true but mempool null -> not viable -> fallback (null-guard). |
| 113 | +TEST(DashWorkSource, NullMempoolRoutesFallback) |
| 114 | +{ |
| 115 | + MnStateMachine mn; |
| 116 | + EmbeddedWorkInputs emb; |
| 117 | + emb.has_state = true; |
| 118 | + emb.mnstates = &mn; |
| 119 | + emb.mempool = nullptr; |
| 120 | + EXPECT_FALSE(emb.viable()); |
| 121 | + |
| 122 | + bool emb_ran = false, fb_ran = false; |
| 123 | + WorkSelection sel = select_dash_work( |
| 124 | + emb, |
| 125 | + [&] { return embedded_stub(emb_ran); }, |
| 126 | + [&] { return dashd_stub(fb_ran); }); |
| 127 | + |
| 128 | + EXPECT_EQ(sel.source, WorkSource::DashdFallback); |
| 129 | + EXPECT_FALSE(emb_ran); |
| 130 | + EXPECT_TRUE(fb_ran); |
| 131 | +} |
0 commit comments