diff --git a/src/test/fuzz/cmpctblock.cpp b/src/test/fuzz/cmpctblock.cpp index e2aea86a89e..5a67e8d9c96 100644 --- a/src/test/fuzz/cmpctblock.cpp +++ b/src/test/fuzz/cmpctblock.cpp @@ -14,7 +14,6 @@ #include #include #include -#include #include #include #include @@ -26,20 +25,17 @@ #include #include #include -#include #include #include #include #include #include -#include #include #include #include #include #include #include -#include #include #include @@ -107,33 +103,6 @@ public: } }; -void ResetChainmanAndMempool(TestingSetup& setup) -{ - SetMockTime(Params().GenesisBlock().Time()); - - bilingual_str error{}; - setup.m_node.mempool.reset(); - setup.m_node.mempool = std::make_unique(MemPoolOptionsForTest(setup.m_node), error); - Assert(error.empty()); - - setup.m_node.chainman.reset(); - setup.m_make_chainman(); - setup.LoadVerifyActivateChainstate(); - - node::BlockCreateOptions options; - options.coinbase_output_script = P2WSH_OP_TRUE; - - g_mature_coinbase.clear(); - - for (int i = 0; i < 2 * COINBASE_MATURITY; ++i) { - COutPoint prevout{MineBlock(setup.m_node, options)}; - if (i < COINBASE_MATURITY) { - LOCK(cs_main); - CAmount subsidy{setup.m_node.chainman->ActiveChainstate().CoinsTip().GetCoin(prevout)->out.nValue}; - g_mature_coinbase.emplace_back(prevout, subsidy); - } - } -} //! Used to run tasks in a std::thread to avoid DEBUG_LOCKORDER false positives. class ImmediateBackgroundTaskRunner : public util::TaskRunnerInterface @@ -155,7 +124,7 @@ void initialize_cmpctblock() g_nBits = Params().GenesisBlock().nBits; // Replace validation_signals before creating chainman and mempool so they use it. testing_setup->m_node.validation_signals = std::make_unique(std::make_unique()); - ResetChainmanAndMempool(*g_setup); + g_mature_coinbase = ResetChainmanAndMempool(*g_setup); } FUZZ_TARGET(cmpctblock, .init = initialize_cmpctblock) @@ -163,7 +132,7 @@ FUZZ_TARGET(cmpctblock, .init = initialize_cmpctblock) SeedRandomStateForTest(SeedRand::ZEROS); FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); - FakeNodeClock clock{1610000000s}; + GetFakeNodeClock().set(1610000000s); FakeSteadyClock steady_clock; auto setup = g_setup; @@ -453,10 +422,10 @@ FUZZ_TARGET(cmpctblock, .init = initialize_cmpctblock) [&]() { // Set mock time randomly or to tip's time. if (fuzzed_data_provider.ConsumeBool()) { - clock.set(ConsumeTime(fuzzed_data_provider)); + GetFakeNodeClock().set(ConsumeTime(fuzzed_data_provider)); } else { const NodeSeconds tip_time = WITH_LOCK(::cs_main, return chainman.ActiveChain().Tip()->Time()); - clock.set(tip_time); + GetFakeNodeClock().set(tip_time); } sent_net_msg = false; @@ -509,6 +478,6 @@ FUZZ_TARGET(cmpctblock, .init = initialize_cmpctblock) if (initial_index_size != end_index_size || initial_sequence != end_sequence) { MakeRandDeterministicDANGEROUS(uint256::ZERO); - ResetChainmanAndMempool(*g_setup); + g_mature_coinbase = ResetChainmanAndMempool(*g_setup); } } diff --git a/src/test/fuzz/p2p_handshake.cpp b/src/test/fuzz/p2p_handshake.cpp index a71a61b85c9..9b657d68360 100644 --- a/src/test/fuzz/p2p_handshake.cpp +++ b/src/test/fuzz/p2p_handshake.cpp @@ -41,7 +41,7 @@ FUZZ_TARGET(p2p_handshake, .init = ::initialize) auto& node{g_setup->m_node}; auto& connman{static_cast(*node.connman)}; auto& chainman{static_cast(*node.chainman)}; - FakeNodeClock clock{1610000000s}; // any time to successfully reset ibd + FakeNodeClock clock{1610000000s}; // 2021-01-07, arbitrary FakeSteadyClock steady_clock; chainman.ResetIbd(); @@ -72,6 +72,10 @@ FUZZ_TARGET(p2p_handshake, .init = ::initialize) static_cast(fuzzed_data_provider.ConsumeIntegral())); } + // Toggle IBD from within the loop, so that some messages may be processed + // under IBD and the rest after leaving it. JumpOutOfIbd() latches, so guard + // it to call at most once. + bool jump_out_of_ibd{false}; LIMITED_WHILE (fuzzed_data_provider.ConsumeBool(), 100) { CNode& connection = *PickValue(fuzzed_data_provider, peers); if (connection.fDisconnect || connection.fSuccessfullyConnected) { @@ -80,6 +84,9 @@ FUZZ_TARGET(p2p_handshake, .init = ::initialize) continue; } + if (!jump_out_of_ibd) jump_out_of_ibd = fuzzed_data_provider.ConsumeBool(); + if (jump_out_of_ibd && chainman.IsInitialBlockDownload()) chainman.JumpOutOfIbd(); + clock += std::chrono::seconds{ fuzzed_data_provider.ConsumeIntegralInRange( -std::chrono::seconds{10min}.count(), // Allow mocktime to go backwards slightly diff --git a/src/test/fuzz/process_message.cpp b/src/test/fuzz/process_message.cpp index 7712ea6fcda..e4f0a36dbee 100644 --- a/src/test/fuzz/process_message.cpp +++ b/src/test/fuzz/process_message.cpp @@ -4,11 +4,9 @@ #include #include -#include #include #include #include -#include #include #include #include @@ -17,12 +15,12 @@ #include #include #include -#include #include #include #include #include #include +#include #include #include #include @@ -44,19 +42,10 @@ namespace { TestingSetup* g_setup; std::string_view LIMIT_TO_MESSAGE_TYPE{}; -void ResetChainman(TestingSetup& setup) -{ - SetMockTime(setup.m_node.chainman->GetParams().GenesisBlock().Time()); - setup.m_node.chainman.reset(); - setup.m_make_chainman(); - setup.LoadVerifyActivateChainstate(); - for (int i = 0; i < 2 * COINBASE_MATURITY; i++) { - node::BlockCreateOptions options; - MineBlock(setup.m_node, options); - } -} } // namespace +extern void MakeRandDeterministicDANGEROUS(const uint256& seed) noexcept; + void initialize_process_message() { if (const auto val{std::getenv("LIMIT_TO_MESSAGE_TYPE")}) { @@ -70,7 +59,7 @@ void initialize_process_message() {}), }; g_setup = testing_setup.get(); - ResetChainman(*g_setup); + ResetChainmanAndMempool(*g_setup); } FUZZ_TARGET(process_message, .init = initialize_process_message) @@ -83,7 +72,8 @@ FUZZ_TARGET(process_message, .init = initialize_process_message) connman.Reset(); auto& chainman{static_cast(*node.chainman)}; const auto block_index_size{WITH_LOCK(chainman.GetMutex(), return chainman.BlockIndex().size())}; - FakeNodeClock clock{1610000000s}; // any time to successfully reset ibd + const auto initial_sequence{WITH_LOCK(node.mempool->cs, return node.mempool->GetSequence())}; + GetFakeNodeClock().set(1610000000s); // 2021-01-07, arbitrary FakeSteadyClock steady_clock; chainman.ResetIbd(); chainman.DisableNextWrite(); @@ -117,7 +107,7 @@ FUZZ_TARGET(process_message, .init = initialize_process_message) connman.AddTestNode(p2p_node); FillNode(fuzzed_data_provider, connman, p2p_node); - clock.set(ConsumeTime(fuzzed_data_provider)); + GetFakeNodeClock().set(ConsumeTime(fuzzed_data_provider)); CSerializedNetMsg net_msg; net_msg.m_type = random_message_type; @@ -126,6 +116,10 @@ FUZZ_TARGET(process_message, .init = initialize_process_message) connman.FlushSendBuffer(p2p_node); (void)connman.ReceiveMsgFrom(p2p_node, std::move(net_msg)); + if (fuzzed_data_provider.ConsumeBool()) { + chainman.JumpOutOfIbd(); + } + bool more_work{true}; while (more_work) { p2p_node.fPauseSend = false; @@ -138,8 +132,10 @@ FUZZ_TARGET(process_message, .init = initialize_process_message) node.validation_signals->SyncWithValidationInterfaceQueue(); node.validation_signals->UnregisterValidationInterface(node.peerman.get()); node.connman->StopNodes(); - if (block_index_size != WITH_LOCK(chainman.GetMutex(), return chainman.BlockIndex().size())) { - // Reuse the global chainman, but reset it when it is dirty - ResetChainman(*g_setup); + const auto end_sequence{WITH_LOCK(node.mempool->cs, return node.mempool->GetSequence())}; + if (block_index_size != WITH_LOCK(chainman.GetMutex(), return chainman.BlockIndex().size()) || initial_sequence != end_sequence) { + // Reuse the global chainman and mempool, but reset them when dirty. + MakeRandDeterministicDANGEROUS(uint256::ZERO); + ResetChainmanAndMempool(*g_setup); } } diff --git a/src/test/fuzz/process_messages.cpp b/src/test/fuzz/process_messages.cpp index 89c03fa6183..983d2a5e14b 100644 --- a/src/test/fuzz/process_messages.cpp +++ b/src/test/fuzz/process_messages.cpp @@ -4,11 +4,9 @@ #include #include -#include #include #include #include -#include #include #include #include @@ -17,12 +15,13 @@ #include #include #include -#include #include #include #include #include #include +#include +#include #include #include #include @@ -38,19 +37,10 @@ namespace { TestingSetup* g_setup; -void ResetChainman(TestingSetup& setup) -{ - SetMockTime(setup.m_node.chainman->GetParams().GenesisBlock().Time()); - setup.m_node.chainman.reset(); - setup.m_make_chainman(); - setup.LoadVerifyActivateChainstate(); - node::BlockCreateOptions options; - for (int i = 0; i < 2 * COINBASE_MATURITY; i++) { - MineBlock(setup.m_node, options); - } -} } // namespace +extern void MakeRandDeterministicDANGEROUS(const uint256& seed) noexcept; + void initialize_process_messages() { static const auto testing_setup{ @@ -59,7 +49,7 @@ void initialize_process_messages() {}), }; g_setup = testing_setup.get(); - ResetChainman(*g_setup); + ResetChainmanAndMempool(*g_setup); } FUZZ_TARGET(process_messages, .init = initialize_process_messages) @@ -72,7 +62,8 @@ FUZZ_TARGET(process_messages, .init = initialize_process_messages) connman.Reset(); auto& chainman{static_cast(*node.chainman)}; const auto block_index_size{WITH_LOCK(chainman.GetMutex(), return chainman.BlockIndex().size())}; - FakeNodeClock clock{1610000000s}; // any time to successfully reset ibd + const auto initial_sequence{WITH_LOCK(node.mempool->cs, return node.mempool->GetSequence())}; + GetFakeNodeClock().set(1610000000s); // 2021-01-07, arbitrary FakeSteadyClock steady_clock; chainman.ResetIbd(); chainman.DisableNextWrite(); @@ -107,10 +98,16 @@ FUZZ_TARGET(process_messages, .init = initialize_process_messages) connman.AddTestNode(p2p_node); } + // Toggle IBD from within the loop, so that some messages may be processed + // under IBD and the rest after leaving it. JumpOutOfIbd() latches, so guard + // it to call at most once. + bool jump_out_of_ibd{false}; LIMITED_WHILE (fuzzed_data_provider.ConsumeBool(), 30) { + if (!jump_out_of_ibd) jump_out_of_ibd = fuzzed_data_provider.ConsumeBool(); + if (jump_out_of_ibd && chainman.IsInitialBlockDownload()) chainman.JumpOutOfIbd(); const std::string random_message_type{fuzzed_data_provider.ConsumeBytesAsString(CMessageHeader::MESSAGE_TYPE_SIZE).c_str()}; - clock.set(ConsumeTime(fuzzed_data_provider)); + GetFakeNodeClock().set(ConsumeTime(fuzzed_data_provider)); CSerializedNetMsg net_msg; net_msg.m_type = random_message_type; @@ -135,8 +132,10 @@ FUZZ_TARGET(process_messages, .init = initialize_process_messages) node.validation_signals->SyncWithValidationInterfaceQueue(); node.validation_signals->UnregisterValidationInterface(node.peerman.get()); node.connman->StopNodes(); - if (block_index_size != WITH_LOCK(chainman.GetMutex(), return chainman.BlockIndex().size())) { - // Reuse the global chainman, but reset it when it is dirty - ResetChainman(*g_setup); + const auto end_sequence{WITH_LOCK(node.mempool->cs, return node.mempool->GetSequence())}; + if (block_index_size != WITH_LOCK(chainman.GetMutex(), return chainman.BlockIndex().size()) || initial_sequence != end_sequence) { + // Reuse the global chainman and mempool, but reset them when dirty. + MakeRandDeterministicDANGEROUS(uint256::ZERO); + ResetChainmanAndMempool(*g_setup); } } diff --git a/src/test/fuzz/utxo_snapshot.cpp b/src/test/fuzz/utxo_snapshot.cpp index 815517bc55c..e4098457916 100644 --- a/src/test/fuzz/utxo_snapshot.cpp +++ b/src/test/fuzz/utxo_snapshot.cpp @@ -73,7 +73,7 @@ void initialize_chain() const auto params{CreateChainParams(ArgsManager{}, ChainType::REGTEST)}; static const auto chain{CreateBlockChain(2 * COINBASE_MATURITY, *params)}; g_chain = &chain; - SetMockTime(chain.back()->Time()); + GetFakeNodeClock().set(chain.back()->Time()); // Make sure we can generate a valid snapshot. sanity_check_snapshot(); @@ -104,7 +104,7 @@ void utxo_snapshot_fuzz(FuzzBufferType buffer) { SeedRandomStateForTest(SeedRand::ZEROS); FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); - FakeNodeClock clock{ConsumeTime(fuzzed_data_provider, /*min=*/1296688602)}; // regtest genesis block timestamp + GetFakeNodeClock().set(ConsumeTime(fuzzed_data_provider, /*min=*/1296688602)); // regtest genesis block timestamp auto& setup{*g_setup}; bool dirty_chainman{false}; // Reuse the global chainman, but reset it when it is dirty auto& chainman{*setup.m_node.chainman}; diff --git a/src/test/util/time.h b/src/test/util/time.h index 213d7b2764c..e20df3fa75b 100644 --- a/src/test/util/time.h +++ b/src/test/util/time.h @@ -76,4 +76,10 @@ public: void operator-=(std::chrono::seconds d) { set(m_t -= d); } }; +inline FakeNodeClock& GetFakeNodeClock() +{ + static FakeNodeClock g_fake_node_clock{0s}; + return g_fake_node_clock; +} + #endif // BITCOIN_TEST_UTIL_TIME_H diff --git a/src/test/util/validation.cpp b/src/test/util/validation.cpp index 7e09597ea34..61a16657f58 100644 --- a/src/test/util/validation.cpp +++ b/src/test/util/validation.cpp @@ -4,12 +4,25 @@ #include +#include +#include #include +#include +#include +#include +#include +#include +#include +#include #include #include #include #include +#include +#include +#include + using kernel::ChainstateRole; void TestBlockManager::CleanupForFuzzing() @@ -91,3 +104,31 @@ void TestChainstateManager::ResetBestInvalid() { m_best_invalid = nullptr; } + +std::vector> ResetChainmanAndMempool(TestingSetup& setup) +{ + GetFakeNodeClock().set(setup.m_node.chainman->GetParams().GenesisBlock().Time()); + + bilingual_str error{}; + setup.m_node.mempool.reset(); + setup.m_node.mempool = std::make_unique(MemPoolOptionsForTest(setup.m_node), error); + Assert(error.empty()); + + setup.m_node.chainman.reset(); + setup.m_make_chainman(); + setup.LoadVerifyActivateChainstate(); + + node::BlockCreateOptions options; + options.coinbase_output_script = P2WSH_OP_TRUE; + + std::vector> mature_coinbase; + for (int i = 0; i < 2 * COINBASE_MATURITY; ++i) { + COutPoint prevout{MineBlock(setup.m_node, options)}; + if (i < COINBASE_MATURITY) { + LOCK(cs_main); + CAmount subsidy{setup.m_node.chainman->ActiveChainstate().CoinsTip().GetCoin(prevout)->out.nValue}; + mature_coinbase.emplace_back(prevout, subsidy); + } + } + return mature_coinbase; +} diff --git a/src/test/util/validation.h b/src/test/util/validation.h index 1be3f6a64db..87a2d2efe1a 100644 --- a/src/test/util/validation.h +++ b/src/test/util/validation.h @@ -5,12 +5,18 @@ #ifndef BITCOIN_TEST_UTIL_VALIDATION_H #define BITCOIN_TEST_UTIL_VALIDATION_H +#include +#include #include +#include +#include + namespace node { class BlockManager; } class CValidationInterface; +struct TestingSetup; struct TestBlockManager : public node::BlockManager { /** Test-only method to clear internal state for fuzzing */ @@ -41,4 +47,6 @@ public: const CBlockIndex* pindex); }; +std::vector> ResetChainmanAndMempool(TestingSetup& setup); + #endif // BITCOIN_TEST_UTIL_VALIDATION_H