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

Code before:
        throw std::runtime_error("Batch must be an array");
    }
    const size_t num {in.size()};
    std::vector<UniValue> batch(num);
    for (const UniValue& rec : in.getValues()) {
        if (!rec.isObject()) {
            throw std::runtime_error("Batch member must be an object");
        }
        size_t id = rec["id"].getInt<int>();
        if (id >= num) {
            throw std::runtime_error("Batch member id is larger than batch size");
        }
        batch[id] = rec;
    }
    return batch;
}

void JSONRPCRequest::parse(const UniValue& valRequest)
{
    // Parse request
    if (!valRequest.isObject())
        throw JSONRPCError(RPC_INVALID_REQUEST, "Invalid Request object");
    const UniValue& request = valRequest.get_obj();

    // Check for JSON-RPC 2.0 (default 1.1)
    // We must do this before looking for the id field
    m_json_version = JSONVersion::JSON_1_BTC;
    const UniValue& valJsonRPC = request.find_value("jsonrpc");
    if (!valJsonRPC.isNull()) {
        if (!valJsonRPC.isStr()) {
            throw JSONRPCError(RPC_INVALID_REQUEST, "jsonrpc field must be a string");
        }
        if (valJsonRPC.get_str() == "1.0") {
            m_json_version = JSONVersion::JSON_1_BTC;
        } else if (valJsonRPC.get_str() == "2.0") {
            m_json_version = JSONVersion::JSON_2_0;
        } else {
            throw JSONRPCError(RPC_INVALID_REQUEST, "JSON-RPC version not supported");
        }
    }

    // Parse id now so errors from here on will have the id.
    // In 1.1 a default null value is inserted if no id field was present in the request
    // In 2.0 a missing id field is a notification, but `"id": null` is not
    if (m_json_version != JSONVersion::JSON_2_0 || request.exists("id")) {
        id = request.find_value("id");
    } else {
        // Because we reuse JSONRPCRequest objects with multiple valRequests in
        // a batch request, we may need to reset this optional value.
        id.reset();
    }

Review comment (performance):
In commit "rpc: JSON-RPC 2.0 should not respond to "notifications"" (d62dcf81e152d03aa3cf02d0eb0e7e9c44807464)

This is just stated without a reason and doesn't appear to be true.

Also, checking for the version field after the id field is bad because it means if a batch request includes an unrecognized version number, the id is lost and there is no indication which call in the batch is causing the problem.

Code after:
}

std::vector<UniValue> JSONRPCProcessBatchReply(const UniValue& in)
{
    if (!in.isArray()) {
        throw std::runtime_error("Batch must be an array");
    }
    const size_t num {in.size()};
    std::vector<UniValue> batch(num);
    for (const UniValue& rec : in.getValues()) {
        if (!rec.isObject()) {
            throw std::runtime_error("Batch member must be an object");
        }
        size_t id = rec["id"].getInt<int>();
        if (id >= num) {
            throw std::runtime_error("Batch member id is larger than batch size");
        }
        batch[id] = rec;
    }
    return batch;
}

void JSONRPCRequest::parse(const UniValue& valRequest)
{
    // Parse request
    if (!valRequest.isObject())
        throw JSONRPCError(RPC_INVALID_REQUEST, "Invalid Request object");
    const UniValue& request = valRequest.get_obj();

    // Parse id now so errors from here on will have the id
    if (request.exists("id")) {
        id = request.find_value("id");
    } else {
        id = std::nullopt;
    }

    // Check for JSON-RPC 2.0 (default 1.1)
    m_json_version = JSONRPCVersion::V1_LEGACY;
    const UniValue& jsonrpc_version = request.find_value("jsonrpc");
    if (!jsonrpc_version.isNull()) {
        if (!jsonrpc_version.isStr()) {
            throw JSONRPCError(RPC_INVALID_REQUEST, "jsonrpc field must be a string");
        }
        // The "jsonrpc" key was added in the 2.0 spec, but some older documentation
        // incorrectly included {"jsonrpc":"1.0"} in a request object, so we
        // maintain that for backwards compatibility.
        if (jsonrpc_version.get_str() == "1.0") {
            m_json_version = JSONRPCVersion::V1_LEGACY;
        } else if (jsonrpc_version.get_str() == "2.0") {
            m_json_version = JSONRPCVersion::V2;
        } else {