|
|
@ -88,12 +88,21 @@ CAmount maxTxFee = DEFAULT_TRANSACTION_MAXFEE; |
|
|
|
CTxMemPool mempool(::minRelayTxFee); |
|
|
|
CTxMemPool mempool(::minRelayTxFee); |
|
|
|
FeeFilterRounder filterRounder(::minRelayTxFee); |
|
|
|
FeeFilterRounder filterRounder(::minRelayTxFee); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
struct IteratorComparator |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
template<typename I> |
|
|
|
|
|
|
|
bool operator()(const I& a, const I& b) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
return &(*a) < &(*b); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
struct COrphanTx { |
|
|
|
struct COrphanTx { |
|
|
|
CTransaction tx; |
|
|
|
CTransaction tx; |
|
|
|
NodeId fromPeer; |
|
|
|
NodeId fromPeer; |
|
|
|
}; |
|
|
|
}; |
|
|
|
map<uint256, COrphanTx> mapOrphanTransactions GUARDED_BY(cs_main); |
|
|
|
map<uint256, COrphanTx> mapOrphanTransactions GUARDED_BY(cs_main); |
|
|
|
map<uint256, set<uint256> > mapOrphanTransactionsByPrev GUARDED_BY(cs_main); |
|
|
|
map<COutPoint, set<map<uint256, COrphanTx>::iterator, IteratorComparator>> mapOrphanTransactionsByPrev GUARDED_BY(cs_main); |
|
|
|
void EraseOrphansFor(NodeId peer) EXCLUSIVE_LOCKS_REQUIRED(cs_main); |
|
|
|
void EraseOrphansFor(NodeId peer) EXCLUSIVE_LOCKS_REQUIRED(cs_main); |
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
/**
|
|
|
@ -632,31 +641,33 @@ bool AddOrphanTx(const CTransaction& tx, NodeId peer) EXCLUSIVE_LOCKS_REQUIRED(c |
|
|
|
return false; |
|
|
|
return false; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
mapOrphanTransactions[hash].tx = tx; |
|
|
|
auto ret = mapOrphanTransactions.emplace(hash, COrphanTx{tx, peer}); |
|
|
|
mapOrphanTransactions[hash].fromPeer = peer; |
|
|
|
assert(ret.second); |
|
|
|
BOOST_FOREACH(const CTxIn& txin, tx.vin) |
|
|
|
BOOST_FOREACH(const CTxIn& txin, tx.vin) { |
|
|
|
mapOrphanTransactionsByPrev[txin.prevout.hash].insert(hash); |
|
|
|
mapOrphanTransactionsByPrev[txin.prevout].insert(ret.first); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
LogPrint("mempool", "stored orphan tx %s (mapsz %u prevsz %u)\n", hash.ToString(), |
|
|
|
LogPrint("mempool", "stored orphan tx %s (mapsz %u outsz %u)\n", hash.ToString(), |
|
|
|
mapOrphanTransactions.size(), mapOrphanTransactionsByPrev.size()); |
|
|
|
mapOrphanTransactions.size(), mapOrphanTransactionsByPrev.size()); |
|
|
|
return true; |
|
|
|
return true; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void static EraseOrphanTx(uint256 hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main) |
|
|
|
int static EraseOrphanTx(uint256 hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main) |
|
|
|
{ |
|
|
|
{ |
|
|
|
map<uint256, COrphanTx>::iterator it = mapOrphanTransactions.find(hash); |
|
|
|
map<uint256, COrphanTx>::iterator it = mapOrphanTransactions.find(hash); |
|
|
|
if (it == mapOrphanTransactions.end()) |
|
|
|
if (it == mapOrphanTransactions.end()) |
|
|
|
return; |
|
|
|
return 0; |
|
|
|
BOOST_FOREACH(const CTxIn& txin, it->second.tx.vin) |
|
|
|
BOOST_FOREACH(const CTxIn& txin, it->second.tx.vin) |
|
|
|
{ |
|
|
|
{ |
|
|
|
map<uint256, set<uint256> >::iterator itPrev = mapOrphanTransactionsByPrev.find(txin.prevout.hash); |
|
|
|
auto itPrev = mapOrphanTransactionsByPrev.find(txin.prevout); |
|
|
|
if (itPrev == mapOrphanTransactionsByPrev.end()) |
|
|
|
if (itPrev == mapOrphanTransactionsByPrev.end()) |
|
|
|
continue; |
|
|
|
continue; |
|
|
|
itPrev->second.erase(hash); |
|
|
|
itPrev->second.erase(it); |
|
|
|
if (itPrev->second.empty()) |
|
|
|
if (itPrev->second.empty()) |
|
|
|
mapOrphanTransactionsByPrev.erase(itPrev); |
|
|
|
mapOrphanTransactionsByPrev.erase(itPrev); |
|
|
|
} |
|
|
|
} |
|
|
|
mapOrphanTransactions.erase(it); |
|
|
|
mapOrphanTransactions.erase(it); |
|
|
|
|
|
|
|
return 1; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void EraseOrphansFor(NodeId peer) |
|
|
|
void EraseOrphansFor(NodeId peer) |
|
|
@ -668,8 +679,7 @@ void EraseOrphansFor(NodeId peer) |
|
|
|
map<uint256, COrphanTx>::iterator maybeErase = iter++; // increment to avoid iterator becoming invalid
|
|
|
|
map<uint256, COrphanTx>::iterator maybeErase = iter++; // increment to avoid iterator becoming invalid
|
|
|
|
if (maybeErase->second.fromPeer == peer) |
|
|
|
if (maybeErase->second.fromPeer == peer) |
|
|
|
{ |
|
|
|
{ |
|
|
|
EraseOrphanTx(maybeErase->second.tx.GetHash()); |
|
|
|
nErased += EraseOrphanTx(maybeErase->second.tx.GetHash()); |
|
|
|
++nErased; |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
if (nErased > 0) LogPrint("mempool", "Erased %d orphan tx from peer %d\n", nErased, peer); |
|
|
|
if (nErased > 0) LogPrint("mempool", "Erased %d orphan tx from peer %d\n", nErased, peer); |
|
|
@ -5019,7 +5029,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, |
|
|
|
return true; |
|
|
|
return true; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
vector<uint256> vWorkQueue; |
|
|
|
deque<COutPoint> vWorkQueue; |
|
|
|
vector<uint256> vEraseQueue; |
|
|
|
vector<uint256> vEraseQueue; |
|
|
|
CTransaction tx; |
|
|
|
CTransaction tx; |
|
|
|
vRecv >> tx; |
|
|
|
vRecv >> tx; |
|
|
@ -5038,7 +5048,9 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, |
|
|
|
if (!AlreadyHave(inv) && AcceptToMemoryPool(mempool, state, tx, true, &fMissingInputs)) { |
|
|
|
if (!AlreadyHave(inv) && AcceptToMemoryPool(mempool, state, tx, true, &fMissingInputs)) { |
|
|
|
mempool.check(pcoinsTip); |
|
|
|
mempool.check(pcoinsTip); |
|
|
|
RelayTransaction(tx); |
|
|
|
RelayTransaction(tx); |
|
|
|
vWorkQueue.push_back(inv.hash); |
|
|
|
for (unsigned int i = 0; i < tx.vout.size(); i++) { |
|
|
|
|
|
|
|
vWorkQueue.emplace_back(inv.hash, i); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
LogPrint("mempool", "AcceptToMemoryPool: peer=%d: accepted %s (poolsz %u txn, %u kB)\n", |
|
|
|
LogPrint("mempool", "AcceptToMemoryPool: peer=%d: accepted %s (poolsz %u txn, %u kB)\n", |
|
|
|
pfrom->id, |
|
|
|
pfrom->id, |
|
|
@ -5047,18 +5059,18 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, |
|
|
|
|
|
|
|
|
|
|
|
// Recursively process any orphan transactions that depended on this one
|
|
|
|
// Recursively process any orphan transactions that depended on this one
|
|
|
|
set<NodeId> setMisbehaving; |
|
|
|
set<NodeId> setMisbehaving; |
|
|
|
for (unsigned int i = 0; i < vWorkQueue.size(); i++) |
|
|
|
while (!vWorkQueue.empty()) { |
|
|
|
{ |
|
|
|
auto itByPrev = mapOrphanTransactionsByPrev.find(vWorkQueue.front()); |
|
|
|
map<uint256, set<uint256> >::iterator itByPrev = mapOrphanTransactionsByPrev.find(vWorkQueue[i]); |
|
|
|
vWorkQueue.pop_front(); |
|
|
|
if (itByPrev == mapOrphanTransactionsByPrev.end()) |
|
|
|
if (itByPrev == mapOrphanTransactionsByPrev.end()) |
|
|
|
continue; |
|
|
|
continue; |
|
|
|
for (set<uint256>::iterator mi = itByPrev->second.begin(); |
|
|
|
for (auto mi = itByPrev->second.begin(); |
|
|
|
mi != itByPrev->second.end(); |
|
|
|
mi != itByPrev->second.end(); |
|
|
|
++mi) |
|
|
|
++mi) |
|
|
|
{ |
|
|
|
{ |
|
|
|
const uint256& orphanHash = *mi; |
|
|
|
const CTransaction& orphanTx = (*mi)->second.tx; |
|
|
|
const CTransaction& orphanTx = mapOrphanTransactions[orphanHash].tx; |
|
|
|
const uint256& orphanHash = orphanTx.GetHash(); |
|
|
|
NodeId fromPeer = mapOrphanTransactions[orphanHash].fromPeer; |
|
|
|
NodeId fromPeer = (*mi)->second.fromPeer; |
|
|
|
bool fMissingInputs2 = false; |
|
|
|
bool fMissingInputs2 = false; |
|
|
|
// Use a dummy CValidationState so someone can't setup nodes to counter-DoS based on orphan
|
|
|
|
// Use a dummy CValidationState so someone can't setup nodes to counter-DoS based on orphan
|
|
|
|
// resolution (that is, feeding people an invalid transaction based on LegitTxX in order to get
|
|
|
|
// resolution (that is, feeding people an invalid transaction based on LegitTxX in order to get
|
|
|
@ -5071,7 +5083,9 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, |
|
|
|
if (AcceptToMemoryPool(mempool, stateDummy, orphanTx, true, &fMissingInputs2)) { |
|
|
|
if (AcceptToMemoryPool(mempool, stateDummy, orphanTx, true, &fMissingInputs2)) { |
|
|
|
LogPrint("mempool", " accepted orphan tx %s\n", orphanHash.ToString()); |
|
|
|
LogPrint("mempool", " accepted orphan tx %s\n", orphanHash.ToString()); |
|
|
|
RelayTransaction(orphanTx); |
|
|
|
RelayTransaction(orphanTx); |
|
|
|
vWorkQueue.push_back(orphanHash); |
|
|
|
for (unsigned int i = 0; i < orphanTx.vout.size(); i++) { |
|
|
|
|
|
|
|
vWorkQueue.emplace_back(orphanHash, i); |
|
|
|
|
|
|
|
} |
|
|
|
vEraseQueue.push_back(orphanHash); |
|
|
|
vEraseQueue.push_back(orphanHash); |
|
|
|
} |
|
|
|
} |
|
|
|
else if (!fMissingInputs2) |
|
|
|
else if (!fMissingInputs2) |
|
|
|