diff options
author | Russell Yanofsky <russ@yanofsky.org> | 2020-11-24 13:59:33 -0500 |
---|---|---|
committer | Russell Yanofsky <russ@yanofsky.org> | 2021-04-23 03:02:50 -0500 |
commit | 84934bf70e11fe4cda1cfda60113a54895d4fdd5 (patch) | |
tree | a76e8bd27d3e32a9f433d6f24857d6b96e499efc | |
parent | 7d76cf667eff512043a28d4407cc89f58796c42b (diff) |
multiprocess: Add echoipc RPC method and test
Add simple interfaces::Echo IPC interface with one method that just takes and
returns a string, to test multiprocess framework and provide an example of how
it can be used to spawn and call between processes.
-rw-r--r-- | src/Makefile.am | 3 | ||||
-rw-r--r-- | src/init/bitcoin-node.cpp | 2 | ||||
-rw-r--r-- | src/interfaces/echo.cpp | 18 | ||||
-rw-r--r-- | src/interfaces/echo.h | 26 | ||||
-rw-r--r-- | src/interfaces/init.cpp | 2 | ||||
-rw-r--r-- | src/interfaces/init.h | 2 | ||||
-rw-r--r-- | src/ipc/capnp/echo.capnp | 17 | ||||
-rw-r--r-- | src/ipc/capnp/init-types.h | 3 | ||||
-rw-r--r-- | src/ipc/capnp/init.capnp | 4 | ||||
-rw-r--r-- | src/rpc/misc.cpp | 41 | ||||
-rwxr-xr-x | test/functional/rpc_misc.py | 3 |
11 files changed, 121 insertions, 0 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index 71dd7b65a0..447015fc66 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -159,6 +159,7 @@ BITCOIN_CORE_H = \ init.h \ init/common.h \ interfaces/chain.h \ + interfaces/echo.h \ interfaces/handler.h \ interfaces/init.h \ interfaces/ipc.h \ @@ -563,6 +564,7 @@ libbitcoin_util_a_SOURCES = \ compat/glibcxx_sanity.cpp \ compat/strnlen.cpp \ fs.cpp \ + interfaces/echo.cpp \ interfaces/handler.cpp \ interfaces/init.cpp \ logging.cpp \ @@ -815,6 +817,7 @@ if HARDEN endif libbitcoin_ipc_mpgen_input = \ + ipc/capnp/echo.capnp \ ipc/capnp/init.capnp EXTRA_DIST += $(libbitcoin_ipc_mpgen_input) %.capnp: diff --git a/src/init/bitcoin-node.cpp b/src/init/bitcoin-node.cpp index b1c8a5b561..49684ede83 100644 --- a/src/init/bitcoin-node.cpp +++ b/src/init/bitcoin-node.cpp @@ -2,6 +2,7 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. +#include <interfaces/echo.h> #include <interfaces/init.h> #include <interfaces/ipc.h> #include <node/context.h> @@ -21,6 +22,7 @@ public: { m_node.init = this; } + std::unique_ptr<interfaces::Echo> makeEcho() override { return interfaces::MakeEcho(); } interfaces::Ipc* ipc() override { return m_ipc.get(); } NodeContext& m_node; std::unique_ptr<interfaces::Ipc> m_ipc; diff --git a/src/interfaces/echo.cpp b/src/interfaces/echo.cpp new file mode 100644 index 0000000000..9bbb42217b --- /dev/null +++ b/src/interfaces/echo.cpp @@ -0,0 +1,18 @@ +// Copyright (c) 2021 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 <interfaces/echo.h> + +#include <memory> + +namespace interfaces { +namespace { +class EchoImpl : public Echo +{ +public: + std::string echo(const std::string& echo) override { return echo; } +}; +} // namespace +std::unique_ptr<Echo> MakeEcho() { return std::make_unique<EchoImpl>(); } +} // namespace interfaces diff --git a/src/interfaces/echo.h b/src/interfaces/echo.h new file mode 100644 index 0000000000..5578d9d9e6 --- /dev/null +++ b/src/interfaces/echo.h @@ -0,0 +1,26 @@ +// Copyright (c) 2021 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_ECHO_H +#define BITCOIN_INTERFACES_ECHO_H + +#include <memory> +#include <string> + +namespace interfaces { +//! Simple string echoing interface for testing. +class Echo +{ +public: + virtual ~Echo() {} + + //! Echo provided string. + virtual std::string echo(const std::string& echo) = 0; +}; + +//! Return implementation of Echo interface. +std::unique_ptr<Echo> MakeEcho(); +} // namespace interfaces + +#endif // BITCOIN_INTERFACES_ECHO_H diff --git a/src/interfaces/init.cpp b/src/interfaces/init.cpp index 1eeea9b5cb..a3c949e616 100644 --- a/src/interfaces/init.cpp +++ b/src/interfaces/init.cpp @@ -3,6 +3,7 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include <interfaces/chain.h> +#include <interfaces/echo.h> #include <interfaces/init.h> #include <interfaces/node.h> #include <interfaces/wallet.h> @@ -11,5 +12,6 @@ namespace interfaces { std::unique_ptr<Node> Init::makeNode() { return {}; } std::unique_ptr<Chain> Init::makeChain() { return {}; } std::unique_ptr<WalletClient> Init::makeWalletClient(Chain& chain) { return {}; } +std::unique_ptr<Echo> Init::makeEcho() { return {}; } Ipc* Init::ipc() { return nullptr; } } // namespace interfaces diff --git a/src/interfaces/init.h b/src/interfaces/init.h index 8aea027f01..2a38054a17 100644 --- a/src/interfaces/init.h +++ b/src/interfaces/init.h @@ -11,6 +11,7 @@ struct NodeContext; namespace interfaces { class Chain; +class Echo; class Ipc; class Node; class WalletClient; @@ -29,6 +30,7 @@ public: virtual std::unique_ptr<Node> makeNode(); virtual std::unique_ptr<Chain> makeChain(); virtual std::unique_ptr<WalletClient> makeWalletClient(Chain& chain); + virtual std::unique_ptr<Echo> makeEcho(); virtual Ipc* ipc(); }; diff --git a/src/ipc/capnp/echo.capnp b/src/ipc/capnp/echo.capnp new file mode 100644 index 0000000000..df36ee0de3 --- /dev/null +++ b/src/ipc/capnp/echo.capnp @@ -0,0 +1,17 @@ +# Copyright (c) 2021 The Bitcoin Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. + +@0x888b4f7f51e691f7; + +using Cxx = import "/capnp/c++.capnp"; +$Cxx.namespace("ipc::capnp::messages"); + +using Proxy = import "/mp/proxy.capnp"; +$Proxy.include("interfaces/echo.h"); +$Proxy.include("ipc/capnp/echo.capnp.h"); + +interface Echo $Proxy.wrap("interfaces::Echo") { + destroy @0 (context :Proxy.Context) -> (); + echo @1 (context :Proxy.Context, echo: Text) -> (result :Text); +} diff --git a/src/ipc/capnp/init-types.h b/src/ipc/capnp/init-types.h index c84b94802a..42031441b5 100644 --- a/src/ipc/capnp/init-types.h +++ b/src/ipc/capnp/init-types.h @@ -4,4 +4,7 @@ #ifndef BITCOIN_IPC_CAPNP_INIT_TYPES_H #define BITCOIN_IPC_CAPNP_INIT_TYPES_H + +#include <ipc/capnp/echo.capnp.proxy-types.h> + #endif // BITCOIN_IPC_CAPNP_INIT_TYPES_H diff --git a/src/ipc/capnp/init.capnp b/src/ipc/capnp/init.capnp index be6eecb4b9..e6d358c665 100644 --- a/src/ipc/capnp/init.capnp +++ b/src/ipc/capnp/init.capnp @@ -8,9 +8,13 @@ using Cxx = import "/capnp/c++.capnp"; $Cxx.namespace("ipc::capnp::messages"); using Proxy = import "/mp/proxy.capnp"; +$Proxy.include("interfaces/echo.h"); $Proxy.include("interfaces/init.h"); $Proxy.includeTypes("ipc/capnp/init-types.h"); +using Echo = import "echo.capnp"; + interface Init $Proxy.wrap("interfaces::Init") { construct @0 (threadMap: Proxy.ThreadMap) -> (threadMap :Proxy.ThreadMap); + makeEcho @1 (context :Proxy.Context) -> (result :Echo.Echo); } diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp index 00a06260ea..09b32345a2 100644 --- a/src/rpc/misc.cpp +++ b/src/rpc/misc.cpp @@ -7,6 +7,9 @@ #include <index/blockfilterindex.h> #include <index/txindex.h> #include <interfaces/chain.h> +#include <interfaces/echo.h> +#include <interfaces/init.h> +#include <interfaces/ipc.h> #include <key_io.h> #include <node/context.h> #include <outputtype.h> @@ -644,6 +647,43 @@ static RPCHelpMan echo(const std::string& name) static RPCHelpMan echo() { return echo("echo"); } static RPCHelpMan echojson() { return echo("echojson"); } +static RPCHelpMan echoipc() +{ + return RPCHelpMan{ + "echoipc", + "\nEcho back the input argument, passing it through a spawned process in a multiprocess build.\n" + "This command is for testing.\n", + {{"arg", RPCArg::Type::STR, RPCArg::Optional::NO, "The string to echo",}}, + RPCResult{RPCResult::Type::STR, "echo", "The echoed string."}, + RPCExamples{HelpExampleCli("echo", "\"Hello world\"") + + HelpExampleRpc("echo", "\"Hello world\"")}, + [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue { + std::unique_ptr<interfaces::Echo> echo; + if (interfaces::Ipc* ipc = Assert(EnsureAnyNodeContext(request.context).init)->ipc()) { + // Spawn a new bitcoin-node process and call makeEcho to get a + // client pointer to a interfaces::Echo instance running in + // that process. This is just for testing. A slightly more + // realistic test spawning a different executable instead of + // the same executable would add a new bitcoin-echo executable, + // and spawn bitcoin-echo below instead of bitcoin-node. But + // using bitcoin-node avoids the need to build and install a + // new executable just for this one test. + auto init = ipc->spawnProcess("bitcoin-node"); + echo = init->makeEcho(); + ipc->addCleanup(*echo, [init = init.release()] { delete init; }); + } else { + // IPC support is not available because this is a bitcoind + // process not a bitcoind-node process, so just create a local + // interfaces::Echo object and return it so the `echoipc` RPC + // method will work, and the python test calling `echoipc` + // can expect the same result. + echo = interfaces::MakeEcho(); + } + return echo->echo(request.params[0].get_str()); + }, + }; +} + static UniValue SummaryToJSON(const IndexSummary&& summary, std::string index_name) { UniValue ret_summary(UniValue::VOBJ); @@ -719,6 +759,7 @@ static const CRPCCommand commands[] = { "hidden", &mockscheduler, }, { "hidden", &echo, }, { "hidden", &echojson, }, + { "hidden", &echoipc, }, }; // clang-format on for (const auto& c : commands) { diff --git a/test/functional/rpc_misc.py b/test/functional/rpc_misc.py index 1398d1237f..a80fa596cd 100755 --- a/test/functional/rpc_misc.py +++ b/test/functional/rpc_misc.py @@ -61,6 +61,9 @@ class RpcMiscTest(BitcoinTestFramework): node.logging(include=['qt']) assert_equal(node.logging()['qt'], True) + self.log.info("test echoipc (testing spawned process in multiprocess build)") + assert_equal(node.echoipc("hello"), "hello") + self.log.info("test getindexinfo") # Without any indices running the RPC returns an empty object assert_equal(node.getindexinfo(), {}) |