|
|
@ -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)
|
|
|
|