]>
Commit | Line | Data |
---|---|---|
69d605f4 | 1 | // Copyright (c) 2010 Satoshi Nakamoto |
f914f1a7 | 2 | // Copyright (c) 2009-2014 The Bitcoin Core developers |
77920402 | 3 | // Distributed under the MIT software license, see the accompanying |
3a25a2b9 | 4 | // file COPYING or http://www.opensource.org/licenses/mit-license.php. |
69d605f4 | 5 | |
fb78cc23 | 6 | #include "rpcserver.h" |
51ed9ec9 BD |
7 | |
8 | #include "base58.h" | |
0eeb4f5d | 9 | #include "init.h" |
4401b2d7 EL |
10 | #include "random.h" |
11 | #include "sync.h" | |
48ba56cd | 12 | #include "ui_interface.h" |
c037531d | 13 | #include "util.h" |
4401b2d7 | 14 | #include "utilstrencodings.h" |
fc72c078 S |
15 | #include "asyncrpcqueue.h" |
16 | ||
17 | #include <memory> | |
51ed9ec9 | 18 | |
afd64f76 WL |
19 | #include <univalue.h> |
20 | ||
914dc012 | 21 | #include <boost/bind.hpp> |
95d888a6 | 22 | #include <boost/filesystem.hpp> |
a0780ba0 | 23 | #include <boost/foreach.hpp> |
69d605f4 WL |
24 | #include <boost/iostreams/concepts.hpp> |
25 | #include <boost/iostreams/stream.hpp> | |
914dc012 | 26 | #include <boost/shared_ptr.hpp> |
4401b2d7 | 27 | #include <boost/signals2/signal.hpp> |
ad49c256 | 28 | #include <boost/thread.hpp> |
afd64f76 | 29 | #include <boost/algorithm/string/case_conv.hpp> // for to_upper() |
5ce4c2a2 | 30 | |
4401b2d7 | 31 | using namespace RPCServer; |
40a158e1 | 32 | using namespace std; |
69d605f4 | 33 | |
ff6a7af1 | 34 | static bool fRPCRunning = false; |
af82884a DK |
35 | static bool fRPCInWarmup = true; |
36 | static std::string rpcWarmupStatus("RPC server started"); | |
37 | static CCriticalSection cs_rpcWarmup; | |
afd64f76 WL |
38 | /* Timer-creating functions */ |
39 | static std::vector<RPCTimerInterface*> timerInterfaces; | |
40 | /* Map of name to timer. | |
41 | * @note Can be changed to std::unique_ptr when C++11 */ | |
42 | static std::map<std::string, boost::shared_ptr<RPCTimerBase> > deadlineTimers; | |
e9205293 | 43 | |
4401b2d7 EL |
44 | static struct CRPCSignals |
45 | { | |
46 | boost::signals2::signal<void ()> Started; | |
47 | boost::signals2::signal<void ()> Stopped; | |
48 | boost::signals2::signal<void (const CRPCCommand&)> PreCommand; | |
49 | boost::signals2::signal<void (const CRPCCommand&)> PostCommand; | |
50 | } g_rpcSignals; | |
51 | ||
52 | void RPCServer::OnStarted(boost::function<void ()> slot) | |
53 | { | |
54 | g_rpcSignals.Started.connect(slot); | |
55 | } | |
56 | ||
57 | void RPCServer::OnStopped(boost::function<void ()> slot) | |
58 | { | |
59 | g_rpcSignals.Stopped.connect(slot); | |
60 | } | |
61 | ||
62 | void RPCServer::OnPreCommand(boost::function<void (const CRPCCommand&)> slot) | |
63 | { | |
64 | g_rpcSignals.PreCommand.connect(boost::bind(slot, _1)); | |
65 | } | |
66 | ||
67 | void RPCServer::OnPostCommand(boost::function<void (const CRPCCommand&)> slot) | |
68 | { | |
69 | g_rpcSignals.PostCommand.connect(boost::bind(slot, _1)); | |
70 | } | |
71 | ||
d014114d JS |
72 | void RPCTypeCheck(const UniValue& params, |
73 | const list<UniValue::VType>& typesExpected, | |
cc6dfd1f | 74 | bool fAllowNull) |
899d373b | 75 | { |
cc71666a | 76 | size_t i = 0; |
d014114d | 77 | BOOST_FOREACH(UniValue::VType t, typesExpected) |
899d373b GA |
78 | { |
79 | if (params.size() <= i) | |
80 | break; | |
81 | ||
d014114d | 82 | const UniValue& v = params[i]; |
ed21d5bd | 83 | if (!((v.type() == t) || (fAllowNull && (v.isNull())))) |
899d373b GA |
84 | { |
85 | string err = strprintf("Expected type %s, got %s", | |
ed21d5bd | 86 | uvTypeName(t), uvTypeName(v.type())); |
738835d7 | 87 | throw JSONRPCError(RPC_TYPE_ERROR, err); |
899d373b GA |
88 | } |
89 | i++; | |
90 | } | |
91 | } | |
92 | ||
ed21d5bd JG |
93 | void RPCTypeCheckObj(const UniValue& o, |
94 | const map<string, UniValue::VType>& typesExpected, | |
cc6dfd1f | 95 | bool fAllowNull) |
899d373b | 96 | { |
d014114d | 97 | BOOST_FOREACH(const PAIRTYPE(string, UniValue::VType)& t, typesExpected) |
899d373b | 98 | { |
d014114d | 99 | const UniValue& v = find_value(o, t.first); |
ed21d5bd | 100 | if (!fAllowNull && v.isNull()) |
7d9d134b | 101 | throw JSONRPCError(RPC_TYPE_ERROR, strprintf("Missing %s", t.first)); |
cc6dfd1f | 102 | |
ed21d5bd | 103 | if (!((v.type() == t.second) || (fAllowNull && (v.isNull())))) |
899d373b GA |
104 | { |
105 | string err = strprintf("Expected type %s for %s, got %s", | |
ed21d5bd | 106 | uvTypeName(t.second), t.first, uvTypeName(v.type())); |
738835d7 | 107 | throw JSONRPCError(RPC_TYPE_ERROR, err); |
899d373b GA |
108 | } |
109 | } | |
110 | } | |
111 | ||
d014114d | 112 | CAmount AmountFromValue(const UniValue& value) |
69d605f4 | 113 | { |
84d1d5fd WL |
114 | if (!value.isNum() && !value.isStr()) |
115 | throw JSONRPCError(RPC_TYPE_ERROR, "Amount is not a number or string"); | |
c66dff3d | 116 | CAmount amount; |
fed500e2 | 117 | if (!ParseFixedPoint(value.getValStr(), 8, &amount)) |
738835d7 | 118 | throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount"); |
c66dff3d WL |
119 | if (!MoneyRange(amount)) |
120 | throw JSONRPCError(RPC_TYPE_ERROR, "Amount out of range"); | |
121 | return amount; | |
69d605f4 WL |
122 | } |
123 | ||
851f58f9 | 124 | UniValue ValueFromAmount(const CAmount& amount) |
69d605f4 | 125 | { |
d5bf1afa WL |
126 | bool sign = amount < 0; |
127 | int64_t n_abs = (sign ? -amount : amount); | |
128 | int64_t quotient = n_abs / COIN; | |
129 | int64_t remainder = n_abs % COIN; | |
130 | return UniValue(UniValue::VNUM, | |
131 | strprintf("%s%d.%08d", sign ? "-" : "", quotient, remainder)); | |
69d605f4 WL |
132 | } |
133 | ||
d014114d | 134 | uint256 ParseHashV(const UniValue& v, string strName) |
463c9710 PT |
135 | { |
136 | string strHex; | |
ed21d5bd | 137 | if (v.isStr()) |
463c9710 PT |
138 | strHex = v.get_str(); |
139 | if (!IsHex(strHex)) // Note: IsHex("") is false | |
140 | throw JSONRPCError(RPC_INVALID_PARAMETER, strName+" must be hexadecimal string (not '"+strHex+"')"); | |
141 | uint256 result; | |
142 | result.SetHex(strHex); | |
143 | return result; | |
144 | } | |
d014114d | 145 | uint256 ParseHashO(const UniValue& o, string strKey) |
463c9710 PT |
146 | { |
147 | return ParseHashV(find_value(o, strKey), strKey); | |
148 | } | |
d014114d | 149 | vector<unsigned char> ParseHexV(const UniValue& v, string strName) |
463c9710 PT |
150 | { |
151 | string strHex; | |
ed21d5bd | 152 | if (v.isStr()) |
463c9710 PT |
153 | strHex = v.get_str(); |
154 | if (!IsHex(strHex)) | |
155 | throw JSONRPCError(RPC_INVALID_PARAMETER, strName+" must be hexadecimal string (not '"+strHex+"')"); | |
156 | return ParseHex(strHex); | |
157 | } | |
d014114d | 158 | vector<unsigned char> ParseHexO(const UniValue& o, string strKey) |
463c9710 PT |
159 | { |
160 | return ParseHexV(find_value(o, strKey), strKey); | |
161 | } | |
69d605f4 | 162 | |
77920402 MF |
163 | /** |
164 | * Note: This interface may still be subject to change. | |
165 | */ | |
69d605f4 | 166 | |
db954a65 | 167 | std::string CRPCTable::help(const std::string& strCommand) const |
69d605f4 | 168 | { |
69d605f4 | 169 | string strRet; |
6b5b7cbf | 170 | string category; |
69d605f4 | 171 | set<rpcfn_type> setDone; |
6b5b7cbf CL |
172 | vector<pair<string, const CRPCCommand*> > vCommands; |
173 | ||
9862229d | 174 | for (map<string, const CRPCCommand*>::const_iterator mi = mapCommands.begin(); mi != mapCommands.end(); ++mi) |
6b5b7cbf CL |
175 | vCommands.push_back(make_pair(mi->second->category + mi->first, mi->second)); |
176 | sort(vCommands.begin(), vCommands.end()); | |
177 | ||
178 | BOOST_FOREACH(const PAIRTYPE(string, const CRPCCommand*)& command, vCommands) | |
69d605f4 | 179 | { |
6b5b7cbf CL |
180 | const CRPCCommand *pcmd = command.second; |
181 | string strMethod = pcmd->name; | |
69d605f4 | 182 | // We already filter duplicates, but these deprecated screw up the sort order |
47f48a65 | 183 | if (strMethod.find("label") != string::npos) |
69d605f4 | 184 | continue; |
bd9aebf1 | 185 | if ((strCommand != "" || pcmd->category == "hidden") && strMethod != strCommand) |
69d605f4 WL |
186 | continue; |
187 | try | |
188 | { | |
851f58f9 | 189 | UniValue params; |
dc42bf52 | 190 | rpcfn_type pfn = pcmd->actor; |
69d605f4 WL |
191 | if (setDone.insert(pfn).second) |
192 | (*pfn)(params, true); | |
193 | } | |
27df4123 | 194 | catch (const std::exception& e) |
69d605f4 WL |
195 | { |
196 | // Help text is returned in an exception | |
197 | string strHelp = string(e.what()); | |
198 | if (strCommand == "") | |
6b5b7cbf | 199 | { |
ab9dc75a | 200 | if (strHelp.find('\n') != string::npos) |
69d605f4 | 201 | strHelp = strHelp.substr(0, strHelp.find('\n')); |
6b5b7cbf CL |
202 | |
203 | if (category != pcmd->category) | |
204 | { | |
205 | if (!category.empty()) | |
206 | strRet += "\n"; | |
207 | category = pcmd->category; | |
208 | string firstLetter = category.substr(0,1); | |
209 | boost::to_upper(firstLetter); | |
210 | strRet += "== " + firstLetter + category.substr(1) + " ==\n"; | |
211 | } | |
212 | } | |
69d605f4 WL |
213 | strRet += strHelp + "\n"; |
214 | } | |
215 | } | |
216 | if (strRet == "") | |
7d9d134b | 217 | strRet = strprintf("help: unknown command: %s\n", strCommand); |
69d605f4 WL |
218 | strRet = strRet.substr(0,strRet.size()-1); |
219 | return strRet; | |
220 | } | |
221 | ||
d014114d | 222 | UniValue help(const UniValue& params, bool fHelp) |
9862229d PW |
223 | { |
224 | if (fHelp || params.size() > 1) | |
225 | throw runtime_error( | |
a6099ef3 | 226 | "help ( \"command\" )\n" |
227 | "\nList all commands, or get help for a specified command.\n" | |
228 | "\nArguments:\n" | |
229 | "1. \"command\" (string, optional) The command to get help on\n" | |
230 | "\nResult:\n" | |
231 | "\"text\" (string) The help text\n" | |
232 | ); | |
9862229d PW |
233 | |
234 | string strCommand; | |
235 | if (params.size() > 0) | |
236 | strCommand = params[0].get_str(); | |
237 | ||
238 | return tableRPC.help(strCommand); | |
239 | } | |
240 | ||
9edf27ec | 241 | extern char ASSETCHAINS_SYMBOL[KOMODO_ASSETCHAIN_MAXLEN]; |
69d605f4 | 242 | |
d014114d | 243 | UniValue stop(const UniValue& params, bool fHelp) |
69d605f4 | 244 | { |
ebc4965c | 245 | char buf[64]; |
ce906ce7 | 246 | // Accept the deprecated and ignored 'detach' boolean argument |
3731f578 | 247 | if (fHelp || params.size() > 1) |
69d605f4 | 248 | throw runtime_error( |
92467073 | 249 | "stop\n" |
e80de3b2 | 250 | "\nStop Komodo server."); |
69d605f4 | 251 | // Shutdown will take long enough that the response should get back |
9247134e | 252 | StartShutdown(); |
ebc4965c | 253 | sprintf(buf,"%s Komodo server stopping",ASSETCHAINS_SYMBOL); |
254 | return buf; | |
69d605f4 WL |
255 | } |
256 | ||
77920402 MF |
257 | /** |
258 | * Call Table | |
259 | */ | |
e46704dd | 260 | static const CRPCCommand vRPCCommands[] = |
b9fb692d JS |
261 | { // category name actor (function) okSafeMode |
262 | // --------------------- ------------------------ ----------------------- ---------- | |
ab88ed93 | 263 | /* Overall control/query calls */ |
b9fb692d JS |
264 | { "control", "getinfo", &getinfo, true }, /* uses wallet if enabled */ |
265 | { "control", "help", &help, true }, | |
266 | { "control", "stop", &stop, true }, | |
ab88ed93 WL |
267 | |
268 | /* P2P networking */ | |
b9fb692d | 269 | { "network", "getnetworkinfo", &getnetworkinfo, true }, |
9d2974ed | 270 | { "network", "getdeprecationinfo", &getdeprecationinfo, true }, |
b9fb692d | 271 | { "network", "addnode", &addnode, true }, |
94ee48c4 | 272 | { "network", "disconnectnode", &disconnectnode, true }, |
b9fb692d JS |
273 | { "network", "getaddednodeinfo", &getaddednodeinfo, true }, |
274 | { "network", "getconnectioncount", &getconnectioncount, true }, | |
275 | { "network", "getnettotals", &getnettotals, true }, | |
276 | { "network", "getpeerinfo", &getpeerinfo, true }, | |
277 | { "network", "ping", &ping, true }, | |
ed3f13a0 JS |
278 | { "network", "setban", &setban, true }, |
279 | { "network", "listbanned", &listbanned, true }, | |
280 | { "network", "clearbanned", &clearbanned, true }, | |
ab88ed93 WL |
281 | |
282 | /* Block chain and UTXO */ | |
26204e59 | 283 | { "blockchain", "coinsupply", &coinsupply, true }, |
b9fb692d JS |
284 | { "blockchain", "getblockchaininfo", &getblockchaininfo, true }, |
285 | { "blockchain", "getbestblockhash", &getbestblockhash, true }, | |
286 | { "blockchain", "getblockcount", &getblockcount, true }, | |
287 | { "blockchain", "getblock", &getblock, true }, | |
8b78a819 T |
288 | { "blockchain", "getblockdeltas", &getblockdeltas, false }, |
289 | { "blockchain", "getblockhashes", &getblockhashes, true }, | |
b9fb692d | 290 | { "blockchain", "getblockhash", &getblockhash, true }, |
d3d5483e | 291 | { "blockchain", "getblockheader", &getblockheader, true }, |
b9fb692d JS |
292 | { "blockchain", "getchaintips", &getchaintips, true }, |
293 | { "blockchain", "getdifficulty", &getdifficulty, true }, | |
294 | { "blockchain", "getmempoolinfo", &getmempoolinfo, true }, | |
295 | { "blockchain", "getrawmempool", &getrawmempool, true }, | |
296 | { "blockchain", "gettxout", &gettxout, true }, | |
59ed61b3 MC |
297 | { "blockchain", "gettxoutproof", &gettxoutproof, true }, |
298 | { "blockchain", "verifytxoutproof", &verifytxoutproof, true }, | |
b9fb692d JS |
299 | { "blockchain", "gettxoutsetinfo", &gettxoutsetinfo, true }, |
300 | { "blockchain", "verifychain", &verifychain, true }, | |
8b78a819 | 301 | { "blockchain", "getspentinfo", &getspentinfo, false }, |
de5678d8 | 302 | { "blockchain", "paxprice", &paxprice, true }, |
9bd3a9cd | 303 | { "blockchain", "paxpending", &paxpending, true }, |
83a7f513 | 304 | { "blockchain", "paxprices", &paxprices, true }, |
336ab141 | 305 | { "blockchain", "notaries", ¬aries, true }, |
dbaf1154 | 306 | { "blockchain", "minerids", &minerids, true }, |
d20fb2db | 307 | { "blockchain", "kvsearch", &kvsearch, true }, |
ab6e81ec | 308 | { "blockchain", "kvupdate", &kvupdate, true }, |
48ba56cd | 309 | |
e4f943d8 SS |
310 | /* Cross chain utilities */ |
311 | { "crosschain", "MoMoMdata", &MoMoMdata, true }, | |
312 | { "crosschain", "calc_MoM", &calc_MoM, true }, | |
313 | { "crosschain", "height_MoM", &height_MoM, true }, | |
314 | { "crosschain", "assetchainproof", &assetchainproof, true }, | |
315 | { "crosschain", "crosschainproof", &crosschainproof, true }, | |
0b485d3c SS |
316 | { "crosschain", "migrate_converttoexport", &migrate_converttoexport, true }, |
317 | { "crosschain", "migrate_createimporttransaction", &migrate_createimporttransaction, true }, | |
318 | { "crosschain", "migrate_completeimporttransaction", &migrate_completeimporttransaction, true }, | |
e4f943d8 | 319 | |
4a85e067 | 320 | /* Mining */ |
b9fb692d JS |
321 | { "mining", "getblocktemplate", &getblocktemplate, true }, |
322 | { "mining", "getmininginfo", &getmininginfo, true }, | |
000499ae JG |
323 | { "mining", "getlocalsolps", &getlocalsolps, true }, |
324 | { "mining", "getnetworksolps", &getnetworksolps, true }, | |
b9fb692d JS |
325 | { "mining", "getnetworkhashps", &getnetworkhashps, true }, |
326 | { "mining", "prioritisetransaction", &prioritisetransaction, true }, | |
327 | { "mining", "submitblock", &submitblock, true }, | |
1b114e54 | 328 | { "mining", "getblocksubsidy", &getblocksubsidy, true }, |
6b5b7cbf | 329 | |
8e8b6d70 | 330 | #ifdef ENABLE_MINING |
6b5b7cbf | 331 | /* Coin generation */ |
b9fb692d JS |
332 | { "generating", "getgenerate", &getgenerate, true }, |
333 | { "generating", "setgenerate", &setgenerate, true }, | |
334 | { "generating", "generate", &generate, true }, | |
6b5b7cbf | 335 | #endif |
ab88ed93 WL |
336 | |
337 | /* Raw transactions */ | |
b9fb692d JS |
338 | { "rawtransactions", "createrawtransaction", &createrawtransaction, true }, |
339 | { "rawtransactions", "decoderawtransaction", &decoderawtransaction, true }, | |
340 | { "rawtransactions", "decodescript", &decodescript, true }, | |
341 | { "rawtransactions", "getrawtransaction", &getrawtransaction, true }, | |
342 | { "rawtransactions", "sendrawtransaction", &sendrawtransaction, false }, | |
343 | { "rawtransactions", "signrawtransaction", &signrawtransaction, false }, /* uses wallet if enabled */ | |
3d8013a0 MC |
344 | #ifdef ENABLE_WALLET |
345 | { "rawtransactions", "fundrawtransaction", &fundrawtransaction, false }, | |
346 | #endif | |
8ee5c997 | 347 | /* auction */ |
348 | { "auction", "auctionaddress", &auctionaddress, true }, | |
349 | ||
350 | /* lotto */ | |
351 | { "lotto", "lottoaddress", &lottoaddress, true }, | |
352 | ||
353 | /* ponzi */ | |
354 | { "ponzi", "ponziaddress", &ponziaddress, true }, | |
355 | ||
e37d99ce | 356 | /* rewards */ |
fdd22810 | 357 | { "rewards", "rewardslist", &rewardslist, true }, |
358 | { "rewards", "rewardsinfo", &rewardsinfo, true }, | |
c4e7f616 | 359 | { "rewards", "rewardscreatefunding", &rewardscreatefunding, true }, |
360 | { "rewards", "rewardsaddfunding", &rewardsaddfunding, true }, | |
e37d99ce | 361 | { "rewards", "rewardslock", &rewardslock, true }, |
362 | { "rewards", "rewardsunlock", &rewardsunlock, true }, | |
8a8ff823 | 363 | { "rewards", "rewardsaddress", &rewardsaddress, true }, |
e37d99ce | 364 | |
65a961ff | 365 | /* faucet */ |
8a8ff823 | 366 | { "faucet", "faucetfund", &faucetfund, true }, |
367 | { "faucet", "faucetget", &faucetget, true }, | |
6ff08712 | 368 | { "faucet", "faucetaddress", &faucetaddress, true }, |
cfea7a46 | 369 | |
370 | /* dice */ | |
c857567a | 371 | { "dice", "dicelist", &dicelist, true }, |
372 | { "dice", "diceinfo", &diceinfo, true }, | |
cfea7a46 | 373 | { "dice", "dicefund", &dicefund, true }, |
587e715d | 374 | { "dice", "diceaddfunds", &diceaddfunds, true }, |
cfea7a46 | 375 | { "dice", "dicebet", &dicebet, true }, |
376 | { "dice", "diceaddress", &diceaddress, true }, | |
ab88ed93 | 377 | |
cfea7a46 | 378 | /* tokens */ |
c66eb36d | 379 | { "tokens", "tokeninfo", &tokeninfo, true }, |
380 | { "tokens", "tokenlist", &tokenlist, true }, | |
143488c8 | 381 | { "tokens", "tokenorders", &tokenorders, true }, |
382 | { "tokens", "tokenaddress", &tokenaddress, true }, | |
383 | { "tokens", "tokenbalance", &tokenbalance, true }, | |
384 | { "tokens", "tokencreate", &tokencreate, true }, | |
385 | { "tokens", "tokentransfer", &tokentransfer, true }, | |
386 | { "tokens", "tokenbid", &tokenbid, true }, | |
387 | { "tokens", "tokencancelbid", &tokencancelbid, true }, | |
b40d83c9 | 388 | { "tokens", "tokenfillbid", &tokenfillbid, true }, |
143488c8 | 389 | { "tokens", "tokenask", &tokenask, true }, |
390 | { "tokens", "tokenswapask", &tokenswapask, true }, | |
85bdc758 | 391 | { "tokens", "tokencancelask", &tokencancelask, true }, |
143488c8 | 392 | { "tokens", "tokenfillask", &tokenfillask, true }, |
85bdc758 | 393 | { "tokens", "tokenfillswap", &tokenfillswap, true }, |
b40d83c9 | 394 | |
8b78a819 T |
395 | /* Address index */ |
396 | { "addressindex", "getaddressmempool", &getaddressmempool, true }, | |
397 | { "addressindex", "getaddressutxos", &getaddressutxos, false }, | |
398 | { "addressindex", "getaddressdeltas", &getaddressdeltas, false }, | |
399 | { "addressindex", "getaddresstxids", &getaddresstxids, false }, | |
400 | { "addressindex", "getaddressbalance", &getaddressbalance, false }, | |
22007819 | 401 | { "addressindex", "getsnapshot", &getsnapshot, false }, |
8b78a819 | 402 | |
ab88ed93 | 403 | /* Utility functions */ |
b9fb692d JS |
404 | { "util", "createmultisig", &createmultisig, true }, |
405 | { "util", "validateaddress", &validateaddress, true }, /* uses wallet if enabled */ | |
406 | { "util", "verifymessage", &verifymessage, true }, | |
407 | { "util", "estimatefee", &estimatefee, true }, | |
408 | { "util", "estimatepriority", &estimatepriority, true }, | |
4e16a724 | 409 | { "util", "z_validateaddress", &z_validateaddress, true }, /* uses wallet if enabled */ |
5539178c | 410 | { "util", "jumblr_deposit", &jumblr_deposit, true }, |
411 | { "util", "jumblr_secret", &jumblr_secret, true }, | |
eea03b7b | 412 | { "util", "jumblr_pause", &jumblr_pause, true }, |
413 | { "util", "jumblr_resume", &jumblr_resume, true }, | |
4a85e067 | 414 | |
e3906925 | 415 | { "util", "invalidateblock", &invalidateblock, true }, |
416 | { "util", "reconsiderblock", &reconsiderblock, true }, | |
bd9aebf1 | 417 | /* Not shown in help */ |
b9fb692d | 418 | { "hidden", "setmocktime", &setmocktime, true }, |
0f5954c4 | 419 | #ifdef ENABLE_WALLET |
e3906925 | 420 | { "wallet", "resendwallettransactions", &resendwallettransactions, true}, |
0f5954c4 | 421 | #endif |
bd9aebf1 | 422 | |
4a85e067 WL |
423 | #ifdef ENABLE_WALLET |
424 | /* Wallet */ | |
b9fb692d JS |
425 | { "wallet", "addmultisigaddress", &addmultisigaddress, true }, |
426 | { "wallet", "backupwallet", &backupwallet, true }, | |
427 | { "wallet", "dumpprivkey", &dumpprivkey, true }, | |
428 | { "wallet", "dumpwallet", &dumpwallet, true }, | |
429 | { "wallet", "encryptwallet", &encryptwallet, true }, | |
430 | { "wallet", "getaccountaddress", &getaccountaddress, true }, | |
431 | { "wallet", "getaccount", &getaccount, true }, | |
432 | { "wallet", "getaddressesbyaccount", &getaddressesbyaccount, true }, | |
433 | { "wallet", "getbalance", &getbalance, false }, | |
4f02fc40 | 434 | { "wallet", "getbalance64", &getbalance64, false }, |
b9fb692d | 435 | { "wallet", "getnewaddress", &getnewaddress, true }, |
2732d384 | 436 | // { "wallet", "getnewaddress64", &getnewaddress64, true }, |
b9fb692d JS |
437 | { "wallet", "getrawchangeaddress", &getrawchangeaddress, true }, |
438 | { "wallet", "getreceivedbyaccount", &getreceivedbyaccount, false }, | |
439 | { "wallet", "getreceivedbyaddress", &getreceivedbyaddress, false }, | |
440 | { "wallet", "gettransaction", &gettransaction, false }, | |
441 | { "wallet", "getunconfirmedbalance", &getunconfirmedbalance, false }, | |
442 | { "wallet", "getwalletinfo", &getwalletinfo, false }, | |
443 | { "wallet", "importprivkey", &importprivkey, true }, | |
444 | { "wallet", "importwallet", &importwallet, true }, | |
445 | { "wallet", "importaddress", &importaddress, true }, | |
446 | { "wallet", "keypoolrefill", &keypoolrefill, true }, | |
447 | { "wallet", "listaccounts", &listaccounts, false }, | |
448 | { "wallet", "listaddressgroupings", &listaddressgroupings, false }, | |
449 | { "wallet", "listlockunspent", &listlockunspent, false }, | |
450 | { "wallet", "listreceivedbyaccount", &listreceivedbyaccount, false }, | |
451 | { "wallet", "listreceivedbyaddress", &listreceivedbyaddress, false }, | |
452 | { "wallet", "listsinceblock", &listsinceblock, false }, | |
453 | { "wallet", "listtransactions", &listtransactions, false }, | |
454 | { "wallet", "listunspent", &listunspent, false }, | |
455 | { "wallet", "lockunspent", &lockunspent, true }, | |
456 | { "wallet", "move", &movecmd, false }, | |
457 | { "wallet", "sendfrom", &sendfrom, false }, | |
458 | { "wallet", "sendmany", &sendmany, false }, | |
459 | { "wallet", "sendtoaddress", &sendtoaddress, false }, | |
460 | { "wallet", "setaccount", &setaccount, true }, | |
461 | { "wallet", "settxfee", &settxfee, true }, | |
462 | { "wallet", "signmessage", &signmessage, true }, | |
463 | { "wallet", "walletlock", &walletlock, true }, | |
464 | { "wallet", "walletpassphrasechange", &walletpassphrasechange, true }, | |
465 | { "wallet", "walletpassphrase", &walletpassphrase, true }, | |
6962bb3d | 466 | { "wallet", "zcbenchmark", &zc_benchmark, true }, |
730790f7 | 467 | { "wallet", "zcrawkeygen", &zc_raw_keygen, true }, |
b7e4abd6 | 468 | { "wallet", "zcrawjoinsplit", &zc_raw_joinsplit, true }, |
1737627c | 469 | { "wallet", "zcrawreceive", &zc_raw_receive, true }, |
c1c45943 | 470 | { "wallet", "zcsamplejoinsplit", &zc_sample_joinsplit, true }, |
6c41028f | 471 | { "wallet", "z_listreceivedbyaddress",&z_listreceivedbyaddress,false }, |
a0a3334c S |
472 | { "wallet", "z_getbalance", &z_getbalance, false }, |
473 | { "wallet", "z_gettotalbalance", &z_gettotalbalance, false }, | |
6e9c7629 | 474 | { "wallet", "z_mergetoaddress", &z_mergetoaddress, false }, |
6d2d045c | 475 | { "wallet", "z_sendmany", &z_sendmany, false }, |
06c19063 | 476 | { "wallet", "z_shieldcoinbase", &z_shieldcoinbase, false }, |
fc72c078 | 477 | { "wallet", "z_getoperationstatus", &z_getoperationstatus, true }, |
c1eae280 | 478 | { "wallet", "z_getoperationresult", &z_getoperationresult, true }, |
34f0001c | 479 | { "wallet", "z_listoperationids", &z_listoperationids, true }, |
c1c45943 | 480 | { "wallet", "z_getnewaddress", &z_getnewaddress, true }, |
e709997f | 481 | { "wallet", "z_listaddresses", &z_listaddresses, true }, |
c1c45943 | 482 | { "wallet", "z_exportkey", &z_exportkey, true }, |
92444edc | 483 | { "wallet", "z_importkey", &z_importkey, true }, |
e85b33a5 JG |
484 | { "wallet", "z_exportviewingkey", &z_exportviewingkey, true }, |
485 | { "wallet", "z_importviewingkey", &z_importviewingkey, true }, | |
92444edc | 486 | { "wallet", "z_exportwallet", &z_exportwallet, true }, |
d7d27bb3 | 487 | { "wallet", "z_importwallet", &z_importwallet, true }, |
45232b19 S |
488 | |
489 | // TODO: rearrange into another category | |
490 | { "disclosure", "z_getpaymentdisclosure", &z_getpaymentdisclosure, true }, | |
491 | { "disclosure", "z_validatepaymentdisclosure", &z_validatepaymentdisclosure, true } | |
48ba56cd | 492 | #endif // ENABLE_WALLET |
69d605f4 | 493 | }; |
69d605f4 | 494 | |
9862229d | 495 | CRPCTable::CRPCTable() |
dc42bf52 | 496 | { |
dc42bf52 JG |
497 | unsigned int vcidx; |
498 | for (vcidx = 0; vcidx < (sizeof(vRPCCommands) / sizeof(vRPCCommands[0])); vcidx++) | |
499 | { | |
e46704dd | 500 | const CRPCCommand *pcmd; |
69d605f4 | 501 | |
dc42bf52 JG |
502 | pcmd = &vRPCCommands[vcidx]; |
503 | mapCommands[pcmd->name] = pcmd; | |
504 | } | |
505 | } | |
69d605f4 | 506 | |
afd64f76 | 507 | const CRPCCommand *CRPCTable::operator[](const std::string &name) const |
9862229d PW |
508 | { |
509 | map<string, const CRPCCommand*>::const_iterator it = mapCommands.find(name); | |
510 | if (it == mapCommands.end()) | |
511 | return NULL; | |
512 | return (*it).second; | |
513 | } | |
69d605f4 | 514 | |
afd64f76 | 515 | bool StartRPC() |
deb3572a | 516 | { |
afd64f76 | 517 | LogPrint("rpc", "Starting RPC\n"); |
ff6a7af1 | 518 | fRPCRunning = true; |
4401b2d7 | 519 | g_rpcSignals.Started(); |
fc72c078 | 520 | |
008fccfa | 521 | // Launch one async rpc worker. The ability to launch multiple workers is not recommended at present and thus the option is disabled. |
f86f625d | 522 | getAsyncRPCQueue()->addWorker(); |
06c19063 | 523 | /* |
8d08172d S |
524 | int n = GetArg("-rpcasyncthreads", 1); |
525 | if (n<1) { | |
526 | LogPrintf("ERROR: Invalid value %d for -rpcasyncthreads. Must be at least 1.\n", n); | |
527 | strerr = strprintf(_("An error occurred while setting up the Async RPC threads, invalid parameter value of %d (must be at least 1)."), n); | |
528 | uiInterface.ThreadSafeMessageBox(strerr, "", CClientUIInterface::MSG_ERROR); | |
529 | StartShutdown(); | |
530 | return; | |
531 | } | |
532 | for (int i = 0; i < n; i++) | |
f86f625d | 533 | getAsyncRPCQueue()->addWorker(); |
008fccfa | 534 | */ |
afd64f76 | 535 | return true; |
21eb5ada GA |
536 | } |
537 | ||
afd64f76 | 538 | void InterruptRPC() |
a8db31c8 | 539 | { |
afd64f76 WL |
540 | LogPrint("rpc", "Interrupting RPC\n"); |
541 | // Interrupt e.g. running longpolls | |
542 | fRPCRunning = false; | |
a8db31c8 WL |
543 | } |
544 | ||
afd64f76 | 545 | void StopRPC() |
21eb5ada | 546 | { |
afd64f76 | 547 | LogPrint("rpc", "Stopping RPC\n"); |
92f2c1fe | 548 | deadlineTimers.clear(); |
4401b2d7 | 549 | g_rpcSignals.Stopped(); |
fc72c078 S |
550 | |
551 | // Tells async queue to cancel all operations and shutdown. | |
3b54bf58 | 552 | LogPrintf("%s: waiting for async rpc workers to stop\n", __func__); |
f86f625d | 553 | getAsyncRPCQueue()->closeAndWait(); |
e9205293 DJS |
554 | } |
555 | ||
ff6a7af1 LD |
556 | bool IsRPCRunning() |
557 | { | |
558 | return fRPCRunning; | |
559 | } | |
560 | ||
af82884a DK |
561 | void SetRPCWarmupStatus(const std::string& newStatus) |
562 | { | |
563 | LOCK(cs_rpcWarmup); | |
564 | rpcWarmupStatus = newStatus; | |
565 | } | |
566 | ||
567 | void SetRPCWarmupFinished() | |
568 | { | |
569 | LOCK(cs_rpcWarmup); | |
570 | assert(fRPCInWarmup); | |
571 | fRPCInWarmup = false; | |
572 | } | |
573 | ||
78bdc810 JS |
574 | bool RPCIsInWarmup(std::string *outStatus) |
575 | { | |
576 | LOCK(cs_rpcWarmup); | |
577 | if (outStatus) | |
578 | *outStatus = rpcWarmupStatus; | |
579 | return fRPCInWarmup; | |
580 | } | |
581 | ||
d014114d | 582 | void JSONRequest::parse(const UniValue& valRequest) |
c6494d82 JG |
583 | { |
584 | // Parse request | |
ed21d5bd | 585 | if (!valRequest.isObject()) |
738835d7 | 586 | throw JSONRPCError(RPC_INVALID_REQUEST, "Invalid Request object"); |
d014114d | 587 | const UniValue& request = valRequest.get_obj(); |
c6494d82 JG |
588 | |
589 | // Parse id now so errors from here on will have the id | |
590 | id = find_value(request, "id"); | |
591 | ||
592 | // Parse method | |
851f58f9 | 593 | UniValue valMethod = find_value(request, "method"); |
ed21d5bd | 594 | if (valMethod.isNull()) |
738835d7 | 595 | throw JSONRPCError(RPC_INVALID_REQUEST, "Missing method"); |
ed21d5bd | 596 | if (!valMethod.isStr()) |
738835d7 | 597 | throw JSONRPCError(RPC_INVALID_REQUEST, "Method must be a string"); |
c6494d82 | 598 | strMethod = valMethod.get_str(); |
cf0c47b2 | 599 | if (strMethod != "getblocktemplate") |
28d4cff0 | 600 | LogPrint("rpc", "ThreadRPCServer method=%s\n", SanitizeString(strMethod)); |
c6494d82 JG |
601 | |
602 | // Parse params | |
851f58f9 | 603 | UniValue valParams = find_value(request, "params"); |
ed21d5bd | 604 | if (valParams.isArray()) |
c6494d82 | 605 | params = valParams.get_array(); |
ed21d5bd | 606 | else if (valParams.isNull()) |
d014114d | 607 | params = UniValue(UniValue::VARR); |
c6494d82 | 608 | else |
738835d7 | 609 | throw JSONRPCError(RPC_INVALID_REQUEST, "Params must be an array"); |
c6494d82 JG |
610 | } |
611 | ||
d014114d | 612 | static UniValue JSONRPCExecOne(const UniValue& req) |
61338901 | 613 | { |
38fc4b70 | 614 | UniValue rpc_result(UniValue::VOBJ); |
61338901 JG |
615 | |
616 | JSONRequest jreq; | |
617 | try { | |
618 | jreq.parse(req); | |
619 | ||
851f58f9 | 620 | UniValue result = tableRPC.execute(jreq.strMethod, jreq.params); |
ed21d5bd | 621 | rpc_result = JSONRPCReplyObj(result, NullUniValue, jreq.id); |
61338901 | 622 | } |
d014114d | 623 | catch (const UniValue& objError) |
61338901 | 624 | { |
ed21d5bd | 625 | rpc_result = JSONRPCReplyObj(NullUniValue, objError, jreq.id); |
61338901 | 626 | } |
27df4123 | 627 | catch (const std::exception& e) |
61338901 | 628 | { |
ed21d5bd | 629 | rpc_result = JSONRPCReplyObj(NullUniValue, |
738835d7 | 630 | JSONRPCError(RPC_PARSE_ERROR, e.what()), jreq.id); |
61338901 JG |
631 | } |
632 | ||
633 | return rpc_result; | |
634 | } | |
635 | ||
afd64f76 | 636 | std::string JSONRPCExecBatch(const UniValue& vReq) |
61338901 | 637 | { |
bf3f5602 | 638 | UniValue ret(UniValue::VARR); |
cc71666a | 639 | for (size_t reqIdx = 0; reqIdx < vReq.size(); reqIdx++) |
61338901 JG |
640 | ret.push_back(JSONRPCExecOne(vReq[reqIdx])); |
641 | ||
ed21d5bd | 642 | return ret.write() + "\n"; |
61338901 JG |
643 | } |
644 | ||
851f58f9 | 645 | UniValue CRPCTable::execute(const std::string &strMethod, const UniValue ¶ms) const |
854d0130 | 646 | { |
483672f7 | 647 | // Return immediately if in warmup |
854d0130 | 648 | { |
483672f7 FV |
649 | LOCK(cs_rpcWarmup); |
650 | if (fRPCInWarmup) | |
651 | throw JSONRPCError(RPC_IN_WARMUP, rpcWarmupStatus); | |
854d0130 JG |
652 | } |
653 | ||
460c51fd WL |
654 | // Find method |
655 | const CRPCCommand *pcmd = tableRPC[strMethod]; | |
656 | if (!pcmd) | |
738835d7 | 657 | throw JSONRPCError(RPC_METHOD_NOT_FOUND, "Method not found"); |
69d605f4 | 658 | |
4401b2d7 | 659 | g_rpcSignals.PreCommand(*pcmd); |
460c51fd WL |
660 | |
661 | try | |
662 | { | |
663 | // Execute | |
4401b2d7 | 664 | return pcmd->actor(params, false); |
460c51fd | 665 | } |
27df4123 | 666 | catch (const std::exception& e) |
460c51fd | 667 | { |
738835d7 | 668 | throw JSONRPCError(RPC_MISC_ERROR, e.what()); |
460c51fd | 669 | } |
4401b2d7 EL |
670 | |
671 | g_rpcSignals.PostCommand(*pcmd); | |
460c51fd | 672 | } |
69d605f4 | 673 | |
db954a65 PK |
674 | std::string HelpExampleCli(const std::string& methodname, const std::string& args) |
675 | { | |
589c42c1 | 676 | return "> komodo-cli " + methodname + " " + args + "\n"; |
bbb09365 WL |
677 | } |
678 | ||
db954a65 PK |
679 | std::string HelpExampleRpc(const std::string& methodname, const std::string& args) |
680 | { | |
bbb09365 | 681 | return "> curl --user myusername --data-binary '{\"jsonrpc\": \"1.0\", \"id\":\"curltest\", " |
41572034 | 682 | "\"method\": \"" + methodname + "\", \"params\": [" + args + "] }' -H 'content-type: text/plain;' http://127.0.0.1:7771/\n"; |
bbb09365 WL |
683 | } |
684 | ||
afd64f76 WL |
685 | void RPCRegisterTimerInterface(RPCTimerInterface *iface) |
686 | { | |
687 | timerInterfaces.push_back(iface); | |
688 | } | |
689 | ||
690 | void RPCUnregisterTimerInterface(RPCTimerInterface *iface) | |
691 | { | |
692 | std::vector<RPCTimerInterface*>::iterator i = std::find(timerInterfaces.begin(), timerInterfaces.end(), iface); | |
693 | assert(i != timerInterfaces.end()); | |
694 | timerInterfaces.erase(i); | |
695 | } | |
696 | ||
697 | void RPCRunLater(const std::string& name, boost::function<void(void)> func, int64_t nSeconds) | |
698 | { | |
699 | if (timerInterfaces.empty()) | |
700 | throw JSONRPCError(RPC_INTERNAL_ERROR, "No timer handler registered for RPC"); | |
701 | deadlineTimers.erase(name); | |
702 | RPCTimerInterface* timerInterface = timerInterfaces[0]; | |
703 | LogPrint("rpc", "queue run of timer %s in %i seconds (using %s)\n", name, nSeconds, timerInterface->Name()); | |
c922edd0 | 704 | deadlineTimers.insert(std::make_pair(name, boost::shared_ptr<RPCTimerBase>(timerInterface->NewTimer(func, nSeconds*1000)))); |
afd64f76 WL |
705 | } |
706 | ||
e46704dd | 707 | const CRPCTable tableRPC; |
fc72c078 S |
708 | |
709 | // Return async rpc queue | |
710 | std::shared_ptr<AsyncRPCQueue> getAsyncRPCQueue() | |
711 | { | |
f86f625d | 712 | return AsyncRPCQueue::sharedInstance(); |
fc72c078 | 713 | } |