Repository: bitcoin/bitcoin
File: src/rpc/blockchain.cpp (C++)

Code before:
        [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
    int timeout = 0;
    if (!request.params[0].isNull())
        timeout = request.params[0].getInt<int>();
    if (timeout < 0) throw JSONRPCError(RPC_MISC_ERROR, "Negative timeout");

    NodeContext& node = EnsureAnyNodeContext(request.context);
    Mining& miner = EnsureMining(node);

    // Abort if RPC came out of warmup too early
    BlockRef current_block{CHECK_NONFATAL(miner.getTip()).value()};
    std::optional<BlockRef> block = timeout ? miner.waitTipChanged(current_block.hash, std::chrono::milliseconds(timeout)) :
                                              miner.waitTipChanged(current_block.hash);

    // Return current block upon shutdown
    if (block) current_block = *block;

    UniValue ret(UniValue::VOBJ);
    ret.pushKV("hash", current_block.hash.GetHex());
    ret.pushKV("height", current_block.height);
    return ret;
},
    };
}

static RPCHelpMan waitforblock()
{
    return RPCHelpMan{"waitforblock",
                "\nWaits for a specific new block and returns useful info about it.\n"
                "\nReturns the current block on timeout or exit.\n"
                "\nMake sure to use no RPC timeout (bitcoin-cli -rpcclienttimeout=0)",
                {
                    {"blockhash", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "Block hash to wait for."},
                    {"timeout", RPCArg::Type::NUM, RPCArg::Default{0}, "Time in milliseconds to wait for a response. 0 indicates no timeout."},
                },
                RPCResult{
                    RPCResult::Type::OBJ, "", "",
                    {
                        {RPCResult::Type::STR_HEX, "hash", "The blockhash"},
                        {RPCResult::Type::NUM, "height", "Block height"},
                    }},
                RPCExamples{
                    HelpExampleCli("waitforblock", "\"0000000000079f8ef3d2c688c244eb7a4570b24c9ed7b4a8c619eb02596f8862\" 1000")
            + HelpExampleRpc("waitforblock", "\"0000000000079f8ef3d2c688c244eb7a4570b24c9ed7b4a8c619eb02596f8862\", 1000")
                },
        [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
    int timeout = 0;

    uint256 hash(ParseHashV(request.params[0], "blockhash"));

Review comment (bug):
I think it would be good to simplify all of this to just:

```c++
    // Use tip as reference block, unless a block hash was provided.
    uint256 current_tip{request.params[1].isNull()
        ? miner.getTip().value_or(BlockRef{}).hash
        : ParseHashV(request.params[1], "blockhash")};

    BlockRef block{timeout > 0
        ? miner.waitTipChanged(current_tip, std::chrono::milliseconds(timeout))
        : miner.waitTipChanged(current_tip)};
```

In addition to being simpler, this would fix a number of problems and inconveniences of the current implementation:

- If an unrecognized block hash was passed, it would make the RPC return the current block tip instead of throwing an exception.
- If no block hash was passed and tip was null, it would make the RPC wait for a block to be connected instead of throwing an exception.
- It would fix the bug marco described https://github.com/bitcoin/bitcoin/pull/30635#discussion_r1778228488 where this function does not wait in the GUI console.


Code after:
                },
        [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
    int timeout = 0;
    if (!request.params[0].isNull())
        timeout = request.params[0].getInt<int>();
    if (timeout < 0) throw JSONRPCError(RPC_MISC_ERROR, "Negative timeout");

    NodeContext& node = EnsureAnyNodeContext(request.context);
    Mining& miner = EnsureMining(node);

    // 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()};

    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<BlockRef> block = timeout ? miner.waitTipChanged(tip_hash, std::chrono::milliseconds(timeout)) :
                                              miner.waitTipChanged(tip_hash);

    // Return current block upon shutdown
    if (block) current_block = *block;

    UniValue ret(UniValue::VOBJ);
    ret.pushKV("hash", current_block.hash.GetHex());
    ret.pushKV("height", current_block.height);
    return ret;
},
    };
}

static RPCHelpMan waitforblock()
{
    return RPCHelpMan{"waitforblock",
                "\nWaits for a specific new block and returns useful info about it.\n"
                "\nReturns the current block on timeout or exit.\n"
                "\nMake sure to use no RPC timeout (bitcoin-cli -rpcclienttimeout=0)",
                {
                    {"blockhash", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "Block hash to wait for."},
                    {"timeout", RPCArg::Type::NUM, RPCArg::Default{0}, "Time in milliseconds to wait for a response. 0 indicates no timeout."},
                },
                RPCResult{
                    RPCResult::Type::OBJ, "", "",