txorphanage.cpp
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 #include <primitives/transaction.h> 11 #include <util/time.h> 12 13 #include <cassert> 14 15 bool TxOrphanage::AddTx(const CTransactionRef& tx, NodeId peer) 16 { 17 const Txid& hash = tx->GetHash(); 18 const Wtxid& wtxid = tx->GetWitnessHash(); 19 if (auto it{m_orphans.find(wtxid)}; it != m_orphans.end()) { 20 AddAnnouncer(wtxid, peer); 21 // No new orphan entry was created. An announcer may have been added. 22 return false; 23 } 24 25 // Ignore big transactions, to avoid a 26 // send-big-orphans memory exhaustion attack. If a peer has a legitimate 27 // large transaction with a missing parent then we assume 28 // it will rebroadcast it later, after the parent transaction(s) 29 // have been mined or received. 30 // 100 orphans, each of which is at most 100,000 bytes big is 31 // at most 10 megabytes of orphans and somewhat more byprev index (in the worst case): 32 unsigned int sz = GetTransactionWeight(*tx); 33 if (sz > MAX_STANDARD_TX_WEIGHT) 34 { 35 LogDebug(BCLog::TXPACKAGES, "ignoring large orphan tx (size: %u, txid: %s, wtxid: %s)\n", sz, hash.ToString(), wtxid.ToString()); 36 return false; 37 } 38 39 auto ret = m_orphans.emplace(wtxid, OrphanTx{{tx, {peer}, Now<NodeSeconds>() + ORPHAN_TX_EXPIRE_TIME}, m_orphan_list.size()}); 40 assert(ret.second); 41 m_orphan_list.push_back(ret.first); 42 for (const CTxIn& txin : tx->vin) { 43 m_outpoint_to_orphan_it[txin.prevout].insert(ret.first); 44 } 45 m_total_orphan_usage += sz; 46 m_total_announcements += 1; 47 auto& peer_info = m_peer_orphanage_info.try_emplace(peer).first->second; 48 peer_info.m_total_usage += sz; 49 50 LogDebug(BCLog::TXPACKAGES, "stored orphan tx %s (wtxid=%s), weight: %u (mapsz %u outsz %u)\n", hash.ToString(), wtxid.ToString(), sz, 51 m_orphans.size(), m_outpoint_to_orphan_it.size()); 52 return true; 53 } 54 55 bool TxOrphanage::AddAnnouncer(const Wtxid& wtxid, NodeId peer) 56 { 57 const auto it = m_orphans.find(wtxid); 58 if (it != m_orphans.end()) { 59 Assume(!it->second.announcers.empty()); 60 const auto ret = it->second.announcers.insert(peer); 61 if (ret.second) { 62 auto& peer_info = m_peer_orphanage_info.try_emplace(peer).first->second; 63 peer_info.m_total_usage += it->second.GetUsage(); 64 m_total_announcements += 1; 65 LogDebug(BCLog::TXPACKAGES, "added peer=%d as announcer of orphan tx %s\n", peer, wtxid.ToString()); 66 return true; 67 } 68 } 69 return false; 70 } 71 72 int TxOrphanage::EraseTx(const Wtxid& wtxid) 73 { 74 std::map<Wtxid, OrphanTx>::iterator it = m_orphans.find(wtxid); 75 if (it == m_orphans.end()) 76 return 0; 77 for (const CTxIn& txin : it->second.tx->vin) 78 { 79 auto itPrev = m_outpoint_to_orphan_it.find(txin.prevout); 80 if (itPrev == m_outpoint_to_orphan_it.end()) 81 continue; 82 itPrev->second.erase(it); 83 if (itPrev->second.empty()) 84 m_outpoint_to_orphan_it.erase(itPrev); 85 } 86 87 const auto tx_size{it->second.GetUsage()}; 88 m_total_orphan_usage -= tx_size; 89 m_total_announcements -= it->second.announcers.size(); 90 // Decrement each announcer's m_total_usage 91 for (const auto& peer : it->second.announcers) { 92 auto peer_it = m_peer_orphanage_info.find(peer); 93 if (Assume(peer_it != m_peer_orphanage_info.end())) { 94 peer_it->second.m_total_usage -= tx_size; 95 } 96 } 97 98 size_t old_pos = it->second.list_pos; 99 assert(m_orphan_list[old_pos] == it); 100 if (old_pos + 1 != m_orphan_list.size()) { 101 // Unless we're deleting the last entry in m_orphan_list, move the last 102 // entry to the position we're deleting. 103 auto it_last = m_orphan_list.back(); 104 m_orphan_list[old_pos] = it_last; 105 it_last->second.list_pos = old_pos; 106 } 107 const auto& txid = it->second.tx->GetHash(); 108 // Time spent in orphanage = difference between current and entry time. 109 // Entry time is equal to ORPHAN_TX_EXPIRE_TIME earlier than entry's expiry. 110 LogDebug(BCLog::TXPACKAGES, " removed orphan tx %s (wtxid=%s) after %ds\n", txid.ToString(), wtxid.ToString(), 111 Ticks<std::chrono::seconds>(NodeClock::now() + ORPHAN_TX_EXPIRE_TIME - it->second.nTimeExpire)); 112 m_orphan_list.pop_back(); 113 114 m_orphans.erase(it); 115 return 1; 116 } 117 118 void TxOrphanage::EraseForPeer(NodeId peer) 119 { 120 // Zeroes out this peer's m_total_usage. 121 m_peer_orphanage_info.erase(peer); 122 123 int nErased = 0; 124 std::map<Wtxid, OrphanTx>::iterator iter = m_orphans.begin(); 125 while (iter != m_orphans.end()) 126 { 127 // increment to avoid iterator becoming invalid after erasure 128 auto& [wtxid, orphan] = *iter++; 129 auto orphan_it = orphan.announcers.find(peer); 130 if (orphan_it != orphan.announcers.end()) { 131 orphan.announcers.erase(peer); 132 m_total_announcements -= 1; 133 134 // No remaining announcers: clean up entry 135 if (orphan.announcers.empty()) { 136 nErased += EraseTx(orphan.tx->GetWitnessHash()); 137 } 138 } 139 } 140 if (nErased > 0) LogDebug(BCLog::TXPACKAGES, "Erased %d orphan transaction(s) from peer=%d\n", nErased, peer); 141 } 142 143 void TxOrphanage::LimitOrphans(unsigned int max_orphans, FastRandomContext& rng) 144 { 145 unsigned int nEvicted = 0; 146 auto nNow{Now<NodeSeconds>()}; 147 if (m_next_sweep <= nNow) { 148 // Sweep out expired orphan pool entries: 149 int nErased = 0; 150 auto nMinExpTime{nNow + ORPHAN_TX_EXPIRE_TIME - ORPHAN_TX_EXPIRE_INTERVAL}; 151 std::map<Wtxid, OrphanTx>::iterator iter = m_orphans.begin(); 152 while (iter != m_orphans.end()) 153 { 154 std::map<Wtxid, OrphanTx>::iterator maybeErase = iter++; 155 if (maybeErase->second.nTimeExpire <= nNow) { 156 nErased += EraseTx(maybeErase->first); 157 } else { 158 nMinExpTime = std::min(maybeErase->second.nTimeExpire, nMinExpTime); 159 } 160 } 161 // Sweep again 5 minutes after the next entry that expires in order to batch the linear scan. 162 m_next_sweep = nMinExpTime + ORPHAN_TX_EXPIRE_INTERVAL; 163 if (nErased > 0) LogDebug(BCLog::TXPACKAGES, "Erased %d orphan tx due to expiration\n", nErased); 164 } 165 while (m_orphans.size() > max_orphans) 166 { 167 // Evict a random orphan: 168 size_t randompos = rng.randrange(m_orphan_list.size()); 169 EraseTx(m_orphan_list[randompos]->first); 170 ++nEvicted; 171 } 172 if (nEvicted > 0) LogDebug(BCLog::TXPACKAGES, "orphanage overflow, removed %u tx\n", nEvicted); 173 } 174 175 void TxOrphanage::AddChildrenToWorkSet(const CTransaction& tx, FastRandomContext& rng) 176 { 177 for (unsigned int i = 0; i < tx.vout.size(); i++) { 178 const auto it_by_prev = m_outpoint_to_orphan_it.find(COutPoint(tx.GetHash(), i)); 179 if (it_by_prev != m_outpoint_to_orphan_it.end()) { 180 for (const auto& elem : it_by_prev->second) { 181 // Belt and suspenders, each orphan should always have at least 1 announcer. 182 if (!Assume(!elem->second.announcers.empty())) continue; 183 184 // Select a random peer to assign orphan processing, reducing wasted work if the orphan is still missing 185 // inputs. However, we don't want to create an issue in which the assigned peer can purposefully stop us 186 // from processing the orphan by disconnecting. 187 auto announcer_iter = std::begin(elem->second.announcers); 188 std::advance(announcer_iter, rng.randrange(elem->second.announcers.size())); 189 auto announcer = *(announcer_iter); 190 191 // Get this source peer's work set, emplacing an empty set if it didn't exist 192 // (note: if this peer wasn't still connected, we would have removed the orphan tx already) 193 std::set<Wtxid>& orphan_work_set = m_peer_orphanage_info.try_emplace(announcer).first->second.m_work_set; 194 // Add this tx to the work set 195 orphan_work_set.insert(elem->first); 196 LogDebug(BCLog::TXPACKAGES, "added %s (wtxid=%s) to peer %d workset\n", 197 tx.GetHash().ToString(), tx.GetWitnessHash().ToString(), announcer); 198 } 199 } 200 } 201 } 202 203 bool TxOrphanage::HaveTx(const Wtxid& wtxid) const 204 { 205 return m_orphans.count(wtxid); 206 } 207 208 CTransactionRef TxOrphanage::GetTx(const Wtxid& wtxid) const 209 { 210 auto it = m_orphans.find(wtxid); 211 return it != m_orphans.end() ? it->second.tx : nullptr; 212 } 213 214 bool TxOrphanage::HaveTxFromPeer(const Wtxid& wtxid, NodeId peer) const 215 { 216 auto it = m_orphans.find(wtxid); 217 return (it != m_orphans.end() && it->second.announcers.contains(peer)); 218 } 219 220 CTransactionRef TxOrphanage::GetTxToReconsider(NodeId peer) 221 { 222 auto peer_it = m_peer_orphanage_info.find(peer); 223 if (peer_it == m_peer_orphanage_info.end()) return nullptr; 224 225 auto& work_set = peer_it->second.m_work_set; 226 while (!work_set.empty()) { 227 Wtxid wtxid = *work_set.begin(); 228 work_set.erase(work_set.begin()); 229 230 const auto orphan_it = m_orphans.find(wtxid); 231 if (orphan_it != m_orphans.end()) { 232 return orphan_it->second.tx; 233 } 234 } 235 return nullptr; 236 } 237 238 bool TxOrphanage::HaveTxToReconsider(NodeId peer) 239 { 240 auto peer_it = m_peer_orphanage_info.find(peer); 241 if (peer_it == m_peer_orphanage_info.end()) return false; 242 243 auto& work_set = peer_it->second.m_work_set; 244 return !work_set.empty(); 245 } 246 247 void TxOrphanage::EraseForBlock(const CBlock& block) 248 { 249 std::vector<Wtxid> vOrphanErase; 250 251 for (const CTransactionRef& ptx : block.vtx) { 252 const CTransaction& tx = *ptx; 253 254 // Which orphan pool entries must we evict? 255 for (const auto& txin : tx.vin) { 256 auto itByPrev = m_outpoint_to_orphan_it.find(txin.prevout); 257 if (itByPrev == m_outpoint_to_orphan_it.end()) continue; 258 for (auto mi = itByPrev->second.begin(); mi != itByPrev->second.end(); ++mi) { 259 const CTransaction& orphanTx = *(*mi)->second.tx; 260 vOrphanErase.push_back(orphanTx.GetWitnessHash()); 261 } 262 } 263 } 264 265 // Erase orphan transactions included or precluded by this block 266 if (vOrphanErase.size()) { 267 int nErased = 0; 268 for (const auto& orphanHash : vOrphanErase) { 269 nErased += EraseTx(orphanHash); 270 } 271 LogDebug(BCLog::TXPACKAGES, "Erased %d orphan transaction(s) included or conflicted by block\n", nErased); 272 } 273 } 274 275 std::vector<CTransactionRef> TxOrphanage::GetChildrenFromSamePeer(const CTransactionRef& parent, NodeId nodeid) const 276 { 277 // First construct a vector of iterators to ensure we do not return duplicates of the same tx 278 // and so we can sort by nTimeExpire. 279 std::vector<OrphanMap::iterator> iters; 280 281 // For each output, get all entries spending this prevout, filtering for ones from the specified peer. 282 for (unsigned int i = 0; i < parent->vout.size(); i++) { 283 const auto it_by_prev = m_outpoint_to_orphan_it.find(COutPoint(parent->GetHash(), i)); 284 if (it_by_prev != m_outpoint_to_orphan_it.end()) { 285 for (const auto& elem : it_by_prev->second) { 286 if (elem->second.announcers.contains(nodeid)) { 287 iters.emplace_back(elem); 288 } 289 } 290 } 291 } 292 293 // Sort by address so that duplicates can be deleted. At the same time, sort so that more recent 294 // orphans (which expire later) come first. Break ties based on address, as nTimeExpire is 295 // quantified in seconds and it is possible for orphans to have the same expiry. 296 std::sort(iters.begin(), iters.end(), [](const auto& lhs, const auto& rhs) { 297 if (lhs->second.nTimeExpire == rhs->second.nTimeExpire) { 298 return &(*lhs) < &(*rhs); 299 } else { 300 return lhs->second.nTimeExpire > rhs->second.nTimeExpire; 301 } 302 }); 303 // Erase duplicates 304 iters.erase(std::unique(iters.begin(), iters.end()), iters.end()); 305 306 // Convert to a vector of CTransactionRef 307 std::vector<CTransactionRef> children_found; 308 children_found.reserve(iters.size()); 309 for (const auto& child_iter : iters) { 310 children_found.emplace_back(child_iter->second.tx); 311 } 312 return children_found; 313 } 314 315 std::vector<TxOrphanage::OrphanTxBase> TxOrphanage::GetOrphanTransactions() const 316 { 317 std::vector<OrphanTxBase> ret; 318 ret.reserve(m_orphans.size()); 319 for (auto const& o : m_orphans) { 320 ret.push_back({o.second.tx, o.second.announcers, o.second.nTimeExpire}); 321 } 322 return ret; 323 } 324 325 void TxOrphanage::SanityCheck() const 326 { 327 // Check that cached m_total_announcements is correct 328 unsigned int counted_total_announcements{0}; 329 // Check that m_total_orphan_usage is correct 330 unsigned int counted_total_usage{0}; 331 332 // Check that cached PeerOrphanInfo::m_total_size is correct 333 std::map<NodeId, unsigned int> counted_size_per_peer; 334 335 for (const auto& [wtxid, orphan] : m_orphans) { 336 counted_total_announcements += orphan.announcers.size(); 337 counted_total_usage += orphan.GetUsage(); 338 339 Assume(!orphan.announcers.empty()); 340 for (const auto& peer : orphan.announcers) { 341 auto& count_peer_entry = counted_size_per_peer.try_emplace(peer).first->second; 342 count_peer_entry += orphan.GetUsage(); 343 } 344 } 345 346 Assume(m_total_announcements >= m_orphans.size()); 347 Assume(counted_total_announcements == m_total_announcements); 348 Assume(counted_total_usage == m_total_orphan_usage); 349 350 // There must be an entry in m_peer_orphanage_info for each peer 351 // However, there may be m_peer_orphanage_info entries corresponding to peers for whom we 352 // previously had orphans but no longer do. 353 Assume(counted_size_per_peer.size() <= m_peer_orphanage_info.size()); 354 355 for (const auto& [peerid, info] : m_peer_orphanage_info) { 356 auto it_counted = counted_size_per_peer.find(peerid); 357 if (it_counted == counted_size_per_peer.end()) { 358 Assume(info.m_total_usage == 0); 359 } else { 360 Assume(it_counted->second == info.m_total_usage); 361 } 362 } 363 }