aboutsummaryrefslogtreecommitdiff
path: root/src/txorphanage.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/txorphanage.cpp')
-rw-r--r--src/txorphanage.cpp157
1 files changed, 113 insertions, 44 deletions
diff --git a/src/txorphanage.cpp b/src/txorphanage.cpp
index e907fffd4f..3eaf53939d 100644
--- a/src/txorphanage.cpp
+++ b/src/txorphanage.cpp
@@ -8,13 +8,14 @@
#include <logging.h>
#include <policy/policy.h>
#include <primitives/transaction.h>
+#include <util/time.h>
#include <cassert>
-/** Expiration time for orphan transactions in seconds */
-static constexpr int64_t ORPHAN_TX_EXPIRE_TIME = 20 * 60;
-/** Minimum time between orphan transactions expire time checks in seconds */
-static constexpr int64_t ORPHAN_TX_EXPIRE_INTERVAL = 5 * 60;
+/** Expiration time for orphan transactions */
+static constexpr auto ORPHAN_TX_EXPIRE_TIME{20min};
+/** Minimum time between orphan transactions expire time checks */
+static constexpr auto ORPHAN_TX_EXPIRE_INTERVAL{5min};
bool TxOrphanage::AddTx(const CTransactionRef& tx, NodeId peer)
@@ -23,7 +24,7 @@ bool TxOrphanage::AddTx(const CTransactionRef& tx, NodeId peer)
const Txid& hash = tx->GetHash();
const Wtxid& wtxid = tx->GetWitnessHash();
- if (m_orphans.count(hash))
+ if (m_orphans.count(wtxid))
return false;
// Ignore big transactions, to avoid a
@@ -40,30 +41,28 @@ bool TxOrphanage::AddTx(const CTransactionRef& tx, NodeId peer)
return false;
}
- auto ret = m_orphans.emplace(hash, OrphanTx{tx, peer, GetTime() + ORPHAN_TX_EXPIRE_TIME, m_orphan_list.size()});
+ auto ret = m_orphans.emplace(wtxid, OrphanTx{tx, peer, Now<NodeSeconds>() + ORPHAN_TX_EXPIRE_TIME, m_orphan_list.size()});
assert(ret.second);
m_orphan_list.push_back(ret.first);
- // Allow for lookups in the orphan pool by wtxid, as well as txid
- m_wtxid_to_orphan_it.emplace(tx->GetWitnessHash(), ret.first);
for (const CTxIn& txin : tx->vin) {
m_outpoint_to_orphan_it[txin.prevout].insert(ret.first);
}
- LogPrint(BCLog::TXPACKAGES, "stored orphan tx %s (wtxid=%s) (mapsz %u outsz %u)\n", hash.ToString(), wtxid.ToString(),
+ LogPrint(BCLog::TXPACKAGES, "stored orphan tx %s (wtxid=%s), weight: %u (mapsz %u outsz %u)\n", hash.ToString(), wtxid.ToString(), sz,
m_orphans.size(), m_outpoint_to_orphan_it.size());
return true;
}
-int TxOrphanage::EraseTx(const Txid& txid)
+int TxOrphanage::EraseTx(const Wtxid& wtxid)
{
LOCK(m_mutex);
- return EraseTxNoLock(txid);
+ return EraseTxNoLock(wtxid);
}
-int TxOrphanage::EraseTxNoLock(const Txid& txid)
+int TxOrphanage::EraseTxNoLock(const Wtxid& wtxid)
{
AssertLockHeld(m_mutex);
- std::map<Txid, OrphanTx>::iterator it = m_orphans.find(txid);
+ std::map<Wtxid, OrphanTx>::iterator it = m_orphans.find(wtxid);
if (it == m_orphans.end())
return 0;
for (const CTxIn& txin : it->second.tx->vin)
@@ -85,10 +84,12 @@ int TxOrphanage::EraseTxNoLock(const Txid& txid)
m_orphan_list[old_pos] = it_last;
it_last->second.list_pos = old_pos;
}
- const auto& wtxid = it->second.tx->GetWitnessHash();
- LogPrint(BCLog::TXPACKAGES, " removed orphan tx %s (wtxid=%s)\n", txid.ToString(), wtxid.ToString());
+ const auto& txid = it->second.tx->GetHash();
+ // Time spent in orphanage = difference between current and entry time.
+ // Entry time is equal to ORPHAN_TX_EXPIRE_TIME earlier than entry's expiry.
+ LogPrint(BCLog::TXPACKAGES, " removed orphan tx %s (wtxid=%s) after %ds\n", txid.ToString(), wtxid.ToString(),
+ Ticks<std::chrono::seconds>(NodeClock::now() + ORPHAN_TX_EXPIRE_TIME - it->second.nTimeExpire));
m_orphan_list.pop_back();
- m_wtxid_to_orphan_it.erase(it->second.tx->GetWitnessHash());
m_orphans.erase(it);
return 1;
@@ -101,16 +102,16 @@ void TxOrphanage::EraseForPeer(NodeId peer)
m_peer_work_set.erase(peer);
int nErased = 0;
- std::map<Txid, OrphanTx>::iterator iter = m_orphans.begin();
+ std::map<Wtxid, OrphanTx>::iterator iter = m_orphans.begin();
while (iter != m_orphans.end())
{
- std::map<Txid, OrphanTx>::iterator maybeErase = iter++; // increment to avoid iterator becoming invalid
- if (maybeErase->second.fromPeer == peer)
- {
- nErased += EraseTxNoLock(maybeErase->second.tx->GetHash());
+ // increment to avoid iterator becoming invalid after erasure
+ const auto& [wtxid, orphan] = *iter++;
+ if (orphan.fromPeer == peer) {
+ nErased += EraseTxNoLock(wtxid);
}
}
- if (nErased > 0) LogPrint(BCLog::TXPACKAGES, "Erased %d orphan tx from peer=%d\n", nErased, peer);
+ if (nErased > 0) LogPrint(BCLog::TXPACKAGES, "Erased %d orphan transaction(s) from peer=%d\n", nErased, peer);
}
void TxOrphanage::LimitOrphans(unsigned int max_orphans, FastRandomContext& rng)
@@ -118,31 +119,30 @@ void TxOrphanage::LimitOrphans(unsigned int max_orphans, FastRandomContext& rng)
LOCK(m_mutex);
unsigned int nEvicted = 0;
- static int64_t nNextSweep;
- int64_t nNow = GetTime();
- if (nNextSweep <= nNow) {
+ auto nNow{Now<NodeSeconds>()};
+ if (m_next_sweep <= nNow) {
// Sweep out expired orphan pool entries:
int nErased = 0;
- int64_t nMinExpTime = nNow + ORPHAN_TX_EXPIRE_TIME - ORPHAN_TX_EXPIRE_INTERVAL;
- std::map<Txid, OrphanTx>::iterator iter = m_orphans.begin();
+ auto nMinExpTime{nNow + ORPHAN_TX_EXPIRE_TIME - ORPHAN_TX_EXPIRE_INTERVAL};
+ std::map<Wtxid, OrphanTx>::iterator iter = m_orphans.begin();
while (iter != m_orphans.end())
{
- std::map<Txid, OrphanTx>::iterator maybeErase = iter++;
+ std::map<Wtxid, OrphanTx>::iterator maybeErase = iter++;
if (maybeErase->second.nTimeExpire <= nNow) {
- nErased += EraseTxNoLock(maybeErase->second.tx->GetHash());
+ nErased += EraseTxNoLock(maybeErase->second.tx->GetWitnessHash());
} else {
nMinExpTime = std::min(maybeErase->second.nTimeExpire, nMinExpTime);
}
}
// Sweep again 5 minutes after the next entry that expires in order to batch the linear scan.
- nNextSweep = nMinExpTime + ORPHAN_TX_EXPIRE_INTERVAL;
+ m_next_sweep = nMinExpTime + ORPHAN_TX_EXPIRE_INTERVAL;
if (nErased > 0) LogPrint(BCLog::TXPACKAGES, "Erased %d orphan tx due to expiration\n", nErased);
}
while (m_orphans.size() > max_orphans)
{
// Evict a random orphan:
size_t randompos = rng.randrange(m_orphan_list.size());
- EraseTxNoLock(m_orphan_list[randompos]->first);
+ EraseTxNoLock(m_orphan_list[randompos]->second.tx->GetWitnessHash());
++nEvicted;
}
if (nEvicted > 0) LogPrint(BCLog::TXPACKAGES, "orphanage overflow, removed %u tx\n", nEvicted);
@@ -159,7 +159,7 @@ void TxOrphanage::AddChildrenToWorkSet(const CTransaction& tx)
for (const auto& elem : it_by_prev->second) {
// Get this source peer's work set, emplacing an empty set if it didn't exist
// (note: if this peer wasn't still connected, we would have removed the orphan tx already)
- std::set<Txid>& orphan_work_set = m_peer_work_set.try_emplace(elem->second.fromPeer).first->second;
+ std::set<Wtxid>& orphan_work_set = m_peer_work_set.try_emplace(elem->second.fromPeer).first->second;
// Add this tx to the work set
orphan_work_set.insert(elem->first);
LogPrint(BCLog::TXPACKAGES, "added %s (wtxid=%s) to peer %d workset\n",
@@ -169,14 +169,10 @@ void TxOrphanage::AddChildrenToWorkSet(const CTransaction& tx)
}
}
-bool TxOrphanage::HaveTx(const GenTxid& gtxid) const
+bool TxOrphanage::HaveTx(const Wtxid& wtxid) const
{
LOCK(m_mutex);
- if (gtxid.IsWtxid()) {
- return m_wtxid_to_orphan_it.count(Wtxid::FromUint256(gtxid.GetHash()));
- } else {
- return m_orphans.count(Txid::FromUint256(gtxid.GetHash()));
- }
+ return m_orphans.count(wtxid);
}
CTransactionRef TxOrphanage::GetTxToReconsider(NodeId peer)
@@ -187,10 +183,10 @@ CTransactionRef TxOrphanage::GetTxToReconsider(NodeId peer)
if (work_set_it != m_peer_work_set.end()) {
auto& work_set = work_set_it->second;
while (!work_set.empty()) {
- Txid txid = *work_set.begin();
+ Wtxid wtxid = *work_set.begin();
work_set.erase(work_set.begin());
- const auto orphan_it = m_orphans.find(txid);
+ const auto orphan_it = m_orphans.find(wtxid);
if (orphan_it != m_orphans.end()) {
return orphan_it->second.tx;
}
@@ -215,7 +211,7 @@ void TxOrphanage::EraseForBlock(const CBlock& block)
{
LOCK(m_mutex);
- std::vector<Txid> vOrphanErase;
+ std::vector<Wtxid> vOrphanErase;
for (const CTransactionRef& ptx : block.vtx) {
const CTransaction& tx = *ptx;
@@ -226,8 +222,7 @@ void TxOrphanage::EraseForBlock(const CBlock& block)
if (itByPrev == m_outpoint_to_orphan_it.end()) continue;
for (auto mi = itByPrev->second.begin(); mi != itByPrev->second.end(); ++mi) {
const CTransaction& orphanTx = *(*mi)->second.tx;
- const auto& orphanHash = orphanTx.GetHash();
- vOrphanErase.push_back(orphanHash);
+ vOrphanErase.push_back(orphanTx.GetWitnessHash());
}
}
}
@@ -238,6 +233,80 @@ void TxOrphanage::EraseForBlock(const CBlock& block)
for (const auto& orphanHash : vOrphanErase) {
nErased += EraseTxNoLock(orphanHash);
}
- LogPrint(BCLog::TXPACKAGES, "Erased %d orphan tx included or conflicted by block\n", nErased);
+ LogPrint(BCLog::TXPACKAGES, "Erased %d orphan transaction(s) included or conflicted by block\n", nErased);
+ }
+}
+
+std::vector<CTransactionRef> TxOrphanage::GetChildrenFromSamePeer(const CTransactionRef& parent, NodeId nodeid) const
+{
+ LOCK(m_mutex);
+
+ // First construct a vector of iterators to ensure we do not return duplicates of the same tx
+ // and so we can sort by nTimeExpire.
+ std::vector<OrphanMap::iterator> iters;
+
+ // For each output, get all entries spending this prevout, filtering for ones from the specified peer.
+ for (unsigned int i = 0; i < parent->vout.size(); i++) {
+ const auto it_by_prev = m_outpoint_to_orphan_it.find(COutPoint(parent->GetHash(), i));
+ if (it_by_prev != m_outpoint_to_orphan_it.end()) {
+ for (const auto& elem : it_by_prev->second) {
+ if (elem->second.fromPeer == nodeid) {
+ iters.emplace_back(elem);
+ }
+ }
+ }
+ }
+
+ // Sort by address so that duplicates can be deleted. At the same time, sort so that more recent
+ // orphans (which expire later) come first. Break ties based on address, as nTimeExpire is
+ // quantified in seconds and it is possible for orphans to have the same expiry.
+ std::sort(iters.begin(), iters.end(), [](const auto& lhs, const auto& rhs) {
+ if (lhs->second.nTimeExpire == rhs->second.nTimeExpire) {
+ return &(*lhs) < &(*rhs);
+ } else {
+ return lhs->second.nTimeExpire > rhs->second.nTimeExpire;
+ }
+ });
+ // Erase duplicates
+ iters.erase(std::unique(iters.begin(), iters.end()), iters.end());
+
+ // Convert to a vector of CTransactionRef
+ std::vector<CTransactionRef> children_found;
+ children_found.reserve(iters.size());
+ for (const auto& child_iter : iters) {
+ children_found.emplace_back(child_iter->second.tx);
+ }
+ return children_found;
+}
+
+std::vector<std::pair<CTransactionRef, NodeId>> TxOrphanage::GetChildrenFromDifferentPeer(const CTransactionRef& parent, NodeId nodeid) const
+{
+ LOCK(m_mutex);
+
+ // First construct vector of iterators to ensure we do not return duplicates of the same tx.
+ std::vector<OrphanMap::iterator> iters;
+
+ // For each output, get all entries spending this prevout, filtering for ones not from the specified peer.
+ for (unsigned int i = 0; i < parent->vout.size(); i++) {
+ const auto it_by_prev = m_outpoint_to_orphan_it.find(COutPoint(parent->GetHash(), i));
+ if (it_by_prev != m_outpoint_to_orphan_it.end()) {
+ for (const auto& elem : it_by_prev->second) {
+ if (elem->second.fromPeer != nodeid) {
+ iters.emplace_back(elem);
+ }
+ }
+ }
+ }
+
+ // Erase duplicates
+ std::sort(iters.begin(), iters.end(), IteratorComparator());
+ iters.erase(std::unique(iters.begin(), iters.end()), iters.end());
+
+ // Convert iterators to pair<CTransactionRef, NodeId>
+ std::vector<std::pair<CTransactionRef, NodeId>> children_found;
+ children_found.reserve(iters.size());
+ for (const auto& child_iter : iters) {
+ children_found.emplace_back(child_iter->second.tx, child_iter->second.fromPeer);
}
+ return children_found;
}