Skip to content

Commit fc2ec32

Browse files
committed
dgb: M3 softfork-detection standalone regression guard
Pin collect_softfork_names() (softfork_check.hpp) across all three getblockchaininfo softfork encodings (array-of-objects / array-of-strings / object-keyed) plus accumulation + scalar no-op robustness. Guards the softfork-gate parser NodeRPC uses to confirm segwit before trusting the Scrypt GBT path, BEFORE the deferred post-#145 wiring CI-links rpc.cpp. Header-only + nlohmann + gtest, no transport link -- standalone, does not enter the dgb OBJECT lib.
1 parent 00c39eb commit fc2ec32

1 file changed

Lines changed: 108 additions & 0 deletions

File tree

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
// ---------------------------------------------------------------------------
2+
// dgb M3 softfork-detection regression guard.
3+
//
4+
// Pins collect_softfork_names() (softfork_check.hpp), the parser NodeRPC uses
5+
// to decide whether "segwit" (and friends) are active on the external
6+
// digibyted BEFORE c2pool-dgb trusts the Scrypt getblocktemplate path. The
7+
// parser must tolerate all three getblockchaininfo["softforks"]/["bip9_softforks"]
8+
// encodings shipped by different DigiByte Core versions:
9+
//
10+
// 1. array of objects: [{"id":"segwit",...}, ...] (modern digibyted)
11+
// 2. array of strings: ["segwit","taproot", ...] (compact form)
12+
// 3. object with keys: {"segwit":{...}, ...} (BIP9 style)
13+
//
14+
// Header-only + nlohmann + gtest -- no boost::beast/jsonrpccxx transport, so it
15+
// builds standalone without entering the dgb OBJECT lib (same gate-safe shape
16+
// as rpc_request_test.cpp).
17+
// ---------------------------------------------------------------------------
18+
19+
#include <gtest/gtest.h>
20+
21+
#include <impl/dgb/coin/softfork_check.hpp>
22+
23+
#include <set>
24+
#include <string>
25+
26+
using dgb::coin::collect_softfork_names;
27+
using json = nlohmann::json;
28+
29+
// --- Format 1: array of objects (modern digibyted) --------------------------
30+
31+
TEST(DgbSoftforkCheck, ArrayOfObjectsCollectsIds)
32+
{
33+
auto v = json::parse(R"([{"id":"segwit","active":true},{"id":"taproot"}])");
34+
std::set<std::string> out;
35+
collect_softfork_names(v, out);
36+
EXPECT_EQ(out, (std::set<std::string>{"segwit", "taproot"}));
37+
}
38+
39+
TEST(DgbSoftforkCheck, ArrayObjectMissingOrNonStringIdSkipped)
40+
{
41+
// An object without a string "id" must be ignored, not crash the parse.
42+
auto v = json::parse(R"([{"id":"segwit"},{"name":"noid"},{"id":42}])");
43+
std::set<std::string> out;
44+
collect_softfork_names(v, out);
45+
EXPECT_EQ(out, (std::set<std::string>{"segwit"}));
46+
}
47+
48+
// --- Format 2: array of strings (compact form) ------------------------------
49+
50+
TEST(DgbSoftforkCheck, ArrayOfStringsCollectsValues)
51+
{
52+
auto v = json::parse(R"(["segwit","taproot"])");
53+
std::set<std::string> out;
54+
collect_softfork_names(v, out);
55+
EXPECT_EQ(out, (std::set<std::string>{"segwit", "taproot"}));
56+
}
57+
58+
// --- Format 3: object keyed by softfork name (BIP9 style) -------------------
59+
60+
TEST(DgbSoftforkCheck, ObjectCollectsKeys)
61+
{
62+
auto v = json::parse(R"({"segwit":{"status":"active"},"taproot":{"status":"defined"}})");
63+
std::set<std::string> out;
64+
collect_softfork_names(v, out);
65+
EXPECT_EQ(out, (std::set<std::string>{"segwit", "taproot"}));
66+
}
67+
68+
// --- The contract that actually matters: segwit is detectable in every form -
69+
70+
TEST(DgbSoftforkCheck, SegwitDetectedAcrossAllThreeEncodings)
71+
{
72+
for (const auto& src : {R"([{"id":"segwit"}])", R"(["segwit"])", R"({"segwit":{}})"})
73+
{
74+
std::set<std::string> out;
75+
collect_softfork_names(json::parse(src), out);
76+
EXPECT_TRUE(out.count("segwit")) << "segwit not found in: " << src;
77+
}
78+
}
79+
80+
// --- Accumulation + robustness ---------------------------------------------
81+
82+
TEST(DgbSoftforkCheck, AccumulatesIntoExistingSet)
83+
{
84+
// Caller merges the "softforks" and "bip9_softforks" fields into one set;
85+
// collect_softfork_names must append, never clear.
86+
std::set<std::string> out{"preexisting"};
87+
collect_softfork_names(json::parse(R"(["segwit"])"), out);
88+
EXPECT_EQ(out, (std::set<std::string>{"preexisting", "segwit"}));
89+
}
90+
91+
TEST(DgbSoftforkCheck, EmptyContainersYieldNothing)
92+
{
93+
std::set<std::string> out;
94+
collect_softfork_names(json::parse("[]"), out);
95+
collect_softfork_names(json::parse("{}"), out);
96+
EXPECT_TRUE(out.empty());
97+
}
98+
99+
TEST(DgbSoftforkCheck, ScalarValueIsNoOp)
100+
{
101+
// A missing field deserialized as null/number/string must be a safe no-op
102+
// (the field may be absent on very old daemons).
103+
std::set<std::string> out;
104+
collect_softfork_names(json(nullptr), out);
105+
collect_softfork_names(json(7), out);
106+
collect_softfork_names(json("segwit"), out); // bare scalar, not a container
107+
EXPECT_TRUE(out.empty());
108+
}

0 commit comments

Comments
 (0)