diff --git a/src/Makefile.am b/src/Makefile.am index 58a9cec760fe..cc3a31cf7366 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -920,6 +920,7 @@ libbitcoin_common_a_SOURCES = \ evo/netinfo.cpp \ external_signer.cpp \ governance/common.cpp \ + governance/core_write.cpp \ init/common.cpp \ key.cpp \ key_io.cpp \ diff --git a/src/core_io.h b/src/core_io.h index 8dd365f95518..85deffd5c2fd 100644 --- a/src/core_io.h +++ b/src/core_io.h @@ -58,6 +58,6 @@ void ScriptToUniv(const CScript& script, UniValue& out, bool include_hex = true, void TxToUniv(const CTransaction& tx, const uint256& block_hash, UniValue& entry, bool include_hex = true, int serialize_flags = 0, const CTxUndo* txundo = nullptr, TxVerbosity verbosity = TxVerbosity::SHOW_DETAILS, const CSpentIndexTxInfo* ptxSpentInfo = nullptr); // evo/core_write.cpp -RPCResult GetRpcResult(const std::string& key, bool optional = false); +RPCResult GetRpcResult(const std::string& key, bool optional = false, const std::string& override_name = ""); #endif // BITCOIN_CORE_IO_H diff --git a/src/evo/core_write.cpp b/src/evo/core_write.cpp index 0779abe51893..b3e414d7c779 100644 --- a/src/evo/core_write.cpp +++ b/src/evo/core_write.cpp @@ -37,6 +37,7 @@ const std::map RPCRESULT_MAP{{ {RPCResult::Type::ARR, "platform_https", /*optional=*/true, "Addresses used for Platform HTTPS API", {{RPCResult::Type::STR, "address", ""}}}, }}}, + RESULT_MAP_ENTRY("collateralAddress", RPCResult::Type::STR, "Dash address used for collateral"), RESULT_MAP_ENTRY("collateralHash", RPCResult::Type::STR_HEX, "Collateral transaction hash"), RESULT_MAP_ENTRY("collateralIndex", RPCResult::Type::NUM, "Collateral transaction output index"), RESULT_MAP_ENTRY("consecutivePayments", RPCResult::Type::NUM, "Consecutive payments masternode has received in payment cycle"), @@ -44,10 +45,12 @@ const std::map RPCRESULT_MAP{{ RESULT_MAP_ENTRY("inputsHash", RPCResult::Type::STR_HEX, "Hash of all the outpoints of the transaction inputs"), RESULT_MAP_ENTRY("lastPaidHeight", RPCResult::Type::NUM, "Height masternode was last paid"), RESULT_MAP_ENTRY("llmqType", RPCResult::Type::NUM, "Quorum type"), + RESULT_MAP_ENTRY("memberIndex", RPCResult::Type::NUM, "Quorum member index"), RESULT_MAP_ENTRY("merkleRootMNList", RPCResult::Type::STR_HEX, "Merkle root of the masternode list"), RESULT_MAP_ENTRY("merkleRootQuorums", RPCResult::Type::STR_HEX, "Merkle root of the quorum list"), RESULT_MAP_ENTRY("operatorPayoutAddress", RPCResult::Type::STR, "Dash address used for operator reward payments"), RESULT_MAP_ENTRY("operatorReward", RPCResult::Type::NUM, "Fraction in %% of reward shared with the operator between 0 and 10000"), + RESULT_MAP_ENTRY("outpoint", RPCResult::Type::STR_HEX,"The outpoint of the masternode"), RESULT_MAP_ENTRY("ownerAddress", RPCResult::Type::STR, "Dash address used for payee updates and proposal voting"), RESULT_MAP_ENTRY("payoutAddress", RPCResult::Type::STR, "Dash address used for masternode reward payments"), RESULT_MAP_ENTRY("platformHTTPPort", RPCResult::Type::NUM, "(DEPRECATED) TCP port of Platform HTTP API"), @@ -65,17 +68,18 @@ const std::map RPCRESULT_MAP{{ RESULT_MAP_ENTRY("revocationReason", RPCResult::Type::NUM, "Reason for ProUpRegTx revocation"), RESULT_MAP_ENTRY("service", RPCResult::Type::STR, "(DEPRECATED) IP address and port of the masternode"), RESULT_MAP_ENTRY("type", RPCResult::Type::NUM, "Masternode type"), + RESULT_MAP_ENTRY("type_str", RPCResult::Type::STR, "Masternode type (human-readable string)"), RESULT_MAP_ENTRY("version", RPCResult::Type::NUM, "Special transaction version"), RESULT_MAP_ENTRY("votingAddress", RPCResult::Type::STR, "Dash address used for voting"), }}; #undef RESULT_MAP_ENTRY } // anonymous namespace -RPCResult GetRpcResult(const std::string& key, bool optional) +RPCResult GetRpcResult(const std::string& key, bool optional, const std::string& override_name) { if (const auto it = RPCRESULT_MAP.find(key); it != RPCRESULT_MAP.end()) { const auto& ret{it->second}; - return RPCResult{ret.m_type, ret.m_key_name, optional, ret.m_description, ret.m_inner}; + return RPCResult{ret.m_type, override_name.empty() ? ret.m_key_name : override_name, optional, ret.m_description, ret.m_inner}; } throw NonFatalCheckError(strprintf("Requested invalid RPCResult for nonexistent key \"%s\"", key).c_str(), __FILE__, __LINE__, __func__); @@ -178,11 +182,11 @@ RPCResult CDeterministicMN::GetJsonHelp(const std::string& key, bool optional) { return {RPCResult::Type::OBJ, key, optional, key.empty() ? "" : "The masternode's details", { - {RPCResult::Type::STR, "type", "Masternode type"}, + GetRpcResult("type_str", /*optional=*/false, /*override_name=*/"type"), GetRpcResult("proTxHash"), GetRpcResult("collateralHash"), GetRpcResult("collateralIndex"), - {RPCResult::Type::STR, "collateralAddress", /*optional=*/true, "Dash address used for collateral"}, + GetRpcResult("collateralAddress", /*optional=*/true), GetRpcResult("operatorReward"), CDeterministicMNState::GetJsonHelp(/*key=*/"state", /*optional=*/false), }}; @@ -501,7 +505,7 @@ RPCResult CSimplifiedMNListEntry::GetJsonHelp(const std::string& key, bool optio return {RPCResult::Type::OBJ, key, optional, key.empty() ? "" : "The simplified masternode list entry", { {RPCResult::Type::NUM, "nVersion", "Version of the entry"}, - {RPCResult::Type::NUM, "nType", "Masternode type"}, + GetRpcResult("type", /*optional=*/false, /*override_name=*/"nType"), {RPCResult::Type::STR_HEX, "proRegTxHash", "Hash of the ProRegTx identifying the masternode"}, {RPCResult::Type::STR_HEX, "confirmedHash", "Hash of the block where the masternode was confirmed"}, GetRpcResult("service"), diff --git a/src/governance/common.cpp b/src/governance/common.cpp index 30d000384024..8c23db0e261c 100644 --- a/src/governance/common.cpp +++ b/src/governance/common.cpp @@ -8,9 +8,7 @@ #include #include -namespace Governance -{ - +namespace Governance { Object::Object(const uint256& nHashParent, int nRevision, int64_t nTime, const uint256& nCollateralHash, const std::string& strDataHex) : hashParent{nHashParent}, revision{nRevision}, @@ -40,25 +38,6 @@ uint256 Object::GetHash() const return ss.GetHash(); } -UniValue Object::ToJson() const -{ - UniValue obj(UniValue::VOBJ); - obj.pushKV("objectHash", GetHash().ToString()); - obj.pushKV("parentHash", hashParent.ToString()); - obj.pushKV("collateralHash", collateralHash.ToString()); - obj.pushKV("createdAt", time); - obj.pushKV("revision", revision); - UniValue data; - if (!data.read(GetDataAsPlainString())) { - data.clear(); - data.setObject(); - data.pushKV("plain", GetDataAsPlainString()); - } - data.pushKV("hex", GetDataAsHexString()); - obj.pushKV("data", data); - return obj; -} - std::string Object::GetDataAsHexString() const { return HexStr(vchData); @@ -68,5 +47,4 @@ std::string Object::GetDataAsPlainString() const { return std::string(vchData.begin(), vchData.end()); } - } // namespace Governance diff --git a/src/governance/common.h b/src/governance/common.h index 48606f39b77f..4129880c8362 100644 --- a/src/governance/common.h +++ b/src/governance/common.h @@ -13,13 +13,15 @@ #include #include +struct RPCResult; + +class UniValue; + /** * This module is a public interface of governance module that can be used * in other components such as wallet */ -class UniValue; - enum class GovernanceObject : int { UNKNOWN = 0, PROPOSAL, @@ -27,8 +29,7 @@ enum class GovernanceObject : int { }; template<> struct is_serializable_enum : std::true_type {}; -namespace Governance -{ +namespace Governance { class Object { public: @@ -36,7 +37,8 @@ class Object Object(const uint256& nHashParent, int nRevision, int64_t nTime, const uint256& nCollateralHash, const std::string& strDataHex); - UniValue ToJson() const; + [[nodiscard]] static RPCResult GetJsonHelp(const std::string& key, bool optional); + [[nodiscard]] UniValue ToJson() const; uint256 GetHash() const; diff --git a/src/governance/core_write.cpp b/src/governance/core_write.cpp new file mode 100644 index 000000000000..4bb3ca996066 --- /dev/null +++ b/src/governance/core_write.cpp @@ -0,0 +1,155 @@ +// Copyright (c) 2025 The Dash Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include +#include + +#include +#include + +#include + +RPCResult CGovernanceManager::GetJsonHelp(const std::string& key, bool optional) +{ + return {RPCResult::Type::OBJ, key, optional, key.empty() ? "" : "Count of governance objects and votes", + { + {RPCResult::Type::NUM, "objects_total", "Total number of all governance objects"}, + {RPCResult::Type::NUM, "proposals", "Number of governance proposals"}, + {RPCResult::Type::NUM, "triggers", "Number of triggers"}, + {RPCResult::Type::NUM, "other", "Total number of unknown governance objects"}, + {RPCResult::Type::NUM, "erased", "Number of removed (expired) objects"}, + {RPCResult::Type::NUM, "votes", "Total number of votes"}, + }}; +} + +UniValue CGovernanceManager::ToJson() const +{ + LOCK(cs_store); + + int nProposalCount = 0; + int nTriggerCount = 0; + int nOtherCount = 0; + + for (const auto& [_, govobj] : mapObjects) { + switch (Assert(govobj)->GetObjectType()) { + case GovernanceObject::PROPOSAL: + nProposalCount++; + break; + case GovernanceObject::TRIGGER: + nTriggerCount++; + break; + default: + nOtherCount++; + break; + } + } + + UniValue jsonObj(UniValue::VOBJ); + jsonObj.pushKV("objects_total", mapObjects.size()); + jsonObj.pushKV("proposals", nProposalCount); + jsonObj.pushKV("triggers", nTriggerCount); + jsonObj.pushKV("other", nOtherCount); + jsonObj.pushKV("erased", mapErasedGovernanceObjects.size()); + jsonObj.pushKV("votes", cmapVoteToObject.GetSize()); + return jsonObj; +} + +RPCResult CGovernanceObject::GetInnerJsonHelp(const std::string& key, bool optional) +{ + return Governance::Object::GetJsonHelp(key, optional); +} + +UniValue CGovernanceObject::GetInnerJson() const +{ + return m_obj.ToJson(); +} + +// CGovernanceObject::GetStateJson() defined in governance/object.cpp +RPCResult CGovernanceObject::GetStateJsonHelp(const std::string& key, bool optional, const std::string& local_valid_key) +{ + return {RPCResult::Type::OBJ, key, optional, key.empty() ? "" : "Object state info", + { + {RPCResult::Type::STR_HEX, "DataHex", "Governance object (hex)"}, + {RPCResult::Type::STR, "DataString", "Governance object (string)"}, + {RPCResult::Type::STR_HEX, "Hash", "Hash of governance object"}, + GetRpcResult("collateralHash", /*optional=*/false, /*override_name=*/"CollateralHash"), + {RPCResult::Type::NUM, "ObjectType", "Object types"}, + {RPCResult::Type::NUM, "CreationTime", "Object creation timestamp"}, + {RPCResult::Type::STR_HEX, "SigningMasternode", /*optional=*/true, "Signing masternode’s vin (for triggers only)"}, + {RPCResult::Type::BOOL, local_valid_key, "Returns true if valid"}, + {RPCResult::Type::STR, "IsValidReason", strprintf("%s error (human-readable string, empty if %s true)", local_valid_key, local_valid_key)}, + {RPCResult::Type::BOOL, "fCachedValid", "Returns true if minimum support has been reached flagging object as valid"}, + {RPCResult::Type::BOOL, "fCachedFunding", "Returns true if minimum support has been reached flagging object as fundable"}, + {RPCResult::Type::BOOL, "fCachedDelete", "Returns true if minimum support has been reached flagging object as marked for deletion"}, + {RPCResult::Type::BOOL, "fCachedEndorsed", "Returns true if minimum support has been reached flagging object as endorsed"}, + }}; +} + +RPCResult CGovernanceObject::GetVotesJsonHelp(const std::string& key, bool optional) +{ + return {RPCResult::Type::OBJ, key, optional, key.empty() ? "" : "Object vote counts", + { + {RPCResult::Type::NUM, "AbsoluteYesCount", "Number of Yes votes minus number of No votes"}, + {RPCResult::Type::NUM, "YesCount", "Number of Yes votes"}, + {RPCResult::Type::NUM, "NoCount", "Number of No votes"}, + {RPCResult::Type::NUM, "AbstainCount", "Number of Abstain votes"}, + }}; +} + +UniValue CGovernanceObject::GetVotesJson(const CDeterministicMNList& tip_mn_list, vote_signal_enum_t signal) const +{ + UniValue obj(UniValue::VOBJ); + obj.pushKV("AbsoluteYesCount", GetAbsoluteYesCount(tip_mn_list, signal)); + obj.pushKV("YesCount", GetYesCount(tip_mn_list, signal)); + obj.pushKV("NoCount", GetNoCount(tip_mn_list, signal)); + obj.pushKV("AbstainCount", GetAbstainCount(tip_mn_list, signal)); + return obj; +} + +namespace Governance { +RPCResult Object::GetJsonHelp(const std::string& key, bool optional) +{ + return {RPCResult::Type::OBJ, key, optional, key.empty() ? "" : "Object info", + { + {RPCResult::Type::STR_HEX, "objectHash", "Hash of proposal object"}, + {RPCResult::Type::STR_HEX, "parentHash", "Hash of the parent object (root node has a hash of 0)"}, + GetRpcResult("collateralHash"), + {RPCResult::Type::NUM, "createdAt", "Proposal creation timestamp"}, + {RPCResult::Type::NUM, "revision", "Proposal revision number"}, + {RPCResult::Type::OBJ, "data", "", { + // Fields emitted through GetDataAsPlainString(), read by CProposalValidator + {RPCResult::Type::STR, "end_epoch", /*optional=*/true, "Proposal end timestamp"}, + {RPCResult::Type::STR, "name", /*optional=*/true, "Proposal name"}, + {RPCResult::Type::STR, "payment_address", /*optional=*/true, "Proposal payment address"}, + {RPCResult::Type::STR, "payment_amount", /*optional=*/true, "Proposal payment amount"}, + {RPCResult::Type::STR, "start_epoch", /*optional=*/true, "Proposal start timestamp"}, + {RPCResult::Type::STR, "type", /*optional=*/true, "Object type"}, + {RPCResult::Type::STR, "url", /*optional=*/true, "Proposal URL"}, + // Failure case for GetDataAsPlainString() + {RPCResult::Type::STR, "plain", /*optional=*/true, "Governance object data as string"}, + // Always emitted by ToJson() + {RPCResult::Type::STR_HEX, "hex", "Governance object data as hex"}, + }}, + }}; +} + +UniValue Object::ToJson() const +{ + UniValue obj(UniValue::VOBJ); + obj.pushKV("objectHash", GetHash().ToString()); + obj.pushKV("parentHash", hashParent.ToString()); + obj.pushKV("collateralHash", collateralHash.ToString()); + obj.pushKV("createdAt", time); + obj.pushKV("revision", revision); + UniValue data; + if (!data.read(GetDataAsPlainString())) { + data.clear(); + data.setObject(); + data.pushKV("plain", GetDataAsPlainString()); + } + data.pushKV("hex", GetDataAsHexString()); + obj.pushKV("data", data); + return obj; +} +} // namespace Governance diff --git a/src/governance/governance.cpp b/src/governance/governance.cpp index 2a421fd2db7a..2da13c38faf1 100644 --- a/src/governance/governance.cpp +++ b/src/governance/governance.cpp @@ -1108,38 +1108,6 @@ std::string CGovernanceManager::ToString() const return strprintf("%s, Votes: %d", GovernanceStore::ToString(), (int)cmapVoteToObject.GetSize()); } -UniValue CGovernanceManager::ToJson() const -{ - LOCK(cs_store); - - int nProposalCount = 0; - int nTriggerCount = 0; - int nOtherCount = 0; - - for (const auto& [_, govobj] : mapObjects) { - switch (Assert(govobj)->GetObjectType()) { - case GovernanceObject::PROPOSAL: - nProposalCount++; - break; - case GovernanceObject::TRIGGER: - nTriggerCount++; - break; - default: - nOtherCount++; - break; - } - } - - UniValue jsonObj(UniValue::VOBJ); - jsonObj.pushKV("objects_total", mapObjects.size()); - jsonObj.pushKV("proposals", nProposalCount); - jsonObj.pushKV("triggers", nTriggerCount); - jsonObj.pushKV("other", nOtherCount); - jsonObj.pushKV("erased", mapErasedGovernanceObjects.size()); - jsonObj.pushKV("votes", cmapVoteToObject.GetSize()); - return jsonObj; -} - void CGovernanceManager::UpdatedBlockTip(const CBlockIndex* pindex) { AssertLockNotHeld(cs_store); diff --git a/src/governance/governance.h b/src/governance/governance.h index 781ac81fe3c4..9236b8bdf086 100644 --- a/src/governance/governance.h +++ b/src/governance/governance.h @@ -31,6 +31,7 @@ template class CFlatDB; class CInv; class CNode; +struct RPCResult; class CDeterministicMNList; class CDeterministicMNManager; @@ -282,9 +283,10 @@ class CGovernanceManager : public GovernanceStore, public GovernanceSignerParent bool IsValid() const override { return is_valid; } bool LoadCache(bool load_cache) EXCLUSIVE_LOCKS_REQUIRED(!cs_store); + [[nodiscard]] static RPCResult GetJsonHelp(const std::string& key, bool optional); std::string ToString() const EXCLUSIVE_LOCKS_REQUIRED(!cs_store); - UniValue ToJson() const + [[nodiscard]] UniValue ToJson() const EXCLUSIVE_LOCKS_REQUIRED(!cs_store); void Clear() EXCLUSIVE_LOCKS_REQUIRED(!cs_store); diff --git a/src/governance/object.cpp b/src/governance/object.cpp index 173d2ab8db88..02804d532e4c 100644 --- a/src/governance/object.cpp +++ b/src/governance/object.cpp @@ -278,6 +278,28 @@ UniValue CGovernanceObject::GetJSONObject() const return obj; } +UniValue CGovernanceObject::GetStateJson(const ChainstateManager& chainman, const CDeterministicMNList& tip_mn_list, const std::string& local_valid_key) const +{ + UniValue ret(UniValue::VOBJ); + ret.pushKV("DataHex", GetDataAsHexString()); + ret.pushKV("DataString", GetDataAsPlainString()); + ret.pushKV("Hash", GetHash().ToString()); + ret.pushKV("CollateralHash", GetCollateralHash().ToString()); + ret.pushKV("ObjectType", ToUnderlying(GetObjectType())); + ret.pushKV("CreationTime", GetCreationTime()); + if (const COutPoint& outpoint = GetMasternodeOutpoint(); outpoint != COutPoint{}) { + ret.pushKV("SigningMasternode", outpoint.ToStringShort()); + } + std::string strError; + ret.pushKV(local_valid_key, WITH_LOCK(::cs_main, return IsValidLocally(tip_mn_list, chainman, strError, /*fCheckCollateral=*/false))); + ret.pushKV("IsValidReason", strError.c_str()); + ret.pushKV("fCachedValid", IsSetCachedValid()); + ret.pushKV("fCachedFunding", IsSetCachedFunding()); + ret.pushKV("fCachedDelete", IsSetCachedDelete()); + ret.pushKV("fCachedEndorsed", IsSetCachedEndorsed()); + return ret; +} + /** * LoadData * -------------------------------------------------------- @@ -341,11 +363,6 @@ std::string CGovernanceObject::GetDataAsPlainString() const return m_obj.GetDataAsPlainString(); } -UniValue CGovernanceObject::ToJson() const -{ - return m_obj.ToJson(); -} - void CGovernanceObject::UpdateLocalValidity(const CDeterministicMNList& tip_mn_list, const ChainstateManager& chainman) { AssertLockHeld(::cs_main); diff --git a/src/governance/object.h b/src/governance/object.h index 01bd7b65b35d..9e149e4ac9af 100644 --- a/src/governance/object.h +++ b/src/governance/object.h @@ -22,6 +22,7 @@ class CGovernanceObject; class CGovernanceVote; class ChainstateManager; class CMasternodeMetaMan; +struct RPCResult; extern RecursiveMutex cs_main; @@ -260,7 +261,17 @@ class CGovernanceObject // AFTER DESERIALIZATION OCCURS, CACHED VARIABLES MUST BE CALCULATED MANUALLY } - UniValue ToJson() const; + // JSON emitters/help + [[nodiscard]] static RPCResult GetInnerJsonHelp(const std::string& key, bool optional); + [[nodiscard]] UniValue GetInnerJson() const; + + [[nodiscard]] static RPCResult GetStateJsonHelp(const std::string& key, bool optional, const std::string& local_valid_key); + [[nodiscard]] UniValue GetStateJson(const ChainstateManager& chainman, const CDeterministicMNList& tip_mn_list, const std::string& local_valid_key) const + EXCLUSIVE_LOCKS_REQUIRED(!cs); + + [[nodiscard]] static RPCResult GetVotesJsonHelp(const std::string& key, bool optional); + [[nodiscard]] UniValue GetVotesJson(const CDeterministicMNList& tip_mn_list, vote_signal_enum_t signal) const + EXCLUSIVE_LOCKS_REQUIRED(!cs); // FUNCTIONS FOR DEALING WITH DATA STRING void LoadData(); diff --git a/src/llmq/core_write.cpp b/src/llmq/core_write.cpp index 5ffe3c46f956..91375c7b644c 100644 --- a/src/llmq/core_write.cpp +++ b/src/llmq/core_write.cpp @@ -3,6 +3,7 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include +#include #include #include @@ -15,6 +16,84 @@ #include namespace llmq { +// CDKGDebugSessionStatus::ToJson() defined in llmq/debug.cpp +RPCResult CDKGDebugSessionStatus::GetJsonHelp(const std::string& key, bool optional) +{ + return {RPCResult::Type::OBJ, key, optional, key.empty() ? "" : "The state of a DKG session", + { + GetRpcResult("llmqType"), + GetRpcResult("quorumHash"), + {RPCResult::Type::NUM, "quorumHeight", "Block height of the quorum"}, + {RPCResult::Type::NUM, "phase", "Active DKG phase"}, + {RPCResult::Type::BOOL, "sentContributions", "Returns true if contributions sent"}, + {RPCResult::Type::BOOL, "sentComplaint", "Returns true if complaints sent"}, + {RPCResult::Type::BOOL, "sentJustification", "Returns true if justifications sent"}, + {RPCResult::Type::BOOL, "sentPrematureCommitment", "Returns true if premature commitments sent"}, + {RPCResult::Type::BOOL, "aborted", "Returns true if DKG session aborted"}, + {RPCResult{"for detail_level = 0", RPCResult::Type::NUM, "badMembers", "Number of bad members"}}, + {RPCResult{"for detail_level = 0", RPCResult::Type::NUM, "weComplain", "Number of complaints sent"}}, + {RPCResult{"for detail_level = 0", RPCResult::Type::NUM, "receivedContributions", "Number of contributions received"}}, + {RPCResult{"for detail_level = 0", RPCResult::Type::NUM, "receivedComplaints", "Number of complaints received"}}, + {RPCResult{"for detail_level = 0", RPCResult::Type::NUM, "receivedJustifications", "Number of justifications received"}}, + {RPCResult{"for detail_level = 0", RPCResult::Type::NUM, "receivedPrematureCommitments", "Number of premature commitments received"}}, + {RPCResult{"for detail_level = 1", RPCResult::Type::ARR, "badMembers", "Array of indexes for each bad member", { + {RPCResult::Type::NUM, "", "Quorum member index"}}}}, + {RPCResult{"for detail_level = 1", RPCResult::Type::ARR, "weComplain", "Array of indexes for each complaint sent", { + {RPCResult::Type::NUM, "", "Quorum member index"}}}}, + {RPCResult{"for detail_level = 1", RPCResult::Type::ARR, "receivedContributions", "Array of indexes for each contribution received", { + {RPCResult::Type::NUM, "", "Quorum member index"}}}}, + {RPCResult{"for detail_level = 1", RPCResult::Type::ARR, "receivedComplaints", "Array of indexes for each complaint received", { + {RPCResult::Type::NUM, "", "Quorum member index"}}}}, + {RPCResult{"for detail_level = 1", RPCResult::Type::ARR, "receivedJustifications", "Array of indexes for each justification received", { + {RPCResult::Type::NUM, "", "Quorum member index"}}}}, + {RPCResult{"for detail_level = 1", RPCResult::Type::ARR, "receivedPrematureCommitments", "Array of indexes for each commitment received", { + {RPCResult::Type::NUM, "", "Quorum member index"}}}}, + {RPCResult{"for detail_level = 2", RPCResult::Type::ARR, "badMembers", "Array of objects for each bad member", { + {RPCResult::Type::OBJ, "", "", { + GetRpcResult("memberIndex"), + GetRpcResult("proTxHash", /*optional=*/true)}}}}}, + {RPCResult{"for detail_level = 2", RPCResult::Type::ARR, "weComplain", "Array of objects for each complaint sent", { + {RPCResult::Type::OBJ, "", "", { + GetRpcResult("memberIndex"), + GetRpcResult("proTxHash", /*optional=*/true)}}}}}, + {RPCResult{"for detail_level = 2", RPCResult::Type::ARR, "receivedContributions", "Array of objects for each contribution received", { + {RPCResult::Type::OBJ, "", "", { + GetRpcResult("memberIndex"), + GetRpcResult("proTxHash", /*optional=*/true)}}}}}, + {RPCResult{"for detail_level = 2", RPCResult::Type::ARR, "receivedComplaints", "Array of objects for each complaint received", { + {RPCResult::Type::OBJ, "", "", { + GetRpcResult("memberIndex"), + GetRpcResult("proTxHash", /*optional=*/true)}}}}}, + {RPCResult{"for detail_level = 2", RPCResult::Type::ARR, "receivedJustifications", "Array of objects for each justification received", { + {RPCResult::Type::OBJ, "", "", { + GetRpcResult("memberIndex"), + GetRpcResult("proTxHash", /*optional=*/true)}}}}}, + {RPCResult{"for detail_level = 2", RPCResult::Type::ARR, "receivedPrematureCommitments", "Array of objects for each commitment received", { + {RPCResult::Type::OBJ, "", "", { + GetRpcResult("memberIndex"), + GetRpcResult("proTxHash", /*optional=*/true)}}}}}, + {RPCResult{"for detail_level = 2", RPCResult::Type::ARR, "allMembers", "Array of provider registration transaction hash for all quorum members", { + GetRpcResult("proTxHash")}}}, + }}; +} + +// CDKGDebugStatus::ToJson() defined in llmq/debug.cpp +RPCResult CDKGDebugStatus::GetJsonHelp(const std::string& key, bool optional) +{ + return {RPCResult::Type::OBJ, key, optional, key.empty() ? "" : "The state of the node's DKG sessions", + { + {RPCResult::Type::NUM, "time", "Adjusted time for the last update, timestamp"}, + {RPCResult::Type::STR, "timeStr", "Adjusted time for the last update, human friendly"}, + {RPCResult::Type::ARR, "session", "", { + {RPCResult::Type::OBJ, "", "", { + {RPCResult::Type::NUM, "llmqType", "Name of quorum"}, + GetRpcResult("quorumIndex"), + CDKGDebugSessionStatus::GetJsonHelp(/*key=*/"status", /*optional=*/false) + }}, + }} + }}; +} + RPCResult CFinalCommitment::GetJsonHelp(const std::string& key, bool optional) { return {RPCResult::Type::OBJ, key, optional, key.empty() ? "" : "The quorum commitment payload", diff --git a/src/llmq/debug.h b/src/llmq/debug.h index 4ea8e597b022..cebaadbee93f 100644 --- a/src/llmq/debug.h +++ b/src/llmq/debug.h @@ -17,6 +17,7 @@ class CDeterministicMNManager; class ChainstateManager; class CInv; class CScheduler; +struct RPCResult; namespace llmq { @@ -77,8 +78,9 @@ class CDKGDebugSessionStatus public: CDKGDebugSessionStatus() : statusBitset(0) {} - UniValue ToJson(CDeterministicMNManager& dmnman, CQuorumSnapshotManager& qsnapman, - const ChainstateManager& chainman, int quorumIndex, int detailLevel) const; + [[nodiscard]] static RPCResult GetJsonHelp(const std::string& key, bool optional); + [[nodiscard]] UniValue ToJson(CDeterministicMNManager& dmnman, CQuorumSnapshotManager& qsnapman, + const ChainstateManager& chainman, int quorumIndex, int detailLevel) const; }; class CDKGDebugStatus @@ -90,8 +92,9 @@ class CDKGDebugStatus //std::map sessions; public: - UniValue ToJson(CDeterministicMNManager& dmnman, CQuorumSnapshotManager& qsnapman, - const ChainstateManager& chainman, int detailLevel) const; + [[nodiscard]] static RPCResult GetJsonHelp(const std::string& key, bool optional); + [[nodiscard]] UniValue ToJson(CDeterministicMNManager& dmnman, CQuorumSnapshotManager& qsnapman, + const ChainstateManager& chainman, int detailLevel) const; }; class CDKGDebugManager diff --git a/src/qt/governancelist.cpp b/src/qt/governancelist.cpp index 37f61fca4a06..667d55a89b22 100644 --- a/src/qt/governancelist.cpp +++ b/src/qt/governancelist.cpp @@ -110,7 +110,7 @@ void Proposal::openUrl() const QString Proposal::toJson() const { - const auto json = govObj.ToJson(); + const auto json = govObj.GetInnerJson(); return QString::fromStdString(json.write(2)); } diff --git a/src/rpc/coinjoin.cpp b/src/rpc/coinjoin.cpp index d3dd399dd35d..7eead4884ad6 100644 --- a/src/rpc/coinjoin.cpp +++ b/src/rpc/coinjoin.cpp @@ -431,7 +431,7 @@ static RPCHelpMan getcoinjoininfo() {RPCResult::Type::OBJ, "", "", { {RPCResult::Type::STR_HEX, "protxhash", "The ProTxHash of the masternode"}, - {RPCResult::Type::STR_HEX, "outpoint", "The outpoint of the masternode"}, + GetRpcResult("outpoint"), {RPCResult::Type::STR, "service", "(DEPRECATED) The IP address and port of the masternode"}, {RPCResult::Type::ARR, "addrs_core_p2p", "Network addresses of the masternode used for protocol P2P", { diff --git a/src/rpc/evo.cpp b/src/rpc/evo.cpp index deee9ffe8a78..29790e1e1e2f 100644 --- a/src/rpc/evo.cpp +++ b/src/rpc/evo.cpp @@ -1657,31 +1657,12 @@ static RPCHelpMan protx_listdiff() {RPCResult::Type::NUM, "baseHeight", "Height of base (starting) block"}, {RPCResult::Type::NUM, "blockHeight", "Height of target (ending) block"}, {RPCResult::Type::ARR, "addedMNs", "Added masternodes", - { - {RPCResult::Type::OBJ, "", "", - { - // TODO: list fields of output for RPC help instead ELISION - {RPCResult::Type::ELISION, "", ""} - }}, - }, - }, + {CDeterministicMN::GetJsonHelp(/*key=*/"", /*optional=*/false)}}, {RPCResult::Type::ARR, "removedMns", "Removed masternodes", - { - {RPCResult::Type::STR_HEX, "protx", "ProTx of removed masternode"}, - }, - }, + {{RPCResult::Type::STR_HEX, "protx", "ProTx of removed masternode"}}}, {RPCResult::Type::ARR, "updatedMNs", "Updated masternodes", - { - {RPCResult::Type::OBJ, "", "", - { - {RPCResult::Type::OBJ, "protx", "ProTx of updated masternode", - { - // TODO: list fields of output for RPC help instead ELISION - {RPCResult::Type::ELISION, "", ""} - }}, - }}, - }, - }, + {{RPCResult::Type::OBJ, "", "", + {CDeterministicMNStateDiff::GetJsonHelp(/*key=*/"", /*optional=*/false)}}}}, }, }, RPCExamples{""}, @@ -1718,7 +1699,6 @@ static RPCHelpMan protx_listdiff() for(const auto& mn : mnDiff.addedMNs) { jaddedMNs.push_back(mn->ToJson()); } - // TODO: Use CDeterministicMN::GetJsonHelp() for mn ret.pushKV("addedMNs", jaddedMNs); UniValue jremovedMNs(UniValue::VARR); @@ -1741,7 +1721,6 @@ static RPCHelpMan protx_listdiff() obj.pushKV(dmn->proTxHash.ToString(), stateDiff.ToJson(dmn->nType)); jupdatedMNs.push_back(obj); } - // TODO: Use CDeterministicMNStateDiff::GetJsonHelp() for stateDiff ret.pushKV("updatedMNs", jupdatedMNs); return ret; diff --git a/src/rpc/governance.cpp b/src/rpc/governance.cpp index 985a43818eba..6cb7e31d7659 100644 --- a/src/rpc/governance.cpp +++ b/src/rpc/governance.cpp @@ -38,23 +38,14 @@ using wallet::isminetype; static RPCHelpMan gobject_count() { + const auto json_help{CGovernanceManager::GetJsonHelp(/*key=*/"", /*optional=*/false)}; return RPCHelpMan{"gobject count", "Count governance objects and votes\n", { {"mode", RPCArg::Type::STR, RPCArg::DefaultHint{"json"}, "Output format: json (\"json\") or string in free form (\"all\")"}, }, { - RPCResult{"for mode = json", - RPCResult::Type::OBJ, "", "", - { - {RPCResult::Type::NUM, "objects_total", "Total number of all governance objects"}, - {RPCResult::Type::NUM, "proposals", "Number of governance proposals"}, - {RPCResult::Type::NUM, "triggers", "Number of triggers"}, - {RPCResult::Type::NUM, "other", "Total number of unknown governance objects"}, - {RPCResult::Type::NUM, "erased", "Number of removed (expired) objects"}, - {RPCResult::Type::NUM, "votes", "Total number of votes"}, - } - }, + RPCResult{"for mode = json", json_help.m_type, json_help.m_key_name, json_help.m_description, json_help.m_inner}, RPCResult{"for mode = all", RPCResult::Type::STR, "", "Human-friendly summary string for proposals and votes"}, }, RPCExamples{""}, @@ -275,11 +266,7 @@ static RPCHelpMan gobject_list_prepared() RPCResult{ RPCResult::Type::ARR, "", "list of governance objects", { - {RPCResult::Type::OBJ, "", "", - { - // TODO: list fields of output for RPC help instead ELISION - {RPCResult::Type::ELISION, "", ""} - }}, + Governance::Object::GetJsonHelp(/*key=*/"", /*optional=*/false) } }, RPCExamples{""}, @@ -636,22 +623,15 @@ static RPCHelpMan gobject_vote_alias() static UniValue ListObjects(CGovernanceManager& govman, const CDeterministicMNList& tip_mn_list, const ChainstateManager& chainman, const std::string& strCachedSignal, const std::string& strType, int nStartTime) { - UniValue objResult(UniValue::VOBJ); - - // GET MATCHING GOVERNANCE OBJECTS - if (g_txindex) { g_txindex->BlockUntilSyncedToCurrentChain(); } - LOCK(cs_main); - std::vector objs; govman.GetAllNewerThan(objs, nStartTime); - govman.UpdateLastDiffTime(GetTime()); - // CREATE RESULTS FOR USER + UniValue objResult(UniValue::VOBJ); for (const auto& govObj : objs) { if (strCachedSignal == "valid" && !govObj.IsSetCachedValid()) continue; if (strCachedSignal == "funding" && !govObj.IsSetCachedFunding()) continue; @@ -661,40 +641,28 @@ static UniValue ListObjects(CGovernanceManager& govman, const CDeterministicMNLi if (strType == "proposals" && govObj.GetObjectType() != GovernanceObject::PROPOSAL) continue; if (strType == "triggers" && govObj.GetObjectType() != GovernanceObject::TRIGGER) continue; - UniValue bObj(UniValue::VOBJ); - bObj.pushKV("DataHex", govObj.GetDataAsHexString()); - bObj.pushKV("DataString", govObj.GetDataAsPlainString()); - bObj.pushKV("Hash", govObj.GetHash().ToString()); - bObj.pushKV("CollateralHash", govObj.GetCollateralHash().ToString()); - bObj.pushKV("ObjectType", ToUnderlying(govObj.GetObjectType())); - bObj.pushKV("CreationTime", govObj.GetCreationTime()); - const COutPoint& masternodeOutpoint = govObj.GetMasternodeOutpoint(); - if (masternodeOutpoint != COutPoint()) { - bObj.pushKV("SigningMasternode", masternodeOutpoint.ToStringShort()); - } - - // REPORT STATUS FOR FUNDING VOTES SPECIFICALLY - bObj.pushKV("AbsoluteYesCount", govObj.GetAbsoluteYesCount(tip_mn_list, VOTE_SIGNAL_FUNDING)); - bObj.pushKV("YesCount", govObj.GetYesCount(tip_mn_list, VOTE_SIGNAL_FUNDING)); - bObj.pushKV("NoCount", govObj.GetNoCount(tip_mn_list, VOTE_SIGNAL_FUNDING)); - bObj.pushKV("AbstainCount", govObj.GetAbstainCount(tip_mn_list, VOTE_SIGNAL_FUNDING)); - - // REPORT VALIDITY AND CACHING FLAGS FOR VARIOUS SETTINGS - std::string strError; - bObj.pushKV("fBlockchainValidity", govObj.IsValidLocally(tip_mn_list, chainman, strError, false)); - bObj.pushKV("IsValidReason", strError.c_str()); - bObj.pushKV("fCachedValid", govObj.IsSetCachedValid()); - bObj.pushKV("fCachedFunding", govObj.IsSetCachedFunding()); - bObj.pushKV("fCachedDelete", govObj.IsSetCachedDelete()); - bObj.pushKV("fCachedEndorsed", govObj.IsSetCachedEndorsed()); - - objResult.pushKV(govObj.GetHash().ToString(), bObj); + UniValue entry{govObj.GetStateJson(chainman, tip_mn_list, /*local_valid_key=*/"fBlockchainValidity")}; + UniValue votes_funding{govObj.GetVotesJson(tip_mn_list, VOTE_SIGNAL_FUNDING)}; + entry.pushKV("AbsoluteYesCount", votes_funding["AbsoluteYesCount"]); + entry.pushKV("YesCount", votes_funding["YesCount"]); + entry.pushKV("NoCount", votes_funding["NoCount"]); + entry.pushKV("AbstainCount", votes_funding["AbstainCount"]); + objResult.pushKV(govObj.GetHash().ToString(), entry); } return objResult; } -// USERS CAN QUERY THE SYSTEM FOR A LIST OF VARIOUS GOVERNANCE ITEMS +static RPCResult ListObjectsHelp() +{ + auto ret = CGovernanceObject::GetStateJsonHelp(/*key=*/"", /*optional=*/false, /*local_valid_key=*/"fBlockchainValidity"); + auto mod_inner = ret.m_inner; + for (const auto& result : CGovernanceObject::GetVotesJsonHelp(/*key=*/"", /*optional=*/false).m_inner) { + mod_inner.push_back(result); + } + return RPCResult{ret.m_type, ret.m_key_name, ret.m_description, mod_inner}; +} + static RPCHelpMan gobject_list_helper(const bool make_a_diff) { const std::string command{make_a_diff ? "gobject diff" : "gobject list"}; @@ -709,11 +677,7 @@ static RPCHelpMan gobject_list_helper(const bool make_a_diff) }, { RPCResult{"If request is valid", - RPCResult::Type::OBJ, "hash", "Object details", - { - // TODO: list fields of output for RPC help instead ELISION - {RPCResult::Type::ELISION, "", ""} - }, + RPCResult::Type::OBJ, "hash", "Object details", {ListObjectsHelp()}, }, RPCResult{"If request is invalid", RPCResult::Type::STR, "", "Error string" @@ -756,7 +720,17 @@ static RPCHelpMan gobject_diff() return gobject_list_helper(true); } -// GET SPECIFIC GOVERNANCE ENTRY +static RPCResult gobject_get_help() +{ + auto ret = CGovernanceObject::GetStateJsonHelp(/*key=*/"", /*optional=*/false, /*local_valid_key=*/"fLocalValidity"); + auto mod_inner = ret.m_inner; + mod_inner.push_back({RPCResult::Type::OBJ, "FundingResult", "Funding vote details", {CGovernanceObject::GetVotesJsonHelp(/*key=*/"", /*optional=*/false)}}); + mod_inner.push_back({RPCResult::Type::OBJ, "ValidResult", "Object validity vote details", {CGovernanceObject::GetVotesJsonHelp(/*key=*/"", /*optional=*/false)}}); + mod_inner.push_back({RPCResult::Type::OBJ, "DeleteResult", "Delete vote details", {CGovernanceObject::GetVotesJsonHelp(/*key=*/"", /*optional=*/false)}}); + mod_inner.push_back({RPCResult::Type::OBJ, "EndorsedResult", "Endorsed vote details", {CGovernanceObject::GetVotesJsonHelp(/*key=*/"", /*optional=*/false)}}); + return RPCResult{ret.m_type, ret.m_key_name, ret.m_description, mod_inner}; +} + static RPCHelpMan gobject_get() { return RPCHelpMan{"gobject get", @@ -764,96 +738,30 @@ static RPCHelpMan gobject_get() { {"governance-hash", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "object id"}, }, - { - RPCResult{ - RPCResult::Type::OBJ, "", "", - { - // TODO: list fields of output for RPC help instead ELISION - {RPCResult::Type::ELISION, "", ""} - } - }, - }, + gobject_get_help(), RPCExamples{""}, [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue { - // COLLECT VARIABLES FROM OUR USER - uint256 hash(ParseHashV(request.params[0], "GovObj hash")); - if (g_txindex) { g_txindex->BlockUntilSyncedToCurrentChain(); } - // FIND THE GOVERNANCE OBJECT THE USER IS LOOKING FOR const NodeContext& node = EnsureAnyNodeContext(request.context); const ChainstateManager& chainman = EnsureChainman(node); - CHECK_NONFATAL(node.govman); - LOCK(cs_main); - auto pGovObj = node.govman->FindConstGovernanceObject(hash); - + uint256 hash(ParseHashV(request.params[0], "GovObj hash")); + auto pGovObj = CHECK_NONFATAL(node.govman)->FindConstGovernanceObject(hash); if (pGovObj == nullptr) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Unknown governance object"); } - // REPORT BASIC OBJECT STATS - - UniValue objResult(UniValue::VOBJ); - objResult.pushKV("DataHex", pGovObj->GetDataAsHexString()); - objResult.pushKV("DataString", pGovObj->GetDataAsPlainString()); - objResult.pushKV("Hash", pGovObj->GetHash().ToString()); - objResult.pushKV("CollateralHash", pGovObj->GetCollateralHash().ToString()); - objResult.pushKV("ObjectType", ToUnderlying(pGovObj->GetObjectType())); - objResult.pushKV("CreationTime", pGovObj->GetCreationTime()); - const COutPoint& masternodeOutpoint = pGovObj->GetMasternodeOutpoint(); - if (masternodeOutpoint != COutPoint()) { - objResult.pushKV("SigningMasternode", masternodeOutpoint.ToStringShort()); - } - - // SHOW (MUCH MORE) INFORMATION ABOUT VOTES FOR GOVERNANCE OBJECT (THAN LIST/DIFF ABOVE) - // -- FUNDING VOTING RESULTS - auto tip_mn_list = CHECK_NONFATAL(node.dmnman)->GetListAtChainTip(); - - UniValue objFundingResult(UniValue::VOBJ); - objFundingResult.pushKV("AbsoluteYesCount", pGovObj->GetAbsoluteYesCount(tip_mn_list, VOTE_SIGNAL_FUNDING)); - objFundingResult.pushKV("YesCount", pGovObj->GetYesCount(tip_mn_list, VOTE_SIGNAL_FUNDING)); - objFundingResult.pushKV("NoCount", pGovObj->GetNoCount(tip_mn_list, VOTE_SIGNAL_FUNDING)); - objFundingResult.pushKV("AbstainCount", pGovObj->GetAbstainCount(tip_mn_list, VOTE_SIGNAL_FUNDING)); - objResult.pushKV("FundingResult", objFundingResult); - - // -- VALIDITY VOTING RESULTS - UniValue objValid(UniValue::VOBJ); - objValid.pushKV("AbsoluteYesCount", pGovObj->GetAbsoluteYesCount(tip_mn_list, VOTE_SIGNAL_VALID)); - objValid.pushKV("YesCount", pGovObj->GetYesCount(tip_mn_list, VOTE_SIGNAL_VALID)); - objValid.pushKV("NoCount", pGovObj->GetNoCount(tip_mn_list, VOTE_SIGNAL_VALID)); - objValid.pushKV("AbstainCount", pGovObj->GetAbstainCount(tip_mn_list, VOTE_SIGNAL_VALID)); - objResult.pushKV("ValidResult", objValid); - - // -- DELETION CRITERION VOTING RESULTS - UniValue objDelete(UniValue::VOBJ); - objDelete.pushKV("AbsoluteYesCount", pGovObj->GetAbsoluteYesCount(tip_mn_list, VOTE_SIGNAL_DELETE)); - objDelete.pushKV("YesCount", pGovObj->GetYesCount(tip_mn_list, VOTE_SIGNAL_DELETE)); - objDelete.pushKV("NoCount", pGovObj->GetNoCount(tip_mn_list, VOTE_SIGNAL_DELETE)); - objDelete.pushKV("AbstainCount", pGovObj->GetAbstainCount(tip_mn_list, VOTE_SIGNAL_DELETE)); - objResult.pushKV("DeleteResult", objDelete); - - // -- ENDORSED VIA MASTERNODE-ELECTED BOARD - UniValue objEndorsed(UniValue::VOBJ); - objEndorsed.pushKV("AbsoluteYesCount", pGovObj->GetAbsoluteYesCount(tip_mn_list, VOTE_SIGNAL_ENDORSED)); - objEndorsed.pushKV("YesCount", pGovObj->GetYesCount(tip_mn_list, VOTE_SIGNAL_ENDORSED)); - objEndorsed.pushKV("NoCount", pGovObj->GetNoCount(tip_mn_list, VOTE_SIGNAL_ENDORSED)); - objEndorsed.pushKV("AbstainCount", pGovObj->GetAbstainCount(tip_mn_list, VOTE_SIGNAL_ENDORSED)); - objResult.pushKV("EndorsedResult", objEndorsed); - - // -- - std::string strError; - objResult.pushKV("fLocalValidity", pGovObj->IsValidLocally(tip_mn_list, chainman, strError, false)); - objResult.pushKV("IsValidReason", strError.c_str()); - objResult.pushKV("fCachedValid", pGovObj->IsSetCachedValid()); - objResult.pushKV("fCachedFunding", pGovObj->IsSetCachedFunding()); - objResult.pushKV("fCachedDelete", pGovObj->IsSetCachedDelete()); - objResult.pushKV("fCachedEndorsed", pGovObj->IsSetCachedEndorsed()); - return objResult; + UniValue ret{pGovObj->GetStateJson(chainman, tip_mn_list, /*local_valid_key=*/"fLocalValidity")}; + ret.pushKV("FundingResult", pGovObj->GetVotesJson(tip_mn_list, VOTE_SIGNAL_FUNDING)); + ret.pushKV("ValidResult", pGovObj->GetVotesJson(tip_mn_list, VOTE_SIGNAL_VALID)); + ret.pushKV("DeleteResult", pGovObj->GetVotesJson(tip_mn_list, VOTE_SIGNAL_DELETE)); + ret.pushKV("EndorsedResult", pGovObj->GetVotesJson(tip_mn_list, VOTE_SIGNAL_ENDORSED)); + return ret; }, }; } diff --git a/src/rpc/masternode.cpp b/src/rpc/masternode.cpp index 7a8537da3356..f44228e23f38 100644 --- a/src/rpc/masternode.cpp +++ b/src/rpc/masternode.cpp @@ -182,8 +182,15 @@ static RPCHelpMan masternode_status() RPCResult{ RPCResult::Type::OBJ, "", "", { - // TODO: implement proper type validator instead ELISION - {RPCResult::Type::ELISION, "", ""} + GetRpcResult("outpoint"), + GetRpcResult("service"), + GetRpcResult("proTxHash", /*optional=*/true), + GetRpcResult("type_str", /*optional=*/true, /*override_name=*/"type"), + GetRpcResult("collateralHash", /*optional=*/true), + GetRpcResult("collateralIndex", /*optional=*/true), + CDeterministicMNState::GetJsonHelp(/*key=*/"dmnState", /*optional=*/true), + {RPCResult::Type::STR, "state", "Masternode state (human-readable string)"}, + {RPCResult::Type::STR, "status", "Masternode status (human-readable string, based on current state)"}, } }, RPCExamples{""}, @@ -205,7 +212,6 @@ static RPCHelpMan masternode_status() mnObj.pushKV("type", std::string(GetMnType(dmn->nType).description)); mnObj.pushKV("collateralHash", dmn->collateralOutpoint.hash.ToString()); mnObj.pushKV("collateralIndex", dmn->collateralOutpoint.n); - // TODO: Use CDeterministicMNState::GetJsonHelp() for dmnState mnObj.pushKV("dmnState", dmn->pdmnState->ToJson(dmn->nType)); } mnObj.pushKV("state", node.mn_activeman->GetStateString()); @@ -525,10 +531,36 @@ static RPCHelpMan masternodelist_helper(bool is_composite) {"filter", RPCArg::Type::STR, RPCArg::Default{""}, "Filter results. Partial match by outpoint by default in all modes, additional matches in some modes are also available"}, }, RPCResult{ - RPCResult::Type::OBJ, "", "", - { - // TODO: implement proper type validator instead ELISION - {RPCResult::Type::ELISION, "", ""} + RPCResult::Type::OBJ, "", "", { + RPCResult{"for mode = addr", RPCResult::Type::STR, "
", "Flattened list of all addresses registered to masternode"}, + RPCResult{"for mode = full", RPCResult::Type::STR, "", "Flattened list of a masternode's status, payee address, last paid block's timestamp, height and service addresses"}, + RPCResult{"for mode = info", RPCResult::Type::STR, "", "Flattened list of a masternode's status, payee address and service addresses"}, + RPCResult{"for mode = evo, json or recent", RPCResult::Type::OBJ, "", "", { + GetRpcResult("proTxHash"), + GetRpcResult("service", /*optional=*/false, /*override_name=*/"address"), + GetRpcResult("addresses"), + GetRpcResult("payoutAddress", /*optional=*/false, /*override_name=*/"payee"), + {RPCResult::Type::STR, "status", "Masternode status (human-readable string)"}, + GetRpcResult("type_str", /*optional=*/false, /*override_name=*/"type"), + GetRpcResult("platformNodeID", /*optional=*/true), + GetRpcResult("platformP2PPort", /*optional=*/true), + GetRpcResult("platformHTTPPort", /*optional=*/true), + GetRpcResult("PoSePenalty", /*optional=*/false, /*override_name=*/"pospenaltyscore"), + GetRpcResult("consecutivePayments"), + {RPCResult::Type::NUM, "lastpaidtime", "Timestamp of block the masternode was last paid"}, + GetRpcResult("lastPaidHeight", /*optional=*/false, /*override_name=*/"lastpaidblock"), + GetRpcResult("ownerAddress", /*optional=*/false, /*override_name=*/"owneraddress"), + GetRpcResult("votingAddress", /*optional=*/false, /*override_name=*/"votingaddress"), + GetRpcResult("collateralAddress", /*optional=*/false, /*override_name=*/"collateraladdress"), + GetRpcResult("pubKeyOperator", /*optional=*/false, /*override_name=*/"pubkeyoperator"), + }}, + RPCResult{"for mode = lastpaidblock", RPCResult::Type::NUM, "", "Height masternode was last paid"}, + RPCResult{"for mode = lastpaidtime", RPCResult::Type::NUM, "