aboutsummaryrefslogtreecommitdiff
path: root/src/rpc
diff options
context:
space:
mode:
Diffstat (limited to 'src/rpc')
-rw-r--r--src/rpc/blockchain.cpp25
-rw-r--r--src/rpc/client.cpp1
-rw-r--r--src/rpc/mining.cpp9
-rw-r--r--src/rpc/misc.cpp39
-rw-r--r--src/rpc/protocol.h4
-rw-r--r--src/rpc/rawtransaction.cpp83
-rw-r--r--src/rpc/safemode.cpp14
-rw-r--r--src/rpc/safemode.h12
8 files changed, 110 insertions, 77 deletions
diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp
index 06c68ea27c..5593cc9acc 100644
--- a/src/rpc/blockchain.cpp
+++ b/src/rpc/blockchain.cpp
@@ -47,8 +47,6 @@ static std::mutex cs_blockchange;
static std::condition_variable cond_blockchange;
static CUpdatedBlock latestblock;
-extern void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry);
-
/* Calculate the difficulty for a given block index,
* or the block index of the given chain.
*/
@@ -360,17 +358,23 @@ UniValue getdifficulty(const JSONRPCRequest& request)
std::string EntryDescriptionString()
{
return " \"size\" : n, (numeric) virtual transaction size as defined in BIP 141. This is different from actual serialized size for witness transactions as witness data is discounted.\n"
- " \"fee\" : n, (numeric) transaction fee in " + CURRENCY_UNIT + "\n"
- " \"modifiedfee\" : n, (numeric) transaction fee with fee deltas used for mining priority\n"
+ " \"fee\" : n, (numeric) transaction fee in " + CURRENCY_UNIT + " (DEPRECATED)\n"
+ " \"modifiedfee\" : n, (numeric) transaction fee with fee deltas used for mining priority (DEPRECATED)\n"
" \"time\" : n, (numeric) local time transaction entered pool in seconds since 1 Jan 1970 GMT\n"
" \"height\" : n, (numeric) block height when transaction entered pool\n"
" \"descendantcount\" : n, (numeric) number of in-mempool descendant transactions (including this one)\n"
" \"descendantsize\" : n, (numeric) virtual transaction size of in-mempool descendants (including this one)\n"
- " \"descendantfees\" : n, (numeric) modified fees (see above) of in-mempool descendants (including this one)\n"
+ " \"descendantfees\" : n, (numeric) modified fees (see above) of in-mempool descendants (including this one) (DEPRECATED)\n"
" \"ancestorcount\" : n, (numeric) number of in-mempool ancestor transactions (including this one)\n"
" \"ancestorsize\" : n, (numeric) virtual transaction size of in-mempool ancestors (including this one)\n"
- " \"ancestorfees\" : n, (numeric) modified fees (see above) of in-mempool ancestors (including this one)\n"
+ " \"ancestorfees\" : n, (numeric) modified fees (see above) of in-mempool ancestors (including this one) (DEPRECATED)\n"
" \"wtxid\" : hash, (string) hash of serialized transaction, including witness data\n"
+ " \"fees\" : {\n"
+ " \"base\" : n, (numeric) transaction fee in " + CURRENCY_UNIT + "\n"
+ " \"modified\" : n, (numeric) transaction fee with fee deltas used for mining priority in " + CURRENCY_UNIT + "\n"
+ " \"ancestor\" : n, (numeric) modified fees (see above) of in-mempool ancestors (including this one) in " + CURRENCY_UNIT + "\n"
+ " \"descendant\" : n, (numeric) modified fees (see above) of in-mempool descendants (including this one) in " + CURRENCY_UNIT + "\n"
+ " }\n"
" \"depends\" : [ (array) unconfirmed transactions used as inputs for this transaction\n"
" \"transactionid\", (string) parent transaction id\n"
" ... ]\n"
@@ -383,6 +387,13 @@ void entryToJSON(UniValue &info, const CTxMemPoolEntry &e)
{
AssertLockHeld(mempool.cs);
+ UniValue fees(UniValue::VOBJ);
+ fees.pushKV("base", ValueFromAmount(e.GetFee()));
+ fees.pushKV("modified", ValueFromAmount(e.GetModifiedFee()));
+ fees.pushKV("ancestor", ValueFromAmount(e.GetModFeesWithAncestors()));
+ fees.pushKV("descendant", ValueFromAmount(e.GetModFeesWithDescendants()));
+ info.pushKV("fees", fees);
+
info.pushKV("size", (int)e.GetTxSize());
info.pushKV("fee", ValueFromAmount(e.GetFee()));
info.pushKV("modifiedfee", ValueFromAmount(e.GetModifiedFee()));
@@ -929,7 +940,7 @@ UniValue pruneblockchain(const JSONRPCRequest& request)
else if (height > chainHeight)
throw JSONRPCError(RPC_INVALID_PARAMETER, "Blockchain is shorter than the attempted prune height.");
else if (height > chainHeight - MIN_BLOCKS_TO_KEEP) {
- LogPrint(BCLog::RPC, "Attempt to prune blocks close to the tip. Retaining the minimum number of blocks.");
+ LogPrint(BCLog::RPC, "Attempt to prune blocks close to the tip. Retaining the minimum number of blocks.\n");
height = chainHeight - MIN_BLOCKS_TO_KEEP;
}
diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp
index 01f932dbb4..34c41b3b6b 100644
--- a/src/rpc/client.cpp
+++ b/src/rpc/client.cpp
@@ -51,6 +51,7 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "listreceivedbylabel", 0, "minconf" },
{ "listreceivedbylabel", 1, "include_empty" },
{ "listreceivedbylabel", 2, "include_watchonly" },
+ { "getlabeladdress", 1, "force" },
{ "getbalance", 1, "minconf" },
{ "getbalance", 2, "include_watchonly" },
{ "getblockhash", 0, "height" },
diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp
index 06882c0dfd..b4bb78e689 100644
--- a/src/rpc/mining.cpp
+++ b/src/rpc/mining.cpp
@@ -116,7 +116,7 @@ UniValue generateBlocks(std::shared_ptr<CReserveScript> coinbaseScript, int nGen
}
unsigned int nExtraNonce = 0;
UniValue blockHashes(UniValue::VARR);
- while (nHeight < nHeightEnd)
+ while (nHeight < nHeightEnd && !ShutdownRequested())
{
std::unique_ptr<CBlockTemplate> pblocktemplate(BlockAssembler(Params()).CreateNewBlock(coinbaseScript->reserveScript));
if (!pblocktemplate.get())
@@ -235,6 +235,7 @@ UniValue prioritisetransaction(const JSONRPCRequest& request)
"2. dummy (numeric, optional) API-Compatibility for previous API. Must be zero or null.\n"
" DEPRECATED. For forward compatibility use named arguments and omit this parameter.\n"
"3. fee_delta (numeric, required) The fee value (in satoshis) to add (or subtract, if negative).\n"
+ " Note, that this value is not a fee rate. It is a value to modify absolute fee of the TX.\n"
" The fee is not actually paid, only the algorithm for selecting transactions into a block\n"
" considers the transaction as it would have paid a higher (or lower) fee.\n"
"\nResult:\n"
@@ -470,10 +471,10 @@ UniValue getblocktemplate(const JSONRPCRequest& request)
{
checktxtime = std::chrono::steady_clock::now() + std::chrono::minutes(1);
- WaitableLock lock(csBestBlock);
- while (chainActive.Tip()->GetBlockHash() == hashWatchedChain && IsRPCRunning())
+ WaitableLock lock(g_best_block_mutex);
+ while (g_best_block == hashWatchedChain && IsRPCRunning())
{
- if (cvBlockChange.wait_until(lock, checktxtime) == std::cv_status::timeout)
+ if (g_best_block_cv.wait_until(lock, checktxtime) == std::cv_status::timeout)
{
// Timeout: Check transactions for update
if (mempool.GetTransactionsUpdated() != nTransactionsUpdatedLastLP)
diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp
index 49e865a64a..0c93108bce 100644
--- a/src/rpc/misc.cpp
+++ b/src/rpc/misc.cpp
@@ -69,7 +69,7 @@ UniValue validateaddress(const JSONRPCRequest& request)
{
#ifdef ENABLE_WALLET
- if (!::vpwallets.empty() && IsDeprecatedRPCEnabled("validateaddress")) {
+ if (HasWallets() && IsDeprecatedRPCEnabled("validateaddress")) {
ret.pushKVs(getaddressinfo(request));
}
#endif
@@ -346,21 +346,22 @@ UniValue getmemoryinfo(const JSONRPCRequest& request)
}
}
-uint32_t getCategoryMask(UniValue cats) {
+void EnableOrDisableLogCategories(UniValue cats, bool enable) {
cats = cats.get_array();
- uint32_t mask = 0;
for (unsigned int i = 0; i < cats.size(); ++i) {
- uint32_t flag = 0;
std::string cat = cats[i].get_str();
- if (!GetLogCategory(&flag, &cat)) {
- throw JSONRPCError(RPC_INVALID_PARAMETER, "unknown logging category " + cat);
+
+ bool success;
+ if (enable) {
+ success = g_logger->EnableCategory(cat);
+ } else {
+ success = g_logger->DisableCategory(cat);
}
- if (flag == BCLog::NONE) {
- return 0;
+
+ if (!success) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "unknown logging category " + cat);
}
- mask |= flag;
}
- return mask;
}
UniValue logging(const JSONRPCRequest& request)
@@ -399,25 +400,25 @@ UniValue logging(const JSONRPCRequest& request)
);
}
- uint32_t originalLogCategories = logCategories;
+ uint32_t original_log_categories = g_logger->GetCategoryMask();
if (request.params[0].isArray()) {
- logCategories |= getCategoryMask(request.params[0]);
+ EnableOrDisableLogCategories(request.params[0], true);
}
-
if (request.params[1].isArray()) {
- logCategories &= ~getCategoryMask(request.params[1]);
+ EnableOrDisableLogCategories(request.params[1], false);
}
+ uint32_t updated_log_categories = g_logger->GetCategoryMask();
+ uint32_t changed_log_categories = original_log_categories ^ updated_log_categories;
// Update libevent logging if BCLog::LIBEVENT has changed.
// If the library version doesn't allow it, UpdateHTTPServerLogging() returns false,
// in which case we should clear the BCLog::LIBEVENT flag.
// Throw an error if the user has explicitly asked to change only the libevent
// flag and it failed.
- uint32_t changedLogCategories = originalLogCategories ^ logCategories;
- if (changedLogCategories & BCLog::LIBEVENT) {
- if (!UpdateHTTPServerLogging(logCategories & BCLog::LIBEVENT)) {
- logCategories &= ~BCLog::LIBEVENT;
- if (changedLogCategories == BCLog::LIBEVENT) {
+ if (changed_log_categories & BCLog::LIBEVENT) {
+ if (!UpdateHTTPServerLogging(g_logger->WillLogCategory(BCLog::LIBEVENT))) {
+ g_logger->DisableCategory(BCLog::LIBEVENT);
+ if (changed_log_categories == BCLog::LIBEVENT) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "libevent logging cannot be updated when using libevent before v2.1.1.");
}
}
diff --git a/src/rpc/protocol.h b/src/rpc/protocol.h
index 4a265735d2..6954aed252 100644
--- a/src/rpc/protocol.h
+++ b/src/rpc/protocol.h
@@ -46,7 +46,6 @@ enum RPCErrorCode
//! General application defined errors
RPC_MISC_ERROR = -1, //!< std::exception thrown in command handling
- RPC_FORBIDDEN_BY_SAFE_MODE = -2, //!< Server is in safe mode, and command is not allowed in safe mode
RPC_TYPE_ERROR = -3, //!< Unexpected type was passed as parameter
RPC_INVALID_ADDRESS_OR_KEY = -5, //!< Invalid address or key
RPC_OUT_OF_MEMORY = -7, //!< Ran out of memory during operation
@@ -88,6 +87,9 @@ enum RPCErrorCode
//! Backwards compatible aliases
RPC_WALLET_INVALID_ACCOUNT_NAME = RPC_WALLET_INVALID_LABEL_NAME,
+
+ //! Unused reserved codes, kept around for backwards compatibility. Do not reuse.
+ RPC_FORBIDDEN_BY_SAFE_MODE = -2, //!< Server is in safe mode, and command is not allowed in safe mode
};
UniValue JSONRPCRequestObj(const std::string& strMethod, const UniValue& params, const UniValue& id);
diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp
index 9d7aa58894..102ff3b443 100644
--- a/src/rpc/rawtransaction.cpp
+++ b/src/rpc/rawtransaction.cpp
@@ -7,6 +7,7 @@
#include <coins.h>
#include <consensus/validation.h>
#include <core_io.h>
+#include <index/txindex.h>
#include <init.h>
#include <keystore.h>
#include <validation.h>
@@ -18,7 +19,6 @@
#include <policy/rbf.h>
#include <primitives/transaction.h>
#include <rpc/rawtransaction.h>
-#include <rpc/safemode.h>
#include <rpc/server.h>
#include <script/script.h>
#include <script/script_error.h>
@@ -47,6 +47,8 @@ void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry)
TxToUniv(tx, uint256(), entry, true, RPCSerializationFlags());
if (!hashBlock.IsNull()) {
+ LOCK(cs_main);
+
entry.pushKV("blockhash", hashBlock.GetHex());
CBlockIndex* pindex = LookupBlockIndex(hashBlock);
if (pindex) {
@@ -94,6 +96,7 @@ UniValue getrawtransaction(const JSONRPCRequest& request)
" \"hash\" : \"id\", (string) The transaction hash (differs from txid for witness transactions)\n"
" \"size\" : n, (numeric) The serialized transaction size\n"
" \"vsize\" : n, (numeric) The virtual transaction size (differs from size for witness transactions)\n"
+ " \"weight\" : n, (numeric) The transaction's weight (between vsize*4-3 and vsize*4)\n"
" \"version\" : n, (numeric) The version\n"
" \"locktime\" : ttt, (numeric) The lock time\n"
" \"vin\" : [ (array of json objects)\n"
@@ -140,8 +143,6 @@ UniValue getrawtransaction(const JSONRPCRequest& request)
+ HelpExampleCli("getrawtransaction", "\"mytxid\" true \"myblockhash\"")
);
- LOCK(cs_main);
-
bool in_active_chain = true;
uint256 hash = ParseHashV(request.params[0], "parameter 1");
CBlockIndex* blockindex = nullptr;
@@ -158,6 +159,8 @@ UniValue getrawtransaction(const JSONRPCRequest& request)
}
if (!request.params[2].isNull()) {
+ LOCK(cs_main);
+
uint256 blockhash = ParseHashV(request.params[2], "parameter 3");
blockindex = LookupBlockIndex(blockhash);
if (!blockindex) {
@@ -166,6 +169,11 @@ UniValue getrawtransaction(const JSONRPCRequest& request)
in_active_chain = chainActive.Contains(blockindex);
}
+ bool f_txindex_ready = false;
+ if (g_txindex && !blockindex) {
+ f_txindex_ready = g_txindex->BlockUntilSyncedToCurrentChain();
+ }
+
CTransactionRef tx;
uint256 hash_block;
if (!GetTransaction(hash, tx, Params().GetConsensus(), hash_block, true, blockindex)) {
@@ -175,10 +183,12 @@ UniValue getrawtransaction(const JSONRPCRequest& request)
throw JSONRPCError(RPC_MISC_ERROR, "Block not available");
}
errmsg = "No such transaction found in the provided block";
+ } else if (!g_txindex) {
+ errmsg = "No such mempool transaction. Use -txindex to enable blockchain transaction queries";
+ } else if (!f_txindex_ready) {
+ errmsg = "No such mempool transaction. Blockchain transactions are still in the process of being indexed";
} else {
- errmsg = fTxIndex
- ? "No such mempool or blockchain transaction"
- : "No such mempool transaction. Use -txindex to enable blockchain transaction queries";
+ errmsg = "No such mempool or blockchain transaction";
}
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, errmsg + ". Use gettransaction for wallet transactions.");
}
@@ -228,19 +238,18 @@ UniValue gettxoutproof(const JSONRPCRequest& request)
oneTxid = hash;
}
- LOCK(cs_main);
-
CBlockIndex* pblockindex = nullptr;
-
uint256 hashBlock;
- if (!request.params[1].isNull())
- {
+ if (!request.params[1].isNull()) {
+ LOCK(cs_main);
hashBlock = uint256S(request.params[1].get_str());
pblockindex = LookupBlockIndex(hashBlock);
if (!pblockindex) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
}
} else {
+ LOCK(cs_main);
+
// Loop through txids and try to find which block they're in. Exit loop once a block is found.
for (const auto& tx : setTxids) {
const Coin& coin = AccessByTxid(*pcoinsTip, tx);
@@ -251,6 +260,14 @@ UniValue gettxoutproof(const JSONRPCRequest& request)
}
}
+
+ // Allow txindex to catch up if we need to query it and before we acquire cs_main.
+ if (g_txindex && !pblockindex) {
+ g_txindex->BlockUntilSyncedToCurrentChain();
+ }
+
+ LOCK(cs_main);
+
if (pblockindex == nullptr)
{
CTransactionRef tx;
@@ -494,6 +511,7 @@ UniValue decoderawtransaction(const JSONRPCRequest& request)
" \"hash\" : \"id\", (string) The transaction hash (differs from txid for witness transactions)\n"
" \"size\" : n, (numeric) The transaction size\n"
" \"vsize\" : n, (numeric) The virtual transaction size (differs from size for witness transactions)\n"
+ " \"weight\" : n, (numeric) The transaction's weight (between vsize*4 - 3 and vsize*4)\n"
" \"version\" : n, (numeric) The version\n"
" \"locktime\" : ttt, (numeric) The lock time\n"
" \"vin\" : [ (array of json objects)\n"
@@ -595,6 +613,38 @@ UniValue decodescript(const JSONRPCRequest& request)
// P2SH cannot be wrapped in a P2SH. If this script is already a P2SH,
// don't return the address for a P2SH of the P2SH.
r.pushKV("p2sh", EncodeDestination(CScriptID(script)));
+ // P2SH and witness programs cannot be wrapped in P2WSH, if this script
+ // is a witness program, don't return addresses for a segwit programs.
+ if (type.get_str() == "pubkey" || type.get_str() == "pubkeyhash" || type.get_str() == "multisig" || type.get_str() == "nonstandard") {
+ txnouttype which_type;
+ std::vector<std::vector<unsigned char>> solutions_data;
+ Solver(script, which_type, solutions_data);
+ // Uncompressed pubkeys cannot be used with segwit checksigs.
+ // If the script contains an uncompressed pubkey, skip encoding of a segwit program.
+ if ((which_type == TX_PUBKEY) || (which_type == TX_MULTISIG)) {
+ for (const auto& solution : solutions_data) {
+ if ((solution.size() != 1) && !CPubKey(solution).IsCompressed()) {
+ return r;
+ }
+ }
+ }
+ UniValue sr(UniValue::VOBJ);
+ CScript segwitScr;
+ if (which_type == TX_PUBKEY) {
+ segwitScr = GetScriptForDestination(WitnessV0KeyHash(Hash160(solutions_data[0].begin(), solutions_data[0].end())));
+ } else if (which_type == TX_PUBKEYHASH) {
+ segwitScr = GetScriptForDestination(WitnessV0KeyHash(solutions_data[0]));
+ } else {
+ // Scripts that are not fit for P2WPKH are encoded as P2WSH.
+ // Newer segwit program versions should be considered when then become available.
+ uint256 scriptHash;
+ CSHA256().Write(script.data(), script.size()).Finalize(scriptHash.begin());
+ segwitScr = GetScriptForDestination(WitnessV0ScriptHash(scriptHash));
+ }
+ ScriptPubKeyToUniv(segwitScr, sr, true);
+ sr.pushKV("p2sh-segwit", EncodeDestination(CScriptID(segwitScr)));
+ r.pushKV("segwit", sr);
+ }
}
return r;
@@ -774,9 +824,6 @@ UniValue SignTransaction(CMutableTransaction& mtx, const UniValue& prevTxsUnival
if (is_temp_keystore && (scriptPubKey.IsPayToScriptHash() || scriptPubKey.IsPayToWitnessScriptHash())) {
RPCTypeCheckObj(prevOut,
{
- {"txid", UniValueType(UniValue::VSTR)},
- {"vout", UniValueType(UniValue::VNUM)},
- {"scriptPubKey", UniValueType(UniValue::VSTR)},
{"redeemScript", UniValueType(UniValue::VSTR)},
});
UniValue v = find_value(prevOut, "redeemScript");
@@ -831,7 +878,7 @@ UniValue SignTransaction(CMutableTransaction& mtx, const UniValue& prevTxsUnival
SignatureData sigdata;
// Only sign SIGHASH_SINGLE if there's a corresponding output:
if (!fHashSingle || (i < mtx.vout.size())) {
- ProduceSignature(MutableTransactionSignatureCreator(keystore, &mtx, i, amount, nHashType), prevPubKey, sigdata);
+ ProduceSignature(*keystore, MutableTransactionSignatureCreator(&mtx, i, amount, nHashType), prevPubKey, sigdata);
}
sigdata = CombineSignatures(prevPubKey, TransactionSignatureChecker(&txConst, i, amount), sigdata, DataFromTransaction(mtx, i));
@@ -1060,8 +1107,6 @@ UniValue sendrawtransaction(const JSONRPCRequest& request)
+ HelpExampleRpc("sendrawtransaction", "\"signedhex\"")
);
- ObserveSafeMode();
-
std::promise<void> promise;
RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VBOOL});
@@ -1169,8 +1214,6 @@ UniValue testmempoolaccept(const JSONRPCRequest& request)
);
}
- ObserveSafeMode();
-
RPCTypeCheck(request.params, {UniValue::VARR, UniValue::VBOOL});
if (request.params[0].get_array().size() != 1) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Array must contain exactly one raw transaction for now");
@@ -1198,7 +1241,7 @@ UniValue testmempoolaccept(const JSONRPCRequest& request)
{
LOCK(cs_main);
test_accept_res = AcceptToMemoryPool(mempool, state, std::move(tx), &missing_inputs,
- nullptr /* plTxnReplaced */, false /* bypass_limits */, max_raw_tx_fee, /* test_accpet */ true);
+ nullptr /* plTxnReplaced */, false /* bypass_limits */, max_raw_tx_fee, /* test_accept */ true);
}
result_0.pushKV("allowed", test_accept_res);
if (!test_accept_res) {
diff --git a/src/rpc/safemode.cpp b/src/rpc/safemode.cpp
deleted file mode 100644
index 9f3a9d30b8..0000000000
--- a/src/rpc/safemode.cpp
+++ /dev/null
@@ -1,14 +0,0 @@
-#include <rpc/safemode.h>
-
-#include <rpc/protocol.h>
-#include <util.h>
-#include <warnings.h>
-
-void ObserveSafeMode()
-{
- std::string warning = GetWarnings("rpc");
- if (warning != "" && !gArgs.GetBoolArg("-disablesafemode", DEFAULT_DISABLE_SAFEMODE)) {
- throw JSONRPCError(RPC_FORBIDDEN_BY_SAFE_MODE, std::string("Safe mode: ") + warning);
- }
-}
-
diff --git a/src/rpc/safemode.h b/src/rpc/safemode.h
deleted file mode 100644
index 8466d6b2f9..0000000000
--- a/src/rpc/safemode.h
+++ /dev/null
@@ -1,12 +0,0 @@
-// Copyright (c) 2017 The Bitcoin Core developers
-// Distributed under the MIT software license, see the accompanying
-// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-
-#ifndef BITCOIN_RPC_SAFEMODE_H
-#define BITCOIN_RPC_SAFEMODE_H
-
-static const bool DEFAULT_DISABLE_SAFEMODE = true;
-
-void ObserveSafeMode();
-
-#endif // BITCOIN_RPC_SAFEMODE_H