mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-09-07 06:01:50 +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)
|
void TxDownloadManagerImpl::BlockConnected(const std::shared_ptr<const CBlock>& pblock)
|
||||||
{
|
{
|
||||||
m_orphanage.EraseForBlock(*pblock);
|
m_orphanage->EraseForBlock(*pblock);
|
||||||
|
|
||||||
for (const auto& ptx : pblock->vtx) {
|
for (const auto& ptx : pblock->vtx) {
|
||||||
RecentConfirmedTransactionsFilter().insert(ptx->GetHash().ToUint256());
|
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.
|
// 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
|
// 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.
|
// 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;
|
if (include_reconsiderable && RecentRejectsReconsiderableFilter().contains(hash)) return true;
|
||||||
|
|
||||||
@@ -157,7 +157,7 @@ void TxDownloadManagerImpl::ConnectedPeer(NodeId nodeid, const TxDownloadConnect
|
|||||||
|
|
||||||
void TxDownloadManagerImpl::DisconnectedPeer(NodeId nodeid)
|
void TxDownloadManagerImpl::DisconnectedPeer(NodeId nodeid)
|
||||||
{
|
{
|
||||||
m_orphanage.EraseForPeer(nodeid);
|
m_orphanage->EraseForPeer(nodeid);
|
||||||
m_txrequest.DisconnectedPeer(nodeid);
|
m_txrequest.DisconnectedPeer(nodeid);
|
||||||
|
|
||||||
if (auto it = m_peer_info.find(nodeid); it != m_peer_info.end()) {
|
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
|
// - exists in orphanage
|
||||||
// - peer can be an orphan resolution candidate
|
// - peer can be an orphan resolution candidate
|
||||||
if (const auto* wtxid = std::get_if<Wtxid>(>xid)) {
|
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)};
|
auto unique_parents{GetUniqueParents(*orphan_tx)};
|
||||||
std::erase_if(unique_parents, [&](const auto& txid) {
|
std::erase_if(unique_parents, [&](const auto& txid) {
|
||||||
return AlreadyHaveTx(txid, /*include_reconsiderable=*/false);
|
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)) {
|
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.
|
// 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);
|
auto it_peer = m_peer_info.find(nodeid);
|
||||||
if (it_peer == m_peer_info.end()) return false;
|
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& peer_entry = m_peer_info.at(nodeid);
|
||||||
const auto& info = peer_entry.m_connection_info;
|
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
|
// 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
|
// of an orphan, this does not exclude parent + orphan pairs that we happened to request from
|
||||||
// different peers.
|
// 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
|
// 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
|
// 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->GetHash());
|
||||||
m_txrequest.ForgetTxHash(tx->GetWitnessHash());
|
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.
|
// 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)
|
std::vector<Txid> TxDownloadManagerImpl::GetUniqueParents(const CTransaction& tx)
|
||||||
@@ -398,7 +398,7 @@ node::RejectedTxTodo TxDownloadManagerImpl::MempoolRejectedTx(const CTransaction
|
|||||||
const auto& wtxid = ptx->GetWitnessHash();
|
const auto& wtxid = ptx->GetWitnessHash();
|
||||||
// Potentially flip add_extra_compact_tx to false if tx is already in orphanage, which
|
// Potentially flip add_extra_compact_tx to false if tx is already in orphanage, which
|
||||||
// means it was already added to vExtraTxnForCompact.
|
// 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
|
// 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
|
// 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) {
|
for (const auto& nodeid : orphan_resolution_candidates) {
|
||||||
if (MaybeAddOrphanResolutionCandidate(unique_parents, ptx->GetWitnessHash(), nodeid, now)) {
|
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)
|
// 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
|
// Note that, if the orphanage reaches capacity, it's possible that we immediately evict
|
||||||
// the transaction we just added.
|
// the transaction we just added.
|
||||||
m_orphanage.LimitOrphans(m_opts.m_rng);
|
m_orphanage->LimitOrphans(m_opts.m_rng);
|
||||||
} else {
|
} else {
|
||||||
unique_parents.clear();
|
unique_parents.clear();
|
||||||
LogDebug(BCLog::MEMPOOL, "not keeping orphan with rejected parents %s (wtxid=%s)\n",
|
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
|
// 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.
|
// 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());
|
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)
|
bool TxDownloadManagerImpl::HaveMoreWork(NodeId nodeid)
|
||||||
{
|
{
|
||||||
return m_orphanage.HaveTxToReconsider(nodeid);
|
return m_orphanage->HaveTxToReconsider(nodeid);
|
||||||
}
|
}
|
||||||
|
|
||||||
CTransactionRef TxDownloadManagerImpl::GetTxToReconsider(NodeId nodeid)
|
CTransactionRef TxDownloadManagerImpl::GetTxToReconsider(NodeId nodeid)
|
||||||
{
|
{
|
||||||
return m_orphanage.GetTxToReconsider(nodeid);
|
return m_orphanage->GetTxToReconsider(nodeid);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TxDownloadManagerImpl::CheckIsEmpty(NodeId nodeid)
|
void TxDownloadManagerImpl::CheckIsEmpty(NodeId nodeid)
|
||||||
{
|
{
|
||||||
assert(m_txrequest.Count(nodeid) == 0);
|
assert(m_txrequest.Count(nodeid) == 0);
|
||||||
assert(m_orphanage.UsageByPeer(nodeid) == 0);
|
assert(m_orphanage->UsageByPeer(nodeid) == 0);
|
||||||
}
|
}
|
||||||
void TxDownloadManagerImpl::CheckIsEmpty()
|
void TxDownloadManagerImpl::CheckIsEmpty()
|
||||||
{
|
{
|
||||||
assert(m_orphanage.TotalOrphanUsage() == 0);
|
assert(m_orphanage->TotalOrphanUsage() == 0);
|
||||||
assert(m_orphanage.Size() == 0);
|
assert(m_orphanage->Size() == 0);
|
||||||
assert(m_txrequest.Size() == 0);
|
assert(m_txrequest.Size() == 0);
|
||||||
assert(m_num_wtxid_peers == 0);
|
assert(m_num_wtxid_peers == 0);
|
||||||
}
|
}
|
||||||
std::vector<TxOrphanage::OrphanTxBase> TxDownloadManagerImpl::GetOrphanTransactions() const
|
std::vector<TxOrphanage::OrphanTxBase> TxDownloadManagerImpl::GetOrphanTransactions() const
|
||||||
{
|
{
|
||||||
return m_orphanage.GetOrphanTransactions();
|
return m_orphanage->GetOrphanTransactions();
|
||||||
}
|
}
|
||||||
} // namespace node
|
} // namespace node
|
||||||
|
@@ -22,7 +22,7 @@ public:
|
|||||||
TxDownloadOptions m_opts;
|
TxDownloadOptions m_opts;
|
||||||
|
|
||||||
/** Manages unvalidated tx data (orphan transactions for which we are downloading ancestors). */
|
/** 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. */
|
/** Tracks candidates for requesting and downloading transaction data. */
|
||||||
TxRequestTracker m_txrequest;
|
TxRequestTracker m_txrequest;
|
||||||
|
|
||||||
@@ -128,7 +128,7 @@ public:
|
|||||||
return *m_lazy_recent_confirmed_transactions;
|
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 {
|
struct PeerInfo {
|
||||||
/** Information relevant to scheduling tx requests. */
|
/** Information relevant to scheduling tx requests. */
|
||||||
|
@@ -12,8 +12,85 @@
|
|||||||
|
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
|
|
||||||
namespace node{
|
namespace node {
|
||||||
bool TxOrphanage::AddTx(const CTransactionRef& tx, NodeId peer)
|
|
||||||
|
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 Txid& hash = tx->GetHash();
|
||||||
const Wtxid& wtxid = tx->GetWitnessHash();
|
const Wtxid& wtxid = tx->GetWitnessHash();
|
||||||
@@ -53,7 +130,7 @@ bool TxOrphanage::AddTx(const CTransactionRef& tx, NodeId peer)
|
|||||||
return true;
|
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);
|
const auto it = m_orphans.find(wtxid);
|
||||||
if (it != m_orphans.end()) {
|
if (it != m_orphans.end()) {
|
||||||
@@ -70,7 +147,7 @@ bool TxOrphanage::AddAnnouncer(const Wtxid& wtxid, NodeId peer)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
int TxOrphanage::EraseTx(const Wtxid& wtxid)
|
int TxOrphanageImpl::EraseTx(const Wtxid& wtxid)
|
||||||
{
|
{
|
||||||
std::map<Wtxid, OrphanTx>::iterator it = m_orphans.find(wtxid);
|
std::map<Wtxid, OrphanTx>::iterator it = m_orphans.find(wtxid);
|
||||||
if (it == m_orphans.end())
|
if (it == m_orphans.end())
|
||||||
@@ -116,7 +193,7 @@ int TxOrphanage::EraseTx(const Wtxid& wtxid)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TxOrphanage::EraseForPeer(NodeId peer)
|
void TxOrphanageImpl::EraseForPeer(NodeId peer)
|
||||||
{
|
{
|
||||||
// Zeroes out this peer's m_total_usage.
|
// Zeroes out this peer's m_total_usage.
|
||||||
m_peer_orphanage_info.erase(peer);
|
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);
|
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;
|
unsigned int nEvicted = 0;
|
||||||
auto nNow{Now<NodeSeconds>()};
|
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);
|
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++) {
|
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));
|
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);
|
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);
|
auto it = m_orphans.find(wtxid);
|
||||||
return it != m_orphans.end() ? it->second.tx : nullptr;
|
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);
|
auto it = m_orphans.find(wtxid);
|
||||||
return (it != m_orphans.end() && it->second.announcers.contains(peer));
|
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);
|
auto peer_it = m_peer_orphanage_info.find(peer);
|
||||||
if (peer_it == m_peer_orphanage_info.end()) return nullptr;
|
if (peer_it == m_peer_orphanage_info.end()) return nullptr;
|
||||||
@@ -236,7 +314,7 @@ CTransactionRef TxOrphanage::GetTxToReconsider(NodeId peer)
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TxOrphanage::HaveTxToReconsider(NodeId peer)
|
bool TxOrphanageImpl::HaveTxToReconsider(NodeId peer)
|
||||||
{
|
{
|
||||||
auto peer_it = m_peer_orphanage_info.find(peer);
|
auto peer_it = m_peer_orphanage_info.find(peer);
|
||||||
if (peer_it == m_peer_orphanage_info.end()) return false;
|
if (peer_it == m_peer_orphanage_info.end()) return false;
|
||||||
@@ -245,7 +323,7 @@ bool TxOrphanage::HaveTxToReconsider(NodeId peer)
|
|||||||
return !work_set.empty();
|
return !work_set.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
void TxOrphanage::EraseForBlock(const CBlock& block)
|
void TxOrphanageImpl::EraseForBlock(const CBlock& block)
|
||||||
{
|
{
|
||||||
std::vector<Wtxid> vOrphanErase;
|
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
|
// First construct a vector of iterators to ensure we do not return duplicates of the same tx
|
||||||
// and so we can sort by nTimeExpire.
|
// and so we can sort by nTimeExpire.
|
||||||
@@ -313,7 +391,7 @@ std::vector<CTransactionRef> TxOrphanage::GetChildrenFromSamePeer(const CTransac
|
|||||||
return children_found;
|
return children_found;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<TxOrphanage::OrphanTxBase> TxOrphanage::GetOrphanTransactions() const
|
std::vector<TxOrphanage::OrphanTxBase> TxOrphanageImpl::GetOrphanTransactions() const
|
||||||
{
|
{
|
||||||
std::vector<OrphanTxBase> ret;
|
std::vector<OrphanTxBase> ret;
|
||||||
ret.reserve(m_orphans.size());
|
ret.reserve(m_orphans.size());
|
||||||
@@ -323,7 +401,13 @@ std::vector<TxOrphanage::OrphanTxBase> TxOrphanage::GetOrphanTransactions() cons
|
|||||||
return ret;
|
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
|
// Check that cached m_total_announcements is correct
|
||||||
unsigned int counted_total_announcements{0};
|
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
|
} // namespace node
|
||||||
|
@@ -15,7 +15,7 @@
|
|||||||
#include <map>
|
#include <map>
|
||||||
#include <set>
|
#include <set>
|
||||||
|
|
||||||
namespace node{
|
namespace node {
|
||||||
/** Expiration time for orphan transactions */
|
/** Expiration time for orphan transactions */
|
||||||
static constexpr auto ORPHAN_TX_EXPIRE_TIME{20min};
|
static constexpr auto ORPHAN_TX_EXPIRE_TIME{20min};
|
||||||
/** Minimum time between orphan transactions expire time checks */
|
/** Minimum time between orphan transactions expire time checks */
|
||||||
@@ -31,56 +31,6 @@ static const uint32_t DEFAULT_MAX_ORPHAN_TRANSACTIONS{100};
|
|||||||
*/
|
*/
|
||||||
class TxOrphanage {
|
class TxOrphanage {
|
||||||
public:
|
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 */
|
/** Allows providing orphan information externally */
|
||||||
struct OrphanTxBase {
|
struct OrphanTxBase {
|
||||||
CTransactionRef tx;
|
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
|
/** Get the total usage (weight) of all orphans. If an orphan has multiple announcers, its usage is
|
||||||
* only counted once within this total. */
|
* 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
|
/** 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
|
* announcers, its weight will be accounted for in each PeerOrphanInfo, so the total of all
|
||||||
* peers' UsageByPeer() may be larger than TotalOrphanBytes(). */
|
* peers' UsageByPeer() may be larger than TotalOrphanBytes(). */
|
||||||
int64_t UsageByPeer(NodeId peer) const {
|
virtual int64_t UsageByPeer(NodeId peer) const = 0;
|
||||||
auto peer_it = m_peer_orphanage_info.find(peer);
|
|
||||||
return peer_it == m_peer_orphanage_info.end() ? 0 : peer_it->second.m_total_usage;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Check consistency between PeerOrphanInfo and m_orphans. Recalculate counters and ensure they
|
/** Check consistency between PeerOrphanInfo and m_orphans. Recalculate counters and ensure they
|
||||||
* match what is cached. */
|
* match what is cached. */
|
||||||
void SanityCheck() const;
|
virtual void SanityCheck() const = 0;
|
||||||
|
|
||||||
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};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/** Create a new TxOrphanage instance */
|
||||||
|
std::unique_ptr<TxOrphanage> MakeTxOrphanage() noexcept;
|
||||||
|
|
||||||
} // namespace node
|
} // namespace node
|
||||||
#endif // BITCOIN_NODE_TXORPHANAGE_H
|
#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)
|
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
|
// Orphanage usage should never exceed what is allowed
|
||||||
Assert(orphanage.Size() <= max_orphan_count);
|
Assert(txdownload_impl.m_orphanage->Size() <= max_orphan_count);
|
||||||
txdownload_impl.m_orphanage.SanityCheck();
|
txdownload_impl.m_orphanage->SanityCheck();
|
||||||
|
|
||||||
// We should never have more than the maximum in-flight requests out for a peer.
|
// We should never have more than the maximum in-flight requests out for a peer.
|
||||||
for (NodeId peer = 0; peer < NUM_PEERS; ++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);
|
block.vtx.push_back(rand_tx);
|
||||||
txdownload_impl.BlockConnected(std::make_shared<CBlock>(block));
|
txdownload_impl.BlockConnected(std::make_shared<CBlock>(block));
|
||||||
// Block transactions must be removed from orphanage
|
// 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();
|
txdownload_impl.BlockDisconnected();
|
||||||
@@ -401,7 +399,7 @@ FUZZ_TARGET(txdownloadman_impl, .init = initialize)
|
|||||||
const auto& package = maybe_package->m_txns;
|
const auto& package = maybe_package->m_txns;
|
||||||
// Parent is in m_lazy_recent_rejects_reconsiderable and child is in m_orphanage
|
// 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.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
|
// Package has not been rejected
|
||||||
Assert(!txdownload_impl.RecentRejectsReconsiderableFilter().contains(GetPackageHash(package)));
|
Assert(!txdownload_impl.RecentRejectsReconsiderableFilter().contains(GetPackageHash(package)));
|
||||||
// Neither is in m_lazy_recent_rejects
|
// Neither is in m_lazy_recent_rejects
|
||||||
|
@@ -37,7 +37,7 @@ FUZZ_TARGET(txorphan, .init = initialize_orphanage)
|
|||||||
FastRandomContext orphanage_rng{ConsumeUInt256(fuzzed_data_provider)};
|
FastRandomContext orphanage_rng{ConsumeUInt256(fuzzed_data_provider)};
|
||||||
SetMockTime(ConsumeTime(fuzzed_data_provider));
|
SetMockTime(ConsumeTime(fuzzed_data_provider));
|
||||||
|
|
||||||
node::TxOrphanage orphanage;
|
auto orphanage = node::MakeTxOrphanage();
|
||||||
std::vector<COutPoint> outpoints; // Duplicates are tolerated
|
std::vector<COutPoint> outpoints; // Duplicates are tolerated
|
||||||
outpoints.reserve(200'000);
|
outpoints.reserve(200'000);
|
||||||
|
|
||||||
@@ -86,11 +86,11 @@ FUZZ_TARGET(txorphan, .init = initialize_orphanage)
|
|||||||
// previous loop and potentially the parent of this tx.
|
// previous loop and potentially the parent of this tx.
|
||||||
if (ptx_potential_parent) {
|
if (ptx_potential_parent) {
|
||||||
// Set up future GetTxToReconsider call.
|
// 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.
|
// Check that all txns returned from GetChildrenFrom* are indeed a direct child of this tx.
|
||||||
NodeId peer_id = fuzzed_data_provider.ConsumeIntegral<NodeId>();
|
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) {
|
assert(std::any_of(child->vin.cbegin(), child->vin.cend(), [&](const auto& input) {
|
||||||
return input.prevout.hash == ptx_potential_parent->GetHash();
|
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)
|
LIMITED_WHILE(fuzzed_data_provider.ConsumeBool(), 10 * node::DEFAULT_MAX_ORPHAN_TRANSACTIONS)
|
||||||
{
|
{
|
||||||
NodeId peer_id = fuzzed_data_provider.ConsumeIntegral<NodeId>();
|
NodeId peer_id = fuzzed_data_provider.ConsumeIntegral<NodeId>();
|
||||||
const auto total_bytes_start{orphanage.TotalOrphanUsage()};
|
const auto total_bytes_start{orphanage->TotalOrphanUsage()};
|
||||||
const auto total_peer_bytes_start{orphanage.UsageByPeer(peer_id)};
|
const auto total_peer_bytes_start{orphanage->UsageByPeer(peer_id)};
|
||||||
const auto tx_weight{GetTransactionWeight(*tx)};
|
const auto tx_weight{GetTransactionWeight(*tx)};
|
||||||
|
|
||||||
CallOneOf(
|
CallOneOf(
|
||||||
fuzzed_data_provider,
|
fuzzed_data_provider,
|
||||||
[&] {
|
[&] {
|
||||||
{
|
{
|
||||||
CTransactionRef ref = orphanage.GetTxToReconsider(peer_id);
|
CTransactionRef ref = orphanage->GetTxToReconsider(peer_id);
|
||||||
if (ref) {
|
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
|
// 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
|
// 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
|
// have_tx == true -> add_tx == false
|
||||||
Assert(!have_tx || !add_tx);
|
Assert(!have_tx || !add_tx);
|
||||||
|
|
||||||
if (add_tx) {
|
if (add_tx) {
|
||||||
Assert(orphanage.UsageByPeer(peer_id) == tx_weight + total_peer_bytes_start);
|
Assert(orphanage->UsageByPeer(peer_id) == tx_weight + total_peer_bytes_start);
|
||||||
Assert(orphanage.TotalOrphanUsage() == tx_weight + total_bytes_start);
|
Assert(orphanage->TotalOrphanUsage() == tx_weight + total_bytes_start);
|
||||||
Assert(tx_weight <= MAX_STANDARD_TX_WEIGHT);
|
Assert(tx_weight <= MAX_STANDARD_TX_WEIGHT);
|
||||||
} else {
|
} else {
|
||||||
// Peer may have been added as an announcer.
|
// Peer may have been added as an announcer.
|
||||||
if (orphanage.UsageByPeer(peer_id) == tx_weight + total_peer_bytes_start) {
|
if (orphanage->UsageByPeer(peer_id) == tx_weight + total_peer_bytes_start) {
|
||||||
Assert(orphanage.HaveTxFromPeer(wtxid, peer_id));
|
Assert(orphanage->HaveTxFromPeer(wtxid, peer_id));
|
||||||
} else {
|
} else {
|
||||||
// Otherwise, there must not be any change to the peer byte count.
|
// 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.
|
// 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
|
// if have_tx is still false, it must be too big
|
||||||
Assert(!have_tx == (tx_weight > MAX_STANDARD_TX_WEIGHT));
|
Assert(!have_tx == (tx_weight > MAX_STANDARD_TX_WEIGHT));
|
||||||
Assert(!have_tx || !add_tx);
|
Assert(!have_tx || !add_tx);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[&] {
|
[&] {
|
||||||
bool have_tx = orphanage.HaveTx(tx->GetWitnessHash());
|
bool have_tx = orphanage->HaveTx(tx->GetWitnessHash());
|
||||||
bool have_tx_and_peer = orphanage.HaveTxFromPeer(tx->GetWitnessHash(), peer_id);
|
bool have_tx_and_peer = orphanage->HaveTxFromPeer(tx->GetWitnessHash(), peer_id);
|
||||||
// AddAnnouncer should return false if tx doesn't exist or we already HaveTxFromPeer.
|
// 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
|
// have_tx == false -> added_announcer == false
|
||||||
Assert(have_tx || !added_announcer);
|
Assert(have_tx || !added_announcer);
|
||||||
// have_tx_and_peer == true -> added_announcer == false
|
// 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
|
// Total bytes should not have changed. If peer was added as announcer, byte
|
||||||
// accounting must have been updated.
|
// accounting must have been updated.
|
||||||
Assert(orphanage.TotalOrphanUsage() == total_bytes_start);
|
Assert(orphanage->TotalOrphanUsage() == total_bytes_start);
|
||||||
if (added_announcer) {
|
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 {
|
} 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 = orphanage->HaveTx(tx->GetWitnessHash());
|
||||||
bool have_tx_and_peer{orphanage.HaveTxFromPeer(wtxid, peer_id)};
|
bool have_tx_and_peer{orphanage->HaveTxFromPeer(wtxid, peer_id)};
|
||||||
// EraseTx should return 0 if m_orphans doesn't have the tx
|
// EraseTx should return 0 if m_orphans doesn't have the tx
|
||||||
{
|
{
|
||||||
auto bytes_from_peer_before{orphanage.UsageByPeer(peer_id)};
|
auto bytes_from_peer_before{orphanage->UsageByPeer(peer_id)};
|
||||||
Assert(have_tx == orphanage.EraseTx(tx->GetWitnessHash()));
|
Assert(have_tx == orphanage->EraseTx(tx->GetWitnessHash()));
|
||||||
if (have_tx) {
|
if (have_tx) {
|
||||||
Assert(orphanage.TotalOrphanUsage() == total_bytes_start - tx_weight);
|
Assert(orphanage->TotalOrphanUsage() == total_bytes_start - tx_weight);
|
||||||
if (have_tx_and_peer) {
|
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 {
|
} else {
|
||||||
Assert(orphanage.UsageByPeer(peer_id) == bytes_from_peer_before);
|
Assert(orphanage->UsageByPeer(peer_id) == bytes_from_peer_before);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Assert(orphanage.TotalOrphanUsage() == total_bytes_start);
|
Assert(orphanage->TotalOrphanUsage() == total_bytes_start);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
have_tx = orphanage.HaveTx(tx->GetWitnessHash());
|
have_tx = orphanage->HaveTx(tx->GetWitnessHash());
|
||||||
have_tx_and_peer = orphanage.HaveTxFromPeer(wtxid, peer_id);
|
have_tx_and_peer = orphanage->HaveTxFromPeer(wtxid, peer_id);
|
||||||
// have_tx should be false and EraseTx should fail
|
// 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);
|
orphanage->EraseForPeer(peer_id);
|
||||||
Assert(!orphanage.HaveTxFromPeer(tx->GetWitnessHash(), peer_id));
|
Assert(!orphanage->HaveTxFromPeer(tx->GetWitnessHash(), peer_id));
|
||||||
Assert(orphanage.UsageByPeer(peer_id) == 0);
|
Assert(orphanage->UsageByPeer(peer_id) == 0);
|
||||||
},
|
},
|
||||||
[&] {
|
[&] {
|
||||||
// Make a block out of txs and then EraseForBlock
|
// 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);
|
auto& tx_to_remove = PickValue(fuzzed_data_provider, tx_history);
|
||||||
block.vtx.push_back(tx_to_remove);
|
block.vtx.push_back(tx_to_remove);
|
||||||
}
|
}
|
||||||
orphanage.EraseForBlock(block);
|
orphanage->EraseForBlock(block);
|
||||||
for (const auto& tx_removed : block.vtx) {
|
for (const auto& tx_removed : block.vtx) {
|
||||||
Assert(!orphanage.HaveTx(tx_removed->GetWitnessHash()));
|
Assert(!orphanage->HaveTx(tx_removed->GetWitnessHash()));
|
||||||
Assert(!orphanage.HaveTxFromPeer(tx_removed->GetWitnessHash(), peer_id));
|
Assert(!orphanage->HaveTxFromPeer(tx_removed->GetWitnessHash(), peer_id));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[&] {
|
[&] {
|
||||||
// test mocktime and expiry
|
// test mocktime and expiry
|
||||||
SetMockTime(ConsumeTime(fuzzed_data_provider));
|
SetMockTime(ConsumeTime(fuzzed_data_provider));
|
||||||
orphanage.LimitOrphans(orphanage_rng);
|
orphanage->LimitOrphans(orphanage_rng);
|
||||||
Assert(orphanage.Size() <= node::DEFAULT_MAX_ORPHAN_TRANSACTIONS);
|
Assert(orphanage->Size() <= node::DEFAULT_MAX_ORPHAN_TRANSACTIONS);
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -228,9 +228,9 @@ FUZZ_TARGET(txorphan, .init = initialize_orphanage)
|
|||||||
ptx_potential_parent = tx;
|
ptx_potential_parent = tx;
|
||||||
}
|
}
|
||||||
|
|
||||||
const bool have_tx{orphanage.HaveTx(tx->GetWitnessHash())};
|
const bool have_tx{orphanage->HaveTx(tx->GetWitnessHash())};
|
||||||
const bool get_tx_nonnull{orphanage.GetTx(tx->GetWitnessHash()) != nullptr};
|
const bool get_tx_nonnull{orphanage->GetTx(tx->GetWitnessHash()) != nullptr};
|
||||||
Assert(have_tx == get_tx_nonnull);
|
Assert(have_tx == get_tx_nonnull);
|
||||||
}
|
}
|
||||||
orphanage.SanityCheck();
|
orphanage->SanityCheck();
|
||||||
}
|
}
|
||||||
|
@@ -21,14 +21,6 @@
|
|||||||
|
|
||||||
BOOST_FIXTURE_TEST_SUITE(orphanage_tests, TestingSetup)
|
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)
|
static void MakeNewKeyWithFastRandomContext(CKey& key, FastRandomContext& rand_ctx)
|
||||||
{
|
{
|
||||||
std::vector<unsigned char> keydata;
|
std::vector<unsigned char> keydata;
|
||||||
@@ -90,7 +82,7 @@ BOOST_AUTO_TEST_CASE(DoS_mapOrphans)
|
|||||||
// signature's R and S values have leading zeros.
|
// signature's R and S values have leading zeros.
|
||||||
m_rng.Reseed(uint256{33});
|
m_rng.Reseed(uint256{33});
|
||||||
|
|
||||||
TxOrphanageTest orphanage{m_rng};
|
std::unique_ptr<node::TxOrphanage> orphanage{node::MakeTxOrphanage()};
|
||||||
CKey key;
|
CKey key;
|
||||||
MakeNewKeyWithFastRandomContext(key, m_rng);
|
MakeNewKeyWithFastRandomContext(key, m_rng);
|
||||||
FillableSigningProvider keystore;
|
FillableSigningProvider keystore;
|
||||||
@@ -115,7 +107,7 @@ BOOST_AUTO_TEST_CASE(DoS_mapOrphans)
|
|||||||
tx.vout[0].scriptPubKey = GetScriptForDestination(PKHash(key.GetPubKey()));
|
tx.vout[0].scriptPubKey = GetScriptForDestination(PKHash(key.GetPubKey()));
|
||||||
|
|
||||||
auto ptx = MakeTransactionRef(tx);
|
auto ptx = MakeTransactionRef(tx);
|
||||||
orphanage.AddTx(ptx, i);
|
orphanage->AddTx(ptx, i);
|
||||||
orphans_added.emplace_back(ptx);
|
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));
|
BOOST_CHECK(SignSignature(keystore, *txPrev, tx, 0, SIGHASH_ALL, empty));
|
||||||
|
|
||||||
auto ptx = MakeTransactionRef(tx);
|
auto ptx = MakeTransactionRef(tx);
|
||||||
orphanage.AddTx(ptx, i);
|
orphanage->AddTx(ptx, i);
|
||||||
orphans_added.emplace_back(ptx);
|
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++)
|
for (unsigned int j = 1; j < tx.vin.size(); j++)
|
||||||
tx.vin[j].scriptSig = tx.vin[0].scriptSig;
|
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
|
// Non-existent peer; nothing should be deleted
|
||||||
orphanage.EraseForPeer(/*peer=*/-1);
|
orphanage->EraseForPeer(/*peer=*/-1);
|
||||||
BOOST_CHECK_EQUAL(orphanage.Size(), expected_num_orphans);
|
BOOST_CHECK_EQUAL(orphanage->Size(), expected_num_orphans);
|
||||||
|
|
||||||
// Each of first three peers stored
|
// Each of first three peers stored
|
||||||
// two transactions each.
|
// two transactions each.
|
||||||
for (NodeId i = 0; i < 3; i++)
|
for (NodeId i = 0; i < 3; i++)
|
||||||
{
|
{
|
||||||
orphanage.EraseForPeer(i);
|
orphanage->EraseForPeer(i);
|
||||||
expected_num_orphans -= 2;
|
expected_num_orphans -= 2;
|
||||||
BOOST_CHECK(orphanage.Size() == expected_num_orphans);
|
BOOST_CHECK(orphanage->Size() == expected_num_orphans);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test LimitOrphanTxSize() function, nothing should timeout:
|
// Test LimitOrphanTxSize() function, nothing should timeout:
|
||||||
FastRandomContext rng{/*fDeterministic=*/true};
|
FastRandomContext rng{/*fDeterministic=*/true};
|
||||||
orphanage.LimitOrphans(rng);
|
orphanage->LimitOrphans(rng);
|
||||||
BOOST_CHECK_EQUAL(orphanage.Size(), expected_num_orphans);
|
BOOST_CHECK_EQUAL(orphanage->Size(), expected_num_orphans);
|
||||||
|
|
||||||
// Add one more orphan, check timeout logic
|
// Add one more orphan, check timeout logic
|
||||||
auto timeout_tx = MakeTransactionSpending(/*outpoints=*/{}, rng);
|
auto timeout_tx = MakeTransactionSpending(/*outpoints=*/{}, rng);
|
||||||
orphanage.AddTx(timeout_tx, 0);
|
orphanage->AddTx(timeout_tx, 0);
|
||||||
expected_num_orphans += 1;
|
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
|
// One second shy of expiration
|
||||||
SetMockTime(now + node::ORPHAN_TX_EXPIRE_TIME - 1s);
|
SetMockTime(now + node::ORPHAN_TX_EXPIRE_TIME - 1s);
|
||||||
orphanage.LimitOrphans(rng);
|
orphanage->LimitOrphans(rng);
|
||||||
BOOST_CHECK_EQUAL(orphanage.Size(), expected_num_orphans);
|
BOOST_CHECK_EQUAL(orphanage->Size(), expected_num_orphans);
|
||||||
|
|
||||||
// Jump one more second, orphan should be timed out on limiting
|
// Jump one more second, orphan should be timed out on limiting
|
||||||
SetMockTime(now + node::ORPHAN_TX_EXPIRE_TIME);
|
SetMockTime(now + node::ORPHAN_TX_EXPIRE_TIME);
|
||||||
orphanage.LimitOrphans(rng);
|
orphanage->LimitOrphans(rng);
|
||||||
BOOST_CHECK_EQUAL(orphanage.Size(), 0);
|
BOOST_CHECK_EQUAL(orphanage->Size(), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(same_txid_diff_witness)
|
BOOST_AUTO_TEST_CASE(same_txid_diff_witness)
|
||||||
{
|
{
|
||||||
FastRandomContext det_rand{true};
|
FastRandomContext det_rand{true};
|
||||||
node::TxOrphanage orphanage;
|
std::unique_ptr<node::TxOrphanage> orphanage{node::MakeTxOrphanage()};
|
||||||
NodeId peer{0};
|
NodeId peer{0};
|
||||||
|
|
||||||
std::vector<COutPoint> empty_outpoints;
|
std::vector<COutPoint> empty_outpoints;
|
||||||
@@ -218,31 +210,31 @@ BOOST_AUTO_TEST_CASE(same_txid_diff_witness)
|
|||||||
const auto& mutated_wtxid = child_mutated->GetWitnessHash();
|
const auto& mutated_wtxid = child_mutated->GetWitnessHash();
|
||||||
BOOST_CHECK(normal_wtxid != mutated_wtxid);
|
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.
|
// EraseTx fails as transaction by this wtxid doesn't exist.
|
||||||
BOOST_CHECK_EQUAL(orphanage.EraseTx(mutated_wtxid), 0);
|
BOOST_CHECK_EQUAL(orphanage->EraseTx(mutated_wtxid), 0);
|
||||||
BOOST_CHECK(orphanage.HaveTx(normal_wtxid));
|
BOOST_CHECK(orphanage->HaveTx(normal_wtxid));
|
||||||
BOOST_CHECK(orphanage.GetTx(normal_wtxid) == child_normal);
|
BOOST_CHECK(orphanage->GetTx(normal_wtxid) == child_normal);
|
||||||
BOOST_CHECK(!orphanage.HaveTx(mutated_wtxid));
|
BOOST_CHECK(!orphanage->HaveTx(mutated_wtxid));
|
||||||
BOOST_CHECK(orphanage.GetTx(mutated_wtxid) == nullptr);
|
BOOST_CHECK(orphanage->GetTx(mutated_wtxid) == nullptr);
|
||||||
|
|
||||||
// Must succeed. Both transactions should be present in orphanage.
|
// Must succeed. Both transactions should be present in orphanage.
|
||||||
BOOST_CHECK(orphanage.AddTx(child_mutated, peer));
|
BOOST_CHECK(orphanage->AddTx(child_mutated, peer));
|
||||||
BOOST_CHECK(orphanage.HaveTx(normal_wtxid));
|
BOOST_CHECK(orphanage->HaveTx(normal_wtxid));
|
||||||
BOOST_CHECK(orphanage.HaveTx(mutated_wtxid));
|
BOOST_CHECK(orphanage->HaveTx(mutated_wtxid));
|
||||||
|
|
||||||
// Outpoints map should track all entries: check that both are returned as children of the parent.
|
// 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};
|
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
|
// Erase by wtxid: mutated first
|
||||||
BOOST_CHECK_EQUAL(orphanage.EraseTx(mutated_wtxid), 1);
|
BOOST_CHECK_EQUAL(orphanage->EraseTx(mutated_wtxid), 1);
|
||||||
BOOST_CHECK(orphanage.HaveTx(normal_wtxid));
|
BOOST_CHECK(orphanage->HaveTx(normal_wtxid));
|
||||||
BOOST_CHECK(!orphanage.HaveTx(mutated_wtxid));
|
BOOST_CHECK(!orphanage->HaveTx(mutated_wtxid));
|
||||||
|
|
||||||
BOOST_CHECK_EQUAL(orphanage.EraseTx(normal_wtxid), 1);
|
BOOST_CHECK_EQUAL(orphanage->EraseTx(normal_wtxid), 1);
|
||||||
BOOST_CHECK(!orphanage.HaveTx(normal_wtxid));
|
BOOST_CHECK(!orphanage->HaveTx(normal_wtxid));
|
||||||
BOOST_CHECK(!orphanage.HaveTx(mutated_wtxid));
|
BOOST_CHECK(!orphanage->HaveTx(mutated_wtxid));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -272,34 +264,34 @@ BOOST_AUTO_TEST_CASE(get_children)
|
|||||||
|
|
||||||
// All orphans provided by node1
|
// All orphans provided by node1
|
||||||
{
|
{
|
||||||
node::TxOrphanage orphanage;
|
std::unique_ptr<node::TxOrphanage> orphanage{node::MakeTxOrphanage()};
|
||||||
BOOST_CHECK(orphanage.AddTx(child_p1n0, node1));
|
BOOST_CHECK(orphanage->AddTx(child_p1n0, node1));
|
||||||
BOOST_CHECK(orphanage.AddTx(child_p2n1, node1));
|
BOOST_CHECK(orphanage->AddTx(child_p2n1, node1));
|
||||||
BOOST_CHECK(orphanage.AddTx(child_p1n0_p1n1, node1));
|
BOOST_CHECK(orphanage->AddTx(child_p1n0_p1n1, node1));
|
||||||
BOOST_CHECK(orphanage.AddTx(child_p1n0_p2n0, 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_parent1_children{child_p1n0, child_p1n0_p2n0, child_p1n0_p1n1};
|
||||||
std::set<CTransactionRef> expected_parent2_children{child_p2n1, child_p1n0_p2n0};
|
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_parent1_children, orphanage->GetChildrenFromSamePeer(parent1, node1)));
|
||||||
BOOST_CHECK(EqualTxns(expected_parent2_children, orphanage.GetChildrenFromSamePeer(parent2, node1)));
|
BOOST_CHECK(EqualTxns(expected_parent2_children, orphanage->GetChildrenFromSamePeer(parent2, node1)));
|
||||||
|
|
||||||
// The peer must match
|
// The peer must match
|
||||||
BOOST_CHECK(orphanage.GetChildrenFromSamePeer(parent1, node2).empty());
|
BOOST_CHECK(orphanage->GetChildrenFromSamePeer(parent1, node2).empty());
|
||||||
BOOST_CHECK(orphanage.GetChildrenFromSamePeer(parent2, node2).empty());
|
BOOST_CHECK(orphanage->GetChildrenFromSamePeer(parent2, node2).empty());
|
||||||
|
|
||||||
// There shouldn't be any children of this tx in the orphanage
|
// 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, node1).empty());
|
||||||
BOOST_CHECK(orphanage.GetChildrenFromSamePeer(child_p1n0_p2n0, node2).empty());
|
BOOST_CHECK(orphanage->GetChildrenFromSamePeer(child_p1n0_p2n0, node2).empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Orphans provided by node1 and node2
|
// Orphans provided by node1 and node2
|
||||||
{
|
{
|
||||||
node::TxOrphanage orphanage;
|
std::unique_ptr<node::TxOrphanage> orphanage{node::MakeTxOrphanage()};
|
||||||
BOOST_CHECK(orphanage.AddTx(child_p1n0, node1));
|
BOOST_CHECK(orphanage->AddTx(child_p1n0, node1));
|
||||||
BOOST_CHECK(orphanage.AddTx(child_p2n1, node1));
|
BOOST_CHECK(orphanage->AddTx(child_p2n1, node1));
|
||||||
BOOST_CHECK(orphanage.AddTx(child_p1n0_p1n1, node2));
|
BOOST_CHECK(orphanage->AddTx(child_p1n0_p1n1, node2));
|
||||||
BOOST_CHECK(orphanage.AddTx(child_p1n0_p2n0, node2));
|
BOOST_CHECK(orphanage->AddTx(child_p1n0_p2n0, node2));
|
||||||
|
|
||||||
// +----------------+---------------+----------------------------------+
|
// +----------------+---------------+----------------------------------+
|
||||||
// | | sender=node1 | sender=node2 |
|
// | | sender=node1 | sender=node2 |
|
||||||
@@ -312,53 +304,53 @@ BOOST_AUTO_TEST_CASE(get_children)
|
|||||||
{
|
{
|
||||||
std::set<CTransactionRef> expected_parent1_node1{child_p1n0};
|
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:
|
// Children of parent2 from node1:
|
||||||
{
|
{
|
||||||
std::set<CTransactionRef> expected_parent2_node1{child_p2n1};
|
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:
|
// Children of parent1 from node2:
|
||||||
{
|
{
|
||||||
std::set<CTransactionRef> expected_parent1_node2{child_p1n0_p1n1, child_p1n0_p2n0};
|
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:
|
// Children of parent2 from node2:
|
||||||
{
|
{
|
||||||
std::set<CTransactionRef> expected_parent2_node2{child_p1n0_p2n0};
|
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)
|
BOOST_AUTO_TEST_CASE(too_large_orphan_tx)
|
||||||
{
|
{
|
||||||
node::TxOrphanage orphanage;
|
std::unique_ptr<node::TxOrphanage> orphanage{node::MakeTxOrphanage()};
|
||||||
CMutableTransaction tx;
|
CMutableTransaction tx;
|
||||||
tx.vin.resize(1);
|
tx.vin.resize(1);
|
||||||
|
|
||||||
// check that txs larger than MAX_STANDARD_TX_WEIGHT are not added to the orphanage
|
// check that txs larger than MAX_STANDARD_TX_WEIGHT are not added to the orphanage
|
||||||
BulkTransaction(tx, MAX_STANDARD_TX_WEIGHT + 4);
|
BulkTransaction(tx, MAX_STANDARD_TX_WEIGHT + 4);
|
||||||
BOOST_CHECK_EQUAL(GetTransactionWeight(CTransaction(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();
|
tx.vout.clear();
|
||||||
BulkTransaction(tx, MAX_STANDARD_TX_WEIGHT);
|
BulkTransaction(tx, MAX_STANDARD_TX_WEIGHT);
|
||||||
BOOST_CHECK_EQUAL(GetTransactionWeight(CTransaction(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)
|
BOOST_AUTO_TEST_CASE(process_block)
|
||||||
{
|
{
|
||||||
FastRandomContext det_rand{true};
|
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
|
// Create outpoints that will be spent by transactions in the block
|
||||||
std::vector<COutPoint> outpoints;
|
std::vector<COutPoint> outpoints;
|
||||||
@@ -373,10 +365,10 @@ BOOST_AUTO_TEST_CASE(process_block)
|
|||||||
const NodeId node{0};
|
const NodeId node{0};
|
||||||
|
|
||||||
auto control_tx = MakeTransactionSpending({}, det_rand);
|
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);
|
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);
|
block.vtx.emplace_back(bo_tx_same_txid);
|
||||||
|
|
||||||
// 2 transactions with the same txid but different witness
|
// 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);
|
block.vtx.emplace_back(b_tx_same_txid_diff_witness);
|
||||||
|
|
||||||
auto o_tx_same_txid_diff_witness = MakeMutation(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.
|
// 2 different transactions that spend the same input.
|
||||||
auto b_tx_conflict = MakeTransactionSpending({outpoints.at(2)}, det_rand);
|
auto b_tx_conflict = MakeTransactionSpending({outpoints.at(2)}, det_rand);
|
||||||
block.vtx.emplace_back(b_tx_conflict);
|
block.vtx.emplace_back(b_tx_conflict);
|
||||||
|
|
||||||
auto o_tx_conflict = MakeTransactionSpending({outpoints.at(2)}, det_rand);
|
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.
|
// 2 different transactions that have 1 overlapping input.
|
||||||
auto b_tx_conflict_partial = MakeTransactionSpending({outpoints.at(3), outpoints.at(4)}, det_rand);
|
auto b_tx_conflict_partial = MakeTransactionSpending({outpoints.at(3), outpoints.at(4)}, det_rand);
|
||||||
block.vtx.emplace_back(b_tx_conflict_partial);
|
block.vtx.emplace_back(b_tx_conflict_partial);
|
||||||
|
|
||||||
auto o_tx_conflict_partial_2 = MakeTransactionSpending({outpoints.at(4), outpoints.at(5)}, det_rand);
|
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}) {
|
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();
|
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
|
// Only remaining tx is control_tx
|
||||||
BOOST_CHECK_EQUAL(orphanage.Size(), 1);
|
BOOST_CHECK_EQUAL(orphanage->Size(), 1);
|
||||||
BOOST_CHECK(orphanage.HaveTx(control_tx->GetWitnessHash()));
|
BOOST_CHECK(orphanage->HaveTx(control_tx->GetWitnessHash()));
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(multiple_announcers)
|
BOOST_AUTO_TEST_CASE(multiple_announcers)
|
||||||
@@ -417,60 +409,60 @@ BOOST_AUTO_TEST_CASE(multiple_announcers)
|
|||||||
const NodeId node2{2};
|
const NodeId node2{2};
|
||||||
size_t expected_total_count{0};
|
size_t expected_total_count{0};
|
||||||
FastRandomContext det_rand{true};
|
FastRandomContext det_rand{true};
|
||||||
TxOrphanageTest orphanage{det_rand};
|
std::unique_ptr<node::TxOrphanage> orphanage{node::MakeTxOrphanage()};
|
||||||
|
|
||||||
// Check accounting per peer.
|
// Check accounting per peer.
|
||||||
// Check that EraseForPeer works with multiple announcers.
|
// Check that EraseForPeer works with multiple announcers.
|
||||||
{
|
{
|
||||||
auto ptx = MakeTransactionSpending({}, det_rand);
|
auto ptx = MakeTransactionSpending({}, det_rand);
|
||||||
const auto& wtxid = ptx->GetWitnessHash();
|
const auto& wtxid = ptx->GetWitnessHash();
|
||||||
BOOST_CHECK(orphanage.AddTx(ptx, node0));
|
BOOST_CHECK(orphanage->AddTx(ptx, node0));
|
||||||
BOOST_CHECK(orphanage.HaveTx(wtxid));
|
BOOST_CHECK(orphanage->HaveTx(wtxid));
|
||||||
expected_total_count += 1;
|
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.
|
// Adding again should do nothing.
|
||||||
BOOST_CHECK(!orphanage.AddTx(ptx, node0));
|
BOOST_CHECK(!orphanage->AddTx(ptx, node0));
|
||||||
BOOST_CHECK_EQUAL(orphanage.Size(), expected_total_count);
|
BOOST_CHECK_EQUAL(orphanage->Size(), expected_total_count);
|
||||||
|
|
||||||
// We can add another tx with the same txid but different witness.
|
// We can add another tx with the same txid but different witness.
|
||||||
auto ptx_mutated{MakeMutation(ptx)};
|
auto ptx_mutated{MakeMutation(ptx)};
|
||||||
BOOST_CHECK(orphanage.AddTx(ptx_mutated, node0));
|
BOOST_CHECK(orphanage->AddTx(ptx_mutated, node0));
|
||||||
BOOST_CHECK(orphanage.HaveTx(ptx_mutated->GetWitnessHash()));
|
BOOST_CHECK(orphanage->HaveTx(ptx_mutated->GetWitnessHash()));
|
||||||
expected_total_count += 1;
|
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.
|
// Adding a new announcer should not change overall accounting.
|
||||||
BOOST_CHECK(orphanage.AddAnnouncer(ptx->GetWitnessHash(), node2));
|
BOOST_CHECK(orphanage->AddAnnouncer(ptx->GetWitnessHash(), node2));
|
||||||
BOOST_CHECK_EQUAL(orphanage.Size(), expected_total_count);
|
BOOST_CHECK_EQUAL(orphanage->Size(), expected_total_count);
|
||||||
|
|
||||||
// If we already have this announcer, AddAnnouncer returns false.
|
// If we already have this announcer, AddAnnouncer returns false.
|
||||||
BOOST_CHECK(orphanage.HaveTxFromPeer(ptx->GetWitnessHash(), node2));
|
BOOST_CHECK(orphanage->HaveTxFromPeer(ptx->GetWitnessHash(), node2));
|
||||||
BOOST_CHECK(!orphanage.AddAnnouncer(ptx->GetWitnessHash(), node2));
|
BOOST_CHECK(!orphanage->AddAnnouncer(ptx->GetWitnessHash(), node2));
|
||||||
|
|
||||||
// Same with using AddTx for an existing tx, which is equivalent to using AddAnnouncer
|
// Same with using AddTx for an existing tx, which is equivalent to using AddAnnouncer
|
||||||
BOOST_CHECK(!orphanage.AddTx(ptx, node1));
|
BOOST_CHECK(!orphanage->AddTx(ptx, node1));
|
||||||
BOOST_CHECK_EQUAL(orphanage.Size(), expected_total_count);
|
BOOST_CHECK_EQUAL(orphanage->Size(), expected_total_count);
|
||||||
|
|
||||||
// if EraseForPeer is called for an orphan with multiple announcers, the orphanage should only
|
// if EraseForPeer is called for an orphan with multiple announcers, the orphanage should only
|
||||||
// erase that peer from the announcers set.
|
// erase that peer from the announcers set.
|
||||||
orphanage.EraseForPeer(node0);
|
orphanage->EraseForPeer(node0);
|
||||||
BOOST_CHECK(orphanage.HaveTx(ptx->GetWitnessHash()));
|
BOOST_CHECK(orphanage->HaveTx(ptx->GetWitnessHash()));
|
||||||
BOOST_CHECK(!orphanage.HaveTxFromPeer(ptx->GetWitnessHash(), node0));
|
BOOST_CHECK(!orphanage->HaveTxFromPeer(ptx->GetWitnessHash(), node0));
|
||||||
// node0 is the only one that announced ptx_mutated
|
// 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;
|
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.
|
// EraseForPeer should delete the orphan if it's the only announcer left.
|
||||||
orphanage.EraseForPeer(node1);
|
orphanage->EraseForPeer(node1);
|
||||||
BOOST_CHECK_EQUAL(orphanage.Size(), expected_total_count);
|
BOOST_CHECK_EQUAL(orphanage->Size(), expected_total_count);
|
||||||
BOOST_CHECK(orphanage.HaveTx(ptx->GetWitnessHash()));
|
BOOST_CHECK(orphanage->HaveTx(ptx->GetWitnessHash()));
|
||||||
orphanage.EraseForPeer(node2);
|
orphanage->EraseForPeer(node2);
|
||||||
expected_total_count -= 1;
|
expected_total_count -= 1;
|
||||||
BOOST_CHECK_EQUAL(orphanage.Size(), expected_total_count);
|
BOOST_CHECK_EQUAL(orphanage->Size(), expected_total_count);
|
||||||
BOOST_CHECK(!orphanage.HaveTx(ptx->GetWitnessHash()));
|
BOOST_CHECK(!orphanage->HaveTx(ptx->GetWitnessHash()));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check that erasure for blocks removes for all peers.
|
// Check that erasure for blocks removes for all peers.
|
||||||
@@ -478,18 +470,18 @@ BOOST_AUTO_TEST_CASE(multiple_announcers)
|
|||||||
CBlock block;
|
CBlock block;
|
||||||
auto tx_block = MakeTransactionSpending({}, det_rand);
|
auto tx_block = MakeTransactionSpending({}, det_rand);
|
||||||
block.vtx.emplace_back(tx_block);
|
block.vtx.emplace_back(tx_block);
|
||||||
BOOST_CHECK(orphanage.AddTx(tx_block, node0));
|
BOOST_CHECK(orphanage->AddTx(tx_block, node0));
|
||||||
BOOST_CHECK(!orphanage.AddTx(tx_block, node1));
|
BOOST_CHECK(!orphanage->AddTx(tx_block, node1));
|
||||||
|
|
||||||
expected_total_count += 1;
|
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;
|
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)
|
BOOST_AUTO_TEST_CASE(peer_worksets)
|
||||||
@@ -498,7 +490,7 @@ BOOST_AUTO_TEST_CASE(peer_worksets)
|
|||||||
const NodeId node1{1};
|
const NodeId node1{1};
|
||||||
const NodeId node2{2};
|
const NodeId node2{2};
|
||||||
FastRandomContext det_rand{true};
|
FastRandomContext det_rand{true};
|
||||||
TxOrphanageTest orphanage{det_rand};
|
std::unique_ptr<node::TxOrphanage> orphanage{node::MakeTxOrphanage()};
|
||||||
// AddChildrenToWorkSet should pick an announcer randomly
|
// AddChildrenToWorkSet should pick an announcer randomly
|
||||||
{
|
{
|
||||||
auto tx_missing_parent = MakeTransactionSpending({}, det_rand);
|
auto tx_missing_parent = MakeTransactionSpending({}, det_rand);
|
||||||
@@ -506,18 +498,18 @@ BOOST_AUTO_TEST_CASE(peer_worksets)
|
|||||||
const auto& orphan_wtxid = tx_orphan->GetWitnessHash();
|
const auto& orphan_wtxid = tx_orphan->GetWitnessHash();
|
||||||
|
|
||||||
// All 3 peers are announcers.
|
// All 3 peers are announcers.
|
||||||
BOOST_CHECK(orphanage.AddTx(tx_orphan, node0));
|
BOOST_CHECK(orphanage->AddTx(tx_orphan, node0));
|
||||||
BOOST_CHECK(!orphanage.AddTx(tx_orphan, node1));
|
BOOST_CHECK(!orphanage->AddTx(tx_orphan, node1));
|
||||||
BOOST_CHECK(orphanage.AddAnnouncer(orphan_wtxid, node2));
|
BOOST_CHECK(orphanage->AddAnnouncer(orphan_wtxid, node2));
|
||||||
for (NodeId node = node0; node <= node2; ++node) {
|
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.
|
// Parent accepted: child is added to 1 of 3 worksets.
|
||||||
orphanage.AddChildrenToWorkSet(*tx_missing_parent, det_rand);
|
orphanage->AddChildrenToWorkSet(*tx_missing_parent, det_rand);
|
||||||
int node0_reconsider = orphanage.HaveTxToReconsider(node0);
|
int node0_reconsider = orphanage->HaveTxToReconsider(node0);
|
||||||
int node1_reconsider = orphanage.HaveTxToReconsider(node1);
|
int node1_reconsider = orphanage->HaveTxToReconsider(node1);
|
||||||
int node2_reconsider = orphanage.HaveTxToReconsider(node2);
|
int node2_reconsider = orphanage->HaveTxToReconsider(node2);
|
||||||
BOOST_CHECK_EQUAL(node0_reconsider + node1_reconsider + node2_reconsider, 1);
|
BOOST_CHECK_EQUAL(node0_reconsider + node1_reconsider + node2_reconsider, 1);
|
||||||
|
|
||||||
NodeId assigned_peer;
|
NodeId assigned_peer;
|
||||||
@@ -531,15 +523,15 @@ BOOST_AUTO_TEST_CASE(peer_worksets)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// EraseForPeer also removes that tx from the workset.
|
// EraseForPeer also removes that tx from the workset.
|
||||||
orphanage.EraseForPeer(assigned_peer);
|
orphanage->EraseForPeer(assigned_peer);
|
||||||
BOOST_CHECK_EQUAL(orphanage.GetTxToReconsider(node0), nullptr);
|
BOOST_CHECK_EQUAL(orphanage->GetTxToReconsider(node0), nullptr);
|
||||||
|
|
||||||
// Delete this tx, clearing the orphanage.
|
// Delete this tx, clearing the orphanage.
|
||||||
BOOST_CHECK_EQUAL(orphanage.EraseTx(orphan_wtxid), 1);
|
BOOST_CHECK_EQUAL(orphanage->EraseTx(orphan_wtxid), 1);
|
||||||
BOOST_CHECK_EQUAL(orphanage.Size(), 0);
|
BOOST_CHECK_EQUAL(orphanage->Size(), 0);
|
||||||
for (NodeId node = node0; node <= node2; ++node) {
|
for (NodeId node = node0; node <= node2; ++node) {
|
||||||
BOOST_CHECK_EQUAL(orphanage.GetTxToReconsider(node), nullptr);
|
BOOST_CHECK_EQUAL(orphanage->GetTxToReconsider(node), nullptr);
|
||||||
BOOST_CHECK(!orphanage.HaveTxFromPeer(orphan_wtxid, node));
|
BOOST_CHECK(!orphanage->HaveTxFromPeer(orphan_wtxid, node));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -80,7 +80,7 @@ static bool CheckOrphanBehavior(node::TxDownloadManagerImpl& txdownload_impl, co
|
|||||||
return false;
|
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");
|
err_msg = strprintf("unexpectedly %s tx in orphanage", expect_orphan ? "did not find" : "found");
|
||||||
return false;
|
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()));
|
BOOST_CHECK_EQUAL(parent_txid_rejected, txdownload_impl.RecentRejectsFilter().contains(child_wtxid.ToUint256()));
|
||||||
|
|
||||||
// Unless rejected, the child should be in orphanage.
|
// 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