Add verbose boolean to getrawmempool
Also changes mempool to store CTxMemPoolEntries to keep track of when they enter/exit the pool.
This commit is contained in:
parent
0733c1bde6
commit
4d707d5120
12 changed files with 212 additions and 76 deletions
|
@ -178,3 +178,19 @@ bool CCoinsViewCache::HaveInputs(const CTransaction& tx)
|
||||||
}
|
}
|
||||||
return true;
|
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);
|
||||||
|
}
|
||||||
|
|
|
@ -346,6 +346,9 @@ 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);
|
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);
|
const CTxOut &GetOutputFor(const CTxIn& input);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
19
src/core.cpp
19
src/core.cpp
|
@ -118,6 +118,25 @@ int64_t CTransaction::GetValueOut() const
|
||||||
return nValueOut;
|
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 CTransaction::ToString() const
|
||||||
{
|
{
|
||||||
std::string str;
|
std::string str;
|
||||||
|
|
|
@ -54,11 +54,11 @@ public:
|
||||||
class CInPoint
|
class CInPoint
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
CTransaction* ptx;
|
const CTransaction* ptx;
|
||||||
unsigned int n;
|
unsigned int n;
|
||||||
|
|
||||||
CInPoint() { SetNull(); }
|
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; }
|
void SetNull() { ptx = NULL; n = (unsigned int) -1; }
|
||||||
bool IsNull() const { return (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
|
// GetValueIn() is a method on CCoinsViewCache, because
|
||||||
// inputs must be known to compute value in.
|
// 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
|
bool IsCoinBase() const
|
||||||
{
|
{
|
||||||
return (vin.size() == 1 && vin[0].prevout.IsNull());
|
return (vin.size() == 1 && vin[0].prevout.IsNull());
|
||||||
|
|
14
src/main.cpp
14
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
|
// you should add code here to check that the transaction does a
|
||||||
// reasonable number of ECDSA signature verifications.
|
// reasonable number of ECDSA signature verifications.
|
||||||
|
|
||||||
int64_t nFees = view.GetValueIn(tx)-tx.GetValueOut();
|
int64_t nValueIn = view.GetValueIn(tx);
|
||||||
unsigned int nSize = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION);
|
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
|
// Don't accept it if it can't get into a block
|
||||||
int64_t txMinFee = GetMinFee(tx, nSize, true, GMF_RELAY);
|
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());
|
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);
|
g_signals.SyncTransaction(hash, tx, NULL);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -93,12 +93,12 @@ unsigned int static ScanHash_CryptoPP(char* pmidstate, char* pdata, char* phash1
|
||||||
class COrphan
|
class COrphan
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
CTransaction* ptx;
|
const CTransaction* ptx;
|
||||||
set<uint256> setDependsOn;
|
set<uint256> setDependsOn;
|
||||||
double dPriority;
|
double dPriority;
|
||||||
double dFeePerKb;
|
double dFeePerKb;
|
||||||
|
|
||||||
COrphan(CTransaction* ptxIn)
|
COrphan(const CTransaction* ptxIn)
|
||||||
{
|
{
|
||||||
ptx = ptxIn;
|
ptx = ptxIn;
|
||||||
dPriority = dFeePerKb = 0;
|
dPriority = dFeePerKb = 0;
|
||||||
|
@ -118,7 +118,7 @@ uint64_t nLastBlockTx = 0;
|
||||||
uint64_t nLastBlockSize = 0;
|
uint64_t nLastBlockSize = 0;
|
||||||
|
|
||||||
// We want to sort transactions by priority and fee, so:
|
// 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
|
class TxPriorityCompare
|
||||||
{
|
{
|
||||||
bool byFee;
|
bool byFee;
|
||||||
|
@ -191,9 +191,10 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn)
|
||||||
// This vector will be sorted into a priority queue:
|
// This vector will be sorted into a priority queue:
|
||||||
vector<TxPriority> vecPriority;
|
vector<TxPriority> vecPriority;
|
||||||
vecPriority.reserve(mempool.mapTx.size());
|
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))
|
if (tx.IsCoinBase() || !IsFinalTx(tx))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
@ -228,7 +229,7 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn)
|
||||||
}
|
}
|
||||||
mapDependers[txin.prevout.hash].push_back(porphan);
|
mapDependers[txin.prevout.hash].push_back(porphan);
|
||||||
porphan->setDependsOn.insert(txin.prevout.hash);
|
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;
|
continue;
|
||||||
}
|
}
|
||||||
const CCoins &coins = view.GetCoins(txin.prevout.hash);
|
const CCoins &coins = view.GetCoins(txin.prevout.hash);
|
||||||
|
@ -244,19 +245,7 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn)
|
||||||
|
|
||||||
// Priority is sum(valuein * age) / modified_txsize
|
// Priority is sum(valuein * age) / modified_txsize
|
||||||
unsigned int nTxSize = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION);
|
unsigned int nTxSize = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION);
|
||||||
unsigned int nTxSizeMod = nTxSize;
|
dPriority = tx.ComputePriority(dPriority, 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;
|
|
||||||
|
|
||||||
// This is a more accurate fee-per-kilobyte than is used by the client code, because the
|
// 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
|
// 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;
|
porphan->dFeePerKb = dFeePerKb;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
vecPriority.push_back(TxPriority(dPriority, dFeePerKb, &(*mi).second));
|
vecPriority.push_back(TxPriority(dPriority, dFeePerKb, &mi->second.GetTx()));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Collect transactions into block
|
// Collect transactions into block
|
||||||
|
@ -286,7 +275,7 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn)
|
||||||
// Take highest priority transaction off the priority queue:
|
// Take highest priority transaction off the priority queue:
|
||||||
double dPriority = vecPriority.front().get<0>();
|
double dPriority = vecPriority.front().get<0>();
|
||||||
double dFeePerKb = vecPriority.front().get<1>();
|
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);
|
std::pop_heap(vecPriority.begin(), vecPriority.end(), comparer);
|
||||||
vecPriority.pop_back();
|
vecPriority.pop_back();
|
||||||
|
|
|
@ -153,28 +153,79 @@ Value settxfee(const Array& params, bool fHelp)
|
||||||
|
|
||||||
Value getrawmempool(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(
|
throw runtime_error(
|
||||||
"getrawmempool\n"
|
"getrawmempool ( verbose )\n"
|
||||||
"\nReturns all transaction ids in memory pool as a json array of string transaction ids.\n"
|
"\nReturns all transaction ids in memory pool as a json array of string transaction ids.\n"
|
||||||
"\nResult:\n"
|
"\nArguments:\n"
|
||||||
"[ (json array of string)\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"
|
" \"transactionid\" (string) The transaction id\n"
|
||||||
" ,...\n"
|
" ,...\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"
|
"\nExamples\n"
|
||||||
+ HelpExampleCli("getrawmempool", "")
|
+ HelpExampleCli("getrawmempool", "true")
|
||||||
+ HelpExampleRpc("getrawmempool", "")
|
+ HelpExampleRpc("getrawmempool", "true")
|
||||||
);
|
);
|
||||||
|
|
||||||
vector<uint256> vtxid;
|
bool fVerbose = false;
|
||||||
mempool.queryHashes(vtxid);
|
if (params.size() > 0)
|
||||||
|
fVerbose = params[0].get_bool();
|
||||||
|
|
||||||
Array a;
|
if (fVerbose)
|
||||||
BOOST_FOREACH(const uint256& hash, vtxid)
|
{
|
||||||
a.push_back(hash.ToString());
|
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);
|
||||||
|
|
||||||
return a;
|
Array a;
|
||||||
|
BOOST_FOREACH(const uint256& hash, vtxid)
|
||||||
|
a.push_back(hash.ToString());
|
||||||
|
|
||||||
|
return a;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Value getblockhash(const Array& params, bool fHelp)
|
Value getblockhash(const Array& params, bool fHelp)
|
||||||
|
|
|
@ -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 > 0) ConvertTo<boost::int64_t>(params[0]);
|
||||||
if (strMethod == "verifychain" && n > 1) ConvertTo<boost::int64_t>(params[1]);
|
if (strMethod == "verifychain" && n > 1) ConvertTo<boost::int64_t>(params[1]);
|
||||||
if (strMethod == "keypoolrefill" && n > 0) ConvertTo<boost::int64_t>(params[0]);
|
if (strMethod == "keypoolrefill" && n > 0) ConvertTo<boost::int64_t>(params[0]);
|
||||||
|
if (strMethod == "getrawmempool" && n > 0) ConvertTo<bool>(params[0]);
|
||||||
|
|
||||||
return params;
|
return params;
|
||||||
}
|
}
|
||||||
|
|
|
@ -99,7 +99,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
|
||||||
{
|
{
|
||||||
tx.vout[0].nValue -= 1000000;
|
tx.vout[0].nValue -= 1000000;
|
||||||
hash = tx.GetHash();
|
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].prevout.hash = hash;
|
||||||
}
|
}
|
||||||
BOOST_CHECK(pblocktemplate = CreateNewBlockWithKey(reservekey));
|
BOOST_CHECK(pblocktemplate = CreateNewBlockWithKey(reservekey));
|
||||||
|
@ -119,7 +119,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
|
||||||
{
|
{
|
||||||
tx.vout[0].nValue -= 10000000;
|
tx.vout[0].nValue -= 10000000;
|
||||||
hash = tx.GetHash();
|
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].prevout.hash = hash;
|
||||||
}
|
}
|
||||||
BOOST_CHECK(pblocktemplate = CreateNewBlockWithKey(reservekey));
|
BOOST_CHECK(pblocktemplate = CreateNewBlockWithKey(reservekey));
|
||||||
|
@ -128,7 +128,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
|
||||||
|
|
||||||
// orphan in mempool
|
// orphan in mempool
|
||||||
hash = tx.GetHash();
|
hash = tx.GetHash();
|
||||||
mempool.addUnchecked(hash, tx);
|
mempool.addUnchecked(hash, CTxMemPoolEntry(tx, 11, GetTime(), 111.0, 11));
|
||||||
BOOST_CHECK(pblocktemplate = CreateNewBlockWithKey(reservekey));
|
BOOST_CHECK(pblocktemplate = CreateNewBlockWithKey(reservekey));
|
||||||
delete pblocktemplate;
|
delete pblocktemplate;
|
||||||
mempool.clear();
|
mempool.clear();
|
||||||
|
@ -138,7 +138,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
|
||||||
tx.vin[0].prevout.hash = txFirst[1]->GetHash();
|
tx.vin[0].prevout.hash = txFirst[1]->GetHash();
|
||||||
tx.vout[0].nValue = 4900000000LL;
|
tx.vout[0].nValue = 4900000000LL;
|
||||||
hash = tx.GetHash();
|
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].prevout.hash = hash;
|
||||||
tx.vin.resize(2);
|
tx.vin.resize(2);
|
||||||
tx.vin[1].scriptSig = CScript() << OP_1;
|
tx.vin[1].scriptSig = CScript() << OP_1;
|
||||||
|
@ -146,7 +146,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
|
||||||
tx.vin[1].prevout.n = 0;
|
tx.vin[1].prevout.n = 0;
|
||||||
tx.vout[0].nValue = 5900000000LL;
|
tx.vout[0].nValue = 5900000000LL;
|
||||||
hash = tx.GetHash();
|
hash = tx.GetHash();
|
||||||
mempool.addUnchecked(hash, tx);
|
mempool.addUnchecked(hash, CTxMemPoolEntry(tx, 11, GetTime(), 111.0, 11));
|
||||||
BOOST_CHECK(pblocktemplate = CreateNewBlockWithKey(reservekey));
|
BOOST_CHECK(pblocktemplate = CreateNewBlockWithKey(reservekey));
|
||||||
delete pblocktemplate;
|
delete pblocktemplate;
|
||||||
mempool.clear();
|
mempool.clear();
|
||||||
|
@ -157,7 +157,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
|
||||||
tx.vin[0].scriptSig = CScript() << OP_0 << OP_1;
|
tx.vin[0].scriptSig = CScript() << OP_0 << OP_1;
|
||||||
tx.vout[0].nValue = 0;
|
tx.vout[0].nValue = 0;
|
||||||
hash = tx.GetHash();
|
hash = tx.GetHash();
|
||||||
mempool.addUnchecked(hash, tx);
|
mempool.addUnchecked(hash, CTxMemPoolEntry(tx, 11, GetTime(), 111.0, 11));
|
||||||
BOOST_CHECK(pblocktemplate = CreateNewBlockWithKey(reservekey));
|
BOOST_CHECK(pblocktemplate = CreateNewBlockWithKey(reservekey));
|
||||||
delete pblocktemplate;
|
delete pblocktemplate;
|
||||||
mempool.clear();
|
mempool.clear();
|
||||||
|
@ -170,12 +170,12 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
|
||||||
script = CScript() << OP_0;
|
script = CScript() << OP_0;
|
||||||
tx.vout[0].scriptPubKey.SetDestination(script.GetID());
|
tx.vout[0].scriptPubKey.SetDestination(script.GetID());
|
||||||
hash = tx.GetHash();
|
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].prevout.hash = hash;
|
||||||
tx.vin[0].scriptSig = CScript() << (std::vector<unsigned char>)script;
|
tx.vin[0].scriptSig = CScript() << (std::vector<unsigned char>)script;
|
||||||
tx.vout[0].nValue -= 1000000;
|
tx.vout[0].nValue -= 1000000;
|
||||||
hash = tx.GetHash();
|
hash = tx.GetHash();
|
||||||
mempool.addUnchecked(hash,tx);
|
mempool.addUnchecked(hash, CTxMemPoolEntry(tx, 11, GetTime(), 111.0, 11));
|
||||||
BOOST_CHECK(pblocktemplate = CreateNewBlockWithKey(reservekey));
|
BOOST_CHECK(pblocktemplate = CreateNewBlockWithKey(reservekey));
|
||||||
delete pblocktemplate;
|
delete pblocktemplate;
|
||||||
mempool.clear();
|
mempool.clear();
|
||||||
|
@ -186,10 +186,10 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
|
||||||
tx.vout[0].nValue = 4900000000LL;
|
tx.vout[0].nValue = 4900000000LL;
|
||||||
tx.vout[0].scriptPubKey = CScript() << OP_1;
|
tx.vout[0].scriptPubKey = CScript() << OP_1;
|
||||||
hash = tx.GetHash();
|
hash = tx.GetHash();
|
||||||
mempool.addUnchecked(hash, tx);
|
mempool.addUnchecked(hash, CTxMemPoolEntry(tx, 11, GetTime(), 111.0, 11));
|
||||||
tx.vout[0].scriptPubKey = CScript() << OP_2;
|
tx.vout[0].scriptPubKey = CScript() << OP_2;
|
||||||
hash = tx.GetHash();
|
hash = tx.GetHash();
|
||||||
mempool.addUnchecked(hash, tx);
|
mempool.addUnchecked(hash, CTxMemPoolEntry(tx, 11, GetTime(), 111.0, 11));
|
||||||
BOOST_CHECK(pblocktemplate = CreateNewBlockWithKey(reservekey));
|
BOOST_CHECK(pblocktemplate = CreateNewBlockWithKey(reservekey));
|
||||||
delete pblocktemplate;
|
delete pblocktemplate;
|
||||||
mempool.clear();
|
mempool.clear();
|
||||||
|
|
|
@ -8,6 +8,33 @@
|
||||||
|
|
||||||
using namespace std;
|
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()
|
CTxMemPool::CTxMemPool()
|
||||||
{
|
{
|
||||||
// Sanity checks off by default for performance, because otherwise
|
// 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.
|
// Add to memory pool without checking anything.
|
||||||
// Used by main.cpp AcceptToMemoryPool(), which DOES do
|
// Used by main.cpp AcceptToMemoryPool(), which DOES do
|
||||||
// all the appropriate checks.
|
// all the appropriate checks.
|
||||||
LOCK(cs);
|
LOCK(cs);
|
||||||
{
|
{
|
||||||
mapTx[hash] = tx;
|
mapTx[hash] = entry;
|
||||||
|
const CTransaction& tx = mapTx[hash].GetTx();
|
||||||
for (unsigned int i = 0; i < tx.vin.size(); i++)
|
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++;
|
nTransactionsUpdated++;
|
||||||
}
|
}
|
||||||
return true;
|
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());
|
LogPrint("mempool", "Checking mempool with %u transactions and %u inputs\n", (unsigned int)mapTx.size(), (unsigned int)mapNextTx.size());
|
||||||
|
|
||||||
LOCK(cs);
|
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;
|
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.
|
// 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()) {
|
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 {
|
} else {
|
||||||
CCoins &coins = pcoins->GetCoins(txin.prevout.hash);
|
CCoins &coins = pcoins->GetCoins(txin.prevout.hash);
|
||||||
assert(coins.IsAvailable(txin.prevout.n));
|
assert(coins.IsAvailable(txin.prevout.n));
|
||||||
|
@ -127,37 +157,38 @@ void CTxMemPool::check(CCoinsViewCache *pcoins) const
|
||||||
// Check whether its inputs are marked in mapNextTx.
|
// Check whether its inputs are marked in mapNextTx.
|
||||||
std::map<COutPoint, CInPoint>::const_iterator it3 = mapNextTx.find(txin.prevout);
|
std::map<COutPoint, CInPoint>::const_iterator it3 = mapNextTx.find(txin.prevout);
|
||||||
assert(it3 != mapNextTx.end());
|
assert(it3 != mapNextTx.end());
|
||||||
assert(it3->second.ptx == &it->second);
|
assert(it3->second.ptx == &tx);
|
||||||
assert(it3->second.n == i);
|
assert(it3->second.n == i);
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (std::map<COutPoint, CInPoint>::const_iterator it = mapNextTx.begin(); it != mapNextTx.end(); it++) {
|
for (std::map<COutPoint, CInPoint>::const_iterator it = mapNextTx.begin(); it != mapNextTx.end(); it++) {
|
||||||
uint256 hash = it->second.ptx->GetHash();
|
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 != mapTx.end());
|
||||||
assert(&it2->second == it->second.ptx);
|
assert(&tx == it->second.ptx);
|
||||||
assert(it2->second.vin.size() > it->second.n);
|
assert(tx.vin.size() > it->second.n);
|
||||||
assert(it->first == it->second.ptx->vin[it->second.n].prevout);
|
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();
|
vtxid.clear();
|
||||||
|
|
||||||
LOCK(cs);
|
LOCK(cs);
|
||||||
vtxid.reserve(mapTx.size());
|
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);
|
vtxid.push_back((*mi).first);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CTxMemPool::lookup(uint256 hash, CTransaction& result) const
|
bool CTxMemPool::lookup(uint256 hash, CTransaction& result) const
|
||||||
{
|
{
|
||||||
LOCK(cs);
|
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;
|
if (i == mapTx.end()) return false;
|
||||||
result = i->second;
|
result = i->second.GetTx();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,33 @@
|
||||||
/** Fake height value used in CCoins to signify they are only in the memory pool (since 0.8) */
|
/** 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;
|
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
|
* CTxMemPool stores valid-according-to-the-current-best-chain
|
||||||
* transactions that may be included in the next block.
|
* transactions that may be included in the next block.
|
||||||
|
@ -30,7 +57,7 @@ private:
|
||||||
|
|
||||||
public:
|
public:
|
||||||
mutable CCriticalSection cs;
|
mutable CCriticalSection cs;
|
||||||
std::map<uint256, CTransaction> mapTx;
|
std::map<uint256, CTxMemPoolEntry> mapTx;
|
||||||
std::map<COutPoint, CInPoint> mapNextTx;
|
std::map<COutPoint, CInPoint> mapNextTx;
|
||||||
|
|
||||||
CTxMemPool();
|
CTxMemPool();
|
||||||
|
@ -44,7 +71,7 @@ public:
|
||||||
void check(CCoinsViewCache *pcoins) const;
|
void check(CCoinsViewCache *pcoins) const;
|
||||||
void setSanityCheck(bool _fSanityCheck) { fSanityCheck = _fSanityCheck; }
|
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 remove(const CTransaction &tx, bool fRecursive = false);
|
||||||
bool removeConflicts(const CTransaction &tx);
|
bool removeConflicts(const CTransaction &tx);
|
||||||
void clear();
|
void clear();
|
||||||
|
|
|
@ -1342,15 +1342,7 @@ bool CWallet::CreateTransaction(const vector<pair<CScript, int64_t> >& vecSend,
|
||||||
strFailReason = _("Transaction too large");
|
strFailReason = _("Transaction too large");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
unsigned int nTxSizeMod = nBytes;
|
dPriority = wtxNew.ComputePriority(dPriority, 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;
|
|
||||||
|
|
||||||
// Check that enough fee is included
|
// Check that enough fee is included
|
||||||
int64_t nPayFee = nTransactionFee * (1 + (int64_t)nBytes / 1000);
|
int64_t nPayFee = nTransactionFee * (1 + (int64_t)nBytes / 1000);
|
||||||
|
|
Loading…
Reference in a new issue