aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/coins.cpp16
-rw-r--r--src/coins.h4
-rw-r--r--src/core.cpp31
-rw-r--r--src/core.h16
-rw-r--r--src/main.cpp67
-rw-r--r--src/main.h8
-rw-r--r--src/miner.cpp35
-rw-r--r--src/qt/transactiondesc.cpp2
-rw-r--r--src/qt/transactionrecord.cpp2
-rw-r--r--src/rpcblockchain.cpp75
-rw-r--r--src/rpcclient.cpp1
-rw-r--r--src/rpcwallet.cpp2
-rw-r--r--src/test/miner_tests.cpp20
-rw-r--r--src/txmempool.cpp61
-rw-r--r--src/txmempool.h31
-rw-r--r--src/wallet.cpp12
16 files changed, 244 insertions, 139 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 2c72ee88e1..0ad28524a1 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 7a1c90e587..f41ea87fea 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 e31a7e6582..e61cad90ec 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 b6888f9750..457fc941e7 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<CScriptCheck> 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 1d3ac1cdbb..c4e1839443 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 b01b60cc34..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<uint256> 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<double, double, CTransaction*> TxPriority;
+typedef boost::tuple<double, double, const CTransaction*> TxPriority;
class TxPriorityCompare
{
bool byFee;
@@ -191,9 +191,10 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn)
// This vector will be sorted into a priority queue:
vector<TxPriority> vecPriority;
vecPriority.reserve(mempool.mapTx.size());
- for (map<uint256, CTransaction>::iterator mi = mempool.mapTx.begin(); mi != mempool.mapTx.end(); ++mi)
+ for (map<uint256, CTxMemPoolEntry>::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 8bd2bf564a..c0c4d53732 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 += "<b>" + tr("Credit") + ":</b> " + BitcoinUnits::formatWithUnit(unit, nValue) + "<br>";
}
- int64_t nTxFee = nDebit - GetValueOut(wtx);
+ int64_t nTxFee = nDebit - wtx.GetValueOut();
if (nTxFee > 0)
strHTML += "<b>" + tr("Transaction fee") + ":</b> " + BitcoinUnits::formatWithUnit(unit, -nTxFee) + "<br>";
}
diff --git a/src/qt/transactionrecord.cpp b/src/qt/transactionrecord.cpp
index 675daa9c9c..6823557ebc 100644
--- a/src/qt/transactionrecord.cpp
+++ b/src/qt/transactionrecord.cpp
@@ -95,7 +95,7 @@ QList<TransactionRecord> 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 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<uint256> 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<string> 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<uint256> 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 2667a5d5a5..f571ca52d6 100644
--- a/src/rpcclient.cpp
+++ b/src/rpcclient.cpp
@@ -176,6 +176,7 @@ Array RPCConvertValues(const std::string &strMethod, const std::vector<std::stri
if (strMethod == "verifychain" && n > 0) ConvertTo<boost::int64_t>(params[0]);
if (strMethod == "verifychain" && n > 1) ConvertTo<boost::int64_t>(params[1]);
if (strMethod == "keypoolrefill" && n > 0) ConvertTo<boost::int64_t>(params[0]);
+ if (strMethod == "getrawmempool" && n > 0) ConvertTo<bool>(params[0]);
return params;
}
diff --git a/src/rpcwallet.cpp b/src/rpcwallet.cpp
index 1fd061547e..b4e522de8f 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 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<unsigned char>)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<uint256, CTransaction>::const_iterator it = mapTx.begin(); it != mapTx.end(); it++) {
+ for (std::map<uint256, CTxMemPoolEntry>::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<uint256, CTransaction>::const_iterator it2 = mapTx.find(txin.prevout.hash);
+ std::map<uint256, CTxMemPoolEntry>::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<COutPoint, CInPoint>::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<COutPoint, CInPoint>::const_iterator it = mapNextTx.begin(); it != mapNextTx.end(); it++) {
uint256 hash = it->second.ptx->GetHash();
- std::map<uint256, CTransaction>::const_iterator it2 = mapTx.find(hash);
+ map<uint256, CTxMemPoolEntry>::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<uint256>& vtxid)
+void CTxMemPool::queryHashes(vector<uint256>& vtxid)
{
vtxid.clear();
LOCK(cs);
vtxid.reserve(mapTx.size());
- for (map<uint256, CTransaction>::iterator mi = mapTx.begin(); mi != mapTx.end(); ++mi)
+ for (map<uint256, CTxMemPoolEntry>::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<uint256, CTransaction>::const_iterator i = mapTx.find(hash);
+ map<uint256, CTxMemPoolEntry>::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
@@ -13,6 +13,33 @@
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<uint256, CTransaction> mapTx;
+ std::map<uint256, CTxMemPoolEntry> mapTx;
std::map<COutPoint, CInPoint> 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 db957cbd05..b9110d1271 100644
--- a/src/wallet.cpp
+++ b/src/wallet.cpp
@@ -655,7 +655,7 @@ void CWalletTx::GetAmounts(list<pair<CTxDestination, int64_t> >& 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<pair<CScript, int64_t> >& 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);