diff options
Diffstat (limited to 'src')
60 files changed, 850 insertions, 490 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index 5a5e3abcfa..69ae02f582 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -111,6 +111,7 @@ BITCOIN_CORE_H = \ net.h \ netaddress.h \ netbase.h \ + netmessagemaker.h \ noui.h \ policy/fees.h \ policy/policy.h \ diff --git a/src/Makefile.bench.include b/src/Makefile.bench.include index 246797a1b2..e58bd9dfbf 100644 --- a/src/Makefile.bench.include +++ b/src/Makefile.bench.include @@ -22,7 +22,9 @@ bench_bench_bitcoin_SOURCES = \ bench/mempool_eviction.cpp \ bench/verify_script.cpp \ bench/base58.cpp \ - bench/lockedpool.cpp + bench/lockedpool.cpp \ + bench/perf.cpp \ + bench/perf.h nodist_bench_bench_bitcoin_SOURCES = $(GENERATED_TEST_FILES) diff --git a/src/addrdb.cpp b/src/addrdb.cpp index ddf41f92de..23715bbea4 100644 --- a/src/addrdb.cpp +++ b/src/addrdb.cpp @@ -103,7 +103,7 @@ bool CBanDB::Read(banmap_t& banSet) if (memcmp(pchMsgTmp, Params().MessageStart(), sizeof(pchMsgTmp))) return error("%s: Invalid network magic number", __func__); - // de-serialize address data into one CAddrMan object + // de-serialize ban data ssBanlist >> banSet; } catch (const std::exception& e) { diff --git a/src/addrman.h b/src/addrman.h index cabacbbea9..76e7b210f7 100644 --- a/src/addrman.h +++ b/src/addrman.h @@ -482,6 +482,7 @@ public: //! Return the number of (unique) addresses in all tables. size_t size() const { + LOCK(cs); // TODO: Cache this in an atomic to avoid this overhead return vRandom.size(); } @@ -501,13 +502,11 @@ public: //! Add a single address. bool Add(const CAddress &addr, const CNetAddr& source, int64_t nTimePenalty = 0) { + LOCK(cs); bool fRet = false; - { - LOCK(cs); - Check(); - fRet |= Add_(addr, source, nTimePenalty); - Check(); - } + Check(); + fRet |= Add_(addr, source, nTimePenalty); + Check(); if (fRet) LogPrint("addrman", "Added %s from %s: %i tried, %i new\n", addr.ToStringIPPort(), source.ToString(), nTried, nNew); return fRet; @@ -516,14 +515,12 @@ public: //! Add multiple addresses. bool Add(const std::vector<CAddress> &vAddr, const CNetAddr& source, int64_t nTimePenalty = 0) { + LOCK(cs); int nAdd = 0; - { - LOCK(cs); - Check(); - for (std::vector<CAddress>::const_iterator it = vAddr.begin(); it != vAddr.end(); it++) - nAdd += Add_(*it, source, nTimePenalty) ? 1 : 0; - Check(); - } + Check(); + for (std::vector<CAddress>::const_iterator it = vAddr.begin(); it != vAddr.end(); it++) + nAdd += Add_(*it, source, nTimePenalty) ? 1 : 0; + Check(); if (nAdd) LogPrint("addrman", "Added %i addresses from %s: %i tried, %i new\n", nAdd, source.ToString(), nTried, nNew); return nAdd > 0; @@ -532,23 +529,19 @@ public: //! Mark an entry as accessible. void Good(const CService &addr, int64_t nTime = GetAdjustedTime()) { - { - LOCK(cs); - Check(); - Good_(addr, nTime); - Check(); - } + LOCK(cs); + Check(); + Good_(addr, nTime); + Check(); } //! Mark an entry as connection attempted to. void Attempt(const CService &addr, bool fCountFailure, int64_t nTime = GetAdjustedTime()) { - { - LOCK(cs); - Check(); - Attempt_(addr, fCountFailure, nTime); - Check(); - } + LOCK(cs); + Check(); + Attempt_(addr, fCountFailure, nTime); + Check(); } /** @@ -582,12 +575,10 @@ public: //! Mark an entry as currently-connected-to. void Connected(const CService &addr, int64_t nTime = GetAdjustedTime()) { - { - LOCK(cs); - Check(); - Connected_(addr, nTime); - Check(); - } + LOCK(cs); + Check(); + Connected_(addr, nTime); + Check(); } void SetServices(const CService &addr, ServiceFlags nServices) diff --git a/src/bench/bench.cpp b/src/bench/bench.cpp index 8942da8c74..af3d152c9a 100644 --- a/src/bench/bench.cpp +++ b/src/bench/bench.cpp @@ -3,6 +3,7 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include "bench.h" +#include "perf.h" #include <iostream> #include <iomanip> @@ -26,7 +27,9 @@ BenchRunner::BenchRunner(std::string name, BenchFunction func) void BenchRunner::RunAll(double elapsedTimeForOne) { - std::cout << "#Benchmark" << "," << "count" << "," << "min" << "," << "max" << "," << "average" << "\n"; + perf_init(); + std::cout << "#Benchmark" << "," << "count" << "," << "min" << "," << "max" << "," << "average" << "," + << "min_cycles" << "," << "max_cycles" << "," << "average_cycles" << "\n"; for (std::map<std::string,BenchFunction>::iterator it = benchmarks.begin(); it != benchmarks.end(); ++it) { @@ -35,6 +38,7 @@ BenchRunner::RunAll(double elapsedTimeForOne) BenchFunction& func = it->second; func(state); } + perf_fini(); } bool State::KeepRunning() @@ -44,8 +48,10 @@ bool State::KeepRunning() return true; } double now; + uint64_t nowCycles; if (count == 0) { lastTime = beginTime = now = gettimedouble(); + lastCycles = beginCycles = nowCycles = perf_cpucycles(); } else { now = gettimedouble(); @@ -53,6 +59,13 @@ bool State::KeepRunning() double elapsedOne = elapsed * countMaskInv; if (elapsedOne < minTime) minTime = elapsedOne; if (elapsedOne > maxTime) maxTime = elapsedOne; + + // We only use relative values, so don't have to handle 64-bit wrap-around specially + nowCycles = perf_cpucycles(); + uint64_t elapsedOneCycles = (nowCycles - lastCycles) * countMaskInv; + if (elapsedOneCycles < minCycles) minCycles = elapsedOneCycles; + if (elapsedOneCycles > maxCycles) maxCycles = elapsedOneCycles; + if (elapsed*128 < maxElapsed) { // If the execution was much too fast (1/128th of maxElapsed), increase the count mask by 8x and restart timing. // The restart avoids including the overhead of this code in the measurement. @@ -61,6 +74,8 @@ bool State::KeepRunning() count = 0; minTime = std::numeric_limits<double>::max(); maxTime = std::numeric_limits<double>::min(); + minCycles = std::numeric_limits<uint64_t>::max(); + maxCycles = std::numeric_limits<uint64_t>::min(); return true; } if (elapsed*16 < maxElapsed) { @@ -72,6 +87,7 @@ bool State::KeepRunning() } } lastTime = now; + lastCycles = nowCycles; ++count; if (now - beginTime < maxElapsed) return true; // Keep going @@ -80,7 +96,9 @@ bool State::KeepRunning() // Output results double average = (now-beginTime)/count; - std::cout << std::fixed << std::setprecision(15) << name << "," << count << "," << minTime << "," << maxTime << "," << average << "\n"; + int64_t averageCycles = (nowCycles-beginCycles)/count; + std::cout << std::fixed << std::setprecision(15) << name << "," << count << "," << minTime << "," << maxTime << "," << average << "," + << minCycles << "," << maxCycles << "," << averageCycles << "\n"; return false; } diff --git a/src/bench/bench.h b/src/bench/bench.h index f13b145aaf..caf73e949b 100644 --- a/src/bench/bench.h +++ b/src/bench/bench.h @@ -41,12 +41,18 @@ namespace benchmark { double maxElapsed; double beginTime; double lastTime, minTime, maxTime, countMaskInv; - int64_t count; - int64_t countMask; + uint64_t count; + uint64_t countMask; + uint64_t beginCycles; + uint64_t lastCycles; + uint64_t minCycles; + uint64_t maxCycles; 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(); + minCycles = std::numeric_limits<uint64_t>::max(); + maxCycles = std::numeric_limits<uint64_t>::min(); countMask = 1; countMaskInv = 1./(countMask + 1); } diff --git a/src/bench/perf.cpp b/src/bench/perf.cpp new file mode 100644 index 0000000000..1f43e5d3ac --- /dev/null +++ b/src/bench/perf.cpp @@ -0,0 +1,53 @@ +// Copyright (c) 2016 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 "perf.h" + +#if defined(__i386__) || defined(__x86_64__) + +/* These architectures support quering the cycle counter + * from user space, no need for any syscall overhead. + */ +void perf_init(void) { } +void perf_fini(void) { } + +#elif defined(__linux__) + +#include <unistd.h> +#include <sys/syscall.h> +#include <linux/perf_event.h> + +static int fd = -1; +static struct perf_event_attr attr; + +void perf_init(void) +{ + attr.type = PERF_TYPE_HARDWARE; + attr.config = PERF_COUNT_HW_CPU_CYCLES; + fd = syscall(__NR_perf_event_open, &attr, 0, -1, -1, 0); +} + +void perf_fini(void) +{ + if (fd != -1) { + close(fd); + } +} + +uint64_t perf_cpucycles(void) +{ + uint64_t result = 0; + if (fd == -1 || read(fd, &result, sizeof(result)) < (ssize_t)sizeof(result)) { + return 0; + } + return result; +} + +#else /* Unhandled platform */ + +void perf_init(void) { } +void perf_fini(void) { } +uint64_t perf_cpucycles(void) { return 0; } + +#endif diff --git a/src/bench/perf.h b/src/bench/perf.h new file mode 100644 index 0000000000..681bd0c8a2 --- /dev/null +++ b/src/bench/perf.h @@ -0,0 +1,37 @@ +// Copyright (c) 2016 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +/** Functions for measurement of CPU cycles */ +#ifndef H_PERF +#define H_PERF + +#include <stdint.h> + +#if defined(__i386__) + +static inline uint64_t perf_cpucycles(void) +{ + uint64_t x; + __asm__ volatile (".byte 0x0f, 0x31" : "=A" (x)); + return x; +} + +#elif defined(__x86_64__) + +static inline uint64_t perf_cpucycles(void) +{ + uint32_t hi, lo; + __asm__ __volatile__ ("rdtsc" : "=a"(lo), "=d"(hi)); + return ((uint64_t)lo)|(((uint64_t)hi)<<32); +} +#else + +uint64_t perf_cpucycles(void); + +#endif + +void perf_init(void); +void perf_fini(void); + +#endif // H_PERF diff --git a/src/bitcoin-cli.cpp b/src/bitcoin-cli.cpp index 392d1b9329..b272640819 100644 --- a/src/bitcoin-cli.cpp +++ b/src/bitcoin-cli.cpp @@ -24,15 +24,13 @@ #include <univalue.h> -using namespace std; - static const char DEFAULT_RPCCONNECT[] = "127.0.0.1"; static const int DEFAULT_HTTP_CLIENT_TIMEOUT=900; static const int CONTINUE_EXECUTION=-1; std::string HelpMessageCli() { - string strUsage; + std::string strUsage; strUsage += HelpMessageGroup(_("Options:")); strUsage += HelpMessageOpt("-?", _("This help message")); strUsage += HelpMessageOpt("-conf=<file>", strprintf(_("Specify configuration file (default: %s)"), BITCOIN_CONF_FILENAME)); @@ -187,7 +185,7 @@ static void http_error_cb(enum evhttp_request_error err, void *ctx) } #endif -UniValue CallRPC(const string& strMethod, const UniValue& params) +UniValue CallRPC(const std::string& strMethod, const UniValue& params) { std::string host = GetArg("-rpcconnect", DEFAULT_RPCCONNECT); int port = GetArg("-rpcport", BaseParams().RPCPort()); @@ -195,18 +193,18 @@ UniValue CallRPC(const string& strMethod, const UniValue& params) // Create event base struct event_base *base = event_base_new(); // TODO RAII if (!base) - throw runtime_error("cannot create event_base"); + throw std::runtime_error("cannot create event_base"); // Synchronously look up hostname struct evhttp_connection *evcon = evhttp_connection_base_new(base, NULL, host.c_str(), port); // TODO RAII if (evcon == NULL) - throw runtime_error("create connection failed"); + throw std::runtime_error("create connection failed"); evhttp_connection_set_timeout(evcon, GetArg("-rpcclienttimeout", DEFAULT_HTTP_CLIENT_TIMEOUT)); HTTPReply response; struct evhttp_request *req = evhttp_request_new(http_request_done, (void*)&response); // TODO RAII if (req == NULL) - throw runtime_error("create http request failed"); + throw std::runtime_error("create http request failed"); #if LIBEVENT_VERSION_NUMBER >= 0x02010300 evhttp_request_set_error_cb(req, http_error_cb); #endif @@ -216,7 +214,7 @@ UniValue CallRPC(const string& strMethod, const UniValue& params) if (mapArgs["-rpcpassword"] == "") { // Try fall back to cookie-based authentication if no password is provided if (!GetAuthCookie(&strRPCUserColonPass)) { - throw runtime_error(strprintf( + throw std::runtime_error(strprintf( _("Could not locate RPC credentials. No authentication cookie could be found, and no rpcpassword is set in the configuration file (%s)"), GetConfigFile(GetArg("-conf", BITCOIN_CONF_FILENAME)).string().c_str())); @@ -251,26 +249,26 @@ UniValue CallRPC(const string& strMethod, const UniValue& params) if (response.status == 0) throw CConnectionFailed(strprintf("couldn't connect to server\n(make sure server is running and you are connecting to the correct RPC port: %d %s)", response.error, http_errorstring(response.error))); else if (response.status == HTTP_UNAUTHORIZED) - throw runtime_error("incorrect rpcuser or rpcpassword (authorization failed)"); + throw std::runtime_error("incorrect rpcuser or rpcpassword (authorization failed)"); else if (response.status >= 400 && response.status != HTTP_BAD_REQUEST && response.status != HTTP_NOT_FOUND && response.status != HTTP_INTERNAL_SERVER_ERROR) - throw runtime_error(strprintf("server returned HTTP error %d", response.status)); + throw std::runtime_error(strprintf("server returned HTTP error %d", response.status)); else if (response.body.empty()) - throw runtime_error("no response from server"); + throw std::runtime_error("no response from server"); // Parse reply UniValue valReply(UniValue::VSTR); if (!valReply.read(response.body)) - throw runtime_error("couldn't parse reply from server"); + throw std::runtime_error("couldn't parse reply from server"); const UniValue& reply = valReply.get_obj(); if (reply.empty()) - throw runtime_error("expected reply to have result, error and id properties"); + throw std::runtime_error("expected reply to have result, error and id properties"); return reply; } int CommandLineRPC(int argc, char *argv[]) { - string strPrint; + std::string strPrint; int nRet = 0; try { // Skip switches @@ -286,7 +284,7 @@ int CommandLineRPC(int argc, char *argv[]) args.push_back(line); } if (args.size() < 1) - throw runtime_error("too few parameters (need at least command)"); + throw std::runtime_error("too few parameters (need at least command)"); std::string strMethod = args[0]; UniValue params = RPCConvertValues(strMethod, std::vector<std::string>(args.begin()+1, args.end())); @@ -340,7 +338,7 @@ int CommandLineRPC(int argc, char *argv[]) throw; } catch (const std::exception& e) { - strPrint = string("error: ") + e.what(); + strPrint = std::string("error: ") + e.what(); nRet = EXIT_FAILURE; } catch (...) { diff --git a/src/bitcoin-tx.cpp b/src/bitcoin-tx.cpp index 6d17bc392f..346d8e180d 100644 --- a/src/bitcoin-tx.cpp +++ b/src/bitcoin-tx.cpp @@ -26,10 +26,8 @@ #include <boost/algorithm/string.hpp> #include <boost/assign/list_of.hpp> -using namespace std; - static bool fCreateBlank; -static map<string,UniValue> registers; +static std::map<std::string,UniValue> registers; static const int CONTINUE_EXECUTION=-1; // @@ -103,52 +101,52 @@ static int AppInitRawTx(int argc, char* argv[]) return CONTINUE_EXECUTION; } -static void RegisterSetJson(const string& key, const string& rawJson) +static void RegisterSetJson(const std::string& key, const std::string& rawJson) { UniValue val; if (!val.read(rawJson)) { - string strErr = "Cannot parse JSON for key " + key; - throw runtime_error(strErr); + std::string strErr = "Cannot parse JSON for key " + key; + throw std::runtime_error(strErr); } registers[key] = val; } -static void RegisterSet(const string& strInput) +static void RegisterSet(const std::string& strInput) { // separate NAME:VALUE in string size_t pos = strInput.find(':'); - if ((pos == string::npos) || + if ((pos == std::string::npos) || (pos == 0) || (pos == (strInput.size() - 1))) - throw runtime_error("Register input requires NAME:VALUE"); + throw std::runtime_error("Register input requires NAME:VALUE"); - string key = strInput.substr(0, pos); - string valStr = strInput.substr(pos + 1, string::npos); + std::string key = strInput.substr(0, pos); + std::string valStr = strInput.substr(pos + 1, std::string::npos); RegisterSetJson(key, valStr); } -static void RegisterLoad(const string& strInput) +static void RegisterLoad(const std::string& strInput) { // separate NAME:FILENAME in string size_t pos = strInput.find(':'); - if ((pos == string::npos) || + if ((pos == std::string::npos) || (pos == 0) || (pos == (strInput.size() - 1))) - throw runtime_error("Register load requires NAME:FILENAME"); + throw std::runtime_error("Register load requires NAME:FILENAME"); - string key = strInput.substr(0, pos); - string filename = strInput.substr(pos + 1, string::npos); + std::string key = strInput.substr(0, pos); + std::string filename = strInput.substr(pos + 1, std::string::npos); FILE *f = fopen(filename.c_str(), "r"); if (!f) { - string strErr = "Cannot open file " + filename; - throw runtime_error(strErr); + std::string strErr = "Cannot open file " + filename; + throw std::runtime_error(strErr); } // load file chunks into one big buffer - string valStr; + std::string valStr; while ((!feof(f)) && (!ferror(f))) { char buf[4096]; int bread = fread(buf, 1, sizeof(buf), f); @@ -162,55 +160,55 @@ static void RegisterLoad(const string& strInput) fclose(f); if (error) { - string strErr = "Error reading file " + filename; - throw runtime_error(strErr); + std::string strErr = "Error reading file " + filename; + throw std::runtime_error(strErr); } // evaluate as JSON buffer register RegisterSetJson(key, valStr); } -static void MutateTxVersion(CMutableTransaction& tx, const string& cmdVal) +static void MutateTxVersion(CMutableTransaction& tx, const std::string& cmdVal) { int64_t newVersion = atoi64(cmdVal); if (newVersion < 1 || newVersion > CTransaction::MAX_STANDARD_VERSION) - throw runtime_error("Invalid TX version requested"); + throw std::runtime_error("Invalid TX version requested"); tx.nVersion = (int) newVersion; } -static void MutateTxLocktime(CMutableTransaction& tx, const string& cmdVal) +static void MutateTxLocktime(CMutableTransaction& tx, const std::string& cmdVal) { int64_t newLocktime = atoi64(cmdVal); if (newLocktime < 0LL || newLocktime > 0xffffffffLL) - throw runtime_error("Invalid TX locktime requested"); + throw std::runtime_error("Invalid TX locktime requested"); tx.nLockTime = (unsigned int) newLocktime; } -static void MutateTxAddInput(CMutableTransaction& tx, const string& strInput) +static void MutateTxAddInput(CMutableTransaction& tx, const std::string& strInput) { std::vector<std::string> vStrInputParts; boost::split(vStrInputParts, strInput, boost::is_any_of(":")); // separate TXID:VOUT in string if (vStrInputParts.size()<2) - throw runtime_error("TX input missing separator"); + throw std::runtime_error("TX input missing separator"); // extract and validate TXID - string strTxid = vStrInputParts[0]; + std::string strTxid = vStrInputParts[0]; if ((strTxid.size() != 64) || !IsHex(strTxid)) - throw runtime_error("invalid TX input txid"); + throw std::runtime_error("invalid TX input txid"); uint256 txid(uint256S(strTxid)); static const unsigned int minTxOutSz = 9; static const unsigned int maxVout = MAX_BLOCK_BASE_SIZE / minTxOutSz; // extract and validate vout - string strVout = vStrInputParts[1]; + std::string strVout = vStrInputParts[1]; int vout = atoi(strVout); if ((vout < 0) || (vout > (int)maxVout)) - throw runtime_error("invalid TX input vout"); + throw std::runtime_error("invalid TX input vout"); // extract the optional sequence number uint32_t nSequenceIn=std::numeric_limits<unsigned int>::max(); @@ -222,26 +220,26 @@ static void MutateTxAddInput(CMutableTransaction& tx, const string& strInput) tx.vin.push_back(txin); } -static void MutateTxAddOutAddr(CMutableTransaction& tx, const string& strInput) +static void MutateTxAddOutAddr(CMutableTransaction& tx, const std::string& strInput) { // separate VALUE:ADDRESS in string size_t pos = strInput.find(':'); - if ((pos == string::npos) || + if ((pos == std::string::npos) || (pos == 0) || (pos == (strInput.size() - 1))) - throw runtime_error("TX output missing separator"); + throw std::runtime_error("TX output missing separator"); // extract and validate VALUE - string strValue = strInput.substr(0, pos); + std::string strValue = strInput.substr(0, pos); CAmount value; if (!ParseMoney(strValue, value)) - throw runtime_error("invalid TX output value"); + throw std::runtime_error("invalid TX output value"); // extract and validate ADDRESS - string strAddr = strInput.substr(pos + 1, string::npos); + std::string strAddr = strInput.substr(pos + 1, std::string::npos); CBitcoinAddress addr(strAddr); if (!addr.IsValid()) - throw runtime_error("invalid TX output address"); + throw std::runtime_error("invalid TX output address"); // build standard output script via GetScriptForDestination() CScript scriptPubKey = GetScriptForDestination(addr.Get()); @@ -251,7 +249,7 @@ static void MutateTxAddOutAddr(CMutableTransaction& tx, const string& strInput) tx.vout.push_back(txout); } -static void MutateTxAddOutData(CMutableTransaction& tx, const string& strInput) +static void MutateTxAddOutData(CMutableTransaction& tx, const std::string& strInput) { CAmount value = 0; @@ -259,20 +257,20 @@ static void MutateTxAddOutData(CMutableTransaction& tx, const string& strInput) size_t pos = strInput.find(':'); if (pos==0) - throw runtime_error("TX output value not specified"); + throw std::runtime_error("TX output value not specified"); - if (pos != string::npos) { + if (pos != std::string::npos) { // extract and validate VALUE - string strValue = strInput.substr(0, pos); + std::string strValue = strInput.substr(0, pos); if (!ParseMoney(strValue, value)) - throw runtime_error("invalid TX output value"); + throw std::runtime_error("invalid TX output value"); } // extract and validate DATA - string strData = strInput.substr(pos + 1, string::npos); + std::string strData = strInput.substr(pos + 1, std::string::npos); if (!IsHex(strData)) - throw runtime_error("invalid TX output data"); + throw std::runtime_error("invalid TX output data"); std::vector<unsigned char> data = ParseHex(strData); @@ -280,22 +278,22 @@ static void MutateTxAddOutData(CMutableTransaction& tx, const string& strInput) tx.vout.push_back(txout); } -static void MutateTxAddOutScript(CMutableTransaction& tx, const string& strInput) +static void MutateTxAddOutScript(CMutableTransaction& tx, const std::string& strInput) { // separate VALUE:SCRIPT in string size_t pos = strInput.find(':'); - if ((pos == string::npos) || + if ((pos == std::string::npos) || (pos == 0)) - throw runtime_error("TX output missing separator"); + throw std::runtime_error("TX output missing separator"); // extract and validate VALUE - string strValue = strInput.substr(0, pos); + std::string strValue = strInput.substr(0, pos); CAmount value; if (!ParseMoney(strValue, value)) - throw runtime_error("invalid TX output value"); + throw std::runtime_error("invalid TX output value"); // extract and validate script - string strScript = strInput.substr(pos + 1, string::npos); + std::string strScript = strInput.substr(pos + 1, std::string::npos); CScript scriptPubKey = ParseScript(strScript); // throws on err // construct TxOut, append to transaction output list @@ -303,26 +301,26 @@ static void MutateTxAddOutScript(CMutableTransaction& tx, const string& strInput tx.vout.push_back(txout); } -static void MutateTxDelInput(CMutableTransaction& tx, const string& strInIdx) +static void MutateTxDelInput(CMutableTransaction& tx, const std::string& strInIdx) { // parse requested deletion index int inIdx = atoi(strInIdx); if (inIdx < 0 || inIdx >= (int)tx.vin.size()) { - string strErr = "Invalid TX input index '" + strInIdx + "'"; - throw runtime_error(strErr.c_str()); + std::string strErr = "Invalid TX input index '" + strInIdx + "'"; + throw std::runtime_error(strErr.c_str()); } // delete input from transaction tx.vin.erase(tx.vin.begin() + inIdx); } -static void MutateTxDelOutput(CMutableTransaction& tx, const string& strOutIdx) +static void MutateTxDelOutput(CMutableTransaction& tx, const std::string& strOutIdx) { // parse requested deletion index int outIdx = atoi(strOutIdx); if (outIdx < 0 || outIdx >= (int)tx.vout.size()) { - string strErr = "Invalid TX output index '" + strOutIdx + "'"; - throw runtime_error(strErr.c_str()); + std::string strErr = "Invalid TX output index '" + strOutIdx + "'"; + throw std::runtime_error(strErr.c_str()); } // delete output from transaction @@ -342,7 +340,7 @@ static const struct { {"SINGLE|ANYONECANPAY", SIGHASH_SINGLE|SIGHASH_ANYONECANPAY}, }; -static bool findSighashFlags(int& flags, const string& flagStr) +static bool findSighashFlags(int& flags, const std::string& flagStr) { flags = 0; @@ -356,17 +354,17 @@ static bool findSighashFlags(int& flags, const string& flagStr) return false; } -uint256 ParseHashUO(map<string,UniValue>& o, string strKey) +uint256 ParseHashUO(std::map<std::string,UniValue>& o, std::string strKey) { if (!o.count(strKey)) return uint256(); return ParseHashUV(o[strKey], strKey); } -vector<unsigned char> ParseHexUO(map<string,UniValue>& o, string strKey) +std::vector<unsigned char> ParseHexUO(std::map<std::string,UniValue>& o, std::string strKey) { if (!o.count(strKey)) { - vector<unsigned char> emptyVec; + std::vector<unsigned char> emptyVec; return emptyVec; } return ParseHexUV(o[strKey], strKey); @@ -375,24 +373,24 @@ vector<unsigned char> ParseHexUO(map<string,UniValue>& o, string strKey) static CAmount AmountFromValue(const UniValue& value) { if (!value.isNum() && !value.isStr()) - throw runtime_error("Amount is not a number or string"); + throw std::runtime_error("Amount is not a number or string"); CAmount amount; if (!ParseFixedPoint(value.getValStr(), 8, &amount)) - throw runtime_error("Invalid amount"); + throw std::runtime_error("Invalid amount"); if (!MoneyRange(amount)) - throw runtime_error("Amount out of range"); + throw std::runtime_error("Amount out of range"); return amount; } -static void MutateTxSign(CMutableTransaction& tx, const string& flagStr) +static void MutateTxSign(CMutableTransaction& tx, const std::string& flagStr) { int nHashType = SIGHASH_ALL; if (flagStr.size() > 0) if (!findSighashFlags(nHashType, flagStr)) - throw runtime_error("unknown sighash flag/sign option"); + throw std::runtime_error("unknown sighash flag/sign option"); - vector<CTransaction> txVariants; + std::vector<CTransaction> txVariants; txVariants.push_back(tx); // mergedTx will end up with all the signatures; it @@ -403,17 +401,17 @@ static void MutateTxSign(CMutableTransaction& tx, const string& flagStr) CCoinsViewCache view(&viewDummy); if (!registers.count("privatekeys")) - throw runtime_error("privatekeys register variable must be set."); + throw std::runtime_error("privatekeys register variable must be set."); CBasicKeyStore tempKeystore; UniValue keysObj = registers["privatekeys"]; for (unsigned int kidx = 0; kidx < keysObj.size(); kidx++) { if (!keysObj[kidx].isStr()) - throw runtime_error("privatekey not a string"); + throw std::runtime_error("privatekey not a std::string"); CBitcoinSecret vchSecret; bool fGood = vchSecret.SetString(keysObj[kidx].getValStr()); if (!fGood) - throw runtime_error("privatekey not valid"); + throw std::runtime_error("privatekey not valid"); CKey key = vchSecret.GetKey(); tempKeystore.AddKey(key); @@ -421,34 +419,34 @@ static void MutateTxSign(CMutableTransaction& tx, const string& flagStr) // Add previous txouts given in the RPC call: if (!registers.count("prevtxs")) - throw runtime_error("prevtxs register variable must be set."); + throw std::runtime_error("prevtxs register variable must be set."); UniValue prevtxsObj = registers["prevtxs"]; { for (unsigned int previdx = 0; previdx < prevtxsObj.size(); previdx++) { UniValue prevOut = prevtxsObj[previdx]; if (!prevOut.isObject()) - throw runtime_error("expected prevtxs internal object"); + throw std::runtime_error("expected prevtxs internal object"); - map<string,UniValue::VType> types = boost::assign::map_list_of("txid", UniValue::VSTR)("vout",UniValue::VNUM)("scriptPubKey",UniValue::VSTR); + std::map<std::string,UniValue::VType> types = boost::assign::map_list_of("txid", UniValue::VSTR)("vout",UniValue::VNUM)("scriptPubKey",UniValue::VSTR); if (!prevOut.checkObject(types)) - throw runtime_error("prevtxs internal object typecheck fail"); + throw std::runtime_error("prevtxs internal object typecheck fail"); uint256 txid = ParseHashUV(prevOut["txid"], "txid"); int nOut = atoi(prevOut["vout"].getValStr()); if (nOut < 0) - throw runtime_error("vout must be positive"); + throw std::runtime_error("vout must be positive"); - vector<unsigned char> pkData(ParseHexUV(prevOut["scriptPubKey"], "scriptPubKey")); + std::vector<unsigned char> pkData(ParseHexUV(prevOut["scriptPubKey"], "scriptPubKey")); CScript scriptPubKey(pkData.begin(), pkData.end()); { CCoinsModifier coins = view.ModifyCoins(txid); if (coins->IsAvailable(nOut) && coins->vout[nOut].scriptPubKey != scriptPubKey) { - string err("Previous output scriptPubKey mismatch:\n"); + std::string err("Previous output scriptPubKey mismatch:\n"); err = err + ScriptToAsmStr(coins->vout[nOut].scriptPubKey) + "\nvs:\n"+ ScriptToAsmStr(scriptPubKey); - throw runtime_error(err); + throw std::runtime_error(err); } if ((unsigned int)nOut >= coins->vout.size()) coins->vout.resize(nOut+1); @@ -464,7 +462,7 @@ static void MutateTxSign(CMutableTransaction& tx, const string& flagStr) if ((scriptPubKey.IsPayToScriptHash() || scriptPubKey.IsPayToWitnessScriptHash()) && prevOut.exists("redeemScript")) { UniValue v = prevOut["redeemScript"]; - vector<unsigned char> rsData(ParseHexUV(v, "redeemScript")); + std::vector<unsigned char> rsData(ParseHexUV(v, "redeemScript")); CScript redeemScript(rsData.begin(), rsData.end()); tempKeystore.AddCScript(redeemScript); } @@ -521,8 +519,8 @@ public: } }; -static void MutateTx(CMutableTransaction& tx, const string& command, - const string& commandVal) +static void MutateTx(CMutableTransaction& tx, const std::string& command, + const std::string& commandVal) { std::unique_ptr<Secp256k1Init> ecc; @@ -557,7 +555,7 @@ static void MutateTx(CMutableTransaction& tx, const string& command, RegisterSet(commandVal); else - throw runtime_error("unknown command"); + throw std::runtime_error("unknown command"); } static void OutputTxJSON(const CTransaction& tx) @@ -565,20 +563,20 @@ static void OutputTxJSON(const CTransaction& tx) UniValue entry(UniValue::VOBJ); TxToUniv(tx, uint256(), entry); - string jsonOutput = entry.write(4); + std::string jsonOutput = entry.write(4); fprintf(stdout, "%s\n", jsonOutput.c_str()); } static void OutputTxHash(const CTransaction& tx) { - string strHexHash = tx.GetHash().GetHex(); // the hex-encoded transaction hash (aka the transaction id) + std::string strHexHash = tx.GetHash().GetHex(); // the hex-encoded transaction hash (aka the transaction id) fprintf(stdout, "%s\n", strHexHash.c_str()); } static void OutputTxHex(const CTransaction& tx) { - string strHex = EncodeHexTx(tx); + std::string strHex = EncodeHexTx(tx); fprintf(stdout, "%s\n", strHex.c_str()); } @@ -593,10 +591,10 @@ static void OutputTx(const CTransaction& tx) OutputTxHex(tx); } -static string readStdin() +static std::string readStdin() { char buf[4096]; - string ret; + std::string ret; while (!feof(stdin)) { size_t bread = fread(buf, 1, sizeof(buf), stdin); @@ -606,7 +604,7 @@ static string readStdin() } if (ferror(stdin)) - throw runtime_error("error reading stdin"); + throw std::runtime_error("error reading stdin"); boost::algorithm::trim_right(ret); @@ -615,7 +613,7 @@ static string readStdin() static int CommandLineRawTx(int argc, char* argv[]) { - string strPrint; + std::string strPrint; int nRet = 0; try { // Skip switches; Permit common stdin convention "-" @@ -631,15 +629,15 @@ static int CommandLineRawTx(int argc, char* argv[]) if (!fCreateBlank) { // require at least one param if (argc < 2) - throw runtime_error("too few parameters"); + throw std::runtime_error("too few parameters"); // param: hex-encoded bitcoin transaction - string strHexTx(argv[1]); + std::string strHexTx(argv[1]); if (strHexTx == "-") // "-" implies standard input strHexTx = readStdin(); if (!DecodeHexTx(txDecodeTmp, strHexTx, true)) - throw runtime_error("invalid transaction encoding"); + throw std::runtime_error("invalid transaction encoding"); startArg = 2; } else @@ -648,10 +646,10 @@ static int CommandLineRawTx(int argc, char* argv[]) CMutableTransaction tx(txDecodeTmp); for (int i = startArg; i < argc; i++) { - string arg = argv[i]; - string key, value; + std::string arg = argv[i]; + std::string key, value; size_t eqpos = arg.find('='); - if (eqpos == string::npos) + if (eqpos == std::string::npos) key = arg; else { key = arg.substr(0, eqpos); @@ -668,7 +666,7 @@ static int CommandLineRawTx(int argc, char* argv[]) throw; } catch (const std::exception& e) { - strPrint = string("error: ") + e.what(); + strPrint = std::string("error: ") + e.what(); nRet = EXIT_FAILURE; } catch (...) { diff --git a/src/bitcoind.cpp b/src/bitcoind.cpp index 3352a76de6..ba3ccac615 100644 --- a/src/bitcoind.cpp +++ b/src/bitcoind.cpp @@ -128,6 +128,26 @@ bool AppInit(int argc, char* argv[]) fprintf(stderr, "Error: There is no RPC client functionality in bitcoind anymore. Use the bitcoin-cli utility instead.\n"); exit(EXIT_FAILURE); } + // -server defaults to true for bitcoind but not for the GUI so do this here + SoftSetBoolArg("-server", true); + // Set this early so that parameter interactions go to console + InitLogging(); + InitParameterInteraction(); + if (!AppInitBasicSetup()) + { + // InitError will have been called with detailed error, which ends up on console + exit(1); + } + if (!AppInitParameterInteraction()) + { + // InitError will have been called with detailed error, which ends up on console + exit(1); + } + if (!AppInitSanityChecks()) + { + // InitError will have been called with detailed error, which ends up on console + exit(1); + } if (GetBoolArg("-daemon", false)) { #if HAVE_DECL_DAEMON @@ -143,12 +163,8 @@ bool AppInit(int argc, char* argv[]) return false; #endif // HAVE_DECL_DAEMON } - SoftSetBoolArg("-server", true); - // Set this early so that parameter interactions go to console - InitLogging(); - InitParameterInteraction(); - fRet = AppInit2(threadGroup, scheduler); + fRet = AppInitMain(threadGroup, scheduler); } catch (const std::exception& e) { PrintExceptionContinue(&e, "AppInit()"); diff --git a/src/blockencodings.cpp b/src/blockencodings.cpp index f14ae1c412..2efeda9cc6 100644 --- a/src/blockencodings.cpp +++ b/src/blockencodings.cpp @@ -62,7 +62,7 @@ ReadStatus PartiallyDownloadedBlock::InitData(const CBlockHeaderAndShortTxIDs& c if (cmpctblock.prefilledtxn[i].tx->IsNull()) return READ_STATUS_INVALID; - lastprefilledindex += cmpctblock.prefilledtxn[i].index + 1; //index is a uint16_t, so cant overflow here + lastprefilledindex += cmpctblock.prefilledtxn[i].index + 1; //index is a uint16_t, so can't overflow here if (lastprefilledindex > std::numeric_limits<uint16_t>::max()) return READ_STATUS_INVALID; if ((uint32_t)lastprefilledindex > cmpctblock.shorttxids.size() + i) { diff --git a/src/compat.h b/src/compat.h index 79a297e5e4..2578d6d344 100644 --- a/src/compat.h +++ b/src/compat.h @@ -34,6 +34,7 @@ #else #include <sys/fcntl.h> #include <sys/mman.h> +#include <sys/select.h> #include <sys/socket.h> #include <sys/types.h> #include <net/if.h> diff --git a/src/core_io.h b/src/core_io.h index b559d44bf5..5aecbc4489 100644 --- a/src/core_io.h +++ b/src/core_io.h @@ -15,18 +15,18 @@ class uint256; class UniValue; // core_read.cpp -extern CScript ParseScript(const std::string& s); -extern std::string ScriptToAsmStr(const CScript& script, const bool fAttemptSighashDecode = false); -extern bool DecodeHexTx(CTransaction& tx, const std::string& strHexTx, bool fTryNoWitness = false); -extern bool DecodeHexBlk(CBlock&, const std::string& strHexBlk); -extern uint256 ParseHashUV(const UniValue& v, const std::string& strName); -extern uint256 ParseHashStr(const std::string&, const std::string& strName); -extern std::vector<unsigned char> ParseHexUV(const UniValue& v, const std::string& strName); +CScript ParseScript(const std::string& s); +std::string ScriptToAsmStr(const CScript& script, const bool fAttemptSighashDecode = false); +bool DecodeHexTx(CTransaction& tx, const std::string& strHexTx, bool fTryNoWitness = false); +bool DecodeHexBlk(CBlock&, const std::string& strHexBlk); +uint256 ParseHashUV(const UniValue& v, const std::string& strName); +uint256 ParseHashStr(const std::string&, const std::string& strName); +std::vector<unsigned char> ParseHexUV(const UniValue& v, const std::string& strName); // core_write.cpp -extern std::string FormatScript(const CScript& script); -extern std::string EncodeHexTx(const CTransaction& tx); -extern void ScriptPubKeyToUniv(const CScript& scriptPubKey, UniValue& out, bool fIncludeHex); -extern void TxToUniv(const CTransaction& tx, const uint256& hashBlock, UniValue& entry); +std::string FormatScript(const CScript& script); +std::string EncodeHexTx(const CTransaction& tx); +void ScriptPubKeyToUniv(const CScript& scriptPubKey, UniValue& out, bool fIncludeHex); +void TxToUniv(const CTransaction& tx, const uint256& hashBlock, UniValue& entry); #endif // BITCOIN_CORE_IO_H diff --git a/src/crypto/ctaes/test.c b/src/crypto/ctaes/test.c index fce1696acd..21439a16f1 100644 --- a/src/crypto/ctaes/test.c +++ b/src/crypto/ctaes/test.c @@ -102,7 +102,7 @@ int main(void) { } } if (fail == 0) { - fprintf(stderr, "All tests succesful\n"); + fprintf(stderr, "All tests successful\n"); } else { fprintf(stderr, "%i tests failed\n", fail); } diff --git a/src/init.cpp b/src/init.cpp index ca5437fb91..377d196f2b 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -780,10 +780,17 @@ void InitLogging() LogPrintf("Bitcoin version %s\n", FormatFullVersion()); } -/** Initialize bitcoin. - * @pre Parameters should be parsed and config file should be read. - */ -bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) +namespace { // Variables internal to initialization process only + +ServiceFlags nRelevantServices = NODE_NETWORK; +int nMaxConnections; +int nUserMaxConnections; +int nFD; +ServiceFlags nLocalServices = NODE_NETWORK; + +} + +bool AppInitBasicSetup() { // ********************************************************* Step 1: setup #ifdef _MSC_VER @@ -835,9 +842,13 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) // Ignore SIGPIPE, otherwise it will bring the daemon down if the client closes unexpectedly signal(SIGPIPE, SIG_IGN); #endif + return true; +} - // ********************************************************* Step 2: parameter interactions +bool AppInitParameterInteraction() +{ const CChainParams& chainparams = Params(); + // ********************************************************* Step 2: parameter interactions // also see: InitParameterInteraction() @@ -848,13 +859,15 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) } // Make sure enough file descriptors are available - int nBind = std::max((int)mapArgs.count("-bind") + (int)mapArgs.count("-whitebind"), 1); - int nUserMaxConnections = GetArg("-maxconnections", DEFAULT_MAX_PEER_CONNECTIONS); - int nMaxConnections = std::max(nUserMaxConnections, 0); + int nBind = std::max( + (mapMultiArgs.count("-bind") ? mapMultiArgs.at("-bind").size() : 0) + + (mapMultiArgs.count("-whitebind") ? mapMultiArgs.at("-whitebind").size() : 0), size_t(1)); + nUserMaxConnections = GetArg("-maxconnections", DEFAULT_MAX_PEER_CONNECTIONS); + nMaxConnections = std::max(nUserMaxConnections, 0); // Trim requested connection counts, to fit into system limitations nMaxConnections = std::max(std::min(nMaxConnections, (int)(FD_SETSIZE - nBind - MIN_CORE_FILEDESCRIPTORS)), 0); - int nFD = RaiseFileDescriptorLimit(nMaxConnections + MIN_CORE_FILEDESCRIPTORS); + nFD = RaiseFileDescriptorLimit(nMaxConnections + MIN_CORE_FILEDESCRIPTORS); if (nFD < MIN_CORE_FILEDESCRIPTORS) return InitError(_("Not enough file descriptors available.")); nMaxConnections = std::min(nFD - MIN_CORE_FILEDESCRIPTORS, nMaxConnections); @@ -912,8 +925,6 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) else if (nScriptCheckThreads > MAX_SCRIPTCHECK_THREADS) nScriptCheckThreads = MAX_SCRIPTCHECK_THREADS; - fServer = GetBoolArg("-server", false); - // block pruning; get the amount of disk space (in MiB) to allot for block & undo files int64_t nSignedPruneTarget = GetArg("-prune", 0) * 1024 * 1024; if (nSignedPruneTarget < 0) { @@ -969,9 +980,6 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) // Option to startup with mocktime set (used for regression testing): SetMockTime(GetArg("-mocktime", 0)); // SetMockTime(0) is a no-op - ServiceFlags nLocalServices = NODE_NETWORK; - ServiceFlags nRelevantServices = NODE_NETWORK; - if (GetBoolArg("-peerbloomfilters", DEFAULT_PEERBLOOMFILTERS)) nLocalServices = ServiceFlags(nLocalServices | NODE_BLOOM); @@ -1020,17 +1028,11 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) } } } + return true; +} - // ********************************************************* Step 4: application initialization: dir lock, daemonize, pidfile, debug log - - // Initialize elliptic curve code - ECC_Start(); - globalVerifyHandle.reset(new ECCVerifyHandle()); - - // Sanity check - if (!InitSanityCheck()) - return InitError(strprintf(_("Initialization sanity check failed. %s is shutting down."), _(PACKAGE_NAME))); - +static bool LockDataDirectory(bool probeOnly) +{ std::string strDataDir = GetDataDir().string(); // Make sure only a single Bitcoin process is using the data directory. @@ -1040,11 +1042,45 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) try { static boost::interprocess::file_lock lock(pathLockFile.string().c_str()); - if (!lock.try_lock()) + if (!lock.try_lock()) { return InitError(strprintf(_("Cannot obtain a lock on data directory %s. %s is probably already running."), strDataDir, _(PACKAGE_NAME))); + } + if (probeOnly) { + lock.unlock(); + } } catch(const boost::interprocess::interprocess_exception& e) { return InitError(strprintf(_("Cannot obtain a lock on data directory %s. %s is probably already running.") + " %s.", strDataDir, _(PACKAGE_NAME), e.what())); } + return true; +} + +bool AppInitSanityChecks() +{ + // ********************************************************* Step 4: sanity checks + + // Initialize elliptic curve code + ECC_Start(); + globalVerifyHandle.reset(new ECCVerifyHandle()); + + // Sanity check + if (!InitSanityCheck()) + return InitError(strprintf(_("Initialization sanity check failed. %s is shutting down."), _(PACKAGE_NAME))); + + // Probe the data directory lock to give an early error message, if possible + return LockDataDirectory(true); +} + +bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler) +{ + const CChainParams& chainparams = Params(); + // ********************************************************* Step 4a: application initialization + // After daemonization get the data directory lock again and hold on to it until exit + // This creates a slight window for a race condition to happen, however this condition is harmless: it + // will at most make us exit without printing a message to console. + if (!LockDataDirectory(false)) { + // Detailed error printed inside LockDataDirectory + return false; + } #ifndef WIN32 CreatePidFile(GetPidFile(), getpid()); @@ -1058,7 +1094,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) if (!fLogTimestamps) LogPrintf("Startup time: %s\n", DateTimeStrFormat("%Y-%m-%d %H:%M:%S", GetTime())); LogPrintf("Default data directory %s\n", GetDefaultDataDir().string()); - LogPrintf("Using data directory %s\n", strDataDir); + LogPrintf("Using data directory %s\n", GetDataDir().string()); LogPrintf("Using config file %s\n", GetConfigFile(GetArg("-conf", BITCOIN_CONF_FILENAME)).string()); LogPrintf("Using at most %i connections (%i file descriptors available)\n", nMaxConnections, nFD); @@ -1077,7 +1113,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) * that the server is there and will be ready later). Warmup mode will * be disabled when initialisation is finished. */ - if (fServer) + if (GetBoolArg("-server", false)) { uiInterface.InitMessage.connect(SetRPCWarmupStatus); if (!AppInitServers(threadGroup)) diff --git a/src/init.h b/src/init.h index 63e07ccb3c..1b87e0ec52 100644 --- a/src/init.h +++ b/src/init.h @@ -25,7 +25,30 @@ void Shutdown(); void InitLogging(); //!Parameter interaction: change current parameters depending on various rules void InitParameterInteraction(); -bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler); + +/** Initialize bitcoin core: Basic context setup. + * @note This can be done before daemonization. + * @pre Parameters should be parsed and config file should be read. + */ +bool AppInitBasicSetup(); +/** + * Initialization: parameter interaction. + * @note This can be done before daemonization. + * @pre Parameters should be parsed and config file should be read, AppInitBasicSetup should have been called. + */ +bool AppInitParameterInteraction(); +/** + * Initialization sanity checks: ecc init, sanity checks, dir lock. + * @note This can be done before daemonization. + * @pre Parameters should be parsed and config file should be read, AppInitParameterInteraction should have been called. + */ +bool AppInitSanityChecks(); +/** + * Bitcoin core main initialization. + * @note This should only be done after daemonization. + * @pre Parameters should be parsed and config file should be read, AppInitSanityChecks should have been called. + */ +bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler); /** The help message mode determines what help message to show */ enum HelpMessageMode { diff --git a/src/main.cpp b/src/main.cpp index 04a55b3dcc..e2a1f31228 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -18,6 +18,7 @@ #include "init.h" #include "merkleblock.h" #include "net.h" +#include "netmessagemaker.h" #include "netbase.h" #include "policy/fees.h" #include "policy/policy.h" @@ -68,7 +69,7 @@ int64_t nTimeBestReceived = 0; // Used only to inform the wallet of when we last CWaitableCriticalSection csBestBlock; CConditionVariable cvBlockChange; int nScriptCheckThreads = 0; -bool fImporting = false; +std::atomic_bool fImporting(false); bool fReindex = false; bool fTxIndex = false; bool fHavePruned = false; @@ -368,8 +369,8 @@ void PushNodeVersion(CNode *pnode, CConnman& connman, int64_t nTime) CAddress addrYou = (addr.IsRoutable() && !IsProxy(addr) ? addr : CAddress(CService(), addr.nServices)); CAddress addrMe = CAddress(CService(), nLocalNodeServices); - connman.PushMessageWithVersion(pnode, INIT_PROTO_VERSION, NetMsgType::VERSION, PROTOCOL_VERSION, (uint64_t)nLocalNodeServices, nTime, addrYou, addrMe, - nonce, strSubVersion, nNodeStartingHeight, ::fRelayTxes); + connman.PushMessage(pnode, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::VERSION, PROTOCOL_VERSION, (uint64_t)nLocalNodeServices, nTime, addrYou, addrMe, + nonce, strSubVersion, nNodeStartingHeight, ::fRelayTxes)); if (fLogIPs) LogPrint("net", "send version message: version %d, blocks=%d, us=%s, them=%s, peer=%d\n", PROTOCOL_VERSION, nNodeStartingHeight, addrMe.ToString(), addrYou.ToString(), nodeid); @@ -530,13 +531,13 @@ void MaybeSetPeerAsAnnouncingHeaderAndIDs(const CNodeState* nodestate, CNode* pf // As per BIP152, we only get 3 of our peers to announce // blocks using compact encodings. connman.ForNode(lNodesAnnouncingHeaderAndIDs.front(), [&connman, fAnnounceUsingCMPCTBLOCK, nCMPCTBLOCKVersion](CNode* pnodeStop){ - connman.PushMessage(pnodeStop, NetMsgType::SENDCMPCT, fAnnounceUsingCMPCTBLOCK, nCMPCTBLOCKVersion); + connman.PushMessage(pnodeStop, CNetMsgMaker(pnodeStop->GetSendVersion()).Make(NetMsgType::SENDCMPCT, fAnnounceUsingCMPCTBLOCK, nCMPCTBLOCKVersion)); return true; }); lNodesAnnouncingHeaderAndIDs.pop_front(); } fAnnounceUsingCMPCTBLOCK = true; - connman.PushMessage(pfrom, NetMsgType::SENDCMPCT, fAnnounceUsingCMPCTBLOCK, nCMPCTBLOCKVersion); + connman.PushMessage(pfrom, CNetMsgMaker(pfrom->GetSendVersion()).Make(NetMsgType::SENDCMPCT, fAnnounceUsingCMPCTBLOCK, nCMPCTBLOCKVersion)); lNodesAnnouncingHeaderAndIDs.push_back(pfrom->GetId()); } } @@ -3511,15 +3512,8 @@ std::vector<unsigned char> GenerateCoinbaseCommitment(CBlock& block, const CBloc { std::vector<unsigned char> commitment; int commitpos = GetWitnessCommitmentIndex(block); - bool fHaveWitness = false; - for (size_t t = 1; t < block.vtx.size(); t++) { - if (!block.vtx[t]->wit.IsNull()) { - fHaveWitness = true; - break; - } - } std::vector<unsigned char> ret(32, 0x00); - if (fHaveWitness && IsWitnessEnabled(pindexPrev, consensusParams)) { + if (consensusParams.vDeployments[Consensus::DEPLOYMENT_SEGWIT].nTimeout != 0) { if (commitpos == -1) { uint256 witnessroot = BlockWitnessMerkleRoot(block, NULL); CHash256().Write(witnessroot.begin(), 32).Write(&ret[0], 32).Finalize(witnessroot.begin()); @@ -4908,9 +4902,8 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam { std::deque<CInv>::iterator it = pfrom->vRecvGetData.begin(); unsigned int nMaxSendBufferSize = connman.GetSendBufferSize(); - vector<CInv> vNotFound; - + CNetMsgMaker msgMaker(pfrom->GetSendVersion()); LOCK(cs_main); while (it != pfrom->vRecvGetData.end()) { @@ -4964,9 +4957,9 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam if (!ReadBlockFromDisk(block, (*mi).second, consensusParams)) assert(!"cannot load block from disk"); if (inv.type == MSG_BLOCK) - connman.PushMessageWithFlag(pfrom, SERIALIZE_TRANSACTION_NO_WITNESS, NetMsgType::BLOCK, block); + connman.PushMessage(pfrom, msgMaker.Make(SERIALIZE_TRANSACTION_NO_WITNESS, NetMsgType::BLOCK, block)); else if (inv.type == MSG_WITNESS_BLOCK) - connman.PushMessage(pfrom, NetMsgType::BLOCK, block); + connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::BLOCK, block)); else if (inv.type == MSG_FILTERED_BLOCK) { bool sendMerkleBlock = false; @@ -4979,7 +4972,7 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam } } if (sendMerkleBlock) { - connman.PushMessage(pfrom, NetMsgType::MERKLEBLOCK, merkleBlock); + connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::MERKLEBLOCK, merkleBlock)); // CMerkleBlock just contains hashes, so also push any transactions in the block the client did not see // This avoids hurting performance by pointlessly requiring a round-trip // Note that there is currently no way for a node to request any single transactions we didn't send here - @@ -4988,7 +4981,7 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam // however we MUST always provide at least what the remote peer needs typedef std::pair<unsigned int, uint256> PairType; BOOST_FOREACH(PairType& pair, merkleBlock.vMatchedTxn) - connman.PushMessageWithFlag(pfrom, SERIALIZE_TRANSACTION_NO_WITNESS, NetMsgType::TX, *block.vtx[pair.first]); + connman.PushMessage(pfrom, msgMaker.Make(SERIALIZE_TRANSACTION_NO_WITNESS, NetMsgType::TX, *block.vtx[pair.first])); } // else // no response @@ -4996,15 +4989,16 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam else if (inv.type == MSG_CMPCT_BLOCK) { // If a peer is asking for old blocks, we're almost guaranteed - // they wont have a useful mempool to match against a compact block, + // they won't have a useful mempool to match against a compact block, // and we don't feel like constructing the object for them, so // instead we respond with the full, non-compact block. bool fPeerWantsWitness = State(pfrom->GetId())->fWantsCmpctWitness; + int nSendFlags = fPeerWantsWitness ? 0 : SERIALIZE_TRANSACTION_NO_WITNESS; if (CanDirectFetch(consensusParams) && mi->second->nHeight >= chainActive.Height() - MAX_CMPCTBLOCK_DEPTH) { CBlockHeaderAndShortTxIDs cmpctblock(block, fPeerWantsWitness); - connman.PushMessageWithFlag(pfrom, fPeerWantsWitness ? 0 : SERIALIZE_TRANSACTION_NO_WITNESS, NetMsgType::CMPCTBLOCK, cmpctblock); + connman.PushMessage(pfrom, msgMaker.Make(nSendFlags, NetMsgType::CMPCTBLOCK, cmpctblock)); } else - connman.PushMessageWithFlag(pfrom, fPeerWantsWitness ? 0 : SERIALIZE_TRANSACTION_NO_WITNESS, NetMsgType::BLOCK, block); + connman.PushMessage(pfrom, msgMaker.Make(nSendFlags, NetMsgType::BLOCK, block)); } // Trigger the peer node to send a getblocks request for the next batch of inventory @@ -5015,7 +5009,7 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam // wait for other stuff first. vector<CInv> vInv; vInv.push_back(CInv(MSG_BLOCK, chainActive.Tip()->GetBlockHash())); - connman.PushMessage(pfrom, NetMsgType::INV, vInv); + connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::INV, vInv)); pfrom->hashContinue.SetNull(); } } @@ -5025,15 +5019,16 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam // Send stream from relay memory bool push = false; auto mi = mapRelay.find(inv.hash); + int nSendFlags = (inv.type == MSG_TX ? SERIALIZE_TRANSACTION_NO_WITNESS : 0); if (mi != mapRelay.end()) { - connman.PushMessageWithFlag(pfrom, inv.type == MSG_TX ? SERIALIZE_TRANSACTION_NO_WITNESS : 0, NetMsgType::TX, *mi->second); + connman.PushMessage(pfrom, msgMaker.Make(nSendFlags, NetMsgType::TX, *mi->second)); push = true; } else if (pfrom->timeLastMempoolReq) { auto txinfo = mempool.info(inv.hash); // To protect privacy, do not answer getdata using the mempool when // that TX couldn't have been INVed in reply to a MEMPOOL request. if (txinfo.tx && txinfo.nTime <= pfrom->timeLastMempoolReq) { - connman.PushMessageWithFlag(pfrom, inv.type == MSG_TX ? SERIALIZE_TRANSACTION_NO_WITNESS : 0, NetMsgType::TX, *txinfo.tx); + connman.PushMessage(pfrom, msgMaker.Make(nSendFlags, NetMsgType::TX, *txinfo.tx)); push = true; } } @@ -5060,7 +5055,7 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam // do that because they want to know about (and store and rebroadcast and // risk analyze) the dependencies of transactions relevant to them, without // having to download the entire memory pool. - connman.PushMessage(pfrom, NetMsgType::NOTFOUND, vNotFound); + connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::NOTFOUND, vNotFound)); } } @@ -5101,16 +5096,10 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, if (strCommand == NetMsgType::VERSION) { - // Feeler connections exist only to verify if address is online. - if (pfrom->fFeeler) { - assert(pfrom->fInbound == false); - pfrom->fDisconnect = true; - } - // Each connection can only send one version message if (pfrom->nVersion != 0) { - connman.PushMessageWithVersion(pfrom, INIT_PROTO_VERSION, NetMsgType::REJECT, strCommand, REJECT_DUPLICATE, string("Duplicate version message")); + connman.PushMessage(pfrom, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::REJECT, strCommand, REJECT_DUPLICATE, string("Duplicate version message"))); LOCK(cs_main); Misbehaving(pfrom->GetId(), 1); return false; @@ -5130,8 +5119,8 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, if (pfrom->nServicesExpected & ~pfrom->nServices) { LogPrint("net", "peer=%d does not offer the expected services (%08x offered, %08x expected); disconnecting\n", pfrom->id, pfrom->nServices, pfrom->nServicesExpected); - connman.PushMessageWithVersion(pfrom, INIT_PROTO_VERSION, NetMsgType::REJECT, strCommand, REJECT_NONSTANDARD, - strprintf("Expected to offer services %08x", pfrom->nServicesExpected)); + connman.PushMessage(pfrom, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::REJECT, strCommand, REJECT_NONSTANDARD, + strprintf("Expected to offer services %08x", pfrom->nServicesExpected))); pfrom->fDisconnect = true; return false; } @@ -5140,8 +5129,8 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, { // disconnect from peers older than this proto version LogPrintf("peer=%d using obsolete version %i; disconnecting\n", pfrom->id, pfrom->nVersion); - connman.PushMessageWithVersion(pfrom, INIT_PROTO_VERSION, NetMsgType::REJECT, strCommand, REJECT_OBSOLETE, - strprintf("Version must be %d or greater", MIN_PEER_PROTO_VERSION)); + connman.PushMessage(pfrom, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::REJECT, strCommand, REJECT_OBSOLETE, + strprintf("Version must be %d or greater", MIN_PEER_PROTO_VERSION))); pfrom->fDisconnect = true; return false; } @@ -5198,8 +5187,9 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, } // Change version - connman.PushMessageWithVersion(pfrom, INIT_PROTO_VERSION, NetMsgType::VERACK); - pfrom->SetSendVersion(min(pfrom->nVersion, PROTOCOL_VERSION)); + connman.PushMessage(pfrom, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::VERACK)); + int nSendVersion = std::min(pfrom->nVersion, PROTOCOL_VERSION); + pfrom->SetSendVersion(nSendVersion); if (!pfrom->fInbound) { @@ -5222,7 +5212,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, // Get recent addresses if (pfrom->fOneShot || pfrom->nVersion >= CADDR_TIME_VERSION || connman.GetAddressCount() < 1000) { - connman.PushMessage(pfrom, NetMsgType::GETADDR); + connman.PushMessage(pfrom, CNetMsgMaker(nSendVersion).Make(NetMsgType::GETADDR)); pfrom->fGetAddr = true; } connman.MarkAddressGood(pfrom->addr); @@ -5242,6 +5232,13 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, int64_t nTimeOffset = nTime - GetTime(); pfrom->nTimeOffset = nTimeOffset; AddTimeData(pfrom->addr, nTimeOffset); + + // Feeler connections exist only to verify if address is online. + if (pfrom->fFeeler) { + assert(pfrom->fInbound == false); + pfrom->fDisconnect = true; + } + return true; } @@ -5253,13 +5250,15 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, return false; } + // At this point, the outgoing message serialization version can't change. + CNetMsgMaker msgMaker(pfrom->GetSendVersion()); - else if (strCommand == NetMsgType::VERACK) + if (strCommand == NetMsgType::VERACK) { pfrom->SetRecvVersion(min(pfrom->nVersion, PROTOCOL_VERSION)); - // Mark this node as currently connected, so we update its timestamp later. - if (pfrom->fNetworkNode) { + if (!pfrom->fInbound) { + // Mark this node as currently connected, so we update its timestamp later. LOCK(cs_main); State(pfrom->GetId())->fCurrentlyConnected = true; } @@ -5269,7 +5268,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, // We send this to non-NODE NETWORK peers as well, because even // non-NODE NETWORK peers can announce blocks (such as pruning // nodes) - connman.PushMessage(pfrom, NetMsgType::SENDHEADERS); + connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::SENDHEADERS)); } if (pfrom->nVersion >= SHORT_IDS_BLOCKS_VERSION) { // Tell our peer we are willing to provide version 1 or 2 cmpctblocks @@ -5280,9 +5279,9 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, bool fAnnounceUsingCMPCTBLOCK = false; uint64_t nCMPCTBLOCKVersion = 2; if (pfrom->GetLocalServices() & NODE_WITNESS) - connman.PushMessage(pfrom, NetMsgType::SENDCMPCT, fAnnounceUsingCMPCTBLOCK, nCMPCTBLOCKVersion); + connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::SENDCMPCT, fAnnounceUsingCMPCTBLOCK, nCMPCTBLOCKVersion)); nCMPCTBLOCKVersion = 1; - connman.PushMessage(pfrom, NetMsgType::SENDCMPCT, fAnnounceUsingCMPCTBLOCK, nCMPCTBLOCKVersion); + connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::SENDCMPCT, fAnnounceUsingCMPCTBLOCK, nCMPCTBLOCKVersion)); } } @@ -5407,7 +5406,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, // fell back to inv we probably have a reorg which we should get the headers for first, // we now only provide a getheaders response here. When we receive the headers, we will // then ask for the blocks we need. - connman.PushMessage(pfrom, NetMsgType::GETHEADERS, chainActive.GetLocator(pindexBestHeader), inv.hash); + connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::GETHEADERS, chainActive.GetLocator(pindexBestHeader), inv.hash)); LogPrint("net", "getheaders (%d) %s to peer=%d\n", pindexBestHeader->nHeight, inv.hash.ToString(), pfrom->id); } } @@ -5430,7 +5429,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, } if (!vToFetch.empty()) - connman.PushMessage(pfrom, NetMsgType::GETDATA, vToFetch); + connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::GETDATA, vToFetch)); } @@ -5542,7 +5541,8 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, } resp.txn[i] = block.vtx[req.indexes[i]]; } - connman.PushMessageWithFlag(pfrom, State(pfrom->GetId())->fWantsCmpctWitness ? 0 : SERIALIZE_TRANSACTION_NO_WITNESS, NetMsgType::BLOCKTXN, resp); + int nSendFlags = State(pfrom->GetId())->fWantsCmpctWitness ? 0 : SERIALIZE_TRANSACTION_NO_WITNESS; + connman.PushMessage(pfrom, msgMaker.Make(nSendFlags, NetMsgType::BLOCKTXN, resp)); } @@ -5591,7 +5591,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, // headers message). In both cases it's safe to update // pindexBestHeaderSent to be our tip. nodestate->pindexBestHeaderSent = pindex ? pindex : chainActive.Tip(); - connman.PushMessage(pfrom, NetMsgType::HEADERS, vHeaders); + connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::HEADERS, vHeaders)); } @@ -5705,8 +5705,9 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, } } if (!fRejectedParents) { + uint32_t nFetchFlags = GetFetchFlags(pfrom, chainActive.Tip(), chainparams.GetConsensus()); BOOST_FOREACH(const CTxIn& txin, tx.vin) { - CInv _inv(MSG_TX, txin.prevout.hash); + CInv _inv(MSG_TX | nFetchFlags, txin.prevout.hash); pfrom->AddInventoryKnown(_inv); if (!AlreadyHave(_inv)) pfrom->AskFor(_inv); } @@ -5754,8 +5755,8 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, pfrom->id, FormatStateMessage(state)); if (state.GetRejectCode() < REJECT_INTERNAL) // Never send AcceptToMemoryPool's internal codes over P2P - connman.PushMessage(pfrom, NetMsgType::REJECT, strCommand, (unsigned char)state.GetRejectCode(), - state.GetRejectReason().substr(0, MAX_REJECT_MESSAGE_LENGTH), inv.hash); + connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::REJECT, strCommand, (unsigned char)state.GetRejectCode(), + state.GetRejectReason().substr(0, MAX_REJECT_MESSAGE_LENGTH), inv.hash)); if (nDoS > 0) { Misbehaving(pfrom->GetId(), nDoS); } @@ -5774,7 +5775,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, if (mapBlockIndex.find(cmpctblock.header.hashPrevBlock) == mapBlockIndex.end()) { // Doesn't connect (or is genesis), instead of DoSing in AcceptBlockHeader, request deeper headers if (!IsInitialBlockDownload()) - connman.PushMessage(pfrom, NetMsgType::GETHEADERS, chainActive.GetLocator(pindexBestHeader), uint256()); + connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::GETHEADERS, chainActive.GetLocator(pindexBestHeader), uint256())); return true; } } @@ -5811,7 +5812,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, // so we just grab the block via normal getdata std::vector<CInv> vInv(1); vInv[0] = CInv(MSG_BLOCK | GetFetchFlags(pfrom, pindex->pprev, chainparams.GetConsensus()), cmpctblock.header.GetHash()); - connman.PushMessage(pfrom, NetMsgType::GETDATA, vInv); + connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::GETDATA, vInv)); } return true; } @@ -5855,7 +5856,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, // Duplicate txindexes, the block is now in-flight, so just request it std::vector<CInv> vInv(1); vInv[0] = CInv(MSG_BLOCK | GetFetchFlags(pfrom, pindex->pprev, chainparams.GetConsensus()), cmpctblock.header.GetHash()); - connman.PushMessage(pfrom, NetMsgType::GETDATA, vInv); + connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::GETDATA, vInv)); return true; } @@ -5879,7 +5880,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, return ProcessMessage(pfrom, NetMsgType::BLOCKTXN, blockTxnMsg, nTimeReceived, chainparams, connman); } else { req.blockhash = pindex->GetBlockHash(); - connman.PushMessage(pfrom, NetMsgType::GETBLOCKTXN, req); + connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::GETBLOCKTXN, req)); } } } else { @@ -5888,7 +5889,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, // mempool will probably be useless - request the block normally std::vector<CInv> vInv(1); vInv[0] = CInv(MSG_BLOCK | GetFetchFlags(pfrom, pindex->pprev, chainparams.GetConsensus()), cmpctblock.header.GetHash()); - connman.PushMessage(pfrom, NetMsgType::GETDATA, vInv); + connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::GETDATA, vInv)); return true; } else { // If this was an announce-cmpctblock, we want the same treatment as a header message @@ -5930,7 +5931,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, // Might have collided, fall back to getdata now :( std::vector<CInv> invs; invs.push_back(CInv(MSG_BLOCK | GetFetchFlags(pfrom, chainActive.Tip(), chainparams.GetConsensus()), resp.blockhash)); - connman.PushMessage(pfrom, NetMsgType::GETDATA, invs); + connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::GETDATA, invs)); } else { // Block is either okay, or possibly we received // READ_STATUS_CHECKBLOCK_FAILED. @@ -6007,7 +6008,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, // nUnconnectingHeaders gets reset back to 0. if (mapBlockIndex.find(headers[0].hashPrevBlock) == mapBlockIndex.end() && nCount < MAX_BLOCKS_TO_ANNOUNCE) { nodestate->nUnconnectingHeaders++; - connman.PushMessage(pfrom, NetMsgType::GETHEADERS, chainActive.GetLocator(pindexBestHeader), uint256()); + connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::GETHEADERS, chainActive.GetLocator(pindexBestHeader), uint256())); LogPrint("net", "received header %s: missing prev block %s, sending getheaders (%d) to end (peer=%d, nUnconnectingHeaders=%d)\n", headers[0].GetHash().ToString(), headers[0].hashPrevBlock.ToString(), @@ -6062,7 +6063,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, // TODO: optimize: if pindexLast is an ancestor of chainActive.Tip or pindexBestHeader, continue // from there instead. LogPrint("net", "more getheaders (%d) to end to peer=%d (startheight:%d)\n", pindexLast->nHeight, pfrom->id, pfrom->nStartingHeight); - connman.PushMessage(pfrom, NetMsgType::GETHEADERS, chainActive.GetLocator(pindexLast), uint256()); + connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::GETHEADERS, chainActive.GetLocator(pindexLast), uint256())); } bool fCanDirectFetch = CanDirectFetch(chainparams.GetConsensus()); @@ -6115,7 +6116,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, // In any case, we want to download using a compact block, not a regular one vGetData[0] = CInv(MSG_CMPCT_BLOCK, vGetData[0].hash); } - connman.PushMessage(pfrom, NetMsgType::GETDATA, vGetData); + connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::GETDATA, vGetData)); } } } @@ -6217,7 +6218,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, // it, if the remote node sends a ping once per second and this node takes 5 // seconds to respond to each, the 5th ping the remote sends would appear to // return very quickly. - connman.PushMessage(pfrom, NetMsgType::PONG, nonce); + connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::PONG, nonce)); } } @@ -6473,7 +6474,7 @@ bool ProcessMessages(CNode* pfrom, CConnman& connman) } catch (const std::ios_base::failure& e) { - connman.PushMessageWithVersion(pfrom, INIT_PROTO_VERSION, NetMsgType::REJECT, strCommand, REJECT_MALFORMED, string("error parsing message")); + connman.PushMessage(pfrom, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::REJECT, strCommand, REJECT_MALFORMED, string("error parsing message"))); if (strstr(e.what(), "end of data")) { // Allow exceptions from under-length message on vRecv @@ -6538,9 +6539,12 @@ bool SendMessages(CNode* pto, CConnman& connman) const Consensus::Params& consensusParams = Params().GetConsensus(); { // Don't send anything until we get its version message - if (pto->nVersion == 0) + if (pto->nVersion == 0 || pto->fDisconnect) return true; + // If we get here, the outgoing message serialization version is set and can't change. + CNetMsgMaker msgMaker(pto->GetSendVersion()); + // // Message: ping // @@ -6553,7 +6557,7 @@ bool SendMessages(CNode* pto, CConnman& connman) // Ping automatically sent as a latency probe & keepalive. pingSend = true; } - if (pingSend && !pto->fDisconnect) { + if (pingSend) { uint64_t nonce = 0; while (nonce == 0) { GetRandBytes((unsigned char*)&nonce, sizeof(nonce)); @@ -6562,11 +6566,11 @@ bool SendMessages(CNode* pto, CConnman& connman) pto->nPingUsecStart = GetTimeMicros(); if (pto->nVersion > BIP0031_VERSION) { pto->nPingNonceSent = nonce; - connman.PushMessage(pto, NetMsgType::PING, nonce); + connman.PushMessage(pto, msgMaker.Make(NetMsgType::PING, nonce)); } else { // Peer is too old to support ping command with nonce, pong will never arrive. pto->nPingNonceSent = 0; - connman.PushMessage(pto, NetMsgType::PING); + connman.PushMessage(pto, msgMaker.Make(NetMsgType::PING)); } } @@ -6574,6 +6578,28 @@ bool SendMessages(CNode* pto, CConnman& connman) if (!lockMain) return true; + CNodeState &state = *State(pto->GetId()); + + BOOST_FOREACH(const CBlockReject& reject, state.rejects) + connman.PushMessage(pto, msgMaker.Make(NetMsgType::REJECT, (string)NetMsgType::BLOCK, reject.chRejectCode, reject.strRejectReason, reject.hashBlock)); + state.rejects.clear(); + + if (state.fShouldBan) { + state.fShouldBan = false; + if (pto->fWhitelisted) + LogPrintf("Warning: not punishing whitelisted peer %s!\n", pto->addr.ToString()); + else { + pto->fDisconnect = true; + if (pto->addr.IsLocal()) + LogPrintf("Warning: not banning local peer %s!\n", pto->addr.ToString()); + else + { + connman.Ban(pto->addr, BanReasonNodeMisbehaving); + } + return true; + } + } + // Address refresh broadcast int64_t nNow = GetTimeMicros(); if (!IsInitialBlockDownload() && pto->nNextLocalAddrSend < nNow) { @@ -6597,44 +6623,24 @@ bool SendMessages(CNode* pto, CConnman& connman) // receiver rejects addr messages larger than 1000 if (vAddr.size() >= 1000) { - connman.PushMessage(pto, NetMsgType::ADDR, vAddr); + connman.PushMessage(pto, msgMaker.Make(NetMsgType::ADDR, vAddr)); vAddr.clear(); } } } pto->vAddrToSend.clear(); if (!vAddr.empty()) - connman.PushMessage(pto, NetMsgType::ADDR, vAddr); + connman.PushMessage(pto, msgMaker.Make(NetMsgType::ADDR, vAddr)); // we only send the big addr message once if (pto->vAddrToSend.capacity() > 40) pto->vAddrToSend.shrink_to_fit(); } - CNodeState &state = *State(pto->GetId()); - if (state.fShouldBan) { - if (pto->fWhitelisted) - LogPrintf("Warning: not punishing whitelisted peer %s!\n", pto->addr.ToString()); - else { - pto->fDisconnect = true; - if (pto->addr.IsLocal()) - LogPrintf("Warning: not banning local peer %s!\n", pto->addr.ToString()); - else - { - connman.Ban(pto->addr, BanReasonNodeMisbehaving); - } - } - state.fShouldBan = false; - } - - BOOST_FOREACH(const CBlockReject& reject, state.rejects) - connman.PushMessage(pto, NetMsgType::REJECT, (string)NetMsgType::BLOCK, reject.chRejectCode, reject.strRejectReason, reject.hashBlock); - state.rejects.clear(); - // Start block sync if (pindexBestHeader == NULL) pindexBestHeader = chainActive.Tip(); bool fFetch = state.fPreferredDownload || (nPreferredDownload == 0 && !pto->fClient && !pto->fOneShot); // Download if this is a nice peer, or we have no nice peers and this one might do. - if (!state.fSyncStarted && !pto->fClient && !pto->fDisconnect && !fImporting && !fReindex) { + if (!state.fSyncStarted && !pto->fClient && !fImporting && !fReindex) { // Only actively request headers from a single peer, unless we're close to today. if ((nSyncStarted == 0 && fFetch) || pindexBestHeader->GetBlockTime() > GetAdjustedTime() - 24 * 60 * 60) { state.fSyncStarted = true; @@ -6650,7 +6656,7 @@ bool SendMessages(CNode* pto, CConnman& connman) if (pindexStart->pprev) pindexStart = pindexStart->pprev; LogPrint("net", "initial getheaders (%d) to peer=%d (startheight:%d)\n", pindexStart->nHeight, pto->id, pto->nStartingHeight); - connman.PushMessage(pto, NetMsgType::GETHEADERS, chainActive.GetLocator(pindexStart), uint256()); + connman.PushMessage(pto, msgMaker.Make(NetMsgType::GETHEADERS, chainActive.GetLocator(pindexStart), uint256())); } } @@ -6739,7 +6745,8 @@ bool SendMessages(CNode* pto, CConnman& connman) CBlock block; assert(ReadBlockFromDisk(block, pBestIndex, consensusParams)); CBlockHeaderAndShortTxIDs cmpctblock(block, state.fWantsCmpctWitness); - connman.PushMessageWithFlag(pto, state.fWantsCmpctWitness ? 0 : SERIALIZE_TRANSACTION_NO_WITNESS, NetMsgType::CMPCTBLOCK, cmpctblock); + int nSendFlags = state.fWantsCmpctWitness ? 0 : SERIALIZE_TRANSACTION_NO_WITNESS; + connman.PushMessage(pto, msgMaker.Make(nSendFlags, NetMsgType::CMPCTBLOCK, cmpctblock)); state.pindexBestHeaderSent = pBestIndex; } else if (state.fPreferHeaders) { if (vHeaders.size() > 1) { @@ -6751,7 +6758,7 @@ bool SendMessages(CNode* pto, CConnman& connman) LogPrint("net", "%s: sending header %s to peer=%d\n", __func__, vHeaders.front().GetHash().ToString(), pto->id); } - connman.PushMessage(pto, NetMsgType::HEADERS, vHeaders); + connman.PushMessage(pto, msgMaker.Make(NetMsgType::HEADERS, vHeaders)); state.pindexBestHeaderSent = pBestIndex; } else fRevertToInv = true; @@ -6797,7 +6804,7 @@ bool SendMessages(CNode* pto, CConnman& connman) BOOST_FOREACH(const uint256& hash, pto->vInventoryBlockToSend) { vInv.push_back(CInv(MSG_BLOCK, hash)); if (vInv.size() == MAX_INV_SZ) { - connman.PushMessage(pto, NetMsgType::INV, vInv); + connman.PushMessage(pto, msgMaker.Make(NetMsgType::INV, vInv)); vInv.clear(); } } @@ -6843,7 +6850,7 @@ bool SendMessages(CNode* pto, CConnman& connman) pto->filterInventoryKnown.insert(hash); vInv.push_back(inv); if (vInv.size() == MAX_INV_SZ) { - connman.PushMessage(pto, NetMsgType::INV, vInv); + connman.PushMessage(pto, msgMaker.Make(NetMsgType::INV, vInv)); vInv.clear(); } } @@ -6909,7 +6916,7 @@ bool SendMessages(CNode* pto, CConnman& connman) } } if (vInv.size() == MAX_INV_SZ) { - connman.PushMessage(pto, NetMsgType::INV, vInv); + connman.PushMessage(pto, msgMaker.Make(NetMsgType::INV, vInv)); vInv.clear(); } pto->filterInventoryKnown.insert(hash); @@ -6917,28 +6924,30 @@ bool SendMessages(CNode* pto, CConnman& connman) } } if (!vInv.empty()) - connman.PushMessage(pto, NetMsgType::INV, vInv); + connman.PushMessage(pto, msgMaker.Make(NetMsgType::INV, vInv)); // Detect whether we're stalling nNow = GetTimeMicros(); - if (!pto->fDisconnect && state.nStallingSince && state.nStallingSince < nNow - 1000000 * BLOCK_STALLING_TIMEOUT) { + if (state.nStallingSince && state.nStallingSince < nNow - 1000000 * BLOCK_STALLING_TIMEOUT) { // Stalling only triggers when the block download window cannot move. During normal steady state, // the download window should be much larger than the to-be-downloaded set of blocks, so disconnection // should only happen during initial block download. LogPrintf("Peer=%d is stalling block download, disconnecting\n", pto->id); pto->fDisconnect = true; + return true; } // In case there is a block that has been in flight from this peer for 2 + 0.5 * N times the block interval // (with N the number of peers from which we're downloading validated blocks), disconnect due to timeout. // We compensate for other peers to prevent killing off peers due to our own downstream link // being saturated. We only count validated in-flight blocks so peers can't advertise non-existing block hashes // to unreasonably increase our timeout. - if (!pto->fDisconnect && state.vBlocksInFlight.size() > 0) { + if (state.vBlocksInFlight.size() > 0) { QueuedBlock &queuedBlock = state.vBlocksInFlight.front(); int nOtherPeersWithValidatedDownloads = nPeersWithValidatedDownloads - (state.nBlocksInFlightValidHeaders > 0); if (nNow > state.nDownloadingSince + consensusParams.nPowTargetSpacing * (BLOCK_DOWNLOAD_TIMEOUT_BASE + BLOCK_DOWNLOAD_TIMEOUT_PER_PEER * nOtherPeersWithValidatedDownloads)) { LogPrintf("Timeout downloading block %s from peer=%d, disconnecting\n", queuedBlock.hash.ToString(), pto->id); pto->fDisconnect = true; + return true; } } @@ -6946,7 +6955,7 @@ bool SendMessages(CNode* pto, CConnman& connman) // Message: getdata (blocks) // vector<CInv> vGetData; - if (!pto->fDisconnect && !pto->fClient && (fFetch || !IsInitialBlockDownload()) && state.nBlocksInFlight < MAX_BLOCKS_IN_TRANSIT_PER_PEER) { + if (!pto->fClient && (fFetch || !IsInitialBlockDownload()) && state.nBlocksInFlight < MAX_BLOCKS_IN_TRANSIT_PER_PEER) { vector<CBlockIndex*> vToDownload; NodeId staller = -1; FindNextBlocksToDownload(pto->GetId(), MAX_BLOCKS_IN_TRANSIT_PER_PEER - state.nBlocksInFlight, vToDownload, staller, consensusParams); @@ -6968,7 +6977,7 @@ bool SendMessages(CNode* pto, CConnman& connman) // // Message: getdata (non-blocks) // - while (!pto->fDisconnect && !pto->mapAskFor.empty() && (*pto->mapAskFor.begin()).first <= nNow) + while (!pto->mapAskFor.empty() && (*pto->mapAskFor.begin()).first <= nNow) { const CInv& inv = (*pto->mapAskFor.begin()).second; if (!AlreadyHave(inv)) @@ -6978,7 +6987,7 @@ bool SendMessages(CNode* pto, CConnman& connman) vGetData.push_back(inv); if (vGetData.size() >= 1000) { - connman.PushMessage(pto, NetMsgType::GETDATA, vGetData); + connman.PushMessage(pto, msgMaker.Make(NetMsgType::GETDATA, vGetData)); vGetData.clear(); } } else { @@ -6988,20 +6997,20 @@ bool SendMessages(CNode* pto, CConnman& connman) pto->mapAskFor.erase(pto->mapAskFor.begin()); } if (!vGetData.empty()) - connman.PushMessage(pto, NetMsgType::GETDATA, vGetData); + connman.PushMessage(pto, msgMaker.Make(NetMsgType::GETDATA, vGetData)); // // Message: feefilter // // We don't want white listed peers to filter txs to us if we have -whitelistforcerelay - if (!pto->fDisconnect && pto->nVersion >= FEEFILTER_VERSION && GetBoolArg("-feefilter", DEFAULT_FEEFILTER) && + if (pto->nVersion >= FEEFILTER_VERSION && GetBoolArg("-feefilter", DEFAULT_FEEFILTER) && !(pto->fWhitelisted && GetBoolArg("-whitelistforcerelay", DEFAULT_WHITELISTFORCERELAY))) { CAmount currentFilter = mempool.GetMinFee(GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000).GetFeePerK(); int64_t timeNow = GetTimeMicros(); if (timeNow > pto->nextSendTimeFeeFilter) { CAmount filterToSend = filterRounder.round(currentFilter); if (filterToSend != pto->lastSentFeeFilter) { - connman.PushMessage(pto, NetMsgType::FEEFILTER, filterToSend); + connman.PushMessage(pto, msgMaker.Make(NetMsgType::FEEFILTER, filterToSend)); pto->lastSentFeeFilter = filterToSend; } pto->nextSendTimeFeeFilter = PoissonNextSend(timeNow, AVG_FEEFILTER_BROADCAST_INTERVAL); diff --git a/src/main.h b/src/main.h index 8f51e33242..6b2668e568 100644 --- a/src/main.h +++ b/src/main.h @@ -165,7 +165,7 @@ extern uint64_t nLastBlockWeight; extern const std::string strMessageMagic; extern CWaitableCriticalSection csBestBlock; extern CConditionVariable cvBlockChange; -extern bool fImporting; +extern std::atomic_bool fImporting; extern bool fReindex; extern int nScriptCheckThreads; extern bool fTxIndex; diff --git a/src/net.cpp b/src/net.cpp index 15c4514f15..27b200b3f0 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -74,7 +74,6 @@ bool fRelayTxes = true; CCriticalSection cs_mapLocalHost; std::map<CNetAddr, LocalServiceInfo> mapLocalHost; static bool vfLimited[NET_MAX] = {}; -static CNode* pnodeLocalHost = NULL; std::string strSubVersion; limitedmap<uint256, int64_t> mapAlreadyAskedFor(MAX_INV_SZ); @@ -768,13 +767,13 @@ const uint256& CNetMessage::GetMessageHash() const // requires LOCK(cs_vSend) size_t SocketSendData(CNode *pnode) { - std::deque<CSerializeData>::iterator it = pnode->vSendMsg.begin(); + auto it = pnode->vSendMsg.begin(); size_t nSentSize = 0; while (it != pnode->vSendMsg.end()) { - const CSerializeData &data = *it; + const auto &data = *it; assert(data.size() > pnode->nSendOffset); - int nBytes = send(pnode->hSocket, &data[pnode->nSendOffset], data.size() - pnode->nSendOffset, MSG_NOSIGNAL | MSG_DONTWAIT); + int nBytes = send(pnode->hSocket, reinterpret_cast<const char*>(data.data()) + pnode->nSendOffset, data.size() - pnode->nSendOffset, MSG_NOSIGNAL | MSG_DONTWAIT); if (nBytes > 0) { pnode->nLastSend = GetTime(); pnode->nSendBytes += nBytes; @@ -1067,8 +1066,7 @@ void CConnman::ThreadSocketHandler() pnode->CloseSocketDisconnect(); // hold in disconnected pool until all refs are released - if (pnode->fNetworkNode || pnode->fInbound) - pnode->Release(); + pnode->Release(); vNodesDisconnected.push_back(pnode); } } @@ -1103,8 +1101,13 @@ void CConnman::ThreadSocketHandler() } } } - if(vNodes.size() != nPrevNodeCount) { - nPrevNodeCount = vNodes.size(); + size_t vNodesSize; + { + LOCK(cs_vNodes); + vNodesSize = vNodes.size(); + } + if(vNodesSize != nPrevNodeCount) { + nPrevNodeCount = vNodesSize; if(clientInterface) clientInterface->NotifyNumConnectionsChanged(nPrevNodeCount); } @@ -1808,7 +1811,6 @@ bool CConnman::OpenNetworkConnection(const CAddress& addrConnect, bool fCountFai return false; if (grantOutbound) grantOutbound->MoveTo(pnode->grantOutbound); - pnode->fNetworkNode = true; if (fOneShot) pnode->fOneShot = true; if (fFeeler) @@ -2139,17 +2141,6 @@ bool CConnman::Start(boost::thread_group& threadGroup, CScheduler& scheduler, st semOutbound = new CSemaphore(std::min((nMaxOutbound + nMaxFeeler), nMaxConnections)); } - if (pnodeLocalHost == NULL) { - CNetAddr local; - LookupHost("127.0.0.1", local, false); - - NodeId id = GetNewNodeId(); - uint64_t nonce = GetDeterministicRandomizer(RANDOMIZER_ID_LOCALHOSTNONCE).Write(id).Finalize(); - - pnodeLocalHost = new CNode(id, nLocalServices, GetBestHeight(), INVALID_SOCKET, CAddress(CService(local, 0), nLocalServices), 0, nonce); - GetNodeSignals().InitializeNode(pnodeLocalHost, *this); - } - // // Start threads // @@ -2227,9 +2218,6 @@ void CConnman::Stop() vhListenSocket.clear(); delete semOutbound; semOutbound = NULL; - if(pnodeLocalHost) - DeleteNode(pnodeLocalHost); - pnodeLocalHost = NULL; } void CConnman::DeleteNode(CNode* pnode) @@ -2531,7 +2519,6 @@ CNode::CNode(NodeId idIn, ServiceFlags nLocalServicesIn, int nMyStartingHeightIn fOneShot = false; fClient = false; // set by version message fFeeler = false; - fNetworkNode = false; fSuccessfullyConnected = false; fDisconnect = false; nRefCount = 0; @@ -2612,30 +2599,19 @@ void CNode::AskFor(const CInv& inv) mapAskFor.insert(std::make_pair(nRequestTime, inv)); } -CDataStream CConnman::BeginMessage(CNode* pnode, int nVersion, int flags, const std::string& sCommand) +void CConnman::PushMessage(CNode* pnode, CSerializedNetMsg&& msg) { - return {SER_NETWORK, (nVersion ? nVersion : pnode->GetSendVersion()) | flags, CMessageHeader(Params().MessageStart(), sCommand.c_str(), 0) }; -} - -void CConnman::EndMessage(CDataStream& strm) -{ - // Set the size - assert(strm.size () >= CMessageHeader::HEADER_SIZE); - unsigned int nSize = strm.size() - CMessageHeader::HEADER_SIZE; - WriteLE32((uint8_t*)&strm[CMessageHeader::MESSAGE_SIZE_OFFSET], nSize); - // Set the checksum - uint256 hash = Hash(strm.begin() + CMessageHeader::HEADER_SIZE, strm.end()); - memcpy((char*)&strm[CMessageHeader::CHECKSUM_OFFSET], hash.begin(), CMessageHeader::CHECKSUM_SIZE); - -} + size_t nMessageSize = msg.data.size(); + size_t nTotalSize = nMessageSize + CMessageHeader::HEADER_SIZE; + LogPrint("net", "sending %s (%d bytes) peer=%d\n", SanitizeString(msg.command.c_str()), nMessageSize, pnode->id); -void CConnman::PushMessage(CNode* pnode, CDataStream& strm, const std::string& sCommand) -{ - if(strm.empty()) - return; + std::vector<unsigned char> serializedHeader; + serializedHeader.reserve(CMessageHeader::HEADER_SIZE); + uint256 hash = Hash(msg.data.data(), msg.data.data() + nMessageSize); + CMessageHeader hdr(Params().MessageStart(), msg.command.c_str(), nMessageSize); + memcpy(hdr.pchChecksum, hash.begin(), CMessageHeader::CHECKSUM_SIZE); - unsigned int nSize = strm.size() - CMessageHeader::HEADER_SIZE; - LogPrint("net", "sending %s (%d bytes) peer=%d\n", SanitizeString(sCommand.c_str()), nSize, pnode->id); + CVectorWriter{SER_NETWORK, INIT_PROTO_VERSION, serializedHeader, 0, hdr}; size_t nBytesSent = 0; { @@ -2644,11 +2620,14 @@ void CConnman::PushMessage(CNode* pnode, CDataStream& strm, const std::string& s return; } bool optimisticSend(pnode->vSendMsg.empty()); - pnode->vSendMsg.emplace_back(strm.begin(), strm.end()); //log total amount of bytes per command - pnode->mapSendBytesPerMsgCmd[sCommand] += strm.size(); - pnode->nSendSize += strm.size(); + pnode->mapSendBytesPerMsgCmd[msg.command] += nTotalSize; + pnode->nSendSize += nTotalSize; + + pnode->vSendMsg.push_back(std::move(serializedHeader)); + if (nMessageSize) + pnode->vSendMsg.push_back(std::move(msg.data)); // If write queue empty, attempt "optimistic write" if (optimisticSend == true) @@ -101,6 +101,20 @@ class CTransaction; class CNodeStats; class CClientUIInterface; +struct CSerializedNetMsg +{ + CSerializedNetMsg() = default; + CSerializedNetMsg(CSerializedNetMsg&&) = default; + CSerializedNetMsg& operator=(CSerializedNetMsg&&) = default; + // No copying, only moves. + CSerializedNetMsg(const CSerializedNetMsg& msg) = delete; + CSerializedNetMsg& operator=(const CSerializedNetMsg&) = delete; + + std::vector<unsigned char> data; + std::string command; +}; + + class CConnman { public: @@ -138,32 +152,7 @@ public: bool ForNode(NodeId id, std::function<bool(CNode* pnode)> func); - template <typename... Args> - void PushMessageWithVersionAndFlag(CNode* pnode, int nVersion, int flag, const std::string& sCommand, Args&&... args) - { - auto msg(BeginMessage(pnode, nVersion, flag, sCommand)); - ::SerializeMany(msg, std::forward<Args>(args)...); - EndMessage(msg); - PushMessage(pnode, msg, sCommand); - } - - template <typename... Args> - void PushMessageWithFlag(CNode* pnode, int flag, const std::string& sCommand, Args&&... args) - { - PushMessageWithVersionAndFlag(pnode, 0, flag, sCommand, std::forward<Args>(args)...); - } - - template <typename... Args> - void PushMessageWithVersion(CNode* pnode, int nVersion, const std::string& sCommand, Args&&... args) - { - PushMessageWithVersionAndFlag(pnode, nVersion, 0, sCommand, std::forward<Args>(args)...); - } - - template <typename... Args> - void PushMessage(CNode* pnode, const std::string& sCommand, Args&&... args) - { - PushMessageWithVersionAndFlag(pnode, 0, 0, sCommand, std::forward<Args>(args)...); - } + void PushMessage(CNode* pnode, CSerializedNetMsg&& msg); template<typename Callable> bool ForEachNodeContinueIf(Callable&& func) @@ -374,10 +363,6 @@ private: unsigned int GetReceiveFloodSize() const; - CDataStream BeginMessage(CNode* node, int nVersion, int flags, const std::string& sCommand); - void PushMessage(CNode* pnode, CDataStream& strm, const std::string& sCommand); - void EndMessage(CDataStream& strm); - // Network stats void RecordBytesRecv(uint64_t bytes); void RecordBytesSent(uint64_t bytes); @@ -601,7 +586,7 @@ public: size_t nSendSize; // total size of all vSendMsg entries size_t nSendOffset; // offset inside the first vSendMsg already sent uint64_t nSendBytes; - std::deque<CSerializeData> vSendMsg; + std::deque<std::vector<unsigned char>> vSendMsg; CCriticalSection cs_vSend; std::deque<CInv> vRecvGetData; @@ -628,9 +613,8 @@ public: bool fOneShot; bool fClient; const bool fInbound; - bool fNetworkNode; bool fSuccessfullyConnected; - bool fDisconnect; + std::atomic_bool fDisconnect; // We use fRelayTxes for two purposes - // a) it allows us to not relay tx invs before receiving the peer's version message // b) the peer may tell us in its version message that we should not relay tx invs @@ -771,7 +755,7 @@ public: { // The send version should always be explicitly set to // INIT_PROTO_VERSION rather than using this value until the handshake - // is complete. See PushMessageWithVersion(). + // is complete. assert(nSendVersion != 0); return nSendVersion; } diff --git a/src/netmessagemaker.h b/src/netmessagemaker.h new file mode 100644 index 0000000000..7167434a19 --- /dev/null +++ b/src/netmessagemaker.h @@ -0,0 +1,36 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2016 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_NETMESSAGEMAKER_H +#define BITCOIN_NETMESSAGEMAKER_H + +#include "net.h" +#include "serialize.h" + +class CNetMsgMaker +{ +public: + CNetMsgMaker(int nVersionIn) : nVersion(nVersionIn){} + + template <typename... Args> + CSerializedNetMsg Make(int nFlags, std::string sCommand, Args&&... args) + { + CSerializedNetMsg msg; + msg.command = std::move(sCommand); + CVectorWriter{ SER_NETWORK, nFlags | nVersion, msg.data, 0, std::forward<Args>(args)... }; + return msg; + } + + template <typename... Args> + CSerializedNetMsg Make(std::string sCommand, Args&&... args) + { + return Make(0, std::move(sCommand), std::forward<Args>(args)...); + } + +private: + const int nVersion; +}; + +#endif // BITCOIN_NETMESSAGEMAKER_H diff --git a/src/qt/addressbookpage.cpp b/src/qt/addressbookpage.cpp index 58cf4dede0..6076837fde 100644 --- a/src/qt/addressbookpage.cpp +++ b/src/qt/addressbookpage.cpp @@ -83,7 +83,7 @@ AddressBookPage::AddressBookPage(const PlatformStyle *platformStyle, Mode _mode, deleteAction = new QAction(ui->deleteAddress->text(), this); // Build context menu - contextMenu = new QMenu(); + contextMenu = new QMenu(this); contextMenu->addAction(copyAddressAction); contextMenu->addAction(copyLabelAction); contextMenu->addAction(editAction); diff --git a/src/qt/bantablemodel.cpp b/src/qt/bantablemodel.cpp index 6e11e23904..fe53d89ba9 100644 --- a/src/qt/bantablemodel.cpp +++ b/src/qt/bantablemodel.cpp @@ -87,7 +87,7 @@ BanTableModel::BanTableModel(ClientModel *parent) : clientModel(parent) { columns << tr("IP/Netmask") << tr("Banned Until"); - priv = new BanTablePriv(); + priv.reset(new BanTablePriv()); // default to unsorted priv->sortColumn = -1; @@ -95,6 +95,11 @@ BanTableModel::BanTableModel(ClientModel *parent) : refresh(); } +BanTableModel::~BanTableModel() +{ + // Intentionally left empty +} + int BanTableModel::rowCount(const QModelIndex &parent) const { Q_UNUSED(parent); diff --git a/src/qt/bantablemodel.h b/src/qt/bantablemodel.h index fe9600ac0b..3c03d05c05 100644 --- a/src/qt/bantablemodel.h +++ b/src/qt/bantablemodel.h @@ -40,6 +40,7 @@ class BanTableModel : public QAbstractTableModel public: explicit BanTableModel(ClientModel *parent = 0); + ~BanTableModel(); void startAutoRefresh(); void stopAutoRefresh(); @@ -66,7 +67,7 @@ public Q_SLOTS: private: ClientModel *clientModel; QStringList columns; - BanTablePriv *priv; + std::unique_ptr<BanTablePriv> priv; }; #endif // BITCOIN_QT_BANTABLEMODEL_H diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index c828234f44..4f48e21a22 100644 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -245,6 +245,7 @@ private: #endif int returnValue; const PlatformStyle *platformStyle; + std::unique_ptr<QWidget> shutdownWindow; void startThread(); }; @@ -267,7 +268,22 @@ void BitcoinCore::initialize() try { qDebug() << __func__ << ": Running AppInit2 in thread"; - int rv = AppInit2(threadGroup, scheduler); + if (!AppInitBasicSetup()) + { + Q_EMIT initializeResult(false); + return; + } + if (!AppInitParameterInteraction()) + { + Q_EMIT initializeResult(false); + return; + } + if (!AppInitSanityChecks()) + { + Q_EMIT initializeResult(false); + return; + } + int rv = AppInitMain(threadGroup, scheduler); Q_EMIT initializeResult(rv); } catch (const std::exception& e) { handleRunawayException(&e); @@ -365,9 +381,8 @@ void BitcoinApplication::createWindow(const NetworkStyle *networkStyle) void BitcoinApplication::createSplashScreen(const NetworkStyle *networkStyle) { SplashScreen *splash = new SplashScreen(0, networkStyle); - // We don't hold a direct pointer to the splash screen after creation, so use - // Qt::WA_DeleteOnClose to make sure that the window will be deleted eventually. - splash->setAttribute(Qt::WA_DeleteOnClose); + // We don't hold a direct pointer to the splash screen after creation, but the splash + // screen will take care of deleting itself when slotFinish happens. splash->show(); connect(this, SIGNAL(splashFinished(QWidget*)), splash, SLOT(slotFinish(QWidget*))); connect(this, SIGNAL(requestedShutdown()), splash, SLOT(close())); @@ -409,6 +424,11 @@ void BitcoinApplication::requestInitialize() void BitcoinApplication::requestShutdown() { + // Show a simple window indicating shutdown status + // Do this first as some of the steps may take some time below, + // for example the RPC console may still be executing a command. + shutdownWindow.reset(ShutdownWindow::showShutdownWindow(window)); + qDebug() << __func__ << ": Requesting shutdown"; startThread(); window->hide(); @@ -423,9 +443,6 @@ void BitcoinApplication::requestShutdown() delete clientModel; clientModel = 0; - // Show a simple window indicating shutdown status - ShutdownWindow::showShutdownWindow(window); - // Request shutdown from core thread Q_EMIT requestedShutdown(); } diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index b7302e3969..6112a1d256 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -511,6 +511,13 @@ void BitcoinGUI::setClientModel(ClientModel *_clientModel) // Disable context menu on tray icon trayIconMenu->clear(); } + // Propagate cleared model to child objects + rpcConsole->setClientModel(nullptr); +#ifdef ENABLE_WALLET + walletFrame->setClientModel(nullptr); +#endif // ENABLE_WALLET + unitDisplayControl->setOptionsModel(nullptr); + connectionsControl->setClientModel(nullptr); } } @@ -1191,7 +1198,7 @@ void UnitDisplayStatusBarControl::mousePressEvent(QMouseEvent *event) /** Creates context menu, its actions, and wires up all the relevant signals for mouse events. */ void UnitDisplayStatusBarControl::createContextMenu() { - menu = new QMenu(); + menu = new QMenu(this); Q_FOREACH(BitcoinUnits::Unit u, BitcoinUnits::availableUnits()) { QAction *menuAction = new QAction(QString(BitcoinUnits::name(u)), this); @@ -1248,7 +1255,5 @@ void NetworkToggleStatusBarControl::mousePressEvent(QMouseEvent *event) /** Lets the control know about the Client Model */ void NetworkToggleStatusBarControl::setClientModel(ClientModel *_clientModel) { - if (_clientModel) { - this->clientModel = _clientModel; - } + this->clientModel = _clientModel; } diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp index 1010a62fc4..d77db39b3c 100644 --- a/src/qt/coincontroldialog.cpp +++ b/src/qt/coincontroldialog.cpp @@ -59,7 +59,7 @@ CoinControlDialog::CoinControlDialog(const PlatformStyle *_platformStyle, QWidge unlockAction = new QAction(tr("Unlock unspent"), this); // we need to enable/disable this // context menu - contextMenu = new QMenu(); + contextMenu = new QMenu(this); contextMenu->addAction(copyAddressAction); contextMenu->addAction(copyLabelAction); contextMenu->addAction(copyAmountAction); diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp index 9dc75c2e1a..130cfc6e7d 100644 --- a/src/qt/guiutil.cpp +++ b/src/qt/guiutil.cpp @@ -585,7 +585,8 @@ void TableViewLastColumnResizingFixer::on_geometriesChanged() * Initializes all internal variables and prepares the * the resize modes of the last 2 columns of the table and */ -TableViewLastColumnResizingFixer::TableViewLastColumnResizingFixer(QTableView* table, int lastColMinimumWidth, int allColsMinimumWidth) : +TableViewLastColumnResizingFixer::TableViewLastColumnResizingFixer(QTableView* table, int lastColMinimumWidth, int allColsMinimumWidth, QObject *parent) : + QObject(parent), tableView(table), lastColumnMinimumWidth(lastColMinimumWidth), allColumnsMinimumWidth(allColsMinimumWidth) diff --git a/src/qt/guiutil.h b/src/qt/guiutil.h index 64cbd51eb6..8f1f3fbb2c 100644 --- a/src/qt/guiutil.h +++ b/src/qt/guiutil.h @@ -149,7 +149,7 @@ namespace GUIUtil Q_OBJECT public: - TableViewLastColumnResizingFixer(QTableView* table, int lastColMinimumWidth, int allColsMinimumWidth); + TableViewLastColumnResizingFixer(QTableView* table, int lastColMinimumWidth, int allColsMinimumWidth, QObject *parent); void stretchColumnWidth(int column); private: diff --git a/src/qt/optionsmodel.h b/src/qt/optionsmodel.h index b23b5f2607..d2e1fb23a3 100644 --- a/src/qt/optionsmodel.h +++ b/src/qt/optionsmodel.h @@ -81,7 +81,7 @@ private: int nDisplayUnit; QString strThirdPartyTxUrls; bool fCoinControlFeatures; - /* settings that were overriden by command-line */ + /* settings that were overridden by command-line */ QString strOverriddenByCommandLine; // Add option to list of GUI options overridden through command line/config file diff --git a/src/qt/overviewpage.cpp b/src/qt/overviewpage.cpp index 7ccdb89c0c..ea1bb2fc94 100644 --- a/src/qt/overviewpage.cpp +++ b/src/qt/overviewpage.cpp @@ -25,8 +25,8 @@ class TxViewDelegate : public QAbstractItemDelegate { Q_OBJECT public: - TxViewDelegate(const PlatformStyle *_platformStyle): - QAbstractItemDelegate(), unit(BitcoinUnits::BTC), + TxViewDelegate(const PlatformStyle *_platformStyle, QObject *parent=nullptr): + QAbstractItemDelegate(parent), unit(BitcoinUnits::BTC), platformStyle(_platformStyle) { @@ -119,8 +119,7 @@ OverviewPage::OverviewPage(const PlatformStyle *platformStyle, QWidget *parent) currentWatchOnlyBalance(-1), currentWatchUnconfBalance(-1), currentWatchImmatureBalance(-1), - txdelegate(new TxViewDelegate(platformStyle)), - filter(0) + txdelegate(new TxViewDelegate(platformStyle, this)) { ui->setupUi(this); @@ -220,7 +219,7 @@ void OverviewPage::setWalletModel(WalletModel *model) if(model && model->getOptionsModel()) { // Set up transaction list - filter = new TransactionFilterProxy(); + filter.reset(new TransactionFilterProxy()); filter->setSourceModel(model->getTransactionTableModel()); filter->setLimit(NUM_ITEMS); filter->setDynamicSortFilter(true); @@ -228,7 +227,7 @@ void OverviewPage::setWalletModel(WalletModel *model) filter->setShowInactive(false); filter->sort(TransactionTableModel::Date, Qt::DescendingOrder); - ui->listTransactions->setModel(filter); + ui->listTransactions->setModel(filter.get()); ui->listTransactions->setModelColumn(TransactionTableModel::ToAddress); // Keep up to date with wallet diff --git a/src/qt/overviewpage.h b/src/qt/overviewpage.h index 65cd3341b6..ffe0de328c 100644 --- a/src/qt/overviewpage.h +++ b/src/qt/overviewpage.h @@ -8,6 +8,7 @@ #include "amount.h" #include <QWidget> +#include <memory> class ClientModel; class TransactionFilterProxy; @@ -56,7 +57,7 @@ private: CAmount currentWatchImmatureBalance; TxViewDelegate *txdelegate; - TransactionFilterProxy *filter; + std::unique_ptr<TransactionFilterProxy> filter; private Q_SLOTS: void updateDisplayUnit(); diff --git a/src/qt/paymentserver.cpp b/src/qt/paymentserver.cpp index 478f5ccf12..229752cad2 100644 --- a/src/qt/paymentserver.cpp +++ b/src/qt/paymentserver.cpp @@ -58,14 +58,19 @@ const char* BIP71_MIMETYPE_PAYMENTREQUEST = "application/bitcoin-paymentrequest" // BIP70 max payment request size in bytes (DoS protection) const qint64 BIP70_MAX_PAYMENTREQUEST_SIZE = 50000; -X509_STORE* PaymentServer::certStore = NULL; -void PaymentServer::freeCertStore() +struct X509StoreDeleter { + void operator()(X509_STORE* b) { + X509_STORE_free(b); + } +}; + +struct X509Deleter { + void operator()(X509* b) { X509_free(b); } +}; + +namespace // Anon namespace { - if (PaymentServer::certStore != NULL) - { - X509_STORE_free(PaymentServer::certStore); - PaymentServer::certStore = NULL; - } + std::unique_ptr<X509_STORE, X509StoreDeleter> certStore; } // @@ -107,20 +112,15 @@ static void ReportInvalidCertificate(const QSslCertificate& cert) // void PaymentServer::LoadRootCAs(X509_STORE* _store) { - if (PaymentServer::certStore == NULL) - atexit(PaymentServer::freeCertStore); - else - freeCertStore(); - // Unit tests mostly use this, to pass in fake root CAs: if (_store) { - PaymentServer::certStore = _store; + certStore.reset(_store); return; } // Normal execution, use either -rootcertificates or system certs: - PaymentServer::certStore = X509_STORE_new(); + certStore.reset(X509_STORE_new()); // Note: use "-system-" default here so that users can pass -rootcertificates="" // and get 'I don't like X.509 certificates, don't trust anybody' behavior: @@ -167,11 +167,11 @@ void PaymentServer::LoadRootCAs(X509_STORE* _store) QByteArray certData = cert.toDer(); const unsigned char *data = (const unsigned char *)certData.data(); - X509* x509 = d2i_X509(0, &data, certData.size()); - if (x509 && X509_STORE_add_cert(PaymentServer::certStore, x509)) + std::unique_ptr<X509, X509Deleter> x509(d2i_X509(0, &data, certData.size())); + if (x509 && X509_STORE_add_cert(certStore.get(), x509.get())) { - // Note: X509_STORE_free will free the X509* objects when - // the PaymentServer is destroyed + // Note: X509_STORE increases the reference count to the X509 object, + // we still have to release our reference to it. ++nRootCerts; } else @@ -550,7 +550,7 @@ bool PaymentServer::processPaymentRequest(const PaymentRequestPlus& request, Sen recipient.paymentRequest = request; recipient.message = GUIUtil::HtmlEscape(request.getDetails().memo()); - request.getMerchant(PaymentServer::certStore, recipient.authenticatedMerchant); + request.getMerchant(certStore.get(), recipient.authenticatedMerchant); QList<std::pair<CScript, CAmount> > sendingTos = request.getPayTo(); QStringList addresses; @@ -807,3 +807,8 @@ bool PaymentServer::verifyAmount(const CAmount& requestAmount) } return fVerified; } + +X509_STORE* PaymentServer::getCertStore() +{ + return certStore.get(); +} diff --git a/src/qt/paymentserver.h b/src/qt/paymentserver.h index 2d27ed078b..7202e7dada 100644 --- a/src/qt/paymentserver.h +++ b/src/qt/paymentserver.h @@ -83,7 +83,7 @@ public: static void LoadRootCAs(X509_STORE* store = NULL); // Return certificate store - static X509_STORE* getCertStore() { return certStore; } + static X509_STORE* getCertStore(); // OptionsModel is used for getting proxy settings and display unit void setOptionsModel(OptionsModel *optionsModel); @@ -140,9 +140,6 @@ private: bool saveURIs; // true during startup QLocalServer* uriServer; - static X509_STORE* certStore; // Trusted root certificates - static void freeCertStore(); - QNetworkAccessManager* netManager; // Used to fetch payment requests OptionsModel *optionsModel; diff --git a/src/qt/peertablemodel.cpp b/src/qt/peertablemodel.cpp index a2f9471fcc..c0b4900285 100644 --- a/src/qt/peertablemodel.cpp +++ b/src/qt/peertablemodel.cpp @@ -114,12 +114,12 @@ PeerTableModel::PeerTableModel(ClientModel *parent) : timer(0) { columns << tr("NodeId") << tr("Node/Service") << tr("User Agent") << tr("Ping"); - priv = new PeerTablePriv(); + priv.reset(new PeerTablePriv()); // default to unsorted priv->sortColumn = -1; // set up timer for auto refresh - timer = new QTimer(); + timer = new QTimer(this); connect(timer, SIGNAL(timeout()), SLOT(refresh())); timer->setInterval(MODEL_UPDATE_DELAY); @@ -127,6 +127,11 @@ PeerTableModel::PeerTableModel(ClientModel *parent) : refresh(); } +PeerTableModel::~PeerTableModel() +{ + // Intentionally left empty +} + void PeerTableModel::startAutoRefresh() { timer->start(); diff --git a/src/qt/peertablemodel.h b/src/qt/peertablemodel.h index a4f7bbdb3d..af34b147b1 100644 --- a/src/qt/peertablemodel.h +++ b/src/qt/peertablemodel.h @@ -46,6 +46,7 @@ class PeerTableModel : public QAbstractTableModel public: explicit PeerTableModel(ClientModel *parent = 0); + ~PeerTableModel(); const CNodeCombinedStats *getNodeStats(int idx); int getRowByNodeId(NodeId nodeid); void startAutoRefresh(); @@ -75,7 +76,7 @@ public Q_SLOTS: private: ClientModel *clientModel; QStringList columns; - PeerTablePriv *priv; + std::unique_ptr<PeerTablePriv> priv; QTimer *timer; }; diff --git a/src/qt/receivecoinsdialog.cpp b/src/qt/receivecoinsdialog.cpp index b50cad4975..dd83547a91 100644 --- a/src/qt/receivecoinsdialog.cpp +++ b/src/qt/receivecoinsdialog.cpp @@ -25,6 +25,7 @@ ReceiveCoinsDialog::ReceiveCoinsDialog(const PlatformStyle *_platformStyle, QWidget *parent) : QDialog(parent), ui(new Ui::ReceiveCoinsDialog), + columnResizingFixer(0), model(0), platformStyle(_platformStyle) { @@ -49,7 +50,7 @@ ReceiveCoinsDialog::ReceiveCoinsDialog(const PlatformStyle *_platformStyle, QWid QAction *copyAmountAction = new QAction(tr("Copy amount"), this); // context menu - contextMenu = new QMenu(); + contextMenu = new QMenu(this); contextMenu->addAction(copyURIAction); contextMenu->addAction(copyLabelAction); contextMenu->addAction(copyMessageAction); @@ -91,7 +92,7 @@ void ReceiveCoinsDialog::setModel(WalletModel *_model) SIGNAL(selectionChanged(QItemSelection, QItemSelection)), this, SLOT(recentRequestsView_selectionChanged(QItemSelection, QItemSelection))); // Last 2 columns are set by the columnResizingFixer, when the table geometry is ready. - columnResizingFixer = new GUIUtil::TableViewLastColumnResizingFixer(tableView, AMOUNT_MINIMUM_COLUMN_WIDTH, DATE_COLUMN_WIDTH); + columnResizingFixer = new GUIUtil::TableViewLastColumnResizingFixer(tableView, AMOUNT_MINIMUM_COLUMN_WIDTH, DATE_COLUMN_WIDTH, this); } } diff --git a/src/qt/receiverequestdialog.cpp b/src/qt/receiverequestdialog.cpp index 998c9176d7..f896461735 100644 --- a/src/qt/receiverequestdialog.cpp +++ b/src/qt/receiverequestdialog.cpp @@ -32,7 +32,7 @@ QRImageWidget::QRImageWidget(QWidget *parent): QLabel(parent), contextMenu(0) { - contextMenu = new QMenu(); + contextMenu = new QMenu(this); QAction *saveImageAction = new QAction(tr("&Save Image..."), this); connect(saveImageAction, SIGNAL(triggered()), this, SLOT(saveImage())); contextMenu->addAction(saveImageAction); diff --git a/src/qt/recentrequeststablemodel.cpp b/src/qt/recentrequeststablemodel.cpp index 2335d6b282..35d37bb22b 100644 --- a/src/qt/recentrequeststablemodel.cpp +++ b/src/qt/recentrequeststablemodel.cpp @@ -14,7 +14,7 @@ #include <boost/foreach.hpp> RecentRequestsTableModel::RecentRequestsTableModel(CWallet *wallet, WalletModel *parent) : - walletModel(parent) + QAbstractTableModel(parent), walletModel(parent) { Q_UNUSED(wallet); nReceiveRequestsMaxId = 0; diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp index 47af6a5724..520d229901 100644 --- a/src/qt/rpcconsole.cpp +++ b/src/qt/rpcconsole.cpp @@ -382,7 +382,6 @@ RPCConsole::RPCConsole(const PlatformStyle *_platformStyle, QWidget *parent) : // based timer interface RPCSetTimerInterfaceIfUnset(rpcTimerInterface); - startExecutor(); setTrafficGraphRange(INITIAL_TRAFFIC_GRAPH_MINS); ui->detailWidget->hide(); @@ -396,7 +395,6 @@ RPCConsole::RPCConsole(const PlatformStyle *_platformStyle, QWidget *parent) : RPCConsole::~RPCConsole() { GUIUtil::saveWindowGeometry("nRPCConsoleWindow", this); - Q_EMIT stopExecutor(); RPCUnsetTimerInterface(rpcTimerInterface); delete rpcTimerInterface; delete ui; @@ -486,7 +484,7 @@ void RPCConsole::setClientModel(ClientModel *model) QAction* banAction365d = new QAction(tr("Ban for") + " " + tr("1 &year"), this); // create peer table context menu - peersTableContextMenu = new QMenu(); + peersTableContextMenu = new QMenu(this); peersTableContextMenu->addAction(disconnectAction); peersTableContextMenu->addAction(banAction1h); peersTableContextMenu->addAction(banAction24h); @@ -534,7 +532,7 @@ void RPCConsole::setClientModel(ClientModel *model) QAction* unbanAction = new QAction(tr("&Unban"), this); // create ban table context menu - banTableContextMenu = new QMenu(); + banTableContextMenu = new QMenu(this); banTableContextMenu->addAction(unbanAction); // ban table context menu signals @@ -565,6 +563,14 @@ void RPCConsole::setClientModel(ClientModel *model) autoCompleter = new QCompleter(wordList, this); ui->lineEdit->setCompleter(autoCompleter); autoCompleter->popup()->installEventFilter(this); + // Start thread to execute RPC commands. + startExecutor(); + } + if (!model) { + // Client model is being set to 0, this means shutdown() is about to be called. + // Make sure we clean up the executor thread + Q_EMIT stopExecutor(); + thread.wait(); } } @@ -759,9 +765,8 @@ void RPCConsole::browseHistory(int offset) void RPCConsole::startExecutor() { - QThread *thread = new QThread; RPCExecutor *executor = new RPCExecutor(); - executor->moveToThread(thread); + executor->moveToThread(&thread); // Replies from executor object must go to this object connect(executor, SIGNAL(reply(int,QString)), this, SLOT(message(int,QString))); @@ -769,16 +774,15 @@ void RPCConsole::startExecutor() connect(this, SIGNAL(cmdRequest(QString)), executor, SLOT(request(QString))); // On stopExecutor signal - // - queue executor for deletion (in execution thread) // - quit the Qt event loop in the execution thread - connect(this, SIGNAL(stopExecutor()), executor, SLOT(deleteLater())); - connect(this, SIGNAL(stopExecutor()), thread, SLOT(quit())); - // Queue the thread for deletion (in this thread) when it is finished - connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater())); + connect(this, SIGNAL(stopExecutor()), &thread, SLOT(quit())); + // - queue executor for deletion (in execution thread) + connect(&thread, SIGNAL(finished()), executor, SLOT(deleteLater()), Qt::DirectConnection); + connect(&thread, SIGNAL(finished()), this, SLOT(test()), Qt::DirectConnection); // Default implementation of QThread::run() simply spins up an event loop in the thread, // which is what we want. - thread->start(); + thread.start(); } void RPCConsole::on_tabWidget_currentChanged(int index) diff --git a/src/qt/rpcconsole.h b/src/qt/rpcconsole.h index 8c20379a8c..344d5ecb98 100644 --- a/src/qt/rpcconsole.h +++ b/src/qt/rpcconsole.h @@ -12,6 +12,7 @@ #include <QWidget> #include <QCompleter> +#include <QThread> class ClientModel; class PlatformStyle; @@ -148,6 +149,7 @@ private: QMenu *banTableContextMenu; int consoleFontSize; QCompleter *autoCompleter; + QThread thread; /** Update UI with latest network info from model. */ void updateNetworkState(); diff --git a/src/qt/splashscreen.cpp b/src/qt/splashscreen.cpp index cd27385653..1b6e28e93e 100644 --- a/src/qt/splashscreen.cpp +++ b/src/qt/splashscreen.cpp @@ -147,6 +147,7 @@ void SplashScreen::slotFinish(QWidget *mainWin) if (isMinimized()) showNormal(); hide(); + deleteLater(); // No more need for this } static void InitMessage(SplashScreen *splash, const std::string &message) diff --git a/src/qt/transactionview.cpp b/src/qt/transactionview.cpp index 856b16d2c4..f5cbde6238 100644 --- a/src/qt/transactionview.cpp +++ b/src/qt/transactionview.cpp @@ -37,7 +37,7 @@ TransactionView::TransactionView(const PlatformStyle *platformStyle, QWidget *parent) : QWidget(parent), model(0), transactionProxyModel(0), - transactionView(0), abandonAction(0) + transactionView(0), abandonAction(0), columnResizingFixer(0) { // Build filter row setContentsMargins(0,0,0,0); @@ -147,7 +147,7 @@ TransactionView::TransactionView(const PlatformStyle *platformStyle, QWidget *pa QAction *editLabelAction = new QAction(tr("Edit label"), this); QAction *showDetailsAction = new QAction(tr("Show transaction details"), this); - contextMenu = new QMenu(); + contextMenu = new QMenu(this); contextMenu->addAction(copyAddressAction); contextMenu->addAction(copyLabelAction); contextMenu->addAction(copyAmountAction); @@ -212,7 +212,7 @@ void TransactionView::setModel(WalletModel *_model) transactionView->setColumnWidth(TransactionTableModel::Type, TYPE_COLUMN_WIDTH); transactionView->setColumnWidth(TransactionTableModel::Amount, AMOUNT_MINIMUM_COLUMN_WIDTH); - columnResizingFixer = new GUIUtil::TableViewLastColumnResizingFixer(transactionView, AMOUNT_MINIMUM_COLUMN_WIDTH, MINIMUM_COLUMN_WIDTH); + columnResizingFixer = new GUIUtil::TableViewLastColumnResizingFixer(transactionView, AMOUNT_MINIMUM_COLUMN_WIDTH, MINIMUM_COLUMN_WIDTH, this); if (_model->getOptionsModel()) { diff --git a/src/qt/utilitydialog.cpp b/src/qt/utilitydialog.cpp index 947bcdb15a..4ec022881c 100644 --- a/src/qt/utilitydialog.cpp +++ b/src/qt/utilitydialog.cpp @@ -171,22 +171,20 @@ ShutdownWindow::ShutdownWindow(QWidget *parent, Qt::WindowFlags f): setLayout(layout); } -void ShutdownWindow::showShutdownWindow(BitcoinGUI *window) +QWidget *ShutdownWindow::showShutdownWindow(BitcoinGUI *window) { if (!window) - return; + return nullptr; // Show a simple window indicating shutdown status QWidget *shutdownWindow = new ShutdownWindow(); - // We don't hold a direct pointer to the shutdown window after creation, so use - // Qt::WA_DeleteOnClose to make sure that the window will be deleted eventually. - shutdownWindow->setAttribute(Qt::WA_DeleteOnClose); shutdownWindow->setWindowTitle(window->windowTitle()); // Center shutdown window at where main window was const QPoint global = window->mapToGlobal(window->rect().center()); shutdownWindow->move(global.x() - shutdownWindow->width() / 2, global.y() - shutdownWindow->height() / 2); shutdownWindow->show(); + return shutdownWindow; } void ShutdownWindow::closeEvent(QCloseEvent *event) diff --git a/src/qt/utilitydialog.h b/src/qt/utilitydialog.h index 843bd7f67b..b930429578 100644 --- a/src/qt/utilitydialog.h +++ b/src/qt/utilitydialog.h @@ -43,7 +43,7 @@ class ShutdownWindow : public QWidget public: ShutdownWindow(QWidget *parent=0, Qt::WindowFlags f=0); - static void showShutdownWindow(BitcoinGUI *window); + static QWidget *showShutdownWindow(BitcoinGUI *window); protected: void closeEvent(QCloseEvent *event); diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 154107c0dc..733d14d24e 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -194,12 +194,12 @@ UniValue waitfornewblock(const JSONRPCRequest& request) { if (request.fHelp || request.params.size() > 1) throw runtime_error( - "waitfornewblock\n" + "waitfornewblock (timeout)\n" "\nWaits for a specific new block and returns useful info about it.\n" "\nReturns the current block on timeout or exit.\n" "\nArguments:\n" - "1. timeout (milliseconds) (int, optional, default=false)\n" - "\nResult::\n" + "1. timeout (int, optional, default=0) Time in milliseconds to wait for a response. 0 indicates no timeout.\n" + "\nResult:\n" "{ (json object)\n" " \"hash\" : { (string) The blockhash\n" " \"height\" : { (int) Block height\n" @@ -232,13 +232,13 @@ UniValue waitforblock(const JSONRPCRequest& request) { if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) throw runtime_error( - "waitforblock\n" + "waitforblock <blockhash> (timeout)\n" "\nWaits for a specific new block and returns useful info about it.\n" "\nReturns the current block on timeout or exit.\n" "\nArguments:\n" "1. blockhash to wait for (string)\n" - "2. timeout (milliseconds) (int, optional, default=false)\n" - "\nResult::\n" + "2. timeout (int, optional, default=0) Time in milliseconds to wait for a response. 0 indicates no timeout.\n" + "\nResult:\n" "{ (json object)\n" " \"hash\" : { (string) The blockhash\n" " \"height\" : { (int) Block height\n" @@ -274,14 +274,14 @@ UniValue waitforblockheight(const JSONRPCRequest& request) { if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) throw runtime_error( - "waitforblock\n" + "waitforblockheight <blockheight> (timeout)\n" "\nWaits for (at least) block height and returns the height and hash\n" - "\nof the current tip.\n" + "of the current tip.\n" "\nReturns the current block on timeout or exit.\n" "\nArguments:\n" "1. block height to wait for (int)\n" - "2. timeout (milliseconds) (int, optional, default=false)\n" - "\nResult::\n" + "2. timeout (int, optional, default=0) Time in milliseconds to wait for a response. 0 indicates no timeout.\n" + "\nResult:\n" "{ (json object)\n" " \"hash\" : { (string) The blockhash\n" " \"height\" : { (int) Block height\n" diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index 6b0e52a309..f3cd1fbf0b 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -680,7 +680,9 @@ UniValue getblocktemplate(const JSONRPCRequest& request) result.push_back(Pair("curtime", pblock->GetBlockTime())); result.push_back(Pair("bits", strprintf("%08x", pblock->nBits))); result.push_back(Pair("height", (int64_t)(pindexPrev->nHeight+1))); - if (!pblocktemplate->vchCoinbaseCommitment.empty()) { + + const struct BIP9DeploymentInfo& segwit_info = VersionBitsDeploymentInfo[Consensus::DEPLOYMENT_SEGWIT]; + if (!pblocktemplate->vchCoinbaseCommitment.empty() && setClientRules.find(segwit_info.name) != setClientRules.end()) { result.push_back(Pair("default_witness_commitment", HexStr(pblocktemplate->vchCoinbaseCommitment.begin(), pblocktemplate->vchCoinbaseCommitment.end()))); } diff --git a/src/streams.h b/src/streams.h index c3e7c9e9e4..b508784238 100644 --- a/src/streams.h +++ b/src/streams.h @@ -69,6 +69,75 @@ OverrideStream<S> WithOrVersion(S* s, int nVersionFlag) return OverrideStream<S>(s, s->GetType(), s->GetVersion() | nVersionFlag); } +/* Minimal stream for overwriting and/or appending to an existing byte vector + * + * The referenced vector will grow as necessary + */ +class CVectorWriter +{ + public: + +/* + * @param[in] nTypeIn Serialization Type + * @param[in] nVersionIn Serialization Version (including any flags) + * @param[in] vchDataIn Referenced byte vector to overwrite/append + * @param[in] nPosIn Starting position. Vector index where writes should start. The vector will initially + * grow as necessary to max(index, vec.size()). So to append, use vec.size(). +*/ + CVectorWriter(int nTypeIn, int nVersionIn, std::vector<unsigned char>& vchDataIn, size_t nPosIn) : nType(nTypeIn), nVersion(nVersionIn), vchData(vchDataIn), nPos(nPosIn) + { + if(nPos > vchData.size()) + vchData.resize(nPos); + } +/* + * (other params same as above) + * @param[in] args A list of items to serialize starting at nPos. +*/ + template <typename... Args> + CVectorWriter(int nTypeIn, int nVersionIn, std::vector<unsigned char>& vchDataIn, size_t nPosIn, Args&&... args) : CVectorWriter(nTypeIn, nVersionIn, vchDataIn, nPosIn) + { + ::SerializeMany(*this, std::forward<Args>(args)...); + } + void write(const char* pch, size_t nSize) + { + assert(nPos <= vchData.size()); + size_t nOverwrite = std::min(nSize, vchData.size() - nPos); + if (nOverwrite) { + memcpy(vchData.data() + nPos, reinterpret_cast<const unsigned char*>(pch), nOverwrite); + } + if (nOverwrite < nSize) { + vchData.insert(vchData.end(), reinterpret_cast<const unsigned char*>(pch) + nOverwrite, reinterpret_cast<const unsigned char*>(pch) + nSize); + } + nPos += nSize; + } + template<typename T> + CVectorWriter& operator<<(const T& obj) + { + // Serialize to this stream + ::Serialize(*this, obj); + return (*this); + } + int GetVersion() const + { + return nVersion; + } + int GetType() const + { + return nType; + } + void seek(size_t nSize) + { + nPos += nSize; + if(nPos > vchData.size()) + vchData.resize(nPos); + } +private: + const int nType; + const int nVersion; + std::vector<unsigned char>& vchData; + size_t nPos; +}; + /** Double ended buffer combining vector and stream-like interfaces. * * >> and << read and write unformatted data using the above serialization templates. diff --git a/src/support/lockedpool.h b/src/support/lockedpool.h index 3403415436..f5212bc266 100644 --- a/src/support/lockedpool.h +++ b/src/support/lockedpool.h @@ -109,7 +109,7 @@ private: * An arena manages a contiguous region of memory. The pool starts out with one arena * but can grow to multiple arenas if the need arises. * - * Unlike a normal C heap, the administrative structures are seperate from the managed + * Unlike a normal C heap, the administrative structures are separate from the managed * memory. This has been done as the sizes and bases of objects are not in themselves sensitive * information, as to conserve precious locked memory. In some operating systems * the amount of memory that can be locked is small. diff --git a/src/test/bitcoin-util-test.py b/src/test/bitcoin-util-test.py index 4301b93b7c..1c090b3f3f 100755 --- a/src/test/bitcoin-util-test.py +++ b/src/test/bitcoin-util-test.py @@ -15,7 +15,7 @@ help_text="""Test framework for bitcoin utils. Runs automatically during `make check`. -Can also be run manually from the src directory by specifiying the source directory: +Can also be run manually from the src directory by specifying the source directory: test/bitcoin-util-test.py --srcdir='srcdir' [--verbose] """ diff --git a/src/test/data/tx_valid.json b/src/test/data/tx_valid.json index 2f299aa5fe..27acb2f16a 100644 --- a/src/test/data/tx_valid.json +++ b/src/test/data/tx_valid.json @@ -53,7 +53,7 @@ ["The following tests for the presence of a bug in the handling of SIGHASH_SINGLE"], ["It results in signing the constant 1, instead of something generated based on the transaction,"], ["when the input doing the signing has an index greater than the maximum output index"], -[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "DUP HASH160 0x14 0xe52b482f2faa8ecbf0db344f93c84ac908557f33 EQUALVERIFY CHECKSIG"], ["0000000000000000000000000000000000000000000000000000000000000200", 0, "1"]], +[[["0000000000000000000000000000000000000000000000000000000000000200", 0, "1"], ["0000000000000000000000000000000000000000000000000000000000000100", 0, "DUP HASH160 0x14 0xe52b482f2faa8ecbf0db344f93c84ac908557f33 EQUALVERIFY CHECKSIG"]], "01000000020002000000000000000000000000000000000000000000000000000000000000000000000151ffffffff0001000000000000000000000000000000000000000000000000000000000000000000006b483045022100c9cdd08798a28af9d1baf44a6c77bcc7e279f47dc487c8c899911bc48feaffcc0220503c5c50ae3998a733263c5c0f7061b483e2b56c4c41b456e7d2f5a78a74c077032102d5c25adb51b61339d2b05315791e21bbe80ea470a49db0135720983c905aace0ffffffff010000000000000000015100000000", "P2SH"], ["An invalid P2SH Transaction"], @@ -334,9 +334,9 @@ "0100000000010100010000000000000000000000000000000000000000000000000000000000000000000023220020ff25429251b5a84f452230a3c75fd886b7fc5a7865ce4a7bb7a9d7c5be6da3dbffffffff01e8030000000000001976a9144c9c3dfac4207d5d8cb89df5722cb3d712385e3f88ac02483045022100aa5d8aa40a90f23ce2c3d11bc845ca4a12acd99cbea37de6b9f6d86edebba8cb022022dedc2aa0a255f74d04c0b76ece2d7c691f9dd11a64a8ac49f62a99c3a05f9d01232103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc71ac00000000", "P2SH,WITNESS"], ["Witness with SigHash Single|AnyoneCanPay"], -[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x51", 1100], +[[["0000000000000000000000000000000000000000000000000000000000000100", 2, "0x51", 3100], ["0000000000000000000000000000000000000000000000000000000000000100", 1, "0x00 0x14 0x4c9c3dfac4207d5d8cb89df5722cb3d712385e3f", 2000], -["0000000000000000000000000000000000000000000000000000000000000100", 2, "0x51", 3100], +["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x51", 1100], ["0000000000000000000000000000000000000000000000000000000000000100", 3, "0x51", 4100]], "0100000000010400010000000000000000000000000000000000000000000000000000000000000200000000ffffffff00010000000000000000000000000000000000000000000000000000000000000100000000ffffffff00010000000000000000000000000000000000000000000000000000000000000000000000ffffffff00010000000000000000000000000000000000000000000000000000000000000300000000ffffffff05540b0000000000000151d0070000000000000151840300000000000001513c0f00000000000001512c010000000000000151000248304502210092f4777a0f17bf5aeb8ae768dec5f2c14feabf9d1fe2c89c78dfed0f13fdb86902206da90a86042e252bcd1e80a168c719e4a1ddcc3cebea24b9812c5453c79107e9832103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc71000000000000", "P2SH,WITNESS"], @@ -359,9 +359,9 @@ "0100000000010300010000000000000000000000000000000000000000000000000000000000000000000000ffffffff00010000000000000000000000000000000000000000000000000000000000000100000000ffffffff00010000000000000000000000000000000000000000000000000000000000000200000000ffffffff03e8030000000000000151d0070000000000000151b80b000000000000015100024730440220699e6b0cfe015b64ca3283e6551440a34f901ba62dd4c72fe1cb815afb2e6761022021cc5e84db498b1479de14efda49093219441adc6c543e5534979605e273d80b032103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc710000000000", "P2SH,WITNESS"], ["Witness with SigHash None|AnyoneCanPay"], -[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x51", 1100], +[[["0000000000000000000000000000000000000000000000000000000000000100", 2, "0x51", 3100], +["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x51", 1100], ["0000000000000000000000000000000000000000000000000000000000000100", 1, "0x00 0x14 0x4c9c3dfac4207d5d8cb89df5722cb3d712385e3f", 2000], -["0000000000000000000000000000000000000000000000000000000000000100", 2, "0x51", 3100], ["0000000000000000000000000000000000000000000000000000000000000100", 3, "0x51", 4100]], "0100000000010400010000000000000000000000000000000000000000000000000000000000000200000000ffffffff00010000000000000000000000000000000000000000000000000000000000000000000000ffffffff00010000000000000000000000000000000000000000000000000000000000000100000000ffffffff00010000000000000000000000000000000000000000000000000000000000000300000000ffffffff04b60300000000000001519e070000000000000151860b00000000000001009600000000000000015100000248304502210091b32274295c2a3fa02f5bce92fb2789e3fc6ea947fbe1a76e52ea3f4ef2381a022079ad72aefa3837a2e0c033a8652a59731da05fa4a813f4fc48e87c075037256b822103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc710000000000", "P2SH,WITNESS"], @@ -390,9 +390,9 @@ "01000000000103000100000000000000000000000000000000000000000000000000000000000000000000000200000000010000000000000000000000000000000000000000000000000000000000000100000000ffffffff000100000000000000000000000000000000000000000000000000000000000002000000000200000003e8030000000000000151d0070000000000000151b80b00000000000001510002473044022022fceb54f62f8feea77faac7083c3b56c4676a78f93745adc8a35800bc36adfa022026927df9abcf0a8777829bcfcce3ff0a385fa54c3f9df577405e3ef24ee56479022103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc710000000000", "P2SH,WITNESS"], ["Witness with SigHash All|AnyoneCanPay"], -[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x51", 1100], +[[["0000000000000000000000000000000000000000000000000000000000000100", 2, "0x51", 3100], +["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x51", 1100], ["0000000000000000000000000000000000000000000000000000000000000100", 1, "0x00 0x14 0x4c9c3dfac4207d5d8cb89df5722cb3d712385e3f", 2000], -["0000000000000000000000000000000000000000000000000000000000000100", 2, "0x51", 3100], ["0000000000000000000000000000000000000000000000000000000000000100", 3, "0x51", 4100]], "0100000000010400010000000000000000000000000000000000000000000000000000000000000200000000ffffffff00010000000000000000000000000000000000000000000000000000000000000000000000ffffffff00010000000000000000000000000000000000000000000000000000000000000100000000ffffffff00010000000000000000000000000000000000000000000000000000000000000300000000ffffffff03e8030000000000000151d0070000000000000151b80b0000000000000151000002483045022100a3cec69b52cba2d2de623eeef89e0ba1606184ea55476c0f8189fda231bc9cbb022003181ad597f7c380a7d1c740286b1d022b8b04ded028b833282e055e03b8efef812103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc710000000000", "P2SH,WITNESS"], @@ -458,8 +458,8 @@ "0100000000010200010000000000000000000000000000000000000000000000000000000000000000000000ffffffff00010000000000000000000000000000000000000000000000000000000000000100000000ffffffff02e8030000000000000151e90300000000000001510247304402206d59682663faab5e4cb733c562e22cdae59294895929ec38d7c016621ff90da0022063ef0af5f970afe8a45ea836e3509b8847ed39463253106ac17d19c437d3d56b832103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc710248304502210085001a820bfcbc9f9de0298af714493f8a37b3b354bfd21a7097c3e009f2018c022050a8b4dbc8155d4d04da2f5cdd575dcf8dd0108de8bec759bd897ea01ecb3af7832103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc7100000000", "P2SH,WITNESS"], ["Witness Single|AnyoneCanPay does not hash input's position (permutation)"], -[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x00 0x14 0x4c9c3dfac4207d5d8cb89df5722cb3d712385e3f", 1000], -["0000000000000000000000000000000000000000000000000000000000000100", 1, "0x00 0x14 0x4c9c3dfac4207d5d8cb89df5722cb3d712385e3f", 1001]], +[[["0000000000000000000000000000000000000000000000000000000000000100", 1, "0x00 0x14 0x4c9c3dfac4207d5d8cb89df5722cb3d712385e3f", 1001], +["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x00 0x14 0x4c9c3dfac4207d5d8cb89df5722cb3d712385e3f", 1000]], "0100000000010200010000000000000000000000000000000000000000000000000000000000000100000000ffffffff00010000000000000000000000000000000000000000000000000000000000000000000000ffffffff02e9030000000000000151e80300000000000001510248304502210085001a820bfcbc9f9de0298af714493f8a37b3b354bfd21a7097c3e009f2018c022050a8b4dbc8155d4d04da2f5cdd575dcf8dd0108de8bec759bd897ea01ecb3af7832103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc710247304402206d59682663faab5e4cb733c562e22cdae59294895929ec38d7c016621ff90da0022063ef0af5f970afe8a45ea836e3509b8847ed39463253106ac17d19c437d3d56b832103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc7100000000", "P2SH,WITNESS"], ["Non witness Single|AnyoneCanPay hash input's position"], diff --git a/src/test/limitedmap_tests.cpp b/src/test/limitedmap_tests.cpp index faaddffad8..e3531486aa 100644 --- a/src/test/limitedmap_tests.cpp +++ b/src/test/limitedmap_tests.cpp @@ -47,7 +47,7 @@ BOOST_AUTO_TEST_CASE(limitedmap_test) // make sure the item is present BOOST_CHECK(map.count(i) == 1); - // use the iterator to check for the expected key adn value + // use the iterator to check for the expected key and value BOOST_CHECK(it->first == i); BOOST_CHECK(it->second == i + 1); diff --git a/src/test/miner_tests.cpp b/src/test/miner_tests.cpp index aea8920936..85a2e907c2 100644 --- a/src/test/miner_tests.cpp +++ b/src/test/miner_tests.cpp @@ -214,6 +214,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) txCoinbase.vin[0].scriptSig = CScript(); txCoinbase.vin[0].scriptSig.push_back(blockinfo[i].extranonce); txCoinbase.vin[0].scriptSig.push_back(chainActive.Height()); + txCoinbase.vout.resize(1); // Ignore the (optional) segwit commitment added by CreateNewBlock (as the hardcoded nonces don't account for this) txCoinbase.vout[0].scriptPubKey = CScript(); pblock->vtx[0] = MakeTransactionRef(std::move(txCoinbase)); if (txFirst.size() == 0) diff --git a/src/test/streams_tests.cpp b/src/test/streams_tests.cpp index 34f501e867..8b715ce93e 100644 --- a/src/test/streams_tests.cpp +++ b/src/test/streams_tests.cpp @@ -15,6 +15,64 @@ using namespace boost::assign; // bring 'operator+=()' into scope BOOST_FIXTURE_TEST_SUITE(streams_tests, BasicTestingSetup) +BOOST_AUTO_TEST_CASE(streams_vector_writer) +{ + unsigned char a(1); + unsigned char b(2); + unsigned char bytes[] = { 3, 4, 5, 6 }; + std::vector<unsigned char> vch; + + // Each test runs twice. Serializing a second time at the same starting + // point should yield the same results, even if the first test grew the + // vector. + + CVectorWriter(SER_NETWORK, INIT_PROTO_VERSION, vch, 0, a, b); + BOOST_CHECK((vch == std::vector<unsigned char>{{1, 2}})); + CVectorWriter(SER_NETWORK, INIT_PROTO_VERSION, vch, 0, a, b); + BOOST_CHECK((vch == std::vector<unsigned char>{{1, 2}})); + vch.clear(); + + CVectorWriter(SER_NETWORK, INIT_PROTO_VERSION, vch, 2, a, b); + BOOST_CHECK((vch == std::vector<unsigned char>{{0, 0, 1, 2}})); + CVectorWriter(SER_NETWORK, INIT_PROTO_VERSION, vch, 2, a, b); + BOOST_CHECK((vch == std::vector<unsigned char>{{0, 0, 1, 2}})); + vch.clear(); + + vch.resize(5, 0); + CVectorWriter(SER_NETWORK, INIT_PROTO_VERSION, vch, 2, a, b); + BOOST_CHECK((vch == std::vector<unsigned char>{{0, 0, 1, 2, 0}})); + CVectorWriter(SER_NETWORK, INIT_PROTO_VERSION, vch, 2, a, b); + BOOST_CHECK((vch == std::vector<unsigned char>{{0, 0, 1, 2, 0}})); + vch.clear(); + + vch.resize(4, 0); + CVectorWriter(SER_NETWORK, INIT_PROTO_VERSION, vch, 3, a, b); + BOOST_CHECK((vch == std::vector<unsigned char>{{0, 0, 0, 1, 2}})); + CVectorWriter(SER_NETWORK, INIT_PROTO_VERSION, vch, 3, a, b); + BOOST_CHECK((vch == std::vector<unsigned char>{{0, 0, 0, 1, 2}})); + vch.clear(); + + vch.resize(4, 0); + CVectorWriter(SER_NETWORK, INIT_PROTO_VERSION, vch, 4, a, b); + BOOST_CHECK((vch == std::vector<unsigned char>{{0, 0, 0, 0, 1, 2}})); + CVectorWriter(SER_NETWORK, INIT_PROTO_VERSION, vch, 4, a, b); + BOOST_CHECK((vch == std::vector<unsigned char>{{0, 0, 0, 0, 1, 2}})); + vch.clear(); + + CVectorWriter(SER_NETWORK, INIT_PROTO_VERSION, vch, 0, FLATDATA(bytes)); + BOOST_CHECK((vch == std::vector<unsigned char>{{3, 4, 5, 6}})); + CVectorWriter(SER_NETWORK, INIT_PROTO_VERSION, vch, 0, FLATDATA(bytes)); + BOOST_CHECK((vch == std::vector<unsigned char>{{3, 4, 5, 6}})); + vch.clear(); + + vch.resize(4, 8); + CVectorWriter(SER_NETWORK, INIT_PROTO_VERSION, vch, 2, a, FLATDATA(bytes), b); + BOOST_CHECK((vch == std::vector<unsigned char>{{8, 8, 1, 3, 4, 5, 6, 2}})); + CVectorWriter(SER_NETWORK, INIT_PROTO_VERSION, vch, 2, a, FLATDATA(bytes), b); + BOOST_CHECK((vch == std::vector<unsigned char>{{8, 8, 1, 3, 4, 5, 6, 2}})); + vch.clear(); +} + BOOST_AUTO_TEST_CASE(streams_serializedata_xor) { std::vector<char> in; diff --git a/src/torcontrol.cpp b/src/torcontrol.cpp index 1ca6b46566..ffb9993f90 100644 --- a/src/torcontrol.cpp +++ b/src/torcontrol.cpp @@ -470,7 +470,7 @@ void TorController::auth_cb(TorControlConnection& _conn, const TorControlReply& // Finally - now create the service if (private_key.empty()) // No private key, generate one - private_key = "NEW:BEST"; + private_key = "NEW:RSA1024"; // Explicitly request RSA1024 - see issue #9214 // Request hidden service, redirect port. // Note that the 'virtual' port doesn't have to be the same as our internal port, but this is just a convenient // choice. TODO; refactor the shutdown sequence some day. diff --git a/src/txmempool.h b/src/txmempool.h index 29b59363a2..23fe5a7abe 100644 --- a/src/txmempool.h +++ b/src/txmempool.h @@ -62,7 +62,7 @@ class CTxMemPool; /** \class CTxMemPoolEntry * - * CTxMemPoolEntry stores data about the correponding transaction, as well + * CTxMemPoolEntry stores data about the corresponding transaction, as well * as data about all in-mempool transactions that depend on the transaction * ("descendant" transactions). * diff --git a/src/util.cpp b/src/util.cpp index c20ede6221..014013d214 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -107,7 +107,6 @@ map<string, vector<string> > mapMultiArgs; bool fDebug = false; bool fPrintToConsole = false; bool fPrintToDebugLog = true; -bool fServer = false; string strMiscWarning; bool fLogTimestamps = DEFAULT_LOGTIMESTAMPS; bool fLogTimeMicros = DEFAULT_LOGTIMEMICROS; @@ -259,7 +258,7 @@ bool LogAcceptCategory(const char* category) * suppress printing of the timestamp when multiple calls are made that don't * end in a newline. Initialize it to true, and hold it, in the calling context. */ -static std::string LogTimestampStr(const std::string &str, bool *fStartedNewLine) +static std::string LogTimestampStr(const std::string &str, std::atomic_bool *fStartedNewLine) { string strStamped; @@ -286,7 +285,7 @@ static std::string LogTimestampStr(const std::string &str, bool *fStartedNewLine int LogPrintStr(const std::string &str) { int ret = 0; // Returns total number of characters written - static bool fStartedNewLine = true; + static std::atomic_bool fStartedNewLine(true); string strTimestamped = LogTimestampStr(str, &fStartedNewLine); diff --git a/src/util.h b/src/util.h index bbb9b5db82..e8aa266f28 100644 --- a/src/util.h +++ b/src/util.h @@ -46,7 +46,6 @@ extern std::map<std::string, std::vector<std::string> > mapMultiArgs; extern bool fDebug; extern bool fPrintToConsole; extern bool fPrintToDebugLog; -extern bool fServer; extern std::string strMiscWarning; extern bool fLogTimestamps; extern bool fLogTimeMicros; diff --git a/src/utiltime.cpp b/src/utiltime.cpp index da590f8889..51d545ef8a 100644 --- a/src/utiltime.cpp +++ b/src/utiltime.cpp @@ -74,8 +74,9 @@ void MilliSleep(int64_t n) std::string DateTimeStrFormat(const char* pszFormat, int64_t nTime) { + static std::locale classic(std::locale::classic()); // std::locale takes ownership of the pointer - std::locale loc(std::locale::classic(), new boost::posix_time::time_facet(pszFormat)); + std::locale loc(classic, new boost::posix_time::time_facet(pszFormat)); std::stringstream ss; ss.imbue(loc); ss << boost::posix_time::from_time_t(nTime); |