mirror of
https://github.com/kvazar-network/kevacoin.git
synced 2025-01-11 23:58:18 +00:00
Merge pull request #6898
553cad9
Rewrite CreateNewBlock (Alex Morcos)5f12263
Expose FormatStateMessage (Alex Morcos)1f09287
Make accessing mempool parents and children public (Alex Morcos)7230187
Add TxPriority class and comparator (Alex Morcos)f3fe836
Add a score index to the mempool. (Alex Morcos)c49d5bc
Store the total sig op count of a tx. (Alex Morcos)
This commit is contained in:
commit
4077ad20d0
@ -816,7 +816,7 @@ CAmount GetMinRelayFee(const CTransaction& tx, const CTxMemPool& pool, unsigned
|
||||
}
|
||||
|
||||
/** Convert CValidationState to a human-readable message for logging */
|
||||
static std::string FormatStateMessage(const CValidationState &state)
|
||||
std::string FormatStateMessage(const CValidationState &state)
|
||||
{
|
||||
return strprintf("%s%s (code %i)",
|
||||
state.GetRejectReason(),
|
||||
@ -964,7 +964,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
|
||||
}
|
||||
}
|
||||
|
||||
CTxMemPoolEntry entry(tx, nFees, GetTime(), dPriority, chainActive.Height(), pool.HasNoInputsOf(tx), inChainInputValue, fSpendsCoinbase);
|
||||
CTxMemPoolEntry entry(tx, nFees, GetTime(), dPriority, chainActive.Height(), pool.HasNoInputsOf(tx), inChainInputValue, fSpendsCoinbase, nSigOps);
|
||||
unsigned int nSize = entry.GetTxSize();
|
||||
|
||||
// Don't accept it if it can't get into a block
|
||||
|
@ -257,6 +257,8 @@ void PruneAndFlush();
|
||||
bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransaction &tx, bool fLimitFree,
|
||||
bool* pfMissingInputs, bool fOverrideMempoolLimit=false, bool fRejectAbsurdFee=false);
|
||||
|
||||
/** Convert CValidationState to a human-readable message for logging */
|
||||
std::string FormatStateMessage(const CValidationState &state);
|
||||
|
||||
struct CNodeStateStats {
|
||||
int nMisbehavior;
|
||||
|
299
src/miner.cpp
299
src/miner.cpp
@ -27,6 +27,7 @@
|
||||
|
||||
#include <boost/thread.hpp>
|
||||
#include <boost/tuple/tuple.hpp>
|
||||
#include <queue>
|
||||
|
||||
using namespace std;
|
||||
|
||||
@ -40,48 +41,18 @@ using namespace std;
|
||||
// transactions in the memory pool. When we select transactions from the
|
||||
// pool, we select by highest priority or fee rate, so we might consider
|
||||
// transactions that depend on transactions that aren't yet in the block.
|
||||
// The COrphan class keeps track of these 'temporary orphans' while
|
||||
// CreateBlock is figuring out which transactions to include.
|
||||
//
|
||||
class COrphan
|
||||
{
|
||||
public:
|
||||
const CTransaction* ptx;
|
||||
set<uint256> setDependsOn;
|
||||
CFeeRate feeRate;
|
||||
double dPriority;
|
||||
|
||||
COrphan(const CTransaction* ptxIn) : ptx(ptxIn), feeRate(0), dPriority(0)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
uint64_t nLastBlockTx = 0;
|
||||
uint64_t nLastBlockSize = 0;
|
||||
|
||||
// We want to sort transactions by priority and fee rate, so:
|
||||
typedef boost::tuple<double, CFeeRate, const CTransaction*> TxPriority;
|
||||
class TxPriorityCompare
|
||||
class ScoreCompare
|
||||
{
|
||||
bool byFee;
|
||||
|
||||
public:
|
||||
TxPriorityCompare(bool _byFee) : byFee(_byFee) { }
|
||||
ScoreCompare() {}
|
||||
|
||||
bool operator()(const TxPriority& a, const TxPriority& b)
|
||||
bool operator()(const CTxMemPool::txiter a, const CTxMemPool::txiter b)
|
||||
{
|
||||
if (byFee)
|
||||
{
|
||||
if (a.get<1>() == b.get<1>())
|
||||
return a.get<0>() < b.get<0>();
|
||||
return a.get<1>() < b.get<1>();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (a.get<0>() == b.get<0>())
|
||||
return a.get<1>() < b.get<1>();
|
||||
return a.get<0>() < b.get<0>();
|
||||
}
|
||||
return CompareTxMemPoolEntryByScore()(*b,*a); // Convert to less than
|
||||
}
|
||||
};
|
||||
|
||||
@ -141,6 +112,22 @@ CBlockTemplate* CreateNewBlock(const CChainParams& chainparams, const CScript& s
|
||||
nBlockMinSize = std::min(nBlockMaxSize, nBlockMinSize);
|
||||
|
||||
// Collect memory pool transactions into the block
|
||||
CTxMemPool::setEntries inBlock;
|
||||
CTxMemPool::setEntries waitSet;
|
||||
|
||||
// This vector will be sorted into a priority queue:
|
||||
vector<TxCoinAgePriority> vecPriority;
|
||||
TxCoinAgePriorityCompare pricomparer;
|
||||
std::map<CTxMemPool::txiter, double, CTxMemPool::CompareIteratorByHash> waitPriMap;
|
||||
typedef std::map<CTxMemPool::txiter, double, CTxMemPool::CompareIteratorByHash>::iterator waitPriIter;
|
||||
double actualPriority = -1;
|
||||
|
||||
std::priority_queue<CTxMemPool::txiter, std::vector<CTxMemPool::txiter>, ScoreCompare> clearedTxs;
|
||||
bool fPrintPriority = GetBoolArg("-printpriority", DEFAULT_PRINTPRIORITY);
|
||||
uint64_t nBlockSize = 1000;
|
||||
uint64_t nBlockTx = 0;
|
||||
unsigned int nBlockSigOps = 100;
|
||||
int lastFewTxs = 0;
|
||||
CAmount nFees = 0;
|
||||
|
||||
{
|
||||
@ -149,157 +136,102 @@ CBlockTemplate* CreateNewBlock(const CChainParams& chainparams, const CScript& s
|
||||
const int nHeight = pindexPrev->nHeight + 1;
|
||||
pblock->nTime = GetAdjustedTime();
|
||||
const int64_t nMedianTimePast = pindexPrev->GetMedianTimePast();
|
||||
CCoinsViewCache view(pcoinsTip);
|
||||
|
||||
// Priority order to process transactions
|
||||
list<COrphan> vOrphan; // list memory doesn't move
|
||||
map<uint256, vector<COrphan*> > mapDependers;
|
||||
bool fPrintPriority = GetBoolArg("-printpriority", DEFAULT_PRINTPRIORITY);
|
||||
|
||||
// This vector will be sorted into a priority queue:
|
||||
vector<TxPriority> vecPriority;
|
||||
vecPriority.reserve(mempool.mapTx.size());
|
||||
for (CTxMemPool::indexed_transaction_set::iterator mi = mempool.mapTx.begin();
|
||||
mi != mempool.mapTx.end(); ++mi)
|
||||
{
|
||||
const CTransaction& tx = mi->GetTx();
|
||||
|
||||
int64_t nLockTimeCutoff = (STANDARD_LOCKTIME_VERIFY_FLAGS & LOCKTIME_MEDIAN_TIME_PAST)
|
||||
? nMedianTimePast
|
||||
: pblock->GetBlockTime();
|
||||
|
||||
if (tx.IsCoinBase() || !IsFinalTx(tx, nHeight, nLockTimeCutoff))
|
||||
continue;
|
||||
bool fPriorityBlock = nBlockPrioritySize > 0;
|
||||
if (fPriorityBlock) {
|
||||
vecPriority.reserve(mempool.mapTx.size());
|
||||
for (CTxMemPool::indexed_transaction_set::iterator mi = mempool.mapTx.begin();
|
||||
mi != mempool.mapTx.end(); ++mi)
|
||||
{
|
||||
double dPriority = mi->GetPriority(nHeight);
|
||||
CAmount dummy;
|
||||
mempool.ApplyDeltas(mi->GetTx().GetHash(), dPriority, dummy);
|
||||
vecPriority.push_back(TxCoinAgePriority(dPriority, mi));
|
||||
}
|
||||
std::make_heap(vecPriority.begin(), vecPriority.end(), pricomparer);
|
||||
}
|
||||
|
||||
COrphan* porphan = NULL;
|
||||
double dPriority = 0;
|
||||
CAmount nTotalIn = 0;
|
||||
bool fMissingInputs = false;
|
||||
BOOST_FOREACH(const CTxIn& txin, tx.vin)
|
||||
CTxMemPool::indexed_transaction_set::nth_index<3>::type::iterator mi = mempool.mapTx.get<3>().begin();
|
||||
CTxMemPool::txiter iter;
|
||||
|
||||
while (mi != mempool.mapTx.get<3>().end() || !clearedTxs.empty())
|
||||
{
|
||||
// Read prev transaction
|
||||
if (!view.HaveCoins(txin.prevout.hash))
|
||||
bool priorityTx = false;
|
||||
if (fPriorityBlock && !vecPriority.empty()) { // add a tx from priority queue to fill the blockprioritysize
|
||||
priorityTx = true;
|
||||
iter = vecPriority.front().second;
|
||||
actualPriority = vecPriority.front().first;
|
||||
std::pop_heap(vecPriority.begin(), vecPriority.end(), pricomparer);
|
||||
vecPriority.pop_back();
|
||||
}
|
||||
else if (clearedTxs.empty()) { // add tx with next highest score
|
||||
iter = mempool.mapTx.project<0>(mi);
|
||||
mi++;
|
||||
}
|
||||
else { // try to add a previously postponed child tx
|
||||
iter = clearedTxs.top();
|
||||
clearedTxs.pop();
|
||||
}
|
||||
|
||||
if (inBlock.count(iter))
|
||||
continue; // could have been added to the priorityBlock
|
||||
|
||||
const CTransaction& tx = iter->GetTx();
|
||||
|
||||
bool fOrphan = false;
|
||||
BOOST_FOREACH(CTxMemPool::txiter parent, mempool.GetMemPoolParents(iter))
|
||||
{
|
||||
// This should never happen; all transactions in the memory
|
||||
// pool should connect to either transactions in the chain
|
||||
// or other transactions in the memory pool.
|
||||
if (!mempool.mapTx.count(txin.prevout.hash))
|
||||
{
|
||||
LogPrintf("ERROR: mempool transaction missing input\n");
|
||||
if (fDebug) assert("mempool transaction missing input" == 0);
|
||||
fMissingInputs = true;
|
||||
if (porphan)
|
||||
vOrphan.pop_back();
|
||||
if (!inBlock.count(parent)) {
|
||||
fOrphan = true;
|
||||
break;
|
||||
}
|
||||
|
||||
// Has to wait for dependencies
|
||||
if (!porphan)
|
||||
{
|
||||
// Use list for automatic deletion
|
||||
vOrphan.push_back(COrphan(&tx));
|
||||
porphan = &vOrphan.back();
|
||||
}
|
||||
mapDependers[txin.prevout.hash].push_back(porphan);
|
||||
porphan->setDependsOn.insert(txin.prevout.hash);
|
||||
nTotalIn += mempool.mapTx.find(txin.prevout.hash)->GetTx().vout[txin.prevout.n].nValue;
|
||||
continue;
|
||||
}
|
||||
const CCoins* coins = view.AccessCoins(txin.prevout.hash);
|
||||
assert(coins);
|
||||
|
||||
CAmount nValueIn = coins->vout[txin.prevout.n].nValue;
|
||||
nTotalIn += nValueIn;
|
||||
|
||||
int nConf = nHeight - coins->nHeight;
|
||||
|
||||
dPriority += (double)nValueIn * nConf;
|
||||
}
|
||||
if (fMissingInputs) continue;
|
||||
|
||||
// Priority is sum(valuein * age) / modified_txsize
|
||||
unsigned int nTxSize = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION);
|
||||
dPriority = tx.ComputePriority(dPriority, nTxSize);
|
||||
|
||||
uint256 hash = tx.GetHash();
|
||||
mempool.ApplyDeltas(hash, dPriority, nTotalIn);
|
||||
|
||||
CFeeRate feeRate(nTotalIn-tx.GetValueOut(), nTxSize);
|
||||
|
||||
if (porphan)
|
||||
{
|
||||
porphan->dPriority = dPriority;
|
||||
porphan->feeRate = feeRate;
|
||||
}
|
||||
if (fOrphan) {
|
||||
if (priorityTx)
|
||||
waitPriMap.insert(std::make_pair(iter,actualPriority));
|
||||
else
|
||||
vecPriority.push_back(TxPriority(dPriority, feeRate, &(mi->GetTx())));
|
||||
waitSet.insert(iter);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Collect transactions into block
|
||||
uint64_t nBlockSize = 1000;
|
||||
uint64_t nBlockTx = 0;
|
||||
int nBlockSigOps = 100;
|
||||
bool fSortedByFee = (nBlockPrioritySize <= 0);
|
||||
|
||||
TxPriorityCompare comparer(fSortedByFee);
|
||||
std::make_heap(vecPriority.begin(), vecPriority.end(), comparer);
|
||||
|
||||
while (!vecPriority.empty())
|
||||
{
|
||||
// Take highest priority transaction off the priority queue:
|
||||
double dPriority = vecPriority.front().get<0>();
|
||||
CFeeRate feeRate = vecPriority.front().get<1>();
|
||||
const CTransaction& tx = *(vecPriority.front().get<2>());
|
||||
|
||||
std::pop_heap(vecPriority.begin(), vecPriority.end(), comparer);
|
||||
vecPriority.pop_back();
|
||||
|
||||
// Size limits
|
||||
unsigned int nTxSize = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION);
|
||||
if (nBlockSize + nTxSize >= nBlockMaxSize)
|
||||
unsigned int nTxSize = iter->GetTxSize();
|
||||
if (fPriorityBlock &&
|
||||
(nBlockSize + nTxSize >= nBlockPrioritySize || !AllowFree(actualPriority))) {
|
||||
fPriorityBlock = false;
|
||||
waitPriMap.clear();
|
||||
}
|
||||
if (!priorityTx &&
|
||||
(iter->GetModifiedFee() < ::minRelayTxFee.GetFee(nTxSize) && nBlockSize >= nBlockMinSize)) {
|
||||
break;
|
||||
}
|
||||
if (nBlockSize + nTxSize >= nBlockMaxSize) {
|
||||
if (nBlockSize > nBlockMaxSize - 100 || lastFewTxs > 50) {
|
||||
break;
|
||||
}
|
||||
// Once we're within 1000 bytes of a full block, only look at 50 more txs
|
||||
// to try to fill the remaining space.
|
||||
if (nBlockSize > nBlockMaxSize - 1000) {
|
||||
lastFewTxs++;
|
||||
}
|
||||
continue;
|
||||
|
||||
// Legacy limits on sigOps:
|
||||
unsigned int nTxSigOps = GetLegacySigOpCount(tx);
|
||||
if (nBlockSigOps + nTxSigOps >= MAX_BLOCK_SIGOPS)
|
||||
continue;
|
||||
|
||||
// Skip free transactions if we're past the minimum block size:
|
||||
const uint256& hash = tx.GetHash();
|
||||
double dPriorityDelta = 0;
|
||||
CAmount nFeeDelta = 0;
|
||||
mempool.ApplyDeltas(hash, dPriorityDelta, nFeeDelta);
|
||||
if (fSortedByFee && (dPriorityDelta <= 0) && (nFeeDelta <= 0) && (feeRate < ::minRelayTxFee) && (nBlockSize + nTxSize >= nBlockMinSize))
|
||||
continue;
|
||||
|
||||
// Prioritise by fee once past the priority size or we run out of high-priority
|
||||
// transactions:
|
||||
if (!fSortedByFee &&
|
||||
((nBlockSize + nTxSize >= nBlockPrioritySize) || !AllowFree(dPriority)))
|
||||
{
|
||||
fSortedByFee = true;
|
||||
comparer = TxPriorityCompare(fSortedByFee);
|
||||
std::make_heap(vecPriority.begin(), vecPriority.end(), comparer);
|
||||
}
|
||||
|
||||
if (!view.HaveInputs(tx))
|
||||
if (!IsFinalTx(tx, nHeight, nLockTimeCutoff))
|
||||
continue;
|
||||
|
||||
CAmount nTxFees = view.GetValueIn(tx)-tx.GetValueOut();
|
||||
|
||||
nTxSigOps += GetP2SHSigOpCount(tx, view);
|
||||
if (nBlockSigOps + nTxSigOps >= MAX_BLOCK_SIGOPS)
|
||||
unsigned int nTxSigOps = iter->GetSigOpCount();
|
||||
if (nBlockSigOps + nTxSigOps >= MAX_BLOCK_SIGOPS) {
|
||||
if (nBlockSigOps > MAX_BLOCK_SIGOPS - 2) {
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// Note that flags: we don't want to set mempool/IsStandard()
|
||||
// policy here, but we still have to ensure that the block we
|
||||
// create only contains transactions that are valid in new blocks.
|
||||
CValidationState state;
|
||||
if (!CheckInputs(tx, state, view, true, MANDATORY_SCRIPT_VERIFY_FLAGS, true))
|
||||
continue;
|
||||
|
||||
UpdateCoins(tx, state, view, nHeight);
|
||||
|
||||
CAmount nTxFees = iter->GetFee();
|
||||
// Added
|
||||
pblock->vtx.push_back(tx);
|
||||
pblocktemplate->vTxFees.push_back(nTxFees);
|
||||
@ -311,31 +243,37 @@ CBlockTemplate* CreateNewBlock(const CChainParams& chainparams, const CScript& s
|
||||
|
||||
if (fPrintPriority)
|
||||
{
|
||||
double dPriority = iter->GetPriority(nHeight);
|
||||
CAmount dummy;
|
||||
mempool.ApplyDeltas(tx.GetHash(), dPriority, dummy);
|
||||
LogPrintf("priority %.1f fee %s txid %s\n",
|
||||
dPriority, feeRate.ToString(), tx.GetHash().ToString());
|
||||
dPriority , CFeeRate(iter->GetModifiedFee(), nTxSize).ToString(), tx.GetHash().ToString());
|
||||
}
|
||||
|
||||
inBlock.insert(iter);
|
||||
|
||||
// Add transactions that depend on this one to the priority queue
|
||||
if (mapDependers.count(hash))
|
||||
BOOST_FOREACH(CTxMemPool::txiter child, mempool.GetMemPoolChildren(iter))
|
||||
{
|
||||
BOOST_FOREACH(COrphan* porphan, mapDependers[hash])
|
||||
{
|
||||
if (!porphan->setDependsOn.empty())
|
||||
{
|
||||
porphan->setDependsOn.erase(hash);
|
||||
if (porphan->setDependsOn.empty())
|
||||
{
|
||||
vecPriority.push_back(TxPriority(porphan->dPriority, porphan->feeRate, porphan->ptx));
|
||||
std::push_heap(vecPriority.begin(), vecPriority.end(), comparer);
|
||||
if (fPriorityBlock) {
|
||||
waitPriIter wpiter = waitPriMap.find(child);
|
||||
if (wpiter != waitPriMap.end()) {
|
||||
vecPriority.push_back(TxCoinAgePriority(wpiter->second,child));
|
||||
std::push_heap(vecPriority.begin(), vecPriority.end(), pricomparer);
|
||||
waitPriMap.erase(wpiter);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (waitSet.count(child)) {
|
||||
clearedTxs.push(child);
|
||||
waitSet.erase(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nLastBlockTx = nBlockTx;
|
||||
nLastBlockSize = nBlockSize;
|
||||
LogPrintf("CreateNewBlock(): total size %u\n", nBlockSize);
|
||||
LogPrintf("CreateNewBlock(): total size %u txs: %u fees: %ld sigops %d\n", nBlockSize, nBlockTx, nFees, nBlockSigOps);
|
||||
|
||||
// Compute final coinbase transaction.
|
||||
txNew.vout[0].nValue = nFees + GetBlockSubsidy(nHeight, chainparams.GetConsensus());
|
||||
@ -351,8 +289,9 @@ CBlockTemplate* CreateNewBlock(const CChainParams& chainparams, const CScript& s
|
||||
pblocktemplate->vTxSigOps[0] = GetLegacySigOpCount(pblock->vtx[0]);
|
||||
|
||||
CValidationState state;
|
||||
if (!TestBlockValidity(state, chainparams, *pblock, pindexPrev, false, false))
|
||||
throw std::runtime_error("CreateNewBlock(): TestBlockValidity failed");
|
||||
if (!TestBlockValidity(state, chainparams, *pblock, pindexPrev, false, false)) {
|
||||
throw std::runtime_error(strprintf("%s: TestBlockValidity failed: %s", __func__, FormatStateMessage(state)));
|
||||
}
|
||||
}
|
||||
|
||||
return pblocktemplate.release();
|
||||
|
@ -190,6 +190,7 @@ UniValue mempoolToJSON(bool fVerbose = false)
|
||||
UniValue info(UniValue::VOBJ);
|
||||
info.push_back(Pair("size", (int)e.GetTxSize()));
|
||||
info.push_back(Pair("fee", ValueFromAmount(e.GetFee())));
|
||||
info.push_back(Pair("modifiedfee", ValueFromAmount(e.GetModifiedFee())));
|
||||
info.push_back(Pair("time", e.GetTime()));
|
||||
info.push_back(Pair("height", (int)e.GetHeight()));
|
||||
info.push_back(Pair("startingpriority", e.GetPriority(e.GetHeight())));
|
||||
@ -247,6 +248,7 @@ UniValue getrawmempool(const UniValue& params, bool fHelp)
|
||||
" \"transactionid\" : { (json object)\n"
|
||||
" \"size\" : n, (numeric) transaction size in bytes\n"
|
||||
" \"fee\" : n, (numeric) transaction fee in " + CURRENCY_UNIT + "\n"
|
||||
" \"modifiedfee\" : n, (numeric) transaction fee with fee deltas used for mining priority\n"
|
||||
" \"time\" : n, (numeric) local time transaction entered pool in seconds since 1 Jan 1970 GMT\n"
|
||||
" \"height\" : n, (numeric) block height when transaction entered pool\n"
|
||||
" \"startingpriority\" : n, (numeric) priority when transaction entered pool\n"
|
||||
|
@ -102,12 +102,13 @@ BOOST_AUTO_TEST_CASE(MempoolRemoveTest)
|
||||
removed.clear();
|
||||
}
|
||||
|
||||
template<int index>
|
||||
void CheckSort(CTxMemPool &pool, std::vector<std::string> &sortedOrder)
|
||||
{
|
||||
BOOST_CHECK_EQUAL(pool.size(), sortedOrder.size());
|
||||
CTxMemPool::indexed_transaction_set::nth_index<1>::type::iterator it = pool.mapTx.get<1>().begin();
|
||||
typename CTxMemPool::indexed_transaction_set::nth_index<index>::type::iterator it = pool.mapTx.get<index>().begin();
|
||||
int count=0;
|
||||
for (; it != pool.mapTx.get<1>().end(); ++it, ++count) {
|
||||
for (; it != pool.mapTx.get<index>().end(); ++it, ++count) {
|
||||
BOOST_CHECK_EQUAL(it->GetTx().GetHash().ToString(), sortedOrder[count]);
|
||||
}
|
||||
}
|
||||
@ -163,7 +164,7 @@ BOOST_AUTO_TEST_CASE(MempoolIndexingTest)
|
||||
sortedOrder[2] = tx1.GetHash().ToString(); // 10000
|
||||
sortedOrder[3] = tx4.GetHash().ToString(); // 15000
|
||||
sortedOrder[4] = tx2.GetHash().ToString(); // 20000
|
||||
CheckSort(pool, sortedOrder);
|
||||
CheckSort<1>(pool, sortedOrder);
|
||||
|
||||
/* low fee but with high fee child */
|
||||
/* tx6 -> tx7 -> tx8, tx9 -> tx10 */
|
||||
@ -175,7 +176,7 @@ BOOST_AUTO_TEST_CASE(MempoolIndexingTest)
|
||||
BOOST_CHECK_EQUAL(pool.size(), 6);
|
||||
// Check that at this point, tx6 is sorted low
|
||||
sortedOrder.insert(sortedOrder.begin(), tx6.GetHash().ToString());
|
||||
CheckSort(pool, sortedOrder);
|
||||
CheckSort<1>(pool, sortedOrder);
|
||||
|
||||
CTxMemPool::setEntries setAncestors;
|
||||
setAncestors.insert(pool.mapTx.find(tx6.GetHash()));
|
||||
@ -201,7 +202,7 @@ BOOST_AUTO_TEST_CASE(MempoolIndexingTest)
|
||||
sortedOrder.erase(sortedOrder.begin());
|
||||
sortedOrder.push_back(tx6.GetHash().ToString());
|
||||
sortedOrder.push_back(tx7.GetHash().ToString());
|
||||
CheckSort(pool, sortedOrder);
|
||||
CheckSort<1>(pool, sortedOrder);
|
||||
|
||||
/* low fee child of tx7 */
|
||||
CMutableTransaction tx8 = CMutableTransaction();
|
||||
@ -216,7 +217,7 @@ BOOST_AUTO_TEST_CASE(MempoolIndexingTest)
|
||||
|
||||
// Now tx8 should be sorted low, but tx6/tx both high
|
||||
sortedOrder.insert(sortedOrder.begin(), tx8.GetHash().ToString());
|
||||
CheckSort(pool, sortedOrder);
|
||||
CheckSort<1>(pool, sortedOrder);
|
||||
|
||||
/* low fee child of tx7 */
|
||||
CMutableTransaction tx9 = CMutableTransaction();
|
||||
@ -231,7 +232,7 @@ BOOST_AUTO_TEST_CASE(MempoolIndexingTest)
|
||||
// tx9 should be sorted low
|
||||
BOOST_CHECK_EQUAL(pool.size(), 9);
|
||||
sortedOrder.insert(sortedOrder.begin(), tx9.GetHash().ToString());
|
||||
CheckSort(pool, sortedOrder);
|
||||
CheckSort<1>(pool, sortedOrder);
|
||||
|
||||
std::vector<std::string> snapshotOrder = sortedOrder;
|
||||
|
||||
@ -273,7 +274,7 @@ BOOST_AUTO_TEST_CASE(MempoolIndexingTest)
|
||||
sortedOrder.insert(sortedOrder.begin()+5, tx9.GetHash().ToString());
|
||||
sortedOrder.insert(sortedOrder.begin()+6, tx8.GetHash().ToString());
|
||||
sortedOrder.insert(sortedOrder.begin()+7, tx10.GetHash().ToString()); // tx10 is just before tx6
|
||||
CheckSort(pool, sortedOrder);
|
||||
CheckSort<1>(pool, sortedOrder);
|
||||
|
||||
// there should be 10 transactions in the mempool
|
||||
BOOST_CHECK_EQUAL(pool.size(), 10);
|
||||
@ -281,9 +282,42 @@ BOOST_AUTO_TEST_CASE(MempoolIndexingTest)
|
||||
// Now try removing tx10 and verify the sort order returns to normal
|
||||
std::list<CTransaction> removed;
|
||||
pool.remove(pool.mapTx.find(tx10.GetHash())->GetTx(), removed, true);
|
||||
CheckSort(pool, snapshotOrder);
|
||||
CheckSort<1>(pool, snapshotOrder);
|
||||
|
||||
pool.remove(pool.mapTx.find(tx9.GetHash())->GetTx(), removed, true);
|
||||
pool.remove(pool.mapTx.find(tx8.GetHash())->GetTx(), removed, true);
|
||||
/* Now check the sort on the mining score index.
|
||||
* Final order should be:
|
||||
*
|
||||
* tx7 (2M)
|
||||
* tx2 (20k)
|
||||
* tx4 (15000)
|
||||
* tx1/tx5 (10000)
|
||||
* tx3/6 (0)
|
||||
* (Ties resolved by hash)
|
||||
*/
|
||||
sortedOrder.clear();
|
||||
sortedOrder.push_back(tx7.GetHash().ToString());
|
||||
sortedOrder.push_back(tx2.GetHash().ToString());
|
||||
sortedOrder.push_back(tx4.GetHash().ToString());
|
||||
if (tx1.GetHash() < tx5.GetHash()) {
|
||||
sortedOrder.push_back(tx5.GetHash().ToString());
|
||||
sortedOrder.push_back(tx1.GetHash().ToString());
|
||||
} else {
|
||||
sortedOrder.push_back(tx1.GetHash().ToString());
|
||||
sortedOrder.push_back(tx5.GetHash().ToString());
|
||||
}
|
||||
if (tx3.GetHash() < tx6.GetHash()) {
|
||||
sortedOrder.push_back(tx6.GetHash().ToString());
|
||||
sortedOrder.push_back(tx3.GetHash().ToString());
|
||||
} else {
|
||||
sortedOrder.push_back(tx3.GetHash().ToString());
|
||||
sortedOrder.push_back(tx6.GetHash().ToString());
|
||||
}
|
||||
CheckSort<3>(pool, sortedOrder);
|
||||
}
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_CASE(MempoolSizeLimitTest)
|
||||
{
|
||||
CTxMemPool pool(CFeeRate(1000));
|
||||
|
@ -120,7 +120,22 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
|
||||
tx.vout[0].nValue -= 1000000;
|
||||
hash = tx.GetHash();
|
||||
bool spendsCoinbase = (i == 0) ? true : false; // only first tx spends coinbase
|
||||
mempool.addUnchecked(hash, entry.Time(GetTime()).SpendsCoinbase(spendsCoinbase).FromTx(tx));
|
||||
// If we don't set the # of sig ops in the CTxMemPoolEntry, template creation fails
|
||||
mempool.addUnchecked(hash, entry.Fee(1000000).Time(GetTime()).SpendsCoinbase(spendsCoinbase).FromTx(tx));
|
||||
tx.vin[0].prevout.hash = hash;
|
||||
}
|
||||
BOOST_CHECK_THROW(CreateNewBlock(chainparams, scriptPubKey), std::runtime_error);
|
||||
mempool.clear();
|
||||
|
||||
tx.vin[0].prevout.hash = txFirst[0]->GetHash();
|
||||
tx.vout[0].nValue = 5000000000LL;
|
||||
for (unsigned int i = 0; i < 1001; ++i)
|
||||
{
|
||||
tx.vout[0].nValue -= 1000000;
|
||||
hash = tx.GetHash();
|
||||
bool spendsCoinbase = (i == 0) ? true : false; // only first tx spends coinbase
|
||||
// If we do set the # of sig ops in the CTxMemPoolEntry, template creation passes
|
||||
mempool.addUnchecked(hash, entry.Fee(1000000).Time(GetTime()).SpendsCoinbase(spendsCoinbase).SigOps(20).FromTx(tx));
|
||||
tx.vin[0].prevout.hash = hash;
|
||||
}
|
||||
BOOST_CHECK(pblocktemplate = CreateNewBlock(chainparams, scriptPubKey));
|
||||
@ -141,18 +156,17 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
|
||||
tx.vout[0].nValue -= 10000000;
|
||||
hash = tx.GetHash();
|
||||
bool spendsCoinbase = (i == 0) ? true : false; // only first tx spends coinbase
|
||||
mempool.addUnchecked(hash, entry.Time(GetTime()).SpendsCoinbase(spendsCoinbase).FromTx(tx));
|
||||
mempool.addUnchecked(hash, entry.Fee(1000000).Time(GetTime()).SpendsCoinbase(spendsCoinbase).FromTx(tx));
|
||||
tx.vin[0].prevout.hash = hash;
|
||||
}
|
||||
BOOST_CHECK(pblocktemplate = CreateNewBlock(chainparams, scriptPubKey));
|
||||
delete pblocktemplate;
|
||||
mempool.clear();
|
||||
|
||||
// orphan in mempool
|
||||
// orphan in mempool, template creation fails
|
||||
hash = tx.GetHash();
|
||||
mempool.addUnchecked(hash, entry.Time(GetTime()).FromTx(tx));
|
||||
BOOST_CHECK(pblocktemplate = CreateNewBlock(chainparams, scriptPubKey));
|
||||
delete pblocktemplate;
|
||||
mempool.addUnchecked(hash, entry.Fee(1000000).Time(GetTime()).FromTx(tx));
|
||||
BOOST_CHECK_THROW(CreateNewBlock(chainparams, scriptPubKey), std::runtime_error);
|
||||
mempool.clear();
|
||||
|
||||
// child with higher priority than parent
|
||||
@ -160,7 +174,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
|
||||
tx.vin[0].prevout.hash = txFirst[1]->GetHash();
|
||||
tx.vout[0].nValue = 4900000000LL;
|
||||
hash = tx.GetHash();
|
||||
mempool.addUnchecked(hash, entry.Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
|
||||
mempool.addUnchecked(hash, entry.Fee(100000000LL).Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
|
||||
tx.vin[0].prevout.hash = hash;
|
||||
tx.vin.resize(2);
|
||||
tx.vin[1].scriptSig = CScript() << OP_1;
|
||||
@ -168,23 +182,23 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
|
||||
tx.vin[1].prevout.n = 0;
|
||||
tx.vout[0].nValue = 5900000000LL;
|
||||
hash = tx.GetHash();
|
||||
mempool.addUnchecked(hash, entry.Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
|
||||
mempool.addUnchecked(hash, entry.Fee(400000000LL).Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
|
||||
BOOST_CHECK(pblocktemplate = CreateNewBlock(chainparams, scriptPubKey));
|
||||
delete pblocktemplate;
|
||||
mempool.clear();
|
||||
|
||||
// coinbase in mempool
|
||||
// coinbase in mempool, template creation fails
|
||||
tx.vin.resize(1);
|
||||
tx.vin[0].prevout.SetNull();
|
||||
tx.vin[0].scriptSig = CScript() << OP_0 << OP_1;
|
||||
tx.vout[0].nValue = 0;
|
||||
hash = tx.GetHash();
|
||||
mempool.addUnchecked(hash, entry.Time(GetTime()).SpendsCoinbase(false).FromTx(tx));
|
||||
BOOST_CHECK(pblocktemplate = CreateNewBlock(chainparams, scriptPubKey));
|
||||
delete pblocktemplate;
|
||||
// give it a fee so it'll get mined
|
||||
mempool.addUnchecked(hash, entry.Fee(100000).Time(GetTime()).SpendsCoinbase(false).FromTx(tx));
|
||||
BOOST_CHECK_THROW(CreateNewBlock(chainparams, scriptPubKey), std::runtime_error);
|
||||
mempool.clear();
|
||||
|
||||
// invalid (pre-p2sh) txn in mempool
|
||||
// invalid (pre-p2sh) txn in mempool, template creation fails
|
||||
tx.vin[0].prevout.hash = txFirst[0]->GetHash();
|
||||
tx.vin[0].prevout.n = 0;
|
||||
tx.vin[0].scriptSig = CScript() << OP_1;
|
||||
@ -192,28 +206,26 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
|
||||
script = CScript() << OP_0;
|
||||
tx.vout[0].scriptPubKey = GetScriptForDestination(CScriptID(script));
|
||||
hash = tx.GetHash();
|
||||
mempool.addUnchecked(hash, entry.Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
|
||||
mempool.addUnchecked(hash, entry.Fee(10000000L).Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
|
||||
tx.vin[0].prevout.hash = hash;
|
||||
tx.vin[0].scriptSig = CScript() << std::vector<unsigned char>(script.begin(), script.end());
|
||||
tx.vout[0].nValue -= 1000000;
|
||||
hash = tx.GetHash();
|
||||
mempool.addUnchecked(hash, entry.Time(GetTime()).SpendsCoinbase(false).FromTx(tx));
|
||||
BOOST_CHECK(pblocktemplate = CreateNewBlock(chainparams, scriptPubKey));
|
||||
delete pblocktemplate;
|
||||
mempool.addUnchecked(hash, entry.Fee(1000000).Time(GetTime()).SpendsCoinbase(false).FromTx(tx));
|
||||
BOOST_CHECK_THROW(CreateNewBlock(chainparams, scriptPubKey), std::runtime_error);
|
||||
mempool.clear();
|
||||
|
||||
// double spend txn pair in mempool
|
||||
// double spend txn pair in mempool, template creation fails
|
||||
tx.vin[0].prevout.hash = txFirst[0]->GetHash();
|
||||
tx.vin[0].scriptSig = CScript() << OP_1;
|
||||
tx.vout[0].nValue = 4900000000LL;
|
||||
tx.vout[0].scriptPubKey = CScript() << OP_1;
|
||||
hash = tx.GetHash();
|
||||
mempool.addUnchecked(hash, entry.Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
|
||||
mempool.addUnchecked(hash, entry.Fee(100000000L).Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
|
||||
tx.vout[0].scriptPubKey = CScript() << OP_2;
|
||||
hash = tx.GetHash();
|
||||
mempool.addUnchecked(hash, entry.Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
|
||||
BOOST_CHECK(pblocktemplate = CreateNewBlock(chainparams, scriptPubKey));
|
||||
delete pblocktemplate;
|
||||
mempool.addUnchecked(hash, entry.Fee(100000000L).Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
|
||||
BOOST_CHECK_THROW(CreateNewBlock(chainparams, scriptPubKey), std::runtime_error);
|
||||
mempool.clear();
|
||||
|
||||
// subsidy changing
|
||||
@ -237,7 +249,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
|
||||
tx.vout[0].scriptPubKey = CScript() << OP_1;
|
||||
tx.nLockTime = chainActive.Tip()->nHeight+1;
|
||||
hash = tx.GetHash();
|
||||
mempool.addUnchecked(hash, entry.Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
|
||||
mempool.addUnchecked(hash, entry.Fee(100000000L).Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
|
||||
BOOST_CHECK(!CheckFinalTx(tx, LOCKTIME_MEDIAN_TIME_PAST));
|
||||
|
||||
// time locked
|
||||
@ -251,7 +263,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
|
||||
tx2.vout[0].scriptPubKey = CScript() << OP_1;
|
||||
tx2.nLockTime = chainActive.Tip()->GetMedianTimePast()+1;
|
||||
hash = tx2.GetHash();
|
||||
mempool.addUnchecked(hash, entry.Time(GetTime()).SpendsCoinbase(true).FromTx(tx2));
|
||||
mempool.addUnchecked(hash, entry.Fee(100000000L).Time(GetTime()).SpendsCoinbase(true).FromTx(tx2));
|
||||
BOOST_CHECK(!CheckFinalTx(tx2, LOCKTIME_MEDIAN_TIME_PAST));
|
||||
|
||||
BOOST_CHECK(pblocktemplate = CreateNewBlock(chainparams, scriptPubKey));
|
||||
|
@ -150,7 +150,7 @@ CTxMemPoolEntry TestMemPoolEntryHelper::FromTx(CMutableTransaction &tx, CTxMemPo
|
||||
CAmount inChainValue = hasNoDependencies ? txn.GetValueOut() : 0;
|
||||
|
||||
return CTxMemPoolEntry(txn, nFee, nTime, dPriority, nHeight,
|
||||
hasNoDependencies, inChainValue, spendsCoinbase);
|
||||
hasNoDependencies, inChainValue, spendsCoinbase, sigOpCount);
|
||||
}
|
||||
|
||||
void Shutdown(void* parg)
|
||||
|
@ -66,10 +66,11 @@ struct TestMemPoolEntryHelper
|
||||
unsigned int nHeight;
|
||||
bool hadNoDependencies;
|
||||
bool spendsCoinbase;
|
||||
unsigned int sigOpCount;
|
||||
|
||||
TestMemPoolEntryHelper() :
|
||||
nFee(0), nTime(0), dPriority(0.0), nHeight(1),
|
||||
hadNoDependencies(false), spendsCoinbase(false) { }
|
||||
hadNoDependencies(false), spendsCoinbase(false), sigOpCount(1) { }
|
||||
|
||||
CTxMemPoolEntry FromTx(CMutableTransaction &tx, CTxMemPool *pool = NULL);
|
||||
|
||||
@ -80,5 +81,6 @@ struct TestMemPoolEntryHelper
|
||||
TestMemPoolEntryHelper &Height(unsigned int _height) { nHeight = _height; return *this; }
|
||||
TestMemPoolEntryHelper &HadNoDependencies(bool _hnd) { hadNoDependencies = _hnd; return *this; }
|
||||
TestMemPoolEntryHelper &SpendsCoinbase(bool _flag) { spendsCoinbase = _flag; return *this; }
|
||||
TestMemPoolEntryHelper &SigOps(unsigned int _sigops) { sigOpCount = _sigops; return *this; }
|
||||
};
|
||||
#endif
|
||||
|
@ -22,10 +22,10 @@ using namespace std;
|
||||
CTxMemPoolEntry::CTxMemPoolEntry(const CTransaction& _tx, const CAmount& _nFee,
|
||||
int64_t _nTime, double _entryPriority, unsigned int _entryHeight,
|
||||
bool poolHasNoInputsOf, CAmount _inChainInputValue,
|
||||
bool _spendsCoinbase):
|
||||
bool _spendsCoinbase, unsigned int _sigOps):
|
||||
tx(_tx), nFee(_nFee), nTime(_nTime), entryPriority(_entryPriority), entryHeight(_entryHeight),
|
||||
hadNoDependencies(poolHasNoInputsOf), inChainInputValue(_inChainInputValue),
|
||||
spendsCoinbase(_spendsCoinbase)
|
||||
spendsCoinbase(_spendsCoinbase), sigOpCount(_sigOps)
|
||||
{
|
||||
nTxSize = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION);
|
||||
nModSize = tx.CalculateModifiedSize(nTxSize);
|
||||
@ -36,6 +36,8 @@ CTxMemPoolEntry::CTxMemPoolEntry(const CTransaction& _tx, const CAmount& _nFee,
|
||||
nFeesWithDescendants = nFee;
|
||||
CAmount nValueIn = tx.GetValueOut()+nFee;
|
||||
assert(inChainInputValue <= nValueIn);
|
||||
|
||||
feeDelta = 0;
|
||||
}
|
||||
|
||||
CTxMemPoolEntry::CTxMemPoolEntry(const CTxMemPoolEntry& other)
|
||||
@ -53,6 +55,11 @@ CTxMemPoolEntry::GetPriority(unsigned int currentHeight) const
|
||||
return dResult;
|
||||
}
|
||||
|
||||
void CTxMemPoolEntry::UpdateFeeDelta(int64_t newFeeDelta)
|
||||
{
|
||||
feeDelta = newFeeDelta;
|
||||
}
|
||||
|
||||
// Update the given tx for any in-mempool descendants.
|
||||
// Assumes that setMemPoolChildren is correct for the given tx and all
|
||||
// descendants.
|
||||
@ -392,6 +399,15 @@ bool CTxMemPool::addUnchecked(const uint256& hash, const CTxMemPoolEntry &entry,
|
||||
}
|
||||
UpdateAncestorsOf(true, newit, setAncestors);
|
||||
|
||||
// Update transaction's score for any feeDelta created by PrioritiseTransaction
|
||||
std::map<uint256, std::pair<double, CAmount> >::const_iterator pos = mapDeltas.find(hash);
|
||||
if (pos != mapDeltas.end()) {
|
||||
const std::pair<double, CAmount> &deltas = pos->second;
|
||||
if (deltas.second) {
|
||||
mapTx.modify(newit, update_fee_delta(deltas.second));
|
||||
}
|
||||
}
|
||||
|
||||
nTransactionsUpdated++;
|
||||
totalTxSize += entry.GetTxSize();
|
||||
minerPolicyEstimator->processTransaction(entry, fCurrentEstimate);
|
||||
@ -769,6 +785,10 @@ void CTxMemPool::PrioritiseTransaction(const uint256 hash, const string strHash,
|
||||
std::pair<double, CAmount> &deltas = mapDeltas[hash];
|
||||
deltas.first += dPriorityDelta;
|
||||
deltas.second += nFeeDelta;
|
||||
txiter it = mapTx.find(hash);
|
||||
if (it != mapTx.end()) {
|
||||
mapTx.modify(it, update_fee_delta(deltas.second));
|
||||
}
|
||||
}
|
||||
LogPrintf("PrioritiseTransaction: %s priority += %f, fee += %d\n", strHash, dPriorityDelta, FormatMoney(nFeeDelta));
|
||||
}
|
||||
@ -818,8 +838,8 @@ bool CCoinsViewMemPool::HaveCoins(const uint256 &txid) const {
|
||||
|
||||
size_t CTxMemPool::DynamicMemoryUsage() const {
|
||||
LOCK(cs);
|
||||
// Estimate the overhead of mapTx to be 9 pointers + an allocation, as no exact formula for boost::multi_index_contained is implemented.
|
||||
return memusage::MallocUsage(sizeof(CTxMemPoolEntry) + 9 * sizeof(void*)) * mapTx.size() + memusage::DynamicUsage(mapNextTx) + memusage::DynamicUsage(mapDeltas) + memusage::DynamicUsage(mapLinks) + cachedInnerUsage;
|
||||
// Estimate the overhead of mapTx to be 12 pointers + an allocation, as no exact formula for boost::multi_index_contained is implemented.
|
||||
return memusage::MallocUsage(sizeof(CTxMemPoolEntry) + 12 * sizeof(void*)) * mapTx.size() + memusage::DynamicUsage(mapNextTx) + memusage::DynamicUsage(mapDeltas) + memusage::DynamicUsage(mapLinks) + cachedInnerUsage;
|
||||
}
|
||||
|
||||
void CTxMemPool::RemoveStaged(setEntries &stage) {
|
||||
|
@ -68,6 +68,8 @@ private:
|
||||
bool hadNoDependencies; //! Not dependent on any other txs when it entered the mempool
|
||||
CAmount inChainInputValue; //! Sum of all txin values that are already in blockchain
|
||||
bool spendsCoinbase; //! keep track of transactions that spend a coinbase
|
||||
unsigned int sigOpCount; //! Legacy sig ops plus P2SH sig op count
|
||||
int64_t feeDelta; //! Used for determining the priority of the transaction for mining in a block
|
||||
|
||||
// Information about descendants of this transaction that are in the
|
||||
// mempool; if we remove this transaction we must remove all of these
|
||||
@ -81,7 +83,8 @@ private:
|
||||
public:
|
||||
CTxMemPoolEntry(const CTransaction& _tx, const CAmount& _nFee,
|
||||
int64_t _nTime, double _entryPriority, unsigned int _entryHeight,
|
||||
bool poolHasNoInputsOf, CAmount _inChainInputValue, bool spendsCoinbase);
|
||||
bool poolHasNoInputsOf, CAmount _inChainInputValue, bool spendsCoinbase,
|
||||
unsigned int nSigOps);
|
||||
CTxMemPoolEntry(const CTxMemPoolEntry& other);
|
||||
|
||||
const CTransaction& GetTx() const { return this->tx; }
|
||||
@ -95,10 +98,14 @@ public:
|
||||
int64_t GetTime() const { return nTime; }
|
||||
unsigned int GetHeight() const { return entryHeight; }
|
||||
bool WasClearAtEntry() const { return hadNoDependencies; }
|
||||
unsigned int GetSigOpCount() const { return sigOpCount; }
|
||||
int64_t GetModifiedFee() const { return nFee + feeDelta; }
|
||||
size_t DynamicMemoryUsage() const { return nUsageSize; }
|
||||
|
||||
// Adjusts the descendant state, if this entry is not dirty.
|
||||
void UpdateState(int64_t modifySize, CAmount modifyFee, int64_t modifyCount);
|
||||
// Updates the fee delta used for mining priority score
|
||||
void UpdateFeeDelta(int64_t feeDelta);
|
||||
|
||||
/** We can set the entry to be dirty if doing the full calculation of in-
|
||||
* mempool descendants will be too expensive, which can potentially happen
|
||||
@ -136,6 +143,16 @@ struct set_dirty
|
||||
{ e.SetDirty(); }
|
||||
};
|
||||
|
||||
struct update_fee_delta
|
||||
{
|
||||
update_fee_delta(int64_t _feeDelta) : feeDelta(_feeDelta) { }
|
||||
|
||||
void operator() (CTxMemPoolEntry &e) { e.UpdateFeeDelta(feeDelta); }
|
||||
|
||||
private:
|
||||
int64_t feeDelta;
|
||||
};
|
||||
|
||||
// extracts a TxMemPoolEntry's transaction hash
|
||||
struct mempoolentry_txid
|
||||
{
|
||||
@ -183,6 +200,24 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
/** \class CompareTxMemPoolEntryByScore
|
||||
*
|
||||
* Sort by score of entry ((fee+delta)/size) in descending order
|
||||
*/
|
||||
class CompareTxMemPoolEntryByScore
|
||||
{
|
||||
public:
|
||||
bool operator()(const CTxMemPoolEntry& a, const CTxMemPoolEntry& b)
|
||||
{
|
||||
double f1 = (double)a.GetModifiedFee() * b.GetTxSize();
|
||||
double f2 = (double)b.GetModifiedFee() * a.GetTxSize();
|
||||
if (f1 == f2) {
|
||||
return b.GetTx().GetHash() < a.GetTx().GetHash();
|
||||
}
|
||||
return f1 > f2;
|
||||
}
|
||||
};
|
||||
|
||||
class CompareTxMemPoolEntryByEntryTime
|
||||
{
|
||||
public:
|
||||
@ -220,10 +255,11 @@ public:
|
||||
*
|
||||
* CTxMemPool::mapTx, and CTxMemPoolEntry bookkeeping:
|
||||
*
|
||||
* mapTx is a boost::multi_index that sorts the mempool on 3 criteria:
|
||||
* mapTx is a boost::multi_index that sorts the mempool on 4 criteria:
|
||||
* - transaction hash
|
||||
* - feerate [we use max(feerate of tx, feerate of tx with all descendants)]
|
||||
* - time in mempool
|
||||
* - mining score (feerate modified by any fee deltas from PrioritiseTransaction)
|
||||
*
|
||||
* Note: the term "descendant" refers to in-mempool transactions that depend on
|
||||
* this one, while "ancestor" refers to in-mempool transactions that a given
|
||||
@ -320,6 +356,11 @@ public:
|
||||
boost::multi_index::ordered_non_unique<
|
||||
boost::multi_index::identity<CTxMemPoolEntry>,
|
||||
CompareTxMemPoolEntryByEntryTime
|
||||
>,
|
||||
// sorted by score (for mining prioritization)
|
||||
boost::multi_index::ordered_unique<
|
||||
boost::multi_index::identity<CTxMemPoolEntry>,
|
||||
CompareTxMemPoolEntryByScore
|
||||
>
|
||||
>
|
||||
> indexed_transaction_set;
|
||||
@ -334,6 +375,8 @@ public:
|
||||
};
|
||||
typedef std::set<txiter, CompareIteratorByHash> setEntries;
|
||||
|
||||
const setEntries & GetMemPoolParents(txiter entry) const;
|
||||
const setEntries & GetMemPoolChildren(txiter entry) const;
|
||||
private:
|
||||
typedef std::map<txiter, setEntries, CompareIteratorByHash> cacheMap;
|
||||
|
||||
@ -345,8 +388,6 @@ private:
|
||||
typedef std::map<txiter, TxLinks, CompareIteratorByHash> txlinksMap;
|
||||
txlinksMap mapLinks;
|
||||
|
||||
const setEntries & GetMemPoolParents(txiter entry) const;
|
||||
const setEntries & GetMemPoolChildren(txiter entry) const;
|
||||
void UpdateParent(txiter entry, txiter parent, bool add);
|
||||
void UpdateChild(txiter entry, txiter child, bool add);
|
||||
|
||||
@ -546,4 +587,17 @@ public:
|
||||
bool HaveCoins(const uint256 &txid) const;
|
||||
};
|
||||
|
||||
// We want to sort transactions by coin age priority
|
||||
typedef std::pair<double, CTxMemPool::txiter> TxCoinAgePriority;
|
||||
|
||||
struct TxCoinAgePriorityCompare
|
||||
{
|
||||
bool operator()(const TxCoinAgePriority& a, const TxCoinAgePriority& b)
|
||||
{
|
||||
if (a.first == b.first)
|
||||
return CompareTxMemPoolEntryByScore()(*(b.second), *(a.second)); //Reverse order to make sort less than
|
||||
return a.first < b.first;
|
||||
}
|
||||
};
|
||||
|
||||
#endif // BITCOIN_TXMEMPOOL_H
|
||||
|
Loading…
Reference in New Issue
Block a user