From be6795a292645871f1976daaa00fd19b11bea6ae Mon Sep 17 00:00:00 2001 From: Ryan Ofsky Date: Fri, 26 Jul 2024 08:47:05 -0400 Subject: [PATCH] multiprocess: Add bitcoin-wallet -ipcconnect option Add `-ipcconnect` option to `bitcoin-wallet` to allow connecting to a bitcoin node process over IPC. The `bitcoin-wallet` tool doesn't really do anything with its connection to the node yet, but it could potentially run or serve RPCs that require being online. Example usage: src/bitcoin-node -regtest -debug -ipcbind=unix src/bitcoin-wallet -regtest -ipcconnect=unix info --- src/bitcoin-wallet.cpp | 22 +++++++++++++++++++--- src/init/bitcoin-wallet-ipc.cpp | 1 + src/interfaces/init.h | 1 + src/wallet/wallettool.cpp | 11 +++++------ src/wallet/wallettool.h | 6 +++++- 5 files changed, 31 insertions(+), 10 deletions(-) diff --git a/src/bitcoin-wallet.cpp b/src/bitcoin-wallet.cpp index 033cf7a0f39..80e1b78975e 100644 --- a/src/bitcoin-wallet.cpp +++ b/src/bitcoin-wallet.cpp @@ -10,7 +10,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -28,7 +30,7 @@ using util::Join; const std::function G_TRANSLATION_FUN = nullptr; -static void SetupWalletToolArgs(ArgsManager& argsman) +static void SetupWalletToolArgs(ArgsManager& argsman, bool can_connect_ipc) { SetupHelpOptions(argsman); SetupChainParamsBaseOptions(argsman); @@ -43,6 +45,9 @@ static void SetupWalletToolArgs(ArgsManager& argsman) argsman.AddArg("-format=", "The format of the wallet file to create. Either \"bdb\" or \"sqlite\". Only used with 'createfromdump'", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); argsman.AddArg("-printtoconsole", "Send trace/debug info to console (default: 1 when no -debug is true, 0 otherwise).", ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST); argsman.AddArg("-withinternalbdb", "Use the internal Berkeley DB parser when dumping a Berkeley DB wallet file (default: false)", ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST); + if (can_connect_ipc) { + argsman.AddArg("-ipcconnect=
", "Connect to bitcoin-node process in the background to perform online operations. Valid
values are 'auto' to try connecting to default socket in /sockets/node.sock, but proceed offline if it isn't available, 'unix' to connect to the default socket and fail if it isn't available, 'unix:' to connect to a socket at a nonstandard path, and -noipcconnect to not connect. Default value: auto", ArgsManager::ALLOW_ANY, OptionsCategory::IPC); + } argsman.AddCommand("info", "Get wallet info"); argsman.AddCommand("create", "Create new wallet file"); @@ -53,7 +58,6 @@ static void SetupWalletToolArgs(ArgsManager& argsman) static std::optional WalletAppInit(ArgsManager& args, int argc, char* argv[]) { - SetupWalletToolArgs(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); @@ -113,6 +117,7 @@ MAIN_FUNCTION SetupEnvironment(); RandomInit(); try { + SetupWalletToolArgs(args, init->canConnectIpc()); if (const auto maybe_exit{WalletAppInit(args, argc, argv)}) return *maybe_exit; } catch (const std::exception& e) { PrintExceptionContinue(&e, "WalletAppInit()"); @@ -133,7 +138,18 @@ MAIN_FUNCTION } ECC_Context ecc_context{}; - if (!wallet::WalletTool::ExecuteWalletToolFunc(args, command->command)) { + + std::unique_ptr chain; + if (interfaces::Ipc* ipc = init->ipc()) { + std::string address = args.GetArg("-ipcconnect", "auto"); + if (auto init = ipc->connectAddress(address)) { + tfm::format(std::cout, "Connected to IPC address %s\n", address); + chain = init->makeChain(); + ipc->addCleanup(*chain, [init = init.release()] { delete init; }); + } + } + + if (!wallet::WalletTool::ExecuteWalletToolFunc(args, chain.get(), command->command)) { return EXIT_FAILURE; } return EXIT_SUCCESS; diff --git a/src/init/bitcoin-wallet-ipc.cpp b/src/init/bitcoin-wallet-ipc.cpp index dad3649de27..eec62040360 100644 --- a/src/init/bitcoin-wallet-ipc.cpp +++ b/src/init/bitcoin-wallet-ipc.cpp @@ -72,6 +72,7 @@ public: return MakeWalletLoader(chain, gArgs); } interfaces::Ipc* ipc() override { return m_ipc.get(); } + bool canConnectIpc() override { return true; } std::unique_ptr m_ipc; std::optional m_kernel; std::optional m_ecc_context; diff --git a/src/interfaces/init.h b/src/interfaces/init.h index b496ada05f4..6943ea23fdf 100644 --- a/src/interfaces/init.h +++ b/src/interfaces/init.h @@ -37,6 +37,7 @@ public: virtual std::unique_ptr makeWalletLoader(Chain& chain) { return nullptr; } virtual std::unique_ptr makeEcho() { return nullptr; } virtual Ipc* ipc() { return nullptr; } + virtual bool canConnectIpc() { return false; } virtual bool canListenIpc() { return false; } }; diff --git a/src/wallet/wallettool.cpp b/src/wallet/wallettool.cpp index f7e7b74fc36..5fba038d521 100644 --- a/src/wallet/wallettool.cpp +++ b/src/wallet/wallettool.cpp @@ -45,7 +45,7 @@ static void WalletCreate(CWallet* wallet_instance, uint64_t wallet_creation_flag wallet_instance->TopUpKeyPool(); } -static std::shared_ptr MakeWallet(const std::string& name, const fs::path& path, DatabaseOptions options) +static std::shared_ptr MakeWallet(const std::string& name, const fs::path& path, DatabaseOptions options, interfaces::Chain* chain) { DatabaseStatus status; bilingual_str error; @@ -55,8 +55,7 @@ static std::shared_ptr MakeWallet(const std::string& name, const fs::pa return nullptr; } - // dummy chain interface - std::shared_ptr wallet_instance{new CWallet(/*chain=*/nullptr, name, std::move(database)), WalletToolReleaseWallet}; + std::shared_ptr wallet_instance{new CWallet(chain, name, std::move(database)), WalletToolReleaseWallet}; DBErrors load_wallet_ret; try { load_wallet_ret = wallet_instance->LoadWallet(); @@ -110,7 +109,7 @@ static void WalletShowInfo(CWallet* wallet_instance) tfm::format(std::cout, "Address Book: %zu\n", wallet_instance->m_address_book.size()); } -bool ExecuteWalletToolFunc(const ArgsManager& args, const std::string& command) +bool ExecuteWalletToolFunc(const ArgsManager& args, interfaces::Chain* chain, const std::string& command) { if (args.IsArgSet("-format") && command != "createfromdump") { tfm::format(std::cerr, "The -format option can only be used with the \"createfromdump\" command.\n"); @@ -156,7 +155,7 @@ bool ExecuteWalletToolFunc(const ArgsManager& args, const std::string& command) options.require_format = DatabaseFormat::SQLITE; } - const std::shared_ptr wallet_instance = MakeWallet(name, path, options); + const std::shared_ptr wallet_instance = MakeWallet(name, path, options, chain); if (wallet_instance) { WalletShowInfo(wallet_instance.get()); wallet_instance->Close(); @@ -165,7 +164,7 @@ bool ExecuteWalletToolFunc(const ArgsManager& args, const std::string& command) DatabaseOptions options; ReadDatabaseArgs(args, options); options.require_existing = true; - const std::shared_ptr wallet_instance = MakeWallet(name, path, options); + const std::shared_ptr wallet_instance = MakeWallet(name, path, options, chain); if (!wallet_instance) return false; WalletShowInfo(wallet_instance.get()); wallet_instance->Close(); diff --git a/src/wallet/wallettool.h b/src/wallet/wallettool.h index 9e0fe2b0ecb..ea22b51088d 100644 --- a/src/wallet/wallettool.h +++ b/src/wallet/wallettool.h @@ -9,10 +9,14 @@ class ArgsManager; +namespace interfaces { +class Chain; +} // namespace interfaces + namespace wallet { namespace WalletTool { -bool ExecuteWalletToolFunc(const ArgsManager& args, const std::string& command); +bool ExecuteWalletToolFunc(const ArgsManager& args, interfaces::Chain* chain, const std::string& command); } // namespace WalletTool } // namespace wallet