From 4d707d512070ed88c888fdf625c0ae0f85f68d9b Mon Sep 17 00:00:00 2001 From: Gavin Andresen Date: Mon, 11 Nov 2013 17:35:14 +1000 Subject: [PATCH] Add verbose boolean to getrawmempool Also changes mempool to store CTxMemPoolEntries to keep track of when they enter/exit the pool. --- src/coins.cpp | 16 +++++++++ src/coins.h | 3 ++ src/core.cpp | 19 ++++++++++ src/core.h | 7 ++-- src/main.cpp | 14 +++++--- src/miner.cpp | 31 ++++++----------- src/rpcblockchain.cpp | 75 +++++++++++++++++++++++++++++++++------- src/rpcclient.cpp | 1 + src/test/miner_tests.cpp | 20 +++++------ src/txmempool.cpp | 61 ++++++++++++++++++++++++-------- src/txmempool.h | 31 +++++++++++++++-- src/wallet.cpp | 10 +----- 12 files changed, 212 insertions(+), 76 deletions(-) diff --git a/src/coins.cpp b/src/coins.cpp index ed82c9df8b..86b2a6ef17 100644 --- a/src/coins.cpp +++ b/src/coins.cpp @@ -178,3 +178,19 @@ bool CCoinsViewCache::HaveInputs(const CTransaction& tx) } return true; } + +double CCoinsViewCache::GetPriority(const CTransaction &tx, int nHeight) +{ + if (tx.IsCoinBase()) + return 0.0; + double dResult = 0.0; + BOOST_FOREACH(const CTxIn& txin, tx.vin) + { + const CCoins &coins = GetCoins(txin.prevout.hash); + if (!coins.IsAvailable(txin.prevout.n)) continue; + if (coins.nHeight < nHeight) { + dResult += coins.vout[txin.prevout.n].nValue * (nHeight-coins.nHeight); + } + } + return tx.ComputePriority(dResult); +} diff --git a/src/coins.h b/src/coins.h index 409d5713f3..0ad28524a1 100644 --- a/src/coins.h +++ b/src/coins.h @@ -346,6 +346,9 @@ public: // Check whether all prevouts of the transaction are present in the UTXO set represented by this view bool HaveInputs(const CTransaction& tx); + // Return priority of tx at height nHeight + double GetPriority(const CTransaction &tx, int nHeight); + const CTxOut &GetOutputFor(const CTxIn& input); private: diff --git a/src/core.cpp b/src/core.cpp index 5da6f11b51..f41ea87fea 100644 --- a/src/core.cpp +++ b/src/core.cpp @@ -118,6 +118,25 @@ int64_t CTransaction::GetValueOut() const return nValueOut; } +double CTransaction::ComputePriority(double dPriorityInputs, unsigned int nTxSize) const +{ + // In order to avoid disincentivizing cleaning up the UTXO set we don't count + // the constant overhead for each txin and up to 110 bytes of scriptSig (which + // is enough to cover a compressed pubkey p2sh redemption) for priority. + // Providing any more cleanup incentive than making additional inputs free would + // risk encouraging people to create junk outputs to redeem later. + if (nTxSize == 0) + nTxSize = ::GetSerializeSize(*this, SER_NETWORK, PROTOCOL_VERSION); + BOOST_FOREACH(const CTxIn& txin, vin) + { + unsigned int offset = 41U + std::min(110U, (unsigned int)txin.scriptSig.size()); + if (nTxSize > offset) + nTxSize -= offset; + } + if (nTxSize == 0) return 0.0; + return dPriorityInputs / nTxSize; +} + std::string CTransaction::ToString() const { std::string str; diff --git a/src/core.h b/src/core.h index 7b4c6af319..e61cad90ec 100644 --- a/src/core.h +++ b/src/core.h @@ -54,11 +54,11 @@ public: class CInPoint { public: - CTransaction* ptx; + const CTransaction* ptx; unsigned int n; CInPoint() { SetNull(); } - CInPoint(CTransaction* ptxIn, unsigned int nIn) { ptx = ptxIn; n = nIn; } + CInPoint(const CTransaction* ptxIn, unsigned int nIn) { ptx = ptxIn; n = nIn; } void SetNull() { ptx = NULL; n = (unsigned int) -1; } bool IsNull() const { return (ptx == NULL && n == (unsigned int) -1); } }; @@ -226,6 +226,9 @@ public: // GetValueIn() is a method on CCoinsViewCache, because // inputs must be known to compute value in. + // Compute priority, given priority of inputs and (optionally) tx size + double ComputePriority(double dPriorityInputs, unsigned int nTxSize=0) const; + bool IsCoinBase() const { return (vin.size() == 1 && vin[0].prevout.IsNull()); diff --git a/src/main.cpp b/src/main.cpp index 0cc134b8fb..457fc941e7 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -702,8 +702,13 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa // you should add code here to check that the transaction does a // reasonable number of ECDSA signature verifications. - int64_t nFees = view.GetValueIn(tx)-tx.GetValueOut(); - unsigned int nSize = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION); + int64_t nValueIn = view.GetValueIn(tx); + int64_t nValueOut = tx.GetValueOut(); + int64_t nFees = nValueIn-nValueOut; + double dPriority = view.GetPriority(tx, chainActive.Height()); + + CTxMemPoolEntry entry(tx, nFees, GetTime(), dPriority, chainActive.Height()); + unsigned int nSize = entry.GetTxSize(); // Don't accept it if it can't get into a block int64_t txMinFee = GetMinFee(tx, nSize, true, GMF_RELAY); @@ -747,11 +752,10 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa { return error("AcceptToMemoryPool: : ConnectInputs failed %s", hash.ToString().c_str()); } + // Store transaction in memory + pool.addUnchecked(hash, entry); } - // Store transaction in memory - pool.addUnchecked(hash, tx); - g_signals.SyncTransaction(hash, tx, NULL); return true; diff --git a/src/miner.cpp b/src/miner.cpp index 5661037acf..ecc40ac708 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -93,12 +93,12 @@ unsigned int static ScanHash_CryptoPP(char* pmidstate, char* pdata, char* phash1 class COrphan { public: - CTransaction* ptx; + const CTransaction* ptx; set setDependsOn; double dPriority; double dFeePerKb; - COrphan(CTransaction* ptxIn) + COrphan(const CTransaction* ptxIn) { ptx = ptxIn; dPriority = dFeePerKb = 0; @@ -118,7 +118,7 @@ uint64_t nLastBlockTx = 0; uint64_t nLastBlockSize = 0; // We want to sort transactions by priority and fee, so: -typedef boost::tuple TxPriority; +typedef boost::tuple TxPriority; class TxPriorityCompare { bool byFee; @@ -191,9 +191,10 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn) // This vector will be sorted into a priority queue: vector vecPriority; vecPriority.reserve(mempool.mapTx.size()); - for (map::iterator mi = mempool.mapTx.begin(); mi != mempool.mapTx.end(); ++mi) + for (map::iterator mi = mempool.mapTx.begin(); + mi != mempool.mapTx.end(); ++mi) { - CTransaction& tx = (*mi).second; + const CTransaction& tx = mi->second.GetTx(); if (tx.IsCoinBase() || !IsFinalTx(tx)) continue; @@ -228,7 +229,7 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn) } mapDependers[txin.prevout.hash].push_back(porphan); porphan->setDependsOn.insert(txin.prevout.hash); - nTotalIn += mempool.mapTx[txin.prevout.hash].vout[txin.prevout.n].nValue; + nTotalIn += mempool.mapTx[txin.prevout.hash].GetTx().vout[txin.prevout.n].nValue; continue; } const CCoins &coins = view.GetCoins(txin.prevout.hash); @@ -244,19 +245,7 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn) // Priority is sum(valuein * age) / modified_txsize unsigned int nTxSize = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION); - unsigned int nTxSizeMod = nTxSize; - // In order to avoid disincentivizing cleaning up the UTXO set we don't count - // the constant overhead for each txin and up to 110 bytes of scriptSig (which - // is enough to cover a compressed pubkey p2sh redemption) for priority. - // Providing any more cleanup incentive than making additional inputs free would - // risk encouraging people to create junk outputs to redeem later. - BOOST_FOREACH(const CTxIn& txin, tx.vin) - { - unsigned int offset = 41U + min(110U, (unsigned int)txin.scriptSig.size()); - if (nTxSizeMod > offset) - nTxSizeMod -= offset; - } - dPriority /= nTxSizeMod; + dPriority = tx.ComputePriority(dPriority, nTxSize); // This is a more accurate fee-per-kilobyte than is used by the client code, because the // client code rounds up the size to the nearest 1K. That's good, because it gives an @@ -269,7 +258,7 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn) porphan->dFeePerKb = dFeePerKb; } else - vecPriority.push_back(TxPriority(dPriority, dFeePerKb, &(*mi).second)); + vecPriority.push_back(TxPriority(dPriority, dFeePerKb, &mi->second.GetTx())); } // Collect transactions into block @@ -286,7 +275,7 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn) // Take highest priority transaction off the priority queue: double dPriority = vecPriority.front().get<0>(); double dFeePerKb = vecPriority.front().get<1>(); - CTransaction& tx = *(vecPriority.front().get<2>()); + const CTransaction& tx = *(vecPriority.front().get<2>()); std::pop_heap(vecPriority.begin(), vecPriority.end(), comparer); vecPriority.pop_back(); diff --git a/src/rpcblockchain.cpp b/src/rpcblockchain.cpp index 71663bbb34..34ae6e0543 100644 --- a/src/rpcblockchain.cpp +++ b/src/rpcblockchain.cpp @@ -153,28 +153,79 @@ Value settxfee(const Array& params, bool fHelp) Value getrawmempool(const Array& params, bool fHelp) { - if (fHelp || params.size() != 0) + if (fHelp || params.size() > 1) throw runtime_error( - "getrawmempool\n" + "getrawmempool ( verbose )\n" "\nReturns all transaction ids in memory pool as a json array of string transaction ids.\n" - "\nResult:\n" - "[ (json array of string)\n" + "\nArguments:\n" + "1. verbose (boolean, optional, default=false) true for a json object, false for array of transaction ids\n" + "\nResult: (for verbose = false):\n" + "[ (json array of string)\n" " \"transactionid\" (string) The transaction id\n" " ,...\n" "]\n" + "\nResult: (for verbose = true):\n" + "{ (json object)\n" + " \"transactionid\" : { (json object)\n" + " \"size\" : n, (numeric) transaction size in bytes\n" + " \"fee\" : n, (numeric) transaction fee in bitcoins\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" + " \"currentpriority\" : n, (numeric) transaction priority now\n" + " \"depends\" : [ (array) unconfirmed transactions used as inputs for this transaction\n" + " \"transactionid\", (string) parent transaction id\n" + " ... ]\n" + " }, ...\n" + "]\n" "\nExamples\n" - + HelpExampleCli("getrawmempool", "") - + HelpExampleRpc("getrawmempool", "") + + HelpExampleCli("getrawmempool", "true") + + HelpExampleRpc("getrawmempool", "true") ); - vector vtxid; - mempool.queryHashes(vtxid); + bool fVerbose = false; + if (params.size() > 0) + fVerbose = params[0].get_bool(); - Array a; - BOOST_FOREACH(const uint256& hash, vtxid) - a.push_back(hash.ToString()); + if (fVerbose) + { + LOCK(mempool.cs); + Object o; + BOOST_FOREACH(const PAIRTYPE(uint256, CTxMemPoolEntry)& entry, mempool.mapTx) + { + const uint256& hash = entry.first; + const CTxMemPoolEntry& e = entry.second; + Object info; + info.push_back(Pair("size", (int)e.GetTxSize())); + info.push_back(Pair("fee", ValueFromAmount(e.GetFee()))); + info.push_back(Pair("time", (boost::int64_t)e.GetTime())); + info.push_back(Pair("height", (int)e.GetHeight())); + info.push_back(Pair("startingpriority", e.GetPriority(e.GetHeight()))); + info.push_back(Pair("currentpriority", e.GetPriority(chainActive.Height()))); + const CTransaction& tx = e.GetTx(); + set setDepends; + BOOST_FOREACH(const CTxIn& txin, tx.vin) + { + if (mempool.exists(txin.prevout.hash)) + setDepends.insert(txin.prevout.hash.ToString()); + } + Array depends(setDepends.begin(), setDepends.end()); + info.push_back(Pair("depends", depends)); + o.push_back(Pair(hash.ToString(), info)); + } + return o; + } + else + { + vector vtxid; + mempool.queryHashes(vtxid); - return a; + Array a; + BOOST_FOREACH(const uint256& hash, vtxid) + a.push_back(hash.ToString()); + + return a; + } } Value getblockhash(const Array& params, bool fHelp) diff --git a/src/rpcclient.cpp b/src/rpcclient.cpp index 2667a5d5a5..f571ca52d6 100644 --- a/src/rpcclient.cpp +++ b/src/rpcclient.cpp @@ -176,6 +176,7 @@ Array RPCConvertValues(const std::string &strMethod, const std::vector 0) ConvertTo(params[0]); if (strMethod == "verifychain" && n > 1) ConvertTo(params[1]); if (strMethod == "keypoolrefill" && n > 0) ConvertTo(params[0]); + if (strMethod == "getrawmempool" && n > 0) ConvertTo(params[0]); return params; } diff --git a/src/test/miner_tests.cpp b/src/test/miner_tests.cpp index dcb7f9abd4..8001c4f65a 100644 --- a/src/test/miner_tests.cpp +++ b/src/test/miner_tests.cpp @@ -99,7 +99,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) { tx.vout[0].nValue -= 1000000; hash = tx.GetHash(); - mempool.addUnchecked(hash, tx); + mempool.addUnchecked(hash, CTxMemPoolEntry(tx, 11, GetTime(), 111.0, 11)); tx.vin[0].prevout.hash = hash; } BOOST_CHECK(pblocktemplate = CreateNewBlockWithKey(reservekey)); @@ -119,7 +119,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) { tx.vout[0].nValue -= 10000000; hash = tx.GetHash(); - mempool.addUnchecked(hash, tx); + mempool.addUnchecked(hash, CTxMemPoolEntry(tx, 11, GetTime(), 111.0, 11)); tx.vin[0].prevout.hash = hash; } BOOST_CHECK(pblocktemplate = CreateNewBlockWithKey(reservekey)); @@ -128,7 +128,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) // orphan in mempool hash = tx.GetHash(); - mempool.addUnchecked(hash, tx); + mempool.addUnchecked(hash, CTxMemPoolEntry(tx, 11, GetTime(), 111.0, 11)); BOOST_CHECK(pblocktemplate = CreateNewBlockWithKey(reservekey)); delete pblocktemplate; mempool.clear(); @@ -138,7 +138,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, tx); + mempool.addUnchecked(hash, CTxMemPoolEntry(tx, 11, GetTime(), 111.0, 11)); tx.vin[0].prevout.hash = hash; tx.vin.resize(2); tx.vin[1].scriptSig = CScript() << OP_1; @@ -146,7 +146,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) tx.vin[1].prevout.n = 0; tx.vout[0].nValue = 5900000000LL; hash = tx.GetHash(); - mempool.addUnchecked(hash, tx); + mempool.addUnchecked(hash, CTxMemPoolEntry(tx, 11, GetTime(), 111.0, 11)); BOOST_CHECK(pblocktemplate = CreateNewBlockWithKey(reservekey)); delete pblocktemplate; mempool.clear(); @@ -157,7 +157,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) tx.vin[0].scriptSig = CScript() << OP_0 << OP_1; tx.vout[0].nValue = 0; hash = tx.GetHash(); - mempool.addUnchecked(hash, tx); + mempool.addUnchecked(hash, CTxMemPoolEntry(tx, 11, GetTime(), 111.0, 11)); BOOST_CHECK(pblocktemplate = CreateNewBlockWithKey(reservekey)); delete pblocktemplate; mempool.clear(); @@ -170,12 +170,12 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) script = CScript() << OP_0; tx.vout[0].scriptPubKey.SetDestination(script.GetID()); hash = tx.GetHash(); - mempool.addUnchecked(hash, tx); + mempool.addUnchecked(hash, CTxMemPoolEntry(tx, 11, GetTime(), 111.0, 11)); tx.vin[0].prevout.hash = hash; tx.vin[0].scriptSig = CScript() << (std::vector)script; tx.vout[0].nValue -= 1000000; hash = tx.GetHash(); - mempool.addUnchecked(hash,tx); + mempool.addUnchecked(hash, CTxMemPoolEntry(tx, 11, GetTime(), 111.0, 11)); BOOST_CHECK(pblocktemplate = CreateNewBlockWithKey(reservekey)); delete pblocktemplate; mempool.clear(); @@ -186,10 +186,10 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) tx.vout[0].nValue = 4900000000LL; tx.vout[0].scriptPubKey = CScript() << OP_1; hash = tx.GetHash(); - mempool.addUnchecked(hash, tx); + mempool.addUnchecked(hash, CTxMemPoolEntry(tx, 11, GetTime(), 111.0, 11)); tx.vout[0].scriptPubKey = CScript() << OP_2; hash = tx.GetHash(); - mempool.addUnchecked(hash, tx); + mempool.addUnchecked(hash, CTxMemPoolEntry(tx, 11, GetTime(), 111.0, 11)); BOOST_CHECK(pblocktemplate = CreateNewBlockWithKey(reservekey)); delete pblocktemplate; mempool.clear(); diff --git a/src/txmempool.cpp b/src/txmempool.cpp index d501b89ecf..be251d1d64 100644 --- a/src/txmempool.cpp +++ b/src/txmempool.cpp @@ -8,6 +8,33 @@ using namespace std; +CTxMemPoolEntry::CTxMemPoolEntry() +{ + nHeight = MEMPOOL_HEIGHT; +} + +CTxMemPoolEntry::CTxMemPoolEntry(const CTransaction& _tx, int64_t _nFee, + int64_t _nTime, double _dPriority, + unsigned int _nHeight): + tx(_tx), nFee(_nFee), nTime(_nTime), dPriority(_dPriority), nHeight(_nHeight) +{ + nTxSize = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION); +} + +CTxMemPoolEntry::CTxMemPoolEntry(const CTxMemPoolEntry& other) +{ + *this = other; +} + +double +CTxMemPoolEntry::GetPriority(unsigned int currentHeight) const +{ + int64_t nValueIn = tx.GetValueOut()+nFee; + double deltaPriority = ((double)(currentHeight-nHeight)*nValueIn)/nTxSize; + double dResult = dPriority + deltaPriority; + return dResult; +} + CTxMemPool::CTxMemPool() { // Sanity checks off by default for performance, because otherwise @@ -42,16 +69,17 @@ void CTxMemPool::AddTransactionsUpdated(unsigned int n) } -bool CTxMemPool::addUnchecked(const uint256& hash, const CTransaction &tx) +bool CTxMemPool::addUnchecked(const uint256& hash, const CTxMemPoolEntry &entry) { // Add to memory pool without checking anything. // Used by main.cpp AcceptToMemoryPool(), which DOES do // all the appropriate checks. LOCK(cs); { - mapTx[hash] = tx; + mapTx[hash] = entry; + const CTransaction& tx = mapTx[hash].GetTx(); for (unsigned int i = 0; i < tx.vin.size(); i++) - mapNextTx[tx.vin[i].prevout] = CInPoint(&mapTx[hash], i); + mapNextTx[tx.vin[i].prevout] = CInPoint(&tx, i); nTransactionsUpdated++; } return true; @@ -113,13 +141,15 @@ void CTxMemPool::check(CCoinsViewCache *pcoins) const LogPrint("mempool", "Checking mempool with %u transactions and %u inputs\n", (unsigned int)mapTx.size(), (unsigned int)mapNextTx.size()); LOCK(cs); - for (std::map::const_iterator it = mapTx.begin(); it != mapTx.end(); it++) { + for (std::map::const_iterator it = mapTx.begin(); it != mapTx.end(); it++) { unsigned int i = 0; - BOOST_FOREACH(const CTxIn &txin, it->second.vin) { + const CTransaction& tx = it->second.GetTx(); + BOOST_FOREACH(const CTxIn &txin, tx.vin) { // Check that every mempool transaction's inputs refer to available coins, or other mempool tx's. - std::map::const_iterator it2 = mapTx.find(txin.prevout.hash); + std::map::const_iterator it2 = mapTx.find(txin.prevout.hash); if (it2 != mapTx.end()) { - assert(it2->second.vout.size() > txin.prevout.n && !it2->second.vout[txin.prevout.n].IsNull()); + const CTransaction& tx2 = it2->second.GetTx(); + assert(tx2.vout.size() > txin.prevout.n && !tx2.vout[txin.prevout.n].IsNull()); } else { CCoins &coins = pcoins->GetCoins(txin.prevout.hash); assert(coins.IsAvailable(txin.prevout.n)); @@ -127,37 +157,38 @@ void CTxMemPool::check(CCoinsViewCache *pcoins) const // Check whether its inputs are marked in mapNextTx. std::map::const_iterator it3 = mapNextTx.find(txin.prevout); assert(it3 != mapNextTx.end()); - assert(it3->second.ptx == &it->second); + assert(it3->second.ptx == &tx); assert(it3->second.n == i); i++; } } for (std::map::const_iterator it = mapNextTx.begin(); it != mapNextTx.end(); it++) { uint256 hash = it->second.ptx->GetHash(); - std::map::const_iterator it2 = mapTx.find(hash); + map::const_iterator it2 = mapTx.find(hash); + const CTransaction& tx = it2->second.GetTx(); assert(it2 != mapTx.end()); - assert(&it2->second == it->second.ptx); - assert(it2->second.vin.size() > it->second.n); + assert(&tx == it->second.ptx); + assert(tx.vin.size() > it->second.n); assert(it->first == it->second.ptx->vin[it->second.n].prevout); } } -void CTxMemPool::queryHashes(std::vector& vtxid) +void CTxMemPool::queryHashes(vector& vtxid) { vtxid.clear(); LOCK(cs); vtxid.reserve(mapTx.size()); - for (map::iterator mi = mapTx.begin(); mi != mapTx.end(); ++mi) + for (map::iterator mi = mapTx.begin(); mi != mapTx.end(); ++mi) vtxid.push_back((*mi).first); } bool CTxMemPool::lookup(uint256 hash, CTransaction& result) const { LOCK(cs); - std::map::const_iterator i = mapTx.find(hash); + map::const_iterator i = mapTx.find(hash); if (i == mapTx.end()) return false; - result = i->second; + result = i->second.GetTx(); return true; } diff --git a/src/txmempool.h b/src/txmempool.h index 57b92789fb..a652c424a4 100644 --- a/src/txmempool.h +++ b/src/txmempool.h @@ -12,6 +12,33 @@ /** Fake height value used in CCoins to signify they are only in the memory pool (since 0.8) */ static const unsigned int MEMPOOL_HEIGHT = 0x7FFFFFFF; +/* + * CTxMemPool stores these: + */ +class CTxMemPoolEntry +{ +private: + CTransaction tx; + int64_t nFee; // Cached to avoid expensive parent-transaction lookups + size_t nTxSize; // ... and avoid recomputing tx size + int64_t nTime; // Local time when entering the mempool + double dPriority; // Priority when entering the mempool + unsigned int nHeight; // Chain height when entering the mempool + +public: + CTxMemPoolEntry(const CTransaction& _tx, int64_t _nFee, + int64_t _nTime, double _dPriority, unsigned int _nHeight); + CTxMemPoolEntry(); + CTxMemPoolEntry(const CTxMemPoolEntry& other); + + const CTransaction& GetTx() const { return this->tx; } + double GetPriority(unsigned int currentHeight) const; + int64_t GetFee() const { return nFee; } + size_t GetTxSize() const { return nTxSize; } + int64_t GetTime() const { return nTime; } + unsigned int GetHeight() const { return nHeight; } +}; + /* * CTxMemPool stores valid-according-to-the-current-best-chain * transactions that may be included in the next block. @@ -30,7 +57,7 @@ private: public: mutable CCriticalSection cs; - std::map mapTx; + std::map mapTx; std::map mapNextTx; CTxMemPool(); @@ -44,7 +71,7 @@ public: void check(CCoinsViewCache *pcoins) const; void setSanityCheck(bool _fSanityCheck) { fSanityCheck = _fSanityCheck; } - bool addUnchecked(const uint256& hash, const CTransaction &tx); + bool addUnchecked(const uint256& hash, const CTxMemPoolEntry &entry); bool remove(const CTransaction &tx, bool fRecursive = false); bool removeConflicts(const CTransaction &tx); void clear(); diff --git a/src/wallet.cpp b/src/wallet.cpp index 46d6cc5663..b9110d1271 100644 --- a/src/wallet.cpp +++ b/src/wallet.cpp @@ -1342,15 +1342,7 @@ bool CWallet::CreateTransaction(const vector >& vecSend, strFailReason = _("Transaction too large"); return false; } - unsigned int nTxSizeMod = nBytes; - // See miner.c's dPriority logic for the matching network-node side code. - BOOST_FOREACH(const CTxIn& txin, (*(CTransaction*)&wtxNew).vin) - { - unsigned int offset = 41U + min(110U, (unsigned int)txin.scriptSig.size()); - if (nTxSizeMod > offset) - nTxSizeMod -= offset; - } - dPriority /= nTxSizeMod; + dPriority = wtxNew.ComputePriority(dPriority, nBytes); // Check that enough fee is included int64_t nPayFee = nTransactionFee * (1 + (int64_t)nBytes / 1000);