Bitcoin Core  25.99.0
P2P Digital Currency
txorphanage.cpp
Go to the documentation of this file.
1 // Copyright (c) 2021-2022 The Bitcoin Core developers
2 // Distributed under the MIT software license, see the accompanying
3 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
4 
5 #include <txorphanage.h>
6 
7 #include <consensus/validation.h>
8 #include <logging.h>
9 #include <policy/policy.h>
10 
11 #include <cassert>
12 
14 static constexpr int64_t ORPHAN_TX_EXPIRE_TIME = 20 * 60;
16 static constexpr int64_t ORPHAN_TX_EXPIRE_INTERVAL = 5 * 60;
17 
18 
20 {
21  LOCK(m_mutex);
22 
23  const uint256& hash = tx->GetHash();
24  if (m_orphans.count(hash))
25  return false;
26 
27  // Ignore big transactions, to avoid a
28  // send-big-orphans memory exhaustion attack. If a peer has a legitimate
29  // large transaction with a missing parent then we assume
30  // it will rebroadcast it later, after the parent transaction(s)
31  // have been mined or received.
32  // 100 orphans, each of which is at most 100,000 bytes big is
33  // at most 10 megabytes of orphans and somewhat more byprev index (in the worst case):
34  unsigned int sz = GetTransactionWeight(*tx);
35  if (sz > MAX_STANDARD_TX_WEIGHT)
36  {
37  LogPrint(BCLog::MEMPOOL, "ignoring large orphan tx (size: %u, hash: %s)\n", sz, hash.ToString());
38  return false;
39  }
40 
41  auto ret = m_orphans.emplace(hash, OrphanTx{tx, peer, GetTime() + ORPHAN_TX_EXPIRE_TIME, m_orphan_list.size()});
42  assert(ret.second);
43  m_orphan_list.push_back(ret.first);
44  // Allow for lookups in the orphan pool by wtxid, as well as txid
45  m_wtxid_to_orphan_it.emplace(tx->GetWitnessHash(), ret.first);
46  for (const CTxIn& txin : tx->vin) {
47  m_outpoint_to_orphan_it[txin.prevout].insert(ret.first);
48  }
49 
50  LogPrint(BCLog::MEMPOOL, "stored orphan tx %s (mapsz %u outsz %u)\n", hash.ToString(),
51  m_orphans.size(), m_outpoint_to_orphan_it.size());
52  return true;
53 }
54 
55 int TxOrphanage::EraseTx(const uint256& txid)
56 {
57  LOCK(m_mutex);
58  return _EraseTx(txid);
59 }
60 
62 {
64  std::map<uint256, OrphanTx>::iterator it = m_orphans.find(txid);
65  if (it == m_orphans.end())
66  return 0;
67  for (const CTxIn& txin : it->second.tx->vin)
68  {
69  auto itPrev = m_outpoint_to_orphan_it.find(txin.prevout);
70  if (itPrev == m_outpoint_to_orphan_it.end())
71  continue;
72  itPrev->second.erase(it);
73  if (itPrev->second.empty())
74  m_outpoint_to_orphan_it.erase(itPrev);
75  }
76 
77  size_t old_pos = it->second.list_pos;
78  assert(m_orphan_list[old_pos] == it);
79  if (old_pos + 1 != m_orphan_list.size()) {
80  // Unless we're deleting the last entry in m_orphan_list, move the last
81  // entry to the position we're deleting.
82  auto it_last = m_orphan_list.back();
83  m_orphan_list[old_pos] = it_last;
84  it_last->second.list_pos = old_pos;
85  }
86  m_orphan_list.pop_back();
87  m_wtxid_to_orphan_it.erase(it->second.tx->GetWitnessHash());
88 
89  m_orphans.erase(it);
90  return 1;
91 }
92 
94 {
95  LOCK(m_mutex);
96 
97  m_peer_work_set.erase(peer);
98 
99  int nErased = 0;
100  std::map<uint256, OrphanTx>::iterator iter = m_orphans.begin();
101  while (iter != m_orphans.end())
102  {
103  std::map<uint256, OrphanTx>::iterator maybeErase = iter++; // increment to avoid iterator becoming invalid
104  if (maybeErase->second.fromPeer == peer)
105  {
106  nErased += _EraseTx(maybeErase->second.tx->GetHash());
107  }
108  }
109  if (nErased > 0) LogPrint(BCLog::MEMPOOL, "Erased %d orphan tx from peer=%d\n", nErased, peer);
110 }
111 
112 void TxOrphanage::LimitOrphans(unsigned int max_orphans)
113 {
114  LOCK(m_mutex);
115 
116  unsigned int nEvicted = 0;
117  static int64_t nNextSweep;
118  int64_t nNow = GetTime();
119  if (nNextSweep <= nNow) {
120  // Sweep out expired orphan pool entries:
121  int nErased = 0;
122  int64_t nMinExpTime = nNow + ORPHAN_TX_EXPIRE_TIME - ORPHAN_TX_EXPIRE_INTERVAL;
123  std::map<uint256, OrphanTx>::iterator iter = m_orphans.begin();
124  while (iter != m_orphans.end())
125  {
126  std::map<uint256, OrphanTx>::iterator maybeErase = iter++;
127  if (maybeErase->second.nTimeExpire <= nNow) {
128  nErased += _EraseTx(maybeErase->second.tx->GetHash());
129  } else {
130  nMinExpTime = std::min(maybeErase->second.nTimeExpire, nMinExpTime);
131  }
132  }
133  // Sweep again 5 minutes after the next entry that expires in order to batch the linear scan.
134  nNextSweep = nMinExpTime + ORPHAN_TX_EXPIRE_INTERVAL;
135  if (nErased > 0) LogPrint(BCLog::MEMPOOL, "Erased %d orphan tx due to expiration\n", nErased);
136  }
137  FastRandomContext rng;
138  while (m_orphans.size() > max_orphans)
139  {
140  // Evict a random orphan:
141  size_t randompos = rng.randrange(m_orphan_list.size());
142  _EraseTx(m_orphan_list[randompos]->first);
143  ++nEvicted;
144  }
145  if (nEvicted > 0) LogPrint(BCLog::MEMPOOL, "orphanage overflow, removed %u tx\n", nEvicted);
146 }
147 
149 {
150  LOCK(m_mutex);
151 
152 
153  for (unsigned int i = 0; i < tx.vout.size(); i++) {
154  const auto it_by_prev = m_outpoint_to_orphan_it.find(COutPoint(tx.GetHash(), i));
155  if (it_by_prev != m_outpoint_to_orphan_it.end()) {
156  for (const auto& elem : it_by_prev->second) {
157  // Get this source peer's work set, emplacing an empty set if it didn't exist
158  // (note: if this peer wasn't still connected, we would have removed the orphan tx already)
159  std::set<uint256>& orphan_work_set = m_peer_work_set.try_emplace(elem->second.fromPeer).first->second;
160  // Add this tx to the work set
161  orphan_work_set.insert(elem->first);
162  }
163  }
164  }
165 }
166 
167 bool TxOrphanage::HaveTx(const GenTxid& gtxid) const
168 {
169  LOCK(m_mutex);
170  if (gtxid.IsWtxid()) {
171  return m_wtxid_to_orphan_it.count(gtxid.GetHash());
172  } else {
173  return m_orphans.count(gtxid.GetHash());
174  }
175 }
176 
178 {
179  LOCK(m_mutex);
180 
181  auto work_set_it = m_peer_work_set.find(peer);
182  if (work_set_it != m_peer_work_set.end()) {
183  auto& work_set = work_set_it->second;
184  while (!work_set.empty()) {
185  uint256 txid = *work_set.begin();
186  work_set.erase(work_set.begin());
187 
188  const auto orphan_it = m_orphans.find(txid);
189  if (orphan_it != m_orphans.end()) {
190  return orphan_it->second.tx;
191  }
192  }
193  }
194  return nullptr;
195 }
196 
198 {
199  LOCK(m_mutex);
200 
201  auto work_set_it = m_peer_work_set.find(peer);
202  if (work_set_it != m_peer_work_set.end()) {
203  auto& work_set = work_set_it->second;
204  return !work_set.empty();
205  }
206  return false;
207 }
208 
210 {
211  LOCK(m_mutex);
212 
213  std::vector<uint256> vOrphanErase;
214 
215  for (const CTransactionRef& ptx : block.vtx) {
216  const CTransaction& tx = *ptx;
217 
218  // Which orphan pool entries must we evict?
219  for (const auto& txin : tx.vin) {
220  auto itByPrev = m_outpoint_to_orphan_it.find(txin.prevout);
221  if (itByPrev == m_outpoint_to_orphan_it.end()) continue;
222  for (auto mi = itByPrev->second.begin(); mi != itByPrev->second.end(); ++mi) {
223  const CTransaction& orphanTx = *(*mi)->second.tx;
224  const uint256& orphanHash = orphanTx.GetHash();
225  vOrphanErase.push_back(orphanHash);
226  }
227  }
228  }
229 
230  // Erase orphan transactions included or precluded by this block
231  if (vOrphanErase.size()) {
232  int nErased = 0;
233  for (const uint256& orphanHash : vOrphanErase) {
234  nErased += _EraseTx(orphanHash);
235  }
236  LogPrint(BCLog::MEMPOOL, "Erased %d orphan tx included or conflicted by block\n", nErased);
237  }
238 }
int ret
Definition: block.h:69
std::vector< CTransactionRef > vtx
Definition: block.h:72
An outpoint - a combination of a transaction hash and an index n into its vout.
Definition: transaction.h:36
The basic transaction that is broadcasted on the network and contained in blocks.
Definition: transaction.h:295
const std::vector< CTxOut > vout
Definition: transaction.h:306
const uint256 & GetHash() const
Definition: transaction.h:337
const std::vector< CTxIn > vin
Definition: transaction.h:305
An input of a transaction.
Definition: transaction.h:75
COutPoint prevout
Definition: transaction.h:77
Fast randomness source.
Definition: random.h:144
uint64_t randrange(uint64_t range) noexcept
Generate a random integer in the range [0..range).
Definition: random.h:202
A generic txid reference (txid or wtxid).
Definition: transaction.h:426
bool IsWtxid() const
Definition: transaction.h:434
const uint256 & GetHash() const
Definition: transaction.h:435
bool HaveTxToReconsider(NodeId peer) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
Does this peer have any work to do?
void AddChildrenToWorkSet(const CTransaction &tx) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
Add any orphans that list a particular tx as a parent into the from peer's work set.
void EraseForBlock(const CBlock &block) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
Erase all orphans included in or invalidated by a new block.
Mutex m_mutex
Guards orphan transactions.
Definition: txorphanage.h:63
bool AddTx(const CTransactionRef &tx, NodeId peer) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
Add a new orphan transaction.
Definition: txorphanage.cpp:19
bool HaveTx(const GenTxid &gtxid) const EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
Check if we already have an orphan transaction (by txid or wtxid)
CTransactionRef GetTxToReconsider(NodeId peer) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
Extract a transaction from a peer's work set Returns nullptr if there are no transactions to work on.
void EraseForPeer(NodeId peer) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
Erase all orphans announced by a peer (eg, after that peer disconnects)
Definition: txorphanage.cpp:93
int _EraseTx(const uint256 &txid) EXCLUSIVE_LOCKS_REQUIRED(m_mutex)
Erase an orphan by txid.
Definition: txorphanage.cpp:61
int EraseTx(const uint256 &txid) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
Erase an orphan by txid.
Definition: txorphanage.cpp:55
void LimitOrphans(unsigned int max_orphans) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
Limit the orphanage to the given maximum.
std::string ToString() const
Definition: uint256.cpp:55
constexpr unsigned char * begin()
Definition: uint256.h:67
256-bit opaque blob.
Definition: uint256.h:105
static int64_t GetTransactionWeight(const CTransaction &tx)
Definition: validation.h:148
#define LogPrint(category,...)
Definition: logging.h:245
@ MEMPOOL
Definition: logging.h:42
int64_t NodeId
Definition: net.h:94
static constexpr unsigned int MAX_STANDARD_TX_WEIGHT
The maximum weight for transactions we're willing to relay/mine.
Definition: policy.h:27
std::shared_ptr< const CTransaction > CTransactionRef
Definition: transaction.h:421
#define LOCK(cs)
Definition: sync.h:258
int64_t GetTime()
Definition: time.cpp:97
static constexpr int64_t ORPHAN_TX_EXPIRE_INTERVAL
Minimum time between orphan transactions expire time checks in seconds.
Definition: txorphanage.cpp:16
static constexpr int64_t ORPHAN_TX_EXPIRE_TIME
Expiration time for orphan transactions in seconds.
Definition: txorphanage.cpp:14
AssertLockHeld(pool.cs)
assert(!tx.IsCoinBase())