Repository: dogecoin/dogecoin
File: src/rpc/misc.cpp (C++)

Code before:

    // For now, don't change mocktime if we're in the middle of validation, as
    // this could have an effect on mempool time-based eviction, as well as
    // IsCurrentForFeeEstimation() and IsInitialBlockDownload().
    // TODO: figure out the right way to synchronize around mocktime, and
    // ensure all callsites of GetTime() are accessing this safely.
    LOCK(cs_main);

    RPCTypeCheck(request.params, boost::assign::list_of(UniValue::VNUM));
    SetMockTime(request.params[0].get_int64());

    return NullUniValue;
}

UniValue getmocktime(const JSONRPCRequest& request)
{
    if (request.fHelp || request.params.size() != 0)
        throw runtime_error(
            "getmocktime timestamp\n"
            "\nGet the local time to given timestamp (-regtest only)\n"
        );

    if (!Params().MineBlocksOnDemand())
        throw runtime_error("getmocktime for regression testing (-regtest mode) only");

    LOCK(cs_main);

    RPCTypeCheck(request.params, boost::assign::list_of(UniValue::VNUM));
    UniValue result(UniValue::VOBJ);
    result.pushKV("timestamp", GetMockTime());
    return result;
}

static UniValue RPCLockedMemoryInfo()
{
    LockedPool::Stats stats = LockedPoolManager::Instance().stats();
    UniValue obj(UniValue::VOBJ);
    obj.pushKV("used", uint64_t(stats.used));
    obj.pushKV("free", uint64_t(stats.free));
    obj.pushKV("total", uint64_t(stats.total));
    obj.pushKV("locked", uint64_t(stats.locked));
    obj.pushKV("chunks_used", uint64_t(stats.chunks_used));
    obj.pushKV("chunks_free", uint64_t(stats.chunks_free));
    return obj;
}

UniValue getmemoryinfo(const JSONRPCRequest& request)
{
    /* Please, avoid using the word "pool" here in the RPC interface or help,
     * as users will undoubtedly confuse it with the other "memory pool"
     */

Review comment (bug):
Locking `cs_main` here means that when a node is busy (processing messages for the most part), this RPC call locks the thread until it's freed. At the same time, we also lock it when doing `setmocktime`, so if we rapidly call one after the other, we may lock 2 RPC threads. This will create a race condition between the 2 RPC threads and we'd need a fifo queue to preserve call order.

To not have to introduce a lot of queue logic (and with that attack surface), I think that if we document the intended use of this function (a means to check whether a specific call to `setmocktime` has been processed during automated tests, and blocking further execution of that test until that condition is met) then we could remove the lock and not write a queue system.

What I think the intended use is:
1. call `setmocktime 123`
2. call `getmocktime` in a loop until it returns 123, or times out

Using test framework:

```python
node.setmocktime(123) # set the mocktime
def mocktime_is_set():
    return node.getmocktime()['timestamp'] == 123 # lambda checks if the specific mocktime has been set 
assert wait_until(mocktime_is_set, timeout=60) # execute the lambda repeatedly for at most 60 seconds
```

Code after:

    // For now, don't change mocktime if we're in the middle of validation, as
    // this could have an effect on mempool time-based eviction, as well as
    // IsCurrentForFeeEstimation() and IsInitialBlockDownload().
    // TODO: figure out the right way to synchronize around mocktime, and
    // ensure all callsites of GetTime() are accessing this safely.
    LOCK(cs_main);

    RPCTypeCheck(request.params, boost::assign::list_of(UniValue::VNUM));
    SetMockTime(request.params[0].get_int64());

    return NullUniValue;
}

UniValue getmocktime(const JSONRPCRequest& request)
{
    if (request.fHelp || request.params.size() != 0)
        throw runtime_error(
            "getmocktime\n"
            "\nGet the current mocktime (-regtest only)\n"
            "Note: this is an asynchronous read, and does not take into account any\n"
            "setmocktime calls that are pending."
            "\nResult:\n"
            "\"timestamp\"          (int64) The current mocktime (0 = no mocktime is set)\n"
            "\nExamples:\n"
            + HelpExampleCli("getmocktime", "")
            + HelpExampleRpc("getmocktime", "")
        );

    UniValue result(GetMockTime());
    return result;
}

static UniValue RPCLockedMemoryInfo()
{
    LockedPool::Stats stats = LockedPoolManager::Instance().stats();
    UniValue obj(UniValue::VOBJ);
    obj.pushKV("used", uint64_t(stats.used));
    obj.pushKV("free", uint64_t(stats.free));
    obj.pushKV("total", uint64_t(stats.total));
    obj.pushKV("locked", uint64_t(stats.locked));
    obj.pushKV("chunks_used", uint64_t(stats.chunks_used));
    obj.pushKV("chunks_free", uint64_t(stats.chunks_free));
    return obj;
}

UniValue getmemoryinfo(const JSONRPCRequest& request)
{
    /* Please, avoid using the word "pool" here in the RPC interface or help,
     * as users will undoubtedly confuse it with the other "memory pool"
     */