Page MenuHomePhabricator

No OneTemporary

This file is larger than 256 KB, so syntax highlighting was skipped.
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 8c56a1053..ec9eebd2f 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -1,1024 +1,1028 @@
# Copyright (c) 2017 The Bitcoin developers
project(bitcoind)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# Default visibility is hidden on all targets.
set(CMAKE_C_VISIBILITY_PRESET hidden)
set(CMAKE_CXX_VISIBILITY_PRESET hidden)
option(BUILD_BITCOIN_WALLET "Activate the wallet functionality" ON)
option(BUILD_BITCOIN_ZMQ "Activate the ZeroMQ functionalities" ON)
option(BUILD_BITCOIN_CLI "Build bitcoin-cli" ON)
option(BUILD_BITCOIN_TX "Build bitcoin-tx" ON)
option(BUILD_BITCOIN_QT "Build bitcoin-qt" ON)
option(BUILD_BITCOIN_SEEDER "Build bitcoin-seeder" ON)
option(BUILD_LIBBITCOINCONSENSUS "Build the bitcoinconsenus shared library" ON)
option(BUILD_BITCOIN_CHAINSTATE "Build bitcoin-chainstate" OFF)
option(BUILD_BITCOIN_IGUANA "Activate the Iguana debugger" ON)
option(ENABLE_BIP70 "Enable BIP70 (payment protocol) support in GUI" ON)
option(ENABLE_HARDENING "Harden the executables" ON)
option(ENABLE_REDUCE_EXPORTS "Reduce the amount of exported symbols" OFF)
option(ENABLE_STATIC_LIBSTDCXX "Statically link libstdc++" OFF)
option(ENABLE_QRCODE "Enable QR code display" ON)
option(ENABLE_UPNP "Enable UPnP support" ON)
option(START_WITH_UPNP "Make UPnP the default to map ports" OFF)
option(ENABLE_NATPMP "Enable NAT-PMP support" ON)
option(START_WITH_NATPMP "Make NAT-PMP the default to map ports" OFF)
option(ENABLE_CLANG_TIDY "Enable clang-tidy checks for Bitcoin ABC" OFF)
option(ENABLE_PROFILING "Select the profiling tool to use" OFF)
option(ENABLE_TRACING "Enable eBPF user static defined tracepoints" OFF)
# Linker option
if(CMAKE_CROSSCOMPILING)
set(DEFAULT_LINKER "")
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
set(DEFAULT_LINKER lld)
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
set(DEFAULT_LINKER gold)
else()
set(DEFAULT_LINKER "")
endif()
set(USE_LINKER "${DEFAULT_LINKER}" CACHE STRING "Linker to be used (default: ${DEFAULT_LINKER}). Set to empty string to use the system's default.")
set(OS_WITH_JEMALLOC_AS_SYSTEM_DEFAULT
"Android"
"FreeBSD"
"NetBSD"
)
if(NOT CMAKE_SYSTEM_NAME IN_LIST OS_WITH_JEMALLOC_AS_SYSTEM_DEFAULT)
set(USE_JEMALLOC_DEFAULT ON)
endif()
# FIXME: Building against jemalloc causes the software to segfault on OSX.
# See https://github.com/Bitcoin-ABC/bitcoin-abc/issues/401
if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin" AND NOT CMAKE_CROSSCOMPILING)
set(USE_JEMALLOC_DEFAULT OFF)
endif()
option(USE_JEMALLOC "Use jemalloc as an allocation library" ${USE_JEMALLOC_DEFAULT})
if(${CMAKE_SYSTEM_NAME} MATCHES "Linux")
set(DEFAULT_ENABLE_DBUS_NOTIFICATIONS ON)
endif()
option(ENABLE_DBUS_NOTIFICATIONS "Enable DBus desktop notifications. Linux only." ${DEFAULT_ENABLE_DBUS_NOTIFICATIONS})
# If ccache is available, then use it.
find_program(CCACHE ccache)
if(CCACHE)
message(STATUS "Using ccache: ${CCACHE}")
set(CMAKE_C_COMPILER_LAUNCHER ${CCACHE})
set(CMAKE_CXX_COMPILER_LAUNCHER ${CCACHE})
endif(CCACHE)
# Disable what we do not need for the native build.
include(NativeExecutable)
native_add_cmake_flags(
"-DBUILD_BITCOIN_WALLET=OFF"
"-DBUILD_BITCOIN_CHRONIK=OFF"
"-DBUILD_BITCOIN_QT=OFF"
"-DBUILD_BITCOIN_ZMQ=OFF"
"-DENABLE_QRCODE=OFF"
"-DENABLE_NATPMP=OFF"
"-DENABLE_UPNP=OFF"
"-DUSE_JEMALLOC=OFF"
"-DENABLE_CLANG_TIDY=OFF"
"-DENABLE_BIP70=OFF"
"-DUSE_LINKER="
)
if(ENABLE_CLANG_TIDY)
include(ClangTidy)
endif()
if(ENABLE_SANITIZERS)
include(Sanitizers)
enable_sanitizers(${ENABLE_SANITIZERS})
endif()
include(AddCompilerFlags)
if(USE_LINKER)
set(LINKER_FLAG "-fuse-ld=${USE_LINKER}")
custom_check_linker_flag(IS_LINKER_SUPPORTED ${LINKER_FLAG})
if(NOT IS_LINKER_SUPPORTED)
message(FATAL_ERROR "The ${USE_LINKER} linker is not supported, make sure ${USE_LINKER} is properly installed or use -DUSE_LINKER= to use the system's linker")
endif()
add_linker_flags(${LINKER_FLAG})
# Remember the selected linker, it will be used for the subsequent
# custom_check_linker_flag calls
set(GLOBAL_LINKER_FLAGS ${LINKER_FLAG} CACHE INTERNAL "Additional linker flags for flag support checking")
endif()
# Prefer -g3, defaults to -g if unavailable
foreach(LANGUAGE C CXX)
set(COMPILER_DEBUG_LEVEL -g)
check_compiler_flags(G3_IS_SUPPORTED ${LANGUAGE} -g3)
if(${G3_IS_SUPPORTED})
set(COMPILER_DEBUG_LEVEL -g3)
endif()
add_compile_options_to_configuration_for_language(Debug ${LANGUAGE} ${COMPILER_DEBUG_LEVEL})
endforeach()
# Define some debugging symbols when the Debug build type is selected.
add_compile_definitions_to_configuration(Debug DEBUG DEBUG_LOCKORDER ABORT_ON_FAILED_ASSUME)
# Add -ftrapv when building in Debug
add_compile_options_to_configuration(Debug -ftrapv)
# All versions of gcc that we commonly use for building are subject to bug
# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=90348. To work around that, set
# -fstack-reuse=none for all gcc builds. (Only gcc understands this flag)
if(CMAKE_CXX_COMPILER_ID MATCHES "GNU")
add_compiler_flags(-fstack-reuse=none)
endif()
if(${CMAKE_SYSTEM_NAME} MATCHES "Windows")
# Ensure that WINDRES_PREPROC is enabled when using windres.
list(APPEND CMAKE_RC_FLAGS "-DWINDRES_PREPROC")
# Build all static so there is no dll file to distribute.
add_linker_flags(-static)
add_compile_definitions(
# Windows 7
_WIN32_WINNT=0x0601
# Internet Explorer 5.01 (!)
_WIN32_IE=0x0501
# Define WIN32_LEAN_AND_MEAN to exclude APIs such as Cryptography, DDE,
# RPC, Shell, and Windows Sockets.
WIN32_LEAN_AND_MEAN
)
# We require Windows 7 (NT 6.1) or later
add_linker_flags(-Wl,--major-subsystem-version,6 -Wl,--minor-subsystem-version,1)
endif()
if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
add_compile_definitions(MAC_OSX OBJC_OLD_DISPATCH_PROTOTYPES=0)
add_linker_flags(-Wl,-dead_strip_dylibs)
endif()
if(ENABLE_REDUCE_EXPORTS)
# Default visibility is set by CMAKE_<LANG>_VISIBILITY_PRESET, but this
# doesn't tell if the visibility set is effective.
# Check if the flag -fvisibility=hidden is supported, as using the hidden
# visibility is a requirement to reduce exports.
check_compiler_flags(HAS_CXX_FVISIBILITY CXX -fvisibility=hidden)
if(NOT HAS_CXX_FVISIBILITY)
message(FATAL_ERROR "Cannot set default symbol visibility. Use -DENABLE_REDUCE_EXPORTS=OFF.")
endif()
# Also hide symbols from static libraries
add_linker_flags(-Wl,--exclude-libs,libstdc++)
endif()
# Enable statically linking libstdc++
if(ENABLE_STATIC_LIBSTDCXX)
add_linker_flags(-static-libstdc++)
endif()
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
if(ENABLE_HARDENING)
# Enable stack protection
add_cxx_compiler_flags(-fstack-protector-all -Wstack-protector)
# Enable control-flow enforcement
add_cxx_compiler_flags(-fcf-protection=full)
if(NOT ${CMAKE_SYSTEM_NAME} MATCHES "Windows")
# stack-clash-protection does not work properly when building for Windows.
# See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=90458
add_cxx_compiler_flags(-fstack-clash-protection)
endif()
add_linker_flags(-Wl,-z,noexecstack)
# Enable some buffer overflow checking, except in -O0 builds which
# do not support them
add_compiler_flags(-U_FORTIFY_SOURCE)
add_compile_options($<$<NOT:$<CONFIG:Debug>>:-D_FORTIFY_SOURCE=2>)
# Enable ASLR (these flags are primarily targeting MinGw)
add_linker_flags(-Wl,--enable-reloc-section -Wl,--dynamicbase -Wl,--nxcompat -Wl,--high-entropy-va)
# Make the relocated sections read-only
add_linker_flags(-Wl,-z,relro -Wl,-z,now)
# Avoids mixing code pages with data to improve cache performance as well
# as security
add_linker_flags(-Wl,-z,separate-code)
# CMake provides the POSITION_INDEPENDENT_CODE property to set PIC/PIE.
cmake_policy(SET CMP0083 NEW)
include(CheckPIESupported)
check_pie_supported()
if(${CMAKE_SYSTEM_NAME} MATCHES "Windows")
# MinGw provides its own libssp for stack smashing protection.
# It might not be needed for recent versions of mingw, but the version
# shipped with debian bullseye needs it and we still support it at the
# time of writing.
link_libraries(ssp)
endif()
if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
add_linker_flags(-Wl,-fixup_chains)
endif()
endif()
if(ENABLE_PROFILING MATCHES "gprof")
message(STATUS "Enable profiling with gprof")
# -pg is incompatible with -pie. Since hardening and profiling together
# doesn't make sense, we simply make them mutually exclusive here.
# Additionally, hardened toolchains may force -pie by default, in which
# case it needs to be turned off with -no-pie.
if(ENABLE_HARDENING)
message(FATAL_ERROR "Profiling with gprof requires disabling hardening with -DENABLE_HARDENING=OFF.")
endif()
add_linker_flags(-no-pie)
add_compiler_flags(-pg)
add_linker_flags(-pg)
endif()
# Enable warning
add_c_compiler_flags(-Wnested-externs -Wstrict-prototypes)
add_compiler_flags(
-Wall
-Wextra
-Wformat
-Wgnu
-Wvla
-Wcast-align
-Wunused-parameter
-Wmissing-braces
-Wthread-safety
-Wrange-loop-analysis
-Wredundant-decls
-Wunreachable-code-loop-increment
-Wsign-compare
-Wconditional-uninitialized
-Wduplicated-branches
-Wduplicated-cond
-Wlogical-op
-Wdocumentation
)
add_compiler_flag_group(-Wformat -Wformat-security)
add_cxx_compiler_flags(
-Wredundant-move
-Woverloaded-virtual
)
if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
# GCC has no flag variant which is granular enough to avoid raising the clang
# -Wshadow-uncaptured-local equivalent. This is causing a lot of warnings
# on serialize.h which cannot be disabled locally, so drop the flag.
add_compiler_flags(
-Wshadow
-Wshadow-field
)
endif()
option(EXTRA_WARNINGS "Enable extra warnings" OFF)
if(EXTRA_WARNINGS)
add_cxx_compiler_flags(-Wsuggest-override)
else()
add_compiler_flags(
-Wno-unused-parameter
-Wno-implicit-fallthrough
-Wno-psabi
)
endif()
# libtool style configure
add_subdirectory(config)
# Enable LFS (Large File Support) on targets that don't have it natively.
# This should be defined before the libraries are included as leveldb need the
# definition to be set.
if(NOT HAVE_LARGE_FILE_SUPPORT)
add_compile_definitions(_FILE_OFFSET_BITS=64)
add_linker_flags(-Wl,--large-address-aware)
endif()
if(USE_JEMALLOC)
# Most of the sanitizers require their instrumented allocation functions to
# be fully functional. This is obviously the case for all the memory related
# sanitizers (asan, lsan, msan) but not only.
if(ENABLE_SANITIZERS)
message(WARNING "Jemalloc is incompatible with the sanitizers and has been disabled.")
else()
find_package(Jemalloc 3.6.0 REQUIRED)
link_libraries(Jemalloc::jemalloc)
endif()
endif()
# These flags are needed for using std::filesystem with GCC < 9.1 & Clang < 9.0
# Since these are optional libraries they need to be placed accordingly on the
# command line.
add_linker_flags(-lstdc++fs -lc++fs)
custom_check_linker_flag(LINKER_HAS_STDCXXFS "-lstdc++fs")
if(LINKER_HAS_STDCXXFS)
link_libraries(stdc++fs)
endif()
custom_check_linker_flag(LINKER_HAS_CXXFS "-lc++fs")
if(LINKER_HAS_CXXFS)
link_libraries(c++fs)
endif()
# Make sure that all the global compiler and linker flags are set BEFORE
# including the libraries so they apply as needed.
# libraries
add_subdirectory(crypto)
add_subdirectory(leveldb)
add_subdirectory(secp256k1)
add_subdirectory(univalue)
# Find the git root, and returns the full path to the .git/logs/HEAD file if
# it exists.
function(find_git_head_logs_file RESULT)
find_package(Git)
if(GIT_FOUND)
execute_process(
COMMAND "${GIT_EXECUTABLE}" "rev-parse" "--show-toplevel"
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
OUTPUT_VARIABLE GIT_ROOT
RESULT_VARIABLE GIT_RESULT
OUTPUT_STRIP_TRAILING_WHITESPACE
ERROR_QUIET
)
if(GIT_RESULT EQUAL 0)
set(GIT_LOGS_DIR "${GIT_ROOT}/.git/logs")
set(GIT_HEAD_LOGS_FILE "${GIT_LOGS_DIR}/HEAD")
# If the .git/logs/HEAD does not exist, create it
if(NOT EXISTS "${GIT_HEAD_LOGS_FILE}")
file(MAKE_DIRECTORY "${GIT_LOGS_DIR}")
file(TOUCH "${GIT_HEAD_LOGS_FILE}")
endif()
set(${RESULT} "${GIT_HEAD_LOGS_FILE}" PARENT_SCOPE)
endif()
endif()
endfunction()
find_git_head_logs_file(GIT_HEAD_LOGS_FILE)
set(OBJ_DIR "${CMAKE_CURRENT_BINARY_DIR}/obj")
file(MAKE_DIRECTORY "${OBJ_DIR}")
set(BUILD_HEADER "${OBJ_DIR}/build.h")
set(BUILD_HEADER_TMP "${BUILD_HEADER}.tmp")
add_custom_command(
DEPENDS
"${GIT_HEAD_LOGS_FILE}"
"${CMAKE_SOURCE_DIR}/share/genbuild.sh"
OUTPUT
"${BUILD_HEADER}"
COMMAND
"${CMAKE_SOURCE_DIR}/share/genbuild.sh"
"${BUILD_HEADER_TMP}"
"${CMAKE_SOURCE_DIR}"
COMMAND
${CMAKE_COMMAND} -E copy_if_different "${BUILD_HEADER_TMP}" "${BUILD_HEADER}"
COMMAND
${CMAKE_COMMAND} -E remove "${BUILD_HEADER_TMP}"
)
# Because the Bitcoin ABc source code is disorganised, we
# end up with a bunch of libraries without any apparent
# cohesive structure. This is inherited from Bitcoin Core
# and reflecting this.
# TODO: Improve the structure once cmake is rocking.
# Various completely unrelated features shared by all executables.
add_library(util
chainparamsbase.cpp
clientversion.cpp
compat/strnlen.cpp
currencyunit.cpp
interfaces/handler.cpp
logging.cpp
random.cpp
randomenv.cpp
rcu.cpp
rpc/request.cpp
support/cleanse.cpp
support/lockedpool.cpp
sync.cpp
threadinterrupt.cpp
uint256.cpp
util/asmap.cpp
util/batchpriority.cpp
util/bip32.cpp
util/bytevectorhash.cpp
util/check.cpp
util/hasher.cpp
util/exception.cpp
util/error.cpp
util/fs.cpp
util/fs_helpers.cpp
util/getuniquepath.cpp
util/message.cpp
util/moneystr.cpp
util/readwritefile.cpp
util/settings.cpp
util/string.cpp
util/sock.cpp
util/spanparsing.cpp
util/strencodings.cpp
util/string.cpp
util/syserror.cpp
util/thread.cpp
util/threadnames.cpp
util/time.cpp
util/tokenpipe.cpp
util/url.cpp
# obj/build.h
"${BUILD_HEADER}"
)
target_compile_definitions(util PUBLIC HAVE_CONFIG_H HAVE_BUILD_INFO)
target_include_directories(util
PUBLIC
.
# To access the config/ and obj/ directories
${CMAKE_CURRENT_BINARY_DIR}
)
# Work around jemalloc printing harmless warnings to stderr with qemu.
# We match the emulator against qemu but not ^qemu so we are compatible with the
# use of absolute paths.
if(CMAKE_CROSSCOMPILING AND USE_JEMALLOC AND CMAKE_CROSSCOMPILING_EMULATOR MATCHES "qemu-.+")
message(WARNING "Overriding Jemalloc malloc_message() function for use with QEMU.")
target_sources(util PRIVATE jemalloc_message.cpp)
endif()
set(Boost_USE_STATIC_LIBS ON)
macro(link_windows_dependencies TARGET)
find_package(SHLWAPI REQUIRED)
target_link_libraries(${TARGET} SHLWAPI::shlwapi)
find_library(WS2_32_LIBRARY NAMES ws2_32)
target_link_libraries(${TARGET} ${WS2_32_LIBRARY})
endmacro()
# Target specific configs
if(${CMAKE_SYSTEM_NAME} MATCHES "Windows")
set(Boost_USE_STATIC_RUNTIME ON)
set(Boost_THREADAPI win32)
link_windows_dependencies(util)
endif()
target_link_libraries(util univalue crypto)
macro(link_event TARGET)
non_native_target_link_libraries(${TARGET} Event 2.1.8 ${ARGN})
endmacro()
link_event(util event)
macro(link_boost_headers_only TARGET)
non_native_target_link_boost_headers_only(${TARGET} 1.64)
endmacro()
link_boost_headers_only(util)
target_compile_definitions(util PUBLIC BOOST_NO_CXX98_FUNCTION_BASE)
set(THREADS_PREFER_PTHREAD_FLAG ON)
find_package(Threads REQUIRED)
target_link_libraries(util Threads::Threads)
function(add_network_sources NETWORK_SOURCES)
set(NETWORK_DIR abc)
list(TRANSFORM
ARGN
PREPEND "networks/${NETWORK_DIR}/"
OUTPUT_VARIABLE NETWORK_SOURCES
)
set(NETWORK_SOURCES ${NETWORK_SOURCES} PARENT_SCOPE)
endfunction()
add_network_sources(NETWORK_SOURCES
checkpoints.cpp
chainparamsconstants.cpp
)
# More completely unrelated features shared by all executables.
# Because nothing says this is different from util than "common"
add_library(common
base58.cpp
common/args.cpp
common/bloom.cpp
common/configfile.cpp
common/system.cpp
cashaddr.cpp
cashaddrenc.cpp
chainparams.cpp
config.cpp
consensus/merkle.cpp
coins.cpp
compressor.cpp
eventloop.cpp
feerate.cpp
core_read.cpp
core_write.cpp
kernel/chainparams.cpp
key.cpp
key_io.cpp
merkleblock.cpp
net_permissions.cpp
netaddress.cpp
netbase.cpp
outputtype.cpp
policy/policy.cpp
primitives/block.cpp
protocol.cpp
psbt.cpp
rpc/rawtransaction_util.cpp
rpc/util.cpp
scheduler.cpp
warnings.cpp
${NETWORK_SOURCES}
)
target_link_libraries(common bitcoinconsensus util secp256k1 script)
# script library
add_library(script
script/bitfield.cpp
script/descriptor.cpp
script/interpreter.cpp
script/intmath.cpp
script/script.cpp
script/script_error.cpp
script/sigencoding.cpp
script/sign.cpp
script/signingprovider.cpp
script/standard.cpp
)
target_link_libraries(script common)
# libbitcoinconsensus
add_library(bitcoinconsensus
arith_uint256.cpp
hash.cpp
primitives/transaction.cpp
pubkey.cpp
uint256.cpp
util/strencodings.cpp
consensus/amount.cpp
consensus/tx_check.cpp
)
target_link_libraries(bitcoinconsensus script)
include(InstallationHelper)
if(BUILD_LIBBITCOINCONSENSUS)
target_compile_definitions(bitcoinconsensus
PUBLIC
BUILD_BITCOIN_INTERNAL
HAVE_CONSENSUS_LIB
)
install_shared_library(bitcoinconsensus
script/bitcoinconsensus.cpp
PUBLIC_HEADER script/bitcoinconsensus.h
)
endif()
# Bitcoin server facilities
add_library(server
addrdb.cpp
addrman.cpp
avalanche/compactproofs.cpp
avalanche/delegation.cpp
avalanche/delegationbuilder.cpp
avalanche/peermanager.cpp
avalanche/processor.cpp
avalanche/proof.cpp
avalanche/proofid.cpp
avalanche/proofbuilder.cpp
avalanche/proofpool.cpp
avalanche/stakecontendercache.cpp
avalanche/voterecord.cpp
banman.cpp
blockencodings.cpp
blockfileinfo.cpp
blockfilter.cpp
blockindex.cpp
chain.cpp
checkpoints.cpp
config.cpp
consensus/activation.cpp
consensus/tx_verify.cpp
dbwrapper.cpp
deploymentstatus.cpp
dnsseeds.cpp
flatfile.cpp
headerssync.cpp
httprpc.cpp
httpserver.cpp
i2p.cpp
index/base.cpp
index/blockfilterindex.cpp
index/coinstatsindex.cpp
index/txindex.cpp
init.cpp
init/common.cpp
invrequest.cpp
+ kernel/checks.cpp
kernel/coinstats.cpp
+ kernel/context.cpp
kernel/cs_main.cpp
kernel/disconnected_transactions.cpp
kernel/mempool_persist.cpp
mapport.cpp
mempool_args.cpp
minerfund.cpp
net.cpp
net_processing.cpp
node/blockmanager_args.cpp
node/blockstorage.cpp
node/caches.cpp
node/chainstate.cpp
node/chainstatemanager_args.cpp
node/coin.cpp
node/coinstats.cpp
node/coins_view_args.cpp
node/context.cpp
node/database_args.cpp
node/interfaces.cpp
node/kernel_notifications.cpp
node/mempool_persist_args.cpp
node/miner.cpp
node/peerman_args.cpp
node/psbt.cpp
node/transaction.cpp
node/ui_interface.cpp
node/utxo_snapshot.cpp
node/validation_cache_args.cpp
noui.cpp
policy/block/minerfund.cpp
policy/block/preconsensus.cpp
policy/block/rtt.cpp
policy/block/stakingrewards.cpp
policy/fees.cpp
policy/packages.cpp
policy/settings.cpp
pow/aserti32d.cpp
pow/daa.cpp
pow/eda.cpp
pow/grasberg.cpp
pow/pow.cpp
rest.cpp
rpc/abc.cpp
rpc/avalanche.cpp
rpc/blockchain.cpp
rpc/command.cpp
rpc/mempool.cpp
rpc/mining.cpp
rpc/misc.cpp
rpc/net.cpp
rpc/rawtransaction.cpp
rpc/server.cpp
rpc/server_util.cpp
rpc/txoutproof.cpp
script/scriptcache.cpp
script/sigcache.cpp
shutdown.cpp
timedata.cpp
torcontrol.cpp
txdb.cpp
txmempool.cpp
txpool.cpp
validation.cpp
validationinterface.cpp
versionbits.cpp
)
target_include_directories(server PRIVATE leveldb/helpers/memenv)
target_link_libraries(server
bitcoinconsensus
leveldb
memenv
)
link_event(server event)
if(NOT ${CMAKE_SYSTEM_NAME} MATCHES "Windows")
link_event(server pthreads)
endif()
if(ENABLE_UPNP)
find_package(MiniUPnPc 1.9 REQUIRED)
target_link_libraries(server MiniUPnPc::miniupnpc)
if(${CMAKE_SYSTEM_NAME} MATCHES "Windows")
# TODO: check if we are really using a static library. Assume this is
# the one from the depends for now since the native windows build is not
# supported.
target_compile_definitions(server
PUBLIC -DSTATICLIB
PUBLIC -DMINIUPNP_STATICLIB
)
endif()
endif()
if(ENABLE_NATPMP)
find_package(NATPMP REQUIRED)
target_link_libraries(server NATPMP::natpmp)
if(${CMAKE_SYSTEM_NAME} MATCHES "Windows")
target_compile_definitions(server
PUBLIC -DSTATICLIB
PUBLIC -DNATPMP_STATICLIB
)
endif()
endif()
# Test suites.
add_subdirectory(test)
add_subdirectory(avalanche/test)
add_subdirectory(pow/test)
# Benchmark suite.
add_subdirectory(bench)
include(BinaryTest)
include(WindowsVersionInfo)
# Wallet
if(BUILD_BITCOIN_WALLET)
add_subdirectory(wallet)
target_link_libraries(server wallet)
# bitcoin-wallet
add_executable(bitcoin-wallet bitcoin-wallet.cpp)
generate_windows_version_info(bitcoin-wallet
DESCRIPTION "CLI tool for ${PACKAGE_NAME} wallets"
)
target_link_libraries(bitcoin-wallet wallet-tool common util)
add_to_symbols_check(bitcoin-wallet)
add_to_security_check(bitcoin-wallet)
install_target(bitcoin-wallet)
install_manpages(bitcoin-wallet)
else()
target_sources(server PRIVATE dummywallet.cpp)
endif()
# ZeroMQ
if(BUILD_BITCOIN_ZMQ)
add_subdirectory(zmq)
target_link_libraries(server zmq)
# FIXME: This is needed because of an unwanted dependency:
# zmqpublishnotifier.cpp -> blockstorage.h -> txdb.h -> dbwrapper.h -> leveldb/db.h
target_link_libraries(zmq leveldb)
endif()
# RPC client support
add_library(rpcclient
compat/stdin.cpp
rpc/client.cpp
)
target_link_libraries(rpcclient univalue util)
# bitcoin-seeder
if(BUILD_BITCOIN_SEEDER)
add_subdirectory(seeder)
endif()
# bitcoin-cli
if(BUILD_BITCOIN_CLI)
add_executable(bitcoin-cli bitcoin-cli.cpp)
generate_windows_version_info(bitcoin-cli
DESCRIPTION "JSON-RPC client for ${PACKAGE_NAME}"
)
target_link_libraries(bitcoin-cli common rpcclient)
link_event(bitcoin-cli event)
add_to_symbols_check(bitcoin-cli)
add_to_security_check(bitcoin-cli)
install_target(bitcoin-cli)
install_manpages(bitcoin-cli)
endif()
# bitcoin-tx
if(BUILD_BITCOIN_TX)
add_executable(bitcoin-tx bitcoin-tx.cpp)
generate_windows_version_info(bitcoin-tx
DESCRIPTION "CLI Bitcoin transaction editor utility"
)
target_link_libraries(bitcoin-tx bitcoinconsensus)
add_to_symbols_check(bitcoin-tx)
add_to_security_check(bitcoin-tx)
install_target(bitcoin-tx)
install_manpages(bitcoin-tx)
endif()
# bitcoin-chainstate
if(BUILD_BITCOIN_CHAINSTATE)
# TODO: libbitcoinkernel is a work in progress consensus engine library, as more
# and more modules are decoupled from the consensus engine, this list will
# shrink to only those which are absolutely necessary. For example, things
# like index/*.cpp will be removed.
add_library(bitcoinkernel
kernel/bitcoinkernel.cpp
kernel/chainparams.cpp
+ kernel/checks.cpp
kernel/coinstats.cpp
+ kernel/context.cpp
kernel/cs_main.cpp
kernel/disconnected_transactions.cpp
kernel/mempool_persist.cpp
arith_uint256.cpp
blockfileinfo.cpp
blockindex.cpp
common/args.cpp
common/bloom.cpp
common/configfile.cpp
chain.cpp
chainparamsbase.cpp
chainparams.cpp
checkpoints.cpp
clientversion.cpp
coins.cpp
compressor.cpp
config.cpp
consensus/activation.cpp
consensus/amount.cpp
consensus/merkle.cpp
consensus/tx_check.cpp
consensus/tx_verify.cpp
core_read.cpp
dbwrapper.cpp
deploymentstatus.cpp
eventloop.cpp
feerate.cpp
flatfile.cpp
hash.cpp
init/common.cpp
key.cpp
logging.cpp
networks/abc/chainparamsconstants.cpp
networks/abc/checkpoints.cpp
node/blockstorage.cpp
node/chainstate.cpp
node/ui_interface.cpp
node/utxo_snapshot.cpp
policy/fees.cpp
policy/packages.cpp
policy/policy.cpp
policy/settings.cpp
pow/aserti32d.cpp
pow/daa.cpp
pow/eda.cpp
pow/pow.cpp
primitives/block.cpp
primitives/transaction.cpp
pubkey.cpp
random.cpp
randomenv.cpp
rcu.cpp
scheduler.cpp
script/bitfield.cpp
script/interpreter.cpp
script/script.cpp
script/scriptcache.cpp
script/script_error.cpp
script/sigcache.cpp
script/sigencoding.cpp
script/standard.cpp
shutdown.cpp
support/cleanse.cpp
support/lockedpool.cpp
sync.cpp
threadinterrupt.cpp
txdb.cpp
txmempool.cpp
txpool.cpp
uint256.cpp
util/batchpriority.cpp
util/bytevectorhash.cpp
util/check.cpp
util/exception.cpp
util/fs.cpp
util/fs_helpers.cpp
util/getuniquepath.cpp
util/hasher.cpp
util/moneystr.cpp
util/settings.cpp
util/strencodings.cpp
util/string.cpp
util/syserror.cpp
util/thread.cpp
util/threadnames.cpp
util/time.cpp
util/tokenpipe.cpp
validation.cpp
validationinterface.cpp
versionbits.cpp
warnings.cpp
# Bitcoin ABC specific dependencies that will not go away with Core backports
addrdb.cpp # via banman.cpp
addrman.cpp # via net.cpp
banman.cpp # via net.cpp
base58.cpp # via key_io.cpp
avalanche/delegation.cpp
avalanche/delegationbuilder.cpp
avalanche/peermanager.cpp
avalanche/processor.cpp
avalanche/proof.cpp
avalanche/proofid.cpp
avalanche/proofpool.cpp
avalanche/stakecontendercache.cpp
avalanche/voterecord.cpp
cashaddr.cpp # via cashaddrenc.cpp
cashaddrenc.cpp # via key_io.cpp
dnsseeds.cpp # via net.cpp (GetRandomizedDNSSeeds)
i2p.cpp # via net.cppTx
key_io.cpp # avalanche/processor.cpp uses DecodeSecret
minerfund.cpp # via policy/block/minerfund.cpp
net.cpp # avalanche uses CConnman
netaddress.cpp # via net.cpp
netbase.cpp # via net.cpp
net_permissions.cpp # via net.cpp
policy/block/minerfund.cpp
policy/block/preconsensus.cpp
policy/block/rtt.cpp
policy/block/stakingrewards.cpp
protocol.cpp # avalanche/processor.cpp uses NetMsgType
timedata.cpp # via net.cpp
util/asmap.cpp # via netaddress.cpp
util/error.cpp # via net_permissions.cpp (ResolveErrMsg)
util/readwritefile.cpp # via i2p.cpp
util/sock.cpp # via net.cpp
)
target_include_directories(bitcoinkernel
PUBLIC
.
leveldb/helpers/memenv
# To access the config/ and obj/ directories
${CMAKE_CURRENT_BINARY_DIR}
)
target_link_libraries(bitcoinkernel crypto univalue secp256k1 leveldb memenv)
link_boost_headers_only(bitcoinkernel headers)
if(${CMAKE_SYSTEM_NAME} MATCHES "Windows")
link_windows_dependencies(bitcoinkernel)
endif()
add_executable(bitcoin-chainstate bitcoin-chainstate.cpp)
target_link_libraries(bitcoin-chainstate bitcoinkernel)
generate_windows_version_info(bitcoin-chainstate
DESCRIPTION "CLI Datadir information utility (experimental)"
)
add_to_symbols_check(bitcoin-chainstate)
add_to_security_check(bitcoin-chainstate)
endif()
# bitcoind
add_executable(bitcoind bitcoind.cpp)
target_link_libraries(bitcoind server)
generate_windows_version_info(bitcoind
DESCRIPTION "Bitcoin node with a JSON-RPC server"
)
add_to_symbols_check(bitcoind)
add_to_security_check(bitcoind)
install_target(bitcoind)
install_manpages(bitcoind)
# Bitcoin-qt
if(BUILD_BITCOIN_QT)
add_subdirectory(qt)
endif()
if(BUILD_BITCOIN_IGUANA)
add_subdirectory(iguana)
endif()
diff --git a/src/bitcoin-chainstate.cpp b/src/bitcoin-chainstate.cpp
index 8dadbad3d..898c36c27 100644
--- a/src/bitcoin-chainstate.cpp
+++ b/src/bitcoin-chainstate.cpp
@@ -1,332 +1,336 @@
// Copyright (c) 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.
//
// The bitcoin-chainstate executable serves to surface the dependencies required
// by a program wishing to use Bitcoin ABC's consensus engine as it is right
// now.
//
// DEVELOPER NOTE: Since this is a "demo-only", experimental, etc. executable,
// it may diverge from Bitcoin ABC's coding style.
//
// It is part of the libbitcoinkernel project.
#include <kernel/chainparams.h>
#include <kernel/chainstatemanager_opts.h>
+#include <kernel/checks.h>
+#include <kernel/context.h>
#include <kernel/validation_cache_sizes.h>
#include <chainparams.h>
#include <common/args.h>
#include <config.h>
#include <consensus/validation.h>
#include <core_io.h>
-#include <init/common.h>
#include <node/blockstorage.h>
#include <node/caches.h>
#include <node/chainstate.h>
#include <scheduler.h>
#include <script/scriptcache.h>
#include <script/sigcache.h>
#include <util/thread.h>
#include <util/translation.h>
#include <validation.h>
#include <validationinterface.h>
+#include <cassert>
#include <cstdint>
#include <filesystem>
#include <functional>
#include <iosfwd>
#include <memory>
int main(int argc, char *argv[]) {
// SETUP: Argument parsing and handling
if (argc != 2) {
std::cerr << "Usage: " << argv[0] << " DATADIR" << std::endl
<< "Display DATADIR information, and process hex-encoded "
"blocks on standard input."
<< std::endl
<< std::endl
<< "IMPORTANT: THIS EXECUTABLE IS EXPERIMENTAL, FOR TESTING "
"ONLY, AND EXPECTED TO"
<< std::endl
<< " BREAK IN FUTURE VERSIONS. DO NOT USE ON YOUR "
"ACTUAL DATADIR."
<< std::endl;
return 1;
}
std::filesystem::path abs_datadir = std::filesystem::absolute(argv[1]);
std::filesystem::create_directories(abs_datadir);
gArgs.ForceSetArg("-datadir", abs_datadir.string());
// SETUP: Misc Globals
SelectParams(CBaseChainParams::MAIN);
auto chainparams = CChainParams::Main(CChainParams::ChainOptions{});
auto &config = const_cast<Config &>(GetConfig());
config.SetChainParams(*chainparams);
// ECC_Start, etc.
- init::SetGlobals();
+ kernel::Context kernel_context{};
+ // We can't use a goto here, but we can use an assert since none of the
+ // things instantiated so far requires running the epilogue to be torn down
+ // properly
+ assert(!kernel::SanityChecks(kernel_context).has_value());
// Necessary for CheckInputScripts (eventually called by ProcessNewBlock),
// which will try the script cache first and fall back to actually
// performing the check with the signature cache.
kernel::ValidationCacheSizes validation_cache_sizes{};
Assert(InitSignatureCache(validation_cache_sizes.signature_cache_bytes));
Assert(InitScriptExecutionCache(
validation_cache_sizes.script_execution_cache_bytes));
// SETUP: Scheduling and Background Signals
CScheduler scheduler{};
// Start the lightweight task scheduler thread
scheduler.m_service_thread = std::thread(util::TraceThread, "scheduler",
[&] { scheduler.serviceQueue(); });
// Gather some entropy once per minute.
scheduler.scheduleEvery(
[] {
RandAddPeriodic();
return true;
},
std::chrono::minutes{1});
GetMainSignals().RegisterBackgroundSignalScheduler(scheduler);
class KernelNotifications : public kernel::Notifications {
public:
void blockTip(SynchronizationState, CBlockIndex &) override {
std::cout << "Block tip changed" << std::endl;
}
void headerTip(SynchronizationState, int64_t height, int64_t timestamp,
bool presync) override {
std::cout << "Header tip changed: " << height << ", " << timestamp
<< ", " << presync << std::endl;
}
void progress(const bilingual_str &title, int progress_percent,
bool resume_possible) override {
std::cout << "Progress: " << title.original << ", "
<< progress_percent << ", " << resume_possible
<< std::endl;
}
void warning(const std::string &warning) override {
std::cout << "Warning: " << warning << std::endl;
}
};
auto notifications = std::make_unique<KernelNotifications>();
// SETUP: Chainstate
const ChainstateManager::Options chainman_opts{
.config = config,
.datadir = gArgs.GetDataDirNet(),
.adjusted_time_callback = NodeClock::now,
.notifications = *notifications,
};
const node::BlockManager::Options blockman_opts{
.chainparams = chainman_opts.config.GetChainParams(),
.blocks_dir = gArgs.GetBlocksDirPath(),
};
ChainstateManager chainman{chainman_opts, blockman_opts};
node::CacheSizes cache_sizes;
cache_sizes.block_tree_db = 2 << 20;
cache_sizes.coins_db = 2 << 22;
cache_sizes.coins = (450 << 20) - (2 << 20) - (2 << 22);
node::ChainstateLoadOptions options;
options.check_interrupt = [] { return false; };
auto [status, error] = node::LoadChainstate(chainman, cache_sizes, options);
if (status != node::ChainstateLoadStatus::SUCCESS) {
std::cerr << "Failed to load Chain state from your datadir."
<< std::endl;
goto epilogue;
}
std::tie(status, error) = node::VerifyLoadedChainstate(chainman, options);
if (status != node::ChainstateLoadStatus::SUCCESS) {
std::cerr << "Failed to verify loaded Chain state from your datadir."
<< std::endl;
goto epilogue;
}
for (Chainstate *chainstate :
WITH_LOCK(::cs_main, return chainman.GetAll())) {
BlockValidationState state;
if (!chainstate->ActivateBestChain(state, nullptr)) {
std::cerr << "Failed to connect best block (" << state.ToString()
<< ")" << std::endl;
goto epilogue;
}
}
// Main program logic starts here
std::cout
<< "Hello! I'm going to print out some information about your datadir."
<< std::endl;
{
LOCK(chainman.GetMutex());
std::cout << "\t"
<< "Path: " << gArgs.GetDataDirNet() << std::endl
<< "\t"
<< "Reindexing: " << std::boolalpha << node::fReindex.load()
<< std::noboolalpha << std::endl
<< "\t"
<< "Snapshot Active: " << std::boolalpha
<< chainman.IsSnapshotActive() << std::noboolalpha
<< std::endl
<< "\t"
<< "Active Height: " << chainman.ActiveHeight() << std::endl
<< "\t"
<< "Active IBD: " << std::boolalpha
<< chainman.IsInitialBlockDownload() << std::noboolalpha
<< std::endl;
CBlockIndex *tip = chainman.ActiveTip();
if (tip) {
std::cout << "\t" << tip->ToString() << std::endl;
}
}
for (std::string line; std::getline(std::cin, line);) {
if (line.empty()) {
std::cerr << "Empty line found" << std::endl;
break;
}
std::shared_ptr<CBlock> blockptr = std::make_shared<CBlock>();
CBlock &block = *blockptr;
if (!DecodeHexBlk(block, line)) {
std::cerr << "Block decode failed" << std::endl;
break;
}
if (block.vtx.empty() || !block.vtx[0]->IsCoinBase()) {
std::cerr << "Block does not start with a coinbase" << std::endl;
break;
}
BlockHash hash = block.GetHash();
{
LOCK(cs_main);
const CBlockIndex *pindex =
chainman.m_blockman.LookupBlockIndex(hash);
if (pindex) {
if (pindex->IsValid(BlockValidity::SCRIPTS)) {
std::cerr << "Duplicate" << std::endl;
break;
}
if (pindex->nStatus.hasFailed()) {
std::cerr << "Duplicate-invalid" << std::endl;
break;
}
}
}
// Adapted from rpc/mining.cpp
class submitblock_StateCatcher final : public CValidationInterface {
public:
BlockHash hash;
bool found;
BlockValidationState state;
explicit submitblock_StateCatcher(const BlockHash &hashIn)
: hash(hashIn), found(false), state() {}
protected:
void BlockChecked(const CBlock &block,
const BlockValidationState &stateIn) override {
if (block.GetHash() != hash) {
return;
}
found = true;
state = stateIn;
}
};
bool new_block;
auto sc = std::make_shared<submitblock_StateCatcher>(block.GetHash());
RegisterSharedValidationInterface(sc);
bool accepted = chainman.ProcessNewBlock(blockptr,
/*force_processing=*/true,
/*min_pow_checked=*/true,
/*new_block=*/&new_block);
UnregisterSharedValidationInterface(sc);
if (!new_block && accepted) {
std::cerr << "Duplicate" << std::endl;
break;
}
if (!sc->found) {
std::cerr << "Inconclusive" << std::endl;
break;
}
std::cout << sc->state.ToString() << std::endl;
switch (sc->state.GetResult()) {
case BlockValidationResult::BLOCK_RESULT_UNSET:
std::cerr << "Initial value. Block has not yet been rejected"
<< std::endl;
break;
case BlockValidationResult::BLOCK_HEADER_LOW_WORK:
std::cerr
<< "the block header may be on a too-little-work chain"
<< std::endl;
break;
case BlockValidationResult::BLOCK_CONSENSUS:
std::cerr << "Invalid by consensus rules (excluding any below "
"reasons)"
<< std::endl;
break;
case BlockValidationResult::BLOCK_CACHED_INVALID:
std::cerr << "This block was cached as being invalid and we "
"didn't store the reason why"
<< std::endl;
break;
case BlockValidationResult::BLOCK_INVALID_HEADER:
std::cerr << "Invalid proof of work or time too old"
<< std::endl;
break;
case BlockValidationResult::BLOCK_MUTATED:
std::cerr << "The block's data didn't match the data committed "
"to by the PoW"
<< std::endl;
break;
case BlockValidationResult::BLOCK_MISSING_PREV:
std::cerr << "We don't have the previous block the checked one "
"is built on"
<< std::endl;
break;
case BlockValidationResult::BLOCK_INVALID_PREV:
std::cerr << "A block this one builds on is invalid"
<< std::endl;
break;
case BlockValidationResult::BLOCK_TIME_FUTURE:
std::cerr << "Block timestamp was > 2 hours in the future (or "
"our clock is bad)"
<< std::endl;
break;
case BlockValidationResult::BLOCK_CHECKPOINT:
std::cerr << "The block failed to meet one of our checkpoints"
<< std::endl;
break;
}
}
epilogue:
// Without this precise shutdown sequence, there will be a lot of nullptr
// dereferencing and UB.
scheduler.stop();
if (chainman.m_load_block.joinable()) {
chainman.m_load_block.join();
}
StopScriptCheckWorkerThreads();
GetMainSignals().FlushBackgroundCallbacks();
{
LOCK(cs_main);
for (Chainstate *chainstate : chainman.GetAll()) {
if (chainstate->CanFlushToDisk()) {
chainstate->ForceFlushStateToDisk();
chainstate->ResetCoinsViews();
}
}
}
GetMainSignals().UnregisterBackgroundSignalScheduler();
-
- init::UnsetGlobals();
}
diff --git a/src/bitcoind.cpp b/src/bitcoind.cpp
index 8e4f0b91f..2c0f18e76 100644
--- a/src/bitcoind.cpp
+++ b/src/bitcoind.cpp
@@ -1,329 +1,332 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#if defined(HAVE_CONFIG_H)
#include <config/bitcoin-config.h>
#endif
#include <chainparams.h>
#include <clientversion.h>
#include <common/args.h>
#include <common/system.h>
#include <compat.h>
#include <config.h>
#include <httprpc.h>
#include <init.h>
#include <interfaces/chain.h>
#include <node/context.h>
#include <node/ui_interface.h>
#include <noui.h>
#include <shutdown.h>
#include <util/check.h>
#include <util/exception.h>
#include <util/strencodings.h>
#include <util/syserror.h>
#include <util/threadnames.h>
#include <util/tokenpipe.h>
#include <util/translation.h>
#include <any>
#include <functional>
using node::NodeContext;
const std::function<std::string(const char *)> G_TRANSLATION_FUN = nullptr;
#if HAVE_DECL_FORK
/**
* Custom implementation of daemon(). This implements the same order of
* operations as glibc.
* Opens a pipe to the child process to be able to wait for an event to occur.
*
* @returns 0 if successful, and in child process.
* >0 if successful, and in parent process.
* -1 in case of error (in parent process).
*
* In case of success, endpoint will be one end of a pipe from the
* child to parent process, which can be used with TokenWrite (in
* the child) or TokenRead (in the parent).
*/
int fork_daemon(bool nochdir, bool noclose, TokenPipeEnd &endpoint) {
// communication pipe with child process
std::optional<TokenPipe> umbilical = TokenPipe::Make();
if (!umbilical) {
// pipe or pipe2 failed.
return -1;
}
int pid = fork();
if (pid < 0) {
// fork failed.
return -1;
}
if (pid != 0) {
// Parent process gets read end, closes write end.
endpoint = umbilical->TakeReadEnd();
umbilical->TakeWriteEnd().Close();
int status = endpoint.TokenRead();
// Something went wrong while setting up child process.
if (status != 0) {
endpoint.Close();
return -1;
}
return pid;
}
// Child process gets write end, closes read end.
endpoint = umbilical->TakeWriteEnd();
umbilical->TakeReadEnd().Close();
#if HAVE_DECL_SETSID
if (setsid() < 0) {
// setsid failed.
exit(1);
}
#endif
if (!nochdir) {
if (chdir("/") != 0) {
// chdir failed.
exit(1);
}
}
if (!noclose) {
// Open /dev/null, and clone it into STDIN, STDOUT and STDERR to detach
// from terminal.
int fd = open("/dev/null", O_RDWR);
if (fd >= 0) {
bool err = dup2(fd, STDIN_FILENO) < 0 ||
dup2(fd, STDOUT_FILENO) < 0 ||
dup2(fd, STDERR_FILENO) < 0;
// Don't close if fd<=2 to try to handle the case where the program
// was invoked without any file descriptors open.
if (fd > 2) {
close(fd);
}
if (err) {
// dup2 failed.
exit(1);
}
} else {
// open /dev/null failed.
exit(1);
}
}
// Success
endpoint.TokenWrite(0);
return 0;
}
#endif
//////////////////////////////////////////////////////////////////////////////
//
// Start
//
static bool AppInit(int argc, char *argv[]) {
// FIXME: Ideally, we'd like to build the config here, but that's currently
// not possible as the whole application has too many global state. However,
// this is a first step.
auto &config = const_cast<Config &>(GetConfig());
RPCServer rpcServer;
NodeContext node;
std::any context{&node};
HTTPRPCRequestProcessor httpRPCRequestProcessor(config, rpcServer, context);
bool fRet = false;
util::ThreadSetInternalName("init");
// If Qt is used, parameters/bitcoin.conf are parsed in qt/bitcoin.cpp's
// main()
SetupServerArgs(node);
ArgsManager &args = *Assert(node.args);
std::string error;
if (!args.ParseParameters(argc, argv, error)) {
return InitError(Untranslated(
strprintf("Error parsing command line arguments: %s\n", error)));
}
// Process help and version before taking care about datadir
if (HelpRequested(args) || args.IsArgSet("-version")) {
std::string strUsage =
PACKAGE_NAME " version " + FormatFullVersion() + "\n";
if (args.IsArgSet("-version")) {
strUsage += FormatParagraph(LicenseInfo()) + "\n";
} else {
strUsage += "\nUsage: bitcoind [options] "
"Start " PACKAGE_NAME "\n";
strUsage += "\n" + args.GetHelpMessage();
}
tfm::format(std::cout, "%s", strUsage);
return true;
}
#if HAVE_DECL_FORK
// Communication with parent after daemonizing. This is used for signalling
// in the following ways:
// - a boolean token is sent when the initialization process (all the Init*
// functions) have finished to indicate that the parent process can quit,
// and whether it was successful/unsuccessful.
// - an unexpected shutdown of the child process creates an unexpected end
// of stream at the parent end, which is interpreted as failure to start.
TokenPipeEnd daemon_ep;
#endif
try {
if (!CheckDataDirOption(args)) {
return InitError(Untranslated(
strprintf("Specified data directory \"%s\" does not exist.\n",
args.GetArg("-datadir", ""))));
}
if (!args.ReadConfigFiles(error, true)) {
return InitError(Untranslated(
strprintf("Error reading configuration file: %s\n", error)));
}
// Check for -chain, -testnet or -regtest parameter (Params() calls are
// only valid after this clause)
try {
SelectParams(args.GetChainName());
} catch (const std::exception &e) {
return InitError(Untranslated(strprintf("%s\n", e.what())));
}
// Make sure we create the net-specific data directory early on: if it
// is new, this has a side effect of also creating
// <datadir>/<net>/wallets/.
//
// TODO: this should be removed once gArgs.GetDataDirNet() no longer
// creates the wallets/ subdirectory. See more info at:
// https://reviews.bitcoinabc.org/D3312
gArgs.GetDataDirNet();
// Error out when loose non-argument tokens are encountered on command
// line
for (int i = 1; i < argc; i++) {
if (!IsSwitchChar(argv[i][0])) {
return InitError(Untranslated(
strprintf("Command line contains unexpected token '%s', "
"see bitcoind -h for a list of options.\n",
argv[i])));
}
}
if (!args.InitSettings(error)) {
InitError(Untranslated(error));
return false;
}
// -server defaults to true for bitcoind but not for the GUI so do this
// here
args.SoftSetBoolArg("-server", true);
// Set this early so that parameter interactions go to console
InitLogging(args);
InitParameterInteraction(args);
if (!AppInitBasicSetup(args)) {
// InitError will have been called with detailed error, which ends
// up on console
return false;
}
if (!AppInitParameterInteraction(config, args)) {
// InitError will have been called with detailed error, which ends
// up on console
return false;
}
- if (!AppInitSanityChecks()) {
+
+ node.kernel = std::make_unique<kernel::Context>();
+ if (!AppInitSanityChecks(*node.kernel)) {
// InitError will have been called with detailed error, which ends
// up on console
return false;
}
+
if (args.GetBoolArg("-daemon", DEFAULT_DAEMON) ||
args.GetBoolArg("-daemonwait", DEFAULT_DAEMONWAIT)) {
#if HAVE_DECL_FORK
tfm::format(std::cout, PACKAGE_NAME " starting\n");
// Daemonize
// don't chdir (1), do close FDs (0)
switch (fork_daemon(1, 0, daemon_ep)) {
case 0:
// Child: continue.
// If -daemonwait is not enabled, immediately send a success
// token the parent.
if (!args.GetBoolArg("-daemonwait", DEFAULT_DAEMONWAIT)) {
daemon_ep.TokenWrite(1);
daemon_ep.Close();
}
break;
case -1:
// Error happened.
return InitError(Untranslated(strprintf(
"fork_daemon() failed: %s\n", SysErrorString(errno))));
default: {
// Parent: wait and exit.
int token = daemon_ep.TokenRead();
if (token) {
// Success
exit(EXIT_SUCCESS);
} else {
// fRet = false or token read error (premature exit).
tfm::format(std::cerr, "Error during initialization - "
"check debug.log for details\n");
exit(EXIT_FAILURE);
}
}
}
#else
return InitError(Untranslated(
"-daemon is not supported on this operating system\n"));
#endif // HAVE_DECL_FORK
}
// Lock data directory after daemonization
if (!AppInitLockDataDirectory()) {
// If locking the data directory failed, exit immediately
return false;
}
fRet = AppInitInterfaces(node) &&
AppInitMain(config, rpcServer, httpRPCRequestProcessor, node);
} catch (const std::exception &e) {
PrintExceptionContinue(&e, "AppInit()");
} catch (...) {
PrintExceptionContinue(nullptr, "AppInit()");
}
#if HAVE_DECL_FORK
if (daemon_ep.IsOpen()) {
// Signal initialization status to parent, then close pipe.
daemon_ep.TokenWrite(fRet);
daemon_ep.Close();
}
#endif
if (fRet) {
WaitForShutdown();
}
Interrupt(node);
Shutdown(node);
return fRet;
}
int main(int argc, char *argv[]) {
#ifdef WIN32
common::WinCmdLineArgs winArgs;
std::tie(argc, argv) = winArgs.get();
#endif
SetupEnvironment();
// Connect bitcoind signal handlers
noui_connect();
return (AppInit(argc, argv) ? EXIT_SUCCESS : EXIT_FAILURE);
}
diff --git a/src/init.cpp b/src/init.cpp
index febb5e814..1e3c05913 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -1,3032 +1,3045 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2018 The Bitcoin Core developers
// 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 <config/bitcoin-config.h>
#endif
#include <init.h>
+#include <kernel/checks.h>
#include <kernel/mempool_persist.h>
#include <kernel/validation_cache_sizes.h>
#include <addrman.h>
#include <avalanche/avalanche.h>
#include <avalanche/processor.h>
#include <avalanche/proof.h> // For AVALANCHE_LEGACY_PROOF_DEFAULT
#include <avalanche/validation.h>
#include <avalanche/voterecord.h> // For AVALANCHE_VOTE_STALE_*
#include <banman.h>
#include <blockfilter.h>
#include <chain.h>
#include <chainparams.h>
#include <common/args.h>
#include <config.h>
#include <consensus/amount.h>
#include <currencyunit.h>
#include <flatfile.h>
#include <hash.h>
#include <httprpc.h>
#include <httpserver.h>
#include <index/blockfilterindex.h>
#include <index/coinstatsindex.h>
#include <index/txindex.h>
#include <init/common.h>
#include <interfaces/chain.h>
#include <interfaces/node.h>
#include <mapport.h>
#include <mempool_args.h>
#include <net.h>
#include <net_permissions.h>
#include <net_processing.h>
#include <netbase.h>
#include <node/blockmanager_args.h>
#include <node/blockstorage.h>
#include <node/caches.h>
#include <node/chainstate.h>
#include <node/chainstatemanager_args.h>
#include <node/context.h>
#include <node/kernel_notifications.h>
#include <node/mempool_persist_args.h>
#include <node/miner.h>
#include <node/peerman_args.h>
#include <node/ui_interface.h>
#include <node/validation_cache_args.h>
#include <policy/block/rtt.h>
#include <policy/policy.h>
#include <policy/settings.h>
#include <rpc/blockchain.h>
#include <rpc/register.h>
#include <rpc/server.h>
#include <rpc/util.h>
#include <scheduler.h>
#include <script/scriptcache.h>
#include <script/sigcache.h>
#include <script/standard.h>
#include <shutdown.h>
#include <sync.h>
#include <timedata.h>
#include <torcontrol.h>
#include <txdb.h>
#include <txmempool.h>
#include <util/asmap.h>
#include <util/check.h>
#include <util/fs.h>
#include <util/fs_helpers.h>
#include <util/moneystr.h>
#include <util/string.h>
#include <util/syserror.h>
#include <util/thread.h>
#include <util/threadnames.h>
#include <util/translation.h>
#include <validation.h>
#include <validationinterface.h>
#include <walletinitinterface.h>
#include <boost/signals2/signal.hpp>
#if ENABLE_CHRONIK
#include <chronik-cpp/chronik.h>
#endif
#if ENABLE_ZMQ
#include <zmq/zmqabstractnotifier.h>
#include <zmq/zmqnotificationinterface.h>
#include <zmq/zmqrpc.h>
#endif
#ifndef WIN32
#include <cerrno>
#include <csignal>
#include <sys/stat.h>
#endif
#include <algorithm>
#include <condition_variable>
#include <cstdint>
#include <cstdio>
#include <fstream>
#include <functional>
#include <set>
#include <string>
#include <thread>
#include <vector>
using kernel::DEFAULT_STOPAFTERBLOCKIMPORT;
using kernel::DumpMempool;
using kernel::ValidationCacheSizes;
using node::ApplyArgsManOptions;
using node::BlockManager;
using node::CacheSizes;
using node::CalculateCacheSizes;
using node::DEFAULT_PERSIST_MEMPOOL;
using node::fReindex;
using node::g_indexes_ready_to_sync;
using node::KernelNotifications;
using node::LoadChainstate;
using node::MempoolPath;
using node::NodeContext;
using node::ShouldPersistMempool;
using node::ThreadImport;
using node::VerifyLoadedChainstate;
static const bool DEFAULT_PROXYRANDOMIZE = true;
static const bool DEFAULT_REST_ENABLE = false;
static constexpr bool DEFAULT_CHRONIK = false;
#ifdef WIN32
// Win32 LevelDB doesn't use filedescriptors, and the ones used for accessing
// block files don't count towards the fd_set size limit anyway.
#define MIN_CORE_FILEDESCRIPTORS 0
#else
#define MIN_CORE_FILEDESCRIPTORS 150
#endif
static const char *DEFAULT_ASMAP_FILENAME = "ip_asn.map";
static const std::string HEADERS_TIME_FILE_NAME{"headerstime.dat"};
/**
* The PID file facilities.
*/
static const char *BITCOIN_PID_FILENAME = "bitcoind.pid";
static fs::path GetPidFile(const ArgsManager &args) {
return AbsPathForConfigVal(args,
args.GetPathArg("-pid", BITCOIN_PID_FILENAME));
}
[[nodiscard]] static bool CreatePidFile(const ArgsManager &args) {
std::ofstream file{GetPidFile(args)};
if (file) {
#ifdef WIN32
tfm::format(file, "%d\n", GetCurrentProcessId());
#else
tfm::format(file, "%d\n", getpid());
#endif
return true;
} else {
return InitError(strprintf(_("Unable to create the PID file '%s': %s"),
fs::PathToString(GetPidFile(args)),
SysErrorString(errno)));
}
}
//////////////////////////////////////////////////////////////////////////////
//
// Shutdown
//
//
// Thread management and startup/shutdown:
//
// The network-processing threads are all part of a thread group created by
// AppInit() or the Qt main() function.
//
// A clean exit happens when StartShutdown() or the SIGTERM signal handler sets
// fRequestShutdown, which makes main thread's WaitForShutdown() interrupts the
// thread group.
// And then, WaitForShutdown() makes all other on-going threads in the thread
// group join the main thread.
// Shutdown() is then called to clean up database connections, and stop other
// threads that should only be stopped after the main network-processing threads
// have exited.
//
// Shutdown for Qt is very similar, only it uses a QTimer to detect
// ShutdownRequested() getting set, and then does the normal Qt shutdown thing.
//
void Interrupt(NodeContext &node) {
InterruptHTTPServer();
InterruptHTTPRPC();
InterruptRPC();
InterruptREST();
InterruptTorControl();
InterruptMapPort();
if (node.avalanche) {
// Avalanche needs to be stopped before we interrupt the thread group as
// the scheduler will stop working then.
node.avalanche->stopEventLoop();
}
if (node.connman) {
node.connman->Interrupt();
}
if (g_txindex) {
g_txindex->Interrupt();
}
ForEachBlockFilterIndex([](BlockFilterIndex &index) { index.Interrupt(); });
if (g_coin_stats_index) {
g_coin_stats_index->Interrupt();
}
}
void Shutdown(NodeContext &node) {
static Mutex g_shutdown_mutex;
TRY_LOCK(g_shutdown_mutex, lock_shutdown);
if (!lock_shutdown) {
return;
}
LogPrintf("%s: In progress...\n", __func__);
Assert(node.args);
/// Note: Shutdown() must be able to handle cases in which initialization
/// failed part of the way, for example if the data directory was found to
/// be locked. Be sure that anything that writes files or flushes caches
/// only does this if the respective module was initialized.
util::ThreadRename("shutoff");
if (node.mempool) {
node.mempool->AddTransactionsUpdated(1);
}
StopHTTPRPC();
StopREST();
StopRPC();
StopHTTPServer();
for (const auto &client : node.chain_clients) {
client->flush();
}
StopMapPort();
// Because avalanche and the network depend on each other, it is important
// to shut them down in this order:
// 1. Stop avalanche event loop.
// 2. Shutdown network processing.
// 3. Destroy avalanche::Processor.
// 4. Destroy CConnman
if (node.avalanche) {
node.avalanche->stopEventLoop();
}
// Because these depend on each-other, we make sure that neither can be
// using the other before destroying them.
if (node.peerman) {
UnregisterValidationInterface(node.peerman.get());
}
if (node.connman) {
node.connman->Stop();
}
StopTorControl();
// After everything has been shut down, but before things get flushed, stop
// the CScheduler/checkqueue, scheduler and load block thread.
if (node.scheduler) {
node.scheduler->stop();
}
if (node.chainman && node.chainman->m_load_block.joinable()) {
node.chainman->m_load_block.join();
}
StopScriptCheckWorkerThreads();
// After the threads that potentially access these pointers have been
// stopped, destruct and reset all to nullptr.
node.peerman.reset();
// Destroy various global instances
node.avalanche.reset();
node.connman.reset();
node.banman.reset();
node.addrman.reset();
if (node.mempool && node.mempool->GetLoadTried() &&
ShouldPersistMempool(*node.args)) {
DumpMempool(*node.mempool, MempoolPath(*node.args));
}
// FlushStateToDisk generates a ChainStateFlushed callback, which we should
// avoid missing
if (node.chainman) {
LOCK(cs_main);
for (Chainstate *chainstate : node.chainman->GetAll()) {
if (chainstate->CanFlushToDisk()) {
chainstate->ForceFlushStateToDisk();
}
}
}
// After there are no more peers/RPC left to give us new data which may
// generate CValidationInterface callbacks, flush them...
GetMainSignals().FlushBackgroundCallbacks();
#if ENABLE_CHRONIK
if (node.args->GetBoolArg("-chronik", DEFAULT_CHRONIK)) {
chronik::Stop();
}
#endif
// Stop and delete all indexes only after flushing background callbacks.
if (g_txindex) {
g_txindex->Stop();
g_txindex.reset();
}
if (g_coin_stats_index) {
g_coin_stats_index->Stop();
g_coin_stats_index.reset();
}
ForEachBlockFilterIndex([](BlockFilterIndex &index) { index.Stop(); });
DestroyAllBlockFilterIndexes();
// Any future callbacks will be dropped. This should absolutely be safe - if
// missing a callback results in an unrecoverable situation, unclean
// shutdown would too. The only reason to do the above flushes is to let the
// wallet catch up with our current chain to avoid any strange pruning edge
// cases and make next startup faster by avoiding rescan.
if (node.chainman) {
LOCK(cs_main);
for (Chainstate *chainstate : node.chainman->GetAll()) {
if (chainstate->CanFlushToDisk()) {
chainstate->ForceFlushStateToDisk();
chainstate->ResetCoinsViews();
}
}
node.chainman->DumpRecentHeadersTime(node.chainman->m_options.datadir /
HEADERS_TIME_FILE_NAME);
}
for (const auto &client : node.chain_clients) {
client->stop();
}
#if ENABLE_ZMQ
if (g_zmq_notification_interface) {
UnregisterValidationInterface(g_zmq_notification_interface.get());
g_zmq_notification_interface.reset();
}
#endif
node.chain_clients.clear();
UnregisterAllValidationInterfaces();
GetMainSignals().UnregisterBackgroundSignalScheduler();
- init::UnsetGlobals();
+ node.kernel.reset();
node.mempool.reset();
node.chainman.reset();
node.scheduler.reset();
try {
if (!fs::remove(GetPidFile(*node.args))) {
LogPrintf("%s: Unable to remove PID file: File does not exist\n",
__func__);
}
} catch (const fs::filesystem_error &e) {
LogPrintf("%s: Unable to remove PID file: %s\n", __func__,
fsbridge::get_filesystem_error_message(e));
}
LogPrintf("%s: done\n", __func__);
}
/**
* Signal handlers are very limited in what they are allowed to do.
* The execution context the handler is invoked in is not guaranteed,
* so we restrict handler operations to just touching variables:
*/
#ifndef WIN32
static void HandleSIGTERM(int) {
StartShutdown();
}
static void HandleSIGHUP(int) {
LogInstance().m_reopen_file = true;
}
#else
static BOOL WINAPI consoleCtrlHandler(DWORD dwCtrlType) {
StartShutdown();
Sleep(INFINITE);
return true;
}
#endif
#ifndef WIN32
static void registerSignalHandler(int signal, void (*handler)(int)) {
struct sigaction sa;
sa.sa_handler = handler;
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
sigaction(signal, &sa, NULL);
}
#endif
static boost::signals2::connection rpc_notify_block_change_connection;
static void OnRPCStarted() {
rpc_notify_block_change_connection = uiInterface.NotifyBlockTip_connect(
std::bind(RPCNotifyBlockChange, std::placeholders::_2));
}
static void OnRPCStopped() {
rpc_notify_block_change_connection.disconnect();
RPCNotifyBlockChange(nullptr);
g_best_block_cv.notify_all();
LogPrint(BCLog::RPC, "RPC stopped.\n");
}
void SetupServerArgs(NodeContext &node) {
assert(!node.args);
node.args = &gArgs;
ArgsManager &argsman = *node.args;
SetupHelpOptions(argsman);
SetupCurrencyUnitOptions(argsman);
// server-only for now
argsman.AddArg("-help-debug",
"Print help message with debugging options and exit", false,
OptionsCategory::DEBUG_TEST);
init::AddLoggingArgs(argsman);
const auto defaultBaseParams =
CreateBaseChainParams(CBaseChainParams::MAIN);
const auto testnetBaseParams =
CreateBaseChainParams(CBaseChainParams::TESTNET);
const auto regtestBaseParams =
CreateBaseChainParams(CBaseChainParams::REGTEST);
const auto defaultChainParams =
CreateChainParams(argsman, CBaseChainParams::MAIN);
const auto testnetChainParams =
CreateChainParams(argsman, CBaseChainParams::TESTNET);
const auto regtestChainParams =
CreateChainParams(argsman, CBaseChainParams::REGTEST);
// Hidden Options
std::vector<std::string> hidden_args = {
"-dbcrashratio",
"-forcecompactdb",
"-maxaddrtosend",
"-parkdeepreorg",
"-automaticunparking",
"-replayprotectionactivationtime",
"-enableminerfund",
"-chronikallowpause",
"-chronikcors",
// GUI args. These will be overwritten by SetupUIArgs for the GUI
"-allowselfsignedrootcertificates",
"-choosedatadir",
"-lang=<lang>",
"-min",
"-resetguisettings",
"-rootcertificates=<file>",
"-splash",
"-uiplatform",
// TODO remove after the May. 2025 upgrade
"-schumpeteractivationtime",
};
// Set all of the args and their help
// When adding new options to the categories, please keep and ensure
// alphabetical ordering. Do not translate _(...) -help-debug options, Many
// technical terms, and only a very small audience, so is unnecessary stress
// to translators.
argsman.AddArg("-version", "Print version and exit", ArgsManager::ALLOW_ANY,
OptionsCategory::OPTIONS);
#if defined(HAVE_SYSTEM)
argsman.AddArg(
"-alertnotify=<cmd>",
"Execute command when a relevant alert is received or we see "
"a really long fork (%s in cmd is replaced by message)",
ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
#endif
argsman.AddArg(
"-assumevalid=<hex>",
strprintf(
"If this block is in the chain assume that it and its ancestors "
"are valid and potentially skip their script verification (0 to "
"verify all, default: %s, testnet: %s)",
defaultChainParams->GetConsensus().defaultAssumeValid.GetHex(),
testnetChainParams->GetConsensus().defaultAssumeValid.GetHex()),
ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-blocksdir=<dir>",
"Specify directory to hold blocks subdirectory for *.dat "
"files (default: <datadir>)",
ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-fastprune",
"Use smaller block files and lower minimum prune height for "
"testing purposes",
ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY,
OptionsCategory::DEBUG_TEST);
#if defined(HAVE_SYSTEM)
argsman.AddArg("-blocknotify=<cmd>",
"Execute command when the best block changes (%s in cmd is "
"replaced by block hash)",
ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
#endif
argsman.AddArg("-blockreconstructionextratxn=<n>",
strprintf("Extra transactions to keep in memory for compact "
"block reconstructions (default: %u)",
DEFAULT_BLOCK_RECONSTRUCTION_EXTRA_TXN),
ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg(
"-blocksonly",
strprintf("Whether to reject transactions from network peers. "
"Disables automatic broadcast and rebroadcast of "
"transactions, unless the source peer has the "
"'forcerelay' permission. RPC transactions are"
" not affected. (default: %u)",
DEFAULT_BLOCKSONLY),
ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-coinstatsindex",
strprintf("Maintain coinstats index used by the "
"gettxoutsetinfo RPC (default: %u)",
DEFAULT_COINSTATSINDEX),
ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg(
"-conf=<file>",
strprintf("Specify path to read-only configuration file. Relative "
"paths will be prefixed by datadir location. (default: %s)",
BITCOIN_CONF_FILENAME),
ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-datadir=<dir>", "Specify data directory",
ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg(
"-dbbatchsize",
strprintf("Maximum database write batch size in bytes (default: %u)",
DEFAULT_DB_BATCH_SIZE),
ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY,
OptionsCategory::OPTIONS);
argsman.AddArg(
"-dbcache=<n>",
strprintf("Set database cache size in MiB (%d to %d, default: %d)",
MIN_DB_CACHE_MB, MAX_DB_CACHE_MB, DEFAULT_DB_CACHE_MB),
ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg(
"-includeconf=<file>",
"Specify additional configuration file, relative to the -datadir path "
"(only useable from configuration file, not command line)",
ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-loadblock=<file>",
"Imports blocks from external file on startup",
ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-maxmempool=<n>",
strprintf("Keep the transaction memory pool below <n> "
"megabytes (default: %u)",
DEFAULT_MAX_MEMPOOL_SIZE_MB),
ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-maxorphantx=<n>",
strprintf("Keep at most <n> unconnectable transactions in "
"memory (default: %u)",
DEFAULT_MAX_ORPHAN_TRANSACTIONS),
ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-mempoolexpiry=<n>",
strprintf("Do not keep transactions in the mempool longer "
"than <n> hours (default: %u)",
DEFAULT_MEMPOOL_EXPIRY_HOURS),
ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg(
"-minimumchainwork=<hex>",
strprintf(
"Minimum work assumed to exist on a valid chain in hex "
"(default: %s, testnet: %s)",
defaultChainParams->GetConsensus().nMinimumChainWork.GetHex(),
testnetChainParams->GetConsensus().nMinimumChainWork.GetHex()),
ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY,
OptionsCategory::OPTIONS);
argsman.AddArg(
"-par=<n>",
strprintf("Set the number of script verification threads (%u to %d, 0 "
"= auto, <0 = leave that many cores free, default: %d)",
-GetNumCores(), MAX_SCRIPTCHECK_THREADS,
DEFAULT_SCRIPTCHECK_THREADS),
ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-persistmempool",
strprintf("Whether to save the mempool on shutdown and load "
"on restart (default: %u)",
DEFAULT_PERSIST_MEMPOOL),
ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg(
"-persistrecentheaderstime",
strprintf(
"Whether the node stores the recent headers reception time to a "
"file and load it upon startup. This is intended for mining nodes "
"to overestimate the real time target upon restart (default: %u)",
DEFAULT_STORE_RECENT_HEADERS_TIME),
ArgsManager::ALLOW_BOOL, OptionsCategory::OPTIONS);
argsman.AddArg(
"-pid=<file>",
strprintf("Specify pid file. Relative paths will be prefixed "
"by a net-specific datadir location. (default: %s)",
BITCOIN_PID_FILENAME),
ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg(
"-prune=<n>",
strprintf("Reduce storage requirements by enabling pruning (deleting) "
"of old blocks. This allows the pruneblockchain RPC to be "
"called to delete specific blocks and enables automatic "
"pruning of old blocks if a target size in MiB is provided. "
"This mode is incompatible with -txindex and -rescan. "
"Warning: Reverting this setting requires re-downloading the "
"entire blockchain. (default: 0 = disable pruning blocks, "
"1 = allow manual pruning via RPC, >=%u = automatically "
"prune block files to stay under the specified target size "
"in MiB)",
MIN_DISK_SPACE_FOR_BLOCK_FILES / 1024 / 1024),
ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg(
"-reindex-chainstate",
"Rebuild chain state from the currently indexed blocks. When "
"in pruning mode or if blocks on disk might be corrupted, use "
"full -reindex instead.",
ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg(
"-reindex",
"Rebuild chain state and block index from the blk*.dat files on disk."
" This will also rebuild active optional indexes.",
ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg(
"-settings=<file>",
strprintf(
"Specify path to dynamic settings data file. Can be disabled with "
"-nosettings. File is written at runtime and not meant to be "
"edited by users (use %s instead for custom settings). Relative "
"paths will be prefixed by datadir location. (default: %s)",
BITCOIN_CONF_FILENAME, BITCOIN_SETTINGS_FILENAME),
ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
#if HAVE_SYSTEM
argsman.AddArg("-startupnotify=<cmd>", "Execute command on startup.",
ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
#endif
#ifndef WIN32
argsman.AddArg(
"-sysperms",
"Create new files with system default permissions, instead of umask "
"077 (only effective with disabled wallet functionality)",
ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
#else
hidden_args.emplace_back("-sysperms");
#endif
argsman.AddArg("-txindex",
strprintf("Maintain a full transaction index, used by the "
"getrawtransaction rpc call (default: %d)",
DEFAULT_TXINDEX),
ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
#if ENABLE_CHRONIK
argsman.AddArg(
"-chronik",
strprintf("Enable the Chronik indexer, which can be read via a "
"dedicated HTTP/Protobuf interface (default: %d)",
DEFAULT_CHRONIK),
ArgsManager::ALLOW_BOOL, OptionsCategory::CHRONIK);
argsman.AddArg(
"-chronikbind=<addr>[:port]",
strprintf(
"Bind the Chronik indexer to the given address to listen for "
"HTTP/Protobuf connections to access the index. Unlike the "
"JSON-RPC, it's ok to have this publicly exposed on the internet. "
"This option can be specified multiple times (default: %s; default "
"port: %u, testnet: %u, regtest: %u)",
Join(chronik::DEFAULT_BINDS, ", "),
defaultBaseParams->ChronikPort(), testnetBaseParams->ChronikPort(),
regtestBaseParams->ChronikPort()),
ArgsManager::ALLOW_STRING | ArgsManager::NETWORK_ONLY,
OptionsCategory::CHRONIK);
argsman.AddArg("-chroniktokenindex",
"Enable token indexing in Chronik (default: 1)",
ArgsManager::ALLOW_BOOL, OptionsCategory::CHRONIK);
argsman.AddArg("-chroniklokadidindex",
"Enable LOKAD ID indexing in Chronik (default: 1)",
ArgsManager::ALLOW_BOOL, OptionsCategory::CHRONIK);
argsman.AddArg("-chronikreindex",
"Reindex the Chronik indexer from genesis, but leave the "
"other indexes untouched",
ArgsManager::ALLOW_BOOL, OptionsCategory::CHRONIK);
argsman.AddArg(
"-chroniktxnumcachebuckets",
strprintf(
"Tuning param of the TxNumCache, specifies how many buckets "
"to use on the belt. Caution against setting this too high, "
"it may slow down indexing. Set to 0 to disable. (default: %d)",
chronik::DEFAULT_TX_NUM_CACHE_BUCKETS),
ArgsManager::ALLOW_INT, OptionsCategory::CHRONIK);
argsman.AddArg(
"-chroniktxnumcachebucketsize",
strprintf(
"Tuning param of the TxNumCache, specifies the size of each bucket "
"on the belt. Unlike the number of buckets, this may be increased "
"without much danger of slowing the indexer down. The total cache "
"size will be `num_buckets * bucket_size * 40B`, so by default the "
"cache will require %dkB of memory. (default: %d)",
chronik::DEFAULT_TX_NUM_CACHE_BUCKETS *
chronik::DEFAULT_TX_NUM_CACHE_BUCKET_SIZE * 40 / 1000,
chronik::DEFAULT_TX_NUM_CACHE_BUCKET_SIZE),
ArgsManager::ALLOW_INT, OptionsCategory::CHRONIK);
argsman.AddArg("-chronikperfstats",
"Output some performance statistics (e.g. num cache hits, "
"seconds spent) into a <datadir>/perf folder. (default: 0)",
ArgsManager::ALLOW_BOOL, OptionsCategory::CHRONIK);
argsman.AddArg(
"-chronikscripthashindex",
"Enable the scripthash index for the Chronik indexer (default: 0) ",
ArgsManager::ALLOW_BOOL, OptionsCategory::CHRONIK);
argsman.AddArg(
"-chronikelectrumbind=<addr>[:port][:t|s]",
strprintf(
"Bind the Chronik Electrum interface to the given "
"address:port:protocol. If not set, the Electrum interface will "
"not start. This option can be specified multiple times. The "
"protocol is selected by a single letter, where 't' means TCP and "
"'s' means TLS. If TLS is selected, the certificate chain and "
"private key must both be passed (see -chronikelectrumcert and "
"-chronikelectrumprivkey (default: disabled; default port: %u, "
"testnet: %u, regtest: %u; default protocol: TCP)",
defaultBaseParams->ChronikElectrumPort(),
testnetBaseParams->ChronikElectrumPort(),
regtestBaseParams->ChronikElectrumPort()),
ArgsManager::ALLOW_STRING | ArgsManager::NETWORK_ONLY,
OptionsCategory::HIDDEN);
argsman.AddArg(
"-chronikelectrumcert",
"Path to the certificate file to be used by the Chronik Electrum "
"server when the TLS protocol is selected. The file should contain "
"the whole certificate chain (typically a .pem file). If used the "
"-chronikelectrumprivkey must be set as well.",
ArgsManager::ALLOW_STRING | ArgsManager::NETWORK_ONLY,
OptionsCategory::HIDDEN);
argsman.AddArg(
"-chronikelectrumprivkey",
"Path to the private key file to be used by the Chronik Electrum "
"server when the TLS protocol is selected. If used the "
"-chronikelectrumcert must be set as well.",
ArgsManager::ALLOW_STRING | ArgsManager::NETWORK_ONLY,
OptionsCategory::HIDDEN);
argsman.AddArg(
"-chronikelectrummaxhistory",
strprintf("Largest tx history we are willing to serve. (default: %u)",
chronik::DEFAULT_ELECTRUM_MAX_HISTORY),
ArgsManager::ALLOW_INT, OptionsCategory::HIDDEN);
argsman.AddArg(
"-chronikelectrumdonationaddress",
strprintf(
"The server donation address. No checks are done on the server "
"side to ensure this is a valid eCash address, it is just relayed "
"to clients verbatim as a text string (%u characters maximum).",
chronik::MAX_LENGTH_DONATION_ADDRESS),
ArgsManager::ALLOW_STRING, OptionsCategory::HIDDEN);
#endif
argsman.AddArg(
"-blockfilterindex=<type>",
strprintf("Maintain an index of compact filters by block "
"(default: %s, values: %s).",
DEFAULT_BLOCKFILTERINDEX, ListBlockFilterTypes()) +
" If <type> is not supplied or if <type> = 1, indexes for "
"all known types are enabled.",
ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg(
"-usecashaddr",
"Use Cash Address for destination encoding instead of base58 "
"(activate by default on Jan, 14)",
ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg(
"-addnode=<ip>",
"Add a node to connect to and attempt to keep the connection "
"open (see the `addnode` RPC command help for more info)",
ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY,
OptionsCategory::CONNECTION);
argsman.AddArg("-asmap=<file>",
strprintf("Specify asn mapping used for bucketing of the "
"peers (default: %s). Relative paths will be "
"prefixed by the net-specific datadir location.",
DEFAULT_ASMAP_FILENAME),
ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-bantime=<n>",
strprintf("Default duration (in seconds) of manually "
"configured bans (default: %u)",
DEFAULT_MISBEHAVING_BANTIME),
ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg(
"-bind=<addr>[:<port>][=onion]",
strprintf("Bind to given address and always listen on it (default: "
"0.0.0.0). Use [host]:port notation for IPv6. Append =onion "
"to tag any incoming connections to that address and port as "
"incoming Tor connections (default: 127.0.0.1:%u=onion, "
"testnet: 127.0.0.1:%u=onion, regtest: 127.0.0.1:%u=onion)",
defaultBaseParams->OnionServiceTargetPort(),
testnetBaseParams->OnionServiceTargetPort(),
regtestBaseParams->OnionServiceTargetPort()),
ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY,
OptionsCategory::CONNECTION);
argsman.AddArg(
"-connect=<ip>",
"Connect only to the specified node(s); -connect=0 disables automatic "
"connections (the rules for this peer are the same as for -addnode)",
ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY,
OptionsCategory::CONNECTION);
argsman.AddArg(
"-discover",
"Discover own IP addresses (default: 1 when listening and no "
"-externalip or -proxy)",
ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-dns",
strprintf("Allow DNS lookups for -addnode, -seednode and "
"-connect (default: %d)",
DEFAULT_NAME_LOOKUP),
ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg(
"-dnsseed",
strprintf(
"Query for peer addresses via DNS lookup, if low on addresses "
"(default: %u unless -connect used)",
DEFAULT_DNSSEED),
ArgsManager::ALLOW_BOOL, OptionsCategory::CONNECTION);
argsman.AddArg("-externalip=<ip>", "Specify your own public address",
ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg(
"-fixedseeds",
strprintf(
"Allow fixed seeds if DNS seeds don't provide peers (default: %u)",
DEFAULT_FIXEDSEEDS),
ArgsManager::ALLOW_BOOL, OptionsCategory::CONNECTION);
argsman.AddArg(
"-forcednsseed",
strprintf(
"Always query for peer addresses via DNS lookup (default: %d)",
DEFAULT_FORCEDNSSEED),
ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-overridednsseed",
"If set, only use the specified DNS seed when "
"querying for peer addresses via DNS lookup.",
ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg(
"-listen",
"Accept connections from outside (default: 1 if no -proxy or -connect)",
ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg(
"-listenonion",
strprintf("Automatically create Tor onion service (default: %d)",
DEFAULT_LISTEN_ONION),
ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg(
"-maxconnections=<n>",
strprintf("Maintain at most <n> connections to peers. The effective "
"limit depends on system limitations and might be lower than "
"the specified value (default: %u)",
DEFAULT_MAX_PEER_CONNECTIONS),
ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-maxreceivebuffer=<n>",
strprintf("Maximum per-connection receive buffer, <n>*1000 "
"bytes (default: %u)",
DEFAULT_MAXRECEIVEBUFFER),
ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg(
"-maxsendbuffer=<n>",
strprintf(
"Maximum per-connection send buffer, <n>*1000 bytes (default: %u)",
DEFAULT_MAXSENDBUFFER),
ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg(
"-maxtimeadjustment",
strprintf("Maximum allowed median peer time offset adjustment. Local "
"perspective of time may be influenced by peers forward or "
"backward by this amount. (default: %u seconds)",
DEFAULT_MAX_TIME_ADJUSTMENT),
ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-onion=<ip:port>",
strprintf("Use separate SOCKS5 proxy to reach peers via Tor "
"onion services (default: %s)",
"-proxy"),
ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-i2psam=<ip:port>",
"I2P SAM proxy to reach I2P peers and accept I2P "
"connections (default: none)",
ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg(
"-i2pacceptincoming",
"If set and -i2psam is also set then incoming I2P connections are "
"accepted via the SAM proxy. If this is not set but -i2psam is set "
"then only outgoing connections will be made to the I2P network. "
"Ignored if -i2psam is not set. Listening for incoming I2P connections "
"is done through the SAM proxy, not by binding to a local address and "
"port (default: 1)",
ArgsManager::ALLOW_BOOL, OptionsCategory::CONNECTION);
argsman.AddArg(
"-onlynet=<net>",
"Make outgoing connections only through network <net> (" +
Join(GetNetworkNames(), ", ") +
"). Incoming connections are not affected by this option. This "
"option can be specified multiple times to allow multiple "
"networks. Warning: if it is used with non-onion networks "
"and the -onion or -proxy option is set, then outbound onion "
"connections will still be made; use -noonion or -onion=0 to "
"disable outbound onion connections in this case",
ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-peerbloomfilters",
strprintf("Support filtering of blocks and transaction with "
"bloom filters (default: %d)",
DEFAULT_PEERBLOOMFILTERS),
ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg(
"-peerblockfilters",
strprintf(
"Serve compact block filters to peers per BIP 157 (default: %u)",
DEFAULT_PEERBLOCKFILTERS),
ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-permitbaremultisig",
strprintf("Relay non-P2SH multisig (default: %d)",
DEFAULT_PERMIT_BAREMULTISIG),
ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
// TODO: remove the sentence "Nodes not using ... incoming connections."
// once the changes from https://github.com/bitcoin/bitcoin/pull/23542 have
// become widespread.
argsman.AddArg("-port=<port>",
strprintf("Listen for connections on <port>. Nodes not "
"using the default ports (default: %u, "
"testnet: %u, regtest: %u) are unlikely to get "
"incoming connections. Not relevant for I2P (see "
"doc/i2p.md).",
defaultChainParams->GetDefaultPort(),
testnetChainParams->GetDefaultPort(),
regtestChainParams->GetDefaultPort()),
ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY,
OptionsCategory::CONNECTION);
argsman.AddArg("-proxy=<ip:port>", "Connect through SOCKS5 proxy",
ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg(
"-proxyrandomize",
strprintf("Randomize credentials for every proxy connection. "
"This enables Tor stream isolation (default: %d)",
DEFAULT_PROXYRANDOMIZE),
ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg(
"-seednode=<ip>",
"Connect to a node to retrieve peer addresses, and disconnect",
ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg(
"-networkactive",
"Enable all P2P network activity (default: 1). Can be changed "
"by the setnetworkactive RPC command",
ArgsManager::ALLOW_BOOL, OptionsCategory::CONNECTION);
argsman.AddArg("-timeout=<n>",
strprintf("Specify connection timeout in milliseconds "
"(minimum: 1, default: %d)",
DEFAULT_CONNECT_TIMEOUT),
ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg(
"-peertimeout=<n>",
strprintf("Specify p2p connection timeout in seconds. This option "
"determines the amount of time a peer may be inactive before "
"the connection to it is dropped. (minimum: 1, default: %d)",
DEFAULT_PEER_CONNECT_TIMEOUT),
true, OptionsCategory::CONNECTION);
argsman.AddArg(
"-torcontrol=<ip>:<port>",
strprintf(
"Tor control port to use if onion listening enabled (default: %s)",
DEFAULT_TOR_CONTROL),
ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-torpassword=<pass>",
"Tor control port password (default: empty)",
ArgsManager::ALLOW_ANY | ArgsManager::SENSITIVE,
OptionsCategory::CONNECTION);
#ifdef USE_UPNP
#if USE_UPNP
argsman.AddArg("-upnp",
"Use UPnP to map the listening port (default: 1 when "
"listening and no -proxy)",
ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
#else
argsman.AddArg(
"-upnp",
strprintf("Use UPnP to map the listening port (default: %u)", 0),
ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
#endif
#else
hidden_args.emplace_back("-upnp");
#endif
#ifdef USE_NATPMP
argsman.AddArg(
"-natpmp",
strprintf("Use NAT-PMP to map the listening port (default: %s)",
DEFAULT_NATPMP ? "1 when listening and no -proxy" : "0"),
ArgsManager::ALLOW_BOOL, OptionsCategory::CONNECTION);
#else
hidden_args.emplace_back("-natpmp");
#endif // USE_NATPMP
argsman.AddArg(
"-whitebind=<[permissions@]addr>",
"Bind to the given address and add permission flags to the peers "
"connecting to it."
"Use [host]:port notation for IPv6. Allowed permissions: " +
Join(NET_PERMISSIONS_DOC, ", ") +
". "
"Specify multiple permissions separated by commas (default: "
"download,noban,mempool,relay). Can be specified multiple times.",
ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-whitelist=<[permissions@]IP address or network>",
"Add permission flags to the peers using the given "
"IP address (e.g. 1.2.3.4) or CIDR-notated network "
"(e.g. 1.2.3.0/24). "
"Uses the same permissions as -whitebind. "
"Additional flags \"in\" and \"out\" control whether "
"permissions apply to incoming connections and/or manual "
"(default: incoming only). "
"Can be specified multiple times.",
ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg(
"-maxuploadtarget=<n>",
strprintf("Tries to keep outbound traffic under the given target (in "
"MiB per 24h). Limit does not apply to peers with 'download' "
"permission. 0 = no limit (default: %d)",
DEFAULT_MAX_UPLOAD_TARGET),
ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
g_wallet_init_interface.AddWalletOptions(argsman);
#if ENABLE_ZMQ
argsman.AddArg("-zmqpubhashblock=<address>",
"Enable publish hash block in <address>",
ArgsManager::ALLOW_ANY, OptionsCategory::ZMQ);
argsman.AddArg("-zmqpubhashtx=<address>",
"Enable publish hash transaction in <address>",
ArgsManager::ALLOW_ANY, OptionsCategory::ZMQ);
argsman.AddArg("-zmqpubrawblock=<address>",
"Enable publish raw block in <address>",
ArgsManager::ALLOW_ANY, OptionsCategory::ZMQ);
argsman.AddArg("-zmqpubrawtx=<address>",
"Enable publish raw transaction in <address>",
ArgsManager::ALLOW_ANY, OptionsCategory::ZMQ);
argsman.AddArg("-zmqpubsequence=<address>",
"Enable publish hash block and tx sequence in <address>",
ArgsManager::ALLOW_ANY, OptionsCategory::ZMQ);
argsman.AddArg(
"-zmqpubhashblockhwm=<n>",
strprintf("Set publish hash block outbound message high water "
"mark (default: %d)",
CZMQAbstractNotifier::DEFAULT_ZMQ_SNDHWM),
ArgsManager::ALLOW_ANY, OptionsCategory::ZMQ);
argsman.AddArg(
"-zmqpubhashtxhwm=<n>",
strprintf("Set publish hash transaction outbound message high "
"water mark (default: %d)",
CZMQAbstractNotifier::DEFAULT_ZMQ_SNDHWM),
false, OptionsCategory::ZMQ);
argsman.AddArg(
"-zmqpubrawblockhwm=<n>",
strprintf("Set publish raw block outbound message high water "
"mark (default: %d)",
CZMQAbstractNotifier::DEFAULT_ZMQ_SNDHWM),
ArgsManager::ALLOW_ANY, OptionsCategory::ZMQ);
argsman.AddArg(
"-zmqpubrawtxhwm=<n>",
strprintf("Set publish raw transaction outbound message high "
"water mark (default: %d)",
CZMQAbstractNotifier::DEFAULT_ZMQ_SNDHWM),
ArgsManager::ALLOW_ANY, OptionsCategory::ZMQ);
argsman.AddArg("-zmqpubsequencehwm=<n>",
strprintf("Set publish hash sequence message high water mark"
" (default: %d)",
CZMQAbstractNotifier::DEFAULT_ZMQ_SNDHWM),
ArgsManager::ALLOW_ANY, OptionsCategory::ZMQ);
#else
hidden_args.emplace_back("-zmqpubhashblock=<address>");
hidden_args.emplace_back("-zmqpubhashtx=<address>");
hidden_args.emplace_back("-zmqpubrawblock=<address>");
hidden_args.emplace_back("-zmqpubrawtx=<address>");
hidden_args.emplace_back("-zmqpubsequence=<n>");
hidden_args.emplace_back("-zmqpubhashblockhwm=<n>");
hidden_args.emplace_back("-zmqpubhashtxhwm=<n>");
hidden_args.emplace_back("-zmqpubrawblockhwm=<n>");
hidden_args.emplace_back("-zmqpubrawtxhwm=<n>");
hidden_args.emplace_back("-zmqpubsequencehwm=<n>");
#endif
argsman.AddArg(
"-checkblocks=<n>",
strprintf("How many blocks to check at startup (default: %u, 0 = all)",
DEFAULT_CHECKBLOCKS),
ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY,
OptionsCategory::DEBUG_TEST);
argsman.AddArg("-checklevel=<n>",
strprintf("How thorough the block verification of "
"-checkblocks is: %s (0-4, default: %u)",
Join(CHECKLEVEL_DOC, ", "), DEFAULT_CHECKLEVEL),
ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY,
OptionsCategory::DEBUG_TEST);
argsman.AddArg("-checkblockindex",
strprintf("Do a consistency check for the block tree, "
"chainstate, and other validation data structures "
"occasionally. (default: %u, regtest: %u)",
defaultChainParams->DefaultConsistencyChecks(),
regtestChainParams->DefaultConsistencyChecks()),
ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY,
OptionsCategory::DEBUG_TEST);
argsman.AddArg("-checkaddrman=<n>",
strprintf("Run addrman consistency checks every <n> "
"operations. Use 0 to disable. (default: %u)",
DEFAULT_ADDRMAN_CONSISTENCY_CHECKS),
ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY,
OptionsCategory::DEBUG_TEST);
argsman.AddArg(
"-checkmempool=<n>",
strprintf("Run mempool consistency checks every <n> transactions. Use "
"0 to disable. (default: %u, regtest: %u)",
defaultChainParams->DefaultConsistencyChecks(),
regtestChainParams->DefaultConsistencyChecks()),
ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY,
OptionsCategory::DEBUG_TEST);
argsman.AddArg("-checkpoints",
strprintf("Only accept block chain matching built-in "
"checkpoints (default: %d)",
DEFAULT_CHECKPOINTS_ENABLED),
ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY,
OptionsCategory::DEBUG_TEST);
argsman.AddArg("-deprecatedrpc=<method>",
"Allows deprecated RPC method(s) to be used",
ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY,
OptionsCategory::DEBUG_TEST);
argsman.AddArg(
"-stopafterblockimport",
strprintf("Stop running after importing blocks from disk (default: %d)",
DEFAULT_STOPAFTERBLOCKIMPORT),
ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY,
OptionsCategory::DEBUG_TEST);
argsman.AddArg("-stopatheight",
strprintf("Stop running after reaching the given height in "
"the main chain (default: %u)",
DEFAULT_STOPATHEIGHT),
ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY,
OptionsCategory::DEBUG_TEST);
argsman.AddArg("-addrmantest", "Allows to test address relay on localhost",
ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY,
OptionsCategory::DEBUG_TEST);
argsman.AddArg("-capturemessages", "Capture all P2P messages to disk",
ArgsManager::ALLOW_BOOL | ArgsManager::DEBUG_ONLY,
OptionsCategory::DEBUG_TEST);
argsman.AddArg("-mocktime=<n>",
"Replace actual time with " + UNIX_EPOCH_TIME +
" (default: 0)",
ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY,
OptionsCategory::DEBUG_TEST);
argsman.AddArg(
"-maxsigcachesize=<n>",
strprintf("Limit size of signature cache to <n> MiB (default: %u)",
DEFAULT_MAX_SIG_CACHE_BYTES >> 20),
ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY,
OptionsCategory::DEBUG_TEST);
argsman.AddArg(
"-maxscriptcachesize=<n>",
strprintf("Limit size of script cache to <n> MiB (default: %u)",
DEFAULT_MAX_SCRIPT_CACHE_BYTES >> 20),
ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY,
OptionsCategory::DEBUG_TEST);
argsman.AddArg("-maxtipage=<n>",
strprintf("Maximum tip age in seconds to consider node in "
"initial block download (default: %u)",
Ticks<std::chrono::seconds>(DEFAULT_MAX_TIP_AGE)),
ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY,
OptionsCategory::DEBUG_TEST);
argsman.AddArg("-uacomment=<cmt>",
"Append comment to the user agent string",
ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST);
argsman.AddArg("-uaclientname=<clientname>", "Set user agent client name",
ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST);
argsman.AddArg("-uaclientversion=<clientversion>",
"Set user agent client version", ArgsManager::ALLOW_ANY,
OptionsCategory::DEBUG_TEST);
SetupChainParamsBaseOptions(argsman);
argsman.AddArg(
"-acceptnonstdtxn",
strprintf(
"Relay and mine \"non-standard\" transactions (%sdefault: %u)",
"testnet/regtest only; ", defaultChainParams->RequireStandard()),
ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY,
OptionsCategory::NODE_RELAY);
argsman.AddArg("-excessiveblocksize=<n>",
strprintf("Do not accept blocks larger than this limit, in "
"bytes (default: %d)",
DEFAULT_MAX_BLOCK_SIZE),
ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY,
OptionsCategory::NODE_RELAY);
const auto &ticker = Currency::get().ticker;
argsman.AddArg(
"-dustrelayfee=<amt>",
strprintf("Fee rate (in %s/kB) used to define dust, the value of an "
"output such that it will cost about 1/3 of its value in "
"fees at this fee rate to spend it. (default: %s)",
ticker, FormatMoney(DUST_RELAY_TX_FEE)),
ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY,
OptionsCategory::NODE_RELAY);
argsman.AddArg(
"-bytespersigcheck",
strprintf("Equivalent bytes per sigCheck in transactions for relay and "
"mining (default: %u).",
DEFAULT_BYTES_PER_SIGCHECK),
ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY);
argsman.AddArg(
"-bytespersigop",
strprintf("DEPRECATED: Equivalent bytes per sigCheck in transactions "
"for relay and mining (default: %u). This has been "
"deprecated since v0.26.8 and will be removed in the future, "
"please use -bytespersigcheck instead.",
DEFAULT_BYTES_PER_SIGCHECK),
ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY);
argsman.AddArg(
"-datacarrier",
strprintf("Relay and mine data carrier transactions (default: %d)",
DEFAULT_ACCEPT_DATACARRIER),
ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY);
argsman.AddArg(
"-datacarriersize",
strprintf("Maximum size of data in data carrier transactions "
"we relay and mine (default: %u)",
MAX_OP_RETURN_RELAY),
ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY);
argsman.AddArg(
"-minrelaytxfee=<amt>",
strprintf("Fees (in %s/kB) smaller than this are rejected for "
"relaying, mining and transaction creation (default: %s)",
ticker, FormatMoney(DEFAULT_MIN_RELAY_TX_FEE_PER_KB)),
ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY);
argsman.AddArg(
"-whitelistrelay",
strprintf("Add 'relay' permission to whitelisted peers "
"with default permissions. This will accept relayed "
"transactions even when not relaying transactions "
"(default: %d)",
DEFAULT_WHITELISTRELAY),
ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY);
argsman.AddArg(
"-whitelistforcerelay",
strprintf("Add 'forcerelay' permission to whitelisted peers "
"with default permissions. This will relay transactions "
"even if the transactions were already in the mempool "
"(default: %d)",
DEFAULT_WHITELISTFORCERELAY),
ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY);
argsman.AddArg("-blockmaxsize=<n>",
strprintf("Set maximum block size in bytes (default: %d)",
DEFAULT_MAX_GENERATED_BLOCK_SIZE),
ArgsManager::ALLOW_ANY, OptionsCategory::BLOCK_CREATION);
argsman.AddArg(
"-blockmintxfee=<amt>",
strprintf("Set lowest fee rate (in %s/kB) for transactions to "
"be included in block creation. (default: %s)",
ticker, FormatMoney(DEFAULT_BLOCK_MIN_TX_FEE_PER_KB)),
ArgsManager::ALLOW_ANY, OptionsCategory::BLOCK_CREATION);
argsman.AddArg("-simplegbt",
"Use a simplified getblocktemplate output (default: 0)",
ArgsManager::ALLOW_BOOL, OptionsCategory::BLOCK_CREATION);
argsman.AddArg("-blockversion=<n>",
"Override block version to test forking scenarios",
ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY,
OptionsCategory::BLOCK_CREATION);
argsman.AddArg("-server", "Accept command line and JSON-RPC commands",
ArgsManager::ALLOW_ANY, OptionsCategory::RPC);
argsman.AddArg("-rest",
strprintf("Accept public REST requests (default: %d)",
DEFAULT_REST_ENABLE),
ArgsManager::ALLOW_ANY, OptionsCategory::RPC);
argsman.AddArg(
"-rpcbind=<addr>[:port]",
"Bind to given address to listen for JSON-RPC connections. Do not "
"expose the RPC server to untrusted networks such as the public "
"internet! This option is ignored unless -rpcallowip is also passed. "
"Port is optional and overrides -rpcport. Use [host]:port notation "
"for IPv6. This option can be specified multiple times (default: "
"127.0.0.1 and ::1 i.e., localhost)",
ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY |
ArgsManager::SENSITIVE,
OptionsCategory::RPC);
argsman.AddArg(
"-rpcdoccheck",
strprintf("Throw a non-fatal error at runtime if the documentation for "
"an RPC is incorrect (default: %u)",
DEFAULT_RPC_DOC_CHECK),
ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::RPC);
argsman.AddArg(
"-rpccookiefile=<loc>",
"Location of the auth cookie. Relative paths will be prefixed "
"by a net-specific datadir location. (default: data dir)",
ArgsManager::ALLOW_ANY, OptionsCategory::RPC);
argsman.AddArg("-rpcuser=<user>", "Username for JSON-RPC connections",
ArgsManager::ALLOW_ANY | ArgsManager::SENSITIVE,
OptionsCategory::RPC);
argsman.AddArg("-rpcpassword=<pw>", "Password for JSON-RPC connections",
ArgsManager::ALLOW_ANY | ArgsManager::SENSITIVE,
OptionsCategory::RPC);
argsman.AddArg(
"-rpcwhitelist=<whitelist>",
"Set a whitelist to filter incoming RPC calls for a specific user. The "
"field <whitelist> comes in the format: <USERNAME>:<rpc 1>,<rpc "
"2>,...,<rpc n>. If multiple whitelists are set for a given user, they "
"are set-intersected. See -rpcwhitelistdefault documentation for "
"information on default whitelist behavior.",
ArgsManager::ALLOW_ANY, OptionsCategory::RPC);
argsman.AddArg(
"-rpcwhitelistdefault",
"Sets default behavior for rpc whitelisting. Unless "
"rpcwhitelistdefault is set to 0, if any -rpcwhitelist is set, the rpc "
"server acts as if all rpc users are subject to "
"empty-unless-otherwise-specified whitelists. If rpcwhitelistdefault "
"is set to 1 and no -rpcwhitelist is set, rpc server acts as if all "
"rpc users are subject to empty whitelists.",
ArgsManager::ALLOW_BOOL, OptionsCategory::RPC);
argsman.AddArg(
"-rpcauth=<userpw>",
"Username and HMAC-SHA-256 hashed password for JSON-RPC connections. "
"The field <userpw> comes in the format: <USERNAME>:<SALT>$<HASH>. A "
"canonical python script is included in share/rpcauth. The client then "
"connects normally using the rpcuser=<USERNAME>/rpcpassword=<PASSWORD> "
"pair of arguments. This option can be specified multiple times",
ArgsManager::ALLOW_ANY | ArgsManager::SENSITIVE, OptionsCategory::RPC);
argsman.AddArg("-rpcport=<port>",
strprintf("Listen for JSON-RPC connections on <port> "
"(default: %u, testnet: %u, regtest: %u)",
defaultBaseParams->RPCPort(),
testnetBaseParams->RPCPort(),
regtestBaseParams->RPCPort()),
ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY,
OptionsCategory::RPC);
argsman.AddArg(
"-rpcallowip=<ip>",
"Allow JSON-RPC connections from specified source. Valid for "
"<ip> are a single IP (e.g. 1.2.3.4), a network/netmask (e.g. "
"1.2.3.4/255.255.255.0) or a network/CIDR (e.g. 1.2.3.4/24). "
"This option can be specified multiple times",
ArgsManager::ALLOW_ANY, OptionsCategory::RPC);
argsman.AddArg(
"-rpcthreads=<n>",
strprintf(
"Set the number of threads to service RPC calls (default: %d)",
DEFAULT_HTTP_THREADS),
ArgsManager::ALLOW_ANY, OptionsCategory::RPC);
argsman.AddArg(
"-rpccorsdomain=value",
"Domain from which to accept cross origin requests (browser enforced)",
ArgsManager::ALLOW_ANY, OptionsCategory::RPC);
argsman.AddArg("-rpcworkqueue=<n>",
strprintf("Set the depth of the work queue to service RPC "
"calls (default: %d)",
DEFAULT_HTTP_WORKQUEUE),
ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY,
OptionsCategory::RPC);
argsman.AddArg("-rpcservertimeout=<n>",
strprintf("Timeout during HTTP requests (default: %d)",
DEFAULT_HTTP_SERVER_TIMEOUT),
ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY,
OptionsCategory::RPC);
#if HAVE_DECL_FORK
argsman.AddArg("-daemon",
strprintf("Run in the background as a daemon and accept "
"commands (default: %d)",
DEFAULT_DAEMON),
ArgsManager::ALLOW_BOOL, OptionsCategory::OPTIONS);
argsman.AddArg("-daemonwait",
strprintf("Wait for initialization to be finished before "
"exiting. This implies -daemon (default: %d)",
DEFAULT_DAEMONWAIT),
ArgsManager::ALLOW_BOOL, OptionsCategory::OPTIONS);
#else
hidden_args.emplace_back("-daemon");
hidden_args.emplace_back("-daemonwait");
#endif
// Avalanche options.
argsman.AddArg("-avalanche",
strprintf("Enable the avalanche feature (default: %u)",
AVALANCHE_DEFAULT_ENABLED),
ArgsManager::ALLOW_ANY, OptionsCategory::AVALANCHE);
argsman.AddArg(
"-avalanchestakingrewards",
strprintf("Enable the avalanche staking rewards feature (default: %u, "
"testnet: %u, regtest: %u)",
defaultChainParams->GetConsensus().enableStakingRewards,
testnetChainParams->GetConsensus().enableStakingRewards,
regtestChainParams->GetConsensus().enableStakingRewards),
ArgsManager::ALLOW_BOOL, OptionsCategory::AVALANCHE);
argsman.AddArg("-avalancheconflictingproofcooldown",
strprintf("Mandatory cooldown before a proof conflicting "
"with an already registered one can be considered "
"in seconds (default: %u)",
AVALANCHE_DEFAULT_CONFLICTING_PROOF_COOLDOWN),
ArgsManager::ALLOW_INT, OptionsCategory::AVALANCHE);
argsman.AddArg("-avalanchepeerreplacementcooldown",
strprintf("Mandatory cooldown before a peer can be replaced "
"in seconds (default: %u)",
AVALANCHE_DEFAULT_PEER_REPLACEMENT_COOLDOWN),
ArgsManager::ALLOW_INT, OptionsCategory::AVALANCHE);
argsman.AddArg(
"-avaminquorumstake",
strprintf(
"Minimum amount of known stake for a usable quorum (default: %s)",
FormatMoney(AVALANCHE_DEFAULT_MIN_QUORUM_STAKE)),
ArgsManager::ALLOW_ANY, OptionsCategory::AVALANCHE);
argsman.AddArg(
"-avaminquorumconnectedstakeratio",
strprintf("Minimum proportion of known stake we"
" need nodes for to have a usable quorum (default: %s). "
"This parameter is parsed with a maximum precision of "
"0.000001.",
AVALANCHE_DEFAULT_MIN_QUORUM_CONNECTED_STAKE_RATIO),
ArgsManager::ALLOW_STRING, OptionsCategory::AVALANCHE);
argsman.AddArg(
"-avaminavaproofsnodecount",
strprintf("Minimum number of node that needs to send us an avaproofs"
" message before we consider we have a usable quorum"
" (default: %s)",
AVALANCHE_DEFAULT_MIN_AVAPROOFS_NODE_COUNT),
ArgsManager::ALLOW_INT, OptionsCategory::AVALANCHE);
argsman.AddArg(
"-avastalevotethreshold",
strprintf("Number of avalanche votes before a voted item goes stale "
"when voting confidence is low (default: %u)",
AVALANCHE_VOTE_STALE_THRESHOLD),
ArgsManager::ALLOW_INT, OptionsCategory::AVALANCHE);
argsman.AddArg(
"-avastalevotefactor",
strprintf(
"Factor affecting the number of avalanche votes before a voted "
"item goes stale when voting confidence is high (default: %u)",
AVALANCHE_VOTE_STALE_FACTOR),
ArgsManager::ALLOW_INT, OptionsCategory::AVALANCHE);
argsman.AddArg("-avacooldown",
strprintf("Mandatory cooldown between two avapoll in "
"milliseconds (default: %u)",
AVALANCHE_DEFAULT_COOLDOWN),
ArgsManager::ALLOW_ANY, OptionsCategory::AVALANCHE);
argsman.AddArg(
"-avatimeout",
strprintf("Avalanche query timeout in milliseconds (default: %u)",
AVALANCHE_DEFAULT_QUERY_TIMEOUT.count()),
ArgsManager::ALLOW_ANY, OptionsCategory::AVALANCHE);
argsman.AddArg(
"-avadelegation",
"Avalanche proof delegation to the master key used by this node "
"(default: none). Should be used in conjunction with -avaproof and "
"-avamasterkey",
ArgsManager::ALLOW_ANY, OptionsCategory::AVALANCHE);
argsman.AddArg("-avaproof",
"Avalanche proof to be used by this node (default: none)",
ArgsManager::ALLOW_ANY, OptionsCategory::AVALANCHE);
argsman.AddArg(
"-avaproofstakeutxoconfirmations",
strprintf(
"Minimum number of confirmations before a stake utxo is mature"
" enough to be included into a proof. Utxos in the mempool are not "
"accepted (i.e this value must be greater than 0) (default: %s)",
AVALANCHE_DEFAULT_STAKE_UTXO_CONFIRMATIONS),
ArgsManager::ALLOW_INT, OptionsCategory::HIDDEN);
argsman.AddArg("-avaproofstakeutxodustthreshold",
strprintf("Minimum value each stake utxo must have to be "
"considered valid (default: %s)",
avalanche::PROOF_DUST_THRESHOLD),
ArgsManager::ALLOW_ANY, OptionsCategory::HIDDEN);
argsman.AddArg("-avamasterkey",
"Master key associated with the proof. If a proof is "
"required, this is mandatory.",
ArgsManager::ALLOW_ANY | ArgsManager::SENSITIVE,
OptionsCategory::AVALANCHE);
argsman.AddArg("-avasessionkey", "Avalanche session key (default: random)",
ArgsManager::ALLOW_ANY | ArgsManager::SENSITIVE,
OptionsCategory::HIDDEN);
argsman.AddArg("-enablertt",
strprintf("Whether to enforce Real Time Targeting via "
"Avalanche, default (%u)",
DEFAULT_ENABLE_RTT),
ArgsManager::ALLOW_BOOL, OptionsCategory::AVALANCHE);
argsman.AddArg(
"-maxavalancheoutbound",
strprintf(
"Set the maximum number of avalanche outbound peers to connect to. "
"Note that this option takes precedence over the -maxconnections "
"option (default: %u).",
DEFAULT_MAX_AVALANCHE_OUTBOUND_CONNECTIONS),
ArgsManager::ALLOW_INT, OptionsCategory::AVALANCHE);
argsman.AddArg(
"-persistavapeers",
strprintf("Whether to save the avalanche peers upon shutdown and load "
"them upon startup (default: %u).",
DEFAULT_PERSIST_AVAPEERS),
ArgsManager::ALLOW_BOOL, OptionsCategory::AVALANCHE);
hidden_args.emplace_back("-avalanchepreconsensus");
hidden_args.emplace_back("-avalanchestakingpreconsensus");
// Add the hidden options
argsman.AddHiddenArgs(hidden_args);
}
static bool fHaveGenesis = false;
static GlobalMutex g_genesis_wait_mutex;
static std::condition_variable g_genesis_wait_cv;
static void BlockNotifyGenesisWait(const CBlockIndex *pBlockIndex) {
if (pBlockIndex != nullptr) {
{
LOCK(g_genesis_wait_mutex);
fHaveGenesis = true;
}
g_genesis_wait_cv.notify_all();
}
}
#if HAVE_SYSTEM
static void StartupNotify(const ArgsManager &args) {
std::string cmd = args.GetArg("-startupnotify", "");
if (!cmd.empty()) {
std::thread t(runCommand, cmd);
// thread runs free
t.detach();
}
}
#endif
static bool AppInitServers(Config &config,
HTTPRPCRequestProcessor &httpRPCRequestProcessor,
NodeContext &node) {
const ArgsManager &args = *Assert(node.args);
RPCServerSignals::OnStarted(&OnRPCStarted);
RPCServerSignals::OnStopped(&OnRPCStopped);
if (!InitHTTPServer(config)) {
return false;
}
StartRPC();
node.rpc_interruption_point = RpcInterruptionPoint;
if (!StartHTTPRPC(httpRPCRequestProcessor)) {
return false;
}
if (args.GetBoolArg("-rest", DEFAULT_REST_ENABLE)) {
StartREST(&node);
}
StartHTTPServer();
return true;
}
// Parameter interaction based on rules
void InitParameterInteraction(ArgsManager &args) {
// when specifying an explicit binding address, you want to listen on it
// even when -connect or -proxy is specified.
if (args.IsArgSet("-bind")) {
if (args.SoftSetBoolArg("-listen", true)) {
LogPrintf(
"%s: parameter interaction: -bind set -> setting -listen=1\n",
__func__);
}
}
if (args.IsArgSet("-whitebind")) {
if (args.SoftSetBoolArg("-listen", true)) {
LogPrintf("%s: parameter interaction: -whitebind set -> setting "
"-listen=1\n",
__func__);
}
}
if (args.IsArgSet("-connect")) {
// when only connecting to trusted nodes, do not seed via DNS, or listen
// by default.
if (args.SoftSetBoolArg("-dnsseed", false)) {
LogPrintf("%s: parameter interaction: -connect set -> setting "
"-dnsseed=0\n",
__func__);
}
if (args.SoftSetBoolArg("-listen", false)) {
LogPrintf("%s: parameter interaction: -connect set -> setting "
"-listen=0\n",
__func__);
}
}
if (args.IsArgSet("-proxy")) {
// to protect privacy, do not listen by default if a default proxy
// server is specified.
if (args.SoftSetBoolArg("-listen", false)) {
LogPrintf(
"%s: parameter interaction: -proxy set -> setting -listen=0\n",
__func__);
}
// to protect privacy, do not map ports when a proxy is set. The user
// may still specify -listen=1 to listen locally, so don't rely on this
// happening through -listen below.
if (args.SoftSetBoolArg("-upnp", false)) {
LogPrintf(
"%s: parameter interaction: -proxy set -> setting -upnp=0\n",
__func__);
}
if (args.SoftSetBoolArg("-natpmp", false)) {
LogPrintf(
"%s: parameter interaction: -proxy set -> setting -natpmp=0\n",
__func__);
}
// to protect privacy, do not discover addresses by default
if (args.SoftSetBoolArg("-discover", false)) {
LogPrintf("%s: parameter interaction: -proxy set -> setting "
"-discover=0\n",
__func__);
}
}
if (!args.GetBoolArg("-listen", DEFAULT_LISTEN)) {
// do not map ports or try to retrieve public IP when not listening
// (pointless)
if (args.SoftSetBoolArg("-upnp", false)) {
LogPrintf(
"%s: parameter interaction: -listen=0 -> setting -upnp=0\n",
__func__);
}
if (args.SoftSetBoolArg("-natpmp", false)) {
LogPrintf(
"%s: parameter interaction: -listen=0 -> setting -natpmp=0\n",
__func__);
}
if (args.SoftSetBoolArg("-discover", false)) {
LogPrintf(
"%s: parameter interaction: -listen=0 -> setting -discover=0\n",
__func__);
}
if (args.SoftSetBoolArg("-listenonion", false)) {
LogPrintf("%s: parameter interaction: -listen=0 -> setting "
"-listenonion=0\n",
__func__);
}
if (args.SoftSetBoolArg("-i2pacceptincoming", false)) {
LogPrintf("%s: parameter interaction: -listen=0 -> setting "
"-i2pacceptincoming=0\n",
__func__);
}
}
if (args.IsArgSet("-externalip")) {
// if an explicit public IP is specified, do not try to find others
if (args.SoftSetBoolArg("-discover", false)) {
LogPrintf("%s: parameter interaction: -externalip set -> setting "
"-discover=0\n",
__func__);
}
}
// disable whitelistrelay in blocksonly mode
if (args.GetBoolArg("-blocksonly", DEFAULT_BLOCKSONLY)) {
if (args.SoftSetBoolArg("-whitelistrelay", false)) {
LogPrintf("%s: parameter interaction: -blocksonly=1 -> setting "
"-whitelistrelay=0\n",
__func__);
}
}
// Forcing relay from whitelisted hosts implies we will accept relays from
// them in the first place.
if (args.GetBoolArg("-whitelistforcerelay", DEFAULT_WHITELISTFORCERELAY)) {
if (args.SoftSetBoolArg("-whitelistrelay", true)) {
LogPrintf("%s: parameter interaction: -whitelistforcerelay=1 -> "
"setting -whitelistrelay=1\n",
__func__);
}
}
// If avalanche is set, soft set all the feature flags accordingly.
if (args.IsArgSet("-avalanche")) {
const bool fAvalanche =
args.GetBoolArg("-avalanche", AVALANCHE_DEFAULT_ENABLED);
args.SoftSetBoolArg("-automaticunparking", !fAvalanche);
}
}
/**
* Initialize global loggers.
*
* Note that this is called very early in the process lifetime, so you should be
* careful about what global state you rely on here.
*/
void InitLogging(const ArgsManager &args) {
init::SetLoggingOptions(args);
init::LogPackageVersion();
}
namespace { // Variables internal to initialization process only
int nMaxConnections;
int nUserMaxConnections;
int nFD;
ServiceFlags nLocalServices = ServiceFlags(NODE_NETWORK | NODE_NETWORK_LIMITED);
int64_t peer_connect_timeout;
std::set<BlockFilterType> g_enabled_filter_types;
} // namespace
[[noreturn]] static void new_handler_terminate() {
// Rather than throwing std::bad-alloc if allocation fails, terminate
// immediately to (try to) avoid chain corruption. Since LogPrintf may
// itself allocate memory, set the handler directly to terminate first.
std::set_new_handler(std::terminate);
LogPrintf("Error: Out of memory. Terminating.\n");
// The log was successful, terminate now.
std::terminate();
};
bool AppInitBasicSetup(const ArgsManager &args) {
// Step 1: setup
#ifdef _MSC_VER
// Turn off Microsoft heap dump noise
_CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);
_CrtSetReportFile(_CRT_WARN, CreateFileA("NUL", GENERIC_WRITE, 0, nullptr,
OPEN_EXISTING, 0, 0));
// Disable confusing "helpful" text message on abort, Ctrl-C
_set_abort_behavior(0, _WRITE_ABORT_MSG | _CALL_REPORTFAULT);
#endif
#ifdef WIN32
// Enable Data Execution Prevention (DEP)
SetProcessDEPPolicy(PROCESS_DEP_ENABLE);
#endif
if (!InitShutdownState()) {
return InitError(
Untranslated("Initializing wait-for-shutdown state failed."));
}
if (!SetupNetworking()) {
return InitError(Untranslated("Initializing networking failed"));
}
#ifndef WIN32
if (!args.GetBoolArg("-sysperms", false)) {
umask(077);
}
// Clean shutdown on SIGTERM
registerSignalHandler(SIGTERM, HandleSIGTERM);
registerSignalHandler(SIGINT, HandleSIGTERM);
// Reopen debug.log on SIGHUP
registerSignalHandler(SIGHUP, HandleSIGHUP);
// Ignore SIGPIPE, otherwise it will bring the daemon down if the client
// closes unexpectedly
signal(SIGPIPE, SIG_IGN);
#else
SetConsoleCtrlHandler(consoleCtrlHandler, true);
#endif
std::set_new_handler(new_handler_terminate);
return true;
}
bool AppInitParameterInteraction(Config &config, const ArgsManager &args) {
const CChainParams &chainparams = config.GetChainParams();
// Step 2: parameter interactions
// also see: InitParameterInteraction()
// Error if network-specific options (-addnode, -connect, etc) are
// specified in default section of config file, but not overridden
// on the command line or in this network's section of the config file.
std::string network = args.GetChainName();
bilingual_str errors;
for (const auto &arg : args.GetUnsuitableSectionOnlyArgs()) {
errors += strprintf(_("Config setting for %s only applied on %s "
"network when in [%s] section.") +
Untranslated("\n"),
arg, network, network);
}
if (!errors.empty()) {
return InitError(errors);
}
// Warn if unrecognized section name are present in the config file.
bilingual_str warnings;
for (const auto &section : args.GetUnrecognizedSections()) {
warnings += strprintf(Untranslated("%s:%i ") +
_("Section [%s] is not recognized.") +
Untranslated("\n"),
section.m_file, section.m_line, section.m_name);
}
if (!warnings.empty()) {
InitWarning(warnings);
}
if (!fs::is_directory(args.GetBlocksDirPath())) {
return InitError(
strprintf(_("Specified blocks directory \"%s\" does not exist."),
args.GetArg("-blocksdir", "")));
}
// parse and validate enabled filter types
std::string blockfilterindex_value =
args.GetArg("-blockfilterindex", DEFAULT_BLOCKFILTERINDEX);
if (blockfilterindex_value == "" || blockfilterindex_value == "1") {
g_enabled_filter_types = AllBlockFilterTypes();
} else if (blockfilterindex_value != "0") {
const std::vector<std::string> names =
args.GetArgs("-blockfilterindex");
for (const auto &name : names) {
BlockFilterType filter_type;
if (!BlockFilterTypeByName(name, filter_type)) {
return InitError(
strprintf(_("Unknown -blockfilterindex value %s."), name));
}
g_enabled_filter_types.insert(filter_type);
}
}
// Signal NODE_COMPACT_FILTERS if peerblockfilters and basic filters index
// are both enabled.
if (args.GetBoolArg("-peerblockfilters", DEFAULT_PEERBLOCKFILTERS)) {
if (g_enabled_filter_types.count(BlockFilterType::BASIC) != 1) {
return InitError(
_("Cannot set -peerblockfilters without -blockfilterindex."));
}
nLocalServices = ServiceFlags(nLocalServices | NODE_COMPACT_FILTERS);
}
if (args.GetIntArg("-prune", 0)) {
if (args.GetBoolArg("-txindex", DEFAULT_TXINDEX)) {
return InitError(_("Prune mode is incompatible with -txindex."));
}
if (args.GetBoolArg("-reindex-chainstate", false)) {
return InitError(
_("Prune mode is incompatible with -reindex-chainstate. Use "
"full -reindex instead."));
}
if (args.GetBoolArg("-chronik", DEFAULT_CHRONIK)) {
return InitError(_("Prune mode is incompatible with -chronik."));
}
}
// -bind and -whitebind can't be set when not listening
size_t nUserBind =
args.GetArgs("-bind").size() + args.GetArgs("-whitebind").size();
if (nUserBind != 0 && !args.GetBoolArg("-listen", DEFAULT_LISTEN)) {
return InitError(Untranslated(
"Cannot set -bind or -whitebind together with -listen=0"));
}
// Make sure enough file descriptors are available
int nBind = std::max(nUserBind, size_t(1));
nUserMaxConnections =
args.GetIntArg("-maxconnections", DEFAULT_MAX_PEER_CONNECTIONS);
nMaxConnections = std::max(nUserMaxConnections, 0);
// -maxavalancheoutbound takes precedence over -maxconnections
const int maxAvalancheOutbound = args.GetIntArg(
"-maxavalancheoutbound", DEFAULT_MAX_AVALANCHE_OUTBOUND_CONNECTIONS);
const bool fAvalanche =
args.GetBoolArg("-avalanche", AVALANCHE_DEFAULT_ENABLED);
if (fAvalanche && maxAvalancheOutbound > nMaxConnections) {
nMaxConnections = std::max(maxAvalancheOutbound, nMaxConnections);
// Indicate the value set by the user
LogPrintf("Increasing -maxconnections from %d to %d to comply with "
"-maxavalancheoutbound\n",
nUserMaxConnections, nMaxConnections);
}
// Trim requested connection counts, to fit into system limitations
// <int> in std::min<int>(...) to work around FreeBSD compilation issue
// described in #2695
nFD = RaiseFileDescriptorLimit(
nMaxConnections + nBind + MIN_CORE_FILEDESCRIPTORS +
MAX_ADDNODE_CONNECTIONS + NUM_FDS_MESSAGE_CAPTURE);
#ifdef USE_POLL
int fd_max = nFD;
#else
int fd_max = FD_SETSIZE;
#endif
nMaxConnections = std::max(
std::min<int>(nMaxConnections,
fd_max - nBind - MIN_CORE_FILEDESCRIPTORS -
MAX_ADDNODE_CONNECTIONS - NUM_FDS_MESSAGE_CAPTURE),
0);
if (nFD < MIN_CORE_FILEDESCRIPTORS) {
return InitError(_("Not enough file descriptors available."));
}
nMaxConnections =
std::min(nFD - MIN_CORE_FILEDESCRIPTORS - MAX_ADDNODE_CONNECTIONS,
nMaxConnections);
if (nMaxConnections < nUserMaxConnections) {
// Not categorizing as "Warning" because this is the normal behavior for
// platforms using the select() interface for which FD_SETSIZE is
// usually 1024.
LogPrintf("Reducing -maxconnections from %d to %d, because of system "
"limitations.\n",
nUserMaxConnections, nMaxConnections);
}
// Step 3: parameter-to-internal-flags
init::SetLoggingCategories(args);
// Configure excessive block size.
const int64_t nProposedExcessiveBlockSize =
args.GetIntArg("-excessiveblocksize", DEFAULT_MAX_BLOCK_SIZE);
if (nProposedExcessiveBlockSize <= 0 ||
!config.SetMaxBlockSize(nProposedExcessiveBlockSize)) {
return InitError(
_("Excessive block size must be > 1,000,000 bytes (1MB)"));
}
// Check blockmaxsize does not exceed maximum accepted block size.
const int64_t nProposedMaxGeneratedBlockSize =
args.GetIntArg("-blockmaxsize", DEFAULT_MAX_GENERATED_BLOCK_SIZE);
if (nProposedMaxGeneratedBlockSize <= 0) {
return InitError(_("Max generated block size must be greater than 0"));
}
if (uint64_t(nProposedMaxGeneratedBlockSize) > config.GetMaxBlockSize()) {
return InitError(_("Max generated block size (blockmaxsize) cannot "
"exceed the excessive block size "
"(excessiveblocksize)"));
}
nConnectTimeout = args.GetIntArg("-timeout", DEFAULT_CONNECT_TIMEOUT);
if (nConnectTimeout <= 0) {
nConnectTimeout = DEFAULT_CONNECT_TIMEOUT;
}
peer_connect_timeout =
args.GetIntArg("-peertimeout", DEFAULT_PEER_CONNECT_TIMEOUT);
if (peer_connect_timeout <= 0) {
return InitError(Untranslated(
"peertimeout cannot be configured with a negative value."));
}
// Sanity check argument for min fee for including tx in block
// TODO: Harmonize which arguments need sanity checking and where that
// happens.
if (args.IsArgSet("-blockmintxfee")) {
Amount n = Amount::zero();
if (!ParseMoney(args.GetArg("-blockmintxfee", ""), n)) {
return InitError(AmountErrMsg("blockmintxfee",
args.GetArg("-blockmintxfee", "")));
}
}
nBytesPerSigCheck =
args.IsArgSet("-bytespersigcheck")
? args.GetIntArg("-bytespersigcheck", nBytesPerSigCheck)
: args.GetIntArg("-bytespersigop", nBytesPerSigCheck);
if (!g_wallet_init_interface.ParameterInteraction()) {
return false;
}
// Option to startup with mocktime set (used for regression testing):
SetMockTime(args.GetIntArg("-mocktime", 0)); // SetMockTime(0) is a no-op
if (args.GetBoolArg("-peerbloomfilters", DEFAULT_PEERBLOOMFILTERS)) {
nLocalServices = ServiceFlags(nLocalServices | NODE_BLOOM);
}
if (args.IsArgSet("-proxy") && args.GetArg("-proxy", "").empty()) {
return InitError(_(
"No proxy server specified. Use -proxy=<ip> or -proxy=<ip:port>."));
}
// Avalanche parameters
const int64_t stakeUtxoMinConfirmations =
args.GetIntArg("-avaproofstakeutxoconfirmations",
AVALANCHE_DEFAULT_STAKE_UTXO_CONFIRMATIONS);
if (!chainparams.IsTestChain() &&
stakeUtxoMinConfirmations !=
AVALANCHE_DEFAULT_STAKE_UTXO_CONFIRMATIONS) {
return InitError(_("Avalanche stake UTXO minimum confirmations can "
"only be set on test chains."));
}
if (stakeUtxoMinConfirmations <= 0) {
return InitError(_("Avalanche stake UTXO minimum confirmations must be "
"a positive integer."));
}
if (args.IsArgSet("-avaproofstakeutxodustthreshold")) {
Amount amount = Amount::zero();
auto parsed = ParseMoney(
args.GetArg("-avaproofstakeutxodustthreshold", ""), amount);
if (!parsed || Amount::zero() == amount) {
return InitError(AmountErrMsg(
"avaproofstakeutxodustthreshold",
args.GetArg("-avaproofstakeutxodustthreshold", "")));
}
if (!chainparams.IsTestChain() &&
amount != avalanche::PROOF_DUST_THRESHOLD) {
return InitError(_("Avalanche stake UTXO dust threshold can "
"only be set on test chains."));
}
}
// This is a staking node
if (fAvalanche && args.IsArgSet("-avaproof")) {
if (!args.GetBoolArg("-listen", true)) {
return InitError(_("Running a staking node requires accepting "
"inbound connections. Please enable -listen."));
}
if (args.IsArgSet("-proxy")) {
return InitError(_("Running a staking node behind a proxy is not "
"supported. Please disable -proxy."));
}
if (args.IsArgSet("-i2psam")) {
return InitError(_("Running a staking node behind I2P is not "
"supported. Please disable -i2psam."));
}
if (args.IsArgSet("-onlynet")) {
return InitError(
_("Restricting the outbound network is not supported when "
"running a staking node. Please disable -onlynet."));
}
}
// Also report errors from parsing before daemonization
{
KernelNotifications notifications{};
ChainstateManager::Options chainman_opts_dummy{
.config = config,
.datadir = args.GetDataDirNet(),
.notifications = notifications,
};
if (const auto error{ApplyArgsManOptions(args, chainman_opts_dummy)}) {
return InitError(*error);
}
BlockManager::Options blockman_opts_dummy{
.chainparams = chainman_opts_dummy.config.GetChainParams(),
.blocks_dir = args.GetBlocksDirPath(),
};
if (const auto error{ApplyArgsManOptions(args, blockman_opts_dummy)}) {
return InitError(*error);
}
}
return true;
}
static bool LockDataDirectory(bool probeOnly) {
// Make sure only a single Bitcoin process is using the data directory.
fs::path datadir = gArgs.GetDataDirNet();
if (!DirIsWritable(datadir)) {
return InitError(strprintf(
_("Cannot write to data directory '%s'; check permissions."),
fs::PathToString(datadir)));
}
if (!LockDirectory(datadir, ".lock", probeOnly)) {
return InitError(strprintf(_("Cannot obtain a lock on data directory "
"%s. %s is probably already running."),
fs::PathToString(datadir), PACKAGE_NAME));
}
return true;
}
-bool AppInitSanityChecks() {
+bool AppInitSanityChecks(const kernel::Context &kernel) {
// Step 4: sanity checks
+ auto maybe_error = kernel::SanityChecks(kernel);
+
+ if (maybe_error.has_value()) {
+ switch (maybe_error.value()) {
+ case kernel::SanityCheckError::ERROR_ECC:
+ InitError(Untranslated("Elliptic curve cryptography sanity "
+ "check failure. Aborting."));
+ break;
+ case kernel::SanityCheckError::ERROR_RANDOM:
+ InitError(Untranslated(
+ "OS cryptographic RNG sanity check failure. Aborting."));
+ break;
+ case kernel::SanityCheckError::ERROR_CHRONO:
+ InitError(Untranslated("Clock epoch mismatch. Aborting."));
+ break;
+ } // no default case, so the compiler can warn about missing cases
- init::SetGlobals();
-
- // Sanity check
- if (!init::SanityChecks()) {
return InitError(strprintf(
_("Initialization sanity check failed. %s is shutting down."),
PACKAGE_NAME));
}
// Probe the data directory lock to give an early error message, if possible
// We cannot hold the data directory lock here, as the forking for daemon()
// hasn't yet happened, and a fork will cause weird behavior to it.
return LockDataDirectory(true);
}
bool AppInitLockDataDirectory() {
// After daemonization get the data directory lock again and hold on to it
// until exit. This creates a slight window for a race condition to happen,
// however this condition is harmless: it will at most make us exit without
// printing a message to console.
if (!LockDataDirectory(false)) {
// Detailed error printed inside LockDataDirectory
return false;
}
return true;
}
bool AppInitInterfaces(NodeContext &node) {
node.chain = interfaces::MakeChain(node, Params());
// Create client interfaces for wallets that are supposed to be loaded
// according to -wallet and -disablewallet options. This only constructs
// the interfaces, it doesn't load wallet data. Wallets actually get loaded
// when load() and start() interface methods are called below.
g_wallet_init_interface.Construct(node);
return true;
}
bool AppInitMain(Config &config, RPCServer &rpcServer,
HTTPRPCRequestProcessor &httpRPCRequestProcessor,
NodeContext &node,
interfaces::BlockAndHeaderTipInfo *tip_info) {
// Step 4a: application initialization
const ArgsManager &args = *Assert(node.args);
const CChainParams &chainparams = config.GetChainParams();
if (!CreatePidFile(args)) {
// Detailed error printed inside CreatePidFile().
return false;
}
if (!init::StartLogging(args)) {
// Detailed error printed inside StartLogging().
return false;
}
LogPrintf("Using at most %i automatic connections (%i file descriptors "
"available)\n",
nMaxConnections, nFD);
// Warn about relative -datadir path.
if (args.IsArgSet("-datadir") &&
!args.GetPathArg("-datadir").is_absolute()) {
LogPrintf("Warning: relative datadir option '%s' specified, which will "
"be interpreted relative to the current working directory "
"'%s'. This is fragile, because if bitcoin is started in the "
"future from a different location, it will be unable to "
"locate the current data files. There could also be data "
"loss if bitcoin is started while in a temporary "
"directory.\n",
args.GetArg("-datadir", ""),
fs::PathToString(fs::current_path()));
}
ValidationCacheSizes validation_cache_sizes{};
ApplyArgsManOptions(args, validation_cache_sizes);
if (!InitSignatureCache(validation_cache_sizes.signature_cache_bytes)) {
return InitError(strprintf(
_("Unable to allocate memory for -maxsigcachesize: '%s' MiB"),
args.GetIntArg("-maxsigcachesize",
DEFAULT_MAX_SIG_CACHE_BYTES >> 20)));
}
if (!InitScriptExecutionCache(
validation_cache_sizes.script_execution_cache_bytes)) {
return InitError(strprintf(
_("Unable to allocate memory for -maxscriptcachesize: '%s' MiB"),
args.GetIntArg("-maxscriptcachesize",
DEFAULT_MAX_SCRIPT_CACHE_BYTES >> 20)));
}
int script_threads = args.GetIntArg("-par", DEFAULT_SCRIPTCHECK_THREADS);
if (script_threads <= 0) {
// -par=0 means autodetect (number of cores - 1 script threads)
// -par=-n means "leave n cores free" (number of cores - n - 1 script
// threads)
script_threads += GetNumCores();
}
// Subtract 1 because the main thread counts towards the par threads
script_threads = std::max(script_threads - 1, 0);
// Number of script-checking threads <= MAX_SCRIPTCHECK_THREADS
script_threads = std::min(script_threads, MAX_SCRIPTCHECK_THREADS);
LogPrintf("Script verification uses %d additional threads\n",
script_threads);
if (script_threads >= 1) {
StartScriptCheckWorkerThreads(script_threads);
}
assert(!node.scheduler);
node.scheduler = std::make_unique<CScheduler>();
// Start the lightweight task scheduler thread
node.scheduler->m_service_thread =
std::thread(&util::TraceThread, "scheduler",
[&] { node.scheduler->serviceQueue(); });
// Gather some entropy once per minute.
node.scheduler->scheduleEvery(
[] {
RandAddPeriodic();
return true;
},
std::chrono::minutes{1});
GetMainSignals().RegisterBackgroundSignalScheduler(*node.scheduler);
/**
* Register RPC commands regardless of -server setting so they will be
* available in the GUI RPC console even if external calls are disabled.
*/
RegisterAllRPCCommands(config, rpcServer, tableRPC);
for (const auto &client : node.chain_clients) {
client->registerRpcs();
}
#if ENABLE_ZMQ
RegisterZMQRPCCommands(tableRPC);
#endif
/**
* Start the RPC server. It will be started in "warmup" mode and not
* process calls yet (but it will verify that the server is there and will
* be ready later). Warmup mode will be completed when initialisation is
* finished.
*/
if (args.GetBoolArg("-server", false)) {
uiInterface.InitMessage_connect(SetRPCWarmupStatus);
if (!AppInitServers(config, httpRPCRequestProcessor, node)) {
return InitError(
_("Unable to start HTTP server. See debug log for details."));
}
}
// Step 5: verify wallet database integrity
for (const auto &client : node.chain_clients) {
if (!client->verify()) {
return false;
}
}
// Step 6: network initialization
// Note that we absolutely cannot open any actual connections
// until the very end ("start node") as the UTXO/block state
// is not yet setup and may end up being set up twice if we
// need to reindex later.
fListen = args.GetBoolArg("-listen", DEFAULT_LISTEN);
fDiscover = args.GetBoolArg("-discover", true);
{
// Initialize addrman
assert(!node.addrman);
// Read asmap file if configured
std::vector<bool> asmap;
if (args.IsArgSet("-asmap")) {
fs::path asmap_path =
args.GetPathArg("-asmap", DEFAULT_ASMAP_FILENAME);
if (!asmap_path.is_absolute()) {
asmap_path = args.GetDataDirNet() / asmap_path;
}
if (!fs::exists(asmap_path)) {
InitError(strprintf(_("Could not find asmap file %s"),
fs::quoted(fs::PathToString(asmap_path))));
return false;
}
asmap = DecodeAsmap(asmap_path);
if (asmap.size() == 0) {
InitError(strprintf(_("Could not parse asmap file %s"),
fs::quoted(fs::PathToString(asmap_path))));
return false;
}
const uint256 asmap_version = (HashWriter{} << asmap).GetHash();
LogPrintf("Using asmap version %s for IP bucketing\n",
asmap_version.ToString());
} else {
LogPrintf("Using /16 prefix for IP bucketing\n");
}
uiInterface.InitMessage(_("Loading P2P addresses...").translated);
auto addrman{LoadAddrman(chainparams, asmap, args)};
if (!addrman) {
return InitError(util::ErrorString(addrman));
}
node.addrman = std::move(*addrman);
}
assert(!node.banman);
node.banman = std::make_unique<BanMan>(
args.GetDataDirNet() / "banlist.dat", config.GetChainParams(),
&uiInterface, args.GetIntArg("-bantime", DEFAULT_MISBEHAVING_BANTIME));
assert(!node.connman);
node.connman = std::make_unique<CConnman>(
config, GetRand<uint64_t>(), GetRand<uint64_t>(), *node.addrman,
args.GetBoolArg("-networkactive", true));
// sanitize comments per BIP-0014, format user agent and check total size
std::vector<std::string> uacomments;
for (const std::string &cmt : args.GetArgs("-uacomment")) {
if (cmt != SanitizeString(cmt, SAFE_CHARS_UA_COMMENT)) {
return InitError(strprintf(
_("User Agent comment (%s) contains unsafe characters."), cmt));
}
uacomments.push_back(cmt);
}
const std::string client_name = args.GetArg("-uaclientname", CLIENT_NAME);
const std::string client_version =
args.GetArg("-uaclientversion", FormatVersion(CLIENT_VERSION));
if (client_name != SanitizeString(client_name, SAFE_CHARS_UA_COMMENT)) {
return InitError(strprintf(
_("-uaclientname (%s) contains invalid characters."), client_name));
}
if (client_version !=
SanitizeString(client_version, SAFE_CHARS_UA_COMMENT)) {
return InitError(
strprintf(_("-uaclientversion (%s) contains invalid characters."),
client_version));
}
const std::string strSubVersion =
FormatUserAgent(client_name, client_version, uacomments);
if (strSubVersion.size() > MAX_SUBVERSION_LENGTH) {
return InitError(strprintf(
_("Total length of network version string (%i) exceeds maximum "
"length (%i). Reduce the number or size of uacomments."),
strSubVersion.size(), MAX_SUBVERSION_LENGTH));
}
if (args.IsArgSet("-onlynet")) {
std::set<enum Network> nets;
for (const std::string &snet : args.GetArgs("-onlynet")) {
enum Network net = ParseNetwork(snet);
if (net == NET_UNROUTABLE) {
return InitError(strprintf(
_("Unknown network specified in -onlynet: '%s'"), snet));
}
nets.insert(net);
}
for (int n = 0; n < NET_MAX; n++) {
enum Network net = (enum Network)n;
if (!nets.count(net)) {
SetReachable(net, false);
}
}
}
// Check for host lookup allowed before parsing any network related
// parameters
fNameLookup = args.GetBoolArg("-dns", DEFAULT_NAME_LOOKUP);
bool proxyRandomize =
args.GetBoolArg("-proxyrandomize", DEFAULT_PROXYRANDOMIZE);
// -proxy sets a proxy for all outgoing network traffic
// -noproxy (or -proxy=0) as well as the empty string can be used to not set
// a proxy, this is the default
std::string proxyArg = args.GetArg("-proxy", "");
SetReachable(NET_ONION, false);
if (proxyArg != "" && proxyArg != "0") {
CService proxyAddr;
if (!Lookup(proxyArg, proxyAddr, 9050, fNameLookup)) {
return InitError(strprintf(
_("Invalid -proxy address or hostname: '%s'"), proxyArg));
}
proxyType addrProxy = proxyType(proxyAddr, proxyRandomize);
if (!addrProxy.IsValid()) {
return InitError(strprintf(
_("Invalid -proxy address or hostname: '%s'"), proxyArg));
}
SetProxy(NET_IPV4, addrProxy);
SetProxy(NET_IPV6, addrProxy);
SetProxy(NET_ONION, addrProxy);
SetNameProxy(addrProxy);
// by default, -proxy sets onion as reachable, unless -noonion later
SetReachable(NET_ONION, true);
}
// -onion can be used to set only a proxy for .onion, or override normal
// proxy for .onion addresses.
// -noonion (or -onion=0) disables connecting to .onion entirely. An empty
// string is used to not override the onion proxy (in which case it defaults
// to -proxy set above, or none)
std::string onionArg = args.GetArg("-onion", "");
if (onionArg != "") {
if (onionArg == "0") {
// Handle -noonion/-onion=0
SetReachable(NET_ONION, false);
} else {
CService onionProxy;
if (!Lookup(onionArg, onionProxy, 9050, fNameLookup)) {
return InitError(strprintf(
_("Invalid -onion address or hostname: '%s'"), onionArg));
}
proxyType addrOnion = proxyType(onionProxy, proxyRandomize);
if (!addrOnion.IsValid()) {
return InitError(strprintf(
_("Invalid -onion address or hostname: '%s'"), onionArg));
}
SetProxy(NET_ONION, addrOnion);
SetReachable(NET_ONION, true);
}
}
for (const std::string &strAddr : args.GetArgs("-externalip")) {
CService addrLocal;
if (Lookup(strAddr, addrLocal, GetListenPort(), fNameLookup) &&
addrLocal.IsValid()) {
AddLocal(addrLocal, LOCAL_MANUAL);
} else {
return InitError(ResolveErrMsg("externalip", strAddr));
}
}
#if ENABLE_ZMQ
g_zmq_notification_interface = CZMQNotificationInterface::Create(
[&chainman = node.chainman](CBlock &block, const CBlockIndex &index) {
assert(chainman);
return chainman->m_blockman.ReadBlockFromDisk(block, index);
});
if (g_zmq_notification_interface) {
RegisterValidationInterface(g_zmq_notification_interface.get());
}
#endif
// Step 7: load block chain
node.notifications = std::make_unique<KernelNotifications>();
fReindex = args.GetBoolArg("-reindex", false);
bool fReindexChainState = args.GetBoolArg("-reindex-chainstate", false);
ChainstateManager::Options chainman_opts{
.config = config,
.datadir = args.GetDataDirNet(),
.adjusted_time_callback = GetAdjustedTime,
.notifications = *node.notifications,
};
// no error can happen, already checked in AppInitParameterInteraction
Assert(!ApplyArgsManOptions(args, chainman_opts));
if (chainman_opts.checkpoints_enabled) {
LogPrintf("Checkpoints will be verified.\n");
} else {
LogPrintf("Skipping checkpoint verification.\n");
}
BlockManager::Options blockman_opts{
.chainparams = chainman_opts.config.GetChainParams(),
.blocks_dir = args.GetBlocksDirPath(),
};
// no error can happen, already checked in AppInitParameterInteraction
Assert(!ApplyArgsManOptions(args, blockman_opts));
// cache size calculations
CacheSizes cache_sizes =
CalculateCacheSizes(args, g_enabled_filter_types.size());
LogPrintf("Cache configuration:\n");
LogPrintf("* Using %.1f MiB for block index database\n",
cache_sizes.block_tree_db * (1.0 / 1024 / 1024));
if (args.GetBoolArg("-txindex", DEFAULT_TXINDEX)) {
LogPrintf("* Using %.1f MiB for transaction index database\n",
cache_sizes.tx_index * (1.0 / 1024 / 1024));
}
for (BlockFilterType filter_type : g_enabled_filter_types) {
LogPrintf("* Using %.1f MiB for %s block filter index database\n",
cache_sizes.filter_index * (1.0 / 1024 / 1024),
BlockFilterTypeName(filter_type));
}
LogPrintf("* Using %.1f MiB for chain state database\n",
cache_sizes.coins_db * (1.0 / 1024 / 1024));
assert(!node.mempool);
assert(!node.chainman);
CTxMemPool::Options mempool_opts{
.check_ratio = chainparams.DefaultConsistencyChecks() ? 1 : 0,
};
if (const auto err{ApplyArgsManOptions(args, chainparams, mempool_opts)}) {
return InitError(*err);
}
mempool_opts.check_ratio =
std::clamp<int>(mempool_opts.check_ratio, 0, 1'000'000);
// FIXME: this legacy limit comes from the DEFAULT_DESCENDANT_SIZE_LIMIT
// (101) that was enforced before the wellington activation. While it's
// still a good idea to have some minimum mempool size, using this value as
// a threshold is no longer relevant.
int64_t nMempoolSizeMin = 101 * 1000 * 40;
if (mempool_opts.max_size_bytes < 0 ||
(!chainparams.IsTestChain() &&
mempool_opts.max_size_bytes < nMempoolSizeMin)) {
return InitError(strprintf(_("-maxmempool must be at least %d MB"),
std::ceil(nMempoolSizeMin / 1000000.0)));
}
LogPrintf("* Using %.1f MiB for in-memory UTXO set (plus up to %.1f MiB of "
"unused mempool space)\n",
cache_sizes.coins * (1.0 / 1024 / 1024),
mempool_opts.max_size_bytes * (1.0 / 1024 / 1024));
for (bool fLoaded = false; !fLoaded && !ShutdownRequested();) {
node.mempool = std::make_unique<CTxMemPool>(mempool_opts);
node.chainman =
std::make_unique<ChainstateManager>(chainman_opts, blockman_opts);
ChainstateManager &chainman = *node.chainman;
node::ChainstateLoadOptions options;
options.mempool = Assert(node.mempool.get());
options.reindex = node::fReindex;
options.reindex_chainstate = fReindexChainState;
options.prune = chainman.m_blockman.IsPruneMode();
options.check_blocks =
args.GetIntArg("-checkblocks", DEFAULT_CHECKBLOCKS);
options.check_level = args.GetIntArg("-checklevel", DEFAULT_CHECKLEVEL);
options.require_full_verification =
args.IsArgSet("-checkblocks") || args.IsArgSet("-checklevel");
options.check_interrupt = ShutdownRequested;
options.coins_error_cb = [] {
uiInterface.ThreadSafeMessageBox(
_("Error reading from database, shutting down."), "",
CClientUIInterface::MSG_ERROR);
};
uiInterface.InitMessage(_("Loading block index...").translated);
const int64_t load_block_index_start_time = GetTimeMillis();
auto catch_exceptions = [](auto &&f) {
try {
return f();
} catch (const std::exception &e) {
LogPrintf("%s\n", e.what());
return std::make_tuple(node::ChainstateLoadStatus::FAILURE,
_("Error opening block database"));
}
};
auto [status, error] = catch_exceptions(
[&] { return LoadChainstate(chainman, cache_sizes, options); });
if (status == node::ChainstateLoadStatus::SUCCESS) {
uiInterface.InitMessage(_("Verifying blocks...").translated);
if (chainman.m_blockman.m_have_pruned &&
options.check_blocks > MIN_BLOCKS_TO_KEEP) {
LogPrintfCategory(BCLog::PRUNE,
"pruned datadir may not have more than %d "
"blocks; only checking available blocks\n",
MIN_BLOCKS_TO_KEEP);
}
std::tie(status, error) = catch_exceptions(
[&] { return VerifyLoadedChainstate(chainman, options); });
if (status == node::ChainstateLoadStatus::SUCCESS) {
WITH_LOCK(cs_main, return node.chainman->LoadRecentHeadersTime(
node.chainman->m_options.datadir /
HEADERS_TIME_FILE_NAME));
fLoaded = true;
LogPrintf(" block index %15dms\n",
GetTimeMillis() - load_block_index_start_time);
}
}
if (status == node::ChainstateLoadStatus::FAILURE_FATAL ||
status == node::ChainstateLoadStatus::FAILURE_INCOMPATIBLE_DB ||
status ==
node::ChainstateLoadStatus::FAILURE_INSUFFICIENT_DBCACHE) {
return InitError(error);
}
if (!fLoaded && !ShutdownRequested()) {
// first suggest a reindex
if (!options.reindex) {
bool fRet = uiInterface.ThreadSafeQuestion(
error + Untranslated(".\n\n") +
_("Do you want to rebuild the block database now?"),
error.original + ".\nPlease restart with -reindex or "
"-reindex-chainstate to recover.",
"",
CClientUIInterface::MSG_ERROR |
CClientUIInterface::BTN_ABORT);
if (fRet) {
fReindex = true;
AbortShutdown();
} else {
LogPrintf("Aborted block database rebuild. Exiting.\n");
return false;
}
} else {
return InitError(error);
}
}
}
// As LoadBlockIndex can take several minutes, it's possible the user
// requested to kill the GUI during the last operation. If so, exit.
// As the program has not fully started yet, Shutdown() is possibly
// overkill.
if (ShutdownRequested()) {
LogPrintf("Shutdown requested. Exiting.\n");
return false;
}
ChainstateManager &chainman = *Assert(node.chainman);
if (args.GetBoolArg("-avalanche", AVALANCHE_DEFAULT_ENABLED)) {
// Initialize Avalanche.
bilingual_str avalancheError;
node.avalanche = avalanche::Processor::MakeProcessor(
args, *node.chain, node.connman.get(), chainman, node.mempool.get(),
*node.scheduler, avalancheError);
if (!node.avalanche) {
InitError(avalancheError);
return false;
}
if (node.avalanche->isAvalancheServiceAvailable()) {
nLocalServices = ServiceFlags(nLocalServices | NODE_AVALANCHE);
}
}
PeerManager::Options peerman_opts{};
ApplyArgsManOptions(args, peerman_opts);
assert(!node.peerman);
node.peerman = PeerManager::make(*node.connman, *node.addrman,
node.banman.get(), chainman, *node.mempool,
node.avalanche.get(), peerman_opts);
RegisterValidationInterface(node.peerman.get());
// Encoded addresses using cashaddr instead of base58.
// We do this by default to avoid confusion with BTC addresses.
config.SetCashAddrEncoding(args.GetBoolArg("-usecashaddr", true));
// Step 8: load indexers
// If reindex-chainstate was specified, delay syncing indexes until
// ThreadImport has reindexed the chain
if (!fReindexChainState) {
g_indexes_ready_to_sync = true;
}
if (args.GetBoolArg("-txindex", DEFAULT_TXINDEX)) {
auto result{
WITH_LOCK(cs_main, return CheckLegacyTxindex(*Assert(
chainman.m_blockman.m_block_tree_db)))};
if (!result) {
return InitError(util::ErrorString(result));
}
g_txindex =
std::make_unique<TxIndex>(interfaces::MakeChain(node, Params()),
cache_sizes.tx_index, false, fReindex);
if (!g_txindex->Start()) {
return false;
}
}
for (const auto &filter_type : g_enabled_filter_types) {
InitBlockFilterIndex(
[&] { return interfaces::MakeChain(node, Params()); }, filter_type,
cache_sizes.filter_index, false, fReindex);
if (!GetBlockFilterIndex(filter_type)->Start()) {
return false;
}
}
if (args.GetBoolArg("-coinstatsindex", DEFAULT_COINSTATSINDEX)) {
g_coin_stats_index = std::make_unique<CoinStatsIndex>(
interfaces::MakeChain(node, Params()), /* cache size */ 0, false,
fReindex);
if (!g_coin_stats_index->Start()) {
return false;
}
}
#if ENABLE_CHRONIK
if (args.GetBoolArg("-chronik", DEFAULT_CHRONIK)) {
const bool fReindexChronik =
fReindex || args.GetBoolArg("-chronikreindex", false);
if (!chronik::Start(args, config, node, fReindexChronik)) {
return false;
}
}
#endif
// Step 9: load wallet
for (const auto &client : node.chain_clients) {
if (!client->load()) {
return false;
}
}
// Step 10: data directory maintenance
// if pruning, unset the service bit and perform the initial blockstore
// prune after any wallet rescanning has taken place.
if (chainman.m_blockman.IsPruneMode()) {
LogPrintf("Unsetting NODE_NETWORK on prune mode\n");
nLocalServices = ServiceFlags(nLocalServices & ~NODE_NETWORK);
if (!fReindex) {
LOCK(cs_main);
for (Chainstate *chainstate : chainman.GetAll()) {
uiInterface.InitMessage(_("Pruning blockstore...").translated);
chainstate->PruneAndFlush();
}
}
}
// Step 11: import blocks
if (!CheckDiskSpace(args.GetDataDirNet())) {
InitError(
strprintf(_("Error: Disk space is low for %s"),
fs::quoted(fs::PathToString(args.GetDataDirNet()))));
return false;
}
if (!CheckDiskSpace(args.GetBlocksDirPath())) {
InitError(
strprintf(_("Error: Disk space is low for %s"),
fs::quoted(fs::PathToString(args.GetBlocksDirPath()))));
return false;
}
// Either install a handler to notify us when genesis activates, or set
// fHaveGenesis directly.
// No locking, as this happens before any background thread is started.
boost::signals2::connection block_notify_genesis_wait_connection;
if (WITH_LOCK(chainman.GetMutex(),
return chainman.ActiveChain().Tip() == nullptr)) {
block_notify_genesis_wait_connection =
uiInterface.NotifyBlockTip_connect(
std::bind(BlockNotifyGenesisWait, std::placeholders::_2));
} else {
fHaveGenesis = true;
}
#if defined(HAVE_SYSTEM)
const std::string block_notify = args.GetArg("-blocknotify", "");
if (!block_notify.empty()) {
uiInterface.NotifyBlockTip_connect([block_notify](
SynchronizationState sync_state,
const CBlockIndex *pBlockIndex) {
if (sync_state != SynchronizationState::POST_INIT || !pBlockIndex) {
return;
}
std::string command = block_notify;
ReplaceAll(command, "%s", pBlockIndex->GetBlockHash().GetHex());
std::thread t(runCommand, command);
// thread runs free
t.detach();
});
}
#endif
std::vector<fs::path> vImportFiles;
for (const std::string &strFile : args.GetArgs("-loadblock")) {
vImportFiles.push_back(fs::PathFromString(strFile));
}
avalanche::Processor *const avalanche = node.avalanche.get();
chainman.m_load_block =
std::thread(&util::TraceThread, "loadblk", [=, &chainman, &args] {
ThreadImport(chainman, avalanche, vImportFiles,
ShouldPersistMempool(args) ? MempoolPath(args)
: fs::path{});
});
// Wait for genesis block to be processed
{
WAIT_LOCK(g_genesis_wait_mutex, lock);
// We previously could hang here if StartShutdown() is called prior to
// ThreadImport getting started, so instead we just wait on a timer to
// check ShutdownRequested() regularly.
while (!fHaveGenesis && !ShutdownRequested()) {
g_genesis_wait_cv.wait_for(lock, std::chrono::milliseconds(500));
}
block_notify_genesis_wait_connection.disconnect();
}
if (ShutdownRequested()) {
return false;
}
// Step 12: start node
int chain_active_height;
//// debug print
{
LOCK(cs_main);
LogPrintf("block tree size = %u\n", chainman.BlockIndex().size());
chain_active_height = chainman.ActiveChain().Height();
if (tip_info) {
tip_info->block_height = chain_active_height;
tip_info->block_time =
chainman.ActiveChain().Tip()
? chainman.ActiveChain().Tip()->GetBlockTime()
: chainman.GetParams().GenesisBlock().GetBlockTime();
tip_info->verification_progress = GuessVerificationProgress(
chainman.GetParams().TxData(), chainman.ActiveChain().Tip());
}
if (tip_info && chainman.m_best_header) {
tip_info->header_height = chainman.m_best_header->nHeight;
tip_info->header_time = chainman.m_best_header->GetBlockTime();
}
}
LogPrintf("nBestHeight = %d\n", chain_active_height);
if (node.peerman) {
node.peerman->SetBestHeight(chain_active_height);
}
// Map ports with UPnP or NAT-PMP.
StartMapPort(args.GetBoolArg("-upnp", DEFAULT_UPNP),
args.GetBoolArg("-natpmp", DEFAULT_NATPMP));
CConnman::Options connOptions;
connOptions.nLocalServices = nLocalServices;
connOptions.nMaxConnections = nMaxConnections;
connOptions.m_max_avalanche_outbound =
node.avalanche
? args.GetIntArg("-maxavalancheoutbound",
DEFAULT_MAX_AVALANCHE_OUTBOUND_CONNECTIONS)
: 0;
connOptions.m_max_outbound_full_relay = std::min(
MAX_OUTBOUND_FULL_RELAY_CONNECTIONS,
connOptions.nMaxConnections - connOptions.m_max_avalanche_outbound);
connOptions.m_max_outbound_block_relay = std::min(
MAX_BLOCK_RELAY_ONLY_CONNECTIONS,
connOptions.nMaxConnections - connOptions.m_max_avalanche_outbound -
connOptions.m_max_outbound_full_relay);
connOptions.nMaxAddnode = MAX_ADDNODE_CONNECTIONS;
connOptions.nMaxFeeler = MAX_FEELER_CONNECTIONS;
connOptions.uiInterface = &uiInterface;
connOptions.m_banman = node.banman.get();
connOptions.m_msgproc.push_back(node.peerman.get());
if (node.avalanche) {
connOptions.m_msgproc.push_back(node.avalanche.get());
}
connOptions.nSendBufferMaxSize =
1000 * args.GetIntArg("-maxsendbuffer", DEFAULT_MAXSENDBUFFER);
connOptions.nReceiveFloodSize =
1000 * args.GetIntArg("-maxreceivebuffer", DEFAULT_MAXRECEIVEBUFFER);
connOptions.m_added_nodes = args.GetArgs("-addnode");
connOptions.nMaxOutboundLimit =
1024 * 1024 *
args.GetIntArg("-maxuploadtarget", DEFAULT_MAX_UPLOAD_TARGET);
connOptions.m_peer_connect_timeout = peer_connect_timeout;
connOptions.whitelist_forcerelay =
args.GetBoolArg("-whitelistforcerelay", DEFAULT_WHITELISTFORCERELAY);
connOptions.whitelist_relay =
args.GetBoolArg("-whitelistrelay", DEFAULT_WHITELISTRELAY);
// Port to bind to if `-bind=addr` is provided without a `:port` suffix.
const uint16_t default_bind_port = static_cast<uint16_t>(
args.GetIntArg("-port", config.GetChainParams().GetDefaultPort()));
const auto BadPortWarning = [](const char *prefix, uint16_t port) {
return strprintf(_("%s request to listen on port %u. This port is "
"considered \"bad\" and "
"thus it is unlikely that any Bitcoin ABC peers "
"connect to it. See "
"doc/p2p-bad-ports.md for details and a full list."),
prefix, port);
};
for (const std::string &bind_arg : args.GetArgs("-bind")) {
CService bind_addr;
const size_t index = bind_arg.rfind('=');
if (index == std::string::npos) {
if (Lookup(bind_arg, bind_addr, default_bind_port,
/*fAllowLookup=*/false)) {
connOptions.vBinds.push_back(bind_addr);
if (IsBadPort(bind_addr.GetPort())) {
InitWarning(BadPortWarning("-bind", bind_addr.GetPort()));
}
continue;
}
} else {
const std::string network_type = bind_arg.substr(index + 1);
if (network_type == "onion") {
const std::string truncated_bind_arg =
bind_arg.substr(0, index);
if (Lookup(truncated_bind_arg, bind_addr,
BaseParams().OnionServiceTargetPort(), false)) {
connOptions.onion_binds.push_back(bind_addr);
continue;
}
}
}
return InitError(ResolveErrMsg("bind", bind_arg));
}
for (const std::string &strBind : args.GetArgs("-whitebind")) {
NetWhitebindPermissions whitebind;
bilingual_str error;
if (!NetWhitebindPermissions::TryParse(strBind, whitebind, error)) {
return InitError(error);
}
connOptions.vWhiteBinds.push_back(whitebind);
}
// If the user did not specify -bind= or -whitebind= then we bind
// on any address - 0.0.0.0 (IPv4) and :: (IPv6).
connOptions.bind_on_any =
args.GetArgs("-bind").empty() && args.GetArgs("-whitebind").empty();
// Emit a warning if a bad port is given to -port= but only if -bind and
// -whitebind are not given, because if they are, then -port= is ignored.
if (connOptions.bind_on_any && args.IsArgSet("-port")) {
const uint16_t port_arg = args.GetIntArg("-port", 0);
if (IsBadPort(port_arg)) {
InitWarning(BadPortWarning("-port", port_arg));
}
}
CService onion_service_target;
if (!connOptions.onion_binds.empty()) {
onion_service_target = connOptions.onion_binds.front();
} else if (!connOptions.vBinds.empty()) {
onion_service_target = connOptions.vBinds.front();
} else {
onion_service_target = DefaultOnionServiceTarget();
connOptions.onion_binds.push_back(onion_service_target);
}
if (args.GetBoolArg("-listenonion", DEFAULT_LISTEN_ONION)) {
if (connOptions.onion_binds.size() > 1) {
InitWarning(strprintf(
_("More than one onion bind address is provided. Using %s "
"for the automatically created Tor onion service."),
onion_service_target.ToStringIPPort()));
}
StartTorControl(onion_service_target);
}
if (connOptions.bind_on_any) {
// Only add all IP addresses of the machine if we would be listening on
// any address - 0.0.0.0 (IPv4) and :: (IPv6).
Discover();
}
for (const auto &net : args.GetArgs("-whitelist")) {
NetWhitelistPermissions subnet;
ConnectionDirection connection_direction;
bilingual_str error;
if (!NetWhitelistPermissions::TryParse(net, subnet,
connection_direction, error)) {
return InitError(error);
}
if (connection_direction & ConnectionDirection::In) {
connOptions.vWhitelistedRangeIncoming.push_back(subnet);
}
if (connection_direction & ConnectionDirection::Out) {
connOptions.vWhitelistedRangeOutgoing.push_back(subnet);
}
}
connOptions.vSeedNodes = args.GetArgs("-seednode");
// Initiate outbound connections unless connect=0
connOptions.m_use_addrman_outgoing = !args.IsArgSet("-connect");
if (!connOptions.m_use_addrman_outgoing) {
const auto connect = args.GetArgs("-connect");
if (connect.size() != 1 || connect[0] != "0") {
connOptions.m_specified_outgoing = connect;
}
}
const std::string &i2psam_arg = args.GetArg("-i2psam", "");
if (!i2psam_arg.empty()) {
CService addr;
if (!Lookup(i2psam_arg, addr, 7656, fNameLookup) || !addr.IsValid()) {
return InitError(strprintf(
_("Invalid -i2psam address or hostname: '%s'"), i2psam_arg));
}
SetReachable(NET_I2P, true);
SetProxy(NET_I2P, proxyType{addr});
} else {
SetReachable(NET_I2P, false);
}
connOptions.m_i2p_accept_incoming =
args.GetBoolArg("-i2pacceptincoming", true);
if (!node.connman->Start(*node.scheduler, connOptions)) {
return false;
}
// Step 13: finished
// At this point, the RPC is "started", but still in warmup, which means it
// cannot yet be called. Before we make it callable, we need to make sure
// that the RPC's view of the best block is valid and consistent with
// ChainstateManager's active tip.
//
// If we do not do this, RPC's view of the best block will be height=0 and
// hash=0x0. This will lead to erroroneous responses for things like
// waitforblockheight.
RPCNotifyBlockChange(
WITH_LOCK(chainman.GetMutex(), return chainman.ActiveTip()));
SetRPCWarmupFinished();
uiInterface.InitMessage(_("Done loading").translated);
for (const auto &client : node.chain_clients) {
client->start(*node.scheduler);
}
BanMan *banman = node.banman.get();
node.scheduler->scheduleEvery(
[banman] {
banman->DumpBanlist();
return true;
},
DUMP_BANS_INTERVAL);
// Start Avalanche's event loop.
if (node.avalanche) {
node.avalanche->startEventLoop(*node.scheduler);
}
if (node.peerman) {
node.peerman->StartScheduledTasks(*node.scheduler);
}
#if HAVE_SYSTEM
StartupNotify(args);
#endif
return true;
}
diff --git a/src/init.h b/src/init.h
index 88bd33f1f..a3bcf6828 100644
--- a/src/init.h
+++ b/src/init.h
@@ -1,93 +1,96 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2018 The Bitcoin Core developers
// Copyright (c) 2018-2019 The Bitcoin developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef BITCOIN_INIT_H
#define BITCOIN_INIT_H
#include <common/system.h>
#include <memory>
#include <string>
//! Default value for -daemon option
static constexpr bool DEFAULT_DAEMON = false;
//! Default value for -daemonwait option
static constexpr bool DEFAULT_DAEMONWAIT = false;
class ArgsManager;
class Config;
class CScheduler;
class CWallet;
class HTTPRPCRequestProcessor;
namespace interfaces {
struct BlockAndHeaderTipInfo;
}
+namespace kernel {
+struct Context;
+}
namespace node {
struct NodeContext;
} // namespace node
class RPCServer;
/** Interrupt threads */
void Interrupt(node::NodeContext &node);
void Shutdown(node::NodeContext &node);
//! Initialize the logging infrastructure
void InitLogging(const ArgsManager &args);
//! Parameter interaction: change current parameters depending on various rules
void InitParameterInteraction(ArgsManager &args);
/**
* Initialize bitcoin: Basic context setup.
* @note This can be done before daemonization.
* Do not call Shutdown() if this function fails.
* @pre Parameters should be parsed and config file should be read.
*/
bool AppInitBasicSetup(const ArgsManager &args);
/**
* Initialization: parameter interaction.
* @note This can be done before daemonization.
* Do not call Shutdown() if this function fails.
* @pre Parameters should be parsed and config file should be read,
* AppInitBasicSetup should have been called.
*/
bool AppInitParameterInteraction(Config &config, const ArgsManager &args);
/**
* Initialization sanity checks.
* @note This can be done before daemonization.
* Do not call Shutdown() if this function fails.
* @pre Parameters should be parsed and config file should be read,
* AppInitParameterInteraction should have been called.
*/
-bool AppInitSanityChecks();
+bool AppInitSanityChecks(const kernel::Context &kernel);
/**
* Lock bitcoin data directory.
* @note This should only be done after daemonization.
* Do not call Shutdown() if this function fails.
* @pre Parameters should be parsed and config file should be read,
* AppInitSanityChecks should have been called.
*/
bool AppInitLockDataDirectory();
/**
* Initialize node and wallet interface pointers. Has no prerequisites or side
* effects besides allocating memory.
*/
bool AppInitInterfaces(node::NodeContext &node);
/**
* Bitcoin main initialization.
* @note This should only be done after daemonization.
* @pre Parameters should be parsed and config file should be read,
* AppInitLockDataDirectory should have been called.
*/
bool AppInitMain(Config &config, RPCServer &rpcServer,
HTTPRPCRequestProcessor &httpRPCRequestProcessor,
node::NodeContext &node,
interfaces::BlockAndHeaderTipInfo *tip_info = nullptr);
/**
* Register all arguments with the ArgsManager
*/
void SetupServerArgs(node::NodeContext &node);
#endif // BITCOIN_INIT_H
diff --git a/src/init/common.cpp b/src/init/common.cpp
index b32e2050d..605d478be 100644
--- a/src/init/common.cpp
+++ b/src/init/common.cpp
@@ -1,240 +1,201 @@
// Copyright (c) 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.
#if defined(HAVE_CONFIG_H)
#include <config/bitcoin-config.h>
#endif
#include <clientversion.h>
#include <common/args.h>
-#include <crypto/sha256.h>
-#include <key.h>
#include <logging.h>
#include <node/miner.h>
#include <node/ui_interface.h>
-#include <pubkey.h>
-#include <random.h>
#include <util/fs_helpers.h>
#include <util/time.h>
#include <util/translation.h>
-#include <memory>
-
using node::DEFAULT_PRINTPRIORITY;
-static std::unique_ptr<ECCVerifyHandle> globalVerifyHandle;
-
namespace init {
-void SetGlobals() {
- std::string sha256_algo = SHA256AutoDetect();
- LogPrintf("Using the '%s' SHA256 implementation\n", sha256_algo);
- RandomInit();
- ECC_Start();
- globalVerifyHandle.reset(new ECCVerifyHandle());
-}
-
-void UnsetGlobals() {
- globalVerifyHandle.reset();
- ECC_Stop();
-}
-
-bool SanityChecks() {
- if (!ECC_InitSanityCheck()) {
- return InitError(Untranslated(
- "Elliptic curve cryptography sanity check failure. Aborting."));
- }
-
- if (!Random_SanityCheck()) {
- return InitError(Untranslated(
- "OS cryptographic RNG sanity check failure. Aborting."));
- }
-
- if (!ChronoSanityCheck()) {
- return InitError(Untranslated("Clock epoch mismatch. Aborting."));
- }
-
- return true;
-}
-
void AddLoggingArgs(ArgsManager &argsman) {
argsman.AddArg(
"-debuglogfile=<file>",
strprintf("Specify location of debug log file. Relative paths will be "
"prefixed by a net-specific datadir location. "
"(-nodebuglogfile to disable; default: %s)",
DEFAULT_DEBUGLOGFILE),
ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-debug=<category>",
"Output debugging information (default: -nodebug, supplying "
"<category> is optional). "
"If <category> is not supplied or if <category> = 1, output "
"all debugging information. <category> can be: " +
LogInstance().LogCategoriesString() +
". This option can be specified multiple times to "
"output multiple categories.",
ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST);
argsman.AddArg(
"-debugexclude=<category>",
strprintf(
"Exclude debugging information for a category. Can be used in "
"conjunction with -debug=1 to output debug logs for all categories "
"except the specified category. This option can be specified "
"multiple times to exclude multiple categories."),
ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST);
argsman.AddArg(
"-logips",
strprintf("Include IP addresses in debug output (default: %u)",
DEFAULT_LOGIPS),
ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST);
argsman.AddArg(
"-logtimestamps",
strprintf("Prepend debug output with timestamp (default: %u)",
DEFAULT_LOGTIMESTAMPS),
ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST);
#ifdef HAVE_THREAD_LOCAL
argsman.AddArg(
"-logthreadnames",
strprintf(
"Prepend debug output with name of the originating thread (only "
"available on platforms supporting thread_local) (default: %u)",
DEFAULT_LOGTHREADNAMES),
ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST);
#else
argsman.AddHiddenArgs({"-logthreadnames"});
#endif
argsman.AddArg(
"-logsourcelocations",
strprintf(
"Prepend debug output with name of the originating source location "
"(source file, line number and function name) (default: %u)",
DEFAULT_LOGSOURCELOCATIONS),
ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST);
argsman.AddArg(
"-logtimemicros",
strprintf("Add microsecond precision to debug timestamps (default: %u)",
DEFAULT_LOGTIMEMICROS),
ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY,
OptionsCategory::DEBUG_TEST);
argsman.AddArg("-printtoconsole",
"Send trace/debug info to console (default: 1 when no "
"-daemon. To disable logging to file, set -nodebuglogfile)",
ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST);
argsman.AddArg("-printpriority",
strprintf("Log transaction priority and fee per kB when "
"mining blocks (default: %d)",
DEFAULT_PRINTPRIORITY),
ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY,
OptionsCategory::DEBUG_TEST);
argsman.AddArg(
"-shrinkdebugfile",
"Shrink debug.log file on client startup (default: 1 when no -debug)",
ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST);
}
void SetLoggingOptions(const ArgsManager &args) {
LogInstance().m_print_to_file = !args.IsArgNegated("-debuglogfile");
LogInstance().m_file_path = AbsPathForConfigVal(
args, args.GetPathArg("-debuglogfile", DEFAULT_DEBUGLOGFILE));
LogInstance().m_print_to_console =
args.GetBoolArg("-printtoconsole", !args.GetBoolArg("-daemon", false));
LogInstance().m_log_timestamps =
args.GetBoolArg("-logtimestamps", DEFAULT_LOGTIMESTAMPS);
LogInstance().m_log_time_micros =
args.GetBoolArg("-logtimemicros", DEFAULT_LOGTIMEMICROS);
#ifdef HAVE_THREAD_LOCAL
LogInstance().m_log_threadnames =
args.GetBoolArg("-logthreadnames", DEFAULT_LOGTHREADNAMES);
#endif
LogInstance().m_log_sourcelocations =
args.GetBoolArg("-logsourcelocations", DEFAULT_LOGSOURCELOCATIONS);
fLogIPs = args.GetBoolArg("-logips", DEFAULT_LOGIPS);
}
void SetLoggingCategories(const ArgsManager &args) {
if (args.IsArgSet("-debug")) {
// Special-case: if -debug=0/-nodebug is set, turn off debugging
// messages
const std::vector<std::string> categories = args.GetArgs("-debug");
if (std::none_of(
categories.begin(), categories.end(),
[](std::string cat) { return cat == "0" || cat == "none"; })) {
for (const auto &cat : categories) {
if (!LogInstance().EnableCategory(cat)) {
InitWarning(
strprintf(_("Unsupported logging category %s=%s."),
"-debug", cat));
}
}
}
}
// Now remove the logging categories which were explicitly excluded
for (const std::string &cat : args.GetArgs("-debugexclude")) {
if (!LogInstance().DisableCategory(cat)) {
InitWarning(strprintf(_("Unsupported logging category %s=%s."),
"-debugexclude", cat));
}
}
}
bool StartLogging(const ArgsManager &args) {
BCLog::Logger &logger = LogInstance();
if (logger.m_print_to_file) {
if (args.GetBoolArg("-shrinkdebugfile",
logger.DefaultShrinkDebugFile())) {
// Do this first since it both loads a bunch of debug.log into
// memory, and because this needs to happen before any other
// debug.log printing.
logger.ShrinkDebugFile();
}
}
if (!logger.StartLogging()) {
return InitError(
strprintf(Untranslated("Could not open debug log file %s"),
fs::PathToString(logger.m_file_path)));
}
if (!logger.m_log_timestamps) {
LogPrintf("Startup time: %s\n", FormatISO8601DateTime(GetTime()));
}
LogPrintf("Default data directory %s\n",
fs::PathToString(GetDefaultDataDir()));
LogPrintf("Using data directory %s\n",
fs::PathToString(gArgs.GetDataDirNet()));
// Only log conf file usage message if conf file actually exists.
fs::path config_file_path = args.GetConfigFilePath();
if (fs::exists(config_file_path)) {
LogPrintf("Config file: %s\n", fs::PathToString(config_file_path));
} else if (args.IsArgSet("-conf")) {
// Warn if no conf file exists at path provided by user
InitWarning(
strprintf(_("The specified config file %s does not exist\n"),
fs::PathToString(config_file_path)));
} else {
// Not categorizing as "Warning" because it's the default behavior
LogPrintf("Config file: %s (not found, skipping)\n",
fs::PathToString(config_file_path));
}
// Log the config arguments to debug.log
args.LogArgs();
return true;
}
void LogPackageVersion() {
std::string version_string = FormatFullVersion();
#ifdef DEBUG
version_string += " (debug build)";
#else
version_string += " (release build)";
#endif
LogPrintf("%s version %s\n", CLIENT_NAME, version_string);
}
} // namespace init
diff --git a/src/init/common.h b/src/init/common.h
index 002e5c700..e1caf47da 100644
--- a/src/init/common.h
+++ b/src/init/common.h
@@ -1,28 +1,21 @@
// Copyright (c) 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.
//! @file
//! @brief Common init functions shared by bitcoin-node, bitcoin-wallet, etc.
#ifndef BITCOIN_INIT_COMMON_H
#define BITCOIN_INIT_COMMON_H
class ArgsManager;
namespace init {
-void SetGlobals();
-void UnsetGlobals();
-/**
- * Ensure a usable environment with all
- * necessary library support.
- */
-bool SanityChecks();
void AddLoggingArgs(ArgsManager &args);
void SetLoggingOptions(const ArgsManager &args);
void SetLoggingCategories(const ArgsManager &args);
bool StartLogging(const ArgsManager &args);
void LogPackageVersion();
} // namespace init
#endif // BITCOIN_INIT_COMMON_H
diff --git a/src/kernel/checks.cpp b/src/kernel/checks.cpp
new file mode 100644
index 000000000..3b0aab244
--- /dev/null
+++ b/src/kernel/checks.cpp
@@ -0,0 +1,29 @@
+// Copyright (c) 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 <kernel/checks.h>
+
+#include <key.h>
+#include <random.h>
+#include <util/time.h>
+
+namespace kernel {
+
+std::optional<SanityCheckError> SanityChecks(const Context &) {
+ if (!ECC_InitSanityCheck()) {
+ return SanityCheckError::ERROR_ECC;
+ }
+
+ if (!Random_SanityCheck()) {
+ return SanityCheckError::ERROR_RANDOM;
+ }
+
+ if (!ChronoSanityCheck()) {
+ return SanityCheckError::ERROR_CHRONO;
+ }
+
+ return std::nullopt;
+}
+
+} // namespace kernel
diff --git a/src/kernel/checks.h b/src/kernel/checks.h
new file mode 100644
index 000000000..d743061fc
--- /dev/null
+++ b/src/kernel/checks.h
@@ -0,0 +1,27 @@
+// Copyright (c) 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.
+
+#ifndef BITCOIN_KERNEL_CHECKS_H
+#define BITCOIN_KERNEL_CHECKS_H
+
+#include <optional>
+
+namespace kernel {
+
+struct Context;
+
+enum class SanityCheckError {
+ ERROR_ECC,
+ ERROR_RANDOM,
+ ERROR_CHRONO,
+};
+
+/**
+ * Ensure a usable environment with all necessary library support.
+ */
+std::optional<SanityCheckError> SanityChecks(const Context &);
+
+} // namespace kernel
+
+#endif // BITCOIN_KERNEL_CHECKS_H
diff --git a/src/kernel/context.cpp b/src/kernel/context.cpp
new file mode 100644
index 000000000..527e19574
--- /dev/null
+++ b/src/kernel/context.cpp
@@ -0,0 +1,30 @@
+// Copyright (c) 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 <kernel/context.h>
+
+#include <crypto/sha256.h>
+#include <key.h>
+#include <logging.h>
+#include <pubkey.h>
+#include <random.h>
+
+#include <string>
+
+namespace kernel {
+
+Context::Context() {
+ std::string sha256_algo = SHA256AutoDetect();
+ LogPrintf("Using the '%s' SHA256 implementation\n", sha256_algo);
+ RandomInit();
+ ECC_Start();
+ ecc_verify_handle.reset(new ECCVerifyHandle());
+}
+
+Context::~Context() {
+ ecc_verify_handle.reset();
+ ECC_Stop();
+}
+
+} // namespace kernel
diff --git a/src/kernel/context.h b/src/kernel/context.h
new file mode 100644
index 000000000..9746ef994
--- /dev/null
+++ b/src/kernel/context.h
@@ -0,0 +1,31 @@
+// Copyright (c) 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.
+
+#ifndef BITCOIN_KERNEL_CONTEXT_H
+#define BITCOIN_KERNEL_CONTEXT_H
+
+#include <memory>
+
+class ECCVerifyHandle;
+
+namespace kernel {
+//! Context struct holding the kernel library's logically global state, and
+//! passed to external libbitcoin_kernel functions which need access to this
+//! state. The kernel library API is a work in progress, so state organization
+//! and member list will evolve over time.
+//!
+//! State stored directly in this struct should be simple. More complex state
+//! should be stored to std::unique_ptr members pointing to opaque types.
+struct Context {
+ std::unique_ptr<ECCVerifyHandle> ecc_verify_handle;
+
+ //! Declare default constructor and destructor that are not inline, so code
+ //! instantiating the kernel::Context struct doesn't need to #include class
+ //! definitions for all the unique_ptr members.
+ Context();
+ ~Context();
+};
+} // namespace kernel
+
+#endif // BITCOIN_KERNEL_CONTEXT_H
diff --git a/src/node/context.cpp b/src/node/context.cpp
index e2d389b8e..7e7582fc8 100644
--- a/src/node/context.cpp
+++ b/src/node/context.cpp
@@ -1,21 +1,22 @@
// Copyright (c) 2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <node/context.h>
#include <addrman.h>
#include <avalanche/processor.h>
#include <banman.h>
#include <interfaces/chain.h>
+#include <kernel/context.h>
#include <net.h>
#include <net_processing.h>
#include <node/kernel_notifications.h>
#include <scheduler.h>
#include <txmempool.h>
#include <validation.h>
namespace node {
NodeContext::NodeContext() {}
NodeContext::~NodeContext() {}
} // namespace node
diff --git a/src/node/context.h b/src/node/context.h
index a7dc51105..dbecd6591 100644
--- a/src/node/context.h
+++ b/src/node/context.h
@@ -1,73 +1,77 @@
// Copyright (c) 2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef BITCOIN_NODE_CONTEXT_H
#define BITCOIN_NODE_CONTEXT_H
+#include <kernel/context.h>
+
#include <cassert>
#include <functional>
#include <memory>
#include <vector>
class ArgsManager;
class BanMan;
class AddrMan;
class CConnman;
class CScheduler;
class CTxMemPool;
class ChainstateManager;
class PeerManager;
namespace interfaces {
class Chain;
class ChainClient;
class WalletClient;
} // namespace interfaces
namespace avalanche {
class Processor;
} // namespace avalanche
namespace node {
class KernelNotifications;
//! NodeContext struct containing references to chain state and connection
//! state.
//!
//! This is used by init, rpc, and test code to pass object references around
//! without needing to declare the same variables and parameters repeatedly, or
//! to use globals. More variables could be added to this struct (particularly
//! references to validation objects) to eliminate use of globals
//! and make code more modular and testable. The struct isn't intended to have
//! any member functions. It should just be a collection of references that can
//! be used without pulling in unwanted dependencies or functionality.
struct NodeContext {
+ //! libbitcoin_kernel context
+ std::unique_ptr<kernel::Context> kernel;
std::unique_ptr<AddrMan> addrman;
std::unique_ptr<CConnman> connman;
std::unique_ptr<CTxMemPool> mempool;
std::unique_ptr<PeerManager> peerman;
std::unique_ptr<ChainstateManager> chainman;
std::unique_ptr<BanMan> banman;
// Currently a raw pointer because the memory is not managed by this struct
ArgsManager *args{nullptr};
std::unique_ptr<interfaces::Chain> chain;
//! List of all chain clients (wallet processes or other client) connected
//! to node.
std::vector<std::unique_ptr<interfaces::ChainClient>> chain_clients;
//! Reference to chain client that should used to load or create wallets
//! opened by the gui.
interfaces::WalletClient *wallet_client{nullptr};
std::unique_ptr<CScheduler> scheduler;
std::function<void()> rpc_interruption_point = [] {};
std::unique_ptr<KernelNotifications> notifications;
std::unique_ptr<avalanche::Processor> avalanche;
//! Declare default constructor and destructor that are not inline, so code
//! instantiating the NodeContext struct doesn't need to #include class
//! definitions for all the unique_ptr members.
NodeContext();
~NodeContext();
};
} // namespace node
#endif // BITCOIN_NODE_CONTEXT_H
diff --git a/src/node/interfaces.cpp b/src/node/interfaces.cpp
index 31cfe3def..bc65bef5b 100644
--- a/src/node/interfaces.cpp
+++ b/src/node/interfaces.cpp
@@ -1,803 +1,819 @@
// Copyright (c) 2018 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 <addrdb.h>
#include <banman.h>
#include <chain.h>
#include <chainparams.h>
#include <common/args.h>
#include <config.h>
#include <init.h>
#include <interfaces/chain.h>
#include <interfaces/handler.h>
#include <interfaces/node.h>
#include <interfaces/wallet.h>
#include <kernel/mempool_entry.h>
#include <mapport.h>
#include <net.h>
#include <net_processing.h>
#include <netaddress.h>
#include <netbase.h>
#include <node/blockstorage.h>
#include <node/coin.h>
#include <node/context.h>
#include <node/transaction.h>
#include <node/ui_interface.h>
#include <policy/settings.h>
#include <primitives/block.h>
#include <primitives/transaction.h>
#include <rpc/protocol.h>
#include <rpc/server.h>
#include <shutdown.h>
#include <sync.h>
#include <txmempool.h>
#include <uint256.h>
#include <util/check.h>
#include <util/translation.h>
#include <validation.h>
#include <validationinterface.h>
#include <warnings.h>
#if defined(HAVE_CONFIG_H)
#include <config/bitcoin-config.h>
#endif
#include <univalue.h>
#include <boost/signals2/signal.hpp>
#include <memory>
#include <utility>
class HTTPRPCRequestProcessor;
using interfaces::BlockTip;
using interfaces::Chain;
using interfaces::FoundBlock;
using interfaces::Handler;
using interfaces::MakeHandler;
using interfaces::Node;
using interfaces::WalletClient;
namespace node {
namespace {
class NodeImpl : public Node {
private:
ChainstateManager &chainman() { return *Assert(m_context->chainman); }
public:
explicit NodeImpl(NodeContext *context) { setContext(context); }
void initLogging() override { InitLogging(*Assert(m_context->args)); }
void initParameterInteraction() override {
InitParameterInteraction(*Assert(m_context->args));
}
bilingual_str getWarnings() override { return GetWarnings(true); }
bool baseInitialize(Config &config) override {
- return AppInitBasicSetup(gArgs) &&
- AppInitParameterInteraction(config, gArgs) &&
- AppInitSanityChecks() && AppInitLockDataDirectory() &&
- AppInitInterfaces(*m_context);
+ if (!AppInitBasicSetup(gArgs)) {
+ return false;
+ }
+ if (!AppInitParameterInteraction(config, gArgs)) {
+ return false;
+ }
+
+ m_context->kernel = std::make_unique<kernel::Context>();
+ if (!AppInitSanityChecks(*m_context->kernel)) {
+ return false;
+ }
+
+ if (!AppInitLockDataDirectory()) {
+ return false;
+ }
+ if (!AppInitInterfaces(*m_context)) {
+ return false;
+ }
+
+ return true;
}
bool appInitMain(Config &config, RPCServer &rpcServer,
HTTPRPCRequestProcessor &httpRPCRequestProcessor,
interfaces::BlockAndHeaderTipInfo *tip_info) override {
return AppInitMain(config, rpcServer, httpRPCRequestProcessor,
*m_context, tip_info);
}
void appShutdown() override {
Interrupt(*m_context);
Shutdown(*m_context);
}
void startShutdown() override {
StartShutdown();
// Stop RPC for clean shutdown if any of waitfor* commands is
// executed.
if (gArgs.GetBoolArg("-server", false)) {
InterruptRPC();
StopRPC();
}
}
bool shutdownRequested() override { return ShutdownRequested(); }
bool isPersistentSettingIgnored(const std::string &name) override {
bool ignored = false;
gArgs.LockSettings([&](util::Settings &settings) {
if (auto *options =
util::FindKey(settings.command_line_options, name)) {
ignored = !options->empty();
}
});
return ignored;
}
util::SettingsValue
getPersistentSetting(const std::string &name) override {
return gArgs.GetPersistentSetting(name);
}
void updateRwSetting(const std::string &name,
const util::SettingsValue &value) override {
gArgs.LockSettings([&](util::Settings &settings) {
if (value.isNull()) {
settings.rw_settings.erase(name);
} else {
settings.rw_settings[name] = value;
}
});
gArgs.WriteSettingsFile();
}
void forceSetting(const std::string &name,
const util::SettingsValue &value) override {
gArgs.LockSettings([&](util::Settings &settings) {
if (value.isNull()) {
settings.forced_settings.erase(name);
} else {
settings.forced_settings[name] = value;
}
});
}
void resetSettings() override {
gArgs.WriteSettingsFile(/*errors=*/nullptr, /*backup=*/true);
gArgs.LockSettings([&](util::Settings &settings) {
settings.rw_settings.clear();
});
gArgs.WriteSettingsFile();
}
void mapPort(bool use_upnp, bool use_natpmp) override {
StartMapPort(use_upnp, use_natpmp);
}
bool getProxy(Network net, proxyType &proxy_info) override {
return GetProxy(net, proxy_info);
}
size_t getNodeCount(ConnectionDirection flags) override {
return m_context->connman ? m_context->connman->GetNodeCount(flags)
: 0;
}
bool getNodesStats(NodesStats &stats) override {
stats.clear();
if (m_context->connman) {
std::vector<CNodeStats> stats_temp;
m_context->connman->GetNodeStats(stats_temp);
stats.reserve(stats_temp.size());
for (auto &node_stats_temp : stats_temp) {
stats.emplace_back(std::move(node_stats_temp), false,
CNodeStateStats());
}
// Try to retrieve the CNodeStateStats for each node.
if (m_context->peerman) {
TRY_LOCK(::cs_main, lockMain);
if (lockMain) {
for (auto &node_stats : stats) {
std::get<1>(node_stats) =
m_context->peerman->GetNodeStateStats(
std::get<0>(node_stats).nodeid,
std::get<2>(node_stats));
}
}
}
return true;
}
return false;
}
bool getBanned(banmap_t &banmap) override {
if (m_context->banman) {
m_context->banman->GetBanned(banmap);
return true;
}
return false;
}
bool ban(const CNetAddr &net_addr, int64_t ban_time_offset) override {
if (m_context->banman) {
m_context->banman->Ban(net_addr, ban_time_offset);
return true;
}
return false;
}
bool unban(const CSubNet &ip) override {
if (m_context->banman) {
m_context->banman->Unban(ip);
return true;
}
return false;
}
bool disconnectByAddress(const CNetAddr &net_addr) override {
if (m_context->connman) {
return m_context->connman->DisconnectNode(net_addr);
}
return false;
}
bool disconnectById(NodeId id) override {
if (m_context->connman) {
return m_context->connman->DisconnectNode(id);
}
return false;
}
int64_t getTotalBytesRecv() override {
return m_context->connman ? m_context->connman->GetTotalBytesRecv()
: 0;
}
int64_t getTotalBytesSent() override {
return m_context->connman ? m_context->connman->GetTotalBytesSent()
: 0;
}
size_t getMempoolSize() override {
return m_context->mempool ? m_context->mempool->size() : 0;
}
size_t getMempoolDynamicUsage() override {
return m_context->mempool ? m_context->mempool->DynamicMemoryUsage()
: 0;
}
bool getHeaderTip(int &height, int64_t &block_time) override {
LOCK(::cs_main);
auto best_header = chainman().m_best_header;
if (best_header) {
height = best_header->nHeight;
block_time = best_header->GetBlockTime();
return true;
}
return false;
}
int getNumBlocks() override {
LOCK(::cs_main);
return chainman().ActiveChain().Height();
}
BlockHash getBestBlockHash() override {
const CBlockIndex *tip =
WITH_LOCK(::cs_main, return chainman().ActiveTip());
return tip ? tip->GetBlockHash()
: chainman().GetParams().GenesisBlock().GetHash();
}
int64_t getLastBlockTime() override {
LOCK(::cs_main);
if (chainman().ActiveChain().Tip()) {
return chainman().ActiveChain().Tip()->GetBlockTime();
}
// Genesis block's time of current network
return chainman().GetParams().GenesisBlock().GetBlockTime();
}
double getVerificationProgress() override {
const CBlockIndex *tip;
{
LOCK(::cs_main);
tip = chainman().ActiveChain().Tip();
}
return GuessVerificationProgress(chainman().GetParams().TxData(),
tip);
}
bool isInitialBlockDownload() override {
return chainman().IsInitialBlockDownload();
}
bool isLoadingBlocks() override {
return chainman().m_blockman.LoadingBlocks();
}
void setNetworkActive(bool active) override {
if (m_context->connman) {
m_context->connman->SetNetworkActive(active);
}
}
bool getNetworkActive() override {
return m_context->connman && m_context->connman->GetNetworkActive();
}
CFeeRate getDustRelayFee() override {
if (!m_context->mempool) {
return CFeeRate{DUST_RELAY_TX_FEE};
}
return m_context->mempool->m_dust_relay_feerate;
}
UniValue executeRpc(const Config &config, const std::string &command,
const UniValue &params,
const std::string &uri) override {
JSONRPCRequest req;
req.context = m_context;
req.params = params;
req.strMethod = command;
req.URI = uri;
return ::tableRPC.execute(config, req);
}
std::vector<std::string> listRpcCommands() override {
return ::tableRPC.listCommands();
}
void rpcSetTimerInterfaceIfUnset(RPCTimerInterface *iface) override {
RPCSetTimerInterfaceIfUnset(iface);
}
void rpcUnsetTimerInterface(RPCTimerInterface *iface) override {
RPCUnsetTimerInterface(iface);
}
bool getUnspentOutput(const COutPoint &output, Coin &coin) override {
LOCK(::cs_main);
return chainman().ActiveChainstate().CoinsTip().GetCoin(output,
coin);
}
WalletClient &walletClient() override {
return *Assert(m_context->wallet_client);
}
std::unique_ptr<Handler> handleInitMessage(InitMessageFn fn) override {
return MakeHandler(::uiInterface.InitMessage_connect(fn));
}
std::unique_ptr<Handler> handleMessageBox(MessageBoxFn fn) override {
return MakeHandler(::uiInterface.ThreadSafeMessageBox_connect(fn));
}
std::unique_ptr<Handler> handleQuestion(QuestionFn fn) override {
return MakeHandler(::uiInterface.ThreadSafeQuestion_connect(fn));
}
std::unique_ptr<Handler>
handleShowProgress(ShowProgressFn fn) override {
return MakeHandler(::uiInterface.ShowProgress_connect(fn));
}
std::unique_ptr<Handler> handleNotifyNumConnectionsChanged(
NotifyNumConnectionsChangedFn fn) override {
return MakeHandler(
::uiInterface.NotifyNumConnectionsChanged_connect(fn));
}
std::unique_ptr<Handler> handleNotifyNetworkActiveChanged(
NotifyNetworkActiveChangedFn fn) override {
return MakeHandler(
::uiInterface.NotifyNetworkActiveChanged_connect(fn));
}
std::unique_ptr<Handler>
handleNotifyAlertChanged(NotifyAlertChangedFn fn) override {
return MakeHandler(::uiInterface.NotifyAlertChanged_connect(fn));
}
std::unique_ptr<Handler>
handleBannedListChanged(BannedListChangedFn fn) override {
return MakeHandler(::uiInterface.BannedListChanged_connect(fn));
}
std::unique_ptr<Handler>
handleNotifyBlockTip(NotifyBlockTipFn fn) override {
return MakeHandler(::uiInterface.NotifyBlockTip_connect(
[fn](SynchronizationState sync_state,
const CBlockIndex *block) {
fn(sync_state,
BlockTip{block->nHeight, block->GetBlockTime(),
block->GetBlockHash()},
GuessVerificationProgress(Params().TxData(), block));
}));
}
std::unique_ptr<Handler>
handleNotifyHeaderTip(NotifyHeaderTipFn fn) override {
/* verification progress is unused when a header was received */
return MakeHandler(::uiInterface.NotifyHeaderTip_connect(
[fn](SynchronizationState sync_state, int64_t height,
int64_t timestamp, bool presync) {
fn(sync_state,
BlockTip{int(height), timestamp, BlockHash{}}, presync);
}));
}
NodeContext *context() override { return m_context; }
void setContext(NodeContext *context) override { m_context = context; }
NodeContext *m_context{nullptr};
};
bool FillBlock(const CBlockIndex *index, const FoundBlock &block,
UniqueLock<RecursiveMutex> &lock, const CChain &active,
const BlockManager &blockman) {
if (!index) {
return false;
}
if (block.m_hash) {
*block.m_hash = index->GetBlockHash();
}
if (block.m_height) {
*block.m_height = index->nHeight;
}
if (block.m_time) {
*block.m_time = index->GetBlockTime();
}
if (block.m_max_time) {
*block.m_max_time = index->GetBlockTimeMax();
}
if (block.m_mtp_time) {
*block.m_mtp_time = index->GetMedianTimePast();
}
if (block.m_in_active_chain) {
*block.m_in_active_chain = active[index->nHeight] == index;
}
// TODO backport core#25494 with change from core#25717
if (block.m_next_block) {
FillBlock(active[index->nHeight] == index
? active[index->nHeight + 1]
: nullptr,
*block.m_next_block, lock, active, blockman);
}
if (block.m_data) {
REVERSE_LOCK(lock);
if (!blockman.ReadBlockFromDisk(*block.m_data, *index)) {
block.m_data->SetNull();
}
}
return true;
}
class NotificationsProxy : public CValidationInterface {
public:
explicit NotificationsProxy(
std::shared_ptr<Chain::Notifications> notifications)
: m_notifications(std::move(notifications)) {}
virtual ~NotificationsProxy() = default;
void TransactionAddedToMempool(const CTransactionRef &tx,
std::shared_ptr<const std::vector<Coin>>,
uint64_t mempool_sequence) override {
m_notifications->transactionAddedToMempool(tx, mempool_sequence);
}
void TransactionRemovedFromMempool(const CTransactionRef &tx,
MemPoolRemovalReason reason,
uint64_t mempool_sequence) override {
m_notifications->transactionRemovedFromMempool(tx, reason,
mempool_sequence);
}
void BlockConnected(const std::shared_ptr<const CBlock> &block,
const CBlockIndex *index) override {
m_notifications->blockConnected(*block, index->nHeight);
}
void BlockDisconnected(const std::shared_ptr<const CBlock> &block,
const CBlockIndex *index) override {
m_notifications->blockDisconnected(*block, index->nHeight);
}
void UpdatedBlockTip(const CBlockIndex *index,
const CBlockIndex *fork_index,
bool is_ibd) override {
m_notifications->updatedBlockTip();
}
void ChainStateFlushed(const CBlockLocator &locator) override {
m_notifications->chainStateFlushed(locator);
}
std::shared_ptr<Chain::Notifications> m_notifications;
};
class NotificationsHandlerImpl : public Handler {
public:
explicit NotificationsHandlerImpl(
std::shared_ptr<Chain::Notifications> notifications)
: m_proxy(std::make_shared<NotificationsProxy>(
std::move(notifications))) {
RegisterSharedValidationInterface(m_proxy);
}
~NotificationsHandlerImpl() override { disconnect(); }
void disconnect() override {
if (m_proxy) {
UnregisterSharedValidationInterface(m_proxy);
m_proxy.reset();
}
}
std::shared_ptr<NotificationsProxy> m_proxy;
};
class RpcHandlerImpl : public Handler {
public:
explicit RpcHandlerImpl(const CRPCCommand &command)
: m_command(command), m_wrapped_command(&command) {
m_command.actor = [this](const Config &config,
const JSONRPCRequest &request,
UniValue &result, bool last_handler) {
if (!m_wrapped_command) {
return false;
}
try {
return m_wrapped_command->actor(config, request, result,
last_handler);
} catch (const UniValue &e) {
// If this is not the last handler and a wallet not found
// exception was thrown, return false so the next handler
// can try to handle the request. Otherwise, reraise the
// exception.
if (!last_handler) {
const UniValue &code = e["code"];
if (code.isNum() &&
code.getInt<int>() == RPC_WALLET_NOT_FOUND) {
return false;
}
}
throw;
}
};
::tableRPC.appendCommand(m_command.name, &m_command);
}
void disconnect() final {
if (m_wrapped_command) {
m_wrapped_command = nullptr;
::tableRPC.removeCommand(m_command.name, &m_command);
}
}
~RpcHandlerImpl() override { disconnect(); }
CRPCCommand m_command;
const CRPCCommand *m_wrapped_command;
};
class ChainImpl : public Chain {
private:
ChainstateManager &chainman() { return *Assert(m_node.chainman); }
public:
explicit ChainImpl(NodeContext &node, const CChainParams &params)
: m_node(node), m_params(params) {}
std::optional<int> getHeight() override {
LOCK(::cs_main);
const CChain &active = Assert(m_node.chainman)->ActiveChain();
int height = active.Height();
if (height >= 0) {
return height;
}
return std::nullopt;
}
BlockHash getBlockHash(int height) override {
LOCK(::cs_main);
const CChain &active = Assert(m_node.chainman)->ActiveChain();
CBlockIndex *block = active[height];
assert(block);
return block->GetBlockHash();
}
bool haveBlockOnDisk(int height) override {
LOCK(cs_main);
const CChain &active = Assert(m_node.chainman)->ActiveChain();
CBlockIndex *block = active[height];
return block && (block->nStatus.hasData() != 0) && block->nTx > 0;
}
CBlockLocator getTipLocator() override {
LOCK(cs_main);
const CChain &active = Assert(m_node.chainman)->ActiveChain();
return active.GetLocator();
}
// TODO: backport core#25036 with changes from core#25717
std::optional<int>
findLocatorFork(const CBlockLocator &locator) override {
LOCK(cs_main);
const Chainstate &active =
Assert(m_node.chainman)->ActiveChainstate();
if (const CBlockIndex *fork =
active.FindForkInGlobalIndex(locator)) {
return fork->nHeight;
}
return std::nullopt;
}
bool findBlock(const BlockHash &hash,
const FoundBlock &block) override {
WAIT_LOCK(cs_main, lock);
const CChain &active = Assert(m_node.chainman)->ActiveChain();
return FillBlock(m_node.chainman->m_blockman.LookupBlockIndex(hash),
block, lock, active, chainman().m_blockman);
}
bool findFirstBlockWithTimeAndHeight(int64_t min_time, int min_height,
const FoundBlock &block) override {
WAIT_LOCK(cs_main, lock);
const CChain &active = Assert(m_node.chainman)->ActiveChain();
return FillBlock(active.FindEarliestAtLeast(min_time, min_height),
block, lock, active, chainman().m_blockman);
}
bool findAncestorByHeight(const BlockHash &block_hash,
int ancestor_height,
const FoundBlock &ancestor_out) override {
WAIT_LOCK(cs_main, lock);
const CChain &active = Assert(m_node.chainman)->ActiveChain();
if (const CBlockIndex *block =
m_node.chainman->m_blockman.LookupBlockIndex(block_hash)) {
if (const CBlockIndex *ancestor =
block->GetAncestor(ancestor_height)) {
return FillBlock(ancestor, ancestor_out, lock, active,
chainman().m_blockman);
}
}
return FillBlock(nullptr, ancestor_out, lock, active,
chainman().m_blockman);
}
bool findAncestorByHash(const BlockHash &block_hash,
const BlockHash &ancestor_hash,
const FoundBlock &ancestor_out) override {
WAIT_LOCK(cs_main, lock);
const CChain &active = Assert(m_node.chainman)->ActiveChain();
const CBlockIndex *block =
m_node.chainman->m_blockman.LookupBlockIndex(block_hash);
const CBlockIndex *ancestor =
m_node.chainman->m_blockman.LookupBlockIndex(ancestor_hash);
if (block && ancestor &&
block->GetAncestor(ancestor->nHeight) != ancestor) {
ancestor = nullptr;
}
return FillBlock(ancestor, ancestor_out, lock, active,
chainman().m_blockman);
}
bool findCommonAncestor(const BlockHash &block_hash1,
const BlockHash &block_hash2,
const FoundBlock &ancestor_out,
const FoundBlock &block1_out,
const FoundBlock &block2_out) override {
WAIT_LOCK(cs_main, lock);
const CChain &active = Assert(m_node.chainman)->ActiveChain();
const CBlockIndex *block1 =
m_node.chainman->m_blockman.LookupBlockIndex(block_hash1);
const CBlockIndex *block2 =
m_node.chainman->m_blockman.LookupBlockIndex(block_hash2);
const CBlockIndex *ancestor =
block1 && block2 ? LastCommonAncestor(block1, block2) : nullptr;
// Using & instead of && below to avoid short circuiting and leaving
// output uninitialized. Cast bool to int to avoid
// -Wbitwise-instead-of-logical compiler warnings.
return int{FillBlock(ancestor, ancestor_out, lock, active,
chainman().m_blockman)} &
int{FillBlock(block1, block1_out, lock, active,
chainman().m_blockman)} &
int{FillBlock(block2, block2_out, lock, active,
chainman().m_blockman)};
}
void findCoins(std::map<COutPoint, Coin> &coins) override {
return FindCoins(m_node, coins);
}
double guessVerificationProgress(const BlockHash &block_hash) override {
LOCK(cs_main);
return GuessVerificationProgress(
chainman().GetParams().TxData(),
chainman().m_blockman.LookupBlockIndex(block_hash));
}
bool hasBlocks(const BlockHash &block_hash, int min_height,
std::optional<int> max_height) override {
// hasBlocks returns true if all ancestors of block_hash in
// specified range have block data (are not pruned), false if any
// ancestors in specified range are missing data.
//
// For simplicity and robustness, min_height and max_height are only
// used to limit the range, and passing min_height that's too low or
// max_height that's too high will not crash or change the result.
LOCK(::cs_main);
if (const CBlockIndex *block =
chainman().m_blockman.LookupBlockIndex(block_hash)) {
if (max_height && block->nHeight >= *max_height) {
block = block->GetAncestor(*max_height);
}
for (; block->nStatus.hasData(); block = block->pprev) {
// Check pprev to not segfault if min_height is too low
if (block->nHeight <= min_height || !block->pprev) {
return true;
}
}
}
return false;
}
bool broadcastTransaction(const Config &config,
const CTransactionRef &tx,
const Amount &max_tx_fee, bool relay,
std::string &err_string) override {
const TransactionError err =
BroadcastTransaction(m_node, tx, err_string, max_tx_fee, relay,
/*wait_callback=*/false);
// Chain clients only care about failures to accept the tx to the
// mempool. Disregard non-mempool related failures. Note: this will
// need to be updated if BroadcastTransactions() is updated to
// return other non-mempool failures that Chain clients do not need
// to know about.
return err == TransactionError::OK;
}
CFeeRate estimateFee() const override {
if (!m_node.mempool) {
return {};
}
return m_node.mempool->estimateFee();
}
CFeeRate relayMinFee() override {
if (!m_node.mempool) {
return CFeeRate{DEFAULT_MIN_RELAY_TX_FEE_PER_KB};
}
return m_node.mempool->m_min_relay_feerate;
}
CFeeRate relayDustFee() override {
if (!m_node.mempool) {
return CFeeRate{DUST_RELAY_TX_FEE};
}
return m_node.mempool->m_dust_relay_feerate;
}
bool havePruned() override {
LOCK(cs_main);
return m_node.chainman->m_blockman.m_have_pruned;
}
bool isReadyToBroadcast() override {
return !chainman().m_blockman.LoadingBlocks() &&
!isInitialBlockDownload();
}
bool isInitialBlockDownload() override {
return chainman().IsInitialBlockDownload();
}
bool shutdownRequested() override { return ShutdownRequested(); }
void initMessage(const std::string &message) override {
::uiInterface.InitMessage(message);
}
void initWarning(const bilingual_str &message) override {
InitWarning(message);
}
void initError(const bilingual_str &message) override {
InitError(message);
}
void showProgress(const std::string &title, int progress,
bool resume_possible) override {
::uiInterface.ShowProgress(title, progress, resume_possible);
}
std::unique_ptr<Handler> handleNotifications(
std::shared_ptr<Notifications> notifications) override {
return std::make_unique<NotificationsHandlerImpl>(
std::move(notifications));
}
void
waitForNotificationsIfTipChanged(const BlockHash &old_tip) override {
if (!old_tip.IsNull()) {
LOCK(::cs_main);
const CChain &active = Assert(m_node.chainman)->ActiveChain();
if (old_tip == active.Tip()->GetBlockHash()) {
return;
}
}
SyncWithValidationInterfaceQueue();
}
std::unique_ptr<Handler>
handleRpc(const CRPCCommand &command) override {
return std::make_unique<RpcHandlerImpl>(command);
}
bool rpcEnableDeprecated(const std::string &method) override {
return IsDeprecatedRPCEnabled(gArgs, method);
}
void rpcRunLater(const std::string &name, std::function<void()> fn,
int64_t seconds) override {
RPCRunLater(name, std::move(fn), seconds);
}
int rpcSerializationFlags() override { return RPCSerializationFlags(); }
util::SettingsValue getSetting(const std::string &name) override {
return gArgs.GetSetting(name);
}
std::vector<util::SettingsValue>
getSettingsList(const std::string &name) override {
return gArgs.GetSettingsList(name);
}
util::SettingsValue getRwSetting(const std::string &name) override {
util::SettingsValue result;
gArgs.LockSettings([&](const util::Settings &settings) {
if (const util::SettingsValue *value =
util::FindKey(settings.rw_settings, name)) {
result = *value;
}
});
return result;
}
bool updateRwSetting(const std::string &name,
const util::SettingsValue &value,
bool write) override {
gArgs.LockSettings([&](util::Settings &settings) {
if (value.isNull()) {
settings.rw_settings.erase(name);
} else {
settings.rw_settings[name] = value;
}
});
return !write || gArgs.WriteSettingsFile();
}
void requestMempoolTransactions(Notifications &notifications) override {
if (!m_node.mempool) {
return;
}
LOCK2(::cs_main, m_node.mempool->cs);
for (const CTxMemPoolEntryRef &entry : m_node.mempool->mapTx) {
notifications.transactionAddedToMempool(entry->GetSharedTx(),
/*mempool_sequence=*/0);
}
}
bool hasAssumedValidChain() override {
return Assert(m_node.chainman)->IsSnapshotActive();
}
const CChainParams &params() const override { return m_params; }
NodeContext *context() override { return &m_node; }
NodeContext &m_node;
const CChainParams &m_params;
};
} // namespace
} // namespace node
namespace interfaces {
std::unique_ptr<Node> MakeNode(node::NodeContext *context) {
return std::make_unique<node::NodeImpl>(context);
}
std::unique_ptr<Chain> MakeChain(node::NodeContext &node,
const CChainParams &params) {
return std::make_unique<node::ChainImpl>(node, params);
}
} // namespace interfaces
diff --git a/src/test/util/setup_common.cpp b/src/test/util/setup_common.cpp
index 1ba3dcfb5..b990d3211 100644
--- a/src/test/util/setup_common.cpp
+++ b/src/test/util/setup_common.cpp
@@ -1,661 +1,660 @@
// Copyright (c) 2011-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <test/util/setup_common.h>
#include <kernel/mempool_entry.h>
#include <kernel/validation_cache_sizes.h>
#include <addrman.h>
#include <banman.h>
#include <chainparams.h>
#include <common/system.h>
#include <config.h>
#include <consensus/consensus.h>
#include <consensus/merkle.h>
#include <consensus/validation.h>
#include <crypto/sha256.h>
#include <init.h>
+#include <init/common.h>
#include <interfaces/chain.h>
#include <logging.h>
#include <mempool_args.h>
#include <net.h>
#include <net_processing.h>
#include <node/blockstorage.h>
#include <node/chainstate.h>
#include <node/chainstatemanager_args.h>
#include <node/context.h>
#include <node/kernel_notifications.h>
#include <node/miner.h>
#include <node/peerman_args.h>
#include <node/validation_cache_args.h>
#include <noui.h>
#include <pow/pow.h>
#include <random.h>
#include <rpc/blockchain.h>
#include <rpc/register.h>
#include <rpc/server.h>
#include <scheduler.h>
#include <script/script_error.h>
#include <script/scriptcache.h>
#include <script/sigcache.h>
#include <shutdown.h>
#include <streams.h>
#include <timedata.h>
#include <txdb.h>
#include <txmempool.h>
#include <util/strencodings.h>
#include <util/thread.h>
#include <util/threadnames.h>
#include <util/time.h>
#include <util/translation.h>
#include <util/vector.h>
#include <validation.h>
#include <validationinterface.h>
#include <walletinitinterface.h>
#include <test/util/mining.h>
#include <test/util/random.h>
#include <algorithm>
#include <functional>
#include <memory>
using kernel::ValidationCacheSizes;
using node::ApplyArgsManOptions;
using node::BlockAssembler;
using node::BlockManager;
using node::CalculateCacheSizes;
using node::fReindex;
using node::KernelNotifications;
using node::LoadChainstate;
using node::NodeContext;
using node::VerifyLoadedChainstate;
const std::function<std::string(const char *)> G_TRANSLATION_FUN = nullptr;
/**
* Random context to get unique temp data dirs. Separate from
* g_insecure_rand_ctx, which can be seeded from a const env var
*/
static FastRandomContext g_insecure_rand_ctx_temp_path;
std::ostream &operator<<(std::ostream &os, const uint256 &num) {
os << num.ToString();
return os;
}
std::ostream &operator<<(std::ostream &os, const ScriptError &err) {
os << ScriptErrorString(err);
return os;
}
std::vector<const char *> fixture_extra_args{};
BasicTestingSetup::BasicTestingSetup(
const std::string &chainName, const std::vector<const char *> &extra_args)
: m_path_root{fsbridge::GetTempDirectoryPath() /
"test_common_" PACKAGE_NAME /
g_insecure_rand_ctx_temp_path.rand256().ToString()},
m_args{} {
// clang-format off
std::vector<const char *> arguments = Cat(
{
"dummy",
"-printtoconsole=0",
"-logsourcelocations",
"-logtimemicros",
"-debug",
"-debugexclude=libevent",
"-debugexclude=leveldb",
},
extra_args);
// clang-format on
arguments = Cat(arguments, fixture_extra_args);
auto &config = const_cast<Config &>(GetConfig());
SetMockTime(0);
fs::create_directories(m_path_root);
m_args.ForceSetArg("-datadir", fs::PathToString(m_path_root));
gArgs.ForceSetArg("-datadir", fs::PathToString(m_path_root));
gArgs.ClearPathCache();
{
SetupServerArgs(m_node);
std::string error;
const bool success{m_node.args->ParseParameters(
arguments.size(), arguments.data(), error)};
assert(success);
assert(error.empty());
}
SelectParams(chainName);
SeedInsecureRand();
InitLogging(*m_node.args);
AppInitParameterInteraction(config, *m_node.args);
LogInstance().StartLogging();
- SHA256AutoDetect();
- ECC_Start();
+ m_node.kernel = std::make_unique<kernel::Context>();
SetupEnvironment();
SetupNetworking();
ValidationCacheSizes validation_cache_sizes{};
ApplyArgsManOptions(*m_node.args, validation_cache_sizes);
Assert(InitSignatureCache(validation_cache_sizes.signature_cache_bytes));
Assert(InitScriptExecutionCache(
validation_cache_sizes.script_execution_cache_bytes));
m_node.chain = interfaces::MakeChain(m_node, config.GetChainParams());
g_wallet_init_interface.Construct(m_node);
static bool noui_connected = false;
if (!noui_connected) {
noui_connect();
noui_connected = true;
}
node::g_indexes_ready_to_sync = true;
}
BasicTestingSetup::~BasicTestingSetup() {
LogInstance().DisconnectTestLogger();
fs::remove_all(m_path_root);
gArgs.ClearArgs();
- ECC_Stop();
}
CTxMemPool::Options MemPoolOptionsForTest(const NodeContext &node) {
CTxMemPool::Options mempool_opts{
// Default to always checking mempool regardless of
// chainparams.DefaultConsistencyChecks for tests
.check_ratio = 1,
};
const auto err{ApplyArgsManOptions(
*node.args, ::GetConfig().GetChainParams(), mempool_opts)};
Assert(!err);
return mempool_opts;
}
ChainTestingSetup::ChainTestingSetup(
const std::string &chainName, const std::vector<const char *> &extra_args)
: BasicTestingSetup(chainName, extra_args) {
const Config &config = GetConfig();
// We have to run a scheduler thread to prevent ActivateBestChain
// from blocking due to queue overrun.
m_node.scheduler = std::make_unique<CScheduler>();
m_node.scheduler->m_service_thread =
std::thread(util::TraceThread, "scheduler",
[&] { m_node.scheduler->serviceQueue(); });
GetMainSignals().RegisterBackgroundSignalScheduler(*m_node.scheduler);
m_node.mempool =
std::make_unique<CTxMemPool>(MemPoolOptionsForTest(m_node));
m_cache_sizes = CalculateCacheSizes(m_args);
m_node.notifications = std::make_unique<KernelNotifications>();
ChainstateManager::Options chainman_opts{
.config = config,
.datadir = m_args.GetDataDirNet(),
.adjusted_time_callback = GetAdjustedTime,
.check_block_index = true,
.notifications = *m_node.notifications,
};
ApplyArgsManOptions(*m_node.args, chainman_opts);
const BlockManager::Options blockman_opts{
.chainparams = chainman_opts.config.GetChainParams(),
.blocks_dir = m_args.GetBlocksDirPath(),
};
m_node.chainman =
std::make_unique<ChainstateManager>(chainman_opts, blockman_opts);
m_node.chainman->m_blockman.m_block_tree_db =
std::make_unique<CBlockTreeDB>(DBParams{
.path = m_args.GetDataDirNet() / "blocks" / "index",
.cache_bytes = static_cast<size_t>(m_cache_sizes.block_tree_db),
.memory_only = true});
// Call Upgrade on the block database so that the version field is set,
// else LoadBlockIndexGuts will fail (see D8319).
m_node.chainman->m_blockman.m_block_tree_db->Upgrade();
constexpr int script_check_threads = 2;
StartScriptCheckWorkerThreads(script_check_threads);
}
ChainTestingSetup::~ChainTestingSetup() {
if (m_node.scheduler) {
m_node.scheduler->stop();
}
StopScriptCheckWorkerThreads();
GetMainSignals().FlushBackgroundCallbacks();
GetMainSignals().UnregisterBackgroundSignalScheduler();
m_node.connman.reset();
m_node.banman.reset();
m_node.addrman.reset();
m_node.args = nullptr;
m_node.mempool.reset();
m_node.scheduler.reset();
m_node.chainman.reset();
}
void TestingSetup::LoadVerifyActivateChainstate() {
auto &chainman{*Assert(m_node.chainman)};
node::ChainstateLoadOptions options;
options.mempool = Assert(m_node.mempool.get());
options.block_tree_db_in_memory = m_block_tree_db_in_memory;
options.coins_db_in_memory = m_coins_db_in_memory;
options.reindex = node::fReindex;
options.reindex_chainstate =
m_args.GetBoolArg("-reindex-chainstate", false);
options.prune = chainman.m_blockman.IsPruneMode();
options.check_blocks =
m_args.GetIntArg("-checkblocks", DEFAULT_CHECKBLOCKS);
options.check_level = m_args.GetIntArg("-checklevel", DEFAULT_CHECKLEVEL);
options.require_full_verification =
m_args.IsArgSet("-checkblocks") || m_args.IsArgSet("-checklevel");
auto [status, error] = LoadChainstate(chainman, m_cache_sizes, options);
assert(status == node::ChainstateLoadStatus::SUCCESS);
std::tie(status, error) = VerifyLoadedChainstate(chainman, options);
assert(status == node::ChainstateLoadStatus::SUCCESS);
BlockValidationState state;
if (!chainman.ActiveChainstate().ActivateBestChain(state)) {
throw std::runtime_error(
strprintf("ActivateBestChain failed. (%s)", state.ToString()));
}
}
TestingSetup::TestingSetup(const std::string &chainName,
const std::vector<const char *> &extra_args,
const bool coins_db_in_memory,
const bool block_tree_db_in_memory)
: ChainTestingSetup(chainName, extra_args),
m_coins_db_in_memory(coins_db_in_memory),
m_block_tree_db_in_memory(block_tree_db_in_memory) {
const Config &config = GetConfig();
// Ideally we'd move all the RPC tests to the functional testing framework
// instead of unit tests, but for now we need these here.
RPCServer rpcServer;
RegisterAllRPCCommands(config, rpcServer, tableRPC);
/**
* RPC does not come out of the warmup state on its own. Normally, this is
* handled in bitcoind's init path, but unit tests do not trigger this
* codepath, so we call it explicitly as part of setup.
*/
std::string rpcWarmupStatus;
if (RPCIsInWarmup(&rpcWarmupStatus)) {
SetRPCWarmupFinished();
}
LoadVerifyActivateChainstate();
m_node.addrman = std::make_unique<AddrMan>(
/* asmap= */ std::vector<bool>(), /* consistency_check_ratio= */ 0);
m_node.banman = std::make_unique<BanMan>(
m_args.GetDataDirBase() / "banlist.dat", config.GetChainParams(),
nullptr, DEFAULT_MISBEHAVING_BANTIME);
// Deterministic randomness for tests.
m_node.connman =
std::make_unique<CConnman>(config, 0x1337, 0x1337, *m_node.addrman);
PeerManager::Options peerman_opts;
ApplyArgsManOptions(*m_node.args, peerman_opts);
peerman_opts.deterministic_rng = true;
m_node.peerman = PeerManager::make(
*m_node.connman, *m_node.addrman, m_node.banman.get(), *m_node.chainman,
*m_node.mempool, /*avalanche=*/nullptr, peerman_opts);
{
CConnman::Options options;
options.m_msgproc.push_back(m_node.peerman.get());
m_node.connman->Init(options);
}
}
TestChain100Setup::TestChain100Setup(
const std::string &chain_name, const std::vector<const char *> &extra_args,
const bool coins_db_in_memory, const bool block_tree_db_in_memory)
: TestingSetup{CBaseChainParams::REGTEST, extra_args, coins_db_in_memory,
block_tree_db_in_memory} {
SetMockTime(1598887952);
constexpr std::array<uint8_t, 32> vchKey = {
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}};
coinbaseKey.Set(vchKey.begin(), vchKey.end(), true);
// Generate a 100-block chain:
this->mineBlocks(COINBASE_MATURITY);
{
LOCK(::cs_main);
assert(
m_node.chainman->ActiveTip()->GetBlockHash().ToString() ==
"5afde277a26b6f36aee8f61a1dbf755587e1c6be63e654a88abe2a1ff0fbfb05");
}
}
void TestChain100Setup::mineBlocks(int num_blocks) {
CScript scriptPubKey = CScript() << ToByteVector(coinbaseKey.GetPubKey())
<< OP_CHECKSIG;
for (int i = 0; i < num_blocks; i++) {
std::vector<CMutableTransaction> noTxns;
CBlock b = CreateAndProcessBlock(noTxns, scriptPubKey);
SetMockTime(GetTime() + 1);
m_coinbase_txns.push_back(b.vtx[0]);
}
}
CBlock
TestChain100Setup::CreateBlock(const std::vector<CMutableTransaction> &txns,
const CScript &scriptPubKey,
Chainstate &chainstate) {
const Config &config = GetConfig();
CBlock block = BlockAssembler{config, chainstate, nullptr}
.CreateNewBlock(scriptPubKey)
->block;
Assert(block.vtx.size() == 1);
for (const CMutableTransaction &tx : txns) {
block.vtx.push_back(MakeTransactionRef(tx));
}
// Order transactions by canonical order
std::sort(std::begin(block.vtx) + 1, std::end(block.vtx),
[](const std::shared_ptr<const CTransaction> &txa,
const std::shared_ptr<const CTransaction> &txb) -> bool {
return txa->GetId() < txb->GetId();
});
createCoinbaseAndMerkleRoot(&block,
WITH_LOCK(m_node.chainman->GetMutex(),
return m_node.chainman->ActiveTip()),
config.GetMaxBlockSize());
const Consensus::Params &params = config.GetChainParams().GetConsensus();
while (!CheckProofOfWork(block.GetHash(), block.nBits, params)) {
++block.nNonce;
}
return block;
}
CBlock TestChain100Setup::CreateAndProcessBlock(
const std::vector<CMutableTransaction> &txns, const CScript &scriptPubKey,
Chainstate *chainstate) {
if (!chainstate) {
chainstate = &Assert(m_node.chainman)->ActiveChainstate();
}
const CBlock block = this->CreateBlock(txns, scriptPubKey, *chainstate);
std::shared_ptr<const CBlock> shared_pblock =
std::make_shared<const CBlock>(block);
Assert(m_node.chainman)
->ProcessNewBlock(shared_pblock, true, true, nullptr);
return block;
}
CMutableTransaction TestChain100Setup::CreateValidMempoolTransaction(
CTransactionRef input_transaction, int input_vout, int input_height,
CKey input_signing_key, CScript output_destination, Amount output_amount,
bool submit) {
// Transaction we will submit to the mempool
CMutableTransaction mempool_txn;
// Create an input
COutPoint outpoint_to_spend(input_transaction->GetId(), input_vout);
CTxIn input(outpoint_to_spend);
mempool_txn.vin.push_back(input);
// Create an output
CTxOut output(output_amount, output_destination);
mempool_txn.vout.push_back(output);
// Sign the transaction
// - Add the signing key to a keystore
FillableSigningProvider keystore;
keystore.AddKey(input_signing_key);
// - Populate a CoinsViewCache with the unspent output
CCoinsView coins_view;
CCoinsViewCache coins_cache(&coins_view);
AddCoins(coins_cache, *input_transaction.get(), input_height);
// - Use GetCoin to properly populate utxo_to_spend,
Coin utxo_to_spend;
assert(coins_cache.GetCoin(outpoint_to_spend, utxo_to_spend));
// - Then add it to a map to pass in to SignTransaction
std::map<COutPoint, Coin> input_coins;
input_coins.insert({outpoint_to_spend, utxo_to_spend});
// - Default signature hashing type
SigHashType nHashType = SigHashType().withForkId();
std::map<int, std::string> input_errors;
assert(SignTransaction(mempool_txn, &keystore, input_coins, nHashType,
input_errors));
// If submit=true, add transaction to the mempool.
if (submit) {
LOCK(cs_main);
const MempoolAcceptResult result = m_node.chainman->ProcessTransaction(
MakeTransactionRef(mempool_txn));
assert(result.m_result_type == MempoolAcceptResult::ResultType::VALID);
}
return mempool_txn;
}
TestChain100Setup::~TestChain100Setup() {
SetMockTime(0);
}
std::vector<CTransactionRef>
TestChain100Setup::PopulateMempool(FastRandomContext &det_rand,
size_t num_transactions, bool submit) {
std::vector<CTransactionRef> mempool_transactions;
std::deque<std::pair<COutPoint, Amount>> unspent_prevouts;
std::transform(m_coinbase_txns.begin(), m_coinbase_txns.end(),
std::back_inserter(unspent_prevouts), [](const auto &tx) {
return std::make_pair(COutPoint(tx->GetId(), 0),
tx->vout[0].nValue);
});
while (num_transactions > 0 && !unspent_prevouts.empty()) {
// The number of inputs and outputs are random, between 1 and 24.
CMutableTransaction mtx = CMutableTransaction();
const size_t num_inputs = det_rand.randrange(24) + 1;
Amount total_in{Amount::zero()};
for (size_t n{0}; n < num_inputs; ++n) {
if (unspent_prevouts.empty()) {
break;
}
const auto &[prevout, amount] = unspent_prevouts.front();
mtx.vin.push_back(CTxIn(prevout, CScript()));
total_in += amount;
unspent_prevouts.pop_front();
}
const size_t num_outputs = det_rand.randrange(24) + 1;
// Approximately 1000sat "fee," equal output amounts.
const Amount amount_per_output =
(total_in - 1000 * SATOSHI) / int(num_outputs);
for (size_t n{0}; n < num_outputs; ++n) {
CScript spk = CScript() << CScriptNum(num_transactions + n);
mtx.vout.push_back(CTxOut(amount_per_output, spk));
}
CTransactionRef ptx = MakeTransactionRef(mtx);
mempool_transactions.push_back(ptx);
if (amount_per_output > 2000 * SATOSHI) {
// If the value is high enough to fund another transaction + fees,
// keep track of it so it can be used to build a more complex
// transaction graph. Insert randomly into unspent_prevouts for
// extra randomness in the resulting structures.
for (size_t n{0}; n < num_outputs; ++n) {
unspent_prevouts.push_back(std::make_pair(
COutPoint(ptx->GetId(), n), amount_per_output));
std::swap(unspent_prevouts.back(),
unspent_prevouts[det_rand.randrange(
unspent_prevouts.size())]);
}
}
if (submit) {
LOCK2(m_node.mempool->cs, cs_main);
LockPoints lp;
m_node.mempool->addUnchecked(
CTxMemPoolEntryRef::make(ptx, 1000 * SATOSHI, 0, 1, 4, lp));
}
--num_transactions;
}
return mempool_transactions;
}
void TestChain100Setup::MockMempoolMinFee(const CFeeRate &target_feerate) {
LOCK2(cs_main, m_node.mempool->cs);
// Transactions in the mempool will affect the new minimum feerate.
assert(m_node.mempool->size() == 0);
// The target feerate cannot be too low...
// ...otherwise the transaction's feerate will need to be negative.
assert(target_feerate > MEMPOOL_FULL_FEE_INCREMENT);
// ...otherwise this is not meaningful. The feerate policy uses the maximum
// of both feerates.
assert(target_feerate > m_node.mempool->m_min_relay_feerate);
// Manually create an invalid transaction. Manually set the fee in the
// CTxMemPoolEntry to achieve the exact target feerate.
CMutableTransaction mtx = CMutableTransaction();
mtx.vin.push_back(CTxIn{COutPoint{TxId{g_insecure_rand_ctx.rand256()}, 0}});
mtx.vout.push_back(CTxOut(
1 * COIN, GetScriptForDestination(ScriptHash(CScript() << OP_TRUE))));
const auto tx{MakeTransactionRef(mtx)};
// The new mempool min feerate is equal to the removed package's feerate +
// incremental feerate.
const auto tx_fee =
target_feerate.GetFee(GetVirtualTransactionSize(*tx)) -
MEMPOOL_FULL_FEE_INCREMENT.GetFee(GetVirtualTransactionSize(*tx));
TestMemPoolEntryHelper entryHelper;
auto entry =
entryHelper.Fee(tx_fee).Time(0).Height(1).SigChecks(1).FromTx(tx);
m_node.mempool->addUnchecked(std::move(entry));
m_node.mempool->TrimToSize(0);
assert(m_node.mempool->GetMinFee() == target_feerate);
}
CTxMemPoolEntryRef
TestMemPoolEntryHelper::FromTx(const CMutableTransaction &tx) const {
return FromTx(MakeTransactionRef(tx));
}
CTxMemPoolEntryRef
TestMemPoolEntryHelper::FromTx(const CTransactionRef &tx) const {
CTxMemPoolEntry ret(tx, nFee, nTime, nHeight, nSigChecks, LockPoints());
ret.SetEntryId(entryId);
return CTxMemPoolEntryRef::make(std::move(ret));
}
/**
* @returns a real block
* (0000000000013b8ab2cd513b0261a14096412195a72a0c4827d229dcc7e0f7af) with 9
* txs.
*/
CBlock getBlock13b8a() {
CBlock block;
CDataStream stream(
ParseHex(
"0100000090f0a9f110702f808219ebea1173056042a714bad51b916cb680000000"
"0000005275289558f51c9966699404ae2294730c3c9f9bda53523ce50e9b95e558"
"da2fdb261b4d4c86041b1ab1bf9309010000000100000000000000000000000000"
"00000000000000000000000000000000000000ffffffff07044c86041b0146ffff"
"ffff0100f2052a01000000434104e18f7afbe4721580e81e8414fc8c24d7cfacf2"
"54bb5c7b949450c3e997c2dc1242487a8169507b631eb3771f2b425483fb13102c"
"4eb5d858eef260fe70fbfae0ac00000000010000000196608ccbafa16abada9027"
"80da4dc35dafd7af05fa0da08cf833575f8cf9e836000000004a493046022100da"
"b24889213caf43ae6adc41cf1c9396c08240c199f5225acf45416330fd7dbd0221"
"00fe37900e0644bf574493a07fc5edba06dbc07c311b947520c2d514bc5725dcb4"
"01ffffffff0100f2052a010000001976a914f15d1921f52e4007b146dfa60f369e"
"d2fc393ce288ac000000000100000001fb766c1288458c2bafcfec81e48b24d98e"
"c706de6b8af7c4e3c29419bfacb56d000000008c493046022100f268ba165ce0ad"
"2e6d93f089cfcd3785de5c963bb5ea6b8c1b23f1ce3e517b9f022100da7c0f21ad"
"c6c401887f2bfd1922f11d76159cbc597fbd756a23dcbb00f4d7290141042b4e86"
"25a96127826915a5b109852636ad0da753c9e1d5606a50480cd0c40f1f8b8d8982"
"35e571fe9357d9ec842bc4bba1827daaf4de06d71844d0057707966affffffff02"
"80969800000000001976a9146963907531db72d0ed1a0cfb471ccb63923446f388"
"ac80d6e34c000000001976a914f0688ba1c0d1ce182c7af6741e02658c7d4dfcd3"
"88ac000000000100000002c40297f730dd7b5a99567eb8d27b78758f607507c522"
"92d02d4031895b52f2ff010000008b483045022100f7edfd4b0aac404e5bab4fd3"
"889e0c6c41aa8d0e6fa122316f68eddd0a65013902205b09cc8b2d56e1cd1f7f2f"
"afd60a129ed94504c4ac7bdc67b56fe67512658b3e014104732012cb962afa90d3"
"1b25d8fb0e32c94e513ab7a17805c14ca4c3423e18b4fb5d0e676841733cb83aba"
"f975845c9f6f2a8097b7d04f4908b18368d6fc2d68ecffffffffca5065ff9617cb"
"cba45eb23726df6498a9b9cafed4f54cbab9d227b0035ddefb000000008a473044"
"022068010362a13c7f9919fa832b2dee4e788f61f6f5d344a7c2a0da6ae7406056"
"58022006d1af525b9a14a35c003b78b72bd59738cd676f845d1ff3fc25049e0100"
"3614014104732012cb962afa90d31b25d8fb0e32c94e513ab7a17805c14ca4c342"
"3e18b4fb5d0e676841733cb83abaf975845c9f6f2a8097b7d04f4908b18368d6fc"
"2d68ecffffffff01001ec4110200000043410469ab4181eceb28985b9b4e895c13"
"fa5e68d85761b7eee311db5addef76fa8621865134a221bd01f28ec9999ee3e021"
"e60766e9d1f3458c115fb28650605f11c9ac000000000100000001cdaf2f758e91"
"c514655e2dc50633d1e4c84989f8aa90a0dbc883f0d23ed5c2fa010000008b4830"
"4502207ab51be6f12a1962ba0aaaf24a20e0b69b27a94fac5adf45aa7d2d18ffd9"
"236102210086ae728b370e5329eead9accd880d0cb070aea0c96255fae6c4f1ddc"
"ce1fd56e014104462e76fd4067b3a0aa42070082dcb0bf2f388b6495cf33d78990"
"4f07d0f55c40fbd4b82963c69b3dc31895d0c772c812b1d5fbcade15312ef1c0e8"
"ebbb12dcd4ffffffff02404b4c00000000001976a9142b6ba7c9d796b75eef7942"
"fc9288edd37c32f5c388ac002d3101000000001976a9141befba0cdc1ad5652937"
"1864d9f6cb042faa06b588ac000000000100000001b4a47603e71b61bc3326efd9"
"0111bf02d2f549b067f4c4a8fa183b57a0f800cb010000008a4730440220177c37"
"f9a505c3f1a1f0ce2da777c339bd8339ffa02c7cb41f0a5804f473c9230220585b"
"25a2ee80eb59292e52b987dad92acb0c64eced92ed9ee105ad153cdb12d0014104"
"43bd44f683467e549dae7d20d1d79cbdb6df985c6e9c029c8d0c6cb46cc1a4d3cf"
"7923c5021b27f7a0b562ada113bc85d5fda5a1b41e87fe6e8802817cf69996ffff"
"ffff0280651406000000001976a9145505614859643ab7b547cd7f1f5e7e2a1232"
"2d3788ac00aa0271000000001976a914ea4720a7a52fc166c55ff2298e07baf70a"
"e67e1b88ac00000000010000000586c62cd602d219bb60edb14a3e204de0705176"
"f9022fe49a538054fb14abb49e010000008c493046022100f2bc2aba2534becbdf"
"062eb993853a42bbbc282083d0daf9b4b585bd401aa8c9022100b1d7fd7ee0b956"
"00db8535bbf331b19eed8d961f7a8e54159c53675d5f69df8c014104462e76fd40"
"67b3a0aa42070082dcb0bf2f388b6495cf33d789904f07d0f55c40fbd4b82963c6"
"9b3dc31895d0c772c812b1d5fbcade15312ef1c0e8ebbb12dcd4ffffffff03ad0e"
"58ccdac3df9dc28a218bcf6f1997b0a93306faaa4b3a28ae83447b217901000000"
"8b483045022100be12b2937179da88599e27bb31c3525097a07cdb52422d165b3c"
"a2f2020ffcf702200971b51f853a53d644ebae9ec8f3512e442b1bcb6c315a5b49"
"1d119d10624c83014104462e76fd4067b3a0aa42070082dcb0bf2f388b6495cf33"
"d789904f07d0f55c40fbd4b82963c69b3dc31895d0c772c812b1d5fbcade15312e"
"f1c0e8ebbb12dcd4ffffffff2acfcab629bbc8685792603762c921580030ba144a"
"f553d271716a95089e107b010000008b483045022100fa579a840ac258871365dd"
"48cd7552f96c8eea69bd00d84f05b283a0dab311e102207e3c0ee9234814cfbb1b"
"659b83671618f45abc1326b9edcc77d552a4f2a805c0014104462e76fd4067b3a0"
"aa42070082dcb0bf2f388b6495cf33d789904f07d0f55c40fbd4b82963c69b3dc3"
"1895d0c772c812b1d5fbcade15312ef1c0e8ebbb12dcd4ffffffffdcdc6023bbc9"
"944a658ddc588e61eacb737ddf0a3cd24f113b5a8634c517fcd2000000008b4830"
"450221008d6df731df5d32267954bd7d2dda2302b74c6c2a6aa5c0ca64ecbabc1a"
"f03c75022010e55c571d65da7701ae2da1956c442df81bbf076cdbac25133f99d9"
"8a9ed34c014104462e76fd4067b3a0aa42070082dcb0bf2f388b6495cf33d78990"
"4f07d0f55c40fbd4b82963c69b3dc31895d0c772c812b1d5fbcade15312ef1c0e8"
"ebbb12dcd4ffffffffe15557cd5ce258f479dfd6dc6514edf6d7ed5b21fcfa4a03"
"8fd69f06b83ac76e010000008b483045022023b3e0ab071eb11de2eb1cc3a67261"
"b866f86bf6867d4558165f7c8c8aca2d86022100dc6e1f53a91de3efe8f6351285"
"0811f26284b62f850c70ca73ed5de8771fb451014104462e76fd4067b3a0aa4207"
"0082dcb0bf2f388b6495cf33d789904f07d0f55c40fbd4b82963c69b3dc31895d0"
"c772c812b1d5fbcade15312ef1c0e8ebbb12dcd4ffffffff01404b4c0000000000"
"1976a9142b6ba7c9d796b75eef7942fc9288edd37c32f5c388ac00000000010000"
"000166d7577163c932b4f9690ca6a80b6e4eb001f0a2fa9023df5595602aae96ed"
"8d000000008a4730440220262b42546302dfb654a229cefc86432b89628ff259dc"
"87edd1154535b16a67e102207b4634c020a97c3e7bbd0d4d19da6aa2269ad9dded"
"4026e896b213d73ca4b63f014104979b82d02226b3a4597523845754d44f13639e"
"3bf2df5e82c6aab2bdc79687368b01b1ab8b19875ae3c90d661a3d0a33161dab29"
"934edeb36aa01976be3baf8affffffff02404b4c00000000001976a9144854e695"
"a02af0aeacb823ccbc272134561e0a1688ac40420f00000000001976a914abee93"
"376d6b37b5c2940655a6fcaf1c8e74237988ac0000000001000000014e3f8ef2e9"
"1349a9059cb4f01e54ab2597c1387161d3da89919f7ea6acdbb371010000008c49"
"304602210081f3183471a5ca22307c0800226f3ef9c353069e0773ac76bb580654"
"d56aa523022100d4c56465bdc069060846f4fbf2f6b20520b2a80b08b168b31e66"
"ddb9c694e240014104976c79848e18251612f8940875b2b08d06e6dc73b9840e88"
"60c066b7e87432c477e9a59a453e71e6d76d5fe34058b800a098fc1740ce3012e8"
"fc8a00c96af966ffffffff02c0e1e400000000001976a9144134e75a6fcb604203"
"4aab5e18570cf1f844f54788ac404b4c00000000001976a9142b6ba7c9d796b75e"
"ef7942fc9288edd37c32f5c388ac00000000"),
SER_NETWORK, PROTOCOL_VERSION);
stream >> block;
return block;
}
DummyConfig::DummyConfig()
: chainParams(CreateChainParams(ArgsManager{}, CBaseChainParams::REGTEST)) {
}
DummyConfig::DummyConfig(std::string net)
: chainParams(CreateChainParams(ArgsManager{}, net)) {}
diff --git a/src/test/util/setup_common.h b/src/test/util/setup_common.h
index 19649513b..dfb944ad8 100644
--- a/src/test/util/setup_common.h
+++ b/src/test/util/setup_common.h
@@ -1,284 +1,284 @@
// Copyright (c) 2015-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef BITCOIN_TEST_UTIL_SETUP_COMMON_H
#define BITCOIN_TEST_UTIL_SETUP_COMMON_H
#include <blockindex.h>
#include <chainparamsbase.h>
#include <common/args.h>
#include <config.h>
#include <consensus/amount.h>
#include <key.h>
#include <node/caches.h>
#include <node/context.h>
#include <primitives/transaction.h>
#include <pubkey.h>
#include <stdexcept>
#include <txmempool.h>
#include <util/check.h>
#include <util/fs.h>
#include <util/string.h>
#include <util/vector.h>
#include <type_traits>
#include <vector>
class CFeeRate;
class Config;
class FastRandomContext;
// Enable BOOST_CHECK_EQUAL for enum class types
template <typename T>
std::ostream &operator<<(
typename std::enable_if<std::is_enum<T>::value, std::ostream>::type &stream,
const T &e) {
return stream << static_cast<typename std::underlying_type<T>::type>(e);
}
static constexpr Amount CENT(COIN / 100);
extern std::vector<const char *> fixture_extra_args;
/**
* Basic testing setup.
* This just configures logging, data dir and chain parameters.
*/
struct BasicTestingSetup {
- ECCVerifyHandle globalVerifyHandle;
+ // keep as first member to be destructed last
node::NodeContext m_node;
explicit BasicTestingSetup(
const std::string &chainName = CBaseChainParams::MAIN,
const std::vector<const char *> &extra_args = {});
~BasicTestingSetup();
const fs::path m_path_root;
ArgsManager m_args;
};
CTxMemPool::Options MemPoolOptionsForTest(const node::NodeContext &node);
/**
* Testing setup that performs all steps up until right before
* ChainstateManager gets initialized. Meant for testing ChainstateManager
* initialization behaviour.
*/
struct ChainTestingSetup : public BasicTestingSetup {
node::CacheSizes m_cache_sizes{};
explicit ChainTestingSetup(
const std::string &chainName = CBaseChainParams::MAIN,
const std::vector<const char *> &extra_args = {});
~ChainTestingSetup();
};
/**
* Testing setup that configures a complete environment.
*/
struct TestingSetup : public ChainTestingSetup {
bool m_coins_db_in_memory{true};
bool m_block_tree_db_in_memory{true};
void LoadVerifyActivateChainstate();
explicit TestingSetup(const std::string &chainName = CBaseChainParams::MAIN,
const std::vector<const char *> &extra_args = {},
const bool coins_db_in_memory = true,
const bool block_tree_db_in_memory = true);
};
/** Identical to TestingSetup, but chain set to regtest */
struct RegTestingSetup : public TestingSetup {
RegTestingSetup() : TestingSetup{CBaseChainParams::REGTEST} {}
};
class CBlock;
class Chainstate;
class CMutableTransaction;
class CScript;
/**
* Testing fixture that pre-creates a 100-block REGTEST-mode block chain
*/
struct TestChain100Setup : public TestingSetup {
TestChain100Setup(const std::string &chain_name = CBaseChainParams::REGTEST,
const std::vector<const char *> &extra_args = {},
const bool coins_db_in_memory = true,
const bool block_tree_db_in_memory = true);
/**
* Create a new block with just given transactions, coinbase paying to
* scriptPubKey, and try to add it to the current chain.
* If no chainstate is specified, default to the active.
*/
CBlock CreateAndProcessBlock(const std::vector<CMutableTransaction> &txns,
const CScript &scriptPubKey,
Chainstate *chainstate = nullptr);
/**
* Create a new block with just given transactions, coinbase paying to
* scriptPubKey.
*/
CBlock CreateBlock(const std::vector<CMutableTransaction> &txns,
const CScript &scriptPubKey, Chainstate &chainstate);
//! Mine a series of new blocks on the active chain.
void mineBlocks(int num_blocks);
/**
* Create a transaction and submit to the mempool.
*
* @param input_transaction The transaction to spend
* @param input_vout The vout to spend from the input_transaction
* @param input_height The height of the block that included the
* input_transaction
* @param input_signing_key The key to spend the input_transaction
* @param output_destination Where to send the output
* @param output_amount How much to send
* @param submit Whether or not to submit to mempool
*/
CMutableTransaction CreateValidMempoolTransaction(
CTransactionRef input_transaction, int input_vout, int input_height,
CKey input_signing_key, CScript output_destination,
Amount output_amount = COIN, bool submit = true);
~TestChain100Setup();
/**
* Create transactions spending from m_coinbase_txns. These transactions
* will only spend coins that exist in the current chain, but may be
* premature coinbase spends, have missing signatures, or violate some other
* consensus rules. They should only be used for testing mempool
* consistency. All transactions will have some random number of inputs and
* outputs (between 1 and 24). Transactions may or may not be dependent upon
* each other; if dependencies exit, every parent will always be somewhere
* in the list before the child so each transaction can be submitted in the
* same order they appear in the list.
* @param[in] submit When true, submit transactions to the mempool.
* When false, return them but don't submit them.
* @returns A vector of transactions that can be submitted to the mempool.
*/
std::vector<CTransactionRef> PopulateMempool(FastRandomContext &det_rand,
size_t num_transactions,
bool submit);
/**
* Mock the mempool minimum feerate by adding a transaction and calling
* TrimToSize(0), simulating the mempool "reaching capacity" and evicting by
* descendant feerate. Note that this clears the mempool, and the new
* minimum feerate will depend on the maximum feerate of transactions
* removed, so this must be called while the mempool is empty.
*
* @param target_feerate The new mempool minimum feerate after this
* function returns. Must be above
* max(incremental feerate, min relay feerate), or
* 1 sat/B with default settings.
*/
void MockMempoolMinFee(const CFeeRate &target_feerate);
// For convenience, coinbase transactions.
std::vector<CTransactionRef> m_coinbase_txns;
// private/public key needed to spend coinbase transactions.
CKey coinbaseKey;
};
/**
* Make a test setup that has disk access to the debug.log file disabled. Can
* be used in "hot loops", for example fuzzing or benchmarking.
*/
template <class T = const BasicTestingSetup>
std::unique_ptr<T>
MakeNoLogFileContext(const std::string &chain_name = CBaseChainParams::REGTEST,
const std::vector<const char *> &extra_args = {}) {
const std::vector<const char *> arguments = Cat(
{
"-nodebuglogfile",
"-nodebug",
},
extra_args);
return std::make_unique<T>(chain_name, arguments);
}
struct TestMemPoolEntryHelper {
// Default values
Amount nFee;
int64_t nTime;
unsigned int nHeight;
unsigned int nSigChecks;
uint64_t entryId = 0;
TestMemPoolEntryHelper() : nFee(), nTime(0), nHeight(1), nSigChecks(1) {}
CTxMemPoolEntryRef FromTx(const CMutableTransaction &tx) const;
CTxMemPoolEntryRef FromTx(const CTransactionRef &tx) const;
// Change the default value
TestMemPoolEntryHelper &Fee(Amount _fee) {
nFee = _fee;
return *this;
}
TestMemPoolEntryHelper &Time(int64_t _time) {
nTime = _time;
return *this;
}
TestMemPoolEntryHelper &Height(unsigned int _height) {
nHeight = _height;
return *this;
}
TestMemPoolEntryHelper &SigChecks(unsigned int _nSigChecks) {
nSigChecks = _nSigChecks;
return *this;
}
TestMemPoolEntryHelper &EntryId(uint64_t _entryId) {
entryId = _entryId;
return *this;
}
};
enum class ScriptError;
// define implicit conversions here so that these types may be used in
// BOOST_*_EQUAL
std::ostream &operator<<(std::ostream &os, const uint256 &num);
std::ostream &operator<<(std::ostream &os, const ScriptError &err);
CBlock getBlock13b8a();
/**
* BOOST_CHECK_EXCEPTION predicates to check the specific validation error.
* Use as
* BOOST_CHECK_EXCEPTION(code that throws, exception type, HasReason("foo"));
*/
class HasReason {
public:
explicit HasReason(const std::string &reason) : m_reason(reason) {}
bool operator()(const std::exception &e) const {
return std::string(e.what()).find(m_reason) != std::string::npos;
};
private:
const std::string m_reason;
};
// Dummy for subclassing in unittests
class DummyConfig : public Config {
public:
DummyConfig();
explicit DummyConfig(std::string net);
bool SetMaxBlockSize(uint64_t maxBlockSize) override { return false; }
uint64_t GetMaxBlockSize() const override { return 32'000'000; }
void SetChainParams(const CChainParams chainParamsIn) override {}
const CChainParams &GetChainParams() const override { return *chainParams; }
void SetCashAddrEncoding(bool) override {}
bool UseCashAddrEncoding() const override { return false; }
private:
std::unique_ptr<const CChainParams> chainParams;
};
#endif // BITCOIN_TEST_UTIL_SETUP_COMMON_H

File Metadata

Mime Type
text/x-diff
Expires
Wed, May 21, 18:03 (21 h, 29 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
5865646
Default Alt Text
(275 KB)

Event Timeline