Line data Source code
1 : // Copyright (c) 2014-2025 The Dash Core developers
2 : // Distributed under the MIT/X11 software license, see the accompanying
3 : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
4 :
5 : #include <governance/vote.h>
6 :
7 : #include <bls/bls.h>
8 : #include <evo/deterministicmns.h>
9 : #include <evo/dmn_types.h>
10 : #include <masternode/sync.h>
11 : #include <messagesigner.h>
12 :
13 : #include <chainparams.h>
14 : #include <logging.h>
15 : #include <timedata.h>
16 : #include <util/string.h>
17 :
18 1667 : std::string CGovernanceVoting::ConvertOutcomeToString(vote_outcome_enum_t nOutcome)
19 : {
20 1667 : static const std::map<vote_outcome_enum_t, std::string> mapOutcomeString = {
21 44 : { VOTE_OUTCOME_NONE, "none" },
22 44 : { VOTE_OUTCOME_YES, "yes" },
23 44 : { VOTE_OUTCOME_NO, "no" },
24 44 : { VOTE_OUTCOME_ABSTAIN, "abstain" } };
25 :
26 1667 : const auto& it = mapOutcomeString.find(nOutcome);
27 1667 : if (it == mapOutcomeString.end()) {
28 0 : LogPrintf("CGovernanceVoting::%s -- ERROR: Unknown outcome %d\n", __func__, nOutcome);
29 0 : return "error";
30 : }
31 1667 : return it->second;
32 1667 : }
33 :
34 1224 : std::string CGovernanceVoting::ConvertSignalToString(vote_signal_enum_t nSignal)
35 : {
36 1224 : static const std::map<vote_signal_enum_t, std::string> mapSignalsString = {
37 44 : { VOTE_SIGNAL_FUNDING, "funding" },
38 44 : { VOTE_SIGNAL_VALID, "valid" },
39 44 : { VOTE_SIGNAL_DELETE, "delete" },
40 44 : { VOTE_SIGNAL_ENDORSED, "endorsed" } };
41 :
42 1224 : const auto& it = mapSignalsString.find(nSignal);
43 1224 : if (it == mapSignalsString.end()) {
44 0 : LogPrintf("CGovernanceVoting::%s -- ERROR: Unknown signal %d\n", __func__, nSignal);
45 0 : return "none";
46 : }
47 1224 : return it->second;
48 1224 : }
49 :
50 :
51 40 : vote_outcome_enum_t CGovernanceVoting::ConvertVoteOutcome(const std::string& strVoteOutcome)
52 : {
53 40 : static const std::map<std::string, vote_outcome_enum_t> mapStringOutcome = {
54 8 : { "none", VOTE_OUTCOME_NONE },
55 8 : { "yes", VOTE_OUTCOME_YES },
56 8 : { "no", VOTE_OUTCOME_NO },
57 8 : { "abstain", VOTE_OUTCOME_ABSTAIN } };
58 :
59 40 : const auto& it = mapStringOutcome.find(strVoteOutcome);
60 40 : if (it == mapStringOutcome.end()) {
61 0 : LogPrintf("CGovernanceVoting::%s -- ERROR: Unknown outcome %s\n", __func__, strVoteOutcome);
62 0 : return VOTE_OUTCOME_NONE;
63 : }
64 40 : return it->second;
65 :
66 40 : }
67 :
68 40 : vote_signal_enum_t CGovernanceVoting::ConvertVoteSignal(const std::string& strVoteSignal)
69 : {
70 40 : static const std::map<std::string, vote_signal_enum_t> mapStrVoteSignals = {
71 8 : {"funding", VOTE_SIGNAL_FUNDING},
72 8 : {"valid", VOTE_SIGNAL_VALID},
73 8 : {"delete", VOTE_SIGNAL_DELETE},
74 8 : {"endorsed", VOTE_SIGNAL_ENDORSED}};
75 :
76 40 : const auto& it = mapStrVoteSignals.find(strVoteSignal);
77 40 : if (it == mapStrVoteSignals.end()) {
78 0 : LogPrintf("CGovernanceVoting::%s -- ERROR: Unknown signal %s\n", __func__, strVoteSignal);
79 0 : return VOTE_SIGNAL_NONE;
80 : }
81 40 : return it->second;
82 40 : }
83 :
84 828 : CGovernanceVote::CGovernanceVote(const COutPoint& outpointMasternodeIn, const uint256& nParentHashIn,
85 : vote_signal_enum_t eVoteSignalIn, vote_outcome_enum_t eVoteOutcomeIn) :
86 276 : masternodeOutpoint(outpointMasternodeIn),
87 276 : nParentHash(nParentHashIn),
88 276 : nVoteOutcome(eVoteOutcomeIn),
89 276 : nVoteSignal(eVoteSignalIn),
90 276 : nTime(GetAdjustedTime())
91 276 : {
92 : UpdateHash();
93 276 : }
94 :
95 1224 : std::string CGovernanceVote::ToString(const CDeterministicMNList& tip_mn_list) const
96 : {
97 1224 : auto dmn = tip_mn_list.GetMNByCollateral(masternodeOutpoint);
98 1224 : int voteWeight = dmn != nullptr ? GetMnType(dmn->nType).voting_weight : 0;
99 1224 : return strprintf("%s:%d:%s:%s:%d",
100 1224 : masternodeOutpoint.ToStringShort(), nTime,
101 1224 : CGovernanceVoting::ConvertOutcomeToString(GetOutcome()), CGovernanceVoting::ConvertSignalToString(GetSignal()),
102 : voteWeight);
103 1224 : }
104 :
105 1656 : void CGovernanceVote::UpdateHash() const
106 : {
107 : // Note: doesn't match serialization
108 :
109 1656 : CHashWriter ss(SER_GETHASH, PROTOCOL_VERSION);
110 1656 : ss << masternodeOutpoint << uint8_t{} << 0xffffffff; // adding dummy values here to match old hashing format
111 1656 : ss << nParentHash;
112 1656 : ss << nVoteSignal;
113 1656 : ss << nVoteOutcome;
114 1656 : ss << nTime;
115 1656 : *const_cast<uint256*>(&hash) = ss.GetHash();
116 1656 : }
117 :
118 313411 : uint256 CGovernanceVote::GetHash() const
119 : {
120 313411 : return hash;
121 : }
122 :
123 :
124 680 : bool CGovernanceVote::CheckSignature(const CKeyID& keyID) const
125 : {
126 680 : std::string strError;
127 :
128 : // Harden Spork6 so that it is active on testnet and no other networks
129 680 : if (Params().NetworkIDString() == CBaseChainParams::TESTNET) {
130 0 : if (!CHashSigner::VerifyHash(GetSignatureHash(), keyID, vchSig, strError)) {
131 0 : LogPrint(BCLog::GOBJECT, "CGovernanceVote::IsValid -- VerifyHash() failed, error: %s\n", strError);
132 0 : return false;
133 : }
134 0 : } else {
135 680 : if (!CMessageSigner::VerifyMessage(keyID, vchSig, GetSignatureString(), strError)) {
136 0 : LogPrint(BCLog::GOBJECT, "CGovernanceVote::IsValid -- VerifyMessage() failed, error: %s\n", strError);
137 0 : return false;
138 : }
139 : }
140 :
141 680 : return true;
142 680 : }
143 :
144 952 : bool CGovernanceVote::CheckSignature(const CBLSPublicKey& pubKey) const
145 : {
146 952 : CBLSSignature sig;
147 952 : sig.SetBytes(vchSig, false);
148 952 : if (!sig.VerifyInsecure(pubKey, GetSignatureHash(), false)) {
149 0 : LogPrintf("CGovernanceVote::CheckSignature -- VerifyInsecure() failed\n");
150 0 : return false;
151 : }
152 952 : return true;
153 952 : }
154 :
155 1512 : bool CGovernanceVote::IsValid(const CDeterministicMNList& tip_mn_list, bool useVotingKey) const
156 : {
157 1512 : if (nTime > GetAdjustedTime() + (60 * 60)) {
158 0 : LogPrint(BCLog::GOBJECT, "CGovernanceVote::IsValid -- vote is too far ahead of current time - %s - nTime %lli - Max Time %lli\n", GetHash().ToString(), nTime, GetAdjustedTime() + (60 * 60));
159 0 : return false;
160 : }
161 :
162 1512 : if (nVoteSignal < VOTE_SIGNAL_NONE || nVoteSignal >= VOTE_SIGNAL_UNKNOWN) {
163 0 : LogPrint(BCLog::GOBJECT, "CGovernanceVote::IsValid -- Client attempted to vote on invalid signal(%d) - %s\n",
164 : nVoteSignal, GetHash().ToString());
165 0 : return false;
166 : }
167 :
168 1512 : if (nVoteOutcome < VOTE_OUTCOME_NONE || nVoteOutcome >= VOTE_OUTCOME_UNKNOWN) {
169 0 : LogPrint(BCLog::GOBJECT, "CGovernanceVote::IsValid -- Client attempted to vote on invalid outcome(%d) - %s\n",
170 : nVoteOutcome, GetHash().ToString());
171 0 : return false;
172 : }
173 :
174 1512 : auto dmn = tip_mn_list.GetMNByCollateral(masternodeOutpoint);
175 1512 : if (!dmn) {
176 0 : LogPrint(BCLog::GOBJECT, "CGovernanceVote::IsValid -- Unknown Masternode - %s\n", masternodeOutpoint.ToStringShort());
177 0 : return false;
178 : }
179 :
180 1512 : if (useVotingKey) {
181 560 : return CheckSignature(dmn->pdmnState->keyIDVoting);
182 : } else {
183 952 : return CheckSignature(dmn->pdmnState->pubKeyOperator.Get());
184 : }
185 1512 : }
186 :
187 :
188 0 : bool operator==(const CGovernanceVote& vote1, const CGovernanceVote& vote2)
189 : {
190 0 : bool fResult = ((vote1.masternodeOutpoint == vote2.masternodeOutpoint) &&
191 0 : (vote1.nParentHash == vote2.nParentHash) &&
192 0 : (vote1.nVoteOutcome == vote2.nVoteOutcome) &&
193 0 : (vote1.nVoteSignal == vote2.nVoteSignal) &&
194 0 : (vote1.nTime == vote2.nTime));
195 0 : return fResult;
196 : }
197 :
198 0 : bool operator<(const CGovernanceVote& vote1, const CGovernanceVote& vote2)
199 : {
200 0 : bool fResult = (vote1.masternodeOutpoint < vote2.masternodeOutpoint);
201 0 : if (!fResult) {
202 0 : return false;
203 : }
204 0 : fResult = (vote1.masternodeOutpoint == vote2.masternodeOutpoint);
205 :
206 0 : fResult = fResult && (vote1.nParentHash < vote2.nParentHash);
207 0 : if (!fResult) {
208 0 : return false;
209 : }
210 0 : fResult = (vote1.nParentHash == vote2.nParentHash);
211 :
212 0 : fResult = fResult && (vote1.nVoteOutcome < vote2.nVoteOutcome);
213 0 : if (!fResult) {
214 0 : return false;
215 : }
216 0 : fResult = (vote1.nVoteOutcome == vote2.nVoteOutcome);
217 :
218 0 : fResult = fResult && (vote1.nVoteSignal == vote2.nVoteSignal);
219 0 : if (!fResult) {
220 0 : return false;
221 : }
222 0 : fResult = (vote1.nVoteSignal == vote2.nVoteSignal);
223 :
224 0 : fResult = fResult && (vote1.nTime < vote2.nTime);
225 :
226 0 : return fResult;
227 0 : }
|