Skip to content

Commit

Permalink
multiprocess: Allow bitcoin-mine to spawn bitcoin-node
Browse files Browse the repository at this point in the history
Allow bitcoin-mine executable to spawn a new bitcoin-node process and start the
node, instead of only being able to connect to an existing bitcoin-node
process.
  • Loading branch information
ryanofsky committed Sep 26, 2024
1 parent 9b353b7 commit f3c2d4a
Show file tree
Hide file tree
Showing 11 changed files with 160 additions and 53 deletions.
5 changes: 3 additions & 2 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,7 @@ add_library(bitcoin_node STATIC EXCLUDE_FROM_ALL
addrman.cpp
banman.cpp
bip324.cpp
bitcoind.cpp
blockencodings.cpp
blockfilter.cpp
chain.cpp
Expand Down Expand Up @@ -302,7 +303,7 @@ target_link_libraries(bitcoin_node
# Bitcoin Core bitcoind.
if(BUILD_DAEMON)
add_executable(bitcoind
bitcoind.cpp
main.cpp
init/bitcoind.cpp
)
add_windows_resources(bitcoind bitcoind-res.rc)
Expand All @@ -315,7 +316,7 @@ if(BUILD_DAEMON)
endif()
if(WITH_MULTIPROCESS)
add_executable(bitcoin-node
bitcoind.cpp
main.cpp
init/bitcoin-node.cpp
)
target_link_libraries(bitcoin-node
Expand Down
55 changes: 40 additions & 15 deletions src/bitcoin-mine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,19 +21,25 @@ static const char* const HELP_USAGE{R"(
bitcoin-mine is a test program for interacting with bitcoin-node via IPC.
Usage:
bitcoin-mine [options]
bitcoin-mine [options] [--] [node 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.
# Connect to existing bitcoin-node or spawn new one if not running.
bitcoin-mine -regtest
# Stop bitcoin node.
bitcoin-mine -regtest -stop;
# Run with debug output.
bitcoin-mine -regtest -debug
# Pass extra options to bitcoin-node when spawning it
bitcoin-mine -regtest -- -upnp
)"};

const std::function<std::string(const char*)> G_TRANSLATION_FUN = nullptr;
Expand All @@ -44,16 +50,23 @@ static void AddArgs(ArgsManager& args)
SetupChainParamsBaseOptions(args);
args.AddArg("-version", "Print version and exit", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
args.AddArg("-datadir=<dir>", "Specify data directory", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
args.AddArg("-ipcconnect=<address>", "Connect to bitcoin-node process in the background to perform online operations. Valid <address> values are 'unix' to connect to the default socket, 'unix:<socket path>' to connect to a socket at a nonstandard path. Default value: unix", ArgsManager::ALLOW_ANY, OptionsCategory::IPC);
args.AddArg("-stop", "Stop bitcoin-node process if it is running.", ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::OPTIONS);
args.AddArg("-ipcconnect=<address>", "Connect to bitcoin-node process in the background to perform online operations. Valid <address> values are 'auto' to try connecting to default socket in <datadir>/sockets/node.sock, but and spawn a node if it isn't available, 'unix' to connect to the default socket and fail if it isn't available, 'unix:<socket path>' to connect to a socket at a nonstandard path, and -noipcconnect to not try to connect. Default value: auto", ArgsManager::ALLOW_ANY, OptionsCategory::IPC);
init::AddLoggingArgs(args);
}

MAIN_FUNCTION
{
// Look for -- separator for arguments which should be passed to bitcoin-node.
int argc_mine{argc};
for (int i{0}; i < argc; ++i) {
if (std::string_view{argv[i]} == "--") argc_mine = i;
}

ArgsManager& args = gArgs;
AddArgs(args);
std::string error_message;
if (!args.ParseParameters(argc, argv, error_message)) {
if (!args.ParseParameters(argc_mine, argv, error_message)) {
tfm::format(std::cerr, "Error parsing command line arguments: %s\n", error_message);
return EXIT_FAILURE;
}
Expand Down Expand Up @@ -90,20 +103,25 @@ MAIN_FUNCTION
// Connect to existing bitcoin-node process or spawn new one.
std::unique_ptr<interfaces::Init> mine_init{interfaces::MakeMineInit(argc, argv)};
assert(mine_init);
std::unique_ptr<interfaces::Init> 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;
std::string address{args.GetArg("-ipcconnect", "auto")};
std::unique_ptr<interfaces::Init> node_init{mine_init->ipc()->connectAddress(address)};
bool spawn{!node_init};
if (spawn) {
tfm::format(std::cout, "Spawning bitcoin-node\n");
node_init = mine_init->ipc()->spawnProcess("bitcoin-node", /*detach=*/true);
assert(node_init);
} else {
tfm::format(std::cout, "Connected to bitcoin-node\n");
}
assert(node_init);
tfm::format(std::cout, "Connected to bitcoin-node\n");
std::unique_ptr<interfaces::Mining> mining{node_init->makeMining()};
assert(mining);
if (spawn) {
args.LockSettings([&](common::Settings& settings) {
const int node_argc{argc - std::min(argc, argc_mine + 1)};
const char* const* node_argv{argv + argc - node_argc};
mining->startNode(settings, node_argc, node_argv);
});
}

auto tip{mining->getTip()};
if (tip) {
Expand All @@ -112,5 +130,12 @@ MAIN_FUNCTION
tfm::format(std::cout, "Tip hash is null.\n");
}

if (args.GetBoolArg("-stop", false)) {
tfm::format(std::cout, "Stopping bitcoin-node.\n");
int exit_status;
mining->stopNode(exit_status);
tfm::format(std::cout, "bitcoin-node exited with status %i.\n", exit_status);
}

return EXIT_SUCCESS;
}
21 changes: 4 additions & 17 deletions src/bitcoind.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include <node/interface_ui.h>
#include <node/warnings.h>
#include <noui.h>
#include <univalue.h>
#include <util/check.h>
#include <util/exception.h>
#include <util/signalinterrupt.h>
Expand All @@ -34,8 +35,6 @@

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.
Expand Down Expand Up @@ -109,7 +108,7 @@ int fork_daemon(bool nochdir, bool noclose, TokenPipeEnd& endpoint)

#endif

static bool ParseArgs(NodeContext& node, int argc, char* argv[])
bool ParseArgs(NodeContext& node, int argc, char* argv[])
{
ArgsManager& args{*Assert(node.args)};
// If Qt is used, parameters/bitcoin.conf are parsed in qt/bitcoin.cpp's main()
Expand Down Expand Up @@ -153,7 +152,7 @@ static bool ProcessInitCommands(ArgsManager& args)
return false;
}

static bool AppInit(NodeContext& node)
bool AppInit(NodeContext& node)
{
bool fRet = false;
ArgsManager& args = *Assert(node.args);
Expand Down Expand Up @@ -246,20 +245,8 @@ static bool AppInit(NodeContext& node)
return fRet;
}

MAIN_FUNCTION
int NodeMain(NodeContext& node, int argc, char* argv[])
{
#ifdef WIN32
common::WinCmdLineArgs winArgs;
std::tie(argc, argv) = winArgs.get();
#endif

NodeContext node;
int exit_status;
std::unique_ptr<interfaces::Init> init = interfaces::MakeNodeInit(node, argc, argv, exit_status);
if (!init) {
return exit_status;
}

SetupEnvironment();

// Connect bitcoind signal handlers
Expand Down
16 changes: 16 additions & 0 deletions src/bitcoind.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Copyright (c) 2024 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_BITCOIND_H
#define BITCOIN_BITCOIND_H

namespace node {
struct NodeContext;
} // namespace node

bool ParseArgs(node::NodeContext& node, int argc, char* argv[]);
bool AppInit(node::NodeContext& node);
int NodeMain(node::NodeContext& node, int argc, char* argv[]);

#endif // BITCOIN_BITCOIND_H
1 change: 0 additions & 1 deletion src/common/args.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,6 @@ void ArgsManager::SelectConfigNetwork(const std::string& network)
bool ArgsManager::ParseParameters(int argc, const char* const argv[], std::string& error)
{
LOCK(cs_args);
m_settings.command_line_options.clear();

for (int i = 1; i < argc; i++) {
std::string key(argv[i]);
Expand Down
9 changes: 9 additions & 0 deletions src/interfaces/mining.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@
#include <optional> // for optional
#include <vector> // for vector

namespace common {
struct Settings;
} // namespace common
namespace node {
struct NodeContext;
} // namespace node
Expand Down Expand Up @@ -51,6 +54,12 @@ class Mining
public:
virtual ~Mining() = default;

//! Start node. Return false if node failed to start up or was already started.
virtual bool startNode(const common::Settings& settings, int argc, const char* const argv[]) = 0;

// Stop node. Return false if node was not started.
virtual bool stopNode(int& exit_status) = 0;

//! If this chain is exclusively used for testing
virtual bool isTestChain() = 0;

Expand Down
2 changes: 2 additions & 0 deletions src/ipc/capnp/mining.capnp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ interface Mining $Proxy.wrap("interfaces::Mining") {
processNewBlock @5 (context :Proxy.Context, block: Data) -> (newBlock: Bool, result: Bool);
getTransactionsUpdated @6 (context :Proxy.Context) -> (result: UInt32);
testBlockValidity @7 (context :Proxy.Context, block: Data, checkMerkleRoot: Bool) -> (state: BlockValidationState, result: Bool);
startNode @8 (context :Proxy.Context, settings: Common.Settings, argv: List(Text) $Proxy.count(2)) -> (result: Bool);
stopNode @9 (context :Proxy.Context) -> (exitStatus: Int32, result: Bool);
}

interface BlockTemplate $Proxy.wrap("interfaces::BlockTemplate") {
Expand Down
31 changes: 31 additions & 0 deletions src/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Copyright (c) 2024 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 <bitcoind.h>
#include <interfaces/init.h>
#include <node/context.h>

#include <functional>
#include <string>

using node::NodeContext;

const std::function<std::string(const char*)> G_TRANSLATION_FUN = nullptr;

MAIN_FUNCTION
{
#ifdef WIN32
common::WinCmdLineArgs winArgs;
std::tie(argc, argv) = winArgs.get();
#endif

NodeContext node;
int exit_status;
std::unique_ptr<interfaces::Init> init = interfaces::MakeNodeInit(node, argc, argv, exit_status);
if (!init) {
return exit_status;
}

return NodeMain(node, argc, argv);
}
31 changes: 31 additions & 0 deletions src/node/interfaces.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

#include <addrdb.h>
#include <banman.h>
#include <bitcoind.h>
#include <blockfilter.h>
#include <chain.h>
#include <chainparams.h>
Expand All @@ -15,6 +16,8 @@
#include <init.h>
#include <interfaces/chain.h>
#include <interfaces/handler.h>
#include <interfaces/init.h>
#include <interfaces/ipc.h>
#include <interfaces/mining.h>
#include <interfaces/node.h>
#include <interfaces/types.h>
Expand All @@ -24,6 +27,7 @@
#include <kernel/mempool_entry.h>
#include <logging.h>
#include <mapport.h>
#include <noui.h>
#include <net.h>
#include <net_processing.h>
#include <netaddress.h>
Expand Down Expand Up @@ -918,6 +922,33 @@ class MinerImpl : public Mining
public:
explicit MinerImpl(NodeContext& node) : m_node(node) {}

bool startNode(const common::Settings& settings, int argc, const char* const argv[]) override
{
if (m_node.kernel) return false;
if (interfaces::Ipc* ipc{Assert(m_node.init)->ipc()}) ipc->attach();
ArgsManager& args = *Assert(m_node.args);
args.LockSettings([&](common::Settings& node_settings) {
node_settings = settings;
node_settings.forced_settings["ipcbind"] = "unix";
});

if (!ParseArgs(m_node, argc, (char**)argv)) return false;
SelectParams(args.GetChainType());

SetupEnvironment();
noui_connect();
return AppInit(m_node);
}

bool stopNode(int& exit_status) override
{
if (!m_node.kernel) return false;
Interrupt(m_node);
Shutdown(m_node);
if (interfaces::Ipc* ipc{Assert(m_node.init)->ipc()}) ipc->detach();
return true;
}

bool isTestChain() override
{
return chainman().GetParams().IsTestChain();
Expand Down
Loading

0 comments on commit f3c2d4a

Please sign in to comment.