mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-09-01 11:01:45 +02:00
[prep/refactor] make TxOrphanage a virtual class implemented by TxOrphanageImpl
This commit is contained in:
@@ -97,7 +97,7 @@ void TxDownloadManagerImpl::ActiveTipChange()
|
||||
|
||||
void TxDownloadManagerImpl::BlockConnected(const std::shared_ptr<const CBlock>& pblock)
|
||||
{
|
||||
m_orphanage.EraseForBlock(*pblock);
|
||||
m_orphanage->EraseForBlock(*pblock);
|
||||
|
||||
for (const auto& ptx : pblock->vtx) {
|
||||
RecentConfirmedTransactionsFilter().insert(ptx->GetHash().ToUint256());
|
||||
@@ -137,7 +137,7 @@ bool TxDownloadManagerImpl::AlreadyHaveTx(const GenTxid& gtxid, bool include_rec
|
||||
// While we won't query by txid, we can try to "guess" what the wtxid is based on the txid.
|
||||
// A non-segwit transaction's txid == wtxid. Query this txhash "casted" to a wtxid. This will
|
||||
// help us find non-segwit transactions, saving bandwidth, and should have no false positives.
|
||||
if (m_orphanage.HaveTx(Wtxid::FromUint256(hash))) return true;
|
||||
if (m_orphanage->HaveTx(Wtxid::FromUint256(hash))) return true;
|
||||
|
||||
if (include_reconsiderable && RecentRejectsReconsiderableFilter().contains(hash)) return true;
|
||||
|
||||
@@ -157,7 +157,7 @@ void TxDownloadManagerImpl::ConnectedPeer(NodeId nodeid, const TxDownloadConnect
|
||||
|
||||
void TxDownloadManagerImpl::DisconnectedPeer(NodeId nodeid)
|
||||
{
|
||||
m_orphanage.EraseForPeer(nodeid);
|
||||
m_orphanage->EraseForPeer(nodeid);
|
||||
m_txrequest.DisconnectedPeer(nodeid);
|
||||
|
||||
if (auto it = m_peer_info.find(nodeid); it != m_peer_info.end()) {
|
||||
@@ -174,7 +174,7 @@ bool TxDownloadManagerImpl::AddTxAnnouncement(NodeId peer, const GenTxid& gtxid,
|
||||
// - exists in orphanage
|
||||
// - peer can be an orphan resolution candidate
|
||||
if (const auto* wtxid = std::get_if<Wtxid>(>xid)) {
|
||||
if (auto orphan_tx{m_orphanage.GetTx(*wtxid)}) {
|
||||
if (auto orphan_tx{m_orphanage->GetTx(*wtxid)}) {
|
||||
auto unique_parents{GetUniqueParents(*orphan_tx)};
|
||||
std::erase_if(unique_parents, [&](const auto& txid) {
|
||||
return AlreadyHaveTx(txid, /*include_reconsiderable=*/false);
|
||||
@@ -187,7 +187,7 @@ bool TxDownloadManagerImpl::AddTxAnnouncement(NodeId peer, const GenTxid& gtxid,
|
||||
}
|
||||
|
||||
if (MaybeAddOrphanResolutionCandidate(unique_parents, *wtxid, peer, now)) {
|
||||
m_orphanage.AddAnnouncer(orphan_tx->GetWitnessHash(), peer);
|
||||
m_orphanage->AddAnnouncer(orphan_tx->GetWitnessHash(), peer);
|
||||
}
|
||||
|
||||
// Return even if the peer isn't an orphan resolution candidate. This would be caught by AlreadyHaveTx.
|
||||
@@ -227,7 +227,7 @@ bool TxDownloadManagerImpl::MaybeAddOrphanResolutionCandidate(const std::vector<
|
||||
{
|
||||
auto it_peer = m_peer_info.find(nodeid);
|
||||
if (it_peer == m_peer_info.end()) return false;
|
||||
if (m_orphanage.HaveTxFromPeer(wtxid, nodeid)) return false;
|
||||
if (m_orphanage->HaveTxFromPeer(wtxid, nodeid)) return false;
|
||||
|
||||
const auto& peer_entry = m_peer_info.at(nodeid);
|
||||
const auto& info = peer_entry.m_connection_info;
|
||||
@@ -305,7 +305,7 @@ std::optional<PackageToValidate> TxDownloadManagerImpl::Find1P1CPackage(const CT
|
||||
// children instead of the real one provided by the honest peer. Since we track all announcers
|
||||
// of an orphan, this does not exclude parent + orphan pairs that we happened to request from
|
||||
// different peers.
|
||||
const auto cpfp_candidates_same_peer{m_orphanage.GetChildrenFromSamePeer(ptx, nodeid)};
|
||||
const auto cpfp_candidates_same_peer{m_orphanage->GetChildrenFromSamePeer(ptx, nodeid)};
|
||||
|
||||
// These children should be sorted from newest to oldest. In the (probably uncommon) case
|
||||
// of children that replace each other, this helps us accept the highest feerate (probably the
|
||||
@@ -327,9 +327,9 @@ void TxDownloadManagerImpl::MempoolAcceptedTx(const CTransactionRef& tx)
|
||||
m_txrequest.ForgetTxHash(tx->GetHash());
|
||||
m_txrequest.ForgetTxHash(tx->GetWitnessHash());
|
||||
|
||||
m_orphanage.AddChildrenToWorkSet(*tx, m_opts.m_rng);
|
||||
m_orphanage->AddChildrenToWorkSet(*tx, m_opts.m_rng);
|
||||
// If it came from the orphanage, remove it. No-op if the tx is not in txorphanage.
|
||||
m_orphanage.EraseTx(tx->GetWitnessHash());
|
||||
m_orphanage->EraseTx(tx->GetWitnessHash());
|
||||
}
|
||||
|
||||
std::vector<Txid> TxDownloadManagerImpl::GetUniqueParents(const CTransaction& tx)
|
||||
@@ -398,7 +398,7 @@ node::RejectedTxTodo TxDownloadManagerImpl::MempoolRejectedTx(const CTransaction
|
||||
const auto& wtxid = ptx->GetWitnessHash();
|
||||
// Potentially flip add_extra_compact_tx to false if tx is already in orphanage, which
|
||||
// means it was already added to vExtraTxnForCompact.
|
||||
add_extra_compact_tx &= !m_orphanage.HaveTx(wtxid);
|
||||
add_extra_compact_tx &= !m_orphanage->HaveTx(wtxid);
|
||||
|
||||
// If there is no candidate for orphan resolution, AddTx will not be called. This means
|
||||
// that if a peer is overloading us with invs and orphans, they will eventually not be
|
||||
@@ -411,7 +411,7 @@ node::RejectedTxTodo TxDownloadManagerImpl::MempoolRejectedTx(const CTransaction
|
||||
|
||||
for (const auto& nodeid : orphan_resolution_candidates) {
|
||||
if (MaybeAddOrphanResolutionCandidate(unique_parents, ptx->GetWitnessHash(), nodeid, now)) {
|
||||
m_orphanage.AddTx(ptx, nodeid);
|
||||
m_orphanage->AddTx(ptx, nodeid);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -422,7 +422,7 @@ node::RejectedTxTodo TxDownloadManagerImpl::MempoolRejectedTx(const CTransaction
|
||||
// DoS prevention: do not allow m_orphanage to grow unbounded (see CVE-2012-3789)
|
||||
// Note that, if the orphanage reaches capacity, it's possible that we immediately evict
|
||||
// the transaction we just added.
|
||||
m_orphanage.LimitOrphans(m_opts.m_rng);
|
||||
m_orphanage->LimitOrphans(m_opts.m_rng);
|
||||
} else {
|
||||
unique_parents.clear();
|
||||
LogDebug(BCLog::MEMPOOL, "not keeping orphan with rejected parents %s (wtxid=%s)\n",
|
||||
@@ -491,7 +491,7 @@ node::RejectedTxTodo TxDownloadManagerImpl::MempoolRejectedTx(const CTransaction
|
||||
|
||||
// If the tx failed in ProcessOrphanTx, it should be removed from the orphanage unless the
|
||||
// tx was still missing inputs. If the tx was not in the orphanage, EraseTx does nothing and returns 0.
|
||||
if (state.GetResult() != TxValidationResult::TX_MISSING_INPUTS && m_orphanage.EraseTx(ptx->GetWitnessHash()) > 0) {
|
||||
if (state.GetResult() != TxValidationResult::TX_MISSING_INPUTS && m_orphanage->EraseTx(ptx->GetWitnessHash()) > 0) {
|
||||
LogDebug(BCLog::TXPACKAGES, " removed orphan tx %s (wtxid=%s)\n", ptx->GetHash().ToString(), ptx->GetWitnessHash().ToString());
|
||||
}
|
||||
|
||||
@@ -561,28 +561,28 @@ std::pair<bool, std::optional<PackageToValidate>> TxDownloadManagerImpl::Receive
|
||||
|
||||
bool TxDownloadManagerImpl::HaveMoreWork(NodeId nodeid)
|
||||
{
|
||||
return m_orphanage.HaveTxToReconsider(nodeid);
|
||||
return m_orphanage->HaveTxToReconsider(nodeid);
|
||||
}
|
||||
|
||||
CTransactionRef TxDownloadManagerImpl::GetTxToReconsider(NodeId nodeid)
|
||||
{
|
||||
return m_orphanage.GetTxToReconsider(nodeid);
|
||||
return m_orphanage->GetTxToReconsider(nodeid);
|
||||
}
|
||||
|
||||
void TxDownloadManagerImpl::CheckIsEmpty(NodeId nodeid)
|
||||
{
|
||||
assert(m_txrequest.Count(nodeid) == 0);
|
||||
assert(m_orphanage.UsageByPeer(nodeid) == 0);
|
||||
assert(m_orphanage->UsageByPeer(nodeid) == 0);
|
||||
}
|
||||
void TxDownloadManagerImpl::CheckIsEmpty()
|
||||
{
|
||||
assert(m_orphanage.TotalOrphanUsage() == 0);
|
||||
assert(m_orphanage.Size() == 0);
|
||||
assert(m_orphanage->TotalOrphanUsage() == 0);
|
||||
assert(m_orphanage->Size() == 0);
|
||||
assert(m_txrequest.Size() == 0);
|
||||
assert(m_num_wtxid_peers == 0);
|
||||
}
|
||||
std::vector<TxOrphanage::OrphanTxBase> TxDownloadManagerImpl::GetOrphanTransactions() const
|
||||
{
|
||||
return m_orphanage.GetOrphanTransactions();
|
||||
return m_orphanage->GetOrphanTransactions();
|
||||
}
|
||||
} // namespace node
|
||||
|
@@ -22,7 +22,7 @@ public:
|
||||
TxDownloadOptions m_opts;
|
||||
|
||||
/** Manages unvalidated tx data (orphan transactions for which we are downloading ancestors). */
|
||||
TxOrphanage m_orphanage;
|
||||
std::unique_ptr<TxOrphanage> m_orphanage;
|
||||
/** Tracks candidates for requesting and downloading transaction data. */
|
||||
TxRequestTracker m_txrequest;
|
||||
|
||||
@@ -128,7 +128,7 @@ public:
|
||||
return *m_lazy_recent_confirmed_transactions;
|
||||
}
|
||||
|
||||
TxDownloadManagerImpl(const TxDownloadOptions& options) : m_opts{options}, m_txrequest{options.m_deterministic_txrequest} {}
|
||||
TxDownloadManagerImpl(const TxDownloadOptions& options) : m_opts{options}, m_orphanage{MakeTxOrphanage()}, m_txrequest{options.m_deterministic_txrequest} {}
|
||||
|
||||
struct PeerInfo {
|
||||
/** Information relevant to scheduling tx requests. */
|
||||
|
@@ -12,8 +12,85 @@
|
||||
|
||||
#include <cassert>
|
||||
|
||||
namespace node{
|
||||
bool TxOrphanage::AddTx(const CTransactionRef& tx, NodeId peer)
|
||||
namespace node {
|
||||
|
||||
class TxOrphanageImpl final : public TxOrphanage {
|
||||
private:
|
||||
struct OrphanTx : public OrphanTxBase {
|
||||
NodeSeconds nTimeExpire;
|
||||
size_t list_pos;
|
||||
};
|
||||
|
||||
/** Total usage (weight) of all entries in m_orphans. */
|
||||
int64_t m_total_orphan_usage{0};
|
||||
|
||||
/** Total number of <peer, tx> pairs. Can be larger than m_orphans.size() because multiple peers
|
||||
* may have announced the same orphan. */
|
||||
unsigned int m_total_announcements{0};
|
||||
|
||||
/** Map from wtxid to orphan transaction record. Limited by
|
||||
* DEFAULT_MAX_ORPHAN_TRANSACTIONS */
|
||||
std::map<Wtxid, OrphanTx> m_orphans;
|
||||
|
||||
struct PeerOrphanInfo {
|
||||
/** List of transactions that should be reconsidered: added to in AddChildrenToWorkSet,
|
||||
* removed from one-by-one with each call to GetTxToReconsider. The wtxids may refer to
|
||||
* transactions that are no longer present in orphanage; these are lazily removed in
|
||||
* GetTxToReconsider. */
|
||||
std::set<Wtxid> m_work_set;
|
||||
|
||||
/** Total weight of orphans for which this peer is an announcer.
|
||||
* If orphans are provided by different peers, its weight will be accounted for in each
|
||||
* PeerOrphanInfo, so the total of all peers' m_total_usage may be larger than
|
||||
* m_total_orphan_size. If a peer is removed as an announcer, even if the orphan still
|
||||
* remains in the orphanage, this number will be decremented. */
|
||||
int64_t m_total_usage{0};
|
||||
};
|
||||
std::map<NodeId, PeerOrphanInfo> m_peer_orphanage_info;
|
||||
|
||||
using OrphanMap = decltype(m_orphans);
|
||||
|
||||
struct IteratorComparator
|
||||
{
|
||||
template<typename I>
|
||||
bool operator()(const I& a, const I& b) const
|
||||
{
|
||||
return a->first < b->first;
|
||||
}
|
||||
};
|
||||
|
||||
/** Index from the parents' COutPoint into the m_orphans. Used
|
||||
* to remove orphan transactions from the m_orphans */
|
||||
std::map<COutPoint, std::set<OrphanMap::iterator, IteratorComparator>> m_outpoint_to_orphan_it;
|
||||
|
||||
/** Orphan transactions in vector for quick random eviction */
|
||||
std::vector<OrphanMap::iterator> m_orphan_list;
|
||||
|
||||
/** Timestamp for the next scheduled sweep of expired orphans */
|
||||
NodeSeconds m_next_sweep{0s};
|
||||
|
||||
public:
|
||||
bool AddTx(const CTransactionRef& tx, NodeId peer) override;
|
||||
bool AddAnnouncer(const Wtxid& wtxid, NodeId peer) override;
|
||||
CTransactionRef GetTx(const Wtxid& wtxid) const override;
|
||||
bool HaveTx(const Wtxid& wtxid) const override;
|
||||
bool HaveTxFromPeer(const Wtxid& wtxid, NodeId peer) const override;
|
||||
CTransactionRef GetTxToReconsider(NodeId peer) override;
|
||||
int EraseTx(const Wtxid& wtxid) override;
|
||||
void EraseForPeer(NodeId peer) override;
|
||||
void EraseForBlock(const CBlock& block) override;
|
||||
void LimitOrphans(FastRandomContext& rng) override;
|
||||
void AddChildrenToWorkSet(const CTransaction& tx, FastRandomContext& rng) override;
|
||||
bool HaveTxToReconsider(NodeId peer) override;
|
||||
std::vector<CTransactionRef> GetChildrenFromSamePeer(const CTransactionRef& parent, NodeId nodeid) const override;
|
||||
size_t Size() const override { return m_orphans.size(); }
|
||||
std::vector<OrphanTxBase> GetOrphanTransactions() const override;
|
||||
int64_t TotalOrphanUsage() const override { return m_total_orphan_usage; }
|
||||
int64_t UsageByPeer(NodeId peer) const override;
|
||||
void SanityCheck() const override;
|
||||
};
|
||||
|
||||
bool TxOrphanageImpl::AddTx(const CTransactionRef& tx, NodeId peer)
|
||||
{
|
||||
const Txid& hash = tx->GetHash();
|
||||
const Wtxid& wtxid = tx->GetWitnessHash();
|
||||
@@ -53,7 +130,7 @@ bool TxOrphanage::AddTx(const CTransactionRef& tx, NodeId peer)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TxOrphanage::AddAnnouncer(const Wtxid& wtxid, NodeId peer)
|
||||
bool TxOrphanageImpl::AddAnnouncer(const Wtxid& wtxid, NodeId peer)
|
||||
{
|
||||
const auto it = m_orphans.find(wtxid);
|
||||
if (it != m_orphans.end()) {
|
||||
@@ -70,7 +147,7 @@ bool TxOrphanage::AddAnnouncer(const Wtxid& wtxid, NodeId peer)
|
||||
return false;
|
||||
}
|
||||
|
||||
int TxOrphanage::EraseTx(const Wtxid& wtxid)
|
||||
int TxOrphanageImpl::EraseTx(const Wtxid& wtxid)
|
||||
{
|
||||
std::map<Wtxid, OrphanTx>::iterator it = m_orphans.find(wtxid);
|
||||
if (it == m_orphans.end())
|
||||
@@ -116,7 +193,7 @@ int TxOrphanage::EraseTx(const Wtxid& wtxid)
|
||||
return 1;
|
||||
}
|
||||
|
||||
void TxOrphanage::EraseForPeer(NodeId peer)
|
||||
void TxOrphanageImpl::EraseForPeer(NodeId peer)
|
||||
{
|
||||
// Zeroes out this peer's m_total_usage.
|
||||
m_peer_orphanage_info.erase(peer);
|
||||
@@ -141,7 +218,7 @@ void TxOrphanage::EraseForPeer(NodeId peer)
|
||||
if (nErased > 0) LogDebug(BCLog::TXPACKAGES, "Erased %d orphan transaction(s) from peer=%d\n", nErased, peer);
|
||||
}
|
||||
|
||||
void TxOrphanage::LimitOrphans(FastRandomContext& rng)
|
||||
void TxOrphanageImpl::LimitOrphans(FastRandomContext& rng)
|
||||
{
|
||||
unsigned int nEvicted = 0;
|
||||
auto nNow{Now<NodeSeconds>()};
|
||||
@@ -173,7 +250,7 @@ void TxOrphanage::LimitOrphans(FastRandomContext& rng)
|
||||
if (nEvicted > 0) LogDebug(BCLog::TXPACKAGES, "orphanage overflow, removed %u tx\n", nEvicted);
|
||||
}
|
||||
|
||||
void TxOrphanage::AddChildrenToWorkSet(const CTransaction& tx, FastRandomContext& rng)
|
||||
void TxOrphanageImpl::AddChildrenToWorkSet(const CTransaction& tx, FastRandomContext& rng)
|
||||
{
|
||||
for (unsigned int i = 0; i < tx.vout.size(); i++) {
|
||||
const auto it_by_prev = m_outpoint_to_orphan_it.find(COutPoint(tx.GetHash(), i));
|
||||
@@ -201,24 +278,25 @@ void TxOrphanage::AddChildrenToWorkSet(const CTransaction& tx, FastRandomContext
|
||||
}
|
||||
}
|
||||
|
||||
bool TxOrphanage::HaveTx(const Wtxid& wtxid) const
|
||||
bool TxOrphanageImpl::HaveTx(const Wtxid& wtxid) const
|
||||
{
|
||||
return m_orphans.count(wtxid);
|
||||
}
|
||||
|
||||
CTransactionRef TxOrphanage::GetTx(const Wtxid& wtxid) const
|
||||
CTransactionRef TxOrphanageImpl::GetTx(const Wtxid& wtxid) const
|
||||
{
|
||||
auto it = m_orphans.find(wtxid);
|
||||
return it != m_orphans.end() ? it->second.tx : nullptr;
|
||||
}
|
||||
|
||||
bool TxOrphanage::HaveTxFromPeer(const Wtxid& wtxid, NodeId peer) const
|
||||
|
||||
bool TxOrphanageImpl::HaveTxFromPeer(const Wtxid& wtxid, NodeId peer) const
|
||||
{
|
||||
auto it = m_orphans.find(wtxid);
|
||||
return (it != m_orphans.end() && it->second.announcers.contains(peer));
|
||||
}
|
||||
|
||||
CTransactionRef TxOrphanage::GetTxToReconsider(NodeId peer)
|
||||
CTransactionRef TxOrphanageImpl::GetTxToReconsider(NodeId peer)
|
||||
{
|
||||
auto peer_it = m_peer_orphanage_info.find(peer);
|
||||
if (peer_it == m_peer_orphanage_info.end()) return nullptr;
|
||||
@@ -236,7 +314,7 @@ CTransactionRef TxOrphanage::GetTxToReconsider(NodeId peer)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool TxOrphanage::HaveTxToReconsider(NodeId peer)
|
||||
bool TxOrphanageImpl::HaveTxToReconsider(NodeId peer)
|
||||
{
|
||||
auto peer_it = m_peer_orphanage_info.find(peer);
|
||||
if (peer_it == m_peer_orphanage_info.end()) return false;
|
||||
@@ -245,7 +323,7 @@ bool TxOrphanage::HaveTxToReconsider(NodeId peer)
|
||||
return !work_set.empty();
|
||||
}
|
||||
|
||||
void TxOrphanage::EraseForBlock(const CBlock& block)
|
||||
void TxOrphanageImpl::EraseForBlock(const CBlock& block)
|
||||
{
|
||||
std::vector<Wtxid> vOrphanErase;
|
||||
|
||||
@@ -273,7 +351,7 @@ void TxOrphanage::EraseForBlock(const CBlock& block)
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<CTransactionRef> TxOrphanage::GetChildrenFromSamePeer(const CTransactionRef& parent, NodeId nodeid) const
|
||||
std::vector<CTransactionRef> TxOrphanageImpl::GetChildrenFromSamePeer(const CTransactionRef& parent, NodeId nodeid) const
|
||||
{
|
||||
// First construct a vector of iterators to ensure we do not return duplicates of the same tx
|
||||
// and so we can sort by nTimeExpire.
|
||||
@@ -313,7 +391,7 @@ std::vector<CTransactionRef> TxOrphanage::GetChildrenFromSamePeer(const CTransac
|
||||
return children_found;
|
||||
}
|
||||
|
||||
std::vector<TxOrphanage::OrphanTxBase> TxOrphanage::GetOrphanTransactions() const
|
||||
std::vector<TxOrphanage::OrphanTxBase> TxOrphanageImpl::GetOrphanTransactions() const
|
||||
{
|
||||
std::vector<OrphanTxBase> ret;
|
||||
ret.reserve(m_orphans.size());
|
||||
@@ -323,7 +401,13 @@ std::vector<TxOrphanage::OrphanTxBase> TxOrphanage::GetOrphanTransactions() cons
|
||||
return ret;
|
||||
}
|
||||
|
||||
void TxOrphanage::SanityCheck() const
|
||||
int64_t TxOrphanageImpl::UsageByPeer(NodeId peer) const
|
||||
{
|
||||
auto peer_it = m_peer_orphanage_info.find(peer);
|
||||
return peer_it == m_peer_orphanage_info.end() ? 0 : peer_it->second.m_total_usage;
|
||||
}
|
||||
|
||||
void TxOrphanageImpl::SanityCheck() const
|
||||
{
|
||||
// Check that cached m_total_announcements is correct
|
||||
unsigned int counted_total_announcements{0};
|
||||
@@ -362,4 +446,10 @@ void TxOrphanage::SanityCheck() const
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<TxOrphanage> MakeTxOrphanage() noexcept
|
||||
{
|
||||
return std::make_unique<TxOrphanageImpl>();
|
||||
}
|
||||
|
||||
} // namespace node
|
||||
|
@@ -15,7 +15,7 @@
|
||||
#include <map>
|
||||
#include <set>
|
||||
|
||||
namespace node{
|
||||
namespace node {
|
||||
/** Expiration time for orphan transactions */
|
||||
static constexpr auto ORPHAN_TX_EXPIRE_TIME{20min};
|
||||
/** Minimum time between orphan transactions expire time checks */
|
||||
@@ -31,56 +31,6 @@ static const uint32_t DEFAULT_MAX_ORPHAN_TRANSACTIONS{100};
|
||||
*/
|
||||
class TxOrphanage {
|
||||
public:
|
||||
/** Add a new orphan transaction */
|
||||
bool AddTx(const CTransactionRef& tx, NodeId peer);
|
||||
|
||||
/** Add an additional announcer to an orphan if it exists. Otherwise, do nothing. */
|
||||
bool AddAnnouncer(const Wtxid& wtxid, NodeId peer);
|
||||
|
||||
CTransactionRef GetTx(const Wtxid& wtxid) const;
|
||||
|
||||
/** Check if we already have an orphan transaction (by wtxid only) */
|
||||
bool HaveTx(const Wtxid& wtxid) const;
|
||||
|
||||
/** Check if a {tx, peer} exists in the orphanage.*/
|
||||
bool HaveTxFromPeer(const Wtxid& wtxid, NodeId peer) const;
|
||||
|
||||
/** Extract a transaction from a peer's work set
|
||||
* Returns nullptr if there are no transactions to work on.
|
||||
* Otherwise returns the transaction reference, and removes
|
||||
* it from the work set.
|
||||
*/
|
||||
CTransactionRef GetTxToReconsider(NodeId peer);
|
||||
|
||||
/** Erase an orphan by wtxid */
|
||||
int EraseTx(const Wtxid& wtxid);
|
||||
|
||||
/** Maybe erase all orphans announced by a peer (eg, after that peer disconnects). If an orphan
|
||||
* has been announced by another peer, don't erase, just remove this peer from the list of announcers. */
|
||||
void EraseForPeer(NodeId peer);
|
||||
|
||||
/** Erase all orphans included in or invalidated by a new block */
|
||||
void EraseForBlock(const CBlock& block);
|
||||
|
||||
/** Limit the orphanage to DEFAULT_MAX_ORPHAN_TRANSACTIONS. */
|
||||
void LimitOrphans(FastRandomContext& rng);
|
||||
|
||||
/** Add any orphans that list a particular tx as a parent into the from peer's work set */
|
||||
void AddChildrenToWorkSet(const CTransaction& tx, FastRandomContext& rng);
|
||||
|
||||
/** Does this peer have any work to do? */
|
||||
bool HaveTxToReconsider(NodeId peer);
|
||||
|
||||
/** Get all children that spend from this tx and were received from nodeid. Sorted from most
|
||||
* recent to least recent. */
|
||||
std::vector<CTransactionRef> GetChildrenFromSamePeer(const CTransactionRef& parent, NodeId nodeid) const;
|
||||
|
||||
/** Return how many entries exist in the orphange */
|
||||
size_t Size() const
|
||||
{
|
||||
return m_orphans.size();
|
||||
}
|
||||
|
||||
/** Allows providing orphan information externally */
|
||||
struct OrphanTxBase {
|
||||
CTransactionRef tx;
|
||||
@@ -93,77 +43,75 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
std::vector<OrphanTxBase> GetOrphanTransactions() const;
|
||||
virtual ~TxOrphanage() = default;
|
||||
|
||||
/** Add a new orphan transaction */
|
||||
virtual bool AddTx(const CTransactionRef& tx, NodeId peer) = 0;
|
||||
|
||||
/** Add an additional announcer to an orphan if it exists. Otherwise, do nothing. */
|
||||
virtual bool AddAnnouncer(const Wtxid& wtxid, NodeId peer) = 0;
|
||||
|
||||
/** Get a transaction by its witness txid */
|
||||
virtual CTransactionRef GetTx(const Wtxid& wtxid) const = 0;
|
||||
|
||||
/** Check if we already have an orphan transaction (by wtxid only) */
|
||||
virtual bool HaveTx(const Wtxid& wtxid) const = 0;
|
||||
|
||||
/** Check if a {tx, peer} exists in the orphanage.*/
|
||||
virtual bool HaveTxFromPeer(const Wtxid& wtxid, NodeId peer) const = 0;
|
||||
|
||||
/** Extract a transaction from a peer's work set
|
||||
* Returns nullptr if there are no transactions to work on.
|
||||
* Otherwise returns the transaction reference, and removes
|
||||
* it from the work set.
|
||||
*/
|
||||
virtual CTransactionRef GetTxToReconsider(NodeId peer) = 0;
|
||||
|
||||
/** Erase an orphan by wtxid */
|
||||
virtual int EraseTx(const Wtxid& wtxid) = 0;
|
||||
|
||||
/** Maybe erase all orphans announced by a peer (eg, after that peer disconnects). If an orphan
|
||||
* has been announced by another peer, don't erase, just remove this peer from the list of announcers. */
|
||||
virtual void EraseForPeer(NodeId peer) = 0;
|
||||
|
||||
/** Erase all orphans included in or invalidated by a new block */
|
||||
virtual void EraseForBlock(const CBlock& block) = 0;
|
||||
|
||||
/** Limit the orphanage to DEFAULT_MAX_ORPHAN_TRANSACTIONS. */
|
||||
virtual void LimitOrphans(FastRandomContext& rng) = 0;
|
||||
|
||||
/** Add any orphans that list a particular tx as a parent into the from peer's work set */
|
||||
virtual void AddChildrenToWorkSet(const CTransaction& tx, FastRandomContext& rng) = 0;
|
||||
|
||||
/** Does this peer have any work to do? */
|
||||
virtual bool HaveTxToReconsider(NodeId peer) = 0;
|
||||
|
||||
/** Get all children that spend from this tx and were received from nodeid. Sorted from most
|
||||
* recent to least recent. */
|
||||
virtual std::vector<CTransactionRef> GetChildrenFromSamePeer(const CTransactionRef& parent, NodeId nodeid) const = 0;
|
||||
|
||||
/** Return how many entries exist in the orphange */
|
||||
virtual size_t Size() const = 0;
|
||||
|
||||
/** Get all orphan transactions */
|
||||
virtual std::vector<OrphanTxBase> GetOrphanTransactions() const = 0;
|
||||
|
||||
/** Get the total usage (weight) of all orphans. If an orphan has multiple announcers, its usage is
|
||||
* only counted once within this total. */
|
||||
int64_t TotalOrphanUsage() const { return m_total_orphan_usage; }
|
||||
virtual int64_t TotalOrphanUsage() const = 0;
|
||||
|
||||
/** Total usage (weight) of orphans for which this peer is an announcer. If an orphan has multiple
|
||||
* announcers, its weight will be accounted for in each PeerOrphanInfo, so the total of all
|
||||
* peers' UsageByPeer() may be larger than TotalOrphanBytes(). */
|
||||
int64_t UsageByPeer(NodeId peer) const {
|
||||
auto peer_it = m_peer_orphanage_info.find(peer);
|
||||
return peer_it == m_peer_orphanage_info.end() ? 0 : peer_it->second.m_total_usage;
|
||||
}
|
||||
virtual int64_t UsageByPeer(NodeId peer) const = 0;
|
||||
|
||||
/** Check consistency between PeerOrphanInfo and m_orphans. Recalculate counters and ensure they
|
||||
* match what is cached. */
|
||||
void SanityCheck() const;
|
||||
|
||||
protected:
|
||||
struct OrphanTx : public OrphanTxBase {
|
||||
NodeSeconds nTimeExpire;
|
||||
size_t list_pos;
|
||||
};
|
||||
|
||||
/** Total usage (weight) of all entries in m_orphans. */
|
||||
int64_t m_total_orphan_usage{0};
|
||||
|
||||
/** Total number of <peer, tx> pairs. Can be larger than m_orphans.size() because multiple peers
|
||||
* may have announced the same orphan. */
|
||||
unsigned int m_total_announcements{0};
|
||||
|
||||
/** Map from wtxid to orphan transaction record. Limited by
|
||||
* DEFAULT_MAX_ORPHAN_TRANSACTIONS */
|
||||
std::map<Wtxid, OrphanTx> m_orphans;
|
||||
|
||||
struct PeerOrphanInfo {
|
||||
/** List of transactions that should be reconsidered: added to in AddChildrenToWorkSet,
|
||||
* removed from one-by-one with each call to GetTxToReconsider. The wtxids may refer to
|
||||
* transactions that are no longer present in orphanage; these are lazily removed in
|
||||
* GetTxToReconsider. */
|
||||
std::set<Wtxid> m_work_set;
|
||||
|
||||
/** Total weight of orphans for which this peer is an announcer.
|
||||
* If orphans are provided by different peers, its weight will be accounted for in each
|
||||
* PeerOrphanInfo, so the total of all peers' m_total_usage may be larger than
|
||||
* m_total_orphan_size. If a peer is removed as an announcer, even if the orphan still
|
||||
* remains in the orphanage, this number will be decremented. */
|
||||
int64_t m_total_usage{0};
|
||||
};
|
||||
std::map<NodeId, PeerOrphanInfo> m_peer_orphanage_info;
|
||||
|
||||
using OrphanMap = decltype(m_orphans);
|
||||
|
||||
struct IteratorComparator
|
||||
{
|
||||
template<typename I>
|
||||
bool operator()(const I& a, const I& b) const
|
||||
{
|
||||
return a->first < b->first;
|
||||
}
|
||||
};
|
||||
|
||||
/** Index from the parents' COutPoint into the m_orphans. Used
|
||||
* to remove orphan transactions from the m_orphans */
|
||||
std::map<COutPoint, std::set<OrphanMap::iterator, IteratorComparator>> m_outpoint_to_orphan_it;
|
||||
|
||||
/** Orphan transactions in vector for quick random eviction */
|
||||
std::vector<OrphanMap::iterator> m_orphan_list;
|
||||
|
||||
/** Timestamp for the next scheduled sweep of expired orphans */
|
||||
NodeSeconds m_next_sweep{0s};
|
||||
virtual void SanityCheck() const = 0;
|
||||
};
|
||||
|
||||
/** Create a new TxOrphanage instance */
|
||||
std::unique_ptr<TxOrphanage> MakeTxOrphanage() noexcept;
|
||||
|
||||
} // namespace node
|
||||
#endif // BITCOIN_NODE_TXORPHANAGE_H
|
||||
|
@@ -279,11 +279,9 @@ static bool HasRelayPermissions(NodeId peer) { return peer == 0; }
|
||||
|
||||
static void CheckInvariants(const node::TxDownloadManagerImpl& txdownload_impl, size_t max_orphan_count)
|
||||
{
|
||||
const node::TxOrphanage& orphanage = txdownload_impl.m_orphanage;
|
||||
|
||||
// Orphanage usage should never exceed what is allowed
|
||||
Assert(orphanage.Size() <= max_orphan_count);
|
||||
txdownload_impl.m_orphanage.SanityCheck();
|
||||
Assert(txdownload_impl.m_orphanage->Size() <= max_orphan_count);
|
||||
txdownload_impl.m_orphanage->SanityCheck();
|
||||
|
||||
// We should never have more than the maximum in-flight requests out for a peer.
|
||||
for (NodeId peer = 0; peer < NUM_PEERS; ++peer) {
|
||||
@@ -349,7 +347,7 @@ FUZZ_TARGET(txdownloadman_impl, .init = initialize)
|
||||
block.vtx.push_back(rand_tx);
|
||||
txdownload_impl.BlockConnected(std::make_shared<CBlock>(block));
|
||||
// Block transactions must be removed from orphanage
|
||||
Assert(!txdownload_impl.m_orphanage.HaveTx(rand_tx->GetWitnessHash()));
|
||||
Assert(!txdownload_impl.m_orphanage->HaveTx(rand_tx->GetWitnessHash()));
|
||||
},
|
||||
[&] {
|
||||
txdownload_impl.BlockDisconnected();
|
||||
@@ -401,7 +399,7 @@ FUZZ_TARGET(txdownloadman_impl, .init = initialize)
|
||||
const auto& package = maybe_package->m_txns;
|
||||
// Parent is in m_lazy_recent_rejects_reconsiderable and child is in m_orphanage
|
||||
Assert(txdownload_impl.RecentRejectsReconsiderableFilter().contains(rand_tx->GetWitnessHash().ToUint256()));
|
||||
Assert(txdownload_impl.m_orphanage.HaveTx(maybe_package->m_txns.back()->GetWitnessHash()));
|
||||
Assert(txdownload_impl.m_orphanage->HaveTx(maybe_package->m_txns.back()->GetWitnessHash()));
|
||||
// Package has not been rejected
|
||||
Assert(!txdownload_impl.RecentRejectsReconsiderableFilter().contains(GetPackageHash(package)));
|
||||
// Neither is in m_lazy_recent_rejects
|
||||
|
@@ -37,7 +37,7 @@ FUZZ_TARGET(txorphan, .init = initialize_orphanage)
|
||||
FastRandomContext orphanage_rng{ConsumeUInt256(fuzzed_data_provider)};
|
||||
SetMockTime(ConsumeTime(fuzzed_data_provider));
|
||||
|
||||
node::TxOrphanage orphanage;
|
||||
auto orphanage = node::MakeTxOrphanage();
|
||||
std::vector<COutPoint> outpoints; // Duplicates are tolerated
|
||||
outpoints.reserve(200'000);
|
||||
|
||||
@@ -86,11 +86,11 @@ FUZZ_TARGET(txorphan, .init = initialize_orphanage)
|
||||
// previous loop and potentially the parent of this tx.
|
||||
if (ptx_potential_parent) {
|
||||
// Set up future GetTxToReconsider call.
|
||||
orphanage.AddChildrenToWorkSet(*ptx_potential_parent, orphanage_rng);
|
||||
orphanage->AddChildrenToWorkSet(*ptx_potential_parent, orphanage_rng);
|
||||
|
||||
// Check that all txns returned from GetChildrenFrom* are indeed a direct child of this tx.
|
||||
NodeId peer_id = fuzzed_data_provider.ConsumeIntegral<NodeId>();
|
||||
for (const auto& child : orphanage.GetChildrenFromSamePeer(ptx_potential_parent, peer_id)) {
|
||||
for (const auto& child : orphanage->GetChildrenFromSamePeer(ptx_potential_parent, peer_id)) {
|
||||
assert(std::any_of(child->vin.cbegin(), child->vin.cend(), [&](const auto& input) {
|
||||
return input.prevout.hash == ptx_potential_parent->GetHash();
|
||||
}));
|
||||
@@ -101,60 +101,60 @@ FUZZ_TARGET(txorphan, .init = initialize_orphanage)
|
||||
LIMITED_WHILE(fuzzed_data_provider.ConsumeBool(), 10 * node::DEFAULT_MAX_ORPHAN_TRANSACTIONS)
|
||||
{
|
||||
NodeId peer_id = fuzzed_data_provider.ConsumeIntegral<NodeId>();
|
||||
const auto total_bytes_start{orphanage.TotalOrphanUsage()};
|
||||
const auto total_peer_bytes_start{orphanage.UsageByPeer(peer_id)};
|
||||
const auto total_bytes_start{orphanage->TotalOrphanUsage()};
|
||||
const auto total_peer_bytes_start{orphanage->UsageByPeer(peer_id)};
|
||||
const auto tx_weight{GetTransactionWeight(*tx)};
|
||||
|
||||
CallOneOf(
|
||||
fuzzed_data_provider,
|
||||
[&] {
|
||||
{
|
||||
CTransactionRef ref = orphanage.GetTxToReconsider(peer_id);
|
||||
CTransactionRef ref = orphanage->GetTxToReconsider(peer_id);
|
||||
if (ref) {
|
||||
Assert(orphanage.HaveTx(ref->GetWitnessHash()));
|
||||
Assert(orphanage->HaveTx(ref->GetWitnessHash()));
|
||||
}
|
||||
}
|
||||
},
|
||||
[&] {
|
||||
bool have_tx = orphanage.HaveTx(tx->GetWitnessHash());
|
||||
bool have_tx = orphanage->HaveTx(tx->GetWitnessHash());
|
||||
// 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);
|
||||
bool add_tx = orphanage->AddTx(tx, peer_id);
|
||||
// have_tx == true -> add_tx == false
|
||||
Assert(!have_tx || !add_tx);
|
||||
|
||||
if (add_tx) {
|
||||
Assert(orphanage.UsageByPeer(peer_id) == tx_weight + total_peer_bytes_start);
|
||||
Assert(orphanage.TotalOrphanUsage() == tx_weight + total_bytes_start);
|
||||
Assert(orphanage->UsageByPeer(peer_id) == tx_weight + total_peer_bytes_start);
|
||||
Assert(orphanage->TotalOrphanUsage() == tx_weight + total_bytes_start);
|
||||
Assert(tx_weight <= MAX_STANDARD_TX_WEIGHT);
|
||||
} else {
|
||||
// Peer may have been added as an announcer.
|
||||
if (orphanage.UsageByPeer(peer_id) == tx_weight + total_peer_bytes_start) {
|
||||
Assert(orphanage.HaveTxFromPeer(wtxid, peer_id));
|
||||
if (orphanage->UsageByPeer(peer_id) == tx_weight + total_peer_bytes_start) {
|
||||
Assert(orphanage->HaveTxFromPeer(wtxid, peer_id));
|
||||
} else {
|
||||
// Otherwise, there must not be any change to the peer byte count.
|
||||
Assert(orphanage.UsageByPeer(peer_id) == total_peer_bytes_start);
|
||||
Assert(orphanage->UsageByPeer(peer_id) == total_peer_bytes_start);
|
||||
}
|
||||
|
||||
// Regardless, total bytes should not have changed.
|
||||
Assert(orphanage.TotalOrphanUsage() == total_bytes_start);
|
||||
Assert(orphanage->TotalOrphanUsage() == total_bytes_start);
|
||||
}
|
||||
}
|
||||
have_tx = orphanage.HaveTx(tx->GetWitnessHash());
|
||||
have_tx = orphanage->HaveTx(tx->GetWitnessHash());
|
||||
{
|
||||
bool add_tx = orphanage.AddTx(tx, peer_id);
|
||||
bool add_tx = orphanage->AddTx(tx, peer_id);
|
||||
// if have_tx is still false, it must be too big
|
||||
Assert(!have_tx == (tx_weight > MAX_STANDARD_TX_WEIGHT));
|
||||
Assert(!have_tx || !add_tx);
|
||||
}
|
||||
},
|
||||
[&] {
|
||||
bool have_tx = orphanage.HaveTx(tx->GetWitnessHash());
|
||||
bool have_tx_and_peer = orphanage.HaveTxFromPeer(tx->GetWitnessHash(), peer_id);
|
||||
bool have_tx = orphanage->HaveTx(tx->GetWitnessHash());
|
||||
bool have_tx_and_peer = orphanage->HaveTxFromPeer(tx->GetWitnessHash(), peer_id);
|
||||
// AddAnnouncer should return false if tx doesn't exist or we already HaveTxFromPeer.
|
||||
{
|
||||
bool added_announcer = orphanage.AddAnnouncer(tx->GetWitnessHash(), peer_id);
|
||||
bool added_announcer = orphanage->AddAnnouncer(tx->GetWitnessHash(), peer_id);
|
||||
// have_tx == false -> added_announcer == false
|
||||
Assert(have_tx || !added_announcer);
|
||||
// have_tx_and_peer == true -> added_announcer == false
|
||||
@@ -162,43 +162,43 @@ FUZZ_TARGET(txorphan, .init = initialize_orphanage)
|
||||
|
||||
// Total bytes should not have changed. If peer was added as announcer, byte
|
||||
// accounting must have been updated.
|
||||
Assert(orphanage.TotalOrphanUsage() == total_bytes_start);
|
||||
Assert(orphanage->TotalOrphanUsage() == total_bytes_start);
|
||||
if (added_announcer) {
|
||||
Assert(orphanage.UsageByPeer(peer_id) == tx_weight + total_peer_bytes_start);
|
||||
Assert(orphanage->UsageByPeer(peer_id) == tx_weight + total_peer_bytes_start);
|
||||
} else {
|
||||
Assert(orphanage.UsageByPeer(peer_id) == total_peer_bytes_start);
|
||||
Assert(orphanage->UsageByPeer(peer_id) == total_peer_bytes_start);
|
||||
}
|
||||
}
|
||||
},
|
||||
[&] {
|
||||
bool have_tx = orphanage.HaveTx(tx->GetWitnessHash());
|
||||
bool have_tx_and_peer{orphanage.HaveTxFromPeer(wtxid, peer_id)};
|
||||
bool have_tx = orphanage->HaveTx(tx->GetWitnessHash());
|
||||
bool have_tx_and_peer{orphanage->HaveTxFromPeer(wtxid, peer_id)};
|
||||
// EraseTx should return 0 if m_orphans doesn't have the tx
|
||||
{
|
||||
auto bytes_from_peer_before{orphanage.UsageByPeer(peer_id)};
|
||||
Assert(have_tx == orphanage.EraseTx(tx->GetWitnessHash()));
|
||||
auto bytes_from_peer_before{orphanage->UsageByPeer(peer_id)};
|
||||
Assert(have_tx == orphanage->EraseTx(tx->GetWitnessHash()));
|
||||
if (have_tx) {
|
||||
Assert(orphanage.TotalOrphanUsage() == total_bytes_start - tx_weight);
|
||||
Assert(orphanage->TotalOrphanUsage() == total_bytes_start - tx_weight);
|
||||
if (have_tx_and_peer) {
|
||||
Assert(orphanage.UsageByPeer(peer_id) == bytes_from_peer_before - tx_weight);
|
||||
Assert(orphanage->UsageByPeer(peer_id) == bytes_from_peer_before - tx_weight);
|
||||
} else {
|
||||
Assert(orphanage.UsageByPeer(peer_id) == bytes_from_peer_before);
|
||||
Assert(orphanage->UsageByPeer(peer_id) == bytes_from_peer_before);
|
||||
}
|
||||
} else {
|
||||
Assert(orphanage.TotalOrphanUsage() == total_bytes_start);
|
||||
Assert(orphanage->TotalOrphanUsage() == total_bytes_start);
|
||||
}
|
||||
}
|
||||
have_tx = orphanage.HaveTx(tx->GetWitnessHash());
|
||||
have_tx_and_peer = orphanage.HaveTxFromPeer(wtxid, peer_id);
|
||||
have_tx = orphanage->HaveTx(tx->GetWitnessHash());
|
||||
have_tx_and_peer = orphanage->HaveTxFromPeer(wtxid, peer_id);
|
||||
// have_tx should be false and EraseTx should fail
|
||||
{
|
||||
Assert(!have_tx && !have_tx_and_peer && !orphanage.EraseTx(wtxid));
|
||||
Assert(!have_tx && !have_tx_and_peer && !orphanage->EraseTx(wtxid));
|
||||
}
|
||||
},
|
||||
[&] {
|
||||
orphanage.EraseForPeer(peer_id);
|
||||
Assert(!orphanage.HaveTxFromPeer(tx->GetWitnessHash(), peer_id));
|
||||
Assert(orphanage.UsageByPeer(peer_id) == 0);
|
||||
orphanage->EraseForPeer(peer_id);
|
||||
Assert(!orphanage->HaveTxFromPeer(tx->GetWitnessHash(), peer_id));
|
||||
Assert(orphanage->UsageByPeer(peer_id) == 0);
|
||||
},
|
||||
[&] {
|
||||
// Make a block out of txs and then EraseForBlock
|
||||
@@ -208,17 +208,17 @@ FUZZ_TARGET(txorphan, .init = initialize_orphanage)
|
||||
auto& tx_to_remove = PickValue(fuzzed_data_provider, tx_history);
|
||||
block.vtx.push_back(tx_to_remove);
|
||||
}
|
||||
orphanage.EraseForBlock(block);
|
||||
orphanage->EraseForBlock(block);
|
||||
for (const auto& tx_removed : block.vtx) {
|
||||
Assert(!orphanage.HaveTx(tx_removed->GetWitnessHash()));
|
||||
Assert(!orphanage.HaveTxFromPeer(tx_removed->GetWitnessHash(), peer_id));
|
||||
Assert(!orphanage->HaveTx(tx_removed->GetWitnessHash()));
|
||||
Assert(!orphanage->HaveTxFromPeer(tx_removed->GetWitnessHash(), peer_id));
|
||||
}
|
||||
},
|
||||
[&] {
|
||||
// test mocktime and expiry
|
||||
SetMockTime(ConsumeTime(fuzzed_data_provider));
|
||||
orphanage.LimitOrphans(orphanage_rng);
|
||||
Assert(orphanage.Size() <= node::DEFAULT_MAX_ORPHAN_TRANSACTIONS);
|
||||
orphanage->LimitOrphans(orphanage_rng);
|
||||
Assert(orphanage->Size() <= node::DEFAULT_MAX_ORPHAN_TRANSACTIONS);
|
||||
});
|
||||
|
||||
}
|
||||
@@ -228,9 +228,9 @@ FUZZ_TARGET(txorphan, .init = initialize_orphanage)
|
||||
ptx_potential_parent = tx;
|
||||
}
|
||||
|
||||
const bool have_tx{orphanage.HaveTx(tx->GetWitnessHash())};
|
||||
const bool get_tx_nonnull{orphanage.GetTx(tx->GetWitnessHash()) != nullptr};
|
||||
const bool have_tx{orphanage->HaveTx(tx->GetWitnessHash())};
|
||||
const bool get_tx_nonnull{orphanage->GetTx(tx->GetWitnessHash()) != nullptr};
|
||||
Assert(have_tx == get_tx_nonnull);
|
||||
}
|
||||
orphanage.SanityCheck();
|
||||
orphanage->SanityCheck();
|
||||
}
|
||||
|
@@ -21,14 +21,6 @@
|
||||
|
||||
BOOST_FIXTURE_TEST_SUITE(orphanage_tests, TestingSetup)
|
||||
|
||||
class TxOrphanageTest : public node::TxOrphanage
|
||||
{
|
||||
public:
|
||||
TxOrphanageTest(FastRandomContext& rng) : m_rng{rng} {}
|
||||
|
||||
FastRandomContext& m_rng;
|
||||
};
|
||||
|
||||
static void MakeNewKeyWithFastRandomContext(CKey& key, FastRandomContext& rand_ctx)
|
||||
{
|
||||
std::vector<unsigned char> keydata;
|
||||
@@ -90,7 +82,7 @@ BOOST_AUTO_TEST_CASE(DoS_mapOrphans)
|
||||
// signature's R and S values have leading zeros.
|
||||
m_rng.Reseed(uint256{33});
|
||||
|
||||
TxOrphanageTest orphanage{m_rng};
|
||||
std::unique_ptr<node::TxOrphanage> orphanage{node::MakeTxOrphanage()};
|
||||
CKey key;
|
||||
MakeNewKeyWithFastRandomContext(key, m_rng);
|
||||
FillableSigningProvider keystore;
|
||||
@@ -115,7 +107,7 @@ BOOST_AUTO_TEST_CASE(DoS_mapOrphans)
|
||||
tx.vout[0].scriptPubKey = GetScriptForDestination(PKHash(key.GetPubKey()));
|
||||
|
||||
auto ptx = MakeTransactionRef(tx);
|
||||
orphanage.AddTx(ptx, i);
|
||||
orphanage->AddTx(ptx, i);
|
||||
orphans_added.emplace_back(ptx);
|
||||
}
|
||||
|
||||
@@ -135,7 +127,7 @@ BOOST_AUTO_TEST_CASE(DoS_mapOrphans)
|
||||
BOOST_CHECK(SignSignature(keystore, *txPrev, tx, 0, SIGHASH_ALL, empty));
|
||||
|
||||
auto ptx = MakeTransactionRef(tx);
|
||||
orphanage.AddTx(ptx, i);
|
||||
orphanage->AddTx(ptx, i);
|
||||
orphans_added.emplace_back(ptx);
|
||||
}
|
||||
|
||||
@@ -161,50 +153,50 @@ BOOST_AUTO_TEST_CASE(DoS_mapOrphans)
|
||||
for (unsigned int j = 1; j < tx.vin.size(); j++)
|
||||
tx.vin[j].scriptSig = tx.vin[0].scriptSig;
|
||||
|
||||
BOOST_CHECK(!orphanage.AddTx(MakeTransactionRef(tx), i));
|
||||
BOOST_CHECK(!orphanage->AddTx(MakeTransactionRef(tx), i));
|
||||
}
|
||||
|
||||
size_t expected_num_orphans = orphanage.Size();
|
||||
size_t expected_num_orphans = orphanage->Size();
|
||||
|
||||
// Non-existent peer; nothing should be deleted
|
||||
orphanage.EraseForPeer(/*peer=*/-1);
|
||||
BOOST_CHECK_EQUAL(orphanage.Size(), expected_num_orphans);
|
||||
orphanage->EraseForPeer(/*peer=*/-1);
|
||||
BOOST_CHECK_EQUAL(orphanage->Size(), expected_num_orphans);
|
||||
|
||||
// Each of first three peers stored
|
||||
// two transactions each.
|
||||
for (NodeId i = 0; i < 3; i++)
|
||||
{
|
||||
orphanage.EraseForPeer(i);
|
||||
orphanage->EraseForPeer(i);
|
||||
expected_num_orphans -= 2;
|
||||
BOOST_CHECK(orphanage.Size() == expected_num_orphans);
|
||||
BOOST_CHECK(orphanage->Size() == expected_num_orphans);
|
||||
}
|
||||
|
||||
// Test LimitOrphanTxSize() function, nothing should timeout:
|
||||
FastRandomContext rng{/*fDeterministic=*/true};
|
||||
orphanage.LimitOrphans(rng);
|
||||
BOOST_CHECK_EQUAL(orphanage.Size(), expected_num_orphans);
|
||||
orphanage->LimitOrphans(rng);
|
||||
BOOST_CHECK_EQUAL(orphanage->Size(), expected_num_orphans);
|
||||
|
||||
// Add one more orphan, check timeout logic
|
||||
auto timeout_tx = MakeTransactionSpending(/*outpoints=*/{}, rng);
|
||||
orphanage.AddTx(timeout_tx, 0);
|
||||
orphanage->AddTx(timeout_tx, 0);
|
||||
expected_num_orphans += 1;
|
||||
BOOST_CHECK_EQUAL(orphanage.Size(), expected_num_orphans);
|
||||
BOOST_CHECK_EQUAL(orphanage->Size(), expected_num_orphans);
|
||||
|
||||
// One second shy of expiration
|
||||
SetMockTime(now + node::ORPHAN_TX_EXPIRE_TIME - 1s);
|
||||
orphanage.LimitOrphans(rng);
|
||||
BOOST_CHECK_EQUAL(orphanage.Size(), expected_num_orphans);
|
||||
orphanage->LimitOrphans(rng);
|
||||
BOOST_CHECK_EQUAL(orphanage->Size(), expected_num_orphans);
|
||||
|
||||
// Jump one more second, orphan should be timed out on limiting
|
||||
SetMockTime(now + node::ORPHAN_TX_EXPIRE_TIME);
|
||||
orphanage.LimitOrphans(rng);
|
||||
BOOST_CHECK_EQUAL(orphanage.Size(), 0);
|
||||
orphanage->LimitOrphans(rng);
|
||||
BOOST_CHECK_EQUAL(orphanage->Size(), 0);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(same_txid_diff_witness)
|
||||
{
|
||||
FastRandomContext det_rand{true};
|
||||
node::TxOrphanage orphanage;
|
||||
std::unique_ptr<node::TxOrphanage> orphanage{node::MakeTxOrphanage()};
|
||||
NodeId peer{0};
|
||||
|
||||
std::vector<COutPoint> empty_outpoints;
|
||||
@@ -218,31 +210,31 @@ BOOST_AUTO_TEST_CASE(same_txid_diff_witness)
|
||||
const auto& mutated_wtxid = child_mutated->GetWitnessHash();
|
||||
BOOST_CHECK(normal_wtxid != mutated_wtxid);
|
||||
|
||||
BOOST_CHECK(orphanage.AddTx(child_normal, peer));
|
||||
BOOST_CHECK(orphanage->AddTx(child_normal, peer));
|
||||
// EraseTx fails as transaction by this wtxid doesn't exist.
|
||||
BOOST_CHECK_EQUAL(orphanage.EraseTx(mutated_wtxid), 0);
|
||||
BOOST_CHECK(orphanage.HaveTx(normal_wtxid));
|
||||
BOOST_CHECK(orphanage.GetTx(normal_wtxid) == child_normal);
|
||||
BOOST_CHECK(!orphanage.HaveTx(mutated_wtxid));
|
||||
BOOST_CHECK(orphanage.GetTx(mutated_wtxid) == nullptr);
|
||||
BOOST_CHECK_EQUAL(orphanage->EraseTx(mutated_wtxid), 0);
|
||||
BOOST_CHECK(orphanage->HaveTx(normal_wtxid));
|
||||
BOOST_CHECK(orphanage->GetTx(normal_wtxid) == child_normal);
|
||||
BOOST_CHECK(!orphanage->HaveTx(mutated_wtxid));
|
||||
BOOST_CHECK(orphanage->GetTx(mutated_wtxid) == nullptr);
|
||||
|
||||
// Must succeed. Both transactions should be present in orphanage.
|
||||
BOOST_CHECK(orphanage.AddTx(child_mutated, peer));
|
||||
BOOST_CHECK(orphanage.HaveTx(normal_wtxid));
|
||||
BOOST_CHECK(orphanage.HaveTx(mutated_wtxid));
|
||||
BOOST_CHECK(orphanage->AddTx(child_mutated, peer));
|
||||
BOOST_CHECK(orphanage->HaveTx(normal_wtxid));
|
||||
BOOST_CHECK(orphanage->HaveTx(mutated_wtxid));
|
||||
|
||||
// Outpoints map should track all entries: check that both are returned as children of the parent.
|
||||
std::set<CTransactionRef> expected_children{child_normal, child_mutated};
|
||||
BOOST_CHECK(EqualTxns(expected_children, orphanage.GetChildrenFromSamePeer(parent, peer)));
|
||||
BOOST_CHECK(EqualTxns(expected_children, orphanage->GetChildrenFromSamePeer(parent, peer)));
|
||||
|
||||
// Erase by wtxid: mutated first
|
||||
BOOST_CHECK_EQUAL(orphanage.EraseTx(mutated_wtxid), 1);
|
||||
BOOST_CHECK(orphanage.HaveTx(normal_wtxid));
|
||||
BOOST_CHECK(!orphanage.HaveTx(mutated_wtxid));
|
||||
BOOST_CHECK_EQUAL(orphanage->EraseTx(mutated_wtxid), 1);
|
||||
BOOST_CHECK(orphanage->HaveTx(normal_wtxid));
|
||||
BOOST_CHECK(!orphanage->HaveTx(mutated_wtxid));
|
||||
|
||||
BOOST_CHECK_EQUAL(orphanage.EraseTx(normal_wtxid), 1);
|
||||
BOOST_CHECK(!orphanage.HaveTx(normal_wtxid));
|
||||
BOOST_CHECK(!orphanage.HaveTx(mutated_wtxid));
|
||||
BOOST_CHECK_EQUAL(orphanage->EraseTx(normal_wtxid), 1);
|
||||
BOOST_CHECK(!orphanage->HaveTx(normal_wtxid));
|
||||
BOOST_CHECK(!orphanage->HaveTx(mutated_wtxid));
|
||||
}
|
||||
|
||||
|
||||
@@ -272,34 +264,34 @@ BOOST_AUTO_TEST_CASE(get_children)
|
||||
|
||||
// All orphans provided by node1
|
||||
{
|
||||
node::TxOrphanage orphanage;
|
||||
BOOST_CHECK(orphanage.AddTx(child_p1n0, node1));
|
||||
BOOST_CHECK(orphanage.AddTx(child_p2n1, node1));
|
||||
BOOST_CHECK(orphanage.AddTx(child_p1n0_p1n1, node1));
|
||||
BOOST_CHECK(orphanage.AddTx(child_p1n0_p2n0, node1));
|
||||
std::unique_ptr<node::TxOrphanage> orphanage{node::MakeTxOrphanage()};
|
||||
BOOST_CHECK(orphanage->AddTx(child_p1n0, node1));
|
||||
BOOST_CHECK(orphanage->AddTx(child_p2n1, node1));
|
||||
BOOST_CHECK(orphanage->AddTx(child_p1n0_p1n1, node1));
|
||||
BOOST_CHECK(orphanage->AddTx(child_p1n0_p2n0, node1));
|
||||
|
||||
std::set<CTransactionRef> expected_parent1_children{child_p1n0, child_p1n0_p2n0, child_p1n0_p1n1};
|
||||
std::set<CTransactionRef> expected_parent2_children{child_p2n1, child_p1n0_p2n0};
|
||||
|
||||
BOOST_CHECK(EqualTxns(expected_parent1_children, orphanage.GetChildrenFromSamePeer(parent1, node1)));
|
||||
BOOST_CHECK(EqualTxns(expected_parent2_children, orphanage.GetChildrenFromSamePeer(parent2, node1)));
|
||||
BOOST_CHECK(EqualTxns(expected_parent1_children, orphanage->GetChildrenFromSamePeer(parent1, node1)));
|
||||
BOOST_CHECK(EqualTxns(expected_parent2_children, orphanage->GetChildrenFromSamePeer(parent2, node1)));
|
||||
|
||||
// The peer must match
|
||||
BOOST_CHECK(orphanage.GetChildrenFromSamePeer(parent1, node2).empty());
|
||||
BOOST_CHECK(orphanage.GetChildrenFromSamePeer(parent2, node2).empty());
|
||||
BOOST_CHECK(orphanage->GetChildrenFromSamePeer(parent1, node2).empty());
|
||||
BOOST_CHECK(orphanage->GetChildrenFromSamePeer(parent2, node2).empty());
|
||||
|
||||
// There shouldn't be any children of this tx in the orphanage
|
||||
BOOST_CHECK(orphanage.GetChildrenFromSamePeer(child_p1n0_p2n0, node1).empty());
|
||||
BOOST_CHECK(orphanage.GetChildrenFromSamePeer(child_p1n0_p2n0, node2).empty());
|
||||
BOOST_CHECK(orphanage->GetChildrenFromSamePeer(child_p1n0_p2n0, node1).empty());
|
||||
BOOST_CHECK(orphanage->GetChildrenFromSamePeer(child_p1n0_p2n0, node2).empty());
|
||||
}
|
||||
|
||||
// Orphans provided by node1 and node2
|
||||
{
|
||||
node::TxOrphanage orphanage;
|
||||
BOOST_CHECK(orphanage.AddTx(child_p1n0, node1));
|
||||
BOOST_CHECK(orphanage.AddTx(child_p2n1, node1));
|
||||
BOOST_CHECK(orphanage.AddTx(child_p1n0_p1n1, node2));
|
||||
BOOST_CHECK(orphanage.AddTx(child_p1n0_p2n0, node2));
|
||||
std::unique_ptr<node::TxOrphanage> orphanage{node::MakeTxOrphanage()};
|
||||
BOOST_CHECK(orphanage->AddTx(child_p1n0, node1));
|
||||
BOOST_CHECK(orphanage->AddTx(child_p2n1, node1));
|
||||
BOOST_CHECK(orphanage->AddTx(child_p1n0_p1n1, node2));
|
||||
BOOST_CHECK(orphanage->AddTx(child_p1n0_p2n0, node2));
|
||||
|
||||
// +----------------+---------------+----------------------------------+
|
||||
// | | sender=node1 | sender=node2 |
|
||||
@@ -312,53 +304,53 @@ BOOST_AUTO_TEST_CASE(get_children)
|
||||
{
|
||||
std::set<CTransactionRef> expected_parent1_node1{child_p1n0};
|
||||
|
||||
BOOST_CHECK(EqualTxns(expected_parent1_node1, orphanage.GetChildrenFromSamePeer(parent1, node1)));
|
||||
BOOST_CHECK(EqualTxns(expected_parent1_node1, orphanage->GetChildrenFromSamePeer(parent1, node1)));
|
||||
}
|
||||
|
||||
// Children of parent2 from node1:
|
||||
{
|
||||
std::set<CTransactionRef> expected_parent2_node1{child_p2n1};
|
||||
|
||||
BOOST_CHECK(EqualTxns(expected_parent2_node1, orphanage.GetChildrenFromSamePeer(parent2, node1)));
|
||||
BOOST_CHECK(EqualTxns(expected_parent2_node1, orphanage->GetChildrenFromSamePeer(parent2, node1)));
|
||||
}
|
||||
|
||||
// Children of parent1 from node2:
|
||||
{
|
||||
std::set<CTransactionRef> expected_parent1_node2{child_p1n0_p1n1, child_p1n0_p2n0};
|
||||
|
||||
BOOST_CHECK(EqualTxns(expected_parent1_node2, orphanage.GetChildrenFromSamePeer(parent1, node2)));
|
||||
BOOST_CHECK(EqualTxns(expected_parent1_node2, orphanage->GetChildrenFromSamePeer(parent1, node2)));
|
||||
}
|
||||
|
||||
// Children of parent2 from node2:
|
||||
{
|
||||
std::set<CTransactionRef> expected_parent2_node2{child_p1n0_p2n0};
|
||||
|
||||
BOOST_CHECK(EqualTxns(expected_parent2_node2, orphanage.GetChildrenFromSamePeer(parent2, node2)));
|
||||
BOOST_CHECK(EqualTxns(expected_parent2_node2, orphanage->GetChildrenFromSamePeer(parent2, node2)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(too_large_orphan_tx)
|
||||
{
|
||||
node::TxOrphanage orphanage;
|
||||
std::unique_ptr<node::TxOrphanage> orphanage{node::MakeTxOrphanage()};
|
||||
CMutableTransaction tx;
|
||||
tx.vin.resize(1);
|
||||
|
||||
// check that txs larger than MAX_STANDARD_TX_WEIGHT are not added to the orphanage
|
||||
BulkTransaction(tx, MAX_STANDARD_TX_WEIGHT + 4);
|
||||
BOOST_CHECK_EQUAL(GetTransactionWeight(CTransaction(tx)), MAX_STANDARD_TX_WEIGHT + 4);
|
||||
BOOST_CHECK(!orphanage.AddTx(MakeTransactionRef(tx), 0));
|
||||
BOOST_CHECK(!orphanage->AddTx(MakeTransactionRef(tx), 0));
|
||||
|
||||
tx.vout.clear();
|
||||
BulkTransaction(tx, MAX_STANDARD_TX_WEIGHT);
|
||||
BOOST_CHECK_EQUAL(GetTransactionWeight(CTransaction(tx)), MAX_STANDARD_TX_WEIGHT);
|
||||
BOOST_CHECK(orphanage.AddTx(MakeTransactionRef(tx), 0));
|
||||
BOOST_CHECK(orphanage->AddTx(MakeTransactionRef(tx), 0));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(process_block)
|
||||
{
|
||||
FastRandomContext det_rand{true};
|
||||
TxOrphanageTest orphanage{det_rand};
|
||||
std::unique_ptr<node::TxOrphanage> orphanage{node::MakeTxOrphanage()};
|
||||
|
||||
// Create outpoints that will be spent by transactions in the block
|
||||
std::vector<COutPoint> outpoints;
|
||||
@@ -373,10 +365,10 @@ BOOST_AUTO_TEST_CASE(process_block)
|
||||
const NodeId node{0};
|
||||
|
||||
auto control_tx = MakeTransactionSpending({}, det_rand);
|
||||
BOOST_CHECK(orphanage.AddTx(control_tx, node));
|
||||
BOOST_CHECK(orphanage->AddTx(control_tx, node));
|
||||
|
||||
auto bo_tx_same_txid = MakeTransactionSpending({outpoints.at(0)}, det_rand);
|
||||
BOOST_CHECK(orphanage.AddTx(bo_tx_same_txid, node));
|
||||
BOOST_CHECK(orphanage->AddTx(bo_tx_same_txid, node));
|
||||
block.vtx.emplace_back(bo_tx_same_txid);
|
||||
|
||||
// 2 transactions with the same txid but different witness
|
||||
@@ -384,30 +376,30 @@ BOOST_AUTO_TEST_CASE(process_block)
|
||||
block.vtx.emplace_back(b_tx_same_txid_diff_witness);
|
||||
|
||||
auto o_tx_same_txid_diff_witness = MakeMutation(b_tx_same_txid_diff_witness);
|
||||
BOOST_CHECK(orphanage.AddTx(o_tx_same_txid_diff_witness, node));
|
||||
BOOST_CHECK(orphanage->AddTx(o_tx_same_txid_diff_witness, node));
|
||||
|
||||
// 2 different transactions that spend the same input.
|
||||
auto b_tx_conflict = MakeTransactionSpending({outpoints.at(2)}, det_rand);
|
||||
block.vtx.emplace_back(b_tx_conflict);
|
||||
|
||||
auto o_tx_conflict = MakeTransactionSpending({outpoints.at(2)}, det_rand);
|
||||
BOOST_CHECK(orphanage.AddTx(o_tx_conflict, node));
|
||||
BOOST_CHECK(orphanage->AddTx(o_tx_conflict, node));
|
||||
|
||||
// 2 different transactions that have 1 overlapping input.
|
||||
auto b_tx_conflict_partial = MakeTransactionSpending({outpoints.at(3), outpoints.at(4)}, det_rand);
|
||||
block.vtx.emplace_back(b_tx_conflict_partial);
|
||||
|
||||
auto o_tx_conflict_partial_2 = MakeTransactionSpending({outpoints.at(4), outpoints.at(5)}, det_rand);
|
||||
BOOST_CHECK(orphanage.AddTx(o_tx_conflict_partial_2, node));
|
||||
BOOST_CHECK(orphanage->AddTx(o_tx_conflict_partial_2, node));
|
||||
|
||||
orphanage.EraseForBlock(block);
|
||||
orphanage->EraseForBlock(block);
|
||||
for (const auto& expected_removed : {bo_tx_same_txid, o_tx_same_txid_diff_witness, o_tx_conflict, o_tx_conflict_partial_2}) {
|
||||
const auto& expected_removed_wtxid = expected_removed->GetWitnessHash();
|
||||
BOOST_CHECK(!orphanage.HaveTx(expected_removed_wtxid));
|
||||
BOOST_CHECK(!orphanage->HaveTx(expected_removed_wtxid));
|
||||
}
|
||||
// Only remaining tx is control_tx
|
||||
BOOST_CHECK_EQUAL(orphanage.Size(), 1);
|
||||
BOOST_CHECK(orphanage.HaveTx(control_tx->GetWitnessHash()));
|
||||
BOOST_CHECK_EQUAL(orphanage->Size(), 1);
|
||||
BOOST_CHECK(orphanage->HaveTx(control_tx->GetWitnessHash()));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(multiple_announcers)
|
||||
@@ -417,60 +409,60 @@ BOOST_AUTO_TEST_CASE(multiple_announcers)
|
||||
const NodeId node2{2};
|
||||
size_t expected_total_count{0};
|
||||
FastRandomContext det_rand{true};
|
||||
TxOrphanageTest orphanage{det_rand};
|
||||
std::unique_ptr<node::TxOrphanage> orphanage{node::MakeTxOrphanage()};
|
||||
|
||||
// Check accounting per peer.
|
||||
// Check that EraseForPeer works with multiple announcers.
|
||||
{
|
||||
auto ptx = MakeTransactionSpending({}, det_rand);
|
||||
const auto& wtxid = ptx->GetWitnessHash();
|
||||
BOOST_CHECK(orphanage.AddTx(ptx, node0));
|
||||
BOOST_CHECK(orphanage.HaveTx(wtxid));
|
||||
BOOST_CHECK(orphanage->AddTx(ptx, node0));
|
||||
BOOST_CHECK(orphanage->HaveTx(wtxid));
|
||||
expected_total_count += 1;
|
||||
BOOST_CHECK_EQUAL(orphanage.Size(), expected_total_count);
|
||||
BOOST_CHECK_EQUAL(orphanage->Size(), expected_total_count);
|
||||
|
||||
// Adding again should do nothing.
|
||||
BOOST_CHECK(!orphanage.AddTx(ptx, node0));
|
||||
BOOST_CHECK_EQUAL(orphanage.Size(), expected_total_count);
|
||||
BOOST_CHECK(!orphanage->AddTx(ptx, node0));
|
||||
BOOST_CHECK_EQUAL(orphanage->Size(), expected_total_count);
|
||||
|
||||
// We can add another tx with the same txid but different witness.
|
||||
auto ptx_mutated{MakeMutation(ptx)};
|
||||
BOOST_CHECK(orphanage.AddTx(ptx_mutated, node0));
|
||||
BOOST_CHECK(orphanage.HaveTx(ptx_mutated->GetWitnessHash()));
|
||||
BOOST_CHECK(orphanage->AddTx(ptx_mutated, node0));
|
||||
BOOST_CHECK(orphanage->HaveTx(ptx_mutated->GetWitnessHash()));
|
||||
expected_total_count += 1;
|
||||
|
||||
BOOST_CHECK(!orphanage.AddTx(ptx, node0));
|
||||
BOOST_CHECK(!orphanage->AddTx(ptx, node0));
|
||||
|
||||
// Adding a new announcer should not change overall accounting.
|
||||
BOOST_CHECK(orphanage.AddAnnouncer(ptx->GetWitnessHash(), node2));
|
||||
BOOST_CHECK_EQUAL(orphanage.Size(), expected_total_count);
|
||||
BOOST_CHECK(orphanage->AddAnnouncer(ptx->GetWitnessHash(), node2));
|
||||
BOOST_CHECK_EQUAL(orphanage->Size(), expected_total_count);
|
||||
|
||||
// If we already have this announcer, AddAnnouncer returns false.
|
||||
BOOST_CHECK(orphanage.HaveTxFromPeer(ptx->GetWitnessHash(), node2));
|
||||
BOOST_CHECK(!orphanage.AddAnnouncer(ptx->GetWitnessHash(), node2));
|
||||
BOOST_CHECK(orphanage->HaveTxFromPeer(ptx->GetWitnessHash(), node2));
|
||||
BOOST_CHECK(!orphanage->AddAnnouncer(ptx->GetWitnessHash(), node2));
|
||||
|
||||
// Same with using AddTx for an existing tx, which is equivalent to using AddAnnouncer
|
||||
BOOST_CHECK(!orphanage.AddTx(ptx, node1));
|
||||
BOOST_CHECK_EQUAL(orphanage.Size(), expected_total_count);
|
||||
BOOST_CHECK(!orphanage->AddTx(ptx, node1));
|
||||
BOOST_CHECK_EQUAL(orphanage->Size(), expected_total_count);
|
||||
|
||||
// if EraseForPeer is called for an orphan with multiple announcers, the orphanage should only
|
||||
// erase that peer from the announcers set.
|
||||
orphanage.EraseForPeer(node0);
|
||||
BOOST_CHECK(orphanage.HaveTx(ptx->GetWitnessHash()));
|
||||
BOOST_CHECK(!orphanage.HaveTxFromPeer(ptx->GetWitnessHash(), node0));
|
||||
orphanage->EraseForPeer(node0);
|
||||
BOOST_CHECK(orphanage->HaveTx(ptx->GetWitnessHash()));
|
||||
BOOST_CHECK(!orphanage->HaveTxFromPeer(ptx->GetWitnessHash(), node0));
|
||||
// node0 is the only one that announced ptx_mutated
|
||||
BOOST_CHECK(!orphanage.HaveTx(ptx_mutated->GetWitnessHash()));
|
||||
BOOST_CHECK(!orphanage->HaveTx(ptx_mutated->GetWitnessHash()));
|
||||
expected_total_count -= 1;
|
||||
BOOST_CHECK_EQUAL(orphanage.Size(), expected_total_count);
|
||||
BOOST_CHECK_EQUAL(orphanage->Size(), expected_total_count);
|
||||
|
||||
// EraseForPeer should delete the orphan if it's the only announcer left.
|
||||
orphanage.EraseForPeer(node1);
|
||||
BOOST_CHECK_EQUAL(orphanage.Size(), expected_total_count);
|
||||
BOOST_CHECK(orphanage.HaveTx(ptx->GetWitnessHash()));
|
||||
orphanage.EraseForPeer(node2);
|
||||
orphanage->EraseForPeer(node1);
|
||||
BOOST_CHECK_EQUAL(orphanage->Size(), expected_total_count);
|
||||
BOOST_CHECK(orphanage->HaveTx(ptx->GetWitnessHash()));
|
||||
orphanage->EraseForPeer(node2);
|
||||
expected_total_count -= 1;
|
||||
BOOST_CHECK_EQUAL(orphanage.Size(), expected_total_count);
|
||||
BOOST_CHECK(!orphanage.HaveTx(ptx->GetWitnessHash()));
|
||||
BOOST_CHECK_EQUAL(orphanage->Size(), expected_total_count);
|
||||
BOOST_CHECK(!orphanage->HaveTx(ptx->GetWitnessHash()));
|
||||
}
|
||||
|
||||
// Check that erasure for blocks removes for all peers.
|
||||
@@ -478,18 +470,18 @@ BOOST_AUTO_TEST_CASE(multiple_announcers)
|
||||
CBlock block;
|
||||
auto tx_block = MakeTransactionSpending({}, det_rand);
|
||||
block.vtx.emplace_back(tx_block);
|
||||
BOOST_CHECK(orphanage.AddTx(tx_block, node0));
|
||||
BOOST_CHECK(!orphanage.AddTx(tx_block, node1));
|
||||
BOOST_CHECK(orphanage->AddTx(tx_block, node0));
|
||||
BOOST_CHECK(!orphanage->AddTx(tx_block, node1));
|
||||
|
||||
expected_total_count += 1;
|
||||
|
||||
BOOST_CHECK_EQUAL(orphanage.Size(), expected_total_count);
|
||||
BOOST_CHECK_EQUAL(orphanage->Size(), expected_total_count);
|
||||
|
||||
orphanage.EraseForBlock(block);
|
||||
orphanage->EraseForBlock(block);
|
||||
|
||||
expected_total_count -= 1;
|
||||
|
||||
BOOST_CHECK_EQUAL(orphanage.Size(), expected_total_count);
|
||||
BOOST_CHECK_EQUAL(orphanage->Size(), expected_total_count);
|
||||
}
|
||||
}
|
||||
BOOST_AUTO_TEST_CASE(peer_worksets)
|
||||
@@ -498,7 +490,7 @@ BOOST_AUTO_TEST_CASE(peer_worksets)
|
||||
const NodeId node1{1};
|
||||
const NodeId node2{2};
|
||||
FastRandomContext det_rand{true};
|
||||
TxOrphanageTest orphanage{det_rand};
|
||||
std::unique_ptr<node::TxOrphanage> orphanage{node::MakeTxOrphanage()};
|
||||
// AddChildrenToWorkSet should pick an announcer randomly
|
||||
{
|
||||
auto tx_missing_parent = MakeTransactionSpending({}, det_rand);
|
||||
@@ -506,18 +498,18 @@ BOOST_AUTO_TEST_CASE(peer_worksets)
|
||||
const auto& orphan_wtxid = tx_orphan->GetWitnessHash();
|
||||
|
||||
// All 3 peers are announcers.
|
||||
BOOST_CHECK(orphanage.AddTx(tx_orphan, node0));
|
||||
BOOST_CHECK(!orphanage.AddTx(tx_orphan, node1));
|
||||
BOOST_CHECK(orphanage.AddAnnouncer(orphan_wtxid, node2));
|
||||
BOOST_CHECK(orphanage->AddTx(tx_orphan, node0));
|
||||
BOOST_CHECK(!orphanage->AddTx(tx_orphan, node1));
|
||||
BOOST_CHECK(orphanage->AddAnnouncer(orphan_wtxid, node2));
|
||||
for (NodeId node = node0; node <= node2; ++node) {
|
||||
BOOST_CHECK(orphanage.HaveTxFromPeer(orphan_wtxid, node));
|
||||
BOOST_CHECK(orphanage->HaveTxFromPeer(orphan_wtxid, node));
|
||||
}
|
||||
|
||||
// Parent accepted: child is added to 1 of 3 worksets.
|
||||
orphanage.AddChildrenToWorkSet(*tx_missing_parent, det_rand);
|
||||
int node0_reconsider = orphanage.HaveTxToReconsider(node0);
|
||||
int node1_reconsider = orphanage.HaveTxToReconsider(node1);
|
||||
int node2_reconsider = orphanage.HaveTxToReconsider(node2);
|
||||
orphanage->AddChildrenToWorkSet(*tx_missing_parent, det_rand);
|
||||
int node0_reconsider = orphanage->HaveTxToReconsider(node0);
|
||||
int node1_reconsider = orphanage->HaveTxToReconsider(node1);
|
||||
int node2_reconsider = orphanage->HaveTxToReconsider(node2);
|
||||
BOOST_CHECK_EQUAL(node0_reconsider + node1_reconsider + node2_reconsider, 1);
|
||||
|
||||
NodeId assigned_peer;
|
||||
@@ -531,15 +523,15 @@ BOOST_AUTO_TEST_CASE(peer_worksets)
|
||||
}
|
||||
|
||||
// EraseForPeer also removes that tx from the workset.
|
||||
orphanage.EraseForPeer(assigned_peer);
|
||||
BOOST_CHECK_EQUAL(orphanage.GetTxToReconsider(node0), nullptr);
|
||||
orphanage->EraseForPeer(assigned_peer);
|
||||
BOOST_CHECK_EQUAL(orphanage->GetTxToReconsider(node0), nullptr);
|
||||
|
||||
// Delete this tx, clearing the orphanage.
|
||||
BOOST_CHECK_EQUAL(orphanage.EraseTx(orphan_wtxid), 1);
|
||||
BOOST_CHECK_EQUAL(orphanage.Size(), 0);
|
||||
BOOST_CHECK_EQUAL(orphanage->EraseTx(orphan_wtxid), 1);
|
||||
BOOST_CHECK_EQUAL(orphanage->Size(), 0);
|
||||
for (NodeId node = node0; node <= node2; ++node) {
|
||||
BOOST_CHECK_EQUAL(orphanage.GetTxToReconsider(node), nullptr);
|
||||
BOOST_CHECK(!orphanage.HaveTxFromPeer(orphan_wtxid, node));
|
||||
BOOST_CHECK_EQUAL(orphanage->GetTxToReconsider(node), nullptr);
|
||||
BOOST_CHECK(!orphanage->HaveTxFromPeer(orphan_wtxid, node));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -80,7 +80,7 @@ static bool CheckOrphanBehavior(node::TxDownloadManagerImpl& txdownload_impl, co
|
||||
return false;
|
||||
}
|
||||
|
||||
if (expect_orphan != txdownload_impl.m_orphanage.HaveTx(tx->GetWitnessHash())) {
|
||||
if (expect_orphan != txdownload_impl.m_orphanage->HaveTx(tx->GetWitnessHash())) {
|
||||
err_msg = strprintf("unexpectedly %s tx in orphanage", expect_orphan ? "did not find" : "found");
|
||||
return false;
|
||||
}
|
||||
@@ -162,7 +162,7 @@ BOOST_FIXTURE_TEST_CASE(tx_rejection_types, TestChain100Setup)
|
||||
BOOST_CHECK_EQUAL(parent_txid_rejected, txdownload_impl.RecentRejectsFilter().contains(child_wtxid.ToUint256()));
|
||||
|
||||
// Unless rejected, the child should be in orphanage.
|
||||
BOOST_CHECK_EQUAL(!parent_txid_rejected, txdownload_impl.m_orphanage.HaveTx(ptx_child->GetWitnessHash()));
|
||||
BOOST_CHECK_EQUAL(!parent_txid_rejected, txdownload_impl.m_orphanage->HaveTx(ptx_child->GetWitnessHash()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user