diff --git a/fuzz/CMakeLists.txt b/fuzz/CMakeLists.txt index 06515a5d7..8d2791385 100644 --- a/fuzz/CMakeLists.txt +++ b/fuzz/CMakeLists.txt @@ -5,8 +5,6 @@ vbk_define(VBK_FUZZING_UNSAFE_FOR_PRODUCTION) # WEIGHT defines how "important" fuzzer is. More important fuzzers will run longer in CI. set(TOTAL_WEIGHT 27) -add_subdirectory(e2e) - add_fuzz(address_deserialization_fuzz SOURCES address_deserialization_fuzz.cpp ARGS @@ -26,9 +24,7 @@ add_fuzz(value_sorted_map_fuzz add_fuzz(e2e_fuzz SOURCES - e2e/fuzzer.cpp - e2e/tree.cpp - e2e/tree.hpp + e2e_fuzz.cpp ARGS # fail if malloc(>10MB) is called -malloc_limit_mb=10 @@ -38,3 +34,4 @@ add_fuzz(e2e_fuzz -max_len=100000 WEIGHT 20 ) +target_link_libraries(e2e_fuzz PUBLIC e2e_utils) diff --git a/fuzz/e2e/CMakeLists.txt b/fuzz/e2e/CMakeLists.txt deleted file mode 100644 index e69de29bb..000000000 diff --git a/fuzz/e2e/fuzzer.cpp b/fuzz/e2e/fuzzer.cpp deleted file mode 100644 index a28c30d28..000000000 --- a/fuzz/e2e/fuzzer.cpp +++ /dev/null @@ -1,334 +0,0 @@ -// Copyright (c) 2019-2022 Xenios SEZC -// https://www.veriblock.org -// Distributed under the MIT software license, see the accompanying -// file LICENSE or http://www.opensource.org/licenses/mit-license.php. - -#include - -#include "../EntitiesProviders.hpp" -#include "../FuzzedDataProvider.hpp" -#include "rand.hpp" -#include "tree.hpp" - -namespace ai = altintegration; - -static const int hashsize = 8; - -struct AtvCandidate { - ai::VbkTx tx; - ai::VbkBlock blockOfProof; -}; - -struct FuzzState { - ai::AltChainParamsRegTest altparam; - ai::VbkChainParamsRegTest vbkparam; - ai::BtcChainParamsRegTest btcparam; - ai::MockMiner mm{altparam, vbkparam, btcparam}; - fuzz::Tree tree; - std::list vtbs; - std::list atvs; - std::list atvcandidates; - std::vector vbktxes; - std::vector vbkpoptxes; - std::vector> btctxes; - - ai::MockMiner& APM() { return mm; } - - fuzz::Tree& TREE() { return tree; } -}; - -enum class TxType { - VBK_TX, - VBK_POP_TX, - - kMaxValue = VBK_POP_TX -}; - -enum class Action { - MINE_ALT, - MINE_VBK, - MINE_BTC, - CREATE_BTC_TX, - CREATE_VBK_TX, - CREATE_VBK_POP_TX, - SUBMIT_ATV, - SUBMIT_VTB, - SUBMIT_VBK, - SUBMIT_ALL, - - kMaxValue = SUBMIT_ALL -}; - -enum class VbkSubmitType { ACTIVE_CHAIN, RANDOM, kMaxValue = RANDOM }; - -enum class ForkOption { - NEXT_AFTER_TIP, - RANDOM_BLOCK, - ONE_OF_TIPS, - kMaxValue = ONE_OF_TIPS -}; - -template -const typename Tree::index_t* selectBlock(FuzzedDataProvider& p, Tree& tree) { - auto option = p.ConsumeEnum(); - switch (option) { - case ForkOption::NEXT_AFTER_TIP: - return tree.getBestChain().tip(); - case ForkOption::RANDOM_BLOCK: { - auto blocks = tree.getBlocks(); - auto it = select_randomly(blocks.begin(), blocks.end()); - return *it; - } - case ForkOption::ONE_OF_TIPS: - const auto& tips = tree.getTips(); - auto it = select_randomly(tips.begin(), tips.end()); - return *it; - } - - VBK_ASSERT(false); -} - -fuzz::Block mineNextBlock(const ai::BlockIndex* index, - FuzzState& state) { - fuzz::Block block; - block.popdata = state.TREE().popcontext->getMemPool().generatePopData(); - std::generate_n( - std::back_inserter(block.hash), hashsize, []() { return rand(); }); - block.timestamp = index->getTimestamp() + 1; - block.height = index->getHeight() + 1; - block.prevhash = index->getHash(); - return block; -} - -bool handle(FuzzedDataProvider& p, FuzzState& state) { - if (p.remaining_bytes() == 0) { - return false; - } - - switch (p.ConsumeEnum()) { - case Action::MINE_ALT: { - // in ALT mine only chain - auto* prev = state.TREE().popcontext->getAltBlockTree().getBlockIndex( - state.TREE().bestBlock); - VBK_ASSERT(prev != nullptr); - auto block = mineNextBlock(prev, state); - /* ignore=*/state.TREE().acceptBlock(block); - break; - } - case Action::MINE_VBK: { - auto* prev = selectBlock(p, state.APM().vbk()); - auto option = p.ConsumeEnum(); - switch (option) { - case TxType::VBK_TX: { - auto* blockOfProof = - state.APM().mineVbkBlocks(1, *prev, state.vbktxes); - if (blockOfProof == nullptr) { - state.vbktxes.pop_back(); - break; - } - for (auto& z : state.vbktxes) { - AtvCandidate c; - c.blockOfProof = blockOfProof->getHeader(); - c.tx = z; - state.atvcandidates.push_back(std::move(c)); - } - state.vbktxes.clear(); - break; - } - case TxType::VBK_POP_TX: - auto* containing = - state.APM().mineVbkBlocks(1, *prev, state.vbkpoptxes); - if (containing == nullptr) { - state.vbkpoptxes.pop_back(); - break; - } - for (auto& z : state.vbkpoptxes) { - auto vtb = state.APM().createVTB(containing->getHeader(), z); - state.vtbs.push_back(vtb); - } - state.vbkpoptxes.clear(); - break; - } - - break; - } - case Action::MINE_BTC: { - auto* prev = selectBlock(p, state.APM().btc()); - std::vector v; - v.reserve(state.btctxes.size()); - for (const auto& z : state.btctxes) { - v.push_back(z.first); - } - /* ignore=*/state.APM().mineBtcBlocks(1, *prev, v); - state.btctxes.clear(); - break; - } - case Action::CREATE_BTC_TX: { - auto* block = selectBlock(p, state.APM().vbk()); - auto tx = state.APM().createBtcTxEndorsingVbkBlock(block->getHeader()); - state.btctxes.emplace_back(tx, block->getHeader()); - break; - } - case Action::CREATE_VBK_TX: { - auto* endorsedIndex = - selectBlock(p, state.TREE().popcontext->getAltBlockTree()); - VBK_ASSERT(endorsedIndex); - std::vector mroot{1, 2, 3, 4, 5}; - auto* endorsedBlock = state.TREE().getBlock(endorsedIndex->getHash()); - // if endorsedBlock is not found in TREE() then skip - VBK_ASSERT_MSG( - endorsedBlock != nullptr, - "TREE:\n%s\n\nAltBlockTree:\n%s\n\n", - state.TREE().toPrettyString(), - state.TREE().popcontext->getAltBlockTree().toPrettyString()); - ai::PublicationData pd; - bool result = ai::GeneratePublicationData( - endorsedIndex->getHash(), - mroot, - endorsedBlock->popdata, - mroot, - state.TREE().popcontext->getAltBlockTree(), - pd); - VBK_ASSERT(result); - auto tx = state.APM().createVbkTxEndorsingAltBlock(pd); - state.vbktxes.push_back(std::move(tx)); - break; - } - case Action::CREATE_VBK_POP_TX: { - auto* block = selectBlock(p, state.APM().vbk()); - auto tx = state.APM().createVbkPopTxEndorsingVbkBlock( - block->getHeader(), state.TREE().lastBtc().getHash()); - state.vbkpoptxes.push_back(tx); - break; - } - case Action::SUBMIT_ATV: { - if (state.atvcandidates.empty()) { - break; - } - - auto candidate = state.atvcandidates.front(); - state.atvcandidates.pop_front(); - - auto atv = state.APM().createATV(candidate.blockOfProof, candidate.tx); - ai::ValidationState dummy; - /* ignore= */ state.TREE().popcontext->getMemPool().submit( - atv, dummy); - break; - } - case Action::SUBMIT_VTB: { - if (state.vtbs.empty()) { - break; - } - - auto vtb = state.vtbs.front(); - state.vtbs.pop_front(); - - ai::ValidationState dummy; - /* ignore= */ state.TREE().popcontext->getMemPool().submit( - vtb, dummy); - break; - } - case Action::SUBMIT_VBK: { - auto option = p.ConsumeEnum(); - switch (option) { - case VbkSubmitType::ACTIVE_CHAIN: { - auto lastKnownVbk = state.TREE().lastVbk(); - auto* index = state.APM().vbk().getBlockIndex(lastKnownVbk.getHash()); - VBK_ASSERT(index); - - auto* fork = ai::findFork(state.APM().vbk().getBestChain(), index); - VBK_ASSERT(fork); - - auto* tip = state.APM().vbk().getBestChain().tip(); - VBK_ASSERT(tip->getHeight() >= fork->getHeight()); - auto context = - ai::getContext(state.APM().vbk(), - tip->getHash(), - tip->getHeight() - fork->getHeight() + 1); - - for (auto& c : context) { - ai::ValidationState dummy; - /* ignore= */ state.TREE() - .popcontext->getMemPool() - .submit(c, dummy); - } - - break; - } - case VbkSubmitType::RANDOM: { - auto blocks = state.APM().vbk().getBlocks(); - auto it = select_randomly(blocks.begin(), blocks.end()); - VBK_ASSERT(it != blocks.end()); - - ai::ValidationState dummy; - /* ignore= */ state.TREE() - .popcontext->getMemPool() - .submit((*it)->getHeader(), dummy); - break; - } - } - break; - } - case Action::SUBMIT_ALL: { - // submit everything we have - - // all vbk blocks - const auto& blocks = state.APM().vbk().getBlocks(); - for (auto& block : blocks) { - ai::ValidationState dummy; - /* ignore= */ state.TREE() - .popcontext->getMemPool() - .submit(block->getHeader(), dummy); - } - - // submit all ATVs - for (auto& a : state.atvs) { - ai::ValidationState dummy; - /* ignore= */ state.TREE().popcontext->getMemPool().submit( - a, dummy); - } - state.atvs.clear(); - - // submit all VTBs - for (auto& a : state.vtbs) { - ai::ValidationState dummy; - /* ignore= */ state.TREE().popcontext->getMemPool().submit( - a, dummy); - } - state.vtbs.clear(); - - for (auto& a : state.APM().getAllVTBs()) { - for (auto& vtb : a.second) { - ai::ValidationState dummy; - /* ignore= */ state.TREE().popcontext->getMemPool().submit( - vtb, dummy); - } - } - break; - } - } - - return true; -} - -extern "C" int LLVMFuzzerTestOneInput(const uint8_t* Data, size_t Size) { - // make sure input is at least of size 10 - if (Size < 10) { - return 0; - } - - FuzzedDataProvider p(Data, Size); - srand(p.ConsumeIntegral()); - FuzzState state; - - bool hasEnoughData = true; - do { - try { - hasEnoughData = handle(p, state); - } catch (const NotEnoughDataException& e) { - break; - } - } while (hasEnoughData); - - return 0; -} \ No newline at end of file diff --git a/fuzz/e2e/rand.hpp b/fuzz/e2e/rand.hpp deleted file mode 100644 index a781fb834..000000000 --- a/fuzz/e2e/rand.hpp +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright (c) 2019-2022 Xenios SEZC -// https://www.veriblock.org -// Distributed under the MIT software license, see the accompanying -// file LICENSE or http://www.opensource.org/licenses/mit-license.php. - -#ifndef VERIBLOCK_POP_CPP_RAND_FUZZ_HPP -#define VERIBLOCK_POP_CPP_RAND_FUZZ_HPP - -#include -#include -#include - -template -Iter select_randomly(Iter start, Iter end, RandomGenerator g) { - std::uniform_int_distribution<> dis(0, std::distance(start, end) - 1); - std::advance(start, dis(g)); - return start; -} - -template -Iter select_randomly(Iter start, Iter end) { - std::mt19937 m(rand()); - return select_randomly(start, end, m); -} - -#endif // VERIBLOCK_POP_CPP_RAND_FUZZ_HPP diff --git a/fuzz/e2e/tree.cpp b/fuzz/e2e/tree.cpp deleted file mode 100644 index 16f5fbbeb..000000000 --- a/fuzz/e2e/tree.cpp +++ /dev/null @@ -1,98 +0,0 @@ -// Copyright (c) 2019-2022 Xenios SEZC -// https://www.veriblock.org -// Distributed under the MIT software license, see the accompanying -// file LICENSE or http://www.opensource.org/licenses/mit-license.php. - -#include "tree.hpp" - -namespace fuzz { - -Block genesisBlock = []() -> Block { - Block b; - b.timestamp = 0; - b.hash = {1, 2, 3, 4}; - b.height = 0; - return b; -}(); - -bool Tree::acceptBlock(const Block& block) { - if (block.prevhash == block.hash) { - // bad hash - return false; - } - - auto* prev = getBlock(block.prevhash); - if (prev == nullptr) { - // can't connect - return false; - } - - auto altheader = block.toAltBlock(); - using altintegration::ValidationState; - ValidationState state; - if (!popcontext->getAltBlockTree().acceptBlockHeader(altheader, state)) { - return false; - } - - auto* bestIndex = popcontext->getAltBlockTree().getBlockIndex(bestBlock); - VBK_ASSERT(bestIndex); - VBK_ASSERT(bestIndex->isConnected()); - - popcontext->getAltBlockTree().acceptBlock(altheader.hash, block.popdata); - - blocks[block.hash] = std::make_shared(block); - - auto* index = popcontext->getAltBlockTree().getBlockIndex(altheader.hash); - VBK_ASSERT(index); - if (!index->isConnected()) { - // do not invoke POP FR if index is not connected - return true; - } - - // try POP FR - bool success = popcontext->getAltBlockTree().setState(*bestIndex, state); - VBK_ASSERT(success); - - int result = popcontext->getAltBlockTree().comparePopScore( - bestIndex->getHash(), index->getHash()); - if (result < 0) { - bestBlock = index->getHash(); - } else if (result == 0) { - if (bestIndex->getHeight() < index->getHeight()) { - bestBlock = index->getHash(); - } - } else { - // bestIndex is still best - } - - success = popcontext->getAltBlockTree().setState(*bestIndex, state); - VBK_ASSERT(success); - - return true; -} - -Block* Tree::getBlock(const std::vector& hash) { - auto it = blocks.find(hash); - if (it == blocks.end()) { - return nullptr; - } - - return it->second.get(); -} - -Tree::Tree() { - // bootstrap with genesis block - blocks[genesisBlock.hash] = std::make_shared(genesisBlock); - bestBlock = genesisBlock.hash; - params = std::make_shared(); - pp = std::make_shared(storage); - bp = std::make_shared(storage, *params); - - auto config = std::make_shared(); - config->SelectBtcParams("regtest", 0, {}); - config->SelectVbkParams("regtest", 0, {}); - config->SelectAltParams(params); - - popcontext = altintegration::PopContext::create(config, pp, bp); -} -} // namespace fuzz \ No newline at end of file diff --git a/fuzz/e2e/tree.hpp b/fuzz/e2e/tree.hpp deleted file mode 100644 index edcbc4551..000000000 --- a/fuzz/e2e/tree.hpp +++ /dev/null @@ -1,102 +0,0 @@ -// Copyright (c) 2019-2022 Xenios SEZC -// https://www.veriblock.org -// Distributed under the MIT software license, see the accompanying -// file LICENSE or http://www.opensource.org/licenses/mit-license.php. - -#ifndef VERIBLOCK_POP_CPP_TREE_HPP -#define VERIBLOCK_POP_CPP_TREE_HPP - -#include - -namespace fuzz { - -struct Block { - std::vector hash; - std::vector prevhash; - int height = 0; - int timestamp = altintegration::currentTimestamp4(); - altintegration::PopData popdata; - - altintegration::AltBlock toAltBlock() const { - altintegration::AltBlock block; - block.hash = hash; - block.previousBlock = prevhash; - block.timestamp = timestamp; - block.height = height; - return block; - } -}; - -extern Block genesisBlock; - -struct FuzzAltChainParams : public altintegration::AltChainParams { - int64_t getIdentifier() const noexcept override { return 0x1337; } - - altintegration::AltBlock getBootstrapBlock() const noexcept override { - return genesisBlock.toAltBlock(); - } - - std::vector getHash( - const std::vector& bytes) const noexcept override { - // endorsed header == endorsed hash - return bytes; - } - - bool checkBlockHeader( - const std::vector& bytes, - const std::vector& root, - altintegration::ValidationState& state) const noexcept override { - (void)state; - (void)bytes; - (void)root; - return true; - } -}; - -class Tree { - public: - Tree(); - - bool acceptBlock(const Block& block); - - Block* getBlock(const std::vector& hash); - - altintegration::BtcBlock lastBtc() const { - return popcontext->getBtcBlockTree().getBestChain().tip()->getHeader(); - } - - altintegration::VbkBlock lastVbk() const { - return popcontext->getVbkBlockTree().getBestChain().tip()->getHeader(); - } - - std::string toPrettyString() const { - std::string s; - std::vector b; - for (auto& it : blocks) { - b.push_back(it.second.get()); - } - std::sort(b.begin(), b.end(), [](const Block* a, const Block* b) { - return a->height < b->height; - }); - - for (auto& p : b) { - s += fmt::format("Block(height={} hash={})\n", - p->height, - altintegration::HexStr(p->hash)); - } - - return s; - } - - std::vector bestBlock; - altintegration::adaptors::InmemStorageImpl storage{}; - std::shared_ptr pp = nullptr; - std::shared_ptr bp = nullptr; - std::shared_ptr params = nullptr; - std::shared_ptr popcontext = nullptr; - std::unordered_map, std::shared_ptr> blocks; -}; - -} // namespace fuzz - -#endif // VERIBLOCK_POP_CPP_TREE_HPP diff --git a/fuzz/e2e_fuzz.cpp b/fuzz/e2e_fuzz.cpp new file mode 100644 index 000000000..32ef86f8c --- /dev/null +++ b/fuzz/e2e_fuzz.cpp @@ -0,0 +1,73 @@ +// Copyright (c) 2019-2022 Xenios SEZC +// https://www.veriblock.org +// Distributed under the MIT software license, see the accompanying +// file LICENSE or http://www.opensource.org/licenses/mit-license.php. + +#include +#include + +#include "../test/e2e/e2e_utils.hpp" +#include "EntitiesProviders.hpp" +#include "FuzzedDataProvider.hpp" + +struct FuzzState { + altintegration::AltChainParamsRegTest altparam{}; + altintegration::VbkChainParamsRegTest vbkparam{}; + altintegration::BtcChainParamsRegTest btcparam{}; + + altintegration::adaptors::InmemStorageImpl storage{}; + altintegration::adaptors::PayloadsStorageImpl payloadsProvider{storage}; + altintegration::adaptors::BlockReaderImpl blockProvider{storage, altparam}; + + altintegration::AltBlockTree tree{ + altparam, vbkparam, btcparam, payloadsProvider, blockProvider}; + altintegration::MemPool mempool{tree}; + + altintegration::testing_utils::E2EState e2e_state{ + altparam, vbkparam, btcparam}; +}; + +bool handle(FuzzedDataProvider& p, FuzzState& state) { + if (p.remaining_bytes() == 0) { + return false; + } + + // 10% probability - submit action + // 90% probability - create action + if (p.ConsumeIntegralInRange(0, 100) < 10) { + state.e2e_state.createAction( + p.ConsumeEnum(), + p.ConsumeEnum(), + state.tree); + + } else { + state.e2e_state.submitAction( + p.ConsumeEnum(), + state.mempool, + state.tree); + } + + return true; +} + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* Data, size_t Size) { + // make sure input is at least of size 10 + if (Size < 10) { + return 0; + } + + FuzzedDataProvider p(Data, Size); + srand(p.ConsumeIntegral()); + FuzzState state; + + bool hasEnoughData = true; + do { + try { + hasEnoughData = handle(p, state); + } catch (const NotEnoughDataException& e) { + break; + } + } while (hasEnoughData); + + return 0; +} \ No newline at end of file diff --git a/test/e2e/e2e_utils.hpp b/test/e2e/e2e_utils.hpp index 87df06bf9..fc4115df5 100644 --- a/test/e2e/e2e_utils.hpp +++ b/test/e2e/e2e_utils.hpp @@ -26,7 +26,7 @@ enum class CreateOption : uint8_t { CREATE_VTB, CREATE_ATV, - kMaxValue = CREATE_VBK_POP_TX + kMaxValue = CREATE_VBK_POP_TX, }; enum class SubmitOption : uint8_t { @@ -46,11 +46,6 @@ enum class ForkOption : uint8_t { kMaxValue = RANDOM_BLOCK }; -template -OptionT GetRandomOption() { - return static_cast(::rand() % static_cast(OptionT::kMaxValue)); -} - template const typename tree_t::index_t* getBlock(ForkOption fork, const tree_t& tree) { switch (fork) { diff --git a/test/e2e/e2e_utils_test.cpp b/test/e2e/e2e_utils_test.cpp index 27c214540..44ffe954b 100644 --- a/test/e2e/e2e_utils_test.cpp +++ b/test/e2e/e2e_utils_test.cpp @@ -19,6 +19,11 @@ struct E2E_Utils : public PopTestFixture, public ::testing::Test { : PopTestFixture(), e2e(this->altparam, this->vbkparam, this->btcparam) {} }; +template + OptionT GetRandomOption() { + return (OptionT)(rand() % (uint8_t)OptionT::kMaxValue); + } + TEST_F(E2E_Utils, submit_vbk) { auto fork = GetRandomOption();