diff --git a/ci/test/00_setup_env_native_qt5.sh b/ci/test/00_setup_env_native_qt5.sh index a25dc2cb6bfec..3727fb9aeaa1f 100755 --- a/ci/test/00_setup_env_native_qt5.sh +++ b/ci/test/00_setup_env_native_qt5.sh @@ -14,4 +14,4 @@ export DEP_OPTS="NO_UPNP=1 DEBUG=1" export RUN_UNIT_TESTS_SEQUENTIAL="true" export RUN_UNIT_TESTS="false" export GOAL="install" -export BITCOIN_CONFIG="--enable-zmq --enable-reduce-exports LDFLAGS=-static-libstdc++" +export BITCOIN_CONFIG="--enable-zmq --enable-reduce-exports --disable-fuzz-binary LDFLAGS=-static-libstdc++" diff --git a/configure.ac b/configure.ac index 1ddd7f51f8c78..2e1bf83a61509 100644 --- a/configure.ac +++ b/configure.ac @@ -178,10 +178,16 @@ AC_ARG_ENABLE([extended-functional-tests], AC_ARG_ENABLE([fuzz], AS_HELP_STRING([--enable-fuzz], - [enable building of fuzz targets (default no). enabling this will disable all other targets]), + [build for fuzzing (default no). enabling this will disable all other targets and override --{enable,disable}-fuzz-binary]), [enable_fuzz=$enableval], [enable_fuzz=no]) +AC_ARG_ENABLE([fuzz-binary], + AS_HELP_STRING([--enable-fuzz-binary], + [enable building of fuzz binary (default yes).]), + [enable_fuzz_binary=$enableval], + [enable_fuzz_binary=yes]) + AC_ARG_ENABLE([danger_fuzz_link_all], AS_HELP_STRING([--enable-danger-fuzz-link-all], [Danger! Modifies source code. Needs git and gnu sed installed. Link each fuzz target (default no).]), @@ -1275,7 +1281,7 @@ AC_DEFUN([SUPPRESS_WARNINGS], dnl enable-fuzz should disable all other targets if test "x$enable_fuzz" = "xyes"; then - AC_MSG_WARN(enable-fuzz will disable all other targets) + AC_MSG_WARN(enable-fuzz will disable all other targets and force --enable-fuzz-binary=yes) build_bitcoin_utils=no build_bitcoin_cli=no build_bitcoin_tx=no @@ -1290,15 +1296,16 @@ if test "x$enable_fuzz" = "xyes"; then use_upnp=no use_natpmp=no use_zmq=no + enable_fuzz_binary=yes AX_CHECK_PREPROC_FLAG([-DABORT_ON_FAILED_ASSUME],[[DEBUG_CPPFLAGS="$DEBUG_CPPFLAGS -DABORT_ON_FAILED_ASSUME"]],,[[$CXXFLAG_WERROR]]) - AC_MSG_CHECKING([whether main function is needed]) + AC_MSG_CHECKING([whether main function is needed for fuzz binary]) AX_CHECK_LINK_FLAG( [[-fsanitize=$use_sanitizers]], [AC_MSG_RESULT([no])], [AC_MSG_RESULT([yes]) - CPPFLAGS="$CPPFLAGS -DPROVIDE_MAIN_FUNCTION"], + CPPFLAGS="$CPPFLAGS -DPROVIDE_FUZZ_MAIN_FUNCTION"], [], [AC_LANG_PROGRAM([[ #include @@ -1321,6 +1328,8 @@ else QT_DBUS_INCLUDES=SUPPRESS_WARNINGS($QT_DBUS_INCLUDES) QT_TEST_INCLUDES=SUPPRESS_WARNINGS($QT_TEST_INCLUDES) fi + + CPPFLAGS="$CPPFLAGS -DPROVIDE_FUZZ_MAIN_FUNCTION" fi if test x$enable_wallet != xno; then @@ -1733,6 +1742,7 @@ AM_CONDITIONAL([USE_SQLITE], [test "x$use_sqlite" = "xyes"]) AM_CONDITIONAL([USE_BDB], [test "x$use_bdb" = "xyes"]) AM_CONDITIONAL([ENABLE_TESTS],[test x$BUILD_TEST = xyes]) AM_CONDITIONAL([ENABLE_FUZZ],[test x$enable_fuzz = xyes]) +AM_CONDITIONAL([ENABLE_FUZZ_BINARY],[test x$enable_fuzz_binary = xyes]) AM_CONDITIONAL([ENABLE_FUZZ_LINK_ALL],[test x$enable_danger_fuzz_link_all = xyes]) AM_CONDITIONAL([ENABLE_QT],[test x$bitcoin_enable_qt = xyes]) AM_CONDITIONAL([ENABLE_QT_TESTS],[test x$BUILD_TEST_QT = xyes]) @@ -1748,6 +1758,8 @@ AM_CONDITIONAL([ENABLE_ARM_CRC],[test x$enable_arm_crc = xyes]) AM_CONDITIONAL([ENABLE_ARM_SHANI], [test "$enable_arm_shani" = "yes"]) AM_CONDITIONAL([USE_ASM],[test x$use_asm = xyes]) AM_CONDITIONAL([WORDS_BIGENDIAN],[test x$ac_cv_c_bigendian = xyes]) +AM_CONDITIONAL([USE_NATPMP],[test x$use_natpmp = xyes]) +AM_CONDITIONAL([USE_UPNP],[test x$use_upnp = xyes]) AC_DEFINE(CLIENT_VERSION_MAJOR, _CLIENT_VERSION_MAJOR, [Major version]) AC_DEFINE(CLIENT_VERSION_MINOR, _CLIENT_VERSION_MINOR, [Minor version]) diff --git a/src/Makefile.am b/src/Makefile.am index b6b6f658b0814..d12ecaf5f2772 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -508,6 +508,7 @@ libbitcoin_wallet_a_SOURCES = \ coinjoin/client.cpp \ coinjoin/options.cpp \ coinjoin/util.cpp \ + hdchain.cpp \ wallet/coincontrol.cpp \ wallet/context.cpp \ wallet/crypter.cpp \ @@ -682,7 +683,6 @@ libbitcoin_common_a_SOURCES = \ compressor.cpp \ core_read.cpp \ core_write.cpp \ - hdchain.cpp \ key.cpp \ key_io.cpp \ merkleblock.cpp \ diff --git a/src/Makefile.test.include b/src/Makefile.test.include index 5907bcccdcea5..b425f6444e588 100644 --- a/src/Makefile.test.include +++ b/src/Makefile.test.include @@ -3,9 +3,11 @@ # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. -if ENABLE_FUZZ +if ENABLE_FUZZ_BINARY noinst_PROGRAMS += test/fuzz/fuzz -else +endif + +if !ENABLE_FUZZ bin_PROGRAMS += test/test_dash endif @@ -61,6 +63,14 @@ FUZZ_SUITE_LD_COMMON = \ $(GMP_LIBS) \ $(BACKTRACE_LIB) +if USE_UPNP +FUZZ_SUITE_LD_COMMON += $(MINIUPNPC_LIBS) +endif + +if USE_NATPMP +FUZZ_SUITE_LD_COMMON += $(NATPMP_LIBS) +endif + # test_dash binary # BITCOIN_TESTS =\ test/arith_uint256_tests.cpp \ @@ -178,10 +188,16 @@ BITCOIN_TESTS += \ wallet/test/ismine_tests.cpp \ wallet/test/scriptpubkeyman_tests.cpp +FUZZ_SUITE_LD_COMMON +=\ + $(LIBBITCOIN_WALLET) \ + $(SQLITE_LIBS) \ + $(BDB_LIBS) + if USE_BDB BITCOIN_TESTS += wallet/test/db_tests.cpp endif + BITCOIN_TEST_SUITE += \ wallet/test/wallet_test_fixture.cpp \ wallet/test/wallet_test_fixture.h \ @@ -204,11 +220,12 @@ test_test_dash_LDFLAGS = $(LDFLAGS_WRAP_EXCEPTIONS) $(RELDFLAGS) $(AM_LDFLAGS) $ if ENABLE_ZMQ test_test_dash_LDADD += $(LIBBITCOIN_ZMQ) $(ZMQ_LIBS) +FUZZ_SUITE_LD_COMMON += $(LIBBITCOIN_ZMQ) $(ZMQ_LIBS) endif -if ENABLE_FUZZ FUZZ_SUITE_LDFLAGS_COMMON = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) $(LDFLAGS_WRAP_EXCEPTIONS) $(PTHREAD_FLAGS) +if ENABLE_FUZZ_BINARY test_fuzz_fuzz_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) test_fuzz_fuzz_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) test_fuzz_fuzz_LDADD = $(FUZZ_SUITE_LD_COMMON) @@ -240,6 +257,7 @@ test_fuzz_fuzz_SOURCES = \ test/fuzz/crypto_hkdf_hmac_sha256_l32.cpp \ test/fuzz/crypto_poly1305.cpp \ test/fuzz/cuckoocache.cpp \ + test/fuzz/data_stream.cpp \ test/fuzz/decode_tx.cpp \ test/fuzz/descriptor_parse.cpp \ test/fuzz/deserialize.cpp \ @@ -260,6 +278,7 @@ test_fuzz_fuzz_SOURCES = \ test/fuzz/locale.cpp \ test/fuzz/merkleblock.cpp \ test/fuzz/message.cpp \ + test/fuzz/muhash.cpp \ test/fuzz/multiplication_overflow.cpp \ test/fuzz/net.cpp \ test/fuzz/net_permissions.cpp \ @@ -272,6 +291,7 @@ test_fuzz_fuzz_SOURCES = \ test/fuzz/parse_script.cpp \ test/fuzz/parse_univalue.cpp \ test/fuzz/policy_estimator.cpp \ + test/fuzz/policy_estimator_io.cpp \ test/fuzz/pow.cpp \ test/fuzz/prevector.cpp \ test/fuzz/primitives_transaction.cpp \ @@ -300,11 +320,13 @@ test_fuzz_fuzz_SOURCES = \ test/fuzz/strprintf.cpp \ test/fuzz/system.cpp \ test/fuzz/timedata.cpp \ + test/fuzz/torcontrol.cpp \ test/fuzz/transaction.cpp \ test/fuzz/tx_in.cpp \ - test/fuzz/tx_out.cpp - -endif # ENABLE_FUZZ + test/fuzz/tx_out.cpp \ + test/fuzz/validation_load_mempool.cpp \ + test/fuzz/versionbits.cpp +endif # ENABLE_FUZZ_BINARY nodist_test_test_dash_SOURCES = $(GENERATED_TEST_FILES) diff --git a/src/chainparams.cpp b/src/chainparams.cpp index 78d61f8b9d0fe..293024341fad8 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -136,6 +136,7 @@ class CMainParams : public CChainParams { CMainParams() { strNetworkID = CBaseChainParams::MAIN; consensus.nSubsidyHalvingInterval = 210240; // Note: actual number of blocks per calendar year with DGW v3 is ~200700 (for example 449750 - 249050) + consensus.BIP16Height = 0; consensus.nMasternodePaymentsStartBlock = 100000; // not true, but it's ok as long as it's less then nMasternodePaymentsIncreaseBlock consensus.nMasternodePaymentsIncreaseBlock = 158000; // actual historical value consensus.nMasternodePaymentsIncreasePeriod = 576*30; // 17280 - actual historical value @@ -182,7 +183,7 @@ class CMainParams : public CChainParams { consensus.vDeployments[Consensus::DEPLOYMENT_V20].bit = 9; consensus.vDeployments[Consensus::DEPLOYMENT_V20].nStartTime = 19999999999; // TODO: To be determined later - consensus.vDeployments[Consensus::DEPLOYMENT_V20].nTimeout = 999999999999ULL; + consensus.vDeployments[Consensus::DEPLOYMENT_V20].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT; consensus.vDeployments[Consensus::DEPLOYMENT_V20].nWindowSize = 4032; consensus.vDeployments[Consensus::DEPLOYMENT_V20].nThresholdStart = 3226; // 80% of 4032 consensus.vDeployments[Consensus::DEPLOYMENT_V20].nThresholdMin = 2420; // 60% of 4032 @@ -190,7 +191,7 @@ class CMainParams : public CChainParams { consensus.vDeployments[Consensus::DEPLOYMENT_MN_RR].bit = 10; consensus.vDeployments[Consensus::DEPLOYMENT_MN_RR].nStartTime = 19999999999; // TODO: To be determined later - consensus.vDeployments[Consensus::DEPLOYMENT_MN_RR].nTimeout = 999999999999ULL; + consensus.vDeployments[Consensus::DEPLOYMENT_MN_RR].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT; consensus.vDeployments[Consensus::DEPLOYMENT_MN_RR].nWindowSize = 4032; consensus.vDeployments[Consensus::DEPLOYMENT_MN_RR].nThresholdStart = 3226; // 80% of 4032 consensus.vDeployments[Consensus::DEPLOYMENT_MN_RR].nThresholdMin = 2420; // 60% of 4032 @@ -332,6 +333,7 @@ class CTestNetParams : public CChainParams { CTestNetParams() { strNetworkID = CBaseChainParams::TESTNET; consensus.nSubsidyHalvingInterval = 210240; + consensus.BIP16Height = 0; consensus.nMasternodePaymentsStartBlock = 4010; // not true, but it's ok as long as it's less then nMasternodePaymentsIncreaseBlock consensus.nMasternodePaymentsIncreaseBlock = 4030; consensus.nMasternodePaymentsIncreasePeriod = 10; @@ -378,7 +380,7 @@ class CTestNetParams : public CChainParams { consensus.vDeployments[Consensus::DEPLOYMENT_V20].bit = 9; consensus.vDeployments[Consensus::DEPLOYMENT_V20].nStartTime = 19999999999; // TODO: To be determined later - consensus.vDeployments[Consensus::DEPLOYMENT_V20].nTimeout = 999999999999ULL; + consensus.vDeployments[Consensus::DEPLOYMENT_V20].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT; consensus.vDeployments[Consensus::DEPLOYMENT_V20].nWindowSize = 100; consensus.vDeployments[Consensus::DEPLOYMENT_V20].nThresholdStart = 80; // 80% of 100 consensus.vDeployments[Consensus::DEPLOYMENT_V20].nThresholdMin = 60; // 60% of 100 @@ -386,7 +388,7 @@ class CTestNetParams : public CChainParams { consensus.vDeployments[Consensus::DEPLOYMENT_MN_RR].bit = 10; consensus.vDeployments[Consensus::DEPLOYMENT_MN_RR].nStartTime = 19999999999; // TODO: To be determined later - consensus.vDeployments[Consensus::DEPLOYMENT_MN_RR].nTimeout = 999999999999ULL; + consensus.vDeployments[Consensus::DEPLOYMENT_MN_RR].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT; consensus.vDeployments[Consensus::DEPLOYMENT_MN_RR].nWindowSize = 100; consensus.vDeployments[Consensus::DEPLOYMENT_MN_RR].nThresholdStart = 80; // 80% of 100 consensus.vDeployments[Consensus::DEPLOYMENT_MN_RR].nThresholdMin = 60; // 60% of 100 @@ -503,6 +505,7 @@ class CDevNetParams : public CChainParams { explicit CDevNetParams(const ArgsManager& args) { strNetworkID = CBaseChainParams::DEVNET; consensus.nSubsidyHalvingInterval = 210240; + consensus.BIP16Height = 0; consensus.nMasternodePaymentsStartBlock = 4010; // not true, but it's ok as long as it's less then nMasternodePaymentsIncreaseBlock consensus.nMasternodePaymentsIncreaseBlock = 4030; consensus.nMasternodePaymentsIncreasePeriod = 10; @@ -548,7 +551,7 @@ class CDevNetParams : public CChainParams { consensus.vDeployments[Consensus::DEPLOYMENT_V20].bit = 9; consensus.vDeployments[Consensus::DEPLOYMENT_V20].nStartTime = 1661990400; // Sep 1st, 2022 - consensus.vDeployments[Consensus::DEPLOYMENT_V20].nTimeout = 999999999999ULL; + consensus.vDeployments[Consensus::DEPLOYMENT_V20].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT; consensus.vDeployments[Consensus::DEPLOYMENT_V20].nWindowSize = 120; consensus.vDeployments[Consensus::DEPLOYMENT_V20].nThresholdStart = 80; // 80% of 100 consensus.vDeployments[Consensus::DEPLOYMENT_V20].nThresholdMin = 60; // 60% of 100 @@ -556,7 +559,7 @@ class CDevNetParams : public CChainParams { consensus.vDeployments[Consensus::DEPLOYMENT_MN_RR].bit = 10; consensus.vDeployments[Consensus::DEPLOYMENT_MN_RR].nStartTime = 1661990400; // Sep 1st, 2022 - consensus.vDeployments[Consensus::DEPLOYMENT_MN_RR].nTimeout = 999999999999ULL; + consensus.vDeployments[Consensus::DEPLOYMENT_MN_RR].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT; consensus.vDeployments[Consensus::DEPLOYMENT_MN_RR].nWindowSize = 120; consensus.vDeployments[Consensus::DEPLOYMENT_MN_RR].nThresholdStart = 80; // 80% of 100 consensus.vDeployments[Consensus::DEPLOYMENT_MN_RR].nThresholdMin = 60; // 60% of 100 @@ -739,6 +742,7 @@ class CRegTestParams : public CChainParams { explicit CRegTestParams(const ArgsManager& args) { strNetworkID = CBaseChainParams::REGTEST; consensus.nSubsidyHalvingInterval = 150; + consensus.BIP16Height = 0; // always enforce P2SH BIP16 on regtest consensus.nMasternodePaymentsStartBlock = 240; consensus.nMasternodePaymentsIncreaseBlock = 350; consensus.nMasternodePaymentsIncreasePeriod = 10; @@ -781,11 +785,11 @@ class CRegTestParams : public CChainParams { consensus.nMinerConfirmationWindow = 144; // Faster than normal for regtest (144 instead of 2016) consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].bit = 28; consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nStartTime = 0; - consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nTimeout = 999999999999ULL; + consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT; consensus.vDeployments[Consensus::DEPLOYMENT_V20].bit = 9; consensus.vDeployments[Consensus::DEPLOYMENT_V20].nStartTime = 0; - consensus.vDeployments[Consensus::DEPLOYMENT_V20].nTimeout = 999999999999ULL; + consensus.vDeployments[Consensus::DEPLOYMENT_V20].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT; consensus.vDeployments[Consensus::DEPLOYMENT_V20].nWindowSize = 480; consensus.vDeployments[Consensus::DEPLOYMENT_V20].nThresholdStart = 384; // 80% of 480 consensus.vDeployments[Consensus::DEPLOYMENT_V20].nThresholdMin = 288; // 60% of 480 @@ -793,7 +797,7 @@ class CRegTestParams : public CChainParams { consensus.vDeployments[Consensus::DEPLOYMENT_MN_RR].bit = 10; consensus.vDeployments[Consensus::DEPLOYMENT_MN_RR].nStartTime = 0; - consensus.vDeployments[Consensus::DEPLOYMENT_MN_RR].nTimeout = 999999999999ULL; + consensus.vDeployments[Consensus::DEPLOYMENT_MN_RR].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT; consensus.vDeployments[Consensus::DEPLOYMENT_MN_RR].nWindowSize = 1030; consensus.vDeployments[Consensus::DEPLOYMENT_MN_RR].nThresholdStart = 800; // 80% of 1000 consensus.vDeployments[Consensus::DEPLOYMENT_MN_RR].nThresholdMin = 600; // 60% of 1000 diff --git a/src/consensus/params.h b/src/consensus/params.h index 7922112870170..4e3442cb0ee8f 100644 --- a/src/consensus/params.h +++ b/src/consensus/params.h @@ -9,6 +9,7 @@ #include #include +#include #include namespace Consensus { @@ -39,6 +40,15 @@ struct BIP9Deployment { int64_t nThresholdMin{0}; /** A coefficient which adjusts the speed a required number of signaling blocks is decreasing from nThresholdStart to nThresholdMin at with each period. */ int64_t nFalloffCoeff{0}; + + /** Constant for nTimeout very far in the future. */ + static constexpr int64_t NO_TIMEOUT = std::numeric_limits::max(); + + /** Special value for nStartTime indicating that the deployment is always active. + * This is useful for testing, as it means tests don't need to deal with the activation + * process (which takes at least 3 BIP9 intervals). Only tests that specifically test the + * behaviour during activation cannot use this. */ + static constexpr int64_t ALWAYS_ACTIVE = -1; }; /** @@ -48,6 +58,8 @@ struct Params { uint256 hashGenesisBlock; uint256 hashDevnetGenesisBlock; int nSubsidyHalvingInterval; + /** Block height at which BIP16 becomes active */ + int BIP16Height; int nMasternodePaymentsStartBlock; int nMasternodePaymentsIncreaseBlock; int nMasternodePaymentsIncreasePeriod; // in blocks diff --git a/src/test/fuzz/banman.cpp b/src/test/fuzz/banman.cpp index eb7d3d2ae6049..bd2dce6a40be5 100644 --- a/src/test/fuzz/banman.cpp +++ b/src/test/fuzz/banman.cpp @@ -26,7 +26,7 @@ int64_t ConsumeBanTimeOffset(FuzzedDataProvider& fuzzed_data_provider) noexcept void initialize_banman() { - InitializeFuzzingContext(); + static const auto testing_setup = MakeNoLogFileContext<>(); } FUZZ_TARGET_INIT(banman, initialize_banman) diff --git a/src/test/fuzz/coins_view.cpp b/src/test/fuzz/coins_view.cpp index 1d5f756bcfa01..624bec097681c 100644 --- a/src/test/fuzz/coins_view.cpp +++ b/src/test/fuzz/coins_view.cpp @@ -37,9 +37,7 @@ bool operator==(const Coin& a, const Coin& b) void initialize_coins_view() { - static const ECCVerifyHandle ecc_verify_handle; - ECC_Start(); - SelectParams(CBaseChainParams::REGTEST); + static const auto testing_setup = MakeNoLogFileContext(); } FUZZ_TARGET_INIT(coins_view, initialize_coins_view) diff --git a/src/test/fuzz/connman.cpp b/src/test/fuzz/connman.cpp index 826ad5350f6ce..8bf1af550fa58 100644 --- a/src/test/fuzz/connman.cpp +++ b/src/test/fuzz/connman.cpp @@ -17,7 +17,7 @@ void initialize_connman() { - InitializeFuzzingContext(); + static const auto testing_setup = MakeNoLogFileContext<>(); } FUZZ_TARGET_INIT(connman, initialize_connman) diff --git a/src/test/fuzz/data_stream.cpp b/src/test/fuzz/data_stream.cpp new file mode 100644 index 0000000000000..30f18095b90f3 --- /dev/null +++ b/src/test/fuzz/data_stream.cpp @@ -0,0 +1,25 @@ +// Copyright (c) 2020 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include +#include +#include +#include +#include + +#include +#include + +void initialize_data_stream_addr_man() +{ + static const auto testing_setup = MakeNoLogFileContext<>(); +} + +FUZZ_TARGET_INIT(data_stream_addr_man, initialize_data_stream_addr_man) +{ + FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()}; + CDataStream data_stream = ConsumeDataStream(fuzzed_data_provider); + CAddrMan addr_man; + CAddrDB::Read(addr_man, data_stream); +} diff --git a/src/test/fuzz/fuzz.cpp b/src/test/fuzz/fuzz.cpp index 9ef8b5967609a..6ac2420b9e723 100644 --- a/src/test/fuzz/fuzz.cpp +++ b/src/test/fuzz/fuzz.cpp @@ -43,7 +43,7 @@ void initialize() std::get<1>(it->second)(); } -#if defined(PROVIDE_MAIN_FUNCTION) +#if defined(PROVIDE_FUZZ_MAIN_FUNCTION) static bool read_stdin(std::vector& data) { uint8_t buffer[1024]; @@ -71,8 +71,8 @@ extern "C" int LLVMFuzzerInitialize(int* argc, char*** argv) return 0; } -#if defined(PROVIDE_MAIN_FUNCTION) -__attribute__((weak)) int main(int argc, char** argv) +#if defined(PROVIDE_FUZZ_MAIN_FUNCTION) +int main(int argc, char** argv) { initialize(); static const auto& test_one_input = *Assert(g_test_one_input); diff --git a/src/test/fuzz/i2p.cpp b/src/test/fuzz/i2p.cpp index 9f4e2bbf2229f..345d68502adea 100644 --- a/src/test/fuzz/i2p.cpp +++ b/src/test/fuzz/i2p.cpp @@ -14,7 +14,7 @@ void initialize_i2p() { - InitializeFuzzingContext(); + static const auto testing_setup = MakeNoLogFileContext<>(); } FUZZ_TARGET_INIT(i2p, initialize_i2p) diff --git a/src/test/fuzz/kitchen_sink.cpp b/src/test/fuzz/kitchen_sink.cpp index 0656ddc54764f..40559b324caeb 100644 --- a/src/test/fuzz/kitchen_sink.cpp +++ b/src/test/fuzz/kitchen_sink.cpp @@ -2,6 +2,8 @@ // 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 @@ -9,9 +11,31 @@ #include #include +#include #include #include +namespace { +constexpr TransactionError ALL_TRANSACTION_ERROR[] = { + TransactionError::OK, + TransactionError::MISSING_INPUTS, + TransactionError::ALREADY_IN_CHAIN, + TransactionError::P2P_DISABLED, + TransactionError::MEMPOOL_REJECTED, + TransactionError::MEMPOOL_ERROR, + TransactionError::INVALID_PSBT, + TransactionError::PSBT_MISMATCH, + TransactionError::SIGHASH_MISMATCH, + TransactionError::MAX_FEE_EXCEEDED, +}; + +constexpr FeeEstimateHorizon ALL_FEE_EST_HORIZON[] = { + FeeEstimateHorizon::SHORT_HALFLIFE, + FeeEstimateHorizon::MED_HALFLIFE, + FeeEstimateHorizon::LONG_HALFLIFE, +}; +}; // namespace + // The fuzzing kitchen sink: Fuzzing harness for functions that need to be // fuzzed but a.) don't belong in any existing fuzzing harness file, and // b.) are not important enough to warrant their own fuzzing harness file. @@ -19,8 +43,15 @@ FUZZ_TARGET(kitchen_sink) { FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); - const TransactionError transaction_error = fuzzed_data_provider.PickValueInArray({TransactionError::OK, TransactionError::MISSING_INPUTS, TransactionError::ALREADY_IN_CHAIN, TransactionError::P2P_DISABLED, TransactionError::MEMPOOL_REJECTED, TransactionError::MEMPOOL_ERROR, TransactionError::INVALID_PSBT, TransactionError::PSBT_MISMATCH, TransactionError::SIGHASH_MISMATCH, TransactionError::MAX_FEE_EXCEEDED}); + const TransactionError transaction_error = fuzzed_data_provider.PickValueInArray(ALL_TRANSACTION_ERROR); (void)JSONRPCTransactionError(transaction_error); (void)RPCErrorFromTransactionError(transaction_error); (void)TransactionErrorString(transaction_error); + + (void)StringForFeeEstimateHorizon(fuzzed_data_provider.PickValueInArray(ALL_FEE_EST_HORIZON)); + + const std::vector bytes = ConsumeRandomLengthByteVector(fuzzed_data_provider); + const std::vector bits = BytesToBits(bytes); + const std::vector bytes_decoded = BitsToBytes(bits); + assert(bytes == bytes_decoded); } diff --git a/src/test/fuzz/load_external_block_file.cpp b/src/test/fuzz/load_external_block_file.cpp index cdff13fc91fd4..61381f9005505 100644 --- a/src/test/fuzz/load_external_block_file.cpp +++ b/src/test/fuzz/load_external_block_file.cpp @@ -15,7 +15,7 @@ void initialize_load_external_block_file() { - InitializeFuzzingContext(); + static const auto testing_setup = MakeNoLogFileContext(); } FUZZ_TARGET_INIT(load_external_block_file, initialize_load_external_block_file) diff --git a/src/test/fuzz/muhash.cpp b/src/test/fuzz/muhash.cpp new file mode 100644 index 0000000000000..2d761cef15dbc --- /dev/null +++ b/src/test/fuzz/muhash.cpp @@ -0,0 +1,63 @@ +// Copyright (c) 2020 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include +#include +#include +#include + +#include + +FUZZ_TARGET(muhash) +{ + FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()}; + std::vector data = ConsumeRandomLengthByteVector(fuzzed_data_provider); + std::vector data2 = ConsumeRandomLengthByteVector(fuzzed_data_provider); + if (data.empty()) { + data.resize(fuzzed_data_provider.ConsumeIntegralInRange(1, 4096), fuzzed_data_provider.ConsumeIntegral()); + } + if (data2.empty()) { + data2.resize(fuzzed_data_provider.ConsumeIntegralInRange(1, 4096), fuzzed_data_provider.ConsumeIntegral()); + } + + data = ConsumeRandomLengthByteVector(fuzzed_data_provider); + data2 = ConsumeRandomLengthByteVector(fuzzed_data_provider); + + MuHash3072 muhash; + + // Test that MuHash result is consistent independent of order of operations + muhash.Insert(data); + muhash.Insert(data2); + + uint256 out; + muhash.Finalize(out); + + muhash = MuHash3072(); + muhash.Insert(data2); + muhash.Insert(data); + + uint256 out2; + muhash.Finalize(out2); + + assert(out == out2); + MuHash3072 muhash3; + muhash3 *= muhash; + uint256 out3; + muhash3.Finalize(out3); + assert(out == out3); + + // Test that removing all added elements brings the object back to it's initial state + muhash /= muhash; + muhash.Finalize(out); + + MuHash3072 muhash2; + muhash2.Finalize(out2); + + assert(out == out2); + + muhash3.Remove(data); + muhash3.Remove(data2); + muhash3.Finalize(out3); + assert(out == out3); +} diff --git a/src/test/fuzz/net.cpp b/src/test/fuzz/net.cpp index a6751d8e29145..61e3453ed6eb1 100644 --- a/src/test/fuzz/net.cpp +++ b/src/test/fuzz/net.cpp @@ -22,7 +22,7 @@ void initialize_net() { - static const BasicTestingSetup basic_testing_setup; + static const auto testing_setup = MakeNoLogFileContext<>(CBaseChainParams::MAIN); } FUZZ_TARGET_INIT(net, initialize_net) diff --git a/src/test/fuzz/netaddress.cpp b/src/test/fuzz/netaddress.cpp index 521b9d283e677..06f6c1e52b4b5 100644 --- a/src/test/fuzz/netaddress.cpp +++ b/src/test/fuzz/netaddress.cpp @@ -9,7 +9,6 @@ #include #include -#include #include FUZZ_TARGET(netaddress) diff --git a/src/test/fuzz/policy_estimator.cpp b/src/test/fuzz/policy_estimator.cpp index 8c77391e99f70..c147dfe7966d0 100644 --- a/src/test/fuzz/policy_estimator.cpp +++ b/src/test/fuzz/policy_estimator.cpp @@ -14,7 +14,12 @@ #include #include -FUZZ_TARGET(policy_estimator) +void initialize_policy_estimator() +{ + static const auto testing_setup = MakeNoLogFileContext<>(); +} + +FUZZ_TARGET_INIT(policy_estimator, initialize_policy_estimator) { FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); CBlockPolicyEstimator block_policy_estimator; @@ -62,4 +67,10 @@ FUZZ_TARGET(policy_estimator) (void)block_policy_estimator.estimateSmartFee(fuzzed_data_provider.ConsumeIntegral(), fuzzed_data_provider.ConsumeBool() ? &fee_calculation : nullptr, fuzzed_data_provider.ConsumeBool()); (void)block_policy_estimator.HighestTargetTracked(fuzzed_data_provider.PickValueInArray({FeeEstimateHorizon::SHORT_HALFLIFE, FeeEstimateHorizon::MED_HALFLIFE, FeeEstimateHorizon::LONG_HALFLIFE})); } + { + FuzzedAutoFileProvider fuzzed_auto_file_provider = ConsumeAutoFile(fuzzed_data_provider); + CAutoFile fuzzed_auto_file = fuzzed_auto_file_provider.open(); + block_policy_estimator.Write(fuzzed_auto_file); + block_policy_estimator.Read(fuzzed_auto_file); + } } diff --git a/src/test/fuzz/policy_estimator_io.cpp b/src/test/fuzz/policy_estimator_io.cpp new file mode 100644 index 0000000000000..178ccc8e76636 --- /dev/null +++ b/src/test/fuzz/policy_estimator_io.cpp @@ -0,0 +1,28 @@ +// Copyright (c) 2020 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include +#include +#include +#include + +#include +#include + +void initialize_policy_estimator_io() +{ + static const auto testing_setup = MakeNoLogFileContext<>(); +} + +FUZZ_TARGET_INIT(policy_estimator_io, initialize_policy_estimator_io) +{ + FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); + FuzzedAutoFileProvider fuzzed_auto_file_provider = ConsumeAutoFile(fuzzed_data_provider); + CAutoFile fuzzed_auto_file = fuzzed_auto_file_provider.open(); + // Re-using block_policy_estimator across runs to avoid costly creation of CBlockPolicyEstimator object. + static CBlockPolicyEstimator block_policy_estimator; + if (block_policy_estimator.Read(fuzzed_auto_file)) { + block_policy_estimator.Write(fuzzed_auto_file); + } +} diff --git a/src/test/fuzz/process_message.cpp b/src/test/fuzz/process_message.cpp index be3affd1691e5..795e5b08eb0e5 100644 --- a/src/test/fuzz/process_message.cpp +++ b/src/test/fuzz/process_message.cpp @@ -44,14 +44,8 @@ const TestingSetup* g_setup; void initialize_process_message() { - static TestingSetup setup{ - CBaseChainParams::REGTEST, - { - "-nodebuglogfile", - }, - }; - g_setup = &setup; - + static const auto testing_setup = MakeNoLogFileContext(); + g_setup = testing_setup.get(); for (int i = 0; i < 2 * COINBASE_MATURITY; i++) { MineBlock(g_setup->m_node, CScript() << OP_TRUE); } diff --git a/src/test/fuzz/process_messages.cpp b/src/test/fuzz/process_messages.cpp index 217d2448c2132..2e81d76cf86c8 100644 --- a/src/test/fuzz/process_messages.cpp +++ b/src/test/fuzz/process_messages.cpp @@ -15,18 +15,14 @@ #include #include +namespace { const TestingSetup* g_setup; +} // namespace void initialize_process_messages() { - static TestingSetup setup{ - CBaseChainParams::REGTEST, - { - "-nodebuglogfile", - }, - }; - g_setup = &setup; - + static const auto testing_setup = MakeNoLogFileContext(); + g_setup = testing_setup.get(); for (int i = 0; i < 2 * COINBASE_MATURITY; i++) { MineBlock(g_setup->m_node, CScript() << OP_TRUE); } diff --git a/src/test/fuzz/socks5.cpp b/src/test/fuzz/socks5.cpp index 0e80b1afe0561..3da03f1dea716 100644 --- a/src/test/fuzz/socks5.cpp +++ b/src/test/fuzz/socks5.cpp @@ -20,7 +20,7 @@ extern int g_socks5_recv_timeout; void initialize_socks5() { - InitializeFuzzingContext(); + static const auto testing_setup = MakeNoLogFileContext<>(); default_socks5_recv_timeout = g_socks5_recv_timeout; } diff --git a/src/test/fuzz/string.cpp b/src/test/fuzz/string.cpp index bd6febcdefd15..6c618c51f0883 100644 --- a/src/test/fuzz/string.cpp +++ b/src/test/fuzz/string.cpp @@ -172,6 +172,9 @@ FUZZ_TARGET(string) } (void)SanitizeString(random_string_1); (void)SanitizeString(random_string_1, fuzzed_data_provider.ConsumeIntegralInRange(0, 3)); +#ifndef WIN32 + (void)ShellEscape(random_string_1); +#endif // WIN32 uint16_t port_out; std::string host_out; SplitHostPort(random_string_1, port_out, host_out); diff --git a/src/test/fuzz/torcontrol.cpp b/src/test/fuzz/torcontrol.cpp new file mode 100644 index 0000000000000..b7a42ea7f47fa --- /dev/null +++ b/src/test/fuzz/torcontrol.cpp @@ -0,0 +1,79 @@ +// Copyright (c) 2020 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include +#include +#include +#include + +#include +#include +#include + +class DummyTorControlConnection : public TorControlConnection +{ +public: + DummyTorControlConnection() : TorControlConnection{nullptr} + { + } + + bool Connect(const std::string&, const ConnectionCB&, const ConnectionCB&) + { + return true; + } + + void Disconnect() + { + } + + bool Command(const std::string&, const ReplyHandlerCB&) + { + return true; + } +}; + +void initialize_torcontrol() +{ + static const auto testing_setup = MakeNoLogFileContext<>(); +} + +FUZZ_TARGET_INIT(torcontrol, initialize_torcontrol) +{ + FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()}; + + TorController tor_controller; + while (fuzzed_data_provider.ConsumeBool()) { + TorControlReply tor_control_reply; + CallOneOf( + fuzzed_data_provider, + [&] { + tor_control_reply.code = 250; + }, + [&] { + tor_control_reply.code = 510; + }, + [&] { + tor_control_reply.code = fuzzed_data_provider.ConsumeIntegral(); + }); + tor_control_reply.lines = ConsumeRandomLengthStringVector(fuzzed_data_provider); + if (tor_control_reply.lines.empty()) { + break; + } + DummyTorControlConnection dummy_tor_control_connection; + CallOneOf( + fuzzed_data_provider, + [&] { + tor_controller.add_onion_cb(dummy_tor_control_connection, tor_control_reply); + }, + [&] { + tor_controller.auth_cb(dummy_tor_control_connection, tor_control_reply); + }, + [&] { + tor_controller.authchallenge_cb(dummy_tor_control_connection, tor_control_reply); + }, + [&] { + tor_controller.protocolinfo_cb(dummy_tor_control_connection, tor_control_reply); + }); + } +} diff --git a/src/test/fuzz/util.h b/src/test/fuzz/util.h index bc7a1099750b3..ef48cf290efa5 100644 --- a/src/test/fuzz/util.h +++ b/src/test/fuzz/util.h @@ -28,6 +28,7 @@ #include #include #include +#include #include #include @@ -247,6 +248,16 @@ void SetFuzzedErrNo(FuzzedDataProvider& fuzzed_data_provider, const std::array(0, 133); +} + /** * Returns a byte vector of specified size regardless of the number of remaining bytes available * from the fuzzer. Pads with zero value bytes if needed to achieve the specified size. @@ -321,9 +332,17 @@ inline std::unique_ptr ConsumeNodeAsUniquePtr(FuzzedDataProvider& fdp, co void FillNode(FuzzedDataProvider& fuzzed_data_provider, CNode& node, bool init_version) noexcept; -inline void InitializeFuzzingContext(const std::string& chain_name = CBaseChainParams::REGTEST) +template +std::unique_ptr MakeNoLogFileContext(const std::string& chain_name = CBaseChainParams::REGTEST, const std::vector& extra_args = {}) { - static const BasicTestingSetup basic_testing_setup{chain_name, {"-nodebuglogfile"}}; + // Prepend default arguments for fuzzing + const std::vector arguments = Cat( + { + "-nodebuglogfile", + }, + extra_args); + + return std::make_unique(chain_name, arguments); } class FuzzedFileProvider @@ -338,6 +357,7 @@ class FuzzedFileProvider FILE* open() { + SetFuzzedErrNo(m_fuzzed_data_provider); if (m_fuzzed_data_provider.ConsumeBool()) { return nullptr; } @@ -379,6 +399,7 @@ class FuzzedFileProvider static ssize_t read(void* cookie, char* buf, size_t size) { FuzzedFileProvider* fuzzed_file = (FuzzedFileProvider*)cookie; + SetFuzzedErrNo(fuzzed_file->m_fuzzed_data_provider); if (buf == nullptr || size == 0 || fuzzed_file->m_fuzzed_data_provider.ConsumeBool()) { return fuzzed_file->m_fuzzed_data_provider.ConsumeBool() ? 0 : -1; } @@ -397,6 +418,7 @@ class FuzzedFileProvider static ssize_t write(void* cookie, const char* buf, size_t size) { FuzzedFileProvider* fuzzed_file = (FuzzedFileProvider*)cookie; + SetFuzzedErrNo(fuzzed_file->m_fuzzed_data_provider); const ssize_t n = fuzzed_file->m_fuzzed_data_provider.ConsumeIntegralInRange(0, size); if (AdditionOverflow(static_cast(fuzzed_file->m_offset), n)) { return fuzzed_file->m_fuzzed_data_provider.ConsumeBool() ? 0 : -1; @@ -407,8 +429,9 @@ class FuzzedFileProvider static int seek(void* cookie, int64_t* offset, int whence) { - assert(whence == SEEK_SET || whence == SEEK_CUR); // SEEK_END not implemented yet. + assert(whence == SEEK_SET || whence == SEEK_CUR || whence == SEEK_END); FuzzedFileProvider* fuzzed_file = (FuzzedFileProvider*)cookie; + SetFuzzedErrNo(fuzzed_file->m_fuzzed_data_provider); int64_t new_offset = 0; if (whence == SEEK_SET) { new_offset = *offset; @@ -417,6 +440,12 @@ class FuzzedFileProvider return -1; } new_offset = fuzzed_file->m_offset + *offset; + } else if (whence == SEEK_END) { + const int64_t n = fuzzed_file->m_fuzzed_data_provider.ConsumeIntegralInRange(0, 4096); + if (AdditionOverflow(n, *offset)) { + return -1; + } + new_offset = n + *offset; } if (new_offset < 0) { return -1; @@ -429,6 +458,7 @@ class FuzzedFileProvider static int close(void* cookie) { FuzzedFileProvider* fuzzed_file = (FuzzedFileProvider*)cookie; + SetFuzzedErrNo(fuzzed_file->m_fuzzed_data_provider); return fuzzed_file->m_fuzzed_data_provider.ConsumeIntegralInRange(-1, 0); } }; diff --git a/src/test/fuzz/validation_load_mempool.cpp b/src/test/fuzz/validation_load_mempool.cpp new file mode 100644 index 0000000000000..e1a21b6c5397a --- /dev/null +++ b/src/test/fuzz/validation_load_mempool.cpp @@ -0,0 +1,34 @@ +// Copyright (c) 2020 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +void initialize_validation_load_mempool() +{ + static const auto testing_setup = MakeNoLogFileContext(); +} + +FUZZ_TARGET_INIT(validation_load_mempool, initialize_validation_load_mempool) +{ + FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()}; + SetMockTime(ConsumeTime(fuzzed_data_provider)); + FuzzedFileProvider fuzzed_file_provider = ConsumeFile(fuzzed_data_provider); + + CTxMemPool pool{}; + auto fuzzed_fopen = [&](const fs::path&, const char*) { + return fuzzed_file_provider.open(); + }; + (void)LoadMempool(pool, ::ChainstateActive(), fuzzed_fopen); + (void)DumpMempool(pool, fuzzed_fopen, true); +} diff --git a/src/test/fuzz/versionbits.cpp b/src/test/fuzz/versionbits.cpp new file mode 100644 index 0000000000000..9563908679aa5 --- /dev/null +++ b/src/test/fuzz/versionbits.cpp @@ -0,0 +1,352 @@ +// Copyright (c) 2020-2021 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 + +namespace { +class TestConditionChecker : public AbstractThresholdConditionChecker +{ +private: + mutable ThresholdConditionCache m_cache; + const Consensus::Params dummy_params{}; + +public: + const int64_t m_begin; + const int64_t m_end; + const int m_period; + const int m_threshold; + const int m_bit; + + TestConditionChecker(int64_t begin, int64_t end, int period, int threshold, int bit) + : m_begin{begin}, m_end{end}, m_period{period}, m_threshold{threshold}, m_bit{bit} + { + assert(m_period > 0); + assert(0 <= m_threshold && m_threshold <= m_period); + assert(0 <= m_bit && m_bit < 32 && m_bit < VERSIONBITS_NUM_BITS); + } + + bool Condition(const CBlockIndex* pindex, const Consensus::Params& params) const override { return Condition(pindex->nVersion); } + int64_t BeginTime(const Consensus::Params& params) const override { return m_begin; } + int64_t EndTime(const Consensus::Params& params) const override { return m_end; } + int Period(const Consensus::Params& params) const override { return m_period; } + int Threshold(const Consensus::Params& params, int nAttempt) const override { return m_threshold; } + + ThresholdState GetStateFor(const CBlockIndex* pindexPrev) const { return AbstractThresholdConditionChecker::GetStateFor(pindexPrev, dummy_params, m_cache); } + int GetStateSinceHeightFor(const CBlockIndex* pindexPrev) const { return AbstractThresholdConditionChecker::GetStateSinceHeightFor(pindexPrev, dummy_params, m_cache); } + BIP9Stats GetStateStatisticsFor(const CBlockIndex* pindexPrev) const { return AbstractThresholdConditionChecker::GetStateStatisticsFor(pindexPrev, dummy_params, m_cache); } + + bool Condition(int32_t version) const + { + uint32_t mask = ((uint32_t)1) << m_bit; + return (((version & VERSIONBITS_TOP_MASK) == VERSIONBITS_TOP_BITS) && (version & mask) != 0); + } + + bool Condition(const CBlockIndex* pindex) const { return Condition(pindex->nVersion); } +}; + +/** Track blocks mined for test */ +class Blocks +{ +private: + std::vector> m_blocks; + const uint32_t m_start_time; + const uint32_t m_interval; + const int32_t m_signal; + const int32_t m_no_signal; + +public: + Blocks(uint32_t start_time, uint32_t interval, int32_t signal, int32_t no_signal) + : m_start_time{start_time}, m_interval{interval}, m_signal{signal}, m_no_signal{no_signal} {} + + size_t size() const { return m_blocks.size(); } + + CBlockIndex* tip() const + { + return m_blocks.empty() ? nullptr : m_blocks.back().get(); + } + + CBlockIndex* mine_block(bool signal) + { + CBlockHeader header; + header.nVersion = signal ? m_signal : m_no_signal; + header.nTime = m_start_time + m_blocks.size() * m_interval; + header.nBits = 0x1d00ffff; + + auto current_block = std::make_unique(header); + current_block->pprev = tip(); + current_block->nHeight = m_blocks.size(); + current_block->BuildSkip(); + + return m_blocks.emplace_back(std::move(current_block)).get(); + } +}; + +std::unique_ptr g_params; + +void initialize_versionbits() +{ + // this is actually comparatively slow, so only do it once + g_params = CreateChainParams(CBaseChainParams::MAIN); + assert(g_params != nullptr); +} + +constexpr uint32_t MAX_START_TIME = 4102444800; // 2100-01-01 + +FUZZ_TARGET_INIT(versionbits, initialize_versionbits) +{ + const CChainParams& params = *g_params; + const int64_t interval = params.GetConsensus().nPowTargetSpacing; + assert(interval > 1); // need to be able to halve it + assert(interval < std::numeric_limits::max()); + + FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); + + // making period/max_periods larger slows these tests down significantly + const int period = 32; + const size_t max_periods = 16; + const size_t max_blocks = 2 * period * max_periods; + + const int threshold = fuzzed_data_provider.ConsumeIntegralInRange(1, period); + assert(0 < threshold && threshold <= period); // must be able to both pass and fail threshold! + + // too many blocks at 10min each might cause uint32_t time to overflow if + // block_start_time is at the end of the range above + assert(std::numeric_limits::max() - MAX_START_TIME > interval * max_blocks); + + const int64_t block_start_time = fuzzed_data_provider.ConsumeIntegralInRange(params.GenesisBlock().nTime, MAX_START_TIME); + + // what values for version will we use to signal / not signal? + const int32_t ver_signal = fuzzed_data_provider.ConsumeIntegral(); + const int32_t ver_nosignal = fuzzed_data_provider.ConsumeIntegral(); + + // select deployment parameters: bit, start time, timeout + const int bit = fuzzed_data_provider.ConsumeIntegralInRange(0, VERSIONBITS_NUM_BITS - 1); + + bool always_active_test = false; + bool never_active_test = false; + int64_t start_time; + int64_t timeout; + if (fuzzed_data_provider.ConsumeBool()) { + // pick the timestamp to switch based on a block + // note states will change *after* these blocks because mediantime lags + int start_block = fuzzed_data_provider.ConsumeIntegralInRange(0, period * (max_periods - 3)); + int end_block = fuzzed_data_provider.ConsumeIntegralInRange(start_block, period * (max_periods - 3)); + + start_time = block_start_time + start_block * interval; + timeout = block_start_time + end_block * interval; + + assert(start_time <= timeout); + + // allow for times to not exactly match a block + if (fuzzed_data_provider.ConsumeBool()) start_time += interval / 2; + if (fuzzed_data_provider.ConsumeBool()) timeout += interval / 2; + + // this may make timeout too early; if so, don't run the test + if (start_time > timeout) return; + } else { + if (fuzzed_data_provider.ConsumeBool()) { + start_time = Consensus::BIP9Deployment::ALWAYS_ACTIVE; + timeout = Consensus::BIP9Deployment::NO_TIMEOUT; + always_active_test = true; + } else { + start_time = 1199145601; // January 1, 2008 + timeout = 1230767999; // December 31, 2008 + never_active_test = true; + } + } + + TestConditionChecker checker(start_time, timeout, period, threshold, bit); + + // Early exit if the versions don't signal sensibly for the deployment + if (!checker.Condition(ver_signal)) return; + if (checker.Condition(ver_nosignal)) return; + if (ver_nosignal < 0) return; + + // TOP_BITS should ensure version will be positive and meet min + // version requirement + assert(ver_signal > 0); + assert(ver_signal >= VERSIONBITS_LAST_OLD_BLOCK_VERSION); + + // Now that we have chosen time and versions, setup to mine blocks + Blocks blocks(block_start_time, interval, ver_signal, ver_nosignal); + + /* Strategy: + * * we will mine a final period worth of blocks, with + * randomised signalling according to a mask + * * but before we mine those blocks, we will mine some + * randomised number of prior periods; with either all + * or no blocks in the period signalling + * + * We establish the mask first, then consume "bools" until + * we run out of fuzz data to work out how many prior periods + * there are and which ones will signal. + */ + + // establish the mask + const uint32_t signalling_mask = fuzzed_data_provider.ConsumeIntegral(); + + // mine prior periods + while (fuzzed_data_provider.remaining_bytes() > 0) { + // all blocks in these periods either do or don't signal + bool signal = fuzzed_data_provider.ConsumeBool(); + for (int b = 0; b < period; ++b) { + blocks.mine_block(signal); + } + + // don't risk exceeding max_blocks or times may wrap around + if (blocks.size() + 2 * period > max_blocks) break; + } + // NOTE: fuzzed_data_provider may be fully consumed at this point and should not be used further + + // now we mine the final period and check that everything looks sane + + // count the number of signalling blocks + int blocks_sig = 0; + + // get the info for the first block of the period + CBlockIndex* prev = blocks.tip(); + const int exp_since = checker.GetStateSinceHeightFor(prev); + const ThresholdState exp_state = checker.GetStateFor(prev); + BIP9Stats last_stats = checker.GetStateStatisticsFor(prev); + + int prev_next_height = (prev == nullptr ? 0 : prev->nHeight + 1); + assert(exp_since <= prev_next_height); + + // mine (period-1) blocks and check state + for (int b = 1; b < period; ++b) { + const bool signal = (signalling_mask >> (b % 32)) & 1; + if (signal) ++blocks_sig; + + CBlockIndex* current_block = blocks.mine_block(signal); + + // verify that signalling attempt was interpreted correctly + assert(checker.Condition(current_block) == signal); + + // state and since don't change within the period + const ThresholdState state = checker.GetStateFor(current_block); + const int since = checker.GetStateSinceHeightFor(current_block); + assert(state == exp_state); + assert(since == exp_since); + + // GetStateStatistics may crash when state is not STARTED + if (state != ThresholdState::STARTED) continue; + + // check that after mining this block stats change as expected + const BIP9Stats stats = checker.GetStateStatisticsFor(current_block); + assert(stats.period == period); + assert(stats.threshold == threshold); + assert(stats.elapsed == b); + assert(stats.count == last_stats.count + (signal ? 1 : 0)); + assert(stats.possible == (stats.count + period >= stats.elapsed + threshold)); + last_stats = stats; + } + + if (exp_state == ThresholdState::STARTED) { + // double check that stats.possible is sane + if (blocks_sig >= threshold - 1) assert(last_stats.possible); + } + + // mine the final block + bool signal = (signalling_mask >> (period % 32)) & 1; + if (signal) ++blocks_sig; + CBlockIndex* current_block = blocks.mine_block(signal); + assert(checker.Condition(current_block) == signal); + + // GetStateStatistics is safe on a period boundary + // and has progressed to a new period + const BIP9Stats stats = checker.GetStateStatisticsFor(current_block); + assert(stats.period == period); + assert(stats.threshold == threshold); + assert(stats.elapsed == 0); + assert(stats.count == 0); + assert(stats.possible == true); + + // More interesting is whether the state changed. + const ThresholdState state = checker.GetStateFor(current_block); + const int since = checker.GetStateSinceHeightFor(current_block); + + // since is straightforward: + assert(since % period == 0); + assert(0 <= since && since <= current_block->nHeight + 1); + if (state == exp_state) { + assert(since == exp_since); + } else { + assert(since == current_block->nHeight + 1); + } + + // state is where everything interesting is + switch (state) { + case ThresholdState::DEFINED: + assert(since == 0); + assert(exp_state == ThresholdState::DEFINED); + assert(current_block->GetMedianTimePast() < checker.m_begin); + assert(current_block->GetMedianTimePast() < checker.m_end); + break; + case ThresholdState::STARTED: + assert(current_block->GetMedianTimePast() >= checker.m_begin); + assert(current_block->GetMedianTimePast() < checker.m_end); + if (exp_state == ThresholdState::STARTED) { + assert(blocks_sig < threshold); + } else { + assert(exp_state == ThresholdState::DEFINED); + } + break; + case ThresholdState::LOCKED_IN: + assert(exp_state == ThresholdState::STARTED); + assert(current_block->GetMedianTimePast() < checker.m_end); + assert(blocks_sig >= threshold); + break; + case ThresholdState::ACTIVE: + assert(exp_state == ThresholdState::ACTIVE || exp_state == ThresholdState::LOCKED_IN); + break; + case ThresholdState::FAILED: + assert(current_block->GetMedianTimePast() >= checker.m_end); + assert(exp_state != ThresholdState::LOCKED_IN && exp_state != ThresholdState::ACTIVE); + break; + default: + assert(false); + } + + if (blocks.size() >= period * max_periods) { + // we chose the timeout (and block times) so that by the time we have this many blocks it's all over + assert(state == ThresholdState::ACTIVE || state == ThresholdState::FAILED); + } + + // "always active" has additional restrictions + if (always_active_test) { + assert(state == ThresholdState::ACTIVE); + assert(exp_state == ThresholdState::ACTIVE); + assert(since == 0); + } else { + // except for always active, the initial state is always DEFINED + assert(since > 0 || state == ThresholdState::DEFINED); + assert(exp_since > 0 || exp_state == ThresholdState::DEFINED); + } + + // "never active" does too + if (never_active_test) { + assert(state == ThresholdState::FAILED); + assert(since == period); + if (exp_since == 0) { + assert(exp_state == ThresholdState::DEFINED); + } else { + assert(exp_state == ThresholdState::FAILED); + } + } +} +} // namespace diff --git a/src/test/miner_tests.cpp b/src/test/miner_tests.cpp index 9e61e1058c6d9..cb56ecd027402 100644 --- a/src/test/miner_tests.cpp +++ b/src/test/miner_tests.cpp @@ -334,7 +334,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey)); m_node.mempool->clear(); - // orphan in *m_node.mempool, template creation fails + // orphan in mempool, template creation fails hash = tx.GetHash(); m_node.mempool->addUnchecked(entry.Fee(LOWFEE).Time(GetTime()).FromTx(tx)); BOOST_CHECK_EXCEPTION(AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey), std::runtime_error, HasReason("bad-txns-inputs-missingorspent")); @@ -357,7 +357,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey)); m_node.mempool->clear(); - // coinbase in *m_node.mempool, template creation fails + // coinbase in mempool, template creation fails tx.vin.resize(1); tx.vin[0].prevout.SetNull(); tx.vin[0].scriptSig = CScript() << OP_0 << OP_1; @@ -369,25 +369,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) BOOST_CHECK_EXCEPTION(AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey), std::runtime_error, HasReason("bad-cb-multiple")); m_node.mempool->clear(); - // invalid (pre-p2sh) txn in *m_node.mempool, template creation fails - tx.vin[0].prevout.hash = txFirst[0]->GetHash(); - tx.vin[0].prevout.n = 0; - tx.vin[0].scriptSig = CScript() << OP_1; - tx.vout[0].nValue = BLOCKSUBSIDY-LOWFEE; - script = CScript() << OP_0; - tx.vout[0].scriptPubKey = GetScriptForDestination(ScriptHash(script)); - hash = tx.GetHash(); - m_node.mempool->addUnchecked(entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx)); - tx.vin[0].prevout.hash = hash; - tx.vin[0].scriptSig = CScript() << std::vector(script.begin(), script.end()); - tx.vout[0].nValue -= LOWFEE; - hash = tx.GetHash(); - m_node.mempool->addUnchecked(entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(false).FromTx(tx)); - // Should throw block-validation-failed - BOOST_CHECK_EXCEPTION(AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey), std::runtime_error, HasReason("block-validation-failed")); - m_node.mempool->clear(); - - // double spend txn pair in *m_node.mempool, template creation fails + // double spend txn pair in mempool, template creation fails tx.vin[0].prevout.hash = txFirst[0]->GetHash(); tx.vin[0].scriptSig = CScript() << OP_1; tx.vout[0].nValue = BLOCKSUBSIDY-HIGHFEE; @@ -426,6 +408,25 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) // ::ChainActive().SetTip(next); // } //BOOST_CHECK(pblocktemplate = BlockAssembler(chainparams).CreateNewBlock(scriptPubKey)); + + // invalid (pre-p2sh) txn in mempool, template creation fails + tx.vin[0].prevout.hash = txFirst[0]->GetHash(); + tx.vin[0].prevout.n = 0; + tx.vin[0].scriptSig = CScript() << OP_1; + tx.vout[0].nValue = BLOCKSUBSIDY-LOWFEE; + script = CScript() << OP_0; + tx.vout[0].scriptPubKey = GetScriptForDestination(ScriptHash(script)); + hash = tx.GetHash(); + m_node.mempool->addUnchecked(entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx)); + tx.vin[0].prevout.hash = hash; + tx.vin[0].scriptSig = CScript() << std::vector(script.begin(), script.end()); + tx.vout[0].nValue -= LOWFEE; + hash = tx.GetHash(); + m_node.mempool->addUnchecked(entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(false).FromTx(tx)); + // Should throw block-validation-failed + BOOST_CHECK_EXCEPTION(AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey), std::runtime_error, HasReason("block-validation-failed")); + m_node.mempool->clear(); + // // Delete the dummy blocks again. // while (::ChainActive().Tip()->nHeight > nHeight) { // CBlockIndex* del = ::ChainActive().Tip(); diff --git a/src/test/versionbits_tests.cpp b/src/test/versionbits_tests.cpp index 9c657ffc6898f..6bafad64726a1 100644 --- a/src/test/versionbits_tests.cpp +++ b/src/test/versionbits_tests.cpp @@ -32,6 +32,12 @@ class TestConditionChecker : public AbstractThresholdConditionChecker int GetStateSinceHeightFor(const CBlockIndex* pindexPrev) const { return AbstractThresholdConditionChecker::GetStateSinceHeightFor(pindexPrev, paramsDummy, cache); } }; +class TestAlwaysActiveConditionChecker : public TestConditionChecker +{ +public: + int64_t BeginTime(const Consensus::Params& params) const override { return Consensus::BIP9Deployment::ALWAYS_ACTIVE; } +}; + #define CHECKERS 6 class VersionBitsTester @@ -43,6 +49,8 @@ class VersionBitsTester // The first one performs all checks, the second only 50%, the third only 25%, etc... // This is to test whether lack of cached information leads to the same results. TestConditionChecker checker[CHECKERS]; + // Another 6 that assume always active activation + TestAlwaysActiveConditionChecker checker_always[CHECKERS]; // Test counter (to identify failures) int num; @@ -56,6 +64,7 @@ class VersionBitsTester } for (unsigned int i = 0; i < CHECKERS; i++) { checker[i] = TestConditionChecker(); + checker_always[i] = TestAlwaysActiveConditionChecker(); } vpblock.clear(); return *this; @@ -82,6 +91,7 @@ class VersionBitsTester for (int i = 0; i < CHECKERS; i++) { if (InsecureRandBits(i) == 0) { BOOST_CHECK_MESSAGE(checker[i].GetStateSinceHeightFor(vpblock.empty() ? nullptr : vpblock.back()) == height, strprintf("Test %i for StateSinceHeight", num)); + BOOST_CHECK_MESSAGE(checker_always[i].GetStateSinceHeightFor(vpblock.empty() ? nullptr : vpblock.back()) == 0, strprintf("Test %i for StateSinceHeight (always active)", num)); } } num++; @@ -92,6 +102,7 @@ class VersionBitsTester for (int i = 0; i < CHECKERS; i++) { if (InsecureRandBits(i) == 0) { BOOST_CHECK_MESSAGE(checker[i].GetStateFor(vpblock.empty() ? nullptr : vpblock.back()) == ThresholdState::DEFINED, strprintf("Test %i for DEFINED", num)); + BOOST_CHECK_MESSAGE(checker_always[i].GetStateFor(vpblock.empty() ? nullptr : vpblock.back()) == ThresholdState::ACTIVE, strprintf("Test %i for ACTIVE (always active)", num)); } } num++; @@ -102,6 +113,7 @@ class VersionBitsTester for (int i = 0; i < CHECKERS; i++) { if (InsecureRandBits(i) == 0) { BOOST_CHECK_MESSAGE(checker[i].GetStateFor(vpblock.empty() ? nullptr : vpblock.back()) == ThresholdState::STARTED, strprintf("Test %i for STARTED", num)); + BOOST_CHECK_MESSAGE(checker_always[i].GetStateFor(vpblock.empty() ? nullptr : vpblock.back()) == ThresholdState::ACTIVE, strprintf("Test %i for ACTIVE (always active)", num)); } } num++; @@ -112,6 +124,7 @@ class VersionBitsTester for (int i = 0; i < CHECKERS; i++) { if (InsecureRandBits(i) == 0) { BOOST_CHECK_MESSAGE(checker[i].GetStateFor(vpblock.empty() ? nullptr : vpblock.back()) == ThresholdState::LOCKED_IN, strprintf("Test %i for LOCKED_IN", num)); + BOOST_CHECK_MESSAGE(checker_always[i].GetStateFor(vpblock.empty() ? nullptr : vpblock.back()) == ThresholdState::ACTIVE, strprintf("Test %i for ACTIVE (always active)", num)); } } num++; @@ -122,6 +135,7 @@ class VersionBitsTester for (int i = 0; i < CHECKERS; i++) { if (InsecureRandBits(i) == 0) { BOOST_CHECK_MESSAGE(checker[i].GetStateFor(vpblock.empty() ? nullptr : vpblock.back()) == ThresholdState::ACTIVE, strprintf("Test %i for ACTIVE", num)); + BOOST_CHECK_MESSAGE(checker_always[i].GetStateFor(vpblock.empty() ? nullptr : vpblock.back()) == ThresholdState::ACTIVE, strprintf("Test %i for ACTIVE (always active)", num)); } } num++; @@ -132,6 +146,7 @@ class VersionBitsTester for (int i = 0; i < CHECKERS; i++) { if (InsecureRandBits(i) == 0) { BOOST_CHECK_MESSAGE(checker[i].GetStateFor(vpblock.empty() ? nullptr : vpblock.back()) == ThresholdState::FAILED, strprintf("Test %i for FAILED", num)); + BOOST_CHECK_MESSAGE(checker_always[i].GetStateFor(vpblock.empty() ? nullptr : vpblock.back()) == ThresholdState::ACTIVE, strprintf("Test %i for ACTIVE (always active)", num)); } } num++; diff --git a/src/torcontrol.cpp b/src/torcontrol.cpp index c60506fb5e6c7..ca9eef212f9b8 100644 --- a/src/torcontrol.cpp +++ b/src/torcontrol.cpp @@ -52,77 +52,6 @@ static const int MAX_LINE_LENGTH = 100000; /****** Low-level TorControlConnection ********/ -/** Reply from Tor, can be single or multi-line */ -class TorControlReply -{ -public: - TorControlReply() { Clear(); } - - int code; - std::vector lines; - - void Clear() - { - code = 0; - lines.clear(); - } -}; - -/** Low-level handling for Tor control connection. - * Speaks the SMTP-like protocol as defined in torspec/control-spec.txt - */ -class TorControlConnection -{ -public: - typedef std::function ConnectionCB; - typedef std::function ReplyHandlerCB; - - /** Create a new TorControlConnection. - */ - explicit TorControlConnection(struct event_base *base); - ~TorControlConnection(); - - /** - * Connect to a Tor control port. - * tor_control_center is address of the form host:port. - * connected is the handler that is called when connection is successfully established. - * disconnected is a handler that is called when the connection is broken. - * Return true on success. - */ - bool Connect(const std::string& tor_control_center, const ConnectionCB& connected, const ConnectionCB& disconnected); - - /** - * Disconnect from Tor control port. - */ - void Disconnect(); - - /** Send a command, register a handler for the reply. - * A trailing CRLF is automatically added. - * Return true on success. - */ - bool Command(const std::string &cmd, const ReplyHandlerCB& reply_handler); - - /** Response handlers for async replies */ - boost::signals2::signal async_handler; -private: - /** Callback when ready for use */ - std::function connected; - /** Callback when connection lost */ - std::function disconnected; - /** Libevent event base */ - struct event_base *base; - /** Connection to control socket */ - struct bufferevent *b_conn; - /** Message being received */ - TorControlReply message; - /** Response handlers */ - std::deque reply_handlers; - - /** Libevent handlers: internal */ - static void readcb(struct bufferevent *bev, void *ctx); - static void eventcb(struct bufferevent *bev, short what, void *ctx); -}; - TorControlConnection::TorControlConnection(struct event_base *_base): base(_base), b_conn(nullptr) { @@ -366,55 +295,6 @@ std::map ParseTorReplyMapping(const std::string &s) return mapping; } -/****** Bitcoin specific TorController implementation ********/ - -/** Controller that connects to Tor control socket, authenticate, then create - * and maintain an ephemeral onion service. - */ -class TorController -{ -public: - TorController(struct event_base* base, const std::string& tor_control_center, const CService& target); - ~TorController(); - - /** Get name of file to store private key in */ - fs::path GetPrivateKeyFile(); - - /** Reconnect, after getting disconnected */ - void Reconnect(); -private: - struct event_base* base; - const std::string m_tor_control_center; - TorControlConnection conn; - std::string private_key; - std::string service_id; - bool reconnect; - struct event *reconnect_ev; - float reconnect_timeout; - CService service; - const CService m_target; - /** Cookie for SAFECOOKIE auth */ - std::vector cookie; - /** ClientNonce for SAFECOOKIE auth */ - std::vector clientNonce; - - /** Callback for ADD_ONION result */ - void add_onion_cb(TorControlConnection& conn, const TorControlReply& reply); - /** Callback for AUTHENTICATE result */ - void auth_cb(TorControlConnection& conn, const TorControlReply& reply); - /** Callback for AUTHCHALLENGE result */ - void authchallenge_cb(TorControlConnection& conn, const TorControlReply& reply); - /** Callback for PROTOCOLINFO result */ - void protocolinfo_cb(TorControlConnection& conn, const TorControlReply& reply); - /** Callback after successful connection */ - void connected_cb(TorControlConnection& conn); - /** Callback after connection lost or failed connection attempt */ - void disconnected_cb(TorControlConnection& conn); - - /** Callback for reconnect timer */ - static void reconnect_cb(evutil_socket_t fd, short what, void *arg); -}; - TorController::TorController(struct event_base* _base, const std::string& tor_control_center, const CService& target): base(_base), m_tor_control_center(tor_control_center), conn(base), reconnect(true), reconnect_ev(0), diff --git a/src/torcontrol.h b/src/torcontrol.h index ad5dada39bd77..f5dc9d413f4ab 100644 --- a/src/torcontrol.h +++ b/src/torcontrol.h @@ -8,7 +8,19 @@ #ifndef BITCOIN_TORCONTROL_H #define BITCOIN_TORCONTROL_H +#include +#include + +#include + +#include +#include + +#include +#include +#include #include +#include class CService; @@ -21,4 +33,128 @@ void StopTorControl(); CService DefaultOnionServiceTarget(); +/** Reply from Tor, can be single or multi-line */ +class TorControlReply +{ +public: + TorControlReply() { Clear(); } + + int code; + std::vector lines; + + void Clear() + { + code = 0; + lines.clear(); + } +}; + +/** Low-level handling for Tor control connection. + * Speaks the SMTP-like protocol as defined in torspec/control-spec.txt + */ +class TorControlConnection +{ +public: + typedef std::function ConnectionCB; + typedef std::function ReplyHandlerCB; + + /** Create a new TorControlConnection. + */ + explicit TorControlConnection(struct event_base *base); + ~TorControlConnection(); + + /** + * Connect to a Tor control port. + * tor_control_center is address of the form host:port. + * connected is the handler that is called when connection is successfully established. + * disconnected is a handler that is called when the connection is broken. + * Return true on success. + */ + bool Connect(const std::string& tor_control_center, const ConnectionCB& connected, const ConnectionCB& disconnected); + + /** + * Disconnect from Tor control port. + */ + void Disconnect(); + + /** Send a command, register a handler for the reply. + * A trailing CRLF is automatically added. + * Return true on success. + */ + bool Command(const std::string &cmd, const ReplyHandlerCB& reply_handler); + + /** Response handlers for async replies */ + boost::signals2::signal async_handler; +private: + /** Callback when ready for use */ + std::function connected; + /** Callback when connection lost */ + std::function disconnected; + /** Libevent event base */ + struct event_base *base; + /** Connection to control socket */ + struct bufferevent *b_conn; + /** Message being received */ + TorControlReply message; + /** Response handlers */ + std::deque reply_handlers; + + /** Libevent handlers: internal */ + static void readcb(struct bufferevent *bev, void *ctx); + static void eventcb(struct bufferevent *bev, short what, void *ctx); +}; + +/****** Bitcoin specific TorController implementation ********/ + +/** Controller that connects to Tor control socket, authenticate, then create + * and maintain an ephemeral onion service. + */ +class TorController +{ +public: + TorController(struct event_base* base, const std::string& tor_control_center, const CService& target); + TorController() : conn{nullptr} { + // Used for testing only. + } + ~TorController(); + + /** Get name of file to store private key in */ + fs::path GetPrivateKeyFile(); + + /** Reconnect, after getting disconnected */ + void Reconnect(); +private: + struct event_base* base; + const std::string m_tor_control_center; + TorControlConnection conn; + std::string private_key; + std::string service_id; + bool reconnect; + struct event *reconnect_ev = nullptr; + float reconnect_timeout; + CService service; + const CService m_target; + /** Cookie for SAFECOOKIE auth */ + std::vector cookie; + /** ClientNonce for SAFECOOKIE auth */ + std::vector clientNonce; + +public: + /** Callback for ADD_ONION result */ + void add_onion_cb(TorControlConnection& conn, const TorControlReply& reply); + /** Callback for AUTHENTICATE result */ + void auth_cb(TorControlConnection& conn, const TorControlReply& reply); + /** Callback for AUTHCHALLENGE result */ + void authchallenge_cb(TorControlConnection& conn, const TorControlReply& reply); + /** Callback for PROTOCOLINFO result */ + void protocolinfo_cb(TorControlConnection& conn, const TorControlReply& reply); + /** Callback after successful connection */ + void connected_cb(TorControlConnection& conn); + /** Callback after connection lost or failed connection attempt */ + void disconnected_cb(TorControlConnection& conn); + + /** Callback for reconnect timer */ + static void reconnect_cb(evutil_socket_t fd, short what, void *arg); +}; + #endif /* BITCOIN_TORCONTROL_H */ diff --git a/src/validation.cpp b/src/validation.cpp index 54621f3c93bdf..081c1c70db28e 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -1966,11 +1966,12 @@ static ThresholdConditionCache warningcache[VERSIONBITS_NUM_BITS] GUARDED_BY(cs_ static unsigned int GetBlockScriptFlags(const CBlockIndex* pindex, const Consensus::Params& consensusparams) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { AssertLockHeld(cs_main); - // BIP16 didn't become active until Apr 1 2012 - int64_t nBIP16SwitchTime = 1333238400; - bool fStrictPayToScriptHash = (pindex->GetBlockTime() >= nBIP16SwitchTime); + unsigned int flags = SCRIPT_VERIFY_NONE; - unsigned int flags = fStrictPayToScriptHash ? SCRIPT_VERIFY_P2SH : SCRIPT_VERIFY_NONE; + // Start enforcing P2SH (BIP16) + if (pindex->nHeight >= consensusparams.BIP16Height) { + flags |= SCRIPT_VERIFY_P2SH; + } // Start enforcing the DERSIG (BIP66) rule if (pindex->nHeight >= consensusparams.BIP66Height) { @@ -5507,11 +5508,11 @@ CBlockFileInfo* GetBlockFileInfo(size_t n) static const uint64_t MEMPOOL_DUMP_VERSION = 1; -bool LoadMempool(CTxMemPool& pool, CChainState& active_chainstate) +bool LoadMempool(CTxMemPool& pool, CChainState& active_chainstate, FopenFn mockable_fopen_function) { const CChainParams& chainparams = Params(); int64_t nExpiryTimeout = gArgs.GetArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY) * 60 * 60; - FILE* filestr = fsbridge::fopen(GetDataDir() / "mempool.dat", "rb"); + FILE* filestr{mockable_fopen_function(GetDataDir() / "mempool.dat", "rb")}; CAutoFile file(filestr, SER_DISK, CLIENT_VERSION); if (file.IsNull()) { LogPrintf("Failed to open mempool file from disk. Continuing anyway.\n"); @@ -5601,7 +5602,7 @@ bool LoadMempool(CTxMemPool& pool, CChainState& active_chainstate) return true; } -bool DumpMempool(const CTxMemPool& pool) +bool DumpMempool(const CTxMemPool& pool, FopenFn mockable_fopen_function, bool skip_file_commit) { int64_t start = GetTimeMicros(); @@ -5624,7 +5625,7 @@ bool DumpMempool(const CTxMemPool& pool) int64_t mid = GetTimeMicros(); try { - FILE* filestr = fsbridge::fopen(GetDataDir() / "mempool.dat.new", "wb"); + FILE* filestr{mockable_fopen_function(GetDataDir() / "mempool.dat.new", "wb")}; if (!filestr) { return false; } @@ -5647,7 +5648,7 @@ bool DumpMempool(const CTxMemPool& pool) LogPrintf("Writing %d unbroadcast transactions to disk.\n", unbroadcast_txids.size()); file << unbroadcast_txids; - if (!FileCommit(file.Get())) + if (!skip_file_commit && !FileCommit(file.Get())) throw std::runtime_error("FileCommit failed"); file.fclose(); if (!RenameOver(GetDataDir() / "mempool.dat.new", GetDataDir() / "mempool.dat")) { diff --git a/src/validation.h b/src/validation.h index 3e5f067aa7cbf..fdbd7ea4abe33 100644 --- a/src/validation.h +++ b/src/validation.h @@ -1087,11 +1087,13 @@ bool GetBlockHash(uint256& hashRet, int nBlockHeight = -1); /** Get block file info entry for one block file */ CBlockFileInfo* GetBlockFileInfo(size_t n); +using FopenFn = std::function; + /** Dump the mempool to disk. */ -bool DumpMempool(const CTxMemPool& pool); +bool DumpMempool(const CTxMemPool& pool, FopenFn mockable_fopen_function = fsbridge::fopen, bool skip_file_commit = false); /** Load the mempool from disk. */ -bool LoadMempool(CTxMemPool& pool, CChainState& active_chainstate); +bool LoadMempool(CTxMemPool& pool, CChainState& active_chainstate, FopenFn mockable_fopen_function = fsbridge::fopen); //! Check whether the block associated with this index entry is pruned or not. inline bool IsBlockPruned(const CBlockIndex* pblockindex) diff --git a/src/versionbits.cpp b/src/versionbits.cpp index 95ec5d6651425..e3910056d8234 100644 --- a/src/versionbits.cpp +++ b/src/versionbits.cpp @@ -11,6 +11,11 @@ ThresholdState AbstractThresholdConditionChecker::GetStateFor(const CBlockIndex* int64_t nTimeStart = BeginTime(params); int64_t nTimeTimeout = EndTime(params); + // Check if this deployment is always active. + if (nTimeStart == Consensus::BIP9Deployment::ALWAYS_ACTIVE) { + return ThresholdState::ACTIVE; + } + // A block's state is always the same as that of the first of its period, so it is computed based on a pindexPrev whose height equals a multiple of nPeriod - 1. if (pindexPrev != nullptr) { pindexPrev = pindexPrev->GetAncestor(pindexPrev->nHeight - ((pindexPrev->nHeight + 1) % nPeriod)); @@ -161,6 +166,11 @@ BIP9Stats AbstractThresholdConditionChecker::GetStateStatisticsFor(const CBlockI int AbstractThresholdConditionChecker::GetStateSinceHeightFor(const CBlockIndex* pindexPrev, const Consensus::Params& params, ThresholdConditionCache& cache) const { + int64_t start_time = BeginTime(params); + if (start_time == Consensus::BIP9Deployment::ALWAYS_ACTIVE) { + return 0; + } + const ThresholdState initialState = GetStateFor(pindexPrev, params, cache); // BIP 9 about state DEFINED: "The genesis block is by definition in this state for each deployment." diff --git a/test/functional/rpc_blockchain.py b/test/functional/rpc_blockchain.py index 699b297f47fc1..b9d4d3c00300f 100755 --- a/test/functional/rpc_blockchain.py +++ b/test/functional/rpc_blockchain.py @@ -145,7 +145,7 @@ def _test_getblockchaininfo(self): 'bip9': { 'status': 'defined', 'start_time': 0, - 'timeout': 999999999999, + 'timeout': 9223372036854775807, 'since': 0 }, 'active': False}, 'mn_rr': { @@ -153,7 +153,7 @@ def _test_getblockchaininfo(self): 'bip9': { 'status': 'defined', 'start_time': 0, - 'timeout': 999999999999, + 'timeout': 9223372036854775807, 'since': 0 }, 'active': False}, @@ -163,7 +163,7 @@ def _test_getblockchaininfo(self): 'status': 'started', 'bit': 28, 'start_time': 0, - 'timeout': 999999999999, # testdummy does not have a timeout so is set to the max int64 value + 'timeout': 9223372036854775807, # testdummy does not have a timeout so is set to the max int64 value 'since': 144, 'statistics': { 'period': 144,