diff --git a/configure.ac b/configure.ac index af682ac9c1..ed959a503a 100644 --- a/configure.ac +++ b/configure.ac @@ -1053,7 +1053,7 @@ if test x$TARGET_OS = xdarwin; then AX_CHECK_LINK_FLAG([[-Wl,-fixup_chains]], [HARDENED_LDFLAGS="$HARDENED_LDFLAGS -Wl,-fixup_chains"], [], [[$LDFLAG_WERROR]]) fi -AC_CHECK_HEADERS([endian.h sys/endian.h byteswap.h stdio.h stdlib.h unistd.h strings.h sys/types.h sys/stat.h sys/select.h sys/prctl.h sys/sysctl.h vm/vm_param.h sys/vmmeter.h sys/resources.h]) +AC_CHECK_HEADERS([sys/select.h sys/prctl.h sys/sysctl.h vm/vm_param.h sys/vmmeter.h sys/resources.h]) AC_CHECK_DECLS([getifaddrs, freeifaddrs],[CHECK_SOCKET],, [#include @@ -1068,18 +1068,6 @@ AC_CHECK_DECLS([pipe2]) AC_CHECK_FUNCS([timingsafe_bcmp]) -AC_CHECK_DECLS([le16toh, le32toh, le64toh, htole16, htole32, htole64, be16toh, be32toh, be64toh, htobe16, htobe32, htobe64],,, - [#if HAVE_ENDIAN_H - #include - #elif HAVE_SYS_ENDIAN_H - #include - #endif]) - -AC_CHECK_DECLS([bswap_16, bswap_32, bswap_64],,, - [#if HAVE_BYTESWAP_H - #include - #endif]) - dnl Check for mallopt(M_ARENA_MAX) (to set glibc arenas) AC_MSG_CHECKING(for mallopt M_ARENA_MAX) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include ]], diff --git a/src/Makefile.test.include b/src/Makefile.test.include index 7583a616b9..f37db7fb8d 100644 --- a/src/Makefile.test.include +++ b/src/Makefile.test.include @@ -73,6 +73,7 @@ endif # test_dash binary # BITCOIN_TESTS =\ + test/argsman_tests.cpp \ test/arith_uint256_tests.cpp \ test/scriptnum10.h \ test/addrman_tests.cpp \ diff --git a/src/bench/ellswift.cpp b/src/bench/ellswift.cpp index 325b92bb2c..3cf9ae489d 100644 --- a/src/bench/ellswift.cpp +++ b/src/bench/ellswift.cpp @@ -17,12 +17,12 @@ static void EllSwiftCreate(benchmark::Bench& bench) uint256 entropy = GetRandHash(); bench.batch(1).unit("pubkey").run([&] { - auto ret = key.EllSwiftCreate(AsBytes(Span{entropy})); + auto ret = key.EllSwiftCreate(MakeByteSpan(entropy)); /* Use the first 32 bytes of the ellswift encoded public key as next private key. */ key.Set(UCharCast(ret.data()), UCharCast(ret.data()) + 32, true); assert(key.IsValid()); /* Use the last 32 bytes of the ellswift encoded public key as next entropy. */ - std::copy(ret.begin() + 32, ret.begin() + 64, BytePtr(entropy.data())); + std::copy(ret.begin() + 32, ret.begin() + 64, MakeWritableByteSpan(entropy).begin()); }); ECC_Stop(); diff --git a/src/bitcoin-tx.cpp b/src/bitcoin-tx.cpp index 7875efb28c..584f9966cb 100644 --- a/src/bitcoin-tx.cpp +++ b/src/bitcoin-tx.cpp @@ -26,10 +26,10 @@ #include #include +#include #include #include #include -#include #include diff --git a/src/bitcoind.cpp b/src/bitcoind.cpp index a3b9910e2d..2b0bf5d33a 100644 --- a/src/bitcoind.cpp +++ b/src/bitcoind.cpp @@ -27,8 +27,8 @@ #include #include +#include #include -#include const std::function G_TRANSLATION_FUN = nullptr; UrlDecodeFn* const URL_DECODE = urlDecode; diff --git a/src/compat/byteswap.h b/src/compat/byteswap.h index 27ef1a18df..a027a770e7 100644 --- a/src/compat/byteswap.h +++ b/src/compat/byteswap.h @@ -5,44 +5,66 @@ #ifndef BITCOIN_COMPAT_BYTESWAP_H #define BITCOIN_COMPAT_BYTESWAP_H -#if defined(HAVE_CONFIG_H) -#include +#include +#ifdef _MSC_VER +#include #endif -#include -#if defined(HAVE_BYTESWAP_H) -#include +// All internal_bswap_* functions can be replaced with std::byteswap once we +// require c++23. Both libstdc++ and libc++ implement std::byteswap via these +// builtins. + +#ifndef DISABLE_BUILTIN_BSWAPS +# if defined __has_builtin +# if __has_builtin(__builtin_bswap16) +# define bitcoin_builtin_bswap16(x) __builtin_bswap16(x) +# endif +# if __has_builtin(__builtin_bswap32) +# define bitcoin_builtin_bswap32(x) __builtin_bswap32(x) +# endif +# if __has_builtin(__builtin_bswap64) +# define bitcoin_builtin_bswap64(x) __builtin_bswap64(x) +# endif +# elif defined(_MSC_VER) +# define bitcoin_builtin_bswap16(x) _byteswap_ushort(x) +# define bitcoin_builtin_bswap32(x) _byteswap_ulong(x) +# define bitcoin_builtin_bswap64(x) _byteswap_uint64(x) +# endif #endif -#if defined(MAC_OSX) - -#include -#define bswap_16(x) OSSwapInt16(x) -#define bswap_32(x) OSSwapInt32(x) -#define bswap_64(x) OSSwapInt64(x) +// MSVC's _byteswap_* functions are not constexpr +#ifndef _MSC_VER +#define BSWAP_CONSTEXPR constexpr #else -// Non-MacOS / non-Darwin +#define BSWAP_CONSTEXPR +#endif -#if HAVE_DECL_BSWAP_16 == 0 -inline uint16_t bswap_16(uint16_t x) +inline BSWAP_CONSTEXPR uint16_t internal_bswap_16(uint16_t x) { +#ifdef bitcoin_builtin_bswap16 + return bitcoin_builtin_bswap16(x); +#else return (x >> 8) | (x << 8); +#endif } -#endif // HAVE_DECL_BSWAP16 == 0 -#if HAVE_DECL_BSWAP_32 == 0 -inline uint32_t bswap_32(uint32_t x) +inline BSWAP_CONSTEXPR uint32_t internal_bswap_32(uint32_t x) { +#ifdef bitcoin_builtin_bswap32 + return bitcoin_builtin_bswap32(x); +#else return (((x & 0xff000000U) >> 24) | ((x & 0x00ff0000U) >> 8) | ((x & 0x0000ff00U) << 8) | ((x & 0x000000ffU) << 24)); +#endif } -#endif // HAVE_DECL_BSWAP32 == 0 -#if HAVE_DECL_BSWAP_64 == 0 -inline uint64_t bswap_64(uint64_t x) +inline BSWAP_CONSTEXPR uint64_t internal_bswap_64(uint64_t x) { +#ifdef bitcoin_builtin_bswap64 + return bitcoin_builtin_bswap64(x); +#else return (((x & 0xff00000000000000ull) >> 56) | ((x & 0x00ff000000000000ull) >> 40) | ((x & 0x0000ff0000000000ull) >> 24) @@ -51,9 +73,7 @@ inline uint64_t bswap_64(uint64_t x) | ((x & 0x0000000000ff0000ull) << 24) | ((x & 0x000000000000ff00ull) << 40) | ((x & 0x00000000000000ffull) << 56)); +#endif } -#endif // HAVE_DECL_BSWAP64 == 0 - -#endif // defined(MAC_OSX) #endif // BITCOIN_COMPAT_BYTESWAP_H diff --git a/src/compat/cpuid.h b/src/compat/cpuid.h index 0877ad47d3..e78c1ce6d1 100644 --- a/src/compat/cpuid.h +++ b/src/compat/cpuid.h @@ -10,6 +10,8 @@ #include +#include + // We can't use cpuid.h's __get_cpuid as it does not support subleafs. void static inline GetCPUID(uint32_t leaf, uint32_t subleaf, uint32_t& a, uint32_t& b, uint32_t& c, uint32_t& d) { diff --git a/src/compat/endian.h b/src/compat/endian.h index 26955a057d..c7469ba0be 100644 --- a/src/compat/endian.h +++ b/src/compat/endian.h @@ -5,237 +5,70 @@ #ifndef BITCOIN_COMPAT_ENDIAN_H #define BITCOIN_COMPAT_ENDIAN_H -#if defined(HAVE_CONFIG_H) -#include -#endif - #include -#include +#include +#include -#if defined(HAVE_ENDIAN_H) -#include -#elif defined(HAVE_SYS_ENDIAN_H) -#include -#endif - -#ifndef HAVE_CONFIG_H -// While not technically a supported configuration, defaulting to defining these -// DECLs when we were compiled without autotools makes it easier for other build -// systems to build things like libdashconsensus for strange targets. -#ifdef htobe16 -#define HAVE_DECL_HTOBE16 1 -#endif -#ifdef htole16 -#define HAVE_DECL_HTOLE16 1 -#endif -#ifdef be16toh -#define HAVE_DECL_BE16TOH 1 -#endif -#ifdef le16toh -#define HAVE_DECL_LE16TOH 1 -#endif - -#ifdef htobe32 -#define HAVE_DECL_HTOBE32 1 -#endif -#ifdef htole32 -#define HAVE_DECL_HTOLE32 1 -#endif -#ifdef be32toh -#define HAVE_DECL_BE32TOH 1 -#endif -#ifdef le32toh -#define HAVE_DECL_LE32TOH 1 -#endif - -#ifdef htobe64 -#define HAVE_DECL_HTOBE64 1 -#endif -#ifdef htole64 -#define HAVE_DECL_HTOLE64 1 -#endif -#ifdef be64toh -#define HAVE_DECL_BE64TOH 1 -#endif -#ifdef le64toh -#define HAVE_DECL_LE64TOH 1 -#endif - -#endif // HAVE_CONFIG_H - -#if defined(WORDS_BIGENDIAN) - -#if HAVE_DECL_HTOBE16 == 0 -inline uint16_t htobe16(uint16_t host_16bits) +inline BSWAP_CONSTEXPR uint16_t htobe16_internal(uint16_t host_16bits) { - return host_16bits; + if constexpr (std::endian::native == std::endian::little) return internal_bswap_16(host_16bits); + else return host_16bits; } -#endif // HAVE_DECL_HTOBE16 - -#if HAVE_DECL_HTOLE16 == 0 -inline uint16_t htole16(uint16_t host_16bits) +inline BSWAP_CONSTEXPR uint16_t htole16_internal(uint16_t host_16bits) { - return bswap_16(host_16bits); + if constexpr (std::endian::native == std::endian::big) return internal_bswap_16(host_16bits); + else return host_16bits; } -#endif // HAVE_DECL_HTOLE16 - -#if HAVE_DECL_BE16TOH == 0 -inline uint16_t be16toh(uint16_t big_endian_16bits) +inline BSWAP_CONSTEXPR uint16_t be16toh_internal(uint16_t big_endian_16bits) { - return big_endian_16bits; + if constexpr (std::endian::native == std::endian::little) return internal_bswap_16(big_endian_16bits); + else return big_endian_16bits; } -#endif // HAVE_DECL_BE16TOH - -#if HAVE_DECL_LE16TOH == 0 -inline uint16_t le16toh(uint16_t little_endian_16bits) +inline BSWAP_CONSTEXPR uint16_t le16toh_internal(uint16_t little_endian_16bits) { - return bswap_16(little_endian_16bits); + if constexpr (std::endian::native == std::endian::big) return internal_bswap_16(little_endian_16bits); + else return little_endian_16bits; } -#endif // HAVE_DECL_LE16TOH - -#if HAVE_DECL_HTOBE32 == 0 -inline uint32_t htobe32(uint32_t host_32bits) +inline BSWAP_CONSTEXPR uint32_t htobe32_internal(uint32_t host_32bits) { - return host_32bits; + if constexpr (std::endian::native == std::endian::little) return internal_bswap_32(host_32bits); + else return host_32bits; } -#endif // HAVE_DECL_HTOBE32 - -#if HAVE_DECL_HTOLE32 == 0 -inline uint32_t htole32(uint32_t host_32bits) +inline BSWAP_CONSTEXPR uint32_t htole32_internal(uint32_t host_32bits) { - return bswap_32(host_32bits); + if constexpr (std::endian::native == std::endian::big) return internal_bswap_32(host_32bits); + else return host_32bits; } -#endif // HAVE_DECL_HTOLE32 - -#if HAVE_DECL_BE32TOH == 0 -inline uint32_t be32toh(uint32_t big_endian_32bits) +inline BSWAP_CONSTEXPR uint32_t be32toh_internal(uint32_t big_endian_32bits) { - return big_endian_32bits; + if constexpr (std::endian::native == std::endian::little) return internal_bswap_32(big_endian_32bits); + else return big_endian_32bits; } -#endif // HAVE_DECL_BE32TOH - -#if HAVE_DECL_LE32TOH == 0 -inline uint32_t le32toh(uint32_t little_endian_32bits) +inline BSWAP_CONSTEXPR uint32_t le32toh_internal(uint32_t little_endian_32bits) { - return bswap_32(little_endian_32bits); + if constexpr (std::endian::native == std::endian::big) return internal_bswap_32(little_endian_32bits); + else return little_endian_32bits; } -#endif // HAVE_DECL_LE32TOH - -#if HAVE_DECL_HTOBE64 == 0 -inline uint64_t htobe64(uint64_t host_64bits) +inline BSWAP_CONSTEXPR uint64_t htobe64_internal(uint64_t host_64bits) { - return host_64bits; + if constexpr (std::endian::native == std::endian::little) return internal_bswap_64(host_64bits); + else return host_64bits; } -#endif // HAVE_DECL_HTOBE64 - -#if HAVE_DECL_HTOLE64 == 0 -inline uint64_t htole64(uint64_t host_64bits) +inline BSWAP_CONSTEXPR uint64_t htole64_internal(uint64_t host_64bits) { - return bswap_64(host_64bits); + if constexpr (std::endian::native == std::endian::big) return internal_bswap_64(host_64bits); + else return host_64bits; } -#endif // HAVE_DECL_HTOLE64 - -#if HAVE_DECL_BE64TOH == 0 -inline uint64_t be64toh(uint64_t big_endian_64bits) +inline BSWAP_CONSTEXPR uint64_t be64toh_internal(uint64_t big_endian_64bits) { - return big_endian_64bits; + if constexpr (std::endian::native == std::endian::little) return internal_bswap_64(big_endian_64bits); + else return big_endian_64bits; } -#endif // HAVE_DECL_BE64TOH - -#if HAVE_DECL_LE64TOH == 0 -inline uint64_t le64toh(uint64_t little_endian_64bits) +inline BSWAP_CONSTEXPR uint64_t le64toh_internal(uint64_t little_endian_64bits) { - return bswap_64(little_endian_64bits); + if constexpr (std::endian::native == std::endian::big) return internal_bswap_64(little_endian_64bits); + else return little_endian_64bits; } -#endif // HAVE_DECL_LE64TOH - -#else // WORDS_BIGENDIAN - -#if HAVE_DECL_HTOBE16 == 0 -inline uint16_t htobe16(uint16_t host_16bits) -{ - return bswap_16(host_16bits); -} -#endif // HAVE_DECL_HTOBE16 - -#if HAVE_DECL_HTOLE16 == 0 -inline uint16_t htole16(uint16_t host_16bits) -{ - return host_16bits; -} -#endif // HAVE_DECL_HTOLE16 - -#if HAVE_DECL_BE16TOH == 0 -inline uint16_t be16toh(uint16_t big_endian_16bits) -{ - return bswap_16(big_endian_16bits); -} -#endif // HAVE_DECL_BE16TOH - -#if HAVE_DECL_LE16TOH == 0 -inline uint16_t le16toh(uint16_t little_endian_16bits) -{ - return little_endian_16bits; -} -#endif // HAVE_DECL_LE16TOH - -#if HAVE_DECL_HTOBE32 == 0 -inline uint32_t htobe32(uint32_t host_32bits) -{ - return bswap_32(host_32bits); -} -#endif // HAVE_DECL_HTOBE32 - -#if HAVE_DECL_HTOLE32 == 0 -inline uint32_t htole32(uint32_t host_32bits) -{ - return host_32bits; -} -#endif // HAVE_DECL_HTOLE32 - -#if HAVE_DECL_BE32TOH == 0 -inline uint32_t be32toh(uint32_t big_endian_32bits) -{ - return bswap_32(big_endian_32bits); -} -#endif // HAVE_DECL_BE32TOH - -#if HAVE_DECL_LE32TOH == 0 -inline uint32_t le32toh(uint32_t little_endian_32bits) -{ - return little_endian_32bits; -} -#endif // HAVE_DECL_LE32TOH - -#if HAVE_DECL_HTOBE64 == 0 -inline uint64_t htobe64(uint64_t host_64bits) -{ - return bswap_64(host_64bits); -} -#endif // HAVE_DECL_HTOBE64 - -#if HAVE_DECL_HTOLE64 == 0 -inline uint64_t htole64(uint64_t host_64bits) -{ - return host_64bits; -} -#endif // HAVE_DECL_HTOLE64 - -#if HAVE_DECL_BE64TOH == 0 -inline uint64_t be64toh(uint64_t big_endian_64bits) -{ - return bswap_64(big_endian_64bits); -} -#endif // HAVE_DECL_BE64TOH - -#if HAVE_DECL_LE64TOH == 0 -inline uint64_t le64toh(uint64_t little_endian_64bits) -{ - return little_endian_64bits; -} -#endif // HAVE_DECL_LE64TOH - -#endif // WORDS_BIGENDIAN #endif // BITCOIN_COMPAT_ENDIAN_H diff --git a/src/compat/stdin.cpp b/src/compat/stdin.cpp index b6975a4cd3..40fa14066c 100644 --- a/src/compat/stdin.cpp +++ b/src/compat/stdin.cpp @@ -2,23 +2,19 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#if defined(HAVE_CONFIG_H) -#include -#endif +#include -#include // for fileno(), stdin +#include #ifdef WIN32 -#include // for SetStdinEcho() -#include // for isatty() +#include +#include #else -#include // for SetStdinEcho() -#include // for SetStdinEcho(), isatty() -#include // for StdinReady() +#include +#include +#include #endif -#include - // https://stackoverflow.com/questions/1413445/reading-a-password-from-stdcin void SetStdinEcho(bool enable) { diff --git a/src/crypto/chacha20.h b/src/crypto/chacha20.h index b5ad5d2b8b..666002fee7 100644 --- a/src/crypto/chacha20.h +++ b/src/crypto/chacha20.h @@ -9,8 +9,8 @@ #include #include +#include #include -#include #include // classes for ChaCha20 256-bit stream cipher developed by Daniel J. Bernstein diff --git a/src/crypto/common.h b/src/crypto/common.h index dc12ed9942..1dc4f3f55c 100644 --- a/src/crypto/common.h +++ b/src/crypto/common.h @@ -5,106 +5,81 @@ #ifndef BITCOIN_CRYPTO_COMMON_H #define BITCOIN_CRYPTO_COMMON_H -#if defined(HAVE_CONFIG_H) -#include -#endif - -#include -#include - #include +#include +#include + uint16_t static inline ReadLE16(const unsigned char* ptr) { uint16_t x; - memcpy((char*)&x, ptr, 2); - return le16toh(x); + memcpy(&x, ptr, 2); + return le16toh_internal(x); } uint32_t static inline ReadLE32(const unsigned char* ptr) { uint32_t x; - memcpy((char*)&x, ptr, 4); - return le32toh(x); + memcpy(&x, ptr, 4); + return le32toh_internal(x); } uint64_t static inline ReadLE64(const unsigned char* ptr) { uint64_t x; - memcpy((char*)&x, ptr, 8); - return le64toh(x); + memcpy(&x, ptr, 8); + return le64toh_internal(x); } void static inline WriteLE16(unsigned char* ptr, uint16_t x) { - uint16_t v = htole16(x); - memcpy(ptr, (char*)&v, 2); + uint16_t v = htole16_internal(x); + memcpy(ptr, &v, 2); } void static inline WriteLE32(unsigned char* ptr, uint32_t x) { - uint32_t v = htole32(x); - memcpy(ptr, (char*)&v, 4); + uint32_t v = htole32_internal(x); + memcpy(ptr, &v, 4); } void static inline WriteLE64(unsigned char* ptr, uint64_t x) { - uint64_t v = htole64(x); - memcpy(ptr, (char*)&v, 8); + uint64_t v = htole64_internal(x); + memcpy(ptr, &v, 8); } uint16_t static inline ReadBE16(const unsigned char* ptr) { uint16_t x; - memcpy((char*)&x, ptr, 2); - return be16toh(x); + memcpy(&x, ptr, 2); + return be16toh_internal(x); } uint32_t static inline ReadBE32(const unsigned char* ptr) { uint32_t x; - memcpy((char*)&x, ptr, 4); - return be32toh(x); + memcpy(&x, ptr, 4); + return be32toh_internal(x); } uint64_t static inline ReadBE64(const unsigned char* ptr) { uint64_t x; - memcpy((char*)&x, ptr, 8); - return be64toh(x); + memcpy(&x, ptr, 8); + return be64toh_internal(x); } void static inline WriteBE32(unsigned char* ptr, uint32_t x) { - uint32_t v = htobe32(x); - memcpy(ptr, (char*)&v, 4); + uint32_t v = htobe32_internal(x); + memcpy(ptr, &v, 4); } void static inline WriteBE64(unsigned char* ptr, uint64_t x) { - uint64_t v = htobe64(x); - memcpy(ptr, (char*)&v, 8); -} - -/** Return the smallest number n such that (x >> n) == 0 (or 64 if the highest bit in x is set. */ -uint64_t static inline CountBits(uint64_t x) -{ -#if HAVE_BUILTIN_CLZL - if (sizeof(unsigned long) >= sizeof(uint64_t)) { - return x ? 8 * sizeof(unsigned long) - __builtin_clzl(x) : 0; - } -#endif -#if HAVE_BUILTIN_CLZLL - if (sizeof(unsigned long long) >= sizeof(uint64_t)) { - return x ? 8 * sizeof(unsigned long long) - __builtin_clzll(x) : 0; - } -#endif - int ret = 0; - while (x) { - x >>= 1; - ++ret; - } - return ret; + uint64_t v = htobe64_internal(x); + memcpy(ptr, &v, 8); } #endif // BITCOIN_CRYPTO_COMMON_H diff --git a/src/crypto/hkdf_sha256_32.h b/src/crypto/hkdf_sha256_32.h index fa1e42aec1..878b03a37f 100644 --- a/src/crypto/hkdf_sha256_32.h +++ b/src/crypto/hkdf_sha256_32.h @@ -7,8 +7,8 @@ #include +#include #include -#include /** A rfc5869 HKDF implementation with HMAC_SHA256 and fixed key output length of 32 bytes (L=32) */ class CHKDF_HMAC_SHA256_L32 diff --git a/src/crypto/hmac_sha256.h b/src/crypto/hmac_sha256.h index 40d40f9fb2..f167961ae3 100644 --- a/src/crypto/hmac_sha256.h +++ b/src/crypto/hmac_sha256.h @@ -7,8 +7,8 @@ #include +#include #include -#include /** A hasher class for HMAC-SHA-256. */ class CHMAC_SHA256 diff --git a/src/crypto/hmac_sha512.h b/src/crypto/hmac_sha512.h index 6d3fd59f27..582064a33f 100644 --- a/src/crypto/hmac_sha512.h +++ b/src/crypto/hmac_sha512.h @@ -7,8 +7,8 @@ #include +#include #include -#include /** A hasher class for HMAC-SHA-512. */ class CHMAC_SHA512 diff --git a/src/crypto/pkcs5_pbkdf2_hmac_sha512.h b/src/crypto/pkcs5_pbkdf2_hmac_sha512.h index 3974bf7f4f..dbf9203a7a 100644 --- a/src/crypto/pkcs5_pbkdf2_hmac_sha512.h +++ b/src/crypto/pkcs5_pbkdf2_hmac_sha512.h @@ -5,8 +5,8 @@ #ifndef BITCOIN_CRYPTO_PKCS5_PBKDF2_HMAC_SHA512_H #define BITCOIN_CRYPTO_PKCS5_PBKDF2_HMAC_SHA512_H +#include #include -#include /** A rfc2898 implementation of PKCS#5 v2.0 password based encryption key * derivation function PBKDF2 with HMAC_SHA512. This implementation is diff --git a/src/crypto/poly1305.h b/src/crypto/poly1305.h index 1f42f9c8f9..609ff1913b 100644 --- a/src/crypto/poly1305.h +++ b/src/crypto/poly1305.h @@ -8,8 +8,8 @@ #include #include +#include #include -#include #define POLY1305_BLOCK_SIZE 16 diff --git a/src/crypto/ripemd160.h b/src/crypto/ripemd160.h index bd41f02508..025121f24a 100644 --- a/src/crypto/ripemd160.h +++ b/src/crypto/ripemd160.h @@ -5,8 +5,8 @@ #ifndef BITCOIN_CRYPTO_RIPEMD160_H #define BITCOIN_CRYPTO_RIPEMD160_H +#include #include -#include /** A hasher class for RIPEMD-160. */ class CRIPEMD160 diff --git a/src/crypto/sha1.h b/src/crypto/sha1.h index 8fb20810be..f1b7dffab4 100644 --- a/src/crypto/sha1.h +++ b/src/crypto/sha1.h @@ -5,8 +5,8 @@ #ifndef BITCOIN_CRYPTO_SHA1_H #define BITCOIN_CRYPTO_SHA1_H +#include #include -#include /** A hasher class for SHA1. */ class CSHA1 diff --git a/src/crypto/sha256.h b/src/crypto/sha256.h index 4d6812cc8c..335b123687 100644 --- a/src/crypto/sha256.h +++ b/src/crypto/sha256.h @@ -5,8 +5,8 @@ #ifndef BITCOIN_CRYPTO_SHA256_H #define BITCOIN_CRYPTO_SHA256_H +#include #include -#include #include /** A hasher class for SHA-256. */ diff --git a/src/crypto/sha256_sse4.cpp b/src/crypto/sha256_sse4.cpp index 6504a1f12e..aadb9de016 100644 --- a/src/crypto/sha256_sse4.cpp +++ b/src/crypto/sha256_sse4.cpp @@ -5,8 +5,8 @@ // This is a translation to GCC extended asm syntax from YASM code by Intel // (available at the bottom of this file). +#include #include -#include #if defined(__x86_64__) || defined(__amd64__) diff --git a/src/crypto/sha3.h b/src/crypto/sha3.h index 88d8c1204d..78608eae76 100644 --- a/src/crypto/sha3.h +++ b/src/crypto/sha3.h @@ -7,8 +7,8 @@ #include +#include #include -#include //! The Keccak-f[1600] transform. void KeccakF(uint64_t (&st)[25]); diff --git a/src/crypto/sha512.h b/src/crypto/sha512.h index 21ca930c75..7356dff6d9 100644 --- a/src/crypto/sha512.h +++ b/src/crypto/sha512.h @@ -5,8 +5,8 @@ #ifndef BITCOIN_CRYPTO_SHA512_H #define BITCOIN_CRYPTO_SHA512_H +#include #include -#include /** A hasher class for SHA-512. */ class CSHA512 diff --git a/src/dbwrapper.h b/src/dbwrapper.h index f9247c1df9..f1153e548d 100644 --- a/src/dbwrapper.h +++ b/src/dbwrapper.h @@ -21,6 +21,8 @@ static const size_t DBWRAPPER_PREALLOC_KEY_SIZE = 64; static const size_t DBWRAPPER_PREALLOC_VALUE_SIZE = 1024; +inline auto CharCast(const std::byte* data) { return reinterpret_cast(data); } + class dbwrapper_error : public std::runtime_error { public: @@ -83,12 +85,12 @@ public: template void Write(const CDataStream& _ssKey, const V& value) { - leveldb::Slice slKey((const char*)_ssKey.data(), _ssKey.size()); + leveldb::Slice slKey(CharCast(_ssKey.data()), _ssKey.size()); ssValue.reserve(DBWRAPPER_PREALLOC_VALUE_SIZE); ssValue << value; ssValue.Xor(dbwrapper_private::GetObfuscateKey(parent)); - leveldb::Slice slValue((const char*)ssValue.data(), ssValue.size()); + leveldb::Slice slValue(CharCast(ssValue.data()), ssValue.size()); batch.Put(slKey, slValue); // - varint: key length (1 byte up to 127B, 2 bytes up to 16383B, ...) @@ -110,7 +112,7 @@ public: } void Erase(const CDataStream& _ssKey) { - leveldb::Slice slKey((const char*)_ssKey.data(), _ssKey.size()); + leveldb::Slice slKey(CharCast(_ssKey.data()), _ssKey.size()); batch.Delete(slKey); // - byte: header @@ -151,7 +153,7 @@ public: } void Seek(const CDataStream& ssKey) { - leveldb::Slice slKey((const char*)ssKey.data(), ssKey.size()); + leveldb::Slice slKey(CharCast(ssKey.data()), ssKey.size()); piter->Seek(slKey); } @@ -259,7 +261,7 @@ public: bool ReadDataStream(const CDataStream& ssKey, CDataStream& ssValue) const { - leveldb::Slice slKey((const char*)ssKey.data(), ssKey.size()); + leveldb::Slice slKey(CharCast(ssKey.data()), ssKey.size()); std::string strValue; leveldb::Status status = pdb->Get(readoptions, slKey, &strValue); @@ -319,7 +321,7 @@ public: bool Exists(const CDataStream& key) const { - leveldb::Slice slKey((const char*)key.data(), key.size()); + leveldb::Slice slKey(CharCast(key.data()), key.size()); std::string strValue; leveldb::Status status = pdb->Get(readoptions, slKey, &strValue); @@ -363,8 +365,8 @@ public: ssKey2.reserve(DBWRAPPER_PREALLOC_KEY_SIZE); ssKey1 << key_begin; ssKey2 << key_end; - leveldb::Slice slKey1((const char*)ssKey1.data(), ssKey1.size()); - leveldb::Slice slKey2((const char*)ssKey2.data(), ssKey2.size()); + leveldb::Slice slKey1(CharCast(ssKey1.data()), ssKey1.size()); + leveldb::Slice slKey2(CharCast(ssKey2.data()), ssKey2.size()); uint64_t size = 0; leveldb::Range range(slKey1, slKey2); pdb->GetApproximateSizes(&range, 1, &size); @@ -382,8 +384,8 @@ public: ssKey2.reserve(DBWRAPPER_PREALLOC_KEY_SIZE); ssKey1 << key_begin; ssKey2 << key_end; - leveldb::Slice slKey1((const char*)ssKey1.data(), ssKey1.size()); - leveldb::Slice slKey2((const char*)ssKey2.data(), ssKey2.size()); + leveldb::Slice slKey1(CharCast(ssKey1.data()), ssKey1.size()); + leveldb::Slice slKey2(CharCast(ssKey2.data()), ssKey2.size()); pdb->CompactRange(&slKey1, &slKey2); } diff --git a/src/httpserver.cpp b/src/httpserver.cpp index b4b263eb50..cb00268751 100644 --- a/src/httpserver.cpp +++ b/src/httpserver.cpp @@ -19,8 +19,8 @@ #include #include +#include #include -#include #include #include diff --git a/src/i2p.cpp b/src/i2p.cpp index 8ecb50645f..5ad1cfc8f4 100644 --- a/src/i2p.cpp +++ b/src/i2p.cpp @@ -355,7 +355,7 @@ Binary Session::MyDestination() const uint16_t cert_len; memcpy(&cert_len, &m_private_key.at(CERT_LEN_POS), sizeof(cert_len)); - cert_len = be16toh(cert_len); + cert_len = be16toh_internal(cert_len); const size_t dest_len = DEST_LEN_BASE + cert_len; diff --git a/src/ipc/interfaces.cpp b/src/ipc/interfaces.cpp index 580590fde9..ee0d4123ce 100644 --- a/src/ipc/interfaces.cpp +++ b/src/ipc/interfaces.cpp @@ -12,11 +12,11 @@ #include #include +#include +#include #include #include #include -#include -#include #include #include #include diff --git a/src/ipc/process.cpp b/src/ipc/process.cpp index 9036b80c45..9474ad2c4a 100644 --- a/src/ipc/process.cpp +++ b/src/ipc/process.cpp @@ -10,10 +10,10 @@ #include #include +#include #include #include #include -#include #include #include #include diff --git a/src/memusage.h b/src/memusage.h index 5fffe4ec07..cda5b1d1f3 100644 --- a/src/memusage.h +++ b/src/memusage.h @@ -9,9 +9,8 @@ #include #include -#include - #include +#include #include #include #include diff --git a/src/prevector.h b/src/prevector.h index fd65c243ab..81dc49978b 100644 --- a/src/prevector.h +++ b/src/prevector.h @@ -6,12 +6,12 @@ #define BITCOIN_PREVECTOR_H #include -#include #include #include #include #include +#include #include #include @@ -47,26 +47,28 @@ public: typedef const value_type* const_pointer; class iterator { - T* ptr; + T* ptr{}; public: typedef Diff difference_type; typedef T value_type; typedef T* pointer; typedef T& reference; - typedef std::random_access_iterator_tag iterator_category; + using element_type = T; + using iterator_category = std::contiguous_iterator_tag; + iterator() = default; iterator(T* ptr_) : ptr(ptr_) {} T& operator*() const { return *ptr; } T* operator->() const { return ptr; } - T& operator[](size_type pos) { return ptr[pos]; } - const T& operator[](size_type pos) const { return ptr[pos]; } + T& operator[](size_type pos) const { return ptr[pos]; } iterator& operator++() { ptr++; return *this; } iterator& operator--() { ptr--; return *this; } iterator operator++(int) { iterator copy(*this); ++(*this); return copy; } iterator operator--(int) { iterator copy(*this); --(*this); return copy; } difference_type friend operator-(iterator a, iterator b) { return (&(*a) - &(*b)); } - iterator operator+(size_type n) { return iterator(ptr + n); } + iterator operator+(size_type n) const { return iterator(ptr + n); } + iterator friend operator+(size_type n, iterator x) { return x + n; } iterator& operator+=(size_type n) { ptr += n; return *this; } - iterator operator-(size_type n) { return iterator(ptr - n); } + iterator operator-(size_type n) const { return iterator(ptr - n); } iterator& operator-=(size_type n) { ptr -= n; return *this; } bool operator==(iterator x) const { return ptr == x.ptr; } bool operator!=(iterator x) const { return ptr != x.ptr; } @@ -77,18 +79,17 @@ public: }; class reverse_iterator { - T* ptr; + T* ptr{}; public: typedef Diff difference_type; typedef T value_type; typedef T* pointer; typedef T& reference; typedef std::bidirectional_iterator_tag iterator_category; + reverse_iterator() = default; reverse_iterator(T* ptr_) : ptr(ptr_) {} - T& operator*() { return *ptr; } - const T& operator*() const { return *ptr; } - T* operator->() { return ptr; } - const T* operator->() const { return ptr; } + T& operator*() const { return *ptr; } + T* operator->() const { return ptr; } reverse_iterator& operator--() { ptr++; return *this; } reverse_iterator& operator++() { ptr--; return *this; } reverse_iterator operator++(int) { reverse_iterator copy(*this); ++(*this); return copy; } @@ -98,13 +99,15 @@ public: }; class const_iterator { - const T* ptr; + const T* ptr{}; public: typedef Diff difference_type; typedef const T value_type; typedef const T* pointer; typedef const T& reference; - typedef std::random_access_iterator_tag iterator_category; + using element_type = const T; + using iterator_category = std::contiguous_iterator_tag; + const_iterator() = default; const_iterator(const T* ptr_) : ptr(ptr_) {} const_iterator(iterator x) : ptr(&(*x)) {} const T& operator*() const { return *ptr; } @@ -115,9 +118,10 @@ public: const_iterator operator++(int) { const_iterator copy(*this); ++(*this); return copy; } const_iterator operator--(int) { const_iterator copy(*this); --(*this); return copy; } difference_type friend operator-(const_iterator a, const_iterator b) { return (&(*a) - &(*b)); } - const_iterator operator+(size_type n) { return const_iterator(ptr + n); } + const_iterator operator+(size_type n) const { return const_iterator(ptr + n); } + const_iterator friend operator+(size_type n, const_iterator x) { return x + n; } const_iterator& operator+=(size_type n) { ptr += n; return *this; } - const_iterator operator-(size_type n) { return const_iterator(ptr - n); } + const_iterator operator-(size_type n) const { return const_iterator(ptr - n); } const_iterator& operator-=(size_type n) { ptr -= n; return *this; } bool operator==(const_iterator x) const { return ptr == x.ptr; } bool operator!=(const_iterator x) const { return ptr != x.ptr; } @@ -128,13 +132,14 @@ public: }; class const_reverse_iterator { - const T* ptr; + const T* ptr{}; public: typedef Diff difference_type; typedef const T value_type; typedef const T* pointer; typedef const T& reference; typedef std::bidirectional_iterator_tag iterator_category; + const_reverse_iterator() = default; const_reverse_iterator(const T* ptr_) : ptr(ptr_) {} const_reverse_iterator(reverse_iterator x) : ptr(&(*x)) {} const T& operator*() const { return *ptr; } diff --git a/src/qt/utilitydialog.cpp b/src/qt/utilitydialog.cpp index 83743878b3..785e0d7bf3 100644 --- a/src/qt/utilitydialog.cpp +++ b/src/qt/utilitydialog.cpp @@ -18,7 +18,7 @@ #include #include -#include +#include #include #include diff --git a/src/random.cpp b/src/random.cpp index e98b068d3c..c2487588c0 100644 --- a/src/random.cpp +++ b/src/random.cpp @@ -19,7 +19,7 @@ #include #include -#include +#include #include #ifdef WIN32 diff --git a/src/random.h b/src/random.h index 7f3b903622..3060c99c3d 100644 --- a/src/random.h +++ b/src/random.h @@ -11,6 +11,7 @@ #include #include +#include #include #include // For std::chrono::microseconds #include @@ -196,7 +197,7 @@ public: { assert(range); --range; - int bits = CountBits(range); + int bits = std::bit_width(range); while (true) { uint64_t ret = randbits(bits); if (ret <= range) return ret; diff --git a/src/serialize.h b/src/serialize.h index c8b57ad482..b822951e00 100644 --- a/src/serialize.h +++ b/src/serialize.h @@ -60,27 +60,27 @@ template inline void ser_writedata8(Stream &s, uint8_t obj) } template inline void ser_writedata16(Stream &s, uint16_t obj) { - obj = htole16(obj); + obj = htole16_internal(obj); s.write(AsBytes(Span{&obj, 1})); } template inline void ser_writedata16be(Stream &s, uint16_t obj) { - obj = htobe16(obj); + obj = htobe16_internal(obj); s.write(AsBytes(Span{&obj, 1})); } template inline void ser_writedata32(Stream &s, uint32_t obj) { - obj = htole32(obj); + obj = htole32_internal(obj); s.write(AsBytes(Span{&obj, 1})); } template inline void ser_writedata32be(Stream &s, uint32_t obj) { - obj = htobe32(obj); + obj = htobe32_internal(obj); s.write(AsBytes(Span{&obj, 1})); } template inline void ser_writedata64(Stream &s, uint64_t obj) { - obj = htole64(obj); + obj = htole64_internal(obj); s.write(AsBytes(Span{&obj, 1})); } template inline uint8_t ser_readdata8(Stream &s) @@ -93,31 +93,31 @@ template inline uint16_t ser_readdata16(Stream &s) { uint16_t obj; s.read(AsWritableBytes(Span{&obj, 1})); - return le16toh(obj); + return le16toh_internal(obj); } template inline uint16_t ser_readdata16be(Stream &s) { uint16_t obj; s.read(AsWritableBytes(Span{&obj, 1})); - return be16toh(obj); + return be16toh_internal(obj); } template inline uint32_t ser_readdata32(Stream &s) { uint32_t obj; s.read(AsWritableBytes(Span{&obj, 1})); - return le32toh(obj); + return le32toh_internal(obj); } template inline uint32_t ser_readdata32be(Stream &s) { uint32_t obj; s.read(AsWritableBytes(Span{&obj, 1})); - return be32toh(obj); + return be32toh_internal(obj); } template inline uint64_t ser_readdata64(Stream &s) { uint64_t obj; s.read(AsWritableBytes(Span{&obj, 1})); - return le64toh(obj); + return le64toh_internal(obj); } @@ -655,11 +655,11 @@ struct CustomUintFormatter { if (v < 0 || v > MAX) throw std::ios_base::failure("CustomUintFormatter value out of range"); if (BigEndian) { - uint64_t raw = htobe64(v); - s.write({BytePtr(&raw) + 8 - Bytes, Bytes}); + uint64_t raw = htobe64_internal(v); + s.write(AsBytes(Span{&raw, 1}).last(Bytes)); } else { - uint64_t raw = htole64(v); - s.write({BytePtr(&raw), Bytes}); + uint64_t raw = htole64_internal(v); + s.write(AsBytes(Span{&raw, 1}).first(Bytes)); } } @@ -669,11 +669,11 @@ struct CustomUintFormatter static_assert(std::numeric_limits::max() >= MAX && std::numeric_limits::min() <= 0, "Assigned type too small"); uint64_t raw = 0; if (BigEndian) { - s.read({BytePtr(&raw) + 8 - Bytes, Bytes}); - v = static_cast(be64toh(raw)); + s.read(AsWritableBytes(Span{&raw, 1}).last(Bytes)); + v = static_cast(be64toh_internal(raw)); } else { - s.read({BytePtr(&raw), Bytes}); - v = static_cast(le64toh(raw)); + s.read(AsWritableBytes(Span{&raw, 1}).first(Bytes)); + v = static_cast(le64toh_internal(raw)); } } }; diff --git a/src/span.h b/src/span.h index a7ebd48cc7..1671c1805a 100644 --- a/src/span.h +++ b/src/span.h @@ -5,10 +5,10 @@ #ifndef BITCOIN_SPAN_H #define BITCOIN_SPAN_H -#include -#include #include -#include +#include +#include +#include #ifdef DEBUG_CORE #define CONSTEXPR_IF_NOT_DEBUG @@ -243,21 +243,16 @@ T& SpanPopBack(Span& span) return back; } -//! Convert a data pointer to a std::byte data pointer. -//! Where possible, please use the safer AsBytes helpers. -inline const std::byte* BytePtr(const void* data) { return reinterpret_cast(data); } -inline std::byte* BytePtr(void* data) { return reinterpret_cast(data); } - // From C++20 as_bytes and as_writeable_bytes template Span AsBytes(Span s) noexcept { - return {BytePtr(s.data()), s.size_bytes()}; + return {reinterpret_cast(s.data()), s.size_bytes()}; } template Span AsWritableBytes(Span s) noexcept { - return {BytePtr(s.data()), s.size_bytes()}; + return {reinterpret_cast(s.data()), s.size_bytes()}; } template @@ -272,10 +267,10 @@ Span MakeWritableByteSpan(V&& v) noexcept } // Helper functions to safely cast to unsigned char pointers. -inline unsigned char* UCharCast(char* c) { return (unsigned char*)c; } +inline unsigned char* UCharCast(char* c) { return reinterpret_cast(c); } inline unsigned char* UCharCast(unsigned char* c) { return c; } -inline unsigned char* UCharCast(std::byte* c) { return (unsigned char*)c; } -inline const unsigned char* UCharCast(const char* c) { return (unsigned char*)c; } +inline unsigned char* UCharCast(std::byte* c) { return reinterpret_cast(c); } +inline const unsigned char* UCharCast(const char* c) { return reinterpret_cast(c); } inline const unsigned char* UCharCast(const unsigned char* c) { return c; } inline const unsigned char* UCharCast(const std::byte* c) { return reinterpret_cast(c); } diff --git a/src/streams.h b/src/streams.h index 76feda569f..fc0f9b633c 100644 --- a/src/streams.h +++ b/src/streams.h @@ -13,11 +13,11 @@ #include #include +#include #include #include #include #include -#include #include #include #include diff --git a/src/support/cleanse.h b/src/support/cleanse.h index 8c1210a114..b1227770c7 100644 --- a/src/support/cleanse.h +++ b/src/support/cleanse.h @@ -6,7 +6,7 @@ #ifndef BITCOIN_SUPPORT_CLEANSE_H #define BITCOIN_SUPPORT_CLEANSE_H -#include +#include /** Secure overwrite a buffer (possibly containing secret data) with zero-bytes. The write * operation will not be optimized out by the compiler. */ diff --git a/src/test/argsman_tests.cpp b/src/test/argsman_tests.cpp new file mode 100644 index 0000000000..8af6081eec --- /dev/null +++ b/src/test/argsman_tests.cpp @@ -0,0 +1,1043 @@ +// Copyright (c) 2011-2022 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 +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +BOOST_FIXTURE_TEST_SUITE(argsman_tests, BasicTestingSetup) + +BOOST_AUTO_TEST_CASE(util_datadir) +{ + // Use local args variable instead of m_args to avoid making assumptions about test setup + ArgsManager args; + args.ForceSetArg("-datadir", fs::PathToString(m_path_root)); + + const fs::path dd_norm = args.GetDataDirBase(); + + args.ForceSetArg("-datadir", fs::PathToString(dd_norm) + "/"); + args.ClearPathCache(); + BOOST_CHECK_EQUAL(dd_norm, args.GetDataDirBase()); + + args.ForceSetArg("-datadir", fs::PathToString(dd_norm) + "/."); + args.ClearPathCache(); + BOOST_CHECK_EQUAL(dd_norm, args.GetDataDirBase()); + + args.ForceSetArg("-datadir", fs::PathToString(dd_norm) + "/./"); + args.ClearPathCache(); + BOOST_CHECK_EQUAL(dd_norm, args.GetDataDirBase()); + + args.ForceSetArg("-datadir", fs::PathToString(dd_norm) + "/.//"); + args.ClearPathCache(); + BOOST_CHECK_EQUAL(dd_norm, args.GetDataDirBase()); +} + +struct TestArgsManager : public ArgsManager +{ + TestArgsManager() { m_network_only_args.clear(); } + void ReadConfigString(const std::string str_config) + { + std::istringstream streamConfig(str_config); + { + LOCK(cs_args); + m_settings.ro_config.clear(); + m_config_sections.clear(); + } + std::string error; + BOOST_REQUIRE(ReadConfigStream(streamConfig, "", error)); + } + void SetNetworkOnlyArg(const std::string arg) + { + LOCK(cs_args); + m_network_only_args.insert(arg); + } + void SetupArgs(const std::vector>& args) + { + for (const auto& arg : args) { + AddArg(arg.first, "", arg.second, OptionsCategory::OPTIONS); + } + } + using ArgsManager::GetSetting; + using ArgsManager::GetSettingsList; + using ArgsManager::ReadConfigStream; + using ArgsManager::cs_args; + using ArgsManager::m_network; + using ArgsManager::m_settings; +}; + +//! Test GetSetting and GetArg type coercion, negation, and default value handling. +class CheckValueTest : public TestChain100Setup +{ +public: + struct Expect { + util::SettingsValue setting; + bool default_string = false; + bool default_int = false; + bool default_bool = false; + const char* string_value = nullptr; + std::optional int_value; + std::optional bool_value; + std::optional> list_value; + const char* error = nullptr; + + explicit Expect(util::SettingsValue s) : setting(std::move(s)) {} + Expect& DefaultString() { default_string = true; return *this; } + Expect& DefaultInt() { default_int = true; return *this; } + Expect& DefaultBool() { default_bool = true; return *this; } + Expect& String(const char* s) { string_value = s; return *this; } + Expect& Int(int64_t i) { int_value = i; return *this; } + Expect& Bool(bool b) { bool_value = b; return *this; } + Expect& List(std::vector m) { list_value = std::move(m); return *this; } + Expect& Error(const char* e) { error = e; return *this; } + }; + + void CheckValue(unsigned int flags, const char* arg, const Expect& expect) + { + TestArgsManager test; + test.SetupArgs({{"-value", flags}}); + const char* argv[] = {"ignored", arg}; + std::string error; + bool success = test.ParseParameters(arg ? 2 : 1, argv, error); + + BOOST_CHECK_EQUAL(test.GetSetting("-value").write(), expect.setting.write()); + auto settings_list = test.GetSettingsList("-value"); + if (expect.setting.isNull() || expect.setting.isFalse()) { + BOOST_CHECK_EQUAL(settings_list.size(), 0U); + } else { + BOOST_CHECK_EQUAL(settings_list.size(), 1U); + BOOST_CHECK_EQUAL(settings_list[0].write(), expect.setting.write()); + } + + if (expect.error) { + BOOST_CHECK(!success); + BOOST_CHECK_NE(error.find(expect.error), std::string::npos); + } else { + BOOST_CHECK(success); + BOOST_CHECK_EQUAL(error, ""); + } + + if (expect.default_string) { + BOOST_CHECK_EQUAL(test.GetArg("-value", "zzzzz"), "zzzzz"); + } else if (expect.string_value) { + BOOST_CHECK_EQUAL(test.GetArg("-value", "zzzzz"), expect.string_value); + } else { + BOOST_CHECK(!success); + } + + if (expect.default_int) { + BOOST_CHECK_EQUAL(test.GetArg("-value", 99999), 99999); + } else if (expect.int_value) { + BOOST_CHECK_EQUAL(test.GetArg("-value", 99999), *expect.int_value); + } else { + BOOST_CHECK(!success); + } + + if (expect.default_bool) { + BOOST_CHECK_EQUAL(test.GetBoolArg("-value", false), false); + BOOST_CHECK_EQUAL(test.GetBoolArg("-value", true), true); + } else if (expect.bool_value) { + BOOST_CHECK_EQUAL(test.GetBoolArg("-value", false), *expect.bool_value); + BOOST_CHECK_EQUAL(test.GetBoolArg("-value", true), *expect.bool_value); + } else { + BOOST_CHECK(!success); + } + + if (expect.list_value) { + auto l = test.GetArgs("-value"); + BOOST_CHECK_EQUAL_COLLECTIONS(l.begin(), l.end(), expect.list_value->begin(), expect.list_value->end()); + } else { + BOOST_CHECK(!success); + } + } +}; + +BOOST_FIXTURE_TEST_CASE(util_CheckValue, CheckValueTest) +{ + using M = ArgsManager; + + CheckValue(M::ALLOW_ANY, nullptr, Expect{{}}.DefaultString().DefaultInt().DefaultBool().List({})); + CheckValue(M::ALLOW_ANY, "-novalue", Expect{false}.String("0").Int(0).Bool(false).List({})); + CheckValue(M::ALLOW_ANY, "-novalue=", Expect{false}.String("0").Int(0).Bool(false).List({})); + CheckValue(M::ALLOW_ANY, "-novalue=0", Expect{true}.String("1").Int(1).Bool(true).List({"1"})); + CheckValue(M::ALLOW_ANY, "-novalue=1", Expect{false}.String("0").Int(0).Bool(false).List({})); + CheckValue(M::ALLOW_ANY, "-novalue=2", Expect{false}.String("0").Int(0).Bool(false).List({})); + CheckValue(M::ALLOW_ANY, "-novalue=abc", Expect{true}.String("1").Int(1).Bool(true).List({"1"})); + CheckValue(M::ALLOW_ANY, "-value", Expect{""}.String("").Int(0).Bool(true).List({""})); + CheckValue(M::ALLOW_ANY, "-value=", Expect{""}.String("").Int(0).Bool(true).List({""})); + CheckValue(M::ALLOW_ANY, "-value=0", Expect{"0"}.String("0").Int(0).Bool(false).List({"0"})); + CheckValue(M::ALLOW_ANY, "-value=1", Expect{"1"}.String("1").Int(1).Bool(true).List({"1"})); + CheckValue(M::ALLOW_ANY, "-value=2", Expect{"2"}.String("2").Int(2).Bool(true).List({"2"})); + CheckValue(M::ALLOW_ANY, "-value=abc", Expect{"abc"}.String("abc").Int(0).Bool(false).List({"abc"})); +} + +struct NoIncludeConfTest { + std::string Parse(const char* arg) + { + TestArgsManager test; + test.SetupArgs({{"-includeconf", ArgsManager::ALLOW_ANY}}); + std::array argv{"ignored", arg}; + std::string error; + (void)test.ParseParameters(argv.size(), argv.data(), error); + return error; + } +}; + +BOOST_FIXTURE_TEST_CASE(util_NoIncludeConf, NoIncludeConfTest) +{ + BOOST_CHECK_EQUAL(Parse("-noincludeconf"), ""); + BOOST_CHECK_EQUAL(Parse("-includeconf"), "-includeconf cannot be used from commandline; -includeconf=\"\""); + BOOST_CHECK_EQUAL(Parse("-includeconf=file"), "-includeconf cannot be used from commandline; -includeconf=\"file\""); +} + +BOOST_AUTO_TEST_CASE(util_ParseParameters) +{ + TestArgsManager testArgs; + const auto a = std::make_pair("-a", ArgsManager::ALLOW_ANY); + const auto b = std::make_pair("-b", ArgsManager::ALLOW_ANY); + const auto ccc = std::make_pair("-ccc", ArgsManager::ALLOW_ANY); + const auto d = std::make_pair("-d", ArgsManager::ALLOW_ANY); + + const char *argv_test[] = {"-ignored", "-a", "-b", "-ccc=argument", "-ccc=multiple", "f", "-d=e"}; + + std::string error; + LOCK(testArgs.cs_args); + testArgs.SetupArgs({a, b, ccc, d}); + BOOST_CHECK(testArgs.ParseParameters(0, argv_test, error)); + BOOST_CHECK(testArgs.m_settings.command_line_options.empty() && testArgs.m_settings.ro_config.empty()); + + BOOST_CHECK(testArgs.ParseParameters(1, argv_test, error)); + BOOST_CHECK(testArgs.m_settings.command_line_options.empty() && testArgs.m_settings.ro_config.empty()); + + BOOST_CHECK(testArgs.ParseParameters(7, argv_test, error)); + // expectation: -ignored is ignored (program name argument), + // -a, -b and -ccc end up in map, -d ignored because it is after + // a non-option argument (non-GNU option parsing) + BOOST_CHECK(testArgs.m_settings.command_line_options.size() == 3 && testArgs.m_settings.ro_config.empty()); + BOOST_CHECK(testArgs.IsArgSet("-a") && testArgs.IsArgSet("-b") && testArgs.IsArgSet("-ccc") + && !testArgs.IsArgSet("f") && !testArgs.IsArgSet("-d")); + BOOST_CHECK(testArgs.m_settings.command_line_options.count("a") && testArgs.m_settings.command_line_options.count("b") && testArgs.m_settings.command_line_options.count("ccc") + && !testArgs.m_settings.command_line_options.count("f") && !testArgs.m_settings.command_line_options.count("d")); + + BOOST_CHECK(testArgs.m_settings.command_line_options["a"].size() == 1); + BOOST_CHECK(testArgs.m_settings.command_line_options["a"].front().get_str() == ""); + BOOST_CHECK(testArgs.m_settings.command_line_options["ccc"].size() == 2); + BOOST_CHECK(testArgs.m_settings.command_line_options["ccc"].front().get_str() == "argument"); + BOOST_CHECK(testArgs.m_settings.command_line_options["ccc"].back().get_str() == "multiple"); + BOOST_CHECK(testArgs.GetArgs("-ccc").size() == 2); +} + +BOOST_AUTO_TEST_CASE(util_ParseInvalidParameters) +{ + TestArgsManager test; + test.SetupArgs({{"-registered", ArgsManager::ALLOW_ANY}}); + + const char* argv[] = {"ignored", "-registered"}; + std::string error; + BOOST_CHECK(test.ParseParameters(2, argv, error)); + BOOST_CHECK_EQUAL(error, ""); + + argv[1] = "-unregistered"; + BOOST_CHECK(!test.ParseParameters(2, argv, error)); + BOOST_CHECK_EQUAL(error, "Invalid parameter -unregistered"); + + // Make sure registered parameters prefixed with a chain name trigger errors. + // (Previously, they were accepted and ignored.) + argv[1] = "-test.registered"; + BOOST_CHECK(!test.ParseParameters(2, argv, error)); + BOOST_CHECK_EQUAL(error, "Invalid parameter -test.registered"); +} + +static void TestParse(const std::string& str, bool expected_bool, int64_t expected_int) +{ + TestArgsManager test; + test.SetupArgs({{"-value", ArgsManager::ALLOW_ANY}}); + std::string arg = "-value=" + str; + const char* argv[] = {"ignored", arg.c_str()}; + std::string error; + BOOST_CHECK(test.ParseParameters(2, argv, error)); + BOOST_CHECK_EQUAL(test.GetBoolArg("-value", false), expected_bool); + BOOST_CHECK_EQUAL(test.GetBoolArg("-value", true), expected_bool); + BOOST_CHECK_EQUAL(test.GetArg("-value", 99998), expected_int); + BOOST_CHECK_EQUAL(test.GetArg("-value", 99999), expected_int); +} + +// Test bool and int parsing. +BOOST_AUTO_TEST_CASE(util_ArgParsing) +{ + // Some of these cases could be ambiguous or surprising to users, and might + // be worth triggering errors or warnings in the future. But for now basic + // test coverage is useful to avoid breaking backwards compatibility + // unintentionally. + TestParse("", true, 0); + TestParse(" ", false, 0); + TestParse("0", false, 0); + TestParse("0 ", false, 0); + TestParse(" 0", false, 0); + TestParse("+0", false, 0); + TestParse("-0", false, 0); + TestParse("5", true, 5); + TestParse("5 ", true, 5); + TestParse(" 5", true, 5); + TestParse("+5", true, 5); + TestParse("-5", true, -5); + TestParse("0 5", false, 0); + TestParse("5 0", true, 5); + TestParse("050", true, 50); + TestParse("0.", false, 0); + TestParse("5.", true, 5); + TestParse("0.0", false, 0); + TestParse("0.5", false, 0); + TestParse("5.0", true, 5); + TestParse("5.5", true, 5); + TestParse("x", false, 0); + TestParse("x0", false, 0); + TestParse("x5", false, 0); + TestParse("0x", false, 0); + TestParse("5x", true, 5); + TestParse("0x5", false, 0); + TestParse("false", false, 0); + TestParse("true", false, 0); + TestParse("yes", false, 0); + TestParse("no", false, 0); +} + +BOOST_AUTO_TEST_CASE(util_GetBoolArg) +{ + TestArgsManager testArgs; + const auto a = std::make_pair("-a", ArgsManager::ALLOW_ANY); + const auto b = std::make_pair("-b", ArgsManager::ALLOW_ANY); + const auto c = std::make_pair("-c", ArgsManager::ALLOW_ANY); + const auto d = std::make_pair("-d", ArgsManager::ALLOW_ANY); + const auto e = std::make_pair("-e", ArgsManager::ALLOW_ANY); + const auto f = std::make_pair("-f", ArgsManager::ALLOW_ANY); + + const char *argv_test[] = { + "ignored", "-a", "-nob", "-c=0", "-d=1", "-e=false", "-f=true"}; + std::string error; + LOCK(testArgs.cs_args); + testArgs.SetupArgs({a, b, c, d, e, f}); + BOOST_CHECK(testArgs.ParseParameters(7, argv_test, error)); + + // Each letter should be set. + for (const char opt : "abcdef") + BOOST_CHECK(testArgs.IsArgSet({'-', opt}) || !opt); + + // Nothing else should be in the map + BOOST_CHECK(testArgs.m_settings.command_line_options.size() == 6 && + testArgs.m_settings.ro_config.empty()); + + // The -no prefix should get stripped on the way in. + BOOST_CHECK(!testArgs.IsArgSet("-nob")); + + // The -b option is flagged as negated, and nothing else is + BOOST_CHECK(testArgs.IsArgNegated("-b")); + BOOST_CHECK(!testArgs.IsArgNegated("-a")); + + // Check expected values. + BOOST_CHECK(testArgs.GetBoolArg("-a", false) == true); + BOOST_CHECK(testArgs.GetBoolArg("-b", true) == false); + BOOST_CHECK(testArgs.GetBoolArg("-c", true) == false); + BOOST_CHECK(testArgs.GetBoolArg("-d", false) == true); + BOOST_CHECK(testArgs.GetBoolArg("-e", true) == false); + BOOST_CHECK(testArgs.GetBoolArg("-f", true) == false); +} + +BOOST_AUTO_TEST_CASE(util_GetBoolArgEdgeCases) +{ + // Test some awful edge cases that hopefully no user will ever exercise. + TestArgsManager testArgs; + + // Params test + const auto foo = std::make_pair("-foo", ArgsManager::ALLOW_ANY); + const auto bar = std::make_pair("-bar", ArgsManager::ALLOW_ANY); + const char *argv_test[] = {"ignored", "-nofoo", "-foo", "-nobar=0"}; + testArgs.SetupArgs({foo, bar}); + std::string error; + BOOST_CHECK(testArgs.ParseParameters(4, argv_test, error)); + + // This was passed twice, second one overrides the negative setting. + BOOST_CHECK(!testArgs.IsArgNegated("-foo")); + BOOST_CHECK(testArgs.GetArg("-foo", "xxx") == ""); + + // A double negative is a positive, and not marked as negated. + BOOST_CHECK(!testArgs.IsArgNegated("-bar")); + BOOST_CHECK(testArgs.GetArg("-bar", "xxx") == "1"); + + // Config test + const char *conf_test = "nofoo=1\nfoo=1\nnobar=0\n"; + BOOST_CHECK(testArgs.ParseParameters(1, argv_test, error)); + testArgs.ReadConfigString(conf_test); + + // This was passed twice, second one overrides the negative setting, + // and the value. + BOOST_CHECK(!testArgs.IsArgNegated("-foo")); + BOOST_CHECK(testArgs.GetArg("-foo", "xxx") == "1"); + + // A double negative is a positive, and does not count as negated. + BOOST_CHECK(!testArgs.IsArgNegated("-bar")); + BOOST_CHECK(testArgs.GetArg("-bar", "xxx") == "1"); + + // Combined test + const char *combo_test_args[] = {"ignored", "-nofoo", "-bar"}; + const char *combo_test_conf = "foo=1\nnobar=1\n"; + BOOST_CHECK(testArgs.ParseParameters(3, combo_test_args, error)); + testArgs.ReadConfigString(combo_test_conf); + + // Command line overrides, but doesn't erase old setting + BOOST_CHECK(testArgs.IsArgNegated("-foo")); + BOOST_CHECK(testArgs.GetArg("-foo", "xxx") == "0"); + BOOST_CHECK(testArgs.GetArgs("-foo").size() == 0); + + // Command line overrides, but doesn't erase old setting + BOOST_CHECK(!testArgs.IsArgNegated("-bar")); + BOOST_CHECK(testArgs.GetArg("-bar", "xxx") == ""); + BOOST_CHECK(testArgs.GetArgs("-bar").size() == 1 + && testArgs.GetArgs("-bar").front() == ""); +} + +BOOST_AUTO_TEST_CASE(util_ReadConfigStream) +{ + const char *str_config = + "a=\n" + "b=1\n" + "ccc=argument\n" + "ccc=multiple\n" + "d=e\n" + "nofff=1\n" + "noggg=0\n" + "h=1\n" + "noh=1\n" + "noi=1\n" + "i=1\n" + "sec1.ccc=extend1\n" + "\n" + "[sec1]\n" + "ccc=extend2\n" + "d=eee\n" + "h=1\n" + "[sec2]\n" + "ccc=extend3\n" + "iii=2\n"; + + TestArgsManager test_args; + LOCK(test_args.cs_args); + const auto a = std::make_pair("-a", ArgsManager::ALLOW_ANY); + const auto b = std::make_pair("-b", ArgsManager::ALLOW_ANY); + const auto ccc = std::make_pair("-ccc", ArgsManager::ALLOW_ANY); + const auto d = std::make_pair("-d", ArgsManager::ALLOW_ANY); + const auto e = std::make_pair("-e", ArgsManager::ALLOW_ANY); + const auto fff = std::make_pair("-fff", ArgsManager::ALLOW_ANY); + const auto ggg = std::make_pair("-ggg", ArgsManager::ALLOW_ANY); + const auto h = std::make_pair("-h", ArgsManager::ALLOW_ANY); + const auto i = std::make_pair("-i", ArgsManager::ALLOW_ANY); + const auto iii = std::make_pair("-iii", ArgsManager::ALLOW_ANY); + test_args.SetupArgs({a, b, ccc, d, e, fff, ggg, h, i, iii}); + + test_args.ReadConfigString(str_config); + // expectation: a, b, ccc, d, fff, ggg, h, i end up in map + // so do sec1.ccc, sec1.d, sec1.h, sec2.ccc, sec2.iii + + BOOST_CHECK(test_args.m_settings.command_line_options.empty()); + BOOST_CHECK(test_args.m_settings.ro_config.size() == 3); + BOOST_CHECK(test_args.m_settings.ro_config[""].size() == 8); + BOOST_CHECK(test_args.m_settings.ro_config["sec1"].size() == 3); + BOOST_CHECK(test_args.m_settings.ro_config["sec2"].size() == 2); + + BOOST_CHECK(test_args.m_settings.ro_config[""].count("a")); + BOOST_CHECK(test_args.m_settings.ro_config[""].count("b")); + BOOST_CHECK(test_args.m_settings.ro_config[""].count("ccc")); + BOOST_CHECK(test_args.m_settings.ro_config[""].count("d")); + BOOST_CHECK(test_args.m_settings.ro_config[""].count("fff")); + BOOST_CHECK(test_args.m_settings.ro_config[""].count("ggg")); + BOOST_CHECK(test_args.m_settings.ro_config[""].count("h")); + BOOST_CHECK(test_args.m_settings.ro_config[""].count("i")); + BOOST_CHECK(test_args.m_settings.ro_config["sec1"].count("ccc")); + BOOST_CHECK(test_args.m_settings.ro_config["sec1"].count("h")); + BOOST_CHECK(test_args.m_settings.ro_config["sec2"].count("ccc")); + BOOST_CHECK(test_args.m_settings.ro_config["sec2"].count("iii")); + + BOOST_CHECK(test_args.IsArgSet("-a")); + BOOST_CHECK(test_args.IsArgSet("-b")); + BOOST_CHECK(test_args.IsArgSet("-ccc")); + BOOST_CHECK(test_args.IsArgSet("-d")); + BOOST_CHECK(test_args.IsArgSet("-fff")); + BOOST_CHECK(test_args.IsArgSet("-ggg")); + BOOST_CHECK(test_args.IsArgSet("-h")); + BOOST_CHECK(test_args.IsArgSet("-i")); + BOOST_CHECK(!test_args.IsArgSet("-zzz")); + BOOST_CHECK(!test_args.IsArgSet("-iii")); + + BOOST_CHECK_EQUAL(test_args.GetArg("-a", "xxx"), ""); + BOOST_CHECK_EQUAL(test_args.GetArg("-b", "xxx"), "1"); + BOOST_CHECK_EQUAL(test_args.GetArg("-ccc", "xxx"), "argument"); + BOOST_CHECK_EQUAL(test_args.GetArg("-d", "xxx"), "e"); + BOOST_CHECK_EQUAL(test_args.GetArg("-fff", "xxx"), "0"); + BOOST_CHECK_EQUAL(test_args.GetArg("-ggg", "xxx"), "1"); + BOOST_CHECK_EQUAL(test_args.GetArg("-h", "xxx"), "0"); + BOOST_CHECK_EQUAL(test_args.GetArg("-i", "xxx"), "1"); + BOOST_CHECK_EQUAL(test_args.GetArg("-zzz", "xxx"), "xxx"); + BOOST_CHECK_EQUAL(test_args.GetArg("-iii", "xxx"), "xxx"); + + for (const bool def : {false, true}) { + BOOST_CHECK(test_args.GetBoolArg("-a", def)); + BOOST_CHECK(test_args.GetBoolArg("-b", def)); + BOOST_CHECK(!test_args.GetBoolArg("-ccc", def)); + BOOST_CHECK(!test_args.GetBoolArg("-d", def)); + BOOST_CHECK(!test_args.GetBoolArg("-fff", def)); + BOOST_CHECK(test_args.GetBoolArg("-ggg", def)); + BOOST_CHECK(!test_args.GetBoolArg("-h", def)); + BOOST_CHECK(test_args.GetBoolArg("-i", def)); + BOOST_CHECK(test_args.GetBoolArg("-zzz", def) == def); + BOOST_CHECK(test_args.GetBoolArg("-iii", def) == def); + } + + BOOST_CHECK(test_args.GetArgs("-a").size() == 1 + && test_args.GetArgs("-a").front() == ""); + BOOST_CHECK(test_args.GetArgs("-b").size() == 1 + && test_args.GetArgs("-b").front() == "1"); + BOOST_CHECK(test_args.GetArgs("-ccc").size() == 2 + && test_args.GetArgs("-ccc").front() == "argument" + && test_args.GetArgs("-ccc").back() == "multiple"); + BOOST_CHECK(test_args.GetArgs("-fff").size() == 0); + BOOST_CHECK(test_args.GetArgs("-nofff").size() == 0); + BOOST_CHECK(test_args.GetArgs("-ggg").size() == 1 + && test_args.GetArgs("-ggg").front() == "1"); + BOOST_CHECK(test_args.GetArgs("-noggg").size() == 0); + BOOST_CHECK(test_args.GetArgs("-h").size() == 0); + BOOST_CHECK(test_args.GetArgs("-noh").size() == 0); + BOOST_CHECK(test_args.GetArgs("-i").size() == 1 + && test_args.GetArgs("-i").front() == "1"); + BOOST_CHECK(test_args.GetArgs("-noi").size() == 0); + BOOST_CHECK(test_args.GetArgs("-zzz").size() == 0); + + BOOST_CHECK(!test_args.IsArgNegated("-a")); + BOOST_CHECK(!test_args.IsArgNegated("-b")); + BOOST_CHECK(!test_args.IsArgNegated("-ccc")); + BOOST_CHECK(!test_args.IsArgNegated("-d")); + BOOST_CHECK(test_args.IsArgNegated("-fff")); + BOOST_CHECK(!test_args.IsArgNegated("-ggg")); + BOOST_CHECK(test_args.IsArgNegated("-h")); // last setting takes precedence + BOOST_CHECK(!test_args.IsArgNegated("-i")); // last setting takes precedence + BOOST_CHECK(!test_args.IsArgNegated("-zzz")); + + // Test sections work + test_args.SelectConfigNetwork("sec1"); + + // same as original + BOOST_CHECK_EQUAL(test_args.GetArg("-a", "xxx"), ""); + BOOST_CHECK_EQUAL(test_args.GetArg("-b", "xxx"), "1"); + BOOST_CHECK_EQUAL(test_args.GetArg("-fff", "xxx"), "0"); + BOOST_CHECK_EQUAL(test_args.GetArg("-ggg", "xxx"), "1"); + BOOST_CHECK_EQUAL(test_args.GetArg("-zzz", "xxx"), "xxx"); + BOOST_CHECK_EQUAL(test_args.GetArg("-iii", "xxx"), "xxx"); + // d is overridden + BOOST_CHECK(test_args.GetArg("-d", "xxx") == "eee"); + // section-specific setting + BOOST_CHECK(test_args.GetArg("-h", "xxx") == "1"); + // section takes priority for multiple values + BOOST_CHECK(test_args.GetArg("-ccc", "xxx") == "extend1"); + // check multiple values works + const std::vector sec1_ccc_expected = {"extend1","extend2","argument","multiple"}; + const auto& sec1_ccc_res = test_args.GetArgs("-ccc"); + BOOST_CHECK_EQUAL_COLLECTIONS(sec1_ccc_res.begin(), sec1_ccc_res.end(), sec1_ccc_expected.begin(), sec1_ccc_expected.end()); + + test_args.SelectConfigNetwork("sec2"); + + // same as original + BOOST_CHECK(test_args.GetArg("-a", "xxx") == ""); + BOOST_CHECK(test_args.GetArg("-b", "xxx") == "1"); + BOOST_CHECK(test_args.GetArg("-d", "xxx") == "e"); + BOOST_CHECK(test_args.GetArg("-fff", "xxx") == "0"); + BOOST_CHECK(test_args.GetArg("-ggg", "xxx") == "1"); + BOOST_CHECK(test_args.GetArg("-zzz", "xxx") == "xxx"); + BOOST_CHECK(test_args.GetArg("-h", "xxx") == "0"); + // section-specific setting + BOOST_CHECK(test_args.GetArg("-iii", "xxx") == "2"); + // section takes priority for multiple values + BOOST_CHECK(test_args.GetArg("-ccc", "xxx") == "extend3"); + // check multiple values works + const std::vector sec2_ccc_expected = {"extend3","argument","multiple"}; + const auto& sec2_ccc_res = test_args.GetArgs("-ccc"); + BOOST_CHECK_EQUAL_COLLECTIONS(sec2_ccc_res.begin(), sec2_ccc_res.end(), sec2_ccc_expected.begin(), sec2_ccc_expected.end()); + + // Test section only options + + test_args.SetNetworkOnlyArg("-d"); + test_args.SetNetworkOnlyArg("-ccc"); + test_args.SetNetworkOnlyArg("-h"); + + test_args.SelectConfigNetwork(CBaseChainParams::MAIN); + BOOST_CHECK(test_args.GetArg("-d", "xxx") == "e"); + BOOST_CHECK(test_args.GetArgs("-ccc").size() == 2); + BOOST_CHECK(test_args.GetArg("-h", "xxx") == "0"); + + test_args.SelectConfigNetwork("sec1"); + BOOST_CHECK(test_args.GetArg("-d", "xxx") == "eee"); + BOOST_CHECK(test_args.GetArgs("-d").size() == 1); + BOOST_CHECK(test_args.GetArgs("-ccc").size() == 2); + BOOST_CHECK(test_args.GetArg("-h", "xxx") == "1"); + + test_args.SelectConfigNetwork("sec2"); + BOOST_CHECK(test_args.GetArg("-d", "xxx") == "xxx"); + BOOST_CHECK(test_args.GetArgs("-d").size() == 0); + BOOST_CHECK(test_args.GetArgs("-ccc").size() == 1); + BOOST_CHECK(test_args.GetArg("-h", "xxx") == "0"); +} + +BOOST_AUTO_TEST_CASE(util_GetArg) +{ + TestArgsManager testArgs; + LOCK(testArgs.cs_args); + testArgs.m_settings.command_line_options.clear(); + testArgs.m_settings.command_line_options["strtest1"] = {"string..."}; + // strtest2 undefined on purpose + testArgs.m_settings.command_line_options["inttest1"] = {"12345"}; + testArgs.m_settings.command_line_options["inttest2"] = {"81985529216486895"}; + // inttest3 undefined on purpose + testArgs.m_settings.command_line_options["booltest1"] = {""}; + // booltest2 undefined on purpose + testArgs.m_settings.command_line_options["booltest3"] = {"0"}; + testArgs.m_settings.command_line_options["booltest4"] = {"1"}; + + // priorities + testArgs.m_settings.command_line_options["pritest1"] = {"a", "b"}; + testArgs.m_settings.ro_config[""]["pritest2"] = {"a", "b"}; + testArgs.m_settings.command_line_options["pritest3"] = {"a"}; + testArgs.m_settings.ro_config[""]["pritest3"] = {"b"}; + testArgs.m_settings.command_line_options["pritest4"] = {"a","b"}; + testArgs.m_settings.ro_config[""]["pritest4"] = {"c","d"}; + + BOOST_CHECK_EQUAL(testArgs.GetArg("strtest1", "default"), "string..."); + BOOST_CHECK_EQUAL(testArgs.GetArg("strtest2", "default"), "default"); + BOOST_CHECK_EQUAL(testArgs.GetArg("inttest1", -1), 12345); + BOOST_CHECK_EQUAL(testArgs.GetArg("inttest2", -1), 81985529216486895LL); + BOOST_CHECK_EQUAL(testArgs.GetArg("inttest3", -1), -1); + BOOST_CHECK_EQUAL(testArgs.GetBoolArg("booltest1", false), true); + BOOST_CHECK_EQUAL(testArgs.GetBoolArg("booltest2", false), false); + BOOST_CHECK_EQUAL(testArgs.GetBoolArg("booltest3", false), false); + BOOST_CHECK_EQUAL(testArgs.GetBoolArg("booltest4", false), true); + + BOOST_CHECK_EQUAL(testArgs.GetArg("pritest1", "default"), "b"); + BOOST_CHECK_EQUAL(testArgs.GetArg("pritest2", "default"), "a"); + BOOST_CHECK_EQUAL(testArgs.GetArg("pritest3", "default"), "a"); + BOOST_CHECK_EQUAL(testArgs.GetArg("pritest4", "default"), "b"); +} + +BOOST_AUTO_TEST_CASE(util_GetChainName) +{ + TestArgsManager test_args; + const auto testnet = std::make_pair("-testnet", ArgsManager::ALLOW_ANY); + const auto regtest = std::make_pair("-regtest", ArgsManager::ALLOW_ANY); + test_args.SetupArgs({testnet, regtest}); + + const char* argv_testnet[] = {"cmd", "-testnet"}; + const char* argv_regtest[] = {"cmd", "-regtest"}; + const char* argv_test_no_reg[] = {"cmd", "-testnet", "-noregtest"}; + const char* argv_both[] = {"cmd", "-testnet", "-regtest"}; + + // equivalent to "-testnet" + // regtest in testnet section is ignored + const char* testnetconf = "testnet=1\nregtest=0\n[test]\nregtest=1"; + std::string error; + + BOOST_CHECK(test_args.ParseParameters(0, argv_testnet, error)); + BOOST_CHECK_EQUAL(test_args.GetChainName(), "main"); + + BOOST_CHECK(test_args.ParseParameters(2, argv_testnet, error)); + BOOST_CHECK_EQUAL(test_args.GetChainName(), "test"); + + BOOST_CHECK(test_args.ParseParameters(2, argv_regtest, error)); + BOOST_CHECK_EQUAL(test_args.GetChainName(), "regtest"); + + BOOST_CHECK(test_args.ParseParameters(3, argv_test_no_reg, error)); + BOOST_CHECK_EQUAL(test_args.GetChainName(), "test"); + + BOOST_CHECK(test_args.ParseParameters(3, argv_both, error)); + BOOST_CHECK_THROW(test_args.GetChainName(), std::runtime_error); + + BOOST_CHECK(test_args.ParseParameters(0, argv_testnet, error)); + test_args.ReadConfigString(testnetconf); + BOOST_CHECK_EQUAL(test_args.GetChainName(), "test"); + + BOOST_CHECK(test_args.ParseParameters(2, argv_testnet, error)); + test_args.ReadConfigString(testnetconf); + BOOST_CHECK_EQUAL(test_args.GetChainName(), "test"); + + BOOST_CHECK(test_args.ParseParameters(2, argv_regtest, error)); + test_args.ReadConfigString(testnetconf); + BOOST_CHECK_THROW(test_args.GetChainName(), std::runtime_error); + + BOOST_CHECK(test_args.ParseParameters(3, argv_test_no_reg, error)); + test_args.ReadConfigString(testnetconf); + BOOST_CHECK_EQUAL(test_args.GetChainName(), "test"); + + BOOST_CHECK(test_args.ParseParameters(3, argv_both, error)); + test_args.ReadConfigString(testnetconf); + BOOST_CHECK_THROW(test_args.GetChainName(), std::runtime_error); + + // check setting the network to test (and thus making + // [test] regtest=1 potentially relevant) doesn't break things + test_args.SelectConfigNetwork("test"); + + BOOST_CHECK(test_args.ParseParameters(0, argv_testnet, error)); + test_args.ReadConfigString(testnetconf); + BOOST_CHECK_EQUAL(test_args.GetChainName(), "test"); + + BOOST_CHECK(test_args.ParseParameters(2, argv_testnet, error)); + test_args.ReadConfigString(testnetconf); + BOOST_CHECK_EQUAL(test_args.GetChainName(), "test"); + + BOOST_CHECK(test_args.ParseParameters(2, argv_regtest, error)); + test_args.ReadConfigString(testnetconf); + BOOST_CHECK_THROW(test_args.GetChainName(), std::runtime_error); + + BOOST_CHECK(test_args.ParseParameters(2, argv_test_no_reg, error)); + test_args.ReadConfigString(testnetconf); + BOOST_CHECK_EQUAL(test_args.GetChainName(), "test"); + + BOOST_CHECK(test_args.ParseParameters(3, argv_both, error)); + test_args.ReadConfigString(testnetconf); + BOOST_CHECK_THROW(test_args.GetChainName(), std::runtime_error); +} + +// Test different ways settings can be merged, and verify results. This test can +// be used to confirm that updates to settings code don't change behavior +// unintentionally. +// +// The test covers: +// +// - Combining different setting actions. Possible actions are: configuring a +// setting, negating a setting (adding "-no" prefix), and configuring/negating +// settings in a network section (adding "main." or "test." prefixes). +// +// - Combining settings from command line arguments and a config file. +// +// - Combining SoftSet and ForceSet calls. +// +// - Testing "main" and "test" network values to make sure settings from network +// sections are applied and to check for mainnet-specific behaviors like +// inheriting settings from the default section. +// +// - Testing network-specific settings like "-wallet", that may be ignored +// outside a network section, and non-network specific settings like "-server" +// that aren't sensitive to the network. +// +struct ArgsMergeTestingSetup : public BasicTestingSetup { + //! Max number of actions to sequence together. Can decrease this when + //! debugging to make test results easier to understand. + static constexpr int MAX_ACTIONS = 3; + + enum Action { NONE, SET, NEGATE, SECTION_SET, SECTION_NEGATE }; + using ActionList = Action[MAX_ACTIONS]; + + //! Enumerate all possible test configurations. + template + void ForEachMergeSetup(Fn&& fn) + { + ActionList arg_actions = {}; + // command_line_options do not have sections. Only iterate over SET and NEGATE + ForEachNoDup(arg_actions, SET, NEGATE, [&] { + ActionList conf_actions = {}; + ForEachNoDup(conf_actions, SET, SECTION_NEGATE, [&] { + for (bool soft_set : {false, true}) { + for (bool force_set : {false, true}) { + for (const std::string& section : {CBaseChainParams::MAIN, CBaseChainParams::TESTNET}) { + for (const std::string& network : {CBaseChainParams::MAIN, CBaseChainParams::TESTNET}) { + for (bool net_specific : {false, true}) { + fn(arg_actions, conf_actions, soft_set, force_set, section, network, net_specific); + } + } + } + } + } + }); + }); + } + + //! Translate actions into a list of = setting strings. + std::vector GetValues(const ActionList& actions, + const std::string& section, + const std::string& name, + const std::string& value_prefix) + { + std::vector values; + int suffix = 0; + for (Action action : actions) { + if (action == NONE) break; + std::string prefix; + if (action == SECTION_SET || action == SECTION_NEGATE) prefix = section + "."; + if (action == SET || action == SECTION_SET) { + for (int i = 0; i < 2; ++i) { + values.push_back(prefix + name + "=" + value_prefix + ToString(++suffix)); + } + } + if (action == NEGATE || action == SECTION_NEGATE) { + values.push_back(prefix + "no" + name + "=1"); + } + } + return values; + } +}; + +// Regression test covering different ways config settings can be merged. The +// test parses and merges settings, representing the results as strings that get +// compared against an expected hash. To debug, the result strings can be dumped +// to a file (see comments below). +BOOST_FIXTURE_TEST_CASE(util_ArgsMerge, ArgsMergeTestingSetup) +{ + CHash256 out_sha; + FILE* out_file = nullptr; + if (const char* out_path = getenv("ARGS_MERGE_TEST_OUT")) { + out_file = fsbridge::fopen(out_path, "w"); + if (!out_file) throw std::system_error(errno, std::generic_category(), "fopen failed"); + } + + ForEachMergeSetup([&](const ActionList& arg_actions, const ActionList& conf_actions, bool soft_set, bool force_set, + const std::string& section, const std::string& network, bool net_specific) { + TestArgsManager parser; + LOCK(parser.cs_args); + + std::string desc = "net="; + desc += network; + parser.m_network = network; + + const std::string& name = net_specific ? "wallet" : "server"; + const std::string key = "-" + name; + parser.AddArg(key, name, ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); + if (net_specific) parser.SetNetworkOnlyArg(key); + + auto args = GetValues(arg_actions, section, name, "a"); + std::vector argv = {"ignored"}; + for (auto& arg : args) { + arg.insert(0, "-"); + desc += " "; + desc += arg; + argv.push_back(arg.c_str()); + } + std::string error; + BOOST_CHECK(parser.ParseParameters(argv.size(), argv.data(), error)); + BOOST_CHECK_EQUAL(error, ""); + + std::string conf; + for (auto& conf_val : GetValues(conf_actions, section, name, "c")) { + desc += " "; + desc += conf_val; + conf += conf_val; + conf += "\n"; + } + std::istringstream conf_stream(conf); + BOOST_CHECK(parser.ReadConfigStream(conf_stream, "filepath", error)); + BOOST_CHECK_EQUAL(error, ""); + + if (soft_set) { + desc += " soft"; + parser.SoftSetArg(key, "soft1"); + parser.SoftSetArg(key, "soft2"); + } + + if (force_set) { + desc += " force"; + parser.ForceSetArg(key, "force1"); + parser.ForceSetArg(key, "force2"); + } + + desc += " || "; + + if (!parser.IsArgSet(key)) { + desc += "unset"; + BOOST_CHECK(!parser.IsArgNegated(key)); + BOOST_CHECK_EQUAL(parser.GetArg(key, "default"), "default"); + BOOST_CHECK(parser.GetArgs(key).empty()); + } else if (parser.IsArgNegated(key)) { + desc += "negated"; + BOOST_CHECK_EQUAL(parser.GetArg(key, "default"), "0"); + BOOST_CHECK(parser.GetArgs(key).empty()); + } else { + desc += parser.GetArg(key, "default"); + desc += " |"; + for (const auto& arg : parser.GetArgs(key)) { + desc += " "; + desc += arg; + } + } + + std::set ignored = parser.GetUnsuitableSectionOnlyArgs(); + if (!ignored.empty()) { + desc += " | ignored"; + for (const auto& arg : ignored) { + desc += " "; + desc += arg; + } + } + + desc += "\n"; + + out_sha.Write(MakeUCharSpan(desc)); + if (out_file) { + BOOST_REQUIRE(fwrite(desc.data(), 1, desc.size(), out_file) == desc.size()); + } + }); + + if (out_file) { + if (fclose(out_file)) throw std::system_error(errno, std::generic_category(), "fclose failed"); + out_file = nullptr; + } + + unsigned char out_sha_bytes[CSHA256::OUTPUT_SIZE]; + out_sha.Finalize(out_sha_bytes); + std::string out_sha_hex = HexStr(out_sha_bytes); + + // If check below fails, should manually dump the results with: + // + // ARGS_MERGE_TEST_OUT=results.txt ./test_bitcoin --run_test=util_tests/util_ArgsMerge + // + // And verify diff against previous results to make sure the changes are expected. + // + // Results file is formatted like: + // + // || | | + BOOST_CHECK_EQUAL(out_sha_hex, "8fd4877bb8bf337badca950ede6c917441901962f160e52514e06a60dea46cde"); +} + +// Similar test as above, but for ArgsManager::GetChainName function. +struct ChainMergeTestingSetup : public BasicTestingSetup { + static constexpr int MAX_ACTIONS = 2; + + enum Action { NONE, ENABLE_TEST, DISABLE_TEST, NEGATE_TEST, ENABLE_REG, DISABLE_REG, NEGATE_REG }; + using ActionList = Action[MAX_ACTIONS]; + + //! Enumerate all possible test configurations. + template + void ForEachMergeSetup(Fn&& fn) + { + ActionList arg_actions = {}; + ForEachNoDup(arg_actions, ENABLE_TEST, NEGATE_REG, [&] { + ActionList conf_actions = {}; + ForEachNoDup(conf_actions, ENABLE_TEST, NEGATE_REG, [&] { fn(arg_actions, conf_actions); }); + }); + } +}; + +BOOST_FIXTURE_TEST_CASE(util_ChainMerge, ChainMergeTestingSetup) +{ + CHash256 out_sha; + FILE* out_file = nullptr; + if (const char* out_path = getenv("CHAIN_MERGE_TEST_OUT")) { + out_file = fsbridge::fopen(out_path, "w"); + if (!out_file) throw std::system_error(errno, std::generic_category(), "fopen failed"); + } + + ForEachMergeSetup([&](const ActionList& arg_actions, const ActionList& conf_actions) { + TestArgsManager parser; + LOCK(parser.cs_args); + parser.AddArg("-regtest", "regtest", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); + parser.AddArg("-testnet", "testnet", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); + + auto arg = [](Action action) { return action == ENABLE_TEST ? "-testnet=1" : + action == DISABLE_TEST ? "-testnet=0" : + action == NEGATE_TEST ? "-notestnet=1" : + action == ENABLE_REG ? "-regtest=1" : + action == DISABLE_REG ? "-regtest=0" : + action == NEGATE_REG ? "-noregtest=1" : nullptr; }; + + std::string desc; + std::vector argv = {"ignored"}; + for (Action action : arg_actions) { + const char* argstr = arg(action); + if (!argstr) break; + argv.push_back(argstr); + desc += " "; + desc += argv.back(); + } + std::string error; + BOOST_CHECK(parser.ParseParameters(argv.size(), argv.data(), error)); + BOOST_CHECK_EQUAL(error, ""); + + std::string conf; + for (Action action : conf_actions) { + const char* argstr = arg(action); + if (!argstr) break; + desc += " "; + desc += argstr + 1; + conf += argstr + 1; + conf += "\n"; + } + std::istringstream conf_stream(conf); + BOOST_CHECK(parser.ReadConfigStream(conf_stream, "filepath", error)); + BOOST_CHECK_EQUAL(error, ""); + + desc += " || "; + try { + desc += parser.GetChainName(); + } catch (const std::runtime_error& e) { + desc += "error: "; + desc += e.what(); + } + desc += "\n"; + + out_sha.Write(MakeUCharSpan(desc)); + if (out_file) { + BOOST_REQUIRE(fwrite(desc.data(), 1, desc.size(), out_file) == desc.size()); + } + }); + + if (out_file) { + if (fclose(out_file)) throw std::system_error(errno, std::generic_category(), "fclose failed"); + out_file = nullptr; + } + + unsigned char out_sha_bytes[CSHA256::OUTPUT_SIZE]; + out_sha.Finalize(out_sha_bytes); + std::string out_sha_hex = HexStr(out_sha_bytes); + + // If check below fails, should manually dump the results with: + // + // CHAIN_MERGE_TEST_OUT=results.txt ./test_bitcoin --run_test=util_tests/util_ChainMerge + // + // And verify diff against previous results to make sure the changes are expected. + // + // Results file is formatted like: + // + // || + BOOST_CHECK_EQUAL(out_sha_hex, "1cd2697d1665d086f15ed18b3333bc356c3d39ba490589dc58afd8fc2f457731"); +} + +BOOST_AUTO_TEST_CASE(util_ReadWriteSettings) +{ + // Test writing setting. + TestArgsManager args1; + args1.ForceSetArg("-datadir", fs::PathToString(m_path_root)); + args1.LockSettings([&](util::Settings& settings) { settings.rw_settings["name"] = "value"; }); + args1.WriteSettingsFile(); + + // Test reading setting. + TestArgsManager args2; + args2.ForceSetArg("-datadir", fs::PathToString(m_path_root)); + args2.ReadSettingsFile(); + args2.LockSettings([&](util::Settings& settings) { BOOST_CHECK_EQUAL(settings.rw_settings["name"].get_str(), "value"); }); + + // Test error logging, and remove previously written setting. + { + ASSERT_DEBUG_LOG("Failed renaming settings file"); + fs::remove(args1.GetDataDirBase() / "settings.json"); + fs::create_directory(args1.GetDataDirBase() / "settings.json"); + args2.WriteSettingsFile(); + fs::remove(args1.GetDataDirBase() / "settings.json"); + } +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/blockchain_tests.cpp b/src/test/blockchain_tests.cpp index c8e8cdeeb3..0157e25071 100644 --- a/src/test/blockchain_tests.cpp +++ b/src/test/blockchain_tests.cpp @@ -4,13 +4,13 @@ #include -#include - #include #include #include #include +#include + /* Equality between doubles is imprecise. Comparison should be done * with a small threshold of tolerance, rather than exact equality. */ diff --git a/src/test/bswap_tests.cpp b/src/test/bswap_tests.cpp index 4e75e74d77..0dba16a426 100644 --- a/src/test/bswap_tests.cpp +++ b/src/test/bswap_tests.cpp @@ -16,9 +16,9 @@ BOOST_AUTO_TEST_CASE(bswap_tests) uint16_t e1 = 0x3412; uint32_t e2 = 0xbc9a7856; uint64_t e3 = 0xbc9a78563412f0de; - BOOST_CHECK(bswap_16(u1) == e1); - BOOST_CHECK(bswap_32(u2) == e2); - BOOST_CHECK(bswap_64(u3) == e3); + BOOST_CHECK(internal_bswap_16(u1) == e1); + BOOST_CHECK(internal_bswap_32(u2) == e2); + BOOST_CHECK(internal_bswap_64(u3) == e3); } BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/crypto_tests.cpp b/src/test/crypto_tests.cpp index bddab92fbc..f9e66964b7 100644 --- a/src/test/crypto_tests.cpp +++ b/src/test/crypto_tests.cpp @@ -1083,28 +1083,6 @@ BOOST_AUTO_TEST_CASE(hkdf_hmac_sha256_l32_tests) "8da4e775a563c18f715f802a063c5a31b8a11f5c5ee1879ec3454e5f3c738d2d"); } -BOOST_AUTO_TEST_CASE(countbits_tests) -{ - FastRandomContext ctx; - for (unsigned int i = 0; i <= 64; ++i) { - if (i == 0) { - // Check handling of zero. - BOOST_CHECK_EQUAL(CountBits(0), 0U); - } else if (i < 10) { - for (uint64_t j = (uint64_t)1 << (i - 1); (j >> i) == 0; ++j) { - // Exhaustively test up to 10 bits - BOOST_CHECK_EQUAL(CountBits(j), i); - } - } else { - for (int k = 0; k < 1000; k++) { - // Randomly test 1000 samples of each length above 10 bits. - uint64_t j = ((uint64_t)1) << (i - 1) | ctx.randbits(i - 1); - BOOST_CHECK_EQUAL(CountBits(j), i); - } - } - } -} - BOOST_AUTO_TEST_CASE(sha256d64) { for (int i = 0; i <= 32; ++i) { diff --git a/src/test/fuzz/integer.cpp b/src/test/fuzz/integer.cpp index 5ab3810578..8d80b395a3 100644 --- a/src/test/fuzz/integer.cpp +++ b/src/test/fuzz/integer.cpp @@ -75,7 +75,6 @@ FUZZ_TARGET_INIT(integer, initialize_integer) static const uint256 u256_max(uint256S("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")); const std::vector v256{u256, u256_min, u256_max}; (void)ComputeMerkleRoot(v256); - (void)CountBits(u64); (void)DecompressAmount(u64); { diff --git a/src/test/raii_event_tests.cpp b/src/test/raii_event_tests.cpp index c489ac04e9..d4487cd941 100644 --- a/src/test/raii_event_tests.cpp +++ b/src/test/raii_event_tests.cpp @@ -4,8 +4,8 @@ #include +#include #include -#include #include diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp index fbdb851ae1..00ae3a0703 100644 --- a/src/test/util_tests.cpp +++ b/src/test/util_tests.cpp @@ -9,9 +9,7 @@ #include // For Hash() #include // For CKey #include -#include #include -#include #include #include #include // For MessageSign(), MessageVerify(), MESSAGE_MAGIC @@ -58,31 +56,6 @@ namespace BCLog { BOOST_FIXTURE_TEST_SUITE(util_tests, BasicTestingSetup) -BOOST_AUTO_TEST_CASE(util_datadir) -{ - // Use local args variable instead of m_args to avoid making assumptions about test setup - ArgsManager args; - args.ForceSetArg("-datadir", fs::PathToString(m_path_root)); - - const fs::path dd_norm = args.GetDataDirBase(); - - args.ForceSetArg("-datadir", fs::PathToString(dd_norm) + "/"); - args.ClearPathCache(); - BOOST_CHECK_EQUAL(dd_norm, args.GetDataDirBase()); - - args.ForceSetArg("-datadir", fs::PathToString(dd_norm) + "/."); - args.ClearPathCache(); - BOOST_CHECK_EQUAL(dd_norm, args.GetDataDirBase()); - - args.ForceSetArg("-datadir", fs::PathToString(dd_norm) + "/./"); - args.ClearPathCache(); - BOOST_CHECK_EQUAL(dd_norm, args.GetDataDirBase()); - - args.ForceSetArg("-datadir", fs::PathToString(dd_norm) + "/.//"); - args.ClearPathCache(); - BOOST_CHECK_EQUAL(dd_norm, args.GetDataDirBase()); -} - namespace { class NoCopyOrMove { @@ -311,1000 +284,6 @@ BOOST_AUTO_TEST_CASE(util_FormatISO8601Time) BOOST_CHECK_EQUAL(FormatISO8601Time(1317425777), "23:36:17Z"); } -struct TestArgsManager : public ArgsManager -{ - TestArgsManager() { m_network_only_args.clear(); } - void ReadConfigString(const std::string str_config) - { - std::istringstream streamConfig(str_config); - { - LOCK(cs_args); - m_settings.ro_config.clear(); - m_config_sections.clear(); - } - std::string error; - BOOST_REQUIRE(ReadConfigStream(streamConfig, "", error)); - } - void SetNetworkOnlyArg(const std::string arg) - { - LOCK(cs_args); - m_network_only_args.insert(arg); - } - void SetupArgs(const std::vector>& args) - { - for (const auto& arg : args) { - AddArg(arg.first, "", arg.second, OptionsCategory::OPTIONS); - } - } - using ArgsManager::GetSetting; - using ArgsManager::GetSettingsList; - using ArgsManager::ReadConfigStream; - using ArgsManager::cs_args; - using ArgsManager::m_network; - using ArgsManager::m_settings; -}; - -//! Test GetSetting and GetArg type coercion, negation, and default value handling. -class CheckValueTest : public TestChain100Setup -{ -public: - struct Expect { - util::SettingsValue setting; - bool default_string = false; - bool default_int = false; - bool default_bool = false; - const char* string_value = nullptr; - std::optional int_value; - std::optional bool_value; - std::optional> list_value; - const char* error = nullptr; - - explicit Expect(util::SettingsValue s) : setting(std::move(s)) {} - Expect& DefaultString() { default_string = true; return *this; } - Expect& DefaultInt() { default_int = true; return *this; } - Expect& DefaultBool() { default_bool = true; return *this; } - Expect& String(const char* s) { string_value = s; return *this; } - Expect& Int(int64_t i) { int_value = i; return *this; } - Expect& Bool(bool b) { bool_value = b; return *this; } - Expect& List(std::vector m) { list_value = std::move(m); return *this; } - Expect& Error(const char* e) { error = e; return *this; } - }; - - void CheckValue(unsigned int flags, const char* arg, const Expect& expect) - { - TestArgsManager test; - test.SetupArgs({{"-value", flags}}); - const char* argv[] = {"ignored", arg}; - std::string error; - bool success = test.ParseParameters(arg ? 2 : 1, (char**)argv, error); - - BOOST_CHECK_EQUAL(test.GetSetting("-value").write(), expect.setting.write()); - auto settings_list = test.GetSettingsList("-value"); - if (expect.setting.isNull() || expect.setting.isFalse()) { - BOOST_CHECK_EQUAL(settings_list.size(), 0U); - } else { - BOOST_CHECK_EQUAL(settings_list.size(), 1U); - BOOST_CHECK_EQUAL(settings_list[0].write(), expect.setting.write()); - } - - if (expect.error) { - BOOST_CHECK(!success); - BOOST_CHECK_NE(error.find(expect.error), std::string::npos); - } else { - BOOST_CHECK(success); - BOOST_CHECK_EQUAL(error, ""); - } - - if (expect.default_string) { - BOOST_CHECK_EQUAL(test.GetArg("-value", "zzzzz"), "zzzzz"); - } else if (expect.string_value) { - BOOST_CHECK_EQUAL(test.GetArg("-value", "zzzzz"), expect.string_value); - } else { - BOOST_CHECK(!success); - } - - if (expect.default_int) { - BOOST_CHECK_EQUAL(test.GetArg("-value", 99999), 99999); - } else if (expect.int_value) { - BOOST_CHECK_EQUAL(test.GetArg("-value", 99999), *expect.int_value); - } else { - BOOST_CHECK(!success); - } - - if (expect.default_bool) { - BOOST_CHECK_EQUAL(test.GetBoolArg("-value", false), false); - BOOST_CHECK_EQUAL(test.GetBoolArg("-value", true), true); - } else if (expect.bool_value) { - BOOST_CHECK_EQUAL(test.GetBoolArg("-value", false), *expect.bool_value); - BOOST_CHECK_EQUAL(test.GetBoolArg("-value", true), *expect.bool_value); - } else { - BOOST_CHECK(!success); - } - - if (expect.list_value) { - auto l = test.GetArgs("-value"); - BOOST_CHECK_EQUAL_COLLECTIONS(l.begin(), l.end(), expect.list_value->begin(), expect.list_value->end()); - } else { - BOOST_CHECK(!success); - } - } -}; - -BOOST_FIXTURE_TEST_CASE(util_CheckValue, CheckValueTest) -{ - using M = ArgsManager; - - CheckValue(M::ALLOW_ANY, nullptr, Expect{{}}.DefaultString().DefaultInt().DefaultBool().List({})); - CheckValue(M::ALLOW_ANY, "-novalue", Expect{false}.String("0").Int(0).Bool(false).List({})); - CheckValue(M::ALLOW_ANY, "-novalue=", Expect{false}.String("0").Int(0).Bool(false).List({})); - CheckValue(M::ALLOW_ANY, "-novalue=0", Expect{true}.String("1").Int(1).Bool(true).List({"1"})); - CheckValue(M::ALLOW_ANY, "-novalue=1", Expect{false}.String("0").Int(0).Bool(false).List({})); - CheckValue(M::ALLOW_ANY, "-novalue=2", Expect{false}.String("0").Int(0).Bool(false).List({})); - CheckValue(M::ALLOW_ANY, "-novalue=abc", Expect{true}.String("1").Int(1).Bool(true).List({"1"})); - CheckValue(M::ALLOW_ANY, "-value", Expect{""}.String("").Int(0).Bool(true).List({""})); - CheckValue(M::ALLOW_ANY, "-value=", Expect{""}.String("").Int(0).Bool(true).List({""})); - CheckValue(M::ALLOW_ANY, "-value=0", Expect{"0"}.String("0").Int(0).Bool(false).List({"0"})); - CheckValue(M::ALLOW_ANY, "-value=1", Expect{"1"}.String("1").Int(1).Bool(true).List({"1"})); - CheckValue(M::ALLOW_ANY, "-value=2", Expect{"2"}.String("2").Int(2).Bool(true).List({"2"})); - CheckValue(M::ALLOW_ANY, "-value=abc", Expect{"abc"}.String("abc").Int(0).Bool(false).List({"abc"})); -} - -struct NoIncludeConfTest { - std::string Parse(const char* arg) - { - TestArgsManager test; - test.SetupArgs({{"-includeconf", ArgsManager::ALLOW_ANY}}); - std::array argv{"ignored", arg}; - std::string error; - (void)test.ParseParameters(argv.size(), argv.data(), error); - return error; - } -}; - -BOOST_FIXTURE_TEST_CASE(util_NoIncludeConf, NoIncludeConfTest) -{ - BOOST_CHECK_EQUAL(Parse("-noincludeconf"), ""); - BOOST_CHECK_EQUAL(Parse("-includeconf"), "-includeconf cannot be used from commandline; -includeconf=\"\""); - BOOST_CHECK_EQUAL(Parse("-includeconf=file"), "-includeconf cannot be used from commandline; -includeconf=\"file\""); -} - -BOOST_AUTO_TEST_CASE(util_ParseParameters) -{ - TestArgsManager testArgs; - const auto a = std::make_pair("-a", ArgsManager::ALLOW_ANY); - const auto b = std::make_pair("-b", ArgsManager::ALLOW_ANY); - const auto ccc = std::make_pair("-ccc", ArgsManager::ALLOW_ANY); - const auto d = std::make_pair("-d", ArgsManager::ALLOW_ANY); - - const char *argv_test[] = {"-ignored", "-a", "-b", "-ccc=argument", "-ccc=multiple", "f", "-d=e"}; - - std::string error; - LOCK(testArgs.cs_args); - testArgs.SetupArgs({a, b, ccc, d}); - BOOST_CHECK(testArgs.ParseParameters(0, (char**)argv_test, error)); - BOOST_CHECK(testArgs.m_settings.command_line_options.empty() && testArgs.m_settings.ro_config.empty()); - - BOOST_CHECK(testArgs.ParseParameters(1, (char**)argv_test, error)); - BOOST_CHECK(testArgs.m_settings.command_line_options.empty() && testArgs.m_settings.ro_config.empty()); - - BOOST_CHECK(testArgs.ParseParameters(7, (char**)argv_test, error)); - // expectation: -ignored is ignored (program name argument), - // -a, -b and -ccc end up in map, -d ignored because it is after - // a non-option argument (non-GNU option parsing) - BOOST_CHECK(testArgs.m_settings.command_line_options.size() == 3 && testArgs.m_settings.ro_config.empty()); - BOOST_CHECK(testArgs.IsArgSet("-a") && testArgs.IsArgSet("-b") && testArgs.IsArgSet("-ccc") - && !testArgs.IsArgSet("f") && !testArgs.IsArgSet("-d")); - BOOST_CHECK(testArgs.m_settings.command_line_options.count("a") && testArgs.m_settings.command_line_options.count("b") && testArgs.m_settings.command_line_options.count("ccc") - && !testArgs.m_settings.command_line_options.count("f") && !testArgs.m_settings.command_line_options.count("d")); - - BOOST_CHECK(testArgs.m_settings.command_line_options["a"].size() == 1); - BOOST_CHECK(testArgs.m_settings.command_line_options["a"].front().get_str() == ""); - BOOST_CHECK(testArgs.m_settings.command_line_options["ccc"].size() == 2); - BOOST_CHECK(testArgs.m_settings.command_line_options["ccc"].front().get_str() == "argument"); - BOOST_CHECK(testArgs.m_settings.command_line_options["ccc"].back().get_str() == "multiple"); - BOOST_CHECK(testArgs.GetArgs("-ccc").size() == 2); -} - -BOOST_AUTO_TEST_CASE(util_ParseInvalidParameters) -{ - TestArgsManager test; - test.SetupArgs({{"-registered", ArgsManager::ALLOW_ANY}}); - - const char* argv[] = {"ignored", "-registered"}; - std::string error; - BOOST_CHECK(test.ParseParameters(2, (char**)argv, error)); - BOOST_CHECK_EQUAL(error, ""); - - argv[1] = "-unregistered"; - BOOST_CHECK(!test.ParseParameters(2, (char**)argv, error)); - BOOST_CHECK_EQUAL(error, "Invalid parameter -unregistered"); - - // Make sure registered parameters prefixed with a chain name trigger errors. - // (Previously, they were accepted and ignored.) - argv[1] = "-test.registered"; - BOOST_CHECK(!test.ParseParameters(2, (char**)argv, error)); - BOOST_CHECK_EQUAL(error, "Invalid parameter -test.registered"); -} - -static void TestParse(const std::string& str, bool expected_bool, int64_t expected_int) -{ - TestArgsManager test; - test.SetupArgs({{"-value", ArgsManager::ALLOW_ANY}}); - std::string arg = "-value=" + str; - const char* argv[] = {"ignored", arg.c_str()}; - std::string error; - BOOST_CHECK(test.ParseParameters(2, (char**)argv, error)); - BOOST_CHECK_EQUAL(test.GetBoolArg("-value", false), expected_bool); - BOOST_CHECK_EQUAL(test.GetBoolArg("-value", true), expected_bool); - BOOST_CHECK_EQUAL(test.GetArg("-value", 99998), expected_int); - BOOST_CHECK_EQUAL(test.GetArg("-value", 99999), expected_int); -} - -// Test bool and int parsing. -BOOST_AUTO_TEST_CASE(util_ArgParsing) -{ - // Some of these cases could be ambiguous or surprising to users, and might - // be worth triggering errors or warnings in the future. But for now basic - // test coverage is useful to avoid breaking backwards compatibility - // unintentionally. - TestParse("", true, 0); - TestParse(" ", false, 0); - TestParse("0", false, 0); - TestParse("0 ", false, 0); - TestParse(" 0", false, 0); - TestParse("+0", false, 0); - TestParse("-0", false, 0); - TestParse("5", true, 5); - TestParse("5 ", true, 5); - TestParse(" 5", true, 5); - TestParse("+5", true, 5); - TestParse("-5", true, -5); - TestParse("0 5", false, 0); - TestParse("5 0", true, 5); - TestParse("050", true, 50); - TestParse("0.", false, 0); - TestParse("5.", true, 5); - TestParse("0.0", false, 0); - TestParse("0.5", false, 0); - TestParse("5.0", true, 5); - TestParse("5.5", true, 5); - TestParse("x", false, 0); - TestParse("x0", false, 0); - TestParse("x5", false, 0); - TestParse("0x", false, 0); - TestParse("5x", true, 5); - TestParse("0x5", false, 0); - TestParse("false", false, 0); - TestParse("true", false, 0); - TestParse("yes", false, 0); - TestParse("no", false, 0); -} - -BOOST_AUTO_TEST_CASE(util_GetBoolArg) -{ - TestArgsManager testArgs; - const auto a = std::make_pair("-a", ArgsManager::ALLOW_ANY); - const auto b = std::make_pair("-b", ArgsManager::ALLOW_ANY); - const auto c = std::make_pair("-c", ArgsManager::ALLOW_ANY); - const auto d = std::make_pair("-d", ArgsManager::ALLOW_ANY); - const auto e = std::make_pair("-e", ArgsManager::ALLOW_ANY); - const auto f = std::make_pair("-f", ArgsManager::ALLOW_ANY); - - const char *argv_test[] = { - "ignored", "-a", "-nob", "-c=0", "-d=1", "-e=false", "-f=true"}; - std::string error; - LOCK(testArgs.cs_args); - testArgs.SetupArgs({a, b, c, d, e, f}); - BOOST_CHECK(testArgs.ParseParameters(7, (char**)argv_test, error)); - - // Each letter should be set. - for (const char opt : "abcdef") - BOOST_CHECK(testArgs.IsArgSet({'-', opt}) || !opt); - - // Nothing else should be in the map - BOOST_CHECK(testArgs.m_settings.command_line_options.size() == 6 && - testArgs.m_settings.ro_config.empty()); - - // The -no prefix should get stripped on the way in. - BOOST_CHECK(!testArgs.IsArgSet("-nob")); - - // The -b option is flagged as negated, and nothing else is - BOOST_CHECK(testArgs.IsArgNegated("-b")); - BOOST_CHECK(!testArgs.IsArgNegated("-a")); - - // Check expected values. - BOOST_CHECK(testArgs.GetBoolArg("-a", false) == true); - BOOST_CHECK(testArgs.GetBoolArg("-b", true) == false); - BOOST_CHECK(testArgs.GetBoolArg("-c", true) == false); - BOOST_CHECK(testArgs.GetBoolArg("-d", false) == true); - BOOST_CHECK(testArgs.GetBoolArg("-e", true) == false); - BOOST_CHECK(testArgs.GetBoolArg("-f", true) == false); -} - -BOOST_AUTO_TEST_CASE(util_GetBoolArgEdgeCases) -{ - // Test some awful edge cases that hopefully no user will ever exercise. - TestArgsManager testArgs; - - // Params test - const auto foo = std::make_pair("-foo", ArgsManager::ALLOW_ANY); - const auto bar = std::make_pair("-bar", ArgsManager::ALLOW_ANY); - const char *argv_test[] = {"ignored", "-nofoo", "-foo", "-nobar=0"}; - testArgs.SetupArgs({foo, bar}); - std::string error; - BOOST_CHECK(testArgs.ParseParameters(4, (char**)argv_test, error)); - - // This was passed twice, second one overrides the negative setting. - BOOST_CHECK(!testArgs.IsArgNegated("-foo")); - BOOST_CHECK(testArgs.GetArg("-foo", "xxx") == ""); - - // A double negative is a positive, and not marked as negated. - BOOST_CHECK(!testArgs.IsArgNegated("-bar")); - BOOST_CHECK(testArgs.GetArg("-bar", "xxx") == "1"); - - // Config test - const char *conf_test = "nofoo=1\nfoo=1\nnobar=0\n"; - BOOST_CHECK(testArgs.ParseParameters(1, (char**)argv_test, error)); - testArgs.ReadConfigString(conf_test); - - // This was passed twice, second one overrides the negative setting, - // and the value. - BOOST_CHECK(!testArgs.IsArgNegated("-foo")); - BOOST_CHECK(testArgs.GetArg("-foo", "xxx") == "1"); - - // A double negative is a positive, and does not count as negated. - BOOST_CHECK(!testArgs.IsArgNegated("-bar")); - BOOST_CHECK(testArgs.GetArg("-bar", "xxx") == "1"); - - // Combined test - const char *combo_test_args[] = {"ignored", "-nofoo", "-bar"}; - const char *combo_test_conf = "foo=1\nnobar=1\n"; - BOOST_CHECK(testArgs.ParseParameters(3, (char**)combo_test_args, error)); - testArgs.ReadConfigString(combo_test_conf); - - // Command line overrides, but doesn't erase old setting - BOOST_CHECK(testArgs.IsArgNegated("-foo")); - BOOST_CHECK(testArgs.GetArg("-foo", "xxx") == "0"); - BOOST_CHECK(testArgs.GetArgs("-foo").size() == 0); - - // Command line overrides, but doesn't erase old setting - BOOST_CHECK(!testArgs.IsArgNegated("-bar")); - BOOST_CHECK(testArgs.GetArg("-bar", "xxx") == ""); - BOOST_CHECK(testArgs.GetArgs("-bar").size() == 1 - && testArgs.GetArgs("-bar").front() == ""); -} - -BOOST_AUTO_TEST_CASE(util_ReadConfigStream) -{ - const char *str_config = - "a=\n" - "b=1\n" - "ccc=argument\n" - "ccc=multiple\n" - "d=e\n" - "nofff=1\n" - "noggg=0\n" - "h=1\n" - "noh=1\n" - "noi=1\n" - "i=1\n" - "sec1.ccc=extend1\n" - "\n" - "[sec1]\n" - "ccc=extend2\n" - "d=eee\n" - "h=1\n" - "[sec2]\n" - "ccc=extend3\n" - "iii=2\n"; - - TestArgsManager test_args; - LOCK(test_args.cs_args); - const auto a = std::make_pair("-a", ArgsManager::ALLOW_ANY); - const auto b = std::make_pair("-b", ArgsManager::ALLOW_ANY); - const auto ccc = std::make_pair("-ccc", ArgsManager::ALLOW_ANY); - const auto d = std::make_pair("-d", ArgsManager::ALLOW_ANY); - const auto e = std::make_pair("-e", ArgsManager::ALLOW_ANY); - const auto fff = std::make_pair("-fff", ArgsManager::ALLOW_ANY); - const auto ggg = std::make_pair("-ggg", ArgsManager::ALLOW_ANY); - const auto h = std::make_pair("-h", ArgsManager::ALLOW_ANY); - const auto i = std::make_pair("-i", ArgsManager::ALLOW_ANY); - const auto iii = std::make_pair("-iii", ArgsManager::ALLOW_ANY); - test_args.SetupArgs({a, b, ccc, d, e, fff, ggg, h, i, iii}); - - test_args.ReadConfigString(str_config); - // expectation: a, b, ccc, d, fff, ggg, h, i end up in map - // so do sec1.ccc, sec1.d, sec1.h, sec2.ccc, sec2.iii - - BOOST_CHECK(test_args.m_settings.command_line_options.empty()); - BOOST_CHECK(test_args.m_settings.ro_config.size() == 3); - BOOST_CHECK(test_args.m_settings.ro_config[""].size() == 8); - BOOST_CHECK(test_args.m_settings.ro_config["sec1"].size() == 3); - BOOST_CHECK(test_args.m_settings.ro_config["sec2"].size() == 2); - - BOOST_CHECK(test_args.m_settings.ro_config[""].count("a")); - BOOST_CHECK(test_args.m_settings.ro_config[""].count("b")); - BOOST_CHECK(test_args.m_settings.ro_config[""].count("ccc")); - BOOST_CHECK(test_args.m_settings.ro_config[""].count("d")); - BOOST_CHECK(test_args.m_settings.ro_config[""].count("fff")); - BOOST_CHECK(test_args.m_settings.ro_config[""].count("ggg")); - BOOST_CHECK(test_args.m_settings.ro_config[""].count("h")); - BOOST_CHECK(test_args.m_settings.ro_config[""].count("i")); - BOOST_CHECK(test_args.m_settings.ro_config["sec1"].count("ccc")); - BOOST_CHECK(test_args.m_settings.ro_config["sec1"].count("h")); - BOOST_CHECK(test_args.m_settings.ro_config["sec2"].count("ccc")); - BOOST_CHECK(test_args.m_settings.ro_config["sec2"].count("iii")); - - BOOST_CHECK(test_args.IsArgSet("-a")); - BOOST_CHECK(test_args.IsArgSet("-b")); - BOOST_CHECK(test_args.IsArgSet("-ccc")); - BOOST_CHECK(test_args.IsArgSet("-d")); - BOOST_CHECK(test_args.IsArgSet("-fff")); - BOOST_CHECK(test_args.IsArgSet("-ggg")); - BOOST_CHECK(test_args.IsArgSet("-h")); - BOOST_CHECK(test_args.IsArgSet("-i")); - BOOST_CHECK(!test_args.IsArgSet("-zzz")); - BOOST_CHECK(!test_args.IsArgSet("-iii")); - - BOOST_CHECK_EQUAL(test_args.GetArg("-a", "xxx"), ""); - BOOST_CHECK_EQUAL(test_args.GetArg("-b", "xxx"), "1"); - BOOST_CHECK_EQUAL(test_args.GetArg("-ccc", "xxx"), "argument"); - BOOST_CHECK_EQUAL(test_args.GetArg("-d", "xxx"), "e"); - BOOST_CHECK_EQUAL(test_args.GetArg("-fff", "xxx"), "0"); - BOOST_CHECK_EQUAL(test_args.GetArg("-ggg", "xxx"), "1"); - BOOST_CHECK_EQUAL(test_args.GetArg("-h", "xxx"), "0"); - BOOST_CHECK_EQUAL(test_args.GetArg("-i", "xxx"), "1"); - BOOST_CHECK_EQUAL(test_args.GetArg("-zzz", "xxx"), "xxx"); - BOOST_CHECK_EQUAL(test_args.GetArg("-iii", "xxx"), "xxx"); - - for (const bool def : {false, true}) { - BOOST_CHECK(test_args.GetBoolArg("-a", def)); - BOOST_CHECK(test_args.GetBoolArg("-b", def)); - BOOST_CHECK(!test_args.GetBoolArg("-ccc", def)); - BOOST_CHECK(!test_args.GetBoolArg("-d", def)); - BOOST_CHECK(!test_args.GetBoolArg("-fff", def)); - BOOST_CHECK(test_args.GetBoolArg("-ggg", def)); - BOOST_CHECK(!test_args.GetBoolArg("-h", def)); - BOOST_CHECK(test_args.GetBoolArg("-i", def)); - BOOST_CHECK(test_args.GetBoolArg("-zzz", def) == def); - BOOST_CHECK(test_args.GetBoolArg("-iii", def) == def); - } - - BOOST_CHECK(test_args.GetArgs("-a").size() == 1 - && test_args.GetArgs("-a").front() == ""); - BOOST_CHECK(test_args.GetArgs("-b").size() == 1 - && test_args.GetArgs("-b").front() == "1"); - BOOST_CHECK(test_args.GetArgs("-ccc").size() == 2 - && test_args.GetArgs("-ccc").front() == "argument" - && test_args.GetArgs("-ccc").back() == "multiple"); - BOOST_CHECK(test_args.GetArgs("-fff").size() == 0); - BOOST_CHECK(test_args.GetArgs("-nofff").size() == 0); - BOOST_CHECK(test_args.GetArgs("-ggg").size() == 1 - && test_args.GetArgs("-ggg").front() == "1"); - BOOST_CHECK(test_args.GetArgs("-noggg").size() == 0); - BOOST_CHECK(test_args.GetArgs("-h").size() == 0); - BOOST_CHECK(test_args.GetArgs("-noh").size() == 0); - BOOST_CHECK(test_args.GetArgs("-i").size() == 1 - && test_args.GetArgs("-i").front() == "1"); - BOOST_CHECK(test_args.GetArgs("-noi").size() == 0); - BOOST_CHECK(test_args.GetArgs("-zzz").size() == 0); - - BOOST_CHECK(!test_args.IsArgNegated("-a")); - BOOST_CHECK(!test_args.IsArgNegated("-b")); - BOOST_CHECK(!test_args.IsArgNegated("-ccc")); - BOOST_CHECK(!test_args.IsArgNegated("-d")); - BOOST_CHECK(test_args.IsArgNegated("-fff")); - BOOST_CHECK(!test_args.IsArgNegated("-ggg")); - BOOST_CHECK(test_args.IsArgNegated("-h")); // last setting takes precedence - BOOST_CHECK(!test_args.IsArgNegated("-i")); // last setting takes precedence - BOOST_CHECK(!test_args.IsArgNegated("-zzz")); - - // Test sections work - test_args.SelectConfigNetwork("sec1"); - - // same as original - BOOST_CHECK_EQUAL(test_args.GetArg("-a", "xxx"), ""); - BOOST_CHECK_EQUAL(test_args.GetArg("-b", "xxx"), "1"); - BOOST_CHECK_EQUAL(test_args.GetArg("-fff", "xxx"), "0"); - BOOST_CHECK_EQUAL(test_args.GetArg("-ggg", "xxx"), "1"); - BOOST_CHECK_EQUAL(test_args.GetArg("-zzz", "xxx"), "xxx"); - BOOST_CHECK_EQUAL(test_args.GetArg("-iii", "xxx"), "xxx"); - // d is overridden - BOOST_CHECK(test_args.GetArg("-d", "xxx") == "eee"); - // section-specific setting - BOOST_CHECK(test_args.GetArg("-h", "xxx") == "1"); - // section takes priority for multiple values - BOOST_CHECK(test_args.GetArg("-ccc", "xxx") == "extend1"); - // check multiple values works - const std::vector sec1_ccc_expected = {"extend1","extend2","argument","multiple"}; - const auto& sec1_ccc_res = test_args.GetArgs("-ccc"); - BOOST_CHECK_EQUAL_COLLECTIONS(sec1_ccc_res.begin(), sec1_ccc_res.end(), sec1_ccc_expected.begin(), sec1_ccc_expected.end()); - - test_args.SelectConfigNetwork("sec2"); - - // same as original - BOOST_CHECK(test_args.GetArg("-a", "xxx") == ""); - BOOST_CHECK(test_args.GetArg("-b", "xxx") == "1"); - BOOST_CHECK(test_args.GetArg("-d", "xxx") == "e"); - BOOST_CHECK(test_args.GetArg("-fff", "xxx") == "0"); - BOOST_CHECK(test_args.GetArg("-ggg", "xxx") == "1"); - BOOST_CHECK(test_args.GetArg("-zzz", "xxx") == "xxx"); - BOOST_CHECK(test_args.GetArg("-h", "xxx") == "0"); - // section-specific setting - BOOST_CHECK(test_args.GetArg("-iii", "xxx") == "2"); - // section takes priority for multiple values - BOOST_CHECK(test_args.GetArg("-ccc", "xxx") == "extend3"); - // check multiple values works - const std::vector sec2_ccc_expected = {"extend3","argument","multiple"}; - const auto& sec2_ccc_res = test_args.GetArgs("-ccc"); - BOOST_CHECK_EQUAL_COLLECTIONS(sec2_ccc_res.begin(), sec2_ccc_res.end(), sec2_ccc_expected.begin(), sec2_ccc_expected.end()); - - // Test section only options - - test_args.SetNetworkOnlyArg("-d"); - test_args.SetNetworkOnlyArg("-ccc"); - test_args.SetNetworkOnlyArg("-h"); - - test_args.SelectConfigNetwork(CBaseChainParams::MAIN); - BOOST_CHECK(test_args.GetArg("-d", "xxx") == "e"); - BOOST_CHECK(test_args.GetArgs("-ccc").size() == 2); - BOOST_CHECK(test_args.GetArg("-h", "xxx") == "0"); - - test_args.SelectConfigNetwork("sec1"); - BOOST_CHECK(test_args.GetArg("-d", "xxx") == "eee"); - BOOST_CHECK(test_args.GetArgs("-d").size() == 1); - BOOST_CHECK(test_args.GetArgs("-ccc").size() == 2); - BOOST_CHECK(test_args.GetArg("-h", "xxx") == "1"); - - test_args.SelectConfigNetwork("sec2"); - BOOST_CHECK(test_args.GetArg("-d", "xxx") == "xxx"); - BOOST_CHECK(test_args.GetArgs("-d").size() == 0); - BOOST_CHECK(test_args.GetArgs("-ccc").size() == 1); - BOOST_CHECK(test_args.GetArg("-h", "xxx") == "0"); -} - -BOOST_AUTO_TEST_CASE(util_GetArg) -{ - TestArgsManager testArgs; - LOCK(testArgs.cs_args); - testArgs.m_settings.command_line_options.clear(); - testArgs.m_settings.command_line_options["strtest1"] = {"string..."}; - // strtest2 undefined on purpose - testArgs.m_settings.command_line_options["inttest1"] = {"12345"}; - testArgs.m_settings.command_line_options["inttest2"] = {"81985529216486895"}; - // inttest3 undefined on purpose - testArgs.m_settings.command_line_options["booltest1"] = {""}; - // booltest2 undefined on purpose - testArgs.m_settings.command_line_options["booltest3"] = {"0"}; - testArgs.m_settings.command_line_options["booltest4"] = {"1"}; - - // priorities - testArgs.m_settings.command_line_options["pritest1"] = {"a", "b"}; - testArgs.m_settings.ro_config[""]["pritest2"] = {"a", "b"}; - testArgs.m_settings.command_line_options["pritest3"] = {"a"}; - testArgs.m_settings.ro_config[""]["pritest3"] = {"b"}; - testArgs.m_settings.command_line_options["pritest4"] = {"a","b"}; - testArgs.m_settings.ro_config[""]["pritest4"] = {"c","d"}; - - BOOST_CHECK_EQUAL(testArgs.GetArg("strtest1", "default"), "string..."); - BOOST_CHECK_EQUAL(testArgs.GetArg("strtest2", "default"), "default"); - BOOST_CHECK_EQUAL(testArgs.GetArg("inttest1", -1), 12345); - BOOST_CHECK_EQUAL(testArgs.GetArg("inttest2", -1), 81985529216486895LL); - BOOST_CHECK_EQUAL(testArgs.GetArg("inttest3", -1), -1); - BOOST_CHECK_EQUAL(testArgs.GetBoolArg("booltest1", false), true); - BOOST_CHECK_EQUAL(testArgs.GetBoolArg("booltest2", false), false); - BOOST_CHECK_EQUAL(testArgs.GetBoolArg("booltest3", false), false); - BOOST_CHECK_EQUAL(testArgs.GetBoolArg("booltest4", false), true); - - BOOST_CHECK_EQUAL(testArgs.GetArg("pritest1", "default"), "b"); - BOOST_CHECK_EQUAL(testArgs.GetArg("pritest2", "default"), "a"); - BOOST_CHECK_EQUAL(testArgs.GetArg("pritest3", "default"), "a"); - BOOST_CHECK_EQUAL(testArgs.GetArg("pritest4", "default"), "b"); -} - -BOOST_AUTO_TEST_CASE(util_GetChainName) -{ - TestArgsManager test_args; - const auto testnet = std::make_pair("-testnet", ArgsManager::ALLOW_ANY); - const auto regtest = std::make_pair("-regtest", ArgsManager::ALLOW_ANY); - test_args.SetupArgs({testnet, regtest}); - - const char* argv_testnet[] = {"cmd", "-testnet"}; - const char* argv_regtest[] = {"cmd", "-regtest"}; - const char* argv_test_no_reg[] = {"cmd", "-testnet", "-noregtest"}; - const char* argv_both[] = {"cmd", "-testnet", "-regtest"}; - - // equivalent to "-testnet" - // regtest in testnet section is ignored - const char* testnetconf = "testnet=1\nregtest=0\n[test]\nregtest=1"; - std::string error; - - BOOST_CHECK(test_args.ParseParameters(0, (char**)argv_testnet, error)); - BOOST_CHECK_EQUAL(test_args.GetChainName(), "main"); - - BOOST_CHECK(test_args.ParseParameters(2, (char**)argv_testnet, error)); - BOOST_CHECK_EQUAL(test_args.GetChainName(), "test"); - - BOOST_CHECK(test_args.ParseParameters(2, (char**)argv_regtest, error)); - BOOST_CHECK_EQUAL(test_args.GetChainName(), "regtest"); - - BOOST_CHECK(test_args.ParseParameters(3, (char**)argv_test_no_reg, error)); - BOOST_CHECK_EQUAL(test_args.GetChainName(), "test"); - - BOOST_CHECK(test_args.ParseParameters(3, (char**)argv_both, error)); - BOOST_CHECK_THROW(test_args.GetChainName(), std::runtime_error); - - BOOST_CHECK(test_args.ParseParameters(0, (char**)argv_testnet, error)); - test_args.ReadConfigString(testnetconf); - BOOST_CHECK_EQUAL(test_args.GetChainName(), "test"); - - BOOST_CHECK(test_args.ParseParameters(2, (char**)argv_testnet, error)); - test_args.ReadConfigString(testnetconf); - BOOST_CHECK_EQUAL(test_args.GetChainName(), "test"); - - BOOST_CHECK(test_args.ParseParameters(2, (char**)argv_regtest, error)); - test_args.ReadConfigString(testnetconf); - BOOST_CHECK_THROW(test_args.GetChainName(), std::runtime_error); - - BOOST_CHECK(test_args.ParseParameters(3, (char**)argv_test_no_reg, error)); - test_args.ReadConfigString(testnetconf); - BOOST_CHECK_EQUAL(test_args.GetChainName(), "test"); - - BOOST_CHECK(test_args.ParseParameters(3, (char**)argv_both, error)); - test_args.ReadConfigString(testnetconf); - BOOST_CHECK_THROW(test_args.GetChainName(), std::runtime_error); - - // check setting the network to test (and thus making - // [test] regtest=1 potentially relevant) doesn't break things - test_args.SelectConfigNetwork("test"); - - BOOST_CHECK(test_args.ParseParameters(0, (char**)argv_testnet, error)); - test_args.ReadConfigString(testnetconf); - BOOST_CHECK_EQUAL(test_args.GetChainName(), "test"); - - BOOST_CHECK(test_args.ParseParameters(2, (char**)argv_testnet, error)); - test_args.ReadConfigString(testnetconf); - BOOST_CHECK_EQUAL(test_args.GetChainName(), "test"); - - BOOST_CHECK(test_args.ParseParameters(2, (char**)argv_regtest, error)); - test_args.ReadConfigString(testnetconf); - BOOST_CHECK_THROW(test_args.GetChainName(), std::runtime_error); - - BOOST_CHECK(test_args.ParseParameters(2, (char**)argv_test_no_reg, error)); - test_args.ReadConfigString(testnetconf); - BOOST_CHECK_EQUAL(test_args.GetChainName(), "test"); - - BOOST_CHECK(test_args.ParseParameters(3, (char**)argv_both, error)); - test_args.ReadConfigString(testnetconf); - BOOST_CHECK_THROW(test_args.GetChainName(), std::runtime_error); -} - -// Test different ways settings can be merged, and verify results. This test can -// be used to confirm that updates to settings code don't change behavior -// unintentionally. -// -// The test covers: -// -// - Combining different setting actions. Possible actions are: configuring a -// setting, negating a setting (adding "-no" prefix), and configuring/negating -// settings in a network section (adding "main." or "test." prefixes). -// -// - Combining settings from command line arguments and a config file. -// -// - Combining SoftSet and ForceSet calls. -// -// - Testing "main" and "test" network values to make sure settings from network -// sections are applied and to check for mainnet-specific behaviors like -// inheriting settings from the default section. -// -// - Testing network-specific settings like "-wallet", that may be ignored -// outside a network section, and non-network specific settings like "-server" -// that aren't sensitive to the network. -// -struct ArgsMergeTestingSetup : public BasicTestingSetup { - //! Max number of actions to sequence together. Can decrease this when - //! debugging to make test results easier to understand. - static constexpr int MAX_ACTIONS = 3; - - enum Action { NONE, SET, NEGATE, SECTION_SET, SECTION_NEGATE }; - using ActionList = Action[MAX_ACTIONS]; - - //! Enumerate all possible test configurations. - template - void ForEachMergeSetup(Fn&& fn) - { - ActionList arg_actions = {}; - // command_line_options do not have sections. Only iterate over SET and NEGATE - ForEachNoDup(arg_actions, SET, NEGATE, [&] { - ActionList conf_actions = {}; - ForEachNoDup(conf_actions, SET, SECTION_NEGATE, [&] { - for (bool soft_set : {false, true}) { - for (bool force_set : {false, true}) { - for (const std::string& section : {CBaseChainParams::MAIN, CBaseChainParams::TESTNET}) { - for (const std::string& network : {CBaseChainParams::MAIN, CBaseChainParams::TESTNET}) { - for (bool net_specific : {false, true}) { - fn(arg_actions, conf_actions, soft_set, force_set, section, network, net_specific); - } - } - } - } - } - }); - }); - } - - //! Translate actions into a list of = setting strings. - std::vector GetValues(const ActionList& actions, - const std::string& section, - const std::string& name, - const std::string& value_prefix) - { - std::vector values; - int suffix = 0; - for (Action action : actions) { - if (action == NONE) break; - std::string prefix; - if (action == SECTION_SET || action == SECTION_NEGATE) prefix = section + "."; - if (action == SET || action == SECTION_SET) { - for (int i = 0; i < 2; ++i) { - values.push_back(prefix + name + "=" + value_prefix + ToString(++suffix)); - } - } - if (action == NEGATE || action == SECTION_NEGATE) { - values.push_back(prefix + "no" + name + "=1"); - } - } - return values; - } -}; - -// Regression test covering different ways config settings can be merged. The -// test parses and merges settings, representing the results as strings that get -// compared against an expected hash. To debug, the result strings can be dumped -// to a file (see comments below). -BOOST_FIXTURE_TEST_CASE(util_ArgsMerge, ArgsMergeTestingSetup) -{ - CHash256 out_sha; - FILE* out_file = nullptr; - if (const char* out_path = getenv("ARGS_MERGE_TEST_OUT")) { - out_file = fsbridge::fopen(out_path, "w"); - if (!out_file) throw std::system_error(errno, std::generic_category(), "fopen failed"); - } - - ForEachMergeSetup([&](const ActionList& arg_actions, const ActionList& conf_actions, bool soft_set, bool force_set, - const std::string& section, const std::string& network, bool net_specific) { - TestArgsManager parser; - LOCK(parser.cs_args); - - std::string desc = "net="; - desc += network; - parser.m_network = network; - - const std::string& name = net_specific ? "wallet" : "server"; - const std::string key = "-" + name; - parser.AddArg(key, name, ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); - if (net_specific) parser.SetNetworkOnlyArg(key); - - auto args = GetValues(arg_actions, section, name, "a"); - std::vector argv = {"ignored"}; - for (auto& arg : args) { - arg.insert(0, "-"); - desc += " "; - desc += arg; - argv.push_back(arg.c_str()); - } - std::string error; - BOOST_CHECK(parser.ParseParameters(argv.size(), argv.data(), error)); - BOOST_CHECK_EQUAL(error, ""); - - std::string conf; - for (auto& conf_val : GetValues(conf_actions, section, name, "c")) { - desc += " "; - desc += conf_val; - conf += conf_val; - conf += "\n"; - } - std::istringstream conf_stream(conf); - BOOST_CHECK(parser.ReadConfigStream(conf_stream, "filepath", error)); - BOOST_CHECK_EQUAL(error, ""); - - if (soft_set) { - desc += " soft"; - parser.SoftSetArg(key, "soft1"); - parser.SoftSetArg(key, "soft2"); - } - - if (force_set) { - desc += " force"; - parser.ForceSetArg(key, "force1"); - parser.ForceSetArg(key, "force2"); - } - - desc += " || "; - - if (!parser.IsArgSet(key)) { - desc += "unset"; - BOOST_CHECK(!parser.IsArgNegated(key)); - BOOST_CHECK_EQUAL(parser.GetArg(key, "default"), "default"); - BOOST_CHECK(parser.GetArgs(key).empty()); - } else if (parser.IsArgNegated(key)) { - desc += "negated"; - BOOST_CHECK_EQUAL(parser.GetArg(key, "default"), "0"); - BOOST_CHECK(parser.GetArgs(key).empty()); - } else { - desc += parser.GetArg(key, "default"); - desc += " |"; - for (const auto& arg : parser.GetArgs(key)) { - desc += " "; - desc += arg; - } - } - - std::set ignored = parser.GetUnsuitableSectionOnlyArgs(); - if (!ignored.empty()) { - desc += " | ignored"; - for (const auto& arg : ignored) { - desc += " "; - desc += arg; - } - } - - desc += "\n"; - - out_sha.Write(MakeUCharSpan(desc)); - if (out_file) { - BOOST_REQUIRE(fwrite(desc.data(), 1, desc.size(), out_file) == desc.size()); - } - }); - - if (out_file) { - if (fclose(out_file)) throw std::system_error(errno, std::generic_category(), "fclose failed"); - out_file = nullptr; - } - - unsigned char out_sha_bytes[CSHA256::OUTPUT_SIZE]; - out_sha.Finalize(out_sha_bytes); - std::string out_sha_hex = HexStr(out_sha_bytes); - - // If check below fails, should manually dump the results with: - // - // ARGS_MERGE_TEST_OUT=results.txt ./test_dash --run_test=util_tests/util_ArgsMerge - // - // And verify diff against previous results to make sure the changes are expected. - // - // Results file is formatted like: - // - // || | | - BOOST_CHECK_EQUAL(out_sha_hex, "8fd4877bb8bf337badca950ede6c917441901962f160e52514e06a60dea46cde"); -} - -// Similar test as above, but for ArgsManager::GetChainName function. -struct ChainMergeTestingSetup : public BasicTestingSetup { - static constexpr int MAX_ACTIONS = 2; - - enum Action { NONE, ENABLE_TEST, DISABLE_TEST, NEGATE_TEST, ENABLE_REG, DISABLE_REG, NEGATE_REG }; - using ActionList = Action[MAX_ACTIONS]; - - //! Enumerate all possible test configurations. - template - void ForEachMergeSetup(Fn&& fn) - { - ActionList arg_actions = {}; - ForEachNoDup(arg_actions, ENABLE_TEST, NEGATE_REG, [&] { - ActionList conf_actions = {}; - ForEachNoDup(conf_actions, ENABLE_TEST, NEGATE_REG, [&] { fn(arg_actions, conf_actions); }); - }); - } -}; - -BOOST_FIXTURE_TEST_CASE(util_ChainMerge, ChainMergeTestingSetup) -{ - CHash256 out_sha; - FILE* out_file = nullptr; - if (const char* out_path = getenv("CHAIN_MERGE_TEST_OUT")) { - out_file = fsbridge::fopen(out_path, "w"); - if (!out_file) throw std::system_error(errno, std::generic_category(), "fopen failed"); - } - - ForEachMergeSetup([&](const ActionList& arg_actions, const ActionList& conf_actions) { - TestArgsManager parser; - LOCK(parser.cs_args); - parser.AddArg("-regtest", "regtest", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); - parser.AddArg("-testnet", "testnet", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); - - auto arg = [](Action action) { return action == ENABLE_TEST ? "-testnet=1" : - action == DISABLE_TEST ? "-testnet=0" : - action == NEGATE_TEST ? "-notestnet=1" : - action == ENABLE_REG ? "-regtest=1" : - action == DISABLE_REG ? "-regtest=0" : - action == NEGATE_REG ? "-noregtest=1" : nullptr; }; - - std::string desc; - std::vector argv = {"ignored"}; - for (Action action : arg_actions) { - const char* argstr = arg(action); - if (!argstr) break; - argv.push_back(argstr); - desc += " "; - desc += argv.back(); - } - std::string error; - BOOST_CHECK(parser.ParseParameters(argv.size(), argv.data(), error)); - BOOST_CHECK_EQUAL(error, ""); - - std::string conf; - for (Action action : conf_actions) { - const char* argstr = arg(action); - if (!argstr) break; - desc += " "; - desc += argstr + 1; - conf += argstr + 1; - conf += "\n"; - } - std::istringstream conf_stream(conf); - BOOST_CHECK(parser.ReadConfigStream(conf_stream, "filepath", error)); - BOOST_CHECK_EQUAL(error, ""); - - desc += " || "; - try { - desc += parser.GetChainName(); - } catch (const std::runtime_error& e) { - desc += "error: "; - desc += e.what(); - } - desc += "\n"; - - out_sha.Write(MakeUCharSpan(desc)); - if (out_file) { - BOOST_REQUIRE(fwrite(desc.data(), 1, desc.size(), out_file) == desc.size()); - } - }); - - if (out_file) { - if (fclose(out_file)) throw std::system_error(errno, std::generic_category(), "fclose failed"); - out_file = nullptr; - } - - unsigned char out_sha_bytes[CSHA256::OUTPUT_SIZE]; - out_sha.Finalize(out_sha_bytes); - std::string out_sha_hex = HexStr(out_sha_bytes); - - // If check below fails, should manually dump the results with: - // - // CHAIN_MERGE_TEST_OUT=results.txt ./test_dash --run_test=util_tests/util_ChainMerge - // - // And verify diff against previous results to make sure the changes are expected. - // - // Results file is formatted like: - // - // || - BOOST_CHECK_EQUAL(out_sha_hex, "1cd2697d1665d086f15ed18b3333bc356c3d39ba490589dc58afd8fc2f457731"); -} - -BOOST_AUTO_TEST_CASE(util_ReadWriteSettings) -{ - // Test writing setting. - TestArgsManager args1; - args1.ForceSetArg("-datadir", fs::PathToString(m_path_root)); - args1.LockSettings([&](util::Settings& settings) { settings.rw_settings["name"] = "value"; }); - args1.WriteSettingsFile(); - - // Test reading setting. - TestArgsManager args2; - args2.ForceSetArg("-datadir", fs::PathToString(m_path_root)); - args2.ReadSettingsFile(); - args2.LockSettings([&](util::Settings& settings) { BOOST_CHECK_EQUAL(settings.rw_settings["name"].get_str(), "value"); }); - - // Test error logging, and remove previously written setting. - { - ASSERT_DEBUG_LOG("Failed renaming settings file"); - fs::remove(args1.GetDataDirBase() / "settings.json"); - fs::create_directory(args1.GetDataDirBase() / "settings.json"); - args2.WriteSettingsFile(); - fs::remove(args1.GetDataDirBase() / "settings.json"); - } -} - BOOST_AUTO_TEST_CASE(util_FormatMoney) { BOOST_CHECK_EQUAL(FormatMoney(0), "0.00"); diff --git a/src/util/asmap.cpp b/src/util/asmap.cpp index 21ff6cc3ce..3b63ad44a4 100644 --- a/src/util/asmap.cpp +++ b/src/util/asmap.cpp @@ -5,11 +5,11 @@ #include #include -#include #include #include #include +#include #include #include #include @@ -108,7 +108,7 @@ uint32_t Interpret(const std::vector &asmap, const std::vector &ip) } else if (opcode == Instruction::MATCH) { match = DecodeMatch(pos, endpos); if (match == INVALID) break; // Match bits straddle EOF - matchlen = CountBits(match) - 1; + matchlen = std::bit_width(match) - 1; if (bits < matchlen) break; // Not enough input bits for (uint32_t bit = 0; bit < matchlen; bit++) { if ((ip[ip.size() - bits]) != ((match >> (matchlen - 1 - bit)) & 1)) { @@ -172,7 +172,7 @@ bool SanityCheckASMap(const std::vector& asmap, int bits) } else if (opcode == Instruction::MATCH) { uint32_t match = DecodeMatch(pos, endpos); if (match == INVALID) return false; // Match bits straddle EOF - int matchlen = CountBits(match) - 1; + int matchlen = std::bit_width(match) - 1; if (prevopcode != Instruction::MATCH) had_incomplete_match = false; if (matchlen < 8 && had_incomplete_match) return false; // Within a sequence of matches only at most one should be incomplete had_incomplete_match = (matchlen < 8); diff --git a/src/util/bip32.cpp b/src/util/bip32.cpp index 4c7e948368..d2b9aaef1d 100644 --- a/src/util/bip32.cpp +++ b/src/util/bip32.cpp @@ -3,11 +3,11 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include -#include #include #include #include +#include bool ParseHDKeypath(const std::string& keypath_str, std::vector& keypath) { diff --git a/src/util/readwritefile.cpp b/src/util/readwritefile.cpp index 7bf0c7dd8a..fe183627f5 100644 --- a/src/util/readwritefile.cpp +++ b/src/util/readwritefile.cpp @@ -7,8 +7,8 @@ #include +#include #include -#include #include #include diff --git a/src/util/url.cpp b/src/util/url.cpp index e42d93bce8..ea9323e666 100644 --- a/src/util/url.cpp +++ b/src/util/url.cpp @@ -5,7 +5,8 @@ #include #include -#include + +#include #include std::string urlDecode(const std::string &urlEncoded) { diff --git a/src/wallet/bdb.cpp b/src/wallet/bdb.cpp index ef6947a25f..39e27e5ea6 100644 --- a/src/wallet/bdb.cpp +++ b/src/wallet/bdb.cpp @@ -25,6 +25,10 @@ #endif namespace { +Span SpanFromDbt(const BerkeleyBatch::SafeDbt& dbt) +{ + return {reinterpret_cast(dbt.get_data()), dbt.get_size()}; +} //! Make sure database has a unique fileid within the environment. If it //! doesn't, throw an error. BDB caches do not work properly when more than one @@ -689,10 +693,10 @@ bool BerkeleyBatch::ReadAtCursor(CDataStream& ssKey, CDataStream& ssValue, bool& // Convert to streams ssKey.SetType(SER_DISK); ssKey.clear(); - ssKey.write({BytePtr(datKey.get_data()), datKey.get_size()}); + ssKey.write(SpanFromDbt(datKey)); ssValue.SetType(SER_DISK); ssValue.clear(); - ssValue.write({BytePtr(datValue.get_data()), datValue.get_size()}); + ssValue.write(SpanFromDbt(datValue)); return true; } @@ -764,7 +768,8 @@ bool BerkeleyBatch::ReadKey(CDataStream&& key, CDataStream& value) SafeDbt datValue; int ret = pdb->get(activeTxn, datKey, datValue, 0); if (ret == 0 && datValue.get_data() != nullptr) { - value.write({BytePtr(datValue.get_data()), datValue.get_size()}); + value.clear(); + value.write(SpanFromDbt(datValue)); return true; } return false; diff --git a/src/wallet/bdb.h b/src/wallet/bdb.h index f09e76047b..62e6c4107b 100644 --- a/src/wallet/bdb.h +++ b/src/wallet/bdb.h @@ -168,6 +168,7 @@ public: /** RAII class that provides access to a Berkeley database */ class BerkeleyBatch : public DatabaseBatch { +public: /** RAII class that automatically cleanses its data on destruction */ class SafeDbt final { diff --git a/src/wallet/crypter.cpp b/src/wallet/crypter.cpp index 93801b24e5..7fd8b38b65 100644 --- a/src/wallet/crypter.cpp +++ b/src/wallet/crypter.cpp @@ -111,7 +111,7 @@ bool EncryptSecret(const CKeyingMaterial& vMasterKey, const CKeyingMaterial &vch memcpy(chIV.data(), &nIV, WALLET_CRYPTO_IV_SIZE); if(!cKeyCrypter.SetKey(vMasterKey, chIV)) return false; - return cKeyCrypter.Encrypt(*((const CKeyingMaterial*)&vchPlaintext), vchCiphertext); + return cKeyCrypter.Encrypt(vchPlaintext, vchCiphertext); } // General secure AES 256 CBC encryption routine diff --git a/src/wallet/sqlite.cpp b/src/wallet/sqlite.cpp index f41b27d28f..6a82a29954 100644 --- a/src/wallet/sqlite.cpp +++ b/src/wallet/sqlite.cpp @@ -25,6 +25,12 @@ static constexpr int32_t WALLET_SCHEMA_VERSION = 0; static Mutex g_sqlite_mutex; static int g_sqlite_count GUARDED_BY(g_sqlite_mutex) = 0; +static Span SpanFromBlob(sqlite3_stmt* stmt, int col) +{ + return {reinterpret_cast(sqlite3_column_blob(stmt, col)), + static_cast(sqlite3_column_bytes(stmt, col))}; +} + static void ErrorLogCallback(void* arg, int code, const char* msg) { // From sqlite3_config() documentation for the SQLITE_CONFIG_LOG option: @@ -404,9 +410,8 @@ bool SQLiteBatch::ReadKey(CDataStream&& key, CDataStream& value) return false; } // Leftmost column in result is index 0 - const std::byte* data{BytePtr(sqlite3_column_blob(m_read_stmt, 0))}; - size_t data_size(sqlite3_column_bytes(m_read_stmt, 0)); - value.write({data, data_size}); + value.clear(); + value.write(SpanFromBlob(m_read_stmt, 0)); sqlite3_clear_bindings(m_read_stmt); sqlite3_reset(m_read_stmt); @@ -495,13 +500,12 @@ bool SQLiteBatch::ReadAtCursor(CDataStream& key, CDataStream& value, bool& compl return false; } + key.clear(); + value.clear(); + // Leftmost column in result is index 0 - const std::byte* key_data{BytePtr(sqlite3_column_blob(m_cursor_stmt, 0))}; - size_t key_data_size(sqlite3_column_bytes(m_cursor_stmt, 0)); - key.write({key_data, key_data_size}); - const std::byte* value_data{BytePtr(sqlite3_column_blob(m_cursor_stmt, 1))}; - size_t value_data_size(sqlite3_column_bytes(m_cursor_stmt, 1)); - value.write({value_data, value_data_size}); + key.write(SpanFromBlob(m_cursor_stmt, 0)); + value.write(SpanFromBlob(m_cursor_stmt, 1)); return true; }