From 5076d20fdb70a4bfafc4bdfe8293e347cb6bfa78 Mon Sep 17 00:00:00 2001 From: Ryan Ofsky Date: Thu, 13 Feb 2025 16:00:15 -0500 Subject: [PATCH 1/7] util: Add cross-platform ExecVp and GetExePath functions These functions are just meant to serve the needs of the bitcoin wrapper executable, and are intentionally not very general purpose so they can be simple. --- src/util/CMakeLists.txt | 1 + src/util/exec.cpp | 75 +++++++++++++++++++++++++++++++++++++++++ src/util/exec.h | 23 +++++++++++++ 3 files changed, 99 insertions(+) create mode 100644 src/util/exec.cpp create mode 100644 src/util/exec.h diff --git a/src/util/CMakeLists.txt b/src/util/CMakeLists.txt index 4999dbf13f0..0ae9a08eebb 100644 --- a/src/util/CMakeLists.txt +++ b/src/util/CMakeLists.txt @@ -9,6 +9,7 @@ add_library(bitcoin_util STATIC EXCLUDE_FROM_ALL bytevectorhash.cpp chaintype.cpp check.cpp + exec.cpp exception.cpp feefrac.cpp fs.cpp diff --git a/src/util/exec.cpp b/src/util/exec.cpp new file mode 100644 index 00000000000..0f13d1d4af3 --- /dev/null +++ b/src/util/exec.cpp @@ -0,0 +1,75 @@ +// 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 +#include + +#include +#include + +#ifdef WIN32 +#include +#include +#else +#include +#endif + +namespace util { +int ExecVp(const char* file, char* const argv[]) +{ +#ifndef WIN32 + return execvp(file, argv); +#else + std::vector escaped_args; + std::wstring_convert> converter; + for (char* const* arg_ptr{argv}; *arg_ptr; ++arg_ptr) { + subprocess::util::quote_argument(converter.from_bytes(*arg_ptr), escaped_args.emplace_back(), false); + } + + std::vector new_argv; + new_argv.reserve(escaped_args.size() + 1); + for (const auto& s : escaped_args) new_argv.push_back(s.c_str()); + new_argv.push_back(nullptr); + return _wexecvp(converter.from_bytes(file).c_str(), new_argv.data()); +#endif +} + +fs::path GetExePath(std::string_view argv0) +{ + // Try to figure out where executable is located. This does a simplified + // search that won't work perfectly on every platform and doesn't need to, + // as it is only currently being used in a convenience wrapper binary to try + // to prioritize locally built or installed executables over system + // executables. + const fs::path argv0_path{fs::PathFromString(std::string{argv0})}; + fs::path path{argv0_path}; + std::error_code ec; +#ifndef WIN32 + // If argv0 doesn't contain a path separator, it was invoked from the system + // PATH and can be searched for there. + if (!argv0_path.has_parent_path()) { + if (const char* path_env = std::getenv("PATH")) { + size_t start{0}, end{0}; + for (std::string_view paths{path_env}; end != std::string_view::npos; start = end + 1) { + end = paths.find(':', start); + fs::path candidate = fs::path(paths.substr(start, end - start)) / argv0_path; + if (fs::is_regular_file(candidate, ec)) { + path = candidate; + break; + } + } + } + } +#else + wchar_t module_path[MAX_PATH]; + if (GetModuleFileNameW(nullptr, module_path, MAX_PATH) > 0) { + path = fs::path{module_path}; + } +#endif + return path; +} + +} // namespace util diff --git a/src/util/exec.h b/src/util/exec.h new file mode 100644 index 00000000000..4c207265fa0 --- /dev/null +++ b/src/util/exec.h @@ -0,0 +1,23 @@ +// 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. + +#ifndef BITCOIN_UTIL_EXEC_H +#define BITCOIN_UTIL_EXEC_H + +#include + +#include + +namespace util { +//! Cross-platform wrapper for POSIX execvp function. +//! Arguments and return value are the same as for POSIX execvp, and the argv +//! array should consist of null terminated strings and be null terminated +//! itself, like the POSIX function. +int ExecVp(const char* file, char* const argv[]); +//! Return path to current executable assuming it was invoked with argv0. +//! If path could not be determined, returns an empty path. +fs::path GetExePath(std::string_view argv0); +} // namespace util + +#endif // BITCOIN_UTIL_EXEC_H From 9c8c68891b43053acfe7b8eb9d2e0d2bcfcb4e1e Mon Sep 17 00:00:00 2001 From: Ryan Ofsky Date: Tue, 1 Oct 2024 16:06:28 -0400 Subject: [PATCH 2/7] multiprocess: Add bitcoin wrapper executable Intended to make bitcoin command line features more discoverable and allow installing new multiprocess binaries in libexec/ instead of bin/ so they don't cause confusion. Idea and implementation of this were discussed in https://github.com/bitcoin/bitcoin/issues/30983 Co-authored-by: Sjors Provoost --- CMakeLists.txt | 3 + contrib/guix/security-check.py | 3 + src/CMakeLists.txt | 6 + src/bitcoin.cpp | 204 +++++++++++++++++++++++++++++++++ 4 files changed, 216 insertions(+) create mode 100644 src/bitcoin.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 69f23d44b16..720c768674d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -94,6 +94,7 @@ endif() #============================= include(CMakeDependentOption) # When adding a new option, end the with a full stop for consistency. +option(BUILD_BITCOIN_BIN "Build bitcoin executable." ON) option(BUILD_DAEMON "Build bitcoind executable." ON) option(BUILD_GUI "Build bitcoin-qt executable." OFF) option(BUILD_CLI "Build bitcoin-cli executable." ON) @@ -202,6 +203,7 @@ target_link_libraries(core_interface INTERFACE if(BUILD_FOR_FUZZING) message(WARNING "BUILD_FOR_FUZZING=ON will disable all other targets and force BUILD_FUZZ_BINARY=ON.") + set(BUILD_BITCOIN_BIN OFF) set(BUILD_DAEMON OFF) set(BUILD_CLI OFF) set(BUILD_TX OFF) @@ -643,6 +645,7 @@ message("\n") message("Configure summary") message("=================") message("Executables:") +message(" bitcoin ............................. ${BUILD_BITCOIN_BIN}") message(" bitcoind ............................ ${BUILD_DAEMON}") if(BUILD_DAEMON AND ENABLE_IPC) set(bitcoin_daemon_status ON) diff --git a/contrib/guix/security-check.py b/contrib/guix/security-check.py index b29b888101e..04185a2fae1 100755 --- a/contrib/guix/security-check.py +++ b/contrib/guix/security-check.py @@ -126,6 +126,9 @@ def check_ELF_FORTIFY(binary) -> bool: # bitcoin-util does not currently contain any fortified functions if 'Bitcoin Core bitcoin-util utility version ' in binary.strings: return True + # bitcoin wrapper does not currently contain any fortified functions + if '--monolithic' in binary.strings: + return True chk_funcs = set() diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index dac8872080a..40d5e8667e4 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -331,6 +331,12 @@ target_link_libraries(bitcoin_node $ ) +# Bitcoin wrapper executable that can call other executables. +if(BUILD_BITCOIN_BIN) + add_executable(bitcoin bitcoin.cpp) + target_link_libraries(bitcoin core_interface bitcoin_util) + install_binary_component(bitcoin) +endif() # Bitcoin Core bitcoind. if(BUILD_DAEMON) diff --git a/src/bitcoin.cpp b/src/bitcoin.cpp new file mode 100644 index 00000000000..65776513941 --- /dev/null +++ b/src/bitcoin.cpp @@ -0,0 +1,204 @@ +// 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 + +const TranslateFn G_TRANSLATION_FUN{nullptr}; + +static constexpr auto HELP_USAGE = R"(Usage: %s [OPTIONS] COMMAND... + +Options: + -m, --multiprocess Run multiprocess binaries bitcoin-node, bitcoin-gui. + -M, --monolithic Run monolithic binaries bitcoind, bitcoin-qt. (Default behavior) + -v, --version Show version information + -h, --help Show this help message + +Commands: + gui [ARGS] Start GUI, equivalent to running 'bitcoin-qt [ARGS]' or 'bitcoin-gui [ARGS]'. + node [ARGS] Start node, equivalent to running 'bitcoind [ARGS]' or 'bitcoin-node [ARGS]'. + rpc [ARGS] Call RPC method, equivalent to running 'bitcoin-cli -named [ARGS]'. + wallet [ARGS] Call wallet command, equivalent to running 'bitcoin-wallet [ARGS]'. + tx [ARGS] Manipulate hex-encoded transactions, equivalent to running 'bitcoin-tx [ARGS]'. + help [-a] Show this help message. Include -a or --all to show additional commands. +)"; + +static constexpr auto HELP_EXTRA = R"( +Additional less commonly used commands: + bench [ARGS] Run bench command, equivalent to running 'bench_bitcoin [ARGS]'. + chainstate [ARGS] Run bitcoin kernel chainstate util, equivalent to running 'bitcoin-chainstate [ARGS]'. + test [ARGS] Run unit tests, equivalent to running 'test_bitcoin [ARGS]'. + test-gui [ARGS] Run GUI unit tests, equivalent to running 'test_bitcoin-qt [ARGS]'. +)"; + +struct CommandLine { + bool use_multiprocess{false}; + bool show_version{false}; + bool show_help{false}; + bool show_help_all{false}; + std::string_view command; + std::vector args; +}; + +CommandLine ParseCommandLine(int argc, char* argv[]); +static void ExecCommand(const std::vector& args, std::string_view argv0); + +int main(int argc, char* argv[]) +{ + try { + CommandLine cmd{ParseCommandLine(argc, argv)}; + if (cmd.show_version) { + tfm::format(std::cout, "%s version %s\n%s", CLIENT_NAME, FormatFullVersion(), FormatParagraph(LicenseInfo())); + return EXIT_SUCCESS; + } + + std::vector args; + if (cmd.show_help || cmd.command.empty()) { + tfm::format(std::cout, HELP_USAGE, argv[0]); + if (cmd.show_help_all) tfm::format(std::cout, HELP_EXTRA); + return cmd.show_help ? EXIT_SUCCESS : EXIT_FAILURE; + } else if (cmd.command == "gui") { + args.emplace_back(cmd.use_multiprocess ? "bitcoin-gui" : "bitcoin-qt"); + } else if (cmd.command == "node") { + args.emplace_back(cmd.use_multiprocess ? "bitcoin-node" : "bitcoind"); + } else if (cmd.command == "rpc") { + args.emplace_back("bitcoin-cli"); + // Since "bitcoin rpc" is a new interface that doesn't need to be + // backward compatible, enable -named by default so it is convenient + // for callers to use a mix of named and unnamed parameters. Callers + // can override this by specifying -nonamed, but should not need to + // unless they are passing string values containing '=' characters + // as unnamed parameters. + args.emplace_back("-named"); + } else if (cmd.command == "wallet") { + args.emplace_back("bitcoin-wallet"); + } else if (cmd.command == "tx") { + args.emplace_back("bitcoin-tx"); + } else if (cmd.command == "bench") { + args.emplace_back("bench_bitcoin"); + } else if (cmd.command == "chainstate") { + args.emplace_back("bitcoin-chainstate"); + } else if (cmd.command == "test") { + args.emplace_back("test_bitcoin"); + } else if (cmd.command == "test-gui") { + args.emplace_back("test_bitcoin-qt"); + } else if (cmd.command == "util") { + args.emplace_back("bitcoin-util"); + } else { + throw std::runtime_error(strprintf("Unrecognized command: '%s'", cmd.command)); + } + if (!args.empty()) { + args.insert(args.end(), cmd.args.begin(), cmd.args.end()); + ExecCommand(args, argv[0]); + } + } catch (const std::exception& e) { + tfm::format(std::cerr, "Error: %s\nTry '%s --help' for more information.\n", e.what(), argv[0]); + return EXIT_FAILURE; + } + return EXIT_SUCCESS; +} + +CommandLine ParseCommandLine(int argc, char* argv[]) +{ + CommandLine cmd; + cmd.args.reserve(argc); + for (int i = 1; i < argc; ++i) { + std::string_view arg = argv[i]; + if (!cmd.command.empty()) { + cmd.args.emplace_back(argv[i]); + } else if (arg == "-m" || arg == "--multiprocess") { + cmd.use_multiprocess = true; + } else if (arg == "-M" || arg == "--monolithic") { + cmd.use_multiprocess = false; + } else if (arg == "-v" || arg == "--version") { + cmd.show_version = true; + } else if (arg == "-h" || arg == "--help" || arg == "help") { + cmd.show_help = true; + } else if (cmd.show_help && (arg == "-a" || arg == "--all")) { + cmd.show_help_all = true; + } else if (arg.starts_with("-")) { + throw std::runtime_error(strprintf("Unknown option: %s", arg)); + } else if (!arg.empty()) { + cmd.command = arg; + } + } + return cmd; +} + +//! Execute the specified bitcoind, bitcoin-qt or other command line in `args` +//! using src, bin and libexec directory paths relative to this executable, where +//! the path to this executable is specified in `wrapper_argv0`. +//! +//! @param args Command line arguments to execute, where first argument should +//! be a relative path to a bitcoind, bitcoin-qt or other executable +//! that will be located on the PATH or relative to wrapper_argv0. +//! +//! @param wrapper_argv0 String containing first command line argument passed to +//! main() to run the current executable. This is used to +//! help determine the path to the current executable and +//! how to look for new executables. +// +//! @note This function doesn't currently print anything but can be debugged +//! from the command line using strace or dtrace like: +//! +//! strace -e trace=execve -s 10000 build/bin/bitcoin ... +//! dtrace -n 'proc:::exec-success /pid == $target/ { trace(curpsinfo->pr_psargs); }' -c ... +static void ExecCommand(const std::vector& args, std::string_view wrapper_argv0) +{ + // Construct argument string for execvp + std::vector exec_args{args}; + exec_args.emplace_back(nullptr); + + // Try to call ExecVp with given exe path. + auto try_exec = [&](fs::path exe_path, bool allow_notfound = true) { + std::string exe_path_str{fs::PathToString(exe_path)}; + exec_args[0] = exe_path_str.c_str(); + if (util::ExecVp(exec_args[0], (char*const*)exec_args.data()) == -1) { + if (allow_notfound && errno == ENOENT) return false; + throw std::system_error(errno, std::system_category(), strprintf("execvp failed to execute '%s'", exec_args[0])); + } + throw std::runtime_error("execvp returned unexpectedly"); + }; + + // Get the wrapper executable path. + const fs::path wrapper_path{util::GetExePath(wrapper_argv0)}; + + // Try to resolve any symlinks and figure out the directory containing the wrapper executable. + std::error_code ec; + fs::path wrapper_dir{fs::weakly_canonical(wrapper_path, ec)}; + if (wrapper_dir.empty()) wrapper_dir = wrapper_path; // Restore previous path if weakly_canonical failed. + wrapper_dir = wrapper_dir.parent_path(); + + // Get path of the executable to be invoked. + const fs::path arg0{fs::PathFromString(args[0])}; + + // Decide whether to fall back to the operating system to search for the + // specified executable. Avoid doing this if it looks like the wrapper + // executable was invoked by path, rather than by search, to avoid + // unintentionally launching system executables in a local build. + // (https://github.com/bitcoin/bitcoin/pull/31375#discussion_r1861814807) + const bool fallback_os_search{!fs::PathFromString(std::string{wrapper_argv0}).has_parent_path()}; + + // If wrapper is installed in a bin/ directory, look for target executable + // in libexec/ + (wrapper_dir.filename() == "bin" && try_exec(fs::path{wrapper_dir.parent_path()} / "libexec" / arg0.filename())) || +#ifdef WIN32 + // Otherwise check the "daemon" subdirectory in a windows install. + (!wrapper_dir.empty() && try_exec(wrapper_dir / "daemon" / arg0.filename())) || +#endif + // Otherwise look for target executable next to current wrapper + (!wrapper_dir.empty() && try_exec(wrapper_dir / arg0.filename(), fallback_os_search)) || + // Otherwise just look on the system path. + (fallback_os_search && try_exec(arg0.filename(), false)); +} From 29bdd743bb843f8b8ed2e426b6df36e9d7e54215 Mon Sep 17 00:00:00 2001 From: Ryan Ofsky Date: Tue, 26 Nov 2024 11:51:41 -0500 Subject: [PATCH 3/7] test: Support BITCOIN_CMD environment variable Support new BITCOIN_CMD environment variable in functional test to be able to test the new bitcoin wrapper executable and run other commands through it instead of calling them directly. Co-authored-by: Sjors Provoost --- .../test_framework/test_framework.py | 32 ++++++++++++------- test/functional/test_framework/test_node.py | 2 +- 2 files changed, 21 insertions(+), 13 deletions(-) diff --git a/test/functional/test_framework/test_framework.py b/test/functional/test_framework/test_framework.py index 75a0cb6f112..97a18974409 100755 --- a/test/functional/test_framework/test_framework.py +++ b/test/functional/test_framework/test_framework.py @@ -13,6 +13,7 @@ import platform import pdb import random import re +import shlex import shutil import subprocess import sys @@ -72,33 +73,37 @@ class Binaries: self.paths = paths self.bin_dir = bin_dir - def daemon_argv(self): + def node_argv(self): "Return argv array that should be used to invoke bitcoind" - return self._argv(self.paths.bitcoind) + return self._argv("node", self.paths.bitcoind) def rpc_argv(self): "Return argv array that should be used to invoke bitcoin-cli" - return self._argv(self.paths.bitcoincli) + # Add -nonamed because "bitcoin rpc" enables -named by default, but bitcoin-cli doesn't + return self._argv("rpc", self.paths.bitcoincli) + ["-nonamed"] def util_argv(self): "Return argv array that should be used to invoke bitcoin-util" - return self._argv(self.paths.bitcoinutil) + return self._argv("util", self.paths.bitcoinutil) def wallet_argv(self): "Return argv array that should be used to invoke bitcoin-wallet" - return self._argv(self.paths.bitcoinwallet) + return self._argv("wallet", self.paths.bitcoinwallet) def chainstate_argv(self): "Return argv array that should be used to invoke bitcoin-chainstate" - return self._argv(self.paths.bitcoinchainstate) + return self._argv("chainstate", self.paths.bitcoinchainstate) - def _argv(self, bin_path): - """Return argv array that should be used to invoke the command. - Normally this will return binary paths directly from the paths object, - but when bin_dir is set (by tests calling binaries from previous - releases) it will return paths relative to bin_dir instead.""" + def _argv(self, command, bin_path): + """Return argv array that should be used to invoke the command. It + either uses the bitcoin wrapper executable (if BITCOIN_CMD is set), or + the direct binary path (bitcoind, etc). When bin_dir is set (by tests + calling binaries from previous releases) it always uses the direct + path.""" if self.bin_dir is not None: return [os.path.join(self.bin_dir, os.path.basename(bin_path))] + elif self.paths.bitcoin_cmd is not None: + return self.paths.bitcoin_cmd + [command] else: return [bin_path] @@ -292,6 +297,9 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass): binary + self.config["environment"]["EXEEXT"], ) setattr(paths, attribute_name, os.getenv(env_variable_name, default=default_filename)) + # BITCOIN_CMD environment variable can be specified to invoke bitcoin + # wrapper binary instead of other executables. + paths.bitcoin_cmd = shlex.split(os.getenv("BITCOIN_CMD", "")) or None return paths def get_binaries(self, bin_dir=None): @@ -537,7 +545,7 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass): bins_missing = False for bin_path in (argv[0] for bin_dir in bin_dirs for binaries in (self.get_binaries(bin_dir),) - for argv in (binaries.daemon_argv(), binaries.rpc_argv())): + for argv in (binaries.node_argv(), binaries.rpc_argv())): if shutil.which(bin_path) is None: self.log.error(f"Binary not found: {bin_path}") bins_missing = True diff --git a/test/functional/test_framework/test_node.py b/test/functional/test_framework/test_node.py index 449c0628753..f7edb98bc83 100755 --- a/test/functional/test_framework/test_node.py +++ b/test/functional/test_framework/test_node.py @@ -108,7 +108,7 @@ class TestNode(): # Configuration for logging is set as command-line args rather than in the bitcoin.conf file. # This means that starting a bitcoind using the temp dir to debug a failed test won't # spam debug.log. - self.args = self.binaries.daemon_argv() + [ + self.args = self.binaries.node_argv() + [ f"-datadir={self.datadir_path}", "-logtimemicros", "-debug", From ba649c00063a43b59a63db17b509179a658a8d9a Mon Sep 17 00:00:00 2001 From: Ryan Ofsky Date: Tue, 26 Nov 2024 11:58:16 -0500 Subject: [PATCH 4/7] ci: Run multiprocess tests through wrapper executable --- ci/test/00_setup_env_i686_multiprocess.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ci/test/00_setup_env_i686_multiprocess.sh b/ci/test/00_setup_env_i686_multiprocess.sh index d7c8e3ef1b9..3144cf22fc5 100755 --- a/ci/test/00_setup_env_i686_multiprocess.sh +++ b/ci/test/00_setup_env_i686_multiprocess.sh @@ -20,4 +20,4 @@ export BITCOIN_CONFIG="\ -DCMAKE_CXX_COMPILER='clang++;-m32' \ -DAPPEND_CPPFLAGS='-DBOOST_MULTI_INDEX_ENABLE_SAFE_MODE' \ " -export BITCOIND=bitcoin-node # Used in functional tests +export BITCOIN_CMD="bitcoin -m" # Used in functional tests From d2739d75c911c8bf73a4d3005c57add1ae4a67ae Mon Sep 17 00:00:00 2001 From: Sjors Provoost Date: Fri, 10 Jan 2025 15:33:52 +0100 Subject: [PATCH 5/7] build: add bitcoin.exe to windows installer --- cmake/module/GenerateSetupNsi.cmake | 1 + cmake/module/Maintenance.cmake | 5 +++-- share/setup.nsi.in | 2 ++ 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/cmake/module/GenerateSetupNsi.cmake b/cmake/module/GenerateSetupNsi.cmake index 97a53b071db..c8d5bd67c5f 100644 --- a/cmake/module/GenerateSetupNsi.cmake +++ b/cmake/module/GenerateSetupNsi.cmake @@ -7,6 +7,7 @@ function(generate_setup_nsi) set(abs_top_builddir ${PROJECT_BINARY_DIR}) set(CLIENT_URL ${PROJECT_HOMEPAGE_URL}) set(CLIENT_TARNAME "bitcoin") + set(BITCOIN_WRAPPER_NAME "bitcoin") set(BITCOIN_GUI_NAME "bitcoin-qt") set(BITCOIN_DAEMON_NAME "bitcoind") set(BITCOIN_CLI_NAME "bitcoin-cli") diff --git a/cmake/module/Maintenance.cmake b/cmake/module/Maintenance.cmake index a704dff175a..d09559c5a9a 100644 --- a/cmake/module/Maintenance.cmake +++ b/cmake/module/Maintenance.cmake @@ -23,7 +23,7 @@ function(add_maintenance_targets) return() endif() - foreach(target IN ITEMS bitcoind bitcoin-qt bitcoin-cli bitcoin-tx bitcoin-util bitcoin-wallet test_bitcoin bench_bitcoin) + foreach(target IN ITEMS bitcoin bitcoind bitcoin-qt bitcoin-cli bitcoin-tx bitcoin-util bitcoin-wallet test_bitcoin bench_bitcoin) if(TARGET ${target}) list(APPEND executables $) endif() @@ -43,7 +43,7 @@ function(add_maintenance_targets) endfunction() function(add_windows_deploy_target) - if(MINGW AND TARGET bitcoin-qt AND TARGET bitcoind AND TARGET bitcoin-cli AND TARGET bitcoin-tx AND TARGET bitcoin-wallet AND TARGET bitcoin-util AND TARGET test_bitcoin) + if(MINGW AND TARGET bitcoin AND TARGET bitcoin-qt AND TARGET bitcoind AND TARGET bitcoin-cli AND TARGET bitcoin-tx AND TARGET bitcoin-wallet AND TARGET bitcoin-util AND TARGET test_bitcoin) find_program(MAKENSIS_EXECUTABLE makensis) if(NOT MAKENSIS_EXECUTABLE) add_custom_target(deploy @@ -59,6 +59,7 @@ function(add_windows_deploy_target) add_custom_command( OUTPUT ${PROJECT_BINARY_DIR}/bitcoin-win64-setup.exe COMMAND ${CMAKE_COMMAND} -E make_directory ${PROJECT_BINARY_DIR}/release + COMMAND ${CMAKE_STRIP} $ -o ${PROJECT_BINARY_DIR}/release/$ COMMAND ${CMAKE_STRIP} $ -o ${PROJECT_BINARY_DIR}/release/$ COMMAND ${CMAKE_STRIP} $ -o ${PROJECT_BINARY_DIR}/release/$ COMMAND ${CMAKE_STRIP} $ -o ${PROJECT_BINARY_DIR}/release/$ diff --git a/share/setup.nsi.in b/share/setup.nsi.in index 60b2f09ba14..2160cc05a12 100644 --- a/share/setup.nsi.in +++ b/share/setup.nsi.in @@ -73,6 +73,7 @@ Section -Main SEC0000 SetOutPath $INSTDIR SetOverwrite on File @abs_top_builddir@/release/@BITCOIN_GUI_NAME@@EXEEXT@ + File @abs_top_builddir@/release/@BITCOIN_WRAPPER_NAME@@EXEEXT@ File /oname=COPYING.txt @abs_top_srcdir@/COPYING File /oname=readme.txt @abs_top_srcdir@/doc/README_windows.txt File @abs_top_srcdir@/share/examples/bitcoin.conf @@ -129,6 +130,7 @@ done${UNSECTION_ID}: # Uninstaller sections Section /o -un.Main UNSEC0000 + Delete /REBOOTOK $INSTDIR\@BITCOIN_WRAPPER_NAME@@EXEEXT@ Delete /REBOOTOK $INSTDIR\@BITCOIN_GUI_NAME@@EXEEXT@ Delete /REBOOTOK $INSTDIR\COPYING.txt Delete /REBOOTOK $INSTDIR\readme.txt From 258bda80c009a25d1f1bdeffccf9ed1ffde29cb2 Mon Sep 17 00:00:00 2001 From: Ryan Ofsky Date: Tue, 15 Apr 2025 10:19:27 -0400 Subject: [PATCH 6/7] doc: Mention bitcoin wrapper executable in documentation --- doc/JSON-RPC-interface.md | 2 ++ doc/README.md | 3 +++ doc/build-osx.md | 4 ++++ doc/build-unix.md | 1 + doc/cjdns.md | 2 ++ doc/external-signer.md | 4 ++++ doc/managing-wallets.md | 2 ++ doc/multiprocess.md | 15 ++++++++++----- doc/multisig-tutorial.md | 2 ++ doc/offline-signing-tutorial.md | 2 ++ doc/tor.md | 4 ++++ doc/zmq.md | 2 ++ 12 files changed, 38 insertions(+), 5 deletions(-) diff --git a/doc/JSON-RPC-interface.md b/doc/JSON-RPC-interface.md index 10d8ee52ebe..e7f085a377a 100644 --- a/doc/JSON-RPC-interface.md +++ b/doc/JSON-RPC-interface.md @@ -62,6 +62,8 @@ bitcoin-cli -named createwallet wallet_name=mywallet load_on_startup=true bitcoin-cli -named createwallet mywallet load_on_startup=true ``` +`bitcoin rpc` can also be substituted for `bitcoin-cli -named`, and is a newer alternative. + ## Versioning The RPC interface might change from one major version of Bitcoin Core to the diff --git a/doc/README.md b/doc/README.md index 79ca53ce763..0959c6c7f04 100644 --- a/doc/README.md +++ b/doc/README.md @@ -17,6 +17,9 @@ Unpack the files into a directory and run: - `bin/bitcoin-qt` (GUI) or - `bin/bitcoind` (headless) +- `bin/bitcoin` (wrapper command) + +The `bitcoin` command supports subcommands like `bitcoin gui`, `bitcoin node`, and `bitcoin rpc` exposing different functionality. Subcommands can be listed with `bitcoin help`. ### Windows diff --git a/doc/build-osx.md b/doc/build-osx.md index b30568b50cd..08e2e54c2aa 100644 --- a/doc/build-osx.md +++ b/doc/build-osx.md @@ -199,6 +199,10 @@ cmake --build build --target deploy Bitcoin Core should now be available at `./build/bin/bitcoind`. If you compiled support for the GUI, it should be available at `./build/bin/bitcoin-qt`. +There is also a multifunction command line interface at `./build/bin/bitcoin` +supporting subcommands like `bitcoin node`, `bitcoin gui`, `bitcoin rpc`, and +others that can be listed with `bitcoin help`. + The first time you run `bitcoind` or `bitcoin-qt`, it will start downloading the blockchain. This process could take many hours, or even days on slower than average systems. diff --git a/doc/build-unix.md b/doc/build-unix.md index d8857d43a79..591b229ecb7 100644 --- a/doc/build-unix.md +++ b/doc/build-unix.md @@ -174,4 +174,5 @@ This example lists the steps necessary to setup and build a command line only di cmake --build build ctest --test-dir build ./build/bin/bitcoind + ./build/bin/bitcoin help diff --git a/doc/cjdns.md b/doc/cjdns.md index 031cd1978bb..35fe87adfac 100644 --- a/doc/cjdns.md +++ b/doc/cjdns.md @@ -113,3 +113,5 @@ To see which CJDNS peers your node is connected to, use `bitcoin-cli -netinfo 4` or the `getpeerinfo` RPC (i.e. `bitcoin-cli getpeerinfo`). You can use the `getnodeaddresses` RPC to fetch a number of CJDNS peers known to your node; run `bitcoin-cli help getnodeaddresses` for details. + +`bitcoin rpc` can also be substituted for `bitcoin-cli`. diff --git a/doc/external-signer.md b/doc/external-signer.md index 1468e852ef6..c1b6b20ce01 100644 --- a/doc/external-signer.md +++ b/doc/external-signer.md @@ -14,6 +14,8 @@ Start Bitcoin Core: $ bitcoind -signer=../HWI/hwi.py ``` +`bitcoin node` can also be substituted for `bitcoind`. + ### Device setup Follow the hardware manufacturers instructions for the initial device setup, as well as their instructions for creating a backup. Alternatively, for some devices, you can use the `setup`, `restore` and `backup` commands provided by [HWI](https://github.com/bitcoin-core/HWI). @@ -40,6 +42,8 @@ Create a wallet, this automatically imports the public keys: $ bitcoin-cli createwallet "hww" true true "" true true true ``` +`bitcoin rpc` can also be substituted for `bitcoin-cli`. + ### Verify an address Display an address on the device: diff --git a/doc/managing-wallets.md b/doc/managing-wallets.md index d24c4139802..0e052bd8dbb 100644 --- a/doc/managing-wallets.md +++ b/doc/managing-wallets.md @@ -15,6 +15,8 @@ The following command, for example, creates a descriptor wallet. More informatio $ bitcoin-cli createwallet "wallet-01" ``` +`bitcoin rpc` can also be substituted for `bitcoin-cli`. + By default, wallets are created in the `wallets` folder of the data directory, which varies by operating system, as shown below. The user can change the default by using the `-datadir` or `-walletdir` initialization parameters. | Operating System | Default wallet directory | diff --git a/doc/multiprocess.md b/doc/multiprocess.md index 33c5a44aa82..5a91b513de5 100644 --- a/doc/multiprocess.md +++ b/doc/multiprocess.md @@ -25,8 +25,8 @@ make -C depends NO_QT=1 MULTIPROCESS=1 HOST_PLATFORM="x86_64-pc-linux-gnu" cmake -B build --toolchain=depends/$HOST_PLATFORM/toolchain.cmake cmake --build build -build/bin/bitcoin-node -regtest -printtoconsole -debug=ipc -BITCOIND=$(pwd)/build/bin/bitcoin-node build/test/functional/test_runner.py +build/bin/bitcoin -m node -regtest -printtoconsole -debug=ipc +BITCOIN_CMD="bitcoin -m" build/test/functional/test_runner.py ``` The `cmake` build will pick up settings and library locations from the depends directory, so there is no need to pass `-DENABLE_IPC=ON` as a separate flag when using the depends system (it's controlled by the `MULTIPROCESS=1` option). @@ -41,6 +41,11 @@ By default when `-DENABLE_IPC=ON` is enabled, the libmultiprocess sources at [.. ## Usage -`bitcoin-node` is a drop-in replacement for `bitcoind`, and `bitcoin-gui` is a drop-in replacement for `bitcoin-qt`, and there are no differences in use or external behavior between the new and old executables. But internally after [#10102](https://github.com/bitcoin/bitcoin/pull/10102), `bitcoin-gui` will spawn a `bitcoin-node` process to run P2P and RPC code, communicating with it across a socket pair, and `bitcoin-node` will spawn `bitcoin-wallet` to run wallet code, also communicating over a socket pair. This will let node, wallet, and GUI code run in separate address spaces for better isolation, and allow future improvements like being able to start and stop components independently on different machines and environments. -[#19460](https://github.com/bitcoin/bitcoin/pull/19460) also adds a new `bitcoin-node` `-ipcbind` option and a `bitcoind-wallet` `-ipcconnect` option to allow new wallet processes to connect to an existing node process. -And [#19461](https://github.com/bitcoin/bitcoin/pull/19461) adds a new `bitcoin-gui` `-ipcconnect` option to allow new GUI processes to connect to an existing node process. +Recommended way to use multiprocess binaries is to invoke `bitcoin` CLI like `bitcoin -m node -debug=ipc` or `bitcoin -m gui -printtoconsole -debug=ipc`. + +When the `-m` (`--multiprocess`) option is used the `bitcoin` command will execute multiprocess binaries instead of monolithic ones (`bitcoin-node` instead of `bitcoind`, and `bitcoin-gui` instead of `bitcoin-qt`). The multiprocess binaries can also be invoked directly, but this is not recommended as they may change or be renamed in the future, and they are not installed in the PATH. + +The multiprocess binaries currently function the same as the monolithic binaries, except they support an `-ipcbind` option. + +In the future, after [#10102](https://github.com/bitcoin/bitcoin/pull/10102) they will have other differences. Specifically `bitcoin-gui` will spawn a `bitcoin-node` process to run P2P and RPC code, communicating with it across a socket pair, and `bitcoin-node` will spawn `bitcoin-wallet` to run wallet code, also communicating over a socket pair. This will let node, wallet, and GUI code run in separate address spaces for better isolation, and allow future improvements like being able to start and stop components independently on different machines and environments. [#19460](https://github.com/bitcoin/bitcoin/pull/19460) also adds a new `bitcoin-wallet -ipcconnect` option to allow new wallet processes to connect to an existing node process. +And [#19461](https://github.com/bitcoin/bitcoin/pull/19461) adds a new `bitcoin-gui -ipcconnect` option to allow new GUI processes to connect to an existing node process. diff --git a/doc/multisig-tutorial.md b/doc/multisig-tutorial.md index 57d3d8d6722..0c39bc6c9ca 100644 --- a/doc/multisig-tutorial.md +++ b/doc/multisig-tutorial.md @@ -31,6 +31,8 @@ do done ``` +`bitcoin rpc` can also be substituted for `bitcoin-cli`. + Extract the xpub of each wallet. To do this, the `listdescriptors` RPC is used. By default, Bitcoin Core single-sig wallets are created using path `m/44'/1'/0'` for PKH, `m/84'/1'/0'` for WPKH, `m/49'/1'/0'` for P2WPKH-nested-in-P2SH and `m/86'/1'/0'` for P2TR based accounts. Each of them uses the chain 0 for external addresses and chain 1 for internal ones, as shown in the example below. ``` diff --git a/doc/offline-signing-tutorial.md b/doc/offline-signing-tutorial.md index 522303b032e..2b21af2441a 100644 --- a/doc/offline-signing-tutorial.md +++ b/doc/offline-signing-tutorial.md @@ -34,6 +34,8 @@ We are going to first create an `offline_wallet` on the offline host. We will th } ``` +`bitcoin rpc` can also be substituted for `bitcoin-cli`. + > [!NOTE] > The use of a passphrase is crucial to encrypt the wallet.dat file. This encryption ensures that even if an unauthorized individual gains access to the offline host, they won't be able to access the wallet's contents. Further details about securing your wallet can be found in [Managing the Wallet](https://github.com/bitcoin/bitcoin/blob/master/doc/managing-wallets.md#12-encrypting-the-wallet) diff --git a/doc/tor.md b/doc/tor.md index 30c2381049a..ef31bcc87dd 100644 --- a/doc/tor.md +++ b/doc/tor.md @@ -27,6 +27,8 @@ e.g. for `-onlynet=onion`. You can use the `getnodeaddresses` RPC to fetch a number of onion peers known to your node; run `bitcoin-cli help getnodeaddresses` for details. +`bitcoin rpc` can also be substituted for `bitcoin-cli`. + ## 1. Run Bitcoin Core behind a Tor proxy The first step is running Bitcoin Core behind a Tor proxy. This will already anonymize all @@ -64,6 +66,8 @@ In a typical situation, this suffices to run behind a Tor proxy: ./bitcoind -proxy=127.0.0.1:9050 +`bitcoin node` or `bitcoin gui` can also be substituted for `bitcoind`. + ## 2. Automatically create a Bitcoin Core onion service Bitcoin Core makes use of Tor's control socket API to create and destroy diff --git a/doc/zmq.md b/doc/zmq.md index c04f6db21b7..9637144d502 100644 --- a/doc/zmq.md +++ b/doc/zmq.md @@ -87,6 +87,8 @@ For instance: -zmqpubrawtx=ipc:///tmp/bitcoind.tx.raw \ -zmqpubhashtxhwm=10000 +`bitcoin node` or `bitcoin gui` can also be substituted for `bitcoind`. + Notification types correspond to message topics (details in next section). For instance, for the notification `-zmqpubhashtx` the topic is `hashtx`. These options can also be provided in bitcoin.conf. From a5ac43d98d1ad3ebed934f2c50208a85aae17e5e Mon Sep 17 00:00:00 2001 From: Ryan Ofsky Date: Tue, 15 Apr 2025 10:30:23 -0400 Subject: [PATCH 7/7] doc: Add release notes describing bitcoin wrapper executable --- doc/release-notes-31375.md | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 doc/release-notes-31375.md diff --git a/doc/release-notes-31375.md b/doc/release-notes-31375.md new file mode 100644 index 00000000000..e1e7f5c0afb --- /dev/null +++ b/doc/release-notes-31375.md @@ -0,0 +1,11 @@ +New command line interface +-------------------------- + +A new `bitcoin` command line tool has been added to make features more +discoverable and convenient to use. The `bitcoin` tool just calls other +executables and does not implement any functionality on its own. Specifically +`bitcoin node` is a synonym for `bitcoind`, `bitcoin gui` is a synonym for +`bitcoin-qt`, and `bitcoin rpc` is a synonym for `bitcoin-cli -named`. Other +commands and options can be listed with `bitcoin help`. The new tool does not +replace other tools, so all existing commands should continue working and there +are no plans to deprecate them.