Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,9 @@ OSX_PLIST=$(top_builddir)/share/qt/Info.plist #not installed
OSX_QT_TRANSLATIONS = ar,bg,ca,cs,da,de,es,fa,fi,fr,gd,gl,he,hu,it,ja,ko,lt,lv,pl,pt,ru,sk,sl,sv,uk,zh_CN,zh_TW

DIST_CONTRIB = \
$(top_srcdir)/contrib/debian/copyright \
$(top_srcdir)/contrib/linearize/linearize-data.py \
$(top_srcdir)/contrib/linearize/linearize-hashes.py

DIST_SHARE = \
$(top_srcdir)/share/genbuild.sh \
$(top_srcdir)/share/rpcauth
Expand Down
5 changes: 5 additions & 0 deletions doc/release-notes-14982.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
New RPCs
--------

- The RPC `getrpcinfo` returns runtime details of the RPC server. At the moment
it returns the active commands and the corresponding execution time.
7 changes: 4 additions & 3 deletions src/chain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,10 +59,11 @@ const CBlockIndex *CChain::FindFork(const CBlockIndex *pindex) const {
return pindex;
}

CBlockIndex* CChain::FindEarliestAtLeast(int64_t nTime) const
CBlockIndex* CChain::FindEarliestAtLeast(int64_t nTime, int height) const
{
std::vector<CBlockIndex*>::const_iterator lower = std::lower_bound(vChain.begin(), vChain.end(), nTime,
[](CBlockIndex* pBlock, const int64_t& time) -> bool { return pBlock->GetBlockTimeMax() < time; });
std::pair<int64_t, int> blockparams = std::make_pair(nTime, height);
std::vector<CBlockIndex*>::const_iterator lower = std::lower_bound(vChain.begin(), vChain.end(), blockparams,
[](CBlockIndex* pBlock, const std::pair<int64_t, int>& blockparams) -> bool { return pBlock->GetBlockTimeMax() < blockparams.first || pBlock->nHeight < blockparams.second; });
return (lower == vChain.end() ? nullptr : *lower);
}

Expand Down
4 changes: 2 additions & 2 deletions src/chain.h
Original file line number Diff line number Diff line change
Expand Up @@ -430,8 +430,8 @@ class CChain {
/** Find the last common block between this chain and a block index entry. */
const CBlockIndex *FindFork(const CBlockIndex *pindex) const;

/** Find the earliest block with timestamp equal or greater than the given. */
CBlockIndex* FindEarliestAtLeast(int64_t nTime) const;
/** Find the earliest block with timestamp equal or greater than the given time and height equal or greater than the given height. */
CBlockIndex* FindEarliestAtLeast(int64_t nTime, int height) const;
};

#endif // BITCOIN_CHAIN_H
2 changes: 1 addition & 1 deletion src/init.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -702,7 +702,7 @@ void SetupServerArgs()
gArgs.AddArg("-platform-user=<user>", "Set the username for the \"platform user\", a restricted user intended to be used by Dash Platform, to the specified username.", ArgsManager::ALLOW_ANY, OptionsCategory::MASTERNODE);

gArgs.AddArg("-acceptnonstdtxn", strprintf("Relay and mine \"non-standard\" transactions (%sdefault: %u)", "testnet/regtest only; ", !testnetChainParams->RequireStandard()), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::NODE_RELAY);
gArgs.AddArg("-dustrelayfee=<amt>", strprintf("Fee rate (in %s/kB) used to defined dust, the value of an output such that it will cost more than its value in fees at this fee rate to spend it. (default: %s)", CURRENCY_UNIT, FormatMoney(DUST_RELAY_TX_FEE)), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::NODE_RELAY);
gArgs.AddArg("-dustrelayfee=<amt>", strprintf("Fee rate (in %s/kB) used to define dust, the value of an output such that it will cost more than its value in fees at this fee rate to spend it. (default: %s)", CURRENCY_UNIT, FormatMoney(DUST_RELAY_TX_FEE)), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::NODE_RELAY);
gArgs.AddArg("-incrementalrelayfee=<amt>", strprintf("Fee rate (in %s/kB) used to define cost of relay, used for mempool limiting and BIP 125 replacement. (default: %s)", CURRENCY_UNIT, FormatMoney(DEFAULT_INCREMENTAL_RELAY_FEE)), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::NODE_RELAY);
gArgs.AddArg("-bytespersigop", strprintf("Equivalent bytes per sigop in transactions for relay and mining (default: %u)", DEFAULT_BYTES_PER_SIGOP), ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY);
gArgs.AddArg("-datacarrier", strprintf("Relay and mine data carrier transactions (default: %u)", DEFAULT_ACCEPT_DATACARRIER), ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY);
Expand Down
18 changes: 2 additions & 16 deletions src/interfaces/chain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -89,30 +89,16 @@ class LockImpl : public Chain::Lock, public UniqueLock<CCriticalSection>
CBlockIndex* block = ::ChainActive()[height];
return block && ((block->nStatus & BLOCK_HAVE_DATA) != 0) && block->nTx > 0;
}
Optional<int> findFirstBlockWithTime(int64_t time, uint256* hash) override
Optional<int> findFirstBlockWithTimeAndHeight(int64_t time, int height, uint256* hash) override
{
LockAnnotation lock(::cs_main);
CBlockIndex* block = ::ChainActive().FindEarliestAtLeast(time);
CBlockIndex* block = ::ChainActive().FindEarliestAtLeast(time, height);
Comment thread
UdjinM6 marked this conversation as resolved.
Outdated
if (block) {
if (hash) *hash = block->GetBlockHash();
return block->nHeight;
}
return nullopt;
}
Optional<int> findFirstBlockWithTimeAndHeight(int64_t time, int height) override
{
// TODO: Could update CChain::FindEarliestAtLeast() to take a height
// parameter and use it with std::lower_bound() to make this
// implementation more efficient and allow combining
// findFirstBlockWithTime and findFirstBlockWithTimeAndHeight into one
// method.
for (CBlockIndex* block = ::ChainActive()[height]; block; block = ::ChainActive().Next(block)) {
if (block->GetBlockTime() >= time) {
return block->nHeight;
}
}
return nullopt;
}
Optional<int> findPruned(int start_height, Optional<int> stop_height) override
{
LockAnnotation lock(::cs_main);
Expand Down
17 changes: 4 additions & 13 deletions src/interfaces/chain.h
Original file line number Diff line number Diff line change
Expand Up @@ -99,21 +99,12 @@ class Chain
//! pruned), and contains transactions.
virtual bool haveBlockOnDisk(int height) = 0;

//! Return height of the first block in the chain with timestamp equal
//! or greater than the given time, or nullopt if there is no block with
//! a high enough timestamp. Also return the block hash as an optional
//! output parameter (to avoid the cost of a second lookup in case this
//! information is needed.)
virtual Optional<int> findFirstBlockWithTime(int64_t time, uint256* hash) = 0;

//! Return height of the first block in the chain with timestamp equal
//! or greater than the given time and height equal or greater than the
//! given height, or nullopt if there is no such block.
//!
//! Calling this with height 0 is equivalent to calling
//! findFirstBlockWithTime, but less efficient because it requires a
//! linear instead of a binary search.
virtual Optional<int> findFirstBlockWithTimeAndHeight(int64_t time, int height) = 0;
//! given height, or nullopt if there is no block with a high enough
//! timestamp and height. Also return the block hash as an optional output parameter
//! (to avoid the cost of a second lookup in case this information is needed.)
virtual Optional<int> findFirstBlockWithTimeAndHeight(int64_t time, int height, uint256* hash) = 0;

//! Return height of last block in the specified range which is pruned, or
//! nullopt if no block in the range is pruned. Range is inclusive.
Expand Down
2 changes: 1 addition & 1 deletion src/rpc/blockchain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1203,7 +1203,7 @@ static UniValue pruneblockchain(const JSONRPCRequest& request)
// too low to be a block time (corresponds to timestamp from Sep 2001).
if (heightParam > 1000000000) {
// Add a 2 hour buffer to include blocks which might have had old timestamps
CBlockIndex* pindex = ::ChainActive().FindEarliestAtLeast(heightParam - TIMESTAMP_WINDOW);
CBlockIndex* pindex = ::ChainActive().FindEarliestAtLeast(heightParam - TIMESTAMP_WINDOW, 0);
if (!pindex) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Could not find block with at least the specified timestamp.");
}
Expand Down
35 changes: 23 additions & 12 deletions src/rpc/rawtransaction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -530,33 +530,44 @@ static UniValue decoderawtransaction(const JSONRPCRequest& request)
return result;
}

static std::string GetAllOutputTypes()
{
std::string ret;
for (int i = TX_NONSTANDARD; i <= TX_NULL_DATA; ++i) {
if (i != TX_NONSTANDARD) ret += ", ";
ret += GetTxnOutputType(static_cast<txnouttype>(i));
}
return ret;
}

static UniValue decodescript(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 1)
throw std::runtime_error(
RPCHelpMan{"decodescript",
const RPCHelpMan help{"decodescript",
"\nDecode a hex-encoded script.\n",
{
{"hexstring", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "the hex-encoded script"},
},
RPCResult{
"{\n"
" \"asm\":\"asm\", (string) Script public key\n"
" \"hex\":\"hex\", (string) hex-encoded public key\n"
" \"type\":\"type\", (string) The output type\n"
" \"reqSigs\": n, (numeric) The required signatures\n"
" \"addresses\": [ (json array of string)\n"
" \"address\" (string) dash address\n"
" \"asm\":\"asm\", (string) Script public key\n"
" \"type\":\"type\", (string) The output type (e.g. "+GetAllOutputTypes()+")\n"
" \"reqSigs\": n, (numeric) The required signatures\n"
" \"addresses\": [ (json array of string)\n"
" \"address\" (string) dash address\n"
" ,...\n"
" ],\n"
" \"p2sh\",\"address\" (string) address of P2SH script wrapping this redeem script (not returned if the script is already a P2SH).\n"
" \"p2sh\":\"str\" (string) address of P2SH script wrapping this redeem script (not returned if the script is already a P2SH).\n"
"}\n"
},
RPCExamples{
HelpExampleCli("decodescript", "\"hexstring\"")
+ HelpExampleRpc("decodescript", "\"hexstring\"")
},
}.ToString());
};

if (request.fHelp || !help.IsValidNumArgs(request.params.size())) {
throw std::runtime_error(help.ToString());
}

RPCTypeCheck(request.params, {UniValue::VSTR});

Expand All @@ -568,7 +579,7 @@ static UniValue decodescript(const JSONRPCRequest& request)
} else {
// Empty scripts are valid
}
ScriptPubKeyToUniv(script, r, false);
ScriptPubKeyToUniv(script, r, /* fIncludeHex */ false);

UniValue type;

Expand Down
59 changes: 59 additions & 0 deletions src/rpc/server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,35 @@ static bool ExecuteCommand(const CRPCCommand& command, const JSONRPCRequest& req
// Any commands submitted by this user will have their commands filtered based on the mapPlatformRestrictions
static const std::string defaultPlatformUser = "platform-user";

struct RPCCommandExecutionInfo
{
std::string method;
int64_t start;
};

struct RPCServerInfo
{
Mutex mutex;
std::list<RPCCommandExecutionInfo> active_commands GUARDED_BY(mutex);
};

static RPCServerInfo g_rpc_server_info;

struct RPCCommandExecution
{
std::list<RPCCommandExecutionInfo>::iterator it;
explicit RPCCommandExecution(const std::string& method)
{
LOCK(g_rpc_server_info.mutex);
it = g_rpc_server_info.active_commands.insert(g_rpc_server_info.active_commands.cend(), {method, GetTimeMicros()});
}
~RPCCommandExecution()
{
LOCK(g_rpc_server_info.mutex);
g_rpc_server_info.active_commands.erase(it);
}
};

static struct CRPCSignals
{
boost::signals2::signal<void ()> Started;
Expand Down Expand Up @@ -191,11 +220,40 @@ static UniValue uptime(const JSONRPCRequest& jsonRequest)
return GetTime() - GetStartupTime();
}

static UniValue getrpcinfo(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() > 0) {
throw std::runtime_error(
RPCHelpMan{"getrpcinfo",
"\nReturns details of the RPC server.\n",
{},
RPCResults{},
RPCExamples{""},
}.ToString()
);
}

LOCK(g_rpc_server_info.mutex);
UniValue active_commands(UniValue::VARR);
for (const RPCCommandExecutionInfo& info : g_rpc_server_info.active_commands) {
UniValue entry(UniValue::VOBJ);
entry.pushKV("method", info.method);
entry.pushKV("duration", GetTimeMicros() - info.start);
active_commands.push_back(entry);
}

UniValue result(UniValue::VOBJ);
result.pushKV("active_commands", active_commands);

return result;
}

// clang-format off
static const CRPCCommand vRPCCommands[] =
{ // category name actor (function) argNames
// --------------------- ------------------------ ----------------------- ----------
/* Overall control/query calls */
{ "control", "getrpcinfo", &getrpcinfo, {} },
{ "control", "help", &help, {"command","subcommand"} },
{ "control", "stop", &stop, {"wait"} },
{ "control", "uptime", &uptime, {} },
Expand Down Expand Up @@ -495,6 +553,7 @@ static bool ExecuteCommand(const CRPCCommand& command, const JSONRPCRequest& req

try
{
RPCCommandExecution execution(request.strMethod);
// Execute, convert arguments to array if necessary
if (request.params.isObject()) {
return command.actor(transformNamedArguments(request, command.argNames), result, last_handler);
Expand Down
46 changes: 29 additions & 17 deletions src/test/skiplist_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ BOOST_AUTO_TEST_CASE(findearliestatleast_test)
// Pick a random element in vBlocksMain.
int r = InsecureRandRange(vBlocksMain.size());
int64_t test_time = vBlocksMain[r].nTime;
CBlockIndex *ret = chain.FindEarliestAtLeast(test_time);
CBlockIndex* ret = chain.FindEarliestAtLeast(test_time, 0);
BOOST_CHECK(ret->nTimeMax >= test_time);
BOOST_CHECK((ret->pprev==nullptr) || ret->pprev->nTimeMax < test_time);
BOOST_CHECK(vBlocksMain[r].GetAncestor(ret->nHeight) == ret);
Expand All @@ -157,22 +157,34 @@ BOOST_AUTO_TEST_CASE(findearliestatleast_edge_test)
CChain chain;
chain.SetTip(&blocks.back());

BOOST_CHECK_EQUAL(chain.FindEarliestAtLeast(50)->nHeight, 0);
BOOST_CHECK_EQUAL(chain.FindEarliestAtLeast(100)->nHeight, 0);
BOOST_CHECK_EQUAL(chain.FindEarliestAtLeast(150)->nHeight, 3);
BOOST_CHECK_EQUAL(chain.FindEarliestAtLeast(200)->nHeight, 3);
BOOST_CHECK_EQUAL(chain.FindEarliestAtLeast(250)->nHeight, 6);
BOOST_CHECK_EQUAL(chain.FindEarliestAtLeast(300)->nHeight, 6);
BOOST_CHECK(!chain.FindEarliestAtLeast(350));

BOOST_CHECK_EQUAL(chain.FindEarliestAtLeast(0)->nHeight, 0);
BOOST_CHECK_EQUAL(chain.FindEarliestAtLeast(-1)->nHeight, 0);

BOOST_CHECK_EQUAL(chain.FindEarliestAtLeast(std::numeric_limits<int64_t>::min())->nHeight, 0);
BOOST_CHECK_EQUAL(chain.FindEarliestAtLeast(-int64_t(std::numeric_limits<unsigned int>::max()) - 1)->nHeight, 0);
BOOST_CHECK(!chain.FindEarliestAtLeast(std::numeric_limits<int64_t>::max()));
BOOST_CHECK(!chain.FindEarliestAtLeast(std::numeric_limits<unsigned int>::max()));
BOOST_CHECK(!chain.FindEarliestAtLeast(int64_t(std::numeric_limits<unsigned int>::max()) + 1));
BOOST_CHECK_EQUAL(chain.FindEarliestAtLeast(50, 0)->nHeight, 0);
BOOST_CHECK_EQUAL(chain.FindEarliestAtLeast(100, 0)->nHeight, 0);
BOOST_CHECK_EQUAL(chain.FindEarliestAtLeast(150, 0)->nHeight, 3);
BOOST_CHECK_EQUAL(chain.FindEarliestAtLeast(200, 0)->nHeight, 3);
BOOST_CHECK_EQUAL(chain.FindEarliestAtLeast(250, 0)->nHeight, 6);
BOOST_CHECK_EQUAL(chain.FindEarliestAtLeast(300, 0)->nHeight, 6);
BOOST_CHECK(!chain.FindEarliestAtLeast(350, 0));

BOOST_CHECK_EQUAL(chain.FindEarliestAtLeast(0, 0)->nHeight, 0);
BOOST_CHECK_EQUAL(chain.FindEarliestAtLeast(-1, 0)->nHeight, 0);

BOOST_CHECK_EQUAL(chain.FindEarliestAtLeast(std::numeric_limits<int64_t>::min(), 0)->nHeight, 0);
BOOST_CHECK_EQUAL(chain.FindEarliestAtLeast(-int64_t(std::numeric_limits<unsigned int>::max()) - 1, 0)->nHeight, 0);
BOOST_CHECK(!chain.FindEarliestAtLeast(std::numeric_limits<int64_t>::max(), 0));
BOOST_CHECK(!chain.FindEarliestAtLeast(std::numeric_limits<unsigned int>::max(), 0));
BOOST_CHECK(!chain.FindEarliestAtLeast(int64_t(std::numeric_limits<unsigned int>::max()) + 1, 0));

BOOST_CHECK_EQUAL(chain.FindEarliestAtLeast(0, -1)->nHeight, 0);
BOOST_CHECK_EQUAL(chain.FindEarliestAtLeast(0, 0)->nHeight, 0);
BOOST_CHECK_EQUAL(chain.FindEarliestAtLeast(0, 3)->nHeight, 3);
BOOST_CHECK_EQUAL(chain.FindEarliestAtLeast(0, 8)->nHeight, 8);
BOOST_CHECK(!chain.FindEarliestAtLeast(0, 9));

CBlockIndex* ret1 = chain.FindEarliestAtLeast(100, 2);
BOOST_CHECK(ret1->nTimeMax >= 100 && ret1->nHeight == 2);
BOOST_CHECK(!chain.FindEarliestAtLeast(300, 9));
CBlockIndex* ret2 = chain.FindEarliestAtLeast(200, 4);
BOOST_CHECK(ret2->nTimeMax >= 200 && ret2->nHeight == 4);
}

BOOST_AUTO_TEST_SUITE_END()
2 changes: 2 additions & 0 deletions src/wallet/coincontrol.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ class CCoinControl
bool m_avoid_partial_spends;
//! Fee estimation mode to control arguments to estimateSmartFee
FeeEstimateMode m_fee_mode;
//! Minimum chain depth value for coin availability
int m_min_depth{0};
//! Controls which types of coins are allowed to be used (default: ALL_COINS)
CoinType nCoinType;

Expand Down
6 changes: 3 additions & 3 deletions src/wallet/wallet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2179,7 +2179,7 @@ int64_t CWallet::RescanFromTime(int64_t startTime, const WalletRescanReserver& r
uint256 start_block;
{
auto locked_chain = chain().lock();
const Optional<int> start_height = locked_chain->findFirstBlockWithTime(startTime - TIMESTAMP_WINDOW, &start_block);
const Optional<int> start_height = locked_chain->findFirstBlockWithTimeAndHeight(startTime - TIMESTAMP_WINDOW, 0, &start_block);
const Optional<int> tip_height = locked_chain->getHeight();
WalletLogPrintf("%s: Rescanning last %i blocks\n", __func__, tip_height && start_height ? *tip_height - *start_height + 1 : 0);
}
Expand Down Expand Up @@ -3627,7 +3627,7 @@ bool CWallet::CreateTransaction(interfaces::Chain::Lock& locked_chain, const std
{
CAmount nAmountAvailable{0};
std::vector<COutput> vAvailableCoins;
AvailableCoins(*locked_chain, vAvailableCoins, true, &coin_control);
AvailableCoins(*locked_chain, vAvailableCoins, true, &coin_control, 1, MAX_MONEY, MAX_MONEY, 0, coin_control.m_min_depth);
CoinSelectionParams coin_selection_params; // Parameters for coin selection, init with dummy
coin_selection_params.use_bnb = false; // never use BnB

Expand Down Expand Up @@ -5265,7 +5265,7 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain,
// unless a full rescan was requested
if (gArgs.GetArg("-rescan", 0) != 2) {
if (walletInstance->nTimeFirstKey) {
if (Optional<int> first_block = locked_chain->findFirstBlockWithTimeAndHeight(walletInstance->nTimeFirstKey - TIMESTAMP_WINDOW, rescan_height)) {
if (Optional<int> first_block = locked_chain->findFirstBlockWithTimeAndHeight(walletInstance->nTimeFirstKey - TIMESTAMP_WINDOW, rescan_height, nullptr)) {
rescan_height = *first_block;
}
}
Expand Down
Loading