diff --git a/src/bench/block_assemble.cpp b/src/bench/block_assemble.cpp index 4c097961a..3c319d6df 100644 --- a/src/bench/block_assemble.cpp +++ b/src/bench/block_assemble.cpp @@ -1,63 +1,56 @@ // Copyright (c) 2011-2017 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 <bench/bench.h> #include <config.h> #include <consensus/validation.h> #include <script/standard.h> #include <test/util/mining.h> #include <test/util/setup_common.h> #include <test/util/wallet.h> #include <txmempool.h> #include <validation.h> #include <vector> static void AssembleBlock(benchmark::Bench &bench) { const Config &config = GetConfig(); - TestingSetup test_setup{ - CBaseChainParams::REGTEST, - /* extra_args */ - { - "-nodebuglogfile", - "-nodebug", - }, - }; + const auto test_setup = MakeNoLogFileContext<const TestingSetup>(); const CScript redeemScript = CScript() << OP_DROP << OP_TRUE; const CScript SCRIPT_PUB = CScript() << OP_HASH160 << ToByteVector(CScriptID(redeemScript)) << OP_EQUAL; const CScript scriptSig = CScript() << std::vector<uint8_t>(100, 0xff) << ToByteVector(redeemScript); // Collect some loose transactions that spend the coinbases of our mined // blocks constexpr size_t NUM_BLOCKS{200}; std::array<CTransactionRef, NUM_BLOCKS - COINBASE_MATURITY + 1> txs; for (size_t b = 0; b < NUM_BLOCKS; ++b) { CMutableTransaction tx; - tx.vin.push_back(MineBlock(config, test_setup.m_node, SCRIPT_PUB)); + tx.vin.push_back(MineBlock(config, test_setup->m_node, SCRIPT_PUB)); tx.vin.back().scriptSig = scriptSig; tx.vout.emplace_back(1337 * SATOSHI, SCRIPT_PUB); if (NUM_BLOCKS - b >= COINBASE_MATURITY) { txs.at(b) = MakeTransactionRef(tx); } } { LOCK(::cs_main); for (const auto &txr : txs) { const MempoolAcceptResult res = - test_setup.m_node.chainman->ProcessTransaction(txr); + test_setup->m_node.chainman->ProcessTransaction(txr); assert(res.m_result_type == MempoolAcceptResult::ResultType::VALID); } } - bench.run([&] { PrepareBlock(config, test_setup.m_node, SCRIPT_PUB); }); + bench.run([&] { PrepareBlock(config, test_setup->m_node, SCRIPT_PUB); }); } BENCHMARK(AssembleBlock); diff --git a/src/bench/duplicate_inputs.cpp b/src/bench/duplicate_inputs.cpp index 74cab476b..2d88b359e 100644 --- a/src/bench/duplicate_inputs.cpp +++ b/src/bench/duplicate_inputs.cpp @@ -1,79 +1,73 @@ // Copyright (c) 2011-2019 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 <bench/bench.h> #include <chain.h> #include <chainparams.h> #include <config.h> #include <consensus/amount.h> #include <consensus/merkle.h> #include <consensus/validation.h> #include <pow/pow.h> #include <random.h> #include <script/scriptcache.h> #include <test/util/setup_common.h> #include <txmempool.h> #include <validation.h> static void DuplicateInputs(benchmark::Bench &bench) { - TestingSetup test_setup{ - CBaseChainParams::REGTEST, - /* extra_args */ - { - "-nodebuglogfile", - "-nodebug", - }, - }; + const auto testing_setup = MakeNoLogFileContext<const TestingSetup>(); const CScript SCRIPT_PUB{CScript(OP_TRUE)}; const CChainParams &chainParams = Params(); const Consensus::Params &consensusParams = chainParams.GetConsensus(); CBlock block{}; CMutableTransaction coinbaseTx{}; CMutableTransaction naughtyTx{}; LOCK(cs_main); - CBlockIndex *pindexPrev = test_setup.m_node.chainman->ActiveChain().Tip(); + CBlockIndex *pindexPrev = + testing_setup->m_node.chainman->ActiveChain().Tip(); assert(pindexPrev != nullptr); block.nBits = GetNextWorkRequired(pindexPrev, &block, chainParams); block.nNonce = 0; auto nHeight = pindexPrev->nHeight + 1; // Make a coinbase TX coinbaseTx.vin.resize(1); coinbaseTx.vin[0].prevout = COutPoint(); coinbaseTx.vout.resize(1); coinbaseTx.vout[0].scriptPubKey = SCRIPT_PUB; coinbaseTx.vout[0].nValue = GetBlockSubsidy(nHeight, consensusParams); coinbaseTx.vin[0].scriptSig = CScript() << nHeight << OP_0; naughtyTx.vout.resize(1); naughtyTx.vout[0].nValue = Amount::zero(); naughtyTx.vout[0].scriptPubKey = SCRIPT_PUB; uint64_t n_inputs = ((MAX_TX_SIZE - CTransaction(naughtyTx).GetTotalSize()) / 41) - 100; for (uint64_t x = 0; x < (n_inputs - 1); ++x) { naughtyTx.vin.emplace_back(TxId(GetRandHash()), 0, CScript(), 0); } naughtyTx.vin.emplace_back(naughtyTx.vin.back()); block.vtx.push_back(MakeTransactionRef(std::move(coinbaseTx))); block.vtx.push_back(MakeTransactionRef(std::move(naughtyTx))); block.hashMerkleRoot = BlockMerkleRoot(block); bench.run([&] { BlockValidationState cvstate{}; assert(!CheckBlock(block, cvstate, consensusParams, BlockValidationOptions(GetConfig()) .withCheckPoW(false) .withCheckMerkleRoot(false))); assert(cvstate.GetRejectReason() == "bad-txns-inputs-duplicate"); }); } BENCHMARK(DuplicateInputs); diff --git a/src/bench/mempool_eviction.cpp b/src/bench/mempool_eviction.cpp index 94fb28b0c..d5660aaf3 100644 --- a/src/bench/mempool_eviction.cpp +++ b/src/bench/mempool_eviction.cpp @@ -1,129 +1,122 @@ // Copyright (c) 2011-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. #include <bench/bench.h> #include <consensus/amount.h> #include <policy/policy.h> #include <test/util/setup_common.h> #include <txmempool.h> static void AddTx(const CTransactionRef &tx, const Amount &nFee, CTxMemPool &pool) EXCLUSIVE_LOCKS_REQUIRED(cs_main, pool.cs) { int64_t nTime = 0; unsigned int nHeight = 1; bool spendsCoinbase = false; unsigned int nSigChecks = 1; LockPoints lp; pool.addUnchecked(CTxMemPoolEntryRef::make(tx, nFee, nTime, nHeight, spendsCoinbase, nSigChecks, lp)); } // Right now this is only testing eviction performance in an extremely small // mempool. Code needs to be written to generate a much wider variety of // unique transactions for a more meaningful performance measurement. static void MempoolEviction(benchmark::Bench &bench) { - TestingSetup test_setup{ - CBaseChainParams::REGTEST, - /* extra_args */ - { - "-nodebuglogfile", - "-nodebug", - }, - }; + const auto testing_setup = MakeNoLogFileContext<const TestingSetup>(); CMutableTransaction tx1 = CMutableTransaction(); tx1.vin.resize(1); tx1.vin[0].scriptSig = CScript() << OP_1; tx1.vout.resize(1); tx1.vout[0].scriptPubKey = CScript() << OP_1 << OP_EQUAL; tx1.vout[0].nValue = 10 * COIN; CMutableTransaction tx2 = CMutableTransaction(); tx2.vin.resize(1); tx2.vin[0].scriptSig = CScript() << OP_2; tx2.vout.resize(1); tx2.vout[0].scriptPubKey = CScript() << OP_2 << OP_EQUAL; tx2.vout[0].nValue = 10 * COIN; CMutableTransaction tx3 = CMutableTransaction(); tx3.vin.resize(1); tx3.vin[0].prevout = COutPoint(tx2.GetId(), 0); tx3.vin[0].scriptSig = CScript() << OP_2; tx3.vout.resize(1); tx3.vout[0].scriptPubKey = CScript() << OP_3 << OP_EQUAL; tx3.vout[0].nValue = 10 * COIN; CMutableTransaction tx4 = CMutableTransaction(); tx4.vin.resize(2); tx4.vin[0].prevout = COutPoint(); tx4.vin[0].scriptSig = CScript() << OP_4; tx4.vin[1].prevout = COutPoint(); tx4.vin[1].scriptSig = CScript() << OP_4; tx4.vout.resize(2); tx4.vout[0].scriptPubKey = CScript() << OP_4 << OP_EQUAL; tx4.vout[0].nValue = 10 * COIN; tx4.vout[1].scriptPubKey = CScript() << OP_4 << OP_EQUAL; tx4.vout[1].nValue = 10 * COIN; CMutableTransaction tx5 = CMutableTransaction(); tx5.vin.resize(2); tx5.vin[0].prevout = COutPoint(tx4.GetId(), 0); tx5.vin[0].scriptSig = CScript() << OP_4; tx5.vin[1].prevout = COutPoint(); tx5.vin[1].scriptSig = CScript() << OP_5; tx5.vout.resize(2); tx5.vout[0].scriptPubKey = CScript() << OP_5 << OP_EQUAL; tx5.vout[0].nValue = 10 * COIN; tx5.vout[1].scriptPubKey = CScript() << OP_5 << OP_EQUAL; tx5.vout[1].nValue = 10 * COIN; CMutableTransaction tx6 = CMutableTransaction(); tx6.vin.resize(2); tx6.vin[0].prevout = COutPoint(tx4.GetId(), 1); tx6.vin[0].scriptSig = CScript() << OP_4; tx6.vin[1].prevout = COutPoint(); tx6.vin[1].scriptSig = CScript() << OP_6; tx6.vout.resize(2); tx6.vout[0].scriptPubKey = CScript() << OP_6 << OP_EQUAL; tx6.vout[0].nValue = 10 * COIN; tx6.vout[1].scriptPubKey = CScript() << OP_6 << OP_EQUAL; tx6.vout[1].nValue = 10 * COIN; CMutableTransaction tx7 = CMutableTransaction(); tx7.vin.resize(2); tx7.vin[0].prevout = COutPoint(tx5.GetId(), 0); tx7.vin[0].scriptSig = CScript() << OP_5; tx7.vin[1].prevout = COutPoint(tx6.GetId(), 0); tx7.vin[1].scriptSig = CScript() << OP_6; tx7.vout.resize(2); tx7.vout[0].scriptPubKey = CScript() << OP_7 << OP_EQUAL; tx7.vout[0].nValue = 10 * COIN; tx7.vout[1].scriptPubKey = CScript() << OP_7 << OP_EQUAL; tx7.vout[1].nValue = 10 * COIN; - CTxMemPool &pool = *Assert(test_setup.m_node.mempool); + CTxMemPool &pool = *Assert(testing_setup->m_node.mempool); LOCK2(cs_main, pool.cs); // Create transaction references outside the "hot loop" const CTransactionRef tx1_r{MakeTransactionRef(tx1)}; const CTransactionRef tx2_r{MakeTransactionRef(tx2)}; const CTransactionRef tx3_r{MakeTransactionRef(tx3)}; const CTransactionRef tx4_r{MakeTransactionRef(tx4)}; const CTransactionRef tx5_r{MakeTransactionRef(tx5)}; const CTransactionRef tx6_r{MakeTransactionRef(tx6)}; const CTransactionRef tx7_r{MakeTransactionRef(tx7)}; bench.run([&]() NO_THREAD_SAFETY_ANALYSIS { AddTx(tx1_r, 10000 * SATOSHI, pool); AddTx(tx2_r, 5000 * SATOSHI, pool); AddTx(tx3_r, 20000 * SATOSHI, pool); AddTx(tx4_r, 7000 * SATOSHI, pool); AddTx(tx5_r, 1000 * SATOSHI, pool); AddTx(tx6_r, 1100 * SATOSHI, pool); AddTx(tx7_r, 9000 * SATOSHI, pool); pool.TrimToSize(pool.DynamicMemoryUsage() * 3 / 4); pool.TrimToSize(GetSerializeSize(*tx1_r, PROTOCOL_VERSION)); }); } BENCHMARK(MempoolEviction); diff --git a/src/bench/mempool_stress.cpp b/src/bench/mempool_stress.cpp index aa300311c..1c5934cdc 100644 --- a/src/bench/mempool_stress.cpp +++ b/src/bench/mempool_stress.cpp @@ -1,131 +1,131 @@ // Copyright (c) 2011-2019 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 <bench/bench.h> #include <policy/policy.h> #include <random.h> #include <test/util/setup_common.h> #include <txmempool.h> #include <validation.h> #include <vector> static void AddTx(const CTransactionRef &tx, CTxMemPool &pool) EXCLUSIVE_LOCKS_REQUIRED(cs_main, pool.cs) { int64_t nTime = 0; unsigned int nHeight = 1; bool spendsCoinbase = false; unsigned int sigChecks = 1; LockPoints lp; pool.addUnchecked(CTxMemPoolEntryRef::make( tx, 1000 * SATOSHI, nTime, nHeight, spendsCoinbase, sigChecks, lp)); } struct Available { CTransactionRef ref; size_t vin_left{0}; size_t tx_count; Available(CTransactionRef &_ref, size_t _tx_count) : ref(_ref), tx_count(_tx_count) {} }; static std::vector<CTransactionRef> CreateOrderedCoins(FastRandomContext &det_rand, int childTxs, int min_ancestors) { std::vector<Available> available_coins; std::vector<CTransactionRef> ordered_coins; // Create some base transactions size_t tx_counter = 1; for (auto x = 0; x < 100; ++x) { CMutableTransaction tx = CMutableTransaction(); tx.vin.resize(1); tx.vin[0].scriptSig = CScript() << CScriptNum(tx_counter); tx.vout.resize(det_rand.randrange(10) + 2); for (auto &out : tx.vout) { out.scriptPubKey = CScript() << CScriptNum(tx_counter) << OP_EQUAL; out.nValue = 10 * COIN; } ordered_coins.emplace_back(MakeTransactionRef(tx)); available_coins.emplace_back(ordered_coins.back(), tx_counter++); } for (auto x = 0; x < childTxs && !available_coins.empty(); ++x) { CMutableTransaction tx = CMutableTransaction(); size_t n_ancestors = det_rand.randrange(10) + 1; for (size_t ancestor = 0; ancestor < n_ancestors && !available_coins.empty(); ++ancestor) { size_t idx = det_rand.randrange(available_coins.size()); Available coin = available_coins[idx]; TxId txid = coin.ref->GetId(); // biased towards taking min_ancestors parents, but maybe more size_t n_to_take = det_rand.randrange(2) == 0 ? min_ancestors : min_ancestors + det_rand.randrange(coin.ref->vout.size() - coin.vin_left); for (size_t i = 0; i < n_to_take; ++i) { tx.vin.emplace_back(); tx.vin.back().prevout = COutPoint(txid, coin.vin_left++); tx.vin.back().scriptSig = CScript() << coin.tx_count; } if (coin.vin_left == coin.ref->vin.size()) { coin = available_coins.back(); available_coins.pop_back(); } tx.vout.resize(det_rand.randrange(10) + 2); for (auto &out : tx.vout) { out.scriptPubKey = CScript() << CScriptNum(tx_counter) << OP_EQUAL; out.nValue = 10 * COIN; } } ordered_coins.emplace_back(MakeTransactionRef(tx)); available_coins.emplace_back(ordered_coins.back(), tx_counter++); } return ordered_coins; } static void ComplexMemPool(benchmark::Bench &bench) { FastRandomContext det_rand{true}; int childTxs = 800; if (bench.complexityN() > 1) { childTxs = static_cast<int>(bench.complexityN()); } std::vector<CTransactionRef> ordered_coins = CreateOrderedCoins(det_rand, childTxs, /* min_ancestors */ 1); - TestingSetup test_setup; - + const auto testing_setup = + MakeNoLogFileContext<const TestingSetup>(CBaseChainParams::MAIN); CTxMemPool pool; LOCK2(cs_main, pool.cs); bench.run([&]() NO_THREAD_SAFETY_ANALYSIS { for (auto &tx : ordered_coins) { AddTx(tx, pool); } pool.TrimToSize(pool.DynamicMemoryUsage() * 3 / 4); pool.TrimToSize(GetVirtualTransactionSize(*ordered_coins.front())); }); } static void MempoolCheck(benchmark::Bench &bench) { FastRandomContext det_rand{true}; const int childTxs = bench.complexityN() > 1 ? static_cast<int>(bench.complexityN()) : 2000; const std::vector<CTransactionRef> ordered_coins = CreateOrderedCoins(det_rand, childTxs, /* min_ancestors */ 5); const auto testing_setup = TestingSetup(CBaseChainParams::MAIN, {"-checkmempool=1"}); CTxMemPool pool; LOCK2(cs_main, pool.cs); const CCoinsViewCache &coins_tip = testing_setup.m_node.chainman->ActiveChainstate().CoinsTip(); for (auto &tx : ordered_coins) { AddTx(tx, pool); } bench.run([&]() NO_THREAD_SAFETY_ANALYSIS { pool.check(coins_tip, /* spendheight */ 2); }); } BENCHMARK(ComplexMemPool); BENCHMARK(MempoolCheck); diff --git a/src/bench/rpc_blockchain.cpp b/src/bench/rpc_blockchain.cpp index 58601fe5d..bca3fc944 100644 --- a/src/bench/rpc_blockchain.cpp +++ b/src/bench/rpc_blockchain.cpp @@ -1,64 +1,65 @@ // Copyright (c) 2016-2019 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 <bench/bench.h> #include <bench/data.h> #include <rpc/blockchain.h> #include <streams.h> #include <validation.h> #include <test/util/setup_common.h> #include <univalue.h> namespace { struct TestBlockAndIndex { - TestingSetup test_setup{}; + const std::unique_ptr<const TestingSetup> testing_setup{ + MakeNoLogFileContext<const TestingSetup>(CBaseChainParams::MAIN)}; CBlock block{}; BlockHash blockHash{}; CBlockIndex blockindex{}; TestBlockAndIndex() { CDataStream stream(benchmark::data::block413567, SER_NETWORK, PROTOCOL_VERSION); char a = '\0'; // Prevent compaction stream.write(&a, 1); stream >> block; blockHash = block.GetHash(); blockindex.phashBlock = &blockHash; blockindex.nBits = 403014710; } }; } // namespace static void BlockToJsonVerbose(benchmark::Bench &bench) { TestBlockAndIndex data; bench.run([&] { - auto univalue = - blockToJSON(data.test_setup.m_node.chainman->m_blockman, data.block, - &data.blockindex, &data.blockindex, /*txDetails=*/true); + auto univalue = blockToJSON( + data.testing_setup->m_node.chainman->m_blockman, data.block, + &data.blockindex, &data.blockindex, /*txDetails=*/true); ankerl::nanobench::doNotOptimizeAway(univalue); }); } BENCHMARK(BlockToJsonVerbose); static void BlockToJsonVerboseWrite(benchmark::Bench &bench) { TestBlockAndIndex data; - auto univalue = blockToJSON(data.test_setup.m_node.chainman->m_blockman, + auto univalue = blockToJSON(data.testing_setup->m_node.chainman->m_blockman, data.block, &data.blockindex, &data.blockindex, /*txDetails=*/true); bench.run([&] { auto str = univalue.write(); ankerl::nanobench::doNotOptimizeAway(str); }); } BENCHMARK(BlockToJsonVerboseWrite); diff --git a/src/bench/wallet_balance.cpp b/src/bench/wallet_balance.cpp index dcf43a374..4ff45f676 100644 --- a/src/bench/wallet_balance.cpp +++ b/src/bench/wallet_balance.cpp @@ -1,99 +1,92 @@ // Copyright (c) 2012-2019 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 <bench/bench.h> #include <config.h> #include <consensus/amount.h> #include <interfaces/chain.h> #include <node/context.h> #include <validationinterface.h> #include <wallet/receive.h> #include <wallet/wallet.h> #include <test/util/mining.h> #include <test/util/setup_common.h> #include <test/util/wallet.h> #include <optional> static void WalletBalance(benchmark::Bench &bench, const bool set_dirty, const bool add_watchonly, const bool add_mine) { - TestingSetup test_setup{ - CBaseChainParams::REGTEST, - /* extra_args */ - { - "-nodebuglogfile", - "-nodebug", - }, - }; + const auto test_setup = MakeNoLogFileContext<const TestingSetup>(); const auto &ADDRESS_WATCHONLY = ADDRESS_ECREG_UNSPENDABLE; const Config &config = GetConfig(); - CWallet wallet{test_setup.m_node.chain.get(), "", + CWallet wallet{test_setup->m_node.chain.get(), "", CreateMockWalletDatabase()}; { wallet.SetupLegacyScriptPubKeyMan(); bool first_run; if (wallet.LoadWallet(first_run) != DBErrors::LOAD_OK) { assert(false); } } - auto handler = test_setup.m_node.chain->handleNotifications( + auto handler = test_setup->m_node.chain->handleNotifications( {&wallet, [](CWallet *) {}}); const std::optional<std::string> address_mine{ add_mine ? std::optional<std::string>{getnewaddress(config, wallet)} : std::nullopt}; if (add_watchonly) { importaddress(wallet, ADDRESS_WATCHONLY); } for (int i = 0; i < 100; ++i) { - generatetoaddress(config, test_setup.m_node, + generatetoaddress(config, test_setup->m_node, address_mine.value_or(ADDRESS_WATCHONLY)); - generatetoaddress(config, test_setup.m_node, ADDRESS_WATCHONLY); + generatetoaddress(config, test_setup->m_node, ADDRESS_WATCHONLY); } SyncWithValidationInterfaceQueue(); // Cache auto bal = GetBalance(wallet); bench.run([&] { if (set_dirty) { wallet.MarkDirty(); } bal = GetBalance(wallet); if (add_mine) { assert(bal.m_mine_trusted > Amount::zero()); } if (add_watchonly) { assert(bal.m_watchonly_trusted > Amount::zero()); } }); } static void WalletBalanceDirty(benchmark::Bench &bench) { WalletBalance(bench, /* set_dirty */ true, /* add_watchonly */ true, /* add_mine */ true); } static void WalletBalanceClean(benchmark::Bench &bench) { WalletBalance(bench, /* set_dirty */ false, /* add_watchonly */ true, /* add_mine */ true); } static void WalletBalanceMine(benchmark::Bench &bench) { WalletBalance(bench, /* set_dirty */ false, /* add_watchonly */ false, /* add_mine */ true); } static void WalletBalanceWatch(benchmark::Bench &bench) { WalletBalance(bench, /* set_dirty */ false, /* add_watchonly */ true, /* add_mine */ false); } BENCHMARK(WalletBalanceDirty); BENCHMARK(WalletBalanceClean); BENCHMARK(WalletBalanceMine); BENCHMARK(WalletBalanceWatch); diff --git a/src/test/fuzz/banman.cpp b/src/test/fuzz/banman.cpp index f41a08d91..f1e772729 100644 --- a/src/test/fuzz/banman.cpp +++ b/src/test/fuzz/banman.cpp @@ -1,73 +1,74 @@ // Copyright (c) 2020 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 <banman.h> #include <config.h> #include <fs.h> #include <netaddress.h> #include <util/system.h> #include <test/fuzz/FuzzedDataProvider.h> #include <test/fuzz/fuzz.h> #include <test/fuzz/util.h> +#include <test/util/setup_common.h> #include <cstdint> #include <limits> #include <string> #include <vector> namespace { int64_t ConsumeBanTimeOffset(FuzzedDataProvider &fuzzed_data_provider) noexcept { // Avoid signed integer overflow by capping to int32_t max: // banman.cpp:137:73: runtime error: signed integer overflow: 1591700817 + // 9223372036854775807 cannot be represented in type 'long' return fuzzed_data_provider.ConsumeIntegralInRange<int64_t>( std::numeric_limits<int64_t>::min(), std::numeric_limits<int32_t>::max()); } } // namespace void initialize_banman() { static const auto testing_setup = MakeNoLogFileContext<>(); } FUZZ_TARGET_INIT(banman, initialize_banman) { FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()}; const fs::path banlist_file = gArgs.GetDataDirNet() / "fuzzed_banlist.dat"; fs::remove(banlist_file); const CChainParams &chainparams = GetConfig().GetChainParams(); { BanMan ban_man{banlist_file, chainparams, nullptr, ConsumeBanTimeOffset(fuzzed_data_provider)}; while (fuzzed_data_provider.ConsumeBool()) { CallOneOf( fuzzed_data_provider, [&] { ban_man.Ban(ConsumeNetAddr(fuzzed_data_provider), ConsumeBanTimeOffset(fuzzed_data_provider), fuzzed_data_provider.ConsumeBool()); }, [&] { ban_man.Ban(ConsumeSubNet(fuzzed_data_provider), ConsumeBanTimeOffset(fuzzed_data_provider), fuzzed_data_provider.ConsumeBool()); }, [&] { ban_man.ClearBanned(); }, [] {}, [&] { ban_man.IsBanned(ConsumeNetAddr(fuzzed_data_provider)); }, [&] { ban_man.IsBanned(ConsumeSubNet(fuzzed_data_provider)); }, [&] { ban_man.Unban(ConsumeNetAddr(fuzzed_data_provider)); }, [&] { ban_man.Unban(ConsumeSubNet(fuzzed_data_provider)); }, [&] { banmap_t banmap; ban_man.GetBanned(banmap); }, [&] { ban_man.DumpBanlist(); }, [] {}, [&] { ban_man.Discourage(ConsumeNetAddr(fuzzed_data_provider)); }); } } fs::remove(fs::PathToString(banlist_file)); } diff --git a/src/test/fuzz/coins_view.cpp b/src/test/fuzz/coins_view.cpp index 931a6d224..71482f655 100644 --- a/src/test/fuzz/coins_view.cpp +++ b/src/test/fuzz/coins_view.cpp @@ -1,286 +1,287 @@ // Copyright (c) 2020 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 <chainparams.h> #include <chainparamsbase.h> #include <coins.h> #include <consensus/amount.h> #include <consensus/tx_verify.h> #include <consensus/validation.h> #include <key.h> #include <policy/policy.h> #include <primitives/blockhash.h> #include <primitives/transaction.h> #include <pubkey.h> #include <validation.h> #include <test/fuzz/FuzzedDataProvider.h> #include <test/fuzz/fuzz.h> #include <test/fuzz/util.h> +#include <test/util/setup_common.h> #include <cstdint> #include <limits> #include <optional> #include <string> #include <vector> namespace { const TestingSetup *g_setup; const Coin EMPTY_COIN{}; bool operator==(const Coin &a, const Coin &b) { if (a.IsSpent() && b.IsSpent()) { return true; } return a.IsCoinBase() == b.IsCoinBase() && a.GetHeight() == b.GetHeight() && a.GetTxOut() == b.GetTxOut(); } } // namespace void initialize_coins_view() { static const auto testing_setup = MakeNoLogFileContext<const TestingSetup>(); g_setup = testing_setup.get(); } FUZZ_TARGET_INIT(coins_view, initialize_coins_view) { FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()}; CCoinsView backend_coins_view; CCoinsViewCache coins_view_cache{&backend_coins_view}; COutPoint random_out_point; Coin random_coin; CMutableTransaction random_mutable_transaction; while (fuzzed_data_provider.ConsumeBool()) { CallOneOf( fuzzed_data_provider, [&] { if (random_coin.IsSpent()) { return; } Coin coin = random_coin; bool expected_code_path = false; const bool possible_overwrite = fuzzed_data_provider.ConsumeBool(); try { coins_view_cache.AddCoin(random_out_point, std::move(coin), possible_overwrite); expected_code_path = true; } catch (const std::logic_error &e) { if (e.what() == std::string{"Attempted to overwrite an unspent coin " "(when possible_overwrite is false)"}) { assert(!possible_overwrite); expected_code_path = true; } } assert(expected_code_path); }, [&] { (void)coins_view_cache.Flush(); }, [&] { coins_view_cache.SetBestBlock( BlockHash{ConsumeUInt256(fuzzed_data_provider)}); }, [&] { Coin move_to; (void)coins_view_cache.SpendCoin( random_out_point, fuzzed_data_provider.ConsumeBool() ? &move_to : nullptr); }, [&] { coins_view_cache.Uncache(random_out_point); }, [&] { if (fuzzed_data_provider.ConsumeBool()) { backend_coins_view = CCoinsView{}; } coins_view_cache.SetBackend(backend_coins_view); }, [&] { const std::optional<COutPoint> opt_out_point = ConsumeDeserializable<COutPoint>(fuzzed_data_provider); if (!opt_out_point) { return; } random_out_point = *opt_out_point; }, [&] { const std::optional<Coin> opt_coin = ConsumeDeserializable<Coin>(fuzzed_data_provider); if (!opt_coin) { return; } random_coin = *opt_coin; }, [&] { const std::optional<CMutableTransaction> opt_mutable_transaction = ConsumeDeserializable<CMutableTransaction>( fuzzed_data_provider); if (!opt_mutable_transaction) { return; } random_mutable_transaction = *opt_mutable_transaction; }, [&] { CCoinsMap coins_map; while (fuzzed_data_provider.ConsumeBool()) { CCoinsCacheEntry coins_cache_entry; coins_cache_entry.flags = fuzzed_data_provider.ConsumeIntegral<uint8_t>(); if (fuzzed_data_provider.ConsumeBool()) { coins_cache_entry.coin = random_coin; } else { const std::optional<Coin> opt_coin = ConsumeDeserializable<Coin>(fuzzed_data_provider); if (!opt_coin) { return; } coins_cache_entry.coin = *opt_coin; } coins_map.emplace(random_out_point, std::move(coins_cache_entry)); } bool expected_code_path = false; try { coins_view_cache.BatchWrite( coins_map, fuzzed_data_provider.ConsumeBool() ? BlockHash{ConsumeUInt256(fuzzed_data_provider)} : coins_view_cache.GetBestBlock()); expected_code_path = true; } catch (const std::logic_error &e) { if (e.what() == std::string{"FRESH flag misapplied to coin that exists " "in parent cache"}) { expected_code_path = true; } } assert(expected_code_path); }); } { const Coin &coin_using_access_coin = coins_view_cache.AccessCoin(random_out_point); const bool exists_using_access_coin = !(coin_using_access_coin == EMPTY_COIN); const bool exists_using_have_coin = coins_view_cache.HaveCoin(random_out_point); const bool exists_using_have_coin_in_cache = coins_view_cache.HaveCoinInCache(random_out_point); Coin coin_using_get_coin; const bool exists_using_get_coin = coins_view_cache.GetCoin(random_out_point, coin_using_get_coin); if (exists_using_get_coin) { assert(coin_using_get_coin == coin_using_access_coin); } assert((exists_using_access_coin && exists_using_have_coin_in_cache && exists_using_have_coin && exists_using_get_coin) || (!exists_using_access_coin && !exists_using_have_coin_in_cache && !exists_using_have_coin && !exists_using_get_coin)); const bool exists_using_have_coin_in_backend = backend_coins_view.HaveCoin(random_out_point); if (exists_using_have_coin_in_backend) { assert(exists_using_have_coin); } Coin coin_using_backend_get_coin; if (backend_coins_view.GetCoin(random_out_point, coin_using_backend_get_coin)) { assert(exists_using_have_coin_in_backend); assert(coin_using_get_coin == coin_using_backend_get_coin); } else { assert(!exists_using_have_coin_in_backend); } } { bool expected_code_path = false; try { (void)coins_view_cache.Cursor(); } catch (const std::logic_error &) { expected_code_path = true; } assert(expected_code_path); (void)coins_view_cache.DynamicMemoryUsage(); (void)coins_view_cache.EstimateSize(); (void)coins_view_cache.GetBestBlock(); (void)coins_view_cache.GetCacheSize(); (void)coins_view_cache.GetHeadBlocks(); (void)coins_view_cache.HaveInputs( CTransaction{random_mutable_transaction}); } { const CCoinsViewCursor *coins_view_cursor = backend_coins_view.Cursor(); assert(coins_view_cursor == nullptr); (void)backend_coins_view.EstimateSize(); (void)backend_coins_view.GetBestBlock(); (void)backend_coins_view.GetHeadBlocks(); } if (fuzzed_data_provider.ConsumeBool()) { CallOneOf( fuzzed_data_provider, [&] { const CTransaction transaction{random_mutable_transaction}; bool is_spent = false; for (const CTxOut &tx_out : transaction.vout) { if (Coin{tx_out, 0, transaction.IsCoinBase()}.IsSpent()) { is_spent = true; } } if (is_spent) { // Avoid: // coins.cpp:69: void CCoinsViewCache::AddCoin(const // COutPoint &, Coin &&, bool): Assertion `!coin.IsSpent()' // failed. return; } bool expected_code_path = false; const int height{ int(fuzzed_data_provider.ConsumeIntegral<uint32_t>() >> 1)}; const bool possible_overwrite = fuzzed_data_provider.ConsumeBool(); try { AddCoins(coins_view_cache, transaction, height, possible_overwrite); expected_code_path = true; } catch (const std::logic_error &e) { if (e.what() == std::string{"Attempted to overwrite an unspent coin " "(when possible_overwrite is false)"}) { assert(!possible_overwrite); expected_code_path = true; } } assert(expected_code_path); }, [&] { uint32_t flags = fuzzed_data_provider.ConsumeIntegral<uint32_t>(); (void)AreInputsStandard( CTransaction{random_mutable_transaction}, coins_view_cache, flags); }, [&] { TxValidationState state; Amount tx_fee_out; const CTransaction transaction{random_mutable_transaction}; if (ContainsSpentInput(transaction, coins_view_cache)) { // Avoid: // consensus/tx_verify.cpp:171: bool // Consensus::CheckTxInputs(const CTransaction &, // TxValidationState &, const CCoinsViewCache &, int, // CAmount &): Assertion `!coin.IsSpent()' failed. } try { (void)Consensus::CheckTxInputs( transaction, state, coins_view_cache, fuzzed_data_provider.ConsumeIntegralInRange<int>( 0, std::numeric_limits<int>::max()), tx_fee_out); assert(MoneyRange(tx_fee_out)); } catch (const std::runtime_error &) { } }); } } diff --git a/src/test/fuzz/connman.cpp b/src/test/fuzz/connman.cpp index 9d40a6f29..789384e16 100644 --- a/src/test/fuzz/connman.cpp +++ b/src/test/fuzz/connman.cpp @@ -1,126 +1,127 @@ // Copyright (c) 2020 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 <config.h> #include <net.h> #include <netaddress.h> #include <protocol.h> #include <test/fuzz/FuzzedDataProvider.h> #include <test/fuzz/fuzz.h> #include <test/fuzz/util.h> +#include <test/util/setup_common.h> #include <cstdint> #include <vector> namespace { const TestingSetup *g_setup; } // namespace void initialize_connman() { static const auto testing_setup = MakeNoLogFileContext<const TestingSetup>(); g_setup = testing_setup.get(); } FUZZ_TARGET_INIT(connman, initialize_connman) { const Config &config = GetConfig(); FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()}; CConnman connman{config, fuzzed_data_provider.ConsumeIntegral<uint64_t>(), fuzzed_data_provider.ConsumeIntegral<uint64_t>(), *g_setup->m_node.addrman, fuzzed_data_provider.ConsumeBool()}; CNetAddr random_netaddr; CNode random_node = ConsumeNode(fuzzed_data_provider); CSubNet random_subnet; std::string random_string; while (fuzzed_data_provider.ConsumeBool()) { CallOneOf( fuzzed_data_provider, [&] { random_netaddr = ConsumeNetAddr(fuzzed_data_provider); }, [&] { random_subnet = ConsumeSubNet(fuzzed_data_provider); }, [&] { random_string = fuzzed_data_provider.ConsumeRandomLengthString(64); }, [&] { connman.AddNode(random_string); }, [&] { connman.CheckIncomingNonce( fuzzed_data_provider.ConsumeIntegral<uint64_t>()); }, [&] { connman.DisconnectNode( fuzzed_data_provider.ConsumeIntegral<NodeId>()); }, [&] { connman.DisconnectNode(random_netaddr); }, [&] { connman.DisconnectNode(random_string); }, [&] { connman.DisconnectNode(random_subnet); }, [&] { connman.ForEachNode([](auto) {}); }, [&] { (void)connman.ForNode( fuzzed_data_provider.ConsumeIntegral<NodeId>(), [&](auto) { return fuzzed_data_provider.ConsumeBool(); }); }, [&] { (void)connman.GetAddresses( fuzzed_data_provider.ConsumeIntegral<size_t>(), fuzzed_data_provider.ConsumeIntegral<size_t>(), std::nullopt); }, [&] { (void)connman.GetAddresses( random_node, fuzzed_data_provider.ConsumeIntegral<size_t>(), fuzzed_data_provider.ConsumeIntegral<size_t>()); }, [&] { (void)connman.GetDeterministicRandomizer( fuzzed_data_provider.ConsumeIntegral<uint64_t>()); }, [&] { (void)connman.GetNodeCount( fuzzed_data_provider.PickValueInArray( {CConnman::CONNECTIONS_NONE, CConnman::CONNECTIONS_IN, CConnman::CONNECTIONS_OUT, CConnman::CONNECTIONS_ALL})); }, [&] { (void)connman.OutboundTargetReached( fuzzed_data_provider.ConsumeBool()); }, [&] { CSerializedNetMsg serialized_net_msg; serialized_net_msg.m_type = fuzzed_data_provider.ConsumeRandomLengthString( CMessageHeader::COMMAND_SIZE); serialized_net_msg.data = ConsumeRandomLengthByteVector(fuzzed_data_provider); connman.PushMessage(&random_node, std::move(serialized_net_msg)); }, [&] { connman.RemoveAddedNode(random_string); }, [&] { connman.SetNetworkActive(fuzzed_data_provider.ConsumeBool()); }, [&] { connman.SetTryNewOutboundPeer( fuzzed_data_provider.ConsumeBool()); connman.SetTryNewOutboundPeer( fuzzed_data_provider.ConsumeBool()); }); } (void)connman.GetAddedNodeInfo(); (void)connman.GetExtraFullOutboundCount(); (void)connman.GetLocalServices(); (void)connman.GetMaxOutboundTarget(); (void)connman.GetMaxOutboundTimeframe(); (void)connman.GetMaxOutboundTimeLeftInCycle(); (void)connman.GetNetworkActive(); std::vector<CNodeStats> stats; connman.GetNodeStats(stats); (void)connman.GetOutboundTargetBytesLeft(); (void)connman.GetReceiveFloodSize(); (void)connman.GetTotalBytesRecv(); (void)connman.GetTotalBytesSent(); (void)connman.GetTryNewOutboundPeer(); (void)connman.GetUseAddrmanOutgoing(); } diff --git a/src/test/fuzz/util.h b/src/test/fuzz/util.h index a7dfc7e4e..b639d882f 100644 --- a/src/test/fuzz/util.h +++ b/src/test/fuzz/util.h @@ -1,521 +1,506 @@ // Copyright (c) 2009-2019 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_TEST_FUZZ_UTIL_H #define BITCOIN_TEST_FUZZ_UTIL_H #include <arith_uint256.h> #include <chainparamsbase.h> #include <coins.h> #include <consensus/amount.h> #include <net.h> #include <netbase.h> #include <script/script.h> #include <script/standard.h> #include <serialize.h> #include <streams.h> #include <uint256.h> #include <util/vector.h> #include <version.h> #include <test/fuzz/FuzzedDataProvider.h> #include <test/fuzz/fuzz.h> -#include <test/util/setup_common.h> #include <algorithm> #include <cstdint> #include <cstdio> #include <optional> #include <string> -#include <vector> namespace fuzzer { // FIXME find a better way to avoid duplicating the MAX_MONEY definition constexpr int64_t MAX_MONEY_AS_INT = int64_t(21000000) * int64_t(100000000); } // end namespace fuzzer using namespace fuzzer; template <typename... Callables> void CallOneOf(FuzzedDataProvider &fuzzed_data_provider, Callables... callables) { constexpr size_t call_size{sizeof...(callables)}; static_assert(call_size >= 1); const size_t call_index{ fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, call_size - 1)}; size_t i{0}; return ((i++ == call_index ? callables() : void()), ...); } [[nodiscard]] inline std::vector<uint8_t> ConsumeRandomLengthByteVector(FuzzedDataProvider &fuzzed_data_provider, const size_t max_length = 4096) noexcept { const std::string s = fuzzed_data_provider.ConsumeRandomLengthString(max_length); return {s.begin(), s.end()}; } [[nodiscard]] inline CDataStream ConsumeDataStream(FuzzedDataProvider &fuzzed_data_provider, const size_t max_length = 4096) noexcept { return CDataStream{ ConsumeRandomLengthByteVector(fuzzed_data_provider, max_length), SER_NETWORK, INIT_PROTO_VERSION}; } [[nodiscard]] inline std::vector<std::string> ConsumeRandomLengthStringVector(FuzzedDataProvider &fuzzed_data_provider, const size_t max_vector_size = 16, const size_t max_string_length = 16) noexcept { const size_t n_elements = fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, max_vector_size); std::vector<std::string> r; for (size_t i = 0; i < n_elements; ++i) { r.push_back( fuzzed_data_provider.ConsumeRandomLengthString(max_string_length)); } return r; } template <typename T> [[nodiscard]] inline std::vector<T> ConsumeRandomLengthIntegralVector(FuzzedDataProvider &fuzzed_data_provider, const size_t max_vector_size = 16) noexcept { const size_t n_elements = fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, max_vector_size); std::vector<T> r; for (size_t i = 0; i < n_elements; ++i) { r.push_back(fuzzed_data_provider.ConsumeIntegral<T>()); } return r; } template <typename T> [[nodiscard]] inline std::optional<T> ConsumeDeserializable(FuzzedDataProvider &fuzzed_data_provider, const size_t max_length = 4096) noexcept { const std::vector<uint8_t> buffer = ConsumeRandomLengthByteVector(fuzzed_data_provider, max_length); CDataStream ds{buffer, SER_NETWORK, INIT_PROTO_VERSION}; T obj; try { ds >> obj; } catch (const std::ios_base::failure &) { return std::nullopt; } return obj; } [[nodiscard]] inline opcodetype ConsumeOpcodeType(FuzzedDataProvider &fuzzed_data_provider) noexcept { return static_cast<opcodetype>( fuzzed_data_provider.ConsumeIntegralInRange<uint32_t>(0, MAX_OPCODE)); } [[nodiscard]] inline Amount ConsumeMoney(FuzzedDataProvider &fuzzed_data_provider) noexcept { return fuzzed_data_provider.ConsumeIntegralInRange<int64_t>( 0, MAX_MONEY_AS_INT) * SATOSHI; } [[nodiscard]] inline CScript ConsumeScript(FuzzedDataProvider &fuzzed_data_provider) noexcept { const std::vector<uint8_t> b = ConsumeRandomLengthByteVector(fuzzed_data_provider); return {b.begin(), b.end()}; } [[nodiscard]] inline CScriptNum ConsumeScriptNum(FuzzedDataProvider &fuzzed_data_provider) noexcept { return CScriptNum{fuzzed_data_provider.ConsumeIntegral<int64_t>()}; } [[nodiscard]] inline uint160 ConsumeUInt160(FuzzedDataProvider &fuzzed_data_provider) noexcept { const std::vector<uint8_t> v160 = fuzzed_data_provider.ConsumeBytes<uint8_t>(160 / 8); if (v160.size() != 160 / 8) { return {}; } return uint160{v160}; } [[nodiscard]] inline uint256 ConsumeUInt256(FuzzedDataProvider &fuzzed_data_provider) noexcept { const std::vector<uint8_t> v256 = fuzzed_data_provider.ConsumeBytes<uint8_t>(256 / 8); if (v256.size() != 256 / 8) { return {}; } return uint256{v256}; } [[nodiscard]] inline arith_uint256 ConsumeArithUInt256(FuzzedDataProvider &fuzzed_data_provider) noexcept { return UintToArith256(ConsumeUInt256(fuzzed_data_provider)); } [[nodiscard]] inline CTxDestination ConsumeTxDestination(FuzzedDataProvider &fuzzed_data_provider) noexcept { CTxDestination tx_destination; CallOneOf( fuzzed_data_provider, [&] { tx_destination = CNoDestination{}; }, [&] { tx_destination = PKHash{ConsumeUInt160(fuzzed_data_provider)}; }, [&] { tx_destination = ScriptHash{ConsumeUInt160(fuzzed_data_provider)}; }); return tx_destination; } template <typename T> [[nodiscard]] bool MultiplicationOverflow(const T i, const T j) noexcept { static_assert(std::is_integral<T>::value, "Integral required."); if (std::numeric_limits<T>::is_signed) { if (i > 0) { if (j > 0) { return i > (std::numeric_limits<T>::max() / j); } else { return j < (std::numeric_limits<T>::min() / i); } } else { if (j > 0) { return i < (std::numeric_limits<T>::min() / j); } else { return i != 0 && (j < (std::numeric_limits<T>::max() / i)); } } } else { return j != 0 && i > std::numeric_limits<T>::max() / j; } } template <class T> [[nodiscard]] bool AdditionOverflow(const T i, const T j) noexcept { static_assert(std::is_integral<T>::value, "Integral required."); if (std::numeric_limits<T>::is_signed) { return (i > 0 && j > std::numeric_limits<T>::max() - i) || (i < 0 && j < std::numeric_limits<T>::min() - i); } return std::numeric_limits<T>::max() - i < j; } [[nodiscard]] inline bool ContainsSpentInput(const CTransaction &tx, const CCoinsViewCache &inputs) noexcept { for (const CTxIn &tx_in : tx.vin) { const Coin &coin = inputs.AccessCoin(tx_in.prevout); if (coin.IsSpent()) { return true; } } return false; } /** * Returns a byte vector of specified size regardless of the number of remaining * bytes available from the fuzzer. Pads with zero value bytes if needed to * achieve the specified size. */ [[nodiscard]] inline std::vector<uint8_t> ConsumeFixedLengthByteVector(FuzzedDataProvider &fuzzed_data_provider, const size_t length) noexcept { std::vector<uint8_t> result(length); const std::vector<uint8_t> random_bytes = fuzzed_data_provider.ConsumeBytes<uint8_t>(length); if (!random_bytes.empty()) { std::memcpy(result.data(), random_bytes.data(), random_bytes.size()); } return result; } inline CNetAddr ConsumeNetAddr(FuzzedDataProvider &fuzzed_data_provider) noexcept { const Network network = fuzzed_data_provider.PickValueInArray( {Network::NET_IPV4, Network::NET_IPV6, Network::NET_INTERNAL, Network::NET_ONION}); CNetAddr net_addr; if (network == Network::NET_IPV4) { const in_addr v4_addr = { .s_addr = fuzzed_data_provider.ConsumeIntegral<uint32_t>()}; net_addr = CNetAddr{v4_addr}; } else if (network == Network::NET_IPV6) { if (fuzzed_data_provider.remaining_bytes() >= 16) { in6_addr v6_addr = {}; memcpy(v6_addr.s6_addr, fuzzed_data_provider.ConsumeBytes<uint8_t>(16).data(), 16); net_addr = CNetAddr{ v6_addr, fuzzed_data_provider.ConsumeIntegral<uint32_t>()}; } } else if (network == Network::NET_INTERNAL) { net_addr.SetInternal(fuzzed_data_provider.ConsumeBytesAsString(32)); } else if (network == Network::NET_ONION) { net_addr.SetSpecial(fuzzed_data_provider.ConsumeBytesAsString(32)); } return net_addr; } inline CSubNet ConsumeSubNet(FuzzedDataProvider &fuzzed_data_provider) noexcept { return {ConsumeNetAddr(fuzzed_data_provider), fuzzed_data_provider.ConsumeIntegral<uint8_t>()}; } inline CService ConsumeService(FuzzedDataProvider &fuzzed_data_provider) noexcept { return {ConsumeNetAddr(fuzzed_data_provider), fuzzed_data_provider.ConsumeIntegral<uint16_t>()}; } inline CAddress ConsumeAddress(FuzzedDataProvider &fuzzed_data_provider) noexcept { return {ConsumeService(fuzzed_data_provider), static_cast<ServiceFlags>( fuzzed_data_provider.ConsumeIntegral<uint64_t>()), NodeSeconds{std::chrono::seconds{ fuzzed_data_provider.ConsumeIntegral<uint32_t>()}}}; } inline CNode ConsumeNode(FuzzedDataProvider &fuzzed_data_provider) noexcept { const NodeId node_id = fuzzed_data_provider.ConsumeIntegral<NodeId>(); const SOCKET socket = INVALID_SOCKET; const CAddress address = ConsumeAddress(fuzzed_data_provider); const uint64_t keyed_net_group = fuzzed_data_provider.ConsumeIntegral<uint64_t>(); const uint64_t local_host_nonce = fuzzed_data_provider.ConsumeIntegral<uint64_t>(); const uint64_t local_extra_entropy = fuzzed_data_provider.ConsumeIntegral<uint64_t>(); const CAddress addr_bind = ConsumeAddress(fuzzed_data_provider); const std::string addr_name = fuzzed_data_provider.ConsumeRandomLengthString(64); const ConnectionType conn_type = fuzzed_data_provider.PickValueInArray( {ConnectionType::INBOUND, ConnectionType::OUTBOUND_FULL_RELAY, ConnectionType::MANUAL, ConnectionType::FEELER, ConnectionType::BLOCK_RELAY, ConnectionType::ADDR_FETCH}); const bool inbound_onion = fuzzed_data_provider.ConsumeBool(); return {node_id, socket, address, keyed_net_group, local_host_nonce, local_extra_entropy, addr_bind, addr_name, conn_type, inbound_onion}; } -template <class T = const BasicTestingSetup> -std::unique_ptr<T> -MakeNoLogFileContext(const std::string &chain_name = CBaseChainParams::REGTEST, - const std::vector<const char *> &extra_args = {}) { - // Prepend default arguments for fuzzing - const std::vector<const char *> arguments = Cat( - { - "-nodebuglogfile", - }, - extra_args); - - return std::make_unique<T>(chain_name, arguments); -} class FuzzedFileProvider { FuzzedDataProvider &m_fuzzed_data_provider; int64_t m_offset = 0; public: FuzzedFileProvider(FuzzedDataProvider &fuzzed_data_provider) : m_fuzzed_data_provider{fuzzed_data_provider} {} FILE *open() { if (m_fuzzed_data_provider.ConsumeBool()) { return nullptr; } std::string mode; // clang-format off CallOneOf( m_fuzzed_data_provider, [&] { mode = "r"; }, [&] { mode = "r+"; }, [&] { mode = "w"; }, [&] { mode = "w+"; }, [&] { mode = "a"; }, [&] { mode = "a+"; } ); // clang-format on #ifdef _GNU_SOURCE const cookie_io_functions_t io_hooks = { FuzzedFileProvider::read, FuzzedFileProvider::write, FuzzedFileProvider::seek, FuzzedFileProvider::close, }; return fopencookie(this, mode.c_str(), io_hooks); #else (void)mode; return nullptr; #endif } static ssize_t read(void *cookie, char *buf, size_t size) { FuzzedFileProvider *fuzzed_file = (FuzzedFileProvider *)cookie; if (buf == nullptr || size == 0 || fuzzed_file->m_fuzzed_data_provider.ConsumeBool()) { return fuzzed_file->m_fuzzed_data_provider.ConsumeBool() ? 0 : -1; } const std::vector<uint8_t> random_bytes = fuzzed_file->m_fuzzed_data_provider.ConsumeBytes<uint8_t>(size); if (random_bytes.empty()) { return 0; } std::memcpy(buf, random_bytes.data(), random_bytes.size()); if (AdditionOverflow(fuzzed_file->m_offset, int64_t(random_bytes.size()))) { return fuzzed_file->m_fuzzed_data_provider.ConsumeBool() ? 0 : -1; } fuzzed_file->m_offset += random_bytes.size(); return random_bytes.size(); } static ssize_t write(void *cookie, const char *buf, size_t size) { FuzzedFileProvider *fuzzed_file = (FuzzedFileProvider *)cookie; const ssize_t n = fuzzed_file->m_fuzzed_data_provider.ConsumeIntegralInRange<ssize_t>( 0, size); if (AdditionOverflow(fuzzed_file->m_offset, int64_t(n))) { return fuzzed_file->m_fuzzed_data_provider.ConsumeBool() ? 0 : -1; } fuzzed_file->m_offset += n; return n; } static int seek(void *cookie, int64_t *offset, int whence) { // SEEK_END not implemented yet. assert(whence == SEEK_SET || whence == SEEK_CUR); FuzzedFileProvider *fuzzed_file = (FuzzedFileProvider *)cookie; int64_t new_offset = 0; if (whence == SEEK_SET) { new_offset = *offset; } else if (whence == SEEK_CUR) { if (AdditionOverflow(fuzzed_file->m_offset, *offset)) { return -1; } new_offset = fuzzed_file->m_offset + *offset; } if (new_offset < 0) { return -1; } fuzzed_file->m_offset = new_offset; *offset = new_offset; return fuzzed_file->m_fuzzed_data_provider.ConsumeIntegralInRange<int>( -1, 0); } static int close(void *cookie) { FuzzedFileProvider *fuzzed_file = (FuzzedFileProvider *)cookie; return fuzzed_file->m_fuzzed_data_provider.ConsumeIntegralInRange<int>( -1, 0); } }; [[nodiscard]] inline FuzzedFileProvider ConsumeFile(FuzzedDataProvider &fuzzed_data_provider) noexcept { return {fuzzed_data_provider}; } class FuzzedAutoFileProvider { FuzzedFileProvider m_fuzzed_file_provider; public: FuzzedAutoFileProvider(FuzzedDataProvider &fuzzed_data_provider) : m_fuzzed_file_provider{fuzzed_data_provider} {} AutoFile open() { return AutoFile{m_fuzzed_file_provider.open()}; } }; [[nodiscard]] inline FuzzedAutoFileProvider ConsumeAutoFile(FuzzedDataProvider &fuzzed_data_provider) noexcept { return {fuzzed_data_provider}; } #define WRITE_TO_STREAM_CASE(type, consume) \ [&] { \ type o = consume; \ stream << o; \ } template <typename Stream> void WriteToStream(FuzzedDataProvider &fuzzed_data_provider, Stream &stream) noexcept { while (fuzzed_data_provider.ConsumeBool()) { try { CallOneOf( fuzzed_data_provider, WRITE_TO_STREAM_CASE(bool, fuzzed_data_provider.ConsumeBool()), WRITE_TO_STREAM_CASE( char, fuzzed_data_provider.ConsumeIntegral<char>()), WRITE_TO_STREAM_CASE( int8_t, fuzzed_data_provider.ConsumeIntegral<int8_t>()), WRITE_TO_STREAM_CASE( uint8_t, fuzzed_data_provider.ConsumeIntegral<uint8_t>()), WRITE_TO_STREAM_CASE( int16_t, fuzzed_data_provider.ConsumeIntegral<int16_t>()), WRITE_TO_STREAM_CASE( uint16_t, fuzzed_data_provider.ConsumeIntegral<uint16_t>()), WRITE_TO_STREAM_CASE( int32_t, fuzzed_data_provider.ConsumeIntegral<int32_t>()), WRITE_TO_STREAM_CASE( uint32_t, fuzzed_data_provider.ConsumeIntegral<uint32_t>()), WRITE_TO_STREAM_CASE( int64_t, fuzzed_data_provider.ConsumeIntegral<int64_t>()), WRITE_TO_STREAM_CASE( uint64_t, fuzzed_data_provider.ConsumeIntegral<uint64_t>()), WRITE_TO_STREAM_CASE( float, fuzzed_data_provider.ConsumeFloatingPoint<float>()), WRITE_TO_STREAM_CASE( double, fuzzed_data_provider.ConsumeFloatingPoint<double>()), WRITE_TO_STREAM_CASE( std::string, fuzzed_data_provider.ConsumeRandomLengthString(32)), WRITE_TO_STREAM_CASE(std::vector<char>, ConsumeRandomLengthIntegralVector<char>( fuzzed_data_provider))); } catch (const std::ios_base::failure &) { break; } } } #define READ_FROM_STREAM_CASE(type) \ [&] { \ type o; \ stream >> o; \ } template <typename Stream> void ReadFromStream(FuzzedDataProvider &fuzzed_data_provider, Stream &stream) noexcept { while (fuzzed_data_provider.ConsumeBool()) { try { // clang-format off CallOneOf( fuzzed_data_provider, READ_FROM_STREAM_CASE(bool), READ_FROM_STREAM_CASE(char), READ_FROM_STREAM_CASE(int8_t), READ_FROM_STREAM_CASE(uint8_t), READ_FROM_STREAM_CASE(int16_t), READ_FROM_STREAM_CASE(uint16_t), READ_FROM_STREAM_CASE(int32_t), READ_FROM_STREAM_CASE(uint32_t), READ_FROM_STREAM_CASE(int64_t), READ_FROM_STREAM_CASE(uint64_t), READ_FROM_STREAM_CASE(float), READ_FROM_STREAM_CASE(double), READ_FROM_STREAM_CASE(std::string), READ_FROM_STREAM_CASE(std::vector<char>) ); // clang-format on } catch (const std::ios_base::failure &) { break; } } } #endif // BITCOIN_TEST_FUZZ_UTIL_H diff --git a/src/test/util/setup_common.h b/src/test/util/setup_common.h index ab0f97574..495aedce4 100644 --- a/src/test/util/setup_common.h +++ b/src/test/util/setup_common.h @@ -1,269 +1,288 @@ // Copyright (c) 2015-2019 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_TEST_UTIL_SETUP_COMMON_H #define BITCOIN_TEST_UTIL_SETUP_COMMON_H #include <blockindex.h> #include <chainparamsbase.h> #include <consensus/amount.h> #include <fs.h> #include <key.h> #include <node/caches.h> #include <node/context.h> #include <primitives/transaction.h> #include <pubkey.h> #include <random.h> #include <stdexcept> #include <txmempool.h> #include <util/check.h> #include <util/string.h> #include <util/system.h> +#include <util/vector.h> #include <type_traits> #include <vector> class Config; // Enable BOOST_CHECK_EQUAL for enum class types template <typename T> std::ostream &operator<<( typename std::enable_if<std::is_enum<T>::value, std::ostream>::type &stream, const T &e) { return stream << static_cast<typename std::underlying_type<T>::type>(e); } /** * This global and the helpers that use it are not thread-safe. * * If thread-safety is needed, the global could be made thread_local (given * that thread_local is supported on all architectures we support) or a * per-thread instance could be used in the multi-threaded test. */ extern FastRandomContext g_insecure_rand_ctx; /** * Flag to make GetRand in random.h return the same number */ extern bool g_mock_deterministic_tests; enum class SeedRand { ZEROS, //!< Seed with a compile time constant of zeros SEED, //!< Call the Seed() helper }; /** * Seed the given random ctx or use the seed passed in via an * environment var */ void Seed(FastRandomContext &ctx); static inline void SeedInsecureRand(SeedRand seed = SeedRand::SEED) { if (seed == SeedRand::ZEROS) { g_insecure_rand_ctx = FastRandomContext(/* deterministic */ true); } else { Seed(g_insecure_rand_ctx); } } static inline uint32_t InsecureRand32() { return g_insecure_rand_ctx.rand32(); } static inline uint160 InsecureRand160() { return g_insecure_rand_ctx.rand160(); } static inline uint256 InsecureRand256() { return g_insecure_rand_ctx.rand256(); } static inline uint64_t InsecureRandBits(int bits) { return g_insecure_rand_ctx.randbits(bits); } static inline uint64_t InsecureRandRange(uint64_t range) { return g_insecure_rand_ctx.randrange(range); } static inline bool InsecureRandBool() { return g_insecure_rand_ctx.randbool(); } static constexpr Amount CENT(COIN / 100); extern std::vector<const char *> fixture_extra_args; /** * Basic testing setup. * This just configures logging, data dir and chain parameters. */ struct BasicTestingSetup { ECCVerifyHandle globalVerifyHandle; node::NodeContext m_node; explicit BasicTestingSetup( const std::string &chainName = CBaseChainParams::MAIN, const std::vector<const char *> &extra_args = {}); ~BasicTestingSetup(); const fs::path m_path_root; ArgsManager m_args; }; /** * Testing setup that performs all steps up until right before * ChainstateManager gets initialized. Meant for testing ChainstateManager * initialization behaviour. */ struct ChainTestingSetup : public BasicTestingSetup { node::CacheSizes m_cache_sizes{}; explicit ChainTestingSetup( const std::string &chainName = CBaseChainParams::MAIN, const std::vector<const char *> &extra_args = {}); ~ChainTestingSetup(); }; /** * Testing setup that configures a complete environment. */ struct TestingSetup : public ChainTestingSetup { bool m_coins_db_in_memory{true}; bool m_block_tree_db_in_memory{true}; void LoadVerifyActivateChainstate(const Config &config); explicit TestingSetup(const std::string &chainName = CBaseChainParams::MAIN, const std::vector<const char *> &extra_args = {}, const bool coins_db_in_memory = true, const bool block_tree_db_in_memory = true); }; /** Identical to TestingSetup, but chain set to regtest */ struct RegTestingSetup : public TestingSetup { RegTestingSetup() : TestingSetup{CBaseChainParams::REGTEST} {} }; class CBlock; class Chainstate; class CMutableTransaction; class CScript; /** * Testing fixture that pre-creates a 100-block REGTEST-mode block chain */ struct TestChain100Setup : public TestingSetup { TestChain100Setup(const std::string &chain_name = CBaseChainParams::REGTEST, const std::vector<const char *> &extra_args = {}, const bool coins_db_in_memory = true, const bool block_tree_db_in_memory = true); /** * Create a new block with just given transactions, coinbase paying to * scriptPubKey, and try to add it to the current chain. * If no chainstate is specified, default to the active. */ CBlock CreateAndProcessBlock(const std::vector<CMutableTransaction> &txns, const CScript &scriptPubKey, Chainstate *chainstate = nullptr); /** * Create a new block with just given transactions, coinbase paying to * scriptPubKey. */ CBlock CreateBlock(const std::vector<CMutableTransaction> &txns, const CScript &scriptPubKey, Chainstate &chainstate); //! Mine a series of new blocks on the active chain. void mineBlocks(int num_blocks); /** * Create a transaction and submit to the mempool. * * @param input_transaction The transaction to spend * @param input_vout The vout to spend from the input_transaction * @param input_height The height of the block that included the * input_transaction * @param input_signing_key The key to spend the input_transaction * @param output_destination Where to send the output * @param output_amount How much to send * @param submit Whether or not to submit to mempool */ CMutableTransaction CreateValidMempoolTransaction( CTransactionRef input_transaction, int input_vout, int input_height, CKey input_signing_key, CScript output_destination, Amount output_amount = COIN, bool submit = true); ~TestChain100Setup(); // For convenience, coinbase transactions. std::vector<CTransactionRef> m_coinbase_txns; // private/public key needed to spend coinbase transactions. CKey coinbaseKey; }; +/** + * Make a test setup that has disk access to the debug.log file disabled. Can + * be used in "hot loops", for example fuzzing or benchmarking. + */ +template <class T = const BasicTestingSetup> +std::unique_ptr<T> +MakeNoLogFileContext(const std::string &chain_name = CBaseChainParams::REGTEST, + const std::vector<const char *> &extra_args = {}) { + const std::vector<const char *> arguments = Cat( + { + "-nodebuglogfile", + "-nodebug", + }, + extra_args); + + return std::make_unique<T>(chain_name, arguments); +} + struct TestMemPoolEntryHelper { // Default values Amount nFee; int64_t nTime; unsigned int nHeight; bool spendsCoinbase; unsigned int nSigChecks; uint64_t entryId = 0; TestMemPoolEntryHelper() : nFee(), nTime(0), nHeight(1), spendsCoinbase(false), nSigChecks(1) {} CTxMemPoolEntryRef FromTx(const CMutableTransaction &tx) const; CTxMemPoolEntryRef FromTx(const CTransactionRef &tx) const; // Change the default value TestMemPoolEntryHelper &Fee(Amount _fee) { nFee = _fee; return *this; } TestMemPoolEntryHelper &Time(int64_t _time) { nTime = _time; return *this; } TestMemPoolEntryHelper &Height(unsigned int _height) { nHeight = _height; return *this; } TestMemPoolEntryHelper &SpendsCoinbase(bool _flag) { spendsCoinbase = _flag; return *this; } TestMemPoolEntryHelper &SigChecks(unsigned int _nSigChecks) { nSigChecks = _nSigChecks; return *this; } TestMemPoolEntryHelper &EntryId(uint64_t _entryId) { entryId = _entryId; return *this; } }; enum class ScriptError; // define implicit conversions here so that these types may be used in // BOOST_*_EQUAL std::ostream &operator<<(std::ostream &os, const uint256 &num); std::ostream &operator<<(std::ostream &os, const ScriptError &err); CBlock getBlock13b8a(); /** * BOOST_CHECK_EXCEPTION predicates to check the specific validation error. * Use as * BOOST_CHECK_EXCEPTION(code that throws, exception type, HasReason("foo")); */ class HasReason { public: explicit HasReason(const std::string &reason) : m_reason(reason) {} bool operator()(const std::exception &e) const { return std::string(e.what()).find(m_reason) != std::string::npos; }; private: const std::string m_reason; }; #endif // BITCOIN_TEST_UTIL_SETUP_COMMON_H