LCOV - code coverage report
Current view: top level - src/rpc - rpcevo.cpp (source / functions) Hit Total Coverage
Test: total_coverage.info Lines: 351 521 67.4 %
Date: 2025-02-23 09:33:43 Functions: 44 51 86.3 %

          Line data    Source code
       1             : // Copyright (c) 2018-2021 The Dash Core developers
       2             : // Copyright (c) 2021-2022 The PIVX Core developers
       3             : // Distributed under the MIT software license, see the accompanying
       4             : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
       5             : 
       6             : #include "activemasternode.h"
       7             : #include "bls/key_io.h"
       8             : #include "bls/bls_wrapper.h"
       9             : #include "core_io.h"
      10             : #include "destination_io.h"
      11             : #include "evo/deterministicmns.h"
      12             : #include "evo/specialtx_validation.h"
      13             : #include "evo/providertx.h"
      14             : #include "key_io.h"
      15             : #include "masternode.h"
      16             : #include "messagesigner.h"
      17             : #include "netbase.h"
      18             : #include "operationresult.h"
      19             : #include "policy/policy.h"
      20             : #include "pubkey.h" // COMPACT_SIGNATURE_SIZE
      21             : #include "rpc/server.h"
      22             : #include "script/sign.h"
      23             : #include "tiertwo/masternode_meta_manager.h"
      24             : #include "util/validation.h"
      25             : #include "utilmoneystr.h"
      26             : 
      27             : #ifdef ENABLE_WALLET
      28             : #include "coincontrol.h"
      29             : #include "wallet/wallet.h"
      30             : #include "wallet/rpcwallet.h"
      31             : 
      32             : extern void TryATMP(const CMutableTransaction& mtx, bool fOverrideFees);
      33             : extern void RelayTx(const uint256& hashTx);
      34             : #endif//ENABLE_WALLET
      35             : 
      36             : enum ProRegParam {
      37             :     collateralAddress,
      38             :     collateralHash,
      39             :     collateralIndex,
      40             :     ipAndPort_register,
      41             :     ipAndPort_update,
      42             :     operatorPubKey_register,
      43             :     operatorPubKey_update,
      44             :     operatorPayoutAddress_register,
      45             :     operatorPayoutAddress_update,
      46             :     operatorReward,
      47             :     operatorKey,
      48             :     ownerAddress,
      49             :     ownerKey,
      50             :     proTxHash,
      51             :     payoutAddress_register,
      52             :     payoutAddress_update,
      53             :     revocationReason,
      54             :     votingAddress_register,
      55             :     votingAddress_update,
      56             : };
      57             : 
      58             : static const std::map<ProRegParam, std::string> mapParamHelp = {
      59             :         {collateralAddress,
      60             :             "%d. \"collateralAddress\"     (string, required) The PIVX address to send the collateral to.\n"
      61             :         },
      62             :         {collateralHash,
      63             :             "%d. \"collateralHash\"        (string, required) The collateral transaction hash.\n"
      64             :         },
      65             :         {collateralIndex,
      66             :             "%d. collateralIndex           (numeric, required) The collateral transaction output index.\n"
      67             :         },
      68             :         {ipAndPort_register,
      69             :             "%d. \"ipAndPort\"             (string, required) IP and port in the form \"IP:PORT\".\n"
      70             :             "                                Must be unique on the network. Can be set to 0, which will require a ProUpServTx afterwards.\n"
      71             :         },
      72             :         {ipAndPort_update,
      73             :             "%d. \"ipAndPort\"             (string, required) IP and port in the form \"IP:PORT\".\n"
      74             :             "                                If set to an empty string, the currently active ip is reused.\n"
      75             :         },
      76             :         {operatorPubKey_register,
      77             :             "%d. \"operatorPubKey\"       (string, required) The operator BLS public key. The BLS private key does not have to be known.\n"
      78             :             "                              It has to match the BLS private key which is later used when operating the masternode.\n"
      79             :         },
      80             :         {operatorPubKey_update,
      81             :             "%d. \"operatorPubKey\"       (string, required) The operator BLS public key. The BLS private key does not have to be known.\n"
      82             :             "                                It has to match the BLS private key which is later used when operating the masternode.\n"
      83             :             "                                If set to an empty string, the currently active operator BLS public key is reused.\n"
      84             :         },
      85             :         {operatorKey,
      86             :             "%d. \"operatorKey\"           (string, optional) The operator BLS private key associated with the\n"
      87             :             "                                 registered operator public key. If not specified, or set to an empty string, then this command must\n"
      88             :             "                                 be performed on the active masternode with the corresponding operator key.\n"
      89             :         },
      90             :         {operatorPayoutAddress_register,
      91             :             "%d. \"operatorPayoutAddress\" (string, optional) The address used for operator reward payments.\n"
      92             :             "                                Only allowed when the ProRegTx had a non-zero operatorReward value.\n"
      93             :             "                                If set to an empty string, the operatorPubKey is used.\n"
      94             :         },
      95             :         {operatorPayoutAddress_update,
      96             :             "%d. \"operatorPayoutAddress\" (string, optional) The address used for operator reward payments.\n"
      97             :             "                                Only allowed when the ProRegTx had a non-zero operatorReward value.\n"
      98             :             "                                If set to an empty string, the currently active one is reused.\n"
      99             :         },
     100             :         {operatorReward,
     101             :             "%d. \"operatorReward\"        (numeric, optional) The fraction in %% to share with the operator. The value must be\n"
     102             :             "                                between 0.00 and 100.00. If not set, it takes the default value of 0.0\n"
     103             :         },
     104             :         {ownerAddress,
     105             :             "%d. \"ownerAddress\"          (string, required) The PIVX address to use for payee updates and proposal voting.\n"
     106             :             "                                The private key belonging to this address must be known in your wallet, in order to send updates.\n"
     107             :             "                                The address must not be already registered, and must differ from the collateralAddress\n"
     108             :         },
     109             :         {ownerKey,
     110             :             "%d. \"ownerKey\"              (string, optional) The owner key associated with the operator address of the masternode.\n"
     111             :             "                                If not specified, or set to an empty string, then the mn key must be known by your wallet, in order to sign the tx.\n"
     112             :         },
     113             :         {payoutAddress_register,
     114             :             "%d. \"payoutAddress\"          (string, required) The PIVX address to use for masternode reward payments.\n"
     115             :         },
     116             :         {payoutAddress_update,
     117             :             "%d. \"payoutAddress\"          (string, required) The PIVX address to use for masternode reward payments.\n"
     118             :             "                                 If set to an empty string, the currently active payout address is reused.\n"
     119             :         },
     120             :         {proTxHash,
     121             :             "%d. \"proTxHash\"              (string, required) The hash of the initial ProRegTx.\n"
     122             :         },
     123             :         {revocationReason,
     124             :             "%d. reason                     (numeric, optional) The reason for masternode service revocation. Default: 0.\n"
     125             :             "                                 0=not_specified, 1=service_termination, 2=compromised_keys, 3=keys_change.\n"
     126             :         },
     127             :         {votingAddress_register,
     128             :             "%d. \"votingAddress\"          (string, required) The voting key address. The private key does not have to be known by your wallet.\n"
     129             :             "                                 It has to match the private key which is later used when voting on proposals.\n"
     130             :             "                                 If set to an empty string, ownerAddress will be used.\n"
     131             :         },
     132             :         {votingAddress_update,
     133             :             "%d. \"votingAddress\"          (string, required) The voting key address. The private key does not have to be known by your wallet.\n"
     134             :             "                                 It has to match the private key which is later used when voting on proposals.\n"
     135             :             "                                 If set to an empty string, the currently active voting key address is reused.\n"
     136             :         },
     137             :     };
     138             : 
     139           0 : std::string GetHelpString(int nParamNum, ProRegParam p)
     140             : {
     141           0 :     auto it = mapParamHelp.find(p);
     142           0 :     if (it == mapParamHelp.end())
     143           0 :         throw std::runtime_error(strprintf("FIXME: WRONG PARAM: %d!", (int)p));
     144             : 
     145           0 :     return strprintf(it->second, nParamNum);
     146             : }
     147             : 
     148             : #ifdef ENABLE_WALLET
     149           8 : static CKey GetKeyFromWallet(CWallet* pwallet, const CKeyID& keyID)
     150             : {
     151           8 :     assert(pwallet);
     152           8 :     CKey key;
     153           8 :     if (!pwallet->GetKey(keyID, key)) {
     154           0 :         throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY,
     155           0 :                            strprintf("key for address %s not in wallet", EncodeDestination(keyID)));
     156             :     }
     157           8 :     return key;
     158             : }
     159             : #endif
     160             : 
     161         145 : static void CheckEvoUpgradeEnforcement()
     162             : {
     163         290 :     const int nHeight = WITH_LOCK(cs_main, return chainActive.Height(); );
     164         145 :     if (!Params().GetConsensus().NetworkUpgradeActive(nHeight, Consensus::UPGRADE_V6_0)) {
     165           6 :         throw JSONRPCError(RPC_MISC_ERROR, "Evo upgrade is not active yet");
     166             :     }
     167         142 : }
     168             : 
     169             : // Allows to specify PIVX address or priv key (as strings). In case of PIVX address, the priv key is taken from the wallet
     170           2 : static CKey ParsePrivKey(CWallet* pwallet, const std::string &strKeyOrAddress, bool allowAddresses = true) {
     171           2 :     bool isStaking{false}, isShield{false}, isExchange{false};
     172           4 :     const CWDestination& cwdest = Standard::DecodeDestination(strKeyOrAddress, isStaking, isExchange, isShield);
     173           2 :     if (isStaking) {
     174           0 :         throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "cold staking addresses not supported");
     175             :     }
     176           2 :     if (isShield) {
     177           0 :         throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "shield addresses not supported");
     178             :     }
     179           2 :     const CTxDestination* dest = Standard::GetTransparentDestination(cwdest);
     180           2 :     if (allowAddresses && IsValidDestination(*dest)) {
     181             : #ifdef ENABLE_WALLET
     182           0 :         if (!pwallet) {
     183           0 :             throw std::runtime_error("addresses not supported when wallet is disabled");
     184             :         }
     185           0 :         EnsureWalletIsUnlocked(pwallet);
     186           0 :         const CKeyID* keyID = boost::get<CKeyID>(dest);
     187           0 :         assert (keyID != nullptr);  // we just checked IsValidDestination
     188           0 :         return GetKeyFromWallet(pwallet, *keyID);
     189             : #else   // ENABLE_WALLET
     190             :         throw std::runtime_error("addresses not supported in no-wallet builds");
     191             : #endif  // ENABLE_WALLET
     192             :     }
     193             : 
     194           4 :     CKey key = KeyIO::DecodeSecret(strKeyOrAddress);
     195           2 :     if (!key.IsValid()) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid private key encoding");
     196           2 :     return key;
     197             : }
     198             : 
     199         267 : static CKeyID ParsePubKeyIDFromAddress(const std::string& strAddress)
     200             : {
     201         267 :     bool isStaking{false}, isShield{false}, isExchange{false};
     202         267 :     const CWDestination& cwdest = Standard::DecodeDestination(strAddress, isStaking, isExchange, isShield);
     203         267 :     if (isStaking) {
     204           0 :         throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "cold staking addresses not supported");
     205             :     }
     206         267 :     if (isShield) {
     207           0 :         throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "shield addresses not supported");
     208             :     }
     209         267 :     const CKeyID* keyID = boost::get<CKeyID>(Standard::GetTransparentDestination(cwdest));
     210         267 :     if (!keyID) {
     211           4 :         throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("invalid PIVX address %s", strAddress));
     212             :     }
     213         265 :     return *keyID;
     214             : }
     215             : 
     216          78 : static CBLSPublicKey ParseBLSPubKey(const CChainParams& params, const std::string& strKey)
     217             : {
     218         156 :     auto opKey = bls::DecodePublic(params, strKey);
     219          78 :     if (!opKey) {
     220           0 :         throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("invalid BLS public key: %s", strKey));
     221             :     }
     222          78 :     return *opKey;
     223             : }
     224             : 
     225          11 : static CBLSSecretKey ParseBLSSecretKey(const CChainParams& params, const std::string& strKey)
     226             : {
     227          11 :     auto opKey = bls::DecodeSecret(params, strKey);
     228          11 :     if (!opKey) {
     229           0 :         throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("invalid BLS secret key: %s", strKey));
     230             :     }
     231          22 :     return *opKey;
     232             : }
     233             : 
     234          13 : static CBLSSecretKey GetBLSSecretKey(const CChainParams& params, const std::string& hexKey)
     235             : {
     236          13 :     if (!hexKey.empty()) {
     237          11 :         return ParseBLSSecretKey(params, hexKey);
     238             :     }
     239             :     // If empty, get the active masternode key
     240          19 :     CBLSSecretKey sk; CTxIn vin;
     241           2 :     if (!GetActiveDMNKeys(sk, vin)) {
     242           0 :         throw JSONRPCError(RPC_INVALID_PARAMETER, "Active masternode key not found. Insert DMN operator private key.");
     243             :     }
     244           2 :     return sk;
     245             : }
     246             : 
     247           0 : static UniValue DmnToJson(const CDeterministicMNCPtr dmn)
     248             : {
     249           0 :     UniValue ret(UniValue::VOBJ);
     250           0 :     dmn->ToJson(ret);
     251           0 :     Coin coin;
     252           0 :     if (!WITH_LOCK(cs_main, return pcoinsTip->GetUTXOCoin(dmn->collateralOutpoint, coin); )) {
     253             :         return ret;
     254             :     }
     255           0 :     CTxDestination dest;
     256           0 :     if (!ExtractDestination(coin.out.scriptPubKey, dest)) {
     257             :         return ret;
     258             :     }
     259           0 :     ret.pushKV("collateralAddress", EncodeDestination(dest));
     260           0 :     return ret;
     261             : }
     262             : 
     263             : #ifdef ENABLE_WALLET
     264             : 
     265             : template<typename SpecialTxPayload>
     266          94 : static void FundSpecialTx(CWallet* pwallet, CMutableTransaction& tx, SpecialTxPayload& payload)
     267             : {
     268          94 :     SetTxPayload(tx, payload);
     269             : 
     270          94 :     static CTxOut dummyTxOut(0, CScript() << OP_RETURN);
     271           0 :     std::vector<CRecipient> vecSend;
     272          94 :     bool dummyTxOutAdded = false;
     273             : 
     274          94 :     if (tx.vout.empty()) {
     275             :         // add dummy txout as CreateTransaction requires at least one recipient
     276          51 :         tx.vout.emplace_back(dummyTxOut);
     277             :         dummyTxOutAdded = true;
     278             :     }
     279             : 
     280             :     CAmount nFee;
     281          94 :     CFeeRate feeRate = CFeeRate(0);
     282          94 :     int nChangePos = -1;
     283         188 :     std::string strFailReason;
     284          94 :     std::set<int> setSubtractFeeFromOutputs;
     285          94 :     if (!pwallet->FundTransaction(tx, nFee, false, feeRate, nChangePos, strFailReason, false, false, {}))
     286           0 :         throw JSONRPCError(RPC_INTERNAL_ERROR, strFailReason);
     287             : 
     288          94 :     if (dummyTxOutAdded && tx.vout.size() > 1) {
     289             :         // FundTransaction added a change output, so we don't need the dummy txout anymore
     290             :         // Removing it results in slight overpayment of fees, but we ignore this for now (as it's a very low amount)
     291          51 :         auto it = std::find(tx.vout.begin(), tx.vout.end(), dummyTxOut);
     292          51 :         assert(it != tx.vout.end());
     293          51 :         tx.vout.erase(it);
     294             :     }
     295             : 
     296          94 :     UpdateSpecialTxInputsHash(tx, payload);
     297          94 : }
     298             : 
     299             : #endif
     300             : 
     301             : template<typename SpecialTxPayload>
     302          94 : static void UpdateSpecialTxInputsHash(const CMutableTransaction& tx, SpecialTxPayload& payload)
     303             : {
     304          94 :     payload.inputsHash = CalcTxInputsHash(tx);
     305          94 : }
     306             : 
     307             : template<typename SpecialTxPayload>
     308          10 : static void SignSpecialTxPayloadByHash(const CMutableTransaction& tx, SpecialTxPayload& payload, const CKey& key)
     309             : {
     310          10 :     payload.vchSig.clear();
     311             : 
     312          10 :     uint256 hash = ::SerializeHash(payload);
     313          10 :     if (!CHashSigner::SignHash(hash, key, payload.vchSig)) {
     314           0 :         throw JSONRPCError(RPC_INTERNAL_ERROR, "failed to sign special tx payload");
     315             :     }
     316          10 : }
     317             : 
     318             : template<typename SpecialTxPayload>
     319          12 : static void SignSpecialTxPayloadByHash(const CMutableTransaction& tx, SpecialTxPayload& payload, const CBLSSecretKey& key)
     320             : {
     321          12 :     payload.sig = key.Sign(::SerializeHash(payload));
     322          12 :     if (!payload.sig.IsValid()) {
     323           0 :         throw JSONRPCError(RPC_INTERNAL_ERROR, "failed to sign special tx payload");
     324             :     }
     325          12 : }
     326             : 
     327             : template<typename SpecialTxPayload>
     328          13 : static void SignSpecialTxPayloadByString(SpecialTxPayload& payload, const CKey& key)
     329             : {
     330          13 :     payload.vchSig.clear();
     331             : 
     332          13 :     std::string m = payload.MakeSignString();
     333          13 :     if (!CMessageSigner::SignMessage(m, payload.vchSig, key)) {
     334           0 :         throw JSONRPCError(RPC_INTERNAL_ERROR, "failed to sign special tx payload");
     335             :     }
     336          13 : }
     337             : 
     338           0 : static std::string TxInErrorToString(int i, const CTxIn& txin, const std::string& strError)
     339             : {
     340           0 :     return strprintf("Input %d (%s): %s", i, txin.prevout.ToStringShort(), strError);
     341             : }
     342             : 
     343             : #ifdef ENABLE_WALLET
     344             : 
     345          88 : static OperationResult SignTransaction(CWallet* const pwallet, CMutableTransaction& tx)
     346             : {
     347         264 :     LOCK2(cs_main, pwallet->cs_wallet);
     348         176 :     const CTransaction txConst(tx);
     349         181 :     for (unsigned int i = 0; i < tx.vin.size(); i++) {
     350          93 :         CTxIn& txin = tx.vin[i];
     351          93 :         const Coin& coin = pcoinsTip->AccessCoin(txin.prevout);
     352          93 :         if (coin.IsSpent()) {
     353           0 :             return errorOut(TxInErrorToString(i, txin, "not found or already spent"));
     354             :         }
     355          93 :         SigVersion sv = tx.GetRequiredSigVersion();
     356          93 :         txin.scriptSig.clear();
     357         186 :         SignatureData sigdata;
     358          93 :         if (!ProduceSignature(MutableTransactionSignatureCreator(pwallet, &tx, i, coin.out.nValue, SIGHASH_ALL),
     359          93 :                               coin.out.scriptPubKey, sigdata, sv, false)) {
     360           0 :             return errorOut(TxInErrorToString(i, txin, "signature failed"));
     361             :         }
     362          93 :         UpdateTransaction(tx, i, sigdata);
     363             :     }
     364          88 :     return OperationResult(true);
     365             : }
     366             : 
     367             : template<typename SpecialTxPayload>
     368          93 : static std::string SignAndSendSpecialTx(CWallet* const pwallet, CMutableTransaction& tx, const SpecialTxPayload& pl)
     369             : {
     370          93 :     SetTxPayload(tx, pl);
     371             : 
     372         186 :     CValidationState state;
     373          93 :     CCoinsViewCache view(pcoinsTip.get());
     374         289 :     if (!WITH_LOCK(cs_main, return CheckSpecialTx(tx, GetChainTip(), &view, state); )) {
     375          10 :         throw JSONRPCError(RPC_MISC_ERROR, FormatStateMessage(state));
     376             :     }
     377             : 
     378         176 :     const OperationResult& sigRes = SignTransaction(pwallet, tx);
     379          88 :     if (!sigRes) {
     380           0 :         throw JSONRPCError(RPC_INTERNAL_ERROR, sigRes.getError());
     381             :     }
     382             : 
     383          88 :     TryATMP(tx, false);
     384          83 :     const uint256& hashTx = tx.GetHash();
     385          83 :     RelayTx(hashTx);
     386         166 :     return hashTx.GetHex();
     387             : }
     388             : 
     389             : // Parses inputs (starting from index paramIdx) and returns ProReg payload
     390          72 : static ProRegPL ParseProRegPLParams(const UniValue& params, unsigned int paramIdx)
     391             : {
     392          72 :     assert(params.size() > paramIdx + 4);
     393          72 :     assert(params.size() < paramIdx + 8);
     394          72 :     const auto& chainparams = Params();
     395          72 :     ProRegPL pl;
     396             : 
     397             :     // ip and port
     398          72 :     const std::string& strIpPort = params[paramIdx].get_str();
     399          72 :     if (!strIpPort.empty()) {
     400          72 :         if (!Lookup(strIpPort, pl.addr, chainparams.GetDefaultPort(), false)) {
     401           0 :             throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("invalid network address %s", strIpPort));
     402             :         }
     403             :     }
     404             : 
     405             :     // addresses/keys
     406          72 :     const std::string& strAddOwner = params[paramIdx + 1].get_str();
     407          72 :     const std::string& strPubKeyOperator = params[paramIdx + 2].get_str();
     408          72 :     const std::string& strAddVoting = params[paramIdx + 3].get_str();
     409          72 :     pl.keyIDOwner = ParsePubKeyIDFromAddress(strAddOwner);
     410          72 :     pl.pubKeyOperator = ParseBLSPubKey(chainparams, strPubKeyOperator);
     411          72 :     pl.keyIDVoting = pl.keyIDOwner;
     412          72 :     if (!strAddVoting.empty()) {
     413          72 :         pl.keyIDVoting = ParsePubKeyIDFromAddress(strAddVoting);
     414             :     }
     415             : 
     416             :     // payout script (!TODO: add support for P2CS)
     417          72 :     const std::string& strAddPayee = params[paramIdx + 4].get_str();
     418          72 :     pl.scriptPayout = GetScriptForDestination(CTxDestination(ParsePubKeyIDFromAddress(strAddPayee)));
     419             : 
     420             :     // operator reward
     421          72 :     pl.nOperatorReward = 0;
     422          72 :     if (params.size() > paramIdx + 5) {
     423           1 :         int64_t operReward = 0;
     424           1 :         if (!ParseFixedPoint(params[paramIdx + 5].getValStr(), 2, &operReward)) {
     425           0 :             throw JSONRPCError(RPC_INVALID_PARAMETER, "operatorReward must be a number");
     426             :         }
     427           1 :         if (operReward < 0 || operReward > 10000) {
     428           0 :             throw JSONRPCError(RPC_INVALID_PARAMETER, "operatorReward must be between 0.00 and 100.00");
     429             :         }
     430           1 :         pl.nOperatorReward = (uint16_t)operReward;
     431           1 :         if (params.size() > paramIdx + 6) {
     432             :             // operator reward payout script
     433           1 :             const std::string& strAddOpPayee = params[paramIdx + 6].get_str();
     434           1 :             if (pl.nOperatorReward > 0 && !strAddOpPayee.empty()) {
     435           1 :                 pl.scriptOperatorPayout = GetScriptForDestination(CTxDestination(ParsePubKeyIDFromAddress(strAddOpPayee)));
     436           0 :             } else if (!strAddOpPayee.empty()) {
     437           0 :                 throw JSONRPCError(RPC_INVALID_PARAMETER, "operatorPayoutAddress must be empty when operatorReward is 0");
     438             :             }
     439             :         }
     440             :     }
     441          72 :     return pl;
     442             : }
     443             : 
     444             : // handles protx_register, and protx_register_prepare
     445          30 : static UniValue ProTxRegister(const JSONRPCRequest& request, bool fSignAndSend)
     446             : {
     447          30 :     CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
     448             : 
     449          30 :     if (!EnsureWalletIsAvailable(pwallet, request.fHelp))
     450           0 :         return NullUniValue;
     451             : 
     452          30 :     if (request.fHelp || request.params.size() < 7 || request.params.size() > 9) {
     453           0 :         throw std::runtime_error(
     454             :                 (fSignAndSend ?
     455             :                     "protx_register \"collateralHash\" collateralIndex \"ipAndPort\" \"ownerAddress\" \"operatorPubKey\" \"votingAddress\" \"payoutAddress\" (operatorReward \"operatorPayoutAddress\")\n"
     456             :                     "The collateral is specified through \"collateralHash\" and \"collateralIndex\" and must be an unspent\n"
     457             :                     "transaction output spendable by this wallet. It must also not be used by any other masternode.\n"
     458             :                         :
     459             :                     "protx_register_prepare \"collateralHash\" collateralIndex \"ipAndPort\" \"ownerAddress\" \"operatorPubKey\" \"votingAddress\" \"payoutAddress\" (operatorReward \"operatorPayoutAddress\")\n"
     460             :                     "\nCreates an unsigned ProTx and returns it. The ProTx must be signed externally with the collateral\n"
     461             :                     "key and then passed to \"protx_register_submit\".\n"
     462             :                     "The collateral is specified through \"collateralHash\" and \"collateralIndex\" and must be an unspent transaction output.\n"
     463             :                 )
     464           0 :                 + HelpRequiringPassphrase(pwallet) + "\n"
     465             :                 "\nArguments:\n"
     466           0 :                 + GetHelpString(1, collateralHash)
     467           0 :                 + GetHelpString(2, collateralIndex)
     468           0 :                 + GetHelpString(3, ipAndPort_register)
     469           0 :                 + GetHelpString(4, ownerAddress)
     470           0 :                 + GetHelpString(5, operatorPubKey_register)
     471           0 :                 + GetHelpString(6, votingAddress_register)
     472           0 :                 + GetHelpString(7, payoutAddress_register)
     473           0 :                 + GetHelpString(8, operatorReward)
     474           0 :                 + GetHelpString(9, operatorPayoutAddress_register) +
     475           0 :                 "\nResult:\n" +
     476           0 :                 (fSignAndSend ? (
     477             :                         "\"txid\"                 (string) The transaction id.\n"
     478             :                         "\nExamples:\n"
     479           0 :                         + HelpExampleCli("protx_register", "\"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef\" 0 \"168.192.1.100:51472\" \"DMJRSsuU9zfyrvxVaAEFQqK4MxZg6vgeS6\" \"DMJRSsuU9zfyrvxVaAEFQqK4MxZg6vgeS6\" \"DMJRSsuU9zfyrvxVaAEFQqK4MxZg6vgeS6\" \"DMJRSsuU9zfyrvxVaAEFQqK4MxZg6vgeS6\"")
     480             :                         ) : (
     481             :                         "{                        (json object)\n"
     482             :                         "  \"tx\" :                 (string) The serialized ProTx in hex format.\n"
     483             :                         "  \"collateralAddress\" :  (string) The collateral address.\n"
     484             :                         "  \"signMessage\" :        (string) The string message that needs to be signed with the collateral key\n"
     485             :                         "}\n"
     486             :                         "\nExamples:\n"
     487           0 :                         + HelpExampleCli("protx_register_prepare", "\"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef\" 0 \"168.192.1.100:51472\" \"DMJRSsuU9zfyrvxVaAEFQqK4MxZg6vgeS6\" \"DMJRSsuU9zfyrvxVaAEFQqK4MxZg6vgeS6\" \"DMJRSsuU9zfyrvxVaAEFQqK4MxZg6vgeS6\" \"DMJRSsuU9zfyrvxVaAEFQqK4MxZg6vgeS6\"")
     488             :                         )
     489             :                 )
     490           0 :         );
     491             :     }
     492          30 :     if (fSignAndSend) CheckEvoUpgradeEnforcement();
     493             : 
     494          29 :     EnsureWalletIsUnlocked(pwallet);
     495             :     // Make sure the results are valid at least up to the most recent block
     496             :     // the user could have gotten from another RPC command prior to now
     497          29 :     pwallet->BlockUntilSyncedToCurrentChain();
     498             : 
     499          29 :     const uint256& collateralHash = ParseHashV(request.params[0], "collateralHash");
     500          29 :     const int32_t collateralIndex = request.params[1].get_int();
     501          29 :     if (collateralIndex < 0) {
     502           0 :         throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("invalid collateral index (negative): %d", collateralIndex));
     503             :     }
     504             : 
     505          58 :     ProRegPL pl = ParseProRegPLParams(request.params, 2);
     506          29 :     pl.nVersion = ProRegPL::CURRENT_VERSION;
     507          29 :     pl.collateralOutpoint = COutPoint(collateralHash, (uint32_t)collateralIndex);
     508             : 
     509          29 :     CMutableTransaction tx;
     510          29 :     tx.nVersion = CTransaction::TxVersion::SAPLING;
     511          29 :     tx.nType = CTransaction::TxType::PROREG;
     512             : 
     513             :     // referencing unspent collateral outpoint
     514          58 :     Coin coin;
     515          87 :     if (!WITH_LOCK(cs_main, return pcoinsTip->GetUTXOCoin(pl.collateralOutpoint, coin); )) {
     516           0 :         throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("collateral not found: %s-%d", collateralHash.ToString(), collateralIndex));
     517             :     }
     518          29 :     if (coin.out.nValue != Params().GetConsensus().nMNCollateralAmt) {
     519           0 :         throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("collateral %s-%d with invalid value %d", collateralHash.ToString(), collateralIndex, coin.out.nValue));
     520             :     }
     521          58 :     CTxDestination txDest;
     522          29 :     ExtractDestination(coin.out.scriptPubKey, txDest);
     523          29 :     const CKeyID* keyID = boost::get<CKeyID>(&txDest);
     524          29 :     if (!keyID) {
     525           0 :         throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("collateral type not supported: %s-%d", collateralHash.ToString(), collateralIndex));
     526             :     }
     527          58 :     CKey keyCollateral;
     528          29 :     if (fSignAndSend && !pwallet->GetKey(*keyID, keyCollateral)) {
     529           0 :         throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("collateral key not in wallet: %s", EncodeDestination(txDest)));
     530             :     }
     531             : 
     532             :     // make sure fee calculation works
     533          29 :     pl.vchSig.resize(CPubKey::COMPACT_SIGNATURE_SIZE);
     534             : 
     535          29 :     FundSpecialTx(pwallet, tx, pl);
     536             : 
     537          29 :     if (fSignAndSend) {
     538          13 :         SignSpecialTxPayloadByString(pl, keyCollateral); // prove we own the collateral
     539             :         // check the payload, add the tx inputs sigs, and send the tx.
     540          26 :         return SignAndSendSpecialTx(pwallet, tx, pl);
     541             :     }
     542             :     // external signing with collateral key
     543          16 :     pl.vchSig.clear();
     544          16 :     SetTxPayload(tx, pl);
     545          32 :     UniValue ret(UniValue::VOBJ);
     546          48 :     ret.pushKV("tx", EncodeHexTx(tx));
     547          32 :     ret.pushKV("collateralAddress", EncodeDestination(txDest));
     548          32 :     ret.pushKV("signMessage", pl.MakeSignString());
     549          16 :     return ret;
     550             : }
     551             : 
     552          14 : UniValue protx_register(const JSONRPCRequest& request)
     553             : {
     554          14 :     return ProTxRegister(request, true);
     555             : }
     556             : 
     557          16 : UniValue protx_register_prepare(const JSONRPCRequest& request)
     558             : {
     559          16 :     return ProTxRegister(request, false);
     560             : }
     561             : 
     562          16 : UniValue protx_register_submit(const JSONRPCRequest& request)
     563             : {
     564          16 :     CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
     565             : 
     566          16 :     if (!EnsureWalletIsAvailable(pwallet, request.fHelp))
     567           0 :         return NullUniValue;
     568             : 
     569          16 :     if (request.fHelp || request.params.size() != 2) {
     570           0 :         throw std::runtime_error(
     571             :                 "protx_register_submit \"tx\" \"sig\"\n"
     572             :                 "\nSubmits the specified ProTx to the network. This command will also sign the inputs of the transaction\n"
     573             :                 "which were previously added by \"protx_register_prepare\" to cover transaction fees\n"
     574           0 :                 + HelpRequiringPassphrase(pwallet) + "\n"
     575             :                 "\nArguments:\n"
     576             :                 "1. \"tx\"                 (string, required) The serialized transaction previously returned by \"protx_register_prepare\"\n"
     577             :                 "2. \"sig\"                (string, required) The signature signed with the collateral key. Must be in base64 format.\n"
     578             :                 "\nResult:\n"
     579             :                 "\"txid\"                  (string) The transaction id.\n"
     580             :                 "\nExamples:\n"
     581           0 :                 + HelpExampleCli("protx_register_submit", "\"tx\" \"sig\"")
     582           0 :         );
     583             :     }
     584          16 :     CheckEvoUpgradeEnforcement();
     585             : 
     586          15 :     EnsureWalletIsUnlocked(pwallet);
     587             :     // Make sure the results are valid at least up to the most recent block
     588             :     // the user could have gotten from another RPC command prior to now
     589          15 :     pwallet->BlockUntilSyncedToCurrentChain();
     590             : 
     591          30 :     CMutableTransaction tx;
     592          15 :     if (!DecodeHexTx(tx, request.params[0].get_str())) {
     593           0 :         throw JSONRPCError(RPC_INVALID_PARAMETER, "transaction not deserializable");
     594             :     }
     595          15 :     if (tx.nType != CTransaction::TxType::PROREG) {
     596           0 :         throw JSONRPCError(RPC_INVALID_PARAMETER, "transaction not a ProRegTx");
     597             :     }
     598          15 :     ProRegPL pl;
     599          15 :     if (!GetTxPayload(tx, pl)) {
     600           0 :         throw JSONRPCError(RPC_INVALID_PARAMETER, "transaction payload not deserializable");
     601             :     }
     602          15 :     if (!pl.vchSig.empty()) {
     603           0 :         throw JSONRPCError(RPC_INVALID_PARAMETER, "payload signature not empty");
     604             :     }
     605             : 
     606          30 :     pl.vchSig = DecodeBase64(request.params[1].get_str().c_str());
     607             : 
     608             :     // check the payload, add the tx inputs sigs, and send the tx.
     609          30 :     return SignAndSendSpecialTx(pwallet, tx, pl);
     610             : }
     611             : 
     612          44 : UniValue protx_register_fund(const JSONRPCRequest& request)
     613             : {
     614          44 :     CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
     615             : 
     616          44 :     if (!EnsureWalletIsAvailable(pwallet, request.fHelp))
     617           0 :         return NullUniValue;
     618             : 
     619          44 :     if (request.fHelp || request.params.size() < 6 || request.params.size() > 8) {
     620           0 :         throw std::runtime_error(
     621             :                 "protx_register_fund \"collateralAddress\" \"ipAndPort\" \"ownerAddress\" \"operatorPubKey\" \"votingAddress\" \"payoutAddress\" (operatorReward \"operatorPayoutAddress\")\n"
     622             :                 "\nCreates, funds and sends a ProTx to the network. The resulting transaction will move 10000 PIV\n"
     623             :                 "to the address specified by collateralAddress and will then function as masternode collateral.\n"
     624           0 :                 + HelpRequiringPassphrase(pwallet) + "\n"
     625             :                 "\nArguments:\n"
     626           0 :                 + GetHelpString(1, collateralAddress)
     627           0 :                 + GetHelpString(2, ipAndPort_register)
     628           0 :                 + GetHelpString(3, ownerAddress)
     629           0 :                 + GetHelpString(4, operatorPubKey_register)
     630           0 :                 + GetHelpString(5, votingAddress_register)
     631           0 :                 + GetHelpString(6, payoutAddress_register)
     632           0 :                 + GetHelpString(7, operatorReward)
     633           0 :                 + GetHelpString(8, operatorPayoutAddress_register) +
     634             :                 "\nResult:\n"
     635             :                 "\"txid\"                        (string) The transaction id.\n"
     636             :                 "\nExamples:\n"
     637           0 :                 + HelpExampleCli("protx_register_fund", "\"DKHHBsuU9zfxxxVaqqqQqK4MxZg6vzpf8\" \"168.192.1.100:51472\" \"DMJRSsuU9zfyrvxVaAEFQqK4MxZg6vgeS6\" \"DMJRSsuU9zfyrvxVaAEFQqK4MxZg6vgeS6\" \"DMJRSsuU9zfyrvxVaAEFQqK4MxZg6vgeS6\" \"DMJRSsuU9zfyrvxVaAEFQqK4MxZg6vgeS6\"")
     638           0 :         );
     639             :     }
     640          44 :     CheckEvoUpgradeEnforcement();
     641             : 
     642          43 :     EnsureWalletIsUnlocked(pwallet);
     643             :     // Make sure the results are valid at least up to the most recent block
     644             :     // the user could have gotten from another RPC command prior to now
     645          43 :     pwallet->BlockUntilSyncedToCurrentChain();
     646             : 
     647          80 :     const CTxDestination& collateralDest(ParsePubKeyIDFromAddress(request.params[0].get_str()));
     648          86 :     const CScript& collateralScript = GetScriptForDestination(collateralDest);
     649          43 :     const CAmount collAmt = Params().GetConsensus().nMNCollateralAmt;
     650             : 
     651          86 :     ProRegPL pl = ParseProRegPLParams(request.params, 1);
     652          43 :     pl.nVersion = ProRegPL::CURRENT_VERSION;
     653             : 
     654          43 :     CMutableTransaction tx;
     655          43 :     tx.nVersion = CTransaction::TxVersion::SAPLING;
     656          43 :     tx.nType = CTransaction::TxType::PROREG;
     657          43 :     tx.vout.emplace_back(collAmt, collateralScript);
     658             : 
     659          43 :     FundSpecialTx(pwallet, tx, pl);
     660             : 
     661          61 :     for (uint32_t i = 0; i < tx.vout.size(); i++) {
     662          61 :         if (tx.vout[i].nValue == collAmt && tx.vout[i].scriptPubKey == collateralScript) {
     663          43 :             pl.collateralOutpoint.n = i;
     664          43 :             break;
     665             :         }
     666             :     }
     667          43 :     assert(pl.collateralOutpoint.n != (uint32_t) -1);
     668             :     // update payload on tx (with final collateral outpoint)
     669          43 :     pl.vchSig.clear();
     670             :     // check the payload, add the tx inputs sigs, and send the tx.
     671          80 :     return SignAndSendSpecialTx(pwallet, tx, pl);
     672             : }
     673             : 
     674             : #endif  //ENABLE_WALLET
     675             : 
     676           0 : static bool CheckWalletOwnsScript(CWallet* pwallet, const CScript& script)
     677             : {
     678             : #ifdef ENABLE_WALLET
     679           0 :     if (!pwallet)
     680             :         return false;
     681           0 :     AssertLockHeld(pwallet->cs_wallet);
     682           0 :     CTxDestination dest;
     683           0 :     if (ExtractDestination(script, dest)) {
     684           0 :         const CKeyID* keyID = boost::get<CKeyID>(&dest);
     685           0 :         if (keyID && pwallet->HaveKey(*keyID))
     686             :             return true;
     687           0 :         const CScriptID* scriptID = boost::get<CScriptID>(&dest);
     688           0 :         if (scriptID && pwallet->HaveCScript(*scriptID))
     689           0 :             return true;
     690             :     }
     691             :     return false;
     692             : #else
     693             :     return false;
     694             : #endif
     695             : }
     696             : 
     697           0 : static UniValue ToJson(const CMasternodeMetaInfoPtr& info)
     698             : {
     699           0 :     UniValue ret(UniValue::VOBJ);
     700           0 :     auto now = GetAdjustedTime();
     701           0 :     auto lastAttempt = info->GetLastOutboundAttempt();
     702           0 :     auto lastSuccess = info->GetLastOutboundSuccess();
     703           0 :     ret.pushKV("last_outbound_attempt", lastAttempt);
     704           0 :     ret.pushKV("last_outbound_attempt_elapsed", now - lastAttempt);
     705           0 :     ret.pushKV("last_outbound_success", lastSuccess);
     706           0 :     ret.pushKV("last_outbound_success_elapsed", now - lastSuccess);
     707           0 :     return ret;
     708             : }
     709             : 
     710         139 : static void AddDMNEntryToList(UniValue& ret, CWallet* pwallet, const CDeterministicMNCPtr& dmn, bool fVerbose, bool fFromWallet)
     711             : {
     712         139 :     assert(!fFromWallet || pwallet);
     713         139 :     assert(ret.isArray());
     714             : 
     715         139 :     bool hasOwnerKey{false};
     716         139 :     bool hasVotingKey{false};
     717         139 :     bool ownsCollateral{false};
     718         139 :     bool ownsPayeeScript{false};
     719             : 
     720             :     // No need to check wallet if not wallet_only and not verbose
     721         139 :     bool skipWalletCheck = !fFromWallet && !fVerbose;
     722             : 
     723         139 :     if (pwallet && !skipWalletCheck) {
     724           0 :         LOCK(pwallet->cs_wallet);
     725           0 :         hasOwnerKey = pwallet->HaveKey(dmn->pdmnState->keyIDOwner);
     726           0 :         hasVotingKey = pwallet->HaveKey(dmn->pdmnState->keyIDVoting);
     727           0 :         ownsPayeeScript = CheckWalletOwnsScript(pwallet, dmn->pdmnState->scriptPayout);
     728           0 :         CTransactionRef collTx;
     729           0 :         uint256 hashBlock;
     730           0 :         if (GetTransaction(dmn->collateralOutpoint.hash, collTx, hashBlock, true)) {
     731           0 :             ownsCollateral = CheckWalletOwnsScript(pwallet, collTx->vout[dmn->collateralOutpoint.n].scriptPubKey);
     732             :         }
     733             :     }
     734             : 
     735         139 :     if (fFromWallet && !hasOwnerKey && !hasVotingKey && !ownsCollateral && !ownsPayeeScript) {
     736             :         // not one of ours
     737             :         return;
     738             :     }
     739             : 
     740         139 :     if (fVerbose) {
     741           0 :         UniValue o = DmnToJson(dmn);
     742           0 :         int confs = WITH_LOCK(cs_main, return pcoinsTip->GetCoinDepthAtHeight(dmn->collateralOutpoint, chainActive.Height()); );
     743           0 :         o.pushKV("confirmations", confs);
     744           0 :         o.pushKV("has_owner_key", hasOwnerKey);
     745           0 :         o.pushKV("has_voting_key", hasVotingKey);
     746           0 :         o.pushKV("owns_collateral", ownsCollateral);
     747           0 :         o.pushKV("owns_payee_script", ownsPayeeScript);
     748             :         // net info
     749           0 :         auto metaInfo = g_mmetaman.GetMetaInfo(dmn->proTxHash);
     750           0 :         if (metaInfo) o.pushKV("metaInfo", ToJson(metaInfo));
     751           0 :         ret.push_back(o);
     752             :     } else {
     753         278 :         ret.push_back(dmn->proTxHash.ToString());
     754             :     }
     755             : }
     756             : 
     757          42 : UniValue protx_list(const JSONRPCRequest& request)
     758             : {
     759          42 :     if (request.fHelp || request.params.size() > 4) {
     760           0 :         throw std::runtime_error(
     761             :                 "protx_list (detailed wallet_only valid_only height)\n"
     762             :                 "\nLists all ProTxs.\n"
     763             :                 "\nArguments:\n"
     764             :                 "1. \"detailed\"               (bool, optional, default=true) Return detailed information about each protx.\n"
     765             :                 "                                 If set to false, return only the list of txids.\n"
     766             :                 "2. \"wallet_only\"            (bool, optional, default=false) If set to true, return only protx which involves\n"
     767             :                 "                                 keys from this wallet (collateral, owner, operator, voting, or payout addresses).\n"
     768             :                 "3. \"valid_only\"             (bool, optional, default=false) If set to true, return only ProTx which are active/valid\n"
     769             :                 "                                 at the height specified.\n"
     770             :                 "4. \"height\"                 (numeric, optional) If height is not specified, it defaults to the current chain-tip.\n"
     771             :                 "\nResult:\n"
     772             :                 "[...]                         (list) List of protx txids or, if detailed=true, list of json objects.\n"
     773             :                 "\nExamples:\n"
     774           0 :                 + HelpExampleCli("protx_list", "")
     775           0 :                 + HelpExampleCli("protx_list", "true false false 200000")
     776           0 :         );
     777             :     }
     778             : 
     779          42 :     CheckEvoUpgradeEnforcement();
     780             : 
     781             : #ifdef ENABLE_WALLET
     782          42 :     CWallet* const pwallet = GetWalletForJSONRPCRequest(request);
     783             : #else
     784             :     CWallet* const pwallet = nullptr;
     785             : #endif
     786             : 
     787          42 :     const bool fVerbose = (request.params.size() == 0 || request.params[0].get_bool());
     788          42 :     const bool fFromWallet = (request.params.size() > 1 && request.params[1].get_bool());
     789          42 :     const bool fValidOnly = (request.params.size() > 2 && request.params[2].get_bool());
     790             : 
     791          42 :     if (fFromWallet && !pwallet) {
     792           0 :         throw JSONRPCError(RPC_INVALID_PARAMETER, "wallet_only not supported when wallet is disabled");
     793             :     }
     794             : 
     795             :     // Get a reference to the block index at the specified height (or at the chain tip)
     796          42 :     const CBlockIndex* pindex;
     797          42 :     {
     798          42 :         LOCK(cs_main);
     799          42 :         const CBlockIndex* pindexTip = chainActive.Tip();
     800          42 :         if (request.params.size() > 3) {
     801           0 :             const int height = request.params[3].get_int();
     802           0 :             if (height <= 0 || height > pindexTip->nHeight) {
     803           0 :                 throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("height must be between 1 and %d", pindexTip->nHeight));
     804             :             }
     805           0 :             pindexTip = chainActive[height];
     806             :         }
     807          42 :         pindex = mapBlockIndex.at(pindexTip->GetBlockHash());
     808             :     }
     809             : 
     810             :     // Get the deterministic mn list at the index
     811          42 :     CDeterministicMNList mnList = deterministicMNManager->GetListForBlock(pindex);
     812             : 
     813             :     // Build/filter the list
     814          42 :     UniValue ret(UniValue::VARR);
     815          42 :     mnList.ForEachMN(fValidOnly, [&](const CDeterministicMNCPtr& dmn) {
     816         139 :         AddDMNEntryToList(ret, pwallet, dmn, fVerbose, fFromWallet);
     817         139 :     });
     818          42 :     return ret;
     819             : }
     820             : 
     821             : #ifdef ENABLE_WALLET
     822          11 : UniValue protx_update_service(const JSONRPCRequest& request)
     823             : {
     824          11 :     CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
     825             : 
     826          11 :     if (!EnsureWalletIsAvailable(pwallet, request.fHelp))
     827           0 :         return NullUniValue;
     828             : 
     829          11 :     if (request.fHelp || request.params.size() < 2 || request.params.size() > 4) {
     830           0 :         throw std::runtime_error(
     831             :                 "protx_update_service \"proTxHash\" \"ipAndPort\" (\"operatorPayoutAddress\" \"operatorKey\")\n"
     832             :                 "\nCreates and sends a ProUpServTx to the network. This will update the IP address\n"
     833             :                 "of a masternode, and/or the operator payout address.\n"
     834             :                 "If the IP is changed for a masternode that got PoSe-banned, the ProUpServTx will also revive this masternode.\n"
     835           0 :                 + HelpRequiringPassphrase(pwallet) + "\n"
     836             :                 "\nArguments:\n"
     837           0 :                 + GetHelpString(1, proTxHash)
     838           0 :                 + GetHelpString(2, ipAndPort_update)
     839           0 :                 + GetHelpString(3, operatorPayoutAddress_update)
     840           0 :                 + GetHelpString(4, operatorKey) +
     841             :                 "\nResult:\n"
     842             :                 "\"txid\"                        (string) The transaction id.\n"
     843             :                 "\nExamples:\n"
     844           0 :                 + HelpExampleCli("protx_update_service", "\"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef\" \"168.192.1.100:51472\"")
     845           0 :         );
     846             :     }
     847          11 :     CheckEvoUpgradeEnforcement();
     848             : 
     849          11 :     EnsureWalletIsUnlocked(pwallet);
     850             :     // Make sure the results are valid at least up to the most recent block
     851             :     // the user could have gotten from another RPC command prior to now
     852          11 :     pwallet->BlockUntilSyncedToCurrentChain();
     853             : 
     854          11 :     ProUpServPL pl;
     855          11 :     pl.nVersion = ProUpServPL::CURRENT_VERSION;
     856          11 :     pl.proTxHash = ParseHashV(request.params[0], "proTxHash");
     857             : 
     858          22 :     auto dmn = deterministicMNManager->GetListAtChainTip().GetMN(pl.proTxHash);
     859          11 :     if (!dmn) {
     860           3 :         throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("masternode with hash %s not found", pl.proTxHash.ToString()));
     861             :     }
     862          10 :     const auto& chainparams = Params();
     863          10 :     const std::string& addrStr = request.params[1].get_str();
     864          10 :     if (!addrStr.empty()) {
     865           8 :         if (!Lookup(addrStr.c_str(), pl.addr, chainparams.GetDefaultPort(), false)) {
     866           0 :             throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("invalid network address %s", addrStr));
     867             :         }
     868             :     } else {
     869           2 :         pl.addr = dmn->pdmnState->addr;
     870             :     }
     871          10 :     pl.scriptOperatorPayout = dmn->pdmnState->scriptOperatorPayout;
     872          10 :     if (request.params.size() > 2) {
     873           9 :         const std::string& strAddOpPayee = request.params[2].get_str();
     874           9 :         if (!strAddOpPayee.empty()) {
     875           3 :             if (dmn->nOperatorReward > 0) {
     876           6 :                 pl.scriptOperatorPayout = GetScriptForDestination(CTxDestination(ParsePubKeyIDFromAddress(strAddOpPayee)));
     877             :             } else {
     878           2 :                 throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Operator reward is 0. Cannot set operator payout address"));
     879             :             }
     880             :         }
     881             :     }
     882             : 
     883          23 :     const std::string& strOpKey = request.params.size() > 3 ? request.params[3].get_str() : "";
     884          16 :     const CBLSSecretKey& operatorKey = GetBLSSecretKey(chainparams, strOpKey);
     885             : 
     886          14 :     CMutableTransaction tx;
     887           8 :     tx.nVersion = CTransaction::TxVersion::SAPLING;
     888           8 :     tx.nType = CTransaction::TxType::PROUPSERV;
     889             : 
     890           8 :     FundSpecialTx(pwallet, tx, pl);
     891           8 :     SignSpecialTxPayloadByHash(tx, pl, operatorKey);
     892             : 
     893          14 :     return SignAndSendSpecialTx(pwallet, tx, pl);
     894             : }
     895             : 
     896          12 : UniValue protx_update_registrar(const JSONRPCRequest& request)
     897             : {
     898          12 :     CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
     899             : 
     900          12 :     if (!EnsureWalletIsAvailable(pwallet, request.fHelp))
     901           0 :         return NullUniValue;
     902             : 
     903          12 :     if (request.fHelp || request.params.size() < 4 || request.params.size() > 5) {
     904           0 :         throw std::runtime_error(
     905             :                 "protx update_registrar \"proTxHash\" \"operatorPubKey\" \"votingAddress\" \"payoutAddress\" (\"ownerKey\")\n"
     906             :                 "\nCreates and sends a ProUpRegTx to the network. This will update the operator key, voting key and payout\n"
     907             :                 "address of the masternode specified by \"proTxHash\".\n"
     908             :                 "The owner key of this masternode must be known to your wallet.\n"
     909           0 :                 + HelpRequiringPassphrase(pwallet) + "\n"
     910             :                 "\nArguments:\n"
     911           0 :                 + GetHelpString(1, proTxHash)
     912           0 :                 + GetHelpString(2, operatorPubKey_update)
     913           0 :                 + GetHelpString(3, votingAddress_update)
     914           0 :                 + GetHelpString(4, payoutAddress_update)
     915           0 :                 + GetHelpString(5, ownerKey) +
     916             :                 "\nResult:\n"
     917             :                 "\"txid\"                        (string) The transaction id.\n"
     918             :                 "\nExamples:\n"
     919           0 :                 + HelpExampleCli("protx_update_registrar", "\"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef\" \"DMJRSsuU9zfyrvxVaAEFQqK4MxZg6vgeS6\" \"DMJRSsuU9zfyrvxVaAEFQqK4MxZg6vgeS6\" \"DMJRSsuU9zfyrvxVaAEFQqK4MxZg6vgeS6\"")
     920           0 :         );
     921             :     }
     922          12 :     CheckEvoUpgradeEnforcement();
     923             : 
     924          12 :     if (!deterministicMNManager->LegacyMNObsolete()) {
     925           0 :         throw JSONRPCError(RPC_MISC_ERROR, "Legacy masternode system still active. ProUpReg transactions are not accepted yet.");
     926             :     }
     927             : 
     928          12 :     EnsureWalletIsUnlocked(pwallet);
     929             :     // Make sure the results are valid at least up to the most recent block
     930             :     // the user could have gotten from another RPC command prior to now
     931          12 :     pwallet->BlockUntilSyncedToCurrentChain();
     932          12 :     const auto& chainparams = Params();
     933             : 
     934          12 :     ProUpRegPL pl;
     935          12 :     pl.nVersion = ProUpServPL::CURRENT_VERSION;
     936          12 :     pl.proTxHash = ParseHashV(request.params[0], "proTxHash");
     937             : 
     938          24 :     auto dmn = deterministicMNManager->GetListAtChainTip().GetMN(pl.proTxHash);
     939          12 :     if (!dmn) {
     940           3 :         throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("masternode with hash %s not found", pl.proTxHash.ToString()));
     941             :     }
     942          11 :     const std::string& strPubKeyOperator = request.params[1].get_str();
     943          11 :     pl.pubKeyOperator = strPubKeyOperator.empty() ? dmn->pdmnState->pubKeyOperator.Get()
     944             :                                                   : ParseBLSPubKey(chainparams, strPubKeyOperator);
     945             : 
     946          11 :     const std::string& strVotingAddress = request.params[2].get_str();
     947          11 :     pl.keyIDVoting = strVotingAddress.empty() ? dmn->pdmnState->keyIDVoting
     948           3 :                                               : ParsePubKeyIDFromAddress(strVotingAddress);
     949             : 
     950          11 :     const std::string& strPayee = request.params[3].get_str();
     951          12 :     pl.scriptPayout = strPayee.empty() ? pl.scriptPayout = dmn->pdmnState->scriptPayout
     952          14 :                                        : GetScriptForDestination(CTxDestination(ParsePubKeyIDFromAddress(strPayee)));
     953             : 
     954          22 :     const std::string& strOwnKey = request.params.size() > 4 ? request.params[4].get_str() : "";
     955          10 :     const CKey& ownerKey = strOwnKey.empty() ? GetKeyFromWallet(pwallet, dmn->pdmnState->keyIDOwner)
     956          20 :                                              : ParsePrivKey(pwallet, strOwnKey, false);
     957             : 
     958          18 :     CMutableTransaction tx;
     959          10 :     tx.nVersion = CTransaction::TxVersion::SAPLING;
     960          10 :     tx.nType = CTransaction::TxType::PROUPREG;
     961             : 
     962             :     // make sure fee calculation works
     963          10 :     pl.vchSig.resize(CPubKey::COMPACT_SIGNATURE_SIZE);
     964          10 :     FundSpecialTx(pwallet, tx, pl);
     965          10 :     SignSpecialTxPayloadByHash(tx, pl, ownerKey);
     966             : 
     967          18 :     return SignAndSendSpecialTx(pwallet, tx, pl);
     968             : }
     969             : 
     970           6 : UniValue protx_revoke(const JSONRPCRequest& request)
     971             : {
     972           6 :     CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
     973             : 
     974           6 :     if (!EnsureWalletIsAvailable(pwallet, request.fHelp))
     975           0 :         return NullUniValue;
     976             : 
     977           6 :     if (request.fHelp || request.params.size() < 1 || request.params.size() > 3) {
     978           0 :         throw std::runtime_error(
     979             :                 "protx_update_revoke \"proTxHash\" (\"operatorKey\" reason)\n"
     980             :                 "\nCreates and sends a ProUpRevTx to the network. This will revoke the operator key of the masternode and\n"
     981             :                 "put it into the PoSe-banned state. It will also set the service field of the masternode\n"
     982             :                 "to zero. Use this in case your operator key got compromised or you want to stop providing your service\n"
     983             :                 "to the masternode owner.\n"
     984           0 :                 + HelpRequiringPassphrase(pwallet) + "\n"
     985             :                 "\nArguments:\n"
     986           0 :                 + GetHelpString(1, proTxHash)
     987           0 :                 + GetHelpString(2, operatorKey)
     988           0 :                 + GetHelpString(3, revocationReason) +
     989             :                 "\nResult:\n"
     990             :                 "\"txid\"                        (string) The transaction id.\n"
     991             :                 "\nExamples:\n"
     992           0 :                 + HelpExampleCli("protx_revoke", "\"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef\"")
     993           0 :                 + HelpExampleCli("protx_revoke", "\"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef\" \"\" 2")
     994           0 :         );
     995             :     }
     996           6 :     CheckEvoUpgradeEnforcement();
     997             : 
     998           6 :     EnsureWalletIsUnlocked(pwallet);
     999             :     // Make sure the results are valid at least up to the most recent block
    1000             :     // the user could have gotten from another RPC command prior to now
    1001           6 :     pwallet->BlockUntilSyncedToCurrentChain();
    1002           6 :     const auto& chainparams = Params();
    1003             : 
    1004           6 :     ProUpRevPL pl;
    1005           6 :     pl.nVersion = ProUpServPL::CURRENT_VERSION;
    1006           6 :     pl.proTxHash = ParseHashV(request.params[0], "proTxHash");
    1007             : 
    1008          10 :     auto dmn = deterministicMNManager->GetListAtChainTip().GetMN(pl.proTxHash);
    1009           6 :     if (!dmn) {
    1010           3 :         throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("masternode with hash %s not found", pl.proTxHash.ToString()));
    1011             :     }
    1012             : 
    1013          15 :     const std::string& strOpKey = request.params.size() > 1 ? request.params[1].get_str() : "";
    1014          10 :     const CBLSSecretKey& operatorKey = GetBLSSecretKey(chainparams, strOpKey);
    1015             : 
    1016           5 :     pl.nReason = ProUpRevPL::RevocationReason::REASON_NOT_SPECIFIED;
    1017           5 :     if (request.params.size() > 2) {
    1018           3 :         int nReason = request.params[2].get_int();
    1019           3 :         if (nReason < 0 || nReason > ProUpRevPL::RevocationReason::REASON_LAST) {
    1020           0 :             throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("invalid reason %d, must be between 0 and %d",
    1021           2 :                                                                 nReason, ProUpRevPL::RevocationReason::REASON_LAST));
    1022             :         }
    1023           2 :         pl.nReason = (uint16_t)nReason;
    1024             :     }
    1025             : 
    1026           8 :     CMutableTransaction tx;
    1027           4 :     tx.nVersion = CTransaction::TxVersion::SAPLING;
    1028           4 :     tx.nType = CTransaction::TxType::PROUPREV;
    1029             : 
    1030           4 :     FundSpecialTx(pwallet, tx, pl);
    1031           4 :     SignSpecialTxPayloadByHash(tx, pl, operatorKey);
    1032             : 
    1033           8 :     return SignAndSendSpecialTx(pwallet, tx, pl);
    1034             : }
    1035             : #endif
    1036             : 
    1037          69 : UniValue generateblskeypair(const JSONRPCRequest& request)
    1038             : {
    1039          69 :     if (request.fHelp || !request.params.empty()) {
    1040           0 :         throw std::runtime_error(
    1041             :                 "generateblskeypair\n"
    1042             :                 "\nReturns a BLS secret/public key pair.\n"
    1043             :                 "\nResult:\n"
    1044             :                 "{\n"
    1045             :                 "  \"secret\": \"xxxx\",        (string) BLS secret key\n"
    1046             :                 "  \"public\": \"xxxx\",        (string) BLS public key\n"
    1047             :                 "}\n"
    1048             :                 "\nExamples:\n"
    1049           0 :                 + HelpExampleCli("generateblskeypair", "")
    1050           0 :                 + HelpExampleRpc("generateblskeypair", "")
    1051           0 :         );
    1052             :     }
    1053             : 
    1054          69 :     const auto& params = Params();
    1055          69 :     CBLSSecretKey sk;
    1056          69 :     sk.MakeNewKey();
    1057          69 :     UniValue ret(UniValue::VOBJ);
    1058         138 :     ret.pushKV("secret", bls::EncodeSecret(params, sk));
    1059         138 :     ret.pushKV("public", bls::EncodePublic(params, sk.GetPublicKey()));
    1060          69 :     return ret;
    1061             : }
    1062             : 
    1063             : // clang-format off
    1064             : static const CRPCCommand commands[] =
    1065             : { //  category       name                              actor (function)         okSafe argNames
    1066             :   //  -------------- --------------------------------- ------------------------ ------ --------
    1067             :     { "evo",         "generateblskeypair",             &generateblskeypair,     true,  {} },
    1068             :     { "evo",         "protx_list",                     &protx_list,             true,  {"detailed","wallet_only","valid_only","height"} },
    1069             : #ifdef ENABLE_WALLET
    1070             :     { "evo",         "protx_register",                 &protx_register,         true,  {"collateralHash","collateralIndex","ipAndPort","ownerAddress","operatorPubKey","votingAddress","payoutAddress","operatorReward","operatorPayoutAddress"} },
    1071             :     { "evo",         "protx_register_fund",            &protx_register_fund,    true,  {"collateralAddress","ipAndPort","ownerAddress","operatorPubKey","votingAddress","payoutAddress","operatorReward","operatorPayoutAddress"} },
    1072             :     { "evo",         "protx_register_prepare",         &protx_register_prepare, true,  {"collateralHash","collateralIndex","ipAndPort","ownerAddress","operatorPubKey","votingAddress","payoutAddress","operatorReward","operatorPayoutAddress"} },
    1073             :     { "evo",         "protx_register_submit",          &protx_register_submit,  true,  {"tx","sig"} },
    1074             :     { "evo",         "protx_revoke",                   &protx_revoke,           true,  {"proTxHash","operatorKey","reason"} },
    1075             :     { "evo",         "protx_update_registrar",         &protx_update_registrar, true,  {"proTxHash","operatorPubKey","votingAddress","payoutAddress","ownerKey"} },
    1076             :     { "evo",         "protx_update_service",           &protx_update_service,   true,  {"proTxHash","ipAndPort","operatorPayoutAddress","operatorKey"} },
    1077             : #endif  //ENABLE_WALLET
    1078             : };
    1079             : // clang-format on
    1080             : 
    1081         494 : void RegisterEvoRPCCommands(CRPCTable& _tableRPC)
    1082             : {
    1083        4940 :     for (const auto& command : commands) {
    1084        4446 :         _tableRPC.appendCommand(command.name, &command);
    1085             :     }
    1086         494 : }

Generated by: LCOV version 1.14