Line data Source code
1 : // Copyright (c) 2010 Satoshi Nakamoto
2 : // Copyright (c) 2009-2014 The Bitcoin developers
3 : // Copyright (c) 2014-2015 The Dash developers
4 : // Copyright (c) 2015-2022 The PIVX Core developers
5 : // Distributed under the MIT software license, see the accompanying
6 : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
7 :
8 : #include "clientversion.h"
9 : #include "httpserver.h"
10 : #include "key_io.h"
11 : #include "sapling/key_io_sapling.h"
12 : #include "masternode-sync.h"
13 : #include "messagesigner.h"
14 : #include "net.h"
15 : #include "netbase.h"
16 : #include "tiertwo/net_masternodes.h"
17 : #include "rpc/server.h"
18 : #include "spork.h"
19 : #include "timedata.h"
20 : #include "tiertwo/tiertwo_sync_state.h"
21 : #include "util/system.h"
22 : #ifdef ENABLE_WALLET
23 : #include "wallet/rpcwallet.h"
24 : #include "wallet/wallet.h"
25 : #endif
26 : #include "warnings.h"
27 :
28 : #include <stdint.h>
29 :
30 : #include <univalue.h>
31 :
32 : extern std::vector<CSporkDef> sporkDefs;
33 :
34 : /** getinfo depends on getsupplyinfo defined in rpc/blockchain.cpp */
35 : UniValue getsupplyinfo(const JSONRPCRequest& request);
36 :
37 : /**
38 : * @note Do not add or change anything in the information returned by this
39 : * method. `getinfo` exists for backwards-compatibility only. It combines
40 : * information from wildly different sources in the program, which is a mess,
41 : * and is thus planned to be deprecated eventually.
42 : *
43 : * Based on the source of the information, new information should be added to:
44 : * - `getblockchaininfo`,
45 : * - `getnetworkinfo` or
46 : * - `getwalletinfo`
47 : *
48 : * Or alternatively, create a specific query method for the information.
49 : **/
50 1 : UniValue getinfo(const JSONRPCRequest& request)
51 : {
52 1 : CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
53 :
54 1 : if (request.fHelp || request.params.size() != 0)
55 0 : throw std::runtime_error(
56 : "getinfo\n"
57 : "\nReturns an object containing various state info.\n"
58 :
59 : "\nResult:\n"
60 : "{\n"
61 : " \"version\": xxxxx, (numeric) the server version\n"
62 : " \"protocolversion\": xxxxx, (numeric) the protocol version\n"
63 : " \"services\": \"xxxx\", (string) The network services provided by this client\n"
64 : " \"walletversion\": xxxxx, (numeric) the wallet version\n"
65 : " \"balance\": xxxxxxx, (numeric) the total pivx balance of the wallet\n"
66 : " \"staking status\": true|false, (boolean) if the wallet is staking or not\n"
67 : " \"blocks\": xxxxxx, (numeric) the current number of blocks processed in the server\n"
68 : " \"timeoffset\": xxxxx, (numeric) the time offset\n"
69 : " \"connections\": xxxxx, (numeric) the number of connections\n"
70 : " \"proxy\": \"host:port\", (string, optional) the proxy used by the server\n"
71 : " \"difficulty\": xxxxxx, (numeric) the current difficulty\n"
72 : " \"testnet\": true|false, (boolean) if the server is using testnet or not\n"
73 : " \"moneysupply\": n (numeric) The sum of transparentsupply and shieldedsupply\n"
74 : " \"transparentsupply\" : n (numeric) The sum of the value of all unspent outputs when the chainstate was\n"
75 : " last flushed to disk (use getsupplyinfo to know the update-height, or\n"
76 : " to trigger the money supply update/recalculation)"
77 : " \"shieldsupply\": n (numeric) Chain tip shield pool value\n"
78 : " \"keypoololdest\": xxxxxx, (numeric) the timestamp (seconds since GMT epoch) of the oldest pre-generated key in the key pool\n"
79 : " \"keypoolsize\": xxxx, (numeric) how many new keys are pre-generated\n"
80 : " \"unlocked_until\": ttt, (numeric) the timestamp in seconds since epoch (midnight Jan 1 1970 GMT) that the wallet is unlocked for transfers, or 0 if the wallet is locked\n"
81 0 : " \"paytxfee\": x.xxxx, (numeric) the transaction fee set in " + CURRENCY_UNIT + "/kB\n"
82 0 : " \"relayfee\": x.xxxx, (numeric) minimum relay fee for transactions in " + CURRENCY_UNIT + "/kB\n"
83 : " \"errors\": \"...\" (string) any error messages\n"
84 : "}\n"
85 :
86 0 : "\nExamples:\n" +
87 0 : HelpExampleCli("getinfo", "") + HelpExampleRpc("getinfo", ""));
88 :
89 : #ifdef ENABLE_WALLET
90 2 : LOCK2(cs_main, pwallet ? &pwallet->cs_wallet : nullptr);
91 : #else
92 : LOCK(cs_main);
93 : #endif
94 :
95 2 : std::string services;
96 9 : for (int i = 0; i < 8; i++) {
97 8 : uint64_t check = 1 << i;
98 8 : if (g_connman->GetLocalServices() & check) {
99 2 : switch (check) {
100 1 : case NODE_NETWORK:
101 1 : services+= "NETWORK/";
102 : break;
103 1 : case NODE_BLOOM:
104 1 : case NODE_BLOOM_WITHOUT_MN:
105 1 : services+= "BLOOM/";
106 : break;
107 0 : default:
108 8 : services+= "UNKNOWN/";
109 : }
110 : }
111 : }
112 :
113 2 : proxyType proxy;
114 1 : GetProxy(NET_IPV4, proxy);
115 :
116 1 : UniValue obj(UniValue::VOBJ);
117 1 : obj.pushKV("version", CLIENT_VERSION);
118 1 : obj.pushKV("protocolversion", PROTOCOL_VERSION);
119 1 : obj.pushKV("services", services);
120 : #ifdef ENABLE_WALLET
121 1 : if (pwallet) {
122 1 : obj.pushKV("walletversion", pwallet->GetVersion());
123 2 : obj.pushKV("balance", ValueFromAmount(pwallet->GetAvailableBalance()));
124 3 : obj.pushKV("staking status", (pwallet->pStakerStatus->IsActive() ? "Staking Active" : "Staking Not Active"));
125 : }
126 : #endif
127 1 : obj.pushKV("blocks", (int)chainActive.Height());
128 1 : obj.pushKV("timeoffset", GetTimeOffset());
129 1 : if(g_connman) {
130 2 : obj.pushKV("connections", (int)g_connman->GetNodeCount(CConnman::CONNECTIONS_ALL));
131 : }
132 3 : obj.pushKV("proxy", (proxy.IsValid() ? proxy.proxy.ToStringIPPort() : std::string()));
133 1 : obj.pushKV("difficulty", (double)GetDifficulty());
134 1 : obj.pushKV("testnet", Params().IsTestnet());
135 :
136 : // Add (cached) money supply via getsupplyinfo RPC
137 2 : UniValue supply_info = getsupplyinfo(JSONRPCRequest());
138 2 : obj.pushKV("moneysupply", supply_info["totalsupply"]);
139 2 : obj.pushKV("transparentsupply", supply_info["transparentsupply"]);
140 2 : obj.pushKV("shieldsupply", supply_info["shieldsupply"]);
141 :
142 : #ifdef ENABLE_WALLET
143 1 : if (pwallet) {
144 1 : obj.pushKV("keypoololdest", pwallet->GetOldestKeyPoolTime());
145 1 : size_t kpExternalSize = pwallet->KeypoolCountExternalKeys();
146 2 : obj.pushKV("keypoolsize", (int64_t)kpExternalSize);
147 : }
148 1 : if (pwallet && pwallet->IsCrypted())
149 0 : obj.pushKV("unlocked_until", pwallet->nRelockTime);
150 2 : obj.pushKV("paytxfee", ValueFromAmount(payTxFee.GetFeePerK()));
151 : #endif
152 2 : obj.pushKV("relayfee", ValueFromAmount(::minRelayTxFee.GetFeePerK()));
153 3 : obj.pushKV("errors", GetWarnings("statusbar"));
154 2 : return obj;
155 : }
156 :
157 245 : UniValue mnsync(const JSONRPCRequest& request)
158 : {
159 490 : std::string strMode;
160 245 : if (request.params.size() == 1)
161 245 : strMode = request.params[0].get_str();
162 :
163 490 : if (request.fHelp || request.params.size() != 1 || (strMode != "status" && strMode != "reset")) {
164 0 : throw std::runtime_error(
165 : "mnsync \"status|reset\"\n"
166 : "\nReturns the sync status or resets sync.\n"
167 :
168 : "\nArguments:\n"
169 : "1. \"mode\" (string, required) either 'status' or 'reset'\n"
170 :
171 : "\nResult ('status' mode):\n"
172 : "{\n"
173 : " \"IsBlockchainSynced\": true|false, (boolean) 'true' if blockchain is synced\n"
174 : " \"lastMasternodeList\": xxxx, (numeric) Timestamp of last MN list message\n"
175 : " \"lastMasternodeWinner\": xxxx, (numeric) Timestamp of last MN winner message\n"
176 : " \"lastBudgetItem\": xxxx, (numeric) Timestamp of last MN budget message\n"
177 : " \"lastFailure\": xxxx, (numeric) Timestamp of last failed sync\n"
178 : " \"nCountFailures\": n, (numeric) Number of failed syncs (total)\n"
179 : " \"sumMasternodeList\": n, (numeric) Number of MN list messages (total)\n"
180 : " \"sumMasternodeWinner\": n, (numeric) Number of MN winner messages (total)\n"
181 : " \"sumBudgetItemProp\": n, (numeric) Number of MN budget messages (total)\n"
182 : " \"sumBudgetItemFin\": n, (numeric) Number of MN budget finalization messages (total)\n"
183 : " \"countMasternodeList\": n, (numeric) Number of MN list messages (local)\n"
184 : " \"countMasternodeWinner\": n, (numeric) Number of MN winner messages (local)\n"
185 : " \"countBudgetItemProp\": n, (numeric) Number of MN budget messages (local)\n"
186 : " \"countBudgetItemFin\": n, (numeric) Number of MN budget finalization messages (local)\n"
187 : " \"RequestedMasternodeAssets\": n, (numeric) Status code of last sync phase\n"
188 : " \"RequestedMasternodeAttempt\": n, (numeric) Status code of last sync attempt\n"
189 : "}\n"
190 :
191 : "\nResult ('reset' mode):\n"
192 : "\"status\" (string) 'success'\n"
193 :
194 0 : "\nExamples:\n" +
195 0 : HelpExampleCli("mnsync", "\"status\"") + HelpExampleRpc("mnsync", "\"status\""));
196 : }
197 :
198 245 : if (strMode == "status") {
199 490 : UniValue obj(UniValue::VOBJ);
200 :
201 245 : obj.pushKV("IsBlockchainSynced", g_tiertwo_sync_state.IsBlockchainSynced());
202 245 : obj.pushKV("lastMasternodeList", g_tiertwo_sync_state.GetlastMasternodeList());
203 245 : obj.pushKV("lastMasternodeWinner", g_tiertwo_sync_state.GetlastMasternodeWinner());
204 245 : obj.pushKV("lastBudgetItem", g_tiertwo_sync_state.GetlastBudgetItem());
205 245 : obj.pushKV("lastFailure", masternodeSync.lastFailure);
206 245 : obj.pushKV("nCountFailures", masternodeSync.nCountFailures);
207 245 : obj.pushKV("sumMasternodeList", masternodeSync.sumMasternodeList);
208 245 : obj.pushKV("sumMasternodeWinner", masternodeSync.sumMasternodeWinner);
209 245 : obj.pushKV("sumBudgetItemProp", masternodeSync.sumBudgetItemProp);
210 245 : obj.pushKV("sumBudgetItemFin", masternodeSync.sumBudgetItemFin);
211 245 : obj.pushKV("countMasternodeList", masternodeSync.countMasternodeList);
212 245 : obj.pushKV("countMasternodeWinner", masternodeSync.countMasternodeWinner);
213 245 : obj.pushKV("countBudgetItemProp", masternodeSync.countBudgetItemProp);
214 245 : obj.pushKV("countBudgetItemFin", masternodeSync.countBudgetItemFin);
215 245 : obj.pushKV("RequestedMasternodeAssets", g_tiertwo_sync_state.GetSyncPhase());
216 245 : obj.pushKV("RequestedMasternodeAttempt", masternodeSync.RequestedMasternodeAttempt);
217 :
218 245 : return obj;
219 : }
220 :
221 0 : if (strMode == "reset") {
222 0 : masternodeSync.Reset();
223 0 : return "success";
224 : }
225 0 : return "failure";
226 : }
227 :
228 : #ifdef ENABLE_WALLET
229 : class DescribeAddressVisitor : public boost::static_visitor<UniValue>
230 : {
231 : public:
232 : CWallet * const pwallet;
233 :
234 96 : explicit DescribeAddressVisitor(CWallet *_pwallet) : pwallet(_pwallet) {}
235 :
236 0 : UniValue operator()(const CNoDestination &dest) const { return UniValue(UniValue::VOBJ); }
237 :
238 91 : UniValue operator()(const CKeyID &keyID) const {
239 91 : UniValue obj(UniValue::VOBJ);
240 91 : CPubKey vchPubKey;
241 91 : obj.pushKV("isscript", false);
242 91 : if (pwallet && pwallet->GetPubKey(keyID, vchPubKey)) {
243 144 : obj.pushKV("pubkey", HexStr(vchPubKey));
244 144 : obj.pushKV("iscompressed", vchPubKey.IsCompressed());
245 : }
246 91 : return obj;
247 : }
248 :
249 1 : UniValue operator()(const CExchangeKeyID &keyID) const {
250 1 : UniValue obj(UniValue::VOBJ);
251 1 : CPubKey vchPubKey;
252 1 : obj.pushKV("isscript", false);
253 1 : if (pwallet && pwallet->GetPubKey(keyID, vchPubKey)) {
254 0 : obj.pushKV("exchangepubkey", HexStr(vchPubKey));
255 0 : obj.pushKV("iscompressed", vchPubKey.IsCompressed());
256 : }
257 1 : return obj;
258 : }
259 :
260 4 : UniValue operator()(const CScriptID &scriptID) const {
261 4 : UniValue obj(UniValue::VOBJ);
262 4 : obj.pushKV("isscript", true);
263 4 : CScript subscript;
264 4 : if (pwallet && pwallet->GetCScript(scriptID, subscript)) {
265 6 : std::vector<CTxDestination> addresses;
266 3 : txnouttype whichType;
267 3 : int nRequired;
268 3 : ExtractDestinations(subscript, whichType, addresses, nRequired);
269 3 : obj.pushKV("script", GetTxnOutputType(whichType));
270 9 : obj.pushKV("hex", HexStr(subscript));
271 6 : UniValue a(UniValue::VARR);
272 11 : for (const CTxDestination& addr : addresses)
273 16 : a.push_back(EncodeDestination(addr));
274 3 : obj.pushKV("addresses", a);
275 3 : if (whichType == TX_MULTISIG)
276 6 : obj.pushKV("sigsrequired", nRequired);
277 : }
278 7 : return obj;
279 : }
280 : };
281 : #endif
282 :
283 : /*
284 : Used for updating/reading spork settings on the network
285 : */
286 169 : UniValue spork(const JSONRPCRequest& request)
287 : {
288 169 : if (request.params.size() == 1 && request.params[0].get_str() == "show") {
289 184 : UniValue ret(UniValue::VOBJ);
290 1012 : for (const auto& sporkDef : sporkDefs) {
291 920 : ret.pushKV(sporkDef.name, sporkManager.GetSporkValue(sporkDef.sporkId));
292 : }
293 92 : return ret;
294 77 : } else if (request.params.size() == 1 && request.params[0].get_str() == "active") {
295 72 : UniValue ret(UniValue::VOBJ);
296 396 : for (const auto& sporkDef : sporkDefs) {
297 360 : ret.pushKV(sporkDef.name, sporkManager.IsSporkActive(sporkDef.sporkId));
298 : }
299 36 : return ret;
300 41 : } else if (request.params.size() == 2) {
301 : // advanced mode, update spork values
302 41 : SporkId nSporkID = sporkManager.GetSporkIDByName(request.params[0].get_str());
303 41 : if (nSporkID == SPORK_INVALID) {
304 0 : throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid spork name");
305 : }
306 :
307 : // SPORK VALUE
308 41 : int64_t nValue = request.params[1].get_int64();
309 :
310 : //broadcast new spork
311 41 : if (sporkManager.UpdateSpork(nSporkID, nValue)) {
312 41 : return "success";
313 : } else {
314 0 : return "failure";
315 : }
316 : }
317 :
318 0 : throw std::runtime_error(
319 : "spork \"name\" ( value )\n"
320 : "\nReturn spork values or their active state.\n"
321 :
322 : "\nArguments:\n"
323 : "1. \"name\" (string, required) \"show\" to show values, \"active\" to show active state.\n"
324 : " When set up as a spork signer, the name of the spork can be used to update it's value.\n"
325 : "2. value (numeric, required when updating a spork) The new value for the spork.\n"
326 :
327 : "\nResult (show):\n"
328 : "{\n"
329 : " \"spork_name\": nnn (key/value) Key is the spork name, value is it's current value.\n"
330 : " ,...\n"
331 : "}\n"
332 :
333 : "\nResult (active):\n"
334 : "{\n"
335 : " \"spork_name\": true|false (key/value) Key is the spork name, value is a boolean for it's active state.\n"
336 : " ,...\n"
337 : "}\n"
338 :
339 : "\nResult (name):\n"
340 : " \"success|failure\" (string) Whether or not the update succeeded.\n"
341 :
342 0 : "\nExamples:\n" +
343 0 : HelpExampleCli("spork", "show") + HelpExampleRpc("spork", "show"));
344 : }
345 :
346 : // Every possibly address
347 : typedef boost::variant<libzcash::InvalidEncoding, libzcash::SaplingPaymentAddress, CTxDestination> PPaymentAddress;
348 :
349 : class DescribePaymentAddressVisitor : public boost::static_visitor<UniValue>
350 : {
351 : public:
352 98 : explicit DescribePaymentAddressVisitor(CWallet *_pwallet, bool _isStaking) : pwallet(_pwallet), isStaking(_isStaking) {}
353 0 : UniValue operator()(const libzcash::InvalidEncoding &zaddr) const { return UniValue(UniValue::VOBJ); }
354 :
355 2 : UniValue operator()(const libzcash::SaplingPaymentAddress &zaddr) const {
356 2 : UniValue obj(UniValue::VOBJ);
357 4 : obj.pushKV("diversifier", HexStr(zaddr.d));
358 4 : obj.pushKV("diversifiedtransmissionkey", zaddr.pk_d.GetHex());
359 : #ifdef ENABLE_WALLET
360 2 : if (pwallet) {
361 4 : obj.pushKV("ismine", pwallet->HaveSpendingKeyForPaymentAddress(zaddr));
362 : }
363 : #endif
364 2 : return obj;
365 : }
366 :
367 96 : UniValue operator()(const CTxDestination &dest) const {
368 96 : UniValue ret(UniValue::VOBJ);
369 96 : CScript scriptPubKey = GetScriptForDestination(dest);
370 288 : ret.pushKV("scriptPubKey", HexStr(scriptPubKey));
371 :
372 : #ifdef ENABLE_WALLET
373 96 : isminetype mine = pwallet ? IsMine(*pwallet, dest) : ISMINE_NO;
374 96 : ret.pushKV("ismine", bool(mine & (ISMINE_SPENDABLE_ALL | ISMINE_COLD)));
375 96 : ret.pushKV("isstaking", isStaking);
376 96 : ret.pushKV("iswatchonly", bool(mine & ISMINE_WATCH_ONLY));
377 288 : UniValue detail = boost::apply_visitor(DescribeAddressVisitor(pwallet), dest);
378 96 : ret.pushKVs(detail);
379 191 : if (pwallet && pwallet->HasAddressBook(dest))
380 186 : ret.pushKV("label", pwallet->GetNameForAddressBookEntry(dest));
381 : #endif
382 192 : return ret;
383 : }
384 :
385 : private:
386 : CWallet * const pwallet;
387 : bool isStaking{false};
388 : };
389 :
390 102 : UniValue validateaddress(const JSONRPCRequest& request)
391 : {
392 102 : CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
393 :
394 102 : if (request.fHelp || request.params.size() != 1)
395 2 : throw std::runtime_error(
396 : "validateaddress \"pivxaddress\"\n"
397 : "\nReturn information about the given pivx address.\n"
398 :
399 : "\nArguments:\n"
400 : "1. \"pivxaddress\" (string, required) The pivx address to validate\n"
401 :
402 : "\nResult:\n"
403 : "{\n"
404 : " \"isvalid\" : true|false, (boolean) If the address is valid or not. If not, this is the only property returned.\n"
405 : " \"address\" : \"pivxaddress\", (string) The pivx address validated\n"
406 : " \"scriptPubKey\" : \"hex\", (string) The hex encoded scriptPubKey generated by the address -only if is standard address-\n"
407 : " \"ismine\" : true|false, (boolean) If the address is yours or not\n"
408 : " \"isstaking\" : true|false, (boolean) If the address is a staking address for PIVX cold staking -only if is standard address-\n"
409 : " \"iswatchonly\" : true|false, (boolean) If the address is watchonly -only if standard address-\n"
410 : " \"isscript\" : true|false, (boolean) If the key is a script -only if standard address-\n"
411 : " \"hex\" : \"hex\", (string, optional) The redeemscript for the P2SH address -only if standard address-\n"
412 : " \"pubkey\" : \"publickeyhex\", (string) The hex value of the raw public key -only if standard address-\n"
413 : " \"iscompressed\" : true|false, (boolean) If the address is compressed -only if standard address-\n"
414 : " \"label\" : \"label\" (string) The label associated with the address, \"\" is the default label\n"
415 : // Sapling
416 : " \"diversifier\" : \"hex\", (string) [sapling] The hex value of the diversifier, d -only if is sapling address-\n"
417 : " \"diversifiedtransmissionkey\" : \"hex\", (string) [sapling] The hex value of pk_d -only if is sapling address-\n"
418 : "}\n"
419 :
420 4 : "\nExamples:\n" +
421 10 : HelpExampleCli("validateaddress", "\"DMJRSsuU9zfyrvxVaAEFQqK4MxZg6vgeS6\"") +
422 10 : HelpExampleCli("validateaddress", "\"ps1ra969yfhvhp73rw5ak2xvtcm9fkuqsnmad7qln79mphhdrst3lwu9vvv03yuyqlh42p42st47qd\"") +
423 8 : HelpExampleRpc("validateaddress", "\"DMJRSsuU9zfyrvxVaAEFQqK4MxZg6vgeS6\""));
424 :
425 : #ifdef ENABLE_WALLET
426 200 : LOCK2(cs_main, pwallet ? &pwallet->cs_wallet : nullptr);
427 : #else
428 : LOCK(cs_main);
429 : #endif
430 :
431 200 : std::string strAddress = request.params[0].get_str();
432 :
433 : // First check if it's a regular address
434 100 : bool isStaking = false;
435 100 : bool isExchange = false;
436 200 : CTxDestination dest = DecodeDestination(strAddress, isStaking, isExchange);
437 100 : bool isValid = IsValidDestination(dest);
438 :
439 200 : PPaymentAddress finalAddress;
440 100 : if (!isValid) {
441 4 : isValid = KeyIO::IsValidPaymentAddressString(strAddress);
442 6 : if (isValid) finalAddress = KeyIO::DecodePaymentAddress(strAddress);
443 : } else {
444 96 : finalAddress = dest;
445 : }
446 :
447 100 : UniValue ret(UniValue::VOBJ);
448 100 : ret.pushKV("isvalid", isValid);
449 100 : if (isValid) {
450 98 : ret.pushKV("address", strAddress);
451 196 : UniValue detail = boost::apply_visitor(DescribePaymentAddressVisitor(pwallet, isStaking), finalAddress);
452 98 : ret.pushKVs(detail);
453 : }
454 :
455 200 : return ret;
456 : }
457 :
458 : /**
459 : * Used by addmultisigaddress / createmultisig:
460 : */
461 20 : CScript _createmultisig_redeemScript(CWallet * const pwallet, const UniValue& params)
462 : {
463 20 : int nRequired = params[0].get_int();
464 20 : const UniValue& keys = params[1].get_array();
465 :
466 : // Gather public keys
467 20 : if (nRequired < 1)
468 0 : throw std::runtime_error("a multisignature address must require at least one key to redeem");
469 20 : if ((int)keys.size() < nRequired)
470 0 : throw std::runtime_error(
471 0 : strprintf("not enough keys supplied "
472 : "(got %u keys, but need at least %d to redeem)",
473 0 : keys.size(), nRequired));
474 20 : if (keys.size() > 16)
475 0 : throw std::runtime_error("Number of addresses involved in the multisignature address creation > 16\nReduce the number");
476 20 : std::vector<CPubKey> pubkeys;
477 20 : pubkeys.resize(keys.size());
478 104 : for (unsigned int i = 0; i < keys.size(); i++) {
479 86 : const std::string& ks = keys[i].get_str();
480 : #ifdef ENABLE_WALLET
481 : // Case 1: PIVX address and we have full public key:
482 172 : CTxDestination dest = DecodeDestination(ks);
483 86 : if (pwallet && IsValidDestination(dest)) {
484 62 : const CKeyID* keyID = boost::get<CKeyID>(&dest);
485 62 : if (!keyID) {
486 0 : throw std::runtime_error(
487 0 : strprintf("%s does not refer to a key", ks));
488 : }
489 62 : CPubKey vchPubKey;
490 62 : if (!pwallet->GetPubKey(*keyID, vchPubKey))
491 1 : throw std::runtime_error(
492 2 : strprintf("no full public key for address %s", ks));
493 61 : if (!vchPubKey.IsFullyValid())
494 0 : throw std::runtime_error(" Invalid public key: " + ks);
495 61 : pubkeys[i] = vchPubKey;
496 : }
497 :
498 : // Case 2: hex public key
499 : else
500 : #endif
501 24 : if (IsHex(ks)) {
502 24 : CPubKey vchPubKey(ParseHex(ks));
503 24 : if (!vchPubKey.IsFullyValid())
504 2 : throw std::runtime_error(" Invalid public key: " + ks);
505 23 : pubkeys[i] = vchPubKey;
506 : } else {
507 0 : throw std::runtime_error(" Invalid public key: " + ks);
508 : }
509 : }
510 18 : CScript result = GetScriptForMultisig(nRequired, pubkeys);
511 :
512 36 : if (result.size() > MAX_SCRIPT_ELEMENT_SIZE)
513 0 : throw std::runtime_error(
514 0 : strprintf("redeemScript exceeds size limit: %d > %d", result.size(), MAX_SCRIPT_ELEMENT_SIZE));
515 :
516 36 : return result;
517 : }
518 :
519 8 : UniValue createmultisig(const JSONRPCRequest& request)
520 : {
521 8 : CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
522 :
523 8 : if (request.fHelp || request.params.size() < 2 || request.params.size() > 2)
524 0 : throw std::runtime_error(
525 : "createmultisig nrequired [\"key\",...]\n"
526 : "\nCreates a multi-signature address with n signature of m keys required.\n"
527 : "It returns a json object with the address and redeemScript.\n"
528 :
529 : "\nArguments:\n"
530 : "1. nrequired (numeric, required) The number of required signatures out of the n keys or addresses.\n"
531 : "2. \"keys\" (string, required) A json array of keys which are pivx addresses or hex-encoded public keys\n"
532 : " [\n"
533 : " \"key\" (string) pivx address or hex-encoded public key\n"
534 : " ,...\n"
535 : " ]\n"
536 :
537 : "\nResult:\n"
538 : "{\n"
539 : " \"address\":\"multisigaddress\", (string) The value of the new multisig address.\n"
540 : " \"redeemScript\":\"script\" (string) The string value of the hex-encoded redemption script.\n"
541 : "}\n"
542 :
543 : "\nExamples:\n"
544 0 : "\nCreate a multisig address from 2 addresses\n" +
545 0 : HelpExampleCli("createmultisig", "2 \"[\\\"DMJRSsuU9zfyrvxVaAEFQqK4MxZg6vgeS6\\\",\\\"DAD3Y6ivr8nPQLT1NEPX84DxGCw9jz9Jvg\\\"]\"") +
546 0 : "\nAs a json rpc call\n" +
547 0 : HelpExampleRpc("createmultisig", "2, \"[\\\"DMJRSsuU9zfyrvxVaAEFQqK4MxZg6vgeS6\\\",\\\"DAD3Y6ivr8nPQLT1NEPX84DxGCw9jz9Jvg\\\"]\""));
548 :
549 : // Construct using pay-to-script-hash:
550 8 : CScript inner = _createmultisig_redeemScript(pwallet, request.params);
551 6 : CScriptID innerID(inner);
552 :
553 6 : UniValue result(UniValue::VOBJ);
554 18 : result.pushKV("address", EncodeDestination(innerID));
555 18 : result.pushKV("redeemScript", HexStr(inner));
556 :
557 12 : return result;
558 : }
559 :
560 3 : UniValue verifymessage(const JSONRPCRequest& request)
561 : {
562 3 : if (request.fHelp || request.params.size() != 3)
563 0 : throw std::runtime_error(
564 : "verifymessage \"pivxaddress\" \"signature\" \"message\"\n"
565 : "\nVerify a signed message\n"
566 :
567 : "\nArguments:\n"
568 : "1. \"pivxaddress\" (string, required) The pivx address to use for the signature.\n"
569 : "2. \"signature\" (string, required) The signature provided by the signer in base 64 encoding (see signmessage).\n"
570 : "3. \"message\" (string, required) The message that was signed.\n"
571 :
572 : "\nResult:\n"
573 : "true|false (boolean) If the signature is verified or not.\n"
574 :
575 : "\nExamples:\n"
576 0 : "\nUnlock the wallet for 30 seconds\n" +
577 0 : HelpExampleCli("walletpassphrase", "\"mypassphrase\" 30") +
578 0 : "\nCreate the signature\n" +
579 0 : HelpExampleCli("signmessage", "\"DAD3Y6ivr8nPQLT1NEPX84DxGCw9jz9Jvg\" \"my message\"") +
580 0 : "\nVerify the signature\n" +
581 0 : HelpExampleCli("verifymessage", "\"DAD3Y6ivr8nPQLT1NEPX84DxGCw9jz9Jvg\" \"signature\" \"my message\"") +
582 0 : "\nAs json rpc\n" +
583 0 : HelpExampleRpc("verifymessage", "\"DAD3Y6ivr8nPQLT1NEPX84DxGCw9jz9Jvg\", \"signature\", \"my message\""));
584 :
585 3 : LOCK(cs_main);
586 :
587 6 : std::string strAddress = request.params[0].get_str();
588 6 : std::string strSign = request.params[1].get_str();
589 6 : std::string strMessage = request.params[2].get_str();
590 :
591 6 : CTxDestination destination = DecodeDestination(strAddress);
592 3 : if (!IsValidDestination(destination))
593 0 : throw JSONRPCError(RPC_TYPE_ERROR, "Invalid address");
594 :
595 3 : const CKeyID* keyID = boost::get<CKeyID>(&destination);
596 3 : if (!keyID) {
597 0 : throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to key");
598 : }
599 :
600 3 : bool fInvalid = false;
601 6 : std::vector<unsigned char> vchSig = DecodeBase64(strSign.c_str(), &fInvalid);
602 :
603 3 : if (fInvalid)
604 0 : throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Malformed base64 encoding");
605 :
606 6 : std::string strError;
607 6 : return CMessageSigner::VerifyMessage(*keyID, vchSig, strMessage, strError);
608 : }
609 :
610 10439 : UniValue setmocktime(const JSONRPCRequest& request)
611 : {
612 10439 : if (request.fHelp || request.params.size() != 1)
613 0 : throw std::runtime_error(
614 : "setmocktime timestamp\n"
615 : "\nSet the local time to given timestamp (-regtest only)\n"
616 :
617 : "\nArguments:\n"
618 : "1. timestamp (numeric, required) Unix seconds-since-epoch timestamp\n"
619 0 : " Pass 0 to go back to using the system time.");
620 :
621 10439 : if (!Params().IsRegTestNet())
622 0 : throw std::runtime_error("setmocktime for regression testing (-regtest mode) only");
623 :
624 : // For now, don't change mocktime if we're in the middle of validation, as
625 : // this could have an effect on mempool time-based eviction, as well as
626 : // IsCurrentForFeeEstimation() and IsInitialBlockDownload().
627 : // TODO: figure out the right way to synchronize around mocktime, and
628 : // ensure all callsites of GetTime() are accessing this safely.
629 10439 : LOCK(cs_main);
630 :
631 10439 : RPCTypeCheck(request.params, {UniValue::VNUM});
632 10439 : SetMockTime(request.params[0].get_int64());
633 :
634 20878 : return NullUniValue;
635 : }
636 :
637 0 : void EnableOrDisableLogCategories(UniValue cats, bool enable) {
638 0 : cats = cats.get_array();
639 0 : for (unsigned int i = 0; i < cats.size(); ++i) {
640 0 : std::string cat = cats[i].get_str();
641 :
642 0 : bool success;
643 0 : if (enable) {
644 0 : success = g_logger->EnableCategory(cat);
645 : } else {
646 0 : success = g_logger->DisableCategory(cat);
647 : }
648 :
649 0 : if (!success)
650 0 : throw JSONRPCError(RPC_INVALID_PARAMETER, "unknown logging category " + cat);
651 : }
652 0 : }
653 :
654 0 : UniValue logging(const JSONRPCRequest& request)
655 : {
656 0 : if (request.fHelp || request.params.size() > 2) {
657 0 : throw std::runtime_error(
658 : "logging [include,...] ( [exclude,...] )\n"
659 : "Gets and sets the logging configuration.\n"
660 : "When called without an argument, returns the list of categories that are currently being debug logged.\n"
661 : "When called with arguments, adds or removes categories from debug logging.\n"
662 0 : "The valid logging categories are: " + ListLogCategories() + "\n"
663 : "libevent logging is configured on startup and cannot be modified by this RPC during runtime."
664 :
665 : "Arguments:\n"
666 : "1. \"include\" (array of strings) add debug logging for these categories.\n"
667 : "2. \"exclude\" (array of strings) remove debug logging for these categories.\n"
668 :
669 : "\nResult:\n"
670 : "{ (object): a JSON object of the logging categories that are active.\n"
671 : " \"category\": fEnabled, (key/value) Key is the category name, value is a boolean of it's active state.\n"
672 : " ...,\n"
673 : "}\n"
674 :
675 : "\nExamples:\n"
676 0 : + HelpExampleCli("logging", "\"[\\\"all\\\"]\" \"[\\\"http\\\"]\"")
677 0 : + HelpExampleRpc("logging", "[\"all\"], \"[libevent]\"")
678 0 : );
679 : }
680 :
681 0 : uint32_t original_log_categories = g_logger->GetCategoryMask();
682 0 : if (request.params.size() > 0 && request.params[0].isArray()) {
683 0 : EnableOrDisableLogCategories(request.params[0], true);
684 : }
685 :
686 0 : if (request.params.size() > 1 && request.params[1].isArray()) {
687 0 : EnableOrDisableLogCategories(request.params[1], false);
688 : }
689 0 : uint32_t updated_log_categories = g_logger->GetCategoryMask();
690 0 : uint32_t changed_log_categories = original_log_categories ^ updated_log_categories;
691 :
692 : // Update libevent logging if BCLog::LIBEVENT has changed.
693 : // If the library version doesn't allow it, UpdateHTTPServerLogging() returns false,
694 : // in which case we should clear the BCLog::LIBEVENT flag.
695 : // Throw an error if the user has explicitly asked to change only the libevent
696 : // flag and it failed.
697 0 : if (changed_log_categories & BCLog::LIBEVENT) {
698 0 : if (!UpdateHTTPServerLogging(g_logger->WillLogCategory(BCLog::LIBEVENT))) {
699 0 : g_logger->DisableCategory(BCLog::LIBEVENT);
700 0 : if (changed_log_categories == BCLog::LIBEVENT) {
701 0 : throw JSONRPCError(RPC_INVALID_PARAMETER, "libevent logging cannot be updated when using libevent before v2.1.1.");
702 : }
703 : }
704 : }
705 :
706 0 : UniValue result(UniValue::VOBJ);
707 0 : std::vector<CLogCategoryActive> vLogCatActive = ListActiveLogCategories();
708 0 : for (const auto& logCatActive : vLogCatActive) {
709 0 : result.pushKV(logCatActive.category, logCatActive.active);
710 : }
711 :
712 0 : return result;
713 : }
714 :
715 2 : static UniValue RPCLockedMemoryInfo()
716 : {
717 2 : LockedPool::Stats stats = LockedPoolManager::Instance().stats();
718 2 : UniValue obj(UniValue::VOBJ);
719 2 : obj.pushKV("used", uint64_t(stats.used));
720 2 : obj.pushKV("free", uint64_t(stats.free));
721 2 : obj.pushKV("total", uint64_t(stats.total));
722 2 : obj.pushKV("locked", uint64_t(stats.locked));
723 2 : obj.pushKV("chunks_used", uint64_t(stats.chunks_used));
724 2 : obj.pushKV("chunks_free", uint64_t(stats.chunks_free));
725 2 : return obj;
726 : }
727 :
728 2 : UniValue getmemoryinfo(const JSONRPCRequest& request)
729 : {
730 : /* Please, avoid using the word "pool" here in the RPC interface or help,
731 : * as users will undoubtedly confuse it with the other "memory pool"
732 : */
733 2 : if (request.fHelp || request.params.size() != 0)
734 0 : throw std::runtime_error(
735 : "getmemoryinfo\n"
736 : "Returns an object containing information about memory usage.\n"
737 : "\nResult:\n"
738 : "{\n"
739 : " \"locked\": { (json object) Information about locked memory manager\n"
740 : " \"used\": xxxxx, (numeric) Number of bytes used\n"
741 : " \"free\": xxxxx, (numeric) Number of bytes available in current arenas\n"
742 : " \"total\": xxxxxxx, (numeric) Total number of bytes managed\n"
743 : " \"locked\": xxxxxx, (numeric) Amount of bytes that succeeded locking. If this number is smaller than total, locking pages failed at some point and key data could be swapped to disk.\n"
744 : " \"chunks_used\": xxxxx, (numeric) Number allocated chunks\n"
745 : " \"chunks_free\": xxxxx, (numeric) Number unused chunks\n"
746 : " }\n"
747 : "}\n"
748 : "\nExamples:\n"
749 0 : + HelpExampleCli("getmemoryinfo", "")
750 0 : + HelpExampleRpc("getmemoryinfo", "")
751 0 : );
752 2 : UniValue obj(UniValue::VOBJ);
753 4 : obj.pushKV("locked", RPCLockedMemoryInfo());
754 2 : return obj;
755 : }
756 :
757 5 : UniValue echo(const JSONRPCRequest& request)
758 : {
759 5 : if (request.fHelp)
760 0 : throw std::runtime_error(
761 : "echo|echojson \"message\" ...\n"
762 : "\nSimply echo back the input arguments. This command is for testing.\n"
763 : "\nThe difference between echo and echojson is that echojson has argument conversion enabled in the client-side table in"
764 : "pivx-cli and the GUI. There is no server-side difference."
765 0 : );
766 :
767 5 : return request.params;
768 : }
769 :
770 : // mnconnect command operation types
771 : const char* SINGLE_CONN = "single_conn";
772 : const char* QUORUM_MEMBERS_CONN = "quorum_members_conn";
773 : const char* IQR_MEMBERS_CONN = "iqr_members_conn";
774 : const char* PROBE_CONN = "probe_conn";
775 : const char* CLEAR_CONN = "clear_conn";
776 :
777 : /* What essentially does is add a pending MN connection
778 : * Can be in the following forms:
779 : * 1) Direct single DMN connection.
780 : * 2) Quorum members connection (set of DMNs to connect).
781 : * 3) Quorum relay members connections (set of DMNs to connect and relay intra-quorum messages).
782 : * 4) Probe DMN connection.
783 : * 5) Clear tier two net connections cache
784 : **/
785 13 : UniValue mnconnect(const JSONRPCRequest& request)
786 : {
787 13 : if (request.fHelp || request.params.empty() || request.params.size() > 4) {
788 0 : throw std::runtime_error(
789 : "mnconnect \"op_type\" (\"[pro_tx_hash, pro_tx_hash,..]\" llmq_type \"quorum_hash\")\n"
790 : "\nAdd manual quorum members connections for internal testing purposes of the tier two p2p network layer\n"
791 0 : );
792 : }
793 :
794 13 : const auto& chainparams = Params();
795 13 : if (!chainparams.IsRegTestNet())
796 0 : throw std::runtime_error("mnconnect for regression testing (-regtest mode) only");
797 :
798 : // Connection type
799 13 : RPCTypeCheck(request.params, {UniValue::VSTR});
800 13 : const std::string& op_type = request.params[0].get_str();
801 :
802 : // DMNs pro_tx list
803 26 : std::set<uint256> set_dmn_protxhash;
804 13 : if (request.params.size() > 1) {
805 11 : RPCTypeCheckArgument(request.params[1], UniValue::VARR);
806 11 : const auto& array{request.params[1].get_array()};
807 46 : for (unsigned int i = 0; i < array.size(); i++) {
808 105 : set_dmn_protxhash.emplace(ParseHashV(array[i], strprintf("pro_tx_hash (index %d)", i)));
809 : }
810 : }
811 :
812 13 : Consensus::LLMQType llmq_type = Consensus::LLMQ_NONE;
813 13 : if (request.params.size() > 2) {
814 9 : RPCTypeCheckArgument(request.params[2], UniValue::VNUM);
815 9 : llmq_type = (Consensus::LLMQType)request.params[2].get_int();
816 : }
817 :
818 13 : uint256 quorum_hash;
819 13 : if (request.params.size() > 3) {
820 18 : quorum_hash = ParseHashV(request.params[3], "quorum_hash");
821 : }
822 :
823 13 : const auto& mn_connan = g_connman->GetTierTwoConnMan();
824 13 : if (op_type == SINGLE_CONN) {
825 2 : for (const auto& protxhash : set_dmn_protxhash) {
826 : // if the connection exist or if the dmn doesn't exist,
827 : // it will simply not even try to connect to it.
828 1 : mn_connan->addPendingMasternode(protxhash);
829 : }
830 1 : return true;
831 12 : } else if (op_type == QUORUM_MEMBERS_CONN) {
832 2 : mn_connan->setQuorumNodes(llmq_type, quorum_hash, set_dmn_protxhash);
833 2 : return true;
834 10 : } else if (op_type == IQR_MEMBERS_CONN) {
835 7 : mn_connan->setMasternodeQuorumRelayMembers(llmq_type, quorum_hash, set_dmn_protxhash);
836 7 : return true;
837 3 : } else if (op_type == PROBE_CONN) {
838 1 : mn_connan->addPendingProbeConnections(set_dmn_protxhash);
839 1 : return true;
840 2 : } else if (op_type == CLEAR_CONN) {
841 2 : mn_connan->clear();
842 2 : return true;
843 : }
844 0 : return false;
845 : }
846 :
847 : // clang-format off
848 : static const CRPCCommand commands[] =
849 : { // category name actor (function) okSafe argNames
850 : // --------------------- ------------------------ ----------------------- ------ --------
851 : { "control", "getinfo", &getinfo, true, {} }, /* uses wallet if enabled */
852 : { "control", "getmemoryinfo", &getmemoryinfo, true, {} },
853 : { "control", "mnsync", &mnsync, true, {"mode"} },
854 : { "control", "spork", &spork, true, {"name","value"} },
855 :
856 : { "util", "createmultisig", &createmultisig, true, {"nrequired","keys"} },
857 : { "util", "logging", &logging, true, {"include", "exclude"} },
858 : { "util", "validateaddress", &validateaddress, true, {"pivxaddress"} }, /* uses wallet if enabled */
859 : { "util", "verifymessage", &verifymessage, true, {"pivxaddress","signature","message"} },
860 :
861 : /** Not shown in help */
862 : { "hidden", "echo", &echo, true, {"arg0","arg1","arg2","arg3","arg4","arg5","arg6","arg7","arg8","arg9"} },
863 : { "hidden", "echojson", &echo, true, {"arg0","arg1","arg2","arg3","arg4","arg5","arg6","arg7","arg8","arg9"} },
864 : { "hidden", "setmocktime", &setmocktime, true, {"timestamp"} },
865 : { "hidden", "mnconnect", &mnconnect, true, {"op_type", "mn_list", "llmq_type", "quorum_hash"} },
866 : };
867 : // clang-format on
868 :
869 494 : void RegisterMiscRPCCommands(CRPCTable &tableRPC)
870 : {
871 6422 : for (unsigned int vcidx = 0; vcidx < ARRAYLEN(commands); vcidx++)
872 5928 : tableRPC.appendCommand(commands[vcidx].name, &commands[vcidx]);
873 494 : }
|