diff options
Diffstat (limited to 'src')
76 files changed, 2155 insertions, 658 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index 462774389a..f35b9dc898 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: @@ -64,6 +64,7 @@ endif bin_PROGRAMS = TESTS = +BENCHMARKS = if BUILD_BITCOIND bin_PROGRAMS += bitcoind @@ -109,7 +110,7 @@ BITCOIN_CORE_H = \ init.h \ key.h \ keystore.h \ - leveldbwrapper.h \ + dbwrapper.h \ limitedmap.h \ main.h \ memusage.h \ @@ -187,7 +188,7 @@ libbitcoin_server_a_SOURCES = \ httprpc.cpp \ httpserver.cpp \ init.cpp \ - leveldbwrapper.cpp \ + dbwrapper.cpp \ main.cpp \ merkleblock.cpp \ miner.cpp \ @@ -213,7 +214,7 @@ libbitcoin_server_a_SOURCES = \ if ENABLE_ZMQ LIBBITCOIN_ZMQ=libbitcoin_zmq.a -libbitcoin_zmq_a_CPPFLAGS = $(BITCOIN_INCLUDES) +libbitcoin_zmq_a_CPPFLAGS = $(BITCOIN_INCLUDES) $(ZMQ_CFLAGS) libbitcoin_zmq_a_SOURCES = \ zmq/zmqabstractnotifier.cpp \ zmq/zmqnotificationinterface.cpp \ @@ -411,7 +412,19 @@ libbitcoinconsensus_la_CPPFLAGS = $(CRYPTO_CFLAGS) -I$(builddir)/obj -DBUILD_BIT endif # -CLEANFILES = leveldb/libleveldb.a leveldb/libmemenv.a *.gcda *.gcno +CLEANFILES = leveldb/libleveldb.a leveldb/libmemenv.a +CLEANFILES += *.gcda *.gcno +CLEANFILES += compat/*.gcda compat/*.gcno +CLEANFILES += consensus/*.gcda consensus/*.gcno +CLEANFILES += crypto/*.gcda crypto/*.gcno +CLEANFILES += policy/*.gcda policy/*.gcno +CLEANFILES += primitives/*.gcda primitives/*.gcno +CLEANFILES += script/*.gcda script/*.gcno +CLEANFILES += support/*.gcda support/*.gcno +CLEANFILES += univalue/*.gcda univalue/*.gcno +CLEANFILES += wallet/*.gcda wallet/*.gcno +CLEANFILES += wallet/test/*.gcda wallet/test/*.gcno +CLEANFILES += zmq/*.gcda zmq/*.gcno DISTCLEANFILES = obj/build.h @@ -420,7 +433,8 @@ EXTRA_DIST = leveldb clean-local: -$(MAKE) -C leveldb clean -$(MAKE) -C secp256k1 clean - rm -f leveldb/*/*.gcno leveldb/helpers/memenv/*.gcno + -$(MAKE) -C univalue clean + -rm -f leveldb/*/*.gcda leveldb/*/*.gcno leveldb/helpers/memenv/*.gcda leveldb/helpers/memenv/*.gcno -rm -f config.h .rc.o: @@ -439,6 +453,10 @@ if ENABLE_TESTS include Makefile.test.include endif +if ENABLE_BENCH +include Makefile.bench.include +endif + if ENABLE_QT include Makefile.qt.include endif diff --git a/src/Makefile.bench.include b/src/Makefile.bench.include new file mode 100644 index 0000000000..61fe9e287d --- /dev/null +++ b/src/Makefile.bench.include @@ -0,0 +1,45 @@ +bin_PROGRAMS += bench/bench_bitcoin +BENCH_SRCDIR = bench +BENCH_BINARY = bench/bench_bitcoin$(EXEEXT) + + +bench_bench_bitcoin_SOURCES = \ + bench/bench_bitcoin.cpp \ + bench/bench.cpp \ + bench/bench.h \ + bench/Examples.cpp + +bench_bench_bitcoin_CPPFLAGS = $(BITCOIN_INCLUDES) $(EVENT_CLFAGS) $(EVENT_PTHREADS_CFLAGS) -I$(builddir)/bench/ +bench_bench_bitcoin_LDADD = \ + $(LIBBITCOIN_SERVER) \ + $(LIBBITCOIN_COMMON) \ + $(LIBBITCOIN_UNIVALUE) \ + $(LIBBITCOIN_UTIL) \ + $(LIBBITCOIN_CRYPTO) \ + $(LIBLEVELDB) \ + $(LIBMEMENV) \ + $(LIBSECP256K1) + +if ENABLE_ZMQ +bench_bench_bitcoin_LDADD += $(LIBBITCOIN_ZMQ) $(ZMQ_LIBS) +endif + +if ENABLE_WALLET +bench_bench_bitcoin_LDADD += $(LIBBITCOIN_WALLET) +endif + +bench_bench_bitcoin_LDADD += $(BOOST_LIBS) $(BDB_LIBS) $(SSL_LIBS) $(CRYPTO_LIBS) $(MINIUPNPC_LIBS) $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS) +bench_bench_bitcoin_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) + + +CLEAN_BITCOIN_BENCH = bench/*.gcda bench/*.gcno + +CLEANFILES += $(CLEAN_BITCOIN_BENCH) + +bitcoin_bench: $(BENCH_BINARY) + +bench: $(BENCH_BINARY) FORCE + $(BENCH_BINARY) + +bitcoin_bench_clean : FORCE + rm -f $(CLEAN_BITCOIN_BENCH) $(bench_bench_bitcoin_OBJECTS) $(BENCH_BINARY) diff --git a/src/Makefile.test.include b/src/Makefile.test.include index 9a6e43631b..f23a8f41fc 100644 --- a/src/Makefile.test.include +++ b/src/Makefile.test.include @@ -36,6 +36,7 @@ GENERATED_TEST_FILES = $(JSON_TEST_FILES:.json=.json.h) $(RAW_TEST_FILES:.raw=.r BITCOIN_TESTS =\ test/arith_uint256_tests.cpp \ test/bignum.h \ + test/addrman_tests.cpp \ test/alert_tests.cpp \ test/allocator_tests.cpp \ test/base32_tests.cpp \ @@ -53,6 +54,7 @@ BITCOIN_TESTS =\ test/hash_tests.cpp \ test/key_tests.cpp \ test/limitedmap_tests.cpp \ + test/dbwrapper_tests.cpp \ test/main_tests.cpp \ test/mempool_tests.cpp \ test/miner_tests.cpp \ @@ -73,6 +75,7 @@ BITCOIN_TESTS =\ test/sighash_tests.cpp \ test/sigopcount_tests.cpp \ test/skiplist_tests.cpp \ + test/streams_tests.cpp \ test/test_bitcoin.cpp \ test/test_bitcoin.h \ test/timedata_tests.cpp \ diff --git a/src/addrman.cpp b/src/addrman.cpp index ff1f7e9187..078b9e1681 100644 --- a/src/addrman.cpp +++ b/src/addrman.cpp @@ -329,13 +329,17 @@ void CAddrMan::Attempt_(const CService& addr, int64_t nTime) info.nAttempts++; } -CAddrInfo CAddrMan::Select_() +CAddrInfo CAddrMan::Select_(bool newOnly) { if (size() == 0) return CAddrInfo(); + if (newOnly && nNew == 0) + return CAddrInfo(); + // Use a 50% chance for choosing between tried and new table entries. - if (nTried > 0 && (nNew == 0 || GetRandInt(2) == 0)) { + if (!newOnly && + (nTried > 0 && (nNew == 0 || GetRandInt(2) == 0))) { // use a tried node double fChanceFactor = 1.0; while (1) { diff --git a/src/addrman.h b/src/addrman.h index 384b6cfdb9..1123caabfa 100644 --- a/src/addrman.h +++ b/src/addrman.h @@ -22,6 +22,8 @@ */ class CAddrInfo : public CAddress { + + public: //! last try whatsoever by us (memory only) int64_t nLastTry; @@ -230,8 +232,8 @@ protected: //! Mark an entry as attempted to connect. void Attempt_(const CService &addr, int64_t nTime); - //! Select an address to connect to. - CAddrInfo Select_(); + //! Select an address to connect to, if newOnly is set to true, only the new table is selected from. + CAddrInfo Select_(bool newOnly); #ifdef DEBUG_ADDRMAN //! Perform consistency check. Returns an error code or zero. @@ -532,13 +534,13 @@ public: /** * Choose an address to connect to. */ - CAddrInfo Select() + CAddrInfo Select(bool newOnly = false) { CAddrInfo addrRet; { LOCK(cs); Check(); - addrRet = Select_(); + addrRet = Select_(newOnly); Check(); } return addrRet; @@ -567,6 +569,12 @@ public: Check(); } } + + //! Ensure that bucket placement is always the same for testing purposes. + void MakeDeterministic(){ + nKey.SetNull(); //Do not use outside of tests. + } + }; #endif // BITCOIN_ADDRMAN_H diff --git a/src/amount.h b/src/amount.h index 90e6b5aa8e..a4c7764cda 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/bench/.gitignore b/src/bench/.gitignore new file mode 100644 index 0000000000..e231fe4cab --- /dev/null +++ b/src/bench/.gitignore @@ -0,0 +1 @@ +bench_bitcoin diff --git a/src/bench/Examples.cpp b/src/bench/Examples.cpp new file mode 100644 index 0000000000..b6b020a971 --- /dev/null +++ b/src/bench/Examples.cpp @@ -0,0 +1,34 @@ +// 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. + +#include "bench.h" +#include "main.h" +#include "utiltime.h" + +// Sanity test: this should loop ten times, and +// min/max/average should be close to 100ms. +static void Sleep100ms(benchmark::State& state) +{ + while (state.KeepRunning()) { + MilliSleep(100); + } +} + +BENCHMARK(Sleep100ms); + +// Extremely fast-running benchmark: +#include <math.h> + +volatile double sum = 0.0; // volatile, global so not optimized away + +static void Trig(benchmark::State& state) +{ + double d = 0.01; + while (state.KeepRunning()) { + sum += sin(d); + d += 0.000001; + } +} + +BENCHMARK(Trig); diff --git a/src/bench/bench.cpp b/src/bench/bench.cpp new file mode 100644 index 0000000000..6ee3cdc27a --- /dev/null +++ b/src/bench/bench.cpp @@ -0,0 +1,70 @@ +// 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. + +#include "bench.h" + +#include <iostream> +#include <sys/time.h> + +using namespace benchmark; + +std::map<std::string, BenchFunction> BenchRunner::benchmarks; + +static double gettimedouble(void) { + struct timeval tv; + gettimeofday(&tv, NULL); + return tv.tv_usec * 0.000001 + tv.tv_sec; +} + +BenchRunner::BenchRunner(std::string name, BenchFunction func) +{ + benchmarks.insert(std::make_pair(name, func)); +} + +void +BenchRunner::RunAll(double elapsedTimeForOne) +{ + std::cout << "Benchmark" << "," << "count" << "," << "min" << "," << "max" << "," << "average" << "\n"; + + for (std::map<std::string,BenchFunction>::iterator it = benchmarks.begin(); + it != benchmarks.end(); ++it) { + + State state(it->first, elapsedTimeForOne); + BenchFunction& func = it->second; + func(state); + } +} + +bool State::KeepRunning() +{ + double now; + if (count == 0) { + beginTime = now = gettimedouble(); + } + else { + // timeCheckCount is used to avoid calling gettime most of the time, + // so benchmarks that run very quickly get consistent results. + if ((count+1)%timeCheckCount != 0) { + ++count; + return true; // keep going + } + now = gettimedouble(); + double elapsedOne = (now - lastTime)/timeCheckCount; + if (elapsedOne < minTime) minTime = elapsedOne; + if (elapsedOne > maxTime) maxTime = elapsedOne; + if (elapsedOne*timeCheckCount < maxElapsed/16) timeCheckCount *= 2; + } + lastTime = now; + ++count; + + if (now - beginTime < maxElapsed) return true; // Keep going + + --count; + + // Output results + double average = (now-beginTime)/count; + std::cout << name << "," << count << "," << minTime << "," << maxTime << "," << average << "\n"; + + return false; +} diff --git a/src/bench/bench.h b/src/bench/bench.h new file mode 100644 index 0000000000..5ce13c642b --- /dev/null +++ b/src/bench/bench.h @@ -0,0 +1,72 @@ +// 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_BENCH_BENCH_H +#define BITCOIN_BENCH_BENCH_H + +#include <map> +#include <string> + +#include <boost/function.hpp> +#include <boost/preprocessor/cat.hpp> +#include <boost/preprocessor/stringize.hpp> + +// Simple micro-benchmarking framework; API mostly matches a subset of the Google Benchmark +// framework (see https://github.com/google/benchmark) +// Wny not use the Google Benchmark framework? Because adding Yet Another Dependency +// (that uses cmake as its build system and has lots of features we don't need) isn't +// worth it. + +/* + * Usage: + +static void CODE_TO_TIME(benchmark::State& state) +{ + ... do any setup needed... + while (state.KeepRunning()) { + ... do stuff you want to time... + } + ... do any cleanup needed... +} + +BENCHMARK(CODE_TO_TIME); + + */ + +namespace benchmark { + + class State { + std::string name; + double maxElapsed; + double beginTime; + double lastTime, minTime, maxTime; + int64_t count; + int64_t timeCheckCount; + public: + State(std::string _name, double _maxElapsed) : name(_name), maxElapsed(_maxElapsed), count(0) { + minTime = std::numeric_limits<double>::max(); + maxTime = std::numeric_limits<double>::min(); + timeCheckCount = 1; + } + bool KeepRunning(); + }; + + typedef boost::function<void(State&)> BenchFunction; + + class BenchRunner + { + static std::map<std::string, BenchFunction> benchmarks; + + public: + BenchRunner(std::string name, BenchFunction func); + + static void RunAll(double elapsedTimeForOne=1.0); + }; +} + +// BENCHMARK(foo) expands to: benchmark::BenchRunner bench_11foo("foo", foo); +#define BENCHMARK(n) \ + benchmark::BenchRunner BOOST_PP_CAT(bench_, BOOST_PP_CAT(__LINE__, n))(BOOST_PP_STRINGIZE(n), n); + +#endif // BITCOIN_BENCH_BENCH_H diff --git a/src/bench/bench_bitcoin.cpp b/src/bench/bench_bitcoin.cpp new file mode 100644 index 0000000000..db1402216d --- /dev/null +++ b/src/bench/bench_bitcoin.cpp @@ -0,0 +1,21 @@ +// 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. + +#include "bench.h" + +#include "key.h" +#include "main.h" +#include "util.h" + +int +main(int argc, char** argv) +{ + ECC_Start(); + SetupEnvironment(); + fPrintToDebugLog = false; // don't want to write to debug.log file + + benchmark::BenchRunner::RunAll(); + + ECC_Stop(); +} diff --git a/src/bitcoin-cli.cpp b/src/bitcoin-cli.cpp index e0fe6aa5bf..9564573657 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 (const 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 f7518fab5d..48033cd8ad 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 (const 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 b512f74c22..addf0e6a26 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 (const std::exception& e) { + fprintf(stderr, "Error: %s\n", e.what()); return false; } diff --git a/src/chainparams.cpp b/src/chainparams.cpp index 430b75683b..dd26c3b31a 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 342bccb12f..cb061d596e 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 9c87bf2154..db2dc751f5 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 = ®TestParams; - 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 4369d0aef7..095c4cbdcb 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 5378c2c761..20c2a25143 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 c480a1cce1..efbbbed352 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/dbwrapper.cpp b/src/dbwrapper.cpp new file mode 100644 index 0000000000..b6307cf0bf --- /dev/null +++ b/src/dbwrapper.cpp @@ -0,0 +1,152 @@ +// Copyright (c) 2012-2014 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "dbwrapper.h" + +#include "util.h" +#include "random.h" + +#include <boost/filesystem.hpp> + +#include <leveldb/cache.h> +#include <leveldb/env.h> +#include <leveldb/filter_policy.h> +#include <memenv.h> +#include <stdint.h> + +void HandleError(const leveldb::Status& status) throw(dbwrapper_error) +{ + if (status.ok()) + return; + LogPrintf("%s\n", status.ToString()); + if (status.IsCorruption()) + throw dbwrapper_error("Database corrupted"); + if (status.IsIOError()) + throw dbwrapper_error("Database I/O error"); + if (status.IsNotFound()) + throw dbwrapper_error("Database entry missing"); + throw dbwrapper_error("Unknown database error"); +} + +static leveldb::Options GetOptions(size_t nCacheSize) +{ + leveldb::Options options; + options.block_cache = leveldb::NewLRUCache(nCacheSize / 2); + options.write_buffer_size = nCacheSize / 4; // up to two write buffers may be held in memory simultaneously + options.filter_policy = leveldb::NewBloomFilterPolicy(10); + options.compression = leveldb::kNoCompression; + options.max_open_files = 64; + if (leveldb::kMajorVersion > 1 || (leveldb::kMajorVersion == 1 && leveldb::kMinorVersion >= 16)) { + // LevelDB versions before 1.16 consider short writes to be corruption. Only trigger error + // on corruption in later versions. + options.paranoid_checks = true; + } + return options; +} + +CDBWrapper::CDBWrapper(const boost::filesystem::path& path, size_t nCacheSize, bool fMemory, bool fWipe, bool obfuscate) +{ + penv = NULL; + readoptions.verify_checksums = true; + iteroptions.verify_checksums = true; + iteroptions.fill_cache = false; + syncoptions.sync = true; + options = GetOptions(nCacheSize); + options.create_if_missing = true; + if (fMemory) { + penv = leveldb::NewMemEnv(leveldb::Env::Default()); + options.env = penv; + } else { + if (fWipe) { + LogPrintf("Wiping LevelDB in %s\n", path.string()); + leveldb::Status result = leveldb::DestroyDB(path.string(), options); + HandleError(result); + } + TryCreateDirectory(path); + LogPrintf("Opening LevelDB in %s\n", path.string()); + } + leveldb::Status status = leveldb::DB::Open(options, path.string(), &pdb); + HandleError(status); + LogPrintf("Opened LevelDB successfully\n"); + + // The base-case obfuscation key, which is a noop. + obfuscate_key = std::vector<unsigned char>(OBFUSCATE_KEY_NUM_BYTES, '\000'); + + bool key_exists = Read(OBFUSCATE_KEY_KEY, obfuscate_key); + + if (!key_exists && obfuscate && IsEmpty()) { + // Initialize non-degenerate obfuscation if it won't upset + // existing, non-obfuscated data. + std::vector<unsigned char> new_key = CreateObfuscateKey(); + + // Write `new_key` so we don't obfuscate the key with itself + Write(OBFUSCATE_KEY_KEY, new_key); + obfuscate_key = new_key; + + LogPrintf("Wrote new obfuscate key for %s: %s\n", path.string(), GetObfuscateKeyHex()); + } + + LogPrintf("Using obfuscation key for %s: %s\n", path.string(), GetObfuscateKeyHex()); +} + +CDBWrapper::~CDBWrapper() +{ + delete pdb; + pdb = NULL; + delete options.filter_policy; + options.filter_policy = NULL; + delete options.block_cache; + options.block_cache = NULL; + delete penv; + options.env = NULL; +} + +bool CDBWrapper::WriteBatch(CDBBatch& batch, bool fSync) throw(dbwrapper_error) +{ + leveldb::Status status = pdb->Write(fSync ? syncoptions : writeoptions, &batch.batch); + HandleError(status); + return true; +} + +// Prefixed with null character to avoid collisions with other keys +// +// We must use a string constructor which specifies length so that we copy +// past the null-terminator. +const std::string CDBWrapper::OBFUSCATE_KEY_KEY("\000obfuscate_key", 14); + +const unsigned int CDBWrapper::OBFUSCATE_KEY_NUM_BYTES = 8; + +/** + * Returns a string (consisting of 8 random bytes) suitable for use as an + * obfuscating XOR key. + */ +std::vector<unsigned char> CDBWrapper::CreateObfuscateKey() const +{ + unsigned char buff[OBFUSCATE_KEY_NUM_BYTES]; + GetRandBytes(buff, OBFUSCATE_KEY_NUM_BYTES); + return std::vector<unsigned char>(&buff[0], &buff[OBFUSCATE_KEY_NUM_BYTES]); + +} + +bool CDBWrapper::IsEmpty() +{ + boost::scoped_ptr<CDBIterator> it(NewIterator()); + it->SeekToFirst(); + return !(it->Valid()); +} + +const std::vector<unsigned char>& CDBWrapper::GetObfuscateKey() const +{ + return obfuscate_key; +} + +std::string CDBWrapper::GetObfuscateKeyHex() const +{ + return HexStr(obfuscate_key); +} + +CDBIterator::~CDBIterator() { delete piter; } +bool CDBIterator::Valid() { return piter->Valid(); } +void CDBIterator::SeekToFirst() { piter->SeekToFirst(); } +void CDBIterator::Next() { piter->Next(); } diff --git a/src/dbwrapper.h b/src/dbwrapper.h new file mode 100644 index 0000000000..1d31ab8ae5 --- /dev/null +++ b/src/dbwrapper.h @@ -0,0 +1,280 @@ +// Copyright (c) 2012-2014 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_DBWRAPPER_H +#define BITCOIN_DBWRAPPER_H + +#include "clientversion.h" +#include "serialize.h" +#include "streams.h" +#include "util.h" +#include "utilstrencodings.h" +#include "version.h" + +#include <boost/filesystem/path.hpp> + +#include <leveldb/db.h> +#include <leveldb/write_batch.h> + +class dbwrapper_error : public std::runtime_error +{ +public: + dbwrapper_error(const std::string& msg) : std::runtime_error(msg) {} +}; + +void HandleError(const leveldb::Status& status) throw(dbwrapper_error); + +/** Batch of changes queued to be written to a CDBWrapper */ +class CDBBatch +{ + friend class CDBWrapper; + +private: + leveldb::WriteBatch batch; + const std::vector<unsigned char> *obfuscate_key; + +public: + /** + * @param[in] obfuscate_key If passed, XOR data with this key. + */ + CDBBatch(const std::vector<unsigned char> *obfuscate_key) : obfuscate_key(obfuscate_key) { }; + + template <typename K, typename V> + void Write(const K& key, const V& value) + { + CDataStream ssKey(SER_DISK, CLIENT_VERSION); + ssKey.reserve(ssKey.GetSerializeSize(key)); + ssKey << key; + leveldb::Slice slKey(&ssKey[0], ssKey.size()); + + CDataStream ssValue(SER_DISK, CLIENT_VERSION); + ssValue.reserve(ssValue.GetSerializeSize(value)); + ssValue << value; + ssValue.Xor(*obfuscate_key); + leveldb::Slice slValue(&ssValue[0], ssValue.size()); + + batch.Put(slKey, slValue); + } + + template <typename K> + void Erase(const K& key) + { + CDataStream ssKey(SER_DISK, CLIENT_VERSION); + ssKey.reserve(ssKey.GetSerializeSize(key)); + ssKey << key; + leveldb::Slice slKey(&ssKey[0], ssKey.size()); + + batch.Delete(slKey); + } +}; + +class CDBIterator +{ +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. + */ + CDBIterator(leveldb::Iterator *piterIn, const std::vector<unsigned char>* obfuscate_key) : + piter(piterIn), obfuscate_key(obfuscate_key) { }; + ~CDBIterator(); + + bool Valid(); + + void SeekToFirst(); + + 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(); + + 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 (const std::exception&) { + 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 (const std::exception&) { + return false; + } + return true; + } + + unsigned int GetValueSize() { + return piter->value().size(); + } + +}; + +class CDBWrapper +{ +private: + //! custom environment this database is using (may be NULL in case of default environment) + leveldb::Env* penv; + + //! database options used + leveldb::Options options; + + //! options used when reading from the database + leveldb::ReadOptions readoptions; + + //! options used when iterating over values of the database + leveldb::ReadOptions iteroptions; + + //! options used when writing to the database + leveldb::WriteOptions writeoptions; + + //! options used when sync writing to the database + leveldb::WriteOptions syncoptions; + + //! the database itself + leveldb::DB* pdb; + + //! a key used for optional XOR-obfuscation of the database + std::vector<unsigned char> obfuscate_key; + + //! the key under which the obfuscation key is stored + static const std::string OBFUSCATE_KEY_KEY; + + //! the length of the obfuscate key in number of bytes + static const unsigned int OBFUSCATE_KEY_NUM_BYTES; + + std::vector<unsigned char> CreateObfuscateKey() const; + +public: + /** + * @param[in] path Location in the filesystem where leveldb data will be stored. + * @param[in] nCacheSize Configures various leveldb cache settings. + * @param[in] fMemory If true, use leveldb's memory environment. + * @param[in] fWipe If true, remove all existing data. + * @param[in] obfuscate If true, store data obfuscated via simple XOR. If false, XOR + * with a zero'd byte array. + */ + CDBWrapper(const boost::filesystem::path& path, size_t nCacheSize, bool fMemory = false, bool fWipe = false, bool obfuscate = false); + ~CDBWrapper(); + + template <typename K, typename V> + bool Read(const K& key, V& value) const throw(dbwrapper_error) + { + CDataStream ssKey(SER_DISK, CLIENT_VERSION); + ssKey.reserve(ssKey.GetSerializeSize(key)); + ssKey << key; + leveldb::Slice slKey(&ssKey[0], ssKey.size()); + + std::string strValue; + leveldb::Status status = pdb->Get(readoptions, slKey, &strValue); + if (!status.ok()) { + if (status.IsNotFound()) + return false; + LogPrintf("LevelDB read failure: %s\n", status.ToString()); + HandleError(status); + } + try { + CDataStream ssValue(strValue.data(), strValue.data() + strValue.size(), SER_DISK, CLIENT_VERSION); + ssValue.Xor(obfuscate_key); + ssValue >> value; + } catch (const std::exception&) { + return false; + } + return true; + } + + template <typename K, typename V> + bool Write(const K& key, const V& value, bool fSync = false) throw(dbwrapper_error) + { + CDBBatch batch(&obfuscate_key); + batch.Write(key, value); + return WriteBatch(batch, fSync); + } + + template <typename K> + bool Exists(const K& key) const throw(dbwrapper_error) + { + CDataStream ssKey(SER_DISK, CLIENT_VERSION); + ssKey.reserve(ssKey.GetSerializeSize(key)); + ssKey << key; + leveldb::Slice slKey(&ssKey[0], ssKey.size()); + + std::string strValue; + leveldb::Status status = pdb->Get(readoptions, slKey, &strValue); + if (!status.ok()) { + if (status.IsNotFound()) + return false; + LogPrintf("LevelDB read failure: %s\n", status.ToString()); + HandleError(status); + } + return true; + } + + template <typename K> + bool Erase(const K& key, bool fSync = false) throw(dbwrapper_error) + { + CDBBatch batch(&obfuscate_key); + batch.Erase(key); + return WriteBatch(batch, fSync); + } + + bool WriteBatch(CDBBatch& batch, bool fSync = false) throw(dbwrapper_error); + + // not available for LevelDB; provide for compatibility with BDB + bool Flush() + { + return true; + } + + bool Sync() throw(dbwrapper_error) + { + CDBBatch batch(&obfuscate_key); + return WriteBatch(batch, true); + } + + CDBIterator *NewIterator() + { + return new CDBIterator(pdb->NewIterator(iteroptions), &obfuscate_key); + } + + /** + * Return true if the database managed by this class contains no entries. + */ + bool IsEmpty(); + + /** + * Accessor for obfuscate_key. + */ + const std::vector<unsigned char>& GetObfuscateKey() const; + + /** + * Return the obfuscate_key as a hex-formatted string. + */ + std::string GetObfuscateKeyHex() const; + +}; + +#endif // BITCOIN_DBWRAPPER_H + diff --git a/src/httpserver.cpp b/src/httpserver.cpp index 0a7f903e9f..8698abb900 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 3c8a9a08ff..d0f73a632b 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 @@ -367,6 +369,7 @@ std::string HelpMessage(HelpMessageMode mode) strUsage += HelpMessageOpt("-whitebind=<addr>", _("Bind to given address and whitelist peers connecting to it. Use [host]:port notation for IPv6")); strUsage += HelpMessageOpt("-whitelist=<netmask>", _("Whitelist peers connecting from the given netmask or IP address. Can be specified multiple times.") + " " + _("Whitelisted peers cannot be DoS banned and their transactions are always relayed, even if they are already in the mempool, useful e.g. for a gateway")); + strUsage += HelpMessageOpt("-maxuploadtarget=<n>", strprintf(_("Tries to keep outbound traffic under the given target (in MiB per 24h), 0 = no limit (default: %d)"), 0)); #ifdef ENABLE_WALLET strUsage += HelpMessageGroup(_("Wallet options:")); @@ -432,6 +435,7 @@ std::string HelpMessage(HelpMessageMode mode) strUsage += HelpMessageOpt("-logtimestamps", strprintf(_("Prepend debug output with timestamp (default: %u)"), 1)); if (showDebug) { + strUsage += HelpMessageOpt("-logtimemicros", strprintf("Add microsecond precision to debug timestamps (default: %u)", DEFAULT_LOGTIMEMICROS)); strUsage += HelpMessageOpt("-limitfreerelay=<n>", strprintf("Continuously rate-limit free transactions to <n>*1000 bytes per minute (default: %u)", 15)); strUsage += HelpMessageOpt("-relaypriority", strprintf("Require high priority for relaying free or low-fee transactions (default: %u)", 1)); strUsage += HelpMessageOpt("-maxsigcachesize=<n>", strprintf("Limit size of signature cache to <n> entries (default: %u)", 50000)); @@ -443,11 +447,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) @@ -693,13 +696,13 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) #endif if (!SetupNetworking()) - return InitError("Error: Initializing networking failed"); + return InitError("Initializing networking failed"); #ifndef WIN32 if (GetBoolArg("-sysperms", false)) { #ifdef ENABLE_WALLET if (!GetBoolArg("-disablewallet", false)) - return InitError("Error: -sysperms is not allowed in combination with enabled wallet functionality"); + return InitError("-sysperms is not allowed in combination with enabled wallet functionality"); #endif } else { umask(077); @@ -730,6 +733,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) // Set this early so that parameter interactions go to console fPrintToConsole = GetBoolArg("-printtoconsole", false); fLogTimestamps = GetBoolArg("-logtimestamps", true); + fLogTimeMicros = GetBoolArg("-logtimemicros", DEFAULT_LOGTIMEMICROS); fLogIPs = GetBoolArg("-logips", false); LogPrintf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"); @@ -829,22 +833,31 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) // Check for -debugnet if (GetBoolArg("-debugnet", false)) - InitWarning(_("Warning: Unsupported argument -debugnet ignored, use -debug=net.")); + InitWarning(_("Unsupported argument -debugnet ignored, use -debug=net.")); // Check for -socks - as this is a privacy risk to continue, exit here if (mapArgs.count("-socks")) - return InitError(_("Error: Unsupported argument -socks found. Setting SOCKS version isn't possible anymore, only SOCKS5 proxies are supported.")); + return InitError(_("Unsupported argument -socks found. Setting SOCKS version isn't possible anymore, only SOCKS5 proxies are supported.")); // Check for -tor - as this is a privacy risk to continue, exit here if (GetBoolArg("-tor", false)) - return InitError(_("Error: Unsupported argument -tor found, use -onion.")); + return InitError(_("Unsupported argument -tor found, use -onion.")); if (GetBoolArg("-benchmark", false)) - InitWarning(_("Warning: Unsupported argument -benchmark ignored, use -debug=bench.")); + InitWarning(_("Unsupported argument -benchmark ignored, use -debug=bench.")); // Checkmempool and checkblockindex default to true in regtest mode - mempool.setSanityCheck(GetBoolArg("-checkmempool", chainparams.DefaultConsistencyChecks())); + int ratio = std::min<int>(std::max<int>(GetArg("-checkmempool", chainparams.DefaultConsistencyChecks() ? 1 : 0), 0), 1000000); + if (ratio != 0) { + mempool.setSanityCheck(1.0 / ratio); + } 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(_("-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) @@ -912,7 +925,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) if (!ParseMoney(mapArgs["-paytxfee"], nFeePerK)) return InitError(strprintf(_("Invalid amount for -paytxfee=<amount>: '%s'"), mapArgs["-paytxfee"])); if (nFeePerK > nHighTransactionFeeWarning) - InitWarning(_("Warning: -paytxfee is set very high! This is the transaction fee you will pay if you send a transaction.")); + InitWarning(_("-paytxfee is set very high! This is the transaction fee you will pay if you send a transaction.")); payTxFee = CFeeRate(nFeePerK, 1000); if (payTxFee < ::minRelayTxFee) { @@ -926,7 +939,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) if (!ParseMoney(mapArgs["-maxtxfee"], nMaxFee)) return InitError(strprintf(_("Invalid amount for -maxtxfee=<amount>: '%s'"), mapArgs["-maptxfee"])); if (nMaxFee > nHighTransactionMaxFeeWarning) - InitWarning(_("Warning: -maxtxfee is set very high! Fees this large could be paid on a single transaction.")); + InitWarning(_("-maxtxfee is set very high! Fees this large could be paid on a single transaction.")); maxTxFee = nMaxFee; if (CFeeRate(maxTxFee, 1000) < ::minRelayTxFee) { @@ -1053,12 +1066,12 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) BOOST_FOREACH(string cmt, mapMultiArgs["-uacomment"]) { if (cmt != SanitizeString(cmt, SAFE_CHARS_UA_COMMENT)) - return InitError(strprintf("User Agent comment (%s) contains unsafe characters.", cmt)); + return InitError(strprintf(_("User Agent comment (%s) contains unsafe characters."), cmt)); uacomments.push_back(SanitizeString(cmt, SAFE_CHARS_UA_COMMENT)); } strSubVersion = FormatSubVersion(CLIENT_NAME, CLIENT_VERSION, uacomments); if (strSubVersion.size() > MAX_SUBVERSION_LENGTH) { - return InitError(strprintf("Total length of network version string %i exceeds maximum of %i characters. Reduce the number and/or size of uacomments.", + return InitError(strprintf(_("Total length of network version string (%i) exceeds maximum length (%i). Reduce the number or size of uacomments."), strSubVersion.size(), MAX_SUBVERSION_LENGTH)); } @@ -1171,6 +1184,9 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) RegisterValidationInterface(pzmqNotificationInterface); } #endif + if (mapArgs.count("-maxuploadtarget")) { + CNode::SetMaxOutboundTarget(GetArg("-maxuploadtarget", 0)*1024*1024); + } // ********************************************************* Step 7: load block chain @@ -1379,9 +1395,8 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) strErrors << _("Error loading wallet.dat: Wallet corrupted") << "\n"; else if (nLoadWalletRet == DB_NONCRITICAL_ERROR) { - string msg(_("Warning: error reading wallet.dat! All keys read correctly, but transaction data" + InitWarning(_("Error reading wallet.dat! All keys read correctly, but transaction data" " or address book entries might be missing or incorrect.")); - InitWarning(msg); } else if (nLoadWalletRet == DB_TOO_NEW) strErrors << _("Error loading wallet.dat: Wallet requires newer version of Bitcoin Core") << "\n"; @@ -1502,10 +1517,10 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) // if pruning, unset the service bit and perform the initial blockstore prune // after any wallet rescanning has taken place. if (fPruneMode) { - uiInterface.InitMessage(_("Pruning blockstore...")); LogPrintf("Unsetting NODE_NETWORK on prune mode\n"); nLocalServices &= ~NODE_NETWORK; if (!fReindex) { + uiInterface.InitMessage(_("Pruning blockstore...")); PruneAndFlush(); } } diff --git a/src/leveldbwrapper.cpp b/src/leveldbwrapper.cpp deleted file mode 100644 index 26cacf95ae..0000000000 --- a/src/leveldbwrapper.cpp +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright (c) 2012-2014 The Bitcoin Core developers -// Distributed under the MIT software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -#include "leveldbwrapper.h" - -#include "util.h" - -#include <boost/filesystem.hpp> - -#include <leveldb/cache.h> -#include <leveldb/env.h> -#include <leveldb/filter_policy.h> -#include <memenv.h> - -void HandleError(const leveldb::Status& status) throw(leveldb_error) -{ - if (status.ok()) - return; - LogPrintf("%s\n", status.ToString()); - if (status.IsCorruption()) - throw leveldb_error("Database corrupted"); - if (status.IsIOError()) - throw leveldb_error("Database I/O error"); - if (status.IsNotFound()) - throw leveldb_error("Database entry missing"); - throw leveldb_error("Unknown database error"); -} - -static leveldb::Options GetOptions(size_t nCacheSize) -{ - leveldb::Options options; - options.block_cache = leveldb::NewLRUCache(nCacheSize / 2); - options.write_buffer_size = nCacheSize / 4; // up to two write buffers may be held in memory simultaneously - options.filter_policy = leveldb::NewBloomFilterPolicy(10); - options.compression = leveldb::kNoCompression; - options.max_open_files = 64; - if (leveldb::kMajorVersion > 1 || (leveldb::kMajorVersion == 1 && leveldb::kMinorVersion >= 16)) { - // LevelDB versions before 1.16 consider short writes to be corruption. Only trigger error - // on corruption in later versions. - options.paranoid_checks = true; - } - return options; -} - -CLevelDBWrapper::CLevelDBWrapper(const boost::filesystem::path& path, size_t nCacheSize, bool fMemory, bool fWipe) -{ - penv = NULL; - readoptions.verify_checksums = true; - iteroptions.verify_checksums = true; - iteroptions.fill_cache = false; - syncoptions.sync = true; - options = GetOptions(nCacheSize); - options.create_if_missing = true; - if (fMemory) { - penv = leveldb::NewMemEnv(leveldb::Env::Default()); - options.env = penv; - } else { - if (fWipe) { - LogPrintf("Wiping LevelDB in %s\n", path.string()); - leveldb::Status result = leveldb::DestroyDB(path.string(), options); - HandleError(result); - } - TryCreateDirectory(path); - LogPrintf("Opening LevelDB in %s\n", path.string()); - } - leveldb::Status status = leveldb::DB::Open(options, path.string(), &pdb); - HandleError(status); - LogPrintf("Opened LevelDB successfully\n"); -} - -CLevelDBWrapper::~CLevelDBWrapper() -{ - delete pdb; - pdb = NULL; - delete options.filter_policy; - options.filter_policy = NULL; - delete options.block_cache; - options.block_cache = NULL; - delete penv; - options.env = NULL; -} - -bool CLevelDBWrapper::WriteBatch(CLevelDBBatch& batch, bool fSync) throw(leveldb_error) -{ - leveldb::Status status = pdb->Write(fSync ? syncoptions : writeoptions, &batch.batch); - HandleError(status); - return true; -} diff --git a/src/leveldbwrapper.h b/src/leveldbwrapper.h deleted file mode 100644 index c65e842704..0000000000 --- a/src/leveldbwrapper.h +++ /dev/null @@ -1,173 +0,0 @@ -// Copyright (c) 2012-2014 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_LEVELDBWRAPPER_H -#define BITCOIN_LEVELDBWRAPPER_H - -#include "clientversion.h" -#include "serialize.h" -#include "streams.h" -#include "util.h" -#include "version.h" - -#include <boost/filesystem/path.hpp> - -#include <leveldb/db.h> -#include <leveldb/write_batch.h> - -class leveldb_error : public std::runtime_error -{ -public: - leveldb_error(const std::string& msg) : std::runtime_error(msg) {} -}; - -void HandleError(const leveldb::Status& status) throw(leveldb_error); - -/** Batch of changes queued to be written to a CLevelDBWrapper */ -class CLevelDBBatch -{ - friend class CLevelDBWrapper; - -private: - leveldb::WriteBatch batch; - -public: - template <typename K, typename V> - void Write(const K& key, const V& value) - { - CDataStream ssKey(SER_DISK, CLIENT_VERSION); - ssKey.reserve(ssKey.GetSerializeSize(key)); - ssKey << key; - leveldb::Slice slKey(&ssKey[0], ssKey.size()); - - CDataStream ssValue(SER_DISK, CLIENT_VERSION); - ssValue.reserve(ssValue.GetSerializeSize(value)); - ssValue << value; - leveldb::Slice slValue(&ssValue[0], ssValue.size()); - - batch.Put(slKey, slValue); - } - - template <typename K> - void Erase(const K& key) - { - CDataStream ssKey(SER_DISK, CLIENT_VERSION); - ssKey.reserve(ssKey.GetSerializeSize(key)); - ssKey << key; - leveldb::Slice slKey(&ssKey[0], ssKey.size()); - - batch.Delete(slKey); - } -}; - -class CLevelDBWrapper -{ -private: - //! custom environment this database is using (may be NULL in case of default environment) - leveldb::Env* penv; - - //! database options used - leveldb::Options options; - - //! options used when reading from the database - leveldb::ReadOptions readoptions; - - //! options used when iterating over values of the database - leveldb::ReadOptions iteroptions; - - //! options used when writing to the database - leveldb::WriteOptions writeoptions; - - //! options used when sync writing to the database - leveldb::WriteOptions syncoptions; - - //! the database itself - leveldb::DB* pdb; - -public: - CLevelDBWrapper(const boost::filesystem::path& path, size_t nCacheSize, bool fMemory = false, bool fWipe = false); - ~CLevelDBWrapper(); - - template <typename K, typename V> - bool Read(const K& key, V& value) const throw(leveldb_error) - { - CDataStream ssKey(SER_DISK, CLIENT_VERSION); - ssKey.reserve(ssKey.GetSerializeSize(key)); - ssKey << key; - leveldb::Slice slKey(&ssKey[0], ssKey.size()); - - std::string strValue; - leveldb::Status status = pdb->Get(readoptions, slKey, &strValue); - if (!status.ok()) { - if (status.IsNotFound()) - return false; - LogPrintf("LevelDB read failure: %s\n", status.ToString()); - HandleError(status); - } - try { - CDataStream ssValue(strValue.data(), strValue.data() + strValue.size(), SER_DISK, CLIENT_VERSION); - ssValue >> value; - } catch (const std::exception&) { - return false; - } - return true; - } - - template <typename K, typename V> - bool Write(const K& key, const V& value, bool fSync = false) throw(leveldb_error) - { - CLevelDBBatch batch; - batch.Write(key, value); - return WriteBatch(batch, fSync); - } - - template <typename K> - bool Exists(const K& key) const throw(leveldb_error) - { - CDataStream ssKey(SER_DISK, CLIENT_VERSION); - ssKey.reserve(ssKey.GetSerializeSize(key)); - ssKey << key; - leveldb::Slice slKey(&ssKey[0], ssKey.size()); - - std::string strValue; - leveldb::Status status = pdb->Get(readoptions, slKey, &strValue); - if (!status.ok()) { - if (status.IsNotFound()) - return false; - LogPrintf("LevelDB read failure: %s\n", status.ToString()); - HandleError(status); - } - return true; - } - - template <typename K> - bool Erase(const K& key, bool fSync = false) throw(leveldb_error) - { - CLevelDBBatch batch; - batch.Erase(key); - return WriteBatch(batch, fSync); - } - - bool WriteBatch(CLevelDBBatch& batch, bool fSync = false) throw(leveldb_error); - - // not available for LevelDB; provide for compatibility with BDB - bool Flush() - { - return true; - } - - bool Sync() throw(leveldb_error) - { - CLevelDBBatch batch; - return WriteBatch(batch, true); - } - - // not exactly clean encapsulation, but it's easiest for now - leveldb::Iterator* NewIterator() - { - return pdb->NewIterator(iteroptions); - } -}; - -#endif // BITCOIN_LEVELDBWRAPPER_H diff --git a/src/main.cpp b/src/main.cpp index 5cfb05b0d2..26a22ae6fd 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); @@ -1740,11 +1751,18 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin unsigned int flags = fStrictPayToScriptHash ? SCRIPT_VERIFY_P2SH : SCRIPT_VERIFY_NONE; - // Start enforcing the DERSIG (BIP66) rules, for block.nVersion=3 blocks, when 75% of the network has upgraded: + // Start enforcing the DERSIG (BIP66) rules, for block.nVersion=3 blocks, + // when 75% of the network has upgraded: if (block.nVersion >= 3 && IsSuperMajority(3, pindex->pprev, chainparams.GetConsensus().nMajorityEnforceBlockUpgrade, chainparams.GetConsensus())) { flags |= SCRIPT_VERIFY_DERSIG; } + // Start enforcing CHECKLOCKTIMEVERIFY, (BIP65) for block.nVersion=4 + // blocks, when 75% of the network has upgraded: + if (block.nVersion >= 4 && IsSuperMajority(4, pindex->pprev, chainparams.GetConsensus().nMajorityEnforceBlockUpgrade, chainparams.GetConsensus())) { + flags |= SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY; + } + CBlockUndo blockundo; CCheckQueueControl<CScriptCheck> control(fScriptChecks && nScriptCheckThreads ? &scriptcheckqueue : NULL); @@ -1881,7 +1899,7 @@ bool static FlushStateToDisk(CValidationState &state, FlushStateMode mode) { std::set<int> setFilesToPrune; bool fFlushForPrune = false; try { - if (fPruneMode && fCheckForPruning) { + if (fPruneMode && fCheckForPruning && !fReindex) { FindFilesToPrune(setFilesToPrune); fCheckForPruning = false; if (!setFilesToPrune.empty()) { @@ -2020,7 +2038,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 +2065,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 +2238,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 +2288,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 +2377,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(); @@ -2684,6 +2709,11 @@ bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& sta return state.Invalid(error("%s : rejected nVersion=2 block", __func__), REJECT_OBSOLETE, "bad-version"); + // Reject block.nVersion=3 blocks when 95% (75% on testnet) of the network has upgraded: + if (block.nVersion < 4 && IsSuperMajority(4, pindexPrev, consensusParams.nMajorityRejectBlockOutdated, consensusParams)) + return state.Invalid(error("%s : rejected nVersion=3 block", __func__), + REJECT_OBSOLETE, "bad-version"); + return true; } @@ -3775,6 +3805,16 @@ void static ProcessGetData(CNode* pfrom) } } } + // disconnect node in case we have reached the outbound limit for serving historical blocks + static const int nOneWeek = 7 * 24 * 60 * 60; // assume > 1 week = historical + if (send && CNode::OutboundTargetReached(true) && ( ((pindexBestHeader != NULL) && (pindexBestHeader->GetBlockTime() - mi->second->GetBlockTime() > nOneWeek)) || inv.type == MSG_FILTERED_BLOCK) ) + { + LogPrint("net", "historical block serving limit reached, disconnect peer=%d\n", pfrom->GetId()); + + //disconnect node + pfrom->fDisconnect = true; + send = false; + } // Pruned nodes may have deleted the block, so check whether // it's available before trying to send. if (send && (mi->second->nStatus & BLOCK_HAVE_DATA)) @@ -4290,10 +4330,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 +4995,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 7fe004c3a1..c3874be663 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) */ @@ -228,7 +232,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/memusage.h b/src/memusage.h index b475c3313b..e96c5bf038 100644 --- a/src/memusage.h +++ b/src/memusage.h @@ -121,4 +121,4 @@ static inline size_t DynamicUsage(const boost::unordered_map<X, Y, Z>& m) } -#endif +#endif // BITCOIN_MEMUSAGE_H diff --git a/src/net.cpp b/src/net.cpp index 87c4f0af0a..e18e8d0e29 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -12,6 +12,7 @@ #include "addrman.h" #include "chainparams.h" #include "clientversion.h" +#include "consensus/consensus.h" #include "crypto/common.h" #include "hash.h" #include "primitives/transaction.h" @@ -326,6 +327,11 @@ uint64_t CNode::nTotalBytesSent = 0; CCriticalSection CNode::cs_totalBytesRecv; CCriticalSection CNode::cs_totalBytesSent; +uint64_t CNode::nMaxOutboundLimit = 0; +uint64_t CNode::nMaxOutboundTotalBytesSentInCycle = 0; +uint64_t CNode::nMaxOutboundTimeframe = 60*60*24; //1 day +uint64_t CNode::nMaxOutboundCycleStartTime = 0; + CNode* FindNode(const CNetAddr& ip) { LOCK(cs_vNodes); @@ -963,6 +969,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 +1805,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 @@ -2071,6 +2089,94 @@ void CNode::RecordBytesSent(uint64_t bytes) { LOCK(cs_totalBytesSent); nTotalBytesSent += bytes; + + uint64_t now = GetTime(); + if (nMaxOutboundCycleStartTime + nMaxOutboundTimeframe < now) + { + // timeframe expired, reset cycle + nMaxOutboundCycleStartTime = now; + nMaxOutboundTotalBytesSentInCycle = 0; + } + + // TODO, exclude whitebind peers + nMaxOutboundTotalBytesSentInCycle += bytes; +} + +void CNode::SetMaxOutboundTarget(uint64_t limit) +{ + LOCK(cs_totalBytesSent); + uint64_t recommendedMinimum = (nMaxOutboundTimeframe / 600) * MAX_BLOCK_SIZE; + nMaxOutboundLimit = limit; + + if (limit < recommendedMinimum) + LogPrintf("Max outbound target is very small (%s) and will be overshot. Recommended minimum is %s\n.", nMaxOutboundLimit, recommendedMinimum); +} + +uint64_t CNode::GetMaxOutboundTarget() +{ + LOCK(cs_totalBytesSent); + return nMaxOutboundLimit; +} + +uint64_t CNode::GetMaxOutboundTimeframe() +{ + LOCK(cs_totalBytesSent); + return nMaxOutboundTimeframe; +} + +uint64_t CNode::GetMaxOutboundTimeLeftInCycle() +{ + LOCK(cs_totalBytesSent); + if (nMaxOutboundLimit == 0) + return 0; + + if (nMaxOutboundCycleStartTime == 0) + return nMaxOutboundTimeframe; + + uint64_t cycleEndTime = nMaxOutboundCycleStartTime + nMaxOutboundTimeframe; + uint64_t now = GetTime(); + return (cycleEndTime < now) ? 0 : cycleEndTime - GetTime(); +} + +void CNode::SetMaxOutboundTimeframe(uint64_t timeframe) +{ + LOCK(cs_totalBytesSent); + if (nMaxOutboundTimeframe != timeframe) + { + // reset measure-cycle in case of changing + // the timeframe + nMaxOutboundCycleStartTime = GetTime(); + } + nMaxOutboundTimeframe = timeframe; +} + +bool CNode::OutboundTargetReached(bool historicalBlockServingLimit) +{ + LOCK(cs_totalBytesSent); + if (nMaxOutboundLimit == 0) + return false; + + if (historicalBlockServingLimit) + { + // keep a large enought buffer to at least relay each block once + uint64_t timeLeftInCycle = GetMaxOutboundTimeLeftInCycle(); + uint64_t buffer = timeLeftInCycle / 600 * MAX_BLOCK_SIZE; + if (buffer >= nMaxOutboundLimit || nMaxOutboundTotalBytesSentInCycle >= nMaxOutboundLimit - buffer) + return true; + } + else if (nMaxOutboundTotalBytesSentInCycle >= nMaxOutboundLimit) + return true; + + return false; +} + +uint64_t CNode::GetOutboundTargetBytesLeft() +{ + LOCK(cs_totalBytesSent); + if (nMaxOutboundLimit == 0) + return 0; + + return (nMaxOutboundTotalBytesSentInCycle >= nMaxOutboundLimit) ? 0 : nMaxOutboundLimit - nMaxOutboundTotalBytesSentInCycle; } uint64_t CNode::GetTotalBytesRecv() @@ -400,6 +400,12 @@ private: static uint64_t nTotalBytesRecv; static uint64_t nTotalBytesSent; + // outbound limit & stats + static uint64_t nMaxOutboundTotalBytesSentInCycle; + static uint64_t nMaxOutboundCycleStartTime; + static uint64_t nMaxOutboundLimit; + static uint64_t nMaxOutboundTimeframe; + CNode(const CNode&); void operator=(const CNode&); @@ -701,6 +707,27 @@ public: static uint64_t GetTotalBytesRecv(); static uint64_t GetTotalBytesSent(); + + //!set the max outbound target in bytes + static void SetMaxOutboundTarget(uint64_t limit); + static uint64_t GetMaxOutboundTarget(); + + //!set the timeframe for the max outbound target + static void SetMaxOutboundTimeframe(uint64_t timeframe); + static uint64_t GetMaxOutboundTimeframe(); + + //!check if the outbound target is reached + // if param historicalBlockServingLimit is set true, the function will + // response true if the limit for serving historical blocks has been reached + static bool OutboundTargetReached(bool historicalBlockServingLimit); + + //!response the bytes left in the current max outbound cycle + // in case of no limit, it will always response 0 + static uint64_t GetOutboundTargetBytesLeft(); + + //!response the time in second left in the current max outbound cycle + // in case of no limit, it will always response 0 + static uint64_t GetMaxOutboundTimeLeftInCycle(); }; diff --git a/src/netbase.cpp b/src/netbase.cpp index c3d56d9184..f5316965ce 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/policy/policy.h b/src/policy/policy.h index 1551aecde8..747c5ce8ce 100644 --- a/src/policy/policy.h +++ b/src/policy/policy.h @@ -3,8 +3,8 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#ifndef BITCOIN_POLICY_H -#define BITCOIN_POLICY_H +#ifndef BITCOIN_POLICY_POLICY_H +#define BITCOIN_POLICY_POLICY_H #include "consensus/consensus.h" #include "script/interpreter.h" @@ -37,7 +37,8 @@ static const unsigned int STANDARD_SCRIPT_VERIFY_FLAGS = MANDATORY_SCRIPT_VERIFY SCRIPT_VERIFY_NULLDUMMY | SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS | SCRIPT_VERIFY_CLEANSTACK | - SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY; + SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY | + SCRIPT_VERIFY_LOW_S; /** For convenience, standard but not mandatory verify flags. */ static const unsigned int STANDARD_NOT_MANDATORY_VERIFY_FLAGS = STANDARD_SCRIPT_VERIFY_FLAGS & ~MANDATORY_SCRIPT_VERIFY_FLAGS; @@ -55,4 +56,4 @@ bool IsStandardTx(const CTransaction& tx, std::string& reason); */ bool AreInputsStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs); -#endif // BITCOIN_POLICY_H +#endif // BITCOIN_POLICY_POLICY_H diff --git a/src/pow.cpp b/src/pow.cpp index bb53ad204b..5ace3fbc9b 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/primitives/block.h b/src/primitives/block.h index 86106098f5..54731ff557 100644 --- a/src/primitives/block.h +++ b/src/primitives/block.h @@ -21,7 +21,7 @@ class CBlockHeader { public: // header - static const int32_t CURRENT_VERSION=3; + static const int32_t CURRENT_VERSION=4; int32_t nVersion; uint256 hashPrevBlock; uint256 hashMerkleRoot; diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index ea7f86d18e..bda8acff15 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/qt/bitcoinstrings.cpp b/src/qt/bitcoinstrings.cpp index 3cde2657cf..538b8912ab 100644 --- a/src/qt/bitcoinstrings.cpp +++ b/src/qt/bitcoinstrings.cpp @@ -13,6 +13,12 @@ QT_TRANSLATE_NOOP("bitcoin-core", "" "(1 = keep tx meta data e.g. account owner and payment request information, 2 " "= drop tx meta data)"), QT_TRANSLATE_NOOP("bitcoin-core", "" +"-maxtxfee is set very high! Fees this large could be paid on a single " +"transaction."), +QT_TRANSLATE_NOOP("bitcoin-core", "" +"-paytxfee is set very high! This is the transaction fee you will pay if you " +"send a transaction."), +QT_TRANSLATE_NOOP("bitcoin-core", "" "Allow JSON-RPC connections from specified source. Valid for <ip> are a " "single IP (e.g. 1.2.3.4), a network/netmask (e.g. 1.2.3.4/255.255.255.0) or " "a network/CIDR (e.g. 1.2.3.4/24). This option can be specified multiple times"), @@ -42,10 +48,12 @@ QT_TRANSLATE_NOOP("bitcoin-core", "" "Distributed under the MIT software license, see the accompanying file " "COPYING or <http://www.opensource.org/licenses/mit-license.php>."), QT_TRANSLATE_NOOP("bitcoin-core", "" -"Error: Listening for incoming connections failed (listen returned error %s)"), +"Do not keep transactions in the mempool longer than <n> hours (default: %u)"), QT_TRANSLATE_NOOP("bitcoin-core", "" -"Error: Unsupported argument -socks found. Setting SOCKS version isn't " -"possible anymore, only SOCKS5 proxies are supported."), +"Error reading wallet.dat! All keys read correctly, but transaction data or " +"address book entries might be missing or incorrect."), +QT_TRANSLATE_NOOP("bitcoin-core", "" +"Error: Listening for incoming connections failed (listen returned error %s)"), QT_TRANSLATE_NOOP("bitcoin-core", "" "Execute command when a relevant alert is received or we see a really long " "fork (%s in cmd is replaced by message)"), @@ -127,9 +135,18 @@ QT_TRANSLATE_NOOP("bitcoin-core", "" "the OpenSSL Toolkit <https://www.openssl.org/> and cryptographic software " "written by Eric Young and UPnP software written by Thomas Bernard."), QT_TRANSLATE_NOOP("bitcoin-core", "" +"Total length of network version string (%i) exceeds maximum length (%i). " +"Reduce the number or size of uacomments."), +QT_TRANSLATE_NOOP("bitcoin-core", "" +"Tries to keep outbound traffic under the given target (in MiB per 24h), 0 = " +"no limit (default: %d)"), +QT_TRANSLATE_NOOP("bitcoin-core", "" "Unable to bind to %s on this computer. Bitcoin Core is probably already " "running."), QT_TRANSLATE_NOOP("bitcoin-core", "" +"Unsupported argument -socks found. Setting SOCKS version isn't possible " +"anymore, only SOCKS5 proxies are supported."), +QT_TRANSLATE_NOOP("bitcoin-core", "" "Use UPnP to map the listening port (default: 1 when listening and no -proxy)"), QT_TRANSLATE_NOOP("bitcoin-core", "" "Use separate SOCKS5 proxy to reach peers via Tor hidden services (default: " @@ -141,21 +158,12 @@ QT_TRANSLATE_NOOP("bitcoin-core", "" "WARNING: check your network connection, %d blocks received in the last %d " "hours (%d expected)"), QT_TRANSLATE_NOOP("bitcoin-core", "" -"Warning: -maxtxfee is set very high! Fees this large could be paid on a " -"single transaction."), -QT_TRANSLATE_NOOP("bitcoin-core", "" -"Warning: -paytxfee is set very high! This is the transaction fee you will " -"pay if you send a transaction."), -QT_TRANSLATE_NOOP("bitcoin-core", "" "Warning: The network does not appear to fully agree! Some miners appear to " "be experiencing issues."), QT_TRANSLATE_NOOP("bitcoin-core", "" "Warning: We do not appear to fully agree with our peers! You may need to " "upgrade, or other nodes may need to upgrade."), QT_TRANSLATE_NOOP("bitcoin-core", "" -"Warning: error reading wallet.dat! All keys read correctly, but transaction " -"data or address book entries might be missing or incorrect."), -QT_TRANSLATE_NOOP("bitcoin-core", "" "Warning: wallet.dat corrupt, data salvaged! Original wallet.dat saved as " "wallet.{timestamp}.bak in %s; if your balance or transactions are incorrect " "you should restore from a backup."), @@ -171,6 +179,7 @@ QT_TRANSLATE_NOOP("bitcoin-core", "" QT_TRANSLATE_NOOP("bitcoin-core", "(default: %s)"), QT_TRANSLATE_NOOP("bitcoin-core", "(default: %u)"), QT_TRANSLATE_NOOP("bitcoin-core", "(default: 1)"), +QT_TRANSLATE_NOOP("bitcoin-core", "-maxmempool must be at least %d MB"), QT_TRANSLATE_NOOP("bitcoin-core", "<category> can be:"), QT_TRANSLATE_NOOP("bitcoin-core", "Accept command line and JSON-RPC commands"), QT_TRANSLATE_NOOP("bitcoin-core", "Accept connections from outside (default: 1 if no -proxy or -connect)"), @@ -212,7 +221,6 @@ QT_TRANSLATE_NOOP("bitcoin-core", "Error reading from database, shutting down.") QT_TRANSLATE_NOOP("bitcoin-core", "Error"), QT_TRANSLATE_NOOP("bitcoin-core", "Error: A fatal internal error occurred, see debug.log for details"), QT_TRANSLATE_NOOP("bitcoin-core", "Error: Disk space is low!"), -QT_TRANSLATE_NOOP("bitcoin-core", "Error: Unsupported argument -tor found, use -onion."), QT_TRANSLATE_NOOP("bitcoin-core", "Failed to listen on any port. Use -listen=0 if you want this."), QT_TRANSLATE_NOOP("bitcoin-core", "Fee (in %s/kB) to add to transactions you send (default: %s)"), QT_TRANSLATE_NOOP("bitcoin-core", "Generate coins (default: %u)"), @@ -233,6 +241,7 @@ QT_TRANSLATE_NOOP("bitcoin-core", "Invalid amount for -paytxfee=<amount>: '%s' ( QT_TRANSLATE_NOOP("bitcoin-core", "Invalid amount for -paytxfee=<amount>: '%s'"), QT_TRANSLATE_NOOP("bitcoin-core", "Invalid netmask specified in -whitelist: '%s'"), QT_TRANSLATE_NOOP("bitcoin-core", "Keep at most <n> unconnectable transactions in memory (default: %u)"), +QT_TRANSLATE_NOOP("bitcoin-core", "Keep the transaction memory pool below <n> megabytes (default: %u)"), QT_TRANSLATE_NOOP("bitcoin-core", "Listen for JSON-RPC connections on <port> (default: %u or testnet: %u)"), QT_TRANSLATE_NOOP("bitcoin-core", "Listen for connections on <port> (default: %u or testnet: %u)"), QT_TRANSLATE_NOOP("bitcoin-core", "Loading addresses..."), @@ -294,9 +303,12 @@ QT_TRANSLATE_NOOP("bitcoin-core", "UI Options:"), QT_TRANSLATE_NOOP("bitcoin-core", "Unable to bind to %s on this computer (bind returned error %s)"), QT_TRANSLATE_NOOP("bitcoin-core", "Unable to start HTTP server. See debug log for details."), QT_TRANSLATE_NOOP("bitcoin-core", "Unknown network specified in -onlynet: '%s'"), +QT_TRANSLATE_NOOP("bitcoin-core", "Unsupported argument -benchmark ignored, use -debug=bench."), +QT_TRANSLATE_NOOP("bitcoin-core", "Unsupported argument -debugnet ignored, use -debug=net."), +QT_TRANSLATE_NOOP("bitcoin-core", "Unsupported argument -tor found, use -onion."), QT_TRANSLATE_NOOP("bitcoin-core", "Upgrade wallet to latest format"), QT_TRANSLATE_NOOP("bitcoin-core", "Use UPnP to map the listening port (default: %u)"), -QT_TRANSLATE_NOOP("bitcoin-core", "Use the test network"), +QT_TRANSLATE_NOOP("bitcoin-core", "User Agent comment (%s) contains unsafe characters."), QT_TRANSLATE_NOOP("bitcoin-core", "Username for JSON-RPC connections"), QT_TRANSLATE_NOOP("bitcoin-core", "Verifying blocks..."), QT_TRANSLATE_NOOP("bitcoin-core", "Verifying wallet..."), @@ -305,8 +317,6 @@ QT_TRANSLATE_NOOP("bitcoin-core", "Wallet needed to be rewritten: restart Bitcoi QT_TRANSLATE_NOOP("bitcoin-core", "Wallet options:"), QT_TRANSLATE_NOOP("bitcoin-core", "Warning"), QT_TRANSLATE_NOOP("bitcoin-core", "Warning: This version is obsolete; upgrade required!"), -QT_TRANSLATE_NOOP("bitcoin-core", "Warning: Unsupported argument -benchmark ignored, use -debug=bench."), -QT_TRANSLATE_NOOP("bitcoin-core", "Warning: Unsupported argument -debugnet ignored, use -debug=net."), QT_TRANSLATE_NOOP("bitcoin-core", "You need to rebuild the database using -reindex to change -txindex"), QT_TRANSLATE_NOOP("bitcoin-core", "Zapping all transactions from wallet..."), QT_TRANSLATE_NOOP("bitcoin-core", "ZeroMQ notification options:"), diff --git a/src/qt/locale/bitcoin_en.ts b/src/qt/locale/bitcoin_en.ts index 3ebb4d0bf6..58921a9f8b 100644 --- a/src/qt/locale/bitcoin_en.ts +++ b/src/qt/locale/bitcoin_en.ts @@ -3706,7 +3706,7 @@ <context> <name>bitcoin-core</name> <message> - <location filename="../bitcoinstrings.cpp" line="+249"/> + <location filename="../bitcoinstrings.cpp" line="+258"/> <source>Options:</source> <translation>Options:</translation> </message> @@ -3731,7 +3731,7 @@ <translation>Accept command line and JSON-RPC commands</translation> </message> <message> - <location line="-117"/> + <location line="-118"/> <source>Fees (in %s/kB) smaller than this are considered zero fee for relaying (default: %s)</source> <translation type="unfinished"></translation> </message> @@ -3771,17 +3771,17 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+109"/> + <location line="+110"/> <source>Error: A fatal internal error occurred, see debug.log for details</source> <translation type="unfinished"></translation> </message> <message> - <location line="+4"/> + <location line="+3"/> <source>Fee (in %s/kB) to add to transactions you send (default: %s)</source> <translation type="unfinished"></translation> </message> <message> - <location line="+37"/> + <location line="+38"/> <source>Pruning blockstore...</source> <translation type="unfinished"></translation> </message> @@ -3796,17 +3796,12 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+4"/> - <source>Use the test network</source> - <translation>Use the test network</translation> - </message> - <message> - <location line="-123"/> + <location line="-119"/> <source>Accept connections from outside (default: 1 if no -proxy or -connect)</source> <translation>Accept connections from outside (default: 1 if no -proxy or -connect)</translation> </message> <message> - <location line="-157"/> + <location line="-160"/> <source>Bind to given address and always listen on it. Use [host]:port notation for IPv6</source> <translation>Bind to given address and always listen on it. Use [host]:port notation for IPv6</translation> </message> @@ -3821,7 +3816,7 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+11"/> + <location line="+13"/> <source>Execute command when a wallet transaction changes (%s in cmd is replaced by TxID)</source> <translation>Execute command when a wallet transaction changes (%s in cmd is replaced by TxID)</translation> </message> @@ -3841,12 +3836,12 @@ <translation>This is a pre-release test build - use at your own risk - do not use for mining or merchant applications</translation> </message> <message> - <location line="+7"/> + <location line="+13"/> <source>Unable to bind to %s on this computer. Bitcoin Core is probably already running.</source> <translation type="unfinished"></translation> </message> <message> - <location line="+3"/> + <location line="+6"/> <source>Use UPnP to map the listening port (default: 1 when listening and no -proxy)</source> <translation type="unfinished"></translation> </message> @@ -3861,11 +3856,6 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+6"/> - <source>Warning: -paytxfee is set very high! This is the transaction fee you will pay if you send a transaction.</source> - <translation>Warning: -paytxfee is set very high! This is the transaction fee you will pay if you send a transaction.</translation> - </message> - <message> <location line="+3"/> <source>Warning: The network does not appear to fully agree! Some miners appear to be experiencing issues.</source> <translation>Warning: The network does not appear to fully agree! Some miners appear to be experiencing issues.</translation> @@ -3877,11 +3867,6 @@ </message> <message> <location line="+3"/> - <source>Warning: error reading wallet.dat! All keys read correctly, but transaction data or address book entries might be missing or incorrect.</source> - <translation>Warning: error reading wallet.dat! All keys read correctly, but transaction data or address book entries might be missing or incorrect.</translation> - </message> - <message> - <location line="+3"/> <source>Warning: wallet.dat corrupt, data salvaged! Original wallet.dat saved as wallet.{timestamp}.bak in %s; if your balance or transactions are incorrect you should restore from a backup.</source> <translation>Warning: wallet.dat corrupt, data salvaged! Original wallet.dat saved as wallet.{timestamp}.bak in %s; if your balance or transactions are incorrect you should restore from a backup.</translation> </message> @@ -3897,6 +3882,11 @@ </message> <message> <location line="+1"/> + <source>-maxmempool must be at least %d MB</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+1"/> <source><category> can be:</source> <translation type="unfinished"></translation> </message> @@ -3986,7 +3976,7 @@ <translation>Error: Disk space is low!</translation> </message> <message> - <location line="+2"/> + <location line="+1"/> <source>Failed to listen on any port. Use -listen=0 if you want this.</source> <translation>Failed to listen on any port. Use -listen=0 if you want this.</translation> </message> @@ -4006,7 +3996,12 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+20"/> + <location line="+9"/> + <source>Keep the transaction memory pool below <n> megabytes (default: %u)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+12"/> <source>Not enough file descriptors available.</source> <translation>Not enough file descriptors available.</translation> </message> @@ -4041,12 +4036,32 @@ <translation>Specify wallet file (within data directory)</translation> </message> <message> - <location line="+17"/> + <location line="+16"/> + <source>Unsupported argument -benchmark ignored, use -debug=bench.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+1"/> + <source>Unsupported argument -debugnet ignored, use -debug=net.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+1"/> + <source>Unsupported argument -tor found, use -onion.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+2"/> <source>Use UPnP to map the listening port (default: %u)</source> <translation type="unfinished"></translation> </message> <message> - <location line="+3"/> + <location line="+1"/> + <source>User Agent comment (%s) contains unsafe characters.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+2"/> <source>Verifying blocks...</source> <translation>Verifying blocks...</translation> </message> @@ -4071,17 +4086,17 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+3"/> + <location line="+1"/> <source>You need to rebuild the database using -reindex to change -txindex</source> <translation>You need to rebuild the database using -reindex to change -txindex</translation> </message> <message> - <location line="-89"/> + <location line="-91"/> <source>Imports blocks from external blk000??.dat file</source> <translation>Imports blocks from external blk000??.dat file</translation> </message> <message> - <location line="-206"/> + <location line="-208"/> <source>Allow JSON-RPC connections from specified source. Valid for <ip> are a single IP (e.g. 1.2.3.4), a network/netmask (e.g. 1.2.3.4/255.255.255.0) or a network/CIDR (e.g. 1.2.3.4/24). This option can be specified multiple times</source> <translation type="unfinished"></translation> </message> @@ -4111,17 +4126,12 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+6"/> + <location line="+11"/> <source>Error: Listening for incoming connections failed (listen returned error %s)</source> <translation type="unfinished"></translation> </message> <message> <location line="+2"/> - <source>Error: Unsupported argument -socks found. Setting SOCKS version isn't possible anymore, only SOCKS5 proxies are supported.</source> - <translation type="unfinished"></translation> - </message> - <message> - <location line="+3"/> <source>Execute command when a relevant alert is received or we see a really long fork (%s in cmd is replaced by message)</source> <translation>Execute command when a relevant alert is received or we see a really long fork (%s in cmd is replaced by message)</translation> </message> @@ -4171,12 +4181,7 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+18"/> - <source>Warning: -maxtxfee is set very high! Fees this large could be paid on a single transaction.</source> - <translation type="unfinished"></translation> - </message> - <message> - <location line="+22"/> + <location line="+40"/> <source>Whitelisted peers cannot be DoS banned and their transactions are always relayed, even if they are already in the mempool, useful e.g. for a gateway</source> <translation type="unfinished"></translation> </message> @@ -4191,7 +4196,7 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+5"/> + <location line="+6"/> <source>Accept public REST requests (default: %u)</source> <translation type="unfinished"></translation> </message> @@ -4231,12 +4236,7 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+4"/> - <source>Error: Unsupported argument -tor found, use -onion.</source> - <translation type="unfinished"></translation> - </message> - <message> - <location line="+9"/> + <location line="+12"/> <source>Information</source> <translation>Information</translation> </message> @@ -4276,7 +4276,7 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+10"/> + <location line="+11"/> <source>Need to specify a port with -whitebind: '%s'</source> <translation type="unfinished"></translation> </message> @@ -4391,7 +4391,7 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+6"/> + <location line="+9"/> <source>Username for JSON-RPC connections</source> <translation>Username for JSON-RPC connections</translation> </message> @@ -4406,17 +4406,7 @@ <translation>Warning</translation> </message> <message> - <location line="+2"/> - <source>Warning: Unsupported argument -benchmark ignored, use -debug=bench.</source> - <translation type="unfinished"></translation> - </message> - <message> - <location line="+1"/> - <source>Warning: Unsupported argument -debugnet ignored, use -debug=net.</source> - <translation type="unfinished"></translation> - </message> - <message> - <location line="+2"/> + <location line="+3"/> <source>Zapping all transactions from wallet...</source> <translation type="unfinished"></translation> </message> @@ -4436,22 +4426,22 @@ <translation>wallet.dat corrupt, salvage failed</translation> </message> <message> - <location line="-64"/> + <location line="-65"/> <source>Password for JSON-RPC connections</source> <translation>Password for JSON-RPC connections</translation> </message> <message> - <location line="-195"/> + <location line="-196"/> <source>Execute command when the best block changes (%s in cmd is replaced by block hash)</source> <translation>Execute command when the best block changes (%s in cmd is replaced by block hash)</translation> </message> <message> - <location line="+242"/> + <location line="+246"/> <source>Upgrade wallet to latest format</source> <translation>Upgrade wallet to latest format</translation> </message> <message> - <location line="-36"/> + <location line="-39"/> <source>Rescan the block chain for missing wallet transactions</source> <translation>Rescan the block chain for missing wallet transactions</translation> </message> @@ -4476,12 +4466,32 @@ <translation>Error loading wallet.dat: Wallet corrupted</translation> </message> <message> - <location line="-196"/> + <location line="-205"/> <source>(1 = keep tx meta data e.g. account owner and payment request information, 2 = drop tx meta data)</source> <translation type="unfinished"></translation> </message> <message> - <location line="+49"/> + <location line="+3"/> + <source>-maxtxfee is set very high! Fees this large could be paid on a single transaction.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+3"/> + <source>-paytxfee is set very high! This is the transaction fee you will pay if you send a transaction.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+32"/> + <source>Do not keep transactions in the mempool longer than <n> hours (default: %u)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+2"/> + <source>Error reading wallet.dat! All keys read correctly, but transaction data or address book entries might be missing or incorrect.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+17"/> <source>How thorough the block verification of -checkblocks is (0-4, default: %u)</source> <translation type="unfinished"></translation> </message> @@ -4501,17 +4511,32 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+51"/> + <location line="+46"/> + <source>Total length of network version string (%i) exceeds maximum length (%i). Reduce the number or size of uacomments.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+3"/> + <source>Tries to keep outbound traffic under the given target (in MiB per 24h), 0 = no limit (default: %d)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+6"/> + <source>Unsupported argument -socks found. Setting SOCKS version isn't possible anymore, only SOCKS5 proxies are supported.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+5"/> <source>Use separate SOCKS5 proxy to reach peers via Tor hidden services (default: %s)</source> <translation type="unfinished"></translation> </message> <message> - <location line="+37"/> + <location line="+28"/> <source>(default: %s)</source> <translation type="unfinished"></translation> </message> <message> - <location line="+10"/> + <location line="+11"/> <source>Always query for peer addresses via DNS lookup (default: %u)</source> <translation type="unfinished"></translation> </message> @@ -4521,7 +4546,7 @@ <translation>Error loading wallet.dat</translation> </message> <message> - <location line="+11"/> + <location line="+10"/> <source>Generate coins (default: %u)</source> <translation type="unfinished"></translation> </message> @@ -4541,7 +4566,7 @@ <translation>Invalid -proxy address: '%s'</translation> </message> <message> - <location line="+8"/> + <location line="+9"/> <source>Listen for JSON-RPC connections on <port> (default: %u or testnet: %u)</source> <translation type="unfinished"></translation> </message> @@ -4641,7 +4666,7 @@ <translation>Cannot resolve -externalip address: '%s'</translation> </message> <message> - <location line="+47"/> + <location line="+46"/> <source>Invalid amount for -paytxfee=<amount>: '%s'</source> <translation>Invalid amount for -paytxfee=<amount>: '%s'</translation> </message> @@ -4651,7 +4676,7 @@ <translation>Insufficient funds</translation> </message> <message> - <location line="+13"/> + <location line="+14"/> <source>Loading block index...</source> <translation>Loading block index...</translation> </message> diff --git a/src/rpcblockchain.cpp b/src/rpcblockchain.cpp index 58f49f9f56..146eb3905a 100644 --- a/src/rpcblockchain.cpp +++ b/src/rpcblockchain.cpp @@ -648,6 +648,7 @@ UniValue getblockchaininfo(const UniValue& params, bool fHelp) UniValue softforks(UniValue::VARR); softforks.push_back(SoftForkDesc("bip34", 2, tip, consensusParams)); softforks.push_back(SoftForkDesc("bip66", 3, tip, consensusParams)); + softforks.push_back(SoftForkDesc("bip65", 4, tip, consensusParams)); obj.push_back(Pair("softforks", softforks)); if (fPruneMode) @@ -772,6 +773,9 @@ UniValue mempoolInfoToJSON() 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())); + size_t maxmempool = GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000; + ret.push_back(Pair("maxmempool", (int64_t) maxmempool)); + ret.push_back(Pair("mempoolminfee", ValueFromAmount(mempool.GetMinFee(maxmempool).GetFeePerK()))); return ret; } diff --git a/src/rpcclient.cpp b/src/rpcclient.cpp index 4064c2fee3..343b6234d4 100644 --- a/src/rpcclient.cpp +++ b/src/rpcclient.cpp @@ -76,6 +76,7 @@ static const CRPCConvertParam vRPCConvertParams[] = { "getrawtransaction", 1 }, { "createrawtransaction", 0 }, { "createrawtransaction", 1 }, + { "createrawtransaction", 2 }, { "signrawtransaction", 1 }, { "signrawtransaction", 2 }, { "sendrawtransaction", 1 }, diff --git a/src/rpcnet.cpp b/src/rpcnet.cpp index 7746be25f7..9bf017e385 100644 --- a/src/rpcnet.cpp +++ b/src/rpcnet.cpp @@ -29,7 +29,7 @@ UniValue getconnectioncount(const UniValue& params, bool fHelp) throw runtime_error( "getconnectioncount\n" "\nReturns the number of connections to other nodes.\n" - "\nbResult:\n" + "\nResult:\n" "n (numeric) The connection count\n" "\nExamples:\n" + HelpExampleCli("getconnectioncount", "") @@ -83,7 +83,7 @@ UniValue getpeerinfo(const UniValue& params, bool fHelp) throw runtime_error( "getpeerinfo\n" "\nReturns data about each connected network node as a json array of objects.\n" - "\nbResult:\n" + "\nResult:\n" "[\n" " {\n" " \"id\": n, (numeric) Peer index\n" @@ -379,6 +379,15 @@ UniValue getnettotals(const UniValue& params, bool fHelp) obj.push_back(Pair("totalbytesrecv", CNode::GetTotalBytesRecv())); obj.push_back(Pair("totalbytessent", CNode::GetTotalBytesSent())); obj.push_back(Pair("timemillis", GetTimeMillis())); + + UniValue outboundLimit(UniValue::VOBJ); + outboundLimit.push_back(Pair("timeframe", CNode::GetMaxOutboundTimeframe())); + outboundLimit.push_back(Pair("target", CNode::GetMaxOutboundTarget())); + outboundLimit.push_back(Pair("target_reached", CNode::OutboundTargetReached(false))); + outboundLimit.push_back(Pair("serve_historical_blocks", !CNode::OutboundTargetReached(true))); + outboundLimit.push_back(Pair("bytes_left_in_cycle", CNode::GetOutboundTargetBytesLeft())); + outboundLimit.push_back(Pair("time_left_in_cycle", CNode::GetMaxOutboundTimeLeftInCycle())); + obj.push_back(Pair("uploadtarget", outboundLimit)); return obj; } diff --git a/src/rpcrawtransaction.cpp b/src/rpcrawtransaction.cpp index 4dec53396d..5f3363d097 100644 --- a/src/rpcrawtransaction.cpp +++ b/src/rpcrawtransaction.cpp @@ -316,9 +316,9 @@ UniValue verifytxoutproof(const UniValue& params, bool fHelp) UniValue createrawtransaction(const UniValue& params, bool fHelp) { - if (fHelp || params.size() != 2) + if (fHelp || params.size() < 2 || params.size() > 3) throw runtime_error( - "createrawtransaction [{\"txid\":\"id\",\"vout\":n},...] {\"address\":amount,\"data\":\"hex\",...}\n" + "createrawtransaction [{\"txid\":\"id\",\"vout\":n},...] {\"address\":amount,\"data\":\"hex\",...} ( locktime )\n" "\nCreate a transaction spending the given inputs and creating new outputs.\n" "Outputs can be addresses or data.\n" "Returns hex-encoded raw transaction.\n" @@ -340,6 +340,7 @@ UniValue createrawtransaction(const UniValue& params, bool fHelp) " \"data\": \"hex\", (string, required) The key is \"data\", the value is hex encoded data\n" " ...\n" " }\n" + "3. locktime (numeric, optional, default=0) Raw locktime. Non-0 value also locktime-activates inputs\n" "\nResult:\n" "\"transaction\" (string) hex string of the transaction\n" @@ -351,13 +352,22 @@ UniValue createrawtransaction(const UniValue& params, bool fHelp) ); LOCK(cs_main); - RPCTypeCheck(params, boost::assign::list_of(UniValue::VARR)(UniValue::VOBJ)); + RPCTypeCheck(params, boost::assign::list_of(UniValue::VARR)(UniValue::VOBJ)(UniValue::VNUM), true); + if (params[0].isNull() || params[1].isNull()) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, arguments 1 and 2 must be non-null"); UniValue inputs = params[0].get_array(); UniValue sendTo = params[1].get_obj(); CMutableTransaction rawTx; + if (params.size() > 2 && !params[2].isNull()) { + int64_t nLockTime = params[2].get_int64(); + if (nLockTime < 0 || nLockTime > std::numeric_limits<uint32_t>::max()) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, locktime out of range"); + rawTx.nLockTime = nLockTime; + } + for (unsigned int idx = 0; idx < inputs.size(); idx++) { const UniValue& input = inputs[idx]; const UniValue& o = input.get_obj(); @@ -371,7 +381,9 @@ UniValue createrawtransaction(const UniValue& params, bool fHelp) if (nOutput < 0) throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout must be positive"); - CTxIn in(COutPoint(txid, nOutput)); + uint32_t nSequence = (rawTx.nLockTime ? std::numeric_limits<uint32_t>::max() - 1 : std::numeric_limits<uint32_t>::max()); + CTxIn in(COutPoint(txid, nOutput), CScript(), nSequence); + rawTx.vin.push_back(in); } @@ -809,7 +821,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/rpcserver.cpp b/src/rpcserver.cpp index fa60f8c833..8bda5a0373 100644 --- a/src/rpcserver.cpp +++ b/src/rpcserver.cpp @@ -563,7 +563,7 @@ void RPCRunLater(const std::string& name, boost::function<void(void)> func, int6 deadlineTimers.erase(name); RPCTimerInterface* timerInterface = timerInterfaces[0]; LogPrint("rpc", "queue run of timer %s in %i seconds (using %s)\n", name, nSeconds, timerInterface->Name()); - deadlineTimers.insert(std::make_pair(name, timerInterface->NewTimer(func, nSeconds*1000))); + deadlineTimers.insert(std::make_pair(name, boost::shared_ptr<RPCTimerBase>(timerInterface->NewTimer(func, nSeconds*1000)))); } const CRPCTable tableRPC; diff --git a/src/script/bitcoinconsensus.h b/src/script/bitcoinconsensus.h index 0320577797..a48ff1e18d 100644 --- a/src/script/bitcoinconsensus.h +++ b/src/script/bitcoinconsensus.h @@ -44,9 +44,10 @@ typedef enum bitcoinconsensus_error_t /** Script verification flags */ enum { - bitcoinconsensus_SCRIPT_FLAGS_VERIFY_NONE = 0, - bitcoinconsensus_SCRIPT_FLAGS_VERIFY_P2SH = (1U << 0), // evaluate P2SH (BIP16) subscripts - bitcoinconsensus_SCRIPT_FLAGS_VERIFY_DERSIG = (1U << 2), // enforce strict DER (BIP66) compliance + bitcoinconsensus_SCRIPT_FLAGS_VERIFY_NONE = 0, + bitcoinconsensus_SCRIPT_FLAGS_VERIFY_P2SH = (1U << 0), // evaluate P2SH (BIP16) subscripts + bitcoinconsensus_SCRIPT_FLAGS_VERIFY_DERSIG = (1U << 2), // enforce strict DER (BIP66) compliance + bitcoinconsensus_SCRIPT_FLAGS_VERIFY_CHECKLOCKTIMEVERIFY = (1U << 9), // enable CHECKLOCKTIMEVERIFY (BIP65) }; /// Returns 1 if the input nIn of the serialized transaction pointed to by diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index d3aec26020..6a20d497c0 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -273,7 +273,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un return set_error(serror, SCRIPT_ERR_PUSH_SIZE); // Note how OP_RESERVED does not count towards the opcode limit. - if (opcode > OP_16 && ++nOpCount > 201) + if (opcode > OP_16 && ++nOpCount > MAX_OPS_PER_SCRIPT) return set_error(serror, SCRIPT_ERR_OP_COUNT); if (opcode == OP_CAT || @@ -869,10 +869,10 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION); int nKeysCount = CScriptNum(stacktop(-i), fRequireMinimal).getint(); - if (nKeysCount < 0 || nKeysCount > 20) + if (nKeysCount < 0 || nKeysCount > MAX_PUBKEYS_PER_MULTISIG) return set_error(serror, SCRIPT_ERR_PUBKEY_COUNT); nOpCount += nKeysCount; - if (nOpCount > 201) + if (nOpCount > MAX_OPS_PER_SCRIPT) return set_error(serror, SCRIPT_ERR_OP_COUNT); int ikey = ++i; i += nKeysCount; diff --git a/src/script/script.cpp b/src/script/script.cpp index 9a0c067a33..263c89defe 100644 --- a/src/script/script.cpp +++ b/src/script/script.cpp @@ -170,7 +170,7 @@ unsigned int CScript::GetSigOpCount(bool fAccurate) const if (fAccurate && lastOpcode >= OP_1 && lastOpcode <= OP_16) n += DecodeOP_N(lastOpcode); else - n += 20; + n += MAX_PUBKEYS_PER_MULTISIG; } lastOpcode = opcode; } diff --git a/src/script/script.h b/src/script/script.h index cdc9a71bb2..a38d33a189 100644 --- a/src/script/script.h +++ b/src/script/script.h @@ -17,7 +17,14 @@ #include <string> #include <vector> -static const unsigned int MAX_SCRIPT_ELEMENT_SIZE = 520; // bytes +// Maximum number of bytes pushable to the stack +static const unsigned int MAX_SCRIPT_ELEMENT_SIZE = 520; + +// Maximum number of non-push operations per script +static const int MAX_OPS_PER_SCRIPT = 201; + +// Maximum number of public keys per multisig +static const int MAX_PUBKEYS_PER_MULTISIG = 20; // Threshold for nLockTime: below this value it is interpreted as block number, // otherwise as UNIX timestamp. diff --git a/src/streams.h b/src/streams.h index fa1e18defe..8610e4d18e 100644 --- a/src/streams.h +++ b/src/streams.h @@ -296,6 +296,29 @@ public: data.insert(data.end(), begin(), end()); clear(); } + + /** + * XOR the contents of this stream with a certain key. + * + * @param[in] key The key used to XOR the data in this stream. + */ + void Xor(const std::vector<unsigned char>& key) + { + if (key.size() == 0) { + return; + } + + for (size_type i = 0, j = 0; i != size(); i++) { + vch[i] ^= key[j++]; + + // This potentially acts on very many bytes of data, so it's + // important that we calculate `j`, i.e. the `key` index in this + // way instead of doing a %, which would effectively be a division + // for each byte Xor'd -- much slower than need be. + if (j == key.size()) + j = 0; + } + } }; diff --git a/src/test/README.md b/src/test/README.md index e36112bd4f..b2d6be14f1 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/addrman_tests.cpp b/src/test/addrman_tests.cpp new file mode 100644 index 0000000000..cfcdd9abb2 --- /dev/null +++ b/src/test/addrman_tests.cpp @@ -0,0 +1,180 @@ +// Copyright (c) 2012-2013 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. +#include "addrman.h" +#include "test/test_bitcoin.h" +#include <string> +#include <boost/test/unit_test.hpp> + +#include "random.h" + +using namespace std; + +class CAddrManTest : public CAddrMan{}; + +BOOST_FIXTURE_TEST_SUITE(addrman_tests, BasicTestingSetup) + +BOOST_AUTO_TEST_CASE(addrman_simple) +{ + CAddrManTest addrman; + + // Set addrman addr placement to be deterministic. + addrman.MakeDeterministic(); + + CNetAddr source = CNetAddr("252.2.2.2:8333"); + + // Test 1: Does Addrman respond correctly when empty. + BOOST_CHECK(addrman.size() == 0); + CAddrInfo addr_null = addrman.Select(); + BOOST_CHECK(addr_null.ToString() == "[::]:0"); + + // Test 2: Does Addrman::Add work as expected. + CService addr1 = CService("250.1.1.1:8333"); + addrman.Add(CAddress(addr1), source); + BOOST_CHECK(addrman.size() == 1); + CAddrInfo addr_ret1 = addrman.Select(); + BOOST_CHECK(addr_ret1.ToString() == "250.1.1.1:8333"); + + // Test 3: Does IP address deduplication work correctly. + // Expected dup IP should not be added. + CService addr1_dup = CService("250.1.1.1:8333"); + addrman.Add(CAddress(addr1_dup), source); + BOOST_CHECK(addrman.size() == 1); + + + // Test 5: New table has one addr and we add a diff addr we should + // have two addrs. + CService addr2 = CService("250.1.1.2:8333"); + addrman.Add(CAddress(addr2), source); + BOOST_CHECK(addrman.size() == 2); + + // Test 6: AddrMan::Clear() should empty the new table. + addrman.Clear(); + BOOST_CHECK(addrman.size() == 0); + CAddrInfo addr_null2 = addrman.Select(); + BOOST_CHECK(addr_null2.ToString() == "[::]:0"); +} + +BOOST_AUTO_TEST_CASE(addrman_ports) +{ + CAddrManTest addrman; + + // Set addrman addr placement to be deterministic. + addrman.MakeDeterministic(); + + CNetAddr source = CNetAddr("252.2.2.2:8333"); + + BOOST_CHECK(addrman.size() == 0); + + // Test 7; Addr with same IP but diff port does not replace existing addr. + CService addr1 = CService("250.1.1.1:8333"); + addrman.Add(CAddress(addr1), source); + BOOST_CHECK(addrman.size() == 1); + + CService addr1_port = CService("250.1.1.1:8334"); + addrman.Add(CAddress(addr1_port), source); + BOOST_CHECK(addrman.size() == 1); + CAddrInfo addr_ret2 = addrman.Select(); + BOOST_CHECK(addr_ret2.ToString() == "250.1.1.1:8333"); + + // Test 8: Add same IP but diff port to tried table, it doesn't get added. + // Perhaps this is not ideal behavior but it is the current behavior. + addrman.Good(CAddress(addr1_port)); + BOOST_CHECK(addrman.size() == 1); + bool newOnly = true; + CAddrInfo addr_ret3 = addrman.Select(newOnly); + BOOST_CHECK(addr_ret3.ToString() == "250.1.1.1:8333"); +} + + +BOOST_AUTO_TEST_CASE(addrman_select) +{ + CAddrManTest addrman; + + // Set addrman addr placement to be deterministic. + addrman.MakeDeterministic(); + + CNetAddr source = CNetAddr("252.2.2.2:8333"); + + // Test 9: Select from new with 1 addr in new. + CService addr1 = CService("250.1.1.1:8333"); + addrman.Add(CAddress(addr1), source); + BOOST_CHECK(addrman.size() == 1); + + bool newOnly = true; + CAddrInfo addr_ret1 = addrman.Select(newOnly); + BOOST_CHECK(addr_ret1.ToString() == "250.1.1.1:8333"); + + + // Test 10: move addr to tried, select from new expected nothing returned. + addrman.Good(CAddress(addr1)); + BOOST_CHECK(addrman.size() == 1); + CAddrInfo addr_ret2 = addrman.Select(newOnly); + BOOST_CHECK(addr_ret2.ToString() == "[::]:0"); + + CAddrInfo addr_ret3 = addrman.Select(); + BOOST_CHECK(addr_ret3.ToString() == "250.1.1.1:8333"); +} + +BOOST_AUTO_TEST_CASE(addrman_new_collisions) +{ + CAddrManTest addrman; + + // Set addrman addr placement to be deterministic. + addrman.MakeDeterministic(); + + CNetAddr source = CNetAddr("252.2.2.2:8333"); + + BOOST_CHECK(addrman.size() == 0); + + for (unsigned int i = 1; i < 4; i++){ + CService addr = CService("250.1.1."+boost::to_string(i)); + addrman.Add(CAddress(addr), source); + + //Test 11: No collision in new table yet. + BOOST_CHECK(addrman.size() == i); + } + + //Test 12: new table collision! + CService addr1 = CService("250.1.1.4"); + addrman.Add(CAddress(addr1), source); + BOOST_CHECK(addrman.size() == 3); + + CService addr2 = CService("250.1.1.5"); + addrman.Add(CAddress(addr2), source); + BOOST_CHECK(addrman.size() == 4); +} + +BOOST_AUTO_TEST_CASE(addrman_tried_collisions) +{ + CAddrManTest addrman; + + // Set addrman addr placement to be deterministic. + addrman.MakeDeterministic(); + + CNetAddr source = CNetAddr("252.2.2.2:8333"); + + BOOST_CHECK(addrman.size() == 0); + + for (unsigned int i = 1; i < 75; i++){ + CService addr = CService("250.1.1."+boost::to_string(i)); + addrman.Add(CAddress(addr), source); + addrman.Good(CAddress(addr)); + + //Test 13: No collision in tried table yet. + BOOST_TEST_MESSAGE(addrman.size()); + BOOST_CHECK(addrman.size() == i); + } + + //Test 14: tried table collision! + CService addr1 = CService("250.1.1.76"); + addrman.Add(CAddress(addr1), source); + BOOST_CHECK(addrman.size() == 74); + + CService addr2 = CService("250.1.1.77"); + addrman.Add(CAddress(addr2), source); + BOOST_CHECK(addrman.size() == 75); +} + + +BOOST_AUTO_TEST_SUITE_END()
\ No newline at end of file diff --git a/src/test/alert_tests.cpp b/src/test/alert_tests.cpp index dd3c51d09b..468eda1c9b 100644 --- a/src/test/alert_tests.cpp +++ b/src/test/alert_tests.cpp @@ -217,10 +217,12 @@ BOOST_AUTO_TEST_CASE(PartitionAlert) // use them } + strMiscWarning = ""; + // Test 1: chain with blocks every nPowTargetSpacing seconds, // as normal, no worries: PartitionCheck(falseFunc, csDummy, &indexDummy[99], nPowTargetSpacing); - BOOST_CHECK(strMiscWarning.empty()); + BOOST_CHECK_MESSAGE(strMiscWarning.empty(), strMiscWarning); // Test 2: go 3.5 hours without a block, expect a warning: now += 3*60*60+30*60; diff --git a/src/test/data/tx_invalid.json b/src/test/data/tx_invalid.json index 5cad5af7c3..cc059e814f 100644 --- a/src/test/data/tx_invalid.json +++ b/src/test/data/tx_invalid.json @@ -64,9 +64,13 @@ [[["0000000000000000000000000000000000000000000000000000000000000000", -1, "1"]], "01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff655151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151ffffffff010000000000000000015100000000", "P2SH"], -["Null txin"], -[[["0000000000000000000000000000000000000000000000000000000000000000", -1, "HASH160 0x14 0x02dae7dbbda56097959cba59b1989dd3e47937bf EQUAL"]], -"01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff6e49304602210086f39e028e46dafa8e1e3be63906465f4cf038fbe5ed6403dc3e74ae876e6431022100c4625c675cfc5c7e3a0e0d7eaec92ac24da20c73a88eb40d09253e51ac6def5201232103a183ddc41e84753aca47723c965d1b5c8b0e2b537963518355e6dd6cf8415e50acffffffff010000000000000000015100000000", "P2SH"], +["Null txin, but without being a coinbase (because there are two inputs)"], +[[["0000000000000000000000000000000000000000000000000000000000000000", -1, "1"], + ["0000000000000000000000000000000000000000000000000000000000000100", 0, "1"]], +"01000000020000000000000000000000000000000000000000000000000000000000000000ffffffff00ffffffff00010000000000000000000000000000000000000000000000000000000000000000000000ffffffff010000000000000000015100000000", "P2SH"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "1"], + ["0000000000000000000000000000000000000000000000000000000000000000", -1, "1"]], +"010000000200010000000000000000000000000000000000000000000000000000000000000000000000ffffffff0000000000000000000000000000000000000000000000000000000000000000ffffffff00ffffffff010000000000000000015100000000", "P2SH"], ["Same as the transactions in valid with one input SIGHASH_ALL and one SIGHASH_ANYONECANPAY, but we set the _ANYONECANPAY sequence number, invalidating the SIGHASH_ALL signature"], [[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x21 0x035e7f0d4d0841bcd56c39337ed086b1a633ee770c1ffdd94ac552a95ac2ce0efc CHECKSIG"], @@ -193,5 +197,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 9744a3c848..0dfef73ae5 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/dbwrapper_tests.cpp b/src/test/dbwrapper_tests.cpp new file mode 100644 index 0000000000..8b6b0697ab --- /dev/null +++ b/src/test/dbwrapper_tests.cpp @@ -0,0 +1,207 @@ +// Copyright (c) 2012-2013 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "dbwrapper.h" +#include "uint256.h" +#include "random.h" +#include "test/test_bitcoin.h" + +#include <boost/assign/std/vector.hpp> // for 'operator+=()' +#include <boost/assert.hpp> +#include <boost/test/unit_test.hpp> + +using namespace std; +using namespace boost::assign; // bring 'operator+=()' into scope +using namespace boost::filesystem; + +// Test if a string consists entirely of null characters +bool is_null_key(const vector<unsigned char>& key) { + bool isnull = true; + + for (unsigned int i = 0; i < key.size(); i++) + isnull &= (key[i] == '\x00'); + + return isnull; +} + +BOOST_FIXTURE_TEST_SUITE(dbwrapper_tests, BasicTestingSetup) + +BOOST_AUTO_TEST_CASE(dbwrapper) +{ + // 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(); + CDBWrapper dbw(ph, (1 << 20), true, false, obfuscate); + char key = 'k'; + uint256 in = GetRandHash(); + uint256 res; + + // Ensure that we're doing real obfuscation when obfuscate=true + BOOST_CHECK(obfuscate != is_null_key(dbw.GetObfuscateKey())); + + BOOST_CHECK(dbw.Write(key, in)); + BOOST_CHECK(dbw.Read(key, res)); + BOOST_CHECK_EQUAL(res.ToString(), in.ToString()); + } +} + +// Test batch operations +BOOST_AUTO_TEST_CASE(dbwrapper_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(); + CDBWrapper 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; + CDBBatch 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(dbwrapper_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(); + CDBWrapper 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<CDBIterator> it(const_cast<CDBWrapper*>(&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) +{ + // We're going to share this path between two wrappers + path ph = temp_directory_path() / unique_path(); + create_directories(ph); + + // Set up a non-obfuscated wrapper to write some initial data. + CDBWrapper* dbw = new CDBWrapper(ph, (1 << 10), false, false, false); + char key = 'k'; + uint256 in = GetRandHash(); + uint256 res; + + BOOST_CHECK(dbw->Write(key, in)); + BOOST_CHECK(dbw->Read(key, res)); + BOOST_CHECK_EQUAL(res.ToString(), in.ToString()); + + // Call the destructor to free leveldb LOCK + delete dbw; + + // Now, set up another wrapper that wants to obfuscate the same directory + CDBWrapper odbw(ph, (1 << 10), false, false, true); + + // Check that the key/val we wrote with unobfuscated wrapper exists and + // is readable. + uint256 res2; + BOOST_CHECK(odbw.Read(key, res2)); + BOOST_CHECK_EQUAL(res2.ToString(), in.ToString()); + + BOOST_CHECK(!odbw.IsEmpty()); // There should be existing data + BOOST_CHECK(is_null_key(odbw.GetObfuscateKey())); // The key should be an empty string + + uint256 in2 = GetRandHash(); + uint256 res3; + + // Check that we can write successfully + BOOST_CHECK(odbw.Write(key, in2)); + BOOST_CHECK(odbw.Read(key, res3)); + BOOST_CHECK_EQUAL(res3.ToString(), in2.ToString()); +} + +// Ensure that we start obfuscating during a reindex. +BOOST_AUTO_TEST_CASE(existing_data_reindex) +{ + // We're going to share this path between two wrappers + path ph = temp_directory_path() / unique_path(); + create_directories(ph); + + // Set up a non-obfuscated wrapper to write some initial data. + CDBWrapper* dbw = new CDBWrapper(ph, (1 << 10), false, false, false); + char key = 'k'; + uint256 in = GetRandHash(); + uint256 res; + + BOOST_CHECK(dbw->Write(key, in)); + BOOST_CHECK(dbw->Read(key, res)); + BOOST_CHECK_EQUAL(res.ToString(), in.ToString()); + + // Call the destructor to free leveldb LOCK + delete dbw; + + // Simulate a -reindex by wiping the existing data store + CDBWrapper odbw(ph, (1 << 10), false, true, true); + + // Check that the key/val we wrote with unobfuscated wrapper doesn't exist + uint256 res2; + BOOST_CHECK(!odbw.Read(key, res2)); + BOOST_CHECK(!is_null_key(odbw.GetObfuscateKey())); + + uint256 in2 = GetRandHash(); + uint256 res3; + + // Check that we can write successfully + BOOST_CHECK(odbw.Write(key, in2)); + BOOST_CHECK(odbw.Read(key, res3)); + BOOST_CHECK_EQUAL(res3.ToString(), in2.ToString()); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/mempool_tests.cpp b/src/test/mempool_tests.cpp index 5bf1e98e8f..0cf906a259 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/streams_tests.cpp b/src/test/streams_tests.cpp new file mode 100644 index 0000000000..0ed8f363d7 --- /dev/null +++ b/src/test/streams_tests.cpp @@ -0,0 +1,67 @@ +// Copyright (c) 2012-2013 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "streams.h" +#include "support/allocators/zeroafterfree.h" +#include "test/test_bitcoin.h" + +#include <boost/assign/std/vector.hpp> // for 'operator+=()' +#include <boost/assert.hpp> +#include <boost/test/unit_test.hpp> + +using namespace std; +using namespace boost::assign; // bring 'operator+=()' into scope + +BOOST_FIXTURE_TEST_SUITE(streams_tests, BasicTestingSetup) + +BOOST_AUTO_TEST_CASE(streams_serializedata_xor) +{ + std::vector<char> in; + std::vector<char> expected_xor; + std::vector<unsigned char> key; + CDataStream ds(in, 0, 0); + + // Degenerate case + + key += '\x00','\x00'; + ds.Xor(key); + BOOST_CHECK_EQUAL( + std::string(expected_xor.begin(), expected_xor.end()), + std::string(ds.begin(), ds.end())); + + in += '\x0f','\xf0'; + expected_xor += '\xf0','\x0f'; + + // Single character key + + ds.clear(); + ds.insert(ds.begin(), in.begin(), in.end()); + key.clear(); + + key += '\xff'; + ds.Xor(key); + BOOST_CHECK_EQUAL( + std::string(expected_xor.begin(), expected_xor.end()), + std::string(ds.begin(), ds.end())); + + // Multi character key + + in.clear(); + expected_xor.clear(); + in += '\xf0','\x0f'; + expected_xor += '\x0f','\x00'; + + ds.clear(); + ds.insert(ds.begin(), in.begin(), in.end()); + + key.clear(); + key += '\xff','\x0f'; + + ds.Xor(key); + BOOST_CHECK_EQUAL( + std::string(expected_xor.begin(), expected_xor.end()), + std::string(ds.begin(), ds.end())); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/test_bitcoin.cpp b/src/test/test_bitcoin.cpp index 8d81275a6f..23e5e66d84 100644 --- a/src/test/test_bitcoin.cpp +++ b/src/test/test_bitcoin.cpp @@ -32,13 +32,14 @@ CWallet* pwalletMain; extern bool fPrintToConsole; extern void noui_connect(); -BasicTestingSetup::BasicTestingSetup(CBaseChainParams::Network network) +BasicTestingSetup::BasicTestingSetup(const std::string& chainName) { ECC_Start(); SetupEnvironment(); + SetupNetworking(); fPrintToDebugLog = false; // don't want to write to debug.log file fCheckBlockIndex = true; - SelectParams(network); + SelectParams(chainName); noui_connect(); } @@ -47,7 +48,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 b9314d0611..0bab4b6831 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 9847f6512e..f9423bc0de 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 edad18644e..9b8e1c088b 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 21ecd65238..cd76c0155c 100644 --- a/src/txdb.cpp +++ b/src/txdb.cpp @@ -29,18 +29,8 @@ static const char DB_REINDEX_FLAG = 'R'; static const char DB_LAST_BLOCK = 'l'; -void static BatchWriteCoins(CLevelDBBatch &batch, const uint256 &hash, const CCoins &coins) { - if (coins.IsPruned()) - batch.Erase(make_pair(DB_COINS, hash)); - else - batch.Write(make_pair(DB_COINS, hash), coins); -} - -void static BatchWriteHashBestChain(CLevelDBBatch &batch, const uint256 &hash) { - batch.Write(DB_BEST_BLOCK, hash); -} - -CCoinsViewDB::CCoinsViewDB(size_t nCacheSize, bool fMemory, bool fWipe) : db(GetDataDir() / "chainstate", nCacheSize, fMemory, fWipe) { +CCoinsViewDB::CCoinsViewDB(size_t nCacheSize, bool fMemory, bool fWipe) : db(GetDataDir() / "chainstate", nCacheSize, fMemory, fWipe, true) +{ } bool CCoinsViewDB::GetCoins(const uint256 &txid, CCoins &coins) const { @@ -59,12 +49,15 @@ uint256 CCoinsViewDB::GetBestBlock() const { } bool CCoinsViewDB::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock) { - CLevelDBBatch batch; + CDBBatch batch(&db.GetObfuscateKey()); size_t count = 0; size_t changed = 0; for (CCoinsMap::iterator it = mapCoins.begin(); it != mapCoins.end();) { if (it->second.flags & CCoinsCacheEntry::DIRTY) { - BatchWriteCoins(batch, it->first, it->second.coins); + if (it->second.coins.IsPruned()) + batch.Erase(make_pair(DB_COINS, it->first)); + else + batch.Write(make_pair(DB_COINS, it->first), it->second.coins); changed++; } count++; @@ -72,13 +65,13 @@ bool CCoinsViewDB::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock) { mapCoins.erase(itOld); } if (!hashBlock.IsNull()) - BatchWriteHashBestChain(batch, hashBlock); + batch.Write(DB_BEST_BLOCK, hashBlock); LogPrint("coindb", "Committing %u changed transactions (out of %u) to coin database...\n", (unsigned int)changed, (unsigned int)count); return db.WriteBatch(batch); } -CBlockTreeDB::CBlockTreeDB(size_t nCacheSize, bool fMemory, bool fWipe) : CLevelDBWrapper(GetDataDir() / "blocks" / "index", nCacheSize, fMemory, fWipe) { +CBlockTreeDB::CBlockTreeDB(size_t nCacheSize, bool fMemory, bool fWipe) : CDBWrapper(GetDataDir() / "blocks" / "index", nCacheSize, fMemory, fWipe) { } bool CBlockTreeDB::ReadBlockFileInfo(int nFile, CBlockFileInfo &info) { @@ -105,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<CDBIterator> pcursor(const_cast<CDBWrapper*>(&db)->NewIterator()); + pcursor->Seek(DB_COINS); CHashWriter ss(SER_GETHASH, PROTOCOL_VERSION); stats.hashBlock = GetBestBlock(); @@ -114,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]; @@ -140,13 +121,15 @@ bool CCoinsViewDB::GetStats(CCoinsStats &stats) const { nTotalAmount += out.nValue; } } - stats.nSerializedSize += 32 + slValue.size(); + stats.nSerializedSize += 32 + pcursor->GetValueSize(); 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); @@ -158,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; + CDBBatch 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); } @@ -174,7 +157,7 @@ bool CBlockTreeDB::ReadTxIndex(const uint256 &txid, CDiskTxPos &pos) { } bool CBlockTreeDB::WriteTxIndex(const std::vector<std::pair<uint256, CDiskTxPos> >&vect) { - CLevelDBBatch batch; + CDBBatch 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); @@ -194,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<CDBIterator> 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); @@ -234,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/txdb.h b/src/txdb.h index bef5dc9fd1..586ab55d0d 100644 --- a/src/txdb.h +++ b/src/txdb.h @@ -7,7 +7,7 @@ #define BITCOIN_TXDB_H #include "coins.h" -#include "leveldbwrapper.h" +#include "dbwrapper.h" #include <map> #include <string> @@ -26,11 +26,11 @@ static const int64_t nMaxDbCache = sizeof(void*) > 4 ? 16384 : 1024; //! min. -dbcache in (MiB) static const int64_t nMinDbCache = 4; -/** CCoinsView backed by the LevelDB coin database (chainstate/) */ +/** CCoinsView backed by the coin database (chainstate/) */ class CCoinsViewDB : public CCoinsView { protected: - CLevelDBWrapper db; + CDBWrapper db; public: CCoinsViewDB(size_t nCacheSize, bool fMemory = false, bool fWipe = false); @@ -42,7 +42,7 @@ public: }; /** Access to the block database (blocks/index/) */ -class CBlockTreeDB : public CLevelDBWrapper +class CBlockTreeDB : public CDBWrapper { public: CBlockTreeDB(size_t nCacheSize, bool fMemory = false, bool fWipe = false); diff --git a/src/txmempool.cpp b/src/txmempool.cpp index 1370cab0c0..a772e7adea 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(); //lock free 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; + nCheckFrequency = 0; - minerPolicyEstimator = new CBlockPolicyEstimator(_minRelayFee); + minerPolicyEstimator = new CBlockPolicyEstimator(_minReasonableRelayFee); + minReasonableRelayFee = _minReasonableRelayFee; } CTxMemPool::~CTxMemPool() @@ -483,7 +487,7 @@ void CTxMemPool::removeCoinbaseSpends(const CCoinsViewCache *pcoins, unsigned in if (it2 != mapTx.end()) continue; const CCoins *coins = pcoins->AccessCoins(txin.prevout.hash); - if (fSanityCheck) assert(coins); + if (nCheckFrequency != 0) assert(coins); if (!coins || (coins->IsCoinBase() && ((signed long)nMemPoolHeight) - coins->nHeight < COINBASE_MATURITY)) { transactionsToRemove.push_back(tx); break; @@ -508,6 +512,7 @@ void CTxMemPool::removeConflicts(const CTransaction &tx, std::list<CTransaction> if (txConflict != tx) { remove(txConflict, removed, true); + ClearPrioritisation(txConflict.GetHash()); } } } @@ -538,22 +543,35 @@ 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() +void CTxMemPool::_clear() { - LOCK(cs); mapLinks.clear(); mapTx.clear(); mapNextTx.clear(); totalTxSize = 0; cachedInnerUsage = 0; + lastRollingFeeUpdate = GetTime(); + blockSinceLastRollingFeeBump = false; + rollingMinimumFeeRate = 0; ++nTransactionsUpdated; } +void CTxMemPool::clear() +{ + LOCK(cs); + _clear(); +} + void CTxMemPool::check(const CCoinsViewCache *pcoins) const { - if (!fSanityCheck) + if (nCheckFrequency == 0) + return; + + if (insecure_rand() >= nCheckFrequency) return; LogPrint("mempool", "Checking mempool with %u transactions and %u inputs\n", (unsigned int)mapTx.size(), (unsigned int)mapNextTx.size()); @@ -735,10 +753,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 +810,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 +871,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 c0eef0dd22..7b5843a8d0 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 @@ -277,14 +278,25 @@ public: class CTxMemPool { private: - bool fSanityCheck; //! Normally false, true if -checkmempool or -regtest + uint32_t nCheckFrequency; //! Value n means that n times in 2^32 we check. unsigned int nTransactionsUpdated; CBlockPolicyEstimator* minerPolicyEstimator; 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(); /** @@ -338,7 +360,7 @@ public: * check does nothing. */ void check(const CCoinsViewCache *pcoins) const; - void setSanityCheck(bool _fSanityCheck) { fSanityCheck = _fSanityCheck; } + void setSanityCheck(double dFrequency = 1.0) { nCheckFrequency = dFrequency * 4294967295.0; } // addUnchecked must updated state for all ancestors of a given transaction, // to track size/count of descendant transactions. First version of @@ -353,6 +375,7 @@ public: void removeForBlock(const std::vector<CTransaction>& vtx, unsigned int nBlockHeight, std::list<CTransaction>& conflicts, bool fCurrentEstimate = true); void clear(); + void _clear(); //lock free void queryHashes(std::vector<uint256>& vtxid); void pruneSpent(const uint256& hash, CCoins &coins); unsigned int GetTransactionsUpdated() const; @@ -365,7 +388,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 +420,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 ca9e842348..a7a2ca9197 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 2800f466dc..df9e66229c 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 e69de29bb2..0000000000 --- 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 0000000000..f063686524 --- /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 ca8c16dcd4..ee7fc2851c 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 e4dea0df72..4afa094b10 100644 --- a/src/univalue/test/.gitignore +++ b/src/univalue/test/.gitignore @@ -1,7 +1 @@ - unitester - -*.log -*.trs - -.libs diff --git a/src/util.cpp b/src/util.cpp index f50d25e17a..e8514a2ef0 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -108,6 +108,7 @@ bool fDaemon = false; bool fServer = false; string strMiscWarning; bool fLogTimestamps = false; +bool fLogTimeMicros = DEFAULT_LOGTIMEMICROS; bool fLogIPs = false; volatile bool fReopenDebugLog = false; CTranslationInterface translationInterface; @@ -263,9 +264,13 @@ static std::string LogTimestampStr(const std::string &str, bool *fStartedNewLine if (!fLogTimestamps) return str; - if (*fStartedNewLine) - strStamped = DateTimeStrFormat("%Y-%m-%d %H:%M:%S", GetTime()) + ' ' + str; - else + if (*fStartedNewLine) { + int64_t nTimeMicros = GetLogTimeMicros(); + strStamped = DateTimeStrFormat("%Y-%m-%d %H:%M:%S", nTimeMicros/1000000); + if (fLogTimeMicros) + strStamped += strprintf(".%06d", nTimeMicros%1000000); + strStamped += ' ' + str; + } else strStamped = str; if (!str.empty() && str[str.size()-1] == '\n') @@ -280,10 +285,13 @@ int LogPrintStr(const std::string &str) { int ret = 0; // Returns total number of characters written static bool fStartedNewLine = true; + + string strTimestamped = LogTimestampStr(str, &fStartedNewLine); + if (fPrintToConsole) { // print to console - ret = fwrite(str.data(), 1, str.size(), stdout); + ret = fwrite(strTimestamped.data(), 1, strTimestamped.size(), stdout); fflush(stdout); } else if (fPrintToDebugLog) @@ -291,8 +299,6 @@ int LogPrintStr(const std::string &str) boost::call_once(&DebugPrintInit, debugPrintInitFlag); boost::mutex::scoped_lock scoped_lock(*mutexDebugLog); - string strTimestamped = LogTimestampStr(str, &fStartedNewLine); - // buffer if we haven't opened the log yet if (fileout == NULL) { assert(vMsgsBeforeOpenLog); diff --git a/src/util.h b/src/util.h index 0b2dc01ac6..b2779fe782 100644 --- a/src/util.h +++ b/src/util.h @@ -28,6 +28,8 @@ #include <boost/signals2/signal.hpp> #include <boost/thread/exceptions.hpp> +static const bool DEFAULT_LOGTIMEMICROS = false; + /** Signals for translation. */ class CTranslationInterface { @@ -44,6 +46,7 @@ extern bool fPrintToDebugLog; extern bool fServer; extern std::string strMiscWarning; extern bool fLogTimestamps; +extern bool fLogTimeMicros; extern bool fLogIPs; extern volatile bool fReopenDebugLog; extern CTranslationInterface translationInterface; diff --git a/src/utiltime.cpp b/src/utiltime.cpp index d316288999..3202c47f1d 100644 --- a/src/utiltime.cpp +++ b/src/utiltime.cpp @@ -40,6 +40,14 @@ int64_t GetTimeMicros() boost::posix_time::ptime(boost::gregorian::date(1970,1,1))).total_microseconds(); } +/** Return a time useful for the debug log */ +int64_t GetLogTimeMicros() +{ + if (nMockTime) return nMockTime*1000000; + + return GetTimeMicros(); +} + void MilliSleep(int64_t n) { diff --git a/src/utiltime.h b/src/utiltime.h index 900992f871..241b5211e9 100644 --- a/src/utiltime.h +++ b/src/utiltime.h @@ -12,6 +12,7 @@ int64_t GetTime(); int64_t GetTimeMillis(); int64_t GetTimeMicros(); +int64_t GetLogTimeMicros(); void SetMockTime(int64_t nMockTimeIn); void MilliSleep(int64_t n); diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index bd3004061b..3f2d5a05f6 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); } diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp index 0624e442d1..ea8a4eb043 100644 --- a/src/wallet/walletdb.cpp +++ b/src/wallet/walletdb.cpp @@ -512,8 +512,13 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, } else if (strType == "ckey") { - vector<unsigned char> vchPubKey; + CPubKey vchPubKey; ssKey >> vchPubKey; + if (!vchPubKey.IsValid()) + { + strErr = "Error reading wallet database: CPubKey corrupt"; + return false; + } vector<unsigned char> vchPrivKey; ssValue >> vchPrivKey; wss.nCKeys++; |