Files

211 lines
7.0 KiB
C++
Raw Permalink Normal View History

2017-05-02 15:25:05 +02:00
/*
2017-11-09 19:34:51 +01:00
* This file is part of the Flowee project
2021-06-20 22:44:44 +02:00
* Copyright (C) 2017 Tom Zander <tom@flowee.org>
2017-05-02 15:25:05 +02:00
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
2017-02-08 17:17:38 +01:00
#include "txorphancache.h"
2018-02-12 14:15:24 +01:00
#include <SettingsDefaults.h>
2026-05-14 13:13:40 +02:00
#include "utiltime.h"
2017-02-08 17:17:38 +01:00
#include "util.h"
#include "net.h"
CTxOrphanCache::CTxOrphanCache()
2018-02-12 14:15:24 +01:00
: m_limit(Settings::DefaultMaxOrphanTransactions)
2017-02-08 17:17:38 +01:00
{
}
CTxOrphanCache* CTxOrphanCache::s_instance = 0;
CTxOrphanCache* CTxOrphanCache::instance()
{
if (s_instance == 0)
s_instance = new CTxOrphanCache();
return s_instance;
}
2019-11-21 20:03:26 +01:00
bool CTxOrphanCache::addOrphanTx(const CTransaction& tx, NodeId peer, uint32_t onResultFlags, uint64_t originalEntryTime)
2017-02-08 17:17:38 +01:00
{
LOCK(m_lock);
uint256 hash = tx.GetHash();
if (m_mapOrphanTransactions.count(hash))
return false;
// Ignore big transactions, to avoid a
// send-big-orphans memory exhaustion attack. If a peer has a legitimate
// large transaction with a missing parent then we assume
// it will rebroadcast it later, after the parent transaction(s)
// have been mined or received.
// 5000 orphans, each of which is at most 100,000 bytes big is
// at most 500 megabytes of orphans:
unsigned int sz = tx.GetSerializeSize(SER_NETWORK, CTransaction::CURRENT_VERSION);
if (sz > 100000) {
2017-08-17 10:05:11 +02:00
logDebug(Log::Mempool) << "ignoring large orphan tx. Size:" << sz << "hash:" << hash.ToString();
2017-02-08 17:17:38 +01:00
return false;
}
2018-01-15 15:26:12 +00:00
COrphanTx &entry = m_mapOrphanTransactions[hash];
entry.tx = tx;
entry.fromPeer = peer;
if (originalEntryTime == 0)
originalEntryTime = GetTime();
entry.nEntryTime = originalEntryTime;
entry.onResultFlags = onResultFlags;
2018-12-30 15:33:11 +01:00
for (const CTxIn& txin : tx.vin) {
2017-02-08 17:17:38 +01:00
m_mapOrphanTransactionsByPrev[txin.prevout.hash].insert(hash);
}
2017-08-17 10:05:11 +02:00
logDebug(Log::Mempool) << "stored orphan tx" << hash << "(mapsz"
<< m_mapOrphanTransactions.size() << "prevsz " << m_mapOrphanTransactionsByPrev.size() << ')';
2017-02-08 17:17:38 +01:00
return true;
}
2019-11-21 20:03:26 +01:00
void CTxOrphanCache::eraseOrphanTx(uint256 hash)
2017-02-08 17:17:38 +01:00
{
std::map<uint256, COrphanTx>::iterator it = m_mapOrphanTransactions.find(hash);
if (it == m_mapOrphanTransactions.end())
return;
2018-12-30 15:33:11 +01:00
for (const CTxIn& txin : it->second.tx.vin) {
2017-02-08 17:17:38 +01:00
auto itPrev = m_mapOrphanTransactionsByPrev.find(txin.prevout.hash);
if (itPrev == m_mapOrphanTransactionsByPrev.end())
continue;
itPrev->second.erase(hash);
if (itPrev->second.empty())
m_mapOrphanTransactionsByPrev.erase(itPrev);
}
m_mapOrphanTransactions.erase(it);
}
2019-11-21 20:03:26 +01:00
void CTxOrphanCache::eraseOrphansByTime()
2017-02-08 17:17:38 +01:00
{
LOCK(m_lock);
static int64_t nLastOrphanCheck = GetTime();
// Because we have to iterate through the entire orphan cache which can be large we don't want to check this
// every time a tx enters the mempool but just once every 5 minutes is good enough.
if (GetTime() < nLastOrphanCheck + 5 * 60)
return;
2018-02-12 14:15:24 +01:00
int64_t nOrphanTxCutoffTime = GetTime() - GetArg("-mempoolexpiry", Settings::DefaultMempoolExpiry) * 60 * 60;
2017-02-08 17:17:38 +01:00
std::map<uint256, COrphanTx>::iterator iter = m_mapOrphanTransactions.begin();
while (iter != m_mapOrphanTransactions.end()) {
const auto entry = iter++;
int64_t nEntryTime = entry->second.nEntryTime;
if (nEntryTime < nOrphanTxCutoffTime) {
uint256 txHash = entry->second.tx.GetHash();
2019-11-21 20:03:26 +01:00
eraseOrphanTx(txHash);
2017-08-17 10:05:11 +02:00
logDebug(Log::Mempool) << "Erased old orphan tx" << txHash << "of age" << (GetTime() - nEntryTime) << "seconds";
2017-02-08 17:17:38 +01:00
}
}
nLastOrphanCheck = GetTime();
}
2019-11-21 20:03:26 +01:00
std::uint32_t CTxOrphanCache::limitOrphanTxSize(std::uint32_t nMaxOrphans)
2017-02-08 17:17:38 +01:00
{
LOCK(m_lock);
unsigned int nEvicted = 0;
while (m_mapOrphanTransactions.size() > nMaxOrphans) {
// Evict a random orphan:
uint256 randomhash = GetRandHash();
std::map<uint256, COrphanTx>::iterator it = m_mapOrphanTransactions.lower_bound(randomhash);
if (it == m_mapOrphanTransactions.end())
it = m_mapOrphanTransactions.begin();
2019-11-21 20:03:26 +01:00
eraseOrphanTx(it->first);
2017-02-08 17:17:38 +01:00
++nEvicted;
}
return nEvicted;
}
2019-11-21 20:03:26 +01:00
uint32_t CTxOrphanCache::limitOrphanTxSize()
2017-02-08 17:17:38 +01:00
{
2019-11-21 20:03:26 +01:00
return limitOrphanTxSize(m_limit);
2017-02-08 17:17:38 +01:00
}
void CTxOrphanCache::clear()
{
if (s_instance) {
LOCK(s_instance->m_lock);
s_instance->m_mapOrphanTransactions.clear();
s_instance->m_mapOrphanTransactionsByPrev.clear();
}
}
bool CTxOrphanCache::value(const uint256 &txid, CTransaction &output)
{
CTxOrphanCache *s = instance();
LOCK(s->m_lock);
auto iter = s->m_mapOrphanTransactions.find(txid);
if (iter == s->m_mapOrphanTransactions.end())
return false;
output = iter->second.tx;
return true;
}
bool CTxOrphanCache::contains(const uint256 &txid)
{
CTxOrphanCache *s = instance();
LOCK(s->m_lock);
return s->m_mapOrphanTransactions.count(txid) > 0;
}
std::vector<uint256> CTxOrphanCache::fetchTransactionIds() const
{
LOCK(m_lock);
std::vector<uint256> answer;
answer.reserve(m_mapOrphanTransactions.size());
for (auto iter = m_mapOrphanTransactions.begin(); iter != m_mapOrphanTransactions.end(); ++iter)
answer.push_back((*iter).first);
return answer;
}
void CTxOrphanCache::setLimit(uint32_t limit)
{
m_limit = limit;
}
std::vector<CTxOrphanCache::COrphanTx> CTxOrphanCache::fetchTransactionsByPrev(const uint256 &txid) const
{
LOCK(m_lock);
std::vector<CTxOrphanCache::COrphanTx> answer;
auto itByPrev = m_mapOrphanTransactionsByPrev.find(txid);
if (itByPrev == m_mapOrphanTransactionsByPrev.end())
return answer;
for (auto mi = itByPrev->second.begin(); mi != itByPrev->second.end(); ++mi) {
const uint256& orphanHash = *mi;
answer.push_back(m_mapOrphanTransactions.at(orphanHash));
}
return answer;
}
2019-11-21 20:03:26 +01:00
void CTxOrphanCache::eraseOrphans(const std::vector<uint256> &txIds)
2017-02-08 17:17:38 +01:00
{
LOCK(m_lock);
for (auto hashIter = txIds.begin(); hashIter != txIds.end(); ++hashIter) {
auto it = m_mapOrphanTransactions.find(*hashIter);
if (it == m_mapOrphanTransactions.end())
continue;
2018-12-30 15:33:11 +01:00
for (const CTxIn& txin : it->second.tx.vin) {
2017-02-08 17:17:38 +01:00
auto itPrev = m_mapOrphanTransactionsByPrev.find(txin.prevout.hash);
if (itPrev == m_mapOrphanTransactionsByPrev.end())
continue;
itPrev->second.erase(*hashIter);
if (itPrev->second.empty())
m_mapOrphanTransactionsByPrev.erase(itPrev);
}
m_mapOrphanTransactions.erase(it);
}
}