diff --git a/src/init.cpp b/src/init.cpp index 6a6e7a925b2..da024bdde42 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -719,7 +719,7 @@ void SetupServerArgs(ArgsManager& argsman, bool can_listen_ipc) argsman.AddArg("-rpcworkqueue=", 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=
", "Bind to Unix socket address and listen for incoming connections. Valid address values are \"unix\" to listen on the default path, /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=
", "Bind to Unix socket address and listen for incoming connections. Valid address values are \"unix\" to listen on the default path, /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 diff --git a/src/init/bitcoin-gui.cpp b/src/init/bitcoin-gui.cpp index ca3077b9bb2..02e8f063620 100644 --- a/src/init/bitcoin-gui.cpp +++ b/src/init/bitcoin-gui.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -33,6 +34,7 @@ public: return MakeWalletLoader(chain, *Assert(m_node.args)); } std::unique_ptr makeEcho() override { return interfaces::MakeEcho(); } + std::unique_ptr 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 diff --git a/src/init/bitcoin-node.cpp b/src/init/bitcoin-node.cpp index e42521938a6..b4e5b8eeb6d 100644 --- a/src/init/bitcoin-node.cpp +++ b/src/init/bitcoin-node.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -36,6 +37,7 @@ public: return MakeWalletLoader(chain, *Assert(m_node.args)); } std::unique_ptr makeEcho() override { return interfaces::MakeEcho(); } + std::unique_ptr 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; } diff --git a/src/interfaces/README.md b/src/interfaces/README.md index 97167d5298f..cbb2c40f319 100644 --- a/src/interfaces/README.md +++ b/src/interfaces/README.md @@ -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. diff --git a/src/interfaces/init.h b/src/interfaces/init.h index f214b2878fb..d5b394b0e23 100644 --- a/src/interfaces/init.h +++ b/src/interfaces/init.h @@ -9,6 +9,7 @@ #include #include #include +#include #include #include @@ -36,6 +37,7 @@ public: virtual std::unique_ptr makeMining() { return nullptr; } virtual std::unique_ptr makeWalletLoader(Chain& chain) { return nullptr; } virtual std::unique_ptr makeEcho() { return nullptr; } + virtual std::unique_ptr makeRpc() { return nullptr; } virtual Ipc* ipc() { return nullptr; } virtual bool canListenIpc() { return false; } virtual const char* exeName() { return nullptr; } diff --git a/src/interfaces/rpc.h b/src/interfaces/rpc.h new file mode 100644 index 00000000000..d8d5566ad87 --- /dev/null +++ b/src/interfaces/rpc.h @@ -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 +#include + +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 MakeRpc(node::NodeContext& node); + +} // namespace interfaces + +#endif // BITCOIN_INTERFACES_RPC_H diff --git a/src/ipc/CMakeLists.txt b/src/ipc/CMakeLists.txt index 5378ef1924b..8326423d8bc 100644 --- a/src/ipc/CMakeLists.txt +++ b/src/ipc/CMakeLists.txt @@ -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 diff --git a/src/ipc/capnp/init-types.h b/src/ipc/capnp/init-types.h index 2abd7b211e1..c6764d2a280 100644 --- a/src/ipc/capnp/init-types.h +++ b/src/ipc/capnp/init-types.h @@ -7,5 +7,6 @@ #include #include +#include #endif // BITCOIN_IPC_CAPNP_INIT_TYPES_H diff --git a/src/ipc/capnp/init.capnp b/src/ipc/capnp/init.capnp index a20ef2fcaf3..d95bfb7426a 100644 --- a/src/ipc/capnp/init.capnp +++ b/src/ipc/capnp/init.capnp @@ -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 () -> (); diff --git a/src/ipc/capnp/rpc-types.h b/src/ipc/capnp/rpc-types.h new file mode 100644 index 00000000000..4d385dee312 --- /dev/null +++ b/src/ipc/capnp/rpc-types.h @@ -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 +#include +#include + +#endif // BITCOIN_IPC_CAPNP_RPC_TYPES_H diff --git a/src/ipc/capnp/rpc.capnp b/src/ipc/capnp/rpc.capnp new file mode 100644 index 00000000000..c831424a046 --- /dev/null +++ b/src/ipc/capnp/rpc.capnp @@ -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); +} diff --git a/src/node/interfaces.cpp b/src/node/interfaces.cpp index 6c61b2105d9..f806c40ed7c 100644 --- a/src/node/interfaces.cpp +++ b/src/node/interfaces.cpp @@ -12,12 +12,14 @@ #include #include #include +#include #include #include #include #include #include #include +#include #include #include #include @@ -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 MakeMining(node::NodeContext& context, bool wait_loaded) } return std::make_unique(context); } +std::unique_ptr MakeRpc(node::NodeContext& context) { return std::make_unique(context); } } // namespace interfaces