ronantakizawa/codereview-bench huggingface dataset:
Repository: bitcoin/bitcoin
File: test/functional/feature_bind_extra.py (Python)

Code before:
        # Node2, no -bind=...=onion, thus no extra port for Tor target.
        self.expected.append(
            [
                [f"-bind=127.0.0.1:{port}"],
                [(loopback_ipv4, port)]
            ],
        )
        port += 1

        self.extra_args = list(map(lambda e: e[0], self.expected))
        self.setup_nodes()

    def run_test(self):
        for i, (args, expected_services) in enumerate(self.expected):
            self.log.info(f"Checking listening ports of node {i} with {args}")
            pid = self.nodes[i].process.pid
            binds = set(get_bind_addrs(pid))
            # Remove IPv6 addresses because on some CI environments "::1" is not configured
            # on the system (so our test_ipv6_local() would return False), but it is
            # possible to bind on "::". This makes it unpredictable whether to expect
            # that bitcoind has bound on "::1" (for RPC) and "::" (for P2P).
            ipv6_addr_len_bytes = 32
            binds = set(filter(lambda e: len(e[0]) != ipv6_addr_len_bytes, binds))
            # Remove RPC ports. They are not relevant for this test.
            binds = set(filter(lambda e: e[1] != rpc_port(i), binds))
            assert_equal(binds, set(self.expected[i][1]))

if __name__ == '__main__':
    BindExtraTest().main()


Review comment (suggestion):
Thanks for taking my suggestion in https://github.com/bitcoin/bitcoin/pull/22729#discussion_r1607299718! To increase readability one can also use the named loop-variable here, sorry I didn't mention it before:
```suggestion
            assert_equal(binds, set(expected_services))
```

Code after:
        # Node2, no -bind=...=onion, thus no extra port for Tor target.
        self.expected.append(
            [
                [f"-bind=127.0.0.1:{port}"],
                [(loopback_ipv4, port)]
            ],
        )
        port += 1

        self.extra_args = list(map(lambda e: e[0], self.expected))
        self.setup_nodes()

    def run_test(self):
        for i, (args, expected_services) in enumerate(self.expected):
            self.log.info(f"Checking listening ports of node {i} with {args}")
            pid = self.nodes[i].process.pid
            binds = set(get_bind_addrs(pid))
            # Remove IPv6 addresses because on some CI environments "::1" is not configured
            # on the system (so our test_ipv6_local() would return False), but it is
            # possible to bind on "::". This makes it unpredictable whether to expect
            # that bitcoind has bound on "::1" (for RPC) and "::" (for P2P).
            ipv6_addr_len_bytes = 32
            binds = set(filter(lambda e: len(e[0]) != ipv6_addr_len_bytes, binds))
            # Remove RPC ports. They are not relevant for this test.
            binds = set(filter(lambda e: e[1] != rpc_port(i), binds))
            assert_equal(binds, set(expected_services))

if __name__ == '__main__':
    BindExtraTest().main()
Repository: bitcoin/bitcoin
File: src/wallet/coincontrol.cpp (C++)

Code before:

#include <common/args.h>

namespace wallet {
CCoinControl::CCoinControl()
{
    m_avoid_partial_spends = gArgs.GetBoolArg("-avoidpartialspends", DEFAULT_AVOIDPARTIALSPENDS);
}

bool CCoinControl::HasSelected() const
{
    return !m_selected_inputs.empty();
}

bool CCoinControl::IsSelected(const COutPoint& output) const
{
    return m_selected_inputs.count(output) > 0;
}

bool CCoinControl::IsExternalSelected(const COutPoint& output) const
{
    return m_external_txouts.count(output) > 0;
}

std::optional<CTxOut> CCoinControl::GetExternalOutput(const COutPoint& outpoint) const
{
    const auto ext_it = m_external_txouts.find(outpoint);
    if (ext_it == m_external_txouts.end()) {
        return std::nullopt;
    }

    return std::make_optional(ext_it->second);
}

void CCoinControl::Select(const COutPoint& output)
{
    m_selected_inputs.insert(output);
}

void CCoinControl::SelectExternal(const COutPoint& outpoint, const CTxOut& txout)
{
    m_selected_inputs.insert(outpoint);
    m_external_txouts.emplace(outpoint, txout);
}

void CCoinControl::UnSelect(const COutPoint& output)
{
    m_selected_inputs.erase(output);
}

void CCoinControl::UnSelectAll()

Review comment (suggestion):
(in commit 2de8e8309ac0ee60742e45b8cc1342e61c725566)
nit: could be written as one-liner
```suggestion
    return (it != m_selected.end() && it->second.HasTxOut());
```
(same for `HasInputWeight`, though that gets replaced anyway in the next commit)

Code after:

#include <common/args.h>

namespace wallet {
CCoinControl::CCoinControl()
{
    m_avoid_partial_spends = gArgs.GetBoolArg("-avoidpartialspends", DEFAULT_AVOIDPARTIALSPENDS);
}

bool CCoinControl::HasSelected() const
{
    return !m_selected.empty();
}

bool CCoinControl::IsSelected(const COutPoint& outpoint) const
{
    return m_selected.count(outpoint) > 0;
}

bool CCoinControl::IsExternalSelected(const COutPoint& outpoint) const
{
    const auto it = m_selected.find(outpoint);
    return it != m_selected.end() && it->second.HasTxOut();
}

std::optional<CTxOut> CCoinControl::GetExternalOutput(const COutPoint& outpoint) const
{
    const auto it = m_selected.find(outpoint);
    if (it == m_selected.end() || !it->second.HasTxOut()) {
        return std::nullopt;
    }
    return it->second.GetTxOut();
}

PreselectedInput& CCoinControl::Select(const COutPoint& outpoint)
{
    auto& input = m_selected[outpoint];
    input.SetPosition(m_selection_pos);
    ++m_selection_pos;
    return input;
}
void CCoinControl::UnSelect(const COutPoint& outpoint)
{
    m_selected.erase(outpoint);
}

void CCoinControl::UnSelectAll()
{
    m_selected.clear();
}
Repository: bitcoin/bitcoin
File: src/wallet/coincontrol.cpp (C++)

Code before:
namespace wallet {
CCoinControl::CCoinControl()
{
    m_avoid_partial_spends = gArgs.GetBoolArg("-avoidpartialspends", DEFAULT_AVOIDPARTIALSPENDS);
}

bool CCoinControl::HasSelected() const
{
    return !m_selected_inputs.empty();
}

bool CCoinControl::IsSelected(const COutPoint& output) const
{
    return m_selected_inputs.count(output) > 0;
}

bool CCoinControl::IsExternalSelected(const COutPoint& output) const
{
    return m_external_txouts.count(output) > 0;
}

std::optional<CTxOut> CCoinControl::GetExternalOutput(const COutPoint& outpoint) const
{
    const auto ext_it = m_external_txouts.find(outpoint);
    if (ext_it == m_external_txouts.end()) {
        return std::nullopt;
    }

    return std::make_optional(ext_it->second);
}

void CCoinControl::Select(const COutPoint& output)
{
    m_selected_inputs.insert(output);
}

void CCoinControl::SelectExternal(const COutPoint& outpoint, const CTxOut& txout)
{
    m_selected_inputs.insert(outpoint);
    m_external_txouts.emplace(outpoint, txout);
}

void CCoinControl::UnSelect(const COutPoint& output)
{
    m_selected_inputs.erase(output);
}

void CCoinControl::UnSelectAll()
{
    m_selected_inputs.clear();
}

Review comment (suggestion):
in https://github.com/bitcoin/bitcoin/pull/25273/commits/7b5d22f44f515b9e6ab301ca4cb2a155796e6ebc:

Is there a situation where an internal input would have a value for `TxOut`, accidentally or purposefully? Only asking because it feels _slightly_ more fragile to rely only on this to determine if something is external or internal vs having two separate vectors, but I also don't have a good sense if what I'm worried about is even possible (internal having `TxOut` set) or if its even that bad if we confuse external and internal inputs :shrug: 

Code after:
namespace wallet {
CCoinControl::CCoinControl()
{
    m_avoid_partial_spends = gArgs.GetBoolArg("-avoidpartialspends", DEFAULT_AVOIDPARTIALSPENDS);
}

bool CCoinControl::HasSelected() const
{
    return !m_selected.empty();
}

bool CCoinControl::IsSelected(const COutPoint& outpoint) const
{
    return m_selected.count(outpoint) > 0;
}

bool CCoinControl::IsExternalSelected(const COutPoint& outpoint) const
{
    const auto it = m_selected.find(outpoint);
    return it != m_selected.end() && it->second.HasTxOut();
}

std::optional<CTxOut> CCoinControl::GetExternalOutput(const COutPoint& outpoint) const
{
    const auto it = m_selected.find(outpoint);
    if (it == m_selected.end() || !it->second.HasTxOut()) {
        return std::nullopt;
    }
    return it->second.GetTxOut();
}

PreselectedInput& CCoinControl::Select(const COutPoint& outpoint)
{
    auto& input = m_selected[outpoint];
    input.SetPosition(m_selection_pos);
    ++m_selection_pos;
    return input;
}
void CCoinControl::UnSelect(const COutPoint& outpoint)
{
    m_selected.erase(outpoint);
}

void CCoinControl::UnSelectAll()
{
    m_selected.clear();
}

std::vector<COutPoint> CCoinControl::ListSelected() const
{
    std::vector<COutPoint> outpoints;
Repository: bitcoin/bitcoin
File: src/wallet/coincontrol.cpp (C++)

Code before:
}

bool CCoinControl::IsSelected(const COutPoint& output) const
{
    return m_selected_inputs.count(output) > 0;
}

bool CCoinControl::IsExternalSelected(const COutPoint& output) const
{
    return m_external_txouts.count(output) > 0;
}

std::optional<CTxOut> CCoinControl::GetExternalOutput(const COutPoint& outpoint) const
{
    const auto ext_it = m_external_txouts.find(outpoint);
    if (ext_it == m_external_txouts.end()) {
        return std::nullopt;
    }

    return std::make_optional(ext_it->second);
}

void CCoinControl::Select(const COutPoint& output)
{
    m_selected_inputs.insert(output);
}

void CCoinControl::SelectExternal(const COutPoint& outpoint, const CTxOut& txout)
{
    m_selected_inputs.insert(outpoint);
    m_external_txouts.emplace(outpoint, txout);
}

void CCoinControl::UnSelect(const COutPoint& output)
{
    m_selected_inputs.erase(output);
}

void CCoinControl::UnSelectAll()
{
    m_selected_inputs.clear();
}

std::vector<COutPoint> CCoinControl::ListSelected() const
{
    return {m_selected_inputs.begin(), m_selected_inputs.end()};
}

void CCoinControl::SetInputWeight(const COutPoint& outpoint, int64_t weight)
{
    m_input_weights[outpoint] = weight;

Review comment (suggestion):
in https://github.com/bitcoin/bitcoin/pull/25273/commits/e53789afe60ddbcc014931467ae6b4cab15d0c67:

TIL `m_var++` passes the value, then increments.

nit: I think it's considered more idiomatic (and more readable imo) to do something like:

```suggestion
    input.SetPosition(m_selection_pos);
    m_selection_pos++;
```

Code after:
}

bool CCoinControl::IsSelected(const COutPoint& outpoint) const
{
    return m_selected.count(outpoint) > 0;
}

bool CCoinControl::IsExternalSelected(const COutPoint& outpoint) const
{
    const auto it = m_selected.find(outpoint);
    return it != m_selected.end() && it->second.HasTxOut();
}

std::optional<CTxOut> CCoinControl::GetExternalOutput(const COutPoint& outpoint) const
{
    const auto it = m_selected.find(outpoint);
    if (it == m_selected.end() || !it->second.HasTxOut()) {
        return std::nullopt;
    }
    return it->second.GetTxOut();
}

PreselectedInput& CCoinControl::Select(const COutPoint& outpoint)
{
    auto& input = m_selected[outpoint];
    input.SetPosition(m_selection_pos);
    ++m_selection_pos;
    return input;
}
void CCoinControl::UnSelect(const COutPoint& outpoint)
{
    m_selected.erase(outpoint);
}

void CCoinControl::UnSelectAll()
{
    m_selected.clear();
}

std::vector<COutPoint> CCoinControl::ListSelected() const
{
    std::vector<COutPoint> outpoints;
    std::transform(m_selected.begin(), m_selected.end(), std::back_inserter(outpoints),
        [](const std::map<COutPoint, PreselectedInput>::value_type& pair) {
            return pair.first;
        });
    return outpoints;
}

void CCoinControl::SetInputWeight(const COutPoint& outpoint, int64_t weight)
{
Repository: bitcoin/bitcoin
File: src/bench/checkqueue.cpp (C++)

Code before:
        explicit PrevectorJob(FastRandomContext& insecure_rand){
            p.resize(insecure_rand.randrange(PREVECTOR_SIZE*2));
        }
        bool operator()()
        {
            return true;
        }
    };
    CCheckQueue<PrevectorJob> queue {QUEUE_BATCH_SIZE};
    // The main thread should be counted to prevent thread oversubscription, and
    // to decrease the variance of benchmark results.
    queue.StartWorkerThreads(GetNumCores() - 1);

    // create all the data once, then submit copies in the benchmark.
    FastRandomContext insecure_rand(true);
    std::vector<std::vector<PrevectorJob>> vBatches(BATCHES);
    for (auto& vChecks : vBatches) {
        vChecks.reserve(BATCH_SIZE);
        for (size_t x = 0; x < BATCH_SIZE; ++x)
            vChecks.emplace_back(insecure_rand);
    }

    bench.minEpochIterations(10).batch(BATCH_SIZE * BATCHES).unit("job").run([&] {
        // Make insecure_rand here so that each iteration is identical.
        CCheckQueueControl<PrevectorJob> control(&queue);
        for (auto&& vChecks : vBatches) {
            control.Add(vChecks);
        }
        // control waits for completion by RAII, but
        // it is done explicitly here for clarity
        control.Wait();
    });
    queue.StopWorkerThreads();
    ECC_Stop();
}
BENCHMARK(CCheckQueueSpeedPrevectorJob, benchmark::PriorityLevel::HIGH);


Review comment (bug):
3f3479c5be1cd868d51c166a3808565afc56fb9f: This looks wrong? Previously it would create a copy. Now it steals the memory and then continues to iterate on the "empty" memory?

Code after:
            return true;
        }
    };
    CCheckQueue<PrevectorJob> queue {QUEUE_BATCH_SIZE};
    // The main thread should be counted to prevent thread oversubscription, and
    // to decrease the variance of benchmark results.
    queue.StartWorkerThreads(GetNumCores() - 1);

    // create all the data once, then submit copies in the benchmark.
    FastRandomContext insecure_rand(true);
    std::vector<std::vector<PrevectorJob>> vBatches(BATCHES);
    for (auto& vChecks : vBatches) {
        vChecks.reserve(BATCH_SIZE);
        for (size_t x = 0; x < BATCH_SIZE; ++x)
            vChecks.emplace_back(insecure_rand);
    }

    bench.minEpochIterations(10).batch(BATCH_SIZE * BATCHES).unit("job").run([&] {
        // Make insecure_rand here so that each iteration is identical.
        CCheckQueueControl<PrevectorJob> control(&queue);
        for (auto vChecks : vBatches) {
            control.Add(std::move(vChecks));
        }
        // control waits for completion by RAII, but
        // it is done explicitly here for clarity
        control.Wait();
    });
    queue.StopWorkerThreads();
    ECC_Stop();
}
BENCHMARK(CCheckQueueSpeedPrevectorJob, benchmark::PriorityLevel::HIGH);
Repository: bitcoin/bitcoin
File: src/rpc/server.cpp (C++)

Code before:

void SetRPCWarmupStatus(const std::string& newStatus)
{
    LOCK(g_rpc_warmup_mutex);
    rpcWarmupStatus = newStatus;
}

void SetRPCWarmupFinished()
{
    LOCK(g_rpc_warmup_mutex);
    assert(fRPCInWarmup);
    fRPCInWarmup = false;
}

bool RPCIsInWarmup(std::string *outStatus)
{
    LOCK(g_rpc_warmup_mutex);
    if (outStatus)
        *outStatus = rpcWarmupStatus;
    return fRPCInWarmup;
}

bool IsDeprecatedRPCEnabled(const std::string& method)
{
    const std::vector<std::string> enabled_methods = gArgs.GetArgs("-deprecatedrpc");

    return find(enabled_methods.begin(), enabled_methods.end(), method) != enabled_methods.end();
}

static UniValue JSONRPCExecOne(JSONRPCRequest jreq, const UniValue& req)
{
    UniValue rpc_result(UniValue::VOBJ);

    try {
        jreq.parse(req);

        UniValue result = tableRPC.execute(jreq);
        rpc_result = JSONRPCReplyObj(result, NullUniValue, jreq.id);
    }
    catch (const UniValue& objError)
    {
        rpc_result = JSONRPCReplyObj(NullUniValue, objError, jreq.id);
    }
    catch (const std::exception& e)
    {
        rpc_result = JSONRPCReplyObj(NullUniValue,
                                     JSONRPCError(RPC_PARSE_ERROR, e.what()), jreq.id);
    }

    return rpc_result;
}

Review comment (performance):
In commit "rpc: refactor single/batch requests" (fcc8cd08dbed9a44bab277ad267306dd2a9f5199)

Would be clearer if jreq were a const reference, since it is not modified

Code after:
{
    LOCK(g_rpc_warmup_mutex);
    rpcWarmupStatus = newStatus;
}

void SetRPCWarmupFinished()
{
    LOCK(g_rpc_warmup_mutex);
    assert(fRPCInWarmup);
    fRPCInWarmup = false;
}

bool RPCIsInWarmup(std::string *outStatus)
{
    LOCK(g_rpc_warmup_mutex);
    if (outStatus)
        *outStatus = rpcWarmupStatus;
    return fRPCInWarmup;
}

bool IsDeprecatedRPCEnabled(const std::string& method)
{
    const std::vector<std::string> enabled_methods = gArgs.GetArgs("-deprecatedrpc");

    return find(enabled_methods.begin(), enabled_methods.end(), method) != enabled_methods.end();
}

UniValue JSONRPCExec(const JSONRPCRequest& jreq, bool catch_errors)
{
    UniValue result;
    if (catch_errors) {
        try {
            result = tableRPC.execute(jreq);
        } catch (UniValue& e) {
            return JSONRPCReplyObj(NullUniValue, std::move(e), jreq.id, jreq.m_json_version);
        } catch (const std::exception& e) {
            return JSONRPCReplyObj(NullUniValue, JSONRPCError(RPC_MISC_ERROR, e.what()), jreq.id, jreq.m_json_version);
        }
    } else {
        result = tableRPC.execute(jreq);
    }

    return JSONRPCReplyObj(std::move(result), NullUniValue, jreq.id, jreq.m_json_version);
}

/**
 * Process named arguments into a vector of positional arguments, based on the
 * passed-in specification for the RPC call's arguments.
 */
static inline JSONRPCRequest transformNamedArguments(const JSONRPCRequest& in, const std::vector<std::pair<std::string, bool>>& argNames)
{
Repository: bitcoin/bitcoin
File: src/rpc/request.h (C/C++)

Code before:
// Copyright (c) 2010 Satoshi Nakamoto
// Copyright (c) 2009-2021 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#ifndef BITCOIN_RPC_REQUEST_H
#define BITCOIN_RPC_REQUEST_H

#include <any>
#include <string>

#include <univalue.h>

UniValue JSONRPCRequestObj(const std::string& strMethod, const UniValue& params, const UniValue& id);
UniValue JSONRPCReplyObj(const UniValue& result, const UniValue& error, const UniValue& id);
std::string JSONRPCReply(const UniValue& result, const UniValue& error, const UniValue& id);
UniValue JSONRPCError(int code, const std::string& message);

/** Generate a new RPC authentication cookie and write it to disk */
bool GenerateAuthCookie(std::string *cookie_out);
/** Read the RPC authentication cookie from disk */
bool GetAuthCookie(std::string *cookie_out);
/** Delete RPC authentication cookie from disk */
void DeleteAuthCookie();
/** Parse JSON-RPC batch reply into a vector */
std::vector<UniValue> JSONRPCProcessBatchReply(const UniValue& in);

class JSONRPCRequest
{
public:
    UniValue id;
    std::string strMethod;
    UniValue params;
    enum Mode { EXECUTE, GET_HELP, GET_ARGS } mode = EXECUTE;
    std::string URI;
    std::string authUser;
    std::string peerAddr;
    std::any context;

    void parse(const UniValue& valRequest);
};

#endif // BITCOIN_RPC_REQUEST_H


Review comment (suggestion):
In commit "rpc: JSON-RPC 2.0 should not respond to "notifications"" (bc2d55898b315d1ee80a70377d65f79c829246ed)

This does not seem correct. According to https://www.jsonrpc.org/specification#notification a notification is a 'Request object without an "id" member', not a request object with a `null` id member. It also explicitly says requests that have "Null as a value for the id member" are treated as "Responses with an unknown id" not notifications.

Probably a reasonable way to fix this would be to change the `JSONRPCRequest::id` type from `UniValue` to `std::optional<UniValue>` to distinction between a null id and a missing id.

Code after:

/** Generate a new RPC authentication cookie and write it to disk */
bool GenerateAuthCookie(std::string *cookie_out);
/** Read the RPC authentication cookie from disk */
bool GetAuthCookie(std::string *cookie_out);
/** Delete RPC authentication cookie from disk */
void DeleteAuthCookie();
/** Parse JSON-RPC batch reply into a vector */
std::vector<UniValue> JSONRPCProcessBatchReply(const UniValue& in);

class JSONRPCRequest
{
public:
    std::optional<UniValue> id = UniValue::VNULL;
    std::string strMethod;
    UniValue params;
    enum Mode { EXECUTE, GET_HELP, GET_ARGS } mode = EXECUTE;
    std::string URI;
    std::string authUser;
    std::string peerAddr;
    std::any context;
    JSONRPCVersion m_json_version = JSONRPCVersion::V1_LEGACY;

    void parse(const UniValue& valRequest);
    [[nodiscard]] bool IsNotification() const { return !id.has_value() && m_json_version == JSONRPCVersion::V2; };
};

#endif // BITCOIN_RPC_REQUEST_H
Repository: bitcoin/bitcoin
File: src/rpc/request.h (C/C++)

Code before:
// Copyright (c) 2010 Satoshi Nakamoto
// Copyright (c) 2009-2021 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#ifndef BITCOIN_RPC_REQUEST_H
#define BITCOIN_RPC_REQUEST_H

#include <any>
#include <string>

#include <univalue.h>

class JSONRPCRequest;
enum class JSONVersion {
    JSON_1_BTC,
    JSON_2_0
};

UniValue JSONRPCRequestObj(const std::string& strMethod, const UniValue& params, const UniValue& id);
UniValue JSONRPCReplyObj(UniValue result, UniValue error, const UniValue& id, JSONVersion json_version = JSONVersion::JSON_1_BTC);
std::string JSONRPCReply(const UniValue& result, const UniValue& error, const JSONRPCRequest& jreq);
UniValue JSONRPCError(int code, const std::string& message);

/** Generate a new RPC authentication cookie and write it to disk */
bool GenerateAuthCookie(std::string *cookie_out);
/** Read the RPC authentication cookie from disk */
bool GetAuthCookie(std::string *cookie_out);
/** Delete RPC authentication cookie from disk */
void DeleteAuthCookie();
/** Parse JSON-RPC batch reply into a vector */
std::vector<UniValue> JSONRPCProcessBatchReply(const UniValue& in);

class JSONRPCRequest
{
public:
    UniValue id;
    std::string strMethod;
    UniValue params;

Review comment (suggestion):
In commit "rpc: make error and result mutually exclusive in JSON-RPC 2.0 replies" (9cae84c18706ce3b23b6f31466553161112deb70)

This declaration appears to be unused

Code after:
// Copyright (c) 2010 Satoshi Nakamoto
// Copyright (c) 2009-2021 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#ifndef BITCOIN_RPC_REQUEST_H
#define BITCOIN_RPC_REQUEST_H

#include <any>
#include <optional>
#include <string>

#include <univalue.h>

enum class JSONRPCVersion {
    V1_LEGACY,
    V2
};

UniValue JSONRPCRequestObj(const std::string& strMethod, const UniValue& params, const UniValue& id);
UniValue JSONRPCReplyObj(UniValue result, UniValue error, std::optional<UniValue> id, JSONRPCVersion jsonrpc_version);
UniValue JSONRPCError(int code, const std::string& message);

/** Generate a new RPC authentication cookie and write it to disk */
bool GenerateAuthCookie(std::string *cookie_out);
/** Read the RPC authentication cookie from disk */
bool GetAuthCookie(std::string *cookie_out);
/** Delete RPC authentication cookie from disk */
void DeleteAuthCookie();
/** Parse JSON-RPC batch reply into a vector */
std::vector<UniValue> JSONRPCProcessBatchReply(const UniValue& in);

class JSONRPCRequest
{
public:
    std::optional<UniValue> id = UniValue::VNULL;
    std::string strMethod;
    UniValue params;
    enum Mode { EXECUTE, GET_HELP, GET_ARGS } mode = EXECUTE;