diff --git a/src/coins.cpp b/src/coins.cpp index f0ea5c045..723e11470 100644 --- a/src/coins.cpp +++ b/src/coins.cpp @@ -243,8 +243,9 @@ bool CCoinsViewCache::HaveInputs(const CTransaction& tx) const 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()) return 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); assert(coins); 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); + inChainInputValue += coins->vout[txin.prevout.n].nValue; } } return tx.ComputePriority(dResult); diff --git a/src/coins.h b/src/coins.h index 99b25de45..d17442210 100644 --- a/src/coins.h +++ b/src/coins.h @@ -456,8 +456,12 @@ public: //! Check whether all prevouts of the transaction are present in the UTXO set represented by this view 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; diff --git a/src/main.cpp b/src/main.cpp index 33bd2e0ce..55b051734 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -950,9 +950,10 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa CAmount nValueOut = tx.GetValueOut(); 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(); // 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); if (mempoolRejectFee > 0 && 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. return state.DoS(0, false, REJECT_INSUFFICIENTFEE, "insufficient priority"); } diff --git a/src/test/policyestimator_tests.cpp b/src/test/policyestimator_tests.cpp index 1315146f1..644c3da21 100644 --- a/src/test/policyestimator_tests.cpp +++ b/src/test/policyestimator_tests.cpp @@ -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 // 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] mpool.TrimToSize(1); BOOST_CHECK(mpool.GetMinFee(1).GetFeePerK() > feeV[0][5]); diff --git a/src/test/test_bitcoin.cpp b/src/test/test_bitcoin.cpp index 2fe190f88..351870014 100644 --- a/src/test/test_bitcoin.cpp +++ b/src/test/test_bitcoin.cpp @@ -144,8 +144,13 @@ TestChain100Setup::~TestChain100Setup() CTxMemPoolEntry TestMemPoolEntryHelper::FromTx(CMutableTransaction &tx, CTxMemPool *pool) { - return CTxMemPoolEntry(tx, nFee, nTime, dPriority, nHeight, - pool ? pool->HasNoInputsOf(tx) : hadNoDependencies); + CTransaction txn(tx); + 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) diff --git a/src/txmempool.cpp b/src/txmempool.cpp index ea3aad34a..6d1df0b3d 100644 --- a/src/txmempool.cpp +++ b/src/txmempool.cpp @@ -19,10 +19,10 @@ using namespace std; CTxMemPoolEntry::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): tx(_tx), nFee(_nFee), nTime(_nTime), entryPriority(_entryPriority), entryHeight(_entryHeight), - hadNoDependencies(poolHasNoInputsOf) + hadNoDependencies(poolHasNoInputsOf), inChainInputValue(_inChainInputValue) { nTxSize = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION); nModSize = tx.CalculateModifiedSize(nTxSize); @@ -31,6 +31,8 @@ CTxMemPoolEntry::CTxMemPoolEntry(const CTransaction& _tx, const CAmount& _nFee, nCountWithDescendants = 1; nSizeWithDescendants = nTxSize; nFeesWithDescendants = nFee; + CAmount nValueIn = tx.GetValueOut()+nFee; + assert(inChainInputValue <= nValueIn); } CTxMemPoolEntry::CTxMemPoolEntry(const CTxMemPoolEntry& other) @@ -41,9 +43,10 @@ CTxMemPoolEntry::CTxMemPoolEntry(const CTxMemPoolEntry& other) double CTxMemPoolEntry::GetPriority(unsigned int currentHeight) const { - CAmount nValueIn = tx.GetValueOut()+nFee; - double deltaPriority = ((double)(currentHeight-entryHeight)*nValueIn)/nModSize; + double deltaPriority = ((double)(currentHeight-entryHeight)*inChainInputValue)/nModSize; 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; } diff --git a/src/txmempool.h b/src/txmempool.h index e189e2e46..c470bbe28 100644 --- a/src/txmempool.h +++ b/src/txmempool.h @@ -66,6 +66,7 @@ private: double entryPriority; //! Priority 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 + CAmount inChainInputValue; //! Sum of all txin values that are already in blockchain // Information about descendants of this transaction that are in the // mempool; if we remove this transaction we must remove all of these @@ -78,10 +79,15 @@ private: public: 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); 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; const CAmount& GetFee() const { return nFee; } size_t GetTxSize() const { return nTxSize; }