Change GetPriority calculation.

Compute the value of inputs that already are in the chain at time of mempool entry and only increase priority due to aging for those inputs.  This effectively changes the CTxMemPoolEntry's GetPriority calculation from an upper bound to a lower bound.
This commit is contained in:
Alex Morcos 2015-11-13 10:05:21 -05:00
parent 71f1d9fd4a
commit c0353064dd
7 changed files with 37 additions and 16 deletions

View file

@ -243,8 +243,9 @@ bool CCoinsViewCache::HaveInputs(const CTransaction& tx) const
return true; return true;
} }
double CCoinsViewCache::GetPriority(const CTransaction &tx, int nHeight) const double CCoinsViewCache::GetPriority(const CTransaction &tx, int nHeight, CAmount &inChainInputValue) const
{ {
inChainInputValue = 0;
if (tx.IsCoinBase()) if (tx.IsCoinBase())
return 0.0; return 0.0;
double dResult = 0.0; double dResult = 0.0;
@ -253,8 +254,9 @@ double CCoinsViewCache::GetPriority(const CTransaction &tx, int nHeight) const
const CCoins* coins = AccessCoins(txin.prevout.hash); const CCoins* coins = AccessCoins(txin.prevout.hash);
assert(coins); assert(coins);
if (!coins->IsAvailable(txin.prevout.n)) continue; if (!coins->IsAvailable(txin.prevout.n)) continue;
if (coins->nHeight < nHeight) { if (coins->nHeight <= nHeight) {
dResult += coins->vout[txin.prevout.n].nValue * (nHeight-coins->nHeight); dResult += coins->vout[txin.prevout.n].nValue * (nHeight-coins->nHeight);
inChainInputValue += coins->vout[txin.prevout.n].nValue;
} }
} }
return tx.ComputePriority(dResult); return tx.ComputePriority(dResult);

View file

@ -456,8 +456,12 @@ public:
//! Check whether all prevouts of the transaction are present in the UTXO set represented by this view //! Check whether all prevouts of the transaction are present in the UTXO set represented by this view
bool HaveInputs(const CTransaction& tx) const; bool HaveInputs(const CTransaction& tx) const;
//! Return priority of tx at height nHeight /**
double GetPriority(const CTransaction &tx, int nHeight) const; * Return priority of tx at height nHeight. Also calculate the sum of the values of the inputs
* that are already in the chain. These are the inputs that will age and increase priority as
* new blocks are added to the chain.
*/
double GetPriority(const CTransaction &tx, int nHeight, CAmount &inChainInputValue) const;
const CTxOut &GetOutputFor(const CTxIn& input) const; const CTxOut &GetOutputFor(const CTxIn& input) const;

View file

@ -950,9 +950,10 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
CAmount nValueOut = tx.GetValueOut(); CAmount nValueOut = tx.GetValueOut();
CAmount nFees = nValueIn-nValueOut; CAmount nFees = nValueIn-nValueOut;
double dPriority = view.GetPriority(tx, chainActive.Height()); CAmount inChainInputValue;
double dPriority = view.GetPriority(tx, chainActive.Height(), inChainInputValue);
CTxMemPoolEntry entry(tx, nFees, GetTime(), dPriority, chainActive.Height(), pool.HasNoInputsOf(tx)); CTxMemPoolEntry entry(tx, nFees, GetTime(), dPriority, chainActive.Height(), pool.HasNoInputsOf(tx), inChainInputValue);
unsigned int nSize = entry.GetTxSize(); unsigned int nSize = entry.GetTxSize();
// Don't accept it if it can't get into a block // Don't accept it if it can't get into a block
@ -964,7 +965,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
CAmount mempoolRejectFee = pool.GetMinFee(GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000).GetFee(nSize); CAmount mempoolRejectFee = pool.GetMinFee(GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000).GetFee(nSize);
if (mempoolRejectFee > 0 && nFees < mempoolRejectFee) { if (mempoolRejectFee > 0 && nFees < mempoolRejectFee) {
return state.DoS(0, false, REJECT_INSUFFICIENTFEE, "mempool min fee not met", false, strprintf("%d < %d", nFees, mempoolRejectFee)); return state.DoS(0, false, REJECT_INSUFFICIENTFEE, "mempool min fee not met", false, strprintf("%d < %d", nFees, mempoolRejectFee));
} else if (GetBoolArg("-relaypriority", DEFAULT_RELAYPRIORITY) && nFees < ::minRelayTxFee.GetFee(nSize) && !AllowFree(view.GetPriority(tx, chainActive.Height() + 1))) { } else if (GetBoolArg("-relaypriority", DEFAULT_RELAYPRIORITY) && nFees < ::minRelayTxFee.GetFee(nSize) && !AllowFree(entry.GetPriority(chainActive.Height() + 1))) {
// Require that free transactions have sufficient priority to be mined in the next block. // Require that free transactions have sufficient priority to be mined in the next block.
return state.DoS(0, false, REJECT_INSUFFICIENTFEE, "insufficient priority"); return state.DoS(0, false, REJECT_INSUFFICIENTFEE, "insufficient priority");
} }

View file

@ -196,7 +196,7 @@ BOOST_AUTO_TEST_CASE(BlockPolicyEstimates)
// Test that if the mempool is limited, estimateSmartFee won't return a value below the mempool min fee // Test that if the mempool is limited, estimateSmartFee won't return a value below the mempool min fee
// and that estimateSmartPriority returns essentially an infinite value // and that estimateSmartPriority returns essentially an infinite value
mpool.addUnchecked(tx.GetHash(), CTxMemPoolEntry(tx, feeV[0][5], GetTime(), priV[1][5], blocknum, mpool.HasNoInputsOf(tx))); mpool.addUnchecked(tx.GetHash(), entry.Fee(feeV[0][5]).Time(GetTime()).Priority(priV[1][5]).Height(blocknum).FromTx(tx, &mpool));
// evict that transaction which should set a mempool min fee of minRelayTxFee + feeV[0][5] // evict that transaction which should set a mempool min fee of minRelayTxFee + feeV[0][5]
mpool.TrimToSize(1); mpool.TrimToSize(1);
BOOST_CHECK(mpool.GetMinFee(1).GetFeePerK() > feeV[0][5]); BOOST_CHECK(mpool.GetMinFee(1).GetFeePerK() > feeV[0][5]);

View file

@ -144,8 +144,13 @@ TestChain100Setup::~TestChain100Setup()
CTxMemPoolEntry TestMemPoolEntryHelper::FromTx(CMutableTransaction &tx, CTxMemPool *pool) { CTxMemPoolEntry TestMemPoolEntryHelper::FromTx(CMutableTransaction &tx, CTxMemPool *pool) {
return CTxMemPoolEntry(tx, nFee, nTime, dPriority, nHeight, CTransaction txn(tx);
pool ? pool->HasNoInputsOf(tx) : hadNoDependencies); bool hasNoDependencies = pool ? pool->HasNoInputsOf(tx) : hadNoDependencies;
// Hack to assume either its completely dependent on other mempool txs or not at all
CAmount inChainValue = hasNoDependencies ? txn.GetValueOut() : 0;
return CTxMemPoolEntry(txn, nFee, nTime, dPriority, nHeight,
hasNoDependencies, inChainValue);
} }
void Shutdown(void* parg) void Shutdown(void* parg)

View file

@ -19,10 +19,10 @@
using namespace std; using namespace std;
CTxMemPoolEntry::CTxMemPoolEntry(const CTransaction& _tx, const CAmount& _nFee, CTxMemPoolEntry::CTxMemPoolEntry(const CTransaction& _tx, const CAmount& _nFee,
int64_t _nTime, double _entryPriority, int64_t _nTime, double _entryPriority, unsigned int _entryHeight,
unsigned int _entryHeight, bool poolHasNoInputsOf): bool poolHasNoInputsOf, CAmount _inChainInputValue):
tx(_tx), nFee(_nFee), nTime(_nTime), entryPriority(_entryPriority), entryHeight(_entryHeight), tx(_tx), nFee(_nFee), nTime(_nTime), entryPriority(_entryPriority), entryHeight(_entryHeight),
hadNoDependencies(poolHasNoInputsOf) hadNoDependencies(poolHasNoInputsOf), inChainInputValue(_inChainInputValue)
{ {
nTxSize = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION); nTxSize = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION);
nModSize = tx.CalculateModifiedSize(nTxSize); nModSize = tx.CalculateModifiedSize(nTxSize);
@ -31,6 +31,8 @@ CTxMemPoolEntry::CTxMemPoolEntry(const CTransaction& _tx, const CAmount& _nFee,
nCountWithDescendants = 1; nCountWithDescendants = 1;
nSizeWithDescendants = nTxSize; nSizeWithDescendants = nTxSize;
nFeesWithDescendants = nFee; nFeesWithDescendants = nFee;
CAmount nValueIn = tx.GetValueOut()+nFee;
assert(inChainInputValue <= nValueIn);
} }
CTxMemPoolEntry::CTxMemPoolEntry(const CTxMemPoolEntry& other) CTxMemPoolEntry::CTxMemPoolEntry(const CTxMemPoolEntry& other)
@ -41,9 +43,10 @@ CTxMemPoolEntry::CTxMemPoolEntry(const CTxMemPoolEntry& other)
double double
CTxMemPoolEntry::GetPriority(unsigned int currentHeight) const CTxMemPoolEntry::GetPriority(unsigned int currentHeight) const
{ {
CAmount nValueIn = tx.GetValueOut()+nFee; double deltaPriority = ((double)(currentHeight-entryHeight)*inChainInputValue)/nModSize;
double deltaPriority = ((double)(currentHeight-entryHeight)*nValueIn)/nModSize;
double dResult = entryPriority + deltaPriority; double dResult = entryPriority + deltaPriority;
if (dResult < 0) // This should only happen if it was called with a height below entry height
dResult = 0;
return dResult; return dResult;
} }

View file

@ -66,6 +66,7 @@ private:
double entryPriority; //! Priority when entering the mempool double entryPriority; //! Priority when entering the mempool
unsigned int entryHeight; //! Chain height when entering the mempool unsigned int entryHeight; //! Chain height when entering the mempool
bool hadNoDependencies; //! Not dependent on any other txs when it entered the mempool 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
// Information about descendants of this transaction that are in the // Information about descendants of this transaction that are in the
// mempool; if we remove this transaction we must remove all of these // mempool; if we remove this transaction we must remove all of these
@ -78,10 +79,15 @@ private:
public: public:
CTxMemPoolEntry(const CTransaction& _tx, const CAmount& _nFee, CTxMemPoolEntry(const CTransaction& _tx, const CAmount& _nFee,
int64_t _nTime, double _entryPriority, unsigned int _entryHeight, bool poolHasNoInputsOf); int64_t _nTime, double _entryPriority, unsigned int _entryHeight,
bool poolHasNoInputsOf, CAmount _inChainInputValue);
CTxMemPoolEntry(const CTxMemPoolEntry& other); CTxMemPoolEntry(const CTxMemPoolEntry& other);
const CTransaction& GetTx() const { return this->tx; } const CTransaction& GetTx() const { return this->tx; }
/**
* Fast calculation of lower bound of current priority as update
* from entry priority. Only inputs that were originally in-chain will age.
*/
double GetPriority(unsigned int currentHeight) const; double GetPriority(unsigned int currentHeight) const;
const CAmount& GetFee() const { return nFee; } const CAmount& GetFee() const { return nFee; }
size_t GetTxSize() const { return nTxSize; } size_t GetTxSize() const { return nTxSize; }