| 418 | } |
| 419 | |
| 420 | static RPCMethod waitforblockheight() |
| 421 | { |
| 422 | return RPCMethod{ |
| 423 | "waitforblockheight", |
| 424 | "Waits for (at least) block height and returns the height and hash\n" |
| 425 | "of the current tip.\n" |
| 426 | "\nReturns the current block on timeout or exit.\n" |
| 427 | "\nMake sure to use no RPC timeout (bitcoin-cli -rpcclienttimeout=0)", |
| 428 | { |
| 429 | {"height", RPCArg::Type::NUM, RPCArg::Optional::NO, "Block height to wait for."}, |
| 430 | {"timeout", RPCArg::Type::NUM, RPCArg::Default{0}, "Time in milliseconds to wait for a response. 0 indicates no timeout."}, |
| 431 | }, |
| 432 | RPCResult{ |
| 433 | RPCResult::Type::OBJ, "", "", |
| 434 | { |
| 435 | {RPCResult::Type::STR_HEX, "hash", "The blockhash"}, |
| 436 | {RPCResult::Type::NUM, "height", "Block height"}, |
| 437 | }}, |
| 438 | RPCExamples{ |
| 439 | HelpExampleCli("waitforblockheight", "100 1000") |
| 440 | + HelpExampleRpc("waitforblockheight", "100, 1000") |
| 441 | }, |
| 442 | [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue |
| 443 | { |
| 444 | int timeout = 0; |
| 445 | |
| 446 | int height = request.params[0].getInt<int>(); |
| 447 | |
| 448 | if (!request.params[1].isNull()) |
| 449 | timeout = request.params[1].getInt<int>(); |
| 450 | if (timeout < 0) throw JSONRPCError(RPC_MISC_ERROR, "Negative timeout"); |
| 451 | |
| 452 | NodeContext& node = EnsureAnyNodeContext(request.context); |
| 453 | Mining& miner = EnsureMining(node); |
| 454 | |
| 455 | // Abort if RPC came out of warmup too early |
| 456 | BlockRef current_block{CHECK_NONFATAL(miner.getTip()).value()}; |
| 457 | |
| 458 | const auto deadline{std::chrono::steady_clock::now() + 1ms * timeout}; |
| 459 | |
| 460 | while (current_block.height < height) { |
| 461 | std::optional<BlockRef> block; |
| 462 | if (timeout) { |
| 463 | auto now{std::chrono::steady_clock::now()}; |
| 464 | if (now >= deadline) break; |
| 465 | const MillisecondsDouble remaining{deadline - now}; |
| 466 | block = miner.waitTipChanged(current_block.hash, remaining); |
| 467 | } else { |
| 468 | block = miner.waitTipChanged(current_block.hash); |
| 469 | } |
| 470 | // Return current block on shutdown |
| 471 | if (!block) break; |
| 472 | current_block = *block; |
| 473 | } |
| 474 | |
| 475 | UniValue ret(UniValue::VOBJ); |
| 476 | ret.pushKV("hash", current_block.hash.GetHex()); |
| 477 | ret.pushKV("height", current_block.height); |
nothing calls this directly
no test coverage detected