Merge #8680: Address Travis spurious failures

d6a5dc4 add waitfornewblock/waitforblock/waitforblockheight rpcs and use them for tests (Cory Fields)
This commit is contained in:
Wladimir J. van der Laan 2016-09-09 08:33:26 +02:00 committed by Alexander Block
parent 6924212f09
commit c2a601f507
5 changed files with 168 additions and 2 deletions

View File

@ -135,12 +135,16 @@ def sync_blocks(rpc_connections, wait=1, timeout=60):
""" """
Wait until everybody has the same tip Wait until everybody has the same tip
""" """
maxheight = 0
while timeout > 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): if tips == [ tips[0] ]*len(tips):
return True 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 timeout -= wait
maxheight = max(heights)
raise AssertionError("Block sync failed") raise AssertionError("Block sync failed")
def sync_mempools(rpc_connections, wait=1, timeout=60): def sync_mempools(rpc_connections, wait=1, timeout=60):

View File

@ -355,9 +355,15 @@ bool static Bind(CConnman& connman, const CService &addr, unsigned int flags) {
} }
return true; return true;
} }
void OnRPCStarted()
{
uiInterface.NotifyBlockTip.connect(&RPCNotifyBlockChange);
}
void OnRPCStopped() void OnRPCStopped()
{ {
uiInterface.NotifyBlockTip.disconnect(&RPCNotifyBlockChange);
RPCNotifyBlockChange(false, nullptr);
cvBlockChange.notify_all(); cvBlockChange.notify_all();
LogPrint("rpc", "RPC stopped.\n"); LogPrint("rpc", "RPC stopped.\n");
} }
@ -774,6 +780,7 @@ bool InitSanityCheck(void)
bool AppInitServers(boost::thread_group& threadGroup) bool AppInitServers(boost::thread_group& threadGroup)
{ {
RPCServer::OnStarted(&OnRPCStarted);
RPCServer::OnStopped(&OnRPCStopped); RPCServer::OnStopped(&OnRPCStopped);
RPCServer::OnPreCommand(&OnRPCPreCommand); RPCServer::OnPreCommand(&OnRPCPreCommand);
if (!InitHTTPServer()) if (!InitHTTPServer())
@ -1554,6 +1561,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
{ {
LOCK(cs_main); LOCK(cs_main);
CBlockIndex* tip = chainActive.Tip(); CBlockIndex* tip = chainActive.Tip();
RPCNotifyBlockChange(true, tip);
if (tip && tip->nTime > GetAdjustedTime() + 2 * 60 * 60) { if (tip && tip->nTime > GetAdjustedTime() + 2 * 60 * 60) {
strLoadError = _("The block database contains a block which appears to be from the future. " 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. " "This may be due to your computer's date and time being set incorrectly. "

View File

@ -28,8 +28,20 @@
#include <boost/thread/thread.hpp> // boost::thread::interrupt #include <boost/thread/thread.hpp> // boost::thread::interrupt
#include <mutex>
#include <condition_variable>
using namespace std; 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); extern void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry);
void ScriptPubKeyToJSON(const CScript& scriptPubKey, UniValue& out, bool fIncludeHex); 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(); 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) UniValue getdifficulty(const UniValue& params, bool fHelp)
{ {
if (fHelp || params.size() != 0) if (fHelp || params.size() != 0)
@ -1364,6 +1508,9 @@ static const CRPCCommand commands[] =
/* Not shown in help */ /* Not shown in help */
{ "hidden", "invalidateblock", &invalidateblock, true }, { "hidden", "invalidateblock", &invalidateblock, true },
{ "hidden", "reconsiderblock", &reconsiderblock, true }, { "hidden", "reconsiderblock", &reconsiderblock, true },
{ "hidden", "waitfornewblock", &waitfornewblock, true },
{ "hidden", "waitforblock", &waitforblock, true },
{ "hidden", "waitforblockheight", &waitforblockheight, true },
}; };
void RegisterBlockchainRPCCommands(CRPCTable &t) void RegisterBlockchainRPCCommands(CRPCTable &t)

View File

@ -58,6 +58,12 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "getchaintips", 1 }, { "getchaintips", 1 },
{ "getblockhash", 0 }, { "getblockhash", 0 },
{ "getsuperblockbudget", 0 }, { "getsuperblockbudget", 0 },
{ "waitforblockheight", 0 },
{ "waitforblockheight", 1 },
{ "waitforblock", 1 },
{ "waitforblock", 2 },
{ "waitfornewblock", 0 },
{ "waitfornewblock", 1 },
{ "move", 2 }, { "move", 2 },
{ "move", 3 }, { "move", 3 },
{ "sendfrom", 2 }, { "sendfrom", 2 },

View File

@ -193,5 +193,6 @@ bool StartRPC();
void InterruptRPC(); void InterruptRPC();
void StopRPC(); void StopRPC();
std::string JSONRPCExecBatch(const UniValue& vReq); std::string JSONRPCExecBatch(const UniValue& vReq);
void RPCNotifyBlockChange(bool ibd, const CBlockIndex *);
#endif // BITCOIN_RPCSERVER_H #endif // BITCOIN_RPCSERVER_H