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