From 84934bf70e11fe4cda1cfda60113a54895d4fdd5 Mon Sep 17 00:00:00 2001 From: Russell Yanofsky Date: Tue, 24 Nov 2020 13:59:33 -0500 Subject: 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. --- src/Makefile.am | 3 +++ src/init/bitcoin-node.cpp | 2 ++ src/interfaces/echo.cpp | 18 ++++++++++++++++++ src/interfaces/echo.h | 26 ++++++++++++++++++++++++++ src/interfaces/init.cpp | 2 ++ src/interfaces/init.h | 2 ++ src/ipc/capnp/echo.capnp | 17 +++++++++++++++++ src/ipc/capnp/init-types.h | 3 +++ src/ipc/capnp/init.capnp | 4 ++++ src/rpc/misc.cpp | 41 +++++++++++++++++++++++++++++++++++++++++ 10 files changed, 118 insertions(+) create mode 100644 src/interfaces/echo.cpp create mode 100644 src/interfaces/echo.h create mode 100644 src/ipc/capnp/echo.capnp (limited to 'src') 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 #include #include #include @@ -21,6 +22,7 @@ public: { m_node.init = this; } + std::unique_ptr makeEcho() override { return interfaces::MakeEcho(); } interfaces::Ipc* ipc() override { return m_ipc.get(); } NodeContext& m_node; std::unique_ptr 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 + +#include + +namespace interfaces { +namespace { +class EchoImpl : public Echo +{ +public: + std::string echo(const std::string& echo) override { return echo; } +}; +} // namespace +std::unique_ptr MakeEcho() { return std::make_unique(); } +} // 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 +#include + +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 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 +#include #include #include #include @@ -11,5 +12,6 @@ namespace interfaces { std::unique_ptr Init::makeNode() { return {}; } std::unique_ptr Init::makeChain() { return {}; } std::unique_ptr Init::makeWalletClient(Chain& chain) { return {}; } +std::unique_ptr 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 makeNode(); virtual std::unique_ptr makeChain(); virtual std::unique_ptr makeWalletClient(Chain& chain); + virtual std::unique_ptr 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 + #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 #include #include +#include +#include +#include #include #include #include @@ -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 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) { -- cgit v1.2.3