aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am7
-rw-r--r--src/amount.h1
-rw-r--r--src/bitcoin-cli.cpp12
-rw-r--r--src/bitcoin-tx.cpp11
-rw-r--r--src/bitcoind.cpp8
-rw-r--r--src/chainparams.cpp33
-rw-r--r--src/chainparams.h15
-rw-r--r--src/chainparamsbase.cpp47
-rw-r--r--src/chainparamsbase.h31
-rw-r--r--src/compat.h1
-rw-r--r--src/consensus/params.h1
-rw-r--r--src/httpserver.cpp4
-rw-r--r--src/init.cpp13
-rw-r--r--src/leveldbwrapper.cpp9
-rw-r--r--src/leveldbwrapper.h82
-rw-r--r--src/main.cpp67
-rw-r--r--src/main.h6
-rw-r--r--src/net.cpp12
-rw-r--r--src/netbase.cpp9
-rw-r--r--src/pow.cpp3
-rw-r--r--src/qt/bitcoin.cpp8
-rw-r--r--src/rpcrawtransaction.cpp2
-rw-r--r--src/test/README.md2
-rw-r--r--src/test/data/tx_invalid.json4
-rw-r--r--src/test/data/tx_valid.json4
-rw-r--r--src/test/leveldbwrapper_tests.cpp81
-rw-r--r--src/test/mempool_tests.cpp199
-rw-r--r--src/test/test_bitcoin.cpp6
-rw-r--r--src/test/test_bitcoin.h4
-rw-r--r--src/test/transaction_tests.cpp2
-rw-r--r--src/test/txvalidationcache_tests.cpp2
-rw-r--r--src/txdb.cpp67
-rw-r--r--src/txmempool.cpp92
-rw-r--r--src/txmempool.h48
-rw-r--r--src/univalue/.gitignore9
-rw-r--r--src/univalue/Makefile.am10
-rw-r--r--src/univalue/build-aux/m4/.empty0
-rw-r--r--src/univalue/build-aux/m4/.gitignore1
-rw-r--r--src/univalue/lib/.gitignore8
-rw-r--r--src/univalue/test/.gitignore6
-rw-r--r--src/wallet/wallet.cpp2
41 files changed, 700 insertions, 229 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index 0654ff422..0a16a863e 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -31,13 +31,13 @@ LIBBITCOIN_UTIL=libbitcoin_util.a
LIBBITCOIN_CRYPTO=crypto/libbitcoin_crypto.a
LIBBITCOINQT=qt/libbitcoinqt.a
LIBSECP256K1=secp256k1/libsecp256k1.la
-LIBUNIVALUE=univalue/lib/libunivalue.la
+LIBUNIVALUE=univalue/libunivalue.la
$(LIBSECP256K1): $(wildcard secp256k1/src/*) $(wildcard secp256k1/include/*)
$(AM_V_at)$(MAKE) $(AM_MAKEFLAGS) -C $(@D) $(@F)
-$(LIBUNIVALUE): $(wildcard univalue/lib/*)
- $(AM_V_at)$(MAKE) $(AM_MAKEFLAGS) -C univalue/
+$(LIBUNIVALUE): $(wildcard univalue/lib/*) $(wildcard univalue/include/*)
+ $(AM_V_at)$(MAKE) $(AM_MAKEFLAGS) -C $(@D) $(@F)
# Make is not made aware of per-object dependencies to avoid limiting building parallelization
# But to build the less dependent modules first, we manually select their order here:
@@ -421,6 +421,7 @@ EXTRA_DIST = leveldb
clean-local:
-$(MAKE) -C leveldb clean
-$(MAKE) -C secp256k1 clean
+ -$(MAKE) -C univalue clean
rm -f leveldb/*/*.gcno leveldb/helpers/memenv/*.gcno
-rm -f config.h
diff --git a/src/amount.h b/src/amount.h
index 90e6b5aa8..a4c7764cd 100644
--- a/src/amount.h
+++ b/src/amount.h
@@ -51,6 +51,7 @@ public:
friend bool operator==(const CFeeRate& a, const CFeeRate& b) { return a.nSatoshisPerK == b.nSatoshisPerK; }
friend bool operator<=(const CFeeRate& a, const CFeeRate& b) { return a.nSatoshisPerK <= b.nSatoshisPerK; }
friend bool operator>=(const CFeeRate& a, const CFeeRate& b) { return a.nSatoshisPerK >= b.nSatoshisPerK; }
+ CFeeRate& operator+=(const CFeeRate& a) { nSatoshisPerK += a.nSatoshisPerK; return *this; }
std::string ToString() const;
ADD_SERIALIZE_METHODS;
diff --git a/src/bitcoin-cli.cpp b/src/bitcoin-cli.cpp
index e0fe6aa5b..6f22c7049 100644
--- a/src/bitcoin-cli.cpp
+++ b/src/bitcoin-cli.cpp
@@ -31,9 +31,7 @@ std::string HelpMessageCli()
strUsage += HelpMessageOpt("-?", _("This help message"));
strUsage += HelpMessageOpt("-conf=<file>", strprintf(_("Specify configuration file (default: %s)"), "bitcoin.conf"));
strUsage += HelpMessageOpt("-datadir=<dir>", _("Specify data directory"));
- strUsage += HelpMessageOpt("-testnet", _("Use the test network"));
- strUsage += HelpMessageOpt("-regtest", _("Enter regression test mode, which uses a special chain in which blocks can be "
- "solved instantly. This is intended for regression testing tools and app development."));
+ AppendParamsHelpMessages(strUsage);
strUsage += HelpMessageOpt("-rpcconnect=<ip>", strprintf(_("Send commands to node running on <ip> (default: %s)"), "127.0.0.1"));
strUsage += HelpMessageOpt("-rpcport=<port>", strprintf(_("Connect to JSON-RPC on <port> (default: %u or testnet: %u)"), 8332, 18332));
strUsage += HelpMessageOpt("-rpcwait", _("Wait for RPC server to start"));
@@ -69,7 +67,7 @@ static bool AppInitRPC(int argc, char* argv[])
// Parameters
//
ParseParameters(argc, argv);
- if (argc<2 || mapArgs.count("-?") || mapArgs.count("-help") || mapArgs.count("-version")) {
+ if (argc<2 || mapArgs.count("-?") || mapArgs.count("-h") || mapArgs.count("-help") || mapArgs.count("-version")) {
std::string strUsage = _("Bitcoin Core RPC client version") + " " + FormatFullVersion() + "\n";
if (!mapArgs.count("-version")) {
strUsage += "\n" + _("Usage:") + "\n" +
@@ -94,8 +92,10 @@ static bool AppInitRPC(int argc, char* argv[])
return false;
}
// Check for -testnet or -regtest parameter (BaseParams() calls are only valid after this clause)
- if (!SelectBaseParamsFromCommandLine()) {
- fprintf(stderr, "Error: Invalid combination of -regtest and -testnet.\n");
+ try {
+ SelectBaseParams(ChainNameFromCommandLine());
+ } catch(std::exception &e) {
+ fprintf(stderr, "Error: %s\n", e.what());
return false;
}
if (GetBoolArg("-rpcssl", false))
diff --git a/src/bitcoin-tx.cpp b/src/bitcoin-tx.cpp
index f7518fab5..3330fe5d1 100644
--- a/src/bitcoin-tx.cpp
+++ b/src/bitcoin-tx.cpp
@@ -35,14 +35,16 @@ static bool AppInitRawTx(int argc, char* argv[])
ParseParameters(argc, argv);
// Check for -testnet or -regtest parameter (Params() calls are only valid after this clause)
- if (!SelectParamsFromCommandLine()) {
- fprintf(stderr, "Error: Invalid combination of -regtest and -testnet.\n");
+ try {
+ SelectParams(ChainNameFromCommandLine());
+ } catch(std::exception &e) {
+ fprintf(stderr, "Error: %s\n", e.what());
return false;
}
fCreateBlank = GetBoolArg("-create", false);
- if (argc<2 || mapArgs.count("-?") || mapArgs.count("-help"))
+ if (argc<2 || mapArgs.count("-?") || mapArgs.count("-h") || mapArgs.count("-help"))
{
// First part of help message is specific to this utility
std::string strUsage = _("Bitcoin Core bitcoin-tx utility version") + " " + FormatFullVersion() + "\n\n" +
@@ -58,8 +60,7 @@ static bool AppInitRawTx(int argc, char* argv[])
strUsage += HelpMessageOpt("-create", _("Create new, empty TX."));
strUsage += HelpMessageOpt("-json", _("Select JSON output"));
strUsage += HelpMessageOpt("-txid", _("Output only the hex-encoded transaction id of the resultant transaction."));
- strUsage += HelpMessageOpt("-regtest", _("Enter regression test mode, which uses a special chain in which blocks can be solved instantly."));
- strUsage += HelpMessageOpt("-testnet", _("Use the test network"));
+ AppendParamsHelpMessages(strUsage);
fprintf(stdout, "%s", strUsage.c_str());
diff --git a/src/bitcoind.cpp b/src/bitcoind.cpp
index b512f74c2..d2af89724 100644
--- a/src/bitcoind.cpp
+++ b/src/bitcoind.cpp
@@ -72,7 +72,7 @@ bool AppInit(int argc, char* argv[])
ParseParameters(argc, argv);
// Process help and version before taking care about datadir
- if (mapArgs.count("-?") || mapArgs.count("-help") || mapArgs.count("-version"))
+ if (mapArgs.count("-?") || mapArgs.count("-h") || mapArgs.count("-help") || mapArgs.count("-version"))
{
std::string strUsage = _("Bitcoin Core Daemon") + " " + _("version") + " " + FormatFullVersion() + "\n";
@@ -107,8 +107,10 @@ bool AppInit(int argc, char* argv[])
return false;
}
// Check for -testnet or -regtest parameter (Params() calls are only valid after this clause)
- if (!SelectParamsFromCommandLine()) {
- fprintf(stderr, "Error: Invalid combination of -regtest and -testnet.\n");
+ try {
+ SelectParams(ChainNameFromCommandLine());
+ } catch(std::exception &e) {
+ fprintf(stderr, "Error: %s\n", e.what());
return false;
}
diff --git a/src/chainparams.cpp b/src/chainparams.cpp
index 430b75683..dd26c3b31 100644
--- a/src/chainparams.cpp
+++ b/src/chainparams.cpp
@@ -5,6 +5,7 @@
#include "chainparams.h"
+#include "tinyformat.h"
#include "util.h"
#include "utilstrencodings.h"
@@ -76,6 +77,7 @@ public:
consensus.nPowTargetTimespan = 14 * 24 * 60 * 60; // two weeks
consensus.nPowTargetSpacing = 10 * 60;
consensus.fPowAllowMinDifficultyBlocks = false;
+ consensus.fPowNoRetargeting = false;
/**
* The message start string is designed to be unlikely to occur in normal data.
* The characters are rarely used upper ASCII, not valid as UTF-8, and produce
@@ -155,6 +157,7 @@ public:
consensus.nPowTargetTimespan = 14 * 24 * 60 * 60; // two weeks
consensus.nPowTargetSpacing = 10 * 60;
consensus.fPowAllowMinDifficultyBlocks = true;
+ consensus.fPowNoRetargeting = false;
pchMessageStart[0] = 0x0b;
pchMessageStart[1] = 0x11;
pchMessageStart[2] = 0x09;
@@ -217,6 +220,7 @@ public:
consensus.nPowTargetTimespan = 14 * 24 * 60 * 60; // two weeks
consensus.nPowTargetSpacing = 10 * 60;
consensus.fPowAllowMinDifficultyBlocks = true;
+ consensus.fPowNoRetargeting = true;
pchMessageStart[0] = 0xfa;
pchMessageStart[1] = 0xbf;
@@ -263,31 +267,20 @@ const CChainParams &Params() {
return *pCurrentParams;
}
-CChainParams &Params(CBaseChainParams::Network network) {
- switch (network) {
- case CBaseChainParams::MAIN:
+CChainParams& Params(const std::string& chain)
+{
+ if (chain == CBaseChainParams::MAIN)
return mainParams;
- case CBaseChainParams::TESTNET:
+ else if (chain == CBaseChainParams::TESTNET)
return testNetParams;
- case CBaseChainParams::REGTEST:
+ else if (chain == CBaseChainParams::REGTEST)
return regTestParams;
- default:
- assert(false && "Unimplemented network");
- return mainParams;
- }
+ else
+ throw std::runtime_error(strprintf("%s: Unknown chain %s.", __func__, chain));
}
-void SelectParams(CBaseChainParams::Network network) {
+void SelectParams(const std::string& network)
+{
SelectBaseParams(network);
pCurrentParams = &Params(network);
}
-
-bool SelectParamsFromCommandLine()
-{
- CBaseChainParams::Network network = NetworkIdFromCommandLine();
- if (network == CBaseChainParams::MAX_NETWORK_TYPES)
- return false;
-
- SelectParams(network);
- return true;
-}
diff --git a/src/chainparams.h b/src/chainparams.h
index 342bccb12..cb061d596 100644
--- a/src/chainparams.h
+++ b/src/chainparams.h
@@ -105,16 +105,15 @@ protected:
*/
const CChainParams &Params();
-/** Return parameters for the given network. */
-CChainParams &Params(CBaseChainParams::Network network);
-
-/** Sets the params returned by Params() to those for the given network. */
-void SelectParams(CBaseChainParams::Network network);
+/**
+ * @returns CChainParams for the given BIP70 chain name.
+ */
+CChainParams& Params(const std::string& chain);
/**
- * Looks for -regtest or -testnet and then calls SelectParams as appropriate.
- * Returns false if an invalid combination is given.
+ * Sets the params returned by Params() to those for the given BIP70 chain name.
+ * @throws std::runtime_error when the chain is not supported.
*/
-bool SelectParamsFromCommandLine();
+void SelectParams(const std::string& chain);
#endif // BITCOIN_CHAINPARAMS_H
diff --git a/src/chainparamsbase.cpp b/src/chainparamsbase.cpp
index 9c87bf215..db2dc751f 100644
--- a/src/chainparamsbase.cpp
+++ b/src/chainparamsbase.cpp
@@ -5,10 +5,25 @@
#include "chainparamsbase.h"
+#include "tinyformat.h"
#include "util.h"
#include <assert.h>
+const std::string CBaseChainParams::MAIN = "main";
+const std::string CBaseChainParams::TESTNET = "test";
+const std::string CBaseChainParams::REGTEST = "regtest";
+
+void AppendParamsHelpMessages(std::string& strUsage, bool debugHelp)
+{
+ strUsage += HelpMessageGroup(_("Chain selection options:"));
+ strUsage += HelpMessageOpt("-testnet", _("Use the test chain"));
+ if (debugHelp) {
+ strUsage += HelpMessageOpt("-regtest", "Enter regression test mode, which uses a special chain in which blocks can be solved instantly. "
+ "This is intended for regression testing tools and app development.");
+ }
+}
+
/**
* Main network
*/
@@ -71,31 +86,25 @@ const CBaseChainParams& BaseParams()
return *pCurrentBaseParams;
}
-void SelectBaseParams(CBaseChainParams::Network network)
+void SelectBaseParams(const std::string& chain)
{
- switch (network) {
- case CBaseChainParams::MAIN:
+ if (chain == CBaseChainParams::MAIN)
pCurrentBaseParams = &mainParams;
- break;
- case CBaseChainParams::TESTNET:
+ else if (chain == CBaseChainParams::TESTNET)
pCurrentBaseParams = &testNetParams;
- break;
- case CBaseChainParams::REGTEST:
+ else if (chain == CBaseChainParams::REGTEST)
pCurrentBaseParams = &regTestParams;
- break;
- default:
- assert(false && "Unimplemented network");
- return;
- }
+ else
+ throw std::runtime_error(strprintf("%s: Unknown chain %s.", __func__, chain));
}
-CBaseChainParams::Network NetworkIdFromCommandLine()
+std::string ChainNameFromCommandLine()
{
bool fRegTest = GetBoolArg("-regtest", false);
bool fTestNet = GetBoolArg("-testnet", false);
if (fTestNet && fRegTest)
- return CBaseChainParams::MAX_NETWORK_TYPES;
+ throw std::runtime_error("Invalid combination of -regtest and -testnet.");
if (fRegTest)
return CBaseChainParams::REGTEST;
if (fTestNet)
@@ -103,16 +112,6 @@ CBaseChainParams::Network NetworkIdFromCommandLine()
return CBaseChainParams::MAIN;
}
-bool SelectBaseParamsFromCommandLine()
-{
- CBaseChainParams::Network network = NetworkIdFromCommandLine();
- if (network == CBaseChainParams::MAX_NETWORK_TYPES)
- return false;
-
- SelectBaseParams(network);
- return true;
-}
-
bool AreBaseParamsConfigured()
{
return pCurrentBaseParams != NULL;
diff --git a/src/chainparamsbase.h b/src/chainparamsbase.h
index 4369d0aef..095c4cbdc 100644
--- a/src/chainparamsbase.h
+++ b/src/chainparamsbase.h
@@ -15,13 +15,10 @@
class CBaseChainParams
{
public:
- enum Network {
- MAIN,
- TESTNET,
- REGTEST,
-
- MAX_NETWORK_TYPES
- };
+ /** BIP70 chain name strings (main, test or regtest) */
+ static const std::string MAIN;
+ static const std::string TESTNET;
+ static const std::string REGTEST;
const std::string& DataDir() const { return strDataDir; }
int RPCPort() const { return nRPCPort; }
@@ -34,25 +31,25 @@ protected:
};
/**
+ * Append the help messages for the chainparams options to the
+ * parameter string.
+ */
+void AppendParamsHelpMessages(std::string& strUsage, bool debugHelp=true);
+
+/**
* Return the currently selected parameters. This won't change after app
* startup, except for unit tests.
*/
const CBaseChainParams& BaseParams();
/** Sets the params returned by Params() to those for the given network. */
-void SelectBaseParams(CBaseChainParams::Network network);
-
-/**
- * Looks for -regtest or -testnet and returns the appropriate Network ID.
- * Returns MAX_NETWORK_TYPES if an invalid combination is given.
- */
-CBaseChainParams::Network NetworkIdFromCommandLine();
+void SelectBaseParams(const std::string& chain);
/**
- * Calls NetworkIdFromCommandLine() and then calls SelectParams as appropriate.
- * Returns false if an invalid combination is given.
+ * Looks for -regtest, -testnet and returns the appropriate BIP70 chain name.
+ * @return CBaseChainParams::MAX_NETWORK_TYPES if an invalid combination is given. CBaseChainParams::MAIN by default.
*/
-bool SelectBaseParamsFromCommandLine();
+std::string ChainNameFromCommandLine();
/**
* Return true if SelectBaseParamsFromCommandLine() has been called to select
diff --git a/src/compat.h b/src/compat.h
index 5378c2c76..20c2a2514 100644
--- a/src/compat.h
+++ b/src/compat.h
@@ -38,6 +38,7 @@
#include <sys/types.h>
#include <net/if.h>
#include <netinet/in.h>
+#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <ifaddrs.h>
#include <limits.h>
diff --git a/src/consensus/params.h b/src/consensus/params.h
index c480a1cce..efbbbed35 100644
--- a/src/consensus/params.h
+++ b/src/consensus/params.h
@@ -22,6 +22,7 @@ struct Params {
/** Proof of work parameters */
uint256 powLimit;
bool fPowAllowMinDifficultyBlocks;
+ bool fPowNoRetargeting;
int64_t nPowTargetSpacing;
int64_t nPowTargetTimespan;
int64_t DifficultyAdjustmentInterval() const { return nPowTargetTimespan / nPowTargetSpacing; }
diff --git a/src/httpserver.cpp b/src/httpserver.cpp
index 0a7f903e9..8698abb90 100644
--- a/src/httpserver.cpp
+++ b/src/httpserver.cpp
@@ -38,6 +38,9 @@
#include <boost/foreach.hpp>
#include <boost/scoped_ptr.hpp>
+/** Maximum size of http request (request line + headers) */
+static const size_t MAX_HEADERS_SIZE = 8192;
+
/** HTTP request work item */
class HTTPWorkItem : public HTTPClosure
{
@@ -414,6 +417,7 @@ bool InitHTTPServer()
}
evhttp_set_timeout(http, GetArg("-rpcservertimeout", DEFAULT_HTTP_SERVER_TIMEOUT));
+ evhttp_set_max_headers_size(http, MAX_HEADERS_SIZE);
evhttp_set_max_body_size(http, MAX_SIZE);
evhttp_set_gencb(http, http_request_cb, NULL);
diff --git a/src/init.cpp b/src/init.cpp
index a079dce5b..5c961a3ad 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -320,6 +320,8 @@ std::string HelpMessage(HelpMessageMode mode)
strUsage += HelpMessageOpt("-dbcache=<n>", strprintf(_("Set database cache size in megabytes (%d to %d, default: %d)"), nMinDbCache, nMaxDbCache, nDefaultDbCache));
strUsage += HelpMessageOpt("-loadblock=<file>", _("Imports blocks from external blk000??.dat file") + " " + _("on startup"));
strUsage += HelpMessageOpt("-maxorphantx=<n>", strprintf(_("Keep at most <n> unconnectable transactions in memory (default: %u)"), DEFAULT_MAX_ORPHAN_TRANSACTIONS));
+ strUsage += HelpMessageOpt("-maxmempool=<n>", strprintf(_("Keep the transaction memory pool below <n> megabytes (default: %u)"), DEFAULT_MAX_MEMPOOL_SIZE));
+ strUsage += HelpMessageOpt("-mempoolexpiry=<n>", strprintf(_("Do not keep transactions in the mempool longer than <n> hours (default: %u)"), DEFAULT_MEMPOOL_EXPIRY));
strUsage += HelpMessageOpt("-par=<n>", strprintf(_("Set the number of script verification threads (%u to %d, 0 = auto, <0 = leave that many cores free, default: %d)"),
-GetNumCores(), MAX_SCRIPTCHECK_THREADS, DEFAULT_SCRIPTCHECK_THREADS));
#ifndef WIN32
@@ -439,11 +441,10 @@ std::string HelpMessage(HelpMessageMode mode)
{
strUsage += HelpMessageOpt("-printpriority", strprintf("Log transaction priority and fee per kB when mining blocks (default: %u)", 0));
strUsage += HelpMessageOpt("-privdb", strprintf("Sets the DB_PRIVATE flag in the wallet db environment (default: %u)", 1));
- strUsage += HelpMessageOpt("-regtest", "Enter regression test mode, which uses a special chain in which blocks can be solved instantly. "
- "This is intended for regression testing tools and app development.");
}
strUsage += HelpMessageOpt("-shrinkdebugfile", _("Shrink debug.log file on client startup (default: 1 when no -debug)"));
- strUsage += HelpMessageOpt("-testnet", _("Use the test network"));
+
+ AppendParamsHelpMessages(strUsage, showDebug);
strUsage += HelpMessageGroup(_("Node relay options:"));
if (showDebug)
@@ -841,6 +842,12 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
fCheckBlockIndex = GetBoolArg("-checkblockindex", chainparams.DefaultConsistencyChecks());
fCheckpointsEnabled = GetBoolArg("-checkpoints", true);
+ // -mempoollimit limits
+ int64_t nMempoolSizeLimit = GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000;
+ int64_t nMempoolDescendantSizeLimit = GetArg("-limitdescendantsize", DEFAULT_DESCENDANT_SIZE_LIMIT) * 1000;
+ if (nMempoolSizeLimit < 0 || nMempoolSizeLimit < nMempoolDescendantSizeLimit * 40)
+ return InitError(strprintf(_("Error: -maxmempool must be at least %d MB"), GetArg("-limitdescendantsize", DEFAULT_DESCENDANT_SIZE_LIMIT) / 25));
+
// -par=0 means autodetect, but nScriptCheckThreads==0 means no concurrency
nScriptCheckThreads = GetArg("-par", DEFAULT_SCRIPTCHECK_THREADS);
if (nScriptCheckThreads <= 0)
diff --git a/src/leveldbwrapper.cpp b/src/leveldbwrapper.cpp
index ce96b5c8a..32c9345be 100644
--- a/src/leveldbwrapper.cpp
+++ b/src/leveldbwrapper.cpp
@@ -131,7 +131,7 @@ std::vector<unsigned char> CLevelDBWrapper::CreateObfuscateKey() const
bool CLevelDBWrapper::IsEmpty()
{
- boost::scoped_ptr<leveldb::Iterator> it(NewIterator());
+ boost::scoped_ptr<CLevelDBIterator> it(NewIterator());
it->SeekToFirst();
return !(it->Valid());
}
@@ -145,3 +145,10 @@ std::string CLevelDBWrapper::GetObfuscateKeyHex() const
{
return HexStr(obfuscate_key);
}
+
+CLevelDBIterator::~CLevelDBIterator() { delete piter; }
+bool CLevelDBIterator::Valid() { return piter->Valid(); }
+void CLevelDBIterator::SeekToFirst() { piter->SeekToFirst(); }
+void CLevelDBIterator::SeekToLast() { piter->SeekToLast(); }
+void CLevelDBIterator::Next() { piter->Next(); }
+void CLevelDBIterator::Prev() { piter->Prev(); }
diff --git a/src/leveldbwrapper.h b/src/leveldbwrapper.h
index f5c463830..60101e18c 100644
--- a/src/leveldbwrapper.h
+++ b/src/leveldbwrapper.h
@@ -32,13 +32,13 @@ class CLevelDBBatch
private:
leveldb::WriteBatch batch;
- const std::vector<unsigned char> obfuscate_key;
+ const std::vector<unsigned char> *obfuscate_key;
public:
/**
* @param[in] obfuscate_key If passed, XOR data with this key.
*/
- CLevelDBBatch(const std::vector<unsigned char>& obfuscate_key) : obfuscate_key(obfuscate_key) { };
+ CLevelDBBatch(const std::vector<unsigned char> *obfuscate_key) : obfuscate_key(obfuscate_key) { };
template <typename K, typename V>
void Write(const K& key, const V& value)
@@ -51,7 +51,7 @@ public:
CDataStream ssValue(SER_DISK, CLIENT_VERSION);
ssValue.reserve(ssValue.GetSerializeSize(value));
ssValue << value;
- ssValue.Xor(obfuscate_key);
+ ssValue.Xor(*obfuscate_key);
leveldb::Slice slValue(&ssValue[0], ssValue.size());
batch.Put(slKey, slValue);
@@ -68,7 +68,72 @@ public:
batch.Delete(slKey);
}
};
+
+class CLevelDBIterator
+{
+private:
+ leveldb::Iterator *piter;
+ const std::vector<unsigned char> *obfuscate_key;
+
+public:
+
+ /**
+ * @param[in] piterIn The original leveldb iterator.
+ * @param[in] obfuscate_key If passed, XOR data with this key.
+ */
+ CLevelDBIterator(leveldb::Iterator *piterIn, const std::vector<unsigned char>* obfuscate_key) :
+ piter(piterIn), obfuscate_key(obfuscate_key) { };
+ ~CLevelDBIterator();
+
+ bool Valid();
+ void SeekToFirst();
+ void SeekToLast();
+
+ template<typename K> void Seek(const K& key) {
+ CDataStream ssKey(SER_DISK, CLIENT_VERSION);
+ ssKey.reserve(ssKey.GetSerializeSize(key));
+ ssKey << key;
+ leveldb::Slice slKey(&ssKey[0], ssKey.size());
+ piter->Seek(slKey);
+ }
+
+ void Next();
+ void Prev();
+
+ template<typename K> bool GetKey(K& key) {
+ leveldb::Slice slKey = piter->key();
+ try {
+ CDataStream ssKey(slKey.data(), slKey.data() + slKey.size(), SER_DISK, CLIENT_VERSION);
+ ssKey >> key;
+ } catch(std::exception &e) {
+ return false;
+ }
+ return true;
+ }
+
+ unsigned int GetKeySize() {
+ return piter->key().size();
+ }
+
+ template<typename V> bool GetValue(V& value) {
+ leveldb::Slice slValue = piter->value();
+ try {
+ CDataStream ssValue(slValue.data(), slValue.data() + slValue.size(), SER_DISK, CLIENT_VERSION);
+ ssValue.Xor(*obfuscate_key);
+ ssValue >> value;
+ } catch(std::exception &e) {
+ return false;
+ }
+ return true;
+ }
+
+ unsigned int GetValueSize() {
+ return piter->value().size();
+ }
+
+};
+
class CLevelDBWrapper
{
private:
@@ -145,7 +210,7 @@ public:
template <typename K, typename V>
bool Write(const K& key, const V& value, bool fSync = false) throw(leveldb_error)
{
- CLevelDBBatch batch(obfuscate_key);
+ CLevelDBBatch batch(&obfuscate_key);
batch.Write(key, value);
return WriteBatch(batch, fSync);
}
@@ -172,7 +237,7 @@ public:
template <typename K>
bool Erase(const K& key, bool fSync = false) throw(leveldb_error)
{
- CLevelDBBatch batch(obfuscate_key);
+ CLevelDBBatch batch(&obfuscate_key);
batch.Erase(key);
return WriteBatch(batch, fSync);
}
@@ -187,14 +252,13 @@ public:
bool Sync() throw(leveldb_error)
{
- CLevelDBBatch batch(obfuscate_key);
+ CLevelDBBatch batch(&obfuscate_key);
return WriteBatch(batch, true);
}
- // not exactly clean encapsulation, but it's easiest for now
- leveldb::Iterator* NewIterator()
+ CLevelDBIterator *NewIterator()
{
- return pdb->NewIterator(iteroptions);
+ return new CLevelDBIterator(pdb->NewIterator(iteroptions), &obfuscate_key);
}
/**
diff --git a/src/main.cpp b/src/main.cpp
index 5cfb05b0d..d870280e9 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -740,17 +740,14 @@ bool CheckTransaction(const CTransaction& tx, CValidationState &state)
return true;
}
-CAmount GetMinRelayFee(const CTransaction& tx, unsigned int nBytes, bool fAllowFree)
+CAmount GetMinRelayFee(const CTransaction& tx, const CTxMemPool& pool, unsigned int nBytes, bool fAllowFree)
{
- {
- LOCK(mempool.cs);
- uint256 hash = tx.GetHash();
- double dPriorityDelta = 0;
- CAmount nFeeDelta = 0;
- mempool.ApplyDeltas(hash, dPriorityDelta, nFeeDelta);
- if (dPriorityDelta > 0 || nFeeDelta > 0)
- return 0;
- }
+ uint256 hash = tx.GetHash();
+ double dPriorityDelta = 0;
+ CAmount nFeeDelta = 0;
+ pool.ApplyDeltas(hash, dPriorityDelta, nFeeDelta);
+ if (dPriorityDelta > 0 || nFeeDelta > 0)
+ return 0;
CAmount nMinFee = ::minRelayTxFee.GetFee(nBytes);
@@ -779,7 +776,7 @@ static std::string FormatStateMessage(const CValidationState &state)
}
bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransaction &tx, bool fLimitFree,
- bool* pfMissingInputs, bool fRejectAbsurdFee)
+ bool* pfMissingInputs, bool fOverrideMempoolLimit, bool fRejectAbsurdFee)
{
AssertLockHeld(cs_main);
if (pfMissingInputs)
@@ -879,17 +876,20 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
CAmount nFees = nValueIn-nValueOut;
double dPriority = view.GetPriority(tx, chainActive.Height());
- CTxMemPoolEntry entry(tx, nFees, GetTime(), dPriority, chainActive.Height(), mempool.HasNoInputsOf(tx));
+ CTxMemPoolEntry entry(tx, nFees, GetTime(), dPriority, chainActive.Height(), pool.HasNoInputsOf(tx));
unsigned int nSize = entry.GetTxSize();
// Don't accept it if it can't get into a block
- CAmount txMinFee = GetMinRelayFee(tx, nSize, true);
+ CAmount txMinFee = GetMinRelayFee(tx, pool, nSize, true);
if (fLimitFree && nFees < txMinFee)
return state.DoS(0, false, REJECT_INSUFFICIENTFEE, "insufficient fee", false,
strprintf("%d < %d", nFees, txMinFee));
- // Require that free transactions have sufficient priority to be mined in the next block.
- if (GetBoolArg("-relaypriority", true) && nFees < ::minRelayTxFee.GetFee(nSize) && !AllowFree(view.GetPriority(tx, chainActive.Height() + 1))) {
+ CAmount mempoolRejectFee = pool.GetMinFee(GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000).GetFee(nSize);
+ if (mempoolRejectFee > 0 && nFees < mempoolRejectFee) {
+ return state.DoS(0, false, REJECT_INSUFFICIENTFEE, "mempool min fee not met", false, strprintf("%d < %d", nFees, mempoolRejectFee));
+ } else if (GetBoolArg("-relaypriority", true) && nFees < ::minRelayTxFee.GetFee(nSize) && !AllowFree(view.GetPriority(tx, chainActive.Height() + 1))) {
+ // Require that free transactions have sufficient priority to be mined in the next block.
return state.DoS(0, false, REJECT_INSUFFICIENTFEE, "insufficient priority");
}
@@ -954,6 +954,17 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
// Store transaction in memory
pool.addUnchecked(hash, entry, setAncestors, !IsInitialBlockDownload());
+
+ // trim mempool and check if tx was trimmed
+ if (!fOverrideMempoolLimit) {
+ int expired = pool.Expire(GetTime() - GetArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY) * 60 * 60);
+ if (expired != 0)
+ LogPrint("mempool", "Expired %i transactions from the memory pool\n", expired);
+
+ pool.TrimToSize(GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000);
+ if (!pool.exists(tx.GetHash()))
+ return state.DoS(0, false, REJECT_INSUFFICIENTFEE, "mempool full");
+ }
}
SyncWithWallets(tx, NULL);
@@ -2020,7 +2031,7 @@ void static UpdateTip(CBlockIndex *pindexNew) {
}
}
-/** Disconnect chainActive's tip. */
+/** Disconnect chainActive's tip. You want to manually re-limit mempool size after this */
bool static DisconnectTip(CValidationState &state) {
CBlockIndex *pindexDelete = chainActive.Tip();
assert(pindexDelete);
@@ -2047,7 +2058,7 @@ bool static DisconnectTip(CValidationState &state) {
// ignore validation errors in resurrected transactions
list<CTransaction> removed;
CValidationState stateDummy;
- if (tx.IsCoinBase() || !AcceptToMemoryPool(mempool, stateDummy, tx, false, NULL)) {
+ if (tx.IsCoinBase() || !AcceptToMemoryPool(mempool, stateDummy, tx, false, NULL, true)) {
mempool.remove(tx, removed, true);
} else if (mempool.exists(tx.GetHash())) {
vHashUpdate.push_back(tx.GetHash());
@@ -2220,9 +2231,11 @@ static bool ActivateBestChainStep(CValidationState &state, CBlockIndex *pindexMo
const CBlockIndex *pindexFork = chainActive.FindFork(pindexMostWork);
// Disconnect active blocks which are no longer in the best chain.
+ bool fBlocksDisconnected = false;
while (chainActive.Tip() && chainActive.Tip() != pindexFork) {
if (!DisconnectTip(state))
return false;
+ fBlocksDisconnected = true;
}
// Build list of new blocks to connect.
@@ -2268,6 +2281,9 @@ static bool ActivateBestChainStep(CValidationState &state, CBlockIndex *pindexMo
}
}
+ if (fBlocksDisconnected)
+ mempool.TrimToSize(GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000);
+
// Callbacks/notifications for a new best chain.
if (fInvalidFound)
CheckForkWarningConditionsOnNewFork(vpindexToConnect.back());
@@ -2354,6 +2370,8 @@ bool InvalidateBlock(CValidationState& state, CBlockIndex *pindex) {
}
}
+ mempool.TrimToSize(GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000);
+
// The resulting new best tip may not be in setBlockIndexCandidates anymore, so
// add it again.
BlockMap::iterator it = mapBlockIndex.begin();
@@ -4290,10 +4308,10 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
RelayTransaction(tx);
vWorkQueue.push_back(inv.hash);
- LogPrint("mempool", "AcceptToMemoryPool: peer=%d: accepted %s (poolsz %u)\n",
+ LogPrint("mempool", "AcceptToMemoryPool: peer=%d: accepted %s (poolsz %u txn, %u kB)\n",
pfrom->id,
tx.GetHash().ToString(),
- mempool.size());
+ mempool.size(), mempool.DynamicMemoryUsage() / 1000);
// Recursively process any orphan transactions that depended on this one
set<NodeId> setMisbehaving;
@@ -4955,7 +4973,16 @@ bool SendMessages(CNode* pto, bool fSendTrickle)
if ((nSyncStarted == 0 && fFetch) || pindexBestHeader->GetBlockTime() > GetAdjustedTime() - 24 * 60 * 60) {
state.fSyncStarted = true;
nSyncStarted++;
- CBlockIndex *pindexStart = pindexBestHeader->pprev ? pindexBestHeader->pprev : pindexBestHeader;
+ const CBlockIndex *pindexStart = pindexBestHeader;
+ /* If possible, start at the block preceding the currently
+ best known header. This ensures that we always get a
+ non-empty list of headers back as long as the peer
+ is up-to-date. With a non-empty response, we can initialise
+ the peer's known best block. This wouldn't be possible
+ if we requested starting at pindexBestHeader and
+ got back an empty response. */
+ if (pindexStart->pprev)
+ pindexStart = pindexStart->pprev;
LogPrint("net", "initial getheaders (%d) to peer=%d (startheight:%d)\n", pindexStart->nHeight, pto->id, pto->nStartingHeight);
pto->PushMessage("getheaders", chainActive.GetLocator(pindexStart), uint256());
}
diff --git a/src/main.h b/src/main.h
index a6001eed8..202d2c772 100644
--- a/src/main.h
+++ b/src/main.h
@@ -51,6 +51,10 @@ static const unsigned int DEFAULT_ANCESTOR_SIZE_LIMIT = 900;
static const unsigned int DEFAULT_DESCENDANT_LIMIT = 1000;
/** Default for -limitdescendantsize, maximum kilobytes of in-mempool descendants */
static const unsigned int DEFAULT_DESCENDANT_SIZE_LIMIT = 2500;
+/** Default for -maxmempool, maximum megabytes of mempool memory usage */
+static const unsigned int DEFAULT_MAX_MEMPOOL_SIZE = 300;
+/** Default for -mempoolexpiry, expiration time for mempool transactions in hours */
+static const unsigned int DEFAULT_MEMPOOL_EXPIRY = 72;
/** The maximum size of a blk?????.dat file (since 0.8) */
static const unsigned int MAX_BLOCKFILE_SIZE = 0x8000000; // 128 MiB
/** The pre-allocation chunk size for blk?????.dat files (since 0.8) */
@@ -225,7 +229,7 @@ void PruneAndFlush();
/** (try to) add transaction to memory pool **/
bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransaction &tx, bool fLimitFree,
- bool* pfMissingInputs, bool fRejectAbsurdFee=false);
+ bool* pfMissingInputs, bool fOverrideMempoolLimit=false, bool fRejectAbsurdFee=false);
struct CNodeStateStats {
diff --git a/src/net.cpp b/src/net.cpp
index 87c4f0af0..58b946f4a 100644
--- a/src/net.cpp
+++ b/src/net.cpp
@@ -963,6 +963,15 @@ static void AcceptConnection(const ListenSocket& hListenSocket) {
return;
}
+ // According to the internet TCP_NODELAY is not carried into accepted sockets
+ // on all platforms. Set it again here just to be sure.
+ int set = 1;
+#ifdef WIN32
+ setsockopt(hSocket, IPPROTO_TCP, TCP_NODELAY, (const char*)&set, sizeof(int));
+#else
+ setsockopt(hSocket, IPPROTO_TCP, TCP_NODELAY, (void*)&set, sizeof(int));
+#endif
+
if (CNode::IsBanned(addr) && !whitelisted)
{
LogPrintf("connection from %s dropped (banned)\n", addr.ToString());
@@ -1790,8 +1799,11 @@ bool BindListenPort(const CService &addrBind, string& strError, bool fWhiteliste
// Allow binding if the port is still in TIME_WAIT state after
// the program was closed and restarted.
setsockopt(hListenSocket, SOL_SOCKET, SO_REUSEADDR, (void*)&nOne, sizeof(int));
+ // Disable Nagle's algorithm
+ setsockopt(hListenSocket, IPPROTO_TCP, TCP_NODELAY, (void*)&nOne, sizeof(int));
#else
setsockopt(hListenSocket, SOL_SOCKET, SO_REUSEADDR, (const char*)&nOne, sizeof(int));
+ setsockopt(hListenSocket, IPPROTO_TCP, TCP_NODELAY, (const char*)&nOne, sizeof(int));
#endif
// Set to non-blocking, incoming connections will also inherit this
diff --git a/src/netbase.cpp b/src/netbase.cpp
index c3d56d918..f5316965c 100644
--- a/src/netbase.cpp
+++ b/src/netbase.cpp
@@ -444,12 +444,19 @@ bool static ConnectSocketDirectly(const CService &addrConnect, SOCKET& hSocketRe
if (hSocket == INVALID_SOCKET)
return false;
-#ifdef SO_NOSIGPIPE
int set = 1;
+#ifdef SO_NOSIGPIPE
// Different way of disabling SIGPIPE on BSD
setsockopt(hSocket, SOL_SOCKET, SO_NOSIGPIPE, (void*)&set, sizeof(int));
#endif
+ //Disable Nagle's algorithm
+#ifdef WIN32
+ setsockopt(hSocket, IPPROTO_TCP, TCP_NODELAY, (const char*)&set, sizeof(int));
+#else
+ setsockopt(hSocket, IPPROTO_TCP, TCP_NODELAY, (void*)&set, sizeof(int));
+#endif
+
// Set to non-blocking
if (!SetSocketNonBlocking(hSocket, true))
return error("ConnectSocketDirectly: Setting socket to non-blocking failed, error %s\n", NetworkErrorString(WSAGetLastError()));
diff --git a/src/pow.cpp b/src/pow.cpp
index bb53ad204..5ace3fbc9 100644
--- a/src/pow.cpp
+++ b/src/pow.cpp
@@ -52,6 +52,9 @@ unsigned int GetNextWorkRequired(const CBlockIndex* pindexLast, const CBlockHead
unsigned int CalculateNextWorkRequired(const CBlockIndex* pindexLast, int64_t nFirstBlockTime, const Consensus::Params& params)
{
+ if (params.fPowNoRetargeting)
+ return pindexLast->nBits;
+
// Limit adjustment step
int64_t nActualTimespan = pindexLast->GetBlockTime() - nFirstBlockTime;
LogPrintf(" nActualTimespan = %d before bounds\n", nActualTimespan);
diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp
index ea7f86d18..bda8acff1 100644
--- a/src/qt/bitcoin.cpp
+++ b/src/qt/bitcoin.cpp
@@ -563,7 +563,7 @@ int main(int argc, char *argv[])
// Show help message immediately after parsing command-line options (for "-lang") and setting locale,
// but before showing splash screen.
- if (mapArgs.count("-?") || mapArgs.count("-help") || mapArgs.count("-version"))
+ if (mapArgs.count("-?") || mapArgs.count("-h") || mapArgs.count("-help") || mapArgs.count("-version"))
{
HelpMessageDialog help(NULL, mapArgs.count("-version"));
help.showOrPrint();
@@ -597,8 +597,10 @@ int main(int argc, char *argv[])
// - Needs to be done before createOptionsModel
// Check for -testnet or -regtest parameter (Params() calls are only valid after this clause)
- if (!SelectParamsFromCommandLine()) {
- QMessageBox::critical(0, QObject::tr("Bitcoin Core"), QObject::tr("Error: Invalid combination of -regtest and -testnet."));
+ try {
+ SelectParams(ChainNameFromCommandLine());
+ } catch(std::exception &e) {
+ QMessageBox::critical(0, QObject::tr("Bitcoin Core"), QObject::tr("Error: %1").arg(e.what()));
return 1;
}
#ifdef ENABLE_WALLET
diff --git a/src/rpcrawtransaction.cpp b/src/rpcrawtransaction.cpp
index 4dec53396..4b9647350 100644
--- a/src/rpcrawtransaction.cpp
+++ b/src/rpcrawtransaction.cpp
@@ -809,7 +809,7 @@ UniValue sendrawtransaction(const UniValue& params, bool fHelp)
// push to local node and sync with wallets
CValidationState state;
bool fMissingInputs;
- if (!AcceptToMemoryPool(mempool, state, tx, false, &fMissingInputs, !fOverrideFees)) {
+ if (!AcceptToMemoryPool(mempool, state, tx, false, &fMissingInputs, false, !fOverrideFees)) {
if (state.IsInvalid()) {
throw JSONRPCError(RPC_TRANSACTION_REJECTED, strprintf("%i: %s", state.GetRejectCode(), state.GetRejectReason()));
} else {
diff --git a/src/test/README.md b/src/test/README.md
index e36112bd4..b2d6be14f 100644
--- a/src/test/README.md
+++ b/src/test/README.md
@@ -16,6 +16,8 @@ their tests in a test suite called "<source_filename>_tests". For an
examples of this pattern, examine uint160_tests.cpp and
uint256_tests.cpp.
+Add the source files to /src/Makefile.test.include to add them to the build.
+
For further reading, I found the following website to be helpful in
explaining how the boost unit test framework works:
[http://www.alittlemadness.com/2009/03/31/c-unit-testing-with-boosttest/](http://www.alittlemadness.com/2009/03/31/c-unit-testing-with-boosttest/).
diff --git a/src/test/data/tx_invalid.json b/src/test/data/tx_invalid.json
index 5cad5af7c..d3c859434 100644
--- a/src/test/data/tx_invalid.json
+++ b/src/test/data/tx_invalid.json
@@ -193,5 +193,9 @@
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "HASH160 0x14 0xc5b93064159b3b2d6ab506a41b1f50463771b988 EQUAL"]],
"0100000001000100000000000000000000000000000000000000000000000000000000000000000000030251b1000000000100000000000000000000000000", "P2SH,CHECKLOCKTIMEVERIFY"],
+["A transaction with a non-standard DER signature."],
+[[["b1dbc81696c8a9c0fccd0693ab66d7c368dbc38c0def4e800685560ddd1b2132", 0, "DUP HASH160 0x14 0x4b3bd7eba3bc0284fd3007be7f3be275e94f5826 EQUALVERIFY CHECKSIG"]],
+"010000000132211bdd0d568506804eef0d8cc3db68c3d766ab9306cdfcc0a9c89616c8dbb1000000006c493045022100c7bb0faea0522e74ff220c20c022d2cb6033f8d167fb89e75a50e237a35fd6d202203064713491b1f8ad5f79e623d0219ad32510bfaa1009ab30cbee77b59317d6e30001210237af13eb2d84e4545af287b919c2282019c9691cc509e78e196a9d8274ed1be0ffffffff0100000000000000001976a914f1b3ed2eda9a2ebe5a9374f692877cdf87c0f95b88ac00000000", "P2SH,DERSIG"],
+
["Make diffs cleaner by leaving a comment here without comma at the end"]
]
diff --git a/src/test/data/tx_valid.json b/src/test/data/tx_valid.json
index 9744a3c84..0dfef73ae 100644
--- a/src/test/data/tx_valid.json
+++ b/src/test/data/tx_valid.json
@@ -229,5 +229,9 @@
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "HASH160 0x14 0xc5b93064159b3b2d6ab506a41b1f50463771b988 EQUAL"]],
"0100000001000100000000000000000000000000000000000000000000000000000000000000000000030251b1000000000100000000000000000001000000", "P2SH,CHECKLOCKTIMEVERIFY"],
+["A transaction with a non-standard DER signature."],
+[[["b1dbc81696c8a9c0fccd0693ab66d7c368dbc38c0def4e800685560ddd1b2132", 0, "DUP HASH160 0x14 0x4b3bd7eba3bc0284fd3007be7f3be275e94f5826 EQUALVERIFY CHECKSIG"]],
+"010000000132211bdd0d568506804eef0d8cc3db68c3d766ab9306cdfcc0a9c89616c8dbb1000000006c493045022100c7bb0faea0522e74ff220c20c022d2cb6033f8d167fb89e75a50e237a35fd6d202203064713491b1f8ad5f79e623d0219ad32510bfaa1009ab30cbee77b59317d6e30001210237af13eb2d84e4545af287b919c2282019c9691cc509e78e196a9d8274ed1be0ffffffff0100000000000000001976a914f1b3ed2eda9a2ebe5a9374f692877cdf87c0f95b88ac00000000", "P2SH"],
+
["Make diffs cleaner by leaving a comment here without comma at the end"]
]
diff --git a/src/test/leveldbwrapper_tests.cpp b/src/test/leveldbwrapper_tests.cpp
index db04f3ea4..606313b00 100644
--- a/src/test/leveldbwrapper_tests.cpp
+++ b/src/test/leveldbwrapper_tests.cpp
@@ -46,7 +46,86 @@ BOOST_AUTO_TEST_CASE(leveldbwrapper)
BOOST_CHECK_EQUAL(res.ToString(), in.ToString());
}
}
-
+
+// Test batch operations
+BOOST_AUTO_TEST_CASE(leveldbwrapper_batch)
+{
+ // Perform tests both obfuscated and non-obfuscated.
+ for (int i = 0; i < 2; i++) {
+ bool obfuscate = (bool)i;
+ path ph = temp_directory_path() / unique_path();
+ CLevelDBWrapper dbw(ph, (1 << 20), true, false, obfuscate);
+
+ char key = 'i';
+ uint256 in = GetRandHash();
+ char key2 = 'j';
+ uint256 in2 = GetRandHash();
+ char key3 = 'k';
+ uint256 in3 = GetRandHash();
+
+ uint256 res;
+ CLevelDBBatch batch(&dbw.GetObfuscateKey());
+
+ batch.Write(key, in);
+ batch.Write(key2, in2);
+ batch.Write(key3, in3);
+
+ // Remove key3 before it's even been written
+ batch.Erase(key3);
+
+ dbw.WriteBatch(batch);
+
+ BOOST_CHECK(dbw.Read(key, res));
+ BOOST_CHECK_EQUAL(res.ToString(), in.ToString());
+ BOOST_CHECK(dbw.Read(key2, res));
+ BOOST_CHECK_EQUAL(res.ToString(), in2.ToString());
+
+ // key3 never should've been written
+ BOOST_CHECK(dbw.Read(key3, res) == false);
+ }
+}
+
+BOOST_AUTO_TEST_CASE(leveldbwrapper_iterator)
+{
+ // Perform tests both obfuscated and non-obfuscated.
+ for (int i = 0; i < 2; i++) {
+ bool obfuscate = (bool)i;
+ path ph = temp_directory_path() / unique_path();
+ CLevelDBWrapper dbw(ph, (1 << 20), true, false, obfuscate);
+
+ // The two keys are intentionally chosen for ordering
+ char key = 'j';
+ uint256 in = GetRandHash();
+ BOOST_CHECK(dbw.Write(key, in));
+ char key2 = 'k';
+ uint256 in2 = GetRandHash();
+ BOOST_CHECK(dbw.Write(key2, in2));
+
+ boost::scoped_ptr<CLevelDBIterator> it(const_cast<CLevelDBWrapper*>(&dbw)->NewIterator());
+
+ // Be sure to seek past the obfuscation key (if it exists)
+ it->Seek(key);
+
+ char key_res;
+ uint256 val_res;
+
+ it->GetKey(key_res);
+ it->GetValue(val_res);
+ BOOST_CHECK_EQUAL(key_res, key);
+ BOOST_CHECK_EQUAL(val_res.ToString(), in.ToString());
+
+ it->Next();
+
+ it->GetKey(key_res);
+ it->GetValue(val_res);
+ BOOST_CHECK_EQUAL(key_res, key2);
+ BOOST_CHECK_EQUAL(val_res.ToString(), in2.ToString());
+
+ it->Next();
+ BOOST_CHECK_EQUAL(it->Valid(), false);
+ }
+}
+
// Test that we do not obfuscation if there is existing data.
BOOST_AUTO_TEST_CASE(existing_data_no_obfuscate)
{
diff --git a/src/test/mempool_tests.cpp b/src/test/mempool_tests.cpp
index 5bf1e98e8..0cf906a25 100644
--- a/src/test/mempool_tests.cpp
+++ b/src/test/mempool_tests.cpp
@@ -153,11 +153,11 @@ BOOST_AUTO_TEST_CASE(MempoolIndexingTest)
std::vector<std::string> sortedOrder;
sortedOrder.resize(5);
- sortedOrder[0] = tx2.GetHash().ToString(); // 20000
- sortedOrder[1] = tx4.GetHash().ToString(); // 15000
+ sortedOrder[0] = tx3.GetHash().ToString(); // 0
+ sortedOrder[1] = tx5.GetHash().ToString(); // 10000
sortedOrder[2] = tx1.GetHash().ToString(); // 10000
- sortedOrder[3] = tx5.GetHash().ToString(); // 10000
- sortedOrder[4] = tx3.GetHash().ToString(); // 0
+ sortedOrder[3] = tx4.GetHash().ToString(); // 15000
+ sortedOrder[4] = tx2.GetHash().ToString(); // 20000
CheckSort(pool, sortedOrder);
/* low fee but with high fee child */
@@ -169,7 +169,7 @@ BOOST_AUTO_TEST_CASE(MempoolIndexingTest)
pool.addUnchecked(tx6.GetHash(), CTxMemPoolEntry(tx6, 0LL, 1, 10.0, 1, true));
BOOST_CHECK_EQUAL(pool.size(), 6);
// Check that at this point, tx6 is sorted low
- sortedOrder.push_back(tx6.GetHash().ToString());
+ sortedOrder.insert(sortedOrder.begin(), tx6.GetHash().ToString());
CheckSort(pool, sortedOrder);
CTxMemPool::setEntries setAncestors;
@@ -194,9 +194,9 @@ BOOST_AUTO_TEST_CASE(MempoolIndexingTest)
BOOST_CHECK_EQUAL(pool.size(), 7);
// Now tx6 should be sorted higher (high fee child): tx7, tx6, tx2, ...
- sortedOrder.erase(sortedOrder.end()-1);
- sortedOrder.insert(sortedOrder.begin(), tx6.GetHash().ToString());
- sortedOrder.insert(sortedOrder.begin(), tx7.GetHash().ToString());
+ sortedOrder.erase(sortedOrder.begin());
+ sortedOrder.push_back(tx6.GetHash().ToString());
+ sortedOrder.push_back(tx7.GetHash().ToString());
CheckSort(pool, sortedOrder);
/* low fee child of tx7 */
@@ -211,7 +211,7 @@ BOOST_AUTO_TEST_CASE(MempoolIndexingTest)
pool.addUnchecked(tx8.GetHash(), CTxMemPoolEntry(tx8, 0LL, 2, 10.0, 1, true), setAncestors);
// Now tx8 should be sorted low, but tx6/tx both high
- sortedOrder.push_back(tx8.GetHash().ToString());
+ sortedOrder.insert(sortedOrder.begin(), tx8.GetHash().ToString());
CheckSort(pool, sortedOrder);
/* low fee child of tx7 */
@@ -226,7 +226,7 @@ BOOST_AUTO_TEST_CASE(MempoolIndexingTest)
// tx9 should be sorted low
BOOST_CHECK_EQUAL(pool.size(), 9);
- sortedOrder.push_back(tx9.GetHash().ToString());
+ sortedOrder.insert(sortedOrder.begin(), tx9.GetHash().ToString());
CheckSort(pool, sortedOrder);
std::vector<std::string> snapshotOrder = sortedOrder;
@@ -255,21 +255,21 @@ BOOST_AUTO_TEST_CASE(MempoolIndexingTest)
* tx8 and tx9 should both now be sorted higher
* Final order after tx10 is added:
*
- * tx7 = 2.2M (4 txs)
- * tx6 = 2.2M (5 txs)
- * tx10 = 200k (1 tx)
- * tx8 = 200k (2 txs)
- * tx9 = 200k (2 txs)
- * tx2 = 20000 (1)
- * tx4 = 15000 (1)
- * tx1 = 10000 (1)
- * tx5 = 10000 (1)
* tx3 = 0 (1)
+ * tx5 = 10000 (1)
+ * tx1 = 10000 (1)
+ * tx4 = 15000 (1)
+ * tx2 = 20000 (1)
+ * tx9 = 200k (2 txs)
+ * tx8 = 200k (2 txs)
+ * tx10 = 200k (1 tx)
+ * tx6 = 2.2M (5 txs)
+ * tx7 = 2.2M (4 txs)
*/
- sortedOrder.erase(sortedOrder.end()-2, sortedOrder.end()); // take out tx8, tx9 from the end
- sortedOrder.insert(sortedOrder.begin()+2, tx10.GetHash().ToString()); // tx10 is after tx6
- sortedOrder.insert(sortedOrder.begin()+3, tx9.GetHash().ToString());
- sortedOrder.insert(sortedOrder.begin()+3, tx8.GetHash().ToString());
+ sortedOrder.erase(sortedOrder.begin(), sortedOrder.begin()+2); // take out tx9, tx8 from the beginning
+ sortedOrder.insert(sortedOrder.begin()+5, tx9.GetHash().ToString());
+ sortedOrder.insert(sortedOrder.begin()+6, tx8.GetHash().ToString());
+ sortedOrder.insert(sortedOrder.begin()+7, tx10.GetHash().ToString()); // tx10 is just before tx6
CheckSort(pool, sortedOrder);
// there should be 10 transactions in the mempool
@@ -281,4 +281,157 @@ BOOST_AUTO_TEST_CASE(MempoolIndexingTest)
CheckSort(pool, snapshotOrder);
}
+BOOST_AUTO_TEST_CASE(MempoolSizeLimitTest)
+{
+ CTxMemPool pool(CFeeRate(1000));
+
+ CMutableTransaction tx1 = CMutableTransaction();
+ tx1.vin.resize(1);
+ tx1.vin[0].scriptSig = CScript() << OP_1;
+ tx1.vout.resize(1);
+ tx1.vout[0].scriptPubKey = CScript() << OP_1 << OP_EQUAL;
+ tx1.vout[0].nValue = 10 * COIN;
+ pool.addUnchecked(tx1.GetHash(), CTxMemPoolEntry(tx1, 10000LL, 0, 10.0, 1, pool.HasNoInputsOf(tx1)));
+
+ CMutableTransaction tx2 = CMutableTransaction();
+ tx2.vin.resize(1);
+ tx2.vin[0].scriptSig = CScript() << OP_2;
+ tx2.vout.resize(1);
+ tx2.vout[0].scriptPubKey = CScript() << OP_2 << OP_EQUAL;
+ tx2.vout[0].nValue = 10 * COIN;
+ pool.addUnchecked(tx2.GetHash(), CTxMemPoolEntry(tx2, 5000LL, 0, 10.0, 1, pool.HasNoInputsOf(tx2)));
+
+ pool.TrimToSize(pool.DynamicMemoryUsage()); // should do nothing
+ BOOST_CHECK(pool.exists(tx1.GetHash()));
+ BOOST_CHECK(pool.exists(tx2.GetHash()));
+
+ pool.TrimToSize(pool.DynamicMemoryUsage() * 3 / 4); // should remove the lower-feerate transaction
+ BOOST_CHECK(pool.exists(tx1.GetHash()));
+ BOOST_CHECK(!pool.exists(tx2.GetHash()));
+
+ pool.addUnchecked(tx2.GetHash(), CTxMemPoolEntry(tx2, 5000LL, 0, 10.0, 1, pool.HasNoInputsOf(tx2)));
+ CMutableTransaction tx3 = CMutableTransaction();
+ tx3.vin.resize(1);
+ tx3.vin[0].prevout = COutPoint(tx2.GetHash(), 0);
+ tx3.vin[0].scriptSig = CScript() << OP_2;
+ tx3.vout.resize(1);
+ tx3.vout[0].scriptPubKey = CScript() << OP_3 << OP_EQUAL;
+ tx3.vout[0].nValue = 10 * COIN;
+ pool.addUnchecked(tx3.GetHash(), CTxMemPoolEntry(tx3, 20000LL, 0, 10.0, 1, pool.HasNoInputsOf(tx3)));
+
+ pool.TrimToSize(pool.DynamicMemoryUsage() * 3 / 4); // tx3 should pay for tx2 (CPFP)
+ BOOST_CHECK(!pool.exists(tx1.GetHash()));
+ BOOST_CHECK(pool.exists(tx2.GetHash()));
+ BOOST_CHECK(pool.exists(tx3.GetHash()));
+
+ pool.TrimToSize(::GetSerializeSize(CTransaction(tx1), SER_NETWORK, PROTOCOL_VERSION)); // mempool is limited to tx1's size in memory usage, so nothing fits
+ BOOST_CHECK(!pool.exists(tx1.GetHash()));
+ BOOST_CHECK(!pool.exists(tx2.GetHash()));
+ BOOST_CHECK(!pool.exists(tx3.GetHash()));
+
+ CFeeRate maxFeeRateRemoved(25000, ::GetSerializeSize(CTransaction(tx3), SER_NETWORK, PROTOCOL_VERSION) + ::GetSerializeSize(CTransaction(tx2), SER_NETWORK, PROTOCOL_VERSION));
+ BOOST_CHECK_EQUAL(pool.GetMinFee(1).GetFeePerK(), maxFeeRateRemoved.GetFeePerK() + 1000);
+
+ CMutableTransaction tx4 = CMutableTransaction();
+ tx4.vin.resize(2);
+ tx4.vin[0].prevout.SetNull();
+ tx4.vin[0].scriptSig = CScript() << OP_4;
+ tx4.vin[1].prevout.SetNull();
+ tx4.vin[1].scriptSig = CScript() << OP_4;
+ tx4.vout.resize(2);
+ tx4.vout[0].scriptPubKey = CScript() << OP_4 << OP_EQUAL;
+ tx4.vout[0].nValue = 10 * COIN;
+ tx4.vout[1].scriptPubKey = CScript() << OP_4 << OP_EQUAL;
+ tx4.vout[1].nValue = 10 * COIN;
+
+ CMutableTransaction tx5 = CMutableTransaction();
+ tx5.vin.resize(2);
+ tx5.vin[0].prevout = COutPoint(tx4.GetHash(), 0);
+ tx5.vin[0].scriptSig = CScript() << OP_4;
+ tx5.vin[1].prevout.SetNull();
+ tx5.vin[1].scriptSig = CScript() << OP_5;
+ tx5.vout.resize(2);
+ tx5.vout[0].scriptPubKey = CScript() << OP_5 << OP_EQUAL;
+ tx5.vout[0].nValue = 10 * COIN;
+ tx5.vout[1].scriptPubKey = CScript() << OP_5 << OP_EQUAL;
+ tx5.vout[1].nValue = 10 * COIN;
+
+ CMutableTransaction tx6 = CMutableTransaction();
+ tx6.vin.resize(2);
+ tx6.vin[0].prevout = COutPoint(tx4.GetHash(), 1);
+ tx6.vin[0].scriptSig = CScript() << OP_4;
+ tx6.vin[1].prevout.SetNull();
+ tx6.vin[1].scriptSig = CScript() << OP_6;
+ tx6.vout.resize(2);
+ tx6.vout[0].scriptPubKey = CScript() << OP_6 << OP_EQUAL;
+ tx6.vout[0].nValue = 10 * COIN;
+ tx6.vout[1].scriptPubKey = CScript() << OP_6 << OP_EQUAL;
+ tx6.vout[1].nValue = 10 * COIN;
+
+ CMutableTransaction tx7 = CMutableTransaction();
+ tx7.vin.resize(2);
+ tx7.vin[0].prevout = COutPoint(tx5.GetHash(), 0);
+ tx7.vin[0].scriptSig = CScript() << OP_5;
+ tx7.vin[1].prevout = COutPoint(tx6.GetHash(), 0);
+ tx7.vin[1].scriptSig = CScript() << OP_6;
+ tx7.vout.resize(2);
+ tx7.vout[0].scriptPubKey = CScript() << OP_7 << OP_EQUAL;
+ tx7.vout[0].nValue = 10 * COIN;
+ tx7.vout[0].scriptPubKey = CScript() << OP_7 << OP_EQUAL;
+ tx7.vout[0].nValue = 10 * COIN;
+
+ pool.addUnchecked(tx4.GetHash(), CTxMemPoolEntry(tx4, 7000LL, 0, 10.0, 1, pool.HasNoInputsOf(tx4)));
+ pool.addUnchecked(tx5.GetHash(), CTxMemPoolEntry(tx5, 1000LL, 0, 10.0, 1, pool.HasNoInputsOf(tx5)));
+ pool.addUnchecked(tx6.GetHash(), CTxMemPoolEntry(tx6, 1100LL, 0, 10.0, 1, pool.HasNoInputsOf(tx6)));
+ pool.addUnchecked(tx7.GetHash(), CTxMemPoolEntry(tx7, 9000LL, 0, 10.0, 1, pool.HasNoInputsOf(tx7)));
+
+ // we only require this remove, at max, 2 txn, because its not clear what we're really optimizing for aside from that
+ pool.TrimToSize(pool.DynamicMemoryUsage() - 1);
+ BOOST_CHECK(pool.exists(tx4.GetHash()));
+ BOOST_CHECK(pool.exists(tx6.GetHash()));
+ BOOST_CHECK(!pool.exists(tx7.GetHash()));
+
+ if (!pool.exists(tx5.GetHash()))
+ pool.addUnchecked(tx5.GetHash(), CTxMemPoolEntry(tx5, 1000LL, 0, 10.0, 1, pool.HasNoInputsOf(tx5)));
+ pool.addUnchecked(tx7.GetHash(), CTxMemPoolEntry(tx7, 9000LL, 0, 10.0, 1, pool.HasNoInputsOf(tx7)));
+
+ pool.TrimToSize(pool.DynamicMemoryUsage() / 2); // should maximize mempool size by only removing 5/7
+ BOOST_CHECK(pool.exists(tx4.GetHash()));
+ BOOST_CHECK(!pool.exists(tx5.GetHash()));
+ BOOST_CHECK(pool.exists(tx6.GetHash()));
+ BOOST_CHECK(!pool.exists(tx7.GetHash()));
+
+ pool.addUnchecked(tx5.GetHash(), CTxMemPoolEntry(tx5, 1000LL, 0, 10.0, 1, pool.HasNoInputsOf(tx5)));
+ pool.addUnchecked(tx7.GetHash(), CTxMemPoolEntry(tx7, 9000LL, 0, 10.0, 1, pool.HasNoInputsOf(tx7)));
+
+ std::vector<CTransaction> vtx;
+ std::list<CTransaction> conflicts;
+ SetMockTime(42);
+ SetMockTime(42 + CTxMemPool::ROLLING_FEE_HALFLIFE);
+ BOOST_CHECK_EQUAL(pool.GetMinFee(1).GetFeePerK(), maxFeeRateRemoved.GetFeePerK() + 1000);
+ // ... we should keep the same min fee until we get a block
+ pool.removeForBlock(vtx, 1, conflicts);
+ SetMockTime(42 + 2*CTxMemPool::ROLLING_FEE_HALFLIFE);
+ BOOST_CHECK_EQUAL(pool.GetMinFee(1).GetFeePerK(), (maxFeeRateRemoved.GetFeePerK() + 1000)/2);
+ // ... then feerate should drop 1/2 each halflife
+
+ SetMockTime(42 + 2*CTxMemPool::ROLLING_FEE_HALFLIFE + CTxMemPool::ROLLING_FEE_HALFLIFE/2);
+ BOOST_CHECK_EQUAL(pool.GetMinFee(pool.DynamicMemoryUsage() * 5 / 2).GetFeePerK(), (maxFeeRateRemoved.GetFeePerK() + 1000)/4);
+ // ... with a 1/2 halflife when mempool is < 1/2 its target size
+
+ SetMockTime(42 + 2*CTxMemPool::ROLLING_FEE_HALFLIFE + CTxMemPool::ROLLING_FEE_HALFLIFE/2 + CTxMemPool::ROLLING_FEE_HALFLIFE/4);
+ BOOST_CHECK_EQUAL(pool.GetMinFee(pool.DynamicMemoryUsage() * 9 / 2).GetFeePerK(), (maxFeeRateRemoved.GetFeePerK() + 1000)/8);
+ // ... with a 1/4 halflife when mempool is < 1/4 its target size
+
+ SetMockTime(42 + 7*CTxMemPool::ROLLING_FEE_HALFLIFE + CTxMemPool::ROLLING_FEE_HALFLIFE/2 + CTxMemPool::ROLLING_FEE_HALFLIFE/4);
+ BOOST_CHECK_EQUAL(pool.GetMinFee(1).GetFeePerK(), 1000);
+ // ... but feerate should never drop below 1000
+
+ SetMockTime(42 + 8*CTxMemPool::ROLLING_FEE_HALFLIFE + CTxMemPool::ROLLING_FEE_HALFLIFE/2 + CTxMemPool::ROLLING_FEE_HALFLIFE/4);
+ BOOST_CHECK_EQUAL(pool.GetMinFee(1).GetFeePerK(), 0);
+ // ... unless it has gone all the way to 0 (after getting past 1000/2)
+
+ SetMockTime(0);
+}
+
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/test_bitcoin.cpp b/src/test/test_bitcoin.cpp
index 8d81275a6..a74fbfc0d 100644
--- a/src/test/test_bitcoin.cpp
+++ b/src/test/test_bitcoin.cpp
@@ -32,13 +32,13 @@ CWallet* pwalletMain;
extern bool fPrintToConsole;
extern void noui_connect();
-BasicTestingSetup::BasicTestingSetup(CBaseChainParams::Network network)
+BasicTestingSetup::BasicTestingSetup(const std::string& chainName)
{
ECC_Start();
SetupEnvironment();
fPrintToDebugLog = false; // don't want to write to debug.log file
fCheckBlockIndex = true;
- SelectParams(network);
+ SelectParams(chainName);
noui_connect();
}
@@ -47,7 +47,7 @@ BasicTestingSetup::~BasicTestingSetup()
ECC_Stop();
}
-TestingSetup::TestingSetup(CBaseChainParams::Network network) : BasicTestingSetup(network)
+TestingSetup::TestingSetup(const std::string& chainName) : BasicTestingSetup(chainName)
{
#ifdef ENABLE_WALLET
bitdb.MakeMock();
diff --git a/src/test/test_bitcoin.h b/src/test/test_bitcoin.h
index b9314d061..0bab4b683 100644
--- a/src/test/test_bitcoin.h
+++ b/src/test/test_bitcoin.h
@@ -12,7 +12,7 @@
* This just configures logging and chain parameters.
*/
struct BasicTestingSetup {
- BasicTestingSetup(CBaseChainParams::Network network = CBaseChainParams::MAIN);
+ BasicTestingSetup(const std::string& chainName = CBaseChainParams::MAIN);
~BasicTestingSetup();
};
@@ -25,7 +25,7 @@ struct TestingSetup: public BasicTestingSetup {
boost::filesystem::path pathTemp;
boost::thread_group threadGroup;
- TestingSetup(CBaseChainParams::Network network = CBaseChainParams::MAIN);
+ TestingSetup(const std::string& chainName = CBaseChainParams::MAIN);
~TestingSetup();
};
diff --git a/src/test/transaction_tests.cpp b/src/test/transaction_tests.cpp
index 9847f6512..f9423bc0d 100644
--- a/src/test/transaction_tests.cpp
+++ b/src/test/transaction_tests.cpp
@@ -345,7 +345,7 @@ BOOST_AUTO_TEST_CASE(test_IsStandard)
t.vout[0].nValue = 501; // dust
BOOST_CHECK(!IsStandardTx(t, reason));
- t.vout[0].nValue = 601; // not dust
+ t.vout[0].nValue = 2730; // not dust
BOOST_CHECK(IsStandardTx(t, reason));
t.vout[0].scriptPubKey = CScript() << OP_1;
diff --git a/src/test/txvalidationcache_tests.cpp b/src/test/txvalidationcache_tests.cpp
index edad18644..9b8e1c088 100644
--- a/src/test/txvalidationcache_tests.cpp
+++ b/src/test/txvalidationcache_tests.cpp
@@ -23,7 +23,7 @@ ToMemPool(CMutableTransaction& tx)
LOCK(cs_main);
CValidationState state;
- return AcceptToMemoryPool(mempool, state, tx, false, NULL, false);
+ return AcceptToMemoryPool(mempool, state, tx, false, NULL, true, false);
}
BOOST_FIXTURE_TEST_CASE(tx_mempool_block_doublespend, TestChain100Setup)
diff --git a/src/txdb.cpp b/src/txdb.cpp
index 9738dea03..a441aea68 100644
--- a/src/txdb.cpp
+++ b/src/txdb.cpp
@@ -49,7 +49,7 @@ uint256 CCoinsViewDB::GetBestBlock() const {
}
bool CCoinsViewDB::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock) {
- CLevelDBBatch batch(db.GetObfuscateKey());
+ CLevelDBBatch batch(&db.GetObfuscateKey());
size_t count = 0;
size_t changed = 0;
for (CCoinsMap::iterator it = mapCoins.begin(); it != mapCoins.end();) {
@@ -98,8 +98,8 @@ bool CCoinsViewDB::GetStats(CCoinsStats &stats) const {
/* It seems that there are no "const iterators" for LevelDB. Since we
only need read operations on it, use a const-cast to get around
that restriction. */
- boost::scoped_ptr<leveldb::Iterator> pcursor(const_cast<CLevelDBWrapper*>(&db)->NewIterator());
- pcursor->SeekToFirst();
+ boost::scoped_ptr<CLevelDBIterator> pcursor(const_cast<CLevelDBWrapper*>(&db)->NewIterator());
+ pcursor->Seek(DB_COINS);
CHashWriter ss(SER_GETHASH, PROTOCOL_VERSION);
stats.hashBlock = GetBestBlock();
@@ -107,22 +107,10 @@ bool CCoinsViewDB::GetStats(CCoinsStats &stats) const {
CAmount nTotalAmount = 0;
while (pcursor->Valid()) {
boost::this_thread::interruption_point();
- try {
- leveldb::Slice slKey = pcursor->key();
- CDataStream ssKey(slKey.data(), slKey.data()+slKey.size(), SER_DISK, CLIENT_VERSION);
- char chType;
- ssKey >> chType;
- if (chType == DB_COINS) {
- leveldb::Slice slValue = pcursor->value();
- CDataStream ssValue(slValue.data(), slValue.data()+slValue.size(), SER_DISK, CLIENT_VERSION);
- CCoins coins;
- ssValue >> coins;
- uint256 txhash;
- ssKey >> txhash;
- ss << txhash;
- ss << VARINT(coins.nVersion);
- ss << (coins.fCoinBase ? 'c' : 'n');
- ss << VARINT(coins.nHeight);
+ std::pair<char, uint256> key;
+ CCoins coins;
+ if (pcursor->GetKey(key) && key.first == DB_COINS) {
+ if (pcursor->GetValue(coins)) {
stats.nTransactions++;
for (unsigned int i=0; i<coins.vout.size(); i++) {
const CTxOut &out = coins.vout[i];
@@ -133,13 +121,15 @@ bool CCoinsViewDB::GetStats(CCoinsStats &stats) const {
nTotalAmount += out.nValue;
}
}
- stats.nSerializedSize += 32 + slValue.size();
+ stats.nSerializedSize += 32 + pcursor->GetKeySize();
ss << VARINT(0);
+ } else {
+ return error("CCoinsViewDB::GetStats() : unable to read value");
}
- pcursor->Next();
- } catch (const std::exception& e) {
- return error("%s: Deserialize or I/O error - %s", __func__, e.what());
+ } else {
+ break;
}
+ pcursor->Next();
}
{
LOCK(cs_main);
@@ -151,7 +141,7 @@ bool CCoinsViewDB::GetStats(CCoinsStats &stats) const {
}
bool CBlockTreeDB::WriteBatchSync(const std::vector<std::pair<int, const CBlockFileInfo*> >& fileInfo, int nLastFile, const std::vector<const CBlockIndex*>& blockinfo) {
- CLevelDBBatch batch(GetObfuscateKey());
+ CLevelDBBatch batch(&GetObfuscateKey());
for (std::vector<std::pair<int, const CBlockFileInfo*> >::const_iterator it=fileInfo.begin(); it != fileInfo.end(); it++) {
batch.Write(make_pair(DB_BLOCK_FILES, it->first), *it->second);
}
@@ -167,7 +157,7 @@ bool CBlockTreeDB::ReadTxIndex(const uint256 &txid, CDiskTxPos &pos) {
}
bool CBlockTreeDB::WriteTxIndex(const std::vector<std::pair<uint256, CDiskTxPos> >&vect) {
- CLevelDBBatch batch(GetObfuscateKey());
+ CLevelDBBatch batch(&GetObfuscateKey());
for (std::vector<std::pair<uint256,CDiskTxPos> >::const_iterator it=vect.begin(); it!=vect.end(); it++)
batch.Write(make_pair(DB_TXINDEX, it->first), it->second);
return WriteBatch(batch);
@@ -187,26 +177,17 @@ bool CBlockTreeDB::ReadFlag(const std::string &name, bool &fValue) {
bool CBlockTreeDB::LoadBlockIndexGuts()
{
- boost::scoped_ptr<leveldb::Iterator> pcursor(NewIterator());
+ boost::scoped_ptr<CLevelDBIterator> pcursor(NewIterator());
- CDataStream ssKeySet(SER_DISK, CLIENT_VERSION);
- ssKeySet << make_pair(DB_BLOCK_INDEX, uint256());
- pcursor->Seek(ssKeySet.str());
+ pcursor->Seek(make_pair(DB_BLOCK_INDEX, uint256()));
// Load mapBlockIndex
while (pcursor->Valid()) {
boost::this_thread::interruption_point();
- try {
- leveldb::Slice slKey = pcursor->key();
- CDataStream ssKey(slKey.data(), slKey.data()+slKey.size(), SER_DISK, CLIENT_VERSION);
- char chType;
- ssKey >> chType;
- if (chType == DB_BLOCK_INDEX) {
- leveldb::Slice slValue = pcursor->value();
- CDataStream ssValue(slValue.data(), slValue.data()+slValue.size(), SER_DISK, CLIENT_VERSION);
- CDiskBlockIndex diskindex;
- ssValue >> diskindex;
-
+ std::pair<char, uint256> key;
+ if (pcursor->GetKey(key) && key.first == DB_BLOCK_INDEX) {
+ CDiskBlockIndex diskindex;
+ if (pcursor->GetValue(diskindex)) {
// Construct block index object
CBlockIndex* pindexNew = InsertBlockIndex(diskindex.GetBlockHash());
pindexNew->pprev = InsertBlockIndex(diskindex.hashPrev);
@@ -227,10 +208,10 @@ bool CBlockTreeDB::LoadBlockIndexGuts()
pcursor->Next();
} else {
- break; // if shutdown requested or finished loading block index
+ return error("LoadBlockIndex() : failed to read value");
}
- } catch (const std::exception& e) {
- return error("%s: Deserialize or I/O error - %s", __func__, e.what());
+ } else {
+ break;
}
}
diff --git a/src/txmempool.cpp b/src/txmempool.cpp
index 1370cab0c..bb148005c 100644
--- a/src/txmempool.cpp
+++ b/src/txmempool.cpp
@@ -13,6 +13,7 @@
#include "streams.h"
#include "util.h"
#include "utilmoneystr.h"
+#include "utiltime.h"
#include "version.h"
using namespace std;
@@ -305,15 +306,18 @@ void CTxMemPoolEntry::UpdateState(int64_t modifySize, CAmount modifyFee, int64_t
}
}
-CTxMemPool::CTxMemPool(const CFeeRate& _minRelayFee) :
+CTxMemPool::CTxMemPool(const CFeeRate& _minReasonableRelayFee) :
nTransactionsUpdated(0)
{
+ clear();
+
// Sanity checks off by default for performance, because otherwise
// accepting transactions becomes O(N^2) where N is the number
// of transactions in the pool
fSanityCheck = false;
- minerPolicyEstimator = new CBlockPolicyEstimator(_minRelayFee);
+ minerPolicyEstimator = new CBlockPolicyEstimator(_minReasonableRelayFee);
+ minReasonableRelayFee = _minReasonableRelayFee;
}
CTxMemPool::~CTxMemPool()
@@ -538,6 +542,8 @@ void CTxMemPool::removeForBlock(const std::vector<CTransaction>& vtx, unsigned i
}
// After the txs in the new block have been removed from the mempool, update policy estimates
minerPolicyEstimator->processBlock(nBlockHeight, entries, fCurrentEstimate);
+ lastRollingFeeUpdate = GetTime();
+ blockSinceLastRollingFeeBump = true;
}
void CTxMemPool::clear()
@@ -548,6 +554,9 @@ void CTxMemPool::clear()
mapNextTx.clear();
totalTxSize = 0;
cachedInnerUsage = 0;
+ lastRollingFeeUpdate = GetTime();
+ blockSinceLastRollingFeeBump = false;
+ rollingMinimumFeeRate = 0;
++nTransactionsUpdated;
}
@@ -735,10 +744,10 @@ void CTxMemPool::PrioritiseTransaction(const uint256 hash, const string strHash,
LogPrintf("PrioritiseTransaction: %s priority += %f, fee += %d\n", strHash, dPriorityDelta, FormatMoney(nFeeDelta));
}
-void CTxMemPool::ApplyDeltas(const uint256 hash, double &dPriorityDelta, CAmount &nFeeDelta)
+void CTxMemPool::ApplyDeltas(const uint256 hash, double &dPriorityDelta, CAmount &nFeeDelta) const
{
LOCK(cs);
- std::map<uint256, std::pair<double, CAmount> >::iterator pos = mapDeltas.find(hash);
+ std::map<uint256, std::pair<double, CAmount> >::const_iterator pos = mapDeltas.find(hash);
if (pos == mapDeltas.end())
return;
const std::pair<double, CAmount> &deltas = pos->second;
@@ -792,6 +801,22 @@ void CTxMemPool::RemoveStaged(setEntries &stage) {
}
}
+int CTxMemPool::Expire(int64_t time) {
+ LOCK(cs);
+ indexed_transaction_set::nth_index<2>::type::iterator it = mapTx.get<2>().begin();
+ setEntries toremove;
+ while (it != mapTx.get<2>().end() && it->GetTime() < time) {
+ toremove.insert(mapTx.project<0>(it));
+ it++;
+ }
+ setEntries stage;
+ BOOST_FOREACH(txiter removeit, toremove) {
+ CalculateDescendants(removeit, stage);
+ }
+ RemoveStaged(stage);
+ return stage.size();
+}
+
bool CTxMemPool::addUnchecked(const uint256&hash, const CTxMemPoolEntry &entry, bool fCurrentEstimate)
{
LOCK(cs);
@@ -837,3 +862,62 @@ const CTxMemPool::setEntries & CTxMemPool::GetMemPoolChildren(txiter entry) cons
assert(it != mapLinks.end());
return it->second.children;
}
+
+CFeeRate CTxMemPool::GetMinFee(size_t sizelimit) const {
+ LOCK(cs);
+ if (!blockSinceLastRollingFeeBump || rollingMinimumFeeRate == 0)
+ return CFeeRate(rollingMinimumFeeRate);
+
+ int64_t time = GetTime();
+ if (time > lastRollingFeeUpdate + 10) {
+ double halflife = ROLLING_FEE_HALFLIFE;
+ if (DynamicMemoryUsage() < sizelimit / 4)
+ halflife /= 4;
+ else if (DynamicMemoryUsage() < sizelimit / 2)
+ halflife /= 2;
+
+ rollingMinimumFeeRate = rollingMinimumFeeRate / pow(2.0, (time - lastRollingFeeUpdate) / halflife);
+ lastRollingFeeUpdate = time;
+
+ if (rollingMinimumFeeRate < minReasonableRelayFee.GetFeePerK() / 2) {
+ rollingMinimumFeeRate = 0;
+ return CFeeRate(0);
+ }
+ }
+ return std::max(CFeeRate(rollingMinimumFeeRate), minReasonableRelayFee);
+}
+
+void CTxMemPool::trackPackageRemoved(const CFeeRate& rate) {
+ AssertLockHeld(cs);
+ if (rate.GetFeePerK() > rollingMinimumFeeRate) {
+ rollingMinimumFeeRate = rate.GetFeePerK();
+ blockSinceLastRollingFeeBump = false;
+ }
+}
+
+void CTxMemPool::TrimToSize(size_t sizelimit) {
+ LOCK(cs);
+
+ unsigned nTxnRemoved = 0;
+ CFeeRate maxFeeRateRemoved(0);
+ while (DynamicMemoryUsage() > sizelimit) {
+ indexed_transaction_set::nth_index<1>::type::iterator it = mapTx.get<1>().begin();
+
+ // We set the new mempool min fee to the feerate of the removed set, plus the
+ // "minimum reasonable fee rate" (ie some value under which we consider txn
+ // to have 0 fee). This way, we don't allow txn to enter mempool with feerate
+ // equal to txn which were removed with no block in between.
+ CFeeRate removed(it->GetFeesWithDescendants(), it->GetSizeWithDescendants());
+ removed += minReasonableRelayFee;
+ trackPackageRemoved(removed);
+ maxFeeRateRemoved = std::max(maxFeeRateRemoved, removed);
+
+ setEntries stage;
+ CalculateDescendants(mapTx.project<0>(it), stage);
+ RemoveStaged(stage);
+ nTxnRemoved += stage.size();
+ }
+
+ if (maxFeeRateRemoved > CFeeRate(0))
+ LogPrint("mempool", "Removed %u txn, rolling minimum fee bumped to %s\n", nTxnRemoved, maxFeeRateRemoved.ToString());
+}
diff --git a/src/txmempool.h b/src/txmempool.h
index c0eef0dd2..d44995eef 100644
--- a/src/txmempool.h
+++ b/src/txmempool.h
@@ -83,7 +83,7 @@ public:
const CTransaction& GetTx() const { return this->tx; }
double GetPriority(unsigned int currentHeight) const;
- CAmount GetFee() const { return nFee; }
+ const CAmount& GetFee() const { return nFee; }
size_t GetTxSize() const { return nTxSize; }
int64_t GetTime() const { return nTime; }
unsigned int GetHeight() const { return nHeight; }
@@ -160,9 +160,9 @@ public:
double f2 = aSize * bFees;
if (f1 == f2) {
- return a.GetTime() < b.GetTime();
+ return a.GetTime() >= b.GetTime();
}
- return f1 > f2;
+ return f1 < f2;
}
// Calculate which feerate to use for an entry (avoiding division).
@@ -211,9 +211,10 @@ public:
*
* CTxMemPool::mapTx, and CTxMemPoolEntry bookkeeping:
*
- * mapTx is a boost::multi_index that sorts the mempool on 2 criteria:
+ * mapTx is a boost::multi_index that sorts the mempool on 3 criteria:
* - transaction hash
* - feerate [we use max(feerate of tx, feerate of tx with all descendants)]
+ * - time in mempool
*
* Note: the term "descendant" refers to in-mempool transactions that depend on
* this one, while "ancestor" refers to in-mempool transactions that a given
@@ -284,7 +285,18 @@ private:
uint64_t totalTxSize; //! sum of all mempool tx' byte sizes
uint64_t cachedInnerUsage; //! sum of dynamic memory usage of all the map elements (NOT the maps themselves)
+ CFeeRate minReasonableRelayFee;
+
+ mutable int64_t lastRollingFeeUpdate;
+ mutable bool blockSinceLastRollingFeeBump;
+ mutable double rollingMinimumFeeRate; //! minimum fee to get into the pool, decreases exponentially
+
+ void trackPackageRemoved(const CFeeRate& rate);
+
public:
+
+ static const int ROLLING_FEE_HALFLIFE = 60 * 60 * 12; // public only for testing
+
typedef boost::multi_index_container<
CTxMemPoolEntry,
boost::multi_index::indexed_by<
@@ -294,6 +306,11 @@ public:
boost::multi_index::ordered_non_unique<
boost::multi_index::identity<CTxMemPoolEntry>,
CompareTxMemPoolEntryByFee
+ >,
+ // sorted by entry time
+ boost::multi_index::ordered_non_unique<
+ boost::multi_index::identity<CTxMemPoolEntry>,
+ CompareTxMemPoolEntryByEntryTime
>
>
> indexed_transaction_set;
@@ -328,7 +345,12 @@ public:
std::map<COutPoint, CInPoint> mapNextTx;
std::map<uint256, std::pair<double, CAmount> > mapDeltas;
- CTxMemPool(const CFeeRate& _minRelayFee);
+ /** Create a new CTxMemPool.
+ * minReasonableRelayFee should be a feerate which is, roughly, somewhere
+ * around what it "costs" to relay a transaction around the network and
+ * below which we would reasonably say a transaction has 0-effective-fee.
+ */
+ CTxMemPool(const CFeeRate& _minReasonableRelayFee);
~CTxMemPool();
/**
@@ -365,7 +387,7 @@ public:
/** Affect CreateNewBlock prioritisation of transactions */
void PrioritiseTransaction(const uint256 hash, const std::string strHash, double dPriorityDelta, const CAmount& nFeeDelta);
- void ApplyDeltas(const uint256 hash, double &dPriorityDelta, CAmount &nFeeDelta);
+ void ApplyDeltas(const uint256 hash, double &dPriorityDelta, CAmount &nFeeDelta) const;
void ClearPrioritisation(const uint256 hash);
public:
@@ -397,6 +419,20 @@ public:
*/
bool CalculateMemPoolAncestors(const CTxMemPoolEntry &entry, setEntries &setAncestors, uint64_t limitAncestorCount, uint64_t limitAncestorSize, uint64_t limitDescendantCount, uint64_t limitDescendantSize, std::string &errString, bool fSearchForParents = true);
+ /** The minimum fee to get into the mempool, which may itself not be enough
+ * for larger-sized transactions.
+ * The minReasonableRelayFee constructor arg is used to bound the time it
+ * takes the fee rate to go back down all the way to 0. When the feerate
+ * would otherwise be half of this, it is set to 0 instead.
+ */
+ CFeeRate GetMinFee(size_t sizelimit) const;
+
+ /** Remove transactions from the mempool until its dynamic size is <= sizelimit. */
+ void TrimToSize(size_t sizelimit);
+
+ /** Expire all transaction (and their dependencies) in the mempool older than time. Return the number of removed transactions. */
+ int Expire(int64_t time);
+
unsigned long size()
{
LOCK(cs);
diff --git a/src/univalue/.gitignore b/src/univalue/.gitignore
index ca9e84234..a7a2ca919 100644
--- a/src/univalue/.gitignore
+++ b/src/univalue/.gitignore
@@ -19,4 +19,13 @@ test-driver
libtool
ltmain.sh
+*.a
+*.la
+*.lo
+*.logs
*.o
+*.pc
+*.trs
+
+.dirstamp
+.libs
diff --git a/src/univalue/Makefile.am b/src/univalue/Makefile.am
index 2800f466d..df9e66229 100644
--- a/src/univalue/Makefile.am
+++ b/src/univalue/Makefile.am
@@ -5,20 +5,20 @@ ACLOCAL_AMFLAGS = -I build-aux/m4
include_HEADERS = include/univalue.h
noinst_HEADERS = lib/univalue_escapes.h
-lib_LTLIBRARIES = lib/libunivalue.la
+lib_LTLIBRARIES = libunivalue.la
pkgconfigdir = $(libdir)/pkgconfig
pkgconfig_DATA = pc/libunivalue.pc
-lib_libunivalue_la_SOURCES = \
+libunivalue_la_SOURCES = \
lib/univalue.cpp \
lib/univalue_read.cpp \
lib/univalue_write.cpp
-lib_libunivalue_la_LDFLAGS = \
+libunivalue_la_LDFLAGS = \
-version-info $(LIBUNIVALUE_CURRENT):$(LIBUNIVALUE_REVISION):$(LIBUNIVALUE_AGE) \
-no-undefined
-lib_libunivalue_la_CXXFLAGS = -I$(top_srcdir)/include
+libunivalue_la_CXXFLAGS = -I$(top_srcdir)/include
TESTS = test/unitester
@@ -38,7 +38,7 @@ noinst_PROGRAMS = $(TESTS)
TEST_DATA_DIR=test
test_unitester_SOURCES = test/unitester.cpp
-test_unitester_LDADD = lib/libunivalue.la
+test_unitester_LDADD = libunivalue.la
test_unitester_CXXFLAGS = -I$(top_srcdir)/include -DJSON_TEST_SRC=\"$(srcdir)/$(TEST_DATA_DIR)\"
test_unitester_LDFLAGS = -static $(LIBTOOL_APP_LDFLAGS)
diff --git a/src/univalue/build-aux/m4/.empty b/src/univalue/build-aux/m4/.empty
deleted file mode 100644
index e69de29bb..000000000
--- a/src/univalue/build-aux/m4/.empty
+++ /dev/null
diff --git a/src/univalue/build-aux/m4/.gitignore b/src/univalue/build-aux/m4/.gitignore
new file mode 100644
index 000000000..f06368652
--- /dev/null
+++ b/src/univalue/build-aux/m4/.gitignore
@@ -0,0 +1 @@
+/*.m4
diff --git a/src/univalue/lib/.gitignore b/src/univalue/lib/.gitignore
index ca8c16dcd..ee7fc2851 100644
--- a/src/univalue/lib/.gitignore
+++ b/src/univalue/lib/.gitignore
@@ -1,10 +1,2 @@
-
-libunivalue-uninstalled.pc
-libunivalue.pc
-libunivalue.a
gen
-
.libs
-*.lo
-*.la
-
diff --git a/src/univalue/test/.gitignore b/src/univalue/test/.gitignore
index e4dea0df7..4afa094b1 100644
--- a/src/univalue/test/.gitignore
+++ b/src/univalue/test/.gitignore
@@ -1,7 +1 @@
-
unitester
-
-*.log
-*.trs
-
-.libs
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
index bd3004061..3f2d5a05f 100644
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -2863,6 +2863,6 @@ int CMerkleTx::GetBlocksToMaturity() const
bool CMerkleTx::AcceptToMemoryPool(bool fLimitFree, bool fRejectAbsurdFee)
{
CValidationState state;
- return ::AcceptToMemoryPool(mempool, state, *this, fLimitFree, NULL, fRejectAbsurdFee);
+ return ::AcceptToMemoryPool(mempool, state, *this, fLimitFree, NULL, false, fRejectAbsurdFee);
}