LCOV - code coverage report
Current view: top level - src/budget - budgetutil.cpp (source / functions) Hit Total Coverage
Test: total_coverage.info Lines: 94 139 67.6 %
Date: 2025-02-23 09:33:43 Functions: 13 15 86.7 %

          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             : }

Generated by: LCOV version 1.14