aboutsummaryrefslogtreecommitdiff
path: root/src/test/fuzz/block_index.cpp
blob: eef8c2efc808c06d167348d2f64affdc76ccd7af (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
// Copyright (c) 2023 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 <chain.h>
#include <chainparams.h>
#include <node/blockstorage.h>
#include <test/fuzz/FuzzedDataProvider.h>
#include <test/fuzz/fuzz.h>
#include <test/fuzz/util.h>
#include <test/util/setup_common.h>
#include <txdb.h>
#include <validation.h>

namespace {

const BasicTestingSetup* g_setup;

// Hardcoded block hash and nBits to make sure the blocks we store pass the pow check.
uint256 g_block_hash;

bool operator==(const CBlockFileInfo& a, const CBlockFileInfo& b)
{
    return a.nBlocks == b.nBlocks &&
        a.nSize == b.nSize &&
        a.nUndoSize == b.nUndoSize &&
        a.nHeightFirst == b.nHeightFirst &&
        a.nHeightLast == b.nHeightLast &&
        a.nTimeFirst == b.nTimeFirst &&
        a.nTimeLast == b.nTimeLast;
}

CBlockHeader ConsumeBlockHeader(FuzzedDataProvider& provider)
{
    CBlockHeader header;
    header.nVersion = provider.ConsumeIntegral<decltype(header.nVersion)>();
    header.hashPrevBlock = g_block_hash;
    header.hashMerkleRoot = g_block_hash;
    header.nTime = provider.ConsumeIntegral<decltype(header.nTime)>();
    header.nBits = Params().GenesisBlock().nBits;
    header.nNonce = provider.ConsumeIntegral<decltype(header.nNonce)>();
    return header;
}

} // namespace

void init_block_index()
{
    static const auto testing_setup = MakeNoLogFileContext<>(ChainType::MAIN);
    g_setup = testing_setup.get();
    g_block_hash = Params().GenesisBlock().GetHash();
}

FUZZ_TARGET(block_index, .init = init_block_index)
{
    FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
    auto block_index = kernel::BlockTreeDB(DBParams{
        .path = "", // Memory only.
        .cache_bytes = 1 << 20, // 1MB.
        .memory_only = true,
    });

    // Generate a number of block files to be stored in the index.
    int files_count = fuzzed_data_provider.ConsumeIntegralInRange(1, 100);
    std::vector<std::unique_ptr<CBlockFileInfo>> files;
    files.reserve(files_count);
    std::vector<std::pair<int, const CBlockFileInfo*>> files_info;
    files_info.reserve(files_count);
    for (int i = 0; i < files_count; i++) {
        if (auto file_info = ConsumeDeserializable<CBlockFileInfo>(fuzzed_data_provider)) {
            files.push_back(std::make_unique<CBlockFileInfo>(std::move(*file_info)));
            files_info.emplace_back(i, files.back().get());
        } else {
            return;
        }
    }

    // Generate a number of block headers to be stored in the index.
    int blocks_count = fuzzed_data_provider.ConsumeIntegralInRange(files_count * 10, files_count * 100);
    std::vector<std::unique_ptr<CBlockIndex>> blocks;
    blocks.reserve(blocks_count);
    std::vector<const CBlockIndex*> blocks_info;
    blocks_info.reserve(blocks_count);
    for (int i = 0; i < blocks_count; i++) {
        CBlockHeader header{ConsumeBlockHeader(fuzzed_data_provider)};
        blocks.push_back(std::make_unique<CBlockIndex>(std::move(header)));
        blocks.back()->phashBlock = &g_block_hash;
        blocks_info.push_back(blocks.back().get());
    }

    // Store these files and blocks in the block index. It should not fail.
    assert(block_index.WriteBatchSync(files_info, files_count - 1, blocks_info));

    // We should be able to read every block file info we stored. Its value should correspond to
    // what we stored above.
    CBlockFileInfo info;
    for (const auto& [n, file_info]: files_info) {
        assert(block_index.ReadBlockFileInfo(n, info));
        assert(info == *file_info);
    }

    // We should be able to read the last block file number. Its value should be consistent.
    int last_block_file;
    assert(block_index.ReadLastBlockFile(last_block_file));
    assert(last_block_file == files_count - 1);

    // We should be able to flip and read the reindexing flag.
    bool reindexing;
    block_index.WriteReindexing(true);
    block_index.ReadReindexing(reindexing);
    assert(reindexing);
    block_index.WriteReindexing(false);
    block_index.ReadReindexing(reindexing);
    assert(!reindexing);

    // We should be able to set and read the value of any random flag.
    const std::string flag_name = fuzzed_data_provider.ConsumeRandomLengthString(100);
    bool flag_value;
    block_index.WriteFlag(flag_name, true);
    block_index.ReadFlag(flag_name, flag_value);
    assert(flag_value);
    block_index.WriteFlag(flag_name, false);
    block_index.ReadFlag(flag_name, flag_value);
    assert(!flag_value);

    // We should be able to load everything we've previously stored. Note to assert on the
    // return value we need to make sure all blocks pass the pow check.
    const auto params{Params().GetConsensus()};
    const auto inserter = [&](const uint256&) {
        return blocks.back().get();
    };
    WITH_LOCK(::cs_main, assert(block_index.LoadBlockIndexGuts(params, inserter, g_setup->m_interrupt)));
}