mirror of
https://github.com/bitcoin/bitcoin.git
synced 2026-01-19 14:53:43 +01:00
c58c249a5bnet_processing: indicate more work to do when orphans are ready to reconsider (Anthony Towns)ecb0a3e425net_processing: Don't process tx after processing orphans (Anthony Towns)c583775706net_processing: only process orphans before messages (Anthony Towns)be2304676btxorphange: Drop redundant originator arg from GetTxToReconsider (Anthony Towns)a4fe09973atxorphanage: index workset by originating peer (Anthony Towns) Pull request description: We currently process orphans by assigning them to the peer that provided a missing parent; instead assign them to the peer that provided the orphan in the first place. This prevents a peer from being able to marginally delay another peer's transactions and also simplifies the internal API slightly. Because we're now associating orphan processing with the peer that provided the orphan originally, we no longer process orphans immediately after receiving the parent, but defer until a future call to `ProcessMessage`. Based on #26295 ACKs for top commit: naumenkogs: utACKc58c249a5bglozow: ACKc58c249a5bmzumsande: Code Review ACKc58c249a5bTree-SHA512: 3186c346f21e60440266a2a80a9d23d7b96071414e14b2b3bfe50457c04c18b1eab109c3d8c2a7726a6b10a2eda1f0512510a52c102da112820a26f5d96f12de
141 lines
5.8 KiB
C++
141 lines
5.8 KiB
C++
// 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 <consensus/validation.h>
|
|
#include <net_processing.h>
|
|
#include <node/eviction.h>
|
|
#include <policy/policy.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 <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::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(outpoints.size() < 200'000 && 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 popped outpoints
|
|
for (auto& in : tx_mut.vin) {
|
|
outpoints.push_back(in.prevout);
|
|
}
|
|
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,
|
|
[&] {
|
|
orphanage.AddChildrenToWorkSet(*tx);
|
|
},
|
|
[&] {
|
|
{
|
|
CTransactionRef ref = orphanage.GetTxToReconsider(peer_id);
|
|
if (ref) {
|
|
bool have_tx = orphanage.HaveTx(GenTxid::Txid(ref->GetHash())) || orphanage.HaveTx(GenTxid::Wtxid(ref->GetHash()));
|
|
Assert(have_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
|
|
// tx weight is unknown, we only check when tx is already in orphanage
|
|
{
|
|
bool add_tx = orphanage.AddTx(tx, peer_id);
|
|
// have_tx == true -> add_tx == false
|
|
Assert(!have_tx || !add_tx);
|
|
}
|
|
have_tx = orphanage.HaveTx(GenTxid::Txid(tx->GetHash())) || orphanage.HaveTx(GenTxid::Wtxid(tx->GetHash()));
|
|
{
|
|
bool add_tx = orphanage.AddTx(tx, peer_id);
|
|
// if have_tx is still false, it must be too big
|
|
Assert(!have_tx == (GetTransactionWeight(*tx) > MAX_STANDARD_TX_WEIGHT));
|
|
Assert(!have_tx || !add_tx);
|
|
}
|
|
},
|
|
[&] {
|
|
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
|
|
{
|
|
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
|
|
{
|
|
Assert(!have_tx && !orphanage.EraseTx(tx->GetHash()));
|
|
}
|
|
},
|
|
[&] {
|
|
orphanage.EraseForPeer(peer_id);
|
|
},
|
|
[&] {
|
|
// test mocktime and expiry
|
|
SetMockTime(ConsumeTime(fuzzed_data_provider));
|
|
auto limit = fuzzed_data_provider.ConsumeIntegral<unsigned int>();
|
|
orphanage.LimitOrphans(limit);
|
|
Assert(orphanage.Size() <= limit);
|
|
});
|
|
}
|
|
}
|
|
}
|