diff options
author | Cory Fields <cory-nospam-@coryfields.com> | 2016-09-01 21:55:21 -0400 |
---|---|---|
committer | Cory Fields <cory-nospam-@coryfields.com> | 2016-09-07 12:46:01 -0400 |
commit | d6a5dc4a2eaa0d7348804254ca09e75fc3a858ab (patch) | |
tree | dd60ab61035edb6e519f9f69b4d80d48de079ff2 /src/rpc | |
parent | 5b2ea29cf4fd298346437bb16a54407f8c1f9dca (diff) |
add waitfornewblock/waitforblock/waitforblockheight rpcs and use them for tests
waitfornewblock waits until a new block is received, or the timeout expires, then
returns the current block height/hash.
waitforblock waits for a specific blockhash, or until the timeout expires, then
returns the current block height/hash. If the target blockhash is the current
tip, it will return immediately.
waitforblockheight waits until the tip has reached a certain height or higher,
then returns the current height and hash.
waitforblockheight is used to avoid polling in the rpc tests.
Diffstat (limited to 'src/rpc')
-rw-r--r-- | src/rpc/blockchain.cpp | 147 | ||||
-rw-r--r-- | src/rpc/client.cpp | 6 | ||||
-rw-r--r-- | src/rpc/server.h | 1 |
3 files changed, 154 insertions, 0 deletions
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 |