From fd6ccaf98961e84df1388e797ab6f965b169e674 Mon Sep 17 00:00:00 2001 From: Kittywhiskers Van Gogh <63189531+kwvg@users.noreply.github.com> Date: Sat, 14 Mar 2026 13:38:38 +0530 Subject: [PATCH 1/7] fix: don't string quote numerical arguments --- src/test/rpc_tests.cpp | 2 +- test/functional/feature_llmq_dkgerrors.py | 26 +++++++++++------------ test/functional/feature_llmq_evo.py | 2 +- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/test/rpc_tests.cpp b/src/test/rpc_tests.cpp index cff19c4fd8d6..f54104fb579b 100644 --- a/src/test/rpc_tests.cpp +++ b/src/test/rpc_tests.cpp @@ -583,7 +583,7 @@ BOOST_AUTO_TEST_CASE(rpc_convert_composite_commands) BOOST_CHECK_EQUAL(result[0].get_str(), "register_prepare"); BOOST_CHECK_EQUAL(result[1].get_str(), "6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000"); - BOOST_CHECK_EQUAL(result[2].get_str(), "1"); + BOOST_CHECK_EQUAL(result[2].getInt(), 1); BOOST_CHECK(result[3].isArray()); BOOST_CHECK_EQUAL(result[3].size(), 2); BOOST_CHECK_EQUAL(result[3][0].get_str(), "1.1.1.1:19999"); diff --git a/test/functional/feature_llmq_dkgerrors.py b/test/functional/feature_llmq_dkgerrors.py index 90f7a812fadd..1041c7c3d726 100755 --- a/test/functional/feature_llmq_dkgerrors.py +++ b/test/functional/feature_llmq_dkgerrors.py @@ -40,18 +40,18 @@ def run_test(self): mninfos_valid = self.mninfo.copy()[1:] self.log.info("Lets omit the contribution") - self.mninfo[0].get_node(self).quorum('dkgsimerror', 'contribution-omit', '100') + self.mninfo[0].get_node(self).quorum('dkgsimerror', 'contribution-omit', 100) qh = self.mine_quorum(expected_contributions=2, mninfos_valid=mninfos_valid) self.assert_member_valid(qh, self.mninfo[0].proTxHash, False) self.log.info("Lets lie in the contribution but provide a correct justification") - self.mninfo[0].get_node(self).quorum('dkgsimerror', 'contribution-omit', '0') - self.mninfo[0].get_node(self).quorum('dkgsimerror', 'contribution-lie', '100') + self.mninfo[0].get_node(self).quorum('dkgsimerror', 'contribution-omit', 0) + self.mninfo[0].get_node(self).quorum('dkgsimerror', 'contribution-lie', 100) qh = self.mine_quorum(expected_contributions=3, expected_complaints=2, expected_justifications=1, mninfos_valid=mninfos_valid) self.assert_member_valid(qh, self.mninfo[0].proTxHash, True) self.log.info("Lets lie in the contribution and then omit the justification") - self.mninfo[0].get_node(self).quorum('dkgsimerror', 'justify-omit', '100') + self.mninfo[0].get_node(self).quorum('dkgsimerror', 'justify-omit', 100) qh = self.mine_quorum(expected_contributions=3, expected_complaints=2, mninfos_valid=mninfos_valid) self.assert_member_valid(qh, self.mninfo[0].proTxHash, False) @@ -59,27 +59,27 @@ def run_test(self): self.heal_masternodes(33) self.log.info("Lets lie in the contribution and then also lie in the justification") - self.mninfo[0].get_node(self).quorum('dkgsimerror', 'justify-omit', '0') - self.mninfo[0].get_node(self).quorum('dkgsimerror', 'justify-lie', '100') + self.mninfo[0].get_node(self).quorum('dkgsimerror', 'justify-omit', 0) + self.mninfo[0].get_node(self).quorum('dkgsimerror', 'justify-lie', 100) qh = self.mine_quorum(expected_contributions=3, expected_complaints=2, expected_justifications=1, mninfos_valid=mninfos_valid) self.assert_member_valid(qh, self.mninfo[0].proTxHash, False) self.log.info("Lets lie about another MN") - self.mninfo[0].get_node(self).quorum('dkgsimerror', 'contribution-lie', '0') - self.mninfo[0].get_node(self).quorum('dkgsimerror', 'justify-lie', '0') - self.mninfo[0].get_node(self).quorum('dkgsimerror', 'complain-lie', '100') + self.mninfo[0].get_node(self).quorum('dkgsimerror', 'contribution-lie', 0) + self.mninfo[0].get_node(self).quorum('dkgsimerror', 'justify-lie', 0) + self.mninfo[0].get_node(self).quorum('dkgsimerror', 'complain-lie', 100) qh = self.mine_quorum(expected_contributions=3, expected_complaints=1, expected_justifications=2, mninfos_valid=mninfos_valid) self.assert_member_valid(qh, self.mninfo[0].proTxHash, True) self.log.info("Lets omit 1 premature commitments") - self.mninfo[0].get_node(self).quorum('dkgsimerror', 'complain-lie', '0') - self.mninfo[0].get_node(self).quorum('dkgsimerror', 'commit-omit', '100') + self.mninfo[0].get_node(self).quorum('dkgsimerror', 'complain-lie', 0) + self.mninfo[0].get_node(self).quorum('dkgsimerror', 'commit-omit', 100) qh = self.mine_quorum(expected_contributions=3, expected_complaints=0, expected_justifications=0, expected_commitments=2, mninfos_valid=mninfos_valid) self.assert_member_valid(qh, self.mninfo[0].proTxHash, True) self.log.info("Lets lie in 1 premature commitments") - self.mninfo[0].get_node(self).quorum('dkgsimerror', 'commit-omit', '0') - self.mninfo[0].get_node(self).quorum('dkgsimerror', 'commit-lie', '100') + self.mninfo[0].get_node(self).quorum('dkgsimerror', 'commit-omit', 0) + self.mninfo[0].get_node(self).quorum('dkgsimerror', 'commit-lie', 100) qh = self.mine_quorum(expected_contributions=3, expected_complaints=0, expected_justifications=0, expected_commitments=2, mninfos_valid=mninfos_valid) self.assert_member_valid(qh, self.mninfo[0].proTxHash, True) diff --git a/test/functional/feature_llmq_evo.py b/test/functional/feature_llmq_evo.py index 74818633b4be..abafa410e920 100755 --- a/test/functional/feature_llmq_evo.py +++ b/test/functional/feature_llmq_evo.py @@ -202,7 +202,7 @@ def test_masternode_count(self, expected_mns_count, expected_evo_count): def test_masternode_winners(self, mn_rr_active=False): # ignore recent winners, test future ones only # we get up to 21 entries here: tip + up to 20 future payees - winners = self.nodes[0].masternode('winners', '0') + winners = self.nodes[0].masternode('winners', 0) weighted_count = self.mn_count + self.evo_count * (1 if mn_rr_active else 4) assert_equal(len(winners.keys()) - 1, 20 if weighted_count > 20 else weighted_count) consecutive_payments = 0 From 635ecd9e4f06964d28a66ac3399c632b92e4d621 Mon Sep 17 00:00:00 2001 From: Kittywhiskers Van Gogh <63189531+kwvg@users.noreply.github.com> Date: Sat, 14 Mar 2026 13:38:22 +0530 Subject: [PATCH 2/7] refactor: remove `ParseInt{32,64}V()` usage in Dash-specific code --- src/rpc/client.cpp | 33 +++++++++++++++++++++++++++++++++ src/rpc/evo.cpp | 12 ++++++------ src/rpc/governance.cpp | 12 ++++++------ src/rpc/masternode.cpp | 4 ++-- src/rpc/quorums.cpp | 32 ++++++++++++++++---------------- 5 files changed, 63 insertions(+), 30 deletions(-) diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp index 5e4d34afb95e..db7d73cfee49 100644 --- a/src/rpc/client.cpp +++ b/src/rpc/client.cpp @@ -256,8 +256,21 @@ static const CRPCConvertParam vRPCConvertParams[] = { "verifyislock", 3, "maxHeight" }, { "submitchainlock", 2, "blockHeight" }, { "mnauth", 0, "nodeId" }, + // Compound RPCs (note: index position is offset by one to account for subcommand) + { "gobject list-prepared", 1, "count" }, + { "gobject prepare", 2, "revision" }, + { "gobject prepare", 3, "time" }, + { "gobject prepare", 7, "outputIndex" }, + { "gobject submit", 2, "revision" }, + { "gobject submit", 3, "time" }, + { "masternode payments", 2, "count" }, + { "masternode winners", 1, "count" }, + { "protx list", 3, "height" }, + { "protx register", 2, "collateralIndex" }, { "protx register", 3, "coreP2PAddrs", true }, + { "protx register_legacy", 2, "collateralIndex" }, { "protx register_legacy", 3, "coreP2PAddrs", true }, + { "protx register_evo", 2, "collateralIndex" }, { "protx register_evo", 3, "coreP2PAddrs", true }, { "protx register_evo", 10, "platformP2PAddrs", true }, { "protx register_evo", 11, "platformHTTPSAddrs", true }, @@ -266,15 +279,35 @@ static const CRPCConvertParam vRPCConvertParams[] = { "protx register_fund_evo", 2, "coreP2PAddrs", true }, { "protx register_fund_evo", 9, "platformP2PAddrs", true }, { "protx register_fund_evo", 10, "platformHTTPSAddrs", true }, + { "protx register_prepare", 2, "collateralIndex" }, { "protx register_prepare", 3, "coreP2PAddrs", true }, + { "protx register_prepare_legacy", 2, "collateralIndex" }, { "protx register_prepare_legacy", 3, "coreP2PAddrs", true }, + { "protx register_prepare_evo", 2, "collateralIndex" }, { "protx register_prepare_evo", 3, "coreP2PAddrs", true }, { "protx register_prepare_evo", 10, "platformP2PAddrs", true }, { "protx register_prepare_evo", 11, "platformHTTPSAddrs", true }, + { "protx revoke", 3, "reason" }, { "protx update_service", 2, "coreP2PAddrs", true }, { "protx update_service_evo", 2, "coreP2PAddrs", true }, { "protx update_service_evo", 5, "platformP2PAddrs", true }, { "protx update_service_evo", 6, "platformHTTPSAddrs", true }, + { "quorum dkgsimerror", 2, "rate" }, + { "quorum dkgstatus", 1, "detail_level" }, + { "quorum getdata", 1, "nodeId" }, + { "quorum getdata", 2, "llmqType" }, + { "quorum getdata", 4, "dataMask" }, + { "quorum getrecsig", 1, "llmqType" }, + { "quorum hasrecsig", 1, "llmqType" }, + { "quorum info", 1, "llmqType" }, + { "quorum isconflicting", 1, "llmqType" }, + { "quorum listextended", 1, "height" }, + { "quorum list", 1, "count" }, + { "quorum memberof", 2, "scanQuorumsCount" }, + { "quorum selectquorum", 1, "llmqType" }, + { "quorum sign", 1, "llmqType" }, + { "quorum verify", 1, "llmqType" }, + { "quorum verify", 6, "signHeight" }, }; // clang-format on diff --git a/src/rpc/evo.cpp b/src/rpc/evo.cpp index a3a6555e3982..6c638d3ff87c 100644 --- a/src/rpc/evo.cpp +++ b/src/rpc/evo.cpp @@ -713,7 +713,7 @@ static UniValue protx_register_common_wrapper(const JSONRPCRequest& request, paramIdx++; } else { uint256 collateralHash(ParseHashV(request.params[paramIdx], "collateralHash")); - int32_t collateralIndex = ParseInt32V(request.params[paramIdx + 1], "collateralIndex"); + int32_t collateralIndex = request.params[paramIdx + 1].getInt(); if (collateralHash.IsNull() || collateralIndex < 0) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("invalid hash or index: %s-%d", collateralHash.ToString(), collateralIndex)); } @@ -1277,7 +1277,7 @@ static RPCHelpMan protx_revoke() CBLSSecretKey keyOperator = ParseBLSSecretKey(request.params[1].get_str(), "operatorKey"); if (!request.params[2].isNull()) { - int32_t nReason = ParseInt32V(request.params[2], "reason"); + int32_t nReason = request.params[2].getInt(); if (nReason < 0 || nReason > CProUpRevTx::REASON_LAST) { throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("invalid reason %d, must be between 0 and %d", nReason, CProUpRevTx::REASON_LAST)); } @@ -1462,7 +1462,7 @@ static RPCHelpMan protx_list() bool detailed = !request.params[1].isNull() ? ParseBoolV(request.params[1], "detailed") : false; LOCK2(wallet->cs_wallet, ::cs_main); - int height = !request.params[2].isNull() ? ParseInt32V(request.params[2], "height") : chainman.ActiveChain().Height(); + int height = !request.params[2].isNull() ? request.params[2].getInt() : chainman.ActiveChain().Height(); if (height < 1 || height > chainman.ActiveChain().Height()) { throw JSONRPCError(RPC_INVALID_PARAMETER, "invalid height specified"); } @@ -1495,7 +1495,7 @@ static RPCHelpMan protx_list() #else LOCK(::cs_main); #endif - int height = !request.params[2].isNull() ? ParseInt32V(request.params[2], "height") : chainman.ActiveChain().Height(); + int height = !request.params[2].isNull() ? request.params[2].getInt() : chainman.ActiveChain().Height(); if (height < 1 || height > chainman.ActiveChain().Height()) { throw JSONRPCError(RPC_INVALID_PARAMETER, "invalid height specified"); } @@ -1587,7 +1587,7 @@ static uint256 ParseBlock(const UniValue& v, const ChainstateManager& chainman, try { return ParseHashV(v, strName); } catch (...) { - int h = ParseInt32V(v, strName); + int h = v.getInt(); if (h < 1 || h > chainman.ActiveChain().Height()) throw std::runtime_error(strprintf("%s must be a block hash or chain height and not %s", strName, v.getValStr())); return *chainman.ActiveChain()[h]->phashBlock; @@ -1646,7 +1646,7 @@ static const CBlockIndex* ParseBlockIndex(const UniValue& v, const ChainstateMan throw std::runtime_error(strprintf("Block %s with hash %s not found", strName, v.getValStr())); return pblockindex; } catch (...) { - int h = ParseInt32V(v, strName); + int h = v.getInt(); if (h < 1 || h > chainman.ActiveChain().Height()) throw std::runtime_error(strprintf("%s must be a chain height and not %s", strName, v.getValStr())); return chainman.ActiveChain()[h]; diff --git a/src/rpc/governance.cpp b/src/rpc/governance.cpp index e1eaa0cef392..7770f7f1cbf3 100644 --- a/src/rpc/governance.cpp +++ b/src/rpc/governance.cpp @@ -176,8 +176,8 @@ static RPCHelpMan gobject_prepare() hashParent = ParseHashV(request.params[0], "parent-hash"); } - int nRevision = ParseInt32V(request.params[1], "revision"); - int64_t nTime = ParseInt64V(request.params[2], "time"); + int nRevision = request.params[1].getInt(); + int64_t nTime = request.params[2].getInt(); std::string strDataHex = request.params[3].get_str(); // CREATE A NEW COLLATERAL TRANSACTION FOR THIS SPECIFIC OBJECT @@ -225,7 +225,7 @@ static RPCHelpMan gobject_prepare() outpoint.SetNull(); if (!request.params[5].isNull() && !request.params[6].isNull()) { uint256 collateralHash(ParseHashV(request.params[5], "outputHash")); - int32_t collateralIndex = ParseInt32V(request.params[6], "outputIndex"); + int32_t collateralIndex = request.params[6].getInt(); if (collateralHash.IsNull() || collateralIndex < 0) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("invalid hash or index: %s-%d", collateralHash.ToString(), collateralIndex)); } @@ -278,7 +278,7 @@ static RPCHelpMan gobject_list_prepared() if (!wallet) return UniValue::VNULL; EnsureWalletIsUnlocked(*wallet); - int64_t nCount = request.params.empty() ? 10 : ParseInt64V(request.params[0], "count"); + int64_t nCount = request.params.empty() ? 10 : request.params[0].getInt(); if (nCount < 0) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Negative count"); } @@ -361,8 +361,8 @@ static RPCHelpMan gobject_submit() // GET THE PARAMETERS FROM USER - int nRevision = ParseInt32V(request.params[1], "revision"); - int64_t nTime = ParseInt64V(request.params[2], "time"); + int nRevision = request.params[1].getInt(); + int64_t nTime = request.params[2].getInt(); std::string strDataHex = request.params[3].get_str(); CGovernanceObject govobj(hashParent, nRevision, nTime, txidFee, strDataHex); diff --git a/src/rpc/masternode.cpp b/src/rpc/masternode.cpp index cac3cb7880d1..201d126e5252 100644 --- a/src/rpc/masternode.cpp +++ b/src/rpc/masternode.cpp @@ -288,7 +288,7 @@ static RPCHelpMan masternode_winners() std::string strFilter; if (!request.params[0].isNull()) { - nCount = LocaleIndependentAtoi(request.params[0].get_str()); + nCount = request.params[0].getInt(); } if (!request.params[1].isNull()) { @@ -378,7 +378,7 @@ static RPCHelpMan masternode_payments() } } - int64_t nCount = request.params.size() > 1 ? ParseInt64V(request.params[1], "count") : 1; + int64_t nCount = request.params.size() > 1 ? request.params[1].getInt() : 1; // A temporary vector which is used to sort results properly (there is no "reverse" in/for UniValue) std::vector vecPayments; diff --git a/src/rpc/quorums.cpp b/src/rpc/quorums.cpp index eedaba18501e..34194fadc640 100644 --- a/src/rpc/quorums.cpp +++ b/src/rpc/quorums.cpp @@ -71,7 +71,7 @@ static RPCHelpMan quorum_list() int count = -1; if (!request.params[0].isNull()) { - count = ParseInt32V(request.params[0], "count"); + count = request.params[0].getInt(); if (count < -1) { throw JSONRPCError(RPC_INVALID_PARAMETER, "count can't be negative"); } @@ -137,7 +137,7 @@ static RPCHelpMan quorum_list_extended() int nHeight = -1; if (!request.params[0].isNull()) { - nHeight = ParseInt32V(request.params[0], "height"); + nHeight = request.params[0].getInt(); if (nHeight < 0 || nHeight > WITH_LOCK(cs_main, return chainman.ActiveChain().Height())) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Block height out of range"); } @@ -278,7 +278,7 @@ static RPCHelpMan quorum_info() const NodeContext& node = EnsureAnyNodeContext(request.context); const LLMQContext& llmq_ctx = EnsureLLMQContext(node); - const Consensus::LLMQType llmqType{static_cast(ParseInt32V(request.params[0], "llmqType"))}; + const Consensus::LLMQType llmqType{static_cast(request.params[0].getInt())}; if (!Params().GetLLMQ(llmqType).has_value()) { throw JSONRPCError(RPC_INVALID_PARAMETER, "invalid LLMQ type"); } @@ -339,7 +339,7 @@ static RPCHelpMan quorum_dkgstatus() { int detailLevel = 0; if (!request.params[0].isNull()) { - detailLevel = ParseInt32V(request.params[0], "detail_level"); + detailLevel = request.params[0].getInt(); if (detailLevel < 0 || detailLevel > 2) { throw JSONRPCError(RPC_INVALID_PARAMETER, "invalid detail_level"); } @@ -463,7 +463,7 @@ static RPCHelpMan quorum_memberof() uint256 protxHash(ParseHashV(request.params[0], "proTxHash")); int scanQuorumsCount = -1; if (!request.params[1].isNull()) { - scanQuorumsCount = ParseInt32V(request.params[1], "scanQuorumsCount"); + scanQuorumsCount = request.params[1].getInt(); if (scanQuorumsCount <= 0) { throw JSONRPCError(RPC_INVALID_PARAMETER, "invalid scanQuorumsCount parameter"); } @@ -593,7 +593,7 @@ static RPCHelpMan quorum_sign() RPCExamples{""}, [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue { - const Consensus::LLMQType llmqType{static_cast(ParseInt32V(request.params[0], "llmqType"))}; + const Consensus::LLMQType llmqType{static_cast(request.params[0].getInt())}; JSONRPCRequest new_request{request}; new_request.params.setArray(); @@ -662,7 +662,7 @@ static RPCHelpMan quorum_verify() const ChainstateManager& chainman = EnsureChainman(node); const LLMQContext& llmq_ctx = EnsureLLMQContext(node); - const Consensus::LLMQType llmqType{static_cast(ParseInt32V(request.params[0], "llmqType"))}; + const Consensus::LLMQType llmqType{static_cast(request.params[0].getInt())}; const auto llmq_params_opt = Params().GetLLMQ(llmqType); if (!llmq_params_opt.has_value()) { @@ -681,7 +681,7 @@ static RPCHelpMan quorum_verify() if (request.params[4].isNull() || (request.params[4].get_str().empty() && !request.params[5].isNull())) { int signHeight{-1}; if (!request.params[5].isNull()) { - signHeight = ParseInt32V(request.params[5], "signHeight"); + signHeight = request.params[5].getInt(); } return VerifyRecoveredSigLatestQuorums(*llmq_params_opt, chainman.ActiveChain(), *llmq_ctx.qman, signHeight, id, msgHash, sig); } @@ -715,7 +715,7 @@ static RPCHelpMan quorum_hasrecsig() const NodeContext& node = EnsureAnyNodeContext(request.context); const LLMQContext& llmq_ctx = EnsureLLMQContext(node); - const Consensus::LLMQType llmqType{static_cast(ParseInt32V(request.params[0], "llmqType"))}; + const Consensus::LLMQType llmqType{static_cast(request.params[0].getInt())}; if (!Params().GetLLMQ(llmqType).has_value()) { throw JSONRPCError(RPC_INVALID_PARAMETER, "invalid LLMQ type"); } @@ -744,7 +744,7 @@ static RPCHelpMan quorum_getrecsig() const NodeContext& node = EnsureAnyNodeContext(request.context); const LLMQContext& llmq_ctx = EnsureLLMQContext(node); - const Consensus::LLMQType llmqType{static_cast(ParseInt32V(request.params[0], "llmqType"))}; + const Consensus::LLMQType llmqType{static_cast(request.params[0].getInt())}; if (!Params().GetLLMQ(llmqType).has_value()) { throw JSONRPCError(RPC_INVALID_PARAMETER, "invalid LLMQ type"); } @@ -780,7 +780,7 @@ static RPCHelpMan quorum_isconflicting() const NodeContext& node = EnsureAnyNodeContext(request.context); const LLMQContext& llmq_ctx = EnsureLLMQContext(node); - const Consensus::LLMQType llmqType{static_cast(ParseInt32V(request.params[0], "llmqType"))}; + const Consensus::LLMQType llmqType{static_cast(request.params[0].getInt())}; if (!Params().GetLLMQ(llmqType).has_value()) { throw JSONRPCError(RPC_INVALID_PARAMETER, "invalid LLMQ type"); } @@ -817,7 +817,7 @@ static RPCHelpMan quorum_selectquorum() const ChainstateManager& chainman = EnsureChainman(node); const LLMQContext& llmq_ctx = EnsureLLMQContext(node); - const Consensus::LLMQType llmqType{static_cast(ParseInt32V(request.params[0], "llmqType"))}; + const Consensus::LLMQType llmqType{static_cast(request.params[0].getInt())}; const auto llmq_params_opt = Params().GetLLMQ(llmqType); if (!llmq_params_opt.has_value()) { throw JSONRPCError(RPC_INVALID_PARAMETER, "invalid LLMQ type"); @@ -859,7 +859,7 @@ static RPCHelpMan quorum_dkgsimerror() [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue { std::string type_str = request.params[0].get_str(); - int32_t rate = ParseInt32V(request.params[1], "rate"); + int32_t rate = request.params[1].getInt(); if (rate < 0 || rate > 100) { throw JSONRPCError(RPC_INVALID_PARAMETER, "invalid rate. Must be between 0 and 100"); @@ -899,10 +899,10 @@ static RPCHelpMan quorum_getdata() const LLMQContext& llmq_ctx = EnsureLLMQContext(node); CConnman& connman = EnsureConnman(node); - NodeId nodeId = ParseInt64V(request.params[0], "nodeId"); - Consensus::LLMQType llmqType = static_cast(ParseInt32V(request.params[1], "llmqType")); + NodeId nodeId = request.params[0].getInt(); + Consensus::LLMQType llmqType = static_cast(request.params[1].getInt()); uint256 quorumHash(ParseHashV(request.params[2], "quorumHash")); - uint16_t nDataMask = static_cast(ParseInt32V(request.params[3], "dataMask")); + uint16_t nDataMask = static_cast(request.params[3].getInt()); uint256 proTxHash; // Check if request wants ENCRYPTED_CONTRIBUTIONS data From e2ee1f7de63d731c0dc9b79b4eaa1485c35def56 Mon Sep 17 00:00:00 2001 From: Kittywhiskers Van Gogh <63189531+kwvg@users.noreply.github.com> Date: Sat, 14 Mar 2026 07:17:37 +0530 Subject: [PATCH 3/7] refactor: drop array parsing logic in `quorum rotationinfo` --- src/rpc/client.cpp | 1 + src/rpc/quorums.cpp | 11 ++--------- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp index db7d73cfee49..3b194fe19411 100644 --- a/src/rpc/client.cpp +++ b/src/rpc/client.cpp @@ -304,6 +304,7 @@ static const CRPCConvertParam vRPCConvertParams[] = { "quorum listextended", 1, "height" }, { "quorum list", 1, "count" }, { "quorum memberof", 2, "scanQuorumsCount" }, + { "quorum rotationinfo", 3, "baseBlockHashes" }, { "quorum selectquorum", 1, "llmqType" }, { "quorum sign", 1, "llmqType" }, { "quorum verify", 1, "llmqType" }, diff --git a/src/rpc/quorums.cpp b/src/rpc/quorums.cpp index 34194fadc640..77a10b73d02e 100644 --- a/src/rpc/quorums.cpp +++ b/src/rpc/quorums.cpp @@ -957,15 +957,8 @@ static RPCHelpMan quorum_rotationinfo() cmd.extraShare = request.params[1].isNull() ? false : ParseBoolV(request.params[1], "extraShare"); if (!request.params[2].isNull()) { - UniValue hashes; - if (request.params[2].isStr() && hashes.read(request.params[2].get_str()) && hashes.isArray()) { - // pass - } else if (request.params[2].isArray()) { - hashes = request.params[2].get_array(); - } else { - throw std::runtime_error(std::string("Error parsing JSON: ") + request.params[2].get_str()); - } - for (const auto& hash : hashes.get_array().getValues()) { + const auto& hashes = request.params[2].get_array(); + for (const auto& hash : hashes.getValues()) { cmd.baseBlockHashes.emplace_back(ParseHashV(hash, "baseBlockHash")); } } From 5c34d856b477cda9bccb94da8e1bb92661d2ca50 Mon Sep 17 00:00:00 2001 From: Kittywhiskers Van Gogh <63189531+kwvg@users.noreply.github.com> Date: Sat, 14 Mar 2026 06:34:05 +0530 Subject: [PATCH 4/7] fix: adapt `ParseBlockIndex` to work with stringy integers, update docs --- src/rpc/evo.cpp | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/rpc/evo.cpp b/src/rpc/evo.cpp index 6c638d3ff87c..6bdcb0f5d1e1 100644 --- a/src/rpc/evo.cpp +++ b/src/rpc/evo.cpp @@ -1587,7 +1587,7 @@ static uint256 ParseBlock(const UniValue& v, const ChainstateManager& chainman, try { return ParseHashV(v, strName); } catch (...) { - int h = v.getInt(); + int h = v.isNum() ? v.getInt() : LocaleIndependentAtoi(v.get_str()); if (h < 1 || h > chainman.ActiveChain().Height()) throw std::runtime_error(strprintf("%s must be a block hash or chain height and not %s", strName, v.getValStr())); return *chainman.ActiveChain()[h]->phashBlock; @@ -1599,8 +1599,8 @@ static RPCHelpMan protx_diff() return RPCHelpMan{"protx diff", "\nCalculates a diff between two deterministic masternode lists. The result also contains proof data.\n", { - {"baseBlock", RPCArg::Type::NUM, RPCArg::Optional::NO, "The starting block height."}, - {"block", RPCArg::Type::NUM, RPCArg::Optional::NO, "The ending block height."}, + {"baseBlock", RPCArg::Type::STR, RPCArg::Optional::NO, "The starting block hash or height."}, + {"block", RPCArg::Type::STR, RPCArg::Optional::NO, "The ending block hash or height."}, {"extended", RPCArg::Type::BOOL, RPCArg::Optional::OMITTED, "Show additional fields."}, }, CSimplifiedMNListDiff::GetJsonHelp(/*key=*/"", /*optional=*/false), @@ -1646,9 +1646,9 @@ static const CBlockIndex* ParseBlockIndex(const UniValue& v, const ChainstateMan throw std::runtime_error(strprintf("Block %s with hash %s not found", strName, v.getValStr())); return pblockindex; } catch (...) { - int h = v.getInt(); + int h = v.isNum() ? v.getInt() : LocaleIndependentAtoi(v.get_str()); if (h < 1 || h > chainman.ActiveChain().Height()) - throw std::runtime_error(strprintf("%s must be a chain height and not %s", strName, v.getValStr())); + throw std::runtime_error(strprintf("%s must be a block hash or chain height and not %s", strName, v.getValStr())); return chainman.ActiveChain()[h]; } } @@ -1658,8 +1658,8 @@ static RPCHelpMan protx_listdiff() return RPCHelpMan{"protx listdiff", "\nCalculate a full MN list diff between two masternode lists.\n", { - {"baseBlock", RPCArg::Type::NUM, RPCArg::Optional::NO, "The starting block height."}, - {"block", RPCArg::Type::NUM, RPCArg::Optional::NO, "The ending block height."}, + {"baseBlock", RPCArg::Type::STR, RPCArg::Optional::NO, "The starting block hash or height."}, + {"block", RPCArg::Type::STR, RPCArg::Optional::NO, "The ending block hash or height."}, }, RPCResult { RPCResult::Type::OBJ, "", "", @@ -1834,8 +1834,8 @@ static RPCHelpMan evodb_verify() "This is a read-only operation that does not modify the database.\n" "If no heights are specified, defaults to the full range from DIP0003 activation to chain tip.\n", { - {"startBlock", RPCArg::Type::NUM, RPCArg::Optional::OMITTED, "The starting block height (defaults to DIP0003 activation height)."}, - {"stopBlock", RPCArg::Type::NUM, RPCArg::Optional::OMITTED, "The ending block height (defaults to current chain tip)."}, + {"startBlock", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "The starting block hash or height (defaults to DIP0003 activation height)."}, + {"stopBlock", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "The ending block hash or height (defaults to current chain tip)."}, }, RPCResult{ RPCResult::Type::OBJ, "", "", @@ -1872,8 +1872,8 @@ static RPCHelpMan evodb_repair() "If verification fails, recalculates diffs from blockchain data and replaces corrupted records.\n" "If no heights are specified, defaults to the full range from DIP0003 activation to chain tip.\n", { - {"startBlock", RPCArg::Type::NUM, RPCArg::Optional::OMITTED, "The starting block height (defaults to DIP0003 activation height)."}, - {"stopBlock", RPCArg::Type::NUM, RPCArg::Optional::OMITTED, "The ending block height (defaults to current chain tip)."}, + {"startBlock", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "The starting block hash or height (defaults to DIP0003 activation height)."}, + {"stopBlock", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "The ending block hash or height (defaults to current chain tip)."}, }, RPCResult{ RPCResult::Type::OBJ, "", "", From 42bc930a8065f42718170386ee98592b3b332908 Mon Sep 17 00:00:00 2001 From: Kittywhiskers Van Gogh <63189531+kwvg@users.noreply.github.com> Date: Sat, 10 Jan 2026 13:13:49 +0530 Subject: [PATCH 5/7] refactor: drop unused `ParseInt{32,64}V()`, unresolvable `ParseDoubleV()` --- src/rpc/util.cpp | 18 ------------------ src/rpc/util.h | 3 --- 2 files changed, 21 deletions(-) diff --git a/src/rpc/util.cpp b/src/rpc/util.cpp index 5e83622f93c1..39a16273634d 100644 --- a/src/rpc/util.cpp +++ b/src/rpc/util.cpp @@ -121,24 +121,6 @@ std::vector ParseHexO(const UniValue& o, std::string strKey) return ParseHexV(o.find_value(strKey), strKey); } -int32_t ParseInt32V(const UniValue& v, const std::string &strName) -{ - const std::string& strNum = v.getValStr(); - int32_t num; - if (!ParseInt32(strNum, &num)) - throw JSONRPCError(RPC_INVALID_PARAMETER, strName+" must be a 32bit integer (not '"+strNum+"')"); - return num; -} - -int64_t ParseInt64V(const UniValue& v, const std::string &strName) -{ - const std::string& strNum = v.getValStr(); - int64_t num; - if (!ParseInt64(strNum, &num)) - throw JSONRPCError(RPC_INVALID_PARAMETER, strName+" must be a 64bit integer (not '"+strNum+"')"); - return num; -} - bool ParseBoolV(const UniValue& v, const std::string &strName) { std::string strBool; diff --git a/src/rpc/util.h b/src/rpc/util.h index 6a62ca00e3e2..b017b9f08e5d 100644 --- a/src/rpc/util.h +++ b/src/rpc/util.h @@ -93,9 +93,6 @@ uint256 ParseHashO(const UniValue& o, std::string strKey); std::vector ParseHexV(const UniValue& v, std::string strName); std::vector ParseHexO(const UniValue& o, std::string strKey); -int32_t ParseInt32V(const UniValue& v, const std::string &strName); -int64_t ParseInt64V(const UniValue& v, const std::string &strName); -double ParseDoubleV(const UniValue& v, const std::string &strName); bool ParseBoolV(const UniValue& v, const std::string &strName); /** From 10dfab51b039cdf5ea4a546b6e479d7a5659f11b Mon Sep 17 00:00:00 2001 From: Kittywhiskers Van Gogh <63189531+kwvg@users.noreply.github.com> Date: Sat, 14 Mar 2026 06:56:33 +0530 Subject: [PATCH 6/7] refactor: add composite RPC bools to conversion table, deprecate old fn We cannot simply get rid of `ParseBoolV` because upstream parsing logic is much more stricter, constituting a breaking change. This requires us to therefore follow the deprecation process. --- src/rpc/client.cpp | 22 ++++++++++++ src/rpc/util.cpp | 37 +++++++++++++-------- src/test/rpc_tests.cpp | 14 ++++---- test/functional/feature_llmq_connections.py | 2 +- 4 files changed, 53 insertions(+), 22 deletions(-) diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp index 3b194fe19411..61a2e58ef6ce 100644 --- a/src/rpc/client.cpp +++ b/src/rpc/client.cpp @@ -257,28 +257,41 @@ static const CRPCConvertParam vRPCConvertParams[] = { "submitchainlock", 2, "blockHeight" }, { "mnauth", 0, "nodeId" }, // Compound RPCs (note: index position is offset by one to account for subcommand) + { "bls generate", 1, "legacy" }, + { "bls fromsecret", 2, "legacy" }, + { "coinjoinsalt generate", 1, "overwrite" }, + { "coinjoinsalt set", 2, "overwrite" }, { "gobject list-prepared", 1, "count" }, { "gobject prepare", 2, "revision" }, { "gobject prepare", 3, "time" }, { "gobject prepare", 7, "outputIndex" }, { "gobject submit", 2, "revision" }, { "gobject submit", 3, "time" }, + { "masternode connect", 2, "v2transport" }, { "masternode payments", 2, "count" }, { "masternode winners", 1, "count" }, + { "protx diff", 3, "extended" }, + { "protx list", 2, "detailed" }, { "protx list", 3, "height" }, { "protx register", 2, "collateralIndex" }, { "protx register", 3, "coreP2PAddrs", true }, + { "protx register", 10, "submit" }, { "protx register_legacy", 2, "collateralIndex" }, { "protx register_legacy", 3, "coreP2PAddrs", true }, + { "protx register_legacy", 10, "submit" }, { "protx register_evo", 2, "collateralIndex" }, { "protx register_evo", 3, "coreP2PAddrs", true }, { "protx register_evo", 10, "platformP2PAddrs", true }, { "protx register_evo", 11, "platformHTTPSAddrs", true }, + { "protx register_evo", 13, "submit" }, { "protx register_fund", 2, "coreP2PAddrs", true }, + { "protx register_fund", 9, "submit" }, { "protx register_fund_legacy", 2, "coreP2PAddrs", true }, + { "protx register_fund_legacy", 9, "submit" }, { "protx register_fund_evo", 2, "coreP2PAddrs", true }, { "protx register_fund_evo", 9, "platformP2PAddrs", true }, { "protx register_fund_evo", 10, "platformHTTPSAddrs", true }, + { "protx register_fund_evo", 12, "submit" }, { "protx register_prepare", 2, "collateralIndex" }, { "protx register_prepare", 3, "coreP2PAddrs", true }, { "protx register_prepare_legacy", 2, "collateralIndex" }, @@ -288,10 +301,15 @@ static const CRPCConvertParam vRPCConvertParams[] = { "protx register_prepare_evo", 10, "platformP2PAddrs", true }, { "protx register_prepare_evo", 11, "platformHTTPSAddrs", true }, { "protx revoke", 3, "reason" }, + { "protx revoke", 5, "submit" }, + { "protx update_registrar", 6, "submit" }, + { "protx update_registrar_legacy", 6, "submit" }, { "protx update_service", 2, "coreP2PAddrs", true }, + { "protx update_service", 6, "submit" }, { "protx update_service_evo", 2, "coreP2PAddrs", true }, { "protx update_service_evo", 5, "platformP2PAddrs", true }, { "protx update_service_evo", 6, "platformHTTPSAddrs", true }, + { "protx update_service_evo", 9, "submit" }, { "quorum dkgsimerror", 2, "rate" }, { "quorum dkgstatus", 1, "detail_level" }, { "quorum getdata", 1, "nodeId" }, @@ -300,13 +318,17 @@ static const CRPCConvertParam vRPCConvertParams[] = { "quorum getrecsig", 1, "llmqType" }, { "quorum hasrecsig", 1, "llmqType" }, { "quorum info", 1, "llmqType" }, + { "quorum info", 3, "includeSkShare" }, { "quorum isconflicting", 1, "llmqType" }, { "quorum listextended", 1, "height" }, { "quorum list", 1, "count" }, { "quorum memberof", 2, "scanQuorumsCount" }, + { "quorum platformsign", 4, "submit" }, + { "quorum rotationinfo", 2, "extraShare" }, { "quorum rotationinfo", 3, "baseBlockHashes" }, { "quorum selectquorum", 1, "llmqType" }, { "quorum sign", 1, "llmqType" }, + { "quorum sign", 5, "submit" }, { "quorum verify", 1, "llmqType" }, { "quorum verify", 6, "signHeight" }, }; diff --git a/src/rpc/util.cpp b/src/rpc/util.cpp index 39a16273634d..7e33a84d15b1 100644 --- a/src/rpc/util.cpp +++ b/src/rpc/util.cpp @@ -12,6 +12,7 @@ #include