Skip to content

Commit 36f42e1

Browse files
author
MarcoFalke
committed
Merge #18037: Util: Allow scheduler to be mocked
8bca30e [rpc] expose ability to mock scheduler via the rpc (Amiti Uttarwar) 7c8b6e5 [lib] add scheduler to node context (Amiti Uttarwar) 930d837 [test] add chainparams property to indicate chain allows time mocking (Amiti Uttarwar) 1cd43e8 [test] unit test for new MockForward scheduler method (Amiti Uttarwar) a6f6359 [util] allow scheduler to be mocked (Amiti Uttarwar) Pull request description: This PR is to support functional tests by allowing the scheduler to be mocked via the RPC. It adds a `MockForward` method to the scheduler class that iterates through the task queue and reschedules them to be `delta_seconds` sooner. This is currently used to support functional testing of the "unbroadcast" set tracking in #18038. If this patch is accepted, it would also be useful to simplify the code in #16698. ACKs for top commit: MarcoFalke: ACK 8bca30e, only change is some style fixups 🕓 Tree-SHA512: 2a97fe8ade2b7fd1fb5cdfa1dcafb3227a377d7a847e3845a228bc119eb77824b4aefa43d922a06d583939b22725e223f308cf092961048079d36f6b1d9a639b
2 parents 179504c + 8bca30e commit 36f42e1

13 files changed

+134
-19
lines changed

src/chainparams.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,7 @@ class CMainParams : public CChainParams {
135135
fDefaultConsistencyChecks = false;
136136
fRequireStandard = true;
137137
m_is_test_chain = false;
138+
m_is_mockable_chain = false;
138139

139140
checkpointData = {
140141
{
@@ -231,7 +232,7 @@ class CTestNetParams : public CChainParams {
231232
fDefaultConsistencyChecks = false;
232233
fRequireStandard = false;
233234
m_is_test_chain = true;
234-
235+
m_is_mockable_chain = false;
235236

236237
checkpointData = {
237238
{
@@ -303,6 +304,7 @@ class CRegTestParams : public CChainParams {
303304
fDefaultConsistencyChecks = true;
304305
fRequireStandard = true;
305306
m_is_test_chain = true;
307+
m_is_mockable_chain = true;
306308

307309
checkpointData = {
308310
{

src/chainparams.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,8 @@ class CChainParams
6868
bool RequireStandard() const { return fRequireStandard; }
6969
/** If this chain is exclusively used for testing */
7070
bool IsTestChain() const { return m_is_test_chain; }
71+
/** If this chain allows time to be mocked */
72+
bool IsMockableChain() const { return m_is_mockable_chain; }
7173
uint64_t PruneAfterHeight() const { return nPruneAfterHeight; }
7274
/** Minimum free space (in GB) needed for data directory */
7375
uint64_t AssumedBlockchainSize() const { return m_assumed_blockchain_size; }
@@ -102,6 +104,7 @@ class CChainParams
102104
bool fDefaultConsistencyChecks;
103105
bool fRequireStandard;
104106
bool m_is_test_chain;
107+
bool m_is_mockable_chain;
105108
CCheckpointData checkpointData;
106109
ChainTxData chainTxData;
107110
};

src/init.cpp

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,6 @@ NODISCARD static bool CreatePidFile()
157157
static std::unique_ptr<ECCVerifyHandle> globalVerifyHandle;
158158

159159
static boost::thread_group threadGroup;
160-
static CScheduler scheduler;
161160

162161
void Interrupt(NodeContext& node)
163162
{
@@ -295,6 +294,7 @@ void Shutdown(NodeContext& node)
295294
globalVerifyHandle.reset();
296295
ECC_Stop();
297296
if (node.mempool) node.mempool = nullptr;
297+
node.scheduler.reset();
298298
LogPrintf("%s: done\n", __func__);
299299
}
300300

@@ -1268,16 +1268,19 @@ bool AppInitMain(NodeContext& node)
12681268
}
12691269
}
12701270

1271+
assert(!node.scheduler);
1272+
node.scheduler = MakeUnique<CScheduler>();
1273+
12711274
// Start the lightweight task scheduler thread
1272-
CScheduler::Function serviceLoop = std::bind(&CScheduler::serviceQueue, &scheduler);
1275+
CScheduler::Function serviceLoop = [&node]{ node.scheduler->serviceQueue(); };
12731276
threadGroup.create_thread(std::bind(&TraceThread<CScheduler::Function>, "scheduler", serviceLoop));
12741277

12751278
// Gather some entropy once per minute.
1276-
scheduler.scheduleEvery([]{
1279+
node.scheduler->scheduleEvery([]{
12771280
RandAddPeriodic();
12781281
}, 60000);
12791282

1280-
GetMainSignals().RegisterBackgroundSignalScheduler(scheduler);
1283+
GetMainSignals().RegisterBackgroundSignalScheduler(*node.scheduler);
12811284

12821285
// Create client interfaces for wallets that are supposed to be loaded
12831286
// according to -wallet and -disablewallet options. This only constructs
@@ -1327,7 +1330,7 @@ bool AppInitMain(NodeContext& node)
13271330
assert(!node.connman);
13281331
node.connman = std::unique_ptr<CConnman>(new CConnman(GetRand(std::numeric_limits<uint64_t>::max()), GetRand(std::numeric_limits<uint64_t>::max())));
13291332

1330-
node.peer_logic.reset(new PeerLogicValidation(node.connman.get(), node.banman.get(), scheduler));
1333+
node.peer_logic.reset(new PeerLogicValidation(node.connman.get(), node.banman.get(), *node.scheduler));
13311334
RegisterValidationInterface(node.peer_logic.get());
13321335

13331336
// sanitize comments per BIP-0014, format user agent and check total size
@@ -1819,7 +1822,7 @@ bool AppInitMain(NodeContext& node)
18191822
connOptions.m_specified_outgoing = connect;
18201823
}
18211824
}
1822-
if (!node.connman->Start(scheduler, connOptions)) {
1825+
if (!node.connman->Start(*node.scheduler, connOptions)) {
18231826
return false;
18241827
}
18251828

@@ -1848,11 +1851,11 @@ bool AppInitMain(NodeContext& node)
18481851
uiInterface.InitMessage(_("Done loading").translated);
18491852

18501853
for (const auto& client : node.chain_clients) {
1851-
client->start(scheduler);
1854+
client->start(*node.scheduler);
18521855
}
18531856

18541857
BanMan* banman = node.banman.get();
1855-
scheduler.scheduleEvery([banman]{
1858+
node.scheduler->scheduleEvery([banman]{
18561859
banman->DumpBanlist();
18571860
}, DUMP_BANS_INTERVAL * 1000);
18581861

src/node/context.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include <interfaces/chain.h>
99
#include <net.h>
1010
#include <net_processing.h>
11+
#include <scheduler.h>
1112

1213
NodeContext::NodeContext() {}
1314
NodeContext::~NodeContext() {}

src/node/context.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
class BanMan;
1212
class CConnman;
13+
class CScheduler;
1314
class CTxMemPool;
1415
class PeerLogicValidation;
1516
namespace interfaces {
@@ -34,6 +35,7 @@ struct NodeContext {
3435
std::unique_ptr<BanMan> banman;
3536
std::unique_ptr<interfaces::Chain> chain;
3637
std::vector<std::unique_ptr<interfaces::ChainClient>> chain_clients;
38+
std::unique_ptr<CScheduler> scheduler;
3739

3840
//! Declare default constructor and destructor that are not inline, so code
3941
//! instantiating the NodeContext struct doesn't need to #include class

src/rpc/client.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ class CRPCConvertParam
2727
static const CRPCConvertParam vRPCConvertParams[] =
2828
{
2929
{ "setmocktime", 0, "timestamp" },
30+
{ "mockscheduler", 0, "delta_time" },
3031
{ "utxoupdatepsbt", 1, "descriptors" },
3132
{ "generatetoaddress", 0, "nblocks" },
3233
{ "generatetoaddress", 2, "maxtries" },

src/rpc/misc.cpp

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,12 @@
55

66
#include <httpserver.h>
77
#include <key_io.h>
8+
#include <node/context.h>
89
#include <outputtype.h>
910
#include <rpc/blockchain.h>
1011
#include <rpc/server.h>
1112
#include <rpc/util.h>
13+
#include <scheduler.h>
1214
#include <script/descriptor.h>
1315
#include <util/check.h>
1416
#include <util/strencodings.h>
@@ -371,6 +373,36 @@ static UniValue setmocktime(const JSONRPCRequest& request)
371373
return NullUniValue;
372374
}
373375

376+
static UniValue mockscheduler(const JSONRPCRequest& request)
377+
{
378+
RPCHelpMan{"mockscheduler",
379+
"\nBump the scheduler into the future (-regtest only)\n",
380+
{
381+
{"delta_time", RPCArg::Type::NUM, RPCArg::Optional::NO, "Number of seconds to forward the scheduler into the future." },
382+
},
383+
RPCResults{},
384+
RPCExamples{""},
385+
}.Check(request);
386+
387+
if (!Params().IsMockableChain()) {
388+
throw std::runtime_error("mockscheduler is for regression testing (-regtest mode) only");
389+
}
390+
391+
// check params are valid values
392+
RPCTypeCheck(request.params, {UniValue::VNUM});
393+
int64_t delta_seconds = request.params[0].get_int64();
394+
if ((delta_seconds <= 0) || (delta_seconds > 3600)) {
395+
throw std::runtime_error("delta_time must be between 1 and 3600 seconds (1 hr)");
396+
}
397+
398+
// protect against null pointer dereference
399+
CHECK_NONFATAL(g_rpc_node);
400+
CHECK_NONFATAL(g_rpc_node->scheduler);
401+
g_rpc_node->scheduler->MockForward(boost::chrono::seconds(delta_seconds));
402+
403+
return NullUniValue;
404+
}
405+
374406
static UniValue RPCLockedMemoryInfo()
375407
{
376408
LockedPool::Stats stats = LockedPoolManager::Instance().stats();
@@ -575,6 +607,7 @@ static const CRPCCommand commands[] =
575607

576608
/* Not shown in help */
577609
{ "hidden", "setmocktime", &setmocktime, {"timestamp"}},
610+
{ "hidden", "mockscheduler", &mockscheduler, {"delta_time"}},
578611
{ "hidden", "echo", &echo, {"arg0","arg1","arg2","arg3","arg4","arg5","arg6","arg7","arg8","arg9"}},
579612
{ "hidden", "echojson", &echo, {"arg0","arg1","arg2","arg3","arg4","arg5","arg6","arg7","arg8","arg9"}},
580613
};

src/scheduler.cpp

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,28 @@ void CScheduler::scheduleFromNow(CScheduler::Function f, int64_t deltaMilliSecon
114114
schedule(f, boost::chrono::system_clock::now() + boost::chrono::milliseconds(deltaMilliSeconds));
115115
}
116116

117+
void CScheduler::MockForward(boost::chrono::seconds delta_seconds)
118+
{
119+
assert(delta_seconds.count() > 0 && delta_seconds < boost::chrono::hours{1});
120+
121+
{
122+
boost::unique_lock<boost::mutex> lock(newTaskMutex);
123+
124+
// use temp_queue to maintain updated schedule
125+
std::multimap<boost::chrono::system_clock::time_point, Function> temp_queue;
126+
127+
for (const auto& element : taskQueue) {
128+
temp_queue.emplace_hint(temp_queue.cend(), element.first - delta_seconds, element.second);
129+
}
130+
131+
// point taskQueue to temp_queue
132+
taskQueue = std::move(temp_queue);
133+
}
134+
135+
// notify that the taskQueue needs to be processed
136+
newTaskScheduled.notify_one();
137+
}
138+
117139
static void Repeat(CScheduler* s, CScheduler::Function f, int64_t deltaMilliSeconds)
118140
{
119141
f();

src/scheduler.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,13 @@ class CScheduler
5555
// need more accurate scheduling, don't use this method.
5656
void scheduleEvery(Function f, int64_t deltaMilliSeconds);
5757

58+
/**
59+
* Mock the scheduler to fast forward in time.
60+
* Iterates through items on taskQueue and reschedules them
61+
* to be delta_seconds sooner.
62+
*/
63+
void MockForward(boost::chrono::seconds delta_seconds);
64+
5865
// To keep things as simple as possible, there is no unschedule.
5966

6067
// Services the queue 'forever'. Should be run in a thread,

src/test/denialofservice_tests.cpp

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ BOOST_FIXTURE_TEST_SUITE(denialofservice_tests, TestingSetup)
7878
BOOST_AUTO_TEST_CASE(outbound_slow_chain_eviction)
7979
{
8080
auto connman = MakeUnique<CConnman>(0x1337, 0x1337);
81-
auto peerLogic = MakeUnique<PeerLogicValidation>(connman.get(), nullptr, scheduler);
81+
auto peerLogic = MakeUnique<PeerLogicValidation>(connman.get(), nullptr, *m_node.scheduler);
8282

8383
// Mock an outbound peer
8484
CAddress addr1(ip(0xa0b0c001), NODE_NONE);
@@ -148,7 +148,7 @@ static void AddRandomOutboundPeer(std::vector<CNode *> &vNodes, PeerLogicValidat
148148
BOOST_AUTO_TEST_CASE(stale_tip_peer_management)
149149
{
150150
auto connman = MakeUnique<CConnmanTest>(0x1337, 0x1337);
151-
auto peerLogic = MakeUnique<PeerLogicValidation>(connman.get(), nullptr, scheduler);
151+
auto peerLogic = MakeUnique<PeerLogicValidation>(connman.get(), nullptr, *m_node.scheduler);
152152

153153
const Consensus::Params& consensusParams = Params().GetConsensus();
154154
constexpr int max_outbound_full_relay = 8;
@@ -221,7 +221,7 @@ BOOST_AUTO_TEST_CASE(DoS_banning)
221221
{
222222
auto banman = MakeUnique<BanMan>(GetDataDir() / "banlist.dat", nullptr, DEFAULT_MISBEHAVING_BANTIME);
223223
auto connman = MakeUnique<CConnman>(0x1337, 0x1337);
224-
auto peerLogic = MakeUnique<PeerLogicValidation>(connman.get(), banman.get(), scheduler);
224+
auto peerLogic = MakeUnique<PeerLogicValidation>(connman.get(), banman.get(), *m_node.scheduler);
225225

226226
banman->ClearBanned();
227227
CAddress addr1(ip(0xa0b0c001), NODE_NONE);
@@ -276,7 +276,7 @@ BOOST_AUTO_TEST_CASE(DoS_banscore)
276276
{
277277
auto banman = MakeUnique<BanMan>(GetDataDir() / "banlist.dat", nullptr, DEFAULT_MISBEHAVING_BANTIME);
278278
auto connman = MakeUnique<CConnman>(0x1337, 0x1337);
279-
auto peerLogic = MakeUnique<PeerLogicValidation>(connman.get(), banman.get(), scheduler);
279+
auto peerLogic = MakeUnique<PeerLogicValidation>(connman.get(), banman.get(), *m_node.scheduler);
280280

281281
banman->ClearBanned();
282282
gArgs.ForceSetArg("-banscore", "111"); // because 11 is my favorite number
@@ -323,7 +323,7 @@ BOOST_AUTO_TEST_CASE(DoS_bantime)
323323
{
324324
auto banman = MakeUnique<BanMan>(GetDataDir() / "banlist.dat", nullptr, DEFAULT_MISBEHAVING_BANTIME);
325325
auto connman = MakeUnique<CConnman>(0x1337, 0x1337);
326-
auto peerLogic = MakeUnique<PeerLogicValidation>(connman.get(), banman.get(), scheduler);
326+
auto peerLogic = MakeUnique<PeerLogicValidation>(connman.get(), banman.get(), *m_node.scheduler);
327327

328328
banman->ClearBanned();
329329
int64_t nStartTime = GetTime();

0 commit comments

Comments
 (0)