@@ -88,12 +88,22 @@ CAmount maxTxFee = DEFAULT_TRANSACTION_MAXFEE;
88
88
CTxMemPool mempool (::minRelayTxFee);
89
89
FeeFilterRounder filterRounder (::minRelayTxFee);
90
90
91
+ struct IteratorComparator
92
+ {
93
+ template <typename I>
94
+ bool operator ()(const I& a, const I& b)
95
+ {
96
+ return &(*a) < &(*b);
97
+ }
98
+ };
99
+
91
100
struct COrphanTx {
92
101
CTransaction tx;
93
102
NodeId fromPeer;
103
+ int64_t nTimeExpire;
94
104
};
95
105
map<uint256, COrphanTx> mapOrphanTransactions GUARDED_BY (cs_main);
96
- map<uint256 , set<uint256> > mapOrphanTransactionsByPrev GUARDED_BY (cs_main);
106
+ map<COutPoint , set<map< uint256, COrphanTx>::iterator, IteratorComparator> > mapOrphanTransactionsByPrev GUARDED_BY (cs_main);
97
107
void EraseOrphansFor (NodeId peer) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
98
108
99
109
/* *
@@ -623,40 +633,42 @@ bool AddOrphanTx(const CTransaction& tx, NodeId peer) EXCLUSIVE_LOCKS_REQUIRED(c
623
633
// large transaction with a missing parent then we assume
624
634
// it will rebroadcast it later, after the parent transaction(s)
625
635
// have been mined or received.
626
- // 10,000 orphans, each of which is at most 5,000 bytes big is
627
- // at most 500 megabytes of orphans:
636
+ // 100 orphans, each of which is at most 99,999 bytes big is
637
+ // at most 10 megabytes of orphans and somewhat more byprev index (in the worst case) :
628
638
unsigned int sz = tx.GetSerializeSize (SER_NETWORK, CTransaction::CURRENT_VERSION);
629
- if (sz > 5000 )
639
+ if (sz >= MAX_STANDARD_TX_SIZE )
630
640
{
631
641
LogPrint (" mempool" , " ignoring large orphan tx (size: %u, hash: %s)\n " , sz, hash.ToString ());
632
642
return false ;
633
643
}
634
644
635
- mapOrphanTransactions[hash].tx = tx;
636
- mapOrphanTransactions[hash].fromPeer = peer;
637
- BOOST_FOREACH (const CTxIn& txin, tx.vin )
638
- mapOrphanTransactionsByPrev[txin.prevout .hash ].insert (hash);
645
+ auto ret = mapOrphanTransactions.emplace (hash, COrphanTx{tx, peer, GetTime () + ORPHAN_TX_EXPIRE_TIME});
646
+ assert (ret.second );
647
+ BOOST_FOREACH (const CTxIn& txin, tx.vin ) {
648
+ mapOrphanTransactionsByPrev[txin.prevout ].insert (ret.first );
649
+ }
639
650
640
- LogPrint (" mempool" , " stored orphan tx %s (mapsz %u prevsz %u)\n " , hash.ToString (),
651
+ LogPrint (" mempool" , " stored orphan tx %s (mapsz %u outsz %u)\n " , hash.ToString (),
641
652
mapOrphanTransactions.size (), mapOrphanTransactionsByPrev.size ());
642
653
return true ;
643
654
}
644
655
645
- void static EraseOrphanTx (uint256 hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
656
+ int static EraseOrphanTx (uint256 hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
646
657
{
647
658
map<uint256, COrphanTx>::iterator it = mapOrphanTransactions.find (hash);
648
659
if (it == mapOrphanTransactions.end ())
649
- return ;
660
+ return 0 ;
650
661
BOOST_FOREACH (const CTxIn& txin, it->second .tx .vin )
651
662
{
652
- map<uint256, set<uint256> >::iterator itPrev = mapOrphanTransactionsByPrev.find (txin.prevout . hash );
663
+ auto itPrev = mapOrphanTransactionsByPrev.find (txin.prevout );
653
664
if (itPrev == mapOrphanTransactionsByPrev.end ())
654
665
continue ;
655
- itPrev->second .erase (hash );
666
+ itPrev->second .erase (it );
656
667
if (itPrev->second .empty ())
657
668
mapOrphanTransactionsByPrev.erase (itPrev);
658
669
}
659
670
mapOrphanTransactions.erase (it);
671
+ return 1 ;
660
672
}
661
673
662
674
void EraseOrphansFor (NodeId peer)
@@ -668,8 +680,7 @@ void EraseOrphansFor(NodeId peer)
668
680
map<uint256, COrphanTx>::iterator maybeErase = iter++; // increment to avoid iterator becoming invalid
669
681
if (maybeErase->second .fromPeer == peer)
670
682
{
671
- EraseOrphanTx (maybeErase->second .tx .GetHash ());
672
- ++nErased;
683
+ nErased += EraseOrphanTx (maybeErase->second .tx .GetHash ());
673
684
}
674
685
}
675
686
if (nErased > 0 ) LogPrint (" mempool" , " Erased %d orphan tx from peer %d\n " , nErased, peer);
@@ -679,6 +690,26 @@ void EraseOrphansFor(NodeId peer)
679
690
unsigned int LimitOrphanTxSize (unsigned int nMaxOrphans) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
680
691
{
681
692
unsigned int nEvicted = 0 ;
693
+ static int64_t nNextSweep;
694
+ int64_t nNow = GetTime ();
695
+ if (nNextSweep <= nNow) {
696
+ // Sweep out expired orphan pool entries:
697
+ int nErased = 0 ;
698
+ int64_t nMinExpTime = nNow + ORPHAN_TX_EXPIRE_TIME - ORPHAN_TX_EXPIRE_INTERVAL;
699
+ map<uint256, COrphanTx>::iterator iter = mapOrphanTransactions.begin ();
700
+ while (iter != mapOrphanTransactions.end ())
701
+ {
702
+ map<uint256, COrphanTx>::iterator maybeErase = iter++;
703
+ if (maybeErase->second .nTimeExpire <= nNow) {
704
+ nErased += EraseOrphanTx (maybeErase->second .tx .GetHash ());
705
+ } else {
706
+ nMinExpTime = std::min (maybeErase->second .nTimeExpire , nMinExpTime);
707
+ }
708
+ }
709
+ // Sweep again 5 minutes after the next entry that expires in order to batch the linear scan.
710
+ nNextSweep = nMinExpTime + ORPHAN_TX_EXPIRE_INTERVAL;
711
+ if (nErased > 0 ) LogPrint (" mempool" , " Erased %d orphan tx due to expiration\n " , nErased);
712
+ }
682
713
while (mapOrphanTransactions.size () > nMaxOrphans)
683
714
{
684
715
// Evict a random orphan:
@@ -2335,6 +2366,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
2335
2366
2336
2367
CCheckQueueControl<CScriptCheck> control (fScriptChecks && nScriptCheckThreads ? &scriptcheckqueue : NULL );
2337
2368
2369
+ std::vector<uint256> vOrphanErase;
2338
2370
std::vector<int > prevheights;
2339
2371
CAmount nFees = 0 ;
2340
2372
int nInputs = 0 ;
@@ -2367,6 +2399,17 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
2367
2399
prevheights[j] = view.AccessCoins (tx.vin [j].prevout .hash )->nHeight ;
2368
2400
}
2369
2401
2402
+ // Which orphan pool entries must we evict?
2403
+ for (size_t j = 0 ; j < tx.vin .size (); j++) {
2404
+ auto itByPrev = mapOrphanTransactionsByPrev.find (tx.vin [j].prevout );
2405
+ if (itByPrev == mapOrphanTransactionsByPrev.end ()) continue ;
2406
+ for (auto mi = itByPrev->second .begin (); mi != itByPrev->second .end (); ++mi) {
2407
+ const CTransaction& orphanTx = (*mi)->second .tx ;
2408
+ const uint256& orphanHash = orphanTx.GetHash ();
2409
+ vOrphanErase.push_back (orphanHash);
2410
+ }
2411
+ }
2412
+
2370
2413
if (!SequenceLocks (tx, nLockTimeFlags, &prevheights, *pindex)) {
2371
2414
return state.DoS (100 , error (" %s: contains a non-BIP68-final transaction" , __func__),
2372
2415
REJECT_INVALID, " bad-txns-nonfinal" );
@@ -2454,6 +2497,15 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
2454
2497
GetMainSignals ().UpdatedTransaction (hashPrevBestCoinBase);
2455
2498
hashPrevBestCoinBase = block.vtx [0 ].GetHash ();
2456
2499
2500
+ // Erase orphan transactions include or precluded by this block
2501
+ if (vOrphanErase.size ()) {
2502
+ int nErased = 0 ;
2503
+ BOOST_FOREACH (uint256 &orphanHash, vOrphanErase) {
2504
+ nErased += EraseOrphanTx (orphanHash);
2505
+ }
2506
+ LogPrint (" mempool" , " Erased %d orphan tx included or conflicted by block\n " , nErased);
2507
+ }
2508
+
2457
2509
int64_t nTime6 = GetTimeMicros (); nTimeCallbacks += nTime6 - nTime5;
2458
2510
LogPrint (" bench" , " - Callbacks: %.2fms [%.2fs]\n " , 0.001 * (nTime6 - nTime5), nTimeCallbacks * 0.000001 );
2459
2511
@@ -5041,7 +5093,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
5041
5093
return true ;
5042
5094
}
5043
5095
5044
- vector<uint256 > vWorkQueue;
5096
+ deque<COutPoint > vWorkQueue;
5045
5097
vector<uint256> vEraseQueue;
5046
5098
CTransaction tx;
5047
5099
vRecv >> tx;
@@ -5060,7 +5112,9 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
5060
5112
if (!AlreadyHave (inv) && AcceptToMemoryPool (mempool, state, tx, true , &fMissingInputs )) {
5061
5113
mempool.check (pcoinsTip);
5062
5114
RelayTransaction (tx);
5063
- vWorkQueue.push_back (inv.hash );
5115
+ for (unsigned int i = 0 ; i < tx.vout .size (); i++) {
5116
+ vWorkQueue.emplace_back (inv.hash , i);
5117
+ }
5064
5118
5065
5119
pfrom->nLastTXTime = GetTime ();
5066
5120
@@ -5071,18 +5125,18 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
5071
5125
5072
5126
// Recursively process any orphan transactions that depended on this one
5073
5127
set<NodeId> setMisbehaving;
5074
- for ( unsigned int i = 0 ; i < vWorkQueue.size (); i++)
5075
- {
5076
- map<uint256, set<uint256> >::iterator itByPrev = mapOrphanTransactionsByPrev. find (vWorkQueue[i] );
5128
+ while (! vWorkQueue.empty ()) {
5129
+ auto itByPrev = mapOrphanTransactionsByPrev. find (vWorkQueue. front ());
5130
+ vWorkQueue. pop_front ( );
5077
5131
if (itByPrev == mapOrphanTransactionsByPrev.end ())
5078
5132
continue ;
5079
- for (set<uint256>::iterator mi = itByPrev->second .begin ();
5133
+ for (auto mi = itByPrev->second .begin ();
5080
5134
mi != itByPrev->second .end ();
5081
5135
++mi)
5082
5136
{
5083
- const uint256& orphanHash = *mi;
5084
- const CTransaction& orphanTx = mapOrphanTransactions[orphanHash]. tx ;
5085
- NodeId fromPeer = mapOrphanTransactions[orphanHash] .fromPeer ;
5137
+ const CTransaction& orphanTx = ( *mi)-> second . tx ;
5138
+ const uint256& orphanHash = orphanTx. GetHash () ;
5139
+ NodeId fromPeer = (*mi)-> second .fromPeer ;
5086
5140
bool fMissingInputs2 = false ;
5087
5141
// Use a dummy CValidationState so someone can't setup nodes to counter-DoS based on orphan
5088
5142
// resolution (that is, feeding people an invalid transaction based on LegitTxX in order to get
@@ -5095,7 +5149,9 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
5095
5149
if (AcceptToMemoryPool (mempool, stateDummy, orphanTx, true , &fMissingInputs2 )) {
5096
5150
LogPrint (" mempool" , " accepted orphan tx %s\n " , orphanHash.ToString ());
5097
5151
RelayTransaction (orphanTx);
5098
- vWorkQueue.push_back (orphanHash);
5152
+ for (unsigned int i = 0 ; i < orphanTx.vout .size (); i++) {
5153
+ vWorkQueue.emplace_back (orphanHash, i);
5154
+ }
5099
5155
vEraseQueue.push_back (orphanHash);
5100
5156
}
5101
5157
else if (!fMissingInputs2 )
@@ -5124,13 +5180,29 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
5124
5180
}
5125
5181
else if (fMissingInputs )
5126
5182
{
5127
- AddOrphanTx (tx, pfrom->GetId ());
5183
+ bool fRejectedParents = false ; // It may be the case that the orphans parents have all been rejected
5184
+ BOOST_FOREACH (const CTxIn& txin, tx.vin ) {
5185
+ if (recentRejects->contains (txin.prevout .hash )) {
5186
+ fRejectedParents = true ;
5187
+ break ;
5188
+ }
5189
+ }
5190
+ if (!fRejectedParents ) {
5191
+ BOOST_FOREACH (const CTxIn& txin, tx.vin ) {
5192
+ CInv inv (MSG_TX, txin.prevout .hash );
5193
+ pfrom->AddInventoryKnown (inv);
5194
+ if (!AlreadyHave (inv)) pfrom->AskFor (inv);
5195
+ }
5196
+ AddOrphanTx (tx, pfrom->GetId ());
5128
5197
5129
- // DoS prevention: do not allow mapOrphanTransactions to grow unbounded
5130
- unsigned int nMaxOrphanTx = (unsigned int )std::max ((int64_t )0 , GetArg (" -maxorphantx" , DEFAULT_MAX_ORPHAN_TRANSACTIONS));
5131
- unsigned int nEvicted = LimitOrphanTxSize (nMaxOrphanTx);
5132
- if (nEvicted > 0 )
5133
- LogPrint (" mempool" , " mapOrphan overflow, removed %u tx\n " , nEvicted);
5198
+ // DoS prevention: do not allow mapOrphanTransactions to grow unbounded
5199
+ unsigned int nMaxOrphanTx = (unsigned int )std::max ((int64_t )0 , GetArg (" -maxorphantx" , DEFAULT_MAX_ORPHAN_TRANSACTIONS));
5200
+ unsigned int nEvicted = LimitOrphanTxSize (nMaxOrphanTx);
5201
+ if (nEvicted > 0 )
5202
+ LogPrint (" mempool" , " mapOrphan overflow, removed %u tx\n " , nEvicted);
5203
+ } else {
5204
+ LogPrint (" mempool" , " not keeping orphan with rejected parents %s\n " ,tx.GetHash ().ToString ());
5205
+ }
5134
5206
} else {
5135
5207
assert (recentRejects);
5136
5208
recentRejects->insert (tx.GetHash ());
0 commit comments