aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xcontrib/devtools/lint-whitespace.sh2
-rw-r--r--contrib/gitian-descriptors/gitian-linux.yml2
-rw-r--r--contrib/gitian-descriptors/gitian-osx.yml2
-rw-r--r--contrib/gitian-descriptors/gitian-win.yml2
-rw-r--r--depends/packages/native_mac_alias.mk9
-rw-r--r--depends/patches/native_mac_alias/python3.patch56
-rw-r--r--doc/build-windows.md7
-rw-r--r--doc/release-notes.md7
-rw-r--r--src/init.cpp2
-rw-r--r--src/qt/addresstablemodel.cpp12
-rw-r--r--src/qt/walletmodel.cpp1
-rw-r--r--src/rpc/blockchain.cpp8
-rw-r--r--src/rpc/mining.cpp4
-rw-r--r--src/rpc/rawtransaction.cpp6
-rw-r--r--src/test/util_tests.cpp130
-rw-r--r--src/util.cpp35
-rw-r--r--src/util.h6
-rw-r--r--src/validation.cpp4
-rw-r--r--src/wallet/feebumper.cpp3
-rw-r--r--src/wallet/rpcwallet.cpp5
-rw-r--r--src/wallet/test/wallet_tests.cpp3
-rw-r--r--src/wallet/wallet.cpp9
-rwxr-xr-xtest/functional/feature_nulldummy.py2
-rwxr-xr-xtest/functional/mempool_limit.py10
-rwxr-xr-xtest/functional/mining_prioritisetransaction.py2
-rwxr-xr-xtest/functional/rpc_fundrawtransaction.py3
-rw-r--r--test/functional/test_framework/util.py1
-rwxr-xr-xtest/functional/test_runner.py2
28 files changed, 224 insertions, 111 deletions
diff --git a/contrib/devtools/lint-whitespace.sh b/contrib/devtools/lint-whitespace.sh
index 989923f31a..af9a57910a 100755
--- a/contrib/devtools/lint-whitespace.sh
+++ b/contrib/devtools/lint-whitespace.sh
@@ -16,7 +16,7 @@ if [ -z "${TRAVIS_COMMIT_RANGE}" ]; then
fi
showdiff() {
- if ! git diff -U0 "${TRAVIS_COMMIT_RANGE}" -- "." ":(exclude)src/leveldb/" ":(exclude)src/secp256k1/" ":(exclude)src/univalue/" ":(exclude)doc/release-notes/"; then
+ if ! git diff -U0 "${TRAVIS_COMMIT_RANGE}" -- "." ":(exclude)depends/patches/" ":(exclude)src/leveldb/" ":(exclude)src/secp256k1/" ":(exclude)src/univalue/" ":(exclude)doc/release-notes/"; then
echo "Failed to get a diff"
exit 1
fi
diff --git a/contrib/gitian-descriptors/gitian-linux.yml b/contrib/gitian-descriptors/gitian-linux.yml
index c80e19edbb..3e9ee0495a 100644
--- a/contrib/gitian-descriptors/gitian-linux.yml
+++ b/contrib/gitian-descriptors/gitian-linux.yml
@@ -1,5 +1,5 @@
---
-name: "bitcoin-linux-0.16"
+name: "bitcoin-linux-0.17"
enable_cache: true
suites:
- "trusty"
diff --git a/contrib/gitian-descriptors/gitian-osx.yml b/contrib/gitian-descriptors/gitian-osx.yml
index cbf286d2cd..a84dce3e3a 100644
--- a/contrib/gitian-descriptors/gitian-osx.yml
+++ b/contrib/gitian-descriptors/gitian-osx.yml
@@ -1,5 +1,5 @@
---
-name: "bitcoin-osx-0.16"
+name: "bitcoin-osx-0.17"
enable_cache: true
suites:
- "trusty"
diff --git a/contrib/gitian-descriptors/gitian-win.yml b/contrib/gitian-descriptors/gitian-win.yml
index 95ff9759c7..8a87d91754 100644
--- a/contrib/gitian-descriptors/gitian-win.yml
+++ b/contrib/gitian-descriptors/gitian-win.yml
@@ -1,5 +1,5 @@
---
-name: "bitcoin-win-0.16"
+name: "bitcoin-win-0.17"
enable_cache: true
suites:
- "trusty"
diff --git a/depends/packages/native_mac_alias.mk b/depends/packages/native_mac_alias.mk
index 488ec8b59c..306c835656 100644
--- a/depends/packages/native_mac_alias.mk
+++ b/depends/packages/native_mac_alias.mk
@@ -1,14 +1,9 @@
package=native_mac_alias
-$(package)_version=2.0.6
+$(package)_version=2.0.7
$(package)_download_path=https://github.com/al45tair/mac_alias/archive/
$(package)_file_name=v$($(package)_version).tar.gz
-$(package)_sha256_hash=78a3332d9a597eebf09ae652d38ad1e263b28db5c2e6dd725fad357b03b77371
+$(package)_sha256_hash=6f606d3b6bccd2112aeabf1a063f5b5ece87005a5d7e97c8faca23b916e88838
$(package)_install_libdir=$(build_prefix)/lib/python/dist-packages
-$(package)_patches=python3.patch
-
-define $(package)_preprocess_cmds
- patch -p1 < $($(package)_patch_dir)/python3.patch
-endef
define $(package)_build_cmds
python setup.py build
diff --git a/depends/patches/native_mac_alias/python3.patch b/depends/patches/native_mac_alias/python3.patch
deleted file mode 100644
index 6f2f5534a2..0000000000
--- a/depends/patches/native_mac_alias/python3.patch
+++ /dev/null
@@ -1,56 +0,0 @@
-diff -dur a/mac_alias/alias.py b/mac_alias/alias.py
---- a/mac_alias/alias.py
-+++ b/mac_alias/alias.py
-@@ -258,10 +258,10 @@
- alias = Alias()
- alias.appinfo = appinfo
-
-- alias.volume = VolumeInfo (volname.replace('/',':'),
-+ alias.volume = VolumeInfo (volname.decode().replace('/',':'),
- voldate, fstype, disktype,
- volattrs, volfsid)
-- alias.target = TargetInfo (kind, filename.replace('/',':'),
-+ alias.target = TargetInfo (kind, filename.decode().replace('/',':'),
- folder_cnid, cnid,
- crdate, creator_code, type_code)
- alias.target.levels_from = levels_from
-@@ -276,9 +276,9 @@
- b.read(1)
-
- if tag == TAG_CARBON_FOLDER_NAME:
-- alias.target.folder_name = value.replace('/',':')
-+ alias.target.folder_name = value.decode().replace('/',':')
- elif tag == TAG_CNID_PATH:
-- alias.target.cnid_path = struct.unpack(b'>%uI' % (length // 4),
-+ alias.target.cnid_path = struct.unpack('>%uI' % (length // 4),
- value)
- elif tag == TAG_CARBON_PATH:
- alias.target.carbon_path = value
-@@ -313,9 +313,9 @@
- alias.target.creation_date \
- = mac_epoch + datetime.timedelta(seconds=seconds)
- elif tag == TAG_POSIX_PATH:
-- alias.target.posix_path = value
-+ alias.target.posix_path = value.decode()
- elif tag == TAG_POSIX_PATH_TO_MOUNTPOINT:
-- alias.volume.posix_path = value
-+ alias.volume.posix_path = value.decode()
- elif tag == TAG_RECURSIVE_ALIAS_OF_DISK_IMAGE:
- alias.volume.disk_image_alias = Alias.from_bytes(value)
- elif tag == TAG_USER_HOME_LENGTH_PREFIX:
-@@ -467,12 +467,12 @@
-
- b.write(struct.pack(b'>hhQhhQ',
- TAG_HIGH_RES_VOLUME_CREATION_DATE,
-- 8, long(voldate * 65536),
-+ 8, int(voldate * 65536),
- TAG_HIGH_RES_CREATION_DATE,
-- 8, long(crdate * 65536)))
-+ 8, int(crdate * 65536)))
-
- if self.target.cnid_path:
-- cnid_path = struct.pack(b'>%uI' % len(self.target.cnid_path),
-+ cnid_path = struct.pack('>%uI' % len(self.target.cnid_path),
- *self.target.cnid_path)
- b.write(struct.pack(b'>hh', TAG_CNID_PATH,
- len(cnid_path)))
diff --git a/doc/build-windows.md b/doc/build-windows.md
index 8d4afdc817..a10654c7ee 100644
--- a/doc/build-windows.md
+++ b/doc/build-windows.md
@@ -34,10 +34,9 @@ Full instructions to install WSL are available on the above link.
To install WSL on Windows 10 with Fall Creators Update installed (version >= 16215.0) do the following:
1. Enable the Windows Subsystem for Linux feature
- * From Start, search for "Turn Windows features on or off" (type 'turn')
- * Select Windows Subsystem for Linux
- * Click OK
- * Restart if necessary
+ * Open the Windows Features dialog (`OptionalFeatures.exe`)
+ * Enable 'Windows Susbsystem for Linux'
+ * Click 'OK' and restart if necessary
2. Install Ubuntu
* Open Microsoft Store and search for Ubuntu or use [this link](https://www.microsoft.com/store/productId/9NBLGGH4MSV6)
* Click Install
diff --git a/doc/release-notes.md b/doc/release-notes.md
index 0292eaa4d2..528cb81a38 100644
--- a/doc/release-notes.md
+++ b/doc/release-notes.md
@@ -56,6 +56,13 @@ frequently tested on them.
Notable changes
===============
+RPC changes
+------------
+
+### Low-level changes
+
+- The `fundrawtransaction` rpc will reject the previously deprecated `reserveChangeKey` option.
+
Credits
=======
diff --git a/src/init.cpp b/src/init.cpp
index b28baba779..895a5358f4 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -680,11 +680,13 @@ void ThreadImport(std::vector<fs::path> vImportFiles)
if (!ActivateBestChain(state, chainparams)) {
LogPrintf("Failed to connect best block");
StartShutdown();
+ return;
}
if (gArgs.GetBoolArg("-stopafterblockimport", DEFAULT_STOPAFTERBLOCKIMPORT)) {
LogPrintf("Stopping after block import\n");
StartShutdown();
+ return;
}
} // End scope of CImportingNow
if (gArgs.GetArg("-persistmempool", DEFAULT_PERSIST_MEMPOOL)) {
diff --git a/src/qt/addresstablemodel.cpp b/src/qt/addresstablemodel.cpp
index 74f0db3520..ffb5bff4de 100644
--- a/src/qt/addresstablemodel.cpp
+++ b/src/qt/addresstablemodel.cpp
@@ -393,11 +393,8 @@ QString AddressTableModel::addRow(const QString &type, const QString &label, con
}
// Add entry
- {
- LOCK(wallet->cs_wallet);
- wallet->SetAddressBook(DecodeDestination(strAddress), strLabel,
- (type == Send ? "send" : "receive"));
- }
+ wallet->SetAddressBook(DecodeDestination(strAddress), strLabel,
+ (type == Send ? "send" : "receive"));
return QString::fromStdString(strAddress);
}
@@ -411,10 +408,7 @@ bool AddressTableModel::removeRows(int row, int count, const QModelIndex &parent
// Also refuse to remove receiving addresses.
return false;
}
- {
- LOCK(wallet->cs_wallet);
- wallet->DelAddressBook(DecodeDestination(rec->address.toStdString()));
- }
+ wallet->DelAddressBook(DecodeDestination(rec->address.toStdString()));
return true;
}
diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp
index 541114e5fe..34954a6bfa 100644
--- a/src/qt/walletmodel.cpp
+++ b/src/qt/walletmodel.cpp
@@ -42,6 +42,7 @@ WalletModel::WalletModel(const PlatformStyle *platformStyle, CWallet *_wallet, O
transactionTableModel(0),
recentRequestsTableModel(0),
cachedBalance(0), cachedUnconfirmedBalance(0), cachedImmatureBalance(0),
+ cachedWatchOnlyBalance{0}, cachedWatchUnconfBalance{0}, cachedWatchImmatureBalance{0},
cachedEncryptionStatus(Unencrypted),
cachedNumBlocks(0)
{
diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp
index 189da6ae48..f1352a13cf 100644
--- a/src/rpc/blockchain.cpp
+++ b/src/rpc/blockchain.cpp
@@ -1434,7 +1434,7 @@ UniValue preciousblock(const JSONRPCRequest& request)
PreciousBlock(state, Params(), pblockindex);
if (!state.IsValid()) {
- throw JSONRPCError(RPC_DATABASE_ERROR, state.GetRejectReason());
+ throw JSONRPCError(RPC_DATABASE_ERROR, FormatStateMessage(state));
}
return NullUniValue;
@@ -1472,7 +1472,7 @@ UniValue invalidateblock(const JSONRPCRequest& request)
}
if (!state.IsValid()) {
- throw JSONRPCError(RPC_DATABASE_ERROR, state.GetRejectReason());
+ throw JSONRPCError(RPC_DATABASE_ERROR, FormatStateMessage(state));
}
return NullUniValue;
@@ -1509,7 +1509,7 @@ UniValue reconsiderblock(const JSONRPCRequest& request)
ActivateBestChain(state, Params());
if (!state.IsValid()) {
- throw JSONRPCError(RPC_DATABASE_ERROR, state.GetRejectReason());
+ throw JSONRPCError(RPC_DATABASE_ERROR, FormatStateMessage(state));
}
return NullUniValue;
@@ -1563,7 +1563,7 @@ UniValue getchaintxstats(const JSONRPCRequest& request)
pindex = chainActive.Tip();
}
}
-
+
assert(pindex != nullptr);
if (request.params[0].isNull()) {
diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp
index dd74095b62..3f3bfa0cfd 100644
--- a/src/rpc/mining.cpp
+++ b/src/rpc/mining.cpp
@@ -264,11 +264,11 @@ static UniValue BIP22ValidationResult(const CValidationState& state)
if (state.IsValid())
return NullUniValue;
- std::string strRejectReason = state.GetRejectReason();
if (state.IsError())
- throw JSONRPCError(RPC_VERIFY_ERROR, strRejectReason);
+ throw JSONRPCError(RPC_VERIFY_ERROR, FormatStateMessage(state));
if (state.IsInvalid())
{
+ std::string strRejectReason = state.GetRejectReason();
if (strRejectReason.empty())
return "rejected";
return strRejectReason;
diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp
index 24f2431efc..ef5f04e4ee 100644
--- a/src/rpc/rawtransaction.cpp
+++ b/src/rpc/rawtransaction.cpp
@@ -848,6 +848,8 @@ UniValue signrawtransaction(const JSONRPCRequest& request)
std::vector<unsigned char> rsData(ParseHexV(v, "redeemScript"));
CScript redeemScript(rsData.begin(), rsData.end());
tempKeystore.AddCScript(redeemScript);
+ // Automatically also add the P2WSH wrapped version of the script (to deal with P2SH-P2WSH).
+ tempKeystore.AddCScript(GetScriptForWitness(redeemScript));
}
}
}
@@ -981,12 +983,12 @@ UniValue sendrawtransaction(const JSONRPCRequest& request)
if (!AcceptToMemoryPool(mempool, state, std::move(tx), &fMissingInputs,
nullptr /* plTxnReplaced */, false /* bypass_limits */, nMaxRawTxFee)) {
if (state.IsInvalid()) {
- throw JSONRPCError(RPC_TRANSACTION_REJECTED, strprintf("%i: %s", state.GetRejectCode(), state.GetRejectReason()));
+ throw JSONRPCError(RPC_TRANSACTION_REJECTED, FormatStateMessage(state));
} else {
if (fMissingInputs) {
throw JSONRPCError(RPC_TRANSACTION_ERROR, "Missing inputs");
}
- throw JSONRPCError(RPC_TRANSACTION_ERROR, state.GetRejectReason());
+ throw JSONRPCError(RPC_TRANSACTION_ERROR, FormatStateMessage(state));
}
} else {
// If wallet is enabled, ensure that the wallet has been made aware
diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp
index 55d60d95e9..4b2da3e219 100644
--- a/src/test/util_tests.cpp
+++ b/src/test/util_tests.cpp
@@ -13,6 +13,10 @@
#include <stdint.h>
#include <vector>
+#ifndef WIN32
+#include <sys/types.h>
+#include <sys/wait.h>
+#endif
#include <boost/test/unit_test.hpp>
@@ -603,4 +607,130 @@ BOOST_AUTO_TEST_CASE(test_ParseFixedPoint)
BOOST_CHECK(!ParseFixedPoint("1.", 8, &amount));
}
+static void TestOtherThread(fs::path dirname, std::string lockname, bool *result)
+{
+ *result = LockDirectory(dirname, lockname);
+}
+
+#ifndef WIN32 // Cannot do this test on WIN32 due to lack of fork()
+static constexpr char LockCommand = 'L';
+static constexpr char UnlockCommand = 'U';
+static constexpr char ExitCommand = 'X';
+
+static void TestOtherProcess(fs::path dirname, std::string lockname, int fd)
+{
+ char ch;
+ int rv;
+ while (true) {
+ rv = read(fd, &ch, 1); // Wait for command
+ assert(rv == 1);
+ switch(ch) {
+ case LockCommand:
+ ch = LockDirectory(dirname, lockname);
+ rv = write(fd, &ch, 1);
+ assert(rv == 1);
+ break;
+ case UnlockCommand:
+ ReleaseDirectoryLocks();
+ ch = true; // Always succeeds
+ rv = write(fd, &ch, 1);
+ break;
+ case ExitCommand:
+ close(fd);
+ exit(0);
+ default:
+ assert(0);
+ }
+ }
+}
+#endif
+
+BOOST_AUTO_TEST_CASE(test_LockDirectory)
+{
+ fs::path dirname = fs::temp_directory_path() / fs::unique_path();
+ const std::string lockname = ".lock";
+#ifndef WIN32
+ // Revert SIGCHLD to default, otherwise boost.test will catch and fail on
+ // it: there is BOOST_TEST_IGNORE_SIGCHLD but that only works when defined
+ // at build-time of the boost library
+ void (*old_handler)(int) = signal(SIGCHLD, SIG_DFL);
+
+ // Fork another process for testing before creating the lock, so that we
+ // won't fork while holding the lock (which might be undefined, and is not
+ // relevant as test case as that is avoided with -daemonize).
+ int fd[2];
+ BOOST_CHECK_EQUAL(socketpair(AF_UNIX, SOCK_STREAM, 0, fd), 0);
+ pid_t pid = fork();
+ if (!pid) {
+ BOOST_CHECK_EQUAL(close(fd[1]), 0); // Child: close parent end
+ TestOtherProcess(dirname, lockname, fd[0]);
+ }
+ BOOST_CHECK_EQUAL(close(fd[0]), 0); // Parent: close child end
+#endif
+ // Lock on non-existent directory should fail
+ BOOST_CHECK_EQUAL(LockDirectory(dirname, lockname), false);
+
+ fs::create_directories(dirname);
+
+ // Probing lock on new directory should succeed
+ BOOST_CHECK_EQUAL(LockDirectory(dirname, lockname, true), true);
+
+ // Persistent lock on new directory should succeed
+ BOOST_CHECK_EQUAL(LockDirectory(dirname, lockname), true);
+
+ // Another lock on the directory from the same thread should succeed
+ BOOST_CHECK_EQUAL(LockDirectory(dirname, lockname), true);
+
+ // Another lock on the directory from a different thread within the same process should succeed
+ bool threadresult;
+ std::thread thr(TestOtherThread, dirname, lockname, &threadresult);
+ thr.join();
+ BOOST_CHECK_EQUAL(threadresult, true);
+#ifndef WIN32
+ // Try to aquire lock in child process while we're holding it, this should fail.
+ char ch;
+ BOOST_CHECK_EQUAL(write(fd[1], &LockCommand, 1), 1);
+ BOOST_CHECK_EQUAL(read(fd[1], &ch, 1), 1);
+ BOOST_CHECK_EQUAL((bool)ch, false);
+
+ // Give up our lock
+ ReleaseDirectoryLocks();
+ // Probing lock from our side now should succeed, but not hold on to the lock.
+ BOOST_CHECK_EQUAL(LockDirectory(dirname, lockname, true), true);
+
+ // Try to acquire the lock in the child process, this should be succesful.
+ BOOST_CHECK_EQUAL(write(fd[1], &LockCommand, 1), 1);
+ BOOST_CHECK_EQUAL(read(fd[1], &ch, 1), 1);
+ BOOST_CHECK_EQUAL((bool)ch, true);
+
+ // When we try to probe the lock now, it should fail.
+ BOOST_CHECK_EQUAL(LockDirectory(dirname, lockname, true), false);
+
+ // Unlock the lock in the child process
+ BOOST_CHECK_EQUAL(write(fd[1], &UnlockCommand, 1), 1);
+ BOOST_CHECK_EQUAL(read(fd[1], &ch, 1), 1);
+ BOOST_CHECK_EQUAL((bool)ch, true);
+
+ // When we try to probe the lock now, it should succeed.
+ BOOST_CHECK_EQUAL(LockDirectory(dirname, lockname, true), true);
+
+ // Re-lock the lock in the child process, then wait for it to exit, check
+ // successful return. After that, we check that exiting the process
+ // has released the lock as we would expect by probing it.
+ int processstatus;
+ BOOST_CHECK_EQUAL(write(fd[1], &LockCommand, 1), 1);
+ BOOST_CHECK_EQUAL(write(fd[1], &ExitCommand, 1), 1);
+ BOOST_CHECK_EQUAL(waitpid(pid, &processstatus, 0), pid);
+ BOOST_CHECK_EQUAL(processstatus, 0);
+ BOOST_CHECK_EQUAL(LockDirectory(dirname, lockname, true), true);
+
+ // Restore SIGCHLD
+ signal(SIGCHLD, old_handler);
+ BOOST_CHECK_EQUAL(close(fd[1]), 0); // Close our side of the socketpair
+#endif
+ // Clean up
+ ReleaseDirectoryLocks();
+ fs::remove_all(dirname);
+}
+
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/util.cpp b/src/util.cpp
index 6738bbc6e4..dcf7ed38b1 100644
--- a/src/util.cpp
+++ b/src/util.cpp
@@ -373,20 +373,37 @@ int LogPrintStr(const std::string &str)
return ret;
}
+/** A map that contains all the currently held directory locks. After
+ * successful locking, these will be held here until the global destructor
+ * cleans them up and thus automatically unlocks them, or ReleaseDirectoryLocks
+ * is called.
+ */
+static std::map<std::string, std::unique_ptr<boost::interprocess::file_lock>> dir_locks;
+/** Mutex to protect dir_locks. */
+static std::mutex cs_dir_locks;
+
bool LockDirectory(const fs::path& directory, const std::string lockfile_name, bool probe_only)
{
+ std::lock_guard<std::mutex> ulock(cs_dir_locks);
fs::path pathLockFile = directory / lockfile_name;
- FILE* file = fsbridge::fopen(pathLockFile, "a"); // empty lock file; created if it doesn't exist.
+
+ // If a lock for this directory already exists in the map, don't try to re-lock it
+ if (dir_locks.count(pathLockFile.string())) {
+ return true;
+ }
+
+ // Create empty lock file if it doesn't exist.
+ FILE* file = fsbridge::fopen(pathLockFile, "a");
if (file) fclose(file);
try {
- static std::map<std::string, boost::interprocess::file_lock> locks;
- boost::interprocess::file_lock& lock = locks.emplace(pathLockFile.string(), pathLockFile.string().c_str()).first->second;
- if (!lock.try_lock()) {
+ auto lock = MakeUnique<boost::interprocess::file_lock>(pathLockFile.string().c_str());
+ if (!lock->try_lock()) {
return false;
}
- if (probe_only) {
- lock.unlock();
+ if (!probe_only) {
+ // Lock successful and we're not just probing, put it into the map
+ dir_locks.emplace(pathLockFile.string(), std::move(lock));
}
} catch (const boost::interprocess::interprocess_exception& e) {
return error("Error while attempting to lock directory %s: %s", directory.string(), e.what());
@@ -394,6 +411,12 @@ bool LockDirectory(const fs::path& directory, const std::string lockfile_name, b
return true;
}
+void ReleaseDirectoryLocks()
+{
+ std::lock_guard<std::mutex> ulock(cs_dir_locks);
+ dir_locks.clear();
+}
+
/** Interpret string as boolean, for argument parsing */
static bool InterpretBool(const std::string& strValue)
{
diff --git a/src/util.h b/src/util.h
index 05138a9bfe..9490a5678f 100644
--- a/src/util.h
+++ b/src/util.h
@@ -174,6 +174,12 @@ int RaiseFileDescriptorLimit(int nMinFD);
void AllocateFileRange(FILE *file, unsigned int offset, unsigned int length);
bool RenameOver(fs::path src, fs::path dest);
bool LockDirectory(const fs::path& directory, const std::string lockfile_name, bool probe_only=false);
+
+/** Release all directory locks. This is used for unit testing only, at runtime
+ * the global destructor will take care of the locks.
+ */
+void ReleaseDirectoryLocks();
+
bool TryCreateDirectories(const fs::path& p);
fs::path GetDefaultDataDir();
const fs::path &GetDataDir(bool fNetSpecific = true);
diff --git a/src/validation.cpp b/src/validation.cpp
index 371460a6f0..e809f66e25 100644
--- a/src/validation.cpp
+++ b/src/validation.cpp
@@ -712,7 +712,7 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool
CAmount mempoolRejectFee = pool.GetMinFee(gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000).GetFee(nSize);
if (!bypass_limits && mempoolRejectFee > 0 && nModifiedFees < mempoolRejectFee) {
- return state.DoS(0, false, REJECT_INSUFFICIENTFEE, "mempool min fee not met", false, strprintf("%d < %d", nFees, mempoolRejectFee));
+ return state.DoS(0, false, REJECT_INSUFFICIENTFEE, "mempool min fee not met", false, strprintf("%d < %d", nModifiedFees, mempoolRejectFee));
}
// No transactions are allowed below minRelayTxFee except from disconnected blocks
@@ -2087,7 +2087,7 @@ bool static FlushStateToDisk(const CChainParams& chainparams, CValidationState &
nLastWrite = nNow;
}
// Flush best chain related state. This can only be done if the blocks / block index write was also done.
- if (fDoFullFlush) {
+ if (fDoFullFlush && !pcoinsTip->GetBestBlock().IsNull()) {
// Typical Coin structures on disk are around 48 bytes in size.
// Pushing a new one to the database can cause it to be written
// twice (once in the log, and once in the tables). This is already
diff --git a/src/wallet/feebumper.cpp b/src/wallet/feebumper.cpp
index 5234a69710..9cae660c60 100644
--- a/src/wallet/feebumper.cpp
+++ b/src/wallet/feebumper.cpp
@@ -274,7 +274,7 @@ Result CommitTransaction(CWallet* wallet, const uint256& txid, CMutableTransacti
CValidationState state;
if (!wallet->CommitTransaction(wtxBumped, reservekey, g_connman.get(), state)) {
// NOTE: CommitTransaction never returns false, so this should never happen.
- errors.push_back(strprintf("The transaction was rejected: %s", state.GetRejectReason()));
+ errors.push_back(strprintf("The transaction was rejected: %s", FormatStateMessage(state)));
return Result::WALLET_ERROR;
}
@@ -297,4 +297,3 @@ Result CommitTransaction(CWallet* wallet, const uint256& txid, CMutableTransacti
}
} // namespace feebumper
-
diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp
index b466cf1a81..8f378acd1a 100644
--- a/src/wallet/rpcwallet.cpp
+++ b/src/wallet/rpcwallet.cpp
@@ -435,7 +435,7 @@ static void SendMoney(CWallet * const pwallet, const CTxDestination &address, CA
}
CValidationState state;
if (!pwallet->CommitTransaction(wtxNew, reservekey, g_connman.get(), state)) {
- strError = strprintf("Error: The transaction was rejected! Reason given: %s", state.GetRejectReason());
+ strError = strprintf("Error: The transaction was rejected! Reason given: %s", FormatStateMessage(state));
throw JSONRPCError(RPC_WALLET_ERROR, strError);
}
}
@@ -1155,7 +1155,7 @@ UniValue sendmany(const JSONRPCRequest& request)
throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, strFailReason);
CValidationState state;
if (!pwallet->CommitTransaction(wtx, keyChange, g_connman.get(), state)) {
- strFailReason = strprintf("Transaction commit failed:: %s", state.GetRejectReason());
+ strFailReason = strprintf("Transaction commit failed:: %s", FormatStateMessage(state));
throw JSONRPCError(RPC_WALLET_ERROR, strFailReason);
}
@@ -3129,7 +3129,6 @@ UniValue fundrawtransaction(const JSONRPCRequest& request)
{"change_type", UniValueType(UniValue::VSTR)},
{"includeWatching", UniValueType(UniValue::VBOOL)},
{"lockUnspents", UniValueType(UniValue::VBOOL)},
- {"reserveChangeKey", UniValueType(UniValue::VBOOL)}, // DEPRECATED (and ignored), should be removed in 0.16 or so.
{"feeRate", UniValueType()}, // will be checked below
{"subtractFeeFromOutputs", UniValueType(UniValue::VARR)},
{"replaceable", UniValueType(UniValue::VBOOL)},
diff --git a/src/wallet/test/wallet_tests.cpp b/src/wallet/test/wallet_tests.cpp
index 161372784b..9db5d63922 100644
--- a/src/wallet/test/wallet_tests.cpp
+++ b/src/wallet/test/wallet_tests.cpp
@@ -451,6 +451,9 @@ BOOST_FIXTURE_TEST_CASE(rescan, TestChain100Setup)
// than or equal to key birthday.
BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup)
{
+ g_address_type = OUTPUT_TYPE_DEFAULT;
+ g_change_type = OUTPUT_TYPE_DEFAULT;
+
// Create two blocks with same timestamp to verify that importwallet rescan
// will pick up both blocks, not just the first.
const int64_t BLOCK_TIME = chainActive.Tip()->GetBlockTimeMax() + 5;
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
index 513819606b..408a01c50b 100644
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -3092,7 +3092,7 @@ bool CWallet::CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey, CCon
{
// Broadcast
if (!wtx.AcceptToMemoryPool(maxTxFee, state)) {
- LogPrintf("CommitTransaction(): Transaction cannot be broadcast immediately, %s\n", state.GetRejectReason());
+ LogPrintf("CommitTransaction(): Transaction cannot be broadcast immediately, %s\n", FormatStateMessage(state));
// TODO: if we expect the failure to be long term or permanent, instead delete wtx from the wallet and return failure.
} else {
wtx.RelayWalletTransaction(connman);
@@ -4179,11 +4179,6 @@ int CMerkleTx::GetBlocksToMaturity() const
bool CWalletTx::AcceptToMemoryPool(const CAmount& nAbsurdFee, CValidationState& state)
{
- // Quick check to avoid re-setting fInMempool to false
- if (mempool.exists(tx->GetHash())) {
- return false;
- }
-
// We must set fInMempool here - while it will be re-set to true by the
// entered-mempool callback, if we did not there would be a race where a
// user could call sendmoney in a loop and hit spurious out of funds errors
@@ -4191,7 +4186,7 @@ bool CWalletTx::AcceptToMemoryPool(const CAmount& nAbsurdFee, CValidationState&
// unavailable as we're not yet aware its in mempool.
bool ret = ::AcceptToMemoryPool(mempool, state, tx, nullptr /* pfMissingInputs */,
nullptr /* plTxnReplaced */, false /* bypass_limits */, nAbsurdFee);
- fInMempool = ret;
+ fInMempool |= ret;
return ret;
}
diff --git a/test/functional/feature_nulldummy.py b/test/functional/feature_nulldummy.py
index e4f413cc2a..740c498ce6 100755
--- a/test/functional/feature_nulldummy.py
+++ b/test/functional/feature_nulldummy.py
@@ -21,7 +21,7 @@ from test_framework.script import CScript
from io import BytesIO
import time
-NULLDUMMY_ERROR = "64: non-mandatory-script-verify-flag (Dummy CHECKMULTISIG argument must be zero)"
+NULLDUMMY_ERROR = "non-mandatory-script-verify-flag (Dummy CHECKMULTISIG argument must be zero) (code 64)"
def trueDummy(tx):
scriptSig = CScript(tx.vin[0].scriptSig)
diff --git a/test/functional/mempool_limit.py b/test/functional/mempool_limit.py
index e7ce3820d2..7e01663c96 100755
--- a/test/functional/mempool_limit.py
+++ b/test/functional/mempool_limit.py
@@ -50,5 +50,15 @@ class MempoolLimitTest(BitcoinTestFramework):
assert_equal(self.nodes[0].getmempoolinfo()['minrelaytxfee'], Decimal('0.00001000'))
assert_greater_than(self.nodes[0].getmempoolinfo()['mempoolminfee'], Decimal('0.00001000'))
+ self.log.info('Create a mempool tx that will not pass mempoolminfee')
+ us0 = utxos.pop()
+ inputs = [{ "txid" : us0["txid"], "vout" : us0["vout"]}]
+ outputs = {self.nodes[0].getnewaddress() : 0.0001}
+ tx = self.nodes[0].createrawtransaction(inputs, outputs)
+ # specifically fund this tx with a fee < mempoolminfee, >= than minrelaytxfee
+ txF = self.nodes[0].fundrawtransaction(tx, {'feeRate': relayfee})
+ txFS = self.nodes[0].signrawtransaction(txF['hex'])
+ assert_raises_rpc_error(-26, "mempool min fee not met, 166 < 411 (code 66)", self.nodes[0].sendrawtransaction, txFS['hex'])
+
if __name__ == '__main__':
MempoolLimitTest().main()
diff --git a/test/functional/mining_prioritisetransaction.py b/test/functional/mining_prioritisetransaction.py
index 57954ce321..8cea9c2783 100755
--- a/test/functional/mining_prioritisetransaction.py
+++ b/test/functional/mining_prioritisetransaction.py
@@ -120,7 +120,7 @@ class PrioritiseTransactionTest(BitcoinTestFramework):
tx_id = self.nodes[0].decoderawtransaction(tx_hex)["txid"]
# This will raise an exception due to min relay fee not being met
- assert_raises_rpc_error(-26, "66: min relay fee not met", self.nodes[0].sendrawtransaction, tx_hex)
+ assert_raises_rpc_error(-26, "min relay fee not met (code 66)", self.nodes[0].sendrawtransaction, tx_hex)
assert(tx_id not in self.nodes[0].getrawmempool())
# This is a less than 1000-byte transaction, so just set the fee
diff --git a/test/functional/rpc_fundrawtransaction.py b/test/functional/rpc_fundrawtransaction.py
index 6b9c9c15b7..4d3be18516 100755
--- a/test/functional/rpc_fundrawtransaction.py
+++ b/test/functional/rpc_fundrawtransaction.py
@@ -181,6 +181,9 @@ class RawTransactionsTest(BitcoinTestFramework):
assert_raises_rpc_error(-3, "Unexpected key foo", self.nodes[2].fundrawtransaction, rawtx, {'foo':'bar'})
+ # reserveChangeKey was deprecated and is now removed
+ assert_raises_rpc_error(-3, "Unexpected key reserveChangeKey", lambda: self.nodes[2].fundrawtransaction(hexstring=rawtx, options={'reserveChangeKey': True}))
+
############################################################
# test a fundrawtransaction with an invalid change address #
############################################################
diff --git a/test/functional/test_framework/util.py b/test/functional/test_framework/util.py
index 7fdc171332..8bf75ca1ae 100644
--- a/test/functional/test_framework/util.py
+++ b/test/functional/test_framework/util.py
@@ -292,6 +292,7 @@ def initialize_datadir(dirname, n):
f.write("port=" + str(p2p_port(n)) + "\n")
f.write("rpcport=" + str(rpc_port(n)) + "\n")
f.write("listenonion=0\n")
+ f.write("bind=127.0.0.1\n")
return datadir
def get_datadir_path(dirname, n):
diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py
index 0cf3424c71..945f645eac 100755
--- a/test/functional/test_runner.py
+++ b/test/functional/test_runner.py
@@ -342,7 +342,7 @@ def run_tests(test_list, src_dir, build_dir, exeext, tmpdir, jobs=1, enable_cove
print('\n============')
print('{}Combined log for {}:{}'.format(BOLD[1], testdir, BOLD[0]))
print('============\n')
- combined_logs, _ = subprocess.Popen([sys.executble, os.path.join(tests_dir, 'combine_logs.py'), '-c', testdir], universal_newlines=True, stdout=subprocess.PIPE).communicate()
+ combined_logs, _ = subprocess.Popen([sys.executable, os.path.join(tests_dir, 'combine_logs.py'), '-c', testdir], universal_newlines=True, stdout=subprocess.PIPE).communicate()
print("\n".join(deque(combined_logs.splitlines(), combined_logs_len)))
print_results(test_results, max_len_name, (int(time.time() - time0)))