Line data Source code
1 : // Copyright (c) 2025 The Dash Core developers
2 : // Distributed under the MIT software license, see the accompanying
3 : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
4 :
5 : #include <rpc/evo_util.h>
6 :
7 : #include <evo/deterministicmns.h>
8 : #include <evo/providertx.h>
9 : #include <index/txindex.h>
10 : #include <rpc/protocol.h>
11 : #include <rpc/request.h>
12 :
13 : #include <univalue.h>
14 :
15 : namespace {
16 0 : bool IsNumeric(std::string_view input) { return input.find_first_not_of("0123456789") == std::string::npos; }
17 :
18 : template <typename ProTx>
19 0 : void ParseInput(ProTx& ptx, std::string_view field_name, const std::string& input_str, NetInfoPurpose purpose,
20 : size_t idx, bool optional)
21 : {
22 0 : if (input_str.empty()) {
23 0 : if (!optional) {
24 0 : throw JSONRPCError(RPC_INVALID_PARAMETER,
25 0 : strprintf("Invalid param for %s[%zu], cannot be empty", field_name, idx));
26 : }
27 0 : return; // Nothing to do
28 : }
29 0 : if (auto ret = ptx.netInfo->AddEntry(purpose, input_str); ret != NetInfoStatus::Success) {
30 0 : throw JSONRPCError(RPC_INVALID_PARAMETER,
31 0 : strprintf("Error setting %s[%zu] to '%s' (%s)", field_name, idx, input_str, NISToString(ret)));
32 : }
33 0 : }
34 : } // anonymous namespace
35 :
36 : template <typename ProTx>
37 0 : void ProcessNetInfoCore(ProTx& ptx, const UniValue& input, const bool optional)
38 : {
39 0 : CHECK_NONFATAL(ptx.netInfo);
40 :
41 0 : if (input.isStr()) {
42 0 : ParseInput(ptx, /*field_name=*/"coreP2PAddrs", input.get_str(), NetInfoPurpose::CORE_P2P, /*idx=*/0, optional);
43 0 : } else if (input.isArray()) {
44 0 : const UniValue& entries = input.get_array();
45 0 : if (!optional && entries.empty()) {
46 0 : throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid param for coreP2PAddrs, cannot be empty");
47 : }
48 0 : for (size_t idx{0}; idx < entries.size(); idx++) {
49 0 : const UniValue& entry_uv{entries[idx]};
50 0 : if (!entry_uv.isStr()) {
51 0 : throw JSONRPCError(RPC_INVALID_PARAMETER,
52 0 : strprintf("Invalid param for coreP2PAddrs[%zu], must be string", idx));
53 : }
54 0 : ParseInput(ptx, /*field_name=*/"coreP2PAddrs", entry_uv.get_str(), NetInfoPurpose::CORE_P2P, idx,
55 : /*optional=*/false);
56 0 : }
57 0 : } else {
58 0 : throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid param for coreP2PAddrs, must be string or array");
59 : }
60 0 : }
61 : template void ProcessNetInfoCore(CProRegTx& ptx, const UniValue& input, const bool optional);
62 : template void ProcessNetInfoCore(CProUpServTx& ptx, const UniValue& input, const bool optional);
63 :
64 : template <typename ProTx>
65 0 : void ProcessNetInfoPlatform(ProTx& ptx, const UniValue& input_p2p, const UniValue& input_http, const bool optional)
66 : {
67 0 : CHECK_NONFATAL(ptx.netInfo);
68 :
69 0 : auto process_field = [&](uint16_t& maybe_target, const UniValue& input, const NetInfoPurpose purpose,
70 : std::string_view field_name) {
71 0 : if (!input.isArray() && !input.isNum() && !input.isStr()) {
72 0 : throw JSONRPCError(RPC_INVALID_PARAMETER,
73 0 : strprintf("Invalid param for %s, must be array, number or string", field_name));
74 : }
75 :
76 0 : bool is_empty{input.isArray() ? input.get_array().empty() : input.getValStr().empty()};
77 0 : bool is_nonnumeric_str{input.isStr() && !IsNumeric(input.getValStr())};
78 0 : if (is_empty || is_nonnumeric_str || input.isArray()) {
79 0 : if (is_empty) {
80 0 : if (!optional) {
81 : // Mandatory field, cannot specify blank value
82 0 : throw JSONRPCError(RPC_INVALID_PARAMETER,
83 0 : strprintf("Invalid param for %s, cannot be empty", field_name));
84 : }
85 0 : if (!ptx.netInfo->IsEmpty()) {
86 : // Blank values are tolerable so long as no other field has been populated.
87 0 : throw JSONRPCError(RPC_INVALID_PARAMETER,
88 0 : strprintf("Invalid param for %s, cannot be empty if other fields populated",
89 : field_name));
90 : }
91 0 : }
92 0 : if (!ptx.netInfo->CanStorePlatform()) {
93 : // Arrays: Expected to be address strings, if relying on platform{HTTP,P2P}Port, bail out.
94 : // Empty Input: We can tolerate blank values if netInfo can store platform fields, if it cannot, we are relying
95 : // on platform{HTTP,P2P}Port, where it is mandatory even if their netInfo counterpart is optional.
96 : // String: If not parsable as port and relying on platform{HTTP,P2P}Port, bail out.
97 0 : throw JSONRPCError(RPC_INVALID_PARAMETER,
98 0 : strprintf("Invalid param for %s, ProTx version only supports ports", field_name));
99 : }
100 0 : if (input.isArray()) {
101 0 : const UniValue& entries = input.get_array();
102 0 : for (size_t idx{0}; idx < entries.size(); idx++) {
103 0 : const UniValue& entry{entries[idx]};
104 0 : if (!entry.isStr() || IsNumeric(entry.get_str())) {
105 0 : throw JSONRPCError(RPC_INVALID_PARAMETER,
106 0 : strprintf("Invalid param for %s[%zu], must be string", field_name, idx));
107 : }
108 0 : ParseInput(ptx, field_name, entry.get_str(), purpose, idx, /*optional=*/false);
109 0 : }
110 0 : } else {
111 0 : CHECK_NONFATAL(is_empty || is_nonnumeric_str);
112 0 : ParseInput(ptx, field_name, input.get_str(), purpose, /*idx=*/0, /*optional=*/true);
113 : }
114 0 : } else {
115 0 : if (int32_t port{0};
116 0 : ParseInt32(input.getValStr(), &port) && port >= 1 && port <= std::numeric_limits<uint16_t>::max()) {
117 : // Valid port
118 0 : if (!ptx.netInfo->CanStorePlatform()) {
119 0 : maybe_target = static_cast<uint16_t>(port);
120 0 : return; // Parsing complete
121 : }
122 : // We cannot store *only* a port number in netInfo so we need to associate it with the primary service of CORE_P2P manually
123 0 : if (!ptx.netInfo->HasEntries(NetInfoPurpose::CORE_P2P)) {
124 0 : throw JSONRPCError(RPC_INVALID_PARAMETER,
125 0 : strprintf("Cannot set param for %s, must specify coreP2PAddrs first", field_name));
126 : }
127 0 : const CService service{CNetAddr{ptx.netInfo->GetPrimary()}, static_cast<uint16_t>(port)};
128 0 : CHECK_NONFATAL(service.IsValid());
129 0 : ParseInput(ptx, field_name, service.ToStringAddrPort(), purpose, /*idx=*/0, /*optional=*/false);
130 0 : } else {
131 0 : throw JSONRPCError(RPC_INVALID_PARAMETER,
132 0 : strprintf("Invalid param for %s, must be a valid port [1-65535]", field_name));
133 : }
134 : }
135 0 : };
136 0 : process_field(ptx.platformP2PPort, input_p2p, NetInfoPurpose::PLATFORM_P2P, "platformP2PAddrs");
137 0 : process_field(ptx.platformHTTPPort, input_http, NetInfoPurpose::PLATFORM_HTTPS, "platformHTTPSAddrs");
138 0 : }
139 : template void ProcessNetInfoPlatform(CProRegTx& ptx, const UniValue& input_p2p, const UniValue& input_http, const bool optional);
140 : template void ProcessNetInfoPlatform(CProUpServTx& ptx, const UniValue& input_p2p, const UniValue& input_http, const bool optional);
141 :
142 :
143 0 : UniValue CDeterministicMN::ToJson() const
144 : {
145 0 : UniValue obj(UniValue::VOBJ);
146 0 : obj.pushKV("type", std::string(GetMnType(nType).description));
147 0 : obj.pushKV("proTxHash", proTxHash.ToString());
148 0 : obj.pushKV("collateralHash", collateralOutpoint.hash.ToString());
149 0 : obj.pushKV("collateralIndex", collateralOutpoint.n);
150 :
151 0 : if (g_txindex) {
152 0 : CTransactionRef collateralTx;
153 0 : uint256 nBlockHash;
154 0 : g_txindex->FindTx(collateralOutpoint.hash, nBlockHash, collateralTx);
155 0 : if (collateralTx) {
156 0 : CTxDestination dest;
157 0 : if (ExtractDestination(collateralTx->vout[collateralOutpoint.n].scriptPubKey, dest)) {
158 0 : obj.pushKV("collateralAddress", EncodeDestination(dest));
159 0 : }
160 0 : }
161 0 : }
162 :
163 0 : obj.pushKV("operatorReward", (double)nOperatorReward / 100);
164 0 : obj.pushKV("state", pdmnState->ToJson(nType));
165 0 : return obj;
166 0 : }
|