Line data Source code
1 : // Copyright (c) 2021 The PIVX Core developers
2 : // Distributed under the MIT software license, see the accompanying
3 : // file COPYING or https://www.opensource.org/licenses/mit-license.php.
4 :
5 : #include "budget/budgetutil.h"
6 :
7 : #include "budget/budgetmanager.h"
8 : #include "masternodeman.h"
9 : #include "masternodeconfig.h"
10 : #include "util/validation.h"
11 :
12 : #ifdef ENABLE_WALLET
13 : #include "wallet/wallet.h" // future: use interface instead.
14 : #endif
15 :
16 :
17 12 : static UniValue packRetStatus(const std::string& nodeType, const std::string& result, const std::string& error)
18 : {
19 12 : UniValue statusObj(UniValue::VOBJ);
20 12 : statusObj.pushKV("node", nodeType);
21 12 : statusObj.pushKV("result", result);
22 12 : statusObj.pushKV("error", error);
23 12 : return statusObj;
24 : }
25 :
26 0 : static UniValue packErrorRetStatus(const std::string& nodeType, const std::string& error)
27 : {
28 0 : return packRetStatus(nodeType, "failed", error);
29 : }
30 :
31 11 : static UniValue packVoteReturnValue(const UniValue& details, int success, int failed)
32 : {
33 11 : UniValue returnObj(UniValue::VOBJ);
34 22 : returnObj.pushKV("overall", strprintf("Voted successfully %d time(s) and failed %d time(s).", success, failed));
35 11 : returnObj.pushKV("detail", details);
36 11 : return returnObj;
37 : }
38 :
39 : // key, alias and collateral outpoint of a masternode. Struct used to sign proposal/budget votes
40 : struct MnKeyData
41 : {
42 : std::string mnAlias;
43 : const COutPoint* collateralOut;
44 :
45 : MnKeyData() = delete;
46 11 : MnKeyData(const std::string& _mnAlias, const COutPoint* _collateralOut, const CKey& _key):
47 : mnAlias(_mnAlias),
48 : collateralOut(_collateralOut),
49 : key(_key),
50 11 : use_bls(false)
51 11 : {}
52 1 : MnKeyData(const std::string& _mnAlias, const COutPoint* _collateralOut, const CBLSSecretKey& _key):
53 : mnAlias(_mnAlias),
54 : collateralOut(_collateralOut),
55 : blsKey(_key),
56 1 : use_bls(true)
57 1 : {}
58 :
59 12 : bool Sign(CSignedMessage* msg) const
60 : {
61 12 : return use_bls ? msg->Sign(blsKey)
62 12 : : msg->Sign(key, key.GetPubKey().GetID());
63 : }
64 :
65 : private:
66 : CKey key;
67 : CBLSSecretKey blsKey;
68 : bool use_bls; // whether to use a CKey (mbv) or blsKey (fbv, mnw) to sign
69 : };
70 :
71 : typedef std::list<MnKeyData> mnKeyList;
72 :
73 7 : static UniValue voteProposal(const uint256& propHash, const CBudgetVote::VoteDirection& nVote,
74 : const mnKeyList& mnKeys, UniValue resultsObj, int failed)
75 : {
76 7 : int success = 0;
77 14 : for (const auto& k : mnKeys) {
78 21 : CBudgetVote vote(CTxIn(*k.collateralOut), propHash, nVote);
79 7 : if (!k.Sign(&vote)) {
80 0 : resultsObj.push_back(packErrorRetStatus(k.mnAlias, "Failure to sign."));
81 0 : failed++;
82 0 : continue;
83 : }
84 7 : CValidationState state;
85 7 : if (!g_budgetman.ProcessProposalVote(vote, nullptr, state)) {
86 0 : resultsObj.push_back(packErrorRetStatus(k.mnAlias, FormatStateMessage(state)));
87 0 : failed++;
88 0 : continue;
89 : }
90 7 : resultsObj.push_back(packRetStatus(k.mnAlias, "success", ""));
91 7 : success++;
92 : }
93 :
94 7 : return packVoteReturnValue(resultsObj, success, failed);
95 : }
96 :
97 4 : static UniValue voteFinalBudget(const uint256& budgetHash,
98 : const mnKeyList& mnKeys, UniValue resultsObj, int failed)
99 : {
100 4 : int success = 0;
101 9 : for (const auto& k : mnKeys) {
102 15 : CFinalizedBudgetVote vote(CTxIn(*k.collateralOut), budgetHash);
103 5 : if (!k.Sign(&vote)) {
104 0 : resultsObj.push_back(packErrorRetStatus(k.mnAlias, "Failure to sign."));
105 0 : failed++;
106 0 : continue;
107 : }
108 5 : CValidationState state;
109 5 : if (!g_budgetman.ProcessFinalizedBudgetVote(vote, nullptr, state)) {
110 0 : resultsObj.push_back(packErrorRetStatus(k.mnAlias, FormatStateMessage(state)));
111 0 : failed++;
112 0 : continue;
113 : }
114 5 : resultsObj.push_back(packRetStatus(k.mnAlias, "success", ""));
115 5 : success++;
116 : }
117 :
118 4 : return packVoteReturnValue(resultsObj, success, failed);
119 : }
120 :
121 : // Legacy masternodes
122 9 : static mnKeyList getMNKeys(const Optional<std::string>& mnAliasFilter,
123 : UniValue& resultsObj, int& failed)
124 : {
125 9 : mnKeyList mnKeys;
126 23 : for (const CMasternodeConfig::CMasternodeEntry& mne : masternodeConfig.getEntries()) {
127 14 : if (mnAliasFilter && *mnAliasFilter != mne.getAlias()) continue;
128 20 : CKey mnKey; CPubKey mnPubKey;
129 10 : const std::string& mnAlias = mne.getAlias();
130 10 : if (!CMessageSigner::GetKeysFromSecret(mne.getPrivKey(), mnKey, mnPubKey)) {
131 0 : resultsObj.push_back(packErrorRetStatus(mnAlias, "Could not get key from masternode.conf"));
132 0 : failed++;
133 4 : continue;
134 : }
135 10 : CMasternode* pmn = mnodeman.Find(mnPubKey);
136 10 : if (!pmn) {
137 0 : resultsObj.push_back(packErrorRetStatus(mnAlias, "Can't find masternode by pubkey"));
138 0 : failed++;
139 0 : continue;
140 : }
141 20 : mnKeys.emplace_back(mnAlias, &pmn->vin.prevout, mnKey);
142 : }
143 9 : return mnKeys;
144 : }
145 :
146 0 : static mnKeyList getMNKeysForActiveMasternode(UniValue& resultsObj)
147 : {
148 : // local node must be a masternode
149 0 : if (!fMasterNode) {
150 0 : throw std::runtime_error(_("This is not a masternode. 'local' option disabled."));
151 : }
152 :
153 0 : if (activeMasternode.vin == nullopt) {
154 0 : throw std::runtime_error(_("Active Masternode not initialized."));
155 : }
156 :
157 0 : CKey mnKey; CPubKey mnPubKey;
158 0 : activeMasternode.GetKeys(mnKey, mnPubKey);
159 0 : CMasternode* pmn = mnodeman.Find(mnPubKey);
160 0 : if (!pmn) {
161 0 : resultsObj.push_back(packErrorRetStatus("local", "Can't find masternode by pubkey"));
162 0 : return mnKeyList();
163 : }
164 :
165 0 : return {MnKeyData("local", &pmn->vin.prevout, mnKey)};
166 : }
167 :
168 : // Deterministic masternodes
169 1 : static mnKeyList getDMNVotingKeys(CWallet* const pwallet, const Optional<std::string>& mnAliasFilter, bool fFinal, UniValue& resultsObj, int& failed)
170 : {
171 1 : if (!pwallet) {
172 0 : throw std::runtime_error( "Wallet (with voting key) not found.");
173 : }
174 :
175 1 : auto mnList = deterministicMNManager->GetListAtChainTip();
176 :
177 1 : CDeterministicMNCPtr mnFilter{nullptr};
178 1 : if (mnAliasFilter) {
179 : // vote with a single masternode (identified by ProTx)
180 1 : const uint256& proTxHash = uint256S(*mnAliasFilter);
181 2 : mnFilter = mnList.GetValidMN(proTxHash);
182 1 : if (!mnFilter) {
183 0 : resultsObj.push_back(packErrorRetStatus(*mnAliasFilter, "Invalid or unknown proTxHash"));
184 0 : failed++;
185 0 : return {};
186 : }
187 : }
188 :
189 2 : mnKeyList mnKeys;
190 1 : mnList.ForEachMN(true, [&](const CDeterministicMNCPtr& dmn) {
191 1 : bool filtered = mnFilter && dmn->proTxHash == mnFilter->proTxHash;
192 1 : if (!mnFilter || filtered) {
193 1 : if (fFinal) {
194 : // We should never get here. BLS operator key (for active mn) is needed.
195 0 : throw std::runtime_error("Finalized budget voting is allowed only locally, from the masternode");
196 : }
197 : // Get voting key from the wallet
198 2 : LOCK(pwallet->cs_wallet);
199 2 : CKey mnKey;
200 1 : if (pwallet->GetKey(dmn->pdmnState->keyIDVoting, mnKey)) {
201 3 : mnKeys.emplace_back(dmn->proTxHash.ToString(), &dmn->collateralOutpoint, mnKey);
202 0 : } else if (filtered) {
203 0 : resultsObj.push_back(packErrorRetStatus(*mnAliasFilter, strprintf(
204 : "Private key for voting address %s not known by this wallet",
205 0 : EncodeDestination(dmn->pdmnState->keyIDVoting)))
206 : );
207 0 : failed++;
208 : }
209 : }
210 1 : });
211 :
212 2 : return mnKeys;
213 : }
214 :
215 1 : static mnKeyList getDMNKeysForActiveMasternode(UniValue& resultsObj)
216 : {
217 : // local node must be a masternode
218 1 : if (!activeMasternodeManager) {
219 0 : throw std::runtime_error(_("This is not a deterministic masternode. 'local' option disabled."));
220 : }
221 :
222 2 : CBLSSecretKey sk; CDeterministicMNCPtr dmn;
223 2 : auto res = activeMasternodeManager->GetOperatorKey(sk, dmn);
224 1 : if (!res) {
225 0 : resultsObj.push_back(packErrorRetStatus("local", res.getError()));
226 1 : return {};
227 : }
228 :
229 2 : return {MnKeyData("local", &dmn->collateralOutpoint, sk)};
230 : }
231 :
232 : // vote on proposal (finalized budget, if fFinal=true) with all possible keys or a single mn (mnAliasFilter)
233 : // Note: for DMNs only proposal voting is allowed with the voting key
234 : // (finalized budget voting requires the operator BLS key)
235 10 : UniValue mnBudgetVoteInner(CWallet* const pwallet, bool fLegacyMN, const uint256& budgetHash, bool fFinal,
236 : const CBudgetVote::VoteDirection& nVote, const Optional<std::string>& mnAliasFilter)
237 : {
238 10 : if (fFinal && !fLegacyMN) {
239 0 : throw std::runtime_error("Finalized budget voting is allowed only locally, from the masternode");
240 : }
241 10 : UniValue resultsObj(UniValue::VARR);
242 10 : int failed = 0;
243 :
244 10 : mnKeyList mnKeys = fLegacyMN ? getMNKeys(mnAliasFilter, resultsObj, failed)
245 20 : : getDMNVotingKeys(pwallet, mnAliasFilter, fFinal, resultsObj, failed);
246 :
247 10 : if (mnKeys.empty()) {
248 0 : return packVoteReturnValue(resultsObj, 0, failed);
249 : }
250 :
251 10 : return (fFinal ? voteFinalBudget(budgetHash, mnKeys, resultsObj, failed)
252 10 : : voteProposal(budgetHash, nVote, mnKeys, resultsObj, failed));
253 : }
254 :
255 : // vote on proposal (finalized budget, if fFinal=true) with the active local masternode
256 : // Note: for DMNs only finalized budget voting is allowed with the operator key
257 : // (proposal voting requires the voting key)
258 1 : UniValue mnLocalBudgetVoteInner(bool fLegacyMN, const uint256& budgetHash, bool fFinal,
259 : const CBudgetVote::VoteDirection& nVote)
260 : {
261 1 : UniValue resultsObj(UniValue::VARR);
262 :
263 1 : mnKeyList mnKeys = fLegacyMN ? getMNKeysForActiveMasternode(resultsObj)
264 2 : : getDMNKeysForActiveMasternode(resultsObj);
265 :
266 1 : if (mnKeys.empty()) {
267 0 : return packVoteReturnValue(resultsObj, 0, 1);
268 : }
269 :
270 1 : return (fFinal ? voteFinalBudget(budgetHash, mnKeys, resultsObj, 0)
271 1 : : voteProposal(budgetHash, nVote, mnKeys, resultsObj, 0));
272 : }
|