Auto merge of #2175 - str4d:2074-txn-mempool, r=bitcartel
Bitcoin 0.12 mempool memory usage PRs Cherry-picked from the following upstream PRs: - bitcoin/bitcoin#6410 - bitcoin/bitcoin#6453 - bitcoin/bitcoin#6013 (excludes changes to docs we deleted) Part of #2074.
This commit is contained in:
@@ -54,15 +54,31 @@ class MempoolSpendCoinbaseTest(BitcoinTestFramework):
|
|||||||
assert_raises(JSONRPCException, self.nodes[0].sendrawtransaction, spends_raw[1])
|
assert_raises(JSONRPCException, self.nodes[0].sendrawtransaction, spends_raw[1])
|
||||||
|
|
||||||
# mempool should have just spend_101:
|
# mempool should have just spend_101:
|
||||||
|
mempoolinfo = self.nodes[0].getmempoolinfo()
|
||||||
|
assert_equal(mempoolinfo['size'], 1)
|
||||||
assert_equal(self.nodes[0].getrawmempool(), [ spend_101_id ])
|
assert_equal(self.nodes[0].getrawmempool(), [ spend_101_id ])
|
||||||
|
|
||||||
|
# the size of the memory pool should be greater than 1x ~100 bytes
|
||||||
|
assert_greater_than(mempoolinfo['bytes'], 100)
|
||||||
|
# the actual memory usage should be strictly greater than the size
|
||||||
|
# of the memory pool
|
||||||
|
assert_greater_than(mempoolinfo['usage'], mempoolinfo['bytes'])
|
||||||
|
|
||||||
# mine a block, spend_101 should get confirmed
|
# mine a block, spend_101 should get confirmed
|
||||||
self.nodes[0].generate(1)
|
self.nodes[0].generate(1)
|
||||||
|
mempoolinfo = self.nodes[0].getmempoolinfo()
|
||||||
|
assert_equal(mempoolinfo['size'], 0)
|
||||||
|
assert_equal(mempoolinfo['bytes'], 0)
|
||||||
|
assert_equal(mempoolinfo['usage'], 0)
|
||||||
assert_equal(set(self.nodes[0].getrawmempool()), set())
|
assert_equal(set(self.nodes[0].getrawmempool()), set())
|
||||||
|
|
||||||
# ... and now height 102 can be spent:
|
# ... and now height 102 can be spent:
|
||||||
spend_102_id = self.nodes[0].sendrawtransaction(spends_raw[1])
|
spend_102_id = self.nodes[0].sendrawtransaction(spends_raw[1])
|
||||||
|
mempoolinfo = self.nodes[0].getmempoolinfo()
|
||||||
|
assert_equal(mempoolinfo['size'], 1)
|
||||||
assert_equal(self.nodes[0].getrawmempool(), [ spend_102_id ])
|
assert_equal(self.nodes[0].getrawmempool(), [ spend_102_id ])
|
||||||
|
assert_greater_than(mempoolinfo['bytes'], 100)
|
||||||
|
assert_greater_than(mempoolinfo['usage'], mempoolinfo['bytes'])
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
MempoolSpendCoinbaseTest().main()
|
MempoolSpendCoinbaseTest().main()
|
||||||
|
|||||||
@@ -301,6 +301,19 @@ class RESTTest (BitcoinTestFramework):
|
|||||||
txs.append(self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 1))
|
txs.append(self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 1))
|
||||||
self.sync_all()
|
self.sync_all()
|
||||||
|
|
||||||
|
# check that there are exactly 3 transactions in the TX memory pool before generating the block
|
||||||
|
json_string = http_get_call(url.hostname, url.port, '/rest/mempool/info'+self.FORMAT_SEPARATOR+'json')
|
||||||
|
json_obj = json.loads(json_string)
|
||||||
|
assert_equal(json_obj['size'], 3)
|
||||||
|
# the size of the memory pool should be greater than 3x ~100 bytes
|
||||||
|
assert_greater_than(json_obj['bytes'], 300)
|
||||||
|
|
||||||
|
# check that there are our submitted transactions in the TX memory pool
|
||||||
|
json_string = http_get_call(url.hostname, url.port, '/rest/mempool/contents'+self.FORMAT_SEPARATOR+'json')
|
||||||
|
json_obj = json.loads(json_string)
|
||||||
|
for tx in txs:
|
||||||
|
assert_equal(tx in json_obj, True)
|
||||||
|
|
||||||
# now mine the transactions
|
# now mine the transactions
|
||||||
newblockhash = self.nodes[1].generate(1)
|
newblockhash = self.nodes[1].generate(1)
|
||||||
self.sync_all()
|
self.sync_all()
|
||||||
|
|||||||
@@ -115,6 +115,7 @@ BITCOIN_CORE_H = \
|
|||||||
consensus/params.h \
|
consensus/params.h \
|
||||||
consensus/validation.h \
|
consensus/validation.h \
|
||||||
core_io.h \
|
core_io.h \
|
||||||
|
core_memusage.h \
|
||||||
eccryptoverify.h \
|
eccryptoverify.h \
|
||||||
ecwrapper.h \
|
ecwrapper.h \
|
||||||
hash.h \
|
hash.h \
|
||||||
|
|||||||
@@ -101,7 +101,7 @@ CCoinsMap::const_iterator CCoinsViewCache::FetchCoins(const uint256 &txid) const
|
|||||||
// version as fresh.
|
// version as fresh.
|
||||||
ret->second.flags = CCoinsCacheEntry::FRESH;
|
ret->second.flags = CCoinsCacheEntry::FRESH;
|
||||||
}
|
}
|
||||||
cachedCoinsUsage += memusage::DynamicUsage(ret->second.coins);
|
cachedCoinsUsage += ret->second.coins.DynamicMemoryUsage();
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -124,7 +124,7 @@ bool CCoinsViewCache::GetAnchorAt(const uint256 &rt, ZCIncrementalMerkleTree &tr
|
|||||||
CAnchorsMap::iterator ret = cacheAnchors.insert(std::make_pair(rt, CAnchorsCacheEntry())).first;
|
CAnchorsMap::iterator ret = cacheAnchors.insert(std::make_pair(rt, CAnchorsCacheEntry())).first;
|
||||||
ret->second.entered = true;
|
ret->second.entered = true;
|
||||||
ret->second.tree = tree;
|
ret->second.tree = tree;
|
||||||
cachedCoinsUsage += memusage::DynamicUsage(ret->second.tree);
|
cachedCoinsUsage += ret->second.tree.DynamicMemoryUsage();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -163,7 +163,7 @@ void CCoinsViewCache::PushAnchor(const ZCIncrementalMerkleTree &tree) {
|
|||||||
|
|
||||||
if (insertRet.second) {
|
if (insertRet.second) {
|
||||||
// An insert took place
|
// An insert took place
|
||||||
cachedCoinsUsage += memusage::DynamicUsage(ret->second.tree);
|
cachedCoinsUsage += ret->second.tree.DynamicMemoryUsage();
|
||||||
}
|
}
|
||||||
|
|
||||||
hashAnchor = newrt;
|
hashAnchor = newrt;
|
||||||
@@ -224,7 +224,7 @@ CCoinsModifier CCoinsViewCache::ModifyCoins(const uint256 &txid) {
|
|||||||
ret.first->second.flags = CCoinsCacheEntry::FRESH;
|
ret.first->second.flags = CCoinsCacheEntry::FRESH;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
cachedCoinUsage = memusage::DynamicUsage(ret.first->second.coins);
|
cachedCoinUsage = ret.first->second.coins.DynamicMemoryUsage();
|
||||||
}
|
}
|
||||||
// Assume that whenever ModifyCoins is called, the entry will be modified.
|
// Assume that whenever ModifyCoins is called, the entry will be modified.
|
||||||
ret.first->second.flags |= CCoinsCacheEntry::DIRTY;
|
ret.first->second.flags |= CCoinsCacheEntry::DIRTY;
|
||||||
@@ -284,7 +284,7 @@ bool CCoinsViewCache::BatchWrite(CCoinsMap &mapCoins,
|
|||||||
assert(it->second.flags & CCoinsCacheEntry::FRESH);
|
assert(it->second.flags & CCoinsCacheEntry::FRESH);
|
||||||
CCoinsCacheEntry& entry = cacheCoins[it->first];
|
CCoinsCacheEntry& entry = cacheCoins[it->first];
|
||||||
entry.coins.swap(it->second.coins);
|
entry.coins.swap(it->second.coins);
|
||||||
cachedCoinsUsage += memusage::DynamicUsage(entry.coins);
|
cachedCoinsUsage += entry.coins.DynamicMemoryUsage();
|
||||||
entry.flags = CCoinsCacheEntry::DIRTY | CCoinsCacheEntry::FRESH;
|
entry.flags = CCoinsCacheEntry::DIRTY | CCoinsCacheEntry::FRESH;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -292,13 +292,13 @@ bool CCoinsViewCache::BatchWrite(CCoinsMap &mapCoins,
|
|||||||
// The grandparent does not have an entry, and the child is
|
// The grandparent does not have an entry, and the child is
|
||||||
// modified and being pruned. This means we can just delete
|
// modified and being pruned. This means we can just delete
|
||||||
// it from the parent.
|
// it from the parent.
|
||||||
cachedCoinsUsage -= memusage::DynamicUsage(itUs->second.coins);
|
cachedCoinsUsage -= itUs->second.coins.DynamicMemoryUsage();
|
||||||
cacheCoins.erase(itUs);
|
cacheCoins.erase(itUs);
|
||||||
} else {
|
} else {
|
||||||
// A normal modification.
|
// A normal modification.
|
||||||
cachedCoinsUsage -= memusage::DynamicUsage(itUs->second.coins);
|
cachedCoinsUsage -= itUs->second.coins.DynamicMemoryUsage();
|
||||||
itUs->second.coins.swap(it->second.coins);
|
itUs->second.coins.swap(it->second.coins);
|
||||||
cachedCoinsUsage += memusage::DynamicUsage(itUs->second.coins);
|
cachedCoinsUsage += itUs->second.coins.DynamicMemoryUsage();
|
||||||
itUs->second.flags |= CCoinsCacheEntry::DIRTY;
|
itUs->second.flags |= CCoinsCacheEntry::DIRTY;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -318,7 +318,7 @@ bool CCoinsViewCache::BatchWrite(CCoinsMap &mapCoins,
|
|||||||
entry.tree = child_it->second.tree;
|
entry.tree = child_it->second.tree;
|
||||||
entry.flags = CAnchorsCacheEntry::DIRTY;
|
entry.flags = CAnchorsCacheEntry::DIRTY;
|
||||||
|
|
||||||
cachedCoinsUsage += memusage::DynamicUsage(entry.tree);
|
cachedCoinsUsage += entry.tree.DynamicMemoryUsage();
|
||||||
} else {
|
} else {
|
||||||
if (parent_it->second.entered != child_it->second.entered) {
|
if (parent_it->second.entered != child_it->second.entered) {
|
||||||
// The parent may have removed the entry.
|
// The parent may have removed the entry.
|
||||||
@@ -498,6 +498,6 @@ CCoinsModifier::~CCoinsModifier()
|
|||||||
cache.cacheCoins.erase(it);
|
cache.cacheCoins.erase(it);
|
||||||
} else {
|
} else {
|
||||||
// If the coin still exists after the modification, add the new usage
|
// If the coin still exists after the modification, add the new usage
|
||||||
cache.cachedCoinsUsage += memusage::DynamicUsage(it->second.coins);
|
cache.cachedCoinsUsage += it->second.coins.DynamicMemoryUsage();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
#define BITCOIN_COINS_H
|
#define BITCOIN_COINS_H
|
||||||
|
|
||||||
#include "compressor.h"
|
#include "compressor.h"
|
||||||
|
#include "core_memusage.h"
|
||||||
#include "memusage.h"
|
#include "memusage.h"
|
||||||
#include "serialize.h"
|
#include "serialize.h"
|
||||||
#include "uint256.h"
|
#include "uint256.h"
|
||||||
@@ -258,8 +259,7 @@ public:
|
|||||||
size_t DynamicMemoryUsage() const {
|
size_t DynamicMemoryUsage() const {
|
||||||
size_t ret = memusage::DynamicUsage(vout);
|
size_t ret = memusage::DynamicUsage(vout);
|
||||||
BOOST_FOREACH(const CTxOut &out, vout) {
|
BOOST_FOREACH(const CTxOut &out, vout) {
|
||||||
const std::vector<unsigned char> *script = &out.scriptPubKey;
|
ret += RecursiveDynamicUsage(out.scriptPubKey);
|
||||||
ret += memusage::DynamicUsage(*script);
|
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|||||||
62
src/core_memusage.h
Normal file
62
src/core_memusage.h
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
// Copyright (c) 2015 The Bitcoin Core developers
|
||||||
|
// Distributed under the MIT software license, see the accompanying
|
||||||
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
|
||||||
|
#ifndef BITCOIN_CORE_MEMUSAGE_H
|
||||||
|
#define BITCOIN_CORE_MEMUSAGE_H
|
||||||
|
|
||||||
|
#include "primitives/transaction.h"
|
||||||
|
#include "primitives/block.h"
|
||||||
|
#include "memusage.h"
|
||||||
|
|
||||||
|
static inline size_t RecursiveDynamicUsage(const CScript& script) {
|
||||||
|
return memusage::DynamicUsage(*static_cast<const std::vector<unsigned char>*>(&script));
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline size_t RecursiveDynamicUsage(const COutPoint& out) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline size_t RecursiveDynamicUsage(const CTxIn& in) {
|
||||||
|
return RecursiveDynamicUsage(in.scriptSig) + RecursiveDynamicUsage(in.prevout);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline size_t RecursiveDynamicUsage(const CTxOut& out) {
|
||||||
|
return RecursiveDynamicUsage(out.scriptPubKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline size_t RecursiveDynamicUsage(const CTransaction& tx) {
|
||||||
|
size_t mem = memusage::DynamicUsage(tx.vin) + memusage::DynamicUsage(tx.vout);
|
||||||
|
for (std::vector<CTxIn>::const_iterator it = tx.vin.begin(); it != tx.vin.end(); it++) {
|
||||||
|
mem += RecursiveDynamicUsage(*it);
|
||||||
|
}
|
||||||
|
for (std::vector<CTxOut>::const_iterator it = tx.vout.begin(); it != tx.vout.end(); it++) {
|
||||||
|
mem += RecursiveDynamicUsage(*it);
|
||||||
|
}
|
||||||
|
return mem;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline size_t RecursiveDynamicUsage(const CMutableTransaction& tx) {
|
||||||
|
size_t mem = memusage::DynamicUsage(tx.vin) + memusage::DynamicUsage(tx.vout);
|
||||||
|
for (std::vector<CTxIn>::const_iterator it = tx.vin.begin(); it != tx.vin.end(); it++) {
|
||||||
|
mem += RecursiveDynamicUsage(*it);
|
||||||
|
}
|
||||||
|
for (std::vector<CTxOut>::const_iterator it = tx.vout.begin(); it != tx.vout.end(); it++) {
|
||||||
|
mem += RecursiveDynamicUsage(*it);
|
||||||
|
}
|
||||||
|
return mem;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline size_t RecursiveDynamicUsage(const CBlock& block) {
|
||||||
|
size_t mem = memusage::DynamicUsage(block.vtx) + memusage::DynamicUsage(block.vMerkleTree);
|
||||||
|
for (std::vector<CTransaction>::const_iterator it = block.vtx.begin(); it != block.vtx.end(); it++) {
|
||||||
|
mem += RecursiveDynamicUsage(*it);
|
||||||
|
}
|
||||||
|
return mem;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline size_t RecursiveDynamicUsage(const CBlockLocator& locator) {
|
||||||
|
return memusage::DynamicUsage(locator.vHave);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // BITCOIN_CORE_MEMUSAGE_H
|
||||||
@@ -11,6 +11,7 @@
|
|||||||
#include <set>
|
#include <set>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#include <boost/foreach.hpp>
|
||||||
#include <boost/unordered_set.hpp>
|
#include <boost/unordered_set.hpp>
|
||||||
#include <boost/unordered_map.hpp>
|
#include <boost/unordered_map.hpp>
|
||||||
|
|
||||||
@@ -20,19 +21,27 @@ namespace memusage
|
|||||||
/** Compute the total memory used by allocating alloc bytes. */
|
/** Compute the total memory used by allocating alloc bytes. */
|
||||||
static size_t MallocUsage(size_t alloc);
|
static size_t MallocUsage(size_t alloc);
|
||||||
|
|
||||||
|
/** Dynamic memory usage for built-in types is zero. */
|
||||||
|
static inline size_t DynamicUsage(const int8_t& v) { return 0; }
|
||||||
|
static inline size_t DynamicUsage(const uint8_t& v) { return 0; }
|
||||||
|
static inline size_t DynamicUsage(const int16_t& v) { return 0; }
|
||||||
|
static inline size_t DynamicUsage(const uint16_t& v) { return 0; }
|
||||||
|
static inline size_t DynamicUsage(const int32_t& v) { return 0; }
|
||||||
|
static inline size_t DynamicUsage(const uint32_t& v) { return 0; }
|
||||||
|
static inline size_t DynamicUsage(const int64_t& v) { return 0; }
|
||||||
|
static inline size_t DynamicUsage(const uint64_t& v) { return 0; }
|
||||||
|
static inline size_t DynamicUsage(const float& v) { return 0; }
|
||||||
|
static inline size_t DynamicUsage(const double& v) { return 0; }
|
||||||
|
template<typename X> static inline size_t DynamicUsage(X * const &v) { return 0; }
|
||||||
|
template<typename X> static inline size_t DynamicUsage(const X * const &v) { return 0; }
|
||||||
|
|
||||||
/** Compute the memory used for dynamically allocated but owned data structures.
|
/** Compute the memory used for dynamically allocated but owned data structures.
|
||||||
* For generic data types, this is *not* recursive. DynamicUsage(vector<vector<int> >)
|
* For generic data types, this is *not* recursive. DynamicUsage(vector<vector<int> >)
|
||||||
* will compute the memory used for the vector<int>'s, but not for the ints inside.
|
* will compute the memory used for the vector<int>'s, but not for the ints inside.
|
||||||
* This is for efficiency reasons, as these functions are intended to be fast. If
|
* This is for efficiency reasons, as these functions are intended to be fast. If
|
||||||
* application data structures require more accurate inner accounting, they should
|
* application data structures require more accurate inner accounting, they should
|
||||||
* do the recursion themselves, or use more efficient caching + updating on modification.
|
* iterate themselves, or use more efficient caching + updating on modification.
|
||||||
*/
|
*/
|
||||||
template<typename X> static size_t DynamicUsage(const std::vector<X>& v);
|
|
||||||
template<typename X> static size_t DynamicUsage(const std::set<X>& s);
|
|
||||||
template<typename X, typename Y> static size_t DynamicUsage(const std::map<X, Y>& m);
|
|
||||||
template<typename X, typename Y> static size_t DynamicUsage(const boost::unordered_set<X, Y>& s);
|
|
||||||
template<typename X, typename Y, typename Z> static size_t DynamicUsage(const boost::unordered_map<X, Y, Z>& s);
|
|
||||||
template<typename X> static size_t DynamicUsage(const X& x);
|
|
||||||
|
|
||||||
static inline size_t MallocUsage(size_t alloc)
|
static inline size_t MallocUsage(size_t alloc)
|
||||||
{
|
{
|
||||||
@@ -98,14 +107,6 @@ static inline size_t DynamicUsage(const boost::unordered_map<X, Y, Z>& m)
|
|||||||
return MallocUsage(sizeof(boost_unordered_node<std::pair<const X, Y> >)) * m.size() + MallocUsage(sizeof(void*) * m.bucket_count());
|
return MallocUsage(sizeof(boost_unordered_node<std::pair<const X, Y> >)) * m.size() + MallocUsage(sizeof(void*) * m.bucket_count());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dispatch to class method as fallback
|
|
||||||
|
|
||||||
template<typename X>
|
|
||||||
static inline size_t DynamicUsage(const X& x)
|
|
||||||
{
|
|
||||||
return x.DynamicMemoryUsage();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
56
src/rest.cpp
56
src/rest.cpp
@@ -64,6 +64,8 @@ public:
|
|||||||
|
|
||||||
extern void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry);
|
extern void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry);
|
||||||
extern UniValue blockToJSON(const CBlock& block, const CBlockIndex* blockindex, bool txDetails = false);
|
extern UniValue blockToJSON(const CBlock& block, const CBlockIndex* blockindex, bool txDetails = false);
|
||||||
|
extern UniValue mempoolInfoToJSON();
|
||||||
|
extern UniValue mempoolToJSON(bool fVerbose = false);
|
||||||
extern void ScriptPubKeyToJSON(const CScript& scriptPubKey, UniValue& out, bool fIncludeHex);
|
extern void ScriptPubKeyToJSON(const CScript& scriptPubKey, UniValue& out, bool fIncludeHex);
|
||||||
extern UniValue blockheaderToJSON(const CBlockIndex* blockindex);
|
extern UniValue blockheaderToJSON(const CBlockIndex* blockindex);
|
||||||
|
|
||||||
@@ -292,6 +294,58 @@ static bool rest_chaininfo(AcceptedConnection* conn,
|
|||||||
return true; // continue to process further HTTP reqs on this cxn
|
return true; // continue to process further HTTP reqs on this cxn
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool rest_mempool_info(AcceptedConnection* conn,
|
||||||
|
const std::string& strURIPart,
|
||||||
|
const std::string& strRequest,
|
||||||
|
const std::map<std::string, std::string>& mapHeaders,
|
||||||
|
bool fRun)
|
||||||
|
{
|
||||||
|
vector<string> params;
|
||||||
|
const RetFormat rf = ParseDataFormat(params, strURIPart);
|
||||||
|
|
||||||
|
switch (rf) {
|
||||||
|
case RF_JSON: {
|
||||||
|
UniValue mempoolInfoObject = mempoolInfoToJSON();
|
||||||
|
|
||||||
|
string strJSON = mempoolInfoObject.write() + "\n";
|
||||||
|
conn->stream() << HTTPReply(HTTP_OK, strJSON, fRun) << std::flush;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
throw RESTERR(HTTP_NOT_FOUND, "output format not found (available: json)");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// not reached
|
||||||
|
return true; // continue to process further HTTP reqs on this cxn
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool rest_mempool_contents(AcceptedConnection* conn,
|
||||||
|
const std::string& strURIPart,
|
||||||
|
const std::string& strRequest,
|
||||||
|
const std::map<std::string, std::string>& mapHeaders,
|
||||||
|
bool fRun)
|
||||||
|
{
|
||||||
|
vector<string> params;
|
||||||
|
const RetFormat rf = ParseDataFormat(params, strURIPart);
|
||||||
|
|
||||||
|
switch (rf) {
|
||||||
|
case RF_JSON: {
|
||||||
|
UniValue mempoolObject = mempoolToJSON(true);
|
||||||
|
|
||||||
|
string strJSON = mempoolObject.write() + "\n";
|
||||||
|
conn->stream() << HTTPReply(HTTP_OK, strJSON, fRun) << std::flush;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
throw RESTERR(HTTP_NOT_FOUND, "output format not found (available: json)");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// not reached
|
||||||
|
return true; // continue to process further HTTP reqs on this cxn
|
||||||
|
}
|
||||||
|
|
||||||
static bool rest_tx(AcceptedConnection* conn,
|
static bool rest_tx(AcceptedConnection* conn,
|
||||||
const std::string& strURIPart,
|
const std::string& strURIPart,
|
||||||
const std::string& strRequest,
|
const std::string& strRequest,
|
||||||
@@ -552,6 +606,8 @@ static const struct {
|
|||||||
{"/rest/block/notxdetails/", rest_block_notxdetails},
|
{"/rest/block/notxdetails/", rest_block_notxdetails},
|
||||||
{"/rest/block/", rest_block_extended},
|
{"/rest/block/", rest_block_extended},
|
||||||
{"/rest/chaininfo", rest_chaininfo},
|
{"/rest/chaininfo", rest_chaininfo},
|
||||||
|
{"/rest/mempool/info", rest_mempool_info},
|
||||||
|
{"/rest/mempool/contents", rest_mempool_contents},
|
||||||
{"/rest/headers/", rest_headers},
|
{"/rest/headers/", rest_headers},
|
||||||
{"/rest/getutxos", rest_getutxos},
|
{"/rest/getutxos", rest_getutxos},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -191,45 +191,8 @@ UniValue getdifficulty(const UniValue& params, bool fHelp)
|
|||||||
return GetNetworkDifficulty();
|
return GetNetworkDifficulty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
UniValue mempoolToJSON(bool fVerbose = false)
|
||||||
UniValue getrawmempool(const UniValue& params, bool fHelp)
|
|
||||||
{
|
{
|
||||||
if (fHelp || params.size() > 1)
|
|
||||||
throw runtime_error(
|
|
||||||
"getrawmempool ( verbose )\n"
|
|
||||||
"\nReturns all transaction ids in memory pool as a json array of string transaction ids.\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", "true")
|
|
||||||
+ HelpExampleRpc("getrawmempool", "true")
|
|
||||||
);
|
|
||||||
|
|
||||||
LOCK(cs_main);
|
|
||||||
|
|
||||||
bool fVerbose = false;
|
|
||||||
if (params.size() > 0)
|
|
||||||
fVerbose = params[0].get_bool();
|
|
||||||
|
|
||||||
if (fVerbose)
|
if (fVerbose)
|
||||||
{
|
{
|
||||||
LOCK(mempool.cs);
|
LOCK(mempool.cs);
|
||||||
@@ -277,6 +240,47 @@ UniValue getrawmempool(const UniValue& params, bool fHelp)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
UniValue getrawmempool(const UniValue& params, bool fHelp)
|
||||||
|
{
|
||||||
|
if (fHelp || params.size() > 1)
|
||||||
|
throw runtime_error(
|
||||||
|
"getrawmempool ( verbose )\n"
|
||||||
|
"\nReturns all transaction ids in memory pool as a json array of string transaction ids.\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", "true")
|
||||||
|
+ HelpExampleRpc("getrawmempool", "true")
|
||||||
|
);
|
||||||
|
|
||||||
|
LOCK(cs_main);
|
||||||
|
|
||||||
|
bool fVerbose = false;
|
||||||
|
if (params.size() > 0)
|
||||||
|
fVerbose = params[0].get_bool();
|
||||||
|
|
||||||
|
return mempoolToJSON(fVerbose);
|
||||||
|
}
|
||||||
|
|
||||||
UniValue getblockhash(const UniValue& params, bool fHelp)
|
UniValue getblockhash(const UniValue& params, bool fHelp)
|
||||||
{
|
{
|
||||||
if (fHelp || params.size() != 1)
|
if (fHelp || params.size() != 1)
|
||||||
@@ -777,6 +781,16 @@ UniValue getchaintips(const UniValue& params, bool fHelp)
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
UniValue mempoolInfoToJSON()
|
||||||
|
{
|
||||||
|
UniValue ret(UniValue::VOBJ);
|
||||||
|
ret.push_back(Pair("size", (int64_t) mempool.size()));
|
||||||
|
ret.push_back(Pair("bytes", (int64_t) mempool.GetTotalTxSize()));
|
||||||
|
ret.push_back(Pair("usage", (int64_t) mempool.DynamicMemoryUsage()));
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
UniValue getmempoolinfo(const UniValue& params, bool fHelp)
|
UniValue getmempoolinfo(const UniValue& params, bool fHelp)
|
||||||
{
|
{
|
||||||
if (fHelp || params.size() != 0)
|
if (fHelp || params.size() != 0)
|
||||||
@@ -787,17 +801,14 @@ UniValue getmempoolinfo(const UniValue& params, bool fHelp)
|
|||||||
"{\n"
|
"{\n"
|
||||||
" \"size\": xxxxx (numeric) Current tx count\n"
|
" \"size\": xxxxx (numeric) Current tx count\n"
|
||||||
" \"bytes\": xxxxx (numeric) Sum of all tx sizes\n"
|
" \"bytes\": xxxxx (numeric) Sum of all tx sizes\n"
|
||||||
|
" \"usage\": xxxxx (numeric) Total memory usage for the mempool\n"
|
||||||
"}\n"
|
"}\n"
|
||||||
"\nExamples:\n"
|
"\nExamples:\n"
|
||||||
+ HelpExampleCli("getmempoolinfo", "")
|
+ HelpExampleCli("getmempoolinfo", "")
|
||||||
+ HelpExampleRpc("getmempoolinfo", "")
|
+ HelpExampleRpc("getmempoolinfo", "")
|
||||||
);
|
);
|
||||||
|
|
||||||
UniValue ret(UniValue::VOBJ);
|
return mempoolInfoToJSON();
|
||||||
ret.push_back(Pair("size", (int64_t) mempool.size()));
|
|
||||||
ret.push_back(Pair("bytes", (int64_t) mempool.GetTotalTxSize()));
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
UniValue invalidateblock(const UniValue& params, bool fHelp)
|
UniValue invalidateblock(const UniValue& params, bool fHelp)
|
||||||
|
|||||||
@@ -143,9 +143,9 @@ public:
|
|||||||
memusage::DynamicUsage(cacheAnchors) +
|
memusage::DynamicUsage(cacheAnchors) +
|
||||||
memusage::DynamicUsage(cacheNullifiers);
|
memusage::DynamicUsage(cacheNullifiers);
|
||||||
for (CCoinsMap::iterator it = cacheCoins.begin(); it != cacheCoins.end(); it++) {
|
for (CCoinsMap::iterator it = cacheCoins.begin(); it != cacheCoins.end(); it++) {
|
||||||
ret += memusage::DynamicUsage(it->second.coins);
|
ret += it->second.coins.DynamicMemoryUsage();
|
||||||
}
|
}
|
||||||
BOOST_CHECK_EQUAL(memusage::DynamicUsage(*this), ret);
|
BOOST_CHECK_EQUAL(DynamicMemoryUsage(), ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
CTxMemPoolEntry::CTxMemPoolEntry():
|
CTxMemPoolEntry::CTxMemPoolEntry():
|
||||||
nFee(0), nTxSize(0), nModSize(0), nTime(0), dPriority(0.0), hadNoDependencies(false)
|
nFee(0), nTxSize(0), nModSize(0), nUsageSize(0), nTime(0), dPriority(0.0), hadNoDependencies(false)
|
||||||
{
|
{
|
||||||
nHeight = MEMPOOL_HEIGHT;
|
nHeight = MEMPOOL_HEIGHT;
|
||||||
}
|
}
|
||||||
@@ -31,6 +31,7 @@ CTxMemPoolEntry::CTxMemPoolEntry(const CTransaction& _tx, const CAmount& _nFee,
|
|||||||
{
|
{
|
||||||
nTxSize = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION);
|
nTxSize = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION);
|
||||||
nModSize = tx.CalculateModifiedSize(nTxSize);
|
nModSize = tx.CalculateModifiedSize(nTxSize);
|
||||||
|
nUsageSize = RecursiveDynamicUsage(tx);
|
||||||
}
|
}
|
||||||
|
|
||||||
CTxMemPoolEntry::CTxMemPoolEntry(const CTxMemPoolEntry& other)
|
CTxMemPoolEntry::CTxMemPoolEntry(const CTxMemPoolEntry& other)
|
||||||
@@ -106,6 +107,7 @@ bool CTxMemPool::addUnchecked(const uint256& hash, const CTxMemPoolEntry &entry,
|
|||||||
}
|
}
|
||||||
nTransactionsUpdated++;
|
nTransactionsUpdated++;
|
||||||
totalTxSize += entry.GetTxSize();
|
totalTxSize += entry.GetTxSize();
|
||||||
|
cachedInnerUsage += entry.DynamicMemoryUsage();
|
||||||
minerPolicyEstimator->processTransaction(entry, fCurrentEstimate);
|
minerPolicyEstimator->processTransaction(entry, fCurrentEstimate);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -156,6 +158,7 @@ void CTxMemPool::remove(const CTransaction &origTx, std::list<CTransaction>& rem
|
|||||||
|
|
||||||
removed.push_back(tx);
|
removed.push_back(tx);
|
||||||
totalTxSize -= mapTx[hash].GetTxSize();
|
totalTxSize -= mapTx[hash].GetTxSize();
|
||||||
|
cachedInnerUsage -= mapTx[hash].DynamicMemoryUsage();
|
||||||
mapTx.erase(hash);
|
mapTx.erase(hash);
|
||||||
nTransactionsUpdated++;
|
nTransactionsUpdated++;
|
||||||
minerPolicyEstimator->removeTx(hash);
|
minerPolicyEstimator->removeTx(hash);
|
||||||
@@ -275,6 +278,7 @@ void CTxMemPool::clear()
|
|||||||
mapTx.clear();
|
mapTx.clear();
|
||||||
mapNextTx.clear();
|
mapNextTx.clear();
|
||||||
totalTxSize = 0;
|
totalTxSize = 0;
|
||||||
|
cachedInnerUsage = 0;
|
||||||
++nTransactionsUpdated;
|
++nTransactionsUpdated;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -286,6 +290,7 @@ void CTxMemPool::check(const 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());
|
||||||
|
|
||||||
uint64_t checkTotal = 0;
|
uint64_t checkTotal = 0;
|
||||||
|
uint64_t innerUsage = 0;
|
||||||
|
|
||||||
CCoinsViewCache mempoolDuplicate(const_cast<CCoinsViewCache*>(pcoins));
|
CCoinsViewCache mempoolDuplicate(const_cast<CCoinsViewCache*>(pcoins));
|
||||||
|
|
||||||
@@ -294,6 +299,7 @@ void CTxMemPool::check(const CCoinsViewCache *pcoins) const
|
|||||||
for (std::map<uint256, CTxMemPoolEntry>::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;
|
||||||
checkTotal += it->second.GetTxSize();
|
checkTotal += it->second.GetTxSize();
|
||||||
|
innerUsage += it->second.DynamicMemoryUsage();
|
||||||
const CTransaction& tx = it->second.GetTx();
|
const CTransaction& tx = it->second.GetTx();
|
||||||
bool fDependsWait = false;
|
bool fDependsWait = false;
|
||||||
BOOST_FOREACH(const CTxIn &txin, tx.vin) {
|
BOOST_FOREACH(const CTxIn &txin, tx.vin) {
|
||||||
@@ -379,6 +385,7 @@ void CTxMemPool::check(const CCoinsViewCache *pcoins) const
|
|||||||
}
|
}
|
||||||
|
|
||||||
assert(totalTxSize == checkTotal);
|
assert(totalTxSize == checkTotal);
|
||||||
|
assert(innerUsage == cachedInnerUsage);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CTxMemPool::queryHashes(vector<uint256>& vtxid)
|
void CTxMemPool::queryHashes(vector<uint256>& vtxid)
|
||||||
@@ -506,3 +513,8 @@ bool CCoinsViewMemPool::GetCoins(const uint256 &txid, CCoins &coins) const {
|
|||||||
bool CCoinsViewMemPool::HaveCoins(const uint256 &txid) const {
|
bool CCoinsViewMemPool::HaveCoins(const uint256 &txid) const {
|
||||||
return mempool.exists(txid) || base->HaveCoins(txid);
|
return mempool.exists(txid) || base->HaveCoins(txid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t CTxMemPool::DynamicMemoryUsage() const {
|
||||||
|
LOCK(cs);
|
||||||
|
return memusage::DynamicUsage(mapTx) + memusage::DynamicUsage(mapNextTx) + memusage::DynamicUsage(mapDeltas) + cachedInnerUsage;
|
||||||
|
}
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ private:
|
|||||||
CAmount nFee; //! Cached to avoid expensive parent-transaction lookups
|
CAmount nFee; //! Cached to avoid expensive parent-transaction lookups
|
||||||
size_t nTxSize; //! ... and avoid recomputing tx size
|
size_t nTxSize; //! ... and avoid recomputing tx size
|
||||||
size_t nModSize; //! ... and modified size for priority
|
size_t nModSize; //! ... and modified size for priority
|
||||||
|
size_t nUsageSize; //! ... and total memory usage
|
||||||
int64_t nTime; //! Local time when entering the mempool
|
int64_t nTime; //! Local time when entering the mempool
|
||||||
double dPriority; //! Priority when entering the mempool
|
double dPriority; //! Priority when entering the mempool
|
||||||
unsigned int nHeight; //! Chain height when entering the mempool
|
unsigned int nHeight; //! Chain height when entering the mempool
|
||||||
@@ -58,6 +59,7 @@ public:
|
|||||||
int64_t GetTime() const { return nTime; }
|
int64_t GetTime() const { return nTime; }
|
||||||
unsigned int GetHeight() const { return nHeight; }
|
unsigned int GetHeight() const { return nHeight; }
|
||||||
bool WasClearAtEntry() const { return hadNoDependencies; }
|
bool WasClearAtEntry() const { return hadNoDependencies; }
|
||||||
|
size_t DynamicMemoryUsage() const { return nUsageSize; }
|
||||||
};
|
};
|
||||||
|
|
||||||
class CBlockPolicyEstimator;
|
class CBlockPolicyEstimator;
|
||||||
@@ -73,6 +75,7 @@ public:
|
|||||||
CInPoint(const CTransaction* ptxIn, uint32_t nIn) { ptx = ptxIn; n = nIn; }
|
CInPoint(const CTransaction* ptxIn, uint32_t nIn) { ptx = ptxIn; n = nIn; }
|
||||||
void SetNull() { ptx = NULL; n = (uint32_t) -1; }
|
void SetNull() { ptx = NULL; n = (uint32_t) -1; }
|
||||||
bool IsNull() const { return (ptx == NULL && n == (uint32_t) -1); }
|
bool IsNull() const { return (ptx == NULL && n == (uint32_t) -1); }
|
||||||
|
size_t DynamicMemoryUsage() const { return 0; }
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -93,6 +96,7 @@ private:
|
|||||||
CBlockPolicyEstimator* minerPolicyEstimator;
|
CBlockPolicyEstimator* minerPolicyEstimator;
|
||||||
|
|
||||||
uint64_t totalTxSize = 0; //! sum of all mempool tx' byte sizes
|
uint64_t totalTxSize = 0; //! sum of all mempool tx' byte sizes
|
||||||
|
uint64_t cachedInnerUsage; //! sum of dynamic memory usage of all the map elements (NOT the maps themselves)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
mutable CCriticalSection cs;
|
mutable CCriticalSection cs;
|
||||||
@@ -141,6 +145,7 @@ public:
|
|||||||
LOCK(cs);
|
LOCK(cs);
|
||||||
return mapTx.size();
|
return mapTx.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t GetTotalTxSize()
|
uint64_t GetTotalTxSize()
|
||||||
{
|
{
|
||||||
LOCK(cs);
|
LOCK(cs);
|
||||||
@@ -164,6 +169,8 @@ public:
|
|||||||
/** Write/Read estimates to disk */
|
/** Write/Read estimates to disk */
|
||||||
bool WriteFeeEstimates(CAutoFile& fileout) const;
|
bool WriteFeeEstimates(CAutoFile& fileout) const;
|
||||||
bool ReadFeeEstimates(CAutoFile& filein);
|
bool ReadFeeEstimates(CAutoFile& filein);
|
||||||
|
|
||||||
|
size_t DynamicMemoryUsage() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user