aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/release-notes.md4
-rw-r--r--src/Makefile.test.include29
-rw-r--r--src/base58.cpp20
-rw-r--r--src/base58.h8
-rw-r--r--src/bench/base58.cpp2
-rw-r--r--src/key_io.cpp8
-rw-r--r--src/protocol.h1
-rw-r--r--src/qt/guiutil.cpp41
-rw-r--r--src/test/base58_tests.cpp25
-rw-r--r--src/test/fuzz/parse_hd_keypath.cpp13
-rw-r--r--src/test/fuzz/parse_numbers.cpp35
-rw-r--r--src/test/fuzz/parse_script.cpp16
-rw-r--r--src/test/fuzz/parse_univalue.cpp91
-rw-r--r--src/wallet/scriptpubkeyman.cpp59
-rw-r--r--src/wallet/scriptpubkeyman.h18
-rw-r--r--src/wallet/wallet.cpp42
-rw-r--r--src/wallet/wallet.h20
-rwxr-xr-xtest/functional/feature_segwit.py1
-rwxr-xr-xtest/functional/mempool_package_onemore.py1
-rwxr-xr-xtest/functional/mempool_packages.py1
-rwxr-xr-xtest/functional/rpc_fundrawtransaction.py1
-rwxr-xr-xtest/functional/rpc_signrawtransaction.py1
-rwxr-xr-xtest/functional/rpc_txoutproof.py1
-rwxr-xr-xtest/functional/test_framework/test_node.py3
-rw-r--r--test/functional/test_framework/util.py5
-rwxr-xr-xtest/functional/wallet_abandonconflict.py1
-rwxr-xr-xtest/functional/wallet_balance.py1
-rwxr-xr-xtest/functional/wallet_bumpfee.py1
-rwxr-xr-xtest/functional/wallet_bumpfee_totalfee_deprecation.py1
-rwxr-xr-xtest/functional/wallet_listsinceblock.py1
-rwxr-xr-xtest/functional/wallet_reorgsrestore.py1
-rwxr-xr-xtest/fuzz/test_runner.py6
-rwxr-xr-xtest/lint/lint-locale-dependence.sh1
33 files changed, 339 insertions, 120 deletions
diff --git a/doc/release-notes.md b/doc/release-notes.md
index 07caf0e53a..cdf83178c6 100644
--- a/doc/release-notes.md
+++ b/doc/release-notes.md
@@ -104,6 +104,10 @@ Low-level changes
Tests
-----
+- It is now an error to use an unqualified `walletdir=path` setting in the config file if running on testnet or regtest
+ networks. The setting now needs to be qualified as `chain.walletdir=path` or placed in the appropriate `[chain]`
+ section. (#17447)
+
- `-fallbackfee` was 0 (disabled) by default for the main chain, but 0.0002 by default for the test chains. Now it is 0
by default for all chains. Testnet and regtest users will have to add `fallbackfee=0.0002` to their configuration if
they weren't setting it and they want it to keep working like before. (#16524)
diff --git a/src/Makefile.test.include b/src/Makefile.test.include
index 6edbe77776..0225edf29e 100644
--- a/src/Makefile.test.include
+++ b/src/Makefile.test.include
@@ -33,10 +33,14 @@ FUZZ_TARGETS = \
test/fuzz/messageheader_deserialize \
test/fuzz/netaddr_deserialize \
test/fuzz/out_point_deserialize \
+ test/fuzz/parse_hd_keypath \
test/fuzz/parse_iso8601 \
test/fuzz/partial_merkle_tree_deserialize \
test/fuzz/partially_signed_transaction_deserialize \
test/fuzz/prefilled_transaction_deserialize \
+ test/fuzz/parse_numbers \
+ test/fuzz/parse_script \
+ test/fuzz/parse_univalue \
test/fuzz/psbt \
test/fuzz/psbt_input_deserialize \
test/fuzz/psbt_output_deserialize \
@@ -94,6 +98,7 @@ FUZZ_SUITE_LD_COMMON = \
$(LIBTEST_UTIL) \
$(LIBBITCOIN_CONSENSUS) \
$(LIBBITCOIN_CRYPTO) \
+ $(LIBBITCOIN_CLI) \
$(LIBUNIVALUE) \
$(LIBLEVELDB) \
$(LIBLEVELDB_SSE42) \
@@ -518,6 +523,30 @@ test_fuzz_tx_out_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_tx_out_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
test_fuzz_tx_out_LDADD = $(FUZZ_SUITE_LD_COMMON)
+test_fuzz_parse_hd_keypath_SOURCES = $(FUZZ_SUITE) test/fuzz/parse_hd_keypath.cpp
+test_fuzz_parse_hd_keypath_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
+test_fuzz_parse_hd_keypath_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
+test_fuzz_parse_hd_keypath_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_parse_hd_keypath_LDADD = $(FUZZ_SUITE_LD_COMMON)
+
+test_fuzz_parse_script_SOURCES = $(FUZZ_SUITE) test/fuzz/parse_script.cpp
+test_fuzz_parse_script_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
+test_fuzz_parse_script_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
+test_fuzz_parse_script_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_parse_script_LDADD = $(FUZZ_SUITE_LD_COMMON)
+
+test_fuzz_parse_numbers_SOURCES = $(FUZZ_SUITE) test/fuzz/parse_numbers.cpp
+test_fuzz_parse_numbers_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
+test_fuzz_parse_numbers_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
+test_fuzz_parse_numbers_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_parse_numbers_LDADD = $(FUZZ_SUITE_LD_COMMON)
+
+test_fuzz_parse_univalue_SOURCES = $(FUZZ_SUITE) test/fuzz/parse_univalue.cpp
+test_fuzz_parse_univalue_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
+test_fuzz_parse_univalue_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
+test_fuzz_parse_univalue_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_parse_univalue_LDADD = $(FUZZ_SUITE_LD_COMMON)
+
endif # ENABLE_FUZZ
nodist_test_test_bitcoin_SOURCES = $(GENERATED_TEST_FILES)
diff --git a/src/base58.cpp b/src/base58.cpp
index e3d2853399..a0149fb641 100644
--- a/src/base58.cpp
+++ b/src/base58.cpp
@@ -11,6 +11,8 @@
#include <assert.h>
#include <string.h>
+#include <limits>
+
/** All alphanumeric characters except for "0", "I", "O", and "l" */
static const char* pszBase58 = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
static const int8_t mapBase58[256] = {
@@ -32,7 +34,7 @@ static const int8_t mapBase58[256] = {
-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1,
};
-bool DecodeBase58(const char* psz, std::vector<unsigned char>& vch)
+bool DecodeBase58(const char* psz, std::vector<unsigned char>& vch, int max_ret_len)
{
// Skip leading spaces.
while (*psz && IsSpace(*psz))
@@ -42,6 +44,7 @@ bool DecodeBase58(const char* psz, std::vector<unsigned char>& vch)
int length = 0;
while (*psz == '1') {
zeroes++;
+ if (zeroes > max_ret_len) return false;
psz++;
}
// Allocate enough space in big-endian base256 representation.
@@ -62,6 +65,7 @@ bool DecodeBase58(const char* psz, std::vector<unsigned char>& vch)
}
assert(carry == 0);
length = i;
+ if (length + zeroes > max_ret_len) return false;
psz++;
}
// Skip trailing spaces.
@@ -71,8 +75,6 @@ bool DecodeBase58(const char* psz, std::vector<unsigned char>& vch)
return false;
// Skip leading zeroes in b256.
std::vector<unsigned char>::iterator it = b256.begin() + (size - length);
- while (it != b256.end() && *it == 0)
- it++;
// Copy result into output vector.
vch.reserve(zeroes + (b256.end() - it));
vch.assign(zeroes, 0x00);
@@ -126,9 +128,9 @@ std::string EncodeBase58(const std::vector<unsigned char>& vch)
return EncodeBase58(vch.data(), vch.data() + vch.size());
}
-bool DecodeBase58(const std::string& str, std::vector<unsigned char>& vchRet)
+bool DecodeBase58(const std::string& str, std::vector<unsigned char>& vchRet, int max_ret_len)
{
- return DecodeBase58(str.c_str(), vchRet);
+ return DecodeBase58(str.c_str(), vchRet, max_ret_len);
}
std::string EncodeBase58Check(const std::vector<unsigned char>& vchIn)
@@ -140,9 +142,9 @@ std::string EncodeBase58Check(const std::vector<unsigned char>& vchIn)
return EncodeBase58(vch);
}
-bool DecodeBase58Check(const char* psz, std::vector<unsigned char>& vchRet)
+bool DecodeBase58Check(const char* psz, std::vector<unsigned char>& vchRet, int max_ret_len)
{
- if (!DecodeBase58(psz, vchRet) ||
+ if (!DecodeBase58(psz, vchRet, max_ret_len > std::numeric_limits<int>::max() - 4 ? std::numeric_limits<int>::max() : max_ret_len + 4) ||
(vchRet.size() < 4)) {
vchRet.clear();
return false;
@@ -157,7 +159,7 @@ bool DecodeBase58Check(const char* psz, std::vector<unsigned char>& vchRet)
return true;
}
-bool DecodeBase58Check(const std::string& str, std::vector<unsigned char>& vchRet)
+bool DecodeBase58Check(const std::string& str, std::vector<unsigned char>& vchRet, int max_ret)
{
- return DecodeBase58Check(str.c_str(), vchRet);
+ return DecodeBase58Check(str.c_str(), vchRet, max_ret);
}
diff --git a/src/base58.h b/src/base58.h
index d6e0299a1e..90eded4992 100644
--- a/src/base58.h
+++ b/src/base58.h
@@ -35,13 +35,13 @@ std::string EncodeBase58(const std::vector<unsigned char>& vch);
* return true if decoding is successful.
* psz cannot be nullptr.
*/
-NODISCARD bool DecodeBase58(const char* psz, std::vector<unsigned char>& vchRet);
+NODISCARD bool DecodeBase58(const char* psz, std::vector<unsigned char>& vchRet, int max_ret_len);
/**
* Decode a base58-encoded string (str) into a byte vector (vchRet).
* return true if decoding is successful.
*/
-NODISCARD bool DecodeBase58(const std::string& str, std::vector<unsigned char>& vchRet);
+NODISCARD bool DecodeBase58(const std::string& str, std::vector<unsigned char>& vchRet, int max_ret_len);
/**
* Encode a byte vector into a base58-encoded string, including checksum
@@ -52,12 +52,12 @@ std::string EncodeBase58Check(const std::vector<unsigned char>& vchIn);
* Decode a base58-encoded string (psz) that includes a checksum into a byte
* vector (vchRet), return true if decoding is successful
*/
-NODISCARD bool DecodeBase58Check(const char* psz, std::vector<unsigned char>& vchRet);
+NODISCARD bool DecodeBase58Check(const char* psz, std::vector<unsigned char>& vchRet, int max_ret_len);
/**
* Decode a base58-encoded string (str) that includes a checksum into a byte
* vector (vchRet), return true if decoding is successful
*/
-NODISCARD bool DecodeBase58Check(const std::string& str, std::vector<unsigned char>& vchRet);
+NODISCARD bool DecodeBase58Check(const std::string& str, std::vector<unsigned char>& vchRet, int max_ret_len);
#endif // BITCOIN_BASE58_H
diff --git a/src/bench/base58.cpp b/src/bench/base58.cpp
index 40a7b5e320..d8c9b2e01a 100644
--- a/src/bench/base58.cpp
+++ b/src/bench/base58.cpp
@@ -47,7 +47,7 @@ static void Base58Decode(benchmark::State& state)
const char* addr = "17VZNX1SN5NtKa8UQFxwQbFeFc3iqRYhem";
std::vector<unsigned char> vch;
while (state.KeepRunning()) {
- (void) DecodeBase58(addr, vch);
+ (void) DecodeBase58(addr, vch, 64);
}
}
diff --git a/src/key_io.cpp b/src/key_io.cpp
index 363055d6b3..af06db7343 100644
--- a/src/key_io.cpp
+++ b/src/key_io.cpp
@@ -73,7 +73,7 @@ CTxDestination DecodeDestination(const std::string& str, const CChainParams& par
{
std::vector<unsigned char> data;
uint160 hash;
- if (DecodeBase58Check(str, data)) {
+ if (DecodeBase58Check(str, data, 21)) {
// base58-encoded Bitcoin addresses.
// Public-key-hash-addresses have version 0 (or 111 testnet).
// The data vector contains RIPEMD160(SHA256(pubkey)), where pubkey is the serialized public key.
@@ -133,7 +133,7 @@ CKey DecodeSecret(const std::string& str)
{
CKey key;
std::vector<unsigned char> data;
- if (DecodeBase58Check(str, data)) {
+ if (DecodeBase58Check(str, data, 34)) {
const std::vector<unsigned char>& privkey_prefix = Params().Base58Prefix(CChainParams::SECRET_KEY);
if ((data.size() == 32 + privkey_prefix.size() || (data.size() == 33 + privkey_prefix.size() && data.back() == 1)) &&
std::equal(privkey_prefix.begin(), privkey_prefix.end(), data.begin())) {
@@ -164,7 +164,7 @@ CExtPubKey DecodeExtPubKey(const std::string& str)
{
CExtPubKey key;
std::vector<unsigned char> data;
- if (DecodeBase58Check(str, data)) {
+ if (DecodeBase58Check(str, data, 78)) {
const std::vector<unsigned char>& prefix = Params().Base58Prefix(CChainParams::EXT_PUBLIC_KEY);
if (data.size() == BIP32_EXTKEY_SIZE + prefix.size() && std::equal(prefix.begin(), prefix.end(), data.begin())) {
key.Decode(data.data() + prefix.size());
@@ -187,7 +187,7 @@ CExtKey DecodeExtKey(const std::string& str)
{
CExtKey key;
std::vector<unsigned char> data;
- if (DecodeBase58Check(str, data)) {
+ if (DecodeBase58Check(str, data, 78)) {
const std::vector<unsigned char>& prefix = Params().Base58Prefix(CChainParams::EXT_SECRET_KEY);
if (data.size() == BIP32_EXTKEY_SIZE + prefix.size() && std::equal(prefix.begin(), prefix.end(), data.begin())) {
key.Decode(data.data() + prefix.size());
diff --git a/src/protocol.h b/src/protocol.h
index 3032310fa1..db07efb9f9 100644
--- a/src/protocol.h
+++ b/src/protocol.h
@@ -237,6 +237,7 @@ const std::vector<std::string> &getAllNetMessageTypes();
/** nServices flags */
enum ServiceFlags : uint64_t {
+ // NOTE: When adding here, be sure to update qt/guiutil.cpp's formatServicesStr too
// Nothing
NODE_NONE = 0,
// NODE_NETWORK means that the node is capable of serving the complete block chain. It is currently
diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp
index 2bb9535441..843de651e5 100644
--- a/src/qt/guiutil.cpp
+++ b/src/qt/guiutil.cpp
@@ -731,32 +731,33 @@ QString formatDurationStr(int secs)
return strList.join(" ");
}
+QString serviceFlagToStr(const quint64 mask, const int bit)
+{
+ switch (ServiceFlags(mask)) {
+ case NODE_NONE: abort(); // impossible
+ case NODE_NETWORK: return "NETWORK";
+ case NODE_GETUTXO: return "GETUTXO";
+ case NODE_BLOOM: return "BLOOM";
+ case NODE_WITNESS: return "WITNESS";
+ case NODE_NETWORK_LIMITED: return "NETWORK_LIMITED";
+ // Not using default, so we get warned when a case is missing
+ }
+ if (bit < 8) {
+ return QString("%1[%2]").arg("UNKNOWN").arg(mask);
+ } else {
+ return QString("%1[2^%2]").arg("UNKNOWN").arg(bit);
+ }
+}
+
QString formatServicesStr(quint64 mask)
{
QStringList strList;
- // Just scan the last 8 bits for now.
- for (int i = 0; i < 8; i++) {
- uint64_t check = 1 << i;
+ for (int i = 0; i < 64; i++) {
+ uint64_t check = 1LL << i;
if (mask & check)
{
- switch (check)
- {
- case NODE_NETWORK:
- strList.append("NETWORK");
- break;
- case NODE_GETUTXO:
- strList.append("GETUTXO");
- break;
- case NODE_BLOOM:
- strList.append("BLOOM");
- break;
- case NODE_WITNESS:
- strList.append("WITNESS");
- break;
- default:
- strList.append(QString("%1[%2]").arg("UNKNOWN").arg(check));
- }
+ strList.append(serviceFlagToStr(check, i));
}
}
diff --git a/src/test/base58_tests.cpp b/src/test/base58_tests.cpp
index 52301f799a..96fdf8c86d 100644
--- a/src/test/base58_tests.cpp
+++ b/src/test/base58_tests.cpp
@@ -7,6 +7,7 @@
#include <base58.h>
#include <test/util/setup_common.h>
#include <util/strencodings.h>
+#include <util/vector.h>
#include <univalue.h>
@@ -53,17 +54,33 @@ BOOST_AUTO_TEST_CASE(base58_DecodeBase58)
}
std::vector<unsigned char> expected = ParseHex(test[0].get_str());
std::string base58string = test[1].get_str();
- BOOST_CHECK_MESSAGE(DecodeBase58(base58string, result), strTest);
+ BOOST_CHECK_MESSAGE(DecodeBase58(base58string, result, 256), strTest);
BOOST_CHECK_MESSAGE(result.size() == expected.size() && std::equal(result.begin(), result.end(), expected.begin()), strTest);
}
- BOOST_CHECK(!DecodeBase58("invalid", result));
+ BOOST_CHECK(!DecodeBase58("invalid", result, 100));
// check that DecodeBase58 skips whitespace, but still fails with unexpected non-whitespace at the end.
- BOOST_CHECK(!DecodeBase58(" \t\n\v\f\r skip \r\f\v\n\t a", result));
- BOOST_CHECK( DecodeBase58(" \t\n\v\f\r skip \r\f\v\n\t ", result));
+ BOOST_CHECK(!DecodeBase58(" \t\n\v\f\r skip \r\f\v\n\t a", result, 3));
+ BOOST_CHECK( DecodeBase58(" \t\n\v\f\r skip \r\f\v\n\t ", result, 3));
std::vector<unsigned char> expected = ParseHex("971a55");
BOOST_CHECK_EQUAL_COLLECTIONS(result.begin(), result.end(), expected.begin(), expected.end());
}
+BOOST_AUTO_TEST_CASE(base58_random_encode_decode)
+{
+ for (int n = 0; n < 1000; ++n) {
+ unsigned int len = 1 + InsecureRandBits(8);
+ unsigned int zeroes = InsecureRandBool() ? InsecureRandRange(len + 1) : 0;
+ auto data = Cat(std::vector<unsigned char>(zeroes, '\000'), g_insecure_rand_ctx.randbytes(len - zeroes));
+ auto encoded = EncodeBase58Check(data);
+ std::vector<unsigned char> decoded;
+ auto ok_too_small = DecodeBase58Check(encoded, decoded, InsecureRandRange(len));
+ BOOST_CHECK(!ok_too_small);
+ auto ok = DecodeBase58Check(encoded, decoded, len + InsecureRandRange(257 - len));
+ BOOST_CHECK(ok);
+ BOOST_CHECK(data == decoded);
+ }
+}
+
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/fuzz/parse_hd_keypath.cpp b/src/test/fuzz/parse_hd_keypath.cpp
new file mode 100644
index 0000000000..9a23f4b2d4
--- /dev/null
+++ b/src/test/fuzz/parse_hd_keypath.cpp
@@ -0,0 +1,13 @@
+// Copyright (c) 2009-2019 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 <test/fuzz/fuzz.h>
+#include <util/bip32.h>
+
+void test_one_input(const std::vector<uint8_t>& buffer)
+{
+ const std::string keypath_str(buffer.begin(), buffer.end());
+ std::vector<uint32_t> keypath;
+ (void)ParseHDKeypath(keypath_str, keypath);
+}
diff --git a/src/test/fuzz/parse_numbers.cpp b/src/test/fuzz/parse_numbers.cpp
new file mode 100644
index 0000000000..59f89dc9fb
--- /dev/null
+++ b/src/test/fuzz/parse_numbers.cpp
@@ -0,0 +1,35 @@
+// Copyright (c) 2009-2019 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 <test/fuzz/fuzz.h>
+#include <util/moneystr.h>
+#include <util/strencodings.h>
+
+#include <string>
+
+void test_one_input(const std::vector<uint8_t>& buffer)
+{
+ const std::string random_string(buffer.begin(), buffer.end());
+
+ CAmount amount;
+ (void)ParseMoney(random_string, amount);
+
+ double d;
+ (void)ParseDouble(random_string, &d);
+
+ int32_t i32;
+ (void)ParseInt32(random_string, &i32);
+ (void)atoi(random_string);
+
+ uint32_t u32;
+ (void)ParseUInt32(random_string, &u32);
+
+ int64_t i64;
+ (void)atoi64(random_string);
+ (void)ParseFixedPoint(random_string, 3, &i64);
+ (void)ParseInt64(random_string, &i64);
+
+ uint64_t u64;
+ (void)ParseUInt64(random_string, &u64);
+}
diff --git a/src/test/fuzz/parse_script.cpp b/src/test/fuzz/parse_script.cpp
new file mode 100644
index 0000000000..21ac1aecf3
--- /dev/null
+++ b/src/test/fuzz/parse_script.cpp
@@ -0,0 +1,16 @@
+// Copyright (c) 2009-2019 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 <core_io.h>
+#include <script/script.h>
+#include <test/fuzz/fuzz.h>
+
+void test_one_input(const std::vector<uint8_t>& buffer)
+{
+ const std::string script_string(buffer.begin(), buffer.end());
+ try {
+ (void)ParseScript(script_string);
+ } catch (const std::runtime_error&) {
+ }
+}
diff --git a/src/test/fuzz/parse_univalue.cpp b/src/test/fuzz/parse_univalue.cpp
new file mode 100644
index 0000000000..3ad112dbad
--- /dev/null
+++ b/src/test/fuzz/parse_univalue.cpp
@@ -0,0 +1,91 @@
+// Copyright (c) 2009-2019 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 <chainparams.h>
+#include <core_io.h>
+#include <rpc/client.h>
+#include <rpc/util.h>
+#include <test/fuzz/fuzz.h>
+#include <util/memory.h>
+
+#include <limits>
+#include <string>
+
+void initialize()
+{
+ static const auto verify_handle = MakeUnique<ECCVerifyHandle>();
+ SelectParams(CBaseChainParams::REGTEST);
+}
+
+void test_one_input(const std::vector<uint8_t>& buffer)
+{
+ const std::string random_string(buffer.begin(), buffer.end());
+ bool valid = true;
+ const UniValue univalue = [&] {
+ try {
+ return ParseNonRFCJSONValue(random_string);
+ } catch (const std::runtime_error&) {
+ valid = false;
+ return NullUniValue;
+ }
+ }();
+ if (!valid) {
+ return;
+ }
+ try {
+ (void)ParseHashO(univalue, "A");
+ (void)ParseHashO(univalue, random_string);
+ } catch (const UniValue&) {
+ } catch (const std::runtime_error&) {
+ }
+ try {
+ (void)ParseHashV(univalue, "A");
+ (void)ParseHashV(univalue, random_string);
+ } catch (const UniValue&) {
+ } catch (const std::runtime_error&) {
+ }
+ try {
+ (void)ParseHexO(univalue, "A");
+ (void)ParseHexO(univalue, random_string);
+ } catch (const UniValue&) {
+ } catch (const std::runtime_error&) {
+ }
+ try {
+ (void)ParseHexUV(univalue, "A");
+ (void)ParseHexUV(univalue, random_string);
+ } catch (const UniValue&) {
+ } catch (const std::runtime_error&) {
+ }
+ try {
+ (void)ParseHexV(univalue, "A");
+ (void)ParseHexV(univalue, random_string);
+ } catch (const UniValue&) {
+ } catch (const std::runtime_error&) {
+ }
+ try {
+ (void)ParseSighashString(univalue);
+ } catch (const std::runtime_error&) {
+ }
+ try {
+ (void)AmountFromValue(univalue);
+ } catch (const UniValue&) {
+ } catch (const std::runtime_error&) {
+ }
+ try {
+ FlatSigningProvider provider;
+ (void)EvalDescriptorStringOrObject(univalue, provider);
+ } catch (const UniValue&) {
+ } catch (const std::runtime_error&) {
+ }
+ try {
+ (void)ParseConfirmTarget(univalue, std::numeric_limits<unsigned int>::max());
+ } catch (const UniValue&) {
+ } catch (const std::runtime_error&) {
+ }
+ try {
+ (void)ParseDescriptorRange(univalue);
+ } catch (const UniValue&) {
+ } catch (const std::runtime_error&) {
+ }
+}
diff --git a/src/wallet/scriptpubkeyman.cpp b/src/wallet/scriptpubkeyman.cpp
index 2b9e8fbf2a..b4315014cb 100644
--- a/src/wallet/scriptpubkeyman.cpp
+++ b/src/wallet/scriptpubkeyman.cpp
@@ -202,12 +202,11 @@ isminetype LegacyScriptPubKeyMan::IsMine(const CScript& script) const
assert(false);
}
-bool CWallet::Unlock(const CKeyingMaterial& vMasterKeyIn, bool accept_no_keys)
+bool LegacyScriptPubKeyMan::CheckDecryptionKey(const CKeyingMaterial& master_key, bool accept_no_keys)
{
{
LOCK(cs_KeyStore);
- if (!SetCrypted())
- return false;
+ assert(mapKeys.empty());
bool keyPass = mapCryptedKeys.empty(); // Always pass when there are no encrypted keys
bool keyFail = false;
@@ -217,7 +216,7 @@ bool CWallet::Unlock(const CKeyingMaterial& vMasterKeyIn, bool accept_no_keys)
const CPubKey &vchPubKey = (*mi).second.first;
const std::vector<unsigned char> &vchCryptedSecret = (*mi).second.second;
CKey key;
- if (!DecryptKey(vMasterKeyIn, vchCryptedSecret, vchPubKey, key))
+ if (!DecryptKey(master_key, vchCryptedSecret, vchPubKey, key))
{
keyFail = true;
break;
@@ -233,32 +232,39 @@ bool CWallet::Unlock(const CKeyingMaterial& vMasterKeyIn, bool accept_no_keys)
}
if (keyFail || (!keyPass && !accept_no_keys))
return false;
- vMasterKey = vMasterKeyIn;
fDecryptionThoroughlyChecked = true;
}
- NotifyStatusChanged(this);
return true;
}
-bool LegacyScriptPubKeyMan::EncryptKeys(CKeyingMaterial& vMasterKeyIn)
+bool LegacyScriptPubKeyMan::Encrypt(const CKeyingMaterial& master_key, WalletBatch* batch)
{
+ AssertLockHeld(cs_wallet);
LOCK(cs_KeyStore);
- if (!mapCryptedKeys.empty() || IsCrypted())
+ encrypted_batch = batch;
+ if (!mapCryptedKeys.empty()) {
+ encrypted_batch = nullptr;
return false;
+ }
- fUseCrypto = true;
- for (const KeyMap::value_type& mKey : mapKeys)
+ KeyMap keys_to_encrypt;
+ keys_to_encrypt.swap(mapKeys); // Clear mapKeys so AddCryptedKeyInner will succeed.
+ for (const KeyMap::value_type& mKey : keys_to_encrypt)
{
const CKey &key = mKey.second;
CPubKey vchPubKey = key.GetPubKey();
CKeyingMaterial vchSecret(key.begin(), key.end());
std::vector<unsigned char> vchCryptedSecret;
- if (!EncryptSecret(vMasterKeyIn, vchSecret, vchPubKey.GetHash(), vchCryptedSecret))
+ if (!EncryptSecret(master_key, vchSecret, vchPubKey.GetHash(), vchCryptedSecret)) {
+ encrypted_batch = nullptr;
return false;
- if (!AddCryptedKey(vchPubKey, vchCryptedSecret))
+ }
+ if (!AddCryptedKey(vchPubKey, vchCryptedSecret)) {
+ encrypted_batch = nullptr;
return false;
+ }
}
- mapKeys.clear();
+ encrypted_batch = nullptr;
return true;
}
@@ -543,7 +549,7 @@ bool LegacyScriptPubKeyMan::AddKeyPubKeyWithDB(WalletBatch& batch, const CKey& s
RemoveWatchOnly(script);
}
- if (!IsCrypted()) {
+ if (!m_storage.HasEncryptionKeys()) {
return batch.WriteKey(pubkey,
secret.GetPrivKey(),
mapKeyMetadata[pubkey.GetID()]);
@@ -584,7 +590,7 @@ void LegacyScriptPubKeyMan::LoadScriptMetadata(const CScriptID& script_id, const
bool LegacyScriptPubKeyMan::AddKeyPubKeyInner(const CKey& key, const CPubKey &pubkey)
{
LOCK(cs_KeyStore);
- if (!IsCrypted()) {
+ if (!m_storage.HasEncryptionKeys()) {
return FillableSigningProvider::AddKeyPubKey(key, pubkey);
}
@@ -594,7 +600,7 @@ bool LegacyScriptPubKeyMan::AddKeyPubKeyInner(const CKey& key, const CPubKey &pu
std::vector<unsigned char> vchCryptedSecret;
CKeyingMaterial vchSecret(key.begin(), key.end());
- if (!EncryptSecret(vMasterKey, vchSecret, pubkey.GetHash(), vchCryptedSecret)) {
+ if (!EncryptSecret(m_storage.GetEncryptionKey(), vchSecret, pubkey.GetHash(), vchCryptedSecret)) {
return false;
}
@@ -612,9 +618,7 @@ bool LegacyScriptPubKeyMan::LoadCryptedKey(const CPubKey &vchPubKey, const std::
bool LegacyScriptPubKeyMan::AddCryptedKeyInner(const CPubKey &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret)
{
LOCK(cs_KeyStore);
- if (!SetCrypted()) {
- return false;
- }
+ assert(mapKeys.empty());
mapCryptedKeys[vchPubKey.GetID()] = make_pair(vchPubKey, vchCryptedSecret);
ImplicitlyLearnRelatedKeyScripts(vchPubKey);
@@ -741,7 +745,7 @@ void LegacyScriptPubKeyMan::SetHDChain(const CHDChain& chain, bool memonly)
bool LegacyScriptPubKeyMan::HaveKey(const CKeyID &address) const
{
LOCK(cs_KeyStore);
- if (!IsCrypted()) {
+ if (!m_storage.HasEncryptionKeys()) {
return FillableSigningProvider::HaveKey(address);
}
return mapCryptedKeys.count(address) > 0;
@@ -750,7 +754,7 @@ bool LegacyScriptPubKeyMan::HaveKey(const CKeyID &address) const
bool LegacyScriptPubKeyMan::GetKey(const CKeyID &address, CKey& keyOut) const
{
LOCK(cs_KeyStore);
- if (!IsCrypted()) {
+ if (!m_storage.HasEncryptionKeys()) {
return FillableSigningProvider::GetKey(address, keyOut);
}
@@ -759,7 +763,7 @@ bool LegacyScriptPubKeyMan::GetKey(const CKeyID &address, CKey& keyOut) const
{
const CPubKey &vchPubKey = (*mi).second.first;
const std::vector<unsigned char> &vchCryptedSecret = (*mi).second.second;
- return DecryptKey(vMasterKey, vchCryptedSecret, vchPubKey, keyOut);
+ return DecryptKey(m_storage.GetEncryptionKey(), vchCryptedSecret, vchPubKey, keyOut);
}
return false;
}
@@ -797,7 +801,7 @@ bool LegacyScriptPubKeyMan::GetWatchPubKey(const CKeyID &address, CPubKey &pubke
bool LegacyScriptPubKeyMan::GetPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) const
{
LOCK(cs_KeyStore);
- if (!IsCrypted()) {
+ if (!m_storage.HasEncryptionKeys()) {
if (!FillableSigningProvider::GetPubKey(address, vchPubKeyOut)) {
return GetWatchPubKey(address, vchPubKeyOut);
}
@@ -1383,7 +1387,7 @@ bool LegacyScriptPubKeyMan::ImportScriptPubKeys(const std::set<CScript>& script_
std::set<CKeyID> LegacyScriptPubKeyMan::GetKeys() const
{
LOCK(cs_KeyStore);
- if (!IsCrypted()) {
+ if (!m_storage.HasEncryptionKeys()) {
return FillableSigningProvider::GetKeys();
}
std::set<CKeyID> set_address;
@@ -1397,13 +1401,8 @@ std::set<CKeyID> LegacyScriptPubKeyMan::GetKeys() const
LegacyScriptPubKeyMan::LegacyScriptPubKeyMan(CWallet& wallet)
: ScriptPubKeyMan(wallet),
m_wallet(wallet),
- cs_wallet(wallet.cs_wallet),
- vMasterKey(wallet.vMasterKey),
- fUseCrypto(wallet.fUseCrypto),
- fDecryptionThoroughlyChecked(wallet.fDecryptionThoroughlyChecked) {}
+ cs_wallet(wallet.cs_wallet) {}
-bool LegacyScriptPubKeyMan::SetCrypted() { return m_wallet.SetCrypted(); }
-bool LegacyScriptPubKeyMan::IsCrypted() const { return m_wallet.IsCrypted(); }
void LegacyScriptPubKeyMan::NotifyWatchonlyChanged(bool fHaveWatchOnly) const { return m_wallet.NotifyWatchonlyChanged(fHaveWatchOnly); }
void LegacyScriptPubKeyMan::NotifyCanGetAddressesChanged() const { return m_wallet.NotifyCanGetAddressesChanged(); }
template<typename... Params> void LegacyScriptPubKeyMan::WalletLogPrintf(const std::string& fmt, const Params&... parameters) const { return m_wallet.WalletLogPrintf(fmt, parameters...); }
diff --git a/src/wallet/scriptpubkeyman.h b/src/wallet/scriptpubkeyman.h
index 6ed9a4787a..aa5eac3a85 100644
--- a/src/wallet/scriptpubkeyman.h
+++ b/src/wallet/scriptpubkeyman.h
@@ -31,6 +31,8 @@ public:
virtual void UnsetBlankWalletFlag(WalletBatch&) = 0;
virtual bool CanSupportFeature(enum WalletFeature) const = 0;
virtual void SetMinVersion(enum WalletFeature, WalletBatch* = nullptr, bool = false) = 0;
+ virtual const CKeyingMaterial& GetEncryptionKey() const = 0;
+ virtual bool HasEncryptionKeys() const = 0;
virtual bool IsLocked() const = 0;
};
@@ -150,6 +152,10 @@ public:
virtual bool GetNewDestination(const OutputType type, CTxDestination& dest, std::string& error) { return false; }
virtual isminetype IsMine(const CScript& script) const { return ISMINE_NO; }
+ //! Check that the given decryption key is valid for this ScriptPubKeyMan, i.e. it decrypts all of the keys handled by it.
+ virtual bool CheckDecryptionKey(const CKeyingMaterial& master_key, bool accept_no_keys = false) { return false; }
+ virtual bool Encrypt(const CKeyingMaterial& master_key, WalletBatch* batch) { return false; }
+
virtual bool GetReservedDestination(const OutputType type, bool internal, CTxDestination& address, int64_t& index, CKeyPool& keypool) { return false; }
virtual void KeepDestination(int64_t index, const OutputType& type) {}
virtual void ReturnDestination(int64_t index, bool internal, const CTxDestination& addr) {}
@@ -193,6 +199,9 @@ public:
class LegacyScriptPubKeyMan : public ScriptPubKeyMan, public FillableSigningProvider
{
private:
+ //! keeps track of whether Unlock has run a thorough check before
+ bool fDecryptionThoroughlyChecked = false;
+
using WatchOnlySet = std::set<CScript>;
using WatchKeyMap = std::map<CKeyID, CPubKey>;
@@ -272,8 +281,8 @@ public:
bool GetNewDestination(const OutputType type, CTxDestination& dest, std::string& error) override;
isminetype IsMine(const CScript& script) const override;
- //! will encrypt previously unencrypted keys
- bool EncryptKeys(CKeyingMaterial& vMasterKeyIn);
+ bool CheckDecryptionKey(const CKeyingMaterial& master_key, bool accept_no_keys = false) override;
+ bool Encrypt(const CKeyingMaterial& master_key, WalletBatch* batch) override;
bool GetReservedDestination(const OutputType type, bool internal, CTxDestination& address, int64_t& index, CKeyPool& keypool) override;
void KeepDestination(int64_t index, const OutputType& type) override;
@@ -403,16 +412,11 @@ public:
friend class CWallet;
friend class ReserveDestination;
LegacyScriptPubKeyMan(CWallet& wallet);
- bool SetCrypted();
- bool IsCrypted() const;
void NotifyWatchonlyChanged(bool fHaveWatchOnly) const;
void NotifyCanGetAddressesChanged() const;
template<typename... Params> void WalletLogPrintf(const std::string& fmt, const Params&... parameters) const;
CWallet& m_wallet;
CCriticalSection& cs_wallet;
- CKeyingMaterial& vMasterKey GUARDED_BY(cs_KeyStore);
- std::atomic<bool>& fUseCrypto;
- bool& fDecryptionThoroughlyChecked;
};
#endif // BITCOIN_WALLET_SCRIPTPUBKEYMAN_H
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
index abee497c1d..41a816312a 100644
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -532,8 +532,7 @@ bool CWallet::EncryptWallet(const SecureString& strWalletPassphrase)
{
LOCK(cs_wallet);
mapMasterKeys[++nMasterKeyMaxID] = kMasterKey;
- assert(!encrypted_batch);
- encrypted_batch = new WalletBatch(*database);
+ WalletBatch* encrypted_batch = new WalletBatch(*database);
if (!encrypted_batch->TxnBegin()) {
delete encrypted_batch;
encrypted_batch = nullptr;
@@ -542,7 +541,7 @@ bool CWallet::EncryptWallet(const SecureString& strWalletPassphrase)
encrypted_batch->WriteMasterKey(nMasterKeyMaxID, kMasterKey);
if (auto spk_man = m_spk_man.get()) {
- if (!spk_man->EncryptKeys(_vMasterKey)) {
+ if (!spk_man->Encrypt(_vMasterKey, encrypted_batch)) {
encrypted_batch->TxnAbort();
delete encrypted_batch;
encrypted_batch = nullptr;
@@ -4003,15 +4002,9 @@ std::vector<OutputGroup> CWallet::GroupOutputs(const std::vector<COutput>& outpu
return groups;
}
-bool CWallet::SetCrypted()
+bool CWallet::IsCrypted() const
{
- LOCK(cs_KeyStore);
- if (fUseCrypto)
- return true;
- if (!mapKeys.empty())
- return false;
- fUseCrypto = true;
- return true;
+ return HasEncryptionKeys();
}
bool CWallet::IsLocked() const
@@ -4025,7 +4018,7 @@ bool CWallet::IsLocked() const
bool CWallet::Lock()
{
- if (!SetCrypted())
+ if (!IsCrypted())
return false;
{
@@ -4037,6 +4030,21 @@ bool CWallet::Lock()
return true;
}
+bool CWallet::Unlock(const CKeyingMaterial& vMasterKeyIn, bool accept_no_keys)
+{
+ {
+ LOCK(cs_KeyStore);
+ if (m_spk_man) {
+ if (!m_spk_man->CheckDecryptionKey(vMasterKeyIn, accept_no_keys)) {
+ return false;
+ }
+ }
+ vMasterKey = vMasterKeyIn;
+ }
+ NotifyStatusChanged(this);
+ return true;
+}
+
ScriptPubKeyMan* CWallet::GetScriptPubKeyMan(const CScript& script) const
{
return m_spk_man.get();
@@ -4056,3 +4064,13 @@ LegacyScriptPubKeyMan* CWallet::GetLegacyScriptPubKeyMan() const
{
return m_spk_man.get();
}
+
+const CKeyingMaterial& CWallet::GetEncryptionKey() const
+{
+ return vMasterKey;
+}
+
+bool CWallet::HasEncryptionKeys() const
+{
+ return !mapMasterKeys.empty();
+}
diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h
index b02e092f0a..c4511601de 100644
--- a/src/wallet/wallet.h
+++ b/src/wallet/wallet.h
@@ -597,14 +597,7 @@ class CWallet final : public WalletStorage, private interfaces::Chain::Notificat
private:
CKeyingMaterial vMasterKey GUARDED_BY(cs_KeyStore);
- //! if fUseCrypto is true, mapKeys must be empty
- //! if fUseCrypto is false, vMasterKey must be empty
- std::atomic<bool> fUseCrypto;
- //! keeps track of whether Unlock has run a thorough check before
- bool fDecryptionThoroughlyChecked;
-
- bool SetCrypted();
bool Unlock(const CKeyingMaterial& vMasterKeyIn, bool accept_no_keys = false);
std::atomic<bool> fAbortRescan{false};
@@ -734,9 +727,7 @@ public:
/** Construct wallet with specified name and database implementation. */
CWallet(interfaces::Chain* chain, const WalletLocation& location, std::unique_ptr<WalletDatabase> database)
- : fUseCrypto(false),
- fDecryptionThoroughlyChecked(false),
- m_chain(chain),
+ : m_chain(chain),
m_location(location),
database(std::move(database))
{
@@ -746,11 +737,9 @@ public:
{
// Should not have slots connected at this point.
assert(NotifyUnload.empty());
- delete encrypted_batch;
- encrypted_batch = nullptr;
}
- bool IsCrypted() const { return fUseCrypto; }
+ bool IsCrypted() const;
bool IsLocked() const override;
bool Lock();
@@ -1136,6 +1125,9 @@ public:
LegacyScriptPubKeyMan* GetLegacyScriptPubKeyMan() const;
+ const CKeyingMaterial& GetEncryptionKey() const override;
+ bool HasEncryptionKeys() const override;
+
// Temporary LegacyScriptPubKeyMan accessors and aliases.
friend class LegacyScriptPubKeyMan;
std::unique_ptr<LegacyScriptPubKeyMan> m_spk_man = MakeUnique<LegacyScriptPubKeyMan>(*this);
@@ -1145,8 +1137,6 @@ public:
LegacyScriptPubKeyMan::CryptedKeyMap& mapCryptedKeys GUARDED_BY(cs_KeyStore) = m_spk_man->mapCryptedKeys;
LegacyScriptPubKeyMan::WatchOnlySet& setWatchOnly GUARDED_BY(cs_KeyStore) = m_spk_man->setWatchOnly;
LegacyScriptPubKeyMan::WatchKeyMap& mapWatchKeys GUARDED_BY(cs_KeyStore) = m_spk_man->mapWatchKeys;
- WalletBatch*& encrypted_batch GUARDED_BY(cs_wallet) = m_spk_man->encrypted_batch;
- using CryptedKeyMap = LegacyScriptPubKeyMan::CryptedKeyMap;
/** Get last block processed height */
int GetLastBlockHeight() const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet)
diff --git a/test/functional/feature_segwit.py b/test/functional/feature_segwit.py
index bf3c56228e..82c7e55245 100755
--- a/test/functional/feature_segwit.py
+++ b/test/functional/feature_segwit.py
@@ -72,7 +72,6 @@ class SegWitTest(BitcoinTestFramework):
"-addresstype=legacy",
],
]
- self.supports_cli = False
def skip_test_if_missing_module(self):
self.skip_if_no_wallet()
diff --git a/test/functional/mempool_package_onemore.py b/test/functional/mempool_package_onemore.py
index 7ff8c12cc9..0739d7e29b 100755
--- a/test/functional/mempool_package_onemore.py
+++ b/test/functional/mempool_package_onemore.py
@@ -19,7 +19,6 @@ class MempoolPackagesTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 1
self.extra_args = [["-maxorphantx=1000"]]
- self.supports_cli = False
def skip_test_if_missing_module(self):
self.skip_if_no_wallet()
diff --git a/test/functional/mempool_packages.py b/test/functional/mempool_packages.py
index d6498f18a7..7014105d88 100755
--- a/test/functional/mempool_packages.py
+++ b/test/functional/mempool_packages.py
@@ -27,7 +27,6 @@ class MempoolPackagesTest(BitcoinTestFramework):
["-maxorphantx=1000"],
["-maxorphantx=1000", "-limitancestorcount={}".format(MAX_ANCESTORS_CUSTOM)],
]
- self.supports_cli = False
def skip_test_if_missing_module(self):
self.skip_if_no_wallet()
diff --git a/test/functional/rpc_fundrawtransaction.py b/test/functional/rpc_fundrawtransaction.py
index 3ae42cb1f4..6f1ae0d3ba 100755
--- a/test/functional/rpc_fundrawtransaction.py
+++ b/test/functional/rpc_fundrawtransaction.py
@@ -31,7 +31,6 @@ class RawTransactionsTest(BitcoinTestFramework):
# This test isn't testing tx relay. Set whitelist on the peers for
# instant tx relay.
self.extra_args = [['-whitelist=127.0.0.1']] * self.num_nodes
- self.supports_cli = False
def skip_test_if_missing_module(self):
self.skip_if_no_wallet()
diff --git a/test/functional/rpc_signrawtransaction.py b/test/functional/rpc_signrawtransaction.py
index 9fd44762d8..780758e219 100755
--- a/test/functional/rpc_signrawtransaction.py
+++ b/test/functional/rpc_signrawtransaction.py
@@ -15,7 +15,6 @@ class SignRawTransactionsTest(BitcoinTestFramework):
def set_test_params(self):
self.setup_clean_chain = True
self.num_nodes = 2
- self.supports_cli = False
def skip_test_if_missing_module(self):
self.skip_if_no_wallet()
diff --git a/test/functional/rpc_txoutproof.py b/test/functional/rpc_txoutproof.py
index 9f15971171..8913b8698d 100755
--- a/test/functional/rpc_txoutproof.py
+++ b/test/functional/rpc_txoutproof.py
@@ -14,7 +14,6 @@ class MerkleBlockTest(BitcoinTestFramework):
self.setup_clean_chain = True
# Nodes 0/1 are "wallet" nodes, Nodes 2/3 are used for testing
self.extra_args = [[], [], [], ["-txindex"]]
- self.supports_cli = False
def skip_test_if_missing_module(self):
self.skip_if_no_wallet()
diff --git a/test/functional/test_framework/test_node.py b/test/functional/test_framework/test_node.py
index 40681790b9..e5c77ae5fa 100755
--- a/test/functional/test_framework/test_node.py
+++ b/test/functional/test_framework/test_node.py
@@ -30,6 +30,7 @@ from .util import (
rpc_url,
wait_until,
p2p_port,
+ EncodeDecimal,
)
BITCOIND_PROC_WAIT_TIMEOUT = 60
@@ -489,7 +490,7 @@ def arg_to_cli(arg):
if isinstance(arg, bool):
return str(arg).lower()
elif isinstance(arg, dict) or isinstance(arg, list):
- return json.dumps(arg)
+ return json.dumps(arg, default=EncodeDecimal)
else:
return str(arg)
diff --git a/test/functional/test_framework/util.py b/test/functional/test_framework/util.py
index 4d7967273a..5bb73aee7e 100644
--- a/test/functional/test_framework/util.py
+++ b/test/functional/test_framework/util.py
@@ -190,6 +190,11 @@ def check_json_precision():
if satoshis != 2000000000000003:
raise RuntimeError("JSON encode/decode loses precision")
+def EncodeDecimal(o):
+ if isinstance(o, Decimal):
+ return str(o)
+ raise TypeError(repr(o) + " is not JSON serializable")
+
def count_bytes(hex_string):
return len(bytearray.fromhex(hex_string))
diff --git a/test/functional/wallet_abandonconflict.py b/test/functional/wallet_abandonconflict.py
index 3a81f14b00..1122daaf83 100755
--- a/test/functional/wallet_abandonconflict.py
+++ b/test/functional/wallet_abandonconflict.py
@@ -26,7 +26,6 @@ class AbandonConflictTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 2
self.extra_args = [["-minrelaytxfee=0.00001"], []]
- self.supports_cli = False
def skip_test_if_missing_module(self):
self.skip_if_no_wallet()
diff --git a/test/functional/wallet_balance.py b/test/functional/wallet_balance.py
index e0ee33ccdb..a5f9a047ed 100755
--- a/test/functional/wallet_balance.py
+++ b/test/functional/wallet_balance.py
@@ -54,7 +54,6 @@ class WalletTest(BitcoinTestFramework):
['-limitdescendantcount=3'], # Limit mempool descendants as a hack to have wallet txs rejected from the mempool
[],
]
- self.supports_cli = False
def skip_test_if_missing_module(self):
self.skip_if_no_wallet()
diff --git a/test/functional/wallet_bumpfee.py b/test/functional/wallet_bumpfee.py
index 7eb756df51..0c08655833 100755
--- a/test/functional/wallet_bumpfee.py
+++ b/test/functional/wallet_bumpfee.py
@@ -40,7 +40,6 @@ class BumpFeeTest(BitcoinTestFramework):
"-deprecatedrpc=totalFee",
"-addresstype=bech32",
] for i in range(self.num_nodes)]
- self.supports_cli = False
def skip_test_if_missing_module(self):
self.skip_if_no_wallet()
diff --git a/test/functional/wallet_bumpfee_totalfee_deprecation.py b/test/functional/wallet_bumpfee_totalfee_deprecation.py
index af6e7b67b3..b8e097c32e 100755
--- a/test/functional/wallet_bumpfee_totalfee_deprecation.py
+++ b/test/functional/wallet_bumpfee_totalfee_deprecation.py
@@ -16,7 +16,6 @@ class BumpFeeWithTotalFeeArgumentDeprecationTest(BitcoinTestFramework):
"-walletrbf={}".format(i),
"-mintxfee=0.00002",
] for i in range(self.num_nodes)]
- self.supports_cli = False
def skip_test_if_missing_module(self):
self.skip_if_no_wallet()
diff --git a/test/functional/wallet_listsinceblock.py b/test/functional/wallet_listsinceblock.py
index dc69cedc44..6f248c9bd3 100755
--- a/test/functional/wallet_listsinceblock.py
+++ b/test/functional/wallet_listsinceblock.py
@@ -19,7 +19,6 @@ class ListSinceBlockTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 4
self.setup_clean_chain = True
- self.supports_cli = False
def skip_test_if_missing_module(self):
self.skip_if_no_wallet()
diff --git a/test/functional/wallet_reorgsrestore.py b/test/functional/wallet_reorgsrestore.py
index 0797785560..f48018e9fb 100755
--- a/test/functional/wallet_reorgsrestore.py
+++ b/test/functional/wallet_reorgsrestore.py
@@ -27,7 +27,6 @@ from test_framework.util import (
class ReorgsRestoreTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 3
- self.supports_cli = False
def skip_test_if_missing_module(self):
self.skip_if_no_wallet()
diff --git a/test/fuzz/test_runner.py b/test/fuzz/test_runner.py
index f6d84a1dcd..2d255c0bb4 100755
--- a/test/fuzz/test_runner.py
+++ b/test/fuzz/test_runner.py
@@ -24,6 +24,10 @@ FUZZERS_MISSING_CORPORA = [
"key_origin_info_deserialize",
"merkle_block_deserialize",
"out_point_deserialize",
+ "parse_hd_keypath",
+ "parse_numbers",
+ "parse_script",
+ "parse_univalue",
"partial_merkle_tree_deserialize",
"partially_signed_transaction_deserialize",
"prefilled_transaction_deserialize",
@@ -32,8 +36,8 @@ FUZZERS_MISSING_CORPORA = [
"pub_key_deserialize",
"script_deserialize",
"sub_net_deserialize",
- "tx_in_deserialize",
"tx_in",
+ "tx_in_deserialize",
"tx_out",
]
diff --git a/test/lint/lint-locale-dependence.sh b/test/lint/lint-locale-dependence.sh
index 9a1aa766f7..ea9ea7a58d 100755
--- a/test/lint/lint-locale-dependence.sh
+++ b/test/lint/lint-locale-dependence.sh
@@ -11,6 +11,7 @@ KNOWN_VIOLATIONS=(
"src/qt/rpcconsole.cpp:.*atoi"
"src/rest.cpp:.*strtol"
"src/test/dbwrapper_tests.cpp:.*snprintf"
+ "src/test/fuzz/parse_numbers.cpp:.*atoi"
"src/torcontrol.cpp:.*atoi"
"src/torcontrol.cpp:.*strtol"
"src/util/strencodings.cpp:.*atoi"