ipc: Expose an RPC interface over the -ipcbind socket

This allows `bitcoin-cli` to connect to the node via IPC instead TCP to execute
RPC methods in an upcoming commit.
This commit is contained in:
Ryan Ofsky
2025-10-09 12:47:27 -04:00
parent df76891a3b
commit 6a54834895
12 changed files with 95 additions and 1 deletions

View File

@@ -719,7 +719,7 @@ void SetupServerArgs(ArgsManager& argsman, bool can_listen_ipc)
argsman.AddArg("-rpcworkqueue=<n>", strprintf("Set the maximum depth of the work queue to service RPC calls (default: %d)", DEFAULT_HTTP_WORKQUEUE), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::RPC);
argsman.AddArg("-server", "Accept command line and JSON-RPC commands", ArgsManager::ALLOW_ANY, OptionsCategory::RPC);
if (can_listen_ipc) {
argsman.AddArg("-ipcbind=<address>", "Bind to Unix socket address and listen for incoming connections. Valid address values are \"unix\" to listen on the default path, <datadir>/node.sock, or \"unix:/custom/path\" to specify a custom path. Can be specified multiple times to listen on multiple paths. Default behavior is not to listen on any path. If relative paths are specified, they are interpreted relative to the network data directory. If paths include any parent directory components and the parent directories do not exist, they will be created.", ArgsManager::ALLOW_ANY, OptionsCategory::IPC);
argsman.AddArg("-ipcbind=<address>", "Bind to Unix socket address and listen for incoming connections. Valid address values are \"unix\" to listen on the default path, <datadir>/node.sock, or \"unix:/custom/path\" to specify a custom path. Can be specified multiple times to listen on multiple paths. Default behavior is not to listen on any path. If relative paths are specified, they are interpreted relative to the network data directory. If paths include any parent directory components and the parent directories do not exist, they will be created. Enabling this gives local processes that can access the socket unauthenticated RPC access, so it's important to choose a path with secure permissions if customizing this.", ArgsManager::ALLOW_ANY, OptionsCategory::IPC);
}
#if HAVE_DECL_FORK

View File

@@ -8,6 +8,7 @@
#include <interfaces/init.h>
#include <interfaces/ipc.h>
#include <interfaces/node.h>
#include <interfaces/rpc.h>
#include <interfaces/wallet.h>
#include <node/context.h>
#include <util/check.h>
@@ -33,6 +34,7 @@ public:
return MakeWalletLoader(chain, *Assert(m_node.args));
}
std::unique_ptr<interfaces::Echo> makeEcho() override { return interfaces::MakeEcho(); }
std::unique_ptr<interfaces::Rpc> makeRpc() override { return interfaces::MakeRpc(m_node); }
interfaces::Ipc* ipc() override { return m_ipc.get(); }
// bitcoin-gui accepts -ipcbind option even though it does not use it
// directly. It just returns true here to accept the option because

View File

@@ -8,6 +8,7 @@
#include <interfaces/init.h>
#include <interfaces/ipc.h>
#include <interfaces/node.h>
#include <interfaces/rpc.h>
#include <interfaces/wallet.h>
#include <node/context.h>
#include <util/check.h>
@@ -36,6 +37,7 @@ public:
return MakeWalletLoader(chain, *Assert(m_node.args));
}
std::unique_ptr<interfaces::Echo> makeEcho() override { return interfaces::MakeEcho(); }
std::unique_ptr<interfaces::Rpc> makeRpc() override { return interfaces::MakeRpc(m_node); }
interfaces::Ipc* ipc() override { return m_ipc.get(); }
bool canListenIpc() override { return true; }
const char* exeName() override { return EXE_NAME; }

View File

@@ -16,4 +16,6 @@ The following interfaces are defined here:
* [`Ipc`](ipc.h) — used by multiprocess code to access `Init` interface across processes. Added in [#19160](https://github.com/bitcoin/bitcoin/pull/19160).
* [`Rpc`](rpc.h) — used by `bitcoin-cli` to be able to call RPC methods over a unix socket instead of TCP.
The interfaces above define boundaries between major components of bitcoin code (node, wallet, and gui), making it possible for them to run in [different processes](../../doc/multiprocess.md), and be tested, developed, and understood independently. These interfaces are not currently designed to be stable or to be used externally.

View File

@@ -9,6 +9,7 @@
#include <interfaces/echo.h>
#include <interfaces/mining.h>
#include <interfaces/node.h>
#include <interfaces/rpc.h>
#include <interfaces/wallet.h>
#include <memory>
@@ -36,6 +37,7 @@ public:
virtual std::unique_ptr<Mining> makeMining() { return nullptr; }
virtual std::unique_ptr<WalletLoader> makeWalletLoader(Chain& chain) { return nullptr; }
virtual std::unique_ptr<Echo> makeEcho() { return nullptr; }
virtual std::unique_ptr<Rpc> makeRpc() { return nullptr; }
virtual Ipc* ipc() { return nullptr; }
virtual bool canListenIpc() { return false; }
virtual const char* exeName() { return nullptr; }

31
src/interfaces/rpc.h Normal file
View File

@@ -0,0 +1,31 @@
// 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_INTERFACES_RPC_H
#define BITCOIN_INTERFACES_RPC_H
#include <memory>
#include <string>
class UniValue;
namespace node {
struct NodeContext;
} // namespace node
namespace interfaces {
//! Interface giving clients ability to emulate HTTP RPC calls.
class Rpc
{
public:
virtual ~Rpc() = default;
virtual UniValue executeRpc(UniValue request, std::string url, std::string user) = 0;
};
//! Return implementation of Rpc interface.
std::unique_ptr<Rpc> MakeRpc(node::NodeContext& node);
} // namespace interfaces
#endif // BITCOIN_INTERFACES_RPC_H

View File

@@ -14,6 +14,7 @@ target_capnp_sources(bitcoin_ipc ${CMAKE_CURRENT_SOURCE_DIR}
capnp/echo.capnp
capnp/init.capnp
capnp/mining.capnp
capnp/rpc.capnp
)
target_link_libraries(bitcoin_ipc

View File

@@ -7,5 +7,6 @@
#include <ipc/capnp/echo.capnp.proxy-types.h>
#include <ipc/capnp/mining.capnp.proxy-types.h>
#include <ipc/capnp/rpc.capnp.proxy-types.h>
#endif // BITCOIN_IPC_CAPNP_INIT_TYPES_H

View File

@@ -15,11 +15,13 @@ $Proxy.includeTypes("ipc/capnp/init-types.h");
using Echo = import "echo.capnp";
using Mining = import "mining.capnp";
using Rpc = import "rpc.capnp";
interface Init $Proxy.wrap("interfaces::Init") {
construct @0 (threadMap: Proxy.ThreadMap) -> (threadMap :Proxy.ThreadMap);
makeEcho @1 (context :Proxy.Context) -> (result :Echo.Echo);
makeMining @3 (context :Proxy.Context) -> (result :Mining.Mining);
makeRpc @4 (context :Proxy.Context) -> (result :Rpc.Rpc);
# DEPRECATED: no longer supported; server returns an error.
makeMiningOld2 @2 () -> ();

12
src/ipc/capnp/rpc-types.h Normal file
View File

@@ -0,0 +1,12 @@
// 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_IPC_CAPNP_RPC_TYPES_H
#define BITCOIN_IPC_CAPNP_RPC_TYPES_H
#include <ipc/capnp/common.capnp.proxy-types.h>
#include <ipc/capnp/common-types.h>
#include <ipc/capnp/rpc.capnp.proxy.h>
#endif // BITCOIN_IPC_CAPNP_RPC_TYPES_H

17
src/ipc/capnp/rpc.capnp Normal file
View File

@@ -0,0 +1,17 @@
# 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.
@0x9c3505dc45e146ac;
using Cxx = import "/capnp/c++.capnp";
$Cxx.namespace("ipc::capnp::messages");
using Common = import "common.capnp";
using Proxy = import "/mp/proxy.capnp";
$Proxy.include("interfaces/rpc.h");
$Proxy.includeTypes("ipc/capnp/rpc-types.h");
interface Rpc $Proxy.wrap("interfaces::Rpc") {
executeRpc @0 (context :Proxy.Context, request :Text, uri :Text, user :Text) -> (result :Text);
}

View File

@@ -12,12 +12,14 @@
#include <consensus/validation.h>
#include <deploymentstatus.h>
#include <external_signer.h>
#include <httprpc.h>
#include <index/blockfilterindex.h>
#include <init.h>
#include <interfaces/chain.h>
#include <interfaces/handler.h>
#include <interfaces/mining.h>
#include <interfaces/node.h>
#include <interfaces/rpc.h>
#include <interfaces/types.h>
#include <interfaces/wallet.h>
#include <kernel/chain.h>
@@ -81,6 +83,7 @@ using interfaces::Handler;
using interfaces::MakeSignalHandler;
using interfaces::Mining;
using interfaces::Node;
using interfaces::Rpc;
using interfaces::WalletLoader;
using kernel::ChainstateRole;
using node::BlockAssembler;
@@ -1011,6 +1014,24 @@ public:
bool m_interrupt_mining{false};
NodeContext& m_node;
};
class RpcImpl : public Rpc
{
public:
explicit RpcImpl(NodeContext& node) : m_node(node) {}
UniValue executeRpc(UniValue request, std::string uri, std::string user) override
{
JSONRPCRequest req;
req.context = &m_node;
req.URI = std::move(uri);
req.authUser = std::move(user);
HTTPStatusCode status;
return ExecuteHTTPRPC(request, req, status);
}
NodeContext& m_node;
};
} // namespace
} // namespace node
@@ -1030,4 +1051,5 @@ std::unique_ptr<Mining> MakeMining(node::NodeContext& context, bool wait_loaded)
}
return std::make_unique<node::MinerImpl>(context);
}
std::unique_ptr<Rpc> MakeRpc(node::NodeContext& context) { return std::make_unique<node::RpcImpl>(context); }
} // namespace interfaces