From 67a262b47b7c0a2ac6f81399623d4347f2f697ba Mon Sep 17 00:00:00 2001 From: Russell Yanofsky Date: Tue, 5 Dec 2017 15:57:12 -0500 Subject: [PATCH 1/3] interface_ui: move from node to common library Allows InitError and InitWarning to be used by other executables beside bitcoind and bitcoin-node. --- contrib/devtools/check-deps.sh | 6 ------ src/CMakeLists.txt | 2 +- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/contrib/devtools/check-deps.sh b/contrib/devtools/check-deps.sh index 25e948647c0..4f902236677 100755 --- a/contrib/devtools/check-deps.sh +++ b/contrib/devtools/check-deps.sh @@ -44,12 +44,6 @@ declare -A SUPPRESS # init.cpp file currently calls Berkeley DB sanity check function on startup, so # there is an undocumented dependency of the node library on the wallet library. SUPPRESS["init.cpp.o bdb.cpp.o _ZN6wallet27BerkeleyDatabaseSanityCheckEv"]=1 -# init/common.cpp file calls InitError and InitWarning from interface_ui which -# is currently part of the node library. interface_ui should just be part of the -# common library instead, and is moved in -# https://github.com/bitcoin/bitcoin/issues/10102 -SUPPRESS["common.cpp.o interface_ui.cpp.o _Z11InitWarningRK13bilingual_str"]=1 -SUPPRESS["common.cpp.o interface_ui.cpp.o _Z9InitErrorRK13bilingual_str"]=1 usage() { echo "Usage: $(basename "${BASH_SOURCE[0]}") [BUILD_DIR]" diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 41577b2ad6d..4fc14c1cffb 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -161,6 +161,7 @@ add_library(bitcoin_common STATIC EXCLUDE_FROM_ALL net_types.cpp netaddress.cpp netbase.cpp + node/interface_ui.cpp outputtype.cpp policy/feerate.cpp policy/policy.cpp @@ -258,7 +259,6 @@ add_library(bitcoin_node STATIC EXCLUDE_FROM_ALL node/context.cpp node/database_args.cpp node/eviction.cpp - node/interface_ui.cpp node/interfaces.cpp node/kernel_notifications.cpp node/mempool_args.cpp From 8e31982d5a940f04a815ec235cb55c0dc2f870d7 Mon Sep 17 00:00:00 2001 From: Ryan Ofsky Date: Fri, 6 Sep 2024 10:35:44 -0400 Subject: [PATCH 2/3] multiprocess: Add bitcoin-mine test program See src/bitcoin-mine.cpp for usage information. Co-authored-by: Sjors Provoost --- CMakeLists.txt | 1 + contrib/devtools/gen-manpages.py | 1 + src/CMakeLists.txt | 11 +++ src/bitcoin-mine.cpp | 124 +++++++++++++++++++++++++++++++ src/init/bitcoin-mine.cpp | 29 ++++++++ src/interfaces/init.h | 3 + 6 files changed, 169 insertions(+) create mode 100644 src/bitcoin-mine.cpp create mode 100644 src/init/bitcoin-mine.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index de11a4d3e0c..a58918abfdf 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -647,6 +647,7 @@ message(" bitcoin-util ........................ ${BUILD_UTIL}") message(" bitcoin-wallet ...................... ${BUILD_WALLET_TOOL}") message(" bitcoin-chainstate (experimental) ... ${BUILD_UTIL_CHAINSTATE}") message(" libbitcoinkernel (experimental) ..... ${BUILD_KERNEL_LIB}") +message(" bitcoin-mine (experimental) ......... ${bitcoin_daemon_status}") message("Optional features:") message(" wallet support ...................... ${ENABLE_WALLET}") if(ENABLE_WALLET) diff --git a/contrib/devtools/gen-manpages.py b/contrib/devtools/gen-manpages.py index c7678817a99..0197a22f5e8 100755 --- a/contrib/devtools/gen-manpages.py +++ b/contrib/devtools/gen-manpages.py @@ -11,6 +11,7 @@ import argparse BINARIES = [ 'bin/bitcoind', 'bin/bitcoin-cli', +'bin/bitcoin-mine', 'bin/bitcoin-tx', 'bin/bitcoin-wallet', 'bin/bitcoin-util', diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 4fc14c1cffb..52f7babb9ea 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -353,6 +353,17 @@ if(WITH_MULTIPROCESS AND BUILD_DAEMON) $ ) install_binary_component(bitcoin-node) + + add_executable(bitcoin-mine + bitcoin-mine.cpp + init/bitcoin-mine.cpp + ) + target_link_libraries(bitcoin-mine + core_interface + bitcoin_common + bitcoin_ipc + ) + install_binary_component(bitcoin-mine) endif() if(WITH_MULTIPROCESS AND BUILD_TESTS) diff --git a/src/bitcoin-mine.cpp b/src/bitcoin-mine.cpp new file mode 100644 index 00000000000..f72cdf8f2e1 --- /dev/null +++ b/src/bitcoin-mine.cpp @@ -0,0 +1,124 @@ +// Copyright (c) 2025 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 // IWYU pragma: keep + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static const char* const HELP_USAGE{R"( +bitcoin-mine is a test program for interacting with bitcoin-node via IPC. + +Usage: + bitcoin-mine [options] +)"}; + +static const char* HELP_EXAMPLES{R"( +Examples: + # Start separate bitcoin-node that bitcoin-mine can connect to. + bitcoin-node -regtest -ipcbind=unix + + # Connect to bitcoin-node and print tip block hash. + bitcoin-mine -regtest + + # Run with debug output. + bitcoin-mine -regtest -debug +)"}; + +const TranslateFn G_TRANSLATION_FUN{nullptr}; + +static void AddArgs(ArgsManager& args) +{ + SetupHelpOptions(args); + SetupChainParamsBaseOptions(args); + args.AddArg("-version", "Print version and exit", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); + args.AddArg("-datadir=", "Specify data directory", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); + args.AddArg("-ipcconnect=
", "Connect to bitcoin-node process in the background to perform online operations. Valid
values are 'unix' to connect to the default socket, 'unix:' to connect to a socket at a nonstandard path. Default value: unix", ArgsManager::ALLOW_ANY, OptionsCategory::IPC); + init::AddLoggingArgs(args); +} + +MAIN_FUNCTION +{ + ArgsManager& args = gArgs; + AddArgs(args); + std::string error_message; + if (!args.ParseParameters(argc, argv, error_message)) { + tfm::format(std::cerr, "Error parsing command line arguments: %s\n", error_message); + return EXIT_FAILURE; + } + if (!args.ReadConfigFiles(error_message, true)) { + tfm::format(std::cerr, "Error reading config files: %s\n", error_message); + return EXIT_FAILURE; + } + if (HelpRequested(args) || args.IsArgSet("-version")) { + std::string output{strprintf("%s bitcoin-mine version", CLIENT_NAME) + " " + FormatFullVersion() + "\n"}; + if (args.IsArgSet("-version")) { + output += FormatParagraph(LicenseInfo()); + } else { + output += HELP_USAGE; + output += args.GetHelpMessage(); + output += HELP_EXAMPLES; + } + tfm::format(std::cout, "%s", output); + return EXIT_SUCCESS; + } + if (!CheckDataDirOption(args)) { + tfm::format(std::cerr, "Error: Specified data directory \"%s\" does not exist.\n", args.GetArg("-datadir", "")); + return EXIT_FAILURE; + } + SelectParams(args.GetChainType()); + + // Set logging options but override -printtoconsole default to depend on -debug rather than -daemon + init::SetLoggingOptions(args); + if (auto result{init::SetLoggingCategories(args)}; !result) { + tfm::format(std::cerr, "Error: %s\n", util::ErrorString(result).original); + return EXIT_FAILURE; + } + if (auto result{init::SetLoggingLevel(args)}; !result) { + tfm::format(std::cerr, "Error: %s\n", util::ErrorString(result).original); + return EXIT_FAILURE; + } + LogInstance().m_print_to_console = args.GetBoolArg("-printtoconsole", LogInstance().GetCategoryMask()); + if (!init::StartLogging(args)) { + tfm::format(std::cerr, "Error: StartLogging failed\n"); + return EXIT_FAILURE; + } + + // Connect to existing bitcoin-node process or spawn new one. + std::unique_ptr mine_init{interfaces::MakeMineInit(argc, argv)}; + assert(mine_init); + std::unique_ptr node_init; + try { + std::string address{args.GetArg("-ipcconnect", "unix")}; + node_init = mine_init->ipc()->connectAddress(address); + } catch (const std::exception& exception) { + tfm::format(std::cerr, "Error: %s\n", exception.what()); + tfm::format(std::cerr, "Probably bitcoin-node is not running or not listening on a unix socket. Can be started with:\n\n"); + tfm::format(std::cerr, " bitcoin-node -chain=%s -ipcbind=unix\n", args.GetChainTypeString()); + return EXIT_FAILURE; + } + assert(node_init); + tfm::format(std::cout, "Connected to bitcoin-node\n"); + std::unique_ptr mining{node_init->makeMining()}; + assert(mining); + + auto tip{mining->getTip()}; + if (tip) { + tfm::format(std::cout, "Tip hash is %s.\n", tip->hash.ToString()); + } else { + tfm::format(std::cout, "Tip hash is null.\n"); + } + + return EXIT_SUCCESS; +} diff --git a/src/init/bitcoin-mine.cpp b/src/init/bitcoin-mine.cpp new file mode 100644 index 00000000000..ba78f3f06dc --- /dev/null +++ b/src/init/bitcoin-mine.cpp @@ -0,0 +1,29 @@ +// Copyright (c) 2025 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include +#include + +namespace init { +namespace { +const char* EXE_NAME = "bitcoin-mine"; + +class BitcoinMineInit : public interfaces::Init +{ +public: + BitcoinMineInit(const char* arg0) : m_ipc(interfaces::MakeIpc(EXE_NAME, arg0, *this)) + { + } + interfaces::Ipc* ipc() override { return m_ipc.get(); } + std::unique_ptr m_ipc; +}; +} // namespace +} // namespace init + +namespace interfaces { +std::unique_ptr MakeMineInit(int argc, char* argv[]) +{ + return std::make_unique(argc > 0 ? argv[0] : ""); +} +} // namespace interfaces diff --git a/src/interfaces/init.h b/src/interfaces/init.h index b496ada05f4..3c7525d7cda 100644 --- a/src/interfaces/init.h +++ b/src/interfaces/init.h @@ -53,6 +53,9 @@ std::unique_ptr MakeWalletInit(int argc, char* argv[], int& exit_status); //! Return implementation of Init interface for the gui process. std::unique_ptr MakeGuiInit(int argc, char* argv[]); + +//! Return implementation of Init interface for the bitcoin-mine process. +std::unique_ptr MakeMineInit(int argc, char* argv[]); } // namespace interfaces #endif // BITCOIN_INTERFACES_INIT_H From fae300f159cd25a12abf4d5fbb93135cececd38d Mon Sep 17 00:00:00 2001 From: Ryan Ofsky Date: Tue, 26 Nov 2024 22:53:46 -0500 Subject: [PATCH 3/3] test: add interface_ipc_mining.py test calling bitcoin-mine Co-authored-by: Sjors Provoost --- test/CMakeLists.txt | 1 + test/config.ini.in | 1 + test/functional/interface_ipc_mining.py | 46 +++++++++++++++++++ .../test_framework/test_framework.py | 18 ++++++-- test/functional/test_runner.py | 1 + 5 files changed, 64 insertions(+), 3 deletions(-) create mode 100755 test/functional/interface_ipc_mining.py diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index f1d05371ee8..5b3bdfd161e 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -25,6 +25,7 @@ function(create_test_config) set_configure_variable(WITH_ZMQ ENABLE_ZMQ) set_configure_variable(ENABLE_EXTERNAL_SIGNER ENABLE_EXTERNAL_SIGNER) set_configure_variable(WITH_USDT ENABLE_USDT_TRACEPOINTS) + set_configure_variable(WITH_MULTIPROCESS WITH_MULTIPROCESS) configure_file(config.ini.in config.ini USE_SOURCE_PERMISSIONS @ONLY) endfunction() diff --git a/test/config.ini.in b/test/config.ini.in index cdd13a9d8c2..0a64462c193 100644 --- a/test/config.ini.in +++ b/test/config.ini.in @@ -25,3 +25,4 @@ RPCAUTH=@abs_top_srcdir@/share/rpcauth/rpcauth.py @ENABLE_ZMQ_TRUE@ENABLE_ZMQ=true @ENABLE_EXTERNAL_SIGNER_TRUE@ENABLE_EXTERNAL_SIGNER=true @ENABLE_USDT_TRACEPOINTS_TRUE@ENABLE_USDT_TRACEPOINTS=true +@WITH_MULTIPROCESS_TRUE@WITH_MULTIPROCESS=true diff --git a/test/functional/interface_ipc_mining.py b/test/functional/interface_ipc_mining.py new file mode 100755 index 00000000000..b1a42db2f80 --- /dev/null +++ b/test/functional/interface_ipc_mining.py @@ -0,0 +1,46 @@ +#!/usr/bin/env python3 +# Copyright (c) 2025 The Bitcoin Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. +"""Test bitcoin-mine""" + +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import ( + assert_equal, +) + +import subprocess +import tempfile + +class TestBitcoinMine(BitcoinTestFramework): + def set_test_params(self): + self.setup_clean_chain = True + self.num_nodes = 1 + + def skip_test_if_missing_module(self): + self.skip_if_no_multiprocess() + + def setup_nodes(self): + # Always run multiprocess binaries + self.binary_paths.bitcoind = self.binary_paths.bitcoin_node + + # Work around default CI path exceeding maximum socket path length. + # On Linux sun_path is 108 bytes, on macOS it's only 104. Includes + # null terminator. + socket_path = self.options.tmpdir + "/node0/regtest/node.sock" + if len(socket_path.encode('utf-8')) < 104: + self.extra_args = [["-ipcbind=unix"]] + self.mine_args = [] + else: + sock_path = tempfile.mktemp() + self.extra_args = [[f"-ipcbind=unix:{sock_path}"]] + self.mine_args = [f"-ipcconnect=unix:{sock_path}"] + super().setup_nodes() + + def run_test(self): + args = [self.binary_paths.bitcoin_mine, f"-datadir={self.nodes[0].datadir_path}"] + self.mine_args + result = subprocess.run(args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, check=True) + assert_equal(result.stdout, "Connected to bitcoin-node\nTip hash is 0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206.\n") + +if __name__ == '__main__': + TestBitcoinMine(__file__).main() diff --git a/test/functional/test_framework/test_framework.py b/test/functional/test_framework/test_framework.py index c4b10af16c7..a1a937baa07 100755 --- a/test/functional/test_framework/test_framework.py +++ b/test/functional/test_framework/test_framework.py @@ -293,13 +293,16 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass): "bitcoin-util": ("bitcoinutil", "BITCOINUTIL"), "bitcoin-wallet": ("bitcoinwallet", "BITCOINWALLET"), } - for binary, [attribute_name, env_variable_name] in binaries.items(): - default_filename = os.path.join( + def binary_path(binary): + return os.path.join( self.config["environment"]["BUILDDIR"], "bin", binary + self.config["environment"]["EXEEXT"], ) - setattr(paths, attribute_name, os.getenv(env_variable_name, default=default_filename)) + for binary, [attribute_name, env_variable_name] in binaries.items(): + setattr(paths, attribute_name, os.getenv(env_variable_name) or binary_path(binary)) + paths.bitcoin_mine = binary_path("bitcoin-mine") + paths.bitcoin_node = binary_path("bitcoin-node") return paths def get_binaries(self, bin_dir=None): @@ -1027,6 +1030,11 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass): if not self.is_cli_compiled(): raise SkipTest("bitcoin-cli has not been compiled.") + def skip_if_no_multiprocess(self): + """Skip the running test if multiprocess binaries are not compiled.""" + if not self.is_multiprocess_compiled(): + raise SkipTest("multiprocess binaries have not been compiled.") + def skip_if_no_previous_releases(self): """Skip the running test if previous releases are not available.""" if not self.has_previous_releases(): @@ -1085,5 +1093,9 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass): """Checks whether the wallet module was compiled with BDB support.""" return self.config["components"].getboolean("USE_BDB") + def is_multiprocess_compiled(self): + """Checks whether multiprocess binaries are compiled.""" + return self.config["components"].getboolean("WITH_MULTIPROCESS") + def has_blockfile(self, node, filenum: str): return (node.blocks_path/ f"blk{filenum}.dat").is_file() diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py index 4b8c9172b4c..68193323b7f 100755 --- a/test/functional/test_runner.py +++ b/test/functional/test_runner.py @@ -407,6 +407,7 @@ BASE_SCRIPTS = [ 'rpc_help.py', 'p2p_handshake.py', 'p2p_handshake.py --v2transport', + 'interface_ipc_mining.py', 'feature_dirsymlinks.py', 'feature_help.py', 'feature_shutdown.py',