aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--configure.ac4
-rw-r--r--doc/release-notes.md12
-rw-r--r--src/Makefile.test.include42
-rw-r--r--src/bench/bench_bitcoin.cpp2
-rw-r--r--src/init.cpp5
-rw-r--r--src/net_processing.cpp8
-rw-r--r--src/netaddress.cpp10
-rw-r--r--src/netaddress.h6
-rw-r--r--src/node/context.h2
-rw-r--r--src/qt/bitcoin.cpp2
-rw-r--r--src/qt/forms/debugwindow.ui63
-rw-r--r--src/qt/guiutil.cpp22
-rw-r--r--src/qt/guiutil.h15
-rw-r--r--src/qt/rpcconsole.cpp3
-rw-r--r--src/rpc/blockchain.cpp18
-rw-r--r--src/rpc/rawtransaction.cpp33
-rw-r--r--src/rpc/server.cpp50
-rw-r--r--src/rpc/server.h17
-rw-r--r--src/rpc/util.cpp44
-rw-r--r--src/rpc/util.h19
-rw-r--r--src/sync.cpp14
-rw-r--r--src/test/denialofservice_tests.cpp54
-rw-r--r--src/test/fuzz/crypto_aes256.cpp30
-rw-r--r--src/test/fuzz/crypto_aes256cbc.cpp34
-rw-r--r--src/test/fuzz/crypto_chacha20.cpp50
-rw-r--r--src/test/fuzz/crypto_chacha20_poly1305_aead.cpp72
-rw-r--r--src/test/fuzz/crypto_hkdf_hmac_sha256_l32.cpp25
-rw-r--r--src/test/fuzz/crypto_poly1305.cpp22
-rw-r--r--src/test/sync_tests.cpp2
-rw-r--r--src/test/util_tests.cpp10
-rw-r--r--src/util/check.h2
-rw-r--r--src/validation.cpp5
-rw-r--r--src/wallet/bdb.cpp5
-rw-r--r--src/wallet/bdb.h84
-rw-r--r--src/wallet/db.h80
-rw-r--r--src/wallet/walletdb.cpp38
-rw-r--r--src/wallet/walletdb.h12
-rwxr-xr-xtest/functional/p2p_leak.py18
-rwxr-xr-xtest/functional/wallet_encryption.py16
39 files changed, 658 insertions, 292 deletions
diff --git a/configure.ac b/configure.ac
index e1b09dae33..fb63b9fc68 100644
--- a/configure.ac
+++ b/configure.ac
@@ -392,11 +392,11 @@ if test "x$CXXFLAGS_overridden" = "xno"; then
AX_CHECK_COMPILE_FLAG([-Wall],[WARN_CXXFLAGS="$WARN_CXXFLAGS -Wall"],,[[$CXXFLAG_WERROR]])
AX_CHECK_COMPILE_FLAG([-Wextra],[WARN_CXXFLAGS="$WARN_CXXFLAGS -Wextra"],,[[$CXXFLAG_WERROR]])
AX_CHECK_COMPILE_FLAG([-Wgnu],[WARN_CXXFLAGS="$WARN_CXXFLAGS -Wgnu"],,[[$CXXFLAG_WERROR]])
- AX_CHECK_COMPILE_FLAG([-Wformat],[WARN_CXXFLAGS="$WARN_CXXFLAGS -Wformat"],,[[$CXXFLAG_WERROR]])
+ dnl some compilers will ignore -Wformat-security without -Wformat, so just combine the two here.
+ AX_CHECK_COMPILE_FLAG([-Wformat -Wformat-security],[WARN_CXXFLAGS="$WARN_CXXFLAGS -Wformat -Wformat-security"],,[[$CXXFLAG_WERROR]])
AX_CHECK_COMPILE_FLAG([-Wvla],[WARN_CXXFLAGS="$WARN_CXXFLAGS -Wvla"],,[[$CXXFLAG_WERROR]])
AX_CHECK_COMPILE_FLAG([-Wshadow-field],[WARN_CXXFLAGS="$WARN_CXXFLAGS -Wshadow-field"],,[[$CXXFLAG_WERROR]])
AX_CHECK_COMPILE_FLAG([-Wswitch],[WARN_CXXFLAGS="$WARN_CXXFLAGS -Wswitch"],,[[$CXXFLAG_WERROR]])
- AX_CHECK_COMPILE_FLAG([-Wformat-security],[WARN_CXXFLAGS="$WARN_CXXFLAGS -Wformat-security"],,[[$CXXFLAG_WERROR]])
AX_CHECK_COMPILE_FLAG([-Wthread-safety],[WARN_CXXFLAGS="$WARN_CXXFLAGS -Wthread-safety"],,[[$CXXFLAG_WERROR]])
AX_CHECK_COMPILE_FLAG([-Wrange-loop-analysis],[WARN_CXXFLAGS="$WARN_CXXFLAGS -Wrange-loop-analysis"],,[[$CXXFLAG_WERROR]])
AX_CHECK_COMPILE_FLAG([-Wredundant-decls],[WARN_CXXFLAGS="$WARN_CXXFLAGS -Wredundant-decls"],,[[$CXXFLAG_WERROR]])
diff --git a/doc/release-notes.md b/doc/release-notes.md
index 2a2e0f9472..23983dcd7b 100644
--- a/doc/release-notes.md
+++ b/doc/release-notes.md
@@ -98,7 +98,7 @@ will trigger BIP 125 (replace-by-fee) opt-in. (#11413)
- The `bumpfee` command now uses `conf_target` rather than `confTarget` in the
options. (#11413)
-- `getpeerinfo` no longer returns the `banscore` field unless the configuration
+- The `getpeerinfo` RPC no longer returns the `banscore` field unless the configuration
option `-deprecatedrpc=banscore` is used. The `banscore` field will be fully
removed in the next major release. (#19469)
@@ -123,8 +123,9 @@ Updated settings
- The `-banscore` configuration option, which modified the default threshold for
disconnecting and discouraging misbehaving peers, has been removed as part of
- changes in this release to the handling of misbehaving peers. Refer to the
- section, "Changes regarding misbehaving peers", for details. (#19464)
+ changes in 0.20.1 and in this release to the handling of misbehaving peers.
+ Refer to "Changes regarding misbehaving peers" in the 0.20.1 release notes for
+ details. (#19464)
- The `-debug=db` logging category, which was deprecated in 0.20 and replaced by
`-debug=walletdb` to distinguish it from `coindb`, has been removed. (#19202)
@@ -303,6 +304,11 @@ issue.
GUI changes
-----------
+- The GUI Peers window no longer displays a "Ban Score" field. This is part of
+ changes in 0.20.1 and in this release to the handling of misbehaving
+ peers. Refer to "Changes regarding misbehaving peers" in the 0.20.1 release
+ notes for details. (#19512)
+
Low-level changes
=================
diff --git a/src/Makefile.test.include b/src/Makefile.test.include
index 654d019d95..3b51503948 100644
--- a/src/Makefile.test.include
+++ b/src/Makefile.test.include
@@ -34,7 +34,13 @@ FUZZ_TARGETS = \
test/fuzz/coins_deserialize \
test/fuzz/coins_view \
test/fuzz/crypto \
+ test/fuzz/crypto_aes256 \
+ test/fuzz/crypto_aes256cbc \
+ test/fuzz/crypto_chacha20 \
+ test/fuzz/crypto_chacha20_poly1305_aead \
test/fuzz/crypto_common \
+ test/fuzz/crypto_hkdf_hmac_sha256_l32 \
+ test/fuzz/crypto_poly1305 \
test/fuzz/cuckoocache \
test/fuzz/decode_tx \
test/fuzz/descriptor_parse \
@@ -494,12 +500,48 @@ test_fuzz_crypto_LDADD = $(FUZZ_SUITE_LD_COMMON)
test_fuzz_crypto_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
test_fuzz_crypto_SOURCES = test/fuzz/crypto.cpp
+test_fuzz_crypto_aes256_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
+test_fuzz_crypto_aes256_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
+test_fuzz_crypto_aes256_LDADD = $(FUZZ_SUITE_LD_COMMON)
+test_fuzz_crypto_aes256_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_crypto_aes256_SOURCES = test/fuzz/crypto_aes256.cpp
+
+test_fuzz_crypto_aes256cbc_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
+test_fuzz_crypto_aes256cbc_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
+test_fuzz_crypto_aes256cbc_LDADD = $(FUZZ_SUITE_LD_COMMON)
+test_fuzz_crypto_aes256cbc_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_crypto_aes256cbc_SOURCES = test/fuzz/crypto_aes256cbc.cpp
+
+test_fuzz_crypto_chacha20_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
+test_fuzz_crypto_chacha20_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
+test_fuzz_crypto_chacha20_LDADD = $(FUZZ_SUITE_LD_COMMON)
+test_fuzz_crypto_chacha20_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_crypto_chacha20_SOURCES = test/fuzz/crypto_chacha20.cpp
+
+test_fuzz_crypto_chacha20_poly1305_aead_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
+test_fuzz_crypto_chacha20_poly1305_aead_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
+test_fuzz_crypto_chacha20_poly1305_aead_LDADD = $(FUZZ_SUITE_LD_COMMON)
+test_fuzz_crypto_chacha20_poly1305_aead_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_crypto_chacha20_poly1305_aead_SOURCES = test/fuzz/crypto_chacha20_poly1305_aead.cpp
+
test_fuzz_crypto_common_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_crypto_common_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_crypto_common_LDADD = $(FUZZ_SUITE_LD_COMMON)
test_fuzz_crypto_common_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
test_fuzz_crypto_common_SOURCES = test/fuzz/crypto_common.cpp
+test_fuzz_crypto_hkdf_hmac_sha256_l32_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
+test_fuzz_crypto_hkdf_hmac_sha256_l32_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
+test_fuzz_crypto_hkdf_hmac_sha256_l32_LDADD = $(FUZZ_SUITE_LD_COMMON)
+test_fuzz_crypto_hkdf_hmac_sha256_l32_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_crypto_hkdf_hmac_sha256_l32_SOURCES = test/fuzz/crypto_hkdf_hmac_sha256_l32.cpp
+
+test_fuzz_crypto_poly1305_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
+test_fuzz_crypto_poly1305_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
+test_fuzz_crypto_poly1305_LDADD = $(FUZZ_SUITE_LD_COMMON)
+test_fuzz_crypto_poly1305_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_crypto_poly1305_SOURCES = test/fuzz/crypto_poly1305.cpp
+
test_fuzz_cuckoocache_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_cuckoocache_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_cuckoocache_LDADD = $(FUZZ_SUITE_LD_COMMON)
diff --git a/src/bench/bench_bitcoin.cpp b/src/bench/bench_bitcoin.cpp
index 1b75854210..1f872ce700 100644
--- a/src/bench/bench_bitcoin.cpp
+++ b/src/bench/bench_bitcoin.cpp
@@ -4,6 +4,7 @@
#include <bench/bench.h>
+#include <crypto/sha256.h>
#include <util/strencodings.h>
#include <util/system.h>
@@ -35,6 +36,7 @@ int main(int argc, char** argv)
{
ArgsManager argsman;
SetupBenchArgs(argsman);
+ SHA256AutoDetect();
std::string error;
if (!argsman.ParseParameters(argc, argv, error)) {
tfm::format(std::cerr, "Error parsing command line arguments: %s\n", error);
diff --git a/src/init.cpp b/src/init.cpp
index 7b893a08d7..9864bad291 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -774,13 +774,14 @@ static bool InitSanityCheck()
return true;
}
-static bool AppInitServers(const util::Ref& context)
+static bool AppInitServers(const util::Ref& context, NodeContext& node)
{
RPCServer::OnStarted(&OnRPCStarted);
RPCServer::OnStopped(&OnRPCStopped);
if (!InitHTTPServer())
return false;
StartRPC();
+ node.rpc_interruption_point = RpcInterruptionPoint;
if (!StartHTTPRPC(context))
return false;
if (gArgs.GetBoolArg("-rest", DEFAULT_REST_ENABLE)) StartREST(context);
@@ -1351,7 +1352,7 @@ bool AppInitMain(const util::Ref& context, NodeContext& node)
if (gArgs.GetBoolArg("-server", false))
{
uiInterface.InitMessage_connect(SetRPCWarmupStatus);
- if (!AppInitServers(context))
+ if (!AppInitServers(context, node))
return InitError(_("Unable to start HTTP server. See debug log for details."));
}
diff --git a/src/net_processing.cpp b/src/net_processing.cpp
index 8a292eb585..7a58de35d7 100644
--- a/src/net_processing.cpp
+++ b/src/net_processing.cpp
@@ -1989,7 +1989,10 @@ void static ProcessOrphanTx(CConnman& connman, CTxMemPool& mempool, std::set<uin
if (MaybePunishNodeForTx(fromPeer, orphan_state)) {
setMisbehaving.insert(fromPeer);
}
- LogPrint(BCLog::MEMPOOL, " invalid orphan tx %s\n", orphanHash.ToString());
+ LogPrint(BCLog::MEMPOOL, " invalid orphan tx %s from peer=%d. %s\n",
+ orphanHash.ToString(),
+ fromPeer,
+ orphan_state.ToString());
}
// Has inputs but not accepted to mempool
// Probably non-standard or insufficient fee
@@ -2946,8 +2949,7 @@ void ProcessMessage(
// peer simply for relaying a tx that our recentRejects has caught,
// regardless of false positives.
- if (state.IsInvalid())
- {
+ if (state.IsInvalid()) {
LogPrint(BCLog::MEMPOOLREJ, "%s from peer=%d was not accepted: %s\n", tx.GetHash().ToString(),
pfrom.GetId(),
state.ToString());
diff --git a/src/netaddress.cpp b/src/netaddress.cpp
index 6744391616..0aaba440b8 100644
--- a/src/netaddress.cpp
+++ b/src/netaddress.cpp
@@ -726,12 +726,10 @@ bool CService::GetSockAddr(struct sockaddr* paddr, socklen_t *addrlen) const
*/
std::vector<unsigned char> CService::GetKey() const
{
- std::vector<unsigned char> vKey;
- vKey.resize(18);
- memcpy(vKey.data(), ip, 16);
- vKey[16] = port / 0x100; // most significant byte of our port
- vKey[17] = port & 0x0FF; // least significant byte of our port
- return vKey;
+ auto key = GetAddrBytes();
+ key.push_back(port / 0x100); // most significant byte of our port
+ key.push_back(port & 0x0FF); // least significant byte of our port
+ return key;
}
std::string CService::ToStringPort() const
diff --git a/src/netaddress.h b/src/netaddress.h
index c201012154..f2daad7fb6 100644
--- a/src/netaddress.h
+++ b/src/netaddress.h
@@ -160,7 +160,11 @@ class CService : public CNetAddr
CService(const struct in6_addr& ipv6Addr, uint16_t port);
explicit CService(const struct sockaddr_in6& addr);
- SERIALIZE_METHODS(CService, obj) { READWRITE(obj.ip, Using<BigEndianFormatter<2>>(obj.port)); }
+ SERIALIZE_METHODS(CService, obj)
+ {
+ READWRITEAS(CNetAddr, obj);
+ READWRITE(Using<BigEndianFormatter<2>>(obj.port));
+ }
};
bool SanityCheckASMap(const std::vector<bool>& asmap);
diff --git a/src/node/context.h b/src/node/context.h
index c783c39cd6..be568cba36 100644
--- a/src/node/context.h
+++ b/src/node/context.h
@@ -6,6 +6,7 @@
#define BITCOIN_NODE_CONTEXT_H
#include <cassert>
+#include <functional>
#include <memory>
#include <vector>
@@ -41,6 +42,7 @@ struct NodeContext {
std::unique_ptr<interfaces::Chain> chain;
std::vector<std::unique_ptr<interfaces::ChainClient>> chain_clients;
std::unique_ptr<CScheduler> scheduler;
+ std::function<void()> rpc_interruption_point = [] {};
//! Declare default constructor and destructor that are not inline, so code
//! instantiating the NodeContext struct doesn't need to #include class
diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp
index c7dd16d2ed..a304c23a2c 100644
--- a/src/qt/bitcoin.cpp
+++ b/src/qt/bitcoin.cpp
@@ -556,6 +556,8 @@ int GuiMain(int argc, char* argv[])
/// 9. Main GUI initialization
// Install global event filter that makes sure that long tooltips can be word-wrapped
app.installEventFilter(new GUIUtil::ToolTipToRichTextFilter(TOOLTIP_WRAP_THRESHOLD, &app));
+ // Install global event filter that makes sure that out-of-focus labels do not contain text cursor.
+ app.installEventFilter(new GUIUtil::LabelOutOfFocusEventFilter(&app));
#if defined(Q_OS_WIN)
// Install global event filter for processing Windows session related Windows messages (WM_QUERYENDSESSION and WM_ENDSESSION)
qApp->installNativeEventFilter(new WinShutdownMonitor());
diff --git a/src/qt/forms/debugwindow.ui b/src/qt/forms/debugwindow.ui
index 1217ca3e2e..4747dd6d26 100644
--- a/src/qt/forms/debugwindow.ui
+++ b/src/qt/forms/debugwindow.ui
@@ -1264,36 +1264,13 @@
</widget>
</item>
<item row="8" column="0">
- <widget class="QLabel" name="label_24">
- <property name="text">
- <string>Ban Score</string>
- </property>
- </widget>
- </item>
- <item row="8" column="1">
- <widget class="QLabel" name="peerBanScore">
- <property name="cursor">
- <cursorShape>IBeamCursor</cursorShape>
- </property>
- <property name="text">
- <string>N/A</string>
- </property>
- <property name="textFormat">
- <enum>Qt::PlainText</enum>
- </property>
- <property name="textInteractionFlags">
- <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
- </property>
- </widget>
- </item>
- <item row="9" column="0">
<widget class="QLabel" name="label_22">
<property name="text">
<string>Connection Time</string>
</property>
</widget>
</item>
- <item row="9" column="1">
+ <item row="8" column="1">
<widget class="QLabel" name="peerConnTime">
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
@@ -1309,14 +1286,14 @@
</property>
</widget>
</item>
- <item row="10" column="0">
+ <item row="9" column="0">
<widget class="QLabel" name="label_15">
<property name="text">
<string>Last Send</string>
</property>
</widget>
</item>
- <item row="10" column="1">
+ <item row="9" column="1">
<widget class="QLabel" name="peerLastSend">
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
@@ -1332,14 +1309,14 @@
</property>
</widget>
</item>
- <item row="11" column="0">
+ <item row="10" column="0">
<widget class="QLabel" name="label_19">
<property name="text">
<string>Last Receive</string>
</property>
</widget>
</item>
- <item row="11" column="1">
+ <item row="10" column="1">
<widget class="QLabel" name="peerLastRecv">
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
@@ -1355,14 +1332,14 @@
</property>
</widget>
</item>
- <item row="12" column="0">
+ <item row="11" column="0">
<widget class="QLabel" name="label_18">
<property name="text">
<string>Sent</string>
</property>
</widget>
</item>
- <item row="12" column="1">
+ <item row="11" column="1">
<widget class="QLabel" name="peerBytesSent">
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
@@ -1378,14 +1355,14 @@
</property>
</widget>
</item>
- <item row="13" column="0">
+ <item row="12" column="0">
<widget class="QLabel" name="label_20">
<property name="text">
<string>Received</string>
</property>
</widget>
</item>
- <item row="13" column="1">
+ <item row="12" column="1">
<widget class="QLabel" name="peerBytesRecv">
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
@@ -1401,14 +1378,14 @@
</property>
</widget>
</item>
- <item row="14" column="0">
+ <item row="13" column="0">
<widget class="QLabel" name="label_26">
<property name="text">
<string>Ping Time</string>
</property>
</widget>
</item>
- <item row="14" column="1">
+ <item row="13" column="1">
<widget class="QLabel" name="peerPingTime">
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
@@ -1424,7 +1401,7 @@
</property>
</widget>
</item>
- <item row="15" column="0">
+ <item row="14" column="0">
<widget class="QLabel" name="peerPingWaitLabel">
<property name="toolTip">
<string>The duration of a currently outstanding ping.</string>
@@ -1434,7 +1411,7 @@
</property>
</widget>
</item>
- <item row="15" column="1">
+ <item row="14" column="1">
<widget class="QLabel" name="peerPingWait">
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
@@ -1450,14 +1427,14 @@
</property>
</widget>
</item>
- <item row="16" column="0">
+ <item row="15" column="0">
<widget class="QLabel" name="peerMinPingLabel">
<property name="text">
<string>Min Ping</string>
</property>
</widget>
</item>
- <item row="16" column="1">
+ <item row="15" column="1">
<widget class="QLabel" name="peerMinPing">
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
@@ -1473,14 +1450,14 @@
</property>
</widget>
</item>
- <item row="17" column="0">
+ <item row="16" column="0">
<widget class="QLabel" name="label_timeoffset">
<property name="text">
<string>Time Offset</string>
</property>
</widget>
</item>
- <item row="17" column="1">
+ <item row="16" column="1">
<widget class="QLabel" name="timeoffset">
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
@@ -1496,7 +1473,7 @@
</property>
</widget>
</item>
- <item row="18" column="0">
+ <item row="17" column="0">
<widget class="QLabel" name="peerMappedASLabel">
<property name="toolTip">
<string>The mapped Autonomous System used for diversifying peer selection.</string>
@@ -1506,7 +1483,7 @@
</property>
</widget>
</item>
- <item row="18" column="1">
+ <item row="17" column="1">
<widget class="QLabel" name="peerMappedAS">
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
@@ -1522,7 +1499,7 @@
</property>
</widget>
</item>
- <item row="19" column="0">
+ <item row="18" column="0">
<spacer name="verticalSpacer_3">
<property name="orientation">
<enum>Qt::Vertical</enum>
diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp
index 3cadac2f2f..7f439fa45e 100644
--- a/src/qt/guiutil.cpp
+++ b/src/qt/guiutil.cpp
@@ -450,6 +450,28 @@ bool ToolTipToRichTextFilter::eventFilter(QObject *obj, QEvent *evt)
return QObject::eventFilter(obj, evt);
}
+LabelOutOfFocusEventFilter::LabelOutOfFocusEventFilter(QObject* parent)
+ : QObject(parent)
+{
+}
+
+bool LabelOutOfFocusEventFilter::eventFilter(QObject* watched, QEvent* event)
+{
+ if (event->type() == QEvent::FocusOut) {
+ auto focus_out = static_cast<QFocusEvent*>(event);
+ if (focus_out->reason() != Qt::PopupFocusReason) {
+ auto label = qobject_cast<QLabel*>(watched);
+ if (label) {
+ auto flags = label->textInteractionFlags();
+ label->setTextInteractionFlags(Qt::NoTextInteraction);
+ label->setTextInteractionFlags(flags);
+ }
+ }
+ }
+
+ return QObject::eventFilter(watched, event);
+}
+
void TableViewLastColumnResizingFixer::connectViewHeadersSignals()
{
connect(tableView->horizontalHeader(), &QHeaderView::sectionResized, this, &TableViewLastColumnResizingFixer::on_sectionResized);
diff --git a/src/qt/guiutil.h b/src/qt/guiutil.h
index 8741d90102..2bd94b5eb3 100644
--- a/src/qt/guiutil.h
+++ b/src/qt/guiutil.h
@@ -162,6 +162,21 @@ namespace GUIUtil
};
/**
+ * Qt event filter that intercepts QEvent::FocusOut events for QLabel objects, and
+ * resets their `textInteractionFlags' property to get rid of the visible cursor.
+ *
+ * This is a temporary fix of QTBUG-59514.
+ */
+ class LabelOutOfFocusEventFilter : public QObject
+ {
+ Q_OBJECT
+
+ public:
+ explicit LabelOutOfFocusEventFilter(QObject* parent);
+ bool eventFilter(QObject* watched, QEvent* event) override;
+ };
+
+ /**
* Makes a QTableView last column feel as if it was being resized from its left border.
* Also makes sure the column widths are never larger than the table's viewport.
* In Qt, all columns are resizable from the right, but it's not intuitive resizing the last column from the right.
diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp
index 71094f7112..694e1c2eae 100644
--- a/src/qt/rpcconsole.cpp
+++ b/src/qt/rpcconsole.cpp
@@ -1126,9 +1126,6 @@ void RPCConsole::updateNodeDetail(const CNodeCombinedStats *stats)
// This check fails for example if the lock was busy and
// nodeStateStats couldn't be fetched.
if (stats->fNodeStateStatsAvailable) {
- // Ban score is init to 0
- ui->peerBanScore->setText(QString("%1").arg(stats->nodeStateStats.nMisbehavior));
-
// Sync height is init to -1
if (stats->nodeStateStats.nSyncHeight > -1)
ui->peerSyncHeight->setText(QString("%1").arg(stats->nodeStateStats.nSyncHeight));
diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp
index c6c78a983a..2afc9a3d4a 100644
--- a/src/rpc/blockchain.cpp
+++ b/src/rpc/blockchain.cpp
@@ -1002,7 +1002,8 @@ static UniValue gettxoutsetinfo(const JSONRPCRequest& request)
const CoinStatsHashType hash_type = ParseHashType(request.params[0], CoinStatsHashType::HASH_SERIALIZED);
CCoinsView* coins_view = WITH_LOCK(cs_main, return &ChainstateActive().CoinsDB());
- if (GetUTXOStats(coins_view, stats, hash_type, RpcInterruptionPoint)) {
+ NodeContext& node = EnsureNodeContext(request.context);
+ if (GetUTXOStats(coins_view, stats, hash_type, node.rpc_interruption_point)) {
ret.pushKV("height", (int64_t)stats.nHeight);
ret.pushKV("bestblock", stats.hashBlock.GetHex());
ret.pushKV("transactions", (int64_t)stats.nTransactions);
@@ -1972,8 +1973,10 @@ static UniValue savemempool(const JSONRPCRequest& request)
return NullUniValue;
}
+namespace {
//! Search for a given set of pubkey scripts
-bool FindScriptPubKey(std::atomic<int>& scan_progress, const std::atomic<bool>& should_abort, int64_t& count, CCoinsViewCursor* cursor, const std::set<CScript>& needles, std::map<COutPoint, Coin>& out_results) {
+bool FindScriptPubKey(std::atomic<int>& scan_progress, const std::atomic<bool>& should_abort, int64_t& count, CCoinsViewCursor* cursor, const std::set<CScript>& needles, std::map<COutPoint, Coin>& out_results, std::function<void()>& interruption_point)
+{
scan_progress = 0;
count = 0;
while (cursor->Valid()) {
@@ -1981,7 +1984,7 @@ bool FindScriptPubKey(std::atomic<int>& scan_progress, const std::atomic<bool>&
Coin coin;
if (!cursor->GetKey(key) || !cursor->GetValue(coin)) return false;
if (++count % 8192 == 0) {
- RpcInterruptionPoint();
+ interruption_point();
if (should_abort) {
// allow to abort the scan via the abort reference
return false;
@@ -2000,6 +2003,7 @@ bool FindScriptPubKey(std::atomic<int>& scan_progress, const std::atomic<bool>&
scan_progress = 100;
return true;
}
+} // namespace
/** RAII object to prevent concurrency issue when scanning the txout set */
static std::atomic<int> g_scan_progress;
@@ -2148,7 +2152,8 @@ UniValue scantxoutset(const JSONRPCRequest& request)
tip = ::ChainActive().Tip();
CHECK_NONFATAL(tip);
}
- bool res = FindScriptPubKey(g_scan_progress, g_should_abort_scan, count, pcursor.get(), needles, coins);
+ NodeContext& node = EnsureNodeContext(request.context);
+ bool res = FindScriptPubKey(g_scan_progress, g_should_abort_scan, count, pcursor.get(), needles, coins, node.rpc_interruption_point);
result.pushKV("success", res);
result.pushKV("txouts", count);
result.pushKV("height", tip->nHeight);
@@ -2303,6 +2308,7 @@ UniValue dumptxoutset(const JSONRPCRequest& request)
std::unique_ptr<CCoinsViewCursor> pcursor;
CCoinsStats stats;
CBlockIndex* tip;
+ NodeContext& node = EnsureNodeContext(request.context);
{
// We need to lock cs_main to ensure that the coinsdb isn't written to
@@ -2321,7 +2327,7 @@ UniValue dumptxoutset(const JSONRPCRequest& request)
::ChainstateActive().ForceFlushStateToDisk();
- if (!GetUTXOStats(&::ChainstateActive().CoinsDB(), stats, CoinStatsHashType::NONE, RpcInterruptionPoint)) {
+ if (!GetUTXOStats(&::ChainstateActive().CoinsDB(), stats, CoinStatsHashType::NONE, node.rpc_interruption_point)) {
throw JSONRPCError(RPC_INTERNAL_ERROR, "Unable to read UTXO set");
}
@@ -2339,7 +2345,7 @@ UniValue dumptxoutset(const JSONRPCRequest& request)
unsigned int iter{0};
while (pcursor->Valid()) {
- if (iter % 5000 == 0) RpcInterruptionPoint();
+ if (iter % 5000 == 0) node.rpc_interruption_point();
++iter;
if (pcursor->GetKey(key) && pcursor->GetValue(coin)) {
afile << key;
diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp
index 5f8c02df65..7b1da6fdcd 100644
--- a/src/rpc/rawtransaction.cpp
+++ b/src/rpc/rawtransaction.cpp
@@ -1105,39 +1105,38 @@ UniValue decodepsbt(const JSONRPCRequest& request)
UniValue in(UniValue::VOBJ);
// UTXOs
bool have_a_utxo = false;
+ CTxOut txout;
if (!input.witness_utxo.IsNull()) {
- const CTxOut& txout = input.witness_utxo;
-
- UniValue out(UniValue::VOBJ);
-
- out.pushKV("amount", ValueFromAmount(txout.nValue));
- if (MoneyRange(txout.nValue) && MoneyRange(total_in + txout.nValue)) {
- total_in += txout.nValue;
- } else {
- // Hack to just not show fee later
- have_all_utxos = false;
- }
+ txout = input.witness_utxo;
UniValue o(UniValue::VOBJ);
ScriptToUniv(txout.scriptPubKey, o, true);
+
+ UniValue out(UniValue::VOBJ);
+ out.pushKV("amount", ValueFromAmount(txout.nValue));
out.pushKV("scriptPubKey", o);
+
in.pushKV("witness_utxo", out);
+
have_a_utxo = true;
}
if (input.non_witness_utxo) {
+ txout = input.non_witness_utxo->vout[psbtx.tx->vin[i].prevout.n];
+
UniValue non_wit(UniValue::VOBJ);
TxToUniv(*input.non_witness_utxo, uint256(), non_wit, false);
in.pushKV("non_witness_utxo", non_wit);
- CAmount utxo_val = input.non_witness_utxo->vout[psbtx.tx->vin[i].prevout.n].nValue;
- if (MoneyRange(utxo_val) && MoneyRange(total_in + utxo_val)) {
- total_in += utxo_val;
+
+ have_a_utxo = true;
+ }
+ if (have_a_utxo) {
+ if (MoneyRange(txout.nValue) && MoneyRange(total_in + txout.nValue)) {
+ total_in += txout.nValue;
} else {
// Hack to just not show fee later
have_all_utxos = false;
}
- have_a_utxo = true;
- }
- if (!have_a_utxo) {
+ } else {
have_all_utxos = false;
}
diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp
index de8791a935..e5f6b1b9f1 100644
--- a/src/rpc/server.cpp
+++ b/src/rpc/server.cpp
@@ -130,11 +130,9 @@ std::string CRPCTable::help(const std::string& strCommand, const JSONRPCRequest&
return strRet;
}
-UniValue help(const JSONRPCRequest& jsonRequest)
+static RPCHelpMan help()
{
- if (jsonRequest.fHelp || jsonRequest.params.size() > 1)
- throw std::runtime_error(
- RPCHelpMan{"help",
+ return RPCHelpMan{"help",
"\nList all commands, or get help for a specified command.\n",
{
{"command", RPCArg::Type::STR, /* default */ "all commands", "The command to get help on"},
@@ -143,32 +141,32 @@ UniValue help(const JSONRPCRequest& jsonRequest)
RPCResult::Type::STR, "", "The help text"
},
RPCExamples{""},
- }.ToString()
- );
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& jsonRequest) -> UniValue
+{
std::string strCommand;
if (jsonRequest.params.size() > 0)
strCommand = jsonRequest.params[0].get_str();
return tableRPC.help(strCommand, jsonRequest);
+},
+ };
}
-
-UniValue stop(const JSONRPCRequest& jsonRequest)
+static RPCHelpMan stop()
{
static const std::string RESULT{PACKAGE_NAME " stopping"};
- // Accept the deprecated and ignored 'detach' boolean argument
+ return RPCHelpMan{"stop",
// Also accept the hidden 'wait' integer argument (milliseconds)
// For instance, 'stop 1000' makes the call wait 1 second before returning
// to the client (intended for testing)
- if (jsonRequest.fHelp || jsonRequest.params.size() > 1)
- throw std::runtime_error(
- RPCHelpMan{"stop",
"\nRequest a graceful shutdown of " PACKAGE_NAME ".",
- {},
+ {
+ {"wait", RPCArg::Type::NUM, RPCArg::Optional::OMITTED_NAMED_ARG, "how long to wait in ms", "", {}, /* hidden */ true},
+ },
RPCResult{RPCResult::Type::STR, "", "A string with the content '" + RESULT + "'"},
RPCExamples{""},
- }.ToString());
+ [&](const RPCHelpMan& self, const JSONRPCRequest& jsonRequest) -> UniValue
+{
// Event loop will exit after current HTTP requests have been handled, so
// this reply will get back to the client.
StartShutdown();
@@ -176,11 +174,13 @@ UniValue stop(const JSONRPCRequest& jsonRequest)
UninterruptibleSleep(std::chrono::milliseconds{jsonRequest.params[0].get_int()});
}
return RESULT;
+},
+ };
}
-static UniValue uptime(const JSONRPCRequest& jsonRequest)
+static RPCHelpMan uptime()
{
- RPCHelpMan{"uptime",
+ return RPCHelpMan{"uptime",
"\nReturns the total uptime of the server.\n",
{},
RPCResult{
@@ -190,14 +190,16 @@ static UniValue uptime(const JSONRPCRequest& jsonRequest)
HelpExampleCli("uptime", "")
+ HelpExampleRpc("uptime", "")
},
- }.Check(jsonRequest);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
return GetTime() - GetStartupTime();
}
+ };
+}
-static UniValue getrpcinfo(const JSONRPCRequest& request)
+static RPCHelpMan getrpcinfo()
{
- RPCHelpMan{"getrpcinfo",
+ return RPCHelpMan{"getrpcinfo",
"\nReturns details of the RPC server.\n",
{},
RPCResult{
@@ -217,8 +219,8 @@ static UniValue getrpcinfo(const JSONRPCRequest& request)
RPCExamples{
HelpExampleCli("getrpcinfo", "")
+ HelpExampleRpc("getrpcinfo", "")},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
LOCK(g_rpc_server_info.mutex);
UniValue active_commands(UniValue::VARR);
for (const RPCCommandExecutionInfo& info : g_rpc_server_info.active_commands) {
@@ -237,6 +239,8 @@ static UniValue getrpcinfo(const JSONRPCRequest& request)
return result;
}
+ };
+}
// clang-format off
static const CRPCCommand vRPCCommands[] =
diff --git a/src/rpc/server.h b/src/rpc/server.h
index d7a04ff6e8..6da3e94ea2 100644
--- a/src/rpc/server.h
+++ b/src/rpc/server.h
@@ -8,6 +8,7 @@
#include <amount.h>
#include <rpc/request.h>
+#include <rpc/util.h>
#include <functional>
#include <map>
@@ -85,6 +86,7 @@ void RPCUnsetTimerInterface(RPCTimerInterface *iface);
void RPCRunLater(const std::string& name, std::function<void()> func, int64_t nSeconds);
typedef UniValue(*rpcfn_type)(const JSONRPCRequest& jsonRequest);
+typedef RPCHelpMan (*RpcMethodFnType)();
class CRPCCommand
{
@@ -101,6 +103,19 @@ public:
{
}
+ //! Simplified constructor taking plain RpcMethodFnType function pointer.
+ CRPCCommand(std::string category, std::string name_in, RpcMethodFnType fn, std::vector<std::string> args_in)
+ : CRPCCommand(
+ category,
+ fn().m_name,
+ [fn](const JSONRPCRequest& request, UniValue& result, bool) { result = fn().HandleRequest(request); return true; },
+ fn().GetArgNames(),
+ intptr_t(fn))
+ {
+ CHECK_NONFATAL(fn().m_name == name_in);
+ CHECK_NONFATAL(fn().GetArgNames() == args_in);
+ }
+
//! Simplified constructor taking plain rpcfn_type function pointer.
CRPCCommand(const char* category, const char* name, rpcfn_type fn, std::initializer_list<const char*> args)
: CRPCCommand(category, name,
@@ -117,7 +132,7 @@ public:
};
/**
- * Bitcoin RPC command dispatcher.
+ * RPC command dispatcher.
*/
class CRPCTable
{
diff --git a/src/rpc/util.cpp b/src/rpc/util.cpp
index ca73c699c9..9f4c7bee9c 100644
--- a/src/rpc/util.cpp
+++ b/src/rpc/util.cpp
@@ -385,9 +385,7 @@ struct Sections {
PushSection({indent + "]" + (outer_type != OuterType::NONE ? "," : ""), ""});
break;
}
-
- // no default case, so the compiler can warn about missing cases
- }
+ } // no default case, so the compiler can warn about missing cases
}
/**
@@ -398,6 +396,9 @@ struct Sections {
std::string ret;
const size_t pad = m_max_pad + 4;
for (const auto& s : m_sections) {
+ // The left part of a section is assumed to be a single line, usually it is the name of the JSON struct or a
+ // brace like {, }, [, or ]
+ CHECK_NONFATAL(s.m_left.find('\n') == std::string::npos);
if (s.m_right.empty()) {
ret += s.m_left;
ret += "\n";
@@ -432,7 +433,11 @@ struct Sections {
};
RPCHelpMan::RPCHelpMan(std::string name, std::string description, std::vector<RPCArg> args, RPCResults results, RPCExamples examples)
+ : RPCHelpMan{std::move(name), std::move(description), std::move(args), std::move(results), std::move(examples), nullptr} {}
+
+RPCHelpMan::RPCHelpMan(std::string name, std::string description, std::vector<RPCArg> args, RPCResults results, RPCExamples examples, RPCMethodImpl fun)
: m_name{std::move(name)},
+ m_fun{std::move(fun)},
m_description{std::move(description)},
m_args{std::move(args)},
m_results{std::move(results)},
@@ -481,6 +486,16 @@ bool RPCHelpMan::IsValidNumArgs(size_t num_args) const
}
return num_required_args <= num_args && num_args <= m_args.size();
}
+
+std::vector<std::string> RPCHelpMan::GetArgNames() const
+{
+ std::vector<std::string> ret;
+ for (const auto& arg : m_args) {
+ ret.emplace_back(arg.m_names);
+ }
+ return ret;
+}
+
std::string RPCHelpMan::ToString() const
{
std::string ret;
@@ -489,6 +504,7 @@ std::string RPCHelpMan::ToString() const
ret += m_name;
bool was_optional{false};
for (const auto& arg : m_args) {
+ if (arg.m_hidden) continue;
const bool optional = arg.IsOptional();
ret += " ";
if (optional) {
@@ -510,6 +526,7 @@ std::string RPCHelpMan::ToString() const
Sections sections;
for (size_t i{0}; i < m_args.size(); ++i) {
const auto& arg = m_args.at(i);
+ if (arg.m_hidden) continue;
if (i == 0) ret += "\nArguments:\n";
@@ -589,9 +606,7 @@ std::string RPCArg::ToDescriptionString() const
ret += "json array";
break;
}
-
- // no default case, so the compiler can warn about missing cases
- }
+ } // no default case, so the compiler can warn about missing cases
}
if (m_fallback.which() == 1) {
ret += ", optional, default=" + boost::get<std::string>(m_fallback);
@@ -609,9 +624,7 @@ std::string RPCArg::ToDescriptionString() const
ret += ", required";
break;
}
-
- // no default case, so the compiler can warn about missing cases
- }
+ } // no default case, so the compiler can warn about missing cases
}
ret += ")";
ret += m_description.empty() ? "" : " " + m_description;
@@ -706,10 +719,7 @@ void RPCResult::ToSections(Sections& sections, const OuterType outer_type, const
sections.PushSection({indent + "}" + maybe_separator, ""});
return;
}
-
- // no default case, so the compiler can warn about missing cases
- }
-
+ } // no default case, so the compiler can warn about missing cases
CHECK_NONFATAL(false);
}
@@ -746,9 +756,7 @@ std::string RPCArg::ToStringObj(const bool oneline) const
case Type::OBJ_USER_KEYS:
// Currently unused, so avoid writing dead code
CHECK_NONFATAL(false);
-
- // no default case, so the compiler can warn about missing cases
- }
+ } // no default case, so the compiler can warn about missing cases
CHECK_NONFATAL(false);
}
@@ -783,9 +791,7 @@ std::string RPCArg::ToString(const bool oneline) const
}
return "[" + res + "...]";
}
-
- // no default case, so the compiler can warn about missing cases
- }
+ } // no default case, so the compiler can warn about missing cases
CHECK_NONFATAL(false);
}
diff --git a/src/rpc/util.h b/src/rpc/util.h
index 96dd1ea74a..45b0bb0c7e 100644
--- a/src/rpc/util.h
+++ b/src/rpc/util.h
@@ -147,6 +147,7 @@ struct RPCArg {
using Fallback = boost::variant<Optional, /* default value for optional args */ std::string>;
const std::string m_names; //!< The name of the arg (can be empty for inner args, can contain multiple aliases separated by | for named request arguments)
const Type m_type;
+ const bool m_hidden;
const std::vector<RPCArg> m_inner; //!< Only used for arrays or dicts
const Fallback m_fallback;
const std::string m_description;
@@ -159,9 +160,11 @@ struct RPCArg {
const Fallback fallback,
const std::string description,
const std::string oneline_description = "",
- const std::vector<std::string> type_str = {})
+ const std::vector<std::string> type_str = {},
+ const bool hidden = false)
: m_names{std::move(name)},
m_type{std::move(type)},
+ m_hidden{hidden},
m_fallback{std::move(fallback)},
m_description{std::move(description)},
m_oneline_description{std::move(oneline_description)},
@@ -180,6 +183,7 @@ struct RPCArg {
const std::vector<std::string> type_str = {})
: m_names{std::move(name)},
m_type{std::move(type)},
+ m_hidden{false},
m_inner{std::move(inner)},
m_fallback{std::move(fallback)},
m_description{std::move(description)},
@@ -329,8 +333,15 @@ class RPCHelpMan
{
public:
RPCHelpMan(std::string name, std::string description, std::vector<RPCArg> args, RPCResults results, RPCExamples examples);
+ using RPCMethodImpl = std::function<UniValue(const RPCHelpMan&, const JSONRPCRequest&)>;
+ RPCHelpMan(std::string name, std::string description, std::vector<RPCArg> args, RPCResults results, RPCExamples examples, RPCMethodImpl fun);
std::string ToString() const;
+ UniValue HandleRequest(const JSONRPCRequest& request)
+ {
+ Check(request);
+ return m_fun(*this, request);
+ }
/** If the supplied number of args is neither too small nor too high */
bool IsValidNumArgs(size_t num_args) const;
/**
@@ -343,8 +354,12 @@ public:
}
}
-private:
+ std::vector<std::string> GetArgNames() const;
+
const std::string m_name;
+
+private:
+ const RPCMethodImpl m_fun;
const std::string m_description;
const std::vector<RPCArg> m_args;
const RPCResults m_results;
diff --git a/src/sync.cpp b/src/sync.cpp
index 9abdedbed4..10f0483189 100644
--- a/src/sync.cpp
+++ b/src/sync.cpp
@@ -60,7 +60,7 @@ struct CLockLocation {
std::string ToString() const
{
return strprintf(
- "%s %s:%s%s (in thread %s)",
+ "'%s' in %s:%s%s (in thread '%s')",
mutexName, sourceFile, sourceLine, (fTry ? " (TRY)" : ""), m_thread_name);
}
@@ -105,7 +105,7 @@ static void potential_deadlock_detected(const LockPair& mismatch, const LockStac
{
LogPrintf("POTENTIAL DEADLOCK DETECTED\n");
LogPrintf("Previous lock order was:\n");
- for (const LockStackItem& i : s2) {
+ for (const LockStackItem& i : s1) {
if (i.first == mismatch.first) {
LogPrintf(" (1)"); /* Continued */
}
@@ -114,21 +114,25 @@ static void potential_deadlock_detected(const LockPair& mismatch, const LockStac
}
LogPrintf(" %s\n", i.second.ToString());
}
+
+ std::string mutex_a, mutex_b;
LogPrintf("Current lock order is:\n");
- for (const LockStackItem& i : s1) {
+ for (const LockStackItem& i : s2) {
if (i.first == mismatch.first) {
LogPrintf(" (1)"); /* Continued */
+ mutex_a = i.second.Name();
}
if (i.first == mismatch.second) {
LogPrintf(" (2)"); /* Continued */
+ mutex_b = i.second.Name();
}
LogPrintf(" %s\n", i.second.ToString());
}
if (g_debug_lockorder_abort) {
- tfm::format(std::cerr, "Assertion failed: detected inconsistent lock order at %s:%i, details in debug log.\n", __FILE__, __LINE__);
+ tfm::format(std::cerr, "Assertion failed: detected inconsistent lock order for %s, details in debug log.\n", s2.back().second.ToString());
abort();
}
- throw std::logic_error("potential deadlock detected");
+ throw std::logic_error(strprintf("potential deadlock detected: %s -> %s -> %s", mutex_b, mutex_a, mutex_b));
}
static void push_lock(void* c, const CLockLocation& locklocation)
diff --git a/src/test/denialofservice_tests.cpp b/src/test/denialofservice_tests.cpp
index 3d84fa855f..d49b51926c 100644
--- a/src/test/denialofservice_tests.cpp
+++ b/src/test/denialofservice_tests.cpp
@@ -217,7 +217,7 @@ BOOST_AUTO_TEST_CASE(stale_tip_peer_management)
connman->ClearNodes();
}
-BOOST_AUTO_TEST_CASE(DoS_banning)
+BOOST_AUTO_TEST_CASE(peer_discouragement)
{
auto banman = MakeUnique<BanMan>(GetDataDir() / "banlist.dat", nullptr, DEFAULT_MISBEHAVING_BANTIME);
auto connman = MakeUnique<CConnman>(0x1337, 0x1337);
@@ -249,7 +249,7 @@ BOOST_AUTO_TEST_CASE(DoS_banning)
dummyNode2.fSuccessfullyConnected = true;
{
LOCK(cs_main);
- Misbehaving(dummyNode2.GetId(), 50);
+ Misbehaving(dummyNode2.GetId(), DISCOURAGEMENT_THRESHOLD - 1);
}
{
LOCK2(cs_main, dummyNode2.cs_sendProcessing);
@@ -259,64 +259,20 @@ BOOST_AUTO_TEST_CASE(DoS_banning)
BOOST_CHECK(banman->IsDiscouraged(addr1)); // ... but 1 still should be
{
LOCK(cs_main);
- Misbehaving(dummyNode2.GetId(), 50);
+ Misbehaving(dummyNode2.GetId(), 1); // 2 reaches discouragement threshold
}
{
LOCK2(cs_main, dummyNode2.cs_sendProcessing);
BOOST_CHECK(peerLogic->SendMessages(&dummyNode2));
}
- BOOST_CHECK(banman->IsDiscouraged(addr2));
+ BOOST_CHECK(banman->IsDiscouraged(addr1)); // Expect both 1 and 2
+ BOOST_CHECK(banman->IsDiscouraged(addr2)); // to be discouraged now
bool dummy;
peerLogic->FinalizeNode(dummyNode1.GetId(), dummy);
peerLogic->FinalizeNode(dummyNode2.GetId(), dummy);
}
-BOOST_AUTO_TEST_CASE(DoS_banscore)
-{
- auto banman = MakeUnique<BanMan>(GetDataDir() / "banlist.dat", nullptr, DEFAULT_MISBEHAVING_BANTIME);
- auto connman = MakeUnique<CConnman>(0x1337, 0x1337);
- auto peerLogic = MakeUnique<PeerLogicValidation>(connman.get(), banman.get(), *m_node.scheduler, *m_node.chainman, *m_node.mempool);
-
- banman->ClearBanned();
- CAddress addr1(ip(0xa0b0c001), NODE_NONE);
- CNode dummyNode1(id++, NODE_NETWORK, 0, INVALID_SOCKET, addr1, 3, 1, CAddress(), "", true);
- dummyNode1.SetSendVersion(PROTOCOL_VERSION);
- peerLogic->InitializeNode(&dummyNode1);
- dummyNode1.nVersion = 1;
- dummyNode1.fSuccessfullyConnected = true;
- {
- LOCK(cs_main);
- Misbehaving(dummyNode1.GetId(), DISCOURAGEMENT_THRESHOLD - 11);
- }
- {
- LOCK2(cs_main, dummyNode1.cs_sendProcessing);
- BOOST_CHECK(peerLogic->SendMessages(&dummyNode1));
- }
- BOOST_CHECK(!banman->IsDiscouraged(addr1));
- {
- LOCK(cs_main);
- Misbehaving(dummyNode1.GetId(), 10);
- }
- {
- LOCK2(cs_main, dummyNode1.cs_sendProcessing);
- BOOST_CHECK(peerLogic->SendMessages(&dummyNode1));
- }
- BOOST_CHECK(!banman->IsDiscouraged(addr1));
- {
- LOCK(cs_main);
- Misbehaving(dummyNode1.GetId(), 1);
- }
- {
- LOCK2(cs_main, dummyNode1.cs_sendProcessing);
- BOOST_CHECK(peerLogic->SendMessages(&dummyNode1));
- }
- BOOST_CHECK(banman->IsDiscouraged(addr1));
-
- bool dummy;
- peerLogic->FinalizeNode(dummyNode1.GetId(), dummy);
-}
-
BOOST_AUTO_TEST_CASE(DoS_bantime)
{
auto banman = MakeUnique<BanMan>(GetDataDir() / "banlist.dat", nullptr, DEFAULT_MISBEHAVING_BANTIME);
diff --git a/src/test/fuzz/crypto_aes256.cpp b/src/test/fuzz/crypto_aes256.cpp
new file mode 100644
index 0000000000..ae14073c96
--- /dev/null
+++ b/src/test/fuzz/crypto_aes256.cpp
@@ -0,0 +1,30 @@
+// Copyright (c) 2020 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 <crypto/aes.h>
+#include <test/fuzz/FuzzedDataProvider.h>
+#include <test/fuzz/fuzz.h>
+#include <test/fuzz/util.h>
+
+#include <cassert>
+#include <cstdint>
+#include <vector>
+
+void test_one_input(const std::vector<uint8_t>& buffer)
+{
+ FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
+ const std::vector<uint8_t> key = ConsumeFixedLengthByteVector(fuzzed_data_provider, AES256_KEYSIZE);
+
+ AES256Encrypt encrypt{key.data()};
+ AES256Decrypt decrypt{key.data()};
+
+ while (fuzzed_data_provider.ConsumeBool()) {
+ const std::vector<uint8_t> plaintext = ConsumeFixedLengthByteVector(fuzzed_data_provider, AES_BLOCKSIZE);
+ std::vector<uint8_t> ciphertext(AES_BLOCKSIZE);
+ encrypt.Encrypt(ciphertext.data(), plaintext.data());
+ std::vector<uint8_t> decrypted_plaintext(AES_BLOCKSIZE);
+ decrypt.Decrypt(decrypted_plaintext.data(), ciphertext.data());
+ assert(decrypted_plaintext == plaintext);
+ }
+}
diff --git a/src/test/fuzz/crypto_aes256cbc.cpp b/src/test/fuzz/crypto_aes256cbc.cpp
new file mode 100644
index 0000000000..52983c7e79
--- /dev/null
+++ b/src/test/fuzz/crypto_aes256cbc.cpp
@@ -0,0 +1,34 @@
+// Copyright (c) 2020 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 <crypto/aes.h>
+#include <test/fuzz/FuzzedDataProvider.h>
+#include <test/fuzz/fuzz.h>
+#include <test/fuzz/util.h>
+
+#include <cassert>
+#include <cstdint>
+#include <vector>
+
+void test_one_input(const std::vector<uint8_t>& buffer)
+{
+ FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
+ const std::vector<uint8_t> key = ConsumeFixedLengthByteVector(fuzzed_data_provider, AES256_KEYSIZE);
+ const std::vector<uint8_t> iv = ConsumeFixedLengthByteVector(fuzzed_data_provider, AES_BLOCKSIZE);
+ const bool pad = fuzzed_data_provider.ConsumeBool();
+
+ AES256CBCEncrypt encrypt{key.data(), iv.data(), pad};
+ AES256CBCDecrypt decrypt{key.data(), iv.data(), pad};
+
+ while (fuzzed_data_provider.ConsumeBool()) {
+ const std::vector<uint8_t> plaintext = ConsumeRandomLengthByteVector(fuzzed_data_provider);
+ std::vector<uint8_t> ciphertext(plaintext.size() + AES_BLOCKSIZE);
+ const int encrypt_ret = encrypt.Encrypt(plaintext.data(), plaintext.size(), ciphertext.data());
+ ciphertext.resize(encrypt_ret);
+ std::vector<uint8_t> decrypted_plaintext(ciphertext.size());
+ const int decrypt_ret = decrypt.Decrypt(ciphertext.data(), ciphertext.size(), decrypted_plaintext.data());
+ decrypted_plaintext.resize(decrypt_ret);
+ assert(decrypted_plaintext == plaintext || (!pad && plaintext.size() % AES_BLOCKSIZE != 0 && encrypt_ret == 0 && decrypt_ret == 0));
+ }
+}
diff --git a/src/test/fuzz/crypto_chacha20.cpp b/src/test/fuzz/crypto_chacha20.cpp
new file mode 100644
index 0000000000..b7438d312d
--- /dev/null
+++ b/src/test/fuzz/crypto_chacha20.cpp
@@ -0,0 +1,50 @@
+// Copyright (c) 2020 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 <crypto/chacha20.h>
+#include <test/fuzz/FuzzedDataProvider.h>
+#include <test/fuzz/fuzz.h>
+#include <test/fuzz/util.h>
+
+#include <cstdint>
+#include <vector>
+
+void test_one_input(const std::vector<uint8_t>& buffer)
+{
+ FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
+
+ ChaCha20 chacha20;
+ if (fuzzed_data_provider.ConsumeBool()) {
+ const std::vector<unsigned char> key = ConsumeFixedLengthByteVector(fuzzed_data_provider, fuzzed_data_provider.ConsumeIntegralInRange<size_t>(16, 32));
+ chacha20 = ChaCha20{key.data(), key.size()};
+ }
+ while (fuzzed_data_provider.ConsumeBool()) {
+ switch (fuzzed_data_provider.ConsumeIntegralInRange(0, 4)) {
+ case 0: {
+ const std::vector<unsigned char> key = ConsumeFixedLengthByteVector(fuzzed_data_provider, fuzzed_data_provider.ConsumeIntegralInRange<size_t>(16, 32));
+ chacha20.SetKey(key.data(), key.size());
+ break;
+ }
+ case 1: {
+ chacha20.SetIV(fuzzed_data_provider.ConsumeIntegral<uint64_t>());
+ break;
+ }
+ case 2: {
+ chacha20.Seek(fuzzed_data_provider.ConsumeIntegral<uint64_t>());
+ break;
+ }
+ case 3: {
+ std::vector<uint8_t> output(fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 4096));
+ chacha20.Keystream(output.data(), output.size());
+ break;
+ }
+ case 4: {
+ std::vector<uint8_t> output(fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 4096));
+ const std::vector<uint8_t> input = ConsumeFixedLengthByteVector(fuzzed_data_provider, output.size());
+ chacha20.Crypt(input.data(), output.data(), input.size());
+ break;
+ }
+ }
+ }
+}
diff --git a/src/test/fuzz/crypto_chacha20_poly1305_aead.cpp b/src/test/fuzz/crypto_chacha20_poly1305_aead.cpp
new file mode 100644
index 0000000000..48e4263f27
--- /dev/null
+++ b/src/test/fuzz/crypto_chacha20_poly1305_aead.cpp
@@ -0,0 +1,72 @@
+// Copyright (c) 2020 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 <crypto/chacha_poly_aead.h>
+#include <crypto/poly1305.h>
+#include <test/fuzz/FuzzedDataProvider.h>
+#include <test/fuzz/fuzz.h>
+#include <test/fuzz/util.h>
+
+#include <cassert>
+#include <cstdint>
+#include <limits>
+#include <vector>
+
+void test_one_input(const std::vector<uint8_t>& buffer)
+{
+ FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
+
+ const std::vector<uint8_t> k1 = ConsumeFixedLengthByteVector(fuzzed_data_provider, CHACHA20_POLY1305_AEAD_KEY_LEN);
+ const std::vector<uint8_t> k2 = ConsumeFixedLengthByteVector(fuzzed_data_provider, CHACHA20_POLY1305_AEAD_KEY_LEN);
+
+ ChaCha20Poly1305AEAD aead(k1.data(), k1.size(), k2.data(), k2.size());
+ uint64_t seqnr_payload = 0;
+ uint64_t seqnr_aad = 0;
+ int aad_pos = 0;
+ size_t buffer_size = fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 4096);
+ std::vector<uint8_t> in(buffer_size + CHACHA20_POLY1305_AEAD_AAD_LEN + POLY1305_TAGLEN, 0);
+ std::vector<uint8_t> out(buffer_size + CHACHA20_POLY1305_AEAD_AAD_LEN + POLY1305_TAGLEN, 0);
+ bool is_encrypt = fuzzed_data_provider.ConsumeBool();
+ while (fuzzed_data_provider.ConsumeBool()) {
+ switch (fuzzed_data_provider.ConsumeIntegralInRange<int>(0, 6)) {
+ case 0: {
+ buffer_size = fuzzed_data_provider.ConsumeIntegralInRange<size_t>(64, 4096);
+ in = std::vector<uint8_t>(buffer_size + CHACHA20_POLY1305_AEAD_AAD_LEN + POLY1305_TAGLEN, 0);
+ out = std::vector<uint8_t>(buffer_size + CHACHA20_POLY1305_AEAD_AAD_LEN + POLY1305_TAGLEN, 0);
+ break;
+ }
+ case 1: {
+ (void)aead.Crypt(seqnr_payload, seqnr_aad, aad_pos, out.data(), out.size(), in.data(), buffer_size, is_encrypt);
+ break;
+ }
+ case 2: {
+ uint32_t len = 0;
+ const bool ok = aead.GetLength(&len, seqnr_aad, aad_pos, in.data());
+ assert(ok);
+ break;
+ }
+ case 3: {
+ seqnr_payload += 1;
+ aad_pos += CHACHA20_POLY1305_AEAD_AAD_LEN;
+ if (aad_pos + CHACHA20_POLY1305_AEAD_AAD_LEN > CHACHA20_ROUND_OUTPUT) {
+ aad_pos = 0;
+ seqnr_aad += 1;
+ }
+ break;
+ }
+ case 4: {
+ seqnr_payload = fuzzed_data_provider.ConsumeIntegral<int>();
+ break;
+ }
+ case 5: {
+ seqnr_aad = fuzzed_data_provider.ConsumeIntegral<int>();
+ break;
+ }
+ case 6: {
+ is_encrypt = fuzzed_data_provider.ConsumeBool();
+ break;
+ }
+ }
+ }
+}
diff --git a/src/test/fuzz/crypto_hkdf_hmac_sha256_l32.cpp b/src/test/fuzz/crypto_hkdf_hmac_sha256_l32.cpp
new file mode 100644
index 0000000000..e0a4e90c10
--- /dev/null
+++ b/src/test/fuzz/crypto_hkdf_hmac_sha256_l32.cpp
@@ -0,0 +1,25 @@
+// Copyright (c) 2020 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 <crypto/hkdf_sha256_32.h>
+#include <test/fuzz/FuzzedDataProvider.h>
+#include <test/fuzz/fuzz.h>
+#include <test/fuzz/util.h>
+
+#include <cstdint>
+#include <string>
+#include <vector>
+
+void test_one_input(const std::vector<uint8_t>& buffer)
+{
+ FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
+
+ const std::vector<uint8_t> initial_key_material = ConsumeRandomLengthByteVector(fuzzed_data_provider);
+
+ CHKDF_HMAC_SHA256_L32 hkdf_hmac_sha256_l32(initial_key_material.data(), initial_key_material.size(), fuzzed_data_provider.ConsumeRandomLengthString(1024));
+ while (fuzzed_data_provider.ConsumeBool()) {
+ std::vector<uint8_t> out(32);
+ hkdf_hmac_sha256_l32.Expand32(fuzzed_data_provider.ConsumeRandomLengthString(128), out.data());
+ }
+}
diff --git a/src/test/fuzz/crypto_poly1305.cpp b/src/test/fuzz/crypto_poly1305.cpp
new file mode 100644
index 0000000000..5681e6a693
--- /dev/null
+++ b/src/test/fuzz/crypto_poly1305.cpp
@@ -0,0 +1,22 @@
+// Copyright (c) 2020 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 <crypto/poly1305.h>
+#include <test/fuzz/FuzzedDataProvider.h>
+#include <test/fuzz/fuzz.h>
+#include <test/fuzz/util.h>
+
+#include <cstdint>
+#include <vector>
+
+void test_one_input(const std::vector<uint8_t>& buffer)
+{
+ FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
+
+ const std::vector<uint8_t> key = ConsumeFixedLengthByteVector(fuzzed_data_provider, POLY1305_KEYLEN);
+ const std::vector<uint8_t> in = ConsumeRandomLengthByteVector(fuzzed_data_provider);
+
+ std::vector<uint8_t> tag_out(POLY1305_TAGLEN);
+ poly1305_auth(tag_out.data(), in.data(), in.size(), key.data());
+}
diff --git a/src/test/sync_tests.cpp b/src/test/sync_tests.cpp
index 5c6c2ee38e..3ea8714f3a 100644
--- a/src/test/sync_tests.cpp
+++ b/src/test/sync_tests.cpp
@@ -18,7 +18,7 @@ void TestPotentialDeadLockDetected(MutexType& mutex1, MutexType& mutex2)
try {
LOCK2(mutex2, mutex1);
} catch (const std::logic_error& e) {
- BOOST_CHECK_EQUAL(e.what(), "potential deadlock detected");
+ BOOST_CHECK_EQUAL(e.what(), "potential deadlock detected: mutex1 -> mutex2 -> mutex1");
error_thrown = true;
}
#ifdef DEBUG_LOCKORDER
diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp
index 257328974b..e247c09a97 100644
--- a/src/test/util_tests.cpp
+++ b/src/test/util_tests.cpp
@@ -41,6 +41,16 @@ namespace BCLog {
BOOST_FIXTURE_TEST_SUITE(util_tests, BasicTestingSetup)
+BOOST_AUTO_TEST_CASE(util_check)
+{
+ // Check that Assert can forward
+ const std::unique_ptr<int> p_two = Assert(MakeUnique<int>(2));
+ // Check that Assert works on lvalues and rvalues
+ const int two = *Assert(p_two);
+ Assert(two == 2);
+ Assert(true);
+}
+
BOOST_AUTO_TEST_CASE(util_criticalsection)
{
RecursiveMutex cs;
diff --git a/src/util/check.h b/src/util/check.h
index 3d534fd33e..9edf394492 100644
--- a/src/util/check.h
+++ b/src/util/check.h
@@ -54,6 +54,6 @@ T get_pure_r_value(T&& val)
}
/** Identity function. Abort if the value compares equal to zero */
-#define Assert(val) [&]() -> decltype(get_pure_r_value(val))& { auto& check = (val); assert(#val && check); return check; }()
+#define Assert(val) [&]() -> decltype(get_pure_r_value(val)) { auto&& check = (val); assert(#val && check); return std::forward<decltype(get_pure_r_value(val))>(check); }()
#endif // BITCOIN_UTIL_CHECK_H
diff --git a/src/validation.cpp b/src/validation.cpp
index edc623b205..b90ff440be 100644
--- a/src/validation.cpp
+++ b/src/validation.cpp
@@ -573,8 +573,9 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
CAmount& nConflictingFees = ws.m_conflicting_fees;
size_t& nConflictingSize = ws.m_conflicting_size;
- if (!CheckTransaction(tx, state))
+ if (!CheckTransaction(tx, state)) {
return false; // state filled in by CheckTransaction
+ }
// Coinbase is only valid in a block, not as a loose transaction
if (tx.IsCoinBase())
@@ -684,7 +685,7 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
CAmount nFees = 0;
if (!Consensus::CheckTxInputs(tx, state, m_view, GetSpendHeight(m_view), nFees)) {
- return error("%s: Consensus::CheckTxInputs: %s, %s", __func__, tx.GetHash().ToString(), state.ToString());
+ return false; // state filled in by CheckTxInputs
}
// Check for non-standard pay-to-script-hash in inputs
diff --git a/src/wallet/bdb.cpp b/src/wallet/bdb.cpp
index 54a9aa34e0..44d1bafaf6 100644
--- a/src/wallet/bdb.cpp
+++ b/src/wallet/bdb.cpp
@@ -841,3 +841,8 @@ bool BerkeleyBatch::HasKey(CDataStream&& key)
int ret = pdb->exists(activeTxn, datKey, 0);
return ret == 0;
}
+
+std::unique_ptr<BerkeleyBatch> BerkeleyDatabase::MakeBatch(const char* mode, bool flush_on_close)
+{
+ return MakeUnique<BerkeleyBatch>(*this, mode, flush_on_close);
+}
diff --git a/src/wallet/bdb.h b/src/wallet/bdb.h
index 9cc42ddc06..e54776fc0d 100644
--- a/src/wallet/bdb.h
+++ b/src/wallet/bdb.h
@@ -93,6 +93,8 @@ std::shared_ptr<BerkeleyEnvironment> GetWalletEnv(const fs::path& wallet_path, s
/** Return whether a BDB wallet database is currently loaded. */
bool IsBDBWalletLoaded(const fs::path& wallet_path);
+class BerkeleyBatch;
+
/** An instance of this class represents one database.
* For BerkeleyDB this is just a (env, strFile) tuple.
**/
@@ -161,6 +163,9 @@ public:
/** Database pointer. This is initialized lazily and reset during flushes, so it can be null. */
std::unique_ptr<Db> m_db;
+ /** Make a BerkeleyBatch connected to this database */
+ std::unique_ptr<BerkeleyBatch> MakeBatch(const char* mode, bool flush_on_close);
+
private:
std::string strFile;
@@ -172,7 +177,7 @@ private:
};
/** RAII class that provides access to a Berkeley database */
-class BerkeleyBatch
+class BerkeleyBatch : public DatabaseBatch
{
/** RAII class that automatically cleanses its data on destruction */
class SafeDbt final
@@ -195,10 +200,10 @@ class BerkeleyBatch
};
private:
- bool ReadKey(CDataStream&& key, CDataStream& value);
- bool WriteKey(CDataStream&& key, CDataStream&& value, bool overwrite = true);
- bool EraseKey(CDataStream&& key);
- bool HasKey(CDataStream&& key);
+ bool ReadKey(CDataStream&& key, CDataStream& value) override;
+ bool WriteKey(CDataStream&& key, CDataStream&& value, bool overwrite = true) override;
+ bool EraseKey(CDataStream&& key) override;
+ bool HasKey(CDataStream&& key) override;
protected:
Db* pdb;
@@ -211,71 +216,20 @@ protected:
public:
explicit BerkeleyBatch(BerkeleyDatabase& database, const char* pszMode = "r+", bool fFlushOnCloseIn=true);
- ~BerkeleyBatch() { Close(); }
+ ~BerkeleyBatch() override { Close(); }
BerkeleyBatch(const BerkeleyBatch&) = delete;
BerkeleyBatch& operator=(const BerkeleyBatch&) = delete;
- void Flush();
- void Close();
-
- template <typename K, typename T>
- bool Read(const K& key, T& value)
- {
- CDataStream ssKey(SER_DISK, CLIENT_VERSION);
- ssKey.reserve(1000);
- ssKey << key;
-
- CDataStream ssValue(SER_DISK, CLIENT_VERSION);
- if (!ReadKey(std::move(ssKey), ssValue)) return false;
- try {
- ssValue >> value;
- return true;
- } catch (const std::exception&) {
- return false;
- }
- }
-
- template <typename K, typename T>
- bool Write(const K& key, const T& value, bool fOverwrite = true)
- {
- CDataStream ssKey(SER_DISK, CLIENT_VERSION);
- ssKey.reserve(1000);
- ssKey << key;
-
- CDataStream ssValue(SER_DISK, CLIENT_VERSION);
- ssValue.reserve(10000);
- ssValue << value;
-
- return WriteKey(std::move(ssKey), std::move(ssValue), fOverwrite);
- }
-
- template <typename K>
- bool Erase(const K& key)
- {
- CDataStream ssKey(SER_DISK, CLIENT_VERSION);
- ssKey.reserve(1000);
- ssKey << key;
-
- return EraseKey(std::move(ssKey));
- }
-
- template <typename K>
- bool Exists(const K& key)
- {
- CDataStream ssKey(SER_DISK, CLIENT_VERSION);
- ssKey.reserve(1000);
- ssKey << key;
-
- return HasKey(std::move(ssKey));
- }
+ void Flush() override;
+ void Close() override;
- bool StartCursor();
- bool ReadAtCursor(CDataStream& ssKey, CDataStream& ssValue, bool& complete);
- void CloseCursor();
- bool TxnBegin();
- bool TxnCommit();
- bool TxnAbort();
+ bool StartCursor() override;
+ bool ReadAtCursor(CDataStream& ssKey, CDataStream& ssValue, bool& complete) override;
+ void CloseCursor() override;
+ bool TxnBegin() override;
+ bool TxnCommit() override;
+ bool TxnAbort() override;
};
std::string BerkeleyDatabaseVersion();
diff --git a/src/wallet/db.h b/src/wallet/db.h
index 1322bf54fa..76668f8dc2 100644
--- a/src/wallet/db.h
+++ b/src/wallet/db.h
@@ -6,7 +6,9 @@
#ifndef BITCOIN_WALLET_DB_H
#define BITCOIN_WALLET_DB_H
+#include <clientversion.h>
#include <fs.h>
+#include <streams.h>
#include <string>
@@ -14,4 +16,82 @@
fs::path WalletDataFilePath(const fs::path& wallet_path);
void SplitWalletPath(const fs::path& wallet_path, fs::path& env_directory, std::string& database_filename);
+/** RAII class that provides access to a WalletDatabase */
+class DatabaseBatch
+{
+private:
+ virtual bool ReadKey(CDataStream&& key, CDataStream& value) = 0;
+ virtual bool WriteKey(CDataStream&& key, CDataStream&& value, bool overwrite=true) = 0;
+ virtual bool EraseKey(CDataStream&& key) = 0;
+ virtual bool HasKey(CDataStream&& key) = 0;
+
+public:
+ explicit DatabaseBatch() {}
+ virtual ~DatabaseBatch() {}
+
+ DatabaseBatch(const DatabaseBatch&) = delete;
+ DatabaseBatch& operator=(const DatabaseBatch&) = delete;
+
+ virtual void Flush() = 0;
+ virtual void Close() = 0;
+
+ template <typename K, typename T>
+ bool Read(const K& key, T& value)
+ {
+ CDataStream ssKey(SER_DISK, CLIENT_VERSION);
+ ssKey.reserve(1000);
+ ssKey << key;
+
+ CDataStream ssValue(SER_DISK, CLIENT_VERSION);
+ if (!ReadKey(std::move(ssKey), ssValue)) return false;
+ try {
+ ssValue >> value;
+ return true;
+ } catch (const std::exception&) {
+ return false;
+ }
+ }
+
+ template <typename K, typename T>
+ bool Write(const K& key, const T& value, bool fOverwrite = true)
+ {
+ CDataStream ssKey(SER_DISK, CLIENT_VERSION);
+ ssKey.reserve(1000);
+ ssKey << key;
+
+ CDataStream ssValue(SER_DISK, CLIENT_VERSION);
+ ssValue.reserve(10000);
+ ssValue << value;
+
+ return WriteKey(std::move(ssKey), std::move(ssValue), fOverwrite);
+ }
+
+ template <typename K>
+ bool Erase(const K& key)
+ {
+ CDataStream ssKey(SER_DISK, CLIENT_VERSION);
+ ssKey.reserve(1000);
+ ssKey << key;
+
+ return EraseKey(std::move(ssKey));
+ }
+
+ template <typename K>
+ bool Exists(const K& key)
+ {
+ CDataStream ssKey(SER_DISK, CLIENT_VERSION);
+ ssKey.reserve(1000);
+ ssKey << key;
+
+ return HasKey(std::move(ssKey));
+ }
+
+ virtual bool StartCursor() = 0;
+ virtual bool ReadAtCursor(CDataStream& ssKey, CDataStream& ssValue, bool& complete) = 0;
+ virtual void CloseCursor() = 0;
+ virtual bool TxnBegin() = 0;
+ virtual bool TxnCommit() = 0;
+ virtual bool TxnAbort() = 0;
+};
+
#endif // BITCOIN_WALLET_DB_H
diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp
index 1e5c4da6d8..1478687bf9 100644
--- a/src/wallet/walletdb.cpp
+++ b/src/wallet/walletdb.cpp
@@ -121,7 +121,7 @@ bool WalletBatch::WriteCryptedKey(const CPubKey& vchPubKey,
if (!WriteIC(key, std::make_pair(vchCryptedSecret, checksum), false)) {
// It may already exist, so try writing just the checksum
std::vector<unsigned char> val;
- if (!m_batch.Read(key, val)) {
+ if (!m_batch->Read(key, val)) {
return false;
}
if (!WriteIC(key, std::make_pair(val, checksum), true)) {
@@ -166,8 +166,8 @@ bool WalletBatch::WriteBestBlock(const CBlockLocator& locator)
bool WalletBatch::ReadBestBlock(CBlockLocator& locator)
{
- if (m_batch.Read(DBKeys::BESTBLOCK, locator) && !locator.vHave.empty()) return true;
- return m_batch.Read(DBKeys::BESTBLOCK_NOMERKLE, locator);
+ if (m_batch->Read(DBKeys::BESTBLOCK, locator) && !locator.vHave.empty()) return true;
+ return m_batch->Read(DBKeys::BESTBLOCK_NOMERKLE, locator);
}
bool WalletBatch::WriteOrderPosNext(int64_t nOrderPosNext)
@@ -177,7 +177,7 @@ bool WalletBatch::WriteOrderPosNext(int64_t nOrderPosNext)
bool WalletBatch::ReadPool(int64_t nPool, CKeyPool& keypool)
{
- return m_batch.Read(std::make_pair(DBKeys::POOL, nPool), keypool);
+ return m_batch->Read(std::make_pair(DBKeys::POOL, nPool), keypool);
}
bool WalletBatch::WritePool(int64_t nPool, const CKeyPool& keypool)
@@ -690,14 +690,14 @@ DBErrors WalletBatch::LoadWallet(CWallet* pwallet)
LOCK(pwallet->cs_wallet);
try {
int nMinVersion = 0;
- if (m_batch.Read(DBKeys::MINVERSION, nMinVersion)) {
+ if (m_batch->Read(DBKeys::MINVERSION, nMinVersion)) {
if (nMinVersion > FEATURE_LATEST)
return DBErrors::TOO_NEW;
pwallet->LoadMinVersion(nMinVersion);
}
// Get cursor
- if (!m_batch.StartCursor())
+ if (!m_batch->StartCursor())
{
pwallet->WalletLogPrintf("Error getting wallet database cursor\n");
return DBErrors::CORRUPT;
@@ -709,13 +709,13 @@ DBErrors WalletBatch::LoadWallet(CWallet* pwallet)
CDataStream ssKey(SER_DISK, CLIENT_VERSION);
CDataStream ssValue(SER_DISK, CLIENT_VERSION);
bool complete;
- bool ret = m_batch.ReadAtCursor(ssKey, ssValue, complete);
+ bool ret = m_batch->ReadAtCursor(ssKey, ssValue, complete);
if (complete) {
break;
}
else if (!ret)
{
- m_batch.CloseCursor();
+ m_batch->CloseCursor();
pwallet->WalletLogPrintf("Error reading next record from wallet database\n");
return DBErrors::CORRUPT;
}
@@ -745,7 +745,7 @@ DBErrors WalletBatch::LoadWallet(CWallet* pwallet)
} catch (...) {
result = DBErrors::CORRUPT;
}
- m_batch.CloseCursor();
+ m_batch->CloseCursor();
// Set the active ScriptPubKeyMans
for (auto spk_man_pair : wss.m_active_external_spks) {
@@ -782,7 +782,7 @@ DBErrors WalletBatch::LoadWallet(CWallet* pwallet)
// Last client version to open this wallet, was previously the file version number
int last_client = CLIENT_VERSION;
- m_batch.Read(DBKeys::VERSION, last_client);
+ m_batch->Read(DBKeys::VERSION, last_client);
int wallet_version = pwallet->GetVersion();
pwallet->WalletLogPrintf("Wallet File Version = %d\n", wallet_version > 0 ? wallet_version : last_client);
@@ -807,7 +807,7 @@ DBErrors WalletBatch::LoadWallet(CWallet* pwallet)
return DBErrors::NEED_REWRITE;
if (last_client < CLIENT_VERSION) // Update
- m_batch.Write(DBKeys::VERSION, CLIENT_VERSION);
+ m_batch->Write(DBKeys::VERSION, CLIENT_VERSION);
if (wss.fAnyUnordered)
result = pwallet->ReorderTransactions();
@@ -843,13 +843,13 @@ DBErrors WalletBatch::FindWalletTx(std::vector<uint256>& vTxHash, std::list<CWal
try {
int nMinVersion = 0;
- if (m_batch.Read(DBKeys::MINVERSION, nMinVersion)) {
+ if (m_batch->Read(DBKeys::MINVERSION, nMinVersion)) {
if (nMinVersion > FEATURE_LATEST)
return DBErrors::TOO_NEW;
}
// Get cursor
- if (!m_batch.StartCursor())
+ if (!m_batch->StartCursor())
{
LogPrintf("Error getting wallet database cursor\n");
return DBErrors::CORRUPT;
@@ -861,11 +861,11 @@ DBErrors WalletBatch::FindWalletTx(std::vector<uint256>& vTxHash, std::list<CWal
CDataStream ssKey(SER_DISK, CLIENT_VERSION);
CDataStream ssValue(SER_DISK, CLIENT_VERSION);
bool complete;
- bool ret = m_batch.ReadAtCursor(ssKey, ssValue, complete);
+ bool ret = m_batch->ReadAtCursor(ssKey, ssValue, complete);
if (complete) {
break;
} else if (!ret) {
- m_batch.CloseCursor();
+ m_batch->CloseCursor();
LogPrintf("Error reading next record from wallet database\n");
return DBErrors::CORRUPT;
}
@@ -883,7 +883,7 @@ DBErrors WalletBatch::FindWalletTx(std::vector<uint256>& vTxHash, std::list<CWal
} catch (...) {
result = DBErrors::CORRUPT;
}
- m_batch.CloseCursor();
+ m_batch->CloseCursor();
return result;
}
@@ -993,17 +993,17 @@ bool WalletBatch::WriteWalletFlags(const uint64_t flags)
bool WalletBatch::TxnBegin()
{
- return m_batch.TxnBegin();
+ return m_batch->TxnBegin();
}
bool WalletBatch::TxnCommit()
{
- return m_batch.TxnCommit();
+ return m_batch->TxnCommit();
}
bool WalletBatch::TxnAbort()
{
- return m_batch.TxnAbort();
+ return m_batch->TxnAbort();
}
bool IsWalletLoaded(const fs::path& wallet_path)
diff --git a/src/wallet/walletdb.h b/src/wallet/walletdb.h
index 61e0f19e56..6b55361c07 100644
--- a/src/wallet/walletdb.h
+++ b/src/wallet/walletdb.h
@@ -183,12 +183,12 @@ private:
template <typename K, typename T>
bool WriteIC(const K& key, const T& value, bool fOverwrite = true)
{
- if (!m_batch.Write(key, value, fOverwrite)) {
+ if (!m_batch->Write(key, value, fOverwrite)) {
return false;
}
m_database.IncrementUpdateCounter();
if (m_database.nUpdateCounter % 1000 == 0) {
- m_batch.Flush();
+ m_batch->Flush();
}
return true;
}
@@ -196,19 +196,19 @@ private:
template <typename K>
bool EraseIC(const K& key)
{
- if (!m_batch.Erase(key)) {
+ if (!m_batch->Erase(key)) {
return false;
}
m_database.IncrementUpdateCounter();
if (m_database.nUpdateCounter % 1000 == 0) {
- m_batch.Flush();
+ m_batch->Flush();
}
return true;
}
public:
explicit WalletBatch(WalletDatabase& database, const char* pszMode = "r+", bool _fFlushOnClose = true) :
- m_batch(database, pszMode, _fFlushOnClose),
+ m_batch(database.MakeBatch(pszMode, _fFlushOnClose)),
m_database(database)
{
}
@@ -280,7 +280,7 @@ public:
//! Abort current transaction
bool TxnAbort();
private:
- BerkeleyBatch m_batch;
+ std::unique_ptr<BerkeleyBatch> m_batch;
WalletDatabase& m_database;
};
diff --git a/test/functional/p2p_leak.py b/test/functional/p2p_leak.py
index 6cf252e626..fe6e236fc4 100755
--- a/test/functional/p2p_leak.py
+++ b/test/functional/p2p_leak.py
@@ -65,9 +65,10 @@ class CLazyNode(P2PInterface):
# Node that never sends a version. We'll use this to send a bunch of messages
# anyway, and eventually get disconnected.
-class CNodeNoVersionBan(CLazyNode):
- # send a bunch of veracks without sending a message. This should get us disconnected.
- # NOTE: implementation-specific check here. Remove if bitcoind ban behavior changes
+class CNodeNoVersionMisbehavior(CLazyNode):
+ # Send enough veracks without a message to reach the peer discouragement
+ # threshold. This should get us disconnected. NOTE: implementation-specific
+ # test; update if our discouragement policy for peer misbehavior changes.
def on_open(self):
super().on_open()
for _ in range(DISCOURAGEMENT_THRESHOLD):
@@ -108,7 +109,8 @@ class P2PLeakTest(BitcoinTestFramework):
self.num_nodes = 1
def run_test(self):
- no_version_bannode = self.nodes[0].add_p2p_connection(CNodeNoVersionBan(), send_version=False, wait_for_verack=False)
+ no_version_disconnect_node = self.nodes[0].add_p2p_connection(
+ CNodeNoVersionMisbehavior(), send_version=False, wait_for_verack=False)
no_version_idlenode = self.nodes[0].add_p2p_connection(CNodeNoVersionIdle(), send_version=False, wait_for_verack=False)
no_verack_idlenode = self.nodes[0].add_p2p_connection(CNodeNoVerackIdle(), wait_for_verack=False)
@@ -116,7 +118,7 @@ class P2PLeakTest(BitcoinTestFramework):
# verack, since we never sent one
no_verack_idlenode.wait_for_verack()
- wait_until(lambda: no_version_bannode.ever_connected, timeout=10, lock=mininode_lock)
+ wait_until(lambda: no_version_disconnect_node.ever_connected, timeout=10, lock=mininode_lock)
wait_until(lambda: no_version_idlenode.ever_connected, timeout=10, lock=mininode_lock)
wait_until(lambda: no_verack_idlenode.version_received, timeout=10, lock=mininode_lock)
@@ -126,13 +128,13 @@ class P2PLeakTest(BitcoinTestFramework):
#Give the node enough time to possibly leak out a message
time.sleep(5)
- #This node should have been banned
- assert not no_version_bannode.is_connected
+ # Expect this node to be disconnected for misbehavior
+ assert not no_version_disconnect_node.is_connected
self.nodes[0].disconnect_p2ps()
# Make sure no unexpected messages came in
- assert no_version_bannode.unexpected_msg == False
+ assert no_version_disconnect_node.unexpected_msg == False
assert no_version_idlenode.unexpected_msg == False
assert no_verack_idlenode.unexpected_msg == False
diff --git a/test/functional/wallet_encryption.py b/test/functional/wallet_encryption.py
index 6cd82ad250..4509c1e0b2 100755
--- a/test/functional/wallet_encryption.py
+++ b/test/functional/wallet_encryption.py
@@ -13,6 +13,7 @@ from test_framework.util import (
assert_greater_than_or_equal,
)
+
class WalletEncryptionTest(BitcoinTestFramework):
def set_test_params(self):
self.setup_clean_chain = True
@@ -72,20 +73,25 @@ class WalletEncryptionTest(BitcoinTestFramework):
# Test timeout bounds
assert_raises_rpc_error(-8, "Timeout cannot be negative.", self.nodes[0].walletpassphrase, passphrase2, -10)
- # Check the timeout
- # Check a time less than the limit
+
+ self.log.info('Check a timeout less than the limit')
MAX_VALUE = 100000000
expected_time = int(time.time()) + MAX_VALUE - 600
self.nodes[0].walletpassphrase(passphrase2, MAX_VALUE - 600)
+ # give buffer for walletpassphrase, since it iterates over all crypted keys
+ expected_time_with_buffer = time.time() + MAX_VALUE - 600
actual_time = self.nodes[0].getwalletinfo()['unlocked_until']
assert_greater_than_or_equal(actual_time, expected_time)
- assert_greater_than(expected_time + 5, actual_time) # 5 second buffer
- # Check a time greater than the limit
+ assert_greater_than(expected_time_with_buffer, actual_time)
+
+ self.log.info('Check a timeout greater than the limit')
expected_time = int(time.time()) + MAX_VALUE - 1
self.nodes[0].walletpassphrase(passphrase2, MAX_VALUE + 1000)
+ expected_time_with_buffer = time.time() + MAX_VALUE
actual_time = self.nodes[0].getwalletinfo()['unlocked_until']
assert_greater_than_or_equal(actual_time, expected_time)
- assert_greater_than(expected_time + 5, actual_time) # 5 second buffer
+ assert_greater_than(expected_time_with_buffer, actual_time)
+
if __name__ == '__main__':
WalletEncryptionTest().main()