mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-01-12 12:52:35 -03:00
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.
This commit is contained in:
parent
7d76cf667e
commit
84934bf70e
11 changed files with 121 additions and 0 deletions
|
@ -159,6 +159,7 @@ BITCOIN_CORE_H = \
|
||||||
init.h \
|
init.h \
|
||||||
init/common.h \
|
init/common.h \
|
||||||
interfaces/chain.h \
|
interfaces/chain.h \
|
||||||
|
interfaces/echo.h \
|
||||||
interfaces/handler.h \
|
interfaces/handler.h \
|
||||||
interfaces/init.h \
|
interfaces/init.h \
|
||||||
interfaces/ipc.h \
|
interfaces/ipc.h \
|
||||||
|
@ -563,6 +564,7 @@ libbitcoin_util_a_SOURCES = \
|
||||||
compat/glibcxx_sanity.cpp \
|
compat/glibcxx_sanity.cpp \
|
||||||
compat/strnlen.cpp \
|
compat/strnlen.cpp \
|
||||||
fs.cpp \
|
fs.cpp \
|
||||||
|
interfaces/echo.cpp \
|
||||||
interfaces/handler.cpp \
|
interfaces/handler.cpp \
|
||||||
interfaces/init.cpp \
|
interfaces/init.cpp \
|
||||||
logging.cpp \
|
logging.cpp \
|
||||||
|
@ -815,6 +817,7 @@ if HARDEN
|
||||||
endif
|
endif
|
||||||
|
|
||||||
libbitcoin_ipc_mpgen_input = \
|
libbitcoin_ipc_mpgen_input = \
|
||||||
|
ipc/capnp/echo.capnp \
|
||||||
ipc/capnp/init.capnp
|
ipc/capnp/init.capnp
|
||||||
EXTRA_DIST += $(libbitcoin_ipc_mpgen_input)
|
EXTRA_DIST += $(libbitcoin_ipc_mpgen_input)
|
||||||
%.capnp:
|
%.capnp:
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
// Distributed under the MIT software license, see the accompanying
|
// Distributed under the MIT software license, see the accompanying
|
||||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
|
||||||
|
#include <interfaces/echo.h>
|
||||||
#include <interfaces/init.h>
|
#include <interfaces/init.h>
|
||||||
#include <interfaces/ipc.h>
|
#include <interfaces/ipc.h>
|
||||||
#include <node/context.h>
|
#include <node/context.h>
|
||||||
|
@ -21,6 +22,7 @@ public:
|
||||||
{
|
{
|
||||||
m_node.init = this;
|
m_node.init = this;
|
||||||
}
|
}
|
||||||
|
std::unique_ptr<interfaces::Echo> makeEcho() override { return interfaces::MakeEcho(); }
|
||||||
interfaces::Ipc* ipc() override { return m_ipc.get(); }
|
interfaces::Ipc* ipc() override { return m_ipc.get(); }
|
||||||
NodeContext& m_node;
|
NodeContext& m_node;
|
||||||
std::unique_ptr<interfaces::Ipc> m_ipc;
|
std::unique_ptr<interfaces::Ipc> m_ipc;
|
||||||
|
|
18
src/interfaces/echo.cpp
Normal file
18
src/interfaces/echo.cpp
Normal file
|
@ -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
|
26
src/interfaces/echo.h
Normal file
26
src/interfaces/echo.h
Normal file
|
@ -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
|
|
@ -3,6 +3,7 @@
|
||||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
|
||||||
#include <interfaces/chain.h>
|
#include <interfaces/chain.h>
|
||||||
|
#include <interfaces/echo.h>
|
||||||
#include <interfaces/init.h>
|
#include <interfaces/init.h>
|
||||||
#include <interfaces/node.h>
|
#include <interfaces/node.h>
|
||||||
#include <interfaces/wallet.h>
|
#include <interfaces/wallet.h>
|
||||||
|
@ -11,5 +12,6 @@ namespace interfaces {
|
||||||
std::unique_ptr<Node> Init::makeNode() { return {}; }
|
std::unique_ptr<Node> Init::makeNode() { return {}; }
|
||||||
std::unique_ptr<Chain> Init::makeChain() { return {}; }
|
std::unique_ptr<Chain> Init::makeChain() { return {}; }
|
||||||
std::unique_ptr<WalletClient> Init::makeWalletClient(Chain& chain) { return {}; }
|
std::unique_ptr<WalletClient> Init::makeWalletClient(Chain& chain) { return {}; }
|
||||||
|
std::unique_ptr<Echo> Init::makeEcho() { return {}; }
|
||||||
Ipc* Init::ipc() { return nullptr; }
|
Ipc* Init::ipc() { return nullptr; }
|
||||||
} // namespace interfaces
|
} // namespace interfaces
|
||||||
|
|
|
@ -11,6 +11,7 @@ struct NodeContext;
|
||||||
|
|
||||||
namespace interfaces {
|
namespace interfaces {
|
||||||
class Chain;
|
class Chain;
|
||||||
|
class Echo;
|
||||||
class Ipc;
|
class Ipc;
|
||||||
class Node;
|
class Node;
|
||||||
class WalletClient;
|
class WalletClient;
|
||||||
|
@ -29,6 +30,7 @@ public:
|
||||||
virtual std::unique_ptr<Node> makeNode();
|
virtual std::unique_ptr<Node> makeNode();
|
||||||
virtual std::unique_ptr<Chain> makeChain();
|
virtual std::unique_ptr<Chain> makeChain();
|
||||||
virtual std::unique_ptr<WalletClient> makeWalletClient(Chain& chain);
|
virtual std::unique_ptr<WalletClient> makeWalletClient(Chain& chain);
|
||||||
|
virtual std::unique_ptr<Echo> makeEcho();
|
||||||
virtual Ipc* ipc();
|
virtual Ipc* ipc();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
17
src/ipc/capnp/echo.capnp
Normal file
17
src/ipc/capnp/echo.capnp
Normal file
|
@ -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);
|
||||||
|
}
|
|
@ -4,4 +4,7 @@
|
||||||
|
|
||||||
#ifndef BITCOIN_IPC_CAPNP_INIT_TYPES_H
|
#ifndef BITCOIN_IPC_CAPNP_INIT_TYPES_H
|
||||||
#define 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
|
#endif // BITCOIN_IPC_CAPNP_INIT_TYPES_H
|
||||||
|
|
|
@ -8,9 +8,13 @@ using Cxx = import "/capnp/c++.capnp";
|
||||||
$Cxx.namespace("ipc::capnp::messages");
|
$Cxx.namespace("ipc::capnp::messages");
|
||||||
|
|
||||||
using Proxy = import "/mp/proxy.capnp";
|
using Proxy = import "/mp/proxy.capnp";
|
||||||
|
$Proxy.include("interfaces/echo.h");
|
||||||
$Proxy.include("interfaces/init.h");
|
$Proxy.include("interfaces/init.h");
|
||||||
$Proxy.includeTypes("ipc/capnp/init-types.h");
|
$Proxy.includeTypes("ipc/capnp/init-types.h");
|
||||||
|
|
||||||
|
using Echo = import "echo.capnp";
|
||||||
|
|
||||||
interface Init $Proxy.wrap("interfaces::Init") {
|
interface Init $Proxy.wrap("interfaces::Init") {
|
||||||
construct @0 (threadMap: Proxy.ThreadMap) -> (threadMap :Proxy.ThreadMap);
|
construct @0 (threadMap: Proxy.ThreadMap) -> (threadMap :Proxy.ThreadMap);
|
||||||
|
makeEcho @1 (context :Proxy.Context) -> (result :Echo.Echo);
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,9 @@
|
||||||
#include <index/blockfilterindex.h>
|
#include <index/blockfilterindex.h>
|
||||||
#include <index/txindex.h>
|
#include <index/txindex.h>
|
||||||
#include <interfaces/chain.h>
|
#include <interfaces/chain.h>
|
||||||
|
#include <interfaces/echo.h>
|
||||||
|
#include <interfaces/init.h>
|
||||||
|
#include <interfaces/ipc.h>
|
||||||
#include <key_io.h>
|
#include <key_io.h>
|
||||||
#include <node/context.h>
|
#include <node/context.h>
|
||||||
#include <outputtype.h>
|
#include <outputtype.h>
|
||||||
|
@ -644,6 +647,43 @@ static RPCHelpMan echo(const std::string& name)
|
||||||
static RPCHelpMan echo() { return echo("echo"); }
|
static RPCHelpMan echo() { return echo("echo"); }
|
||||||
static RPCHelpMan echojson() { return echo("echojson"); }
|
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)
|
static UniValue SummaryToJSON(const IndexSummary&& summary, std::string index_name)
|
||||||
{
|
{
|
||||||
UniValue ret_summary(UniValue::VOBJ);
|
UniValue ret_summary(UniValue::VOBJ);
|
||||||
|
@ -719,6 +759,7 @@ static const CRPCCommand commands[] =
|
||||||
{ "hidden", &mockscheduler, },
|
{ "hidden", &mockscheduler, },
|
||||||
{ "hidden", &echo, },
|
{ "hidden", &echo, },
|
||||||
{ "hidden", &echojson, },
|
{ "hidden", &echojson, },
|
||||||
|
{ "hidden", &echoipc, },
|
||||||
};
|
};
|
||||||
// clang-format on
|
// clang-format on
|
||||||
for (const auto& c : commands) {
|
for (const auto& c : commands) {
|
||||||
|
|
|
@ -61,6 +61,9 @@ class RpcMiscTest(BitcoinTestFramework):
|
||||||
node.logging(include=['qt'])
|
node.logging(include=['qt'])
|
||||||
assert_equal(node.logging()['qt'], True)
|
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")
|
self.log.info("test getindexinfo")
|
||||||
# Without any indices running the RPC returns an empty object
|
# Without any indices running the RPC returns an empty object
|
||||||
assert_equal(node.getindexinfo(), {})
|
assert_equal(node.getindexinfo(), {})
|
||||||
|
|
Loading…
Reference in a new issue