|
26 | 26 |
|
27 | 27 | #include <boost/thread/thread.hpp> // boost::thread::interrupt |
28 | 28 |
|
| 29 | +#include <mutex> |
| 30 | +#include <condition_variable> |
29 | 31 | using namespace std; |
30 | 32 |
|
| 33 | +struct CUpdatedBlock |
| 34 | +{ |
| 35 | + uint256 hash; |
| 36 | + int height; |
| 37 | +}; |
| 38 | + |
| 39 | +static std::mutex cs_blockchange; |
| 40 | +static std::condition_variable cond_blockchange; |
| 41 | +static CUpdatedBlock latestblock; |
| 42 | + |
31 | 43 | extern void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry); |
32 | 44 | void ScriptPubKeyToJSON(const CScript& scriptPubKey, UniValue& out, bool fIncludeHex); |
33 | 45 |
|
@@ -168,6 +180,138 @@ UniValue getbestblockhash(const UniValue& params, bool fHelp) |
168 | 180 | return chainActive.Tip()->GetBlockHash().GetHex(); |
169 | 181 | } |
170 | 182 |
|
| 183 | +void RPCNotifyBlockChange(bool ibd, const CBlockIndex * pindex) |
| 184 | +{ |
| 185 | + if(pindex) { |
| 186 | + std::lock_guard<std::mutex> lock(cs_blockchange); |
| 187 | + latestblock.hash = pindex->GetBlockHash(); |
| 188 | + latestblock.height = pindex->nHeight; |
| 189 | + } |
| 190 | + cond_blockchange.notify_all(); |
| 191 | +} |
| 192 | + |
| 193 | +UniValue waitfornewblock(const UniValue& params, bool fHelp) |
| 194 | +{ |
| 195 | + if (fHelp || params.size() > 1) |
| 196 | + throw runtime_error( |
| 197 | + "waitfornewblock\n" |
| 198 | + "\nWaits for a specific new block and returns useful info about it.\n" |
| 199 | + "\nReturns the current block on timeout or exit.\n" |
| 200 | + "\nArguments:\n" |
| 201 | + "1. timeout (milliseconds) (int, optional, default=false)\n" |
| 202 | + "\nResult::\n" |
| 203 | + "{ (json object)\n" |
| 204 | + " \"hash\" : { (string) The blockhash\n" |
| 205 | + " \"height\" : { (int) Block height\n" |
| 206 | + "}\n" |
| 207 | + "\nExamples\n" |
| 208 | + + HelpExampleCli("waitfornewblock", "1000") |
| 209 | + + HelpExampleRpc("waitfornewblock", "1000") |
| 210 | + ); |
| 211 | + int timeout = 0; |
| 212 | + if (params.size() > 0) |
| 213 | + timeout = params[0].get_int(); |
| 214 | + |
| 215 | + CUpdatedBlock block; |
| 216 | + { |
| 217 | + std::unique_lock<std::mutex> lock(cs_blockchange); |
| 218 | + block = latestblock; |
| 219 | + if(timeout) |
| 220 | + cond_blockchange.wait_for(lock, std::chrono::milliseconds(timeout), [&block]{return latestblock.height != block.height || latestblock.hash != block.hash || !IsRPCRunning(); }); |
| 221 | + else |
| 222 | + cond_blockchange.wait(lock, [&block]{return latestblock.height != block.height || latestblock.hash != block.hash || !IsRPCRunning(); }); |
| 223 | + block = latestblock; |
| 224 | + } |
| 225 | + UniValue ret(UniValue::VOBJ); |
| 226 | + ret.push_back(Pair("hash", block.hash.GetHex())); |
| 227 | + ret.push_back(Pair("height", block.height)); |
| 228 | + return ret; |
| 229 | +} |
| 230 | + |
| 231 | +UniValue waitforblock(const UniValue& params, bool fHelp) |
| 232 | +{ |
| 233 | + if (fHelp || params.size() < 1 || params.size() > 2) |
| 234 | + throw runtime_error( |
| 235 | + "waitforblock\n" |
| 236 | + "\nWaits for a specific new block and returns useful info about it.\n" |
| 237 | + "\nReturns the current block on timeout or exit.\n" |
| 238 | + "\nArguments:\n" |
| 239 | + "1. blockhash to wait for (string)\n" |
| 240 | + "2. timeout (milliseconds) (int, optional, default=false)\n" |
| 241 | + "\nResult::\n" |
| 242 | + "{ (json object)\n" |
| 243 | + " \"hash\" : { (string) The blockhash\n" |
| 244 | + " \"height\" : { (int) Block height\n" |
| 245 | + "}\n" |
| 246 | + "\nExamples\n" |
| 247 | + + HelpExampleCli("waitforblock", "\"0000000000079f8ef3d2c688c244eb7a4570b24c9ed7b4a8c619eb02596f8862\", 1000") |
| 248 | + + HelpExampleRpc("waitforblock", "\"0000000000079f8ef3d2c688c244eb7a4570b24c9ed7b4a8c619eb02596f8862\", 1000") |
| 249 | + ); |
| 250 | + int timeout = 0; |
| 251 | + |
| 252 | + uint256 hash = uint256S(params[0].get_str()); |
| 253 | + |
| 254 | + if (params.size() > 1) |
| 255 | + timeout = params[1].get_int(); |
| 256 | + |
| 257 | + CUpdatedBlock block; |
| 258 | + { |
| 259 | + std::unique_lock<std::mutex> lock(cs_blockchange); |
| 260 | + if(timeout) |
| 261 | + cond_blockchange.wait_for(lock, std::chrono::milliseconds(timeout), [&hash]{return latestblock.hash == hash || !IsRPCRunning();}); |
| 262 | + else |
| 263 | + cond_blockchange.wait(lock, [&hash]{return latestblock.hash == hash || !IsRPCRunning(); }); |
| 264 | + block = latestblock; |
| 265 | + } |
| 266 | + |
| 267 | + UniValue ret(UniValue::VOBJ); |
| 268 | + ret.push_back(Pair("hash", block.hash.GetHex())); |
| 269 | + ret.push_back(Pair("height", block.height)); |
| 270 | + return ret; |
| 271 | +} |
| 272 | + |
| 273 | +UniValue waitforblockheight(const UniValue& params, bool fHelp) |
| 274 | +{ |
| 275 | + if (fHelp || params.size() < 1 || params.size() > 2) |
| 276 | + throw runtime_error( |
| 277 | + "waitforblock\n" |
| 278 | + "\nWaits for (at least) block height and returns the height and hash\n" |
| 279 | + "\nof the current tip.\n" |
| 280 | + "\nReturns the current block on timeout or exit.\n" |
| 281 | + "\nArguments:\n" |
| 282 | + "1. block height to wait for (int)\n" |
| 283 | + "2. timeout (milliseconds) (int, optional, default=false)\n" |
| 284 | + "\nResult::\n" |
| 285 | + "{ (json object)\n" |
| 286 | + " \"hash\" : { (string) The blockhash\n" |
| 287 | + " \"height\" : { (int) Block height\n" |
| 288 | + "}\n" |
| 289 | + "\nExamples\n" |
| 290 | + + HelpExampleCli("waitforblockheight", "\"100\", 1000") |
| 291 | + + HelpExampleRpc("waitforblockheight", "\"100\", 1000") |
| 292 | + ); |
| 293 | + int timeout = 0; |
| 294 | + |
| 295 | + int height = params[0].get_int(); |
| 296 | + |
| 297 | + if (params.size() > 1) |
| 298 | + timeout = params[1].get_int(); |
| 299 | + |
| 300 | + CUpdatedBlock block; |
| 301 | + { |
| 302 | + std::unique_lock<std::mutex> lock(cs_blockchange); |
| 303 | + if(timeout) |
| 304 | + cond_blockchange.wait_for(lock, std::chrono::milliseconds(timeout), [&height]{return latestblock.height >= height || !IsRPCRunning();}); |
| 305 | + else |
| 306 | + cond_blockchange.wait(lock, [&height]{return latestblock.height >= height || !IsRPCRunning(); }); |
| 307 | + block = latestblock; |
| 308 | + } |
| 309 | + UniValue ret(UniValue::VOBJ); |
| 310 | + ret.push_back(Pair("hash", block.hash.GetHex())); |
| 311 | + ret.push_back(Pair("height", block.height)); |
| 312 | + return ret; |
| 313 | +} |
| 314 | + |
171 | 315 | UniValue getdifficulty(const UniValue& params, bool fHelp) |
172 | 316 | { |
173 | 317 | if (fHelp || params.size() != 0) |
@@ -1203,6 +1347,9 @@ static const CRPCCommand commands[] = |
1203 | 1347 | /* Not shown in help */ |
1204 | 1348 | { "hidden", "invalidateblock", &invalidateblock, true }, |
1205 | 1349 | { "hidden", "reconsiderblock", &reconsiderblock, true }, |
| 1350 | + { "hidden", "waitfornewblock", &waitfornewblock, true }, |
| 1351 | + { "hidden", "waitforblock", &waitforblock, true }, |
| 1352 | + { "hidden", "waitforblockheight", &waitforblockheight, true }, |
1206 | 1353 | }; |
1207 | 1354 |
|
1208 | 1355 | void RegisterBlockchainRPCCommands(CRPCTable &t) |
|
0 commit comments