mirror of https://github.com/bitcoin/bitcoin
Merge bitcoin/bitcoin#25447: fuzz: add low-level target for txorphanage
pull/25565/head6eb0909cb7
fuzz: add low-level target for txorphanage (chinggg) Pull request description: This adds a low-level fuzz target for orphan transaction handling by creating random transactions and calling all functions in `TxOrphanage`. It cannot simulate real-world `orphan/unorphan` scenarios effectively since it does not maintain any state about the node and the chain. A high-level fuzz target which construct well-designed transaction graphs will be added later. ACKs for top commit: MarcoFalke: review ACK6eb0909cb7
🐈 Tree-SHA512: b4d64f5941df77d13981f75ec170cef6ffabe782797c982ede7f34134be01dc0026dd7c0bee614bc1d64715e90a933d2a8c95974d402e32eaba8e24cc928299e
commit
bac52a1cfe
@ -0,0 +1,143 @@
|
||||
// Copyright (c) 2022 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 <consensus/amount.h>
|
||||
#include <net.h>
|
||||
#include <net_processing.h>
|
||||
#include <primitives/transaction.h>
|
||||
#include <script/script.h>
|
||||
#include <sync.h>
|
||||
#include <test/fuzz/FuzzedDataProvider.h>
|
||||
#include <test/fuzz/fuzz.h>
|
||||
#include <test/fuzz/util.h>
|
||||
#include <test/util/setup_common.h>
|
||||
#include <txorphanage.h>
|
||||
#include <uint256.h>
|
||||
#include <util/check.h>
|
||||
#include <util/time.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
void initialize_orphanage()
|
||||
{
|
||||
static const auto testing_setup = MakeNoLogFileContext();
|
||||
}
|
||||
|
||||
FUZZ_TARGET_INIT(txorphan, initialize_orphanage)
|
||||
{
|
||||
FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
|
||||
SetMockTime(ConsumeTime(fuzzed_data_provider));
|
||||
|
||||
TxOrphanage orphanage;
|
||||
std::set<uint256> orphan_work_set;
|
||||
std::vector<COutPoint> outpoints;
|
||||
// initial outpoints used to construct transactions later
|
||||
for (uint8_t i = 0; i < 4; i++) {
|
||||
outpoints.emplace_back(uint256{i}, 0);
|
||||
}
|
||||
// if true, allow duplicate input when constructing tx
|
||||
const bool duplicate_input = fuzzed_data_provider.ConsumeBool();
|
||||
|
||||
LIMITED_WHILE(fuzzed_data_provider.ConsumeBool(), 10 * DEFAULT_MAX_ORPHAN_TRANSACTIONS)
|
||||
{
|
||||
// construct transaction
|
||||
const CTransactionRef tx = [&] {
|
||||
CMutableTransaction tx_mut;
|
||||
const auto num_in = fuzzed_data_provider.ConsumeIntegralInRange<uint32_t>(1, outpoints.size());
|
||||
const auto num_out = fuzzed_data_provider.ConsumeIntegralInRange<uint32_t>(1, outpoints.size());
|
||||
// pick unique outpoints from outpoints as input
|
||||
for (uint32_t i = 0; i < num_in; i++) {
|
||||
auto& prevout = PickValue(fuzzed_data_provider, outpoints);
|
||||
tx_mut.vin.emplace_back(prevout);
|
||||
// pop the picked outpoint if duplicate input is not allowed
|
||||
if (!duplicate_input) {
|
||||
std::swap(prevout, outpoints.back());
|
||||
outpoints.pop_back();
|
||||
}
|
||||
}
|
||||
// output amount will not affect txorphanage
|
||||
for (uint32_t i = 0; i < num_out; i++) {
|
||||
tx_mut.vout.emplace_back(CAmount{0}, CScript{});
|
||||
}
|
||||
// restore previously poped outpoints
|
||||
for (auto& in : tx_mut.vin) {
|
||||
outpoints.push_back(in.prevout);
|
||||
}
|
||||
const auto new_tx = MakeTransactionRef(tx_mut);
|
||||
// add newly constructed transaction to outpoints
|
||||
for (uint32_t i = 0; i < num_out; i++) {
|
||||
outpoints.emplace_back(new_tx->GetHash(), i);
|
||||
}
|
||||
return new_tx;
|
||||
}();
|
||||
|
||||
// trigger orphanage functions
|
||||
LIMITED_WHILE(fuzzed_data_provider.ConsumeBool(), 10 * DEFAULT_MAX_ORPHAN_TRANSACTIONS)
|
||||
{
|
||||
NodeId peer_id = fuzzed_data_provider.ConsumeIntegral<NodeId>();
|
||||
|
||||
CallOneOf(
|
||||
fuzzed_data_provider,
|
||||
[&] {
|
||||
LOCK(g_cs_orphans);
|
||||
orphanage.AddChildrenToWorkSet(*tx, orphan_work_set);
|
||||
},
|
||||
[&] {
|
||||
bool have_tx = orphanage.HaveTx(GenTxid::Txid(tx->GetHash())) || orphanage.HaveTx(GenTxid::Wtxid(tx->GetHash()));
|
||||
{
|
||||
LOCK(g_cs_orphans);
|
||||
bool get_tx = orphanage.GetTx(tx->GetHash()).first != nullptr;
|
||||
Assert(have_tx == get_tx);
|
||||
}
|
||||
},
|
||||
[&] {
|
||||
bool have_tx = orphanage.HaveTx(GenTxid::Txid(tx->GetHash())) || orphanage.HaveTx(GenTxid::Wtxid(tx->GetHash()));
|
||||
// AddTx should return false if tx is too big or already have it
|
||||
{
|
||||
LOCK(g_cs_orphans);
|
||||
Assert(have_tx != orphanage.AddTx(tx, peer_id));
|
||||
}
|
||||
have_tx = orphanage.HaveTx(GenTxid::Txid(tx->GetHash())) || orphanage.HaveTx(GenTxid::Wtxid(tx->GetHash()));
|
||||
// tx should already be added since it will not be too big in the test
|
||||
// have_tx should be true and AddTx should fail
|
||||
{
|
||||
LOCK(g_cs_orphans);
|
||||
Assert(have_tx && !orphanage.AddTx(tx, peer_id));
|
||||
}
|
||||
},
|
||||
[&] {
|
||||
bool have_tx = orphanage.HaveTx(GenTxid::Txid(tx->GetHash())) || orphanage.HaveTx(GenTxid::Wtxid(tx->GetHash()));
|
||||
// EraseTx should return 0 if m_orphans doesn't have the tx
|
||||
{
|
||||
LOCK(g_cs_orphans);
|
||||
Assert(have_tx == orphanage.EraseTx(tx->GetHash()));
|
||||
}
|
||||
have_tx = orphanage.HaveTx(GenTxid::Txid(tx->GetHash())) || orphanage.HaveTx(GenTxid::Wtxid(tx->GetHash()));
|
||||
// have_tx should be false and EraseTx should fail
|
||||
{
|
||||
LOCK(g_cs_orphans);
|
||||
Assert(!have_tx && !orphanage.EraseTx(tx->GetHash()));
|
||||
}
|
||||
},
|
||||
[&] {
|
||||
LOCK(g_cs_orphans);
|
||||
orphanage.EraseForPeer(peer_id);
|
||||
},
|
||||
[&] {
|
||||
// test mocktime and expiry
|
||||
SetMockTime(ConsumeTime(fuzzed_data_provider));
|
||||
auto size_before = orphanage.Size();
|
||||
auto limit = fuzzed_data_provider.ConsumeIntegral<unsigned int>();
|
||||
auto n_evicted = WITH_LOCK(g_cs_orphans, return orphanage.LimitOrphans(limit));
|
||||
Assert(size_before - n_evicted <= limit);
|
||||
Assert(orphanage.Size() <= limit);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in new issue