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
This commit is contained in:
Ryan Ofsky 2024-07-26 08:47:05 -04:00
parent 5a865918d6
commit be6795a292
5 changed files with 31 additions and 10 deletions

View file

@ -10,7 +10,9 @@
#include <common/args.h>
#include <common/system.h>
#include <compat/compat.h>
#include <interfaces/chain.h>
#include <interfaces/init.h>
#include <interfaces/ipc.h>
#include <key.h>
#include <logging.h>
#include <pubkey.h>
@ -28,7 +30,7 @@ using util::Join;
const std::function<std::string(const char*)> 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=<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=<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 proceed offline 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 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<int> 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<interfaces::Chain> 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;

View file

@ -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<interfaces::Ipc> m_ipc;
std::optional<kernel::Context> m_kernel;
std::optional<ECC_Context> m_ecc_context;

View file

@ -37,6 +37,7 @@ public:
virtual std::unique_ptr<WalletLoader> makeWalletLoader(Chain& chain) { return nullptr; }
virtual std::unique_ptr<Echo> makeEcho() { return nullptr; }
virtual Ipc* ipc() { return nullptr; }
virtual bool canConnectIpc() { return false; }
virtual bool canListenIpc() { return false; }
};

View file

@ -45,7 +45,7 @@ static void WalletCreate(CWallet* wallet_instance, uint64_t wallet_creation_flag
wallet_instance->TopUpKeyPool();
}
static std::shared_ptr<CWallet> MakeWallet(const std::string& name, const fs::path& path, DatabaseOptions options)
static std::shared_ptr<CWallet> 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<CWallet> MakeWallet(const std::string& name, const fs::pa
return nullptr;
}
// dummy chain interface
std::shared_ptr<CWallet> wallet_instance{new CWallet(/*chain=*/nullptr, name, std::move(database)), WalletToolReleaseWallet};
std::shared_ptr<CWallet> 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<CWallet> wallet_instance = MakeWallet(name, path, options);
const std::shared_ptr<CWallet> 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<CWallet> wallet_instance = MakeWallet(name, path, options);
const std::shared_ptr<CWallet> wallet_instance = MakeWallet(name, path, options, chain);
if (!wallet_instance) return false;
WalletShowInfo(wallet_instance.get());
wallet_instance->Close();

View file

@ -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