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 0 : std::string CGovernanceVoting::ConvertOutcomeToString(vote_outcome_enum_t nOutcome)
19 : {
20 0 : static const std::map<vote_outcome_enum_t, std::string> mapOutcomeString = {
21 0 : { VOTE_OUTCOME_NONE, "none" },
22 0 : { VOTE_OUTCOME_YES, "yes" },
23 0 : { VOTE_OUTCOME_NO, "no" },
24 0 : { VOTE_OUTCOME_ABSTAIN, "abstain" } };
25 :
26 0 : const auto& it = mapOutcomeString.find(nOutcome);
27 0 : if (it == mapOutcomeString.end()) {
28 0 : LogPrintf("CGovernanceVoting::%s -- ERROR: Unknown outcome %d\n", __func__, nOutcome);
29 0 : return "error";
30 : }
31 0 : return it->second;
32 0 : }
33 :
34 0 : std::string CGovernanceVoting::ConvertSignalToString(vote_signal_enum_t nSignal)
35 : {
36 0 : static const std::map<vote_signal_enum_t, std::string> mapSignalsString = {
37 0 : { VOTE_SIGNAL_FUNDING, "funding" },
38 0 : { VOTE_SIGNAL_VALID, "valid" },
39 0 : { VOTE_SIGNAL_DELETE, "delete" },
40 0 : { VOTE_SIGNAL_ENDORSED, "endorsed" } };
41 :
42 0 : const auto& it = mapSignalsString.find(nSignal);
43 0 : if (it == mapSignalsString.end()) {
44 0 : LogPrintf("CGovernanceVoting::%s -- ERROR: Unknown signal %d\n", __func__, nSignal);
45 0 : return "none";
46 : }
47 0 : return it->second;
48 0 : }
49 :
50 :
51 0 : vote_outcome_enum_t CGovernanceVoting::ConvertVoteOutcome(const std::string& strVoteOutcome)
52 : {
53 0 : static const std::map<std::string, vote_outcome_enum_t> mapStringOutcome = {
54 0 : { "none", VOTE_OUTCOME_NONE },
55 0 : { "yes", VOTE_OUTCOME_YES },
56 0 : { "no", VOTE_OUTCOME_NO },
57 0 : { "abstain", VOTE_OUTCOME_ABSTAIN } };
58 :
59 0 : const auto& it = mapStringOutcome.find(strVoteOutcome);
60 0 : if (it == mapStringOutcome.end()) {
61 0 : LogPrintf("CGovernanceVoting::%s -- ERROR: Unknown outcome %s\n", __func__, strVoteOutcome);
62 0 : return VOTE_OUTCOME_NONE;
63 : }
64 0 : return it->second;
65 :
66 0 : }
67 :
68 0 : vote_signal_enum_t CGovernanceVoting::ConvertVoteSignal(const std::string& strVoteSignal)
69 : {
70 0 : static const std::map<std::string, vote_signal_enum_t> mapStrVoteSignals = {
71 0 : {"funding", VOTE_SIGNAL_FUNDING},
72 0 : {"valid", VOTE_SIGNAL_VALID},
73 0 : {"delete", VOTE_SIGNAL_DELETE},
74 0 : {"endorsed", VOTE_SIGNAL_ENDORSED}};
75 :
76 0 : const auto& it = mapStrVoteSignals.find(strVoteSignal);
77 0 : if (it == mapStrVoteSignals.end()) {
78 0 : LogPrintf("CGovernanceVoting::%s -- ERROR: Unknown signal %s\n", __func__, strVoteSignal);
79 0 : return VOTE_SIGNAL_NONE;
80 : }
81 0 : return it->second;
82 0 : }
83 :
84 0 : CGovernanceVote::CGovernanceVote(const COutPoint& outpointMasternodeIn, const uint256& nParentHashIn,
85 : vote_signal_enum_t eVoteSignalIn, vote_outcome_enum_t eVoteOutcomeIn) :
86 0 : masternodeOutpoint(outpointMasternodeIn),
87 0 : nParentHash(nParentHashIn),
88 0 : nVoteOutcome(eVoteOutcomeIn),
89 0 : nVoteSignal(eVoteSignalIn),
90 0 : nTime(GetAdjustedTime())
91 0 : {
92 : UpdateHash();
93 0 : }
94 :
95 0 : std::string CGovernanceVote::ToString(const CDeterministicMNList& tip_mn_list) const
96 : {
97 0 : auto dmn = tip_mn_list.GetMNByCollateral(masternodeOutpoint);
98 0 : int voteWeight = dmn != nullptr ? GetMnType(dmn->nType).voting_weight : 0;
99 0 : return strprintf("%s:%d:%s:%s:%d",
100 0 : masternodeOutpoint.ToStringShort(), nTime,
101 0 : CGovernanceVoting::ConvertOutcomeToString(GetOutcome()), CGovernanceVoting::ConvertSignalToString(GetSignal()),
102 : voteWeight);
103 0 : }
104 :
105 0 : void CGovernanceVote::UpdateHash() const
106 : {
107 : // Note: doesn't match serialization
108 :
109 0 : CHashWriter ss(SER_GETHASH, PROTOCOL_VERSION);
110 0 : ss << masternodeOutpoint << uint8_t{} << 0xffffffff; // adding dummy values here to match old hashing format
111 0 : ss << nParentHash;
112 0 : ss << nVoteSignal;
113 0 : ss << nVoteOutcome;
114 0 : ss << nTime;
115 0 : *const_cast<uint256*>(&hash) = ss.GetHash();
116 0 : }
117 :
118 0 : uint256 CGovernanceVote::GetHash() const
119 : {
120 0 : return hash;
121 : }
122 :
123 :
124 0 : bool CGovernanceVote::CheckSignature(const CKeyID& keyID) const
125 : {
126 0 : std::string strError;
127 :
128 : // Harden Spork6 so that it is active on testnet and no other networks
129 0 : 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 0 : 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 0 : return true;
142 0 : }
143 :
144 0 : bool CGovernanceVote::CheckSignature(const CBLSPublicKey& pubKey) const
145 : {
146 0 : CBLSSignature sig;
147 0 : sig.SetBytes(vchSig, false);
148 0 : if (!sig.VerifyInsecure(pubKey, GetSignatureHash(), false)) {
149 0 : LogPrintf("CGovernanceVote::CheckSignature -- VerifyInsecure() failed\n");
150 0 : return false;
151 : }
152 0 : return true;
153 0 : }
154 :
155 0 : bool CGovernanceVote::IsValid(const CDeterministicMNList& tip_mn_list, bool useVotingKey) const
156 : {
157 0 : 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 0 : 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 0 : 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 0 : auto dmn = tip_mn_list.GetMNByCollateral(masternodeOutpoint);
175 0 : if (!dmn) {
176 0 : LogPrint(BCLog::GOBJECT, "CGovernanceVote::IsValid -- Unknown Masternode - %s\n", masternodeOutpoint.ToStringShort());
177 0 : return false;
178 : }
179 :
180 0 : if (useVotingKey) {
181 0 : return CheckSignature(dmn->pdmnState->keyIDVoting);
182 : } else {
183 0 : return CheckSignature(dmn->pdmnState->pubKeyOperator.Get());
184 : }
185 0 : }
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 : }
|