diff options
author | MarcoFalke <falke.marco@gmail.com> | 2021-04-03 13:39:42 +0200 |
---|---|---|
committer | MarcoFalke <falke.marco@gmail.com> | 2021-05-16 11:34:27 +0200 |
commit | fa91994b1b9b6da3bb2a2f722d9251d7d0f2167e (patch) | |
tree | 1c79c7d6e26a4088973ba6104d5c0883895f9818 /src/test | |
parent | c8571486364d6e9ca8c86bd1c81e230ca64f8904 (diff) |
fuzz: Add utxo_snapshot target
Diffstat (limited to 'src/test')
-rw-r--r-- | src/test/fuzz/utxo_snapshot.cpp | 87 | ||||
-rw-r--r-- | src/test/util/mining.cpp | 33 | ||||
-rw-r--r-- | src/test/util/mining.h | 5 | ||||
-rw-r--r-- | src/test/validation_tests.cpp | 8 |
4 files changed, 129 insertions, 4 deletions
diff --git a/src/test/fuzz/utxo_snapshot.cpp b/src/test/fuzz/utxo_snapshot.cpp new file mode 100644 index 0000000000..0b42282b34 --- /dev/null +++ b/src/test/fuzz/utxo_snapshot.cpp @@ -0,0 +1,87 @@ +// Copyright (c) 2021 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 <consensus/validation.h> +#include <test/fuzz/FuzzedDataProvider.h> +#include <test/fuzz/fuzz.h> +#include <test/fuzz/util.h> +#include <test/util/mining.h> +#include <test/util/setup_common.h> +#include <validation.h> +#include <validationinterface.h> + +namespace { + +const std::vector<std::shared_ptr<CBlock>>* g_chain; + +void initialize_chain() +{ + const auto params{CreateChainParams(ArgsManager{}, CBaseChainParams::REGTEST)}; + static const auto chain{CreateBlockChain(2 * COINBASE_MATURITY, *params)}; + g_chain = &chain; +} + +FUZZ_TARGET_INIT(utxo_snapshot, initialize_chain) +{ + FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); + std::unique_ptr<const TestingSetup> setup{MakeNoLogFileContext<const TestingSetup>()}; + const auto& node = setup->m_node; + auto& chainman{*node.chainman}; + + const auto snapshot_path = GetDataDir() / "fuzzed_snapshot.dat"; + + Assert(!chainman.SnapshotBlockhash()); + + { + CAutoFile outfile{fsbridge::fopen(snapshot_path, "wb"), SER_DISK, CLIENT_VERSION}; + const auto file_data{ConsumeRandomLengthByteVector(fuzzed_data_provider)}; + outfile << Span<const uint8_t>{file_data}; + } + + const auto ActivateFuzzedSnapshot{[&] { + CAutoFile infile{fsbridge::fopen(snapshot_path, "rb"), SER_DISK, CLIENT_VERSION}; + SnapshotMetadata metadata; + try { + infile >> metadata; + } catch (const std::ios_base::failure&) { + return false; + } + return chainman.ActivateSnapshot(infile, metadata, /* in_memory */ true); + }}; + + if (fuzzed_data_provider.ConsumeBool()) { + for (const auto& block : *g_chain) { + BlockValidationState dummy; + bool processed{chainman.ProcessNewBlockHeaders({*block}, dummy, ::Params())}; + Assert(processed); + const auto* index{WITH_LOCK(::cs_main, return chainman.m_blockman.LookupBlockIndex(block->GetHash()))}; + Assert(index); + } + } + + if (ActivateFuzzedSnapshot()) { + LOCK(::cs_main); + Assert(!chainman.ActiveChainstate().m_from_snapshot_blockhash->IsNull()); + Assert(*chainman.ActiveChainstate().m_from_snapshot_blockhash == + *chainman.SnapshotBlockhash()); + const auto& coinscache{chainman.ActiveChainstate().CoinsTip()}; + int64_t chain_tx{}; + for (const auto& block : *g_chain) { + Assert(coinscache.HaveCoin(COutPoint{block->vtx.at(0)->GetHash(), 0})); + const auto* index{chainman.m_blockman.LookupBlockIndex(block->GetHash())}; + const auto num_tx{Assert(index)->nTx}; + Assert(num_tx == 1); + chain_tx += num_tx; + } + Assert(g_chain->size() == coinscache.GetCacheSize()); + Assert(chain_tx == chainman.ActiveTip()->nChainTx); + } else { + Assert(!chainman.SnapshotBlockhash()); + Assert(!chainman.ActiveChainstate().m_from_snapshot_blockhash); + } + // Snapshot should refuse to load a second time regardless of validity + Assert(!ActivateFuzzedSnapshot()); +} +} // namespace diff --git a/src/test/util/mining.cpp b/src/test/util/mining.cpp index 3fc3329da2..1204873828 100644 --- a/src/test/util/mining.cpp +++ b/src/test/util/mining.cpp @@ -11,8 +11,10 @@ #include <node/context.h> #include <pow.h> #include <script/standard.h> +#include <test/util/script.h> #include <util/check.h> #include <validation.h> +#include <versionbits.h> CTxIn generatetoaddress(const NodeContext& node, const std::string& address) { @@ -23,6 +25,37 @@ CTxIn generatetoaddress(const NodeContext& node, const std::string& address) return MineBlock(node, coinbase_script); } +std::vector<std::shared_ptr<CBlock>> CreateBlockChain(size_t total_height, const CChainParams& params) +{ + std::vector<std::shared_ptr<CBlock>> ret{total_height}; + auto time{params.GenesisBlock().nTime}; + for (size_t height{0}; height < total_height; ++height) { + CBlock& block{*(ret.at(height) = std::make_shared<CBlock>())}; + + CMutableTransaction coinbase_tx; + coinbase_tx.vin.resize(1); + coinbase_tx.vin[0].prevout.SetNull(); + coinbase_tx.vout.resize(1); + coinbase_tx.vout[0].scriptPubKey = P2WSH_OP_TRUE; + coinbase_tx.vout[0].nValue = GetBlockSubsidy(height + 1, params.GetConsensus()); + coinbase_tx.vin[0].scriptSig = CScript() << (height + 1) << OP_0; + block.vtx = {MakeTransactionRef(std::move(coinbase_tx))}; + + block.nVersion = VERSIONBITS_LAST_OLD_BLOCK_VERSION; + block.hashPrevBlock = (height >= 1 ? *ret.at(height - 1) : params.GenesisBlock()).GetHash(); + block.hashMerkleRoot = BlockMerkleRoot(block); + block.nTime = ++time; + block.nBits = params.GenesisBlock().nBits; + block.nNonce = 0; + + while (!CheckProofOfWork(block.GetHash(), block.nBits, params.GetConsensus())) { + ++block.nNonce; + assert(block.nNonce); + } + } + return ret; +} + CTxIn MineBlock(const NodeContext& node, const CScript& coinbase_scriptPubKey) { auto block = PrepareBlock(node, coinbase_scriptPubKey); diff --git a/src/test/util/mining.h b/src/test/util/mining.h index 5f250fffe8..1fc1864b91 100644 --- a/src/test/util/mining.h +++ b/src/test/util/mining.h @@ -7,12 +7,17 @@ #include <memory> #include <string> +#include <vector> class CBlock; +class CChainParams; class CScript; class CTxIn; struct NodeContext; +/** Create a blockchain, starting from genesis */ +std::vector<std::shared_ptr<CBlock>> CreateBlockChain(size_t total_height, const CChainParams& params); + /** Returns the generated coin */ CTxIn MineBlock(const NodeContext&, const CScript& coinbase_scriptPubKey); diff --git a/src/test/validation_tests.cpp b/src/test/validation_tests.cpp index 1e5baec01a..a0c2e76f00 100644 --- a/src/test/validation_tests.cpp +++ b/src/test/validation_tests.cpp @@ -136,11 +136,11 @@ BOOST_AUTO_TEST_CASE(test_assumeutxo) const auto out110 = *ExpectedAssumeutxo(110, *params); BOOST_CHECK_EQUAL(out110.hash_serialized.ToString(), "1ebbf5850204c0bdb15bf030f47c7fe91d45c44c712697e4509ba67adb01c618"); - BOOST_CHECK_EQUAL(out110.nChainTx, (unsigned int)110); + BOOST_CHECK_EQUAL(out110.nChainTx, 110U); - const auto out210 = *ExpectedAssumeutxo(210, *params); - BOOST_CHECK_EQUAL(out210.hash_serialized.ToString(), "9c5ed99ef98544b34f8920b6d1802f72ac28ae6e2bd2bd4c316ff10c230df3f2"); - BOOST_CHECK_EQUAL(out210.nChainTx, (unsigned int)210); + const auto out210 = *ExpectedAssumeutxo(200, *params); + BOOST_CHECK_EQUAL(out210.hash_serialized.ToString(), "51c8d11d8b5c1de51543c579736e786aa2736206d1e11e627568029ce092cf62"); + BOOST_CHECK_EQUAL(out210.nChainTx, 200U); } BOOST_AUTO_TEST_SUITE_END() |