diff --git a/doc/release-30635.md b/doc/release-30635.md new file mode 100644 index 00000000000..0ec68e93cc5 --- /dev/null +++ b/doc/release-30635.md @@ -0,0 +1,5 @@ +Updated RPCs +------------ + +- The waitfornewblock now takes an optional `current_tip` argument. It is also no longer hidden. (#30635) +- The waitforblock and waitforblockheight RPCs are no longer hidden. (#30635) diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 6e5c656f3d8..a51c4843b79 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -266,6 +266,7 @@ static RPCHelpMan waitfornewblock() "\nMake sure to use no RPC timeout (bitcoin-cli -rpcclienttimeout=0)", { {"timeout", RPCArg::Type::NUM, RPCArg::Default{0}, "Time in milliseconds to wait for a response. 0 indicates no timeout."}, + {"current_tip", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED, "Method waits for the chain tip to differ from this."}, }, RPCResult{ RPCResult::Type::OBJ, "", "", @@ -287,10 +288,22 @@ static RPCHelpMan waitfornewblock() NodeContext& node = EnsureAnyNodeContext(request.context); Mining& miner = EnsureMining(node); - // Abort if RPC came out of warmup too early + // If the caller provided a current_tip value, pass it to waitTipChanged(). + // + // If the caller did not provide a current tip hash, call getTip() to get + // one and wait for the tip to be different from this value. This mode is + // less reliable because if the tip changed between waitfornewblock calls, + // it will need to change a second time before this call returns. BlockRef current_block{CHECK_NONFATAL(miner.getTip()).value()}; - std::optional block = timeout ? miner.waitTipChanged(current_block.hash, std::chrono::milliseconds(timeout)) : - miner.waitTipChanged(current_block.hash); + + uint256 tip_hash{request.params[1].isNull() + ? current_block.hash + : ParseHashV(request.params[1], "current_tip")}; + + // If the user provided an invalid current_tip then this call immediately + // returns the current tip. + std::optional block = timeout ? miner.waitTipChanged(tip_hash, std::chrono::milliseconds(timeout)) : + miner.waitTipChanged(tip_hash); // Return current block upon shutdown if (block) current_block = *block; @@ -3424,9 +3437,9 @@ void RegisterBlockchainRPCCommands(CRPCTable& t) {"blockchain", &getchainstates}, {"hidden", &invalidateblock}, {"hidden", &reconsiderblock}, - {"hidden", &waitfornewblock}, - {"hidden", &waitforblock}, - {"hidden", &waitforblockheight}, + {"blockchain", &waitfornewblock}, + {"blockchain", &waitforblock}, + {"blockchain", &waitforblockheight}, {"hidden", &syncwithvalidationinterfacequeue}, }; for (const auto& c : commands) { diff --git a/test/functional/rpc_blockchain.py b/test/functional/rpc_blockchain.py index 2976f9188ae..b0ac519de0d 100755 --- a/test/functional/rpc_blockchain.py +++ b/test/functional/rpc_blockchain.py @@ -579,7 +579,8 @@ class BlockchainTest(BitcoinTestFramework): node.reconsiderblock(rollback_hash) # The chain has probably already been restored by the time reconsiderblock returns, # but poll anyway. - self.wait_until(lambda: node.waitfornewblock(timeout=100)['hash'] == current_hash) + self.wait_until(lambda: node.waitfornewblock(current_tip=rollback_header['previousblockhash'])['hash'] == current_hash) + assert_raises_rpc_error(-1, "Negative timeout", node.waitfornewblock, -1) def _test_waitforblockheight(self):