aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am4
-rw-r--r--src/Makefile.test.include2
-rw-r--r--src/addrdb.cpp18
-rw-r--r--src/addrdb.h6
-rw-r--r--src/bench/checkblock.cpp4
-rw-r--r--src/bench/coin_selection.cpp2
-rw-r--r--src/bitcoin-cli.cpp4
-rw-r--r--src/bitcoind.cpp4
-rw-r--r--src/clientversion.h26
-rw-r--r--src/dbwrapper.cpp5
-rw-r--r--src/dbwrapper.h5
-rw-r--r--src/fs.cpp17
-rw-r--r--src/fs.h24
-rw-r--r--src/httpserver.cpp28
-rw-r--r--src/httpserver.h4
-rw-r--r--src/init.cpp59
-rw-r--r--src/net.h2
-rw-r--r--src/net_processing.cpp32
-rw-r--r--src/net_processing.h8
-rw-r--r--src/qt/bitcoin.cpp4
-rw-r--r--src/qt/forms/optionsdialog.ui102
-rw-r--r--src/qt/forms/sendcoinsdialog.ui2
-rw-r--r--src/qt/guiutil.cpp67
-rw-r--r--src/qt/guiutil.h10
-rw-r--r--src/qt/intro.cpp5
-rw-r--r--src/qt/optionsdialog.cpp12
-rw-r--r--src/qt/optionsdialog.h1
-rw-r--r--src/qt/test/rpcnestedtests.cpp5
-rw-r--r--src/qt/test/test_main.cpp8
-rw-r--r--src/qt/test/wallettests.cpp12
-rw-r--r--src/rpc/client.cpp2
-rw-r--r--src/rpc/mining.cpp34
-rw-r--r--src/rpc/misc.cpp71
-rw-r--r--src/rpc/protocol.cpp12
-rw-r--r--src/rpc/protocol.h5
-rw-r--r--src/rpc/server.cpp6
-rw-r--r--src/test/dbwrapper_tests.cpp18
-rw-r--r--src/test/test_bitcoin.cpp6
-rw-r--r--src/test/test_bitcoin.h4
-rw-r--r--src/test/testutil.cpp6
-rw-r--r--src/test/testutil.h4
-rw-r--r--src/torcontrol.cpp20
-rw-r--r--src/txdb.h8
-rw-r--r--src/txmempool.cpp7
-rw-r--r--src/util.cpp84
-rw-r--r--src/util.h29
-rw-r--r--src/validation.cpp190
-rw-r--r--src/validation.h4
-rw-r--r--src/validationinterface.cpp15
-rw-r--r--src/validationinterface.h35
-rw-r--r--src/wallet/db.cpp28
-rw-r--r--src/wallet/db.h11
-rw-r--r--src/wallet/feebumper.cpp283
-rw-r--r--src/wallet/feebumper.h56
-rw-r--r--src/wallet/rpcwallet.cpp262
-rw-r--r--src/wallet/test/wallet_tests.cpp2
-rw-r--r--src/wallet/wallet.cpp177
-rw-r--r--src/wallet/wallet.h57
-rw-r--r--src/wallet/walletdb.cpp6
-rw-r--r--src/wallet/walletdb.h4
-rw-r--r--src/zmq/zmqnotificationinterface.cpp22
-rw-r--r--src/zmq/zmqnotificationinterface.h7
62 files changed, 1231 insertions, 726 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index 30d027315a..69b5bb48ad 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -98,6 +98,7 @@ BITCOIN_CORE_H = \
core_io.h \
core_memusage.h \
cuckoocache.h \
+ fs.h \
httprpc.h \
httpserver.h \
indirectmap.h \
@@ -156,6 +157,7 @@ BITCOIN_CORE_H = \
wallet/coincontrol.h \
wallet/crypter.h \
wallet/db.h \
+ wallet/feebumper.h \
wallet/rpcwallet.h \
wallet/wallet.h \
wallet/walletdb.h \
@@ -230,6 +232,7 @@ libbitcoin_wallet_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
libbitcoin_wallet_a_SOURCES = \
wallet/crypter.cpp \
wallet/db.cpp \
+ wallet/feebumper.cpp \
wallet/rpcdump.cpp \
wallet/rpcwallet.cpp \
wallet/wallet.cpp \
@@ -326,6 +329,7 @@ libbitcoin_util_a_SOURCES = \
compat/glibc_sanity.cpp \
compat/glibcxx_sanity.cpp \
compat/strnlen.cpp \
+ fs.cpp \
random.cpp \
rpc/protocol.cpp \
support/cleanse.cpp \
diff --git a/src/Makefile.test.include b/src/Makefile.test.include
index fe0ed59fe2..d08c8bde5b 100644
--- a/src/Makefile.test.include
+++ b/src/Makefile.test.include
@@ -97,7 +97,7 @@ endif
test_test_bitcoin_SOURCES = $(BITCOIN_TESTS) $(JSON_TEST_FILES) $(RAW_TEST_FILES)
test_test_bitcoin_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -I$(builddir)/test/ $(TESTDEFS) $(EVENT_CFLAGS)
test_test_bitcoin_LDADD = $(LIBBITCOIN_SERVER) $(LIBBITCOIN_CLI) $(LIBBITCOIN_COMMON) $(LIBBITCOIN_UTIL) $(LIBBITCOIN_CONSENSUS) $(LIBBITCOIN_CRYPTO) $(LIBUNIVALUE) $(LIBLEVELDB) $(LIBMEMENV) \
- $(BOOST_LIBS) $(BOOST_UNIT_TEST_FRAMEWORK_LIB) $(LIBSECP256K1) $(EVENT_LIBS)
+ $(BOOST_LIBS) $(BOOST_UNIT_TEST_FRAMEWORK_LIB) $(LIBSECP256K1) $(EVENT_LIBS) $(EVENT_PTHREADS_LIBS)
test_test_bitcoin_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
if ENABLE_WALLET
test_test_bitcoin_LDADD += $(LIBBITCOIN_WALLET)
diff --git a/src/addrdb.cpp b/src/addrdb.cpp
index 224d3921cc..a3743cd0d4 100644
--- a/src/addrdb.cpp
+++ b/src/addrdb.cpp
@@ -8,13 +8,13 @@
#include "addrman.h"
#include "chainparams.h"
#include "clientversion.h"
+#include "fs.h"
#include "hash.h"
#include "random.h"
#include "streams.h"
#include "tinyformat.h"
#include "util.h"
-#include <boost/filesystem.hpp>
CBanDB::CBanDB()
{
@@ -36,8 +36,8 @@ bool CBanDB::Write(const banmap_t& banSet)
ssBanlist << hash;
// open temp output file, and associate with CAutoFile
- boost::filesystem::path pathTmp = GetDataDir() / tmpfn;
- FILE *file = fopen(pathTmp.string().c_str(), "wb");
+ fs::path pathTmp = GetDataDir() / tmpfn;
+ FILE *file = fsbridge::fopen(pathTmp, "wb");
CAutoFile fileout(file, SER_DISK, CLIENT_VERSION);
if (fileout.IsNull())
return error("%s: Failed to open file %s", __func__, pathTmp.string());
@@ -62,13 +62,13 @@ bool CBanDB::Write(const banmap_t& banSet)
bool CBanDB::Read(banmap_t& banSet)
{
// open input file, and associate with CAutoFile
- FILE *file = fopen(pathBanlist.string().c_str(), "rb");
+ FILE *file = fsbridge::fopen(pathBanlist, "rb");
CAutoFile filein(file, SER_DISK, CLIENT_VERSION);
if (filein.IsNull())
return error("%s: Failed to open file %s", __func__, pathBanlist.string());
// use file size to size memory buffer
- uint64_t fileSize = boost::filesystem::file_size(pathBanlist);
+ uint64_t fileSize = fs::file_size(pathBanlist);
uint64_t dataSize = 0;
// Don't try to resize to a negative number if file is small
if (fileSize >= sizeof(uint256))
@@ -133,8 +133,8 @@ bool CAddrDB::Write(const CAddrMan& addr)
ssPeers << hash;
// open temp output file, and associate with CAutoFile
- boost::filesystem::path pathTmp = GetDataDir() / tmpfn;
- FILE *file = fopen(pathTmp.string().c_str(), "wb");
+ fs::path pathTmp = GetDataDir() / tmpfn;
+ FILE *file = fsbridge::fopen(pathTmp, "wb");
CAutoFile fileout(file, SER_DISK, CLIENT_VERSION);
if (fileout.IsNull())
return error("%s: Failed to open file %s", __func__, pathTmp.string());
@@ -159,13 +159,13 @@ bool CAddrDB::Write(const CAddrMan& addr)
bool CAddrDB::Read(CAddrMan& addr)
{
// open input file, and associate with CAutoFile
- FILE *file = fopen(pathAddr.string().c_str(), "rb");
+ FILE *file = fsbridge::fopen(pathAddr, "rb");
CAutoFile filein(file, SER_DISK, CLIENT_VERSION);
if (filein.IsNull())
return error("%s: Failed to open file %s", __func__, pathAddr.string());
// use file size to size memory buffer
- uint64_t fileSize = boost::filesystem::file_size(pathAddr);
+ uint64_t fileSize = fs::file_size(pathAddr);
uint64_t dataSize = 0;
// Don't try to resize to a negative number if file is small
if (fileSize >= sizeof(uint256))
diff --git a/src/addrdb.h b/src/addrdb.h
index ab985b10cb..c3d509bd3a 100644
--- a/src/addrdb.h
+++ b/src/addrdb.h
@@ -6,11 +6,11 @@
#ifndef BITCOIN_ADDRDB_H
#define BITCOIN_ADDRDB_H
+#include "fs.h"
#include "serialize.h"
#include <string>
#include <map>
-#include <boost/filesystem/path.hpp>
class CSubNet;
class CAddrMan;
@@ -80,7 +80,7 @@ typedef std::map<CSubNet, CBanEntry> banmap_t;
class CAddrDB
{
private:
- boost::filesystem::path pathAddr;
+ fs::path pathAddr;
public:
CAddrDB();
bool Write(const CAddrMan& addr);
@@ -92,7 +92,7 @@ public:
class CBanDB
{
private:
- boost::filesystem::path pathBanlist;
+ fs::path pathBanlist;
public:
CBanDB();
bool Write(const banmap_t& banSet);
diff --git a/src/bench/checkblock.cpp b/src/bench/checkblock.cpp
index 230e4ca773..c6c932454a 100644
--- a/src/bench/checkblock.cpp
+++ b/src/bench/checkblock.cpp
@@ -22,7 +22,7 @@ static void DeserializeBlockTest(benchmark::State& state)
CDataStream stream((const char*)block_bench::block413567,
(const char*)&block_bench::block413567[sizeof(block_bench::block413567)],
SER_NETWORK, PROTOCOL_VERSION);
- char a;
+ char a = '\0';
stream.write(&a, 1); // Prevent compaction
while (state.KeepRunning()) {
@@ -37,7 +37,7 @@ static void DeserializeAndCheckBlockTest(benchmark::State& state)
CDataStream stream((const char*)block_bench::block413567,
(const char*)&block_bench::block413567[sizeof(block_bench::block413567)],
SER_NETWORK, PROTOCOL_VERSION);
- char a;
+ char a = '\0';
stream.write(&a, 1); // Prevent compaction
Consensus::Params params = Params(CBaseChainParams::MAIN).GetConsensus();
diff --git a/src/bench/coin_selection.cpp b/src/bench/coin_selection.cpp
index 06882f1514..42891f345b 100644
--- a/src/bench/coin_selection.cpp
+++ b/src/bench/coin_selection.cpp
@@ -48,7 +48,7 @@ static void CoinSelection(benchmark::State& state)
addCoin(1000 * COIN, wallet, vCoins);
addCoin(3 * COIN, wallet, vCoins);
- std::set<std::pair<const CWalletTx*, unsigned int> > setCoinsRet;
+ std::set<CInputCoin> setCoinsRet;
CAmount nValueRet;
bool success = wallet.SelectCoinsMinConf(1003 * COIN, 1, 6, 0, vCoins, setCoinsRet, nValueRet);
assert(success);
diff --git a/src/bitcoin-cli.cpp b/src/bitcoin-cli.cpp
index ed8ca7e14c..5edd43d41e 100644
--- a/src/bitcoin-cli.cpp
+++ b/src/bitcoin-cli.cpp
@@ -9,12 +9,12 @@
#include "chainparamsbase.h"
#include "clientversion.h"
+#include "fs.h"
#include "rpc/client.h"
#include "rpc/protocol.h"
#include "util.h"
#include "utilstrencodings.h"
-#include <boost/filesystem/operations.hpp>
#include <stdio.h>
#include <event2/buffer.h>
@@ -96,7 +96,7 @@ static int AppInitRPC(int argc, char* argv[])
}
return EXIT_SUCCESS;
}
- if (!boost::filesystem::is_directory(GetDataDir(false))) {
+ if (!fs::is_directory(GetDataDir(false))) {
fprintf(stderr, "Error: Specified data directory \"%s\" does not exist.\n", GetArg("-datadir", "").c_str());
return EXIT_FAILURE;
}
diff --git a/src/bitcoind.cpp b/src/bitcoind.cpp
index a3d02afcdb..31680a8ec7 100644
--- a/src/bitcoind.cpp
+++ b/src/bitcoind.cpp
@@ -10,6 +10,7 @@
#include "chainparams.h"
#include "clientversion.h"
#include "compat.h"
+#include "fs.h"
#include "rpc/server.h"
#include "init.h"
#include "noui.h"
@@ -20,7 +21,6 @@
#include "utilstrencodings.h"
#include <boost/algorithm/string/predicate.hpp>
-#include <boost/filesystem.hpp>
#include <boost/thread.hpp>
#include <stdio.h>
@@ -97,7 +97,7 @@ bool AppInit(int argc, char* argv[])
try
{
- if (!boost::filesystem::is_directory(GetDataDir(false)))
+ if (!fs::is_directory(GetDataDir(false)))
{
fprintf(stderr, "Error: Specified data directory \"%s\" does not exist.\n", GetArg("-datadir", "").c_str());
return false;
diff --git a/src/clientversion.h b/src/clientversion.h
index 69154d546d..3d5392619b 100644
--- a/src/clientversion.h
+++ b/src/clientversion.h
@@ -7,29 +7,13 @@
#if defined(HAVE_CONFIG_H)
#include "config/bitcoin-config.h"
-#else
-
-/**
- * client versioning and copyright year
- */
-
-//! These need to be macros, as clientversion.cpp's and bitcoin*-res.rc's voodoo requires it
-#define CLIENT_VERSION_MAJOR 0
-#define CLIENT_VERSION_MINOR 14
-#define CLIENT_VERSION_REVISION 99
-#define CLIENT_VERSION_BUILD 0
-
-//! Set to true for release, false for prerelease or test build
-#define CLIENT_VERSION_IS_RELEASE false
-
-/**
- * Copyright year (2009-this)
- * Todo: update this when changing our copyright comments in the source
- */
-#define COPYRIGHT_YEAR 2017
-
#endif //HAVE_CONFIG_H
+// Check that required client information is defined
+#if !defined(CLIENT_VERSION_MAJOR) || !defined(CLIENT_VERSION_MINOR) || !defined(CLIENT_VERSION_REVISION) || !defined(CLIENT_VERSION_BUILD) || !defined(CLIENT_VERSION_IS_RELEASE) || !defined(COPYRIGHT_YEAR)
+#error Client version information missing: version is not defined by bitcoin-config.h or in any other way
+#endif
+
/**
* Converts the parameter X to a string after macro replacement on X has been performed.
* Don't merge these into one macro!
diff --git a/src/dbwrapper.cpp b/src/dbwrapper.cpp
index 01fcd07420..3d2098c059 100644
--- a/src/dbwrapper.cpp
+++ b/src/dbwrapper.cpp
@@ -4,11 +4,10 @@
#include "dbwrapper.h"
+#include "fs.h"
#include "util.h"
#include "random.h"
-#include <boost/filesystem.hpp>
-
#include <leveldb/cache.h>
#include <leveldb/env.h>
#include <leveldb/filter_policy.h>
@@ -91,7 +90,7 @@ static leveldb::Options GetOptions(size_t nCacheSize)
return options;
}
-CDBWrapper::CDBWrapper(const boost::filesystem::path& path, size_t nCacheSize, bool fMemory, bool fWipe, bool obfuscate)
+CDBWrapper::CDBWrapper(const fs::path& path, size_t nCacheSize, bool fMemory, bool fWipe, bool obfuscate)
{
penv = NULL;
readoptions.verify_checksums = true;
diff --git a/src/dbwrapper.h b/src/dbwrapper.h
index 414df76a7c..b13e98b7a4 100644
--- a/src/dbwrapper.h
+++ b/src/dbwrapper.h
@@ -6,14 +6,13 @@
#define BITCOIN_DBWRAPPER_H
#include "clientversion.h"
+#include "fs.h"
#include "serialize.h"
#include "streams.h"
#include "util.h"
#include "utilstrencodings.h"
#include "version.h"
-#include <boost/filesystem/path.hpp>
-
#include <leveldb/db.h>
#include <leveldb/write_batch.h>
@@ -195,7 +194,7 @@ public:
* @param[in] obfuscate If true, store data obfuscated via simple XOR. If false, XOR
* with a zero'd byte array.
*/
- CDBWrapper(const boost::filesystem::path& path, size_t nCacheSize, bool fMemory = false, bool fWipe = false, bool obfuscate = false);
+ CDBWrapper(const fs::path& path, size_t nCacheSize, bool fMemory = false, bool fWipe = false, bool obfuscate = false);
~CDBWrapper();
template <typename K, typename V>
diff --git a/src/fs.cpp b/src/fs.cpp
new file mode 100644
index 0000000000..6f2b768de3
--- /dev/null
+++ b/src/fs.cpp
@@ -0,0 +1,17 @@
+#include "fs.h"
+
+#include <boost/filesystem.hpp>
+
+namespace fsbridge {
+
+FILE *fopen(const fs::path& p, const char *mode)
+{
+ return ::fopen(p.string().c_str(), mode);
+}
+
+FILE *freopen(const fs::path& p, const char *mode, FILE *stream)
+{
+ return ::freopen(p.string().c_str(), mode, stream);
+}
+
+} // fsbridge
diff --git a/src/fs.h b/src/fs.h
new file mode 100644
index 0000000000..585cbf9c38
--- /dev/null
+++ b/src/fs.h
@@ -0,0 +1,24 @@
+// Copyright (c) 2017 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_FS_H
+#define BITCOIN_FS_H
+
+#include <stdio.h>
+#include <string>
+
+#include <boost/filesystem.hpp>
+#include <boost/filesystem/fstream.hpp>
+#include <boost/filesystem/detail/utf8_codecvt_facet.hpp>
+
+/** Filesystem operations and types */
+namespace fs = boost::filesystem;
+
+/** Bridge operations to C stdio */
+namespace fsbridge {
+ FILE *fopen(const fs::path& p, const char *mode);
+ FILE *freopen(const fs::path& p, const char *mode, FILE *stream);
+};
+
+#endif
diff --git a/src/httpserver.cpp b/src/httpserver.cpp
index 347433eb11..e7df23295d 100644
--- a/src/httpserver.cpp
+++ b/src/httpserver.cpp
@@ -384,15 +384,13 @@ bool InitHTTPServer()
// Redirect libevent's logging to our own log
event_set_log_callback(&libevent_log_cb);
-#if LIBEVENT_VERSION_NUMBER >= 0x02010100
- // If -debug=libevent, set full libevent debugging.
- // Otherwise, disable all libevent debugging.
- if (LogAcceptCategory(BCLog::LIBEVENT)) {
- event_enable_debug_logging(EVENT_DBG_ALL);
- } else {
- event_enable_debug_logging(EVENT_DBG_NONE);
+ // Update libevent's log handling. Returns false if our version of
+ // libevent doesn't support debug logging, in which case we should
+ // clear the BCLog::LIBEVENT flag.
+ if (!UpdateHTTPServerLogging(logCategories & BCLog::LIBEVENT)) {
+ logCategories &= ~BCLog::LIBEVENT;
}
-#endif
+
#ifdef WIN32
evthread_use_windows_threads();
#else
@@ -435,6 +433,20 @@ bool InitHTTPServer()
return true;
}
+bool UpdateHTTPServerLogging(bool enable) {
+#if LIBEVENT_VERSION_NUMBER >= 0x02010100
+ if (enable) {
+ event_enable_debug_logging(EVENT_DBG_ALL);
+ } else {
+ event_enable_debug_logging(EVENT_DBG_NONE);
+ }
+ return true;
+#else
+ // Can't update libevent logging if version < 02010100
+ return false;
+#endif
+}
+
std::thread threadHTTP;
std::future<bool> threadResult;
diff --git a/src/httpserver.h b/src/httpserver.h
index b55b253bea..6be9950682 100644
--- a/src/httpserver.h
+++ b/src/httpserver.h
@@ -32,6 +32,10 @@ void InterruptHTTPServer();
/** Stop HTTP server */
void StopHTTPServer();
+/** Change logging level for libevent. Removes BCLog::LIBEVENT from logCategories if
+ * libevent doesn't support debug logging.*/
+bool UpdateHTTPServerLogging(bool enable);
+
/** Handler for requests to a certain HTTP path */
typedef std::function<bool(HTTPRequest* req, const std::string &)> HTTPRequestHandler;
/** Register handler for prefix.
diff --git a/src/init.cpp b/src/init.cpp
index 59e5a4a207..1e7e388a52 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -16,6 +16,7 @@
#include "checkpoints.h"
#include "compat/sanity.h"
#include "consensus/validation.h"
+#include "fs.h"
#include "httpserver.h"
#include "httprpc.h"
#include "key.h"
@@ -56,7 +57,6 @@
#include <boost/algorithm/string/replace.hpp>
#include <boost/algorithm/string/split.hpp>
#include <boost/bind.hpp>
-#include <boost/filesystem.hpp>
#include <boost/function.hpp>
#include <boost/interprocess/sync/file_lock.hpp>
#include <boost/thread.hpp>
@@ -212,8 +212,8 @@ void Shutdown()
if (fFeeEstimatesInitialized)
{
- boost::filesystem::path est_path = GetDataDir() / FEE_ESTIMATES_FILENAME;
- CAutoFile est_fileout(fopen(est_path.string().c_str(), "wb"), SER_DISK, CLIENT_VERSION);
+ fs::path est_path = GetDataDir() / FEE_ESTIMATES_FILENAME;
+ CAutoFile est_fileout(fsbridge::fopen(est_path, "wb"), SER_DISK, CLIENT_VERSION);
if (!est_fileout.IsNull())
mempool.WriteFeeEstimates(est_fileout);
else
@@ -250,8 +250,8 @@ void Shutdown()
#ifndef WIN32
try {
- boost::filesystem::remove(GetPidFile());
- } catch (const boost::filesystem::filesystem_error& e) {
+ fs::remove(GetPidFile());
+ } catch (const fs::filesystem_error& e) {
LogPrintf("%s: Unable to remove pidfile: %s\n", __func__, e.what());
}
#endif
@@ -444,8 +444,6 @@ std::string HelpMessage(HelpMessageMode mode)
strUsage += HelpMessageOpt("-debug=<category>", strprintf(_("Output debugging information (default: %u, supplying <category> is optional)"), 0) + ". " +
_("If <category> is not supplied or if <category> = 1, output all debugging information.") + " " + _("<category> can be:") + " " + ListLogCategories() + ".");
strUsage += HelpMessageOpt("-debugexclude=<category>", strprintf(_("Exclude debugging information for a category. Can be used in conjunction with -debug=1 to output debug logs for all categories except one or more specified categories.")));
- if (showDebug)
- strUsage += HelpMessageOpt("-nodebug", "Turn off debugging messages, same as -debug=0");
strUsage += HelpMessageOpt("-help-debug", _("Show all debugging options (usage: --help -help-debug)"));
strUsage += HelpMessageOpt("-logips", strprintf(_("Include IP addresses in debug output (default: %u)"), DEFAULT_LOGIPS));
strUsage += HelpMessageOpt("-logtimestamps", strprintf(_("Prepend debug output with timestamp (default: %u)"), DEFAULT_LOGTIMESTAMPS));
@@ -579,14 +577,14 @@ struct CImportingNow
// works correctly.
void CleanupBlockRevFiles()
{
- std::map<std::string, boost::filesystem::path> mapBlockFiles;
+ std::map<std::string, fs::path> mapBlockFiles;
// Glob all blk?????.dat and rev?????.dat files from the blocks directory.
// Remove the rev files immediately and insert the blk file paths into an
// ordered map keyed by block file index.
LogPrintf("Removing unusable blk?????.dat and rev?????.dat files for -reindex with -prune\n");
- boost::filesystem::path blocksdir = GetDataDir() / "blocks";
- for (boost::filesystem::directory_iterator it(blocksdir); it != boost::filesystem::directory_iterator(); it++) {
+ fs::path blocksdir = GetDataDir() / "blocks";
+ for (fs::directory_iterator it(blocksdir); it != fs::directory_iterator(); it++) {
if (is_regular_file(*it) &&
it->path().filename().string().length() == 12 &&
it->path().filename().string().substr(8,4) == ".dat")
@@ -603,7 +601,7 @@ void CleanupBlockRevFiles()
// keeping a separate counter. Once we hit a gap (or if 0 doesn't exist)
// start removing block files.
int nContigCounter = 0;
- BOOST_FOREACH(const PAIRTYPE(std::string, boost::filesystem::path)& item, mapBlockFiles) {
+ BOOST_FOREACH(const PAIRTYPE(std::string, fs::path)& item, mapBlockFiles) {
if (atoi(item.first) == nContigCounter) {
nContigCounter++;
continue;
@@ -612,7 +610,7 @@ void CleanupBlockRevFiles()
}
}
-void ThreadImport(std::vector<boost::filesystem::path> vImportFiles)
+void ThreadImport(std::vector<fs::path> vImportFiles)
{
const CChainParams& chainparams = Params();
RenameThread("bitcoin-loadblk");
@@ -625,7 +623,7 @@ void ThreadImport(std::vector<boost::filesystem::path> vImportFiles)
int nFile = 0;
while (true) {
CDiskBlockPos pos(nFile, 0);
- if (!boost::filesystem::exists(GetBlockPosFilename(pos, "blk")))
+ if (!fs::exists(GetBlockPosFilename(pos, "blk")))
break; // No block files left to reindex
FILE *file = OpenBlockFile(pos, true);
if (!file)
@@ -642,11 +640,11 @@ void ThreadImport(std::vector<boost::filesystem::path> vImportFiles)
}
// hardcoded $DATADIR/bootstrap.dat
- boost::filesystem::path pathBootstrap = GetDataDir() / "bootstrap.dat";
- if (boost::filesystem::exists(pathBootstrap)) {
- FILE *file = fopen(pathBootstrap.string().c_str(), "rb");
+ fs::path pathBootstrap = GetDataDir() / "bootstrap.dat";
+ if (fs::exists(pathBootstrap)) {
+ FILE *file = fsbridge::fopen(pathBootstrap, "rb");
if (file) {
- boost::filesystem::path pathBootstrapOld = GetDataDir() / "bootstrap.dat.old";
+ fs::path pathBootstrapOld = GetDataDir() / "bootstrap.dat.old";
LogPrintf("Importing bootstrap.dat...\n");
LoadExternalBlockFile(chainparams, file);
RenameOver(pathBootstrap, pathBootstrapOld);
@@ -656,8 +654,8 @@ void ThreadImport(std::vector<boost::filesystem::path> vImportFiles)
}
// -loadblock=
- BOOST_FOREACH(const boost::filesystem::path& path, vImportFiles) {
- FILE *file = fopen(path.string().c_str(), "rb");
+ BOOST_FOREACH(const fs::path& path, vImportFiles) {
+ FILE *file = fsbridge::fopen(path, "rb");
if (file) {
LogPrintf("Importing blocks file %s...\n", path.string());
LoadExternalBlockFile(chainparams, file);
@@ -906,16 +904,16 @@ bool AppInitParameterInteraction()
InitWarning(strprintf(_("Reducing -maxconnections from %d to %d, because of system limitations."), nUserMaxConnections, nMaxConnections));
// ********************************************************* Step 3: parameter-to-internal-flags
-
if (mapMultiArgs.count("-debug") > 0) {
// Special-case: if -debug=0/-nodebug is set, turn off debugging messages
const std::vector<std::string>& categories = mapMultiArgs.at("-debug");
- if (!(GetBoolArg("-nodebug", false) || find(categories.begin(), categories.end(), std::string("0")) != categories.end())) {
+ if (find(categories.begin(), categories.end(), std::string("0")) == categories.end()) {
for (const auto& cat : categories) {
- uint32_t flag;
+ uint32_t flag = 0;
if (!GetLogCategory(&flag, &cat)) {
InitWarning(strprintf(_("Unsupported logging category %s=%s."), "-debug", cat));
+ continue;
}
logCategories |= flag;
}
@@ -926,9 +924,10 @@ bool AppInitParameterInteraction()
if (mapMultiArgs.count("-debugexclude") > 0) {
const std::vector<std::string>& excludedCategories = mapMultiArgs.at("-debugexclude");
for (const auto& cat : excludedCategories) {
- uint32_t flag;
+ uint32_t flag = 0;
if (!GetLogCategory(&flag, &cat)) {
InitWarning(strprintf(_("Unsupported logging category %s=%s."), "-debugexclude", cat));
+ continue;
}
logCategories &= ~flag;
}
@@ -1136,8 +1135,8 @@ static bool LockDataDirectory(bool probeOnly)
std::string strDataDir = GetDataDir().string();
// Make sure only a single Bitcoin process is using the data directory.
- boost::filesystem::path pathLockFile = GetDataDir() / ".lock";
- FILE* file = fopen(pathLockFile.string().c_str(), "a"); // empty lock file; created if it doesn't exist.
+ fs::path pathLockFile = GetDataDir() / ".lock";
+ FILE* file = fsbridge::fopen(pathLockFile, "a"); // empty lock file; created if it doesn't exist.
if (file) fclose(file);
try {
@@ -1185,7 +1184,7 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler)
#ifndef WIN32
CreatePidFile(GetPidFile(), getpid());
#endif
- if (GetBoolArg("-shrinkdebugfile", logCategories != BCLog::NONE)) {
+ if (GetBoolArg("-shrinkdebugfile", logCategories == BCLog::NONE)) {
// Do this first since it both loads a bunch of debug.log into memory,
// and because this needs to happen before any other debug.log printing
ShrinkDebugFile();
@@ -1401,7 +1400,7 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler)
fReindex = GetBoolArg("-reindex", false);
bool fReindexChainState = GetBoolArg("-reindex-chainstate", false);
- boost::filesystem::create_directories(GetDataDir() / "blocks");
+ fs::create_directories(GetDataDir() / "blocks");
// cache size calculations
int64_t nTotalCache = (GetArg("-dbcache", nDefaultDbCache) << 20);
@@ -1547,8 +1546,8 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler)
}
LogPrintf(" block index %15dms\n", GetTimeMillis() - nStart);
- boost::filesystem::path est_path = GetDataDir() / FEE_ESTIMATES_FILENAME;
- CAutoFile est_filein(fopen(est_path.string().c_str(), "rb"), SER_DISK, CLIENT_VERSION);
+ fs::path est_path = GetDataDir() / FEE_ESTIMATES_FILENAME;
+ CAutoFile est_filein(fsbridge::fopen(est_path, "rb"), SER_DISK, CLIENT_VERSION);
// Allowed to fail as this file IS missing on first startup.
if (!est_filein.IsNull())
mempool.ReadFeeEstimates(est_filein);
@@ -1603,7 +1602,7 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler)
if (IsArgSet("-blocknotify"))
uiInterface.NotifyBlockTip.connect(BlockNotifyCallback);
- std::vector<boost::filesystem::path> vImportFiles;
+ std::vector<fs::path> vImportFiles;
if (mapMultiArgs.count("-loadblock"))
{
BOOST_FOREACH(const std::string& strFile, mapMultiArgs.at("-loadblock"))
diff --git a/src/net.h b/src/net.h
index d52b9b7ae4..91b2685e48 100644
--- a/src/net.h
+++ b/src/net.h
@@ -11,6 +11,7 @@
#include "amount.h"
#include "bloom.h"
#include "compat.h"
+#include "fs.h"
#include "hash.h"
#include "limitedmap.h"
#include "netaddress.h"
@@ -32,7 +33,6 @@
#include <arpa/inet.h>
#endif
-#include <boost/filesystem/path.hpp>
#include <boost/foreach.hpp>
#include <boost/signals2/signal.hpp>
diff --git a/src/net_processing.cpp b/src/net_processing.cpp
index 17653f542d..d881b8ddda 100644
--- a/src/net_processing.cpp
+++ b/src/net_processing.cpp
@@ -744,21 +744,23 @@ PeerLogicValidation::PeerLogicValidation(CConnman* connmanIn) : connman(connmanI
recentRejects.reset(new CRollingBloomFilter(120000, 0.000001));
}
-void PeerLogicValidation::SyncTransaction(const CTransaction& tx, const CBlockIndex* pindex, int nPosInBlock) {
- if (nPosInBlock == CMainSignals::SYNC_TRANSACTION_NOT_IN_BLOCK)
- return;
-
+void PeerLogicValidation::BlockConnected(const std::shared_ptr<const CBlock>& pblock, const CBlockIndex* pindex, const std::vector<CTransactionRef>& vtxConflicted) {
LOCK(cs_main);
std::vector<uint256> vOrphanErase;
- // Which orphan pool entries must we evict?
- for (size_t j = 0; j < tx.vin.size(); j++) {
- auto itByPrev = mapOrphanTransactionsByPrev.find(tx.vin[j].prevout);
- if (itByPrev == mapOrphanTransactionsByPrev.end()) continue;
- for (auto mi = itByPrev->second.begin(); mi != itByPrev->second.end(); ++mi) {
- const CTransaction& orphanTx = *(*mi)->second.tx;
- const uint256& orphanHash = orphanTx.GetHash();
- vOrphanErase.push_back(orphanHash);
+
+ for (const CTransactionRef& ptx : pblock->vtx) {
+ const CTransaction& tx = *ptx;
+
+ // Which orphan pool entries must we evict?
+ for (size_t j = 0; j < tx.vin.size(); j++) {
+ auto itByPrev = mapOrphanTransactionsByPrev.find(tx.vin[j].prevout);
+ if (itByPrev == mapOrphanTransactionsByPrev.end()) continue;
+ for (auto mi = itByPrev->second.begin(); mi != itByPrev->second.end(); ++mi) {
+ const CTransaction& orphanTx = *(*mi)->second.tx;
+ const uint256& orphanHash = orphanTx.GetHash();
+ vOrphanErase.push_back(orphanHash);
+ }
}
}
@@ -856,8 +858,8 @@ void PeerLogicValidation::BlockChecked(const CBlock& block, const CValidationSta
int nDoS = 0;
if (state.IsInvalid(nDoS)) {
- if (it != mapBlockSource.end() && State(it->second.first)) {
- assert (state.GetRejectCode() < REJECT_INTERNAL); // Blocks are never rejected with internal reject codes
+ // Don't send reject message with code 0 or an internal reject code.
+ if (it != mapBlockSource.end() && State(it->second.first) && state.GetRejectCode() > 0 && state.GetRejectCode() < REJECT_INTERNAL) {
CBlockReject reject = {(unsigned char)state.GetRejectCode(), state.GetRejectReason().substr(0, MAX_REJECT_MESSAGE_LENGTH), hash};
State(it->second.first)->rejects.push_back(reject);
if (nDoS > 0 && it->second.second)
@@ -1945,7 +1947,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
LogPrint(BCLog::MEMPOOLREJ, "%s from peer=%d was not accepted: %s\n", tx.GetHash().ToString(),
pfrom->id,
FormatStateMessage(state));
- if (state.GetRejectCode() < REJECT_INTERNAL) // Never send AcceptToMemoryPool's internal codes over P2P
+ if (state.GetRejectCode() > 0 && state.GetRejectCode() < REJECT_INTERNAL) // Never send AcceptToMemoryPool's internal codes over P2P
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) {
diff --git a/src/net_processing.h b/src/net_processing.h
index 9e3f1b7156..f460595bc1 100644
--- a/src/net_processing.h
+++ b/src/net_processing.h
@@ -30,10 +30,10 @@ private:
public:
PeerLogicValidation(CConnman* connmanIn);
- virtual void SyncTransaction(const CTransaction& tx, const CBlockIndex* pindex, int nPosInBlock);
- virtual void UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockIndex *pindexFork, bool fInitialDownload);
- virtual void BlockChecked(const CBlock& block, const CValidationState& state);
- virtual void NewPoWValidBlock(const CBlockIndex *pindex, const std::shared_ptr<const CBlock>& pblock);
+ void BlockConnected(const std::shared_ptr<const CBlock>& pblock, const CBlockIndex* pindexConnected, const std::vector<CTransactionRef>& vtxConflicted) override;
+ void UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockIndex *pindexFork, bool fInitialDownload) override;
+ void BlockChecked(const CBlock& block, const CValidationState& state) override;
+ void NewPoWValidBlock(const CBlockIndex *pindex, const std::shared_ptr<const CBlock>& pblock) override;
};
struct CNodeStateStats {
diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp
index 05a3bd71fd..23ec3ab434 100644
--- a/src/qt/bitcoin.cpp
+++ b/src/qt/bitcoin.cpp
@@ -10,6 +10,7 @@
#include "chainparams.h"
#include "clientmodel.h"
+#include "fs.h"
#include "guiconstants.h"
#include "guiutil.h"
#include "intro.h"
@@ -38,7 +39,6 @@
#include <stdint.h>
-#include <boost/filesystem/operations.hpp>
#include <boost/thread.hpp>
#include <QApplication>
@@ -608,7 +608,7 @@ int main(int argc, char *argv[])
/// 6. Determine availability of data directory and parse bitcoin.conf
/// - Do not call GetDataDir(true) before this step finishes
- if (!boost::filesystem::is_directory(GetDataDir(false)))
+ if (!fs::is_directory(GetDataDir(false)))
{
QMessageBox::critical(0, QObject::tr(PACKAGE_NAME),
QObject::tr("Error: Specified data directory \"%1\" does not exist.").arg(QString::fromStdString(GetArg("-datadir", ""))));
diff --git a/src/qt/forms/optionsdialog.ui b/src/qt/forms/optionsdialog.ui
index 0b29201872..0f1b3f4a73 100644
--- a/src/qt/forms/optionsdialog.ui
+++ b/src/qt/forms/optionsdialog.ui
@@ -692,17 +692,34 @@
<item>
<layout class="QHBoxLayout" name="horizontalLayout_Buttons">
<item>
- <widget class="QPushButton" name="resetButton">
- <property name="toolTip">
- <string>Reset all client options to default.</string>
- </property>
- <property name="text">
- <string>&amp;Reset Options</string>
- </property>
- <property name="autoDefault">
- <bool>false</bool>
- </property>
- </widget>
+ <layout class="QVBoxLayout" name="verticalLayout_Buttons">
+ <item>
+ <widget class="QPushButton" name="openBitcoinConfButton">
+ <property name="toolTip">
+ <string>Open the %1 configuration file from the working directory.</string>
+ </property>
+ <property name="text">
+ <string>Open Configuration File</string>
+ </property>
+ <property name="autoDefault">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="resetButton">
+ <property name="toolTip">
+ <string>Reset all client options to default.</string>
+ </property>
+ <property name="text">
+ <string>&amp;Reset Options</string>
+ </property>
+ <property name="autoDefault">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
</item>
<item>
<spacer name="horizontalSpacer_1">
@@ -756,27 +773,48 @@
</spacer>
</item>
<item>
- <widget class="QPushButton" name="okButton">
- <property name="text">
- <string>&amp;OK</string>
- </property>
- <property name="autoDefault">
- <bool>false</bool>
- </property>
- <property name="default">
- <bool>true</bool>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QPushButton" name="cancelButton">
- <property name="text">
- <string>&amp;Cancel</string>
- </property>
- <property name="autoDefault">
- <bool>false</bool>
- </property>
- </widget>
+ <layout class="QVBoxLayout" name="verticalLayout_4">
+ <item>
+ <spacer name="verticalSpacer">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <item>
+ <widget class="QPushButton" name="okButton">
+ <property name="text">
+ <string>&amp;OK</string>
+ </property>
+ <property name="autoDefault">
+ <bool>false</bool>
+ </property>
+ <property name="default">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="cancelButton">
+ <property name="text">
+ <string>&amp;Cancel</string>
+ </property>
+ <property name="autoDefault">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
</item>
</layout>
</item>
diff --git a/src/qt/forms/sendcoinsdialog.ui b/src/qt/forms/sendcoinsdialog.ui
index cc183908d4..52256ca5c4 100644
--- a/src/qt/forms/sendcoinsdialog.ui
+++ b/src/qt/forms/sendcoinsdialog.ui
@@ -1235,7 +1235,7 @@
<bool>false</bool>
</property>
<property name="default">
- <bool>true</bool>
+ <bool>false</bool>
</property>
</widget>
</item>
diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp
index bb5b2d4347..3f3f9b9ccb 100644
--- a/src/qt/guiutil.cpp
+++ b/src/qt/guiutil.cpp
@@ -9,6 +9,7 @@
#include "qvalidatedlineedit.h"
#include "walletmodel.h"
+#include "fs.h"
#include "primitives/transaction.h"
#include "init.h"
#include "policy/policy.h"
@@ -35,9 +36,6 @@
#include "shlwapi.h"
#endif
-#include <boost/filesystem.hpp>
-#include <boost/filesystem/fstream.hpp>
-#include <boost/filesystem/detail/utf8_codecvt_facet.hpp>
#include <boost/scoped_array.hpp>
#include <QAbstractItemView>
@@ -65,7 +63,7 @@
#include <QFontDatabase>
#endif
-static boost::filesystem::detail::utf8_codecvt_facet utf8;
+static fs::detail::utf8_codecvt_facet utf8;
#if defined(Q_OS_MAC)
extern double NSAppKitVersionNumber;
@@ -410,13 +408,29 @@ bool isObscured(QWidget *w)
void openDebugLogfile()
{
- boost::filesystem::path pathDebug = GetDataDir() / "debug.log";
+ fs::path pathDebug = GetDataDir() / "debug.log";
/* Open debug.log with the associated application */
- if (boost::filesystem::exists(pathDebug))
+ if (fs::exists(pathDebug))
QDesktopServices::openUrl(QUrl::fromLocalFile(boostPathToQString(pathDebug)));
}
+bool openBitcoinConf()
+{
+ boost::filesystem::path pathConfig = GetConfigFile(BITCOIN_CONF_FILENAME);
+
+ /* Create the file */
+ boost::filesystem::ofstream configFile(pathConfig, std::ios_base::app);
+
+ if (!configFile.good())
+ return false;
+
+ configFile.close();
+
+ /* Open bitcoin.conf with the associated application */
+ return QDesktopServices::openUrl(QUrl::fromLocalFile(boostPathToQString(pathConfig)));
+}
+
void SubstituteFonts(const QString& language)
{
#if defined(Q_OS_MAC)
@@ -597,7 +611,7 @@ TableViewLastColumnResizingFixer::TableViewLastColumnResizingFixer(QTableView* t
}
#ifdef WIN32
-boost::filesystem::path static StartupShortcutPath()
+fs::path static StartupShortcutPath()
{
std::string chain = ChainNameFromCommandLine();
if (chain == CBaseChainParams::MAIN)
@@ -610,13 +624,13 @@ boost::filesystem::path static StartupShortcutPath()
bool GetStartOnSystemStartup()
{
// check for Bitcoin*.lnk
- return boost::filesystem::exists(StartupShortcutPath());
+ return fs::exists(StartupShortcutPath());
}
bool SetStartOnSystemStartup(bool fAutoStart)
{
// If the shortcut exists already, remove it for updating
- boost::filesystem::remove(StartupShortcutPath());
+ fs::remove(StartupShortcutPath());
if (fAutoStart)
{
@@ -686,10 +700,8 @@ bool SetStartOnSystemStartup(bool fAutoStart)
// Follow the Desktop Application Autostart Spec:
// http://standards.freedesktop.org/autostart-spec/autostart-spec-latest.html
-boost::filesystem::path static GetAutostartDir()
+fs::path static GetAutostartDir()
{
- namespace fs = boost::filesystem;
-
char* pszConfigHome = getenv("XDG_CONFIG_HOME");
if (pszConfigHome) return fs::path(pszConfigHome) / "autostart";
char* pszHome = getenv("HOME");
@@ -697,7 +709,7 @@ boost::filesystem::path static GetAutostartDir()
return fs::path();
}
-boost::filesystem::path static GetAutostartFilePath()
+fs::path static GetAutostartFilePath()
{
std::string chain = ChainNameFromCommandLine();
if (chain == CBaseChainParams::MAIN)
@@ -707,7 +719,7 @@ boost::filesystem::path static GetAutostartFilePath()
bool GetStartOnSystemStartup()
{
- boost::filesystem::ifstream optionFile(GetAutostartFilePath());
+ fs::ifstream optionFile(GetAutostartFilePath());
if (!optionFile.good())
return false;
// Scan through file for "Hidden=true":
@@ -727,7 +739,7 @@ bool GetStartOnSystemStartup()
bool SetStartOnSystemStartup(bool fAutoStart)
{
if (!fAutoStart)
- boost::filesystem::remove(GetAutostartFilePath());
+ fs::remove(GetAutostartFilePath());
else
{
char pszExePath[MAX_PATH+1];
@@ -735,9 +747,9 @@ bool SetStartOnSystemStartup(bool fAutoStart)
if (readlink("/proc/self/exe", pszExePath, sizeof(pszExePath)-1) == -1)
return false;
- boost::filesystem::create_directories(GetAutostartDir());
+ fs::create_directories(GetAutostartDir());
- boost::filesystem::ofstream optionFile(GetAutostartFilePath(), std::ios_base::out|std::ios_base::trunc);
+ fs::ofstream optionFile(GetAutostartFilePath(), std::ios_base::out|std::ios_base::trunc);
if (!optionFile.good())
return false;
std::string chain = ChainNameFromCommandLine();
@@ -843,14 +855,17 @@ void restoreWindowGeometry(const QString& strSetting, const QSize& defaultSize,
QPoint pos = settings.value(strSetting + "Pos").toPoint();
QSize size = settings.value(strSetting + "Size", defaultSize).toSize();
- if (!pos.x() && !pos.y()) {
- QRect screen = QApplication::desktop()->screenGeometry();
- pos.setX((screen.width() - size.width()) / 2);
- pos.setY((screen.height() - size.height()) / 2);
- }
-
parent->resize(size);
parent->move(pos);
+
+ if ((!pos.x() && !pos.y()) || (QApplication::desktop()->screenNumber(parent) == -1))
+ {
+ QRect screen = QApplication::desktop()->screenGeometry();
+ QPoint defaultPos((screen.width() - defaultSize.width()) / 2,
+ (screen.height() - defaultSize.height()) / 2);
+ parent->resize(defaultSize);
+ parent->move(defaultPos);
+ }
}
void setClipboard(const QString& str)
@@ -859,12 +874,12 @@ void setClipboard(const QString& str)
QApplication::clipboard()->setText(str, QClipboard::Selection);
}
-boost::filesystem::path qstringToBoostPath(const QString &path)
+fs::path qstringToBoostPath(const QString &path)
{
- return boost::filesystem::path(path.toStdString(), utf8);
+ return fs::path(path.toStdString(), utf8);
}
-QString boostPathToQString(const boost::filesystem::path &path)
+QString boostPathToQString(const fs::path &path)
{
return QString::fromStdString(path.string(utf8));
}
diff --git a/src/qt/guiutil.h b/src/qt/guiutil.h
index 913aa5e24b..d6aa8c4ea6 100644
--- a/src/qt/guiutil.h
+++ b/src/qt/guiutil.h
@@ -6,6 +6,7 @@
#define BITCOIN_QT_GUIUTIL_H
#include "amount.h"
+#include "fs.h"
#include <QEvent>
#include <QHeaderView>
@@ -16,8 +17,6 @@
#include <QTableView>
#include <QLabel>
-#include <boost/filesystem.hpp>
-
class QValidatedLineEdit;
class SendCoinsRecipient;
@@ -114,6 +113,9 @@ namespace GUIUtil
// Open debug.log
void openDebugLogfile();
+ // Open the config file
+ bool openBitcoinConf();
+
// Replace invalid default fonts with known good ones
void SubstituteFonts(const QString& language);
@@ -183,10 +185,10 @@ namespace GUIUtil
void restoreWindowGeometry(const QString& strSetting, const QSize &defaultSizeIn, QWidget *parent);
/* Convert QString to OS specific boost path through UTF-8 */
- boost::filesystem::path qstringToBoostPath(const QString &path);
+ fs::path qstringToBoostPath(const QString &path);
/* Convert OS specific boost path to QString through UTF-8 */
- QString boostPathToQString(const boost::filesystem::path &path);
+ QString boostPathToQString(const fs::path &path);
/* Convert seconds into a QString with days, hours, mins, secs */
QString formatDurationStr(int secs);
diff --git a/src/qt/intro.cpp b/src/qt/intro.cpp
index 4939648ff0..2460a59109 100644
--- a/src/qt/intro.cpp
+++ b/src/qt/intro.cpp
@@ -6,6 +6,7 @@
#include "config/bitcoin-config.h"
#endif
+#include "fs.h"
#include "intro.h"
#include "ui_intro.h"
@@ -13,8 +14,6 @@
#include "util.h"
-#include <boost/filesystem.hpp>
-
#include <QFileDialog>
#include <QSettings>
#include <QMessageBox>
@@ -70,7 +69,6 @@ FreespaceChecker::FreespaceChecker(Intro *_intro)
void FreespaceChecker::check()
{
- namespace fs = boost::filesystem;
QString dataDirStr = intro->getPathToCheck();
fs::path dataDir = GUIUtil::qstringToBoostPath(dataDirStr);
uint64_t freeBytesAvailable = 0;
@@ -190,7 +188,6 @@ QString Intro::getDefaultDataDirectory()
bool Intro::pickDataDirectory()
{
- namespace fs = boost::filesystem;
QSettings settings;
/* If data directory provided on command line, no need to look at settings
or show a picking dialog */
diff --git a/src/qt/optionsdialog.cpp b/src/qt/optionsdialog.cpp
index 7ff00b1e9e..efb25aaf18 100644
--- a/src/qt/optionsdialog.cpp
+++ b/src/qt/optionsdialog.cpp
@@ -232,6 +232,18 @@ void OptionsDialog::on_resetButton_clicked()
}
}
+void OptionsDialog::on_openBitcoinConfButton_clicked()
+{
+ /* explain the purpose of the config file */
+ QMessageBox::information(this, tr("Configuration options"),
+ tr("The configuration file is used to specify advanced user options which override GUI settings. "
+ "Additionally, any command-line options will override this configuration file."));
+
+ /* show an error if there was some problem opening the file */
+ if (!GUIUtil::openBitcoinConf())
+ QMessageBox::critical(this, tr("Error"), tr("The configuration file could not be opened."));
+}
+
void OptionsDialog::on_okButton_clicked()
{
mapper->submit();
diff --git a/src/qt/optionsdialog.h b/src/qt/optionsdialog.h
index d98c1dc193..f9f5823c05 100644
--- a/src/qt/optionsdialog.h
+++ b/src/qt/optionsdialog.h
@@ -47,6 +47,7 @@ private Q_SLOTS:
/* set OK button state (enabled / disabled) */
void setOkButtonState(bool fState);
void on_resetButton_clicked();
+ void on_openBitcoinConfButton_clicked();
void on_okButton_clicked();
void on_cancelButton_clicked();
diff --git a/src/qt/test/rpcnestedtests.cpp b/src/qt/test/rpcnestedtests.cpp
index a7b82117d8..dada689731 100644
--- a/src/qt/test/rpcnestedtests.cpp
+++ b/src/qt/test/rpcnestedtests.cpp
@@ -6,6 +6,7 @@
#include "chainparams.h"
#include "consensus/validation.h"
+#include "fs.h"
#include "validation.h"
#include "rpc/register.h"
#include "rpc/server.h"
@@ -17,8 +18,6 @@
#include <QDir>
#include <QtGlobal>
-#include <boost/filesystem.hpp>
-
static UniValue rpcNestedTest_rpc(const JSONRPCRequest& request)
{
if (request.fHelp) {
@@ -156,5 +155,5 @@ void RPCNestedTests::rpcNestedTests()
delete pblocktree;
pblocktree = nullptr;
- boost::filesystem::remove_all(boost::filesystem::path(path));
+ fs::remove_all(fs::path(path));
}
diff --git a/src/qt/test/test_main.cpp b/src/qt/test/test_main.cpp
index d8bcfedb7c..cae18f41a5 100644
--- a/src/qt/test/test_main.cpp
+++ b/src/qt/test/test_main.cpp
@@ -31,6 +31,9 @@ Q_IMPORT_PLUGIN(qjpcodecs)
Q_IMPORT_PLUGIN(qtwcodecs)
Q_IMPORT_PLUGIN(qkrcodecs)
#else
+#if defined(QT_QPA_PLATFORM_MINIMAL)
+Q_IMPORT_PLUGIN(QMinimalIntegrationPlugin);
+#endif
#if defined(QT_QPA_PLATFORM_XCB)
Q_IMPORT_PLUGIN(QXcbIntegrationPlugin);
#elif defined(QT_QPA_PLATFORM_WINDOWS)
@@ -53,6 +56,11 @@ int main(int argc, char *argv[])
bool fInvalid = false;
+ // Prefer the "minimal" platform for the test instead of the normal default
+ // platform ("xcb", "windows", or "cocoa") so tests can't unintentially
+ // interfere with any background GUIs and don't require extra resources.
+ setenv("QT_QPA_PLATFORM", "minimal", 0);
+
// Don't remove this, it's needed to access
// QApplication:: and QCoreApplication:: in the tests
QApplication app(argc, argv);
diff --git a/src/qt/test/wallettests.cpp b/src/qt/test/wallettests.cpp
index b7c1e4c4d5..f794b6b382 100644
--- a/src/qt/test/wallettests.cpp
+++ b/src/qt/test/wallettests.cpp
@@ -68,6 +68,18 @@ QModelIndex FindTx(const QAbstractItemModel& model, const uint256& txid)
}
//! Simple qt wallet tests.
+//
+// Test widgets can be debugged interactively calling show() on them and
+// manually running the event loop, e.g.:
+//
+// sendCoinsDialog.show();
+// QEventLoop().exec();
+//
+// This also requires overriding the default minimal Qt platform:
+//
+// src/qt/test/test_bitcoin-qt -platform xcb # Linux
+// src/qt/test/test_bitcoin-qt -platform windows # Windows
+// src/qt/test/test_bitcoin-qt -platform cocoa # macOS
void WalletTests::walletTests()
{
// Set up wallet and chain with 101 blocks (1 mature block for spending).
diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp
index 35bc5d6a82..1f3c4e52ad 100644
--- a/src/rpc/client.cpp
+++ b/src/rpc/client.cpp
@@ -113,6 +113,8 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "getmempoolancestors", 1, "verbose" },
{ "getmempooldescendants", 1, "verbose" },
{ "bumpfee", 1, "options" },
+ { "logging", 0, "include" },
+ { "logging", 1, "exclude" },
// Echo with conversion (For testing only)
{ "echojson", 0, "arg0" },
{ "echojson", 1, "arg1" },
diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp
index b823c159d3..d234bb69ae 100644
--- a/src/rpc/mining.cpp
+++ b/src/rpc/mining.cpp
@@ -27,7 +27,6 @@
#include <stdint.h>
#include <boost/assign/list_of.hpp>
-#include <boost/shared_ptr.hpp>
#include <univalue.h>
@@ -95,7 +94,7 @@ UniValue getnetworkhashps(const JSONRPCRequest& request)
return GetNetworkHashPS(request.params.size() > 0 ? request.params[0].get_int() : 120, request.params.size() > 1 ? request.params[1].get_int() : -1);
}
-UniValue generateBlocks(boost::shared_ptr<CReserveScript> coinbaseScript, int nGenerate, uint64_t nMaxTries, bool keepScript)
+UniValue generateBlocks(std::shared_ptr<CReserveScript> coinbaseScript, int nGenerate, uint64_t nMaxTries, bool keepScript)
{
static const int nInnerLoopCount = 0x10000;
int nHeightStart = 0;
@@ -167,7 +166,7 @@ UniValue generate(const JSONRPCRequest& request)
nMaxTries = request.params[1].get_int();
}
- boost::shared_ptr<CReserveScript> coinbaseScript;
+ std::shared_ptr<CReserveScript> coinbaseScript;
GetMainSignals().ScriptForMining(coinbaseScript);
// If the keypool is exhausted, no script is returned at all. Catch this.
@@ -208,7 +207,7 @@ UniValue generatetoaddress(const JSONRPCRequest& request)
if (!address.IsValid())
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Error: Invalid address");
- boost::shared_ptr<CReserveScript> coinbaseScript(new CReserveScript());
+ std::shared_ptr<CReserveScript> coinbaseScript = std::make_shared<CReserveScript>();
coinbaseScript->reserveScript = GetScriptForDestination(address.Get());
return generateBlocks(coinbaseScript, nGenerate, nMaxTries, false);
@@ -710,7 +709,7 @@ public:
submitblock_StateCatcher(const uint256 &hashIn) : hash(hashIn), found(false), state() {}
protected:
- virtual void BlockChecked(const CBlock& block, const CValidationState& stateIn) {
+ void BlockChecked(const CBlock& block, const CValidationState& stateIn) override {
if (block.GetHash() != hash)
return;
found = true;
@@ -720,7 +719,7 @@ protected:
UniValue submitblock(const JSONRPCRequest& request)
{
- if (request.fHelp || request.params.size() < 1 || request.params.size() > 2)
+ if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) {
throw std::runtime_error(
"submitblock \"hexdata\" ( \"jsonparametersobject\" )\n"
"\nAttempts to submit new block to network.\n"
@@ -738,11 +737,17 @@ UniValue submitblock(const JSONRPCRequest& request)
+ HelpExampleCli("submitblock", "\"mydata\"")
+ HelpExampleRpc("submitblock", "\"mydata\"")
);
+ }
std::shared_ptr<CBlock> blockptr = std::make_shared<CBlock>();
CBlock& block = *blockptr;
- if (!DecodeHexBlk(block, request.params[0].get_str()))
+ if (!DecodeHexBlk(block, request.params[0].get_str())) {
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Block decode failed");
+ }
+
+ if (block.vtx.empty() || !block.vtx[0]->IsCoinBase()) {
+ throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Block does not start with a coinbase");
+ }
uint256 hash = block.GetHash();
bool fBlockPresent = false;
@@ -751,10 +756,12 @@ UniValue submitblock(const JSONRPCRequest& request)
BlockMap::iterator mi = mapBlockIndex.find(hash);
if (mi != mapBlockIndex.end()) {
CBlockIndex *pindex = mi->second;
- if (pindex->IsValid(BLOCK_VALID_SCRIPTS))
+ if (pindex->IsValid(BLOCK_VALID_SCRIPTS)) {
return "duplicate";
- if (pindex->nStatus & BLOCK_FAILED_MASK)
+ }
+ if (pindex->nStatus & BLOCK_FAILED_MASK) {
return "duplicate-invalid";
+ }
// Otherwise, we might only have the header - process the block before returning
fBlockPresent = true;
}
@@ -772,14 +779,15 @@ UniValue submitblock(const JSONRPCRequest& request)
RegisterValidationInterface(&sc);
bool fAccepted = ProcessNewBlock(Params(), blockptr, true, NULL);
UnregisterValidationInterface(&sc);
- if (fBlockPresent)
- {
- if (fAccepted && !sc.found)
+ if (fBlockPresent) {
+ if (fAccepted && !sc.found) {
return "duplicate-inconclusive";
+ }
return "duplicate";
}
- if (!sc.found)
+ if (!sc.found) {
return "inconclusive";
+ }
return BIP22ValidationResult(sc.state);
}
diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp
index 24c5eeffe9..7b8aa572d7 100644
--- a/src/rpc/misc.cpp
+++ b/src/rpc/misc.cpp
@@ -7,6 +7,7 @@
#include "clientversion.h"
#include "init.h"
#include "validation.h"
+#include "httpserver.h"
#include "net.h"
#include "netbase.h"
#include "rpc/blockchain.h"
@@ -555,6 +556,73 @@ UniValue getmemoryinfo(const JSONRPCRequest& request)
}
}
+uint32_t getCategoryMask(UniValue cats) {
+ cats = cats.get_array();
+ uint32_t mask = 0;
+ for (unsigned int i = 0; i < cats.size(); ++i) {
+ uint32_t flag = 0;
+ std::string cat = cats[i].get_str();
+ if (!GetLogCategory(&flag, &cat)) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "unknown logging category " + cat);
+ }
+ mask |= flag;
+ }
+ return mask;
+}
+
+UniValue logging(const JSONRPCRequest& request)
+{
+ if (request.fHelp || request.params.size() > 2) {
+ throw std::runtime_error(
+ "logging [include,...] <exclude>\n"
+ "Gets and sets the logging configuration.\n"
+ "When called without an argument, returns the list of categories that are currently being debug logged.\n"
+ "When called with arguments, adds or removes categories from debug logging.\n"
+ "The valid logging categories are: " + ListLogCategories() + "\n"
+ "libevent logging is configured on startup and cannot be modified by this RPC during runtime."
+ "Arguments:\n"
+ "1. \"include\" (array of strings) add debug logging for these categories.\n"
+ "2. \"exclude\" (array of strings) remove debug logging for these categories.\n"
+ "\nResult: <categories> (string): a list of the logging categories that are active.\n"
+ "\nExamples:\n"
+ + HelpExampleCli("logging", "\"[\\\"all\\\"]\" \"[\\\"http\\\"]\"")
+ + HelpExampleRpc("logging", "[\"all\"], \"[libevent]\"")
+ );
+ }
+
+ uint32_t originalLogCategories = logCategories;
+ if (request.params.size() > 0 && request.params[0].isArray()) {
+ logCategories |= getCategoryMask(request.params[0]);
+ }
+
+ if (request.params.size() > 1 && request.params[1].isArray()) {
+ logCategories &= ~getCategoryMask(request.params[1]);
+ }
+
+ // Update libevent logging if BCLog::LIBEVENT has changed.
+ // If the library version doesn't allow it, UpdateHTTPServerLogging() returns false,
+ // in which case we should clear the BCLog::LIBEVENT flag.
+ // Throw an error if the user has explicitly asked to change only the libevent
+ // flag and it failed.
+ uint32_t changedLogCategories = originalLogCategories ^ logCategories;
+ if (changedLogCategories & BCLog::LIBEVENT) {
+ if (!UpdateHTTPServerLogging(logCategories & BCLog::LIBEVENT)) {
+ logCategories &= ~BCLog::LIBEVENT;
+ if (changedLogCategories == BCLog::LIBEVENT) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "libevent logging cannot be updated when using libevent before v2.1.1.");
+ }
+ }
+ }
+
+ UniValue result(UniValue::VOBJ);
+ std::vector<CLogCategoryActive> vLogCatActive = ListActiveLogCategories();
+ for (const auto& logCatActive : vLogCatActive) {
+ result.pushKV(logCatActive.category, logCatActive.active);
+ }
+
+ return result;
+}
+
UniValue echo(const JSONRPCRequest& request)
{
if (request.fHelp)
@@ -581,7 +649,8 @@ static const CRPCCommand commands[] =
/* Not shown in help */
{ "hidden", "setmocktime", &setmocktime, true, {"timestamp"}},
{ "hidden", "echo", &echo, true, {"arg0","arg1","arg2","arg3","arg4","arg5","arg6","arg7","arg8","arg9"}},
- { "hidden", "echojson", &echo, true, {"arg0","arg1","arg2","arg3","arg4","arg5","arg6","arg7","arg8","arg9"}},
+ { "hidden", "echojson", &echo, true, {"arg0","arg1","arg2","arg3","arg4","arg5","arg6","arg7","arg8","arg9"}},
+ { "hidden", "logging", &logging, true, {"include", "exclude"}},
};
void RegisterMiscRPCCommands(CRPCTable &t)
diff --git a/src/rpc/protocol.cpp b/src/rpc/protocol.cpp
index 2be1edb5a6..823a5775f6 100644
--- a/src/rpc/protocol.cpp
+++ b/src/rpc/protocol.cpp
@@ -66,9 +66,9 @@ static const std::string COOKIEAUTH_USER = "__cookie__";
/** Default name for auth cookie file */
static const std::string COOKIEAUTH_FILE = ".cookie";
-boost::filesystem::path GetAuthCookieFile()
+fs::path GetAuthCookieFile()
{
- boost::filesystem::path path(GetArg("-rpccookiefile", COOKIEAUTH_FILE));
+ fs::path path(GetArg("-rpccookiefile", COOKIEAUTH_FILE));
if (!path.is_complete()) path = GetDataDir() / path;
return path;
}
@@ -84,7 +84,7 @@ bool GenerateAuthCookie(std::string *cookie_out)
* these are set to 077 in init.cpp unless overridden with -sysperms.
*/
std::ofstream file;
- boost::filesystem::path filepath = GetAuthCookieFile();
+ fs::path filepath = GetAuthCookieFile();
file.open(filepath.string().c_str());
if (!file.is_open()) {
LogPrintf("Unable to open cookie authentication file %s for writing\n", filepath.string());
@@ -103,7 +103,7 @@ bool GetAuthCookie(std::string *cookie_out)
{
std::ifstream file;
std::string cookie;
- boost::filesystem::path filepath = GetAuthCookieFile();
+ fs::path filepath = GetAuthCookieFile();
file.open(filepath.string().c_str());
if (!file.is_open())
return false;
@@ -118,8 +118,8 @@ bool GetAuthCookie(std::string *cookie_out)
void DeleteAuthCookie()
{
try {
- boost::filesystem::remove(GetAuthCookieFile());
- } catch (const boost::filesystem::filesystem_error& e) {
+ fs::remove(GetAuthCookieFile());
+ } catch (const fs::filesystem_error& e) {
LogPrintf("%s: Unable to remove random auth cookie file: %s\n", __func__, e.what());
}
}
diff --git a/src/rpc/protocol.h b/src/rpc/protocol.h
index 85bc4db101..70f7092cfe 100644
--- a/src/rpc/protocol.h
+++ b/src/rpc/protocol.h
@@ -6,11 +6,12 @@
#ifndef BITCOIN_RPCPROTOCOL_H
#define BITCOIN_RPCPROTOCOL_H
+#include "fs.h"
+
#include <list>
#include <map>
#include <stdint.h>
#include <string>
-#include <boost/filesystem.hpp>
#include <univalue.h>
@@ -89,7 +90,7 @@ std::string JSONRPCReply(const UniValue& result, const UniValue& error, const Un
UniValue JSONRPCError(int code, const std::string& message);
/** Get name of RPC authentication cookie file */
-boost::filesystem::path GetAuthCookieFile();
+fs::path GetAuthCookieFile();
/** Generate a new RPC authentication cookie and write it to disk */
bool GenerateAuthCookie(std::string *cookie_out);
/** Read the RPC authentication cookie from disk */
diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp
index 141062b3c0..5c493428d8 100644
--- a/src/rpc/server.cpp
+++ b/src/rpc/server.cpp
@@ -6,6 +6,7 @@
#include "rpc/server.h"
#include "base58.h"
+#include "fs.h"
#include "init.h"
#include "random.h"
#include "sync.h"
@@ -16,7 +17,6 @@
#include <univalue.h>
#include <boost/bind.hpp>
-#include <boost/filesystem.hpp>
#include <boost/foreach.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/signals2/signal.hpp>
@@ -369,9 +369,7 @@ void JSONRPCRequest::parse(const UniValue& valRequest)
if (!valMethod.isStr())
throw JSONRPCError(RPC_INVALID_REQUEST, "Method must be a string");
strMethod = valMethod.get_str();
- if (strMethod != "getblocktemplate") {
- LogPrint(BCLog::RPC, "ThreadRPCServer method=%s\n", SanitizeString(strMethod));
- }
+ LogPrint(BCLog::RPC, "ThreadRPCServer method=%s\n", SanitizeString(strMethod));
// Parse params
UniValue valParams = find_value(request, "params");
diff --git a/src/test/dbwrapper_tests.cpp b/src/test/dbwrapper_tests.cpp
index 9d55beb8ea..c9d9849ada 100644
--- a/src/test/dbwrapper_tests.cpp
+++ b/src/test/dbwrapper_tests.cpp
@@ -28,7 +28,7 @@ BOOST_AUTO_TEST_CASE(dbwrapper)
// Perform tests both obfuscated and non-obfuscated.
for (int i = 0; i < 2; i++) {
bool obfuscate = (bool)i;
- boost::filesystem::path ph = boost::filesystem::temp_directory_path() / boost::filesystem::unique_path();
+ fs::path ph = fs::temp_directory_path() / fs::unique_path();
CDBWrapper dbw(ph, (1 << 20), true, false, obfuscate);
char key = 'k';
uint256 in = GetRandHash();
@@ -49,7 +49,7 @@ BOOST_AUTO_TEST_CASE(dbwrapper_batch)
// Perform tests both obfuscated and non-obfuscated.
for (int i = 0; i < 2; i++) {
bool obfuscate = (bool)i;
- boost::filesystem::path ph = boost::filesystem::temp_directory_path() / boost::filesystem::unique_path();
+ fs::path ph = fs::temp_directory_path() / fs::unique_path();
CDBWrapper dbw(ph, (1 << 20), true, false, obfuscate);
char key = 'i';
@@ -86,7 +86,7 @@ BOOST_AUTO_TEST_CASE(dbwrapper_iterator)
// Perform tests both obfuscated and non-obfuscated.
for (int i = 0; i < 2; i++) {
bool obfuscate = (bool)i;
- boost::filesystem::path ph = boost::filesystem::temp_directory_path() / boost::filesystem::unique_path();
+ fs::path ph = fs::temp_directory_path() / fs::unique_path();
CDBWrapper dbw(ph, (1 << 20), true, false, obfuscate);
// The two keys are intentionally chosen for ordering
@@ -125,8 +125,8 @@ BOOST_AUTO_TEST_CASE(dbwrapper_iterator)
// Test that we do not obfuscation if there is existing data.
BOOST_AUTO_TEST_CASE(existing_data_no_obfuscate)
{
- // We're going to share this boost::filesystem::path between two wrappers
- boost::filesystem::path ph = boost::filesystem::temp_directory_path() / boost::filesystem::unique_path();
+ // We're going to share this fs::path between two wrappers
+ fs::path ph = fs::temp_directory_path() / fs::unique_path();
create_directories(ph);
// Set up a non-obfuscated wrapper to write some initial data.
@@ -167,8 +167,8 @@ BOOST_AUTO_TEST_CASE(existing_data_no_obfuscate)
// Ensure that we start obfuscating during a reindex.
BOOST_AUTO_TEST_CASE(existing_data_reindex)
{
- // We're going to share this boost::filesystem::path between two wrappers
- boost::filesystem::path ph = boost::filesystem::temp_directory_path() / boost::filesystem::unique_path();
+ // We're going to share this fs::path between two wrappers
+ fs::path ph = fs::temp_directory_path() / fs::unique_path();
create_directories(ph);
// Set up a non-obfuscated wrapper to write some initial data.
@@ -204,7 +204,7 @@ BOOST_AUTO_TEST_CASE(existing_data_reindex)
BOOST_AUTO_TEST_CASE(iterator_ordering)
{
- boost::filesystem::path ph = boost::filesystem::temp_directory_path() / boost::filesystem::unique_path();
+ fs::path ph = fs::temp_directory_path() / fs::unique_path();
CDBWrapper dbw(ph, (1 << 20), true, false, false);
for (int x=0x00; x<256; ++x) {
uint8_t key = x;
@@ -275,7 +275,7 @@ BOOST_AUTO_TEST_CASE(iterator_string_ordering)
{
char buf[10];
- boost::filesystem::path ph = boost::filesystem::temp_directory_path() / boost::filesystem::unique_path();
+ fs::path ph = fs::temp_directory_path() / fs::unique_path();
CDBWrapper dbw(ph, (1 << 20), true, false, false);
for (int x=0x00; x<10; ++x) {
for (int y = 0; y < 10; y++) {
diff --git a/src/test/test_bitcoin.cpp b/src/test/test_bitcoin.cpp
index abaec45cd7..bda3819662 100644
--- a/src/test/test_bitcoin.cpp
+++ b/src/test/test_bitcoin.cpp
@@ -7,6 +7,7 @@
#include "chainparams.h"
#include "consensus/consensus.h"
#include "consensus/validation.h"
+#include "fs.h"
#include "key.h"
#include "validation.h"
#include "miner.h"
@@ -24,7 +25,6 @@
#include <memory>
-#include <boost/filesystem.hpp>
#include <boost/thread.hpp>
FastRandomContext insecure_rand_ctx(true);
@@ -59,7 +59,7 @@ TestingSetup::TestingSetup(const std::string& chainName) : BasicTestingSetup(cha
RegisterAllCoreRPCCommands(tableRPC);
ClearDatadirCache();
pathTemp = GetTempPath() / strprintf("test_bitcoin_%lu_%i", (unsigned long)GetTime(), (int)(GetRand(100000)));
- boost::filesystem::create_directories(pathTemp);
+ fs::create_directories(pathTemp);
ForceSetArg("-datadir", pathTemp.string());
mempool.setSanityCheck(1.0);
pblocktree = new CBlockTreeDB(1 << 20, true);
@@ -91,7 +91,7 @@ TestingSetup::~TestingSetup()
delete pcoinsTip;
delete pcoinsdbview;
delete pblocktree;
- boost::filesystem::remove_all(pathTemp);
+ fs::remove_all(pathTemp);
}
TestChain100Setup::TestChain100Setup() : TestingSetup(CBaseChainParams::REGTEST)
diff --git a/src/test/test_bitcoin.h b/src/test/test_bitcoin.h
index a593f136eb..60a86d8c48 100644
--- a/src/test/test_bitcoin.h
+++ b/src/test/test_bitcoin.h
@@ -6,12 +6,12 @@
#define BITCOIN_TEST_TEST_BITCOIN_H
#include "chainparamsbase.h"
+#include "fs.h"
#include "key.h"
#include "pubkey.h"
#include "txdb.h"
#include "txmempool.h"
-#include <boost/filesystem.hpp>
#include <boost/thread.hpp>
/** Basic testing setup.
@@ -30,7 +30,7 @@ struct BasicTestingSetup {
class CConnman;
struct TestingSetup: public BasicTestingSetup {
CCoinsViewDB *pcoinsdbview;
- boost::filesystem::path pathTemp;
+ fs::path pathTemp;
boost::thread_group threadGroup;
CConnman* connman;
diff --git a/src/test/testutil.cpp b/src/test/testutil.cpp
index e6d8622979..591d0bf302 100644
--- a/src/test/testutil.cpp
+++ b/src/test/testutil.cpp
@@ -8,8 +8,8 @@
#include <shlobj.h>
#endif
-#include <boost/filesystem.hpp>
+#include "fs.h"
-boost::filesystem::path GetTempPath() {
- return boost::filesystem::temp_directory_path();
+fs::path GetTempPath() {
+ return fs::temp_directory_path();
}
diff --git a/src/test/testutil.h b/src/test/testutil.h
index 5875dc50e6..cbe784d640 100644
--- a/src/test/testutil.h
+++ b/src/test/testutil.h
@@ -8,8 +8,8 @@
#ifndef BITCOIN_TEST_TESTUTIL_H
#define BITCOIN_TEST_TESTUTIL_H
-#include <boost/filesystem/path.hpp>
+#include "fs.h"
-boost::filesystem::path GetTempPath();
+fs::path GetTempPath();
#endif // BITCOIN_TEST_TESTUTIL_H
diff --git a/src/torcontrol.cpp b/src/torcontrol.cpp
index 4c88ebe1d5..c1bd95b00f 100644
--- a/src/torcontrol.cpp
+++ b/src/torcontrol.cpp
@@ -314,9 +314,9 @@ static std::map<std::string,std::string> ParseTorReplyMapping(const std::string
* @param maxsize Puts a maximum size limit on the file that is read. If the file is larger than this, truncated data
* (with len > maxsize) will be returned.
*/
-static std::pair<bool,std::string> ReadBinaryFile(const std::string &filename, size_t maxsize=std::numeric_limits<size_t>::max())
+static std::pair<bool,std::string> ReadBinaryFile(const fs::path &filename, size_t maxsize=std::numeric_limits<size_t>::max())
{
- FILE *f = fopen(filename.c_str(), "rb");
+ FILE *f = fsbridge::fopen(filename, "rb");
if (f == NULL)
return std::make_pair(false,"");
std::string retval;
@@ -334,9 +334,9 @@ static std::pair<bool,std::string> ReadBinaryFile(const std::string &filename, s
/** Write contents of std::string to a file.
* @return true on success.
*/
-static bool WriteBinaryFile(const std::string &filename, const std::string &data)
+static bool WriteBinaryFile(const fs::path &filename, const std::string &data)
{
- FILE *f = fopen(filename.c_str(), "wb");
+ FILE *f = fsbridge::fopen(filename, "wb");
if (f == NULL)
return false;
if (fwrite(data.data(), 1, data.size(), f) != data.size()) {
@@ -359,7 +359,7 @@ public:
~TorController();
/** Get name fo file to store private key in */
- std::string GetPrivateKeyFile();
+ fs::path GetPrivateKeyFile();
/** Reconnect, after getting disconnected */
void Reconnect();
@@ -411,7 +411,7 @@ TorController::TorController(struct event_base* _base, const std::string& _targe
// Read service private key if cached
std::pair<bool,std::string> pkf = ReadBinaryFile(GetPrivateKeyFile());
if (pkf.first) {
- LogPrint(BCLog::TOR, "tor: Reading cached private key from %s\n", GetPrivateKeyFile());
+ LogPrint(BCLog::TOR, "tor: Reading cached private key from %s\n", GetPrivateKeyFile().string());
private_key = pkf.second;
}
}
@@ -442,9 +442,9 @@ void TorController::add_onion_cb(TorControlConnection& _conn, const TorControlRe
service = LookupNumeric(std::string(service_id+".onion").c_str(), GetListenPort());
LogPrintf("tor: Got service ID %s, advertising service %s\n", service_id, service.ToString());
if (WriteBinaryFile(GetPrivateKeyFile(), private_key)) {
- LogPrint(BCLog::TOR, "tor: Cached service private key to %s\n", GetPrivateKeyFile());
+ LogPrint(BCLog::TOR, "tor: Cached service private key to %s\n", GetPrivateKeyFile().string());
} else {
- LogPrintf("tor: Error writing service private key to %s\n", GetPrivateKeyFile());
+ LogPrintf("tor: Error writing service private key to %s\n", GetPrivateKeyFile().string());
}
AddLocal(service, LOCAL_MANUAL);
// ... onion requested - keep connection open
@@ -651,9 +651,9 @@ void TorController::Reconnect()
}
}
-std::string TorController::GetPrivateKeyFile()
+fs::path TorController::GetPrivateKeyFile()
{
- return (GetDataDir() / "onion_private_key").string();
+ return GetDataDir() / "onion_private_key";
}
void TorController::reconnect_cb(evutil_socket_t fd, short what, void *arg)
diff --git a/src/txdb.h b/src/txdb.h
index 7f5cf2b583..d9214ba618 100644
--- a/src/txdb.h
+++ b/src/txdb.h
@@ -21,8 +21,14 @@ class CBlockIndex;
class CCoinsViewDBCursor;
class uint256;
+//! Compensate for extra memory peak (x1.5-x1.9) at flush time.
+static constexpr int DB_PEAK_USAGE_FACTOR = 2;
+//! No need to periodic flush if at least this much space still available.
+static constexpr int MAX_BLOCK_COINSDB_USAGE = 200 * DB_PEAK_USAGE_FACTOR;
+//! Always periodic flush if less than this much space still available.
+static constexpr int MIN_BLOCK_COINSDB_USAGE = 50 * DB_PEAK_USAGE_FACTOR;
//! -dbcache default (MiB)
-static const int64_t nDefaultDbCache = 300;
+static const int64_t nDefaultDbCache = 450;
//! max. -dbcache (MiB)
static const int64_t nMaxDbCache = sizeof(void*) > 4 ? 16384 : 1024;
//! min. -dbcache (MiB)
diff --git a/src/txmempool.cpp b/src/txmempool.cpp
index 36a046ed2a..0794a3902f 100644
--- a/src/txmempool.cpp
+++ b/src/txmempool.cpp
@@ -912,6 +912,13 @@ void CTxMemPool::PrioritiseTransaction(const uint256& hash, const CAmount& nFeeD
BOOST_FOREACH(txiter ancestorIt, setAncestors) {
mapTx.modify(ancestorIt, update_descendant_state(0, nFeeDelta, 0));
}
+ // Now update all descendants' modified fees with ancestors
+ setEntries setDescendants;
+ CalculateDescendants(it, setDescendants);
+ setDescendants.erase(it);
+ BOOST_FOREACH(txiter descendantIt, setDescendants) {
+ mapTx.modify(descendantIt, update_ancestor_state(0, nFeeDelta, 0, 0));
+ }
}
}
LogPrintf("PrioritiseTransaction: %s feerate += %s\n", hash.ToString(), FormatMoney(nFeeDelta));
diff --git a/src/util.cpp b/src/util.cpp
index db45ad1626..0dc203cba5 100644
--- a/src/util.cpp
+++ b/src/util.cpp
@@ -10,6 +10,7 @@
#include "util.h"
#include "chainparamsbase.h"
+#include "fs.h"
#include "random.h"
#include "serialize.h"
#include "sync.h"
@@ -79,8 +80,6 @@
#include <boost/algorithm/string/case_conv.hpp> // for to_lower()
#include <boost/algorithm/string/join.hpp>
#include <boost/algorithm/string/predicate.hpp> // for startswith() and endswith()
-#include <boost/filesystem.hpp>
-#include <boost/filesystem/fstream.hpp>
#include <boost/foreach.hpp>
#include <boost/program_options/detail/config_file.hpp>
#include <boost/program_options/parsers.hpp>
@@ -119,7 +118,7 @@ bool fLogIPs = DEFAULT_LOGIPS;
std::atomic<bool> fReopenDebugLog(false);
CTranslationInterface translationInterface;
-/** Log categories bitfield. Leveldb/libevent need special handling if their flags are changed at runtime. */
+/** Log categories bitfield. */
std::atomic<uint32_t> logCategories(0);
/** Init OpenSSL library multithreading support */
@@ -215,8 +214,8 @@ void OpenDebugLog()
assert(fileout == NULL);
assert(vMsgsBeforeOpenLog);
- boost::filesystem::path pathDebug = GetDataDir() / "debug.log";
- fileout = fopen(pathDebug.string().c_str(), "a");
+ fs::path pathDebug = GetDataDir() / "debug.log";
+ fileout = fsbridge::fopen(pathDebug, "a");
if (fileout) {
setbuf(fileout, NULL); // unbuffered
// dump buffered messages from before we opened the log
@@ -296,6 +295,21 @@ std::string ListLogCategories()
return ret;
}
+std::vector<CLogCategoryActive> ListActiveLogCategories()
+{
+ std::vector<CLogCategoryActive> ret;
+ for (unsigned int i = 0; i < ARRAYLEN(LogCategories); i++) {
+ // Omit the special cases.
+ if (LogCategories[i].flag != BCLog::NONE && LogCategories[i].flag != BCLog::ALL) {
+ CLogCategoryActive catActive;
+ catActive.category = LogCategories[i].category;
+ catActive.active = LogAcceptCategory(LogCategories[i].flag);
+ ret.push_back(catActive);
+ }
+ }
+ return ret;
+}
+
/**
* fStartedNewLine is a state variable held by the calling context that will
* suppress printing of the timestamp when multiple calls are made that don't
@@ -354,8 +368,8 @@ int LogPrintStr(const std::string &str)
// reopen the log file, if requested
if (fReopenDebugLog) {
fReopenDebugLog = false;
- boost::filesystem::path pathDebug = GetDataDir() / "debug.log";
- if (freopen(pathDebug.string().c_str(),"a",fileout) != NULL)
+ fs::path pathDebug = GetDataDir() / "debug.log";
+ if (fsbridge::freopen(pathDebug,"a",fileout) != NULL)
setbuf(fileout, NULL); // unbuffered
}
@@ -512,9 +526,8 @@ void PrintExceptionContinue(const std::exception* pex, const char* pszThread)
fprintf(stderr, "\n\n************************\n%s\n", message.c_str());
}
-boost::filesystem::path GetDefaultDataDir()
+fs::path GetDefaultDataDir()
{
- namespace fs = boost::filesystem;
// Windows < Vista: C:\Documents and Settings\Username\Application Data\Bitcoin
// Windows >= Vista: C:\Users\Username\AppData\Roaming\Bitcoin
// Mac: ~/Library/Application Support/Bitcoin
@@ -539,13 +552,12 @@ boost::filesystem::path GetDefaultDataDir()
#endif
}
-static boost::filesystem::path pathCached;
-static boost::filesystem::path pathCachedNetSpecific;
+static fs::path pathCached;
+static fs::path pathCachedNetSpecific;
static CCriticalSection csPathCached;
-const boost::filesystem::path &GetDataDir(bool fNetSpecific)
+const fs::path &GetDataDir(bool fNetSpecific)
{
- namespace fs = boost::filesystem;
LOCK(csPathCached);
@@ -577,13 +589,13 @@ void ClearDatadirCache()
{
LOCK(csPathCached);
- pathCached = boost::filesystem::path();
- pathCachedNetSpecific = boost::filesystem::path();
+ pathCached = fs::path();
+ pathCachedNetSpecific = fs::path();
}
-boost::filesystem::path GetConfigFile(const std::string& confPath)
+fs::path GetConfigFile(const std::string& confPath)
{
- boost::filesystem::path pathConfigFile(confPath);
+ fs::path pathConfigFile(confPath);
if (!pathConfigFile.is_complete())
pathConfigFile = GetDataDir(false) / pathConfigFile;
@@ -592,7 +604,7 @@ boost::filesystem::path GetConfigFile(const std::string& confPath)
void ReadConfigFile(const std::string& confPath)
{
- boost::filesystem::ifstream streamConfig(GetConfigFile(confPath));
+ fs::ifstream streamConfig(GetConfigFile(confPath));
if (!streamConfig.good())
return; // No bitcoin.conf file is OK
@@ -617,16 +629,16 @@ void ReadConfigFile(const std::string& confPath)
}
#ifndef WIN32
-boost::filesystem::path GetPidFile()
+fs::path GetPidFile()
{
- boost::filesystem::path pathPidFile(GetArg("-pid", BITCOIN_PID_FILENAME));
+ fs::path pathPidFile(GetArg("-pid", BITCOIN_PID_FILENAME));
if (!pathPidFile.is_complete()) pathPidFile = GetDataDir() / pathPidFile;
return pathPidFile;
}
-void CreatePidFile(const boost::filesystem::path &path, pid_t pid)
+void CreatePidFile(const fs::path &path, pid_t pid)
{
- FILE* file = fopen(path.string().c_str(), "w");
+ FILE* file = fsbridge::fopen(path, "w");
if (file)
{
fprintf(file, "%d\n", pid);
@@ -635,7 +647,7 @@ void CreatePidFile(const boost::filesystem::path &path, pid_t pid)
}
#endif
-bool RenameOver(boost::filesystem::path src, boost::filesystem::path dest)
+bool RenameOver(fs::path src, fs::path dest)
{
#ifdef WIN32
return MoveFileExA(src.string().c_str(), dest.string().c_str(),
@@ -651,13 +663,13 @@ bool RenameOver(boost::filesystem::path src, boost::filesystem::path dest)
* Specifically handles case where path p exists, but it wasn't possible for the user to
* write to the parent directory.
*/
-bool TryCreateDirectory(const boost::filesystem::path& p)
+bool TryCreateDirectory(const fs::path& p)
{
try
{
- return boost::filesystem::create_directory(p);
- } catch (const boost::filesystem::filesystem_error&) {
- if (!boost::filesystem::exists(p) || !boost::filesystem::is_directory(p))
+ return fs::create_directory(p);
+ } catch (const fs::filesystem_error&) {
+ if (!fs::exists(p) || !fs::is_directory(p))
throw;
}
@@ -764,11 +776,11 @@ void ShrinkDebugFile()
// Amount of debug.log to save at end when shrinking (must fit in memory)
constexpr size_t RECENT_DEBUG_HISTORY_SIZE = 10 * 1000000;
// Scroll debug.log if it's getting too big
- boost::filesystem::path pathLog = GetDataDir() / "debug.log";
- FILE* file = fopen(pathLog.string().c_str(), "r");
+ fs::path pathLog = GetDataDir() / "debug.log";
+ FILE* file = fsbridge::fopen(pathLog, "r");
// If debug.log file is more than 10% bigger the RECENT_DEBUG_HISTORY_SIZE
// trim it down by saving only the last RECENT_DEBUG_HISTORY_SIZE bytes
- if (file && boost::filesystem::file_size(pathLog) > 11 * (RECENT_DEBUG_HISTORY_SIZE / 10))
+ if (file && fs::file_size(pathLog) > 11 * (RECENT_DEBUG_HISTORY_SIZE / 10))
{
// Restart the file with some of the end
std::vector<char> vch(RECENT_DEBUG_HISTORY_SIZE, 0);
@@ -776,7 +788,7 @@ void ShrinkDebugFile()
int nBytes = fread(vch.data(), 1, vch.size(), file);
fclose(file);
- file = fopen(pathLog.string().c_str(), "w");
+ file = fsbridge::fopen(pathLog, "w");
if (file)
{
fwrite(vch.data(), 1, nBytes, file);
@@ -788,10 +800,8 @@ void ShrinkDebugFile()
}
#ifdef WIN32
-boost::filesystem::path GetSpecialFolderPath(int nFolder, bool fCreate)
+fs::path GetSpecialFolderPath(int nFolder, bool fCreate)
{
- namespace fs = boost::filesystem;
-
char pszPath[MAX_PATH] = "";
if(SHGetSpecialFolderPathA(NULL, pszPath, nFolder, fCreate))
@@ -851,9 +861,9 @@ void SetupEnvironment()
// The path locale is lazy initialized and to avoid deinitialization errors
// in multithreading environments, it is set explicitly by the main thread.
// A dummy locale is used to extract the internal default locale, used by
- // boost::filesystem::path, which is then used to explicitly imbue the path.
- std::locale loc = boost::filesystem::path::imbue(std::locale::classic());
- boost::filesystem::path::imbue(loc);
+ // fs::path, which is then used to explicitly imbue the path.
+ std::locale loc = fs::path::imbue(std::locale::classic());
+ fs::path::imbue(loc);
}
bool SetupNetworking()
diff --git a/src/util.h b/src/util.h
index 09481bc01c..ed28070a3f 100644
--- a/src/util.h
+++ b/src/util.h
@@ -15,6 +15,7 @@
#endif
#include "compat.h"
+#include "fs.h"
#include "tinyformat.h"
#include "utiltime.h"
@@ -25,7 +26,6 @@
#include <string>
#include <vector>
-#include <boost/filesystem/path.hpp>
#include <boost/signals2/signal.hpp>
#include <boost/thread/exceptions.hpp>
@@ -69,6 +69,12 @@ inline std::string _(const char* psz)
void SetupEnvironment();
bool SetupNetworking();
+struct CLogCategoryActive
+{
+ std::string category;
+ bool active;
+};
+
namespace BCLog {
enum LogFlags : uint32_t {
NONE = 0,
@@ -102,9 +108,12 @@ static inline bool LogAcceptCategory(uint32_t category)
return (logCategories.load(std::memory_order_relaxed) & category) != 0;
}
-/** Returns a string with the supported log categories */
+/** Returns a string with the log categories. */
std::string ListLogCategories();
+/** Returns a vector of the active log categories. */
+std::vector<CLogCategoryActive> ListActiveLogCategories();
+
/** Return true if str parses as a log category and set the flags in f */
bool GetLogCategory(uint32_t *f, const std::string *str);
@@ -144,19 +153,19 @@ void FileCommit(FILE *file);
bool TruncateFile(FILE *file, unsigned int length);
int RaiseFileDescriptorLimit(int nMinFD);
void AllocateFileRange(FILE *file, unsigned int offset, unsigned int length);
-bool RenameOver(boost::filesystem::path src, boost::filesystem::path dest);
-bool TryCreateDirectory(const boost::filesystem::path& p);
-boost::filesystem::path GetDefaultDataDir();
-const boost::filesystem::path &GetDataDir(bool fNetSpecific = true);
+bool RenameOver(fs::path src, fs::path dest);
+bool TryCreateDirectory(const fs::path& p);
+fs::path GetDefaultDataDir();
+const fs::path &GetDataDir(bool fNetSpecific = true);
void ClearDatadirCache();
-boost::filesystem::path GetConfigFile(const std::string& confPath);
+fs::path GetConfigFile(const std::string& confPath);
#ifndef WIN32
-boost::filesystem::path GetPidFile();
-void CreatePidFile(const boost::filesystem::path &path, pid_t pid);
+fs::path GetPidFile();
+void CreatePidFile(const fs::path &path, pid_t pid);
#endif
void ReadConfigFile(const std::string& confPath);
#ifdef WIN32
-boost::filesystem::path GetSpecialFolderPath(int nFolder, bool fCreate = true);
+fs::path GetSpecialFolderPath(int nFolder, bool fCreate = true);
#endif
void OpenDebugLog();
void ShrinkDebugFile();
diff --git a/src/validation.cpp b/src/validation.cpp
index 6ade633988..99ce53986f 100644
--- a/src/validation.cpp
+++ b/src/validation.cpp
@@ -12,6 +12,7 @@
#include "consensus/consensus.h"
#include "consensus/merkle.h"
#include "consensus/validation.h"
+#include "fs.h"
#include "hash.h"
#include "init.h"
#include "policy/fees.h"
@@ -41,8 +42,6 @@
#include <boost/algorithm/string/replace.hpp>
#include <boost/algorithm/string/join.hpp>
-#include <boost/filesystem.hpp>
-#include <boost/filesystem/fstream.hpp>
#include <boost/math/distributions/poisson.hpp>
#include <boost/thread.hpp>
@@ -155,39 +154,6 @@ namespace {
std::set<int> setDirtyFileInfo;
} // anon namespace
-/* Use this class to start tracking transactions that are removed from the
- * mempool and pass all those transactions through SyncTransaction when the
- * object goes out of scope. This is currently only used to call SyncTransaction
- * on conflicts removed from the mempool during block connection. Applied in
- * ActivateBestChain around ActivateBestStep which in turn calls:
- * ConnectTip->removeForBlock->removeConflicts
- */
-class MemPoolConflictRemovalTracker
-{
-private:
- std::vector<CTransactionRef> conflictedTxs;
- CTxMemPool &pool;
-
-public:
- MemPoolConflictRemovalTracker(CTxMemPool &_pool) : pool(_pool) {
- pool.NotifyEntryRemoved.connect(boost::bind(&MemPoolConflictRemovalTracker::NotifyEntryRemoved, this, _1, _2));
- }
-
- void NotifyEntryRemoved(CTransactionRef txRemoved, MemPoolRemovalReason reason) {
- if (reason == MemPoolRemovalReason::CONFLICT) {
- conflictedTxs.push_back(txRemoved);
- }
- }
-
- ~MemPoolConflictRemovalTracker() {
- pool.NotifyEntryRemoved.disconnect(boost::bind(&MemPoolConflictRemovalTracker::NotifyEntryRemoved, this, _1, _2));
- for (const auto& tx : conflictedTxs) {
- GetMainSignals().SyncTransaction(*tx, NULL, CMainSignals::SYNC_TRANSACTION_NOT_IN_BLOCK);
- }
- conflictedTxs.clear();
- }
-};
-
CBlockIndex* FindForkInGlobalIndex(const CChain& chain, const CBlockLocator& locator)
{
// Find the first block the caller has in the main chain
@@ -983,7 +949,7 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C
}
}
- GetMainSignals().SyncTransaction(tx, NULL, CMainSignals::SYNC_TRANSACTION_NOT_IN_BLOCK);
+ GetMainSignals().TransactionAddedToMempool(ptx);
return true;
}
@@ -1915,7 +1881,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
REJECT_INVALID, "bad-cb-amount");
if (!control.Wait())
- return state.DoS(100, false);
+ return state.DoS(100, error("%s: CheckQueue failed", __func__), REJECT_INVALID, "block-validation-failed");
int64_t nTime4 = GetTimeMicros(); nTimeVerify += nTime4 - nTime2;
LogPrint(BCLog::BENCH, " - Verify %u txins: %.2fms (%.3fms/txin) [%.2fs]\n", nInputs - 1, 0.001 * (nTime4 - nTime2), nInputs <= 1 ? 0 : 0.001 * (nTime4 - nTime2) / (nInputs-1), nTimeVerify * 0.000001);
@@ -2006,10 +1972,11 @@ bool static FlushStateToDisk(CValidationState &state, FlushStateMode mode, int n
nLastSetChain = nNow;
}
int64_t nMempoolSizeMax = GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000;
- int64_t cacheSize = pcoinsTip->DynamicMemoryUsage() * 2; // Compensate for extra memory peak (x1.5-x1.9) at flush time.
+ int64_t cacheSize = pcoinsTip->DynamicMemoryUsage() * DB_PEAK_USAGE_FACTOR;
int64_t nTotalSpace = nCoinCacheUsage + std::max<int64_t>(nMempoolSizeMax - nMempoolUsage, 0);
- // The cache is large and we're within 10% and 100 MiB of the limit, but we have time now (not in the middle of a block processing).
- bool fCacheLarge = mode == FLUSH_STATE_PERIODIC && cacheSize > std::max((9 * nTotalSpace) / 10, nTotalSpace - 100 * 1024 * 1024);
+ // The cache is large and we're within 10% and 200 MiB or 50% and 50MiB of the limit, but we have time now (not in the middle of a block processing).
+ bool fCacheLarge = mode == FLUSH_STATE_PERIODIC && cacheSize > std::min(std::max(nTotalSpace / 2, nTotalSpace - MIN_BLOCK_COINSDB_USAGE * 1024 * 1024),
+ std::max((9 * nTotalSpace) / 10, nTotalSpace - MAX_BLOCK_COINSDB_USAGE * 1024 * 1024));
// The cache is over the limit, we have to write now.
bool fCacheCritical = mode == FLUSH_STATE_IF_NEEDED && cacheSize > nTotalSpace;
// It's been a while since we wrote the block index to disk. Do this frequently, so we don't need to redownload after a crash.
@@ -2153,7 +2120,8 @@ bool static DisconnectTip(CValidationState& state, const CChainParams& chainpara
CBlockIndex *pindexDelete = chainActive.Tip();
assert(pindexDelete);
// Read block from disk.
- CBlock block;
+ std::shared_ptr<CBlock> pblock = std::make_shared<CBlock>();
+ CBlock& block = *pblock;
if (!ReadBlockFromDisk(block, pindexDelete, chainparams.GetConsensus()))
return AbortNode(state, "Failed to read block");
// Apply the block atomically to the chain state.
@@ -2195,9 +2163,7 @@ bool static DisconnectTip(CValidationState& state, const CChainParams& chainpara
UpdateTip(pindexDelete->pprev, chainparams);
// Let wallets know transactions went from 1-confirmed to
// 0-confirmed or conflicted:
- for (const auto& tx : block.vtx) {
- GetMainSignals().SyncTransaction(*tx, pindexDelete->pprev, CMainSignals::SYNC_TRANSACTION_NOT_IN_BLOCK);
- }
+ GetMainSignals().BlockDisconnected(pblock);
return true;
}
@@ -2207,36 +2173,92 @@ static int64_t nTimeFlush = 0;
static int64_t nTimeChainState = 0;
static int64_t nTimePostConnect = 0;
+struct PerBlockConnectTrace {
+ CBlockIndex* pindex = NULL;
+ std::shared_ptr<const CBlock> pblock;
+ std::shared_ptr<std::vector<CTransactionRef>> conflictedTxs;
+ PerBlockConnectTrace() : conflictedTxs(std::make_shared<std::vector<CTransactionRef>>()) {}
+};
/**
* Used to track blocks whose transactions were applied to the UTXO state as a
* part of a single ActivateBestChainStep call.
+ *
+ * This class also tracks transactions that are removed from the mempool as
+ * conflicts (per block) and can be used to pass all those transactions
+ * through SyncTransaction.
+ *
+ * This class assumes (and asserts) that the conflicted transactions for a given
+ * block are added via mempool callbacks prior to the BlockConnected() associated
+ * with those transactions. If any transactions are marked conflicted, it is
+ * assumed that an associated block will always be added.
+ *
+ * This class is single-use, once you call GetBlocksConnected() you have to throw
+ * it away and make a new one.
*/
-struct ConnectTrace {
- std::vector<std::pair<CBlockIndex*, std::shared_ptr<const CBlock> > > blocksConnected;
+class ConnectTrace {
+private:
+ std::vector<PerBlockConnectTrace> blocksConnected;
+ CTxMemPool &pool;
+
+public:
+ ConnectTrace(CTxMemPool &_pool) : blocksConnected(1), pool(_pool) {
+ pool.NotifyEntryRemoved.connect(boost::bind(&ConnectTrace::NotifyEntryRemoved, this, _1, _2));
+ }
+
+ ~ConnectTrace() {
+ pool.NotifyEntryRemoved.disconnect(boost::bind(&ConnectTrace::NotifyEntryRemoved, this, _1, _2));
+ }
+
+ void BlockConnected(CBlockIndex* pindex, std::shared_ptr<const CBlock> pblock) {
+ assert(!blocksConnected.back().pindex);
+ assert(pindex);
+ assert(pblock);
+ blocksConnected.back().pindex = pindex;
+ blocksConnected.back().pblock = std::move(pblock);
+ blocksConnected.emplace_back();
+ }
+
+ std::vector<PerBlockConnectTrace>& GetBlocksConnected() {
+ // We always keep one extra block at the end of our list because
+ // blocks are added after all the conflicted transactions have
+ // been filled in. Thus, the last entry should always be an empty
+ // one waiting for the transactions from the next block. We pop
+ // the last entry here to make sure the list we return is sane.
+ assert(!blocksConnected.back().pindex);
+ assert(blocksConnected.back().conflictedTxs->empty());
+ blocksConnected.pop_back();
+ return blocksConnected;
+ }
+
+ void NotifyEntryRemoved(CTransactionRef txRemoved, MemPoolRemovalReason reason) {
+ assert(!blocksConnected.back().pindex);
+ if (reason == MemPoolRemovalReason::CONFLICT) {
+ blocksConnected.back().conflictedTxs->emplace_back(std::move(txRemoved));
+ }
+ }
};
/**
* Connect a new block to chainActive. pblock is either NULL or a pointer to a CBlock
* corresponding to pindexNew, to bypass loading it again from disk.
*
- * The block is always added to connectTrace (either after loading from disk or by copying
- * pblock) - if that is not intended, care must be taken to remove the last entry in
- * blocksConnected in case of failure.
+ * The block is added to connectTrace if connection succeeds.
*/
bool static ConnectTip(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexNew, const std::shared_ptr<const CBlock>& pblock, ConnectTrace& connectTrace)
{
assert(pindexNew->pprev == chainActive.Tip());
// Read block from disk.
int64_t nTime1 = GetTimeMicros();
+ std::shared_ptr<const CBlock> pthisBlock;
if (!pblock) {
std::shared_ptr<CBlock> pblockNew = std::make_shared<CBlock>();
- connectTrace.blocksConnected.emplace_back(pindexNew, pblockNew);
if (!ReadBlockFromDisk(*pblockNew, pindexNew, chainparams.GetConsensus()))
return AbortNode(state, "Failed to read block");
+ pthisBlock = pblockNew;
} else {
- connectTrace.blocksConnected.emplace_back(pindexNew, pblock);
+ pthisBlock = pblock;
}
- const CBlock& blockConnecting = *connectTrace.blocksConnected.back().second;
+ const CBlock& blockConnecting = *pthisBlock;
// Apply the block atomically to the chain state.
int64_t nTime2 = GetTimeMicros(); nTimeReadFromDisk += nTime2 - nTime1;
int64_t nTime3;
@@ -2270,6 +2292,8 @@ bool static ConnectTip(CValidationState& state, const CChainParams& chainparams,
int64_t nTime6 = GetTimeMicros(); nTimePostConnect += nTime6 - nTime5; nTimeTotal += nTime6 - nTime1;
LogPrint(BCLog::BENCH, " - Connect postprocess: %.2fms [%.2fs]\n", (nTime6 - nTime5) * 0.001, nTimePostConnect * 0.000001);
LogPrint(BCLog::BENCH, "- Connect block: %.2fms [%.2fs]\n", (nTime6 - nTime1) * 0.001, nTimeTotal * 0.000001);
+
+ connectTrace.BlockConnected(pindexNew, std::move(pthisBlock));
return true;
}
@@ -2388,8 +2412,6 @@ static bool ActivateBestChainStep(CValidationState& state, const CChainParams& c
state = CValidationState();
fInvalidFound = true;
fContinue = false;
- // If we didn't actually connect the block, don't notify listeners about it
- connectTrace.blocksConnected.pop_back();
break;
} else {
// A system error occurred (disk space, database error, ...).
@@ -2461,18 +2483,11 @@ bool ActivateBestChain(CValidationState &state, const CChainParams& chainparams,
break;
const CBlockIndex *pindexFork;
- ConnectTrace connectTrace;
bool fInitialDownload;
{
LOCK(cs_main);
- { // TODO: Temporarily ensure that mempool removals are notified before
- // connected transactions. This shouldn't matter, but the abandoned
- // state of transactions in our wallet is currently cleared when we
- // receive another notification and there is a race condition where
- // notification of a connected conflict might cause an outside process
- // to abandon a transaction and then have it inadvertently cleared by
- // the notification that the conflicted transaction was evicted.
- MemPoolConflictRemovalTracker mrt(mempool);
+ ConnectTrace connectTrace(mempool); // Destructed before cs_main is unlocked
+
CBlockIndex *pindexOldTip = chainActive.Tip();
if (pindexMostWork == NULL) {
pindexMostWork = FindMostWorkChain();
@@ -2495,16 +2510,9 @@ bool ActivateBestChain(CValidationState &state, const CChainParams& chainparams,
pindexFork = chainActive.FindFork(pindexOldTip);
fInitialDownload = IsInitialBlockDownload();
- // throw all transactions though the signal-interface
-
- } // MemPoolConflictRemovalTracker destroyed and conflict evictions are notified
-
- // Transactions in the connected block are notified
- for (const auto& pair : connectTrace.blocksConnected) {
- assert(pair.second);
- const CBlock& block = *(pair.second);
- for (unsigned int i = 0; i < block.vtx.size(); i++)
- GetMainSignals().SyncTransaction(*block.vtx[i], pair.first, i);
+ for (const PerBlockConnectTrace& trace : connectTrace.GetBlocksConnected()) {
+ assert(trace.pblock && trace.pindex);
+ GetMainSignals().BlockConnected(trace.pblock, trace.pindex, *trace.conflictedTxs);
}
}
// When we reach this point, we switched to a new tip (stored in pindexNewTip).
@@ -2882,10 +2890,12 @@ static bool CheckIndexAgainstCheckpoint(const CBlockIndex* pindexPrev, CValidati
return true;
int nHeight = pindexPrev->nHeight+1;
- // Don't accept any forks from the main chain prior to last checkpoint
+ // Don't accept any forks from the main chain prior to last checkpoint.
+ // GetLastCheckpoint finds the last checkpoint in MapCheckpoints that's in our
+ // MapBlockIndex.
CBlockIndex* pcheckpoint = Checkpoints::GetLastCheckpoint(chainparams.Checkpoints());
if (pcheckpoint && nHeight < pcheckpoint->nHeight)
- return state.DoS(100, error("%s: forked chain older than last checkpoint (height %d)", __func__, nHeight));
+ return state.DoS(100, error("%s: forked chain older than last checkpoint (height %d)", __func__, nHeight), REJECT_CHECKPOINT, "bad-fork-prior-to-checkpoint");
return true;
}
@@ -2901,9 +2911,11 @@ bool IsWitnessEnabled(const CBlockIndex* pindexPrev, const Consensus::Params& pa
static int GetWitnessCommitmentIndex(const CBlock& block)
{
int commitpos = -1;
- for (size_t o = 0; o < block.vtx[0]->vout.size(); o++) {
- if (block.vtx[0]->vout[o].scriptPubKey.size() >= 38 && block.vtx[0]->vout[o].scriptPubKey[0] == OP_RETURN && block.vtx[0]->vout[o].scriptPubKey[1] == 0x24 && block.vtx[0]->vout[o].scriptPubKey[2] == 0xaa && block.vtx[0]->vout[o].scriptPubKey[3] == 0x21 && block.vtx[0]->vout[o].scriptPubKey[4] == 0xa9 && block.vtx[0]->vout[o].scriptPubKey[5] == 0xed) {
- commitpos = o;
+ if (!block.vtx.empty()) {
+ for (size_t o = 0; o < block.vtx[0]->vout.size(); o++) {
+ if (block.vtx[0]->vout[o].scriptPubKey.size() >= 38 && block.vtx[0]->vout[o].scriptPubKey[0] == OP_RETURN && block.vtx[0]->vout[o].scriptPubKey[1] == 0x24 && block.vtx[0]->vout[o].scriptPubKey[2] == 0xaa && block.vtx[0]->vout[o].scriptPubKey[3] == 0x21 && block.vtx[0]->vout[o].scriptPubKey[4] == 0xa9 && block.vtx[0]->vout[o].scriptPubKey[5] == 0xed) {
+ commitpos = o;
+ }
}
}
return commitpos;
@@ -3084,7 +3096,7 @@ static bool AcceptBlockHeader(const CBlockHeader& block, CValidationState& state
CBlockIndex* pindexPrev = NULL;
BlockMap::iterator mi = mapBlockIndex.find(block.hashPrevBlock);
if (mi == mapBlockIndex.end())
- return state.DoS(10, error("%s: prev block not found", __func__), 0, "bad-prevblk");
+ return state.DoS(10, error("%s: prev block not found", __func__), 0, "prev-blk-not-found");
pindexPrev = (*mi).second;
if (pindexPrev->nStatus & BLOCK_FAILED_MASK)
return state.DoS(100, error("%s: prev block invalid", __func__), REJECT_INVALID, "bad-prevblk");
@@ -3315,8 +3327,8 @@ void UnlinkPrunedFiles(const std::set<int>& setFilesToPrune)
{
for (std::set<int>::iterator it = setFilesToPrune.begin(); it != setFilesToPrune.end(); ++it) {
CDiskBlockPos pos(*it, 0);
- boost::filesystem::remove(GetBlockPosFilename(pos, "blk"));
- boost::filesystem::remove(GetBlockPosFilename(pos, "rev"));
+ fs::remove(GetBlockPosFilename(pos, "blk"));
+ fs::remove(GetBlockPosFilename(pos, "rev"));
LogPrintf("Prune: %s deleted blk/rev (%05u)\n", __func__, *it);
}
}
@@ -3400,7 +3412,7 @@ void FindFilesToPrune(std::set<int>& setFilesToPrune, uint64_t nPruneAfterHeight
bool CheckDiskSpace(uint64_t nAdditionalBytes)
{
- uint64_t nFreeBytesAvailable = boost::filesystem::space(GetDataDir()).available;
+ uint64_t nFreeBytesAvailable = fs::space(GetDataDir()).available;
// Check for nMinDiskSpace bytes (currently 50MB)
if (nFreeBytesAvailable < nMinDiskSpace + nAdditionalBytes)
@@ -3413,11 +3425,11 @@ FILE* OpenDiskFile(const CDiskBlockPos &pos, const char *prefix, bool fReadOnly)
{
if (pos.IsNull())
return NULL;
- boost::filesystem::path path = GetBlockPosFilename(pos, prefix);
- boost::filesystem::create_directories(path.parent_path());
- FILE* file = fopen(path.string().c_str(), "rb+");
+ fs::path path = GetBlockPosFilename(pos, prefix);
+ fs::create_directories(path.parent_path());
+ FILE* file = fsbridge::fopen(path, "rb+");
if (!file && !fReadOnly)
- file = fopen(path.string().c_str(), "wb+");
+ file = fsbridge::fopen(path, "wb+");
if (!file) {
LogPrintf("Unable to open file %s\n", path.string());
return NULL;
@@ -3440,7 +3452,7 @@ FILE* OpenUndoFile(const CDiskBlockPos &pos, bool fReadOnly) {
return OpenDiskFile(pos, "rev", fReadOnly);
}
-boost::filesystem::path GetBlockPosFilename(const CDiskBlockPos &pos, const char *prefix)
+fs::path GetBlockPosFilename(const CDiskBlockPos &pos, const char *prefix)
{
return GetDataDir() / "blocks" / strprintf("%s%05u.dat", prefix, pos.nFile);
}
@@ -4165,7 +4177,7 @@ static const uint64_t MEMPOOL_DUMP_VERSION = 1;
bool LoadMempool(void)
{
int64_t nExpiryTimeout = GetArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY) * 60 * 60;
- FILE* filestr = fopen((GetDataDir() / "mempool.dat").string().c_str(), "rb");
+ FILE* filestr = fsbridge::fopen(GetDataDir() / "mempool.dat", "rb");
CAutoFile file(filestr, SER_DISK, CLIENT_VERSION);
if (file.IsNull()) {
LogPrintf("Failed to open mempool file from disk. Continuing anyway.\n");
@@ -4245,7 +4257,7 @@ void DumpMempool(void)
int64_t mid = GetTimeMicros();
try {
- FILE* filestr = fopen((GetDataDir() / "mempool.dat.new").string().c_str(), "wb");
+ FILE* filestr = fsbridge::fopen(GetDataDir() / "mempool.dat.new", "wb");
if (!filestr) {
return;
}
diff --git a/src/validation.h b/src/validation.h
index 5f8e9a689c..4aa10cbb0b 100644
--- a/src/validation.h
+++ b/src/validation.h
@@ -13,6 +13,7 @@
#include "amount.h"
#include "chain.h"
#include "coins.h"
+#include "fs.h"
#include "protocol.h" // For CMessageHeader::MessageStartChars
#include "script/script_error.h"
#include "sync.h"
@@ -30,7 +31,6 @@
#include <atomic>
#include <boost/unordered_map.hpp>
-#include <boost/filesystem/path.hpp>
class CBlockIndex;
class CBlockTreeDB;
@@ -250,7 +250,7 @@ FILE* OpenBlockFile(const CDiskBlockPos &pos, bool fReadOnly = false);
/** Open an undo file (rev?????.dat) */
FILE* OpenUndoFile(const CDiskBlockPos &pos, bool fReadOnly = false);
/** Translation to a filesystem path */
-boost::filesystem::path GetBlockPosFilename(const CDiskBlockPos &pos, const char *prefix);
+fs::path GetBlockPosFilename(const CDiskBlockPos &pos, const char *prefix);
/** Import blocks from an external file */
bool LoadExternalBlockFile(const CChainParams& chainparams, FILE* fileIn, CDiskBlockPos *dbp = NULL);
/** Initialize a new block tree database + block data on disk */
diff --git a/src/validationinterface.cpp b/src/validationinterface.cpp
index d4121a28bc..0f699328c7 100644
--- a/src/validationinterface.cpp
+++ b/src/validationinterface.cpp
@@ -14,39 +14,42 @@ CMainSignals& GetMainSignals()
void RegisterValidationInterface(CValidationInterface* pwalletIn) {
g_signals.UpdatedBlockTip.connect(boost::bind(&CValidationInterface::UpdatedBlockTip, pwalletIn, _1, _2, _3));
- g_signals.SyncTransaction.connect(boost::bind(&CValidationInterface::SyncTransaction, pwalletIn, _1, _2, _3));
+ g_signals.TransactionAddedToMempool.connect(boost::bind(&CValidationInterface::TransactionAddedToMempool, pwalletIn, _1));
+ g_signals.BlockConnected.connect(boost::bind(&CValidationInterface::BlockConnected, pwalletIn, _1, _2, _3));
+ g_signals.BlockDisconnected.connect(boost::bind(&CValidationInterface::BlockDisconnected, pwalletIn, _1));
g_signals.UpdatedTransaction.connect(boost::bind(&CValidationInterface::UpdatedTransaction, pwalletIn, _1));
g_signals.SetBestChain.connect(boost::bind(&CValidationInterface::SetBestChain, pwalletIn, _1));
g_signals.Inventory.connect(boost::bind(&CValidationInterface::Inventory, pwalletIn, _1));
g_signals.Broadcast.connect(boost::bind(&CValidationInterface::ResendWalletTransactions, pwalletIn, _1, _2));
g_signals.BlockChecked.connect(boost::bind(&CValidationInterface::BlockChecked, pwalletIn, _1, _2));
g_signals.ScriptForMining.connect(boost::bind(&CValidationInterface::GetScriptForMining, pwalletIn, _1));
- g_signals.BlockFound.connect(boost::bind(&CValidationInterface::ResetRequestCount, pwalletIn, _1));
g_signals.NewPoWValidBlock.connect(boost::bind(&CValidationInterface::NewPoWValidBlock, pwalletIn, _1, _2));
}
void UnregisterValidationInterface(CValidationInterface* pwalletIn) {
- g_signals.BlockFound.disconnect(boost::bind(&CValidationInterface::ResetRequestCount, pwalletIn, _1));
g_signals.ScriptForMining.disconnect(boost::bind(&CValidationInterface::GetScriptForMining, pwalletIn, _1));
g_signals.BlockChecked.disconnect(boost::bind(&CValidationInterface::BlockChecked, pwalletIn, _1, _2));
g_signals.Broadcast.disconnect(boost::bind(&CValidationInterface::ResendWalletTransactions, pwalletIn, _1, _2));
g_signals.Inventory.disconnect(boost::bind(&CValidationInterface::Inventory, pwalletIn, _1));
g_signals.SetBestChain.disconnect(boost::bind(&CValidationInterface::SetBestChain, pwalletIn, _1));
g_signals.UpdatedTransaction.disconnect(boost::bind(&CValidationInterface::UpdatedTransaction, pwalletIn, _1));
- g_signals.SyncTransaction.disconnect(boost::bind(&CValidationInterface::SyncTransaction, pwalletIn, _1, _2, _3));
+ g_signals.TransactionAddedToMempool.disconnect(boost::bind(&CValidationInterface::TransactionAddedToMempool, pwalletIn, _1));
+ g_signals.BlockConnected.disconnect(boost::bind(&CValidationInterface::BlockConnected, pwalletIn, _1, _2, _3));
+ g_signals.BlockDisconnected.disconnect(boost::bind(&CValidationInterface::BlockDisconnected, pwalletIn, _1));
g_signals.UpdatedBlockTip.disconnect(boost::bind(&CValidationInterface::UpdatedBlockTip, pwalletIn, _1, _2, _3));
g_signals.NewPoWValidBlock.disconnect(boost::bind(&CValidationInterface::NewPoWValidBlock, pwalletIn, _1, _2));
}
void UnregisterAllValidationInterfaces() {
- g_signals.BlockFound.disconnect_all_slots();
g_signals.ScriptForMining.disconnect_all_slots();
g_signals.BlockChecked.disconnect_all_slots();
g_signals.Broadcast.disconnect_all_slots();
g_signals.Inventory.disconnect_all_slots();
g_signals.SetBestChain.disconnect_all_slots();
g_signals.UpdatedTransaction.disconnect_all_slots();
- g_signals.SyncTransaction.disconnect_all_slots();
+ g_signals.TransactionAddedToMempool.disconnect_all_slots();
+ g_signals.BlockConnected.disconnect_all_slots();
+ g_signals.BlockDisconnected.disconnect_all_slots();
g_signals.UpdatedBlockTip.disconnect_all_slots();
g_signals.NewPoWValidBlock.disconnect_all_slots();
}
diff --git a/src/validationinterface.h b/src/validationinterface.h
index 7f13a29d23..083c136f2c 100644
--- a/src/validationinterface.h
+++ b/src/validationinterface.h
@@ -7,16 +7,16 @@
#define BITCOIN_VALIDATIONINTERFACE_H
#include <boost/signals2/signal.hpp>
-#include <boost/shared_ptr.hpp>
#include <memory>
+#include "primitives/transaction.h" // CTransaction(Ref)
+
class CBlock;
class CBlockIndex;
struct CBlockLocator;
class CBlockIndex;
class CConnman;
class CReserveScript;
-class CTransaction;
class CValidationInterface;
class CValidationState;
class uint256;
@@ -33,14 +33,15 @@ void UnregisterAllValidationInterfaces();
class CValidationInterface {
protected:
virtual void UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockIndex *pindexFork, bool fInitialDownload) {}
- virtual void SyncTransaction(const CTransaction &tx, const CBlockIndex *pindex, int posInBlock) {}
+ virtual void TransactionAddedToMempool(const CTransactionRef &ptxn) {}
+ virtual void BlockConnected(const std::shared_ptr<const CBlock> &block, const CBlockIndex *pindex, const std::vector<CTransactionRef> &txnConflicted) {}
+ virtual void BlockDisconnected(const std::shared_ptr<const CBlock> &block) {}
virtual void SetBestChain(const CBlockLocator &locator) {}
virtual void UpdatedTransaction(const uint256 &hash) {}
virtual void Inventory(const uint256 &hash) {}
virtual void ResendWalletTransactions(int64_t nBestBlockTime, CConnman* connman) {}
virtual void BlockChecked(const CBlock&, const CValidationState&) {}
- virtual void GetScriptForMining(boost::shared_ptr<CReserveScript>&) {};
- virtual void ResetRequestCount(const uint256 &hash) {};
+ virtual void GetScriptForMining(std::shared_ptr<CReserveScript>&) {};
virtual void NewPoWValidBlock(const CBlockIndex *pindex, const std::shared_ptr<const CBlock>& block) {};
friend void ::RegisterValidationInterface(CValidationInterface*);
friend void ::UnregisterValidationInterface(CValidationInterface*);
@@ -50,17 +51,15 @@ protected:
struct CMainSignals {
/** Notifies listeners of updated block chain tip */
boost::signals2::signal<void (const CBlockIndex *, const CBlockIndex *, bool fInitialDownload)> UpdatedBlockTip;
- /** A posInBlock value for SyncTransaction calls for transactions not
- * included in connected blocks such as transactions removed from mempool,
- * accepted to mempool or appearing in disconnected blocks.*/
- static const int SYNC_TRANSACTION_NOT_IN_BLOCK = -1;
- /** Notifies listeners of updated transaction data (transaction, and
- * optionally the block it is found in). Called with block data when
- * transaction is included in a connected block, and without block data when
- * transaction was accepted to mempool, removed from mempool (only when
- * removal was due to conflict from connected block), or appeared in a
- * disconnected block.*/
- boost::signals2::signal<void (const CTransaction &, const CBlockIndex *pindex, int posInBlock)> SyncTransaction;
+ /** Notifies listeners of a transaction having been added to mempool. */
+ boost::signals2::signal<void (const CTransactionRef &)> TransactionAddedToMempool;
+ /**
+ * Notifies listeners of a block being connected.
+ * Provides a vector of transactions evicted from the mempool as a result.
+ */
+ boost::signals2::signal<void (const std::shared_ptr<const CBlock> &, const CBlockIndex *pindex, const std::vector<CTransactionRef> &)> BlockConnected;
+ /** Notifies listeners of a block being disconnected */
+ boost::signals2::signal<void (const std::shared_ptr<const CBlock> &)> BlockDisconnected;
/** Notifies listeners of an updated transaction without new data (for now: a coinbase potentially becoming visible). */
boost::signals2::signal<void (const uint256 &)> UpdatedTransaction;
/** Notifies listeners of a new active block chain. */
@@ -77,9 +76,7 @@ struct CMainSignals {
*/
boost::signals2::signal<void (const CBlock&, const CValidationState&)> BlockChecked;
/** Notifies listeners that a key for mining is required (coinbase) */
- boost::signals2::signal<void (boost::shared_ptr<CReserveScript>&)> ScriptForMining;
- /** Notifies listeners that a block has been successfully mined */
- boost::signals2::signal<void (const uint256 &)> BlockFound;
+ boost::signals2::signal<void (std::shared_ptr<CReserveScript>&)> ScriptForMining;
/**
* Notifies listeners that a block which builds directly on our current tip
* has been received and connected to the headers tree, though not validated yet */
diff --git a/src/wallet/db.cpp b/src/wallet/db.cpp
index d3333bf1ab..f47fc92b57 100644
--- a/src/wallet/db.cpp
+++ b/src/wallet/db.cpp
@@ -6,6 +6,7 @@
#include "db.h"
#include "addrman.h"
+#include "fs.h"
#include "hash.h"
#include "protocol.h"
#include "util.h"
@@ -17,7 +18,6 @@
#include <sys/stat.h>
#endif
-#include <boost/filesystem.hpp>
#include <boost/foreach.hpp>
#include <boost/thread.hpp>
#include <boost/version.hpp>
@@ -66,7 +66,7 @@ void CDBEnv::Close()
EnvShutdown();
}
-bool CDBEnv::Open(const boost::filesystem::path& pathIn)
+bool CDBEnv::Open(const fs::path& pathIn)
{
if (fDbEnvInit)
return true;
@@ -74,9 +74,9 @@ bool CDBEnv::Open(const boost::filesystem::path& pathIn)
boost::this_thread::interruption_point();
strPath = pathIn.string();
- boost::filesystem::path pathLogDir = pathIn / "database";
+ fs::path pathLogDir = pathIn / "database";
TryCreateDirectory(pathLogDir);
- boost::filesystem::path pathErrorFile = pathIn / "db.log";
+ fs::path pathErrorFile = pathIn / "db.log";
LogPrintf("CDBEnv::Open: LogDir=%s ErrorFile=%s\n", pathLogDir.string(), pathErrorFile.string());
unsigned int nEnvFlags = 0;
@@ -89,7 +89,7 @@ bool CDBEnv::Open(const boost::filesystem::path& pathIn)
dbenv->set_lg_max(1048576);
dbenv->set_lk_max_locks(40000);
dbenv->set_lk_max_objects(40000);
- dbenv->set_errfile(fopen(pathErrorFile.string().c_str(), "a")); /// debug
+ dbenv->set_errfile(fsbridge::fopen(pathErrorFile, "a")); /// debug
dbenv->set_flags(DB_AUTO_COMMIT, 1);
dbenv->set_flags(DB_TXN_WRITE_NOSYNC, 1);
dbenv->log_set_config(DB_LOG_AUTO_REMOVE, 1);
@@ -227,13 +227,13 @@ bool CDB::Recover(const std::string& filename, void *callbackDataIn, bool (*reco
return fSuccess;
}
-bool CDB::VerifyEnvironment(const std::string& walletFile, const boost::filesystem::path& dataDir, std::string& errorStr)
+bool CDB::VerifyEnvironment(const std::string& walletFile, const fs::path& dataDir, std::string& errorStr)
{
LogPrintf("Using BerkeleyDB version %s\n", DbEnv::version(0, 0, 0));
LogPrintf("Using wallet %s\n", walletFile);
// Wallet file must be a plain filename without a directory
- if (walletFile != boost::filesystem::basename(walletFile) + boost::filesystem::extension(walletFile))
+ if (walletFile != fs::basename(walletFile) + fs::extension(walletFile))
{
errorStr = strprintf(_("Wallet %s resides outside data directory %s"), walletFile, dataDir.string());
return false;
@@ -242,12 +242,12 @@ bool CDB::VerifyEnvironment(const std::string& walletFile, const boost::filesyst
if (!bitdb.Open(dataDir))
{
// try moving the database env out of the way
- boost::filesystem::path pathDatabase = dataDir / "database";
- boost::filesystem::path pathDatabaseBak = dataDir / strprintf("database.%d.bak", GetTime());
+ fs::path pathDatabase = dataDir / "database";
+ fs::path pathDatabaseBak = dataDir / strprintf("database.%d.bak", GetTime());
try {
- boost::filesystem::rename(pathDatabase, pathDatabaseBak);
+ fs::rename(pathDatabase, pathDatabaseBak);
LogPrintf("Moved old %s to %s. Retrying.\n", pathDatabase.string(), pathDatabaseBak.string());
- } catch (const boost::filesystem::filesystem_error&) {
+ } catch (const fs::filesystem_error&) {
// failure is ok (well, not really, but it's not worse than what we started with)
}
@@ -261,9 +261,9 @@ bool CDB::VerifyEnvironment(const std::string& walletFile, const boost::filesyst
return true;
}
-bool CDB::VerifyDatabaseFile(const std::string& walletFile, const boost::filesystem::path& dataDir, std::string& warningStr, std::string& errorStr, bool (*recoverFunc)(const std::string& strFile))
+bool CDB::VerifyDatabaseFile(const std::string& walletFile, const fs::path& dataDir, std::string& warningStr, std::string& errorStr, bool (*recoverFunc)(const std::string& strFile))
{
- if (boost::filesystem::exists(dataDir / walletFile))
+ if (fs::exists(dataDir / walletFile))
{
CDBEnv::VerifyResult r = bitdb.Verify(walletFile, recoverFunc);
if (r == CDBEnv::RECOVER_OK)
@@ -590,7 +590,7 @@ void CDBEnv::Flush(bool fShutdown)
dbenv->log_archive(&listp, DB_ARCH_REMOVE);
Close();
if (!fMockDb)
- boost::filesystem::remove_all(boost::filesystem::path(strPath) / "database");
+ fs::remove_all(fs::path(strPath) / "database");
}
}
}
diff --git a/src/wallet/db.h b/src/wallet/db.h
index 19c54e314c..9f912f9a1a 100644
--- a/src/wallet/db.h
+++ b/src/wallet/db.h
@@ -7,6 +7,7 @@
#define BITCOIN_WALLET_DB_H
#include "clientversion.h"
+#include "fs.h"
#include "serialize.h"
#include "streams.h"
#include "sync.h"
@@ -16,8 +17,6 @@
#include <string>
#include <vector>
-#include <boost/filesystem/path.hpp>
-
#include <db_cxx.h>
static const unsigned int DEFAULT_WALLET_DBLOGSIZE = 100;
@@ -28,7 +27,7 @@ class CDBEnv
private:
bool fDbEnvInit;
bool fMockDb;
- // Don't change into boost::filesystem::path, as that can result in
+ // Don't change into fs::path, as that can result in
// shutdown problems/crashes caused by a static initialized internal pointer.
std::string strPath;
@@ -67,7 +66,7 @@ public:
typedef std::pair<std::vector<unsigned char>, std::vector<unsigned char> > KeyValPair;
bool Salvage(const std::string& strFile, bool fAggressive, std::vector<KeyValPair>& vResult);
- bool Open(const boost::filesystem::path& path);
+ bool Open(const fs::path& path);
void Close();
void Flush(bool fShutdown);
void CheckpointLSN(const std::string& strFile);
@@ -110,9 +109,9 @@ public:
ideal to be called periodically */
static bool PeriodicFlush(std::string strFile);
/* verifies the database environment */
- static bool VerifyEnvironment(const std::string& walletFile, const boost::filesystem::path& dataDir, std::string& errorStr);
+ static bool VerifyEnvironment(const std::string& walletFile, const fs::path& dataDir, std::string& errorStr);
/* verifies the database file */
- static bool VerifyDatabaseFile(const std::string& walletFile, const boost::filesystem::path& dataDir, std::string& warningStr, std::string& errorStr, bool (*recoverFunc)(const std::string& strFile));
+ static bool VerifyDatabaseFile(const std::string& walletFile, const fs::path& dataDir, std::string& warningStr, std::string& errorStr, bool (*recoverFunc)(const std::string& strFile));
private:
CDB(const CDB&);
diff --git a/src/wallet/feebumper.cpp b/src/wallet/feebumper.cpp
new file mode 100644
index 0000000000..6b030935f3
--- /dev/null
+++ b/src/wallet/feebumper.cpp
@@ -0,0 +1,283 @@
+// Copyright (c) 2017 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 "consensus/validation.h"
+#include "wallet/feebumper.h"
+#include "wallet/wallet.h"
+#include "policy/policy.h"
+#include "policy/rbf.h"
+#include "validation.h" //for mempool access
+#include "txmempool.h"
+#include "utilmoneystr.h"
+#include "util.h"
+#include "net.h"
+
+// Calculate the size of the transaction assuming all signatures are max size
+// Use DummySignatureCreator, which inserts 72 byte signatures everywhere.
+// TODO: re-use this in CWallet::CreateTransaction (right now
+// CreateTransaction uses the constructed dummy-signed tx to do a priority
+// calculation, but we should be able to refactor after priority is removed).
+// NOTE: this requires that all inputs must be in mapWallet (eg the tx should
+// be IsAllFromMe).
+int64_t CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *pWallet)
+{
+ CMutableTransaction txNew(tx);
+ std::vector<CInputCoin> vCoins;
+ // Look up the inputs. We should have already checked that this transaction
+ // IsAllFromMe(ISMINE_SPENDABLE), so every input should already be in our
+ // wallet, with a valid index into the vout array.
+ for (auto& input : tx.vin) {
+ const auto mi = pWallet->mapWallet.find(input.prevout.hash);
+ assert(mi != pWallet->mapWallet.end() && input.prevout.n < mi->second.tx->vout.size());
+ vCoins.emplace_back(CInputCoin(&(mi->second), input.prevout.n));
+ }
+ if (!pWallet->DummySignTx(txNew, vCoins)) {
+ // This should never happen, because IsAllFromMe(ISMINE_SPENDABLE)
+ // implies that we can sign for every input.
+ return -1;
+ }
+ return GetVirtualTransactionSize(txNew);
+}
+
+CFeeBumper::CFeeBumper(const CWallet *pWallet, const uint256 txidIn, int newConfirmTarget, bool specifiedConfirmTarget, CAmount totalFee, bool newTxReplaceable)
+ :
+ txid(std::move(txidIn)),
+ nOldFee(0),
+ nNewFee(0)
+{
+ vErrors.clear();
+ bumpedTxid.SetNull();
+ AssertLockHeld(pWallet->cs_wallet);
+ if (!pWallet->mapWallet.count(txid)) {
+ vErrors.push_back("Invalid or non-wallet transaction id");
+ currentResult = BumpFeeResult::INVALID_ADDRESS_OR_KEY;
+ return;
+ }
+ auto it = pWallet->mapWallet.find(txid);
+ const CWalletTx& wtx = it->second;
+
+ if (pWallet->HasWalletSpend(txid)) {
+ vErrors.push_back("Transaction has descendants in the wallet");
+ currentResult = BumpFeeResult::INVALID_PARAMETER;
+ return;
+ }
+
+ {
+ LOCK(mempool.cs);
+ auto it_mp = mempool.mapTx.find(txid);
+ if (it_mp != mempool.mapTx.end() && it_mp->GetCountWithDescendants() > 1) {
+ vErrors.push_back("Transaction has descendants in the mempool");
+ currentResult = BumpFeeResult::INVALID_PARAMETER;
+ return;
+ }
+ }
+
+ if (wtx.GetDepthInMainChain() != 0) {
+ vErrors.push_back("Transaction has been mined, or is conflicted with a mined transaction");
+ currentResult = BumpFeeResult::WALLET_ERROR;
+ return;
+ }
+
+ if (!SignalsOptInRBF(wtx)) {
+ vErrors.push_back("Transaction is not BIP 125 replaceable");
+ currentResult = BumpFeeResult::WALLET_ERROR;
+ return;
+ }
+
+ if (wtx.mapValue.count("replaced_by_txid")) {
+ vErrors.push_back(strprintf("Cannot bump transaction %s which was already bumped by transaction %s", txid.ToString(), wtx.mapValue.at("replaced_by_txid")));
+ currentResult = BumpFeeResult::WALLET_ERROR;
+ return;
+ }
+
+ // check that original tx consists entirely of our inputs
+ // if not, we can't bump the fee, because the wallet has no way of knowing the value of the other inputs (thus the fee)
+ if (!pWallet->IsAllFromMe(wtx, ISMINE_SPENDABLE)) {
+ vErrors.push_back("Transaction contains inputs that don't belong to this wallet");
+ currentResult = BumpFeeResult::WALLET_ERROR;
+ return;
+ }
+
+ // figure out which output was change
+ // if there was no change output or multiple change outputs, fail
+ int nOutput = -1;
+ for (size_t i = 0; i < wtx.tx->vout.size(); ++i) {
+ if (pWallet->IsChange(wtx.tx->vout[i])) {
+ if (nOutput != -1) {
+ vErrors.push_back("Transaction has multiple change outputs");
+ currentResult = BumpFeeResult::WALLET_ERROR;
+ return;
+ }
+ nOutput = i;
+ }
+ }
+ if (nOutput == -1) {
+ vErrors.push_back("Transaction does not have a change output");
+ currentResult = BumpFeeResult::WALLET_ERROR;
+ return;
+ }
+
+ // Calculate the expected size of the new transaction.
+ int64_t txSize = GetVirtualTransactionSize(*(wtx.tx));
+ const int64_t maxNewTxSize = CalculateMaximumSignedTxSize(*wtx.tx, pWallet);
+ if (maxNewTxSize < 0) {
+ vErrors.push_back("Transaction contains inputs that cannot be signed");
+ currentResult = BumpFeeResult::INVALID_ADDRESS_OR_KEY;
+ return;
+ }
+
+ // calculate the old fee and fee-rate
+ nOldFee = wtx.GetDebit(ISMINE_SPENDABLE) - wtx.tx->GetValueOut();
+ CFeeRate nOldFeeRate(nOldFee, txSize);
+ CFeeRate nNewFeeRate;
+ // The wallet uses a conservative WALLET_INCREMENTAL_RELAY_FEE value to
+ // future proof against changes to network wide policy for incremental relay
+ // fee that our node may not be aware of.
+ CFeeRate walletIncrementalRelayFee = CFeeRate(WALLET_INCREMENTAL_RELAY_FEE);
+ if (::incrementalRelayFee > walletIncrementalRelayFee) {
+ walletIncrementalRelayFee = ::incrementalRelayFee;
+ }
+
+ if (totalFee > 0) {
+ CAmount minTotalFee = nOldFeeRate.GetFee(maxNewTxSize) + ::incrementalRelayFee.GetFee(maxNewTxSize);
+ if (totalFee < minTotalFee) {
+ vErrors.push_back(strprintf("Insufficient totalFee, must be at least %s (oldFee %s + incrementalFee %s)",
+ FormatMoney(minTotalFee), FormatMoney(nOldFeeRate.GetFee(maxNewTxSize)), FormatMoney(::incrementalRelayFee.GetFee(maxNewTxSize))));
+ currentResult = BumpFeeResult::INVALID_PARAMETER;
+ return;
+ }
+ CAmount requiredFee = CWallet::GetRequiredFee(maxNewTxSize);
+ if (totalFee < requiredFee) {
+ vErrors.push_back(strprintf("Insufficient totalFee (cannot be less than required fee %s)",
+ FormatMoney(requiredFee)));
+ currentResult = BumpFeeResult::INVALID_PARAMETER;
+ return;
+ }
+ nNewFee = totalFee;
+ nNewFeeRate = CFeeRate(totalFee, maxNewTxSize);
+ } else {
+ // if user specified a confirm target then don't consider any global payTxFee
+ if (specifiedConfirmTarget) {
+ nNewFee = CWallet::GetMinimumFee(maxNewTxSize, newConfirmTarget, mempool, CAmount(0));
+ }
+ // otherwise use the regular wallet logic to select payTxFee or default confirm target
+ else {
+ nNewFee = CWallet::GetMinimumFee(maxNewTxSize, newConfirmTarget, mempool);
+ }
+
+ nNewFeeRate = CFeeRate(nNewFee, maxNewTxSize);
+
+ // New fee rate must be at least old rate + minimum incremental relay rate
+ // walletIncrementalRelayFee.GetFeePerK() should be exact, because it's initialized
+ // in that unit (fee per kb).
+ // However, nOldFeeRate is a calculated value from the tx fee/size, so
+ // add 1 satoshi to the result, because it may have been rounded down.
+ if (nNewFeeRate.GetFeePerK() < nOldFeeRate.GetFeePerK() + 1 + walletIncrementalRelayFee.GetFeePerK()) {
+ nNewFeeRate = CFeeRate(nOldFeeRate.GetFeePerK() + 1 + walletIncrementalRelayFee.GetFeePerK());
+ nNewFee = nNewFeeRate.GetFee(maxNewTxSize);
+ }
+ }
+
+ // Check that in all cases the new fee doesn't violate maxTxFee
+ if (nNewFee > maxTxFee) {
+ vErrors.push_back(strprintf("Specified or calculated fee %s is too high (cannot be higher than maxTxFee %s)",
+ FormatMoney(nNewFee), FormatMoney(maxTxFee)));
+ currentResult = BumpFeeResult::WALLET_ERROR;
+ return;
+ }
+
+ // check that fee rate is higher than mempool's minimum fee
+ // (no point in bumping fee if we know that the new tx won't be accepted to the mempool)
+ // This may occur if the user set TotalFee or paytxfee too low, if fallbackfee is too low, or, perhaps,
+ // in a rare situation where the mempool minimum fee increased significantly since the fee estimation just a
+ // moment earlier. In this case, we report an error to the user, who may use totalFee to make an adjustment.
+ CFeeRate minMempoolFeeRate = mempool.GetMinFee(GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000);
+ if (nNewFeeRate.GetFeePerK() < minMempoolFeeRate.GetFeePerK()) {
+ vErrors.push_back(strprintf("New fee rate (%s) is less than the minimum fee rate (%s) to get into the mempool. totalFee value should to be at least %s or settxfee value should be at least %s to add transaction.", FormatMoney(nNewFeeRate.GetFeePerK()), FormatMoney(minMempoolFeeRate.GetFeePerK()), FormatMoney(minMempoolFeeRate.GetFee(maxNewTxSize)), FormatMoney(minMempoolFeeRate.GetFeePerK())));
+ currentResult = BumpFeeResult::WALLET_ERROR;
+ return;
+ }
+
+ // Now modify the output to increase the fee.
+ // If the output is not large enough to pay the fee, fail.
+ CAmount nDelta = nNewFee - nOldFee;
+ assert(nDelta > 0);
+ mtx = *wtx.tx;
+ CTxOut* poutput = &(mtx.vout[nOutput]);
+ if (poutput->nValue < nDelta) {
+ vErrors.push_back("Change output is too small to bump the fee");
+ currentResult = BumpFeeResult::WALLET_ERROR;
+ return;
+ }
+
+ // If the output would become dust, discard it (converting the dust to fee)
+ poutput->nValue -= nDelta;
+ if (poutput->nValue <= poutput->GetDustThreshold(::dustRelayFee)) {
+ LogPrint(BCLog::RPC, "Bumping fee and discarding dust output\n");
+ nNewFee += poutput->nValue;
+ mtx.vout.erase(mtx.vout.begin() + nOutput);
+ }
+
+ // Mark new tx not replaceable, if requested.
+ if (!newTxReplaceable) {
+ for (auto& input : mtx.vin) {
+ if (input.nSequence < 0xfffffffe) input.nSequence = 0xfffffffe;
+ }
+ }
+
+ currentResult = BumpFeeResult::OK;
+}
+
+bool CFeeBumper::signTransaction(CWallet *pWallet)
+{
+ return pWallet->SignTransaction(mtx);
+}
+
+bool CFeeBumper::commit(CWallet *pWallet)
+{
+ AssertLockHeld(pWallet->cs_wallet);
+ if (!vErrors.empty() || currentResult != BumpFeeResult::OK) {
+ return false;
+ }
+ if (txid.IsNull() || !pWallet->mapWallet.count(txid)) {
+ vErrors.push_back("Invalid or non-wallet transaction id");
+ currentResult = BumpFeeResult::MISC_ERROR;
+ return false;
+ }
+ CWalletTx& oldWtx = pWallet->mapWallet[txid];
+
+ CWalletTx wtxBumped(pWallet, MakeTransactionRef(std::move(mtx)));
+ // commit/broadcast the tx
+ CReserveKey reservekey(pWallet);
+ wtxBumped.mapValue = oldWtx.mapValue;
+ wtxBumped.mapValue["replaces_txid"] = oldWtx.GetHash().ToString();
+ wtxBumped.vOrderForm = oldWtx.vOrderForm;
+ wtxBumped.strFromAccount = oldWtx.strFromAccount;
+ wtxBumped.fTimeReceivedIsTxTime = true;
+ wtxBumped.fFromMe = true;
+ CValidationState state;
+ if (!pWallet->CommitTransaction(wtxBumped, reservekey, g_connman.get(), state)) {
+ // NOTE: CommitTransaction never returns false, so this should never happen.
+ vErrors.push_back(strprintf("Error: The transaction was rejected! Reason given: %s", state.GetRejectReason()));
+ return false;
+ }
+
+ bumpedTxid = wtxBumped.GetHash();
+ if (state.IsInvalid()) {
+ // This can happen if the mempool rejected the transaction. Report
+ // what happened in the "errors" response.
+ vErrors.push_back(strprintf("Error: The transaction was rejected: %s", FormatStateMessage(state)));
+ }
+
+ // mark the original tx as bumped
+ if (!pWallet->MarkReplaced(oldWtx.GetHash(), wtxBumped.GetHash())) {
+ // TODO: see if JSON-RPC has a standard way of returning a response
+ // along with an exception. It would be good to return information about
+ // wtxBumped to the caller even if marking the original transaction
+ // replaced does not succeed for some reason.
+ vErrors.push_back("Error: Created new bumpfee transaction but could not mark the original transaction as replaced.");
+ }
+ return true;
+}
+
diff --git a/src/wallet/feebumper.h b/src/wallet/feebumper.h
new file mode 100644
index 0000000000..681f55e4e5
--- /dev/null
+++ b/src/wallet/feebumper.h
@@ -0,0 +1,56 @@
+// Copyright (c) 2017 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_WALLET_FEEBUMPER_H
+#define BITCOIN_WALLET_FEEBUMPER_H
+
+#include <primitives/transaction.h>
+
+class CWallet;
+class uint256;
+
+enum class BumpFeeResult
+{
+ OK,
+ INVALID_ADDRESS_OR_KEY,
+ INVALID_REQUEST,
+ INVALID_PARAMETER,
+ WALLET_ERROR,
+ MISC_ERROR,
+};
+
+class CFeeBumper
+{
+public:
+ CFeeBumper(const CWallet *pWalletIn, const uint256 txidIn, int newConfirmTarget, bool specifiedConfirmTarget, CAmount totalFee, bool newTxReplaceable);
+ BumpFeeResult getResult() const { return currentResult; }
+ const std::vector<std::string>& getErrors() const { return vErrors; }
+ CAmount getOldFee() const { return nOldFee; }
+ CAmount getNewFee() const { return nNewFee; }
+ uint256 getBumpedTxId() const { return bumpedTxid; }
+
+ /* signs the new transaction,
+ * returns false if the tx couldn't be found or if it was
+ * impossible to create the signature(s)
+ */
+ bool signTransaction(CWallet *pWallet);
+
+ /* commits the fee bump,
+ * returns true, in case of CWallet::CommitTransaction was successful
+ * but, eventually sets vErrors if the tx could not be added to the mempool (will try later)
+ * or if the old transaction could not be marked as replaced
+ */
+ bool commit(CWallet *pWalletNonConst);
+
+private:
+ const uint256 txid;
+ uint256 bumpedTxid;
+ CMutableTransaction mtx;
+ std::vector<std::string> vErrors;
+ BumpFeeResult currentResult;
+ CAmount nOldFee;
+ CAmount nNewFee;
+};
+
+#endif // BITCOIN_WALLET_FEEBUMPER_H
diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp
index ccb744c6b2..2cc3072c16 100644
--- a/src/wallet/rpcwallet.cpp
+++ b/src/wallet/rpcwallet.cpp
@@ -18,8 +18,9 @@
#include "timedata.h"
#include "util.h"
#include "utilmoneystr.h"
-#include "wallet.h"
-#include "walletdb.h"
+#include "wallet/feebumper.h"
+#include "wallet/wallet.h"
+#include "wallet/walletdb.h"
#include <stdint.h>
@@ -2778,33 +2779,6 @@ UniValue fundrawtransaction(const JSONRPCRequest& request)
return result;
}
-// Calculate the size of the transaction assuming all signatures are max size
-// Use DummySignatureCreator, which inserts 72 byte signatures everywhere.
-// TODO: re-use this in CWallet::CreateTransaction (right now
-// CreateTransaction uses the constructed dummy-signed tx to do a priority
-// calculation, but we should be able to refactor after priority is removed).
-// NOTE: this requires that all inputs must be in mapWallet (eg the tx should
-// be IsAllFromMe).
-int64_t CalculateMaximumSignedTxSize(const CTransaction &tx, CWallet &wallet)
-{
- CMutableTransaction txNew(tx);
- std::vector<std::pair<CWalletTx*, unsigned int>> vCoins;
- // Look up the inputs. We should have already checked that this transaction
- // IsAllFromMe(ISMINE_SPENDABLE), so every input should already be in our
- // wallet, with a valid index into the vout array.
- for (auto& input : tx.vin) {
- const auto mi = wallet.mapWallet.find(input.prevout.hash);
- assert(mi != wallet.mapWallet.end() && input.prevout.n < mi->second.tx->vout.size());
- vCoins.emplace_back(std::make_pair(&(mi->second), input.prevout.n));
- }
- if (!wallet.DummySignTx(txNew, vCoins)) {
- // This should never happen, because IsAllFromMe(ISMINE_SPENDABLE)
- // implies that we can sign for every input.
- throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Transaction contains inputs that cannot be signed");
- }
- return GetVirtualTransactionSize(txNew);
-}
-
UniValue bumpfee(const JSONRPCRequest& request)
{
CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
@@ -2859,63 +2833,6 @@ UniValue bumpfee(const JSONRPCRequest& request)
uint256 hash;
hash.SetHex(request.params[0].get_str());
- // retrieve the original tx from the wallet
- LOCK2(cs_main, pwallet->cs_wallet);
- EnsureWalletIsUnlocked(pwallet);
- if (!pwallet->mapWallet.count(hash)) {
- throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid or non-wallet transaction id");
- }
- CWalletTx& wtx = pwallet->mapWallet[hash];
-
- if (pwallet->HasWalletSpend(hash)) {
- throw JSONRPCError(RPC_INVALID_PARAMETER, "Transaction has descendants in the wallet");
- }
-
- {
- LOCK(mempool.cs);
- auto it = mempool.mapTx.find(hash);
- if (it != mempool.mapTx.end() && it->GetCountWithDescendants() > 1) {
- throw JSONRPCError(RPC_INVALID_PARAMETER, "Transaction has descendants in the mempool");
- }
- }
-
- if (wtx.GetDepthInMainChain() != 0) {
- throw JSONRPCError(RPC_WALLET_ERROR, "Transaction has been mined, or is conflicted with a mined transaction");
- }
-
- if (!SignalsOptInRBF(wtx)) {
- throw JSONRPCError(RPC_WALLET_ERROR, "Transaction is not BIP 125 replaceable");
- }
-
- if (wtx.mapValue.count("replaced_by_txid")) {
- throw JSONRPCError(RPC_WALLET_ERROR, strprintf("Cannot bump transaction %s which was already bumped by transaction %s", hash.ToString(), wtx.mapValue.at("replaced_by_txid")));
- }
-
- // check that original tx consists entirely of our inputs
- // if not, we can't bump the fee, because the wallet has no way of knowing the value of the other inputs (thus the fee)
- if (!pwallet->IsAllFromMe(wtx, ISMINE_SPENDABLE)) {
- throw JSONRPCError(RPC_WALLET_ERROR, "Transaction contains inputs that don't belong to this wallet");
- }
-
- // figure out which output was change
- // if there was no change output or multiple change outputs, fail
- int nOutput = -1;
- for (size_t i = 0; i < wtx.tx->vout.size(); ++i) {
- if (pwallet->IsChange(wtx.tx->vout[i])) {
- if (nOutput != -1) {
- throw JSONRPCError(RPC_WALLET_ERROR, "Transaction has multiple change outputs");
- }
- nOutput = i;
- }
- }
- if (nOutput == -1) {
- throw JSONRPCError(RPC_WALLET_ERROR, "Transaction does not have a change output");
- }
-
- // Calculate the expected size of the new transaction.
- int64_t txSize = GetVirtualTransactionSize(*(wtx.tx));
- const int64_t maxNewTxSize = CalculateMaximumSignedTxSize(*wtx.tx, *pwallet);
-
// optional parameters
bool specifiedConfirmTarget = false;
int newConfirmTarget = nTxConfirmTarget;
@@ -2941,11 +2858,8 @@ UniValue bumpfee(const JSONRPCRequest& request)
}
} else if (options.exists("totalFee")) {
totalFee = options["totalFee"].get_int64();
- CAmount requiredFee = CWallet::GetRequiredFee(maxNewTxSize);
- if (totalFee < requiredFee ) {
- throw JSONRPCError(RPC_INVALID_PARAMETER,
- strprintf("Insufficient totalFee (cannot be less than required fee %s)",
- FormatMoney(requiredFee)));
+ if (totalFee <= 0) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Invalid totalFee %s (must be greater than 0)", FormatMoney(totalFee)));
}
}
@@ -2954,144 +2868,48 @@ UniValue bumpfee(const JSONRPCRequest& request)
}
}
- // calculate the old fee and fee-rate
- CAmount nOldFee = wtx.GetDebit(ISMINE_SPENDABLE) - wtx.tx->GetValueOut();
- CFeeRate nOldFeeRate(nOldFee, txSize);
- CAmount nNewFee;
- CFeeRate nNewFeeRate;
- // The wallet uses a conservative WALLET_INCREMENTAL_RELAY_FEE value to
- // future proof against changes to network wide policy for incremental relay
- // fee that our node may not be aware of.
- CFeeRate walletIncrementalRelayFee = CFeeRate(WALLET_INCREMENTAL_RELAY_FEE);
- if (::incrementalRelayFee > walletIncrementalRelayFee) {
- walletIncrementalRelayFee = ::incrementalRelayFee;
- }
-
- if (totalFee > 0) {
- CAmount minTotalFee = nOldFeeRate.GetFee(maxNewTxSize) + ::incrementalRelayFee.GetFee(maxNewTxSize);
- if (totalFee < minTotalFee) {
- throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Insufficient totalFee, must be at least %s (oldFee %s + incrementalFee %s)",
- FormatMoney(minTotalFee), FormatMoney(nOldFeeRate.GetFee(maxNewTxSize)), FormatMoney(::incrementalRelayFee.GetFee(maxNewTxSize))));
- }
- nNewFee = totalFee;
- nNewFeeRate = CFeeRate(totalFee, maxNewTxSize);
- } else {
- // if user specified a confirm target then don't consider any global payTxFee
- if (specifiedConfirmTarget) {
- nNewFee = CWallet::GetMinimumFee(maxNewTxSize, newConfirmTarget, mempool, CAmount(0));
- }
- // otherwise use the regular wallet logic to select payTxFee or default confirm target
- else {
- nNewFee = CWallet::GetMinimumFee(maxNewTxSize, newConfirmTarget, mempool);
- }
-
- nNewFeeRate = CFeeRate(nNewFee, maxNewTxSize);
-
- // New fee rate must be at least old rate + minimum incremental relay rate
- // walletIncrementalRelayFee.GetFeePerK() should be exact, because it's initialized
- // in that unit (fee per kb).
- // However, nOldFeeRate is a calculated value from the tx fee/size, so
- // add 1 satoshi to the result, because it may have been rounded down.
- if (nNewFeeRate.GetFeePerK() < nOldFeeRate.GetFeePerK() + 1 + walletIncrementalRelayFee.GetFeePerK()) {
- nNewFeeRate = CFeeRate(nOldFeeRate.GetFeePerK() + 1 + walletIncrementalRelayFee.GetFeePerK());
- nNewFee = nNewFeeRate.GetFee(maxNewTxSize);
- }
- }
-
- // Check that in all cases the new fee doesn't violate maxTxFee
- if (nNewFee > maxTxFee) {
- throw JSONRPCError(RPC_WALLET_ERROR,
- strprintf("Specified or calculated fee %s is too high (cannot be higher than maxTxFee %s)",
- FormatMoney(nNewFee), FormatMoney(maxTxFee)));
- }
-
- // check that fee rate is higher than mempool's minimum fee
- // (no point in bumping fee if we know that the new tx won't be accepted to the mempool)
- // This may occur if the user set TotalFee or paytxfee too low, if fallbackfee is too low, or, perhaps,
- // in a rare situation where the mempool minimum fee increased significantly since the fee estimation just a
- // moment earlier. In this case, we report an error to the user, who may use totalFee to make an adjustment.
- CFeeRate minMempoolFeeRate = mempool.GetMinFee(GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000);
- if (nNewFeeRate.GetFeePerK() < minMempoolFeeRate.GetFeePerK()) {
- throw JSONRPCError(RPC_WALLET_ERROR, strprintf("New fee rate (%s) is less than the minimum fee rate (%s) to get into the mempool. totalFee value should to be at least %s or settxfee value should be at least %s to add transaction.", FormatMoney(nNewFeeRate.GetFeePerK()), FormatMoney(minMempoolFeeRate.GetFeePerK()), FormatMoney(minMempoolFeeRate.GetFee(maxNewTxSize)), FormatMoney(minMempoolFeeRate.GetFeePerK())));
- }
-
- // Now modify the output to increase the fee.
- // If the output is not large enough to pay the fee, fail.
- CAmount nDelta = nNewFee - nOldFee;
- assert(nDelta > 0);
- CMutableTransaction tx(*(wtx.tx));
- CTxOut* poutput = &(tx.vout[nOutput]);
- if (poutput->nValue < nDelta) {
- throw JSONRPCError(RPC_WALLET_ERROR, "Change output is too small to bump the fee");
- }
-
- // If the output would become dust, discard it (converting the dust to fee)
- poutput->nValue -= nDelta;
- if (poutput->nValue <= poutput->GetDustThreshold(::dustRelayFee)) {
- LogPrint(BCLog::RPC, "Bumping fee and discarding dust output\n");
- nNewFee += poutput->nValue;
- tx.vout.erase(tx.vout.begin() + nOutput);
- }
-
- // Mark new tx not replaceable, if requested.
- if (!replaceable) {
- for (auto& input : tx.vin) {
- if (input.nSequence < 0xfffffffe) input.nSequence = 0xfffffffe;
- }
- }
+ LOCK2(cs_main, pwallet->cs_wallet);
+ EnsureWalletIsUnlocked(pwallet);
- // sign the new tx
- CTransaction txNewConst(tx);
- int nIn = 0;
- for (auto& input : tx.vin) {
- std::map<uint256, CWalletTx>::const_iterator mi = pwallet->mapWallet.find(input.prevout.hash);
- assert(mi != pwallet->mapWallet.end() && input.prevout.n < mi->second.tx->vout.size());
- const CScript& scriptPubKey = mi->second.tx->vout[input.prevout.n].scriptPubKey;
- const CAmount& amount = mi->second.tx->vout[input.prevout.n].nValue;
- SignatureData sigdata;
- if (!ProduceSignature(TransactionSignatureCreator(pwallet, &txNewConst, nIn, amount, SIGHASH_ALL), scriptPubKey, sigdata)) {
- throw JSONRPCError(RPC_WALLET_ERROR, "Can't sign transaction.");
+ CFeeBumper feeBump(pwallet, hash, newConfirmTarget, specifiedConfirmTarget, totalFee, replaceable);
+ BumpFeeResult res = feeBump.getResult();
+ if (res != BumpFeeResult::OK)
+ {
+ switch(res) {
+ case BumpFeeResult::INVALID_ADDRESS_OR_KEY:
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, feeBump.getErrors()[0]);
+ break;
+ case BumpFeeResult::INVALID_REQUEST:
+ throw JSONRPCError(RPC_INVALID_REQUEST, feeBump.getErrors()[0]);
+ break;
+ case BumpFeeResult::INVALID_PARAMETER:
+ throw JSONRPCError(RPC_INVALID_PARAMETER, feeBump.getErrors()[0]);
+ break;
+ case BumpFeeResult::WALLET_ERROR:
+ throw JSONRPCError(RPC_WALLET_ERROR, feeBump.getErrors()[0]);
+ break;
+ default:
+ throw JSONRPCError(RPC_MISC_ERROR, feeBump.getErrors()[0]);
+ break;
}
- UpdateTransaction(tx, nIn, sigdata);
- nIn++;
}
- // commit/broadcast the tx
- CReserveKey reservekey(pwallet);
- CWalletTx wtxBumped(pwallet, MakeTransactionRef(std::move(tx)));
- wtxBumped.mapValue = wtx.mapValue;
- wtxBumped.mapValue["replaces_txid"] = hash.ToString();
- wtxBumped.vOrderForm = wtx.vOrderForm;
- wtxBumped.strFromAccount = wtx.strFromAccount;
- wtxBumped.fTimeReceivedIsTxTime = true;
- wtxBumped.fFromMe = true;
- CValidationState state;
- if (!pwallet->CommitTransaction(wtxBumped, reservekey, g_connman.get(), state)) {
- // NOTE: CommitTransaction never returns false, so this should never happen.
- throw JSONRPCError(RPC_WALLET_ERROR, strprintf("Error: The transaction was rejected! Reason given: %s", state.GetRejectReason()));
- }
-
- UniValue vErrors(UniValue::VARR);
- if (state.IsInvalid()) {
- // This can happen if the mempool rejected the transaction. Report
- // what happened in the "errors" response.
- vErrors.push_back(strprintf("Error: The transaction was rejected: %s", FormatStateMessage(state)));
+ // sign bumped transaction
+ if (!feeBump.signTransaction(pwallet)) {
+ throw JSONRPCError(RPC_WALLET_ERROR, "Can't sign transaction.");
}
-
- // mark the original tx as bumped
- if (!pwallet->MarkReplaced(wtx.GetHash(), wtxBumped.GetHash())) {
- // TODO: see if JSON-RPC has a standard way of returning a response
- // along with an exception. It would be good to return information about
- // wtxBumped to the caller even if marking the original transaction
- // replaced does not succeed for some reason.
- vErrors.push_back("Error: Created new bumpfee transaction but could not mark the original transaction as replaced.");
+ // commit the bumped transaction
+ if(!feeBump.commit(pwallet)) {
+ throw JSONRPCError(RPC_WALLET_ERROR, feeBump.getErrors()[0]);
}
-
UniValue result(UniValue::VOBJ);
- result.push_back(Pair("txid", wtxBumped.GetHash().GetHex()));
- result.push_back(Pair("origfee", ValueFromAmount(nOldFee)));
- result.push_back(Pair("fee", ValueFromAmount(nNewFee)));
- result.push_back(Pair("errors", vErrors));
+ result.push_back(Pair("txid", feeBump.getBumpedTxId().GetHex()));
+ result.push_back(Pair("origfee", ValueFromAmount(feeBump.getOldFee())));
+ result.push_back(Pair("fee", ValueFromAmount(feeBump.getNewFee())));
+ UniValue errors(UniValue::VARR);
+ for (const std::string& err: feeBump.getErrors())
+ errors.push_back(err);
+ result.push_back(errors);
return result;
}
diff --git a/src/wallet/test/wallet_tests.cpp b/src/wallet/test/wallet_tests.cpp
index 67e5e90224..0335361921 100644
--- a/src/wallet/test/wallet_tests.cpp
+++ b/src/wallet/test/wallet_tests.cpp
@@ -29,7 +29,7 @@ extern UniValue importmulti(const JSONRPCRequest& request);
std::vector<std::unique_ptr<CWalletTx>> wtxn;
-typedef std::set<std::pair<const CWalletTx*,unsigned int> > CoinSet;
+typedef std::set<CInputCoin> CoinSet;
BOOST_FIXTURE_TEST_SUITE(wallet_tests, WalletTestingSetup)
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
index 68d4bc35ee..c9ca8f653d 100644
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -11,11 +11,13 @@
#include "wallet/coincontrol.h"
#include "consensus/consensus.h"
#include "consensus/validation.h"
+#include "fs.h"
#include "key.h"
#include "keystore.h"
#include "validation.h"
#include "net.h"
#include "policy/policy.h"
+#include "policy/rbf.h"
#include "primitives/block.h"
#include "primitives/transaction.h"
#include "script/script.h"
@@ -30,7 +32,6 @@
#include <assert.h>
#include <boost/algorithm/string/replace.hpp>
-#include <boost/filesystem.hpp>
#include <boost/thread.hpp>
CWallet* pwalletMain = NULL;
@@ -64,10 +65,10 @@ const uint256 CMerkleTx::ABANDON_HASH(uint256S("00000000000000000000000000000000
struct CompareValueOnly
{
- bool operator()(const std::pair<CAmount, std::pair<const CWalletTx*, unsigned int> >& t1,
- const std::pair<CAmount, std::pair<const CWalletTx*, unsigned int> >& t2) const
+ bool operator()(const CInputCoin& t1,
+ const CInputCoin& t2) const
{
- return t1.first < t2.first;
+ return t1.txout.nValue < t2.txout.nValue;
}
};
@@ -956,9 +957,9 @@ bool CWallet::LoadToWallet(const CWalletTx& wtxIn)
/**
* Add a transaction to the wallet, or update it. pIndex and posInBlock should
* be set when the transaction was known to be included in a block. When
- * posInBlock = SYNC_TRANSACTION_NOT_IN_BLOCK (-1) , then wallet state is not
- * updated in AddToWallet, but notifications happen and cached balances are
- * marked dirty.
+ * pIndex == NULL, then wallet state is not updated in AddToWallet, but
+ * notifications happen and cached balances are marked dirty.
+ *
* If fUpdate is true, existing transactions will be updated.
* TODO: One exception to this is that the abandoned state is cleared under the
* assumption that any further notification of a transaction that was considered
@@ -966,12 +967,13 @@ bool CWallet::LoadToWallet(const CWalletTx& wtxIn)
* Abandoned state should probably be more carefully tracked via different
* posInBlock signals or by checking mempool presence when necessary.
*/
-bool CWallet::AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlockIndex* pIndex, int posInBlock, bool fUpdate)
+bool CWallet::AddToWalletIfInvolvingMe(const CTransactionRef& ptx, const CBlockIndex* pIndex, int posInBlock, bool fUpdate)
{
+ const CTransaction& tx = *ptx;
{
AssertLockHeld(cs_wallet);
- if (posInBlock != -1) {
+ if (pIndex != NULL) {
BOOST_FOREACH(const CTxIn& txin, tx.vin) {
std::pair<TxSpends::const_iterator, TxSpends::const_iterator> range = mapTxSpends.equal_range(txin.prevout);
while (range.first != range.second) {
@@ -988,10 +990,10 @@ bool CWallet::AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlockIndex
if (fExisted && !fUpdate) return false;
if (fExisted || IsMine(tx) || IsFromMe(tx))
{
- CWalletTx wtx(this, MakeTransactionRef(tx));
+ CWalletTx wtx(this, ptx);
// Get merkle branch if transaction was found in a block
- if (posInBlock != -1)
+ if (pIndex != NULL)
wtx.SetMerkleBranch(pIndex, posInBlock);
return AddToWallet(wtx, false);
@@ -1116,11 +1118,10 @@ void CWallet::MarkConflicted(const uint256& hashBlock, const uint256& hashTx)
}
}
-void CWallet::SyncTransaction(const CTransaction& tx, const CBlockIndex *pindex, int posInBlock)
-{
- LOCK2(cs_main, cs_wallet);
+void CWallet::SyncTransaction(const CTransactionRef& ptx, const CBlockIndex *pindex, int posInBlock) {
+ const CTransaction& tx = *ptx;
- if (!AddToWalletIfInvolvingMe(tx, pindex, posInBlock, true))
+ if (!AddToWalletIfInvolvingMe(ptx, pindex, posInBlock, true))
return; // Not one of ours
// If a transaction changes 'conflicted' state, that changes the balance
@@ -1133,6 +1134,38 @@ void CWallet::SyncTransaction(const CTransaction& tx, const CBlockIndex *pindex,
}
}
+void CWallet::TransactionAddedToMempool(const CTransactionRef& ptx) {
+ LOCK2(cs_main, cs_wallet);
+ SyncTransaction(ptx);
+}
+
+void CWallet::BlockConnected(const std::shared_ptr<const CBlock>& pblock, const CBlockIndex *pindex, const std::vector<CTransactionRef>& vtxConflicted) {
+ LOCK2(cs_main, cs_wallet);
+ // TODO: Tempoarily ensure that mempool removals are notified before
+ // connected transactions. This shouldn't matter, but the abandoned
+ // state of transactions in our wallet is currently cleared when we
+ // receive another notification and there is a race condition where
+ // notification of a connected conflict might cause an outside process
+ // to abandon a transaction and then have it inadvertantly cleared by
+ // the notification that the conflicted transaction was evicted.
+
+ for (const CTransactionRef& ptx : vtxConflicted) {
+ SyncTransaction(ptx);
+ }
+ for (size_t i = 0; i < pblock->vtx.size(); i++) {
+ SyncTransaction(pblock->vtx[i], pindex, i);
+ }
+}
+
+void CWallet::BlockDisconnected(const std::shared_ptr<const CBlock>& pblock) {
+ LOCK2(cs_main, cs_wallet);
+
+ for (const CTransactionRef& ptx : pblock->vtx) {
+ SyncTransaction(ptx);
+ }
+}
+
+
isminetype CWallet::IsMine(const CTxIn &txin) const
{
@@ -1511,7 +1544,7 @@ CBlockIndex* CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, bool f
CBlock block;
if (ReadBlockFromDisk(block, pindex, Params().GetConsensus())) {
for (size_t posInBlock = 0; posInBlock < block.vtx.size(); ++posInBlock) {
- AddToWalletIfInvolvingMe(*block.vtx[posInBlock], pindex, posInBlock, fUpdate);
+ AddToWalletIfInvolvingMe(block.vtx[posInBlock], pindex, posInBlock, fUpdate);
}
if (!ret) {
ret = pindex;
@@ -1760,10 +1793,7 @@ CAmount CWalletTx::GetChange() const
bool CWalletTx::InMempool() const
{
LOCK(mempool.cs);
- if (mempool.exists(GetHash())) {
- return true;
- }
- return false;
+ return mempool.exists(GetHash());
}
bool CWalletTx::IsTrusted() const
@@ -2031,7 +2061,7 @@ void CWallet::AvailableCoins(std::vector<COutput>& vCoins, bool fOnlySafe, const
}
}
-static void ApproximateBestSubset(const std::vector<std::pair<CAmount, std::pair<const CWalletTx*,unsigned int> > >& vValue, const CAmount& nTotalLower, const CAmount& nTargetValue,
+static void ApproximateBestSubset(const std::vector<CInputCoin>& vValue, const CAmount& nTotalLower, const CAmount& nTargetValue,
std::vector<char>& vfBest, CAmount& nBest, int iterations = 1000)
{
std::vector<char> vfIncluded;
@@ -2058,7 +2088,7 @@ static void ApproximateBestSubset(const std::vector<std::pair<CAmount, std::pair
//the selection random.
if (nPass == 0 ? insecure_rand.rand32()&1 : !vfIncluded[i])
{
- nTotal += vValue[i].first;
+ nTotal += vValue[i].txout.nValue;
vfIncluded[i] = true;
if (nTotal >= nTargetValue)
{
@@ -2068,7 +2098,7 @@ static void ApproximateBestSubset(const std::vector<std::pair<CAmount, std::pair
nBest = nTotal;
vfBest = vfIncluded;
}
- nTotal -= vValue[i].first;
+ nTotal -= vValue[i].txout.nValue;
vfIncluded[i] = false;
}
}
@@ -2078,16 +2108,14 @@ static void ApproximateBestSubset(const std::vector<std::pair<CAmount, std::pair
}
bool CWallet::SelectCoinsMinConf(const CAmount& nTargetValue, const int nConfMine, const int nConfTheirs, const uint64_t nMaxAncestors, std::vector<COutput> vCoins,
- std::set<std::pair<const CWalletTx*,unsigned int> >& setCoinsRet, CAmount& nValueRet) const
+ std::set<CInputCoin>& setCoinsRet, CAmount& nValueRet) const
{
setCoinsRet.clear();
nValueRet = 0;
// List of values less than target
- std::pair<CAmount, std::pair<const CWalletTx*,unsigned int> > coinLowestLarger;
- coinLowestLarger.first = std::numeric_limits<CAmount>::max();
- coinLowestLarger.second.first = NULL;
- std::vector<std::pair<CAmount, std::pair<const CWalletTx*,unsigned int> > > vValue;
+ boost::optional<CInputCoin> coinLowestLarger;
+ std::vector<CInputCoin> vValue;
CAmount nTotalLower = 0;
random_shuffle(vCoins.begin(), vCoins.end(), GetRandInt);
@@ -2106,22 +2134,21 @@ bool CWallet::SelectCoinsMinConf(const CAmount& nTargetValue, const int nConfMin
continue;
int i = output.i;
- CAmount n = pcoin->tx->vout[i].nValue;
- std::pair<CAmount,std::pair<const CWalletTx*,unsigned int> > coin = std::make_pair(n,std::make_pair(pcoin, i));
+ CInputCoin coin = CInputCoin(pcoin, i);
- if (n == nTargetValue)
+ if (coin.txout.nValue == nTargetValue)
{
- setCoinsRet.insert(coin.second);
- nValueRet += coin.first;
+ setCoinsRet.insert(coin);
+ nValueRet += coin.txout.nValue;
return true;
}
- else if (n < nTargetValue + MIN_CHANGE)
+ else if (coin.txout.nValue < nTargetValue + MIN_CHANGE)
{
vValue.push_back(coin);
- nTotalLower += n;
+ nTotalLower += coin.txout.nValue;
}
- else if (n < coinLowestLarger.first)
+ else if (!coinLowestLarger || coin.txout.nValue < coinLowestLarger->txout.nValue)
{
coinLowestLarger = coin;
}
@@ -2131,18 +2158,18 @@ bool CWallet::SelectCoinsMinConf(const CAmount& nTargetValue, const int nConfMin
{
for (unsigned int i = 0; i < vValue.size(); ++i)
{
- setCoinsRet.insert(vValue[i].second);
- nValueRet += vValue[i].first;
+ setCoinsRet.insert(vValue[i]);
+ nValueRet += vValue[i].txout.nValue;
}
return true;
}
if (nTotalLower < nTargetValue)
{
- if (coinLowestLarger.second.first == NULL)
+ if (!coinLowestLarger)
return false;
- setCoinsRet.insert(coinLowestLarger.second);
- nValueRet += coinLowestLarger.first;
+ setCoinsRet.insert(coinLowestLarger.get());
+ nValueRet += coinLowestLarger->txout.nValue;
return true;
}
@@ -2158,25 +2185,25 @@ bool CWallet::SelectCoinsMinConf(const CAmount& nTargetValue, const int nConfMin
// If we have a bigger coin and (either the stochastic approximation didn't find a good solution,
// or the next bigger coin is closer), return the bigger coin
- if (coinLowestLarger.second.first &&
- ((nBest != nTargetValue && nBest < nTargetValue + MIN_CHANGE) || coinLowestLarger.first <= nBest))
+ if (coinLowestLarger &&
+ ((nBest != nTargetValue && nBest < nTargetValue + MIN_CHANGE) || coinLowestLarger->txout.nValue <= nBest))
{
- setCoinsRet.insert(coinLowestLarger.second);
- nValueRet += coinLowestLarger.first;
+ setCoinsRet.insert(coinLowestLarger.get());
+ nValueRet += coinLowestLarger->txout.nValue;
}
else {
for (unsigned int i = 0; i < vValue.size(); i++)
if (vfBest[i])
{
- setCoinsRet.insert(vValue[i].second);
- nValueRet += vValue[i].first;
+ setCoinsRet.insert(vValue[i]);
+ nValueRet += vValue[i].txout.nValue;
}
if (LogAcceptCategory(BCLog::SELECTCOINS)) {
LogPrint(BCLog::SELECTCOINS, "SelectCoins() best subset: ");
for (unsigned int i = 0; i < vValue.size(); i++) {
if (vfBest[i]) {
- LogPrint(BCLog::SELECTCOINS, "%s ", FormatMoney(vValue[i].first));
+ LogPrint(BCLog::SELECTCOINS, "%s ", FormatMoney(vValue[i].txout.nValue));
}
}
LogPrint(BCLog::SELECTCOINS, "total %s\n", FormatMoney(nBest));
@@ -2186,7 +2213,7 @@ bool CWallet::SelectCoinsMinConf(const CAmount& nTargetValue, const int nConfMin
return true;
}
-bool CWallet::SelectCoins(const std::vector<COutput>& vAvailableCoins, const CAmount& nTargetValue, std::set<std::pair<const CWalletTx*,unsigned int> >& setCoinsRet, CAmount& nValueRet, const CCoinControl* coinControl) const
+bool CWallet::SelectCoins(const std::vector<COutput>& vAvailableCoins, const CAmount& nTargetValue, std::set<CInputCoin>& setCoinsRet, CAmount& nValueRet, const CCoinControl* coinControl) const
{
std::vector<COutput> vCoins(vAvailableCoins);
@@ -2198,13 +2225,13 @@ bool CWallet::SelectCoins(const std::vector<COutput>& vAvailableCoins, const CAm
if (!out.fSpendable)
continue;
nValueRet += out.tx->tx->vout[out.i].nValue;
- setCoinsRet.insert(std::make_pair(out.tx, out.i));
+ setCoinsRet.insert(CInputCoin(out.tx, out.i));
}
return (nValueRet >= nTargetValue);
}
// calculate value from preset inputs and store them
- std::set<std::pair<const CWalletTx*, uint32_t> > setPresetCoins;
+ std::set<CInputCoin> setPresetCoins;
CAmount nValueFromPresetInputs = 0;
std::vector<COutPoint> vPresetInputs;
@@ -2220,7 +2247,7 @@ bool CWallet::SelectCoins(const std::vector<COutput>& vAvailableCoins, const CAm
if (pcoin->tx->vout.size() <= outpoint.n)
return false;
nValueFromPresetInputs += pcoin->tx->vout[outpoint.n].nValue;
- setPresetCoins.insert(std::make_pair(pcoin, outpoint.n));
+ setPresetCoins.insert(CInputCoin(pcoin, outpoint.n));
} else
return false; // TODO: Allow non-wallet inputs
}
@@ -2228,7 +2255,7 @@ bool CWallet::SelectCoins(const std::vector<COutput>& vAvailableCoins, const CAm
// remove preset inputs from vCoins
for (std::vector<COutput>::iterator it = vCoins.begin(); it != vCoins.end() && coinControl && coinControl->HasSelected();)
{
- if (setPresetCoins.count(std::make_pair(it->tx, it->i)))
+ if (setPresetCoins.count(CInputCoin(it->tx, it->i)))
it = vCoins.erase(it);
else
++it;
@@ -2255,6 +2282,28 @@ bool CWallet::SelectCoins(const std::vector<COutput>& vAvailableCoins, const CAm
return res;
}
+bool CWallet::SignTransaction(CMutableTransaction &tx)
+{
+ // sign the new tx
+ CTransaction txNewConst(tx);
+ int nIn = 0;
+ for (auto& input : tx.vin) {
+ std::map<uint256, CWalletTx>::const_iterator mi = mapWallet.find(input.prevout.hash);
+ if(mi == mapWallet.end() || input.prevout.n >= mi->second.tx->vout.size()) {
+ return false;
+ }
+ const CScript& scriptPubKey = mi->second.tx->vout[input.prevout.n].scriptPubKey;
+ const CAmount& amount = mi->second.tx->vout[input.prevout.n].nValue;
+ SignatureData sigdata;
+ if (!ProduceSignature(TransactionSignatureCreator(this, &txNewConst, nIn, amount, SIGHASH_ALL), scriptPubKey, sigdata)) {
+ return false;
+ }
+ UpdateTransaction(tx, nIn, sigdata);
+ nIn++;
+ }
+ return true;
+}
+
bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, bool overrideEstimatedFeeRate, const CFeeRate& specificFeeRate, int& nChangePosInOut, std::string& strFailReason, bool includeWatching, bool lockUnspents, const std::set<int>& setSubtractFeeFromOutputs, bool keepReserveKey, const CTxDestination& destChange)
{
std::vector<CRecipient> vecSend;
@@ -2372,7 +2421,7 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletT
assert(txNew.nLockTime < LOCKTIME_THRESHOLD);
{
- std::set<std::pair<const CWalletTx*,unsigned int> > setCoins;
+ std::set<CInputCoin> setCoins;
LOCK2(cs_main, cs_wallet);
{
std::vector<COutput> vAvailableCoins;
@@ -2531,7 +2580,7 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletT
// behavior."
bool rbf = coinControl ? coinControl->signalRbf : fWalletRbf;
for (const auto& coin : setCoins)
- txNew.vin.push_back(CTxIn(coin.first->GetHash(),coin.second,CScript(),
+ txNew.vin.push_back(CTxIn(coin.outpoint,CScript(),
std::numeric_limits<unsigned int>::max() - (rbf ? 2 : 1)));
// Fill in dummy signatures for fee calculation.
@@ -2614,10 +2663,10 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletT
int nIn = 0;
for (const auto& coin : setCoins)
{
- const CScript& scriptPubKey = coin.first->tx->vout[coin.second].scriptPubKey;
+ const CScript& scriptPubKey = coin.txout.scriptPubKey;
SignatureData sigdata;
- if (!ProduceSignature(TransactionSignatureCreator(this, &txNewConst, nIn, coin.first->tx->vout[coin.second].nValue, SIGHASH_ALL), scriptPubKey, sigdata))
+ if (!ProduceSignature(TransactionSignatureCreator(this, &txNewConst, nIn, coin.txout.nValue, SIGHASH_ALL), scriptPubKey, sigdata))
{
strFailReason = _("Signing transaction failed");
return false;
@@ -3345,9 +3394,9 @@ void CWallet::UpdatedTransaction(const uint256 &hashTx)
}
}
-void CWallet::GetScriptForMining(boost::shared_ptr<CReserveScript> &script)
+void CWallet::GetScriptForMining(std::shared_ptr<CReserveScript> &script)
{
- boost::shared_ptr<CReserveKey> rKey(new CReserveKey(this));
+ std::shared_ptr<CReserveKey> rKey = std::make_shared<CReserveKey>(this);
CPubKey pubkey;
if (!rKey->GetReservedKey(pubkey))
return;
@@ -3945,16 +3994,16 @@ bool CWallet::BackupWallet(const std::string& strDest)
bitdb.mapFileUseCount.erase(strWalletFile);
// Copy wallet file
- boost::filesystem::path pathSrc = GetDataDir() / strWalletFile;
- boost::filesystem::path pathDest(strDest);
- if (boost::filesystem::is_directory(pathDest))
+ fs::path pathSrc = GetDataDir() / strWalletFile;
+ fs::path pathDest(strDest);
+ if (fs::is_directory(pathDest))
pathDest /= strWalletFile;
try {
- boost::filesystem::copy_file(pathSrc, pathDest, boost::filesystem::copy_option::overwrite_if_exists);
+ fs::copy_file(pathSrc, pathDest, fs::copy_option::overwrite_if_exists);
LogPrintf("copied %s to %s\n", strWalletFile, pathDest.string());
return true;
- } catch (const boost::filesystem::filesystem_error& e) {
+ } catch (const fs::filesystem_error& e) {
LogPrintf("error copying %s to %s - %s\n", strWalletFile, pathDest.string(), e.what());
return false;
}
diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h
index ccede60097..cc1a6b7183 100644
--- a/src/wallet/wallet.h
+++ b/src/wallet/wallet.h
@@ -28,8 +28,6 @@
#include <utility>
#include <vector>
-#include <boost/shared_ptr.hpp>
-
extern CWallet* pwalletMain;
/**
@@ -475,7 +473,34 @@ public:
};
+class CInputCoin {
+public:
+ CInputCoin(const CWalletTx* walletTx, unsigned int i)
+ {
+ if (!walletTx)
+ throw std::invalid_argument("walletTx should not be null");
+ if (i >= walletTx->tx->vout.size())
+ throw std::out_of_range("The output index is out of range");
+
+ outpoint = COutPoint(walletTx->GetHash(), i);
+ txout = walletTx->tx->vout[i];
+ }
+
+ COutPoint outpoint;
+ CTxOut txout;
+
+ bool operator<(const CInputCoin& rhs) const {
+ return outpoint < rhs.outpoint;
+ }
+ bool operator!=(const CInputCoin& rhs) const {
+ return outpoint != rhs.outpoint;
+ }
+
+ bool operator==(const CInputCoin& rhs) const {
+ return outpoint == rhs.outpoint;
+ }
+};
class COutput
{
@@ -632,7 +657,7 @@ private:
* all coins from coinControl are selected; Never select unconfirmed coins
* if they are not ours
*/
- bool SelectCoins(const std::vector<COutput>& vAvailableCoins, const CAmount& nTargetValue, std::set<std::pair<const CWalletTx*,unsigned int> >& setCoinsRet, CAmount& nValueRet, const CCoinControl *coinControl = NULL) const;
+ bool SelectCoins(const std::vector<COutput>& vAvailableCoins, const CAmount& nTargetValue, std::set<CInputCoin>& setCoinsRet, CAmount& nValueRet, const CCoinControl *coinControl = NULL) const;
CWalletDB *pwalletdbEncryption;
@@ -661,6 +686,10 @@ private:
void SyncMetaData(std::pair<TxSpends::iterator, TxSpends::iterator>);
+ /* Used by TransactionAddedToMemorypool/BlockConnected/Disconnected.
+ * Should be called with pindexBlock and posInBlock if this is for a transaction that is included in a block. */
+ void SyncTransaction(const CTransactionRef& tx, const CBlockIndex *pindex = NULL, int posInBlock = 0);
+
/* the HD chain data model (external chain counters) */
CHDChain hdChain;
@@ -780,7 +809,7 @@ public:
* completion the coin set and corresponding actual target value is
* assembled
*/
- bool SelectCoinsMinConf(const CAmount& nTargetValue, int nConfMine, int nConfTheirs, uint64_t nMaxAncestors, std::vector<COutput> vCoins, std::set<std::pair<const CWalletTx*,unsigned int> >& setCoinsRet, CAmount& nValueRet) const;
+ bool SelectCoinsMinConf(const CAmount& nTargetValue, int nConfMine, int nConfTheirs, uint64_t nMaxAncestors, std::vector<COutput> vCoins, std::set<CInputCoin>& setCoinsRet, CAmount& nValueRet) const;
bool IsSpent(const uint256& hash, unsigned int n) const;
@@ -849,8 +878,10 @@ public:
void MarkDirty();
bool AddToWallet(const CWalletTx& wtxIn, bool fFlushOnClose=true);
bool LoadToWallet(const CWalletTx& wtxIn);
- void SyncTransaction(const CTransaction& tx, const CBlockIndex *pindex, int posInBlock) override;
- bool AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlockIndex* pIndex, int posInBlock, bool fUpdate);
+ void TransactionAddedToMempool(const CTransactionRef& tx) override;
+ void BlockConnected(const std::shared_ptr<const CBlock>& pblock, const CBlockIndex *pindex, const std::vector<CTransactionRef>& vtxConflicted) override;
+ void BlockDisconnected(const std::shared_ptr<const CBlock>& pblock) override;
+ bool AddToWalletIfInvolvingMe(const CTransactionRef& tx, const CBlockIndex* pIndex, int posInBlock, bool fUpdate);
CBlockIndex* ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate = false);
void ReacceptWalletTransactions();
void ResendWalletTransactions(int64_t nBestBlockTime, CConnman* connman) override;
@@ -867,6 +898,7 @@ public:
* calling CreateTransaction();
*/
bool FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, bool overrideEstimatedFeeRate, const CFeeRate& specificFeeRate, int& nChangePosInOut, std::string& strFailReason, bool includeWatching, bool lockUnspents, const std::set<int>& setSubtractFeeFromOutputs, bool keepReserveKey = true, const CTxDestination& destChange = CNoDestination());
+ bool SignTransaction(CMutableTransaction& tx);
/**
* Create a new transaction paying the recipients with a set of coins
@@ -881,7 +913,7 @@ public:
bool AddAccountingEntry(const CAccountingEntry&);
bool AddAccountingEntry(const CAccountingEntry&, CWalletDB *pwalletdb);
template <typename ContainerType>
- bool DummySignTx(CMutableTransaction &txNew, const ContainerType &coins);
+ bool DummySignTx(CMutableTransaction &txNew, const ContainerType &coins) const;
static CFeeRate minTxFee;
static CFeeRate fallbackFee;
@@ -958,12 +990,7 @@ public:
}
}
- void GetScriptForMining(boost::shared_ptr<CReserveScript> &script) override;
- void ResetRequestCount(const uint256 &hash) override
- {
- LOCK(cs_wallet);
- mapRequestCount[hash] = 0;
- };
+ void GetScriptForMining(std::shared_ptr<CReserveScript> &script) override;
unsigned int GetKeyPoolSize()
{
@@ -1125,13 +1152,13 @@ public:
// ContainerType is meant to hold pair<CWalletTx *, int>, and be iterable
// so that each entry corresponds to each vIn, in order.
template <typename ContainerType>
-bool CWallet::DummySignTx(CMutableTransaction &txNew, const ContainerType &coins)
+bool CWallet::DummySignTx(CMutableTransaction &txNew, const ContainerType &coins) const
{
// Fill in dummy signatures for fee calculation.
int nIn = 0;
for (const auto& coin : coins)
{
- const CScript& scriptPubKey = coin.first->tx->vout[coin.second].scriptPubKey;
+ const CScript& scriptPubKey = coin.txout.scriptPubKey;
SignatureData sigdata;
if (!ProduceSignature(DummySignatureCreator(this), scriptPubKey, sigdata))
diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp
index 73d79eb452..ceff2d36e3 100644
--- a/src/wallet/walletdb.cpp
+++ b/src/wallet/walletdb.cpp
@@ -7,6 +7,7 @@
#include "base58.h"
#include "consensus/validation.h"
+#include "fs.h"
#include "validation.h" // For CheckTransaction
#include "protocol.h"
#include "serialize.h"
@@ -18,7 +19,6 @@
#include <atomic>
#include <boost/version.hpp>
-#include <boost/filesystem.hpp>
#include <boost/foreach.hpp>
#include <boost/thread.hpp>
@@ -842,12 +842,12 @@ bool CWalletDB::RecoverKeysOnlyFilter(void *callbackData, CDataStream ssKey, CDa
return true;
}
-bool CWalletDB::VerifyEnvironment(const std::string& walletFile, const boost::filesystem::path& dataDir, std::string& errorStr)
+bool CWalletDB::VerifyEnvironment(const std::string& walletFile, const fs::path& dataDir, std::string& errorStr)
{
return CDB::VerifyEnvironment(walletFile, dataDir, errorStr);
}
-bool CWalletDB::VerifyDatabaseFile(const std::string& walletFile, const boost::filesystem::path& dataDir, std::string& warningStr, std::string& errorStr)
+bool CWalletDB::VerifyDatabaseFile(const std::string& walletFile, const fs::path& dataDir, std::string& warningStr, std::string& errorStr)
{
return CDB::VerifyDatabaseFile(walletFile, dataDir, errorStr, warningStr, CWalletDB::Recover);
}
diff --git a/src/wallet/walletdb.h b/src/wallet/walletdb.h
index 271c1d66c9..b94f341b2e 100644
--- a/src/wallet/walletdb.h
+++ b/src/wallet/walletdb.h
@@ -185,9 +185,9 @@ public:
/* Function to determine if a certain KV/key-type is a key (cryptographical key) type */
static bool IsKeyType(const std::string& strType);
/* verifies the database environment */
- static bool VerifyEnvironment(const std::string& walletFile, const boost::filesystem::path& dataDir, std::string& errorStr);
+ static bool VerifyEnvironment(const std::string& walletFile, const fs::path& dataDir, std::string& errorStr);
/* verifies the database file */
- static bool VerifyDatabaseFile(const std::string& walletFile, const boost::filesystem::path& dataDir, std::string& warningStr, std::string& errorStr);
+ static bool VerifyDatabaseFile(const std::string& walletFile, const fs::path& dataDir, std::string& warningStr, std::string& errorStr);
//! write the hdchain model (external chain child index counter)
bool WriteHDChain(const CHDChain& chain);
diff --git a/src/zmq/zmqnotificationinterface.cpp b/src/zmq/zmqnotificationinterface.cpp
index fac2a3c57a..c063898056 100644
--- a/src/zmq/zmqnotificationinterface.cpp
+++ b/src/zmq/zmqnotificationinterface.cpp
@@ -144,8 +144,12 @@ void CZMQNotificationInterface::UpdatedBlockTip(const CBlockIndex *pindexNew, co
}
}
-void CZMQNotificationInterface::SyncTransaction(const CTransaction& tx, const CBlockIndex* pindex, int posInBlock)
+void CZMQNotificationInterface::TransactionAddedToMempool(const CTransactionRef& ptx)
{
+ // Used by BlockConnected and BlockDisconnected as well, because they're
+ // all the same external callback.
+ const CTransaction& tx = *ptx;
+
for (std::list<CZMQAbstractNotifier*>::iterator i = notifiers.begin(); i!=notifiers.end(); )
{
CZMQAbstractNotifier *notifier = *i;
@@ -160,3 +164,19 @@ void CZMQNotificationInterface::SyncTransaction(const CTransaction& tx, const CB
}
}
}
+
+void CZMQNotificationInterface::BlockConnected(const std::shared_ptr<const CBlock>& pblock, const CBlockIndex* pindexConnected, const std::vector<CTransactionRef>& vtxConflicted)
+{
+ for (const CTransactionRef& ptx : pblock->vtx) {
+ // Do a normal notify for each transaction added in the block
+ TransactionAddedToMempool(ptx);
+ }
+}
+
+void CZMQNotificationInterface::BlockDisconnected(const std::shared_ptr<const CBlock>& pblock)
+{
+ for (const CTransactionRef& ptx : pblock->vtx) {
+ // Do a normal notify for each transaction removed in block disconnection
+ TransactionAddedToMempool(ptx);
+ }
+}
diff --git a/src/zmq/zmqnotificationinterface.h b/src/zmq/zmqnotificationinterface.h
index beabb78da6..eec6f7bc64 100644
--- a/src/zmq/zmqnotificationinterface.h
+++ b/src/zmq/zmqnotificationinterface.h
@@ -8,6 +8,7 @@
#include "validationinterface.h"
#include <string>
#include <map>
+#include <list>
class CBlockIndex;
class CZMQAbstractNotifier;
@@ -24,8 +25,10 @@ protected:
void Shutdown();
// CValidationInterface
- void SyncTransaction(const CTransaction& tx, const CBlockIndex *pindex, int posInBlock);
- void UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockIndex *pindexFork, bool fInitialDownload);
+ void TransactionAddedToMempool(const CTransactionRef& tx) override;
+ void BlockConnected(const std::shared_ptr<const CBlock>& pblock, const CBlockIndex* pindexConnected, const std::vector<CTransactionRef>& vtxConflicted) override;
+ void BlockDisconnected(const std::shared_ptr<const CBlock>& pblock) override;
+ void UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockIndex *pindexFork, bool fInitialDownload) override;
private:
CZMQNotificationInterface();