Changeset View
Changeset View
Standalone View
Standalone View
src/rpc/server.cpp
Show First 20 Lines • Show All 57 Lines • ▼ Show 20 Lines | void RPCServer::OnPostCommand(std::function<void(const CRPCCommand &)> slot) { | ||||
g_rpcSignals.PostCommand.connect(boost::bind(slot, _1)); | g_rpcSignals.PostCommand.connect(boost::bind(slot, _1)); | ||||
} | } | ||||
void RPCTypeCheck(const UniValue ¶ms, | void RPCTypeCheck(const UniValue ¶ms, | ||||
const std::list<UniValue::VType> &typesExpected, | const std::list<UniValue::VType> &typesExpected, | ||||
bool fAllowNull) { | bool fAllowNull) { | ||||
unsigned int i = 0; | unsigned int i = 0; | ||||
for (UniValue::VType t : typesExpected) { | for (UniValue::VType t : typesExpected) { | ||||
if (params.size() <= i) break; | if (params.size() <= i) { | ||||
break; | |||||
} | |||||
const UniValue &v = params[i]; | const UniValue &v = params[i]; | ||||
if (!(fAllowNull && v.isNull())) { | if (!(fAllowNull && v.isNull())) { | ||||
RPCTypeCheckArgument(v, t); | RPCTypeCheckArgument(v, t); | ||||
} | } | ||||
i++; | i++; | ||||
} | } | ||||
} | } | ||||
void RPCTypeCheckArgument(const UniValue &value, UniValue::VType typeExpected) { | void RPCTypeCheckArgument(const UniValue &value, UniValue::VType typeExpected) { | ||||
if (value.type() != typeExpected) { | if (value.type() != typeExpected) { | ||||
throw JSONRPCError(RPC_TYPE_ERROR, strprintf("Expected type %s, got %s", | throw JSONRPCError(RPC_TYPE_ERROR, strprintf("Expected type %s, got %s", | ||||
uvTypeName(typeExpected), | uvTypeName(typeExpected), | ||||
uvTypeName(value.type()))); | uvTypeName(value.type()))); | ||||
} | } | ||||
} | } | ||||
void RPCTypeCheckObj(const UniValue &o, | void RPCTypeCheckObj(const UniValue &o, | ||||
const std::map<std::string, UniValueType> &typesExpected, | const std::map<std::string, UniValueType> &typesExpected, | ||||
bool fAllowNull, bool fStrict) { | bool fAllowNull, bool fStrict) { | ||||
for (const auto &t : typesExpected) { | for (const auto &t : typesExpected) { | ||||
const UniValue &v = find_value(o, t.first); | const UniValue &v = find_value(o, t.first); | ||||
if (!fAllowNull && v.isNull()) | if (!fAllowNull && v.isNull()) { | ||||
throw JSONRPCError(RPC_TYPE_ERROR, | throw JSONRPCError(RPC_TYPE_ERROR, | ||||
strprintf("Missing %s", t.first)); | strprintf("Missing %s", t.first)); | ||||
} | |||||
if (!(t.second.typeAny || v.type() == t.second.type || | if (!(t.second.typeAny || v.type() == t.second.type || | ||||
(fAllowNull && v.isNull()))) { | (fAllowNull && v.isNull()))) { | ||||
std::string err = strprintf("Expected type %s for %s, got %s", | std::string err = strprintf("Expected type %s for %s, got %s", | ||||
uvTypeName(t.second.type), t.first, | uvTypeName(t.second.type), t.first, | ||||
uvTypeName(v.type())); | uvTypeName(v.type())); | ||||
throw JSONRPCError(RPC_TYPE_ERROR, err); | throw JSONRPCError(RPC_TYPE_ERROR, err); | ||||
} | } | ||||
} | } | ||||
if (fStrict) { | if (fStrict) { | ||||
for (const std::string &k : o.getKeys()) { | for (const std::string &k : o.getKeys()) { | ||||
if (typesExpected.count(k) == 0) { | if (typesExpected.count(k) == 0) { | ||||
std::string err = strprintf("Unexpected key %s", k); | std::string err = strprintf("Unexpected key %s", k); | ||||
throw JSONRPCError(RPC_TYPE_ERROR, err); | throw JSONRPCError(RPC_TYPE_ERROR, err); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
} | } | ||||
Amount AmountFromValue(const UniValue &value) { | Amount AmountFromValue(const UniValue &value) { | ||||
if (!value.isNum() && !value.isStr()) | if (!value.isNum() && !value.isStr()) { | ||||
throw JSONRPCError(RPC_TYPE_ERROR, "Amount is not a number or string"); | throw JSONRPCError(RPC_TYPE_ERROR, "Amount is not a number or string"); | ||||
} | |||||
int64_t n; | int64_t n; | ||||
if (!ParseFixedPoint(value.getValStr(), 8, &n)) | if (!ParseFixedPoint(value.getValStr(), 8, &n)) { | ||||
throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount"); | throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount"); | ||||
} | |||||
Amount amt(n); | Amount amt(n); | ||||
if (!MoneyRange(amt)) | if (!MoneyRange(amt)) { | ||||
throw JSONRPCError(RPC_TYPE_ERROR, "Amount out of range"); | throw JSONRPCError(RPC_TYPE_ERROR, "Amount out of range"); | ||||
} | |||||
return amt; | return amt; | ||||
} | } | ||||
UniValue ValueFromAmount(const Amount &amount) { | UniValue ValueFromAmount(const Amount amount) { | ||||
int64_t amt = amount.GetSatoshis(); | int64_t amt = amount.GetSatoshis(); | ||||
bool sign = amt < 0; | bool sign = amt < 0; | ||||
int64_t n_abs = (sign ? -amt : amt); | int64_t n_abs = (sign ? -amt : amt); | ||||
int64_t quotient = n_abs / COIN.GetSatoshis(); | int64_t quotient = n_abs / COIN.GetSatoshis(); | ||||
int64_t remainder = n_abs % COIN.GetSatoshis(); | int64_t remainder = n_abs % COIN.GetSatoshis(); | ||||
return UniValue(UniValue::VNUM, strprintf("%s%d.%08d", sign ? "-" : "", | return UniValue(UniValue::VNUM, strprintf("%s%d.%08d", sign ? "-" : "", | ||||
quotient, remainder)); | quotient, remainder)); | ||||
} | } | ||||
uint256 ParseHashV(const UniValue &v, std::string strName) { | uint256 ParseHashV(const UniValue &v, std::string strName) { | ||||
std::string strHex; | std::string strHex; | ||||
if (v.isStr()) strHex = v.get_str(); | if (v.isStr()) { | ||||
strHex = v.get_str(); | |||||
} | |||||
// Note: IsHex("") is false | // Note: IsHex("") is false | ||||
if (!IsHex(strHex)) | if (!IsHex(strHex)) { | ||||
throw JSONRPCError(RPC_INVALID_PARAMETER, | throw JSONRPCError(RPC_INVALID_PARAMETER, | ||||
strName + " must be hexadecimal string (not '" + | strName + " must be hexadecimal string (not '" + | ||||
strHex + "')"); | strHex + "')"); | ||||
if (64 != strHex.length()) | } | ||||
if (64 != strHex.length()) { | |||||
throw JSONRPCError(RPC_INVALID_PARAMETER, | throw JSONRPCError(RPC_INVALID_PARAMETER, | ||||
strprintf("%s must be of length %d (not %d)", | strprintf("%s must be of length %d (not %d)", | ||||
strName, 64, strHex.length())); | strName, 64, strHex.length())); | ||||
} | |||||
uint256 result; | uint256 result; | ||||
result.SetHex(strHex); | result.SetHex(strHex); | ||||
return result; | return result; | ||||
} | } | ||||
uint256 ParseHashO(const UniValue &o, std::string strKey) { | uint256 ParseHashO(const UniValue &o, std::string strKey) { | ||||
return ParseHashV(find_value(o, strKey), strKey); | return ParseHashV(find_value(o, strKey), strKey); | ||||
} | } | ||||
std::vector<uint8_t> ParseHexV(const UniValue &v, std::string strName) { | std::vector<uint8_t> ParseHexV(const UniValue &v, std::string strName) { | ||||
std::string strHex; | std::string strHex; | ||||
if (v.isStr()) strHex = v.get_str(); | if (v.isStr()) { | ||||
if (!IsHex(strHex)) | strHex = v.get_str(); | ||||
} | |||||
if (!IsHex(strHex)) { | |||||
throw JSONRPCError(RPC_INVALID_PARAMETER, | throw JSONRPCError(RPC_INVALID_PARAMETER, | ||||
strName + " must be hexadecimal string (not '" + | strName + " must be hexadecimal string (not '" + | ||||
strHex + "')"); | strHex + "')"); | ||||
} | |||||
return ParseHex(strHex); | return ParseHex(strHex); | ||||
} | } | ||||
std::vector<uint8_t> ParseHexO(const UniValue &o, std::string strKey) { | std::vector<uint8_t> ParseHexO(const UniValue &o, std::string strKey) { | ||||
return ParseHexV(find_value(o, strKey), strKey); | return ParseHexV(find_value(o, strKey), strKey); | ||||
} | } | ||||
/** | /** | ||||
* Note: This interface may still be subject to change. | * Note: This interface may still be subject to change. | ||||
*/ | */ | ||||
std::string CRPCTable::help(Config &config, const std::string &strCommand, | std::string CRPCTable::help(Config &config, const std::string &strCommand, | ||||
const JSONRPCRequest &helpreq) const { | const JSONRPCRequest &helpreq) const { | ||||
std::string strRet; | std::string strRet; | ||||
std::string category; | std::string category; | ||||
std::set<const CRPCCommand *> setDone; | std::set<const CRPCCommand *> setDone; | ||||
std::vector<std::pair<std::string, const CRPCCommand *>> vCommands; | std::vector<std::pair<std::string, const CRPCCommand *>> vCommands; | ||||
for (std::map<std::string, const CRPCCommand *>::const_iterator mi = | for (std::map<std::string, const CRPCCommand *>::const_iterator mi = | ||||
mapCommands.begin(); | mapCommands.begin(); | ||||
mi != mapCommands.end(); ++mi) | mi != mapCommands.end(); ++mi) { | ||||
vCommands.push_back( | vCommands.push_back( | ||||
std::make_pair(mi->second->category + mi->first, mi->second)); | std::make_pair(mi->second->category + mi->first, mi->second)); | ||||
} | |||||
sort(vCommands.begin(), vCommands.end()); | sort(vCommands.begin(), vCommands.end()); | ||||
JSONRPCRequest jreq(helpreq); | JSONRPCRequest jreq(helpreq); | ||||
jreq.fHelp = true; | jreq.fHelp = true; | ||||
jreq.params = UniValue(); | jreq.params = UniValue(); | ||||
for (const std::pair<std::string, const CRPCCommand *> &command : | for (const std::pair<std::string, const CRPCCommand *> &command : | ||||
vCommands) { | vCommands) { | ||||
Show All 15 Lines | for (const std::pair<std::string, const CRPCCommand *> &command : | ||||
jreq.fHelp = true; | jreq.fHelp = true; | ||||
if (setDone.insert(pcmd).second) { | if (setDone.insert(pcmd).second) { | ||||
pcmd->call(config, jreq); | pcmd->call(config, jreq); | ||||
} | } | ||||
} catch (const std::exception &e) { | } catch (const std::exception &e) { | ||||
// Help text is returned in an exception | // Help text is returned in an exception | ||||
std::string strHelp = std::string(e.what()); | std::string strHelp = std::string(e.what()); | ||||
if (strCommand == "") { | if (strCommand == "") { | ||||
if (strHelp.find('\n') != std::string::npos) | if (strHelp.find('\n') != std::string::npos) { | ||||
strHelp = strHelp.substr(0, strHelp.find('\n')); | strHelp = strHelp.substr(0, strHelp.find('\n')); | ||||
} | |||||
if (category != pcmd->category) { | if (category != pcmd->category) { | ||||
if (!category.empty()) strRet += "\n"; | if (!category.empty()) { | ||||
strRet += "\n"; | |||||
} | |||||
category = pcmd->category; | category = pcmd->category; | ||||
std::string firstLetter = category.substr(0, 1); | std::string firstLetter = category.substr(0, 1); | ||||
boost::to_upper(firstLetter); | boost::to_upper(firstLetter); | ||||
strRet += | strRet += | ||||
"== " + firstLetter + category.substr(1) + " ==\n"; | "== " + firstLetter + category.substr(1) + " ==\n"; | ||||
} | } | ||||
} | } | ||||
strRet += strHelp + "\n"; | strRet += strHelp + "\n"; | ||||
} | } | ||||
} | } | ||||
if (strRet == "") | if (strRet == "") { | ||||
strRet = strprintf("help: unknown command: %s\n", strCommand); | strRet = strprintf("help: unknown command: %s\n", strCommand); | ||||
} | |||||
strRet = strRet.substr(0, strRet.size() - 1); | strRet = strRet.substr(0, strRet.size() - 1); | ||||
return strRet; | return strRet; | ||||
} | } | ||||
static UniValue help(Config &config, const JSONRPCRequest &jsonRequest) { | static UniValue help(Config &config, const JSONRPCRequest &jsonRequest) { | ||||
if (jsonRequest.fHelp || jsonRequest.params.size() > 1) | if (jsonRequest.fHelp || jsonRequest.params.size() > 1) { | ||||
throw std::runtime_error( | throw std::runtime_error( | ||||
"help ( \"command\" )\n" | "help ( \"command\" )\n" | ||||
"\nList all commands, or get help for a specified command.\n" | "\nList all commands, or get help for a specified command.\n" | ||||
"\nArguments:\n" | "\nArguments:\n" | ||||
"1. \"command\" (string, optional) The command to get help on\n" | "1. \"command\" (string, optional) The command to get help on\n" | ||||
"\nResult:\n" | "\nResult:\n" | ||||
"\"text\" (string) The help text\n"); | "\"text\" (string) The help text\n"); | ||||
} | |||||
std::string strCommand; | std::string strCommand; | ||||
if (jsonRequest.params.size() > 0) { | if (jsonRequest.params.size() > 0) { | ||||
strCommand = jsonRequest.params[0].get_str(); | strCommand = jsonRequest.params[0].get_str(); | ||||
} | } | ||||
return tableRPC.help(config, strCommand, jsonRequest); | return tableRPC.help(config, strCommand, jsonRequest); | ||||
} | } | ||||
static UniValue stop(const Config &config, const JSONRPCRequest &jsonRequest) { | static UniValue stop(const Config &config, const JSONRPCRequest &jsonRequest) { | ||||
// Accept the deprecated and ignored 'detach' boolean argument | // Accept the deprecated and ignored 'detach' boolean argument | ||||
if (jsonRequest.fHelp || jsonRequest.params.size() > 1) | if (jsonRequest.fHelp || jsonRequest.params.size() > 1) { | ||||
throw std::runtime_error("stop\n" | throw std::runtime_error("stop\n" | ||||
"\nStop Bitcoin server."); | "\nStop Bitcoin server."); | ||||
} | |||||
// Event loop will exit after current HTTP requests have been handled, so | // Event loop will exit after current HTTP requests have been handled, so | ||||
// this reply will get back to the client. | // this reply will get back to the client. | ||||
StartShutdown(); | StartShutdown(); | ||||
return "Bitcoin server stopping"; | return "Bitcoin server stopping"; | ||||
} | } | ||||
static UniValue uptime(const Config &config, | static UniValue uptime(const Config &config, | ||||
const JSONRPCRequest &jsonRequest) { | const JSONRPCRequest &jsonRequest) { | ||||
Show All 34 Lines | for (vcidx = 0; vcidx < (sizeof(vRPCCommands) / sizeof(vRPCCommands[0])); | ||||
pcmd = &vRPCCommands[vcidx]; | pcmd = &vRPCCommands[vcidx]; | ||||
mapCommands[pcmd->name] = pcmd; | mapCommands[pcmd->name] = pcmd; | ||||
} | } | ||||
} | } | ||||
const CRPCCommand *CRPCTable::operator[](const std::string &name) const { | const CRPCCommand *CRPCTable::operator[](const std::string &name) const { | ||||
std::map<std::string, const CRPCCommand *>::const_iterator it = | std::map<std::string, const CRPCCommand *>::const_iterator it = | ||||
mapCommands.find(name); | mapCommands.find(name); | ||||
if (it == mapCommands.end()) return nullptr; | if (it == mapCommands.end()) { | ||||
return nullptr; | |||||
} | |||||
return (*it).second; | return (*it).second; | ||||
} | } | ||||
bool CRPCTable::appendCommand(const std::string &name, | bool CRPCTable::appendCommand(const std::string &name, | ||||
const CRPCCommand *pcmd) { | const CRPCCommand *pcmd) { | ||||
if (IsRPCRunning()) return false; | if (IsRPCRunning()) { | ||||
return false; | |||||
} | |||||
// don't allow overwriting for now | // don't allow overwriting for now | ||||
std::map<std::string, const CRPCCommand *>::const_iterator it = | std::map<std::string, const CRPCCommand *>::const_iterator it = | ||||
mapCommands.find(name); | mapCommands.find(name); | ||||
if (it != mapCommands.end()) return false; | if (it != mapCommands.end()) { | ||||
return false; | |||||
} | |||||
mapCommands[name] = pcmd; | mapCommands[name] = pcmd; | ||||
return true; | return true; | ||||
} | } | ||||
bool StartRPC() { | bool StartRPC() { | ||||
LogPrint(BCLog::RPC, "Starting RPC\n"); | LogPrint(BCLog::RPC, "Starting RPC\n"); | ||||
fRPCRunning = true; | fRPCRunning = true; | ||||
Show All 26 Lines | |||||
void SetRPCWarmupFinished() { | void SetRPCWarmupFinished() { | ||||
LOCK(cs_rpcWarmup); | LOCK(cs_rpcWarmup); | ||||
assert(fRPCInWarmup); | assert(fRPCInWarmup); | ||||
fRPCInWarmup = false; | fRPCInWarmup = false; | ||||
} | } | ||||
bool RPCIsInWarmup(std::string *outStatus) { | bool RPCIsInWarmup(std::string *outStatus) { | ||||
LOCK(cs_rpcWarmup); | LOCK(cs_rpcWarmup); | ||||
if (outStatus) *outStatus = rpcWarmupStatus; | if (outStatus) { | ||||
*outStatus = rpcWarmupStatus; | |||||
} | |||||
return fRPCInWarmup; | return fRPCInWarmup; | ||||
} | } | ||||
void JSONRPCRequest::parse(const UniValue &valRequest) { | void JSONRPCRequest::parse(const UniValue &valRequest) { | ||||
// Parse request | // Parse request | ||||
if (!valRequest.isObject()) | if (!valRequest.isObject()) { | ||||
throw JSONRPCError(RPC_INVALID_REQUEST, "Invalid Request object"); | throw JSONRPCError(RPC_INVALID_REQUEST, "Invalid Request object"); | ||||
} | |||||
const UniValue &request = valRequest.get_obj(); | const UniValue &request = valRequest.get_obj(); | ||||
// Parse id now so errors from here on will have the id | // Parse id now so errors from here on will have the id | ||||
id = find_value(request, "id"); | id = find_value(request, "id"); | ||||
// Parse method | // Parse method | ||||
UniValue valMethod = find_value(request, "method"); | UniValue valMethod = find_value(request, "method"); | ||||
if (valMethod.isNull()) | if (valMethod.isNull()) { | ||||
throw JSONRPCError(RPC_INVALID_REQUEST, "Missing method"); | throw JSONRPCError(RPC_INVALID_REQUEST, "Missing method"); | ||||
if (!valMethod.isStr()) | } | ||||
if (!valMethod.isStr()) { | |||||
throw JSONRPCError(RPC_INVALID_REQUEST, "Method must be a string"); | throw JSONRPCError(RPC_INVALID_REQUEST, "Method must be a string"); | ||||
} | |||||
strMethod = valMethod.get_str(); | strMethod = valMethod.get_str(); | ||||
if (strMethod != "getblocktemplate") | if (strMethod != "getblocktemplate") { | ||||
LogPrint(BCLog::RPC, "ThreadRPCServer method=%s\n", | LogPrint(BCLog::RPC, "ThreadRPCServer method=%s\n", | ||||
SanitizeString(strMethod)); | SanitizeString(strMethod)); | ||||
} | |||||
// Parse params | // Parse params | ||||
UniValue valParams = find_value(request, "params"); | UniValue valParams = find_value(request, "params"); | ||||
if (valParams.isArray() || valParams.isObject()) | if (valParams.isArray() || valParams.isObject()) { | ||||
params = valParams; | params = valParams; | ||||
else if (valParams.isNull()) | } else if (valParams.isNull()) { | ||||
params = UniValue(UniValue::VARR); | params = UniValue(UniValue::VARR); | ||||
else | } else { | ||||
throw JSONRPCError(RPC_INVALID_REQUEST, | throw JSONRPCError(RPC_INVALID_REQUEST, | ||||
"Params must be an array or object"); | "Params must be an array or object"); | ||||
} | } | ||||
} | |||||
static UniValue JSONRPCExecOne(Config &config, JSONRPCRequest jreq, | static UniValue JSONRPCExecOne(Config &config, JSONRPCRequest jreq, | ||||
const UniValue &req) { | const UniValue &req) { | ||||
UniValue rpc_result(UniValue::VOBJ); | UniValue rpc_result(UniValue::VOBJ); | ||||
try { | try { | ||||
jreq.parse(req); | jreq.parse(req); | ||||
▲ Show 20 Lines • Show All 63 Lines • ▼ Show 20 Lines | transformNamedArguments(const JSONRPCRequest &in, | ||||
return out; | return out; | ||||
} | } | ||||
UniValue CRPCTable::execute(Config &config, | UniValue CRPCTable::execute(Config &config, | ||||
const JSONRPCRequest &request) const { | const JSONRPCRequest &request) const { | ||||
// Return immediately if in warmup | // Return immediately if in warmup | ||||
{ | { | ||||
LOCK(cs_rpcWarmup); | LOCK(cs_rpcWarmup); | ||||
if (fRPCInWarmup) throw JSONRPCError(RPC_IN_WARMUP, rpcWarmupStatus); | if (fRPCInWarmup) { | ||||
throw JSONRPCError(RPC_IN_WARMUP, rpcWarmupStatus); | |||||
} | |||||
} | } | ||||
// Find method | // Find method | ||||
const CRPCCommand *pcmd = tableRPC[request.strMethod]; | const CRPCCommand *pcmd = tableRPC[request.strMethod]; | ||||
if (!pcmd) throw JSONRPCError(RPC_METHOD_NOT_FOUND, "Method not found"); | if (!pcmd) { | ||||
throw JSONRPCError(RPC_METHOD_NOT_FOUND, "Method not found"); | |||||
} | |||||
g_rpcSignals.PreCommand(*pcmd); | g_rpcSignals.PreCommand(*pcmd); | ||||
try { | try { | ||||
// Execute, convert arguments to array if necessary | // Execute, convert arguments to array if necessary | ||||
if (request.params.isObject()) { | if (request.params.isObject()) { | ||||
return pcmd->call(config, | return pcmd->call(config, | ||||
transformNamedArguments(request, pcmd->argNames)); | transformNamedArguments(request, pcmd->argNames)); | ||||
Show All 27 Lines | std::string HelpExampleRpc(const std::string &methodname, | ||||
return "> curl --user myusername --data-binary '{\"jsonrpc\": \"1.0\", " | return "> curl --user myusername --data-binary '{\"jsonrpc\": \"1.0\", " | ||||
"\"id\":\"curltest\", " | "\"id\":\"curltest\", " | ||||
"\"method\": \"" + | "\"method\": \"" + | ||||
methodname + "\", \"params\": [" + args + | methodname + "\", \"params\": [" + args + | ||||
"] }' -H 'content-type: text/plain;' http://127.0.0.1:8332/\n"; | "] }' -H 'content-type: text/plain;' http://127.0.0.1:8332/\n"; | ||||
} | } | ||||
void RPCSetTimerInterfaceIfUnset(RPCTimerInterface *iface) { | void RPCSetTimerInterfaceIfUnset(RPCTimerInterface *iface) { | ||||
if (!timerInterface) timerInterface = iface; | if (!timerInterface) { | ||||
timerInterface = iface; | |||||
} | |||||
} | } | ||||
void RPCSetTimerInterface(RPCTimerInterface *iface) { | void RPCSetTimerInterface(RPCTimerInterface *iface) { | ||||
timerInterface = iface; | timerInterface = iface; | ||||
} | } | ||||
void RPCUnsetTimerInterface(RPCTimerInterface *iface) { | void RPCUnsetTimerInterface(RPCTimerInterface *iface) { | ||||
if (timerInterface == iface) timerInterface = nullptr; | if (timerInterface == iface) { | ||||
timerInterface = nullptr; | |||||
} | |||||
} | } | ||||
void RPCRunLater(const std::string &name, std::function<void(void)> func, | void RPCRunLater(const std::string &name, std::function<void(void)> func, | ||||
int64_t nSeconds) { | int64_t nSeconds) { | ||||
if (!timerInterface) | if (!timerInterface) { | ||||
throw JSONRPCError(RPC_INTERNAL_ERROR, | throw JSONRPCError(RPC_INTERNAL_ERROR, | ||||
"No timer handler registered for RPC"); | "No timer handler registered for RPC"); | ||||
} | |||||
deadlineTimers.erase(name); | deadlineTimers.erase(name); | ||||
LogPrint(BCLog::RPC, "queue run of timer %s in %i seconds (using %s)\n", | LogPrint(BCLog::RPC, "queue run of timer %s in %i seconds (using %s)\n", | ||||
name, nSeconds, timerInterface->Name()); | name, nSeconds, timerInterface->Name()); | ||||
deadlineTimers.emplace( | deadlineTimers.emplace( | ||||
name, std::unique_ptr<RPCTimerBase>( | name, std::unique_ptr<RPCTimerBase>( | ||||
timerInterface->NewTimer(func, nSeconds * 1000))); | timerInterface->NewTimer(func, nSeconds * 1000))); | ||||
} | } | ||||
int RPCSerializationFlags() { | int RPCSerializationFlags() { | ||||
return 0; | return 0; | ||||
} | } | ||||
CRPCTable tableRPC; | CRPCTable tableRPC; |