diff --git a/src/coins.cpp b/src/coins.cpp index ed82c9df8..86b2a6ef1 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 2c72ee88e..0ad28524a 100644 --- a/src/coins.h +++ b/src/coins.h @@ -340,13 +340,15 @@ public: @param[in] tx transaction for which we are checking input total @return Sum of value of all inputs (scriptSigs) - @see CTransaction::FetchInputs */ int64_t GetValueIn(const CTransaction& tx); // 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 7a1c90e58..f41ea87fe 100644 --- a/src/core.cpp +++ b/src/core.cpp @@ -106,6 +106,37 @@ bool CTransaction::IsNewerThan(const CTransaction& old) const return fNewer; } +int64_t CTransaction::GetValueOut() const +{ + int64_t nValueOut = 0; + BOOST_FOREACH(const CTxOut& txout, vout) + { + nValueOut += txout.nValue; + if (!MoneyRange(txout.nValue) || !MoneyRange(nValueOut)) + throw std::runtime_error("CTransaction::GetValueOut() : value out of range"); + } + 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 e31a7e658..e61cad90e 100644 --- a/src/core.h +++ b/src/core.h @@ -14,6 +14,10 @@ class CTransaction; +/** No amount larger than this (in satoshi) is valid */ +static const int64_t MAX_MONEY = 21000000 * COIN; +inline bool MoneyRange(int64_t nValue) { return (nValue >= 0 && nValue <= MAX_MONEY); } + /** An outpoint - a combination of a transaction hash and an index n into its vout */ class COutPoint { @@ -50,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); } }; @@ -217,6 +221,14 @@ public: uint256 GetHash() const; bool IsNewerThan(const CTransaction& old) const; + // Return sum of txouts. + int64_t GetValueOut() const; + // 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 b6888f975..457fc941e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -379,21 +379,6 @@ bool IsFinalTx(const CTransaction &tx, int nBlockHeight, int64_t nBlockTime) return true; } -/** Amount of bitcoins spent by the transaction. - @return sum of all outputs (note: does not include fees) - */ -int64_t GetValueOut(const CTransaction& tx) -{ - int64_t nValueOut = 0; - BOOST_FOREACH(const CTxOut& txout, tx.vout) - { - nValueOut += txout.nValue; - if (!MoneyRange(txout.nValue) || !MoneyRange(nValueOut)) - throw std::runtime_error("GetValueOut() : value out of range"); - } - return nValueOut; -} - // // Check transaction inputs, and make sure any // pay-to-script-hash transactions are evaluating IsStandard scripts @@ -660,7 +645,6 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa return false; // Check for conflicts with in-memory transactions - CTransaction* ptxOld = NULL; { LOCK(pool.cs); // protect pool.mapNextTx for (unsigned int i = 0; i < tx.vin.size(); i++) @@ -670,22 +654,6 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa { // Disable replacement feature for now return false; - - // Allow replacing with a newer version of the same transaction - if (i != 0) - return false; - ptxOld = pool.mapNextTx[outpoint].ptx; - if (IsFinalTx(*ptxOld)) - return false; - if (!tx.IsNewerThan(*ptxOld)) - return false; - for (unsigned int i = 0; i < tx.vin.size(); i++) - { - COutPoint outpoint = tx.vin[i].prevout; - if (!pool.mapNextTx.count(outpoint) || pool.mapNextTx[outpoint].ptx != ptxOld) - return false; - } - break; } } } @@ -734,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)-GetValueOut(tx); - 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); @@ -779,22 +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 - { - if (ptxOld) - { - LogPrint("mempool", "AcceptToMemoryPool: : replacing tx %s with new version\n", ptxOld->GetHash().ToString().c_str()); - pool.remove(*ptxOld); - } - pool.addUnchecked(hash, tx); - } - - ///// are we sure this is ok when loading transactions or restoring block txes - // If updated, erase old tx from wallet - if (ptxOld) - g_signals.EraseTransaction(ptxOld->GetHash()); g_signals.SyncTransaction(hash, tx, NULL); return true; @@ -1370,12 +1331,12 @@ bool CheckInputs(const CTransaction& tx, CValidationState &state, CCoinsViewCach } - if (nValueIn < GetValueOut(tx)) + if (nValueIn < tx.GetValueOut()) return state.DoS(100, error("CheckInputs() : %s value in < value out", tx.GetHash().ToString().c_str()), REJECT_INVALID, "in < out"); // Tally transaction fees - int64_t nTxFee = nValueIn - GetValueOut(tx); + int64_t nTxFee = nValueIn - tx.GetValueOut(); if (nTxFee < 0) return state.DoS(100, error("CheckInputs() : %s nTxFee < 0", tx.GetHash().ToString().c_str()), REJECT_INVALID, "fee < 0"); @@ -1628,7 +1589,7 @@ bool ConnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, C REJECT_INVALID, "too many sigops"); } - nFees += view.GetValueIn(tx)-GetValueOut(tx); + nFees += view.GetValueIn(tx)-tx.GetValueOut(); std::vector vChecks; if (!CheckInputs(tx, state, view, fScriptChecks, flags, nScriptCheckThreads ? &vChecks : NULL)) @@ -1648,10 +1609,10 @@ bool ConnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, C if (fBenchmark) LogPrintf("- Connect %u transactions: %.2fms (%.3fms/tx, %.3fms/txin)\n", (unsigned)block.vtx.size(), 0.001 * nTime, 0.001 * nTime / block.vtx.size(), nInputs <= 1 ? 0 : 0.001 * nTime / (nInputs-1)); - if (GetValueOut(block.vtx[0]) > GetBlockValue(pindex->nHeight, nFees)) + if (block.vtx[0].GetValueOut() > GetBlockValue(pindex->nHeight, nFees)) return state.DoS(100, error("ConnectBlock() : coinbase pays too much (actual=%"PRId64" vs limit=%"PRId64")", - GetValueOut(block.vtx[0]), GetBlockValue(pindex->nHeight, nFees)), + block.vtx[0].GetValueOut(), GetBlockValue(pindex->nHeight, nFees)), REJECT_INVALID, "coinbase too large"); if (!control.Wait()) diff --git a/src/main.h b/src/main.h index 1d3ac1cdb..c4e183944 100644 --- a/src/main.h +++ b/src/main.h @@ -49,9 +49,6 @@ static const unsigned int MAX_BLOCKFILE_SIZE = 0x8000000; // 128 MiB static const unsigned int BLOCKFILE_CHUNK_SIZE = 0x1000000; // 16 MiB /** The pre-allocation chunk size for rev?????.dat files (since 0.8) */ static const unsigned int UNDOFILE_CHUNK_SIZE = 0x100000; // 1 MiB -/** No amount larger than this (in satoshi) is valid */ -static const int64_t MAX_MONEY = 21000000 * COIN; -inline bool MoneyRange(int64_t nValue) { return (nValue >= 0 && nValue <= MAX_MONEY); } /** Coinbase transaction outputs can only be spent after this number of new blocks (network rule) */ static const int COINBASE_MATURITY = 100; /** Threshold for nLockTime: below this value it is interpreted as block number, otherwise as UNIX timestamp. */ @@ -320,11 +317,6 @@ bool IsStandardTx(const CTransaction& tx, std::string& reason); bool IsFinalTx(const CTransaction &tx, int nBlockHeight = 0, int64_t nBlockTime = 0); -/** Amount of bitcoins spent by the transaction. - @return sum of all outputs (note: does not include fees) - */ -int64_t GetValueOut(const CTransaction& tx); - /** Undo information for a CBlock */ class CBlockUndo { diff --git a/src/miner.cpp b/src/miner.cpp index b01b60cc3..ecc40ac70 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,24 +245,12 @@ 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 // incentive to create smaller transactions. - double dFeePerKb = double(nTotalIn-GetValueOut(tx)) / (double(nTxSize)/1000.0); + double dFeePerKb = double(nTotalIn-tx.GetValueOut()) / (double(nTxSize)/1000.0); if (porphan) { @@ -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(); @@ -318,7 +307,7 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn) if (!view.HaveInputs(tx)) continue; - int64_t nTxFees = view.GetValueIn(tx)-GetValueOut(tx); + int64_t nTxFees = view.GetValueIn(tx)-tx.GetValueOut(); nTxSigOps += GetP2SHSigOpCount(tx, view); if (nBlockSigOps + nTxSigOps >= MAX_BLOCK_SIGOPS) diff --git a/src/qt/transactiondesc.cpp b/src/qt/transactiondesc.cpp index 8bd2bf564..c0c4d5373 100644 --- a/src/qt/transactiondesc.cpp +++ b/src/qt/transactiondesc.cpp @@ -194,7 +194,7 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, int vout, int u strHTML += "" + tr("Credit") + ": " + BitcoinUnits::formatWithUnit(unit, nValue) + "
"; } - int64_t nTxFee = nDebit - GetValueOut(wtx); + int64_t nTxFee = nDebit - wtx.GetValueOut(); if (nTxFee > 0) strHTML += "" + tr("Transaction fee") + ": " + BitcoinUnits::formatWithUnit(unit, -nTxFee) + "
"; } diff --git a/src/qt/transactionrecord.cpp b/src/qt/transactionrecord.cpp index 675daa9c9..6823557eb 100644 --- a/src/qt/transactionrecord.cpp +++ b/src/qt/transactionrecord.cpp @@ -95,7 +95,7 @@ QList TransactionRecord::decomposeTransaction(const CWallet * // // Debit // - int64_t nTxFee = nDebit - GetValueOut(wtx); + int64_t nTxFee = nDebit - wtx.GetValueOut(); for (unsigned int nOut = 0; nOut < wtx.vout.size(); nOut++) { diff --git a/src/rpcblockchain.cpp b/src/rpcblockchain.cpp index 71663bbb3..34ae6e054 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(); + + 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); - Array a; - BOOST_FOREACH(const uint256& hash, vtxid) - a.push_back(hash.ToString()); + Array a; + BOOST_FOREACH(const uint256& hash, vtxid) + a.push_back(hash.ToString()); - return a; + return a; + } } Value getblockhash(const Array& params, bool fHelp) diff --git a/src/rpcclient.cpp b/src/rpcclient.cpp index 2667a5d5a..f571ca52d 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/rpcwallet.cpp b/src/rpcwallet.cpp index 1fd061547..b4e522de8 100644 --- a/src/rpcwallet.cpp +++ b/src/rpcwallet.cpp @@ -1673,7 +1673,7 @@ Value gettransaction(const Array& params, bool fHelp) int64_t nCredit = wtx.GetCredit(); int64_t nDebit = wtx.GetDebit(); int64_t nNet = nCredit - nDebit; - int64_t nFee = (wtx.IsFromMe() ? GetValueOut(wtx) - nDebit : 0); + int64_t nFee = (wtx.IsFromMe() ? wtx.GetValueOut() - nDebit : 0); entry.push_back(Pair("amount", ValueFromAmount(nNet - nFee))); if (wtx.IsFromMe()) diff --git a/src/test/miner_tests.cpp b/src/test/miner_tests.cpp index dcb7f9abd..8001c4f65 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 d501b89ec..be251d1d6 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 57b92789f..a652c424a 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 db957cbd0..b9110d127 100644 --- a/src/wallet.cpp +++ b/src/wallet.cpp @@ -655,7 +655,7 @@ void CWalletTx::GetAmounts(list >& listReceived, int64_t nDebit = GetDebit(); if (nDebit > 0) // debit>0 means we signed/sent this transaction { - int64_t nValueOut = GetValueOut(*this); + int64_t nValueOut = GetValueOut(); nFee = nDebit - nValueOut; } @@ -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);