Skip to content

Commit 6264c7b

Browse files
knstkwvg
authored andcommitted
merge bitcoin#21953: fuzz: Add utxo_snapshot target
1 parent 8b7ea28 commit 6264c7b

File tree

6 files changed

+133
-6
lines changed

6 files changed

+133
-6
lines changed

src/Makefile.test.include

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -331,6 +331,7 @@ test_fuzz_fuzz_SOURCES = \
331331
test/fuzz/tx_in.cpp \
332332
test/fuzz/tx_out.cpp \
333333
test/fuzz/tx_pool.cpp \
334+
test/fuzz/utxo_snapshot.cpp \
334335
test/fuzz/validation_load_mempool.cpp \
335336
test/fuzz/versionbits.cpp
336337
endif # ENABLE_FUZZ_BINARY

src/chainparams.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -901,8 +901,8 @@ class CRegTestParams : public CChainParams {
901901
{AssumeutxoHash{uint256S("0x9b2a277a3e3b979f1a539d57e949495d7f8247312dbc32bce6619128c192b44b")}, 110},
902902
},
903903
{
904-
210,
905-
{AssumeutxoHash{uint256S("0xd4c97d32882583b057efc3dce673e44204851435e6ffcef20346e69cddc7c91e")}, 210},
904+
200,
905+
{AssumeutxoHash{uint256S("0x8a5bdd92252fc6b24663244bbe958c947bb036dc1f94ccd15439f48d8d1cb4e3")}, 200},
906906
},
907907
};
908908

src/test/fuzz/utxo_snapshot.cpp

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
// Copyright (c) 2021 The Bitcoin Core developers
2+
// Distributed under the MIT software license, see the accompanying
3+
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4+
5+
#include <chainparams.h>
6+
#include <consensus/validation.h>
7+
#include <node/utxo_snapshot.h>
8+
#include <test/fuzz/FuzzedDataProvider.h>
9+
#include <test/fuzz/fuzz.h>
10+
#include <test/fuzz/util.h>
11+
#include <test/util/mining.h>
12+
#include <test/util/setup_common.h>
13+
#include <validation.h>
14+
#include <validationinterface.h>
15+
16+
namespace {
17+
18+
const std::vector<std::shared_ptr<CBlock>>* g_chain;
19+
20+
void initialize_chain()
21+
{
22+
const auto params{CreateChainParams(ArgsManager{}, CBaseChainParams::REGTEST)};
23+
static const auto chain{CreateBlockChain(2 * COINBASE_MATURITY, *params)};
24+
g_chain = &chain;
25+
}
26+
27+
FUZZ_TARGET_INIT(utxo_snapshot, initialize_chain)
28+
{
29+
FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
30+
std::unique_ptr<const TestingSetup> setup{MakeNoLogFileContext<const TestingSetup>()};
31+
const auto& node = setup->m_node;
32+
auto& chainman{*node.chainman};
33+
34+
const auto snapshot_path = GetDataDir() / "fuzzed_snapshot.dat";
35+
36+
Assert(!chainman.SnapshotBlockhash());
37+
38+
{
39+
CAutoFile outfile{fsbridge::fopen(snapshot_path, "wb"), SER_DISK, CLIENT_VERSION};
40+
const auto file_data{ConsumeRandomLengthByteVector(fuzzed_data_provider)};
41+
outfile << Span<const uint8_t>{file_data};
42+
}
43+
44+
const auto ActivateFuzzedSnapshot{[&] {
45+
CAutoFile infile{fsbridge::fopen(snapshot_path, "rb"), SER_DISK, CLIENT_VERSION};
46+
SnapshotMetadata metadata;
47+
try {
48+
infile >> metadata;
49+
} catch (const std::ios_base::failure&) {
50+
return false;
51+
}
52+
return chainman.ActivateSnapshot(infile, metadata, /* in_memory */ true);
53+
}};
54+
55+
if (fuzzed_data_provider.ConsumeBool()) {
56+
for (const auto& block : *g_chain) {
57+
BlockValidationState dummy;
58+
bool processed{chainman.ProcessNewBlockHeaders({*block}, dummy, ::Params())};
59+
Assert(processed);
60+
const auto* index{WITH_LOCK(::cs_main, return chainman.m_blockman.LookupBlockIndex(block->GetHash()))};
61+
Assert(index);
62+
}
63+
}
64+
65+
if (ActivateFuzzedSnapshot()) {
66+
LOCK(::cs_main);
67+
Assert(!chainman.ActiveChainstate().m_from_snapshot_blockhash->IsNull());
68+
Assert(*chainman.ActiveChainstate().m_from_snapshot_blockhash ==
69+
*chainman.SnapshotBlockhash());
70+
const auto& coinscache{chainman.ActiveChainstate().CoinsTip()};
71+
int64_t chain_tx{};
72+
for (const auto& block : *g_chain) {
73+
Assert(coinscache.HaveCoin(COutPoint{block->vtx.at(0)->GetHash(), 0}));
74+
const auto* index{chainman.m_blockman.LookupBlockIndex(block->GetHash())};
75+
const auto num_tx{Assert(index)->nTx};
76+
Assert(num_tx == 1);
77+
chain_tx += num_tx;
78+
}
79+
Assert(g_chain->size() == coinscache.GetCacheSize());
80+
Assert(chain_tx == chainman.ActiveTip()->nChainTx);
81+
} else {
82+
Assert(!chainman.SnapshotBlockhash());
83+
Assert(!chainman.ActiveChainstate().m_from_snapshot_blockhash);
84+
}
85+
// Snapshot should refuse to load a second time regardless of validity
86+
Assert(!ActivateFuzzedSnapshot());
87+
}
88+
} // namespace

src/test/util/mining.cpp

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,10 @@
1313
#include <pow.h>
1414
#include <script/standard.h>
1515
#include <spork.h>
16+
#include <test/util/script.h>
1617
#include <util/check.h>
1718
#include <validation.h>
19+
#include <versionbits.h>
1820

1921
CTxIn generatetoaddress(const NodeContext& node, const std::string& address)
2022
{
@@ -25,6 +27,37 @@ CTxIn generatetoaddress(const NodeContext& node, const std::string& address)
2527
return MineBlock(node, coinbase_script);
2628
}
2729

30+
std::vector<std::shared_ptr<CBlock>> CreateBlockChain(size_t total_height, const CChainParams& params)
31+
{
32+
std::vector<std::shared_ptr<CBlock>> ret{total_height};
33+
auto time{params.GenesisBlock().nTime};
34+
for (size_t height{0}; height < total_height; ++height) {
35+
CBlock& block{*(ret.at(height) = std::make_shared<CBlock>())};
36+
37+
CMutableTransaction coinbase_tx;
38+
coinbase_tx.vin.resize(1);
39+
coinbase_tx.vin[0].prevout.SetNull();
40+
coinbase_tx.vout.resize(1);
41+
coinbase_tx.vout[0].scriptPubKey = P2SH_OP_TRUE;
42+
coinbase_tx.vout[0].nValue = GetBlockSubsidyInner(params.GenesisBlock().nBits, height, params.GetConsensus(), false);
43+
coinbase_tx.vin[0].scriptSig = CScript() << (height + 1) << OP_0;
44+
block.vtx = {MakeTransactionRef(std::move(coinbase_tx))};
45+
46+
block.nVersion = VERSIONBITS_LAST_OLD_BLOCK_VERSION;
47+
block.hashPrevBlock = (height >= 1 ? *ret.at(height - 1) : params.GenesisBlock()).GetHash();
48+
block.hashMerkleRoot = BlockMerkleRoot(block);
49+
block.nTime = ++time;
50+
block.nBits = params.GenesisBlock().nBits;
51+
block.nNonce = 0;
52+
53+
while (!CheckProofOfWork(block.GetHash(), block.nBits, params.GetConsensus())) {
54+
++block.nNonce;
55+
assert(block.nNonce);
56+
}
57+
}
58+
return ret;
59+
}
60+
2861
CTxIn MineBlock(const NodeContext& node, const CScript& coinbase_scriptPubKey)
2962
{
3063
auto block = PrepareBlock(node, coinbase_scriptPubKey);

src/test/util/mining.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,17 @@
77

88
#include <memory>
99
#include <string>
10+
#include <vector>
1011

1112
class CBlock;
13+
class CChainParams;
1214
class CScript;
1315
class CTxIn;
1416
struct NodeContext;
1517

18+
/** Create a blockchain, starting from genesis */
19+
std::vector<std::shared_ptr<CBlock>> CreateBlockChain(size_t total_height, const CChainParams& params);
20+
1621
/** Returns the generated coin */
1722
CTxIn MineBlock(const NodeContext&, const CScript& coinbase_scriptPubKey);
1823

src/test/validation_tests.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,11 @@ BOOST_AUTO_TEST_CASE(test_assumeutxo)
2828

2929
const auto out110 = *ExpectedAssumeutxo(110, *params);
3030
BOOST_CHECK_EQUAL(out110.hash_serialized.ToString(), "9b2a277a3e3b979f1a539d57e949495d7f8247312dbc32bce6619128c192b44b");
31-
BOOST_CHECK_EQUAL(out110.nChainTx, (unsigned int)110);
31+
BOOST_CHECK_EQUAL(out110.nChainTx, 110U);
3232

33-
const auto out210 = *ExpectedAssumeutxo(210, *params);
34-
BOOST_CHECK_EQUAL(out210.hash_serialized.ToString(), "d4c97d32882583b057efc3dce673e44204851435e6ffcef20346e69cddc7c91e");
35-
BOOST_CHECK_EQUAL(out210.nChainTx, (unsigned int)210);
33+
const auto out210 = *ExpectedAssumeutxo(200, *params);
34+
BOOST_CHECK_EQUAL(out210.hash_serialized.ToString(), "8a5bdd92252fc6b24663244bbe958c947bb036dc1f94ccd15439f48d8d1cb4e3");
35+
BOOST_CHECK_EQUAL(out210.nChainTx, 200U);
3636
}
3737

3838
BOOST_AUTO_TEST_SUITE_END()

0 commit comments

Comments
 (0)