aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--qa/rpc-tests/test_framework/util.py8
-rw-r--r--src/init.cpp8
-rw-r--r--src/rpc/blockchain.cpp147
-rw-r--r--src/rpc/client.cpp6
-rw-r--r--src/rpc/server.h1
5 files changed, 168 insertions, 2 deletions
diff --git a/qa/rpc-tests/test_framework/util.py b/qa/rpc-tests/test_framework/util.py
index 190fa7f661..eee77f1a10 100644
--- a/qa/rpc-tests/test_framework/util.py
+++ b/qa/rpc-tests/test_framework/util.py
@@ -125,12 +125,16 @@ def sync_blocks(rpc_connections, wait=1, timeout=60):
"""
Wait until everybody has the same tip
"""
+ maxheight = 0
while timeout > 0:
- tips = [ x.getbestblockhash() for x in rpc_connections ]
+ tips = [ x.waitforblockheight(maxheight, int(wait * 1000)) for x in rpc_connections ]
+ heights = [ x["height"] for x in tips ]
if tips == [ tips[0] ]*len(tips):
return True
- time.sleep(wait)
+ if heights == [ heights[0] ]*len(heights): #heights are the same but hashes are not
+ raise AssertionError("Block sync failed")
timeout -= wait
+ maxheight = max(heights)
raise AssertionError("Block sync failed")
def sync_mempools(rpc_connections, wait=1, timeout=60):
diff --git a/src/init.cpp b/src/init.cpp
index 27843fa882..64e161b9b2 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -280,9 +280,15 @@ bool static Bind(const CService &addr, unsigned int flags) {
}
return true;
}
+void OnRPCStarted()
+{
+ uiInterface.NotifyBlockTip.connect(&RPCNotifyBlockChange);
+}
void OnRPCStopped()
{
+ uiInterface.NotifyBlockTip.disconnect(&RPCNotifyBlockChange);
+ RPCNotifyBlockChange(false, nullptr);
cvBlockChange.notify_all();
LogPrint("rpc", "RPC stopped.\n");
}
@@ -666,6 +672,7 @@ bool InitSanityCheck(void)
bool AppInitServers(boost::thread_group& threadGroup)
{
+ RPCServer::OnStarted(&OnRPCStarted);
RPCServer::OnStopped(&OnRPCStopped);
RPCServer::OnPreCommand(&OnRPCPreCommand);
if (!InitHTTPServer())
@@ -1357,6 +1364,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
{
LOCK(cs_main);
CBlockIndex* tip = chainActive.Tip();
+ RPCNotifyBlockChange(true, tip);
if (tip && tip->nTime > GetAdjustedTime() + 2 * 60 * 60) {
strLoadError = _("The block database contains a block which appears to be from the future. "
"This may be due to your computer's date and time being set incorrectly. "
diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp
index b90410017b..dc7e4721bb 100644
--- a/src/rpc/blockchain.cpp
+++ b/src/rpc/blockchain.cpp
@@ -26,8 +26,20 @@
#include <boost/thread/thread.hpp> // boost::thread::interrupt
+#include <mutex>
+#include <condition_variable>
using namespace std;
+struct CUpdatedBlock
+{
+ uint256 hash;
+ int height;
+};
+
+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);
void ScriptPubKeyToJSON(const CScript& scriptPubKey, UniValue& out, bool fIncludeHex);
@@ -168,6 +180,138 @@ UniValue getbestblockhash(const UniValue& params, bool fHelp)
return chainActive.Tip()->GetBlockHash().GetHex();
}
+void RPCNotifyBlockChange(bool ibd, const CBlockIndex * pindex)
+{
+ if(pindex) {
+ std::lock_guard<std::mutex> lock(cs_blockchange);
+ latestblock.hash = pindex->GetBlockHash();
+ latestblock.height = pindex->nHeight;
+ }
+ cond_blockchange.notify_all();
+}
+
+UniValue waitfornewblock(const UniValue& params, bool fHelp)
+{
+ if (fHelp || params.size() > 1)
+ throw runtime_error(
+ "waitfornewblock\n"
+ "\nWaits for a specific new block and returns useful info about it.\n"
+ "\nReturns the current block on timeout or exit.\n"
+ "\nArguments:\n"
+ "1. timeout (milliseconds) (int, optional, default=false)\n"
+ "\nResult::\n"
+ "{ (json object)\n"
+ " \"hash\" : { (string) The blockhash\n"
+ " \"height\" : { (int) Block height\n"
+ "}\n"
+ "\nExamples\n"
+ + HelpExampleCli("waitfornewblock", "1000")
+ + HelpExampleRpc("waitfornewblock", "1000")
+ );
+ int timeout = 0;
+ if (params.size() > 0)
+ timeout = params[0].get_int();
+
+ CUpdatedBlock block;
+ {
+ std::unique_lock<std::mutex> lock(cs_blockchange);
+ block = latestblock;
+ if(timeout)
+ cond_blockchange.wait_for(lock, std::chrono::milliseconds(timeout), [&block]{return latestblock.height != block.height || latestblock.hash != block.hash || !IsRPCRunning(); });
+ else
+ cond_blockchange.wait(lock, [&block]{return latestblock.height != block.height || latestblock.hash != block.hash || !IsRPCRunning(); });
+ block = latestblock;
+ }
+ UniValue ret(UniValue::VOBJ);
+ ret.push_back(Pair("hash", block.hash.GetHex()));
+ ret.push_back(Pair("height", block.height));
+ return ret;
+}
+
+UniValue waitforblock(const UniValue& params, bool fHelp)
+{
+ if (fHelp || params.size() < 1 || params.size() > 2)
+ throw runtime_error(
+ "waitforblock\n"
+ "\nWaits for a specific new block and returns useful info about it.\n"
+ "\nReturns the current block on timeout or exit.\n"
+ "\nArguments:\n"
+ "1. blockhash to wait for (string)\n"
+ "2. timeout (milliseconds) (int, optional, default=false)\n"
+ "\nResult::\n"
+ "{ (json object)\n"
+ " \"hash\" : { (string) The blockhash\n"
+ " \"height\" : { (int) Block height\n"
+ "}\n"
+ "\nExamples\n"
+ + HelpExampleCli("waitforblock", "\"0000000000079f8ef3d2c688c244eb7a4570b24c9ed7b4a8c619eb02596f8862\", 1000")
+ + HelpExampleRpc("waitforblock", "\"0000000000079f8ef3d2c688c244eb7a4570b24c9ed7b4a8c619eb02596f8862\", 1000")
+ );
+ int timeout = 0;
+
+ uint256 hash = uint256S(params[0].get_str());
+
+ if (params.size() > 1)
+ timeout = params[1].get_int();
+
+ CUpdatedBlock block;
+ {
+ std::unique_lock<std::mutex> lock(cs_blockchange);
+ if(timeout)
+ cond_blockchange.wait_for(lock, std::chrono::milliseconds(timeout), [&hash]{return latestblock.hash == hash || !IsRPCRunning();});
+ else
+ cond_blockchange.wait(lock, [&hash]{return latestblock.hash == hash || !IsRPCRunning(); });
+ block = latestblock;
+ }
+
+ UniValue ret(UniValue::VOBJ);
+ ret.push_back(Pair("hash", block.hash.GetHex()));
+ ret.push_back(Pair("height", block.height));
+ return ret;
+}
+
+UniValue waitforblockheight(const UniValue& params, bool fHelp)
+{
+ if (fHelp || params.size() < 1 || params.size() > 2)
+ throw runtime_error(
+ "waitforblock\n"
+ "\nWaits for (at least) block height and returns the height and hash\n"
+ "\nof the current tip.\n"
+ "\nReturns the current block on timeout or exit.\n"
+ "\nArguments:\n"
+ "1. block height to wait for (int)\n"
+ "2. timeout (milliseconds) (int, optional, default=false)\n"
+ "\nResult::\n"
+ "{ (json object)\n"
+ " \"hash\" : { (string) The blockhash\n"
+ " \"height\" : { (int) Block height\n"
+ "}\n"
+ "\nExamples\n"
+ + HelpExampleCli("waitforblockheight", "\"100\", 1000")
+ + HelpExampleRpc("waitforblockheight", "\"100\", 1000")
+ );
+ int timeout = 0;
+
+ int height = params[0].get_int();
+
+ if (params.size() > 1)
+ timeout = params[1].get_int();
+
+ CUpdatedBlock block;
+ {
+ std::unique_lock<std::mutex> lock(cs_blockchange);
+ if(timeout)
+ cond_blockchange.wait_for(lock, std::chrono::milliseconds(timeout), [&height]{return latestblock.height >= height || !IsRPCRunning();});
+ else
+ cond_blockchange.wait(lock, [&height]{return latestblock.height >= height || !IsRPCRunning(); });
+ block = latestblock;
+ }
+ UniValue ret(UniValue::VOBJ);
+ ret.push_back(Pair("hash", block.hash.GetHex()));
+ ret.push_back(Pair("height", block.height));
+ return ret;
+}
+
UniValue getdifficulty(const UniValue& params, bool fHelp)
{
if (fHelp || params.size() != 0)
@@ -1203,6 +1347,9 @@ static const CRPCCommand commands[] =
/* Not shown in help */
{ "hidden", "invalidateblock", &invalidateblock, true },
{ "hidden", "reconsiderblock", &reconsiderblock, true },
+ { "hidden", "waitfornewblock", &waitfornewblock, true },
+ { "hidden", "waitforblock", &waitforblock, true },
+ { "hidden", "waitforblockheight", &waitforblockheight, true },
};
void RegisterBlockchainRPCCommands(CRPCTable &t)
diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp
index 3003ea3452..c14d9d6747 100644
--- a/src/rpc/client.cpp
+++ b/src/rpc/client.cpp
@@ -46,6 +46,12 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "getbalance", 1 },
{ "getbalance", 2 },
{ "getblockhash", 0 },
+ { "waitforblockheight", 0 },
+ { "waitforblockheight", 1 },
+ { "waitforblock", 1 },
+ { "waitforblock", 2 },
+ { "waitfornewblock", 0 },
+ { "waitfornewblock", 1 },
{ "move", 2 },
{ "move", 3 },
{ "sendfrom", 2 },
diff --git a/src/rpc/server.h b/src/rpc/server.h
index b5ccc153d0..4e0aa2c6d6 100644
--- a/src/rpc/server.h
+++ b/src/rpc/server.h
@@ -194,5 +194,6 @@ bool StartRPC();
void InterruptRPC();
void StopRPC();
std::string JSONRPCExecBatch(const UniValue& vReq);
+void RPCNotifyBlockChange(bool ibd, const CBlockIndex *);
#endif // BITCOIN_RPCSERVER_H