Skip to content
1 change: 1 addition & 0 deletions src/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -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 \
Expand Down
2 changes: 1 addition & 1 deletion src/core_io.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
14 changes: 9 additions & 5 deletions src/evo/core_write.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,17 +37,20 @@ const std::map<std::string, RPCResult> 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"),
RESULT_MAP_ENTRY("height", RPCResult::Type::NUM, "Block height"),
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"),
Expand All @@ -65,17 +68,18 @@ const std::map<std::string, RPCResult> 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__);
Expand Down Expand Up @@ -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),
}};
Expand Down Expand Up @@ -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"),
Expand Down
24 changes: 1 addition & 23 deletions src/governance/common.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,7 @@
#include <hash.h>
#include <univalue.h>

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},
Expand Down Expand Up @@ -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);
Expand All @@ -68,5 +47,4 @@ std::string Object::GetDataAsPlainString() const
{
return std::string(vchData.begin(), vchData.end());
}

} // namespace Governance
12 changes: 7 additions & 5 deletions src/governance/common.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,30 +13,32 @@
#include <string>
#include <vector>

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,
TRIGGER
};
template<> struct is_serializable_enum<GovernanceObject> : std::true_type {};

namespace Governance
{
namespace Governance {
class Object
{
public:
Object() = default;

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;

Expand Down
155 changes: 155 additions & 0 deletions src/governance/core_write.cpp
Original file line number Diff line number Diff line change
@@ -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 <governance/common.h>
#include <governance/governance.h>

#include <rpc/util.h>
#include <util/check.h>

#include <univalue.h>

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
32 changes: 0 additions & 32 deletions src/governance/governance.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
4 changes: 3 additions & 1 deletion src/governance/governance.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ template<typename T>
class CFlatDB;
class CInv;
class CNode;
struct RPCResult;

class CDeterministicMNList;
class CDeterministicMNManager;
Expand Down Expand Up @@ -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);
Expand Down
Loading
Loading