LCOV - code coverage report
Current view: top level - src/rpc - evo_util.cpp (source / functions) Hit Total Coverage
Test: total_coverage.info Lines: 97 101 96.0 %
Date: 2026-06-25 07:23:43 Functions: 10 10 100.0 %

          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         164 : bool IsNumeric(std::string_view input) { return input.find_first_not_of("0123456789") == std::string::npos; }
      17             : 
      18             : template <typename ProTx>
      19        1151 : void ParseInput(ProTx& ptx, std::string_view field_name, const std::string& input_str, NetInfoPurpose purpose,
      20             :                 size_t idx, bool optional)
      21             : {
      22        1151 :     if (input_str.empty()) {
      23          50 :         if (!optional) {
      24          18 :             throw JSONRPCError(RPC_INVALID_PARAMETER,
      25           8 :                                strprintf("Invalid param for %s[%zu], cannot be empty", field_name, idx));
      26             :         }
      27          42 :         return; // Nothing to do
      28             :     }
      29        1101 :     if (auto ret = ptx.netInfo->AddEntry(purpose, input_str); ret != NetInfoStatus::Success) {
      30          20 :         throw JSONRPCError(RPC_INVALID_PARAMETER,
      31          10 :                            strprintf("Error setting %s[%zu] to '%s' (%s)", field_name, idx, input_str, NISToString(ret)));
      32             :     }
      33        1151 : }
      34             : } // anonymous namespace
      35             : 
      36             : template <typename ProTx>
      37         949 : void ProcessNetInfoCore(ProTx& ptx, const UniValue& input, const bool optional)
      38             : {
      39         949 :     CHECK_NONFATAL(ptx.netInfo);
      40             : 
      41         949 :     if (input.isStr()) {
      42         128 :         ParseInput(ptx, /*field_name=*/"coreP2PAddrs", input.get_str(), NetInfoPurpose::CORE_P2P, /*idx=*/0, optional);
      43         949 :     } else if (input.isArray()) {
      44         821 :         const UniValue& entries = input.get_array();
      45         821 :         if (!optional && entries.empty()) {
      46           8 :             throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid param for coreP2PAddrs, cannot be empty");
      47             :         }
      48        1676 :         for (size_t idx{0}; idx < entries.size(); idx++) {
      49         863 :             const UniValue& entry_uv{entries[idx]};
      50         863 :             if (!entry_uv.isStr()) {
      51           8 :                 throw JSONRPCError(RPC_INVALID_PARAMETER,
      52           8 :                                    strprintf("Invalid param for coreP2PAddrs[%zu], must be string", idx));
      53             :             }
      54         855 :             ParseInput(ptx, /*field_name=*/"coreP2PAddrs", entry_uv.get_str(), NetInfoPurpose::CORE_P2P, idx,
      55             :                        /*optional=*/false);
      56         855 :         }
      57         813 :     } else {
      58           0 :         throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid param for coreP2PAddrs, must be string or array");
      59             :     }
      60         949 : }
      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         184 : void ProcessNetInfoPlatform(ProTx& ptx, const UniValue& input_p2p, const UniValue& input_http, const bool optional)
      66             : {
      67         184 :     CHECK_NONFATAL(ptx.netInfo);
      68             : 
      69         508 :     auto process_field = [&](uint16_t& maybe_target, const UniValue& input, const NetInfoPurpose purpose,
      70             :                              std::string_view field_name) {
      71         324 :         if (!input.isArray() && !input.isNum() && !input.isStr()) {
      72          64 :             throw JSONRPCError(RPC_INVALID_PARAMETER,
      73           0 :                                strprintf("Invalid param for %s, must be array, number or string", field_name));
      74             :         }
      75             : 
      76         324 :         bool is_empty{input.isArray() ? input.get_array().empty() : input.getValStr().empty()};
      77         324 :         bool is_nonnumeric_str{input.isStr() && !IsNumeric(input.getValStr())};
      78         324 :         if (is_empty || is_nonnumeric_str || input.isArray()) {
      79         110 :             if (is_empty) {
      80          36 :                 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          36 :                 if (!ptx.netInfo->IsEmpty()) {
      86             :                     // Blank values are tolerable so long as no other field has been populated.
      87          12 :                     throw JSONRPCError(RPC_INVALID_PARAMETER,
      88          12 :                                        strprintf("Invalid param for %s, cannot be empty if other fields populated",
      89             :                                                  field_name));
      90             :                 }
      91          24 :             }
      92          98 :             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          22 :                 throw JSONRPCError(RPC_INVALID_PARAMETER,
      98          22 :                                    strprintf("Invalid param for %s, ProTx version only supports ports", field_name));
      99             :             }
     100          76 :             if (input.isArray()) {
     101          54 :                 const UniValue& entries = input.get_array();
     102         150 :                 for (size_t idx{0}; idx < entries.size(); idx++) {
     103         104 :                     const UniValue& entry{entries[idx]};
     104         104 :                     if (!entry.isStr() || IsNumeric(entry.get_str())) {
     105           8 :                         throw JSONRPCError(RPC_INVALID_PARAMETER,
     106           8 :                                            strprintf("Invalid param for %s[%zu], must be string", field_name, idx));
     107             :                     }
     108          96 :                     ParseInput(ptx, field_name, entry.get_str(), purpose, idx, /*optional=*/false);
     109          96 :                 }
     110          46 :             } else {
     111          22 :                 CHECK_NONFATAL(is_empty || is_nonnumeric_str);
     112          22 :                 ParseInput(ptx, field_name, input.get_str(), purpose, /*idx=*/0, /*optional=*/true);
     113             :             }
     114          68 :         } else {
     115         412 :             if (int32_t port{0};
     116         214 :                 ParseInt32(input.getValStr(), &port) && port >= 1 && port <= std::numeric_limits<uint16_t>::max()) {
     117             :                 // Valid port
     118         198 :                 if (!ptx.netInfo->CanStorePlatform()) {
     119         152 :                     maybe_target = static_cast<uint16_t>(port);
     120         152 :                     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          46 :                 if (!ptx.netInfo->HasEntries(NetInfoPurpose::CORE_P2P)) {
     124           6 :                     throw JSONRPCError(RPC_INVALID_PARAMETER,
     125           6 :                                        strprintf("Cannot set param for %s, must specify coreP2PAddrs first", field_name));
     126             :                 }
     127          40 :                 const CService service{CNetAddr{ptx.netInfo->GetPrimary()}, static_cast<uint16_t>(port)};
     128          40 :                 CHECK_NONFATAL(service.IsValid());
     129          40 :                 ParseInput(ptx, field_name, service.ToStringAddrPort(), purpose, /*idx=*/0, /*optional=*/false);
     130          40 :             } else {
     131          16 :                 throw JSONRPCError(RPC_INVALID_PARAMETER,
     132          16 :                                    strprintf("Invalid param for %s, must be a valid port [1-65535]", field_name));
     133             :             }
     134             :         }
     135         324 :     };
     136         184 :     process_field(ptx.platformP2PPort, input_p2p, NetInfoPurpose::PLATFORM_P2P, "platformP2PAddrs");
     137         184 :     process_field(ptx.platformHTTPPort, input_http, NetInfoPurpose::PLATFORM_HTTPS, "platformHTTPSAddrs");
     138         184 : }
     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        3854 : UniValue CDeterministicMN::ToJson() const
     144             : {
     145        3854 :     UniValue obj(UniValue::VOBJ);
     146        3854 :     obj.pushKV("type", std::string(GetMnType(nType).description));
     147        3854 :     obj.pushKV("proTxHash", proTxHash.ToString());
     148        3854 :     obj.pushKV("collateralHash", collateralOutpoint.hash.ToString());
     149        3854 :     obj.pushKV("collateralIndex", collateralOutpoint.n);
     150             : 
     151        3854 :     if (g_txindex) {
     152        3854 :         CTransactionRef collateralTx;
     153        3854 :         uint256 nBlockHash;
     154        3854 :         g_txindex->FindTx(collateralOutpoint.hash, nBlockHash, collateralTx);
     155        3854 :         if (collateralTx) {
     156        3854 :             CTxDestination dest;
     157        3854 :             if (ExtractDestination(collateralTx->vout[collateralOutpoint.n].scriptPubKey, dest)) {
     158        3854 :                 obj.pushKV("collateralAddress", EncodeDestination(dest));
     159        3854 :             }
     160        3854 :         }
     161        3854 :     }
     162             : 
     163        3854 :     obj.pushKV("operatorReward", (double)nOperatorReward / 100);
     164        3854 :     obj.pushKV("state", pdmnState->ToJson(nType));
     165        3854 :     return obj;
     166        3854 : }

Generated by: LCOV version 1.16