Skip to content

Commit 84934bf

Browse files
committed
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.
1 parent 7d76cf6 commit 84934bf

File tree

11 files changed

+121
-0
lines changed

11 files changed

+121
-0
lines changed

src/Makefile.am

+3
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,7 @@ BITCOIN_CORE_H = \
159159
init.h \
160160
init/common.h \
161161
interfaces/chain.h \
162+
interfaces/echo.h \
162163
interfaces/handler.h \
163164
interfaces/init.h \
164165
interfaces/ipc.h \
@@ -563,6 +564,7 @@ libbitcoin_util_a_SOURCES = \
563564
compat/glibcxx_sanity.cpp \
564565
compat/strnlen.cpp \
565566
fs.cpp \
567+
interfaces/echo.cpp \
566568
interfaces/handler.cpp \
567569
interfaces/init.cpp \
568570
logging.cpp \
@@ -815,6 +817,7 @@ if HARDEN
815817
endif
816818

817819
libbitcoin_ipc_mpgen_input = \
820+
ipc/capnp/echo.capnp \
818821
ipc/capnp/init.capnp
819822
EXTRA_DIST += $(libbitcoin_ipc_mpgen_input)
820823
%.capnp:

src/init/bitcoin-node.cpp

+2
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// Distributed under the MIT software license, see the accompanying
33
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
44

5+
#include <interfaces/echo.h>
56
#include <interfaces/init.h>
67
#include <interfaces/ipc.h>
78
#include <node/context.h>
@@ -21,6 +22,7 @@ class BitcoinNodeInit : public interfaces::Init
2122
{
2223
m_node.init = this;
2324
}
25+
std::unique_ptr<interfaces::Echo> makeEcho() override { return interfaces::MakeEcho(); }
2426
interfaces::Ipc* ipc() override { return m_ipc.get(); }
2527
NodeContext& m_node;
2628
std::unique_ptr<interfaces::Ipc> m_ipc;

src/interfaces/echo.cpp

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// Copyright (c) 2021 The Bitcoin Core developers
2+
// Distributed under the MIT software license, see the accompanying
3+
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4+
5+
#include <interfaces/echo.h>
6+
7+
#include <memory>
8+
9+
namespace interfaces {
10+
namespace {
11+
class EchoImpl : public Echo
12+
{
13+
public:
14+
std::string echo(const std::string& echo) override { return echo; }
15+
};
16+
} // namespace
17+
std::unique_ptr<Echo> MakeEcho() { return std::make_unique<EchoImpl>(); }
18+
} // namespace interfaces

src/interfaces/echo.h

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// Copyright (c) 2021 The Bitcoin Core developers
2+
// Distributed under the MIT software license, see the accompanying
3+
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4+
5+
#ifndef BITCOIN_INTERFACES_ECHO_H
6+
#define BITCOIN_INTERFACES_ECHO_H
7+
8+
#include <memory>
9+
#include <string>
10+
11+
namespace interfaces {
12+
//! Simple string echoing interface for testing.
13+
class Echo
14+
{
15+
public:
16+
virtual ~Echo() {}
17+
18+
//! Echo provided string.
19+
virtual std::string echo(const std::string& echo) = 0;
20+
};
21+
22+
//! Return implementation of Echo interface.
23+
std::unique_ptr<Echo> MakeEcho();
24+
} // namespace interfaces
25+
26+
#endif // BITCOIN_INTERFACES_ECHO_H

src/interfaces/init.cpp

+2
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
44

55
#include <interfaces/chain.h>
6+
#include <interfaces/echo.h>
67
#include <interfaces/init.h>
78
#include <interfaces/node.h>
89
#include <interfaces/wallet.h>
@@ -11,5 +12,6 @@ namespace interfaces {
1112
std::unique_ptr<Node> Init::makeNode() { return {}; }
1213
std::unique_ptr<Chain> Init::makeChain() { return {}; }
1314
std::unique_ptr<WalletClient> Init::makeWalletClient(Chain& chain) { return {}; }
15+
std::unique_ptr<Echo> Init::makeEcho() { return {}; }
1416
Ipc* Init::ipc() { return nullptr; }
1517
} // namespace interfaces

src/interfaces/init.h

+2
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ struct NodeContext;
1111

1212
namespace interfaces {
1313
class Chain;
14+
class Echo;
1415
class Ipc;
1516
class Node;
1617
class WalletClient;
@@ -29,6 +30,7 @@ class Init
2930
virtual std::unique_ptr<Node> makeNode();
3031
virtual std::unique_ptr<Chain> makeChain();
3132
virtual std::unique_ptr<WalletClient> makeWalletClient(Chain& chain);
33+
virtual std::unique_ptr<Echo> makeEcho();
3234
virtual Ipc* ipc();
3335
};
3436

src/ipc/capnp/echo.capnp

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# Copyright (c) 2021 The Bitcoin Core developers
2+
# Distributed under the MIT software license, see the accompanying
3+
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
4+
5+
@0x888b4f7f51e691f7;
6+
7+
using Cxx = import "/capnp/c++.capnp";
8+
$Cxx.namespace("ipc::capnp::messages");
9+
10+
using Proxy = import "/mp/proxy.capnp";
11+
$Proxy.include("interfaces/echo.h");
12+
$Proxy.include("ipc/capnp/echo.capnp.h");
13+
14+
interface Echo $Proxy.wrap("interfaces::Echo") {
15+
destroy @0 (context :Proxy.Context) -> ();
16+
echo @1 (context :Proxy.Context, echo: Text) -> (result :Text);
17+
}

src/ipc/capnp/init-types.h

+3
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,7 @@
44

55
#ifndef BITCOIN_IPC_CAPNP_INIT_TYPES_H
66
#define BITCOIN_IPC_CAPNP_INIT_TYPES_H
7+
8+
#include <ipc/capnp/echo.capnp.proxy-types.h>
9+
710
#endif // BITCOIN_IPC_CAPNP_INIT_TYPES_H

src/ipc/capnp/init.capnp

+4
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,13 @@ using Cxx = import "/capnp/c++.capnp";
88
$Cxx.namespace("ipc::capnp::messages");
99

1010
using Proxy = import "/mp/proxy.capnp";
11+
$Proxy.include("interfaces/echo.h");
1112
$Proxy.include("interfaces/init.h");
1213
$Proxy.includeTypes("ipc/capnp/init-types.h");
1314

15+
using Echo = import "echo.capnp";
16+
1417
interface Init $Proxy.wrap("interfaces::Init") {
1518
construct @0 (threadMap: Proxy.ThreadMap) -> (threadMap :Proxy.ThreadMap);
19+
makeEcho @1 (context :Proxy.Context) -> (result :Echo.Echo);
1620
}

src/rpc/misc.cpp

+41
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@
77
#include <index/blockfilterindex.h>
88
#include <index/txindex.h>
99
#include <interfaces/chain.h>
10+
#include <interfaces/echo.h>
11+
#include <interfaces/init.h>
12+
#include <interfaces/ipc.h>
1013
#include <key_io.h>
1114
#include <node/context.h>
1215
#include <outputtype.h>
@@ -644,6 +647,43 @@ static RPCHelpMan echo(const std::string& name)
644647
static RPCHelpMan echo() { return echo("echo"); }
645648
static RPCHelpMan echojson() { return echo("echojson"); }
646649

650+
static RPCHelpMan echoipc()
651+
{
652+
return RPCHelpMan{
653+
"echoipc",
654+
"\nEcho back the input argument, passing it through a spawned process in a multiprocess build.\n"
655+
"This command is for testing.\n",
656+
{{"arg", RPCArg::Type::STR, RPCArg::Optional::NO, "The string to echo",}},
657+
RPCResult{RPCResult::Type::STR, "echo", "The echoed string."},
658+
RPCExamples{HelpExampleCli("echo", "\"Hello world\"") +
659+
HelpExampleRpc("echo", "\"Hello world\"")},
660+
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue {
661+
std::unique_ptr<interfaces::Echo> echo;
662+
if (interfaces::Ipc* ipc = Assert(EnsureAnyNodeContext(request.context).init)->ipc()) {
663+
// Spawn a new bitcoin-node process and call makeEcho to get a
664+
// client pointer to a interfaces::Echo instance running in
665+
// that process. This is just for testing. A slightly more
666+
// realistic test spawning a different executable instead of
667+
// the same executable would add a new bitcoin-echo executable,
668+
// and spawn bitcoin-echo below instead of bitcoin-node. But
669+
// using bitcoin-node avoids the need to build and install a
670+
// new executable just for this one test.
671+
auto init = ipc->spawnProcess("bitcoin-node");
672+
echo = init->makeEcho();
673+
ipc->addCleanup(*echo, [init = init.release()] { delete init; });
674+
} else {
675+
// IPC support is not available because this is a bitcoind
676+
// process not a bitcoind-node process, so just create a local
677+
// interfaces::Echo object and return it so the `echoipc` RPC
678+
// method will work, and the python test calling `echoipc`
679+
// can expect the same result.
680+
echo = interfaces::MakeEcho();
681+
}
682+
return echo->echo(request.params[0].get_str());
683+
},
684+
};
685+
}
686+
647687
static UniValue SummaryToJSON(const IndexSummary&& summary, std::string index_name)
648688
{
649689
UniValue ret_summary(UniValue::VOBJ);
@@ -719,6 +759,7 @@ static const CRPCCommand commands[] =
719759
{ "hidden", &mockscheduler, },
720760
{ "hidden", &echo, },
721761
{ "hidden", &echojson, },
762+
{ "hidden", &echoipc, },
722763
};
723764
// clang-format on
724765
for (const auto& c : commands) {

test/functional/rpc_misc.py

+3
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,9 @@ def run_test(self):
6161
node.logging(include=['qt'])
6262
assert_equal(node.logging()['qt'], True)
6363

64+
self.log.info("test echoipc (testing spawned process in multiprocess build)")
65+
assert_equal(node.echoipc("hello"), "hello")
66+
6467
self.log.info("test getindexinfo")
6568
# Without any indices running the RPC returns an empty object
6669
assert_equal(node.getindexinfo(), {})

0 commit comments

Comments
 (0)