diff --git a/src/bench/bench.h b/src/bench/bench.h index f530429780..b63f84caba 100644 --- a/src/bench/bench.h +++ b/src/bench/bench.h @@ -1,144 +1,144 @@ // Copyright (c) 2015-2016 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_BENCH_BENCH_H #define BITCOIN_BENCH_BENCH_H #include #include #include #include #include #include #include #include // Simple micro-benchmarking framework; API mostly matches a subset of the // Google Benchmark framework (see https://github.com/google/benchmark) // Why not use the Google Benchmark framework? Because adding Yet Another // Dependency (that uses cmake as its build system and has lots of features we // don't need) isn't worth it. /* * Usage: static void CODE_TO_TIME(benchmark::State& state) { ... do any setup needed... while (state.KeepRunning()) { ... do stuff you want to time... } ... do any cleanup needed... } // default to running benchmark for 5000 iterations BENCHMARK(CODE_TO_TIME, 5000); */ namespace benchmark { // In case high_resolution_clock is steady, prefer that, otherwise use // steady_clock. struct best_clock { using hi_res_clock = std::chrono::high_resolution_clock; using steady_clock = std::chrono::steady_clock; using type = std::conditional::type; }; using clock = best_clock::type; using time_point = clock::time_point; using duration = clock::duration; class Printer; class State { public: std::string m_name; uint64_t m_num_iters_left; const uint64_t m_num_iters; const uint64_t m_num_evals; std::vector m_elapsed_results; time_point m_start_time; bool UpdateTimer(time_point finish_time); State(std::string name, uint64_t num_evals, double num_iters, Printer &printer) : m_name(name), m_num_iters_left(0), m_num_iters(num_iters), m_num_evals(num_evals) {} inline bool KeepRunning() { if (m_num_iters_left--) { return true; } bool result = UpdateTimer(clock::now()); // measure again so runtime of UpdateTimer is not included m_start_time = clock::now(); return result; } }; typedef std::function BenchFunction; class BenchRunner { struct Bench { BenchFunction func; uint64_t num_iters_for_one_second; }; typedef std::map BenchmarkMap; static BenchmarkMap &benchmarks(); public: BenchRunner(std::string name, BenchFunction func, uint64_t num_iters_for_one_second); static void RunAll(Printer &printer, uint64_t num_evals, double scaling, const std::string &filter, bool is_list_only); }; // interface to output benchmark results. class Printer { public: virtual ~Printer() {} virtual void header() = 0; virtual void result(const State &state) = 0; virtual void footer() = 0; }; // default printer to console, shows min, max, median. class ConsolePrinter : public Printer { public: - void header(); - void result(const State &state); - void footer(); + void header() override; + void result(const State &state) override; + void footer() override; }; // creates box plot with plotly.js class PlotlyPrinter : public Printer { public: PlotlyPrinter(std::string plotly_url, int64_t width, int64_t height); - void header(); - void result(const State &state); - void footer(); + void header() override; + void result(const State &state) override; + void footer() override; private: std::string m_plotly_url; int64_t m_width; int64_t m_height; }; } // namespace benchmark // BENCHMARK(foo, num_iters_for_one_second) expands to: benchmark::BenchRunner // bench_11foo("foo", num_iterations); // Choose a num_iters_for_one_second that takes roughly 1 second. The goal is // that all benchmarks should take approximately // the same time, and scaling factor can be used that the total time is // appropriate for your system. #define BENCHMARK(n, num_iters_for_one_second) \ benchmark::BenchRunner BOOST_PP_CAT(bench_, BOOST_PP_CAT(__LINE__, n))( \ BOOST_PP_STRINGIZE(n), n, (num_iters_for_one_second)); #endif // BITCOIN_BENCH_BENCH_H diff --git a/src/test/validation_block_tests.cpp b/src/test/validation_block_tests.cpp index b557fdaea6..c9252c816a 100644 --- a/src/test/validation_block_tests.cpp +++ b/src/test/validation_block_tests.cpp @@ -1,209 +1,212 @@ // Copyright (c) 2018 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include #include #include #include #include #include #include #include #include #include #include #include #include struct RegtestingSetup : public TestingSetup { RegtestingSetup() : TestingSetup(CBaseChainParams::REGTEST) {} }; BOOST_FIXTURE_TEST_SUITE(validation_block_tests, RegtestingSetup) struct TestSubscriber : public CValidationInterface { uint256 m_expected_tip; TestSubscriber(uint256 tip) : m_expected_tip(tip) {} void UpdatedBlockTip(const CBlockIndex *pindexNew, - const CBlockIndex *pindexFork, bool fInitialDownload) { + const CBlockIndex *pindexFork, + bool fInitialDownload) override { BOOST_CHECK_EQUAL(m_expected_tip, pindexNew->GetBlockHash()); } - void BlockConnected(const std::shared_ptr &block, - const CBlockIndex *pindex, - const std::vector &txnConflicted) { + void + BlockConnected(const std::shared_ptr &block, + const CBlockIndex *pindex, + const std::vector &txnConflicted) override { BOOST_CHECK_EQUAL(m_expected_tip, block->hashPrevBlock); BOOST_CHECK_EQUAL(m_expected_tip, pindex->pprev->GetBlockHash()); m_expected_tip = block->GetHash(); } - void BlockDisconnected(const std::shared_ptr &block) { + void + BlockDisconnected(const std::shared_ptr &block) override { BOOST_CHECK_EQUAL(m_expected_tip, block->GetHash()); m_expected_tip = block->hashPrevBlock; } }; std::shared_ptr Block(const Config &config, const BlockHash &prev_hash) { static int i = 0; static uint64_t time = config.GetChainParams().GenesisBlock().nTime; CScript pubKey; pubKey << i++ << OP_TRUE; auto ptemplate = BlockAssembler(config, g_mempool).CreateNewBlock(pubKey); auto pblock = std::make_shared(ptemplate->block); pblock->hashPrevBlock = prev_hash; pblock->nTime = ++time; CMutableTransaction txCoinbase(*pblock->vtx[0]); txCoinbase.vout.resize(1); pblock->vtx[0] = MakeTransactionRef(std::move(txCoinbase)); return pblock; } std::shared_ptr FinalizeBlock(const Consensus::Params ¶ms, std::shared_ptr pblock) { pblock->hashMerkleRoot = BlockMerkleRoot(*pblock); while (!CheckProofOfWork(pblock->GetHash(), pblock->nBits, params)) { ++(pblock->nNonce); } return pblock; } // construct a valid block const std::shared_ptr GoodBlock(const Config &config, const BlockHash &prev_hash) { return FinalizeBlock(config.GetChainParams().GetConsensus(), Block(config, prev_hash)); } // construct an invalid block (but with a valid header) const std::shared_ptr BadBlock(const Config &config, const BlockHash &prev_hash) { auto pblock = Block(config, prev_hash); CMutableTransaction coinbase_spend; coinbase_spend.vin.push_back( CTxIn(COutPoint(pblock->vtx[0]->GetId(), 0), CScript(), 0)); coinbase_spend.vout.push_back(pblock->vtx[0]->vout[0]); CTransactionRef tx = MakeTransactionRef(coinbase_spend); pblock->vtx.push_back(tx); auto ret = FinalizeBlock(config.GetChainParams().GetConsensus(), pblock); return ret; } void BuildChain(const Config &config, const BlockHash &root, int height, const unsigned int invalid_rate, const unsigned int branch_rate, const unsigned int max_size, std::vector> &blocks) { if (height <= 0 || blocks.size() >= max_size) { return; } bool gen_invalid = InsecureRandRange(100) < invalid_rate; bool gen_fork = InsecureRandRange(100) < branch_rate; const std::shared_ptr pblock = gen_invalid ? BadBlock(config, root) : GoodBlock(config, root); blocks.push_back(pblock); if (!gen_invalid) { BuildChain(config, pblock->GetHash(), height - 1, invalid_rate, branch_rate, max_size, blocks); } if (gen_fork) { blocks.push_back(GoodBlock(config, root)); BuildChain(config, blocks.back()->GetHash(), height - 1, invalid_rate, branch_rate, max_size, blocks); } } BOOST_AUTO_TEST_CASE(processnewblock_signals_ordering) { GlobalConfig config; const CChainParams &chainParams = config.GetChainParams(); // build a large-ish chain that's likely to have some forks std::vector> blocks; while (blocks.size() < 50) { blocks.clear(); BuildChain(config, chainParams.GenesisBlock().GetHash(), 100, 15, 10, 500, blocks); } bool ignored; CValidationState state; std::vector headers; std::transform( blocks.begin(), blocks.end(), std::back_inserter(headers), [](std::shared_ptr b) { return b->GetBlockHeader(); }); // Process all the headers so we understand the toplogy of the chain BOOST_CHECK(ProcessNewBlockHeaders(config, headers, state)); // Connect the genesis block and drain any outstanding events ProcessNewBlock(config, std::make_shared(chainParams.GenesisBlock()), true, &ignored); SyncWithValidationInterfaceQueue(); // subscribe to events (this subscriber will validate event ordering) const CBlockIndex *initial_tip = nullptr; { LOCK(cs_main); initial_tip = ::ChainActive().Tip(); } TestSubscriber sub(initial_tip->GetBlockHash()); RegisterValidationInterface(&sub); // create a bunch of threads that repeatedly process a block generated above // at random this will create parallelism and randomness inside validation - // the ValidationInterface will subscribe to events generated during block // validation and assert on ordering invariance std::vector threads; for (int i = 0; i < 10; i++) { threads.emplace_back([&config, &blocks]() { bool tlignored; FastRandomContext insecure; for (int j = 0; j < 1000; j++) { auto block = blocks[insecure.randrange(blocks.size() - 1)]; ProcessNewBlock(config, block, true, &tlignored); } // to make sure that eventually we process the full chain - do it // here for (auto block : blocks) { if (block->vtx.size() == 1) { bool processed = ProcessNewBlock(config, block, true, &tlignored); assert(processed); } } }); } for (auto &t : threads) { t.join(); } while (GetMainSignals().CallbacksPending() > 0) { MilliSleep(100); } UnregisterValidationInterface(&sub); BOOST_CHECK_EQUAL(sub.m_expected_tip, ::ChainActive().Tip()->GetBlockHash()); } BOOST_AUTO_TEST_SUITE_END()