rpcserver.cpp 43.7 KB
Newer Older
1
// Copyright (c) 2010 Satoshi Nakamoto
2
// Copyright (c) 2009-2014 The Bitcoin developers
3
4
// Copyright (c) 2014-2015 The Dash developers
// Copyright (c) 2014-2018 The Crown developers
5
// Distributed under the MIT software license, see the accompanying
Fordy's avatar
Fordy committed
6
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
7

8
#include "rpcserver.h"
9
10

#include "base58.h"
11
#include "init.h"
12
#include "main.h"
13
#include "ui_interface.h"
14
#include "util.h"
15
#ifdef ENABLE_WALLET
16
#include "wallet.h"
17
#endif
18

19
#include <boost/algorithm/string.hpp>
20
#include <boost/asio.hpp>
21
#include <boost/asio/ssl.hpp>
22
#include <boost/bind.hpp>
Pieter Wuille's avatar
Pieter Wuille committed
23
#include <boost/filesystem.hpp>
24
#include <boost/foreach.hpp>
25
26
#include <boost/iostreams/concepts.hpp>
#include <boost/iostreams/stream.hpp>
27
#include <boost/shared_ptr.hpp>
Wladimir J. van der Laan's avatar
Wladimir J. van der Laan committed
28
#include <boost/thread.hpp>
29
#include "json/json_spirit_writer_template.h"
Gavin Andresen's avatar
Gavin Andresen committed
30

31
32
33
using namespace boost;
using namespace boost::asio;
using namespace json_spirit;
34
using namespace std;
35

36
37
static std::string strRPCUserColonPass;

38
static bool fRPCRunning = false;
39
40
41
42
static bool fRPCInWarmup = true;
static std::string rpcWarmupStatus("RPC server started");
static CCriticalSection cs_rpcWarmup;

43
//! These are created by StartRPCThreads, destroyed in StopRPCThreads
44
static asio::io_service* rpc_io_service = NULL;
45
static map<string, boost::shared_ptr<deadline_timer> > deadlineTimers;
46
47
static ssl::context* rpc_ssl_context = NULL;
static boost::thread_group* rpc_worker_group = NULL;
48
static boost::asio::io_service::work *rpc_dummy_work = NULL;
49
static std::vector<CSubNet> rpc_allow_subnets; //!< List of subnets to allow RPC connections from
50
static std::vector< boost::shared_ptr<ip::tcp::acceptor> > rpc_acceptors;
51

52
void RPCTypeCheck(const Array& params,
53
54
                  const list<Value_type>& typesExpected,
                  bool fAllowNull)
55
{
56
    unsigned int i = 0;
57
58
59
60
61
    BOOST_FOREACH(Value_type t, typesExpected)
    {
        if (params.size() <= i)
            break;

62
63
        const Value& v = params[i];
        if (!((v.type() == t) || (fAllowNull && (v.type() == null_type))))
64
65
66
        {
            string err = strprintf("Expected type %s, got %s",
                                   Value_type_name[t], Value_type_name[v.type()]);
67
            throw JSONRPCError(RPC_TYPE_ERROR, err);
68
69
70
71
72
73
        }
        i++;
    }
}

void RPCTypeCheck(const Object& o,
74
75
                  const map<string, Value_type>& typesExpected,
                  bool fAllowNull)
76
77
78
79
{
    BOOST_FOREACH(const PAIRTYPE(string, Value_type)& t, typesExpected)
    {
        const Value& v = find_value(o, t.first);
80
        if (!fAllowNull && v.type() == null_type)
81
            throw JSONRPCError(RPC_TYPE_ERROR, strprintf("Missing %s", t.first));
82
83

        if (!((v.type() == t.second) || (fAllowNull && (v.type() == null_type))))
84
85
        {
            string err = strprintf("Expected type %s for %s, got %s",
86
                                   Value_type_name[t.second], t.first, Value_type_name[v.type()]);
87
            throw JSONRPCError(RPC_TYPE_ERROR, err);
88
89
90
91
        }
    }
}

Wladimir J. van der Laan's avatar
Wladimir J. van der Laan committed
92
93
94
95
96
static inline int64_t roundint64(double d)
{
    return (int64_t)(d > 0 ? d + 0.5 : d - 0.5);
}

97
CAmount AmountFromValue(const Value& value)
98
99
{
    double dAmount = value.get_real();
100
    if (dAmount <= 0.0 || dAmount > 21000000.0)
101
        throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount");
102
    CAmount nAmount = roundint64(dAmount * COIN);
103
    if (!MoneyRange(nAmount))
104
        throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount");
105
106
107
    return nAmount;
}

108
Value ValueFromAmount(const CAmount& amount)
109
110
111
112
{
    return (double)amount / (double)COIN;
}

113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
uint256 ParseHashV(const Value& v, string strName)
{
    string strHex;
    if (v.type() == str_type)
        strHex = v.get_str();
    if (!IsHex(strHex)) // Note: IsHex("") is false
        throw JSONRPCError(RPC_INVALID_PARAMETER, strName+" must be hexadecimal string (not '"+strHex+"')");
    uint256 result;
    result.SetHex(strHex);
    return result;
}
uint256 ParseHashO(const Object& o, string strKey)
{
    return ParseHashV(find_value(o, strKey), strKey);
}
vector<unsigned char> ParseHexV(const Value& v, string strName)
{
    string strHex;
    if (v.type() == str_type)
        strHex = v.get_str();
    if (!IsHex(strHex))
        throw JSONRPCError(RPC_INVALID_PARAMETER, strName+" must be hexadecimal string (not '"+strHex+"')");
    return ParseHex(strHex);
}
vector<unsigned char> ParseHexO(const Object& o, string strKey)
{
    return ParseHexV(find_value(o, strKey), strKey);
}
141

142

143
144
145
/**
 * Note: This interface may still be subject to change.
 */
146

147
string CRPCTable::help(string strCommand) const
148
149
{
    string strRet;
Cozz Lovan's avatar
Cozz Lovan committed
150
    string category;
151
    set<rpcfn_type> setDone;
Cozz Lovan's avatar
Cozz Lovan committed
152
153
    vector<pair<string, const CRPCCommand*> > vCommands;

154
    for (map<string, const CRPCCommand*>::const_iterator mi = mapCommands.begin(); mi != mapCommands.end(); ++mi)
Cozz Lovan's avatar
Cozz Lovan committed
155
156
157
158
        vCommands.push_back(make_pair(mi->second->category + mi->first, mi->second));
    sort(vCommands.begin(), vCommands.end());

    BOOST_FOREACH(const PAIRTYPE(string, const CRPCCommand*)& command, vCommands)
159
    {
Cozz Lovan's avatar
Cozz Lovan committed
160
161
        const CRPCCommand *pcmd = command.second;
        string strMethod = pcmd->name;
162
        // We already filter duplicates, but these deprecated screw up the sort order
163
        if (strMethod.find("label") != string::npos)
164
            continue;
Pieter Wuille's avatar
Pieter Wuille committed
165
        if ((strCommand != "" || pcmd->category == "hidden") && strMethod != strCommand)
166
            continue;
167
#ifdef ENABLE_WALLET
168
169
        if (pcmd->reqWallet && !pwalletMain)
            continue;
170
#endif
171

172
173
174
        try
        {
            Array params;
175
            rpcfn_type pfn = pcmd->actor;
176
177
178
179
180
181
182
183
            if (setDone.insert(pfn).second)
                (*pfn)(params, true);
        }
        catch (std::exception& e)
        {
            // Help text is returned in an exception
            string strHelp = string(e.what());
            if (strCommand == "")
Cozz Lovan's avatar
Cozz Lovan committed
184
            {
185
                if (strHelp.find('\n') != string::npos)
186
                    strHelp = strHelp.substr(0, strHelp.find('\n'));
Cozz Lovan's avatar
Cozz Lovan committed
187
188
189
190
191
192
193
194
195
196
197

                if (category != pcmd->category)
                {
                    if (!category.empty())
                        strRet += "\n";
                    category = pcmd->category;
                    string firstLetter = category.substr(0,1);
                    boost::to_upper(firstLetter);
                    strRet += "== " + firstLetter + category.substr(1) + " ==\n";
                }
            }
198
199
200
201
            strRet += strHelp + "\n";
        }
    }
    if (strRet == "")
202
        strRet = strprintf("help: unknown command: %s\n", strCommand);
203
204
205
206
    strRet = strRet.substr(0,strRet.size()-1);
    return strRet;
}

207
208
209
210
Value help(const Array& params, bool fHelp)
{
    if (fHelp || params.size() > 1)
        throw runtime_error(
sje's avatar
sje committed
211
212
213
214
215
216
217
            "help ( \"command\" )\n"
            "\nList all commands, or get help for a specified command.\n"
            "\nArguments:\n"
            "1. \"command\"     (string, optional) The command to get help on\n"
            "\nResult:\n"
            "\"text\"     (string) The help text\n"
        );
218
219
220
221
222
223
224
225

    string strCommand;
    if (params.size() > 0)
        strCommand = params[0].get_str();

    return tableRPC.help(strCommand);
}

226
227
228

Value stop(const Array& params, bool fHelp)
{
229
    // Accept the deprecated and ignored 'detach' boolean argument
230
    if (fHelp || params.size() > 1)
231
        throw runtime_error(
232
            "stop\n"
Infernoman's avatar
Infernoman committed
233
            "\nStop Crown server.");
234
    // Shutdown will take long enough that the response should get back
235
    StartShutdown();
Infernoman's avatar
Infernoman committed
236
    return "Crown server stopping";
237
238
}

Ashot's avatar
Ashot committed
239
240
241
242
243
244
245
246
247
248
249
Value restart(const Array& params, bool fHelp)
{
    // Accept the deprecated and ignored 'detach' boolean argument
    if (fHelp || params.size() > 1)
        throw runtime_error(
            "restart\n"
            "\nRestart Crown server.");
    // Restart will take long enough that the response should get back
    StartRestart();
    return "Crown server restarting";
}
250
251


252
253
254
/**
 * Call Table
 */
255
static const CRPCCommand vRPCCommands[] =
Cozz Lovan's avatar
Cozz Lovan committed
256
257
{ //  category              name                      actor (function)         okSafeMode threadSafe reqWallet
  //  --------------------- ------------------------  -----------------------  ---------- ---------- ---------
258
    /* Overall control/query calls */
Cozz Lovan's avatar
Cozz Lovan committed
259
260
261
    { "control",            "getinfo",                &getinfo,                true,      false,      false }, /* uses wallet if enabled */
    { "control",            "help",                   &help,                   true,      true,       false },
    { "control",            "stop",                   &stop,                   true,      true,       false },
Ashot's avatar
Ashot committed
262
    { "control",            "restart",                &restart,                true,      true,       false },
263
264

    /* P2P networking */
Cozz Lovan's avatar
Cozz Lovan committed
265
266
267
268
269
270
271
    { "network",            "getnetworkinfo",         &getnetworkinfo,         true,      false,      false },
    { "network",            "addnode",                &addnode,                true,      true,       false },
    { "network",            "getaddednodeinfo",       &getaddednodeinfo,       true,      true,       false },
    { "network",            "getconnectioncount",     &getconnectioncount,     true,      false,      false },
    { "network",            "getnettotals",           &getnettotals,           true,      true,       false },
    { "network",            "getpeerinfo",            &getpeerinfo,            true,      false,      false },
    { "network",            "ping",                   &ping,                   true,      false,      false },
272
273

    /* Block chain and UTXO */
Cozz Lovan's avatar
Cozz Lovan committed
274
275
276
277
278
    { "blockchain",         "getblockchaininfo",      &getblockchaininfo,      true,      false,      false },
    { "blockchain",         "getbestblockhash",       &getbestblockhash,       true,      false,      false },
    { "blockchain",         "getblockcount",          &getblockcount,          true,      false,      false },
    { "blockchain",         "getblock",               &getblock,               true,      false,      false },
    { "blockchain",         "getblockhash",           &getblockhash,           true,      false,      false },
279
    { "blockchain",         "getblockheader",         &getblockheader,         false,     false,      false },
Cozz Lovan's avatar
Cozz Lovan committed
280
281
    { "blockchain",         "getchaintips",           &getchaintips,           true,      false,      false },
    { "blockchain",         "getdifficulty",          &getdifficulty,          true,      false,      false },
282
    { "blockchain",         "getmempoolinfo",         &getmempoolinfo,         true,      true,       false },
Cozz Lovan's avatar
Cozz Lovan committed
283
284
285
286
    { "blockchain",         "getrawmempool",          &getrawmempool,          true,      false,      false },
    { "blockchain",         "gettxout",               &gettxout,               true,      false,      false },
    { "blockchain",         "gettxoutsetinfo",        &gettxoutsetinfo,        true,      false,      false },
    { "blockchain",         "verifychain",            &verifychain,            true,      false,      false },
287
288
    { "blockchain",         "invalidateblock",        &invalidateblock,        true,      true,       false },
    { "blockchain",         "reconsiderblock",        &reconsiderblock,        true,      true,       false },
289

290
    /* Mining */
Cozz Lovan's avatar
Cozz Lovan committed
291
292
293
294
295
296
297
298
299
300
301
    { "mining",             "getblocktemplate",       &getblocktemplate,       true,      false,      false },
    { "mining",             "getmininginfo",          &getmininginfo,          true,      false,      false },
    { "mining",             "getnetworkhashps",       &getnetworkhashps,       true,      false,      false },
    { "mining",             "prioritisetransaction",  &prioritisetransaction,  true,      false,      false },
    { "mining",             "submitblock",            &submitblock,            true,      true,       false },

#ifdef ENABLE_WALLET
    /* Coin generation */
    { "generating",         "getgenerate",            &getgenerate,            true,      false,      false },
    { "generating",         "gethashespersec",        &gethashespersec,        true,      false,      false },
    { "generating",         "setgenerate",            &setgenerate,            true,      true,       false },
Infernoman's avatar
Infernoman committed
302
    { "generating",         "getauxblock",            &getauxblock,            true,      true,       true  },
Cozz Lovan's avatar
Cozz Lovan committed
303
#endif
304
305

    /* Raw transactions */
Cozz Lovan's avatar
Cozz Lovan committed
306
307
308
309
310
311
    { "rawtransactions",    "createrawtransaction",   &createrawtransaction,   true,      false,      false },
    { "rawtransactions",    "decoderawtransaction",   &decoderawtransaction,   true,      false,      false },
    { "rawtransactions",    "decodescript",           &decodescript,           true,      false,      false },
    { "rawtransactions",    "getrawtransaction",      &getrawtransaction,      true,      false,      false },
    { "rawtransactions",    "sendrawtransaction",     &sendrawtransaction,     false,     false,      false },
    { "rawtransactions",    "signrawtransaction",     &signrawtransaction,     false,     false,      false }, /* uses wallet if enabled */
312
313

    /* Utility functions */
Cozz Lovan's avatar
Cozz Lovan committed
314
315
316
317
318
    { "util",               "createmultisig",         &createmultisig,         true,      true ,      false },
    { "util",               "validateaddress",        &validateaddress,        true,      false,      false }, /* uses wallet if enabled */
    { "util",               "verifymessage",          &verifymessage,          true,      false,      false },
    { "util",               "estimatefee",            &estimatefee,            true,      true,       false },
    { "util",               "estimatepriority",       &estimatepriority,       true,      true,       false },
319

Pieter Wuille's avatar
Pieter Wuille committed
320
321
322
    /* Not shown in help */
    { "hidden",             "invalidateblock",        &invalidateblock,        true,      true,       false },
    { "hidden",             "reconsiderblock",        &reconsiderblock,        true,      true,       false },
323
    { "hidden",             "setmocktime",            &setmocktime,            true,      false,      false },
324
    { "hidden",             "sendalert",              &sendalert,              false,     false,      false },
Infernoman's avatar
Infernoman committed
325
    /* Crown features */
Alastair Clark's avatar
Alastair Clark committed
326
327
328
    { "crown",               "masternode",                &masternode,                 true,      true,       false },
    { "crown",               "masternodelist",            &masternodelist,             true,      true,       false },
    { "crown",               "masternodebroadcast",       &masternodebroadcast,        true,      true,       false },
Infernoman's avatar
Infernoman committed
329
330
331
332
    { "crown",               "mnbudget",              &mnbudget,               true,      true,       false },
    { "crown",               "mnbudgetvoteraw",       &mnbudgetvoteraw,        true,      true,       false },
    { "crown",               "mnfinalbudget",         &mnfinalbudget,          true,      true,       false },
    { "crown",               "mnsync",                &mnsync,                 true,      true,       false },
333
    { "crown",               "snsync",                &snsync,                 true,      true,       false },
Infernoman's avatar
Infernoman committed
334
    { "crown",               "spork",                 &spork,                  true,      true,       false },
335
336
337
    { "crown",               "systemnode",           &systemnode,            true,      true,       false },
    { "crown",               "systemnodelist",       &systemnodelist,        true,      true,       false },
    { "crown",               "systemnodebroadcast",  &systemnodebroadcast,   true,      true,       false },
338
    { "crown",               "node",  &node,   true,      true,       false },
Infernoman's avatar
Infernoman committed
339
340
341
342

    /* API features */
    { "api",                 "service",               &service,                true,      true,       false },

343
#ifdef ENABLE_WALLET
344

345
    /* Wallet */
Cozz Lovan's avatar
Cozz Lovan committed
346
347
348
349
350
351
352
353
354
355
    { "wallet",             "addmultisigaddress",     &addmultisigaddress,     true,      false,      true },
    { "wallet",             "backupwallet",           &backupwallet,           true,      false,      true },
    { "wallet",             "dumpprivkey",            &dumpprivkey,            true,      false,      true },
    { "wallet",             "dumpwallet",             &dumpwallet,             true,      false,      true },
    { "wallet",             "encryptwallet",          &encryptwallet,          true,      false,      true },
    { "wallet",             "getaccountaddress",      &getaccountaddress,      true,      false,      true },
    { "wallet",             "getaccount",             &getaccount,             true,      false,      true },
    { "wallet",             "getaddressesbyaccount",  &getaddressesbyaccount,  true,      false,      true },
    { "wallet",             "getbalance",             &getbalance,             false,     false,      true },
    { "wallet",             "getnewaddress",          &getnewaddress,          true,      false,      true },
356
    { "wallet",             "oldtonewaddress",        &oldtonewaddress,        true,      false,      true },
Cozz Lovan's avatar
Cozz Lovan committed
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
    { "wallet",             "getrawchangeaddress",    &getrawchangeaddress,    true,      false,      true },
    { "wallet",             "getreceivedbyaccount",   &getreceivedbyaccount,   false,     false,      true },
    { "wallet",             "getreceivedbyaddress",   &getreceivedbyaddress,   false,     false,      true },
    { "wallet",             "gettransaction",         &gettransaction,         false,     false,      true },
    { "wallet",             "getunconfirmedbalance",  &getunconfirmedbalance,  false,     false,      true },
    { "wallet",             "getwalletinfo",          &getwalletinfo,          false,     false,      true },
    { "wallet",             "importprivkey",          &importprivkey,          true,      false,      true },
    { "wallet",             "importwallet",           &importwallet,           true,      false,      true },
    { "wallet",             "importaddress",          &importaddress,          true,      false,      true },
    { "wallet",             "keypoolrefill",          &keypoolrefill,          true,      false,      true },
    { "wallet",             "listaccounts",           &listaccounts,           false,     false,      true },
    { "wallet",             "listaddressgroupings",   &listaddressgroupings,   false,     false,      true },
    { "wallet",             "listlockunspent",        &listlockunspent,        false,     false,      true },
    { "wallet",             "listreceivedbyaccount",  &listreceivedbyaccount,  false,     false,      true },
    { "wallet",             "listreceivedbyaddress",  &listreceivedbyaddress,  false,     false,      true },
    { "wallet",             "listsinceblock",         &listsinceblock,         false,     false,      true },
    { "wallet",             "listtransactions",       &listtransactions,       false,     false,      true },
    { "wallet",             "listunspent",            &listunspent,            false,     false,      true },
    { "wallet",             "lockunspent",            &lockunspent,            true,      false,      true },
    { "wallet",             "move",                   &movecmd,                false,     false,      true },
    { "wallet",             "sendfrom",               &sendfrom,               false,     false,      true },
    { "wallet",             "sendmany",               &sendmany,               false,     false,      true },
    { "wallet",             "sendtoaddress",          &sendtoaddress,          false,     false,      true },
380
    { "wallet",             "sendtoaddressix",        &sendtoaddressix,        false,     false,      true },
Cozz Lovan's avatar
Cozz Lovan committed
381
382
383
384
385
386
    { "wallet",             "setaccount",             &setaccount,             true,      false,      true },
    { "wallet",             "settxfee",               &settxfee,               true,      false,      true },
    { "wallet",             "signmessage",            &signmessage,            true,      false,      true },
    { "wallet",             "walletlock",             &walletlock,             true,      false,      true },
    { "wallet",             "walletpassphrasechange", &walletpassphrasechange, true,      false,      true },
    { "wallet",             "walletpassphrase",       &walletpassphrase,       true,      false,      true },
387
    { "wallet",             "update",                 &update,                 true,      false,      true },
388
#endif // ENABLE_WALLET
389
390
};

391
CRPCTable::CRPCTable()
392
393
394
395
{
    unsigned int vcidx;
    for (vcidx = 0; vcidx < (sizeof(vRPCCommands) / sizeof(vRPCCommands[0])); vcidx++)
    {
396
        const CRPCCommand *pcmd;
397

398
399
400
401
        pcmd = &vRPCCommands[vcidx];
        mapCommands[pcmd->name] = pcmd;
    }
}
402

403
404
405
406
407
408
409
const CRPCCommand *CRPCTable::operator[](string name) const
{
    map<string, const CRPCCommand*>::const_iterator it = mapCommands.find(name);
    if (it == mapCommands.end())
        return NULL;
    return (*it).second;
}
410
411
412
413
414
415
416
417
418


bool HTTPAuthorized(map<string, string>& mapHeaders)
{
    string strAuth = mapHeaders["authorization"];
    if (strAuth.substr(0,6) != "Basic ")
        return false;
    string strUserPass64 = strAuth.substr(6); boost::trim(strUserPass64);
    string strUserPass = DecodeBase64(strUserPass64);
419
    return TimingResistantEqual(strUserPass, strRPCUserColonPass);
420
421
422
423
424
}

void ErrorReply(std::ostream& stream, const Object& objError, const Value& id)
{
    // Send error reply from json-rpc error object
425
    int nStatus = HTTP_INTERNAL_SERVER_ERROR;
426
    int code = find_value(objError, "code").get_int();
427
428
    if (code == RPC_INVALID_REQUEST) nStatus = HTTP_BAD_REQUEST;
    else if (code == RPC_METHOD_NOT_FOUND) nStatus = HTTP_NOT_FOUND;
429
    string strReply = JSONRPCReply(Value::null, objError, id);
430
    stream << HTTPReply(nStatus, strReply, false) << std::flush;
431
432
}

433
CNetAddr BoostAsioToCNetAddr(boost::asio::ip::address address)
434
{
435
    CNetAddr netaddr;
436
437
438
439
    // Make sure that IPv4-compatible and IPv4-mapped IPv6 addresses are treated as IPv4 addresses
    if (address.is_v6()
     && (address.to_v6().is_v4_compatible()
      || address.to_v6().is_v4_mapped()))
440
441
442
443
444
445
446
447
448
449
450
451
452
        address = address.to_v6().to_v4();

    if(address.is_v4())
    {
        boost::asio::ip::address_v4::bytes_type bytes = address.to_v4().to_bytes();
        netaddr.SetRaw(NET_IPV4, &bytes[0]);
    }
    else
    {
        boost::asio::ip::address_v6::bytes_type bytes = address.to_v6().to_bytes();
        netaddr.SetRaw(NET_IPV6, &bytes[0]);
    }
    return netaddr;
453
454
}

455
bool ClientAllowed(const boost::asio::ip::address& address)
456
{
457
458
459
    CNetAddr netaddr = BoostAsioToCNetAddr(address);
    BOOST_FOREACH(const CSubNet &subnet, rpc_allow_subnets)
        if (subnet.Match(netaddr))
460
461
462
            return true;
    return false;
}
463

464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
template <typename Protocol>
class AcceptedConnectionImpl : public AcceptedConnection
{
public:
    AcceptedConnectionImpl(
            asio::io_service& io_service,
            ssl::context &context,
            bool fUseSSL) :
        sslStream(io_service, context),
        _d(sslStream, fUseSSL),
        _stream(_d)
    {
    }

    virtual std::iostream& stream()
    {
        return _stream;
    }

    virtual std::string peer_address_to_string() const
    {
        return peer.address().to_string();
    }
487

488
489
490
491
492
493
494
495
496
497
498
    virtual void close()
    {
        _stream.close();
    }

    typename Protocol::endpoint peer;
    asio::ssl::stream<typename Protocol::socket> sslStream;

private:
    SSLIOStreamDevice<Protocol> _d;
    iostreams::stream< SSLIOStreamDevice<Protocol> > _stream;
499
500
};

501
void ServiceConnection(AcceptedConnection *conn);
502

503
//! Forward declaration required for RPCListen
504
505
template <typename Protocol, typename SocketAcceptorService>
static void RPCAcceptHandler(boost::shared_ptr< basic_socket_acceptor<Protocol, SocketAcceptorService> > acceptor,
506
507
                             ssl::context& context,
                             bool fUseSSL,
508
                             boost::shared_ptr< AcceptedConnection > conn,
509
510
511
512
513
                             const boost::system::error_code& error);

/**
 * Sets up I/O resources to accept and handle a new connection.
 */
514
515
template <typename Protocol, typename SocketAcceptorService>
static void RPCListen(boost::shared_ptr< basic_socket_acceptor<Protocol, SocketAcceptorService> > acceptor,
516
517
518
519
                   ssl::context& context,
                   const bool fUseSSL)
{
    // Accept connection
520
    boost::shared_ptr< AcceptedConnectionImpl<Protocol> > conn(new AcceptedConnectionImpl<Protocol>(acceptor->get_io_service(), context, fUseSSL));
521
522
523
524

    acceptor->async_accept(
            conn->sslStream.lowest_layer(),
            conn->peer,
525
            boost::bind(&RPCAcceptHandler<Protocol, SocketAcceptorService>,
526
527
528
529
                acceptor,
                boost::ref(context),
                fUseSSL,
                conn,
530
                _1));
531
532
}

533

534
535
536
/**
 * Accept and handle incoming connection.
 */
537
538
template <typename Protocol, typename SocketAcceptorService>
static void RPCAcceptHandler(boost::shared_ptr< basic_socket_acceptor<Protocol, SocketAcceptorService> > acceptor,
539
540
                             ssl::context& context,
                             const bool fUseSSL,
541
                             boost::shared_ptr< AcceptedConnection > conn,
542
543
                             const boost::system::error_code& error)
{
544
    // Immediately start accepting new connections, except when we're cancelled or our socket is closed.
545
    if (error != asio::error::operation_aborted && acceptor->is_open())
546
        RPCListen(acceptor, context, fUseSSL);
547

548
    AcceptedConnectionImpl<ip::tcp>* tcp_conn = dynamic_cast< AcceptedConnectionImpl<ip::tcp>* >(conn.get());
549

550
551
    if (error)
    {
552
553
        // TODO: Actually handle errors
        LogPrintf("%s: Error: %s\n", __func__, error.message());
554
555
556
557
    }
    // Restrict callers by IP.  It is important to
    // do this before starting client thread, to filter out
    // certain DoS and misbehaving clients.
558
    else if (tcp_conn && !ClientAllowed(tcp_conn->peer.address()))
559
560
561
    {
        // Only send a 403 if we're not using SSL to prevent a DoS during the SSL handshake.
        if (!fUseSSL)
kazcw's avatar
kazcw committed
562
            conn->stream() << HTTPError(HTTP_FORBIDDEN, false) << std::flush;
563
        conn->close();
564
    }
565
    else {
566
        ServiceConnection(conn.get());
567
        conn->close();
568
569
570
    }
}

571
572
573
574
575
576
577
578
static ip::tcp::endpoint ParseEndpoint(const std::string &strEndpoint, int defaultPort)
{
    std::string addr;
    int port = defaultPort;
    SplitHostPort(strEndpoint, port, addr);
    return ip::tcp::endpoint(asio::ip::address::from_string(addr), port);
}

579
void StartRPCThreads()
580
{
581
582
583
584
585
586
587
588
589
590
591
592
    rpc_allow_subnets.clear();
    rpc_allow_subnets.push_back(CSubNet("127.0.0.0/8")); // always allow IPv4 local subnet
    rpc_allow_subnets.push_back(CSubNet("::1")); // always allow IPv6 localhost
    if (mapMultiArgs.count("-rpcallowip"))
    {
        const vector<string>& vAllow = mapMultiArgs["-rpcallowip"];
        BOOST_FOREACH(string strAllow, vAllow)
        {
            CSubNet subnet(strAllow);
            if(!subnet.IsValid())
            {
                uiInterface.ThreadSafeMessageBox(
593
                    strprintf("Invalid -rpcallowip subnet specification: %s. Valid are a single IP (e.g. 1.2.3.4), a network/netmask (e.g. 1.2.3.4/255.255.255.0) or a network/CIDR (e.g. 1.2.3.4/24).", strAllow),
594
595
596
597
598
599
600
601
602
603
604
605
                    "", CClientUIInterface::MSG_ERROR);
                StartShutdown();
                return;
            }
            rpc_allow_subnets.push_back(subnet);
        }
    }
    std::string strAllowed;
    BOOST_FOREACH(const CSubNet &subnet, rpc_allow_subnets)
        strAllowed += subnet.ToString() + " ";
    LogPrint("rpc", "Allowing RPC connections from: %s\n", strAllowed);

606
    strRPCUserColonPass = mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"];
607
608
    if (((mapArgs["-rpcpassword"] == "") ||
         (mapArgs["-rpcuser"] == mapArgs["-rpcpassword"])) && Params().RequireRPCPassword())
609
    {
610
        unsigned char rand_pwd[32];
611
        GetRandBytes(rand_pwd, 32);
612
        uiInterface.ThreadSafeMessageBox(strprintf(
infernoman's avatar
infernoman committed
613
            _("To use crownd, or the -server option to crown-qt, you must set an rpcpassword in the configuration file:\n"
614
              "%s\n"
615
              "It is recommended you use the following random password:\n"
infernoman's avatar
infernoman committed
616
              "rpcuser=crownrpc\n"
617
618
              "rpcpassword=%s\n"
              "(you do not need to remember this password)\n"
619
              "The username and password MUST NOT be the same.\n"
Gavin Andresen's avatar
Gavin Andresen committed
620
621
              "If the file does not exist, create it with owner-readable-only file permissions.\n"
              "It is also recommended to set alertnotify so you are notified of problems;\n"
Infernoman's avatar
Infernoman committed
622
              "for example: alertnotify=echo %%s | mail -s \"Crown Alert\" admin@foo.com\n"),
623
624
                GetConfigFile().string(),
                EncodeBase58(&rand_pwd[0],&rand_pwd[0]+32)),
625
                "", CClientUIInterface::MSG_ERROR | CClientUIInterface::SECURE);
626
        StartShutdown();
627
628
629
        return;
    }

630
631
632
    assert(rpc_io_service == NULL);
    rpc_io_service = new asio::io_service();
    rpc_ssl_context = new ssl::context(*rpc_io_service, ssl::context::sslv23);
633

634
    const bool fUseSSL = GetBoolArg("-rpcssl", false);
635

636
637
    if (fUseSSL)
    {
638
        rpc_ssl_context->set_options(ssl::context::no_sslv2 | ssl::context::no_sslv3);
639
640
641

        filesystem::path pathCertFile(GetArg("-rpcsslcertificatechainfile", "server.cert"));
        if (!pathCertFile.is_complete()) pathCertFile = filesystem::path(GetDataDir()) / pathCertFile;
642
        if (filesystem::exists(pathCertFile)) rpc_ssl_context->use_certificate_chain_file(pathCertFile.string());
643
        else LogPrintf("ThreadRPCServer ERROR: missing server certificate file %s\n", pathCertFile.string());
644
645
646

        filesystem::path pathPKFile(GetArg("-rpcsslprivatekeyfile", "server.pem"));
        if (!pathPKFile.is_complete()) pathPKFile = filesystem::path(GetDataDir()) / pathPKFile;
647
        if (filesystem::exists(pathPKFile)) rpc_ssl_context->use_private_key_file(pathPKFile.string(), ssl::context::pem);
648
        else LogPrintf("ThreadRPCServer ERROR: missing server private key file %s\n", pathPKFile.string());
649

650
        string strCiphers = GetArg("-rpcsslciphers", "TLSv1.2+HIGH:TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!3DES:@STRENGTH");
651
        SSL_CTX_set_cipher_list(rpc_ssl_context->impl(), strCiphers.c_str());
652
653
    }

654
655
    std::vector<ip::tcp::endpoint> vEndpoints;
    bool bBindAny = false;
656
    int defaultPort = GetArg("-rpcport", BaseParams().RPCPort());
657
    if (!mapArgs.count("-rpcallowip")) // Default to loopback if not allowing external IPs
658
    {
659
660
661
662
663
664
665
        vEndpoints.push_back(ip::tcp::endpoint(asio::ip::address_v6::loopback(), defaultPort));
        vEndpoints.push_back(ip::tcp::endpoint(asio::ip::address_v4::loopback(), defaultPort));
        if (mapArgs.count("-rpcbind"))
        {
            LogPrintf("WARNING: option -rpcbind was ignored because -rpcallowip was not specified, refusing to allow everyone to connect\n");
        }
    } else if (mapArgs.count("-rpcbind")) // Specific bind address
666
    {
667
        BOOST_FOREACH(const std::string &addr, mapMultiArgs["-rpcbind"])
668
        {
669
670
671
            try {
                vEndpoints.push_back(ParseEndpoint(addr, defaultPort));
            }
ENikS's avatar
ENikS committed
672
            catch(const boost::system::system_error &)
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
            {
                uiInterface.ThreadSafeMessageBox(
                    strprintf(_("Could not parse -rpcbind value %s as network address"), addr),
                    "", CClientUIInterface::MSG_ERROR);
                StartShutdown();
                return;
            }
        }
    } else { // No specific bind address specified, bind to any
        vEndpoints.push_back(ip::tcp::endpoint(asio::ip::address_v6::any(), defaultPort));
        vEndpoints.push_back(ip::tcp::endpoint(asio::ip::address_v4::any(), defaultPort));
        // Prefer making the socket dual IPv6/IPv4 instead of binding
        // to both addresses seperately.
        bBindAny = true;
    }
688

689
690
    bool fListening = false;
    std::string strerr;
691
    std::string straddress;
692
    BOOST_FOREACH(const ip::tcp::endpoint &endpoint, vEndpoints)
693
    {
694
        try {
695
696
697
698
            asio::ip::address bindAddress = endpoint.address();
            straddress = bindAddress.to_string();
            LogPrintf("Binding RPC on address %s port %i (IPv4+IPv6 bind any: %i)\n", straddress, endpoint.port(), bBindAny);
            boost::system::error_code v6_only_error;
699
            boost::shared_ptr<ip::tcp::acceptor> acceptor(new ip::tcp::acceptor(*rpc_io_service));
700

701
702
            acceptor->open(endpoint.protocol());
            acceptor->set_option(boost::asio::ip::tcp::acceptor::reuse_address(true));
703
704
705
706
707

            // Try making the socket dual IPv6/IPv4 when listening on the IPv6 "any" address
            acceptor->set_option(boost::asio::ip::v6_only(
                !bBindAny || bindAddress != asio::ip::address_v6::any()), v6_only_error);

708
709
710
            acceptor->bind(endpoint);
            acceptor->listen(socket_base::max_connections);

711
            RPCListen(acceptor, *rpc_ssl_context, fUseSSL);
712

713
            rpc_acceptors.push_back(acceptor);
714
            fListening = true;
715
            rpc_acceptors.push_back(acceptor);
716
            // If dual IPv6/IPv4 bind successful, skip binding to IPv4 separately
717
718
719
720
721
            if(bBindAny && bindAddress == asio::ip::address_v6::any() && !v6_only_error)
                break;
        }
        catch(boost::system::system_error &e)
        {
722
723
            LogPrintf("ERROR: Binding RPC on address %s port %i failed: %s\n", straddress, endpoint.port(), e.what());
            strerr = strprintf(_("An error occurred while setting up the RPC address %s port %u for listening: %s"), straddress, endpoint.port(), e.what());
724
        }
725
726
727
    }

    if (!fListening) {
728
        uiInterface.ThreadSafeMessageBox(strerr, "", CClientUIInterface::MSG_ERROR);
729
        StartShutdown();
730
731
        return;
    }
732

733
734
735
    rpc_worker_group = new boost::thread_group();
    for (int i = 0; i < GetArg("-rpcthreads", 4); i++)
        rpc_worker_group->create_thread(boost::bind(&asio::io_service::run, rpc_io_service));
736
    fRPCRunning = true;
737
738
}

739
740
741
742
743
744
745
746
747
748
void StartDummyRPCThread()
{
    if(rpc_io_service == NULL)
    {
        rpc_io_service = new asio::io_service();
        /* Create dummy "work" to keep the thread from exiting when no timeouts active,
         * see http://www.boost.org/doc/libs/1_51_0/doc/html/boost_asio/reference/io_service.html#boost_asio.reference.io_service.stopping_the_io_service_from_running_out_of_work */
        rpc_dummy_work = new asio::io_service::work(*rpc_io_service);
        rpc_worker_group = new boost::thread_group();
        rpc_worker_group->create_thread(boost::bind(&asio::io_service::run, rpc_io_service));
749
        fRPCRunning = true;
750
751
752
    }
}

753
754
755
void StopRPCThreads()
{
    if (rpc_io_service == NULL) return;
756
757
    // Set this to false first, so that longpolling loops will exit when woken up
    fRPCRunning = false;
758

759
760
761
    // First, cancel all timers and acceptors
    // This is not done automatically by ->stop(), and in some cases the destructor of
    // asio::io_service can hang if this is skipped.
762
    boost::system::error_code ec;
763
    BOOST_FOREACH(const boost::shared_ptr<ip::tcp::acceptor> &acceptor, rpc_acceptors)
764
765
766
767
768
    {
        acceptor->cancel(ec);
        if (ec)
            LogPrintf("%s: Warning: %s when cancelling acceptor", __func__, ec.message());
    }
769
770
    rpc_acceptors.clear();
    BOOST_FOREACH(const PAIRTYPE(std::string, boost::shared_ptr<deadline_timer>) &timer, deadlineTimers)
771
772
773
774
775
    {
        timer.second->cancel(ec);
        if (ec)
            LogPrintf("%s: Warning: %s when cancelling timer", __func__, ec.message());
    }
776
    deadlineTimers.clear();
777

778
    rpc_io_service->stop();
779
    cvBlockChange.notify_all();
780
781
    if (rpc_worker_group != NULL)
        rpc_worker_group->join_all();
782
    delete rpc_dummy_work; rpc_dummy_work = NULL;
783
784
785
    delete rpc_worker_group; rpc_worker_group = NULL;
    delete rpc_ssl_context; rpc_ssl_context = NULL;
    delete rpc_io_service; rpc_io_service = NULL;
786
787
}

788
789
790
791
792
bool IsRPCRunning()
{
    return fRPCRunning;
}

793
794
795
796
797
798
799
800
801
802
803
804
805
void SetRPCWarmupStatus(const std::string& newStatus)
{
    LOCK(cs_rpcWarmup);
    rpcWarmupStatus = newStatus;
}

void SetRPCWarmupFinished()
{
    LOCK(cs_rpcWarmup);
    assert(fRPCInWarmup);
    fRPCInWarmup = false;
}

806
807
808
809
810
811
812
813
bool RPCIsInWarmup(std::string *outStatus)
{
    LOCK(cs_rpcWarmup);
    if (outStatus)
        *outStatus = rpcWarmupStatus;
    return fRPCInWarmup;
}

814
815
816
817
818
819
void RPCRunHandler(const boost::system::error_code& err, boost::function<void(void)> func)
{
    if (!err)
        func();
}

820
void RPCRunLater(const std::string& name, boost::function<void(void)> func, int64_t nSeconds)
821
822
823
824
825
826
827
828
829
830
831
832
{
    assert(rpc_io_service != NULL);

    if (deadlineTimers.count(name) == 0)
    {
        deadlineTimers.insert(make_pair(name,
                                        boost::shared_ptr<deadline_timer>(new deadline_timer(*rpc_io_service))));
    }
    deadlineTimers[name]->expires_from_now(posix_time::seconds(nSeconds));
    deadlineTimers[name]->async_wait(boost::bind(RPCRunHandler, _1, func));
}

833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
class JSONRequest
{
public:
    Value id;
    string strMethod;
    Array params;

    JSONRequest() { id = Value::null; }
    void parse(const Value& valRequest);
};

void JSONRequest::parse(const Value& valRequest)
{
    // Parse request
    if (valRequest.type() != obj_type)
848
        throw JSONRPCError(RPC_INVALID_REQUEST, "Invalid Request object");
849
850
851
852
853
854
855
856
    const Object& request = valRequest.get_obj();

    // Parse id now so errors from here on will have the id
    id = find_value(request, "id");

    // Parse method
    Value valMethod = find_value(request, "method");
    if (valMethod.type() == null_type)
857
        throw JSONRPCError(RPC_INVALID_REQUEST, "Missing method");
858
    if (valMethod.type() != str_type)
859
        throw JSONRPCError(RPC_INVALID_REQUEST, "Method must be a string");
860
    strMethod = valMethod.get_str();
Pieter Wuille's avatar
Pieter Wuille committed
861
    if (strMethod != "getblocktemplate")
862
        LogPrint("rpc", "ThreadRPCServer method=%s\n", SanitizeString(strMethod));
863
864
865
866
867
868
869
870

    // Parse params
    Value valParams = find_value(request, "params");
    if (valParams.type() == array_type)
        params = valParams.get_array();
    else if (valParams.type() == null_type)
        params = Array();
    else
871
        throw JSONRPCError(RPC_INVALID_REQUEST, "Params must be an array");
872
873
}

874

875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
static Object JSONRPCExecOne(const Value& req)
{
    Object rpc_result;

    JSONRequest jreq;
    try {
        jreq.parse(req);

        Value result = tableRPC.execute(jreq.strMethod, jreq.params);
        rpc_result = JSONRPCReplyObj(result, Value::null, jreq.id);
    }
    catch (Object& objError)
    {
        rpc_result = JSONRPCReplyObj(Value::null, objError, jreq.id);
    }
    catch (std::exception& e)
    {
        rpc_result = JSONRPCReplyObj(Value::null,
893
                                     JSONRPCError(RPC_PARSE_ERROR, e.what()), jreq.id);
894
895
896
897
898
899
900
901
902
903
904
    }

    return rpc_result;
}

static string JSONRPCExecBatch(const Array& vReq)
{
    Array ret;
    for (unsigned int reqIdx = 0; reqIdx < vReq.size(); reqIdx++)
        ret.push_back(JSONRPCExecOne(vReq[reqIdx]));

905
    return write_string(Value(ret), false) + "\n";
906
907
}

908
909
910
911
912
913
914
915
static bool HTTPReq_JSONRPC(AcceptedConnection *conn,
                            string& strRequest,
                            map<string, string>& mapHeaders,
                            bool fRun)
{
    // Check authorization
    if (mapHeaders.count("authorization") == 0)
    {
kazcw's avatar
kazcw committed
916
        conn->stream() << HTTPError(HTTP_UNAUTHORIZED, false) << std::flush;
917
918
919
920
921
922
        return false;
    }

    if (!HTTPAuthorized(mapHeaders))
    {
        LogPrintf("ThreadRPCServer incorrect password attempt from %s\n", conn->peer_address_to_string());
923
        /* Deter brute-forcing
924
925
           If this results in a DoS the user really
           shouldn't have their RPC port exposed. */
926
        MilliSleep(250);
927

kazcw's avatar
kazcw committed
928
        conn->stream() << HTTPError(HTTP_UNAUTHORIZED, false) << std::flush;
929
930
931
932
933
934
935
936
937
938
939
        return false;
    }

    JSONRequest jreq;
    try
    {
        // Parse request
        Value valRequest;
        if (!read_string(strRequest, valRequest))
            throw JSONRPCError(RPC_PARSE_ERROR, "Parse error");

940
941
942
943
944
945
946
        // Return immediately if in warmup
        {
            LOCK(cs_rpcWarmup);
            if (fRPCInWarmup)
                throw JSONRPCError(RPC_IN_WARMUP, rpcWarmupStatus);
        }

947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
        string strReply;

        // singleton request
        if (valRequest.type() == obj_type) {
            jreq.parse(valRequest);

            Value result = tableRPC.execute(jreq.strMethod, jreq.params);

            // Send reply
            strReply = JSONRPCReply(result, Value::null, jreq.id);

        // array of requests
        } else if (valRequest.type() == array_type)
            strReply = JSONRPCExecBatch(valRequest.get_array());
        else
            throw JSONRPCError(RPC_PARSE_ERROR, "Top-level object parse error");

964
        conn->stream() << HTTPReplyHeader(HTTP_OK, fRun, strReply.size()) << strReply << std::flush;
965
966
967
968
969
970
971
972
973
974
975
976
977
978
    }
    catch (Object& objError)
    {
        ErrorReply(conn->stream(), objError, jreq.id);
        return false;
    }
    catch (std::exception& e)
    {
        ErrorReply(conn->stream(), JSONRPCError(RPC_PARSE_ERROR, e.what()), jreq.id);
        return false;
    }
    return true;
}

979
void ServiceConnection(AcceptedConnection *conn)
980
{
981
    bool fRun = true;
Gavin Andresen's avatar
Gavin Andresen committed
982
    while (fRun && !ShutdownRequested())
983
    {
984
        int nProto = 0;
985
        map<string, string> mapHeaders;
986
987
988
989
990
        string strRequest, strMethod, strURI;

        // Read HTTP request line
        if (!ReadHTTPRequestLine(conn->stream(), nProto, strMethod, strURI))
            break;
991

992
        // Read HTTP message headers and body
993
        ReadHTTPMessage(conn->stream(), mapHeaders, strRequest, nProto, MAX_SIZE);
994

995
        // HTTP Keep-Alive is false; close connection immediately
996
        if ((mapHeaders["connection"] == "close") || (!GetBoolArg("-rpckeepalive", true)))
997
            fRun = false;
998

999
        // Process via JSON-RPC API
1000
1001
1002
        if (strURI == "/") {
            if (!HTTPReq_JSONRPC(conn, strRequest, mapHeaders, fRun))
                break;
1003

1004
        // Process via HTTP REST API
1005
        } else if (strURI.substr(0, 6) == "/rest/" && GetBoolArg("-rest", false)) {
1006
1007
            if (!HTTPReq_REST(conn, strURI, mapHeaders, fRun))
                break;
1008

1009
        } else {
Jeff Garzik's avatar
Jeff Garzik committed
1010
            conn->stream() << HTTPError(HTTP_NOT_FOUND, false) << std::flush;
1011
            break;
1012
1013
1014
1015
        }
    }
}

1016
1017
1018
1019
1020
json_spirit::Value CRPCTable::execute(const std::string &strMethod, const json_spirit::Array &params) const
{
    // Find method
    const CRPCCommand *pcmd = tableRPC[strMethod];
    if (!pcmd)
1021
        throw JSONRPCError(RPC_METHOD_NOT_FOUND, "Method not found");
1022
#ifdef ENABLE_WALLET
1023
1024
    if (pcmd->reqWallet && !pwalletMain)
        throw JSONRPCError(RPC_METHOD_NOT_FOUND, "Method not found (disabled)");
1025
#endif
1026

1027
1028
    // Observe safe mode
    string strWarning = GetWarnings("rpc");
1029
    if (strWarning != "" && !GetBoolArg("-disablesafemode", false) &&
1030
        !pcmd->okSafeMode)
1031
        throw JSONRPCError(RPC_FORBIDDEN_BY_SAFE_MODE, string("Safe mode: ") + strWarning);
1032
1033
1034
1035
1036
1037

    try
    {
        // Execute
        Value result;
        {
1038
            if (pcmd->threadSafe)
1039
                result = pcmd->actor(params, false);
1040
#ifdef ENABLE_WALLET
1041
1042
1043
1044
            else if (!pwalletMain) {
                LOCK(cs_main);
                result = pcmd->actor(params, false);
            } else {
UdjinM6's avatar
UdjinM6 committed
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
                while (true) {
                    TRY_LOCK(cs_main, lockMain);
                    if(!lockMain) { MilliSleep(50); continue; }
                    while (true) {
                        TRY_LOCK(pwalletMain->cs_wallet, lockWallet);
                        if(!lockMain) { MilliSleep(50); continue; }
                        result = pcmd->actor(params, false);
                        break;
                    }
                    break;
                }
1056
            }
1057
1058
1059
1060
1061
1062
#else // ENABLE_WALLET
            else {
                LOCK(cs_main);
                result = pcmd->actor(params, false);
            }
#endif // !ENABLE_WALLET
1063
1064
1065
1066
1067
        }
        return result;
    }
    catch (std::exception& e)
    {
1068
        throw JSONRPCError(RPC_MISC_ERROR, e.what());
1069
1070
    }
}
1071

1072
std::string HelpExampleCli(string methodname, string args){
infernoman's avatar
infernoman committed
1073
    return "> crown-cli " + methodname + " " + args + "\n";
1074
1075
1076
1077
}

std::string HelpExampleRpc(string methodname, string args){
    return "> curl --user myusername --data-binary '{\"jsonrpc\": \"1.0\", \"id\":\"curltest\", "
Infernoman's avatar
Infernoman committed
1078
        "\"method\": \"" + methodname + "\", \"params\": [" + args + "] }' -H 'content-type: text/plain;' http://127.0.0.1:9341/\n";
1079
1080
}

1081
const CRPCTable tableRPC;